Softmax
与候选采样相对
Softmax function, a wonderful activation function that turns numbers aka logits into probabilities that sum to one. Softmax function outputs a vector that represents the probability distributions of a list of potential outcomes.
一种函数,可提供多类别分类模型中每个可能类别的概率。这些概率的总和正好为 1.0。
Example: softmax 可能会得出某个图像是狗、猫和马的概率分别是 0.9、0.08 和 0.02。(也称为完整 softmax。)
softmax的基本概念
分类问题
一个简单的图像分类问题,输入图像的高和宽均为2像素,色彩为灰度。
图像中的4像素分别记为$x_1, x_2, x_3, x_4$。
假设真实标签为狗、猫或者鸡,这些标签对应的离散值为$y_1, y_2, y_3$。
我们通常使用离散的数值来表示类别,例如$y_1=1, y_2=2, y_3=3$。权重矢量
$$
\begin{aligned} o_1 &= x_1 w_{11} + x_2 w_{21} + x_3 w_{31} + x_4 w_{41} + b_1 \end{aligned}
$$
$$
\begin{aligned} o_2 &= x_1 w_{12} + x_2 w_{22} + x_3 w_{32} + x_4 w_{42} + b_2 \end{aligned}
$$
$$
\begin{aligned} o_3 &= x_1 w_{13} + x_2 w_{23} + x_3 w_{33} + x_4 w_{43} + b_3 \end{aligned}
$$
- 神经网络图
下图用神经网络图描绘了上面的计算。softmax回归同线性回归一样,也是一个单层神经网络。由于每个输出$o_1, o_2, o_3$的计算都要依赖于所有的输入$x_1, x_2, x_3, x_4$,softmax回归的输出层也是一个全连接层。
$$
\begin{aligned}softmax回归是一个单层神经网络\end{aligned}
$$
既然分类问题需要得到离散的预测输出,一个简单的办法是将输出值$o_i$当作预测类别是$i$的置信度,并将值最大的输出所对应的类作为预测输出,即输出 $\underset{i}{\arg\max} o_i$。例如,如果$o_1,o_2,o_3$分别为$0.1,10,0.1$,由于$o_2$最大,那么预测类别为2,其代表猫。
- 输出问题
直接使用输出层的输出有两个问题:- 一方面,由于输出层的输出值的范围不确定,我们难以直观上判断这些值的意义。例如,刚才举的例子中的输出值10表示“很置信”图像类别为猫,因为该输出值是其他两类的输出值的100倍。但如果$o_1=o_3=10^3$,那么输出值10却又表示图像类别为猫的概率很低。
- 另一方面,由于真实标签是离散值,这些离散值与不确定范围的输出值之间的误差难以衡量。
softmax运算符(softmax operator)解决了以上两个问题。它通过下式将输出值变换成值为正且和为1的概率分布:
$$
\hat{y}_1, \hat{y}_2, \hat{y}_3 = \text{softmax}(o_1, o_2, o_3)
$$
其中
$$
\hat{y}1 = \frac{ \exp(o_1)}{\sum_{i=1}^3 \exp(o_i)},\quad \hat{y}2 = \frac{ \exp(o_2)}{\sum_{i=1}^3 \exp(o_i)},\quad \hat{y}3 = \frac{ \exp(o_3)}{\sum_{i=1}^3 \exp(o_i)}.
$$
容易看出$\hat{y}_1 + \hat{y}_2 + \hat{y}_3 = 1$且$0 \leq \hat{y}_1, \hat{y}_2, \hat{y}_3 \leq 1$,因此$\hat{y}_1, \hat{y}_2, \hat{y}_3$是一个合法的概率分布。这时候,如果$\hat{y}_2=0.8$,不管$\hat{y}_1$和$\hat{y}_3$的值是多少,我们都知道图像类别为猫的概率是80%。此外,我们注意到
$$
\underset{i}{\arg\max} o_i = \underset{i}{\arg\max} \hat{y}_i
$$
因此softmax运算不改变预测类别输出。
- 计算效率
- 单样本矢量计算表达式
为了提高计算效率,我们可以将单样本分类通过矢量计算来表达。在上面的图像分类问题中,假设softmax回归的权重和偏差参数分别为
- 单样本矢量计算表达式
$$
\boldsymbol{W} = \begin{bmatrix} w_{11} & w_{12} & w_{13} \\ w_{21} & w_{22} & w_{23} \\ w_{31} & w_{32} & w_{33} \\ w_{41} & w_{42} & w_{43} \end{bmatrix},\quad \boldsymbol{b} = \begin{bmatrix} b_1 & b_2 & b_3 \end{bmatrix},
$$
设高和宽分别为2个像素的图像样本$i$的特征为
$$
\boldsymbol{x}^{(i)} = \begin{bmatrix}x_1^{(i)} & x_2^{(i)} & x_3^{(i)} & x_4^{(i)}\end{bmatrix},
$$
输出层的输出为
$$
\boldsymbol{o}^{(i)} = \begin{bmatrix}o_1^{(i)} & o_2^{(i)} & o_3^{(i)}\end{bmatrix},
$$
预测为狗、猫或鸡的概率分布为
$$
\boldsymbol{\hat{y}}^{(i)} = \begin{bmatrix}\hat{y}_1^{(i)} & \hat{y}_2^{(i)} & \hat{y}_3^{(i)}\end{bmatrix}.
$$
softmax回归对样本$i$分类的矢量计算表达式为
$$
\begin{aligned} \boldsymbol{o}^{(i)} &= \boldsymbol{x}^{(i)} \boldsymbol{W} + \boldsymbol{b},\\ \boldsymbol{\hat{y}}^{(i)} &= \text{softmax}(\boldsymbol{o}^{(i)}). \end{aligned}
$$
- 小批量矢量计算表达式
为了进一步提升计算效率,我们通常对小批量数据做矢量计算。广义上讲,给定一个小批量样本,其批量大小为$n$,输入个数(特征数)为$d$,输出个数(类别数)为$q$。设批量特征为$\boldsymbol{X} \in \mathbb{R}^{n \times d}$。假设softmax回归的权重和偏差参数分别为$\boldsymbol{W} \in \mathbb{R}^{d \times q}$和$\boldsymbol{b} \in \mathbb{R}^{1 \times q}$。softmax回归的矢量计算表达式为
$$
\begin{aligned} \boldsymbol{O} &= \boldsymbol{X} \boldsymbol{W} + \boldsymbol{b},\\ \boldsymbol{\hat{Y}} &= \text{softmax}(\boldsymbol{O}), \end{aligned}
$$
其中的加法运算使用了广播机制,$\boldsymbol{O}, \boldsymbol{\hat{Y}} \in \mathbb{R}^{n \times q}$且这两个矩阵的第$i$行分别为样本$i$的输出$\boldsymbol{o}^{(i)}$和概率分布$\boldsymbol{\hat{y}}^{(i)}$。
两种操作对比
numpy 操作:np.exp(x) / np.sum(np.exp(x), axis=0)
pytorch 操作:torch.exp(x)/torch.sum(torch.exp(x), dim=1).view(-1,1)
引入Fashion-MNIST
为方便介绍 Softmax, 为了更加直观的观察到算法之间的差异
引入较为复杂的多分类图像分类数据集导入:torchvision 包【构建计算机视觉模型】
1 | # import needed package |
1 | # get dataset |
1 | # read data |
Softmax 手动实现
import package and module
1 | import torch |
获取数据
1 | batch_size = 256 |
模型参数初始化
1 | # init module param |
Sofmax 定义
1 | # define softmax function |
softmax 回归模型
1 | # define regression model |
损失函数
1 | # define loss function |
准确率
1 | def accuracy(y_hat, y): |
训练模型
1 | num_epochs, lr = 5, 0.1 |
模型预测
1 | X, y = iter(test_iter).next() |
Pytorch 改进
1 | # import package and module |
初始化参数和获取数据
1 | # init param and get data |
定义网络模型
1 | num_inputs = 784 |
初始化模型参数
1 | # init module param |
损失函数
1 | loss = nn.CrossEntropyLoss() # 下面是他的函数原型 |
优化函数
1 | optimizer = torch.optim.SGD(net.parameters(), lr=0.1) # 下面是函数原型 |
训练
1 | num_epochs = 5 |
训练结果分析
最开始:训练数据集上的准确率低于测试数据集上的准确率
原因
训练集上的准确率是在一个epoch的过程中计算得到的
测试集上的准确率是在一个epoch结束后计算得到的
Result: 后者的模型参数更优