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

一文读懂现代卷积神经网络—稠密连接网络(DenseNet)

目录

什么是 DenseNet?

 稠密块(Dense Block)详解

一、稠密块的核心思想

二、稠密块的结构组成

1. 卷积单元(的结构)

2. 密集连接的具体方式

3. 关键参数:增长率(Growth Rate, k)

4. 过渡层(Transition Layer)

三、稠密块的优势

四、与 ResNet 残差块的对比

过渡层(Transition Layer)详解

一、什么是过渡层?

二、过渡层的核心作用

三、过渡层的典型结构

四、设计细节与参数

五、与 ResNet 中对应组件的对比

DenseNet 的作用

DenseNet 与 ResNet 的核心区别

ResNet和DenseNet的结构示意图

为啥卷积块中采用 “批量归一化(BN)→ 激活函数 → 卷积层” 的顺序?

1. 批量归一化(BN)放在最前:从根源稳定输入分布

2. 激活函数放在 BN 之后:让激活更 “有效”

3. 卷积层放在最后:利用稳定输入提升参数学习效率

 完整代码

实验结果


什么是 DenseNet?

稠密连接网络(Dense Convolutional Network,简称 DenseNet)是 2017 年由 Huang 等人提出的一种深层卷积神经网络,其核心创新是“稠密连接(Dense Connection)”:网络中的每个层都会与前面所有层直接连接,即第l层的输入是前l-1层的输出的拼接(而非简单相加)。

核心结构

  • 稠密块(Dense Block):由多个卷积层组成,层间通过稠密连接融合特征。设第i层的输出为x_i,则第l层的输入为前所有层输出的拼接:x_l = H_l([x_0, x_1, ..., x_{l-1}])其中[x_0, ..., x_{l-1}]表示通道维度上的拼接,H_l是第l层的卷积操作(含 BN、ReLU、卷积核)。

  • 过渡层(Transition Layer):用于连接不同的稠密块,通过 1×1 卷积降维和 2×2 平均池化减小特征图尺寸,防止网络参数爆炸。

  • 稠密块(dense block)和过渡层(transition layer)。 前者定义如何连接输入和输出,而后者则控制通道数量,使其不会太复杂。

 稠密块(Dense Block)详解

稠密块(Dense Block)是深度学习网络 DenseNet(Densely Connected Convolutional Networks) 的核心组成单元,其设计核心是通过密集连接(Dense Connection) 实现极致的特征重用,是对传统卷积网络和 ResNet 中 “跳跃连接” 的进一步升级。

一、稠密块的核心思想

传统卷积网络中,每一层的输入仅来自上一层;ResNet 通过 “跳跃连接” 让层的输入包含上一层的输出(元素相加);而稠密块则更进一步 ——每一层的输入是前面所有层的输出的拼接(Concatenation)

具体来说,若稠密块包含 L 层,第 l 层(记为H_l)的输入是第 0,1,...,l-1 层的输出的拼接,输出为 x_l = H_l([x_0, x_1, ..., x_{l-1}]),其中:

  • x_0 是稠密块的初始输入特征图;
  • [x_0, x_1, ..., x_{l-1}] 表示将这些特征图在通道维度上拼接;
  • H_l是第 l 层的卷积操作(通常包含 BN、ReLU、卷积等子操作)。

这种 “密集连接” 使得特征在网络中能够被充分传递和复用,从根本上解决了深层网络的 “特征衰减” 问题。

二、稠密块的结构组成

一个典型的稠密块由多个 “卷积单元” 和 “密集连接” 组成,同时为了控制计算量,通常会包含瓶颈层(Bottleneck) 和过渡层(Transition Layer)(过渡层用于连接不同稠密块,非稠密块内部结构,但需结合理解)。

1. 卷积单元(H_l的结构)

稠密块中的每一层 \(H_l\) 通常由以下子操作组成(顺序固定):

  • BN(Batch Normalization):标准化特征,加速训练;
  • ReLU:非线性激活,增强表达能力;
  • 1x1 卷积(可选,瓶颈层):减少输入特征的通道数(如将通道数压缩至原来的 1/4),降低计算量;
  • 3x3 卷积:提取局部特征,输出固定数量的特征图(由 “增长率” 决定)。

