资讯动态

推荐系统相似度计算全解析:从余弦到Jaccard的工程选型指南

发布时间:2026/10/14 1:15:37 来源:尧图企业网站定制
做推荐系统的同学几乎每天都在跟“相似度”这三个字打交道。召回、粗排、去重、多样性控制甚至冷启动里的相似品映射背后全是同一个问题两个用户或者两个物品到底“像不像”像多少这个问题的答案直接决定你给谁推了什么、候选池里哪些东西被拎出来排序。很多刚入行的朋友看了不少原理文章知道余弦相似度和皮尔逊相关系数的公式真到工程里选型的时候却又卡住了——算出来分值总是怪怪的或者效果跟预期对不上。这篇文章就用实际推荐项目里的场景把常用相似度计算方法逐个拆开讲清楚包括公式背后的直觉、代码实现、适用边界以及我踩过的一些坑。这篇内容适合正在做推荐、搜索或者做用户画像的算法工程师和数据分析师也适合刚接触推荐系统、想理解相似度计算底层逻辑的学习者。我尽量不用教科书式的推导堆砌而是从“什么时候该用哪个、算出来怎么解释、数据不干净怎么处理”这些实际角度来写。看完以后你可以直接拿文章里的实现思路去处理自己的特征向量、Embedding和用户行为序列。1. 相似度计算在推荐系统里的位置与选型思路1.1 为什么所有推荐流程都绕不开相似度先想一个问题一个日活百万的产品物品库几十万甚至上百万用户来了之后你怎么从这么多东西里挑出他可能感兴趣的几十个不可能实时把全量物品遍历一遍做排序成本扛不住。所以工业级推荐系统的通用套路是“召回 排序”先用轻量级方法从全量物品里取回几百上千个候选再用复杂模型精排。这两个环节里相似度计算都是地基。召回阶段最常见的就是协同过滤和向量检索。协同过滤的Item-Based做法就是预先计算“物品与物品”的相似度矩阵用户看了某部电影就把它最像的几个物品拉回来。向量检索则是把用户和物品都映射成Embedding向量然后用近邻搜索找最相似的TopK。这两个场景核心都是相似度。排序阶段也没跑掉粗排模型里经常要拼上“用户最近交互过的物品和当前候选物品的相似度”作为一维特征甚至做多样性打散时也要用相似度衡量两个物品会不会挤占同一个兴趣点。可以说相似度计算的水准决定了推荐结果的“直觉合理性”。算得太糙明明是同类型的东西分值很高结果里混入一堆八竿子打不着的玩意算得太精细线上性能又扛不住。理解每种相似度的数学含义和工程代价是选对方案的前提。1.2 相似度家族分类与适用场景速览相似度计算方法看着很多但归一下类就清楚了。从底层逻辑看主要分四大家族几何距离类欧氏距离、曼哈顿距离、汉明距离。核心思想是“向量在空间里离得近就相似”适合连续稠密特征或者本身就是坐标性质的向量。夹角类余弦相似度。只看方向不看幅度适合文本向量、用户兴趣向量这种“长度没意义、方向才有意义”的数据。相关类皮尔逊相关系数。把向量先中心化再算夹角能消除用户打分尺度偏差适合评分型数据。集合类Jaccard相似度、Overlap系数。把特征当成集合看交集比例适合离散ID、标签、布尔型行为。先把这个分类记住后面遇到具体问题就能快速圈定方向。我见过不少人一上来就默认用余弦结果处理二值化行为序列时效果稀烂换成Jaccard立刻好很多。选型不是越复杂越好而是越贴合数据性质越好。下表是我在实际项目里的经验对照后面每个方法都会详细展开方法数据适配类型对数值幅度敏感度典型场景欧氏距离稠密连续向量高图像特征、坐标类特征、降维后的向量余弦相似度稠密/稀疏向量低文本向量、用户兴趣向量、Embedding召回皮尔逊相关系数评分型稠密向量中消除后不敏感用户评分、打分偏好协同过滤Jaccard相似度离散集合/布尔向量不适用标签匹配、行为交集、冷启动映射曼哈顿距离稠密连续向量高鲁棒性要求高的距离场景汉明距离二值向量不适用签名向量、近重复检测2. 五种常用相似度的原理拆解与计算实操2.1 余弦相似度向量夹角的直觉余弦相似度应该是推荐系统里出场率最高的方法没有之一。它的公式是cos(A, B) (A·B) / (|A| × |B|)也就是两个向量的点积除以两个向量模长的乘积。几何意义是计算两个向量在空间里的夹角余弦值夹角越小越相似。关键特点是它只关心方向不关心向量的绝对长度。为什么推荐场景经常用它因为用户行为数据的“幅度”经常不可靠。举个例子A用户特别喜欢点东西一天点了50个赞B用户很克制一周才点5个赞。如果用欧氏距离这两个用户即使兴趣完全相同算出来的距离也会很远因为向量长度差太多。但余弦相似度只看每个维度上的比例结构点50个赞和点5个赞的人如果喜欢的品类比例一样算出来的相似度会很高。实际计算的时候很多人直接用现成的库函数但工程里有个常用优化先把向量做L2归一化也就是把每个向量除以它的模长让所有向量长度变成1。归一化后余弦相似度就变成两个归一化向量的点积。从计算上看少了一次除法从性能上看归一化向量只存一份后面每次计算都省去模长计算。更重要的是归一化后的向量可以直接对接向量检索库里的内积运算很多近邻搜索库对内积做过深度优化速度比余弦快不少。下面是Python实现不依赖复杂库用NumPy就够import numpy as np def cosine_similarity(a, b): dot np.dot(a, b) norm_a np.linalg.norm(a) norm_b np.linalg.norm(b) if norm_a 0 or norm_b 0: return 0.0 return dot / (norm_a * norm_b) # 归一化后的加速版 def l2_normalize(vec): norm np.linalg.norm(vec) if norm 0: return vec return vec / norm def cosine_similarity_normalized(a, b): a_norm l2_normalize(a) b_norm l2_normalize(b) return np.dot(a_norm, b_norm)注意归一化版本里有个隐藏问题如果原始向量是稀疏的L2归一化后还是稀疏的但点积计算时如果直接用稠密数组跑内存会爆掉。实际工程里要么用稀疏矩阵存储要么用专门的向量检索库下文第3部分会讲。我举一个具体例子来感受数值。假设物品A的类目特征向量是[1, 2, 3, 4, 5]物品B是[2, 3, 4, 5, 6]算出来余弦相似度约等于0.994几乎是强相似因为两个向量的方向高度一致。但如果物品C是[5, 4, 3, 2, 1]和A的余弦相似度算出来大概0.64方向差异开始体现了。这个数值范围也方便设定阈值一般0.9以上认为强相关0.7到0.9中等相关0.5以下基本不相关。但阈值这个东西要看业务文本向量的相似度分布偏紧评分向量的分布偏松不要一套阈值走天下。2.2 欧氏距离绝对差值的度量欧氏距离就是我们在中学学的两点间距离公式的推广d(A, B) sqrt(Σ(ai - bi)^2)它计算的是两个向量在空间里的直线距离。跟余弦不同欧氏距离对向量的每一个维度的数值大小都很敏感。哪怕两个向量方向完全一致但一个长度是另一个的10倍欧氏距离也会很大。所以在推荐系统里欧氏距离最适合那些“幅度本身就有意义”的数据。比如用户画像里的年龄、消费金额、活跃天数这些统计型特征数值大本身就代表更强的消费力或者更深的活跃度这时候用欧氏距离是合理的。再比如图像向量模型输出的特征向量每一维数值经过充分训练幅度分布相对稳定用欧氏距离做相似度也很常见。工程实现同样简单def euclidean_distance(a, b): return np.sqrt(np.sum((a - b) ** 2)) # 更高效一点 def euclidean_distance_fast(a, b): diff a - b return np.linalg.norm(diff)欧氏距离和余弦相似度之间其实有数学上的关联。对L2归一化后的向量欧氏距离和余弦相似度存在单调对应关系两个归一化向量夹角越小欧氏距离就越小。原因很直观归一化之后所有向量都落在单位球面上夹角大小直接体现为球面上的弧长也就对应弦长弦越短离得越近。所以你在网上经常看到“归一化后用欧氏距离等价于余弦相似度”这个说法这是对的但有个前提所有参与计算的向量都必须做了严格L2归一化。这个性质有什么用如果你的向量检索库只支持欧氏距离计算不支持余弦那你可以把所有向量L2归一化然后用欧氏距离检索效果和余弦基本一致。我用这套方案解决过不少向量库的兼容问题不用换库就能上线余弦类召回。注意事项也在这里如果数据本来就不该归一化或者归一化会丢失业务含义就别强行套这个转换。比如你要比较的是“消费金额绝对值”归一化以后等于把消费能力抹平了用欧氏距离的意义就丢了。2.3 皮尔逊相关系数去均值化的余弦皮尔逊相关系数表面上看公式比余弦复杂但只要换一个角度它就是余弦的一个变种。它的定义是r Σ(ai - μa)(bi - μb) / (sqrt(Σ(ai - μa)^2) * sqrt(Σ(bi - μb)^2))其中μa和μb分别是向量A和B的均值。换句话说先对两个向量的每个维度减去自身均值再做余弦计算。减均值这个操作把两个向量的“基准线”对齐了它衡量的是两个向量围绕各自均值波动模式是否一致。在推荐场景里这个操作非常实用。因为用户打分的行为习惯差异太大了。有人打分从来只给4分和5分有人觉得3分已经算高分还有人偏爱给极端分。如果直接用余弦相似度比较两个用户的原始评分向量那个“只给4分和5分”的用户和“3分算高分”的用户哪怕对同一批物品的偏好顺序完全一致算出来的相似度也不会高因为两人所有维度的数值都系统性偏移。皮尔逊相关系数通过减均值把这个偏移抹掉了它看到的是两个用户“相对自己平时标准”的偏好形状是否一致。看个具体计算。A的评分向量[4, 5, 4, 5, 4]B的评分向量[3, 4, 3, 4, 3]。这两个向量的余弦相似度其实已经很高了因为方向一致但皮尔逊值会更高接近1。如果你加入一个用户C评分是[1, 2, 1, 2, 1]C和B的余弦相似度也很高但皮尔逊系数同样是接近1因为三个人都是相同的波动模式。这就是皮尔逊擅长处理的场景用户间一致的偏好形状可以被稳定捕捉。代码实现也不复杂def pearson_similarity(a, b): a_centered a - np.mean(a) b_centered b - np.mean(b) denom np.sqrt(np.sum(a_centered ** 2)) * np.sqrt(np.sum(b_centered ** 2)) if denom 0: return 0.0 return np.dot(a_centered, b_centered) / denom工程上有两个坑提醒一下。第一当向量长度极短时皮尔逊系数很不稳定。比如用户只对两个物品打过分只有两个维度那么任何两个用户在这两个维度上的点都可以连成一条直线皮尔逊系数要么是1要么是-1完全没有区分度。这种情况要设置最小交互数阈值比如至少有过5个共同评分的用户才进入相似度计算。第二皮尔逊系数的分母里包含向量的标准差如果一个向量的所有值都一样比如所有评分都是4分那它的标准差为0算出来会直接除零。代码里要对这种情况做保护返回0或者其他默认值。2.4 Jaccard相似度集合视角的直观度量Jaccard相似度可能是所有相似度里最好理解的一个。它算的是两个集合的交集大小除以并集大小J(A, B) |A ∩ B| / |A ∪ B|举个例子用户A点过的商品是{手机, 耳机, 充电器, 手机壳}用户B点过的商品是{手机, 耳机, 键盘, 鼠标}交集是{手机, 耳机}两个并集是六个Jaccard就是2/6约等于0.333。简单、直观、稳定。这个指标特别适合稀疏和二值化的场景。什么叫二值化就是特征只有0和1比如用户是否购买过某个品类是否点击过某篇文章是否安装过某个App。对这些数据你用余弦相似度计算实际上也是在比较0/1模式但结果和Jaccard并不完全一样。余弦看重的是“两个向量在哪些维度上同时为1”但那些“同时为0”的维度也会参与计算且产生权重这在某些场景下会带来偏差。Jaccard只关心两个集合里共同出现过的元素完全不考虑那些都没出现的维度更符合“共同兴趣”的直觉。推荐系统里的经典用法是做物品的“行为向量相似度”。比如物品X被用户集合U1购买过物品Y被用户集合U2购买过U1和U2的交集越大说明这两个物品经常被同一批人购买它们之间就越相似。这个相似度做基于物品的协同过滤召回很好用尤其是数据稀疏的冷启动阶段效果往往比向量相似度更稳。代码实现有两种一种直接用Python集合一种用NumPy向量化def jaccard_similarity_set(set_a, set_b): inter len(set_a set_b) union len(set_a | set_b) if union 0: return 0.0 return inter / union # 向量版本输入为0/1数组 def jaccard_similarity_vec(a, b): a_bool a.astype(bool) b_bool b.astype(bool) inter np.sum(a_bool b_bool) union np.sum(a_bool | b_bool) if union 0: return 0.0 return inter / unionJaccard的局限性也明显它完全丢弃了数值幅度信息。用户A一个月买了10次手机壳用户B只买过1次在Jaccard眼里这两个用户对手机壳的偏好程度一样。所以如果你手里的数据有明确的行为次数或评分强度Jaccard会浪费信息这时候应该考虑带权重的相似度或者先做数值分档再转成集合。我常用的办法是把行为次数归一化后分桶比如0次、1-2次、3次以上映射成三个不同的特征ID再算Jaccard能在保留部分强度信息的同时维持集合方法的稳定性。2.5 补充两个常被忽略的距离曼哈顿与汉明除了上面几个主力方法还有两个场景化很强的距离值得掌握。曼哈顿距离又叫城市街区距离公式是向量各维度绝对差之和d(A, B) Σ|ai - bi|和欧氏距离的区别在于欧氏距离走的是“直线最短路径”曼哈顿距离走的是“只能横平竖直走”。在推荐场景里曼哈顿距离的鲁棒性通常比欧氏距离好因为平方运算会放大离群维度的影响力而绝对值不会。比如两个向量在大部分维度上一致只有一个维度差了一倍欧氏距离会被这个维度主导曼哈顿距离受的影响就小得多。如果你的特征里有明显的噪声或者异常值又想保留距离语义曼哈顿距离值得考虑。汉明距离则是为二值向量设计的定义是两个等长向量中不同位的个数。比如10110和11010有两位不同汉明距离就是2。它在推荐里最常见的应用是做粗去重和签名比对。比如你把物品的标签集合哈希成固定长度的签名向量用汉明距离快速判断两个物品重不重复或者做“看过的物品里哪些和当前物品接近”的实时检索。汉明距离在工程上有一个非常有用的性质可以用位运算极速计算两个64位整数做异或再数一下1的个数几十亿规模的数据也能实时算完。现在很多向量检索库在实现二值签名的近邻查找时底层走的就是这个思路。3. 工程实现与踩坑实录3.1 手写全套代码从浮点计算到批量矩阵我一直觉得相似度计算这种事第一遍最好是自己手写而不是直接上封装好的库因为手写一遍你就把边界条件全摸清了。下面是全套代码包含上面所有方法以及批量计算版本import numpy as np class SimilarityCalculator: staticmethod def cosine(a, b): dot np.dot(a, b) norm_a np.linalg.norm(a) norm_b np.linalg.norm(b) if norm_a 0 or norm_b 0: return 0.0 return dot / (norm_a * norm_b) staticmethod def euclidean(a, b): return np.sqrt(np.sum((a - b) ** 2)) staticmethod def manhattan(a, b): return np.sum(np.abs(a - b)) staticmethod def pearson(a, b): a_centered a - np.mean(a) b_centered b - np.mean(b) denom np.sqrt(np.sum(a_centered ** 2)) * np.sqrt(np.sum(b_centered ** 2)) if denom 0: return 0.0 return np.dot(a_centered, b_centered) / denom staticmethod def jaccard(a, b): a_bool a.astype(bool) b_bool b.astype(bool) inter np.sum(a_bool b_bool) union np.sum(a_bool | b_bool) if union 0: return 0.0 return inter / union staticmethod def hamming(a, b): return np.sum(a.astype(bool) ! b.astype(bool)) # 批量计算两个矩阵所有行两两之间的余弦相似度 def batch_cosine(matrix_a, matrix_b): # L2归一化后再用矩阵乘法 norm_a np.linalg.norm(matrix_a, axis1, keepdimsTrue) norm_b np.linalg.norm(matrix_b, axis1, keepdimsTrue) matrix_a_norm matrix_a / np.maximum(norm_a, 1e-12) matrix_b_norm matrix_b / np.maximum(norm_b, 1e-12) return np.dot(matrix_a_norm, matrix_b_norm.T)这里有一个很关键的工程技巧批量算余弦相似度永远不要用双层循环一定要转成矩阵运算。上面的batch_cosine函数里我用了L2归一化加矩阵点积的做法算10000行乘10000列的相似度矩阵也就几秒钟。如果写双层循环同样是10000乘10000循环一千万次跑完可以等一天。关于这里用了np.maximum(norm_a, 1e-12)而不是直接判断等于0原因是为了防除零同时保留数值稳定性这是实际项目里惯用的写法。批量计算还应考虑内存问题。100万物品的相似度矩阵就算用float32存储也需要1000000 × 1000000 × 4字节约4TB内存完全不可行。所以工程上要么用稀疏存储只保留TopK近邻要么用分块计算外存合并要么直接用近邻检索库。不要试图在内存里构建全量稠密相似度矩阵。3.2 数据预处理归一化、缺失值、稀疏矩阵存储进了实际项目你很快会意识到相似度算法的代码只是最不重要的一环。数据的形态直接决定算法效果。我在这里踩过两次大坑一次是归一化时机不对一次是缺失值没处理。归一化要分清“为什么要归一化”和“归一化给谁看”。前面讲过如果算法最终用的是归一化后的向量做点积那应该在离线把每个物品向量L2归一化后存起来线上计算时直接用存好的归一化向量做点积。这样省一次在线计算。但如果你用的是欧氏距离而且特征本身幅度有业务含义就不能把消费金额这类数值归一化掉否则等价于把消费能力抹了。我的经验是在做任何相似度计算之前先画一画特征分布图哪些特征方差大、哪些呈长尾分布然后决定是否要做log变换或标准化而不是上来就套StandardScaler。缺失值是最容易被忽视的。用户的评分矩阵天然稀疏用户没看过某部电影不是表示“不喜欢”而是“缺失”。但在余弦相似度的计算里缺失位置如果填0就会被当成“不喜欢”这会严重扭曲结果。解决思路有三种一是只计算共同非缺失维度上的相似度比如只取两个用户都打过分的物品维度算余弦但要注意此时余弦公式的分母也要基于共同维度重新计算二是用均值填充缺失值对皮尔逊这类方法效果不错三是直接上协同过滤类算法把缺失当成未知而不是0交给模型学习。我实际用过第一种比较多虽然计算复杂度高一点但语义最干净。稀疏矩阵的存储也是一个工程点。同样是100万用户×100万物品的交互矩阵密度如果只有0.01%稠密存储完全装不下但用COO格式或者CSR格式存储可能只要几百MB。做相似度计算时尽量使用支持稀疏矩阵运算的库把稀疏矩阵乘以稀疏矩阵用高效算法。不要习惯性转成NumPy稠密数组否则内存直接爆掉。3.3 性能优化向量化、批量、近邻检索工具最后聊一聊线上性能。相似度计算在推荐系统里不只离线做很多场景要线上实时算。用户点了某个物品需要立即从候选池里找出和这个物品最像的100个。这时候要求单次计算在毫秒级完成。如果你的候选池不大几千或者几万个直接用NumPy矩阵运算就能扛住。上面batch_cosine函数就是干这个的候选向量预先归一化好来一个查询向量做一次矩阵点积再取TopK几十毫秒内完成。候选池上了百万量级这个办法就不行了。此时应该用专门的近邻检索库比如Faiss。它对向量索引做了分层和量化支持百万、千万级的TopK召回延迟也在毫秒级。使用Faiss时有一个非常关键的细节索引类型选择。用IndexFlatIP内积索引时必须保证入库和查询的向量都是L2归一化后的因为内积在归一化向量上等于余弦。用IndexIVFFlat这类倒排索引时需要先训练聚类中心训练集的规模和质量会影响召回率。我见过有人把未归一化的向量直接灌进Faiss用内积检索结果召回结果一片混乱排查了很长时间才看出是归一化丢了。还有个小技巧如果数据是二值化的可以用带符号的位向量配合汉明距离来做快速的粗筛先在海量候选里粗筛出几千个再做精排级的相似度计算。这样既能享受汉明距离的低成本又不会损失精度。4. 常见问题排查与避坑速查4.1 为什么相似度结果全是奇怪的极值算出来的相似度要么几乎都是1要么全是-1这是新手最容易撞见的问题。最常见的原因就是没有处理好维度之间的对齐。向量A和向量B的维度含义必须一一对应如果A的第3维是“手机类目点击次数”B的第3维是“笔记本电脑类目点击次数”那算出来的相似度没有任何参考价值。尤其是从不同数据源拼特征时经常出现特征拼接顺序不一致的问题。我排查过一次诡异的“所有相似度都异常高”的问题最后发现是两个Embedding向量在拼接时一个用了特征名排序另一个用了原始顺序维度错位。另一个原因是数值尺度不一致。比如向量的前10维是0到1的小数特征后10维是0到10000的计数特征欧氏距离会被大数值的维度完全主导余弦相似度也会因为模长计算被大数值维度拉动小数值维度失去比较意义。处理方式是先做特征级别的标准化或者归一化。我建议对连续特征尤其是长尾分布的特征做log1p变换再加标准化能显著提升相似度计算的合理性。4.2 用户行为稀疏导致相似度失真怎么办推荐系统的数据天然是稀疏的用户平均交互过几百个物品而物品库有百万级两个用户共同交互过的物品数量可能少得可怜。这会导致两个问题一是交集过小时Jaccard和皮尔逊的数值方差极大二是大量用户之间相似度为0召回候选不足。应对交集过小的问题一个常规做法是设置最小交集阈值比如至少5个共同物品才计算相似度不满足的直接置0。另一个做法是改用向量化Embedding。把用户的行为序列映射成稠密向量虽然会损失部分可解释性但能有效缓解稀疏问题。再就是可以利用“间接相似”用户A和用户B没有直接交集但都跟用户C有相似行为可以通过A-C相似度和C-B相似度估算A-B的相似度。这种做法在冷启动阶段很有用。稀疏矩阵还有一个隐藏陷阱你想当然地用稠密矩阵存储然后一连串计算直接白白消耗大量内存。比如一个10万用户×10万物品的矩阵即使填充率只有0.5%稠密存储也要消耗80GB以上但稀疏存储只有几GB。做批量相似度计算前先确认数据格式是稀疏的。4.3 哪到底该选哪个相似度方法决策清单每回有人问我“应该用哪个相似度”我给的答案都是先看数据形态再看业务意图。这里整理一个决策清单遇到具体问题可以对着查情况推荐方法原因文本/兴趣Embedding向量方向比长度重要余弦相似度不受向量长度影响稳定适合召回特征包含消费金额、活跃度等具有实际幅度的数值欧氏距离/曼哈顿距离保留尺度信息幅度本身有意义用户评分数据用户打分尺度差异大皮尔逊相关系数去均值化消除打分偏移行为是点击/购买/收藏等二值事件交集语义强Jaccard相似度只关心共同交互自然贴合布尔数据需要从海量候选中快速粗筛汉明距离/近邻检索库计算代价低可做第一层召回特征包含离群值且不希望离群值主导曼哈顿距离绝对值求和鲁棒性优于欧氏距离选型的时候不要只看理论指标还要考虑工程成本。余弦相似度配合向量检索库是工业界最成熟的组合能解决八成以上的相似度需求。皮尔逊在评分矩阵上精度好但需要额外处理均值计算和除零。Jaccard思路简单上线速度快适合做冷启动和兜底策略。我的习惯是主路召回用余弦Embedding辅助路召回用Jaccard行为相似排序特征里加皮尔逊评分相似度三格并行各有各的用途。4.4 相似度分数如何转换成业务可用的权重很多调优场景里相似度计算完不是终点你还要把它变成推荐分数的一部分。这里最大的误区是直接把相似度分数当权重用不做任何变换。相似度分数的分布经常是偏态的大部分物品对的相似度集中在0.1到0.3少数同类物品在0.8以上。这种分布直接进去做加权和会导致少数头部相似度主导最终排序。我常用的做法是做分数校准。比如把相似度分数做min-max归一化到0到1或者做分位数映射把原始分数映射到对应的分位点。还有一种经验做法对相似度做指数变换比如用相似度的2次方或3次方作为权重可以拉大头部和尾部的差距让真正相似的物品在排序里获得更大优势。注意这个指数不要选太大否则会过度放大噪声。具体选几次方要根据线上AB实验效果来定没有统一最优参数。5. 最后说一点个人体会做推荐系统这几年我越来越意识到相似度方法本质上是你对“什么是相似”这个问题的一个建模假设。用余弦你假设用户兴趣是方向性的幅度不重要用欧氏你假设数值本身有绝对意义用Jaccard你假设只有共同出现才算数没发生的都不算数。没有哪种方法是绝对正确或绝对错误的只能看它跟你的业务场景和数据形态匹不匹配。如果非要说一个最实用的建议那就是把代码里的相似度计算方法封装成可替换的模块输入输出接口保持一致。这样你可以在离线实验里快速比较不同方法的表现而不是每次换一种方法就要改一遍上游下游代码。我在实际项目中就吃过这个亏早期把余弦算在业务代码里后面想换成皮尔逊牵一发动全身改了两三天。封装好了以后所有相似度方法对比实验跑一遍效果一目了然。最后分享一个调试技巧算完相似度之后不要只看分数把相似度最高的几十个结果拉出来人工看一眼。很多时候数学指标很漂亮业务上却完全不相关这种问题只有靠人工抽检才能发现。相似度的数值再精确也替代不了对业务本身的理解。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