forked from kitian616/jekyll-TeXt-theme
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
189 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
--- | ||
title: 高级的API之keras | ||
key: 20200227 | ||
tags: 深度学习 Tensorflow | ||
|
||
--- | ||
|
||
- [批数据训练](#批数据训练) | ||
- [自定义DataGenerator](#自定义DataGenerator) | ||
- [Sequential与Model模型](#Sequential与Model模型) | ||
- [Layer](#Layer) | ||
- [其他](#其他) | ||
|
||
批数据训练 | ||
--- | ||
- train_on_batch(x, y, sample_weight=None, class_weight=None, reset_metrics=True) | ||
与直接使用fit_generator对generator直接进行训练相比,当我们需要对输入数据进行修改时,或者有多个输入输出时,需要改变generator中的x,y。可以使用train_on_batch函数对每个batch的数据进行训练。 | ||
|
||
- test_on_batch(x, y, sample_weight=None, reset_metrics=True) | ||
对应的,validation部分不参与训练,使用test_on_batch函数。 | ||
|
||
完整的train+val:同时含有如何在train_on_batch中调用tensorboard | ||
``` | ||
def write_log(callback, names, logs, batch_no): | ||
for name, value in zip(names, logs): | ||
summary = tf.Summary() | ||
summary_value = summary.value.add() | ||
summary_value.simple_value = value | ||
summary_value.tag = name | ||
callback.writer.add_summary(summary, batch_no) | ||
callback.writer.flush() | ||
callback = TensorBoard(log_path) | ||
callback.set_model(model) | ||
for i in range(int(args.max_epoch)): | ||
train_step = 0 | ||
for train_batch_data in train_generator: | ||
if len(train_batch_data[0]) < args.batch_size: | ||
continue | ||
X_train, y_train = get_data(train_generator, ...) | ||
metrics = model.train_on_batch(X_train, y_train) | ||
write_log(callback, ['train_loss', 'train_acc', 'train_rc', 'train_pre'], metrics, train_step) | ||
train_step += 1 | ||
if train_step >= args.step_train: | ||
break | ||
val_step = 0 | ||
for val_batch_data in val_generator: | ||
if len(val_batch_data[0]) < args.batch_size: | ||
continue | ||
X_val, y_val = get_data(val_generator, ...) | ||
val_out = model.test_on_batch(X_val, y_val) | ||
val_step += 1 | ||
if val_step >= args.step_val: | ||
break | ||
write_log(callback, ['val_loss', 'val_acc', 'val_rc', 'val_pre'], result, i) | ||
``` | ||
我们需要从train_generator/val_generator中读每个batch的数据。 | ||
我们设置batch_size变量来控制数据量,然而当训练样本总数不是batch_size的整数倍时,往往会多出一部分非batch_size数量的数据。 | ||
对于这份数据我们要么丢弃,要么补充到batch_size的个数。 | ||
我们知道generator是一种生成器,保存的是算法,每次调用next(),就计算出下一个元素的值,直到计算到最后一个元素。所以如果不加控制的读取,会陷入无尽的循环。 | ||
所以当使用for循环读取generator中的数据时,我们应该对以下两个条件进行判断:一个是当数据不满足batch_size时,丢掉这批数据。一个是设置一个epoch中的迭代次数,即训练次数,及时调出循环。 | ||
|
||
此外,使用zip函数也可以有效的对generator中的有效数据进行顺序封装,不需要判断迭代次数。但是在数据量较大时慎用,容易造成堵塞,导致程序被杀死。 | ||
|
||
自定义DataGenerator | ||
--- | ||
[自定义DataGenerator](https://blog.csdn.net/ZWX2445205419/article/details/96109272) | ||
|
||
Sequential与Model模型 | ||
--- | ||
- Sequential模型 | ||
**顺序式模型** | ||
1. Layer提供input与output属性; | ||
2. 第一个Layer必须是InputLayer或者Input函数构建的张量; | ||
|
||
``` | ||
# 定义层 | ||
input_layer = keras.Input(shape=(4,)) # 输入层 | ||
hide1_layer = layers.Dense(units=8, activation='relu') | ||
hide2_layer = layers.Dense(units=4, activation='relu') | ||
output_layer = layers.Dense(units=1, activation='sigmoid') | ||
# 建立模型 | ||
seq_model = keras.Sequential() | ||
seq_model.add(input_layer) | ||
seq_model.add(hide1_layer) | ||
seq_model.add(hide2_layer) | ||
seq_model.add(output_layer) | ||
``` | ||
|
||
- Model模型 | ||
**函数式模型**,比Sequential模型复杂,但是效果很好,可以同时/分阶段输入变量,分阶段输出想要的模型。 | ||
1. 构建的Layer为可调用对象,提供__call__可调用运算符(...),或者使用apply与call实现链式函数调用; | ||
2. Model只需要通过inputs与outputs; | ||
``` | ||
# 定义层 | ||
input_layer = keras.Input(shape=(4,)) # 输入层 | ||
hide1_layer = layers.Dense(units=8, activation='relu') | ||
hide2_layer = layers.Dense(units=4, activation='relu') | ||
output_layer = layers.Dense(units=1, activation='sigmoid') | ||
# 调用 | ||
hide1_layer_tensor = hide1_layer(input_layer) | ||
hide2_layer_tensor = hide2_layer(hide1_layer_tensor) | ||
output_layer_tensor = output_layer(hide2_layer_tensor) | ||
# 建立模型 | ||
model = keras.Model(inputs=input_layer, outputs=output_layer_tensor) | ||
``` | ||
- 定制模型 | ||
|
||
Layer | ||
--- | ||
- 自定义层 | ||
Keras是一个高度封装的库,它的优点是可以进行快速的建模,缺点是它不处理**底层运算**,如张量内积等。为了弥补这个问题,Keras提供“Backend”来实现底层运算操作。目前Keras支持的后端引擎有tensorflow,CNTK,Theano。默认的是使用tensorflow,你可以在.keras/keras.json文件中更改backend。我们可以使用keras提供的后端来实现任意你想实现的layer。 | ||
|
||
``` | ||
from keras import backend as K | ||
from keras.layers import Layer | ||
class MyLayer(Layer): | ||
def __init__(self, output_dim, **kwargs): | ||
self.output_dim = output_dim | ||
super(MyLayer, self).__init__(**kwargs) | ||
def build(self, input_shape): | ||
# Create a trainable weight variable for this layer. | ||
self.kernel = self.add_weight(name='kernel', | ||
shape=(input_shape[1], self.output_dim), initializer='uniform', trainable=True) | ||
super(MyLayer, self).build(input_shape) # Be sure to call this at the end | ||
def call(self, x): | ||
return K.dot(x, self.kernel) | ||
def compute_output_shape(self, input_shape): | ||
return (input_shape[0], self.output_dim) | ||
``` | ||
|
||
- Lambda层 | ||
Keras的最小操作单位是Layer,每次操作的是整个batch。Backend中Tensorflow的最小操作单位是Tensor,每次操作的数据是batch中的所有数据。而对于每个batch中的tensor的一些backend操作需要使用Lambda层进行封装。 | ||
``` | ||
a = Input(shape=(2,)) | ||
b = Input(shape=(2,)) | ||
def minus(inputs): | ||
x, y = inputs | ||
return K.mean(x - y, axis=1) | ||
m = Lambda(minus, name='minus')([a, b]) | ||
model = Model(inputs=[a, b], outputs=[m]) | ||
``` | ||
|
||
其他 | ||
--- | ||
- map_fn高阶函数 | ||
``` | ||
tf.keras.backend.map_fn( | ||
fn, | ||
elems, | ||
name=None, | ||
dtype=None | ||
) | ||
``` | ||
将函数fn映射到元素elems并返回输出。 | ||
其中fn是一个可调用函数,可以使用 lambda 来表示,elems 是需要处理的 tensors, tf 将会从第一维开始展开,进行 map 操作,dtype 表示 fn 函数的输出类型,如果 fn 返回的类型和 elems 中的不同,那么就必须显示指定为和 fn 返回类型相同的类型。 | ||
当各个batch之间没有关联时,可以并行高效处理每个batch中的数据,是一个batch-wise操作。 | ||
|
||
- tf.gather | ||
用一个一维的索引数组,将tensor中对应index的向量提取出来。(不连续索引) | ||
tf.gather_nd允许在多维上进行索引。 | ||
[tf.gather](https://github.com/tensorflow/docs/blob/r1.11/site/en/api_docs/python/tf/gather.md) | ||
|
||
- 使用one_hot进行加减运算 | ||
tf.one_hot(indices, depth, dtype) | ||
indices表示输入的多个数值,通常是矩阵形式;depth表示输出的尺寸。由于one-hot类型数据长度为depth位,其中只用一位数字表示原输入数据,这里的on_value就是这个数字,默认值为1,one-hot数据的其他位用off_value表示,默认值为0。 | ||
indices = 0 对应的输出是[1, 0 … 0, 0], indices = 1 对应的输出是[0, 1 … 0, 0], 依次类推,最大可能值的输出是[0, 0 … 0, 1]。 | ||
|
||
Tensorflow中是不能直接修改tensor的数值的,使用one_hot函数巧妙进行加减运算。下面操作是将1536维tensro中对应非indice中索引的数值赋0: | ||
``` | ||
# inputs.shape(1536,) indices.shape(512,) | ||
one_hot = tf.one_hot(indices, 1536, dtype=tf.float32) | ||
one_hot = tf.reshape(one_hot, (1536, -1)) | ||
one_hot = K.max(one_hot, axis=1, keepdims=False) | ||
x = K.identity(inputs) + one_hot * inputs | ||
``` | ||
|
||
--- | ||
参考文献: | ||
[Keras Documentation](https://keras.io/) | ||
[A detailed example of how to use data generators with Keras](https://stanford.edu/~shervine/blog/keras-how-to-generate-data-on-the-fly) | ||
[Keras的Model模型使用](https://www.jianshu.com/p/c0d6a0c61984) |