Wide&Deep——记忆能力和泛化能力的综合

  • 论文地址
  • 基本原理
  • 网络结构图
  • 代码实现
  • 总结分析
  • 参考文献

论文地址

Wide & Deep Learning for Recommender Systems


基本原理

利用Wide部分加强模型的“记忆能力”,利用Deep部分加强模型的“泛化能力”

  • 优点:开创了组合模型的构造方法,对深度学习推荐系统后续发展产生巨大影响
  • 缺点:Wide部分需要人工进行特征组合的筛选

网络结构图


代码实现

import warningswarnings.filterwarnings("ignore")
import itertools
import pandas as pd
import numpy as np
from tqdm import tqdm
from collections import namedtupleimport tensorflow as tf
from tensorflow.keras.layers import *
from tensorflow.keras.models import *from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler, LabelEncoderfrom utils import SparseFeat, DenseFeat, VarLenSparseFeat
from tensorflow.keras.utils import plot_model# 简单处理特征,包括填充缺失值,数值处理,类别编码
def data_process(data_df, dense_features, sparse_features):data_df[dense_features] = data_df[dense_features].fillna(0.0)for f in dense_features:data_df[f] = data_df[f].apply(lambda x: np.log(x + 1) if x > -1 else -1)data_df[sparse_features] = data_df[sparse_features].fillna("-1")for f in sparse_features:lbe = LabelEncoder()data_df[f] = lbe.fit_transform(data_df[f])return data_df[dense_features + sparse_features]def build_input_layers(feature_columns):# 构建Input层字典,并以dense和sparse两类字典的形式返回dense_input_dict, sparse_input_dict = {}, {}for fc in feature_columns:if isinstance(fc, SparseFeat):sparse_input_dict[fc.name] = Input(shape=(1,), name=fc.name)elif isinstance(fc, DenseFeat):dense_input_dict[fc.name] = Input(shape=(fc.dimension,), name=fc.name)return dense_input_dict, sparse_input_dictdef build_embedding_layers(feature_columns, input_layers_dict, is_linear):# 定义一个embedding层对应的字典embedding_layers_dict = dict()# 将特征中的sparse特征筛选出来sparse_feature_columns = list(filter(lambda x: isinstance(x, SparseFeat), feature_columns)) if feature_columns else []# 如果是用于线性部分的embedding层,其维度为1,否则维度就是自己定义的embedding维度if is_linear:for fc in sparse_feature_columns:embedding_layers_dict[fc.name] = Embedding(fc.vocabulary_size, 1, name='1d_emb_' + fc.name)else:for fc in sparse_feature_columns:embedding_layers_dict[fc.name] = Embedding(fc.vocabulary_size, fc.embedding_dim, name='kd_emb_' + fc.name)return embedding_layers_dictdef get_linear_logits(dense_input_dict, sparse_input_dict, sparse_feature_columns):# 将所有的dense特征的Input层,然后经过一个全连接层得到dense特征的logitsconcat_dense_inputs = Concatenate(axis=1)(list(dense_input_dict.values()))dense_logits_output = Dense(1)(concat_dense_inputs)# 获取linear部分sparse特征的embedding层,这里使用embedding的原因是:# 对于linear部分直接将特征进行onehot然后通过一个全连接层,当维度特别大的时候,计算比较慢# 使用embedding层的好处就是可以通过查表的方式获取到哪些非零的元素对应的权重,然后在将这些权重相加,效率比较高linear_embedding_layers = build_embedding_layers(sparse_feature_columns, sparse_input_dict, is_linear=True)# 将一维的embedding拼接,注意这里需要使用一个Flatten层,使维度对应sparse_1d_embed = []for fc in sparse_feature_columns:feat_input = sparse_input_dict[fc.name]embed = Flatten()(linear_embedding_layers[fc.name](feat_input))  # B x 1sparse_1d_embed.append(embed)# embedding中查询得到的权重就是对应onehot向量中一个位置的权重,所以后面不用再接一个全连接了,本身一维的embedding就相当于全连接# 只不过是这里的输入特征只有0和1,所以直接向非零元素对应的权重相加就等同于进行了全连接操作(非零元素部分乘的是1)sparse_logits_output = Add()(sparse_1d_embed)# 最终将dense特征和sparse特征对应的logits相加,得到最终linear的logitslinear_logits = Add()([dense_logits_output, sparse_logits_output])return linear_logits# 将所有的sparse特征embedding拼接
def concat_embedding_list(feature_columns, input_layer_dict, embedding_layer_dict, flatten=False):# 将sparse特征筛选出来sparse_feature_columns = list(filter(lambda x: isinstance(x, SparseFeat), feature_columns))embedding_list = []for fc in sparse_feature_columns:_input = input_layer_dict[fc.name]  # 获取输入层_embed = embedding_layer_dict[fc.name]  # B x 1 x dim  获取对应的embedding层embed = _embed(_input)  # B x dim  将input层输入到embedding层中# 是否需要flatten, 如果embedding列表最终是直接输入到Dense层中,需要进行Flatten,否则不需要if flatten:embed = Flatten()(embed)embedding_list.append(embed)return embedding_listdef get_dnn_logits(dense_input_dict, sparse_input_dict, sparse_feature_columns, dnn_embedding_layers):concat_dense_inputs = Concatenate(axis=1)(list(dense_input_dict.values()))  # B x n1 (n表示的是dense特征的维度)sparse_kd_embed = concat_embedding_list(sparse_feature_columns, sparse_input_dict, dnn_embedding_layers,flatten=True)concat_sparse_kd_embed = Concatenate(axis=1)(sparse_kd_embed)  # B x n2k  (n2表示的是Sparse特征的维度)dnn_input = Concatenate(axis=1)([concat_dense_inputs, concat_sparse_kd_embed])  # B x (n2k + n1)# dnn层,这里的Dropout参数,Dense中的参数及Dense的层数都可以自己设定dnn_out = Dropout(0.5)(Dense(1024, activation='relu')(dnn_input))dnn_out = Dropout(0.3)(Dense(512, activation='relu')(dnn_out))dnn_out = Dropout(0.1)(Dense(256, activation='relu')(dnn_out))dnn_logits = Dense(1)(dnn_out)return dnn_logits# Wide&Deep 模型的wide部分及Deep部分的特征选择,应该根据实际的业务场景去确定哪些特征应该放在Wide部分,哪些特征应该放在Deep部分
def WideNDeep(linear_feature_columns, dnn_feature_columns):# 构建输入层,即所有特征对应的Input()层,这里使用字典的形式返回,方便后续构建模型dense_input_dict, sparse_input_dict = build_input_layers(linear_feature_columns + dnn_feature_columns)# 将linear部分的特征中sparse特征筛选出来,后面用来做1维的embeddinglinear_sparse_feature_columns = list(filter(lambda x: isinstance(x, SparseFeat), linear_feature_columns))# 构建模型的输入层,模型的输入层不能是字典的形式,应该将字典的形式转换成列表的形式# 注意:这里实际的输入与Input()层的对应,是通过模型输入时候的字典数据的key与对应name的Input层input_layers = list(dense_input_dict.values()) + list(sparse_input_dict.values())# Wide&Deep模型论文中Wide部分使用的特征比较简单,并且得到的特征非常的稀疏,所以使用了FTRL优化Wide部分(这里没有实现FTRL)# 但是是根据他们业务进行选择的,我们这里将所有可能用到的特征都输入到Wide部分,具体的细节可以根据需求进行修改linear_logits = get_linear_logits(dense_input_dict, sparse_input_dict, linear_sparse_feature_columns)# 构建维度为k的embedding层,这里使用字典的形式返回,方便后面搭建模型embedding_layers = build_embedding_layers(dnn_feature_columns, sparse_input_dict, is_linear=False)dnn_sparse_feature_columns = list(filter(lambda x: isinstance(x, SparseFeat), dnn_feature_columns))# 在Wide&Deep模型中,deep部分的输入是将dense特征和embedding特征拼在一起输入到dnn中dnn_logits = get_dnn_logits(dense_input_dict, sparse_input_dict, dnn_sparse_feature_columns, embedding_layers)# 将linear,dnn的logits相加作为最终的logitsoutput_logits = Add()([linear_logits, dnn_logits])# 这里的激活函数使用sigmoidoutput_layer = Activation("sigmoid")(output_logits)model = Model(input_layers, output_layer)return modelif __name__ == "__main__":# 读取数据data = pd.read_csv('../data/criteo_sample.txt')# 划分dense和sparse特征columns = data.columns.valuesdense_features = [feat for feat in columns if 'I' in feat]sparse_features = [feat for feat in columns if 'C' in feat]# 简单的数据预处理train_data = data_process(data, dense_features, sparse_features)train_data['label'] = data['label']# 将特征分组,分成linear部分和dnn部分(根据实际场景进行选择),并将分组之后的特征做标记(使用DenseFeat, SparseFeat)linear_feature_columns = [SparseFeat(feat, vocabulary_size=data[feat].nunique(), embedding_dim=4)for i, feat in enumerate(sparse_features)] + [DenseFeat(feat, 1, )for feat in dense_features]dnn_feature_columns = [SparseFeat(feat, vocabulary_size=data[feat].nunique(), embedding_dim=4)for i, feat in enumerate(sparse_features)] + [DenseFeat(feat, 1, )for feat in dense_features]# 构建WideNDeep模型history = WideNDeep(linear_feature_columns, dnn_feature_columns)history.summary()plot_model(history, to_file='model.png')history.compile(optimizer="adam",loss="binary_crossentropy",metrics=["binary_crossentropy", tf.keras.metrics.AUC(name='auc')])# 将输入数据转化成字典的形式输入train_model_input = {name: data[name] for name in dense_features + sparse_features}# 模型训练history.fit(train_model_input, train_data['label'].values,batch_size=64, epochs=5, validation_split=0.2, )

