[深度应用]·实战掌握Keras图片分类简明教程

本文将通过一个实战方式,让大家能掌握Keras图片分类更轻松遨游在竞赛的海洋里。将以一个初学者的角度一步步教大家如何去完成一个类似分类的比赛,内容包括数据读取,模型搭建,网络训练,预测提交。让大家通过这个实战演练来掌握Keras图片分类。

小宋是呢

[深度应用]·实战掌握Keras图片分类简明教程


1.引文

深度学习的比赛中,图片分类是很常见的比赛,同时也是很难取得特别高名次的比赛,因为图片分类已经被大家研究的很透彻,一些开源的网络很容易取得高分。如果大家还掌握不了使用开源的网络进行训练,再慢慢去模型调优,很难取得较好的成绩。

本文将通过一个实战方式,让大家能掌握Keras图片分类更轻松遨游在竞赛的海洋里。将以一个初学者的角度一步步教大家如何去完成一个类似分类的比赛,内容包括数据读取,模型搭建,网络训练,预测提交。让大家通过这个实战演练来掌握Keras图片分类。

2.数据介绍

数据 下载地址

这次的实战使用的数据是交通标志数据集,共有62类交通标志。其中训练集数据有4572张照片(每个类别大概七十个),测试数据集有2520张照片(每个类别大概40个)。数据包含两个子目录分别train与test:

为什么还需要测试数据集呢?这个测试数据集不会拿来训练,是用来进行模型的评估与调优。

1.png

train与test每个文件夹里又有62个子文件夹,每个类别在同一个文件夹内:

2.png

我从中打开一个文件间,把里面图片展示出来:

3.png

其中每张照片都类似下面的例子,100*100*3的大小。100是照片的照片的长和宽,3是什么呢?这其实是照片的色彩通道数目,RGB。彩色照片存储在计算机里就是以三维数组的形式。我们送入网络的也是这些数组。

   

但是还有一个问题,就是我们可以观察到,其实每张照片的大小并不一样,那送入网络时应该如何处理呢?其实python的一下包可以解决这些问题,可以使用OpenCv或者pillow把读取的照片重新设置一下大小。在这个问题上,此博文把照片的尺寸同一甚至为了224*224,这是很多主流网络模型的输入尺寸。其实这种同一尺寸的方法在文本数据上也有使用,例如一句话有的比较长,而有的比较短,一般也会使用截取和填充的方式同一长度,便于网络输入。

分析这个数据的任务,就是对图片信息进行分类操作。其实很像MNIST数据集,只是MNIST数据集是10分类问题,而且数据读取处理一般都做好了,而我们这个任务就需要自己去读取和组织数据。这样也更加贴近实际的应用,通过这个实战可以让大家学到很多实用的东西。

3.网络构建

我先把代码和训练结果贴在这里,内容后面再慢慢补充。。

from keras.applications import NASNetMobile,ResNet50,DenseNet121,InceptionResNetV2,MobileNetV2
from keras.layers import GlobalAveragePooling2D,GlobalMaxPooling2D,Dense,Flatten,Input,Concatenate,Dropout
from keras.losses import mae, sparse_categorical_crossentropy, binary_crossentropy,categorical_crossentropy
from keras.optimizers import Adam
import keras.backend as K
from keras.models import load_model,Model
import keras
import glob
import pandas as pd
import numpy as np
import math
import cv2


from imgaug import augmenters as iaa

