做毕业设计最怕的不是题目难,而是题目听着热闹、做出来没东西。就拿“餐饮评论情感分析 + 餐厅推荐系统”这个方向来说,每年都有一大批人选,但最后交上来的东西大多长一个样:抓了几千条评论,用现成的情感分析库打个分,再套一个协同过滤公式算出推荐列表。看起来功能齐全,可答辩时老师只要问一句“你的情感分析跟直接看星级有什么区别”“大模型在你系统里到底解决了什么问题”,基本就卡壳了。
这篇我把参与指导这类项目时沉淀下来的一套完整思路写透:数据怎么拿、怎么洗,情感分析怎么选型、怎么做多维度拆解,推荐算法怎么把情感分数真正用起来,最后聊论文、PPT和答辩怎么准备。适合正在做这块毕业设计的人,也适合想认真做一个能上台面的美食推荐系统的开发者。核心就一句话:大模型不是拿来秀的,而是要把“评论里到底在夸什么、骂什么”这件事,拆成可量化、可参与计算的结果。
1. 这个题目拆开来看:评分、评论情感与推荐之间到底什么关系
1.1 评分和真实口碑之间有一条鸿沟
很多同学拿到这个题目的第一反应是“有评分为什么还要做情感分析”。这是整个项目最该想清楚的问题,也是答辩时最容易出彩的切入点。
我见过太多店,评分和评论内容严重不一致。比如一家店综合评分4.2,翻评论全在夸环境好、出片率高,但菜品评价里“太咸”“食材不新鲜”反复出现;另一家3.8分,差评集中在“位置偏、等位久”,口味评价却是清一色的“很正”“会二刷”。如果只看数字评分,这两家店在推荐系统里的差别几乎可以忽略,但用户的真实体验完全不同。
情感分析的真正价值就在这里:把非结构化的文本转成结构化的、可计算的信号。评分是用户冲动之下点出来的结果,评论才是用户愿意花时间写下来的理由。推荐系统要吃的是“理由”,不是“结果”。
1.2 系统拆开来只有四个模块
这个题目的完整系统并不复杂,拆开看就四块:
- 数据采集与清洗:拿到店铺信息、评论文本、用户行为等原始数据,并处理成干净可用的结构。
- 情感分析引擎:用大模型对评论做多维度情感打分,输出口味、服务、环境、价格、整体五个维度的量化结果。
- 推荐算法引擎:基于用户历史行为和情感分析结果,做用户协同过滤、物品协同过滤、情感匹配的融合排序。
- Web展示层:用户登录、浏览店铺、查看情感分析可视化结果、获取带理由的推荐列表。
其中情感分析引擎是整个系统的大脑,推荐算法是骨架,Web展示是脸面。三者缺一不可,但精力分配应该是 5:3:2,最忌讳把一半时间花在调前端样式上。
1.3 这个选题为什么值得做
作为毕业设计,这个题目有三个天然加分点。第一,覆盖完整链路,从数据处理到算法到工程实现都有,写论文时每一章都有内容可写。第二,大模型是热点,但大多数同学只是“接了个API”,如果你能把prompt设计、维度拆解、评测对比做扎实,就明显拉开差距。第三,推荐结果可以做到可解释——“因为你喜欢口味偏重的川菜,为你推荐这家口味分高达0.92的店”,这种功能在演示时非常直观。
2. 评论数据的合规获取与预处理:别让数据环节拖垮整个项目
2.1 学术研究场景下的数据来源边界
先把这个最容易被追问的问题说清楚。毕业设计属于学术研究/教学演示场景,可以使用公开可见的评论数据,但有几个边界必须守:只采集公开页面能直接看到的内容,不碰用户隐私字段(手机号、昵称真实信息等),采集频率控制在正常人手动浏览的节奏,不绕过平台的技术保护措施,不用于任何商业用途。
另一个更省事的方案是使用公开的数据集。国内有一些高校和研究者开放过餐饮评论语料,格式通常是“店铺ID、评分、评论内容、时间戳”,拿来做情感分析和推荐完全够用。如果导师要求必须体现“数据采集”能力,再考虑自己写采集脚本,但采集量控制在几千条量级即可——这个项目演示的是算法和系统能力,不是爬虫技术。
2.2 需要哪些数据字段
我建议数据层至少设计五张表,字段不用多,够用就行:
- 店铺表:shop_id、店铺名、分类标签(川菜/火锅/日料等)、人均价格、区域、综合评分、评论数。
- 评论表:review_id、shop_id、user_id、评分、评论文本、评论时间。
- 用户表:user_id、匿名化标识、注册时间(如果拿不到,就只用user_id做行为关联)。
- 情感结果表:review_id、整体情感、口味分、服务分、环境分、价格分、关键句、分析时间。
- 推荐记录表:user_id、推荐店铺列表、推荐理由、生成时间。
user_id必须做匿名化处理,这是红线。评论表和用户表之间只存匿名ID,不要在系统里保留任何可能定位到个人的信息。
2.3 清洗规则和标注过程
原始评论直接进模型会出很多幺蛾子。我自己常用的清洗规则可以列一份,照着做基本能挡住绝大多数脏数据:
- 去掉重复文本。很多店铺存在同一条评论被复制到多个店铺的情况,按评论内容做MD5去重,再配合“同一用户对同一店铺只保留最新一条”的规则。
- 过滤短评论。少于5个字符的评论信息量太低,比如“可以”“不错”,这类数据会让模型学到噪音。
- 处理特殊符号。评论里大量出现emoji、表情、HTML实体、@用户等,统一用正则清洗或替换成占位符。注意别把emoji全删了——😡和😋在情感上是有信息的,可以映射成“生气/开心”标签。
- 识别无意义评论。“打卡”“路过”“沙发”这类灌水内容,用关键词表过滤。
- 评论归属纠偏。连锁品牌经常出现用户在某家分店评论里抱怨另一家分店的情况,至少要把“店名”和“评论中提到其他店名”的冲突标记出来,便于后续处理。
- 统一文本格式。全角半角、大小写、繁体简体,都归一化一下。餐饮评论里繁体不多,但偶尔有,统一转简体有助于分词和模型理解。
清洗之后还需要人工标注一小批验证集。我的做法是随机抽200条评论,按“好评/中评/差评”和“口味/服务/环境/价格”两个维度分别标注,作为后面评测情感分析准确率的基准。这一步千万别省,否则你后面说“准确率91%”没有任何依据。
3. 情感分析怎么选型:传统方案和大模型方案的实测对比
3.1 词典法和机器学习法能到什么程度
很多教程会让你用情感词典或者TF-IDF加朴素贝叶斯,这些方法不是不能用,而是天花板很矮。
情感词典法(比如经典的知网情感词典、BosonNLP词典)实现最简单:分词后统计评论里积极词和消极词的个数,加减抵消得到情感分。但餐饮评论是重灾区,网络新词太多了,“yyds”“绝绝子”“踩雷”“齁咸”这些词在传统词典里根本不存在。更麻烦的是否定句和反讽,比如“这味道,绝了”在不同上下文里方向完全相反,词典法基本无能为力。
机器学习方法(TF-IDF + 逻辑回归/支持向量机)比词典好一些,但依赖标注数据,而且领域迁移差。在影评上训练好的模型拿到餐饮领域立刻失灵,因为“分量足”“排队两小时”“服务员态度”这些特征在影评里没有。自己做标注的话,2000条起步,工作量不小。
我自己在类似项目上做过一轮对比,结果很能说明问题:
| 方案 | 餐饮评论情感准确率 | 实现成本 | 可解释性 | 维度分析能力 |
|---|---|---|---|---|
| 情感词典法 | 72%左右 | 低 | 一般 | 无 |
| TF-IDF + LR | 78%左右 | 中(需标注) | 弱 | 无 |
| 大模型 few-shot | 91%左右 | 高 | 强 | 强 |
大模型方案准确率高出一截,而且自带多维度分析能力。对于毕业设计来说,这个差距足够让你在“为什么要用大模型”这个问题上站得住脚。
3.2 大模型做情感分析的Prompt设计要点
大模型方案的核心不在模型本身,而在Prompt。我调试过很多版,最后沉淀出一套稳定的模板,要点是:结构化输出、限定取值范围、强制JSON格式、温度归零。
你是一名餐饮点评分析助手。请对下面这条餐饮评论做多维度情感分析。 只输出JSON,不要输出任何解释。 评论:{text} 输出格式: { "整体情感": 0.0~1.0, "口味": 0.0~1.0, "服务": 0.0~1.0, "环境": 0.0~1.0, "价格": 0.0~1.0, "情感倾向": "好评" 或 "中评" 或 "差评", "关键句": ["句子1", "句子2"] } 规则: 1. 1.0表示非常满意,0.0表示非常不满意。 2. 评论没有提到的维度输出 null。 3. 关键句从评论中原文摘取,用于推荐理由展示。几个细节值得注意。temperature必须设为0,否则同一句话两次分析结果可能不一样,推荐系统拿到波动的情感分数会很不稳定。response_format如果服务商支持JSON模式就一定要开,能大幅降低输出解析失败的概率。关键句字段非常有用,它让推荐理由有了“人话”可以展示。
另外一定要给1到2个few-shot例子,尤其是负面的例子。大模型默认倾向于输出“好评”——后面会细讲这个坑,few-shot里放一个明显的差评示例能显著改善。
3.3 餐饮场景的多维度情感拆解
为什么非要拆维度?因为餐饮评论的信息密度极高。看这条:“菜很不错,就是等位太久,环境有点吵。”整体上用户是满意的,但服务和环境是负面信号。如果只输出一个整体情感分,这条评论的信息就浪费了。
拆成口味、服务、环境、价格四个维度之后,每个店铺都能形成一个四维情感画像。这个画像可以直接参与推荐计算:用户之前常去的店都是“环境分高”的,系统就倾向于推荐环境分同样高的店。这就是维度拆解的价值——它让情感分析的结果和推荐算法做到了真正打通,而不是两张皮。
我还建议在情感结果表里存下关键句。推荐理由模板里直接引用原文,比如“因为你在XX店的评论里提到‘环境安静适合聊天’,为你推荐环境情感分排名前10%的YY店”,这种可解释推荐在答辩演示时非常有冲击力。
4. 推荐算法设计:情感分数如何变成用户想看的推荐理由
4.1 基于用户的协同过滤怎么和情感分数结合
先明确一点:只靠协同过滤做餐厅推荐,效果会很差。原因很简单,用户对餐厅的评分数据是极度稀疏的,一个普通用户一年在点评平台上留下的评论可能只有几十条,计算用户相似度时矩阵稀疏得可怜。
我的做法是给用户建一个“情感偏好向量”。对用户评论过的所有店铺,把情感分析结果按维度取均值,得到形如 [口味0.85, 服务0.6, 环境0.7, 价格0.8] 的向量。两个用户的偏好向量做余弦相似度,这样即使他们评论过的店铺完全没有交集,只要维度偏好接近,依然能建立相似关系。
这一步是整个推荐系统的核心创新点,也是论文里最能写的地方:传统协同过滤在“物品”上找相似,这里先在“情感维度”上找相似,数据稀疏问题被明显缓解。
4.2 基于物品的协同过滤和冷启动处理
物品协同过滤在餐饮场景里的逻辑是:喜欢A店的人也喜欢B店,那么A和B是相似店铺。这个逻辑对连锁品牌、同菜系店铺是有效的,但对新店完全失效——新店没有用户行为数据,无法计算相似度,这就是冷启动问题。
冷启动阶段的兜底策略有三层:
- 情感分过滤:新店如果评论数太少,情感分不可信,用威尔逊区间下限做平滑,评论数低于阈值的店先不进推荐候选池。
- 热度补偿:没有行为数据时,用“区域 + 分类 + 情感分 + 评论数”做规则召回,本质上是展示优质热门店。
- 探索机制:推荐列表里固定留一行“你可能没试过的口碑小店”,从低评论数但高情感分的店里随机选,保证系统不是永远推头部。
4.3 融合排序公式与可解释推荐
最后所有信号要合成一个排序分数。我用的融合公式长这样:
最终得分 = α × 用户协同过滤分 + β × 物品协同过滤分 + γ × 情感匹配分 + 热度补偿分超参数α、β、γ用网格搜索调优,评估指标是推荐列表的Precision@5和Recall@5。调参的时候你会发现情感匹配分的权重γ太高会导致结果过于保守——用户喜欢什么就永远推什么,缺少惊喜;γ太低又跟普通协同过滤没区别。我调下来比较稳的组合是 α=0.35, β=0.30, γ=0.35。
情感匹配分的计算逻辑是:把用户偏好向量和候选店铺的情感画像向量做余弦相似度,再根据用户当前场景动态调整维度权重。比如用户搜索时选了“聚会聚餐”场景,环境和服务维度权重就往上调;选了“一人食”场景,口味和价格权重更高。
推荐理由的生成直接用情感关键句和维度分拼模板,不用大模型现场生成,省时省钱又稳定。我存了一个理由模板表,按匹配到的维度去填充,效果已经足够好。
5. 系统落地的架构与代码细节:从API调用到前端呈现
5.1 技术栈选型和理由
这个项目我推荐的组合是:Python 3.10 + FastAPI + MySQL + Redis + Vue + ECharts。选型逻辑很清楚:
FastAPI天然支持异步,大模型API调用是典型的IO密集型操作,同步框架会在这里卡死;MySQL存结构化数据最稳,情感分析结果、店铺信息、用户行为都适合关系型存储;Redis用来缓存情感分析结果和推荐列表——同一批评论没必要重复调用大模型;前端用Vue加ECharts,因为ECharts画雷达图几乎是零成本,而雷达图展示店铺的多维情感画像特别直观。
如果不想写复杂前端,直接用FastAPI挂静态页面加一点原生JS也完全够用。毕业设计评分看的是完整度和逻辑,不会因为你用了重型前端框架加分。
5.2 核心代码:大模型情感分析服务
情感分析服务是整个系统的发动机,代码封装要注意三个点:异步并发、超时重试、结果缓存。给你一个可以直接改着用的骨架:
import asyncio import json import httpx SENTIMENT_PROMPT = """ 你是一名餐饮点评分析助手。请对下面这条餐饮评论做多维度情感分析。 只输出JSON,不要输出任何解释。 评论:{text} (输出格式与规则略,见前文模板) """ async def analyze_one(client: httpx.AsyncClient, text: str, cfg: dict) -> dict: payload = { "model": cfg["model_name"], "messages": [ {"role": "system", "content": "你只输出JSON。"}, {"role": "user", "content": SENTIMENT_PROMPT.format(text=text)}, ], "temperature": 0, "response_format": {"type": "json_object"}, } resp = await client.post( cfg["api_url"], json=payload, headers={"Authorization": f"Bearer {cfg['api_key']}"}, timeout=30, ) content = resp.json()["choices"][0]["message"]["content"] return json.loads(content) async def analyze_batch(reviews: list[str], cfg: dict, concurrency: int = 5) -> list[dict]: sem = asyncio.Semaphore(concurrency) async def worker(text: str): async with sem: for attempt in range(3): try: async with httpx.AsyncClient() as client: return await analyze_one(client, text, cfg) except Exception as exc: if attempt == 2: return {"整体情感": None, "error": str(exc)} await asyncio.sleep(2 ** attempt) return await asyncio.gather(*(worker(t) for t in reviews))信号量控制并发数到5,防止瞬间打爆API配额;三次指数退避重试,对付偶发超时;结果入库前先查Redis,命中缓存直接跳过调用。这三个套路能让几千条评论的离线分析在十几分钟内跑完,而不会频繁报错。
5.3 数据库和缓存设计
情感结果表是核心,建议加一个review_id唯一索引,防止重复分析。Redis的key可以设计成sentiment:{review_id},命中就直接读JSON。推荐列表的缓存key是rec:{user_id}:{scene},TTL设一个小时——用户行为变化不会那么快,一个小时内的推荐结果一致性是可以接受的。
5.4 前端展示什么才不显得空洞
前端页面不用多,三个就够:首页搜索和推荐列表、店铺详情页、个人中心(历史浏览+情感偏好画像)。店铺详情页是最能展示项目深度的地方,放一张ECharts雷达图,五个维度(口味、服务、环境、价格、整体)一目了然,下面跟着情感关键句列表,再下面是“为你推荐的理由”。
推荐列表每一项都显示“推荐理由”标签,比如“口味高度匹配”“环境安静适合约会”“高性价比之选”。这些标签全部来自情感分析结果和推荐算法的匹配逻辑,不是写死的,这是整个系统最显功力的部分。
6. 评测指标与踩坑优化:一次真实的调优过程
6.1 评测体系怎么搭
没有评测,论文里的“系统测试”一章就是凑字数。我的做法是把评测分成两条线。
第一条线评测情感分析模块:拿人工标注的200条验证集,算情感倾向分类的准确率,以及各维度分数的平均绝对误差。第二条线评测推荐模块:把用户历史行为按时间切分,前80%做训练,后20%做验证,算Precision@5、Recall@5、覆盖率(推荐结果覆盖多少不同店铺)、新颖度(非热门店铺在推荐列表中的占比)。
这几个指标算下来,论文的“系统测试”章节就有真材实料了,而且能画出调参曲线图——评审老师很吃这一套。
6.2 三个典型的坑和完整排查链路
坑一:批量分析时大量超时。现象是跑500条评论停了两次,每次都要手动重启。排查过程:先看日志确认是HTTP超时还是解析错误,发现是Timeout;再看代码发现用的是同步requests循环调用,一条一条串行导致整体耗时爆炸。解决方案:全部改成asyncio并发,信号量限制并发数,加上重试。改完后500条评论从40分钟降到12分钟。
坑二:大模型对负面评论的判断严重偏乐观。现象是情感倾向准确率只有84%,把所有差评都判成了好评。排查过程:随机抽样看原始输出,发现模型对“一般般”“凑合”“不会再来了”这类委婉负面表达识别不出来;进一步分析是训练数据本身好评占比高,模型天然偏向正面。解决方案:Prompt里加入负面样本的few-shot示例,并在输出规则里加一条“当用户表达失望、抱怨、不再回购意向时,情感倾向必须为差评”。改完后准确率从84%升到91.5%。
坑三:推荐结果被热门店霸榜。现象是覆盖率只有12%,推荐来推荐去永远是那几十家评论数最多的店。排查过程:打印融合排序的每一项分数,发现评论数对热度补偿分的影响过大,导致情感匹配分被稀释。解决方案:把热度补偿从线性加成改成饱和函数,评论数超过200后补偿不再增加;同时引入探索机制,每屏推荐至少保留一个“口碑新店”位。改完后覆盖率升到31%,Precision@5只下降了不到2个百分点,整体可接受。
6.3 优化效果的量化对比
| 指标 | 初始版本 | 优化后 |
|---|---|---|
| 情感倾向准确率 | 84% | 91.5% |
| 500条评论分析耗时 | 40分钟 | 12分钟 |
| 推荐覆盖率 | 12% | 31% |
| Precision@5 | 38% | 36% |
这个表格直接放进论文测试章节,每一项都有对应的技术改进解释。老师问起来你能讲清楚“为什么变好了”,这就叫闭环。
7. 毕业论文、PPT与答辩准备的实战心得
7.1 论文结构怎么安排才能突出工作量
论文框架用标准的“绪论—相关技术—需求分析—系统设计—系统实现—系统测试—总结”即可,但有两个章节必须重点写。
相关技术章里,大模型部分不要只写“大模型是什么”,要写你的Prompt设计思路和迭代过程,附上不同版本Prompt的对比效果。系统设计章里,情感分析模块的时序逻辑要画清楚(评论采集→清洗→情感分析→结果入库→推荐计算→前端展示),每个环节的输入输出都写明白。系统实现章里放关键代码片段,注意代码截图要清晰,变量命名要规范。系统测试章放上面那张优化对比表,配调参过程。
论文最容易出现的毛病是“大而空”。我见过太多人写“系统性能良好”“推荐结果准确”,没有任何数据支撑。记住一条铁律:论文里每个结论都必须能指向你自己的测试数据,哪怕数据再朴素,也是你的。
7.2 PPT演示的节奏控制
答辩PPT控制在10到12页,节奏是:问题背景2页,技术选型1页,系统架构1页,核心算法2页(大模型Prompt设计和推荐融合算法各1页),系统演示3页,测试结果1页,总结展望1页。
演示环节最容易翻车,给你三条保命经验。第一,所有展示用的推荐结果和情感分析结果,答辩前预先算好缓存住,不要现场实时调大模型API——网络波动、接口限流、额度耗尽,任何一个都能让你当场社死。第二,准备一套完整的备用截图,万一现场投屏出问题,直接放截图也能把流程讲完。第三,演示时先展示结果再讲原理,老师对“这个推荐理由是怎么生成的”的兴趣,远大于对页面UI的兴趣。
7.3 答辩高频问题与应答思路
我总结过这个题目答辩时最常被问到的几个问题,提前准备答案,基本不会冷场。
“为什么用大模型而不用传统情感词典?”答:做过对比实验,词典法在餐饮评论上的准确率约72%,大模型few-shot方案约91.5%,差距主要来自网络新词、委婉表达和讽刺语气的识别。同时大模型能输出多维度分析结果,这是推荐算法需要的关键输入。
“大模型会编造内容,你怎么保证分析结果可信?”答:三个方面控制——temperature设为0保证可复现;输出强制JSON格式;人工标注200条验证集持续评测准确率。再从抽样结果看,未发现模型中编造评论内容的情况。
“你的协同过滤和普通协同过滤比,改进在哪里?”答:传统协同过滤依赖用户对物品的评分行为,数据稀疏时效果差。我的方案先用大模型把评论转成情感偏好向量,在维度层面计算用户相似度,缓解了稀疏问题,也提升了可解释性。
“推荐系统怎么评价好坏?”答:离线评测用Precision@5、Recall@5、覆盖率和ABA测试,另外用案例分析的方式展示推荐理由是否合理。如果时间允许,可以提一句做了TopK参数的敏感性分析,看不同K值下指标的变化。
最后再分享一个很实在的经验:这个项目做完之后,把情感分析模块单独抽出来做成一个可复用的服务,对你后续找工作或者做其他题目都是加分项。我在实际参与过程中最大的体会是,毕业设计不是“把功能堆出来”,而是“把每个环节为什么这么做讲清楚”。数据、情感分析、推荐算法、系统实现、评测这五环只要扣在一起,答辩时你就没有什么可慌的。