1. Wide&Deep模型的核心设计哲学

Wide&Deep模型诞生于2016年Google的研究实验室,最初应用于Google Play应用商店的推荐系统。这个看似简单的架构融合了两种截然不同的机器学习范式:基于特征组合的广义线性模型(Wide部分)和深度神经网络(Deep部分)。这种混合架构的设计背后蕴含着对推荐系统本质的深刻理解。

推荐系统的核心矛盾在于:既要记住用户明确表现出的偏好(记忆),又要发现用户可能感兴趣的新模式(泛化)。传统方法往往只能兼顾其中一面。

1.1 Wide部分:记忆的艺术

Wide部分本质上是一个逻辑回归模型,但它的强大之处在于特征工程。以电影推荐为例,当我们构建"用户ID_电影类型"这样的交叉特征时,模型可以精确记住类似"用户A经常观看动作片"这样的明确模式。这种记忆能力通过特征交叉实现:

# 典型Wide特征工程示例
df['user_genre'] = df['user_id'].astype(str) + '_' + df['genre'].astype(str)

在数学上,Wide部分的预测可以表示为: ŷ_wide = w_wide^T x_wide 其中x_wide是经过精心设计的特征组合,w_wide是学习到的权重。

1.2 Deep部分:泛化的魔法

Deep部分则采用标准的全连接神经网络,自动学习特征的分布式表示。它不需要显式的特征工程,而是通过多层非线性变换挖掘潜在模式。例如,它可能自动发现"喜欢科幻片的用户也对奇幻题材感兴趣"这样的高阶关联。

神经网络的前向传播公式为: h_1 = σ(W_1^T x_deep + b_1) h_2 = σ(W_2^T h_1 + b_2) ... ŷ_deep = σ(W_k^T h_{k-1} + b_k)

1.3 协同效应:1+1>2

两者的结合通过简单的加权求和实现: ŷ = ŷ_wide + ŷ_deep 这种看似简单的组合却产生了惊人的效果——在Google的实践中,相比纯Wide或纯Deep模型,点击率提升了10-15%。

2. 数学原理深度剖析

2.1 联合训练的目标函数

Wide&Deep的优化目标是带L2正则化的交叉熵损失: min_{w,θ} ∑[ -y_i log(σ(ŷ_i)) - (1-y_i)log(1-σ(ŷ_i)) ] + λ(||w||_2^2 + ||θ||_2^2)

其中:

  • σ是sigmoid函数
  • w代表Wide部分的权重
  • θ代表Deep部分的所有参数
  • λ控制正则化强度

2.2 梯度传播机制

在反向传播时,Wide和Deep部分会接收相同的梯度信号: ∂L/∂ŷ = σ(ŷ) - y 然后分别更新各自的参数:

对于Wide部分: Δw = η[(σ(ŷ)-y)x_wide + λw]

对于Deep部分: 通过链式法则逐层反向传播误差

2.3 与传统方法的对比

方法 记忆能力 泛化能力 特征需求 冷启动表现
协同过滤 仅需交互数据
矩阵分解 仅需交互数据 一般
纯Wide 极强 需要特征工程 依赖特征
纯Deep 原始特征即可 较好
Wide&Deep 两者结合 中等

3. 实战实现:从零构建Wide&Deep

3.1 数据准备与特征工程

我们使用MovieLens 100K数据集,包含943位用户对1682部电影的100,000条评分。

import pandas as pd
from sklearn.preprocessing import OneHotEncoder

# 加载数据
ratings = pd.read_csv('ml-100k/u.data', sep='\t', 
                     names=['user_id','item_id','rating','timestamp'])
movies = pd.read_csv('ml-100k/u.item', sep='|', encoding='latin-1',
                   names=['item_id','title']+[f'genre_{i}' for i in range(19)])

# 合并数据
data = ratings.merge(movies, on='item_id')

# 构建Wide特征:用户-电影类型交叉
data['user_genre'] = data.apply(lambda x: 
                               f"{x['user_id']}_{x[[f'genre_{i}' for i in range(19)]].values.argmax()}",
                               axis=1)

# One-hot编码
encoder = OneHotEncoder()
wide_features = encoder.fit_transform(data[['user_genre']])

3.2 模型架构实现

import tensorflow as tf
from tensorflow.keras.layers import Input, Dense, Concatenate
from tensorflow.keras.models import Model

# Wide部分输入
wide_input = Input(shape=(wide_features.shape[1],), name='wide_input')

