机器学习实战笔记——第十章

本文深入探讨了神经网络的基础,包括反向传播训练算法、回归与分类MLP的构建。重点介绍了如何利用tf.keras搭建神经网络,包括顺序API、函数式API和子类API,详细解析了模型构建、训练、优化和保存过程。此外,还涵盖了神经网络参数优化的策略和常用的超参数选择。
摘要由CSDN通过智能技术生成

目录

一、神经网络基础

1.1 反向传播训练算法

1.2 回归MLP

1.3 分类MLP

二、利用 tf.keras 搭建神经网络

2.1 加载数据

2.2 顺序API创建分类模型

2.2.1 tf.keras.Sequential 

2.2.2 tf.keras.layers.Flatten

2.2.3 tf.keras.layers.Dense()

2.3 顺序API创建回归模型

2.4 函数式API创建复杂模型

2.4.1 tf.keras.Input

2.4.2 tf.keras.layers.Concatenate 

2.4.3 tf.keras.Model

2.5 子类API创建动态模型

2.6 保存和还原模型

2.6.1 tf.keras.models.load_model

2.7 回调函数

 2.7.1 定期保存模型的检查点

 2.7.2 为防止过拟合,提前停止训练

2.7.3 自定义回调

三、神经网络参数优化概述


一、神经网络基础

1.1 反向传播训练算法

        使用有效技术自动计算梯度下降,针对每个模型参数计算网络误差的梯度,大致流程如下:

  1. 一次处理一个小批量并多次遍历整个训练集,每次遍历称为一个轮次
  2. 每个轮次中,将小批量传入输入层,后将其传入隐藏层,并将隐藏层的输出传入下一个隐藏层直到输出,保留中间结果 (前向通路)
  3. 使用一种损失函数来测量网络误差
  4. 应用链式法则,计算每个输出连接对错误的贡献程度
  5. 再次使用链式法则测量下面层的误差贡献直到输入层,即向后传播误差梯度 (后向)
  6. 执行梯度下降

       其中需要随机初始化所有隐藏层的连接权重,流行的激活函数有以下几种

  • 逻辑函数:\sigma (z)=\frac{1}{1+e^{z}}
  • 双曲正切函数:\tanh (z)=2\sigma (2z)-1
  • 线性整流函数:ReLU(z)=max(0, z)

1.2 回归MLP

        回归任务的输入神经元的个数与输出维度相同,如果要保证输出始终为正,则需要在输出层中使用 ReLU 激活函数,或者使用该函数的平滑变体 softplus 激活函数,softplus(z)=log(1+e^{z}) ;如果要保证预测值落在给定范围内,则应使用逻辑函数或者双曲正切函数作为输出层的激活函数。

        训练使用的损失函数通常为均方误差,如果输入存在较多的离群值,则应使用平均绝对误差,或者使用 Huber 损失。

回归MLP架构
超参数典型值
输入神经元数量每个输入特征一个
隐藏层数量通常为1到5
每个隐藏层的神经元数量通常为10到100
输出神经元数量等于预测维度
隐藏层的激活函数ReLU
输出层的激活函数无、ReLU/softplus、逻辑、tanh
损失函数MSE、MAE/Huber

1.3 分类MLP

        对于二分类问题,只需要一个输出神经元,将输出结果分为正负类。

        对于多标签二分类问题,如同时预测邮件是否是垃圾邮件和是否是紧急邮件,则需要两个输出神经元,即为每个正类设置一个输出神经元。

        对于多分类问题,则输出神经元的个数等于类的个数,并使用 softmax 激活函数作为输出层的激活函数来保证将概率限制在 0 到 1 之间并且加起来等于 1 。

分类MLP架构
超参数二进制分类多标签二进制分类多类分类
输入层和隐藏层同回归MLP
输出神经元个数1每个标签1个每个类1个
输出层的激活函数逻辑逻辑softmax
损失函数交叉熵

二、利用 tf.keras 搭建神经网络

2.1 加载数据

        Keras 里提供了用以加载数据的常见数据集如 MNIST、 Fashion MNIST等,以 Fashion MNIST 为例:

import tensorflow as tf
from tensorflow import keras

# 加载数据
fashion_mnist = keras.datasets.fashion_mnist
(X_train, y_train), (X_test, y_test) = fashion_mnist.load_data()

        首先查看训练集的形状:

         可以看出训练集有 60000 张图片,每张图片用一个 28*28 的数组存储。注意该训练集并未划分出验证集,所以接下来对训练集进行归一化处理并划分出验证集:

