特征数 m m m,类别数 q q q,样本数 n n n。
单样本
在每个类别上的预测值都是一个线性回归计算;
在 q q q个类别上的预测值可以写为:
y ^ = W ⊤ x + b , \hat{\mathbf y} = \mathbf{W}^\top \mathbf{x} + \mathbf b, y^=W⊤x+b,
输入:特征向量 x ∈ R m × 1 \mathbf{x} \in \mathbb{R}^{m\times1} x∈Rm×1
输出:预测向量 y ^ ∈ R q × 1 \hat{\mathbf y} \in \mathbb{R}^{q\times1} y^∈Rq×1,是各个类别上的预测值
参数:权重矩阵 W ∈ R m × q \mathbf{W} \in \mathbb{R}^{m\times q} W∈Rm×q、向量 b ∈ R q × 1 \mathbf {b} \in {\mathbb {R}}^{q\times 1} b∈Rq×1
多样本
Y ^ = X W + b , \hat{\mathbf Y} = \mathbf{X}\mathbf{W} + \mathbf b, Y^=XW+b,
输入:特征矩阵 X = ( x ( 1 ) , x ( 2 ) , . . . , x ( n ) ) ⊤ ∈ R n × m \mathbf{X}=\left( \mathbf{x}^{(1)}, \mathbf{x}^{(2)}, ..., \mathbf{x}^{(n)} \right)^{\top} \in \mathbb{R}^{n\times m} X=(x(1),x(2),...,x(n))⊤∈Rn×m
输出:预测矩阵 Y ^ = ( y ^ ( 1 ) , y ^ ( 2 ) , . . . , y ^ ( n ) ) ⊤ ∈ R n × q \hat{\mathbf Y}=\left(\hat \mathbf y^{(1)},\hat \mathbf y^{(2)},...,\hat \mathbf y^{(n)} \right)^{\top} \in \mathbb{R}^{n\times q} Y^=(y^(1),y^(2),...,y^(n))⊤∈Rn×q
参数:权重矩阵 W ∈ R m × q \mathbf{W} \in \mathbb{R}^{m\times q} W∈Rm×q、向量 b ∈ R q × 1 \mathbf {b} \in {\mathbb {R}}^{q\times 1} b∈Rq×1(用了广播机制)
至此为止,得到的输出即为深度学习API调用时常说的logits数值,即还没有经过softmax或sigmoid激活函数的预测值,后面选用API时就选with_logits的。
补充一下,logit函数定义为:
L ( p ) = l n 1 1 − p L(p)=ln\frac {1}{1-p} L(p)=ln1−p1
是一种将取值范围在[0,1]内的概率映射到实数域[-inf,inf]的函数,如果p=0.5,函数值为0;p<0.5,函数值为负;p>0.5,函数值为正。(相对地,softmax和sigmoid则都是将[-inf,inf]映射到[0,1]的函数。)
回归正题。直接输出回归预测值会导致两个问题:
(1)输出值的范围不确定,我们难以直观上判断这些值的意义;
(2)真实标签是离散值,难以衡量这些离散值与不确定范围的输出值之间的误差。
因此,在softmax回归预测值后面会跟一个softmax运算符(softmax operator),将一个样本的预测输出变换成值为正且和为1的概率分布:
y ^ = ( y ^ 1 y ^ 2 ⋮ y ^ q ) = softmax ( o 1 o 2 ⋮ o q ) , \hat \mathbf{y} = \begin{pmatrix} \hat{y}_1 \\ \hat{y}_2 \\ \vdots \\ \hat{y}_q \end{pmatrix} = \text{softmax} \begin{pmatrix} o_1\\ o_2 \\ \vdots \\o_q \end{pmatrix}, y^=⎝⎜⎜⎜⎛y^1y^2⋮y^q⎠⎟⎟⎟⎞=softmax⎝⎜⎜⎜⎛o1o2⋮oq⎠⎟⎟⎟⎞,
其中,
y ^ 1 = exp ( o 1 ) ∑ i = 1 q exp ( o i ) , y ^ 2 = exp ( o 2 ) ∑ i = 1 q exp ( o i ) , ⋯ , y ^ q = exp ( o q ) ∑ i = 1 q exp ( o i ) . \hat{y}_1 = \frac{ \exp(o_1)}{\sum_{i=1}^q \exp(o_i)},\quad \hat{y}_2 = \frac{ \exp(o_2)}{\sum_{i=1}^q \exp(o_i)},\quad \cdots,\quad \hat{y}_q = \frac{ \exp(o_q)}{\sum_{i=1}^q \exp(o_i)}. y^1=∑i=1qexp(oi)exp(o1),y^2=∑i=1qexp(oi)exp(o2),⋯,y^q=∑i=1qexp(oi)exp(oq).
交叉熵损失(cross entropy loss)
平方损失对于分类问题来说过于严格,想要预测分类结果正确,其实并不需要在各个类别上的预测概率值(通常某类别预测值比其他类别都大就可以预测对)完全等于标签中各类别的值(通常是one-hot),只需要预测的类别概率分布和标签的类别概率分布一致即可,也即对的类别概率比较大,错的类别概率比较小。通常用交叉熵来衡量两个概率分布的距离。
Softmax回归等价于一个全连接层,激活函数为softmax、输出层为n_class,用Pytorch的API即nn.Linear(n_feature, n_class),loss是nn.CrossEntropyLoss()(这个loss的实现里面已经包含了softmax计算,所以不需要手动在全连接层后面添加softmax激活函数,送入nn.CrossEntropyLoss的预测值是在经过softmax激活函数之前的logits):
softmax回归网络:
num_inputs = 784
num_outputs = 10
# 输入层
class FlattenLayer(nn.Module):
def __init__(self):
super(FlattenLayer, self).__init__()
def forward(self, x): # x 的形状: (batch, *, *, ...)
return x.view(x.shape[0], -1)
# softmax回归层
class LinearNet(nn.Module):
def __init__(self, num_inputs, num_outputs):
super(LinearNet, self).__init__()
self.linear = nn.Linear(num_inputs, num_outputs)
def forward(self, x): # x 的形状: (batch, 1, 28, 28)
y = self.linear(x.view(x.shape[0], -1))
return y
# 整个网络,顺序连接输入层和softmax回归层
from collections import OrderedDict
net = nn.Sequential(
# FlattenLayer(),
# LinearNet(num_inputs, num_outputs)
OrderedDict([
('flatten', FlattenLayer()), # 对输入的形状进行调整,相当于输入层,里面没有要学习的参数
('linear', LinearNet(num_inputs, num_outputs))]) # 或者写成我们自己定义的 LinearNet(num_inputs, num_outputs) 也可以
)
print(net)
利用Softmax回归,实现图像多类别分类代码:
https://github.com/XuanxuanGao/MachineLearningWithPyTorch/blob/master/Softmax%E5%9B%9E%E5%BD%92.ipynb