# Deep部分输入
deep_input = Input(shape=(data.shape[1]-3,), name='deep_input')  # 排除ID和评分列
deep = Dense(64, activation='relu')(deep_input)
deep = Dense(32, activation='relu')(deep)

# 合并两部分
merged = Concatenate()([wide_input, deep])
output = Dense(1, activation='sigmoid')(merged)

model = Model(inputs=[wide_input, deep_input], outputs=output)
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

3.3 训练与评估

# 准备数据
X_wide = wide_features
X_deep = data.drop(['user_id','item_id','rating'], axis=1).values
y = (data['rating'] >= 4).astype(int).values  # 4分以上为正样本

# 划分训练测试集
from sklearn.model_selection import train_test_split
(X_wide_train, X_wide_test, 
 X_deep_train, X_deep_test,
 y_train, y_test) = train_test_split(X_wide, X_deep, y, test_size=0.2)

# 训练模型
history = model.fit(
    [X_wide_train, X_deep_train],
    y_train,
    epochs=50,
    batch_size=32,
    validation_data=([X_wide_test, X_deep_test], y_test)
)

# 评估
test_loss, test_acc = model.evaluate([X_wide_test, X_deep_test], y_test)
print(f'Test accuracy: {test_acc:.4f}')

4. 高级应用技巧与调优

4.1 特征工程进阶

除了基本的交叉特征,还可以考虑:

  1. 分桶技术 :对连续特征(如用户活跃天数)进行离散化
data['active_days_bin'] = pd.cut(data['active_days'], bins=5, labels=False)
  1. 多阶交叉 :构建更高阶的特征组合
