TensorFlow2.0(五)--Keras构建Wide & Deep模型

1. Wide & Deep模型简介

Wide & Deep模型是 Google 发表在 DLRS 2016 上的文章《Wide & Deep Learning for Recommender System》。Wide & Deep 模型的核心思想是结合线性模型的记忆能力DNN 模型的泛化能力,从而提升整体模型性能。Wide & Deep 已成功应用到了 Google Play 的app推荐业务,并于TensorFlow中封装。

下图是Wide & Deep模型与wide模型的对比。wide模型只有一层,稀疏特征(one-hot)直接连接到输出。Wide & Deep模型的左半部分就是一个wide模型,有半部分是一个deep模型,deep模型具有多层的隐藏层神经网络。对于deep模型,稀疏特征首先被表达为密集型向量,然后经过一个多层的神经网络,最终连接到一个输出层。
在这里插入图片描述
下图是google play商店中所用到的一个推荐算法的Wide & Deep模型结构, 其中"User Installed" 和 “Impression APP” 两个特征经过叉乘直接连接到输出作为Wide结构;其余的特征作为deep结构的输入经过embeddings连接多个隐藏层并最终连接到输出。
在这里插入图片描述

2. Keras实现Wide & Deep模型

2.1 导入相应的库
# matplotlib 用于绘图
import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib inline
# 处理数据的库
import numpy as np
import sklearn
import pandas as pd
# 系统库
import os
import sys
import time
# TensorFlow的库
import tensorflow as tf
from tensorflow import keras
2.2 数据集加载与处理

本篇博客选择使用房价预测的回归问题来构建Wide & Deep模型,因为这个问题的输入只有8个维度,比较容易拆分成wide的输入和deep的输入,而图像的分类问题输入是748维数据,而且数据之间的价值都是相等的,没有拆分的意义。
数据集加载:

from sklearn.datasets import fetch_california_housing
housing = fetch_california_housing()

数据集分割为训练集、测试集与验证集

from sklearn.model_selection import train_test_split
# test_size 指的是划分的训练集和测试集的比例
# test_size 默认值为0.25 表示数据分四份,测试集占一份
x_train_all, x_test, y_train_all, y_test = train_test_split(housing.data, housing.target, random_state = 7, test_size = 0.25)
x_train, x_valid, y_train, y_valid = train_test_split(x_train_all, y_train_all, random_state = 11, test_size = 0.25)

数据归一化处理:

# 数据归一化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
# 训练集数据使用的是 fit_transform,和验证集与测试集中使用的 transform 是不一样的
# fit_transform 可以计算数据的均值和方差并记录下来
# 验证集和测试集用到的均值和方差都是训练集数据的,所以二者的归一化使用 transform 即可
# 归一化只针对输入数据, 标签不变
x_train_scaled = scaler.fit_transform(x_train)
x_valid_scaled = scaler.transform(x_valid)
x_test_scaled = scaler.transform(x_test)
2.3 利用函数式API构建Wide & Deep模型

我们可以利用函数式API构建Wide & Deep模型

# 函数式API实现Wide & Deep model
input = keras.layers.Input(shape=x_train.shape[1:])
"""
函数式API就是我们可以将模型中的层结构当做函数来用
如下所示就是函数式API,将input当做参数传给了hidden1
hidden1又当做参数传递给了hidden2
"""
hidden1 = keras.layers.Dense(30, activation='relu')(input)
hidden2 = keras.layers.Dense(30, activation='relu')(hidden1)
"""
input是wide模型的输入
hidden2是deep模型的输出
concatenate将二者拼接,并将拼接之后的结果传递给output layer
"""
concat = keras.layers.concatenate([input, hidden2])
output = keras.layers.Dense(1)(concat)
"""
固化model
"""
model = keras.models.Model(inputs = [input],
                          outputs = [output])

我们可以看一下模型的结构:

model.summary()

在这里插入图片描述
可以很清楚的看到如下的网络结构:

在这里插入图片描述

2.3 利用子类API构建Wide & Deep模型

我们也可以使用子类API构建Wide & Deep模型:

# 子类API实现Wide & Deep model
"""
子类API就是集成父类来实现
我们首先要定义一个Wide & Deep模型父类
然后通过集成该父类进行模型的构建
"""
class WideDeepModel(keras.models.Model):
    def __init__(self):
        super(WideDeepModel, self).__init__()
        """
        定义模型的层次
        """
        self.hidden1_layer = keras.layers.Dense(30, activation = 'relu')
        self.hidden2_layer = keras.layers.Dense(30, activation = 'relu')
        self.output_layer = keras.layers.Dense(1)
    
    def call(self, input):
        """
        完成模型的正向计算
        """
        hidden1 = self.hidden1_layer(input)
        hidden2 = self.hidden2_layer(hidden1)
        concat = keras.layers.concatenate([input, hidden2])
        output = self.output_layer(concat)
        return output

model = WideDeepModel()
model.build(input_shape = (None, 8))

我们可以看一下模型的结构
在这里插入图片描述

2.4 模型的训练与验证

模型编译与回调函数:

# 编译模型, 损失函数为均方误差函数,优化函数为随机梯度下降
model.compile(loss="mean_squared_error", optimizer = keras.optimizers.SGD(0.001))
# 回调函数使用了EarlyStopping,patience设为5, 阈值设置为1e-2
callbacks = [keras.callbacks.EarlyStopping(patience=5, min_delta=1e-2)]

模型训练

history = model.fit(x_train_scaled, y_train,
                   validation_data=(x_valid_scaled, y_valid),
                   epochs = 100,
                   callbacks= callbacks)

模型验证:

model.evaluate(x_test_scaled, y_test)
2.5 多输入结构

如果我们要使前五个特征作为wide模型的输入,后六个特征作为deep模型的输入,这个时候该怎么做呢?

  • 首先我们在构建模型的时候需要这么写:
"""
多输入结构
"""
input_wide = keras.layers.Input(shape=[5]) # 前五个作为wide的输入
input_deep = keras.layers.Input(shape=[6]) # 后六个作为deep的输入
hidden1 = keras.layers.Dense(30, activation='relu')(input_deep)
hidden2 = keras.layers.Dense(30, activation='relu')(hidden1)
concat = keras.layers.concatenate([input_wide, hidden2])
output = keras.layers.Dense(1)(concat)

"""
固化model
"""
model = keras.models.Model(inputs = [input_wide, input_deep],
                          outputs = [output])

我们来看一下我们的模型结构:

model.summary()

在这里插入图片描述
可以看出来,模型结构为:
在这里插入图片描述

  • 其次,我们需要更改所有的输入为双输入:
    • 更改模型的训练
"""
x_wide:数据集前5个特征
x_deep:数据集后6个特征
"""
x_train_scaled_wide = x_train_scaled[:,:5]
x_train_scaled_deep = x_train_scaled[:,2:]
x_valid_scaled_wide = x_valid_scaled[:,:5]
x_valid_scaled_deep = x_valid_scaled[:,2:]
x_test_scaled_wide = x_test_scaled[:,:5]
x_test_scaled_deep = x_test_scaled[:,2:]

history = model.fit([x_train_scaled_wide, x_train_scaled_deep], y_train,
                   validation_data=([x_valid_scaled_wide, x_valid_scaled_deep], y_valid),
                   epochs = 100,
                   callbacks= callbacks)
  • 更改模型的验证
model.evaluate([x_test_scaled_wide, x_test_scaled_deep], y_test)
发布了175 篇原创文章 · 获赞 237 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_42580947/article/details/105302840