final_keras

畅游人工智能之海--Keras教程之Sequential模型篇

各位读者大家好,今天我们就要开始正式地进行Keras的学习了。相信大家都读过了Keras的知识结构那篇文章,我们把Keras的模型讲解放在了最前面。这样布局是为了让大家先对Keras实现神经网络的整体架构了然于胸,在之后的学习中便可以找到其他的零碎知识点在框架中的位置,更有利于大家对Keras的学习。废话不多说,下面我们就开始吧!

Sequential模型,顾名思义,它是一个线性模型,即多个网络层进行线性堆叠构成Sequential模型。Sequential模型的构造方法有两种:一种是将网络层实例的列表传递给Sequential的构造器来创建一个Sequential模型;另一种是使用.add()方法将各层添加到模型中。

1
2
3
4
5
6
7
8
9
10
11
#方法一
model = Sequential([
Dense(32, input_shape=(784,)),
Activation('relu'),
Dense(10),
Activation('softmax'),
])
#方法二
model = Sequential()
model.add(Dense(32, input_dim=784))
model.add(Activation('relu'))

这样,一个简单的模型就搭好了。但是这样的模型是没有经过训练的,无法完成我们想要的功能。若是想对模型进行训练,我们还需要选择优化器、loss函数、评估标准等,使用compile方法对模型进行配置。之后才能使用训练数据对模型进行训练。

在这之前,模型需要知道它所期望的输入的尺寸(只需要告诉第一层输入尺寸信息,之后的层会自行推导。有以下几种方法完成这一任务:①使用input_shape参数传递给第一层,它是一个表示尺寸的元组;②某些2D层支持通过参数input_dim输入尺寸,某些3D时序层支持input_dim和input_length参数。如果想要为输入指定固定的batch大小,可以传递batch_size参数给一个层。下面两行代码是等价的。

1
2
model.add(Dense(32, input_shape=(784,)))
model.add(Dense(32, input_dim=784))

上面提到,我们在训练模型之前需要用compile方法对学习过程进行配置,基本的代码如下:

1
2
3
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])

optimizer就是优化器,loss就是损失函数,metrics就是评估标准。通过输入等号后面的内容可以针对不同的训练任务进行不同的选择。

在完成配置过程之后,我们便可以用fit方法对模型进行训练。代码如下:

1
model.fit(data, labels, epochs=10, batch_size=32)

其中data就是训练集,labels是对应的标签,epochs是训练轮数,batch_size是批大小。

除了上面提到的这些,Sequential模型还有一些其他的API(具体参数含义可以查看Keras文档):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#根据名称(唯一)或索引值查找网络层,如果同时提供了 name 和 index,则 index 将优先。
get_layer(name=None, index=None)
#在测试模式,返回误差值和评估标准值
evaluate(x=None, y=None, batch_size=None, verbose=1, sample_weight=None, steps=None)
#为输入样本生成输出预测
predict(x, batch_size=None, verbose=0, steps=None)
#一批样品的单次梯度更新
train_on_batch(x, y, sample_weight=None, class_weight=None)
#在一批样本上评估模型
test_on_batch(x, y, sample_weight=None)
#返回一批样本的模型预测值
predict_on_batch(x)
#使用 Python 生成器或 Sequence 实例逐批生成的数据,按批次训练模型
fit_generator(generator, steps_per_epoch=None, epochs=1, verbose=1, callbacks=None, validation_data=None, validation_steps=None, class_weight=None, max_queue_size=10, workers=1, use_multiprocessing=False, shuffle=True, initial_epoch=0)
#在数据生成器上评估模型
evaluate_generator(generator, steps=None, max_queue_size=10, workers=1, use_multiprocessing=False, verbose=0)
#为来自数据生成器的输入样本生成预测
predict_generator(generator, steps=None, max_queue_size=10, workers=1, use_multiprocessing=False, verbose=0)

接下来,我们来结合一个实例来梳理一下Sequential模型整体流程,以基于多层感知器的二分类为例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Dropout

# 生成虚拟数据
x_train = np.random.random((1000, 20))
y_train = np.random.randint(2, size=(1000, 1))#训练集标签
x_test = np.random.random((100, 20))
y_test = np.random.randint(2, size=(100, 1))#测试集标签
#Sequential构造器,采取.add()方式构造模型
model = Sequential()
#第一层要指定输入尺寸,这里输入尺寸为一个20维的向量
model.add(Dense(64, input_dim=20, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(1, activation='sigmoid'))
#配置模型的学习过程。选择的优化器为'rmsprop',损失函数为'binary_crossentropy',评估标准为'accuracy'
model.compile(loss='binary_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
#开始用训练集训练模型,轮数为20,批大小为128
model.fit(x_train, y_train,
epochs=20,
batch_size=128)
#用测试集评估模型并打分
score = model.evaluate(x_test, y_test, batch_size=128)

以上就是一个基本流程,关键点在注释里标注。

接下来大家会想,我们辛辛苦苦训练好了一个模型难道程序结束就没有了么?所以我们要采取一些方法保留模型,Keras提供了一些方法。

最完整的保存方法为model.save(filepath),这个方法将Keras模型保存到单个HDF5文件中,该文件包括:模型的结构,允许重新创建模型;模型的权重;训练配置项(损失函数,优化器);优化器状态,允许准确地从你上次结束的地方继续训练。之后还可以使用keras.models.load_model(filepath)重新实例化模型。还有只保存或加载模型的结构的方法:model.to_json(),model.to_yaml();model_from_json(json_string),model_from_yaml(yaml_string)。还有只保存或加载模型的权重的方法:model.save_weights(filepath);model.load_weights(filepath)

相信如果你能掌握这篇文章的内容,就可以自行构造一个Sequential模型啦!关于优化器等等关键参数的选择背后也有很深的学问,这关系到大家处理具体问题时的选择,希望大家在这篇文章之外多多去探索他们背后的学问,一起加油吧!

Model模型

用Sequential只能定义一些简单的模型,如果你想要定义多输入、多输出以及共享网络层,就需要使用Model模型了。

声明方法

1
2
3
4
5
inputs = Input(shape=(784,))
x = Dense(64, activation='relu')(inputs)
x = Dense(64, activation='relu')(x)
out = Dense(10, activation='softmax')(x)
model = Model(inputs=inputs, outputs=out)

在model模型的声明中,需要使用\(y = layer(...)(x)\)这样的格式来构建没一个层次,并在构造函数中声明你的模型的输入和输出是什么。


多输入多输出

来考虑下面的模型。我们试图预测 Twitter 上的一条新闻标题有多少转发和点赞数。模型的主要输入将是新闻标题本身,即一系列词语,但是为了增添趣味,我们的模型还添加了其他的辅助输入来接收额外的数据,例如新闻标题的发布的时间等。 该模型也将通过两个损失函数进行监督学习。较早地在模型中使用主损失函数,是深度学习模型的一个良好正则方法。

模型结构如下图所示:

multi-input-multi-output-graph
1
2
3
4
5
from keras.layers import Input, Embedding, LSTM, Dense
from keras.models import Model
main_input = Input(shape=(100,), dtype='int32', name='main_input')
x = Embedding(output_dim=512, input_dim=10000, input_length=100)(main_input)
lstm_out = LSTM(32)(x)
1
auxiliary_output = Dense(1, activation='sigmoid', name='aux_output')(lstm_out)
1
2
3
4
5
6
7
8
auxiliary_input = Input(shape=(5,), name='aux_input')
x = keras.layers.concatenate([lstm_out, auxiliary_input]) # 共享层,下面介绍

x = Dense(64, activation='relu')(x)
x = Dense(64, activation='relu')(x)
x = Dense(64, activation='relu')(x)

main_output = Dense(1, activation='sigmoid', name='main_output')(x)
1
model = Model(inputs=[main_input, auxiliary_input], outputs=[main_output, auxiliary_output])

共享网络层

多输入依赖于共享,直接看例子。

1
2
3
4
5
6
import keras
from keras.layers import Input, LSTM, Dense
from keras.models import Model

tweet_a = Input(shape=(280, 256))
tweet_b = Input(shape=(280, 256))

要在不同的输入上共享同一个层,只需实例化该层一次,然后根据需要传入你想要的输入即可:

1
2
3
4
5
6
shared_lstm = LSTM(64)
encoded_a = shared_lstm(tweet_a)
encoded_b = shared_lstm(tweet_b)
merged_vector = keras.layers.concatenate([encoded_a, encoded_b], axis=-1)
predictions = Dense(1, activation='sigmoid')(merged_vector)
model = Model(inputs=[tweet_a, tweet_b], outputs=predictions)

总结

在Model模型中,多输入、多输出、共享的实现都是简单的,只要按照一定的逻辑创建好有向图即可!

关于本文内容,部分借鉴自keras文档。

实验代码见https://github.com/1173710224/keras-cnn-captcha.git的model-example分支。

核心网络层(一)

各位读者大家好,上周我们已经详细讲解了Keras的两种模型,相信大家经过学习已经对Keras构建神经网络的两种方式有了一个清晰的认识。那么明确了网络结构之后,我们该选择什么网络层添加进神经网络里面呢?这就需要我们对网络层的作用进行细致的学习了。我们计划从这周开始进入对网络层的学习当中,那么今天,笔者便要向大家详细的介绍核心网络层的类别及功能,让我们一起学习吧!

Dense层
1
2
3
4
5
6
7
8
9
10
11
12
13
keras.layers.Dense(
units, #正整数,输出空间维度
activation=None, #激活函数
use_bias=True, #布尔值,该层是否使用偏置向量
kernel_initializer='glorot_uniform', #kernel 权值矩阵的初始化器
bias_initializer='zeros', #偏置向量的初始化器
kernel_regularizer=None, #运用到 kernel 权值矩阵的正则化函数
bias_regularizer=None, #运用到偏置向的的正则化函数
activity_regularizer=None, #运用到层的输出的正则化函数
kernel_constraint=None, #运用到 kernel 权值矩阵的约束函数
bias_constraint=None #运用到偏置向量的约束函数
#初始化器、正则化函数和约束函数在后文会单独来讲
)

作用:

将输入的特征在Dense层中经过非线性变化,提取这些特征之间的关联,最后映射到输出空间上,减少特征位置对分类带来的影响。实现的操作为:output = activation(dot(input, kernel) + bias) ,其中 activation 是按逐个元素计算的激活函数,kernel 是由网络层创建的权值矩阵,以及 bias 是其创建的偏置向量 (只在 use_biasTrue 时才有用)。如果输入的秩大于2,那么输入首先被展平然后再计算与kernel 的点乘

输入尺寸:nD张量,当使用Dense层作为第一层时,使用input_shape(整数元组)指定输入大小。

输出尺寸:nD张量,输出尺寸为(batch_size,units)

例子:

1
2
3
#Dense不作为第一层时无需input_shape参数
#输入尺寸为(None,16),输出尺寸为(None,32)
model.add(Dense(32, input_shape=(16,)))
Activation层
1
2
3
keras.layers.Activation(
activation#要使用的激活函数名称或者选择一个 Theano 或 TensorFlow 操作。
)

功能:对输入应用激活函数并输出

输入尺寸:任意大小,当使用Activation层作为第一层时,使用input_shape(整数元组)指定输入大小。

输出尺寸:与输入相同。

例子:

1
2
#使用tanh激活函数
model.add(Activation('tanh'))
Dropout层
1
2
3
4
5
keras.layers.Dropout(
rate, #将输入单元的值按比率rate随机设置为 0
noise_shape=None, #1D整数张量,表示将与输入相乘的二进制dropout的形状。
seed=None#一个作为随机种子的Python整数
)

功能:在前向传播的时候,让某个神经元的激活值以一定的概率p停止工作,将输入单元的值按比率随机设置为 0,有效防止过拟合。

输入大小:任意大小。

输出大小:与输入一致。

例子:

1
2
#将输入单元的值按0.02的概率设置为0
model.add(Dropout(0.02))
Flatten层
1
2
3
keras.layers.Flatten(
data_format=None#一个字符串,值为channels_last或者channels_first,表示输入维度展平时保留权重的顺序
)

功能:把输入展平成一维化输出,常用在从卷积层到全连接层的过渡,不影响batch的大小。

输入:任意尺寸。

输出:将输入尺寸的各维大小相乘得到的值即为输出尺寸。

例子:

1
2
3
4
5
# 一开始的输入尺寸为(None, 64, 32, 32)
# None为批大小
model.add(Conv2D(64, (3, 3),input_shape=(3, 32, 32)))
# 展平后输出尺寸为(None, 65536)
model.add(Flatten())
Input层
1
2
keras.engine.input_layer.Input()
#用于第一层时参数为shape,指定输入尺寸

功能:实例化Keras张量,一般用于网络的第一次

输入尺寸:任意大小,用于第一层时参数为shape,指定输入尺寸。

输出尺寸:跟输入尺寸一致。

例子:

1
2
#实例化一个Keras张量,一般用于构建Model类模型
x = Input(shape=(32,))
Reshape层
1
2
3
keras.layers.Reshape(
target_shape#整数元组,表示要把输入调整成的目标尺寸
)

功能:根据给定的参数将输入调整为指定的尺寸

输入尺寸:任意大小,当使用Reshape层作为第一层时,使用input_shape(整数元组)指定输入大小。

输出尺寸:(batch_size,) + 指定的尺寸。

例子:

1
2
3
4
5
6
7
8
#不是第一层时无需导入input_shape=()
#把输入尺寸变为(None,3,4)
# `None` 是批表示的维度
model.add(Reshape((3, 4), input_shape=(12,)))
#可以通过-1来表示维度的尺寸推断
#即程序可以由输入尺寸(None,3,4)推断出-1处的值应为3,因为3*4=3*2*2
#输出尺寸为(None,3,2,2)
model.add(Reshape((-1, 2, 2)))
Permute层
1
2
3
keras.layers.Permute(
dims#整数元组。表示哪几个维度进行置换,索引从1开始,(2,1)表示置换输入的第一个和第二个维度。
)

功能:根据给定的参数置换输入的维度,在某些场景下很有用,例如将RNN和CNN连接在一起。

输入尺寸:任意大小,当使用Permute层作为第一层时,使用input_shape(整数元组)指定输入大小。

输出尺寸:(batch_size,)+输入尺寸经过指定置换后的尺寸。

例子:

1
2
3
4
5
#可以看到置换模式为把输入的第一维和第二维进行置换
#输入尺寸为(None,10,64),所以输出尺寸为(None,64,10)
#不是第一层时无需导入input_shape=()
# `None` 是批表示的维度
model.add(Permute((2, 1), input_shape=(10, 64)))

相信大家经过今天的学习,能够对核心网络层的一部分的类型和功能有一个清晰的认知,明天的文章将带领大家一起学习核心网络层剩下的部分,让我们一起期待吧!

核心网络层(二)

RepeatVector

  • keras.layers.RepeatVector(n)

  • 将该层的输入重复n次

  • 将二维的输入变成三维的,形式化的表述就是\((None,features) -> (None,n,features)\)

1
2
3
model = Sequential()
model.add(Dense(32, input_dim=32)) # model.output_shape == (None, 32)
model.add(RepeatVector(3)) # model.output_shape == (None, 3, 32)

Lambda

  • keras.layers.Lambda(function, output_shape=None, mask=None, arguments=None)
  • 将一个表达式包装为layer对象
1
model.add(Lambda(lambda x: x ** 2)) # 添加了一个层,将所有的x变成x^2
  • 常用的参数就是前两个,第一个指定要对输入进行什么样的操作,第二个对输出的shape进行检查

  • 可以支持任何类型的输入,但是如果是model的第一层需要指定input_shape

ActivityRegularization

  • L1和L2正则化能够有效地避免过拟合,keras将这两个正则化封装在了这个层中(ps:关于两个正则化,可以到 https://www.jianshu.com/p/c9bb6f89cfcc 这个博客学习)
  • keras.layers.ActivityRegularization(l1=0.0, l2=0.0)
  • 该层的输入输出的维度相同,因为正则化不影响维度

Masking

  • 将训练数据中的某些值跳过

  • 一个官方给出的例子是:

    • 考虑一组将要传给LSTM的数据 (samples, timesteps, features)
    • set x[0, 3, :] = 0. and x[2, 5, :] = 0.
    • 并将这个 mask_value=0.Masking层插入到LSTM之前
1
2
3
model = Sequential()
model.add(Masking(mask_value=0., input_shape=(timesteps, features))) # 作为第一层需要指定input_shape
model.add(LSTM(32)) # 第0个样本在第三个时间步会被跳过,第二个同理

SpatialDropout1(2)(3)D

  • 原理与dropout相同
  • 官方给出的解释是,在此版本中,三个layer的作用与dropout相同,都是以一定的概率保持连接

基础卷积层

本讲提要

这次结合代码跟大家分享四种卷积:一维卷积,二维卷积,以及基于它们的可分离卷积。

一维卷积

keras接口

1
keras.layers.Conv1D(filters, kernel_size, strides=1, padding='valid', data_format='channels_last', dilation_rate=1, 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)

其中的regularizer和constraint会在后面进行详细讲解,可以先不用

一维卷积的过程

1
layers.Conv1D(filters=2, kernel_size=4, use_bias=False)

以上面这个卷积层为例,它的工作过程如下:

con1d

在这个例子中,我们对一个全1的输入进行卷积操作,如图中所示的操作应该进行两次,因为我们指定的filters = 2

所以输入和输出的shape应该是

1
2
3
4
5
6
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer) (None, 10, 3) 0
_________________________________________________________________
conv1d_1 (Conv1D) (None, 7, 2) 24
=================================================================

其它参数的含义

  • strides 指的是卷积核每次滑动的长度,图中卷积核每次滑动一格,默认值是1

  • padding 指是不是要对结果进行扩充,它的默认取值是‘valid’,默认情况下不产生任何变化,还可以取'causal'和'same',以增强对边缘数据的特征提取,处理之后的shape如下所示

    1
    2
    3
    4
    5
    6
    7
    _________________________________________________________________
    Layer (type) Output Shape Param #
    =================================================================
    input_1 (InputLayer) (None, 10, 3) 0
    _________________________________________________________________
    conv1d_1 (Conv1D) (None, 10, 2) 24
    =================================================================
  • data_format 没什么用,直接用默认值就行了,这个变量是在规定每个输入的含义,它有两个取值’channels_last‘和’channels_first‘,默认是第一个,也就是默认的输入是 (batch, steps, channels) 这样的,而第二个种对应的输入应该是 (batch, channels, steps) 这样的。

  • dilation_rate 会对卷积核进行扩充,但是不改变原始的卷积核

二维卷积

keras接口

1
keras.layers.Conv2D(filters, kernel_size, strides=(1, 1), padding='valid', data_format=None, dilation_rate=(1, 1), 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)

与一维相比发生的变化

  • input 单条数据从二维变成三维,如图所示展示的是一个3通道(上下)的二维数据

    picture2d
  • kernel_size 应该是一个二元组,这个二元组指的是在一个通道内对数据的卷积

可分离卷积

keras接口

1
keras.layers.SeparableConv1D(filters, kernel_size, strides=1, padding='valid', data_format='channels_last', dilation_rate=1, depth_multiplier=1, activation=None, use_bias=True, depthwise_initializer='glorot_uniform', pointwise_initializer='glorot_uniform', bias_initializer='zeros', depthwise_regularizer=None, pointwise_regularizer=None, bias_regularizer=None, activity_regularizer=None, depthwise_constraint=None, pointwise_constraint=None, bias_constraint=None)# 这是一维的接口,二维的类似,就不占位置了

发生的变化

用不同的卷积核在各个通道上进行卷积,然后再用一个卷积核将不同的通道合并

depth_multiplier 可以把一个通道卷积之后的结果变成多个通道,其实也就是多次卷积,但是需要注意的是整个卷积层最后输出的shape不变

depthwise

keras接口

1
keras.layers.DepthwiseConv2D(kernel_size, strides=(1, 1), padding='valid', depth_multiplier=1, data_format=None, dilation_rate=(1, 1), activation=None, use_bias=True, depthwise_initializer='glorot_uniform', bias_initializer='zeros', depthwise_regularizer=None, bias_regularizer=None, activity_regularizer=None, depthwise_constraint=None, bias_constraint=None)

解释

其实就是可分离卷积的第一步,keras的制作者把它单独提出来做成了一个接口

ending

卷积层还有几个操作,下周二分析

在这里附上实验代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from keras import layers
from keras.models import Model
import keras
input_num = 10
keras.backend.clear_session()
inputs = layers.Input((input_num, 3)) # 定义输入层
# out = layers.Conv1D(filters=2, kernel_size=4, padding='same',use_bias=False)(inputs)
out = layers.SeparableConv1D(filters=2,kernel_size=4,use_bias=False, depth_multiplier=1)(inputs)
model = Model(inputs=inputs, outputs=out)
model.summary()
print(model.get_weights())
lis = list(model.get_weights()[0])
print(sum(lis[0]),sum(lis[1]))
x = [[[1,1,1] for i in range(input_num)]]
import numpy as np
x = np.array(x)
print(model.predict(x))

三维卷积

本讲提要

接着上次的内容,继续说说keras的卷积

  • 三维卷积Conv3D
  • 转置卷积ConvTranspose
  • 裁剪层Cropping
  • 零填充层ZeroPadding

Conv3D

1
keras.layers.Conv3D(filters, kernel_size, strides=(1, 1, 1), padding='valid', data_format=None, dilation_rate=(1, 1, 1), 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)

不同维数的卷积区别在于输入层、卷积核以及输出层的维数不同。

3dconv

图1 三维卷积[1]

转置卷积

对转置卷积的需求通常来自于使用与普通卷积相反方向的转换的愿望,即,从具有某些卷积输出形状的东西到具有其输入形状的东西,同时保持与上述卷积兼容的连接模式。可以简单地理解为把卷积结果还原成卷积前的矩阵。

转置卷积也被称为分数步长卷积,下图是一个stride = 1/2 的示意图,蓝色部分是输入,蓝绿色矩阵是输出,灰色是卷积核。

padding_strides_transposed

图二 Conv2DTranspose with padding,stride[2]

1
keras.layers.Conv2DTranspose(filters, kernel_size, strides=(1, 1), padding='valid', output_padding=None, data_format=None, dilation_rate=(1, 1), 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)

裁剪层

对输入做裁剪的层

1
Cropping2D:keras.layers.Cropping2D(cropping=((0, 0), (0, 0)), data_format=None)

cropping后的两个tuple被解释为: ((top_crop, bottom_crop), (left_crop, right_crop))

上采样层

上采样指的是任何可以让图像变成更高分辨率的技术。UpSampling2D可以看作是Pooling的反向操作,就是采用最近邻插值来对图像进行放大。upsampling2D可用于应对一些需要大尺寸图片来作为输入的任务,比如GAN对抗网络。

UpSampling2D

1
keras.layers.UpSampling2D(size=(2, 2), data_format=None, interpolation='nearest')

##零填充层

ZeroPadding用于为矩阵周围填充0

1
keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)

2D 输入的零填充层(例如图像)。该层可以在图像张量的顶部、底部、左侧和右侧添加零表示的行和列。

[1]三维卷积: http://inhi.kim/archives/1146

[2]转置卷积: https://github.com/vdumoulin/conv_arithmetic

循环神经网络(一)

RNN是啥?

循环神经网络是具有内部存储器的前馈神经网络的概括。RNN本质上是递归的,因为它对数据的每个输入执行相同的操作,而当前输入的输出取决于过去的一次计算。产生输出后,将其复制并发送回循环网络。为了做出决定,它会考虑当前输入和从先前输入中学到的输出,具体的过程如图所示。 rnn.png 对应的计算公式可以表述为: \[ h_t = f(h_{t - 1},x_t) \] 从计算公式中我们也能够看到,循环神经网络被开发的意义所在。一个具体的例子(加了激活函数和神经网络参数)就是: \[ h_t = tanh(W_{h} \cdot h_{t - 1} + W_x \cdot x_t) \]

pros and cons

  1. RNN可以对数据序列进行建模,因此可以假定每个样本都依赖于先前的样本
  2. 循环多了长期的知识就循环没了(梯度消失)

keras 实现

SimpleRNN

1
keras.layers.SimpleRNN(units, activation='tanh', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, dropout=0.0, recurrent_dropout=0.0, return_sequences=False, return_state=False, go_backwards=False, stateful=False, unroll=False)

units:输出维度。

dropout: dropout 是指在深度学习网络的训练过程中,按照一定的概率将一部分神经网络单元暂时从网络中丢弃,相当于从原始的网络中找到一个更瘦的网络,如图所示。在这里就是一个概率值,取值[0,1]。

1585941305922 摘自https://www.jianshu.com/p/b5e93fa01385

recurrent_drop:同理,因为这里有两个输入所以就有两个dropout。 return_sequences: 是返回输出序列中的最后一个输出(False)还是完整序列(True)。 return_state: 除输出外,是否返回最后一个状态。 go_backwards:如果为True,则向后处理输入序列,并返回相反的序列。 stateful:如果为True,则将批次中索引为 i 的每个样本的最后状态用作下一个批次中索引为 i 的样本的初始状态。 unroll:如果为True,则将展开网络,否则将使用符号循环。 展开可以加快RNN的速度,尽管它往往会占用更多的内存。 展开仅适用于短序列。

SimpleRNN‘s example

1
2
3
4
5
6
7
8
9
from keras.layers import SimpleRNN
import keras
from keras.models import Model
x = keras.Input((None, 2))
layer = SimpleRNN(3,use_bias=False) # 可以在这里声明参数
y = layer(x)
model = Model(inputs = x,outputs = y)
model.summary()
print(layer.get_weights())

直接运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) (None, None, 2) 0
_________________________________________________________________
simple_rnn_1 (SimpleRNN) (None, 3) 15
=================================================================
Total params: 15
Trainable params: 15
[array([[ 0.1444943 , 0.14509082, 0.8174648 ],这个数组是处理input的矩阵
[-0.00633633, -0.9820774 , -0.53582764]], dtype=float32),
array([[-0.8286751 , 0.00360382, -0.5597184 ],这个数组是处理上次输出的矩阵
[-0.12797575, -0.9747111 , 0.18319504],
[-0.5449035 , 0.22343954, 0.80818003]], dtype=float32)]

