深度残差收缩网络(DRSN):残差+注意力+软阈值化的抗噪分类实战

深度残差收缩网络(Deep Residual Shrinkage Network,DRSN)这名字听着唬人,实际拆开就是三件事:残差结构、注意力机制和软阈值化。最近我把它的完整 Python 程序从头到尾复现了一遍,专门测试了它在带噪声图像分类上的表现。这篇文章会把 DRSN 的来龙去脉、核心原理、完整训练代码和踩坑记录都摊开来讲,适合已经会 PyTorch 基础操作、想搞懂残差网络变体或者想拿深度学习方法做信号/图像降噪的读者。如果你只需要跑通一个程序,可以直接跳到第 2 节的代码;如果你想知道为什么这个网络有效,建议顺着读。我用的环境是 Python 3.10 + PyTorch 2.1,没有特别花哨的依赖,CPU 也能完成小规模训练。

1. 深度残差收缩网络到底在解决什么问题

1.1 普通残差网络的局限:噪声会进入特征

ResNet 通过恒等映射解决了深层网络退化问题,让梯度可以顺畅回传,这是它成功的关键。但有个点很容易被忽略:ResNet 对特征图的每个通道是同等对待的,卷积核会把输入里的所有响应都编码进特征,包括噪声。当数据来自传感器、工业现场或低光照环境时,原始信号里会混入大量干扰,模型训练时如果把这些噪声也当成有效模式,测试时大概率先下降。

传统做法是先滤波再分类。比如振动信号先用小波去噪,图像先用双边滤波,但这类预处理会损失边缘和细节,而且滤波参数需要人工调,换一个工况又要重调。DRSN 的思路完全不同,它把降噪过程直接嵌入网络,让网络根据当前样本自己决定哪些特征需要保留、哪些特征该被压缩。这样模型既保留残差结构带来的训练稳定性,又能对噪声产生天然的抑制能力。

1.2 软阈值化:怎么做到“部分去噪”

软阈值化的公式是 y = sign(x) · max(|x| − τ, 0)。直观解释就是在数轴上把绝对值小于 τ 的数直接变成 0,绝对值大于 τ 的数向 0 方向收缩 τ。这比硬阈值化(绝对值小于 τ 置 0,大于 τ 不变)更温和,避免了输出在阈值处突然跳变,梯度也更稳定。

我用一个生活化类比:你在收音机里听广播,背景噪声小的时候不需要处理;噪声大的时候,把音量整体压低一些,这样人声虽然也变轻,但不会淹没在杂音里。软阈值化就是这种“压低”操作,τ 是压低幅度。常规降噪算法需要预先知道噪声范围,而 DRSN 的 τ 不是人工定的,是由网络自己学出来的。

1.3 注意力机制决定阈值大小

如果每个通道用同一个固定阈值,显然不够聪明。不同通道对最终分类的贡献不一样,包含噪声的比例也不一样。DRSN 借鉴通道注意力机制,用一个极小的子网络生成阈值:先对特征图做全局平均池化,得到每个通道的整体响应;再经过两个全连接层,中间用 ReLU 激活,最后一层用 Sigmoid 把输出压缩到 0 到 1 之间;最后用这个缩放系数乘以特征图绝对值的均值,得到真正的阈值。

为什么要用 Sigmoid 乘以均值?因为 Sigmoid 输出永远不会超过 1,所以阈值一定小于当前特征图的平均绝对值。如果阈值比所有特征值都大,软阈值化会把整张特征图清零,信息全部丢失;如果太小,又起不到去噪效果。乘均值这个操作保证了阈值和输入尺度匹配,同时保留了一定的学习空间。这个设计是 DRSN 能稳定训练的核心原因。

2. 环境准备与模型结构拆解

2.1 依赖安装与工程目录

复现 DRSN 不需要太多第三方库。我的环境只需要 PyTorch 和 torchvision,画曲线再加一个 matplotlib。安装命令如下:

pip install torch torchvision matplotlib

