Keras 从入门到精通
“sample”“batch”“epoch”
Sample:样本,比如:一张图像是一个样本,一段音频也是一个样本。
Batch:批,含有N个样本的集合。每一个batch的样本都是独立的并行处理。在训练是,一个batch的结果只会用来更新一次模型。
Epoch:轮次,通常通常定义为 [在整个数据集上的一轮迭代],用于训练的不同的阶段,这有利于记录和定期
保存/加载Keras模型(结构+权重+优化器状态)
model.save(filepath)将Keras模型保存到单个HDF5文件中,该文件将包含:
- 模型的结构,允许重新创建模型
- 模型的权重
- 训练配置项(损失函数、优化器)
- 优化器状态,允许准确地从你上次结束的地方继续训练
keras.models.load_model(filepath)重新实例化模型,load_model还将负责使用保存的训练配置项来编译模型(除非模型从未编译过)
from keras.models import load_model model.save('my_model.h5') # 创建 HDF5 文件 'my_model.h5' del model # 删除现有模型 # 返回一个编译好的模型 # 与之前那个相同 model = load_model('my_model.h5')
只保存/加载模型的结构
如果我们只需要保存模型的结构,而非其权重或训练配置项,则:
model.save_weights('my_model_weights.h5')
假如我们有用于实例化模型的代码,则可以将保存的权重加载到具有相同结构的模型中:
model.load_weights('my_model_weights.h5')
如果你需要将权重加载到不同的结构(有一些共同层)的模型中,例如微调或迁移学习,则可以按层的名字来加载权重:
model.load_weights('my_model_weights.h5', by_name=True)
例如:
""" 假设原始模型如下所示: model = Sequential() model.add(Dense(2, input_dim=3, name='dense_1')) model.add(Dense(3, name='dense_2')) ... model.save_weights(fname) """ # 新模型 model = Sequential() model.add(Dense(2, input_dim=3, name='dense_1')) # 将被加载 model.add(Dense(10, name='new_dense')) # 将不被加载 # 从第一个模型加载权重;只会影响第一层,dense_1 model.load_weights(fname, by_name=True)
获取中间层的输出
创建一个新的Model来输出:
from keras.models import Model model = ... # 创建原始模型 layer_name = 'my_layer' intermediate_layer_model = Model(inputs=model.input, outputs=model.get_layer(layer_name).output) intermediate_output = intermediate_layer_model.predict(data)
或者构建一个Keras函数,该函数将在给定输入的情况下返回某一层的输出,例如:
from keras import backend as K # 以 Sequential 模型为例 get_3rd_layer_output = K.function([model.layers[0].input], [model.layers[3].output]) # 测试模式的输出 layer_output = get_3rd_layer_output([x])[0]
使用Keras处理超过内存的数据集
使用 model.train_on_batch(x,y)
和 model.test_on_batch(x,y)
进行批量训练与测试。请参阅 模型文档。
或者,你可以编写一个生成批处理训练数据的生成器,然后使用 model.fit_generator(data_generator,steps_per_epoch,epochs)
方法。可以在 CIFAR10 example 中找到实践代码。
在验证集的误差不再下降时,中断训练
使用 EarlyStopping
回调:
from keras.callbacks import EarlyStopping early_stopping = EarlyStopping(monitor='val_loss', patience=2) model.fit(x, y, validation_split=0.2, callbacks=[early_stopping])
Keras 验证集的划分
如果您将 model.fit
中的 validation_split
参数设置为 0.1,那么使用的验证数据将是最后 10% 的数据。如果设置为 0.25,就是最后 25% 的数据。注意,在提取分割验证集之前,数据不会被混洗,因此验证集仅仅是传递的输入中最后一个 x% 的样本。
所有 epoch 都使用相同的验证集(在同一个 fit
中调用)。
在训练过程中数据是否会混洗?
是的,如果 model.fit
中的 shuffle
参数设置为 True(默认值),则训练数据将在每个 epoch 混洗。
验证集永远不会混洗。
如何在每个 epoch 后记录训练集和验证集的误差和准确率?
model.fit
方法返回一个 History
回调,它具有包含连续误差的列表和其他度量的 history
属性。
hist = model.fit(x, y, validation_split=0.2) print(hist.history)
如何「冻结」网络层?
「冻结」一个层意味着将其排除在训练之外,即其权重将永远不会更新。
可以将 trainable
参数(布尔值)传递给一个层的构造器,以将该层设置为不可训练的:
frozen_layer = Dense(32, trainable=False)
另外,可以在实例化之后将网络层的 trainable
属性设置为 True 或 False。为了使之生效,在修改 trainable
属性之后,需要在模型上调用 compile()
。这是一个例子:
x = Input(shape=(32,)) layer = Dense(32) layer.trainable = False y = layer(x) frozen_model = Model(x, y) # 在下面的模型中,训练期间不会更新层的权重 frozen_model.compile(optimizer='rmsprop', loss='mse') layer.trainable = True trainable_model = Model(x, y) # 使用这个模型,训练期间 `layer` 的权重将被更新 # (这也会影响上面的模型,因为它使用了同一个网络层实例) trainable_model.compile(optimizer='rmsprop', loss='mse') frozen_model.fit(data, labels) # 这不会更新 `layer` 的权重 trainable_model.fit(data, labels) # 这会更新 `layer` 的权重
如何从 Sequential 模型中移除一个层?
你可以通过调用 .pop()
来删除 Sequential
模型中最后添加的层:
model = Sequential() model.add(Dense(32, activation='relu', input_dim=784)) model.add(Dense(32, activation='relu')) print(len(model.layers)) # "2" model.pop() print(len(model.layers)) # "1"
全连接网络
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)
参数
- units: 正整数,输出空间维度。
- activation: 激活函数 (activations)。 若不指定,则不使用激活函数 (即,「线性」激活:
a(x) = x
)。 - use_bias: 布尔值,该层是否使用偏置向量。
- kernel_initializer:
kernel
权值矩阵的初始化器 (initializers)。 - bias_initializer: 偏置向量的初始化器 (initializers).
- kernel_regularizer: 运用到
kernel
权值矩阵的正则化函数 (regularizer)。 - bias_regularizer: 运用到偏置向的的正则化函数 (regularizer)。
- activity_regularizer: 运用到层的输出的正则化函数 (它的 "activation")。 (regularizer)。
- kernel_constraint: 运用到
kernel
权值矩阵的约束函数 (constraints)。 - bias_constraint: 运用到偏置向量的约束函数 (constraints)。
输入尺寸
n维张量,size:(batch_size, ..., input_dim),最常见的情况是一个尺寸为(batch, input_dim)的二维输入。
输出尺寸
n维张量,size:(batch_size, ..., units),例如对于尺寸为 (batch, input_dim)的二维输入,输出尺寸为(datch_size, units)
Activation
keras.layers.Activation(activation)
参数
- activation: 要使用的激活函数的名称 (详见: activations), 或者选择一个 Theano 或 TensorFlow 操作。
输入尺寸和输出尺寸
任意尺寸。 当使用此层作为模型中的第一层时, 使用参数 input_shape
(整数元组,不包括样本数的轴)。
循环网络:简单RNN、LSTM和GRU
在代码仓库的 examples 目录中,你会找到更多高级模型:基于记忆网络的问答系统、基于栈式 LSTM 的文本生成等等。
卷积层和池化层
正则化
批归一化
预定义激活函数概述
Keras实现手写数字识别
MNIST是一个由美国由美国邮政系统开发的手写数字识别数据集。手写内容是0~9,一共有60000个图片样本,我们可以到MNIST官网免费下载,总共4个文件,该文件是二进制内容。
train-images-idx3-ubyte.gz: training set images (9912422 bytes) 图片样本,用来训练模型
train-labels-idx1-ubyte.gz: training set labels (28881 bytes) 图片样本对应的数字标签
t10k-images-idx3-ubyte.gz: test set images (1648877 bytes) 测试样本
t10k-labels-idx1-ubyte.gz: test set labels (4542 bytes) 测试样本对应的数字标签
我们下载下来的文件是.gz
后缀的,表明是一个压缩文件,我们设计代码的时候,需要考虑对文件进行解压。
训练数据集包含 60,000 个样本, 测试数据集包含 10,000 样本。在 MNIST 数据集中的每张图片由 28 x 28 个像素点构成, 每个像素点用一个灰度值表示。
Keras目标函数常见的一些选项
MSE:预测值和真实值指之间的均方误差
Binary cross-entropy:二分对数损失,该目标函数适合二元标签预测
$$-tlog(p)-(1-t)log(1-p)$$
Categorical cross-entropy:多元对数损失,该目标函数适合多分类预测,他也是与激活函数softmax相关联的默认选择
$$L_i=-\sum _jt_{t,j}log(p_{i,j})$$
Keras常见的性能估计指标
- Accuracy:准确率,针对预测目标的预测正确的比例
- Precision:查准率,衡量多分类问题中多少选择项是关联正确的
- Recall:查全率,衡量多分类问题中多少关联正确的数据被选出来
先用model.compile(lose, optimizer, metrics)编译模型
model.compile(loss='categorical_crossentropy', optimizer=SGD(), metrics=['accuracy'])
然后用model.fit()训练模型
model.fit(X_train, Y_train, batch_size, epochs, verbpose, validation_split)
改进神经网络的方法
增加隐层
dropout
DropOut是深度学习中常用的方法,主要是为了克服过拟合的现象。全连接网络极高的VC维,使得它的记忆能力非常强,甚至把一下无关紧要的细枝末节都记住,一来使得网络的参数过多过大,二来这样训练出来的模型容易过拟合。
DropOut:是指在在一轮训练阶段临时关闭一部分网络节点。让这些关闭的节点相当去去掉。如下图所示去掉虚线圆和虚线,原则上是去掉的神经元是随机的。
keras.layers.Dropout(rate, noise_shape=None, seed=None)
将 Dropout 应用于输入。
参数
- rate: 在 0 和 1 之间浮动。需要丢弃的输入比例。
- noise_shape: 1D 整数张量, 表示将与输入相乘的二进制 dropout 掩层的形状。 例如,如果你的输入尺寸为
(batch_size, timesteps, features)
,然后 你希望 dropout 掩层在所有时间步都是一样的, 你可以使用noise_shape=(batch_size, 1, features)
。 - seed: 一个作为随机种子的 Python 整数。
选择更好的优化器
SGD、RMSprop、Adam
增加训练轮次
优化器的学习率
增加内部隐藏神经元的数量
增加批处理的大小
采用正则化方法避免过拟合
机器学习中有3种不同类型的正则化方法
- L1正则化(也称为 lasso):模型复杂度表示为权重的绝对值
- L2正则化(也称为 ridge):模型复杂度表示为权重的平方和
- 弹性网络正则化:模型复杂度通过联合前述两种技术捕捉
L1正则化惩罚项: $Lose = {Lose}_0 + \frac{\lambda }{n}\sum_{w}|w|$
L2正则化惩罚项: $Lose = {Lose}_0 + \frac{\lambda }{2n}\sum_{w}|w^2|$
我们对损失方式添加了正则化惩罚项来进行改造。加上正则化惩罚项能在一定程度上避免过拟合。
前一部分的损失函数叫做“经验风险”,经验风险就是由于拟合结果和样本标签之间的残差总和所产生的这种经验差距所带来的风险。
后一部分的损失函数叫做“结构风险”,结构风险是因为我们希望这种描述能够简洁来保证其泛化性的良好。
注意λλ不是指学习率,而是一个权重,也可以称为正则化系数,表示对后面这部分的“重视程度”。
举个例子,比如我们两个模型的输入权重系数为下所示,我们使用L2正则化惩罚系数,为了方便我们只计算“结构风险”
$$W_1=\begin{pmatrix}1\\ 0\\ 0\\ 0\end{pmatrix}
W_2=\begin{pmatrix}\frac{1}{4}\\ \frac{1}{4}\\ \frac{1}{4}\\ \frac{1}{4}\end{pmatrix}$$
$$\sum W_1^2=[1,0,0,0]^2=1^2+0^2+0^2+0^2=1$$
$$\sum W_2^2=[\frac{1}{4},\frac{1}{4},\frac{1}{4},\frac{1}{4}]^2={\frac{1}{4}}^2+{\frac{1}{4}}^2+{\frac{1}{4}}^2+{\frac{1}{4}}^2={\frac{1}{4}}$$
由上可见,模型2的结构风险比模型1的小,所以模型2的损失函数小,模型2的泛化能力更强。这就是正则化的好处。
相同的正则化方案可以独立地应用于权重、模型和激活函数
model.add(Dense(64, input_dim=64, kernel_regularizer= regularizers.l2(0.01)))
超参数
超参数是指那些在机器学习算法中训练前设定的一些初始化参数,这些参数没法通过算法本身来学习。比如:$W_0$,$b_0$,还有学习率$\eta $
在搭建神经网络的时候,我们有件事情不得不做,那就是参数w、b等的初始化,业界来说,把整个网路中所有的w初始化成以0为均值、以一个很小的$\mu $为标准差的正态分布的方式会比较好。
最常见的是以0为均值、1为方差的正态分布来随机初始化。
还有一种常见的初始化方法是以0为均值、1为方差的正态分布生成后除以当前层的神经元个数的算术平方来获得初始化。
如果你非要问为什么,思路大致是这样的:对于一个模型中输入权重的设置相当于一种重视程度,而一个模型中需要我们非常重视或者不怎么重视的输入一定是少数,而其他大部分输入的信息可能就比较中庸,对判断结果影响比较小的因素的信息比较多。
说来也有意思,自然界中大部分数据的统计分布都服从高斯分布的特点。
输出预测
当模型训练好之后,就可以用于测试了。
predictions = model.predict(X)
对于给定的输入,我们可以计算几种类型的输出:
model.evaluate() :用于计算损失值
model.predict_classes() :用于计算输出类别
model.predict_proba() :用于计算类别概率