机器学习 - 计算 Log-Sum-Exp

计算 Log-Sum-Exp

[原文-Computing Log-Sum-Exp]

本文的计算技巧是有效的,但在机器学习课程上没有明确提到.

假设 N 个 值的数据集 {xn}Nn=1,需要计算的值为:

z=logNn=1exp{xn}

当采用 softmax 对 multinomial 分布进行参数化时,比如,logistic 回归和有多于 2 个无序类别时,这种计算经常出现.

如果想要计算 log likehood,由于存在归一化常数,也会看到这种表达式.

直接进行计算,则是灾难性的,因为存在下溢(underflow) 和上溢(overflow), 取决于 xn 的尺度(scale).

假设一种简单的例子, 向量 [0,1,0] , 看着是很直接明了的,得到的结果 z=1.55 .

z=ln(exp(0)+exp(1)+exp(0))=ln(2+e)=1.55

当向量为 [1000,1001,1000] 时, z=ln(exp(1000)+exp(1001)+exp(1000))=inf .

当向量为 [1000,999,1000] 时, z=ln(exp(1000)+exp(999)+exp(1000))=inf .

中间发生了什么?

在 64-bit 系统中,因为下溢(underflow) 和上溢(overflow) 的存在, exp{1000}=inf , exp{1000}=0 .

即使 log 会以无限精度(infinite precision) 再次合理的缩放(scaled)数值, 但在实际的计算机上,是不可行的.

因此,解决方案是?

采取的技巧是:

logNn=1exp{xn}=a+logNn=1exp{xna}

a 为任意数.

上式意味着,我们可以任意的平移指数的中心值, 放大或缩小.

一种典型的是使,

a={max}nxn

这样保证了取指数时的最大值为0.

这种技巧使得绝对不会出现上溢(overflow),即使其余的下溢(underflow),也可以得到一个合理的值.

  • 15
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
PS-worker算法是一种分布式机器学习算法,使用MPI进行实现比较方便。下面是一个简单的PS-worker程序示例: ```c #include <mpi.h> #include <stdio.h> #include <stdlib.h> #define N 100 // 数据总数 #define K 10 // 模型参数个数 #define M 10 // 每个worker处理的数据个数 #define EPOCHS 100 // 训练轮数 #define ALPHA 0.01 // 学习率 // 生成随机数据 void generate_data(float* X, float* y, int n, int k) { for (int i = 0; i < n * k; i++) { X[i] = (float)rand() / RAND_MAX; } for (int i = 0; i < n; i++) { float sum = 0; for (int j = 0; j < k; j++) { sum += X[i * k + j]; } y[i] = sum > k / 2 ? 1.0 : 0.0; } } // 计算sigmoid函数 float sigmoid(float x) { return 1.0 / (1.0 + exp(-x)); } // 计算损失函数 float loss(float* X, float* y, float* w, int n, int k) { float sum = 0; for (int i = 0; i < n; i++) { float dot = 0; for (int j = 0; j < k; j++) { dot += X[i * k + j] * w[j]; } sum += y[i] * log(sigmoid(dot)) + (1 - y[i]) * log(1 - sigmoid(dot)); } return -sum / n; } int main(int argc, char** argv) { int rank, size; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); srand(rank + 1); // 每个进程使用不同的随机数种子 float* X = (float*)malloc(M * K * sizeof(float)); // 每个worker处理的数据 float* y = (float*)malloc(M * sizeof(float)); generate_data(X, y, M, K); float* w = (float*)malloc(K * sizeof(float)); // 模型参数 for (int i = 0; i < K; i++) { w[i] = 0; } float* grad = (float*)malloc(K * sizeof(float)); // 梯度 for (int i = 0; i < K; i++) { grad[i] = 0; } int n_workers = size - 1; // worker数量 int n_batches = N / (M * n_workers); // 每个worker处理的batch数量 int n_samples = n_batches * M; // 每个worker处理的数据总数 int n_epochs = EPOCHS; if (rank == 0) { // PS进程 printf("Start training...\n"); double start_time = MPI_Wtime(); for (int epoch = 0; epoch < n_epochs; epoch++) { for (int i = 1; i <= n_workers; i++) { MPI_Send(w, K, MPI_FLOAT, i, 0, MPI_COMM_WORLD); // 发送参数 } for (int i = 1; i <= n_workers; i++) { MPI_Recv(grad, K, MPI_FLOAT, i, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); // 接收梯度 for (int j = 0; j < K; j++) { w[j] -= ALPHA * grad[j]; // 更新参数 } } printf("Epoch %d loss: %f\n", epoch, loss(X, y, w, N, K)); } double end_time = MPI_Wtime(); printf("Training finished. Time: %f seconds\n", end_time - start_time); } else { // worker进程 for (int epoch = 0; epoch < n_epochs; epoch++) { for (int batch = 0; batch < n_batches; batch++) { MPI_Recv(w, K, MPI_FLOAT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); // 接收参数 for (int i = 0; i < M; i++) { float dot = 0; for (int j = 0; j < K; j++) { dot += X[i * K + j] * w[j]; } float error = y[i] - sigmoid(dot); for (int j = 0; j < K; j++) { grad[j] += error * X[i * K + j]; // 计算梯度 } } } MPI_Send(grad, K, MPI_FLOAT, 0, 1, MPI_COMM_WORLD); // 发送梯度 for (int i = 0; i < K; i++) { grad[i] = 0; // 清零梯度 } } } MPI_Finalize(); free(X); free(y); free(w); free(grad); return 0; } ``` 该程序使用了一个PS进程和多个worker进程,PS进程负责发送模型参数和接收梯度,worker进程负责接收模型参数、计算梯度并发送给PS进程。每个worker进程处理M个样本,并使用随机梯度下降算法更新模型参数。 该程序可以在MPI环境下进行编译和运行,比如使用mpicc编译: ``` mpicc -o ps_worker ps_worker.c ``` 然后使用mpirun运行: ``` mpirun -n 5 ./ps_worker ``` 其中-n参数指定进程数量,这里使用了5个进程(1个PS进程和4个worker进程)。该程序会进行100轮训练,并输出每轮训练的损失函数值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值