【推荐系统】TensorFlow复现论文PNN网络结构

简介: 【推荐系统】TensorFlow复现论文PNN网络结构

下图为PNN的模型结构图,首先将Sparse特征进行Embedding嵌入,然后将其流入Product,分别进行捕捉线性关系lz和特征交叉lp,然后拼接,流到MLP全连接层,最终输出CTR概率值。

一、导包

import 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, LabelEncoder
from tqdm import tqdm
from collections import namedtuple
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')

二、数据处理

"""数据处理"""
def data_process(data, dense_features, sparse_features):
    data[dense_features] = data[dense_features].fillna(0.0)
    for f in dense_features:
        data[f] = data[f].apply(lambda x:np.log(x+1) if x > -1 else -1)
    data[sparse_features] = data[sparse_features].fillna("-1")
    for f in sparse_features:
        lb = LabelEncoder()
        data[f] = lb.fit_transform(data[f])
    return data[dense_features + sparse_features]

三、搭建模型

3.1 输入层

def build_input_layers(dnn_feature_columns):
    dense_input_dict, sparse_input_dict = {}, {}
    for f in dnn_feature_columns:
        if isinstance(f, SparseFeat):
            sparse_input_dict[f.name] = Input(shape=(1), name=f.name)
        elif isinstance(f, DenseFeat):
            dense_input_dict[f.name] = Input(shape=(f.dimension), name=f.name)
    return dense_input_dict, sparse_input_dict

3.2 Embedding层

def build_embedding_layers(dnn_feature_columns, sparse_input_dict, is_linear=False):
    embedding_layers_dict = {}
    sparse_feature_columns = list(filter(lambda x: isinstance(x, SparseFeat), dnn_feature_columns)) if dnn_feature_columns else []
    if is_linear:
        for f in sparse_feature_columns:
            embedding_layers_dict[f.name] = Embedding(f.vocabulary_size + 1, 1, name='1d_embedding_' + f.name)
    else:
        for f in sparse_feature_columns:
            embedding_layers_dict[f.name] = Embedding(f.vocabulary_size + 1, f.embedding_dim, name='kd_embedding_'+ f.name)
    return embedding_layers_dict

3.3 EmbeddingInput

def concat_embedding_list(dnn_feature_columns, sparse_input_dict, embedding_layer_dict, flatten=False):
    sparse_feature_columns = list(filter(lambda x: isinstance(x, SparseFeat), dnn_feature_columns)) if dnn_feature_columns else []
    embedding_list = []
    for f in sparse_feature_columns:
        _input = sparse_input_dict[f.name]
        _embed = embedding_layer_dict[f.name]
        embed = _embed(_input)
        if flatten:
            embed = Flatten()(embed)
        embedding_list.append(embed)
    return embedding_list

3.4 Product层

