- MNIST 数据集分类:构建简单的CNN对 mnist 数据集进⾏分类。同时,还会在实验中学习池化与卷积操作的基本作⽤
- CIFAR10 数据集分类:使⽤ CNN 对 CIFAR10 数据集进⾏分类
- 使⽤ VGG16 对 CIFAR10 分类
导入包:
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")
list(model.parameters())中list()函数是将对象转换为相应的python列表对象,便于后续的操作,而model.paramenters()是torch中的函数一般是torch.nn.paramete()用于获取一个模型的参数,而nelement()函数是torch中求一个张量中的元素的个数。
1.MNIST 数据集分类:
1. 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
功能:transform
参数用于对加载的输入图像进行转换操作。
类型: 它通常是一个可调用的对象,比如torchvision.transforms
模块中的各种变换函数或自定义的变换函数。
应用场景: 在深度学习中,常见的操作包括图像的归一化、裁剪、翻转、旋转、将图像转换为张量(Tensor)等。transform
参数可以帮助我们将原始图像数据处理成适合神经网络输入的形式。 - target_transform 类似与transform但是transform是用于图像的预处理而target_transform是用于标签的预处理
另外值得注意的是,DataLoader是一个比较重要的类,提供的常用操作有:batch_size(每个batch的大小)一个epoch训练的次数是 样本数/batchsize, shuffle(是否进行随机打乱顺序的操作), num_workers(加载数据的时候使用几个子进程)
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)
ToTensor()用于讲图像转化为张量再用Normalize((0.1307,),(0.3081,))进行标准化
显示数据集中的部分图像:
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');
i + 1
的作用:
假设你在一个循环中创建子图,i
是循环的索引变量。因为 plt.subplot
的 index
从 1 开始,而 Python 的循环索引 i
通常从 0 开始,所以需要使用 i + 1
来将索引调整为从 1 开始。
1.2.创建网络:
定义网络时,需要继承nn.Module,并实现它的forward方法,把网络中具有可学习参数的层放在构造函数__init__中。
只要在nn.Module的子类中定义了forward函数,backward函数就会自动被实现(利用autograd)。
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(
#Linear(input_size,n_hidden)第一个参数是输入的尺寸 第二个是输出的尺寸
nn.Linear(input_size, n_hidden),
nn.ReLU(), # ReLU 激活函数
nn.Linear(n_hidden, n_hidden),
nn.ReLU(), # ReLU 激活函数
nn.Linear(n_hidden, output_size), # 输出层,全连接,输出大小为 output_size
nn.LogSoftmax(dim=1) # 最后的激活函数,使用 LogSoftmax 进行分类
)
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
sequential定义的网络结构和直接定义的网络结构之间的区别:
代码简洁性 | 简洁,适合简单线性模型 | 相对复杂,尤其是当网络结构简单时 |
灵活性 | 灵活性有限,只能按顺序堆叠层 | 灵活性高,支持复杂的前向传播逻辑 |
适用场景 | 简单网络结构,如多层感知机(MLP) | 复杂网络结构,如 CNN、ResNet、RNN等 |
自定义前向传播逻辑 | 不支持,无法自定义复杂操作(如跳跃连接、条件语句) | 支持,前向传播过程完全可自定义 |
调试和中间输出 | 较难,因为所有层都被打包在一起 | 较易,可以在 forward 中插入中间输出 |
Sequential定义的网络结构只能按照定义的顺序执行,不能在网络中加入逻辑语句和残差链接的操作适用于简单的网络。
1.3 定义训练和测试函数
# 训练函数
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))
enumerate()
是 Python 的内置函数,作用是为可迭代对象添加一个计数器。在这个例子中,enumerate(train_loader)
会在每次迭代时,返回一个二元组:- 第一个值是迭代的计数器
batch_idx
,表示当前批次的索引。 - 第二个值是从
train_loader
中取出的数据批次,即(data, target)
,其中data
是输入数据,target
是对应的标签。
- 第一个值是迭代的计数器
1.4在小型全连接网络上训练(Fully-connected network)
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)
1.5 在卷积神经网络上训练:
# 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)
通过上面的测试结果,可以发现,含有相同参数的 CNN 效果要明显优于 简单的全连接网络,是因为 CNN 能够更好的挖掘图像中的信息,主要通过两个手段:
- 卷积:Locality and stationarity in images
- 池化:Builds in some translation invariance
1.6 打乱像素顺序再次在两个网络上训练与测试
考虑到CNN在卷积与池化上的优良特性,如果我们把图像中的像素打乱顺序,这样 卷积 和 池化 就难以发挥作用了,为了验证这个想法,我们把图像中的像素打乱顺序再试试。
首先下面代码展示随机打乱像素顺序后,图像的形态:
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')
定义打乱后的数据:
# 对每个 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))
再次在两个网络上训练:
perm = torch.randperm(784)
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_perm(model_fnn, perm)
test_perm(model_fnn, perm)
perm = torch.randperm(784)
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_perm(model_cnn, perm)
test_perm(model_cnn, perm)
可以观察到打乱像素后CNN网络性能明显下降了很多,这是因为对于卷积神经网络,会利用像素的局部关系,但是打乱顺序以后,这些像素间的关系将无法得到利用。
2.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。
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')
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]])
定义网络,损失函数和优化器:
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)
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')
# 得到一组图像
images, labels = next(iter(testloader))
# 展示图像
imshow(torchvision.utils.make_grid(images))
# 展示图像的标签
for j in range(8):
print(classes[labels[j]])
把图片输入模型,看看CNN把这些图片识别成什么:
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))
从上图可以看出网络的性能较差,正确率只有63,故考虑对网络进行改进。上述网络使用的是5*5的卷积核考虑ResNet的思路将5*5的卷积换成两个3*3的卷积,这样网络可以更深同时参数会少于5*5的卷积核,同时加入BathNorm进行归一化处理,BN可以加快网络的收敛同时BN可以减少层与层之间输出的差距的影响因为在输入下一层之前会对其进行归一化,同时还加入Dropout丢弃部分的神经元防止网络过拟合,原网络是每次卷积之后进行MaxPooling但是改成3*3的卷积之后不能每次卷积之后进程MaxPooling因为原始图像是32*32,经过3*3卷积之后变成了30*30,如果进行2*2的pooling则尺寸就变成了15*15后续就无法pooling了除非使用偶数尺寸的卷积。除了上述改进还可以修改损失函数,如在Week1中将RELU改成LeakRELU来防止神经元死亡,感兴趣可以自行尝试。上述改进虽然可以增加模型的精度,但是因为FC1层的通道数增加了这大大增加了模型的参数,使得模型的训练时间大大增加,可以考虑对全连接层进行改进。
3.使用 VGG16 对 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_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')
RandomCrop(32, padding=4)
:在图像四周添加 4 像素的填充,然后随机裁剪出 32x32 大小的图像。这可以模拟不同的拍摄角度和位置,增加数据的多样性。RandomHorizontalFlip()
:随机水平翻转图像,这可以有效地增加数据集的多样性,尤其是当图像中的物体没有方向性时。
使用了 CIFAR-10 数据集的真实均值和标准差:这些值是根据 CIFAR-10 数据集预先计算得出的,表示整个数据集中各通道(红、绿、蓝)的均值和标准差
trainloader
的批量大小为128
。testloader
的批量大小也是128
。- 批量大小会影响训练速度和内存占用。较大的批量大小(如
128
)可以提高训练速度,但需要更多的内存。较小的批量大小(如8
)则训练过程会更慢,但适合内存较小的环境。
定义网络:
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(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)
# 网络放到GPU上
net = VGG().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=0.001)
网络在colab上训练比较缓慢,可以使用jupyter在本机训练,训练后正确率远高于网络二
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))
【第⼆部分:问题总结】思考下⾯的问题:
1.dataloader ⾥⾯ shuffle 取不同值有什么区别?
shuffle是决定数据是否打乱,取值为TRUE时,在每个epoch执行前都会打乱数据,这样模型每次训练的数据的顺序都不是相同的增强的随机性,而在测试阶段一般选FALSE因为可以保证结果的一致性。
2.transform ⾥,取了不同值,这个有什么区别?
表示了数据不同的预处理,如transforms.RandomCrop()表示对图片进行随机裁剪,transforms.RandomHorizontalFlip()表示对图片进行随机翻转,transforms.Normalize(mean, std)表示对数据进行标准化,transforms.ToTensor()表示将数据进行转换。transforms.Compose()将多个操作合并。
3.epoch 和 batch 的区别?
一次epoch要训练整个训练集,而一个batch只是训练集里的一个分组,例如1k个数据,一个batch可能是100,那么训练一个batch就是100张图片,而一个epoch就是1k张。
4.1x1的卷积和 FC 有什么区别?主要起什么作⽤?
参数个数:1*1的卷积参数个数为卷积核的个数也就是输出的通道数,而FC的参数个数是输入的尺寸*输出的尺寸,FC层的参数个数往往远大于卷积的参数个数。
功能:1*1的卷积每个卷积核对数据的通道都是执行相同的权重,而FC层每个神经元和不同的输入点的权重都不同也就是执行的操作不同。并且卷积可以将不同通道的特征融合到一起,FC层一般是网络的最后层用于预测。
5.residual leanring 为什么能够提升准确率?
残差链接可以防止网络过深导致的梯度反向传播的消失问题,因为残差连接是将某一层的输出直接作为后面某一层的输入,这样网络学的是残差 y=x+F(x),前面的网络就可以直接通过x这一项获取到梯度,避免了梯度过深导致的梯度无法传播的问题。
6.有什么⽅法可以进⼀步提升准确率?
增加epoch,更改激活函数,增强数据,使用残差链接同时让网络更深,使用Norm等归一化函数防止模型过拟合和加快收敛,使用正则化等dropout等技术防止过拟合。