OUC23暑期培训——深度学习基础【第二周】

目录

第一部分 代码练习

1.1 MNIST 数据集分类

1.2 CIFAR10 数据集分类

1.3使用 VGG16 对 CIFAR10 分类

 第二部分 问题总结

代码来源

第一部分 代码练习

1.1 MNIST 数据集分类

  • 使用 PyTorch 进行CNN的训练与测试
  • 展示池化与卷积操作的作用

1.导入需要的一些包和定义函数

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import matplotlib.pyplot as plt
import numpy

# 一个函数,用来计算模型中有多少参数
def get_n_params(model):
    np=0
    for p in list(model.parameters()):
        np += p.nelement()
    return np

# 使用GPU训练,可以在菜单 "代码执行工具" -> "更改运行时类型" 里进行设置
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

  这段代码是用PyTorch框架来导入一些必要的库和模块,以及定义一些通用的函数和变量,用来进行神经网络的训练

2.加载数据 (MNIST)

input_size  = 28*28   # MNIST上的图像尺寸是 28x28
output_size = 10      # 类别为 0 到 9 的数字,因此为十类

train_loader = torch.utils.data.DataLoader(
    datasets.MNIST('./data', train=True, download=True,
        transform=transforms.Compose(
            [transforms.ToTensor(),
             transforms.Normalize((0.1307,), (0.3081,))])),
    batch_size=64, shuffle=True)

test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('./data', train=False, transform=transforms.Compose([
             transforms.ToTensor(),
             transforms.Normalize((0.1307,), (0.3081,))])),
    batch_size=1000, shuffle=True)

   

  这段代码是用PyTorch框架来加载和处理MNIST数据集的,MNIST数据集是一个包含了手写数字图片和标签的数据集,常用来进行图像分类任务,上述代码执行完成后,可在文件栏查看加载进来的数据

plt.figure(figsize=(8, 5))
for i in range(20):
    plt.subplot(4, 5, i + 1)
    image, _ = train_loader.dataset.__getitem__(i)
    plt.imshow(image.squeeze().numpy(),'gray')
    plt.axis('off');

这段代码是用matplotlib.pyplot模块来绘制和显示训练集中的前20张图片的,执行上述代码,可得到数据集中4行5列,长度为8宽度为5的20张图像,结果如下面所示

3.创建网络

class FC2Layer(nn.Module):
    def __init__(self, input_size, n_hidden, output_size):
        # nn.Module子类的函数必须在构造函数中执行父类的构造函数
        # 下式等价于nn.Module.__init__(self)
        super(FC2Layer, self).__init__()
        self.input_size = input_size
        # 这里直接用 Sequential 就定义了网络,注意要和下面 CNN 的代码区分开
        self.network = nn.Sequential(
            nn.Linear(input_size, n_hidden),
            nn.ReLU(),
            nn.Linear(n_hidden, n_hidden),
            nn.ReLU(),
            nn.Linear(n_hidden, output_size),
            nn.LogSoftmax(dim=1)
        )
    def forward(self, x):
        # view一般出现在model类的forward函数中,用于改变输入或输出的形状
        # x.view(-1, self.input_size) 的意思是多维的数据展成二维
        # 代码指定二维数据的列数为 input_size=784,行数 -1 表示我们不想算,电脑会自己计算对应的数字
        # 在 DataLoader 部分,我们可以看到 batch_size 是64,所以得到 x 的行数是64
        # 大家可以加一行代码:print(x.cpu().numpy().shape)
        # 训练过程中,就会看到 (64, 784) 的输出,和我们的预期是一致的

        # forward 函数的作用是,指定网络的运行过程,这个全连接网络可能看不啥意义,
        # 下面的CNN网络可以看出 forward 的作用。
        x = x.view(-1, self.input_size)
        return self.network(x)



class CNN(nn.Module):
    def __init__(self, input_size, n_feature, output_size):
        # 执行父类的构造函数,所有的网络都要这么写
        super(CNN, self).__init__()
        # 下面是网络里典型结构的一些定义,一般就是卷积和全连接
        # 池化、ReLU一类的不用在这里定义
        self.n_feature = n_feature
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=n_feature, kernel_size=5)
        self.conv2 = nn.Conv2d(n_feature, n_feature, kernel_size=5)
        self.fc1 = nn.Linear(n_feature*4*4, 50)
        self.fc2 = nn.Linear(50, 10)

    # 下面的 forward 函数,定义了网络的结构,按照一定顺序,把上面构建的一些结构组织起来
    # 意思就是,conv1, conv2 等等的,可以多次重用
    def forward(self, x, verbose=False):
        x = self.conv1(x)
        x = F.relu(x)
        x = F.max_pool2d(x, kernel_size=2)
        x = self.conv2(x)
        x = F.relu(x)
        x = F.max_pool2d(x, kernel_size=2)
        x = x.view(-1, self.n_feature*4*4)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.fc2(x)
        x = F.log_softmax(x, dim=1)
        return x

  这两段代码都是用PyTorch框架来定义神经网络模型的,第一段代码定义了一个两层的全连接网络(FC2Layer),第二段代码定义了一个卷积神经网络(CNN)

4.定义训练和解释函数

# 训练函数
def train(model):
    model.train()
    # 主里从train_loader里,64个样本一个batch为单位提取样本进行训练
    for batch_idx, (data, target) in enumerate(train_loader):
        # 把数据送到GPU中
        data, target = data.to(device), target.to(device)

        optimizer.zero_grad()
        output = model(data)
        loss = F.nll_loss(output, target)
        loss.backward()
        optimizer.step()
        if batch_idx % 100 == 0:
            print('Train: [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.item()))


def test(model):
    model.eval()
    test_loss = 0
    correct = 0
    for data, target in test_loader:
        # 把数据送到GPU中
        data, target = data.to(device), target.to(device)
        # 把数据送入模型,得到预测结果
        output = model(data)
        # 计算本次batch的损失,并加到 test_loss 中
        test_loss += F.nll_loss(output, target, reduction='sum').item()
        # get the index of the max log-probability,最后一层输出10个数,
        # 值最大的那个即对应着分类结果,然后把分类结果保存在 pred 里
        pred = output.data.max(1, keepdim=True)[1]
        # 将 pred 与 target 相比,得到正确预测结果的数量,并加到 correct 中
        # 这里需要注意一下 view_as ,意思是把 target 变成维度和 pred 一样的意思                                                
        correct += pred.eq(target.data.view_as(pred)).cpu().sum().item()

    test_loss /= len(test_loader.dataset)
    accuracy = 100. * correct / len(test_loader.dataset)
    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        accuracy))

  这两个函数都是用PyTorch框架来进行神经网络模型的训练和测试的,第一个函数train用来对模型进行一次训练迭代,第二个函数test用来对模型进行一次测试评估

5.在小型全连接网络上和卷积神经网络上训练

n_hidden = 8 # number of hidden units

model_fnn = FC2Layer(input_size, n_hidden, output_size)
model_fnn.to(device)
optimizer = optim.SGD(model_fnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_fnn)))

train(model_fnn)
test(model_fnn)

  这段代码是用PyTorch框架来创建和训练一个两层的全连接网络模型(FC2Layer)的,用来对MNIST数据集进行图像分类任务,结果如下图所示


# Training settings
n_features = 6 # number of feature maps

model_cnn = CNN(input_size, n_features, output_size)
model_cnn.to(device)
optimizer = optim.SGD(model_cnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_cnn)))

train(model_cnn)
test(model_cnn)

  这段代码是用PyTorch框架来创建和训练一个卷积神经网络模型(CNN)的,用来对MNIST数据集进行图像分类任务,结果如下面所示

   通过上面的测试结果,可以发现,含有相同参数的 CNN 效果要明显优于 简单的全连接网络

6.打乱像素顺序再次在两个网络上训练与测试

# 这里解释一下 torch.randperm 函数,给定参数n,返回一个从0到n-1的随机整数排列
perm = torch.randperm(784)
plt.figure(figsize=(8, 4))
for i in range(10):
    image, _ = train_loader.dataset.__getitem__(i)
    # permute pixels
    image_perm = image.view(-1, 28*28).clone()
    image_perm = image_perm[:, perm]
    image_perm = image_perm.view(-1, 1, 28, 28)
    plt.subplot(4, 5, i + 1)
    plt.imshow(image.squeeze().numpy(), 'gray')
    plt.axis('off')
    plt.subplot(4, 5, i + 11)
    plt.imshow(image_perm.squeeze().numpy(), 'gray')
    plt.axis('off')

  这段代码是用PyTorch框架和matplotlib.pyplot模块来对训练集中的前10张图片进行随机像素置换,并绘制和显示原图片和置换后的图片的,结果如下图所示


# 对每个 batch 里的数据,打乱像素顺序的函数
def perm_pixel(data, perm):
    # 转化为二维矩阵
    data_new = data.view(-1, 28*28)
    # 打乱像素顺序
    data_new = data_new[:, perm]
    # 恢复为原来4维的 tensor
    data_new = data_new.view(-1, 1, 28, 28)
    return data_new

# 训练函数
def train_perm(model, perm):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        # 像素打乱顺序
        data = perm_pixel(data, perm)

        optimizer.zero_grad()
        output = model(data)
        loss = F.nll_loss(output, target)
        loss.backward()
        optimizer.step()
        if batch_idx % 100 == 0:
            print('Train: [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.item()))

# 测试函数
def test_perm(model, perm):
    model.eval()
    test_loss = 0
    correct = 0
    for data, target in test_loader:
        data, target = data.to(device), target.to(device)

        # 像素打乱顺序
        data = perm_pixel(data, perm)

        output = model(data)
        test_loss += F.nll_loss(output, target, reduction='sum').item()
        pred = output.data.max(1, keepdim=True)[1]                                            
        correct += pred.eq(target.data.view_as(pred)).cpu().sum().item()

    test_loss /= len(test_loader.dataset)
    accuracy = 100. * correct / len(test_loader.dataset)
    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        accuracy))

  这两个函数都是用PyTorch框架来进行神经网络模型的训练和测试的,不同于之前的函数,这两个函数在训练和测试之前,对每个批次的数据进行了随机像素置换,以增加数据的难度和多样性

  从打乱像素顺序的实验结果来看,全连接网络的性能基本上没有发生变化,但是 卷积神经网络的性能明显下降。这是因为对于卷积神经网络,会利用像素的局部关系,但是打乱顺序以后,这些像素间的关系将无法得到利用。

1.2 CIFAR10 数据集分类

1.加载CIFAR10数据集

import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

# 使用GPU训练,可以在菜单 "代码执行工具" -> "更改运行时类型" 里进行设置
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

# 注意下面代码中:训练的 shuffle 是 True,测试的 shuffle 是 false
# 训练时可以打乱顺序增加多样性,测试是没有必要
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64,
                                          shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=8,
                                         shuffle=False, num_workers=2)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

  这段代码是用PyTorch框架和torchvision库来加载和处理CIFAR-10数据集的,CIFAR-10数据集是一个包含了10个类别的彩色图片和标签的数据集,常用来进行图像分类任务。

2.部分数据集展示

   实验时这里报错,因为pyTorch版本问题,如无法顺利运行改为如下代码即可

def imshow(img):
    plt.figure(figsize=(8,8))
    img = img / 2 + 0.5     # 转换到 [0,1] 之间
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()
# 得到一组图像
images,labels = next(iter(trainloader))
# 展示图像
imshow(torchvision.utils.make_grid(images))
# 展示第一行图像的标签
for j in range(8):
    print(classes[labels[j]])

  这段代码是用matplotlib.pyplot模块和torchvision库来绘制和显示训练集中的一组图片和标签的,结果如下图所示

 3.定义和创建神经网络

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# 网络放到GPU上
net = Net().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=0.001)

  这段代码是用PyTorch框架来定义和创建一个卷积神经网络模型(Net)的,用来对CIFAR-10数据集进行图像分类任务

4.训练网络

for epoch in range(10):  # 重复多轮训练
    for i, (inputs, labels) in enumerate(trainloader):
        inputs = inputs.to(device)
        labels = labels.to(device)
        # 优化器梯度归零
        optimizer.zero_grad()
        # 正向传播 + 反向传播 + 优化 
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        # 输出统计信息
        if i % 100 == 0:   
            print('Epoch: %d Minibatch: %5d loss: %.3f' %(epoch + 1, i + 1, loss.item()))

print('Finished Training')

  这段代码是用PyTorch框架来对之前创建的卷积神经网络模型(net)进行多轮训练的,用来对CIFAR-10数据集进行图像分类任务。训练结果如下所示

Epoch: 1 Minibatch:     1 loss: 2.307
Epoch: 1 Minibatch:   101 loss: 2.015
Epoch: 1 Minibatch:   201 loss: 1.888
Epoch: 1 Minibatch:   301 loss: 1.704
Epoch: 1 Minibatch:   401 loss: 1.523
Epoch: 1 Minibatch:   501 loss: 1.413
Epoch: 1 Minibatch:   601 loss: 1.731
Epoch: 1 Minibatch:   701 loss: 1.524
Epoch: 2 Minibatch:     1 loss: 1.507
Epoch: 2 Minibatch:   101 loss: 1.355
Epoch: 2 Minibatch:   201 loss: 1.393
Epoch: 2 Minibatch:   301 loss: 1.438
Epoch: 2 Minibatch:   401 loss: 1.309
Epoch: 2 Minibatch:   501 loss: 1.296
Epoch: 2 Minibatch:   601 loss: 1.380
Epoch: 2 Minibatch:   701 loss: 1.538
Epoch: 3 Minibatch:     1 loss: 1.335
Epoch: 3 Minibatch:   101 loss: 1.211
Epoch: 3 Minibatch:   201 loss: 1.419
Epoch: 3 Minibatch:   301 loss: 1.148
Epoch: 3 Minibatch:   401 loss: 1.133
Epoch: 3 Minibatch:   501 loss: 1.249
Epoch: 3 Minibatch:   601 loss: 1.142
Epoch: 3 Minibatch:   701 loss: 1.230
Epoch: 4 Minibatch:     1 loss: 1.225
Epoch: 4 Minibatch:   101 loss: 1.277
Epoch: 4 Minibatch:   201 loss: 1.152
Epoch: 4 Minibatch:   301 loss: 1.085
Epoch: 4 Minibatch:   401 loss: 1.123
Epoch: 4 Minibatch:   501 loss: 1.040
Epoch: 4 Minibatch:   601 loss: 1.307
Epoch: 4 Minibatch:   701 loss: 0.991
Epoch: 5 Minibatch:     1 loss: 1.151
Epoch: 5 Minibatch:   101 loss: 1.225
Epoch: 5 Minibatch:   201 loss: 0.893
Epoch: 5 Minibatch:   301 loss: 1.249
Epoch: 5 Minibatch:   401 loss: 1.270
Epoch: 5 Minibatch:   501 loss: 0.985
Epoch: 5 Minibatch:   601 loss: 0.970
Epoch: 5 Minibatch:   701 loss: 1.106
Epoch: 6 Minibatch:     1 loss: 1.066
Epoch: 6 Minibatch:   101 loss: 0.891
Epoch: 6 Minibatch:   201 loss: 0.896
Epoch: 6 Minibatch:   301 loss: 1.172
Epoch: 6 Minibatch:   401 loss: 1.276
Epoch: 6 Minibatch:   501 loss: 1.054
Epoch: 6 Minibatch:   601 loss: 1.295
Epoch: 6 Minibatch:   701 loss: 1.016
Epoch: 7 Minibatch:     1 loss: 1.098
Epoch: 7 Minibatch:   101 loss: 1.050
Epoch: 7 Minibatch:   201 loss: 0.919
Epoch: 7 Minibatch:   301 loss: 0.875
Epoch: 7 Minibatch:   401 loss: 1.177
Epoch: 7 Minibatch:   501 loss: 1.093
Epoch: 7 Minibatch:   601 loss: 1.019
Epoch: 7 Minibatch:   701 loss: 0.994
Epoch: 8 Minibatch:     1 loss: 1.157
Epoch: 8 Minibatch:   101 loss: 1.296
Epoch: 8 Minibatch:   201 loss: 0.756
Epoch: 8 Minibatch:   301 loss: 0.782
Epoch: 8 Minibatch:   401 loss: 0.924
Epoch: 8 Minibatch:   501 loss: 0.861
Epoch: 8 Minibatch:   601 loss: 0.864
Epoch: 8 Minibatch:   701 loss: 1.046
Epoch: 9 Minibatch:     1 loss: 0.997
Epoch: 9 Minibatch:   101 loss: 0.689
Epoch: 9 Minibatch:   201 loss: 0.886
Epoch: 9 Minibatch:   301 loss: 0.832
Epoch: 9 Minibatch:   401 loss: 1.049
Epoch: 9 Minibatch:   501 loss: 0.855
Epoch: 9 Minibatch:   601 loss: 1.168
Epoch: 9 Minibatch:   701 loss: 0.836
Epoch: 10 Minibatch:     1 loss: 1.025
Epoch: 10 Minibatch:   101 loss: 0.886
Epoch: 10 Minibatch:   201 loss: 0.699
Epoch: 10 Minibatch:   301 loss: 1.040
Epoch: 10 Minibatch:   401 loss: 0.917
Epoch: 10 Minibatch:   501 loss: 0.988
Epoch: 10 Minibatch:   601 loss: 0.854
Epoch: 10 Minibatch:   701 loss: 0.741
Finished Training

5.训练结果测试

# 得到一组图像
images, labels = iter(testloader).next()
# 展示图像
imshow(torchvision.utils.make_grid(images))
# 展示图像的标签
for j in range(8):
    print(classes[labels[j]])

  这段代码是用matplotlib.pyplot模块和torchvision库来绘制和显示测试集中的一组图片和标签,图片如下所示

outputs = net(images.to(device))
_, predicted = torch.max(outputs, 1)

# 展示预测的结果
for j in range(8):
    print(classes[predicted[j]])

  这段代码是用PyTorch框架和之前创建的卷积神经网络模型(net)来对测试集中的一组图片进行预测,并打印预测的结果

   通过上面结果可以发现训练的模型还存在不小的误差

correct = 0
total = 0

for data in testloader:
    images, labels = data
    images, labels = images.to(device), labels.to(device)
    outputs = net(images)
    _, predicted = torch.max(outputs.data, 1)
    total += labels.size(0)
    correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))

这段代码是用PyTorch框架和之前创建的卷积神经网络模型(net)来对测试集中的所有图片进行预测,并计算并打印模型的准确率,还有提升的空间

1.3使用 VGG16 对 CIFAR10 分类

 1.定义dataloader

import torch # 导入PyTorch库
import torchvision # 导入torchvision库,提供了常用的视觉数据集和模型
import torchvision.transforms as transforms # 导入transforms模块,提供了各种图像预处理的方法
import matplotlib.pyplot as plt # 导入matplotlib库,用于绘制图形
import numpy as np # 导入numpy库,用于处理数组和矩阵
import torch.nn as nn # 导入nn模块,提供了各种神经网络层和损失函数
import torch.nn.functional as F # 导入functional模块,提供了各种激活函数和池化函数
import torch.optim as optim # 导入optim模块,提供了各种优化算法

# 使用GPU训练,可以在菜单 "代码执行工具" -> "更改运行时类型" 里进行设置
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") 

transform_train = transforms.Compose([ # 定义transform_train变换对象
    transforms.RandomCrop(32, padding=4), # 随机裁剪32x32的图像,并在边缘填充4个像素
    transforms.RandomHorizontalFlip(), # 随机水平翻转图像
    transforms.ToTensor(), # 将图像转换为张量,并将像素值范围从[0, 255]变为[0.0, 1.0]
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))]) # 对张量进行归一化,使用给定的均值和标准差

transform_test = transforms.Compose([ # 定义transform_test变换对象
    transforms.ToTensor(), # 将图像转换为张量,并将像素值范围从[0, 255]变为[0.0, 1.0]
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))]) # 对张量进行归一化,使用给定的均值和标准差

trainset = torchvision.datasets.CIFAR10(root='./data', train=True,  download=True, transform=transform_train) # 加载CIFAR10训练集,并对图像进行预处理
testset  = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test) # 加载CIFAR10测试集,并对图像进行预处理

trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True, num_workers=2) # 创建trainloader数据加载器对象,用于从trainset中批量读取数据,设置批量大小为128,打乱顺序,使用2个线程加载
testloader = torch.utils.data.DataLoader(testset, batch_size=128, shuffle=False, num_workers=2) # 创建testloader数据加载器对象,用于从testset中批量读取数据,设置批量大小为128,不打乱顺序,使用2个线程加载

classes = ('plane', 'car', 'bird', 'cat', # 定义classes列表,用于存储CIFAR10数据集的类别名称
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

2.VGG网络定义

  用实验代码可以看到两个矩阵无法相乘,将代码self.classifier = nn.Linear(2048, 10)改为self.classifier = nn.Linear(512, 10) ,如下所示,即可正常运行

class VGG(nn.Module):
    def __init__(self):
        super(VGG, self).__init__()
        self.cfg = [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M']
        self.features = self._make_layers(self.cfg)
        self.classifier = nn.Linear(512, 10)

    def forward(self, x):
        out = self.features(x)
        out = out.view(out.size(0), -1)
        out = self.classifier(out)
        return out

    def _make_layers(self, cfg):
        layers = []
        in_channels = 3
        for x in cfg:
            if x == 'M':
                layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
            else:
                layers += [nn.Conv2d(in_channels, x, kernel_size=3, padding=1),
                           nn.BatchNorm2d(x),
                           nn.ReLU(inplace=True)]
                in_channels = x
        layers += [nn.AvgPool2d(kernel_size=1, stride=1)]
        return nn.Sequential(*layers)

  这段代码是定义了一个VGG网络的类,VGG网络是一种深度卷积神经网络,用于大规模图像识别

3.网络训练

# 网络放到GPU上
net = VGG().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=0.001)
for epoch in range(10):  # 重复多轮训练
    for i, (inputs, labels) in enumerate(trainloader):
        inputs = inputs.to(device)
        labels = labels.to(device)
        # 优化器梯度归零
        optimizer.zero_grad()
        # 正向传播 + 反向传播 + 优化
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        # 输出统计信息
        if i % 100 == 0:
            print('Epoch: %d Minibatch: %5d loss: %.3f' %(epoch + 1, i + 1, loss.item()))

print('Finished Training')

  这段代码是用于训练 VGG 网络的主循环,训练结果如下图所示

 4.测试验证准确率

correct = 0
total = 0

for data in testloader:
    images, labels = data
    images, labels = images.to(device), labels.to(device)
    outputs = net(images)
    _, predicted = torch.max(outputs.data, 1)
    total += labels.size(0)
    correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %.2f %%' % (
    100 * correct / total))

这段代码是用于评估 VGG 网络在测试集上的准确率,结果如下图所示

 第二部分 问题总结

1.dataloader 里面 shuffle 取不同值有什么区别?

  dataloader 里面 shuffle 参数的作用是在每个训练轮次时,是否随机打乱数据集中的样本顺序。如果 shuffle 为 True,那么每个轮次都会对数据集进行一次随机排列,然后按照批次大小划分批次。如果 shuffle 为 False,那么每个轮次都会按照数据集原来的顺序划分批次。

2.transform 里,取了不同值,这个有什么区别?

  transform 里,取了不同值,表示对数据集中的图像进行不同的预处理或增强操作。这些操作可以改变图像的格式、大小、颜色、亮度、对比度、旋转、裁剪等,以适应不同的模型输入或提高模型的泛化能力。例如,以下代码创建了一个 transform 流水线,它包含了以下操作:

  • torchvision.transforms.Resize(256) 将图像缩放到 256x256 的大小。
  • torchvision.transforms.CenterCrop(224) 将图像中心裁剪到 224x224 的大小。
  • torchvision.transforms.ToTensor() 将 PIL 图像转换为张量图像,并将值范围从 [0, 255] 缩放到 [0, 1]。
  • torchvision.transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)) 将张量图像按照给定的均值和标准差进行归一化

3.epoch 和 batch 的区别?

  epoch指的是对整个数据集进行一次完整的遍历。一个 epoch 包含了若干个 batch,batch 指的是将数据集分成若干个等大小的子集,每个子集称为一个 batch。epoch 和 batch 的区别主要在于它们影响梯度下降算法的速度和效果。一般来说,增加 epoch 的数量可以提高模型的训练效果,但也会增加训练时间。选择合适的 batch 大小可以平衡计算效率和收敛速度,但也要考虑内存限制和随机性影响。

4.1x1的卷积和 FC 有什么区别?主要起什么作用?

  1x1 的卷积可以保留输入特征图的空间维度,即输出特征图的高度和宽度,FC会丢失输入特征图的空间信息,即输出特征向量没有高度和宽度。

  • 降维或升维。1x1 的卷积和 FC 都可以通过改变输出特征的通道数或维度来实现降维或升维的效果。这样可以减少参数数量和计算量,或者增加模型的表达能力。
  • 特征融合。1x1 的卷积可以对输入特征图的不同通道进行线性组合,从而实现跨通道的特征融合。这样可以增强特征图的语义信息,或者减少冗余信息。
  • 空间保留。1x1 的卷积可以在不改变输入特征图的空间维度的情况下进行线性变换,从而保留空间信息。这样可以方便后续的空间操作,如池化、上采样、拼接等。

5.residual learning 为什么能够提升准确率?

  • 解决梯度消失或爆炸问题。随着神经网络的深度增加,梯度在反向传播过程中可能会变得非常小或非常大,导致网络难以训练。residual learning 通过在网络中添加跨层的直接连接(即残差连接),使得梯度可以直接从后面的层传递到前面的层,从而缓解梯度消失或爆炸问题。
  • 实现恒等映射。理论上,增加网络的深度应该可以提高网络的表达能力,但实际上,过深的网络可能会导致性能下降。residual learning 通过让每个层学习输入与输出之间的残差函数,而不是直接学习输出函数,使得每个层可以更容易地实现恒等映射(即输出等于输入),从而保证网络性能不会下降。
  • 增强特征表达。residual learning 通过将不同层的特征进行叠加,而不是简单地串联,使得网络可以更好地融合不同层次的特征信息,从而增强特征表达能力。

6.代码练习二里,网络和1989年 Lecun 提出的 LeNet 有什么区别?

  代码练习二中网络输入图像是3通道彩色图像,而LeNet的输入图像是1通道的灰度图像,并且它们卷积层的输出通道和全连接层的输入输出维度也不相同

7.代码练习二里,卷积以后feature map 尺寸会变小,如何应用 Residual Learning?

  使用 1x1 的卷积层来调整残差连接的输入或输出的通道数和尺寸,使得它们可以对齐。这种方法可以同时改变通道数和尺寸,但会增加额外的参数和计算量。

8.有什么方法可以进一步提升准确率?

  选择不同的模型来进行迁移学习,如 ResNet, Inception, Xception, MobileNet 等,使用更深或更复杂的网络结构

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值