news 2026/10/26 22:28:51

NCF视频推荐系统实战:TensorFlow从零实现双塔模型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NCF视频推荐系统实战:TensorFlow从零实现双塔模型

简介:本资源是一套基于深度学习神经网络协同过滤(NCF)模型实现的轻量级视频推荐系统,面向计算机、人工智能、自动化等专业的本科生及初学者,适用于课程设计、毕业设计选题与推荐算法入门实践。压缩包共3个文件,含核心训练脚本ncf_tensorflow.py(TensorFlow实现NCF模型)、测试数据集test-data.csv(用户-视频交互记录)及README.md文档(含环境配置、运行说明与模型原理简述),整体仅3KB,结构精炼、开箱即用。已有294人下载学习,项目代码经实际运行验证,答辩平均分达96分,具备完整训练-预测流程与可复现效果。读者可直接运行体验NCF在稀疏交互场景下的推荐能力,理解嵌入层设计、多层感知机融合及隐向量协同建模逻辑,并以此为基础拓展数据预处理、评估指标添加或模型对比实验。

1. 这不是又一个“协同过滤 demo”:NCF 视频推荐系统源码包,实测跑通、答辩96分、可直接进毕设答辩PPT

你手头那份“基于协同过滤的推荐系统”课程设计,是不是还在用surprise库调SVD,跑完训练集 RMSE 0.87 就截图交差?别急——这个压缩包里装的,是真正把Neural Collaborative Filtering(NCF)拆开揉碎、用 TensorFlow 从零实现、在真实视频交互数据上验证过效果、并最终拿下答辩平均分96 分的完整工程。它不依赖 MovieLens 假数据,而是用test-data.csv模拟用户-视频点击行为;它不只画个 loss 曲线就收工,而是完整封装了数据预处理 → 模型构建 → 训练循环 → Top-K 推荐生成 → 评估指标(HR@10, NDCG@10)计算全流程。适合计算机、人工智能、自动化等专业学生直接用于课程设计、毕业设计开题与实现阶段——尤其当你被导师问“你这个模型和传统 MF 本质区别在哪?”时,能指着ncf_tensorflow.py里GMF和MLP双塔结构那几行代码,说清“非线性特征交叉”到底发生在哪一层。这不是玩具,是经过答辩现场拷问、代码有注释、文档有流程图、连README.md都写了部署注意事项的实战级参考。


2. NCF 不是“深度学习+协同过滤”的简单拼接:为什么选它做视频推荐,以及它的双塔结构怎么落地

2.1 为什么视频推荐场景下 NCF 比传统 MF 更合适?

传统矩阵分解(MF)假设用户偏好和物品特征都落在低维线性空间中,用内积建模交互:$\hat{y}_{ui} = \mathbf{u}_u^\top \mathbf{v}_i$。但视频推荐中,用户行为高度稀疏(一个用户可能只看过几十个视频,而平台有百万级视频),且存在强非线性模式——比如“喜欢《鬼灭之刃》的用户,大概率也喜欢《咒术回战》,但未必喜欢同属“少年漫”的《海贼王》”,这种细粒度偏好无法靠线性内积捕捉。NCF 的核心突破在于:用神经网络替代内积,让模型自己学出用户向量和物品向量之间的高阶非线性关系。它不强行解耦用户/物品隐向量,而是让它们在 MLP 层中充分交叉。对视频推荐而言,这意味着模型能自动发现“观看时长 > 80% + 点赞 + 分享”这类复合行为信号,比单纯“是否点击”更鲁棒。本项目中test-data.csv的字段就包含user_id,video_id,watch_ratio,is_like,is_share,正是为支撑这种多信号融合设计的。

2.2 NCF 的双塔结构:GMF 与 MLP 并行,再拼接输出

NCF 论文(He et al., 2017)提出两种子结构:

  • GMF(Generalized Matrix Factorization):保留 MF 的思想,但用 element-wise product 替代内积,并接入一个线性层:$\mathbf{z}_{\text{GMF}} = \mathbf{u}_u \odot \mathbf{v}_i$,再经Dense(1)输出预测分。它擅长捕捉线性协同信号。
  • MLP(Multi-Layer Perceptron):将用户向量 $\mathbf{u}_u$ 和物品向量 $\mathbf{v}i$ 拼接(concat),送入多层全连接网络:$\mathbf{z}{\text{MLP}} = \text{MLP}([\mathbf{u}_u; \mathbf{v}_i])$,学习高阶非线性交互。

本项目ncf_tensorflow.py中的关键实现如下:

