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;
}
相关推荐
王老师青少年编程26 分钟前
csp信奥赛C++高频考点专项训练之贪心算法 --【贪心与二分判定】:数列分段 Section II
c++·算法·贪心·csp·信奥赛·二分判定·数列分段 section ii
zh_xuan28 分钟前
libcurl调用https接口
c++·libcurl
就叫飞六吧30 分钟前
QT写一个桌面程序exe并动态打包基本流程(c++)
开发语言·c++
蜡笔小马31 分钟前
1.c++设计模式-工厂模式
c++
汉克老师1 小时前
GESP2025年3月认证C++五级( 第三部分编程题(2、原根判断))
c++·算法·模运算·gesp5级·gesp五级·原根·分解质因数
winner88811 小时前
从零吃透C++命名空间、std、#include、string、vector
java·开发语言·c++
Aurorar0rua2 小时前
CS50 x 2024 Notes C - 07
c语言·学习方法
AI进化营-智能译站2 小时前
ROS2 C++开发系列07-高效构建机器人决策逻辑,运算符与控制流实战
开发语言·c++·ai·机器人
爱编码的小八嘎2 小时前
C语言完美演绎9-15
c语言
winner88812 小时前
C++ 命名空间、虚函数、抽象类、protected 权限全套通俗易懂精讲(附与 Java 对比)
java·开发语言·c++