ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

深度集成如何量化模型不确定性:原理、工程实践与避坑指南

深度集成如何量化模型不确定性:原理、工程实践与避坑指南

1. 这不是“加个 Dropout”就能糊弄过去的不确定性——为什么深度集成(Deep Ensembles)成了工业界默认的起点

你有没有遇到过这样的场景:模型在测试集上准确率98%,但部署到线上后,面对一张模糊的、角度奇怪的、甚至被部分遮挡的图片,它却信心十足地给出一个完全离谱的预测,置信度高达0.99?更糟的是,这个错误预测没有任何预警信号。这不是模型“傲慢”,而是它根本没学会区分“我知道”和“我不知道”。这背后缺失的,就是预测不确定性(Predictive Uncertainty)。

过去几年,我参与过多个面向真实业务场景的模型落地项目,从某高校实验室的医疗影像辅助诊断Demo,到某公司内部的工业缺陷检测系统。几乎每一个项目,在模型精度达标后,都会被同一个问题卡住:如何让模型在出错时主动说“我不确定”?这不是锦上添花,而是安全底线。一个自动驾驶系统如果无法识别自己没见过的路标,它必须减速或请求人工接管;一个金融风控模型如果对一笔异常交易毫无警觉,就可能造成真金白银的损失。而“Simple and Scalable Predictive Uncertainty Estimation using Deep Ensembles”这篇论文,恰恰提供了一个既不复杂、又足够可靠、还能直接塞进现有工程流水线里的答案——用多个独立训练的模型组成“智囊团”,让它们投票并观察分歧程度。

这里的关键词是“Simple”和“Scalable”。它没有引入任何新奇的、需要重写整个训练框架的架构(比如复杂的贝叶斯神经网络),也没有依赖于难以调参的正则化技巧(比如MC Dropout对Dropout率和采样次数极其敏感)。它只是回归了机器学习最朴素的直觉:一个人可能看走眼,但一群独立思考的人,意见高度一致时大概率是对的,意见严重分歧时,事情本身就值得警惕。论文标题里那个“Ensembles”(集成),指的就是训练K个结构完全相同、但初始化权重和数据打乱顺序都不同的深度神经网络,让它们各自对同一个输入进行预测。最终的预测结果,是这K个模型输出的平均值;而不确定性,则由这K个模型输出的方差来量化。这个方差,就是我们能直接拿去用的、可解释的、数值化的“不确定度”。

很多人第一反应是:“啊,那不是要训K倍时间?太贵了!” 这正是我最初的想法。但实操下来发现,这个“贵”是可控的。因为K通常取3到5,而不是30或50。更重要的是,这K个模型可以完全并行训练,不共享任何计算图,也不需要复杂的同步机制。你手头有4块GPU,就可以同时跑4个模型,总耗时几乎等同于单个模型的训练时间。这种“简单粗暴”的并行性,让它在算力调度上反而比那些需要序列化采样的方法(如MC Dropout)更友好。所以,“Scalable”在这里,不是指能无限扩展到成千上万个模型,而是指它能以极低的认知成本和工程成本,无缝嵌入到你现有的训练流程中——你不需要改一行模型定义代码,只需要把你的训练脚本包装在一个for循环里,再加一个简单的聚合函数。这就是它成为工业界事实标准(de facto standard)的根本原因:它把一个听起来高深莫测的理论问题,降维成了一个工程上的“复制-粘贴-求平均”操作。

2. 深度集成的不确定性,到底在度量什么?——拆解“认知不确定性”与“数据不确定性”的双重视角

很多初学者会误以为,深度集成(Deep Ensembles)给出的方差,就是模型预测的“错误概率”。这是一个危险的误解。我们必须先厘清,预测不确定性本身,就是一个复合体,它至少包含两个截然不同、但又相互交织的维度:认知不确定性(Epistemic Uncertainty)和数据不确定性(Aleatoric Uncertainty)。理解这两者的区别,是正确使用、解读乃至改进深度集成方法的前提。

2.1 认知不确定性:模型的“知识盲区”,是可以被数据消除的

想象一下,你是一位刚入职的新人工程师,被派去检修一台从未见过型号的老旧设备。你手头只有一本残缺不全的说明书,和三张模糊不清的电路图。此时,你对“哪个电容坏了”的判断,充满了犹豫。这种犹豫,源于你对问题本身的知识不足。如果你能拿到完整的说明书,或者有经验丰富的老师傅带你现场拆解几次,你的判断就会越来越自信。这种源于模型自身能力局限或训练数据不足而导致的不确定性,就是认知不确定性。

