简介:面向机器学习与知识工程学习者,这是一份饮食推荐系统的完整项目资源包。项目基于用户饮食数据构建推荐模型,涵盖从数据清洗、特征分析到推荐算法实现的主要流程,适合作为课程设计、毕业设计或推荐系统入门开发的参考。资源共包含6个文件,类型覆盖2个CSV数据文件、1个Python推荐算法脚本、1个Jupyter Notebook交互式分析文档、1份PDF课程资料及1个说明文档;其中CSV文件提供食物成分与营养分布数据,Python脚本封装了推荐核心逻辑,Notebook则展示从数据探索到模型评估的完整过程,整体压缩包约429KB,结构紧凑、便于按需查阅。目前已有217人学习,说明其内容具备一定参考价值。通过该资源,读者可以获得完整的项目代码与数据,学习如何将机器学习方法应用于饮食推荐场景,理解知识库专家系统与推荐功能结合的实现思路,并可直接调整数据与算法以适配自己的实验需求。
1. 饮食推荐系统这个 zip,值不值得你花一晚上跑通
很多人以为推荐系统是电商和短视频平台的专利,其实「使用机器学习算法的饮食推荐系统」恰恰是机器学习最接地气的落地场景:录入你的身高体重和口味偏好,系统告诉你今天该吃什么、哪道菜的搭配更适合你。我见过不少同学从网上下载这类打包好的 Jupyter Notebook 项目,结果一晚上全耗在装环境和解压上,连数据长什么样都没看到,最后只能把代码截图交作业。这个方向本身不难——核心就是数据清洗、特征工程加相似度计算,跑通它相当于把推荐系统的完整套路走了一遍。这篇笔记适合两类人:要交算法汇报、课程设计的学生,和想判断饮食推荐能不能真正上线的工程师。全文从解压 zip 开始,一步步讲到你拿它做验收或二次开发。
2. 拿到 zip 第一步:解压、配环境、把第一个 notebook 跑起来
这类项目压缩包一般长一个样子:一个.ipynb文件、一份 CSV 或 Excel 格式的数据文件,再加一个requirements.txt或者README。先把这三样东西找齐,再动手配环境,能省掉后面一大半的排错时间。我习惯先把压缩包内容列出来看一遍,而不是直接双击解压——很多坑在解压前就能看出来。
2.1 解压 zip 前先做三件事:文件校验、目录预览、编码确认
在 Linux 服务器上,解压这类 zip 包最常用的是unzip,但我一般会先用unzip -l看一眼包结构。这一步能确认包里到底有没有数据文件、有没有 readme,以及目录是不是嵌套了一层才到 notebook 本体。
# 先看压缩包里有几个文件、路径是什么,不解压也能预览 unzip -l food_recommend_system.zip # 解压到单独目录,避免文件散落一地 mkdir -p ./food_reco && unzip food_recommend_system.zip -d ./food_reco-l是 list 模式,只列出压缩包内容不解压;-d指定解压目标目录。我坚持解压到独立目录的原因是:notebook 项目经常会用相对路径读取data/下的文件,直接右键解压到桌面再移动文件夹,路径一变,代码里pd.read_csv()立刻报错。这也是这类下载项目最常见的翻车点。
如果unzip报错提示文件损坏、或者解出来的文件不全,先别急着删除重下。常见情况是打包工具非标准导致的伪加密问题——文件头里加密标志位是 1,但内容根本没加密,7-Zip 能打开、普通 unzip 却要密码。这种情况优先换工具,Windows 上用 7-Zip 打开后全选拖出来即可,别去折腾什么密码破解。
2.2 Python 环境与 Jupyter Notebook 网页版:最小安装顺序
环境这块是新手最头疼的,我见过太多人把包往全局环境里装,装完 pip 列表乱成一锅粥。我的做法是每个项目开一个虚拟环境,这个习惯能救命。如果你机器上还没装 Python,建议直接装 Anaconda 或者 Miniconda,自带 conda 命令创建环境最省事;已经有 Python 3.8+ 的,用自带的 venv 就够了。
# 创建独立虚拟环境,避免污染全局 Python python3 -m venv .venv source .venv/bin/activate # Windows 下用 .venv\Scripts\activate # 安装 Jupyter Notebook 和常用数据科学依赖 python -m pip install --upgrade pip python -m pip install jupyter pandas numpy scikit-learn matplotlib seaborn这里要特别说明一下为什么用python -m pip而不是直接pip:很多机器上同时存在 Python 2 和 Python 3,或者有多个 Python 版本,直接敲pip装的包很可能进了一个你意想不到的解释器,最后import报错。用python -m pip能确保装到当前激活的虚拟环境里。
装完以后启动 Jupyter Notebook,默认会在浏览器打开一个网页版操作界面,点开.ipynb就能写代码跑实验。
jupyter notebook如果你的服务器是多用户环境,可以指定端口启动避免冲突,jupyter notebook --port=8889 --no-browser。初学者如果看到网页版一直在 “connecting” 状态,大概率是没装ipykernel,这个问题的解法放到第 5 章专门讲。
2.3 安装依赖:有 requirements.txt 和没有是两种玩法
打开解压后的目录,先看有没有requirements.txt。这个文件是项目的依赖清单,有它就按文件批量装;没有就只装核心库,别把网上教程里提到的包全部装一遍,版本冲突会让你怀疑人生。
# 有 requirements.txt 的直接用 -r 批量安装 python -m pip install -r requirements.txt # 没有的话,装这六个就够跑 80% 的饮食推荐 demo python -m pip install pandas numpy scikit-learn matplotlib seaborn jupyter注意一点:scikit-learn的安装包名和导入包名不一样,安装写pip install scikit-learn,代码里import sklearn,少一个连字符就装出问题来。如果 notebook 跑起来以后报ImportError: xxx has no attribute yyy,八成是包版本太老或太新,用pip list看一下版本号,再针对性升降级。这类小项目一般不用锁死版本,能用就行。
2.4 一键验证 notebook 能否完整跑通:nbconvert 的诡异用法
拿到手的 notebook 是在作者的环境里写的,他用的 pandas 版本、sklearn 版本跟你不一定一样,手动在网页版里一个个 cell 连着点太慢。我习惯先用命令行把整个 notebook 自动执行一遍,跑不通的报错会直接打在输出里,这一步能快速暴露环境问题。
# 复制一份再执行,避免原文件被改动后没法回头 cp food_recommend.ipynb food_recommend_backup.ipynb # nbconvert 会按顺序执行所有代码块,生成一个新的 .ipynb jupyter nbconvert --to notebook --execute food_recommend.ipynb --output executed_food.ipynb--to notebook表示输出格式还是 notebook 文件,--execute是让 jupyter 从头到尾执行,--output指定结果文件名。执行成功后,用jupyter notebook打开executed_food.ipynb,直接看每个 cell 下方的输出数据,不用自己重新跑一遍。如果中间某个 cell 报错,报错信息里会告诉你具体是哪一个 cell 挂了——这比在网页版里手动排查快得多,也是我每次拿到别人项目后的第一道检查工序。
3. 算法选型:饮食推荐里的三种机器学习落地方案
打开 notebook 你很快会发现,核心代码其实就是几套经典机器学习算法的组合。饮食推荐场景里最常见的是三条路线:基于内容的过滤、协同过滤、以及矩阵分解或分类模型兜底。新手最容易犯的错误是一上来就堆深度学习,在小数据集上效果未必比一个余弦相似度好,而且还没法跟人讲清楚为什么这样推荐。这一章把三种方案的原理、代码和选型理由拆开讲。
3.1 基于内容的过滤:把菜品特征向量化,用余弦相似度找“像”的菜
基于内容的思路最直观:给每道菜建一份“画像”,包含热量、蛋白质、脂肪、碳水、菜系等特征,然后算出菜与菜之间的相似度。用户如果吃过某几道菜,就把这些菜的画像合并成一份用户画像,再去匹配画像最接近的未推荐菜品。这种方案不依赖用户之间的行为,冷启动时也能用。
import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn.metrics.pairwise import cosine_similarity # 每行是一道菜,列是营养特征,索引是菜名 food = pd.read_csv("food_features.csv", index_col="food_name") X = food[["calories", "protein", "fat", "carbs"]].values # 标准化:热量数值大,蛋白质数值小,不处理相似度会被大数主导 scaler = StandardScaler() X_scaled = scaler.fit_transform(X) # 计算菜与菜之间的余弦相似度矩阵 sim = cosine_similarity(X_scaled) # 查某道菜最像的前 5 道 target = "鸡胸肉沙拉" idx = list(food.index).index(target) top = sim[idx].argsort()[-6:-1][::-1] print([food.index[i] for i in top])cosine_similarity按行计算两两之间的余弦值,结果范围在[-1, 1],越接近 1 表示两道菜在营养结构上越像。argsort()[-6:-1][::-1]是取相似度最高的前 5 个索引并倒序排列,这样打印出来就是从最相似往下排。要换推荐对象,把target变量改成任意菜名即可。
这里最容易忽略的是标准化这一步。热量动辄几百千卡,蛋白质只有几十克,量纲差太多,不标准化的话相似度基本被热量这一个特征控制,其他营养维度全废了。谁要是发现相似度结果看起来很怪,先检查是不是漏了StandardScaler,这种问题很像玄学,其实就是数据预处理没做。
3.2 协同过滤:从用户行为矩阵里找偏好相似的人
协同过滤的思路换了个方向:不分析菜本身长什么样,而是看用户行为。用户都对哪些菜打过高分,口味相似的人吃的菜,大概率你也喜欢。这种方案贯彻了“人以群分”的思想,是推荐系统里的常青树。
具体做法是把评分数据整理成一个用户 × 菜品的矩阵,行是用户、列是菜品、值是评分,然后算用户之间的相似度。最经典的相似度度量是皮尔逊相关系数,它有天然的去中心化效果,两个用户一个普遍打高分、一个普遍打低分,只要偏好顺序一致,相似度依然很高。
import pandas as pd import numpy as np # 三列:user_id / food_id / rating ratings = pd.read_csv("user_ratings.csv") # 透视成用户-菜品矩阵,缺失位置是 NaN matrix = ratings.pivot_table(index="user_id", columns="food_id", values="rating") # 转置后按列求皮尔逊相关系数,得到用户与用户的相似度矩阵 user_sim = matrix.T.corr() # 预测目标用户对某道菜的评分:取最相似 K 个用户的评分做加权平均 target_user = "u_001" food_id = "f_042" sim_users = user_sim[target_user].drop(labels=[target_user]).sort_values(ascending=False).head(10) # 过滤掉没吃过这道菜的用户,用相似度做权重 valid = matrix.loc[sim_users.index, food_id].dropna() pred = np.average(valid, weights=sim_users.loc[valid.index]) print(f"预测用户 {target_user} 对 {food_id} 的评分为 {pred:.2f}")pivot_table会把没评过分的格子填成 NaN;matrix.T.corr()这一步,转置后每列是一个用户的行向量,corr()自动按列计算皮尔逊相关系数。注意weights参数传的是相似度,相似的人说话分量更重。还要留一个心眼:dropna()以后如果剩下的有效用户太少,比如只剩一两个,这个预测值可信度很低,实际项目中至少要有 5 个相似用户都吃过这道菜才算数。
协同过滤最大的坑是数据稀疏。饮食类 app 里用户一天最多记录几顿饭,评分矩阵里 99% 的位置都是空,相似度算出来虚低。我一般会在算相似度之前先过滤掉评分少于 5 条的用户,剩下的人算出来的结果才有点参考价值。
3.3 矩阵分解与分类兜底:评分缺失和冷启动时的替代思路
协同过滤在矩阵稀疏时表现不佳,另一个思路是用矩阵分解把用户-菜品矩阵压缩成低维隐向量。每个用户和每道菜都用一个几十维的向量表示,向量的内积就是预测评分。这样做的好处是能挖掘出表面上没有交集的用户之间潜在的相似口味。
from sklearn.decomposition import TruncatedSVD from sklearn.impute import SimpleImputer from sklearn.metrics.pairwise import cosine_similarity # SVD 不接受 NaN,先用均值填充兜底 filled_matrix = SimpleImputer(strategy="mean").fit_transform(matrix.values) # 压缩成 8 维隐向量,这个维度一般取 5~20 svd = TruncatedSVD(n_components=8) item_latent = svd.fit_transform(filled_matrix.T) # 在隐向量空间里重新计算菜品相似度 item_sim_svd = cosine_similarity(item_latent) print("隐向量相似度矩阵维度:", item_sim_svd.shape)TruncatedSVD和 PCA 不同,它不先做中心化,适合直接处理稀疏矩阵。n_components是隐向量维度,取值太小丢信息、太大容易过拟合,小数据集上 8 左右是个不错的起点。均值填充只是临时方案,它会把稀疏矩阵变成稠密矩阵,内存开销变大,但胜在简单,demo 阶段够用。
除了矩阵分解,还有一类情况要用分类模型兜底:zip 里的数据根本没有任何用户评分,只有用户画像和菜品画像。这时候推荐问题变成了二分类——这道菜对这个用户推还是不推。逻辑回归是通用性最强的选择,特征解释性也好。
from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split X = features # 用户年龄、BMI、口味类别、菜品营养值 y = labels # 是否满意,0 或 1 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) clf = LogisticRegression(max_iter=500, C=1.0) clf.fit(X_train, y_train) print("测试集准确率:", clf.score(X_test, y_test))max_iter=500是给梯度下降足够的迭代次数,防止收敛警告;C是正则化强度的倒数,默认1.0一般不用动;stratify=y保证训练集和测试集里正负样本比例一致,避免测试集里全是“不推荐”导致准确率虚高。这类模型的缺点是只能输出 0 或 1,不能直接排序,但用来做冷启动兜底是合格的。
4. 数据与特征工程:饮食推荐系统里真正花时间的部分
初学者拿到 zip 往往直奔算法代码,但这类小项目真正调优的空间几乎全在数据质量和特征工程上。算法选得再花哨,数据是脏的,结果全是垃圾。我自己的经验是:项目时间分配上,数据处理和特征工程至少要占六成,模型训练反而是最省心的一步。
4.1 数据从哪来:常见 CSV 字段与数据质量检查
这类饮食推荐项目的数据通常有两张表:一张是菜品特征表,字段包括菜名、热量、蛋白质、脂肪、碳水、菜系;另一张是用户行为表,字段是用户 ID、菜品 ID、评分或是否食用。拿到数据第一件事不是写算法,而是把数据读进来做三个基础检查:看规模、看缺失、看分布。
import pandas as pd # 读取两份核心数据,Windows 环境编码不对就改 gbk food = pd.read_csv("food_features.csv", encoding="utf-8") ratings = pd.read_csv("user_ratings.csv", encoding="utf-8") print("菜品表规模:", food.shape) print("行为表规模:", ratings.shape) print(food.head()) # 缺失值检查,NaN 太多会影响相似度计算 print(food.isnull().sum()) # 评分分布是否均匀,集中在一个值说明数据没有区分度 print(ratings["rating"].value_counts())shape一眼看出数据量级,几百道菜的数据算是小样本,几万道菜就要考虑内存优化了。isnull().sum()检查缺失值,如果某些列缺失比例超过 30%,建议直接删列而不是填充,否则噪声比信号还大。value_counts()查评分分布,如果 90% 的评分都是同一个值,说明用户的反馈没有区分度,再强的模型也推不出差异化结果。
数据可视化这一步也很值得花十分钟,用 seaborn 画个评分分布图或者菜系分布图,比盯着 DataFrame 更直观地看出问题。比如评分数值集中在 3 分和 5 分、4 分几乎没有,那这个数据源本身就可能有问题,得回头重新审视采集逻辑。
4.2 特征工程:把口味、菜品类别和营养值变成机器能算的数
算法只能吃数字,所以文本型特征必须转换。这个环节的常见错误是:把所有文本特征都塞进LabelEncoder,完全不区分类别到底有没有顺序关系。菜系这种无序类别,编码成 0、1、2 会带来大小关系,模型会误以为“川菜”大于“粤菜”,这完全是误导,必须用 One-Hot。而“清淡、中等、重口”这种本身带有强弱顺序的,LabelEncoder反而合适。
from sklearn.preprocessing import LabelEncoder from sklearn.preprocessing import StandardScaler # 菜系是无序类别,one-hot 展开成多个 0/1 列 cuisine_oh = pd.get_dummies(food["cuisine"], prefix="cuisine") # 口味程度是有序类别,转成数字保留顺序关系 taste_le = LabelEncoder() food["taste_level"] = taste_le.fit_transform(food["taste"]) # 注意:转换后打印看一下对应关系,确认顺序符合直觉 print(dict(zip(taste_le.classes_, taste_le.transform(taste_le.classes_)))) # 营养列数值差异大,标准化统一量纲 scaler = StandardScaler() food[["calories", "protein", "fat", "carbs"]] = scaler.fit_transform( food[["calories", "protein", "fat", "carbs"]] )pd.get_dummies把菜系字段拆成若干 0/1 列,prefix参数给列名加前缀防混淆。LabelEncoder用之前,我习惯把映射关系打印出来看一眼,防止编码顺序跟直觉相反。标准化这一步做完,food这个 DataFrame 里所有列就都是数值了,可以直接喂给 3.1 的cosine_similarity。
还有一个地方要提醒:特征列拼接到一起时,务必确认索引对齐。从get_dummies得到的新 DataFrame 和原表索引顺序要一致,最稳妥的做法是pd.concat([food, cuisine_oh], axis=1)之前先reset_index(drop=True),否则行错位会带来极其隐蔽的错误。
4.3 训练测试划分与效果评估:别只用准确率
很多人在这个环节翻车翻得毫无自觉。做推荐系统时,如果用train_test_split随机切分数据,同一个用户的评分会同时出现在训练集和测试集里,模型等于见过答案再考试,分数虚高得吓人,一上真实场景立刻暴露。正确的做法是按用户划分,保证同一个用户的数据只落在某一侧。
from sklearn.model_selection import train_test_split # 按用户划分,而不是按评分记录划分 users = pd.unique(ratings["user_id"]) train_users, test_users = train_test_split( users, test_size=0.2, random_state=42 ) train = ratings[ratings["user_id"].isin(train_users)] test = ratings[ratings["user_id"].isin(test_users)] print(f"训练集出现用户数: {train['user_id'].nunique()}") print(f"测试集出现用户数: {test['user_id'].nunique()}")test_size=0.2表示拿 20% 的用户做验证,random_state固定随机种子,保证每次跑结果一致,方便对比调参效果。
评估指标的选择也要说清楚,推荐系统常用的三个指标针对不同目标:
| 指标 | 适用场景 | 说明 |
|---|---|---|
| RMSE | 评分预测类任务 | 对预测误差敏感,0.5 以下算不错 |
| Precision@K | 推荐列表排序 | 看前 10 条里用户真正吃/点赞的比例 |
| Coverage | 推荐多样性 | 推荐结果覆盖多少种菜品,覆盖低说明算法偏科 |
对饮食推荐来说,用户真正关心的是推荐列表里有没有他想吃的菜,而不是预测评分和真实评分差了多少,所以我个人更看重Precision@K。Feature 层面如果用了分类模型提升准确率,还得多看正样本召回率。
5. 避坑指南:跑饮食推荐系统最容易翻车的五个现场
这部分内容是我见过最多的血泪经验汇总。环境问题和数据问题占了这类项目排错时间的一大半,模型跑不出理想效果反而是少数。每一条我都按“现象 → 原因 → 解决”写清楚,直接对号入座就行。
5.1 Jupyter Notebook 内核连不上,网页版一直卡在 “connecting”
现象:点开 notebook 文件后,代码块右侧一直显示 “connecting”,点运行没反应。
原因:Jupyter Notebook 的网页版只是前端界面,实际执行代码的是一个叫 kernel 的后端进程。项目是用某个 Python 环境创建的,但启动 notebook 时没有为当前环境安装ipykernel,前后端对不上号。
解决:在激活的虚拟环境里安装并注册内核,然后重启 notebook。
python -m pip install ipykernel python -m ipykernel install --useripykernel install --user的作用是把当前环境注册成 Jupyter 可选的内核。注册完在 notebook 网页版里点击菜单栏的 “Kernel → Change Kernel”,选对刚才注册的 Python 环境,再重新运行就可以了。
5.2 zip 解压报错,提示伪加密或找不到 EOCD
现象:解压到一半报错 “unsupported” 或者 “could not find EOCD”,文件释放出来只有几个,其他找不着了。
原因:网上流传的 zip 包经常被二次打包,打包器不标准,或者文件头的加密标志位被改动过。伪加密的意思是标志位写的是加密,但内容实际上没有任何加密,zipfile 模块就被这个假标志卡住了。
解决:先用unzip -l看能否列出内容;列不出来就换 Python 的 zipfile 模块尝试,容错性比命令行 unzip 更好。
python -m zipfile -e food_recommend_system.zip ./food_reco-e参数把 zip 解压到指定目录。如果 Python 的 zipfile 能正常解压但命令行 unzip 不行,就是典型的非标准打包问题。Windows 上我更推荐用 7-Zip 打开,能看到内容就直接全选复制出来,不跟命令行斗气。
5.3 pandas 读中文 CSV 报 UnicodeDecodeError
现象:pd.read_csv("food_features.csv")直接红字UnicodeDecodeError,这一行代码都跑不过去。
原因:CSV 文件不是默认的 UTF-8 编码。Windows 上 Excel 另存的 CSV 大概率是 GBK 或 GB18030 编码,pandas 默认用 UTF-8 解码当然炸锅。
解决:读取时指定编码,gb18030是 GBK 的超集,容错率更高。
food = pd.read_csv("food_features.csv", encoding="gb18030")如果不想每次读都写编码参数,可以把文件统一转成 UTF-8 存一份副本,后续所有脚本都读这份新文件。写文件时也用encoding="utf-8",避免在别人电脑上二次报错。这个小习惯能省掉大量重复排错。
5.4 相似度矩阵全 NaN,或者控制台报 RuntimeWarning
现象:cosine_similarity算出来的矩阵全是 NaN,或者运行过程弹出RuntimeWarning: invalid value encountered。
原因:特征矩阵里包含 NaN,相似度计算时 NaN 传染;或者标准化后某些列标准差为 0——比如所有菜的卡路里数值都一模一样,除零产生了 NaN。
解决:先查缺失值,再做预处理。用中位数填充对异常值不敏感,比均值更稳。
from sklearn.impute import SimpleImputer imputer = SimpleImputer(strategy="median") X_clean = imputer.fit_transform(X) print("填充后剩余 NaN:", pd.DataFrame(X_clean).isnull().sum().sum())如果填充以后还是 NaN,就检查是不是某列是常数列。常数列的方差为 0,标准化时除以 0,这种情况直接删掉这一列。
5.5 notebook 跑一半内存炸了,内核直接重启
现象:代码跑到相似度计算时卡住不动,过一会儿 jupyter 页面提示 “Kernel Restarting”,前面算好的结果全没了。
原因:代码里用了双层 for 循环计算 N x N 相似度矩阵。N 是几千道菜的时候,循环次数是千万级,Python 的循环效率又低,内存直接被撑爆。
解决:能用向量化实现就不要手写循环。scipy.spatial.distance.pdist只存上三角矩阵,内存占用比完整矩阵少一半。
from scipy.spatial.distance import pdist, squareform # pdist 返回压缩后的距离向量,只存储上三角 dist_compact = pdist(X_scaled, metric="cosine") # squareform 还原成完整方阵,方便按索引取结果 dist_matrix = squareform(dist_compact)pdist的metric参数可以换成"euclidean"、"correlation"等,按场景调整。如果数据量再大一个量级,连pdist都撑不住,就用分批计算或者上稀疏矩阵方案。
6. 从能跑到能用:推荐效果的验证方法与两个进阶改进
把 notebook 跑通只是第一步,离“能用”还差一个系统评估和两个数据策略上的补救。这一章讲的不是我常用的炫技操作,而是所有推荐系统上线前都必须补的功课。
6.1 离线验证做扎实:RMSE、Precision@K 与表格对比
离线评估最关键的一点:不要用随机切分,按时间切。用户在第一周的行为是历史,第二周的行为是验证,这样才能模拟真实场景。如果手里的数据没有时间戳,才退而求其次按用户切分。
| 指标 | 计算方式 | 合格线参考 |
|---|---|---|
| RMSE | sqrt(平均评分误差平方) | 0.5~0.8 说明预测可用 |
| Precision@K | 推荐前 K 个中命中用户实际食用的比例 | 0.1 以上就算有信号 |
| Coverage | 有推荐记录的菜品数 / 总菜品数 | 低于 50% 需要扩充推荐池 |
这三种指标要在同一批测试用户上同时计算,只报准确率没有说服力。很多模型推荐来推荐去都是那几道高热量菜,覆盖率数字一拉出来就不及格。
6.2 冷启动补救:给新用户和新菜品各留一条退路
纯协同过滤最怕的新用户没有行为记录、新菜品没有评分,这时候模型一片空白。既然你拿到的是饮食推荐项目,就一定能用上这个策略:新用户直接走第 3 章讲的基于内容的推荐,让他先选几个喜欢的口味标签,把标签转成特征向量去匹配菜品;而新上架的菜品,先计算它和已上线菜品的相似度,挂在最相似已上线菜品的详情页里,等评分攒够了再进入协同过滤池。
这个策略不用改模型,只需要调整数据的接入顺序,落地成本最低,收益却几乎所有推荐系统都能复用。在真实项目里我也走过弯路:当时把协同过滤的矩阵分解维度调了一整天,测试集分数提升不到 1%,后来才反应过来模型根本不是瓶颈,新用户根本没有历史记录。最后做事的顺序变成:先接住冷启动,再优化模型。这条经验救了我很多次,希望你跑完这个饮食推荐项目以后也能记得先补这一层,再研究那些花哨参数。希望帮到你。
本文还有配套的精品资源,点击获取