OUC2022秋季软工09组第三次作业
声明
本博客为OUC2022秋季软件工程第三次作业
鄢凯瑞
一、视频学习
本次课程主要讲解了绪论、基本组成结构、卷积神经网络的典型结构。卷积神经网络应用于分类、识别、检测等,如一些比较成熟的技术人脸识别、表情识别、自动驾驶等等。深度学习三部曲:1. 搭建神经网络结构 2. 找到一个合适的损失函数 3. 找到一个合适的优化函数更新参数。卷积神经网络是由卷积层、池化层、全连接层交叉堆叠而成, 卷积是对两个实变函数的一种数学操作。池化是保留了主要特征的同时减少参数和计算量,防止过拟合,提高模型泛化的能力。它一般处于卷积层与卷积层之间,全连接层与全连接层之间,一般有最大值池化和平均值池化。全连接就是前一层网络的每个节点都与后面的节点相连。卷积神经网络结构有Alexnet、ZFNet、VGG、GoogleNet、ResNet。
- AlexNet :大数据训练、非线性激活函数(ReLU)、防止过拟合、双GPU实现
- ZFNet: 网路结构和AlexNet相同,仅仅是做了一些调参
- VGG: 是一个更深网络, 8layers -> 16 ,错误率从11.7% -> 7.3%
- GoogleNet:网络包含22个参数层,独立成块的层有100个,没有FC层
- ResNet: 残差学习网络,深度152层,错误率6.7% -> 3.57%
二、代码练习
1. MNIST 数据集分类
加载数据(MINIST)
显示数据集中的部分:
创建网络:
定义训练和测试函数:
在小型全连接网络上训练(Fully-connected network):
在卷积神经网络上训练:
打乱像素顺序再次在两个网络上训练与测试:
在全连接网络上训练与测试:
在卷积神经网络上训练与测试:
从打乱像素顺序的实验结果来看,全连接网络的性能基本上没有发生变化,但是 卷积神经网络的性能明显下降。
这是因为对于卷积神经网络,会利用像素的局部关系,但是打乱顺序以后,这些像素间的关系将无法得到利用。
2.CIFAR10 数据集分类
准确率还可以,通过改进网络结构,性能还可以进一步提升。在 Kaggle 的LeaderBoard上,准确率高的达到95%以上。
3.使用 VGG16 对 CIFAR10 分类
定义 dataloader:
VGG 网络定义:
网络训练:
报错cfg没有定义,检查代码发现前面是self.cfg, 改为self.cfg 没有报错。
但是在正向传播+反向传播+优化这里发生了错误,猜测可能是之前的改的不对,又将之前的self去了,但是还是不对,询问同学后知道不仅要加self而且要将2048改为512, 我修改后跑出结果如下:
然后进行测试, 测试结果为
可以看到,使用一个简化版的 VGG 网络,就能够显著地将准确率由 64%,提升到 84.92%。
三、相关问题解答
- dataloader 里面 shuffle 取不同值有什么区别?
dataloder是一个类,而shuffle(bool)是一个进行随机打乱顺序的函数, 参数为bool类型,默认情况下参数是false,此时每次迭代训练数据集时不会将输入数据顺序打乱;如果是true时每次迭代训练时都会将数据重新打乱。
- transform 里,取了不同值,这个有什么区别?
transform可以对数据进行变换,可用于PIL Image 或者 Tensor Image,可以对图像进行裁剪、翻转和旋转、图像变换、图像格式转换、系列变换、通用变换、组合变化,如transforms.CenterCrop(size)可以裁剪图像,进行中心裁剪为size * size 大小的图像;transforms.RandomHorizontalFlip§ 水平翻转,默认值为0.5, 大于0.5就会左右翻转。
- epoch 和 batch 的区别?
Batch大小是在更新模型之前处理的多个样本。Epoch数是通过训练数据集的完整传递次数。批处理的大小必须大于或等于1且小于或等于训练数据集中的样本数。可以将epoch设置为1和无穷大之间的整数值。可以根据需要运行算法,甚至可以使用除固定数量的epoch之外的其他条件来停止算法。它们都是整数值,并且它们都是学习算法的超参数,例如学习过程的参数,而不是学习过程找到的内部模型参数。必须为学习算法指定batch大小和epoch数。
- 1x1的卷积和 FC 有什么区别?主要起什么作用?
FC作用:将前一层的输出通过加权隐射到label上去
1x2卷积作用:1*1的卷积可以用于降维(减少通道数),升维(增加通道数),代替fc成为一个分类器
区别:权值共享,参数量较同等功能的fc层相比少,使用了位置信息;fc层对于训练样本要求统一尺寸,但是1*1的卷积不会受该规定的限制
- residual leanring 为什么能够提升准确率?
通过使用residual learning我们可以加深网络层数,我们知道在不断加深神经网络深度时,模型的准确率会先上升然后达到饱和,此时如果再持续增加网络深度会使准确率下降,但如果在后面增加几个恒等映射层,这样即使增加网络深度,误差也不会增加,同时它也可以有效解决梯度消失问题。
- 代码练习二里,网络和1989年 Lecun 提出的 LeNet 有什么区别?
激活函数不同,LeNet使用sigmoid函数作为激活函数,代码练习二中使用ReLu作为激活函数。
- 代码练习二里,卷积以后feature map 尺寸会变小,如何应用 Residual Learning?
以使用带padding的大小为1*1的卷积来改变feature map尺寸使其可以应用Residual learning。
- 有什么方法可以进一步提升准确率?
- 加深网络层数
- 更换激活函数、损失函数
- 增大数据集
王景琪
### 卷积神经网络(Convolutional Neural Network)
绪论
1.卷积神经网络应用
- 分类
- 检索
- 人脸识别
- 表情识别
- 检测
- 人脸验证
- 分割(有点像抠图)
- 自动驾驶
2.传统神经网络vs卷积神经网络
深度学习三部曲
step1. 搭建神经网络结构
step2. 找到一个合适的损失函数
损失函数是衡量吻合度的,可以调整参数/权重W,使得映射的结果和实际类别吻合
传统神经网络也可以应用到计算机视觉,但是会有如下问题:参数太多,会造成过拟合(泛化性能差)
卷积神经网络:局部关联,参数共享
step3. 找到一个合适的优化函数,更新参数
相同之处:分层:卷积层,激活层,池化层,全连接层
基本组成结构
卷积:卷积是对两个实变函数的一种数学操作 实变函数:以实数为自变量的函数
二维卷积
一维卷积:常用在信号处理中,用于计算信号的延迟累计
滤波器(filter)
卷积核:信息的衰减率
基本概念:
- 输入
- 卷积核/滤波器
- 权重
- 感受野(receptive field)
- 特征图(feature map)
- padding(输入的两边补0)
- 深度(channel)
- 输出(output)
- 步长(stride)
池化:(pooling layer):保留了主要特征的同时减少参数和计算量,防止过拟合,提高模型泛化的能力。它一般处于卷积层与卷积层之间,全连接层与全连接层之间。
- 最大值池化
- 平均值池化
全连接:(FC layer)两层之间的所有神经元都有权重链接,通常全连接层在卷积神经网络的尾部,全连接层参数量通常最大
小结:
- 一个典型的卷积神经网络是由卷积层、池化层交叉堆叠而成,最后一层是全连接层
卷积神经网络典型结构
AlexNet
非线性激活函数:ReLU函数:优点:
- 解决了梯度消失问题
- 计算速度特别快,只需要判断输入是否大于0
- 收敛速度远快于sigmoid
AlexNet分层解析
ZFNet
网络结构与AlexNet相同
VGG
VGG是一个更深网络 8->16
GoogleNet
- 网络包含22个带参数的层,独立成块的层总共有100个
- 参数量大概是Alexnet的1/12
- 没有FC层
inception模块 用小的卷积核替代大的卷积核
ResNet(残差学习网络)
代码练习
MNIST 数据集分类
-
加载数据(MINIST)
-
创建网络
-
在小型全连接网络上训练(Fully-connected network)
- 在卷积神经网络上训练
# 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
-
打乱像素顺序再次在两个网络上训练与测试
-
在全连接网络上训练与测试
Number of parameters: 6442 Train: [0/60000 (0%)] Loss: 2.264858 Train: [6400/60000 (11%)] Loss: 2.041490 Train: [12800/60000 (21%)] Loss: 1.552290 Train: [19200/60000 (32%)] Loss: 1.093537 Train: [25600/60000 (43%)] Loss: 0.799383 Train: [32000/60000 (53%)] Loss: 0.847221 Train: [38400/60000 (64%)] Loss: 0.756004 Train: [44800/60000 (75%)] Loss: 0.723714 Train: [51200/60000 (85%)] Loss: 0.438829 Train: [57600/60000 (96%)] Loss: 0.474032 Test set: Average loss: 0.5751, Accuracy: 8348/10000 (83%)
-
在卷积神经网络上训练与测试:
Number of parameters: 6422 Train: [0/60000 (0%)] Loss: 2.300903 Train: [6400/60000 (11%)] Loss: 2.282535 Train: [12800/60000 (21%)] Loss: 2.261807 Train: [19200/60000 (32%)] Loss: 2.111837 Train: [25600/60000 (43%)] Loss: 1.717916 Train: [32000/60000 (53%)] Loss: 1.320999 Train: [38400/60000 (64%)] Loss: 0.960259 Train: [44800/60000 (75%)] Loss: 0.961738 Train: [51200/60000 (85%)] Loss: 0.636504 Train: [57600/60000 (96%)] Loss: 0.507474 Test set: Average loss: 0.6227, Accuracy: 8003/10000 (80%)
从打乱像素顺序的实验结果来看,全连接网络的性能基本上没有发生变化,但是 卷积神经网络的性能明显下降。
这是因为对于卷积神经网络,会利用像素的局部关系,但是打乱顺序以后,这些像素间的关系将无法得到利用。
-
CIFAR10 数据集分类
接下来定义网络,损失函数和优化器:
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')
网络在整个数据集上的表现:
Accuracy of the network on the 10000 test images: 62 %
使用 VGG16 对 CIFAR10 分类,链接:
VGG是由Simonyan 和Zisserman在文献《Very Deep Convolutional Networks for Large Scale Image Recognition》中提出卷积神经网络模型,其名称来源于作者所在的牛津大学视觉几何组(Visual Geometry Group)的缩写。
该模型参加2014年的 ImageNet图像分类与定位挑战赛,取得了优异成绩:在分类任务上排名第二,在定位任务上排名第一。
VGG16的网络结构如下图所示:
-
定义dataloader
需要注意的是,这里的 transform,dataloader 和之前定义的有所不同
-
定义VGG网络
-
网络训练
训练结果
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))
Accuracy of the network on the 10000 test images: 84.92 %
可以看到,使用一个简化版的 VGG 网络,就能够显著地将准确率由 64%,提升到 84.92%
思考
-
dataloader 里面 shuffle 取不同值有什么区别?
Dataloader中设置了shuffle是True,所以每次加载的数据都是随机的。
洗牌。默认设置为False。在每次迭代训练时是否将数据洗牌,默认设置是False。将输入数据的顺序打乱,是为了使数据更有独立 性,但如果数据是有序列特征的,就不要设置成True了
-
transform 里,取了不同值,这个有什么区别?
transform的作用:对图像进行一定的预处理,相当于扩展数据集。
transform的处理方法
1 裁剪-Crop
2 翻转和旋转——Flip and Rotation
3 图像变换
4 对transforms操作,使数据增强更灵活
-
epoch 和 batch 的区别?
epoch:当一个完整的数据集经过神经网络一次,并返回一次,这个过程称为一个epoch。
batch:当数据集很大的时候,对于每个epoch,很难将所有的数据集一次读入到内存中,这是需要将数据集分为几次读入,每次称为一个batch。
-
1x1的卷积和 FC 有什么区别?主要起什么作用?
1*1的卷积:
- 添加非线性特性
即保持特征图尺寸不发生变化且维持上一层的通道数,在增加网络的深度的同时令网络能够学习更为复杂的函数(特征信息)。
- 对通道数实现升维降维:一般用降维来减少计算量
FC(全连接层):起到分类器的作用。对前层的特征进行一个加权和,(卷积层是将数据输入映射到隐层特征空间)将特征空间通过线性变换映射到样本标记空间(也就是label)
区别:
全连接是把特征图拆开组成一个一维向量,再乘以一个权重向量,这两个向量中的元素一一对应所以输出结果是一个值。
11的卷积核那就是表明只由一个权重组成,如果特征图尺寸也是11的话,那输出就是一个值,此时与全连接完全一样。但是如果特征图尺寸不是11,而是wh的话,那么11的卷积输出就不是一个值而是wh的一个矩阵。
用11卷积代替全连接应该是基于输入尺寸的考虑,全连接的输入是特征图所有元素乘以权重再求和,但是这个权重向量是在设计网络的时候就需要固定的,所以全连接没办法适应输入尺寸的变化只能固定。但是11卷积的输出与输入尺寸是一样大的,输出尺寸可以随着输入尺寸的变化而变化,所以1*1卷积无需固定输出尺寸。
-
residual leanring 为什么能够提升准确率?
残差的思想都是去掉相同的主体部分,从而突出微小的变化,引入残差后的映射对输出的变化更敏感。很明显,在残差网络中输出的变化对权重的调整影响更大,也就是说反向传播的梯度值更大,训练就更加容易。
-
代码练习二里,网络和1989年 Lecun 提出的 LeNet 有什么区别?
- LeNet :最早用于手写数字识别的CNN网络
-
有什么方法可以进一步提升准确率?
-
增加更多数据
-
2.处理缺失值和异常值
-
特征工程学:这一步骤有助于从现有数据中提取更多信息。新信息作为新特征被提取出来。这些特征可能会更好地解释训练集中的差异变化。因此能改善模型的准确率。
-
特征选择:特征选择是寻找众多属性的哪个子集合,能够最好的解释目标变量与各个自变量的关系的过程。
你可以根据多种标准选取有用的特征
-
使用多种算法
-
交叉验证:如果想解决这个问题,我们必须使用交叉验证技术(cross validation)。交叉验证是数据建模领域最重要的概念之一。它是指,保留一部分数据样本不用来训练模型,而是在完成模型前用来验证。
-
王义钧
一、视频学习部分
1、AlexNet
第一个现代卷积网络是AlexNet,也正是AlexNet在ImageNet竞赛上的成功,使得卷积神经网络以及深度学习开始风靡,引发了这一轮的深度学习热潮。AlexNet的基本模式同LeNet相同,其成功的关键原因有两个,一是网络时代大量增加的数据,二是GPU提供的算力支持了大型网络的训练。在LeNet的时代,网络尚未完全普及,在图像领域的机器学习研究通常使用小样本,而且样本的维度也不大。在这样的情况下,神经网络并不比传统的机器学习方法更有优势,而且神经网络的训练和调参难度也比传统机器学习方法大,因此神经网络没有获得多少青睐。而后来互联网的普及使得数据集的规模有了增大的可能,而gpu的使用,使得训练更大更深的神经网络更加容易。在这两个条件的基础上,AlexNet取得了成功。
2、VGG
下一个学习的网络结构是有名的VGG Net。其创新点在于提出了卷积块的概念,将若干个卷积层以及激活函数打包成一个块,通过多个块的堆叠构造神经网络,方便了网络结构的设计,并且也取得了很好的效果。其另一个发现是多个33的卷积堆叠,效果要好于使用大尺寸的卷积,例如77,11*11,即使用窄且深的网络比宽且浅的网络好。
一个vgg块包含若干个卷积和ReLU,每个块的第一个卷积会将输入的通道进行改变(或不改变),后续的所有卷积都采用尺寸为3,步长为1,padding为1的卷积,输入的通道和输出的通道数一致,因此在一个vgg块内部,特征图的尺寸不会发生变化,直到块的末尾。
3、残差网络ResNet
如果AlexNet让深度学习风靡,那么ResNet带来了真正的深度。残差连接思想的提出使得我们训练极深的网络成为了可能,残差连接几乎成为了现在深度学习不可缺少的技术。
残差链接使得网络层数的加深和复杂可以包含原有的网络,即模型的效果最坏不会坏于加深之前的效果。具体的计算方式是,每次将输入直接与输出相加,使得网络变成x+f(x)的形式,这样网络最差也是恒等映射,y=x,不会让效果变得更差。更重要的是,残差连接使得梯度可以通过“捷径”从深处传递到前面,缓解了梯度弥散的问题,使得深层的网络更容易训练。
二、代码练习部分
1、MNIST 数据集分类:构建简单的CNN对 mnist 数据集进行分类。
训练前准备:引入pytorch包 计算模型中有多少参数 同时更改训练为GPU。
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")
①加载数据集(MNIST)
使用datasets.MNIST方法 从training.pt创建数据集,放入root文件夹下。两个batch大小不同,但均将数据集打乱。
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)
运行结果:
显示数据集中的部分图像
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');
②创建网络
定义网络时,需要继承nn.Module,并实现它的forward方法,把网络中具有可学习参数的层放在构造函数init中。
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