带残差连接的ResNet18
目录
1 模型构建
1.1 残差单元
1.2 残差网络的整体结构
2 没有残差连接的ResNet18
2.1 模型训练
2.2 模型评价
3 带残差连接的ResNet18
3.1 模型训练
3.2 模型评价
4 与高层API实现版本的对比实验
总结
残差网络(Residual Network,ResNet)是在神经网络模型中给非线性层增加直连边的方式来缓解梯度消失问题,从而使训练深度神经网络变得更加容易。
在残差网络中,最基本的单位为残差单元。
假设为一个或多个神经层,残差单元在$f()$的输入和输出之间加上一个直连边。
不同于传统网络结构中让网络去逼近一个目标函数
,在残差网络中,将目标函数
拆为了两个部分:恒等函数
和残差函数
其中为可学习的参数。
一个典型的残差单元如图所示,由多个级联的卷积层和一个跨层的直连边组成。
一个残差网络通常有很多个残差单元堆叠而成。下面我们来构建一个在计算机视觉中非常典型的残差网络:ResNet18,并重复上一节中的手写体数字识别任务。
1 模型构建
在本节中,我们先构建ResNet18的残差单元,然后在组建完整的网络。
1.1 残差单元
这里,我们实现一个算子ResBlock来构建残差单元,其中定义了use_residual参数,用于在后续实验中控制是否使用残差连接。
残差单元包裹的非线性层的输入和输出形状大小应该一致。如果一个卷积层的输入特征图和输出特征图的通道数不一致,则其输出与输入特征图无法直接相加。为了解决上述问题,我们可以使用大小的卷积将输入特征图的通道数映射为与级联卷积输出特征图的一致通道数。
卷积:与标准卷积完全一样,唯一的特殊点在于卷积核的尺寸是
,也就是不去考虑输入数据局部信息之间的关系,而把关注点放在不同通道间。通过使用
卷积,可以起到如下作用:
- 实现信息的跨通道交互与整合。考虑到卷积运算的输入输出都是3个维度(宽、高、多通道),所以
卷积实际上就是对每个像素点,在不同的通道上进行线性组合,从而整合不同通道的信息;
- 对卷积核通道数进行降维和升维,减少参数量。经过
卷积后的输出保留了输入数据的原有平面结构,通过调控通道数,从而完成升维或降维的作用;
- 利用
卷积后的非线性激活函数,在保持特征图尺寸不变的前提下,大幅增加非线性。
class ResBlock(nn.Module):def __init__(self, in_channels, out_channels, stride=1, use_residual=True):super(ResBlock, self).__init__()self.stride = strideself.use_residual = use_residual# 第一个卷积层,卷积核大小为3×3,可以设置不同输出通道数以及步长self.conv1 = nn.Conv2d(in_channels, out_channels, 3, padding=1, stride=self.stride)# 第二个卷积层,卷积核大小为3×3,不改变输入特征图的形状,步长为1self.conv2 = nn.Conv2d(out_channels, out_channels, 3, padding=1)# 如果conv2的输出和此残差块的输入数据形状不一致,则use_1x1conv = True# 当use_1x1conv = True,添加1个1x1的卷积作用在输入数据上,使其形状变成跟conv2一致if in_channels != out_channels or stride != 1:self.use_1x1conv = Trueelse:self.use_1x1conv = False# 当残差单元包裹的非线性层输入和输出通道数不一致时,需要用1×1卷积调整通道数后再进行相加运算if self.use_1x1conv:self.shortcut = nn.Conv2d(in_channels, out_channels, 1, stride=self.stride)# 每个卷积层后会接一个批量规范化层,批量规范化的内容在7.5.1中会进行详细介绍self.bn1 = nn.BatchNorm2d(out_channels)self.bn2 = nn.BatchNorm2d(out_channels)if self.use_1x1conv:self.bn3 = nn.BatchNorm2d(out_channels)def forward(self, inputs):y = F.relu(self.bn1(self.conv1(inputs)))y = self.bn2(self.conv2(y))if self.use_residual:if self.use_1x1conv: # 如果为真,对inputs进行1×1卷积,将形状调整成跟conv2的输出y一致shortcut = self.shortcut(inputs)shortcut = self.bn3(shortcut)else: # 否则直接将inputs和conv2的输出y相加shortcut = inputsy = torch.add(shortcut, y)out = F.relu(y)return out
1.2 残差网络的整体结构
残差网络就是将很多个残差单元串联起来构成的一个非常深的网络。ResNet18 的网络结构如图所示。

