news 2026/9/28 3:12:05

脑肿瘤分割与生存预测:基于BraTS数据集的2D/3D-UNet与VNet实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
脑肿瘤分割与生存预测:基于BraTS数据集的2D/3D-UNet与VNet实现

简介:这套毕设项目围绕脑肿瘤分割与生存预测展开,提供完整的多模型对比研究源码。项目整合了二维U型网络、三维U型网络与三维V型网络三种经典分割架构,并额外加入基于临床数据的生存预测模型,内容覆盖数据预处理、模型搭建、训练评估与结果可视化。资源包共计50个文件,包含Python程序文件、Jupyter Notebook、编译后的pyc、模型权重joblib、实验输出图片、数据表格、项目报告和说明文档,压缩包大小约3.05MB。源码经过严格测试,功能完善,运行稳定,易复现,既适合人工智能、通信工程、自动化、电子信息等专业的高校学生作为毕业设计或课程设计;也适合科研人员和行业从业者进行算法对比或二次开发。项目内提供了数据生成脚本、评估指标函数、多轴向分割结果展示以及生存数据的回归与分类示例,配套的PDF报告和说明文档能显著降低上手难度。目前已有93人浏览学习,是快速进入医学图像深度学习领域的实用资料。

1. 为什么这个毕设标题值钱:三点共识

脑肿瘤分割是医学影像分割里少有的“数据规范、任务闭环、能直接出指标”的方向,而这个标题把三件事凑齐了:用2D-UNet 跑基线、用 3D-UNet 和 3D-VNet 做体素级分割、再把分割结果接到生存预测模型。做这个方向的人多数不是医学背景,而是奔着“UNet 改进”和“源码可复现”来的——它能在有限算力下榨出完整的训练、评估、预测闭环,毕设答辩时有模型对比、有曲线、有报告,这是它长期热门的原因。适合谁?适合有二三十 G 显存(或租得到云卡)、想在一两个月内做完实验并拿到完整论文素材的同学。接下来我会按我自己的实现路径,把数据、改网络、调显存、避坑、接生存预测五段讲透,全部按可复现的标准写。

2. 把数据先喂明白:BraTS 加载与预处理的两个关键步骤

2.1 通道顺序与标签编码:先搞懂 4 个模态和 3 个标签

这个标题绑定的数据集大概率是 BraTS(脑肿瘤分割挑战赛),也是百度学术和 GitHub 上“脑肿瘤分割源码”最常见的训练数据。BraTS 每个病例给 4 个配准好的模态:T1、T1ce、T2、FLAIR,各自捕捉肿瘤不同部位的对比度差异。通道顺序在预处理阶段就得固定,不然后面模型学到的特征会乱——常见做法是把四者按[T1, T1ce, T2, FLAIR]拼成4 x H x W x D的输入。

标签编码是另一个新手容易看懵的地方。BraTS 原始 label 里通常有 0、1、2、4 四个值,对应背景、水肿(ED)、增强肿瘤(ET)、坏死核心(NCR)。很多源码在训练前会把标签重新映射成三分类任务:整个肿瘤(WT)= 1+2+4,肿瘤核心(TC)= 2+4,增强肿瘤(ET)= 4。这是被反复验证过的做法,论文里常写成“whole tumor / tumor core / enhancing tumor”三个区域分别算 Dice。

import nibabel as nib import numpy as np def load_brats_case(case_dir): mods = [] for name in ["t1", "t1ce", "t2", "flair"]: img = nib.load(f"{case_dir}/{case_dir}_{name}.nii.gz") mods.append(img.get_fdata().astype(np.float32)) image = np.stack(mods, axis=0) # 4 x H x W x D label = nib.load(f"{case_dir}/{case_dir}_seg.nii.gz").get_fdata().astype(np.uint8) return image, label def remap_label(label): wt = (label > 0).astype(np.uint8) tc = np.isin(label, [2, 4]).astype(np.uint8) et = (label == 4).astype(np.uint8) return np.stack([wt, tc, et], axis=0) # 3 x H x W x D

