背景:
信息流场景的内容混排是将各种业务、模态内容以最满足用户个性化喜好的方式呈现,保障整体效率最大化同时,满足各业务的流量诉求。信息流首页如下图所示,有新闻[下图第一个],短内容[下图第2个],视频(横屏),小视频(竖屏)等模态,同时如果点击短内容,或者视频,会进入外流沉浸式上下滑场景。

混排整体流程:
当前内容混排整体流程分成三个部分,用户模态兴趣刻画,用户混排序列产生[generate],用户混排序列评估[evaluate]
本文主要工作是基于MetaHstu【Actions Speak Louder than Words: Trillion-Parameter Sequential Transducers for Generative Recommendations】来完成用户模态兴趣刻画,所以对内容混排的基于beam search的listwise环节不过多展开。
用户模态兴趣刻画:
用户模态兴趣刻画 基于用户过去的历史模态行为,给出用户当下请求的各模态打分,从而归一化得到该请求下用户在各模态的quota个数比如 该次请求return num10个得到【图文 1个,短内容1个,短视频 8个】。得到这个模态的quota是后续listwise generate环节产出序列的强约束。
用户模态兴趣刻画现存问题与why meta-hstu:
用户模态兴趣刻画base 版本是一个多目标【点击,时长等】融合策略统计模型,虽然经过不置信回退,跨场景折算,人群对单用户纠偏等各种策略。但是还是存在一些问题:比如用户历史点击序列的时序问题,时许会严重影响用户模态打分结果的准确性。 这个问题也恰好是transformer架构,self-attention 最能解决的问题。同时Meta-HSTU模型将attention 升级为point-attention 极大提高了大规模参数(几十亿级别)decoder-noly 类语言模型的训练速度,比较适合用在推荐等场景的生成式任务。

主要工作:
HSTU论文中提供了两个范式,
范式(1)建模用户行为序列并预测下一个交互项(item)
范式(2)建模用户行为序列,并结合候选item,给出action强度得分。
我们在内容混排这里需要的基于用户历史行为预测下一个点击的模态,我们选择范式(1)。
离线部分:
(1)样本组织方式:
不同于传统的排序模型每个用户和item单个pv 构成一个训练样本。meta hstu是用户将过去一段时间的点击序列组成一个样本。不需要负样本,训练过程中负采样。
(2)编码:
传统的生成式推荐任务因节省内存,标准化输入等目的需要将itemid,编码成sid。在本文实战中,直接将itemid编码成模态id。 模态id空间可控【图文,短视频,小视频,短内容,外流短视频,外流小视频,外流短内容,其它商业化内容】,字典空间小,且不变。
(3)特征:
需要将用户点击序列按序排列,用户点击时间戳按序排列,用户点击itemid对应的duration也按序排列。
整体流程图如下:

在线部分:
在线的推理有两种模式。
(1)方式1 根据用户历史的点击模态序列,预估得到下一个用户最可能的模态,将这个模态放入点击序列,继续得到下一个点击序列,循环n次到这次请求的所需要的item个数为止【比如10】
(2)方式2,根据用户历史点击模态序列,预估下一个用户最有可能的模态logit分布,从而得到图文/短视频/小视频/短内容的 概率, 用该请求所需要item个数 比如10*分别乘这个概率就可以得到模态的个数。
效果上(1)肯定是略好于(2),从推理性能来看(1)是不可以的。所以落地的时候选择(2)这种方式。同时由于用户模态的兴趣偏好在天内基本是稳定的。所以我们将推理的结果写入KV,推理流程不停滚动,推理完一轮就更新KV。在线请求时候直接KV获取。

模型评估:
模型参数
机器:2N*A100,N训练,N推理。
模型部分参数:
模型参数:2B(20亿+)
token size:8(8个模态)
token embbeding size:256
hstu encoder:num_blocks = 8
q k v u size=32
最大序列长度:210
模型评估指标:
(1)H1rate。 计算方式是让用户的点击模态历史序列N,分成N-1和1, N-1输入模型预测的top1结果 如果和1对上就是true,对不上就是false,计算这个概率。这个能从侧面反映这个模型是否有效。
(2)模型基于用户历史模态行为序列【Day1,,,,DayN-1】预估的模态概率分布向量[A,B,C,D], 以及DayN天用户真实点击的模态的概率分布[A1,B1,C1,D1] ,两个向量的cosine值, cosine 为1表示预估的和实际的100%相似。cosine为0表示一点也不相似。从下图可以看出当用户的点击序列越长,HSTU比基线效果越好,达到了我们最初设计的目的。

