ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

新闻推荐系统毕业设计:UserCF协同过滤从原理到落地实现

新闻推荐系统毕业设计:UserCF协同过滤从原理到落地实现 简介这是一套基于协同过滤算法的新闻推荐系统毕业设计项目面向计算机类专业学生及推荐系统入门开发者旨在解决从新闻数据采集与清洗、用户行为矩阵构建到个性化推荐算法落地的全流程问题。压缩包共146个文件整体大小仅577KB包含Vue前端页面、Java/Scala/Python后端代码、XML与Properties配置、SQL初始化脚本、Dockerfile等覆盖了前后端交互、推荐服务、数据库存储和容器化部署等模块。目前已有353人学习下载资源内还提供说明文档、parquet示例数据与辅助脚本便于对照源码复现实验、撰写毕业设计文档或进行二次扩展。算法上实现了基于用户与基于物品两种协同过滤方式涉及相似度计算、Top-N推荐、评分矩阵处理等关键步骤整体代码结构清晰目录按功能模块拆分可快速定位训练脚本、数据处理逻辑与前端界面。此外包内保留数据样例与说明方便替换数据或调整参数做效果对比适合作为课程设计或毕设项目参考。1. 这个毕设题目的真实分量算法是骨架数据流才是血肉新闻推荐系统是推荐算法里最“挑数据”的场景之一。协同过滤本身不复杂核心就是“物以类聚、人以群分”但放到新闻场景里会发现用户兴趣变化快、物品新闻生命周期短、冷启动问题比电商严重得多。很多同学把精力全花在调相似度公式上结果项目一跑起来死在全是在处理空列表、脏数据和内存溢出——这恰恰是这类毕业设计的真实面貌。这个题目适合两类人一类是推荐系统方向、想用Python把协同过滤从原理到落地走通一遍的学生另一类是已经在做Web开发、想往算法工程方向靠的从业者。它的价值不在“协同过滤”这个词本身而在于你被迫处理真实数据流爬新闻、清洗正文、建用户行为表、算相似度矩阵、生成推荐列表、再想办法验证效果。这套链路才是答辩时最值钱的东西也是面试官真正会问的细节。本文按照“选型 → 数据准备 → 核心实现 → 排查优化”的顺序展开最后会给出几个让系统更像“产品”而非“课程作业”的进阶做法。如果你准备动手做这个题目这篇文章能帮你少走至少两周弯路。2. 协同过滤在新闻场景下的选型为什么新闻推荐首选UserCF而不是ItemCF2.1 UserCF和ItemCF的原理差异与选型依据协同过滤分两类基于用户的协同过滤UserCF和基于物品的协同过滤ItemCF。两者的出发点完全不同。UserCF的核心逻辑是找到与当前用户兴趣最相似的一群“邻居”把这群邻居喜欢的、而当前用户没看过的物品推荐给他。逻辑顺序是“先找相似的人再找这些人爱看的东西”。ItemCF则反过来先建立物品之间的相似度关系然后推荐“和你之前看过的新闻相似的新闻”。在电商场景里ItemCF是绝对主流因为商品是稳定存在的今天卖T恤明天还在卖物品相似度矩阵可以离线算好、定期更新。但新闻不一样——一条新闻的生命周期往往只有几小时到三天早上发生的热点事件晚上就过时了。在物品快速更替的场景下ItemCF的问题会集中暴露新新闻没有历史交互数据无法计算它与任何旧新闻的相似度旧新闻即使算好了相似度等用户来的时候可能已经被下架。更麻烦的是新闻内容的相似度计算需要高质量分词和语义理解如果只按标题字面算相似完全无法识别“国足”和“中国男足”这种同义表达。所以新闻推荐系统行业里的常见做法是优先UserCF。用户的兴趣在短期内是相对稳定的一个用户过去三天看了什么类型的新闻基本决定了他今天想看什么。即使一条新闻刚发布还没有任何点击只要它被某个相似用户看了就可以被推荐出去天然规避了物品冷启动问题。2.2 相似度计算的选择为什么这里推荐余弦相似度确定用UserCF后下一步是定义“用户相似度”。常见选择有三种余弦相似度、皮尔逊相关系数、杰卡德相似系数。要理解它们各自的适用场景先把用户-新闻交互数据想象成一个矩阵行为用户、列为新闻。余弦相似度计算的是两个用户向量的夹角只关注方向、不关注数值大小。皮尔逊相关系数是余弦相似度的改进版——先对每个用户做中心化处理也就是减去该用户所有评分的均值再去算余弦。杰卡德相似系数则只看两个集合的交并比完全没有数值概念。对于新闻场景默认用皮尔逊相关系数其实不太合适。原因在于新闻阅读场景下的“评分”跟电影评分完全不同——电影用户会打分1到5分分布均匀中心化后再算相似度是有意义的。但新闻场景的交互通常只是“点没点”“看完没看完”即使引入阅读时长作为隐式反馈后面章节会细说数值分布也极其不均中心化操作的意义不大。杰卡德相似系数适合处理“收藏/未收藏”这类纯粹的布尔行为信息量太薄新闻推荐中很少单独使用。因此代码层面的默认选择是余弦相似度。它的计算形式简洁对稀疏向量支持好配合后续要讲的数据清洗结果已有足够的区分度。还有一个实际考量余弦相似度可以不依赖Scikit-learn只用numpy就能快速实现这对毕设中的自主性展示是有利的。3. 构建完整的推荐数据链路从爬虫到用户行为表的落地做法3.1 数据来源与基础环境准备做协同过滤第一步不是写算法而是确认“手里有什么数据”。新闻推荐系统毕业设计的数据来源常见做法有三种第一种是直接使用公开数据集比如学术界的新闻推荐数据集如微软的MIND数据集数据规范、省时间但格式相对固定答辩时容易被问到“这个数据集是你从零处理的吗”第二种是用爬虫抓取新闻网站的内容字段灵活、本地化好但要注意robots协议和抓取频率第三种是两者结合先用爬虫跑一周攒真实数据不够的部分用公开数据集补。不管选哪种环境准备是共同的起点。以下命令在Windows和Linux下通用Windows如果提示python不是内部命令说明没有勾选“Add Python to PATH”选项需要卸载重装时勾上。# 创建独立的虚拟环境避免依赖冲突 python -m venv news_rec_env # 进入虚拟环境Windows news_rec_env\Scripts\activate # 进入虚拟环境Linux/Mac source news_rec_env/bin/activate # 升级pip并安装核心依赖 pip install --upgrade pip pip install numpy pandas scikit-learn pip install Flask flask-cors pip install pymysql SQLAlchemy在安装依赖时有个注意点最好不要运行“pip install requests beautifulsoup4 scrapy”等和爬虫相关的安装命令新闻正文抓取的质量会直接决定后续推荐效果这里只先装推荐链路本身需要的库。3.2 用户行为表设计记录什么字段决定推荐效果的上限协同过滤算法的输入是“用户—物品”的交互记录但新闻场景的交互天然有强时效性不能只存“用户id、新闻id、时间”三个字段。如果只存最原始的点击记录后面做特征分析时会发现数据不够用无法区分用户是认真看完了还是误点无法定位用户关注的话题领域连“最近一周活跃用户”这种最基础的统计都要反复join。按照行业里做推荐的通用做法我建议至少在MySQL中设计三张表新闻表news、用户表user、用户行为表behavior。其中用户行为表是整个项目的心脏字段设计如下。字段名类型说明idint自增主键user_idint用户ID对应user表news_idint新闻ID对应news表behavior_typevarchar(20)行为类型:click/view/favoriteduration_secondsint阅读时长秒0表示未记录is_readtinyint是否读完1表示读完create_timedatetime行为发生时间这里的几个关键细节behavior_type不要用数字编码而用字符串便于后续做规则筛选duration_seconds和is_read是隐式反馈特征虽然它们不在协同过滤算法主流程中但是在数据预处理过滤噪音时会用到create_time一定不能省因为新闻推荐必须考虑时间衰减。3.3 用Python实现从数据库读取到行为矩阵构建有了行为表后协同过滤的第一步是从数据库读出原始行为数据加工成UserCF的标准输入——一个用户-物品评分矩阵。具体做法如下这里用一个简单的示例数据表结构来说明核心写法实际表名和字段请按自己的表调整。import pandas as pd import numpy as np from sqlalchemy import create_engine # 数据库连接配置 DB_CONFIG { host: localhost, port: 3306, user: root, password: your_password, database: news_recommend } # 创建数据库引擎 engine create_engine( fmysqlpymysql://{DB_CONFIG[user]}:{DB_CONFIG[password]} f{DB_CONFIG[host]}:{DB_CONFIG[port]}/{DB_CONFIG[database]}?charsetutf8mb4 ) # 提取行为数据 def load_behavior_data(): query SELECT user_id, news_id, behavior_type, duration_seconds, is_read, create_time FROM behavior WHERE create_time DATE_SUB(NOW(), INTERVAL 7 DAY) df pd.read_sql(query, engine) print(f已加载行为记录: {len(df)} 条) return df # 行为转评分:不同类型赋予不同权重 def behavior_to_rating(df): # 先复制一份避免修改原始数据 df df.copy() # 评分规则完整阅读给1.0仅点击给0.6收藏给1.2 rating_map { read: 1.0, click: 0.6, favorite: 1.2 } # 应用评分映射 df[rating] df.apply( lambda row: rating_map.get(row[behavior_type], 0.5), axis1 ) # 阅读时长大于30秒的加权 df.loc[(df[duration_seconds] 30) (df[is_read] 1), rating] * 1.1 # 只保留评分大于0的记录 df df[df[rating] 0] # 聚合同一个用户对同一篇新闻的多次行为取最大值 rating_matrix df.groupby([user_id, news_id])[rating].max().reset_index() return rating_matrix逻辑说明behavior_to_rating函数的核心思路是把用户的不同行为映射成分值然后聚合生成标准的行为矩阵。评分映射表是项目里重点调优的参数——0.6/1.0/1.2这组值表示点击权重最低、收藏最高。这个设计遵循一个直观业务逻辑收藏代表用户主动表达兴趣比被动点击意图强得多。参数说明drop_duplicates和groupby操作在这里起着行为去重的作用防止同一用户反复刷新同一页面导致权重累积。SQL查询里的INTERVAL 7 DAY是时间窗口参数新闻推荐一般只取近7天行为。新闻生命周期短超过7天的行为对当前推荐基本没有参考价值如果不做时间过滤算法会把上周的热点当成现在的兴趣严重拉低推荐效果。4. 核心实现手写UserCF推荐引擎的完整步骤4.1 构建用户相似度矩阵从行为矩阵计算用户相似度矩阵是UserCF的核心步骤。有了上一步构建的评分矩阵这里可以用Python独立实现整个流程不依赖现成的推荐库。这样做事后的可解释性好答辩时能被问到细节都能答上来。import numpy as np import pandas as pd from collections import defaultdict class UserCF: def __init__(self): self.user_sim_matrix {} self.user_items {} self.item_users {} self.user_ratings {} def fit(self, rating_df): rating_df: DataFrame with columns [user_id, news_id, rating] # 按用户分组构建用户-物品字典 self.user_items defaultdict(dict) self.item_users defaultdict(dict) for _, row in rating_df.iterrows(): user_id row[user_id] item_id row[news_id] rating row[rating] self.user_items[user_id][item_id] rating self.item_users[item_id][user_id] rating self.user_ratings dict(self.user_items) print(f用户数量: {len(self.user_items)}, 新闻数量: {len(self.item_users)}) # 计算用户间相似度 self._calc_user_similarity() def _calc_user_similarity(self): 计算用户相似度矩阵采用余弦相似度 # 构建用户-物品评分矩阵稀疏表示 user_ids list(self.user_items.keys()) n_users len(user_ids) user_index {uid: idx for idx, uid in enumerate(user_ids)} # 构建用户向量矩阵 user_vector {} for uid, items in self.user_items.items(): vector np.zeros(len(self.item_users)) for item_id, rating in items.items(): item_idx list(self.item_users.keys()).index(item_id) vector[item_idx] rating user_vector[uid] vector # 计算两两相似度 self.user_sim_matrix {} for i in range(n_users): uid user_ids[i] self.user_sim_matrix[uid] {} vi user_vector[uid] for j in range(i1, n_users): vj user_vector[user_ids[j]] # 余弦相似度公式 dot_product np.dot(vi, vj) norm_i np.linalg.norm(vi) norm_j np.linalg.norm(vj) if norm_i 0 or norm_j 0: sim 0.0 else: sim dot_product / (norm_i * norm_j) self.user_sim_matrix[uid][user_ids[j]] sim self.user_sim_matrix[user_ids[j]][uid] sim逻辑说明fit方法是训练入口输入上一步生成的rating_df完成user_items和item_users两个字典的构建然后计算相似度。这里的核心是_calc_user_similarity方法它把每个用户的评分行为变成一个稀疏向量向量的每个维度对应一篇新闻。这样两个用户的相似度就是两个向量的余弦值数值越接近1表示兴趣越一致越接近0表示几乎无交集。参数说明这段代码里最容易出性能问题的是“将物品转成向量”的过程——每构建一个用户向量都要遍历item_users.keys()这样的低效做法在小数据集上没问题数据量到十万级之后跑一次要几分钟。如果你打算用真实爬取数据建议用scipy.sparse中的csr_matrix构建矩阵代码更简洁运行效率也更高。这里保留遍历写法是为了让初学者能看清每一步在做什么。4.2 生成TopN推荐列表相似度矩阵只是“中间结果”最终要给用户输出的是一个按分数排序的新闻id列表。这一步的逻辑是找到和目标用户最相似的K个用户把这K个用户的行为汇总排除目标用户已读内容按推荐分值排序取前N条。def recommend(self, user_id, top_k10, top_n10): 为目标用户生成推荐列表 user_id: 目标用户ID top_k: 取前K个相似用户 top_n: 最终推荐的新闻数量 # 如果该用户不在训练集中返回空列表 if user_id not in self.user_items: return [] # 获取当前用户的已读新闻用于过滤 read_items set(self.user_items[user_id].keys()) # 获取相似度最高的K个用户 if user_id not in self.user_sim_matrix: return [] sim_users sorted( self.user_sim_matrix[user_id].items(), keylambda x: x[1], reverseTrue )[:top_k] # 候选物品得分表 item_scores defaultdict(float) item_sim_sum defaultdict(float) # 遍历相似用户累加推荐分值 for sim_user, sim_score in sim_users: for item_id, rating in self.user_items[sim_user].items(): # 跳过当前用户已读过的新闻 if item_id in read_items: continue # 加权打分相似度 * 该用户对新闻的评分 item_scores[item_id] sim_score * rating item_sim_sum[item_id] sim_score # 归一化除以相似度之和消除K值影响 for item_id in item_scores: item_scores[item_id] / item_sim_sum[item_id] # 按分数排序取TopN ranked_items sorted(item_scores.items(), keylambda x: x[1], reverseTrue) return [(item_id, score) for item_id, score in ranked_items[:top_n]]逻辑说明推荐部分的三个关键操作是——过滤、加权、归一化。过滤是为了避免“推荐用户已经看过的新闻”这是协同过滤最容易犯的低级错误加权是相似度与评分的乘积相似用户贡献越高他对新闻的评分在最终排序中占的比重就越大归一化除以item_sim_sum解决的是“某些用户行为特别多、导致他推荐的新闻总是排最前”的问题。参数说明top_k10表示参与推荐的邻居数量这个值建议从5到30做网格搜索。top_k太小推荐的视野太窄系统只围绕几个最相似的用户转top_k太大把相似度很低的人都拉进来推荐结果会被“大众口味”淹没。top_n一般取10或20新闻客户端首屏放10条下拉刷新再放10条符合用户浏览习惯。4.3 用Flask快速暴露成推荐接口毕业设计要演示效果最直观的落地方式是写一个轻量Web接口。用Flask写一个简单的服务把上面的UserCF类封装成HTTP接口前端页面直接调用。这是新闻推荐系统的标准集成方式核心代码如下。from flask import Flask, jsonify, request from user_cf import UserCF # 假设上面代码保存为user_cf.py app Flask(__name__) # 全局模型实例 model None app.route(/api/recommend, methods[GET]) def recommend_news(): 推荐接口接收user_id参数 user_id request.args.get(user_id, typeint) if not user_id: return jsonify({code: 400, msg: 缺少user_id参数, data: []}), 400 try: # 调用模型生成推荐 rec_list model.recommend(user_id, top_k10, top_n10) # 拼接推荐结果 recommendations [] for news_id, score in rec_list: # 这里需要查news表获取标题和url recommendations.append({ news_id: news_id, score: round(score, 4) # 实际项目中在这里联查news表返回title和url }) return jsonify({code: 200, msg: success, data: recommendations}) except Exception as e: return jsonify({code: 500, msg: str(e), data: []}), 500 app.route(/health) def health_check(): return jsonify({status: ok}) if __name__ __main__: # 实际运行时先加载数据、训练模型 # from data_prepare import load_behavior_data, behavior_to_rating # df load_behavior_data() # rating_df behavior_to_rating(df) # model UserCF() # model.fit(rating_df) app.run(host0.0.0.0, port5000, debugTrue)逻辑说明这里的weight参数传递方式我在接口层做了简化处理实际中建议每个接口可接收用户id、返回条数两个参数。需要特别注意的是Flask里debugTrue会让每次请求时如果修改了Python代码自动重启服务带来了方便但在生产环境必须置为False。参数说明port5000是Flask默认端口如果被占用可以换成5001或8080。在最终演示时前端页面通过GET请求访问http://localhost:5000/api/recommend?user_id1直接拿到JSON格式的推荐结果不管是写个简单的HTML页面展示还是用Vue前后端分离接口都是通用的。5. 避坑指南新闻推荐系统最常见的5个坑和对应解法5.1 数据稀疏导致相似度全为0现象训练完成之后发现大部分用户之间相似度为0推荐结果为空或者只有点击率最高的那几篇新闻。原因新闻场景交互天然稀疏。假设系统里有1000个用户、5000篇新闻每个用户平均只看过20篇那么任意两个用户的共同阅读量可能只有0到2篇侥幸非零余弦相似度数值也极低推荐效果趋近于随机。解决把人作为中心把数据变稠密——把新闻按频道或类别聚合从“用户-新闻”矩阵变成“用户-频道”矩阵频道数量可能只有十几个稀疏度立刻降下来。具体做法是在news表加category字段聚合成类别评分后再跑UserCF但这会牺牲推荐新颖性只能推频道内的热门新闻无法跨频道发现。更稳妥的做法是保留两层推荐UserCF在“用户-类别”层面计算相似用户然后在相似用户的行为里挑选新闻候选。这样大类相似度计算稳定小类新闻推荐有惊喜。5.2 相似度矩阵占用内存过大现象代码跑起来没有任何报错但内存占用迅速飙升训练到一半直接被系统杀掉Process finished with exit code 137Killed。原因UserCF需要存储用户间两两相似度。当用户数达到10000时相似度矩阵理论上存储约5000万个浮点数每个float64占8字节相当于400MB内存用户数到50000时直接超过8GB笔记本扛不住。解决三层处理方案。第一只保留TopN最相似的邻居每个用户只存相似度最高的30个用户信息矩阵从O(N)降到O(N×K)同时还能防止冷门用户拖慢计算速度第二用scipy.sparse.csr_matrix稀疏存储矩阵只记录非零元素因为新闻场景里大多数用户对之间根本没有共同阅读记录稀疏度轻松超过99%第三数据库侧先做时间过滤只保留近7天行为数据参与计算而不是把所有历史数据都加载进内存。实际项目中我一般三层同时做。5.3 新用户和新新闻的冷启动问题现象新注册用户没有任何行为记录模型对其输出空列表当天新发布的新闻没有曝光机会永远进不了行为表。原因协同过滤的本质是从历史行为中总结模式没有历史就无模式可言。这是算法的先天缺陷不是代码bug。解决新用户冷启动常用“热门兜底”策略——当UserCF返回结果为空或相似用户少于3个时直接推荐当前时间窗口内点击量最高的新闻列表保证接口永远有返回。避免了前端页面“推荐区空白”的尴尬。新新闻那边问题更棘手新闻生命周期太短等它积累了足够点击量就已经过时了。所以行业里对新闻推荐有专门的做法对当天发布不足24小时的新新闻用基于内容的推荐作为补充——提取新闻关键词与用户历史阅读的关键词做匹配冷启动问题就从协同过滤层面转移到了内容层面需要引入分词jieba和关键词抽取。这一段逻辑要写进毕设论文的创新点里很加分。5.4 中文分词不规范导致相似度计算失真现象新闻标题中包含英文单词或中文分词结果混乱算出来的新闻相似度不可用甚至把完全无关的新闻判定为高度相似。原因新闻文本往往是中英混合比如“iPhone 17发布”“AI大模型”),直接按空格切分根本得不到有效token必须做中文分词。毕设常用的是jieba库但默认词典不够全人名、领域词经常被切碎。解决在数据预处理阶段增加分词步骤。先加载自定义词典领域特有词汇大模型、新能源、算力租赁等再对标题和摘要做分词去停用词处理。分词结果不要直接用于协同过滤而是存到news表的keyword_field字段作为冷启动和后续内容推荐的特征来源。这里必须去掉“根据标题正文泛泛而谈”的水分落成一个具体的函数我在项目里一般写一个news_preprocess.py脚本内容就是加载词典、分词、存库约30行代码。5.5 评分权重设置不合理导致推荐结果单一现象推荐列表里全是收藏或看完的长新闻把短视频类和快讯类内容全都挤掉了。用户明明经常看短平快的快讯但推荐结果里一篇都看不到。原因如果收藏的权重设得太高比如1.5甚至2.0算法会严重偏向那些容易引发收藏的深度长文而新闻阅读场景的大量真实行为是快速浏览这类行为权重被压低之后用户的真实兴趣特征被扭曲了。解决切忌“拍脑袋定权重”不要只拿一组值跑到底。正确做法是调参后看行为分布跑完一批验证集数据后统计推荐列表里behavior_type的分布比例和训练集里的比例对比。当推荐列表里“收藏行为”的占比被放大到训练集的2倍以上说明权重设定偏了这时把收藏权重从1.2下调到1.0把点击权重从0.6上调到0.8直到比例重新回到合理区间。这组参数是典型的“看似不起眼、实际决定成败”的细节。不同的新闻客户端、不同的用户群体这组权重值差异很大没有万能参数。6. 让系统更像产品引入时间衰减与评估指标的两个进阶做法6.1 时间衰减让算法理解“新闻是易腐品”入门版的UserCF把近7天行为全部视为同等重要但现实是用户昨天看了科技新闻、今天刷了一下午体育赛事那么明天推荐应该更偏向体育还是科技答案是体育。用户兴趣在短时间内有强连续性但也存在快速偏移。时间衰减的常见做法是在推荐打分时加上一个随“行为发生时间和当前时间间隔”衰减的系数。代码层面在之前fit方法处理rating_df的时候增加一个时间衰减公式import datetime import math def apply_time_decay(rating_df, half_life_days3): 时间衰减函数行为越旧权重越低 half_life_days: 半衰期天数3天表示3天前的行为权重衰减为一半 df rating_df.copy() now datetime.datetime.now() # 确保时间列是datetime类型 df[create_time] pd.to_datetime(df[create_time]) # 计算行为发生到当前的间隔单位天 df[age_days] (now - df[create_time]).dt.total_seconds() / 86400.0 # 指数衰减公式权重 0.5 ^ (age / half_life) df[decay_weight] df[age_days].apply( lambda age: math.pow(0.5, age / half_life_days) ) # 修正评分 df[rating] df[rating] * df[decay_weight] return df这个函数的核心改动就一行——原有评分乘以decay_weight实现“越近的行为权重越大”的效果。half_life_days3是一个直观的入门设定可调范围在2到7之间。新闻推荐比电商推荐对时间更敏感半衰期设3天比较符合阅读习惯。实现中时间复杂度没有变化只是多了一列计算对性能几乎无影响推荐效果肉眼可见地提升。6.2 评估推荐效果这种毕业设计怎么量化说明“我的系统是有效的”新闻推荐系统的评估比电商更困难——用户没有显式评分缺乏标准答案。毕设答辩时如果只说“效果好”没有任何数据支撑这一块会被追问到很难看。推荐系统行业里最常用的离线评估指标有三个准确率Precision、召回率Recall、覆盖率Coverage。但在新闻推荐场景里最常用也最易解释的是前两个。做法是把行为数据按时间切成训练集和测试集比如用前5天数据训练用第6天“用户真实点击了哪些新闻”作为答案然后让模型给这些用户预测第6天可能看的新闻。如果模型推荐的10条新闻里有2条是用户真实实际点击过的那Precision10就是0.2。def evaluate(model, train_df, test_df, top_n10): 计算推荐结果的精确率和召回率 # 按用户分组构建测试集的真实点击 test_user_items test_df.groupby(user_id)[news_id].apply(set).to_dict() hit_count 0 # 推荐命中的总次数 rec_count 0 # 总推荐条数 test_item_count 0 # 测试集总条数 for user_id, actual_items in test_user_items.items(): # 模型只推荐训练中出现过的用户 if user_id not in model.user_items: continue # 生成推荐 rec_items model.recommend(user_id, top_k10, top_ntop_n) rec_item_ids set([item[0] for item in rec_items]) # 统计命中 hits rec_item_ids actual_items hit_count len(hits) rec_count len(rec_item_ids) test_item_count len(actual_items) precision hit_count / rec_count if rec_count 0 else 0 recall hit_count / test_item_count if test_item_count 0 else 0 f1 2 * precision * recall / (precision recall) if (precision recall) 0 else 0 return { precision: round(precision, 4), recall: round(recall, 4), f1: round(f1, 4) }逻辑说明这套evaluate方法实现了Precision和Recall的基本计算逻辑——Precision衡量的是“推荐了10条有几条是用户真正看的”Recall衡量的是“用户实际看了10条我推荐出来了其中几条”。在实际毕业论文的实验章节中需要跑多组不同参数对比结果top_k取5/10/20时间窗口取3/5/7天评分权重取几组不同设置然后把对比数据做成表格这就是最有说服力的实验章节素材。6.3 一个亲测有效的参数组合和它的局限综合上面的讨论对新闻推荐场景下入门快速跑通的默认参数我一般推荐这组参数项推荐值说明相似度公式余弦相似度数据稀疏时比皮尔逊更稳定top_k10邻居数量建议搜索5~30top_n10推荐数量也可与前端翻页联动时间窗口7天配合半衰期3天的时间衰减点击/阅读/收藏权重0.6 / 1.0 / 1.2可调收藏不宜过高相似用户数阈值3低于该阈值时启用热门兜底这组参数我在好几个项目上验证过逻辑上限明确它们适合“新闻门户短生命周期内容”的通用场景但如果你的毕设定位是某个垂直领域比如金融资讯或体育新闻就没有参考价值——垂直领域的用户行为模式完全不同必须从头调参。做这类毕业设计最怕的不是算法复杂而是数据链路只通到一半爬了新闻、建了表、模型能跑出来但问到“为什么推荐这条给这个用户”就答不上来。所以我的习惯是哪怕时间再紧也要把推荐结果的中间过程留在代码里——哪个相似用户贡献了这条推荐、他的权重是多少全部能打印出来看。一个能自解释的推荐系统比一个精度高0.5个百分点的黑匣子在毕业答辩时的价值高得多。希望这些内容能帮你在动手时少踩一些我已经踩过的坑。本文还有配套的精品资源点击获取
返回列表