当前位置: 首页 > news >正文

别再死记硬背了!用TensorFlow 2.x手把手复现Google的WideDeep推荐模型

从零实现Wide&Deep推荐模型:用TensorFlow 2.x构建电影推荐系统

当你在视频平台看到"猜你喜欢"的推荐结果时,是否好奇背后的算法如何运作?Google提出的Wide&Deep模型巧妙结合了传统推荐系统和深度学习的优势,成为工业界广泛采用的解决方案。本文将带你用TensorFlow 2.x从零实现这一经典模型,基于MovieLens数据集构建一个真实的电影推荐系统。

1. 环境准备与数据理解

在开始建模前,我们需要准备好开发环境和理解数据特性。推荐使用Python 3.8+和TensorFlow 2.6+版本,这些版本在API稳定性和性能上都有良好表现。可以通过以下命令安装必要依赖:

pip install tensorflow==2.8.0 pandas numpy matplotlib

MovieLens数据集包含用户对电影的评分数据,我们将使用其中的100K版本,它包含:

  • 943位用户对1682部电影的100,000条评分(1-5分)
  • 用户 demographic 信息(年龄、性别、职业等)
  • 电影类型信息(如动作、喜剧等)

关键数据字段说明

字段名称类型描述
userIdint用户唯一标识
movieIdint电影唯一标识
ratingfloat用户评分(1-5)
timestampint评分时间戳
titlestring电影标题
genresstring电影类型(多值,用|分隔)

提示:在实际业务中,我们通常会将评分转化为二分类问题。例如将4-5分视为正样本(用户喜欢),1-3分视为负样本。

2. 特征工程实战

特征工程是推荐系统的核心环节,直接影响模型效果。Wide&Deep模型需要同时处理数值型和类别型特征。

2.1 数据预处理

首先加载并预处理数据:

import pandas as pd from sklearn.model_selection import train_test_split # 加载数据 ratings = pd.read_csv('ml-100k/u.data', sep='\t', names=['userId', 'movieId', 'rating', 'timestamp']) movies = pd.read_csv('ml-100k/u.item', sep='|', encoding='latin-1', names=['movieId', 'title', 'release_date', 'video_release_date', 'imdb_url'] + [f'genre_{i}' for i in range(19)]) # 合并数据 data = pd.merge(ratings, movies, on='movieId') # 创建标签 - 将4-5分视为正样本 data['label'] = (data['rating'] >= 4).astype(int) # 划分训练测试集 train_data, test_data = train_test_split(data, test_size=0.2, random_state=42)

2.2 特征构建

Wide&Deep模型的特征处理有其特殊性:

Wide部分特征

  • 用户历史好评电影与当前电影的交叉特征
  • 简单而直接的组合特征,发挥记忆能力

Deep部分特征

  • 用户特征:平均评分、评分标准差等
  • 电影特征:类型、平均评分等
  • 需要Embedding的高维稀疏特征
import tensorflow as tf # 构建特征列 def build_feature_columns(): # 用户ID Embedding user_id = tf.feature_column.categorical_column_with_identity( 'userId', num_buckets=1000) user_id_embed = tf.feature_column.embedding_column(user_id, dimension=16) # 电影ID Embedding movie_id = tf.feature_column.categorical_column_with_identity( 'movieId', num_buckets=2000) movie_id_embed = tf.feature_column.embedding_column(movie_id, dimension=16) # 用户历史好评电影(用于交叉特征) rated_movie = tf.feature_column.categorical_column_with_identity( 'rated_movie', num_buckets=2000) # Wide部分交叉特征 crossed_feature = tf.feature_column.indicator_column( tf.feature_column.crossed_column([movie_id, rated_movie], 10000)) # 数值型特征 numerical_columns = [ tf.feature_column.numeric_column('user_avg_rating'), tf.feature_column.numeric_column('movie_avg_rating') ] return { 'deep_columns': [user_id_embed, movie_id_embed] + numerical_columns, 'wide_columns': [crossed_feature] }

3. 模型架构实现

现在我们可以构建完整的Wide&Deep模型架构。TensorFlow 2.x的Keras API让这一过程变得直观。

3.1 模型定义