about return_sequences

1
2
3
4
5
6
7
8
layer = SimpleRNN(3,use_bias=False,return_sequences=True)
y = layer(x)
model = Model(inputs = x,outputs = y)
X = [[[1,2],[2,3]]]
print(model.predict(np.array(X)))
output:
[[[0.93978536 0.7007202 0.23164003]
[0.9509662 0.811069 0.53194606]]]
1
2
3
4
5
6
7
layer = SimpleRNN(3,use_bias=False,return_sequences=False)
y = layer(x)
model = Model(inputs = x,outputs = y)
X = [[[1,2],[2,3]]]
print(model.predict(np.array(X)))
output:
[[ 0.9331477 -0.32495558 -0.2831148 ]]

about return_state

1
2
3
4
5
return_state=False:
[[-0.6329196 -0.95722115 0.35948145]]
return_state=True:
[array([[ 0.26201633, -0.9927976 , 0.9126009 ]], dtype=float32),
array([[ 0.26201633, -0.9927976 , 0.9126009 ]], dtype=float32)]在这里输出就是最后一个状态

循环神经网络(二) | GRU和LSTM

开篇

图示对比

RNN

rnn

LSTM

lstm

GRU

gru

优化的点

在RNN中,随着时间线的延长,距离目前时间比较远的数据所包含的信息会被衰减殆尽,举个例子

“I grew up in France… I speak fluent .” 我们现在想判断出 处应该是什么,我们直观地判断结果应该是Franch,判断的依据是前面有个France,可以认为France是\(x_4\)的数据,我们要推断的是\(x_t(t \geq 8)\)的数据。对RNN而言,这两个单词离得太远了,因此解决不了。

LSTM

本质上增加了存储的信息量,可以认为\(C_t\)中保留了从开始到现在所有需要的信息。这是长期知识,然后通过与RNN中已经提供的短期知识结合,共同判断结果。细致的讲解可以参看下面的博客。

https://www.jianshu.com/p/95d5c461924c

GRU

效果跟LSTM差不多,但是节省了更多的计算资源,因为没有细胞状态了。细致原理参看下面的文章。

https://zhuanlan.zhihu.com/p/32481747

keras实现

LSTM