class ProductLayer(Layer):
    def __init__(self, units, use_inner=True, use_outer=False):
        super(ProductLayer, self).__init__()
        self.use_inner = use_inner # lp使用内积形式
        self.use_outer = use_outer # lp使用外积形式
        self.units = units # lz和lp的神经单元个数
    def build(self, input_shape): # input_shape是输入的Input的形状列表
        self.feat_nums = len(input_shape) # 特征个数
        # input_shape[0]->(None, 1, 4)
        self.embedding_dims = input_shape[0][-1] # Embedding维度4
        # 推导时,使用N*M的Embedding矩阵和同型的W进行内积,这里进行了Flatten展开,然后进行内积
        flatten_dims = self.feat_nums * self.embedding_dims # 26*4=104
        # 定义线性权重 (104,32) 32是神经元个数
        self.linear_w = self.add_weight(name='linear_w',
                                       shape=(flatten_dims, self.units),
                                       initializer='glorot_normal')
        # 定义lp内积权重
        if self.use_inner:
            # 未优化,每个神经元的权重矩阵应为(N,N)
            # 优化后,将w进行分解,用两个N维列向量乘积表示
            self.inner_w = self.add_weight(name='inner_w',
                                          shape=(self.units, self.feat_nums),
                                          initializer='glorot_normal')
        # 定义lp外积权重
        if self.use_outer:
            # 优化后,每个神经元的权重矩阵为(M,M)
            self.outer_w = self.add_weight(name='outer_w',
                                          shape=(self.units, self.embedding_dims, self.embedding_dims),
                                          initializer='glorot_normal')
    def call(self, inputs):
        # inputs是所有Input的列表,要先将其连接
        concat_embedding = Concatenate(axis=1)(inputs) # (?,26,4)
        # 将矩阵展开进行内积
        concat_embed_ = Flatten()(concat_embedding)
        # 进行内积操作
        lz = tf.matmul(concat_embed_, self.linear_w) # (?,104)*(104,32)=>(?,32)
        lp_list = []
        # lp内积形式
        if self.use_inner:
            for i in range(self.units):
                delta = tf.multiply(concat_embedding, tf.expand_dims(self.inner_w[i], axis=1)) # (?,26,4)*(26,1)=>(?,26,4)
                delta = tf.reduce_sum(delta, axis=1) # (?,4)
                delta = tf.reduce_sum(tf.square(delta), axis=1, keepdims=True) # (?,1) 由于reduce会减少一个维度,为了维持能够保留一个特征维度,所以使用keepdims=True
                lp_list.append(delta)
        # lp外积形式
        if self.use_outer:
            feature_sum = tf.reduce_sum(concat_embedding, axis=1) # (?,4) 所有特征的embedding累加求和
            f1 = tf.expand_dims(feature_sum, axis=2) # (?,4,1)
            f2 = tf.expand_dims(feature_sum, axis=1) # (?,1,4)
            product = tf.matmul(f1, f2) # (?,4,1)*(?,1,4)=>(?,4,4)
            for i in range(self.units):
                lpi = tf.multiply(product, self.outer_w[i]) # (?,4,4)*(4,4)=>(?,4,4)
                lpi = tf.reduce_sum(lpi , axis=[1,2]) # (?) 相当于矩阵进行求和
                lpi = tf.expand_dims(lpi, axis=1) # (?,1) 为了后面运算,需要添加一个维度,变成二维矩阵(?,1),每列就代表p和w内积之和
                lp_list.append(lpi)
        # 由于lp_list中装的都是每个神经元的(?,1),所以需要将所有神经元拼接成一个向量
        lp = Concatenate(axis=1)(lp_list) # (?,64) 
        # 将lz和lp进行拼接
        # 正常应该是64,因为lz和lp分别有32个神经元,这里是96是因为lp同时使用了外积和内积导致多了1倍
        product_out = Concatenate(axis=1)([lz, lp]) # (?,96)
        return product_out

3.5 MLP层

def MLP(dnn_inputs, units=(64, 32)):
    for out_dim in units:
        dnn_inputs = Dense(out_dim, activation='relu')(dnn_inputs)
    # 输出层
    output_layers = Dense(1, activation='sigmoid')(dnn_inputs)
    return output_layers

3.6 PNN模型

def PNN(dnn_feature_columns):
    # 1.获取输入层字典
    _, sparse_input_dict = build_input_layers(dnn_feature_columns)
    # 2.获取输入层列表
    input_layers = list(sparse_input_dict.values())
    # 3.获取Embedding层
    embedding_layers_dict = build_embedding_layers(dnn_feature_columns, sparse_input_dict, is_linear=False)
    # 4.将输入层进行Embedding
    sparse_embedding_list = concat_embedding_list(dnn_feature_columns, sparse_input_dict, embedding_layers_dict, flatten=False)
    # 5.将Embedding向量进行Product层操作
    dnn_inputs = ProductLayer(units=32, use_inner=True, use_outer=True)(sparse_embedding_list)
    # 6.将Product层输出传入感知机中
    output_layers = MLP(dnn_inputs, units=(64, 32))
    # 7.构建模型
    model = Model(input_layers,output_layers)
    return model

四、运转模型

4.1 读取数据

"""读取数据"""
data = pd.read_csv('./data/criteo_sample.txt')
columns = data.columns.values
dense_features = [f for f in columns if 'I' in f][:4] # 使用3个特征进行画图
sparse_features =[f for f in columns if 'C' in f][:4]
train_data = data_process(data, dense_features, sparse_features)
train_data['label'] = data['label']

4.2 使用具名数据为特征做标记

SparseFeat = namedtuple('SparseFeat', ['name', 'vocabulary_size', 'embedding_dim'])
DenseFeat = namedtuple('DenseFeat', ['name', 'dimension'])
dnn_feature_columns = [SparseFeat(name=f, vocabulary_size=data[f].nunique(), embedding_dim=4) for f in sparse_features]

4.3 编译模型

# 构建FM模型
history = PNN(dnn_feature_columns)
# history.summary()
history.compile(optimizer="adam", 
            loss="binary_crossentropy", 
            metrics=["binary_crossentropy", tf.keras.metrics.AUC(name='auc')])

4.4 训练模型

# 将输入数据转化成字典的形式输入
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=1,
            validation_split=0.2)

4.5 绘制网络结构

from tensorflow.keras.utils import plot_model
plot_model(history,show_shapes=True)


目录
相关文章
|
17天前
|
机器学习/深度学习 计算机视觉 知识图谱
【YOLOv8改进】MobileViT 更换主干网络: 轻量级、通用且适合移动设备的视觉变压器 (论文笔记+引入代码)
MobileViT是针对移动设备的轻量级视觉Transformer网络,结合CNN的局部特征、Transformer的全局注意力和ViT的表示学习。在ImageNet-1k上,它以600万参数实现78.4%的top-1准确率,超越MobileNetv3和DeiT。MobileViT不仅适用于图像分类,还在目标检测等任务中表现出色,且优化简单,代码已开源。YOLOv8引入了MobileViT块,整合卷积和Transformer结构,提升模型性能。更多详情可参考相关专栏和链接。
|
14天前
|
机器学习/深度学习 算法 TensorFlow
【图像识别】谷物识别系统Python+人工智能深度学习+TensorFlow+卷积算法网络模型+图像识别
谷物识别系统,本系统使用Python作为主要编程语言,通过TensorFlow搭建ResNet50卷积神经算法网络模型,通过对11种谷物图片数据集('大米', '小米', '燕麦', '玉米渣', '红豆', '绿豆', '花生仁', '荞麦', '黄豆', '黑米', '黑豆')进行训练,得到一个进度较高的H5格式的模型文件。然后使用Django框架搭建了一个Web网页端可视化操作界面。实现用户上传一张图片识别其名称。
50 0
【图像识别】谷物识别系统Python+人工智能深度学习+TensorFlow+卷积算法网络模型+图像识别
|
17天前
|
机器学习/深度学习 数据可视化 计算机视觉
【YOLOv8改进】MCA:用于图像识别的深度卷积神经网络中的多维协作注意力 (论文笔记+引入代码)
YOLO目标检测专栏介绍了YOLO的创新改进和实战案例,包括多维协作注意力(MCA)机制,它通过三分支架构同时处理通道、高度和宽度注意力,提高CNN性能。MCA设计了自适应组合和门控机制,增强特征表示,且保持轻量化。该模块适用于各种CNN,实验证明其在图像识别任务上的优越性。此外,文章还展示了如何在YOLOv8中引入MCA层的代码实现和相关任务配置。
|
17天前
|
机器学习/深度学习 计算机视觉
【YOLOv8改进】 YOLOv8 更换骨干网络之GhostNetV2 长距离注意力机制增强廉价操作,构建更强端侧轻量型骨干 (论文笔记+引入代码)
该专栏聚焦YOLO目标检测的创新改进与实战,介绍了轻量级CNNs和注意力机制在移动设备上的应用。文章提出了一种名为GhostNetV2的新架构,结合了硬件友好的DFC注意力机制,强化了特征表达能力和全局信息捕获,同时保持低计算成本和高效推理。GhostNetV2在ImageNet上以167M FLOPs达到75.3%的top-1准确率,优于同类模型。创新点包括DFC注意力、模型结构优化和效率提升。源代码可在GitHub和MindSpore平台上找到。此外,还提到了YOLOv8的相关实现和任务配置。
|
17天前
|
机器学习/深度学习 存储 测试技术
【YOLOv8改进】 YOLOv8 更换骨干网络之 GhostNet :通过低成本操作获得更多特征 (论文笔记+引入代码).md
YOLO目标检测专栏探讨了卷积神经网络的创新改进,如Ghost模块,它通过低成本运算生成更多特征图,降低资源消耗,适用于嵌入式设备。GhostNet利用Ghost模块实现轻量级架构,性能超越MobileNetV3。此外,文章还介绍了SegNeXt,一个高效卷积注意力网络,提升语义分割性能,参数少但效果优于EfficientNet-L2。专栏提供YOLO相关基础解析、改进方法和实战案例。
|
17天前
|
计算机视觉
【YOLOv8改进】 AFPN :渐进特征金字塔网络 (论文笔记+引入代码).md
YOLO目标检测专栏介绍了YOLO的有效改进和实战案例,包括AFPN——一种解决特征金字塔网络信息丢失问题的新方法。AFPN通过非相邻层直接融合和自适应空间融合处理多尺度特征,提高检测性能。此外,还展示了YOLOv8中引入的MPDIoU和ASFF模块的代码实现。详情可参考提供的专栏链接。
|
17天前
|
编解码 计算机视觉 网络架构
【YOLOv8改进】BiFPN:加权双向特征金字塔网络 (论文笔记+引入代码)
该专栏深入研究了YOLO目标检测的神经网络架构优化,提出了加权双向特征金字塔网络(BiFPN)和复合缩放方法,以提升模型效率。BiFPN通过双向跨尺度连接和加权融合增强信息传递,同时具有自适应的网络拓扑结构。结合EfficientNet,构建了EfficientDet系列检测器,在效率和准确性上超越先前技术。此外,介绍了YOLOv8如何引入MPDIoU并应用BiFPN进行可学习权重的特征融合。更多详情可参考提供的专栏链接。
|
18天前
|
机器学习/深度学习 人工智能 算法
中草药识别系统Python+深度学习人工智能+TensorFlow+卷积神经网络算法模型
中草药识别系统Python+深度学习人工智能+TensorFlow+卷积神经网络算法模型
55 0
|
24天前
|
机器学习/深度学习 监控 自动驾驶
【传知代码】从零开始搭建图像去雾神经网络-论文复现
本文介绍了基于集成学习的双分支非均匀去雾神经网络的复现,该网络由迁移学习子网和数据拟合子网组成,分别处理全局表示和数据拟合。网络使用Res2Net作为编码器,并结合通道和像素注意力模块。代码可在提供的链接下载。网络在交通监控、自动驾驶、航海和目标跟踪等领域有广泛应用,通过提升图像质量来提高系统性能。实验在O-Haze、I-Haze和NH-Haze数据集上进行,展示了网络在去除雾霾方面的效果,尽管存在细节模糊和色彩饱和度低的问题。
|
24天前
|
机器学习/深度学习 自然语言处理 搜索推荐
【传知代码】图神经网络长对话理解-论文复现
在ACL2023会议上发表的论文《使用带有辅助跨模态交互的关系时态图神经网络进行对话理解》提出了一种新方法,名为correct,用于多模态情感识别。correct框架通过全局和局部上下文信息捕捉对话情感,同时有效处理跨模态交互和时间依赖。模型利用图神经网络结构,通过构建图来表示对话中的交互和时间关系,提高了情感预测的准确性。在IEMOCAP和CMU-MOSEI数据集上的实验结果证明了correct的有效性。源码和更多细节可在文章链接提供的附件中获取。
【传知代码】图神经网络长对话理解-论文复现

热门文章

最新文章