ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Python的服饰推荐系统:从特征提取到混合推荐实战

基于Python的服饰推荐系统:从特征提取到混合推荐实战 简介这是基于Python开发的一套服饰推荐系统项目包含完整源码与项目文档面向计算机专业学生和开发者适用于毕业设计或课程设计也可用于小型项目开发参考。整体按前端应用、服务端、脚本、图像数据集四个模块划分目录结构清晰能从数据整理、服务端处理一直看到前端展示的完整实现源码已通过测试可放心在此基础上扩展功能。压缩包共两千个文件总大小约两百二十四兆其中一千八百余张服饰图片构成主要数据集代码部分以Python为主负责服务端逻辑前端以JS/Vue相关文件搭建页面CSV/JSON/XML等数据文件支撑业务。附带的项目文档对目录结构、运行方式和扩展思路做了说明方便快速上手并可从源码与数据整理方式中获得迁移思路。目前已有七十九人学习下载适合需要一套可运行的服饰推荐系统参考并继续完善的人。1. 服饰推荐系统先想清楚你要做的是哪一类推荐拿到“基于Python实现的服饰推荐系统”这个标题很多人第一反应是去网上抄一个商城前端再做几个登录注册最后套一个“猜你喜欢”接口。但真正让课程设计和毕业设计有区分度的是推荐链路本身。服饰推荐系统本质是让程序根据用户的浏览、收藏、购买记录从服饰池中计算并输出一批排序后的商品。它适合两类人一类是需要完成毕设或课设的学生另一类是只想快速搭一个推荐demo看看效果的开发者。这个方向最值钱的部分不是前端也不是数据库而是数据特征怎么提取、算法怎么选、参数怎么调、结果怎么评估。把这些链路跑通项目文档自然就有内容可写。2. 数据与特征服饰推荐的地基先把图片和用户行为变成向量2.1 服饰数据集怎么选公开数据集与自建规则的取舍做服饰推荐系统第一步不是写代码而是选数据。常见的公开数据集有四个各有各的脾气。数据集内容格式适合场景Fashion-MNIST10类服饰灰度图28x28图片标签快速验证算法流程课设首选DeepFashion大规模时尚图片含属性标注图片json标注毕设想做深度特征时使用Kaggle Clothing Dataset多类服饰彩色图图片类别CSV推荐分类结合的项目电商爬虫数据自己抓取的图片和价格自定义有合规前提需注明来源Fashion-MNIST虽然只有10个类别但胜在轻量好读用来跑通推荐流程非常合适。DeepFashion更接近真实场景但下载慢、标注复杂第一次做很容易卡在数据解析上。课程设计阶段的建议是先用Fashion-MNIST把算法链路跑通再换一部分彩色服饰图做结果展示。如果你打算用python爬虫自建数据集务必要遵守目标网站的服务条款图片版权和robots协议要提前确认文档里写明数据来源否则答辩时容易被动。2.2 图片特征提取的最小实现让Python看懂衣服推荐系统不能直接把图片塞进相似度计算一般先转成特征向量。用预训练ResNet18做特征提取是最省力的做法不需要自己训练模型直接复用ImageNet上学习到的通用视觉知识。import torch import torchvision.transforms as T from torchvision import models from PIL import Image # 加载预训练 ResNet18去掉最后的全连接层 model models.resnet18(weightsmodels.ResNet18_Weights.DEFAULT) model.fc torch.nn.Identity() # 输出 512 维特征向量 model.eval() # 图像预处理统一尺寸、转张量、按 ImageNet 均值方差归一化 transform T.Compose([ T.Resize((224, 224)), T.ToTensor(), T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) def image_to_vector(path): img Image.open(path).convert(RGB) x transform(img).unsqueeze(0) # 增加 batch 维度 with torch.no_grad(): vec model(x).squeeze(0) # 去掉 batch 维度 return vec.numpy()逻辑说明model.fc torch.nn.Identity()是把ResNet最后的分类头摘掉直接拿倒数第二层的512维输出作为图片特征。这样每张图都被压缩成一个固定长度的向量后续计算余弦相似度时才有数学基础。参数说明Resize((224, 224))是ResNet的标准输入尺寸不能随意改归一化用的均值方差是ImageNet的统计值换成自己的数据集且又没重新统计时别动这两组数。如果机器没有GPU把图片总量控制在几千张以内CPU跑几分钟也能完成顶多慢一点。提取完的特征建议用np.save(features/item_vectors.npy, vectors)保存成文件后面每次启动直接加载省得反复提取。2.3 把用户行为整理成评分矩阵服饰推荐除了图片特征还需要用户行为数据。常见的公开数据集不一定自带交互记录这时候需要自己造一份模拟行为表。行为转评分的常见做法是加权浏览1收藏3加购5购买8。import pandas as pd # 假设原始行为表有 user_id, item_id, action, time df pd.read_csv(user_behavior.csv) weight {view: 1, favorite: 3, cart: 5, buy: 8} df[score] df[action].map(weight) # 同一 user-item 多次行为取最大值避免重复行为叠加 rating df.groupby([user_id, item_id], as_indexFalse)[score].max() # 转成用户-物品矩阵空位填0 rating_matrix rating.pivot(indexuser_id, columnsitem_id, valuesscore).fillna(0) print(rating_matrix.shape)逻辑说明action列先用字典映射成数值分再按user_id, item_id分组取最大值。这样处理是为了防止用户反复浏览同一件衣服导致分数虚高。pivot后生成的矩阵行是用户、列是物品空位填0。参数说明权重表可以根据自己的业务调整但文档里一定要写清楚为什么加购比浏览分值高。给答辩老师的解释是加购意味着更强的购买意图。如果换成另一种权重比如购买10、收藏2推荐结果会有变化你可以把几组权重跑出来对比写进测试报告里会显得很扎实。还有一点要提醒模拟数据在文档里必须明确标注“experimental data”不能让它和真实数据混在一起。3. 核心算法选型基于内容、协同过滤还是混合推荐3.1 基于内容的推荐用标签向量算相似度基于内容的推荐最适合“图片特征已经提取好”的场景。核心思路是对每个用户收集TA曾经表现过偏好的物品特征向量取平均得到用户画像向量再算画像向量与所有物品向量的余弦相似度取TopN。import numpy as np from sklearn.metrics.pairwise import cosine_similarity # item_vectors: 形状为 (n_items, dim)向量已经归一化 # item_ids: 与 item_vectors 行索引对应的物品id列表 def recommend_by_content(item_vectors, item_ids, user_history, top_n10): profile np.mean(item_vectors[user_history], axis0).reshape(1, -1) sim cosine_similarity(profile, item_vectors).ravel() # 排除已买、已看的物品 exclude set(user_history) rank np.argsort(sim)[::-1] result [item_ids[i] for i in rank if item_ids[i] not in exclude][:top_n] return result逻辑说明np.mean在用户历史物品向量上取平均产生一个能代表用户审美的“伪物品”cosine_similarity计算该画像与所有物品的夹角夹角越小越相似argsort按相似度从高到低排列。参数说明user_history建议只取收藏和加购过的物品不要混入浏览记录因为误点击的噪音太大取了之后推荐结果会明显变飘。top_n在课设演示里取5到10比较合适如果取50列表尾部会出现大量长尾物品看起来反而不像推荐系统。基于内容有个天然缺陷新物品没有用户行为也能被推荐因为只依赖视觉特征但它也很难给用户带来惊喜因为推荐的都是“和你以前喜欢的东西长得像”的衣服。所以很多项目会把基于内容作为混合推荐的一端再接入协同过滤。3.2 协同过滤用户-物品矩阵与相似度计算协同过滤有UserCF和ItemCF两种。服饰推荐场景我更推荐ItemCF原因是服饰数据中用户行为非常稀疏两个用户的共同点击列表可能少得可怜算出来的用户相似度噪声大而物品之间的共现关系相对稳定算出来的相似度更可信。下面是ItemCF的最小实现。from sklearn.metrics.pairwise import cosine_similarity def compute_item_similarity(rating_matrix): sim cosine_similarity(rating_matrix.T) # 转置后每一行是一个物品 np.fill_diagonal(sim, 0) # 自己与自己的相似度置0 return sim def recommend_by_itemcf(rating_matrix, user_id, top_n10): user_ratings rating_matrix.loc[user_id] sim compute_item_similarity(rating_matrix) item_index {item: i for i, item in enumerate(rating_matrix.columns)} scored {} for item, score in user_ratings.items(): if score 0: continue item_idx item_index[item] # 找到与该物品相似且用户没有交互过的物品 for sim_item, sim_val in enumerate(sim[item_idx]): if sim_val 0.3 and rating_matrix.loc[user_id, rating_matrix.columns[sim_item]] 0: scored[sim_item] scored.get(sim_item, 0) sim_val * score sorted_list sorted(scored.items(), keylambda x: x[1], reverseTrue)[:top_n] return [rating_matrix.columns[i] for i, _ in sorted_list]逻辑说明rating_matrix.T把矩阵转置成“物品×用户”这样cosine_similarity返回的每一行就是当前物品与其他物品的相似度。外层循环遍历用户已交互物品内层循环遍历与该物品相似的候选物品累加相似度×用户评分作为候选物品的分数。参数说明sim_val 0.3是过滤阈值低于0.3的相似度被认为没有推荐价值你不妨试试0.1和0.5观察候选池数量变化。如果评分矩阵极稀疏很多相似度算出来是0推荐列表容易空这时需要在混合推荐里加入热门兜底。3.3 混合推荐加权融合与冷启动兜底冷启动是服饰推荐系统最常见的翻车点新用户没有行为新商品没有评分。单靠协同过滤直接返回空列表答辩现场就会很难看。常见做法是做一个独立的热门池并在内容推荐和协同过滤之间做加权融合。def hybrid_recommend(content_result, itemcf_result, hot_result, alpha0.6, top_n10): merged {} # alpha 是内容推荐权重1-alpha 是协同过滤权重 for pos, item in enumerate(content_result): merged[item] merged.get(item, 0) (1 - pos * 0.01) * alpha for pos, item in enumerate(itemcf_result): merged[item] merged.get(item, 0) (1 - pos * 0.01) * (1 - alpha) # 冷启动补位保证列表长度 for item in hot_result: if item not in merged: merged[item] 0.01 if len(merged) top_n: break return sorted(merged, keymerged.get, reverseTrue)[:top_n]逻辑说明pos是候选在单个算法结果里的位置位置越靠前权重越大避免简单按照score相除抹掉排序信息alpha0.6表示更信任基于内容的相似度因为服饰推荐里用户偏好主要体现在款式、颜色上视觉相似度往往比行为共现更直观。参数说明如果数据集很大、用户行为记录密集可以把alpha降到0.4让协同过滤主导如果要演示冷启动用户参数不用改直接让content_result和itemcf_result都为空最终列表会全部来自hot_result。这个0.01是补位分数不是核心参数答辩时不需要展开。这里有一个调参技巧把alpha按0.1步长从0.1扫到0.9分别计算推荐结果在测试集上的命中率画一条折线。这一步可以放到项目文档的“实验与调参”章节它会让整个项目从“做了个功能”升级成“做了个实验”。4. 跑通最小系统源码结构、关键代码与项目文档4.1 项目目录怎么组织让你的源码像一份工程很多课设源码是单文件几百行堆在一起运行靠改文件路径这是最要命的问题。我一般建议按下面的结构组织答辩时老师一眼能看到工程思维。fashion_recommend/ ├── data/ │ ├── raw/ # 原始图片和行为表 │ └── processed/ # 清洗后的CSV ├── features/ # 提取好的特征向量和评分矩阵 ├── models/ # 相似度矩阵、热门池 ├── src/ │ ├── data_preprocess.py │ ├── feature_extract.py │ ├── recommend_content.py │ ├── recommend_itemcf.py │ ├── hybrid.py │ └── evaluate.py ├── docs/ │ ├── 需求分析.md │ ├── 架构设计.md │ ├── 测试报告.md │ └── 使用说明.md ├── main.py # 一键运行入口 └── requirements.txt逻辑说明data、features、models、src、docs五个目录分别装原始数据、中间特征、结果模型、源代码和项目文档。好处是跑实验时只要改features目录里的文件不会污染原始数据。main.py是唯一的入口不要在多个分散脚本里各自调用推荐函数。requirements.txt必须用pip freeze requirements.txt生成明确固定版本否则老师换一台电脑后import torch报错第一印象就差了。参数说明目录名不要出现final_v2、test3这种命名源码压缩包解压之后所有路径都要能用相对路径工作。4.2 推荐主流程代码逐段拆解下面是一个能直接跑的main.py骨架重点看数据怎么在模块之间流转不要把所有逻辑都堆在一个文件里。import argparse import numpy as np from src.feature_extract import load_features from src.recommend_content import recommend_by_content from src.recommend_itemcf import recommend_by_itemcf from src.hybrid import hybrid_recommend def get_user_history(rating_matrix, user_id): row rating_matrix.loc[user_id] return list(row[row 0].index) def load_hot_items(top_n): hot np.load(features/hot_items.npy, allow_pickleTrue) return list(hot)[:top_n] def run(user_id, top_n10): item_vectors, item_ids load_features(features/item_vectors.npy) rating_matrix load_rating_matrix(data/processed/rating_matrix.csv) user_history get_user_history(rating_matrix, user_id) content_result recommend_by_content(item_vectors, item_ids, user_history, top_n) itemcf_result recommend_by_itemcf(rating_matrix, user_id, top_n) hot_result load_hot_items(top_n) final hybrid_recommend(content_result, itemcf_result, hot_result, alpha0.6, top_ntop_n) return final if __name__ __main__: parser argparse.ArgumentParser() parser.add_argument(--user_id, typeint, requiredTrue) parser.add_argument(--top_n, typeint, default10) args parser.parse_args() print(run(args.user_id, args.top_n))逻辑说明get_user_history从评分矩阵里取出该用户得分大于0的物品作为“历史偏好”。content_result和itemcf_result分别生成两路候选hybrid_recommend做融合hot_result兜底。参数说明--user_id是必填参数答辩演示时可以先给两个不同的用户ID展示推荐列表差异--top_n默认10需要调参时直接命令行传入。注意如果一个用户完全没有任何行为content_result和itemcf_result都会是空列表这时hybrid_recommend里的热门兜底会自动补位效果看起来不会太差。4.3 项目文档要写哪些内容从需求说明到测试报告标题里带了“项目文档”很多同学只在最后贴一个“个人总结”这是扣分重灾区。一份能扛住答辩的文档最少需要四块需求分析、架构设计、测试报告、使用说明。文档章节核心内容篇幅建议需求分析用户角色、核心功能、数据来源说明2-3页架构设计数据层、算法层、应用层三层图2页测试报告测试环境、指标表、不同用户推荐列表截图3-4页使用说明环境安装、数据集路径、运行命令1-2页需求分析不要写“本项目旨在提高用户体验”这种空话直接写系统输入是用户的浏览/收藏/加购记录输出是TopN服饰列表附带相似理由。架构设计画一张简单的三层图数据层是CSV和npy文件算法层是三个推荐模块应用层是命令行入口这张图用draw.io画20分钟就能搞定。测试报告的重点是给出离线评估指标比如Precision10是0.32、Recall10是0.18并附上计算脚本这比贴十张截图都管用。5. 避坑服饰推荐系统开发中常见的5个翻车点5.1 推荐结果永远都是热门款换个用户结果雷同现象无论给哪个user_id传参推荐列表都是那几件销量最高的T恤项目看起来像没做算法。原因用户行为数据太稀疏相似度矩阵被热门物品主导协同过滤在计算候选分数时热门物品的相似度累加值天然大于长尾物品相当于热门物品“赢在起跑线上”。解决在计算物品相似度时做热门惩罚最直接的办法是引入IDF式的降权。对每个物品统计其被交互过的用户数n_i在相似度累加时乘以log(总用户数 / n_i)这样热门物品的相似度贡献会被压低。另一种更简单的做法是在recommend_by_itemcf的候选累加步骤里把sim_val * score改成sim_val * score * idf[item]代码如下。import math item_popularity rating_matrix.astype(bool).sum(axis0) # 每个物品被多少人交互 total_users len(rating_matrix) idf {item: math.log(total_users / (1 item_popularity[item])) for item in rating_matrix.columns}逻辑说明astype(bool).sum(axis0)统计每列非零个数即每个物品被多少用户交互过idf字典给热门物品一个较小的权重。参数说明分母加1是防止出现“所有用户都交互过这件衣服”导致除零。这个技巧也可以写进测试报告作为对比实验展示加惩罚前后推荐列表的多样性变化。5.2 提取图片特征时内存溢出或速度极慢现象跑image_to_vector时程序直接内存报错或者几千张图片要跑一晚上。原因一次性把所有图片读进list再批量提取PIL解压后的RGB数组全堆在内存里还有一种情况是读取了带透明通道的PNG或灰度图导致input_channels不匹配模型直接崩溃。解决用流式提取一张一张读、一张一张提提完立刻np.save追加或者收集到固定大小数组。读取时强制convert(RGB)避免通道数不统一。我这里给出一个稳妥的循环写法。import numpy as np from pathlib import Path feature_list [] for img_path in sorted(Path(data/raw/images).iterdir()): vec image_to_vector(str(img_path)) feature_list.append(vec) if len(feature_list) % 500 0: print(fprocessed {len(feature_list)} images) item_vectors np.stack(feature_list) np.save(features/item_vectors.npy, item_vectors)逻辑说明Path.iterdir逐个取文件image_to_vector每调用一次只处理一张图内存占用非常稳定。参数说明% 500只是打印进度不影响结果如果图片数量超过两万建议改用np.memmap预分配数组避免最后np.stack时再来一次大内存拷贝。CPU提取单张图大约0.3秒2000张图10分钟能跑完这个速度课设完全能接受。5.3 源码打包发给老师后在新电脑上跑不起来现象在本机运行正常换一台电脑后import torch就报错或者路径找不到整个项目瘫痪。原因requirements.txt写的是包名没有版本号新电脑装上了更高版本接口变化导致报错更常见的是项目中用了相对路径但当前工作目录不是项目根目录。解决用pip freeze生成锁定版本的依赖文件。代码里所有路径都基于项目根目录计算不要依赖os.getcwd()。我在每个src模块开头都会加一段固定写法import os ROOT_DIR os.path.dirname(os.path.dirname(os.path.abspath(__file__))) def abs_path(relative): return os.path.join(ROOT_DIR, relative)逻辑说明__file__是当前模块文件位置向上跳两级就是项目根目录不管你在哪个目录下执行python main.py路径都不会错。参数说明如果你的源码放在src子目录下这一级跳两级是对的如果main.py不在根目录需要相应调整层数。这个坑看着小但在毕业设计检查现场非常致命属于典型的“一台电脑能跑换台电脑翻车”。5.4 测试报告里的指标高得离谱答辩一问就露馅现象Precision10写的是0.95老师一问“你拿去推荐的衣服是不是正好是测试集里的衣服”你答不上来。原因数据划分方式错了。很多初学者把数据随机打散后划分训练集和测试集导致同一件衣服既出现在训练集里又被用来测试模型“见过答案”当然准。解决用“用户划分”而不是“物品划分”。按用户ID切分比如80%用户作为训练集剩下20%用户作为测试集训练阶段只看训练用户的交互测试阶段给测试用户生成推荐再用测试用户真实交互计算命中率。代码如下users rating_matrix.index train_users users[:int(len(users) * 0.8)] test_users users[int(len(users) * 0.8):] train_matrix rating_matrix.loc[train_users] test_matrix rating_matrix.loc[test_users]逻辑说明训练矩阵里完全没有测试用户的交互记录推荐结果只能依赖物品特征和其他用户行为评估结果才真实。参数说明80/20是常见比例如果你的数据量极小可以用70/30但比例不是乱调的文档里写清楚即可。这样算出来的Precision通常会在0.1到0.4之间看起来没那么漂亮但老师没法挑毛病。5.5 没有做可视化演示时只能打印一串物品ID现象答辩现场输入--user_id 1终端里输出[item_123, item_456, ...]台下老师完全不知道这些ID对应的衣服长什么样。原因项目只做了算法层没有做结果呈现层。课程设计虽然不要求完整前端但至少要让人能“看到”推荐效果好。解决把推荐结果映射成图片列表导出成一个带缩略图的HTML报告。用Python生成HTML比写Flask简单得多核心代码只有几十行。html [htmlbodyh3推荐结果/h3div styledisplay:flex;flex-wrap:wrap;] for item_id in final: img fdata/raw/images/{item_id}.jpg html.append(fdiv stylemargin:10pximg src{img} width120) html.append(fp{item_id}/p/div) html.append(/div/body/html) with open(recommend_result.html, w, encodingutf-8) as f: f.write(\n.join(html)) import webbrowser webbrowser.open(recommend_result.html)逻辑说明final是推荐物品ID列表循环拼接HTML片段最后用webbrowser自动打开浏览器。参数说明图片路径是相对路径HTML文件放在项目根目录才能正确显示如果图片多建议只展示前10张避免页面加载过慢。这个技巧能直接把演示效果提升一个档次。6. 进阶从“能跑”到“能答辩”评估指标、界面封装与文档收尾6.1 离线评估指标怎么算推荐系统的离线指标常用PrecisionK和RecallK你需要一个函数让老师信服“推荐效果是能测量的”。def precision_at_k(rec_list, ground_truth, k10): hit len(set(rec_list[:k]) set(ground_truth)) return hit / k def recall_at_k(rec_list, ground_truth, k10): hit len(set(rec_list[:k]) set(ground_truth)) return hit / len(ground_truth) if ground_truth else 0逻辑说明rec_list是推荐给测试用户的物品列表ground_truth是用户测试期真实交互的物品集合两个函数分别计算推荐列表命中真实交互的比例。参数说明K取10即可太大太小都不利于答辩展示。算完所有测试用户的平均值把结果写到测试报告的表格里这比“看起来推荐得挺准”这种描述有说服力得多。6.2 一个进阶技巧把推荐结果导出成可视化HTML如果你不想做前端用第5.5节的HTML导出方案就够了。但还有一个小升级在HTML里同时展示“推荐理由”也就是把相似度分数或相关标签一起输出来。比如纯棉T恤推荐理由写“因为你收藏了这件白色圆领T恤”答辩老师会认为你确实理解了推荐逻辑。不要只写“系统推荐”要让理由可见。我自己的习惯是每次改完参数一定先跑一个冷启动用户和一个老用户的推荐结果做对比冷启动如果全是热门款说明兜底生效老用户如果和冷启动高度雷同说明数据稀疏问题没解决。这个习惯帮我避掉了不少演示时的尴尬。最后说一句这个项目方向不难关键是每个环节都留下可验证的中间产出希望帮到你。本文还有配套的精品资源点击获取
返回列表