tf.keras的各種自定義和高級用法(待續)
- 2021 年 2 月 25 日
- AI
Keras & Tensorflow 2.0: Custom Layers & Models
kaggle上的一個很簡明的教程。
使用內置方法進行訓練和評估 | TensorFlow Core
這裡面的keras的api給了非常多的細緻的定義,搭配蘇建林的文章,基本夠了
tf.keras 各種自定義
metric的自定義是最簡單的:
1、 使用train_on_batch,個人認為最最靈活的方式了,train_on_batch即訓練一個batch size的數據之後,model直接predict得到y_pred,numpy形式,然後sklearn的metric或者自定義基於numpy的metric計算都可以使用,非常的靈活,個人比較推薦train on batch,當然了,直接fit的時候epochs設置為1,然後一個batch 一個batch的讀取原始數據也是可以的,這一點上tf.keras是非常靈活的;基於numpy的自定義metric函數可以使用numba來加速大大提高evaluation的效率;
2、通過
import tensorflow.keras.backend as K
def mean_pred(y_true, y_pred):
return K.mean(y_pred)
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy', mean_pred])
因為在model fit的過程中得到的中間結果是tf的tensor類型,因此無法直接在內部使用numpy函數,可以通過使用backend來寫,backend的常規用法和numpy api很類似,基本上可以無縫銜接;
當然,我們也可以將y_true,y_pred通過 ..numpy()的方式轉化為numpy,然後使用sklearn的或自定義的基於numpy的評估指標,最後通過convert_to_tensor再轉化為tensor也是很靈活的;
loss的自定義也不複雜:
def smape_error(y_true, y_pred):
return K.mean(K.clip(K.abs(y_pred - y_true), 0.0, 1.0), axis=-1)
和metric的定義類似,我們可以使用tf.keras.backend來定義,但是不能轉化為numpy了,因為轉化為numpy之後,tf底層無法識別numpy數據類型,無法針對自定義的loss進行autograd,不過其實直接用backend基本夠了,自帶的函數基本上和常見的numpy函數是一樣的。
backend的內置methods
['__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__path__',
'__spec__',
'_sys',
'abs',
'all',
'any',
'arange',
'argmax',
'argmin',
'backend',
'batch_dot',
'batch_flatten',
'batch_get_value',
'batch_normalization',
'batch_set_value',
'bias_add',
'binary_crossentropy',
'cast',
'cast_to_floatx',
'categorical_crossentropy',
'clear_session',
'clip',
'concatenate',
'constant',
'conv1d',
'conv2d',
'conv2d_transpose',
'conv3d',
'cos',
'count_params',
'ctc_batch_cost',
'ctc_decode',
'ctc_label_dense_to_sparse',
'cumprod',
'cumsum',
'depthwise_conv2d',
'dot',
'dropout',
'dtype',
'elu',
'epsilon',
'equal',
'eval',
'exp',
'expand_dims',
'eye',
'flatten',
'floatx',
'foldl',
'foldr',
'function',
'gather',
'get_uid',
'get_value',
'gradients',
'greater',
'greater_equal',
'hard_sigmoid',
'image_data_format',
'in_test_phase',
'in_top_k',
'in_train_phase',
'int_shape',
'is_keras_tensor',
'is_sparse',
'l2_normalize',
'learning_phase',
'learning_phase_scope',
'less',
'less_equal',
'local_conv1d',
'local_conv2d',
'log',
'manual_variable_initialization',
'map_fn',
'max',
'maximum',
'mean',
'min',
'minimum',
'moving_average_update',
'name_scope',
'ndim',
'normalize_batch_in_training',
'not_equal',
'one_hot',
'ones',
'ones_like',
'permute_dimensions',
'placeholder',
'pool2d',
'pool3d',
'pow',
'print_tensor',
'prod',
'random_binomial',
'random_normal',
'random_normal_variable',
'random_uniform',
'random_uniform_variable',
'relu',
'repeat',
'repeat_elements',
'reset_uids',
'reshape',
'resize_images',
'resize_volumes',
'reverse',
'rnn',
'round',
'separable_conv2d',
'set_epsilon',
'set_floatx',
'set_image_data_format',
'set_learning_phase',
'set_value',
'shape',
'sigmoid',
'sign',
'sin',
'softmax',
'softplus',
'softsign',
'sparse_categorical_crossentropy',
'spatial_2d_padding',
'spatial_3d_padding',
'sqrt',
'square',
'squeeze',
'stack',
'std',
'stop_gradient',
'sum',
'switch',
'tanh',
'temporal_padding',
'tile',
'to_dense',
'transpose',
'truncated_normal',
'update',
'update_add',
'update_sub',
'var',
'variable',
'zeros',
'zeros_like']
除了使用backend之外,tf.keras目前也支援直接使用tf的內置methods來自定義metric與loss,比如:
def customized_mse(y_true, y_pred):
return tf.reduce_mean(tf.squre(y_pred - y_true))
然後是比較複雜的自定義優化器:
//github.com/bojone/accum_optimizer_for_keras/blob/master/accum_optimizer.py
用時間換取效果:Keras梯度累積優化器 – 科學空間|Scientific Spaces
這塊的邏輯定義比較複雜,因為用的比較少,暫時不研究了
自定義layer:
1、最簡單的,使用lambda層,一行搞定,缺點是無法處理太複雜的邏輯,例如:
from keras.layers import *
from keras import backend as K
x_in = Input(shape=(10,))
x = Lambda(lambda x: x+2)(x_in) # 對輸入加上2
lambda layer的用法和pandas中的lambda的用法基本類似,只不過自定義function的時候要通過tf或者tf.keras.backend的方式進行邏輯程式碼的撰寫;
2、通過繼承tf.keras.layer
//www.tensorflow.org/tutorials/customization/custom_layers
官方文檔寫的非常詳細了:
實現自定義層的最佳方法是繼承tf.keras.Layer類並實現:
__init__
,在其中進行所有與輸入無關的變數或常量的初始化build
,在其中知道輸入張量的形狀,並可以進行其餘的初始化call
,在這裡進行前向傳播邏輯的計算的定義
請注意,不必等到build
被調用才創建變數,也可以在__init__中直接定義。但是,在build中創建變數的好處在於,可以根據build的參數 input shape來自動調整變數的shape。而在__init__中創建變數意味著需要顯式指定創建變數的形狀。
看demo:
class MyDenseLayer(tf.keras.layers.Layer):
def __init__(self, num_outputs):
super(MyDenseLayer, self).__init__()
self.num_outputs = num_outputs
def build(self, input_shape):
self.kernel = self.add_weight("kernel",
shape=[int(input_shape[-1]),
self.num_outputs])
def call(self, input):
return tf.matmul(input, self.kernel)
layer = MyDenseLayer(10)
主要,這裡 call部分的input的shape,會自動關聯到 build中的input_shape上;
更加複雜的例子,可見蘇劍林大佬的科學空間:
「讓Keras更酷一些!」:精巧的層與花式的回調 – 科學空間|Scientific Spaces
class Dense_with_Center_loss(Layer):
def __init__(self, output_dim, **kwargs):
self.output_dim = output_dim
super(Dense_with_Center_loss, self).__init__(**kwargs)
def build(self, input_shape):
# 添加可訓練參數
self.kernel = self.add_weight(name='kernel',
shape=(input_shape[1], self.output_dim),
initializer='glorot_normal',
trainable=True)
self.bias = self.add_weight(name='bias',
shape=(self.output_dim,),
initializer='zeros',
trainable=True)
self.centers = self.add_weight(name='centers',
shape=(self.output_dim, input_shape[1]),
initializer='glorot_normal',
trainable=True)
def call(self, inputs):
# 對於center loss來說,返回結果還是跟Dense的返回結果一致
# 所以還是普通的矩陣乘法加上偏置
self.inputs = inputs
return K.dot(inputs, self.kernel) + self.bias
def compute_output_shape(self, input_shape):
return (input_shape[0], self.output_dim)
def loss(self, y_true, y_pred, lamb=0.5):
# 定義完整的loss
y_true = K.cast(y_true, 'int32') # 保證y_true的dtype為int32
crossentropy = K.sparse_categorical_crossentropy(y_true, y_pred, from_logits=True)
centers = K.gather(self.centers, y_true[:, 0]) # 取出樣本中心
center_loss = K.sum(K.square(centers - self.inputs), axis=1) # 計算center loss
return crossentropy + lamb * center_loss
f_size = 2
x_in = Input(shape=(784,))
f = Dense(f_size)(x_in)
dense_center = Dense_with_Center_loss(10)
output = dense_center(f)
model = Model(x_in, output)
model.compile(loss=dense_center.loss,
optimizer='adam',
metrics=['sparse_categorical_accuracy'])
# 這裡是y_train是類別的整數id,不用轉為one hot
model.fit(x_train, y_train, epochs=10)
這裡是完全使用keras.backend的方式來定義的,當然也可以使用tf的函數來定義,這裡給了一種非常靈活的方式,loss和層的權重有關係,將loss直接定義在層中。
//www.tensorflow.org/guide/keras/custom_layers_and_models?hl=zh-cn
//keras.io/examples/keras_recipes/antirectifier/
Keras documentation: Simple custom layer example: Antirectifier
Keras documentation: Simple custom layer example: Antirectifier
另外就是序列化存儲model的時候需要對自定義layer指定get config的方法,具體可見上。不過其實如果要序列化保存模型,個人覺得保存weight和biase就行,再用的時候定義一樣的結構然後load weight,這樣可以避免很多麻煩的問題。
自定義model:
自定義model其實很簡單了,本質上就是對常規的keras的model搭建做了一層封裝:
//www.tensorflow.org/tutorials/customization/custom_layers
class ResnetIdentityBlock(tf.keras.Model):
def __init__(self, kernel_size, filters):
super(ResnetIdentityBlock, self).__init__(name='')
filters1, filters2, filters3 = filters
self.conv2a = tf.keras.layers.Conv2D(filters1, (1, 1))
self.bn2a = tf.keras.layers.BatchNormalization()
self.conv2b = tf.keras.layers.Conv2D(filters2, kernel_size, padding='same')
self.bn2b = tf.keras.layers.BatchNormalization()
self.conv2c = tf.keras.layers.Conv2D(filters3, (1, 1))
self.bn2c = tf.keras.layers.BatchNormalization()
def call(self, input_tensor, training=False):
x = self.conv2a(input_tensor)
x = self.bn2a(x, training=training)
x = tf.nn.relu(x)
x = self.conv2b(x)
x = self.bn2b(x, training=training)
x = tf.nn.relu(x)
x = self.conv2c(x)
x = self.bn2c(x, training=training)
x += input_tensor
return tf.nn.relu(x)
block = ResnetIdentityBlock(1, [1, 2, 3])
自定義model其實就是抄襲了torch的用法,訓練需要人工去寫循環
//www.tensorflow.org/guide/keras/custom_layers_and_models?hl=zh-cn
個人不是很喜歡這種方法,torch本身我最煩的地方就是這裡了。
「讓Keras更酷一些!」:隨意的輸出和靈活的歸一化 – 科學空間|Scientific Spaces
其它的高級用法
「讓Keras更酷一些!」:分層的學習率和自由的梯度 – 科學空間|Scientific Spaces
分層學習率和更靈活的梯度
當Bert遇上Keras:這可能是Bert最簡單的打開姿勢 – 科學空間|Scientific Spaces
bert+keras
6個派生優化器的簡單介紹及其實現 – 科學空間|Scientific Spaces
更加花哨的adam的實現
Keras:Tensorflow的黃金標準 – 科學空間|Scientific Spaces「讓Keras更酷一些!」:層與模型的重用技巧 – 科學空間|Scientific Spaces
tf.recompute_grad
節省顯示記憶體的重計算技巧也有了Keras版了 – 科學空間|Scientific Spaces
keras中的one cycle學習率和lr finder功能
//github.com/titu1994/keras-one-cycle
//github.com/surmenok/keras_lr_finder
optimizer 優化器的一些資料,暫時用不到太複雜的時間就不看了。。。:
tensorflow裡面name_scope, variable_scope等如何理解?//github.com/bojone/accum_optimizer_for_keras/blob/master/accum_optimizer.py
Keras 手動設置優化器 設置梯度操作 實現小記憶體大Batch更新//github.com/bojone/accum_optimizer_for_keras/blob/master/accum_optimizer.py
//github.com/CyberZHG/keras-radam/blob/master/keras_radam/optimizers.py
「讓Keras更酷一些!」:層中層與mask – 科學空間|Scientific Spaces
蘇建林大佬可以算是keras的忠實用戶了,他的科學空間部落格中關於keras的一些靈活的用法都是很好的文章,tf keras黨可以多看看,畢竟tf2.X之後基本上都是keras式的api的天下了。。。而keras和tf.keras在api的調用上差異很小。