当前位置: 首页 > news >正文

【霹雳吧啦】手把手带你入门语义分割の番外12:U2-Net 源码讲解(PyTorch)—— 网络的搭建

目录

前言

Preparation

一、U2-Net 网络结构图

二、U2-Net 网络源代码

1、model.py

(1)ConvBNReLU 类

(2)DownConvBNReLU 类

(3)UpConvBNReLU 类

(4)RSU 类 & RSU4F 类

(5)U2Net 类

(6)model.py 源代码


前言

文章性质:学习笔记 📖

视频教程:U2-Net 源码解析(Pytorch)- 2 网络的搭建

主要内容:根据 视频教程 中提供的 U2-Net 源代码(PyTorch),对 model.py 文件进行具体讲解。

Preparation

源代码:https://github.com/WZMIAOMIAO/deep-learning-for-image-processing/tree/master/pytorch_segmentation/u2net

├── src: 搭建网络相关代码
├── train_utils: 训练以及验证相关代码
├── my_dataset.py: 自定义数据集读取相关代码
├── predict.py: 简易的预测代码
├── train.py: 单GPU或CPU训练代码
├── train_multi_GPU.py: 多GPU并行训练代码
├── validation.py: 单独验证模型相关代码
├── transforms.py: 数据预处理相关代码
└── requirements.txt: 项目依赖

一、U2-Net 网络结构图

原论文提供的 U2-Net 网络结构图如下所示: 

【说明】在 Encoder 阶段,每通过一个 block 后都下采样 2 倍(maxpool),在 Decoder 阶段,每通过一个 block 后都上采样 2 倍(bilinear)。U2-Net 网络的核心 block 是 ReSidual U-block,分为具备上下采样的 block 和不具备上下采样的 block(Encoder5、Encoder6、Decoder5)。

二、U2-Net 网络源代码

1、model.py

(1)ConvBNReLU 类

这个 ConvBNReLU 类继承自 nn.Module 父类,传入了输入通道数 in_ch、输出通道数 out_ch、卷积核大小 kernel_size 和膨胀因子 dilation 等参数,当 dilation 设置为 1 时,代表当前这个卷积层是普通卷积,当 dilation 大于 1 时,代表当前这个卷积层是膨胀卷积。

【代码解析】对 ConvBNReLU 类代码进行具体讲解(结合上图):

  1.  根据传入的 kernel_size 和 dilation 计算对应的 padding
  2.  分别使用 Conv2d、BatchNorm2d、ReLU 实例化卷积层、BN 层、ReLU 层
  3.  定义前向传播函数,使得输入张量 x 依次通过卷积层、BN 层、ReLU 层

(2)DownConvBNReLU 类

这个 DownConvBNReLU 类继承自 ConvBNReLU 类。

【说明】Encoder 部分:使用 F.max_pool2d 方法进行下采样,再依次通过 Conv2d 层、BN 层、ReLU 层。 

(3)UpConvBNReLU 类

这个 UpConvBNReLU 类继承自 ConvBNReLU 类。

【说明】Decoder 部分:使用 F.interpolate 中的双线性插值方法进行上采样,这里注意,经过双线性插值法后,x2 的宽高将与 x1 的宽高相同,再用 torch.cat 将 Encoder 中的 x2 与 Decoder 中的 x1 连接,最后依次通过 Conv2d 层、BN 层、ReLU 层。

(4)RSU 类 & RSU4F 类

在之前的文章中已经具体介绍过 U2-Net 的两种 block 结构:Encoder1~Encoder4 与 Decoder1~Decoder4 采用的是同一种结构的 block ,只不过深度不同。Encoder5、Encoder6、Decoder5 采用的是另一种结构的 block 。 我们先来简单回忆下第一种 block 结构:

  • U2-Net 网络结构中的 Encoder1 和 Decoder1 采用的是 RSU-7 结构
  • U2-Net 网络结构中的 Encoder2 和 Decoder2 采用的是 RSU-6 结构
  • U2-Net 网络结构中的 Encoder3 和 Decoder3 采用的是 RSU-5 结构
  • U2-Net 网络结构中的 Encoder4 和 Decoder4 采用的是 RSU-4 结构

【说明】相邻 block 相差一次下采样和一次上采样,例如 RSU-6 相比于 RSU-7 少了一个下采样卷积和上采样卷积部分,RSU-7 是下采样 32 倍和上采样 32 倍,RSU-6 是下采样 16 倍和上采样 16 倍。

