TF01-05:Keras的Model模型使用

本主题主要阐述下Keras框架中的模型Model的使用,主要包含的内容:
  1.模型的两种使用方式;
  2.经典模型的实现(定制模型);
  3.模型的定制训练;


一. 模型使用的两种方式

  • Keras通过两个API提供两种计算模型:

    • 函数式模型:通过Model类API;
    • 顺序式模型:通过Sequential类API;
  • 本文的业务背景还是是深度全链接网络;

    • 4\to8\to4\to1

1. 函数式模型

  • 函数式模型的编程特点是:
    1. 程序员构建层,通过Layer对象的可调用特性,或者使用apply与call实现链式函数调用;
    2. Model只需要通过inputs与outputs;
  • 这种模式之所以称为函数式模型,是因为Layer提供了几种函数式调用方式,通过这种调用建立层之间的网络模型。
    1. Layer是可调用对象,提供__call__可调用运算符(...)
    2. apply函数;
  1. 函数式模型的示意图
函数式模型的示意图
  1. 函数式模型的示例代码 - 使用Layer的可调用运算
# Author:Louis Young
# Note:使用Iris数据集,构建的4-8-4-1的全链接神经网络
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers
import sklearn.datasets as datasets

# 1. 定义Layer层;
#   1.1. 输入层:必须是InputLayer或者Input创建的Tensor;
input_layer = keras.Input(shape=(4,))   # 4是Iris的特征维数(4个特征:可以参考sklearn中关于iris数据集的特征说明)
#   1.2. 隐藏层:8-4
hide1_layer = layers.Dense(units=8, activation='relu')
hide2_layer = layers.Dense(units=4, activation='relu')
#   1.3. 输出层:1
output_layer = layers.Dense(units=1, activation='sigmoid')

#  2. 构建Layer之间的函数链式关系
hide1_layer_tensor = hide1_layer(input_layer)      # <----------------------使用可调用特性
hide2_layer_tensor = hide2_layer(hide1_layer_tensor)
output_layer_tensor = output_layer(hide2_layer_tensor)

# 3. 使用inputs与outputs建立函数链式模型;
model = keras.Model(inputs=input_layer, outputs=output_layer_tensor)   # inputs与outputs一定是Layer调用输出的张量

# 4. 训练
#   4.1. 训练参数
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['mse'])
#   4.2. 训练
data, target = datasets.load_iris(return_X_y=True)
data = data[:100, :]   # 取前面100个样本(第一类与第二类)
target = target[:100]
model.fit(x=data, y=target, batch_size=10, epochs=1000, verbose=0)
#   4.3. 预测与评估
# 6.预测
pre_result = model.predict(data)
category = [0 if item<=0.5 else 1 for item in pre_result ]
accuracy = (target == category).mean()
print(F'分类准确度:{accuracy *100.0:5.2f}%', )
分类准确度:100.00%
  1. 函数式模型的示例代码-使用Layer的apply函数
    • apply函数实际是__call__运算符的别名。
# Author:Louis Young
# Note:使用Iris数据集,构建的4-8-4-1的全链接神经网络
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers
import sklearn.datasets as datasets

# 1. 定义Layer层;
#   1.1. 输入层:必须是InputLayer或者Input创建的Tensor;
input_layer = keras.Input(shape=(4,))   # 4是Iris的特征维数(4个特征:可以参考sklearn中关于iris数据集的特征说明)
#   1.2. 隐藏层:8-4
hide1_layer = layers.Dense(units=8, activation='relu')
hide2_layer = layers.Dense(units=4, activation='relu')
#   1.3. 输出层:1
output_layer = layers.Dense(units=1, activation='sigmoid')

#  2. 构建Layer之间的函数链式关系    
hide1_layer_tensor = hide1_layer.apply(input_layer)            # <----------------------使用apply
hide2_layer_tensor = hide2_layer.apply(hide1_layer_tensor)
output_layer_tensor = output_layer.apply(hide2_layer_tensor)
# 3. 使用inputs与outputs建立函数链式模型;
model = keras.Model(inputs=input_layer, outputs=output_layer_tensor)   # inputs与outputs一定是Layer调用输出的张量

# 4. 训练
#   4.1. 训练参数
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['mse'])
#   4.2. 训练
data, target = datasets.load_iris(return_X_y=True)
data = data[:100, :]   # 取前面100个样本(第一类与第二类)
target = target[:100]
model.fit(x=data, y=target, batch_size=10, epochs=1000, verbose=0)
#   4.3. 预测与评估
# 5.预测
pre_result = model.predict(data)
category = [0 if item<=0.5 else 1 for item in pre_result ]
accuracy = (target == category).mean()
print(F'分类准确度:{accuracy *100.0:5.2f}%', )
Tensor("dense_29/Identity:0", shape=(None, 1), dtype=float32)
分类准确度:100.00%

2. 顺序式模型

  • 顺序式模型的编程特点:
    1. Layer提供input与output属性;
    2. Sequential类通过Layer的input与output属性来维护层之间的关系,构建网络模型;
      • 第一个Layer必须是InputLayer或者Input函数构建的张量;
  1. 顺序式模型示意图
顺序式模型示意图
  1. 顺序式模型的示例代码-layers参数构建模型
# Author:Louis Young
# Note:使用Iris数据集,构建的4-8-4-1的全链接神经网络
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers
import sklearn.datasets as datasets


# 1. 构建Layer层
# 1. 定义Layer层;
#   1.1. 输入层:必须是InputLayer或者Input创建的Tensor;
input_layer = keras.Input(shape=(4,))   # 4是Iris的特征维数(4个特征:可以参考sklearn中关于iris数据集的特征说明)
#   1.2. 隐藏层:8-4
hide1_layer = layers.Dense(units=8, activation='relu')
hide2_layer = layers.Dense(units=4, activation='relu')
#   1.3. 输出层:1
output_layer = layers.Dense(units=1, activation='sigmoid')

# 2. 使用Sequential构建顺序模型
seq_model = keras.Sequential(layers=[input_layer, hide1_layer, hide2_layer, output_layer])

# -------------------下面部分与上面代码完全相同
# 3. 训练
#   3.1. 训练参数
seq_model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['mse'])
#   3.2. 训练
data, target = datasets.load_iris(return_X_y=True)
data = data[:100, :]   # 取前面100个样本(第一类与第二类)
target = target[:100]
seq_model.fit(x=data, y=target, batch_size=10, epochs=1000, verbose=0)
#   3.3. 预测与评估
# 4.预测
pre_result = seq_model.predict(data)
category = [0 if item<=0.5 else 1 for item in pre_result ]
accuracy = (target == category).mean()
print(F'分类准确度:{accuracy *100.0:5.2f}%', )
分类准确度:100.00%
  1. 顺序式模型的示例代码-使用add方法构建模型
# Author:Louis Young
# Note:使用Iris数据集,构建的4-8-4-1的全链接神经网络
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers
import sklearn.datasets as datasets


# 1. 构建Layer层
# 1. 定义Layer层;
#   1.1. 输入层:必须是InputLayer或者Input创建的Tensor;
input_layer = keras.Input(shape=(4,))   # 4是Iris的特征维数(4个特征:可以参考sklearn中关于iris数据集的特征说明)
#   1.2. 隐藏层:8-4
hide1_layer = layers.Dense(units=8, activation='relu')
hide2_layer = layers.Dense(units=4, activation='relu')
#   1.3. 输出层:1
output_layer = layers.Dense(units=1, activation='sigmoid')

# 2. 使用Sequential构建顺序模型
seq_model = keras.Sequential()
seq_model.add(input_layer)
seq_model.add(hide1_layer)
seq_model.add(hide2_layer)
seq_model.add(output_layer)

# -------------------下面部分与上面代码完全相同
# 3. 训练
#   3.1. 训练参数
seq_model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['mse'])
#   3.2. 训练
data, target = datasets.load_iris(return_X_y=True)
data = data[:100, :]   # 取前面100个样本(第一类与第二类)
target = target[:100]
seq_model.fit(x=data, y=target, batch_size=10, epochs=1000, verbose=0)
#   3.3. 预测与评估
# 4.预测
pre_result = seq_model.predict(data)
category = [0 if item<=0.5 else 1 for item in pre_result ]
accuracy = (target == category).mean()
print(F'分类准确度:{accuracy *100.0:5.2f}%', )
分类准确度:100.00%
  1. Layer类input与output属性的意义
# Author:Louis Young
# Note:使用Iris数据集,构建的4-8-4-1的全链接神经网络
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers
import sklearn.datasets as datasets


# 1. 构建Layer层
# 1. 定义Layer层;
#   1.1. 输入层:必须是InputLayer或者Input创建的Tensor;
input_layer = keras.Input(shape=(4,))   # 4是Iris的特征维数(4个特征:可以参考sklearn中关于iris数据集的特征说明)
#   1.2. 隐藏层:8-4
hide1_layer = layers.Dense(units=8, activation='relu')
hide2_layer = layers.Dense(units=4, activation='relu')
#   1.3. 输出层:1
output_layer = layers.Dense(units=1, activation='sigmoid')

# 2. 使用Sequential构建顺序模型
seq_model = keras.Sequential()
seq_model.add(input_layer)
seq_model.add(hide1_layer)
 
# seq_model = keras.Sequential([input_layer, hide1_layer])   与上面三个语句的作用一样。

# add方法本质也是自动调用Layer的可调用对象,只有调用后,Layer才具有input与output属性
print(hide1_layer.input)    # 可以注销22行。会出现错误,就可以理解add函数的作用。

Tensor("input_17:0", shape=(None, 4), dtype=float32)

二. 经典的模型实现-定制模型Model

  • 专业的模型应用:建议子类化Model实现自己的模型。

1. 子类化Model说明

  • 模型的子类化,就是重载call函数。

    • call函数来自model的父类:tensorflow.python.keras.engine.base_layer.Layer
      • Model应该是特殊的Layer。
  • Mode的继承结构

    tf.keras.models.Model
        |- tensorflow.python.keras.engine.network.Network
            |- tensorflow.python.keras.engine.base_layer.Layer
                |- tensorflow.python.module.module.Module
                    |- tensorflow.python.training.tracking.tracking.AutoTrackable
                        
  • 通过继承 Model 类并在 call 方法中实现自己的前向传播,以创建自己的完全定制化的模型,
    • 注意:Model 类继承 API 引入于 Keras 2.2.0

2. 使用定制Model构建模型

  1. 定制过程

    • 继承Model类;
    • 定制构造器,实现定制属性传递;
    • 定制call函数,实现模型的输出;
  2. 定制Model示例代码

# Author:Louis Young
# Note:使用Iris数据集,构建的4-8-4-1的全链接神经网络
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers
import sklearn.datasets as datasets


class ModelException(Exception):
    def __init__(self, msg='模型构建异常'):
        self.message = msg

# 1. 构建模型
class IrisModel(keras.Model):
    # 构造器
    def __init__(self, networks):
        super(IrisModel, self).__init__(name='iris_model')
        # 判定参数类型是否是list
        if not isinstance(networks, list):
            raise ModelException('networks指定网络结构,类型是列表')
        # 生成networks属性
        self.networks = networks
        # 构建层
        self._layers = []
        for _net in networks[:-1]:  # 不考虑输入层
            layer = layers.Dense(units=_net, activation='relu')
            self._layers.append(layer)

        # 最后一层使用sigmoid函数
        layer = layers.Dense(units=networks[-1], activation='sigmoid')
        self._layers.append(layer)

    # forward方法:构建网络模型
    def call(self, inputs, **kwargs):
        # 根据层构建模型输出
        # 第一层的输入来自参数inputs
        x = self._layers[0](inputs)
        for _layer in self._layers[1:]:
            # 上一层输出作为下一层输入调用参数
            x = _layer(x)

        # 返回最后一层作为输出
        return x