X_valid, X_train = X_train[:5000] / 255.0, X_train[5000:] / 255.0
y_valid, y_train = y_train[:5000], y_train[5000:]

2.2 顺序API创建分类模型

        建立顺序模型的流程如下:

model = keras.models.Sequential()
model.add(keras.layers.Flatten(input_shape=[28, 28]))
model.add(keras.layers.Dense(300, activation="relu"))
model.add(keras.layers.Dense(100, activation="relu"))
model.add(keras.layers.Dense(10, activation="softmax"))

        如上所建立的模型中,有一个输入层,两个隐藏层和一个输出层,两个隐藏层均采用 relu 激活函数,由于是一个多分类问题,并且共有十种类别,所以输出层的神经元个数为 10 个,且采用 softmax 激活函数 。上述编码方式的等效编码方式是:

# 仅作参考

model = keras.Sequential([
    # keras.layers.Conv2D(64, (3, 3), activation='relu', input_shape=(28, 28, 1)),    # 卷积层
    # keras.layers.MaxPooling2D(2, 2),                    #  MaxPooling 取最大特征值
    # keras.layers.Conv2D(64, (3, 3), activation='relu'),    # 卷积层
    # keras.layers.MaxPooling2D(2, 2),
    keras.layers.Flatten(input_shape=(28, 28)),         # 输入层,将28*28的输入展平
    keras.layers.Dense(128, activation=tf.nn.relu),     # 中间层
    keras.layers.Dense(10, activation=tf.nn.softmax)    # 输出层,10个类别
])

         可以通过 model.summary() 来展示模型概要。

        可以通过索引或者 get_layer() 获取层。 

        可以通过 get_weights 和 set_weights 访问各层的参数。

        可以看到各个神经元的偏置均被初始化为0 。

        然后调用 compile() 方法来指定损失函数和要使用的优化器。

model.compile(
    loss="sparse_categorical_crossentropy", 
    optimizer="sgd", 
    metrics=["accuracy"])

        在多分类下,各类互斥,所以使用 sparse_categorical_crossentropy 作为损失函数,使用 softmax 作为输出层的激活函数;若为二分类,则应在输出层使用 sigmoid 即逻辑作为激活函数,使用 binary_crossentropy 作为损失函数。其中采用 sgd 即简单随机梯度下降作为优化器,学习率默认为 0.01,但通常通过 

tf.keras.optimizers.SGD(learning_rate=0.01)

来设置学习率。 

        配置好模型后,就可以开始拟合。

history = model.fit(X_train, y_train, 
                    epochs=100, 
                    validation_data=(X_valid, y_valid), 
                    callbacks=[checkpoint, early_callback, tensorboard_cb])

        如果训练集不平衡,可以通过 fit() 方法中的 class_weight 参数来调整;如果需要每个实例的权重,可以设置 sample_weight 参数;若两个参数都提供,则训练中会将其相乘。

        返回的 history 对象包括训练参数 history.params,训练轮次列表 history.epoch,且有很重要的以字典形式存储的损失指标等 history.history,可以通过 pandas 创建 DataFrame 对象并调用 plot() 方法绘制学习曲线,代码如下。

import pandas as pd
import matplotlib.pyplot as plt

pd.DataFrame(history.history).plot(figsize=(8, 5))
plt.grid(True)
plt.gca().set_ylim(0, 1)
plt.show()

        得到的绘制结果如图所示。

         训练完成后,可以输入测试集来评估其泛化误差。

model.evaluate(X_test, y_test)

         最后可以使用模型的 predict() 方法来进行预测。接下来将对上述流程涉及到的方法进行简单的汇总。

2.2.1 tf.keras.Sequential 

        用来创建一个顺序模型。

