简介面向高校毕业设计或课程设计场景的酒店推荐系统完整项目基于Python 3.7.7与Django框架开发后端使用MySQL 5.7存储涵盖管理员与用户双端功能。管理员可管理用户、客房类型、酒店客房、预定审核、入住登记、续订退房、留言反馈及系统配置用户端支持客房浏览、在线预定、入住登记、续订退房与个人资料维护前后端源码齐全并附带LW文档。资源包共597个文件体积33.83MB主要包含Vue前端页面、svg图标、JavaScript脚本、Python逻辑代码、pyc编译文件、CSS样式以及数据库SQL脚本、启动运行bat和项目说明doc等目录结构清晰便于二次开发与部署调试。压缩包内含安装、运行、构建等bat脚本可按照文档配置环境快速跑通预定、入住、续订、退房全流程。目前已有63人学习下载适合需要参考完整业务闭环或快速搭建毕设演示项目的Python初学者及应届生使用。1. 酒店推荐系统是什么先想清楚这个题目值不值得做把「酒店推荐系统」这种题目做成 Python 毕业设计最大的价值在于它同时压中了推荐算法、前后端分离开发、数据库建模三个考核点。用户打开页面看到一排“猜你喜欢”背后是物品相似度矩阵、用户行为日志和冷启动兜底规则在共同起作用。这篇笔记按我实际做过的方案讲清楚算法怎么选、源码怎么跑、数据怎么造以及哪些坑会让答辩现场翻车。适合准备做 Python 方向选题、又不想只写 CRUD 的同学。2. 推荐算法选型协同过滤、基于内容还是混合策略最稳2.1 协同过滤的两种形态在酒店场景里该用 UserCF 还是 ItemCF推荐系统最常见的落地方案是协同过滤它不关心酒店长什么样只关心“谁和谁的行为像”。核心输入是一张行为矩阵行是用户列是酒店格子里是评分或行为加权值。矩阵里多数位置是空的因为一个用户一辈子也住不了几家酒店。UserCF 的思路是先找与目标用户行为最像的一批人再把他们评分高的酒店加权推荐给目标用户ItemCF 则反过来先算酒店与酒店之间的相似度再根据用户曾经产生过行为的酒店去扩展推荐。两者的中间结果都是相似度矩阵区别只在矩阵的行列方向。对比维度UserCFItemCF计算对象用户与用户的相似度酒店与酒店的相似度实时性用户行为变化会立刻影响邻居集合酒店属性稳定相似度可离线算好冷启动新用户无行为完全失效新酒店无行为完全失效可解释性难以解释“为什么推这家”可用“你收藏过同价位酒店”解释酒店场景结论不推荐做主算法推荐做主算法酒店消费和买书、看视频不一样用户订酒店大多依赖出差、旅行等具体场景同一批人前后两次打开App的口味可能完全不同。UserCF 用“相似用户”去推容易把用户过去某个场景下的偏好带到新场景里而 ItemCF 只关心酒店本身像不像价格区间、星级、位置这些属性不会因为用户心情变化而改变所以更稳。相似度计算多数项目用余弦相似度代码很短import numpy as np import pandas as pd # rating_matrix: DataFrame, 行是用户, 列是酒店, 空位填 0 def item_similarity(rating_matrix): mat rating_matrix.to_numpy(dtypenp.float64) # 按列计算模长, axis0 表示沿着用户维度算每个酒店的向量长度 norm np.linalg.norm(mat, axis0) # 酒店与酒店的相似度 列向量两两点积 / 模长乘积 sim mat.T mat / (norm[:, None] * norm[None, :] 1e-8) return pd.DataFrame(sim, indexrating_matrix.columns, columnsrating_matrix.columns)这里有一个关键参数 1e-8它是个极小的平滑项避免某个酒店完全没有行为时模长为 0 导致除零报错。mat.T mat是一次矩阵乘法也就是把所有酒店的共现次数一次性算完比嵌套 for 循环快几个数量级。如果只用一个用户的两条记录调试它跑得不如循环直观但数据到几千个酒店时矩阵乘法是唯一能扛住的做法。2.2 基于内容与混合推荐冷启动兜底怎么做协同过滤有一个致命缺陷新用户没有任何行为记录系统就不知道他是谁新酒店没有任何曝光系统也不知道它该推给谁。毕业设计里最容易出现的翻车现场就是——注册一个新账号首页推荐列表是空的。基于内容的推荐能部分解决这个问题。它把酒店自身属性做成特征向量城市、价格区间、星级、设施标签如“免费停车”“近地铁”然后计算酒店与酒店在特征空间里的距离或者把用户的偏好规则化之后做匹配。比如用户看过的酒店都是 300 元以下的三星级那就把同价位、同城市的酒店优先推出来。这个方案不依赖行为量适合做冷启动阶段的兜底。实际项目里很少只用一个算法常见做法是混合加权。推荐分 0.6 × ItemCF 分 0.3 × 基于内容分 0.1 × 热门酒店分权重在配置里写成常量方便答辩时现场调。另一个必做的兜底是行为阈值判断当用户历史行为少于 3 条时跳过协同过滤直接走城市热门榜因为这时算出来的相似度几乎没有统计意义硬推荐反而显得系统很蠢。权重怎么调没有标准答案。我一般先拿离线数据算一遍各项指标的命中率再人肉看几个典型用户的结果确认不是“热门酒店霸榜”。如果发现 ItemCF 推荐的酒店全部集中在同一价位就把内容分的权重往上提如果推荐结果五花八门说明基于内容的特征太粗糙优先检查城市和价格有没有进特征。3. 把源码跑通从 SQL 导入到前后端联调的全流程3.1 源码包结构先分清 backend、frontend、sql、LW 各管什么拿到这种毕业设计压缩包第一件事不是急着跑而是先看清目录结构。常见版本会分成四块backendPython 后端一般用 Flask 或 FastAPI 写包含推荐算法、用户登录、酒店列表接口。frontendVue 前端负责页面展示和请求后端接口。sql数据库初始化脚本建表语句加初始数据。LW毕业设计论文和演示说明文档答辩前重点看这里的技术描述。有经验的开发者拿到后先做三件事看根目录有没有 README看 requirements.txt 里缺不缺库看 sql 脚本能不能直接导入。很多包在网上传来传去原作者的 Python 版本是 3.7你的机器是 3.11直接跑必然报错这不是代码问题是版本兼容问题。3.2 环境准备Python 虚拟环境和 Vue 依赖一次装齐强烈建议先建虚拟环境不要图省事直接往全局环境里装依赖。酒店推荐系统的依赖不算多但 Flask、pandas、scikit-learn 这些包互相之间有版本约定项目放到别的机器上答辩时虚拟环境能帮你复现一模一样的运行状态。python -m venv venv # Windows 系统执行 venv\Scripts\activate # macOS / Linux 执行 source venv/bin/activate pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simplePython 版本最好用 3.8 到 3.10 之间太新的 3.11、3.12 有时会遇到某些依赖库还没发对应版本。-i参数指定的是清华 PyPI 镜像国内下载速度快很多。装完后执行pip list核对关键包版本pandas 和 numpy 是计算相似度的基础scikit-learn 是可选加速项。前端部分如果是常见 Vue 项目在 frontend 目录下执行npm install这个命令按 package.json 里的声明拉取依赖。如果 node_modules 已经存在先删掉再重装不然经常出现“当时能跑现在报错”的玄学问题。npm install 是前端项目跑不动的第一排查点。3.3 数据库初始化导入 SQL 和改连接配置数据库是整个系统的地基推荐算法读的数据全在这里。先用命令行建库再导入脚本mysql -uroot -p create database hotel_recommend default charset utf8mb4;字符集一定要用 utf8mb4而不是 utf8。utf8mb4 是 utf8 的超集能正确存储 Emoji 和生僻字酒店名里偶尔出现的特殊符号只有它能扛住。建完库后找到 sql 目录下的 init.sql用 source 命令导入mysql -uroot -p hotel_recommend sql/init.sql导入完成后回到后端目录改连接配置。常见做法是把配置集中在一个 config.py 文件里DB_CONFIG { host: 127.0.0.1, port: 3306, user: root, password: 你的密码, database: hotel_recommend, charset: utf8mb4 }密码是个人环境差异最大的点原作者上传代码时多半会把密码脱敏成 123456 或者 root你不改就跑不通。连接串里的 charset 轻易别省否则会出现“数据库里显示正常Python 读出来变成乱码”的问题。3.4 启动后端与前端两个终端一个地址后端和前端是两个独立进程一个负责算法和接口一个负责页面。先启动后端cd backend python app.py看到日志输出Running on http://127.0.0.1:5000说明后端起来了。如果端口被占用常见的处理是换一个端口启动但要记得前端转发配置里的目标端口也要同步改。再开一个终端启动前端cd frontend npm run serve前端默认跑在 8080 端口浏览器打开 http://127.0.0.1:8080 就能看到页面。此时前后端端口不一样浏览器直接访问前端页面时页面里的 ajax 请求会跨域Vue 项目的开发环境一般会在 vue.config.js 里配置转发module.exports { devServer: { proxy: { /api: { target: http://127.0.0.1:5000, changeOrigin: true } } } };这段配置的意思是前端把以 /api 开头的请求转发到后端 5000 端口浏览器认为请求是同源的跨域问题就消失了。如果项目里没有这个文件就手动建一个改完配置必须重启 npm run serve 才会生效。4. 数据建模与特征设计行为权重、时间衰减和表结构的一次到位设计4.1 四张核心表用户、酒店、行为、推荐日志跑通源码只是起点真正决定推荐效果的是数据库里长什么样。很多毕业设计源码自带的 SQL 里只有几十条假数据算法怎么调都像在猜拳。先看四张最核心的表怎么建CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE, city VARCHAR(50) COMMENT 常用城市, created_at DATETIME ); CREATE TABLE hotel ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100), city VARCHAR(50), price DECIMAL(10, 2), star INT, score DECIMAL(2, 1), tags VARCHAR(255) COMMENT 设施标签, 逗号分隔 ); CREATE TABLE behavior ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT, hotel_id INT, action TINYINT COMMENT 1浏览 2收藏 3下单, created_at DATETIME, INDEX idx_user (user_id), INDEX idx_hotel (hotel_id) ); CREATE TABLE recommend_log ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT, hotel_id INT, strategy VARCHAR(20) COMMENT itemcf/content/hybrid/hot, score FLOAT, created_at DATETIME );行为表是整个推荐系统的燃料三个 action 值分别代表浏览、收藏、下单没有这张表协同过滤就是无米之炊。推荐日志表最容易被人忽略但它答辩时价值极大——你可以指着日志说“这个用户前一天浏览了 3 家经济型酒店今天系统给他推了同价位的新店”。4.2 行为数据转评分权重、时间衰减和 TopN 怎么设行为表里的原始记录不能直接当作评分喂给协同过滤。浏览、收藏、下单是三种强度完全不同的行为常见的权重设置是浏览 0.3、收藏 0.8、下单 1.0。下单权重远大于浏览因为用户真的掏钱了收藏介于中间说明用户感兴趣但还在比较。按用户和酒店分组求和后就得到一张行为评分矩阵def build_rating_matrix(behavior_df): # 行为权重映射 weight_map {1: 0.3, 2: 0.8, 3: 1.0} behavior_df[weight] behavior_df[action].map(weight_map) # 同一用户对同一酒店的多次行为累加 rating behavior_df.groupby([user_id, hotel_id])[weight].sum().unstack().fillna(0) return rating权重是推荐系统里第一个需要整数值的参数。遇到过有人把浏览设成 1.0、下单也设成 1.0结果刷页面比真实下单更影响推荐结果这就是典型的没想清楚行为含义。求和而不是求平均也很重要——同一家酒店用户既浏览又收藏说明兴趣在加强用平均反而把强度抹平了。时间衰减是另一个容易被忽视的参数。三个月前的收藏和昨天的收藏对当前兴趣的指示意义完全不同。常见做法是按天做指数衰减import pandas as pd # 衰减系数 0.98, 时间越久权重越低 behavior_df[decay] 0.98 ** ((pd.Timestamp.now() - behavior_df[created_at]).dt.days) behavior_df[weight] * behavior_df[decay]0.98 表示每过一天权重打 98 折30 天后还剩约 55%半年后几乎归零。这个值不是玄学要结合业务看酒店预订的决策周期很短用户不会半年前收藏一家酒店到半年后才下单所以衰减要快一些。如果换成电影推荐衰减系数就可以放到 0.995 附近。TopN 推荐个数一般设 10 到 20。推荐列表一屏展示大概 10 个卡片N 太小覆盖不到长尾酒店N 太大用户根本划不到底部。答辩时如果被问“为什么是 10”可以从用户体验和数据覆盖率两个角度解释而不要只说“照着别人写的”。4.3 相似度计算的输入数据清洗评分矩阵建好后还有一个隐蔽问题矩阵里绝大多数是 00 在余弦相似度里会被当作“负向打分”参与计算把两个只是都没看过某家酒店的用户算得很像。解决方式有三种一是把 0 替换为 NaN在计算相似度时忽略空值二是对矩阵做物品均值中心化减去每个酒店被打分的平均值三是用 top-N 共现过滤只统计两个酒店同时被同一个人打过分的次数。其中第二种最常用# 只对非零位置做均值中心化 mask rating 0 rating_centered rating - rating.mean(axis0) rating_centered[~mask] 0中心化的含义是把“每个酒店的平均分”作为基线只看用户打分偏离基线的程度。比如一家酒店平均分是 4.5用户给了 5说明是真爱另一家平均分是 2用户给了 3虽然分低但相对积极。不做中心化高分酒店永远霸占相似度榜首推荐结果会向高分店严重倾斜。5. 避坑指南五个让酒店推荐系统当场翻车的常见问题5.1 数据造得假推荐结果像在猜拳现象系统能跑但推荐结果完全没有逻辑比如一个只看过 200 元招待所的用户被推了 2000 元豪华酒店。 原因初始化 SQL 里造数据时所有用户的行为完全随机没有遵循“用户喜欢什么价位、什么城市”的规律。 解决造数据要带业务逻辑。先给每个用户设定一个偏好价位区间再从他所在城市随机挑酒店生成行为浏览、收藏、下单按比例分配。数据量不用大20 个用户、100 家酒店、500 条行为就足够演示效果。5.2 双重循环算相似度用户一多直接卡死现象数据量到几百个酒店时点击推荐要等 5 秒以上。 原因很多源码的相似度计算用了嵌套 for 循环对每个酒店两两求余弦复杂度是 O(n²)n 到 1000 后请求必然超时。 解决用矩阵乘法一次性算完就是上面 2.1 节那十几行代码。另外 ItemCF 的酒店相似度矩阵变化很慢完全可以在项目启动时加载到内存或者每周算一次存 Redis接口只做读取不要每次请求都现场算矩阵。5.3 中文乱码、端口被占、跨域不通联调三连坑现象页面能打开但酒店名称全是“???”或者接口请求报 404、503。 原因三级错误对应三个不同位置。中文乱码看建库语句是不是 utf8mb4以及 pymysql 连接串里有没有 charset404 看前端转发配置有没有把 /api 指向后端端口503 看后端进程是不是没起来或者端口被其他服务占用。 解决按顺序排查。命令行执行mysql -uroot -p进去看表里数据是否正常确认数据没问题再看后端启动日志最后看浏览器 Network 面板里请求的实际地址。前端转发配置改完要重启这个忘了就白改。5.4 冷启动不兜底接口返回空列表现象用新注册的账号登录首页 “猜你喜欢” 区域一片空白。 原因新用户没有行为记录协同过滤计算出空矩阵接口返回了空列表前端又没有做空态处理。 解决三层兜底。行为少于 3 条的用户直接返回城市热门酒店热门酒店也要为空时返回全站评分最高的酒店后端无论如何不返回 500 错误空列表也要给个 200 状态码前端用“暂无推荐先看看热门”引导用户去产生行为。5.5 答辩被问“指标多少”答不上来现象演示很好老师一句“你的推荐准确率是多少”直接卡壳。 原因项目里完全没有评估模块自己都没跑过任何量化指标。 解决写一个三十行的离线评测脚本把数据按时间切分成训练集和测试集算 Precision10 和 Recall10。数值不需要好看关键是能说出来“在 100 个测试用户上推荐列表里有 12% 的酒店命中用户实际下单的酒店”这句话比任何演示都有说服力。6. 验证推荐效果最小的离线评测代码和一个能写进论文的指标6.1 用留一法切分用户行为计算 PrecisionK留一法是最省事的评测方案对每个用户把最后一次行为当作测试集之前的行为当作训练集看系统在前 10 个推荐里有没有命中用户最后选的酒店。代码如下def evaluate(train_df, test_df, top_k10): # 假设 recommend() 返回按得分排名的酒店 id 列表 hit 0 for user_id, row in test_df.iterrows(): rec_list recommend(user_id, train_df, top_k) if row[hotel_id] in rec_list: hit 1 precision hit / len(test_df) recall hit / len(test_df) # 每个用户只取一条测试记录, 二者相等 print(fPrecision{top_k}: {precision:.4f}, 命中用户数: {hit})Precision10 的含义是推荐列表里真正被用户选中的比例答辩时重点解释这个指标就行不需要展开讲公式。推荐效果和冷启动兜底策略强相关如果测试用户里有很多行为极少的新用户指标会被拉低这是正常的评分时老师更关心你是否理解为什么低。6.2 一个能写进论文的进阶方向如果代码和指标都有了想再拉开差距可以尝试把 ItemCF 的相似度矩阵从向量空间换成近邻图或者用 LightFM 同时建模用户侧和物品侧特征。但对于酒店推荐系统最实际的进阶是把推荐原因写进接口比如“因为你收藏过同价位的 XX 酒店”可解释性比再调高一个百分点的准确率更打动答辩老师。我做这类选题吃过最大的亏就是前期只顾跑通代码没想清楚“怎么证明它在工作”。后来养成一个习惯任何推荐项目先建 recommend_log 表每次推荐都记录策略和得分。这样无论出了什么问题第一反应不是猜而是先看日志再做离线评测。希望帮到你。本文还有配套的精品资源点击获取