跟着李沐老师学习深度学习(十一)
经典的卷积神经网络
在本次笔记中主要介绍一些经典的卷积神经网络模型,主要包含以下:
- LeNet:最早发布的卷积神经网络之一,目的是识别图像中的手写数字;
- AlexNet: 是第一个在大规模视觉竞赛中击败传统计算机视觉模型的大型神经网络;
- 使用重复块的网络(VGG):利用许多重复的神经网络块;
- 网络中的网络(NiN):重复使用由卷积层和1×1卷积层(用来代替全连接层)来构建深层网络;
- 含并行连结的网络(GoogLeNet):使用并行连结的网络,通过不同窗口大小的卷积层和最大汇聚层来并行抽取信息;
- 残差网络(ResNet):它通过残差块构建跨层的数据通道,是计算机视觉中最流行的体系架构;
- 稠密连接网络(DenseNet):它的计算成本很高,但能带来了更好的效果。
- 轻量化CNN(mobileNet):旨在优化移动和嵌入式设备上的性能
LeNet
介绍
最早发布的卷积神经网络之一,因其在计算机视觉任务中的高效性能而受到广泛关注。 该模型是由AT&T贝尔实验室的研究员Yann LeCun在1989年提出的(并以其命名),目的是识别图像中的手写数字(应用与美国邮政服务)。
LeNet(LeNet-5)由两个部分组成:
- 卷积编码器:由两个卷积层组成;
- 全连接层密集块:由三个全连接层组成。
在卷积层块中,每个卷积层都使用5 × 5 的窗口,并在输出上使用sigmoid激活函数。第一个卷积层输出通道数为6,第二个卷积层输出通道数则增加到16。
在图中网络结构中汇聚层就是应用最大池化完成的。其中,最大池化的窗口大小为2 × 2,且步幅为2。由于池化窗口与步幅形状相同,池化窗口在输入上每次滑动所覆盖的区域互不重叠。
该网络使用数据集:MNIST数据集
包含50000个训练数据、10000个测试数据;样本均为灰度图像:28*28;输出:10类(0-9)
总结
- LeNet是早期成功的神经网络
- 先使用卷积层来学习图片空间信息
- 然后使用全连接层来转换到类别空间
代码实现
# lenet模型的实现import torch
from torch import nn
from d2l import torch as d2lnet = 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)
)# 我们对原始模型做了一点小改动,去掉了最后一层的高斯激活。除此之外,这个网络与最初的LeNet-5一致。# 检查模型
X = torch.rand(size=(1, 1, 28, 28), dtype=torch.float32)
for layer in net:X = layer(X)print(layer.__class__.__name__,'output shape: \t',X.shape)# 模型训练,上面已经实现了LeNet,接下来让我们看看LeNet在Fashion-MNIST数据集上的表现
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size)# 评估模型
def evaluate_accuracy_gpu(net, data_iter, device=None):if isinstance(net, nn.Module):net.eval() # 设置为评估模式# 如果 device 参数未提供,则通过 next(iter(net.parameters())).device 获取模型第一个参数所在的设备,将其作为后续计算的设备。if not device:device = next(iter(net.parameters())).device# 正常预测的数量,总预测的数量metric = d2l.Accumulator(2)# 在评估阶段,不需要计算梯度with torch.no_grad():for X, y in data_iter:# 如果输入数据 X 是列表类型(通常在 BERT 微调等场景中会遇到),将列表中的每个张量迁移到指定设备上。if isinstance(X, list):# BERT 微调所需的(之后讲)X = [x.to(device) for x in X]else:# 否则直接将输入数据 X 迁移到指定设备上X = X.to(device)# 标签 y 迁移到指定设备上 y = y.to(device)# 使用 d2l 库中的 accuracy 函数计算当前批次的预测准确率,即正确预测的样本数量。metric.add(d2l.accuracy(net(X), y), y.numel())return metric[0] / metric[1]# 训练函数def train_ch6(net, train_iter, test_iter, num_epochs, lr, device):"""用GPU训练模型"""# 1. 初始化模型的权重,使用 Xavier 均匀分布初始化线性层和卷积层的权重。def init_weight(m):if type(m) == nn.Linear or type(m) == nn.Conv2d:nn.init.xavier_uniform_(m.weight)net.apply(init_weight)print('training on', device)# 2. 将模型移动到指定的设备(通常是 GPU)上进行训练。net.to(device)# 3.定义优化器(随机梯度下降 SGD)和损失函数(交叉熵损失)。optimizer = torch.optim.SGD(net.parameters(), lr=lr)loss = nn.CrossEntropyLoss()# 4. 创建一个动画绘制器 Animator 用于实时可视化训练过程中的损失和准确率变化。animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs],legend=['train loss', 'train acc', 'test acc'])timer, num_batches = d2l.Timer(), len(train_iter)# 5. 开始多个轮次的训练,在每个轮次中遍历训练数据集,计算损失、反向传播并更新模型参数。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)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))# 6.每个轮次结束后,评估模型在测试数据集上的准确率。test_acc = evaluate_accuracy_gpu(net, test_iter)animator.add(epoch + 1, (None, None, test_acc))# 7.最后输出训练结束后的训练损失、训练准确率、测试准确率以及训练速度。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)}')# 接下来训练和评估LeNet-5模型
lr, num_epochs = 0.9, 10
train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
得到结果:
AlexNet
先说点其他的
-
背景
当时关于特征提取有两种思想:- 观察图像特征的提取方法:2012年 前,图像特征都是机械地计算出来的。
- 认为特征本身应该被学习
-
AlexNet 诞生的关键因素
- 数据层面:深度模型需大量有标签数据才能优于传统方法。早期受存储和预算限制,研究多基于小数据集。2009 年 ImageNet 数据集发布并举办挑战赛,提供百万样本、千种类别,推动了研究发展。
- 硬件层面:深度学习计算需求高,早期优化凸目标的简单算法因计算资源限制受青睐。GPU 最初用于图形处理,其运算与卷积层计算相似,英伟达和 ATI 将其优化用于通用计算。GPU 由众多小型处理单元组成,虽单个核心性能弱,但数量庞大,在浮点性能、功耗和内存带宽方面优于 CPU。2012 年,Alex Krizhevsky 和 Ilya Sutskever 利用 GPU 实现快速卷积运算,推动了深度学习发展。
介绍
-
网络结构
使用了8层卷积神经网络,前5层是卷积层,剩下的3层是全连接层,具体如下所示。
(由当时GPU内存的限制引起的,作者使用两块GPU进行计算,因此分为了上下两部分) -
与LeNet结构的差异:
-
具体细节:
- 卷积层C1:使用96个核对224×224×3的输入图像进行滤波,卷积核大小为11×11×3,步长为4。将一对55×55×48的特征图分别放入ReLU激活函数,生成激活图。激活后的图像进行最大池化,size为3×3,stride为2,池化后的特征图size为27×27×48(一对)。池化后进行LRN处理。
- 卷积层C2:使用卷积层C1的输出(响应归一化和池化)作为输入,并使用256个卷积核进行滤波,核大小为5 × 5 × 48。
- 卷积层C3:有384个核,核大小为3 × 3 × 256,与卷积层C2的输出(归一化的,池化的)相连。
- 卷积层C4:有384个核,核大小为3 × 3 × 192。
- 卷积层C5:有256个核,核大小为3 × 3 × 192。卷积层C5与C3、C4层相比多了个池化,池化核size同样为3×3,stride为2。
- 全连接F6:此层的全连接实际上是通过卷积进行的,输入6×6×256,4096个6×6×256的卷积核,扩充边缘padding = 0, 步长stride = 1, 因此其FeatureMap大小为(6-6+0×2+1)/1 = 1,即1×1×4096;
-
创新特点:
-
更深的神经网络结构
- AlexNet 是首个真正意义上的深度卷积神经网络,它的深度达到了当时先前神经网络的数倍。通过增加网络深度,AlexNet 能够更好地学习数据集的特征,从而提高了图像分类的精度。
-
ReLU激活函数的使用
- AlexNet 首次使用了修正线性单元ReLU这一非线性激活函数。相比于传统的 sigmoid 和 tanh 函数,ReLU 能够在保持计算速度的同时,有效地解决了梯度消失问题,从而使得训练更加高效。
-
ReLU激活函数的使用
- 对于每个特征图上的每个位置,计算该位置周围的像素的平方和,然后将当前位置的像素值除以这个和。
- LRN是在卷积层和池化层之间添加的一种归一化操作。在卷积层中,每个卷积核都对应一个特征图(feature map),LRN就是对这些特征图进行归一化。具体来说,对于每个特征图上的每个位置,计算该位置周围的像素的平方和,然后将当前位置的像素值除以这个和。
- 对于每个特征图上的每个位置,计算该位置周围的像素的平方和,然后将当前位置的像素值除以这个和。
-
数据增强和Dropout
- 数据增强:通过随机裁剪、水平翻转图像以及对 RGB 通道强度进行 PCA 变换来扩大数据集,减少过拟合,降低错误率。
- Dropout:在全连接层使用 Dropout 技术,以 0.5 概率随机失活神经元,减少神经元间复杂的协同适应,防止过拟合,测试时将神经元输出乘以 0.5。
-
大规模分布式训练
- AlexNet在使用GPU进行训练时,可将卷积层和全连接层分别放到不同的GPU上进行并行计算,从而大大加快了训练速度。像这种大规模 GPU 集群进行分布式训练的方法在后来的深度学习中也得到了广泛的应用。
-
-
分布式GPU的使用方式
-
将网络分布在两个 GTX 580 GPU 上进行训练。两个 GPU 之间能够直接读写彼此内存,无需通过主机内存,这为跨 GPU 并行化提供了便利。
在分布式训练时,各层在两个 GPU 上有不同的分工,如下图:
-
对模型压缩的启发:
-
模型分割与并行化
-
减少冗余与参数共享
如:C4、C5卷积层仅与同GPU的特征图相连。(参数共享、剪枝) -
优化训练策略(调整学习率和优化器参数)
-
利用硬件特性(使用低精度计算、设计轻量级网络结构)
-
-
总结
-
AlexNet的架构与LeNet相似,但使用了更多的卷积层和更多的参数来拟合大规模的ImageNet数据集。
-
现在,AlexNet已经被更有效的架构所超越,但它是从浅层网络到深层网络的关键一步。
-
尽管AlexNet的代码只比LeNet多出几行,但学术界花了很多年才接受深度学习这一概念,并应用其出色的实验结果。这也是由于缺乏有效的计算工具。
-
Dropout、ReLU和预处理是提升计算机视觉任务性能的其他关键步骤。
代码实现
import torch
from torch import nn
from d2l import torch as d2lnet = nn.Sequential(# 这里使用一个11*11的更大窗口来捕捉对象。# 同时,步幅为4,以减少输出的高度和宽度。# 另外,输出通道的数目远大于LeNetnn.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,而非论文中的1000nn.Linear(4096, 10)
)# 构造一个高度和宽度都为224的(单通道数据,来观察每一层输出的形状
X = torch.randn(1, 1, 224, 224)
for layer in net:X = layer(X)print(layer.__class__.__name__,'output shape:\t', X.shape)# 读取数据集
batch_size = 128
# Fashion-MNIST图像的分辨率 低于ImageNet图像。:我们将它们增加到224 x224;这里是为了在AlexNet上使用该数据集;一般不会使用resize=224
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=224)lr, num_epochs = 0.01, 10
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
结果:
可以看到损失比在LeNet上降了很多,训练精度和测试精度都大幅提高.
这里是在Kaggle上跑的,自己电脑根本跑不动(/(ㄒoㄒ)/~~)
VGG
- 设计背景:AlexNet 虽证明深层神经网络有效,但未提供通用设计模板。随着神经网络架构设计的抽象化发展,研究人员转向以块为单位设计网络,VGG 网络便是使用块进行设计的典型代表。
- 经典卷积神经网络的基本组成部分:
- 带填充以保持分辨率的卷积层;
- 非线性激活函数,如ReLU;
- 汇聚(池化)层,如最大汇聚(池化)层。
一个VGG块与之类似,由一系列的卷积层组成,后面再加上用于空间下采样的最大池化层。
介绍
概念说明:卷积层全部为3*3的卷积核,用conv3-xxx来表示,xxx表示通道数。
在VGG论文中展示了六次实验的结果:
其中,详细为:
A:简单的卷积神经网络结构
A-LRN:在A的基础上加了LRN
B:在A的基础上加了两个conv3,即多加了两个33卷积核
C:B的基础上加了三个conv1,即多加了三个11卷积核
D:C的基础上把三个conv1换成了三个33卷积核
E:D的基础上加了三个conv3,即多加了三个33卷积核
- 从以上的实验中可以得到:
- A和A-LRN对比,LRN并没有得到好的效果,可去除
- C和D对比:Conv3 比Conv1 更有效
- 统筹看这六个实验,可发现:随着网络层数的加深,模型的表现会越来越好
总结
- VGG使用 可复用的卷积块构造网络。不同的VGG模型可通过每个块中卷积层数量和输出通道数量的 差异来定义。
- 块的使用导致网络定义的非常简洁,使用块可以有效地设计复杂的网络。
- 在VGG论文中,Simonyan和Ziserman尝试了各种架构。特别是他们发现 深层且窄的卷积(即3 × 3)比 较浅层且宽的卷积更有效。
代码实现
import torch
from torch import nn
from d2l import torch as d2l# 实现 VGG块
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_channelslayers.append(nn.MaxPool2d(kernel_size=2, stride=2))return nn.Sequential(*layers)# VGG 网络: 主要由卷积层和汇聚层组成,第二部分由全连接层组成
# 原始VGG网络有5个卷积块,其中前两个块各有一个卷积层,后三个块各包含两个卷积层。
# 第一个模块有64个输出通道,每个后续模块将输出通道数量翻倍,直到该数字达到512。
# 由于该网络使用8个卷积层和3个全连接层,因此它通常被称为VGG-11。
conv_arch = ((1, 64), (1, 128), (2, 256), (2, 512), (2, 512))# 下面代码实现了VGG-11
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_channelsreturn 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))net = vgg(conv_arch)# 接下来,构建一个高度和宽度为224的单通道数据样本,来观察每个层输出的形状
X = torch.randn(size=(1, 1, 224, 224))
for blk in net:X = blk(X)print(blk.__class__.__name__,'output shape:\t',X.shape)# 训练模型
# [由于VGG-11比AlexNet计算量更大,因此我们构建了一个通道数较少的网络,足够用于训练Fashion-MNIST
ratio = 4
# 这行代码的主要目的是对 conv_arch 列表中的每个元素进行处理,将每个元素(一个二元组)的第二个值除以 ratio 后得到一个新的二元组,然后把这些新的二元组组合成一个新的列表 small_conv_arch。
small_conv_arch = [(pair[0], pair[1] // ratio) for pair in conv_arch]
net = vgg(small_conv_arch)lr, num_epochs, batch_size = 0.05, 10, 28
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=224)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
结果:
也可以看到训练精度和测试精度的大幅提升。
NiN
共同的设计模式(LeNet、AlexNet和VGG):通过一系列的卷积层与池化层来提取空间结构特征;然后通过全连接层对特征的表征进行处理。(AlexNet和VGG对LeNet的改进主要在于如何扩大和加深这两个模块)
带来的问题:使用了全连接层,可能会完全放弃表征的空间结构。
解决:网络中的网络(NiN)提供了一个非常简单的解决方案:在每个像素的通道上分别使用多层感知机(具体实现:使用多个连续的1 × 1 卷积层(被视为 MLP),对每个像素点进行多层的非线性特征变换)。
介绍
- NiN块
卷积层输入和输出:由四维张量组成,每个轴对应样本、通道、高度、宽度。
全连接层输入和输出:二维张量,对应样本、特征。
NiN的想法:在每个像素位置(针对每个高度和宽度)应用一个全连接层。
1×1 卷积层可看作逐像素的全连接层,增
加每像素的非线性变换,还能调整通道数,
在减少参数的同时丰富特征表达。
总结
-
NiN使用由一个卷积层和多个1 x 1卷积层组成的块。该块可以在卷积神经网络中使用,以允许更多的每像素非线性。
-
NiN去除了容易造成过拟合的全连接层,将它们替换为全局平均汇聚层(即在所有位置上进行求和)。该汇聚层通道数量为所需的输出数量(例如,Fashion-MNIST的输出为10)。
-
移除全连接层可减少过拟合,同时显著减少NiN的参数。
-
NiN的设计影响了许多后续卷积神经网络的设计。
代码实现
import torch
from torch import nn
from d2l import torch as d2l# NiN块
# NiN的想法是在每个像素位置(针对每个高度和宽度)应用一个全连接层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 模型
# NiN使用窗口形状为 11×11 、 5×5 和 3×3 的卷积层,输出通道数量与AlexNet中的相同。
# 每个NiN块后有一个最大汇聚层,汇聚窗口形状为 3×3 ,步幅为2。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),# 标签类别数是10nin_block(384, 10, kernel_size=3, strides=1, padding=1),nn.AdaptiveAvgPool2d((1, 1)),# 将四维的输出转为二维的输出,其形状为(批量大小,10)nn.Flatten()
)# 创建一个数据样本来[查看每个块的输出形状]
X = torch.rand(size=(1, 1, 224, 224))
for layer in net:X = layer(X)print(layer.__class__.__name__,'output shape: \t', X.shape)# 使用Fashion-MNIST来训练模型
lr, num_epochs, batch_size = 0.1, 10, 128
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=224)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
结果:
GoogleNet
设计思想:吸收了NiN中串联网络的思想,并在此基础上做了改进。
重点:解决了什么样大小的卷积核最合适的问题
介绍
- Inception块是 GoogLeNet 的基本卷积块。
- 由四条并行路径构成,前三条路径分别使用 1×1、3×3 和 5×5 的卷积层提取不同空间大小的信息,中间两条路径先通过 1×1 卷积层减少通道数,以降低模型复杂度。第四条路径先使用 3×3 最大汇聚层,再用 1×1 卷积层改变通道数。
- 四条路径都使用合适的填充来使输入与输出的高和宽一致,最后将每条线路的输出在通道维度上连结构成输出。
- 在Inception块中,通常调整的超参数是每层输出通道数。
- 模型框架
一共使用9个Inception块和全局平均汇聚层的堆叠来生成其估计值.
总结
-
Inception块相当于一个有4条路径的子网络。它通过不同窗口形状的卷积层和最大汇聚层来并行抽取信息,并使用1 X 1卷积层减少每像素级别上的通道维数从而降低模型复杂度。
-
GoogLeNet将多个设计精细的Inception块与其他层(卷积层、全连接层)串联起来。其中Inception块的通道数分配之比是在ImageNet数据集上通过大量的实验得来的。
-
GoogLeNet和它的后继者们一度是ImageNet上最有效的模型之一:它以较低的计算复杂度提供了类似的测试精度。
GoogLeNet有一些后续版本:
-
添加批量规范化层 (Ioffe and Szegedy, 2015)(batch normalization)
-
对Inception模块进行调整 (Szegedy et al., 2016);
-
使用标签平滑(label smoothing)进行模型正则化 (Szegedy et al., 2016);
-
加入残差连接 (Szegedy et al., 2017)。( 下节将介绍)。
代码实现
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l# (Inception块)
# Inception块由四条并行路径组成。
# 前三条路径使用窗口大小为 1×1 、 3×3 和 5×5 的卷积层,从不同空间大小中提取信息class Inception(nn.Module):# c1--c4是每条路径的输出通道数def __init__(self, in_channels, c1, c2, c3, c4, **kwargs):super(Inception, self).__init__(**kwargs)# 线路1:单1 x 1 卷积层self.p1_1 = nn.Conv2d(in_channels, c1, kernel_size=1)# 线路2:1x1卷积后接 3x3卷积层self.p2_1 = nn.Conv2d(in_channels, c2[0], kernel_size=1)self.p2_2 = nn.Conv2d(c2[0], c2[1], kernel_size=3, padding=1)# 线路3:1x1卷积层后接 5 x 5卷积层self.p3_1 = nn.Conv2d(in_channels, c3[0], kernel_size=1)self.p3_2 = nn.Conv2d(c3[0], c3[1], kernel_size=5, padding=2)# 线路4:3x3最大汇聚层后接 1x1卷积层self.p4_1 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)self.p4_2 = nn.Conv2d(in_channels, c4, kernel_size=1)def forward(self, x):p1 = F.relu(self.p1_1(x))p2 = F.relu(self.p2_2(F.relu(self.p2_1(x))))p3 = F.relu(self.p3_2(F.relu(self.p3_1(x))))p4 = F.relu(self.p4_2(self.p4_1(x)))# 在通道连结输出return torch.cat((p1, p2, p3, p4), dim=1)# 滤波器(filter)的组合,它们可以用各种滤波器尺寸探索图像,这意味着不同大小的滤波器可以有效地识别不同范围的图像细节。
# 同时,我们可以为不同的滤波器分配不同数量的参数。
# GoogLeNet一共使用9个Inception块和全局平均汇聚层的堆叠来生成其估计值。# 逐一来实现GoogleNet 的每个模块,第一个模块使用64个通道、7 x 7 卷积层
b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),nn.ReLU(),nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)# 第二个模块:使用两个卷积层,第一个卷积层是64个通道、1 * 1 卷积层;
# 第二个人卷积层使用将通道数量增加三倍的3 * 3 卷积层, 对应于Inception块中的第二天路径
b2 = nn.Sequential(nn.Conv2d(64, 64, kernel_size=1),nn.ReLU(),nn.Conv2d(64, 192, kernel_size=3, padding=1),nn.ReLU(),nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)# 第三个模块串联两个完整的Inception块
# 第三个模块串联两个完整的Inception块。
# 第一个Inception块的输出通道数为 64+128+32+32=256 ,四个路径之间的输出通道数量比为 64:128:32:32=2:4:1:1 。
# 第二个和第三个路径首先将输入通道的数量分别减少到 96/192=1/2 和 16/192=1/12 然后连接第二个卷积层。
# 第二个Inception块的输出通道数增加到 128+192+96+64=480 ,四个路径之间的输出通道数量比为 128:192:96:64=4:6:3:2 。
# 第二条和第三条路径首先将输入通道的数量分别减少到 128/256=1/2 和 32/256=1/8 。b3 = nn.Sequential(Inception(192, 64, (96, 128), (16, 32), 32),Inception(256, 128, (128, 192), (32, 96), 64),nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)# 第4个模块,串联了五个Inception块
b4 = nn.Sequential(Inception(480, 192, (96, 208), (16, 48), 64),Inception(512, 160, (112, 224), (24, 64), 64),Inception(512, 128, (128, 256), (24, 64), 64),Inception(512, 112, (144, 288), (32, 64), 64),Inception(528, 256, (160, 320), (32, 128), 128),nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)
# 第五模块包含输出通道数为 256+320+128+128=832 和 384+384+128+128=1024 的两个Inception块
b5 = nn.Sequential(Inception(832, 256, (160, 320), (32, 128), 128),Inception(832, 384, (192, 384), (48, 128), 128),nn.AdaptiveAvgPool2d((1,1)),nn.Flatten()
)net = nn.Sequential(b1, b2, b3, b4, b5, nn.Linear(1024, 10))# 模型计算复杂,不如VGG那样便于修改通道数
# 为了使Fashion-MNIST上的训练短小精悍,我们将输入的高和宽从224降到96]
X = torch.rand(size=(1, 1, 96, 96))
for layer in net:X = layer(X)print(layer.__class__.__name__, 'output shape:\t', X.shape)# 训练模型
lr, num_epochs, batch_size = 0.1, 10, 128
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
结果:
批量规范化
批量规范化是在卷积层或全连接层之后、相应的激活函数之前应用的。
-
先计算小批量的均值和标准差:
-
BN公式: 输入x∈B
其中, 可学习参数:拉伸参数(scale)γ和偏移参数(shift)β;
应用标准化后,生成的小批量的平均值为0和单位方差为1。 -
标准化的作用:
- 标准化输入特征可统一参数量级,有助于优化器工作;
- 中间层变量变化范围大,其分布偏移会阻碍网络收敛。不同层变量值相差较大时,需针对性的调整学习率;
- 深层网络结构复杂,容易出现过拟合现象。
ResNet
-
函数类角度
-
具有X特性和y标签的数据集:
F: 神经网络架构(包含学习率及其他超参数设置)
-
当我们想要训练一个更强大的F’,需要满足 F ∈ F’,即较复杂函数类包含较小函数类。
-
也就是说: 新模型的函数类包含了原模型的函数,新模型具备原模型的所有能力,在处理相同任务时,至少能达到与原模型相同的效果。
-
-
针对深度神经网络, 将新添加层训练成恒等映射:
残差网络的核心思想:每个附加层都应更容易地包含原始函数作为其元素之一。 于是,残差块(residual blocks)便诞生了。
介绍
-
残差块
- 对于深度神经网络,学习恒等映射相对容易。以残差块为例,在残差块中拟合 f(x) - x 残差映射比直接拟合f(x)理想映射更简单。当理想映射f(x)极接近于恒等映射时,残差映射更易于捕捉恒等映射的细微波动。
- 将新添加层训练为恒等映射,其权重和偏置参数可近似为零,从优化的角度,这种简单的映射关系使得训练过程更稳定,不易出现梯度消失或爆炸等问题,从而保证新模型能达到与原模型相同的性能水平。
-
模型框架
- 前两层与GoogleNet一样,不同之处在于ResNet每个卷积层后增加了批量规范化层。
- 每个模块有4个卷积层(不包括恒等映射的1 * 1 卷积层)。 加上第一个卷积层和最后一个全连接层,共有18层。 因此,这种模型通常被称为ResNet-18。 (通过配置不同的通道数和模块里的残差块数可以得到不同的ResNet模型,例如更深的含152层的ResNet-152。)
- 虽然ResNet的主体架构跟GoogLeNet类似,但ResNet架构更简单,修改也更方便.
总结
-
学习嵌套函数(nested function)是训练神经网络的理想情况。在深层神经网络中,学习另一层作为恒等映射(identity function)较容易(尽管这是一个极端情况)。
-
残差映射可以更容易地学习同一函数,例如将权重层中的参数近似为零。
-
利用残差块(residual blocks)可以训练出一个有效的深层神经网络:输入可以通过层间的残余连接更快地向前传播。
代码实现
!pip install d2l
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l# 残差块定义
# 残差块里首先有2个有相同输出通道数的 3×3 卷积层。 每个卷积层后接一个批量规范化层和ReLU激活函数。
# 然后我们通过跨层数据通路,跳过这2个卷积运算,将输入直接加在最后的ReLU激活函数前。
# 这样的设计要求2个卷积层的输出与输入形状一样,从而使它们可以相加# 实现如下:
class Residual(nn.Module):def __init__(self, input_channels, num_channels,use_1x1conv=False, strides=1):super().__init__()self.conv1 = nn.Conv2d(input_channels, num_channels,kernel_size=3, padding=1, stride=strides)self.conv2 = nn.Conv2d(num_channels, num_channels,kernel_size=3, padding=1)if use_1x1conv:self.conv3 = nn.Conv2d(input_channels, num_channels,kernel_size=1, stride=strides)else:self.conv3 = Noneself.bn1 = nn.BatchNorm2d(num_channels)self.bn2 = nn.BatchNorm2d(num_channels)def forward(self, X):Y = F.relu(self.bn1(self.conv1(X)))Y = self.bn2(self.conv2(Y))if self.conv3:X = self.conv3(X)Y += Xreturn F.relu(Y)# 查看[输入和输出形状一致]
blk = Residual(3,3)
X = torch.rand(4, 3, 6, 6)
Y = blk(X)
Y.shape# 增加输出通道数的同时,减半输出的高和宽]。
blk = Residual(3,6, use_1x1conv=True, strides=2)
blk(X).shape# ResNet模型
# 前两层跟之前介绍的GoogLeNet中的一样: 在输出通道数为64、步幅为2的 7×7 卷积层后,接步幅为2的 3×3 的最大汇聚层
b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),nn.BatchNorm2d(64), nn.ReLU(),nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)# ResNet则使用4个由残差块组成的模块,每个模块使用若干个同样输出通道数的残差块。
# 第一个模块的通道数同输入通道数一致
# 注意:对第一个模块做了特别处理。
def resnet_block(input_channels, num_channels, num_residuals,first_block=False):blk = []for i in range(num_residuals):if i == 0 and not first_block:blk.append(Residual(input_channels, num_channels,use_1x1conv=True, strides=2))else:blk.append(Residual(num_channels, num_channels))return blk# 接着在ResNet加入所有残差块,这里每个模块使用2个残差块。
b2 = nn.Sequential(*resnet_block(64, 64, 2, first_block=True))
b3 = nn.Sequential(*resnet_block(64, 128, 2))
b4 = nn.Sequential(*resnet_block(128, 256, 2))
b5 = nn.Sequential(*resnet_block(256, 512, 2))# 最后,在ResNet中加入全局平均汇聚层,以及全连接层输出。
net = nn.Sequential(b1,b2,b3,b4,b5,nn.AdaptiveAvgPool2d((1,1)),nn.Flatten(), nn.Linear(512, 10)
)# 观察一下ResNet中不同模块的输入形状是如何变化的
# X = torch.rand(size=(1, 1, 224, 224))
# for layer in net:
# X = layer(X)
# print(layer.__class__.__name__,'output shape:\t', X.shape)# 训练模型
lr, num_epochs, batch_size = 0.05, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
结果:
使用残差网络可以看出训练损失降了很多,并且训练精度和测试精度有大幅提高;不过两者存在一定的gap。
DenseNet
-
从 ResNet 到 DenseNet:
-
继续拓展:
-
稠密连接:
稠密网络由2部分构成:稠密块(dense block)和过渡层(transition layer)。
前者定义如何连接输入和输出,而后者则控制通道数量。
介绍
-
稠密块(dense block)
-
由多个使用相同数量输出通道的卷积块组成
-
卷积块架构: BN - ReLU - Conv (bottleneck)
-
主要作用:实现特征重用(即对不同“级别”的特征——不同表征进行总体性地再探索)
-
-
过渡层(transition layer)
- 主要用于连接两个相邻的DenseBlock。
- 包括一个1x1卷积(用于调整通道数)和2x2AvgPooling(用于降低特征图大小),结构为BN+ReLU+1x1 Conv+2x2 AvgPooling。
- 作用:Transition层可以起到压缩模型的作用。
-
超参数调节
- θ取值(0,1],当θ=1时,feature维度不变,即无压缩;而θ<1时,这种结构称为DenseNet-C(文中使用θ=0.5);对于使用bottleneck层的DenseBlock结构和压缩系数小于1的Transition组合结构称为DenseNet-BC。
-
网络结构
DenseNet的网络结构主要由DenseBlock和Transition组成,一个DenseNet中有3个或4个DenseBlock。而一个DenseBlock中也会有多个Bottleneck layers。最后的DenseBlock之后是一个global AvgPooling层,然后送入一个softmax分类器,得到每个类别所属分数。
总结
代码实现
import torch
from torch import nn
from d2l import torch as d2l# 使用 ResNet 改良版中 "BN、ReLU、Conv"架构def conv_block(input_channels, num_channels):return nn.Sequential(nn.BatchNorm2d(input_channels), nn.ReLU(),nn.Conv2d(input_channels, num_channels, kernel_size=3, padding=1))# 一个稠密块由多个卷积块组成,每个卷积块使用相同数量的输出通道。
# 然而,在前向传播中,我们将每个卷积块的输入和输出在通道维上连结。
class DenseBlock(nn.Module):def __init__(self, num_convs, input_channels, num_channels):super(DenseBlock, self).__init__()layer = []for i in range(num_convs):layer.append(conv_block(num_channels * i + input_channels, num_channels))self.net = nn.Sequential(*layer)def forward(self, X):for blk in self.net:Y = blk(X)# 连接通道维度上每个块的输入和输出X = torch.cat((X, Y), dim=1)return X# 定义一个有2个输出通道数为10的(DenseBlock)
# 使用通道数为3的输入时,我们会得到通道数为 3+2×10=23 的输出blk = DenseBlock(2, 3, 10)
X = torch.randn(4, 3, 8, 8)
Y = blk(X)
Y.shape# 过渡层
# 由于每个稠密块都会带来通道数的增加,使用过多则会过于复杂化模型。
# 而过渡层可以用来控制模型复杂度。
# 它通过 1×1 卷积层来减小通道数,并使用步幅为2的平均汇聚层减半高和宽,从而进一步降低模型复杂度。
def transition_block(input_channels, num_channels):return nn.Sequential(nn.BatchNorm2d(input_channels), nn.ReLU(),nn.Conv2d(input_channels, num_channels, kernel_size=1),nn.AvgPool2d(kernel_size=2, stride=2))# 使用 通道数为10的[过渡层]。 此时输出的通道数减为10,高和宽均减半。
blk = transition_block(23, 10)
blk(Y).shape# 构造 DenseNet模型
# DenseNet首先使用同ResNet一样的单卷积层和最大汇聚层。
b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),nn.BatchNorm2d(64), nn.ReLU(),nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)# DenseNet使用的是4个稠密块
# num_channels为当前的通道数
num_channels, growth_rate = 64, 32
num_convs_in_dense_blocks = [4, 4, 4, 4]
blks = []
for i, num_convs in enumerate(num_convs_in_dense_blocks):blks.append(DenseBlock(num_convs, num_channels, growth_rate))# 上一个稠密块的输出通道数num_channels += num_convs * growth_rate# 在稠密块之间添加一个转换层,使通道数量减半if i != len(num_convs_in_dense_blocks) - 1:blks.append(transition_block(num_channels, num_channels // 2))num_channels = num_channels // 2# 最后接上全局汇聚层和全连接层来输出结果
net = nn.Sequential(b1, *blks,nn.BatchNorm2d(num_channels), nn.ReLU(),nn.AdaptiveAvgPool2d((1, 1)),nn.Flatten(),nn.Linear(num_channels, 10))# 训练模型lr, num_epochs, batch_size = 0.1, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
结果:
MobileNet
-
研究小而高效的CNN模型(思想):
- 一是对训练好的复杂模型进行压缩得到小模型;
- 二是直接设计小模型并进行训练。
-
核心目标:在保持模型性能(accuracy)的前提下降低模型大小(parameters size),同时提升模型速度(speed, low latency)
MobileNet 模型是 Google 针对手机等嵌入式设备提出的一种轻量级的深层神经网络.
V1 介绍
主要操作:
(1)将普通卷积换成了深度可分离卷积;
(2)引入了两个超参数使得可以根据资源来更加灵活的控制自己模型的大小。
-
深度可分离卷积(Depthwise separable convolution)
(追溯历史,可分为空间可分离卷积和深度可分离卷积)-
分解为两个操作: 深度卷积(depthwise convolution) 和点卷积( pointwise convolution)
-
回忆下标准卷积:
-
深度卷积(depthwise convolution)
- 对输入特征图的每个通道分别应用一个滤波器
- 对输入特征图的每个通道分别应用一个滤波器
-
点卷积( pointwise convolution)
- 用 1*1 卷积进行升维和降维
- 用 1*1 卷积进行升维和降维
-
-
与传统卷积的不同:
-
为什么使用ReLU6?
- 与relu函数区别: 对输入的最大输出值做了约束.
- 主要原因解释: 为了在移动端float16/int8等低精度设备时,也能够有很好的数值分辨率。如果对Relu的值没有加以任何限制,则输出范围可以从0到无限大,这就使得激活值很大,分布在一个很大的范围内,而低精度的float16等嵌入式设备就无法很好的精确描述如此大的范围,从而带来精度损失。(创新点1)
-
创新点2:虽然MobileNet网络结构和延迟已经比较小了,但是很多时候在特定应用下还是需要更小更快的模型,为此引入了宽度因子α,为了控制模型大小,引入了分辨因子ρ。
- 宽度因子α(Width Mutiplier)
- 通常α在(0, 1] 之间,比较典型的值由1, 0.75, 0.5, 0.25
- 在每一层对网络的输入输出通道数进行缩减,输出通道数由 M 到 αM,输出通道数由 N 到 αN,变换后的计算量为:
- 分辨因子ρ(resolution multiplier)
- 通常ρ在(0, 1] 之间,比较典型的输入分辨为 224, 192, 160, 128;
- 用于控制输入和内部层表示,即用分辨率因子控制输入的分辨率,深度卷积和逐点卷积的计算量为:
- 宽度因子α(Width Mutiplier)
在MobileNetV1中,深度卷积网络的每个输入通道都应用了单个滤波器。然后,逐点卷积应用 1*1 卷积网络来合并深度卷积的输出。这种标准卷积方法既能滤波,又能一步将输入合并成一组新的输出。
- 网络结构
- 一共由 28层构成(不包括AvgPool 和 FC 层,且把深度卷积和逐点卷积分开算)
其除了第一层采用的是标准卷积核之外,剩下的卷积层都是用Depth Wise Separable Convolution。
- 一共由 28层构成(不包括AvgPool 和 FC 层,且把深度卷积和逐点卷积分开算)
下面简要对 V2 和 V3 进行一些介绍
-
MobileNet V2
- 在V1的基础上,引入了倒残差块(Inverted Residual Block)和线性激活函数(Linear Activation)。这些改进使得V2在保持轻量级特性的同时,实现了更高的准确性和更低的延迟。倒残差块的设计有助于保留和增强特征信息,改善了模型在低资源环境中的表现。
-
MobileNet V3
- 进一步对V2进行了全面改进,采用了HardSwish激活函数、挤压励磁模块(Squeeze-and-Excitation Block),以及MnasNet和NetAdapt等**网络架构搜索(NAS)**技术。这些技术使得V3在保持高性能的同时,实现了更快的推理速度和更小的模型尺寸。
MobileNet V1代码实现
import torch
from torch import nn
from d2l import torch as d2l# 定义深度可分离卷积层
class DepthwiseSeparableConv(nn.Module):def __init__(self, in_channels, out_channels, stride=1):super(DepthwiseSeparableConv, self).__init__()# 深度卷积self.depthwise = nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=stride, padding=1, groups=in_channels, bias=False)self.bn1 = nn.BatchNorm2d(in_channels)self.relu1 = nn.ReLU(inplace=True)# 逐点卷积self.pointwise = nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False)self.bn2 = nn.BatchNorm2d(out_channels)self.relu2 = nn.ReLU(inplace=True)def forward(self, x):x = self.relu1(self.bn1(self.depthwise(x)))x = self.relu2(self.bn2(self.pointwise(x)))return x# 定义 MobileNet V1 网络
class MobileNetV1(nn.Module):def __init__(self, num_classes=10):super(MobileNetV1, self).__init__()# 初始卷积层self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=2, padding=1, bias=False)self.bn1 = nn.BatchNorm2d(32)self.relu1 = nn.ReLU(inplace=True)# 深度可分离卷积层self.layers = nn.Sequential(DepthwiseSeparableConv(32, 64, stride=1),DepthwiseSeparableConv(64, 128, stride=2),DepthwiseSeparableConv(128, 128, stride=1),DepthwiseSeparableConv(128, 256, stride=2),DepthwiseSeparableConv(256, 256, stride=1),DepthwiseSeparableConv(256, 512, stride=2),DepthwiseSeparableConv(512, 512, stride=1),DepthwiseSeparableConv(512, 512, stride=1),DepthwiseSeparableConv(512, 512, stride=1),DepthwiseSeparableConv(512, 512, stride=1),DepthwiseSeparableConv(512, 512, stride=1),DepthwiseSeparableConv(512, 1024, stride=2),DepthwiseSeparableConv(1024, 1024, stride=1))# 全局平均池化层self.avgpool = nn.AdaptiveAvgPool2d((1, 1))# 全连接层self.fc = nn.Linear(1024, num_classes)def forward(self, x):x = self.relu1(self.bn1(self.conv1(x)))x = self.layers(x)x = self.avgpool(x)x = x.view(x.size(0), -1)x = self.fc(x)return x# 初始化模型
net = MobileNetV1()# 训练模型
lr, num_epochs, batch_size = 0.1, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
结果:
总结
- AlexNet :首次运用大型神经网络在大规模视觉竞赛中战胜传统计算机视觉模型,开启深度卷积神经网络应用的新时代。
- VGG :利用大量重复的神经网络块构建模型,探索出一种规整化的网络构建方式,提升了网络的可扩展性和性能。
- NiN :创新性地用卷积层和 1×1 卷积层替代全连接层构建深层网络,减少模型参数数量,提升计算效率。
- GoogLeNet :通过设计 Inception 块,运用不同窗口大小的卷积层和最大汇聚层并行抽取信息,有效提高模型对图像特征的提取能力。(宽度方向)
- ResNet :引入残差块构建跨层数据通道,解决了深层网络训练中的梯度消失等问题,极大地加深了网络深度,提升模型性能。(深度方向)
- DenseNet :采用稠密连接方式,在通道维上连结各层输入与输出,增强了特征传播与复用能力,在一定程度上提升了模型效果,但计算成本较高。(从feature上入手)
- MobileNet 阶段:针对移动端等资源受限场景,提出深度可分离卷积,将普通卷积分解为深度卷积和逐点卷积,在大幅降低计算量与模型大小的同时,保持了较好的分类精度,推动了卷积神经网络在移动设备等领域的广泛应用.
相关文章:
跟着李沐老师学习深度学习(十一)
经典的卷积神经网络 在本次笔记中主要介绍一些经典的卷积神经网络模型,主要包含以下: LeNet:最早发布的卷积神经网络之一,目的是识别图像中的手写数字;AlexNet: 是第一个在大规模视觉竞赛中击败传统计算机…...
小结:OSPF的网络类型,LSA
OSPF(Open Shortest Path First)是一个基于链路状态的内部网关协议(IGP)。以下是对OSPF网络类型、LSA类型、序列号与Age作用,以及相关配置指令的详细讲解。 一、OSPF的网络类型 OSPF支持多种网络类型,不同…...
用vue3写一个好看的wiki前端页面
以下是一个使用 Vue 3 Element Plus 实现的 Wiki 风格前端页面示例,包含现代设计、响应式布局和常用功能: <template><div class"wiki-container"><!-- 头部导航 --><el-header class"wiki-header"><d…...
Redis常见数据结构
目录 基本介绍 特点: 全局命令 数据类型: String Hash List Set Zset 基本介绍 Redis是一个在内存中存储数据的中间件,可作为数据库,缓存,消息队列等。 特点: 持久化:Redis会把数据存储在内存中…...
JavaScript中Map和Set数据结构详解
目录 一、Map 1. 定义与特点 2. 创建Map对象 3. 常用方法 4. 遍历Map 5. Map与对象的区别 二、Set 1. 定义与特点 2. 创建Set对象 3. 常用方法 4. 遍历Set 5. 应用场景 数据去重 集合运算 遍历集合 一、Map 1. 定义与特点 Map 是 JavaScript ES6 中新增的一种数…...
【devops】 Git仓库如何fork一个私有仓库到自己的私有仓库 | git fork 私有仓库
一、场景说明 场景: 比如我们Codeup的私有仓库下载代码 放入我们的Github私有仓库 且保持2个仓库是可以实现fork的状态,即:Github会可以更新到Codeup的最新代码 二、解决方案 1、先从Codeup下载私有仓库代码 下载代码使用 git clone 命令…...
deepseek:三个月备考高级系统架构师
一、备考总体规划(2025年2月11日 - 2025年5月) 1. 第一阶段:基础夯实(2025年2月11日 - 2025年3月10日) 目标:快速掌握系统架构师考试的核心知识点。 重点内容: 计算机组成原理、操作系统、数据…...
设计模式Python版 命令模式(上)
文章目录 前言一、命令模式二、命令模式示例 前言 GOF设计模式分三大类: 创建型模式:关注对象的创建过程,包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式。结构型模式:关注类和对象之间的组合&…...
部署 DeepSeek R1各个版本所需硬件配置清单
DeepSeek-R1 通过其卓越的推理性能和灵活的训练机制,在 2025 年的春节期间受到了广泛关注。 DeepSeek-R1 是一款高性能的 AI 推理模型,主要通过强化学习技术来增强模型在复杂任务场景下的推理能力。 在本地部署 DeepSeek-R1 时,尤其是完整的…...
【NLP 21、实践 ③ 全切分函数切分句子】
当无数个自己离去,我便日益坦然 —— 25.2.9 一、jieba分词器 Jieba 是一款优秀的 Python 中文分词库,它支持多种分词模式,其中全切分方式会将句子中所有可能的词语都扫描出来。 1.原理 全切分方式会找出句子中所有可能的词语组合。对于一…...
pycharm如何对比两个文档
在 PyCharm 中对比两个文档,并使它们同步滚动,可以按照以下步骤操作: 在 PyCharm 的文件列表中右键点击需要对比的其中一个文件。选择 “Compares With” 选项。选择第二个文件进行对比。...
React 第二十五节 <Fragment></Fragment> 的用途以及使用注意事项详解
文章如果错误偏差,烦请及时批评指正 一、为什么要使用 <Fragment>? 因为在 React 中,组件必须返回单个根元素。当我们尝试直接返回相邻的 JSX 元素时: function BrokenComponent() {return (<h1>标题</h1><…...
探秘Hugging Face与DeepSeek:AI开源世界的闪耀双子星
目录 一、引言:AI 开源浪潮的澎湃二、Hugging Face:AI 开源社区的基石(一)起源与发展历程(二)核心技术与特色(三)在 AI 领域的广泛应用 三、DeepSeek:东方崛起的 AI 新势…...
centos 和 ubuntu 区别
一、发行版与支持 1. CentOS 是基于 Red Hat Enterprise Linux(RHEL)源代码重新编译而成的社区版,遵循开源协议。一般由社区进行维护,每 7 年左右发布一个主要版本,注重稳定性和长期支持,适合对系统稳定性…...
windows,docker停止所有容器
CMD命令窗口 你可以尝试使用以下命令来停止所有正在运行的Docker容器: FOR /f "tokens*" %i IN (docker ps -q) DO docker stop %i这条命令的工作原理是: docker ps -q 列出所有正在运行的容器的ID。 FOR /f "tokens*" %i IN (c…...
AI赋能前端性能优化:效率与体验的完美平衡
在当今快节奏的互联网世界中,用户体验至关重要。一个加载缓慢、反应迟钝的网站或应用,很容易导致用户流失。而前端性能优化,作为提升用户体验的关键环节,却常常面临着诸多挑战。复杂的代码逻辑、庞大的资源文件、以及难以捉摸的性…...
8、《5分钟构建RESTful API:Spring Boot Web开发入门》
5分钟构建RESTful API:Spring Boot Web开发入门 一、RESTful API核心认知 REST(Representational State Transfer)通过HTTP协议实现资源操作,其核心特征包括: 资源以URI标识(/api/users)通过…...
【Java八股文】01-Java基础面试篇
【Java八股文】01-Java基础面试篇 概念Java特点Java为什么跨平台JVM、JDK、JRE关系 面向对象什么是面向对象,什么是封装继承多态?多态体现的方面面向对象设计原则重载重写的区别抽象类和实体类区别Java抽象类和接口的区别抽象类可以被实例化吗 深拷贝浅拷…...
读取本地excel删除第一行,并生成List数组
在 pom.xml 里添加如下依赖: <dependencies><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.3</version></dependency><dependency><groupId>org.ap…...
【核心特性】Go 模块化开发入门(go mod 深度解析)
Go 模块化开发入门(go mod 深度解析) 一、引言 在 Go 语言的开发过程中,随着项目规模的不断扩大,依赖管理变得愈发重要。Go Modules 作为 Go 语言官方的依赖管理工具,自 Go 1.11 版本引入后,极大地简化了项…...
【新品解读】AI 应用场景全覆盖!解码超高端 VU+ FPGA 开发平台 AXVU13F
「AXVU13F」Virtex UltraScale XCVU13P Jetson Orin NX 继发布 AMD Virtex UltraScale FPGA PCIE3.0 开发平台 AXVU13P 后,ALINX 进一步研究尖端应用市场,面向 AI 场景进行优化设计,推出 AXVU13F。 AXVU13F 和 AXVU13P 采用相同的 AMD Vir…...
Flask和Django相比哪个更适合新手?
Flask 与 Django:哪个更适合新手? 对于新手来说,选择 Flask 还是 Django 主要取决于你的具体需求和项目复杂度。以下是两者的详细对比,帮助你做出选择: 1. Flask 优点 简单易用:Flask 是一个轻量级的微框架,代码简洁,易于理解和上手。适合初学者快速入门。灵活性高:…...
高通android WIFI debug
参考高通文档:80-76240-16_REV_AA_Wi-Fi_Debug_Techniques 大纲 一、 WLAN Debug Logs –logcat ■ Logcat log logcat is a command-line tool that dumps the log of system messages, ■ Including stack traces when the device throws an error. ■ Need t…...
ADC 的音频实验,无线收发模块( nRF24L01)
nRF24L01 采用 QFN20 封装,有 20 个引脚,以下是各引脚的详细介绍: 1. 电源引脚 ◦ VDD:电源输入端,一般接 3V 电源,为芯片提供工作电压,供电电压范围为 1.9V~3.6V。 ◦ VSS…...
Swagger2 Knife4jConfig 配置,父子项目swagger扫描多个子模块中的Controller生成接口文档:
问题: 需求父子项目swagger扫描多个子模块中的Controller生成接口文档: Swagger2Knife4jConfig 配置放在了 springboot-app 启动项目里,而 tiger-web 和 tiger-web2 这两个子项目有自己的 Controller。这种情况下,Swagger 可能无法正确扫描 tiger-web 和…...
Unity UI 道路线跟随:让图标沿道路轨迹移动
在 Unity UI 开发中,有时需要让图标沿着一条道路轨迹移动,比如地图上的车辆行驶动画、导航路径指示等。本文介绍如何基于 UI 图片中的道路线生成曲线,并使用 Slider 控制图标沿轨迹运动,适用于 UI 导航、路径跟随动画等场景。 1. …...
Springboot核心:拦截器
拦截器是 Spring 框架提供的核心功能之一,属于一种设计模式,它允许在某个方法调用或请求处理前后执行自定义逻辑,是一种AOP(面向切面编程)机制。它们可以在不修改原有业务逻辑的情况下,对进入系统的请求进行预处理和后…...
【AI学习笔记】基于Unity+DeepSeek开发的一些BUG记录解决方案
【AI学习笔记】基于UnityDeepSeek开发的一些BUG记录&解决方案 背景前摇:(省流可不看) Unity是大学学的,AI是研究生学的,DeepSeek是第一份实习偷师的,三合一的梦是最近开始做的,BUG是今天遇…...
六西格玛设计培训如何破解风电设备制造质量与成本困局
2023年,中国风电行业装机容量突破4.3亿千瓦,稳居全球第一,但高速扩张背后暗藏隐忧: 质量痛点:叶片开裂、齿轮箱故障等缺陷频发,运维成本占项目全生命周期成本超30%;成本压力:原材料…...
xml 和json互转工具
工具1 有个问题会导致springboot返回捕获的异常编程xml格式 import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import org.json.JSONObject; import org.json.XML; import org.w3c.dom.Document;import javax.xm…...
【STM32】舵机SG90
1.舵机原理 舵机内部有一个电位器,当转轴随电机旋转,电位器的电压会发生改变,电压会带动转一定的角度,舵机中的控制板就会电位器输出的电压所代表的角度,与输入的PWM所代表的角度进行比较,从而得出一个旋转…...
【RK3588嵌入式图形编程】-SDL2-鼠标输入处理
鼠标输入处理 文章目录 鼠标输入处理1、概述2、鼠标移动事件3、鼠标点击事件4、鼠标点击位置5、鼠标双击6、鼠标进入和离开事件7、总结在本文中,将介绍如何在 SDL2 中检测和处理鼠标输入事件,包括鼠标移动、按钮点击以及窗口进入/退出。 1、概述 在本文中,我们将详细介绍如…...
知识管理成功:关键指标和策略,研究信息的投资回报率
信息过载会影响生产力。没有人工智能的帮助,信息过载会影响生产力。大量的可用信息,知识工作者不仅仅是超负荷工作;他们感到不知所措,他们倾向于浪费时间(和脑细胞)来应付他们被大量的数据抛向他们…...
Linux权限提升-内核溢出
一:Web到Linux-内核溢出Dcow 复现环境:https://www.vulnhub.com/entry/lampiao-1,249/ 1.信息收集:探测⽬标ip及开发端⼝ 2.Web漏洞利⽤: 查找drupal相关漏洞 search drupal # 进⾏漏洞利⽤ use exploit/unix/webapp/drupal_dr…...
Bootstrap5 折叠功能详解
Bootstrap5 折叠功能详解 引言 Bootstrap 是一个流行的前端框架,它提供了许多组件和工具,使得开发者可以轻松构建响应式网站。在 Bootstrap5 中,折叠(Collapse)组件得到了增强,使其更加灵活和易于使用。本…...
JAVA:Spring Boot 项目中自定义 Banner 的技术指南
1、简述 在 Spring Boot 项目中,当应用启动时会显示默认的 Spring 标志和版本信息。定制化的启动 Banner 不仅可以美化应用,还能在项目中增加个性化的品牌印记,甚至可以提供一些关键信息。本文将介绍如何在 Spring Boot 项目中自定义启动 Ba…...
【虚幻引擎UE】UE4.23到UE5.5的核心功能变化
简单总结从UE4.23到UE5.5,虚幻引擎的重大变化: 1. WebGL/HTML5 平台支持和像素流 UE4.23-UE4.25:移除官方HTML5支持,改为社区插件维护。 但通过第三方插件(如WebAssemblyWebGPU)可在浏览器运行部分项目。U…...
信号检测和信道均衡的联系
1. 系统模型 假设一个通信系统的数学模型如下: 发送信号: s [ s 1 , s 2 , … , s N ] T \mathbf{s} [s_1, s_2, \dots, s_N]^T s[s1,s2,…,sN]T,其中 s i s_i si 是发送符号。信道矩阵: H \mathbf{H} H(…...
在WPS中通过JavaScript宏(JSA)调用本地DeepSeek API优化文档教程
既然我们已经在本地部署了DeepSeek,肯定希望能够利用本地的模型对自己软件开发、办公文档进行优化使用,接下来就先在WPS中通过JavaScript宏(JSA)调用本地DeepSeek API优化文档的教程奉上。 前提: (1)已经部署好了DeepSeek,可以看我的文章:个人windows电脑上安装DeepSe…...
26、深度学习-自学之路-NLP自然语言处理-理解加程序,怎么把现实的词翻译给机器识别。
一、怎么能让机器能够理解我们的语言呢,我们可以利用神经网络干很多的事情,那么我们是不是也可以用神经元做自然语言处理呢,现在很多的实际应用已经说明了这个问题,可以这么做。 那我们考虑一下该怎么做,首先我们应该…...
微信服务号推送消息
这里如果 没有 就需要点新的功能去申请一下 申请成功之后就可以设置模版消息 推送到用户接受的页面是 需要后端调用接口 传递token 发送给客户...
Excel常用操作
Excel常用操作 学习资源 37_电子表格处理考点精讲_设置数据格式_哔哩哔哩_bilibili 快速输入数据与编辑数据 一个工作簿可以包含多个工作表 特殊数据的添加格式 输入负数, 例如-3、-5 常规输入, 直接输入-3、-5;使用(), 例如在单元格中输入(3)回车即可变为-3;上述括号不区分中…...
得物端智能视频封面推荐
一、背景 什么要做智能封面? 用户可以在得物购物,也可以在得物社区分享自己的生活。 得物社区中的视频使用双列流,每条内容包含封面、标题等。 对得物社区的创作者而言,选择视频封面是创作链路的重要环节。对得物社区的消费者…...
【Stable Diffusion部署至GNU/Linux】安装流程
以下是安装Stable Diffusion的步骤,以Ubuntu 22.04 LTS为例子。 显卡与计算架构介绍 CUDA是NVIDIA GPU的专用并行计算架构 技术层级说明CUDA Toolkit提供GPU编译器(nvcc)、数学库(cuBLAS)等开发工具cuDNN深度神经网络加速库(需单独下载)GPU驱动包含CUDA Driver(需与CUDA …...
介绍 Liquibase、Flyway、Talend 和 Apache NiFi:选择适合的工具
在现代软件开发中,尤其是在数据库管理和数据集成方面,选择合适的工具至关重要。本文将介绍四个流行的工具:Liquibase、Flyway、Talend 和 Apache NiFi,分析它们的应用、依赖以及如何选择适合的工具。 1. Liquibase 简介ÿ…...
Unity 接入Tripo 文生模型,图生模型
官方网站:https://www.tripo3d.ai/app/home自行注册账号并且登陆下载Unity插件:https://cdn-web.tripo3d.ai/plugin/tripo-unity.zip申请apikey: https://platform.tripo3d.ai/api-keys使用(后续过程就按照第二步下载的插件里面的…...
黑马Redis详细笔记(实战篇---短信登录)
目录 一.短信登录 1.1 导入项目 1.2 Session 实现短信登录 1.3 集群的 Session 共享问题 1.4 基于 Redis 实现共享 Session 登录 一.短信登录 1.1 导入项目 数据库准备 -- 创建用户表 CREATE TABLE user (id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 用户ID,phone …...
Unity 增量打包AssetBundle
背景 打包太慢了,想要没改动的资源不重新打包浪费时间。 来源 官方文章:https://forum.unity.com/threads/about-incremental-build-and-asset-bundle-hashes.1436032/ 官网AB介绍:https://learn.unity.com/tutorial/assets-resources-and…...
前端 TypeError 错误永久消失术
作者:来自 vivo 互联网大前端团队- Sun Maobin 通过开发 Babel 插件,打包时自动为代码添加 可选链运算符(?.),从而有效避免 TypeError 的发生。 一、背景介绍 在 JS 中当获取引用对象为空值的属性时,程序会立即终止运行并报错&…...
【视频总结】Deep Dive into LLMs like ChatGPT 深入探索像ChatGPT这样的大语言模型|Andrej Karpathy
【视频总结】Deep Dive into LLMs like ChatGPT 深入探索像ChatGPT这样的大语言模型|Andrej Karpathy 大型语言模型(LLM)工作原理与使用指南核心观点模型训练三阶段1. 预训练阶段2. 后训练阶段(Post-training)3. 强化学…...