为了让你快速跑起来,我把所有代码放在一个drsn_train.py文件里,不拆模块。单文件的优点是直接复制就能运行,不用建一堆目录;缺点是代码稍长,但胜在清晰。文件内部按这个顺序组织:导入库、定义阈值生成器、定义残差收缩块、定义 DRSN 主体、加载 CIFAR-10、训练循环、测试循环。

2.2 阈值生成器:DRSN 的核心小网络

ThresholdGenerator 是整个模型的灵魂。它的输入是某一个残差块的输出特征图,输出是每个通道对应的阈值。代码如下:

import torch import torch.nn as nn import torch.nn.functional as F class ThresholdGenerator(nn.Module): def __init__(self, in_channels, reduction=16, mode='channel_wise'): super().__init__() self.mode = mode self.avg_pool = nn.AdaptiveAvgPool2d(1) self.fc1 = nn.Linear(in_channels, in_channels // reduction) self.fc2 = nn.Linear(in_channels // reduction, in_channels if mode == 'channel_wise' else 1) self.sigmoid = nn.Sigmoid() def forward(self, x): avg_abs = torch.mean(torch.abs(x), dim=(2, 3), keepdim=True) gap = self.avg_pool(x).view(x.size(0), -1) scale = self.fc2(F.relu(self.fc1(gap))) scale = self.sigmoid(scale).view(-1, scale.size(1), 1, 1) threshold = avg_abs * scale return threshold

逐行解释一下。avg_abs是在每个通道的空间维度上求绝对值均值,形状是[N, C, 1, 1]。gap是常规的全局平均池化,先通过AdaptiveAvgPool2d(1)把特征图压成[N, C, 1, 1],再view成[N, C]。经过两层全连接后,scale的形状是[N, C](channel_wise 模式)或[N, 1](channel_shared 模式)。Sigmoid 把它压到 0~1,再还原成 1×1 的卷积形状,方便和avg_abs做逐通道乘法。

这里的reduction=16是通道压缩比率,可以理解为两个全连接之间的瓶颈宽度。缩小reduction会增加参数量,但也可能提升拟合能力;我试过reduction=8和reduction=16,在 CIFAR-10 上差距不大,16 更省参数。

2.3 残差收缩块:把软阈值化放进残差路径

有了阈值生成器,接下来把它放进残差块里。DRSN 的残差块和普通 ResNet BasicBlock 的区别在于:普通残差块是“卷积-批归一化-激活-卷积-批归一化-恒等映射相加-激活”,DRSN 在最后一个批归一化之后、恒等映射相加之前,插入了软阈值化操作。代码实现如下:

class BasicShrinkageBlock(nn.Module): expansion = 1 def __init__(self, in_channels, out_channels, stride=1, downsample=None, reduction=16, mode='channel_wise'): super().__init__() self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(out_channels) self.relu = nn.ReLU(inplace=True) self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(out_channels) self.threshold_generator = ThresholdGenerator(out_channels, reduction, mode) self.downsample = downsample def _soft_threshold(self, x, threshold): return torch.sign(x) * torch.clamp(torch.abs(x) - threshold, min=0) def forward(self, x): identity = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) threshold = self.threshold_generator(out) out = self._soft_threshold(out, threshold) if self.downsample is not None: identity = self.downsample(x) out += identity out = self.relu(out) return out

可以看到,软阈值化只作用在残差分支上,不直接作用在恒等映射上。这样做的好处是:如果阈值把残留分支的大部分信号压掉,恒等映射仍然保留原始输入,梯度依然能顺畅回传。downsample用于改变通道数或降低分辨率,通常是一个 1×1 卷积加批归一化。

2.4 组装完整 DRSN 网络

把上述残差块堆叠起来,就得到完整的 DRSN。我的配置针对 32×32 小图像做了调整,没有照搬 ImageNet 版本:

class DRSNet(nn.Module): def __init__(self, block=BasicShrinkageBlock, layers=(2, 2, 2), channels=(16, 32, 64), num_classes=10, mode='channel_wise'): super().__init__() self.in_channels = 16 self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(16) self.relu = nn.ReLU(inplace=True) self.layer1 = self._make_layer(block, channels[0], layers[0], stride=1, mode=mode) self.layer2 = self._make_layer(block, channels[1], layers[1], stride=2, mode=mode) self.layer3 = self._make_layer(block, channels[2], layers[2], stride=2, mode=mode) self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) self.fc = nn.Linear(channels[2] * block.expansion, num_classes) def _make_layer(self, block, out_channels, blocks, stride=1, mode='channel_wise'): downsample = None if stride != 1 or self.in_channels != out_channels: downsample = nn.Sequential( nn.Conv2d(self.in_channels, out_channels, kernel_size=1, stride=stride, bias=False), nn.BatchNorm2d(out_channels), ) layers = [] layers.append(block(self.in_channels, out_channels, stride=stride, downsample=downsample, mode=mode)) self.in_channels = out_channels for _ in range(1, blocks): layers.append(block(self.in_channels, out_channels, mode=mode)) return nn.Sequential(*layers) def forward(self, x): x = self.conv1(x) x = self.bn1(x) x = self.relu(x) x = self.layer1(x) x = self.layer2(x) x = self.layer3(x) x = self.avgpool(x) x = torch.flatten(x, 1) x = self.fc(x) return x

这个配置下,输入 32×32 图像,经过第一个卷积后仍为 32×32,layer1 输出 32×32,layer2 输出 16×16,layer3 输出 8×8,最后平均池化得到 1×1 特征。如果想把 DRSN 用到更大的输入,只需要把layers和channels加长。

3. 完整 Python 训练程序与实操

3.1 数据准备:手动给 CIFAR-10 加噪声

我用 CIFAR-10 做演示,因为它是公开数据集,加载方便,而且标签是 10 分类,训练速度快。为了突出 DRSN 的抗噪能力,我在训练和测试时都往图像上叠加高斯噪声。数据增强部分用常规的随机裁剪和水平翻转:

import torchvision import torchvision.transforms as transforms transform_train = transforms.Compose([ transforms.RandomCrop(32, padding=4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), ]) transform_test = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), ]) train_set = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_train) test_set = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test)

加噪声函数写成一个普通 Python 函数,在训练循环里每个 batch 调用。噪声标准差我用 0.1,这个值对归一化后的图像来说已经不小。如果设为 0.3,普通 ResNet 基本会崩,DRSN 也会明显下降;如果设为 0.01,又体现不出差距。

def add_noise(images, noise_std=0.1): noise = torch.randn_like(images) * noise_std return torch.clamp(images + noise, -1, 1)

注意Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))后图像的数值范围是 [-1,1],噪声也要在这个量级加,最后用clamp防止越界。

3.2 训练循环:超参数与学习率调整

训练部分的核心代码示例如下。这里我用 Adam,学习率 0.001,训练 20 个 epoch,每 10 个 epoch 把学习率降到原来的十分之一。为了复现稳定,先固定随机种子:

import torch.optim as optim import numpy as np import random def set_seed(seed=42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) set_seed(42) device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = DRSNet().to(device) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=1e-3, weight_decay=5e-4) scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=[10, 15], gamma=0.1) train_loader = torch.utils.data.DataLoader(train_set, batch_size=128, shuffle=True, num_workers=2) test_loader = torch.utils.data.DataLoader(test_set, batch_size=128, shuffle=False, num_workers=2)

训练循环里最值得注意的就是加噪声的时机。我是在数据加载完成之后、送入网络之前加噪声,这样每个 epoch 看到的噪声都不同,相当于一种数据增强:

loss_history = [] for epoch in range(20): model.train() running_loss = 0.0 for images, labels in train_loader: images = add_noise(images.to(device), noise_std=0.1).to(device) labels = labels.to(device) optimizer.zero_grad() outputs = model(images) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() * images.size(0) epoch_loss = running_loss / len(train_set) loss_history.append(epoch_loss) print(f'Epoch {epoch+1:02d} | Loss: {epoch_loss:.4f}') scheduler.step()

