Python实现图书推荐系统:协同过滤与文本相似度实战

发布时间:2026/10/12 14:57:15

Python实现图书推荐系统:协同过滤与文本相似度实战 简介这份资源是一套基于协同过滤与文本相似度算法的Python图书推荐系统完整源码面向计算机、软件工程等专业需要完成课程设计、毕业设计或推荐算法入门实践的学生与开发者帮助解决推荐系统从算法到前后端落地的完整实现问题。压缩包共1828个文件约218.71MB以1653张jpg与56张png图片、55个html页面、11个py脚本、18个csv数据集及css、js等前端资源为主涵盖界面素材、页面模板、算法代码与数据文件。系统功能覆盖用户注册登录、图书热度排行与分类展示、搜索与详情、评论点赞收藏、公告管理、个人中心等级积分与历史续读后台还提供图书批量导入审核、用户等级与作者认证管理、日志备份及数据统计等模块。目前已有1622人学习下载适合借此理解协同过滤与文本相似度融合的推荐思路并参考其模块划分与目录组织完成二次开发。1. 图书推荐系统为什么值得自己写一遍从「猜你喜欢」到可复现的协同过滤电商和图书馆的「猜你喜欢」背后最经典的一类实现就是协同过滤。它不依赖图书内容只看「谁借了什么、谁打了多少分」就能算出你可能喜欢的书。再叠加一层文本相似度把书名、简介、标签做向量化就能缓解新书没人借、老书被反复推的冷启动问题。这套组合拳用 Python 写出来并不复杂几十行核心逻辑就能跑通但真正落地时会遇到稀疏矩阵、相似度爆炸、推荐结果全是同一本书这些坑。这篇笔记就围绕「Python 实现图书推荐系统基于协同过滤 文本相似度」这个方向把数据准备、算法选型、参数设置、排错方法一步步拆开让新手能照着复现熟手能看到边界和调参空间。适合有 Python 基础、想做一个能写进简历或直接给小型图书馆用的推荐系统的开发者。2. 数据从哪来、怎么洗图书推荐系统的输入决定了上限2.1 三类数据缺一不可用户行为、图书元数据、用户画像协同过滤吃的是用户-物品交互矩阵文本相似度吃的是图书本身的文字信息。一个能跑的图书推荐系统至少要有下面三类数据数据类型典型字段作用缺失后果用户行为user_id, book_id, rating/borrow_count构建评分矩阵协同过滤无法启动图书元数据book_id, title, author, category, description文本向量化新书无法被推荐用户画像可选user_id, age, major, borrow_history冷启动兜底新用户只能推热门我一般会先用 pandas 把三张表读进来检查主键是否对齐。图书 ID 和用户 ID 必须全局唯一否则后面做矩阵映射时会串行。import pandas as pd # 读取三张基础表 ratings pd.read_csv(ratings.csv) # user_id, book_id, rating books pd.read_csv(books.csv) # book_id, title, author, category, description users pd.read_csv(users.csv) # user_id, age, major # 检查主键唯一性和空值 print(ratings.isnull().sum()) print(books[book_id].duplicated().sum()) print(ratings[user_id].nunique(), ratings[book_id].nunique()) # 过滤掉评分次数少于 3 次的用户和图书降低噪声 user_counts ratings[user_id].value_counts() book_counts ratings[book_id].value_counts() ratings ratings[ratings[user_id].isin(user_counts[user_counts 3].index)] ratings ratings[ratings[book_id].isin(book_counts[book_counts 3].index)]这段代码做了三件事读表、查空值和重复、按交互频次过滤。参数 3不是固定的数据量大可以提到 5 或 10数据量小就降到 2。过滤的目的是去掉那些只借过一次书的用户和只被借过一次的书它们对相似度计算的贡献几乎为零反而增加矩阵稀疏度。2.2 评分矩阵的稀疏度计算与处理策略图书推荐场景下用户-图书矩阵通常极度稀疏。一个高校图书馆有几十万册书、几万用户实际借阅记录可能只有几万条稀疏度轻松超过 99%。先算一下稀疏度心里有数再选算法。import numpy as np n_users ratings[user_id].nunique() n_books ratings[book_id].nunique() n_ratings len(ratings) sparsity 1 - n_ratings / (n_users * n_books) print(f稀疏度: {sparsity:.4f})如果稀疏度高于 0.995基于用户的协同过滤UserCF会非常吃力因为两个用户共同借过的书可能只有一两本相似度不可信。这时候优先考虑基于物品的协同过滤ItemCF因为物品之间的相似度可以通过多个用户的共同行为累积相对稳定。另一个办法是先做矩阵降维用 TruncatedSVD 把图书向量压到 50 到 100 维再算相似度。提示稀疏度超过 0.99 时不要直接用余弦相似度在全量矩阵上算内存和时间都会翻车。先做维度约简或只保留 Top-N 活跃用户。3. 协同过滤核心实现ItemCF 和 UserCF 怎么选、参数怎么调3.1 ItemCF 的完整代码实现与相似度矩阵构建ItemCF 的思路是你借了 A 书系统去找和 A 最像的 B 书推给你。相似度用余弦基于用户对两本书的共同评分向量。from sklearn.metrics.pairwise import cosine_similarity from scipy.sparse import csr_matrix # 构建用户-图书稀疏矩阵 user_ids ratings[user_id].astype(category) book_ids ratings[book_id].astype(category) rows user_ids.cat.codes cols book_ids.cat.codes values ratings[rating].values sparse_matrix csr_matrix((values, (rows, cols)), shape(n_users, n_books)) # 转置成图书-用户矩阵算图书间相似度 book_user_matrix sparse_matrix.T item_sim cosine_similarity(book_user_matrix, dense_outputFalse) # 只保留每本书最相似的 20 本其余置零降低噪声和存储 from sklearn.preprocessing import normalize item_sim normalize(item_sim, axis1, norml1)cosine_similarity返回的是稀疏矩阵dense_outputFalse避免内存爆炸。normalize做 L1 归一化让每本书的相似度权重和为 1后续加权评分时不会因为相似书数量不同导致分数尺度不一致。Top-N 的 N 一般取 10 到 30图书类目越细N 可以越小。3.2 评分预测与 Top-N 推荐生成有了相似度矩阵就可以给用户打分并生成推荐列表。def recommend_for_user(user_idx, sparse_matrix, item_sim, top_n10): # 取出该用户已评分的图书 user_ratings sparse_matrix[user_idx].toarray().flatten() rated_items np.where(user_ratings 0)[0] # 用相似度加权预测未评分图书的分数 scores item_sim[rated_items].T.dot(user_ratings[rated_items]) scores np.asarray(scores).flatten() # 屏蔽已评分的书 scores[rated_items] -np.inf top_items np.argsort(scores)[-top_n:][::-1] return top_items, scores[top_items]item_sim[rated_items]取出用户借过的书对应的相似度行转置后与评分向量做点积得到每本候选书的加权分数。scores[rated_items] -np.inf是必须的否则推荐结果里会出现用户已经借过的书体验很差。top_n一般设 10实际展示时再截取前 5。3.3 UserCF 的适用场景与相似用户计算UserCF 适合用户数量远小于图书数量的场景比如班级图书角、小型读书会。它的逻辑是找到和你借书口味最像的 K 个用户把他们喜欢但你没借过的书推给你。user_sim cosine_similarity(sparse_matrix, dense_outputFalse) def recommend_by_usercf(user_idx, sparse_matrix, user_sim, k20, top_n10): sim_scores np.asarray(user_sim[user_idx].todense()).flatten() similar_users np.argsort(sim_scores)[-k-1:-1] # 去掉自己 # 加权汇总相似用户的评分 weighted user_sim[similar_users].T.dot(sparse_matrix[similar_users]) scores np.asarray(weighted.todense()).flatten() rated np.where(sparse_matrix[user_idx].toarray().flatten() 0)[0] scores[rated] -np.inf return np.argsort(scores)[-top_n:][::-1]K 取 20 到 50 比较常见。K 太小相似用户不够推荐覆盖面窄K 太大会引入不相关用户精度下降。我一般会跑几组 K 值看推荐结果的品类多样性再定。4. 文本相似度补位让新书和冷门书也能被推出去4.1 图书文本向量化TF-IDF 与 Sentence-BERT 的取舍协同过滤有个硬伤新上架的书没有任何借阅记录相似度矩阵里整行是零永远推不出去。文本相似度就是来解决这个问题的。把书名、作者、分类、简介拼成一个文本字段做向量化再算书与书之间的文本相似度。from sklearn.feature_extraction.text import TfidfVectorizer books[text] (books[title].fillna() books[author].fillna() books[category].fillna() books[description].fillna()) tfidf TfidfVectorizer(max_features5000, stop_wordsenglish) tfidf_matrix tfidf.fit_transform(books[text]) text_sim cosine_similarity(tfidf_matrix, dense_outputFalse)max_features5000控制词表大小太大容易过拟合太小会丢信息。中文图书需要先分词用 jieba 把简介切开再喂给 TfidfVectorizer。如果数据量在几万本以内TF-IDF 足够用如果追求语义层面的相似比如「机器学习」和「深度学习」应该接近可以换 Sentence-BERT但推理成本会高一个量级。4.2 混合推荐协同过滤分数与文本相似度分数的融合最终推荐分数 α × 协同过滤分数 (1-α) × 文本相似度分数。α 是融合权重需要调。def hybrid_recommend(user_idx, sparse_matrix, item_sim, text_sim, alpha0.7, top_n10): user_ratings sparse_matrix[user_idx].toarray().flatten() rated_items np.where(user_ratings 0)[0] cf_scores np.asarray(item_sim[rated_items].T.dot(user_ratings[rated_items])).flatten() # 文本相似度分数用户借过的书的文本相似度均值 text_scores np.asarray(text_sim[rated_items].mean(axis0)).flatten() # 归一化到 [0,1] cf_scores (cf_scores - cf_scores.min()) / (cf_scores.max() - cf_scores.min() 1e-8) text_scores (text_scores - text_scores.min()) / (text_scores.max() - text_scores.min() 1e-8) final_scores alpha * cf_scores (1 - alpha) * text_scores final_scores[rated_items] -np.inf return np.argsort(final_scores)[-top_n:][::-1]α 取 0.7 表示协同过滤占主导文本相似度做补充。如果系统里新书比例高α 可以降到 0.5 甚至 0.4。归一化那一步不能省否则两个分数尺度不同融合结果会被大尺度的那一方带偏。5. 避坑与排查图书推荐系统上线前必须过的五道坎5.1 推荐结果全是同一类书现象不管用户借过什么推荐列表里永远是计算机类或文学类。原因ItemCF 的相似度矩阵被热门类目主导少数类目的书之间相似度普遍偏高。解决在相似度计算前对热门类目做降权或者对推荐结果做类目打散每类最多出 2 本。5.2 内存溢出cosine_similarity 直接吃满 16G现象跑相似度计算时进程被系统杀掉。原因图书数量上万时稠密相似度矩阵是 N×N 的 float64内存占用是 N²×8 字节。解决用dense_outputFalse保持稀疏或者分块计算每次只算 1000 本之间的相似度。5.3 冷启动用户推荐结果为空现象新用户没有任何借阅记录推荐函数返回空列表。原因协同过滤和文本相似度都依赖用户历史。解决给新用户走热门推荐兜底按借阅次数排序取 Top-N同时结合用户注册时填的专业或兴趣做规则过滤。5.4 评分矩阵归一化方式选错导致推荐偏差现象推荐结果偏向评分高的书而不是用户可能喜欢的书。原因不同用户的评分尺度不同有人习惯打 5 分有人最高只打 3 分。解决对每个用户的评分做均值中心化减去该用户评分的平均值再算相似度。5.5 文本向量化时中文未分词现象TF-IDF 矩阵里出现大量单字相似度计算结果无意义。原因TfidfVectorizer 默认按空格分词中文句子没有空格。解决先用 jieba 分词把结果用空格拼接后再传入或者自定义 tokenizer 参数。6. 把推荐系统跑成服务离线评估与在线接口的衔接技巧离线评估用 PrecisionK 和 RecallK 就够了。把每个用户最后借的 5 本书藏起来做测试集用剩下的数据训练看推荐列表里命中了几本。def precision_recall_at_k(recommend_fn, test_dict, k10): precisions, recalls [], [] for user_idx, true_items in test_dict.items(): pred_items recommend_fn(user_idx)[:k] hits len(set(pred_items) set(true_items)) precisions.append(hits / k) recalls.append(hits / len(true_items)) return np.mean(precisions), np.mean(recalls)Precision10 能到 0.15 以上、Recall10 到 0.1 以上在图书场景就算可用。如果指标太低先检查测试集划分是否随机再调相似度 Top-N 和融合权重 α。在线接口用 Flask 或 FastAPI 包一层把相似度矩阵和稀疏矩阵在启动时加载到内存请求进来只做查表和排序单次推荐耗时能压到 10 毫秒以内。注意矩阵文件用 pickle 或 npz 存别每次请求都重算。我自己的习惯是每次调完参数先把 PrecisionK 和推荐结果的类目分布打出来看一眼确认没有退化成热门榜单再上线。这套东西不复杂但细节决定它能不能真的被人用起来。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/12 14:57:15

