MAPS:如何在大规模场景下实现多模态内容个性化

2026-08-29 49 预计阅读时间: 1 分钟
来源: netflixtechblog.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:13 分钟

流媒体平台给用户展示的并不只是一个标题,而是一整套内容资产:封面、横幅、预告片、字幕、音频、剧照和文案。不同用户看到同一部作品的不同素材,可能直接影响点击、播放和继续观看。

“MAPS: Netflix’s Multimodal Asset Personalization at Scale”这个标题指向一个重要工程问题:如何把多模态资产纳入个性化系统,并在海量用户、内容和请求下稳定地完成选择。由于这里没有提供论文或项目摘要,下面将围绕标题中的核心问题展开,并明确区分可推导的工程思路与示例性实现。

个性化对象不再只是“推荐哪部作品”

传统推荐系统通常先决定给用户展示哪部电影或剧集,再使用固定图片作为展示素材。但一部作品往往拥有多个可选资产:

  • 面向动作片爱好者的角色或冲突场景;
  • 面向家庭用户的群像或轻松场景;
  • 面向不同语言市场的标题、字幕和文案;
  • 适合首页大卡片、移动端小卡片或详情页的不同构图;
  • 视频预告片中的不同起始片段和时长版本。

因此,推荐系统实际上需要解决两个层次的问题:

  1. 内容选择:用户应该看到哪部作品?
  2. 资产选择:这部作品应该用哪张图、哪段视频、哪条文案来展示?

MAPS 这个名字中的 Multimodal Asset Personalization,可以理解为把第二个问题提升为一等能力。系统不只处理结构化元数据,也要处理图像、视频、文本和音频等模态,并将它们与用户上下文关联起来。

一个实用的抽象是把展示单元建模为 asset × context

展示结果 = 排名模型(用户特征, 作品特征, 资产特征, 页面上下文, 设备信息)

这里的资产特征可以包括图像的视觉 embedding、视频时长、画面主体、文案语言、适配的屏幕比例,以及历史曝光和点击统计。

大规模系统的关键:离线重计算,在线轻决策

多模态模型通常计算成本较高,而首页请求要求低延迟。一个可行的系统会把流程拆成离线和在线两部分。

离线阶段:理解和整理资产

内容资产进入系统后,可以执行以下任务:

  • 抽取图片、视频关键帧和音频的 embedding;
  • 识别画面中的人物、场景、颜色和构图;
  • 从字幕、标题和简介中提取语言及语义信息;
  • 判断资产是否适合特定尺寸、地区、年龄分级或终端;
  • 生成质量、合规性和可用性标签;
  • 聚合不同版本的曝光、点击、播放和完成率。

这些结果可以存入向量库、特征库或普通键值存储。重要的是,在线请求不应该临时运行完整的视频理解模型,而应该读取已经准备好的特征。

在线阶段:过滤、排序和兜底

在线服务更像一个严格受约束的决策器:

  1. 根据地区、语言、版权窗口和年龄限制过滤资产;
  2. 根据设备和页面布局过滤尺寸、比例和时长;
  3. 使用用户与资产特征计算候选分数;
  4. 施加多样性、频控和实验分桶规则;
  5. 返回可直接渲染的 CDN 地址;
  6. 如果模型或特征不可用,回退到稳定的默认资产。

这种分层设计有两个好处。第一,模型升级不会直接改变在线接口契约。第二,发生特征延迟、缓存失效或模型故障时,系统仍能展示内容,而不是让首页出现空白。

一个可运行的最小资产选择服务

下面的示例不是 Netflix 内部实现,而是一个可以改造的最小原型。它使用 Python 标准库启动 HTTP 服务,根据用户偏好、设备比例和资产标签进行打分,并提供稳定的回退逻辑。

将代码保存为 asset_service.py,然后运行 python asset_service.py

from http.server import BaseHTTPRequestHandler, HTTPServer
import json
from urllib.parse import urlparse, parse_qs

ASSETS = [
    {
        "id": "hero-action",
        "title": "Night Run",
        "url": "https://cdn.example.com/night-run/action.jpg",
        "genres": ["action", "thriller"],
        "aspect": "wide",
        "quality": 0.92,
    },
    {
        "id": "hero-family",
        "title": "Night Run",
        "url": "https://cdn.example.com/night-run/family.jpg",
        "genres": ["drama", "family"],
        "aspect": "wide",
        "quality": 0.90,
    },
    {
        "id": "poster-action",
        "title": "Night Run",
        "url": "https://cdn.example.com/night-run/action-poster.jpg",
        "genres": ["action", "thriller"],
        "aspect": "poster",
        "quality": 0.88,
    },
]


def choose_asset(user_genre: str, device: str):
    desired_aspect = "poster" if device == "mobile" else "wide"
    candidates = [a for a in ASSETS if a["aspect"] == desired_aspect]

    def score(asset):
        preference = 1.0 if user_genre in asset["genres"] else 0.0
        return 2.0 * preference + asset["quality"]

    # 明确排序规则,保证结果可解释、可测试。
    ranked = sorted(candidates, key=score, reverse=True)
    return ranked[0] if ranked else ASSETS[0]


class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        query = parse_qs(urlparse(self.path).query)
        genre = query.get("genre", ["drama"])[0]
        device = query.get("device", ["web"])[0]
        result = choose_asset(genre, device)

        payload = json.dumps({"asset": result}, ensure_ascii=False).encode("utf-8")
        self.send_response(200)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Content-Length", str(len(payload)))
        self.end_headers()
        self.wfile.write(payload)


if __name__ == "__main__":
    print("listening on http://127.0.0.1:8000")
    HTTPServer(("127.0.0.1", 8000), Handler).serve_forever()

启动后可以用下面的命令验证:

python asset_service.py
curl 'http://127.0.0.1:8000/?genre=action&device=web'
curl 'http://127.0.0.1:8000/?genre=family&device=mobile'

生产系统可以把 score() 替换成模型服务调用或本地推理,把 ASSETS 替换成特征库查询。但接口层仍然应该保留过滤和回退逻辑,不要让模型成为唯一的正确性边界。

不能只优化点击率

资产个性化很容易陷入“点击率越高越好”的单一目标。某张图片可能因为冲突、夸张或剧透而带来更多点击,却降低实际播放完成率,甚至损害用户对内容的信任。

更稳妥的目标函数可以同时考虑多个信号:

最终分数 = 点击倾向
        + 播放倾向
        + 完播或继续观看倾向
        - 负反馈
        - 过度曝光惩罚
        - 资产质量和合规风险

还需要区分曝光和选择偏差。用户只有看到某张图,才有机会点击它;如果系统始终只展示当前冠军资产,就很难知道其他资产是否适合某些人群。因此,实验系统通常需要保留受控探索流量,并按用户群体、地区、设备和页面位置观察结果。

评估指标也应分层:

  • 资产层:加载成功率、点击率、播放启动率;
  • 内容层:观看时长、完成率、继续观看率;
  • 体验层:负反馈、退出率、重复曝光率;
  • 系统层:P95/P99 延迟、缓存命中率、特征新鲜度和回退比例。

多模态系统还要增加数据治理检查:人物和情节标签是否准确,地区规则是否生效,是否把剧透素材展示给不合适的用户,资产版本是否能够追溯。个性化越精细,错误也越可能以“看起来合理”的形式发生,因此可解释日志和人工审核通道不可缺少。

落地时可以采用的渐进路线

不要一开始就把所有图像、视频、音频和文案纳入统一大模型。更可控的路线是:

  1. 先做资产目录:统一资产 ID、作品 ID、语言、地区、尺寸、版本和有效期;
  2. 再做规则过滤:解决版权、终端适配、年龄分级和可用性问题;
  3. 接入离线特征:先使用简单标签和 embedding,不改变在线延迟预算;
  4. 建立小流量实验:只在一个页面或一类内容上验证资产个性化价值;
  5. 加入多目标优化:从点击扩展到播放、观看质量和负反馈;
  6. 完善回退和审计:让每次选择都能回答“为什么展示这份资产”。

采用这类系统时,最值得提前确认的不是模型有多大,而是资产数据是否完整、实验是否可归因、缓存和回退是否可靠。多模态个性化的难点最终会落在数据管道、在线约束和长期指标上,而不只是 embedding 或排序模型本身。

小结:把“展示素材”当成推荐系统的一部分

MAPS 所代表的方向提醒我们:个性化推荐的对象正在从“内容”扩展到“内容如何被呈现”。当图像、视频、文字和音频都成为可选择资产时,平台需要同时管理语义理解、实时排序、实验评估、合规约束和故障回退。

如果准备在自己的产品中尝试,建议先用少量资产和清晰的在线契约做出端到端闭环:离线生成特征,在线完成过滤与选择,记录曝光和后续行为,并始终保留可用的默认结果。等数据证明资产选择确实改善了长期观看体验,再逐步扩大模态范围和模型复杂度。


相关推荐