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

[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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • 该文章为转载文章,作者简介:汪剑,现在在出门问问负责推荐与个性化。曾在微软雅虎工作,从事过搜索和推荐相关工作。 T...
    名字真的不重要阅读 5,048评论 0 3
  • 机器学习术语表 本术语表中列出了一般的机器学习术语和 TensorFlow 专用术语的定义。 A A/B 测试 (...
    yalesaleng阅读 1,930评论 0 11
  • 想从Tensorflow循环生成对抗网络开始。但是发现从最难的内容入手还是?太复杂了所以搜索了一下他的始祖也就是深...
    Feather轻飞阅读 4,989评论 1 4
  • 都说有朋自远方来 不亦乐乎! 机缘巧合之下朋友带我们来到了武汉吉他联盟,我非常喜欢这种氛围,虽然不会弹吉他,但还是...
    77红天行者阅读 165评论 0 0
  • 我本画山水 奈何她来扰 想起晨间事 我在读红楼 她却安然睡 无奈另取纸 权且将她描 初画这美猫 反复思量处 还是不...
    桂之华阅读 364评论 7 8