新闻详情

新闻详情

首页 / 资讯中心 / 详情

工业异常检测 PaDiM 复现:多元高斯与马氏距离定位

发布时间:2026/10/1 14:03:53来源:尧图网络
工业异常检测 PaDiM 复现:多元高斯与马氏距离定位
工业质检这行做久了你会发现一个很尴尬的现实产线上真正能拿到的永远是良品图成千上万张而缺陷图可能一年也就攒出几十张还大概率不重样。这就是异常检测Anomaly Detection在工业视觉里一直绕不开的设定——只用正常样本训练测试时既要判断这张图有没有问题还要把问题圈出来也就是定位Localization。2021 年 ICPR 上那篇 PaDiM: a Patch Distribution Modeling Framework for Anomaly Detection and Localization之所以到现在还被反复提起就是因为它用一套非常轻的思路把这两件事同时做掉了把卷积网络提取的 patch 特征按位置建多元高斯分布测试时算马氏距离。整篇论文的方法朴素得几乎有点可爱但实测指标和推理速度都很能打代码量也小到一个人两三天就能复现出来。这篇我就按自己复现这套框架的完整过程把 Patch Distribution Modeling 里每一个设计选择背后的原因、参数怎么算、坑在哪里一条条摊开讲清楚适合刚接触工业异常检测的新手也适合已经用过 SPADE、想换个更省内存方案的老手。1. PaDiM 到底在解决什么痛点在动手之前得先弄清楚它相对前人到底绕开了什么。2019 年 MVTec AD 数据集放出来之后工业异常检测基本形成了两条路线一种是重构类用自编码器或者 GAN 把输入重建成正常的样子然后拿重建误差当异常分数另一种是特征嵌入类用预训练骨干网络提特征再在特征空间里判断这个特征离正常分布有多远。PaDiM 属于后者而且它把后者的一个关键工程包袱给甩掉了。1.1 重构类方法的两个死穴重构类方法的第一个死穴是太会重建。你训练自编码器最小化重建误差网络学得越好遇到一个划痕它也可能稀里糊涂给你填回去结果异常区域的重建误差反而不大异常图糊成一片。这不是调参能彻底解决的是目标函数决定的。第二个死穴是计算开销逐像素跑解码器224×224 的图要生成 5 万多个像素的输出推理一张图几百毫秒起产线节拍根本吃不住。我早期在一家做 3C 结构件检测的团队待过用自编码器那套跑铝合金外壳划痕不管怎么调学习率细长划痕的定位始终是断断续续的。后来换成特征嵌入路线同样的数据异常图一下子变得连贯了。这种体验上的差别是我后来一直偏好特征嵌入类方法的直接原因。1.2 内存库路线的问题在哪特征嵌入类里最有代表性的是 SPADE。它的做法非常直观把训练集所有正常图的每个位置的特征全部存下来组成一个大内存库memory bank测试时对每个测试 patch 做 K 近邻检索用最近邻距离当异常分数。思路清晰、效果也好但代价很明显——内存库里存的是全部训练特征MVTec 一个类别两三百张图每张 56×56 个位置每个位置 448 维特征算下来是千万级向量的存储和检索。更要命的是推理速度。每个测试 patch 都要和内存库里成千上万个向量比距离虽然可以用 GPU 批量算但整体延迟还是偏高。工程上最难受的一点是内存库大小和训练集规模线性相关你产线上攒了三年数据内存库就会膨胀到没法收拾。PaDiM 的动机就很清楚了既然每个空间位置的特征分布是相对稳定的为什么不用一个参数化分布去概括它而不是把样本全存下来一个位置用均值加协方差就能描述参数量从样本数×维度变成维度×维度与训练样本数量彻底解耦。这一步转换是整个框架的立身之本。2. 核心思路拆解为什么是多元高斯加马氏距离PaDiM 的方法主体一句话能说完假设正常样本在每个 patch 位置上的特征服从多元高斯分布训练阶段估计这些分布的均值和协方差测试阶段用马氏距离衡量偏离程度。听着简单但里面每一环都有讲究尤其是为什么非得是马氏距离和为什么要随机降维这两点论文没花太多篇幅展开实际复现时反而是最影响结果的。2.1 把特征图切成按位置对齐的 patch关键在于按位置这三个字。骨干网络输出的特征图是 C×H×W把空间上每一个 (i,j) 位置上的那个 C 维向量看作一个 patch 的描述子整张图就变成了 H×W 个 patch。不同图片之间同一个 (i,j) 位置的 patch 在语义上是对齐的——比如一张 PCB 图里(12,30) 这个位置在每张正常图上都对应着同一块焊盘区域。这个对齐假设是整套方法的地基。工业图像的成像条件通常被严格控制相机固定、光源固定、工装夹具固定所以位置对齐基本成立。但如果你做的是随手拍的场景、目标会大幅位移或旋转这个假设就会崩异常图会到处都是高亮。这是我踩过的第一个坑后面第 7 节会展开说。具体实现上假设骨干网络是 ResNet18输入 224×224阶段输出通道特征图尺寸说明layer16456×56保留细节对细小缺陷敏感layer212828×28中等语义layer325614×14语义更强但对小目标定位粗三层通道数加起来 64128256448把 layer2、layer3 双线性插值上采样到 56×56 后沿通道拼接就得到 448×56×56 的特征。这样网格有 56×563136 个位置每个位置一个 448 维向量。用多层特征拼接而不是只用最后一层是因为浅层保留的高频细节对细小划痕、脏污这类缺陷特别关键只用深层特征会漏检。2.2 马氏距离比欧氏距离强在哪假设某个位置上的正常特征分布是一个被拉长的椭球——某个方向的波动大另一个方向波动小。这时候来了一个测试特征如果它沿着波动大的那个方向偏了很远其实很正常如果沿着几乎不动的方向偏了一点那才是真异常。欧氏距离对这两个方向的偏离一视同仁都会给出一个不小的值于是误报就来了。马氏距离把协方差的逆矩阵塞进去等于先用分布的形状把空间白化了一遍波动大的方向被压缩波动小的方向被放大。公式是M(x) sqrt( (x − μ)ᵀ Σ⁻¹ (x − μ) )其中 x 是测试 patch 特征μ 是该位置训练集特征的均值向量Σ 是该位置的协方差矩阵。这个写法在统计上还有一层意义如果特征真服从多元高斯那么 M² 服从卡方分布你甚至可以直接从分布推出一个理论阈值而不是靠验证集去试。2.3 随机降维被低估的关键设计如果直接用 448 维去估计协方差每个位置要存一个 448×448 的矩阵3136 个位置就是 3136×448×448×4 字节接近 2.5 GB而且 448 维协方差需要大量样本才能估准MVTec 有些类别只有 60 张正常图根本估不出来。PaDiM 的处理是从 448 维里随机挑 d100 维只用这 100 维来做后续的分布估计。注意是随机挑不是 PCA 那种挑主成分。论文的消融实验里d 从 100 增到 200、300、448指标不升反降一般性结论是维度太高会引入冗余相关反而让协方差估计不稳定。d100 是个甜点区参数量降到 3136×100×100×4 ≈ 125 MB样本需求也降到可接受范围。随手随机挑一把就能比精心设计的降维更好这点第一次看确实反直觉。我的理解是随机子集本身就起到了一种去相关的作用把 448 维里高度冗余的那部分打散了而 PCA 保留的主成分往往是全局方差最大的方向这些方向反而可能是所有位置都共有的、区分度低的分量。论文里也提到过他们试过 PCA效果不如随机选。注意随机挑维度的下标一旦确定训练和测试必须用同一套。复现时务必把这个索引固定下来存进模型文件或者用固定随机种子否则训练时算的分布和测试时算的向量对不上指标会掉得莫名其妙。还有一个实现细节随机降维应该在拼接之后做也就是从 448 维里选 100 维而不是在每层里各选一部分。如果按层平均分配浅层的 64 维会被大量保留深层的 256 维被削得厉害实测会损失一部分语义判别力。3. 数据组织与训练流程实现这一节进入落地环节。整个训练流程其实只有三步把正常图过一遍骨干网络拿到特征按位置统计均值和协方差把协方差求逆缓存起来供推理用。没有反向传播没有优化器没有损失函数——这也是它训练只要一两分钟的原因。3.1 关键前提只用正常样本这一点必须放在最前面强调。PaDiM 是无监督方法训练阶段完全不需要缺陷样本也不需要标注。你手头那 200 张良品图就是全部训练数据。缺陷标签只在评估指标AUROC 等的时候才用得上用来算你真阳性、假阳性。很多新手第一次看代码会困惑怎么没有 label 传进去正是因为它的训练根本不涉及分类损失。数据目录一般按 MVTec 的风格组织dataset/ bottle/ train/ good/ # 只用这些 000.png 001.png test/ good/ # 评估用 broken_large/ broken_small/ contamination/ ground_truth/ broken_large/ 000_mask.png # 评估像素级 AUROC 用训练时遍历train/good/下的所有图其他一律不看。这个设定对工业落地极其友好——你不需要等到缺陷样本积累够才能上线冷启动阶段只要跑一段时间良品就能把模型建起来。3.2 均值与协方差的估计细节对每个位置 (i,j)收集 N 张训练图的特征向量 x₁, x₂, ..., x_N然后μ_ij (1/N) · Σ xₙΣ_ij (1/(N−1)) · Σ (xₙ − μ_ij)(xₙ − μ_ij) εI第二个公式里的 εI 是正则项ε 论文取 0.01。它的作用有两层一是保证矩阵可逆二是控制数值稳定性。为什么必须加考虑 MVTec 的 toothbrush 类别正常训练图只有 60 张而 d100意味着 100 维空间里只有 60 个样本点构成的协方差矩阵秩最多只有 59必然奇异。加一个小的对角扰动相当于给每个维度加一点点各向同性噪声矩阵就满秩了。提示ε 不建议随便改。取得太小比如 1e-6数值上接近奇异马氏距离会爆掉异常图出现大片极端值取得太大比如 1.0协方差矩阵被拉得接近各向同性等于退化成欧氏距离马氏距离的优势就没了。0.01 这个量级在多组数据上都比较稳。另外一个很实际的技巧如果某个类别的训练样本实在太少低于 d在内存允许的前提下可以先用几何变换做数据增广把样本数堆上去。增广方式要选不改变局部纹理统计的比如小角度旋转、亮度微调、小幅平移。千万别用随机裁剪那会破坏位置对齐假设反而把事情搞坏。3.3 完整训练代码骨架下面这段是我复现时用的核心逻辑去掉了一些工程包装RetNet18 用 torchvision 的预训练权重。import torch import torch.nn as nn import torch.nn.functional as F from torchvision.models import resnet18, ResNet18_Weights class PatchFeatureExtractor(nn.Module): 提取 layer1/layer2/layer3 三层特征并拼接 def __init__(self): super().__init__() backbone resnet18(weightsResNet18_Weights.IMAGENET1K_V1) # 输入 224x224 时layer1 输出 64x56x56 self.stem nn.Sequential( backbone.conv1, backbone.bn1, backbone.relu, backbone.maxpool, backbone.layer1, ) self.layer2 backbone.layer2 # 128x28x28 self.layer3 backbone.layer3 # 256x14x14 def forward(self, x): f1 self.stem(x) # B,64,56,56 f2 self.layer2(f1) # B,128,28,28 f3 self.layer3(f2) # B,256,14,14 f2 F.interpolate(f2, sizef1.shape[-2:], modebilinear, align_cornersFalse) f3 F.interpolate(f3, sizef1.shape[-2:], modebilinear, align_cornersFalse) return torch.cat([f1, f2, f3], dim1) # B,448,56,56统计部分我习惯把每个位置的特征拉平到最后一维然后一次性算完所有位置torch.no_grad() def fit_gaussian(extractor, dataloader, device, d100, eps0.01, seed0): extractor.eval().to(device) feats [] for img in dataloader: feats.append(extractor(img.to(device))) feats torch.cat(feats, dim0) # N,448,56,56 N, C, H, W feats.shape # 固定随机下标训练/推理必须一致 g torch.Generator().manual_seed(seed) idx torch.randperm(C, generatorg)[:d] feats feats[:, idx] # N,100,56,56 feats feats.permute(0, 2, 3, 1).reshape(N, H * W, d) # N,HW,d mean feats.mean(dim0) # HW,d centered feats - mean.unsqueeze(0) # N,HW,d cov torch.einsum(nhd,nhe-hde, centered, centered) / (N - 1) cov cov eps * torch.eye(d, devicecov.device).unsqueeze(0) return mean.reshape(H, W, d), cov.reshape(H, W, d, d), idx这里的einsum(nhd,nhe-hde)是关键它把 N 个样本的外积一次性算出来避免了 Python 层循环。H*W3136d100这个矩阵算出来是 3136×100×100float32 下约 125 MB显存能放下如果显存紧张可以按位置分块计算再拼。推理时不要去算矩阵求逆直接用 Cholesky 分解更快也更稳torch.no_grad() def infer(extractor, mean, cov, idx, img, device): extractor.eval().to(device) feat extractor(img.to(device))[:, idx] # B,100,56,56 B, d, H, W feat.shape delta feat.permute(0, 2, 3, 1) - mean # B,H,W,d # 用 Cholesky 代替显式求逆Sigma^{-1} L^{-T} L^{-1} L torch.linalg.cholesky(cov) # H,W,d,d z torch.linalg.solve_triangular( L, delta.unsqueeze(-1), upperFalse).squeeze(-1) # B,H,W,d dist z.pow(2).sum(dim-1).clamp(min1e-8).sqrt() # B,H,W return dist.permute(0, 3, 1, 2) # B,1,H,W用 Cholesky 而不是torch.linalg.inv一方面数值精度更好协方差是正定的对称矩阵Cholesky 天然适配另一方面速度大概快两倍。这个小优化在批量推理时能明显改善吞吐。4. 从距离图到可用的异常热力图训练完只是拿到了一堆分布的参数真正交付给产线的是这张图有没有问题 问题在哪。这一步要把 56×56 的距离图变成原始分辨率的热力图还要定出一个能用的阈值。4.1 高斯滤波为什么不能省直接上采样 56×56 的马氏距离图你会看到异常区域是一格一格的边缘锯齿明显而且经常会有孤立的高亮单点。原因是 feature map 的空间分辨率只有输入的四分之一每个网格对应原图 4×4 的像素块。PaDiM 的做法是先做一个高斯滤波论文用 σ4再双线性上采样到 224×224。高斯滤波的作用是邻域平滑一个真正有缺陷的区域通常在特征图上是连成片的平滑之后会更突出而孤立的高噪点会被周围低值拉下去。实测这一手的收益非常明显尤其是像素级指标加不加高斯滤波能差好几个点。注意σ 别设太大。σ4 是针对 56×56 网格的如果你换了骨干网络或者改了输入尺寸网格分辨率变了σ 也要跟着调。我的经验是按网格尺寸的 1/14 左右取56 网格对应 σ428 网格就取 2 左右。4.2 图像级打分max 还是 top-k 均值从 224×224 的热力图得到一个标量最直接的做法是取最大值。PaDiM 原版就是取 max简单、对局部缺陷敏感。但 max 的缺点是抗噪能力差一个由噪声引起的孤立高点就能把整图判成异常造成误报。我在实际项目里更常用 top-k 均值比如取最大的 1% 像素求平均或者对高斯滤波后的图取 max。两者效果差不多但 top-k 稳定得多。如果你的产线对误报率极度敏感建议直接上 top-k。评估用的是 AUROC这个指标的好处是不需要你选阈值——它衡量的是正样本得分高于负样本得分的概率只看排序。所以调模型阶段不需要纠结阈值等上线时再按目标误报率在验证集上定一个分位数即可。4.3 MVTec AD 上的实际表现为了让你对量级有个概念我把论文报告的关键数字整理一下。以下都是 MVTec AD 15 个类别的平均值具体请以原文和你自己的复现结果为准不同随机种子会有几个千分点的浮动。配置图像级 AUROC像素级 AUROC参数量/内存PaDiM ResNet18, d100约 95.5%约 97.5%约 125 MBPaDiM WideResNet-50, d550约 97.1%约 97.9%约 380 MB换成 d448不降维略降略降约 2.5 GB横向对比同期方法PaDiM 的图像级指标已经超过了当时的一票重构类方法像素级更是稳居第一梯队。它的真正优势在于训练时间——单个类别一两分钟就能跑完而且不需要调学习率、不需要早停、不需要担心过拟合。我的复现里从装环境到跑出第一份完整结果一个下午就够了。WideResNet-50 那版把 d 提到了 550是因为它的三层通道数更多25651210241792维度更高协方差矩阵也更大内存要涨到 300 MB 以上。如果部署在边缘设备上ResNet18 版本基本是唯一现实的选择。5. 和其他方法的取舍什么时候别用 PaDiM复现完再横向看一圈你会发现 PaDiM 并不是万能的它有一批非常明确的适用边界。搞清楚这些边界比多刷两个点更重要。5.1 和 SPADE、PatchCore 的区别SPADE 存全量特征做 KNNPaDiM 存分布参数PatchCore 则是折中路线用一个贪心的 coreset 采样从内存库里挑一小撮有代表性的特征存下来再用局部邻域聚合。三者的取舍大致是这样维度SPADEPaDiMPatchCore存储内容全量训练特征每位置高斯参数采样后的核心特征集存储随数据规模线性增长与样本数无关近似常数训练时间无需训练数分钟需做 coreset 采样对少样本类别表现稳定协方差估计易不稳表现稳定对位置对齐的依赖中等强较弱最关键的一行是最后一行。PaDiM 强依赖位置对齐因为它是逐位置建模的PatchCore 用的是局部邻域聚合对轻微位移宽容得多。如果你的场景里目标会位置漂移、或者用了随机裁剪的增广PaDiM 会明显吃亏。5.2 什么场景该选它我的判断标准通常是这样画面固定、相机不动、光照稳定PaDiM 几乎是首选。训练快、调参少、内存可控。缺陷类型多样但不需要细分类PaDiM 只输出异常程度不做缺陷归类。如果你只需要 OK/NG 加一个位置框够用。数据量小、迭代频繁训练只要几分钟换一批数据重跑成本极低特别适合产线换型的场景。目标会位移、旋转、缩放直接放弃 PaDiM去看 PatchCore 或者带上配准的前处理。需要极低内存的边缘部署125 MB 还要算上骨干网络的权重如果设备只有几百兆内存得考虑量化或者换成更小的骨干。5.3 一个容易忽略的工程点PaDiM 的推理是逐位置算马氏距离位置数是固定的 3136。但如果你在生产环境里遇到大分辨率图比如 2000×2000直接把原图缩到 224 会丢细节小缺陷就查不出来了。我的做法是滑窗 局部统计把大图切成若干有重叠的 224 块但分布参数还是用全局训练集估测试时每块独立算距离再拼回去。重叠区域用加权平均融合避免接缝处出现假异常。代价是推理时间随块数线性增长2000×2000 大概切 100 块左右单卡跑一张要几百毫秒。这套方案在几个实际项目里都跑通了比强行缩小分辨率的召回率高出一大截。6. 踩坑记录这些错误我一个个犯过这部分是我最想写的因为论文和开源代码都不会告诉你这些。以下问题全部来自我实际复现和落地过程中的翻车现场。6.1 常见问题速查表现象可能原因排查方式处理办法正常图上也大片高亮训练集混入了异常图抽查训练集看马氏距离分布的尾部清洗训练集用分布尾部自动筛查可疑样本异常图完全没反应训练/推理的降维下标不一致打印两边 idx 对比固定随机种子并保存 idx热力图有大量孤立噪点未做高斯平滑或 σ 太小关掉滤波看对比加高斯滤波σ 按网格尺寸调整协方差报奇异或 NaNε 太小或样本数远小于 d检查torch.linalg.cholesky是否报错提高 ε 到 0.01 量级增广扩样本指标比论文低好几个点输入归一化不对确认用了 ImageNet 的 mean/std严格按骨干网络的预训练归一化换了个类别指标暴跌该类别的正常样本纹理本身多变看正常图的马氏距离分布方差可能是场景不适用考虑换方法显存溢出cov 张量按 3136×100×100 存储打印张量形状分块计算或降低 d 到 506.2 几个必须记住的实操心得归一化必须和骨干网络一致。ResNet18 的 ImageNet 预训练权重是用 mean[0.485, 0.456, 0.406]、std[0.229, 0.224, 0.225] 训练的。如果你偷懒用 [0,1] 直接喂进去特征分布会整体偏移马氏距离的数值尺度全变指标能掉五到十个点。这个坑我踩过查了两天才发现。训练集一定要清洗。异常检测的假设是训练集全是正常样本但实际产线上你导出的良品文件夹里混几张轻微缺陷的图太常见了。混进去之后那个位置的高斯分布会被拉宽导致该位置的异常敏感度整体下降表现为那一块区域永远不报警。我现在的做法是先跑一遍把所有训练图对自身的马氏距离图算出来取最大值的分布落在极端尾部的图手工看一眼。d 的值不要照抄。论文的 d100 是针对 448 维拼接特征、ResNet18、MVTec 这个组合调出来的。如果你换了骨干网络比如用 EfficientNet 或者 WideResNet特征维度和冗余结构都变了d 需要重新扫一遍。我的习惯是从 50 到 300 按 50 步长扫看验证集图像级 AUROC通常能找到一个明显优于默认值的点。均值要跟着模型一起存。复现时常见的一个低级错误是训练脚本算完统计量只存了协方差均值临时重算。等部署时换了推理脚本均值算错了指标全乱。我的习惯是打包成一个字典mean、cov、idx、骨干网络名、输入尺寸、归一化参数全部塞进去用 torch.save 存成一个文件推理时一次性加载。别忘了把骨干网络设成 eval 模式。这听起来像废话但 ResNet 里的 BatchNorm 在 train 模式下会用当前 batch 的统计量导致同一张图在不同 batch 组成下特征不一样异常图会抖动。我在调试阶段因为这个问题浪费过一个下午——单张推理和批量推理结果对不上最后发现是model.train()忘了切回来。协方差矩阵可以量化存储。125 MB 的 float32 参数量如果部署在内存吃紧的设备上可以考虑把协方差用 float16 存均值也用 float16。代价是马氏距离精度略有损失实测指标掉零点几个点但内存直接砍一半。这个权衡在边缘设备上很划算。7. 这套框架还能怎么扩展跑通之后你会发现 PaDiM 的骨架其实很灵活很多后续工作都是在它的几个模块上做替换。这些方向我也试过一些顺手记一笔。换骨干网络。从 ResNet18 换成 WideResNet-50、EfficientNet-B5指标一般能涨一到两个点代价是内存和推理时间。我的经验是如果 ResNet18 的指标已经满足产线要求就别换性价比不划算。换降维策略。随机选是基线可以试试基于位置相关的选择、或者按层加权选择。我试过按每层的方差贡献比例分配维度效果和随机差不多说明随机选本身已经吃掉大部分收益了。融合多尺度。PaDiM 只用了一层 56×56 的分辨率做统计如果改成多尺度比如同时在 28×28 和 56×56 上建模对大缺陷和小缺陷的兼顾会更好但内存翻倍。适合对内存不敏感的场景。引入局部邻域聚合。这是 PatchCore 的思路把每个位置的特征和它 3×3 邻域内的特征做加权聚合再建模。收益是对轻微位移更鲁棒代价是特征维度变大。如果你的场景有轻微配准误差这一招很值得加。多类别共享参数。产线上一个工位经常要检测好几种产品。逐个类别建一套高斯参数内存会累积。我试过把多个类别的正常样本混合建模实测指标会掉因为不同产品的纹理分布差异太大。更靠谱的做法是共享骨干网络但统计参数按类别分开存这样至少省下了骨干网络的重复开销。最后再讲一个我自己的体会。PaDiM 这类的价值从来不在于它把 AUROC 刷到了多高而在于它把一个原本需要调学习率、调网络结构、调训练轮数的活压缩成了提特征、算统计量、算距离这三步确定性操作。这意味着你在产线上做迭代的时候不需要重新训练、不需要等 GPU 排队改一批数据重跑两分钟就能看到效果。对做工程的人来说这种可预测性有时候比多两个点的指标更值钱。我后来的几个项目里PaDiM 更多是作为快速基线存在的先用它把数据和流程跑通看清问题的难度再决定要不要上更重的模型。这个用法我推荐你也试试。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Codex修改日期逻辑后为什么总差一天?时区、UTC与时间格式排查 2026/10/1 14:47:32