再来回忆第二种 block 结构,U2-Net 中的 Encoder5、Encoder6、Decoder5 采用的都是 RSU-4F 结构,注意 RSU-4F 与 RSU-4 的结构并不相同,在 RSU-4F 中未进行下采样和上采样,而是将 上下采样 全部替换成了 膨胀卷积 ,整个过程中特征图的宽高不变。

下面是 RSU 类和 RSU4F 类的代码截图,在 RSU 类的初始化 __init__ 方法中,传入的 height 参数是指 RSU 结构的深度。

(5)U2Net 类

这个 U2Net 类继承自 nn.Module 父类,在其初始化 __init__ 方法中,传入参数包括 cfg ,而在我们搭建 U2-Net 标准版以及轻量级的版本时,都会传入 cfg ,分别可以在 u2net_full 和 u2net_lite 函数中查看:

除此之外,在 U2-Net 中,默认是针对显著性目标检测任务去做的,只区分前景和背景,因此整个网络最终输出的预测概率图的通道数 out_ch 将设置为 1 ,也就是说预测的每一个像素的概率分数都是从 0 到 1 的,趋于 1 则说明代表二维前景的概率大,趋于 0 则说明代表背景的概率大。 

【说明】经过 concat 拼接后,将得到的特征图通过一个 1x1 的卷积层,融合来自不同尺度的信息,最终得到只有一个通道的特征图。

【说明】使用 decode_outputs.insert(0, x) 将处理后的结果 x 插入到 decode_outputs 列表的第一个位置,以 保持解码器输出的顺序

(6)model.py 源代码

