资讯动态

Python服饰推荐系统实战:从数据到上线的可复现方案

发布时间:2026/10/1 18:03:16 来源:尧图企业网站定制
简介这是一套面向Python初学者与推荐系统爱好者的服饰推荐系统实战项目围绕个性化服装推荐场景综合运用数据处理、机器学习与Web开发技术帮助读者理解数据驱动推荐从数据采集到模型部署的完整链路。压缩包共约2000个文件整体约225.31MB其中以9698张jpg服饰图片作为商品视觉素材配合30个py脚本承载推荐算法与数据处理逻辑另有csv、json、bson等数据文件用于存储用户行为与商品信息js、vue、xml等前端文件支撑界面交互md文档则提供必要的说明。项目涵盖数据收集与预处理、特征工程、用户画像构建、协同过滤与矩阵分解等推荐算法、模型训练评估以及Flask/Django接口部署等关键模块并借助scikit-learn、surprise等库完成标准化与效果评估。目前已有318人学习下载适合希望系统掌握推荐系统构建流程、积累完整项目经验的开发者参考实践。1. 服饰推荐系统到底在推什么从「猜你喜欢」到可复现的 Python 方案打开任何一个电商 App首页那排「猜你喜欢」背后跑的就是推荐系统。而python服饰推荐系统这个标题说的就是用 Python 把「用户—服饰—行为」这三者串起来算出一个排序列表让每个人看到不一样的衣服。它解决的不是「有没有货」的问题而是「这么多货先给谁看哪几件」的问题。适合谁做有订单或点击日志的电商团队、想给服装类目做个性化排序的算法同学、以及拿公开数据集练手的学生。我见过太多人一上来就上深度学习结果连最基础的协同过滤都没跑通召回率还不如热门榜单——这篇就把从数据到上线的最小闭环讲清楚让你少走那段弯路。服饰推荐和图书、电影推荐有个本质区别服饰有强属性、强季节、强尺码还有退货率。一件连衣裙的「颜色、版型、尺码、季节」直接决定它能不能被推给某个用户而电影没有尺码这回事。所以做服饰推荐特征工程比模型选型更吃功夫。下面按「数据怎么来 → 召回怎么搭 → 排序怎么排 → 坑在哪 → 怎么验证」这条线走每一步都给能抄的代码和参数。2. 数据准备与特征工程服饰推荐系统的地基怎么打2.1 三类数据源和它们的获取方式做推荐系统数据永远是第一道坎。服饰场景常见的数据源有三类用户行为日志点击、加购、收藏、下单、退货、商品属性表品类、颜色、尺码、价格、上架时间、用户画像性别、年龄段、历史偏好。如果你手上有生产库直接写 SQL 抽数如果没有用公开数据集起步比如阿里天池的淘宝用户行为数据集、Amazon Reviews 里的 Clothing 子集。用 Python 读进来通常长这样import pandas as pd # 行为日志user_id, item_id, behavior_type, timestamp behavior pd.read_csv(user_behavior.csv, parse_dates[timestamp]) # 商品属性item_id, category, color, size, price items pd.read_csv(item_attrs.csv) # 用户画像user_id, gender, age_range users pd.read_csv(user_profile.csv) # 只保留服饰类目避免跨类目噪声 clothing_cates [连衣裙, T恤, 牛仔裤, 外套, 卫衣] items items[items[category].isin(clothing_cates)] # 行为加权下单权重最高退货给负权重 weight_map {click: 1.0, fav: 2.0, cart: 3.0, buy: 5.0, refund: -3.0} behavior[weight] behavior[behavior_type].map(weight_map)这段代码做了三件事解析时间戳、过滤服饰类目、给不同行为赋权重。参数说明weight_map里的数值不是拍脑袋一般按「点击:收藏:加购:下单 1:2:3:5」的经验比例退货给负权重是为了让模型学到「推了会退」的负信号。如果你的退货率超过 30%这个负权重还要加大。2.2 服饰特有的特征尺码、季节、颜色偏好通用推荐系统那套 one-hot 编码在服饰场景不够用。你得额外构造几类特征特征类型具体字段构造方式为什么重要尺码匹配user_size, item_size用户历史购买尺码众数 vs 商品尺码尺码不对直接退货季节匹配item_season, 当前月份季节标签 月份映射羽绒服夏天推就是浪费曝光颜色偏好user_color_pref用户历史购买颜色分布服饰颜色偏好极强价格带price_bucket按用户历史客单价分桶推太贵直接划走构造尺码匹配特征的代码# 用户历史购买尺码众数 user_size behavior[behavior[behavior_type] buy].merge( items[[item_id, size]], onitem_id ).groupby(user_id)[size].agg(lambda x: x.mode()[0] if len(x) 0 else M) # 商品尺码与用户尺码是否一致 items[size_match] items.apply( lambda row: 1 if user_size.get(row.get(user_id)) row[size] else 0, axis1 )逻辑说明mode()取众数因为用户可能买过多个尺码但最常买的那个才是真实尺码。size_match作为二值特征喂给排序模型能显著降低退货率。注意新用户没有历史购买记录user_size会是 NaN这时候用品类平均尺码兜底别直接填 0。2.3 训练样本的构造负采样怎么做才不翻车推荐系统的训练样本不是「用户买了什么」这么简单而是「正样本 负样本」。正样本是用户真实交互过的商品负样本是用户没交互过的。负采样策略直接决定模型好坏。常见做法是「按曝光未点击采样」也就是从用户看到过但没点的商品里抽。但服饰场景有个坑用户没点可能是因为尺码不全或季节不对这种负样本会教坏模型。我一般会做两层过滤import numpy as np def negative_sampling(behavior, items, neg_ratio4): positives behavior[behavior[weight] 0][[user_id, item_id]] all_items items[item_id].unique() negatives [] for uid, group in behavior.groupby(user_id): seen set(group[item_id]) # 过滤掉尺码缺失、季节不符的商品 candidates items[ (~items[item_id].isin(seen)) (items[size].notna()) (items[season].isin([春, 夏, 秋, 冬])) ][item_id].values if len(candidates) 0: continue sampled np.random.choice(candidates, sizemin(neg_ratio * len(group), len(candidates)), replaceFalse) negatives.extend([(uid, iid) for iid in sampled]) neg_df pd.DataFrame(negatives, columns[user_id, item_id]) neg_df[label] 0 pos_df positives.copy() pos_df[label] 1 return pd.concat([pos_df, neg_df], ignore_indexTrue)参数说明neg_ratio4表示每个正样本配 4 个负样本这是业界常用的比例太高会让训练变慢太低模型学不到区分度。踩坑提醒负采样一定要在「用户维度」做不能全局随机抽否则同一个用户的正负样本分布会偏。3. 召回层用 Python 把候选集从百万降到千3.1 协同过滤召回ItemCF 在服饰场景的适配召回层的目标是从全量商品里快速筛出几百个候选协同过滤是最经典的方案。ItemCF 的核心思想是「买了 A 的人也买了 B」在服饰场景特别有效因为搭配购买很常见——买了牛仔裤的人往往也需要腰带。from collections import defaultdict import math def item_cf_train(behavior, top_k20): # 构建用户-商品交互矩阵 user_items behavior[behavior[weight] 0].groupby(user_id)[item_id].apply(set).to_dict() # 计算商品共现 item_sim defaultdict(dict) item_count defaultdict(int) for uid, items in user_items.items(): for i in items: item_count[i] 1 for j in items: if i ! j: item_sim[i][j] item_sim[i].get(j, 0) 1 # 余弦相似度归一化 for i in item_sim: for j in item_sim[i]: item_sim[i][j] / math.sqrt(item_count[i] * item_count[j]) # 取 top_k 相似 item_sim[i] dict(sorted(item_sim[i].items(), keylambda x: -x[1])[:top_k]) return item_sim def item_cf_recall(user_id, user_items, item_sim, top_n200): scores defaultdict(float) for i in user_items.get(user_id, []): for j, sim in item_sim.get(i, {}).items(): scores[j] sim return sorted(scores.items(), keylambda x: -x[1])[:top_n]逻辑说明先算商品共现次数再用余弦相似度归一化避免热门商品因为曝光大而霸榜。top_k20是每个商品保留 20 个最相似邻居top_n200是给每个用户召回 200 个候选。参数怎么调如果候选集不够加大top_n如果召回太慢减小top_k。服饰场景建议top_k在 15~30 之间。3.2 向量召回用双塔模型做语义匹配协同过滤有个硬伤新商品没有交互数据就召不回。服饰上新快这个问题很致命。双塔模型Two-Tower把用户和商品分别编码成向量用内积算相似度能缓解冷启动。import torch import torch.nn as nn class Tower(nn.Module): def __init__(self, input_dim, hidden_dim128, output_dim64): super().__init__() self.net nn.Sequential( nn.Linear(input_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, output_dim) ) def forward(self, x): return nn.functional.normalize(self.net(x), dim-1) class TwoTower(nn.Module): def __init__(self, user_dim, item_dim): super().__init__() self.user_tower Tower(user_dim) self.item_tower Tower(item_dim) def forward(self, user_feat, item_feat): u self.user_tower(user_feat) i self.item_tower(item_feat) return (u * i).sum(dim-1) # 内积参数说明hidden_dim128是中间层维度output_dim64是最终向量维度。服饰场景建议输出维度不要低于 32否则颜色、尺码这些细粒度信息会被压没。训练时用 BPR loss 或 sampled softmax负样本就用 2.3 节采出来的。注意双塔模型上线时要预计算所有商品向量存进向量库Faiss、Milvus 都行用户向量实时算然后做 ANN 检索。别每次请求都跑一遍模型那样 QPS 撑不住。3.3 多路召回融合规则加权还是学习权重实际系统不会只用一路召回。常见做法是 ItemCF 双塔 热门 新品四路各出 100~200 个候选合并去重后送排序。融合方式有两种规则加权给每路打分归一化后加权求和权重靠 A/B 实验调。简单、可控但需要人工调。学习融合把每路的召回分数作为特征训练一个 LR 或 GBDT 来学权重。效果更好但要多维护一个模型。我一般先上规则加权权重初始值设成 ItemCF:双塔:热门:新品 0.4:0.3:0.2:0.1跑一周看数据再调。血泪经验热门召回一定要限流否则它会挤掉个性化召回的位置最后整个系统退化成榜单。4. 排序层从候选集到最终列表的精细打分4.1 特征工程排序模型吃什么召回层给的是粗排候选排序层要做精细打分。排序模型的特征分四组特征组示例字段来源用户特征性别、年龄段、历史客单价、偏好品类用户画像表商品特征品类、价格、颜色、尺码、上架天数商品属性表交叉特征用户品类偏好分、尺码匹配、价格差实时计算上下文特征当前小时、星期几、是否大促请求上下文交叉特征里最重要的是用户品类偏好分算法是用户对该品类的历史行为加权求和再归一化def user_category_pref(behavior, items): df behavior.merge(items[[item_id, category]], onitem_id) pref df.groupby([user_id, category])[weight].sum().reset_index() # 归一化到 0-1 pref[pref_score] pref.groupby(user_id)[weight].transform( lambda x: (x - x.min()) / (x.max() - x.min() 1e-6) ) return pref[[user_id, category, pref_score]]参数说明1e-6是防止除零。归一化用 min-max 而不是 z-score因为推荐场景更关心相对排序不关心绝对分布。4.2 模型选型GBDT 还是 DeepFM排序模型的选择取决于数据量和特征复杂度数据量 100 万样本先用 LightGBM特征工程做好就能打训练快、可解释。数据量 100 万且特征稀疏上 DeepFM 或 DIN能自动学交叉特征。服饰场景特殊点尺码、季节这类硬约束建议做成规则过滤别全交给模型学。LightGBM 的训练代码import lightgbm as lgb from sklearn.model_selection import train_test_split feature_cols [age, gender, price, category_id, size_match, pref_score, hour, is_promo] X_train, X_val, y_train, y_val train_test_split( samples[feature_cols], samples[label], test_size0.2, random_state42 ) params { objective: binary, metric: auc, learning_rate: 0.05, num_leaves: 63, min_data_in_leaf: 100, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 5, verbose: -1 } dtrain lgb.Dataset(X_train, y_train) dval lgb.Dataset(X_val, y_val, referencedtrain) model lgb.train(params, dtrain, num_boost_round500, valid_sets[dval], callbacks[lgb.early_stopping(50)])参数说明learning_rate0.05配合num_boost_round500是稳妥组合num_leaves63控制树复杂度min_data_in_leaf100防止过拟合。early_stopping(50)表示验证集 50 轮不提升就停。踩坑服饰场景样本不均衡严重正样本远少于负样本记得设scale_pos_weight或对负样本降采样。4.3 重排多样性和业务规则的最后一公里排序模型输出的是「相关性」排序但直接展示会有问题同款不同色霸屏、全是同一品类、价格全挤在一个区间。重排层就是解决这个的。常见做法是 MMR最大边际相关性def mmr_rerank(candidates, lambda_param0.7, top_n20): selected [] remaining candidates.copy() while len(selected) top_n and remaining: best_score -float(inf) best_item None for item in remaining: rel item[score] # 与已选列表的最大相似度 max_sim max([similarity(item, s) for s in selected], default0) mmr lambda_param * rel - (1 - lambda_param) * max_sim if mmr best_score: best_score mmr best_item item selected.append(best_item) remaining.remove(best_item) return selected参数说明lambda_param0.7表示 70% 看相关性、30% 看多样性。这个值越高越偏向相关性越低越多样。服饰场景建议 0.6~0.8太低会推出用户完全不感兴趣的东西。similarity函数可以用品类相同、颜色相同、价格接近来定义。5. 避坑与排查服饰推荐系统上线后最容易翻车的五个点5.1 现象推荐结果全是爆款个性化消失原因热门商品在召回和排序阶段都有天然优势样本里正例多模型学出来就是「推爆款准没错」。解决在召回层给热门商品降权排序层加一个「个性化分数」特征或者在重排层强制插入一定比例的冷门商品。我一般会在重排阶段保留 20% 的位置给「非热门但相关」的商品。5.2 现象新用户推荐完全不准原因新用户没有历史行为协同过滤和双塔都召不回合适商品。解决新用户走「热门 品类多样性」兜底策略同时在前端做引导选性别、选风格拿到初始信号后立刻切个性化。注意别给新用户推高退货率的品类比如紧身类服饰尺码问题会直接导致退货。5.3 现象季节切换时推荐大面积失效原因模型是用历史数据训练的夏天推羽绒服的样本很少模型没学到「季节」这个强特征。解决把季节作为硬过滤规则放在召回前同时训练时对季节特征做加权。更彻底的做法是按季节分模型春夏一个、秋冬一个但维护成本高小团队用规则过滤就够了。5.4 现象A/B 实验指标涨了但退货率也涨了原因模型只优化了点击率或转化率没考虑退货。推了尺码不全或与用户风格不符的商品短期转化好看长期退货吃掉利润。解决把退货率作为负向指标加入模型目标或者在重排层过滤掉「高退货风险」商品。血泪经验服饰类目退货率能到 30%~50%不把退货纳入优化目标推荐系统就是给公司挖坑。5.5 现象线上服务延迟高QPS 上不去原因召回层用了太多路、排序模型太大、特征实时计算太慢。解决按这个顺序排查——先看召回层是不是每路都跑了全量再看排序模型能不能蒸馏成小模型最后看特征能不能预计算。后悔药上线前一定要做压测别等大促当天才发现扛不住。6. 验证与迭代怎么判断这套系统真的有用6.1 离线指标AUC、RecallK、NDCG 怎么看离线评估是上线前的第一道关。常用指标指标含义服饰场景参考值AUC排序模型区分正负样本的能力 0.7 可用 0.75 不错Recall200召回层前 200 个里有多少真实正样本 0.6 及格NDCG20排序列表的质量 0.4 可用覆盖率推荐商品占总商品的比例 30% 避免头部集中算 RecallK 的代码def recall_at_k(model, test_data, k200): hits 0 total 0 for user_id, group in test_data.groupby(user_id): true_items set(group[group[label] 1][item_id]) if not true_items: continue pred_items set(model.recommend(user_id, top_nk)) hits len(true_items pred_items) total len(true_items) return hits / total if total 0 else 0参数说明k200对应召回层候选数离线评估要和线上召回数量对齐否则指标没意义。6.2 在线实验A/B 测试的坑和正确姿势离线指标好不代表线上好必须做 A/B。常见坑分流不均匀、实验周期太短、只看点击率不看 GMV。正确姿势是按用户 ID 哈希分流保证同一用户始终在同一组实验至少跑 7 天覆盖工作日和周末核心指标看 GMV 和退货率辅助指标看点击率和停留时长实验组和对照组样本量要够用 t 检验判断显著性6.3 迭代节奏从周更到日更的路径推荐系统不是一次做完就完事。我的迭代节奏是第一周跑通 ItemCF 热门召回排序用 LR先上线看数据第二周加双塔召回排序换 LightGBM做第一次 A/B第一个月加实时特征、重排多样性优化退货率之后按周迭代特征和模型按天更新商品向量最后说个我自己的习惯每次上线新模型前我都会用历史数据做一次「回放测试」——把过去一周的请求重新跑一遍新模型对比新旧排序结果的差异。如果差异超过 40%说明模型变化太大得谨慎放量。这个习惯帮我躲过了好几次线上事故。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