sometimes = lambda aug: iaa.Sometimes(0.5, aug) #建立lambda表达式,
#imgaug test
seq1 = iaa.Sequential([
iaa.Fliplr(0.5), # 水平翻转图像
iaa.Flipud(0.5), # 竖直翻转
iaa.Crop(px=(0, 16)),
#iaa.Affine(rotate=(-90, 90)),  # rotate by -45 to 45 degrees (affects heatmaps)
sometimes(iaa.GaussianBlur(sigma=(0, 1.0))), # 使用0到1.0的sigma模糊图像
sometimes(iaa.Affine(                          #对一部分图像做仿射变换
scale={"x": (0.9, 1.1), "y": (0.9, 1.1)},#图像缩放为80%到120%之间
translate_percent={"x": (-0.2, 0.2), "y": (-0.2, 0.2)}, #平移±20%之间
rotate=(-90, 90),   #旋转±45度之间
shear=(-16, 16),    #剪切变换±16度,(矩形变平行四边形)
order=[0, 1],   #使用最邻近差值或者双线性差值
#cval=(0, 255),  #全白全黑填充
#mode=iaa.ALL    #定义填充图像外区域的方法
),),
],
random_order=True # 随机的顺序把这些操作用在图像上
)


class DefaultConfigs(object):
#1. string configs
train_data = "./traffic-sign/train/"
test_data = "./traffic-sign/test/"  # if exists else use train_test_split to generate val dataset

#2. numerical configs
lr = 0.001
epochs = 10
init_epoch = 0
num_classes = 62
image_size = 224
batch_size = 8
channels = 3
train_len = 4572
test_len = 2520

config = DefaultConfigs()



def create_csv(PATH_DIR,save_path):
paths = np.array(glob.glob(PATH_DIR+"*/*"))
print(paths[:5])
labels = np.array([int(path.split("\\")[1].split(r".")[0]) for path in paths])
df = pd.DataFrame()
df["x_path"] = paths
df["label"] = labels
df = df.sample(frac=1)
print(df.head(5))
print(len(df))
df.to_csv(save_path,index=None)
print("Finish save csv")

def convert2oneHot(index,Lens):
hot = np.zeros((Lens,))
hot[index] = 1
return(hot)

def get_feature(path):
image = cv2.imread(path)
feature = cv2.resize(image,(config.image_size,config.image_size),interpolation=cv2.INTER_NEAREST)
return(feature)

def get_label(path):
label = int(path)
one_hot_label = convert2oneHot(label,Lens=config.num_classes)
return(one_hot_label)

def xs_gen(path,batch_size,transform=True):
img_list = pd.read_csv(path)
img_list = np.array(img_list)
print("Found %s train items."%len(img_list))
print("list 1 is",img_list[0])
num_batch = math.ceil(len(img_list) / batch_size)    # 确定每轮有多少个batch
while True:
np.random.shuffle(img_list)
for i in range(num_batch):
batch_list = img_list[i * batch_size : i * batch_size + batch_size]
np.random.shuffle(batch_list)
batch_x = np.array([get_feature(x) for x in batch_list[:,0]])
batch_y = np.array([get_label(y) for y in batch_list[:,1]])
if(transform):                                        #数据增强
batch_x = seq1.augment_images(batch_x)
yield batch_x, batch_y

def get_model():
inputs = Input((config.image_size, config.image_size, config.channels))

base_model = NASNetMobile(include_top=False, input_shape=(config.image_size, config.image_size, config.channels))#, weights=None
x = base_model(inputs)

out1 = GlobalMaxPooling2D()(x)  # GMP feature
out2 = GlobalAveragePooling2D()(x) # GAP feature
out3 = Flatten()(x)               # Flatten feature

out = Concatenate(axis=-1)([out1, out2, out3])  #concate all feature
out = Dropout(0.3)(out2) # GAP feature
out = Dense(config.num_classes, activation="softmax", name="classifier")(out)
model = Model(inputs, out)

#for layer in base_model.layers:
#    layer.trainable = False

return(model)

def train(num_epoch):
model = get_model()

print(model.summary())
model.load_weights("model.10-0.2280-0.942.h5")
ckpt = keras.callbacks.ModelCheckpoint(
filepath='model.{epoch:02d}-{val_loss:.4f}-{val_acc:.3f}.h5',
monitor='val_acc', save_best_only=True,verbose=1)
opt = Adam(config.lr,decay=0.05)
model.compile(loss='categorical_crossentropy',
optimizer=opt, metrics=['accuracy'])
train_iter = xs_gen("./traffic-sign/train_datas.csv",config.batch_size)
test_iter = xs_gen("./traffic-sign/test_datas.csv",config.batch_size,train_iter=False)