from typing import Union, List
import torch
import torch.nn as nn
import torch.nn.functional as Fclass ConvBNReLU(nn.Module):def __init__(self, in_ch: int, out_ch: int, kernel_size: int = 3, dilation: int = 1):super().__init__()padding = kernel_size // 2 if dilation == 1 else dilationself.conv = nn.Conv2d(in_ch, out_ch, kernel_size, padding=padding, dilation=dilation, bias=False)self.bn = nn.BatchNorm2d(out_ch)self.relu = nn.ReLU(inplace=True)def forward(self, x: torch.Tensor) -> torch.Tensor:return self.relu(self.bn(self.conv(x)))class DownConvBNReLU(ConvBNReLU):def __init__(self, in_ch: int, out_ch: int, kernel_size: int = 3, dilation: int = 1, flag: bool = True):super().__init__(in_ch, out_ch, kernel_size, dilation)self.down_flag = flagdef forward(self, x: torch.Tensor) -> torch.Tensor:if self.down_flag:x = F.max_pool2d(x, kernel_size=2, stride=2, ceil_mode=True)return self.relu(self.bn(self.conv(x)))class UpConvBNReLU(ConvBNReLU):def __init__(self, in_ch: int, out_ch: int, kernel_size: int = 3, dilation: int = 1, flag: bool = True):super().__init__(in_ch, out_ch, kernel_size, dilation)self.up_flag = flagdef forward(self, x1: torch.Tensor, x2: torch.Tensor) -> torch.Tensor:if self.up_flag:x1 = F.interpolate(x1, size=x2.shape[2:], mode='bilinear', align_corners=False)return self.relu(self.bn(self.conv(torch.cat([x1, x2], dim=1))))class RSU(nn.Module):def __init__(self, height: int, in_ch: int, mid_ch: int, out_ch: int):super().__init__()assert height >= 2self.conv_in = ConvBNReLU(in_ch, out_ch)encode_list = [DownConvBNReLU(out_ch, mid_ch, flag=False)]decode_list = [UpConvBNReLU(mid_ch * 2, mid_ch, flag=False)]for i in range(height - 2):encode_list.append(DownConvBNReLU(mid_ch, mid_ch))decode_list.append(UpConvBNReLU(mid_ch * 2, mid_ch if i < height - 3 else out_ch))encode_list.append(ConvBNReLU(mid_ch, mid_ch, dilation=2))self.encode_modules = nn.ModuleList(encode_list)self.decode_modules = nn.ModuleList(decode_list)def forward(self, x: torch.Tensor) -> torch.Tensor:x_in = self.conv_in(x)x = x_inencode_outputs = []for m in self.encode_modules:x = m(x)encode_outputs.append(x)x = encode_outputs.pop()for m in self.decode_modules:x2 = encode_outputs.pop()x = m(x, x2)return x + x_inclass RSU4F(nn.Module):def __init__(self, in_ch: int, mid_ch: int, out_ch: int):super().__init__()self.conv_in = ConvBNReLU(in_ch, out_ch)self.encode_modules = nn.ModuleList([ConvBNReLU(out_ch, mid_ch),ConvBNReLU(mid_ch, mid_ch, dilation=2),ConvBNReLU(mid_ch, mid_ch, dilation=4),ConvBNReLU(mid_ch, mid_ch, dilation=8)])self.decode_modules = nn.ModuleList([ConvBNReLU(mid_ch * 2, mid_ch, dilation=4),ConvBNReLU(mid_ch * 2, mid_ch, dilation=2),ConvBNReLU(mid_ch * 2, out_ch)])def forward(self, x: torch.Tensor) -> torch.Tensor:x_in = self.conv_in(x)x = x_inencode_outputs = []for m in self.encode_modules:x = m(x)encode_outputs.append(x)x = encode_outputs.pop()for m in self.decode_modules:x2 = encode_outputs.pop()x = m(torch.cat([x, x2], dim=1))return x + x_inclass U2Net(nn.Module):def __init__(self, cfg: dict, out_ch: int = 1):super().__init__()assert "encode" in cfgassert "decode" in cfgself.encode_num = len(cfg["encode"])encode_list = []side_list = []for c in cfg["encode"]:# c: [height, in_ch, mid_ch, out_ch, RSU4F, side]assert len(c) == 6encode_list.append(RSU(*c[:4]) if c[4] is False else RSU4F(*c[1:4]))if c[5] is True:side_list.append(nn.Conv2d(c[3], out_ch, kernel_size=3, padding=1))self.encode_modules = nn.ModuleList(encode_list)decode_list = []for c in cfg["decode"]:# c: [height, in_ch, mid_ch, out_ch, RSU4F, side]assert len(c) == 6decode_list.append(RSU(*c[:4]) if c[4] is False else RSU4F(*c[1:4]))if c[5] is True:side_list.append(nn.Conv2d(c[3], out_ch, kernel_size=3, padding=1))self.decode_modules = nn.ModuleList(decode_list)self.side_modules = nn.ModuleList(side_list)self.out_conv = nn.Conv2d(self.encode_num * out_ch, out_ch, kernel_size=1)def forward(self, x: torch.Tensor) -> Union[torch.Tensor, List[torch.Tensor]]:_, _, h, w = x.shape# collect encode outputsencode_outputs = []for i, m in enumerate(self.encode_modules):x = m(x)encode_outputs.append(x)if i != self.encode_num - 1:x = F.max_pool2d(x, kernel_size=2, stride=2, ceil_mode=True)# collect decode outputsx = encode_outputs.pop()decode_outputs = [x]for m in self.decode_modules:x2 = encode_outputs.pop()x = F.interpolate(x, size=x2.shape[2:], mode='bilinear', align_corners=False)x = m(torch.concat([x, x2], dim=1))decode_outputs.insert(0, x)# collect side outputsside_outputs = []for m in self.side_modules:x = decode_outputs.pop()x = F.interpolate(m(x), size=[h, w], mode='bilinear', align_corners=False)side_outputs.insert(0, x)x = self.out_conv(torch.concat(side_outputs, dim=1))if self.training:# do not use torch.sigmoid for amp safereturn [x] + side_outputselse:return torch.sigmoid(x)def u2net_full(out_ch: int = 1):cfg = {# height, in_ch, mid_ch, out_ch, RSU4F, side"encode": [[7, 3, 32, 64, False, False],      # En1[6, 64, 32, 128, False, False],    # En2[5, 128, 64, 256, False, False],   # En3[4, 256, 128, 512, False, False],  # En4[4, 512, 256, 512, True, False],   # En5[4, 512, 256, 512, True, True]],   # En6# height, in_ch, mid_ch, out_ch, RSU4F, side"decode": [[4, 1024, 256, 512, True, True],   # De5[4, 1024, 128, 256, False, True],  # De4[5, 512, 64, 128, False, True],    # De3[6, 256, 32, 64, False, True],     # De2[7, 128, 16, 64, False, True]]     # De1}return U2Net(cfg, out_ch)def u2net_lite(out_ch: int = 1):cfg = {# height, in_ch, mid_ch, out_ch, RSU4F, side"encode": [[7, 3, 16, 64, False, False],  # En1[6, 64, 16, 64, False, False],  # En2[5, 64, 16, 64, False, False],  # En3[4, 64, 16, 64, False, False],  # En4[4, 64, 16, 64, True, False],  # En5[4, 64, 16, 64, True, True]],  # En6# height, in_ch, mid_ch, out_ch, RSU4F, side"decode": [[4, 128, 16, 64, True, True],  # De5[4, 128, 16, 64, False, True],  # De4[5, 128, 16, 64, False, True],  # De3[6, 128, 16, 64, False, True],  # De2[7, 128, 16, 64, False, True]]  # De1}return U2Net(cfg, out_ch)def convert_onnx(m, save_path):m.eval()x = torch.rand(1, 3, 288, 288, requires_grad=True)# export the modeltorch.onnx.export(m,  # model being runx,  # model input (or a tuple for multiple inputs)save_path,  # where to save the model (can be a file or file-like object)export_params=True,opset_version=11)if __name__ == '__main__':# n_m = RSU(height=7, in_ch=3, mid_ch=12, out_ch=3)# convert_onnx(n_m, "RSU7.onnx")## n_m = RSU4F(in_ch=3, mid_ch=12, out_ch=3)# convert_onnx(n_m, "RSU4F.onnx")u2net = u2net_full()convert_onnx(u2net, "u2net_full.onnx")

