深度学习推荐系统(五)DeepCrossing模型及其在Criteo数据集上的应用

news2025/1/18 6:23:11

深度学习推荐系统(五)Deep&Crossing模型及其在Criteo数据集上的应用

在2016年, 随着微软的Deep Crossing, 谷歌的Wide&Deep以及FNN、PNN等一大批优秀的深度学习模型被提出, 推荐系统全面进入了深度学习时代, 时至今日, 依然是主流。 推荐模型主要有下面两个进展:

  • 与传统的机器学习模型相比, 深度学习模型的表达能力更强, 能够挖掘更多数据中隐藏的模式

  • 深度学习模型结构非常灵活, 能够根据业务场景和数据特点, 灵活调整模型结构, 使模型与应用场景完美契合

深度学习推荐模型,以多层感知机(MLP)为核心, 通过改变神经网络结构进行演化。

在这里插入图片描述

1 Deep&Crossing模型原理

1.1 Deep&Crossing模型提出的背景

  • Wide&Deep 模型的提出不仅综合了记忆能力泛化能力,而且开启了不同网络结构融合的新思路。

  • 在 Wide&Deep 模型之后,有越来越多的工作集中于分别改进Wide&Deep模型的 Wide部分或是 Deep 部分。

  • 典型的工作是2017年由斯坦福大学和谷歌的研究人员提出的 Deep&Cross模型(简称DCN)。

  • Deep&Cross 模型的主要思路是使用 Cross 网络替代原来的 Wide 部分。由于 Deep 部分的设计思路并没有本质的改变,最主要的创新点是Cross 部分的设计思路。

1.2 Deep&Crossing的模型结构

DCN模型的结构非常简洁,从下往上依次为:Embedding和Stacking层、Cross网络层与Deep网络层并列、输出合并层,得到最终的预测结果

在这里插入图片描述

1.2.1 Embedding and stacking layer

Embedding层作用依然是把稀疏离散的类别型特征变成低维密集型。

然后需要将所有的密集型特征(数值型特征)与通过embedding转换后的特征进行联合(Stacking)。

在这里插入图片描述

1.2.2 Cross NetWork模型

在这里插入图片描述

在这里插入图片描述

举例说明

在这里插入图片描述

可以看到

  • x1中包含了所有的x0的1,2阶特征的交互。第l层特征对应的最高的叉乘阶数为l+1

  • Cross网络的参数是共享的, 每一层的这个权重特征之间共享, 这个可以使得模型泛化到看不见的特征交互作用, 并且对噪声更具有鲁棒性。

  • Deep Network及组合层比较简单,不再赘述。

1.3 Deep&Crossing模型代码复现

import torch.nn as nn
import torch.nn.functional as F
import torch


class CrossNetwork(nn.Module):
    """
    Cross Network
    """
    def __init__(self, layer_num, input_dim):
        super(CrossNetwork, self).__init__()
        self.layer_num = layer_num

        # 定义网络层的参数
        self.cross_weights = nn.ParameterList([
            nn.Parameter(torch.rand(input_dim, 1))
            for i in range(self.layer_num)
        ])
        self.cross_bias = nn.ParameterList([
            nn.Parameter(torch.rand(input_dim, 1))
            for i in range(self.layer_num)
        ])

    def forward(self, x):
        # x是(batchsize, dim)的形状, 先扩展一个维度到(batchsize, dim, 1)
        x_0 = torch.unsqueeze(x, dim=2)
        x = x_0.clone()
        xT = x_0.clone().permute((0, 2, 1))     # (batchsize, 1, dim)
        for i in range(self.layer_num):
            x = torch.matmul(torch.bmm(x_0, xT), self.cross_weights[i]) + self.cross_bias[i] + x   # (batchsize, dim, 1)
            xT = x.clone().permute((0, 2, 1))   # (batchsize, 1, dim)

        x = x.squeeze(2)  # (batchsize, dim)
        return x


