news 2026/10/21 6:02:42

基于协同过滤的租房推荐系统:Python+Django全栈实现与踩坑记录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于协同过滤的租房推荐系统:Python+Django全栈实现与踩坑记录

把“毕业设计”做成一个能打的东西:基于协同过滤的租房推荐系统全记录

每年的毕业设计季,我都能看到大量同质化的管理系统、商城网站题目。说实话,这类题目不是不能做,但答辩时很难讲出亮点。相比之下,带推荐算法、带可视化、带数据分析的题目,天然就比增删改查高一个层次,也好讲故事。今天这篇就完整复盘一下我做“基于Python与Django的协同过滤租房推荐系统”的全过程,从选题思路、算法选型、数据处理,到后端实现、可视化展示,再到测评踩坑,基本把我踩过的坑和觉得值得参考的做法都写出来了。

这个题目覆盖了Django框架、协同过滤推荐算法、可视化展示和大数据预处理几个模块,工作量足够撑起一篇合格的毕业设计,技术栈又不会冷门到没人指导。不管你是正在选题的学生,还是想快速上手“推荐系统+Web框架”这套组合的开发者,这篇文章应该都能给你一些直接可用的思路。我会尽量把代码思路、数据结构、算法实现过程讲清楚,而不是只贴一段跑不通的代码。

1. 为什么选“租房推荐”当毕业设计题目:先想清楚这几个问题

1.1 选题的性价比分析

毕业设计最怕什么?最怕题目看着高大上,实际做不出来;也怕题目太简单,答辩被追问两句就露馅。租房推荐这个方向,恰好卡在了一个很舒服的位置。

首先,业务场景足够贴近生活。房源推荐、用户找房、智能匹配,这些概念评委一听就懂,不需要额外解释行业背景。其次,数据获取的难度适中。房源数据不像金融、医疗数据那样敏感,自己构造一套足够真实的数据集并不难,也可以从公开数据集中找思路。再次,推荐算法在本科阶段属于“跳一跳够得着”的难度,协同过滤的数学基础就是相似度计算,不需要深度学习那样的复杂网络结构,但又能讲出完整的推荐链路。

从工作量角度看,这个题目天然包含四个模块:数据部分(采集或构造、清洗、预处理)、算法部分(协同过滤的两种经典思路)、后端部分(Django提供接口和页面渲染)、可视化部分(房源分布、推荐结果解释、用户行为分析),随便展开哪一个,都能写出两三章的内容。

1.2 技术栈搭配的合理性

技术栈选的是 Python 3.9 + Django 3.2 + SQLite/MySQL + ECharts 可视化。Python 是算法实现的默认语言,Django 负责把算法包装成可用的 Web 服务,ECharts 负责把分析结果变成图表。

有人会问,为什么不直接用 Flask?Flask 确实更轻量,但毕业设计里 Django 有个很实际的优势:自带 Admin 后台、ORM、表单、认证体系。答辩现场演示时,Django 后台可以直接管理房源数据和用户数据,视觉效果更完整,也减少了手动写管理页面的工作量。另外,Django 的 ORM 在操作数据时比裸写 SQL 更安全,不容易出现注入问题。

数据库我建议用 MySQL 或 PostgreSQL,本地开发用 SQLite 也够。不过考虑到答辩现场的演示环境可能是临时搭建的,SQLite 文件数据库反而是最稳的选择,拷过去就能跑。我自己最终用了 MySQL,但代码里通过 Django 的 settings 配置切换数据源非常方便,这部分后面会讲到。

1.3 这个项目要解决的核心问题

把题目拆开看,真正要回答的问题就三个:

  • 如何根据用户的历史行为(收藏、浏览、租住),从大量房源中找出他可能感兴趣的房源?
  • 如何让推荐结果“说得清楚”,即为什么推荐这套房?这关系到可视化如何展示。
  • 如何评估推荐效果,证明这个系统不是随便猜的?

这三个问题分别对应推荐算法的输入、输出和评测,也是整篇论文的逻辑主线。我强烈建议所有做推荐系统选题的同学,动工前先把这三个问题写进开题报告里,后面所有工作都是在为这三个问题找答案。

