推荐模型之Wide&Deep

一、概念

        Wide&Deep模型是Google于2016年提出的混合推荐模型,来自论文《Wide & Deep Learning for Recommender Systems》,通过结合线性模型(Wide部分)与深度神经网络(Deep部分),同时利用记忆能力(捕捉历史行为特征)和泛化能力(挖掘潜在特征关联),用于解决推荐系统中精排阶段的CTR预估等问题。由于结合了线性模型和深度神经网络的优点,模型天然地能够同时处理显式特征和隐式特征。

        提出这个模型的背景是:传统推荐领域使用的线性模型可以很好地挖掘组合特征,但泛化能力不足。例如,它可以记住买了“苹果手机”的用户大概率会购买“苹果手机壳”,但它无法从这个组合中抽象出买“手机”的用户大概率会购买“手机壳”这个概念。因此,如果这时候出现了新的“华为手机”和“华为手机壳”,线性模型无法通过泛化能力将二者关联起来。另一方面,神经网络具有很好的泛化能力,但是记忆能力不足,很难学习低纬度的表示,从而造成过度泛化。例如,它会给买了“苹果手机”的用户同时推荐“苹果手机壳”和“华为手机壳”。因此,结合线性模型和神经网络的Wide&Deep成为了更优的解决方案。

二、原理

1、Wide部分(记忆能力)

        Wide部分是一个线性模型,主要用于记忆历史数据中的显式特征组合。它擅长处理稀疏特征,例如用户和商品的交互记录。通过手动设计的特征交叉(feature cross),Wide部分可以直接学习这些特征组合的权重。Wide部分的核心就是记录历史数据中高频出现的组合(类似共现矩阵,但比共现矩阵更高阶,是统计层面的贡献规律/关系的可学习特征化表示),这往往通过人工来设计有业务意义的特征组合。以电商平台为例,当用户浏览“手机”商品时,Wide部分会激活相关的交叉特征,例如“手机类目 * 手机壳”以及“手机品牌 * 快充头”,从而生成相关的共现关系特征。

2、Deep部分(泛化能力)

        Deep部分是一个深度神经网络,主要用于学习隐式特征和复杂的特征交互(如用户画像、应用类别)。它通过嵌入层(embedding layer)将稀疏特征转化为稠密向量,并通过多层神经网络捕获非线性关系和高阶特征交互。

3、联合训练机制

        两个模块共享损失函数进行端到端训练,最终预测结果为:

        其中,表示交叉特征,是神经网络最后一层的激活值。

三、python实现

        假设有一个简单的推荐场景,用户和商品的交互数据已经被处理成稀疏特征和稠密特征。这里的优化器使用了Adam,实际的工程应用可以分别给Wide和Deep部分设计对应的优化器。例如,比较常用的策略是在Wide部分使用FTRL优化器来处理稀疏的特征交叉,Deep部分则使用Adam或者AdamW。

import pandas as pd
import numpy as np

# 生成10000条用户-电影推荐数据
np.random.seed(42)
n_samples = 10000
data = {
    # 用户特征
    "user_id": np.arange(n_samples),
    "user_age": np.random.randint(12, 70, n_samples),
    "user_gender": np.random.choice([0,1], n_samples),  # 0-女,1-男
    "user_history_clicks": np.random.poisson(5, n_samples),  # 历史点击次数
    
    # 物品特征
    "movie_id": np.random.randint(1000, 2000, n_samples),
    "movie_genre": np.random.choice(["动作", "喜剧", "爱情", "科幻"], n_samples),
    "movie_rating": np.round(np.random.uniform(3.0, 5.0, n_samples), 1),
    
    # 上下文特征
    "hour": np.random.randint(0, 24, n_samples),
    "device": np.random.choice(["手机", "PC", "平板"], n_samples),
    
    # 标签
    "click": np.random.choice([0,1], n_samples, p=[0.7, 0.3])  # 30%点击率
}

df = pd.DataFrame(data)

from sklearn.preprocessing import OneHotEncoder, StandardScaler

# 分类特征编码
cat_features = ["movie_genre", "device"]
encoder = OneHotEncoder()
encoded_features = encoder.fit_transform(df[cat_features]).toarray()

# 数值特征标准化
num_features = ["user_age", "user_history_clicks", "movie_rating"]
scaler = StandardScaler()
scaled_features = scaler.fit_transform(df[num_features])

# 构造Wide部分交叉特征(示例:性别×电影类型)
df["gender_genre"] = df["user_gender"].astype(str) + "_" + df["movie_genre"]
wide_encoder = OneHotEncoder()
wide_features = wide_encoder.fit_transform(df[["gender_genre"]]).toarray()

# 合并特征
import torch
X_wide = torch.FloatTensor(wide_features)
X_deep = torch.FloatTensor(np.hstack([encoded_features, scaled_features]))
y = torch.FloatTensor(df["click"].values)

import torch.nn as nn
import torch.optim as optim