其中为了便于理解,可以将ResNet18网络划分为6个模块:
- 第一模块:包含了一个步长为2,大小为
的卷积层,卷积层的输出通道数为64,卷积层的输出经过批量归一化、ReLU激活函数的处理后,接了一个步长为2的
的最大汇聚层;
- 第二模块:包含了两个残差单元,经过运算后,输出通道数为64,特征图的尺寸保持不变;
- 第三模块:包含了两个残差单元,经过运算后,输出通道数为128,特征图的尺寸缩小一半;
- 第四模块:包含了两个残差单元,经过运算后,输出通道数为256,特征图的尺寸缩小一半;
- 第五模块:包含了两个残差单元,经过运算后,输出通道数为512,特征图的尺寸缩小一半;
- 第六模块:包含了一个全局平均汇聚层,将特征图变为
的大小,最终经过全连接层计算出最后的输出。
ResNet18模型的代码实现如下:
定义模块一
def make_first_module(in_channels):m1 = nn.Sequential(nn.Conv2d(in_channels, 64, 7, stride=2, padding=3), nn.BatchNorm2d(64), nn.ReLU(),nn.MaxPool2d(kernel_size=3, stride=2, padding=1))return m1
定义模块二到模块五
def resnet_module(input_channels, out_channels, num_res_blocks, stride=1, use_residual=True):blk = []for i in range(num_res_blocks):if i == 0:blk.append(ResBlock(input_channels, out_channels, stride=stride, use_residual=use_residual))else:blk.append(ResBlock(out_channels, out_channels, use_residual=use_residual))return blk
封装模块二到模块五
def make_modules(use_residual):# 模块二:包含两个残差单元,输入通道数为64,输出通道数为64,步长为1,特征图大小保持不变m2 = nn.Sequential(*resnet_module(64, 64, 2, stride=1, use_residual=use_residual))# 模块三:包含两个残差单元,输入通道数为64,输出通道数为128,步长为2,特征图大小缩小一半。m3 = nn.Sequential(*resnet_module(64, 128, 2, stride=2, use_residual=use_residual))# 模块四:包含两个残差单元,输入通道数为128,输出通道数为256,步长为2,特征图大小缩小一半。m4 = nn.Sequential(*resnet_module(128, 256, 2, stride=2, use_residual=use_residual))# 模块五:包含两个残差单元,输入通道数为256,输出通道数为512,步长为2,特征图大小缩小一半。m5 = nn.Sequential(*resnet_module(256, 512, 2, stride=2, use_residual=use_residual))return m2, m3, m4, m5
定义完整网络
class Model_ResNet18(nn.Module):def __init__(self, in_channels=3, num_classes=10, use_residual=True):super(Model_ResNet18, self).__init__()m1 = make_first_module(in_channels)m2, m3, m4, m5 = make_modules(use_residual)self.net = nn.Sequential(m1, m2, m3, m4, m5, nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Linear(512, num_classes))def forward(self, x):return self.net(x)
这里同样可以使用torchsummary.summary统计模型的参数量。
from torchsummary import summarydevice = torch.device("cuda" if torch.cuda.is_available() else "cpu") # PyTorch v0.4.0
model = Model_ResNet18(in_channels=1, num_classes=10, use_residual=True).to(device)
summary(model, (1, 32, 32))
实验结果:

使用thop.profile统计模型的计算量
from thop import profiledevice = torch.device("cuda" if torch.cuda.is_available() else "cpu") # PyTorch v0.4.0
model = Model_ResNet18(in_channels=1, num_classes=10, use_residual=True).to(device)
dummy_input = torch.randn(1, 1, 32, 32).to(device)flops, params = profile(model, (dummy_input,))
print(flops)

为了验证残差连接对深层卷积神经网络的训练可以起到促进作用,接下来先使用ResNet18(use_residual设置为False)进行手写数字识别实验,再添加残差连接(use_residual设置为True),观察实验对比效果。
2 没有残差连接的ResNet18
为了验证残差连接的效果,先使用没有残差连接的ResNet18进行实验。
2.1 模型训练
使用训练集和验证集进行模型训练,共训练5个epoch。在实验中,保存准确率最高的模型作为最佳模型。代码实现如下
# 固定随机种子
random.seed(0)
# 学习率大小
lr = 0.005
# 批次大小
batch_size = 64
# 加载数据
train_loader = data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
dev_loader = data.DataLoader(dataset=dev_dataset, batch_size=batch_size)
test_loader = data.DataLoader(dataset=test_dataset, batch_size=batch_size)
# 定义网络,不使用残差结构的深层网络
model = Model_ResNet18(in_channels=1, num_classes=10, use_residual=False)
# 定义优化器
optimizer = opt.SGD(lr=lr, params=model.parameters())
# 定义损失函数
loss_fn = F.cross_entropy
# 定义评价指标
metric = Accuracy(is_logist=True)
# 实例化RunnerV3
runner = RunnerV3(model, optimizer, loss_fn, metric)
# 启动训练
log_steps = 15
eval_steps = 15
runner.train(train_loader, dev_loader, num_epochs=5, log_steps=log_steps,eval_steps=eval_steps, save_path="best_model.pdparams")
# 可视化观察训练集与验证集的Loss变化情况
plot(runner, 'cnn-loss2.pdf')


2.2 模型评价
使用测试数据对在训练过程中保存的最佳模型进行评价,观察模型在测试集上的准确率以及损失情况。代码实现如下
![]()
3 带残差连接的ResNet18
3.1 模型训练
使用带残差连接的ResNet18重复上面的实验,代码实现如下:
random.seed(0)
# 加载 mnist 数据集
train_dataset = MNIST_dataset(dataset=train_set, transforms=transforms, mode='train')
test_dataset = MNIST_dataset(dataset=test_set, transforms=transforms, mode='test')
dev_dataset = MNIST_dataset(dataset=dev_set, transforms=transforms, mode='dev')
# 学习率大小
lr = 0.01
# 批次大小
batch_size = 128
# 加载数据
train_loader = data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
dev_loader = data.DataLoader(dev_dataset, batch_size=batch_size)
test_loader = data.DataLoader(test_dataset, batch_size=batch_size)
# 定义网络,通过指定use_residual为True,使用残差结构的深层网络
model = Model_ResNet18(in_channels=1, num_classes=10, use_residual=True)
# 定义优化器
optimizer = opt.SGD(lr=lr, params=model.parameters())
# 定义损失函数
loss_fn = F.cross_entropy
# 定义评价指标
metric = Accuracy(is_logist=True)
# 实例化RunnerV3
runner = RunnerV3(model, optimizer, loss_fn, metric)
# 启动训练
log_steps = 15
eval_steps = 15
runner.train(train_loader, dev_loader, num_epochs=5, log_steps=log_steps,eval_steps=eval_steps, save_path="best_model.pdparams")
# 可视化观察训练集与验证集的Loss变化情况
plot(runner, 'cnn-loss3.pdf')