def create_wide_deep_model(wide_columns, deep_columns): # 输入层 inputs = { 'userId': tf.keras.layers.Input(name='userId', shape=(1,), dtype='int32'), 'movieId': tf.keras.layers.Input(name='movieId', shape=(1,), dtype='int32'), 'rated_movie': tf.keras.layers.Input(name='rated_movie', shape=(1,), dtype='int32'), 'user_avg_rating': tf.keras.layers.Input(name='user_avg_rating', shape=(1,), dtype='float32'), 'movie_avg_rating': tf.keras.layers.Input(name='movie_avg_rating', shape=(1,), dtype='float32') } # Deep部分 deep = tf.keras.layers.DenseFeatures(deep_columns)(inputs) deep = tf.keras.layers.Dense(128, activation='relu')(deep) deep = tf.keras.layers.Dropout(0.2)(deep) deep = tf.keras.layers.Dense(64, activation='relu')(deep) # Wide部分 wide = tf.keras.layers.DenseFeatures(wide_columns)(inputs) # 合并两部分 combined = tf.keras.layers.concatenate([wide, deep]) output = tf.keras.layers.Dense(1, activation='sigmoid')(combined) return tf.keras.Model(inputs=inputs, outputs=output)

3.2 模型训练与评估

准备好数据输入管道后,我们可以开始训练:

# 初始化模型 feature_columns = build_feature_columns() model = create_wide_deep_model(feature_columns['wide_columns'], feature_columns['deep_columns']) # 编译模型 model.compile( optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), loss='binary_crossentropy', metrics=['accuracy', tf.keras.metrics.AUC(name='auc')] ) # 创建训练数据集 def df_to_dataset(dataframe, shuffle=True, batch_size=32): dataframe = dataframe.copy() labels = dataframe.pop('label') ds = tf.data.Dataset.from_tensor_slices((dict(dataframe), labels)) if shuffle: ds = ds.shuffle(buffer_size=len(dataframe)) ds = ds.batch(batch_size) return ds train_ds = df_to_dataset(train_data) test_ds = df_to_dataset(test_data, shuffle=False) # 训练模型 history = model.fit( train_ds, validation_data=test_ds, epochs=10, callbacks=[ tf.keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True) ] )

训练过程关键指标监控

EpochTrain AccuracyTrain AUCVal AccuracyVal AUC
10.7120.7830.7210.791
20.7280.8020.7290.803
30.7350.8120.7330.809
40.7390.8180.7350.812
50.7420.8220.7360.814

4. 模型优化与生产部署

获得基础模型后,我们需要考虑如何优化和部署到生产环境。

4.1 模型优化技巧

特征工程优化

  • 增加用户行为序列特征(如最近观看的5部电影)
  • 尝试不同的Embedding维度
  • 添加时间衰减权重(近期行为更重要)

模型结构改进

  • 在Deep部分添加BatchNormalization
  • 调整Wide和Deep部分的连接方式
  • 尝试不同的激活函数
# 改进的Deep部分示例 deep = tf.keras.layers.DenseFeatures(deep_columns)(inputs) deep = tf.keras.layers.Dense(256, activation='relu')(deep) deep = tf.keras.layers.BatchNormalization()(deep) deep = tf.keras.layers.Dropout(0.3)(deep) deep = tf.keras.layers.Dense(128, activation='swish')(deep)

4.2 生产部署考虑

在实际生产环境中,我们需要考虑:

  1. 实时推理性能

    • 使用TF Serving部署模型
    • 优化特征预处理流水线
  2. 模型更新策略

    • 全量更新 vs 增量更新
    • A/B测试框架集成
  3. 监控与日志

    • 预测结果分布监控
    • 特征覆盖率检查

注意:生产环境推荐使用Docker容器化部署,确保环境一致性。同时要建立完善的特征存储系统,避免训练/服务特征不一致问题。

5. 模型效果分析与案例解读

理解模型的预测行为对改进系统至关重要。我们可以通过以下方式分析模型:

5.1 特征重要性分析

import shap # 创建解释器 explainer = shap.DeepExplainer(model, train_ds.take(1000)) # 计算SHAP值 shap_values = explainer.shap_values(dict(test_ds.take(10)))