class WideAndDeep(nn.Module):
    def __init__(self, wide_dim, deep_dim):
        super().__init__()
        # Wide部分(线性模型)
        self.wide = nn.Linear(wide_dim, 1)
        
        # Deep部分(3层神经网络)
        self.deep = nn.Sequential(
            nn.Linear(deep_dim, 64),
            nn.ReLU(),
            nn.Linear(64, 32),
            nn.ReLU(),
            nn.Linear(32, 1)
        )
        
        # 最终输出层
        self.sigmoid = nn.Sigmoid()

    def forward(self, x_wide, x_deep):
        wide_out = self.wide(x_wide)
        deep_out = self.deep(x_deep)
        combined = wide_out + deep_out
        return self.sigmoid(combined.squeeze())

# 初始化模型
model = WideAndDeep(wide_dim=X_wide.shape[1], deep_dim=X_deep.shape[1])

from torch.utils.data import Dataset, DataLoader

# 自定义数据集
class RecDataset(Dataset):
    def __init__(self, x_wide, x_deep, y):
        self.x_wide = x_wide
        self.x_deep = x_deep
        self.y = y
        
    def __len__(self):
        return len(self.y)
    
    def __getitem__(self, idx):
        return self.x_wide[idx], self.x_deep[idx], self.y[idx]

# 数据加载器
dataset = RecDataset(X_wide, X_deep, y)
train_loader = DataLoader(dataset, batch_size=64, shuffle=True)

# 训练配置
criterion = nn.BCELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练循环
for epoch in range(10):
    for x_wide, x_deep, labels in train_loader:
        optimizer.zero_grad()
        outputs = model(x_wide, x_deep)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
    print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")

# 评估
with torch.no_grad():
    preds = model(X_wide, X_deep)
    accuracy = ((preds > 0.5).float() == y).float().mean()
    print(f"测试准确率:{accuracy.item():.3f}")

四、总结

特性Wide部分Deep部分
处理方式记录明确的商品组合规律发现潜在的跨品类关联
响应速度即时生效(如节日促销)需要数据累积(新品推荐)
应用场景啤酒和尿布等经典组合小众商品的长尾推荐

 

 

### Wide and Deep 模型的工作原理 Wide and Deep 模型是一种结合线性和神经网络技术的混合模型,旨在同时利用记忆能力和泛化能力来提升推荐系统的性能。其核心思想在于通过 **Wide 部分** 增强模型的记忆能力[^2],以及通过 **Deep 部分** 提升模型的泛化能力。 #### 1. Wide 和 Deep 的结合机制 Wide and Deep 模型的核心是将两个独立的部分——Wide 和 Deep 结合在一起,并共享相同的输入特征集。最终输出由两部分共同决定: - **Wide 部分**: 这是一个广义线性模型 (GLM),通常用于处理稀疏高维特征组合。它能够捕捉低阶交叉特征和显式的交互关系,从而增强模型的记忆能力。 - **Deep 部分**: 这是一组多层感知机 (MLP),主要用于学习复杂的非线性特征表示。它可以自动提取隐含的高阶特征交互,因此具有强大的泛化能力。 两者的结果会通过加权求和的方式融合到一起,形成最终预测值 \( \hat{y} = w_{\text{wide}} f_{\text{wide}}(X) + w_{\text{deep}} f_{\text{deep}}(H(X)) \)[^1]。 --- ### 2. 实现方式 以下是基于 TensorFlow 的一种典型实现方法,展示了如何构建 Wide and Deep 模型。 ```python import tensorflow as tf from tensorflow.keras import layers, models # 定义输入特征列 categorical_columns = [ tf.feature_column.categorical_column_with_identity('category', num_buckets=10), ] numerical_columns = [ tf.feature_column.numeric_column('price'), ] embedding_columns = [ tf.feature_column.embedding_column(col, dimension=8) for col in categorical_columns ] # 构建 Wide 部分 wide_columns = categorical_columns + numerical_columns # 构建 Deep 部分 deep_columns = embedding_columns + numerical_columns # 输入函数定义 def input_fn(): features = { 'category': [[1], [2]], 'price': [[10.0], [20.0]] } labels = [[1], [0]] return tf.data.Dataset.from_tensor_slices((features, labels)).batch(2) # 创建模型 feature_layer_wide = tf.keras.layers.DenseFeatures(wide_columns) feature_layer_deep = tf.keras.layers.DenseFeatures(deep_columns) model = models.Sequential() model.add(layers.Concatenate()([feature_layer_wide, feature_layer_deep])) model.add(layers.Dense(128, activation='relu')) model.add(layers.Dense(64, activation='relu')) model.add(layers.Dense(1, activation='sigmoid')) model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # 训练模型 train_data = input_fn() model.fit(train_data, epochs=5) ``` 上述代码片段实现了 Wide and Deep 模型的基本结构,其中 `DenseFeatures` 层分别负责宽和深部分的特征处理。 --- ### 3. 特征选择策略 在实际应用中,合理分配特征至 Wide 和 Deep 部分至关重要。一般来说: - 将已知有效的特征及其交叉项放入 **Wide 部分**,以便更好地捕获显式的关系。 - 对于复杂、未知或潜在关联较强的特征,则更适合放置在 **Deep 部分** 中进行深层次的学习[^3]。 此外,在某些情况下可以考虑引入因子分解机 (Factorization Machine, FM) 来预处理数据[^4],进一步优化特征表达形式。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值