广告 / NOTEBOOK 课程

21 · 非线性、特征交叉与树容量

真实点击分布与独立模拟市场 · 可运行单变量实验

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

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

21 · 非线性、特征交叉与树容量

第18章保留总览;本章单独深入一个问题。正式200k实验不覆盖,教学训练使用独立共享记录。

先预测:加性模型在哪里失败?

线性logit只能表达特征的加性关系。交叉项把“两个字段共同出现”的状态加入设计矩阵;树通过条件路径表达交互。交叉能够增加表达力,也可能增加样本需求与过拟合,不能因为真实任务叫CTR就认定深模型一定更好。

先用明确构造的XOR数据检查表达能力,再用真实40k曝光只改变树叶数7→15。两部分分别是机制与真实训练,不能混成一个benchmark。

PYTHON · 01
from pathlib import Path
import sys
ROOT=Path.cwd()
if not (ROOT/'reclab').is_dir(): ROOT=ROOT.parent
sys.path.insert(0,str(ROOT))
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display
from reclab.ads import latest, load_data, split_groups, NUM, CAT, FEATURES
from reclab.paths import ARTIFACTS
from reclab.probability import evaluate
preview=ARTIFACTS/'notebook-previews';preview.mkdir(parents=True,exist_ok=True)
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
rng=np.random.default_rng(42)
x=rng.integers(0,2,size=(4000,2));y=(x[:,0]!=x[:,1]).astype(int)
print('Constructed XOR X:',x.shape,'y:',y.shape)
plain=LogisticRegression(C=10).fit(x[:3000],y[:3000])
cross=make_pipeline(PolynomialFeatures(degree=2,interaction_only=True,include_bias=False),LogisticRegression(C=10)).fit(x[:3000],y[:3000])
for name,model in [('additive',plain),('one_interaction',cross)]:
 r=evaluate(y[3000:],model.predict_proba(x[3000:])[:,1]);print(name,{k:r[k] for k in ['auc','logloss']})
Notebook 保存的输出 · 本页未执行
Constructed XOR X: (4000, 2) y: (4000,)
additive {'auc': 0.5427435387673956, 'logloss': 0.6928292897764964}
one_interaction {'auc': 1.0, 'logloss': 0.004364692561560054}

交叉的形状与可识别边界

两个输入在interaction_only模式下得到[x1,x2,x1*x2]。这并非完整DeepFM或DCNv2复现,而是检验交互为什么可能有效。若增加字段数d,二阶成对交叉数为d(d−1)/2;匿名高基数onehot的全部交叉很容易超过本机预算。

先预测新增列在四种输入组合上的取值,执行穷举核对。再想想真实数据中一个罕见交叉只有一两条正例时会发生什么。