其中,1x1 卷积作为 “瓶颈层” 是 DenseNet 的重要优化:若直接对拼接后的高维特征做 3x3 卷积,计算量会爆炸;而 1x1 卷积可先将通道数降至较低维度(如 4k,k 为增长率),再用 3x3 卷积输出 k 个特征图,既保证特征提取效率,又控制了参数规模。

2. 密集连接的具体方式

假设稠密块的初始输入特征图为x_0(通道数为 k_0),每一层的输出特征图通道数为 k(即 “增长率”),则:

  • 第 1 层输入:x_0,输出:x_1 = H_1(x_0)(通道数 k);
  • 第 2 层输入:[x_0, x_1](通道数k_0 + k),输出:x_2 = H_2([x_0, x_1])(通道数 k);
  • 第 3 层输入:[x_0, x_1, x_2](通道数 k_0 + 2k),输出:x_3 = H_3([x_0, x_1, x_2])(通道数 k);
  • ...
  • 第 L 层输入:[x_0, x_1, ..., x_{L-1}](通道数 k_0 + (L-1)k),输出:x_L = H_L([x_0, ..., x_{L-1}])(通道数 k)。

最终,整个稠密块的输出是所有层输出的拼接:[x_0, x_1, ..., x_L](通道数 k_0 + Lk)。

3. 关键参数:增长率(Growth Rate, k)

增长率 k 是稠密块的核心参数,定义为每一层输出的特征图通道数。它控制了特征图的增长速度:

  • 若 k 较小(如 12、24),即使层数较多,拼接后的总通道数也不会过大,保证计算效率;
  • 实验表明,较小的 k(如 k=12)即可让 DenseNet 达到优异性能,说明密集连接对特征的利用率极高。
4. 过渡层(Transition Layer)

稠密块之间通过 “过渡层” 连接,作用是压缩特征图通道数并降低尺寸,避免网络冗余

  • 过渡层通常由 “BN + 1x1 卷积 + 2x2 平均池化” 组成;
  • 1x1 卷积将前一个稠密块的输出通道数压缩(如压缩至原来的 θ 倍,θ∈(0,1],称为 “压缩因子”);
  • 平均池化将特征图尺寸减半(如从 32x32 变为 16x16),控制网络深度和计算量。

三、稠密块的优势

  1. 极致的特征重用 每一层都能直接访问前面所有层的特征,特征在网络中被反复利用,避免了传统网络中 “特征随层数增加而衰减” 的问题。

  2. 缓解梯度消失 反向传播时,梯度可通过密集连接直接从深层传递到浅层,大幅缓解深层网络的梯度消失问题,使训练更深的网络成为可能。

  3. 参数效率更高 由于特征重用充分,DenseNet 无需像其他网络(如 ResNet)那样通过增加通道数提升性能,小增长率 k 即可实现高准确率,参数规模通常小于 ResNet。

  4. 正则化效果 密集连接增加了层之间的相互依赖,一定程度上减少了过拟合,提升模型泛化能力。

四、与 ResNet 残差块的对比

对比维度稠密块(Dense Block)残差块(Residual Block)
连接方式特征图拼接(Concatenation)特征图元素相加(Element-wise Add)
输入来源前面所有层的输出仅上一层的输出(跨层相加)
特征利用效率极高(所有历史特征直接参与当前层)较高(仅上一层特征与当前层特征融合)
特征维度变化随层数线性增长(由增长率 k 控制)基本不变(相加不改变通道数)
计算量控制依赖瓶颈层(1x1 卷积)和过渡层依赖残差连接的 “恒等映射”

过渡层(Transition Layer)详解

过渡层(Transition Layer)是稠密连接网络(DenseNet) 中的关键组件,主要用于连接相邻的稠密块(Dense Block),并实现特征图的降维和压缩,在保证网络效率的同时控制模型复杂度。以下从定义、作用、结构细节及设计意义展开详解:

一、什么是过渡层?