1
2
keras.layers.LSTM(units, activation='tanh', recurrent_activation='sigmoid', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', unit_forget_bias=True, kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, dropout=0.0, recurrent_dropout=0.0, implementation=2, return_sequences=False, return_state=False, go_backwards=False, stateful=False, unroll=False)

具体参数的含义在中基本ref都已经介绍过了,这里新增的两个activation通过默认值可以直接与lstm的结构对应上。

GRU

1
keras.layers.GRU(units, activation='tanh', recurrent_activation='sigmoid', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, dropout=0.0, recurrent_dropout=0.0, implementation=2, return_sequences=False, return_state=False, go_backwards=False, stateful=False, unroll=False, reset_after=False)

参考

https://baijiahao.baidu.com/s?id=1639105801622260740&wfr=spider&for=pc

图片摘自https://www.jianshu.com/p/95d5c461924c

循环神经网络(三) | LSTM

什么作用

如果用一句话总结LSTM的作用,它可以用来处理时序数据;如果用一句话总结卷积的作用,它用来处理空间数据。那如果是音频呢,图片随着时间变化,那我们就会想基于卷积和LSTM的结合来解决这个问题。

什么样子

1587168739629 1587168767734在处理图像时,我们通常先将数据处理成一维向量,这个过程一般通过卷积来完成,在得到了图像的一维卷积之后,我们就可以将数据接在LSTM上了。因此LSTM的结构并没有本质改变,一个表示卷积LSTM的cell如图所示。

1587169243502

keras实现

1
keras.layers.ConvLSTM2D(filters, kernel_size, strides=(1, 1), padding='valid', data_format=None, dilation_rate=(1, 1), activation='tanh', recurrent_activation='hard_sigmoid', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', unit_forget_bias=True, kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, return_sequences=False, go_backwards=False, stateful=False, dropout=0.0, recurrent_dropout=0.0)

layer的参数还是卷积和LSTM的参数,并不难理解。

无监督学习的应用

在moving-mnist数据集上对序列信息进行预测。

数据集地址:http://www.cs.toronto.edu/~nitish/unsupervised_video/mnist_test_seq.npy

代码地址:http://www.cs.toronto.edu/~nitish/unsupervised_video/unsup_video_lstm.tar.gz

在论文 Unsupervised Learning of Video Representations using LSTMs 中对该任务进行了详细描述。

http://www.cs.toronto.edu/~nitish/unsup_video.pdf

循环神经网络(四) | 自制循环神经网络

写在开篇

什么是Cell呀?Cell可以理解为循环神经网络的一个单元,如图所示。如果一个网络被定义为 \[ \begin{equation} h_t = \mathcal{F}(h_{t - 1},X_t) \end{equation} \] 那么\(\mathcal{F}\)就是这篇文章要讲的Cell。

1587168739629

换言之,Keras中提供了自定义循环神经网络中的每个计算单元的接口,接下来就结合官方文档给出的一个例子进行分析。

API

自定义下面的代码已经调试过,可直接运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from keras.layers import *
from keras import backend as K
class MinimalRNNCell(Layer):
def __init__(self, units, **kwargs):
self.units = units # output size
self.state_size = units
# 使用方法可以看这个博客 https://blog.csdn.net/qq_38787214/article/details/87902291
super(MinimalRNNCell, self).__init__(**kwargs) # 调用父类的构造函数

def build(self, input_shape):
self.kernel = self.add_weight(shape=(input_shape[-1], self.units),
initializer='uniform',
name='kernel')
self.recurrent_kernel = self.add_weight(
shape=(self.units, self.units),
initializer='uniform',
name='recurrent_kernel')
self.built = True

def call(self, inputs, states):
prev_output = states[0]
h = K.dot(inputs, self.kernel)
output = h + K.dot(prev_output, self.recurrent_kernel)
return output, [output]


import keras
cell = MinimalRNNCell(32)
x = keras.Input((None, 5))
layer = RNN(cell)
y = layer(x)

from keras.models import Model
model = Model(inputs=x,outputs=y)
model.summary()

其中MinimalRNNCell继承Layer类,是一个实现的Cell,按照官网的说法,call函数是必须实现的,也就是\(\mathcal{F}\) 真正做的事情,其中输入输出分别是 (input_at_t, states_at_t)(output_at_t, states_at_t_plus_1)state_sizeoutput_size也必须指明,在实现时units通常代表output_sizebuild函数在这里不是必须的,它本来是Layer类的函数,在这里override了。

封装Cell类

除了可以自定义之外,keras也提供了已经封装好了的Cell类,比如:ConvLSTM2DCellSimpleRNNCellGRUCellLSTMCell。这些类的参数就不再显示在这里了,因为他们跟自己对应的Layer的参数一摸一样,举一个例子,如下所示。

1
2
3
4
# 先是卷积LSTM的层的声明
keras.layers.ConvLSTM2D(filters, kernel_size, strides=(1, 1), padding='valid', data_format=None, dilation_rate=(1, 1), activation='tanh', recurrent_activation='hard_sigmoid', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', unit_forget_bias=True, kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, return_sequences=False, go_backwards=False, stateful=False, dropout=0.0, recurrent_dropout=0.0)
# 然后是卷积LSTMCell的声明
keras.layers.ConvLSTM2DCell(filters, kernel_size, strides=(1, 1), padding='valid', data_format=None, dilation_rate=(1, 1), activation='tanh', recurrent_activation='hard_sigmoid', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', unit_forget_bias=True, kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, dropout=0.0, recurrent_dropout=0.0)

既然参数一样,为什么还要制作两个接口呢?其实在使用cell创建RNN层时,可以一次传入多个cell,这样的Layer叫stacked RNN,比如:

1
2
3
4
cells = [MinimalRNNCell(32), MinimalRNNCell(64)]
x = keras.Input((None, 5))
layer = RNN(cells)
y = layer(x)

而通常封装的API都是一个Cell的。因此,分别给出来有一定的意义。

最后

在循环神经网络部分,总共有三种神经网络,分别是RNN,GRU,LSTM,这些都用来处理时间序列的数据。除此之外还有在gpu下使用的版本,在keras中的名字就是CuDNN。

循环神经网络(五) | 双向循环神经网络

写在开篇

在keras的循环层\(GRU\)\(LSTM\)等中的keras声明中,有一个叫做\(go\_backwards\)的参数。参数的取值为\(true\)或者\(false\)。如果取值为\(true\),输入的序列数据就会被反向处理,并且默认是\(false\),也就是正向处理数据。举个例子,比如\(RNN\)的输入是\(a,b,c,d,e\),如果\(go\_backwards=true\),输入就是\(e,d,c,b,a\)\(go\_backwards\)参数的设置具有一定的意义。

\(go\_backwards\) 的意义所在

循环神经网络本质上处理的是时序信息,或者是分先后顺序的信息。在文本的处理中经常用到,在此不做深究,我们只认为\(RNN\)的处理结果中包含了,时序信息。而且包含的时序信息越多,处理的效果就越好。而正序处理和倒序处理包含了不同的时序信息。这是\(go\_backwards\)参数的意义所在,也是双向循环层的意义所在。

Bidirectional

整体效果

在一次处理中,正向处理一遍,反向处理一遍,时间代价不变,空间代价加倍,获取更多序列信息。

keras实现

1
2
3
tf.keras.layers.Bidirectional(
layer, merge_mode="concat", weights=None, backward_layer=None, **kwargs
)

\(keras\)中的\(Bidirectional\)其实不是一个单独的layer,而是一个wrapper。先看一个例子。

1
2
3
4
5
6
7
8
9
model = Sequential()
forward_layer = LSTM(10, return_sequences=True)
backward_layer = LSTM(10, activation='relu', return_sequences=True,
go_backwards=True)
model.add(Bidirectional(forward_layer, backward_layer=backward_layer,
input_shape=(5, 10)))
model.add(Dense(5))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')

在声明中,我们需要实例化两个循环神经网络层,这个循环层可以是\(RNN,LSTM,GRU\)等,然后将这两个实例化层作为参数传给tf.keras.layers.Bidirectional即可。

总结

比较核心的layer都已经讲完了,接下来会分享一些同样重要的点,比如metrics、callbacks等,他们对于准确率的提升,训练过程的控制等都有重要意义。

标准化层

各位读者大家好,今天我们要一起来学习Keras的标准化层。首先,我会通过图解为大家讲解一下标准化层都做了什么,之后再为大家展示Keras中的标准化层函数。那接下来就让我们开始今天的学习吧!

Keras中的标准化层实际上就是批量标准化。它与普通的数据标准化类似,将分散的数据进行统一,有利于网络对于数据之中规律的学习。

我们先来看看数据标准化的必要性。

v2-8cdb76b58fb2e84a7c59fcf15e0dfda3_hd

从图中可以看出,在神经网络中,当我们使用像tanh这样的激活函数之后,如果Wx的激活值在激励函数的饱和阶段时(如图中的红色部分所示),那么一个极大的值和一个较小的值经过激活函数处理之后的差别不大,即神经网络对较大的x特征范围不再敏感。这是很不好的事情,就像一个人已经无法感受到轻轻的拍打和重重一拳的区别了,这意味着感受能力大大降低。所以批标准化应运而生,来解决这件可怕的事情。它会对输入进行处理,使得数据进入激励函数的敏感部分,加强网络的学习能力。

一般批标准化层添加在每一个全连接和激励函数之间,对全连接层的计算结果经过标准化处理再经过激励函数处理。

计算结果在进入激励函数前的值很重要, 如果我们不单单看一个值, 我们可以说, 计算结果值的分布对于激励函数很重要. 对于数据值大多分布在这个区间的数据, 才能进行更有效的传递. 对比这两个在激活之前的值的分布. 上者没有进行 标准化, 下者进行了 标准化, 这样当然是下者能够更有效地利用 tanh 进行非线性化的过程。

v2-95f654fdf99999db3fa7dab0bbfbc358_hd

由下图可以看出,没有 normalize 的数据 使用 tanh 激活以后, 激活值大部分都分布到了饱和阶段, 也就是大部分的激活值不是-1, 就是1, 而 normalize 以后, 大部分的激活值在每个分布区间都还有存在. 再将这个激活后的分布传递到下一层神经网络进行后续计算, 每个区间都有分布的这一种对于神经网络就会更加有价值。Keras的标准化层不仅会标准化数据,还会反标准化数据。

v2-b31f7d863179f5f0b93d40c4fabbc31a_hd
BatchNormalization
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
keras.layers.BatchNormalization(
axis=-1, #整数,需要标准化的轴(通常是特征轴)。例如,在 data_format="channels_first"的Conv2D 层之后,在BatchNormalization中设置axis=1
momentum=0.99, #移动均值和移动方差的动量
epsilon=0.001, #增加到方差的小的浮点数,以避免除以零
center=True, #如果为True,把beta的偏移量加到标准化的张量上。如果为False,beta被忽略
scale=True, #如果为True,乘以gamma。如果为False,gamma不使用。当下一层为线性层(或者例如 nn.relu),这可以被禁用,因为缩放将由下一层完成
beta_initializer='zeros', # beta 权重的初始化方法
gamma_initializer='ones', #gamma 权重的初始化方法
moving_mean_initializer='zeros', #移动均值的初始化方法
moving_variance_initializer='ones', #移动方差的初始化方法
beta_regularizer=None, #可选的 beta 权重的正则化方法
gamma_regularizer=None, #可选的 gamma 权重的正则化方法
beta_constraint=None, #可选的 beta 权重的约束方法
gamma_constraint=None #可选的 gamma 权重的约束方法
)

作用:批量标准化,在每一个批次的数据中标准化前一层的激活项, 即应用一个维持激活项平均值接近 0,标准差接近 1 的转换。

输入尺寸:可以是任意的。如果将这一层作为模型的第一层,则需要指定 input_shape参数(整数元组,不包含样本数量的维度)。

输出尺寸:与输入相同

相信大家经过今天的学习,能够对标准化层的功能有一个清晰的认知,标准化层是非常重要的网络层,它可以用于改善人工神经网络的性能和稳定性,使机器学习更容易学习到数据之中的规律。 所以希望各位能亲手实践以对它有更好地认识,大家一起加油!

池化层

各位读者大家好,上周我们详细讲解了Keras的核心网络层,相信大家已经对核心网络层的作用以及使用方法有了一个清晰的认识。这周我们将要开展池化层和卷积层的讲解了,这篇文章就是对池化层的讲解。

首先我们要统一说一下池化层的作用。池化层放在连续的卷积层中间,用于压缩数据和参数的量,防止过拟合。其具体操作与卷积层的操作基本相同,只不过池化层的卷积核只取对应位置的最大值、平均值等(最大池化、平均池化),且不经过反向传播的修改。总结一下,作用有两个:①invariance(不变性),这种不变性包括translation(平移),rotation(旋转),scale(尺度);②保留主要的特征同时减少参数(降维,效果类似PCA)和计算量,防止过拟合,提高模型泛化能力。

池化层操作又分为平均池化和最大池化。根据相关理论,特征提取的误差主要来自两个方面:①邻域大小受限造成的估计值方差增大;②卷积层参数误差造成估计均值的偏移。一般来说,平均池化能减小第一种误差,更多地保留图像的背景信息,最大池化能减小第二种误差,更多地保留纹理信息。

池化层的操作方式我们可以通过以下的图片来清晰地了解。

最大池化图示

平均池化图示

全局池化操作即是对指定的张量的全局数据进行最大池化或者平均池化操作。池化层中的1D是对时序数据进行池化操作,2D是对空间数据进行池化操作,3D是对空间或时空间数据进行池化操作。

MaxPooling1D层
1
2
3
4
5
6
keras.layers.MaxPooling1D(
pool_size=2, #整数,最大池化的窗口大小
strides=None, #整数,或者是None。作为缩小比例的因数。例如,2会使得输入张量缩小一半。如果是None,那么默认值是pool_size。
padding='valid', #"valid" 或者 "same"(区分大小写)
data_format='channels_last' #字符串,channels_last(默认)或 channels_first之一。表示输入各维度的顺序。channels_last对应输入尺寸为(batch, steps, features),channels_first对应输入尺寸为(batch, features, steps)
)

输入尺寸:

如果 data_format='channels_last',输入为 3D 张量,尺寸为:(batch_size, steps, features)

如果data_format='channels_first',输入为 3D 张量,尺寸为:(batch_size, features, steps)

输出尺寸:

如果 data_format='channels_last',输出为 3D 张量,尺寸为:(batch_size, downsampled_steps, features)

如果data_format='channels_first',输出为 3D 张量,尺寸为:(batch_size, features, downsampled_steps)

MaxPooling2D
1
2
3
4
5
6
keras.layers.MaxPooling2D(
pool_size=(2, 2), #整数,或者2个整数表示的元组,沿(垂直,水平)方向缩小比例的因数。(2,2)会把输入张量的两个维度都缩小一半。如果只使用一个整数,那么两个维度都会使用同样的窗口长度。
strides=None, #整数,2个整数表示的元组,或者是None。表示步长值。如果是None,那么默认值是pool_size
padding='valid', #"valid" 或者 "same"(区分大小写)
data_format=None #字符串,channels_last(默认)或channels_first之一。表示输入各维度的顺序。channels_last代表尺寸是 (batch, height, width, channels)的输入张量,而channels_firs代表尺寸是(batch, channels, height, width)的输入张量
)

输入尺寸:

如果 data_format='channels_last: 尺寸是 (batch_size, rows, cols, channels)的 4D 张量

如果 data_format='channels_first': 尺寸是 (batch_size, channels, rows, cols)的 4D 张量

输出尺寸:

如果 data_format='channels_last: 尺寸是 (batch_size, pooled_rows, pooled_cols, channels)的 4D 张量

如果 data_format='channels_first': 尺寸是 (batch_size, channels, pooled_rows, pooled_cols)的 4D 张量

MaxPooling3D
1
2
3
4
5
6
keras.layers.MaxPooling3D(
pool_size=(2, 2, 2), #3个整数表示的元组,缩小(dim1,dim2,dim3)比例的因数。(2, 2, 2)会把 3D 输入张量的每个维度缩小一半
strides=None, #3个整数表示的元组,或者是None。表示步长值,意义同上几个层
padding='valid', #"valid" 或者 "same"(区分大小写)
data_format=None #字符串,channels_last(默认)或channels_first之一。表示输入各维度的顺序。channels_last代表尺寸是(batch, spatial_dim1, spatial_dim2, spatial_dim3, channels)的输入张量,而channels_first代表尺寸是(batch, channels, spatial_dim1, spatial_dim2, spatial_dim3)的输入张量
)

输入大小:

如果data_format='channels_last': 尺寸是 (batch_size, spatial_dim1, spatial_dim2, spatial_dim3, channels)的 5D 张量

如果data_format='channels_first': 尺寸是 (batch_size, channels, spatial_dim1, spatial_dim2, spatial_dim3)的 5D 张量

输出大小:

如果data_format='channels_last': 尺寸是 (batch_size, pooled_dim1, pooled_dim2, pooled_dim3, channels)的 5D 张量

如果data_format='channels_first': 尺寸是 (batch_size, channels, pooled_dim1, pooled_dim2, pooled_dim3)的 5D 张量

AveragePooling1D
1
2
3
4
5
6
keras.layers.AveragePooling1D(
pool_size=2, #整数,平均池化的窗口大小
strides=None, #整数,或者是None。作为缩小比例的因数。例如,2会使得输入张量缩小一半。如果是None,那么默认值是pool_size。
padding='valid', #"valid" 或者 "same"(区分大小写)
data_format='channels_last' #字符串,channels_last(默认)或 channels_first之一。表示输入各维度的顺序。channels_last对应输入尺寸为(batch, steps, features),channels_first对应输入尺寸为(batch, features, steps)
)

输入尺寸:

如果 data_format='channels_last',输入为 3D 张量,尺寸为:(batch_size, steps, features)

如果data_format='channels_first',输入为 3D 张量,尺寸为:(batch_size, features, steps)

输出尺寸:

如果 data_format='channels_last',输出为 3D 张量,尺寸为:(batch_size, downsampled_steps, features)

如果data_format='channels_first',输出为 3D 张量,尺寸为:(batch_size, features, downsampled_steps)

AveragePooling2D
1
2
3
4
5
6
keras.layers.AveragePooling2D(
pool_size=(2, 2), #整数,或者2个整数表示的元组,沿(垂直,水平)方向缩小比例的因数。(2,2)会把输入张量的两个维度都缩小一半。如果只使用一个整数,那么两个维度都会使用同样的窗口长度。
strides=None, #整数,2个整数表示的元组,或者是None。表示步长值。如果是None,那么默认值是pool_size
padding='valid', #"valid" 或者 "same"(区分大小写)
data_format=None #字符串,channels_last(默认)或channels_first之一。表示输入各维度的顺序。channels_last代表尺寸是 (batch, height, width, channels)的输入张量,而channels_firs代表尺寸是(batch, channels, height, width)的输入张量
)

输入尺寸:如果 data_format='channels_last: 尺寸是 (batch_size, rows, cols, channels)的 4D 张量

如果 data_format='channels_first': 尺寸是 (batch_size, channels, rows, cols)的 4D 张量

输出尺寸:如果 data_format='channels_last: 尺寸是 (batch_size, pooled_rows, pooled_cols, channels)的 4D 张量

如果 data_format='channels_first': 尺寸是 (batch_size, channels, pooled_rows, pooled_cols)的 4D 张量

AveragePooling3D
1
2
3
4
5
6
keras.layers.AveragePooling3D(
pool_size=(2, 2, 2), #3个整数表示的元组,缩小(dim1,dim2,dim3)比例的因数。(2, 2, 2)会把 3D 输入张量的每个维度缩小一半
strides=None, #3个整数表示的元组,或者是None。表示步长值,意义同上几个层
padding='valid', #"valid" 或者 "same"(区分大小写)
data_format=None #字符串,channels_last(默认)或channels_first之一。表示输入各维度的顺序。channels_last代表尺寸是(batch, spatial_dim1, spatial_dim2, spatial_dim3, channels)的输入张量,而channels_first代表尺寸是(batch, channels, spatial_dim1, spatial_dim2, spatial_dim3)的输入张量
)

输入大小:如果data_format='channels_last': 尺寸是 (batch_size, spatial_dim1, spatial_dim2, spatial_dim3, channels)的 5D 张量

如果data_format='channels_first': 尺寸是 (batch_size, channels, spatial_dim1, spatial_dim2, spatial_dim3)的 5D 张量

输出大小:如果data_format='channels_last': 尺寸是 (batch_size, pooled_dim1, pooled_dim2, pooled_dim3, channels)的 5D 张量

如果data_format='channels_first': 尺寸是 (batch_size, channels, pooled_dim1, pooled_dim2, pooled_dim3)的 5D 张量

GlobalMaxPooling1D
1
2
3
keras.layers.GlobalMaxPooling1D(
data_format='channels_last' #同上1D层
)

输入尺寸:如果 data_format='channels_last',输入为 3D 张量,尺寸为:(batch_size, steps, features)

如果data_format='channels_first',输入为 3D 张量,尺寸为:(batch_size, features, steps)

输出尺寸:尺寸是 (batch_size, features) 的 2D 张量。

GlobalMaxPooling2D
1
2
3
keras.layers.GlobalMaxPooling3D(
data_format='channels_last' #同上2D层
)

输入尺寸:如果 data_format='channels_last: 尺寸是 (batch_size, rows, cols, channels)的 4D 张量

如果 data_format='channels_first': 尺寸是 (batch_size, channels, rows, cols)的 4D 张量

输出尺寸:尺寸是 (batch_size, channels) 的 2D 张量。

GlobalMaxPooling3D
1
2
3
keras.layers.GlobalMaxPooling3D(
data_format='channels_last' #同上3D层
)

输入大小:如果data_format='channels_last': 尺寸是 (batch_size, spatial_dim1, spatial_dim2, spatial_dim3, channels)的 5D 张量

如果data_format='channels_first': 尺寸是 (batch_size, channels, spatial_dim1, spatial_dim2, spatial_dim3)的 5D 张量

输出尺寸:尺寸是 (batch_size, channels) 的 2D 张量。

GlobalAveragePooling1D
1
2
3
keras.layers.GlobalAveragePooling1D(
data_format='channels_last' #同上1D层
)

输入尺寸:如果 data_format='channels_last',输入为 3D 张量,尺寸为:(batch_size, steps, features)

如果data_format='channels_first',输入为 3D 张量,尺寸为:(batch_size, features, steps)

输出尺寸:尺寸是 (batch_size, features) 的 2D 张量。

GlobalAveragePooling2D
1
2
3
keras.layers.GlobalAveragePooling2D(
data_format='channels_last' #同上2D层
)

输入尺寸:如果 data_format='channels_last: 尺寸是 (batch_size, rows, cols, channels)的 4D 张量

如果 data_format='channels_first': 尺寸是 (batch_size, channels, rows, cols)的 4D 张量

输出尺寸:尺寸是 (batch_size, channels) 的 2D 张量。

GlobalAveragePooling3D
1
2
3
keras.layers.GlobalAveragePooling3D(
data_format='channels_last' #同上3D层
)

输入大小:如果data_format='channels_last': 尺寸是 (batch_size, spatial_dim1, spatial_dim2, spatial_dim3, channels)的 5D 张量

如果data_format='channels_first': 尺寸是 (batch_size, channels, spatial_dim1, spatial_dim2, spatial_dim3)的 5D 张量

输出尺寸:尺寸是 (batch_size, channels) 的 2D 张量。

相信大家经过今天的学习,能够获得对池化层的作用以及使用方式的认识,池化层是卷积神经网络的重要组成部分,希望大家在课下可以多多找实例进行再进一步的了解,让我们一起期待明天的卷积层教程吧!

噪声层

各位读者大家好,今天我们要一起来学习Keras的噪声层。在神经网络的研究和应用中,经常会出现噪声层的身影,许多正则化的方法通过向训练数据添加噪声来防止过拟合,提高神经网络的鲁棒性。下面让我们来一起看看Keras噪声层的类型。

GaussianNoise层
1
2
keras.layers.GaussianNoise(stddev #float,噪声分布的标准差
)

作用:

高斯分布即为正态分布,该网络层添加均值为0,标准差为stddev的高斯噪声。

这对缓解过拟合很有用 (你可以将其视为随机数据增强的一种形式)。 高斯噪声(GS)是对真实输入的腐蚀过程的自然选择。

由于它是一个正则化层,因此它只在训练时才被激活。

输入尺寸:

可以是任意的。 如果将该层作为模型的第一层,则需要指定 input_shape 参数 (整数元组,不包含样本数量的维度)。

输出尺寸:

与输入相同。

GaussianDropout层
1
2
keras.layers.GaussianDropout(rate #float,丢弃概率(与 Dropout 相同)。 这个乘性噪声的标准差为 sqrt(rate/(1 - rate))
)

功能:添加均值为1,标准差为sqrt{frac{rate}{1-rate} }的高斯噪声。

由于它是一个正则化层,因此它只在训练时才被激活。

输入尺寸:

可以是任意的。 如果将该层作为模型的第一层,则需要指定 input_shape 参数 (整数元组,不包含样本数量的维度)。

输出尺寸:与输入相同。

AlphaDropout层
1
2
3
4
keras.layers.AlphaDropout(rate, #float,丢弃概率(与 Dropout 相同)。 这个乘性噪声的标准差为 sqrt(rate/(1 - rate))
noise_shape=None,
seed=None #用作随机种子的Python整数
)

功能:将 Alpha Dropout 应用到输入。

Alpha Dropout 是一种 Dropout, 删除一定比例的输入,使得输出的均值和方差与输入的均值和方差很接近,保持数据的自规范性, 以确保即使在 dropout 后也能实现自我归一化。 通过随机将激活设置为负饱和值, Alpha Dropout 非常适合按比例缩放的指数线性单元(SELU)。噪声的标准差为sqrt{frac{rate}{1-rate} }。

输入大小:可以是任意的。 如果将该层作为模型的第一层,则需要指定 input_shape 参数 (整数元组,不包含样本数量的维度)。

输出大小:与输入相同。

相信大家经过今天的学习,能够对噪声层层的类型和功能有一个清晰的认知,融合层是比较重要的网络层,虽然简单但很重要,它在解决神经网络过拟合方面有较为重要的作用,所以希望各位能亲手实践以对它有更好地认识,大家一起加油!

高级激活层

各位读者大家好,今天我们要一起来学习Keras的高级激活层,它实际上就是激活函数的Model类API用法,与激活函数效果相同。激活层是非常重要的网络层,它将非线性变化引入了网络中,使网络可以任意逼进任何非线性函数中,给了神经网络更加强大的功能。没有激活函数的每层都相当于矩阵相乘,就算你叠加了若干层之后,无非还是个矩阵相乘罢了。可以说神经网络的成功与激活层密不可分。那接下来就让我们开始今天的学习吧!

LeakyReLU层
1
2
keras.layers.LeakyReLU(alpha=0.3 #alpha: float >= 0。负斜率系数
)

作用:

LeakyRelU是修正线性单元( Rectified Linear Unit, ReLU)的特殊版本,当不激活时, LeakyReLU仍然会有非零输出值,从而获得一个小梯度,避免ReLU可能出现的神经元“死亡”现象。

当神经元未激活时,它仍允许赋予一个很小的梯度: f(x)=max(0,x)+negative_slope×min(0,x), 其中,negative_slope是一个小的非零数。

输入尺寸:

可以是任意的。如果将这一层作为模型的第一层, 则需要指定 input_shape 参数 (整数元组,不包含样本数量的维度)。

输入尺寸:

可以是任意的。如果将这一层作为模型的第一层, 则需要指定 input_shape 参数 (整数元组,不包含样本数量的维度)。

输入尺寸:

可以是任意的。如果将该层作为模型的第一层, 则需要指定 input_shape 参数 (整数元组,不包含样本数量的维度)。

输出尺寸:

与输入相同

PReLU层
1
2
3
4
5
keras.layers.PReLU(alpha_initializer='zeros', #权重的初始化函数
alpha_regularizer=None, #权重的正则化方法
alpha_constraint=None, #权重的约束
shared_axes=None #激活函数共享可学习参数的轴。 例如,如果输入特征图来自输出形状为 (batch, height, width, channels) 的 2D 卷积层,而且你希望跨空间共享参数,以便每个滤波器只有一组参数, 可设置 shared_axes=[1, 2]
)

功能:

该层为参数化的ReLU( Parametric ReLU)。

形如 f(x) = alpha * x for x < 0, f(x) = x for x >= 0, 其中 alpha 是一个可学习的数组,尺寸与 x 相同。

输入尺寸:

可以是任意的。如果将这一层作为模型的第一层, 则需要指定 input_shape 参数 (整数元组,不包含样本数量的维度)。

输出尺寸:

与输入相同。

ELU层
1
2
keras.layers.ELU(alpha=1.0 #负因子的尺度 
)

功能:

ELU层是指数线性单元( Exponential Linera Unit)。

形如 f(x) = alpha * (exp(x) - 1.) for x < 0, f(x) = x for x >= 0

输入大小:

可以是任意的。如果将这一层作为模型的第一层, 则需要指定 input_shape 参数 (整数元组,不包含样本数量的维度)。

输出大小:

与输入相同。

ThresholdedReLU
1
keras.layers.ThresholdedReLU(theta=1.0 #theta: float >= 0。激活的阈值位)

功能:

该层是带有门限的ReLU

形式: f(x) = x for x > theta, 否则f(x) = 0

输入:

可以是任意的。如果将这一层作为模型的第一层, 则需要指定 input_shape 参数 (整数元组,不包含样本数量的维度)。

输出:

与输入相同。

Softmax
1
2
keras.layers.Softmax(axis=-1 #整数,应用 softmax 标准化的轴
)

功能:

softmax把一个k维的real value向量(a1,a2,a3,a4….)映射成一个(b1,b2,b3,b4….)其中bi是一个0-1的常数,然后可以根据bi的大小来进行多分类的任务。公式如下:

5501600-989369eaa83a4cbb

应用实例:

5501600-6160da655b1b8295

输入尺寸:

可以是任意的。如果将这一层作为模型的第一层, 则需要指定 input_shape 参数 (整数元组,不包含样本数量的维度)。

输出尺寸:

与输入相同。

ReLU
1
2
3
4
keras.layers.ReLU(max_value=None, #浮点数,最大的输出值
negative_slope=0.0, # float >= 0. 负斜率系数
threshold=0.0 #浮点数,"thresholded activation" 的阈值
)

功能:

Relu是最常用的默认激活函数,若不确定用哪个激活函数,就使用Relu或者LeakyRelu

Relu激活函数(The Rectified Linear Unit),用于隐层神经元输出。公式如下

9a504fc2d5628535b85bf9c29cef76c6a6ef639d

函数图像如下:

c252002a9eb4ee8628a2eb2c9624e7fc29b.jpg

它与其他激活函数最大的不同在于它是线性的,因而不存在梯度爆炸的问题,在多层网络结构下梯度会线性传递。

在深度学习中Relu是用的最广泛的一种激活函数。

使用默认值时,它返回逐个元素的 max(x,0)

否则:

  • 如果 x >= max_value,返回 f(x) = max_value
  • 如果 threshold <= x < max_value,返回 f(x) = x,
  • 否则,返回 f(x) = negative_slope * (x - threshold)

输入尺寸:

可以是任意的。如果将这一层作为模型的第一层, 则需要指定 input_shape 参数 (整数元组,不包含样本数量的维度)。

输出尺寸:

与输入相同。

相信大家经过今天的学习,能够对高级激活层的类型和功能有一个清晰的认知,激活层是非常重要的网络层,对于人工神经网络模型去学习、理解非常复杂和非线性的函数来说具有十分重要的作用,它们给神经元引入了非线性因素,使得神经网络可以任意逼近任何非线性函数,这样神经网络就可以应用到众多的非线性模型中。 所以希望各位能亲手实践以对它有更好地认识,大家一起加油!

局部连接层

上周我们刚刚结束了Keras卷积层的学习,卷积层事实上就是一种特殊的局部连接层。那么这周我们就来趁热打铁,来学习一下Keras的局部连接层。

局部连接层区别于全连接层,局部连接层也称为局部感知或稀疏连接,可以用来提取局部特征并且起到减少参数的作用。

img

局部连接示意图

LocallyConnected1D层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
keras.layers.LocallyConnected1D(
filters, #整数,输出空间的维度(即卷积中滤波器的输出数量)
kernel_size, #一个整数,或者单个整数表示的元组或列表, 指明 1D 卷积窗口的长度
strides=1, #一个整数,或者单个整数表示的元组或列表,指明卷积的步长。
padding='valid', #当前仅支持 "valid" (大小写敏感), "same" 可能会在未来支持
data_format=None, #在1D中默认即可
activation=None, #要使用的激活函数 (详见 activations)。 如果你不指定,则不使用激活函数 (即线性激活: a(x) = x)
use_bias=True, #布尔值,该层是否使用偏置向量
kernel_initializer='glorot_uniform', #kernel权值矩阵的初始化器,在之后的文章中会详细说明
bias_initializer='zeros', #偏置向量的初始化器
kernel_regularizer=None, #运用到kernel权值矩阵的正则化函数,在之后的文章中会详细说明
bias_regularizer=None, #运用到偏置向量的正则化函数
activity_regularizer=None, #运用到层输出(它的激活值)的正则化函数
kernel_constraint=None, #运用到kernel权值矩阵的约束函数,在之后的文章中会详细说明
bias_constraint=None #运用到偏置向量的约束函数
)

作用:

对于时序数据进行局部连接。LocallyConnected1D层与Conv1D层的工作方式相同,除了权值不共享外, 也就是说,在输入的每个不同部分应用不同的一组过滤器。

输入尺寸:

3D 张量,尺寸为: (batch_size, steps, input_dim)。

输出尺寸:

3D 张量 ,尺寸为:(batch_size, new_steps, filters),steps值可能因填充或步长而改变。

例子:

1
2
3
4
5
6
7
# 将长度为3的非共享权重1D卷积应用于
# 具有10个时间步长的序列,并使用64个输出滤波器
model.add(LocallyConnected1D(64, 3, input_shape=(10, 32)))
# 现在model.output_shape == (None, 8, 64)
# 在上面再添加一个新的LocallyConnected1D
model.add(LocallyConnected1D(32, 3))
# 现在model.output_shape == (None, 6, 32)
LocallyConnected2D层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
keras.layers.LocallyConnected2D(
filters, #整数,输出空间的维度(即卷积中滤波器的输出数量)
kernel_size, #一个整数,或者2个整数表示的元组或列表,指明2D卷积窗口的宽度和高度。可以是一个整数,为所有空间维度指定相同的值
strides=(1, 1), #一个整数,或者2个整数表示的元组或列表,指明卷积沿宽度和高度方向的步长。可以是一个整数,为所有空间维度指定相同的值
padding='valid', #当前仅支持 "valid" (大小写敏感),"same" 可能会在未来支持
data_format=None, #字符串,channels_last(默认)或channels_first之一。输入中维度的顺序。channels_last对应输入尺寸为 (batch, height, width, channels),channels_first对应输入尺寸为 (batch, channels, height, width)
activation=None, #要使用的激活函数 (详见 activations)。 如果你不指定,则不使用激活函数 (即线性激活: a(x) = x)
use_bias=True, #布尔值,该层是否使用偏置向量
kernel_initializer='glorot_uniform', #kernel权值矩阵的初始化器,在之后的文章中会详细说明
bias_initializer='zeros', #偏置向量的初始化器
kernel_regularizer=None, #运用到kernel权值矩阵的正则化函数,在之后的文章中会详细说明
bias_regularizer=None, #运用到偏置向量的正则化函数
activity_regularizer=None, #运用到层输出(它的激活值)的正则化函数
kernel_constraint=None, #运用到kernel权值矩阵的约束函数,在之后的文章中会详细说明
bias_constraint=None #运用到偏置向量的约束函数
)

作用:

对于空间数据进行局部连接。LocallyConnected2D层与Conv2D层的工作方式相同,除了权值不共享外, 也就是说,在输入的每个不同部分应用不同的一组过滤器。

输入尺寸:

4D 张量,尺寸为: (samples, channels, rows, cols),如果 data_format='channels_first'; 或者 4D 张量,尺寸为: (samples, rows, cols, channels),如果 data_format='channels_last'。

输出尺寸:

4D 张量,尺寸为: (samples, filters, new_rows, new_cols),如果 data_format='channels_first'; 或者 4D 张量,尺寸为: (samples, new_rows, new_cols, filters),如果 data_format='channels_last'。 rows 和 cols 的值可能因填充而改变。

例子:

1
2
3
4
5
6
7
# 在 32x32 图像上应用 3x3 非共享权值和64个输出过滤器的卷积
# 数据格式`data_format="channels_last"`
model.add(LocallyConnected2D(64, (3, 3), input_shape=(32, 32, 3)))
# 现在model.output_shape == (None, 30, 30, 64)
# 在上面再加一个 3x3 非共享权值和 32 个输出滤波器的卷积:
model.add(LocallyConnected2D(32, (3, 3)))
# 现在 model.output_shape == (None, 28, 28, 32)

相信大家经过今天的学习,能够获得对局部连接层的作用以及使用方式的认识,局部连接层是减少网络参数的重要途径,希望大家在课下可以多多找实例进行再进一步的了解,让我们一起期待明天的RNN层教程吧!

融合层

各位读者大家好,上周我们已经详细讲解了Keras的卷积层和池化层,相信大家经过学习已经对两者有了一个清晰的认识,也能更好地把它们运用到图像处理中。池化层和卷积层是非常重要的网络层类型,大家在学习之后也需要动手实践去更好地理解它们。鉴于上周学习了比较难的网络层,那么今天我们就来放松一下,学习Keras中比较简单的融合层。让我们开始今天的学习吧

Add层
1
keras.layers.Add()

作用:

计算输入张量列表的和。

输入尺寸:

它接受一个张量的列表, 所有的张量必须有相同的输入尺寸, 然后返回一个张量(和输入张量尺寸相同)。

输出尺寸:

一个张量,与输入张量列表的张量尺寸一致。

例子:

1
2
3
4
5
6
7
input1 = keras.layers.Input(shape=(16,))
x1 = keras.layers.Dense(8, activation='relu')(input1)
input2 = keras.layers.Input(shape=(32,))
x2 = keras.layers.Dense(8, activation='relu')(input2)
# 相当于 added = keras.layers.add([x1, x2])
# 将x1和x2两个张量相加,得到与两者尺寸相等的输出向量
added = keras.layers.Add()([x1, x2])
Subtract
1
keras.layers.Subtract()

功能:

计算两个输入张量的差。

输入尺寸:

它接受一个长度为 2 的张量列表, 两个张量必须有相同的尺寸,然后返回一个值为 (inputs[0] - inputs[1]) 的张量, 输出张量和输入张量尺寸相同。

输出尺寸:

一个张量,与输入张量列表的张量尺寸一致。

例子:

1
2
3
4
5
6
7
input1 = keras.layers.Input(shape=(16,))
x1 = keras.layers.Dense(8, activation='relu')(input1)
input2 = keras.layers.Input(shape=(32,))
x2 = keras.layers.Dense(8, activation='relu')(input2)
# 相当于 subtracted = keras.layers.subtract([x1, x2])
# 使用x1张量减去x2,最终得到一个尺寸与两者相同的张量
subtracted = keras.layers.Subtract()([x1, x2])
Multiply
1
keras.layers.Multiply()

功能:

计算输入张量列表的(逐元素间的)乘积。

输入大小:

它接受一个张量的列表, 所有的张量必须有相同的输入尺寸, 然后返回一个张量(和输入张量尺寸相同)。

输出大小:

一个张量,与输入张量列表的张量尺寸一致。

例子:

1
2
3
4
5
6
7
input1 = keras.layers.Input(shape=(16,))
x1 = keras.layers.Dense(8, activation='relu')(input1)
input2 = keras.layers.Input(shape=(32,))
x2 = keras.layers.Dense(8, activation='relu')(input2)
# 相当于 multiplied= keras.layers.multiply([x1, x2])
# 使用x1张量乘x2,最终得到一个尺寸与两者相同的张量
multiplied = keras.layers.Multiply()([x1, x2])
Average
1
keras.layers.Average()

功能:

计算输入张量列表的平均值。

输入:

它接受一个张量的列表, 所有的张量必须有相同的输入尺寸, 然后返回一个张量(和输入张量尺寸相同)。

输出:

一个张量,与输入张量列表的张量尺寸一致。

例子:

1
2
3
4
5
6
7
input1 = keras.layers.Input(shape=(16,))
x1 = keras.layers.Dense(8, activation='relu')(input1)
input2 = keras.layers.Input(shape=(32,))
x2 = keras.layers.Dense(8, activation='relu')(input2)
# 相当于 averaged= keras.layers.average([x1, x2])
# 使用x1张量乘x2,最终得到一个尺寸与两者相同的张量
averaged = keras.layers.Average()([x1, x2])
Maximum
1
keras.layers.Maximum()

功能:

计算输入张量列表的(逐元素间的)最大值。

输入尺寸:

它接受一个张量的列表, 所有的张量必须有相同的输入尺寸, 然后返回一个张量(和输入张量尺寸相同)。

输出尺寸:

一个张量,与输入张量列表的张量尺寸一致。

例子:

1
2
3
4
5
6
7
input1 = keras.layers.Input(shape=(16,))
x1 = keras.layers.Dense(8, activation='relu')(input1)
input2 = keras.layers.Input(shape=(32,))
x2 = keras.layers.Dense(8, activation='relu')(input2)
# 相当于 maximum= keras.layers.maximum([x1, x2])
# 输出为x1和x2中的最大值
maximum = keras.layers.Maximum()([x1, x2])
Concatenate
1
2
3
keras.layers.Concatenate(
axis=-1 #连接的轴
)

功能:

连接一个输入张量的列表。

输入尺寸:

它接受一个张量的列表, 除了连接轴之外,其他的尺寸都必须相同, 然后返回一个由所有输入张量连接起来的输出张量。

输出尺寸:

列表中所有张量根据连接轴连接之后形成的张量大小

例子:

1
2
3
4
5
6
7
input1 = keras.layers.Input(shape=(16,))
x1 = keras.layers.Dense(8, activation='relu')(input1)
input2 = keras.layers.Input(shape=(32,))
x2 = keras.layers.Dense(8, activation='relu')(input2)
# 相当于 concatenated= keras.layers.concatenate([x1, x2])
# 将x1和x2连接起来
concatenated = keras.layers.Concatenate()([x1, x2])
Dot
1
2
3
4
keras.layers.Dot(
axes, #整数或者整数元组,一个或者几个进行点积的轴
normalize=False #是否在点积之前对即将进行点积的轴进行 L2 标准化。 如果设置成 True,那么输出两个样本之间的余弦相似值
)

功能:

计算两个张量之间样本的点积。

输入尺寸:

它接受一个长度为 2 的张量列表, 两个张量必须有相同的尺寸,假设两个张量分别为a和b,则返回一个值为∑(a[i] + b[i])的张量。

输出尺寸:

(batch_size,1)

例子:

1
2
3
4
5
6
7
input1 = keras.layers.Input(shape=(16,))
x1 = keras.layers.Dense(8, activation='relu')(input1)
input2 = keras.layers.Input(shape=(32,))
x2 = keras.layers.Dense(8, activation='relu')(input2)
# 相当于 doted= keras.layers.dot([x1, x2])
# 将x1和x2做点积并输出
doted = keras.layers.Dot()([x1, x2])

相信大家经过今天的学习,能够对融合层的类型和功能有一个清晰的认知,融合层也是重要的网络层,虽然简单但很重要,它在构造结构复杂的Model类模型中起到了很重要的作用,所以希望各位能亲手实践以对它有更好地认识,大家一起加油!

包装层

写在开篇

各位读者大家好,今天我们要一起来学习Keras包装层中的两个工具:TimeDistributed 和Bidirectional。虽然并不算十分常见,但自有其神奇之处。

类型说明

TimeDistributed

TimeDistributed这个层还是比较难理解的。事实上通过这个层,可以实现从二维像三维的过渡,甚至通过这个层的包装,可以实现图像分类视频分类的转化。

1
keras.layers.TimeDistributed(layer)

这个封装器将一个层应用于输入的每个时间片。

输入至少为 3D,且第一个维度应该是时间所表示的维度。

考虑 32 个样本的一个 batch, 其中每个样本是 10 个 16 维向量的序列。那么这个 batch 的输入尺寸为 (32, 10, 16), 而 input_shape 不包含样本数量的维度,为 (10, 16)。使用 TimeDistributed 来将 Dense 层独立地应用到 这 10 个时间步的每一个:

可以这么理解,输入数据是一个特征方程,X1+X2+...+X10=Y,从矩阵的角度看,拿出未知数Y,就是10个向量,每个向量有16个维度,这16个维度是评价Y的16个特征方向。TimeDistributed层的作用就是把Dense层应用到这10个具体的向量上,对每一个向量进行了一个Dense操作,假设是下面这段代码:

1
2
3
4
# 作为模型第一层
model = Sequential()
model.add(TimeDistributed(Dense(8), input_shape=(10, 16)))
# 现在 model.output_shape == (None, 10, 8)

输出的尺寸为 (32, 10, 8)。输出还是10个向量,但是输出的维度由16变成了8。

在后续的层中,将不再需要指定 input_shape

1
2
model.add(TimeDistributed(Dense(32)))
# 现在 model.output_shape == (None, 10, 32)

输出的尺寸为 (32, 10, 32)

TimeDistributed 可以应用于任意层,不仅仅是 Dense, 例如运用于 Conv2D 层:

1
2
3
model = Sequential()
model.add(TimeDistributed(Conv2D(64, (3, 3)),
input_shape=(10, 299, 299, 3)))

Bidirectional

1
keras.layers.Bidirectional(layer, merge_mode='concat', weights=None)

RNN 的双向封装器,对序列进行前向和后向计算。

参数

  • layer: Recurrent 实例。
  • merge_mode: 前向和后向 RNN 的输出的结合模式。为 {'sum', 'mul', 'concat', 'ave', None} 其中之一。如果是 None,输出不会被结合,而是作为一个列表被返回。

异常

  • ValueError: 如果参数 merge_mode 非法。

1
2
3
4
5
6
7
model = Sequential()
model.add(Bidirectional(LSTM(10, return_sequences=True),
input_shape=(5, 10)))
model.add(Bidirectional(LSTM(10)))
model.add(Dense(5))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')

Bidirectional一个常用的例子是双向LSTM,下面的例程展示了如何在IMDB情绪分类任务中,训练一个Bidirectional LSTM模型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from __future__ import print_function
import numpy as np

from keras.preprocessing import sequence
from keras.models import Sequential
from keras.layers import Dense, Dropout, Embedding, LSTM, Bidirectional
from keras.datasets import imdb


max_features = 20000
# cut texts after this number of words
# (among top max_features most common words)
maxlen = 100
batch_size = 32

print('Loading data...')
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_features)
print(len(x_train), 'train sequences')
print(len(x_test), 'test sequences')

print('Pad sequences (samples x time)')
x_train = sequence.pad_sequences(x_train, maxlen=maxlen)
x_test = sequence.pad_sequences(x_test, maxlen=maxlen)
print('x_train shape:', x_train.shape)
print('x_test shape:', x_test.shape)
y_train = np.array(y_train)
y_test = np.array(y_test)

model = Sequential()
model.add(Embedding(max_features, 128, input_length=maxlen))
model.add(Bidirectional(LSTM(64)))
model.add(Dropout(0.5))
model.add(Dense(1, activation='sigmoid'))

# try using different optimizers and different optimizer configs
model.compile('adam', 'binary_crossentropy', metrics=['accuracy'])

print('Train...')
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=4,
validation_data=[x_test, y_test])

写在文末

相信大家经过今天的学习,能够对包装层的两个工具有一定的认识。文末还为有兴趣实践的读者附上了Bidirectional的使用例,感兴趣的读者不妨动手一试。

嵌入层

畅游人工智能之海 带你学习Keras的嵌入层

写在开篇

mbedding 实际上是一种映射,将单词从原先的表示映射到新的多维空间中,在这个多维空间中,词向量之间的距离表征了单词之间的语义相关性。例如,cat 和 kitten语义相近,而cow和cat语义相差就比较远,因此一个好的嵌入模型应该能正确的将cat 和kitten放的比较近,同时cat和cow的距离相对较远。620

类型说明

Embedding

嵌入层 其作用是将正整数(索引值)转换为固定尺寸的稠密向量。例如:[[4], [20]] -> [[0.25, 0.1], [0.6, -0.2]],只能用作模型中的第一层。

1
keras.layers.Embedding(input_dim, output_dim, embeddings_initializer='uniform', embeddings_regularizer=None, activity_regularizer=None, embeddings_constraint=None, mask_zero=False, input_length=None)

例子

1
2
3
4
5
6
7
8
9
10
11
model = Sequential()
model.add(Embedding(1000, 64, input_length=10))
# 模型将输入一个大小为 (batch, input_length) 的整数矩阵。
# 输入中最大的整数(即词索引)不应该大于 999 (词汇表大小)
# 现在 model.output_shape == (None, 10, 64),其中 None 是 batch 的维度。

input_array = np.random.randint(1000, size=(32, 10))

model.compile('rmsprop', 'mse')
output_array = model.predict(input_array)
assert output_array.shape == (32, 10, 64)

参数

  • input_dim: int > 0。词汇表大小, 即,最大整数 index + 1。
  • output_dim: int >= 0。词向量的维度。
  • embeddings_initializer: embeddings 矩阵的初始化方法
  • embeddings_regularizer: embeddings matrix 的正则化方法
  • embeddings_constraint: embeddings matrix 的约束函数
  • mask_zero: 是否把 0 看作为一个应该被遮蔽的特殊的 "padding" 值。这对于可变长的 循环神经网络层 十分有用。如果设定为 True,那么接下来的所有层都必须支持 masking,否则就会抛出异常。如果 mask_zero 为 True,作为结果,索引 0 就不能被用于词汇表中 (input_dim 应该与 vocabulary + 1 大小相同)。

input_length: 输入序列的长度,当它是固定的时。如果你需要连接 FlattenDense 层,则这个参数是必须的 (没有它,dense 层的输出尺寸就无法计算)。

输入尺寸

尺寸为 (batch_size, sequence_length) 的 2D 张量。

输出尺寸

尺寸为 (batch_size, sequence_length, output_dim) 的 3D 张量。

keras中的Embedding层有两种词嵌入的方式,如果需要语义特征,可以使用预训练的方式,通过参数指定预训练词向量矩阵,该词向量矩阵可以是基于任何语言模型,如Word2vec、Glove等。如果不需要语义特征,还有另一种方式是随机初始化,Embedding在随机初始化方式下是一个全连接层,而后面得到的词表示是全连接层的权重参数。

实验例子

利用GloVe预训练词向量对新闻文本进行分类

篇幅有限,只保留主要部分,所有代码请参考 在Keras模型中使用预训练的词向量

  • 数据预处理:

    先遍历下语料文件下的所有文件夹,获得不同类别的新闻以及对应的类别标签

  • Embedding layer设置:

    接下来,从GloVe文件中解析出每个词和它所对应的词向量,并用字典的方式存储

1
2
3
4
5
6
7
8
embeddings_index = {}
f = open(os.path.join(GLOVE_DIR, 'glove.6B.100d.txt'))
for line in f:
values = line.split()
word = values[0]
coefs = np.asarray(values[1:], dtype='float32')
embeddings_index[word] = coefs
f.close()

此时,我们可以根据得到的字典生成上文所定义的词向量矩阵

1
2
3
4
5
6
embedding_matrix = np.zeros((len(word_index) + 1, EMBEDDING_DIM))
for word, i in word_index.items():
embedding_vector = embeddings_index.get(word)
if embedding_vector is not None:
# words not found in embedding index will be all-zeros.
embedding_matrix[i] = embedding_vector

现在将这个词向量矩阵加载到Embedding层中,注意,trainable=False使得这个编码层不可再训练。

1
2
3
from keras.layers import Embedding

embedding_layer = Embedding(len(word_index) + 1,EMBEDDING_DIM,weights=[embedding_matrix],input_length=MAX_SEQUENCE_LENGTH,trainable=False)

一个Embedding层的输入应该是一系列的整数序列,比如一个2D的输入,它的shape值为(samples, indices),也就是一个samples行,indeces列的矩阵。每一次的batch训练的输入应该被padded成相同大小。所有的序列中的整数都将被对应的词向量矩阵中对应的列(也就是它的词向量)代替,比如序列[1,2]将被序列[词向量[1],词向量[2]]代替。这样,输入一个2D张量后,我们可以得到一个3D张量。

训练1D卷积

最后,可以使用一个小型的1D卷积解决这个新闻分类问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
sequence_input = Input(shape=(MAX_SEQUENCE_LENGTH,), dtype='int32')
embedded_sequences = embedding_layer(sequence_input)
x = Conv1D(128, 5, activation='relu')(embedded_sequences)
x = MaxPooling1D(5)(x)
x = Conv1D(128, 5, activation='relu')(x)
x = MaxPooling1D(5)(x)
x = Conv1D(128, 5, activation='relu')(x)
x = MaxPooling1D(35)(x) # global max pooling
x = Flatten()(x)
x = Dense(128, activation='relu')(x)
preds = Dense(len(labels_index), activation='softmax')(x)

model = Model(sequence_input, preds)
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['acc'])

# happy learning!
model.fit(x_train, y_train, validation_data=(x_val, y_val),
nb_epoch=2, batch_size=128)

在两次迭代之后,这个模型最后可以达到0.95的分类准确率(4:1分割训练和测试集合)。也可以利用正则方法(例如dropout)或在Embedding层上进行fine-tuning获得更高的准确率。

作为对比,也可以直接使用keras自带的Embedding层训练词向量而不用GloVe向量。

1
2
3
embedding_layer = Embedding(len(word_index) + 1,
EMBEDDING_DIM,
input_length=MAX_SEQUENCE_LENGTH)

写在文末

相信大家经过今天的学习,能够获得对embedding层有一个初步的认识,感兴趣的小伙伴可以自己动手做做实验,我们下次见!

时间序列数据预处理

前言

keras提供的数据预处理总共有三种类型,他们分别用于处理图片数据、时间序列数据、文本数据,这一次要解析的就是处理时间序列数据预处理,这些api将原始数据按照灵活的格式读入,输出指定格式的可直接输入到模型中的数据。用于处理时间序列数据的\(API\)总共有三个,他们分别是:\(timeseries\_dataset\_from\_array function\), \(pad\_sequences function\), \(TimeseriesGenerator class\)

timeseries_dataset_from_array function

1
2
3
4
5
6
7
8
9
10
11
12
tf.keras.preprocessing.timeseries_dataset_from_array(
data,
targets,
sequence_length,
sequence_stride=1,
sampling_rate=1,
batch_size=128,
shuffle=False,
seed=None,
start_index=None,
end_index=None,
)

这个函数将原始数据转换成时间序列数据,所谓的一组时间序列数据就是,数据中的每一条数据都包含了相同时间长度的以当前时间点为结束的数据。

参数解释

data:输入数据

targets:说白了就是训练集的标签

sequence_length:生成的单个序列的长度

sequence_stride:生成的相邻的两个序列的第一个数据点的在原始数据中的位置间隔

sampling_rate:生成的单个序列的中的每个数据点的在原始数据中的位置间隔

shuffle:是否将生成的序列数据打乱顺序

seed:随机种子,只有使用shuffle时才用

start_index:开始下标,另一个参数对应的结束下标

举个例子

考虑输入数据 [0, 1, ... 99],使用如下参数调用该函数 sequence_length=10, sampling_rate=2, sequence_stride=3, shuffle=False,函数的返回值是:

1
2
3
4
5
First sequence:  [0  2  4  6  8 10 12 14 16 18]
Second sequence: [3 5 7 9 11 13 15 17 19 21]
Third sequence: [6 8 10 12 14 16 18 20 22 24]
...
Last sequence: [78 80 82 84 86 88 90 92 94 96]

pad_sequences

1
2
3
tf.keras.preprocessing.sequence.pad_sequences(
sequences, maxlen=None, dtype="int32", padding="pre", truncating="pre", value=0.0
)

这个函数用于将不同的数据补充成相同长度的数据,使用的是padding(补充数据)和truncation(截断)方法。第一个参数就是输入数据,其他的参数就不解释了,直接上例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 注意在实际使用这些程序时,一定要将 tf. 换成tensorflow.
>>> sequence = [[1], [2, 3], [4, 5, 6]]
>>> tf.keras.preprocessing.sequence.pad_sequences(sequence)
array([[0, 0, 1],
[0, 2, 3],
[4, 5, 6]], dtype=int32)
# value 的作用
>>> tf.keras.preprocessing.sequence.pad_sequences(sequence, value=-1)
array([[-1, -1, 1],
[-1, 2, 3],
[ 4, 5, 6]], dtype=int32)
# padding 的作用
>>> tf.keras.preprocessing.sequence.pad_sequences(sequence, padding='post')
array([[1, 0, 0],
[2, 3, 0],
[4, 5, 6]], dtype=int32)
# maxlen 的作用
>>> tf.keras.preprocessing.sequence.pad_sequences(sequence, maxlen=2)
array([[0, 1],
[2, 3],
[5, 6]], dtype=int32)
# truncation 的作用
>>> tf.keras.preprocessing.sequence.pad_sequences(sequence, maxlen=2,truncation='post')
array([[0, 1],
[2, 3],
[4, 5]], dtype=int32)

TimeseriesGenerator

1
2
3
4
5
6
7
8
9
10
11
12
tf.keras.preprocessing.sequence.TimeseriesGenerator(
data,
targets,
length,
sampling_rate=1,
stride=1,
start_index=0,
end_index=None,
shuffle=False,
reverse=False,
batch_size=128,
)

与第一个函数作用相同,不再赘述,关于用法见下面这个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from keras.preprocessing.sequence import TimeseriesGenerator
import numpy as np
data = np.array([[i] for i in range(50)])
targets = np.array([[i] for i in range(50)])
data_gen = TimeseriesGenerator(data, targets,
length=10, sampling_rate=2,
batch_size=2)
assert len(data_gen) == 20
batch_0 = data_gen[0]
x, y = batch_0
assert np.array_equal(x,
np.array([[[0], [2], [4], [6], [8]],
[[1], [3], [5], [7], [9]]]))
assert np.array_equal(y,
np.array([[10], [11]]))

图像数据预处理(一)

上周我们结束了优化器的学习,这周我们将要展开数据预处理中关于图像数据预处理函数的学习。数据预处理的函数可以帮助我们将原始数据转换成可用于训练模型的对象,这也是训练神经网络的过程中非常重要的一步,让我们一起来看看吧。

image_dataset_from_directory函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tf.keras.preprocessing.image_dataset_from_directory(
directory, #数据所在的目录。如果labels="inferred",则它应包含子目录,每个子目录都包含一个类的图像。否则,目录结构将被忽略。
labels="inferred", #“inferred”(标签是从目录结构生成的),或者是整数标签的列表/元组,其大小与目录中找到的图像文件的数量相同。标签应根据图像文件路径的字母数字顺序排序
label_mode="int", #'int': 表示标签被编码为整数;'categorical' 是指标签被编码为分类矢量;'binary' 表示将标签(只能有2个)编码为float32标量,其值为0或1;None 无标签
class_names=None, #仅在labels="inferred"时有效。这是类名称的明确列表(必须与子目录的名称匹配)。用于控制类的顺序(否则使用字母数字顺序)
color_mode="rgb", #“灰度”,“ rgb”,“ rgba”之一。默认值:“ rgb”。图像是否将转换为具有1、3或4个通道。
batch_size=32, #数据批处理的大小。默认值:32
image_size=(256, 256), #从磁盘读取图像后将图像调整大小的大小。默认为(256, 256)。由于流水线处理一批必须全部具有相同大小的图像,因此必须提供这些图像
shuffle=True, #是否随机播放数据。默认值:True。如果设置为False,则按字母数字顺序对数据进行排序
seed=None, #用于随机排列和转换的可选随机种子
validation_split=None, #可选的介于0和1之间的浮点数,可保留一部分数据供验证
subset=None, #仅在validation_split设置时使用
interpolation="bilinear", #字符串,调整图像大小时使用的插值方法。默认为bilinear。支持bilinear,nearest,bicubic, area,lanczos3,lanczos5,gaussian,mitchellcubic。
follow_links=False, #是否访问符号链接指向的子目录。默认为False
)

该函数会从目录中的图像文件生成 tf.data.Dataset 。

如果目录结构如下:

1
2
3
4
5
6
7
main_directory/
...class_a/
......a_image_1.jpg
......a_image_2.jpg
...class_b/
......b_image_1.jpg
......b_image_2.jpg

然后调用image_dataset_from_directory(main_directory,labels='inferred'), 将返回tf.data.Dataset,从子目录class_a和生成批次图像class_b,以及标签0和1(0对应于class_a和1对应于class_b)。

支持的图像格式:jpeg,png,bmp,gif。动画gif被截断到第一帧。

返回值:

一个tf.data.Dataset对象。如果label_mode为None,它将生成形状为(batch_size,image_size[0],image_size[1],num_channels)的float32张量,对图像进行编码。否则,将生成一个元组 (images, labels),其images 形状为(batch_size, image_size[0], image_size[1], num_channels),有可能为int、binary和categorial。如果label_modeint,标签是形如(batch_size,)的int32张量;如果label_modebinary,,标签是形如(batch_size, 1)的0s和1s的float32张量;如果label_modecategorial,标签是形如(batch_size, num_classes)的float32张量,表示类索引的单次编码。

如果color_modegrayscale,则图像张量中有1个通道。如果color_modergb,则图像张量中有3个通道。如果color_modergba,则图像张量中有4个通道。

load_img函数

1
2
3
4
5
6
7
tf.keras.preprocessing.image.load_img(
path, #图像文件的路径
grayscale=False, #灰度,不推荐使用color_mode="grayscale"
color_mode="rgb", #所需的图像格式。“灰度”,“ rgb”,“ rgba”之一。默认值:“ rgb”。
target_size=None, #(None默认为原始大小)或int元组(img_height, img_width)
interpolation="nearest" #如果目标尺寸与加载的图像不同,则使用插值方法对图像重新采样。 支持"nearest", "bilinear", and "bicubic". 默认情况下,使用“nearest”。
)

将图像加载为PIL格式。

例子:

1
2
3
4
image = tf.keras.preprocessing.image.load_img(image_path)
input_arr = keras.preprocessing.image.img_to_array(image)
input_arr = np.array([input_arr]) # 将单个图像转换为批处理
predictions = model.predict(input_arr)

返回:

PIL实例

注意:

ImportError:如果PIL不可用。

ValueError:如果不支持插值方法。

img_to_array函数

1
2
3
4
5
tf.keras.preprocessing.image.img_to_array(
img, #输入PIL图像实例
data_format=None, #图像数据格式,可以是“ channels_first”或“ channels_last”。默认为None,在这种情况下将使用全局设置 tf.keras.backend.image_data_format()(除非更改了它,否则默认为“ channels_last”)。
dtype=None #默认为None,在这种情况下,使用全局设置 tf.keras.backend.floatx()(除非您更改了它,否则默认为“ float32”)
)

返回值:

3D的numpy阵列

注意:

ValueError:如果无效imgdata_format已通过。

ImageDataGenerator类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
tf.keras.preprocessing.image.ImageDataGenerator(
featurewise_center=False, #布尔值。将输入数据的均值设置为 0,逐特征进行
samplewise_center=False, #布尔值。将每个样本的均值设置为 0
featurewise_std_normalization=False, #布尔值。将输入除以数据标准差,逐特征进行
samplewise_std_normalization=False, #布尔值。将每个输入除以其标准差
zca_whitening=False, #ZCA 白化的 epsilon 值,默认为 1e-6
zca_epsilon=1e-06, #布尔值。是否应用 ZCA 白化
rotation_range=0, #整数。随机旋转的度数范围
width_shift_range=0.0, #浮点数、一维数组或整数。
'''
float: 如果 <1,则是除以总宽度的值,或者如果 >=1,则为像素值。
1-D 数组: 数组中的随机元素。
int: 来自间隔 (-width_shift_range, +width_shift_range) 之间的整数个像素。
width_shift_range=2 时,可能值是整数 [-1, 0, +1],与 width_shift_range=[-1, 0, +1] 相同;而 width_shift_range=1.0 时,可能值是 [-1.0, +1.0) 之间的浮点数。
'''
height_shift_range=0.0, #浮点数、一维数组或整数
'''
float: 如果 <1,则是除以总宽度的值,或者如果 >=1,则为像素值。
1-D array-like: 数组中的随机元素。
int: 来自间隔 (-height_shift_range, +height_shift_range) 之间的整数个像素。
height_shift_range=2 时,可能值是整数 [-1, 0, +1],与 height_shift_range=[-1, 0, +1] 相同;而 height_shift_range=1.0 时,可能值是 [-1.0, +1.0) 之间的浮点数。
'''
brightness_range=None, #元组或两个浮点数的列表。从中选择亮度偏移值的范围
shear_range=0.0, #浮点数。剪切强度(以弧度逆时针方向剪切角度)
zoom_range=0.0, #浮点数 或 [lower, upper]
channel_shift_range=0.0, #浮点数。随机通道转换的范围
fill_mode="nearest", # {"constant", "nearest", "reflect" or "wrap"} 之一。默认为 'nearest'
cval=0.0, #浮点数或整数。用于边界之外的点的值,当 fill_mode = "constant" 时
horizontal_flip=False, #布尔值。随机水平翻转
vertical_flip=False, #布尔值。随机垂直翻转
rescale=None, #重缩放因子。默认为 None。如果是 None 或 0,不进行缩放,否则将数据乘以所提供的值(在应用任何其他转换之前)
preprocessing_function=None, #应用于每个输入的函数。这个函数会在任何其他改变之前运行。这个函数需要一个参数:一张图像(秩为 3 的 Numpy 张量),并且应该输出一个同尺寸的 Numpy 张量。
data_format=None, #图像数据格式,{"channels_first", "channels_last"} 之一。"channels_last" 模式表示图像输入尺寸应该为 (samples, height, width, channels),"channels_first" 模式表示输入尺寸应该为 (samples, channels, height, width).如果你从未设置它,那它就是 "channels_last"
validation_split=0.0, #浮点数。Float. 保留用于验证的图像的比例(严格在0和1之间)
dtype=None, #生成数组使用的数据类型
)

使用实时数据增强生成一批张量图像数据。数据将被循环(分批)。

该类有多种API,如flow、flow_from_directory等等,明天我们将进行相关的讲解。

今天我们学习了一部分图像数据预处理的函数和类,大家可以动手尝试将它们运用到代码当中,及时巩固,谢谢大家的阅读,明天见!

图像数据预处理(二)

前言

tf.keras.preprocessing这个库用来将原始数据生成固定可用于训练的格式tf.data.Dataset

flow method

1
2
3
4
5
6
7
8
9
10
11
12
13
from tensorflow.keras.preprocessing import ImageDataGenerator
ImageDataGenerator.flow(
x,
y=None,
batch_size=32,
shuffle=True,
sample_weight=None,
seed=None,
save_to_dir=None,
save_prefix="",
save_format="png",
subset=None,
)

这个函数用于生成batch数据,同时增加了一些辅助功能,其中shuffle、sample_weight、seed、subset在前面已经说过了,save_to_dir、save_prefix是配套使用的,用于将生成的数据保存在硬盘上。

flow_from_dataframe method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ImageDataGenerator.flow_from_dataframe(
dataframe,
directory=None,
x_col="filename",
y_col="class",
weight_col=None,
target_size=(256, 256),
color_mode="rgb",
classes=None,
class_mode="categorical",
batch_size=32,
shuffle=True,
seed=None,
save_to_dir=None,
save_prefix="",
save_format="png",
subset=None,
interpolation="nearest",
validate_filenames=True,
**kwargs
)

用于处理pandas格式下的dataframe,并返回标准化的batch数据。directory实际上是目标程序运行时要读取的路径,在这个函数中相当于是输出路径。在 https://medium.com/@vijayabhaskar96/tutorial-on-keras-imagedatagenerator-with-flow-from-dataframe-8bd5776e45c1 这篇博客中提到了另一种使用该函数的方法:先在命令行中执行 pip uninstall keras-preprocessingpip install git+https://github.com/keras-team/keras-preprocessing.git ,然后使用from keras_preprocessing.image import ImageDataGenerator调用这个类即可。

关于一个使用该函数的例子如下所示,其中数据集可以到这里获取: https://www.kaggle.com/c/cifar-10/data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import pandas as pd
df=pd.read_csv(r".\train.csv")
datagen=ImageDataGenerator(rescale=1./255)
train_generator=datagen.flow_from_dataframe(dataframe=df, directory=".\train_imgs", x_col="id", y_col="label", class_mode="categorical", target_size=(32,32), batch_size=32)
model = Sequential()
model.add(Conv2D(32, (3, 3), padding='same',
input_shape=(32,32,3)))
model.add(Activation('relu'))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))

