本篇主要內(nèi)容:
1、在PaddlePaddle中訓(xùn)練瞻鹏、預(yù)測自定義單字驗證碼數(shù)據(jù)集
一鹿寨、準(zhǔn)備工作
如果對PaddlePaddle不熟悉,在實踐本篇時遇到了問題释移,不妨查看一下本專題下的內(nèi)容。
點擊查看本專題所有文章
驗證碼數(shù)據(jù)集下載:https://github.com/GT-ZhangAcer/DLExample/tree/master/easy02_Reader
二涩蜘、導(dǎo)入關(guān)鍵模塊
import paddle.fluid as fluid
import numpy as np
import PIL.Image as Image
三熏纯、構(gòu)建Reader
在深度學(xué)習(xí)中,訓(xùn)練并不是越多越好误窖,因為訓(xùn)練只是對當(dāng)前送給神經(jīng)網(wǎng)絡(luò)的那一部分負(fù)責(zé)。
舉個考試的例子霹俺,平常老師給我們發(fā)測試題,我們做一遍可能還有不會的愈魏,再做一遍可能會好一些想际,當(dāng)做到第N遍的時候...
你看到題了,就能回想起答案胡本。如果這種情況持續(xù)了10年,你可能會漸漸記下這個答案侧甫,而不是這種題型的做題技巧!
當(dāng)遇到了同一類型的新題時彩扔,還記得當(dāng)年那個聰明的你嗎僻爽?
所以贾惦,我們需要找到一個平衡點,當(dāng)模型對新的數(shù)據(jù)表現(xiàn)不好時立刻停止訓(xùn)練碰镜,保證學(xué)到的是“做題技巧”而不是答案。這樣的新數(shù)據(jù)绪颖,可以是測試集或交叉驗證集(兩者實際還是有一定差別甜奄,本專題深度學(xué)習(xí)開發(fā)篇(二)有簡略介紹)
Reader部分
為了劃分一部分?jǐn)?shù)據(jù)作為交叉驗證集,在這個reader中牍氛,我們在reader
函數(shù)外面再套上一個函數(shù)switch_reader
烟阐,方便選擇reader返回的是哪一部分的數(shù)據(jù)紊扬,同時設(shè)置一個布爾類型的參數(shù)作為開關(guān)唉擂。
def switch_reader(is_val: bool = False):
def reader():
# 讀取標(biāo)簽數(shù)據(jù)
with open(data_path + "/OCR_100P.txt", 'r') as f:
labels = f.read()
# 判斷是否是驗證集
if is_val:
index_range = range(1501, 2000)
else:
index_range = range(1, 1500)
# 抽取數(shù)據(jù)使用迭代器返回
for index in index_range:
im = Image.open(data_path + "/" + str(index) + ".jpg").convert('L') # 使用Pillow讀取圖片
im = np.array(im).reshape(1, 1, 30, 15).astype(np.float32) # NCHW格式
im /= 255 # 歸一化以提升訓(xùn)練效果
lab = labels[index - 1] # 因為循環(huán)中i是從1開始迭代的楔敌,所有這里需要減去1
yield im, int(lab)
return reader # 注意!此處不需要帶括號
這里需要注意的是卵凑,return reader
后不能加括號,因為我們要返回的是reader
這個函數(shù)對象伙判,而不是調(diào)用這個函數(shù)黑忱,等劃分mini batch時再進(jìn)行調(diào)用它更為合適。
開始制作mini_batch
# 劃分mini_batch
batch_size = 128
train_reader = fluid.io.batch(reader=switch_reader(), batch_size=batch_size)
val_reader = fluid.io.batch(reader=switch_reader(is_val=True), batch_size=batch_size)
定義輸入層
該怎樣塞進(jìn)神經(jīng)網(wǎng)絡(luò)呢菇曲?我們需要給它制定一個規(guī)范對不對抚吠?
# 定義網(wǎng)絡(luò)輸入格式
img = fluid.data(name="img", shape=[-1, 1, 30, 15], dtype="float32")
# 把標(biāo)簽也順便定義了吧
label = fluid.data(name='label', shape=[-1, 1], dtype='int64')
這里的-1, 1, 30, 15
分別代表Batch_size、C喊式、H、W岔留。
為什么Batch_size指定為-1
呢检柬?
因為我們無法保證xxx_reader
每次返回的就是我們設(shè)置的128組數(shù)據(jù),也就是說當(dāng)我們只有200條數(shù)據(jù)時里逆,第一組有128條头朱,剩下的72條單獨一組。
所以我們在這里把Batch_size位置設(shè)置為-1
班眯,這樣就可以自適應(yīng)Batch_size的大小了。
四署隘、使用PaddlePaddle搭建網(wǎng)絡(luò)層
因為數(shù)據(jù)集特別簡單,所以用簡單的全連接層組成的網(wǎng)絡(luò)就足以滿足要求违崇。
這里我們使用3層全連接層作為主要網(wǎng)絡(luò),因為剛剛我們已經(jīng)定義好了輸入層羞延,所有現(xiàn)在可以直接定義隱藏層了~
# 定義第一個隱藏層脾还,激活函數(shù)為ReLU
hidden = fluid.layers.fc(input=img, size=200, act='relu')
# 第二個,激活函數(shù)仍為ReLU
hidden = fluid.layers.fc(input=hidden, size=200, act='relu')
# 以softmax為激活函數(shù)的全連接層為輸出層鄙漏,輸出層的大小必須為Label的總數(shù)10
net_out = fluid.layers.fc(input=hidden, size=10, act='softmax')
如果對激活函數(shù)不熟悉的話,可以先無腦Relu巩步,因為Relu在大部分情況下表現(xiàn)很好桦踊。
用他不能保證一定是最優(yōu)秀的,但至少能保證不會很輕松的掛掉钞钙。
五声离、訓(xùn)練開始前的配置
使用API計算正確率
acc = fluid.layers.accuracy(input=net_out, label=label)
這里的input
傳入是輸出層的結(jié)果,label
則為剛剛所定義的標(biāo)簽本刽。
克隆一個程序給驗證集使用
eval_prog = fluid.default_main_program().clone(for_test=True)
查看驗證集的結(jié)果也需要將數(shù)據(jù)喂到神經(jīng)網(wǎng)絡(luò)里赠涮。
是不是還需要再把上面給訓(xùn)練集定義的部分,單獨再定義一遍呢斜友?
直接用API克隆出來一個吧垃它,特別方便烹看!
定義損失函數(shù)
loss = fluid.layers.cross_entropy(input=net_out, label=label)
avg_loss = fluid.layers.mean(loss)
如果對損失函數(shù)的作用不了解惯殊,可以參考之前的文章也殖。
這里我們使用交叉熵?fù)p失函數(shù),差不多就是計算網(wǎng)絡(luò)層的輸出與標(biāo)簽直接的差距還有多大忆嗜。
定義優(yōu)化方法
sgd_optimizer = fluid.optimizer.SGD(learning_rate=0.01)
sgd_optimizer.minimize(avg_loss) # 定義參數(shù)更新(反向傳播也包含在內(nèi))
這里我們使用SGD隨機(jī)梯度下降法作為優(yōu)化方案。
但對于較復(fù)雜的數(shù)據(jù)集還是建議使用Adam來優(yōu)化址愿,效率可能會更高一些冻璃。
之所以把克隆驗證程序放在定義優(yōu)化方法之前,因為我們需要保證在驗證時模型學(xué)到的全部是訓(xùn)練集的參數(shù)娘纷,驗證集不可參與“學(xué)習(xí)”(參數(shù)更新)跋炕,這樣才知道到底學(xué)習(xí)的如何。
定義執(zhí)行器
place = fluid.CPUPlace() #使用CPU訓(xùn)練辐烂,此處也可以換成GPU
exe = fluid.Executor(place)
數(shù)據(jù)傳入順序設(shè)置
feeder = fluid.DataFeeder(place=place, feed_list=[img, label])
這里的feed_list
的順序?qū)τ赗eader里yield返回的順序。
六胳嘲、開始訓(xùn)練
# 對網(wǎng)絡(luò)層進(jìn)行初始化
prog = fluid.default_startup_program()
exe.run(prog)
Epoch = 10 # 訓(xùn)練10輪
for i in range(Epoch):
batch_loss = None
batch_acc = None
# 訓(xùn)練集 只看loss來判斷模型收斂情況
for batch_id, data in enumerate(train_reader()):
outs = exe.run(
feed=feeder.feed(data),
fetch_list=[loss])
batch_loss = np.average(outs[0])
# 驗證集 只看準(zhǔn)確率來判斷收斂情況
for batch_id, data in enumerate(val_reader()):
outs = exe.run(program=eval_prog,
feed=feeder.feed(data),
fetch_list=[acc])
batch_acc = np.average(outs[0])
print("Epoch:", i, "\tLoss:{:3f}".format(batch_loss), "\tAcc:{:2f} %".format(batch_acc * 100))
為了輸出看起來好看一些扣草,對于訓(xùn)練集我們只要求返回loss的值,驗證集只返回正確率的值鹰祸。
這些都定義在fetch_list=[xxx]
中密浑。
七、訓(xùn)練效果
可以看到驗證集上效果非常棒街图,在第8個Epoch上已經(jīng)達(dá)到了100%!(相信你知道為什么是第8個Epoch)
關(guān)于模型保存台夺,下一節(jié)將對模型保存進(jìn)行詳細(xì)介紹。
示例代碼以及數(shù)據(jù)集
https://github.com/GT-ZhangAcer/DLExample/tree/master/easy03_CV_Classify