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:
                    # ...
    

你可能感兴趣的:(Keras上手)