2. 协同过滤到底怎么在房源场景落地:从原理到算法选型

2.1 两种协同过滤的直观理解

协同过滤的核心假设很简单:和你行为相似的人,喜欢的房子大概率你也喜欢;或者,你喜欢的那套房,和另一套房在历史行为上经常被一起关注,那你也很可能喜欢那另一套房。

前者叫基于用户的协同过滤(User-based CF),后者叫基于物品的协同过滤(Item-based CF)。用租房场景举例子可能更容易理解。

用户A收藏了小区X的两套房,收藏了小区Y的一套房;用户B收藏了小区X的一套房。系统通过用户A和用户B在“小区X”上的共同行为,判断他们偏好相似,于是把小区Y的房源推荐给用户B。这是UserCF的逻辑。

另一条线,一套房C被收藏的用户同时收藏了房源D,次数多了就认为C和D是“相似物品”。用户A收藏了C,系统就把D也推荐给他。这是ItemCF的逻辑。

2.2 场景适配:UserCF和ItemCF怎么选

理论上两种都能做,但落地时要考虑数据规模和实时性。

UserCF 适合用户少、物品多的场景,推荐结果偏重“社会热度”,喜欢发掘新东西。但租房场景里用户量级通常远小于房源量级,而且用户行为非常稀疏,多数用户一辈子也就租过几次房,收藏几十套房已经算活跃,导致用户相似度矩阵极其稀疏。

ItemCF 适合物品数量相对少、用户行为相对稳定的场景,推荐结果偏重“相似偏好”,解释性也更强。“因为您收藏了A小区的一居室,所以推荐同小区的另一套一居室”——这种推荐理由用户一眼就能看懂。

房源本身是相对长尾的物品,但一个城市里可租房源的总量有限,同一地段、同一户型的房源有天然的相似性,所以 ItemCF 在租房场景里表现得比 UserCF 更稳定。我的系统最终以 ItemCF 为主,同时保留了 UserCF 的对比实验,用于论文里的效果对比。

2.3 相似度计算的具体选择

相似度计算是整个协同过滤的心脏。常用的有杰卡德相似系数、余弦相似度和皮尔逊相关系数。

杰卡德相似系数适合只关注“是否共同出现”的场景,比如判断两套房是否经常被同一批用户收藏。余弦相似度把用户对房源的评分看成向量,计算向量夹角,适合评分数据。皮尔逊相关系数则在余弦相似度的基础上做了均值中心化,能缓解用户评分尺度不同的问题。

房源交互数据大多数情况下是隐式反馈,即“收藏了就是1,没收藏就是0”,没有真实的评分语义。这种情况下我会优先用杰卡德相似系数或者改进后的余弦相似度(在向量里做0/1统计)。如果数据集里模拟了评分(比如用户对房源打分1到5),再用皮尔逊。这个选择逻辑建议写进论文,能体现你理解这些公式背后的适用条件。

2.4 推荐得分怎么算

ItemCF 的核心流程分两步:

  1. 离线计算物品之间的相似度矩阵,得到“相似物品表”。
  2. 在线推荐时,根据用户历史交互过的物品,累加相似物品表中对应物品的相似度,得到每个候选房源的得分,取 Top-N 输出。

得分公式大致是:

score(user, item) = sum(similarity(item, item_h) * preference(user, item_h))

其中preference(user, item_h)表示用户对这个历史房源的偏好权重,收藏是1,点击是0.5,租住是2。给不同行为配不同权重,是提升推荐效果最直接的小技巧。很多入门项目只用0/1,我实测加了权重之后,推荐列表的相关性明显提升。

3. 房源数据从哪里来:数据集构造、清洗与特征工程

3.1 数据字段设计

租房数据字段不需要太复杂,但要覆盖推荐的输入输出和可视化分析。我设计的核心字段如下:

  • 房源表:房源ID、标题、小区名称、租金、户型(几室几厅)、面积、朝向、楼层、区域、地铁站距离、描述文本、标签(精装/近地铁/南向)、发布时间、图片URL。
  • 用户表:用户ID、用户名、注册时间、偏好标签、常驻区域。
  • 行为表:用户ID、房源ID、行为类型(浏览/收藏/租住)、行为时间。

