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;
}
相关推荐
leiming65 小时前
C++ vector容器
开发语言·c++·算法
SystickInt5 小时前
C语言 strcpy和memcpy 异同/区别
c语言·开发语言
CS Beginner5 小时前
【C语言】windows下编译mingw版本的glew库
c语言·开发语言·windows
JAY_LIN——86 小时前
指针-数组
c语言·排序算法
进阶的猪7 小时前
STM32 使用HAL库SPI读写FLASH(W25Q128JV)数据 Q&A
c语言·stm32·单片机
apocelipes7 小时前
从源码角度解析C++20新特性如何简化线程超时取消
c++·性能优化·golang·并发·c++20·linux编程
ozyzo7 小时前
求1~n的累加和
c++
charlie1145141918 小时前
现代C++嵌入式教程:C++98基础特性:从C到C++的演进(1)
c语言·开发语言·c++·笔记·学习·教程
历程里程碑9 小时前
C++ 18智能指针:告别内存泄漏的利器
开发语言·c++
XFF不秃头10 小时前
力扣刷题笔记-全排列
c++·笔记·算法·leetcode