这段代码把 4 个模态堆成多通道输入,同时把标签重映射成 3 个二值图。np.stack的维度顺序是[通道, H, W, D],后面凡是使用NCDHW格式的网络(PyTorch 3D 卷积默认格式)都能直接喂。注意np.isin(label, [2, 4])这一步很容易写错成label == 2 or label == 4,那会触发 NumPy 的布尔歧义报错。

预处理里还有一个不少人忽略的细节:数据范围。BraTS 原始图是 MRI 的体素强度,不是 0~255 的 PNG,很多病历间强度范围差异很大。我在预处理阶段对每个模态分别做(x - min) / (max - min)缩放到 0~1,再算整体均值和标准差做标准化。如果直接拿原始强度训练,2D-UNet 基线往往会在验证集上出现异常波动——这属于正常现象,不用怀疑网络写错了。

2.2 patch 采样与归一化:3D 模型能不能收敛,一半看这里

3D 数据直接整图输入是做不到的,一张 BraTS 的 MRI 通常是240 x 240 x 155,单精度浮点就有 34MB 以上,模型体积远大于此。因此常见做法是随机裁剪 patch 训练 + 滑窗推理。patch 大小是个强相关参数:2D-UNet 可以用128 x 128的切片,3D-UNet 我一般用128 x 128 x 64,3D-VNet 则降到128 x 128 x 64配合 batch size 2,否则显存直接爆掉。

import numpy as np from scipy import ndimage def sample_patch(image, label, patch_size=(128, 128, 64)): c, h, w, d = image.shape ph, pw, pd_msg = patch_size # 让 patch 有更高概率落在肿瘤区域附近 tumor_mask = (label > 0).any(axis=0) # H x W x D coords = np.argwhere(tumor_mask) if len(coords) > 0: center = coords[np.random.randint(len(coords))] else: center = [h // 2, w // 2, d // 2] # 裁剪范围,越界处理 h0 = np.clip(center[0] - ph // 2, 0, h - ph) w0 = np.clip(center[1] - pw // 2, 0, w - pw) d0 = np.clip(center[2] - pd_msg // 2, 0, d - pd_msg) image_p = image[:, h0:h0+ph, w0:w0+pw, d0:d0+pd_msg] label_p = label[:, h0:h0+ph, w0:w0+pw, d0:d0+pd_msg] return image_p, label_p

这段采样逻辑里有两个参数值得调整:一是tumor_mask用(label > 0).any(axis=0)而不是直接取某个类别,是因为 BraTS 的肿瘤区域在三个标签上有重叠,任何一类阳性都值得采样;二是越界后的np.clip会导致 patch 贴边,如果某个病例肿瘤恰好贴近边界,重复采样会引入偏差,我会在采样前先把图像用np.pad反射填充比 patch 略大的边缘,这样越界问题就消失了。

归一化建议在每个 patch 上做,而不是在整幅图上做。局部归一化能弱化 MRI 偏场效应;全局归一化保留更多绝对强度信息。我在实践中两者都试过,patch 级归一化在 2D-UNet 上平均能涨 1~2 个点 Dice。注意验证和测试时用的归一化统计量必须来自训练集,不能拿当前病例自己算,否则等于数据泄漏。

2.3 数据增强的边界:翻转能用,弹性形变要少用

医学图像数据增强的原则是“不做破坏解剖语义的操作”。随机翻转在 3D MRI 上是安全的,因为脑部左右翻转仍然有解剖意义(但需要注意左右标签对应关系);随机 90 度旋转要小心,因为 MRI 有标准的坐标方向,过度旋转会让模型混淆头脚方向。常见做法是只用轴向翻转 + 小幅随机裁剪 + 强度抖动。