在深度集成的框架下,认知不确定性主要体现在模型对训练数据分布之外的样本(Out-of-Distribution, OOD)的反应上。例如,一个在MNIST手写数字数据集上训练的模型,当它看到一张猫的图片时,K个集成模型的预测结果会非常分散:有的可能猜“3”,有的猜“8”,有的甚至猜“1”。它们之间巨大的方差,正是模型在大声告诉你:“喂,这个东西完全超出了我的认知范围,请别相信我的任何单一预测!” 这种不确定性,理论上是可以通过收集更多、更全面的训练数据,或者采用更强大的模型架构来逐步降低的。它代表了模型的“无知”,而这种无知,是可以被“教育”所消除的。

2.2 数据不确定性:世界的“固有噪声”,是模型无法消除的

再换一个场景。假设你是一位经验丰富的放射科医生,正在看一张肺部CT影像。影像本身质量很好,你的知识也足够渊博。但影像中有一个微小的、边界模糊的结节,它的形态介于良性与恶性之间。即使是最顶尖的专家,也会存在一定的诊断分歧。这种分歧,并非源于医生的无知,而是源于数据本身的模糊性、测量误差或世界固有的随机性。这种不确定性,就是数据不确定性。

在深度集成中,数据不确定性主要反映在模型对训练数据分布之内、但本身带有噪声或歧义的样本上的表现。比如,一张对焦不准、有运动模糊的手写数字“7”,它本身就长得像“1”也像“7”。此时,K个集成模型的预测可能集中在“1”和“7”这两个类别上,它们的输出概率分布会呈现出一种“双峰”形态,方差同样会很高。但请注意,这个高方差的意义,与前面OOD样本的高方差完全不同。它不是在说“我不知道这是什么”,而是在说“我知道这是个数字,但它具体是1还是7,数据本身就没给够清晰的信息”。这种不确定性,是客观存在的,无论你收集多少数据、用多大的模型,都无法将其完全抹除。它代表了世界的“混沌”,而非模型的“无知”。

2.3 深度集成的“混合”特性:一把钥匙,开两把锁

关键点来了:标准的深度集成,同时捕获了上述两种不确定性。它的输出方差,是这两种来源共同作用的结果。这既是它的强大之处,也是它的局限所在。

  • 强大之处在于实用性:在绝大多数现实场景中,我们并不需要严格区分这两种不确定性。我们真正关心的,只是一个统一的、可操作的信号:这个预测值,我该有多大的信任?无论是因为模型没见过(认知),还是因为图片太糊(数据),只要方差大,我们就应该提高警惕,触发人工复核、增加冗余校验,或者降低决策权重。深度集成提供了一个“一揽子”解决方案,无需复杂的建模,就能得到一个非常鲁棒的不确定性指标。

  • 局限之处在于可解释性:如果你的研究目标,是精确地量化“模型还有多少知识盲区”,或者想专门设计一个模块来建模“图像噪声水平”,那么标准的深度集成就显得过于笼统了。它无法告诉你,当前的高方差,有多少来自认知,多少来自数据。要解决这个问题,就需要更高级的变体,比如将深度集成与异方差回归(Heteroscedastic Regression)结合。在这种变体中,每个模型不仅输出预测值y,还额外输出一个表示该预测“噪声水平”的参数σ²。最终的不确定性,就是集成预测的方差,加上所有模型预测的σ²的平均值。这样,你就把“数据不确定性”这部分显式地剥离了出来。

提示:在你的第一个不确定性估计项目中,我强烈建议从标准的深度集成开始。不要一上来就追求理论上的完美分离。先让系统具备基本的“自省”能力,上线运行,收集反馈。你会发现,80%的线上问题,靠这个“混合”不确定性就已经能有效拦截。剩下的20%,才是你后续做精细化建模的发力点。

3. 从零搭建一个可用的深度集成:不是“复制粘贴”,而是理解每一步的工程权衡

现在,让我们把理论落地。假设你已经有一个训练好的PyTorch模型MyModel,以及一个标准的数据加载器train_loader。下面,我将带你一步步构建一个生产就绪的深度集成,并重点解释每一个步骤背后的“为什么”,而不仅仅是“怎么做”。

3.1 模型初始化:独立性是灵魂,随机种子是命脉

深度集成的核心威力,来自于K个模型的独立性。如果它们的权重初始化、数据采样顺序、甚至随机数生成器的状态都一模一样,那么它们的预测结果将完全一致,方差为零,整个方法就彻底失效。因此,初始化阶段的每一个细节,都必须确保“独立”。

import torch import numpy as np from torch import nn def create_ensemble(model_class, k=3, seed_base=42): """ 创建K个独立初始化的模型实例。 seed_base: 基础随机种子,每个模型使用seed_base + i作为其专属种子 """ ensemble = nn.ModuleList() for i in range(k): # 为每个模型设置独立的随机种子 seed = seed_base + i torch.manual_seed(seed) np.random.seed(seed) # 创建模型实例 model = model_class() # 关键:对模型权重进行独立初始化 # 这里不能只调用model.apply(init_weights),因为init_weights函数本身可能依赖全局随机状态 # 更稳妥的做法是,为每个模型创建一个独立的、带种子的初始化器 def init_weights(m): if isinstance(m, nn.Linear) or isinstance(m, nn.Conv2d): # 使用正态分布初始化,但确保每次调用都是独立的 torch.nn.init.normal_(m.weight, mean=0.0, std=0.02) if m.bias is not None: torch.nn.init.zeros_(m.bias) model.apply(init_weights) ensemble.append(model) return ensemble # 使用示例 ensemble_models = create_ensemble(MyModel, k=5)

为什么强调“独立种子”?PyTorch的torch.manual_seed()和NumPy的np.random.seed()控制着所有后续的随机操作。如果你在循环外只设一次种子,那么所有模型的初始化、数据打乱都会遵循同一个随机序列,导致它们本质上是“克隆体”。通过为每个模型分配一个唯一的seed_base + i,我们确保了它们的“人生起点”完全不同。

为什么不用torch.nn.init.xavier_normal_?Xavier初始化虽然经典,但它依赖于层的输入/输出维度,对于某些特定任务(如小样本学习),可能会导致初始权重的方差过大或过小,影响训练稳定性。我更倾向于使用一个固定的、较小的标准差(如0.02),这在实践中对各种模型结构都表现得更稳健。

3.2 并行训练:不是“加速”,而是“解耦”

训练K个模型,最直观的想法是写一个for循环,一个接一个地训。但这在工程上是灾难性的。它不仅耗时,而且无法利用现代GPU的并行计算能力。正确的做法,是让它们完全解耦、并行训练。

# 伪代码:并行训练的逻辑 # 假设你有4个GPU,ID为[0, 1, 2, 3] # 将ensemble_models[0]放到GPU0, ensemble_models[1]放到GPU1...以此类推 # 在每个GPU上,启动一个独立的训练进程 # 每个进程只负责一个模型,拥有自己的优化器、学习率调度器、数据加载器 # 关键:每个进程的数据加载器,必须使用不同的`generator` # 否则,即使在不同GPU上,它们加载的数据批次也可能完全一样! for i, model in enumerate(ensemble_models): # 为每个数据加载器创建独立的随机数生成器 g = torch.Generator() g.manual_seed(42 + i) # 再次强调独立性! train_loader_i = DataLoader( dataset=train_dataset, batch_size=batch_size, shuffle=True, generator=g, # 这是关键! num_workers=4 ) # 启动训练进程...

为什么generator=g如此重要?DataLoader的shuffle=True,其内部是通过一个随机数生成器来打乱数据索引的。如果你不为每个加载器指定一个独立的generator,那么所有加载器默认会使用PyTorch的全局随机生成器。这意味着,尽管你在不同GPU上运行,但它们加载的第一个batch、第二个batch……都将是完全相同的!这会让整个“集成”变成一个昂贵的“单模型重复实验”。generator=g这一行,就是为每个数据流赋予了独立的“命运”,确保了数据层面的多样性。

3.3 预测与不确定性聚合:一个函数,搞定一切

训练完成后,预测阶段就变得异常简单。你只需要一个函数,就能同时获得预测均值和不确定性度量。

def predict_ensemble(ensemble, x, device='cuda'): """ 对单个输入x,使用集成模型进行预测。 返回: (mean_pred, uncertainty) """ # 将输入x移动到设备上 x = x.to(device) # 收集所有模型的预测 predictions = [] with torch.no_grad(): # 关闭梯度,节省显存 for model in ensemble: model = model.to(device) pred = model(x) # 假设pred是logits predictions.append(pred.cpu()) # 移回CPU,避免显存爆炸 # 转换为tensor,形状为 [K, batch_size, num_classes] predictions = torch.stack(predictions, dim=0) # 计算均值预测(用于最终分类) mean_pred = torch.mean(predictions, dim=0) # 计算方差(不确定性) # 注意:这里计算的是logits的方差,而非softmax后的概率。 # 实践中,logits的方差更稳定,且与模型的“信心”关联更强。 variance = torch.var(predictions, dim=0, unbiased=True) # 不确定性可以取方差的均值,或者最大值,取决于你的需求 # 取均值:整体不确定性 # 取最大值:最不确定的那个类别 uncertainty = torch.mean(variance, dim=-1) # shape: [batch_size] return mean_pred, uncertainty # 使用示例 x_sample = next(iter(test_loader))[0][:1] # 取一个样本 mean_logits, unc_score = predict_ensemble(ensemble_models, x_sample) # 获得最终预测类别 final_pred = torch.argmax(torch.softmax(mean_logits, dim=-1), dim=-1) print(f"预测类别: {final_pred.item()}, 不确定性得分: {unc_score.item():.4f}")

为什么计算logits的方差,而不是softmax后的概率?这是一个非常关键的实操细节。Softmax函数具有“挤压”效应,会将原本差异较大的logits,映射成相对平滑的概率分布。这会导致方差被严重低估。例如,一个模型输出logits[10, 1, 1],另一个输出[1, 10, 1],它们的logits方差巨大,但softmax后的概率分别是[0.99, 0.005, 0.005]和[0.005, 0.99, 0.005],方差就小得多。而我们真正关心的,是模型在“原始决策空间”里的分歧程度,这更能反映其内在的不确定性。所以,永远在logits层面计算方差。

4. 深度集成的“暗礁”与“灯塔”:那些论文里不会写的实战陷阱与避坑指南

理论再完美,也架不住现实的“毒打”。在我用深度集成支撑的多个项目中,踩过的坑远比读过的论文多。下面这些,是血泪教训凝结成的经验,它们不会出现在任何一篇学术论文的Methodology章节里,但却是你能否成功落地的关键。

4.1 陷阱一:“K=5”不是黄金法则,而是你的第一个超参数

论文里常常说“K=5 works well”,这让你觉得选5就万事大吉。但现实是,K的选择,必须与你的具体任务、数据规模和算力预算强绑定。

  • 小数据集(<10k样本):K=3往往就足够了。因为数据量小,模型很容易过拟合,增加更多的模型,只会让集成过度拟合训练集的噪声,反而降低泛化能力。我曾在一个只有2k张医学影像的二分类任务中,尝试K=10,结果在验证集上的不确定性校准度(Calibration Error)反而比K=3差了15%。

  • 大数据集(>1M样本):K=5可能还不够。数据量越大,单个模型学到的模式越稳定,它们之间的差异(即方差)就越小。此时,你需要更大的K(如7或10)来“放大”这种差异,从而获得一个更有区分度的不确定性信号。否则,所有样本的不确定性得分都会趋近于一个很小的常数,失去了排序和阈值筛选的价值。

  • 算力受限的边缘设备:如果你的模型最终要部署在车载芯片或手机端,那么K=3是底线。因为推理时,你需要将K个模型的权重全部加载到内存中。K=5意味着5倍的内存占用和5倍的计算量。在这种场景下,“Scalable”的含义,就从“训练可扩展”变成了“推理可扩展”。你可能需要牺牲一点不确定性估计的精度,来换取实时性。

经验技巧:在你的项目初期,不要固定K。把它当作一个超参数,和学习率、batch size一起,用一个小的验证集进行网格搜索。画出“K值” vs “不确定性校准误差”曲线,找到那个拐点。这个拐点,就是属于你项目的、独一无二的最优K。

4.2 陷阱二:训练不“独立”,等于白干——关于数据增强的致命误区

我们强调了模型初始化和数据加载的独立性,但还有一个更隐蔽的陷阱:数据增强(Data Augmentation)。

假设你在训练时,对每张图片都应用了随机旋转、裁剪和颜色抖动。这本身是好习惯。但如果所有K个模型,在处理同一张原始图片时,都使用了同一个随机种子来生成增强参数,那么它们看到的,其实是K张高度相似的增强图。这极大地削弱了它们的独立性,让集成的效果大打折扣。

# ❌ 错误示范:所有模型共享同一个增强器 transform = transforms.Compose([ transforms.RandomRotation(degrees=15), transforms.RandomResizedCrop(224, scale=(0.8, 1.0)), transforms.ColorJitter(brightness=0.2, contrast=0.2) ]) # ✅ 正确做法:为每个模型创建独立的、带种子的增强器 def create_augmenter(seed): # 创建一个独立的随机数生成器 g = torch.Generator() g.manual_seed(seed) # 然后,将这个生成器传递给所有需要随机性的变换 return transforms.Compose([ transforms.RandomRotation(degrees=15, interpolation=transforms.InterpolationMode.BILINEAR, fill=0), transforms.RandomResizedCrop(224, scale=(0.8, 1.0), interpolation=transforms.InterpolationMode.BILINEAR, generator=g), transforms.ColorJitter(brightness=0.2, contrast=0.2, generator=g) ]) # 在创建每个模型的数据加载器时,使用不同的seed augmenter_i = create_augmenter(42 + i) train_dataset_i = MyDataset(transform=augmenter_i)

为什么generator=g要传给RandomResizedCrop和ColorJitter?因为这些变换内部的随机操作(如选择旋转角度、选择裁剪区域、调整亮度值),都需要一个随机数生成器。如果不显式传入,它们就会默认使用全局生成器,再次导致所有模型的增强过程“同频共振”。

4.3 陷阱三:不确定性≠错误率,校准才是通往可信AI的桥梁

这是最常被忽视,也最致命的一点。深度集成给出的不确定性得分(方差),是一个未经校准的原始数值。它告诉你模型“有多不确定”,但没告诉你这个“不确定”对应着多大的实际错误风险。

举个例子:模型A对某个样本给出的不确定性得分为0.5,模型B给出的得分为1.0。你能直接说,B的错误概率是A的两倍吗?不能。这个0.5和1.0,只是两个相对的数字。它们的实际物理意义,需要通过校准(Calibration)来赋予。

校准的目标,是让模型的不确定性得分,能够真实地反映其预测错误的概率。理想情况下,所有不确定性得分为0.1的样本中,应该有大约10%是预测错误的;所有得分为0.5的样本中,应该有大约50%是错误的。

实现校准最简单有效的方法,是温度缩放(Temperature Scaling)。它只在模型的最后一步(logits到概率的转换)引入一个可学习的参数T。

class CalibratedEnsemble(nn.Module): def __init__(self, ensemble, T_init=1.0): super().__init__() self.ensemble = ensemble # T是一个标量参数,需要被学习 self.T = nn.Parameter(torch.tensor(T_init)) def forward(self, x): # 获取集成的logits均值 logits = predict_ensemble_logits(self.ensemble, x) # 一个返回logits均值的函数 # 应用温度缩放 scaled_logits = logits / self.T # 返回校准后的概率 return torch.softmax(scaled_logits, dim=-1) # 校准过程:在验证集上,最小化负对数似然(NLL)损失 calibrated_model = CalibratedEnsemble(ensemble_models) optimizer = torch.optim.Adam([calibrated_model.T], lr=0.01) for epoch in range(10): for x_val, y_val in val_loader: probs = calibrated_model(x_val) loss = -torch.log(probs[range(len(y_val)), y_val]).mean() optimizer.zero_grad() loss.backward() optimizer.step()

为什么温度缩放足够?因为深度集成的不确定性,其核心是logits的方差。而温度缩放,恰好是唯一一个能同时、线性地影响所有logits,并且只引入一个自由度的变换。它就像一个“不确定性放大镜”,通过调整T,我们可以让模型的不确定性得分,完美地匹配其在验证集上观测到的实际错误率。经过校准后,你才能放心地设定一个阈值(比如unc_score > 0.3就触发人工审核),这个阈值才真正具有业务意义。

5. 超越“简单”:当深度集成遇上领域挑战——三个典型场景的进阶实践

深度集成的“Simple”是它的入场券,但它的真正价值,是在解决具体领域的棘手问题时才得以彰显。下面,我将结合三个我亲身经历的、极具代表性的场景,展示如何将这个基础方法,因地制宜地进行改造和强化。

5.1 场景一:长尾分布下的不确定性失真——用“分层集成”重校准少数类

在电商商品识别项目中,我们的数据集极度长尾:头部的10个品类(如“手机”、“T恤”)占了80%的样本,而尾部的100个品类(如“复古打字机”、“手工陶笛”)每个只有几十张图。标准的深度集成在此场景下会“失明”:它对尾部品类的不确定性估计,会系统性地偏低。

原因分析:因为尾部品类样本太少,K个模型在训练时,都很难学到这些类别的鲁棒特征。它们的预测logits,往往都集中在几个头部类别上,彼此之间差异不大,导致方差很小。但事实上,模型对这些尾部类别的预测,是极其不可靠的。

解决方案:分层集成(Hierarchical Ensemble)。我们不再用一个“大一统”的集成来处理所有类别,而是为每个语义层级构建一个独立的集成。

  • 第一层(粗粒度):一个集成,只负责区分“电子数码”、“服装”、“乐器”等大类。
  • 第二层(细粒度):在“电子数码”大类下,再训练一个专门针对“手机”、“耳机”、“打字机”的小集成。

这样做的好处是,每个小集成都专注于一个数据相对均衡的子空间。对于“复古打字机”,它不再需要和“T恤”竞争,它的不确定性,就能真实地反映出它在“电子数码”这个子空间内的辨识难度。我们在项目中实施后,尾部品类的不确定性校准误差(ECE)从0.28降到了0.09,效果立竿见影。

5.2 场景二:实时性要求苛刻的在线服务——用“渐进式集成”平衡速度与精度

在一个为千万级用户提供实时内容推荐的系统中,我们无法承受K次模型推理的延迟。用户点击一个链接,期望毫秒级响应,而不是等待500ms。

解决方案:渐进式集成(Progressive Ensemble)。其核心思想是,不确定性估计本身,也可以是一个“渐进式”的过程。

  • 第一阶段(快速响应):只运行1个模型(比如ensemble_models[0]),得到一个初步预测和一个“快速不确定性”(基于该单模型的Dropout采样方差,或其内部注意力权重的熵)。
  • 第二阶段(按需精修):如果第一阶段的“快速不确定性”低于一个宽松阈值(如0.1),说明模型非常自信,直接返回结果。如果高于该阈值,则启动第二阶段,再并行运行另外2个模型(ensemble_models[1]和[2]),用这3个模型的方差作为最终的、更可靠的不确定性。

这本质上是一种“投机执行”策略。它牺牲了极少数(高不确定性)请求的延迟,换取了绝大多数(低不确定性)请求的极致速度。在我们的压测中,95%的请求延迟降低了60%,而整体的不确定性检测准确率只下降了不到2%。这是一种非常务实的工程权衡。

5.3 场景三:模型持续演进的动态环境——用“增量式集成”告别“从头再来”

在金融风控模型的迭代中,模型需要每周更新,以适应最新的欺诈模式。如果每次更新都重新训练K个全新模型,成本将高得无法承受。

解决方案:增量式集成(Incremental Ensemble)。我们保留原有的K-1个模型不变,只训练1个新模型,它被特别设计为去学习旧模型集体犯错的那些样本。

具体操作:

  1. 用旧的K-1个模型,对最新的训练数据进行预测。
  2. 找出那些被超过半数(K//2)模型预测错误的样本,构成一个“困难样本集”。
  3. 新模型的训练目标,不再是普通的交叉熵,而是聚焦于这些困难样本的加权损失。

这样,新的集成,就不再是K个“平行宇宙”,而是一个“进化树”:旧模型代表了历史经验,新模型代表了对最新挑战的专项攻坚。它让集成的能力,随着业务的发展而持续进化,而不是每一次都归零重启。

我在实际操作中发现,这种方法训练出的新模型,其“补强”效果非常显著。在一次重大欺诈模式变更后,仅用1个新模型,就将整个集成在新数据上的AUC提升了0.03,而训练成本仅为全量重训的20%。这证明了,深度集成的生命力,远不止于论文标题里的“Simple”二字。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进