PyTorch Tutorial 14 - Convolutional N...
神秘网友泰勒
2023年02月17日 15:43

教程Python代码如下:

import torch

import torch.nn as nn

import torch.nn.functional as F

import torchvision

import torchvision.transforms as transforms

import matplotlib.pyplot as plt

import numpy as np

# Device configuration, 将程序迁移到GPU

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# Hyper-parameters, 定义超参数

num_epochs = 20

batch_size = 4

learning_rate = 0.001

"""加载数据集,数据集已经在 Pytorch 中提供,我们直接使用"""

# dataset has PILImage images of range [0, 1].

# We transform them to Tensors of normalized range [-1, 1]

transform = transforms.Compose(

  [transforms.ToTensor(),

   transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

"""定义 Pytorch 数据集"""

# CIFAR10: 60000 32x32 color images in 10 classes, with 6000 images per class

train_dataset = torchvision.datasets.CIFAR10(root='./Data', train=True,

                       download=True, transform=transform)

test_dataset = torchvision.datasets.CIFAR10(root='./Data', train=False,

                      download=True, transform=transform)

"""定义 Pytorch 数据加载器"""

train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size,

                      shuffle=True)

test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size,

                     shuffle=False)

classes = ('plane', 'car', 'bird', 'cat',

      'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

def imshow(img):

  img = img / 2 + 0.5 # unnormalize

  npimg = img.numpy()

  plt.imshow(np.transpose(npimg, (1, 2, 0)))

  plt.show()

# get some random training images

dataiter = iter(train_loader)

images, labels = next(dataiter)

# show images

imshow(torchvision.utils.make_grid(images))

# implem conv net, 定义卷积神经网络

class ConvNet(nn.Module):

  def __init__(self):

    super(ConvNet, self).__init__()

    self.conv1 = nn.Conv2d(3, 6, 5) # 第一个卷积层,输入通道:3(三个颜色通道),假设输出通道:6(),卷积核大小:5()

    self.pool = nn.MaxPool2d(2, 2)  # 池化层,最大池化,内核大小为2(2*2 Max-Pool),步幅为2

    self.conv2 = nn.Conv2d(6, 16, 5) # 第二个卷积层,输出通道:6(上一个输出通道的大小),假设输出通道:16(),卷积核大小:5()

    """为什么是 16*5*5, """

    self.fc1 = nn.Linear(16 * 5 * 5, 120) #第一个全连接层,输入特征:16*5*5(),假设输出特征:120

    self.fc2 = nn.Linear(120, 84)     #第二个全连接层,输入特征:120(上一层的输出特征),假设输出特征:84

    self.fc3 = nn.Linear(84, 10)      #最后一个全连接层,输入特征:84(上一层的输出特征),输出特征:10(项目中只有10个不同的类别)

  def forward(self, x):

    # -> n, 3, 32, 32

    x = self.pool(F.relu(self.conv1(x))) # -> n, 6, 14, 14 #​ 给第一个卷积层一个激活函数之后,令其进入池化层

    x = self.pool(F.relu(self.conv2(x))) # -> n, 16, 5, 5  #​ 给第二个卷积层一个激活函数之后,令其进入池化层

    x = x.view(-1, 16 * 5 * 5) # -> n, 400

    x = F.relu(self.fc1(x)) # -> n, 120

    x = F.relu(self.fc2(x)) # -> n, 84

    x = self.fc3(x) # -> n, 10

    return x

model = ConvNet().to(device)

# Loss and optimizer, 定义损失和优化器

"""这是一个多类分类问题,所以我们用交叉熵损失"""

criterion = nn.CrossEntropyLoss() # softmax已经包含在这里的交叉熵损失中

optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

n_total_steps = len(train_loader)

"""批量优化和批量训练"""

for epoch in range(num_epochs):

  for i, (images, labels) in enumerate(train_loader):

    # origin shape: [4, 3, 32, 32] = 4, 3, 1024

    # input_layer: 3 input channels, 6 output channels, 5 kernel size

    """这里把图像和标签加载到设备上,获得GPU支持"""

    images = images.to(device)

    labels = labels.to(device)

    # Forward pass

    """前向传递,制造loss"""

    outputs = model(images)

    loss = criterion(outputs, labels)

    # Backward and optimize

    """反向传递,不要忘记清空grad"""

    optimizer.zero_grad()

    loss.backward()

    optimizer.step()

    if (i + 1) % 2000 == 0:

      print(f'Epoch [{epoch + 1}/{num_epochs}], Step [{i + 1}/{n_total_steps}], Loss: {loss.item():.4f}')

print('Finished Training')

PATH = './cnn.pth'

torch.save(model.state_dict(), PATH)

"""模型评估,声明无梯度,因为我们不需要这里的反向传播和梯度计算"""

with torch.no_grad():

  n_correct = 0

  n_samples = 0

  n_class_correct = [0 for i in range(10)]

  n_class_samples = [0 for i in range(10)]

  for images, labels in test_loader:

    images = images.to(device)

    labels = labels.to(device)

    outputs = model(images)

    # max returns (value ,index)

    _, predicted = torch.max(outputs, 1)

    n_samples += labels.size(0)

    n_correct += (predicted == labels).sum().item()

    for i in range(batch_size):

      label = labels[i]

      pred = predicted[i]

      if (label == pred):

        n_class_correct[label] += 1

      n_class_samples[label] += 1

  acc = 100.0 * n_correct / n_samples

  print(f'Accuracy of the network: {acc} %')

  """计算每个类的累计精度"""

  for i in range(10):

    acc = 100.0 * n_class_correct[i] / n_class_samples[i]

    print(f'Accuracy of {classes[i]}: {acc} %')