为什么要在 batch 上加噪声而不是在数据集里加?如果事先把整个训练集加一遍噪声,模型只会学习固定的噪声模式,过几个 epoch 就记住了。动态加噪可以让模型见过更多噪声变化,泛化能力更强。

3.3 评估与结果可视化

测试阶段同样加噪声,但噪声强度和训练时保持一致。为了公平对比,我每次评估用一个固定的随机种子生成噪声,这样多次跑结果不会飘得太离谱。

def evaluate(loader, model, noise_std=0.1): model.eval() correct = 0 total = 0 with torch.no_grad(): for images, labels in loader: images = add_noise(images.to(device), noise_std=noise_std).to(device) labels = labels.to(device) outputs = model(images) preds = outputs.argmax(dim=1) correct += (preds == labels).sum().item() total += labels.size(0) return correct / total test_acc = evaluate(test_loader, model, noise_std=0.1) print(f'Test accuracy with noise: {test_acc:.4f}')

如果想把训练损失画出来,用 matplotlib 画个折线图就行。我实际复现时发现,DRSN 在加载噪声后的前几个 epoch loss 下降很慢,但随着阈值生成器学会控制阈值,后面会越来越快。

4. 复现中的几个大坑,建议收藏

4.1 阈值输出过大导致特征全部缩死

我第一次实现时,让阈值生成器的最后一层直接输出阈值,结果训练不到几个 batch loss 就变成 NaN。原因很简单:全连接层输出的数值范围不受控制,可能产生非常大的阈值,软阈值化把所有特征都压成 0。一旦特征全为 0,残差分支没有有效信号,模型只剩下恒等映射和最后的分类层,梯度信号不足,训练直接崩溃。

解决办法就是前面代码里的方案:最后一层用 Sigmoid 输出 0~1 的缩放系数,再乘以特征图绝对值均值。这样阈值永远落在 0 到当前通道平均绝对值之间,不会出现极端值。这个改动让训练从“随缘”变成了“稳”。

4.2 软阈值化求导要小心

PyTorch 的torch.sign在 0 点不可导,通常会被认为梯度为 0。但在torch.sign(x) * torch.clamp(torch.abs(x) - threshold, min=0)这个表达式里,sign是作为常数系数参与乘法的。梯度的主要来源是abs(x)和clamp部分,所以模型依然能正常回传。如果你还是不放心,可以换一个等价写法:

def soft_threshold(x, threshold): return torch.sign(x) * torch.relu(torch.abs(x) - threshold)

我更推荐把这个操作封装成nn.Module,方便复用。

4.3 小图像上别照搬大网络结构

一些网上公开的实现直接使用 ImageNet 版本的 ResNet 结构,第一层就是 7×7 卷积加 stride=2,输入 32×32 时很快变成 16×16,信息损失非常严重。我改成 3×3、stride=1 的起始卷积,并让 layer1 保持分辨率不变。如果你要处理的是 64×64 或更大图像,可以适当增加 stage 数量。

4.4 参数量与训练速度的权衡

DRSN 比普通 ResNet 多的部分主要是阈值生成器的两个全连接层,参数量增加很少。我按现在的配置统计过,普通 ResNet-10 大概 1.2M 参数,DRSN-CW-10 大概 1.3M,多出的 0.1M 基本就是每个残差块里那两层全连接。换来的是带噪场景下测试准确率的明显提升。如果对推理速度敏感,可以把mode改成channel_shared,这样每次只生成一个阈值,计算量更小,但精度会略低于channel_wise。

5. 最后再分享一点我的实测体会

如果你在自己的数据集上测试,我建议先用小噪声强度跑通,再用大噪声强度看 DRSN 的收益。我跑下来的现象是:干净数据上 DRSN 并不比普通 ResNet 强,甚至偶尔低零点几个百分点;但只要测试时加了噪声,两者的差距立刻拉开。这基本符合 DRSN 的设计目标——它本来就不是为了提升干净数据的精度,而是为了让模型在噪声干扰下不崩掉。还有个细节,训练时加噪声强度不要一直固定,可以先小后大,比如前 10 个 epoch 用 0.05,后 10 个 epoch 用 0.1,收敛速度会更快。希望这份复现记录能帮你少踩几个坑。

