VGG网络结构
这也更好的块状结构,256个卷积核
卷积就是我们的一个特征图啊往往都会缩小 ,然后的话但它通道不会变.卷积一般是使用我们的通道C变大,磁化但是它的通道就是我们那个H和W一般都会变小.下采样的意思就是使分辨率变小
- vgg—block内的卷积层都是同结构的意味着输入和输出的尺寸一样,且卷积层可以堆叠复用,其中的实现是通过统一的size为3×3的kernel size + stride1 + padding1实现。
- maxpool层将前一层(vgg—block层)的特征缩减一半 使得尺寸缩减的很规整,从224—112—56—28—14—7。其中是通过pool size2 + stride2实现。
- 深度较深,参数量够大·较深的网络层数使得训练得到的模型分类效果优秀,但是较大的参数对训练和模型保存提出了更大的资源要求。(因为到后面的RESNET的时候,你会发现它解决就是我们模型较深,使我们使我们的效果不好的问题)
- 较小的filter size/kernel size **这里全局的kernel size都 为3×3,相比以前的网络模型来说,尺寸足够小。
- 你会发现我们的选集都是3×3,3×3有什么好处呢,参数少对吧防止过滤
参数详解
VGGNet通过在传统卷积神经网络模型(AlexNet)上的拓展,发现除了较为复杂的模型结构的设计(如GoogLeNet)外,深度对于提高模型准确率很重要,VGG和之前的AlexNet相比,深度更深,参数更多(1.38亿),效果和可移植性更好,且模型设计的简洁而规律,从而被广泛使用。还有一些特点总结如下:
1、小尺寸的filter(3×3)不仅使参数更少,效果也并不弱于大尺寸filter如5×5
2、块的使用导致网络定义的非常简洁。使用块可以有效地设计复杂的网络。
3、AlexNet中的局部响应归一化作用不大
VGG代码实战
model.py
import torch
from torch import nn
from torchsummary import summary
class VGG16(nn.Module()):
def __init__(self):
super(VGG16, self).__init__()
self.block1 = nn.Sequential(
nn.Conv2d(in_channels=1, out_channels=64, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
self.block2 = nn.Sequential(
nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.block3 = nn.Sequential(
nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
self.block4 = nn.Sequential(
nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
self.block5 = nn.Sequential(
nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
self.block6 = nn.Sequential(
nn.FLatten(),
nn.Linear(7 * 7 * 512, 4096),
nn.ReLU(),
nn.Linear(4096, 4096),
nn.ReLU(),
nn.Linear(4096, 10),
)
def forward(self, x):
x = self.block1(x)
x = self.block2(x)
x = self.block3(x)
x = self.block4(x)
x = self.block5(x)
x = self.block6(x)
return x
if __name__ == '__main__':
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = VGG16().to(device)
print(summary(model, (1, 224, 224)))
model_train.py这次训练的结果不是会很好,暴露问题,使用1060训练了332min即5.5个小时
接下来我们来看一下模型的一个
训练的一个结果啊
呃前面也讲了
这次的训练的结果呢
它不会很好
我们是用来暴露我们的问题了
然后的话根据我们的问题干嘛啊
就是来解决对应的问题嘛对吧
首先我们来看一下就是整个训练的一个过程啊
训练过程是332分钟
也就是将近大概是五个半小时左右嘛对吧
五个半小时左右时间还是很长的啊
20轮五个半小时还是很长的
然后的话我们这个
你其实我们从这个通过什么打印出来
一个训练日志可以发现啊
基本上它的一个什么呃精度都是在0.9左右
也就是在什么啊
0.09左右也就10%左右嘛是吧
然后它的loss值啊
也基本上大概大概在这个值左右吧
基本上是不变的一个过程
而通过我们的图呢也更加直观
他这个精确度呢基本上就在这个附近
然后他那个loss值啊也也也不收敛
也就是这两个loss值和我们精确度都不收敛
额这里我插插一个
就是题外话
当时我在写这段代码的时候
我肯定也做了一些测试嘛
哎当时会出现什么情况呢
我训练完之后呢
诶他出现这个问题呃
我我我检查一下了模型啊
发现的模型好像是吧
没有什么问题啊是吧
我就很苦恼
后面呢就是干嘛
后面过了几天之后
我同样代码我也没有改
我也很忙嘛
我没有改
然后又运行同样的代码
我发现诶他又运行OK了
然后我就一直思考
我到底是不是有人动了我代码
还是我自己就是迷迷糊
不知道
动了代码之后呢
然后运行运行一下代码之后呢
然后呃它就变好了
对不对
就比如我们的精度啊
一直一直上升
可能最后在大概在90%多的准确度
还是蛮蛮不错的
对不对
然后的话那代码我也没动
过几天我又又又重新训了一下
之后发现又又不行了
然后不断的重复又训又重复不训呃
就重复训
然后每次是吧嗯结果都不一样啊
就是比如说训了五次之后
哎呀它的精度都是这样子的
不收敛是吧啊
过了下午的时候呢
或者关关机重启之后呢
可能过了一会儿他又好了
哎那时候我就不知道是什么样的原因
就很疑惑
我当时怀疑是不是环境的问题
问哪个问题啊
终于在后面啊
我终于知道问题出在哪里啊
这里我给大家讲一下啊
首先啊给大家讲个这个知识点啊
首先我们模型搭建
我这里给大家讲一下
肯定是没有问题的啊
这里我可以给大家保证
这里模型搭建肯定是没有问题的
然后的话啊这里给大家讲这个知识点啊
首先我们这个VGG对不对
危机是十六十六个层
对不对
然后的话因为模型啊
它相对于我们前面NSNET和n net
它相对来说比较深
声对不对
那就意味着什么呢
因为你这里输入一个X是吧
输入X
然后这里比如说通过模型
通过模型
通过模型啊
我们打个省略号
通过模型对不对
然后一直到最后的Y对啊
这里肯定是没问题的
对不对
然后的话呢嗯对的话
你最好还能得出我们的loss值
你通过loss值不断的去反馈过来是吧
去更新我们的一个什么W的
对不对
然后我们前面也也知道
我们W更新应该是什么样子的
应该是这样的
对不对是吧
这里是我们的什么loss值的函数
然后对对什么对我们的W是一个什么
是求导的过程
事实上你你会发现随着网络越越来越深啊
随着网络越来越深
你其实你很容易出现什么
前面我也讲过
也容易出现什么梯度消失这种问题
就消失或者梯度爆炸
这个问题导致你的W更新之后
其实事实上效果不会很好
或者直接就不收敛了
不收敛对不对啊
不收敛啊
直接就不收敛
因为什么呢
因为你这里链式求导嘛
你比如说打个简单
比方你你你你的你的一个loss值对什么
对我们的一个你的loss是对什么
对我们的W进行求导
事实上它是这样子的
他可能对A函数求导
A函数又是B函数的导数
是吧啊
B函数可能是C函数的导数
C函数可能是D函数的导数
对不对
然后D函数
D函数可能才是W的函数的导数
事实上嗯这个还是少的
可能就五层
事实上你想象一下就是五层十层是吧
呃可能十几层他可能就是越来越深了嘛
越来越深的话
你会发现你会发现
就是如果你你你你链式求导的话
你这里导数假设是很小的话
你这里也很小
你这里也很小
你这小这里也很小的话就会导致什么
你这里这个值趋近于零嘛
就问你这里减跟没减它不是一样的吗
你W就会不更新
对不对
然后的话然后的话往往往往会出现什么情况呢
就是因为一开始是随机的嘛
应该是随机的
对不对
一开始随机会出现什么过大或过小
过大或过小的这种情况是吧
前面我们在讲我们那个什么原理的时候
再讲深度学习那个呃前面那个基础知识的时候
我们前面也讲了嘛
就是我们前面用一个案例去计算什么
梯度下降法
就是反向传播这样的过程嘛
当时的话我们W啊是吧
我们一个初始化的值
比如说等于一
那假设你初始化值可能等于一个过大一个值
你你去比如说你这里啊过大值是吧
事实上真实值最优的值可能是
假设我们W初始值是100上
最优的值可能是什么
是0.1
有可能吧
然后的话你这里啊你正常
哪怕你正常更新的情况下是吧
正常更新的情况下
它会导致什么
你最后你其实你更新完之后
你可能再乘个学习率之后
你这里相当于减一个0.0001
对不对
有这种可能吧
有可能吧
你讲完之后呢
事实上你0.001和什么和99.9
9999几乎上没什么区别嘛
但你最优的一个什么W
应该是在这个区域嘛对吧
所以说最后导致你嘛模型它不收敛
事实上我们希望什么
事实上你其实你的一个码
你随机初始就是你的你W1开始随机随机
随机随机什么赋值的时候
你不应该这么这么这么这么这么这么随便
对不对
所以的话就是我们会有什么
会有一个就是随机初始化的一个方法是吧
来来给你们再再再么在训练我们模型之前
我们把我们把我们W按照一定的方式是吧
按照一定的方式去初始化
按照一个方法去初始化哎
让他不至于这么离谱吗
明白我意思吧
而且因为W会乘上乘上我们对应的X
它会经过什么
经过我们的激活函数
比如六激活函数啊
或者什么SM的激活函数啊
事实上在有时候
这六激活函数或者single mode激活函数啊
它在某个区间内它在什么导数是怎么说呢
或者趋向于趋向于零的
对不对是吧
或者比如说我们LOL函数
直接就是这样一个分段函数
在这个区间内
在这个区间它是没有没有导数的
对不对是吧
如果你的值所以就落在这里的话
你就不更新了呀
对不对
我们可能希望这个值可能落在这个附近
在某个值的时候它不更新啊
再有的只是它更新
对不对
这样我们希望这样子的吧
如果你这个W乘以X的值全部落在这个地方
那那你就全部去按零
它直接不更新了呀
那你W减去最后减去了W等于什么
等于减去一个几乎接近于零的一个数
他就不更新了吗
那最后你等于什么
你的loss值
你的loss是肯定是不会下降的呀
最后你的精确度也也不会上升的呀
明白我的意思吧
所以其实就是有这样的一个方法来干嘛
来初始化我们的W这样子怎么干嘛
就使我们最后的一个更新的速度
第一个可能会变快
第二个什么不至于像刚刚那样不收敛啊
所以的话当时啊就是在写这段代码的时候
我没有加上了
加上一个
这就是我们权重初始化这个这一项导致什么
导致我们模型在训练的时候诶
他好像什么就是一直不收敛是吧
然后的话就又又前面也讲了
那可能过一段时间之后
我再重新运行这段代码的时候是吧
哎可能当时他的因为权重是那个W是随机的嘛
W随机的话可能恰好诶是吧
恰好这一组WA比较接近我们真实值
这组W比较接近我们的真实真实W对不对
哎那可能更新很快就更新好了是吧
所以就会导致什么呢
每一次更新之后是每一次运行
可能过五次运行它不收敛
可能第六次他又收敛了
所以当时就让我很苦恼啊
所以啊所以当我加了什么
加上我们一个什么啊
权重初始化的一个什么方法之后
初始化啊
初始化复制这个方法之后呢
哎基本上你在什么在我们那个运行之后啊
他的一个什么就是结果是很稳定的啊
基本上就是呃大概十轮二轮之后
他就基本收敛了
所以的话接下来我们来看一下
就是既然我们权重是需要初始化的对吧
我们权重是需要初始化的
呃
那我们用代码怎么去
怎么去初始化我们那个权重啊
这里面还讲一下
其实你会发现我们那个ALICENE和我们的net
他当时是没有初始化这个选项的
呃我思考了一下
可能因为这个这个权重啊
这个这个这个模型它不够那么深
所以的话呢可能全都在在在我们什么随机随机
什么随机进行什么啊
进行一个复制之后
然后进行更新
它也是OK的
明白我意思吧
事实上在alex net我也出现过什么
就是当时没有权重初始化是吧
然后我运气说哎发现他也没收敛
有用这种情况
但基本上我基本上每次运行这个ANNASNET的时候
它的效果都是还是OK的
正常都会都会训练的
但是呢随着网络越来越深
比如说我们什么VG啊
后面讲的google
后面讲公开的
它都是需要什么权重知识化的
否则很容易出现什么
很容易出现A你你你你你你你运行
然后的话可能二轮他都不收敛啊
loss值也不下降是吧
我们那个精度也不上升这种情况啊
所以的话这一点我们先讲一下
这个就是我们什么权重初始化
怎么在代码当中加上一个权重初始化
这个方法使我们权重啊
更符合我们真实的实际情况啊
好吧
接下来我们来赚钱
我们这个定义的代码