赞
踩
前言:keras是一个十分便捷的开发框架,为了更好的追踪网络训练过程中的损失函数loss和准确率accuracy,我们有几种处理方式,第一种是直接通过 history=model.fit(),来返回一个history对象,通过这个对象可以访问到训练过程训练集的loss和accuracy以及验证集的loss和accuracy。第二种方式就是通过自定义一个回调函数Call backs,来实现这一功能,本文主要讲解第二种方式。
本文所针对的例子是卷积神经网络Lenet-5,数据集是mnist数据集。
1.1 什么是回调函数
回调函数是一个函数的合集,会在训练的阶段中所使用。你可以使用回调函数来查看训练模型的内在状态和统计。你可以传递一个列表的回调函数(作为 callbacks
关键字参数)到 Sequential
或 Model
类型的 .fit()
方法。在训练时,相应的回调函数的方法就会被在各自的阶段被调用。
这里有两个关键的点:
(1)状态和统计:其实就是我们希望模型在训练过程中需要从过程中获取什么信息,比如我的损失loss,准确率accuracy等信息就是训练过程中的状态与统计信息;再比如我希望每一个epoch结束之后打印一些相应的自定义提示信息,这也是状态信息。
(2)各自的阶段:模型的训练一般是分为多少个epoch,然后每一个epoch又分为多少个batch,所以这个阶段可以是在每一个epoch之后执行回调函数,也可以是在每一个batch之后执行回调函数。
1.2 回调函数的本质
其实回调函数只是一个很形象的说法,它的本质是一个类,我们直接通过 history=model.fit()返回的history对象就是一个回调函数History类的对象,而History类又继承自Callback类。
回调函数的基类——Call back,他的定义如下:
- class Callback(object): # 用来组建新的回调函数的抽象基类
-
- def __init__(self):
- self.validation_data = None
- self.model = None
-
- def set_params(self, params):
- self.params = params
-
- def set_model(self, model):
- self.model = model
-
- def on_epoch_begin(self, epoch, logs=None):
- pass
-
- def on_epoch_end(self, epoch, logs=None):
- pass
-
- def on_batch_begin(self, batch, logs=None):
- pass
-
- def on_batch_end(self, batch, logs=None):
- pass
-
- def on_train_begin(self, logs=None):
- pass
-
- def on_train_end(self, logs=None):
- pass
属性
keras.models.Model
的实例。 指代被训练模型。被回调函数作为参数的 logs
字典,它会含有于当前批量或训练轮相关数据的键。
特别需要注意的是,上面的每一个函数里面均有一个logs参数,这个参数也是记录训练信息的关键,需要注意以下几个点:
(1)logs是一个字典对象directory;
(2)在不同的方法中这个logs有不同的键值;分别如下:
acc
和 loss
的日志, 也可以选择性的包括 val_loss
(如果在 fit
中启用验证),和 val_acc
(如果启用验证和监测精确值)。这个用的是最多的。size
的日志,在当前批量内的样本数量。loss
的日志,也可以选择性的包括 acc
1.3 系统预定义的回调函数
- BaseLogger
- TerminateOnNaN
- ProgbarLogger
- History
- ModelCheckpoint
- EarlyStopping
- RemoteMonitor
- LearningRateScheduler
- TensorBoard
- ReduceLROnPlateau
- CSVLogger
- LambdaCallback
2.1 回调函数的定义
- # 写一个LossHistory类,保存训练集的loss和acc
- # 当然我也可以完全不这么做,可以直接使用model.fit()方法返回的 history对象去做
- '''Callback有6个常用的方法,这里实现其中的四个
- def on_epoch_begin(self, epoch, logs=None):
- def on_epoch_end(self, epoch, logs=None):
- def on_batch_begin(self, batch, logs=None):
- def on_batch_end(self, batch, logs=None):
- def on_train_begin(self, logs=None):
- def on_train_end(self, logs=None):
- '''
- class LossHistory(Callback): # 继承自Callback类
-
- '''
- 在模型开始的时候定义四个属性,每一个属性都是字典类型,存储相对应的值和epoch
- '''
- def on_train_begin(self, logs={}):
- self.losses = {'batch':[], 'epoch':[]}
- self.accuracy = {'batch':[], 'epoch':[]}
- self.val_loss = {'batch':[], 'epoch':[]}
- self.val_acc = {'batch':[], 'epoch':[]}
-
- # 在每一个batch结束后记录相应的值
- def on_batch_end(self, batch, logs={}):
- self.losses['batch'].append(logs.get('loss'))
- self.accuracy['batch'].append(logs.get('acc'))
- self.val_loss['batch'].append(logs.get('val_loss'))
- self.val_acc['batch'].append(logs.get('val_acc'))
-
- # 在每一个epoch之后记录相应的值
- def on_epoch_end(self, batch, logs={}):
- self.losses['epoch'].append(logs.get('loss'))
- self.accuracy['epoch'].append(logs.get('acc'))
- self.val_loss['epoch'].append(logs.get('val_loss'))
- self.val_acc['epoch'].append(logs.get('val_acc'))
-
- def loss_plot(self, loss_type):
- '''
- loss_type:指的是 'epoch'或者是'batch',分别表示是一个batch之后记录还是一个epoch之后记录
- '''
- iters = range(len(self.losses[loss_type]))
- plt.figure()
- # acc
- plt.plot(iters, self.accuracy[loss_type], 'r', label='train acc')
- # loss
- plt.plot(iters, self.losses[loss_type], 'g', label='train loss')
- if loss_type == 'epoch':
- # val_acc
- plt.plot(iters, self.val_acc[loss_type], 'b', label='val acc')
- # val_loss
- plt.plot(iters, self.val_loss[loss_type], 'k', label='val loss')
- plt.grid(True)
- plt.xlabel(loss_type)
- plt.ylabel('acc-loss')
- plt.legend(loc="upper right")
- plt.savefig("mnist_keras.png")
- plt.show()
2.2 模型的搭建以及训练
mnist数据准备
- # 训练参数
- learning_rate = 0.001
- epochs = 10
- batch_size = 128
- n_classes = 10
-
- # 定义图像维度reshape
- img_rows, img_cols = 28, 28
-
-
- # 加载keras中的mnist数据集 分为60,000个训练集,10,000个测试集
- (x_train, y_train), (x_test, y_test) = mnist.load_data()
-
- # 将图片转化为(samples,width,height,channels)的格式
- 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)
-
- # 将X_train, X_test的数据格式转为float32
- x_train = x_train.astype('float32')
- x_test = x_test.astype('float32')
- # 将X_train, X_test归一化0-1
- x_train /= 255
- x_test /= 255
-
- # 输出0-9转换为ont-hot形式
- y_train = np_utils.to_categorical(y_train, n_classes)
- y_test = np_utils.to_categorical(y_test, n_classes)
模型的搭建以及训练
- # 建立模型
- model = Sequential()
-
- # lenet-5
- model.add(Convolution2D(filters=6, kernel_size=(5, 5), padding='valid', input_shape=(img_rows, img_cols, 1), activation='tanh'))
- model.add(MaxPooling2D(pool_size=(2, 2)))
- model.add(Convolution2D(filters=16, kernel_size=(5, 5), padding='valid', activation='tanh'))
- model.add(MaxPooling2D(pool_size=(2, 2)))
- model.add(Flatten())
- model.add(Dense(120, activation='tanh'))
- model.add(Dense(84, activation='tanh'))
- model.add(Dense(n_classes, activation='softmax'))
-
- #打印模型# verbose=1显示进度条
- model.summary()
-
- # 编译模型
- model.compile(optimizer=Adam(lr=learning_rate), loss='categorical_crossentropy',metrics=['accuracy'])
-
- history = LossHistory() # 这里是使用自定义的Callback回调函数,当然本身fit函数也会返回一个history可供使用
- model.fit(x_train, y_train,batch_size=batch_size,epochs=epochs, verbose=1,validation_data=(x_test, y_test),callbacks=[history])
- model.save('./models/lenet5_weight.h5')
绘制训练过程loss和acc曲线
- #绘制训练的acc-loss曲线
- history.loss_plot('epoch') # 每一个epoch展示一次
最终的运行结果如下:
- Epoch 1/10
- 2019-06-23 08:44:32.930737: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1432] Found device 0 with properties:
- name: GeForce GTX 950 major: 5 minor: 2 memoryClockRate(GHz): 1.2155
- pciBusID: 0000:01:00.0
- totalMemory: 2.00GiB freeMemory: 1.64GiB
- 2019-06-23 08:44:32.937390: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1511] Adding visible gpu devices: 0
- 2019-06-23 08:44:37.003650: I tensorflow/core/common_runtime/gpu/gpu_device.cc:982] Device interconnect StreamExecutor with strength 1 edge matrix:
- 2019-06-23 08:44:37.006358: I tensorflow/core/common_runtime/gpu/gpu_device.cc:988] 0
- 2019-06-23 08:44:37.008076: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1001] 0: N
- 2019-06-23 08:44:37.012620: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1115] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 1388 MB memory) -> physical GPU (device: 0, name: GeForce GTX 950, pci bus id: 0000:01:00.0, compute capability: 5.2)
- 60000/60000 [==============================] - 18s 302us/step - loss: 0.2979 - acc: 0.9151 - val_loss: 0.0863 - val_acc: 0.9730
- Epoch 2/10
- 60000/60000 [==============================] - 4s 61us/step - loss: 0.0810 - acc: 0.9753 - val_loss: 0.0611 - val_acc: 0.9808
- Epoch 3/10
- 60000/60000 [==============================] - 4s 59us/step - loss: 0.0575 - acc: 0.9826 - val_loss: 0.0518 - val_acc: 0.9849
- Epoch 4/10
- 60000/60000 [==============================] - 4s 59us/step - loss: 0.0451 - acc: 0.9857 - val_loss: 0.0480 - val_acc: 0.9848
- Epoch 5/10
- 60000/60000 [==============================] - 4s 59us/step - loss: 0.0375 - acc: 0.9886 - val_loss: 0.0449 - val_acc: 0.9860
- Epoch 6/10
- 60000/60000 [==============================] - 3s 57us/step - loss: 0.0307 - acc: 0.9907 - val_loss: 0.0392 - val_acc: 0.9863
- Epoch 7/10
- 60000/60000 [==============================] - 4s 68us/step - loss: 0.0242 - acc: 0.9923 - val_loss: 0.0389 - val_acc: 0.9882
- Epoch 8/10
- 60000/60000 [==============================] - 4s 75us/step - loss: 0.0192 - acc: 0.9944 - val_loss: 0.0354 - val_acc: 0.9891
- Epoch 9/10
- 60000/60000 [==============================] - 4s 66us/step - loss: 0.0180 - acc: 0.9942 - val_loss: 0.0385 - val_acc: 0.9885
- Epoch 10/10
- 60000/60000 [==============================] - 4s 67us/step - loss: 0.0143 - acc: 0.9956 - val_loss: 0.0516 - val_acc: 0.9860
得到的训练曲线如下:
这里需要使用到sklearn库,代码如下:
- from keras.models import load_model
-
- from sklearn.metrics import confusion_matrix, f1_score, precision_score, recall_score,accuracy_score
-
-
-
- # 测试
- model=load_model('./models/lenet5_weight.h5')
-
- y_predict = model.predict(x_test, batch_size=512, verbose=1)
- # y_predict = (y_predict > 0.007).astype(int)
- y_predict = (y_predict > 0.01).astype(int)
- y_true = np.reshape(y_test, [-1])
- y_pred = np.reshape(y_predict, [-1])
-
- # 评价指标
- accuracy = accuracy_score(y_true, y_pred)
- precision = precision_score(y_true, y_pred)
- recall = recall_score(y_true, y_pred, average='binary')
- f1score = f1_score(y_true, y_pred, average='binary')
-
- # Micro F1: 将n分类的评价拆成n个二分类的评价,将n个二分类评价的TP、FP、RN对应相加,计算评价准确率和召回率,由这2个准确率和召回率计算的F1 score即为Micro F1。
- # Macro F1: 将n分类的评价拆成n个二分类的评价,计算每个二分类的F1 score,n个F1 score的平均值即为Macro F1。
- # 一般来讲,Macro F1、Micro F1高的分类效果好。Macro F1受样本数量少的类别影响大。
- micro_f1 = f1_score(y_true, y_pred,average='micro')
- macro_f1 = f1_score(y_true, y_pred,average='macro')
-
-
- print('accuracy:',accuracy)
- print('precision:',precision)
- print('recall:',recall)
- print('f1score:',f1score)
- print('Macro-F1: {}'.format(macro_f1))
- print('Micro-F1: {}'.format(micro_f1))
-
运行结果是:
- 10000/10000 [==============================] - 2s 151us/step
- accuracy: 0.98813
- precision: 0.8956631049654306
- recall: 0.9975
- f1score: 0.9438425509769599
- Macro-F1: 0.9686030934161676
- Micro-F1: 0.98813
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。