深度学习卷积神经网络项目实战——超市商品分类

卷积神经网络项目实战

1.项目简介

1.1项目名称

​ 基于CNN实现超市商品的混合颗粒度分类(500分类)

1.2 项目简介

​ 该项目旨在通过卷积神经网络(CNN)实现超市商品的混合颗粒度分类,主要针对商品的不同种类进行分类,应用场景主要为大型超市的自助收银台,设备通过模型的嵌入快速识别商品种类并为顾客提供结账操作,降低了超市的人力资源,以及提高人员流通性。

​ 本项目引入深度学习中的CNN模型,构建一个专门用于超市常见商品分类的网络架构,充分利用卷积层的局部特征提取能力以及深层网络的高阶特征融合能力,其包含了数据收集、数据预处理、模型训练、模型评估、模型移植、可视化等步骤,使用RP2K数据集进行模型训练,模型通过反复地特征学习提高分类精确度。同时,由于其中也包含部分细粒度难区分的类目(如不同水果、同种包装的不同饮料),项目中使用了数据增强等技术手段提高模型的泛化能力,最终达到高精度地分类结果。

2.数据集

本次项目所使用的数据集为RP2K数据集

RP2K数据集

RP2K (Retail Product 2000K) 是一个用于零售场景的 大规模细粒度商品图像分类数据集

提出背景:在超市、便利店等零售场景中,存在数千上万种商品,很多商品的外观极其相似(比如不同口味的可乐、不同包装的方便面),这使得计算机视觉中的 细粒度图像分类(Fine-grained Image Classification, FGIC)成为一个关键挑战。

RP2K数据集包含了2000+商品类别,数据总量超过 200 万张商品图片,图片来源于真实零售场景(货架拍摄、收银台、监控视频帧)以及部分网络采集,覆盖了多角度(正面、侧面、倾斜)、不同分辨率与清晰度光照差异遮挡(部分商品被挡住)等环境。

本项目节选了其中的500类数据作为该项目的数据集。

下载

官网:RP2K链接 – 品览科技

在这里插入图片描述

下载的数据集已经被划分好,无需自己手动进行数据清洗

在这里插入图片描述

3.模型训练

3.1加载训练集数据/数据预处理

