基于PaddlePaddle框架实现桃子分类

众所周知,图像相比文字能够提供更加生动,容易理解及更具艺术感的信息,是人们转递与交换信息的重要来源。

谈到图像分类,图像分类是根据图像的语义信息对不同类别图像进行区分,是计算机视觉中重要的基础问题,也是图像检测、图像分割、物体跟踪、行为分析等其他高层视觉任务的基础,在许多领域都有着广泛的应用。如:安防领域的人脸识别和智能视频分析等,交通领域的交通场景识别,互联网领域基于内容的图像检索和相册自动归类,医学领域的图像识别等。在本项目里,你将了解使用深度学习进行图片分类的原理、图片数据的一些处理技巧、深度神经网络模型搭建以及训练过程等,除此之外,还会加深你对Paddlepaddle深度学习框架的了解。

1.背景知识

图像分类包括通用图像分类,细粒度图像分类等。

图1展示了通用图像分类效果,即模型可以正确识别图像上的主要物体

                                       图1 通用图像分类展示

图2展示了细粒度图像分类 - 花卉识别的效果,要求模型可以正确识别花的类别。

                                       图2 细粒度图像分类展示

一个好的模型既要对不同类别识别正确,同时也应该能够对不同视角,光照,背景,变形或部分遮挡的图像正确识别(这里我们统一称作图像扰动)。
图3展示了一些图像的扰动,较好的模型会像聪明的人类一样能够正确识别。

                                          图3 扰动图片展示
2.项目说明

在本项目中,我们实现的是基于PaddlePaddle框架利用深度神经网络进行桃子图片的分类。

本项目使用数据集下载地址为:https://pan.baidu.com/s/17i8yYDVNqDDrO6qc4jjaxQ

关于数据集的补充说明:下载下来的数据集data文件里包括train与test两个文件夹,需要进行以下操作:对于命名为train的文件夹 ,train下面有4个文件夹,将文件夹名分别
改为class0,class1,class2,class3。 class0下再建立一个文件夹命名为0,第一类训练图片放在这里,class1下再建立个命名为1的文件夹,class2下建个2,class3下建个3 。
test 文件夹 以此类推操作。然后就可以进行训练测试了。



                                    本实验使用的桃子数据集展示

3.项目实现过程:

3.1 导入相关库
1 import numpy as np
2 import sys
3 from PIL import Image
4 
5 import paddle.v2 as paddle

3.2 数据预处理以及reader的构造

数据集中6400 张大桃照片按照红、大、中、小等元素按照分档建立图片数据集合,实验思路是将图片数据集放入卷积神经网络(CNN)中进行训练,自动提取用于分级的影响要素并形成分类逻辑。

由于本项目的数据集是图片,我们首先来了解一下图片的CHW布局:

缩写:C =通道,H =高度,W =宽度

由cv2或PIL打开的图像的默认布局是HWC。

而PaddlePaddle仅支持CHW布局,而CHW只是HWC的转置,故我们在输入数据前要进行转置操作将HWC布局调整为CHW。

 1 def reader_creator(flag):
 2     def reader():
 3         cnt = 0
 4         if flag == 'train':
 5             path = './train'
 6         else:
 7             path = './test'
 8         for label_dir in os.listdir(path):
 9             if('0' in label_dir or '1' in label_dir or '2' in label_dir or '3' in label_dir):
10                 label = label_dir[-1:]
11                 for dir in os.listdir(path+'/'+label_dir):
12                     if('.' not in dir):
13                         for image_name in os.listdir(path+'/'+label_dir+'/'+dir):
14                             if('png' in image_name):
15                                 im = Image.open(path+'/'+label_dir+'/'+dir+'/'+image_name)
16                                 #if path == './test':
17                                 #    print path+'/'+label_dir+'/'+dir+'/'+image_name
18                                 #    print label
19                                 try:
20                                     pass
21                                     #im = im.resize((800, 600), Image.ANTIALIAS)
22                                     #im = im.resize((32, 32), Image.ANTIALIAS)
23                                 except:
24                                     print 'lose frame'
25                                     continue
26                                 # 由cv2或PIL打开的图像的默认布局是HWC。
27                                 # PaddlePaddle仅支持CHW布局。而CHW只是HWC的转置。
28                                 im = np.array(im).astype(np.float32)
29                                 im = im.transpose((2, 0, 1))  # 转置操作,以得到CHW布局
30                                 im = im.flatten()
31                                 im = im / 255.0
32                                 if im.shape[0] != 230400:
33                                     continue
34                                 cnt = cnt+1
35                                 yield im, int(label)
36         print cnt
37     return reader