相关文章:

【霹雳吧啦】手把手带你入门语义分割の番外12:U2-Net 源码讲解(PyTorch)—— 网络的搭建

目录 前言 Preparation 一、U2-Net 网络结构图 二、U2-Net 网络源代码 1、model.py &#xff08;1&#xff09;ConvBNReLU 类 &#xff08;2&#xff09;DownConvBNReLU 类 &#xff08;3&#xff09;UpConvBNReLU 类 &#xff08;4&#xff09;RSU 类 & RSU4F 类…...

phpstudy面板Table ‘mysql.proc‘ doesn‘t exist解决办法

原因分析&#xff1a;误删了mysql数据库 解决办法如下&#xff1a; 1、停止服务 2、先把mysql文件夹下的data文件夹备份&#xff0c;因为data文件里存有数据库文件。然后再删除data文件。 3、cmd管理员命令进入到mysql中的bin目录下 &#xff0c;执行mysqld --initialize-…...

网安入门09-Sql注入(绕过方法梳理)

ByPass SQL注入ByPass是指攻击者通过各种手段绕过应用程序中已经实施的SQL注入防御措施&#xff0c;例如输入恶意数据、修改请求头等方式&#xff0c;绕过过滤、转义、限制等操作&#xff0c;从而成功地执行恶意SQL语句。攻击者使用SQL注入ByPass技术可以让应用程序的防御措施…...

本地计算机 上的 My5OL808 服务启动后停止,某些服务在未由其他服务或程序使用时将自动停止

客户反馈说mysql启动不了&#xff0c;报错信息&#xff1a; 本地计算机 上的 My5OL808 服务启动后停止&#xff0c;某些服务在未由其他服务或程序使用时将自动停止。 查了不少资料&#xff0c;最后分析问题是这样的&#xff0c;手动或者重复安装mysql时&#xff0c;创建了多个…...

2023机器人行业总结,2024机器人崛起元年(具身智能)

2023总结&#xff1a; 1.Chatgpt引爆了通用人工智能&#xff0c;最大的受益者或是机器人&#xff0c;2023年最热门的创业赛道便是人形机器人&#xff0c;优必选更是成为人形机器人上市第一股&#xff0c; 可以说2023年是机器人开启智能化的元年&#xff0c;而2024则将成为机器…...

go 语言中的类型判断

