1.softmax和分类模型
softmax的基本概念
分类问题
一个简单的图像分类问题,输入图像的高和宽均为2像素,色彩为灰度。
图像中的4像素分别记为
x
1
,
x
2
,
x
3
,
x
4
x
1
,
x
2
,
x
3
,
x
4
x1,x2,x3,x4x1,x2,x3,x4
x1,x2,x3,x4x1,x2,x3,x4
假设真实标签为狗、猫或者鸡,这些标签对应的离散值为
y
1
,
y
2
,
y
3
y
1
,
y
2
,
y
3
y1,y2,y3y1,y2,y3
y1,y2,y3y1,y2,y3
通常使用离散的数值来表示类别,例如
y
1
=
1
,
y
2
=
2
,
y
3
=
3
y
1
=
1
,
y
2
=
2
,
y
3
=
3
y1=1,y2=2,y3=3y1=1,y2=2,y3=3
y1=1,y2=2,y3=3y1=1,y2=2,y3=3
权重矢量
o
1
=
x
1
w
11
+
x
2
w
21
+
x
3
w
31
+
x
4
w
41
+
b
1
o
1
=
x
1
w
11
+
x
2
w
21
+
x
3
w
31
+
x
4
w
41
+
b
1
o
2
=
x
1
w
12
+
x
2
w
22
+
x
3
w
32
+
x
4
w
42
+
b
2
o
2
=
x
1
w
12
+
x
2
w
22
+
x
3
w
32
+
x
4
w
42
+
b
2
o
3
=
x
1
w
13
+
x
2
w
23
+
x
3
w
33
+
x
4
w
43
+
b
3
o
3
=
x
1
w
13
+
x
2
w
23
+
x
3
w
33
+
x
4
w
43
+
b
3
o1=x1w11+x2w21+x3w31+x4w41+b1\\ o1=x1w11+x2w21+x3w31+x4w41+b1\\ o2=x1w12+x2w22+x3w32+x4w42+b2\\ o2=x1w12+x2w22+x3w32+x4w42+b2\\ o3=x1w13+x2w23+x3w33+x4w43+b3\\ o3=x1w13+x2w23+x3w33+x4w43+b3
o1=x1w11+x2w21+x3w31+x4w41+b1o1=x1w11+x2w21+x3w31+x4w41+b1o2=x1w12+x2w22+x3w32+x4w42+b2o2=x1w12+x2w22+x3w32+x4w42+b2o3=x1w13+x2w23+x3w33+x4w43+b3o3=x1w13+x2w23+x3w33+x4w43+b3
神经网络图
softmax回归同线性回归一样,也是一个单层神经网络。由于每个输出 o1,o2,o3o1,o2,o3 的计算都要依赖于所有的输入 x1,x2,x3,x4x1,x2,x3,x4 ,softmax回归的输出层也是一个全连接层。
既然分类问题需要得到离散的预测输出,一个简单的办法是将输出值 oioi 当作预测类别是 ii 的置信度,并将值最大的输出所对应的类作为预测输出,即输出 argmaxioiargmaxioi 。例如,如果 o1,o2,o3o1,o2,o3 分别为 0.1,10,0.10.1,10,0.1 ,由于 o2o2 最大,那么预测类别为2,其代表猫。
输出问题
A
计算效率
单样本矢量计算表达式
为了提高计算效率,我们可以将单样本分类通过矢量计算来表达。在上面的图像分类问题中,假设softmax回归的权重和偏差参数分别为
W=⎡⎣⎢⎢⎢w11w21w31w41w12w22w32w42w13w23w33w43⎤⎦⎥⎥⎥,b=[b1b2b3],
W=[w11w12w13w21w22w23w31w32w33w41w42w43],b=[b1b2b3]
设高和宽分别为2个像素的图像样本 ii 的特征为
x
(
i
)
=
[
x
(
i
)
1
x
(
i
)
2
x
(
i
)
3
x
(
i
)
4
]
,
x
(
i
)
=
[
x
1
(
i
)
x
2
(
i
)
x
3
(
i
)
x
4
(
i
)
]
,
x(i)=[x(i)1x(i)2x(i)3x(i)4], x(i)=[x1(i)x2(i)x3(i)x4(i)],
x(i)=[x(i)1x(i)2x(i)3x(i)4],x(i)=[x1(i)x2(i)x3(i)x4(i)],
输出层的输出为
o
(
i
)
=
[
o
(
i
)
1
o
(
i
)
2
o
(
i
)
3
]
,
o
(
i
)
=
[
o
1
(
i
)
o
2
(
i
)
o
3
(
i
)
]
,
o(i)=[o(i)1o(i)2o(i)3], o(i)=[o1(i)o2(i)o3(i)],
o(i)=[o(i)1o(i)2o(i)3],o(i)=[o1(i)o2(i)o3(i)],
预测为狗、猫或鸡的概率分布为
y(i)=[y(i)1y(i)2y(i)3].
y(i)=[y1(i)y2(i)y3(i)].
softmax回归对样本 ii 分类的矢量计算表达式为
o
(
i
)
y
(
i
)
=
x
(
i
)
W
+
b
,
=
s
o
f
t
m
a
x
(
o
(
i
)
)
.
o
(
i
)
=
x
(
i
)
W
+
b
,
y
(
i
)
=
s
o
f
t
m
a
x
(
o
(
i
)
)
o(i)y^(i)=x(i)W+b,=softmax(o(i)).\\ o(i)=x(i)W+b,y^(i)=softmax(o(i))
o(i)y(i)=x(i)W+b,=softmax(o(i)).o(i)=x(i)W+b,y(i)=softmax(o(i))
小批量矢量计算表达式
小批量数据做矢量计算,可提长计算效率。
广义上讲,给定一个小批量样本,其批量大小为 nn ,输入个数(特征数)为 dd ,输出个数(类别数)为 q 。设批量特征为 X∈Rn×dX∈Rn×d 。假设softmax回归的权重和偏差参数分别为 W∈Rd×qW∈Rd×q 和 b∈R1×qb∈R1×q 。softmax回归的矢量计算表达式为
O
=
X
W
+
b
Y
^
=
s
o
f
t
m
a
x
(
O
)
O=XW+b\\ \hat{Y}=softmax(O)
O=XW+bY^=softmax(O)
交叉熵损失函数
A
模型训练和预测
在训练好softmax回归模型后,给定任一样本特征,就可以预测每个输出类别的概率。通常,我们把预测概率最大的类别作为输出类别。如果它与真实类别(标签)一致,说明这次预测是正确的。
获取Fashion-MNIST训练集和读取数据
在介绍softmax回归的实现前我们先引入一个多类图像分类数据集,以方便我们观察比较算法之间在模型精度和计算效率上的区别。这里使用torchvision包,它是服务于PyTorch深度学习框架的,主要用来构建计算机视觉模型。
torchvision主要由以下几部分构成:
torchvision.datasets: 一些加载数据的函数及常用的数据集接口;
torchvision.models: 包含常用的模型结构(含预训练模型),例如AlexNet、VGG、ResNet等;
torchvision.transforms: 常用的图片变换,例如裁剪、旋转等;
torchvision.utils: 其他的一些有用的方法。
import needed package
%matplotlib inline
from IPython import display
import matplotlib.pyplot as plt
import torch
import torchvision
import torchvision.transforms as transforms
import time
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l
print(torch.__version__)
print(torchvision.__version__)
1.3.0
0.4.1a0+d94043a
Get Dataset
mnist_train = torchvision.datasets.FashionMNIST(root='/home/kesci/input/FashionMNIST2065', train=True, download=True, transform=transforms.ToTensor())
mnist_test = torchvision.datasets.FashionMNIST(root='/home/kesci/input/FashionMNIST2065', train=False, download=True, transform=transforms.ToTensor())
show result
print(type(mnist_train))
print(len(mnist_train), len(mnist_test))
#<class 'torchvision.datasets.mnist.FashionMNIST'>
#60000 10000
我们可以通过下标来访问任意一个样本
feature, label = mnist_train[0]
print(feature.shape, label) # Channel x Height x Width
#torch.Size([1, 28, 28]) 9
如果不做变换输入的数据是图像,我们可以看一下图片的类型参数:
mnist_PIL = torchvision.datasets.FashionMNIST(root='/home/kesci/input/FashionMNIST2065', train=True, download=True)
PIL_feature, label = mnist_PIL[0]
print(PIL_feature)
#<PIL.Image.Image image mode=L size=28x28 at 0x7F57E8736F28>
本函数已保存在d2lzh包中方便以后使用
def get_fashion_mnist_labels(labels):
text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
return [text_labels[int(i)] for i in labels]
def show_fashion_mnist(images, labels):
d2l.use_svg_display()
# 这里的_表示我们忽略(不使用)的变量
_, figs = plt.subplots(1, len(images), figsize=(12, 12))
for f, img, lbl in zip(figs, images, labels):
f.imshow(img.view((28, 28)).numpy())
f.set_title(lbl)
f.axes.get_xaxis().set_visible(False)
f.axes.get_yaxis().set_visible(False)
plt.show()
X, y = [], []
for i in range(10):
X.append(mnist_train[i][0]) # 将第i个feature加到X中
y.append(mnist_train[i][1]) # 将第i个label加到y中
show_fashion_mnist(X, get_fashion_mnist_labels(y))
读取数据
batch_size = 256
num_workers = 4
train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)
start = time.time()
for X, y in train_iter:
continue
print('%.2f sec' % (time.time() - start))
#4.89 sec
2.softmax的简洁实现
加载各种包或者模块
import torch
from torch import nn
from torch.nn import init
import numpy as np
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l
print(torch.__version__)
#1.3.0
初始化参数和获取数据
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
定义网络模型
num_inputs = 784
num_outputs = 10
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
net = LinearNet(num_inputs, num_outputs)
class FlattenLayer(nn.Module):
def __init__(self):
super(FlattenLayer, self).__init__()
def forward(self, x): # x 的形状: (batch, *, *, ...)
return x.view(x.shape[0], -1)
from collections import OrderedDict
net = nn.Sequential(
# FlattenLayer(),
# LinearNet(num_inputs, num_outputs)
OrderedDict([
('flatten', FlattenLayer()),
('linear', nn.Linear(num_inputs, num_outputs))]) # 或者写成我们自己定义的 LinearNet(num_inputs, num_outputs) 也可以
)
初始化模型参数
init.normal_(net.linear.weight, mean=0, std=0.01)
init.constant_(net.linear.bias, val=0)
#Out:
#Parameter containing:
#tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], requires_grad=True)
定义损失函数
loss = nn.CrossEntropyLoss() # 下面是他的函数原型
class torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')
定义优化函数
optimizer = torch.optim.SGD(net.parameters(), lr=0.1) # 下面是函数原型
class torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False)
训练
num_epochs = 5
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, optimizer)
epoch 1, loss 0.0031, train acc 0.749, test acc 0.794
epoch 2, loss 0.0022, train acc 0.814, test acc 0.800
epoch 3, loss 0.0021, train acc 0.826, test acc 0.811
epoch 4, loss 0.0020, train acc 0.833, test acc 0.826
epoch 5, loss 0.0019, train acc 0.837, test acc 0.825