tf.keras.Sequential(
    layers=None, name=None
)
参数注释
layers需要添加到模型中的层列表
name模型的可选名
属性注释
layers存储该神经网络模型中的各层
metrics_names返回所有输出的模型显示标签
该方法返回的 model 对象所含有的方法
方法名参数参数解释方法用途
addlayer一层神经元向模型中添加一层神经元
summary其它见 tf.keras.Sequential  |  TensorFlow Core v2.6.0模型摘要
compileoptimizer优化器名称,见 tf.keras.Optimizers对训练模型的配置
loss损失函数,为字符串或 tf.kears.losses.Loss 实例,见 tf.keras.losses
metrics模型训练和测试期间的评估指标,可以为字符串或者 tf.kears.metrics.Metric 实例,见tf.kears.metrics,注意赋值方式应为 metrics=['accuracy'],应为多输出模型赋予多个指标
其它见 tf.keras.Sequential  |  TensorFlow Core v2.6.0
fitx输入数据,可以为 Numpy 数组、Tensorflow 张量等训练模型
y标签,可以为 Numpy 数组、Tensorflow 张量等
为一个整数,用以指定每次梯度更新的样本数,默认为32;如有 6W 个样本,将 batch_size 设置为32,则每个轮次训练 60000/32 个样本
为整数,指定训练几次模型
callbacks训练期间的回调列表,可用于提前终止对模型的训练等操作,值为 tf.keras.callbacks.Callbacks 实例列表
validation_split介于 0 和 1 之间的浮点数。要用作验证数据的训练数据的一部分。该模型将把这部分训练数据分开,不会对其进行训练,并将在每个时期结束时评估损失和此数据的任何模型指标。
validation_data训练集,值为一个二元的 Numpy 或者 Tensorflow 张量元组,(x_val, y_val)
其它见 tf.keras.Sequential  |  TensorFlow Core v2.6.0
evaluatex, y同上对模型输入测试集后测试模型的性能,并返回一个 history 对象来记录每次训练的指标
其它见 tf.keras.Sequential  |  TensorFlow Core v2.6.0
get_layerindex索引返回一层神经元
name图层名
load_weightsfilepath 要加载的权重文件路径从 TensorFlow 或 HDF5 权重文件加载所有层权重。
by_name为 True 则仅当权重共享相同名称时才加载,否则只有当网络拓扑相同时才加载
skip_mismatch是否跳过权重数量不匹配或权重形状不匹配的层的加载
optionstf.train.CheckpointOption
predictx同上预测
其它​​​​​​tf.keras.Sequential  |  TensorFlow Core v2.6.0
savefilepath存储路径存储模型
其它见 tf.keras.Sequential  |  TensorFlow Core v2.6.0
save_weightsfilepath存储路径存储权重
其它方法见 tf.keras.Sequential  |  TensorFlow Core v2.6.0

2.2.2 tf.keras.layers.Flatten

tf.keras.layers.Flatten(
    data_format=None, **kwargs
)

       创建模型中的一层,仅用于展平输入,即将输入转化为一维数组,如例子中指定 input_shape = [28, 28]

2.2.3 tf.keras.layers.Dense()

tf.keras.layers.Dense(
    units, activation=None, use_bias=True,
    kernel_initializer='glorot_uniform',
    bias_initializer='zeros', kernel_regularizer=None,
    bias_regularizer=None, activity_regularizer=None, kernel_constraint=None,
    bias_constraint=None, **kwargs
)

         创建模型中的一层神经元,参数表如下:

参数注释
units该层神经元的维度
activation激活函数
其它见 tf.keras.layers.Dense  |  TensorFlow Core v2.6.0

2.3 顺序API创建回归模型

        在上一节介绍了顺序 API 创建分类模型的问题,本节则要介绍顺序 API 创建回归模型的问题,相比于分类模型,对于单标签的输出模型中输出神经元仅只有一个,且输出神经元中不适用激活函数,模型采用的损失函数为均方误差。代码参考如下:

model = keras.Sequential([
    keras.layers.Dense(30, activation='relu', input_shape=X_train.shape[1:]),
    keras.layers.Dense(1)
])
model.compile(loss='mean_squared_error', optimizer='sgd')

history = model.fit(X_train, y_train, epochs=20, validation=(X_valid, y_valid))

2.4 函数式API创建复杂模型

        非顺序神经网络的一个代表为“宽深”神经网络,它可将所有或部分输入直接连接至输出层,该架构可使神经网络能够学习深度模式(通过深度路径)或者简单规则(通过短路径)。

        该方式建立神经网络的流程参考如下:

# 首先创建 Input 对象,需要规定 shape 和 dtype
input_ = keras.Input(shape=X_train.shape[1:])

# 创建一个有30个神经元的使用 relu 作为激活函数的隐藏层1,并通过 (input_) 指出该层的输入是 input_
hidden1 = keras.layers.Dense(30, activation='relu')(input_)

# 创建一个同上的隐藏层,并指定该层的输入是 hidden1 的输出
hiddden2 = keras.layers.Dense(30, activation='relu')(hidden1)

