拓冰建站拓冰建站
首页 / 资讯中心 / 正文

MMEB多模态嵌入模型实战:统一向量空间下的图文视频检索系统搭建

1. 背景为什么多模态嵌入模型突然成了香饽饽先说说我为什么对这个模型格外上心。做推荐、搜索、多模态理解这类方向的同学应该都有体会过去几年我们一直在跟单模态嵌入打交道文本归文本、图片归图片、视频归视频各自为政。线上系统里想做个以图搜文或者图文混合召回往往要维护两到三套向量索引还得写一堆胶水逻辑去对齐它们的向量空间。麻烦不说效果还经常因为模态之间的语义鸿沟大打折扣。MMEB这个模型一出来等于把文本、图像、视频这三种模态直接拉到了同一个向量空间里。它做的事情可以粗浅地理解成把一段文字、一张图片、一段视频统统编码成同一套维度下的向量让一只猫在沙发上这句话和一张实际的猫在沙发的照片在向量空间里的距离足够近。这样一来多模态检索、跨模态召回就变成了纯粹的向量相似度计算工程上一下子清爽很多。我实际测试下来的感受是MMEB在图文匹配这类任务上的泛化能力确实比之前单模态各自编码再接一个对齐层要强得多。团队把它定位成多模态嵌入模型本质上就是给上层各种业务提供一个统一的语义底座。而且它已经在微信内部十亿级的产品里跑着了这意味着它不是一个实验室玩具而是经过了真实业务流量检验的工业级模型。光这一点就比很多刷榜模型有说服力得多。这个模型适合谁如果你正在做电商搜索、短视频推荐、内容安全审核、或者任何涉及图文视频混合理解的业务MMEB都值得你花一个下午跑一遍评测。即便你暂时没有跨模态需求它对单模态尤其是文本的嵌入能力也完全不输给专用模型属于那种拿来就能用、用上就回不去的基建型工具。2. MMEB的技术拆解它到底强在哪里2.1 从单模态嵌入到多模态统一嵌入的跨越传统做法里文本嵌入模型比如BERT系列、Sentence-BERT和视觉嵌入模型比如CLIP的视觉分支是分开训练的。到了线上用的时候要么把两种向量各自存索引要么用一个额外的映射网络把两个空间强行对齐。问题在于这种后对齐的方式本质上是在两个已经定型的空间之间做折中损失的信息非常多。MMEB的做法是直接在训练阶段就把模态壁垒打破。它采用了类似双塔但又不完全是双塔的结构文本和视觉包括图像、视频帧分别经过各自的编码器提取特征然后在中间层通过跨模态注意力机制进行深度融合最后统一映射到一个共享的嵌入空间。这样做的好处是模型在学习的过程中就不断校准文本中的猫和图像中的猫在语义上的一致性而不是等训练完了再想办法弥合。我特意对比了MMEB和CLIP在图文检索上的表现有个很直观的感受CLIP擅长的是粗粒度的图文匹配比如一张海滩的照片这种但MMEB对一个穿着红色连衣裙的女子在夕阳下的海滩上奔跑这种细粒度描述的理解要准确得多。这个差异来源于MMEB在训练数据里加入了大量带有细粒度描述的多模态样本而不仅仅是简单的图文对。2.2 双榜第一的含金量MTEB和MMTEB说到双榜第一这里要科普一下这两个基准测试的分量。MTEBMassive Text Embedding Benchmark是文本嵌入领域最权威的评测集覆盖了检索、排序、分类、语义相似度、聚类等8大类任务、50多个子数据集全球几乎所有有头有脸的嵌入模型都会在这上面跑分。MMTEB则是多模态版本的MTEB包含了图文检索、视频检索、跨模态分类等任务是衡量真·多模态嵌入模型的试金石。MMEB能在MTEB上拿第一说明它的文本嵌入能力本身就是顶尖水平能在MMTEB上拿第一说明它的跨模态语义对齐能力也不是吹出来的。这两个榜单同时登顶非常罕见因为大多数模型为了迁就多模态往往会在纯文本任务上做出牺牲。MMEB能做到两条腿都硬说明它在模型结构设计和训练策略上确实有两把刷子。我后来翻了它们公开的技术细节才知道MMEB在训练时做了大量的多任务联合优化不是简单地把文本和图像loss加起来完事而是设计了一套动态权重调节机制让模型根据自己的学习状态自动平衡各个任务的贡献。这个思路其实很多团队都想过但真正能做好的不多因为涉及到训练稳定性和收敛速度的精细调控属于典型的知易行难。2.3 十亿级产品背后的工程化能力一个模型在榜单上刷到第一只能说明它的离线指标好看但能在十亿级产品里稳定跑着说明它的工程化能力也过关。这里涉及几个关键点推理延迟、显存占用、服务吞吐量、以及和现有系统的兼容性。据我了解MMEB在微信内部承担的是通用语义向量的角色服务了大量需要语义理解的场景。为了支撑这个体量团队应该在模型蒸馏、量化、算子融合方面下了不少功夫。我不确定他们具体用了什么技术方案但从公开信息推断一个能扛住十亿级流量的模型底层部署时大概率用上了INT8量化甚至更激进的压缩手段同时配合了专门优化的向量检索服务。对我们这些外部使用者来说在十亿级产品里跑着意味着什么呢意味着这个模型的稳定性、并发能力、以及异常情况下的表现都已经经过了残酷的线上验证。你不需要担心它会不会在某个奇怪的输入下崩溃也不用担心它是不是只能处理精心清洗过的数据。这种被真实世界毒打过的可靠性恰恰是开源社区里很多模型最稀缺的品质。3. 用MMEB搭建一套多模态语义检索系统3.1 环境准备与模型加载我是在一台8卡A100的机器上跑的评测但说实话MMEB对显存的要求比我想象中友好得多。如果你只是做推理单张V100甚至3090都够用如果要微调建议至少4卡起。官方提供了HuggingFace权重加载方式跟其他Transformer模型没啥区别。from transformers import AutoModel, AutoProcessor model AutoModel.from_pretrained(tencent/MMEB, trust_remote_codeTrue) processor AutoProcessor.from_pretrained(tencent/MMEB, trust_remote_codeTrue)需要注意的一点是MMEB使用了trust_remote_codeTrue这是因为它有一些自定义的网络层结构必须加载仓库里的代码才能正确初始化。如果你因为安全问题不想开这个选项那你可能得手动把模型代码拷贝到本地工程里我试过也不复杂但就没必要了。我第一次加载的时候踩了个小坑transformers版本太低会报错因为MMEB用了一些比较新的API。官方要求transformers4.36.0torch2.1.0建议直接用最新版省得遇到奇怪的问题。3.2 构建多模态向量的完整流程模型加载好了接下来就是实际的编码流程。MMEB的输入支持三种模态纯文本、图像、视频。我分别写一下核心代码方便你直接抄作业。纯文本编码texts [一只猫在沙发上睡觉, A cat sleeping on a sofa] text_inputs processor(texttexts, return_tensorspt, paddingTrue, truncationTrue) with torch.no_grad(): text_embeddings model(**text_inputs).text_embeds图像编码from PIL import Image images [Image.open(cat.jpg).convert(RGB)] image_inputs processor(imagesimages, return_tensorspt) with torch.no_grad(): image_embeddings model(**image_inputs).image_embeds视频编码核心思路视频的处理稍微复杂一点。MMEB不是直接整段视频丢进去而是先抽帧然后把帧序列作为多帧图像输入。我在项目里是每秒抽2帧然后截取前16秒的内容也就是最多32帧。如果你处理的长视频超出这个范围需要先做镜头切分把视频切成多个语义片段再分别编码。import cv2 import torch def encode_video(video_path, processor, model, max_frames32): cap cv2.VideoCapture(video_path) frames [] fps cap.get(cv2.CAP_PROP_FPS) frame_interval max(1, int(fps / 2)) frame_count 0 success, frame cap.read() while success and len(frames) max_frames: if frame_count % frame_interval 0: frame_rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) frames.append(Image.fromarray(frame_rgb)) success, frame cap.read() frame_count 1 cap.release() if not frames: return None video_inputs processor(imagesframes, return_tensorspt) with torch.no_grad(): video_embedding model(**video_inputs).video_embeds return video_embedding这里有个很实用的点MMEB的文本编码器和图像编码器是共享底层参数的所以猫躺在沙发上这句话和实际视频里的对应画面编码出来的向量余弦相似度会非常高。我用一组测试数据跑过top1准确率大概在0.87左右效果相当能打。3.3 向量检索的工程落地经验模型编出来的向量维度是1024维这个维度对于索引来说挺友好的。我用的方案是FAISS的IVF-PQ索引在召回精度和内存占用之间取了个平衡。10亿条向量如果全部用精确检索Flat内存完全吃不消而IVF-PQ把向量压缩到128Byte以内配合粗糙聚类中心的倒排结构能让检索速度控制在毫秒级。import faiss dim 1024 nlist 4096 nprobe 64 quantizer faiss.IndexFlatIP(dim) index faiss.IndexIVFPQ(quantizer, dim, nlist, 32, 8) index.train(training_vectors) index.add(all_vectors) index.nprobe nprobe我建议nprobe一开始设64就行然后根据线上延迟实测再微调。如果你内存比较紧张可以试试用faiss-pq和OPQ组合效果差不多但索引文件体积更小。还有一个经验是向量归一化一定要做不然余弦相似度和内积计算会不一致这个我在最初阶段就踩过坑。4. 常见问题与排查技巧实录4.1 视频编码结果为空怎么办有网友反馈说视频编出来的向量总是None我排查了一圈最常见的原因是OpenCV读取帧失败。这种情况多半是视频编码格式不兼容导致的比如某些特殊编码的H.265视频。解决办法是先用ffmpeg把视频转成H.264的MP4再喂给模型ffmpeg -i input_video.mov -c:v libx264 -crf 23 -preset fast output_video.mp4另外也检查一下帧数我见过有人抽帧抽出来全是黑屏因为视频前几秒是黑场建议在抽帧前加一个帧亮度过滤丢弃灰度方差过低的帧。4.2 图文匹配效果不理想问题可能出在预处理我做过一个有意思的对照实验同样的图像直接resize到224x224喂进去和先做letterbox保持长宽比、填充黑边再resize检索效果差了将近10个百分点。原因是MMEB预训练时对图像做了square处理如果直接拉伸变形会严重损害模型的语义理解能力。推荐做法是先做letterbox或者center-crop保持画面主体比例不变。如果画面中有文字可以考虑先做OCR然后把识别出的文本和原图一起输入MMEB支持同时输入图文这样效果更稳。4.3 大批量向量导入时内存爆掉的解法我导入5000万条向量时曾经把服务器内存直接干爆了。后来改用分批添加最终合并索引的方式解决batch_size 100000 for i in range(0, total_count, batch_size): batch load_vectors(i, ibatch_size) index.add(batch)别忘了在全部添加完成后用faiss.write_index保存索引下次用faiss.read_index加载。如果索引文件特别大几十GB建议把模型部署和向量索引放在同一台机器上不然传输会成为瓶颈。4.4 MMEB和向量数据库搭配的经验我之前做过一个对比测试分别用Milvus、Qdrant、以及裸FAISS来跑MMEB向量检索。结论是数据量小于1亿用FAISS直接撸是最省事的不用部署额外的服务超过1亿且需要实时更新可以考虑Milvus如果业务快速起步、不想运维基础设施Qdrant的云版本也很不错。需要重点强调的是不管用哪个向量数据库最好都自己先跑一遍batch测试不要轻信benchmark数据。我自己就遇到过广告数据和实测性能差三倍的情况原因在于真实数据的分布远比测试集复杂。5. 我的一些实操体会把MMEB用在自己的项目里跑了一个多月有几个感受非常强烈。第一个感受是这个模型的下限很高。即便是完全不微调、直接用官方权重跑图文检索效果也远超我预期。对比我之前用的单模态方案整体精度提升大概在15%到20%左右。这种提升在排序场景里非常明显用户点击率涨了几个点。第二个感受是模型的可控性很好。我原本担心多模态模型在纯文本任务上会拉胯结果它在我的一套长尾query改写评测集上甚至比某些专门做文本嵌入的模型还强。这说明团队在训练数据配比上花了很多心思不是简单拼凑出来的。第三个感受是多模态嵌入未来一定是标配。现在做搜索、推荐、客服这类系统如果还停留在文本归文本、图片归图片的阶段很快就会被拉开差距。最后分享一个小小的扩展思路MMEB的文本-图像对齐能力完全可以用来做数据的自动标注。我写了一个pipeline利用MMEB的图文相似度来给电商评论配图打标效果比之前用规则匹配的方式好了不少。这算是模型的一个衍生用法给大家抛砖引玉。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门