大家好,我是小寒。
今天我们来分享第二个深度学习案例:手写数字识别。
原文链接
MNIST 手写数字识别数据集来自美国国家标准与技术研究所(National Institute of Standards and Technology,NIST)。这个数据集由250个不同人手写的数字构成, 其中50%来自高中生, 50%来自美国人口普查局(the Census Bureau) 的工作人员。MNIST 是图像分类中使用最广泛的数据集。
MNIST 手写数字识别数据集中的图像是一个28*28 的灰度图像。我们通过 pytorch 的内置函数将 MNIST 下载并读到内存中。
# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式,
# 并除以255使得所有像素的数值均在0到1之间
trans = transforms.ToTensor()
mnist_train = torchvision.datasets.MNIST(root="../data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.MNIST(root="../data", train=False, transform=trans, download=True)
MNIST由10个类别的图像组成,每个类别由训练数据集(train dataset)中的6000张图像和测试数据集(test dataset)中的1000张图像组成。
len(mnist_train), len(mnist_test)
60000,10000
我们来显示看一下数据集中的图像样本是什么样的。
通过如下方式,我们来可视化的展示训练集中前几个样本。
# 显示数据集
mnist_show = torchvision.datasets.MNIST(root="../data", train=True, transform=torchvision.transforms.ToTensor(), download=True)
images, label = next(iter(data.DataLoader(mnist_show, 20, shuffle=True)))
#多张图合并成一张图片
images_example = torchvision.utils.make_grid(images,nrow=5)
images_example = images_example.numpy().transpose(1,2,0) # 将图像的通道值置换到最后的维度,符合图像的格式
plt.imshow(images_example )
plt.show()
这里我们使用只有一个隐藏层的多层感知机(MLP)来进行手写数字的识别。
模型架构如下图所示。
这里定义了两层的的神经网络,第⼀层是隐藏层,它包含 256 个隐藏单元,并使⽤了 ReLU 激活函数。第⼆层是输出层。
#这里使用多层感知机来进行手写数字的识别
net = nn.Sequential(nn.Flatten(),
nn.Linear(784, 256),
nn.ReLU(),
nn.Linear(256, 10))
以均值为 0 和标准差为 0.1的高斯分布来随机初始化权重。
# PyTorch 不会隐式地调整输⼊的形状。因此,
# 我们在线性层前定义了展平层(flatten),来调整⽹络输⼊的形状
def init_weights(m):
if type(m) == nn.Linear:
#以均值为 0 和标准差为 0.1 随机初始化权重。
nn.init.normal_(m.weight, std=0.1)
由于是分类问题,所以损失函数使用交叉熵损失。
loss = nn.CrossEntropyLoss(reduction='none')
使用 SGD(批量梯度下降算法)来进行优化,这里设置学习率为0.01
trainer = torch.optim.SGD(net.parameters(), lr=0.01)
class Accumulator:
"""在n个变量上累加"""
def __init__(self, n):
self.data = [0.0] * n
def add(self, *args):
self.data = [a + float(b) for a, b in zip(self.data, args)]
def reset(self):
self.data = [0.0] * len(self.data)
def __getitem__(self, idx):
return self.data[idx]
print("随机初始化后,测试集的准确率为")
print(evaluate_accuracy(net, test_iter))
#训练
def train_epoch_ch3(net, train_iter, loss, updater):
"""训练模型⼀个迭代周期(定义⻅第3章)"""
# 将模型设置为训练模式
if isinstance(net, torch.nn.Module):
net.train()
# 训练损失总和、训练准确度总和、样本数
metric = Accumulator(3)
for X, y in train_iter:
#进行预测
y_hat = net(X)
#计算损失
l = loss(y_hat, y)
if isinstance(updater, torch.optim.Optimizer):
# 使⽤PyTorch内置的优化器和损失函数
updater.zero_grad()
l.mean().backward()
updater.step()
else: # 使⽤定制的优化器和损失函数
l.sum().backward()
updater(X.shape[0])
metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
# 返回训练损失和训练精度
return metric[0] / metric[2], metric[1] / metric[2]
train_losss=[]
train_accs=[]
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):
"""训练模型"""
for epoch in range(num_epochs):
train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
train_loss, train_acc = train_metrics
print(f'epoch {epoch + 1}, loss {float(train_loss):f}')
train_losss.append(train_loss)
train_accs.append(train_acc)
num_epochs = 15
print("开始训练:")
train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
输出:
随机初始化后,测试集的准确率为
0.0997
开始训练:
epoch 1, loss 1.719787
epoch 2, loss 1.028229
epoch 3, loss 0.708345
epoch 4, loss 0.633207
epoch 5, loss 0.545152
epoch 6, loss 0.517245
epoch 7, loss 0.435589
epoch 8, loss 0.378610
epoch 9, loss 0.398382
epoch 10, loss 0.355470
epoch 11, loss 0.292634
epoch 12, loss 0.303913
epoch 13, loss 0.297712
epoch 14, loss 0.316524
epoch 15, loss 0.227925
def show_image(num_epochs,train_ls,train_acc):
plt.plot(np.arange(1, num_epochs + 1), train_ls,label='train_loss')
plt.plot(np.arange(1, num_epochs + 1), train_acc,label='train_accuracy')
plt.xlabel('epoch')
plt.legend()
plt.show()
show_image(num_epochs,train_losss,train_accs)
test_acc = evaluate_accuracy(net, test_iter)
print(f'test_acc {float(test_acc):f}')
输出:
test_acc 0.910300
损失变化和准确率变化如下图所示。
今天简单介绍了一个如何用 Pytorch 训练一个简单的房价预测。