# 2. 创建模型实例
model = IrisModel([8, 4, 1])  # 不包含输入的层4
# ----------------------------------------< 以上为典型的模型构建方式。

# 3. 定义训练参数
model.compile(
    optimizer='adam',     # 指定优化器
    loss='binary_crossentropy',   # 指定损失函数
    metrics=['accuracy']
)

# 4. 数据加载
data, target = datasets.load_iris(return_X_y=True)
data = data[:100, :]   # 取前面100个样本(第一类与第二类)
target = target[:100]
# 5. 训练
model.fit(x=data, y=target, batch_size=10, epochs=100, verbose=0)

# 6.预测
# pre_result = model.predict(data)
pre_result = model(data)    # 与上一语句作用一样 predict函数等价于对象的调用
category = [0 if item <= 0.5 else 1 for item in pre_result ]
accuracy = (target == category).mean()
print(F'分类准确度:{accuracy *100.0:5.2f}%', )
分类准确度:100.00%

三. 模型的定制训练

  • 使用Model提供的compile与fit实现训练,确实很方便、易于阅读理解。但想提供更强大的训练控制,就需要理解Model的compile与fit函数实现的细节。
    • 这种细节实际上还是tensorflow的封装,下面我们从更加底层来了解模型的训练。
  • 我们提供一个例子,通过例子来解释模型的训练细节。

1. 定义训练参数

1.1. 优化器对象

  1. Adam构造器说明

    __init__(
        learning_rate=0.001,      # 学习率
        beta_1=0.9,
        beta_2=0.999,
        epsilon=1e-07,
        amsgrad=False,
        name='Adam',
        **kwargs
    )
  1. 优化器的优化调用
    apply_gradients(             # 梯度更新
        grads_and_vars,  
        name=None
    )
  1. 优化器创建代码
import tensorflow.keras.optimizers as optimizers
optimizer = optimizers.Adam(learning_rate=0.0001)

1.2. 损失对象

- 该对象是可调用对象,用来实现损失计算。
  1. CategoricalCrossentropy损失类的构造器说明:


    __init__(
        from_logits=False,
        label_smoothing=0,
        reduction=losses_utils.ReductionV2.AUTO,
        name='categorical_crossentropy'
    )
    
  1. 可调用对象运算符说明
    __call__(
        y_true,        # 标签值
        y_pred,        # 预测值
        sample_weight=None
    )
  • 调用后,可以使用result函数获取结果。
  1. 损失对象构造
import tensorflow.keras.losses as losses
loss =losses.CategoricalCrossentropy()

1.3. 梯度计算

- 使用`tf.GradientTape`计算梯度
  1. 上下文环境
    __enter__()
    
    __exit__(
        typ,
        value,
        traceback
)
  1. 计算梯度
    • 返回与sources一样的结构的梯度结构。
    gradient(
        target,                # 误差项
        sources,
        output_gradients=None,
        unconnected_gradients=tf.UnconnectedGradients.NONE
    )
  1. 梯度对象与梯度计算代码
with tf.GradientTape() as tape:
    gradients = tape.gradient(
        loss,      # 损失张量(不是对象)
        model.trainable_variables)    # 需要更新的可训练权重张量

1.4. 评估度量

- 评估方式用来计算准确率,精准率,召回率等。
- 下面是Accuracy类的说明
  1. Accuracy构造器
    __init__(
        name='accuracy',
        dtype=None
    )
  • 可调用运算符

    • Accuracy类的继承结构是:
    Accuracy
        |- MeanMetricWrapper
            |- Mean
                |- Reduce
                    |- Metric
                        |- tensorflow.python.keras.engine.base_layer.Layer
  • 其中可调用运算符定义:
    def __call__(self, *args, **kwargs):
  • 调用后,可以使用result函数获取结果。
import tensorflow.keras.metrics as metrics
metric = metrics.Accuracy()

