softmax 函数的多种实现方式 包括纯C语言、C++版本、Eigen版本等
flyfish
版本1 规矩的写法
#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 概率
#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。
#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})
#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语言方式
#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;
}