softmax 函数的多种实现方式 包括纯C语言、C++版本、Eigen版本等

softmax 函数的多种实现方式 包括纯C语言、C++版本、Eigen版本等

flyfish

先看这里Softmax函数介绍

版本1 规矩的写法

c 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <cmath>

// 计算 softmax 的函数
std::vector<double> softmax(const std::vector<double>& input) {
    // 找到最大元素以防止 exp 计算时溢出
    double maxProb = *std::max_element(input.begin(), input.end());

    // 计算指数并求和
    std::vector<double> expVals;
    expVals.reserve(input.size());
    for (double val : input) {
        expVals.push_back(std::exp(val - maxProb)); // 计算每个元素的指数
    }

    double sumExp = std::accumulate(expVals.begin(), expVals.end(), 0.0); // 求所有指数的和

    // 归一化指数值以得到 softmax 概率
    std::vector<double> softmaxProb;
    softmaxProb.reserve(input.size());
    for (double val : expVals) {
        softmaxProb.push_back(val / sumExp); // 每个指数值除以总和得到 softmax 概率
    }

    return softmaxProb;
}

// 示例用法
int main() {
    std::vector<double> input = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}; // 示例输入

    std::vector<double> probabilities = softmax(input);

    // 输出 softmax 概率
    std::cout << "Softmax 概率:" << std::endl;
    for (double prob : probabilities) {
        std::cout << prob << " ";
    }
    std::cout << std::endl;

    // 找到具有最高概率的类别
    auto maxElementIter = std::max_element(probabilities.begin(), probabilities.end());
    int classId = std::distance(probabilities.begin(), maxElementIter);
    double confidence = *maxElementIter;

    std::cout << "预测类别: " << classId << " 置信度: " << confidence << std::endl;

    return 0;
}

版本2 合并循环,只使用一个 softmaxProb 向量来存储指数值和最终的 softmax 概率

c 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <cmath>

// 计算 softmax 的函数
std::vector<double> softmax(const std::vector<double>& input) {
    // 找到最大元素以防止 exp 计算时溢出
    double maxProb = *std::max_element(input.begin(), input.end());

    // 计算指数和求和
    std::vector<double> softmaxProb(input.size());
    double sumExp = 0.0;
    for (size_t i = 0; i < input.size(); ++i) {
        softmaxProb[i] = std::exp(input[i] - maxProb);
        sumExp += softmaxProb[i];
    }

    // 归一化指数值以得到 softmax 概率
    for (double& val : softmaxProb) {
        val /= sumExp;
    }

    return softmaxProb;
}

// 示例用法
int main() {
    std::vector<double> input = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}; // 示例输入

    std::vector<double> probabilities = softmax(input);

    // 输出 softmax 概率
    std::cout << "Softmax 概率:" << std::endl;
    for (double prob : probabilities) {
        std::cout << prob << " ";
    }
    std::cout << std::endl;

    // 找到具有最高概率的类别
    auto maxElementIter = std::max_element(probabilities.begin(), probabilities.end());
    int classId = std::distance(probabilities.begin(), maxElementIter);
    double confidence = *maxElementIter;

    std::cout << "预测类别: " << classId << " 置信度: " << confidence << std::endl;

    return 0;
}

版本3 C++17 使用并行执行策略

std::transform:用于计算每个元素的指数值,并存储在 expVals 中。使用并行执行策略可以提升计算效率。

std::reduce:用于并行求和,替代 std::accumulate。

c 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <cmath>
#include <execution>

// 计算 softmax 的函数
std::vector<double> softmax(const std::vector<double>& input) {
    // 找到最大元素以防止 exp 计算时溢出
    double maxProb = *std::max_element(input.begin(), input.end());

    // 计算指数和求和,同时避免重复遍历
    std::vector<double> expVals(input.size());
    std::transform(std::execution::par, input.begin(), input.end(), expVals.begin(), [maxProb](double val) {
        return std::exp(val - maxProb);
    });

    // 使用 std::reduce 并行求和
    double sumExp = std::reduce(std::execution::par, expVals.begin(), expVals.end(), 0.0);

    // 归一化指数值以得到 softmax 概率
    std::vector<double> softmaxProb(input.size());
    std::transform(std::execution::par, expVals.begin(), expVals.end(), softmaxProb.begin(), [sumExp](double val) {
        return val / sumExp;
    });

    return softmaxProb;
}

// 示例用法
int main() {
    std::vector<double> input = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}; // 示例输入

    std::vector<double> probabilities = softmax(input);

    // 输出 softmax 概率
    std::cout << "Softmax 概率:" << std::endl;
    for (double prob : probabilities) {
        std::cout << prob << " ";
    }
    std::cout << std::endl;

    // 找到具有最高概率的类别
    auto maxElementIter = std::max_element(probabilities.begin(), probabilities.end());
    int classId = std::distance(probabilities.begin(), maxElementIter);
    double confidence = *maxElementIter;

    std::cout << "预测类别: " << classId << " 置信度: " << confidence << std::endl;

    return 0;
}