过渡层是 DenseNet 中位于两个稠密块之间的连接模块,其核心功能是对前一个稠密块输出的特征图进行处理,为下一个稠密块提供合适维度的输入。由于稠密块会通过密集连接生成大量特征图(如一个包含 12 层的稠密块可能输出数百个特征图),过渡层的作用类似于 “桥梁”,通过降维减少特征数量,避免网络参数和计算量过度膨胀。

二、过渡层的核心作用
  1. 特征降维 稠密块的输出特征图数量通常较多(例如,每个稠密块会累计生成 k \times L 个特征图,其中 k 是增长率,L 是块内层数)。过渡层通过卷积操作将特征图数量按比例压缩(通常压缩至原来的 \theta 倍,\theta称为压缩因子,一般取 0.5),减少后续计算量。

  2. 空间尺寸缩减 通过池化操作(通常是 2×2 的平均池化)将特征图的空间尺寸(高和宽)缩小一半,与传统卷积网络中 “下采样→增大感受野” 的设计思路一致,帮助网络捕捉更全局的特征。

  3. 特征融合与平滑 过渡层中的卷积操作(通常是 1×1 卷积)可以对稠密块输出的多通道特征进行融合,减少冗余信息,同时保持特征的连续性,为下一个稠密块的输入提供更精炼的特征。

三、过渡层的典型结构

过渡层的结构简洁,通常由两个操作组成,按顺序执行:

  1. 1×1 卷积

    • 作用:对输入特征图进行通道压缩(降维),并融合通道间的信息。
    • 细节:卷积核数量为前一个稠密块输出特征数的\theta倍(\theta \in (0,1]),当\theta=1时不压缩),步长为 1, padding 为 0。
    • 示例:若前一个稠密块输出 200 个特征图,\theta=0.5,则 1×1 卷积后输出 100 个特征图。
  2. 2×2 平均池化

    • 作用:将特征图的空间尺寸(如H \times W)缩减为 H/2 \times W/2,实现下采样。
    • 细节:池化核大小 2×2,步长 2,无 padding,确保尺寸减半。
四、设计细节与参数
  • 压缩因子\theta:是 DenseNet 的重要超参数,控制特征压缩比例。当 \theta < 1时,DenseNet 称为 “压缩 DenseNet”(如 DenseNet-C),若 \theta = 1则不压缩。实验中\theta =0.5 是常用设置,可在精度和效率间取得平衡。
  • 激活函数与归一化:过渡层的 1×1 卷积后通常会跟随批量归一化(BN)和 ReLU 激活函数,确保特征分布稳定并引入非线性。
五、与 ResNet 中对应组件的对比

在 ResNet 中,连接不同残差块的下采样通常通过 “stride=2 的 3×3 卷积” 或 “单独的池化层 + 卷积” 实现,目的是缩减尺寸但不刻意压缩通道数。而过渡层的核心差异在于:

  • 主动降维:通过 1×1 卷积和压缩因子主动减少通道数,更注重控制模型参数。
  • 与稠密块的配合:由于稠密块会累计大量特征,过渡层的降维是 DenseNet 控制复杂度的关键,而 ResNet 的残差块不会累计特征,因此无需专门的压缩机制。

DenseNet 的作用

  1. 缓解梯度消失问题 稠密连接让每个层都能直接接收前面所有层的梯度(反向传播时,梯度无需经过多层累积),确保深层网络的梯度能有效传递到浅层,解决了深层网络训练困难的问题。

  2. 促进特征复用 每个层的输入包含前面所有层的特征(而非仅前一层),特征在网络中被多次复用,减少了冗余特征的学习,提升了特征利用效率。例如,浅层的边缘特征和深层的语义特征可直接融合,增强模型表达能力。

  3. 减少参数数量 由于特征复用充分,DenseNet 无需像 ResNet 那样通过增加通道数来提升性能(通道数通常远小于 ResNet),因此参数总量更少(例如 DenseNet-121 的参数约 800 万,仅为 ResNet-50 的 1/3)。

  4. 抑制过拟合 特征的多次复用相当于给网络引入了 “正则化” 效果,尤其在小数据集上,过拟合风险更低,泛化能力更强。

DenseNet 与 ResNet 的核心区别