相关新闻

RNN层在时间序列预测中的不可替代性与实操指南

RNN层在时间序列预测中的不可替代性与实操指南

1. 项目概述:为什么RNN层仍是时间序列预测里绕不开的“老班长”你打开任何一份工业设备振动监测报告、电商销售趋势分析图,或者某城市未来72小时用电负荷曲线,背后大概率跑着一个带RNN层的模型。这不是因为RNN有多酷炫,而是它在处…

2026/10/20 13:44:14 阅读更多 →
电子元器件基础知识总结

电子元器件基础知识总结

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 13:44:14 阅读更多 →
PhotoScan相机标定与畸变改正:从空三解算到干净影像的完整流程

PhotoScan相机标定与畸变改正:从空三解算到干净影像的完整流程

简介:面向Photoscan(现Metashape)用户的相机标定与畸变改正操作指南,内容聚焦三维重建中获取高精度模型的核心流程,适合测绘、无人机航测及三维建模初学者与工程师参考。资源为单个docx文档,约2.37MB&#…

2026/10/20 13:44:14 阅读更多 →

最新新闻

百度网盘免费发放500G空间了,大家记得及时领取

百度网盘免费发放500G空间了,大家记得及时领取

话不多说,直接上图免费网盘空间500G是有30天时效性的,不过每个自然月都是可领取的,大家过期之后记得重新领取即可。比较适合短期应急使用,全程免费,官方长期活动。

2026/10/20 20:03:02 阅读更多 →
快慢指针找环:为什么重置指针后能精确找到环入口?

快慢指针找环:为什么重置指针后能精确找到环入口?

很多同学在接触“快慢指针找环”这个经典问题时,都是靠背代码过关的。第一阶段:慢指针每次走一步,快指针每次走两步,如果两个指针相遇了,说明链表有环;第二阶段:把某个指针重置到链表头&#xf…

2026/10/20 20:03:02 阅读更多 →
书匠策AI实操指南:从选题到答辩的课程论文全流程

书匠策AI实操指南:从选题到答辩的课程论文全流程

每次课程论文季,朋友圈都会哀嚎一片。找文献找到眼瞎,列提纲列到怀疑人生,写到结论时突然发现引言和正文对不上,这种事情几乎人人遇到过。我见过太多同学卡在第一步,题目拍脑袋定,框架东拼西凑,…

2026/10/20 20:03:02 阅读更多 →
课程论文智能导航:从选题到降重,书匠策AI全流程实操指南

课程论文智能导航:从选题到降重,书匠策AI全流程实操指南

每到期末,图书馆里总会冒出一群“论文难民”:选题方向模糊、文献读不完、框架搭不起来、初稿憋了三天还是三行字。熟悉的朋友问我在用什么工具,我翻来覆去就推荐同一款——书匠策AI。它不是那种一键生成全文的“代写引擎”,更像一…

2026/10/20 20:03:02 阅读更多 →
ContextCapture安装避坑指南:版本选择、驱动适配与服务配置

ContextCapture安装避坑指南:版本选择、驱动适配与服务配置

简介:本资源提供ContextCapture软件的官方可用下载地址及配套获取指引,面向测绘、三维建模、BIM与数字孪生领域的工程师、高校师生及GIS从业者,解决正版软件获取渠道不明确、网盘链接失效或安装路径不明等实际问题。压缩包仅2KB,含…

2026/10/20 20:02:01 阅读更多 →
OCR模型训练数据不够?用数据生成工具批量造数据实战

OCR模型训练数据不够?用数据生成工具批量造数据实战

OCR模型训练最头疼的从来不是网络结构设计,而是标注数据不够。我做过好几个文字识别项目,每次卡在瓶颈上的几乎都是同一件事:真实场景的样本太少,标注成本太高,模型在测试集上看着还行,一上真实业务就崩。后…

2026/10/20 20:02:01 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →