这篇文章承接上一篇预热TensoFlow2.0——IRIS数据集实战,主是想更加详细介绍一下如何使用tensorflow2.0中使用Keras高级API快速构建模型。Keras是一个底层为tensoflow或者theano的高度封装的深度学习库。TensorFlow2.0官方推荐使用Keras进行深度神经网络构建,说明Keras在网络构建方面确实有着其独到的优势。很多人不满意Keras的灵活性,称其无法根据自己的需求构建网络结构。我这里小小的反驳一下:
- 其实Keras可以可以很方便的让用户的自定义神经网络层,
- Keras也可以让用户很方便的定义损失函数,
- Keras本身就是基于tensorflow封装的,能非常好的和tensorflow兼容。
接下来笔者就通过一个房价预估实战,为大家介绍一下这个即将成为TensoFlow2.0版模型构建新宠儿的Keras。这里笔者只使用了tensorflow去构建损失函数,大多数API依然是pure Keras,不过整个流程参考的是TensorFlow2.0 preview版本的教程。
import tensorflow as tf
import keras
from keras.optimizers import Adam
import keras.backend as K
载入数据
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
housing = fetch_california_housing()
房价预测任务就是给模型输入房屋信息数据,模型能够准确的预测出房价的值。数据格式如下图所示一个data是一个维度为8维的数组,是一些房屋的年岁,卧室面积大小之类的信息,需要预测的target是房价的值。
数据预处理
将数据分为训练集和测试剂,然后对数据做一些简单的预处理工作——标准化:即将数据转换为均值为0,方差为1的正态分布。
X_train_full, X_test, y_train_full, y_test = train_test_split(
housing.data, housing.target.reshape(-1, 1), random_state=42)
X_train, X_valid, y_train, y_valid = train_test_split(
X_train_full, y_train_full, random_state=42)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_valid_scaled = scaler.transform(X_valid)
X_test_scaled = scaler.transform(X_test)
用户自定义层
这里就开始使用keras自定义层了,需要注意keras自定义层的格式:
- 一定要继承keras.layers.Layer这个类
- __ init __ 定义网络层的一些基本属性,神经元个数,激活函数之类。这里激活函数继承了keras自带的Activation,
- build方法用于定义需要优化的参数,
- call方法很重要,这里你需要定义网络层需要做的操作,笔者定义的就是一个简单的Dense层的操作F(wx+b),
- compute_output_shape是用来控制输出的维度,如果这个网络层的输入和输出形状一致,那么此方法可以省略。
笔者这里为了演示,把keras.layers.Dense重新实现了一遍而已。大家可以通过这样的格式定义各种其他功能的网络层。
class MyDense(keras.layers.Layer):
def __init__(self,units, activation=None, **kwargs):
self.units = units
self.output_dim = units
self.activation = keras.layers.Activation(activation)
super(MyDense, self).__init__(**kwargs)
def build(self, input_shape):
self.kernel = self.add_weight(name='kernel',
shape=(input_shape[1], self.units),
initializer='uniform',
trainable=True)
self.biases = self.add_weight(name='bias',
shape=(self.units,),
initializer='zeros',
trainable=True)
super(MyDense, self).build(input_shape)
def call(self, X):
return self.activation(K.dot(X,self.kernel) + self.biases)
def compute_output_shape(self, input_shape):
# 计算输出形状,如果输入和输出形状一致,那么可以省略,否则最好加上
return (input_shape[0], self.output_dim)
用户自定义模型(Wide and Deep)
接下来就是用户自定义模型,这里依然有个keras官方建议的标准格式:
- 首先必须继承keras.models.Model这个类,
- __ init __ 定义模型需要的网络层,如果把搭网络架构比作堆积木的话,这里就是从袋子里把积木块拿出来。
- call 就是将这层就是用已有的积木块组合搭建网络结构啦
这里笔者搭建了一个Wide and Deep的网络架构,笔者之前的文章对这个模型进行了详细的介绍,不明白的可以点这个链接去了解下。从call方法的代码中可以看出其具体的操作是——将原输入经过两层神经网络后的输出与原输入拼接之后再喂给一层感知器。
class MyModel(keras.models.Model):
def __init__(self):
super(MyModel, self).__init__()
self.hidden1 = MyDense(30, activation="relu")
self.hidden2 = MyDense(30, activation="relu")
self.output_ = MyDense(1)
def call(self, input):
hidden1 = self.hidden1(input)
hidden2 = self.hidden2(hidden1)
concat = keras.layers.concatenate([input, hidden2])
output = self.output_(concat)
return output
用户自定以损失函数
自定义损失函数,用tensoflow的api,就可以定义你自己想要的损失函数。由于是做一个回归问题,笔者这里定义了一个mean square error的损失函数。
def my_mse(y_true, y_pred):
return tf.reduce_mean(tf.square(y_pred - y_true))
实例化模型
解下来要做的就是实例化和编译模型,代码也很简单。使用keras自带的compile方法,传入之前定义好的损失函数和优化器即可。
model = MyModel()
model.compile(loss=my_mse, optimizer=Adam(lr=1e-4))
定以callbacks
callbacks在笔者看来是keras里面的一个神奇的特性,你可以通过callbacks定义模型一些在训练过程中的适当时机可被调用函数。keras里面已经帮你封装了一些特别有用的callbacks函数,这里介绍三个常用的方法:
- keras.callbacks.TensorBoard 可以通过tensorboard 可视化训练过程,需要传一个可视化文件保存的路径给这个函数,
- keras.callbacks.EarlyStopping能够有效的防止模型过拟合,其机制就是如果模型的val_loss不降反增,此方法能够提前终止模型训练。笔者这里设置的是连续5个epoch模型的val_loss都是增加的话,模型就停止训练。
- keras.callbacks.ModelCheckpoint 保存模型的checkpoint,同时你可以自己定义checkpoint的命名和保存频率,这里笔者选择5个epoch,保存一下checkpoint。
大家也可以自己定义一下其他的callback函数,比如计算acc和recall之类的。
logdir = "./log"
filepath="./checkpoint/model_{epoch:02d}-{val_loss:.2f}.hdf5"
checkpoint = keras.callbacks.ModelCheckpoint(filepath,
monitor='val_loss', save_weights_only=True,verbose=1,save_best_only=True, period=5)
callbacks = [
keras.callbacks.TensorBoard(logdir),
keras.callbacks.EarlyStopping(patience=5),
checkpoint,
]
模型训练
接下来模执行下面代码模型就跑起来了,记得把之前定义的callbacks函数的list传给fit方法。
model.fit(X_train_scaled, y_train,batch_size=32, epochs=20,
validation_data=(X_valid_scaled, y_valid),
callbacks=callbacks)
从模型训练输出,我们可以看到,模型每5个epoch帮我保存了一个checkpoint。不知道你们发现没有,原本笔者定义跑20个epoch,但是最终只跑着13个epoch模型就终止了训练。仔细观察的话,9-13epoch的过程中val_loss一直在增加,说明模型开始过拟合了,callbacks定义的keras.callbacks.EarlyStopping起了作用,在模型过拟合时提前终止了模型的训练。
使用tensorboard --logdir log 之后我们通过浏览器访问localhost:6006 我们就可以看到模型训练过程中的loss曲线了。
结语
整个过程代码量极少,而且模型的构建(复杂的模型也可以很方便的构建),模型的训练,模型的保存,loss的可视化相比于tensorflow之前的操作容易了许多。是时候学习一波tensorflow2.0官方推荐的模型构建API——Keras了。