推荐系统经典模型YouTubeDNN代码

文章目录

    • 前言
    • 数据预处理部分
    • 模型训练预测部分
    • 总结与问答

前言

  • 上一篇讲到过YouTubeDNN论文部分内容,但是没有代码部分。最近网上教学视频里看到一段关于YouTubeDNN召回算法的代码,现在我分享一下给大家参考看一下,并附上一些我对代码的理解。

数据预处理部分

  • 首先我们需要对数据集进行预处理,数据集格式如下图所示
    在这里插入图片描述
  • 根据YouTubeDNN论文,输入的数据是用户的信息、视频的ID序列、用户搜索的特征和一些地理信息等其他信息。到了基于文章内容的信息流产品中,就变成了用户 ID、年龄、性别、城市、阅读的时间戳再加上视频的ID。我们把这些内容可以组合成YouTubeDNN需要的内容,最后处理成需要的Embedding。
from tqdm import tqdm
import numpy as np
import random
from tensorflow.python.keras.preprocessing.sequence import pad_sequences

def gen_data_set(data, negsample=0):
    # 根据timestamp排序数据,并替换
    data.sort_values("timestamp", inplace=True)
    #根据item_id进行去重
    item_ids = data['item_id'].unique()

    # 构建训练与测试list
    train_set = list()
    test_set = list()

    for reviewrID, hist in tqdm(data.groupby('user_id')):
        # 正样本列表
        pos_list = hist['item_id'].tolist()
        rating_list = hist['rating'].tolist()

        if negsample > 0:
            # 候选集中去掉用户看过的item项目
            candidate_set = list(set(item_ids) - set(pos_list))
            # 随机选择负采样样本
            neg_list = np.random.choice(candidate_set, size=len(pos_list) * negsample, replace=True)
        for i in range(1, len(pos_list)):
            if i != len(pos_list) - 1:
                # 训练集和测试集划分
                train_set.append((reviewrID, hist[::-1], pos_list[i], 1, len(hist[:: -1]), rating_list[i]))
                for negi in range(negsample):
                    train_set.append((reviewrID, hist[::-1], neg_list[i * negsample + negi], 0, len(hist[::-1])))
                else:
                    test_set.append((reviewrID, hist[::-1], pos_list[i], 1, len(hist[::-1]), rating_list[i]))

            # 打乱数据集
            random.shuffle(train_set)
            random.shuffle(test_set)
            return train_set, test_set


def gen_model_input(train_set, user_profile, seq_max_len):
    # 用户id
    train_uid = np.array([line[0] for line in train_set])
    # 历史交互序列
    train_seq = [line[1] for line in train_set]
    # 物品id
    train_iid = np.array([line[2] for line in train_set])
    # 正负样本标签
    train_label = np.array([line[3] for line in train_set])
    # 历史交互序列长度
    train_hist_len = np.array([line[4] for line in train_set])

    train_seq_pad = pad_sequences(train_seq, maxlen=seq_max_len, padding='post', truncating='post', value=0 )
    train_model_input = {"user_id": train_uid, "item_id": train_iid, "hist_item_id": train_seq_pad, "hist_len": train_hist_len}
    for key in {"gender", "age", "city"}:
        train_model_input[key] = user_profile.loc[train_model_input['user_id']][key].values

    return train_model_input, train_label
  • 代码解释:
    • **gen_data_set() **主要作用是接收数据集(data)和一个负采样(negsample)参数,返回一个训练集列表(trainset)和一个测试集列表(testset)。具体流程是先通过timetamp列对数据进行排序,根据item_id进行去重;然后根据user_id分组形成正负样本(正样本为购买过的,负样本为没有购买过的),对于negsample大于0,我们就要进行负采样,也就是随机选择一些没有购买过的商品为负样本,然后将它们保存到训练集中;最后,将正负样本数据以及其他信息(如历史交互序列、用户 ID 和历史交互序列的长度)保存到训练集列表和测试集列表中。
    • gen_model_input() 主要作用就是接收一个训练集列表、用户画像信息和序列最大长度参数,返回训练模型的输入和标签。首先将训练集列表拆分成 5 个列表(train_uid train_seq train_iid train_label train_hist_len);然后使用pad_sequences() 函数对历史交互序列进行填充处理,将其变成长度相同的序列。最后,将用户画像信息(gender age city)加入到训练模型的关键字中,返回训练模型的输入和标签。
    • pad_sequences():pad_sequences()这个函数是来自于TensorFlow中数据预处理的一种方法,主要就是数据预填充。在TensorFlow2.8版本之前可以通过from tensorflow.python.keras.preprocessing.sequence import pad_sequences调用,后期版本则是在keras.utils里,这里建议使用低版本tesorflow2,具体版本信息请参考链接。

