1. 这个选题到底在研究什么
先把这个毕设题目的本质拆开说清楚,表面上看它是个包装了“大数据”“深度学习”的电商广告分析项目,但落到实际工程上,做的是这样一件事:采集社区场景里的广告曝光与用户行为数据,用深度学习模型学习广告特征与用户特征之间的交互关系,预测用户对商品产生购买行为的概率,再用可视化系统把结果展示出来。核心业务价值可以概括为一句话:解开“广告到底有没有用、对哪些人有用、为什么有用”这个黑盒。
很多同学看到这种题目会慌,觉得又要爬数据又要训练模型又要写前后端,工作量爆炸。实际上这个题目的合理定位是“一个前后端分离的预测展示系统”,深度学习是其中一个模块,而不是整个项目的全部。把这句话想清楚,整个项目的边界就清晰了:数据怎么来、怎么处理、怎么建模、怎么出结果、怎么展示,一条链路走通,项目就成功了八成。
这类选题适合以下人参考:准备计算机方向毕业设计、需要快速搭建一个有技术深度又有业务解释性的完整系统的同学;对电商推荐、广告点击率预估感兴趣,想用真实业务场景练手的人;以及打算用这套思路做数据分析展示类项目来丰富简历的开发者。看完这篇文章,你至少能掌握这个项目的完整架构、关键代码怎么写、答辩被追问时怎么应对。
2. 技术栈选型的底层逻辑
2.1 为什么是Django + Vue,而不是别的组合
Django和Vue的组合在毕设项目里几乎是“永不过时”的标配,原因很现实:第一,Django自带Admin后台、ORM、认证机制,开发效率高,对于需要管理用户、管理广告数据、管理模型结果的项目来说,很多基础功能不用从头写;第二,Vue的响应式生态和组件化开发非常适合做数据可视化大屏,配合ECharts能很快出效果;第三,这两种技术的学习资料极多,遇到问题随便搜都有答案,不会卡住进度。
有人会问,用Flask行不行?当然行,但Flask需要自己组装的东西更多,ORM要另配、Admin要另装、项目结构要自己约定,对毕设这种需要“稳”的场景来说,Django一刀切式的全家桶体验更省心。还有人会问,前端不用Vue用React行不行?也行,但Vue的中文生态和上手曲线优于React,对于需要在一个学期内完成项目并写论文的场景来说,Vue是更务实的选择。
这里要单独强调一个选型细节:项目里涉及的“大数据”和“深度学习”并不是要用Spark或者Hadoop那一套分布式体系,那是企业级场景的玩法,一个毕设项目撑不起那么大动静。这里更合理的理解是:数据量级达到“万级以上”、处理逻辑涉及批式计算与特征工程,再用深度学习模型(比如DeepFM等)来做预测,这就能在论文里站得住“大数据深度学习”的定位。如果答辩老师问“你这大数据体现在哪里”,答“百万级曝光日志的清洗、聚合与特征工程处理流程”就够了,不需要吹什么实时流式计算。
2.2 深度学习的角色定位
在这个项目里,深度学习模型的任务不是“挖掘隐藏规律”这种玄学,而是解决一个明确的二分类问题:给定一条广告曝光记录和对应的用户特征,判断该用户是否会产生购买行为。本质上就是计算广告领域的CTR/CVR预估问题。
选型的优先级建议是:优先考虑DeepFM,其次考虑Wide&Deep,再次才是传统的Logistic Regression或树模型。原因在于DeepFM同时具备低阶特征交互和高阶特征交互能力,代码实现也有大量现成框架可以用,不需要从零开始写网络结构,这对于毕设周期来说极其重要。同时它的论文引用价值高,写文献综述时有话可说。
深度学习模块的输出不是一个简单的“是否购买”,而是产出购买概率,这个概率会驱动前端展示的很多内容:高概率用户群体的画像标签、不同广告位的转化率对比、不同社区圈层的购买偏好差异等。说白了,模型是整个项目的“计算引擎”,可视化只是把计算结果翻译成人能看懂的形式。
3. 核心数据链路解析
3.1 数据从哪来
这块是大多数同学真正动手时第一个卡住的地方。现实情况是,没有任何公开数据集是专门给“社区广告对电商购买决策影响”这个题目定制的。所以常规做法是混合数据策略:
第一,开放电商行为数据集。网上有一些公开的电商行为日志数据,比如用户点击、加购、购买行为序列,虽然里面的用户ID和商品属性是脱敏的,但行为模式是真实的。
第二,自建模拟广告投放数据。你可以在自己的系统里构建一套广告位管理,模拟不同社区(比如设定“数码爱好者社区”“母婴社区”“健身社区”等虚拟圈层)投放不同广告,记录PV、UV、点击、曝光时长等。
第三,合成数据补齐短板。基于公开数据集的统计分布,用随机采样的方式生成一部分带标注的训练样本,保证数据量达到模型可训练的量级。
注意:毕设项目里合成数据不丢人,关键是要在论文里写清楚哪些是真实数据、哪些是合成数据、合成数据的生成策略是什么。答辩老师反感的是数据来源模糊不清,而不是反感数据不是“纯天然”的。
在设计数据库模型时,至少需要这几张核心表:
| 表名 | 主要字段 | 用途 |
|---|---|---|
| 用户表 | 用户ID、年龄、性别、注册天数、社区标签、历史购买次数 | 刻画用户静态画像 |
| 广告位表 | 广告位ID、所属社区、位置类型、出价方式 | 刻画广告展示场景 |
| 广告内容表 | 广告ID、商品类目、文案类型、图片类型、促销标签 | 刻画广告本身特征 |
| 曝光日志表 | 日志ID、用户ID、广告ID、曝光时间、曝光时长、是否点击 | 记录广告被看到的行为 |
| 行为转化表 | 用户ID、广告ID、商品ID、行为类型(浏览/加购/购买)、行为时间 | 记录后续购买行为 |
| 预测结果表 | 用户ID、广告ID、预测概率、模型版本、生成时间 | 存储模型推理结果供前端展示 |
3.2 特征工程的真实做法
特征工程直接决定模型效果,这比调网络结构更重要。实践里可以把特征分成四类来处理:
首先是用户特征,包括年龄分桶(如18-25、26-30等)、性别独热编码、注册时长、历史累计购买次数、历史累计点击广告次数、用户所在社区的嵌入向量。社区不能简单做一个类别特征然后Label Encoding,而是应该构造一个embedding层来学习社区与购买行为之间的关系。
其次是广告特征,包括商品类目编码、广告文案类型(促销型/功能型/情感型)、图片风格标签、广告位所在社区类型、投放时段(早中晚)。这些特征里很多是非数值的,需要经过embedding或者目标编码处理。
然后是交叉特征,这是体现项目深度的关键。比如“用户社区与广告商品类目的匹配度”——一个健身社区的用户看到运动器材广告,匹配度就要高一些;“用户历史广告点击率与该广告位平均点击率的差值”——体现用户与该广告位之间的偏好关系;还有“曝光时长分桶后的均值/方差”这一类统计特征。
最后是时序特征,从行为日志里提取用户最近7天内的广告点击次数、最近一次购买距今天数、广告曝光到点击的时间间隔等。这些时序特征对购买意图的刻画非常有效。
做完特征之后,必须做归一化和分桶,数值型特征统一归一化到0-1区间,长尾分布强的特征(比如购买金额)要做幂变换或者对数变换。
3.3 模型训练的关键配置
拿DeepFM来举例,实现的时候不需要自己从零写模型结构,直接用TensorFlow或者PyTorch的现成库,重点关注这几个配置点:
嵌入维度建议设置成8或16,不要太大。毕设数据集量级不大,嵌入维度过高会增加过拟合风险。隐藏层结构用[256, 128, 64]的三层全连接,激活函数用ReLU,输出层用Sigmoid。优化器选择Adam,初始学习率0.001,配合学习率衰减。损失函数用二分类交叉熵。
训练集/验证集/测试集按7:2:1切分,并且要注意按时间切分而不是随机切分——用前70%时间的数据训练,中间20%验证,最后10%测试。这样更符合“用过去预测未来”的业务逻辑,答辩时能加分。评估指标看AUC、LogLoss、Accuracy三个,其中AUC是核心,能到0.75以上这个模型就算达标了。
实践中特别容易犯的错:把时间戳字段直接丢进模型当数值特征。这样做会导致严重的过拟合,因为模型会记住“这个时间段的用户都买了”这种幻觉规律。正确做法是参考上文把时间信息转化为“距上次行为间隔”“行为时段”等衍生特征。
4. 从零搭建Django后端
4.1 项目目录结构设计
一个清晰的后端工程结构应该是这样的:
ad_project/ ├── manage.py ├── config/ # 项目配置目录 │ ├── settings.py │ ├── urls.py │ └── asgi.py ├── apps/ │ ├── users/ # 用户模块 │ ├── ads/ # 广告管理模块 │ ├── behaviors/ # 行为日志模块 │ ├── ml/ # 模型训练与预测模块 │ └── dashboard/ # 可视化数据接口模块 ├── scripts/ │ ├── generate_data.py # 数据生成脚本 │ ├── train_model.py # 模型训练脚本 │ └── predict.py # 批量预测脚本 └── static/ # 静态资源按照Django官方推荐的方式把不同业务拆成独立app,后续调试和答辩讲架构的时候会省很多力气。不要一个app里塞所有模型,到后期改一个地方就崩一遍,体验极其痛苦。
4.2 数据模型建模与迁移
以曝光日志表为例,Django模型可以这样写:
# apps/behaviors/models.py from django.db import models from django.contrib.auth.models import User class ExposureLog(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='exposure_logs') ad = models.ForeignKey('ads.Ad', on_delete=models.CASCADE, related_name='exposure_logs') community = models.CharField(max_length=64, db_index=True, verbose_name='社区标识') exposure_time = models.DateTimeField(db_index=True, verbose_name='曝光时间') duration = models.IntegerField(default=0, verbose_name='曝光时长(秒)') is_clicked = models.BooleanField(default=False, verbose_name='是否点击') click_time = models.DateTimeField(null=True, blank=True, verbose_name='点击时间') class Meta: db_table = 'exposure_log' indexes = [ models.Index(fields=['user', '-exposure_time']), models.Index(fields=['ad', 'is_clicked']), ]注意这里给高频查询字段建了组合索引,这是数据量上来之后查询不卡顿的关键。写模型的时候就要把索引考虑进去,而不是等出问题再去优化。
4.3 后端接口设计
整个系统需要提供以下几类API:
广告管理接口负责广告位的增删改查,以及广告内容的上架下架管理。用户数据接口负责注册登录和用户画像管理。曝光日志收集接口是埋点上报的接收端,前端页面产生的用户行为都通过这个接口上报到后端落库。数据统计接口面向可视化大屏,提供整体数据指标、用户画像分布、广告转化漏斗等聚合查询结果。模型预测接口接收用户ID和广告ID,调用深度学习模型服务,返回购买概率和推荐建议。
写DRF(Django Rest Framework)的时候有一个实用技巧:列表接口和详情接口用ViewSet统一管理,视图逻辑简单清晰。
# apps/ads/views.py from rest_framework import viewsets, permissions from .models import Ad from .serializers import AdSerializer class AdViewSet(viewsets.ModelViewSet): queryset = Ad.objects.all().order_by('-created_at') serializer_class = AdSerializer permission_classes = [permissions.IsAdminUser]前端通过Vue的Axios调用这些接口,做到前后端完全分离。这样答辩的时候可以理直气壮地说:这是一个标准的RESTful API架构,前后端通过JSON通信。
4.4 异步任务与性能优化
模型预测如果每次都是实时调用,响应会非常慢,因为加载一次模型文件就可能要几百毫秒甚至更多。实践里的做法是:利用Django的缓存框架,把模型加载一次后常驻内存,或者把模型部署为一个独立服务,通过HTTP或Socket调用。
对于批量的曝光日志入库,不建议一条一条save(),而是采用bulk批量写入。一次写入几千条,性能提升是数量级的。比如写一个数据导入命令:
# apps/behaviors/management/commands/import_logs.py from django.core.management.base import BaseCommand from apps.behaviors.models import ExposureLog class Command(BaseCommand): help = '批量导入曝光日志' def handle(self, *args, **options): batch = [] for line in open('data/logs.txt', encoding='utf-8'): # 解析并构造ExposureLog对象 batch.append(log_obj) if len(batch) >= 5000: ExposureLog.objects.bulk_create(batch, batch_size=1000) batch.clear() if batch: ExposureLog.objects.bulk_create(batch, batch_size=1000)这套做法放到答辩里就是实打实的工程能力体现。老师问“你数据量这么大,怎么保证插入效率”,你有答案,而且是可以直接演示的代码。
5. 前端Vue可视化大屏与核心页面
5.1 可视化大屏的布局思路
前端是整个项目的“脸面”,老师打开系统第一眼看的就是这里。大屏我建议分成这样几个区域:
顶部放核心KPI卡片,包括广告曝光总量、点击率、转化率、预测购买总人数,这些数据实时刷新,让老师第一眼就感知到“这个系统是有数据在流动的”。
左侧放用户画像区域,用条形图展示不同社区的用户数量分布,用词云展示高频购买商品类目,用箱线图展示不同年龄段的消费能力分布。
中间放核心图表,最重要的是漏斗图,展示从曝光到点击再到购买的全链路转化情况,这是整个系统最直观的业务价值;旁边放折线图展示近30天广告效果趋势。
右侧放模型相关结果,用雷达图展示不同广告类型的转化快照,用散点图展示用户购买概率分布,用列表展示高购买概率用户Top10。
5.2 关键代码结构与请求封装
前端工程可以用Vue CLI或者Vite创建,路由用Vue Router,状态管理用Pinia。请求封装建议统一放在一个api目录里:
// api/ads.js import request from '@/utils/request' export function getAdList(params) { return request({ url: '/api/ads/', method: 'get', params }) } export function getAdStats(adId) { return request({ url: `/api/ads/${adId}/stats/`, method: 'get' }) }封装的要点有两个:一是统一处理Token认证,在请求拦截器里带上JWT Token;二是统一处理错误码,避免每个页面重复写try-catch。Vue组件里调用ECharts初始化图表时,记得在beforeDestroy阶段销毁实例,否则切换路由后图表会重复创建,内存泄漏且页面卡顿。
5.3 与后端联调的那些坑
前后端联调是毕设项目中耗时比重很大的环节,很多时间都耗在几个固定问题上:
跨域问题是最常见的,Django后端需要配置django-cors-headers,并且在settings.py中把前端开发服务器的地址加入白名单。Vue生产环境解决跨域,可以在后端配置静态文件托管,或者部署时由Nginx统一转发,让前后端在同一个域下。
时间格式化问题也让人头疼,前端拿到的ISO时间字符串要转成本地时间,建议在请求封装里统一拦截处理,不要每个图表单独转换。
字段命名不一致问题,因为Python端习惯用snake_case,JavaScript端习惯用camelCase,如果不想在后端到处改名,可以在前端请求层加一个转换函数,把下划线字段统一转驼峰。
6. 模型预测效果的调优与验证
6.1 训练脚本设计
模型训练脚本是整个深度学习模块的核心,建议设计成可重复执行的命令行工具:
python scripts/train_model.py --epochs 30 --batch_size 256 --embedding_dim 16 --hidden_units 256 128 64 --lr 0.001脚本内部流程是:加载训练数据,做特征预处理与编码映射,切分数据集,初始化DeepFM模型,进入训练循环同时打印每个epoch的损失和验证集AUC,训练完成后输出特征重要性报告,保存模型文件和特征映射文件。
这里有个关键的实操细节:特征映射的字典一定要和模型一起保存。很多毕设里模型跑完,等到部署到线上,却发现新数据和训练时的特征编码对不上,原因就是只存了模型权重没存特征映射表。踩过一次这个坑之后,我都是把特征配置序列化成
feature_config.json和模型放同一个目录,推理时强制加载。
6.2 模型效果不理想时的排查路径
如果验证集AUC始终在0.6以下,不要急着换网络结构,先检查这些常规问题:
一类是数据泄漏问题,检查特征里是否混入了“未来信息”。比如用“广告曝光后用户的购买行为”去预测“该用户点不点击”,这在逻辑上就是倒因为果。另一类是正负样本不平衡问题,购买行为本来就是稀疏事件,如果负样本远多于正样本,直接用交叉熵训练会导致模型全预测成负类。解决方案是负样本下采样,或者给少数类提高损失权重。还有一类是特征范围问题,检查数值型特征有没有做归一化,有没有极端离群值被原样丢进模型。
AUC到0.7以上后,可以考虑做得更细一点:对不同社区、不同广告类型分别计算AUC,判断模型在哪些业务子域上还比较弱,再针对性补充特征。这样训练产出的不仅是模型,还有业务洞察,论文素材也厚了。
6.3 预测服务的封装
预测模块的输出要对接给前端展示,所以封装一个干净的推理接口很重要:
# apps/ml/services.py class PurchasePredictor: def __init__(self, model_path, feature_config): self.model = load_model(model_path) self.config = load_json(feature_config) def predict(self, user_info, ad_info): features = self.build_features(user_info, ad_info) prob = self.model.predict(features) label = '高意向' if prob > 0.8 else ('中意向' if prob > 0.5 else '低意向') return {'probability': round(prob, 4), 'level': label}在实际接口里传入用户ID和广告ID,从数据库拉取实时数据拼接特征,再调用上面的预测方法。注意实时特征与离线训练特征的来源要一致,否则会出现离线指标高、线上效果差的情况。
7. 从原型到答辩的全流程时间规划
7.1 分阶段拆解任务
这类完整系统项目按8周时间推进,节奏会比较从容。第1周搭项目脚手架,确定前后端的工程结构,跑通ADMIN和基础页面。第2周完成数据库建模,开发广告管理和用户管理模块。第3周实现行为日志采集接口,同时写数据生成脚本,产出第一批可用的测试数据。第4-5周专注特征工程和模型训练,把离线指标调到目标区间。第6周集中做前端可视化大屏,把后端接口全部打通。第7周做系统联调和体验优化,重点检查异常场景和边界条件。第8周写论文、录制演示视频、准备PPT答辩。
这里有一个容易被忽视的环节:数据生成脚本一定要早写早跑。很多同学等到第5周才想起来需要数据,结果临时生成的数据质量差,特征分布不均衡,模型训练效果一塌糊涂。数据脚本要支持随机种子设置、社区-用户-广告关联的幂等生成,方便反复迭代。
7.2 答辩演示的节奏控制
演示系统时默认时间是5-8分钟,节奏建议这样分配:先花半分钟讲清楚项目要解决什么问题,再花两分钟演示大屏,先拉到总览指标,然后点进某个广告位看漏斗图,再展示一个高意向用户的画像标签页面。这是视觉效果最强的部分,要一气呵成,不要中途去切后台。花两分钟讲技术架构和模型部分,画一个简单架构图配讲解,说明Django提供数据接口,Vue做可视化,深度学习模型做概率预估,异步任务处理批量日志。最后一分钟展示难点和亮点,比如“批量写入的性能优化”“特征映射与模型保存的一致性方案”“按时间序列切分数据避免数据泄漏”这些点,全部用具体数据和效果佐证。
讲系统演示前有个准备工作值得做:把浏览器缓存提前清干净,把接口网络调稳定,离线数据准备好。演示系统最怕的就是网络抖动导致接口超时,老师看着转圈,你的气势就弱了一截。如果条件允许,把所有静态资源在本地起服务,完全没有网络依赖是更保险的方案。
7.3 高频答辩问题与应对思路
围绕这个选题,答辩老师反复问的问题集中在几个方向,可以提前把答案准备好:
“你的数据和真实场景一致吗?”回答思路:以公开行为数据为基础,结合社区广告投放的业务逻辑进行合理建模和补充,并做了统计分析保证数据分布符合实际规律。数据来源的局限性和应对方案都在论文里做了说明。
“为什么选择DeepFM?”回答思路:从特征交互的角度讲。传统逻辑回归无法学习特征的高阶交互,FM能建模二阶交互,DeepFM在FM之上引入深度部分建模高阶交互,同时保持了低阶部分的记忆能力。这个模型在CTR预估场景中已有广泛验证,理论上有据可依。
“这个模型除了准确率,还能给业务带来什么?”回答思路:可以从广告定向投放和预算分配的角度来讲。模型输出的购买概率可以对用户分群,针对高分群体定向推荐,针对低分群体优化广告创意,实现预算的高效利用。这既是技术分析,也是业务落地价值。
“你的深度学习体现在什么地方?”回答思路:一是用户和社区的Embedding表示学习;二是DeepFM网络结构对特征交互的建模;三是端到端的训练方式。把这三条讲清楚,深度学习就不是贴标签,而是实打实的技术手段。
8. 踩坑经验总结
做这个项目踩过的坑不少,挑几个最典型的记录一下,帮后面的人少走弯路。
第一个坑是数据生成和模型训练脱节。最开始写完数据脚本就丢一边,直到做模型时才跑了一版数据,结果发现数据里用户ID和广告ID不连续,Embedding层输入维度对不上。所以数据生成脚本必须和特征工程脚本用一个配置源,最好同一个时间点一起跑。
第二个坑是前端图表在大数据量下的卡顿问题。ECharts渲染几千个点没问题,但上万个点就会明显卡顿,而且大数据进行实时滚动刷新时,页面极其容易卡死。解决办法是后端接口做成聚合数据,前端只拿聚合结果,不做全量数据下发的操作。同时把图表动画关闭,Degrade模式打开,性能能提升一大截。
第三个坑是模型按时序切分时,测试集数据分布与训练集差异很大,导致验证集AUC不错但线上效果回退。处理办法是把特征里所有强时序敏感的特征单独列出来,做分位点归一化,并且用滑动窗口的方式重置特征统计值,让时序漂移对结果的影响可控。
第四个坑是Django的Admin后台出现了中文乱码和时区问题。把settings.py里的LANGUAGE_CODE和TIME_ZONE都改成国内时区配置,并且统一用django.utils.timezone.now()来取时间,不要在业务代码里直接取datetime.now(),否则入库时间全是UTC时间,可视化大屏的时间轴全乱了。
最后一个值得说的心态问题。答辩系统演示时,老师经常会当场改参数或者随机点页面。比如突然问你“把模型阈值从0.5改成0.7会怎么样”,如果现场改不动或者报错,气势会受损。所以演示前建议把所有暴露给老师的控件都做成可交互的,预测概率、阈值调节、社区筛选、时间范围选择这几个核心交互点都要真正可用。
根据我个人的经验,这类毕设项目的完成度高低,往往不取决于算法有多高级,而取决于整个数据链路是否完整、可视化是否直观、答辩表述是否能把业务逻辑和技术方案串成一条清晰的故事线。你把社区广告怎样影响消费决策这条逻辑走通,把Django和Vue这条工程链路跑顺,把模型的效果和局限说清楚,这个项目就是一份拿得出手的作品。如果后续还有时间,可以往工程化方向再走一步——把模型预测封装成可配置的API服务,加入特征归因分析模块,或者尝试对比几种不同模型的实验结论,这些都是能让项目再上一个台阶的扩展点。