回到首页

softmax regression realization

Declare trainable parameters

num_inputs = 784
num_outputs = 10

W = np.random.normal(0, 0.01, (num_inputs, num_outputs))
b = np.zeros(num_outputs)
W.attach_grad()
b.attach_grad()
		
我们使用mxnet框架时不须声明权重和偏置项。我们定义完网络结构后,框架自动完成。

Define network structure: y=softmax(xW+b). Define formulas of softmax and cross entropy. Define parameters' optimization method.

def softmax(X):
	X_exp = np.exp(X)
	partition = X_exp.sum(1, keepdims=True)
	return X_exp/partition # broadcasting

def net(X):
	return softmax(np.dot(X.reshape((-1, W.shape[0])), W)+b)

def loss(y_hat, y):
	# cross entropy
	return -np.log(y_hat[range(len(y_hat)), y])

def sgd(params, lr, batch_size):
	# mini-batch stochastic gradient descent
	for param in params:
		param[:]=param-lr*param.grad/batch_size

def updater(batch_size):
    return sgd([W,b], lr, batch_size)
		
使用mxnet框架实现这些内容更简洁:
net = nn.Sequential()
net.add(nn.Dense(10))
net.initialize(init.Normal(sigma=0.01))
loss = gluon.loss.SoftmaxCrossEntropyLoss()
updater = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': lr})

自定义代码中的loss函数的原理:
熵的数学定义:\(H[P]=\sum_j-P(j)logP(j)\),人类不能完全预测所有事件,观察某一事件j,我们赋予它主观概率P(j),当我们遇到预期发生概率较低的事件时,我们的惊异会更大。克劳德香农用\(log{1\over P(j)}=-logP(j)\)表示这种惊异。熵的含义是呈主观概率分布的事件后验预期惊异。
softmax函数给出向量\(\hat y\),表示对给定任意输入x的每个类的条件概率。设数据集{X, Y}有n个样本,索引i的样本由特征向量\(x^{(i)}\)和长度为q的独热标签向量\(y^{(i)}\)组成。有\(P(Y|X)=\prod_{i=1}^nP(y^{(i)}|x^{(i)})\),根据最大似然估计,最小化负对数似然,负对数似然衡量损失,即\(-logP(Y|X)=\sum_{i=1}^n-logP(y^{(i)}|x^{(i)})=\sum_{i=1}^n-\sum_{j=1}^qy^{(i)}_jlog\hat y^{(i)}_j\)。\(-\sum_{j=1}^qy^{(i)}_jlog\hat y^{(i)}_j\)表示真实标签和预测标签的交叉熵。由于y中只有一项是1,其余项是0,所以单个样本的交叉熵是真实标签的预测概率的负对数。
某样本在模型上的损失\(l(y, \hat y)=-\sum_{j=1}^qy_jlog{exp(o_j)\over\sum_{k=1}^qexp(o_k)}=\sum_{j=1}^qy_jlog\sum_{k=1}^qexp(o_k)-\sum_{j=1}^qy_jo_j=log\sum_{k=1}^qexp(o_k)-\sum_{j=1}^qy_jo_j\),其中\(\hat y_j=softmax(o_j)={exp(o_j)\over\sum_{k=1}^qexp(o_k)}\), \(o\)是模型的输出。 于是\(\partial_{o_j}l(y, \hat y)={exp(o_j)\over\sum_{k=1}^qexp(o_k)}-y_j=softmax(o_j)-y_j\),损失对模型某一标签输出的偏导是该标签由softmax分配(模型认为)的概率与实际概率的差。另外,根据熵的含义,指出交叉熵反映“主观概率的观察者(模型)看到未知模式(概率)生成的数据时的预期惊异”,当两种概率的分布一致时,交叉熵达到最低。为了保证模型贴近实际分布,交叉熵损失需要尽可能得小。

mxnet中的SoftmaxCrossEntropyLoss()函数相比自定义的softmax得到预测标签并通过交叉熵计算损失的方法的好处:使得数值计算的结果更稳定。通过如下两方面提高结果计算的稳定性:
未规范化的预测\(o\)的第j个元素\(o_j\)。若\(o_j\)数值非常大,\(exp(o_j)\)可能大于数据类型容许的最大数字,即上溢(overflow),分母或分子变为inf,最后得到0、inf或nan;为解决该问题,从所有\(o_k\)中减去\(max(o_k)\),于是\(\hat y_j={exp(o_j-max(o_k))exp(max(o_k))\over\sum_kexp(o_k-max(o_k))exp(max(o_k))}={exp(o_j-max(o_k))\over\sum_kexp(o_k-max(o_k))}\).
在减法和规范化步骤后,可能有\(o_j-max(o_k)\)具有较大负值的情况,此时,\(exp(o_j-max(o_k))\)将有接近零的值,即下溢(underflow),下溢被取为0的话,\(\hat y_j=0\),\(log(\hat y_j)\)=-inf\),执行反向传播会出现nan结果。为解决这种问题,借由计算交叉熵损失时需要取\(\hat y_j\)的对数,于是\(log(\hat y_j)=log({exp(o_j-max(o_k))\over\sum_kexp(o_k-max(o_k))})=log(exp(o_j-max(o_k)))-log(\sum_kexp(o_k-max(o_k)))=o_j-max(o_k)-log(\sum_kexp(o_k-max(o_k)))\),避免了计算较大负值的指数函数值的情况

softmax回归和线性回归的两点不同:模型的输出需要softmax与否,softmax回归的损失是交叉熵,线性回归的损失是均方误差。

关于softmax回归的两种实现方式的详细代码在,安装mxnet库的wheel文件在

参考链接:softmax回归的从零开始实现softmax回归的简洁实现softmax回归

本文创建于2022.3.7/10.15,修改于2022.3.7/10.15

#softmax_regression #code #cross_entropy