行为表是推荐算法的核心输入,房源表的字段决定特征多样性和可视化的丰富度。建议把“小区名称”和“区域”分开存,别合成一个字段,否则后面做区域热度统计的时候会非常痛苦。

3.2 数据构造:不用纠结没数据,重点是逻辑合理

没有真实房源数据是大多数学生做这个题目的第一道坎。我试过找公开数据集,但很多房产平台的爬取行为有法律风险,不建议碰。最终我用的是自动生成加人工规则约束的方式,生成了约3000套房源、500个用户、数万条行为数据。

构造数据不是随机乱造。要让推荐算法跑起来有意义,数据必须内嵌“可被学习的规律”。比如,随机生成3000套房源,用户行为纯随机,那推荐结果必然是一团糟,答辩时一跑结果全乱,场面会很难看。

我的做法是预先定义一些用户偏好模板,比如“预算5000以内、喜欢南向、靠近地铁”,然后让行为生成器按模板分配用户对这类型房源的收藏概率,其他房源只有少量随机行为。这样既模拟了真实场景,又保证了数据里有明显的偏好结构,算法能学出来,测评指标也好看。这个“内嵌规律”的思路,请大家务必重视,它直接决定你的推荐效果。

3.3 数据预处理的几个关键步骤

原始行为数据不能直接喂给算法,至少要经过三步处理:

  • 去重:同一用户对同一房源多次浏览只保留最大权重的那条行为。
  • 过滤:交互次数过少的用户和房源单独拎出来处理。比如交互少于3次的用户,相似度计算里基本贡献不了有效信息,放到冷启动测试集里单独分析。
  • 时间切分:把行为按时间切分为训练集和测试集,保证测试集的用户行为晚于训练集,模拟“用过去预测未来”,而不是随机打乱。这一点在答辩时很容易被问到。

清洗完的数据我会另存成 CSV,下一步直接用 Python 读取做矩阵运算,这样把数据处理和 Django 解耦,调试起来更快。

4. Django 后端怎么组织:模型设计、推荐服务与接口层

4.1 项目结构规划

用 Django 写推荐系统,最忌讳把所有逻辑塞进 views.py。我的项目结构是这样划分的:

house_project/ ├── manage.py ├── config/ # settings, urls ├── apps/ │ ├── houses/ # 房源模块:模型、视图、序列化 │ ├── users/ # 用户模块 │ ├── behaviors/ # 行为模块:收藏、浏览记录 │ └── rec/ # 推荐模块:算法核心、推荐接口 └── data/ # 预处理脚本、生成好的 CSV

推荐算法单独放在 rec 模块,不跟业务模块混在一起。这样做的原因是推荐逻辑是相对独立的纯计算过程,单元测试、替换算法都很方便。我做了一个简单的推荐接口抽象,ItemCF、UserCF 都实现同一个接口,后面扩展基于内容的推荐时也顺手。

4.2 ORM 模型设计要点

Django 的 ORM 模型对应上面说的三张核心表,这里给出简化版代码思路:

# apps/houses/models.py from django.db import models class House(models.Model): title = models.CharField(max_length=255) community = models.CharField(max_length=128, db_index=True) district = models.CharField(max_length=32, db_index=True) price = models.IntegerField() rooms = models.CharField(max_length=16) # 例如 "3室2厅" area = models.FloatField() orientation = models.CharField(max_length=8) # 南向/北向等 metro_distance = models.IntegerField() # 距最近地铁站米数 tags = models.CharField(max_length=255, blank=True) created_at = models.DateTimeField(auto_now_add=True)

设计上注意几点:需要在 where 条件里做筛选的字段(如小区、区域、租金区间)一定要加db_index=True,既能提升查询效率,也方便后面做可视化交互筛选。价格用整数存月租金,不要用 FloatField,避免浮点精度问题。

