卷积神经网络VGG16 + Cifar10 + Pytorch 深度学习实战

本文使用 pytorchpytorchpytorchCifar10Cifar10Cifar10 数据集完成深度学习实战

网络结构使用 VGG16VGG16VGG16

img

img

导入库

import torch
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import torch.nn as nn
import matplotlib.pyplot as plt
import numpy as np

对图片进行多种预处理操作

# 对训练集图片进行多种预处理操作
train_transform = transforms.Compose([
    transforms.Resize([224, 224])
    # 将图片数组转成张量
    transforms.ToTensor(),  
    # 归一化操作
    transforms.Normalize((0.5,), (0.5,))  
])

# 对测试集图片进行多种预处理操作
test_transform = transforms.Compose([
    transforms.Resize([224, 224]),
    # 将图片数组转成张量
    transforms.ToTensor(),  
    # 归一化操作
    transforms.Normalize((0.5,), (0.5,))  
])

数据加载 (方法有别)

# 将路径下的图片自动加载
# 训练集的加载设置train项为True,download设置为True后,自动从网络下载并解压
train_dataset = datasets.CIFAR10(root="./data", train=True, download=True, transform=train_transform)
test_dataset = datasets.CIFAR10(root="./data", train=False, download=True, transform=test_transform)
# 定义数据加载器
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
# shuffle 用来打乱数据的顺序,防止过拟合提高模型的鲁棒性

查看数据集图片

for i, (images, _) in enumerate(train_loader):
    # print(images.shape)
    # 把一批数据的图片组成一张图片
    img = torchvision.utils.make_grid(images)
    # 调整图片维度,将通道数放在最后一维
    img = np.array(img).transpose(1, 2, 0)
    plt.imshow(img)
    plt.show()
    break

设备检查

# 定义训练设备, 检查是否使用GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

定义超参

# 批大小,训练时每次输入到模型的数据的数量
batch_size = 128
# 定义学习率0.001
learning_rate = 1e-3
# 训练循环次数
epochs = 10
# 分类数
num_classes = 10

定义随机数种子以确保可重复性,设置CPU生成随机数的种子,方便下次复现实验结果

seed = 42
torch.manual_seed(seed)

定义模型结构 VGG16

class CNNNeuralNetwork(nn.Module):
    # 构造函数
    def __init__(self):
        # 访问父类的构造方法
        super().__init__()
        # Flatten层用来将二维图片reshape为一维向量
        self.flatten = nn.Flatten()
        # 在构造方法里,定义网络的结构。Sequential是一种容器,允许用户按顺序去定义神经网络的各个层
        # 卷积部分提取特征
        self.cnn_layer = nn.Sequential(
            # Conv1
            nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2),

            # Conv2
            nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2),

            # Conv3
            nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2),

            # Conv4
            nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2),

            # Conv5
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2)
        
        # 全连接层,进行分类
        self.fc_layer = nn.Sequential(
            nn.Linear(in_features=1 * 1 * 25088, out_features=1 * 1 * 4096),
            nn.ReLU(),
            #nn.Linear(in_features=7 * 7 * 512, out_features=1 * 1 * 4096)
            # 随机丢弃,缓解过拟合,参数为丢弃的概率
            nn.Dropout(0.5),
            nn.Linear(in_features=1 * 1 * 4096, out_features=1 * 1 * 4096),
            nn.ReLU(),
            nn.Dropout(0.5),
            nn.Linear(in_features=4096, out_features=num_classes)
        )

    # 定义前向传播的过程,x是输入模型的数据
    def forward(self, x):
        x = self.cnn_layer(x)
        # view方法类似 numpy 的 reshape,用于改变张量的形状
        x = x.view(-1, 7* 7* 512)
        # 或写为 x = self.flatten(x)
        # logits用来描述模型未经处理(未经过激活层处理)的输出值
        logits = self.fc_layer(x)
        return logits

实例化模型

# 实例化模型
model = CNNNeuralNetwork().to(device)

定义损失函数和优化器

# 定义目标函数,使用交叉熵函数作为目标函数,即损失函数
loss_fn = nn.CrossEntropyLoss()
# 定义优化器(反向传播——随机梯度下降的实现)
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

定义训练过程

def train(dataloader, model, loss_fn, optimizer):
    # 训练集大小
    size = len(dataloader.dataset)
    model.train()
    for batch, (X, y) in enumerate(dataloader):
        X, y = X.to(device), y.to(device)
        # 计算预测值
        predict = model(X)
        # 计算损失值
        loss = loss_fn(predict, y)
        # 反向传播, backward()是用于自动计算梯度并进行反向传播的方法
        loss.backward()
        # 更新神经网络模型中的参数
        optimizer.step()
        # 清楚之前的计算梯度, torch中的梯度计算时,若不进行清除会导致梯度累加
        optimizer.zero_grad()

        # 显示当前训练了多少数据
        if batch % 100 == 0:
            loss, current = loss.item(), (batch + 1) * len(X)
            print(f"loss:{loss:>7f} [{current:>5d}/{size:>5d}]")

定义测试函数

def test(dataloader, model, loss_fn):
    # 训练集大小
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    # 设置为评估模式
    model.eval()
    test_loss, correct = 0, 0
    # 测试过程中不再进行梯度计算
    with torch.no_grad():
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            # 计算预测值
            predict = model(X)
            # 计算整个数据集总的loss
            test_loss += loss_fn(predict, y).item()
            # 计算总正确率, argmax返回最大值的索引
            correct += (predict.argmax(1) == y).type(torch.float).sum().item()
            test_loss /= num_batches
            correct /= size
            print(f"Test Error: \nAccuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
            return correct, test_loss

绘制图像

# 存储迭代次数
iterations = []
accuracies = []
losses = []

# 初始化图形
plt.ion()
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))

ax1.set_title("Accuracy over iterations")
ax1.set_xlabel("Iterations")
ax1.set_ylabel("Accuracy")
accuracy_line, = ax1.plot([], [], 'b')

ax2.set_title("Loss over iterations")
ax2.set_xlabel("Iterations")
ax2.set_ylabel("Loss")
loss_line, = ax2.plot([], [], 'r')


# 实时更新图形的函数
def update_plot(iteration, accuracy, loss):
    # 添加元素到列表的最后面
    iterations.append(iteration)
    accuracies.append(accuracy)
    losses.append(loss)

    # 更新数据
    accuracy_line.set_data(iterations, accuracies)
    loss_line.set_data(iterations, losses)

    # 更新坐标轴范围
    ax1.set_xlim(0, max(iterations))
    ax1.set_ylim(0, 1)
    ax2.set_xlim(0, max(iterations))
    ax2.set_ylim(0, max(losses) if losses else 1)

    plt.draw()
    plt.pause(0.1)

运行测试 traintraintraintesttesttest

if __name__ == '__main__':
    for i in range(epochs):
        print(f"Epoch {i+1}\n--------------------------")
        train(train_loader, model, loss_fn, optimizer)
        acc, loss = test(test_loader, model, loss_fn)
        update_plot(i, acc, loss)
    plt.show()
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