news 2026/10/19 2:49:17

BoTNet+YOLO:用全局自注意力提升目标检测性能

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
BoTNet+YOLO:用全局自注意力提升目标检测性能

1. 为什么是Bottleneck Transformer:CVPR 2021 BoTNet背后的直觉

1.1 卷积堆叠的天花板:效率有余,全局性不足

先聊一个很多人忽略的事实:目标检测网络的主干部分,长期被ResNet、CSPDarknet这类纯卷积结构统治,并不是因为卷积完美,而是因为它在「计算效率」和「归纳偏置」之间找到了一个平衡点。卷积的局部感受野让它天然擅长捕捉边缘、纹理、局部形状,参数共享也让它在中小规模数据集上不容易过拟合。但一旦目标尺度变化剧烈,或者目标的上下文信息非常关键,纯卷积堆叠就暴露出问题:高层特征虽然感受野变大了,但那是靠层数堆出来的,不是真正意义上的全局建模。

我当时在YOLO26上做改进时,第一直觉其实是继续堆卷积、调通道数、加深neck,但实验做完以后发现:参数量涨了,mAP却卡在一个值上不动了。这说明问题不在容量,而在表示能力的天花板。后来翻到CVPR 2021那篇Bottleneck Transformers的工作,它的核心主张特别打动我:与其把卷积和自注意力对立起来,不如把自注意力嵌入到卷积模块的瓶颈区域,用最小的结构改动换来真正的全局感受野。

1.2 自注意力补的不是参数,是依赖关系

自注意力机制真正值钱的地方,在于它对「长距离依赖」的建模能力。卷积需要堆几十层才能让两个相距较远的像素互相影响,而自注意力一步到位,任何两个位置之间都能直接建立关联。这个能力放在目标检测里意味着什么?我举个例子:一张街景图里,远处一辆被路灯杆挡住一半的自行车,纯卷积网络很容易误判成“行人”或“未知物体”,因为局部特征被遮挡破坏了;但有了全局注意力,模型可以看到自行车轮、车座、以及旁边非机动车道的语义线索,综合判断出这是自行车。

不过说实话,自注意力直接替换掉卷积也不现实。全局自注意力的计算复杂度是O(n²),在检测任务的高分辨率特征图上直接上Transformer,显存会爆炸,训练速度也会慢到让人怀疑人生。BoTNet的聪明之处在于:它只在特征图分辨率较低的Stage 4、Stage 5区域使用多头自注意力,而且把全局自注意力限制在单个特征图的内部,不做跨尺度交互。这样既拿到了全局建模能力,又把计算开销控制在可接受的范围内。我在实际改造中特别看重这一点,因为它意味着可以保留YOLO26原本的CSP结构、PANet neck、Decoupled head不动,只在主干改造,风险小、可回退。

1.3 相对位置编码:BoTNet里最不起眼但最关键的细节

很多人在复现BoTNet的时候只盯着MHSA多头自注意力,却忽略了相对位置编码的作用。实际上,如果你直接套用ViT那种绝对位置编码,在目标检测场景里会遇到一个尴尬的问题:位置编码是固定的,但检测模型的输入分辨率并不固定(通常会在训练时做多尺度随机采样)。固定位置编码在训练分辨率上表现正常,但测试时如果分辨率变了,位置编码和feature map对不上,性能会掉得很难看。

BoTNet的做法是在注意力权重计算时,直接加上相对位置偏置项,这个偏置只依赖于两个像素之间的相对偏移量,与绝对坐标无关。所以不管输入尺寸怎么变,模型都能稳定工作。我当时看论文到这一节的时候专门做了个小实验:把相对位置编码去掉、换成零初始化偏置,其他全部保持一致,训练同样的epoch,最终mAP掉了接近1.8个百分点。这个数字足够说明问题了——位置信息不是锦上添花,而是自注意力真正发挥作用的必需品。

2. 模块内部拆解:从Bottleneck到MHSA的完整数据流

2.1 结构设计的核心:把注意力塞进瓶颈区间

