ARTICLE DETAIL

资讯详情

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

Django+LSTM构建用户购物行为分析与预测系统

Django+LSTM构建用户购物行为分析与预测系统 先说点实际的。每年到了毕设季节淘宝上搜大数据毕设能翻出来几百个同质化题目但你仔细看会发现绝大多数都是把几个开源项目拼一下、换个皮就拿出来卖。真正能让你在答辩现场站得住、被老师追问细节时不慌的是你自己清楚这套系统每一层是怎么转起来的。我这个题目的核心是用户购物行为这条数据链从采集、清洗、存储、可视化展示到最后的深度学习预测完整走一遍。这篇文章我把这套系统的设计思路、核心代码实现、还有我在调试过程中踩过的坑全部写出来给正在做同类题目的同学一个参考。你看到标题里的淘宝用户购物别紧张这里的数据并不是真的去爬淘宝那既违规也爬不动而是基于公开的电商行为数据集来构建用户画像和购物路径。整套系统的骨架是Django深度学习部分我用的是序列模型做行为预测可视化部分走的是ECharts大屏方案。适合谁看正在做电商类、大数据类毕设的同学以及想把Django 深度学习这个组合真正跑通、搞懂原理的开发者。1. 整体架构设计为什么是Django而不是Spring Boot1.1 技术选型的真实考量我见过很多同学一上来就问老师能不能用Java写其实对于大数据可视化这类偏重数据处理和展示的题目Python生态的优势太明显了。你在数据处理阶段要用Pandas做清洗聚合在预测阶段要调TensorFlow或PyTorch的模型在展示阶段后端要随时响应前端图表请求——这三个环节如果用Java你得在Spring Boot里硬写JDBC调Hive还得另外搞一套Python服务来做模型推理系统复杂度翻了不止一倍。Django最大的优势在于自带全家桶ORM让你不用写一行原生SQL就能操作MySQL里的用户行为表Admin后台可以让你快速核对数据有没有清洗干净自带的模板系统和DRFDjango REST Framework让前后端交互变得非常规整。最关键的一点Python的深度学习库在Django里直接import就能用你训练好的模型文件就是一个.h5或者.pth在视图函数里load进来就能预测这个链路短到让人觉得这不科学。1.2 系统分层与数据流向我用一张流程把整个系统的数据流向讲清楚你把这个逻辑记在脑子里比背代码重要得多原始行为日志CSV/Excel ↓ 数据清洗去重、缺失值处理、时间戳格式化 ↓ MySQL数据库用户表、商品表、行为表 ↓ Django ORM读取 Pandas聚合统计 ↓ Redis缓存热点统计结果30秒过期 ↓ RESTful API输出JSON ↓ 前端ECharts大屏渲染 ↓ 并行用户行为序列 → LSTM模型 → 预测结果回写这套流程里有几个关键设计决策我解释一下为什么中间加Redis大屏页面通常是秒级刷新如果每一次前端轮询都直接查MySQL在数据量到百万级时聚合查询可能要两三秒大屏就变成幻灯片了。用Redis做一层缓存把最近30秒内的聚合结果直接给前端既保证了实时性又不至于把数据库打爆。为什么行为预测单独走一条线因为预测模块的响应速度不可能做到毫秒级即使我用的是轻量级LSTM单条序列推理也需要几十毫秒。所以预测模块是异步的系统先把用户最近N次行为序列发给模型模型返回购买概率、偏好品类、可能下单时间段然后把这个预测结果存回MySQL可视化页面读取的是这个结果而不需要实时跑模型。1.3 所谓大数据到底数据量多大才够说实话真正做毕设你说要搭一套Hadoop集群处理TB级数据既不现实也没必要。我这套系统用的数据集大概在100万条行为记录量级MySQL单表存储完全能扛住。但为了体现大数据的处理流程我在清洗阶段做了三个关键动作去重同一用户同一秒点击同一商品视为误操作、会话划分连续30分钟无操作视为会话中断、特征构造计算用户平均点击深度、加购转化率、品类集中度。这个体量说不上大数据但整个数据流水线是完整的答辩时你只需要说清楚一个点如果数据量再大10倍只要把MySQL换成ClickHouse把ORM查询换成预计算离线结果架构依然成立。这个演进能力就是老师想听到的东西。2. 数据层实现从原始日志到可分析的结构化数据2.1 Django模型设计的关键细节三个核心模型别偷懒字段设计直接决定了后端的查询效率和你做特征工程时是否顺手。我的设计如下# models.py class User(models.Model): user_id models.IntegerField(primary_keyTrue) age_group models.CharField(max_length20) gender models.CharField(max_length10) occupation models.CharField(max_length50, defaultunknown) class Product(models.Model): product_id models.IntegerField(primary_keyTrue) category models.CharField(max_length50) price models.FloatField() brand models.CharField(max_length50, blankTrue) class Behavior(models.Model): id models.AutoField(primary_keyTrue) user models.ForeignKey(User, on_deletemodels.CASCADE) product models.ForeignKey(Product, on_deletemodels.CASCADE) behavior_type models.CharField(max_length10) # 取值pv(浏览)、cart(加购)、fav(收藏)、buy(购买) timestamp models.DateTimeField() class Meta: indexes [ models.Index(fields[user, timestamp]), models.Index(fields[behavior_type]), ]这里有几个你如果自己写会忽略的细节Behavior表必须加联合索引。你后面统计某用户的时间序列要频繁执行WHERE user_idxxx ORDER BY timestamp没有索引的话百万级数据直接全表扫描一次查询5秒起步。索引加上后能压到几十毫秒。行为类型用CharField而不是IntegerField。有些开源项目用0/1/2/3表示行为类型写起来省事但你在做可视化时要映射回中文标签多一层不说调试时你还得背数字含义。直接用pv/cart/fav/buy代码可读性好一万倍。外键关联别省。虽然Django ORM查外键会自动JOIN但你在统计品类维度时要通过behavior__product__category这类跨表查询没有外键关系这一步会非常痛苦。2.2 数据清洗我用Pandas做了什么处理原始数据集我用的公开电商数据集格式是CSV问题很多常见的有时间戳是Unix格式需要转换、部分用户ID是空的、同一秒内的重复点击。清洗脚本我单独写了一个data_processor.py核心逻辑是这样import pandas as pd from datetime import datetime df pd.read_csv(raw_data.csv) # 1. 去重同一用户在同一秒对同一商品的行为只保留一条 df df.drop_duplicates(subset[user_id, product_id, timestamp]) # 2. 过滤异常值price为负或者超过100万的数据删掉 df df[(df[price] 0) (df[price] 1000000)] # 3. 时间戳转换 df[timestamp] df[timestamp].apply( lambda x: datetime.fromtimestamp(x).strftime(%Y-%m-%d %H:%M:%S) ) # 4. 缺失值处理性别未知的填unknown不要直接删行 df[gender] df[gender].fillna(unknown)清洗时的教训分享一个千万别直接删掉性别缺失的用户。电商行为数据里性别缺失非常常见你直接删行会导致样本偏差——留下来的人全是愿意填资料的预测模型会学到错误规律。正确做法是保留并标记为unknown让模型自己学习这个类别的模式效果反而更好。清洗完之后的数据导入MySQL我用的是Django的bulk_create批量写入一次性导入10万条需要大概15秒如果用单条create循环半小时起步。3. 可视化大屏开发ECharts Django REST Framework3.1 后端API如何设计才能应对大屏密集请求大屏页面通常同时展示6-8个图表如果每个图表单独请求一次接口每次请求都要查一次数据库前端的加载体验会很差。我的做法是一次聚合接口返回所有图表数据# api/views.py from rest_framework.views import APIView from rest_framework.response import Response from django.core.cache import cache import pandas as pd class DashboardDataView(APIView): def get(self, request): cached_data cache.get(dashboard_data) if cached_data: return Response(cached_data) # 从ORM批量取数转为DataFrame qs Behavior.objects.select_related(product, user).all()[:50000] df pd.DataFrame(list(qs.values( behavior_type, timestamp, product__category, product__price ))) # 按小时统计活跃度 df[hour] pd.to_datetime(df[timestamp]).dt.hour hour_activity df.groupby(hour).size().reset_index() # 按品类统计销量TOP10 cate_sales df[df[behavior_type] buy].groupby( product__category ).size().nlargest(10).reset_index() result { hour_activity: hour_activity.to_dict(orientrecords), category_top10: cate_sales.to_dict(orientrecords), # ... 其他图表数据 } cache.set(dashboard_data, result, timeout30) return Response(result)这里有一个对毕设来说非常重要的点Django的ORM查询不擅长做聚合统计你非要用ORM写Count加GroupBy也能写但稍微复杂一点的分组就非常痛苦。我的经验是ORM负责筛选数据Pandas负责聚合计算各干各最擅长的事。数据量在50万以内这个组合的性能非常好实测接口响应在300毫秒以内。3.2 用Redis缓存解决大屏轮询性能瓶颈缓存本身不复杂复杂的是缓存了什么和缓存多久。大屏的实时性要求是看起来在动不是每一秒都精确。所以我设置缓存30秒过期前端每10秒轮询一次这样用户看到的是最多30秒前的数据感官上就是实时的。如果设置1秒过期Redis的命中率极低等于每次都穿透到MySQL缓存就没意义了。Redis在Windows上调试有坑很多同学装不上Windows版Redis。我的建议是直接用Docker拉一个redis:7镜像跑docker run -d --name redis -p 6379:6379 redis:7Django端配置也很简单# settings.py CACHES { default: { BACKEND: django_redis.cache.RedisCache, LOCATION: redis://127.0.0.1:6379/1, TIMEOUT: 30, } }3.3 大屏布局与ECharts动态数据刷新前端我没用复杂的Vue框架直接用Django模板渲染一个大屏页面加原生JS轮询ECharts。布局采用最经典的中间主图 两侧辅助图模式中间主图用户行为转化漏斗浏览→加购→收藏→购买左侧上每小时活跃度折线图左侧下品类销量TOP10柱状图右侧上用户画像雷达图右侧下最新实时行为滚动列表核心的轮询逻辑在这个JS片段里function fetchDashboardData() { fetch(/api/dashboard/) .then(response response.json()) .then(data { myChart.setOption({ series: [{ data: data.funnel_data }] }); // 其他图表同理 }); } // 每10秒刷新一次 setInterval(fetchDashboardData, 10000); fetchDashboardData();这里有一个前端开发常见的坑直接setOption在新数据不完整时会把旧图表的某些配置清掉。稳妥的做法是在setOption里加上notMerge: true或者每次重新init图表。第一种方式更平滑推荐。4. 深度学习行为预测模块不是所有预测都叫AI4.1 我为什么选了LSTM而不是Transformer题目要求的行为预测实际上要预测的是根据用户最近N次行为序列预测下一次行为是什么浏览/加购/收藏/购买。这本质是一个序列分类问题。如果你关注深度学习领域现在Transformer是主流但在这个场景下用LSTM依然合理原因有三用户行为序列长度通常在几十到几百之间LSTM在这类中等长度序列上效果不输TransformerLSTM的参数量小CPU就能推理不需要GPU在答辩演示时你不用依赖实验室的显卡从毕设的创新角度讲LSTM的遗忘门机制可以和用户兴趣衰减做对应解释——用户的旧行为对当前预测的影响本来就应该逐渐减弱这在学术上叫时间衰减用LSTM解释非常自然。4.2 特征工程把行为序列变成模型输入原始数据不能直接喂给模型需要做序列构建。每个用户的最近50次行为按时间排序编码成ID序列再padding到固定长度# sequence_builder.py from tensorflow.keras.preprocessing.sequence import pad_sequences def build_sequences(df, max_len50): # 按用户分组按时间排序取出行为类型编码 user_groups df.sort_values(timestamp).groupby(user_id)[behavior_type] sequences [] labels [] for _, group in user_groups: # 行为类型编码: pv0, cart1, fav2, buy3 seq [behavior_map[x] for x in group] if len(seq) 10: continue # 滑动窗口用前N-1个行为预测第N个 for i in range(10, len(seq)): sequences.append(seq[i-10:i]) labels.append(seq[i]) # padding统一长度 sequences pad_sequences(sequences, maxlen10) return sequences, labels这里我踩过一个很大的坑负样本严重不均衡。真实电商数据里pv占了80%以上buy可能只有2%如果你直接用原始数据训练模型会学成永远预测当前浏览准确率看着95%实际毫无价值。我的处理方式是对pv做下采样对buy和cart做上采样让四类行为比例控制在4:2:2:2左右模型才能真正学到行为转化的模式。4.3 模型训练与部署模型结构不复杂单层LSTM加全连接就能达到不错效果import tensorflow as tf model tf.keras.Sequential([ tf.keras.layers.Embedding(4, 16, input_length10), tf.keras.layers.LSTM(64, return_sequencesTrue), tf.keras.layers.LSTM(32), tf.keras.layers.Dense(16, activationrelu), tf.keras.layers.Dense(4, activationsoftmax) ]) model.compile( optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy] ) model.fit(x_train, y_train, epochs10, batch_size256, validation_split0.2)在100万行为数据上训练10个epochCPU大约要30分钟。建议训练完保存成behavior_prediction.h5部署时Django直接load# prediction_service.py from tensorflow.keras.models import load_model import numpy as np _model load_model(behavior_prediction.h5) def predict_next_behavior(user_recent_sequence): # user_recent_sequence: list of behavior ids, 如 [0,1,2,0,0] padded pad_sequences([user_recent_sequence], maxlen10) probs _model.predict(padded, verbose0)[0] # 返回概率最大的行为类型和置信度 behavior_names [浏览, 加购, 收藏, 购买] idx int(np.argmax(probs)) return { predicted_behavior: behavior_names[idx], confidence: float(probs[idx]), all_probs: { name: float(p) for name, p in zip(behavior_names, probs) } }有一个细节必须要提醒Django的视图函数在处理请求时是线程并发的而TensorFlow模型默认不是线程安全的。如果你在视图里直接调用模型高并发时会报错。我的做法是加一个线程锁import threading _model_lock threading.Lock() def safe_predict(sequence): with _model_lock: return predict_next_behavior(sequence)这个细节如果你在答辩时主动讲出来老师会认为你真正考虑过生产环境的问题加分不少。5. 系统部署与展示从开发机到答辩现场的完整流程5.1 本地开发环境的坑与解决我预估你大概率会有这几个问题Django版本与TensorFlow版本冲突。TensorFlow 2.x系列要求Python 3.6-3.9而Django 4.x要求Python 3.8这个交集没问题。但如果你用Python 3.11装TensorFlow恭喜你准备折腾一晚上吧。我的建议直接用Python 3.9 Django 3.2 TensorFlow 2.10这个组合是我实测最稳的。static文件加载不出来。这个问题在Django里十个新手九个遇到。模板里写{% load static %}img src{% static images/logo.png %}但页面死活不显示F12一看404。原因是Django开发服务器默认不处理static文件需要在settings.py里加STATIC_URL /static/ STATICFILES_DIRS [BASE_DIR / static]代码没问题但缓存问题还可能出现——你改了CSS文件但页面没变浏览器缓存了。CtrlF5强制刷新就好不用怀疑代码写错了。5.2 答辩演示时怎么保证不出意外毕设答辩翻车现场十有八九是网络问题和环境问题。我的建议是把所有服务全部本地化。MySQL用本机的Redis用本机的模型文件在本地加载大屏页面走127.0.0.1访问。一个非常实用的技巧在演示前先把大屏页面所有图表数据加载好并截图万一现场浏览器崩溃或服务启动失败你至少可以用截图将演示进行到底。这个备份方案不丢人反而说明你有风险意识。5.3 说明文档和LW材料怎么组织标题里提到的说明文档和LW其实就是你交给老师的过程材料和论文。我写文档时的组织顺序你可以参考摘要和关键技术说明Django、LSTM、ECharts各解决什么问题系统架构图和数据流图用Visio或draw.io画数据库设计说明三张表的字段含义和关联关系核心功能截图大屏效果、预测结果页面项目运行部署文档环境配置、启动步骤系统测试报告功能测试用例表、性能测试结果总结与展望不足点也要写比如当前模型未考虑商品文本特征后续可引入BERT进行语义增强其中第7点非常重要老师看到你写不足与改进比写十页创新点更可信。毕竟一个毕设系统怎么可能没有缺点你能自己说出来说明你真的做过和思考过。6. 调试中遇到的经典问题实录我把自己开发这套系统时踩过比较深的坑整理成了表格你有类似症状时可以直接对着排查现象根本原因解决方案大屏图表数据一直不变Redis缓存没设过期时间cache.set()时显式加timeout30预测接口响应要2秒每次请求都重新load_model模块加载时一次性加载到全局变量训练准确率很高但预测结果全是一个类样本不均衡对多数类下采样前端轮询后旧图表消失setOption缺少notMerge参数加setOption(option, true)Django admin后台打开很慢数据表没有索引给Behavior表加联合索引导入数据时外键报错数据中product_id不存在导入前先做外键完整性检查模型在Windows上加载特别慢TensorFlow初始化开销改成SSD盘存储模型文件首次访问后预热前端访问接口跨域报错Django未启用CORS安装django-cors-headers中间件加白名单这些坑如果你提前知道至少能节省一周的调试时间。特别是模型加载到全局变量这一点我见过太多人在视图函数里写load_model每一次请求都要重新读磁盘加载文件。7. 这套系统价值在哪从毕设代码到项目经验做完这套系统你在简历上可以正式写主导开发了基于Django和LSTM的用户购物行为分析与预测系统处理百万级行为数据实现多维度可视化分析与行为序列预测模型准确率达到XX%。这个XX%的数据要注意——我们的模型在样本均衡处理后准确率大概在72%到78%之间。有的同学觉得准确率低不好看干脆把数字写成95%这是非常危险的做法因为答辩老师可能追问你用了什么数据、多少样本、混淆矩阵长什么样。你诚实写75%左右然后补一句通过调整序列长度到20后准确率提升了约五个百分点这个回答含金量比虚报100%高得多。最后聊聊后续扩展的方向。我个人做这套系统时就规划了三个可以提高的维度你顺着一条线往下走就能变成期刊论文的素材一是序列模型引入注意力机制让模型自适应地关注那些高价值行为比如加购和收藏而不是一视同仁地看待每次点击二是加入商品画像和用户画像的交叉特征把冷启动用户的问题也纳入模型三是可视化层加入地理热力图和实时推荐结果联动从看数据进化到用数据做决策。这三条路任意一条做到位你这个毕设选题的价值就不只是毕业而是一个能写论文、能面试讲、能真正放在作品集里的完整项目。
返回列表