弹性形变这类在自然图像分割里效果很好的增强,放在 3D 脑肿瘤上要非常谨慎:肿瘤形态是诊断依据,把肿瘤“拉伸”得太厉害会干扰边缘学习。我在做这个标题的项目时,弹性形变只用在 2D-UNet 的 slice 粒度训练上,3D 模型完全不用,不然验证集 Dice 会异常高——那是增强造成了过拟合错觉,换医院数据就露馅。

数据增强还有一个通用原则:训练样本少的时候增强要做足(这是源码包跑通的关键);训练样本超过 300 例后,过度增强反而拖慢收敛。我一般保持 50 轮以内达到平台期,超过 100 轮不收敛就要回查数据预处理,而不是加更多花活增强。

3. 用 2D-UNet 先跑通基线:最小训练链路与三个必调参数

3.1 按 slice 切图的数据管线:为什么先做 2D 不是浪费时间

拿到源码包第一件事不是调 3D 模型,而是先用2D-UNet 跑通一条最小链路。2D 方案把每个病例切成多个H x W的 slice,训练数据量瞬间扩大一个数量级,收敛快、显存占用小、调试成本低。这个做法的价值是验证数据管线有没有问题——如果 2D 出来 Dice 不对,3D 模型大概率也训练不起来。

from torch.utils.data import Dataset, DataLoader import numpy as np class Brats2DDataset(Dataset): def __init__(self, image, label): """ image: 4 x H x W x D label: 3 x H x W x D label 每通道是 wt/tc/et 的独立二值图 """ self.slices = [] for z in range(image.shape[-1]): if label[:, :, :, z].sum() > 0: # 只保留有肿瘤的层 self.slices.append((image[:, :, :, z], label[:, :, :, z])) def __len__(self): return len(self.slices) def __getitem__(self, idx): img, lbl = self.slices[idx] # 转换为 CHW 格式,img: 4 x 240 x 240 return img.astype(np.float32), lbl.astype(np.float32)

这段代码有两个设计点:一是只保留含肿瘤的 slice 参与训练,否则大量背景层会让模型偏向全预测背景——这是 2D 方案里提升速度最明显的一招;二是返回值直接是4 x 240 x 240和3 x 240 x 240,PyTorch 的 2D 卷积接收B x C x H x W,只需要在 DataLoader 里torch.stack成 batch 即可。

用 2D 方案跑通模型后,建议直接观察 3 个输出通道的预测图是否对应 WT、TC、ET 的空间包含关系:WT 应该包住 TC,TC 应该包住 ET。如果这个包含关系不成立,说明网络的类别语义没学好,不建议直接上 3D,而是回查标签重映射。

3.2 损失函数用 Dice 还是 CE:类别不均的解法

脑肿瘤分割的类别不均极其严重,背景占比常超过 98%,使用普通交叉熵会让网络把精力全花在背景上。常见做法是使用 Dice Loss 或其变体:它对前景和背景的梯度是平衡的,不需要手动算类别权重。Dice Loss 本身存在小目标不稳定的问题,我会把 Dice 和交叉熵按 1:1 加权,实践证明在 BraTS 上的收敛速度比纯 Dice 快约 20%。

import torch import torch.nn.functional as F def dice_loss(pred, target, smooth=1.0): """ pred: B x C x H x W,已经过 softmax target: B x C x H x W,二值标签 """ intersection = (pred * target).sum(dim=(2, 3)) union = pred.sum(dim=(2, 3)) + target.sum(dim=(2, 3)) dice = (2.0 * intersection + smooth) / (union + smooth) return 1.0 - dice.mean() def combined_loss(pred_logits, target): pred_soft = F.softmax(pred_logits, dim=1) # target 每通道是独立二值图,交叉熵逐通道计算 ce = F.binary_cross_entropy(pred_soft, target) dice = dice_loss(pred_soft, target) return 0.5 * ce + 0.5 * dice