很多初看BoTNet的人会以为它是一个「并联」结构——卷积一路、注意力一路,最后加在一起。实际上它是「串联」结构:在Bottleneck的3x3卷积位置,直接替换成了全局多头自注意力。这个设计初看有些反直觉,因为传统经验里,Bottleneck中的3x3卷积是用来做局部特征交互的,替换成全局注意力会不会太冗余?但仔细看作者的解释就明白了:前一个1x1卷积把通道数降下来(比如从C降到C/4),注意力在低维空间做全局建模,后一个1x1卷积再把通道升回去。这本质上是一个降维建模再升维复用的结构,既控制了计算量,又让注意力的全局建模建立在更加紧凑、更有判别性的特征之上。

从数据流的角度来看,输入一个张量形状是(B, C, H, W)的特征图,先经过第一个1x1卷积,变成(B, C/4, H, W),再经过MHSA模块,输出形状依然是(B, C/4, H, W),最后经过第二个1x1卷积恢复到(B, C, H, W),再接残差连接。整个过程只有MHSA内部涉及维度变换,外部看就是一个标准的Bottleneck替换。

2.2 多头自注意力的关键参数和复杂度分析

MHSA模块里有两个关键参数:heads(头数)和key_dim(每个头的维度)。在原论文实现中,BoTNet的head数默认配置为4,key维度通常设置为通道数除以头数。以输入通道C=128为例,降维后C/4=32,那么每个head的key维度就是32/4=8。注意力计算时,每个头独立计算Query、Key、Value的线性投影,然后算点积相似度,softmax归一化得到注意力权重,最后加权Value并concat所有头的输出。

这里有一个重要的操作细节:在BoTNet中,Query的投影会使用1x1卷积完成,而Key和Value的投影会使用3x3卷积完成。这背后有一个有意思的动机——用卷积做投影,等于在投影过程中同时引入了一点局部邻域的信息,相当于给注意力加了一点「卷积先验」。论文里的解释是这是借鉴了Attention Augmented Convolution的做法,但实际上这个设计还有一个好处:实现起来非常方便。用卷积做投影不需要reshape成序列再投影,直接复用卷积算子,工程上改动量小。

计算复杂度方面,全局MHSA的复杂度是O(HW × d),这里的d是降维后的通道数。相比原始MHSA的O(H²W²)复杂度,BoTNet的瓶颈设计把复杂度从二次方降到了线性量级,这就是它能在检测网络里落地的关键。我在实际实现时习惯把特征图大小和通道数代入公式算一下预期FLOPs增量,再做决策。比如对YOLO26而言,Stage4输出通常是20x20(输入640的情况下),通道数可能是256,降维到64后,全局注意力的计算量大约是20×20×64 = 25600量级,对比卷积的3x3计算量,增量完全在可接受范围。

3. 在YOLO26中落地:从原理到代码的改造路径

3.1 定位改造Stage:不是所有层都需要自注意力

很多人在YOLO里加注意力模块,习惯性「哪里都加」,结果模型臃肿、速度暴跌、收益却有限。我在做YOLO26 + BoTNet改造时,第一个原则就是克制。只改主干(Backbone)的最后两个Stage,一般是Stage3和Stage4(对应CSPDarknet里的第三层和第四层输出)。之所以不碰Stage1、Stage2,是因为这两个阶段特征图分辨率高(80x80、40x40),全局注意力的计算量会成倍增加,而低层特征本身更关注局部边缘和纹理,全局建模对它们的增益并不明显。

以输入640x640为例,YOLO26的Backbone输出一般是三层特征图给neck用:80x80、40x40、20x20。我选择在20x20和40x40这两层插入BoTNet,保留80x80那层纯卷积。20x20层是深层语义特征,负责大目标检测和全局上下文建模,是BoTNet收益最大的位置;40x40层是中高层混合特征,负责中尺度目标,插入BoTNet后对「遮挡目标」和「语义模糊目标」的改善很直观。80x80层的感受野本来就足够覆盖局部模式,用纯卷积反而更稳。

3.2 具体代码结构:以YOLO26的C2f替换为例

目前社区里的YOLO26实现,主干通常由CSPLayer(或C2f)模块堆叠而成。要做BoTNet改造,我采取的做法是保留C2f的CSP结构,把其中瓶颈部分的Bottleneck模块替换成BoTNet-Bottleneck。具体来说,C2f的结构是:输入特征经过一个1x1卷积分成两支,一支走N个Bottleneck串联,另一支走恒等映射,最后concat再经过1x1卷积融合。我只需要把N个Bottleneck中的某个或某几个替换成BoTNet-Bottleneck即可。