model.compile(optimizers.rmsprop(lr=0.0001),
loss="categorical_crossentropy", metrics=["accuracy"])
STEP_SIZE_TRAIN=train_generator.n//train_generator.batch_size
STEP_SIZE_VALID=valid_generator.n//valid_generator.batch_size
model.fit_generator(generator=train_generator,
steps_per_epoch=STEP_SIZE_TRAIN,
validation_data=valid_generator,
validation_steps=STEP_SIZE_VALID,
epochs=10)

flow_from_directory method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ImageDataGenerator.flow_from_directory(
directory,
target_size=(256, 256),
color_mode="rgb",
classes=None,
class_mode="categorical",
batch_size=32,
shuffle=True,
seed=None,
save_to_dir=None,
save_prefix="",
save_format="png",
follow_links=False,
subset=None,
interpolation="nearest",
)

从某个目录直接获取数据并生成batch数据,但是数据目录需要满足特定的格式,举个例子,更加详细的样例代码见 https://gist.github.com/fchollet/0830affa1f7f19fd47b06d4cf89ed44d :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
data/
train/
dogs/
dog001.jpg
dog002.jpg
...
cats/
cat001.jpg
cat002.jpg
...
validation/
dogs/
dog001.jpg
dog002.jpg
...
cats/
cat001.jpg
cat002.jpg
...