这里用了binary_cross_entropy而不是CrossEntropyLoss,原因是标签是 3 个独立的二值通道(多标签分割),不是互斥的类别索引。smooth=1.0是 Dice 实现经典做法,能避免除零并小幅稳定梯度。如果你的任务需要输出互斥类别(比如像素级三分类),则要改用CrossEntropyLoss并把 label 压缩成单通道索引。2D-UNet 训练时我建议学习率1e-3(Adam),batch size 32,150 轮以内即可停。

3.3 2D 基线常见的三个学习状态:过拟合、不收敛、Dice 死在后两类

跑 2D-UNet 时,训练集 Dice 很快到 0.95,验证集停在 0.8 左右上不去,这是过拟合到 slice 分布的信号。原因在于连续 slice 强相关,网络记住了 slice 编号附近的纹理而不是解剖结构。解决:在 slice 层面做随机采样增强是无效的,真正有效的是打乱病例顺序 + 在 3D 数据里随机抽取不同 z 轴位置,配合Dropout调到 0.1~0.2。

Dice 在 TC 和 ET 两个通道长期低于 0.5,是 2D 方案最常见的卡点。原因很直白:ET 和 TC 区域占体积不到 10%,模型在 patch 采样时容易错过这些区域。解决:采样时按标签通道分别统计肿瘤阳性体素数,三个类别都特别小的病例过采样。这属于数据策略优化,不用改网络即可提升 2~3 个点。

2D-UNet 跑完的正确验收标准是:验证集 WT Dice ≥ 0.88,TC ≥ 0.80,ET ≥ 0.75(BraTS 常见水平)。达不到这个线,3D 模型大概率也难超越,先回头修数据问题,而不是急着换网络。

4. 升级到 3D:3D-UNet 与 3D-VNet 的改造路径、显存调度和残差设计

4.1 从 2D 改到 3D:四步替换与模型配置

3D-UNet 不是新网络,而是把 2D 卷积核从k x k换成k x k x k,池化从 2D 换成 3D,同时所有张量多一个维度。听起来机械,但实际改动里面有四个容易出错的环节:卷积层的padding策略、上采样的对齐方式、跳连拼接的维度、以及输出头的卷积核尺寸。