构造train reader 与test reader:

1 def train():
2     return reader_creator('train');
3 def test():
4     return reader_creator('test');

3.3 搭建模型

在完成了数据预处理与Reader的构造等基础工程之后,我们就可以正式进入神经网络模型的搭建流程了。搭建模型的内容主要有以下几点:

  • 定义网络结构

  • 初始化PaddlePaddle

  • 配置网络结构和设置参数

    • 配置网络结构
    • 定义损失函数cost
    • 创建parameters
    • 定义优化器optimizer
 

(1)定义网络结构

两个隐藏层激活函数选用Relu激活函数,各层均使用全连接层。

1 # 定义多层感知机结构
2 def multilayer_perceptron(img):
3     hidden1 = paddle.layer.fc(input=img, size=128, act=paddle.activation.Relu())
4     hidden2 = paddle.layer.fc(input=hidden1, size=64, act=paddle.activation.Relu())
5     predict = paddle.layer.fc(input=hidden2, size=4, act=paddle.activation.Softmax())
6     return predict

(2)初始化PaddlePaddle

然后进行最基本的初始化操作,在PaddlePaddle中使用paddle.init(use_gpu=False, trainer_count=1)来进行初始化:

  • use_gpu=False表示不使用gpu进行训练
  • trainer_count=1表示仅使用一个训练器进行训练
1 # PaddlePaddle 初始化
2 paddle.init(use_gpu=False, trainer_count=1)

(3)配置网络结构及参数设置

 

定义参数及输入:

设置算法参数(如数据维度、类别数目和batch size等参数),所用数据集是桃子图片。桃子所分的种类是4,因此,classdim=4。

定义数据输入层image和类别标签lbl,本实验使用image=paddle.layer.data(name=”image”, type=paddle.data_type.dense_vector_sequence(data_dim))函数,名称为“image”,数据类型为data_dim维向量;

1 #数据格式
2 datadim = 3 * 320 *240
3 #图片类别数目
4 classdim = 4
5 # 描述神经网络的输入,定义数据输入层image
6 image = paddle.layer.data(name="image", height=320, width=240, type=paddle.data_type.dense_vector(datadim))
7 # 定义类别标签
8 lbl = paddle.layer.data(name="label", type=paddle.data_type.integer_value(classdim))

选用net模型,使用前面定义的multilayer_perceptron()

1 # 使用net模型为 multilayer_perceptron()
2 net = multilayer_perceptron(image)

获得网络最后的Softmax层

1 # 获得网络最后的Softmax层
2 out = paddle.layer.fc(input=net, size=classdim, act=paddle.activation.Softmax())

定义损失函数

在配置网络结构之后,我们需要定义一个损失函数来计算梯度并优化参数。

1 # 定义损失函数
2 cost = paddle.layer.classification_cost(input=out, label=lbl)

创建参数

PaddlePaddle中提供了接口paddle.parameters.create(cost)来创建和初始化参数,参数cost表示基于我们刚刚创建的cost损失函数来创建和初始化参数。

1 # 利用cost创建 parameters Create parameters
2 parameters = paddle.parameters.create(cost)

创建优化器

通过 learning_rate_decay_a (简写a) 、learning_rate_decay_b (简写b) 和 learning_rate_schedule 指定学习率调整策略,这里采用离散指数的方式调节学习率。

计算公式如下, n 代表已经处理过的累计总样本数,lr0 即为参数里设置的 learning_rate。

image.png

1 # 创建 optimizer Create optimizer
2 momentum_optimizer = paddle.optimizer.Momentum(
3     momentum=0.9,
4         regularization=paddle.optimizer.L2Regularization(rate=0.0002 * 128),
5         learning_rate=0.01 / 128.0,
6         learning_rate_decay_a=0.1,
7         learning_rate_decay_b=50000 * 100,
8         learning_rate_schedule='discexp')

定义事件处理程序

定义事件处理函数可以帮助我们在训练时实时了解训练进度

 1 #定义事件处理函数
 2 def event_handler(event):
 3     if isinstance(event, paddle.event.EndIteration):
 4         # 每隔5个batch 打印一次进度
 5         if event.batch_id % 5 == 0:
 6             print "\nPass %d, Batch %d, Cost %f, %s" % (
 7                 event.pass_id, event.batch_id, event.cost, event.metrics)
 8         else:
 9             sys.stdout.write('.')