class Dnn(nn.Module):
    """
    Dnn part
    """

    def __init__(self, hidden_units, dropout=0.):
        """
        hidden_units: 列表, 每个元素表示每一层的神经单元个数, 比如[256, 128, 64], 两层网络, 第一层神经单元128, 第二层64, 第一个维度是输入维度
        dropout: 失活率
        """
        super(Dnn, self).__init__()

        self.dnn_network = nn.ModuleList(
            [nn.Linear(layer[0], layer[1]) for layer in list(zip(hidden_units[:-1], hidden_units[1:]))])
        self.dropout = nn.Dropout(p=dropout)

    def forward(self, x):
        for linear in self.dnn_network:
            x = linear(x)
            x = F.relu(x)

        x = self.dropout(x)
        return x


class DCN(nn.Module):
    def __init__(self, feature_info, hidden_units, layer_num, embed_dim=8,dnn_dropout=0.):
        """
                    feature_info: 特征信息(数值特征, 类别特征, 类别特征embedding映射)
                    hidden_units: 列表, 隐藏单元的个数(多层残差那里的)
                    layer_num: cross network的层数
                    embed_dim: embedding维度
                    dnn_dropout: Dropout层的失活比例
        """
        super(DCN, self).__init__()
        self.dense_features, self.sparse_features, self.sparse_features_map = feature_info

        # embedding层, 这里需要一个列表的形式, 因为每个类别特征都需要embedding
        self.embed_layers = nn.ModuleDict(
            {
                'embed_' + str(key): nn.Embedding(num_embeddings=val, embedding_dim=embed_dim)
                for key, val in self.sparse_features_map.items()
            }
        )

        # 统计embedding_dim的总维度
        # 一个离散型(类别型)变量 通过embedding层变为10纬
        embed_dim_sum = sum([embed_dim] * len(self.sparse_features))
        # 总维度 = 数值型特征的纬度 + 离散型变量经过embedding后的纬度
        dim_sum = len(self.dense_features) + embed_dim_sum
        hidden_units.insert(0, dim_sum)

        # 1、cross Network
        # layer_num是交叉网络的层数, hidden_units[0]表示输入的整体维度大小
        self.cross_network = CrossNetwork(layer_num, hidden_units[0])

        # 2、Deep Network
        self.dnn_network = Dnn(hidden_units,dnn_dropout)

        # 最后一层线性层,输入纬度是(cross Network输出纬度 + Deep Network输出纬度)
        self.final_linear = nn.Linear(hidden_units[-1] + hidden_units[0], 1)

    def forward(self, x):
        # 1、先把输入向量x分成两部分处理、因为数值型和类别型的处理方式不一样
        dense_input, sparse_inputs = x[:, :len(self.dense_features)], x[:, len(self.dense_features):]

        # 2、转换为long形
        sparse_inputs = sparse_inputs.long()

        # 2、不同的类别特征分别embedding
        sparse_embeds = [
            self.embed_layers['embed_' + key](sparse_inputs[:, i]) for key, i in
            zip(self.sparse_features_map.keys(), range(sparse_inputs.shape[1]))
        ]
        # 3、把类别型特征进行拼接,即emdedding后,由3行转换为1行
        sparse_embeds = torch.cat(sparse_embeds, axis=-1)

        # 4、数值型和类别型特征进行拼接
        x = torch.cat([sparse_embeds, dense_input], axis=-1)

        # cross Network
        cross_out = self.cross_network(x)

        # Deep Network
        deep_out = self.dnn_network(x)

        #  Concatenate
        total_x = torch.cat([cross_out, deep_out], axis=-1)

        # out
        outputs = F.sigmoid(self.final_linear(total_x))

        return outputs


