目录
第一部分 代码练习
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 等,使用更深或更复杂的网络结构