本文最后更新于:2023年7月29日 晚上
卷积神经网络的结构 卷积
卷积核大小定义了卷积操作的感受野(Receptive Field),即所有可能影响像 元 x 前向计算的输入区域。步幅定义了卷积核窗口滑动时的单位距离长度;填充 即在图像边缘添加额外的元素(通常为 0),可以使得输入矩阵和输出矩阵具有相 同的大小。
3D 卷积 二维卷积(2D-CNN)能够充分提取图像数据的特征,并用于分类、识别等任务,而对于具有时间维度的视频分析、行为识别等问题,但二维卷积很难捕获数据中的时序信息。因此三维卷积(3D-CNN)操作被提出,并最早应用于行为识别任务,卷积神经网络通过三维的卷积核同时提取数据的时间和空间特征。3D 卷积比 2D 卷积多一个深度维度信息,可以将 2D 卷积看作是深度为 1 的 3D 卷积。
池化层 通过卷积操作,虽然已经对输入特征进行了提取和降维,但是维度仍旧很高,容易在训练中造成过拟合的问题。因此引入池化操作,对卷积后的图像特征进行分块,特征图被分为不相交的多个块,然后计算块内部的最大值或者平均值,从而得到池化后的图像。池化层不包含需要神经网络学习的参数,进行池化操作也不会改变通道数。因此,经过池化层处理的特征图通道数在处理前后相等。此外,采用池化层还可以使神经网络对一些图像局部细节的形态改变保持不变性,其感受野相比运算之前往往更大。
最大值池化(Max Pooling)与均值池化(Mean Pooling)是两种常见的池化操作方式。
全连接层 全连接层(Full Connected Layer)通常处于神经网络的末尾,可以对神经网络所提取的特征进行汇总,将局部特征重新通过权值矩阵组装成完整的图,最终将学习到的“分布式特征表示”映射到样本标记空间。通常使用 softmax 函数将输出结果变换为满足大小为正、和为一的概率分布,以得到最终的分类或回归结果。
MNIST 数据集分类 构建简单的CNN对mnist
数据集进行分类。同时,还会在实验中学习池化与卷积操作的基本作用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import torchimport torch.nn as nnimport torch.nn.functional as Fimport torch.optim as optimfrom torchvision import datasets, transformsimport matplotlib.pyplot as pltimport numpydef get_n_params (model ): np=0 for p in list (model.parameters()): np += p.nelement() return np device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu" )
1. 加载数据 (MNIST) 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 input_size = 28 *28 output_size = 10 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 )
显示数据集中的部分图像
1 2 3 4 5 6 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' );
2. 创建网络 定义网络时,需要继承nn.Module
,并实现它的forward
方法,把网络中具有可学习参数的层放在构造函数**init
**中。
只要在nn.Module
的子类中定义了forward
函数,backward
函数就会自动被实现(利用autograd
)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 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)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
定义训练和测试函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 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()))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))
3. 在小型全连接网络上训练(Fully-connected network) 1 2 3 4 5 6 7 8 9 n_hidden = 8 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)
3. 在卷积神经网络上训练 1 2 3 4 5 6 7 8 9 10 n_features = 6 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 效果要明显优于 简单的全连接网络,是因为 CNN 能够更好的挖掘图像中的信息,主要通过两个手段:
卷积:Locality and stationarity in images
池化:Builds in some translation invariance
5. 打乱像素顺序再次在两个网络上训练与测试 考虑到CNN在卷积与池化上的优良特性,如果我们把图像中的像素打乱顺序 ,这样 卷积 和 池化 就难以发挥作用了,为了验证这个想法,我们把图像中的像素打乱顺序再试试。
首先下面代码展示随机打乱像素顺序后,图像的形态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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' )
重新定义训练与测试函数,我们写了两个函数 train_perm
和 test_perm
,分别对应着加入像素打乱顺序的训练函数与测试函数。
与之前的训练与测试函数基本上完全相同,只是对 data
加入了打乱顺序操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 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_newdef 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))
在全连接网络上训练与测试:
1 2 3 4 5 6 7 8 9 10 perm = torch.randperm(784 ) n_hidden = 8 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_perm(model_fnn, perm) test_perm(model_fnn, perm)
在卷积神经网络上训练与测试:
1 2 3 4 5 6 7 8 9 10 perm = torch.randperm(784 ) n_features = 6 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_perm(model_cnn, perm) test_perm(model_cnn, perm)
从打乱像素顺序的实验结果来看,全连接网络的性能基本上没有发生变化,但是 卷积神经网络的性能明显下降。
思考 卷积神经网络(Convolutional Neural Network,CNN)在处理图像、语音、文本等数据时,利用了数据的局部关系。在图像中,像素之间的相邻关系是非常重要的,因为相邻的像素通常具有相似的颜色、亮度、纹理等特征。在卷积操作中,卷积核会在输入数据上进行滑动,并对每个位置上的像素进行卷积计算,从而捕捉到局部特征。
然而,当像素的顺序被打乱后,像素之间的相邻关系也被破坏了,这会影响到卷积操作的准确性,从而影响到整个卷积神经网络的性能。因为卷积神经网络是通过学习数据中的特征来进行分类、识别等任务的,如果数据中的特征信息被破坏了,网络的性能就会受到影响。
相比之下,全连接神经网络(Fully Connected Neural Network,FCN)在处理数据时并不依赖于像素的顺序,因为所有的神经元都是相互连接的。所以,即使像素的顺序被打乱,全连接神经网络的性能并不会受到太大的影响。
总之,卷积神经网络对于像素的局部关系比较敏感,而全连接神经网络对于数据的组织形式不太敏感,这也是它们在处理打乱像素顺序的实验中出现不同性能表现的原因。
CIFAR10 数据集分类 下面将使用CIFAR10数据集,它包含十个类别:‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’。CIFAR-10 中的图像尺寸为3x32x32,也就是RGB的3层颜色通道,每层通道内的尺寸为32*32。
首先,加载并归一化 CIFAR10 使用 torchvision 。torchvision 数据集的输出是范围在[0,1]之间的 PILImage,我们将他们转换成归一化范围为[-1,1]之间的张量 Tensors。
大家肯定好奇,下面代码中说的是 0.5,怎么就变化到[-1,1]之间了?PyTorch源码中是这么写的:
1 input [channel] = (input [channel] - mean[channel]) / std[channel]
这样就是:((0,1)-0.5)/0.5=(-1,1)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import torchimport torchvisionimport torchvision.transforms as transformsimport matplotlib.pyplot as pltimport numpy as npimport torch.nn as nnimport torch.nn.functional as Fimport torch.optim as optim 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 ) classes = ('plane' , 'car' , 'bird' , 'cat' , 'deer' , 'dog' , 'frog' , 'horse' , 'ship' , 'truck' )
下面展示 CIFAR10 里面的一些图片:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def imshow (img ): plt.figure(figsize=(8 ,8 )) img = img / 2 + 0.5 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]])
1 2 3 4 5 6 7 8 frogcat plane dogcat cat horse ship
接下来定义网络,损失函数和优化器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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 = Net().to(device) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(net.parameters(), lr=0.001 )
训练网络:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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' )
现在我们从测试集中取出8张图片:
1 2 3 4 5 6 7 images, labels = next (iter (testloader)) imshow(torchvision.utils.make_grid(images))for j in range (8 ): print (classes[labels[j]])
1 2 3 4 5 6 7 8 catship ship plane frog frog car frog
我们把图片输入模型,看看CNN把这些图片识别成什么:
1 2 3 4 5 6 outputs = net(images.to(device)) _, predicted = torch.max (outputs, 1 )for j in range (8 ): print (classes[predicted[j]])
1 2 3 4 5 6 7 8 catship ship plane frog frog car frog
让我们看看网络在整个数据集上的表现:
1 2 3 4 5 6 7 8 9 10 11 12 13 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))
Accuracy of the network on the 10000 test images: 61 %
使用 VGG16 对 CIFAR10 分类 VGG是由Simonyan 和Zisserman在文献《Very Deep Convolutional Networks for Large Scale Image Recognition》中提出卷积神经网络模型,其名称来源于作者所在的牛津大学视觉几何组(Visual Geometry Group)的缩写。
该模型参加2014年的 ImageNet图像分类与定位挑战赛,取得了优异成绩:在分类任务上排名第二,在定位任务上排名第一。
VGG16的网络结构如下图所示:
16层网络的结节信息如下:
01: Convolution using 64 filters
02: Convolution using 64 filters + Max pooling
03: Convolution using 128 filters
04: Convolution using 128 filters + Max pooling
05: Convolution using 256 filters
06: Convolution using 256 filters
07: Convolution using 256 filters + Max pooling
08: Convolution using 512 filters
09: Convolution using 512 filters
10: Convolution using 512 filters + Max pooling
11: Convolution using 512 filters
12: Convolution using 512 filters
13: Convolution using 512 filters + Max pooling
14: Fully connected with 4096 nodes
15: Fully connected with 4096 nodes
16: Softmax
1. 定义 dataloader 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import torchimport torchvisionimport torchvision.transforms as transformsimport matplotlib.pyplot as pltimport numpy as npimport torch.nn as nnimport torch.nn.functional as Fimport torch.optim as optim 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' )
2. VGG 网络定义 下面定义VGG网络,现在的结构基本上是:
64 conv, maxpooling,
128 conv, maxpooling,
256 conv, 256 conv, maxpooling,
512 conv, 512 conv, maxpooling,
512 conv, 512 conv, maxpooling,
softmax
下面是模型的实现代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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)
初始化网络,根据实际需要,修改分类层。因为 tiny-imagenet 是对200类图像分类,这里把输出修改为200。
1 2 3 4 net = VGG().to(device) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(net.parameters(), lr=0.001 )
3. 网络训练 训练的代码和以前是完全一样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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' )
4. 测试验证准确率: 测试的代码和之前也是完全一样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 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 网络,就能够显著地将准确率由 61%,提升到 83.56 %
问题总结 1. dataloader 里面 shuffle 取不同值有什么区别? Dataloader中的shuffle
参数用于指定是否在每个epoch开始时对数据进行重新洗牌。当shuffle=True
时,数据将在每个epoch开始时按照随机顺序进行重新排列 ,这有助于增加模型的泛化能力。当shuffle=False
时,数据将按照原始的顺序进行处理,这可能会在某些情况下提高模型的准确性,特别是对于那些依赖于输入数据顺序的模型。
另外,当shuffle=True
时,Dataloader会在每个epoch开始时重新生成一个随机数种子,以确保每次运行时得到相同的结果。这对于需要在多个设备上并行训练时非常有用,因为它可以确保每个设备上的数据顺序是一致的。
在数据转换(transform)中,不同的取值可以导致不同的数据转换结果,这会对模型的训练和性能产生不同的影响。以下是一些常见的transform取值及其可能的影响:
随机旋转(Random Rotation):如果在transform中设置了随机旋转,当取不同值时,每次训练过程中数据集中的图像会被随机旋转一定的角度。这可以增加模型的鲁棒性,使其能够适应不同角度的图像。然而,如果旋转角度过大,可能会导致模型过拟合,因为模型需要学习更多的变化模式。
随机裁剪(Random Cropping):如果在transform中设置了随机裁剪,当取不同值时,每次训练过程中数据集中的图像会被随机裁剪掉一部分。这可以帮助模型捕捉到图像的局部特征,并提高模型的泛化能力。然而,如果裁剪大小过小,可能会导致模型失去一些全局信息,从而影响准确性。
数据增强(Data augmentation):如果在transform中设置了数据增强,当取不同值时,每次训练过程中数据集中的图像会被进行一些修改,例如随机翻转、旋转、缩放等。这可以大大增加模型的泛化能力,因为它能够让模型看到更多的变化模式。然而,如果增强操作过于剧烈,可能会导致模型失去对原始图像的记忆,从而影响准确性。
归一化(Normalization):如果在transform中设置了归一化操作,当取不同值时,数据集中的特征值会被映射到不同的范围内。这可以帮助模型更快地收敛并提高训练的稳定性。然而,如果归一化的范围过大或过小,可能会导致模型无法正确学习到数据的分布特征,从而影响准确性。
3. epoch 和 batch 的区别? 在深度学习中,epoch和batch是两个重要的概念,它们都与模型的训练过程相关,但具有不同的含义和作用。
epoch:epoch指的是一个完整的训练过程,即使用整个训练集进行一次完整的训练。在每个epoch中,模型将从训练集的起始点开始,依次遍历每个样本,并对其进行前向传播和反向传播,以更新模型的参数。一个epoch结束后,模型将重新开始遍历整个训练集,重复这个过程,直到达到预设的迭代次数或满足收敛条件。
batch:batch指的是将整个训练集分成若干个小块(即批量),每次训练时只使用其中一个批量数据进行训练。在每个batch中,模型将使用当前批量数据进行一次前向传播和反向传播,并更新模型的参数。然后,模型将移动到下一个batch,继续进行训练。在每个epoch中,模型通常会使用多个batch进行训练,以提高训练效率。
4. 1x1的卷积和 FC 有什么区别?主要起什么作用? 1x1的卷积层是一种特殊的卷积层,其卷积核的大小为1x1。它对输入数据进行线性的变换,并通过激活函数对结果进行非线性映射。1x1的卷积层在深度神经网络中的作用包括:
降维/升维:1x1的卷积层可以用于改变输入数据的通道数,从而实现降维或升维的功能。通过将通道数减少,可以减少网络的参数数量,提高计算效率;通过将通道数增加,可以增加网络的表达能力,使网络能够适应更复杂的输入数据。
跨通道信息交互:1x1的卷积层可以用于跨通道的信息交互。通过将不同通道的输入数据进行线性组合和变换,可以在不同通道之间传递信息,增强网络对输入数据的理解和表达能力。
参数共享:1x1的卷积层可以实现参数共享,即不同的神经元可以共享同一组参数。这可以有效减少网络的参数量,提高网络的泛化能力。
全连接层(FC)是一种常见的层类型,它通过将输入数据与一个权重矩阵进行乘积,并将结果进行加权求和得到输出。全连接层在深度神经网络中的作用包括:
分类/回归:全连接层通常用于网络的最后一层,用于进行分类或回归任务。它可以将网络的输出映射到类别标签或连续的数值上。
特征组合:全连接层可以用于对输入数据进行特征组合。通过将不同的特征进行线性组合和变换,可以生成新的特征表示,提高网络对输入数据的理解和表达能力。
参数调整:全连接层可以通过调整权重矩阵和偏置向量来调整网络的输出结果。这使得全连接层可以用于优化网络的性能,例如提高准确率或降低损失值。
5. residual leanring 为什么能够提升准确率? 残差学习(Residual Learning)是一种在深度神经网络中提高训练效果的方法,它通过在神经网络中引入残差块(residual block),使得网络可以学习到残差映射 ,即从输入到输出的差异。残差学习的主要思想是在网络中加入短路连接(shortcut connection),使得网络能够直接学习到输入和输出之间的残差映射 ,而不用学习复杂的非线性变换。
残差学习能够提升准确率的原因有以下几点:
缓解了梯度消失问题:在深度神经网络中,当网络层数增加时,梯度可能会逐渐消失,导致网络难以训练。残差学习通过引入短路连接,使得每一层的梯度可以直接从前一层传递到后一层,缓解了梯度消失的问题,提高了网络的训练效果。
增加了网络的非线性能力:残差学习中的残差块引入了非线性变换,使得网络能够学习更加复杂的映射关系。通过学习残差映射,网络能够更好地适应输入和输出之间的复杂关系,提高了网络的非线性能力,从而提高了网络的准确率。
增加了网络的表达能力:残差块中的参数可以通过训练进行优化,从而使得网络能够更好地适应不同的数据分布和任务需求。通过引入残差块,网络能够更好地捕捉数据的变化趋势和规律,提高了网络的表达能力,从而提高了网络的准确率。
6. 代码练习二里,网络和1989年 Lecun 提出的 LeNet 有什么区别?
LeNet1仅在最后一层使用了全连接层,而代码二使用了3层全连接层
LeNet1使用的是平均值池化,而代码二使用的是最大值池化
LeNet1未使用优化器,而代码二使用了Adam优化器
7. 卷积以后feature map 尺寸会变小,如何应用 Residual Learning?
使用扩张卷积(dilated convolution)来保持特征图的尺寸。扩张卷积是一种在卷积操作中使用不同的扩张率(dilated rate)来扩大卷积核的间隔,从而在保持特征图尺寸的同时增加特征图的感受野大小。
使用1×1的卷积核
8. 有什么方法可以进一步提升准确率?