(1)设置数据增强:包含了改变图片大小、随机裁剪、随机水平翻转、颜色改变、随机翻转、归一化操作

    transform = transforms.Compose([transforms.Resize((224, 224)), # 改变图片大小transforms.RandomCrop((224, 224)), # 随机裁剪transforms.RandomHorizontalFlip(), # 随机水平翻转transforms.ColorJitter(0.2, 0.2, 0.2, 0.1), # 改变颜色transforms.RandomRotation(10), # (-10°, 10°)随机旋转transforms.ToTensor(), # 归一化transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])# 标准化

transforms.Compose()PyTorch torchvision 中用于组合多个图像预处理/增强操作的工具。它的作用就是把多个 transform 按顺序串起来,让图像数据在加载时依次经过这些操作。

详细参数介绍参考pytorch官方文档:Illustration of transforms — Torchvision 0.23 documentation

(2)使用pytorch框架的ImageFolder加载训练数据

    data_train = ImageFolder(root='./data/train', transform=transform)

transform参数为(1)中的数据增强设置,即加载的每一张图片都经过transform中的操作处理。

(3)使用数据加载器分批次加载数据

创建数据加载器对象:

train_loader = DataLoader(data_train, batch_size=32, num_workers=4, shuffle=True)

返回的train_loader是由数据包装成的可迭代对象

batch_size为每批次数据的数量大小,根据个人电脑GPU性能进行设置,一般设置为2n2^n2n

num_workers数据加载的子进程数,>0 表示用多进程加载,能加快训练速度,一般设置为 0 或小于 CPU 核数。

shuffle决定是否打乱数据顺序,一般在训练时设置为True,测试时设置为False。

3.2模型选择

这里我选择了googlenet模型。我们的训练思路是使用已经训练好的模型参数,只对最后的输出层进行训练,也就是参数更新,具体操作如下。

加载预训练参数

    # 加载预训练参数pretrained = googlenet(pretrained=True)pre_weights = pretrained.state_dict()pre_weights = {k: v.to(device) for k, v in pre_weights.items()} 

state_dict()是模型通用方法,作用是返回参数字典

字典推导式的作用为遍历原字典的每个键值对 (k,v),对每个 tensor v 调用 .to(device) 方法,把它移动到指定设备,device一般设置为:

    device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

参数和模型默认存放在CPU中,使用to(device)可将其转移至GPU。

清除预训练参数的最后一层:

    # 清除预训练参数最后一层数据pre_weights.pop('fc.weight')pre_weights.pop('fc.bias')

创建新模型:

    # 创建模型model = googlenet(pretrained=False)num_classes = 500 # 分类类别数model.fc = nn.Linear(model.fc.in_features, num_classes)model = model.to(device)

model.fc = nn.Linear(model.fc.in_features, num_classes)的作用是将原本网络结构中的最后一层输出(即类别数)改为500。

更新模型参数:

    # 更新模型参数weigths = model.state_dict()weigths.update(pre_weights)model.load_state_dict(weigths)

将预训练参数更新到模型中。

冻结层:

    # 冻结层for name, param in model.named_parameters():if name != 'fc.weight' and name != 'fc.bias':param.requires_grad = False

将模型最后一层之前的参数的梯度计算禁用。

model.named_parameters() 是一个 生成器(generator),它会遍历模型中的所有参数,并返回 (参数名, 参数张量) 这样的键值对。

设置优化器:

    # 优化器parameters = [x for x in model.parameters() if x.requires_grad]optimizer = opt.Adam(parameters, lr=1e-3)

优化器用于模型进行参数更新,这里我们只更新最后一层参数,并且使用的是Adam优化器。

设置损失函数:

    # 损失函数criterion = nn.CrossEntropyLoss(reduction='sum')

选择交叉熵损失函数作为多分类损失函数。reduction 参数,它决定了 批次(batch)里多个样本的损失要如何合并,这里选择的是进行求和。

定义训练轮次:

    epochs = 50

设置学习率调度器:

    # 学习率调度器scheduler = opt.lr_scheduler.CosineAnnealingLR(optimizer, T_max=epochs, eta_min=1e-5)

这里选择的是余弦学习率调度器,目的是对每轮训练的学习率进行更新,让学习率从 高学习率非线性地逐渐减小到低学习率

3.3训练

    acc = []best_acc = 0# 训练for epoch in range(epochs):acc_total = 0loss_total = 0for i, (data, target) in enumerate(train_loader):data, target = data.to(device), target.to(device)output = model(data)main_out = output[0]  # 主输出aux1_out = output[1]  # 辅助输出1aux2_out = output[2]  # 辅助输出2# 选取最大值作为分类结果,返回的pred为标签下标pred = torch.argmax(main_out, dim=1)acc_total += torch.sum(pred == target).item()# 梯度清零optimizer.zero_grad()# 计算综合损失loss = criterion(main_out, target) + 0.3 * (criterion(aux1_out, target) + criterion(aux2_out, target))loss_total += loss# 反向传播loss.backward()# 参数更新optimizer.step()# 学习率更新scheduler.step()# 将当前轮次模型准确率记录在acc中acc.append(acc_total / len(data_train))current_acc = acc[epoch]# 判断当前轮次参数准确率是否为最佳,若为最佳则保存该参数if current_acc > best_acc:# 更新最佳准确率best_acc = current_acctorch.save(model.state_dict(), './model/model.pth')# 打印每轮训练结果print(f'epoch--->{epoch + 1},'f'loss---> {loss_total.item() / len(data_train):.4f},'f' acc---> {acc_total / len(data_train):.4f}')
  • acc用于保存每轮训练后的参数,best_acc用于保存历史最高准确率
  • 由于Googlenet模型内置有辅助选择器,所以其输出结果为一个元组,为一个主要输出和两个辅助输出,在进行损失函数计算的过程中,需要考虑辅助分类的损失,通过分配不同权重比得到最终的损失函数。
  • 判断当前准确率是否为历史最佳准确率,若为最佳准确率则保存参数,方便后续进行模型迁移。

3.4训练过程可视化

使用tensorboard模块将训练过程可视化:

from torch.utils.tensorboard import SummaryWriter

创建tensorboard对象:

    # 创建tensorboard对象writer = SummaryWriter()

SummaryWriter(log_dir=tbpath)log_dir为tensorboard日志保存路径,未设置保存路径会保存至当前文件夹下自动创建的runs文件夹下。

绘制过程曲线:

        writer.add_scalar('loss', loss_total, epoch)writer.add_scalar('acc', acc_total / len(data_train), epoch)

显示数据增强后的图片:

            if i % 100 == 0:img_grid = torchvision.utils.make_grid(data)writer.add_image(f'r_m_{epoch}', img_grid, i)

运行后会在runs文件夹内生成可视化的文件。

接下来打开终端,输入命令:

tensorboard --logdir=runs   

也可将runs改为指定路径。

随后点击生成的本地网站即可查看绘制的曲线图以及图片:

在这里插入图片描述

4.模型评估

4.1加载预测集数据

同加载训练集类似,只不过在数据增强时一般只进行重新设置图片大小和标准化以及归一化操作,因为模型预测是在真实数据分布下的表现,因此我们 不能引入随机性,而resize图片大小是为了让数据的shape和模型的输入shape一致。

# 加载预测数据集
transform = transforms.Compose([transforms.Resize((224, 224)),transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
data_test = ImageFolder(root='./data/test', transform=transform)
test_loader = DataLoader(data_test, batch_size=32, shuffle=False)

4.2加载模型

加载之前训练好的模型参数

# 加载训练好的模型
model = googlenet(pretrained=False)
model.fc = nn.Linear(model.fc.in_features, 500)
model = model.to(device)
weights = torch.load('./model/model.pth')
model.load_state_dict(weights)

4.3结果预测

首先将模型切至验证模式:

# 验证模式
model.eval()  # 关闭 Dropout 和辅助分类器

注意:这一步至关重要,模型默认会处于训练模式下,这会使得google网络结构中的辅助分类和Dropout没有关闭,dropout未关闭会导致预测时神经元丢失,影响最终结果。

进行验证:

with torch.no_grad():acc = 0for data, label in test_loader:data, label = data.to(device), label.to(device)output = model(data)# 选取最大值作为分类结果,返回的pred为标签下标pred = torch.argmax(output, dim=1)acc += torch.sum(pred == label).item()

with torch.no_grad():作用:关闭参数梯度计算,加速预测,节省内存。

4.4预测评估

打印结果:

acc = acc / len(data_test)
print(f"acc:{acc:.5f}")

4.5预测结果可视化

可引入numpy和pandas将预测的数据转变成excel表格(csv格式文件),具体操作如下:

创建一个列数为502的numpy空二维数组:

data_csv = np.empty((0, 502))

因为模型的输出为每个类别下所对应的输出值,取最大的输出值的类别作为分类结果,所以需要500列存放模型输出值,1列存放预测标签,1列存放实际标签。

将每一批次训练的数据存入numpy数组:

output_csv = output.cpu().detach().numpy()
pred_csv = np.expand_dims(pred.cpu().detach().numpy(), axis=1)
label_csv = np.expand_dims(label.cpu().detach().numpy(), axis=1)
batch_csv = np.concatenate((output_csv, pred_csv, label_csv), axis=1)
data_csv = np.concatenate((data_csv, batch_csv), axis=0)

取出标签:

labels = data_test.classes

将结果转换为Datafream再导出为csv文件:

# 将结果转为csv文件
column = [*labels, 'pred', 'label']
data_csv = pd.DataFrame(data=data_csv, columns=column)
data_csv.to_csv('./data_pred_result/result.csv', index=False)

同时把标签存为csv文件:

# 将标签存为csv文件
df = pd.DataFrame({"index": list(range(len(labels))), "label": [*labels]})
df.to_csv('./labels.csv', index=False)

也可以将网络结构进行可视化观察网络结构:

writer = SummaryWriter("./net_frame")
dummy_input = torch.randn(1, 3, 224, 224)
writer.add_graph(model, dummy_input.to(device))
writer.close()

同样的用终端生成本地网站,进入网站查看可视化结果:

 tensorboard --logdir=文件绝对路径

在这里插入图片描述

4.6生成分类报告

导入matplotlib模块和pandas模块,读到之前预测生成的csv文件,获取标签:

# csv文件地址
data_path = './data_pred_result/result.csv'
# 读取csv数据
data = pd.read_csv(data_path)
# 获取真实标签
true_label = data["label"].values
# 获取预测标签
pred_label = data["pred"].values

接着导入sklearn.metrics模块生成分类报告:

    # 生成分类报告report = classification_report(true_label, pred_label)print(report)acc = accuracy_score(true_label, pred_label)precision = precision_score(true_label, pred_label, average='macro')recall = recall_score(true_label, pred_label, average='macro')f1 = f1_score(true_label, pred_label, average='macro')print(f"准确度:{acc:.6f}\n"f"精确度:{precision:.6f}\n",f"召回率:{recall:.6f}\n",f"f1:{f1:.6f}\n")

用matlplotlib生成混淆矩阵(由于原始数据的混淆矩阵太大,500分类即500*500矩阵,这里只生成前十分类的混淆矩阵作为演示):

def vision_martix(true_label, pred_label, num_classes=10):labels = [x for x in range(num_classes)]matrix = confusion_matrix(true_label, pred_label)print(matrix)plt.figure(figsize=(8, 8))plt.matshow(matrix[:10, :10], cmap=plt.cm.Greens, fignum=1)# 显示颜色条plt.colorbar()# 显示具体的数字的过程for i in range(len(matrix)):for j in range(len(matrix)):plt.annotate(matrix[i, j],xy=(j, i),horizontalalignment="center",verticalalignment="center",)# 美化的东西plt.xlabel("Pred labels")plt.ylabel("True labels")plt.xticks(range(len(labels)), labels, rotation=45)plt.yticks(range(len(labels)), labels)plt.title("训练结果混淆矩阵视图")plt.show()

效果:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

5.模型应用

首先读取图片,这里我导入了opencv模块进行读图,并且将读取到的图片进行预处理操作:

    # 读图处理img = cv.imread(img_path)img = cv.resize(img, (224, 224))transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5))])img = transform(img)img = torch.unsqueeze(img, 0).to(device)

加载训练好的模型:

	    net = googlenet(pretrained=False)net.fc = nn.Linear(net.fc.in_features, 500)net.load_state_dict(torch.load("./model/model.pth"))net.to(device)

进行预测:

    # 预测net.eval()output = net(img)output = torch.argmax(output, dim=1)print(output)

可以用Imagefolder导入训练集或输出集获取标签映射表,将标签映射转换为下标映射:

idx_to_class = {v: k for k, v in data.class_to_idx.items()}
print(idx_to_class[pred])

最后得到结果:

在这里插入图片描述

6.模型迁移

6.1导出onnx模型

首先需要提前在环境内安装依赖包

pip install onnx
pip install onnxruntime

导入训练好的模型:

# 导入训练好的模型
net = googlenet(pretrained=False)
net.fc = nn.Linear(in_features=net.fc.in_features, out_features=500)
weight_path = "./model/model.pth"
net.load_state_dict(torch.load(weight_path))

创建一个实例输入:

x = torch.randn(1, 3, 224, 224)

用torch.onnx.export导出onnx:

torch.onnx.export(net,x,onnx_path,verbose=True,input_names=['input'],output_names=['output']
)

6.2使用onnx推理

加载onnx模型:

# 加载onnx模型
onnx_path = './model_onnx/model.onnx'
session = rt.InferenceSession(onnx_path)

对要进行推理的图片进行预处理:

def read_img(path):# 处理输入图片img = cv.imread(path)img = cv.resize(img, (224, 224))img = img.transpose((2, 0, 1))img = img.astype(np.float32) / 255.0  # 代替Totensor()操作img = np.expand_dims(img, axis=0)return imgimg_path = "./apply/1.jpg"
img1 = read_img(img_path)

进行推理:

# 获取输入输出名
input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].name# 推理
result = session.run([output_name], {input_name: img1})

得到结果并且将结果从下标值映射为标签名:

# 取出分类标签下标
result = np.argmax(result[0], axis=1)
print(result)# 读取保存好的标签csv文件建立 index->label 映射表
df = pd.read_csv("labels.csv")
idx2label = dict(zip(df["index"], df["label"]))
print(f"img的分类为:{idx2label[result.item()]}")

项目源代码仓库:卷积神经网络项目实战: 卷积神经网络个人实战项目

注意:训练集以及生成文件夹需要自己手动创建,具体文件夹已在file_expression文档中说明。

ply/1.jpg"
img1 = read_img(img_path)


进行推理:```python
# 获取输入输出名
input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].name# 推理
result = session.run([output_name], {input_name: img1})

得到结果并且将结果从下标值映射为标签名:

# 取出分类标签下标
result = np.argmax(result[0], axis=1)
print(result)# 读取保存好的标签csv文件建立 index->label 映射表
df = pd.read_csv("labels.csv")
idx2label = dict(zip(df["index"], df["label"]))
print(f"img的分类为:{idx2label[result.item()]}")

项目源代码仓库:卷积神经网络项目实战: 卷积神经网络个人实战项目

注意:训练集以及生成文件夹需要自己手动创建,具体文件夹已在file_expression文档中说明。

如果博主的文档和源代码对你有所帮助,也麻烦给博主点个收藏和赞噢,谢谢☆噜~☆

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/94699.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/94699.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网站如何被搜索引擎收录(Google、Bing、百度等)

1. 【Google 收录】注册 Google Search Console: https://search.google.com/search-console添加网站(主域名、子域名都加)验证所有权(用 DNS、HTML 文件、Meta Tag 都可以)提交 Sitemap(/sitemap.xml&…

JDK 8 → JDK 17 升级说明书(面向 Spring Boot / Spring Cloud / Spring )

自从 JDK 8 发布以来,Java 语言在持续进化,带来了许多新的功能和性能改进。而 JDK 17 作为一个长期支持版本(LTS),在许多方面超越了 JDK 8,不仅提升了语言本身的能力,还进一步提高了性能、可维护…

【ElasticSearch】使用docker compose,通过编写yml安装es8.15和kibana可视化界面操作,go连接es

使用 Docker 安装 Elasticsearch Docker 搭建 Elasticsearch Kibana 环境,并在过程中标注常见问题和解决方案。 1. 准备工作 在开始之前,请确认你本地已经安装了: 工具版本建议检查方式Docker≥ 20.xdocker -vDocker Compose≥ 2.xdocker …

《C 语言文件操作补充:字符串格式化与随机读写全解析》

目录 一. sprintf函数和sscanf函数 1.1 sprintf 函数:将格式化数据写入字符串 1.2 sscanf 函数:从字符串中格式化读取数据 二. 文件的随机读写 2.1 fseek 函数:移动文件读写指针 2.2 ftell 函数:获取当前指针位置 2.3 rew…

SOME/IP-SD报文中 Entry Format(条目格式)-理解笔记4

逐字段解析 AUTOSAR SOME/IP Service Discovery 中的 Entry 格式。我们将它拆解成几个部分,并用清晰的排版和比喻来确保每个字段都得到解释。📜 Entry 的完整结构:三层蛋糕 一条完整的 SD Entry 信息就像一块三层蛋糕,从上到下分别…

在 vue3 和 vue2 中,computed 计算属性和 methods 方法区别是什么

在 Vue 2 和 Vue 3 中,computed(计算属性)和 methods(方法)都是处理数据逻辑的方式,但它们在缓存机制、使用场景、执行时机等方面有显著区别,且这些区别在两个版本中保持一致。 1. 缓存机制&…

android 改机系列之-虚拟摄像头-替换相机预览画面

Android Native 层实现跨进程 YUV 视频帧共享:基于抽象 Unix Socket 的高效通信方案。基于AOSP13源码或者lineage20 或相近版本。非hook 或者lsp 等插件方案。 1.引言 在某些定制化 Android 应用场景中,我们可能需要动态替换系统相机的预览画面 —— 例…

SSM从入门到实战:2.5 SQL映射文件与动态SQL

👋 大家好,我是 阿问学长!专注于分享优质开源项目解析、毕业设计项目指导支持、幼小初高的教辅资料推荐等,欢迎关注交流!🚀 12-SQL映射文件与动态SQL 📖 本文概述 本文是SSM框架系列MyBatis进…

vue+vite打包后的文件希望放在一个子目录下

比如我们常规操作是打包的项目文件直接放在域名下面。如果我们希望把项目放在子域名下面应该怎么处理呢?需要两个步骤vite.config.js里面指定base的路径假设我们希望放在子目录加做call那么我们可以这样base:/call/,注意不是build目录哈。return的最外层。如果本地和…

Java:Docx4j类库简介及使用

1.简介 Docx4j 是一个功能强大的 Java 类库,专门用于创建和操作 Microsoft Open XML 格式(如 Word DOCX、PowerPoint PPTX 和 Excel XLSX)的文件。它深受 Java 开发者喜爱,特别是在需要自动化处理 Office 文档的场景下。 下面是一…

【机械故障】旋转机械故障引起的振动信号调制效应概述

系列文章目录 提示:学习笔记 机械故障信号分析 共振峰 旋转机械故障引起的振动信号调制效应概述系列文章目录一、研究背景与意义二、故障引起的调制效应分类三、非平稳信号分析方法3.1 时频分析方法3.2 信号分解方法一、研究背景与意义 在工程实践中,可…

密码安全隐形基石:随机数、熵源与DRBG核心解析与技术关联

前言:密码安全的 “隐形基石” 在数字化浪潮席卷全球的今天,从金融交易的密钥生成到区块链的共识机制,从量子通信的加密协议到智能汽车的身份认证,随机数如同空气般渗透在信息系统的每一个安全节点。然而,看似简单的 …

Vue3 + Element Plus实现表格多行文本截断与智能Tooltip提示

在实际开发中,我们经常需要在表格中展示较长的文本内容,但又希望保持界面的整洁美观。本文将介绍如何在Vue3 和 Element Plus中实现表格多行文本截断,并智能控制Tooltip的显示——只有当文本被截断时才显示Tooltip,否则不显示。 需…

使用powerquery处理数据,取时间或者日期之前的

Table.AddColumn(#"已更改列类型 1", "自定义 (2)", each letcleanText Text.Replace([备注], "#(lf)", " "),hasTime Text.Contains(cleanText, "时间:"),hasDate Text.Contains(cleanText, "日期&…

Java面试全栈技术解析:从Spring Cloud到Kafka的实战演练

面试官:请简单介绍一下Spring Cloud的核心组件? 谢飞机:嗯...Spring Cloud主要是基于Spring Boot的,然后有Eureka做服务发现,Feign做声明式REST调用,还有Config做配置中心... 面试官:那在电商场…

极简 useState:手写 20 行,支持多次 setState 合并

不依赖 React,用 闭包 批处理队列 实现可合并更新的 useState。一、20 行完整代码 function createUseState(initialValue) {let state initialValue;let pending null; // 合并队列let listeners [];const flush () > {if (pending ! null) {…

LabVIEW Vision视觉引导撑簧圈智能插装

为解决人工插装连接器撑簧圈时劳动强度大、效率低、一致性差的问题,例以 LabVIEW为开发平台,结合 IMAQ Vision 机器视觉库,搭配精密硬件搭建智能插装系统。系统可适配 9 芯、13 芯、25 芯、66 芯、128 芯 5 种规格工件,经 100 只产…

【Lua】题目小练11

-- 题目1:-- 给定表 t {"apple", "banana", "apple", "orange", "banana", "apple"}-- 写一个函数 countFreq(tbl) 返回一个新表,统计每个元素出现次数-- 例如:返回 {apple3, …

ElementUI之菜单(Menu)使用

文章目录项目创建创建项目运行项目整理目录删除src/assets中的所有logo.png删除src/components中的所有文件修改src/route/index.js删除src/views中所有文件修改src/app.vue整理完目录如下引入ElementUI安装ElementUI引入ElementUI测试是否安装成功编写src/app.vue运行结果编写…

Python训练营打卡Day44-通道注意力(SE注意力)

知识点回顾: 不同CNN层的特征图:不同通道的特征图什么是注意力:注意力家族,类似于动物园,都是不同的模块,好不好试了才知道。通道注意力:模型的定义和插入的位置通道注意力后的特征图和热力图 内…