在本文中介绍两种自定义层次的方法:子类法与lambda方法,其中子类法适合用于定义参数较多的层次,而lambda方法更适合实现自定义一个没有参数的层次,例如:激活函数。相比于子类法,lambda方法实现起来更简单,代码量更少,下面进行具体介绍。
1、子类法定义DenseLayer(全连接层)
实现自定义DenseLayer与之前实现的wide_deep模型很相似,都是通过继承类的方式实现。只不过在wide_deep模型中是将初始化函数与build函数合在一块了,而在这里是分开的。
核心代码展示:
# customized dense layer.
class CustomizedDenseLayer(keras.layers.Layer):
def __init__(self, units, activation=None, **kwargs):
self.units = units #输出单元数
self.activation = keras.layers.Activation(activation)
super(CustomizedDenseLayer, self).__init__(**kwargs)
def build(self, input_shape):
"""构建所需要的参数"""
# x * w + b. x的input_shape:[None, a] ,output_shape: [None, b],w:[a,b]
self.kernel = self.add_weight(name = 'kernel',
shape = (input_shape[1], self.units),
initializer = 'uniform', #初始化方法
trainable = True)
self.bias = self.add_weight(name = 'bias',
shape = (self.units, ),
initializer = 'zeros',
trainable = True)
super(CustomizedDenseLayer, self).build(input_shape)
def call(self, x):
"""完成一次正向计算"""
return self.activation(x @ self.kernel + self.bias)
model = keras.models.Sequential([
CustomizedDenseLayer(30, activation='relu',
input_shape=x_train.shape[1:]),
CustomizedDenseLayer(1),
])
note: keras.layers.Activation(activation)里小写的activation是一个函数(或者是字符串),然后传给keras.layers.Activation后构成了一个层次,在调用self.activation()的时候,这个会触发keras.layers.Activation的call方法,在call方法里,就是调用的之前传进去的activation方法去做。
def call(self, inputs):
return self.activation(inputs)
2、使用lambda方法自定义softplus激活函数层
softplus激活函数没有参数所以采用更简单的lambda方法实现。
核心代码展示:
# 激活函数:tf.nn.softplus : log(1+e^x)
customized_softplus = keras.layers.Lambda(lambda x : tf.nn.softplus(x))
#print(customized_softplus([-10., -5., 0., 5., 10.]))
可以看到在上面有一行注释了的用于测试softplus的代码。它的输出为:
tf.Tensor([4.5417706e-05 6.7153489e-03 6.9314718e-01 5.0067153e+00 1.0000046e+01], shape=(5,), dtype=float32)
即输入x,输出为log(1+e^x)
接下来把定义好的softplus层添加到刚刚定义的模型中,如下:
model = keras.models.Sequential([
CustomizedDenseLayer(30, activation='relu',
input_shape=x_train.shape[1:]),
CustomizedDenseLayer(1), #没有传activation,则默认activation为None,在构造函数中被赋值,在call函数中被使用
customized_softplus,
#keras.layers.Dense(1, activation = "softplus"),
#keras.layers.Dense(1), keras.layers.Actication('softplus'),
])
note:其中:
CustomizedDenseLayer(1),
customized_softplus,
与 keras.layers.Dense(1, activation = "softplus"),
和 keras.layers.Dense(1),
keras.layers.Actication('softplus'),
都是等价的。
附全部代码:
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
import tensorflow as tf
from tensorflow import keras
print(tf.__version__)
print(sys.version_info)
for module in mpl, np, pd, sklearn, tf, keras:
print(module.__name__, module.__version__)
layer = tf.keras.layers.Dense(100)
layer = tf.keras.layers.Dense(100, input_shape=(None, 5))
layer(tf.zeros([10, 5]))
#layer的两个主要方法
layer.variables #打印出来layer里面包含的所有参数
# x * w + b
layer.trainable_variables#获得所有可训练的变量
from sklearn.datasets import fetch_california_housing
housing = fetch_california_housing()
print(housing.DESCR)
print(housing.data.shape)
print(housing.target.shape)
from sklearn.model_selection import train_test_split
x_train_all, x_test, y_train_all, y_test = train_test_split(
housing.data, housing.target, random_state = 7)
x_train, x_valid, y_train, y_valid = train_test_split(
x_train_all, y_train_all, random_state = 11)
print(x_train.shape, y_train.shape)
print(x_valid.shape, y_valid.shape)
print(x_test.shape, y_test.shape)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
x_train_scaled = scaler.fit_transform(x_train)
x_valid_scaled = scaler.transform(x_valid)
x_test_scaled = scaler.transform(x_test)
# 激活函数:tf.nn.softplus : log(1+e^x)
customized_softplus = keras.layers.Lambda(lambda x : tf.nn.softplus(x))
#print(customized_softplus([-10., -5., 0., 5., 10.]))
# customized dense layer.
class CustomizedDenseLayer(keras.layers.Layer):
def __init__(self, units, activation=None, **kwargs):
self.units = units #输出单元数
self.activation = keras.layers.Activation(activation)
super(CustomizedDenseLayer, self).__init__(**kwargs)
def build(self, input_shape):
"""构建所需要的参数"""
# x * w + b. x的input_shape:[None, a] ,output_shape: [None, b],w:[a,b]
self.kernel = self.add_weight(name = 'kernel',
shape = (input_shape[1], self.units),
initializer = 'uniform', #初始化方法
trainable = True)
self.bias = self.add_weight(name = 'bias',
shape = (self.units, ),
initializer = 'zeros',
trainable = True)
super(CustomizedDenseLayer, self).build(input_shape)
def call(self, x):
"""完成一次正向计算"""
return self.activation(x @ self.kernel + self.bias)
model = keras.models.Sequential([
CustomizedDenseLayer(30, activation='relu',
input_shape=x_train.shape[1:]),
CustomizedDenseLayer(1),
customized_softplus,
#keras.layers.Dense(1, activation = "softplus"),
#keras.layers.Dense(1), keras.layers.Actication('softplus'),
])
model.summary()
model.compile(loss="mean_squared_error", optimizer="sgd")
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)
def plot_learning_curves(history):
pd.DataFrame(history.history).plot(figsize=(8, 5))
plt.grid(True)
plt.gca().set_ylim(0, 1)
plt.show()
plot_learning_curves(history)
model.evaluate(x_test_scaled, y_test, verbose=0)