Keras上手

30秒上手

https://keras.io/#getting-started-30-seconds-to-keras

核心数据结构是 model,它是网络层的容器。

最简单的 model 是线性容器 Sequential

from keras.models import Sequential

model = Sequential()

.add() 向容器中添加层:

from keras.layers import Dense

# 上层输入100,本层输出64
model.add(Dense(units=64, activation='relu', input_dim=100))

# 上层输入64,本层输出10
model.add(Dense(units=10, activation='softmax'))

.compile() 配置训练参数:

model.compile(loss='categorical_crossentropy',
              optimizer='sgd',
              metrics=['accuracy'])

或更详细地配置优化器参数:

model.compile(loss=keras.losses.categorical_crossentropy,
              optimizer=keras.optimizers.SGD(lr=0.01, momentum=0.9, nesterov=True))

.fit() 进行训练:

# x_train and y_train are Numpy arrays --just like in the Scikit-Learn API.
model.fit(x_train, y_train, epochs=5, batch_size=32)

或手动传入一个 batch:

model.train_on_batch(x_batch, y_batch)

.evaluate() 评估模型:

loss_and_metrics = model.evaluate(x_test, y_test, batch_size=128)

.predict() 进行预测:

classes = model.predict(x_test, batch_size=128)

Sequential 模型上手

指定输入维度

Sequential的第一层必须知道输入的维度信息。

有以下几种方式:

  • 在第一层中指定 input_shape 参数,input_shape 中不包括 batch_size 维度。
  • 有些层(如 Dense)还可以指定 input_dim 参数。

例子:

model = Sequential()
model.add(Dense(32, input_shape=(784,)))
model = Sequential()
model.add(Dense(32, input_dim=784))

配置训练参数

.compile() 函数接收三个参数:

  • 优化器
  • 损失函数
  • 评估指标

例子:

# 多分类问题,输出节点数为类别个数
model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# 二分类问题,输出节点数为1
model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['accuracy'])

# 回归问题
model.compile(optimizer='rmsprop',
              loss='mse')

# 自定义评估指标
import 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])

训练

输入特征和输出label都是 Numpy 数组类型。

.fit()进行训练。

例子:

# 二分类问题,输入特征为一维向量,输出为一个数值

model = Sequential()
model.add(Dense(32, activation='relu', input_dim=100))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['accuracy'])

# 生成1000个训练样本,label值为0/1
import numpy as np
data = np.random.random((1000, 100))
labels = np.random.randint(2, size=(1000, 1))

# 训练模型
model.fit(data, labels, epochs=10, batch_size=32)
# 10类问题

model = Sequential()
model.add(Dense(32, activation='relu', input_dim=100))
model.add(Dense(10, activation='softmax'))
model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# 生成1000个训练样本,label值为0~9
import numpy as np
data = np.random.random((1000, 100))
labels = np.random.randint(10, size=(1000, 1))

# 训练模型
one_hot_labels = keras.utils.to_categorical(labels, num_classes=10)

# Train the model, iterating on the data in batches of 32 samples
model.fit(data, one_hot_labels, epochs=10, batch_size=32)

例子

MLP多分类问题:

import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation
from keras.optimizers import SGD

# 生成训练样本和测试样本
import numpy as np
x_train = np.random.random((1000, 20))
y_train = keras.utils.to_categorical(np.random.randint(10, size=(1000, 1)), num_classes=10)
x_test = np.random.random((100, 20))
y_test = keras.utils.to_categorical(np.random.randint(10, size=(100, 1)), num_classes=10)

model = Sequential()
# Dense(64) 表示包含64个节点的隐层
# 第一层必须指定输入特征的维度
# 这里输入特征是一维向量,长度为20
model.add(Dense(64, activation='relu', input_dim=20))
model.add(Dropout(0.5))
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))

sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy',
              optimizer=sgd,
              metrics=['accuracy'])

model.fit(x_train, y_train,
          epochs=20,
          batch_size=128)
score = model.evaluate(x_test, y_test, batch_size=128)

用一维CNN对序列分类:

from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras.layers import Embedding
from keras.layers import Conv1D, GlobalAveragePooling1D, MaxPooling1D

seq_length = 64