文本数据预处理

前言

keras提供的数据预处理总共有三种类型,他们分别用于处理图片数据、时间序列数据、文本数据,这一次要解析的就是文本数据地预处理,这些api将原始数据按照灵活的格式读入,输出指定格式的可直接输入到模型中的数据。用于处理时间序列数据的有两个,分别是text_dataset_from_directory``和``Tokenizer

text_dataset_from_directory function

1
tf.keras.preprocessing.text_dataset_from_directory(    directory,    labels="inferred",    label_mode="int",    class_names=None,    batch_size=32,    max_length=None,    shuffle=True,    seed=None,    validation_split=None,    subset=None,    follow_links=False,)

这个函数将原始的文本数据(目前只支持.txt格式)转换成tf.data.Dataset类型。

如果你输入的directory目录结构是这样的:

1
main_directory/...class_a/......a_text_1.txt......a_text_2.txt...class_b/......b_text_1.txt......b_text_2.txt
1
那么调用text_dataset_from_directory(main_directory, labels='inferred')将会从子目录的批量文本class_a和class_b中返回一个tf.data.Dataset,其中标签0/1分别代表class_a和class_b.

参数解释

directory:数据所在的目录。如果参数lables被设置为“inferred”,就如上述例子中一样,这个目录包含子目录,否则忽略目录结构。

labels:训练集的标签。

label_mode:标签的数据类型。

class_name:仅当labels是“inferred”时有效。这是类名称的明确列表(必须匹配子目录的名称)。用于控制类的顺序(否则使用字母数字顺序)。

batch_size:数据批次的大小,默认值32。

max_length:文本字符串最长的长度,超过这个值会被截断。

shuffle:是否将生成的序列数据打乱顺序

seed:随机种子,只有使用shuffle时才用

valiation_split:0-1之间的浮点数,保留用于验证的数据片段。

subset:“training”或“valiation”之一。表示valiation_split后的子集将被作为何种用途。

follow_links:是否访问符号链接指向的子目录。

Tokenizer class