PYTHON · 02
grid=np.array([[0,0],[0,1],[1,0],[1,1]])
transformed=cross[0].transform(grid)
display(pd.DataFrame(transformed,columns=['x1','x2','x1*x2']))
assert transformed.shape==(4,3)
assert np.array_equal(transformed[:,2],grid[:,0]*grid[:,1])
print('All second-order interactions for d=39:',39*38//2)
Notebook 保存的输出 · 本页未执行
x1 x2 x1*x2
0 0.0 0.0 0.0
1 0.0 1.0 0.0
2 1.0 0.0 0.0
3 1.0 1.0 1.0
All second-order interactions for d=39: 741

真实训练:只改变树叶数

固定原始数值、训练top255类别、80棵树、学习率.05、最小叶样本100与L2=1;只改变num_leaves。类别用类别分裂,不能把编码整数直接当有序数。预算较原正式200k更小,所以结果只在本章两组之间比较。

注意树深和叶数不是同一概念;15叶不会保证每条路径深4。训练与评估同样走共享registry,读取select而不是audit。

PYTHON · 03
from reclab.ads_teaching import workshop
run_id,comparison=workshop('tree_capacity')
print('Shared workshop:',run_id,comparison['shapes'])
table=pd.DataFrame([{'method':r['method'],'fit_seconds':r['fit_seconds'],**{k:r['select'][k] for k in ['auc','logloss','brier','ece']}} for r in comparison['rows']])
display(table)
fig,axes=plt.subplots(1,2,figsize=(9,3));axes[0].bar(table.method,table.auc);axes[1].bar(table.method,table.logloss)
axes[0].set(ylabel='Select AUC');axes[1].set(ylabel='Select logloss');fig.suptitle('Real click task: capacity is the only changed variable');fig.tight_layout();fig.savefig(preview/'21_trees.png',dpi=130);plt.show()
Notebook 保存的输出 · 本页未执行
Shared workshop: 20261030T130911-14b5a3de {'raw': [40000, 39], 'train': 22024, 'select': 6006}
method fit_seconds auc logloss brier ece
0 leaves_7 0.126307 0.704286 0.134063 0.030587 0.003655
1 leaves_15 0.175748 0.688730 0.137054 0.030825 0.007551
<Figure size 900x300 with 2 Axes>
Notebook 保存的图表

用损失而不是复杂度解释结果

本章叶数对照只回答这段数据中的容量变化,不能拿它和20章不同模型的运行时间作严格架构benchmark。正式200k实验中线性模型优于两个树候选,160棵树也没有胜过80棵;这个负结果保留。

下面读取正式实验聚合,只用于理解“复杂不保证更好”,不要据此重新调当前模型并继续使用相同audit宣称提升。

PYTHON · 04
formal_receipt,formal=latest()
display(pd.DataFrame([{'method':r['method'],'audit_auc':r['audit']['auc'],'audit_logloss':r['audit']['logloss']} for r in formal['rows'] if r['method'] in ['linear_onehot','gbdt_80','gbdt_160']]))
print('Formal run unchanged:',formal_receipt['run_id'])
print('Workshop select and formal audit have different sample/fit budgets; no mixed leaderboard.')
Notebook 保存的输出 · 本页未执行
method audit_auc audit_logloss
0 linear_onehot 0.721468 0.135423
1 gbdt_80 0.706329 0.136029
2 gbdt_160 0.703035 0.137404
Formal run unchanged: 20261030T125738-f82b6582
Workshop select and formal audit have different sample/fit budgets; no mixed leaderboard.

练习:交叉不是万能的

只把构造标签从XOR改成x1,其他输入、训练预算不变,再比较有无交叉。如果目标本来是加性的,额外交叉未必有帮助。这里标签是构造的机制输入,绝不是对真实点击标签进行篡改。

解释时分别写下表达能力、统计误差和优化预算三种可能性。若想探索真实类别交叉,应先在train上限制词表,使用新select协议,并预留新的最终审计。

PYTHON · 05
target=x[:,0].copy()  # Only the synthetic target mechanism changed.
for name,model in [('additive',LogisticRegression(C=10)),('interaction',make_pipeline(PolynomialFeatures(2,interaction_only=True,include_bias=False),LogisticRegression(C=10)))]:
 model.fit(x[:3000],target[:3000]);r=evaluate(target[3000:],model.predict_proba(x[3000:])[:,1]);print(name,r['auc'],r['logloss'])
Notebook 保存的输出 · 本页未执行
additive 1.0 0.0009870029880096182
interaction 1.0 0.0009621421929528383

继续阅读原文

本章来源

作者教程英文

张伟楠 / 徐健 · CIKM 2016 RTB教程

领域背景资料;具体复现边界见本章正文。

164页原作者讲义:响应预测、竞价、归因、pacing、定向、底价。Lab机制自编,非完整论文配方复现。

作者论文索引英文

张伟楠 · RTB论文索引

领域背景资料;具体复现边界见本章正文。

用于延伸阅读和追溯论文;索引本身不是讲义。

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