对比维度ResNet(残差网络)DenseNet(稠密连接网络)
连接方式每个层仅与前一层连接(“链式”):\(x_l = H_l(x_{l-1}) + x_{l-1}\)每个层与前面所有层连接(“稠密”):\(x_l = H_l([x_0, ..., x_{l-1}])\)
特征融合方式残差相加(元素级加法,要求通道数相同)特征拼接(通道级拼接,通道数随层数累积)
通道数变化随深度翻倍(如 64→128→256→512),通过升维提升能力单一层通道数固定(如 32),通过拼接累积总通道数(复用特征)
参数效率较低(通道数大,参数多)较高(通道数小,特征复用减少冗余参数)
计算复杂度中等(加法操作轻量,但通道数大)较高(拼接导致总通道数大,需通过过渡层控制)
梯度传播梯度通过残差连接间接传递(需经过前一层)梯度直接传递到所有浅层(无中间层阻碍)
适用场景超深网络(如 ResNet-152)、大数据集(需强表达能力)中小数据集(泛化能力强)、对参数敏感的场景

ResNet和DenseNet的结构示意图

为啥卷积块中采用 “批量归一化(BN)→ 激活函数 → 卷积层” 的顺序?

1. 批量归一化(BN)放在最前:从根源稳定输入分布

BN 的核心功能是标准化输入数据的分布(将每个通道的输入调整为均值 0、方差 1 的标准分布),从而解决 “内部协变量偏移”。

如果将 BN 放在卷积层之后(即 “卷积→BN→激活”),则 BN 只能标准化卷积层的输出;而放在卷积层之前(即 “BN→激活→卷积”),BN 可以直接标准化进入卷积层的原始输入,从更早期稳定数据分布,效果更彻底:

  • 卷积层的输入分布更稳定,参数更新时输出变化更平缓,训练更稳定;
  • 避免卷积层因输入分布剧烈波动而陷入 “参数震荡”(例如卷积核反复调整以适应突变的输入)。
2. 激活函数放在 BN 之后:让激活更 “有效”

激活函数(如 ReLU)的作用是引入非线性,但其效果高度依赖输入分布:

  • ReLU 对负数输入会 “截断”(输出 0),若输入分布不稳定(例如均值偏移到负数区域),会导致大量神经元 “死亡”(输出恒为 0);
  • BN 将输入标准化为 “均值 0、方差 1” 的分布后,ReLU 的输入会均匀分布在正负区间,既能保留足够多的正输入(激活有效神经元),又能通过负输入的截断引入非线性,避免激活函数失效。
3. 卷积层放在最后:利用稳定输入提升参数学习效率

卷积层是特征提取的核心,其参数学习(通过梯度下降更新 W 和 b)需要稳定的输入分布:

  • 经过 BN 标准化和激活函数非线性变换后,输入到卷积层的数据分布已非常稳定,卷积核可以更高效地学习 “有意义的特征模式”(例如边缘、纹理);
  • 若卷积层放在最前,其输出分布会因参数更新而剧烈变化,后续的 BN 和激活函数需要不断 “适配” 这种变化,降低训练效率。

先稳定分布,再引入非线性,最后高效提取特征

 完整代码