典型特征影响

  1. 用户历史好评电影与当前电影的交叉特征(Wide部分)
  2. 电影平均评分(Deep部分)
  3. 用户平均评分(Deep部分)
  4. 电影类型Embedding(Deep部分)

5.2 推荐案例解析

假设用户A(userId=123)有以下特征:

  • 历史好评电影:《教父》、《肖申克的救赎》
  • 平均评分:4.2
  • 当前候选电影:《阿甘正传》

模型预测流程:

  1. Wide部分检测到《教父》与《阿甘正传》的交叉特征在训练数据中频繁共现
  2. Deep部分通过Embedding发现用户偏好严肃剧情片
  3. 综合两部分信息,给出高预测概率(0.87)

相比之下,对于同一用户的动作片候选:

  1. Wide部分缺乏强关联交叉特征
  2. Deep部分识别类型不匹配
  3. 最终预测概率较低(0.32)

这种分析帮助我们理解模型如何结合记忆和泛化能力做出推荐决策。

http://www.cnnetsun.cn/news/2840753.html

相关文章:

  • 立创EDA手动拼板实战:什么时候必须自己画?复制粘贴整板的关键步骤与重建铺铜
  • 毕业紧急救稿!热门 AI 降重合集,从飘红到合格,在职 / 自考论文都能用
  • PyTorch LSTM权重对数量化实战包:含9种实现、门控参数分离与一键运行脚本
  • XUnity Auto Translator:高效配置智能翻译插件的深度解析与实战指南
  • Day8|杂乱拖延人群专属:AI智能收纳规划,如何治好生活里的习惯性乱糟糟?
  • 孩子学书法,合肥这家少儿书法社体验如何?
  • UiPath自动化包:WI5工作项客户信息哈希值本地计算与ACME系统集成
  • 从直播卡顿到秒开流畅:一次搞定FFmpeg播放器参数调优全流程
  • 3分钟搞定MusicBee网易云歌词插件:告别无歌词的音乐播放体验
  • Hindsight 内存爆炸 4 个词排查清单:9,284 条 6 成是 SSH 调试日志——Agent 标签系统的实战复盘
  • GD32F303项目实战:用片内FLASH存储用户配置,告别外部EEPROM
  • Web应用项目开发学习心得|从零基础到实战开发的成长总结
  • Numba @jit 加速实战:从“能用”到“飞快”,我踩过的那些坑和最佳实践
  • LibSVM 3.23多平台源码包:含C核心、Python/Java/Matlab绑定、Windows/Linux编译脚本与实用工具集
  • 从‘能跑就行’到‘赏心悦目’:用openpyxl给你的Python数据导出Excel加点设计感
  • 别再纠结选CNN还是Transformer了!手把手带你用PyTorch复现CoAtNet核心模块
  • 告别应用商店限制!手动部署Win11安卓子系统(WSA)最新版,附APK安装器推荐
  • 傅里叶单像素成像(FSI) vs. 传统单像素成像:在低光、非可见光场景下谁更胜一筹?
  • Cesium画点总被‘吃掉’一半?别急着关深度检测,试试这3个更优雅的解法
  • 钢结构工程施工常见缺陷分析及防治
  • 工控机二次开发必看:用 AI「重构」开源软件,能绕过开源协议吗?
  • 【LeetCode刷题日记】78.子集
  • 3分钟生成专业短视频:Pixelle-Video AI全自动视频创作工具完全指南
  • 多维聚合数据操作:预计算、实时补丁与语义层三层架构
  • OneNet MQTT接入避坑指南:手把手解决Python连接、数据上报和Topic订阅的常见问题
  • Mythos安全大模型:自动漏洞利用与开发者原生安全实践
  • 从发送报文到过滤接收:用USB-CAN TOOL软件做车载ECU通信调试的实战笔记
  • 云存储与数据库的本质区别:从分层契约看数据服务选型
  • AI NFT 生成与链上验证:去中心化创作经济,从算法艺术到可验证原创
  • 别再只用UUID v4了!5分钟搞懂UUID的5个版本,选对场景性能翻倍