3.2 模型评价
使用测试数据对在训练过程中保存的最佳模型进行评价,观察模型在测试集上的准确率以及损失情况。
# 加载最优模型
runner.load_model('best_model.pdparams')
# 模型评价
score, loss = runner.evaluate(test_loader)
print("[Test] accuracy/loss: {:.4f}/{:.4f}".format(score, loss))
4 与高层API实现版本的对比实验
对于Reset18这种比较经典的图像分类网络,pytorch中都为大家提供了实现好的版本,大家可以不再从头开始实现。这里为高层API版本的resnet18模型和自定义的resnet18模型赋予相同的权重,并使用相同的输入数据,观察输出结果是否一致。
import torchvision.models as models
from collections import OrderedDict
import warningswarnings.filterwarnings("ignore")# 使用飞桨HAPI中实现的resnet18模型,该模型默认输入通道数为3,输出类别数1000
hapi_model = models.resnet18()
# 自定义的resnet18模型
model = Model_ResNet18(in_channels=3, num_classes=1000, use_residual=True)# 获取网络的权重
params = hapi_model.state_dict()# 用来保存参数名映射后的网络权重
new_params = {}
# 将参数名进行映射
for key in params:if 'layer' in key:if 'downsample.0' in key:new_params['net.' + key[5:8] + '.shortcut' + key[-7:]] = params[key]elif 'downsample.1' in key:new_params['net.' + key[5:8] + '.bn3.' + key[22:]] = params[key]else:new_params['net.' + key[5:]] = params[key]elif 'conv1.weight' == key:new_params['net.0.0.weight'] = params[key]elif 'conv1.bias' == key:new_params['net.0.0.bias'] = params[key]elif 'bn1' in key:new_params['net.0.1' + key[3:]] = params[key]elif 'fc' in key:new_params['net.7' + key[2:]] = params[key]new_params['net.0.0.bias'] = torch.zeros([64])
# 将飞桨HAPI中实现的resnet18模型的权重参数赋予自定义的resnet18模型,保持两者一致
model.load_state_dict(OrderedDict(new_params))# 这里用np.random创建一个随机数组作为测试数据
inputs = np.random.randn(*[3, 3, 32, 32])
inputs = inputs.astype('float32')
x = torch.tensor(inputs)output = model(x)
hapi_out = hapi_model(x)# 计算两个模型输出的差异
diff = output - hapi_out
# 取差异最大的值
max_diff = torch.max(diff)
print(max_diff)
注意这里代码跑不通显示如下:
Traceback (most recent call last): File "C:\Users\29134\PycharmProjects\pythonProject\DL\实验12\ResNet.py", line 236, in <module> model.load_state_dict(OrderedDict(new_params)) File "C:\ANACONDA\envs\pytorch\Lib\site-packages\torch\nn\modules\module.py", line 2041, in load_state_dict raise RuntimeError('Error(s) in loading state_dict for {}:\n\t{}'.format( RuntimeError: Error(s) in loading state_dict for Model_ResNet18: Missing key(s) in state_dict: "net.0.0.bias", "net.1.0.conv1.bias", "net.1.0.conv2.bias", "net.1.1.conv1.bias", "net.1.1.conv2.bias", "net.2.0.conv1.bias", "net.2.0.conv2.bias", "net.2.0.shortcut.bias", "net.2.1.conv1.bias", "net.2.1.conv2.bias", "net.3.0.conv1.bias", "net.3.0.conv2.bias", "net.3.0.shortcut.bias", "net.3.1.conv1.bias", "net.3.1.conv2.bias", "net.4.0.conv1.bias", "net.4.0.conv2.bias", "net.4.0.shortcut.bias", "net.4.1.conv1.bias", "net.4.1.conv2.bias".
找了很多资料但是依旧没找到怎么解决,同班同学的代码也跑不通,结论怎么出来的疑惑,这两天时间不太充裕全是结课论文,过两天会回来再次尝试解决这个问题的
总结
首先,使用带残差连接的ResNet模型相比于不带残差的模型,在训练过程中表现出更好的性能。带残差的模型具有更快的收敛速度、更低的损失和更高的准确率。这证明了残差块确实能够为网络带来性能提升,而无脑堆砌网络层并不能有效地提高模型的性能。这个结果也打破了我一直都认为神经网络越深性能越好的理论认知,同时通过学长的博客我认识到残差连接能够有效地缓解梯度消失问题,减少训练难度,并提高了网络的深度和表达能力。这也算一个小小的收获吧(那一大堆推导我真没看懂!!哭)
放上学长的博客:
NNDL 实验六 卷积神经网络(4)ResNet18实现MNIST-CSDN博客
相关文章:
带残差连接的ResNet18
目录 1 模型构建 1.1 残差单元 1.2 残差网络的整体结构 2 没有残差连接的ResNet18 2.1 模型训练 2.2 模型评价 3 带残差连接的ResNet18 3.1 模型训练 3.2 模型评价 4 与高层API实现版本的对比实验 总结 残差网络(Residual Network,ResNet)…...
【深入解析git和gdb:版本控制与调试利器的终极指南】
【本节目标】 1. 掌握简单gdb使用于调试 2. 学习 git 命令行的简单操作, 能够将代码上传到 Github 上 1.Linux调试器-gdb使用 1.1.背景 程序的发布方式有两种,debug模式和release模式release模式不可被调试,debug模式可被调试Linux gcc/g出来的二进制…...
CGAN原理讲解与源码
1.CGAN原理 生成器,输入的是c和z,z是随机噪声,c是条件,对应MNIST数据集,要求规定生成数字是几。 输出是生成的虚假图片。 生成器生成的图片被判别器认为是真实图片,那么标签就是1 其实判别器模型输出的是…...
C#实体类与XML互转以及List和DataTable转XML的使用
引言 在C#开发中,数据的存储和传输是非常常见的需求。使用XML作为数据格式有很多优点,例如可读性强、易于解析等。而实体类、List和DataTable是表示数据模型的常用方式。本文将介绍如何在C#中实现实体类、List和DataTable与XML之间的相互转换,…...
uniapp的vue3的模版的setup函数内使用uniapp内置方法
vue2使用方式直接在method同级使用就行,但是在vue3的setup函数内直接使用会报错,本人找了好久,发现vue3需要导入uniapp模块才能使用,具体如下 使用uniapp上拉加载更多方法 <script>import {onReachBottom} from dcloudio/uni-apponReachBottom(() > {console.log(&qu…...
UI自动化的基本知识
一、UI自动化测试介绍 1、什么是自动化测试 概念:由程序代替人工进行系统校验的过程 1.1自动化测试能解决的问题? 回归测试 (冒烟测试) 针对之前老的功能进行测试 通过自动化的代码来实现。 针对上一个版本的问题的回归 兼容性测试 web实例化不同的浏…...
python实现C++简易自动压行
突发奇想,想要将自己的c压行之后交上去。但是苦于手动压行效率太低,在网上搜索压行网站没有找到,突然发现压行不就是检查检查去个换行符吗。于是心血来潮,用python实现了一个简易压行程序。 首先,宏定义等带#的文件不…...
京东数据分析(京东大数据采集):2023年线上珍珠市场销售数据采集
在珠宝首饰市场,从黄金到钻石,如今年轻人的新风潮又转向了珍珠。珍珠热潮并非刚刚兴起,早在前两年,抖音、快手等短视频台的珍珠开蚌直播内容,就掀起了一波珍珠热潮。 此后,随着珍珠饰品被越来越多社交平台的…...
亚信科技AntDB数据库与库瀚存储方案完成兼容性互认证
近日,亚信科技AntDB数据库与苏州库瀚信息科技有限公司自主研发的RISC-V数据库存储解决方案进行了产品兼容测试。经过双方团队的严格测试,亚信科技AntDB数据库与库瀚数据库存储解决方案完全兼容、运行稳定。除高可用性测试外,双方进一步开展TP…...
现代C++之万能引用、完美转发、引用折叠
现代C之万能引用、完美转发、引用折叠 0.导语1.问题引入2.引入万能引用3.万能引用出现场合4.理解左值与右值4.1 精简版4.2 完整版4.3 生命周期延长4.4 生命周期延长应用5.区分万能引用6.表达式的左右值性与类型无关7.引用折叠和完美转发7.1 引用折叠之本质细节7.2 示例与使用7.…...
ELK日志收集系统-filbeat
filebeat日志收集工具 elk:filebeat日志收集工具和logstash相同 filebeat是一个轻量级的日志收集工具,所使用的系统资源比logstash部署和启动时使用的资源要小的多 filebeat可以运行在非Java环境,它可以代理logstash在非java环境上收集日志…...
Python小知识
个人学习笔记,用于记录使用过程中好用的技巧、好用的库。 1 小知识 1.1 相对路径 1.2 打包Exe文件 命令: pyinstaller -F main.py其中-F:覆盖之前打包的文件 mian.py:需要打包的Python文件 PS:使用pyinstaller 5.1…...
如何在Ubuntu系统上安装Redis
Redis的下载 Redis安装包分为windows版和Linux版当前示例中介绍的是Linux版本Linux的下载地址:Index of /releases/ (redis.io)本次下载的压缩包为:redis-6.2.14.tar.gzRedis的安装 将压缩包通过ssh远程工具上传到Linux服务器中解压压缩包 tar -zxvf red…...
Vue2问题:如何全局使用less和sass变量?
前端功能问题系列文章,点击上方合集↑ 序言 大家好,我是大澈! 本文约2400字,整篇阅读大约需要4分钟。 本文主要内容分三部分,如果您只需要解决问题,请阅读第一、二部分即可。如果您有更多时间ÿ…...
Java 基础学习(四)操作数组、软件开发管理
1 操作数组 1.1.1 System.arraycopy 方法用于数组复制 当需要将一个数组的元素复制到另一个数组中时,可以使用System.arraycopy方法。它提供了一种高效的方式来复制数组的内容,避免了逐个元素赋值的繁琐过程。相对于使用循环逐个元素赋值的方式&#x…...
git仓库如何撤销提交,恢复提交,重置版本命令
撤销提交: 要撤销最近一次提交(未推送到远程仓库),可以使用以下命令: git reset HEAD^该命令将会把最后一次提交的修改从当前主分支中移除,并将这些修改的状态保留在本地工作目录中。 如果想要取消所有的…...
Java 基础学习(三)循环流程控制与数组
1 循环流程控制 1.1 循环流程控制概述 1.1.1 什么是循环流程控制 当一个业务过程需要多次重复执行一个程序单元时,可以使用循环流程控制实现。 Java中包含3种循环结构: 1.2 for循环 1.2.1 for循环基础语法 for循环是最常用的循环流程控制ÿ…...
别太担心,人类只是把一小部分理性和感性放到了AI里
尽管人工智能(AI)在许多方面已经取得了重大进展,但它仍然无法完全复制人类的理性和感性。AI目前主要侧重于处理逻辑和分析任务,而人类则具有更复杂的思维能力和情感经验。 人类已经成功地将一些可以数据化和程序化的理性和感性特征…...
最新AIGC创作系统ChatGPT系统源码+DALL-E3文生图+图片上传对话识图/支持OpenAI-GPT全模型+国内AI全模型
一、AI创作系统 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI…...
在centos7上源码安装nginx
1. 安装必要的编译工具和依赖项 在编译Nginx之前,你需要安装一些编译工具和依赖项。可以通过以下命令安装: yum install gcc-c pcre-devel zlib-devel make 2. 下载Nginx源代码 从Nginx官网下载最新的源代码。你可以使用wget命令来下载: …...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