_. ok : interface{}(a).(B)此语句用于判断对象a是否是B类型 也可以判断对象a是否实现了B接口 package mainimport "fmt"type Pet interface {SetName(name string)Name() stringCategory() string } type Dog struct {name string }func (dog *Dog) SetName(name …...

java基于ssm的房源管理系统+vue论文

目 录 目 录 I 摘 要 III ABSTRACT IV 1 绪论 1 1.1 课题背景 1 1.2 研究现状 1 1.3 研究内容 2 2 系统开发环境 3 2.1 vue技术 3 2.2 JAVA技术 3 2.3 MYSQL数据库 3 2.4 B/S结构 4 2.5 SSM框架技术 4 3 系统分析 5 3.1 可行性分析 5 3.1.1 技术可行性 5 3.1.2 操作可行性 5 3…...

RH850P1X芯片学习笔记-A/D Converter (ADCF)

文章目录 Features of RH850/P1x-C ADCFNumber of UnitsRegister Base AddressClock SupplyInterrupts and DMAHardware ResetExternal Input/Output SignalsVirtual Channel OverviewFunctional OverviewBlock DiagramPhysical Channels, Virtual Channels and Scan Groups Re…...

38 调优kafka

操作系统调优 1.禁止atime更新&#xff0c;减少文件系统的写操作。 mount -o noatime 2.选择高性能的文件系统&#xff0c;如ext4或者XFS 3.swap空间设置&#xff0c;将swappniness设置成很小的一个值比如1&#xff5e;10&#xff0c;防止linux OOM Killer 开启随意杀掉进程。…...

java推荐系统:好友推荐思路

1.表的设计 表里面就两个字段&#xff0c;一个字段是用户id&#xff0c;另外一个字段是好友id&#xff0c;假如A跟B互为好友&#xff0c;那在数据库里面就会有两条数据 2.推荐好友思路 上面的图的意思是&#xff1a;h跟a的互为好友&#xff0c;a跟b&#xff0c;c&am…...

java: 写入数据到HBase

一、添加依赖 <dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-client</artifactId><version>2.6.0</version></dependency><dependency><groupId>org.apache.hbase</groupId><art…...

机器学习-基于Word2vec搜狐新闻文本分类实验

机器学习-基于Word2vec搜狐新闻文本分类实验 实验介绍 Word2vec是一群用来产生词向量的相关模型&#xff0c;由Google公司在2013年开放。Word2vec可以根据给定的语料库&#xff0c;通过优化后的训练模型快速有效地将一个词语表达成向量形式&#xff0c;为自然语言处理领域的应…...

5.vue学习笔记(数组变化的侦测+计算属性+Class绑定)

文章目录 1.数组变化的侦测1.1.变更方法1.2.替换一个数组 2.计算属性计算属性缓存vs方法 3.Class绑定3.1.绑定对象3.2.多个对象的绑定形式3.3.绑定数组3.4.数组与对象 1.数组变化的侦测 1.1.变更方法 vue能够侦听响应式数组的变更方法&#xff0c;并在它们被调用时出发相关的…...

Java十种经典排序算法详解与应用

数组的排序 前言 排序概念 排序是将一组数据&#xff0c;依据指定的顺序进行排列的过程。 排序是算法中的一部分&#xff0c;也叫排序算法。算法处理数据&#xff0c;而数据的处理最好是要找到他们的规律&#xff0c;这个规律中有很大一部分就是要进行排序&#xff0c;所以需…...

git常用命令及概念对比

查看日志 git config --list 查看git的配置 git status 查看暂存区和工作区的变化内容&#xff08;查看工作区和暂存区有哪些修改&#xff09; git log 查看当前分支的commit 记录 git log -p commitID详细查看commitID的具体内容 git log -L :funcName:fileName 查看file…...

57、python 环境搭建[for 计算机视觉从入门到调优项目]

从本节开始,进入到代码实战部分,在开始之前,先简单进行一下说明。 代码实战部分,我会默认大家有一定的编程基础,不需要对编程很精通,但是至少要会 python 的基础语法、python 环境搭建、pip 的使用;C++ 要熟悉基础知识和基础语法,会根据文章中的步骤完成 C++ 的环境搭…...

K8S-应用访问

1 service对象定位 2 Service 实践 手工创建Service 根据应用部署资源对象&#xff0c;创建SVC对象 kubectl expose deployment nginx --port80 --typeNodePortyaml方式创建Service nginx-web的service资源清单文件 apiVersion: v1 kind: Service metadata:name: sswang-ngi…...

商智C店H5性能优化实战

前言 商智C店&#xff0c;是依托移动低码能力搭建的一个应用&#xff0c;产品面向B端商家。随着应用体量持续增大&#xff0c;考虑产品定位及用户体验&#xff0c;我们针对性能较差页面做了一次优化&#xff0c;并取得了不错的效果&#xff0c;用户体验值&#xff08;UEI&…...

Unity 使用 Plastic 同步后,正常工程出现错误

class Newtonsoft.Json.Linq.JToken e CS0433:类型"JToken"同时存在于"Newtonsoft.Json.Net20,Version3.5.0.0,Cultureneutral,,PublicKeyToken30ad4fe6b2a6aeed"和"Newtonsoft.Json, Version12.0.0.0,Cultureneutral,PublicKeyToken30ad4fe6b2a6aeed…...

详细设计文档该怎么写

详细设计文档是软件开发过程中的一个关键阶段&#xff0c;它为每个软件模块的实现提供了详细说明。这份文档通常在概要设计阶段之后编写&#xff0c;目的是指导开发人员如何具体实现软件的功能。以下是撰写详细设计文档的步骤和一些示例&#xff1a; 步骤和组成部分 引言 目的…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...