行为表建议重点设计,它是协同过滤的输入源。

# apps/behaviors/models.py class Behavior(models.Model): USER_ID = "user_id" HOUSE_ID = "house_id" BEHAVIOR_TYPES = [ ("view", "浏览"), ("collect", "收藏"), ("rent", "租住"), ] user = models.ForeignKey(User, on_delete=models.CASCADE) house = models.ForeignKey(House, on_delete=models.CASCADE) behavior_type = models.CharField(max_length=16, choices=BEHAVIOR_TYPES) weight = models.FloatField(default=1.0) # 浏览0.5 收藏1.0 租住2.0 created_at = models.DateTimeField(auto_now_add=True)

为什么额外加一个 weight 字段而不是直接用 behavior_type 判断权重?因为如果后续想调整权重,只改数据不改代码,而且可以把“偏好衰减”做进去——越近的行为权重越高。这个设计在答辩时也能讲出深度。

4.3 推荐接口的实现逻辑

推荐接口的核心思路是:加载离线计算好的物品相似度矩阵,查询用户最近行为,计算候选房源得分,返回 Top-N。

这里给出简化版本的推荐服务代码:

# apps/rec/services.py import pandas as pd import numpy as np from collections import defaultdict class ItemCFRecommender: def __init__(self, sim_matrix_path, top_k=10): self.sim_matrix = pd.read_csv(sim_matrix_path, index_col=0) self.top_k = top_k def recommend_for_user(self, user_behavior_house_ids, preference_dict): """user_behavior_house_ids: 用户交互过的房源id列表 preference_dict: {house_id: weight} """ score = defaultdict(float) for house_id in user_behavior_house_ids: if house_id not in self.sim_matrix.index: continue sim_series = self.sim_matrix[house_id].drop(labels=[house_id]) for cand_id, sim_val in sim_series.items(): if cand_id in user_behavior_house_ids: continue score[cand_id] += sim_val * preference_dict.get(house_id, 1.0) ranked = sorted(score.items(), key=lambda x: x[1], reverse=True) return [house_id for house_id, _ in ranked[:self.top_k]]

注意这里做了一个过滤:已经交互过的房源不要重复推荐,这在业务上非常有必要,否则用户收藏过的房子还会再被推回来。

在 Django view 层调用推荐服务时,不要直接操作相似度矩阵,而是通过 query 参数传入用户ID,service 内部查行为表。这样接口语义清晰,也方便下游可视化页面调用。

4.4 接口返回结构与前端约定

推荐接口我建议返回结构化 JSON,而不是渲染好 HTML。前端用 Aajx 请求 JSON,再根据数据用 ECharts 渲染图表,这样前后端职责清楚,后期想改成小程序或者 App 也能复用。

{ "user_id": 1001, "recommendations": [ { "house_id": 5032, "title": "某某小区南向一居室", "price": 4500, "score": 3.72, "reason": "与您收藏的房源相似,同小区/同户型", "tags": ["近地铁", "精装"] } ], "algorithm": "item_cf", "cost_ms": 12 }

reason 字段是我特意加的,它把“为什么推荐”变成了可展示的内容,这直接为可视化部分提供了解释性素材。cost_ms 则是顺手统计的响应耗时,答辩时可以拿出来讲性能。

5. 可视化展示:让推荐结果“看得见、说得清”

5.1 可视化的四个维度

这一题的关键词里带了“可视化”,那就不能只是画个饼图凑数。我设计了四个可视化模块,每一个都对应论文中的一个分析问题:

  • 房源分布地图:按区域展示房源数量、平均租金,用地图或散点图呈现,回答“房源长什么样”。
  • 用户行为概览:展示行为类型分布、每日新增行为量,回答“数据够不够支撑推荐”。
  • 推荐结果对比:展示不同算法的推荐命中率、Top-N准确率,以柱状图和折线图呈现,回答“哪个算法更有效”。
  • 单个推荐解释:针对指定用户,展示交互过的房源和推荐房源之间的相似关系图,回答“推荐的理由是什么”。