# 创建一个连接层,并指定该层的输入是 input_ 和 hidden2 的输出
concat = keras.layers.Concatenate()([input_, hiddden2])

# 创建输出层并指定该层的输入是 concat 的输出
output = keras.layers.Dense(1)(concat)

# 构建模型
model = keras.Model(inputs=[input_], outputs=output)

        可以看出代码中以类似函数的方式将各层连接在一起,在 keras.Model() 中的参数中,可以通过向 inputs 参数传入一个列表来处理多输出问题,如:

model = keras.Model(inputs=[inpu_A, input_B], outputs=output)

        若存在多输入问题,则调用 fit() 、evaluate() 等方法时都应传入多个矩阵。

        同样可以向 outputs 参数传递一个列表来处理多输出:

model = keras.Model(inputs=[input_A, input_B], outputs=[output_A, output_B])

         在遇到以下问题时可能遇到多输出:

  • 在图片中定位和分类主要物体,这既是回归又是分类
  • 可能有基于同一数据的多个独立任务,但多数情况下为每个任务训练一个模型会有更好的结果
  • 正则化技术减少过拟合。

        以下代码来构建一个正则化示例:

input_A = keras.Input(shape=[5], name="wide_input")
input_A = keras.Input(shape=[65], name="deepwide_input")
hidden1 = keras.layers.Dense(30, activation='relu')(input_B)
hiddden2 = keras.layers.Dense(30, activation='relu')(hidden1)
concat = keras.layers.Concatenate()([input_A, hiddden2])
output = keras.layers.Dense(1)(concat)
aux_output = keras.layers.Dense(1, name="aux_output")(hidden2)

model = keras.Model(inputs=[input_A, input_B], outputs=[output, axu_output])

model.compile(loss=['mse', 'mse'], loss_weights=[0.9, 0.1], optimizer='sgd')

history = model.fit([X_train_A, X_train_B], [y_train, y_train], epochs=20, validation_data=([X_valid_A, X_valid_B], [y_valid, y_valid]))

total_loss, main_loss, axu_loss = model.evaluate([X_test_A, X_test_B], [y_test, y_test])

        可以看到,针对每个输出,在 compile 函数中以列表形式配置了一个损失函数,并且通过 loss_weights 参数来设置损失权重,因为更关系主要输出而不是用于正则化的辅助输出,所以给主要输出设置更高的损失权重。本小节涉及到的一些方法如下:

2.4.1 tf.keras.Input

        用来实例化 tf 张量

tf.keras.Input(
    shape=None, batch_size=None, name=None, dtype=None, sparse=None, tensor=None,
    ragged=None, type_spec=None, **kwargs
)
参数注释
shape形状元组(整数),不包括批量大小。
其它见 tf.keras.Input  |  TensorFlow Core v2.6.0

2.4.2 tf.keras.layers.Concatenate 

        用来连接输入列表的层。它接受一个张量列表作为输入,除了串联轴外,所有形状都相同,并返回一个单一的张量,它是所有输入的串联。

tf.keras.layers.Concatenate(
    axis=-1, **kwargs
)

2.4.3 tf.keras.Model

tf.keras.Model(
    *args, **kwargs
)
参数注释
inputs模型的输入
outputs模型的输出
name模型的名字

        该方法返回一个模型,可参考 2.2.1 节的 model 方法来使用。

2.5 子类API创建动态模型

        顺序式和函数式都是声明式的,即先声明确定使用的层,整个模型是一个静态的图,这样有助于保存、克隆、共享和查看分析其结构。可以通过继承 Model 类,在子类的构造函数中创建所需的层并通过 call 方法来执行计算,这就是子类 API。参考代码如下:

class WideAndDeepModel(keras.Model):
    def __init__(self, units=30, activation="relu", **kwargs):
        super().__init__(**kwargs)
        self.hidden1 = keras.layers.Dense(units, activation=activation)
        self.hidden2 = keras.layers.Dense(units, activation=activation)
        self.main_output = keras.layers.Dense(1)
        self.aux_output = keras.layers.Dense(1)

    def call(self, inputs):
        input_A, input_B = inputs
        hidden1 = self.hidden1(input_B)
        hidden2 = self.hidden2(hidden1)
        concat = keras.layers.concatenate([input_A, hidden2])
        main_output = self.main_output(concat)
        aux_output = self.aux_output(hidden2)
        return main_output, aux_output

        但是这样做会导致模型的架构隐藏在 call 方法中,无法保存和克隆。 

