【第二周】卷积神经网络
学习专知课程《卷积神经网络》,主要内容包括:
- CNN的基本结构:卷积、池化、全连接
- 典型的网络结构:AlexNet、VGG、GoogleNet、ResNet
1.代码练习
- MNIST 数据集分类:构建简单的CNN对 mnist 数据集进行分类。同时,还会在实验中学习池化与卷积操作的基本作用。访问链接
- CIFAR10 数据集分类:使用 CNN 对 CIFAR10 数据集进行分类,访问链接
- 使用 VGG16 对 CIFAR10 分类,访问链接
1.1MNIST 数据集分类
1.1.1加载数据
PyTorch里包含了 MNIST, CIFAR10 等常用数据集,调用 torchvision.datasets 即可把这些数据由远程下载到本地,下面给出MNIST的使用方法:
torchvision.datasets.MNIST(root, train=True, transform=None, target_transform=None, download=False)
- root 为数据集下载到本地后的根目录,包括 training.pt 和 test.pt 文件
- train,如果设置为True,从training.pt创建数据集,否则从test.pt创建。
- download,如果设置为True, 从互联网下载数据并放到root文件夹下
- transform, 一种函数或变换,输入PIL图片,返回变换之后的数据。
- target_transform 一种函数或变换,输入目标,进行变换。
另外值得注意的是,DataLoader是一个比较重要的类,提供的常用操作有:batch_size(每个batch的大小), shuffle(是否进行随机打乱顺序的操作), num_workers(加载数据的时候使用几个子进程)
1.1.2创建网络
然后创建两个网络,分别为全连接神经网络和卷积神经网络
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_featureself.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
定义网络要继承于nn.Module,在构造函数中执行父类的构造函数,以及写明网络中具有可学习参数的层,并用forward函数来定义了网络结构。
然后是训练和测试函数,其中optimizer.zero_grad()用于清除累计的梯度,上个实验中也有提到。
# 训练函数
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 = 0correct = 0for 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))
1.1.3在小型全连接网络上训练
n_hidden = 8 # number of hidden unitsmodel_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)
其中n_hidden是一个超参数,为每个隐层的神经元个数,
在colab上运行结果如下,可以看到模型参数为6442.
1.1.4 在卷积神经网络上训练
# Training settings
n_features = 6 # number of feature mapsmodel_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)
n_features为特征图的数量,决定了网络中卷积层输出的通道数,也可以理解为卷积核的数量。
运行结果如下,可以发现在参数量相同的情况下,CNN表现更好。
1.1.5.打乱像素顺序再次在两个网络上训练与测试
将图像进行打乱,再测试两种网络的识别表现
全连接神经网络:
CNN:
全连接网络的性能基本上没有发生变化,但是卷积神经网络的性能明显下降。
这是因为对于卷积神经网络,会利用像素的局部关系,但是打乱顺序以后,这些像素间的关系将无法得到利用。
通过对图像进行像素打乱操作,会破坏图像的空间局部性特征。说明卷积神经网络的卷积和池化操作,能够对具有局部特征的图像取得更好的分类效果。
1.1.6这个实验对我的一点小启发:
我:
经过打乱后,我已经很难分辨手写数字的图像原本的类别了,但是神经网络却还能有较高的识别能力,这是为什么呢?我不能识别但神经网络能识别,这是一种什么现象?这跟可解释性差有联系吗?
ChatGPT:
确实,当像素顺序被打乱后,图像的外观会发生较大变化,难以被人类视觉系统轻易识别。然而,神经网络在某些情况下可能仍然能够对打乱后的图像进行分类。
这可以归因于神经网络具有一定的模式识别和特征提取能力,它可以通过学习和调整模型的参数来适应不同的输入变化。神经网络通过多个层次的非线性变换和权重优化,可以学习到在输入数据中存在的隐含模式和特征。
然而,对于人类视觉系统而言,像素顺序的打乱会破坏图像的空间局部性和连续性,使得我们很难准确地识别图像的类别。这与神经网络的工作方式不同,神经网络并不直接依赖于具体的空间位置和像素顺序。
这种现象与神经网络的可解释性有一定的关系。神经网络的内部表示和决策过程通常是复杂和抽象的,难以直接解释和理解。神经网络可能会学习到一些抽象的特征和模式,而不仅仅依赖于输入数据的直观特征。因此,即使在像素顺序打乱的情况下,神经网络可能仍然能够通过学习到的抽象特征来进行分类。
1.2 CIFAR10 数据集分类
对于视觉数据,PyTorch 创建了一个叫做 totchvision 的包,该包含有支持加载类似Imagenet,CIFAR10,MNIST 等公共数据集的数据加载模块 torchvision.datasets 和支持加载图像数据数据转换模块 torch.utils.data.DataLoader。
下面将使用CIFAR10数据集,它包含十个类别:‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’。CIFAR-10 中的图像尺寸为3x32x32,也就是RGB的3层颜色通道,每层通道内的尺寸为32*32。
1.2.1加载数据
首先,加载并归一化 CIFAR10 使用 torchvision 。torchvision 数据集的输出是范围在[0,1]之间的 PILImage,我们将他们转换成归一化范围为[-1,1]之间的张量 Tensors。
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')
下面展示 CIFAR10 里面的一些图片,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()# 得到一组图像
dataiter=iter(trainloader)
images, labels = next(dataiter)
# 展示图像
imshow(torchvision.utils.make_grid(images))
# 展示第一行图像的标签
for j in range(8):print(classes[labels[j]])
1.2.2网络的定义和结构
接下来是网络的定义和结构,
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
1.2.3训练代码及过程
# 网络放到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')
Epoch: 1 Minibatch: 1 loss: 2.302
Epoch: 1 Minibatch: 101 loss: 1.879
Epoch: 1 Minibatch: 201 loss: 1.736
Epoch: 1 Minibatch: 301 loss: 1.723
Epoch: 1 Minibatch: 401 loss: 1.844
Epoch: 1 Minibatch: 501 loss: 1.569
Epoch: 1 Minibatch: 601 loss: 1.596
Epoch: 1 Minibatch: 701 loss: 1.519
Epoch: 2 Minibatch: 1 loss: 1.557
Epoch: 2 Minibatch: 101 loss: 1.269
Epoch: 2 Minibatch: 201 loss: 1.283
Epoch: 2 Minibatch: 301 loss: 1.544
Epoch: 2 Minibatch: 401 loss: 1.702
Epoch: 2 Minibatch: 501 loss: 1.398
Epoch: 2 Minibatch: 601 loss: 1.222
Epoch: 2 Minibatch: 701 loss: 1.175
Epoch: 3 Minibatch: 1 loss: 1.268
Epoch: 3 Minibatch: 101 loss: 1.269
Epoch: 3 Minibatch: 201 loss: 1.133
Epoch: 3 Minibatch: 301 loss: 0.968
Epoch: 3 Minibatch: 401 loss: 1.141
Epoch: 3 Minibatch: 501 loss: 1.130
Epoch: 3 Minibatch: 601 loss: 1.415
Epoch: 3 Minibatch: 701 loss: 1.236
Epoch: 4 Minibatch: 1 loss: 1.047
Epoch: 4 Minibatch: 101 loss: 1.026
Epoch: 4 Minibatch: 201 loss: 1.082
Epoch: 4 Minibatch: 301 loss: 1.314
Epoch: 4 Minibatch: 401 loss: 0.839
Epoch: 4 Minibatch: 501 loss: 1.268
Epoch: 4 Minibatch: 601 loss: 1.243
Epoch: 4 Minibatch: 701 loss: 0.982
Epoch: 5 Minibatch: 1 loss: 1.143
Epoch: 5 Minibatch: 101 loss: 0.970
Epoch: 5 Minibatch: 201 loss: 0.779
Epoch: 5 Minibatch: 301 loss: 0.957
Epoch: 5 Minibatch: 401 loss: 0.955
Epoch: 5 Minibatch: 501 loss: 1.166
Epoch: 5 Minibatch: 601 loss: 0.823
Epoch: 5 Minibatch: 701 loss: 0.956
Epoch: 6 Minibatch: 1 loss: 1.096
Epoch: 6 Minibatch: 101 loss: 1.052
Epoch: 6 Minibatch: 201 loss: 1.136
Epoch: 6 Minibatch: 301 loss: 1.098
Epoch: 6 Minibatch: 401 loss: 1.002
Epoch: 6 Minibatch: 501 loss: 0.886
Epoch: 6 Minibatch: 601 loss: 0.841
Epoch: 6 Minibatch: 701 loss: 1.063
Epoch: 7 Minibatch: 1 loss: 0.926
Epoch: 7 Minibatch: 101 loss: 0.988
Epoch: 7 Minibatch: 201 loss: 0.894
Epoch: 7 Minibatch: 301 loss: 1.018
Epoch: 7 Minibatch: 401 loss: 1.180
Epoch: 7 Minibatch: 501 loss: 0.857
Epoch: 7 Minibatch: 601 loss: 0.971
Epoch: 7 Minibatch: 701 loss: 1.221
Epoch: 8 Minibatch: 1 loss: 0.680
Epoch: 8 Minibatch: 101 loss: 0.996
Epoch: 8 Minibatch: 201 loss: 0.908
Epoch: 8 Minibatch: 301 loss: 0.888
Epoch: 8 Minibatch: 401 loss: 1.050
Epoch: 8 Minibatch: 501 loss: 0.822
Epoch: 8 Minibatch: 601 loss: 1.084
Epoch: 8 Minibatch: 701 loss: 1.010
Epoch: 9 Minibatch: 1 loss: 0.897
Epoch: 9 Minibatch: 101 loss: 0.861
Epoch: 9 Minibatch: 201 loss: 0.956
Epoch: 9 Minibatch: 301 loss: 0.869
Epoch: 9 Minibatch: 401 loss: 0.883
Epoch: 9 Minibatch: 501 loss: 0.873
Epoch: 9 Minibatch: 601 loss: 0.911
Epoch: 9 Minibatch: 701 loss: 0.856
Epoch: 10 Minibatch: 1 loss: 0.925
Epoch: 10 Minibatch: 101 loss: 1.085
Epoch: 10 Minibatch: 201 loss: 0.793
Epoch: 10 Minibatch: 301 loss: 1.021
Epoch: 10 Minibatch: 401 loss: 0.917
Epoch: 10 Minibatch: 501 loss: 0.961
Epoch: 10 Minibatch: 601 loss: 1.039
Epoch: 10 Minibatch: 701 loss: 0.661
Finished Training
1.2.4测试结果
网络在整个数据集上的表现仅有63%
correct = 0
total = 0for data in testloader:images, labels = dataimages, 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))
Accuracy of the network on the 10000 test images: 63 %
1.3 使用 VGG16 对 CIFAR10 分类
VGG是由Simonyan 和Zisserman在文献《Very Deep Convolutional Networks for Large Scale Image Recognition》中提出卷积神经网络模型,其名称来源于作者所在的牛津大学视觉几何组(Visual Geometry Group)的缩写。
该模型参加2014年的 ImageNet图像分类与定位挑战赛,取得了优异成绩:在分类任务上排名第二,在定位任务上排名第一。
1.3.1 定义 dataloader
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和dataloader相比:
第一段代码在训练和测试上使用了相同的transform对数据进行预处理操作。而第二段代码中在训练时使用了一些数据增强方式,包括随机裁剪和随机水平翻转。同时二者标准化的参数不同。
这些参数是经过计算得到的 CIFAR-10 数据集的像素通道均值和标准差。对于训练集和测试集,这些参数用于将图像的每个通道进行标准化,使其均值接近 0.5,标准差接近 0.2。
标准化的目的是将数据进行零均值化和单位方差化,以便更好地适应模型的训练。通过减去均值并除以标准差,可以将数据的分布调整为接近标准正态分布,这有助于模型更好地学习特征。
dataloader上,二者的batch_size不同。
1.3.2简化的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(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 outdef _make_layers(self, cfg):layers = []in_channels = 3for 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 = xlayers += [nn.AvgPool2d(kernel_size=1, stride=1)]return nn.Sequential(*layers)# 网络放到GPU上
net = VGG().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=0.001)
1.3.3训练过程
Epoch: 1 Minibatch: 1 loss: 2.473
Epoch: 1 Minibatch: 101 loss: 1.563
Epoch: 1 Minibatch: 201 loss: 1.199
Epoch: 1 Minibatch: 301 loss: 1.172
Epoch: 2 Minibatch: 1 loss: 1.059
Epoch: 2 Minibatch: 101 loss: 1.030
Epoch: 2 Minibatch: 201 loss: 0.892
Epoch: 2 Minibatch: 301 loss: 0.874
Epoch: 3 Minibatch: 1 loss: 0.918
Epoch: 3 Minibatch: 101 loss: 0.718
Epoch: 3 Minibatch: 201 loss: 0.741
Epoch: 3 Minibatch: 301 loss: 0.681
Epoch: 4 Minibatch: 1 loss: 0.837
Epoch: 4 Minibatch: 101 loss: 0.818
Epoch: 4 Minibatch: 201 loss: 0.717
Epoch: 4 Minibatch: 301 loss: 0.582
Epoch: 5 Minibatch: 1 loss: 0.586
Epoch: 5 Minibatch: 101 loss: 0.616
Epoch: 5 Minibatch: 201 loss: 0.735
Epoch: 5 Minibatch: 301 loss: 0.536
Epoch: 6 Minibatch: 1 loss: 0.576
Epoch: 6 Minibatch: 101 loss: 0.459
Epoch: 6 Minibatch: 201 loss: 0.589
Epoch: 6 Minibatch: 301 loss: 0.466
Epoch: 7 Minibatch: 1 loss: 0.519
Epoch: 7 Minibatch: 101 loss: 0.512
Epoch: 7 Minibatch: 201 loss: 0.385
Epoch: 7 Minibatch: 301 loss: 0.453
Epoch: 8 Minibatch: 1 loss: 0.435
Epoch: 8 Minibatch: 101 loss: 0.477
Epoch: 8 Minibatch: 201 loss: 0.404
Epoch: 8 Minibatch: 301 loss: 0.474
Epoch: 9 Minibatch: 1 loss: 0.457
Epoch: 9 Minibatch: 101 loss: 0.325
Epoch: 9 Minibatch: 201 loss: 0.517
Epoch: 9 Minibatch: 301 loss: 0.429
Epoch: 10 Minibatch: 1 loss: 0.331
Epoch: 10 Minibatch: 101 loss: 0.244
Epoch: 10 Minibatch: 201 loss: 0.384
Epoch: 10 Minibatch: 301 loss: 0.367
Finished Training
1.3.4测试及结果
correct = 0
total = 0for data in testloader:images, labels = dataimages, 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))
Accuracy of the network on the 10000 test images: 83.03 %
相较于上一个网络,准确率有了较大的提升。
2.问题总结
2.1 dataloader 里面 shuffle 取不同值有什么区别?
shuffle为True时,可以在每个 epoch 中打乱数据顺序,增加样本的随机性,使模型更好的学到数据的各种特征。
shuffle为False时,dataloader会按照数据集中的顺序逐个加载样本。
一般在训练时shuffle取true,训练时取false。
2.2 transform 里,取了不同值,这个有什么区别?
像这次实验二和实验三中,transform里有不同的参数。transform主要用于数据的预处理,比如将数据转为张量和数据增强方法。实验三中针对CIFAR10数据集的像素通道均值和标准差,设定了标准化参数,实验二中可能就是一个初始的参数。实验三中还使用了随机裁剪和随机水平翻转的数据增强。
transforms.ToTensor()
:将 PIL 图像或 NumPy 数组转换为张量(Tensor),并将像素值从 [0, 255] 缩放到 [0, 1]。transforms.Normalize(mean, std)
:对张量进行标准化操作,即减去均值并除以标准差。mean
和std
是用于标准化的均值和标准差,可以根据数据集的特性进行设置。transforms.Resize(size)
:调整图像大小,使其具有指定的尺寸。可以传入整数来调整图像的较小边的长度,或者传入一个元组来指定具体的尺寸。transforms.CenterCrop(size)
:对图像进行中心裁剪,使其具有指定的尺寸。可以传入整数来指定裁剪的边长,或者传入一个元组来指定具体的尺寸。transforms.RandomCrop(size, padding=None)
:对图像进行随机裁剪,使其具有指定的尺寸。可以传入整数来指定裁剪的边长,或者传入一个元组来指定具体的尺寸。可选的padding
参数可以指定填充的大小。transforms.RandomHorizontalFlip(p=0.5)
:以给定的概率进行随机水平翻转图像。默认概率为 0.5,即有一半的概率进行翻转。- 其他的预处理和增强操作,如旋转、亮度调整、对比度调整等。
2.3 epoch 和 batch 的区别?
epoch是训练时的轮数,应该是模型把所有的训练数据使用一遍进行学习的过程(一次完整的训练),算一个epoch;
batch是训练时,把所有的训练数据分成了多个批次,每一个批次取batchsize个训练样本进行训练,只学习这些样本。
2.4 1x1的卷积和 FC 有什么区别?主要起什么作用?
- 1x1的卷积层在空间维度上进行局部感知,但不改变输入图像的空间尺寸,主要用于通道间的线性组合和特征变换。全连接层将输入特征映射到输出类别的分数,不保持空间结构。
- 1x1的卷积层可以控制输出特征图的通道数,而全连接层的参数量随着输入特征的大小而增加。
- 1x1的卷积层在深度卷积神经网络中常用于调整通道数、特征整合和非线性变换,而全连接层通常用于分类任务的最后一层。
综上所述,1x1的卷积层和全连接层在神经网络中有着不同的作用和应用场景,可以根据任务需求和模型结构来选择使用。
1x1的卷积可以用来调整通道数,起到降维和升维的作用。
2.5 residual leanring 为什么能够提升准确率?
在此模型中,由于x直接连接到下一层,没有任何参数,即网络学习到的是F(x)。
缓解梯度消失问题:深度神经网络存在梯度消失问题,导致在训练过程中难以有效地更新较早层的参数。通过引入跳跃连接,残差学习允许信息在网络中直接传播,减少了梯度在深层传播过程中逐渐消失的情况,使得更深的网络可以更好地学习特征。
提高网络收敛速度:由于跳跃连接的引入,残差学习可以使网络在初始训练阶段更快地收敛。在初始训练阶段,模型还没有很好地学习到有效的特征表示,但通过跳跃连接,网络可以直接传递输入信息到后续层,从而减少了训练初始阶段的信息丢失。
网络深度的有效利用:残差学习允许通过跳跃连接将信息直接传递到后续层,使得网络可以更好地利用深层的特征表示。网络可以选择性地学习残差,将前面层的特征与后面层的特征相加,从而更充分地利用网络的深度。
总的来说残差的结构可以帮助解决网络退化问题,通过允许信息在网络中直接传播来缓解梯度消失,同时能更充分地利用网络的深度。
2.6 代码练习二里,网络和1989年 Lecun 提出的 LeNet 有什么区别?
代码二:
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
LeNet:
class LeNet5(nn.Module):def __init__(self):super().__init__()self.conv1 = nn.Conv2d(1,6,5,padding=2)self.pool1 = nn.AvgPool2d(2)self.conv2 = nn.Conv2d(6,16,5)self.pool2 = nn.AvgPool2d(2)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 = torch.tanh(self.conv1(x))x = self.pool1(x)x = torch.tanh(self.conv2(x))x = self.pool2(x)x = x.view(-1,16*5*5)x = self.fc1(x)x = self.fc2(x)x = self.fc3(x)return x
池化层不同,LeNet使用的是平均池化;
起初LeNet处理灰度图,第一个卷积的输入通道数不同。
2.7 代码练习二里,卷积以后feature map 尺寸会变小,如何应用 Residual Learning?
在网络中添加跳跃连接,将输入特征图直接添加到相应的层的输出特征图中。在正向传播过程中,通过在相应位置将残差添加到输出特征上,实现残差学习的效果。
2.8 有什么方法可以进一步提升准确率?
增加数据集的数据量或者使用数据增强,来增加可用的样本;
尝试不同的网络结构或调整一些参数,使之能够更好发现图像的特征。
尝试不同的激活函数、损失函数、优化器;
用预训练的模型进行微调;