总结分析

  • 特征处理

针对类别型特征进行Embedding后输出作为后续层次的输入,连续型特征直接输入;

  • Wide部分

连续型特征进行Concat后经过Linear层输出1维的特征向量,类别型特征分别进行Linear的Embedding得到1维的输出,再将其Add得到1维的特征向量,再与连续型特征输出做Add操作,得到Wide层输出;

  • Deep部分

连续型特征进行Concat得到P维的特征向量,类别型特征分别进行Embedding后进行Concat得到N*K维的特征向量(其中N是类别特征数,K是Embedding层的维度),再将二者进行Concat得到Deep层输入,经过若干个“全连接层+Dropout”联合层,最后一层的Linear输出1维的推理结果;

  • 输出部分

将Wide部分的输出和Deep部分的输出进行Add,得到输出向量,经过激活函数得到预测结果,这里的激活函数是Sigmoid;


参考文献

很多…


WideDeep——记忆能力和泛化能力的综合相关推荐

  1. 深度学习WideDeep模型——记忆能力和泛化能力的综合

    文章目录 Wide&Deep前言 模型的记忆能力与泛化能力 Wide&Deep模型的结构 Wide&Deep前言 Wide&Deep模型的主要思路正如其名,是由单层的W ...

  2. 全连接神经网络的表达能力与泛化能力

    在深度学习中,我们经常看到两个概念:表达能力和泛化能力 表达能力指的是模型拟合训练集的能力,可以用训练损失来衡量 而泛化集指的是模型迁移到测试集中的能力,可以用测试误差来衡量 一般来说,全连接的神经网 ...

  3. 思考深度学习的泛化能力

      深度神经网络往往带有大量的参数,但依然表现出很强的泛化能力(指训练好的模型在未见过的数据上的表现).深度神经网络为何会拥有如此强的泛化能力?2016年.2017年的两篇论文引起了广泛思考. 神经网 ...

  4. 【啃书吧:深度学习与MindSpore实践】第三章 3.3泛化能力

    初读:2021年1月4日 啃书进度会在目录中标出来.本次目标是完成第三章 3.3节泛化能力(P30-P31) 这一节内容非常简短,说明也非常浅显,但实际上点名的方法有好几个,特别是提高泛化能力的具体方 ...

  5. 机器学习--泛化能力

    一. 什么是泛化能力? 泛化能力就是模型对未知数据的预测能力.在实际当中,我们通常通过测试误差来评价学习方法的泛化能力. 二. 泛化误差的定义           大家很快就能发现,这不是损失函数的期 ...

  6. 机器学习中的泛化能力

    模型的泛化能力:指机器学习算法对新鲜样本的适应能力. 学习的目的:学到隐含在数据背后的规律,对具有同一规律的学习集以外的数据,经过训练的网络也能给出合适的输出,该能力称为泛化能力. 由此可见,经训练样 ...

  7. 漫谈机器学习的【泛化能力】【模型能力】【调参技巧】_CodingPark编程公园

    漫谈机器学习 完成第一幅初稿 完成终稿 1945年12月5日 1946年1月17日 前言 上面的画就是20世纪最著名的抽象派画家毕加索于1946年1月17日完稿的画作--公牛,寥寥几笔,勾勒出公牛的& ...

  8. 1.6 泛化能力,生成模型,判别模型

    文章目录 一.泛化误差 二.泛化误差上界 三.生成模型与判别模型 1.基本概念 2.特点 一.泛化误差 泛化能力:泛化能力是指由学习方法学习到的模型对未知数据的预测能力,是学习方法本质上重要的性质. ...

  9. 泛化能力 归一化处理

    泛化能力(generalization ability)是指机器学习算法对新鲜样本的适应能力. 学习的目的是学到隐含在数据背后的规律,对具有同一规律的学习集以外的数据,经过训练的网络也能给出合适的输出 ...

最新文章

  1. 360浏览器调用selenium
  2. 如何更快地将string转换成int/long
  3. 再有人问你synchronized是什么,就把这篇文章发给他。
  4. boost::mp11::mp_map_find相关用法的测试程序
  5. java8 日期api_我们多么想要新的Java日期/时间API?
  6. eclipse报Access restriction: The type 'BASE64Decoder' is not API处理方法
  7. curl 怎么在xp下使用_Http Post 快速使用
  8. 插入排序(java版)
  9. 《是碰巧还是执着?python所阅读的每一场知识点,唯一的共同点就是——参赛选手中,有python之socket编程!》
  10. directx修复工具v3.2增强版_微PE v2.0维护盘增强版万能启动盘20200726
  11. SQL SERVER模糊匹配查询
  12. [Swift]LeetCode480. 滑动窗口中位数 | Sliding Window Median
  13. js事件流 事件捕获 及时间冒泡详解
  14. SQLServer 2008 r2 安装图解
  15. 一、什么是版本控制器
  16. 三维动画在计算机上的应用,计算机图技术在三维动画中的应用.doc
  17. 2016年上半年光伏企业沉浮录
  18. 从阿里、华为、海尔的崛起看:财务生产力可以有多强
  19. 京东零售大数据云原生平台化实践
  20. C++入门——Day5_分支语句和逻辑运算符

热门文章

  1. mt6582 pwm
  2. tf.constant用法
  3. win10查看计算机系统版本,Win10版本怎么看?Win10系统版本查看方法
  4. ASP.NET 母版页小实例(点击显示文本内容)
  5. fluent物性参数拟合多项式,python,matlab多项式图像绘制
  6. 基于python下django框架 实现外卖点餐系统详细设计
  7. java web应用实现扫码枪获取信息
  8. 2023年最值得关注的十大科技趋势,这些技术将迎来爆发,把握住风口和掘金机会!
  9. Sqlserver (优化Sqlserver数据库)页分裂 和填充因子
  10. 安装Linux 乌班图 Ubuntu 系统