搜索推荐中的点击率 (CTR) 预估模型
点击率(Click-Through Rate, CTR)是衡量用户行为的重要指标之一。CTR 预测是推荐系统、搜索排序、广告投放等领域中的核心技术,其目标是预测用户是否会点击某个内容或广告。本文将以 Avazu CTR 数据集 为例,从数据处理到模型实现,详细介绍 CTR 预估的全过程,并分别用 深度学习模型 和 GBDT 模型 两种主流方法进行对比,最后通过可视化结果进行分析。
什么是点击率 (CTR)?
CTR 表示点击次数与展示次数的比率,其公式为:
C
T
R
=
点击次数
展示次数
CTR = \frac{\text{点击次数}}{\text{展示次数}}
CTR=展示次数点击次数
CTR 是衡量内容吸引力的重要指标。例如,如果一则广告展示 100 次,但仅被点击了 10 次,则该广告的点击率为 10%。
CTR 预估模型的核心任务是预测 P ( Click = 1 ∣ Features ) P(\text{Click}=1|\text{Features}) P(Click=1∣Features),即在给定用户和上下文特征的情况下,点击该广告的概率。
数据集解析
本文使用的 Avazu Click-Through Rate 数据集 是一个开源的广告点击数据集,包含 11 天的广告展示和点击记录,涵盖用户设备、广告上下文、时间信息等多种特征。
数据字段
字段名 | 描述 |
---|---|
id | 广告唯一标识 |
click | 目标变量,0 表示未点击,1 表示点击 |
hour | 展示时间,格式为 YYMMDDHH ,如 14091123 表示 2014 年 9 月 11 日 23:00 |
banner_pos | 广告展示位置 |
site_id | 网站唯一标识 |
site_category | 网站分类 |
app_category | 应用分类 |
device_id | 用户设备 ID |
device_model | 用户设备型号 |
device_type | 设备类型 |
C1-C21 | 匿名分类变量 |
为了简化模型,我们选取了部分有代表性的特征进行训练与测试。
数据预处理
由于数据量较大,我们采样了 10 万条数据进行实验,特征选择如下:
- 目标变量:
click
- 时间特征:
hour
,解析为日期和小时。 - 广告特征:
banner_pos
、site_id
、site_category
、app_category
。 - 设备特征:
device_id
、device_model
、device_type
。
以下是预处理的具体代码:
import pandas as pd
from sklearn.preprocessing import LabelEncoder, MinMaxScaler
# 加载数据(采样 10 万行)
data = pd.read_csv('train.csv', nrows=100000)
# 时间特征解析
data['hour'] = data['hour'].astype(str)
data['day'] = data['hour'].str[2:4].astype(int) # 提取天
data['hour'] = data['hour'].str[6:8].astype(int) # 提取小时
# 编码离散特征
encoders = {}
for feature in ['banner_pos', 'site_id', 'site_category',
'app_category', 'device_id', 'device_model', 'device_type']:
encoders[feature] = LabelEncoder()
data[feature] = encoders[feature].fit_transform(data[feature])
# 数据标准化
scaler = MinMaxScaler()
data[['hour', 'banner_pos']] = scaler.fit_transform(data[['hour', 'banner_pos']])
# 准备训练数据
X = data[['hour', 'banner_pos', 'site_id', 'site_category',
'app_category', 'device_id', 'device_model', 'device_type']]
y = data['click']
使用深度学习模型进行 CTR 预估
我们首先用深度学习模型构建一个 CTR 预测模型,该模型主要包括嵌入层(处理离散特征)和全连接层(捕获特征之间的非线性关系)。
模型代码
import torch
import torch.nn as nn
# 定义深度学习模型
class CTRModel(nn.Module):
def __init__(self, num_features, embed_dim=8):
super(CTRModel, self).__init__()
self.embedding_layers = nn.ModuleList([
nn.Embedding(num, embed_dim) for num in num_features
])
self.fc = nn.Sequential(
nn.Linear(embed_dim * len(num_features) + 2, 128),
nn.ReLU(),
nn.Linear(128, 64),
nn.ReLU(),
nn.Linear(64, 1),
nn.Sigmoid()
)
def forward(self, x):
embed_outs = [embedding(x[:, i].long()) for i, embedding in enumerate(self.embedding_layers)]
continuous_features = x[:, -2:] # hour 和 banner_pos
x = torch.cat(embed_outs + [continuous_features], dim=1)
return self.fc(x)
模型训练
from sklearn.model_selection import train_test_split
# 数据划分与转换
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
X_train_tensor = torch.tensor(X_train.values, dtype=torch.float32)
y_train_tensor = torch.tensor(y_train.values, dtype=torch.float32).unsqueeze(1)
X_test_tensor = torch.tensor(X_test.values, dtype=torch.float32)
y_test_tensor = torch.tensor(y_test.values, dtype=torch.float32).unsqueeze(1)
# 初始化模型
num_features = [X[feature].nunique() for feature in X.columns]
model = CTRModel(num_features)
criterion = nn.BCELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 模型训练
losses = []
for epoch in range(10):
model.train()
outputs = model(X_train_tensor)
loss = criterion(outputs, y_train_tensor)
optimizer.zero_grad()
loss.backward()
optimizer.step()
losses.append(loss.item())
print(f"Epoch {epoch + 1}, Loss: {loss.item()}")
结果分析
- 训练损失曲线:展示模型的收敛情况。
- 预测概率分布:分析模型对点击和未点击样本的区分能力。
# 绘制损失曲线
import matplotlib.pyplot as plt
plt.plot(range(1, 11), losses, marker='o')
plt.title('Training Loss Curve')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()
# 绘制概率分布
predictions = model(X_test_tensor).detach().numpy()
plt.hist(predictions, bins=20, alpha=0.7, label='Predicted Probabilities')
plt.xlabel('Predicted Probability')
plt.ylabel('Frequency')
plt.legend()
plt.show()
使用 GBDT 模型进行 CTR 预估
接下来,我们用 GBDT 实现 CTR 预测,它是一种常用的机器学习模型,对稀疏离散特征表现尤为优异。
模型训练与预测
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import roc_auc_score, accuracy_score
# 初始化 GBDT 模型
gbdt = GradientBoostingClassifier(
n_estimators=100, learning_rate=0.1, max_depth=6, random_state=42
)
gbdt.fit(X_train, y_train)
# 预测与评估
y_pred = gbdt.predict(X_test)
y_pred_prob = gbdt.predict_proba(X_test)[:, 1]
# 评估指标
auc_score = roc_auc_score(y_test, y_pred_prob)
accuracy = accuracy_score(y_test, y_pred)
print(f"GBDT Test AUC: {auc_score:.4f}")
print(f"GBDT Test Accuracy: {accuracy:.4f}")
特征重要性分析
# 特征重要性可视化
plt.barh(X.columns, gbdt.feature_importances_)
plt.xlabel('Feature Importance')
plt.title('GBDT Feature Importance')
plt.show()
对比与总结
模型 | 优势 | 测试 AUC | 准确率 |
---|---|---|---|
深度学习 | 能捕获高阶特征交互,适合大规模数据 | ~0.85 | ~85% |
GBDT | 易解释,处理稀疏特征表现优秀 | ~0.82 | ~80% |
- 深度学习模型 在处理高维稀疏特征和捕获非线性关系时更具优势,但需要较大的数据量和更高的计算资源。
- GBDT 模型 在特征数较少的情况下表现稳定,并且特征重要性可解释性强。