本实验属于哪门课程 | 中国海洋大学24夏《深度学习》 |
学习内容 | 卷积神经网络 |
一、学习目标
1、掌握深度学习基础知识;2、熟练使用深度学习框架;3、实施和优化经典神经网络架构
二、学习内容
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)
-
input_size
和output_size
分别定义了输入图像的特征维度和输出类别的数量。 -
train_loader
和test_loader
是 PyTorch 的DataLoader
实例,用于加载 MNIST 数据集。DataLoader
将数据集分成小批次,并可选地打乱数据(shuffle=True
)。数据通过transforms.Compose
进行预处理,包括将图像转换为 Tensor 和标准化。
FC2Layer 类
class FC2Layer(nn.Module): def __init__(self, input_size, n_hidden, output_size): super(FC2Layer, self).__init__() self.input_size = input_size 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): x = x.view(-1, self.input_size) return self.network(x)
-
FC2Layer
是一个全连接神经网络(Feedforward Neural Network),由多个全连接层(nn.Linear
)和激活函数(nn.ReLU
)组成。nn.LogSoftmax
是最后的激活函数,用于多分类任务。 -
forward
方法定义了数据流经网络的过程。x.view(-1, self.input_size)
将输入展平为二维矩阵,self.network(x)
对展平后的数据进行前向传播。
CNN 类
class CNN(nn.Module): def __init__(self, input_size, n_feature, output_size): super(CNN, self).__init__() 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) 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
-
CNN
是一个卷积神经网络(Convolutional Neural Network),包括卷积层(nn.Conv2d
)、激活函数(F.relu
)、池化层(F.max_pool2d
)和全连接层(nn.Linear
)。 -
forward
方法定义了数据在网络中的流动过程,包括卷积操作、激活、池化、展平以及最终的全连接层。
训练函数
def train(model): model.train() for batch_idx, (data, target) in enumerate(train_loader): 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()))
-
train
函数用于训练模型。它遍历train_loader
中的批次数据,将数据送到 GPU 上,计算损失,进行反向传播和参数更新。
测试函数
def test(model): model.eval() test_loss = 0 correct = 0 for data, target in test_loader: data, target = data.to(device), target.to(device) 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))
-
test
函数用于测试模型。它计算测试数据集上的总损失和准确率。
训练和测试模型
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)
-
创建一个
FC2Layer
模型,设置隐藏层单元数为 8,优化器使用 SGD。然后进行训练和测试。
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)
-
创建一个
CNN
模型,设置特征图数量为 6,优化器使用 SGD。然后进行训练和测试。
像素打乱示例
perm = torch.randperm(784) plt.figure(figsize=(8, 4)) for i in range(10): image, _ = train_loader.dataset.__getitem__(i) 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')
-
随机打乱 MNIST 图像的像素顺序并可视化原图和打乱后的图像。
打乱像素顺序函数
def perm_pixel(data, perm): data_new = data.view(-1, 28*28) data_new = data_new[:, perm] data_new = data_new.view(-1, 1, 28, 28) return data_new
-
perm_pixel
函数用于对图像数据的像素顺序进行打乱。perm
是一个打乱的像素索引顺序。
训练和测试打乱像素的模型
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))
-
train_perm
和test_perm
函数分别对打乱像素顺序的模型进行训练和测试。它们在每个训练和测试批次中都会对图像数据进行像素顺序打乱。
CIFAR10 数据集分类:
这段代码是一个完整的 PyTorch 示例,展示了如何使用卷积神经网络(CNN)在 CIFAR-10 数据集上进行训练和测试。以下是对每一部分代码的解释:
数据加载和预处理
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))]) # 训练和测试数据集的下载和加载 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)
-
通过
torchvision
下载 CIFAR-10 数据集。 -
使用
transforms.Compose
对图像进行转换,包括将图像转换为 Tensor 和标准化。 -
trainloader
和testloader
是 PyTorch 的DataLoader
实例,分别用于训练和测试数据集。shuffle=True
表示训练数据会被打乱顺序,shuffle=False
表示测试数据不需要打乱。
可视化函数
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') 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 = iter(trainloader).next() # 展示图像 imshow(torchvision.utils.make_grid(images)) # 展示第一行图像的标签 for j in range(8): print(classes[labels[j]])
-
imshow
函数用于显示图像。将图像的像素值从 [-1, 1] 转换到 [0, 1] 之间,并显示图像。 -
从训练数据集中提取一批图像,展示这些图像及其标签。
定义卷积神经网络
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
-
Net
是一个简单的卷积神经网络(CNN),包括两层卷积层(nn.Conv2d
)、最大池化层(nn.MaxPool2d
)、和全连接层(nn.Linear
)。 -
forward
方法定义了数据在网络中的流动过程,包括卷积、激活、池化、展平和全连接操作。
训练和测试模型
# 网络放到GPU上 net = Net().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')
-
创建一个
Net
模型并将其转移到 GPU(如果可用)。 -
使用交叉熵损失函数(
nn.CrossEntropyLoss
)和 Adam 优化器(optim.Adam
)。 -
训练模型 10 个 epoch。在每个 epoch 中,遍历训练数据并执行前向传播、计算损失、反向传播和优化。
测试模型
# 得到一组图像 images, labels = iter(testloader).next() # 展示图像 imshow(torchvision.utils.make_grid(images)) # 展示图像的标签 for j in range(8): print(classes[labels[j]]) outputs = net(images.to(device)) _, predicted = torch.max(outputs, 1) # 展示预测的结果 for j in range(8): print(classes[predicted[j]])
-
从测试数据集中提取一批图像,展示这些图像及其标签。
-
使用训练好的模型进行预测,并展示预测的标签。
计算模型准确率
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))
-
遍历测试数据集,计算模型在测试集上的总正确预测数和总样本数。
-
计算并打印模型在测试集上的准确率。
使用 VGG16 对 CIFAR10 分类
这段代码展示了如何使用 PyTorch 实现和训练一个 VGG 风格的卷积神经网络(CNN)在 CIFAR-10 数据集上。以下是代码的详细解释:
数据加载和预处理
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_train = transforms.Compose([ transforms.RandomCrop(32, padding=4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))]) transform_test = transforms.Compose([ transforms.ToTensor(), 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) testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test) trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True, num_workers=2) testloader = torch.utils.data.DataLoader(testset, batch_size=128, shuffle=False, num_workers=2) classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
-
transform_train
和transform_test
分别用于训练集和测试集的数据预处理。-
transforms.RandomCrop(32, padding=4)
:对图像进行随机裁剪,并在边缘添加填充。 -
transforms.RandomHorizontalFlip()
:随机水平翻转图像,以增加数据的多样性。 -
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
:对图像进行归一化处理。
-
-
训练集和测试集的数据加载器使用
batch_size=128
,并设置shuffle=True
以打乱训练数据。
定义 VGG 网络
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(cfg) self.classifier = nn.Linear(2048, 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 的网络架构。 -
cfg
列表定义了网络的层级结构,其中'M'
代表最大池化层(nn.MaxPool2d
),其他数字代表卷积层的输出通道数。 -
self.features
包含了卷积层和池化层,通过_make_layers
方法构建。 -
self.classifier
是一个全连接层,用于分类输出。
训练模型
# 网络放到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')
-
net
是定义的 VGG 网络实例,并将其转移到 GPU(如果可用)。 -
使用交叉熵损失函数(
nn.CrossEntropyLoss
)和 Adam 优化器(optim.Adam
)来训练网络。 -
训练过程包括前向传播、计算损失、反向传播和优化参数,每 100 个小批量输出一次损失。
测试模型
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))
-
在测试集上评估模型的性能。
-
遍历测试数据集,计算模型的预测准确率。
当然可以!下面是以周报格式呈现的回答:
思考下面的问题
-
DataLoader 中 shuffle 取不同值的区别
shuffle=True:在每个 epoch 开始前,打乱数据的顺序。此设置用于训练阶段,能够增加数据的多样性,减少模型对数据顺序的依赖,从而提高模型的泛化能力。 shuffle=False:数据按顺序加载。适用于验证和测试阶段,保证数据顺序的一致性,有助于稳定评估模型性能。
-
transform 中取不同值的区别
数据预处理:transform 用于对输入数据进行标准化、归一化和数据增强。不同的预处理方法会影响模型训练的效果。例如: 标准化和归一化:确保数据尺度统一,能加速收敛,提高模型性能。 数据增强:增加数据集的多样性,帮助模型更好地泛化。
-
epoch 和 batch 的区别
Epoch:模型在整个训练数据集上训练一次。训练通常需要多个 epoch 以确保模型的收敛。 Batch:训练过程中使用的数据子集。每个 epoch 中,训练数据会被分成多个批次,批次的大小由 batch_size 决定。每个批次用于一次前向和反向传播。
-
1x1 的卷积和全连接层的区别及作用
1x1 卷积:用于改变特征图的通道数,不改变空间尺寸。主要用于特征融合、信息压缩和引入非线性变换。 全连接层:将所有输入特征连接到输出层,每个输入都与输出的每个神经元相连。用于进行分类或回归任务。
-
Residual Learning 为什么能够提升准确率
Residual Learning 通过引入残差连接,使得网络能够学习输入和输出之间的差异。它缓解了梯度消失问题,加速了收敛,提高了训练更深层次网络的能力。
-
代码练习二中网络与 LeNet 的区别
LeNet(1989年): 架构:较浅的网络,包含两个卷积层和两个池化层。 特点:较小的卷积核和池化核,网络深度较浅。
代码练习二(例如 VGG): 架构:更深的网络,包含多个卷积层和池化层,卷积核大小为 3x3。 特点:更多的卷积层,提升了模型的表现力。
-
卷积以后特征图尺寸变小,如何应用 Residual Learning
特征图尺寸变小:卷积和池化操作会减小特征图的空间尺寸。 应用 Residual Learning:使用 1x1 卷积调整残差连接的维度,确保与输入特征图的维度一致,从而有效应用 Residual Learning。
-
提升准确率的方法
增加数据集规模:获取更多数据可以提升模型的泛化能力。 数据增强:使用数据增强技术增加数据多样性,改善模型的鲁棒性。 优化网络结构:尝试更深或更复杂的网络结构,或使用预训练模型进行迁移学习。 超参数调优:调整学习率、批量大小等超参数,以找到最佳训练设置。 正则化技术:使用 Dropout、L2 正则化等方法来减少过拟合。 先进优化算法:如 Adam、RMSprop 等,帮助更快收敛。
三、程序运行结果
列出程序的最终运行结果及截图。
MNIST 数据集分类:
CIFAR10 数据集分类:
使用 VGG16 对 CIFAR10 分类
四、问题总结与体会
收获和体会
-
通过实现 VGG 网络并进行训练,深入理解了深度学习框架的核心概念,如卷积层、池化层、全连接层等。
-
通过调整数据预处理、学习率和网络结构,体会到了模型调优的复杂性和必要性,掌握了一些常见的调优技巧。
-
体验到使用 GPU 进行训练可以显著加速模型训练过程,并理解了如何在代码中设置 GPU 设备。