2. 训练过程实现

  • 实现步骤:
    1. 计算模型输出
    2. 计算损失值(损失张量,不是损失对象)
    3. 根据损失值,计算误差项
    4. 使用优化器更新梯度

2.1. 训练准备-创建模型

  • 采用上面重复的代码
# Author:Louis Young
# Note:使用Iris数据集,构建的4-8-4-1的全链接神经网络
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers
import tensorflow.keras.optimizers as optimizers
import tensorflow.keras.losses as losses
import tensorflow.keras.metrics as metrics
import sklearn.datasets as datasets


class ModelException(Exception):
    def __init__(self, msg='模型构建异常'):
        self.message = msg

# 1. 构建模型
class IrisModel(keras.Model):
    # 构造器
    def __init__(self, networks):
        super(IrisModel, self).__init__(name='iris_model')
        # 判定参数类型是否是list
        if not isinstance(networks, list):
            raise ModelException('networks指定网络结构,类型是列表')
        # 生成networks属性
        self.networks = networks
        # 构建层
        self._layers = []
        for _net in networks[:-1]:  # 不考虑输入层
            layer = layers.Dense(units=_net, activation='relu')
            self._layers.append(layer)

        # 最后一层使用sigmoid函数
        layer = layers.Dense(units=networks[-1], activation='sigmoid')
        self._layers.append(layer)

    # forward方法:构建网络模型
    def call(self, inputs, **kwargs):
        # 根据层构建模型输出
        # 第一层的输入来自参数inputs
        x = self._layers[0](inputs)
        for _layer in self._layers[1:]:
            # 上一层输出作为下一层输入调用参数
            x = _layer(x)

        # 返回最后一层作为输出
        return x


# 2. 创建模型实例
model = IrisModel([8, 4, 1])  # 不包含输入的层4
# ----------------------------------------< 以上为典型的模型构建方式。

2.2. 定义训练需要的对象

  • 一般训练必须的对象
    • 优化器:optimizers
    • 损失:losses
    • 梯度计算:tf.GradientTape
  • 可选的对象:如果需要在训练的时候,获取一些评估数据,则可以使用。
    • 评估度量:metrics
# 1. 优化器对象
optimizer = optimizers.Adam()
# 2. 损失计算对象
losser = losses.BinaryCrossentropy()
# 3. 准确率计算对象
accuracier = metrics.Accuracy()
# 4. 梯度计算对象  # GradientTape构建后只能调用一次,所以在with中使用
# tape = tf.GradientTape()

2.3. 训练实现

  1. 准备数据
# 准备数据
data, target = datasets.load_iris(return_X_y=True)
data = data[:100, :]   # 取前面100个样本(第一类与第二类)
target = target[:100]

  1. 训练
epochs = 100
batch_size = 10
batch_num = int(math.ceil(len(data) / batch_size))
for epoch in range(epochs):
    for i in range(len(data)):

        with tf.GradientTape() as tape:
            start_ = i * batch_size
            end_ = (i + 1) * batch_size
            # 计算输出
            predictions = model(data[start_: end_])
            # 计算损失
            loss_value = losser(target[start_: end_], predictions)
        # 计算梯度,不能在with中调用
        gradients = tape.gradient(loss_value, model.trainable_variables)
        # 更新权重
        optimizer.apply_gradients(zip(gradients, model.trainable_variables))

pre_result = model(data)
category = [0 if item <= 0.5 else 1 for item in pre_result ]
accuracy = (target == category).mean()
print(F'分类准确度:{accuracy *100.0:5.2f}%')
print(pre_result)
分类准确度:100.00%
tf.Tensor(
[[0.05429551]
 [0.06767229]
 [0.06427257]
 [0.06895139]

...省略  

 [0.99639302]
 [0.95713528]
 [0.99546698]], shape=(100, 1), dtype=float64)
  • 训练100轮,每轮批次大小10,批次数量是10,训练完成后(共训练次数是100*10=1000次),观察分类预测输出值,训练的效果还不错(因为是鸢尾花简单的数据集:D)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355