基于Streamlit与多模态RAG的电影推荐系统构建 1. 毕设选题为什么是Streamlit多模态RAG电影推荐系统先聊点实在的。每年到毕设季电影推荐系统几乎是计算机专业“腐朽最重灾区”的选题之一。你去知网或者毕业设计展厅扫一圈十个推荐系统八个是协同过滤一个是基于内容的相似度计算还有一个是深度学习的Embedding拼接。不是说这些老思路做不出来而是到了现在这个时间点纯协同过滤或者纯内容推荐很难再撑起一个让人眼前一亮的答辩PPT。评委第一句话往往不是“你做了什么”而是“你这个工作和已有的系统有什么区别”。如果你也是冲着“大数据深度学习”方向去选电影推荐这个题目我强烈建议你在架构上换一个角度切入把“推荐”这件事从传统的评分预测转成“基于内容理解的语义检索生成式解释”。用大白话说就是让系统不仅能告诉你“推荐哪部电影”还能告诉你“为什么推荐这部”。这才是我最终把题目定为“Streamlit多模态RAG电影推荐系统”的核心动机。这篇文章我不会给你贴一个特别完整的毕设代码仓库因为那东西你应该自己写。我更想把从选题、数据处理、向量化、RAG检索、界面搭建到答辩演示的完整路径讲清楚尤其是那些文档里不会写、只有自己踩过坑才会知道的细节。适合谁看适合那些正在做推荐系统类毕设、想用较新架构做出差异化亮点、又不想把自己折腾死在深度学习训练上的同学。当然如果你只是对多模态RAG感兴趣想找一个能快速看到效果的应用场景这篇也能当你的入门参考。2. 数据准备与多模态向量化2.1 数据集选型第一道分水岭电影推荐方向可用的数据集其实不少但绝大多数人第一反应就是用某经典电影数据集。那个数据集的好处是干净、格式统一、用户评分记录多但它有一个致命问题信息维度太单薄。你拿到的往往只是一张评分表加一份电影基本信息表连电影简介都写得像填空题海报这种视觉信息更是想都不要想。做多模态RAG必须保证数据源里“模态够多”。我最后选的是某开源电影全集数据集里面包含电影标题、类型、导演、演员、简介、关键词标签、海报图片路径、用户评分记录。这个数据集的单条电影信息比较丰富而且海报是真实可下载的这就为视觉模态提供了前提。选数据集时有几个硬性标准你可以直接拿去用至少有文本简介而且简介长度不能太短否则向量化之后区分度非常差。有海报或者剧照这样才能做图像模态。有评分或者收藏记录否则协同过滤部分没法做。数据量不用太大5000部电影以内完全够用。多模态向量化虽然不训练模型但要调embedding接口数据量一大时间和钱都扛不住。我当时实际清洗下来大概保留了4200部电影。这个量级对毕设来说刚刚好既能让推荐结果看起来有随机性和丰富度又不会让向量库构建时间超过半小时。2.2 文本向量化不是随便调个接口就完事文本模态覆盖的是电影的简介、类型、关键词和导演演员信息。很多人做RAG文本向量化直接拿一个开源的embedding模型把所有字段拼接成一大段字符串塞进去然后发现检索结果乱七八糟。这里面的核心问题不是模型不行而是拼接方式不对。我建议把文本分成两个层次来做向量第一层是“短文本属性向量”主要编码类型、关键词、导演、演员、年份这种结构化的标签信息。这一层向量负责的是精确匹配比如用户说“我想看诺兰的科幻片”它能在语义空间里快速锁定对应电影。第二层是“长文本语义向量”主要编码电影简介和短评。这一层向量负责的是泛化理解比如用户说“我想看那种烧脑、时空循环、结局反转的电影”它能在语义层面对齐到《恐怖游轮》《源代码》这一类片子上。两个向量分开存后面做RAG召回的时候可以分开做相似度计算再按权重融合。千万不要偷懒把所有字段拼成一个向量那样等于把精确匹配和语义匹配混在一个空间里两头都做不好。向量化的具体实现有条件的可以直接调商用embedding服务效果稳定没条件的可以用开源的句子向量模型。要注意的是中文文本和英文文本的向量化模型最好分开或者用多语模型别拿一个纯中文模型去编码英文人名否则效果会很离谱。2.3 海报向量化不是所有图都适合直接进向量库图像模态是这个项目的亮点也是大多数人翻车的地方。海报向量化不是简单地把图片resize成一个固定尺寸然后丢给图像模型提取特征就行。关键问题是海报的风格差异极大有些是真人剧照有些是极简插画有些是文字海报。如果用同一个全图特征去编码可能出现“把两张颜色相近但内容毫无关系的海报判定为高度相似”的乌龙。我的做法是先对海报做主体区域提取把画面中心区域裁出来再送到图像embedding模型里取特征向量。这个过程不复杂用开源的图像特征提取模型就能完成输出维度一般是512维或者768维。建议把特征向量做L2归一化后面做余弦相似度时数值更稳定。另外海报图像建议统一resize到224x224或者256x256。分辨率不是越高越好因为图像embedding模型的设计输入尺寸就那么大原图分辨率再高模型内部也只会做缩放。分辨率统一还能大幅提升批量向量化的速度。2.4 文本向量与图像向量的空间对齐多模态RAG和单模态RAG最大的区别在于文本向量和图像向量本身不在同一个语义空间里你必须有一个“对齐”的手段。常见的对齐方式有三种第一种是把图像向量和文本向量拼接成一个长向量再进索引。这种方式简单但带来的问题是两种模态的数值范围可能不一致容易造成某一方主导。第二种是分别建索引查询时分别召回最后做结果融合。这种方式灵活可控也是我在毕设里采用的方式。第三种是使用多模态对齐模型比如通过对比学习训练过的双塔模型让对应图文对的向量在空间里靠近。这种方式效果最好但需要一定训练成本对毕设来说性价比不高。我采用的方式具体来说是这样分别构建文本向量库和图像向量库文本召回Top30图像召回对应的向量然后利用评分记录做加权重排把最终Top10结果作为候选集。这其实就是RAG里“召回-重排”的经典范式只是把召回分支从单一文本扩展到了“文本图像”两个通道。答辩的时候把这个流程图放PPT上说清楚“为什么要分叉召回——因为不同模态的信息互为补充”评委一般都会点头。向量存储我用的开源向量数据库构建索引速度快支持元数据过滤和相似度检索。4200条文本向量加4200条图像向量加起来不到一万条对嵌入式向量库来说毫无压力跑在普通笔记本上完全没问题。你要是为了显得项目“大”非要去上分布式向量库那纯属给自己找麻烦答辩被问到“数据量多少”就会很难收场。3. 推荐引擎向量召回混合排序个性化解释3.1 RAG召回把推荐问题翻译成检索问题RAG在这里的使用方式和传统的“检索增强生成问答”不太一样。在标准RAG中用户先提问系统检索出相关文档片段再喂给大模型生成答案。而在电影推荐系统里用户的“查询”不是一个明确的问题而是一个模糊的偏好表达比如“我想要那种适合周末一个人看、节奏慢、画面很美、能让人平静下来的电影”。这个模糊偏好恰恰是RAG能发挥价值的地方。具体流程如下用户输入一段自然语言描述先做文本向量化得到查询向量。然后把这个查询向量同时丢到文本向量库和图像向量库里做相似度检索分别取Top30。检索出来的候选电影再结合用户历史评分记录用协同过滤的打分结果做加权融合。最后从Top10候选里把电影的标题、简介、海报、相似度分数、协同过滤预测分数拼成一个上下文喂给大模型让它生成一段“推荐理由”。这个流程最大的优势是系统终于能对用户说人话了。传统推荐系统只会给你一个列表“你可能喜欢《某某电影》”。多模态RAG推荐系统能给的是“看起来你对视觉风格独特的悬疑片有偏好这部片子在摄影美学和叙事结构上都和你最近标记喜欢的《某某某》相近而且和你历史评分最高的导演是同一个人。推荐你在周末的晚上戴上耳机从头看到尾。”别小看这段推荐理由。对毕设答辩来说这几乎是全场最亮眼的演示环节因为它把“深度学习模型内部发生了什么”这种黑盒问题变成了“系统如何理解我的意思并生成反馈”这种看得见摸得着的交互体验。3.2 混合排序多模态召回结果不能直接拼在一起多通道召回的原始结果直接拼接使用会出问题。文本通道召回来的电影和图像通道召回来的电影相似度分数分布可能不一样。文本相似度普遍偏高图像相似度普遍偏低你直接把两个通道的分数放一起排序文本通道会全面碾压图像通道图像模态就白做了。我当时第一次拼接时Top10里九部都是文本召回来的海报好看但语义不匹配的电影全被挤掉了。后来才发现要做分数归一化。具体做法是对每个通道召回的Top30在通道内部做一次Min-Max归一化把分数压缩到[0,1]区间。然后按下面这个公式算一个融合分最终得分 0.45 x 文本相似度 0.35 x 图像相似度 0.20 x 协同过滤预测分三个系数是可以调的我最后的实验里文本权重最高因为电影偏好的核心表达还是语义。图像权重次之但它起到了一个很关键的作用当用户只说“画面好看”“色调很冷”“色调温暖”这类视觉倾向的词时图像通道能精准命中而文本通道基本无能为力。协同过滤占比最低它主要起到拉高“和你口味相似的人”作用。融合排序做好后推荐结果的多样性明显上升。这个混合排序的部分建议你在答辩PPT里单独放一页画清楚通道组成和权重配比这属于架构设计层面的加分项。3.3 用大模型生成推荐理由上下文拼装的艺术RAG模式下生成推荐理由的质量一半取决于检索结果另一半取决于你怎么拼上下文。我不建议把整段电影简介原封不动地塞给大模型。你要知道简介是面向所有人的而推荐理由是面向当前这个用户的。你要做的事情是“信息裁剪”。我自己用的上下文模板大概是这样的当前用户的偏好描述用户输入的原文用户最近看过的3部电影及评分候选电影的标题、类型、主演信息从海报向量通道角度提炼出的视觉特征描述候选电影与用户偏好的相似度值把这些信息拼成一段结构化的文字再让大模型生成推荐理由。你会发现模型输出的理由明显更有针对性因为它不是在“介绍电影”而是在“向这个人解释为什么这部片子适合ta”。生成式的推荐理由是这套系统的“记忆点”但要控制大模型接口的调用次数。我当时做了缓存处理同一个候选电影同一个用户偏好组合生成的推荐理由缓存一份再遇到相同组合直接复用大大降低了接口费用和响应时间。4. Streamlit应用层从原型到可答辩Demo4.1 功能页面规划不要一上来就堆组件Streamlit的优势是上手快几行代码就能跑一个界面。但也正因为上手快很多同学会陷入“组件堆积”的误区左一个按钮右一个下拉框整个页面像工具箱不像一个产品。毕设演示的界面我建议只保留四个核心功能模块。第一个是偏好推荐区。用户输入一段自然语言偏好描述点“生成推荐”下方显示推荐电影卡片列表每张卡片包含海报、标题、简介和推荐理由。第二个是相似电影探索区。用户从推荐结果里点击某部电影系统用这部电影的文本向量和图像向量去检索最相似的其它电影。这个功能相当于内容推荐的可视化验证演示时评委很容易懂。第三个是历史反馈区。展示用户最近的播放/评价记录以及系统的混合排序权重分布。用Streamlit的表格组件展示协同过滤预测分、文本相似度、图像相似度三项指标直观体现“多模态融合”。第四个是检索过程可视化区。把RAG召回的中间过程展示出来用户查询向量被编码后显示文本通道召回的Top5和图像通道召回的Top5标出各自相似度。这个模块是答辩杀手锏因为大多数评委没在毕设里见过可交互的检索过程展示。页面布局上不要贪多。Streamlit的侧边栏用来放参数调节控件比如TopN取值、融合权重系数滑块。这样评委在答辩现场可以顺手拖一下滑块看推荐结果变化这个交互动作本身就很有说服力。4.2 session_state与推荐历史管理Streamlit的一个大坑是每次交互触发脚本都会从头到尾重新执行一遍。如果不加控制用户输入一个偏好页面重新刷新推荐结果还没出来之前的操作状态就丢了。推荐历史、用户已选的偏好描述、向量库连接状态这些不能放在普通全局变量里必须用st.session_state保存。举个例子我用session_state存了以下几个关键状态用户历史观影记录作为协同过滤部分的输入当前候选电影列表避免每次交互都重新召回大模型生成的推荐理由缓存避免重复调用接口用户切换页面标签时保持推荐列表不丢失这里有个很容易被忽略的点向量数据库连接对象不能反复创建。Streamlit重刷页面时如果你每次都重新连接向量库加载时间会呈指数级上升。正确做法是先判断session_state里有没有已存在的连接对象没有才去创建。这样一个简单的判断逻辑能把页面响应时间从三秒降到零点几秒。4.3 性能调优与演示模式Streamlit应用本身是单机运行的对毕设来说足够了。但你要是拿到答辩现场的电脑上跑有几个性能问题必须提前处理。第一个是首次加载慢。根源在于向量库索引加载和embedding模型初始化。解决办法是启动时预加载或者做一个进度条动画至少让现场看起来是“系统在思考”而不是“卡死了”。第二个是海报图片请求慢。本地图片你当然可以直接读文件但如果你偷懒用了远程图片链接答辩现场断网整个页面就垮了。务必把海报图片在数据准备阶段就下载到本地Streamlit里直接用本地路径渲染。第三个是大模型接口超时。推荐理由生成是网络请求现场答辩时网络抖动请求超时结果页面报错会非常尴尬。我给接口调用加了一个超时重试机制三次失败后自动降级为“只展示推荐结果不生成推荐理由”保证核心演示不断片。我当时是把整个项目打包成一个Docker镜像带了一台笔记本答辩现场直接起容器跑。虽然有点重但确实省去了答辩现场装依赖的麻烦。如果你不想用Docker那至少要把requirements.txt固定版本号不要带一大串空缺版本号的依赖否则现场环境一旦有版本冲突你会在PPT翻页的时候听到自己的心跳声。5. 常见问题速查与答辩避坑指南5.1 调试阶段最容易翻车的四个坑第一个坑语义相似度召回的推荐结果和用户预期严重不符。比如用户说“伤感的爱情片”系统返回了一部“动作片”原因是动作片简介里恰好出现了“伤心”这个词。这个问题的根源是纯向量召回对否定句、转折句的理解能力有限。解决办法是让召回的文本片段更结构化把类型标签作为硬性过滤条件向量相似度作为软性排序条件不能全靠向量。第二个坑图像向量通道把色调相近但题材完全不同的电影混在一起。比如一幅冷色调的海报系统认为它是科幻片其实是一部悬疑剧情片。这是图像embedding的固有局限。解决办法是给图像通道的召回结果降低权重或者增加“海报主体文字检测”如果海报上有大段文字默认减少该海报的权重。第三个坑不同模态的向量维度不一样融合排序时报维度错误。文本向量是768维图像向量是512维如果你不是用同一套模型对齐的就不可能直接放到一个索引里。这一点在“分别建索引”方案里反正避开了但你要是看了网上某些教程用的是“拼接向量”很容易被这个维度不匹配问题折磨到深夜。第四个坑embedding接口的请求并发限制。批量向量化时如果你一次性发太多个请求服务端会限流。我当时给批量向量化脚本加了指数退避的重试每批次处理200条每次请求间隔0.5秒耗时还算可控。5.2 答辩评委最爱问的五个问题“为什么选RAG而不是微调模型”标准回答微调一个电影推荐模型需要大量的监督数据和计算资源而RAG不需要重新训练通过检索增强的方式就能实时接入最新电影数据。对电影推荐这个场景语义检索带来的效果提升足够显著且方案可解释性强。“你的多模态是怎么实现的”这个用前面说的“分别索引、分通道召回、融合排序”来回答。关键是说清楚不是把图像和文本丢进同一个模型而是让它们在召回层面互相补充。“推荐结果里有冷门电影吗”冷门电影这个点一定要提前准备。很多推荐系统都偏向热门的电影因为热门电影的标签和评论多向量化质量高。如果你被问到要回答你的系统是怎么处理长尾数据的我当时是加了“相似度置信度下限过滤”相似度太低的冷门片宁可不出现在结果里。“协同过滤和多模态内容推荐哪个贡献更大”这是一个诱导性问题不能选边站。要回答两者各自擅长什么协同过滤擅长捕捉群体口味规律多模态内容检索擅长捕捉个体语义偏好系统中是相互融合的不是互相替代。“你的系统如果要做成产品最大瓶颈是什么”这个问题考察你的思考深度。我当时的回答是计算成本和数据更新时效性。向量化的成本、大模型生成推荐理由的成本、以及冷启动新片的向量化滞后这三个点都是产品化需要解决的真实问题。5.3 复盘心得做完这个项目之后我自己最大的体会是毕设选题的差异本质上不是技术难度的差异而是“叙事空间”的差异。同是推荐系统纯协同过滤的故事在十五页PPT里就讲完了而多模态RAG的故事从数据清洗讲到向量召回、从融合排序讲到生成式解释全程都有可说的地方。你不需要真的做出一个商业级产品你只需要在一个完整的技术链条里把每一个环节都做出合理的决策并且能解释清楚决策的依据。这才是毕设答辩考察的核心。如果现在有人让我再做一个推荐系统类的毕设我大概率还是会走多模态RAG的路子但会在数据侧加入用户评论的细粒度情感分析让文本模态的理解层次再往下钻一层。这个方向往下走的空间还很大光是文本模态和图像模态的对齐方式就够你再做一整篇论文了。