基於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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM