推荐 / NOTEBOOK 课程

09 · 冷启动与流量调控

真实属性 + 模拟流量

真实数据 + 机制 / 模拟只读 · 已保存的实验

已有输出来自保存的教学实验,并非打开页面时重新执行。 代码和原始输出保持不变;数据与历史注释保留原始语言。

冷启动与流量调控

Goal

把概念变成可检查的输入、运算、梯度与对照。

数据类型:真实属性 + 模拟流量。本地教学参数不是生产默认值;机制实验不是原论文完整复现。

Setup

每本首次从 Setup 起顺序运行,后面的格会复用前面的变量。之后只改一个参数,重跑对应格及下游格。不要把构造数据当作真实业务收益。

PYTHON · 01
from pathlib import Path
import sys
import inspect
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display, Code

PROJECT = next(p for p in [Path.cwd(), *Path.cwd().parents]
               if (p / "reclab/models.py").exists())
if str(PROJECT) not in sys.path:
    sys.path.insert(0, str(PROJECT))
from reclab.data import Dataset
from reclab.paths import ARTIFACTS, PROCESSED, read_json

plt.rcParams.update({"figure.figsize": (9, 3.8), "font.size": 11,
    "axes.spines.top": False, "axes.spines.right": False,
    "axes.labelcolor": "#182b2b", "text.color": "#182b2b",
    "axes.facecolor": "white", "figure.facecolor": "white",
    "grid.color": "#d4ddda", "axes.axisbelow": True})
dataset = Dataset()
print("Project:", PROJECT)
print("Dataset version:", dataset.manifest["dataset_version"])
print("Python:", sys.executable)
assert Path(sys.executable).parent == PROJECT / ".venv/bin", "请选择 Recommendation Lab 内核"
Notebook 保存的输出 · 本页未执行
Project: /Users/lizuolin/local/recommendation-lab
Dataset version: 2772cab0aca8cb81
Python: /Users/lizuolin/local/recommendation-lab/.venv/bin/python

Steps

讲义对应:07_ColdStart_01.pdf, 07_ColdStart_02.pdf, 07_ColdStart_03.pdf, 07_ColdStart_04.pdf, 07_ColdStart_05.pdf, 07_ColdStart_06.pdf

隐藏 ID 后只用类目召回

训练目录内人为隐藏 ID 的压力练习,不是真实新发布商品效果。这里不用隐藏物品的已训练 ID 向量宣称冷启成功。

先预测: 类目缺失或全部是 unknown 时,这条通道会怎样?

PYTHON · 02
categories=sorted({x["category"] for x in dataset.items});lookup={c:i for i,c in enumerate(categories)}
side=np.eye(len(categories),dtype=np.float32)[[lookup[x["category"]] for x in dataset.items]]
user=next(iter(dataset.histories));history=dataset.histories[user]
profile=side[np.array(history[-20:])-1].mean(0);scores=side@profile
hidden_ids=set(range(200))
chosen=[i for i in np.argsort(-scores,kind="stable") if i in hidden_ids][:10]
display(pd.DataFrame([{"item":dataset.items[i]["item_id"],"category":dataset.items[i]["category"],"score":scores[i]} for i in chosen]))
Notebook 保存的输出 · 本页未执行
item category score
0 10034 1219 0.916667
1 18519 1219 0.916667
2 403 unknown 0.083333
3 546 unknown 0.083333
4 2008 unknown 0.083333
5 8497 unknown 0.083333
6 496 707 0.000000
7 829 707 0.000000
8 869 1650 0.000000
9 1152 1051 0.000000

聚类与 Look-Alike 人群扩散

真实类目与训练历史的机制实验。没有真实图文或作者字段,不复现 CNN/BERT 内容相似度训练。

先预测: 如果种子群体偏了,扩散会修正偏差还是放大偏差?