版本4 Eigen 库实现

利用 Eigen 库可以高效地进行矩阵和向量运算。Eigen 库通过优化内存布局和利用 SIMD 指令集来提升性能。

Eigen::Map 可以将标准库中的容器(如 std::vector)映射为 Eigen 向量,从而直接进行高效的向量运算。

配置 CMakeLists.txt

# 添加 Eigen 目录
set(EIGEN3_INCLUDE_DIR "path/to/eigen") # 将此路径替换为你解压缩 Eigen 的目录
include_directories(${EIGEN3_INCLUDE_DIR})
c 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <Eigen/Dense>

// 计算 softmax 的函数
std::vector<double> softmax(const std::vector<double>& input) {
    // 将输入向量转换为 Eigen 向量
    Eigen::VectorXd vec = Eigen::Map<const Eigen::VectorXd>(input.data(), input.size());

    // 找到最大元素以防止 exp 计算时溢出
    double maxProb = vec.maxCoeff();

    // 计算指数和求和
    Eigen::VectorXd expVals = (vec.array() - maxProb).exp();
    double sumExp = expVals.sum();

    // 归一化指数值以得到 softmax 概率
    std::vector<double> softmaxProb(input.size());
    Eigen::VectorXd result = expVals / sumExp;
    Eigen::VectorXd::Map(&softmaxProb[0], result.size()) = result;

    return softmaxProb;
}

// 示例用法
int main() {
    std::vector<double> input = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}; // 示例输入

    std::vector<double> probabilities = softmax(input);

    // 输出 softmax 概率
    std::cout << "Softmax 概率:" << std::endl;
    for (double prob : probabilities) {
        std::cout << prob << " ";
    }
    std::cout << std::endl;

    // 找到具有最高概率的类别
    auto maxElementIter = std::max_element(probabilities.begin(), probabilities.end());
    int classId = std::distance(probabilities.begin(), maxElementIter);
    double confidence = *maxElementIter;

    std::cout << "预测类别: " << classId << " 置信度: " << confidence << std::endl;

    return 0;
}

版本5 纯C语言方式

C 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// 计算 softmax 的函数
void softmax(const double* input, double* softmaxProb, int size) {
    // 找到最大元素以防止 exp 计算时溢出
    double maxProb = input[0];
    for (int i = 1; i < size; ++i) {
        if (input[i] > maxProb) {
            maxProb = input[i];
        }
    }

    // 计算指数和求和
    double sumExp = 0.0;
    for (int i = 0; i < size; ++i) {
        softmaxProb[i] = exp(input[i] - maxProb);
        sumExp += softmaxProb[i];
    }

    // 归一化指数值以得到 softmax 概率
    for (int i = 0; i < size; ++i) {
        softmaxProb[i] /= sumExp;
    }
}

// 示例用法
int main() {
    double input[] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}; // 示例输入
    int size = sizeof(input) / sizeof(input[0]);
    double* probabilities = (double*)malloc(size * sizeof(double));

    if (probabilities == NULL) {
        fprintf(stderr, "内存分配失败\n");
        return 1;
    }

    softmax(input, probabilities, size);

    // 输出 softmax 概率
    printf("Softmax 概率:\n");
    for (int i = 0; i < size; ++i) {
        printf("%f ", probabilities[i]);
    }
    printf("\n");

    // 找到具有最高概率的类别
    double maxProb = probabilities[0];
    int classId = 0;
    for (int i = 1; i < size; ++i) {
        if (probabilities[i] > maxProb) {
            maxProb = probabilities[i];
            classId = i;
        }
    }

    double confidence = maxProb;
    printf("预测类别: %d 置信度: %f\n", classId, confidence);

    free(probabilities);

    return 0;
}
相关推荐
爱吃西瓜的小菜鸡4 小时前
【C语言】判断回文
c语言·学习·算法
别NULL4 小时前
机试题——疯长的草
数据结构·c++·算法
CYBEREXP20085 小时前
MacOS M3源代码编译Qt6.8.1
c++·qt·macos
yuanbenshidiaos5 小时前
c++------------------函数
开发语言·c++
yuanbenshidiaos5 小时前
C++----------函数的调用机制
java·c++·算法
tianmu_sama6 小时前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
羚羊角uou6 小时前
【C++】优先级队列以及仿函数
开发语言·c++
姚先生976 小时前
LeetCode 54. 螺旋矩阵 (C++实现)
c++·leetcode·矩阵
FeboReigns6 小时前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++
FeboReigns6 小时前
C++简明教程(文章要求学过一点C语言)(2)
c语言·开发语言·c++