model.fit_generator(
generator=train_iter,
steps_per_epoch=math.ceil(config.train_len/config.batch_size),
epochs=num_epoch,
initial_epoch=config.init_epoch,
validation_data = test_iter,
validation_steps = math.ceil(config.test_len/config.batch_size),
callbacks=[ckpt],
)
def test():
model = get_model()
print(model.summary())
opt = Adam(0.0001)
model.compile(loss='categorical_crossentropy',
optimizer=opt, metrics=['accuracy'])
train_iter = xs_gen("./traffic-sign/train_datas.csv",config.batch_size)
test_iter = xs_gen("./traffic-sign/test_datas.csv",config.batch_size,train=False)
eval1 = model.evaluate_generator(train_iter,steps=config.train_len//config.batch_size,verbose=1)
eval2 = model.evaluate_generator(test_iter,steps=config.train_len//config.batch_size,verbose=1)
print(eval1,eval2)
if __name__ == "__main__":

create_csv(PATH_DIR=config.train_data,save_path="./traffic-sign/train_datas.csv")
create_csv(PATH_DIR=config.test_data,save_path="./traffic-sign/test_datas.csv")
train(num_epoch=config.epochs)
#test()

训练结果:

(TF_GPU) D:\Files\DATAs\prjs\python\tf_keras\traffic_classifier>C:/Files/APPs/RuanJian/Miniconda3/envs/TF_GPU/python.exe d:/Files/DATAs/prjs/python/tf_keras/traffic_classifier/main.py
Using TensorFlow backend.
['./traffic-sign/train\\00000\\01153_00000.png'
 './traffic-sign/train\\00000\\01153_00001.png'
 './traffic-sign/train\\00000\\01153_00002.png'
 './traffic-sign/train\\00000\\01160_00000.png'
 './traffic-sign/train\\00000\\01160_00001.png']
                                          x_path  label
799   ./traffic-sign/train\00019\00459_00002.png     19
2129  ./traffic-sign/train\00032\01852_00000.png     32
79    ./traffic-sign/train\00001\00965_00002.png      1
3436  ./traffic-sign/train\00045\01679_00000.png     45
1185  ./traffic-sign/train\00022\00946_00001.png     22
4572
Finish save csv
['./traffic-sign/test\\00000\\00017_00000.png'
 './traffic-sign/test\\00000\\00017_00001.png'
 './traffic-sign/test\\00000\\00017_00002.png'
 './traffic-sign/test\\00000\\00021_00000.png'
 './traffic-sign/test\\00000\\00021_00001.png']
                                         x_path  label
119   ./traffic-sign/test\00007\02226_00001.png      7
1987  ./traffic-sign/test\00039\02019_00001.png     39
2354  ./traffic-sign/test\00057\02087_00002.png     57
1978  ./traffic-sign/test\00039\00713_00001.png     39
250   ./traffic-sign/test\00014\00396_00002.png     14
2520
Finish save csv
2019-04-30 18:44:23.514847: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX AVX2
2019-04-30 18:44:24.245533: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1432] Found device 0 with properties:
name: GeForce MX150 major: 6 minor: 1 memoryClockRate(GHz): 1.341
pciBusID: 0000:01:00.0
totalMemory: 2.00GiB freeMemory: 1.62GiB
2019-04-30 18:44:24.252968: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1511] Adding visible gpu devices: 0
2019-04-30 18:44:24.768883: I tensorflow/core/common_runtime/gpu/gpu_device.cc:982] Device interconnect StreamExecutor with strength 1 edge matrix:
2019-04-30 18:44:24.772563: I tensorflow/core/common_runtime/gpu/gpu_device.cc:988]      0
2019-04-30 18:44:24.773999: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1001] 0:   N
2019-04-30 18:44:24.775858: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1115] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 1371 MB memory) -> physical GPU (device: 0, name: GeForce MX150, pci bus id: 0000:01:00.0, compute capability: 6.1)
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
input_1 (InputLayer)         (None, 224, 224, 3)       0
_________________________________________________________________
NASNet (Model)               (None, 7, 7, 1056)        4269716
_________________________________________________________________
global_average_pooling2d_1 ( (None, 1056)              0
_________________________________________________________________
dropout_1 (Dropout)          (None, 1056)              0
_________________________________________________________________
classifier (Dense)           (None, 62)                65534
=================================================================
Total params: 4,335,250
Trainable params: 4,298,512
Non-trainable params: 36,738
_________________________________________________________________
None
Epoch 1/10
Found 2520 train items.
list 1 is ['./traffic-sign/test\\00007\\02226_00001.png' 7]
Found 4572 train items.
list 1 is ['./traffic-sign/train\\00019\\00459_00002.png' 19]
572/572 [==============================] - 484s 846ms/step - loss: 1.4713 - acc: 0.6674 - val_loss: 0.5271 - val_acc: 0.8810

Epoch 00001: val_acc improved from -inf to 0.88095, saving model to model.01-0.5271-0.881.h5
Epoch 2/10
572/572 [==============================] - 426s 744ms/step - loss: 0.7001 - acc: 0.8357 - val_loss: 0.4031 - val_acc: 0.8984

Epoch 00002: val_acc improved from 0.88095 to 0.89841, saving model to model.02-0.4031-0.898.h5
Epoch 3/10
572/572 [==============================] - 426s 744ms/step - loss: 0.5505 - acc: 0.8630 - val_loss: 0.3474 - val_acc: 0.9087

Epoch 00003: val_acc improved from 0.89841 to 0.90873, saving model to model.03-0.3474-0.909.h5
Epoch 4/10
572/572 [==============================] - 426s 744ms/step - loss: 0.4727 - acc: 0.8789 - val_loss: 0.3074 - val_acc: 0.9198

Epoch 00004: val_acc improved from 0.90873 to 0.91984, saving model to model.04-0.3074-0.920.h5
Epoch 5/10
572/572 [==============================] - 426s 745ms/step - loss: 0.4359 - acc: 0.8835 - val_loss: 0.2808 - val_acc: 0.9262

Epoch 00005: val_acc improved from 0.91984 to 0.92619, saving model to model.05-0.2808-0.926.h5
Epoch 6/10
572/572 [==============================] - 427s 747ms/step - loss: 0.3954 - acc: 0.8975 - val_loss: 0.2632 - val_acc: 0.9298

Epoch 00006: val_acc improved from 0.92619 to 0.92976, saving model to model.06-0.2632-0.930.h5
Epoch 7/10
572/572 [==============================] - 427s 746ms/step - loss: 0.3599 - acc: 0.9041 - val_loss: 0.2529 - val_acc: 0.9333

Epoch 00007: val_acc improved from 0.92976 to 0.93333, saving model to model.07-0.2529-0.933.h5
Epoch 8/10
572/572 [==============================] - 427s 747ms/step - loss: 0.3472 - acc: 0.9108 - val_loss: 0.2464 - val_acc: 0.9377

Epoch 00008: val_acc improved from 0.93333 to 0.93770, saving model to model.08-0.2464-0.938.h5
Epoch 9/10
572/572 [==============================] - 427s 746ms/step - loss: 0.3213 - acc: 0.9146 - val_loss: 0.2461 - val_acc: 0.9373

Epoch 00009: val_acc did not improve from 0.93770
Epoch 10/10
572/572 [==============================] - 427s 747ms/step - loss: 0.3073 - acc: 0.9150 - val_loss: 0.2280 - val_acc: 0.9425

Epoch 00010: val_acc improved from 0.93770 to 0.94246, saving model to model.10-0.2280-0.942.h5


评论列表

暂无评论

新的评论

上一篇:
下一篇: