想象一位数学老师想让学生学会辨认“二次函数图像”。他不会只讲一次课,而是反复出题考学生。每次考试,他都会出三道题:一道标准例题作为“锚点”(Anchor),一道长得和锚点差不多的相似题(Positive),一道完全不同的题(Negative)。学生要做的,是把相似题往锚点上“靠”,把不同题“推远”。 但这里面藏着门道。如果老师闭着眼睛从题库随机抽题,十道里有九道太简单,学生闭着眼都会做,练一百遍也没进步。如果老师专挑最刁钻的题,学生可能被考到怀疑人生,心态崩溃。聪明的老师会选那种“有点难、但跳一跳够得着”的题——这就是“半困难样本挖掘”的智慧。 在深度学习中,这个“出题老师”就是采样策略。无数实验证明:选什么三元组来训练,比损失函数本身更能决定模型的好坏。同一个 Triplet Loss 公式,配不同的采样策略,效果天差地别。本文就用“老师出题”这条线,带你彻底搞懂三元组采样与优化的全部套路。
对 AI 从业者:掌握三元组采样策略,是度量学习、人脸识别、向量检索等方向算法工程师的核心竞争力——同样的模型结构,采样策略不同,效果天差地别。
目录
可视化演示
本节配有交互动效(进入视口后自动加载,约 3KB)。
L = max(d(A,P) - d(A,N) + \u03b1, 0) Loss 历史曲线 嵌入空间维度 1 嵌入空间维度 2
三元组概念:一场“三缺一”的配对游戏
老师在黑板上写下一道例题,又贴出两张学生的答卷:一张步骤工整、思路相近,另一张牛头不对马嘴。他要让学生回答:“哪张答卷跟例题是一路的?” 这就是一场三缺一的配对游戏——一道例题、一张相似答卷、一张差异答卷,三个角色缺一不可。
在深度学习中,这三个角色分别叫锚点(Anchor)、正样本(Positive)和负样本(Negative)。模型把每个样本映射成一个高维向量,向量之间的距离就代表相似程度:距离近说明像,距离远说明不像。Triplet Loss 的训练目标,就是让锚点与正样本的距离越来越近,同时把锚点与负样本的距离越推越远。
但“近”和“远”要有个量化标准,于是引入了边际参数 margin(记为 $m$)。它相当于老师规定的“安全线”:负样本必须比正样本离锚点至少远 $m$,才算这道题真正过关。三元组损失公式如下:
老师出的每道题都有“难度系数”:有些题学生已经能轻松辨别,有些题却让学生抓耳挠腮。而选什么难度的题来考,决定了一个学生(模型)能不能高效进步——这正是接下来采样策略要解决的问题。
- 锚点-正样本距离$d(a,p)$ 表示锚点与正样本在嵌入空间中的欧氏距离,数值越小代表两者越相似
- 锚点-负样本距离$d(a,n)$ 表示锚点与负样本的距离,训练希望这个值尽量大
- 边际参数$m$ 是安全缓冲,要求 $d(a,n)$ 至少比 $d(a,p)$ 大 $m$,通常取值 $0.2 \sim 0.5$
- 截断为零$\max(\cdot, 0)$ 表示当 $d(a,p) + m < d(a,n)$ 时损失为 0,说明这个三元组已“学会”,无需再提供梯度
随机采样:闭眼抽题的随缘教学
一位偷懒的老师懒得分析学生弱项,闭着眼睛从题海里随机抽题。运气好时抽到几道有区分度的题,运气差时整张卷子都是送分题。训练效果完全靠天吃饭,绝大多数时间都在做无用功。
随机采样就是这么一种“随缘”策略:每次从训练集中随机挑一个锚点、一个正样本和一个负样本组成三元组,完全不看它们好不好学。它的优点是实现极其简单——一行代码就能搞定,也永远不会让模型“崩溃”,因为绝大多数三元组本来就很容易。但问题恰恰出在这里:随着训练推进,超过九成的随机三元组都是“送分题”,损失值早就为 0,梯度也为 0,对模型更新毫无贡献。
换句话说,模型每吃下一百道题,真正有营养的可能只有两三道。剩下九十七道的计算全部白费,却未必能换来足够的训练信号。从期望的角度看,随机采样的损失期望可以写成:
随机采样就像让一个初中生天天做小学口算——不能说完全没用,但想靠这个考上重点高中,怕是要等到猴年马月。于是,老师们开始琢磨:能不能专挑学生不会的题来练?
- 期望符号$\mathbb{E}$ 表示对所有可能三元组的损失取平均,衡量“平均一道题能提供多少梯度”
- 随机抽取$(a,p,n)\sim\mathcal{D}$ 表示三元组从数据分布中均匀独立抽取,不施加任何筛选条件
- 期望被稀释因为大多数三元组满足 $d(a,n) > d(a,p)+m$,期望中大量项为 0,模型中真实的更新信号被严重稀释
困难样本挖掘:专挑最难的题往死里练
既然送分题没用,那就反着来:老师专门翻出学生最近错得最离谱的题目,哪道不会讲哪道,哪道做错练哪道。这就是“困难样本挖掘”——专挑让模型最头疼的负样本,也就是那些比正样本离锚点还近的“伪装者”。
困难负样本的定义非常直观:一个负样本与锚点的距离 $d(a,n)$ 竟然小于正样本与锚点的距离 $d(a,p)$,说明模型此刻把“不同的人”错认成了“同一个人”。这种样本给出的梯度极大,能在几步之内让模型猛然醒悟——收敛速度往往是随机采样的好几倍。
但物极必反。如果整个 batch 塞满了极端困难样本,模型的参数会像坐过山车一样剧烈震荡。每次更新都试图把那些“几乎重合”的负样本强行推开老远,梯度动辄爆炸,loss 不降反升,最终模型彻底报废。就好比老师把历年奥数决赛压轴题全部堆给学生做,学生不仅没进步,反而被打击到厌学。
困难负样本的判定条件可以写成:
困难样本是“猛药”,能治病也能致命。聪明的老师不会天天上猛药——他们会把题目难度调到“比学生水平高一点点,但不至于完全做不出来”,也就是下一节要讲的半困难采样。
- 困难判定条件$d(a,n) < d(a,p)$ 表示负样本比正样本离锚点更近,模型当前无法区分它们
- 损失必然为正该条件下 $d(a,p) - d(a,n) + m > m > 0$,一定产生正损失,梯度信号强
- 极端情形当 $d(a,n) \approx 0$ 时,损失接近 $d(a,p)+m$,梯度接近理论最大值,极易引发参数跳变
半困难采样:跳一跳够得着的黄金难度
最好的老师从不会把最难的题直接甩给学生。他会选那些“比学生目前水平高一点点”的题:有点挑战,但不至于让人崩溃。这种“跳一跳够得着”的难度,才最能激发学习动力。
半困难负样本正是这种思路的数学化。它要求负样本比正样本离锚点稍远——说明模型已经隐约感觉到这俩不一样——但又没远到超过 margin 的安全区——说明模型还没完全学会。这个“中间地带”里的负样本,是训练性价比最高的素材。FaceNet 等经典工作当年就是靠这种采样策略,在人脸识别上取得了突破性进展。
它的定义可以概括为一个简单的距离不等式:
半困难采样之所以成为实践中的默认选择,正是因为它站在了“学得动”和“学得会”的黄金交叉点上。但这里还有一个绕不开的问题:这些“好题”到底从哪儿找?是课后批改全部作业慢慢挑,还是当堂提问现场抓?这就引出了离线挖掘与在线挖掘之争。
- 下界$d(a,n) > d(a,p)$ 确保负样本不会比正样本还近,避免了灾难性的极端梯度
- 上界$d(a,n) < d(a,p) + m$ 确保负样本还没达到“安全距离”,模型仍需继续发力推开
- 损失恒为正且有界综合上下界,损失落在 $(0, m)$ 区间内,既有信号又不会爆炸
离线挖掘与在线挖掘:课后批卷与当堂提问
传统老师怎么知道学生哪里不会?先把全班作业抱回家,熬夜逐一批改,圈出错题,明天再印成卷子讲。这种“课后批卷”的模式,就是离线挖掘:每隔若干个 epoch,用当前模型把整个数据集的样本两两距离全算一遍,离线筛选出最困难的三元组,再拿这些“精选题目”喂给模型。
但离线挖掘有个先天缺陷——时差。模型每更新一步,所有样本的嵌入表示都在变,昨天挖出来的困难样本,今天可能早就被模型学会了。而且全数据集两两计算距离的开销极大,几百万样本算一次要耗费大量 GPU 资源。相比之下,在线挖掘就像老师当堂扫一眼学生的表情,立刻点名提问:在每个 batch 内部,直接用当前模型算出这个小批次里所有样本的距离,现挖现用。每次迭代拿到的困难样本都是“新鲜出炉”的。
从计算量上可以直观地看到两者的差距:
在线挖掘凭借“即时、便宜、鲜活”三大优势,成了如今训练 Triplet Loss 的事实标准。但不管在线还是离线,都得先把“考卷”出好——下一个问题来了:一个 batch 里到底该放哪些类别的样本?
- 在线距离矩阵$O(B^2)$ 表示只需计算一个 batch 内两两距离,$B$ 通常为 32~512 个样本
- 离线距离矩阵$O(N^2)$ 表示要计算全数据集的两两距离,$N$ 可能是百万量级
- 量级对比$N \gg B$,离线全量挖掘的时间和存储成本比在线高出四到五个数量级,且结果仍会迅速过时
批次构建:考卷上的“题型搭配”
出考卷讲究题型搭配:全是选择题,学生蒙也能蒙对;全是压轴大题,学生做不完。聪明的老师会合理分配题型比例,让每道题都能考出学生的真实水平。对应到训练中,batch 的构建方式直接影响在线挖掘的效果。
最经典的方案是 P×K 策略:每个 batch 随机挑 P 个身份(类别),每个身份取 K 张图片(样本)。这样一来,batch 里既有足够的“同班同学”(正样本),又有大量的“别班同学”(负样本),每个锚点都能抓到正负对比,在线挖掘才有米下锅。如果完全随机采样,一个 batch 里可能全是不同类别的人,压根找不到正样本,Triplet Loss 直接失效。
P×K 构造下,一个 batch 里能组成多少合法三元组呢?把乘法原理一项项拆开:
以 P=32、K=4 为例,一个 batch 共 128 个样本,却能拼出上万组三元组——考卷上的每道小题都被重复利用到了极致。题型搭配好了,剩下的就是老师长期的教学智慧了。
- 锚点总数$PK$ 是 batch 内的总样本数,每个样本都可以作为一次“例题”
- 每个锚点的正样本数$K-1$ 是同一身份里除去锚点本身的其他样本
- 每个锚点的负样本数$PK-K$ 是 batch 内其他身份的全部样本
- 总组合数三者连乘即为全部合法三元组数量,随 P、K 增长而迅速膨胀,训练时要留意显存开销
训练实战:动态调整出题节奏
一位真正资深的老师,手里永远捏着好几套难度不同的卷子:开学用基础卷,期中用提高卷,期末才上综合卷。他不会一上来就抛压轴题,也不会永远停留在送分题。这就是训练实战的核心心法——渐进式调整。
实际训练 Triplet Loss 模型时,最稳妥的路径是:前期用半困难采样或随机采样打底,让模型对数据有个基本认知;随着 loss 的下降,再逐步提高困难样本的比例,加大训练强度。margin 的调节同样要“温柔”:从 0.2 起步,如果验证集表现稳定,再慢慢增加到 0.5;一旦发现 loss 震荡,立刻退回更小的 margin 或改用半困难采样。
为了防止训练崩溃,工程上还有三件套:梯度裁剪(把梯度的模限制在合理范围)、低学习率(减小每一步的更新幅度)、以及监控嵌入空间中正负样本的平均距离。综合这些考量,实际训练中常用的总损失可以写成:
好老师最厉害的地方,不是懂得多,而是知道什么时候上难度、什么时候降难度。训练 Triplet Loss 也一样——采样策略的“度”把握好了,模型效果自然水到渠成。
- Triplet 主损失第一项是三元组损失本身,负责拉近正样本、推开负样本
- 权重衰减项$\lambda \lVert W \rVert_2^2$ 对网络参数做 L2 正则,防止模型在艰难采样下过拟合
- 平衡系数$\lambda$ 通常取 $10^{-4} \sim 10^{-3}$,太小没约束力,太大会压制主损失
AI 应用场景
人脸识别门禁系统
阶段:特征嵌入学习与模型训练
解决的问题:传统分类损失训练的人脸模型,遇到“没见过的脸”就束手无策——分类器只能输出训练时见过的类别。Triplet Loss 配合在线半困难挖掘,让模型学到“谁和谁长得像”的度量空间。这样每个人注册时只需录入一次人脸,之后任意角度、任意光线下的照片都能准确匹配。
具体案例:某园区门禁系统要为 10 万员工建人脸库,最初用随机采样训练 FaceNet 结构,误识率居高不下。改用 BatchHard 在线挖掘 + P=32、K=4 的批次构建后,同样算力下识别准确率从 92% 提升到 98.6%,误放行率降低了五倍。
电商以图搜图
阶段:商品表征学习与向量索引
解决的问题:用户上传一张衣服照片,系统要在千万商品库中找出同款。不同商家拍的同一件衣服,角度、光线、背景都不同,Triplet Loss 的使命就是把“同款不同图”拉近、“不同款”推开。采样策略直接决定检索结果能不能分清“同款”和“撞款”这种细微差别。
具体案例:某电商平台用 P×K 策略(P=64、K=5)训练服装检索模型,每个 batch 里同一款衣服至少有 5 张不同环境下的照片,在线挖掘出大量“看起来像但其实是不同款”的困难负样本。线上 Top-10 命中率提升了 15%,用户搜图下单转化率显著提高。
行人重识别(ReID)
阶段:监控视频中跨镜头目标关联
解决的问题:同一个嫌疑人在不同摄像头下要能被关联起来,这对模型的判别力要求极高——不同人可能穿着相似衣服,同一个人在不同角度下可能差别很大。采样策略的好坏直接决定 ReID 模型的性能天花板。
具体案例:在公开数据集 Market-1501 上,同一套 ResNet 骨干网络,把随机采样换成在线半困难挖掘后,mAP 指标从 55% 跃升到 78%。这个提升幅度比更换任何损失函数变化都大,直观印证了“采样策略 > 损失函数”的经验法则。
Python 演示
import torchimport torch.nn.functional as F def batch_hard_triplet_loss(embeddings, labels, margin=0.5): """在线困难样本挖掘:每个 anchor 取最难正样本和最难负样本。""" emb_norm = F.normalize(embeddings, p=2, dim=1) sim_matrix = torch.matmul(emb_norm, emb_norm.T) dist_matrix = 1.0 - sim_matrix batch_size = embeddings.size(0) mask = labels.unsqueeze(0) == labels.unsqueeze(1) mask.fill_diagonal_(False) neg_mask = labels.unsqueeze(0) != labels.unsqueeze(1) loss = torch.zeros(batch_size, device=embeddings.device) count = 0 for i in range(batch_size): pos_dists = dist_matrix[i][mask[i]] neg_dists = dist_matrix[i][neg_mask[i]] if pos_dists.numel() == 0 or neg_dists.numel() == 0: continue hardest_pos = pos_dists.max() hardest_neg = neg_dists.min() loss[i] = torch.clamp(hardest_pos - hardest_neg + margin, min=0.0) count += 1 if count == 0: return torch.tensor(0.0, device=embeddings.device) return loss.sum() / count def batch_all_triplet_loss(embeddings, labels, margin=0.5): """朴素版本:使用 batch 内所有合法三元组的平均损失。""" emb_norm = F.normalize(embeddings, p=2, dim=1) sim = torch.matmul(emb_norm, emb_norm.T) dist = 1.0 - sim batch_size = embeddings.size(0) pos_mask = labels.unsqueeze(0) == labels.unsqueeze(1) pos_mask.fill_diagonal_(False) neg_mask = labels.unsqueeze(0) != labels.unsqueeze(1) anchor_pos = dist.unsqueeze(2).expand(batch_size, batch_size, batch_size) anchor_neg = dist.unsqueeze(1).expand(batch_size, batch_size, batch_size) losses = torch.clamp(anchor_pos - anchor_neg + margin, min=0.0) valid_mask = (pos_mask.unsqueeze(2) & neg_mask.unsqueeze(1)).float() return (losses * valid_mask).sum() / (valid_mask.sum() + 1e-8) if __name__ == "__main__": torch.manual_seed(42) fake_emb = torch.randn(6, 64) fake_label = torch.tensor([0, 0, 1, 1, 2, 2]) l_hard = batch_hard_triplet_loss(fake_emb, fake_label) l_all = batch_all_triplet_loss(fake_emb, fake_label) print(f"BatchHard loss: {l_hard.item():.4f}") print(f"BatchAll loss: {l_all.item():.4f}")
emb_norm = F.normalize(embeddings, p=2, dim=1)先把嵌入向量归一化,让距离计算稳定且可比sim_matrix = torch.matmul(emb_norm, emb_norm.T)一次矩阵乘法算出 batch 内所有样本两两相似度dist_matrix = 1.0 - sim_matrix相似度转距离:距离近 = 相似度高 = 长得像mask = labels.unsqueeze(0) == labels.unsqueeze(1)广播比较,得到“哪些样本是同一身份”的布尔矩阵mask.fill_diagonal_(False)排除自己和自己配对的情况,锚点不能既是锚点又是正样本hardest_pos = pos_dists.max()在线挖掘最难正样本:同身份里距离最远的那个,最需要拉近hardest_neg = neg_dists.min()在线挖掘最难负样本:不同身份里距离最近的那个,最需要推开loss[i] = torch.clamp(hardest_pos - hardest_neg + margin, min=0.0)若负样本已经够远,损失截断为 0valid_mask = (pos_mask.unsqueeze(2) & neg_mask.unsqueeze(1)).float()构造三维合法三元组掩码,锚点与正样本同身份、与负样本不同身份
常见误区
误区我以为 Triplet Loss 的公式才是训练效果的核心,采样嘛,随便用随机采样凑合一下就行。
正解无数实验证明,采样策略对训练效果的影响远大于损失函数本身的形式。同一个 Triplet Loss,随机采样训练 100 个 epoch 可能还不如半困难采样训练 10 个 epoch 的效果。采样策略决定了“模型每天吃什么题”,比“怎么打分”更关键。
为何常见:损失公式看起来高深莫测,容易被当成主角,忽略了“喂什么数据”才是决定模型上限的根源。
误区既然送分题没用,那就全用最难的题,训练肯定突飞猛进。
正解全困难样本会让梯度方差巨大,模型参数剧烈震荡甚至发散。正确做法是渐进式:前期用半困难打底,模型有基础后再逐步增加困难样本比例。就像健身不能第一天就上最大重量,否则肌肉撕裂反而练不成。
为何常见:凭直觉认为“难度 = 效果”,忽略了训练稳定性这个工程约束。
误区在线挖掘只看一个 batch,视野太窄;离线挖掘在全数据集上找困难样本,信息更全,效果肯定更好。
正解离线挖掘有两个致命伤:一是计算代价巨大,百万级数据两两算距离基本不可行;二是有“时差”——模型每更新一步,困难样本集合就变了,离线挖出来的样本很快就过时。在线挖掘虽然只看 batch 内部,但每次都用最新模型即时采样,性价比远高于离线。
为何常见:只看“信息量”的多少,忽视了“时效性”和“计算成本”这两个工程维度。
误区margin 越大,要求负样本离得越远,学到的特征应该越“纯净”。
正解margin 过大相当于给模型布置了一个不可能完成的任务。很多三元组永远无法满足 $d(a,n) > d(a,p) + m$,损失永远降不到 0,训练迟迟无法收敛。合适的 margin 应该匹配数据的自然分布,通常从 0.2 开始调,而不是贪心求大。
为何常见:把 margin 误解为“训练强度”而非“能力要求”,忽略了模型表达能力的物理上限。
误区随机采样全是送分题,浪费时间浪费算力,应该彻底禁用。
正解随机采样在训练初期能提供稳定的梯度基线,相当于学生的“基础题热身”;它实现简单、永远不会崩溃,是做基线实验的最佳选择。而且随机性本身是一种天然正则化,能防止模型过早陷入局部最优。它的定位是“基础套餐”,不是“垃圾套餐”。
为何常见:只看到随机采样“效率低”的缺点,看不到它在训练节奏和稳定性上的独特价值。
面试题
Q1. Triplet Loss 的三元组由哪三个元素构成?
- A.锚点、正样本、负样本← 正确答案
- B.输入、输出、标签
- C.训练集、验证集、测试集
- D.卷积层、池化层、全连接层
答案:A。Triplet Loss 的三元组固定由锚点(Anchor)、正样本(Positive)、负样本(Negative)组成。锚点是参考对象,正样本与锚点同类别,负样本与锚点不同类别。其他选项描述的都是深度学习中的其他概念,与三元组无关。
Q2. 下列哪个是“困难负样本”的典型定义?
- A.距离锚点比正样本更近的负样本← 正确答案
- B.距离锚点比正样本更远的负样本
- C.距离锚点恰好等于正样本距离的样本
- D.随机挑选且与锚点距离较大的样本
答案:A。困难负样本指 $d(a,n) < d(a,p)$ 的负样本,即它比正样本还“像”锚点,模型当前很难区分。B 描述的是一般负样本,C 是边界情况,D 与困难样本恰恰相反。
Q3. 随机采样训练 Triplet Loss 收敛慢的根本原因是?
- A.大多数三元组损失为零,梯度稀疏← 正确答案
- B.随机采样使用的 batch 尺寸太小
- C.损失函数存在大量局部极小值点
- D.模型参数初始化方式不符合要求
答案:A。训练推进后,大部分随机三元组都满足 $d(a,n) > d(a,p)+m$,损失为 0、梯度为 0,模型得不到有效更新信号。不是 batch 太小、也不是初始化问题,而是“送分题太多”稀释了有效梯度。
Q4. 半困难负样本满足哪个距离关系?(设 $d(a,p)$ 为锚点与正样本距离,$m$ 为 margin)
- A.负样本比正样本离锚点更近
- B.负样本离锚点远到超过安全区
- C.负样本比正样本远但未到安全区← 正确答案
- D.负样本与锚点距离恰好等于正样本
答案:C。半困难的定义是 $d(a,p) < d(a,n) < d(a,p)+m$,即负样本已经比正样本远(有基本区分),但还没到安全距离(仍需学习)。A 是困难样本,B 是容易样本,D 属于罕见边界情况。
Q5. 在线挖掘相比离线挖掘的主要优势是?
- A.使用最新模型挖掘样本,时效性高← 正确答案
- B.能一次性看到整个数据集的分布
- C.不需要构造批次的类别信息
- D.训练时不需要计算两两距离
答案:A。在线挖掘在每个 batch 内基于当前模型即时采样,样本“新鲜不过时”;离线挖掘基于旧模型全量计算,挖出的困难样本很快失效。B 是离线挖掘的特点而非优势,C 和 D 都与实际不符——在线挖掘仍然需要类别标签来构建三元组。
Q6. 以下哪些是困难样本挖掘可能带来的风险?
- A.梯度方差过大导致训练震荡← 正确答案
- B.模型被极端样本带偏甚至崩溃← 正确答案
- C.模型收敛速度大幅提升
- D.训练阶段的损失始终为零
答案:AB。困难样本梯度极大,batch 内全是极端样本时梯度方差飙升,参数剧烈震荡(A);长期训练下模型可能被少数“伪难样本”带偏,整体崩溃(B)。C 是困难挖掘的好处而非风险,D 与困难样本的强梯度特征矛盾。
Q7. 为什么在线挖掘常用 P×K 方式构造 Batch?
- A.保证每个锚点都有正负样本对比← 正确答案
- B.减少训练集的整体存储占用空间
- C.避免模型过拟合到特定类别样本
- D.让每个批次的计算复杂度显著下降
答案:A。P×K 策略随机选 P 个身份、每个身份取 K 个样本,确保 batch 内每个锚点都有 K-1 个正样本和 P(K-1) 个负样本可用。如果完全随机采样,batch 内可能没有同类样本,三元组根本构不成。B、C、D 都偏离了 P×K 设计的核心动机。
Q8. 以下哪些方式能有效防止 Triplet Loss 训练崩溃?
- A.训练初期使用半困难采样← 正确答案
- B.设置过大的 margin 值
- C.对梯度进行裁剪操作← 正确答案
- D.调低学习率减小更新幅度← 正确答案
答案:ACD。半困难采样梯度温和,适合冷启动(A);梯度裁剪把更新幅度限制在安全范围(C);低学习率减小每一步的风险(D)。B 反而会加大训练难度,让更多三元组无法满足 margin 要求,更容易导致不收敛。
Q9. 关于离线挖掘的局限,下列说法正确的是?
- A.挖掘出的难样本随模型更新而过时← 正确答案
- B.全量距离计算的存储开销几乎为零
- C.离线挖掘不需要保存模型中间状态
- D.离线挖掘总能取得更稳定的收敛
答案:A。模型每步都在更新,离线挖掘基于旧模型算出的困难样本会迅速“过时”,这是它的核心局限。全量两两距离的存储开销是 $O(N^2)$,远非零(B 错);离线挖掘同样需要保存模型状态(C 错);由于时差问题,它通常不如在线稳定(D 错)。
Q10. 若 Batch 用 P=3、K=4 的 P×K 方式构建,合法三元组总数约为多少?
- A.96 个左右
- B.144 个左右
- C.288 个左右← 正确答案
- D.576 个左右
答案:C。根据公式 $PK \times (K-1) \times (PK-K)$,代入 P=3、K=4 得:锚点数 $PK=12$,每个锚点的正样本数 $K-1=3$,负样本数 $PK-K=8$,总数为 $12 \times 3 \times 8 = 288$。这显示一个仅 12 个样本的小 batch 也能拼出数百个三元组,侧面说明在线挖掘的组合效率之高。