2.6 保存和还原模型

        使用顺序 API 或者函数式 API 可以非常简单保存训练好的模型, keras 使用 HFD5 格式来保存模型的结构和参数、优化器等。代码参考如下:

# 保存训练好的模型
model.save('./model1.h5')
# 加载模型
model = keras.models.load_model('./model1.h5')

        若使用子类方式,则只能通过 save_weights 和 load_weights 来保存和读取参数。

        本小节涉及到的 API 如下:

2.6.1 tf.keras.models.load_model

tf.keras.models.load_model(
    filepath, custom_objects=None, compile=True, options=None
)
参数注释
filepath

字符串或 pathlib.Path 对象,保存模型的路径;

h5py.File  对象

其它见 tf.keras.models.load_model  |  TensorFlow Core v2.6.0

2.7 回调函数

        注意到在前面模型构建后使用 fit 函数拟合过程中,对 callbacks 参数进行了赋值。该参数可以指定模型在训练开始和结束时、轮次开始和结束时将调用的对象列表。

        会给 callbacks 参数赋予一个列表,列表中的值为回调对象,可以同时结合使用多个回调,目前涉及到的回调对象如下。 

 2.7.1 定期保存模型的检查点

        通过 tf.keras.callbacks.ModelCheckpoint 实现以某个频率保存 Keras 模型或模型权重。

tf.keras.callbacks.ModelCheckpoint(
    filepath, monitor='val_loss', verbose=0, save_best_only=False,
    save_weights_only=False, mode='auto', save_freq='epoch',
    options=None, **kwargs
)
参数注释
filepath保存模型的文件路径
monitor要监控的指标名称,具体设置见链接
save_best_only当模型最佳时才保存
其它见 tf.keras.callbacks.ModelCheckpoint  |  TensorFlow Core v2.6.0

        使用示例如下:

checkpoint = keras.callbacks.ModelCheckpoint("./model1.h5", save_best_only=True)

 2.7.2 为防止过拟合,提前停止训练

        通过 tf.keras.callbacks.EarlyStopping 回调,如果多个轮次在验证集上没有明显进展则会中断训练,并选择回滚至最佳模型。

tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', min_delta=0, patience=0, verbose=0,
    mode='auto', baseline=None, restore_best_weights=False
)
参数注释
patience设置多少轮次没有改进后就停止训练
restore_best_weights是否回滚到最佳模型
其它见 tf.keras.callbacks.EarlyStopping  |  TensorFlow Core v2.6.0

2.7.3 自定义回调

        可以通过继承 tf.keras.callbacks.Callback 类来编写自定义的回调,并通过实现 on_train_begin、on_train_end、on_epoch_begin、on_epoch_end、on_batch_begin、on_batch_end 等函数来自定义回调调用时期,并可以通过实现 on_predict_begin 等函数实现预测,详细见 tf.keras.callbacks.Callback  |  TensorFlow Core v2.6.0 ,代码示例如下:

class MyCallback(tf.keras.callbacks.Callback):
    def on_train_end(self, logs=None):
        pass

三、神经网络参数优化概述

        

可用于优化超参数的python库
Hyperopt用于优化各种复杂的搜索空间(学习率的实数值和层数的离散值等)
Hyperas、kopt或 Talos用于优化 Keras 的超参数
Keras TunerGoogle 针对 Keras 提供的易于使用的超参数优化库,带有可视化分析和托管服务
skopt通用优化库
Spearmint贝叶斯优化库
Hyperband快速超参数调整库
Sklearn-Deap基于进化算法的超参数优化库
超参数
隐藏层数量对于复杂问题,深层网络可以使用更少的神经元对更复杂的函数进行吉纳摩,因而效率较高
每个隐藏层神经元的数量

通常调整成金字塔形状,因为低层特征可以合并成更少的高层特征;

可以选择逐渐增加神经元的数量,也可以选择一个比实际需要的层和神经元更多的模型然后使用提前停止或正则化技术等防止过拟合;

不过通常增加隐藏层的数量会有更好的收益

学习率

一般而言,最佳学习率约为最大学习率的一半;

从非常低的学习率(1e-5)开始,逐渐增加到非常大的值如10

优化器选择比普通的小批量梯度下降更好的优化器
批量大小

大批量可能会导致训练不稳定,模型泛化能力差;

一种思路是首先选择小批量(2-32),从而在短时间内获得更好额模型;

另一种思路是使用大批量处理并慢慢增加学习率,如果训练不稳定则换用小批量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值