尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
基于深度学习的3D MRI分类:从数据集预处理到模型训练与评估
简介本项目是一套面向医学影像分析入门者与深度学习初学者的3D MRI分类演示代码包聚焦将对比学习中的InfoNCE损失扩展至弱监督场景利用受试者年龄、性别等辅助信息改进数据表示。压缩包共12个文件涵盖Python源码、图解图片与说明文档其中Python脚本覆盖数据加载、模型训练、损失设计与配置管理全流程README文档提供完整搭建教程可帮助使用者快速复现并理解y-Aware InfoNCE思路。资源体积仅496KB轻量易用目前已吸引217人学习浏览。通过完整代码注释、UMAP可视化结果与性能对比图像读者可直观看到基于年龄感知的对比学习在精神分裂症、双相障碍等分类任务上的优势并可将内含数据集与模型迁移至自己的医学影像项目中适合作为快速原型验证、课程设计或进一步研究的参考基线。1. 基于深度学习的3D MRI分类数据集和任务为什么要一起打包拿到一个标题叫“基于深度学习的3D MRI分类内含数据集.zip”的压缩包很多人第一反应是把它当成一个黑盒项目解压、跑一遍、出个准确率。但对一线工程师来说这个标题真正值得拆解的是三件事数据长什么样、3D模型怎么写、结果怎么评估才不算自欺欺人。MRI并不是简单的二维切片堆叠它本身是三维体素数据层与层之间的连续性往往比单张切片内的纹理更重要如果只拿2D卷积逐层分类等于主动扔掉了空间上下文。所以“基于深度学习的3D MRI分类”是一个典型的三维医学图像分类任务常见落地场景包括阿尔茨海默症识别、脑肿瘤良恶性判断、膝关节退行性病变分级等。本文就从解压数据集开始把NIfTI读取、3D卷积网络、训练调参、分类评估到最终打包可复现项目这一整条链路讲透适合有2D图像分类基础、想转向3D医学影像的人。2. 3D MRI分类数据集的预处理从NIfTI到可训练的DataLoader2.1 先盘点压缩包里的数据组织方式一个规范的3D MRI分类项目压缩包通常不会把所有文件胡乱塞在一个目录里。你需要先解压并观察目录结构再决定要不要写脚本做统一改名和标签对齐。常见做法是unzip 基于深度学习的3D MRI分类内含数据集.zip -d project tree -L 2 project代码中的unzip命令把压缩包解压到project目录tree展示两层文件结构。这里有一个容易被忽略的坑压缩包内路径如果包含中文在部分Linux服务器上可能出现编码问题导致后续DataLoader找不到文件。我一般会先改成纯英文目录名比如data/、labels/、scripts/避免后续踩编码坑。接下来检查标签文件。大部分MRI分类项目会有一个labels.csv两列分别是subject_id和label。你可以先用pandas快速看类别分布import pandas as pd df pd.read_csv(project/labels.csv) print(df[label].value_counts()) print(df[subject_id].nunique())这一步是为了确认两点类别是否均衡患者是否重复出现。如果同一个患者有多条记录后续划分训练集时必须以患者为单位切分而不是以单条样本为单位否则同一患者的体素会同时出现在训练集和验证集里造成数据泄露模型在验证集上的表现会虚高。2.2 用nibabel读取NIfTI并归一化到统一尺寸MRI最常用的存储格式是NIfTI.nii或.nii.gz一个文件就是一个三维数组加一个仿射矩阵。读取时可以用nibabel也可以使用SimpleITK。推荐前者做轻量加载后者做重采样。因为不同设备扫描出来的体素尺寸不一致比如一个体素是1.2mm×1.2mm×2.0mm另一个是1mm×1mm×1mm直接拿原始数组训练会让网络学到扫描参数而不是解剖结构。所以预处理的第一步是先重采样到统一体素尺寸再统一数组尺寸。import nibabel as nib import numpy as np def load_nii_standard(path, target_shape(128, 128, 128)): img nib.load(path) data img.get_fdata() # 先裁剪到目标范围再补零 shape data.shape slices tuple( slice(0, min(s, t)) for s, t in zip(shape, target_shape) ) data data[slices] pad_width [ (0, max(0, t - s)) for s, t in zip(data.shape, target_shape) ] if any(p for p in pad_width): data np.pad(data, pad_width, modeconstant) # 1%到99%分位归一化避免极值干扰 lo, hi np.percentile(data, [1, 99]) data (np.clip(data, lo, hi) - lo) / (hi - lo 1e-8) return data.astype(np.float32)这段代码做了三件事裁剪或补零到128×128×128用1%和99%分位数做鲁棒归一化最后强制为float32节省显存。这里要注意MRI的灰度值没有统一的物理单位不同样本之间直接线性缩放到0-1并不稳妥因为极端值会影响整体对比度按样本分位数归一化是医学影像里更稳的做法。如果你需要保留各向同性分辨率应该在归一化之前先用SimpleITK.Resample做重采样import SimpleITK as sitk img sitk.ReadImage(path) resampler sitk.ResampleImageFilter() resampler.SetOutputSpacing([1.0, 1.0, 1.0]) resampler.SetSize([128, 128, 128]) resampler.SetInterpolator(sitk.sitkLinear) resample_img resampler.Execute(img) data sitk.GetArrayFromImage(resample_img)这里的参数SetOutputSpacing指定目标体素间距SetSize指定输出数组大小。插值方式选线性插值即可标签图则要用最近邻插值这样不会产生中间类别。2.3 划分训练集、验证集和测试集划分的原则是同一个患者的所有扫描只能出现在同一个集合中。如果labels.csv里没有患者ID需要先根据文件名前缀提取。使用sklearn的train_test_split配合stratify可以保证类别比例在每组中一致from sklearn.model_selection import train_test_split subject_ids df[subject_id].tolist() labels df[label].tolist() train_ids, temp_ids, train_y, temp_y train_test_split( subject_ids, labels, test_size0.3, stratifylabels, random_state42 ) val_ids, test_ids, val_y, test_y train_test_split( temp_ids, temp_y, test_size0.5, stratifytemp_y, random_state42 )这段代码先分30%作为验证加测试再把这30%平均拆成验证和测试。stratifylabels保证了正负样本比例在切分前后不变。如果你的任务是多分类同样适用。接下来是写Dataset。3D数据直接全部加载到内存不现实需要在__getitem__里按需读取import torch from torch.utils.data import Dataset class MRIDataset(Dataset): def __init__(self, ids, labels, data_dir, transformNone): self.ids ids self.labels labels self.data_dir data_dir self.transform transform def __len__(self): return len(self.ids) def __getitem__(self, idx): path f{self.data_dir}/{self.ids[idx]}.nii.gz x load_nii_standard(path) x torch.tensor(x).unsqueeze(0) # (1, 128, 128, 128) y torch.tensor(self.labels[idx], dtypetorch.long) if self.transform: x self.transform(x) return x, yDataset的输出x是(1, D, H, W)因为PyTorch的3D卷积输入要求是(B, C, D, H, W)这里C1代表单通道。后续DataLoader的batch_size不宜设大因为一个128×128×128的float32数组占8MBbatch_size等于8时单是输入就占64MB但这只是显存开销的一部分3D卷积的中间特征图才是大头。下表是一些常用预处理参数配置参数推荐范围说明目标尺寸96~128尺寸越大细节越完整但显存压力指数增长归一化1%~99% 分位数对MRI鲁棒性优于全局min-max训练/验证/测试0.7/0.15/0.15小样本时可改用交叉验证batch_size2~8小显存从2开始重采样间距1.0~1.5mm各向同性有利于3D卷积提示文件读取是训练初期的瓶颈建议把DataLoader的num_workers设为4~8Linux环境下并在示例代码块后加上pin_memoryTrue这样GPU训练时可以避开部分CPU到GPU传输延迟。3. 用3D卷积网络完成MRI分类搭建最小可训练模型3.1 为什么选3D卷积而不是2D逐层分类2D卷积处理MRI时一般有两种做法要么把整个三维体数据拆成多张2D切片独立分类再投票得到最终类别要么取三个正交平面横断面、冠状面、矢状面分别送进2D网络最后融合特征。这两种做法本质上是把空间上下文压缩了。比如一个脑肿瘤是否侵犯周围组织不仅要看某个横断面上的病灶边界还要看它在层与层之间的延伸方向2D卷积无法直接建模这种体素间的邻接关系。3D卷积就是把原来的2D卷积核扩展成3D例如kernel_size3意味着一个卷积核覆盖3×3×3个体素在扫描方向上同样做特征提取。代价很明显参数量和计算量是立方级增长的。一个128×128×128的输入经过第一层池化后变成64×64×64特征图数量如果从1扩到32这一层的内存开销就是32×64×64×64×4字节约32MB看起来还能接受但随着层数加深OOM风险越来越大。因此模型设计上要克制不能把2D ResNet18 直接平移到3D。3.2 用PyTorch搭建轻量3D CNN动手训练前建议先用一个足够小的模型跑通流程再根据效果决定是否加深。下面是一个可以直接用的轻量3D CNNimport torch.nn as nn class Simple3DCNN(nn.Module): def __init__(self, in_channels1, num_classes2): super().__init__() self.features nn.Sequential( nn.Conv3d(in_channels, 32, kernel_size3, padding1), nn.BatchNorm3d(32), nn.ReLU(inplaceTrue), nn.MaxPool3d(2), # 128 - 64 nn.Conv3d(32, 64, kernel_size3, padding1), nn.BatchNorm3d(64), nn.ReLU(inplaceTrue), nn.MaxPool3d(2), # 64 - 32 nn.Conv3d(64, 128, kernel_size3, padding1), nn.BatchNorm3d(128), nn.ReLU(inplaceTrue), nn.AdaptiveAvgPool3d(1) ) self.classifier nn.Linear(128, num_classes) def forward(self, x): x self.features(x) x x.view(x.size(0), -1) return self.classifier(x)这段网络只有三层3D卷积每层之后接BatchNorm和ReLU先下采样再全局平均池化。padding1使得卷积不会改变尺寸只有池化减半。最后AdaptiveAvgPool3d(1)把任意尺寸的特征图压缩成1×1×1然后展平送入全连接分类头。BatchNorm3d对3D输入的计算方式和2D很像但它会统计体素维度的均值方差。ReLU(inplaceTrue)能省一点显存但要注意如果后续要做梯度裁剪或需要原始特征图不要随意改成inplace。如果不想从零设计网络更稳健的路线是使用MONAI或PyTorch官方实现好的3D ResNet# 使用torchvision中自带的R3D模型适用于视频分类 # 它本质是3D卷积可以直接借来改输入通道 from torchvision.models.video import r3d_18, R3D_18_Weights model r3d_18(weightsR3D_18_Weights.DEFAULT) model.stem[0] nn.Conv3d(1, 64, kernel_size(3,7,7), stride(1,2,2), padding(1,3,3), biasFalse) model.fc nn.Linear(model.fc.in_features, num_classes)这里把第一层卷积的输入通道从3改成了1然后替换最后的全连接层。r3d_18是在视频数据集上预训练过的虽然域不同但底层的边缘和纹理滤波器可以直接复用如果微调得当可以比从头训练收敛得更快。3.3 训练循环和损失函数配置分类任务最常见的损失是交叉熵。但MRI分类通常面临严重的不平衡比如健康样本占90%患病样本占10%。直接用普通交叉熵会让模型学到“永远预测健康样本”所以需要给少数类更高的权重。权重的计算通常采用负样本数与正样本数的比例import torch from torch import nn pos_count 1000 neg_count 9000 class_weights torch.tensor([neg_count / (pos_count neg_count), 2 * pos_count / (pos_count neg_count)]) loss_fn nn.CrossEntropyLoss(weightclass_weights)这里把第一类的权重设为负样本占比的缩放第二类权重乘了2实际使用时可以根据验证集F1再微调。CrossEntropyLoss的weight参数会在计算梯度时对每一类的loss做乘法等同放大少见类的信号。训练循环只需要覆盖基本流程前向、反向、更新、验证。一个精简的版本如下device torch.device(cuda if torch.cuda.is_available() else cpu) model Simple3DCNN().to(device) optimizer torch.optim.Adam(model.parameters(), lr1e-4, weight_decay1e-5) scheduler torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, modemin, factor0.5, patience3) for epoch in range(30): model.train() train_loss 0 for x, y in train_loader: x, y x.to(device), y.to(device) optimizer.zero_grad() out model(x) loss loss_fn(out, y) loss.backward() optimizer.step() train_loss loss.item() * x.size(0) train_loss / len(train_loader.dataset) # 验证 model.eval() val_loss 0 correct 0 total 0 with torch.no_grad(): for x, y in valid_loader: x, y x.to(device), y.to(device) out model(x) loss loss_fn(out, y) val_loss loss.item() * x.size(0) pred out.argmax(dim1) correct (pred y).sum().item() total y.size(0) val_loss / len(valid_loader.dataset) acc correct / total scheduler.step(val_loss) print(fEpoch {epoch1}, train loss{train_loss:.4f}, val loss{val_loss:.4f}, val acc{acc:.4f})这里有个细节torch.optim.lr_scheduler.ReduceLROnPlateau的step接收的是当前验证指标这里传的是验证损失所以modemin是让学习率在val loss不再下降时减半。如果只想按epoch降低学习率用StepLR也可以但ReduceLROnPlateau在医学小数据集上更常见。3D模型的一个通病是显存暴增。以batch_size4、输入128×128×128为例中间特征图在第三层卷积后是128×32×32×32单张约0.5MB几十个特征图加起来再乘上batch size很容易在backward时翻倍。如果遇到OOM先把batch_size降到2再用混合精度训练。3.4 模型保存和最佳checkpoint策略训练医学模型时验证集准确率最高的epoch不一定是模型泛化能力最好的epoch。建议同时保存验证损失最低的模型以及验证AUC最高的模型。保存方式有两种best_val_loss 1e9 # 每个epoch结束时 if val_loss best_val_loss: best_val_loss val_loss torch.save({ epoch: epoch, model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), val_loss: val_loss, class_names: class_names }, best_model.pth)保存整个字典比单独保存state_dict要稳因为以后可以接着恢复训练。这里的optimizer_state_dict是为了继续训练class_names是为了推理时能映射回真实类别标签。提示保存模型前一定先跑一遍验证集确认推理代码没问题否则到了部署阶段才发现标签错位就晚了。4. 3D MRI分类模型的评估与调优指标、交叉验证和迁移学习4.1 分类评估指标不能只看准确率准确率在医学影像分类里是最容易骗人的指标。假设患病样本只占5%模型全都预测健康也能有95%准确率但分类任务毫无意义。所以评估时至少要报告混淆矩阵和AUC。以下代码可以在验证或测试集上运行from sklearn.metrics import classification_report, roc_auc_score, confusion_matrix y_true_all [] y_prob_all [] model.eval() with torch.no_grad(): for x, y in test_loader: x, y x.to(device), y.to(device) out model(x) prob torch.softmax(out, dim1)[:, 1].cpu().numpy() y_true_all.extend(y.cpu().numpy()) y_prob_all.extend(prob) y_true_all np.array(y_true_all) y_prob_all np.array(y_prob_all) y_pred_all (y_prob_all 0.5).astype(int) print(classification_report(y_true_all, y_pred_all)) print(AUC:, roc_auc_score(y_true_all, y_prob_all)) print(confusion_matrix(y_true_all, y_pred_all))这里的prob是正类的概率也就是模型输出经过softmax后的第二维。classification_report会输出每类的精确率、召回率和F1-scoreroc_auc_score需要输入真实标签和预测概率注意不能传预测类别否则会丢失排序信息。阈值默认是0.5但如果类别不平衡可以考虑在验证集上寻找最佳阈值让F1最大。4.2 用StratifiedKFold交叉验证提升可信度如果整个数据集只有几百例一次随机划分训练集很可能不够稳定。常见做法是把数据划分成交叉验证的折每一折都独立训练最后把验证结果合并起来计算总体AUC。这样可以评估模型在不同子样本上是否都能够稳定表现。from sklearn.model_selection import StratifiedKFold skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) fold_pred {} fold_prob {} for fold, (train_idx, val_idx) in enumerate(skf.split(subject_ids, labels)): train_ids_fold [subject_ids[i] for i in train_idx] val_ids_fold [subject_ids[i] for i in val_idx] train_labels_fold [labels[i] for i in train_idx] val_labels_fold [labels[i] for i in val_idx] # 构建对应Dataset和DataLoader训练当前fold model Simple3DCNN() train_fold(model, ...) # 保存验证结果 val_probs infer(model, val_ids_fold) fold_prob[fold] val_probs fold_pred[fold] np.array(val_labels_fold) # 合并所有折的概率并计算AUC all_probs np.concatenate(list(fold_prob.values())) all_labels np.concatenate(list(fold_pred.values())) print(5-fold AUC:, roc_auc_score(all_labels, all_probs))这段代码使用了StratifiedKFold它会在保持类别比例的前提下切分数据。注意切分对象是subject_ids而不是每个样本的路径否则同一个病人的不同扫描可能会被分到多个fold效果会被高估。4.3 3D数据增强在体素空间里做变换医学图像分类的数据增强不能像2D那样随便裁剪翻转因为体素间的坐标方向是有解剖学意义的过度旋转可能导致左右脑翻转。常用的增强操作有随机左右翻转、小幅旋转、加噪声、弹性形变。但弹性形变在3D上计算很慢一般先用简单操作。import random import torch def augment_3d(x): # 随机左右翻转以轴向为轴 if random.random() 0.5: x torch.flip(x, dims[2]) # 随机加入高斯噪声 if random.random() 0.3: noise torch.randn_like(x) * 0.01 x x noise # 随机灰度平移 if random.random() 0.3: x x 0.02 * torch.randn(1) return x这里的dim2是深度的方向如果你的数据是[通道, 深度, 高, 宽]排列需要确认翻转方向是左右还是上下。torch.randn生成的高斯噪声在0.01标准差下不会破坏整体结构但能提高鲁棒性。灰度平移相当于轻微调整了MRI的对比度。增强操作必须放在Dataset的transform里而不是在训练循环中随机改变否则每次epoch看到的样本可能是不同的。验证集和测试集不应该做随机增强只做固定归一化。4.4 用迁移学习和超参数调优补足小样本短板3D医学影像数据集通常只有数百甚至几十个样本从头训练一个深层3D网络非常容易过拟合。常见的迁移学习路线有两种一种是使用在视频数据集上预训练的3D CNN模型比如r3d_18另一种是先用自监督学习在同一数据集上预训练一个3D自编码器把编码器部分作为分类模型的特征提取器然后在少量标签上微调。自编码器的重建任务不需要类别标签对未标注的MRI也可以做。超参数调优方面可以用ray[tune]做贝叶斯搜索但很多项目手动调整几个关键参数就够了。重点是学习率和batch size。3D网络显存受限batch size基本固定在2到8之间这时学习率不能按默认值0.001走因为batch size小梯度噪声大建议从1e-4开始。学习率衰减使用ReduceLROnPlateaupatience设置在3到5个epoch之间。下表给出了常见参数的参考范围超参数范围备注初始学习率1e-4 ~ 3e-4batch size 2~8 时不要用1e-3batch size2 ~ 8128^3输入下超过8大概率OOM优化器Adam或AdamWweight_decay 1e-5 ~ 5e-4epoch20 ~ 50早停观察验证集loss数据增强强度小幅旋转15度内大角度会破坏解剖方向5. 如何避免3D分类实验白做数据泄露检查与显存优化技巧这是整篇文章最值得收藏的一节也是很多3D MRI项目最终翻车的根源。第一个最容易犯的错是数据泄露。前面说过要按患者划分数据集但你以为做了就完了吗有些压缩包里的文件名不是患者ID而是一个session ID同一个患者可能在多个session下重复出现。你需要用文件名前缀或者额外的元数据来确认。在开始训练前先把所有subject_id做一次哈希散列手动检查重复值。代码参考s df.groupby(patient_id)[session_id].count() print(s[s 1])如果发现了重复patient_id那么所有涉及该病人数据的增强和划分操作都必须以patient_id为最细粒度。第二个高频问题是显存OOM。3D卷积的梯度计算会占用约两倍于前向的内存。如果提示CUDA out of memory别急着买新卡先做三件事把batch_size降到2把输入尺寸从128降到96把torch.utils.data.DataLoader的num_workers调低。还可以在训练循环内使用梯度累积用两步小batch模拟大batchaccumulation_steps 4 optimizer.zero_grad() for i, (x, y) in enumerate(train_loader): x, y x.to(device), y.to(device) out model(x) loss loss_fn(out, y) / accumulation_steps loss.backward() if (i 1) % accumulation_steps 0: optimizer.step() optimizer.zero_grad()这里把loss除以accumulation_steps再累积4次梯度后更新参数等效于batch_size乘以4。缺点是BatchNorm的统计量仍然基于单个小batch感受野可能不稳定如果模型里用了BatchNorm建议改成GroupNorm或LayerNorm或者把输入尽量放在一个batch里。第三个问题是打包可复现项目时很多人只把代码和数据打包成一个zip就说完成。这在工程上是不可复现的。一个合格的基于深度学习的3D MRI分类内含数据集.zip至少应该包含data/目录下原始或预处理后的NIfTI文件、labels.csv、requirements.txt或environment.yml、train.py、evaluate.py、README.md说明目录结构和超参数。同时要记录数据切分的随机种子否则别人拿到同样的数据解压后跑出来的验证结果和你对不上。最后可以在evaluate.py里增加一个简单的文件哈希校验确保数据没有在传输中被改动md5sum project/data/*.nii.gz | sort checksum.txt把它也打包进zip。这样别人解压后可以直接比对校验和排查是数据损坏还是训练代码问题。当你能把整个实验流程复现到这种程度这份3D MRI分类项目才算是真正交付完成。本文还有配套的精品资源点击获取
RELATED

相关推荐

降AI率工具实测:10款AI写作改写助手效果对比与避坑指南

降AI率工具实测:10款AI写作改写助手效果对比与避坑指南

去年有个学弟来找我,说他毕业论文第二部分被导师圈了一整页,旁边批了四个字:“很像AI写的”。他觉得很冤,因为他确实没有拿AI整段生成,只是让AI帮忙扩了提纲,又自己改了两遍。我拿过来一看,问题…

📅 2026/9/14 7:35:45
AI Agent记忆系统设计实战:分层、存储与召回

AI Agent记忆系统设计实战:分层、存储与召回

1. 定制记忆机制:Agent 为什么必须“记住你” 如果你玩过几款对话式 AI 工具,大概会有一种很典型的感觉:刚聊完一个复杂需求,换个会话再问,它就像失忆了一样,又得从头交代背景。最开始我以为是产品设计缺陷…

📅 2026/9/14 7:35:45
混合路由架构:RAG知识库与NL2SQL统一智能问答方案

混合路由架构:RAG知识库与NL2SQL统一智能问答方案

最近在整理企业数据助手项目时,我发现自己陷入了一个不算新、但特别典型的困境:业务方对“知识”的需求和对“数据”的需求,原本是两套系统分别在满足,可我越做越觉得哪里不对劲。我们当时给公司搭了一个基于RAG知识库的智能问答机…

