基于Python的旅游景点推荐系统毕设:从数据建模到Flask部署的实战全流程

📅 发布时间:2026/7/6 12:23:28 👁️ 浏览次数:
基于Python的旅游景点推荐系统毕设:从数据建模到Flask部署的实战全流程
基于Python的旅游景点推荐系统毕设从数据建模到Flask部署的实战全流程1. 毕设常见痛点数据稀疏、冷启动、工程化缺失做推荐系统毕设最容易被导师三连问“数据这么稀疏结果靠谱吗”“新用户来了你推啥”“代码跑通就算完能上线吗”多数同学直接调 Surprise 或 LightFMAUC 好看却忽视工程闭环– 离线脚本一把梭路径写死– 模型 PKL 一扔前端调不通– 评审现场 502当场社死。痛点总结数据稀疏用户-景点评分矩阵密度 1%纯协同过滤易过拟合。冷启动新用户无行为新景点无评分直接空白。工程化缺失没有 REST 接口、没有缓存、没有异常兜底演示时一并发抖。2. 技术选型为什么 UserCF 内容标签而非矩阵分解或深度模型方案优点缺点毕设场景矩阵分解 (SVD)精度高训练慢、黑盒、难解释导师看不懂深度学习 (DSSM)能融合多模态数据量要大、GPU 贵服务器没有卡UserCF 内容简单、可解释、冷启动友好精度略逊够用、能讲清结论时间紧、资源少、要演示UserCF 负责“老朋友喜欢啥我推啥”内容标签负责“新景点/新用户兜底”两者加权融合5 天可撸完。3. 核心实现拆解3.1 数据预处理原始爬来的 CSV 长这样user_id,spot_id,rating,tag u001,s010,5,古镇,人文步骤去重同一用户对同一景点多次评分取均值。归一化Min-Max 缩到 [0,1]消除评分尺度差异。构造“用户-景点”矩阵pandas pivot_table空值填 0但保留 mask 供后续相似度计算忽略。3.2 相似度计算余弦相似度适合稀疏向量计算快。皮尔逊修正减去用户均值抵消打分偏置。代码片段已 Clean Codedef cosine_plus_pearson(mat: csr_matrix, u_vec: np.ndarray, u_mean: float): 计算修正后的余弦相似度 u_vec_centered u_vec - u_mean sim mat.dot(u_vec_centered) / ( norm(mat, axis1) * norm(u_vec_centered) 1e-8 ) return sim3.3 混合推荐权重UserCF 得分 与 内容标签得分 线性加权final_score α * usercf_score (1-α) * content_scoreα 按用户行为条数动态调整– 行为 ≥10α0.8– 行为 10α0.3冷启动用户直接走内容推荐。4. 完整可运行代码项目结构travel-rec/ ├── app.py # Flask 入口 ├── rec/ │ ├── __init__.py │ ├── data.py # 数据加载 │ ├── model.py # 推荐逻辑 │ └── utils.py # 工具 └── data/ └── travel.csv4.1 model.py核心 50 行import pandas as pd import numpy as np from scipy.sparse import csr_matrix from sklearn.metrics.pairwise import cosine_similarity class HybridRec: def __init__(self, csv_path, top_k20, alpha0.7): self.top_k top_k self.alpha alpha self._load_data(csv_path) self._compute_similarity() def _load_data(self, path): df pd.read_csv(path) self.user_mean df.groupby(user_id)[rating].mean().to_dict() self.mat df.pivot_table(indexuser_id, columnsspot_id, valuesrating).fillna(0) self.spot_tags df.groupby(spot_id)[tag].first().to_dict() self.user_item csr_matrix(self.mat.values) def _compute_similarity(self): self.sim_matrix cosine_similarity(self.user_item) def recommend(self, user_id, n5): if user_id not in self.mat.index: return self._cold_start(n) uid_idx self.mat.index.get_loc(user_id) user_vec self.mat.iloc[uid_idx].values u_mean self.user_mean[user_id] sims self.sim_matrix[uid_idx] # 找到最相似用户 top_users np.argsort(sims)[-self.top_k:] # 加权平均得分 rec_scores np.zeros(self.mat.shape[1]) for u in top_users: rec_scores sims[u] * self.mat.iloc[u].values # 内容得分 content_scores self._content_score(user_vec) # 融合 final self.alpha * rec_scores (1 - self.alpha) * content_scores # 排除已交互 seen user_vec.nonzero()[0] final[seen] -1 top_items np.argsort(final)[-n:][::-1] return self.mat.columns[top_items].tolist() def _content_score(self, user_vec): 简单标签匹配用户历史标签出现频次 from collections import Counter visited np.where(user_vec 0)[0] tags [] for idx in visited: spot self.mat.columns[idx] tags.extend(self.spot_tags.get(spot, ).split(,)) user_tags Counter(tags) content_vec np.zeros(self.mat.shape[1]) for idx, spot in enumerate(self.mat.columns): spot_tag_list self.spot_tags.get(spot, ).split(,) content_vec[idx] sum(user_tags[t] for t in spot_tag_list if t in user_tags) return content_vec / (content_vec.max() 1e-8) def _cold_start(self, n): 冷启动返回热门景点 return self.mat.sum().nlargest(n).index.tolist()4.2 app.py20 行from flask import Flask, request, jsonify from rec.model import HybridRec app Flask(__name__) rec HybridRec(data/travel.csv) app.route(/rec, methods[GET]) def recommend(): user_id request.args.get(user_id) if not user_id: return jsonify({error: missing user_id}), 400 spots rec.recommend(user_id) return jsonify({spots: spots}) if __name__ __main__: app.run(debugFalse, threadedTrue)启动export FLASK_APPapp.py flask run --host0.0.0.0 --port50005. 性能与安全缓存使用 Flask-Caching 把/rec结果缓存 5 min避免重复计算。请求幂等GET 接口天然幂等不加副作用。隐私脱敏返回包只给 spot_id不把用户历史明文带出日志打码 user_id 后四位。6. 生产环境避坑指南坑现象解决本地路径硬编码换电脑就 FileNotFound用pathlib.Path(__file__).parent动态拼NaN 未处理推荐结果空列表fillna(0)后统一转 intFlask 开发服务器并发压测 20 线程直接卡死上 Gunicorn Geventgunicorn -k gevent -w 4 app:app矩阵太大内存炸10 万用户 8 G 爆只保留交互 ≥3 的用户稀疏度降到 0.3%7. 效果演示本地起服务后前端 Vue 调/rec?user_idu001200 ms 内返回 5 个景点导师现场点赞。8. 后续可玩实时行为日志怎么接目前模型离线天级更新。想再卷一点可以用 Nginx Lua 把用户点击日志实时打到 KafkaFlink 流处理 5 min 窗口更新用户最近交互向量通过 Redis Pub/Sub 通知 Flask 热更新相似度缓存。一句话先把离线链路跑通再逐步“流式化”毕设就能从“能跑”进化到“像真的”。把代码推到 GitHubREADME 写清依赖版本答辩时亮出 Swagger 文档和缓存命中率老师基本不再追问。动手跑一遍你会发现推荐系统其实也没那么玄学工程闭环才是硬通货。