模型训练预测部分

  • 进入模型训练阶段,我们需要先了解一下,代码里我们所使用的一些包和函数介绍
    • sklearn.preprocessing.LabelEncoder:对数据进行特征编码
    • deepctr.feature_column.SparseFeat, VarLenSparseFeat:用户构建用户和物品特征输入。
    • deepmatch:用于构建和训练推荐模型
    • faiss:高效向量相似性搜索库
    • models.recall.preprocess.gen_data_set, gen_model_input:数据预处理部分(自建)
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from models.recall.preprocess import gen_data_set, gen_model_input
from deepctr.feature_column import SparseFeat, VarLenSparseFeat
from tensorflow.python.keras import backend as K
from tensorflow.python.keras.models import Model
import tensorflow as tf
from deepmatch.models import *
from deepmatch.utils import recall_N
from deepmatch.utils import sampledsoftmaxloss
import numpy as np
from tqdm import tqdm
import faiss

class YouTubeModel(object):
    def __init__(self, embedding_dim=32):
        self.SEQ_LEN = 50
        self.embedding_dim = embedding_dim
        self.user_feature_columns = None
        self.item_feature_columns = None

    def training_set_construct(self):
        # 数据加载
        data = pd.read_csv('../../data/read_history.csv')
        # 负采样个数
        negsample = 0
        # 特征编码
        features = ["user_id", "item_id", "gender", "age", "city"]
        features_max_idx={}
        for feature in features:
            lbe = LabelEncoder()
            data[feature] = lbe.fit_transform(data[feature]) + 1
            features_max_idx[feature] = data[feature].max() + 1

        # 抽取用户、物品特征(并去重)
        user_info = data[["user_id", "gender", "age", "city"]].drop_duplicates('user_id')
        item_info = data[["item_id"]].drop_duplicates('item_id')

        # 构建输入数据
        train_set, test_set = gen_data_set(data, negsample)
        # 转化模型输入
        train_model_input, train_label = gen_model_input(train_set, user_info, self.SEQ_LEN)
        test_model_input, test_label = gen_model_input(test_set, user_info, self.SEQ_LEN)

        # 用户端特征输入
        self.user_feature_columns = [SparseFeat('user_id', features_max_idx['user_id'], 16),
                                     SparseFeat('gender', features_max_idx['gender'], 16),
                                     SparseFeat('age', features_max_idx['age'], 16),
                                     SparseFeat('city', features_max_idx['city'], 16),
                                     VarLenSparseFeat(SparseFeat('hist_item_id', features_max_idx['item_id'],
                                                                 self.embedding_dim, embedding_name='item_id'),
                                                      self.SEQ_LEN, 'mean', 'hist_len')
                                     ]
        # 物品端特征输入
        self.item_feature_columns = [SparseFeat('item_id', features_max_idx['item_id'], self.embedding_dim)]

        return train_model_input, train_label, test_model_input, test_label, train_set, test_set, user_info, item_info

    def training_model(self, train_model_input, train_label):
        K.set_learning_phase(True)
        if tf.__version__ >= '2.0.0':
            tf.compat.v1.disable_eager_execution()
        # 定义模型
        model = YoutubeDNN(self.user_feature_columns, self.item_feature_columns, num_sampled=100,
                           user_dnn_hidden_units=(128, 64, self.embedding_dim))
        # 使用adam优化,损失函数使用softmax+cross_entropy
        model.compile(optimizer="adam", loss=sampledsoftmaxloss)
        # 训练并保存训练过程中的数据
        model.fit(train_model_input, train_label, batch_size=512, epochs=20, verbose=1, validation_split=0.0,)
        return model

    # 提取用户和物品的embedding layer
    def extract_embedding_layer(self, model, test_model_input, item_info):
        all_item_model_input = {"item_id": item_info['item_id'].values, }
        # 获取用户、item的embedding_layer
        user_embedding_model = Model(inputs=model.user_input, outputs=model.user_embedding)
        item_embedding_model = Model(inputs=model.item_input, outputs=model.item_embedding)
        user_embs = user_embedding_model.predict(test_model_input, batch_size=2 ** 12)
        item_embs = item_embedding_model.predict(all_item_model_input, batch_size=2 ** 12)
        print(user_embs.shape)
        print(item_embs.shape)
        return user_embs, item_embs

    # 计算召回率和命中率
    def eval(self, user_embs, item_embs, test_model_input, item_info, test_set):
        test_true_label = {line[0]: line[2] for line in test_set}
        index = faiss.IndexFlatIP(self.embedding_dim)
        index.add(item_embs)
        D, I = index.search(np.ascontiguousarray(user_embs), 50)
        s = []
        hit = 0

        # 统计预测结果
        for i, uid in tqdm(enumerate(test_model_input['user_id'])):
            try:
                pred = [item_info['item_id'].value[x] for x in I[i]]
                recall_score = recall_N(test_true_label[uid], pred, N=50)
                s.append(recall_score)
                if test_true_label[uid] in pred:
                    hit += 1
            except:
                print(i)

        # 计算召回率和命中率
        recall = np.mean(s)
        hit_rate = hit / len(test_model_input['user_id'])

        return recall, hit_rate

    def scheduler(self):
        # 构建训练集、测试集
        train_model_input, train_label, test_model_input, test_label, \
        train_set, test_set, user_info, item_info = self.training_set_construct()
        self.training_model(train_model_input, train_label)

        # 获取用户、item的layer
        user_embs, item_embs = self.extract_embedding_layer(model, test_model_input, item_info)
        # 评估模型
        recall, hit_rate = self.eval(user_embs, item_embs, test_model_input, item_info, test_set)
        print(recall, hit_rate)

