Keras 入门课4 -- 使用ResNet识别cifar10数据集
来源:互联网 发布:网络销售期货合不合法 编辑:程序博客网 时间:2024/06/05 08:04
Keras入门课4:使用ResNet识别cifar10数据集
前面几节课都是用一些简单的网络来做图像识别,这节课我们要使用经典的ResNet网络对cifar10进行分类。
ResNet是何凯明大神提出的残差网络,具体论文见此
ResNet v1
Deep Residual Learning for Image Recognition
https://arxiv.org/pdf/1512.03385.pdf
ResNet v2
Identity Mappings in Deep Residual Networks
https://arxiv.org/pdf/1603.05027.pdf
这一节课,我们只动手实现v1的一个精简版本(因为数据集cifar10的数据比较小)
import kerasfrom keras.layers import Dense, Conv2D, BatchNormalization, Activationfrom keras.layers import AveragePooling2D, Input, Flattenfrom keras.optimizers import Adamfrom keras.regularizers import l2from keras import backend as Kfrom keras.models import Modelfrom keras.datasets import cifar10from keras.callbacks import ModelCheckpoint, LearningRateSchedulerfrom keras.callbacks import ReduceLROnPlateauimport numpy as npimport os
Using TensorFlow backend.
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz170500096/170498071 [==============================] - 64s 0us/step
x_train = x_train/255x_test = x_test/255y_train = keras.utils.to_categorical(y_train,10)y_test = keras.utils.to_categorical(y_test,10)
↓构建模型基本模块,ResNet Block
这里没有用Sequential模型,而是用了另外一种构建模型的方法,即函数式模型(Functional)
Sequential模型有一个缺陷,即网络只能一层一层的堆叠起来,无法处理分支网络的情况。比如ResNet或GoogleNet中的Inception模块。使用Functional模型,构建起模型来十分自由,可以组合成各种各样的网络,可以说Sequential模型是Functional模型的一个子集。
使用函数式模型很简单,直接在网络层模块后写一个括号,参数就是当前层的输入值,返回值就是当前层的输出值,比如:net = Conv2D(…)(inputs)
↓首先构建一个基本的block模块,就是上图的weight layer,这个模块包含了一个卷积层,一个BN层,一个激活层。可以看到上图下面那个layer没有激活层,所以函数内做了一个判断
BN层的作用是对输出参数做归一化,可以有效使网络更易训练。一般来说,加了BN层的网络,可以不必再用Dropout层。
同时这一次我们在卷积层中加入了L2正则化,目的是提升模型的泛化能力。
#ResNet Blockdef resnet_block(inputs,num_filters=16, kernel_size=3,strides=1, activation='relu'): x = Conv2D(num_filters,kernel_size=kernel_size,strides=strides,padding='same', kernel_initializer='he_normal',kernel_regularizer=l2(1e-4))(inputs) x = BatchNormalization()(x) if(activation): x = Activation('relu')(x) return x
↓这里构建整个网络。由于我们要处理的图像较小,所以ResNet用的也是一个20层的小号版。
总体上分为五大部分。上面那张图我们称之为一个building block
输入层
↓
6层 filter大小为16的building block
↓
6层 filter大小为32的building block
↓
6层 filter大小为64的building block
↓
一层全连接
一层输出层
第2~7层属于一个很规整的层叠加,每一个循环里都是在搭建一个building block
第8~13层里面的首层的strides=2,这样输出就是16*16*32大小的张量,而输入是32*32*16大小的张量,所以对输入又做了一个卷积操作,使得其shape和正常卷积层的输出一直,这样才可以执行add操作。
第14~19层套路一样
返回为通过Model初始化过的一个模型
# 建一个20层的ResNet网络 def resnet_v1(input_shape): inputs = Input(shape=input_shape)# Input层,用来当做占位使用 #第一层 x = resnet_block(inputs) print('layer1,xshape:',x.shape) # 第2~7层 for i in range(6): a = resnet_block(inputs = x) b = resnet_block(inputs=a,activation=None) x = keras.layers.add([x,b]) x = Activation('relu')(x) # out:32*32*16 # 第8~13层 for i in range(6): if i == 0: a = resnet_block(inputs = x,strides=2,num_filters=32) else: a = resnet_block(inputs = x,num_filters=32) b = resnet_block(inputs=a,activation=None,num_filters=32) if i==0: x = Conv2D(32,kernel_size=3,strides=2,padding='same', kernel_initializer='he_normal',kernel_regularizer=l2(1e-4))(x) x = keras.layers.add([x,b]) x = Activation('relu')(x) # out:16*16*32 # 第14~19层 for i in range(6): if i ==0 : a = resnet_block(inputs = x,strides=2,num_filters=64) else: a = resnet_block(inputs = x,num_filters=64) b = resnet_block(inputs=a,activation=None,num_filters=64) if i == 0: x = Conv2D(64,kernel_size=3,strides=2,padding='same', kernel_initializer='he_normal',kernel_regularizer=l2(1e-4))(x) x = keras.layers.add([x,b])# 相加操作,要求x、b shape完全一致 x = Activation('relu')(x) # out:8*8*64 # 第20层 x = AveragePooling2D(pool_size=2)(x) # out:4*4*64 y = Flatten()(x) # out:1024 outputs = Dense(10,activation='softmax', kernel_initializer='he_normal')(y) #初始化模型 #之前的操作只是将多个神经网络层进行了相连,通过下面这一句的初始化操作,才算真正完成了一个模型的结构初始化 model = Model(inputs=inputs,outputs=outputs) return model
model = resnet_v1((32,32,3))model.compile(loss='categorical_crossentropy',optimizer=Adam(),metrics=['accuracy'])model.summary()
模型太长,就不写了
==================================================================================================
Total params: 598,186
Trainable params: 595,466
Non-trainable params: 2,720
________________________________________________________________________________________
↓callbacks
model的.fit方法有一个参数是callbacks,这个参数可以传入一些其他待执行的函数,在训练过程中,每一个epoch会调用一次列表中的callbacks
本次课程用到的几个回调函数
ModelCheckpoint:用来每个epoch存储一遍模型
LearningRateScheduler:用来动态调整学习率。其输入为一个函数,该函数的输入为当前epoch数,返回为对应的学习率
ReduceLROnPlateau:用来在训练停滞不前的时候动态降低学习率。
checkpoint = ModelCheckpoint(filepath='./cifar10_resnet_ckpt.h5',monitor='val_acc', verbose=1,save_best_only=True)def lr_sch(epoch): #200 total if epoch <50: return 1e-3 if 50<=epoch<100: return 1e-4 if epoch>=100: return 1e-5lr_scheduler = LearningRateScheduler(lr_sch)lr_reducer = ReduceLROnPlateau(monitor='val_acc',factor=0.2,patience=5, mode='max',min_lr=1e-3)callbacks = [checkpoint,lr_scheduler,lr_reducer]
model.fit(x_train,y_train,batch_size=64,epochs=200,validation_data=(x_test,y_test),verbose=1,callbacks=callbacks)
Train on 50000 samples, validate on 10000 samplesEpoch 1/20049984/50000 [============================>.] - ETA: 0s - loss: 1.8382 - acc: 0.4365Epoch 00001: val_acc improved from -inf to 0.51090, saving model to ./cifar10_resnet_ckpt.h550000/50000 [==============================] - 302s 6ms/step - loss: 1.8381 - acc: 0.4365 - val_loss: 1.5821 - val_acc: 0.5109.........Epoch 200/20049984/50000 [============================>.] - ETA: 0s - loss: 0.0105 - acc: 1.0000Epoch 00200: val_acc did not improve50000/50000 [==============================] - 279s 6ms/step - loss: 0.0105 - acc: 1.0000 - val_loss: 1.3110 - val_acc: 0.8244
scores = model.evaluate(x_test,y_test,verbose=1)print('Test loss:',scores[0])print('Test accuracy:',scores[1])
10000/10000 [==============================] - 18s 2ms/stepTest loss: 1.310983159506321Test accuracy: 0.8244
通过了200个批次的训练,训练集的准确率已经达到了100%,测试集达到了82.44%。这还是没有使用数据增强的效果,如果使用数据增强,准确率可以达到90+%
总结
- 学习了一种新的构建模型的方法,函数式模型(Functional),更自由灵活
- 学习了如何将通过Functional方式定义的层初始化为一个模型(Model)
- 使用keras.layers.add方法,可以将两个一模一样的张量进行相加
- 搭建了一个精简版的ResNet网络
- 学习了如何在训练中调用回调函数
- 学习了在训练过程中动态的调节学习率(使用LearningRateScheduler)
- 学习了保存checkpoint(使用ModelCheckpoint)
- 使用ReduceLROnPlateau在训练进入平台期的时候动态调节学习率
参考:
https://github.com/keras-team/keras/blob/master/examples/cifar10_resnet.py
- Keras 入门课4 -- 使用ResNet识别cifar10数据集
- Keras入门课3 -- 使用CNN识别cifar10数据集
- keras实现VGG16 CIFAR10数据集
- [Keras实战] 构建DenseNet实现Cifar10数据集90%+准确率
- keras构建卷积神经网络识别cifar10
- Keras入门课2 -- 使用CNN识别mnist手写数字
- caffe使用cifar10数据集问题记录
- Keras入门-预训练模型fine-tune(ResNet)
- 转换Cifar10数据集
- keras 入门 --手写数字识别
- keras -- 实现cifar10分类
- 使用Caffe基于cifar10进行物体识别
- Keras 入门课1 -- 用MLP识别mnist手写字符
- keras入门实战:手写数字识别
- Keras入门例子:cnn实现手写识别
- Caffe在Cifar10上复现ResNet
- Caffe在Cifar10上复现ResNet
- 4用于cifar10的卷积神经网络-4.4/4.5cifar10数据集读取和数据增强扩充(上/下)
- 《CString与double互相转化》
- pdf转CAD教学步骤
- eclipse中查看原码:Source not found
- Linux下配置Nginx+Apache+PHP+Tomcat+Java同时运行
- ORACLE表空间大小限制
- Keras 入门课4 -- 使用ResNet识别cifar10数据集
- 一步一步制作yaffs/yaffs2根文件系统(六)---完善命令行提示符
- maven 在类路径或引导类路径中找不到程序包 java.lang
- Android 8.0
- leetcode 649. Dota2 Senate Doat2议员
- 航空公司VIP客户查询(25 分)(Hash)
- 动态生成easyui PropertyGrid(属性表格)
- Java 位运算(移位、位与、或、异或、非) 以及负数的二进制相互计算
- spark算子实战