import torch.nn as nn class DoubleConv3D(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.conv = nn.Sequential( nn.Conv3d(in_ch, out_ch, kernel_size=3, padding=1), nn.GroupNorm(num_groups=8, num_channels=out_ch), nn.ReLU(inplace=True), nn.Conv3d(out_ch, out_ch, kernel_size=3, padding=1), nn.GroupNorm(num_groups=8, num_channels=out_ch), nn.ReLU(inplace=True), ) def forward(self, x): return self.conv(x)

3D 卷积的padding=1、kernel=3是保持空间尺寸不变的默认组合。如果你把 padding 设成 0,下采样一次后特征图尺寸就会对不上;如果改成padding=2,等效感受野变大但显存显著上升。GroupNorm在 batch size 小的 3D 场景下比BatchNorm稳定,这是因为 3D 模型通常 batch size 只有 2 左右,BN 统计量噪声很大。

完整的 3D-UNet 编码器一般做 4 次下采样:输入4 x 128 x 128 x 64→ 16 通道 → 32 → 64 → 128 → 256,底层的 256 通道特征图尺寸约8 x 8 x 4,显存占用主要来自前两层。如果你只有 16GB 显存,建议首层通道数从 16 砍到 12,或者省略最后一次下采样,换来的 Dice 损失通常不到 1 个点。

4.2 3D-VNet 与 3D-UNet 的结构差异:残差、卷积拼接和参数量

3D-VNet 可以理解为 3D-UNet 的“残差 + 体积卷积”变体:每一层内部用残差连接,卷积核用k x k x k,并且编码器和解码器之间的跳连更密。VNet 原文面向前列腺分割,但用在 BraTS 上的效果和 3D-UNet 基本相当,结构差异却非常值得写进论文对比:VNet 在编码器每个 stage 输出后带一个残差加和,解码路径里还做了“深层特征和浅层特征逐元素相加”而不是 UNet 的 concat 拼接。

我在实际对比中发现一个明确规律:UNet 的 concat 在跳连信息保存上更好,VNet 的残差设计在深层梯度传递上更顺。显存紧张时 VNet 通常会占用更小,因为它避开 concat 带来的通道数翻倍。BraTS 任务里两者都能跑到 0.85+ 的 Dice,差异主要体现在 3D-VNet 对偏小肿瘤区域的轮廓还原略好一些,这是一个可以写进报告的真实对比结论。

关于参数一说,三维卷积的参数膨胀远大于 2D。2D-UNet 参数量约 700 万 ~ 1000 万,3D-UNet 通常会到 1500 万 ~ 2500 万,3D-VNet 介于两者之间。如果你在做源码对比实验,务必在报告里把参数量和 FLOPs 一起列出,不要只比 Dice。

4.3 patch size、batch size 与混合精度显存三部曲

显存不足是本标题下最典型的问题。我来给一个参考配置:3D-UNet 使用输入 patch128 x 128 x 64,batch size 2,PyTorch 开启torch.cuda.amp混合精度,16GB 显存刚好够训练;32GB 显存可以开 batch size 4 或把 patch 加到160 x 160 x 80(通常能带来 1~2 个点 Dice 提升)。网络首层通道数每增加一倍,显存近似增加 4 倍,调参优先级应该是:先调 batch size,再调 patch size,最后调通道数。

import torch scaler = torch.cuda.amp.GradScaler() for batch in dataloader: img, label = batch img, label = img.cuda(), label.cuda() optimizer.zero_grad() with torch.cuda.amp.autocast(): pred = model(img) loss = combined_loss(pred, label) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()

混合精度在 3D 模型上的加速有 40%~70%,几乎白拿的收益。需要注意的是 loss 数值要注意后半段出现莫名 NaN:常见原因是梯度溢出发生在某些极端强激活区域,把GradScaler的init_scale从默认值 65536 抬到 2**24 通常可缓解。这一步是最值得调的参数,不是学习率而是 scaler 的 scale 上限。

推理阶段的滑窗策略同样耗显存:测试时不能整图送入,要按训练时的 patch 大小滑动切块推理,然后把预测概率图拼回原尺寸。重叠区域用8x8x8的步长做平均,效果比硬切好;边界区域因为裁剪不完整,预测概率偏低,可以在拼接时做高斯加权。这个细节对最终 Dice 的影响可达 0.5~1 分。

5. 脑肿瘤分割训练避坑手册:六个最容易翻车的实操记录

5.1 验证集 Dice 高但测试集全线崩盘

现象:跑 3D-UNet 训练,验证集 WT Dice 到 0.92,一换外部测试病例直接掉到 0.7 以下。原因:验证集和训练集来自同一个 BraTS 病例组,MRI 强度分布相似,模型学到了“整体外观”而不是“肿瘤边界信号”;再加上没有把归一化参数固定到训练集统计量上,测试时统计口径漂移。解决:预处理阶段把训练集的均值、方差存成 numpy 文件,验证和测试时从文件读取并应用到数据上;同时按医院或扫描仪来源划分训练/验证,不做随机切分。

5.2 3D-VNet loss 在 20 轮后突然变成 NaN

现象:前 20 轮正常,第 22 轮 loss 变成 nan,后续无法恢复。原因:(1)输入图像里有极端高亮体素,除以小方差后产生极大值,卷积输出溢出;(2)Adam 的 epsilon 太小,梯度二阶矩接近零;(3)VNet 残差连接导致激活值不断累加,深层特征数值爆炸。解决:在输入管道里做np.clip(image, 0, 4)后再归一化;Adam 的 epsilon 从1e-8改到1e-4;在编码器每个 stage 输出后用一次nn.ReLU而不是原始 VNet 的 PReLU,数值稳定性显著提升。

5.3 2D-UNet 训练时 CPU 数据加载比 GPU 计算还慢

现象:GPU 利用率只有 30%,训练一个 epoch 要一个多小时,监控发现 CPU 瓶颈。原因:BraTS 的.nii.gz是压缩文件,nibabel每次读取都做全量解压和类型转换,耗时极高。解决:第一次加载后把所有图像和标签转存成.npy格式,每例约几 MB,后续np.load比nibabel.load快一个数量级;DataLoader设置num_workers=8、prefetch_factor=4。如果仍然慢,可以提前把 patch 采样结果缓存到内存字典,空间换时间。

5.4 多卡数据并行训练时 loss 突然异常升高

现象:单卡跑得好好的,改DataParallel多卡后 loss 比预期高出 30%。原因:DataParallel对输入第一个维度切成多份,但 BraTS 数据处理时的“通道维度”在第二维([B, C, H, W, D]),如果 DataLoader 输出的 batch 里没有把样本维度放第一位,多卡切分时会切到通道上。解决:在Dataset.__getitem__返回单样本,用DataLoader的collate_fn做torch.stack保证B在首位;同时 3D 数据建议直接换成DistributedDataParallel,它不仅正确切分 batch,还顺带解决DataParallel的显存负载不均衡问题。

5.5 训练 200 轮后验证集 Dice 波动超过 2 个点

现象:训练曲线整体收敛,到后期验证集 Dice 上下抖动 2 分以上,无法稳定选点。原因:(1)patch 采样有随机性,每个 epoch 看到的训练子集不同,造成验证集评估震荡;(2)学习率过大,后期模型在损失曲面震荡跳不出局部区域;(3)验证集样本量太少,约 15 例时 Dice 方差极高。解决:学习率余弦退火从1e-4降到1e-6,保存训练最后 10 个 epoch 的模型并在验证集上取平均效果最稳定;验证时固定随机种子,确保每个 epoch 采的是同一组验证病例。

5.6 换了自己的数据(非 BraTS)后完全跑不通

现象:把源码包的数据换成自己的 MRI 数据集,loss 不降或 Dice 极低。原因:BraTS 预处理假设数据已配准、已重采样至一致分辨率、头朝向一致,自己的数据往往没有经过这些步骤。解决:先用nibabel或SimpleITK把自己的数据重采样到1mm x 1mm x 1mm各向同性;再统一坐标方向到 LAS(左侧 + 后方 + 上方),最后检查标签的取值范围,如果不是 0/1 二值,先用阈值label > 0.5转成 mask 再训练。别忘了把 4 通道输入改成自己数据的通道数,否则第一层卷积会直接报维度不匹配。

6. 把分割结果接到生存预测模型:特征怎么提、Cox 怎么训、结果怎么验

BraTS 挑战赛里除了分割还有一个任务:基于术前 MRI 预测患者生存期。标题里出现“生存预测模型”,意味着源码包里往往用分割网络作为前置骨干——先用分割模型预测肿瘤区域,再计算影像组学特征或体积特征,最后喂给 Cox 回归或随机生存森林。这里给一套可复现的最小链路。

from lifelines import CoxPHFitter import pandas as pd # 特征表:每行是一个病例 # 列:WT_volume, TC_volume, ET_volume, age, 以及若干影像特征 df = pd.read_csv("features.csv") df["event"] = df["vital_status"] # 1=死亡,0=删失 df["survival_months"] = df["survival_days"] / 30.0 cph = CoxPHFitter() cph.fit(df[["WT_volume", "TC_volume", "ET_volume", "age", "survival_months", "event"]], duration_col="survival_months", event_col="event") print(cph.summary)

这里的lifelines库是生存分析最常用的工具,拟合速度也快。如果你拿到的源码包没有接好特征表,至少你要从分割结果中计算每个病例的 WT、TC、ET 体积百分比(除以颅腔体积归一化)——这个特征在大多数公开实验里排进前三重要。年龄特征如果有,务必加入;影像组学特征(如 GLCM 纹理)可以从预测的 ET 区域提取,代码量不大但对模型贡献明显。

生存预测模型在毕设报告中的验证方法不用太复杂:计算C-index(一致性指数),0.6 以上即为可用,0.7 以上算优秀。常见的坑有两个:一是生存时间和删失标签没有对齐,注意event=1表示发生了终点事件(死亡),event=0表示删失;二是训练/验证划分时要按病例而不是按 patch 划分,否则同一病例的不同 patch 会泄漏到两个集合里,C-index 虚高 0.1 以上。

最后一件事是验证你的整条链路是否可靠:把分割模型的预测结果和真实标签分别提取特征,各训练一个生存模型,比较两者的 C-index 差异。如果预测分割得到的 C-index 显著低于真实分割,说明瓶颈在分割精度,先回头提升网络而不是继续调生存模型。如果是用多模态 MRI 的原始图像提了一堆手工特征,和我的习惯不符——我一般只做体积特征加少量纹理特征,效果可解释、不容易过拟合,方便报告里画图。

这套从分割到生存预测的链路,我能复现到的合理结果是:BraTS 验证集分割 WT Dice 0.87~0.90,C-index 0.62~0.68。把这些指标写清楚,配好源码目录和报告,整个项目就是完整可交付的毕设。最后提醒一句:拿到别人的源码包先看“数据路径和预处理模块”,不看模型文件——数据通了模型都能跑,这个顺序我拿血泪验证过。希望帮到你。

本文还有配套的精品资源,点击获取

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

计算机行业高质量知识网站推荐(2026版)

计算机行业高质量知识网站推荐(2026版) 按访问难度和内容类型分类,优先推荐国内可直接访问的优质资源一、国内可直接访问的优质网站 系统学习类网站网址核心价值适合场景菜鸟教程runoob.com基础语法在线实例,覆盖主流语言快速入门…

作者头像 李华
网站建设 2026/9/28 3:10:20

this指针的认识+使用

关于C的this指针学习总结需要回答的问题this指针的认识this指针的本质(一句话总结)this的类型this的存储位置this指针的作用1.区分同名变量2.链式调用/连续赋值/连续调用(return *this)3.访问当前对象的地址或成员this指针的使用(this指针与c…

作者头像 李华
网站建设 2026/9/28 3:09:41

Sphinx 集成 Markdown:基于 MyST-Parser 的 Markdown 文档构建实战指南

文档开发工具 【免费下载链接】sphinx The Sphinx documentation generator 项目地址: https://gitcode.com/gh_mirrors/sp/sphinx 点击查看 免费下载 本篇指南围绕 Sphinx 项目中的 Markdown 支持展开,讲解如何通过 MyST-Parser 扩展让 Sphinx 直接解析…

作者头像 李华
网站建设 2026/9/28 3:09:27

Jetson Orin实战:为宇树Go2部署YOLOv5目标检测

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

作者头像 李华
网站建设 2026/9/28 3:06:19

YOLO26改进 - C3k2 | C3k2融合LWGA轻量分组注意力(Light-Weight Grouped Attention):四路径并行架构破解通道冗余难题 | AAAI 2026

前言 本文介绍了轻量级骨干网LWGANet及其核心模块LWGA在YOLO26中的结合。现有用于遥感(RS)视觉质量分析的轻量级神经网络存在空间初始冗余和通道冗余问题,无法应对RS场景挑战。LWGA采用异构分组策略,将通道划分为4个不重叠子集,每个子集对应特定特征尺度,通过专用子模块…

作者头像 李华