Codex修改日期逻辑后为什么总差一天?时区、UTC与时间格式排查

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

阅读更多 →
从源码里深度探索流式推理:SSE、AIMessageChunk 与 LangChain 的协作链路 2026/10/1 14:47:32

从源码里深度探索流式推理:SSE、AIMessageChunk 与 LangChain 的协作链路

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

阅读更多 →
RJ45工业以太网温湿度传感器在配电柜的应用与选型 2026/10/1 14:47:32

RJ45工业以太网温湿度传感器在配电柜的应用与选型

1. 项目概述:为什么配电柜非要装RJ45温湿度传感器?在电力中心这种地方,配电柜不是摆设,它是整个供配电系统的神经节点。我干这行十多年,见过太多因为环境失控导致的故障——夏天柜内温度飙到65℃,继电器触点…

阅读更多 →
AI IDE Qoder 实战指南:从安装配置到专家团与模型优化 2026/10/1 14:47:32

AI IDE Qoder 实战指南:从安装配置到专家团与模型优化

前阵子好几个做开发的朋友都来问我 Qoder 这个 AI IDE 到底值不值得装,装完又该怎么调教才能干活。我当时就说,这工具其实思路很简单——就是把大模型直接塞进编辑器里,让你不用来回切窗口复制代码,但真正用起来门道不少。今天这篇…

阅读更多 →
Linux --TCP Socket编程 2026/10/1 14:47:32

Linux --TCP Socket编程

using func_t std::function<void()>; func_t func std::bind(&TcpServer::Service, this, sockfd, addr); ThreadPool<func_t>::GetInstance()->Push(func);CP 与 UDP 的对比&#xff1a;为什么 TCP 更“可靠”&#xff1f;在开始之前&#xff0c;先看一…

阅读更多 →
打算增加一个每日办公室放松短视频 2026/10/1 14:47:25

打算增加一个每日办公室放松短视频

我觉得这个是真正有价值的短视频-----------特别是&#xff1a;对我自己尤其有效果&#xff0c;所以可以长期坚持下去。我相信对大多数人也是有效果的。

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