对卷积网络的形象理解:
卷积网络在形式上类似于人民代表大会制度,卷积核相当于候选人,多个卷积核就相当于多个候选人,图像中不同的特征会激活不同的卷积核,池化层起着类似于合票的作用,代表着一个区域的得分,全连接层相当于对每个区域选出来的代表进行最终的投票
7.1.0 头文件
import torch
from torch import nn
from d2l import torch as d2l
import os
from matplotlib import pyplot as plt
7.1.1 定义LeNet网络模型
# 定义网络模型
net = nn.Sequential(
nn.Conv2d(1, 6, kernel_size=5, padding=2), #卷积层,输入通道为1,输出通道为6,卷积核尺寸5×5,上下左右填充2列,输出尺寸(1, 6, 28, 28)
# 卷积核的尺寸为(6,1,5,5)
nn.Sigmoid(), #输出尺寸为(1, 6, 28, 28)
nn.AvgPool2d(kernel_size=2, stride=2), #平均池化层,卷积核尺寸2×2,步幅为2×2,输出尺寸为(1, 6, 14, 14)
nn.Conv2d(6, 16, kernel_size=5), #卷积层,输入通道为6,输出通道为16,卷积核尺寸5×5,输出尺寸(1, 16, 10, 10)
# 卷积核的尺寸为(16,6,5,5)
nn.Sigmoid(), #输出尺寸为(1, 16, 10, 10)
nn.AvgPool2d(kernel_size=2, stride=2), #平均池化层,卷积核尺寸2×2,步幅为2×2,输出尺寸为(1, 16, 5, 5)
nn.Flatten(), #输出尺寸为(1, 400)
nn.Linear(16 * 5 * 5, 120), #输出尺寸为(1, 120)
nn.Sigmoid(), #输出尺寸为(1, 120)
nn.Linear(120, 84), #输出尺寸为(1, 84)
nn.Sigmoid(), #输出尺寸为(1, 84)
nn.Linear(84, 10)) #输出尺寸为(1, 10)
# 定义输入特征X,X的尺寸为(批量大小:1,通道数:1,行数:28,列数:28)
X = torch.rand(size=(1, 1, 28, 28), dtype=torch.float32)
for layer in net:
X = layer(X)
print(layer.__class__.__name__,'output shape: \t',X.shape)
print(X)
# 输出:
# Conv2d output shape: torch.Size([1, 6, 28, 28])
# Sigmoid output shape: torch.Size([1, 6, 28, 28])
# AvgPool2d output shape: torch.Size([1, 6, 14, 14])
# Conv2d output shape: torch.Size([1, 16, 10, 10])
# Sigmoid output shape: torch.Size([1, 16, 10, 10])
# AvgPool2d output shape: torch.Size([1, 16, 5, 5])
# Flatten output shape: torch.Size([1, 400])
# Linear output shape: torch.Size([1, 120])
# Sigmoid output shape: torch.Size([1, 120])
# Linear output shape: torch.Size([1, 84])
# Sigmoid output shape: torch.Size([1, 84])
# Linear output shape: torch.Size([1, 10])
# tensor([[-0.5163, 0.2096, -0.0048, 0.3140, -0.2729, 0.1939, -0.0122, 0.0453,
# 0.1425, 0.5025]], grad_fn=<AddmmBackward0>)
7.1.2 下载fashion_mnist数据集
# 定义批量大小
batch_size = 4
# 下载fashion_mnist,并对数据集进行打乱和按批量大小进行切割的操作,得到可迭代的训练集和测试集(训练集和测试集的形式都为(特征数据集合,数字标签集合))
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size)
7.1.3 模型预测准确率计算
# 每经过一轮训练,调用该函数,计算更新参数后的网络模型在测试集上的准确率
# net:网络模型 data_iter:测试集
def evaluate_accuracy_gpu(net, data_iter, device=None):
"""使用GPU计算模型在数据集上的精度"""
if isinstance(net, nn.Module):
net.eval() # 设置为评估模式
if not device:
device = next(iter(net.parameters())).device
# 定义一个累加器,累加器中累加了正确预测的数量、预测的总样本数
metric = d2l.Accumulator(2)
with torch.no_grad():
# 对于测试集中的没一个批量
for X, y in data_iter:
if isinstance(X, list):
# BERT微调所需的(之后将介绍)
X = [x.to(device) for x in X]
else:
X = X.to(device)
# 把特征集合X和标签集合y放在GPU上
y = y.to(device)
# 计算当前批量中在网络模型上预测正确的样本数,和当前批次总样本数,并累加
metric.add(d2l.accuracy(net(X), y), y.numel())
# 返回网络模型在测试集上的准确率
return metric[0] / metric[1]
7.1.4 训练过程
# train_iter:经过打乱并且按照批量大小进行分割后的训练集,格式为(特征数据集合,数字标签集合)
def train_ch6(net, train_iter, test_iter, num_epochs, lr, device):
"""用GPU训练模型(在第六章定义)"""
# 初始化全连接层和卷积层的权重w(从以0为中心,标准差为std = sqrt(2/(fan_in + fan_out))的正态分布中随机取样,其中fan_in为输入神经元的个数,fan_out为输出神经元的个数)
def init_weights(m):
if type(m) == nn.Linear or type(m) == nn.Conv2d:
nn.init.xavier_uniform_(m.weight)
net.apply(init_weights)
print('training on', device)
# 将模型参数放在GPU上
net.to(device)
# 定义梯度下降优化器
optimizer = torch.optim.SGD(net.parameters(), lr=lr)
# 定义交叉熵损失函数
loss = nn.CrossEntropyLoss()
# 定义动画,用来显示训练图像
animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs], legend=['train loss', 'train acc', 'test acc'])
# 定义批量大小、定时器
timer, num_batches = d2l.Timer(), len(train_iter)
# 对于每一轮训练过程
for epoch in range(num_epochs):
# 定义一个累加器,累加器中累加了当前轮的训练损失之和、当前轮的训练准确率之和、当前轮的训练样本数
metric = d2l.Accumulator(3)
net.train()
# 对于每一个批量
for i, (X, y) in enumerate(train_iter):
# 开始计时
timer.start()
optimizer.zero_grad()
# 把当前批量的特征集合X和标签集合y放在GPU上
X, y = X.to(device), y.to(device)
# 获得模型输出的预测标签
y_hat = net(X)
# 计算当前批量的平均损失
l = loss(y_hat, y)
# 用优化器更新权重参数
l.backward()
optimizer.step()
with torch.no_grad():
# 对当前轮的训练损失之和、当前轮的训练准确率之和、当前轮的训练样本数进行累加
metric.add(l * X.shape[0], d2l.accuracy(y_hat, y), X.shape[0])
# 记录当前批量的训练时间
timer.stop()
# 记录在一轮的训练过程中,平均损失和平均准确率的变化
train_l = metric[0] / metric[2]
train_acc = metric[1] / metric[2]
if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:
animator.add(epoch + (i + 1) / num_batches,(train_l, train_acc, None))
# 经过一轮训练,计算更新参数后的网络模型在测试集上的平均准确率
test_acc = evaluate_accuracy_gpu(net, test_iter)
# 更新动画
animator.add(epoch + 1, (None, None, test_acc))
# 输出最后一轮训练时的平均损失、在训练集上的平均准确率、在测试集上的平均准测率
print(f'loss {train_l:.3f}, train acc {train_acc:.3f}, 'f'test acc {test_acc:.3f}')
# 输出每秒能够训练多少张图像
print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec 'f'on {str(device)}')
# 定义学习率和迭代次数
lr, num_epochs = 0.9, 10
train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
# 输出:
# loss 0.338, train acc 0.875, test acc 0.867
# 1095.7 examples/sec on cuda:0
7.1.5 训练结果可视化
plt.savefig('OutPut.png')
本小节完整代码如下
import torch
from torch import nn
from d2l import torch as d2l
import os
from matplotlib import pyplot as plt
# ------------------------------定义LeNet网络模型------------------------------------
# 定义网络模型
net = nn.Sequential(
nn.Conv2d(1, 6, kernel_size=5, padding=2), #卷积层,输入通道为1,输出通道为6,卷积核尺寸5×5,上下左右填充2列,输出尺寸(1, 6, 28, 28)
# 卷积核的尺寸为(6,1,5,5)
nn.Sigmoid(), #输出尺寸为(1, 6, 28, 28)
nn.AvgPool2d(kernel_size=2, stride=2), #平均池化层,卷积核尺寸2×2,步幅为2×2,输出尺寸为(1, 6, 14, 14)
nn.Conv2d(6, 16, kernel_size=5), #卷积层,输入通道为6,输出通道为16,卷积核尺寸5×5,输出尺寸(1, 16, 10, 10)
# 卷积核的尺寸为(16,6,5,5)
nn.Sigmoid(), #输出尺寸为(1, 16, 10, 10)
nn.AvgPool2d(kernel_size=2, stride=2), #平均池化层,卷积核尺寸2×2,步幅为2×2,输出尺寸为(1, 16, 5, 5)
nn.Flatten(), #输出尺寸为(1, 400)
nn.Linear(16 * 5 * 5, 120), #输出尺寸为(1, 120)
nn.Sigmoid(), #输出尺寸为(1, 120)
nn.Linear(120, 84), #输出尺寸为(1, 84)
nn.Sigmoid(), #输出尺寸为(1, 84)
nn.Linear(84, 10)) #输出尺寸为(1, 10)
# 定义输入特征X,X的尺寸为(批量大小:1,通道数:1,行数:28,列数:28)
X = torch.rand(size=(1, 1, 28, 28), dtype=torch.float32)
for layer in net:
X = layer(X)
print(layer.__class__.__name__,'output shape: \t',X.shape)
print(X)
# 输出:
# Conv2d output shape: torch.Size([1, 6, 28, 28])
# Sigmoid output shape: torch.Size([1, 6, 28, 28])
# AvgPool2d output shape: torch.Size([1, 6, 14, 14])
# Conv2d output shape: torch.Size([1, 16, 10, 10])
# Sigmoid output shape: torch.Size([1, 16, 10, 10])
# AvgPool2d output shape: torch.Size([1, 16, 5, 5])
# Flatten output shape: torch.Size([1, 400])
# Linear output shape: torch.Size([1, 120])
# Sigmoid output shape: torch.Size([1, 120])
# Linear output shape: torch.Size([1, 84])
# Sigmoid output shape: torch.Size([1, 84])
# Linear output shape: torch.Size([1, 10])
# tensor([[-0.5163, 0.2096, -0.0048, 0.3140, -0.2729, 0.1939, -0.0122, 0.0453,
# 0.1425, 0.5025]], grad_fn=<AddmmBackward0>)
# ------------------------------下载fashion_mnist数据集------------------------------------
# 定义批量大小
batch_size = 4
# 下载fashion_mnist,并对数据集进行打乱和按批量大小进行切割的操作,得到可迭代的训练集和测试集(训练集和测试集的形式都为(特征数据集合,数字标签集合))
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size)
# ------------------------------模型预测准确率计算------------------------------------
# 每经过一轮训练,调用该函数,计算更新参数后的网络模型在测试集上的准确率
# net:网络模型 data_iter:测试集
def evaluate_accuracy_gpu(net, data_iter, device=None):
"""使用GPU计算模型在数据集上的精度"""
if isinstance(net, nn.Module):
net.eval() # 设置为评估模式
if not device:
device = next(iter(net.parameters())).device
# 定义一个累加器,累加器中累加了正确预测的数量、预测的总样本数
metric = d2l.Accumulator(2)
with torch.no_grad():
# 对于测试集中的没一个批量
for X, y in data_iter:
if isinstance(X, list):
# BERT微调所需的(之后将介绍)
X = [x.to(device) for x in X]
else:
X = X.to(device)
# 把特征集合X和标签集合y放在GPU上
y = y.to(device)
# 计算当前批量中在网络模型上预测正确的样本数,和当前批次总样本数,并累加
metric.add(d2l.accuracy(net(X), y), y.numel())
# 返回网络模型在测试集上的准确率
return metric[0] / metric[1]
# ------------------------------训练过程------------------------------------
# train_iter:经过打乱并且按照批量大小进行分割后的训练集,格式为(特征数据集合,数字标签集合)
def train_ch6(net, train_iter, test_iter, num_epochs, lr, device):
"""用GPU训练模型(在第六章定义)"""
# 初始化全连接层和卷积层的权重w(从以0为中心,标准差为std = sqrt(2/(fan_in + fan_out))的正态分布中随机取样,其中fan_in为输入神经元的个数,fan_out为输出神经元的个数)
def init_weights(m):
if type(m) == nn.Linear or type(m) == nn.Conv2d:
nn.init.xavier_uniform_(m.weight)
net.apply(init_weights)
print('training on', device)
# 将模型参数放在GPU上
net.to(device)
# 定义梯度下降优化器
optimizer = torch.optim.SGD(net.parameters(), lr=lr)
# 定义交叉熵损失函数
loss = nn.CrossEntropyLoss()
# 定义动画,用来显示训练图像
animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs], legend=['train loss', 'train acc', 'test acc'])
# 定义批量大小、定时器
timer, num_batches = d2l.Timer(), len(train_iter)
# 对于每一轮训练过程
for epoch in range(num_epochs):
# 定义一个累加器,累加器中累加了当前轮的训练损失之和、当前轮的训练准确率之和、当前轮的训练样本数
metric = d2l.Accumulator(3)
net.train()
# 对于每一个批量
for i, (X, y) in enumerate(train_iter):
# 开始计时
timer.start()
optimizer.zero_grad()
# 把当前批量的特征集合X和标签集合y放在GPU上
X, y = X.to(device), y.to(device)
# 获得模型输出的预测标签
y_hat = net(X)
# 计算当前批量的平均损失
l = loss(y_hat, y)
# 用优化器更新权重参数
l.backward()
optimizer.step()
with torch.no_grad():
# 对当前轮的训练损失之和、当前轮的训练准确率之和、当前轮的训练样本数进行累加
metric.add(l * X.shape[0], d2l.accuracy(y_hat, y), X.shape[0])
# 记录当前批量的训练时间
timer.stop()
# 记录在一轮的训练过程中,平均损失和平均准确率的变化
train_l = metric[0] / metric[2]
train_acc = metric[1] / metric[2]
if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:
animator.add(epoch + (i + 1) / num_batches,(train_l, train_acc, None))
# 经过一轮训练,计算更新参数后的网络模型在测试集上的平均准确率
test_acc = evaluate_accuracy_gpu(net, test_iter)
# 更新动画
animator.add(epoch + 1, (None, None, test_acc))
# 输出最后一轮训练时的平均损失、在训练集上的平均准确率、在测试集上的平均准测率
print(f'loss {train_l:.3f}, train acc {train_acc:.3f}, 'f'test acc {test_acc:.3f}')
# 输出每秒能够训练多少张图像
print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec 'f'on {str(device)}')
# 定义学习率和迭代次数
lr, num_epochs = 0.9, 10
train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
# 输出:
# loss 0.338, train acc 0.875, test acc 0.867
# 1095.7 examples/sec on cuda:0
# ------------------------------训练结果可视化------------------------------------
plt.savefig('OutPut.png')
浙公网安备 33010602011771号