美文网首页keras学习
【Tool】Keras 基础学习 IV 常用API

【Tool】Keras 基础学习 IV 常用API

作者: ItchyHiker | 来源:发表于2018-09-14 11:03 被阅读0次

    [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

    参考:
    https://stackoverflow.com/questions/40690598/can-keras-with-tensorflow-backend-be-forced-to-use-cpu-or-gpu-at-willi

    相关文章

      网友评论

        本文标题:【Tool】Keras 基础学习 IV 常用API

        本文链接:https://www.haomeiwen.com/subject/rbzngftx.html