if __name__ == '__main__':
    model = YouTubeModel()
    model.scheduler()
  • 代码解释:
    • training_set_construct:加载数据集,特征编码,数据集预处理,使用deepctr库中的SparseFeat(离散), VarLenSparseFeat(变长)实现用户物品的特征输入。
    • training_model:YoutubeDNN构建训练模型,compile编译训练模型,fit模型训练。
    • extract_embedding_layer:提取用户和物品的Embedding Layer。
    • eval:评估模型计算召回率和命中率,使用faiss中的faiss.IndexFlatIP(余弦距离搜索并非余弦相似度),统计预测结果,计算召回率为recall_score的平均值;命中率则是集中次数hit与test_model_input的总数。
    • scheduler:串联整个召回代码的函数,负责调用。

总结与问答

  1. 代码中提到的离散特征和变长特征该如何选择?
  • 答:首先我们要理解一下什么事离散特征,什么是变长特征?
    • 离散特征:是指具有有限取值或离散类别的特征,例如性别、国家、城市等(用户画像信息)。对于离散特征,可以使用embedding来将其映射到低维连续向量空间中。这使得模型能够学习离散特征之间的相关性和交互关系。通常情况下,离散特征需要经过编码(例如one-hot multi-hot)并与其他特征一起输入到模型中。
    • 变长特征:是指具有可变长度的特征,例如用户的历史行为序列或商品的标签列表。对于变长特征,可以使用循环神经网络(RNN)或Transformer等模型来建模。这些模型可以处理可变长度的序列,并捕捉序列中的时序关系和上下文信息。
    • 所以对于多特征输入,通常需要混合使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/417876.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序真机调试:连接局域网失败ws://********:8001/失败,已切换回广域网模式的解决方式

