前言
cifar-9 分类图像数据集是从cifar10图像数据集中提取出的包含9个分类的图像数据集,cifar图像官网提供了cifar10图像数据集和cifar100图像数据集的下载。本次竞赛中,官方给定的数据集是从cifar10图像数据集中剔除掉“青蛙”这个类别的数据集。所有的图像都是32×32×3的RGB彩色图像。
数据预处理
比赛方提供的包含训练数据集和测试数据集的压缩包,每个目录的名称代表目录中数据的标签。在整个任务之前,首先,需要对数据集进行预处理。
数据的文件结构如下所示:
对原始的图像数据进行处理,主要分为以下几个步骤:
原始图像写入csv表格中
为了方便对数据进行操作,采用将图像数据以矩阵的方式写入csv中的方法,csv中,第一列表示图像的标签,用0-8
这几个数字依次表示9个分类,1-3073类表示32×32×3的像素值。具体代码如下所示:
- 解压图像数据
with zipfile.ZipFile("cifarData.zip") as zf:
zf.extractall()
- 获得训练集和测试集的所有标签
按照文件目录,图像的标签即是当前图像所在文件夹(目录)的名称,则有如下代码:
#获得所有标签
train_label = [] #训练集标签
test_label = [] #测试集标签
for i in os.listdir('data/train'):
train_label.append(i)
for i in os.listdir('data/test'):
test_label.append(i)
- 将图像数据写入csv文件中
参数mode
代表了写入数据集的类别,即训练集数据还是测试集数据
import cv
import csv
label_num = 0
def writeDataAsCsv(mode,fileName):
with open(fileName,"w",newline="") as f:
column_name=["lable"]
for i in range(3072):
column_name.extend(["pix"+str(i)])
write = csv.writer(f)
write.writerow(column_name)
if mode==”test“:
for i in test_label:
for j in os.walk('data/'+str(mode)+'/'+str(i)):
#print(i,j[2])
#print(train_label.index(i))
for k in j[2]:
img = cv2.imread("./data/"+str(mode)+"/"+str(i)+'/'+str(k))
row_data = [test_label.index(i)]
row_data.extend(img.flatten())
write.writerow(row_data)
- 对数据集归一化和one-hot编码
对数据集进行归一化和one-hot编码之前,首先需要加载csv格式的数据,为了加快加载数据的速度,可以采用dask.dataframe
替代常用的pandas.DataFrame
,并且需要对csv中数据的顺序随机调整(类似于洗牌操作)具体代码如下所示:
import dask.dataframe as dd
import numpy as np
import pandas as pd
train_data = dd.read_csv("train.csv",engine='python')
train_data = train_data.sample(frac=1)
test_data = dd.read_csv("test.csv",engine='python')
test_data = test_data.sample(frac=1)
#标签数据和图像数据进行划分
train_y = train_data['lable']
train_x = train_data.drop('lable',axis=1)
test_y = test_data['lable']
test_x = test_data.drop('lable',axis=1)
train_x = np.array(train_x).astype('float32')/255
test_x = np.array(test_x).astype('float32')/255
train_x = train_x.reshape(train_x.shape[0],32,32,3)
test_x = test_x.reshape((test_x.shape[0],32,32,3))
from keras.utils import np_utils
train_y = np_utils.to_categorical(train_y,9)
test_y = np_utils.to_categorical(test_y,9)
搭建模型
模型的结构是一个20层resnet的模型,每6层之间用一个shortcut路径进行连接,模型结构的代码如下所示:
# 残差模块
def 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
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(9,activation='softmax',
kernel_initializer='he_normal')(y)
#初始化模型
#之前的操作只是将多个神经网络层进行了相连,通过下面这一句的初始化操作,才算真正完成了一个模型的结构初始化
model = Model(inputs=inputs,outputs=outputs)
return model
拟合模型
模型实现之后,在拟合之前,可以实现数据增强增加模型的泛化能力,利用学习率衰减获得损失函数最优解,利用checkpoint保存模型的最佳的权重参数,具体如下代码所示:
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-5
lr_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]
batch_size = 64
data_generator = tf.keras.preprocessing.image.ImageDataGenerator(width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True)
train_generator = data_generator.flow(train_x, train_y, batch_size)
steps_per_epoch = train_x.shape[0] // batch_size
model.fit_generator(train_generator, validation_data=(test_x, test_y), steps_per_epoch=steps_per_epoch, epochs=200,callbacks=callbacks)
做出预测
训练完成之后,利用保存好的模型权重参数进行预测,如下代码所示
from keras.models import load_model
load_model = load_model("./cifar9_resnet_ckpt.h5")
pred = load_model.predict(test_x)
模型预测的输出是one-hot形式的编码,对one-hot编码的形式进行整理,并将预测结果与对应图片名写入csv文件中,如下所示:
label = np.argmax(pred,axis=1)
data_row_img=[]
data_row_label=[]
path = "./data/test/"
for i in test_label:
for j in os.walk(path+i+"/"):
for k in j[2]:
data_row_img.append(k)
for j in label:
data_row_label.append(test_label[j])
pred_data = pd.DataFrame({"Img_file":data_row_img,"Label":data_row_label})
pred_data.to_csv("submission.csv",index=False)
参考:http://www.voidcn.com/article/p-cguqgdal-brn.html
https://arxiv.org/pdf/1512.03385.pdf
网友评论