wx-ipad协议8049双端适配:Linux与Windows跨平台构建实战

简介:该资源为微信iPad协议8049的Linux与Windows双版本实现,面向需要对接微信通信协议、搭建服务端接口的开发者与运维人员。包内共27个文件,以js脚本、map映射、conf配置、html页面及png图标为主,另含exe可执行程序、yml接口描述…

2026/10/12 14:57:15

2026年安全运维拐点:AI原生安全与行为基线分析实战

2025年做安全运维的时候,我最大的感受是:告警越来越不像人制造的,更像是一台不知疲倦的机器在试探我的边界。几个大项目一跑,很多团队的负责人都在问同一个问题——2026年我们到底该往哪儿投钱、往哪儿堆人?这篇东西不…

2026/10/12 14:52:12

Java开发者必看:LangChain4j大模型应用开发入门实战

1. 为什么Java开发者需要一个专属的LLM框架先说个现象:这半年陆续有几个做Java后端的朋友问我,说他们想在自己的项目里接入大模型能力,结果一搜教程全是Python。什么LangChain、LlamaIndex,Demo跑起来倒是热闹,可真要落…

2026/10/12 15:37:31

sqlmap图形化界面工具:从命令行到GUI的封装与实战

简介:这是一份面向网络安全初学者与渗透测试人员的 sqlmap 图形化界面工具,旨在降低 SQL 注入检测与利用的操作门槛,让不熟悉命令行的用户也能通过可视化界面完成注入点探测、数据库枚举等常见任务。资源包共收录 1456 个文件,以 …

2026/10/12 15:37:31

Spring Boot DevTools + IDEA + LiveReload 热部署配置与踩坑实录

直接进入正题。Spring Boot IntelliJ IDEA这套组合,我做Java后端开发这几年一直在用。起初也跟大多数人一样,改个代码就要手动重启,前端调样式更是折磨,改完CSS切到浏览器按F5,来回折腾一天下来,真正写业务…

2026/10/12 15:37:31

道路结冰数据集:1527张图搞定YOLO目标检测训练与避坑指南

简介:道路结冰数据集包含1527张道路状态样本,覆盖高速公路、城市道路、桥梁隧道出入口等典型场景,提供YOLO与VOC两种标注格式,可直接用于训练结冰检测模型,面向计算机视觉与智能交通方向的深度学习开发者。数据集中区分…

2026/10/12 15:32:30

浏览器播放RTSP监控流:html5_rtsp_player部署与避坑指南

简介:这是一套面向 Web 前端的 HTML5 RTSP 播放器完整源码包,目标是在浏览器中不依赖插件直接播放 RTSP 视频流,适用于网络监控、视频会议等实时流媒体场景。压缩包共 69 个文件,以 57 个 JavaScript 文件为主,覆盖播放…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/12 12:01:26

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/12 12:54:07

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/12 0:04:22

绝缘子缺陷检测数据集清洗与工业级训练实战指南

简介:本资源是面向电力AI研发人员、工业视觉工程师及智能巡检系统开发者的绝缘子缺陷检测专用YOLO格式数据集,解决无人机航拍场景下绝缘子破损、污闪、积雪等9类典型缺陷的精准识别与定位难题。数据集共2139张真实巡检图像(含训练/验证/测试集…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