动手学PaddlePaddle(3):猫脸识别
你將學(xué)會:
-
預(yù)處理圖片數(shù)據(jù)
-
利用PaddlePaddle框架實現(xiàn)Logistic回歸模型:
在開始練習(xí)之前,簡單介紹一下圖片處理的相關(guān)知識:
圖片處理
由于識別貓問題涉及到圖片處理知識,這里對計算機如何保存圖片做一個簡單的介紹。在計算機中,圖片被存儲為三個獨立的矩陣,分別對應(yīng)圖中的紅、綠、藍三個顏色通道,如果圖片是64*64像素的,就會有三個64*64大小的矩陣,要把這些像素值放進一個特征向量中,需要定義一個特征向量X,將三個顏色通道中的所有像素值都列出來。如果圖片是64*64大小的,那么特征向量X的長度就是64*64*3,也就是12288。這樣一個長度為12288的向量就是Logistic回歸模型的一個訓(xùn)練數(shù)據(jù)。
目錄
1 - 引用庫
2 - 數(shù)據(jù)預(yù)處理
3 - 構(gòu)造reader
4 - 訓(xùn)練過程
4.1 獲取訓(xùn)練數(shù)據(jù)
4.2配置網(wǎng)絡(luò)結(jié)構(gòu)和損失函數(shù)
4.3 設(shè)置優(yōu)化方法
4.4 定義訓(xùn)練場所
4.5設(shè)置訓(xùn)練主循環(huán)
5 - 預(yù)測階段
6 - 總結(jié)
1 - 引用庫
import sys import numpy as npimport lr_utils import matplotlib.pyplot as pltimport paddle import paddle.fluid as fluidfrom paddle.utils.plot import Ploter %matplotlib inline2 - 數(shù)據(jù)預(yù)處理
這里簡單介紹數(shù)據(jù)集及其結(jié)構(gòu)。數(shù)據(jù)集以hdf5文件的形式存儲,包含了如下內(nèi)容:
- 訓(xùn)練數(shù)據(jù)集:包含了m_train個圖片的數(shù)據(jù)集,數(shù)據(jù)的標(biāo)簽(Label)分為cat(y=1)和non-cat(y=0)兩類。
- 測試數(shù)據(jù)集:包含了m_test個圖片的數(shù)據(jù)集,數(shù)據(jù)的標(biāo)簽(Label)同上。
單個圖片數(shù)據(jù)的存儲形式為(num_x, num_x, 3),其中num_x表示圖片的長或?qū)?#xff08;數(shù)據(jù)集圖片的長和寬相同),數(shù)字3表示圖片為三通道(RGB)。
需要注意的是,為了方便,添加“_orig”后綴表示該數(shù)據(jù)為原始數(shù)據(jù),之后需要對數(shù)據(jù)做進一步處理。未添加“_orig”的數(shù)據(jù)則表示之后不需要再對該數(shù)據(jù)作進一步處理。
# 調(diào)用load_dataset()函數(shù),讀取數(shù)據(jù)集 train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = lr_utils.load_dataset()# 圖片示例 # 可觀察到索引為“23”的圖片應(yīng)為“non-cat” index = 19 plt.imshow(train_set_x_orig[index]) print ("y = " + str(train_set_y[:, index]) + ", it's a '" + classes[np.squeeze(train_set_y[:, index])].decode("utf-8") + "' picture.")獲取數(shù)據(jù)后的下一步工作是獲得數(shù)據(jù)的相關(guān)信息,如訓(xùn)練樣本個數(shù) m_train、測試樣本個數(shù) m_test 和圖片的長度或?qū)挾?num_x,使用 numpy.array.shape 來獲取數(shù)據(jù)的相關(guān)信息
m_train = train_set_x_orig.shape[0] m_test = test_set_x_orig.shape[0] num_px = train_set_x_orig.shape[1]print ("訓(xùn)練樣本數(shù): m_train = " + str(m_train)) print ("測試樣本數(shù): m_test = " + str(m_test)) print ("圖片高度/寬度: num_px = " + str(num_px)) print ("圖片大小: (" + str(num_px) + ", " + str(num_px) + ", 3)") print ("train_set_x shape: " + str(train_set_x_orig.shape)) print ("train_set_y shape: " + str(train_set_y.shape)) print ("test_set_x shape: " + str(test_set_x_orig.shape)) print ("test_set_y shape: " + str(test_set_y.shape)) 訓(xùn)練樣本數(shù): m_train = 209 測試樣本數(shù): m_test = 50 圖片高度/寬度: num_px = 64 圖片大小: (64, 64, 3) train_set_x shape: (209, 64, 64, 3) train_set_y shape: (1, 209) test_set_x shape: (50, 64, 64, 3) test_set_y shape: (1, 50)接下來需要對數(shù)據(jù)作進一步處理,為了便于訓(xùn)練,可以先忽略圖片的結(jié)構(gòu)信息,將包含圖像長、寬和通道數(shù)信息的三維數(shù)組壓縮成一維數(shù)組,圖片數(shù)據(jù)的形狀將由(64, 64, 3)轉(zhuǎn)化為(64 * 64 * 3, 1)。
# 定義維度 DATA_DIM = num_px * num_px * 3# 轉(zhuǎn)換數(shù)據(jù)形狀train_set_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1) test_set_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1)print ("train_set_x_flatten shape: " + str(train_set_x_flatten.shape)) print ("train_set_y shape: " + str(train_set_y.shape)) print ("test_set_x_flatten shape: " + str(test_set_x_flatten.shape)) print ("test_set_y shape: " + str(test_set_y.shape))?
在開始訓(xùn)練之前,還需要對數(shù)據(jù)進行歸一化處理。圖片采用紅、綠、藍三通道的方式來表示顏色,每個通道的單個像素點都存儲著一個 0-255 的像素值,所以圖片的歸一化處理十分簡單,只需要將數(shù)據(jù)集中的每個像素值除以 255 即可,但需要注意的是計算結(jié)果應(yīng)為 float 類型。
train_set_x = train_set_x_flatten.astype('float32') / 255 test_set_x = test_set_x_flatten.astype('float32') / 255為了方便后續(xù)的測試工作,添加了合并數(shù)據(jù)集和標(biāo)簽集的操作,使用 numpy.hstack 實現(xiàn) numpy 數(shù)組的橫向合并。
train_set = np.hstack((train_set_x, train_set_y.T)) test_set = np.hstack((test_set_x, test_set_y.T))經(jīng)過上面的實驗,需要記住:
對數(shù)據(jù)進行預(yù)處理的一般步驟是:
- 了解數(shù)據(jù)的維度和形狀等信息,例如(m_train, m_test, num_px, ...)
- 降低數(shù)據(jù)緯度,例如將數(shù)據(jù)維度(num_px, num_px, 3)轉(zhuǎn)化為(num_px * num_px * 3, 1)
- 數(shù)據(jù)歸一化
3 - 構(gòu)造reader
構(gòu)造read_data()函數(shù),來讀取訓(xùn)練數(shù)據(jù)集train_set或者測試數(shù)據(jù)集test_set。它的具體實現(xiàn)是在read_data()函數(shù)內(nèi)部構(gòu)造一個reader(),使用yield關(guān)鍵字來讓reader()成為一個Generator(生成器),注意,yield關(guān)鍵字的作用和使用方法類似return關(guān)鍵字,不同之處在于yield關(guān)鍵字可以構(gòu)造生成器(Generator)。雖然我們可以直接創(chuàng)建一個包含所有數(shù)據(jù)的列表,但是由于內(nèi)存限制,我們不可能創(chuàng)建一個無限大的或者巨大的列表,并且很多時候在創(chuàng)建了一個百萬數(shù)量級別的列表之后,我們卻只需要用到開頭的幾個或幾十個數(shù)據(jù),這樣造成了極大的浪費,而生成器的工作方式是在每次循環(huán)時計算下一個值,不斷推算出后續(xù)的元素,不會創(chuàng)建完整的數(shù)據(jù)集列表,從而節(jié)約了內(nèi)存使用。
# 讀取訓(xùn)練數(shù)據(jù)或測試數(shù)據(jù) def read_data(data_set):"""一個readerArgs:data_set -- 要獲取的數(shù)據(jù)集Return:reader -- 用于獲取訓(xùn)練數(shù)據(jù)集及其標(biāo)簽的生成器generator"""def reader():"""一個readerArgs:Return:data[:-1], data[-1:] -- 使用yield返回生成器(generator),data[:-1]表示前n-1個元素組成的list,也就是訓(xùn)練數(shù)據(jù),data[-1]表示最后一個元素,也就是對應(yīng)的標(biāo)簽"""for data in data_set:### START CODE HERE ### (≈ 1 lines of code)yield data[:-1], data[-1]### END CODE HERE ###return reader test_array = [[1,1,1,1,0],[2,2,2,2,1],[3,3,3,3,0]]print("test_array for read_data:") for value in read_data(test_array)():print(value)test_array for read_data: ([1, 1, 1, 1], 0) ([2, 2, 2, 2], 1) ([3, 3, 3, 3], 0)4 - 訓(xùn)練過程
4.1 獲取訓(xùn)練數(shù)據(jù)
關(guān)于參數(shù)的解釋如下:
paddle.reader.shuffle(read_data(data_set), buf_size=BUF_SIZE) 表示從read_data(data_set)這個reader中讀取了BUF_SIZE大小的數(shù)據(jù)并打亂順序paddle.batch(reader(), batch_size=BATCH_SIZE) 表示從reader()中取出BATCH_SIZE大小的數(shù)據(jù)進行一次迭代訓(xùn)練BATCH_SIZE= 32# 設(shè)置訓(xùn)練reader train_reader = paddle.batch(paddle.reader.shuffle(read_data(train_set), buf_size=500),batch_size=BATCH_SIZE)#設(shè)置測試 reader test_reader = paddle.batch(paddle.reader.shuffle(read_data(test_set), buf_size=500),batch_size=BATCH_SIZE)4.2配置網(wǎng)絡(luò)結(jié)構(gòu)和損失函數(shù)
Logistic 回歸模型結(jié)構(gòu)相當(dāng)于一個只含一個神經(jīng)元的神經(jīng)網(wǎng)絡(luò),如下圖所示,只包含輸入數(shù)據(jù)以及輸出層,不存在隱藏層,所以只需配置輸入層(input)、輸出層(predict)和標(biāo)簽層(label)即可。
#配置輸入層、輸出層、標(biāo)簽層 x = fluid.layers.data(name='x', shape=[DATA_DIM], dtype='float32') y_predict = fluid.layers.fc(input=x, size=2, act='softmax') y = fluid.layers.data(name='y', shape=[1], dtype='int64') #定義損失函數(shù) cost = fluid.layers.cross_entropy(input=y_predict, label=y) avg_loss = fluid.layers.mean(cost) acc = fluid.layers.accuracy(input=y_predict, label=y)接下來,我們寫三個語句分別來獲取:
- ①全局主程序main program。該主程序用于訓(xùn)練模型。
- ②全局啟動程序startup_program。該程序用于初始化。
- ③測試程序test_program。用于模型測試。
4.3 設(shè)置優(yōu)化方法
這里選擇Adam優(yōu)化算法:
optimizer = fluid.optimizer.Adam(learning_rate=0.001) optimizer.minimize(avg_loss) print("optimizer is ready")4.4 定義訓(xùn)練場所
# 使用CPU訓(xùn)練 use_cuda = False place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()#創(chuàng)建執(zhí)行器和保存路徑 exe = fluid.Executor(place) save_dirname="recognize_cat_inference.model"train_prompt = "Train cost" cost_ploter = Ploter(train_prompt)# 將訓(xùn)練過程繪圖表示 def event_handler_plot(ploter_title, step, cost):cost_ploter.append(ploter_title, step, cost)cost_ploter.plot()還可以定義一個train_test()函數(shù),作用是:使用測試集數(shù)據(jù),來測試訓(xùn)練效果。
def train_test(train_test_program, train_test_feed, train_test_reader):# 將分類準確率存儲在acc_set中acc_set = []# 將平均損失存儲在avg_loss_set中avg_loss_set = []# 將測試 reader yield 出的每一個數(shù)據(jù)傳入網(wǎng)絡(luò)中進行訓(xùn)練for test_data in train_test_reader():acc_np, avg_loss_np = exe.run(program=train_test_program,feed=train_test_feed.feed(test_data),fetch_list=[acc, avg_loss])acc_set.append(float(acc_np))avg_loss_set.append(float(avg_loss_np))# get test acc and loss# 獲得測試數(shù)據(jù)上的準確率和損失值acc_val_mean = np.array(acc_set).mean()avg_loss_val_mean = np.array(avg_loss_set).mean()# 返回平均損失值,平均準確率return avg_loss_val_mean, acc_val_meanfeeder = fluid.DataFeeder(place=place, feed_list=[x, y]) exe.run(startup_program)4.5設(shè)置訓(xùn)練主循環(huán)
構(gòu)建一個循環(huán)來進行訓(xùn)練,直至訓(xùn)練完成,把模型保存到 params_dirname 。
exe.run(fluid.default_startup_program())PASS_NUM = 150 epochs = [epoch_id for epoch_id in range(PASS_NUM)]lists = []step = 0 for epoch_id in epochs:for step_id, data in enumerate(train_reader()):metrics = exe.run(main_program,feed=feeder.feed(data),fetch_list=[avg_loss,acc])#我們可以把訓(xùn)練結(jié)果打印輸出,也可以用畫圖展示出來if step % 10 == 0: #每訓(xùn)練10次,繪制一次曲線event_handler_plot(train_prompt, step, metrics[0])step += 1# 測試每個epoch的分類效果avg_loss_val, acc_val = train_test(train_test_program=test_program,train_test_reader=test_reader,train_test_feed=feeder)print("Test with Epoch %d, avg_cost: %s, acc: %s" %(epoch_id, avg_loss_val, acc_val))lists.append((epoch_id, avg_loss_val, acc_val))# 保存訓(xùn)練好的模型參數(shù)用于預(yù)測 if save_dirname is not None:fluid.io.save_inference_model(save_dirname, ["x"], [y_predict], exe)# 選擇效果最好的pass best = sorted(lists, key=lambda list: float(list[1]))[0] print('Best pass is %s, testing Avgcost is %s' % (best[0], best[1])) print('The classification accuracy is %.2f%%' % (float(best[2]) * 100))5 - 預(yù)測階段
#指定預(yù)測的作用域 inference_scope = fluid.core.Scope() with fluid.scope_guard(inference_scope):# 使用 fluid.io.load_inference_model 獲取 inference program,# feed_target_names 用于指定需要傳入網(wǎng)絡(luò)的變量名# fetch_targets 指定希望從網(wǎng)絡(luò)中fetch出的變量名[inference_program, feed_target_names,fetch_targets] = fluid.io.load_inference_model(save_dirname, exe)# 將feed構(gòu)建成字典 {feed_target_name: feed_target_data}# 結(jié)果將包含一個與fetch_targets對應(yīng)的數(shù)據(jù)列表# 我們可以實現(xiàn)批量預(yù)測,通過一個循環(huán),每次預(yù)測一個mini_batchfor mini_batch in test_reader():test_x = np.array([data[0] for data in mini_batch]).astype("float32")test_y = np.array([data[1] for data in mini_batch]).astype("int64")# 真實進行預(yù)測mini_batch_result = exe.run(inference_program,feed={feed_target_names[0]: test_x},fetch_list=fetch_targets)# 打印預(yù)測結(jié)果mini_batch_result = np.argsort(mini_batch_result) #找出可能性最大的列標(biāo),升序排列mini_batch_result = mini_batch_result[0][:, -1] #把這些列標(biāo)拿出來print('預(yù)測結(jié)果:%s'%mini_batch_result)# 打印真實結(jié)果 label = test_y # 轉(zhuǎn)化為 labelprint('真實結(jié)果:%s'%label)#計數(shù)label_len = len(label)right_counter = 0for i in range(label_len):if mini_batch_result[i] == label[i]:right_counter += 1ratio = (right_counter/label_len)print("準確率為:%.2f%%"%(ratio*100)) 預(yù)測結(jié)果:[1 0 1 0 0 1 0 0 1 0 0 0 0 1 0 0 1 1 1 1 0 0 1 1 0 1 0 1 1 1 1 0] 真實結(jié)果:[0 1 1 1 0 1 1 0 0 0 0 1 0 1 0 1 1 1 1 0 1 0 1 1 1 0 1 1 1 1 1 0] 預(yù)測結(jié)果:[0 0 0 1 1 1 1 0 1 1 1 1 0 1 1 0 1 1] 真實結(jié)果:[1 1 0 1 1 0 1 0 1 1 1 1 0 1 1 0 1 1] 準確率為:83.33%6 - 總結(jié)
通過這個練習(xí)應(yīng)該記住:
至此Logistic回歸模型的訓(xùn)練工作完成,在使用PaddlePaddle進行模型配置和訓(xùn)練的過程中不用考慮參數(shù)的初始化、成本函數(shù)、激活函數(shù)、梯度下降、參數(shù)更新和預(yù)測等具體細節(jié);只需要簡單地配置網(wǎng)絡(luò)結(jié)構(gòu)和trainer即可,并且PaddlePaddle提供了許多接口來改變學(xué)習(xí)率、成本函數(shù)、批次大小等許多參數(shù)來改變模型的學(xué)習(xí)效果,使用起來更加靈活,方便測試,在之后的練習(xí)中,會對PaddlePaddle更加熟悉。
總結(jié)
以上是生活随笔為你收集整理的动手学PaddlePaddle(3):猫脸识别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Apollo进阶课程⑭ | Apollo
- 下一篇: owmngr.exe - owmngr是