1
tf.keras.preprocessing.text.Tokenizer(    num_words=None,    filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n',    lower=True,    split=" ",    char_level=False,    oov_token=None,    document_count=0,    **kwargs)

这个类允许对文本语料进行向量化,方法是将每个文本转换为一个整数序列(每个整数是字典中一个token的索引),或者转换为一个矢量。

参数解释

num_words:基于单词频率的保留单词的最大数量。只保留最常见的(num_word -1)个词。 filters:一个字符串,其中每个元素是将从文本中过滤掉的字符。默认是所有的标点符号,加上制表符和换行符,减去'字符。 lower:是否将文本转换为小写字母。 split:分词的分隔符。 char_level:如果为真,每个字符将被视为一个标记。 oov_token:如果给定,它将被添加到word_index中,用于替换text_to_sequence调用期间词汇表之外的单词

默认情况下,所有的标点符号都被删除,文本变成由空格分隔的单词组成的序列(单词可能包括“字符”)。然后这些序列被分割成记号列表。然后它们将被索引或向量化。

优化器(一)

前言

从这篇文章开始,分析神经网络的优化器以及其在keras框架中的实现。在前面讲过,神经网络的编写分成:准备数据、搭建网络结构、配置算法参数这几步,其中配置算法参数是关键的一步,而optimizer的配置是其中的重中之重。 神经网络的本质目的就是寻找合适的参数,使得损失函数的值尽可能小,该过程为称为最优化。解决这个问题的算法称为优化器。

理论模型

梯度下降法更新参数是常用的手段,举一个很简单的例子作为说明。

  • 现有三组训练数据\((x,\hat{y})\)如下:
1
2
3
(0,1)
(1,2)
(2,3)
  • 构建神经网络为 \(y = ax + b\)

  • 计算损失函数 \(loss(L) = \frac{1}{2}\sum(y - \hat{y})^2 = \frac{1}{2}\sum(ax + b - \hat{y})^2 = \frac{1}{2}(b - 1)^2 + \frac{1}{2}(a + b - 2)^2 + \frac{1}{2}(2a+b-3)^2\)

  • 计算偏导数(梯度)

    • \(\frac{\partial{L} }{\partial{a} } = \sum x(ax + b - \hat{y})\)
    • \(\frac{\partial{L} }{\partial{b} } = \sum (ax + b - \hat{y})\)
  • 更新参数

    • \(a \gets a - \alpha * \frac{\partial{L} }{\partial{a} }\)
    • \(b \gets b - \alpha * \frac{\partial{L} }{\partial{b} }\)

\(a,b\)初始化为2,运行结果如下所示:

1
2
3
4
5
6
7
1.2 1.4
0.98 1.22
0.924 1.16
0.914 1.1348
0.9166 1.1202
0.9222 1.1091
0.9284 1.0997

source code:

1
2
3
4
5
6
7
8
9
10
11
12
13
global a,b,alpha
a = 2;b = 2;alpha = 0.1;train = [[0,1],[1,2],[2,3]]
def update():
global a,b,alpha
delta_a = 0;delta_b = 0
for tmp in train:
x = tmp[0];y = tmp[1]
delta_a += x * (a * x + b - y);delta_b += (a * x + b - y)
a = a - alpha * delta_a;b = b - alpha * delta_b
return
while (a - 1) > 0.1 or (b - 1) > 0.1:
update()
print(round(a,4),round(b,4))

keras接口

基本optimizer

在调用compile函数时,在函数的参数中给定优化器,可以通过实例化的方法,也可以通过字符串声明。

1
2
3
# 实例化
opt = keras.optimizers.Adam(learning_rate=0.01)
model.compile(loss='categorical_crossentropy', optimizer=opt)
1
2
# 字符串声明
model.compile(loss='categorical_crossentropy', optimizer='adam')

自定义层

在新版本的keras中可以调用apply_gradients()函数,通过将gradients,model作为传入参数更新参数,官网给出的代码如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
optimizer = tf.keras.optimizers.Adam()

# Iterate over the batches of a dataset.
for x, y in dataset:
# Open a GradientTape.
with tf.GradientTape() as tape:
# Forward pass.
logits = model(x)
# Loss value for this batch.
loss_value = loss_fn(y, logits)

# Get gradients of loss wrt the weights.
gradients = tape.gradient(loss_value, model.trainable_weights)

# Update the weights of the model.
optimizer.apply_gradients(zip(gradients, model.trainable_weights))

Learning rate decay / scheduling

通过这个接口,可以设定学习率随着时间变化,不过回调函数也可以做到这一点,后面会分析。

结尾

keras提供了8种具体的优化器,下面的几篇关于keras文章会对其进行讲解。

优化器(二)

各位读者朋友大家好,昨天我们已经了解了什么是优化器以及优化器的作用,今天我们就来看看其中的SGD和RMSprop优化器。

SGD类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
tf.keras.optimizers.SGD(
learning_rate=0.01, #学习率。默认为0.01
momentum=0.0, #float >= 0. 参数,用于加速 SGD 在相关方向上前进,并抑制震荡。默认为0
nesterov=False, #boolean. 是否使用 Nesterov 动量
name="SGD", #应用梯度时创建的操作的可选名称前缀。默认为“SGD”
**kwargs #关键字参数。允许是“clipnorm”或“clipvalue”之一clipnorm“(float)按范数剪裁梯度;“clipvalue”(float)按值剪裁梯度
)
'''
当momentum等于0时参数w根据梯度g的更新规则如下:
w = w - learning_rate * g

当momentum大于0时的更新规则为:
velocity = momentum * velocity - learning_rate * g
w = w * velocity

当nesterov为False时更新规则如下:
velocity = momentum * velocity - learning_rate * g
w = w + momentum * velocity - learning_rate * g
'''

随机梯度下降(Stochastic gradient descent)算法每次从训练集中随机选择一个样本来进行学习,公式如下。

1192699-20180310214057443-2087742064

批量梯度下降算法(BGD)每次都会使用全部训练样本,因此这些计算是冗余的,因为每次都使用完全相同的样本集。而随机梯度下降算法每次只随机选择一个样本来更新模型参数,因此每次的学习是非常快速的,并且可以进行在线更新。

随机梯度下降代码如下

1
2
3
4
5
for i in range(nb_epochs):
np.random.shuffle(data)
for example in data:
params_grad = evaluate_gradient(loss_function, example, params)
params = params - learning_rate * params_grad

随机梯度下降最大的缺点在于每次更新可能并不会按照正确的方向进行,因此可以带来优化波动(扰动),如下图:

1192699-20180310214248005-2068714250

随机梯度下降是通过每个样本来迭代更新一次,如果样本量很大的情况,那么可能只用其中部分的样本,就已经将theta迭代到最优解了。缺点是SGD的噪音较BGD要多,使得SGD并不是每次迭代都向着整体最优化方向所以虽然训练速度快,但是准确度下降,并不是全局最优虽然包含一定的随机性,但是从期望上来看,它是等于正确的导数的。

不过从另一个方面来看,随机梯度下降所带来的波动有个好处就是,对于类似盆地区域(即很多局部极小值点)那么这个波动的特点可能会使得优化的方向从当前的局部极小值点跳到另一个更好的局部极小值点,这样便可能对于非凸函数,最终收敛于一个较好的局部极值点,甚至全局极值点。

例子:

1
2
3
4
5
6
7
>>> opt = tf.keras.optimizers.SGD(learning_rate=0.1)
>>> var = tf.Variable(1.0)
>>> loss = lambda: (var ** 2)/2.0 # d(loss)/d(var1) = var1
>>> step_count = opt.minimize(loss, [var]).numpy()
>>> # Step is `- learning_rate * grad`
>>> var.numpy()
0.9

RMSprop类

1
2
3
4
5
6
7
8
9
tf.keras.optimizers.RMSprop(
learning_rate=0.001, #学习率。默认为0.01
rho=0.9, #历史或未来梯度的dicounting因子,默认为0.9
momentum=0.0, #标量或标量张量。默认为0
epsilon=1e-07, #数值稳定性的一个小常数。默认为1e-7
centered=False, #布尔值。如果为真,则通过梯度的估计方差对梯度进行规范化;如果为假,则通过未居中的第二时刻对梯度进行规范化。设置为True可能有助于训练,但在计算和内存方面代价更大一些。默认为False
name="RMSprop", #应用梯度时创建的操作的可选名称前缀。默认为“RMSprop”
**kwargs #关键字参数。允许是“clipnorm”或“clipvalue”之一clipnorm“(float)按范数剪裁梯度;“clipvalue”(float)按值剪裁梯度
)

建议使用优化器的默认参数 (除了学习率 lr,它可以被自由调节)

这个优化器通常是训练循环神经网络RNN的不错选择。

RMSProp算法的全称叫 Root Mean Square Prop,是Geoffrey E. Hinton在Coursera课程中提出的一种优化算法。所谓的摆动幅度就是在优化中经过更新之后参数的变化范围,如下图所示,绿色的为RMSProp优化算法所走的路线。

20170923134334368

为了进一步优化损失函数在更新中存在摆动幅度过大的问题,并且进一步加快函数的收敛速度,RMSProp算法对权重 W 和偏置 bb的梯度使用了微分平方加权平均数。 其中,假设在第 t 轮迭代过程中,各个公式如下所示:

捕获

算法的主要思想就用上面的公式表达完毕了。在上面的公式中sdw和sdb分别是损失函数在前t−1轮迭代过程中累积的梯度梯度动量,ββ 是梯度累积的一个指数。所不同的是,RMSProp算法对梯度计算了微分平方加权平均数。这种做法有利于消除了摆动幅度大的方向,用来修正摆动幅度,使得各个维度的摆动幅度都较小。另一方面也使得网络函数收敛更快。(比如当 dW或者 db中有一个值比较大的时候,那么我们在更新权重或者偏置的时候除以它之前累积的梯度的平方根,这样就可以使得更新幅度变小)。为了防止分母为零,使用了一个很小的数值 ϵ来进行平滑,一般取值为10−8。

例子:

1
2
3
4
5
6
>>> opt = tf.keras.optimizers.RMSprop(learning_rate=0.1)
>>> var1 = tf.Variable(10.0)
>>> loss = lambda: (var1 ** 2) / 2.0 # d(loss) / d(var1) = var1
>>> step_count = opt.minimize(loss, [var1]).numpy()
>>> var1.numpy()
9.683772

今天我们学习了优化器中的SGD和RMSprop类,优化器对于神经网络来说非常重要,不同的优化方式有不同的效果,应该针对样本进行选择,以实现更好的优化效果,下周我们将继续介绍剩下的优化器,希望大家在学习之余也多多查阅相关资料,更加牢固地掌握这一知识。谢谢大家的阅读。

优化器(三)

各位读者朋友大家好,上周我们已经学习了一部分Keras的优化器,这周我们将完成优化器剩余部分的学习。

今天我们将学习几种自适应学习率优化算法。自适应学习率优化算法针对于机器学习模型的学习率,传统的优化算法要么将学习率设置为常数要么根据训练次数调节学习率,极大忽视了学习率其他变化的可能性。然而,学习率对模型的性能有着显著的影响,因此需要采取一些策略来想办法更新学习率,从而提高训练速度。

Adam类

1
2
3
4
5
6
7
8
9
10
11
12
13
tf.keras.optimizers.Adam(
learning_rate=0.001, #学习率。默认为0.001
beta_1=0.9, #浮点数或常量浮点张量,或不带参数并返回要使用的实际值的可调用函数。一阶矩估计的指数衰减率。大于0小于1,默认为0.9
beta_2=0.999, #浮点数或常量浮点张量,或不带参数并返回要使用的实际值的可调用函数。二阶矩估计的指数衰减率。大于0小于1,默认为0.999
epsilon=1e-07, #数值稳定性的一个小常数。默认为1e-7
amsgrad=False, #布尔值。是否应用该算法的AMSGrad变量。默认为False
name="Adam", #应用梯度时创建的操作的可选名称前缀。默认为“Adam”
**kwargs #关键字参数。允许是“clipnorm”或“clipvalue”之一clipnorm“(float)按范数剪裁梯度;“clipvalue”(float)按值剪裁梯度
)
'''
epsilon的默认值1e-7通常可能不是一个好的默认值。例如,当在ImageNet上训练初始网络时,当前的最佳选择是1.0或0.1。
此算法的稀疏实现(当梯度为indexedSlices对象时使用,通常是因为tf.gather或者前向过程中的嵌入查找)确实会对可变切片应用momentum,即使它们没有在前向过程中使用(意味着它们的梯度等于零)。momentum衰减(beta_1)也适用于整个momentum累加器。这意味着稀疏行为等同于稠密行为(与某些momentum实现不同,后者忽略momentum,除非实际使用可变切片)。
'''

实现Adam算法的优化器。

Adam优化是一种基于随机估计的一阶和二阶矩的随机梯度下降方法。该方法计算效率高,内存需求少,不影响梯度的对角线重缩放,并且非常适合数据/参数较大的问题。

Adam中动量直接并入了梯度一阶矩(指数加权)的估计。相比于缺少修正因子导致二阶矩估计可能在训练初期具有很高偏置的RMSProp,Adam包括偏置修正,修正从原点初始化的一阶矩(动量项)和(非中心的)二阶矩估计。

Adam算法策略可以表示为:

捕获3

其中,mt和vt分别为一阶动量项和二阶动量项。β1,β2为动力值大小通常分别取0.9和0.999;mt,vt分别为各自的修正值。Wt表示t时刻即第t迭代模型的参数,gt=ΔJ(Wt)表示t次迭代代价函数关于W的梯度大小;ϵ是一个取值很小的数(一般为1e-8)为了避免分母为0。

例子:

1
2
3
4
5
6
7
>>> opt = tf.keras.optimizers.Adam(learning_rate=0.1)
>>> var1 = tf.Variable(10.0)
>>> loss = lambda: (var1 ** 2)/2.0 # d(loss)/d(var1) == var1
>>> step_count = opt.minimize(loss, [var1]).numpy()
>>> # The first step is `-learning_rate*sign(grad)`
>>> var1.numpy()
9.9

Adagrad类

1
2
3
4
5
6
7
tf.keras.optimizers.Adagrad(
learning_rate=0.001, #学习率。默认为0.001
initial_accumulator_value=0.1, #浮点值。accumulator起始值必须非负
epsilon=1e-07, #数值稳定性的一个小常数。默认为1e-7
name="Adadelta", #应用梯度时创建的操作的可选名称前缀。默认为“Adadelta”
**kwargs #关键字参数。允许是“clipnorm”或“clipvalue”之一clipnorm“(float)按范数剪裁梯度;“clipvalue”(float)按值剪裁梯度
)

实现Adagrad算法的优化器。

AdaGrad算法,独立地适应所有模型参数的学习率,缩放每个参数反比于其所有梯度历史平均值总和的平方根。具有代价函数最大梯度的参数相应地有个快速下降的学习率,而具有小梯度的参数在学习率上有相对较小的下降。

AdaGrad算法优化策略一般可以表示为:

捕获

假定一个多分类问题,i表示第i个分类,t表示第t迭代同时也表示分类ii累计出现的次数。η0表示初始的学习率取值一般为0.01,ϵ是一个取值很小的数(一般为1e-8)为了避免分母为0。Wt表示t时刻即第t迭代模型的参数,gt,i=ΔJ(Wt,i)表示t时刻,指定分类i,代价函数J(⋅)关于W的梯度。

从表达式可以看出,对出现比较多的类别数据,Adagrad给予越来越小的学习率,而对于比较少的类别数据,会给予较大的学习率。因此Adagrad适用于数据稀疏或者分布不平衡的数据集。

Adagrad 的主要优势在于不需要人为的调节学习率,它可以自动调节;缺点在于,随着迭代次数增多,学习率会越来越小,最终会趋近于0。

Adadelta类

1
2
3
4
5
6
7
tf.keras.optimizers.Adadelta(
learning_rate=0.001, #学习率。默认为0.001
rho=0.95, #一个张量或浮点数。衰减率
epsilon=1e-07, #数值稳定性的一个小常数。默认为1e-7
name="Adadelta", #应用梯度时创建的操作的可选名称前缀。默认为“Adadelta”
**kwargs #关键字参数。允许是“clipnorm”或“clipvalue”之一clipnorm“(float)按范数剪裁梯度;“clipvalue”(float)按值剪裁梯度
)

实现Adadelta算法的优化器。

Adadelta优化是一种随机梯度下降方法,它基于每个维度的自适应学习率来解决两个缺点:

  • 整个训练期间学习率的持续下降
  • 需要手动选择的整体学习率

Adadelta是Adagrad的更强大的扩展,它基于梯度更新的移动窗口来调整学习率,而不是累积所有过去的梯度。这样,即使已完成许多更新,Adadelta仍可继续学习。在此版本中,它可以像大多数其他Keras优化器一样设置初始学习率。

AdaGrad算法和RMSProp算法都需要指定全局学习率,Adadelta算法结合两种算法每次参数的更新步长。它的算法策略可以表示为:

捕获1

其中Wt为第t次迭代的模型参数,gt=ΔJ(Wt)为代价函数关于W的梯度。E[g2]t表示前t次的梯度平方的均值。捕获2表示前t−1次模型参数每次的更新步长累加求根。

Adadelta优势在于在模型训练的初期和中期,Adadelta表现很好,加速效果不错,训练速度快。缺点在模型训练的后期,模型会反复地在局部最小值附近抖动。

优化器对于神经网络来说非常重要,不同的优化方式有不同的效果,应该针对样本进行选择,以实现更好的优化效果,希望大家在学习之余也多多查阅相关资料,更加牢固地掌握这一知识。谢谢大家的阅读。

优化器(四)

写在开篇

各位读者朋友大家好,昨天我们已经学习了一部分Keras的优化器,今天我们将完成优化器剩余部分的学习。

自适应学习率优化算法针对于机器学习模型的学习率,然而,学习率对模型的性能有着显著的影响,因此需要采取一些策略来想办法更新学习率,从而提高训练速度。

类型说明

Adamax类

1
tf.keras.optimizers.Adamax(    learning_rate=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-07, name="Adamax", **kwargs)

它是Adam算法基于无穷范数(infinity norm)的变种。默认参数遵循论文中提供的值。

参数w随着梯度g更新的更新规则如下:

1
2
3
4
5
t += 1
m = beta1 * m + (1 - beta) * g
v = max(beta2 * v, abs(g))
current_lr = learning_rate / (1 - beta1 ** t)
w = w - current_lr * m / (v + epsilon)

伪代码:

img

参数

  • learning_rate: 学习率。
  • beta_1: 第1阶矩估计的指数衰减率。
  • beta_2: 指数加权无穷范数的指数衰减率。
  • epsilon: float >= 0. 模糊因子. 若为 None, 默认为 K.epsilon()。与“Adam”类似,加入的epsilon是为了数值稳定性。
  • name: 应用梯度时创建的操作的可选名称。默认为“Adamax”。
  • **kwargs: 关键字参数。允许是'"clipnorm" 或 "clipvalue" 。"clipnorm" 按照norm裁剪梯度值;"clipvalue"按照value裁剪梯度的值。

Nadam类

1
tf.keras.optimizers.Nadam(  learning_rate=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-07, name="Nadam", **kwargs)

Adam本质上像是带有动量项的RMSprop,Nadam就是带有Nesterov 动量的Adam RMSprop.

img

其中默认参数来自于论文,推荐不要对默认参数进行更改。

可以看出,Nadam对学习率有了更强的约束,同时对梯度的更新也有更直接的影响。一般而言,在想使用带动量的RMSprop,或者Adam的地方,大多可以使用Nadam取得更好的效果。

Ftrl类

1
tf.keras.optimizers.Ftrl(    learning_rate=0.001,    learning_rate_power=-0.5,    initial_accumulator_value=0.1,    l1_regularization_strength=0.0,    l2_regularization_strength=0.0,    name="Ftrl",    l2_shrinkage_regularization_strength=0.0,    **kwargs)

FTRL(Follow-the-regularized-Leader)优化器

实验证明,L1-FOBOS这一类基于梯度下降的方法有较高的精度,但是L1-RDA却能在损失一定精度的情况下产生更好的稀疏性。FTRL综合考虑了FOBOS和RDA对于正则项和W的限制,从而结合了二者的优点。

Arguments

  • learning_rate_power: 控制在训练期间学习率的降低。如果想用固定的学习速率,请置为0。
  • initial_accumulator_value: accumulators的初始值。
  • l2_shrinkage_regularization_strength: 这个L2收缩是一个量值惩罚。当输入是稀疏的时候,收缩只会发生在活跃的权重上。

Ftrl在处理诸如逻辑回归之类的带非光滑正则化项(例如1范数,做模型复杂度控制和稀疏化)的凸优化问题上性能非常出色。

写在文末

优化器对于神经网络来说非常重要,不同的优化方式有不同的效果,应该针对样本进行选择,以实现更好的优化效果,希望大家在学习之余也多多查阅相关资料,更加牢固地掌握这一知识。谢谢大家的阅读。

回调函数(一)

前言

回调函数是在训练阶段可以执行各种活动的对象,训练阶段比如:每一个\(epoch\)的开头或者结尾,每一个\(batch\)之前或者之后等。接下来的讲解顺序大致就是:API的调用方法,十一个API的分析,以及自定义回调函数的例子。

使用回调函数,我们可以做到一下几件事情:

  • 每几个\(batch\)之后,将\(metrics\)信息输出到日志文件中从而对训练过程进行监控
  • 定期将当前的模型保存到磁盘中
  • 执行\(early\ stopping\)
  • 训练期间查看模型的内部状态和统计信息
  • \(\dots\ \dots\)

API调用方法

一般是在\(fit()\)函数中调用,使用函数的\(callbacks\)参数进行传递,可以传递一个回调函数的对象,也可以是一个回调函数的列表,一个简单的例子如下所示,具体的每个回调函数都会在之后讲解。

1
2
3
4
5
6
my_callbacks = [
tf.keras.callbacks.EarlyStopping(patience=2),
tf.keras.callbacks.ModelCheckpoint(filepath='model.{epoch:02d}-{val_loss:.2f}.h5'),
tf.keras.callbacks.TensorBoard(log_dir='./logs'),
]
model.fit(dataset, epochs=10, callbacks=my_callbacks)

API

Base Callback class

所有回调函数的抽象基类,如果是自定义的话,应该继承这个类,但是在API中没有直接的用处。该类有两个参数,一个是\(params\),包含了\(batch size,\ number\ of\ epochs\)等,另一个是\(model\)

1
tf.keras.callbacks.Callback()

Model Checkpoint

以一定的频率保存模型或者模型的参数,这些信息将会被保存在一个\(check\ point\)文件中。被保存的模型后面可以被读取出来,继续进行训练。

更加具体的,如下所示:

  • 是否只保存到目前为止性能最好的一组参数,或者忽视性能,每个\(epoch\)结束的时候都保存一下
  • 在上一个功能中涉及到“性能最好”这一说法,因此就要对这个“最好”,进行定义,可以选择一个指标的最大或者最小作为最好
  • 指定保存的频率,目前支持的频率为每个\(epoch\)结束之后或者在固定的训练批次之后进行保存
  • 指定只保存模型的参数或者整个模型都保存下来
1
2
3
4
5
6
7
8
9
10
tf.keras.callbacks.ModelCheckpoint(
filepath,
monitor="val_loss",
verbose=0,
save_best_only=False,
save_weights_only=False,
mode="auto",
save_freq="epoch",
**kwargs
)

各个参数的含义如下所示:

  • \(filepath\):模型存储的路径,同时可以包含一些格式化选项,比如\(weights.{epoch:02d}-{val_loss:.2f}.hdf5\),对于这样的一个路径名,\(02d,.2f\)将会被替换为相应的指标值

  • \(monitor\):监控指标,决定了”最好“

  • \(verbose\):设置运行时输出信息的详细程度,0或者1

  • \(save\_best\_only\):布尔变量,如果设置为真,则最终结果中只保存效果最好的一组参数

  • \(mode\):字符串,\(auto,min,max\)中的一个,如果是监控的指标是正确率的话,一般是最大,损失函数的话一般是最小化,不过这个一般用\(auto\),程序可以自己判断

  • \(save\_weights\_only\):布尔变量,字面意思

  • \(save\_freq\)\('epoch'\)或者一个整数,前者对应每个\(epoch\)保存一次,后者对应若干个\(batch\)之后保存

在这里给出一个简单的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
EPOCHS = 10
checkpoint_filepath = '/tmp/checkpoint'
model_checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(
filepath=checkpoint_filepath,
save_weights_only=True,
monitor='val_acc',
mode='max',
save_best_only=True)

# Model weights are saved at the end of every epoch, if it's the best seen
# so far.
model.fit(epochs=EPOCHS, callbacks=[model_checkpoint_callback])

# The model weights (that are considered the best) are loaded into the model.
model.load_weights(checkpoint_filepath)

Tensor Board

对训练过程中的数据或者结果进行可视化,具体的如下所示:

  • 指标摘要图
  • 训练图可视化
  • 激活直方图
  • 采样分析

关于tensorboard,在后面会进行更加详细的分析。

Early Stopping

当某个指标已经停止优化时,不过这里用到的指标必须是调用compile函数时必须包含的指标,对于一些没有默认包含的指标,一定要用metrics参数指明。

1
2
3
4
5
6
7
8
9
tf.keras.callbacks.EarlyStopping(
monitor="val_loss",
min_delta=0,
patience=0,
verbose=0,
mode="auto",
baseline=None,
restore_best_weights=False,
)
  • \(monitor\):监控的指标
  • \(min\_delta\):认为在某一指标上提升多少才算一次优化
  • \(patience\):连续多少次没有优化之后就停止
  • \(verbose\):同上
  • \(mode\):同上
  • \(baseline\):阈值,如果结果没有达到这个值,即使\(patience\)到了也不停
  • \(restore\_best\_weights\):是否保存最优结果,与上面的功能有些重叠

Learning Rate Scheduler

可以在训练的过程中使用变化的学习率

1
tf.keras.callbacks.LearningRateScheduler(schedule, verbose=0)

这里的第一个参数是一个函数,函数的传入参数是\(epoch,lr\),其中\(epoch\)是当前的训练轮数,\(lr\)是当前的学习率,返回一个新的学习率,可以自定义函数,根据当前的状况,决定学习率,如果想要实现一个根据loss的变化确定的学习率,也可以设置一个全局变量来解决这个问题。

1
2
3
4
5
6
7
8
9
10
def scheduler(epoch, lr):
if epoch < 10:
return lr
else:
return lr * tf.math.exp(-0.1)
model = tf.keras.models.Sequential([tf.keras.layers.Dense(10)])
model.compile(tf.keras.optimizers.SGD(), loss='mse')
callback = tf.keras.callbacks.LearningRateScheduler(scheduler)
history = model.fit(np.arange(100).reshape(5, 20), np.zeros(5),
epochs=15, callbacks=[callback], verbose=0)

回调函数(二)| tensorboard函数

回调函数 tensorboard

机器学习过程中可视化的有力工具,在keras中的API如下所示:

1
2
3
4
5
6
7
8
9
10
11
tf.keras.callbacks.TensorBoard(
log_dir="logs",
histogram_freq=0,
write_graph=True,
write_images=False,
update_freq="epoch",
profile_batch=2,
embeddings_freq=0,
embeddings_metadata=None,
**kwargs
)

参数解释

  • \(log\_dir\):存储日志文件的路径,这些日志文件将会被tensorboard解析
  • \(histogram\_freq\):模型个层的激活和权重的直方图计算的频率,单位是epoch,如果被置为0,就不进行该计算
  • \(write\_graph\):是否对graph进行可视化,如果使用该选项,日志文件会变得很大
  • \(write\_images\):是否可视化模型参数
  • \(update\_freq\):记录指标和loss的频率,可以是\('batch','epoch'\)或者一个整数,如果是一个整数就是说若干batch记录一次,如果记录的太频繁会降低记录速度
  • \(profile\_batch\):分析批次中以采样计算特征

使用方法

编写keras程序,比如一段主程序如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def test_tensor(epochs = 100):
# 数据预处理--the 1st core step
letter_num = len(APPEARED_LETTERS)
data, label = load_data(pic_folder) # 图片数据预处理
data_train, data_test, label_train, label_test = \
train_test_split(data, label, test_size=0.1, random_state=0)
label_categories_train = to_categorical(label_train, letter_num) # one-hot编码
label_categories_test = to_categorical(label_test, letter_num) # one-hot编码
# x_train,y_train,x_test,y_test 是表示神经网络的输入输出的常用表示
x_train, y_train, x_test, y_test = data_train, label_categories_train,data_test, label_categories_test
# 定义神经网络结构--the 2nd core step
model = None
try:
model = get_model()
except Exception as e:
print('The model you defined has some bugs')
print(traceback.print_exc())
choice = input('backup model will be used, please input 1 for continuing,0 for stopping the program.')
if choice == 1:
model = backup_model()
elif choice == 0:
print('exit!')
exit()
# 编译模型--the 3rd core step
model.compile(
optimizer='adadelta',
loss=['categorical_crossentropy'],
metrics=['accuracy'],
)
# 定义训练中间结果存储
check_point = ModelCheckpoint(
os.path.join(weight_folder, '{epoch:02d}.hdf5'))

tensorboard_callback = TensorBoard(log_dir="logs")
# 训练神经网络--the 4th core step
his = model.fit(
x_train, y_train, batch_size=128, epochs=epochs,
validation_split=0.1, callbacks=[check_point,tensorboard_callback],
)
return his

程序运行结束之后,在文件的同目录下发现多出来一个logs文件夹,这个文件夹的绝对路径命名为\(XXX\),随后在命令行中输入如下命令行tensorboard --logdir=XXX,待tensorboard启动后,使用浏览器中的本地服务器,进入https://localhost:6006/就可以看到tensorboard处理的可视化结果了,如图所示

1593781912781

目录栏目有两个选项,scalar如下所示,展示的metrics的变化情况

1593781856532

graph展示的是模型的结构,可视化如下所示:

1593782009989

完整代码已经被上传在https://github.com/1173710224/tensorboard-usage/tree/master可以通过调整模型的参数查看具体每个参数的作用

回调函数(三)

今天我们继续来学习Keras中的回调函数剩下的部分。

ReduceLROnPlateau类

1
2
3
4
5
6
7
8
9
10
11
tf.keras.callbacks.ReduceLROnPlateau(
monitor="val_loss", #被监测的数据
factor=0.1, #学习率被降低的因数。新的学习速率 = 学习速率 * 因数
patience=10, #没有进步的训练轮数,在这之后学习率会被降低
verbose=0, #整数。0:安静,1:更新信息
mode="auto", # {auto, min, max} 其中之一。如果是 min 模式,学习率会被降低如果被监测的数据已经停止下降; 在 max 模式,学习率会被降低如果被监测的数据已经停止上升; 在 auto 模式,方向会被从被监测的数据中自动推断出来
min_delta=0.0001, #对于测量新的最优化的阀值,只关注巨大的改变
cooldown=0, #在学习率被降低之后,重新恢复正常操作之前等待的训练轮数量
min_lr=0, #学习率的下边界
**kwargs
)

作用:

该函数用于当被监控的数据(monitor)停止提升时,降低学习率。

当数据停止提升时,模型总是会受益于降低2-10倍的学习率。这个回调函数监测一个数据并当这个数据在参数"patience"规定的轮数之后还没有提升的话,那么学习率就会被降低。

例子:

1
2
reduce_lr = ReduceLROnPlateau(monitor='val_loss',factor=0.2,patience=5, min_lr=0.001)
model.fit(X_train, Y_train, callbacks=[reduce_lr])

RemoteMonitor类

1
2
3
4
5
6
7
tf.keras.callbacks.RemoteMonitor(
root="http://localhost:9000", #字符串;目标服务器的根地址
path="/publish/epoch/end/", #字符串;相对于 root 的路径,事件数据被送达的地址
field="data", #字符串;JSON ,数据被保存的领域
headers=None, #字典;可选自定义的 HTTP 的头字段
send_as_json=False, #布尔值;请求是否应该以 application/json 格式发送
)

作用:

可以将事件数据流式处理到服务器。

需要request库。事件被默认发送到 root + '/publish/epoch/end/'。 采用 HTTP POST ,其中的 data 参数是以 JSON 编码的事件数据字典。 如果 send_as_json 设置为 True,请求的 content type 是 application/json。否则,将在表单中发送序列化的 JSON。

LambdaCallback类

1
2
3
4
5
6
7
8
9
tf.keras.callbacks.LambdaCallback(
on_epoch_begin=None, #在每轮开始时被调用
on_epoch_end=None, #在每轮结束时被调用
on_batch_begin=None, #在每批开始时被调用
on_batch_end=None, #在每批结束时被调用
on_train_begin=None, #在模型训练开始时被调用
on_train_end=None, #在模型训练结束时被调用
**kwargs
)

作用:

可以在训练进行中创建简单的自定义回调函数。

这个回调函数在规定的时间被创建。需要注意的是回调函数要求位置型参数,如下:

on_epoch_beginon_epoch_end 要求两个位置型的参数: epoch, logs

on_batch_beginon_batch_end 要求两个位置型的参数: batch, logs

on_train_beginon_train_end 要求一个位置型的参数: logs

例子:

1
2
3
#在每批次前打印批序号
batch_print_callback = LambdaCallback(
on_batch_begin=lambda batch,logs: print(batch))

TerminateOnNaN类

1
tf.keras.callbacks.TerminateOnNaN()

作用:

在遇到一个NaN loss时终止训练。

CSVLogger类

1
2
3
4
5
tf.keras.callbacks.CSVLogger(
filename, #csv 文件的文件名,例如 'run/log.csv'
separator=",", #用来隔离 csv 文件中元素的字符串
append=False #如果文件存在则增加(可以被用于继续训练)。False:覆盖存在的文件
)

作用:

把训练轮结果数据流式处理到csv文件。

支持所有可以被作为字符串表示的值,包括 1D 可迭代数据,例如,np.ndarray。

例子:

1
2
csv_logger = CSVLogger('training.log')
model.fit(X_train, Y_train, callbacks=[csv_logger])

ProgbarLogger类

1
2
3
4
tf.keras.callbacks.ProgbarLogger(
count_mode="samples", #"steps" 或者 "samples"。 进度条是否应该计数看见的样本或步骤(批量)
stateful_metrics=None #可重复使用不应在一个 epoch 上平均的指标的字符串名称。 此列表中的度量标准将按原样记录在 on_epoch_end 中。 所有其他指标将在 on_epoch_end 中取平均值
)

作用:

会把评估以标准输出进行打印

注意:

ValueError:如果采用无效的count_mode会报错

回调函数是一个函数的合集,它在训练的各个阶段都可以使用,使用好回调函数可以让人对网络训练的情况有更直观的了解,并且可以对训练过程进行调整优化,作用很大。所以希望大家能够在实际案例中进行尝试,使自己更深刻地掌握回调函数的用法。谢谢大家观看!

回调函数(四)|自定义回调函数

上周我们整体介绍了回调函数API,今天我们就来讲讲如何编写你自己的回调函数。

所有的回调函数都是keras.callbacks.Callback类的子类,并且覆盖在训练和预测的各个阶段。回调函数对于在训练期间了解模型的内部状态和统计信息很有用。

自定义回调函数方法概述

可以把回调列表(作为关键字参数callbacks)传递给以下模型方法以使用回调函数:

  • keras.Model.fit()
  • keras.Model.evaluate()
  • keras.Model.predict()
全局方法:

on_(train|test|predict)_begin(self, logs=None):在fit/evaluate/predict的开头调用该回调函数。

on_(train|test|predict)_end(self, logs=None):在fit/evaluate/predict结束时调用该回调函数。

批次级方法:

on_(train|test|predict)_batch_begin(self, batch, logs=None):在training/testing/predicting期间处理批次之前调用该回调函数。

on_(train|test|predict)_batch_end(self, batch, logs=None):在training/testing/predicting期间处理批次结束时调用该回调函数。

epoch级方法(仅在train阶段):

on_epoch_begin(self, epoch, logs=None):在train期间的开始时调用。

on_epoch_end(self, epoch, logs=None):在train期间的结束时调用。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#编写该回调函数,使用所有的方法,观察各方法的执行位置
class CustomCallback(keras.callbacks.Callback):
def on_train_begin(self, logs=None):
keys = list(logs.keys())
print("Starting training; got log keys: {}".format(keys))

def on_train_end(self, logs=None):
keys = list(logs.keys())
print("Stop training; got log keys: {}".format(keys))

def on_epoch_begin(self, epoch, logs=None):
keys = list(logs.keys())
print("Start epoch {} of training; got log keys: {}".format(epoch, keys))

def on_epoch_end(self, epoch, logs=None):
keys = list(logs.keys())
print("End epoch {} of training; got log keys: {}".format(epoch, keys))

def on_test_begin(self, logs=None):
keys = list(logs.keys())
print("Start testing; got log keys: {}".format(keys))

def on_test_end(self, logs=None):
keys = list(logs.keys())
print("Stop testing; got log keys: {}".format(keys))

def on_predict_begin(self, logs=None):
keys = list(logs.keys())
print("Start predicting; got log keys: {}".format(keys))

def on_predict_end(self, logs=None):
keys = list(logs.keys())
print("Stop predicting; got log keys: {}".format(keys))

def on_train_batch_begin(self, batch, logs=None):
keys = list(logs.keys())
print("...Training: start of batch {}; got log keys: {}".format(batch, keys))

def on_train_batch_end(self, batch, logs=None):
keys = list(logs.keys())
print("...Training: end of batch {}; got log keys: {}".format(batch, keys))

def on_test_batch_begin(self, batch, logs=None):
keys = list(logs.keys())
print("...Evaluating: start of batch {}; got log keys: {}".format(batch, keys))

def on_test_batch_end(self, batch, logs=None):
keys = list(logs.keys())
print("...Evaluating: end of batch {}; got log keys: {}".format(batch, keys))

def on_predict_batch_begin(self, batch, logs=None):
keys = list(logs.keys())
print("...Predicting: start of batch {}; got log keys: {}".format(batch, keys))

def on_predict_batch_end(self, batch, logs=None):
keys = list(logs.keys())
print("...Predicting: end of batch {}; got log keys: {}".format(batch, keys))

#编写模型并调用此回调函数
def get_model():
model = keras.Sequential()
model.add(keras.layers.Dense(1, input_dim=784))
model.compile(
optimizer=keras.optimizers.RMSprop(learning_rate=0.1),
loss="mean_squared_error",
metrics=["mean_absolute_error"],
)
return model
#执行训练
model = get_model()
model.fit(
x_train,
y_train,
batch_size=128,
epochs=1,
verbose=0,
validation_split=0.5,
callbacks=[CustomCallback()],
)

res = model.evaluate(
x_test, y_test, batch_size=128, verbose=0, callbacks=[CustomCallback()]
)

res = model.predict(x_test, batch_size=128, callbacks=[CustomCallback()])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#执行结果
Starting training; got log keys: []
Start epoch 0 of training; got log keys: []
...Training: start of batch 0; got log keys: []
...Training: end of batch 0; got log keys: ['loss', 'mean_absolute_error']
...Training: start of batch 1; got log keys: []
...Training: end of batch 1; got log keys: ['loss', 'mean_absolute_error']
...Training: start of batch 2; got log keys: []
...Training: end of batch 2; got log keys: ['loss', 'mean_absolute_error']
...Training: start of batch 3; got log keys: []
...Training: end of batch 3; got log keys: ['loss', 'mean_absolute_error']
Start testing; got log keys: []
...Evaluating: start of batch 0; got log keys: []
...Evaluating: end of batch 0; got log keys: ['loss', 'mean_absolute_error']
...Evaluating: start of batch 1; got log keys: []
...Evaluating: end of batch 1; got log keys: ['loss', 'mean_absolute_error']
...Evaluating: start of batch 2; got log keys: []
...Evaluating: end of batch 2; got log keys: ['loss', 'mean_absolute_error']
...Evaluating: start of batch 3; got log keys: []
...Evaluating: end of batch 3; got log keys: ['loss', 'mean_absolute_error']
Stop testing; got log keys: ['loss', 'mean_absolute_error']
End epoch 0 of training; got log keys: ['loss', 'mean_absolute_error', 'val_loss', 'val_mean_absolute_error']
Stop training; got log keys: ['loss', 'mean_absolute_error', 'val_loss', 'val_mean_absolute_error']
Start testing; got log keys: []
...Evaluating: start of batch 0; got log keys: []
...Evaluating: end of batch 0; got log keys: ['loss', 'mean_absolute_error']
...Evaluating: start of batch 1; got log keys: []
...Evaluating: end of batch 1; got log keys: ['loss', 'mean_absolute_error']
...Evaluating: start of batch 2; got log keys: []
...Evaluating: end of batch 2; got log keys: ['loss', 'mean_absolute_error']
...Evaluating: start of batch 3; got log keys: []
...Evaluating: end of batch 3; got log keys: ['loss', 'mean_absolute_error']
...Evaluating: start of batch 4; got log keys: []
...Evaluating: end of batch 4; got log keys: ['loss', 'mean_absolute_error']
...Evaluating: start of batch 5; got log keys: []
...Evaluating: end of batch 5; got log keys: ['loss', 'mean_absolute_error']
...Evaluating: start of batch 6; got log keys: []
...Evaluating: end of batch 6; got log keys: ['loss', 'mean_absolute_error']
...Evaluating: start of batch 7; got log keys: []
...Evaluating: end of batch 7; got log keys: ['loss', 'mean_absolute_error']
Stop testing; got log keys: ['loss', 'mean_absolute_error']
Start predicting; got log keys: []
...Predicting: start of batch 0; got log keys: []
...Predicting: end of batch 0; got log keys: ['outputs']
...Predicting: start of batch 1; got log keys: []
...Predicting: end of batch 1; got log keys: ['outputs']
...Predicting: start of batch 2; got log keys: []
...Predicting: end of batch 2; got log keys: ['outputs']
...Predicting: start of batch 3; got log keys: []
...Predicting: end of batch 3; got log keys: ['outputs']
...Predicting: start of batch 4; got log keys: []
...Predicting: end of batch 4; got log keys: ['outputs']
...Predicting: start of batch 5; got log keys: []
...Predicting: end of batch 5; got log keys: ['outputs']
...Predicting: start of batch 6; got log keys: []
...Predicting: end of batch 6; got log keys: ['outputs']
...Predicting: start of batch 7; got log keys: []
...Predicting: end of batch 7; got log keys: ['outputs']
Stop predicting; got log keys: []

logs字典的用法

logs字典包含loss值,以及批处理或epoch结束时的所有度量。例如包括loss和平均绝对误差。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class LossAndErrorPrintingCallback(keras.callbacks.Callback):
def on_train_batch_end(self, batch, logs=None):
print("For batch {}, loss is {:7.2f}.".format(batch, logs["loss"]))

def on_test_batch_end(self, batch, logs=None):
print("For batch {}, loss is {:7.2f}.".format(batch, logs["loss"]))

def on_epoch_end(self, epoch, logs=None):
print(
"The average loss for epoch {} is {:7.2f} "
"and mean absolute error is {:7.2f}.".format(
epoch, logs["loss"], logs["mean_absolute_error"]
)
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#执行训练结果
For batch 0, loss is 32.45.
For batch 1, loss is 393.79.
For batch 2, loss is 272.00.
For batch 3, loss is 206.95.
For batch 4, loss is 167.29.
For batch 5, loss is 140.41.
For batch 6, loss is 121.19.
For batch 7, loss is 109.21.
The average loss for epoch 0 is 109.21 and mean absolute error is 5.83.
For batch 0, loss is 5.94.
For batch 1, loss is 5.73.
For batch 2, loss is 5.50.
For batch 3, loss is 5.38.
For batch 4, loss is 5.16.
For batch 5, loss is 5.19.
For batch 6, loss is 5.64.
For batch 7, loss is 7.05.
The average loss for epoch 1 is 7.05 and mean absolute error is 2.14.
For batch 0, loss is 40.89.
For batch 1, loss is 42.12.
For batch 2, loss is 41.42.
For batch 3, loss is 42.10.
For batch 4, loss is 42.05.
For batch 5, loss is 42.91.
For batch 6, loss is 43.05.
For batch 7, loss is 42.94.

self.model的用法

除了在调用其中一种方法时接收日志信息外,回调还可以访问与当前一轮fit/evaluate/predict相关联的模型self.model。以下是self.model可以在回调中执行的操作:

  • 设置self.model.stop_training = True为立即中断训练。
  • 修改优化程序的超参数(可通过获取self.model.optimizer),例如self.model.optimizer.learning_rate
  • 定期保存模型。
  • model.predict()在每个时期结束时记录一些测试样本的输出,以在训练期间用作健全性检查。
  • 在每个时期结束时提取中间特征的可视化,以监视模型随时间推移正在学习的内容。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#该回调函数在达到最小损失时停止训练
class EarlyStoppingAtMinLoss(keras.callbacks.Callback):
def __init__(self, patience=0):
super(EarlyStoppingAtMinLoss, self).__init__()
self.patience = patience
# best_weights to store the weights at which the minimum loss occurs.
self.best_weights = None

def on_train_begin(self, logs=None):
# The number of epoch it has waited when loss is no longer minimum.
self.wait = 0
# The epoch the training stops at.
self.stopped_epoch = 0
# Initialize the best as infinity.
self.best = np.Inf

def on_epoch_end(self, epoch, logs=None):
current = logs.get("loss")
if np.less(current, self.best):
self.best = current
self.wait = 0
# Record the best weights if current results is better (less).
self.best_weights = self.model.get_weights()
else:
self.wait += 1
if self.wait >= self.patience:
self.stopped_epoch = epoch
self.model.stop_training = True
print("Restoring model weights from the end of the best epoch.")
self.model.set_weights(self.best_weights)

def on_train_end(self, logs=None):
if self.stopped_epoch > 0:
print("Epoch %05d: early stopping" % (self.stopped_epoch + 1))

相信大家经过今天的学习,对于回调函数会有更深刻的理解,大家需要多多动手尝试,构建自己的回调函数,以加深印象,达到更好的学习效果。谢谢大家观看!

回归损失函数(一)

开篇

这次要与大家分享的是回归损失函数,常见的损失函数有\(mse,me,mae\)等。我们在这里整理了keras官方给出的不同的loss函数的API,并从网上搜集了相关函数的一些特性,把他们整理在了一起。这部分的loss按照keras官方的教程分成class和function两部分,这一次讲的是class部分。

API

MeanSquaredError(mse)

$loss = $

计算的是标签和预测值的误差的平方的平均值,俗称均方误差。

1
2
3
4
5
6
>>> y_true = [[0., 1.], [0., 0.]]
>>> y_pred = [[1., 1.], [1., 0.]]
>>> # Using 'auto'/'sum_over_batch_size' reduction type.
>>> mse = tf.keras.losses.MeanSquaredError()
>>> mse(y_true, y_pred).numpy()
0.5

数理统计中均方误差是指参数估计值与参数值之差平方的期望值,记为MSE。MSE是衡量“平均误差”的一种较方便的方法,MSE可以评价数据的变化程度,MSE的值越小,说明预测模型描述实验数据具有更好的精确度。

在compile时的用法如下所示:

1
model.compile(optimizer='sgd', loss=tf.keras.losses.MeanSquaredError())

缺点

假设我们使用的激活函数为sigmod\(\sigma()\),则在训练过程中存在如下问题:

\(loss = J = \frac{1}{2}(y_i - \hat{y}_i)^2\)

求反向传播所需梯度为 \(\frac{\partial J}{\partial W} = (y_i - \hat{y}_i)\sigma'(Wx_i+b)x_i\)

\[ \sigma'(\Delta) = (\frac{1}{1 + e^{-\Delta} })'\\ = (\frac{1}{1 + e^{-\Delta} })^2 - \frac{1}{1 + e^{-\Delta} }\\ = (\sigma(\Delta))^2 - \sigma(\Delta)\\ = \sigma(\Delta)(\sigma(\Delta) - 1)\\ \] 所以如果预测结果是很接近0或者1的,会导致梯度非常的小,从而训练速度也会很低。

一种解决办法是使用交叉熵作为损失函数,这在后面会讲到。

MeanAbsoluteError(mae)

\(loss = \frac{\sum|y_{true} - y_{pred}|}{n}\)

计算差的绝对值的均值, 可以更好地反映预测值误差的实际情况。

俗称平均绝对值误差,也称L1损失。上面的mse时L2损失。

1
model.compile(optimizer='sgd', loss=tf.keras.losses.MeanAbsoluteError())

MeanAbsolutePercentageError(mape)

\(loss = \frac{\sum100*\frac{|y_{true} - y_{pred}|}{y_{true} } }{n}\)

这个公式更能表示预测值与标签的相对关系,乘以100的目的是为了放大loss函数。

1
2
model.compile(optimizer='sgd',
loss=tf.keras.losses.MeanAbsolutePercentageError())

MeanSquaredLogarithmicError(msle)

$loss = $

从形式上看是把每一项的loss缩小了,是mse的一个修改版本,这样做有什么好处呢?请看下面这个例子(引自 https://blog.csdn.net/leadai/article/details/78876511 )。

\(y_{true}\):[1,2,3,100] \(y_1\):[1,2,3,110] \(y_2\):[2,3,4,100]

可以计算一下如果是mse的话,\(y_2\)会成为更优的预测结果,如果用msle,则是\(y_1\),在这里显然\(y_1\)更优。

1
2
model.compile(optimizer='sgd',
loss=tf.keras.losses.MeanSquaredLogarithmicError())

CosineSimilarity

\(loss = -\sum(l2\_norm(y_{true}) * l2\_norm(y_{pred}))\)

请注意,它是介于-1和0之间的负数,其中0表示正交,而接近-1的值表示更大的相似性。 这使得它在尝试最大化预测值与目标值之间的接近度的设置中可用作损失函数。 如果y_true或y_pred是零向量,则余弦相似度将为0,而与预测值和目标值之间的接近程度无关。

从形式上看,是向量的点乘,从抽象的角度来看,是两个向量之间夹角的衡量,当两个向量正交时是不希望的情况,对应的cosine是0,损失函数的结果也是0,当两个向量夹角为零,cosine为1,正则化的计算结果也是1。最后在公式前面乘以-1,统一化成最小化计算。

1
model.compile(optimizer='sgd', loss=tf.keras.losses.CosineSimilarity(axis=1))

回归损失函数(二)

前言

regression loss(1)中简单介绍了一些关于回归loss函数、相应的性质以及其在keras下的类实现,也就是在调用compile时的使用方法,这一次来简单看一下keras针对此定义的一些loss函数实现、相应的输入输出的shape以及一些编程的细节问题。

实现

以 mean_squared_error 为例

1
tf.keras.losses.mean_squared_error(y_true, y_pred)

一个关于使用该函数的例子如下所示(可运行):

1
2
3
4
5
6
7
8
import numpy as np
import tensorflow as tf
y_true = np.random.randint(0, 2, size=(2, 3))
y_pred = np.random.random(size=(2, 3))
loss = tf.keras.losses.mean_squared_error(y_true, y_pred)
print(y_true.shape,y_true)
print(y_pred.shape,y_pred)
print(loss.shape,np.array(loss))

输出为

1
2
3
4
5
6
7
(2, 3)
[[1 0 1]
[0 0 0]]
(2, 3)
[[0.76175565 0.00847085 0.58019956]
[0.45170964 0.95217628 0.82301612]]
(2,) Tensor("Mean:0", shape=(2,), dtype=float64)

其中输入的两个变量的shape是\((batch\_size,d_0, .. d_N)\),输出的loss的shape是\((batch\_size,d_0, .. d_{N-1})\)

借另外一个例子看一下这个过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(2, 3, 4)
[[[1 0 1 1]
[0 0 1 1]
[1 0 1 0]]
[[1 1 1 0]
[1 0 1 0]
[1 1 0 0]]]
(2, 3, 4)
[[[0.02513952 0.9691113 0.06320494 0.20428369]
[0.34093781 0.80876382 0.15684827 0.72119491]
[0.45385651 0.18147007 0.84950605 0.08463778]]
[[0.67112196 0.59363177 0.96954268 0.64257202]
[0.10844334 0.66677688 0.04690014 0.89629733]
[0.91571837 0.57628731 0.82937847 0.42718047]]]
(2, 3) Tensor("Mean:0", shape=(2, 3), dtype=float64)

第三个维度上的向量长度是4,在\(y\_true\)\(y\_pred\)对应位置的向量会被一次处理,比如上面这个例子中的\([1\ 0\ 1\ 1]\)\([0.02513952\ 0.9691113\ 0.06320494\ 0.20428369]\)会调用\(mse\)被处理成一个值。于是最后计算出来的loss就少一个维度了。

其它的

其余类似,总结一下,如下所示:

1
2
3
4
5
6
7
8
tf.keras.losses.mean_absolute_error(y_true, y_pred)
#loss = mean(abs(y_true - y_pred), axis=-1)
tf.keras.losses.mean_absolute_percentage_error(y_true, y_pred)
#loss = 100 * mean(abs(y_true - y_pred) / y_true, axis=-1)
tf.keras.losses.mean_squared_logarithmic_error(y_true, y_pred)
#loss = mean(square(log(y_true + 1) - log(y_pred + 1)), axis=-1)
tf.keras.losses.cosine_similarity(y_true, y_pred, axis=-1)
#loss = -sum(l2_norm(y_true) * l2_norm(y_pred))

huber class

首先定义\(x \gets y\_true - y\_pred\)

于是loss就如下计算

1
2
loss = 0.5 * x^2                  if |x| <= d
loss = 0.5 * d^2 + d * (|x| - d) if |x| > d

一个简单的例子如下所示:

1
2
3
4
5
6
>>> y_true = [[0, 1], [0, 0]]
>>> y_pred = [[0.6, 0.4], [0.4, 0.6]]
>>> # Using 'auto'/'sum_over_batch_size' reduction type.
>>> h = tf.keras.losses.Huber()
>>> h(y_true, y_pred).numpy()
0.155

在进行编译时的例子:

1
model.compile(optimizer='sgd', loss=tf.keras.losses.Huber())

对于这个loss函数的理解非常简单:当预测值和真实值相差很小的时候就用平方损失函数,否则通过一定的手段降低loss。

概率损失函数篇

上周,我们一起学习了概率loss和回归loss中的class,今天我们便要完成接下来的内容:概率loss的function部分。

binary_crossentropy 函数
1
2
3
4
5
6
tf.keras.losses.binary_crossentropy(
y_true, #标签值。形如[batch_size, d0, .. dN]
y_pred, #预测值。形如[batch_size, d0, .. dN]
from_logits=False, #y_pred是否是logits张量。默认情况下,我们假设y_pred编码概率分布
label_smoothing=0 #[0,1]之间的浮点数。如果大于0则平滑标签
)

作用:

计算二分类任务交叉熵损失。

二分类任务交叉熵损失函数如下所示:

4076491-df2df44a4131abc2

二分类对应的神经网络的最后一层为sigmoid。

例子:

1
2
3
4
5
6
>>> y_true = [[0, 1], [0, 0]]
>>> y_pred = [[0.6, 0.4], [0.4, 0.6]]
>>> loss = tf.keras.losses.binary_crossentropy(y_true, y_pred)
>>> assert loss.shape == (2,)
>>> loss.numpy()
array([0.916 , 0.714], dtype=float32)

最终返回二分类任务交叉熵损失值,形如[batch_size, d0, .. dN-1]

categorical_crossentropy 函数
1
2
3
4
5
6
tf.keras.losses.categorical_crossentropy(
y_true, #标签值的one-hot张量
y_pred, #预测值的张量
from_logits=False, #y_pred是否是logits张量。默认情况下,我们假设y_pred编码概率分布
label_smoothing=0 #[0,1]之间的浮点数。如果大于0则平滑标签
)

作用:

计算多分类任务交叉熵损失。

多分类任务交叉熵损失函数如下所示:

4076491-092ff05a2d62df08

多分类对应的神经网络的最后一层为softmax。

例子:

1
2
3
4
5
6
>>> y_true = [[0, 1, 0], [0, 0, 1]]
>>> y_pred = [[0.05, 0.95, 0], [0.1, 0.8, 0.1]]
>>> loss = tf.keras.losses.categorical_crossentropy(y_true, y_pred)
>>> assert loss.shape == (2,)
>>> loss.numpy()
array([0.0513, 2.303], dtype=float32)

最终返回多分类任务交叉熵损失值。

sparsecategorical_crossentropy 函数
1
2
3
4
5
6
tf.keras.losses.sparse_categorical_crossentropy(
y_true, #标签值。形如[batch_size, d0, .. dN]
y_pred, #预测值。形如[batch_size, d0, .. dN]
from_logits=False, #y_pred是否是logits张量。默认情况下,我们假设y_pred编码概率分布
axis=-1 #(可选)默认为-1。计算熵的维数
)

作用:

计算稀疏多分类任务交叉熵损失。

例子:

1
2
3
4
5
6
>>> y_true = [1, 2]
>>> y_pred = [[0.05, 0.95, 0], [0.1, 0.8, 0.1]]
>>> loss = tf.keras.losses.sparse_categorical_crossentropy(y_true, y_pred)
>>> assert loss.shape == (2,)
>>> loss.numpy()
array([0.0513, 2.303], dtype=float32)

最终返回稀疏多分类任务交叉熵损失值。

poisson函数
1
2
3
4
tf.keras.losses.poisson(
y_true, #标签值。形如[batch_size, d0, .. dN]
y_pred, #预测值。形如[batch_size, d0, .. dN]
)

作用:

计算标签值和预测值之间的泊松loss。

泊松损失是张量y_pred - y_true * log(y_pred)元素的平均值。

例子:

1
2
3
4
5
6
7
8
>>> y_true = np.random.randint(0, 2, size=(2, 3))
>>> y_pred = np.random.random(size=(2, 3))
>>> loss = tf.keras.losses.poisson(y_true, y_pred)
>>> assert loss.shape == (2,)
>>> y_pred = y_pred + 1e-7
>>> assert np.allclose(
... loss.numpy(), np.mean(y_pred - y_true * np.log(y_pred), axis=-1),
... atol=1e-5)

最终返回泊松损失值,形如[batch_size, d0, .. dN-1]。

当y_true和y_pred有不兼容的形状时报错:InvalidArgumentError

kl_divergence函数
1
2
3
4
tf.keras.losses.kl_divergence(
y_true, #标签值。形如[batch_size, d0, .. dN]
y_pred, #预测值。形如[batch_size, d0, .. dN]
)

作用:

计算标签值和预测值之间的Kullback-Leibler散度loss。

loss的计算公式为:loss = y_true * log(y_true / y_pred)。

例子:

1
2
3
4
5
6
7
8
>>> y_true = np.random.randint(0, 2, size=(2, 3)).astype(np.float64)
>>> y_pred = np.random.random(size=(2, 3))
>>> loss = tf.keras.losses.kullback_leibler_divergence(y_true, y_pred)
>>> assert loss.shape == (2,)
>>> y_true = tf.keras.backend.clip(y_true, 1e-7, 1)
>>> y_pred = tf.keras.backend.clip(y_pred, 1e-7, 1)
>>> assert np.array_equal(
... loss.numpy(), np.sum(y_true * np.log(y_true / y_pred), axis=-1))

最终返回Kullback-Leibler散度损失值。

如果y_true不能被转换为y_pred的类型时报错:TypeError

损失函数对于神经网络的训练极其重要,一个适当的损失函数可以大大加快神经网络的训练速度,以及帮助神经网络找到最优解。所以如果想要科学构建神经网络,一定要将损失函数了解透彻,希望大家下去多多查阅相关资料,巩固知识。谢谢大家耐心观看!

数据集(一)

前言

keras提供了几个数据集,可以通过\(load\_data\)函数直接调用,具体如下所示

mnist digits 分类数据集

这是一个包含0,1,2,3,4,5,6,7,8,9的60,000张28x28灰度图像的数据集,以及10,000张图像的测试集。

1
tf.keras.datasets.mnist.load_data(path="mnist.npz")

其中\(path\)参数指定了数据集在本地缓存的位置,函数的返回值是两个numpy类型的数组,\((x\_train, y\_train), (x\_test, y\_test)\)。其中,\((x\_train, y\_train)\)是数据,shape是\((num\_samples,28,28)\)\((x\_test, y\_test)\)是标签,shape是\((num\_samples,)\)。这里得到的仅仅是矩阵数据,如果想要看到这个图片,还需要进一步的可视化操作。

CIFAR10 小图片分类数据集

这是50,000个32x32色彩训练图像和10,000个测试图像的数据集,标记了10个类别。

1
tf.keras.datasets.cifar10.load_data()

这里不支持本地cache,返回值的类型和mnist是一样的,但是数组\((x\_train, y\_train)\)的shape有所区别,\((num\_samples, 3, 32, 32)\),由于是彩色的,使用的是RGB色彩标准,所以是三个通道,相对于mnist就稍微复杂了一些

CIFAR100 小图片分类数据集

这是一个50,000个32x32彩色训练图像和10,000个测试图像的数据集,标记了100多个细粒度的类别,这些细类被分组为20个粗粒度的类别。

1
tf.keras.datasets.cifar100.load_data(label_mode="fine")

由于数据集有两种不同粒度的标签,在调用数据集时应该指明数据集的标签是哪一个,这个信息是通过\(label\_mode\)这个参数传递的,当参数的值为fine时,分为100个类别,当参数值为coarse时,分为10个类别。函数的返回值和cifar10一摸一样。

IMDB 电影评论数据集

这是来自IMDB的25,000条电影评论的数据集,并按情感(正/负)进行标记。 评论已经过预处理,并且每个评论都被编码为单词索引(整数)列表。 为了方便起见,单词以数据集中的整体频率索引,因此,例如整数“ 3”编码数据中第3个最频繁出现的单词。 这样就可以进行快速过滤操作,例如:“仅考虑前10,000个最常用的词,而排除前20个最常用的词”。

按照惯例,“ 0”不代表特定单词,而是用于编码任何未知单词。

1
2
3
4
5
6
7
8
9
10
11
tf.keras.datasets.imdb.load_data(
path="imdb.npz",
num_words=None,
skip_top=0,
maxlen=None,
seed=113,
start_char=1,
oov_char=2,
index_from=3,
**kwargs
)
参数解释

num_words:所有的单词都按照词频进行排序了,如果改参数是一个整数,那么训练集中就只包含词频最高的num_words单词,剩余的单词都会被oov_char代替,否则就是所有的单词。

skip_top:输出训练集的时候跳过词频最高的几个单词,并使用oov_char代替。

maxlen:整数或者空值,表示限制训练集合中的句子序列的最大长度

seed:随机种子,打乱数据集时使用

start_char:给每个序列的开始处标记一个字符,是一个整数,一般可以用1

oov_char:上面提到过了,相当于是一个统一填充值

函数的返回值依然是 \((x\_train, y\_train), (x\_test, y\_test)\)

另一个接口是tf.keras.datasets.imdb.get_word_index(path="imdb_word_index.json"),这个函数用于获取单词和index之间的映射关系,返回值是键值类型的,键是单词字符串,值是单词对应的索引。

数据集(二)

今天我们来继续说明Keras剩下的三个内置数据集。

路透社新闻分类数据集

数据集来源于路透社的 11,228 条新闻文本,总共分为 46 个主题。

load_data功能
1
2
3
4
5
6
7
8
9
10
11
12
tf.keras.datasets.reuters.load_data(
path="reuters.npz", #将数据缓存到的位置(相对于~/.keras/dataset)
num_words=None, #整数或无。根据单词出现的频率(在训练集中)对单词进行排名,并且仅num_words保留最频繁出现的单词。频率较低的单词将oov_char在序列数据中显示为值。如果为None,将保留所有单词。默认为无,因此保留所有单词。
skip_top=0, #跳过前N个最频繁出现的单词(可能没有信息)。这些单词将oov_char在数据集中显示为 值。默认值为0,因此不会跳过任何单词。
maxlen=None, #int或无。最大序列长度。更长的序列将被截断。默认为无,表示没有截断。
test_split=0.2, #在0到1之间浮动。要用作测试数据的数据集的分数。默认值为0.2,表示将数据集的20%用作测试数据。
seed=113, #int。种子,用于可重复的数据改组。
start_char=1, #整数。序列的开始将以该字符标记。默认为1,因为0通常是填充字符。
oov_char=2, #整数。言外之意。由于num_words或 skip_top限制而被删掉的单词将被替换为该字符。
index_from=3, #int。使用此索引和更高的索引来索引实际单词。
**kwargs
)

该功能用于加载路透社新闻分类数据集,这最初是通过解析和预处理经典的Reuters-21578数据集生成的,但是预处理代码不再与Keras打包在一起。

与 IMDB 数据集一样,每条新闻都被编码为一个词索引的序列(相同的约定)。为了方便起见,单词以数据集中的整体频率索引,因此,例如整数“ 3”编码数据中第3个最频繁出现的单词。这样就可以进行快速过滤操作,例如:“仅考虑前10,000个最常用的词,而排除前20个最常用的词”。

按照惯例,“ 0”不代表特定单词,而是用于编码任何未知单词。

返回值:

Numpy数组的元组:(x_train, y_train), (x_test, y_test)

x_train,x_test:序列列表,它是索引(整数)列表。如果num_words参数是特定的,则最大可能的索引值为num_words - 1。如果maxlen指定了参数,则最大可能的序列长度为maxlen

y_train,y_test:整数标签(1或0)的列表。

get_word_index功能
1
2
3
tf.keras.datasets.reuters.get_word_index(
path="reuters_word_index.json" #将数据缓存到的位置(相对于~/.keras/dataset)
)

检索将单词映射到路透数据集中索引的字典。

返回值:

单词索引字典。键是单词字符串,值是它们的索引。

Fashion-MNIST时尚物品数据集

这是一个包含10个时尚类别的60,000张28x28灰度图像的数据集,以及10,000张图像的测试集。该数据集可以用作MNIST的替代品。类标签是:

标签 描述
0 T恤/上衣
1 裤子
2 拉过来
3 连衣裙
4 涂层
5 凉鞋
6 衬衫
7 运动鞋
8
9 脚踝靴
load_data功能
1
tf.keras.datasets.fashion_mnist.load_data()

加载Fashion-MNIST数据集。

返回值:

Numpy数组的元组:(x_train, y_train), (x_test, y_test)

x_train,x_test:uint8个形状为(num_samples,28,28)的灰度图像数据数组。

y_train,y_test:uint8个形状为(num_samples,)的标签数组(范围为0-9的整数)。

Boston房价回归数据集

数据集来自卡内基梅隆大学维护的 StatLib 库。

样本包含 1970 年代的在波士顿郊区不同位置的房屋信息,总共有 13 种房屋属性。 目标值是一个位置的房屋的中值(单位:k$)。

load_data功能
1
2
3
4
5
tf.keras.datasets.boston_housing.load_data(
path="boston_housing.npz", #本地缓存数据集的路径(相对于~/.keras/datasets)
test_split=0.2, #保留为测试集的数据部分
seed=113 #用于在计算测试拆分之前对数据进行混洗的随机种子
)

加载波士顿房屋数据集。

返回值:

Numpy数组的元组:(x_train, y_train), (x_test, y_test)

x_train,x_test:具有(num_samples, 13) 包含训练样本(对于x_train)或测试样本(对于y_train)的形状的numpy数组。

y_train,y_test(num_samples,)包含目标标量的numpy形状数组。目标是通常在10到50之间的浮动标量,以k $表示房价。

谢谢大家的观看!

工具

CustomObjectScope class

1
tf.keras.utils.custom_object_scope(*args)

将自定义类/函数暴露给Keras反序列化内部组件。在具有custom_object_scope(objects_dict)的作用域下,诸如tf.keras.models.load_modeltf.keras.models.model_from_config之类的Keras方法将能够反序列化已保存配置引用的任何自定义对象(例如,自定义层或度量指标)。

例子

考虑一个自定义的正则化器 my_regularizer

1
2
3
4
5
6
layer = Dense(3, kernel_regularizer=my_regularizer)
config = layer.get_config() # Config contains a reference to `my_regularizer`
...
# Later:
with custom_object_scope({'my_regularizer': my_regularizer}):
layer = Dense.from_config(config)

其中{'my_regularizer': my_regularizer}是函数的参数,都是键值类型的。

get_custom_objects function

1
tf.keras.utils.get_custom_objects()

检索对自定义对象的全局词典的实时引用。首选使用custom_object_scope更新和清除自定义对象,但可以使用get_custom_objects直接访问当前的自定义对象集合。

register_keras_serializable function

1
tf.keras.utils.register_keras_serializable(package="Custom", name=None)

向Keras序列化框架注册对象。该修饰器将修饰后的类或函数注入Keras定制对象字典中,以便无需用户提供的定制对象字典中的条目即可对其进行序列化和反序列化。 它还注入了Keras将调用的函数,以获取对象的可序列化的字符串键。请注意,要进行序列化和反序列化,类必须实现get_config()方法。 函数没有此要求。该对象将在“ package> name”项下注册,其中名称为name,如果未通过,则默认为对象名。

下面两个虽然简单,但是是重要的函数

serialize_keras_object function

1
tf.keras.utils.serialize_keras_object(instance)

将Keras对象序列化为JSON兼容的表示形式。

deserialize_keras_object function

1
2
3
tf.keras.utils.deserialize_keras_object(
identifier, module_objects=None, custom_objects=None, printable_module_name="object"
)

将Keras对象的序列化形式转换回实际对象。

畅游人工智能之海 | Keras教程之工具函数(一)

类型介绍

模型绘制

数据集来源于路透社的 11,228 条新闻文本,总共分为 46 个主题。

plot_model参数一览

1
tf.keras.utils.plot_model(    model, #你想绘制的keras模型实例    to_file="model.png" #想要保存的文件路径和文件名    show_shapes=False, #是否显示网络层的尺寸信息    show_dtype=False, #是否显示网络层的dtypes    show_layer_names=True,#显示网络层的名字    rankdir="TB", #这个参数会传递给PyDot,用以确定图像的格式,例如‘TB’会垂直地绘制,‘LB’会水平地绘制图像    expand_nested=False, #是否将嵌套模型扩展为集群    dpi=96, #图像清晰度的参数)

实例:

1
input = tf.keras.Input(shape=(100,), dtype='int32', name='input')x = tf.keras.layers.Embedding(    output_dim=512, input_dim=10000, input_length=100)(input)x = tf.keras.layers.LSTM(32)(x)x = tf.keras.layers.Dense(64, activation='relu')(x)x = tf.keras.layers.Dense(64, activation='relu')(x)x = tf.keras.layers.Dense(64, activation='relu')(x)output = tf.keras.layers.Dense(1, activation='sigmoid', name='output')(x)model = tf.keras.Model(inputs=[input], outputs=[output])dot_img_file = '/tmp/model_1.png'tf.keras.utils.plot_model(model, to_file=dot_img_file, show_shapes=True)

如果运行正确,你应当在对应的tofile路径下找到绘制出的图片

返回值:

该如果安装了Jupyter,该函数就会返回一个Jupyter笔记本的图像对象。

model_to_dot

1
tf.keras.utils.model_to_dot(    model, #keras模型实例    show_shapes=False,    show_dtype=False,    show_layer_names=True,    rankdir="TB",    expand_nested=False,    dpi=96,    subgraph=False,#是否返回一个pydot.Cluster实例)

把Keras模型转换成dot类型

返回值:

返回一个模型对应的pydot.Dot实例,如果subgraph设定为true,则会返回一个 pydot.Cluster实例

序列化工具

CustomObjectScope

1
tf.keras.utils.custom_object_scope(*args) 

Keras反序列化内部公开自定义类/函数。

在custom_object_scope(objects_dict)作用域中,可以使用诸如tf.keras.modelsload_model或tf.keras.models这样的方法来反序列化被保存的配置引用的任何自定义对象(例如自定义层或指标)。

例如:

1
layer = Dense(3, kernel_regularizer=my_regularizer)config = layer.get_config()  # Config contains a reference to `my_regularizer`...# Later:with custom_object_scope({'my_regularizer': my_regularizer}):  layer = Dense.from_config(config

*args是一个字典{name:object}序列。

1
2
register_keras_serializable
tf.keras.utils.register_keras_serializable(package="Custom", name=None)

`` 向Keras序列化框架注册一个对象。 这个装饰器将修饰过的类或函数注入到Keras自定义对象字典中,这样它就可以被序列化和反序列化,而不需要在用户提供的自定义对象dict中添加条目。注意,要序列化和反序列化,类必须实现get_config()方法。函数不需要此要求。

对象将在关键字“package>name”下注册,其中name,如果没有设置,则默认为对象名称。

参数:

package:这个类所属的包名

name:要在这个包下序列化这个类的名称。如果没有,则使用类的名称。

返回值:用传递的名称注册被修饰类的装饰器。

serialize_keras_object

1
tf.keras.utils.serialize_keras_object(instance)

将Keras对象序列化为json兼容的表示。

1
2
deserialize_keras_object
tf.keras.utils.deserialize_keras_object( identifier, module_objects=None, custom_objects=None, printable_module_name="object")

返回值:

将Keras对象的序列化形式转换回实际对象。

后端工具

今天我们要开始学习Keras的后端函数。

首先我们要了解什么是后端。Keras依赖于一个专门的、优化的张量操作库来完成一系列操作,它可以作为Keras的后端引擎。相比单独地选择一个张量库,而将Keras的实现与该库相关联,Keras以模块方式解决这个问题,它可以将几个不同的后端引擎无缝嵌入到Keras中。

目前,Keras有三个后端实现可用:TensorFlow后端、Theano后端和CNTK后端。可以通过手动操作对后端进行切换,这里不再赘述。

以下我们来学习Keras的后端函数。

clear_session函数

1
tf.keras.backend.clear_session()

该函数可以重置Keras生成的所有状态。

Keras管理全局状态,该状态用于实现功能模型构建API并统一自动生成的图层名称。

它可以销毁当前的TF图并创建一个新图。有利于避免旧模型/网络层混乱。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 例1:clear_session()在循环中创建模型时调用
for _ in range(100):
#如果没有clear_session(),每次迭代都会略微增加Keras管理的全局状态的大小
model = tf.keras.Sequential([tf.keras.layers.Dense(10) for _ in range(10)])

for _ in range(100):
# 如果在开始时调用“clear\u session()”,Keras在每次迭代时都会以空白状态开始,并且内存消耗随着时间的推移是恒定的。
tf.keras.backend.clear_session()
model = tf.keras.Sequential([tf.keras.layers.Dense(10) for _ in range(10)])


# 例2:重置图层名称生成计数器
>>> import tensorflow as tf
>>> layers = [tf.keras.layers.Dense(10) for _ in range(10)]
>>> new_layer = tf.keras.layers.Dense(10)
>>> print(new_layer.name)
dense_10
>>> tf.keras.backend.set_learning_phase(1)
>>> print(tf.keras.backend.learning_phase())
1
>>> tf.keras.backend.clear_session()
>>> new_layer = tf.keras.layers.Dense(10)
>>> print(new_layer.name)
dense

floatx函数

1
tf.keras.backend.floatx()

该函数以字符串形式返回默认的float类型。

例如‘float16’,‘float32’,‘float64’

返回值:

String类型,当前的默认浮点类型。

例子:

1
2
>>> tf.keras.backend.floatx()
'float32'

set_floatx函数

1
2
3
tf.keras.backend.set_floatx(
value #字符串; 'float16','float32'或'float64'
)

该函数设置默认的浮点类型。

注意:建议不要将其设置为float16进行训练,因为这可能会导致数值稳定性问题。另外,可以通过调用tf.keras.mixed_precision.experimental.set_policy('mixed_float16')来混合使用float16和float32的混合精度。

例子:

1
2
3
4
5
6
>>> tf.keras.backend.floatx()
'float32'
>>> tf.keras.backend.set_floatx('float64')
>>> tf.keras.backend.floatx()
'float64'
>>> tf.keras.backend.set_floatx('float32')

注意:如果输入值无效时,报错:“ValueError”

image_data_format函数

1
tf.keras.backend.image_data_format()

返回默认的图像数据格式约定。

返回值:

String类型,‘channels_first’或者‘channels_last’

例子:

1
2
>>> tf.keras.backend.image_data_format()
'channels_last'

set_image_data_format函数

1
2
3
tf.keras.backend.set_image_data_format(
data_format #字符串。'channels_first'或'channels_last'
)

设置图像数据格式约定。

例子:

1
2
3
4
5
6
>>> tf.keras.backend.image_data_format()
'channels_last'
>>> tf.keras.backend.set_image_data_format('channels_first')
>>> tf.keras.backend.image_data_format()
'channels_first'
>>> tf.keras.backend.set_image_data_format('channels_last')

注意:如果输入值无效时,报错:“ValueError”

明天我们将继续展开对后端函数的学习,谢谢大家的观看!

epsilon function

注意使用的时候把tf改成tensorflow!

1
tf.keras.backend.epsilon()

返回数字表达式中使用的模糊因子的值,运行下面的程序输出的就是,这个模糊因子可以理解为当两个变量的值差的绝对值不超过这个模糊因子,就认为是相等的。

1
2
import tensorflow
print(tensorflow.keras.backend.epsilon())

set_epsilon function

1
tf.keras.backend.set_epsilon(value)

更改上述提到的模糊因子的值

1
2
3
4
5
6
>>> tf.keras.backend.epsilon()
1e-07
tf.keras.backend.set_epsilon(1e-5)
tf.keras.backend.epsilon()
1e-05
tf.keras.backend.set_epsilon(1e-7)

is_keras_tensor function

1
tf.keras.backend.is_keras_tensor(x)

判断输入中的x是不是keras张量,所谓keras张量就是由keras提供的层API的返回值

官网给出了很多例子,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
np_var = np.array([1, 2])
# A numpy array is not a symbolic tensor.
tf.keras.backend.is_keras_tensor(np_var)
Traceback (most recent call last):
...
ValueError: Unexpectedly found an instance of type `<class 'numpy.ndarray'>`.
Expected a symbolic tensor instance.
keras_var = tf.keras.backend.variable(np_var)
# A variable created with the keras backend is not a Keras tensor.
tf.keras.backend.is_keras_tensor(keras_var)
False
keras_placeholder = tf.keras.backend.placeholder(shape=(2, 4, 5))
# A placeholder is a Keras tensor.
tf.keras.backend.is_keras_tensor(keras_placeholder)
True
keras_input = tf.keras.layers.Input([10])
# An Input is a Keras tensor.
tf.keras.backend.is_keras_tensor(keras_input)
True
keras_layer_output = tf.keras.layers.Dense(10)(keras_input)
# Any Keras layer output is a Keras tensor.
tf.keras.backend.is_keras_tensor(keras_layer_output)
True

get_uid function

1
tf.keras.backend.get_uid(prefix="")

每个建好的神经网络都对应一个计算图,这个图有一个id,通过一个与图相关的字符串可以找到这个id,关于使用方法如下所示:

1
2
get_uid('dense')
1

rnn function 基本没用!

1
2
3
4
5
6
7
8
9
10
11
12
tf.keras.backend.rnn(
step_function,
inputs,
initial_states,
go_backwards=False,
mask=None,
constants=None,
unroll=False,
input_length=None,
time_major=False,
zero_output_for_mask=False,
)

指标函数

Metrics函数是用于判断模型性能的函数。

Metrics函数与损失函数类似,不同之处在于训练模型时不使用指标函数的结果。我们可以使用任何损失函数作为指标。

指标函数有非常多种类,如精度指标、概率指标、回归指标等等,今天我们来整体概述一下Metrics函数。

compile()中的用法

compile:

compile方法会采用一个metrics参数,该参数是Metrics的列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
model.compile(
optimizer='adam',
loss='mean_squared_error',
metrics=[
metrics.MeanSquaredError(),
metrics.AUC(),
]
)
'''
指标值在fit()期间显示,并记录到fit()返回的历史对象中。它们也会被model.evaluate()返回

在训练期间监控指标的最佳方法是通过TensorBoard
'''
# 要跟踪特定名称下的指标,可以将name参数传递给度量构造函数:
model.compile(
optimizer='adam',
loss='mean_squared_error',
metrics=[
metrics.MeanSquaredError(name='my_mse'),
metrics.AUC(name='my_auc'),
]
)
#所有内置指标也可以通过其字符串标识符传递(在这种情况下,使用默认构造函数参数值,包括默认指标名称):
model.compile(
optimizer='adam',
loss='mean_squared_error',
metrics=[
'MeanSquaredError',
'AUC',
]
)

独立使用

与损失不同,指标是有状态的。使用update_state()方法更新其状态,并使用result()方法查询标量指标结果:

1
2
3
4
5
6
m = tf.keras.metrics.AUC()
m.update_state([0, 1, 1, 1], [0, 1, 0, 0])
print('Intermediate result:', float(m.result()))

m.update_state([1, 1, 1, 1], [0, 1, 1, 0])
print('Final result:', float(m.result()))

以下是如何将指标用作简单自定义培训循环的一部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
accuracy = tf.keras.metrics.CategoricalAccuracy()
loss_fn = tf.keras.losses.CategoricalCrossentropy(from_logits=True)
optimizer = tf.keras.optimizers.Adam()

# Iterate over the batches of a dataset.
for step, (x, y) in enumerate(dataset):
with tf.GradientTape() as tape:
logits = model(x)
# Compute the loss value for this batch.
loss_value = loss_fn(y, logits)

# Update the state of the `accuracy` metric.
accuracy.update_state(y, logits)

# Update the weights of the model to minimize the loss value.
gradients = tape.gradient(loss_value, model.trainable_weights)
optimizer.apply_gradients(zip(gradients, model.trainable_weights))

# Logging the current accuracy value so far.
if step % 100 == 0:
print('Step:', step)
print('Total running accuracy so far: %.3f' % accuracy.result())

创建自定义指标

作为简单的可调用(无状态)

与loss函数非常类似,任何具有签名metric_fn(y_true,y_pred)的可调用函数返回一个损失数组(输入批处理中的一个示例)都可以作为指标传递给compile()。请注意,对于任何此类指标,都自动支持样本权重。

下面是一个简单的例子:

1
2
3
4
5
def my_metric_fn(y_true, y_pred):
squared_difference = tf.square(y_true - y_pred)
return tf.reduce_mean(squared_difference, axis=-1) # Note the `axis=-1`

model.compile(optimizer='adam', loss='mean_squared_error', metrics=[my_metric_fn])
作为Metric的子类(有状态)

并不是所有的指标都可以通过无状态的可调用项来表示,因为在培训和评估期间,每个批次的指标值都是经过评估的,但是在某些情况下,每个批次值的平均值并不是您感兴趣的。

假设您想要计算给定评估数据集的AUC:每批AUC值的平均值与整个数据集的AUC值的平均值不同。

对于这样的度量,您将希望Metric类的子类,该类可以跨批维护状态。很简单:

在__init__中创建状态变量

更新Update_state()中给定y_true和y_pred的变量

在result()中返回度量结果

清除reset_states()中的状态

下面是一个计算二进制真正数的简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class BinaryTruePositives(tf.keras.metrics.Metric):

def __init__(self, name='binary_true_positives', **kwargs):
super(BinaryTruePositives, self).__init__(name=name, **kwargs)
self.true_positives = self.add_weight(name='tp', initializer='zeros')

def update_state(self, y_true, y_pred, sample_weight=None):
y_true = tf.cast(y_true, tf.bool)
y_pred = tf.cast(y_pred, tf.bool)

values = tf.logical_and(tf.equal(y_true, True), tf.equal(y_pred, True))
values = tf.cast(values, self.dtype)
if sample_weight is not None:
sample_weight = tf.cast(sample_weight, self.dtype)
values = tf.multiply(values, sample_weight)
self.true_positives.assign_add(tf.reduce_sum(values))

def result(self):
return self.true_positives

def reset_states(self):
self.true_positives.assign(0)

m = BinaryTruePositives()
m.update_state([0, 1, 1, 1], [0, 1, 0, 0])
print('Intermediate result:', float(m.result()))

m.update_state([1, 1, 1, 1], [0, 1, 1, 0])
print('Final result:', float(m.result()))

add_metric() API

在编写自定义层或子类模型的前向传递时,有时可能需要动态记录某些数量,作为指标。在这种情况下,可以使用add_metric()方法。

假设你想要记录一个密集的自定义层的激活的平均值。您可以执行以下操作:

1
2
3
4
5
6
7
8
9
class DenseLike(Layer):
"""y = w.x + b"""

...

def call(self, inputs):
output = tf.matmul(inputs, self.w) + self.b
self.add_metric(tf.reduce_mean(output), aggregation='mean', name='activation_mean')
return output

然后将以“activation_mean”的名称跟踪数量。跟踪的值将是每批指标值的平均值(由aggregation='mean'指定)。

明天我们将具体地学习指标函数,谢谢大家的观看!

精度指标

类型介绍

Accuracy metrics类

1
tf.keras.metrics.Accuracy(name="accuracy", dtype=None)

作用:

该函数用来计算模型预测准确的频率。如果sample_weight为None,权重默认置为1.

例子:

1
2
model.compile(optimizer='sgd',              loss='mse',              metrics=[tf.keras.metrics.Accuracy()])
>>> m.reset_states()>>> m.update_state([[1], [2], [3], [4]], [[0], [2], [3], [4]],... sample_weight=[1, 1, 0, 0])>>> m.result().numpy()0.5 #weight为0的将不会被计入

BinaryAcuracy类

1
tf.keras.metrics.BinaryAccuracy(    name="binary_accuracy", dtype=None, threshold=0.5)

作用:

与Accuracy metrics类相似,只不过判别预测结果与标签是否匹配是按照二分类的情形判别的。

1
>>> m = tf.keras.metrics.BinaryAccuracy()>>> m.update_state([[1], [1], [0], [0]], [[0.98], [1], [0], [0.6]])>>> m.result().numpy()0.75

CategoricalAccuracy类

1
tf.keras.metrics.CategoricalAccuracy(name="categorical_accuracy", dtype=None)

作用:

用来计算one-hot标签的准确率

例子:

1
>>> m.reset_states()>>> m.update_state([[0, 0, 1], [0, 1, 0]], [[0.1, 0.9, 0.8],...                 [0.05, 0.95, 0]],...                sample_weight=[0.7, 0.3])>>> m.result().numpy()0.3

TopCategoricalAccuracy类

1
tf.keras.metrics.TopKCategoricalAccuracy(    k=5, name="top_k_categorical_accuracy", dtype=None)

作用:

计算前k个的准确率,默认值k=5

1
>>> m = tf.keras.metrics.TopKCategoricalAccuracy(k=1)>>> m.update_state([[0, 0, 1], [0, 1, 0]],...                [[0.1, 0.9, 0.8], [0.05, 0.95, 0]])>>> m.result().numpy()0.5

SparseTopK*CategoricalAccuracy*

1
tf.keras.metrics.SparseTopKCategoricalAccuracy(    k=5, name="sparse_top_k_categorical_accuracy", dtype=None)

作用:

计算离散整数的topK准确率

例子:

1
>>> m = tf.keras.metrics.SparseTopKCategoricalAccuracy(k=1)>>> m.update_state([2, 1], [[0.1, 0.9, 0.8], [0.05, 0.95, 0]])>>> m.result().numpy()0.5 #[2,1]相当于对应lable最大值的下标

写在文末

Accuracy metrics是对模型准确度的测量。keras提供了许多函数,来应对不同数据格式下准确率测度计量的问题。