这四个维度做下来,可视化部分的内容量已经非常扎实,完全不用硬凑字数。

5.2 前端选型与交互设计

图表库用 ECharts,理由很简单:生态好、中文资料多、图表类型丰富,尤其适合地图和关系图。后端把聚合好的数据通过 Django 接口输出,前端只负责渲染。

推荐解释这个模块,我用 ECharts 的关系图(graph)展示:中心节点是用户,左边节点是用户收藏过的房源,右边节点是推荐出来的房源,连线表示相似度或者交互关系。这个图答辩演示时效果非常好,评委一眼就能理解协同过滤的推荐链路。

ECharts 的前端代码模板大概是这样的:

// 假设 api 返回图表数据 fetch('/api/v1/recommend/explain?user_id=1001') .then(res => res.json()) .then(data => { const chart = echarts.init(document.getElementById('rec-graph')); chart.setOption({ series: [{ type: 'graph', layout: 'force', data: data.nodes, links: data.links, }] }); });

需要提醒的是,force 布局在节点数超过100时会卡,展示时最好手动限制节点数量,只显示用户最近交互的5套房和推荐结果5套房。

5.3 Django 模板和静态资源部署

可视化页面可以用 Django 模板渲染,也可以做成单页 HTML + 静态接口。我用的方案是 django-echarts 结合方式:首页用 Django 模板展示房源列表和推荐结果表格,数据统计页用独立的 HTML 页面加载 ECharts,接口直接访问 Django 的 JSON API。

开发时记得在 settings.py 里配置静态文件的访问路径,不然图表库加载不出来,这一步常见但容易被忽略。

6. 实测踩坑记录:相似度计算、冷启动、评测指标那些坎

6.1 相似度矩阵的内存和计算问题

房源数量到3000这个量级时,物品相似度矩阵是 3000 x 3000,看起来不大,但如果你老老实实用两层循环去计算,速度慢得让人怀疑人生。一个实用技巧是利用 Pandas 的向量化运算,把“共现矩阵”一次性算出来,不要在 Python 循环里做两两配对。

大体思路是:先把用户行为转成 scipy 的稀疏矩阵,行为矩阵形状是(用户数, 房源数),然后物品相似度矩阵 = 行为矩阵转置乘行为矩阵,再除以范数归一化。这一步直接让计算时间从分钟级降到秒级。

from scipy.sparse import csr_matrix # user_item: 稀疏矩阵,行是用户,列是房源,值是行为权重 # 物品共现矩阵 item_cooccur = user_item.T @ user_item item_cooccur = item_cooccur.toarray()

这个矩阵的对角线是每个房源被交互的总次数,非对角线的位置 (i, j) 是房源 i 和房源 j 被同一用户交互的共现次数。像这样的实现细节,建议写进论文“算法实现”中,比贴教科书公式更能体现工作量。

6.2 冷启动怎么处理

新的用户没有行为数据,协同过滤直接失效。我的处理方案有两层:

第一层,对没有行为的新用户,返回“热门房源”策略,即按收藏总量、浏览总量排序,加上区域热度修正。

第二层,在页面上强制引导用户先选择偏好标签(预算范围、户型、是否近地铁),系统用这些标签匹配房源特征,相当于一个简单的基于内容的推荐,产生初始行为后再切回协同过滤。

答辩被问“冷启动如何处理”时,这两个方案基本能覆盖问题的标准答案。

6.3 推荐评测指标的坑

测评最容易踩的坑就是用了随机划分的数据集,导致“测试集的用户已经出现在训练集里”,这属于数据泄露。推荐系统测评推荐用时间划分,前80%时间窗口作训练,后20%作测试。

我用的指标是召回率、精确率和覆盖率。召回率 Top-N 的公式是:测试集中该用户真实交互的房源里,有多少比例出现在推荐列表里。精确率反过来算推荐列表里有多大比例被用户真实交互。覆盖率算的是推荐系统推荐过的房源占总房源的比例,衡量系统是只推头部热门还是覆盖长尾。