model = Sequential()
model.add(Conv1D(64, 3, activation='relu', input_shape=(seq_length, 100)))
model.add(Conv1D(64, 3, activation='relu'))
model.add(MaxPooling1D(3))
model.add(Conv1D(128, 3, activation='relu'))
model.add(Conv1D(128, 3, activation='relu'))
model.add(GlobalAveragePooling1D())
model.add(Dropout(0.5))
model.add(Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy',
              optimizer='rmsprop',
              metrics=['accuracy'])

model.fit(x_train, y_train, batch_size=16, epochs=10)
score = model.evaluate(x_test, y_test, batch_size=16)

保存模型

保存网络结构+权值+优化器状态

model.save(path) 保存以下内容:

  • 模型结构
  • 权值
  • 训练参数(损失函数、优化器)
  • 优化器状态(以便进行继续训练)

keras.models.load_model(path) 加载上述内容。

例子:

from keras.models import load_model

model.save('my_model.h5')
del model

# 返回编译好的模型(配置好训练参数)
model = load_model('my_model.h5')

仅保存网络结构

model.to_json()model.to_yaml()

json_string = model.to_json()
yaml_string = model.to_yaml()

加载网络结构:

# 从 JSON 字符串中加载网络结构
from keras.models import model_from_json
model = model_from_json(json_string)

# 从 YAML 字符串中加载网络结构
from keras.models import model_from_yaml
model = model_from_yaml(yaml_string)

仅保存网络权值

model.save_weights()

model.save_weights('my_model_weights.h5')

加载网络权值:

model.load_weights('my_model_weights.h5')

回调函数类

keras.callbacks.Callback() 基类:

  • 类成员 params
    • 字典变量
    • 保存了训练参数:如信息显示方法verbosity,batch大小,epoch数
  • 类成员 model:正在训练的模型

用于回调的函数有四个,它们的参数被自动填充相关的训练信息:

  • on_train_batch_begin()
    • 参数 batch:整数,当前 epoch 下已遍历的 batch 数
    • 参数 logs:字典,
      • logs["batch"]:当前 batch 数
      • logs["size"]:batch大小
  • on_batch_end()
    • 参数 batch:整数,当前 epoch 下已遍历的 batch 数
    • 参数 logs:字典,保存了训练集的评估指标值
  • on_epoch_begin()
    • 参数 epoch:当前 epoch 数
  • on_epoch_end()
    • 参数 epoch:当前 epoch 数
    • 参数 logs:字典,保存了训练集和验证集(如果指定了验证集数据)的评估指标值

CSVLogger

将每一代模型评估结果保存到csv文件。若提供了验证集,默认结果包含五列:

  • epoch
  • acc
  • loss
  • val_acc
  • val_loss

例子:

csv_logger = CSVLogger('training.log')
model.fit(X_train, Y_train, callbacks=[csv_logger])

构造参数:

  • filename:csv文件名
  • separator:csv分隔符
  • append:若为 Fasle,则覆盖已有csv文件内容

代码解析

  1. 构造函数参数:

    class CSVLogger(Callback):
        def __init__(self, filename, separator=',', append=False):
            self.sep = separator
            self.filename = filename
            self.append = append
            # ...
    
  2. 成员变量:

    • writer:用于将内容写入CSV文件
    • keys:CSV列名
    • csv_file:CSV文件
    • append_header:是否在第一行想写入列名,初始值为 True
        def __init__(self, filename, separator=',', append=False):
            # ...
            self.writer = None
            self.keys = None
            self.csv_file = None
            self.append_header = True
    
  3. 处理 append 参数:

    • 如果为 True,将文件写入模式设为 'a'
      • 如果CSV文件已存在且已有内容,则不必写入第一行列名(将 append_header 设为 False)
    • 如果为 Fasle,将文件写入模式设为 'w'
        def on_train_begin(self, logs=None):
            if self.append:
                mode = 'a'
                if os.path.exists(self.filename):
                    with open(self.filename, 'r') as f:
                        self.append_header = not bool(len(f.readline()))
            else:
                mode = 'w'
            # ...
    
  4. 打开 CSV 文件:

        def on_train_begin(self, logs=None):
            # ...
            self.csv_file = io.open(self.filename, mode)
    
  5. 写入内容前,先创建 keys 和 writer:

    def on_epoch_end(self, epoch, logs=None):
        if self.keys is None:
            self.keys = sorted(logs.keys)
            
        class CustomDialect(csv.excel):
            delimiter = self.sep
            
        if not self.writer:
            fieldnames = ['epoch'] + self.keys
            self.writer = csv.DictWriter(self.csv_file,
                                         fieldnames=fieldnames,
                                         dialect=CustomDialect)
            if self.append_header:
                self.writer.writeheader()
                
        # ...
    
  6. 更新 CSV:

    def on_epoch_end(self, epoch, logs=None):
        # ...
        
        row_dict = OrderedDict({'epoch': epoch})
        row_dict.update((key, logs[key]) for key in self.keys)
        self.writer.writerow(row_dict)
        self.csv_file.flush()
    
  7. 关闭 CSV 文件:

        def on_train_end(self, logs=None):
            self.csv_file.close()
            self.writer = None
    

ModelCheckpoint

训练时自动保存模型或模型权值。

构造参数:

  • filepath:包含格式化字符串的文件名,例如:
    • weights.{epoch:02d}-{val_loss:.2f}.hdf5
  • monitor:用于评估模型好坏的指标,如 val_accval_loss
  • save_best_only:若为 True,则仅保存根据 monitor 指标选出的更好模型
  • save_weights_only:若为 True,则仅保存模型权值。
  • mode:值为 {'auto', 'min', 'max'} 其中之一,表示评估指标 monitor 越小越好还是越大越好。
    • 对于 val_acc 指标,选 max,即越大越好
    • 对于 val_loss 指标,选 min,即越小越好
  • period:两次保存动作相隔的代数。
  • verbose:是否输出说明文本

代码解析

  1. 构造函数

    class ModelCheckpoint(Callback):
        def __init__(self,
                     filepath,
                     monitor='val_loss',
                     verbose=0,
                     save_best_only=False,
                     save_weights_only=False,
                     mode='auto',
                     period=1):
            super(ModelCheckpoint, self).__init__()
            self.monitor = monitor
            self.verbose = verbose
            self.filepath = filepath
            self.save_best_only = save_best_only
            self.save_weights_only = save_weights_only
            self.period = period
            # ...
    
  2. 根据 mode 调整指标比较方法

        def __init__(self, ...):
            # ...
            if mode == 'min':
                self.monitor_op = np.less
                self.best = np.Inf
            elif mode == 'max':
                self.monitor_op = np.greater
                self.best = -np.Inf
            # ...
    
  3. 创建一个epoch计数器

        def __init__(self, ...):
            # ...
            self.epochs_since_last_save = 0
    
  4. 每训练一代,计数器加1,到达 period值时,保存模型

        def on_epoch_end(self, epoch, logs=None):
            logs = logs or {}
            self.epochs_since_last_save += 1
            if self.epochs_since_last_save >= self.period:
                self.epochs_since_last_save = 0
                # ...
    
  5. 解析文件名

        def on_epoch_end(self, epoch, logs=None):
            if self.epochs_since_last_save >= self.period:
                # ...
                filepath = self.filepath.format(epoch=epoch + 1, **logs)
    
  6. 保存模型

        def on_epoch_end(self, epoch, logs=None):
            if self.epochs_since_last_save >= self.period:
                # ...
                if self.save_best_only:
                    # ...
                else:
                    if self.verbose > 0:
                        print('\nEpoch %05d: saving model to %s' % (epoch + 1, filepath))
                    if self.save_weights_only:
                        self.model.save_weights(filepath, overwrite=True)
                    else:
                        self.model.save(filepath, overwrite=True)
                    
    
  7. 如果参数要求仅保存更好的模型,则需比较模型评估指标

        def on_epoch_end(self, epoch, logs=None):
            if self.epochs_since_last_save >= self.period:
                # ...
                if self.save_best_only:
                    current = logs.get(self.monitor)
                    if self.monitor_op(current, self.best):
                        if self.verbose > 0:
                            print('\nEpoch %05d: %s improved from %0.5f to %0.5f,'
                                  ' saving model to %s' % (epoch + 1, self.monitor, self.best,
                                                           current, filepath))
                        self.best = current
                        if self.save_weights_only:
                            self.model.save_weights(filepath, overwrite=True)
                        else:
                            self.model.save(filepath, overwrite=True)
                    else:
                        if self.verbose > 0:
                            print('\nEpoch %05d: %s did not improve from %0.5f' %
                                  (epoch + 1, self.monitor, self.best))
                else:
                    # ...
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345