# 构建用户嵌入和物品嵌入(共享 embedding 维度 k=32) user_embedding = tf.keras.layers.Embedding( input_dim=num_users, output_dim=embedding_dim, name='user_embedding' )(user_input) item_embedding = tf.keras.layers.Embedding( input_dim=num_items, output_dim=embedding_dim, name='item_embedding' )(item_input) # GMF 分支:element-wise product gmf_output = tf.keras.layers.Multiply()([user_embedding, item_embedding]) gmf_output = tf.keras.layers.Dense(1, activation=None, name='gmf_out')(gmf_output) # MLP 分支:concat + 多层 Dense mlp_input = tf.keras.layers.Concatenate()([user_embedding, item_embedding]) mlp_output = tf.keras.layers.Dense(64, activation='relu', name='mlp_layer_1')(mlp_input) mlp_output = tf.keras.layers.Dropout(0.2)(mlp_output) # 防止过拟合 mlp_output = tf.keras.layers.Dense(32, activation='relu', name='mlp_layer_2')(mlp_output) mlp_output = tf.keras.layers.Dense(16, activation='relu', name='mlp_layer_3')(mlp_output) mlp_output = tf.keras.layers.Dense(1, activation=None, name='mlp_out')(mlp_output) # 融合:GMF 与 MLP 输出拼接后接 final layer final_input = tf.keras.layers.Concatenate()([gmf_output, mlp_output]) output = tf.keras.layers.Dense(1, activation='sigmoid', name='output')(final_input) # 二分类:是否交互

注意:这里output使用sigmoid是因为test-data.csv中标签是 0/1(是否发生有效交互),而非回归评分。若你的数据是评分(1~5星),需改为linear激活 +MSEloss,并在Dense(1)后加tf.clip_by_value限制输出范围。

2.3 数据预处理:从原始 CSV 到模型可喂入的张量

test-data.csv是模拟的视频行为日志,字段包括:user_id,video_id,watch_ratio,is_like,is_share,timestamp。NCF 输入只需user_id,video_id,label(是否正样本)。本项目在data/目录下未提供清洗脚本,但ncf_tensorflow.py开头有明确的数据加载逻辑:

def load_data(file_path, test_ratio=0.2): df = pd.read_csv(file_path) # 构造正样本:watch_ratio > 0.5 且 is_like == 1 或 is_share == 1 df['label'] = ((df['watch_ratio'] > 0.5) & (df[['is_like', 'is_share']].sum(axis=1) > 0)).astype(int) # 过滤掉 label=0 的样本(或按比例采样负样本,本项目采用 1:1 负采样) pos_df = df[df['label'] == 1] neg_df = df[df['label'] == 0].sample(n=len(pos_df), random_state=42) balanced_df = pd.concat([pos_df, neg_df]).sample(frac=1, random_state=42).reset_index(drop=True) # 映射 user_id, video_id 到连续整数索引(关键!否则 embedding 层报错) user2idx = {uid: idx for idx, uid in enumerate(balanced_df['user_id'].unique())} item2idx = {vid: idx for idx, vid in enumerate(balanced_df['video_id'].unique())} balanced_df['user_idx'] = balanced_df['user_id'].map(user2idx) balanced_df['item_idx'] = balanced_df['video_id'].map(item2idx) return balanced_df[['user_idx', 'item_idx', 'label']].values # 加载数据 train_data = load_data('data/test-data.csv')

这段代码完成了三件事:

  1. 正样本定义:不简单以“点击”为正例,而是结合watch_ratio(观看完成度)和is_like/is_share(主动反馈),更符合视频推荐业务逻辑;
  2. 负采样策略:显式做 1:1 负采样,避免类别严重不平衡导致模型只学“全预测 0”;
  3. ID 映射:将原始字符串/数字 ID 转为从 0 开始的连续整数,这是Embedding层的硬性要求——漏掉这步,运行时会报IndexError: index 12345 is out of bounds for axis 0 with size 1000。

3. 从解压到跑通:五步实操指南,含环境配置、训练命令与结果验证

3.1 环境准备:Python 3.7+ + TensorFlow 2.x,拒绝版本玄学

本项目基于 TensorFlow 2.x 实现(非 TF 1.x 的 session 模式),推荐使用 Python 3.8 或 3.9。切勿用 Python 3.11+—— 某些旧版tensorflow在 3.11 下存在numpy兼容问题。创建虚拟环境并安装依赖:

# 创建虚拟环境(推荐 conda,兼容性更好) conda create -n ncf_env python=3.8 conda activate ncf_env # 安装核心依赖(注意版本匹配) pip install tensorflow==2.11.0 # 2.11 是最后一个支持 CUDA 11.2 的稳定版,兼容性好 pip install pandas numpy scikit-learn matplotlib

提示:如果pip install tensorflow报Could not find a version that satisfies...,说明你的 CUDA 版本不匹配。此时改用 CPU 版本:pip install tensorflow-cpu==2.11.0。视频推荐对实时性要求不高,CPU 训练完全可行,且避免 GPU 驱动冲突。

3.2 项目结构解析:四个文件各司其职

解压后目录结构清晰,每个文件都有明确分工:

文件名作用关键内容
ncf_tensorflow.py主模型文件包含数据加载、模型构建(GMF+MLP)、训练循环、评估函数。所有可修改参数(如embedding_dim=32,epochs=20)集中在此文件顶部。
README.md使用说明书写明了数据格式要求、运行命令、预期输出。特别提醒:“训练前请确认data/test-data.csv存在”,否则FileNotFoundError。
data/test-data.csv示例数据集10000 行模拟数据,含user_id,video_id,watch_ratio,is_like,is_share字段。可直接替换为你自己的视频日志(需保持字段名一致)。
NeuralCollaborativeFiltering_NCF_Tensorflow-main/原作者参考仓库(仅作溯源)本项目已将其核心逻辑提取并重构,此目录可删除,不影响运行。

3.3 一键训练:修改参数后执行,3 分钟看到 loss 下降

进入项目根目录,确保data/test-data.csv存在,然后执行:

python ncf_tensorflow.py --epochs 20 --batch_size 256 --embedding_dim 32 --lr 0.001

命令行参数说明:

  • --epochs 20:训练轮数,答辩演示用 10 轮足够(loss 快速收敛);
  • --batch_size 256:批大小,GPU 显存不足时可降至 128;
  • --embedding_dim 32:嵌入维度,增大(如 64)提升表达力但增加显存,减小(如 16)加快训练;
  • --lr 0.001:学习率,NCF 对 lr 敏感,0.001 是经验安全值;若 loss 不降,尝试 0.0005。

训练过程会打印类似输出:

Epoch 1/20 100/100 [==============================] - 12s 118ms/step - loss: 0.6243 - accuracy: 0.6521 Epoch 2/20 100/100 [==============================] - 12s 117ms/step - loss: 0.5821 - accuracy: 0.6893 ... Epoch 20/20 100/100 [==============================] - 12s 119ms/step - loss: 0.4127 - accuracy: 0.7985

逻辑说明:accuracy是二分类准确率,但推荐系统更看重排序能力。因此训练结束后,脚本会自动调用evaluate_model()函数,在测试集上计算HR@10(Hit Rate)和 NDCG@10(Normalized Discounted Cumulative Gain)。这两个指标才是答辩时老师最认可的——它们衡量“给用户推的 Top-10 视频里,有多少个是他真看过的”。

3.4 结果验证:不只是 loss 下降,要看 Top-K 推荐质量

训练完成后,脚本末尾会输出评估结果,例如:

Evaluation Results: HR@10: 0.723 NDCG@10: 0.518

HR@10 解释:随机选 100 个用户,对每个用户预测 Top-10 视频,统计其中至少有一个命中其历史正样本的比例。0.723 表示 72.3% 的用户,其真实观看视频出现在推荐 Top-10 里。
NDCG@10 解释:不仅看“是否命中”,还看“命中的位置”。如果真实视频排在第 1 位,得分高;排在第 10 位,得分低。0.518 是归一化后的分数,越接近 1 越好。

验证技巧:想亲眼看到推荐结果?在ncf_tensorflow.py末尾添加以下代码(在evaluate_model()调用后):

# 为 user_id=0 生成 Top-10 推荐 user_idx = 0 all_item_idxs = np.arange(num_items) user_array = np.full(len(all_item_idxs), user_idx) scores = model.predict([user_array, all_item_idxs]).flatten() top10_items = all_item_idxs[np.argsort(scores)[-10:][::-1]] print(f"Top-10 recommendations for user {user_idx}: {top10_items}")

运行后即可看到具体推荐列表,方便写进毕设 PPT 的“案例展示”页。


4. 避坑指南:那些让答辩前夜崩溃的 4 个血泪问题,现在就帮你绕开

4.1 现象:IndexError: index XXX is out of bounds for axis 0 with size YYY

原因:test-data.csv中的user_id或video_id是字符串(如"U1001"、"V2002"),而Embedding层要求输入是0 ~ num_users-1的整数索引。代码中虽有map(user2idx),但如果user2idx字典没覆盖所有 ID(比如数据里混入了新用户),就会报此错。
解决:检查test-data.csv,确保所有user_id和video_id都在balanced_df的unique()结果中。更稳妥做法是在load_data()函数里加兜底:

# 替换 map 行为,加入默认值 balanced_df['user_idx'] = balanced_df['user_id'].map(user2idx).fillna(len(user2idx)).astype(int) balanced_df['item_idx'] = balanced_df['video_id'].map(item2idx).fillna(len(item2idx)).astype(int)

4.2 现象:训练 loss 不下降,卡在 0.69 左右(≈ log(2))

原因:这是典型的“模型没学到任何东西”信号,常见于两类错误:

  • 标签全为 0 或全为 1:检查test-data.csv中label列是否真的有 0/1 混合。用df['label'].value_counts()验证;
  • 负采样失败:neg_df.sample(n=len(pos_df))若len(pos_df)==0,会报错;若len(pos_df)很小(<100),负采样后数据量不足,模型欠拟合。
    解决:先运行python -c "import pandas as pd; df=pd.read_csv('data/test-data.csv'); print(df['label'].value_counts())",确认正负样本比例合理(理想 1:1)。若正样本极少,放宽正样本定义条件(如watch_ratio > 0.3)。

4.3 现象:AttributeError: module 'tensorflow' has no attribute 'keras'

原因:安装了tensorflow-cpu但版本过低(如 1.x),或同时安装了keras独立包(TF 2.x 自带 Keras,冲突)。
解决:彻底清理后重装:

pip uninstall tensorflow keras -y pip install tensorflow-cpu==2.11.0

验证:python -c "import tensorflow as tf; print(tf.__version__); print(hasattr(tf, 'keras'))"

4.4 现象:ValueError: Input 0 is incompatible with layer ... expected shape=(None, 1), found shape=(None, 2)

原因:model.fit()传入的x是二维数组(如[[u1,i1], [u2,i2]]),但模型输入层期望两个独立的一维张量(user_input和item_input)。
解决:检查ncf_tensorflow.py中model.fit()的调用方式。正确写法是:

# ✅ 正确:传入两个数组 model.fit( x=[train_data[:, 0], train_data[:, 1]], # user_idx 列, item_idx 列 y=train_data[:, 2], ... ) # ❌ 错误:传入一个二维数组 # model.fit(x=train_data[:, :2], y=train_data[:, 2]) # 会报上述错

5. 毕设/课设进阶技巧:三招让项目从“能跑”升级为“有亮点”

5.1 加入时间特征:用timestamp提升序列感知能力

原始test-data.csv包含timestamp,但当前 NCF 模型是静态的(不考虑行为时序)。要体现“用户兴趣会漂移”,可在数据预处理时构造时间衰减权重:

  • 对每个(user, item)样本,计算其timestamp距离数据集最大时间的天数delta_t;
  • 定义权重weight = exp(-delta_t / 30)(30 天为半衰期);
  • 在model.fit()中传入sample_weight参数:
# 在 load_data() 中计算 weight max_ts = balanced_df['timestamp'].max() balanced_df['weight'] = np.exp(-(max_ts - balanced_df['timestamp']) / (24*3600*30)) # fit 时传入 model.fit( x=[train_data[:, 0], train_data[:, 1]], y=train_data[:, 2], sample_weight=train_data[:, 3], # 第四列是 weight ... )

这样,近期行为对模型影响更大,答辩时可强调“解决了冷启动与兴趣漂移问题”。

5.2 替换评估指标:用 Recall@K 替代 Accuracy,更贴合推荐本质

accuracy在推荐中意义有限(99% 样本是负例,准确率虚高)。应替换为Recall@K(召回率):

  • 对每个用户,取其所有正样本true_items;
  • 模型预测 Top-K 推荐pred_items;
  • Recall@K = len(true_items ∩ pred_items) / len(true_items)。
    在evaluate_model()函数中替换计算逻辑:
def recall_at_k(y_true, y_score, k=10): # y_true: 用户真实正样本集合列表,y_score: 每个 item 的预测分列表 recalls = [] for true_set, scores in zip(y_true, y_score): topk_items = np.argsort(scores)[-k:][::-1] hit = len(set(topk_items) & set(true_set)) recalls.append(hit / len(true_set) if len(true_set) > 0 else 0) return np.mean(recalls) # 调用 recall_10 = recall_at_k(user_true_items_list, user_pred_scores_list, k=10) print(f"Recall@10: {recall_10:.3f}")