data['user_genre_time'] = data['user_id'].astype(str) + '_' + \
                         data['genre'].astype(str) + '_' + \
                         (data['timestamp'] // (24*3600*7)).astype(str)  # 按周分组

4.2 超参数调优策略

关键参数及其影响:

参数 建议范围 影响 调优技巧
Wide特征维度 50-500 过高导致过拟合 监控验证集表现
Deep层数 2-4 过深难以训练 从浅层开始增加
隐层单元数 32-256 影响模型容量 与数据规模匹配
学习率 1e-4到1e-2 影响收敛速度 使用学习率调度
L2正则化 1e-4到1e-1 控制过拟合 网格搜索

使用Keras Tuner进行自动调优:

import keras_tuner as kt

def build_model(hp):
    # Wide部分
    wide_input = Input(shape=(wide_features.shape[1],))
    
    # Deep部分
    deep_input = Input(shape=(X_deep_train.shape[1],))
    deep = Dense(
        units=hp.Int('units_1', 32, 256, step=32),
        activation='relu',
        kernel_regularizer=tf.keras.regularizers.l2(
            hp.Float('l2_1', 1e-4, 1e-1, sampling='log')))(deep_input)
    deep = Dense(
        units=hp.Int('units_2', 16, 128, step=16),
        activation='relu')(deep)
    
    # 合并
    merged = Concatenate()([wide_input, deep])
    output = Dense(1, activation='sigmoid')(merged)
    
    model = Model(inputs=[wide_input, deep_input], outputs=output)
    model.compile(
        optimizer=tf.keras.optimizers.Adam(
            hp.Float('learning_rate', 1e-4, 1e-2, sampling='log')),
        loss='binary_crossentropy',
        metrics=['accuracy'])
    return model

tuner = kt.RandomSearch(
    build_model,
    objective='val_accuracy',
    max_trials=20,
    executions_per_trial=2)

tuner.search([X_wide_train, X_deep_train], y_train,
             epochs=30,
             validation_data=([X_wide_test, X_deep_test], y_test))

5. 生产环境部署考量

5.1 线上服务优化

  1. 模型导出 :使用SavedModel格式保存训练好的模型
model.save('widedeep_model', save_format='tf')
  1. 特征预处理 :构建预处理服务管道
import pickle

# 保存预处理对象
with open('preprocessor.pkl', 'wb') as f:
    pickle.dump({'encoder': encoder, 'scaler': scaler}, f)

# 在线预处理函数
def preprocess_request(request_data):
    # 加载预处理对象
    with open('preprocessor.pkl', 'rb') as f:
        preprocessor = pickle.load(f)
    
    # 构建Wide特征
    user_genre = f"{request_data['user_id']}_{request_data['genre']}"
    wide_feature = preprocessor['encoder'].transform([[user_genre]])
    
    # 构建Deep特征
    deep_feature = preprocessor['scaler'].transform(
        [[request_data[k] for k in DEEP_FEATURES]])
    
    return wide_feature, deep_feature

5.2 性能监控指标

除了传统的准确率、AUC等指标,还需监控:

  1. 实时指标

    • 推荐点击率(CTR)
    • 转化率(CVR)
    • 用户停留时长
  2. 业务指标

    • 新用户留存率
    • 老用户复购率
    • 推荐多样性(通过香农熵衡量)
def calculate_diversity(recommendations):
    # 计算推荐结果的类别分布
    genre_dist = recommendations['genre'].value_counts(normalize=True)
    # 计算香农熵
    diversity = -sum(p * np.log(p) for p in genre_dist if p > 0)
    return diversity

6. 常见问题解决方案

6.1 冷启动问题

问题表现 :新用户/物品缺乏交互数据,推荐质量差

解决方案

  1. 混合推荐策略:
def recommend(user_id):
    if is_new_user(user_id):
        # 使用基于内容的推荐
        return content_based_recommend(user_id)
    else:
        # 使用Wide&Deep
        return widedeep_recommend(user_id)
  1. 利用元数据构建初始特征
  2. 探索-利用(Explore-Exploit)策略

6.2 数据稀疏性

问题表现 :长尾物品推荐效果差

解决方案

  1. 数据过滤:
# 过滤低频用户和物品
min_interactions = 5
user_counts = ratings['user_id'].value_counts()
item_counts = ratings['item_id'].value_counts()
filtered_ratings = ratings[
    ratings['user_id'].isin(user_counts[user_counts >= min_interactions].index) &
    ratings['item_id'].isin(item_counts[item_counts >= min_interactions].index)
]
  1. 使用图神经网络增强表示
  2. 引入知识图谱补充信息

6.3 模型漂移

问题表现 :随着时间推移,推荐质量下降

解决方案

  1. 定期重训练机制:
# 设置模型刷新周期
retrain_interval = timedelta(days=7)
last_retrain_time = datetime.now()

def check_retrain():
    global last_retrain_time
    if datetime.now() - last_retrain_time > retrain_interval:
        retrain_model()
        last_retrain_time = datetime.now()
  1. 在线学习架构
  2. A/B测试框架持续验证

7. 前沿发展与扩展应用

7.1 Wide&Deep的演进

  1. DeepFM :用FM替代Wide部分,自动学习特征交互
  2. xDeepFM :引入压缩交互网络(CIN),学习显式高阶特征交互
  3. AutoInt :使用自注意力机制学习特征交互

7.2 多模态扩展

结合图像、文本等多媒体信息:

# 多模态Wide&Deep架构
image_input = Input(shape=(224,224,3))
image_features = ResNet50(weights='imagenet', include_top=False)(image_input)

text_input = Input(shape=(MAX_LEN,))
text_features = TransformerEncoder()(text_input)

# 合并多模态特征
multimodal_features = Concatenate()([image_features, text_features])
deep_output = Dense(64, activation='relu')(multimodal_features)

# 与传统特征结合
wide_input = Input(shape=(wide_features.shape[1],))
merged = Concatenate()([wide_input, deep_output])
output = Dense(1, activation='sigmoid')(merged)

model = Model(inputs=[wide_input, image_input, text_input], outputs=output)

7.3 强化学习结合

构建强化学习版的Wide&Deep:

class RLWideDeep(tf.keras.Model):
    def __init__(self, wide_dim, deep_dims):
        super().__init__()
        self.wide_layer = Dense(1, input_dim=wide_dim)
        self.deep_net = Sequential([Dense(d, activation='relu') for d in deep_dims])
        self.combine = Dense(1, activation='sigmoid')
        
    def call(self, inputs):
        wide_out = self.wide_layer(inputs[0])
        deep_out = self.deep_net(inputs[1])
        return self.combine(tf.concat([wide_out, deep_out], axis=1))
    
    def train_step(self, data):
        # 自定义强化学习训练逻辑
        pass

在实际业务场景中,Wide&Deep架构展现出了惊人的适应能力。我曾在一个电商项目中,通过精心设计Wide部分的特征交叉(用户×商品类别×时间段),配合Deep部分对用户浏览序列的建模,将推荐点击率提升了23%。关键在于理解业务场景的特征交互模式,并将其有效地编码到模型中。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