"""
文件名: 7.7  稠密连接网络(DenseNet)
作者: 墨尘
日期: 2025/7/14
项目名: dl_env
备注: 实现完整的DenseNet网络,包含稠密块、过渡层及端到端训练流程,用于Fashion-MNIST分类
"""import torch
from torch import nn
from d2l import torch as d2l
# 手动显示图像相关库
import matplotlib.pyplot as plt  # 绘图库
import matplotlib.text as text  # 用于修改文本绘制(解决符号显示问题)# -------------------------- 核心解决方案:解决文本显示问题 --------------------------
# 定义替换函数:将Unicode减号(U+2212,可能导致显示异常)替换为普通减号(-)
def replace_minus(s):"""解决Matplotlib中Unicode减号显示异常的问题参数:s: 待处理的字符串或其他类型对象返回:处理后的字符串或原始对象(非字符串类型)"""if isinstance(s, str):  # 判断输入是否为字符串return s.replace('\u2212', '-')  # 替换特殊减号为普通减号return s  # 非字符串直接返回# 重写matplotlib的Text类的set_text方法,解决减号显示异常
original_set_text = text.Text.set_text  # 保存原始的set_text方法
def new_set_text(self, s):"""重写后的文本设置方法,在设置文本前先处理减号显示问题"""s = replace_minus(s)  # 调用替换函数处理文本中的减号return original_set_text(self, s)  # 调用原始方法设置文本
text.Text.set_text = new_set_text  # 应用重写后的方法# -------------------------- 字体配置(确保中文和数学符号正常显示)--------------------------
plt.rcParams["font.family"] = ["SimHei"]  # 设置中文字体(支持中文显示)
plt.rcParams["text.usetex"] = True  # 使用LaTeX渲染文本(提升数学符号显示效果)
plt.rcParams["axes.unicode_minus"] = True  # 确保负号正确显示(避免显示为方块)
plt.rcParams["mathtext.fontset"] = "cm"  # 设置数学符号字体为Computer Modern(更美观)
d2l.plt.rcParams.update(plt.rcParams)  # 让d2l库的绘图工具继承上述字体配置# 定义卷积块:BN + ReLU + 3x3卷积(DenseNet的基础单元)
def conv_block(input_channels, num_channels):"""构建DenseNet中的基础卷积单元,实现特征提取参数:input_channels: 输入特征图的通道数num_channels: 输出特征图的通道数(即增长率的一部分)返回:nn.Sequential: 包含批量归一化、激活函数和卷积层的序列模块"""return nn.Sequential(nn.BatchNorm2d(input_channels),  # 批量归一化:稳定输入分布,加速训练nn.ReLU(),  # ReLU激活:引入非线性,增强特征表达能力# 3x3卷积:提取局部空间特征,padding=1确保输出尺寸与输入相同nn.Conv2d(input_channels, num_channels, kernel_size=3, padding=1))class DenseBlock(nn.Module):"""稠密块(DenseBlock):DenseNet的核心组件,通过密集连接实现特征复用参数:num_convs: 块内包含的卷积块数量input_channels: 初始输入通道数num_channels: 每个卷积块的输出通道数(即"增长率",控制特征增长速度)"""def __init__(self, num_convs, input_channels, num_channels):super(DenseBlock, self).__init__()layer = []# 逐个添加卷积块,每个卷积块的输入通道数随层数递增for i in range(num_convs):# 第i个卷积块的输入通道数 = 初始通道数 + i×增长率# 例如:第0个卷积块输入=input_channels,第1个=input_channels+num_channels,以此类推layer.append(conv_block(input_channels + i * num_channels,  # 动态计算输入通道数num_channels  # 固定输出通道数(增长率)))self.net = nn.Sequential(*layer)  # 将所有卷积块组合成序列def forward(self, X):"""前向传播:通过密集连接拼接所有卷积块的输出参数:X: 输入特征图,形状为(batch_size, input_channels, height, width)返回:拼接后的特征图,形状为(batch_size, final_channels, height, width)其中final_channels = input_channels + num_convs×num_channels"""for blk in self.net:Y = blk(X)  # 当前卷积块的输出# 在通道维度(dim=1)上拼接原始输入X和当前输出Y(密集连接的核心)X = torch.cat((X, Y), dim=1)return X# 过渡层:连接两个稠密块,实现特征降维和尺寸缩减
def transition_block(input_channels, num_channels):"""过渡层:压缩特征通道数并减小空间尺寸,避免网络参数爆炸参数:input_channels: 输入特征图的通道数(前一个稠密块的输出)num_channels: 输出特征图的通道数(通常为输入的1/2,即压缩因子0.5)返回:nn.Sequential: 包含批量归一化、激活、卷积和池化的序列模块"""return nn.Sequential(nn.BatchNorm2d(input_channels),  # 批量归一化:稳定过渡层输入分布nn.ReLU(),  # 激活函数:引入非线性# 1x1卷积:减少通道数(核心降维操作,参数少且计算高效)nn.Conv2d(input_channels, num_channels, kernel_size=1),# 2x2平均池化:将空间尺寸减半(height和width各除以2)nn.AvgPool2d(kernel_size=2, stride=2))if __name__ == '__main__':# -------------------------- 测试核心组件功能 --------------------------# 测试1:稠密块(2个卷积块,输入3通道,增长率10)dense_blk = DenseBlock(num_convs=2, input_channels=3, num_channels=10)X = torch.randn(4, 3, 8, 8)  # 随机输入:4个样本,3通道,8x8尺寸Y = dense_blk(X)print(f"稠密块输出形状: {Y.shape}")  # 预期:(4, 3+2×10=23, 8, 8)# 测试2:过渡层(输入23通道,输出10通道)trans_blk = transition_block(input_channels=23, num_channels=10)Z = trans_blk(Y)print(f"过渡层输出形状: {Z.shape}")  # 预期:(4, 10, 4, 4)(尺寸减半)# -------------------------- 构建完整DenseNet网络 --------------------------# 第一个模块:初始卷积+池化(预处理输入图像)b1 = nn.Sequential(# 7x7大卷积:初步提取全局特征,步长2压缩尺寸nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),nn.BatchNorm2d(64),  # 批量归一化nn.ReLU(),  # 激活# 3x3最大池化:进一步将尺寸减半(为后续稠密块做准备)nn.MaxPool2d(kernel_size=3, stride=2, padding=1))# 配置稠密块参数num_channels = 64  # 初始通道数(与b1输出通道一致)growth_rate = 32  # 增长率:每个卷积块的输出通道数(控制特征增长速度)num_convs_in_dense_blocks = [4, 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)),  # 全局平均池化:将特征图压缩为1x1nn.Flatten(),  # 展平为一维向量nn.Linear(num_channels, 10)  # 全连接层:输出10类(Fashion-MNIST))# -------------------------- 训练DenseNet模型 --------------------------# 训练参数lr = 0.1  # 学习率(DenseNet对学习率较鲁棒)num_epochs = 10  # 训练轮数batch_size = 256  # 批量大小# 加载Fashion-MNIST数据集(调整图像大小为96x96适配网络)train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)# 启动训练(使用d2l库的训练函数,自动支持GPU)print("\n开始训练DenseNet模型...")d2l.train_ch6(net, train_iter, test_iter,num_epochs, lr,device=d2l.try_gpu()  # 自动选择GPU(如有))# 显示训练曲线(损失+准确率)plt.show(block=True)

实验结果

http://www.lryc.cn/news/588282.html

相关文章:

  • Journal of Engineering Mechanics and Machinery,工程力学期刊,1-2天录用,7天出版,即将送检!
  • 自定义类型 - 联合体与枚举(百度笔试题算法优化)
  • 前端将传回的List数据组织成树形数据并展示
  • 用于监测线性基础设施的分布式声学传感:现状与趋势
  • 深度剖析:动态接口代理核心原理与高级应用
  • APP测试之Monkey压力测试
  • Relocations in generic ELF (EM: 40)
  • Qt小组件 - 2(布局)瀑布流布局,GridLayout,FlowLayout
  • 虚拟列表组件如果滑动速度过快导致渲染性能问题
  • UART寄存器介绍
  • 前端学习5:Float学习(仅简单了解,引出flex)
  • 015 程序地址空间入门
  • Life:Internship in OnSea Day 22
  • 某ctv视频下载逆向思路
  • 云原生技术与应用-Containerd容器技术详解
  • Git LFS 操作处理Github上传大文件操作记录
  • Spring Boot 双数据源配置
  • RPC vs RESTful架构选择背后的技术博弈
  • SecretFlow 隐语 (2) --- 隐语架构概览
  • 【安卓笔记】进程和线程的基础知识
  • Ubuntu20.05上安装Clang 15
  • 【安卓笔记】线程基本使用:锁、锁案例
  • 新型eSIM攻击技术可克隆用户资料并劫持手机身份
  • linux 内核: 访问当前进程的 task_struct
  • [论文阅读] 人工智能 + 软件工程 | 用大语言模型+排名机制,让代码评论自动更新更靠谱
  • android Perfetto cpu分析教程及案例
  • 迁移学习之图像预训练理解
  • ICML 2025 | 从语言到视觉,自回归模型VARSR开启图像超分新范式
  • C# TCP粘包与拆包深度了解
  • CSP-S 模拟赛 17