PYTHON · 03
from reclab.learning.retrieval import kmeans
centers,labels=kmeans(side[:600],clusters=8,iterations=5)
user_ids=list(dataset.histories)[:500]
profiles=np.array([side[np.array(dataset.histories[u][-20:])-1].mean(0) for u in user_ids])
seed_category=dataset.items[0]["category"]
seeds=[i for i,u in enumerate(user_ids) if any(dataset.items[j-1]["category"]==seed_category for j in dataset.histories[u])] or [0]
audience=profiles[seeds].mean(0)
audience=audience/max(float(np.linalg.norm(audience)),1e-8)
normalized=profiles/np.maximum(np.linalg.norm(profiles,axis=1,keepdims=True),1e-8)
expanded=np.argsort(-(normalized@audience))[:10]
display(pd.DataFrame({"user":[dataset.users[user_ids[i]] for i in expanded],"similarity":(normalized@audience)[expanded]}))
print("Cluster counts:",np.unique(labels,return_counts=True))
Notebook 保存的输出 · 本页未执行
user similarity
0 104555 0.866971
1 113172 0.866971
2 99319 0.866382
3 77609 0.843095
4 78724 0.740322
5 19967 0.733718
6 102661 0.711574
7 44071 0.699376
8 71099 0.684696
9 39027 0.657632
Cluster counts: (array([0, 1, 2, 3, 4, 5, 6, 7]), array([557,  16,   2,   4,   4,   6,   7,   4]))

保量、差异化目标与供需约束

全部是模拟流量。100 次基础曝光只是讲义示例。供给不足不可能全保量;即使流量足够,固定 boost 也未必成功。

先预测: 保证数量、内容质量与消费体验能否同时最大化?

PYTHON · 04
from reclab.learning.systems import allocate_quotas,bucket
quality=np.linspace(.1,1.,20);QUOTA=100;rows=[]
for requests in [500,2000,4000]:
    exposure=allocate_quotas(quality,requests,quota=QUOTA,boost=3.)
    rows.append({"requests":requests,"required_supply":len(quality)*QUOTA,
        "items_reaching_quota":int((exposure>=QUOTA).sum()),"allocated":int(exposure.sum())})
    assert exposure.sum()==requests
display(pd.DataFrame(rows))
assert rows[0]["items_reaching_quota"]<len(quality)
Notebook 保存的输出 · 本页未执行
requests required_supply items_reaching_quota allocated
0 500 2000 0 500
1 2000 2000 1 2000
2 4000 2000 1 4000

作者侧 A/B 的流量干扰

构造两个作者池;共享预算下 A 增量会挤占 B。没有真实作者发布和用户留存的因果结论。

先预测: 如何区分业务净增与实验组之间搬运流量?

PYTHON · 05
budget=10000;base=np.array([1.,1.]);boost=np.array([1.5,1.])
before=budget*base/base.sum();after=budget*boost/boost.sum()
display(pd.DataFrame({"before":before,"after":after,"delta":after-before},index=["authorA","authorB"]))
assert np.isclose((after-before).sum(),0)
print("Reproducible author buckets:",[bucket(i,"author",2) for i in range(6)])
Notebook 保存的输出 · 本页未执行
before after delta
authorA 5000.0 6000.0 1000.0
authorB 5000.0 4000.0 -1000.0
Reproducible author buckets: [0, 1, 1, 1, 0, 0]

Checks

所有断言和输出都可回溯到代码。它们验证算子、数据快照或机制,不是线上收益。

Next Steps

先记录预测,只改一个变量,再运行对应实验格。需要查看辅助函数时,打开 reclab/learning/。

继续阅读原文

本章来源

作者课程中文

王树森 · 工业界的推荐系统

作者课程对应模块;见下方逐份讲义

45份讲义与逐课实践入口;主题有练习不代表完整复现论文。原件保留原语言与原作者权利。

作者课程中文

冷启动评价指标

07_ColdStart_01.pdf · 18页

机制可读;真实业务任务还需要额外数据与标签。

作者课程中文

冷启动简单召回

07_ColdStart_02.pdf · 21页

对应教学机制可读;原讲义完整方法仍是进阶目标。

作者课程中文

聚类召回

07_ColdStart_03.pdf · 20页

机制可读;真实业务任务还需要额外数据与标签。

作者课程中文

Look-Alike 人群扩散

07_ColdStart_04.pdf · 17页

对应教学机制可读;原讲义完整方法仍是进阶目标。

作者课程中文

流量调控与保量

07_ColdStart_05.pdf · 22页

对应教学机制可读;原讲义完整方法仍是进阶目标。

作者课程中文

冷启动 A/B 测试

07_ColdStart_06.pdf · 20页

机制可读;真实业务任务还需要额外数据与标签。

学习标记可选,仅保存在当前浏览器中。打开页面不会自动标记已学。