这个问题大多数是由于系统上安装了虚拟网卡造成,只要禁用虚拟网卡即可查询方式:windx - 选择设备管理器 - 查看网络适配器,找到虚拟网卡禁用 重新勾选局域网模式进行调试即可

Go 互斥锁的实现原理?

Go sync包提供了两种锁类型:互斥锁sync.Mutex 和 读写互斥锁sync.RWMutex,都属于悲观锁。 概念 Mutex是互斥锁,当一个 goroutine 获得了锁后,其他 goroutine 不能获取锁(只能存在一个写者或读者,不能同时…

Parallels Desktop安装虚拟机要执行此操作,您必须输入主机操作系统管理员认证凭据;执行该操作失败

弹窗1️⃣:执行此操作,您必须输入主机操作系统管理员认证凭据 桌面顶部点击《操作》点击《配置》 很多小伙伴在这一步又退回去重装了,其实不用,在配置里面设置就好了 弹窗2️⃣:执行该操作失败 设置如图&#xff1…

我写了个ImageWindow应用

文章目录 0 引言1 应用简介2 主要功能和特点2.1 多图像同/异步像素级对比2.2 支持多达30种图像格式2.3 高效率的图像处理性能 3 简明使用教程3.1 软件下载安装与更新3.1.1 软件下载与安装3.1.2 软件更新 3.2 多视窗添加并自动最优排列3.3 多样化图像导入方式3.4 自动切换显示模…

tinymce在vue3中的用法以及文本流式输出

一、版本 "tinymce/tinymce-vue": "4.0.5", "tinymce": "5.10.2", 二、步骤 具体步骤可以参考tinymce在vue2中的用法中的步骤 三、在项目index.html-body中引入tinymcejs <script src"tinymce/tinymce.min.js">&…

PyTorch概述(七)---Optim

torch.optim是一个实现多种优化算法的包;很多常用的方法已经被支持;接口丰富;容易整合更为复杂的算法; 如何使用一个优化器 为了使用torch.optim包功能;用户必须构建一个优化器对象;该优化器将保持当前的参数状态且基于计算的梯度更新参数; 构建优化器 要构建一个优化器;必…

【一个上下拉且松手回弹的自定义ScrollView】

文章目录 UserDefineScrollView举例使用activity_main.xmlMainActivity.java文件运行效果下拉前下拉后上拉 普通的scrollView下拉到顶部时就不动了&#xff0c;而如qq设置界面中的布局&#xff0c;下拉到顶端时还能下拉一段距离。本文介绍一个自定义scrollView就可以实现这样的…

遥感、航拍、影像等用于深度学习的数据集集合

遥感图像的纹理特征异常繁杂&#xff0c;地貌类型多变&#xff0c;人工提取往往存在特征提取困难和特征提取不准确的问题&#xff0c;同时&#xff0c;在这个过程中还会耗费海量的人力物力。随着计算力的突破、数据洪流的暴发和算法的不断创新&#xff0c;在具有鲜明“大数据”…

嵌入式中14 个超级牛的免费开源小工具

Homebrew for macOS 地址&#xff1a;https://brew.sh Mac 上非常好用的包管理工具&#xff0c;很多常见的安装都可以通过 brew install app 或者 brew cask install app 直接安装&#xff0c;类似 apt-get 。 Oh My Zsh 地址&#xff1a;https://github.com/robbyrussell…

Machine Vision Technology:Lecture2 Linear filtering

Machine Vision Technology&#xff1a;Lecture2 Linear filtering Types of ImagesImage denoising图像去噪Defining convolution卷积的定义Key properties卷积的关键属性卷积的其它属性Annoying details卷积练习Sharpening锐化Gaussian KernelNoise噪声 分类Gaussian noise高…

江科大stm32学习笔记——【5-2】对射式红外传感器计次旋转编码计次

一.对射式红外传感器计次 1.原理 2.硬件连接 3.程序 CountSensor.c: #include "stm32f10x.h" // Device header #include "Delay.h"uint16_t CountSensor_Count;void CountSensor_Init(void) {//配置RCC时钟&#xff1a;RCC_APB2Perip…

改进YOLO系列 | YOLOv5/v7 引入通用高效层聚合网络 GELAN | YOLOv9 新模块

今天的深度学习方法专注于如何设计最合适的目标函数,以使模型的预测结果最接近真实情况。同时,必须设计一个合适的架构,以便为预测提供足够的信息。现有方法忽视了一个事实,即当输入数据经过逐层特征提取和空间转换时,会丢失大量信息。本文将深入探讨数据通过深度网络传输…

DH秘钥交换算法

1 应用 关于加密&#xff0c;对称加密和非对称加密各有优劣&#xff0c;最佳方案是先使用非对称加密实现秘钥交换&#xff0c;后面再利用协商的结果作为对称加密的秘钥&#xff0c;具体可以参考 《嵌入式算法6---AES加密/解密算法》、《嵌入式算法18---RSA非对称加密算法》。 …

TikTok运营应该使用什么IP?网络问题大全

想要迈过TikTok新手门槛&#xff0c;首先必须要学习的就是网络问题。很多人开始做TikTok账号或者TikTok小店时&#xff0c;都会遇到一些先前没有遇到的词汇和概念&#xff0c;比如原生IP&#xff0c;独享IP&#xff0c;甚至专线&#xff0c;那么一个IP可以做几个账号呢&#xf…

多人同时导出 Excel 干崩服务器?我们来实现一个排队导出功能!

考虑到数据库数据日渐增多&#xff0c;导出会有全量数据的导出&#xff0c;多人同时导出可以会对服务性能造成影响&#xff0c;导出涉及到mysql查询的io操作&#xff0c;还涉及文件输入、输出流的io操作&#xff0c;所以对服务器的性能会影响的比较大&#xff1b; 结合以上原因…

CPD点云配准

一、CPD点云配准 Python 这是github上一位大佬写的Python包&#xff0c;链接&#xff1a;neka-nat/probreg: Python package for point cloud registration using probabilistic model (Coherent Point Drift, GMMReg, SVR, GMMTree, FilterReg, Bayesian CPD) (github.com)你…

深入理解与应用工厂方法模式

文章目录 一、模式概述**二、适用场景****三、模式原理与实现****四、采用工厂方法模式的原因****五、优缺点分析****六、与抽象工厂模式的比较**总结 一、模式概述 ​ 工厂方法模式是一种经典的设计模式&#xff0c;它遵循面向对象的设计原则&#xff0c;特别是“开闭原则”&…

EasyX的使用(详解版)

EasyX的基础概念&#xff1a; 图形化——EasyX的安装-CSDN博客 创建图形化窗口 #include<graphics.h> #include<conio.h> int main() {//创建绘图窗口&#xff0c;大小为100x100像素。//更改为大窗口&#xff0c;像素增大&#xff1b;更改为小窗口&#xff0c;像素…

华为数通方向HCIP-DataCom H12-821题库(单选题:481-500)

第481题 以下关于基于SD-WAN思想的EVPN互联方案的描述,错误的是哪一项? A、通过部署独立的控制面,将网络转发和控制进行了分离,从而实现了网络控制的集中化 B、通过对WAN网络抽象和建模,将上层网络业务和底层网络具体实现架构进行解耦,从而实现网络自动化 C、通过集中的…

上位机图像处理和嵌入式模块部署(当前机器视觉新形态)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 过去的机器视觉处理&#xff0c;大部分都是集中在上位机、或者是服务器领域&#xff0c;这种形式维持了很长的时间。这种业务形态下&#xff0c;无…