news 2026/6/10 9:12:20

别再死记硬背WideDeep了!用TensorFlow 2.x手把手复现一个电影推荐系统(附完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再死记硬背WideDeep了!用TensorFlow 2.x手把手复现一个电影推荐系统(附完整代码)

从零实现Wide&Deep电影推荐系统:TensorFlow 2.x实战指南

推荐系统领域有个经典笑话:当一个工程师说"我理解Wide&Deep模型"时,通常意味着他看过结构图。但真正掌握这个模型的标准是什么?我认为是能够亲手用现代框架完整实现它。本文将带你用TensorFlow 2.x从零构建基于MovieLens数据集的电影推荐系统,过程中你会遇到各种教科书不会提及的工程细节——比如如何处理稀疏特征交叉时的维度爆炸,如何平衡Wide和Deep部分的贡献权重等实际问题。

1. 环境准备与数据洞察

推荐系统的质量90%取决于数据准备。我们从MovieLens 25M数据集入手,这个包含25万用户对6.2万部电影的2500万条评分记录的数据集,比常用的小规模版本更能反映真实场景的复杂性。

关键工具链配置

!pip install tensorflow==2.8.0 pandas==1.4.2 numpy==1.22.3 import tensorflow as tf from tensorflow.keras.layers import Input, Dense, Concatenate print(f"TensorFlow版本: {tf.__version__}") # 应输出2.8.0

数据预处理时需要特别注意几个陷阱:

  • 评分时间戳的时区问题(原始数据使用UTC)
  • 电影标签中的特殊字符(如《Toy Story》包含中文括号)
  • 用户冷启动问题(新用户占测试集的12%)

特征分布示例

特征类型示例值稀疏度
用户ID1234560.003%
电影ID78900.008%
用户平均评分3.75-
电影类型[Action, Comedy]42%

提示:使用tf.data.Dataset处理大型数据集时,务必设置prefetch参数避免I/O阻塞

2. 特征工程深度解析

Wide&Deep模型的核心魔法发生在特征交叉层。我们设计了三类特征:

数值型特征处理

def normalize_numeric(col): mean = train_stats[col]['mean'] std = train_stats[col]['std'] return (df[col] - mean) / std numeric_features = ['user_avg_rating', 'movie_avg_rating'] normalized_features = [normalize_numeric(col) for col in numeric_features]

类别型特征编码

genre_vocab = ['Action','Comedy','Drama'] # 实际应使用完整类型列表 genre_embedding = tf.keras.layers.Embedding( input_dim=len(genre_vocab)+1, output_dim=8, mask_zero=True )

交叉特征构建(Wide部分核心):

crossed_feature = tf.feature_column.indicator_column( tf.feature_column.crossed_column( ['user_top_movie', 'current_movie'], hash_bucket_size=10000 ) )

实际工程中会遇到几个典型问题:

  • 哈希冲突导致特征覆盖(解决方案:增大hash_bucket_size)
  • 交叉特征维度爆炸(解决方案:使用特征筛选)
  • 在线服务时延增加(解决方案:预计算高频交叉项)

3. 模型架构实现细节

用Keras Functional API构建混合模型时,需要特别注意梯度流动问题。以下是经过生产验证的实现方案:

Deep部分构建

deep_inputs = { 'user_id': Input(shape=(1,), name='user_id'), 'movie_id': Input(shape=(1,), name='movie_id') } deep_embed = tf.keras.layers.concatenate([ user_embedding(deep_inputs['user_id']), movie_embedding(deep_inputs['movie_id']) ]) deep_path = Dense(256, activation='relu')(deep_embed) deep_path = Dense(128, activation='relu')(deep_path)

Wide部分构建

wide_inputs = { 'user_top_movie': Input(shape=(1,), name='user_top_movie'), 'current_movie': Input(shape=(1,), name='current_movie') } wide_path = tf.keras.layers.DenseFeatures([crossed_feature])(wide_inputs)

模型组合技巧

combined = Concatenate()([deep_path, wide_path]) output = Dense(1, activation='sigmoid')(combined) model = tf.keras.Model( inputs={**deep_inputs, **wide_inputs}, outputs=output )

注意:Wide部分的输出维度需要与Deep部分最后一层匹配,否则会导致concatenate失败

4. 训练优化与效果评估

与传统模型不同,Wide&Deep需要特殊的训练策略:

混合精度训练配置

policy = tf.keras.mixed_precision.Policy('mixed_float16') tf.keras.mixed_precision.set_global_policy(policy)

自定义损失函数(处理样本不平衡):

def weighted_bce(y_true, y_pred): pos_weight = 2.5 # 正样本权重 loss = tf.keras.losses.binary_crossentropy(y_true, y_pred) weight = y_true * pos_weight + (1 - y_true) return tf.reduce_mean(loss * weight)

评估指标对比

指标Wide部分独立Deep部分独立Wide&Deep联合
AUC0.7820.8010.823
响应时间12ms28ms22ms
冷启动表现较差良好优秀

在MovieLens测试集上的典型训练过程:

Epoch 5/10 1563/1563 [==============================] - 45s 29ms/step - loss: 0.3124 - accuracy: 0.8612 - auc: 0.9021 Val AUC: 0.8246

5. 生产环境部署要点

将模型部署为线上服务时,这几个优化能显著提升性能:

模型轻量化技术

converter = tf.lite.TFLiteConverter.from_keras_model(model) converter.optimizations = [tf.lite.Optimize.DEFAULT] quantized_model = converter.convert()

特征服务缓存策略

class FeatureCache: def __init__(self, max_size=10000): self.cache = LRUCache(max_size) def get(self, user_id): if user_id in self.cache: return self.cache[user_id] else: features = query_feature_service(user_id) self.cache[user_id] = features return features

AB测试方案设计

  • 对照组:传统协同过滤
  • 实验组A:仅Deep部分
  • 实验组B:完整Wide&Deep
  • 关键指标:点击率、观看时长、多样性得分

6. 进阶优化方向

当基础版本跑通后,可以尝试这些提升方案:

动态权重调整

class AdaptiveWeight(tf.keras.layers.Layer): def __init__(self): super().__init__() self.alpha = tf.Variable(0.5, trainable=True) def call(self, wide, deep): return self.alpha * wide + (1 - self.alpha) * deep

多任务学习扩展

rating_pred = Dense(1, activation='linear', name='rating')(combined) click_pred = Dense(1, activation='sigmoid', name='click')(combined) multi_task_model = tf.keras.Model( inputs=inputs, outputs=[rating_pred, click_pred] )

在实际项目中,我发现模型对上映时间较新的电影推荐效果较差。通过分析发现,这是因为时间相关特征没有充分参与交叉。解决方案是在Wide部分添加「用户偏好年代」与「电影年代」的交叉特征,这一调整使新电影点击率提升了18%。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 9:11:25

告别源码修改!用CMake优雅移植CanFestival到ARM Linux(附完整配置流程)

现代CMake工程化实践:零侵入式移植CanFestival到ARM Linux平台在嵌入式开发领域,保持代码整洁性和可维护性已成为衡量工程师专业水平的重要标准。传统"复制-粘贴-修改"的代码移植方式虽然简单直接,却会给项目带来长期维护隐患。本文…

作者头像 李华
网站建设 2026/6/10 8:54:07

人类智能与机器智能的本质差异及人机协同设计指南

1. 项目概述:这不是一场竞赛,而是一次精准的“能力测绘” “3 Key Differences Between Human and Machine Intelligence You Need to Know”——这个标题乍看像一篇泛泛而谈的科普软文,但在我过去十年拆解过200个AI教育类、人机协作类、认知…

作者头像 李华
网站建设 2026/6/10 8:53:01

Sqribble文档工业化流水线:模板驱动的确定性排版系统

1. 项目概述:这不是“一键生成”,而是一套被严重低估的文档工业化流水线你有没有过这种经历:手头有一篇写得不错的公众号长文,老板突然说“赶紧做成个PDF小册子,明天客户要”,结果你打开Word,调…

作者头像 李华
网站建设 2026/6/10 8:41:52

11 个模型同一道闭包题全给 [2,2,2],赢政指数却集体 0 分

#代码执行 #材料约束 #Python闭包 #模型一致性 #工程边界 同一道仅 6 行的 Python 闭包题目,11 个模型的回答几乎完全一致:10 个模型直接给出 [2, 2, 2],仅文心一言 4.5 出现格式破损。这与赢政指数最终全部 0 分的结果形成强烈反差。 题目…

作者头像 李华
网站建设 2026/6/10 8:39:59

242.vivado常用tcl

#生成mcs文件 write_cfgmem -format mcs -size 16 -interface SPIx4 -loadbit {up 0x00000000 "C:/Users/Admin/Desktop/ll/prj_325t/project_1.runs/impl_1/top.bit" } -checksum -force -disablebitswap -file "C:/Users/Admin/Desktop/ll/prj_325t/mcs/led.…

作者头像 李华