Keras的一些使用事项
本文以Anaconda自带的数据及mnist为例,阐述一些小白作者在学习过程中的经验。
-
导入数据集
在第一次导入数据集的时候,由于是图片文件,所以一下子从外网上下载比较慢(60M宽带实测)。在csdn上搜了一些方法,以下是我的整理。
def load_data(path=r'C:\Users\Administrator\anaconda3\Lib\site-packages\keras\datasets\mnist.npz'): """Loads the MNIST dataset. # Arguments path: path where to cache the dataset locally (relative to ~/.keras/datasets). # Returns Tuple of Numpy arrays: `(x_train, y_train), (x_test, y_test)`. """ # ============================================================================= # path = get_file(path, # origin='https://s3.amazonaws.com/img-datasets/mnist.npz', # file_hash='8a61469f7ea1b51cbae51d4f78837e45') # with np.load(path, allow_pickle=True) as f: # x_train, y_train = f['x_train'], f['y_train'] # x_test, y_test = f['x_test'], f['y_test'] # return (x_train, y_train), (x_test, y_test) # ============================================================================= #不下载,使用本地的npz数据 f = np.load(path) x_train, y_train = f['x_train'], f['y_train'] x_test, y_test = f['x_test'], f['y_test'] f.close() return (x_train, y_train), (x_test, y_test)
其中,path参数我改为了自己库的绝对路径(怕相对路径导入会报错)。
灰色注释掉的部分是原本的代码,主要功能是从网上下载数据集。
然后我从网上找了一个直接下载到keras库里,在代码中使用np.load(path)直接导入,和原代码一样找出npz压缩文件的训练数据和测试数据,并返回。 -
判断通道位置
由于使用的是手写数据集(pic),所以需要判断图片是单通道还是多通道,如果是多通道,则需要规定通道位是在前还是在后。from keras import backend as K #判断通道在前(first),或者在后(last) if K.image_data_format() == 'channels_first': x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols) x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols) input_shape = (1, img_rows, img_cols) else: x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1) x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1) input_shape = (img_rows, img_cols, 1)
如上代码所示,如果idf是channels_first,将通道加于shape后,另外则加于最后。使得单通道变多通道。
-
然后是数据的归一化
x_train = x_train.astype('float32') x_test = x_test.astype('float32') x_train /= 255 x_test /= 255 print('x_train shape:', x_train.shape) print(x_train.shape[0], 'train samples') print(x_test.shape[0], 'test samples')
-
独热编码
y_train = keras.utils.to_categorical(y_train, num_classes) y_test = keras.utils.to_categorical(y_test, num_classes)
由于本实验使用的是categorical_crossentropy损失函数,即多分类问题,所以是需要对一维的标签进行独热编码,以适应模型的compile。
-
搭建模型架构
本次实验使用的是Sequential 顺序模型。顺序模型是函数式模型的简略版,为最简单的线性、从头到尾的结构顺序,不分叉,是多个网络层的线性堆叠。
Sequential 实现了很多层,包括core核心层,Convolution卷积层、Pooling池化层等非常丰富有趣的网络结构。
我们可以通过将层的列表传递给Sequential的构造函数,来创建一个Sequential模型。在这里推荐使用model.add()的方法逐层叠加模型。#实例化一个顺序模型 model = Sequential() #二维卷积:32个卷积核,卷积核尺寸为(3, 3),使用relu激活函数,指定输入尺寸(包括通道数),padding填充为原来图片大小('same' or 'valid') model.add(Conv2D(filters=32, kernel_size=(3, 3), activation='relu', input_shape=input_shape, padding='same')) #二维卷积:64个卷积核,卷积核尺寸为(3, 3),使用relu激活函数 model.add(Conv2D(filters=64, kernel_size=(3, 3), activation='relu')) #二维池化函数,采用最大池化,池化核尺寸为(2, 2) model.add(MaxPooling2D(pool_size=(2, 2))) #为防止复杂模型在简单数据集上产生过拟合的情况,随机抛弃部分神经元(概率为0.25),即抛弃每张图片中的部分点 model.add(Dropout(0.25)) #抛弃整张图片,概率为0.1 model.add(SpatialDropout2D(0.1)) #将数据展平 model.add(Flatten()) #一个全连接层 model.add(Dense(128, activation='relu')) #随机抛弃 model.add(Dropout(0.5)) #全连接层,输出维度为分类的数量(num_classes) model.add(Dense(num_classes, activation='softmax'))
值得注意的是,在模型的第一层,需要指定输入的数据的维度,以便让模型知道自己该变成什么样子(有点抽象)。在第二层及以后,顺序模型可以推导出上一层架构输出的数据维度,所以不需要再指定(但是指定也是可以的,但是不能有错误)。
关于模型的各个层推荐去看Keras的中文文档(因为我英语比较菜),或者一些博主的介绍,已经很详细了,我这里就不再做赘述。 -
模型的配置
搭建完毕模型后,就需要对模型进行配置,即指定模型训练过程中的损失函数,优化器,以及在训练模型中显示的模型相关指标。#模型配置:loss采用cat_cross,必须使用独热编码后的标签,opt使用ada优化器,评估标准使用acc、mse等 model.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adadelta(), metrics=['accuracy']) ``` 上文提到:当使用categorical_crossentropy损失函数时,你的标签应为多类模式,例如如果你有10个类别,每一个样本的标签应该是一个10维的向量,该向量在对应有值的索引位置为1其余为0。这里使用了categorical_crossentropy损失函数,所以对应的标签也应该进行独热编码。 7. 模型的训练 在讲模型的训练之前,让我们先了解一下callbacks回调函数(我这里使用写好的代码来说明,写好了对应的注解): callbacks的主要作用是对正在训练中的模型进行实时反馈,例如当学习接近最优时,提早停止训练,方式模型陷入局部最优;或者对每个k个epoch的模型进行保存,从而实时记录模型的情况,并且下次可以直接使用保存好的模型进行训练预测。 ```python #loss是训练集的损失值,val_loss是测试集的损失值,acc同理。 '''早停: monitor(测试指标):val_loss(val_loss,val_accuracy,loss,accuracy); min_delta(在被监测的数据中被认为是提升的最小变化,例如,小于 min_delta 的绝对变化会被认为没有提升。) patience(没有进步的训练轮数,在这之后训练就会被停止。即当前轮次提升数小于(或大于)min_delta,之后再训练patience轮,停止训练。) mode: {auto, min, max} 其中之一。在 min 模式中, 当被监测的数据停止下降,训练就会停止; 在 max 模式中,当被监测的数据停止上升,训练就会停止; 在 auto 模式中,方向会自动从被监测的数据的名字中判断出来。 一般模型为auto。 ''' early_stopping = EarlyStopping(monitor='val_loss', min_delta=0.05, patience=2, verbose=1) '''每轮次最佳模型的保存: filepath: 字符串,保存模型的路径。可以包括命名格式选项,可以由 epoch 的值和logs的键(由on_epoch_end参数传递)来填充,一般保存为.h5文件。 monitor: 被监测的数据。 verbose: 详细信息模式,0或者1。 save_best_only: 如果 save_best_only=True,被监测数据的最佳模型就不会被覆盖。 mode: {auto, min, max} 的其中之一。 如果 save_best_only=True,那么是否覆盖保存文件的决定就取决于被监测数据的最大或者最小值。 对于 val_acc,模式就会是 max,而对于 val_loss,模式就需要是 min,等等。 在 auto 模式中,方向会自动从被监测的数据的名字中判断出来。 save_weights_only: 如果 True,那么只有模型的权重会被保存(model.save_weights(filepath)),否则的话,整个模型会被保存(model.save(filepath))。 period: 每个检查点之间的间隔(训练轮数)。 ''' model_checkpoint = ModelCheckpoint(filepath='ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5', monitor='val_loss', verbose=1, save_best_only=True, save_weights_only=False, period=1) '''在学习停止后,调整学习速率,以达到更好的模型训练结果: 当学习停止时,模型总是会受益于降低 2-10 倍的学习速率。这个回调函数监测一个数据并且当这个数据在一定「有耐心」的训练轮之后还没有进步,那么学习速率就会被降低。 monitor: 被监测的数据。 factor: 学习速率被降低的因数。新的学习速率 = 学习速率 * 因数 patience: 没有进步的训练轮数,在这之后训练速率会被降低。 verbose: 整数。0:安静,1:更新信息。 mode: {auto, min, max} 其中之一。如果是 min 模式,学习速率会被降低如果被监测的数据已经停止下降; 在 max 模式,学习塑料会被降低如果被监测的数据已经停止上升; 在 auto 模式,方向会被从被监测的数据中自动推断出来。 epsilon: 对于测量新的最优化的阀值,只关注巨大的改变。 cooldown: 在学习速率被降低之后,重新恢复正常操作之前等待的训练轮数量。 min_lr: 学习速率的下边界。 ''' reduce_lr_on_plateau = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=2, verbose=1)
模型的训练一般使用fit()方法。
#模型训练 model.fit(x_train, y_train, #训练数据及标签 batch_size=batch_size, #每次梯度更新的样本数 epochs=epochs, #训练的轮次 verbose=1, #训练日志显示模型,0 = 安静模式, 1 = 进度条, 2 = 每轮一行 validation_data=(x_test, y_test), #用来评估损失,以及在每轮结束时的任何模型度量指标。模型将不会在这个数据上进行训练。 callbacks=[early_stopping, model_checkpoint])
这里只使用了early_stopping和model_checkpoint的callbacks方法。
一般在模型训练的时候使用测试数据进行评估(考虑到模型对训练数据的拟合程度较高,得出的评估结果会比较不真实),或者可以考虑交叉验证的方法(这个我在sklearn中用的比较多,keras中不怎么使用)。 -
模型的评估
模型的评估有许多方法,这里我使用了keras自带的evaluate方法。
Keras中model.evaluate()返回的是损失值和你选定的指标值(例如,精度accuracy),以list的形式返回,可以用下标访问。evaluate(x=None, y=None, batch_size=None, verbose=1, sample_weight=None, steps=None)
我简单调用了一下:
score = model.evaluate(x_test, y_test, verbose=1)
-
模型的保存
最后再提一下模型的保存,由于在介绍callbacks的时候涉及到,这里做一下补充。
第一种:model.save(path)
这种方法保存的是整个模型,包括模型的基本架构和权重参数,加载这种模型可以直接用于训练。model_save_path = "model_file_path.h5" # 保存模型 model.save(model_save_path) # 删除当前已存在的模型 del model # 加载模型 from keras.models import load_model model = load_model(model_save_path)
第二种:model.save_weights()
这种保存方法保存的是模型的参数,不能直接用于训练,一般大型的神经网络用的是这种保存方法,需要根据模型本身重新进行模型架构。model_save_path = "model_file_path.h5" # 保存模型权重 model.save_weights(model_save_path) # 加载模型权重 model.load_weights(model_save_path)
这是两种方法的对比:
项目 是否保存模型结构 是否保存模型权重 是否能继续训练网络 是否能进行模型预测 model.save() 是 是 是 是 model.save_weights() 否 是 否 是
这就是我这次要分享的内容,到底了。