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;
}
相关推荐
楼田莉子2 小时前
C++算法专题学习:栈相关的算法
开发语言·c++·算法·leetcode
晨非辰2 小时前
#C语言——刷题攻略:牛客编程入门训练(九):攻克 分支控制(三)、循环控制(一),轻松拿捏!
c语言·开发语言·经验分享·学习方法·visual studio
dragoooon342 小时前
[数据结构——lesson3.单链表]
数据结构·c++·leetcode·学习方法
Suresoft China2 小时前
软件测试|STATIC 代码静态验证工具 C/C++ 工具链设置指南
c++·单元测试·静态测试·测试覆盖率·static·代码覆盖率·工具链设置
陈序猿(代码自用版)3 小时前
【考研C语言编程题】数组元素批量插入实现(含图示+三部曲拆解)
c语言·开发语言·考研
77qqqiqi3 小时前
学习循环语句
c语言
kyle~3 小时前
排序---冒泡排序(Bubble Sort)
c语言·c++·算法
l1t3 小时前
我改写的二分法XML转CSV文件程序速度追上了张泽鹏先生的
xml·c语言·人工智能·算法·expat
hmcjn(小何同学)3 小时前
轻松Linux-9.进程间通信
linux·运维·服务器·c++·bash
落羽的落羽3 小时前
【C++】C++11的包装器:function与bind简介
c++·学习