10             sys.stdout.flush()
11     if isinstance(event, paddle.event.EndPass):
12         # 每隔2个pass 保存一次模型参数parameters
13         if event.pass_id %2 == 0:
14             with open('params_pass_%d.tar' % event.pass_id, 'w') as f:
15                 parameters.to_tar(f)
16 
17         result = trainer.test(
18             reader=paddle.batch(
19                 paddle.reader.shuffle(
20                 test(), buf_size=50000), batch_size=128),
21             feeding={'image': 0,
22                      'label': 1})
23         print "\nTest with Pass %d, %s" % (event.pass_id, result.metrics)

3.4 训练模型

完成了模型搭建的过程,接下来我们就可以开始模型的训练过程了,首先,我们需要用 paddle.trainer.SGD() 函数定义一个随机梯度下降trainer,配置三个参数cost、parameters、update_equation,它们分别表示损失函数、参数和更新公式。

1 # 创建训练器
2 trainer = paddle.trainer.SGD(
3         cost=cost, parameters=parameters, update_equation=momentum_optimizer)

训练器创建完成后就可以正式开始训练了,训练参数设置如下:

  • paddle.reader.shuffle(train(),buf_size=20000)表示trainer从train()这个reader中读取了buf_size=20000大小的数据并打乱顺序
  • paddle.batch(reader(), batch_size=128)表示从打乱的数据中再取出batch_size=128大小的数据进行一次迭代训练
  • 参数feeding是feeding索引,其将数据层image和label输入trainer,也就是训练数据的来源。
  • 参数event_handler是事件管理机制,读者可以自定义event_handler,根据事件信息作相应的操作。
  • 参数num_passes=20 表示迭代训练20次后停止训练。

当然,这些参数可以根据需求自行设置,读者可以通过修改参数来观察其对训练结果的影响。

 1 # 开始训练
 2 trainer.train(
 3     reader=paddle.batch(
 4         paddle.reader.shuffle(
 5             train(), buf_size=20000),
 6         batch_size=128),
 7     num_passes=20,
 8     event_handler=event_handler,
 9     feeding={'image': 0,
10              'label': 1})

3.5 加载模型并进行预测

模型训练完成以后,我们就可以加载训练好的来进行分类预测并观察预测效果了,首先我们需要加载训练好的模型:

1 # 加载训练的模型文件 params_pass_18.tar,可视训练效果调整所要加载的模型文件
2 with open('params_pass_18.tar', 'r') as f:
3         parameters = paddle.parameters.Parameters.from_tar(f)

加载了训练模型以后,我们同样需要对测试集数据进行处理,使之成为预测模型可以接受的输入格式,因此,测试集中的数据同样要进行转置操作,以达到PaddlePaddle输入图片CHW布局要求。

然后使用预测函数 paddle.infer(output_layer = out,parameters = parameters ,input = test_data) 进行预测,其中参数output_layer 表示输出层,参数parameters表示模型参数,参数input表示输入的测试数据。

 1 for i in range(0,10000):
 2     i = str(i)
 3     # 选择测试集所在路径,这里选择的是类别为3的测试集,可以调整为其他类别的继续测试
 4     file = './test/class3/3/'+i+'.png'
 5     try:
 6         im = Image.open(file)
 7     except:
 8         continue
 9     im = np.array(im).astype(np.float32)
10     im = im.transpose((2, 0, 1))  # CHW
11     im = im.flatten()
12     im = im / 255.0
13     if im.shape[0] != 230400:
14         continue
15     # 先建立图像列表文件       
16     test_data = []
17     test_data.append((im, ))
18 
19 
20     probs = paddle.infer( output_layer=out, parameters=parameters, input=test_data)
21     lab = np.argsort(-probs)  # probs and lab are the results of one batch data
22     # np.argsort(x) 返回的是数组值从小到大的索引值 np.argosrt(-x)返回的是数组值从大到小的索引值
23     # 输出预测结果
24     print "Label of image/%s.png is: %d" % (i,lab[0][0])
25     print probs[0][lab[0][0]]

 

4.参考文献
1.https://github.com/PaddlePaddle/book/tree/develop/03.image_classification
2.https://github.com/sorting4peach/sorting4peach

posted @ 2018-05-26 23:04  youngawesome  阅读(6536)  评论(7编辑  收藏  举报