X
首页
技术
模拟电子
单片机
半导体
电源管理
嵌入式
传感器
最能打国产芯
应用
汽车电子
工业控制
家用电子
手机便携
安防电子
医疗电子
网络通信
测试测量
物联网
最能打国产芯
大学堂
首页
直播
专题
TI 培训
论坛
汽车电子
国产芯片
电机驱动控制
电源技术
单片机
模拟电子
PCB设计
电子竞赛
DIY/开源
嵌入式系统
医疗电子
颁奖专区
【厂商专区】
【电子技术】
【创意与实践】
【行业应用】
【休息一下】
最能打国产芯
活动中心
直播
发现活动
颁奖区
电子头条
参考设计
下载中心
分类资源
文集
排行榜
电路图
Datasheet
最能打国产芯
嵌入式系统
[分享] 动手学深度学习(六):卷积神经网络与现代卷积神经网络
EliorFoy
2024-11-10 20:31
楼主
[个人博客] 因为在实际例子中如果想要使用全连接层,如果数据维度比较大要耗费的GPU资源很多,而卷积神经网络(CNN)则能够用较少的参数同时有平移不变性.在图像检测中划分局部区域进行预测. # (一) 卷积神经网络概述 卷积神经网络的设计就是用于探索图像数据,下以图像为例. 将图像划分为若干个区域分别与核函数(向量)相乘得到输出向量.给出一个图就能很直观地明白卷积的过程了: ![互相关](https://zh-v2.d2l.ai/_images/correlation.svg) 具体的代码实现也很简单: ```python class Conv2D(nn.Module): def __init__(self, kernel_size): super().__init__() self.weight = nn.Parameter(torch.rand(kernel_size)) self.bias = nn.Parameter(torch.zeros(1)) def forward(self, x): return corr2d(x, self.weight) + self.bias ``` **边缘检测** 当卷积核是一个二维张量`[1.0,-1.0]`的时候就能检测水平相邻两元素是否相同.如果是相同的才会输出0. # (二) 卷积核学习 有时候一些复杂的卷积核我们不想去手动设计那么我们也就可以通过"学习"(参数递归)来更新卷积核.我们先构造一个卷积层,并将其卷积核初始化为随机张量。接下来,在每次迭代中,我们比较`Y`与卷积层输出的平方误差,然后计算梯度来更新卷积核。 以边缘检测为例,我们可以这样更新: ```python # 构造一个二维卷积层,它具有1个输出通道和形状为(1,2)的卷积核 conv2d = nn.Conv2d(1,1, kernel_size=(1, 2), bias=False) # 这个二维卷积层使用四维输入和输出格式(批量大小、通道、高度、宽度), # 其中批量大小和通道数都为1 X = X.reshape((1, 1, 6, 8)) Y = Y.reshape((1, 1, 6, 7)) lr = 3e-2# 学习率 for i in range(10): Y_hat = conv2d(X) l = (Y_hat - Y) ** 2 conv2d.zero_grad() l.sum().backward() # 迭代卷积核 conv2d.weight.data[:] -= lr * conv2d.weight.grad if (i + 1) % 2 == 0: print(f'epoch {i+1}, loss {l.sum():.3f}') ``` # (三) 填充和步幅 ![互相关](https://zh-v2.d2l.ai/_images/correlation.svg) 如图,输入是3×3但是输出是2×2.为了能让输出和输入保持同样的形状,可以在周围填充一圈0.此时输出形状与填充满足关系$\left(n_h-k_h+p_h+1\right) \times\left(n_w-k_w+p_w+1\right)$.(k是kernal,p是padding) ![填充](https://zh-v2.d2l.ai/_images/conv-pad.svg) 卷积也可以跳过中间位置每次滑动多个元素,每次滑动的元素数量称作步幅.此时输出形状与填充满足关系$(n_h-k_h+p_h+s_h)/s_h\times(n_w-k_w+p_w+s_w)/s_w$. ![步幅](https://zh-v2.d2l.ai/_images/conv-stride.svg) # (四) 多输入多输出通道 当图像不止有一种颜色的时候就对应了有多个通道,可以用多个核函数进行分别相乘.如图: ![两个输入通道](https://zh-v2.d2l.ai/_images/conv-multi-in.svg) 实现一个多输入通道卷积函数: ```python def corr2d_multi_in(X, K): # 先遍历“X”和“K”的第0个维度(通道维度),再把它们加在一起 return sum(d2l.corr2d(x, k) for x, k in zip(X, K)) ``` 如果需要一个多个输出通道的卷积函数: ```python # 相当于有多个核函数 K = torch.stack((K, K + 1, K + 2), 0) # torch.stack是将张量在一个维度上堆叠 def corr2d_multi_in_out(X, K): # 迭代“K”的第0个维度,每次都对输入“X”执行互相关运算。 # 最后将所有结果都叠加在一起 return torch.stack([corr2d_multi_in(X, k) for k in K], 0) ``` 此外还有1×1卷积层,通常用于调整网络层的通道数量和控制模型复杂性. # (五) 汇聚层 和之前的卷积过程的区别是对于每一个块不是进行每个位置的元素相乘然后所有元素求和而是找出最大的那个元素(最大汇聚层)或者平均值(平均汇聚层).如图: ![最大汇聚层](https://zh-v2.d2l.ai/_images/pooling.svg) 实现方式与前面卷积类似: ```python def pool2d(X, pool_size, mode='max'): p_h, p_w = pool_size Y = torch.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w + 1)) for i in range(Y.shape[0]): for j in range(Y.shape[1]): if mode == 'max': Y[i, j] = X[i: i + p_h, j: j + p_w].max() elif mode == 'avg': Y[i, j] = X[i: i + p_h, j: j + p_w].mean() return Y ``` 汇聚层同样可以控制填充和步幅,但是默认情况下,深度学习框架中的步幅与汇聚窗口的大小相同.也可以手动设定: ```python pool2d = nn.MaxPool2d(3, stride=(2, 3), padding=(0, 1)) ``` 汇聚层使用多个通道的时候不是像卷积层一样一个卷积核与多个输入通道都进行运算而是在每个输入通道上单独运算得到一个单独的结果. ```python X = torch.cat((X, X + 1), 1) # 也是连接但是不生成新的维度 ''' tensor([[[[ 0.,1.,2.,3.], [ 4.,5.,6.,7.], [ 8.,9., 10., 11.], [12., 13., 14., 15.]], [[ 1.,2.,3.,4.], [ 5.,6.,7.,8.], [ 9., 10., 11., 12.], [13., 14., 15., 16.]]]]) ''' pool2d = nn.MaxPool2d(3, padding=1, stride=2) pool2d(X) ''' tensor([[[[ 5.,7.], [13., 15.]], [[ 6.,8.], [14., 16.]]]]) ''' ``` # (六) LeNet卷积神经网络 LeNet神经网络是最早发布的卷积神经网络之一,在计算机视觉任务中有高效性能.它有两个部分: - 卷积编码器(两个卷积层) - 全连接层密集层(三个全连接层) ![LeNet](https://zh-v2.d2l.ai/_images/lenet.svg) 通过API我们可以将上图简洁实现如下: ```python net = nn.Sequential( nn.Conv2d(1, 6, kernel_size=5, padding=2), nn.Sigmoid(), nn.AvgPool2d(kernel_size=2, stride=2), nn.Conv2d(6, 16, kernel_size=5), nn.Sigmoid(), nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(), nn.Linear(16 * 5 * 5, 120), nn.Sigmoid(), nn.Linear(120, 84), nn.Sigmoid(), nn.Linear(84, 10)) ``` 训练使用了GPU: ```python #@save def train_ch6(net, train_iter, test_iter, num_epochs, lr, device): def init_weights(m): if type(m) == nn.Linear or type(m) == nn.Conv2d: nn.init.xavier_uniform_(m.weight) net.apply(init_weights) print('training on', device) net.to(device) optimizer = torch.optim.SGD(net.parameters(), lr=lr) # 随机梯度下降 loss = nn.CrossEntropyLoss()# 交叉熵 animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs], legend=['train loss', 'train acc', 'test acc']) timer, num_batches = d2l.Timer(), len(train_iter) for epoch in range(num_epochs): # 训练损失之和,训练准确率之和,样本数 metric = d2l.Accumulator(3) # 功能类似数组用于累加 net.train() for i, (X, y) in enumerate(train_iter): timer.start() optimizer.zero_grad() X, y = X.to(device), y.to(device)# 移动到GPU y_hat = net(X) l = loss(y_hat, y) l.backward() optimizer.step() with torch.no_grad(): metric.add(l * X.shape[0], d2l.accuracy(y_hat, y), X.shape[0]) timer.stop() train_l = metric[0] / metric[2] train_acc = metric[1] / metric[2] if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1: animator.add(epoch + (i + 1) / num_batches, (train_l, train_acc, None)) # 分批次画图 test_acc = evaluate_accuracy_gpu(net, test_iter) animator.add(epoch + 1, (None, None, test_acc)) print(f'loss {train_l:.3f}, train acc {train_acc:.3f}, ' f'test acc {test_acc:.3f}') print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec ' f'on {str(device)}') ``` # (七) 现代卷积神经网络 ## 1.深度卷积神经网络(AlexNet) AlexNet与LeNet相似,但是AlexNet有五个卷积层、两个全连接隐藏层和一个全连接层.两者对比如下: ![两个net的对比](https://zh-v2.d2l.ai/_images/alexnet.svg) 可以看出AlexNet处理的图像像素更大,卷积通道数也更多,最后几个全连接层有4096个输出.并且AlexNet使用了ReLU激活函数比sigmoid更容易训练(LeNet没使用是因为当时还没有ReLU激活函数的提出). 使用API实现如下: ```python net = nn.Sequential( nn.Conv2d(1, 96, kernel_size=11, stride=4, padding=1), nn.ReLU(), nn.MaxPool2d(kernel_size=3, stride=2), # 减小卷积窗口,使用填充为2来使得输入与输出的高和宽一致,且增大输出通道数 nn.Conv2d(96, 256, kernel_size=5, padding=2), nn.ReLU(), nn.MaxPool2d(kernel_size=3, stride=2), # 使用三个连续的卷积层和较小的卷积窗口。 # 除了最后的卷积层,输出通道的数量进一步增加。 # 在前两个卷积层之后,汇聚层不用于减少输入的高度和宽度 nn.Conv2d(256, 384, kernel_size=3, padding=1), nn.ReLU(), nn.Conv2d(384, 384, kernel_size=3, padding=1), nn.ReLU(), nn.Conv2d(384, 256, kernel_size=3, padding=1), nn.ReLU(), nn.MaxPool2d(kernel_size=3, stride=2), nn.Flatten(), # 这里,全连接层的输出数量是LeNet中的好几倍。使用dropout层来减轻过拟合 nn.Linear(6400, 4096), nn.ReLU(), nn.Dropout(p=0.5), nn.Linear(4096, 4096), nn.ReLU(), nn.Dropout(p=0.5), # 最后是输出层。由于这里使用Fashion-MNIST,所以用类别数为10,而非论文中的1000 nn.Linear(4096, 10)) ``` 训练使用与LeNet相同的`train_ch6`函数. ## 2.VGG块 一个VGG块由一系列卷积层组成后面再加上汇聚层. ```python def vgg_block(num_convs, in_channels, out_channels): layers = [] for _ in range(num_convs): layers.append(nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1)) layers.append(nn.ReLU()) in_channels = out_channels layers.append(nn.MaxPool2d(kernel_size=2,stride=2)) return nn.Sequential(*layers) ``` 每一个VGG块都有参数,VGG-11网络就是5个卷积块(8个卷积层+3个全连接层).用`conv_arch = ((1, 64), (1, 128), (2, 256), (2, 512), (2, 512))`来定义输入输出通道数就能实现VGG-11如下: ```python def vgg(conv_arch): conv_blks = [] in_channels = 1 # 卷积层部分 for (num_convs, out_channels) in conv_arch: conv_blks.append(vgg_block(num_convs, in_channels, out_channels)) in_channels = out_channels return nn.Sequential( *conv_blks, nn.Flatten(), # 全连接层部分 nn.Linear(out_channels * 7 * 7, 4096), nn.ReLU(), nn.Dropout(0.5), nn.Linear(4096, 4096), nn.ReLU(), nn.Dropout(0.5), nn.Linear(4096, 10)) ``` 训练与AlexNet还是类似,学习率可以略高一点,VGG-11的计算量比AlexNet计算量大. ## 3.网络中的网络NiN NiN使用由一个卷积层和多个1×1卷积层组成的块,去除了容易造成过拟合的全连接层,将它们替换为全局平均汇聚层.对比图如下: ![NiN](https://zh-v2.d2l.ai/_images/nin.svg) NiN的块如下: ```python def nin_block(in_channels, out_channels, kernel_size, strides, padding): return nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size, strides, padding), nn.ReLU(), nn.Conv2d(out_channels, out_channels, kernel_size=1), nn.ReLU(), nn.Conv2d(out_channels, out_channels, kernel_size=1), nn.ReLU()) ``` NiN网络: ```python net = nn.Sequential( nin_block(1, 96, kernel_size=11, strides=4, padding=0), nn.MaxPool2d(3, stride=2), nin_block(96, 256, kernel_size=5, strides=1, padding=2), nn.MaxPool2d(3, stride=2), nin_block(256, 384, kernel_size=3, strides=1, padding=1), nn.MaxPool2d(3, stride=2), nn.Dropout(0.5), # 标签类别数是10 nin_block(384, 10, kernel_size=3, strides=1, padding=1), nn.AdaptiveAvgPool2d((1, 1)), # 将四维的输出转成二维的输出,其形状为(批量大小,10) nn.Flatten()) ``` ## 4.并行连结网络(GoogLeNet) 这里的GoogLe应该不是谷歌哈哈...该网络的基本卷积块为Inception块: ![Inception块](https://zh-v2.d2l.ai/_images/inception.svg) 网络架构如图: ![GoogLeNet网络](https://zh-v2.d2l.ai/_images/inception-full.svg) 实现起来与之前的类似,这里不再过多赘述. ## 5.残差网络(ResNet) 残差块(右)与正常块的对比: ![残差块](https://zh-v2.d2l.ai/_images/residual-block.svg) 残差块(residual blocks)的核心思想是每个附加层都应该更容易的包含原始函数作为其元素之一.这里加上了原始的输入.如果想要改变通道数,可以引入一个1×1卷积层与输入变换后再相加. ResNet-18架构网络如下: ![ResNet](https://zh-v2.d2l.ai/_images/resnet18.svg) ## 6.稠密连接网络(DenseNet) DenseNet一定程度上是ResNet的逻辑扩展.DenseNet使用了函数展开的思想,比如泰勒展开就能将原函数$f(x)$展开为1阶线性和若干高阶非线性函数的和. 而连接的时候也不是直接相加而是独立的,(右边为DenseNet): ![跨层连接](https://zh-v2.d2l.ai/_images/densenet-block.svg) 稠密网络有稠密块和过渡层.过渡层用于减小通道数,因为每个稠密块都会带来通道数的增加.
点赞
回复评论 (1)
沙发
Jacktang
现代卷积神经网络中的网络这么多,.并行连结网络、残差网络、稠密连接网络等
点赞
2024-11-12 07:32
最新活动
免费申请 | 上百份MPS MIE模块,免费试用还有礼!
TI 有奖直播 | 使用基于 Arm 的 AM6xA 处理器设计智能化楼宇
Follow me第二季第3期来啦!与得捷一起解锁高性能开发板【EK-RA6M5】超能力!
报名直播赢【双肩包、京东卡、水杯】| 高可靠性IGBT的新选择——安世半导体650V IGBT
30套RV1106 Linux开发板(带摄像头),邀您动手挑战边缘AI~
安世半导体理想二极管与负载开关,保障物联网应用的稳健高效运行
随便看看
提问+BB Black LCD Cape中4.3寸屏和7寸屏可以通用吗
有没有对430F5,RTC熟悉的朋友或有什么资料,共享一下!?
改进并行CORDIC算法的研究
Altera EABs
脑电信号采集电路DIY成功
EEWORLD大学堂----简化数字电源
430的捕获模式
CCS5.1破解文件
体脂秤为什么需要交流阻抗测试而不是测试人体的直流阻抗?
新年芯币竞价第二期——开发板竞价
socfpga开发步骤(及常用命令)
veilog入门1.pdf
D类功率放大器设计
S3C2410 使用 ARM-Emulator 仿真器 仿真的问题
模块化编程问题!!!
周边防盗如何供电
向香水请教关于stm32vc的fsmc的问题
G组的一些程序
请问如下我这编译是哪里出问题了?
求助!关于烧写stc89c52无法烧写!
电子工程世界版权所有
京B2-20211791
京ICP备10001474号-1
京公网安备 11010802033920号
回复
写回复
收藏
回复