基于AWS Trainium的vLLM冷启动推荐优化方案

本文详细介绍如何利用vLLM和AWS Trainium芯片解决推荐系统冷启动问题,包括使用大型语言模型生成用户兴趣画像、T5编码器生成嵌入向量、FAISS相似度检索等技术方案,并通过实验数据对比不同模型配置的性能表现。

基于AWS Trainium的vLLM冷启动推荐优化方案

推荐系统中的冷启动问题不仅涉及新用户或新物品,更关键的是在系统启动时完全缺乏个性化信号。当新用户首次访问或新内容出现时,由于没有行为历史数据,推荐引擎无法了解用户偏好,导致所有用户都被归入宽泛的通用类别。这不仅会降低点击率和转化率,还可能在系统有机会学习用户偏好之前就导致用户流失。

传统的解决方案如协同过滤、矩阵分解或热门列表缺乏弥补信号差距的细微差别,其"一刀切"的建议很快会显得过时。相比之下,如果能够从第一天就生成详细的兴趣画像,通过利用大型语言模型(LLM)进行零样本推理,就可以在没有数周交互数据的情况下合成丰富的、上下文感知的用户和物品嵌入,从而将冷启动转变为热情欢迎。

解决方案概述

我们在某中心的EC2 Trainium芯片上构建冷启动解决方案。为简化模型部署,我们使用带有某机构Neuron SDK的深度学习容器(DLC),该SDK安装了经过Neuron优化的PyTorch模块,并预装了最新的AWS Trainium驱动和运行时。

通过NeuronX Distributed(NxD)分布式库处理大型模型在多个Trainium芯片上的分片,该库与vLLM无缝集成。NxD以最少的代码更改管理多个实例间的模型分区,即使70B参数的LLM也能实现并行推理。这种组合——Trainium芯片、Neuron工具和vLLM——为机器学习工程师提供了一个灵活、经济高效、生产就绪的解决方案,可以实验不同的LLM和编码器配置,并在不修改核心模型代码的情况下快速迭代推荐质量指标。

使用LLM扩展用户兴趣画像

我们使用Kaggle上的某中心图书评论数据集(mohamedbakhet/amazon-books-reviews),该数据集提供了数万本书的真实用户评论和元数据。这个丰富的集合让我们能够模拟冷启动场景——全新用户只有单个评论或喜欢——并评估我们基于Meta的Llama 8B和70B模型蒸馏版本的兴趣扩展生成丰富用户画像的效果。

我们使用LLM从最少的初始数据中丰富新用户的画像。例如,如果用户只评论了一本科幻小说,LLM会推断用户可能喜欢的相关子主题——如银河帝国、赛博朋克反乌托邦或太空探索。我们使用结构化提示将用户现有活动嵌入到简洁指令中,以验证一致性和相关性:

1
2
3
4
5
6
prompt = (
f"The user has shown interest in: {user_review_category}.\n"
"Suggest 3–5 related book topics they might enjoy.\n"
"Respond with a JSON list of topic keywords."
)
expanded_topics = llm.generate([prompt])[0].text

通过约束LLM的输出格式——要求返回主题关键词的JSON数组——我们避免了自由形式的偏离,获得了可预测的兴趣扩展列表。现代生成模型(如Meta的Llama)具有广泛的领域知识和类人推理能力,能够连接相关概念,通过从单个评论中推断深层用户偏好,成为强大的冷启动助推器。

这些合成兴趣成为我们推荐管道的新信号,即使只有最少的用户历史记录,我们也能从某中心评论集合中检索和排名图书。您可以尝试从十亿到七百亿参数的Llama变体,以确定哪个模型产生最具区分性和相关性的扩展。这些发现将指导我们选择生产模型,并确定我们配置的某中心EC2 Trainium和Inferentia实例的大小和规模,为实时用户A/B测试做好准备,以验证在实际环境中的性能。

编码用户兴趣和检索相关内容

获得扩展兴趣后,下一步是将这些兴趣和图书目录转换为可比较的向量。我们探索了三种规模的Google T5编码器——base、large和XL——以了解嵌入维度如何影响匹配质量。步骤如下:

  1. 加载每种规模的编码器
  2. 将图书摘要编码为单个NumPy矩阵并归一化
  3. 在这些归一化向量上构建FAISS索引以实现快速最近邻搜索
  4. 以相同方式编码扩展兴趣文本,并查询FAISS以检索前k个最相似的图书
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from transformers import T5Tokenizer, T5EncoderModel
import faiss
import numpy as np

# 我们的图书摘要数据集
content_texts = df["review/summary"].tolist()
encoder_sizes = ["t5-base", "t5-large", "t5-xl"]
top_k = 5

for size in encoder_sizes:
    # 1. 加载该规模的标记器和编码器模型
    tokenizer = T5Tokenizer.from_pretrained(size)
    model = T5EncoderModel.from_pretrained(size)

    # 2. 将所有内容编码为嵌入并归一化
    inputs = tokenizer(content_texts, return_tensors="pt", truncation=True, padding=True)
    outputs = model(**inputs)
    content_embs = outputs.last_hidden_state.mean(dim=1).detach().cpu().numpy().astype("float32")
    faiss.normalize_L2(content_embs)

    # 3. 使用内积构建FAISS索引(等价于单位向量上的余弦)
    index = faiss.IndexFlatIP(content_embs.shape[1])
    index.add(content_embs)

    # 4. 编码单个扩展兴趣并查询索引
    interest = "space opera with political intrigue"
    enc = tokenizer([interest], return_tensors="pt", truncation=True, padding=True)
    interest_emb = model(**enc).last_hidden_state.mean(dim=1).detach().cpu().numpy().astype("float32")
    faiss.normalize_L2(interest_emb)

    distances, indices = index.search(interest_emb, top_k)
    recommendations = [content_texts[i] for i in indices[0]]

    print(f"\nTop {top_k} recommendations using {size}:")
    for title in recommendations:
        print(" -", title)

您可以比较每个编码器规模如何影响平均FAISS距离(即兴趣与内容之间的距离)以及实际推荐的标题。换用不同的编码器系列(如SentenceTransformers)就像替换模型和标记器导入一样简单。

测量和改进推荐质量

现在我们已经为每个LLM-编码器对生成了FAISS索引,并计算了每个扩展兴趣查询与其前10个邻居之间的平均距离,我们确切知道每个模型的嵌入聚类紧密或松散程度。

图表显示,1B和3B模型几乎崩溃为零,而8B和70B模型(尤其是使用更大的编码器)产生逐渐更高的距离,表明有更丰富、更具区分性的推荐信号。

图表显示,1B和3B模型的平均FAISS距离为零,意味着它们的扩展兴趣嵌入基本相同,没有区分性。相比之下,8B模型与t5-base产生约0.5的距离,使用t5-large和t5-xl时进一步上升,这表明更大的编码器捕获了模型的更多细微差别。70B模型只带来小幅提升——且仅在使用XL编码器时——因此其额外成本带来的收益有限。

实际上,Llama 8B LLM与base或large T5编码器配对,在嵌入空间中提供清晰的分离,而没有70B模型更高的推理时间和资源使用。

比较模型和编码器对嵌入分布的影响

为了了解LLM大小和编码器规模如何塑造我们的嵌入空间,您可以测量每个LLM和编码器对中,代表性扩展兴趣向量与其前10个邻居的平均FAISS距离。条形图并排绘制这些平均值。您可以立即发现1B和3B崩溃为零,8B跳到约0.5并随着更大的编码器上升,70B仅在XL规模时增加少量额外分布。这帮助您选择仍然提供有效冷启动推荐所需嵌入多样性的最小组合。

评估Llama变体和编码器间的推荐重叠以平衡一致性和新颖性

在下一个分析中,您构建一个基本的recommend_books辅助函数,对于各种LLM大小和编码器选择,加载相应的扩展兴趣DataFrame,读取其FAISS索引,重建第一个嵌入作为替代查询,并返回前k个图书标题。使用此辅助函数,我们首先测量每对编码器对单个LLM的推荐一致性——比较base与large、base与XL、large与XL——然后分别测量每对LLM大小在固定编码器下的对齐程度。最后,我们专注于8B模型,绘制其编码器重叠的热图,显示base和large共享其前5个选择的约40%,而XL差异更大——说明更改编码器如何改变推荐中一致性和新颖性的平衡。

对于8B模型,热图显示t5_base和t5_large共享其前5个推荐的40%,t5_base和t5_xl也重叠40%,而t5_large与t5_xl仅重叠20%,表明XL编码器相比其他对引入了最多新颖标题。

调整tensor_parallel_size以实现最佳成本性能

为平衡推理速度与资源成本,我们测量了在trn1.32xlarge实例上使用Llama 3.1 8B模型扩展用户兴趣时,增加Neuron张量并行性如何影响延迟。我们在tensor_parallel_size值为2、8、16和32时运行相同的零样本扩展工作负载。

如第一个图表所示,P50延迟下降74%——从TP=2时的2480毫秒到TP=16时的650毫秒——然后在TP=32时小幅下降至532毫秒(额外18%下降)。成本性能图表显示,超过TP=16后,加倍并行性大致使成本翻倍,但延迟仅进一步改善17%。

实际上,将tensor_parallel_size设置为16可提供最佳权衡:您捕获模型分片带来的大部分加速,同时避免最大并行性带来的急剧递减回报和更高的核心小时成本。

前图可视化了Llama 8B测试的成本性能比,强调TP=16在效益趋于平稳之前提供最平衡的效率。

下一步是什么?

既然我们已经确定了要使用的模型和编码器,以及与数据集一起使用的最佳配置(如序列大小和批处理大小),下一步是部署模型并定义生产工作流,生成编码后准备好与更多内容匹配的扩展兴趣。

结论

本文展示了某中心Trainium、Neuron SDK和可扩展LLM推理如何通过丰富稀疏用户画像来解决冷启动挑战,从第一天就提供更好的推荐。

重要的是,我们的实验强调,更大的模型和编码器并不总是意味着更好的结果。虽然它们可以产生更丰富的信号,但收益通常不能证明增加的成本是合理的。您可能会发现,8B LLM与T5-large编码器在性能和效率之间达到了最佳平衡。

这种方法不是假设越大越好,而是帮助团队确定最优的模型-编码器对——以经济高效的基础设施提供高质量的推荐。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计