📅 2026/9/14 7:35:45
MORE NEWS

更多资讯

📰

用Python手写BP神经网络实现鸢尾花分类:从原理到调参

简介:面向Python初学者的人工智能实践项目,使用BP神经网络对经典鸢尾花数据集进行分类,配套完整源码、数据集和文档说明,可满足期末大作业、课程设计等场景。除BP神经网络两个版本(V1/V2)外,还提…

📰

基于GPT-6 Astra的跨平台GitHub查询机器人:QQ与飞书双端实现

上个月我把公司内部使用的 GitHub 辅助查询机器人从单一聊天工具迁移到了 QQ 和飞书双端,同时接入了 GPT-6 Astra 的智能体能力。现在同事在 QQ 群里发一句“帮我看下 fastapi 这个仓库最近的 issue 情况”,机器人会自动调用 GitHub API、拉取数据、再交…

📰

Python毕业设计:恶意代码检测分类平台搭建与实现

简介:面向计算机相关专业毕业设计的高分项目源码包,以Python实现恶意代码检测与分类平台,适用于正在准备毕设、课程设计或期末大作业的学生。项目经导师指导认可,评审分97分,覆盖数据预处理、模型训练、分类识别等完整…

📰

二分查找算法原理、实现与优化指南

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

📰

Doris+Lance实现毫秒级跨模态联合检索

1. 这不是又一个“多模态数据库”概念秀,而是智能驾驶与具身智能落地的硬性瓶颈被捅破了我第一次在某头部自动驾驶公司数据平台组看到他们用 Apache Doris Lance 搭建的实时感知日志分析链路时,第一反应是:这玩意儿居然真能跑通?…

📰

移动应用安全测试全流程指南与最佳实践

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

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