实操中我发现一个现象:ItemCF 的精确率不一定比热门推荐高多少,但覆盖率会好很多。这个观察很值得写进论文的“实验结果与分析”部分,因为它说明自定义的推荐算法不是花架子,确实在探索多样性上有效果。

6.4 Django + 推荐算法部署时容易翻车的细节

  • 每次启动时重新计算相似度矩阵,启动会慢,而且没有必要。建议把相似度矩阵存成 CSV,启动时加载即可,只有数据更新时才重新计算。
  • Django 的 ORM 查询在行为表数据量大的时候,默认懒加载会把性能拖垮。使用select_related或prefetch_related提前加载关联对象。
  • 接口层必须做超时保护。协同过滤服务如果遇到极端稀疏或者相似度矩阵缺失,直接返回热门推荐兜底,不要抛异常。
  • 数据库里的行为数据修改后,离线相似度矩阵需要同步更新。简单做法是每次更新数据后执行一个管理命令python manage.py rec_update,把计算结果刷新到 CSV,Web 进程重启时自动加载新文件。

7. 写在最后的一点个人经验

做过这个项目之后,我的一个强烈感受是:毕业设计选题真的不要贪大,也不要嫌小,关键是能不能形成“数据-算法-系统-评估”的完整闭环。租房推荐系统能闭环,所以它适合作为毕业设计;同样是推荐系统,如果只写一个推荐算法脚本而没有任何 Web 界面和可视化,工作量又明显偏少。两者结合刚刚好。

最后分享一个实用小技巧:给相似度矩阵和推荐结果做一份可导出的静态报告,用图片或 PDF 保存下来,放在论文附录里。答辩时如果系统现场演示出现环境问题,这份报告能直接兜底,帮你把评分保住。真实答辩环节出状况的概率比你想象的高,这份无接触的备份材料值得提前准备。

如果你也在做推荐相关系统的毕业设计,希望这篇整理能帮你少走一点弯路,也祝你的代码和论文都能顺利过关。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 6:02:42

rea概念解析:从技术缩写到工程实践的常见误读与正解

我无法根据当前输入内容生成符合要求的博文。原因如下:项目标题仅为“rea”,无明确语义,既非完整单词(如“real”“react”“realm”等常见缩写或拼写变体),也未提供上下文指向具体领域(编程、设…

作者头像 李华
网站建设 2026/10/21 6:01:25

解析雅可比矩阵验证:数值差分与自动微分实战

1. 为什么要较真:解析雅可比矩阵到底错在哪做机器人力控、运动规划、视觉伺服,或者状态估计里写扩展卡尔曼滤波的人,基本都逃不过一件事:手推雅可比矩阵。回想一下我自己第一次认真对着纸笔推一套六轴机械臂的几何雅可比&#xff…

作者头像 李华
网站建设 2026/10/21 6:01:15

本地优先的知识管理插件体系:采集、整理与检索全闭环

浏览器开了四十几页,书签栏溢出到看不见原名,笔记软件里躺着几百条“待整理”的摘录,代码片段散落在三个聊天记录里——这大概就是知识工作者的桌面常态。我折腾了大半年现成工具之后,索性按自己的习惯做了一套叫knowledge-work-p…

作者头像 李华
网站建设 2026/10/21 6:01:01

MySQL驱动坐标迁移:mysql-connector-java与mysql-connector-j切换指南

提到 MySQL 的 Java 驱动,很多老项目里躺着的是mysql:mysql-connector-java:8.0.33,而新项目或新版本驱动用到的却是com.mysql:mysql-connector-j。我第一次在依赖树里同时看到这两个坐标时,第一反应是有人把 artifactId 写错了——毕竟少了j…

作者头像 李华
网站建设 2026/10/21 6:00:56

Agent-Skills 工业级落地:契约驱动、WASM沙箱与可编排智能体

1. 项目概述:Agent-Skills 不是新玩具,而是智能体落地的“肌肉记忆” 最近在几个技术社区和内部项目复盘会上,“agent-skills”这个词高频出现,但很多人一听到就下意识联想到“大模型调用封装”或者“工具函数集合”,…

作者头像 李华