在线效果:
从5%VS5%开始,到最后推全。历时一个月的时间。其中在50%ExpVs50%Base 观测了7天:取得了。信息流人均pv+0.397%,信息流人均总时长+0.15%,信息流人均活跃天数+0.06%,信息流人均消费活跃天数+0.1%, 信息流消费渗透+0.09%,信息流渗透+0.05%。均置信的收益。
浅析Meta HSTU:
最后浅析一下Meta Hstu ,如果前向传播,并计算loss的demo过程,这个过程我看了几天的源代码,并开始有点理解错的过程中,经过同学的纠正,最终理解。 为了更好的呈现这个理解,我借助千问。展现如下:
(1)输入训练样本
# 输入: [B, L] 的 item ID 序列, B为batch size, L为最大序列长度-1。
比如输入的1个样本是 点击序列是1,2,3,4,5,6,7.
那么对这1一个样本 input_ids = 1,2,3,4,5,6
那么对这1个样本 targets_ids = 2,3,4,5,6,7. 就是说当点击序列是1的时候 target是2,输入是1,2的时候target是3.依次类推。
input_ids = batch['hist_item_ids'] # [B, L]
target_ids = batch['next_item_ids'] # [B, L] (shifted right)
(2)经过HSTU的point attention 过程得到【该过程与传统transformer的attention部分略有不同,请参考论文】
# 通过 Transformer 获取每个位置的上下文表示。
还是拿上面的input_ids = 1,2,3,4,5,6 这个样本来说, 这里的第二维度是L是因为 当序列是1的时候经过self-attention,layerNorm 等得到这个user在这个序列下的user embedding, 当序列是1,2的时候也得到一个。最后得到L个。
hidden_states = model(input_ids) # [B, L, D]
(3)负彩样,我的落地中由于token size 较小可以全彩。tokensize 较大的可以采样几千,
positive_ids = target_ids.view(-1) # [B*L]
negative_ids = sample_negative_items(positive_ids, K=7) # [B*L, K]
(4)look up item embedding,得到正样本以及负样本的item embedding。
all_candidate_ids = torch.cat([positive_ids.unsqueeze(1), negative_ids], dim=1) # [B*L, K+1]
candidate_embs = item_embedding(all_candidate_ids) # [B*L, K+1, D]
# 获取上下文向量(reshape)
context_vec = hidden_states.view(-1, D) # [B*L, D]
(5)计算logits,每一个位置上 都有K+1个logits, 1个正logits, K个负logits,套上sample softmax公式就能得到loss。
logits = torch.bmm(candidate_embs, context_vec.unsqueeze(-1)).squeeze(-1) # [B*L, K+1]
最后就是loss计算了。
labels = torch.zeros_like(logits)
labels[:, 0] = 1.0
# 使用 BCEWithLogitsLoss(等价于带采样的 softmax cross-entropy)
loss = F.binary_cross_entropy_with_logits(logits, labels) #这个地方可以直接samlpe softmax。
结语:
推荐的范式现在因为LLM的巨大成功也逐步转入了生成范式。希望我们始终能够保持实事求是的态度与对新事物探索的热忱。2025年AI2C的角逐如火如荼,可能不用几年,现在的所有流量入口,操作系统都将会被重新定义。大浪淘沙,只有真正的初心不改与事实求是,才能源远流长,而不是一时高潮,后面就被时代遗忘。
最后,在从2017年第一篇文章在简书上写,到现在也快9年时间。很低产,但是在一些有意思的东西出来的时候就会频繁写一些。也希望接下来能继续带来一些有意思的东西。简书这个产品也还一直在很好。
初版完成2025年12月31日。
引用:
(1)meta hstu论文:https://arxiv.org/abs/2402.17152.