[TOC]
工欲善其事,必先利其器,要跑好模型,先要熟悉工具,这里总结下Keras里面常用的API和一些问题的处理方法,以便以后查看。
数据处理 ImageDataGenerator
模型定义 Sequential/Model
模型编译 Compile
compile(optimizer, loss=None, metrics=None, loss_weights=None, sample_weight_mode=None, weighted_metrics=None, target_tensors=None)
- optimzier(): 优化求解器
- loss: 损失函数, 多输出的时候可以通过字典(key为输出层name)传入不同的loss函数,求解的时候模型会最小化总loss, 也可以传入自己定义的loss。
- metrics: 评价指标, 和loss类似。
- loss_weight: 多个loss时, loss权重
- sample_weight_mode: 样本权重模式, 默认是sample-wise的,也可以对时间序列数据timestep-wise
- wighted_metrics: 使用样本权重或者类权重进行计算的评价指标
- target_tensor: 可以为模型定义custom输出
每次对model进行修改之后(如设置layer是否可以训练)都需要重新compile否则修改无效。
模型训练 fit/fit_generator
训练管理callbacks
callbacks可以用来对训练过程中进行管理, 比如保存中间结果和val_loss连续多少次不再下降提前终止训练, 在训练过程中逐渐改变学习率。
BaseLogger()
记录训练过程中每个epoch的评价指标,会在训练过程中自动调用。
TerminateOnNaN()
NaN loss的时候终止训练,一般是由于初始化不正确或其他错误导致梯度为0或者无穷大导致的。
- ProgbarLogger(): 将评价指标(如acc, loss)输出到控制台。
History()
将events记录到History()对象, 自动调用。
ModelCheckpoint
每个epoch保存对象
keras.callbacks.ModelCheckpoint(filepath, monitor='val_loss', verbose=0, save_best_only=False, save_weights_only=False, mode='auto', period=1)
- 参数
- filepath:路径, 如weights.{epoch:02d}-{val_loss:.2f}.hdf5,根据实际epoch和val_loss来保存
- moniter: 要观察的指标, 如val_loss, val_accuracy等
- verbose: 是否在控制台实时监控训练过程
- save_best_only: 是否只保存结果最好的模型
- mode: auto, min, max, 用来save_best的监控值。如acc应该是max, loss应该是min。
- save_weights_only: 值保存模型的权重,否则整个model都会保存。
- period: 多少个epoch保存一次模型。
EarlyStopping
当监控指标停止提升的时候停止训练。
keras.callbacks.EarlyStopping(monitor='val_loss', min_delta=0, patience=0, verbose=0, mode='auto', baseline=None)
- 参数:
- moniter: 监控指标
- min_delta: 认为有提升的最小值
- patience: 连续多少个epoch没有提升之后停止训练
- verbose: 是否输出到控制台
- mode
- baseline: 监控指标的最差值, 如果指标没有在这个值上提升则停止训练。
RemoteMoniter
keras.callbacks.RemoteMonitor(root='http://localhost:9000', path='/publish/epoch/end/', field='data', headers=None, send_as_json=False)
将记录写到服务器上
LearningRateScheduler
管理学习率
keras.callbacks.LearningRateScheduler(schedule, verbose=0)
- 参数:
- schedule: 函数, 传入当前epoch和learning_rate返回新的learning_rate
- verbose: 是否输出信息
Tensorboard
写入日志用于tensorboard可视化
keras.callbacks.TensorBoard(log_dir='./logs', histogram_freq=0, batch_size=32, write_graph=True, write_grads=False, write_images=False, embeddings_freq=0, embeddings_layer_names=None, embeddings_metadata=None, embeddings_data=None)
- 参数
- log_dir: 日志保存路径
- histogram_freq:
- write_graph: 是否在tensorboard可视化图
- write_grads: 是否可视化梯度直方图
- batch_size
- write_images: 是否可视化模型权重
- embeddings_freq:
- embeddings_layer_names:
- embeddings_metadata:
- embeddings_data:
可以在训练的过程中实时监控accuracy和loss的变换:
tensorboard --logdir=logs/
大多数时候,你的服务器在远程,只能通过shell访问,log也保存在远程,那么这个时候如何通过tensorboard监控远程训练呢?
将远程的tensorboard启动后端口重定向到本地的机器特定端口即可,如一般tensorboard启动6006端口,映射到本机16006端口:
ssh -L 16006:127.0.0.1:6006 username@remote_server_ip
这样在服务器上启动tensorboard之后就可以在本地浏览器 127.0.0.1:16006 访问 tensorboard 了。
参考:
https://stackoverflow.com/questions/37987839/how-can-i-run-tensorboard-on-a-remote-server
ReduceLROnPlateau
当某个指标停止提升的时候降低学习率
keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=10, verbose=0, mode='auto', min_delta=0.0001, cooldown=0, min_lr=0)
- 参数
- monitor: 指标
- factor: lr_new = lr*factor
- patience: 多少epoch没有提升降低学习率
- verbose:
- mode
- min_delta: 最低提升指标
- cooldown: 降低学习率之后多少epoch之后继续正常操作
- min_lr: 最小的学习率
CSVLogger
写入csv文件
LambdaCallback
自定义callback函数
结果评价Evaluation
evaluate()
用于计算model对样本x的loss和定义在 metrics 里面的指标。
evaluate(x=None, y=None, batch_size=None, verbose=1, sample_weight=None, steps=None)
- x: 输入数据, 单输入时为numpy array, 多输入时为list of numpy arrays. 输入层如果有命名的话也可以传入字典。
- y: 和输入数据类似。
- batch_size
- verbose
- sample_weight:用于传入样本权重计算loss, weight长度应该和样本数据一样。
- steps: 步数
evaluate_generator()
evaluate_generator(generator, steps=None, max_queue_size=10, workers=1, use_multiprocessing=False, verbose=0)
在data_generator()上评估
进行预测 Predictors
predict
predict(x, batch_size=None, verbose=0, steps=None)
- x: 输入
- batch_size:
- verbose: 显示中间结果和进度条
- steps: 多少个batches结束
predict_on_batch()
predict_on_batch(x)
一个batch的预测结果
predict_generator()
predict_generator(geneartor, steps=None, max_queue_size=10, workers=1, use_multiprocessing=False, verbose=0)
- generator():预测数据生成器
- steps(): 步数, 默认是len(generator)
- max_queue_size(): 生成器队列的最大长度
- workers():
- use_multiprocessing():
- verbose():
其他
**1. 按照name或者index来获取模型的层 get_layer() **
get_layer(name=None, index=None)
根据名称或者index获得layer, 一般用于对layer进行修改(如设置trainable)和模型进行重新连接的时候。
2. 对于样本不均衡的问题如何设置样本权重
在keras里面设置类别权重稍微有点麻烦, 一般是在fit/fit_generator()中设置。
需要将样本权重以label为key, weight为value作为字典传入,label为int值,value为float值。
对于weight的具体计算每个label的权重可以,用数目最多的类别样本数目,除以每个类别的样本数。如类别A: 100, B:50, C:1000,
A, B类别样本数目太少,我们希望在训练模型的时候给二者更多的权重,因此:
weight_A = 1000/100 = 10,
weight_B = 1000/50 = 20,
weight_C = 1000/1000 = 1
或者直接从flow_from_directory()直接计算:
参考这个问题:https://stackoverflow.com/questions/42586475/is-it-possible-to-automatically-infer-the-class-weight-from-flow-from-directory
from collections import Counter
train_datagen = ImageDataGenerator()
train_generator = train_datagen.flow_from_directory(...)
counter = Counter(train_generator.classes)
max_val = float(max(counter.values()))
class_weights = {class_id : max_val/num_images for class_id, num_images in counter.items()}
model.fit_generator(...,
class_weight=class_weights)
我自己初步试验了下,两种方式计算方式得出来的class_weight结果是一致的, 但是第二中方式不许要提前计算好class_weights然后在程序中调用,简单点。
3. 如何在progress bar 输出学习率的变化
通过定义customer metrics 然后在metric中调用
def get_lr_metric(optimizer):
def lr(y_true, y_pred):
return optimizer.lr
return lr
lr_metric = get_lr_metric(optimizer)
model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['acc', lr_metric])
参考: https://stackoverflow.com/questions/48198031/keras-add-variables-to-progress-bar/48206009#48206009
另一种方法是创建learning rate scheduler, 然后在scheduler函数中调用print函数输出下便可。
例如如下代码:
def create_lr_schedule(epochs, lr_base, lr_power=0.99, mode='power_decay'):
return lambda epoch: _lr_schedule(epoch, epochs, lr_base, lr_power, mode)
def _lr_schedule(epoch, epochs, lr_base, lr_power, mode):
if mode is 'power_decay':
lr = lr_base * ((1 - float(epoch) / epochs) ** lr_power)
if mode is 'exp_decay':
lr = (float(lr_base) ** float(lr_power)) ** float(epoch + 1)
if mode is 'adam':
lr = 0.001
print("lr: {0:f}".format(lr))
return lr
4. 如何设置Keras使用CPU/GPU
默认会调用GPU,设置只使用CPU:
在import Keras/Tensorflow之前:
import os
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID" # see issue #152
os.environ["CUDA_VISIBLE_DEVICES"] = ""
或者在运行python脚本之前:
CUDA_VISIBLE_DEVICES="" python ./your_keras_code.py
5. Keras 模型转换为Tensorflow 模型
方法一: 使用keras_to_tensorflow脚本:
keras_to_tensorflow提供好接口,不需要自己管理Tensorflow和Keras之间的接口转换,将已经训练好的keras .h5模型为难,转换为tensorflow binary protobuf .pb文件,直接用于部署到生产环境。同时可以设置 --output_meta_ckpt用于训练节点和训练图文件,用于在Tensorflow中继续训练。
python keras_to_tensorflow.py --input_model=../../SimpleNet-Keras/simplenet_V2_8M.h5 \
--output_model=./model.pb \
--output_meta_ckpt
6. Keras 里面计算 recall/precision/confusion matrix
keras 里面没有直接计算precision和recall的函数
https://stackoverflow.com/questions/43076609/how-to-calculate-precision-and-recall-in-keras
https://pypi.org/project/keras-metrics/
https://github.com/keras-team/keras/issues/5794
https://gist.github.com/RyanAkilos/3808c17f79e77c4117de35aa68447045
7. Keras 调用含有用户自定义 layer 或者 loss 函数的模型
如果是已经训练好的模型,在加载模型的时候,load_model里面的custom_objects参数中提供对应的字典。
model = load_model('my_model.h5', custom_objects={'AttentionLayer': AttentionLayer})
也可以使用customObjectScope()传入custom_objects
from keras.utils import CustomObjectScope
with CustomObjectScope({'AttentionLayer': AttentionLayer}):
model = load_model('my_model.h5')
https://github.com/keras-team/keras/issues/4871
https://keras.io/getting-started/faq/ 里面的 Handling custom layers (or other custom objects) in saved models
8. 如何将 tensorflow 权重转换为keras权重?
# extract_weights.py
from __future__ import print_function
import os
import re
from glob import glob
import numpy as np
import tensorflow as tf
from keras.utils.data_utils import get_file
# regex for renaming the tensors to their corresponding Keras counterpart
re_repeat = re.compile(r'Repeat_[0-9_]*b')
re_block8 = re.compile(r'Block8_[A-Za-z]')
def get_filename(key):
"""Rename tensor name to the corresponding Keras layer weight name.
# Arguments
key: tensor name in TF (determined by tf.variable_scope)
"""
filename = str(key)
filename = filename.replace('/', '_')
filename = filename.replace('InceptionResnetV2_', '')
# remove "Repeat" scope from filename
filename = re_repeat.sub('B', filename)
if re_block8.match(filename):
# the last block8 has different name with the previous 9 occurrences
filename = filename.replace('Block8', 'Block8_10')
elif filename.startswith('Logits'):
# remove duplicate "Logits" scope
filename = filename.replace('Logits_' , '', 1)
# from TF to Keras naming
filename = filename.replace('_weights', '_kernel')
filename = filename.replace('_biases', '_bias')
return filename + '.npy'
def extract_tensors_from_checkpoint_file(filename, output_folder='weights'):
"""Extract tensors from a TF checkpoint file.
# Arguments
filename: TF checkpoint file
output_folder: where to save the output numpy array files
"""
if not os.path.exists(output_folder):
os.makedirs(output_folder)
reader = tf.train.NewCheckpointReader(filename)
for key in reader.get_variable_to_shape_map():
# not saving the following tensors
if key == 'global_step':
continue
if 'AuxLogit' in key:
continue
# convert tensor name into the corresponding Keras layer weight name and save
path = os.path.join(output_folder, get_filename(key))
arr = reader.get_tensor(key)
np.save(path, arr)
print("tensor_name: ", key)
# download TF-slim checkpoint for Inception-ResNet v2 and extract
CKPT_URL = 'http://download.tensorflow.org/models/inception_resnet_v2_2016_08_30.tar.gz'
MODEL_DIR = './models'
checkpoint_tar = get_file(
'inception_resnet_v2_2016_08_30.tar.gz',
CKPT_URL,
file_hash='9e0f18e1259acf943e30690460d96123',
hash_algorithm='md5',
extract=True,
cache_subdir='',
cache_dir=MODEL_DIR)
checkpoint_file = glob(os.path.join(MODEL_DIR, 'inception_resnet_v2_*.ckpt'))[0]
extract_tensors_from_checkpoint_file(checkpoint_file)
# load_weights.py
from __future__ import print_function
import os
import numpy as np
from tqdm import tqdm
from keras.models import Model
from inception_resnet_v2 import InceptionResNetV2
WEIGHTS_DIR = './weights'
MODEL_DIR = './models'
OUTPUT_WEIGHT_FILENAME = 'inception_resnet_v2_weights_tf_dim_ordering_tf_kernels.h5'
OUTPUT_WEIGHT_FILENAME_NOTOP = 'inception_resnet_v2_weights_tf_dim_ordering_tf_kernels_notop.h5'
print('Instantiating an empty InceptionResNetV2 model...')
model = InceptionResNetV2(weights=None, input_shape=(299, 299, 3))
print('Loading weights from', WEIGHTS_DIR)
for layer in tqdm(model.layers):
if layer.weights:
weights = []
for w in layer.weights:
weight_name = os.path.basename(w.name).replace(':0', '')
weight_file = layer.name + '_' + weight_name + '.npy'
weight_arr = np.load(os.path.join(WEIGHTS_DIR, weight_file))
# remove the "background class"
if weight_file.startswith('Logits_bias'):
weight_arr = weight_arr[1:]
elif weight_file.startswith('Logits_kernel'):
weight_arr = weight_arr[:, 1:]
weights.append(weight_arr)
layer.set_weights(weights)
print('Saving model weights...')
if not os.path.exists(MODEL_DIR):
os.makedirs(MODEL_DIR)
model.save_weights(os.path.join(MODEL_DIR, OUTPUT_WEIGHT_FILENAME))
print('Saving model weights (no top)...')
model_notop = Model(model.inputs, model.get_layer('Conv2d_7b_1x1_Activation').output)
model_notop.save_weights(os.path.join(MODEL_DIR, OUTPUT_WEIGHT_FILENAME_NOTOP))
Ref: https://github.com/keras-team/keras/issues/8026
9. Keras 自定义loss 函数
有的时候keras 里面提供的loss函数不能满足我们的需求,我们就需要自己去提供loss函数, 比如dice-loss。
dice-loss 一般是dice-coef 取反, 因此先求dice-coef:
import keras.backend as K
def dice_coef(y_true, y_pred, smooth, thresh):
y_pred = y_pred > thresh
y_true_f = K.flatten(y_true)
y_pred_f = K.flatten(y_pred)
intersection = K.sum(y_true_f * y_pred_f)
return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)
但是keras的loss函数只能传y_true, y_pred作为参数,因此我们使用function closure来实现, 就是用函数来返回函数:
def dice_loss(smooth, thresh):
def dice(y_true, y_pred)
return -dice_coef(y_true, y_pred, smooth, thresh)
return dice
调用:
# build model
model = my_model()
# get the loss function
model_dice = dice_loss(smooth=1e-5, thresh=0.5)
# compile model
model.compile(loss=model_dice)
Ref:
https://stackoverflow.com/questions/45961428/make-a-custom-loss-function-in-keras
https://dev.to/andys0975/what-is-dice-loss-for-image-segmentation-3p85
10. Keras multi-class segmentation loss
Ref:
https://github.com/keras-team/keras/issues/9395
https://stackoverflow.com/questions/43900125/how-to-implement-multi-class-semantic-segmentation
网友评论