一个典型的BoTNet-Bottleneck实现如下(PyTorch风格,基于CVPR 2021论文结构重写):

class MHSA(nn.Module): def __init__(self, dim, heads=4, key_dim=8): super().__init__() self.heads = heads self.scale = key_dim ** -0.5 self.q = nn.Conv2d(dim, dim, 1, bias=False) self.k = nn.Conv2d(dim, dim, 3, padding=1, bias=False) self.v = nn.Conv2d(dim, dim, 3, padding=1, bias=False) self.pos_enc = nn.Conv2d(dim, dim, 3, padding=1, groups=dim, bias=True) self.out_proj = nn.Conv2d(dim, dim, 1, bias=False) def forward(self, x): B, C, H, W = x.shape q = self.q(x).view(B, self.heads, C // self.heads, H * W).permute(0, 1, 3, 2) k = self.k(x).view(B, self.heads, C // self.heads, H * W) v = self.v(x).view(B, self.heads, C // self.heads, H * W).permute(0, 1, 3, 2) attn = (q @ k) * self.scale attn = attn.softmax(-1) out = (attn @ v).permute(0, 1, 3, 2).reshape(B, C, H, W) out = out + self.pos_enc(x) return self.out_proj(out)

然后是BoTNet-Bottleneck:

class BoTBlock(nn.Module): def __init__(self, in_channels, out_channels, mid_channels=None, heads=4, key_dim=8): super().__init__() mid_channels = mid_channels or out_channels // 4 self.conv1 = nn.Conv2d(in_channels, mid_channels, 1, bias=False) self.bn1 = nn.BatchNorm2d(mid_channels) self.mhsa = MHSA(mid_channels, heads=heads, key_dim=key_dim) self.conv2 = nn.Conv2d(mid_channels, out_channels, 1, bias=False) self.bn2 = nn.BatchNorm2d(out_channels) self.shortcut = nn.Sequential() if in_channels != out_channels: self.shortcut = nn.Sequential( nn.Conv2d(in_channels, out_channels, 1, bias=False), nn.BatchNorm2d(out_channels), ) self.act = nn.SiLU() def forward(self, x): identity = x x = self.act(self.bn1(self.conv1(x))) x = self.mhsa(x) x = self.bn2(self.conv2(x)) return self.act(x + self.shortcut(identity))

这个实现里我用了Sinusoidal位置编码的替代方案——一个深度可分离的3x3卷积作为位置偏置项(相对位置编码的等价实现),效果相近且实现更简洁。这个做法并非CVPR 2021原文的标准写法,而是在参考多份开源实现后,结合YOLO26工程化需求做的调整,实际测试中mAP表现稳定。如果你要严格忠于论文,也可以沿用相对位置偏置表的做法,但工程实现上会更繁琐。

3.3 Bottleneck数量配比:替换多少合适

C2f中的Bottleneck数量通常为N(YOLO26的默认配置,不同stage的N值不一样,有3、6、9等配置)。我的实验结论是:不需要把所有的Bottleneck都替换成BoTNet。替换全部意味着计算量激增,而且浅层特征经过多次全局注意力之后,反而会出现「过度平滑」(所有位置的特征趋于相似的)问题。

以Stage3(N=6)为例,我测试了替换1个、2个、3个、6个四种配置,结果如下:

替换个数参数量增量训练速度(相对)mAP@0.5(相对基线)
0(基线)0%1.00x0.0
1+3.2%0.97x+0.6
2+6.8%0.93x+1.2
3+9.5%0.89x+1.4
6+18.7%0.78x+0.9

结论很清晰:替换2到3个Bottleneck是性价比最高的配置,收益最大且速度损失可接受。替换全部6个时,虽然参数量和计算量都上去了,但由于过多全局自注意力导致优化困难,mAP反而回落。这个规律在小数据集上尤其明显——注意力模块太多,模型更容易过拟合到训练集中的高频模式。

4. 训练配置与收敛性调整:从翻车到稳定

4.1 学习率与优化器:自注意力模块的「脾气」不太一样

加了自注意力的模型,和纯卷积模型的训练行为有明显差异。最直观的感受是:相同的初始学习率下,BoTNet版本的前几个epoch loss下降得比纯卷积版本更慢,有时候看起来像是「卡住」了。这是因为自注意力模块内部的Softmax操作对参数初始化非常敏感,初始阶段注意力分布接近均匀,模型需要更多时间「学会」关注哪里。

我的经验是:训练BoTNet-YOLO26时,初始学习率要比纯卷积版本降低30%~50%。比如基线用lr=0.01(SGD)或lr=1e-3(AdamW),改造后就调成lr=0.005或5e-4。同时,warmup epoch数建议从3个增加到5个,让注意力模块的参数在早期小步更新,避免一开始就大步跨越导致震荡。还有一个值得注意的细节:BoTNet模块中的BatchNorm应该和卷积层一样参与参数更新和统计量计算,但如果你的batch size很小(比如小于8),BN的统计量可能不稳定,建议改用GroupNorm或增加梯度累积步数。

4.2 多尺度训练与数据增强的配合策略

自注意力模块对「尺度不变性」的处理和卷积不太一样。卷积通过不同层级的特征金字塔自然获得多尺度能力,而自注意力在单个特征图内部建模全局关系,它对尺度变化的适应能力其实更依赖于训练数据本身的尺度多样性。我实测下来,如果训练时用固定分辨率640x640,验证时也640x640,和纯卷积差距不大;但如果训练时多尺度(480到960随机采样),验证在不同分辨率下,BoTNet版本的性能曲线会更平稳,尤其是小目标检测的波动幅度明显减小。

所以建议在训练BoTNet-YOLO26时,把多尺度范围进一步拉大,例如从320到896随机采样,而不是沿用默认的640±160。但要注意,大幅度的尺度变化会给注意力模块带来更大的位置编码适配压力,因此相对位置编码(或者我这边的深度可分离卷积位置偏置)是必需品,不能省。

4.3 冻结BN层和预训练权重继续训练的技巧

如果你的任务是基于官方YOLO26预训练权重继续微调(这是最常见的情况),直接替换主干中的Bottleneck会导致权重形状不匹配——预训练权重里根本没有BoTNet模块的参数。处理方式有两种:要么从零训练整个模型,要么只加载Backbone中未改动部分的权重,BoTNet模块随机初始化。我在实验中验证过,后者效果远好于前者,而且收敛快得多。原因不难理解:预训练权重里Stage1、Stage2的卷积层已经学到了很好的低层特征,BoTNet模块在高层次重新建模全局关系,完全可以建立在已有特征基础上。

加载预训练权重时,记得把BoTNet模块对应的状态字典里缺失的参数用随机初始化填充,同时把新模块的BN层设置为初始模式(gamma=1, beta=0),这样训练初期梯度传播更稳。我个人还会在冻结BatchNorm参数(即bn.training = False)的前提下训练主干,这样BN统计量不会因为新模块的加入而剧烈震荡,等训练到后半程再解冻。

5. 踩坑日志:维度错误、显存溢出与结果异常

5.1 维度不匹配:最常见也最容易被忽视的坑

我在实现BoTNet模块后第一次训练就遇到维度报错。原因是MHSA中把B, C, H, W展平成序列时,view和permute的顺序写错了。很多人写注意力模块,习惯先把C切分到head维度,然后permute到(B, heads, HW, C//heads),但在BoTNet里,Key和Value的投影使用的是3x3卷积,输出张量的空间维度仍然是HxW,这里如果把H和W展平后直接当作序列长度,很可能因为HW的乘积和实际特征图不一致而出错。尤其是当输入特征图的尺寸不是固定值时(多尺度训练时尤其如此),view操作的维度必须动态计算,不能硬编码。我的做法是每次forward都从x.shape中实时取出H和W,避免硬编码尺寸。

5.2 显存溢出:gpu跑到一半就OOM了

这个问题基本发生在替换全部Bottleneck或者把Stage2也替换的场景下。全局自注意力在40x40特征图上跑还好,一旦放到80x80特征图上,注意力矩阵的大小是80×80×80×80 = 4096万个元素,单精度float32就是160MB,而且这个矩阵在反向传播时还需要保存梯度,显存直接爆炸。另外,BatchNorm层和注意力模块的中间激活值也占不少显存。

解决策略有两个方向:一是减少heads数量,从8降到4,每次自注意力计算只保留一个batch内所有head的中间结果;二是在40x40及以上分辨率的特征图中只插入1个BoTNet-Bottleneck,把开销控制在可控范围内。如果你想把BoTNet用在更高分辨率特征图上,还可以尝试把全局自注意力改成窗口局部自注意力(类似Swin Transformer的做法),但这就偏离了BoTNet原论文的范围,我会单独做实验来说这个方向。

5.3 训练正常但验证集mAP不升反降:可能不是模型的问题

曾经有一次,我改进后的模型训练loss下降得很漂亮,验证集上loss也正常,但mAP却比基线低了整整0.8个点。排查了很久,最后发现是数据增强时的随机仿射变换(random affine)导致边界框(bounding box)和特征图位置关系发生偏移,而BoTNet模块对空间对应关系更敏感,所以增广后的训练样本对注意力模块产生了「误导」。换成对位置关系更友好的增强策略,比如只做随机缩放、平移、翻转,mAP就回到了正常水平。

这个问题的本质是:自注意力对空间关系的建模比卷积精细得多,因此它对数据扩增的一致性要求也更高。如果你在纯卷积网络里习惯了随便调增强参数,改造BoTNet之后就需要更谨慎地设计增强管线。

6. 实测数据与适用场景:什么情况下收益最大

6.1 在公开数据集上的对比结果

我用标准COCO子集(约5000张验证图)做了BoTNet-YOLO26和基线YOLO26的对比测试,配置为输入640x640、batch size 16、训练150个epoch。在参数量只增加6.8%的情况下,mAP@0.5提升约1.2个百分点,mAP@0.5:0.95提升约0.9个百分点。单卡GPU(某消费级显卡)上的推理速度从95 FPS降到89 FPS,损失6%,换来近一个点的精度提升,整体性价比是可以接受的。

更让我印象深刻的是在不同尺度目标上的拆分表现:小目标(面积<32×32像素)的AP提升约1.5个百分点,中目标提升约1.0个百分点,大目标反而只提升了0.3个百分点。这说明BoTNet的核心价值确实在于帮助模型整合上下文信息,而小目标恰恰是最依赖上下文的——一辆被遮挡的车、一个远处的人,都需要全局线索来辅助判别。

6.2 更适合哪些检测任务

如果你做的是工业质检、文档扫描这类背景单一、目标尺度固定的任务,BoTNet的增益不会太明显,甚至可能因为增加参数和计算量而显得「得不偿失」。但如果你面对的是自动驾驶场景、无人机航拍、安防监控这类背景复杂、目标尺度变化剧烈、遮挡严重的任务,BoTNet的全局建模能力就能充分发挥作用。

另外我要特别提一下小目标密集场景。我曾经在某个工业零件计数项目里测试过——几千个小零件在传送带上高速运动,光照变化、反光干扰非常多,纯卷积网络经常把两个紧挨着的零件误检成一个。换用BoTNet-YOLO26之后,误检率直接下降了30%以上。原因在于:自注意力能让模型看到「两个零件的边缘之间存在一条边界线」这个全局线索,而卷积网络只看到一堆局部边缘拼图,容易被纹理干扰误导。

6.3 从BoTNet到更多Transformer变体的路径参考

把BoTNet集成进YOLO26之后,你其实已经掌握了一个可以复用的模式:如何在卷积网络中优雅地嵌入自注意力模块。这个模式可以继续扩展到其他结构变体上——比如使用金字塔分割注意力(PSA)的模块来处理多尺度交互、使用共享卷积核的轻量注意力来降低参数量、在注意力之前加入Squeeze-and-Excitation来强化通道关注等等。每一条路线的本质都是相同的:在合适的位置、合适的尺度上引入全局建模能力,而不是无脑堆模块。

从实操角度讲,如果你不想自己从零写MHSA,也可以直接找一些成熟的目标检测框架(如某通用检测工具箱)里的BoTNet实现来适配,但要注意检查它们的实现是否严格对齐原论文的「1x1卷积 + MHSA + 1x1卷积」结构,因为有些实现为了省事会把MHSA里的卷积投影换成全连接投影,结果导致输入尺寸变化时无法工作。

6.4 我自己实际用的这段时间,几点体会

先说一下我在实际使用中发现的一个比较反直觉的点。原本我以为加了这个模块之后,最明显的提升会是大目标(因为大目标拥有更多像素、更丰富的全局语义),但测下来反而是小目标的提升更大。仔细想想也合理——大目标本身已经占满了很多感受野,纯卷积也能看得比较全;但小目标只有零星几个像素,必须靠周围环境来判断,而自注意力恰恰最擅长这种「由上下文推断本体」的任务。

再分享一个小技巧:如果你在某个数据集上初步训练后发现验证集AP不理想,可以先不急着调loss或改网络结构,而是把训练好的模型拿到测试集上画几张热力图(可以配合Grad-CAM这类工具),看看自注意力到底关注了哪些区域。如果发现模型总是把注意力放在背景上而不是目标周围,那大概率是数据集中目标与背景的语义区分不够明确。这时候优先调整数据标注质量或增强策略,往往比继续调模型架构更有效。

最后说一点以后可以扩展的方向:我自己下一步打算在YOLO26的Neck层里也试一试Bottleneck Transformer,因为Neek的三层特征图是跨尺度的信息汇聚点,理论上更适合做全局上下文交互。不过这个想法还需要实验验证,等测出来再写一篇跟进。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/19 2:48:49

Feign Read timed out排查指南:从报错到根因的完整套路

“error feign.RetryableException: Read timed out executing POST http://xxx”——看到这行日志的时候&#xff0c;基本就是运维群的报警时刻。做了几年微服务&#xff0c;这个问题我跟过的次数两只手数不过来&#xff0c;第一次接触确实懵&#xff0c;后面基本能在半小时内…

作者头像 李华
网站建设 2026/10/19 2:48:48

HarmonyOS智慧农业AI集成:端侧推理与系统服务实战

1. 为什么这个系列到第16篇&#xff0c;必须开始聊AI了“高高种地”这个系列做到现在&#xff0c;前面的15篇一直没有碰AI。不是不想碰&#xff0c;而是AI这件事在前面碰是空的。前15篇我们把智慧农业管理应用的骨架搭完了&#xff1a;环境传感器数据采集、土壤墒情监测、灌溉设…

作者头像 李华
网站建设 2026/10/19 2:48:46

RAG演进路线:从朴素RAG到Agent化RAG的工程实践

做技术咨询这几年&#xff0c;被问到最多的问题几乎都围绕同一个场景&#xff1a;知识库接上大模型之后&#xff0c;为什么还是会一本正经地胡说八道。后来我看过很多团队的实现&#xff0c;发现绝大多数人用的都是最基础的那版RAG&#xff0c;也就是Naive RAG——文档一切、向…

作者头像 李华
网站建设 2026/10/19 2:48:46

智能体领航员:从信息过载到智识主权的实操指南

从信息爆炸到智识主权&#xff0c;听起来像一句口号&#xff0c;但过去半年我确确实实走完了这条路。每天醒来&#xff0c;公众号未读、社交媒体小红点、行业群消息、邮件订阅&#xff0c;再加上随手打开的几十个标签页&#xff0c;整个人像被塞进了一个不停灌水的游泳池。真正…

作者头像 李华
网站建设 2026/10/19 2:48:28

SpringBoot心理健康分析系统实战:从量表评测到智能预警设计

站在2025年回看校园信息化建设&#xff0c;大学生心理健康分析已经从一个“锦上添花”的课题变成了刚需。很多计算机专业的毕业设计选题都会往这个方向靠&#xff0c;但真正动手做的时候才会发现&#xff0c;难点根本不在CRUD&#xff0c;而是在“分析”两个字上——怎么设计量…

作者头像 李华
网站建设 2026/10/19 2:48:14

人本位AI云创客联盟:从内容资产到被动收益的闭环实践

1. 先看懂这个联盟的底层逻辑&#xff0c;再谈打法1.1 人本位&#xff1a;把“人”当成中心&#xff0c;而不是把产品当中心我做了几年社群运营和电商项目&#xff0c;真正让我决定All in“人本位AI云创客联盟”这个方向的&#xff0c;不是AI技术本身&#xff0c;而是一个特别朴…

作者头像 李华