二叉树与平衡树:数据结构核心原理与工程实践
2026/7/18 7:27:40
欢迎来到第90天!
在昨天,我们已经完成了数据探索和预处理,并计算出了用户之间的相似度矩阵。今天,我们要完成这个项目的最后两步:
这将是我们 99 天旅程中的最后一个大型实战项目,它综合了数据分析、机器学习和 Web 开发的知识。
本节内容:
我们的目标是:get_recommendations(user_id, num_recommendations=10)。
算法步骤回顾:
user_id最相似的 K 个邻居。user_id"没看过"的电影。importpandasaspdfromsklearn.metrics.pairwiseimportcosine_similarity# --- 以下代码为 Day 89 的回顾,假设已运行 ---movies=pd.read_csv("./ml-latest-small/movies.csv")ratings=pd.read_csv("./ml-latest-small/ratings.csv")df=pd.merge(ratings,movies,on="movieId")movie_matrix=df.pivot_table(index='userId',columns='title',values='rating')user_item_matrix=movie_matrix.fillna(0)user_similarity=cosine_similarity(user_item_matrix)user_sim_df=pd.DataFrame(user_similarity,index=user_item_matrix.index,columns=user_item_matrix.index)# --- 核心推荐函数 ---defget_recommendations(user_id,num_recommendations=10):# 1. 找到该用户的相似度向量,并排除自己similar_users=user_sim_df[user_id].drop(user_id).sort_values(ascending=False)# 2. 筛选 Top K 邻居 (这里我们简化,取所有相似度>0的用户)neighbors=similar_users[similar_users>0]ifneighbors.empty:returnpd.Series(dtype='float64')# 没有相似用户# 3. 找出用户看过的电影,用于过滤user_watched_movies=user_item_matrix.loc[user_id]user_watched_movies=user_watched_movies[user_watched_movies>0].index.values# 4. 计算推荐分数recommend_scores=pd.Series(dtype='float64')forneighbor_id,similarityinneighbors.items():# 获取邻居看过的电影neighbor_movies=user_item_matrix.loc[neighbor_id]formovie_title,ratinginneighbor_movies[neighbor_movies>4].items():# 只考虑高分电影ifmovie_titlenotinuser_watched_movies:# 累加分数 (相似度 * 邻居评分)recommend_scores.loc[movie_title]=recommend_scores.get(movie_title,0)+similarity*rating# 5. 返回 Top Nreturnrecommend_scores.sort_values(ascending=False).head(num_recommendations)# 测试recs=get_recommendations(1)print(recs)为了方便在 Flask 中调用,我们把整个逻辑封装成一个类。
# recommend_engine.pyimportpandasaspdfromsklearn.metrics.pairwiseimportcosine_similarityclassRecommendationEngine:def__init__(self,ratings_path,movies_path):self.movies=pd.read_csv(movies_path)self.ratings=pd.read_csv(ratings_path)self._prepare_data()def_prepare_data(self):print("正在准备数据...")df=pd.merge(self.ratings,self.movies,on="movieId")movie_matrix=df.pivot_table(index='userId',columns='title',values='rating')self.user_item_matrix=movie_matrix.fillna(0)print("正在计算相似度...")user_similarity=cosine_similarity(self.user_item_matrix)self.user_sim_df=pd.DataFrame(user_similarity,index=self.user_item_matrix.index,columns=self.user_item_matrix.index)print("引擎准备就绪!")defget_recommendations(self,user_id,n=10):# ... (将上面的函数逻辑搬到这里) ...# 注意修改变量名为 self.xxxsimilar_users=self.user_sim_df[user_id].drop(user_id).sort_values(ascending=False)# ... (后续逻辑同上)# 为了方便 API 返回,我们把结果转成 list of dictrecs_df=recs.reset_index()recs_df.columns=['title','score']returnrecs_df.to_dict('records')现在,我们创建一个简单的 Flask 应用来暴露这个推荐服务。
# app.pyfromflaskimportFlask,jsonify,requestfromrecommend_engineimportRecommendationEngine# 假设上面的类已保存app=Flask(__name__)# --- 全局加载推荐引擎 (只在启动时加载一次) ---print("正在初始化推荐引擎,请稍候...")engine=RecommendationEngine("./ml-latest-small/ratings.csv","./ml-latest-small/movies.csv")@app.route("/recommend",methods=["GET"])defrecommend():# 从 URL 参数获取 user_iduser_id=request.args.get('user_id',type=int)ifnotuser_id:returnjsonify({"error":"缺少 user_id 参数"}),400try:recommendations=engine.get_recommendations(user_id)returnjsonify(recommendations)exceptKeyError:returnjsonify({"error":f"用户{user_id}不存在"}),404if__name__=="__main__":# host='0.0.0.0' 让局域网内其他设备也能访问app.run(debug=True,host='0.0.0.0',port=5001)python app.py。http://127.0.0.1:5001/recommend?user_id=1。你应该能看到类似这样的 JSON 返回:
[{"title":"Star Wars: Episode IV - A New Hope (1977)","score":10.5},{"title":"Fight Club (1999)","score":8.2},...]至此,一个最基础的协同过滤推荐系统就完成了。虽然它很简单,但麻雀虽小,五脏俱全。
surprise库或深度学习(如LightGCN)来构建更精准的模型。恭喜你,完成了这个贯穿数据分析、机器学习和 Web 开发的综合项目!
我们回顾一下这个项目的技术栈:
你已经证明了你有能力将多个领域的知识整合起来,解决一个复杂的实际问题。
Day 91:项目篇(三) - AI 聊天机器人 (上) - 结合 LangChain 与 Web- 推荐系统告一段落。明天,我们将启动最后一个终极项目:做一个带记忆、能联网、能读文档的 Web 版 AI 聊天机器人!
系列导航: