很多人接触推荐系统,第一本绕不开的书就是项亮的《推荐系统实践》。这本书出了好些年了,市面上讲推荐系统的资料多如牛毛,但能像它这样把协同过滤、隐语义模型、冷启动这些概念讲得明明白白,还配着能直接跑的代码,确实不多。说白了,这本书解决的核心问题就一个:怎么从零开始,把一个能用的推荐系统做出来,而不是停留在口号和概念上。
我当年就是靠这本书入的门,后来在电商、内容社区都做过推荐相关的项目,回头再看,书里的很多思路依然是底层逻辑。这次借着“项亮推荐系统实践”这个标题,加上最近“一文看懂推荐系统”“基于Spark的电商系统推荐”“电影推荐系统Scala”“就业岗位推荐系统”这些热门方向,我把整个推荐系统的核心框架、算法原理、工程落地的关键步骤,以及我实际踩过的坑,一次性梳理清楚。不管你是刚入门的学生,还是准备在项目里落地推荐功能的后端工程师,这篇都能给你一个能直接照着走的地图。
1. 先把推荐系统的骨架搭明白:数据、算法与反馈闭环
很多人一上来就啃协同过滤公式,结果越看越迷糊。我的建议是:先别碰算法,先把推荐系统到底在解决什么问题搞清楚。
1.1 推荐系统的本质是“匹配”加“过滤”
推荐系统本质上做的是两件事:第一,从海量物品里召回一小批用户可能感兴趣的候选集;第二,把这批候选集按用户偏好程度排个序,把最可能被点击、购买或观看的放到最前面。
这个逻辑听起来简单,但里面藏着一个关键点:推荐系统不是猜用户想要什么,而是在帮用户降低选择成本。博物馆展品太多,观众不知道看哪个,策展人就是推荐系统;视频平台内容几十万部,用户不可能全翻一遍,推荐系统就是那个最懂他的“朋友”。
项亮在书里反复强调一个观点:推荐系统的核心不是算法有多炫,而是数据。没有数据,再好的算法也是空中楼阁。这里的数据不只是用户注册时填的性别、年龄,更重要的是用户的行为数据:看了什么、点了什么、买了什么、在哪个页面停留了多久、把什么加入了购物车又删掉。
1.2 用户、物品、上下文是永远的铁三角
推荐系统里所有算法,最后都是围绕着三个对象在做文章:用户、物品、上下文。
- 用户:用户的长期兴趣和短期兴趣。长期兴趣比如“这个用户一直喜欢科幻片”,短期兴趣比如“他最近在搜王者荣耀相关视频”。
- 物品:物品本身的属性标签,比如电影的类型、导演、演员,电商商品的类目、价格、品牌。
- 上下文:时间、地点、设备、当前场景。晚上十点躺在床上的用户,和早上八点挤地铁的用户,推荐策略应该完全不同。
项亮书里有一个特别经典的“上下文”例子:同一个用户,在办公室和在家里,视频推荐的结果应该不一样。这个理念后来发展成了“情境感知推荐”,但在实际工程里,我们最先能落地的一般是时间维度——比如把“近一小时的热门内容”作为一个召回通道,效果立竿见影。
1.3 反馈闭环比算法本身更值钱
很多团队做推荐系统,上线第一版算法之后觉得万事大吉,其实这才是真正的开始。推荐系统的价值在于形成一个闭环:系统推荐→用户产生行为→行为回传→模型更新→推荐得更准。
我之前在内容社区做过一个视频推荐项目,第一版用协同过滤上线,离线指标不错,但线上点击率就是上不去。排查了很久,发现问题出在行为数据回传链路上:客户端上报的曝光日志有延迟,导致模型训练数据里“曝光未点击”样本严重缺失。没有负样本,模型根本学不会“什么东西用户不喜欢”,推荐结果当然越来越偏。
这给我一个特别深的教训:做推荐系统,从第一天起就要把日志采集、数据管道、效果监控当成一等公民,而不是算法跑通之后才补的功课。
2. 核心算法拆解:从协同过滤到隐语义模型,看懂它们为什么有效
项亮这本书最精彩的部分,就是他把协同过滤、隐语义模型、基于图的推荐这几个主流算法讲得特别透。我不按书里的目录顺序讲,而是按照我自己在实际项目里的选型经验来拆。
2.1 基于用户的协同过滤(UserCF):找和你相似的人
UserCF的核心思想特别朴素:物以类聚,人以群分。要给你推荐东西,先找到和你兴趣最相似的一群用户,看看这群人喜欢什么你没看过的东西,然后把这些东西推荐给你。
具体分三步:
- 建立用户-物品的评分矩阵(或者行为矩阵)。
- 计算用户之间的相似度,找到和目标用户最相似的K个用户。
- 把这K个用户喜欢的、但目标用户没有行为的物品,按权重排序推荐出来。
这里最关键的是相似度计算。项亮书里详细讲了余弦相似度和皮尔逊相关系数。我在实际项目里用的最多的是余弦相似度,因为它实现简单、计算高效,而且在行为数据是0/1(点击/未点击)的情况下表现足够稳定。
余弦相似度的公式长这样(以两个用户的物品评分向量为例):
import numpy as np def cosine_similarity(user_a, user_b): # 假设 user_a 和 user_b 是长度为N的评分向量,没有行为的物品记为0 dot = np.dot(user_a, user_b) norm_a = np.linalg.norm(user_a) norm_b = np.linalg.norm(user_b) if norm_a == 0 or norm_b == 0: return 0 return dot / (norm_a * norm_b)实际项目里要注意:不要对所有用户两两计算相似度,那样复杂度是O(N²),用户量百万级就扛不住了。工程上通用的做法是**“物品到用户的倒排索引”**——先找和目标用户有共同行为的用户,只在这部分用户里算相似度,能省掉几个数量级的计算量。
2.2 基于物品的协同过滤(ItemCF):推荐和你喜欢的东西相似的
ItemCF是目前工业界用得最广的协同过滤变体,也是亚马逊“买了又买”“看了又看”的经典实现。它的逻辑是:如果一个用户同时喜欢A和B,那A和B就是相似的;推荐时,找出用户喜欢的物品,再找出和这些物品相似的、用户没见过的物品来推荐。
注意,ItemCF说的“物品相似”不是内容上的相似,而是行为上的共现相似。比如说《变形金刚》和《复仇者联盟》内容完全不是一个系列,但如果大量用户都同时看了这两部电影,那它们就会被判定为“相似”。
项亮在书里详细对比了UserCF和ItemCF的适用场景,这个对比我至今还在用,直接做成表格:
| 维度 | UserCF | ItemCF |
|---|---|---|
| 适用场景 | 新闻、短视频等兴趣变化快的场景 | 电商、电影、图书等兴趣相对稳定的场景 |
| 实时性要求 | 高,用户有新行为后推荐结果要快速变化 | 相对低,物品相似度可以离线算好 |
| 冷启动物品 | 友好,新物品只要有用户行为就能被推荐 | 不友好,新物品没有行为就难被推荐 |
| 冷启动用户 | 不友好,新用户没有行为无法找相似用户 | 友好,新用户只要对一件物品感兴趣就能推荐相似物品 |
| 计算复杂度 | 用户量大时维护用户相似度表成本高 | 物品量通常小于用户量,物品相似度表更易维护 |
| 推荐解释性 | 弱,“和你相似的人喜欢”说服力一般 | 强,“和你喜欢的某某相似”很容易让用户信服 |
| 覆盖率风险 | 倾向于推荐热门物品,长尾挖掘能力弱 | 相比UserCF更容易挖掘长尾物品 |
这个表是我当年做了好几个推荐场景之后总结出来的,和项亮书里的观点基本一致。实际选型时,电商和视频网站绝大多数场景优先试ItemCF,因为它解释性强、精度高,而且物品数量通常比用户数量少一个量级,工程上好实现。
2.3 一个亲手调通的ItemCF案例:计算逻辑和优化点
我在一个电影推荐小项目里用Scala写过一个完整的ItemCF,结构很简单但非常实用,核心逻辑分成“离线计算物品相似度”和“在线召回”两部分。
离线部分(Spark + Scala):
// 伪代码:计算物品共现矩阵和物品相似度 val userItemRatings: RDD[(String, String, Double)] = // (userId, itemId, rating) // 1. 按用户分组,组内物品两两配对,统计共现次数 val itemCoOccur: RDD[((String, String), Int)] = userItemRatings .groupBy(_._1) .flatMap { case (_, items) => val itemList = items.map(x => (x._2, x._3)).toList for { (itemA, _) <- itemList (itemB, _) <- itemList if itemA != itemB } yield ((itemA, itemB), 1) } .reduceByKey(_ + _) // 2. 计算物品i被多少用户喜欢过(分母) val itemUserCount: RDD[(String, Int)] = userItemRatings .map { case (_, item, _) => (item, 1) } .reduceByKey(_ + _) // 3. 余弦相似度:coOccur(a,b) / sqrt(count(a) * count(b)) val similarity: RDD[((String, String), Double)] = itemCoOccur .join(itemUserCount) // 取a的count ...这里有一个容易被忽略的坑:余弦相似度公式里的分母要用sqrt,不是直接除。很多初学者把分母写成了count(a) * count(b),结果热门物品的相似度全被放大,推荐列表清一色都是爆款,长尾内容根本没有出头之日。
在线召回时,拿到用户最近点击过的N个物品,查物品相似度表,取每个物品最相似的TopK,合并去重后打分排序。打分公式一般这样:
score(user, item) = Σ (user对已有物品i的偏好 × sim(i, item))偏好值可以直接用点击次数、停留时长做归一化,也可以用隐式反馈的置信度。工程上我用过最简单的方案:最近7天点击过的物品权重设为1.0,更早的设为0.5,效果比统一权重好不少,这也是“时间衰减”思想的最朴素实现。
2.4 隐语义模型(LFM):把用户和物品都映射到同一个“兴趣向量空间”
协同过滤的优点是简单有效,但有两个硬伤:一是稀疏性,用户和物品的行为矩阵通常90%以上是空的;二是它本质上是“记忆”而不是“泛化”,很难挖掘出用户潜在的兴趣结构。
隐语义模型(Latent Factor Model)解决这个问题的思路是:把用户和物品都映射到一个K维的隐因子空间里,用户对物品的偏好,用两个向量的内积来刻画。
打个比方:假设K=3,三个维度分别代表“动作”“剧情”“喜剧”。某个用户的兴趣向量是(0.8, 0.5, 0.2),说明他偏爱动作片,其次是剧情片,不太爱喜剧。一部电影的属性向量是(0.9, 0.1, 0.3),两者内积得到0.8×0.9 + 0.5×0.1 + 0.2×0.3 = 0.83,分数高,系统就倾向于推荐这部电影。
这个K维向量不是人手工定义的,而是通过矩阵分解自动学出来的。最经典的方法是SVD(奇异值分解)及其变种,比如FunkSVD——它只分解已有的评分项,用随机梯度下降(SGD)迭代优化,避免了对稀疏矩阵做完整SVD的计算灾难。
FunkSVD的迭代更新逻辑,用Python写出来非常清晰:
def train_lfm(train_data, K=10, alpha=0.01, lamda=0.1, epochs=100): # train_data: [(userId, itemId, rating)] # 初始化用户向量和物品向量 user_vec = {uid: np.random.rand(K) for uid in set(u for u,_,_ in train_data)} item_vec = {iid: np.random.rand(K) for iid in set(i for _,i,_ in train_data)} for epoch in range(epochs): for uid, iid, rating in train_data: pred = np.dot(user_vec[uid], item_vec[iid]) error = rating - pred # 梯度下降更新 user_vec[uid] += alpha * (error * item_vec[iid] - lamda * user_vec[uid]) item_vec[iid] += alpha * (error * user_vec[uid] - lamda * item_vec[iid]) alpha *= 0.9 # 学习率衰减,避免后期震荡 return user_vec, item_vec这段代码的每个细节都值得琢磨:
K是隐因子数量,太小欠拟合,太大容易过拟合,一般取10~50之间,通过离线验证集调参。alpha是学习率,我一般从0.01起步,每轮衰减10%。不衰减的话,后期loss容易在最优解附近来回震荡。lamda是正则化系数,防止用户向量和物品向量的值无限膨胀,一般取0.01~0.1。- 训练时要把数据随机打乱,不然模型会学到样本顺序的假关联。
冷启动和实时性问题怎么解?LFM对用户冷启动依然不友好——新用户没有行为,根本没法给他生成向量。工业界的常规做法是“双通道”策略:新用户没有向量时,用热门榜、地域、设备等维度先兜底,等积累了足够行为再切到个性化模型。
3. 从算法到系统:离线和在线架构怎么设计,效果怎么评估
算法讲完了,真正难的是把它做成一个稳定、可扩展、效果可衡量的系统。这一节我重点讲工程落地,这是很多教程不会细讲、但实际项目里最花时间的部分。
3.1 分层架构:离线计算、近线更新、在线召回三层各司其职
一个成熟的推荐系统,绝对不是“用户来了现算推荐结果”这么简单。因为在线计算代价太高,延迟根本扛不住。正确做法是分三层:
离线层:每天凌晨跑批,计算物品相似度表、用户兴趣向量、热门榜单等。这些结果会写入Redis或HBase,供在线服务直接读取。离线层的特点是数据量大、计算复杂度高,但对时延不敏感。
近线层:每几分钟到几十分钟跑一次增量计算,处理用户刚产生的行为,更新用户最近的兴趣状态。比如用户刚刚点了一个视频,近线任务会马上更新他的“最近兴趣向量”,让下一次请求就能用上。这一层通常用Spark Streaming或者Flink实现。
在线层:用户请求进来后,毫秒级完成召回、粗排、精排、重排,返回最终的推荐结果。在线层不做什么复杂计算,主要是查表、合并、排序。
这个三层架构,项亮书里讲的是2008年前后的思路,但到今天依然是工业界的标准范式。区别只是在线层从单机服务变成了微服务集群,离线层从MapReduce变成了Spark + Flink。
3.2 效果评估:离线指标与在线实验缺一不可
推荐系统的效果评估,最容易犯的错就是“只盯离线指标,不看线上效果”。离线指标只是代理指标,线上真实反馈才是金标准。
离线指标:最常见的是准确率(Precision)、召回率(Recall)、F1、AUC、NDCG。这些指标可以帮你在训练阶段快速筛选模型,但千万别迷信。我见过一个模型,离线AUC刷到了0.85,上线后点击率反而跌了3%。原因很简单:离线测试集是历史数据的随机切分,它反映的是“模型能不能复现历史行为”,而不是“模型能不能提升用户的实际体验”。
在线指标:真正决定推荐系统价值的是AB实验。把用户随机分桶,实验组用新算法,对照组用旧算法(或规则、热门榜),观察一段时间内的核心指标差异。
做AB实验有几个铁律:
- 样本要足够大:至少持续一周,覆盖完整的周末流量。很多推荐系统周中和周末的用户行为差异巨大,只看工作日会得出错误结论。
- 不能只看点击率:点击率提高了,但人均时长下降了,说明推荐的食物变“标题党”了。核心指标应该是和你业务目标直接挂钩的指标,电商看GMV,内容平台看时长和留存。
- 辛普森悖论非常常见:整体点击率提升,但细分到每个用户群体都在下降。出现这种情况,赶紧查分桶是不是均匀的,或者是不是新算法对头部流量特别友好、对长尾流量反而更差。
3.3 冷启动的三个经典解法:这是推荐系统落地时避不开的坎
冷启动是每个推荐系统都要面对的问题,项亮书里专门写了一章。我把冷启动分成三类,每一类的解法我都实际验证过:
用户冷启动(新用户来了推什么):第一屏别硬推个性化内容,先上热门榜、编辑精选、地域热门这些“安全牌”。同时通过注册时的信息(感兴趣的话题、职业、所在城市)做粗粒度的兴趣匹配。等用户产生了3~5个行为后,再逐步切到个性化模型。我见过最有效的做法是“先推密集热门,再快速试探”:前10个物品全部从热门池里选,第11个开始混入个性化候选,观察用户反馈再调整比例。
物品冷启动(新物品怎么获得曝光):新物品没有行为数据,协同过滤根本推不出去。解法是在内容理解上下功夫:给物品打标签,基于标签向量做相似度匹配。比如新上一部电影,导演、主演、类型和《流浪地球》高度重合,那就可以先推荐给喜欢《流浪地球》的用户。这个方案的核心是建立一套靠谱的标签体系,标签质量直接决定冷启动效果的优劣。
系统冷启动(一个全新平台,什么数据都没有):这个阶段没什么捷径,只能用人工运营来“冷启动系统”。编辑手工挑选优质内容,打上详细标签,组成各种主题列表,用人工规则推荐。同时鼓励早期用户通过社交关系(关注、分享)产生行为数据。等数据量积累到一定程度,再切换成算法推荐。项亮书里也提到:冷启动时期的运营数据,是最宝贵的“种子数据”,一定要规范采集,后面训练模型都用得上。
4. 场景化实践:从电商到电影,再到就业岗位推荐,套路是通用的
“基于Spark的电商系统推荐”“电影推荐系统Scala”“就业岗位推荐系统”这几个热词反复出现,说明推荐系统的需求是跨行业的。我分别梳理一下这几个场景的落地要点,你会发现核心套路是完全一样的:召回到排序,离线到在线,数据到反馈。
4.1 电商推荐系统实战要点
电商推荐系统,核心指标是转化率和GMV,而不是点击率。这意味着:
- 召回通道要更多样:基于物品的协同过滤(看了又看)、基于用户的协同过滤(买过的人还买了)、热门榜、新品榜、同店推荐、搭配购。
- 排序阶段要把价格、折扣、店铺信誉、库存等因素加进去。用户在电商场景里,对价格的敏感度非常高。两个商品点击率相同,便宜的那个肯定更容易转化。
- 电商有一个独特的问题:复购。牙膏、洗衣液这种消耗品用户会反复购买,系统要记住用户的购买周期,在快用完的时候主动推荐,而不是每次都推一堆他不需要的新品。
性能方面,电商场景的并发量通常很高,大促期间尤其是。基于Spark的离线计算可以轻松应对百亿级行为数据的相似度计算,但在线服务的Redis缓存设计、降级策略一定要提前做好压测。
4.2 电影推荐系统(Scala实现)实操经验
电影推荐系统是校招简历上最常见的项目,也是项亮书里的核心案例。用Scala写电影推荐,我建议你从这几个模块入手:
- 数据预处理:解析MovieLens数据集(或者自己爬的豆瓣数据),解决评分数据里的时间戳、用户去重、电影信息补全等问题。
- 离线推荐:用Spark MLlib实现ALS(交替最小二乘法)矩阵分解,训练用户特征矩阵和电影特征矩阵,用RMSE评估效果。
- 实时推荐:用户对某个电影产生了评分行为后,通过Kafka + Spark Streaming获取行为,实时更新用户最近评分过的电影列表,然后从电影相似度矩阵里查出最相似的Top20,作为实时推荐候选。
- 推荐解释:给每个推荐结果附上“因为你看过《XXX》”,这个细节能大幅提升用户对推荐结果的信任度。
ALS训练时,rank(隐因子数)、lambda(正则化系数)、alpha(置信度参数)三个超参用网格搜索调一遍,一般能得到不错的基线。我复现过的一个经典参数组合是:rank=20,lambda=0.1,alpha=20,在MovieLens 100K数据集上RMSE大约在0.85左右。
4.3 就业岗位推荐系统:把“用户兴趣”换成“岗位匹配”
就业岗位推荐系统和电商、电影推荐有本质区别:它推荐错了的代价要大得多。用户看了一个不喜欢的电影,损失3分钟;接受了一个不合适的工作,损失可能是几年。所以这类推荐系统,一定要把“匹配度”放在“兴趣度”前面。
要点如下:
- 用户画像:学历、技能标签、工作年限、期望城市、期望薪资、历史求职行为。
- 岗位画像:岗位要求技能、薪资区间、行业、公司规模、地理位置。
- 匹配逻辑:优先做规则匹配(硬性条件过滤),比如学历门槛、工作年限要求,不满足的直接过滤掉;再用模型做软匹配,预测用户对岗位的申请概率。
- 反馈闭环:更要重视“拒绝反馈”。用户看了岗位详情但没有投递,这是一个强信号,说明某个维度不匹配。把这些负反馈样本喂给模型,能显著提升推荐的精准度。
我做这块项目时最深的体会是:就业推荐系统,本质上是一个辅助决策系统,不是娱乐系统。用户需要的是“为什么推荐这个岗位给我”的可解释性,所以在界面上展示匹配的维度(技能匹配、薪资匹配、距离匹配)比单纯列一堆岗位重要得多。
5. 推荐系统最常见的坑:我帮你把能踩的提前踩了
最后这部分是我多年的经验沉淀,每一条都是真金白银换来的教训。我直接做成表格,方便你对照检查自己的项目。
| 问题 | 现象 | 原因 | 解决方案 |
|---|---|---|---|
| 推荐结果全是热门爆款 | 覆盖率低,长尾内容推不出去 | 样本分布不均匀,模型被热门物品主导 | 对热门物品降权;在损失函数里加“多样性”正则项;为长尾物品单独开一个召回通道 |
| 新物品永远没有曝光 | 冷启动物品积累不到行为数据,陷入恶性循环 | 纯行为数据模型对新物品不友好 | 建立内容标签体系,用基于内容的方法弥补行为缺失;给新物品设置“探索流量”配额 |
| 点击率不错但转化率低 | 用户点击了但不下单 | 排序阶段只优化点击,没考虑转化 | 多目标优化,把点击和转化作为两个目标联合建模;引入价格、优惠券等情境特征 |
| 用户反馈“越推越窄” | 推荐结果太单一,用户逐渐厌倦 | 过度追求精度,忽略了多样性 | 在重排阶段加入品类、作者、内容类型的打散逻辑;定期插入探索性推荐 |
| 线上效果和离线评测不一致 | 离线指标好,上线就翻车 | 训练数据分布和线上实时分布有偏差 | 加特征监控和线上指标监控,及时发现分布漂移;用在线学习的框架持续更新模型 |
| 只用点击行为建模 | 推荐结果缺乏深度 | 点击只代表浅层兴趣,不代表真正喜欢 | 引入停留时长、完播率、收藏、分享等深度行为,并给不同行为赋权重 |
| 用户隐私规范问题 | 采集了不该采集的数据,引发合规风险 | 数据埋点没有做合规审查 | 数据采集前明确用途,做脱敏处理;涉及用户敏感信息的,必须按最小化原则处理 |
5.1 曝光偏差:推荐系统里最隐蔽的杀手
我要单独讲一个很多人忽略的问题:曝光偏差。推荐系统的训练数据来源于“曝光→点击”的过程,但用户只能点击他看到的内容,根本不会点击没曝光的内容。所以模型学到的是“在已曝光物品里用户更喜欢哪个”,而不是“所有物品里用户最喜欢哪个”。
这个偏差会导致一个严重后果:越是容易被曝光的物品,越是容易被模型继续推荐,热门的更热门,冷门的更冷门。
缓解这个问题的常用思路是:给训练样本加权重,让被曝光但未被点击的负样本对模型产生更大约束;或者在召回阶段增加随机性,给长尾商品足够的曝光机会,用“探索”换“利用率”。
5.2 推荐的“信息茧房”困境和多样性解法
“信息茧房”是算法推荐被批评最多的一点。从纯技术视角看,这就是“精度和多样性的矛盾”。
解决这个矛盾,工程上最有效的方法是重排阶段的MMR算法(最大边际相关性)。它的核心思想是:每选择一个推荐物品,既要考虑它和用户兴趣的相关性,又要考虑它和已选物品的差异性,两者加权求和:
MMR = argmax( λ * 相关性 - (1 - λ) * 和已选物品的最大相似度 )λ是平衡系数,λ趋近1时追求纯相关,λ趋近0时追求纯多样。我在短视频场景里试过,λ=0.7左右能在点击率基本不掉的情况下,把推荐列表的多样性指标提升40%以上,用户停留时长反而涨了。
写在最后:从“能跑通”到“有效果”,中间隔着大量脏活累活
回到项亮《推荐系统实践》这本书,我重读了三遍,每一遍都有新的体会。第一遍是在大学里,关注的是算法公式怎么实现;第二遍是在做第一个推荐项目时,关注的是数据清洗和特征工程有多重要;第三遍是在负责一个完整推荐系统架构时,关注的是反馈闭环、效果评估和系统稳定性。
如果你正在学推荐系统,我的建议是:不要停留在调包调参。找一份真实的用户行为数据(MovieLens、Amazon Reviews、或者自己埋点采集),把“数据清洗→特征工程→离线训练→在线服务→AB实验→模型迭代”这条完整的链路亲手走一遍。你会发现,真正让你成长的,不是某个模型的AUC提高了多少,而是你在“为什么线上和离线不一致”“为什么新用户推什么都不点”“为什么覆盖率和精度总是打架”这些问题上,做出的每一次权衡和取舍。
推荐系统没有银弹,每一个场景都需要针对性的调优。但底层的能力是通用的:懂数据、懂用户、懂工程、懂业务。把这些基本功打好,不管以后做电商、短视频还是招聘推荐,你都能快速上手。项亮这本书的价值,就是帮你把这套基本功的框架搭起来。剩下的路,需要在真实的项目和真实的数据里去走。