5.3 可视化推荐效果:生成热力图展示用户-视频匹配强度

答辩 PPT 最怕文字堆砌。用matplotlib画一张用户×视频预测分热力图,直观展示模型“懂用户”:

import matplotlib.pyplot as plt import seaborn as sns # 为前 20 个用户,计算对前 50 个视频的预测分 users = np.arange(20) items = np.arange(50) U, I = np.meshgrid(users, items, indexing='ij') scores = model.predict([U.flatten(), I.flatten()]).reshape(U.shape) plt.figure(figsize=(12, 8)) sns.heatmap(scores, annot=True, fmt='.3f', cmap='YlGnBu') plt.title('Predicted Interaction Scores (User × Video)') plt.xlabel('Video ID') plt.ylabel('User ID') plt.savefig('ncf_heatmap.png', dpi=300, bbox_inches='tight') plt.show()

这张图能清晰显示:某些用户对特定视频区域(如左上角)分数极高,证明模型捕获了群体偏好,比单纯说“准确率 79%”有力得多。

从那以后我每次做推荐类毕设,都会强制走一遍df['label'].value_counts()和model.predict()单样本调试——前者防数据污染,后者防模型黑匣子。这两个动作花不了 2 分钟,却能避开 80% 的答辩翻车现场。希望帮到你。

本文还有配套的精品资源,点击获取

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

OpenMontage 深度解析:用 agentic AI 重构视频制作流水线

1. 从"剪辑地狱"到"对话式成片"&#xff1a;OpenMontage 到底在解决什么问题做视频的人都有一个共同的痛&#xff1a;素材拍完只是开始&#xff0c;真正的折磨在后期。一个三分钟的成片&#xff0c;背后可能是四小时的素材筛选、两小时的粗剪、反复调整的转…

作者头像 李华
网站建设 2026/10/26 22:26:09

自考公共管理三科真题速记与高效备考策略

1. 项目概述&#xff1a;自考三科真题速记的价值与定位公共管理类自考科目因其理论性强、知识点密集的特点&#xff0c;一直是考生备考的难点。这套真题速记资料聚焦《公共部门人力资源管理》《公共行政学》《公共经济学》三大核心科目&#xff0c;通过对历年真题的系统梳理&am…

作者头像 李华
网站建设 2026/10/26 22:24:58

VCS仿真全流程配置:从环境变量到回归提速的实战指南

简介&#xff1a;《VCS User Guide》是Synopsys官方发布的VCS验证工具用户手册&#xff08;2021.09版&#xff09;&#xff0c;面向数字、模拟及混合信号IC设计验证工程师&#xff0c;解决从环境搭建、编译仿真到结果调试的自动化验证问题。手册系统讲解VCS的入门流程、仿真器能…

作者头像 李华
网站建设 2026/10/26 22:24:13

WinCC报表控件安装与配置全攻略:版本兼容、静默安装与数据绑定

简介&#xff1a;面向自动化工程师与PLC从业者的WinCC报表控件资源包&#xff0c;聚焦WinCC V7.x与博途WinCC环境下的报表生成需求。它通过图形化配置替代脚本编程&#xff0c;支持曲线图、柱状图、饼图、仪表盘等图表类型&#xff0c;可绑定实时与历史归档数据&#xff0c;并具…

作者头像 李华
网站建设 2026/10/26 22:23:37

深度伪造技术原理、检测与应对方案全解析

1. 深度伪造技术现状与影响分析深度伪造&#xff08;Deepfake&#xff09;技术近年来呈现爆发式增长态势。根据2023年最新研究数据显示&#xff0c;全球深度伪造视频数量较去年增长了近300%&#xff0c;其中恶意伪造内容占比高达42%。这项技术最初由Reddit用户在2017年提出&…

作者头像 李华
网站建设 2026/10/26 22:23:19

大疆Tello无人机PC端图传链路搭建:FFmpeg解码H.264与OpenCV显示实战

简介&#xff1a;这份资源面向希望在PC端操控大疆Tello无人机的开发者与爱好者&#xff0c;围绕UDP指令通信、基础飞行动作控制、Visual Studio集成Qt搭建图形界面&#xff0c;以及FFmpeg解码H.264视频流并用OpenCV在界面中实时显示等环节&#xff0c;提供一套可运行的工程实现…

作者头像 李华