
1. 项目概述与选题价值1.1 这个毕设到底解决什么问题我第一次看到“基于Python协同过滤算法的天气穿搭推荐系统”这个题目时第一反应是这不就是把天气数据和推荐算法拼在一起吗等真正做下来才发现这个选题的精妙之处就在于它完美踩中了毕业设计的三个核心诉求——有算法深度、有完整业务闭环、有可视化展示。这个系统做的事很简单用户输入一个城市系统根据实时天气情况温度、湿度、风力、天气状况结合用户的历史穿搭偏好和相似人群的穿搭选择推荐出最适合今天的衣服组合。它不像市面上那些“天气APP自带的穿衣指数”那样一刀切——温度低就让你穿厚点而是真正做到了“千人千面”的个性化推荐。对于准备做毕设的同学来说这个项目的技术栈覆盖面非常让人舒服Python作为主力语言、爬虫或API获取天气数据、协同过滤算法做推荐、Flask搭Web界面、MySQL存数据面试时能聊的点非常集中。就算毕业后不搞推荐算法方向这段经历也能证明你完整走过“数据采集→数据清洗→特征构建→算法建模→系统实现”的全流程。1.2 需要具备哪些基础我接触过不少拿这个题目做毕设的同学最后做崩的基本都是因为基础没打牢就急着写码。这里我先给个自测清单Python基础至少能熟练使用字典、列表、文件读写能用pandas做简单的DataFrame操作不用精通但得能看懂别人写的代码并改得动。数据库基础懂SQL的增删改查知道什么是主键、外键、多表关联。系统里用户表、穿搭表、评分表是三个核心表这些表的关联逻辑搞不清楚后面必乱。推荐算法认知不要求你手推公式但必须理解协同过滤的核心思想——“物以类聚人以群分”知道基于用户和基于物品两种协同过滤的区别。前端基础这个其实要求最低用现成的Bootstrap模板或简单HTML页面就够了真正的工作量在Flask的模板渲染和后端数据处理上。如果你已经具备这些基础接下来的内容会帮你避开大部分坑。如果还没全掌握建议先把pandas和Flask快速过一遍不需要精通够用就行。1.3 我的整体评价帮别人调试这个项目时我最大的感触是它的难点不在于某个单独的技术点而在于如何把天气数据和推荐算法有机结合起来。很多人做出来的东西只是个简单的“if温度10度推荐棉袄”那根本用不上协同过滤纯属自欺欺人。真正的关键在于天气数据作为“过滤条件”协同过滤作为“排序引擎”两者分工合作让推荐结果在符合天气前提的同时又贴合个人审美偏好——这条主线想清楚系统架构不会跑偏论文也能有真正的核心章节可写。2. 系统架构与核心设计思路2.1 整体架构选型先聊技术选型。我自己做毕设辅导时给出的推荐组合是后端框架Flask。有人喜欢用Django但我更推荐Flask——轻量、灵活、上手快把RESTful API写好后前端页面直接用Jinja2模板渲染就能跑通。毕设答辩时老师问起框架原理Flask这点东西半小时能讲透Django的中间件、ORM、Admin后台反而容易把自己绕晕。数据库MySQL。虽然SQLite更轻量但简历上写“熟练使用MySQL”和“用过SQLite”是完全两个效果。而且课题给的源码文档里基本都配了MySQL保持同环境习惯最后导数据和答辩演示都方便。如果你的本机没装MySQL直接用Navicat或Docker起一个也行记住导出SQL文件时要带上结构。天气数据来源公开天气API。我推荐用和风天气的免费开发者接口注册后能拿到Key按城市代码请求JSON数据包括当前温度、相对湿度、风速、天气现象。免费版完全够毕设演示用了。如果API本身挂了那就用准备好的历史天气JSON做降级兜底保证系统不崩。前端展示Bootstrap模板 ECharts。ECharts用来画温度趋势、风格分布这些图表是答辩时的亮点。不建议自己手写CSS浪费时间且不好看。2.2 核心设计难点天气数据如何参与协同过滤这是整个系统的灵魂环节。我见过好几份开源的项目源码都存在一个普遍问题把天气标签硬编码成几个规则比如“温度低于5度就推荐羽绒服”然后协同过滤只管“猜你喜欢”天气和算法完全脱钩。我的做法是把天气信息转换为“场景化特征向量”让算法在算相似度时能感知天气带来的影响。具体拆解成三层第一层是天气特征归一化。把温度、湿度、风力映射到合适的区间。比如人体舒适温区是18-25度那我把最高温和最低温按“距舒适温区的偏差”做归一化。这样比直接用“30度”“5度”这种绝对数字要合理得多。第二层是穿搭标签的向量化映射。每件衣服入库时给它打上适用天气标签比如“短袖T恤”是“高温/炎热”“风衣”是“微风/凉爽”“羽绒服”是“严寒/强风”。这些标签不是拍脑袋定的而是从穿衣指数分级对照表里提炼的。第三层才是协同过滤。系统先根据当前天气筛出符合条件的所有穿搭物品形成候选集再用协同过滤算相似度对候选集里的物品做个性化排序。换句话说天气负责圈定范围协同过滤负责在范围内做精细排序。这样既不会推荐出“大冬天穿短袖”的离谱结果又能照顾到“同样20度有人觉得穿卫衣就行有人觉得得加件薄外套”的个人差异。2.3 用户冷启动问题的折中方案推荐系统里有个绕不开的痛点——新用户没有任何历史交互数据协同过滤根本无从算起。毕设系统里用户量本来就少这个问题会被放大得更明显。我采用的折中方案是新用户注册时强制勾选“体感偏好”比如“怕冷”“怕热”“正常体感”外加“偏好的穿搭风格”多选项。这样系统把从“体感偏好”映射的规则分数作为基础推荐分数随着用户逐渐产生浏览和收藏行为再逐步过渡到协同过滤结果上。这个设计对毕设来说有着双重的价值提升了真实使用体验论文里也多了一个可以展开写的点——冷启动问题的分析与解决。答辩时老师很爱问这个能把这块讲清楚拿分点就有了。3. 数据库设计与算法实操拆解3.1 三张核心表的设计思路推荐系统的数据层逃不开三张表用户表、物品表、交互表。我们这个项目里具体化为以下设计用户表CREATE TABLE user ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(255) NOT NULL, hot_tolerance TINYINT DEFAULT 1 COMMENT 体感偏好0怕冷1正常2怕热, style_pref VARCHAR(255) COMMENT 风格偏好多选用逗号分隔, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );穿搭物品表CREATE TABLE outfit ( outfit_id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, category VARCHAR(20) COMMENT 上衣/下装/外套/鞋履, style VARCHAR(20) COMMENT 简约/运动/通勤/甜美/休闲, min_temp FLOAT COMMENT 适用最低温度, max_temp FLOAT COMMENT 适用最高温度, weather_tag VARCHAR(50) COMMENT 晴/雨/风/雪/阴, image_url VARCHAR(255), description TEXT );交互评分表CREATE TABLE ratings ( rating_id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, outfit_id INT NOT NULL, score TINYINT DEFAULT 0 COMMENT 评分1-5浏览3收藏4直接使用5, weather_tmp FLOAT COMMENT 当时的温度, weather_code VARCHAR(20) COMMENT 当时的天气编码, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES user(user_id), FOREIGN KEY (outfit_id) REFERENCES outfit(outfit_id) );这里我特别想强调ratings表里的weather_tmp和weather_code两个字段。这是我踩坑后加上的——协同过滤算法评分时不应该只看“人和衣服”的关系还得看“这种关系发生在什么天气条件下”。比如有用户25度时给某件衬衫打了5分系统不该把这个高分直接套用到15度的天气里。加上天气字段后做条件过滤推荐合理性会有质的提升。3.2 协同过滤算法的落地实现我们选的是基于物品的协同过滤ItemCF核心思想是如果用户A喜欢衣服X而衣服X和衣服Y在大量用户的行为里表现出高度相似被同一批人喜欢那当用户A面对衣服X和衣服Y时系统会优先把Y也推荐给他。套用经典公式物品间相似度的计算用余弦相似度sim(i,j) |N(i) ∩ N(j)| / sqrt(|N(i)| * |N(j)|)Pieter(i)和Pieter(j)分别代表对衣物品i和物品j有过行为如评分、收藏的用户集合。这个公式计算的是同时喜欢i和j的人数占喜欢i和j总人数的比例。数字越接近1说明这两个物品的受众越重叠相似度越高。Python实现的核心代码很简洁import pandas as pd from sklearn.metrics.pairwise import cosine_similarity # 构造用户-物品评分矩阵 ratings_df pd.read_sql(SELECT user_id, outfit_id, score FROM ratings, conn) matrix ratings_df.pivot_table(indexuser_id, columnsoutfit_id, valuesscore).fillna(0) # 计算物品间相似度矩阵 item_sim cosine_similarity(matrix.T) item_sim_df pd.DataFrame(item_sim, indexmatrix.columns, columnsmatrix.columns) def recommend_outfits(user_id, top_n5): user_rated matrix.loc[user_id] # 找到用户已评分的物品 rated_items user_rated[user_rated 0].index # 初始化推荐分数 scores {} for item in rated_items: # 和当前物品相似的其他物品 sim_items item_sim_df[item].sort_values(ascendingFalse)[1:10] for sim_item, sim_score in sim_items.items(): if sim_item in rated_items: continue scores[sim_item] scores.get(sim_item, 0) sim_score * user_rated[item] # 按分数排序返回 sorted_items sorted(scores.items(), keylambda x: x[1], reverseTrue) return [item for item, _ in sorted_items[:top_n]]这段代码的逻辑是遍历用户所有已评分物品找到和这些物品最像的其他物品然后用“相似度乘以用户对原物品的评分”累加得到候选物总分。用户给某件衣服打了5分系统就认为他喜欢和这件衣服相似的其他衣服且原评分越高这种信任转移的权重就越大。3.3 天气过滤与算法候选集的融合方式协同过滤算完之后不能直接把结果抛给用户。正确的流程是获取当前城市天气解析出温度temp、天气现象code、湿度humidity、风速windDir。用规则引擎初筛outfit.min_temp temp outfit.max_temp再把天气标签weather_tag对应上。比如下雨天候选集里必须保留“防雨”标签的衣物。协同过滤只对初筛后的候选集算分排序。这一步非常关键——如果先算协同过滤再过滤天气条件很可能协同过滤出来的推荐结果刚好被天气规则全部过滤掉最后系统什么都推荐不出来。那场面在答辩演示时说实话挺尴尬的。正确的做法是先粗筛后精排这也是我这个项目在答辩时最常讲的优化点老师对此很认可。用代码表达式就是weather_filtered outfit_df[(outfit_df[min_temp] current_temp) (outfit_df[max_temp] current_temp) (outfit_df[weather_tag].str.contains(current_condition))] final_recommend itemcf_recommend(weather_filtered, user_id)3.4 初始化数据让协同过滤“有东西可算”初用系统时一个最大的“坑”是把ratings表做成了空的测试协同过滤只能得到“无结果”。后来我学乖了用脚本批量造数据import random # 构造模拟评分数据 for user_id in range(1, 11): for outfit_id in range(1, 31): if random.random() 0.4: rating random.randint(3, 5) # 插入评分数据 cursor.execute(INSERT INTO ratings (user_id, outfit_id, score) VALUES (%s, %s, %s), (user_id, outfit_id, rating))构造了至少几百条评分记录推荐结果才稳定。而且我建议评分分布的“形状”要贴合实际——夏天的衣服在夏天数据里评分高冬天的衣服在冬天评分高它们并存协同过滤才能有季节区分的“直觉”。我还编了一些“用户画像”进入init_data.py比如“用户A”高温天爱穿清凉通勤装、下雨天爱穿防潮外套“用户B”是运动风格重度用户。种子数据越有倾向性后面的个性化推荐就越有区分度演示起来也更有说服力。4. 实操过程与关键环节实现4.1 整体开发流程概览我在实际动手时把开发流程切成了六大步骤每一步有明确的输出物。照这个顺序做基本不会乱环境准备装Python 3.9建虚拟环境安装flask、pandas、sklearn、requests、pymysql。创建数据库按上面的表结构创建三个表写入初始数据。对接天气API先用Postman测试接口确认返回字段再封一个weather_util.py。实现协同过滤算法模块写一个recommend.py封装上面的核心代码。开发Flask Web项目路由分4个首页/今日推荐/历史记录/用户偏好设置。联调与美化把前端Bootstrap模板套上ECharts图表插入测试整条链路。4.2 天气API对接的细节去和风天气的开放平台注册一个免费账号创建一个应用拿到API Key后核心请求就一个import requests def fetch_weather(city): # city传城市代码比如101010100代表北京 url ( fhttps://devapi.qweather.com/v7/weather/now? flocation{city}key你的API_KEY ) resp requests.get(url) if resp.status_code 200: data resp.json()[now] return { temp: float(data[temp]), text: data[text], # 如晴多云小雨 humidity: float(data[humidity]), windDir: data[windDir], windScale: data[windScale] } return None注意免费开发者配额每天只有一定次数写代码时一定要加缓存。我用了个简单技巧——以“城市当天日期”做key每天第一次请求时存到本地JSON文件后续直接读缓存既不浪费配额也提高响应速度。4.3 Flask路由与渲染的关键代码核心路由不复杂重点是逻辑要清晰from flask import Flask, render_template, request, redirect, url_for from recommend import get_recommendations app Flask(__name__) app.route(/) def index(): return render_template(index.html) app.route(/recommend/int:user_id) def recommend(user_id): city request.args.get(city, 101010100) weather fetch_weather(city) if not weather: weather {temp: 20, text: 晴, humidity: 50, windDir: 东南风, windScale: 2} recs get_recommendations(user_id, weather) return render_template(recommend.html, weatherweather, outfitsrecs, user_iduser_id) app.route(/preference/int:user_id, methods[GET, POST]) def preference(user_id): if request.method POST: hot_tolerance request.form.get(hot_tolerance) style_pref request.form.get(style_pref) # 更新数据库里的用户偏好 update_preference(user_id, hot_tolerance, style_pref) return redirect(url_for(recommend, user_iduser_id)) return render_template(preference.html, user_iduser_id) if __name__ __main__: app.run(host0.0.0.0, port5000, debugTrue)4.4 ECharts图表展示技巧为了让答辩时界面看起来有说服力我在“个人中心”页放了两个图表一是近7天穿搭风格分布柱状图统计用户当前天气推荐里出现最多的风格标签比如“简约风占40%”“通勤风占25%”。这样评委一眼就能看出系统确实在学习用户偏好。二是候选集分数Top10条形图展示推荐物品在推荐排序中的计算分数。ECharts的配置不算复杂只需准备好json数据就能渲染var chartData {{ chart_data | safe }}; var myChart echarts.init(document.getElementById(chart)); var option { xAxis: { type: category, data: chartData.names }, yAxis: { type: value }, series: [{ type: bar, data: chartData.scores }] }; myChart.setOption(option);这里的chart_data是从Flask视图函数通过json.dumps传过来的。前端模板里用| safe就是为了防止Jinja2转义把JSON搞坏掉键值结构。5. 开发过程中的高频报错与排查要点5.1 相似度矩阵稀疏全零这是我调试时看到过最多的问题运行协同过滤时matrix里全是0导致相似度计算毫无意义。原因往往是ratings表里要么没数据要么数据太过稀疏比如每个用户只有一两条评分。解决办法很简单先检查一下数据规模SELECT COUNT(*) FROM ratings; SELECT COUNT(DISTINCT user_id) FROM ratings; SELECT COUNT(DISTINCT outfit_id) FROM ratings;如果ratings少于50条记录就别指望协同过滤能算出好东西。宁可在初始化脚本里多造一些带倾向性的数据。另外在计算cosine_similarity之前要对矩阵做一下非零检查。5.2 天气筛选后候选集为空用6月的数据跑冬季场景初筛后候选集可能就空了。但这其实不是“bug”而是数据分布问题。两个解决办法第一初始化衣物数据时保证覆盖各种温度段和天气场景至少每个温度段裹七八件单品从短袖短裤到羽绒服都有。第二在代码里做降级候选集若为空就把温度条件放宽到相邻几度。比如if weather_filtered.empty: weather_filtered outfit_df[(outfit_df[min_temp] current_temp 5) (outfit_df[max_temp] current_temp - 5)]5.3 中文乱码问题又是老生常谈但实在重要MySQL建表时字符集必须是utf8mb4连接加charsetutf8mb4PyMySQL连接时也要显式传入conn pymysql.connect( hostlocalhost, userroot, passwordyourpass, databaserecommend_db, charsetutf8mb4 )文本显示乱码时优先检查数据库字符集别急着改代码。以及JSON返回时中文字符不乱码记得在Flask里设置app.config[JSON_AS_ASCII] False。5.4 协同过滤算出来的结果没有区分度如果每个用户收到的推荐结果是同一批衣服十之八九是因为用户间的评分模式太一致了。我在初始化数据时就刻意加入了随机偏移同一个物品对于不同用户的评分并非全同而是围绕5、4、3等档位有小幅波动。这样用户相似度才拉开差距。如果你用的公开源码里数据是同一个模板复制的那就会出现这个问题。5.5 推荐结果重复协同过滤返回Top5时如果相似物品列表里反复出现同一件衣服或者相似度矩阵里的对角元素自己和自己的相似度被重复累加就会出现推荐里全是同一件衣服的情况。处理方式在聚合分数时加个if sim_item not in candidate_scores检测或者返回前用set去重。另外一提到截取相似物品列表时记得跳过自己[1:10]而不是[:10]。第0个item就是自己这是新手最容易犯的错误。6. 答辩演示与项目经验总结6.1 演示脚本该怎么设计答辩时的现场演示别打无准备之仗。我建议把演示流程固定成五步走画漫画式开场用一张架构图讲清楚用户、天气API、推荐算法、数据库之间的关系。造一个用户现场注册一个新用户选好体感偏好为“怕冷”风格偏好“通勤”。选一个冷天气城市比如哈尔滨展示系统推荐结果包含羽绒服和厚外套同时展示用户A怕冷程度如何影响排序。切换一个温暖的城市比如三亚让系统推荐短袖衬衫、薄裙展示天气筛选生效。对比另一个用户选一个“偏好运动风”的用户登录同一城市展示两屏推荐结果的差异。这个对比是整个演示的高光时刻“个性化”三个字不需要多说。6.2 老师爱问的几个问题提前准备这几个高频答辩问题现场就不慌为什么用基于物品的协同过滤而不用基于用户衣物数量远小于用户数量且物品相似度相对稳定不用每次登录重新计算整个用户相似矩阵实时性更好。冷启动怎么解决新用户没有行为数据时用个人偏好设置体感、风格生成初始推荐积累到一定评分数据后再切协同过滤。天气数据对推荐结果的影响如何体现天气作为硬过滤条件先圈出候选集再进入协同过滤排序所以任何推荐结果都物理符合当天天气这是和纯推荐算法的根本区别。协同过滤的缺点有哪些方案是什么稀疏性问题和冷启动是两大短板我通过种子数据填充和偏好设置兜底双向处理。6.3 论文的写作支点这份毕设写论文时四个章节取舍很关键需求分析章重点写天气场景下的用户痛点以及现有“一刀切”穿衣指数的问题。系统设计章以三张表和“筛选-排序”两阶段框架为核心画好架构图。算法实现章写出相似度公式和Python伪代码结合具体天气案例演算。系统测试章分别测试规则筛选、协同过滤准确率以及不同天气下的推荐比较。6.4 关于“远程调试讲解定制”这类关键词的提醒现在市面上很多毕设源码带远程调试和定制服务用得好能省时间但也有些坑一是远程调试时尽量问对方要完整的SQL文件否则连数据库都要一个个手敲二是“定制”内容得提前把需求列成清单别模糊地说“帮我改一下”三是拿到代码先自己跑环境中走一遍。我见过太多同学拿到一套源码连Flask配置都没改就跑不起来最后疯狂卖家。其实多数跑不起来的原因都在虚拟环境依赖版本上先执行pip install -r requirements.txt也不一定够有时需要手动装某个特定版本的包。7. 写在最后的心得这个项目我调试了好几轮有一个体会想分享毕设的意义不在于做出一个性能碾压大厂的推荐系统那本来也不现实真正有价值的是你能把一个有应用场景的问题用一套完整的技术方案论证清楚并诚实地说出它的局限性和可优化的方向。如果后续你想在这个系统上继续扩展我建议的方向是把协同过滤的相似度计算换成“矩阵分解”或引入隐语义模型LFM来做特征提取再要么就是加入“衣柜管理”功能让用户先“拥有”衣服系统只从已有衣橱里做搭配推荐。这一下子就从“推荐单品”进化到了“推荐穿搭方案”实用性强了一大截。最后再分享一个实际调试的小窍门跑Flask程序时别开着Debug模式去演示万一报错页面弹出一堆红色堆栈信息答辩观感会非常差。预先把可能出现的异常都try-except兜住给用户展示友好的提示页面系统“稳不稳”在演示时比“酷不酷”重要得多。无论你是拿着这套源码二次开发还是准备完全自己写先把上面的表结构建好再跑通一个最小链路城市→天气→初筛→协同过滤→结果展示后面的所有功能都是在这条链路上做深做细。祝你的毕设顺利答辩稳过。