把搜索想象成一座巨大的相亲角。过去的老式红娘(传统搜索引擎)只看“硬条件”——你搜“西红柿炒蛋的做法”,她只给你找字面上带着“西红柿”或“炒蛋”的内容,完全不懂你在问怎么做菜。就算你搜“如何做番茄炒鸡蛋”,她也只会机械地数共同词。于是,两个明明很配的人和内容,因为自我介绍里没有重复的字眼,就永远错过了彼此。 深度学习排序模型 DSSM 和它的大弟子 CDSSM,就是这样一位聪明的“语义红娘”。她先把每个人的自我介绍、经历、喜好统统读一遍,浓缩成几个“性格标签”——也就是数学里的语义向量。然后她把所有人都放进同一个“性格空间”里:两个向量靠得越近,说明越合拍。这套方法,让搜索从“看字”进化到“懂心”。 本文从相亲角的老规矩讲起,一路看到新红娘的底层工作台:词哈希编码、多层神经网络、余弦打分、卷积进阶、训练数据构造,以及工程落地。
理解 DSSM/CDSSM 是掌握深度排序的入门钥匙。这套“双塔编码+相似度打分”的框架,是搜索、推荐、问答系统做语义召回的基本功,几乎所有 AI 排序工程师都会被问到。
可视化演示
展示DSSM/CDSSM深度排序模型的双塔编码架构、Word Hashing语义映射与Listwise Softmax训练损失优化过程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
DSSM双塔语义匹配 查询塔和文档塔分别将文本编码为128维语义向量 双塔结构对称但参数独立,输出通过余弦相似度匹配 sim(q,d)=cos(yq,yd)=(yq·yd)/(||yq||·||yd||) 双塔架构支持文档向量离线预计算,实现高效在线检索
语义匹配困境
旧红娘相亲只看“户口本”——学历、房车、身高体重。搜索引擎的老办法也这样:把查询里的关键词拿到文档里数重复次数,重复得越多分越高,TF-IDF 就是这种户口本逻辑。但问题是,两个真正合适的人和内容,可能一个相同的词都没有。搜“夜跑装备推荐”,一篇标题为“夜间运动小贴士”的文章字面上一个词都不重,却恰恰是用户想看的。
这种只看字面重合的局限,学术界叫“词汇鸿沟”。为了跨越它,老办法是人工设计特征:BM25、PageRank、文档类型、点击率……每加一类特征就像红娘多问一句“你是否有车”。但特征写得再多,也堵不住层出不穷的语言表达,而且每加一个特征都要重新调参。
传统 Learning to Rank 把排序打分看成特征加权求和。下面这个公式是典型词面匹配得分,它只统计查询词在文档中出现的频率:
旧红娘的档案柜里堆满了标签,却依然撮合不了“一个爱猫、一个养布偶”的两个人。要打破僵局,需要一位能读懂弦外之音的新红娘——这正是深度语义模型登场的原因。
- 遍历查询词把查询里的每个词都拿出来算一遍,一个不漏。
- 词频项词 t 在文档 d 里出现的次数,次数越高,字面匹配强度越大。
- 逆文档频率衡量词 t 的区分度,越稀有的词权重越大,常见词权重被压低。
- 相乘累加每个词的“频率 x 重要度”相乘后总加和,最终得分完全由字面重合决定。
词哈希编码
新红娘录入资料时,不直接存原话,而是把每句话拆成一个个“表情碎片”。“我喜欢猫”和“我家养了布偶猫”会被拆成大量重叠的小碎片,哪怕说法完全不同,红娘也能察觉到他们在聊同一件事。
DSSM 处理词的方式如出一辙:它不维护一个几十万词的“户口本词汇表”,而是把每个单词切成 3 个字母一组的小片段(trigram)。比如 “good“ 补上边界符变成 ”#good#“,切成 ”#go”、"goo"、"ood"、"od#" 四个碎片。这套操作叫 word hashing。
通过 n-gram 碎片映射,词表大小从百万级压到 3 万左右,同时天然缓解“没见过的拼写变体”问题。数学上,word hashing 把一个词 w 映射成一个稀疏向量,每一维对应一个 n-gram 碎片是否存在:
其中 $G(w)$ 是 w 的所有 n-gram 碎片集合。
红娘的数据库里存的全是碎片,新朋友的奇怪口癖也能对上大半,这比死记硬背完整词表聪明得多。不过碎片只是素材,真正读懂人,还得靠红娘大脑里的分析流程。
- 碎片集合把单词 w 拆出的全部 n-gram 碎片放进集合,例如 cat 对应 #ca、cat、at#。
- 指示函数判断碎片 g 是否在集合里的“在场开关”,在则为 1,不在则为 0。
- 向量化把所有碎片对应的 0/1 依次排开,得到词对应的稀疏向量。
- 整体作用该向量替代原始 one-hot 词向量,输入维度从几十万降到几万,并覆盖更多拼写变体。
DSSM网络结构
有了碎片化输入,红娘开始做“心理测评”。她不直接看碎片,而是一层层抽象:第一层看出“他常提到猫”,第二层总结“他喜欢毛茸茸动物”,第三层提炼“他有耐心、有爱心”。DSSM 的深层网络就像这套层层递进的测评问卷。
DSSM(Deep Structured Semantic Model)采用多层全连接神经网络,把高维稀疏的 word hashing 向量逐层压缩,最终输出低维稠密向量——语义向量。原论文里输入是 30 万维左右的 trigram 向量,经过 300→300→128 的 tanh 变换,变成 128 维。
记编码网络为 f(·),输入向量为 x,则每一层变换可写成:
其中 W 和 b 是网络权重与偏置,f 是 tanh 激活函数。
红娘用同一套测评流程给 A 画像,也给 B 画像。只要流程统一,两张画像就能放进同一个空间直接比较。
- 输入向量word hashing 生成的稀疏向量,携带词的碎片特征。
- 线性映射权重矩阵 W 与输入相乘并加偏置,完成一次坐标变换。
- 非线性激活tanh 函数把线性结果压到 -1 到 1 之间,为网络引入表达能力。
- 逐层堆叠前一层的输出作为后一层的输入,层的数量代表抽象层级。
- 语义输出最后一层得到低维稠密向量,就是可用于匹配的语义画像。
余弦相似度
画像出来了,怎么判断两人合不合拍?红娘不搞模糊印象,而是量夹角:把两个人的画像想象成从原点射出的两根箭头,箭头靠得越近(夹角越小),说明越相似。
DSSM 把 query 编码成向量 y_q,把 doc 编码成 y_d,两者落在同一个 128 维语义空间,它们之间的余弦相似度直接作为相关性得分。分数越高,文档在搜索结果里排得越靠前。
红娘用夹角测量给所有相亲者排序,一秒钟排出“最可能聊得来”的 Top10。搜索引擎做的事完全一样:把候选文档按余弦分数从高到低排成一列,返回给用户。
- 分子内积两个向量逐维相乘再求和,衡量各维是否“同向共振”。
- 分母模长分别计算两个向量的长度,用于抵消向量长短差异。
- 归一化比值内积除以两个模长,结果只反映方向是否一致。
- 取值范围结果落在 [-1, 1],排序时按从大到小排列候选文档。
CDSSM卷积
老红娘看档案只看孤立关键词,新红娘则要“听语气”。一个人写“我家猫是老大”,另一个人写“猫把我家沙发抓了”,字面大不相同,但语气里都透着“又爱又恨”。CDSSM 就是能听语气的新红娘——它不单看每个词,还看连续几个词的组合。
CDSSM(Convolutional DSSM)在 DSSM 基础上引入卷积神经网络。先用滑动窗口(通常覆盖 3 个词)扫过整个句子,提取局部短语特征;再用 max-pooling 把全文特征压成一个定长向量;最后接 DSSM 式全连接层输出语义向量。
给定词向量序列 $x_1,\dots,x_T$,长度为 ℓ 的卷积核在第 i 个窗口上计算:
这里的 $x_{i:i+\ell-1}$ 表示把从 i 开始的 ℓ 个连续词向量拼接成窗口矩阵。
有了卷积读心术,CDSSM 红娘对“一顿饭的功夫”“猫主子”这类依赖语境的表达更有把握。多层 CNN 组合出短语级语义,比只看单个词的 DSSM 更敏锐。
- 词序列一个句子里的词按先后顺序排好,每个词已经用向量表示。
- 窗口拼接从第 i 个词开始截取连续 ℓ 个词向量,拼成一个窗口矩阵。
- 卷积核点积可学习的权重 W 与窗口矩阵做点积,再加偏置。
- 非线性输出tanh 激活得到第 i 个位置的局部特征值。
- 池化综合对整个特征图取最大值,保留最强响应,形成短语级语义特征。
训练数据
再厉害的红娘也需要大量实践。她每天翻看平台的配对记录:哪些约会成了(用户点击了),哪些被无视(用户没点)。看得多了,她就知道什么样的人配什么样的人。
DSSM 的训练数据来自搜索日志。用户输入 query 后,搜索引擎返回一批结果,用户点击的文档是正样本,未点击的文档是负样本候选。由于未点击文档远多于点击的,需要用负样本采样构造训练对。
常用训练目标:对每个 query,取 1 个正例 d⁺ 和 k 个负例,希望正例得分在候选里概率最大:
负样本采样就像红娘故意把“明显不合适的候选人”和“优质候选人”放一起对比,帮她眼光更犀利。可如果负样本全是完全不着调的人,红娘反而学不到细微差别——采样策略大有讲究。
- 余弦得分正样本的相似度得分,来自前文的余弦公式。
- 缩放因子γ 把得分放大或缩小,控制概率分布的“尖锐”程度。
- 指数运算相似度被映射为正数,分数越高指数越大,差距被拉大。
- 分母总和正样本指数加上所有负样本指数,构成一个“归一化牌桌”。
- 概率输出比值即正样本被选中的概率,训练目标是让这个概率尽量接近 1。
工程实践
红娘培训完开张营业,讲究效率。热门文档的画像可以提前画好存档;用户提出新需求时,红娘只要现场画用户的像,再去档案库里快速找画像最接近的人。
工程上,DSSM 的 query 侧和 doc 侧编码器共享参数。doc 向量离线批量算好,用 ANN(近似最近邻)索引存储;线上收到 query 后只算一遍 query 向量,再在索引里做近似检索,亿级文档也能毫秒级返回候选。
离线评估排序质量常用 NDCG@k(归一化折损累计增益):
其中 rel_i 是第 i 位结果的标注相关度,分子是实际排序的折损累计增益,分母是理想排序的折损累计增益。
红娘岗前培训靠日志,上岗靠档案库和索引——DSSM 的工程化正是如此。离线训练与在线服务分离,让深度排序在工业界真正落地。
- 相关增益把相关度评分映射为指数增益,高相关内容的收益会爆发式增长。
- 位置折损位置越靠后,分母越大,收益被压得越低。
- 累计求和前 k 位的折后增益相加,得到当前排序的质量分。
- 理想排序分母使用最优排序下的 rel_i^*,作为满分参照。
- 归一化实际分数除以理想分数,NDCG 落在 [0, 1],越大越好。
应用展望
红娘这门生意越做越大:从相亲平台扩展到职场搭档推荐、兴趣社群匹配。DSSM 的语义匹配思想也从搜索出发,蔓延到推荐、问答、广告等一切“两段内容要合拍”的场景。
DSSM 是深度语义匹配的鼻祖框架,后来的 Sentence-BERT、DPR 和各种预训练排序模型,大多可以看作“双塔编码 + 相似度打分”的继承与升级。双塔结构因可以预计算 doc 向量、在线快速检索,成了工业界主流召回范式。
双塔的抽象表达:
其中 f 是各自塔的编码器,g 是打分函数(如余弦相似度或一层 MLP)。
从 DSSM 到 CDSSM,再到 BERT 时代的交叉编码器,红娘的工具一直在升级。不变的是那个朴素愿望:让懂的人与懂的人相遇,让好内容恰好被需要它的人看见。
- 查询塔编码把 query 映射成语义向量的编码器,θ_q 是其参数。
- 文档塔编码把 doc 映射进同一语义空间的编码器。
- 打分函数对两个语义向量算合拍度,常用余弦相似度。
- 参数选择双塔可共享参数,也可因两侧文本分布差异而独立训练。
- 部署方式doc 向量离线预计算,线上只计算 query 侧,支撑亿级检索。
AI 应用场景
搜索引擎语义召回
阶段:召回阶段(Recall)
解决的问题:传统倒排索引只能召回包含查询关键词的文档,遇到同义改写、口语化提问就失效。DSSM 双塔把 query 和 doc 分别编码成语义向量,通过向量检索把“字面不同、语义相近”的文档提前捞回,再交给精排模型。
具体案例:某电商 App 搜索“苹果手机壳”,系统先用 DSSM 在千万商品库中做 ANN 检索,成功召回标题为“iPhone 保护套”“手机防摔壳”的商品,即使它们和查询词没有任何字面重合。
推荐系统向量匹配
阶段:粗排阶段(早期候选过滤)
解决的问题:推荐系统要从海量 item 中快速筛选与用户兴趣匹配的候选,人工规则覆盖不全。双塔模型一侧编码用户行为序列,一侧编码 item 描述,用向量相似度做大规模快速匹配。
具体案例:视频平台用 CDSSM 风格模型,用户侧编码最近看过的视频标题,物品侧编码标题和标签,通过向量召回风格相似的下一批视频。
智能客服FAQ匹配
阶段:意图匹配 / 知识检索
解决的问题:用户提问五花八门,FAQ 库里的标准问题有限,字面匹配漏召回率极高。双塔模型把用户问题与 FAQ 问题映射到同一语义空间,用余弦相似度找最接近的标准问题。
具体案例:银行客服机器人把“银行卡丢了怎么办”和 FAQ 库中的“储蓄卡挂失流程”成功匹配,两者字面几乎无重合,但语义高度一致。
Python 演示
import torchimport torch.nn as nnimport torch.nn.functional as F def word_hash(word, n=3): # 将单词切分为 n-gram 碎片, 模拟 DSSM 的 word hashing padded = '#' + word + '#' return [padded[i:i + n] for i in range(len(padded) - n + 1)] class DSSM(nn.Module): # 双塔编码器: query 和 doc 共用同一个 encode 网络 def __init__(self, vocab_size, hidden=128, dim=64): super(DSSM, self).__init__() self.encoder = nn.Sequential( nn.Linear(vocab_size, hidden), nn.Tanh(), nn.Linear(hidden, dim), ) def encode(self, x): # 输出归一化的语义向量 v = self.encoder(x) return F.normalize(v, p=2, dim=1) def forward(self, query, pos_doc, neg_docs): # 正负样本分别编码后计算余弦相似度, 并用 softmax 构造损失 q = self.encode(query) pos = self.encode(pos_doc) negs = [self.encode(neg) for neg in neg_docs] pos_score = (q * pos).sum(dim=1) * 10.0 neg_scores = torch.stack([(q * neg).sum(dim=1) * 10.0 for neg in negs], dim=1) logits = torch.cat([pos_score.unsqueeze(1), neg_scores], dim=1) labels = torch.zeros(len(query), dtype=torch.long) return F.cross_entropy(logits, labels) if __name__ == '__main__': # 模拟一个小 batch: 8 个 query, 每个配 1 个正例和 4 个负例 torch.manual_seed(0) model = DSSM(vocab_size=3000) opt = torch.optim.Adam(model.parameters(), lr=0.01) q = torch.randn(8, 3000) pos = torch.randn(8, 3000) negs = [torch.randn(8, 3000) for _ in range(4)] for step in range(30): loss = model(q, pos, negs) opt.zero_grad() loss.backward() opt.step() if step % 10 == 0: print('step', step, 'loss', round(loss.item(), 4))
return [padded[i:i+n] for i in range(len(padded) - n + 1)]把单词切成连续 n-gram 碎片,对应 DSSM 的 word hashing 实现。self.encoder = nn.Sequential(nn.Linear(...), nn.Tanh(), nn.Linear(...))搭建多层全连接编码网络,对应 DSSM 的深层非线性变换。v = self.encoder(x)输入经过编码网络得到语义向量,query 和 doc 共用同一网络。return F.normalize(v, p=2, dim=1)对向量做 L2 归一化,让余弦相似度只与方向有关。pos_score = (q * pos).sum(dim=1) * 10.0逐维相乘再求和得到余弦相似度,乘 10 对应公式里的 γ 缩放。return F.cross_entropy(logits, labels)对正负样本得分做 softmax 交叉熵损失,训练模型把概率压给正例。
常见误区
误区我以为 DSSM 就是把词换成同义词再匹配,本质还是字面匹配的升级版。
正解DSSM 的多层网络学习的是“隐藏在字面背后的语义结构”,连“西红柿”和“番茄”这种表面完全不同、但在相似上下文里出现的词,都能在语义空间中靠得很近。它不是做替换,而是把两段文本映射到同一把“语义尺子”上量距离。
为何常见:因为大家习惯了“关键词匹配=搜索”的直觉,容易把语义向量想象成高级一些的词表映射。
误区把单词拆成 n-gram 碎片后,词义肯定损失得一干二净,模型还怎么理解“苹果”是水果还是手机?
正解word hashing 的本意是用工程上的小幅信息折损换取大规模泛化。词表里没见过的词在 n-gram 空间里会和见过的词共享碎片,从而获得一个初始表示;真正的语义理解由后续网络层完成,碎片只是原始素材。
为何常见:因为“压缩=损失”是直觉,但实际上 n-gram 保持了词的局部拼写结构,信息并没有想象中丢得多。
误区训练 DSSM 时,负样本采样越多,模型学得越充分。
正解负样本太多会让训练变慢,且大量“完全无关”的简单负样本让模型学不到细微的区分度。实践上常用随机负样本 + 困难负样本混合,比例在 1:4 到 1:50 之间调参,既要效率也要区分度。
为何常见:直觉上“例子多=学得好”,但忽略了类别不平衡和简单负样本带来的梯度淹没。
误区DSSM 是深度模型,应该直接取代 BM25 做最终精排。
正解双塔结构把 query 和 doc 独立编码,最后才做一次相似度计算,中间看不到 query-doc 的交叉特征,语义表达能力有上限。工业界一般用它做召回或粗排,精排阶段改用交叉编码器(如 BERT 全交互),让模型“对着看”两个文本。
为何常见:因为 DSSM 名气大,又是深度模型,容易高估它的排序上限,忽略了双塔结构的信息瓶颈。
误区拿到用户点击日志就能直接训练,不用清洗。
正解点击日志有严重的 position bias——排在前面的文档天然更容易被点,哪怕内容不相关。训练前要做位置偏差校正、异常点击过滤、时间窗口过滤,否则模型学到的可能是“排前面的就是好文档”。
为何常见:因为点击日志是成本最低的行为数据,人们容易忽略它背后的用户行为偏差。
面试题
Q1. DSSM 将查询和文档转换为哪种表示后进行相关性计算?
- A.词频向量直接计算
- B.固定维度语义向量← 正确答案
- C.每篇文档的摘要
- D.TF-IDF 稀疏向量
答案:B。DSSM 的核心思路是用深度网络把查询和文档都压缩成固定维度的语义向量,再算相似度。词频向量和 TF-IDF 向量都停留在字面匹配层面,摘要则根本不是模型输出。语义向量让“西红柿”和“番茄”这类表达不同的内容能直接比较。
Q2. word hashing 的主要作用是什么?
- A.扩大词表覆盖范围
- B.降低输入维度并缓解生词问题← 正确答案
- C.让模型学习词语顺序
- D.替代注意力机制
答案:B。word hashing 把单词拆成 n-gram 碎片,词表从几十万降到几万,同时没见过的词也能用碎片拼出表示,缓解 OOV(生词)问题。它并不建模词序,更不是注意力机制。
Q3. DSSM 中衡量 query 与 doc 匹配度通常使用哪种相似度?
- A.欧氏距离
- B.曼哈顿距离
- C.余弦相似度← 正确答案
- D.KL 散度
答案:C。DSSM 原始论文使用余弦相似度衡量两个语义向量的方向一致性。欧氏距离受向量长度影响大,KL 散度针对概率分布,都不如余弦相似度符合“语义方向一致”的直觉。
Q4. CDSSM 相比 DSSM 的核心改进是什么?
- A.引入 CNN 建模局部上下文← 正确答案
- B.使用更大的词表
- C.增加随机负样本数量
- D.用 GRU 替代全连接层
答案:A。CDSSM 在 DSSM 基础上加入卷积神经网络,用滑动窗口捕捉连续词的搭配信息,再池化压缩成短语级语义特征。扩大词表和增加负样本都不是结构改进,GRU 属于循环结构而非卷积。
Q5. DSSM 训练时,正样本通常来自哪里?
- A.人工标注的四级相关度
- B.用户点击日志中被点击的文档← 正确答案
- C.随机生成的虚拟文档
- D.爬虫抓取的全部网页
答案:B。DSSM 的训练信号来自搜索日志:用户点击的文档视为正例。人工标注成本太高且规模有限,随机生成文档没有意义,全部网页更不可能当作正例。点击日志让训练数据可以大规模自动生成。
Q6. 关于 DSSM 的工程部署,以下说法正确的是?
- A.查询和文档向量都必须在线实时计算
- B.文档向量可离线预计算,线上只算查询向量← 正确答案
- C.双塔结构无法用 ANN 加速
- D.余弦相似度必须转成欧氏距离才能排序
答案:B。DSSM 双塔的核心工程优势就是 doc 向量可以离线算好并建 ANN 索引,线上只对 query 编码一次,再近邻检索。这正是双塔结构能在亿级文档下落地的原因。
Q7. DSSM 训练目标中 γ(gamma)的作用是什么?
- A.控制模型学习率
- B.缩放相似度分数,调整 softmax 的锐度← 正确答案
- C.控制负样本的数量
- D.平衡正负样本的权重
答案:B。γ 是温度缩放因子,乘在余弦相似度上。γ 越大,正负样本分数差异被放大,softmax 概率越尖锐;γ 越小,概率分布越平滑。它不控制学习率、负样本数量或样本权重。
Q8. 以下哪些属于 DSSM 的局限性?
- A.双塔结构缺少 query 和 doc 的交叉特征← 正确答案
- B.word hashing 会损失部分词序信息← 正确答案
- C.完全无法处理任何生词
- D.训练数据依赖点击日志,存在位置偏差← 正确答案
答案:ABD。双塔只在最终打分时交互一次,无法像交叉编码器那样逐词对看,所以缺交叉特征;word hashing 拆碎词后确实丢失词序;点击日志有位置偏差需要校正。C 错误,因为 word hashing 恰恰缓解了生词问题。
Q9. 负样本采样策略中,常用的正确做法包括?
- A.随机采样不相关文档← 正确答案
- B.只采样相关文档
- C.混合困难负样本← 正确答案
- D.只取点击率最高的文档
答案:AC。随机负采样提供基础区分度,困难负样本帮模型学到细微差别,两者常混合使用。只采样相关文档会让模型失去反例,只取点击率最高的文档则引入严重偏差。
Q10. 下列属于 DSSM 之后深度排序模型发展思路的有?
- A.用交叉编码器做 query-doc 注意力交互← 正确答案
- B.基于预训练语言模型做重排序← 正确答案
- C.完全放弃向量表示,退回字面匹配
- D.双塔编码加 ANN 召回← 正确答案
答案:ABD。交叉编码器和预训练模型是精排阶段的升级方案,双塔加 ANN 是召回阶段的工业标准,它们都是 DSSM 思想的延续。C 是倒退,不符合深度排序的发展趋势。