if __name__ == '__main__':
    x = torch.rand(size=(1, 5), dtype=torch.float32)
    feature_info = [
        ['I1', 'I2'],  # 连续性特征
        ['C1', 'C2', 'C3'],  # 离散型特征
        {
            'C1': 20,
            'C2': 20,
            'C3': 20
        }
    ]

    # 建立模型
    hidden_units = [128, 64, 32]

    net = DCN(feature_info, hidden_units,layer_num=2)
    print(net)
    print(net(x))
DCN(
  (embed_layers): ModuleDict(
    (embed_C1): Embedding(20, 8)
    (embed_C2): Embedding(20, 8)
    (embed_C3): Embedding(20, 8)
  )
  (cross_network): CrossNetwork(
    (cross_weights): ParameterList(
        (0): Parameter containing: [torch.FloatTensor of size 26x1]
        (1): Parameter containing: [torch.FloatTensor of size 26x1]
    )
    (cross_bias): ParameterList(
        (0): Parameter containing: [torch.FloatTensor of size 26x1]
        (1): Parameter containing: [torch.FloatTensor of size 26x1]
    )
  )
  (dnn_network): Dnn(
    (dnn_network): ModuleList(
      (0): Linear(in_features=26, out_features=128, bias=True)
      (1): Linear(in_features=128, out_features=64, bias=True)
      (2): Linear(in_features=64, out_features=32, bias=True)
    )
    (dropout): Dropout(p=0.0, inplace=False)
  )
  (final_linear): Linear(in_features=58, out_features=1, bias=True)
)
tensor([[0.9349]], grad_fn=<SigmoidBackward0>)

2 Deep&Crossing模型在Criteo数据集的应用

数据的预处理可以参考

深度学习推荐系统(二)Deep Crossing及其在Criteo数据集上的应用_undo_try的博客-CSDN博客

2.1 准备训练数据

import pandas as pd

import torch
from torch.utils.data import TensorDataset, Dataset, DataLoader

import torch.nn as nn
from sklearn.metrics import auc, roc_auc_score, roc_curve

import warnings
warnings.filterwarnings('ignore')
# 封装为函数
def prepared_data(file_path):
    # 读入训练集,验证集和测试集
    train_set = pd.read_csv(file_path + 'train_set.csv')
    val_set = pd.read_csv(file_path + 'val_set.csv')
    test_set = pd.read_csv(file_path + 'test.csv')

    # 这里需要把特征分成数值型和离散型
    # 因为后面的模型里面离散型的特征需要embedding, 而数值型的特征直接进入了stacking层, 处理方式会不一样
    data_df = pd.concat((train_set, val_set, test_set))

    # 数值型特征直接放入stacking层
    dense_features = ['I' + str(i) for i in range(1, 14)]
    # 离散型特征需要需要进行embedding处理
    sparse_features = ['C' + str(i) for i in range(1, 27)]

    # 定义一个稀疏特征的embedding映射, 字典{key: value},
    # key表示每个稀疏特征, value表示数据集data_df对应列的不同取值个数, 作为embedding输入维度
    sparse_feas_map = {}
    for key in sparse_features:
        sparse_feas_map[key] = data_df[key].nunique()


    feature_info = [dense_features, sparse_features, sparse_feas_map]  # 这里把特征信息进行封装, 建立模型的时候作为参数传入

    # 把数据构建成数据管道
    dl_train_dataset = TensorDataset(
        # 特征信息
        torch.tensor(train_set.drop(columns='Label').values).float(),
        # 标签信息
        torch.tensor(train_set['Label'].values).float()
    )

    dl_val_dataset = TensorDataset(
        # 特征信息
        torch.tensor(val_set.drop(columns='Label').values).float(),
        # 标签信息
        torch.tensor(val_set['Label'].values).float()
    )
    dl_train = DataLoader(dl_train_dataset, shuffle=True, batch_size=16)
    dl_vaild = DataLoader(dl_val_dataset, shuffle=True, batch_size=16)
    return feature_info,dl_train,dl_vaild,test_set
file_path = './preprocessed_data/'

feature_info,dl_train,dl_vaild,test_set = prepared_data(file_path)

2.2 建立Deep&Crossing模型

from _01_DeepAndCrossing import DCN

# 建立模型
hidden_units = [128, 64, 32]


net = DCN(feature_info, hidden_units,layer_num=len(hidden_units))
# 测试一下模型
for feature, label in iter(dl_train):
    out = net(feature)
    print(feature.shape)
    print(out.shape)
    print(out)
    break

2.3 模型的训练

from AnimatorClass import Animator
from TimerClass import Timer


# 模型的相关设置
def metric_func(y_pred, y_true):
    pred = y_pred.data
    y = y_true.data
    return roc_auc_score(y, pred)


def try_gpu(i=0):
    if torch.cuda.device_count() >= i + 1:
        return torch.device(f'cuda:{i}')
    return torch.device('cpu')


def train_ch(net, dl_train, dl_vaild, num_epochs, lr, device):
    """⽤GPU训练模型"""
    print('training on', device)
    net.to(device)
    # 二值交叉熵损失
    loss_func = nn.BCELoss()
    optimizer = torch.optim.Adam(params=net.parameters(), lr=lr)

    animator = Animator(xlabel='epoch', xlim=[1, num_epochs],legend=['train loss', 'train auc', 'val loss', 'val auc']
                        ,figsize=(8.0, 6.0))
    timer, num_batches = Timer(), len(dl_train)
    log_step_freq = 10

    for epoch in range(1, num_epochs + 1):
        # 训练阶段
        net.train()
        loss_sum = 0.0
        metric_sum = 0.0

        for step, (features, labels) in enumerate(dl_train, 1):
            timer.start()
            # 梯度清零
            optimizer.zero_grad()

            # 正向传播
            predictions = net(features)
            loss = loss_func(predictions, labels.unsqueeze(1) )
            try:          # 这里就是如果当前批次里面的y只有一个类别, 跳过去
                metric = metric_func(predictions, labels)
            except ValueError:
                pass

            # 反向传播求梯度
            loss.backward()
            optimizer.step()
            timer.stop()

            # 打印batch级别日志
            loss_sum += loss.item()
            metric_sum += metric.item()

            if step % log_step_freq == 0:
                animator.add(epoch + step / num_batches,(loss_sum/step, metric_sum/step, None, None))

        # 验证阶段
        net.eval()
        val_loss_sum = 0.0
        val_metric_sum = 0.0


        for val_step, (features, labels) in enumerate(dl_vaild, 1):
            with torch.no_grad():
                predictions = net(features)
                val_loss = loss_func(predictions, labels.unsqueeze(1))
                try:
                    val_metric = metric_func(predictions, labels)
                except ValueError:
                    pass

            val_loss_sum += val_loss.item()
            val_metric_sum += val_metric.item()

            if val_step % log_step_freq == 0:
                animator.add(epoch + val_step / num_batches, (None,None,val_loss_sum / val_step , val_metric_sum / val_step))

        print(f'final: loss {loss_sum/len(dl_train):.3f}, auc {metric_sum/len(dl_train):.3f},'
              f' val loss {val_loss_sum/len(dl_vaild):.3f}, val auc {val_metric_sum/len(dl_vaild):.3f}')
        print(f'{num_batches * num_epochs / timer.sum():.1f} examples/sec on {str(device)}')
lr, num_epochs = 0.001, 10
train_ch(net, dl_train, dl_vaild, num_epochs, lr, try_gpu())

在这里插入图片描述

2.4 模型的预测

y_pred_probs = net(torch.tensor(test_set.values).float())
y_pred = torch.where(
    y_pred_probs>0.5,
    torch.ones_like(y_pred_probs),
    torch.zeros_like(y_pred_probs)
)
y_pred.data[:10]

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python实现字符串模糊匹配

在一个字符串中&#xff0c;有时需对其中某些内容进行模糊匹配以实现条件的判定&#xff0c;如在“你好&#xff0c;hello,world”中判断是否含有“llo”。Python中通过re.search()方法实现&#xff0c;特别地&#xff0c;对于首位起始的内容匹配&#xff0c;也可通过re.match(…

Apolo学习

安装&#xff08;java1.8 mysql 5.6.5以上&#xff09; 下载quickStart的包&#xff0c;早apollo下执行两个sql。如果不执行这两个sql&#xff0c;apollo是执行不起来的。会有两个表来记录apollo的执行情况。其中一个表叫apolloportaldb 在apollo目录下会有执行的包。.sh是…

stm32f1xx单片机拦截中断源代码

这个是实现后的效果&#xff0c;可以看到已经没有中断的效果了 这个是拦截前的效果可以看到电平是在变化的 实现原理非常简单&#xff1a;一句话搞定&#xff1a; if(TIM2->CNTTIM2->ARR-5)TIM2->CNT-5; 以下是完整的代码&#xff1a;是用来补充说明和筹字数的 /* …

山西电力市场日前价格预测【2023-09-04】

日前价格预测 预测明日&#xff08;2023-09-04&#xff09;山西电力市场全天平均日前电价为305.57元/MWh。其中&#xff0c;最高日前电价为349.83元/MWh&#xff0c;预计出现在19: 15。最低日前电价为226.28元/MWh&#xff0c;预计出现在13: 30。 价差方向预测 1&#xff1a; 实…

Linux CentOS7 系统中添加用户

在linux centOS7系统中&#xff0c;添加用户是管理员的基本操作。作为学习linux系统的基本操作&#xff0c;对添加用户应该多方面了解。 添加用户的命令useradd&#xff0c;跟上用户名&#xff0c;就可以快速创建一个用户。添加一些选项&#xff0c;可以设置更人性化的用户信息…

并发下的Map常见面试题

HashMap 和 HashTable 有什么区别&#xff1f;java中的另一个线程安全的与HashMap极其类似的类是什么&#xff1f;同样是线程安全&#xff0c;它与HashTable在线程同步上有什么不同&#xff1f;HashMap 与 ConcurrentHashMap的区别&#xff1f;为什么 ConcurrentHashMap 比 Has…

【Python技巧】快速安装各种常用库pip、whl、tar.gz最新最全安装方法(超时、快速安装))

【Python技巧】安装各种常用库pip、whl、tar.gz最新最全安装方法 &#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公粽号&…

Linux系统调试中出现核心转储(core dump)的问题

​ 大家好&#xff0c;我是ST。今天主要分享一下&#xff0c;Linux应用程序发生Segmentation fault段错误时&#xff0c;如何利用core dump文件定位错误。 核心转储 在 Linux 系统中&#xff0c;常将“主内存”称为核心(core)&#xff0c;而核心映像(core image) 就是 “进…

Stable Diffuse 之 本地环境部署/安装包下载搭建过程简单记录

Stable Diffuse 之 本地环境部署/安装包下载搭建过程简单记录 目录 Stable Diffuse 之 本地环境部署/安装包下载搭建过程简单记录 一、简单介绍 二、注意事项 三、环境搭建 git 下载和安装 python 下载和安装 stable-diffusion-webui 下载和安装 测试 stable diffuse w…

【数据分享】2006-2021年我国省份级别的燃气相关指标(免费获取\20多项指标)

《中国城市建设统计年鉴》中细致地统计了我国城市市政公用设施建设与发展情况&#xff0c;在之前的文章中&#xff0c;我们分享过基于2006-2021年《中国城市建设统计年鉴》整理的2006—2021年我国省份级别的市政设施水平相关指标、2006-2021年我国省份级别的各类建设用地面积数…

使用栈检查括号的合法性 C 实现

使用栈检查括号的合法性 思路讲解&#xff1a;首先从数组数组0下标开始&#xff0c;如果是左括号直接无脑压入栈&#xff0c;直到出现右括号开始判断合法与否。遇到右括号分两种情况&#xff0c;第一种是空栈的情况&#xff0c;也就是说我们第一个字符就是右括号&#xff0c;那…

WPF CommunityToolkit.Mvvm Messenger通讯

文章目录 环境WeakReferenceMessenger方法介绍无回调订阅发送Token区分有回调订阅发送 环境 CommunityToolkit.Mvvm Messenger 十月的寒流: 如何使用 CommunityToolkit.Mvvm 中的 Messenger 来进行 ViewModel 之间的通信 WeakReferenceMessenger 我这里只讲简单的弱Messenger…

Unity 之 Start 与 Awake 的区别

文章目录 在Unity中&#xff0c;Awake和Start都是用于脚本中的生命周期方法&#xff0c;用于控制游戏对象在不同阶段的初始化和行为。它们之间的区别在于调用的时间和用途。 Awake: Awake 是一个在游戏对象被实例化时首先调用的方法。它在对象被加载到场景中但在启用之前调用。…

找到自制电子杂志的方法了,快来看看?

终于找到自制电子杂志的方法了&#xff0c;这真是令人兴奋啊&#xff01;现在&#xff0c;我们可以利用这个方法来创造属于自己的电子杂志&#xff0c;将我们的想法和创意以独特的方式展现给世界。 1.需要一个电子杂志制作工具 市面上有许多专门用于制作电子杂志的工具&#x…

消灭怪物的最大数量【力扣1921】

一、题目分析 需要满足的条件&#xff1a; 只能在每分钟的开始使用武器武器能杀死距离城市最近的怪兽怪兽到达城市就会输掉游戏 游戏最优策略&#xff1a;我们可以在每分钟的开始都使用一次武器&#xff0c;用来杀死距离城市最近的怪兽。这样可以在力所能及的范围内&#xf…

文件及Java中File类详解

目录 一, 文件 1,什么是文件 2,路径 二, File类 1,File类的构造方法 签名 说明 2,File类的方法 修饰符及返回值类型 方法签名 说明 2.1,get方法的使用 2.2,普通文件的创建以及删除 2.3,普通目录文件的创建以及删除 3,代码示例 示例1 示例2 一, 文件 1,什么是文…

聊聊每日站会

这是鼎叔的第七十四篇原创文章。行业大牛和刚毕业的小白&#xff0c;都可以进来聊聊。 欢迎关注本专栏和微信公众号《敏捷测试转型》&#xff0c;星标收藏&#xff0c;大量原创思考文章陆续推出。 每日站会是一线敏捷团队自己的会议&#xff0c;快速同步成员为达成迭代目标所…

对象模型和this指针(个人学习笔记黑马学习)

1、成员变量和成员函数 #include <iostream> using namespace std; #include <string>//成员变量和成员函数分开存储class Person {int m_A;//非静态成员变量 属于类的对象上的static int m_B;//静态成员变量 不属于类的对象上void func() {} //非静态成员函数 不…

必读干货!独立站新手卖家常见十大问题解答

新手卖家入局跨境电商独立站&#xff0c;会考虑到很多问题&#xff0c;从网站到产品再到售卖最终的发货。而且卖家普遍对独立站存在认知不全的问题&#xff0c;门槛高、推广难、转化难是最初印象。 那独立站该怎么开始&#xff0c;如何下手&#xff1f;今天整理并解答独立站新…

【大模型AIGC系列课程 3-3】低成本的领域私域大模型训练方法

重磅推荐专栏: 《大模型AIGC》 本专栏致力于探索和讨论当今最前沿的技术趋势和应用领域,包括但不限于ChatGPT和Stable Diffusion等。我们将深入研究大型模型的开发和应用,以及与之相关的人工智能生成内容(AIGC)技术。通过深入的技术解析和实践经验分享,旨在帮助读者更好地…