对卷积网络的形象理解:

卷积网络在形式上类似于人民代表大会制度,卷积核相当于候选人,多个卷积核就相当于多个候选人,图像中不同的特征会激活不同的卷积核,池化层起着类似于合票的作用,代表着一个区域的得分,全连接层相当于对每个区域选出来的代表进行最终的投票

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')

 

posted on 2022-11-09 21:28  yc-limitless  阅读(63)  评论(0)    收藏  举报