上一節(jié)贮预,我們從圍棋服務(wù)器中下載大量棋譜,并將其轉(zhuǎn)換成網(wǎng)絡(luò)可以解析的數(shù)據(jù)格式契讲,在神經(jīng)網(wǎng)絡(luò)的開(kāi)發(fā)中完成了最繁瑣的一步仿吞,也就是數(shù)據(jù)準(zhǔn)備。接下來(lái)我們將創(chuàng)建一個(gè)神經(jīng)網(wǎng)絡(luò)捡偏,對(duì)數(shù)據(jù)進(jìn)行解讀唤冈,使得網(wǎng)絡(luò)具備6到7段的圍棋專業(yè)水平,它尚未具備打敗柯潔或李世石這些頂級(jí)高手的能力银伟,但打敗業(yè)余級(jí)高手則綽綽有余你虹。
我們要完成的網(wǎng)絡(luò)有三種形態(tài)慨削,一種是小型網(wǎng)絡(luò)概荷,一種是中型網(wǎng)絡(luò)嗓化,一種是大型網(wǎng)絡(luò)抚恒,其能力由弱到強(qiáng)糯俗,當(dāng)然訓(xùn)練的耗時(shí)也由少到多珍手。網(wǎng)絡(luò)的基本結(jié)構(gòu)是真竖,前4層我們都使用卷積層荐开,最后一層是一個(gè)含有19*19個(gè)神經(jīng)元的全連接層模孩。這里我們將引入一種新的網(wǎng)絡(luò)層叫ZeroPadding2D層尖阔。卷積網(wǎng)絡(luò)在處理圖像時(shí),它會(huì)把圖像分割成多個(gè)小塊后分別識(shí)別榨咐,識(shí)別所得的結(jié)果在規(guī)格上介却,也就是寬和高上相對(duì)于輸入時(shí)的數(shù)據(jù)而言會(huì)有所縮小,如果卷積層較多块茁,那么圖片在層層處理后齿坷,規(guī)格會(huì)嚴(yán)重減小桂肌。
為了防止規(guī)格縮小得太嚴(yán)重,我們?cè)趯D片輸入卷積網(wǎng)絡(luò)前永淌,會(huì)給它填充若干行和列崎场,假設(shè)我們要處理的圖片規(guī)格為19*19,如果我們執(zhí)行下面語(yǔ)句:
ZeroPadding2D(padding = 2, input_shape = input_shape, data_format='channel_first')
那意味著我們?cè)?919的二維數(shù)組左邊和右邊分別添加2列遂蛀,在上面和底部分別添加2行谭跨,這些列和行全部使用0來(lái)填充,于是就能得到一個(gè)2323的二維數(shù)組李滴。當(dāng)我們把輸入數(shù)據(jù)先擴(kuò)大螃宙,經(jīng)過(guò)卷積網(wǎng)絡(luò)處理后就不會(huì)嚴(yán)重縮水。
同時(shí)我們還得注意所坯,在卷積層谆扎,我們會(huì)用多個(gè)過(guò)濾器對(duì)圖片進(jìn)行掃描,圖片被一個(gè)過(guò)濾器掃描后就得到一個(gè)二維數(shù)組作為掃描結(jié)果芹助,如果多個(gè)過(guò)濾器就會(huì)得到多個(gè)二維數(shù)組堂湖,如下圖:
過(guò)濾器的個(gè)數(shù),我們也叫做channel周瞎,上面代碼中channel_first苗缩,表示卷積網(wǎng)絡(luò)輸出結(jié)果中饵蒂,把過(guò)濾器的數(shù)值放在掃描結(jié)果對(duì)應(yīng)的二維數(shù)組前面声诸,假設(shè)卷積層網(wǎng)絡(luò)有N個(gè)過(guò)濾器,每個(gè)過(guò)濾器掃描圖片后得到的結(jié)果二維數(shù)組為W和H退盯,于是上面‘channel_first'指定卷積層輸出的結(jié)果為:[N, W, H]彼乌,也就是把過(guò)濾器的個(gè)數(shù)放在結(jié)果向量的前面。
有了上面的基本解釋后渊迁,我將用代碼構(gòu)造如下結(jié)構(gòu)的神經(jīng)網(wǎng)絡(luò):
Layer (type) Output Shape Param #
=================================================================
zero_padding2d_5 (ZeroPaddin (None, 1, 25, 25) 0
_________________________________________________________________
conv2d_5 (Conv2D) (None, 48, 19, 19) 2400
_________________________________________________________________
activation_6 (Activation) (None, 48, 19, 19) 0
_________________________________________________________________
zero_padding2d_6 (ZeroPaddin (None, 48, 23, 23) 0
_________________________________________________________________
conv2d_6 (Conv2D) (None, 32, 19, 19) 38432
_________________________________________________________________
activation_7 (Activation) (None, 32, 19, 19) 0
_________________________________________________________________
zero_padding2d_7 (ZeroPaddin (None, 32, 23, 23) 0
_________________________________________________________________
conv2d_7 (Conv2D) (None, 32, 19, 19) 25632
_________________________________________________________________
activation_8 (Activation) (None, 32, 19, 19) 0
_________________________________________________________________
zero_padding2d_8 (ZeroPaddin (None, 32, 23, 23) 0
_________________________________________________________________
conv2d_8 (Conv2D) (None, 32, 19, 19) 25632
_________________________________________________________________
activation_9 (Activation) (None, 32, 19, 19) 0
_________________________________________________________________
flatten_2 (Flatten) (None, 11552) 0
_________________________________________________________________
dense_3 (Dense) (None, 512) 5915136
_________________________________________________________________
activation_10 (Activation) (None, 512) 0
_________________________________________________________________
dense_4 (Dense) (None, 361) 185193
=================================================================
Total params: 6,192,425
Trainable params: 6,192,425
Non-trainable params: 0
有了上面網(wǎng)絡(luò)后慰照,我們?cè)偻ㄟ^(guò)上節(jié)實(shí)現(xiàn)的數(shù)據(jù)加載方法,將棋盤數(shù)據(jù)輸入網(wǎng)絡(luò)進(jìn)行訓(xùn)練琉朽,完成后網(wǎng)絡(luò)就具備了能打敗業(yè)余高手的能力毒租。通過(guò)上面網(wǎng)絡(luò)加上人類棋譜的訓(xùn)練,網(wǎng)絡(luò)對(duì)棋手下一步走法的預(yù)測(cè)率能高達(dá)85%作用箱叁,注意到網(wǎng)絡(luò)預(yù)測(cè)的是專業(yè)六段和七段棋手的走法墅垮,因此它能輕易打敗業(yè)余級(jí)的高手。
接下來(lái)我們看看具體代碼的實(shí)現(xiàn):
from keras.layers.core import Dense, Activation, Flatten
from keras.layers.convolutional import Conv2D, ZeroPadding2D
def layers(input_shape):
return [
ZeroPadding2D(padding =3, input_shape = input_shape,
data_format = 'channels_first'),
Conv2D(48, (7,7), data_format = 'channels_first'),
Activation('relu'),
ZeroPadding2D(padding = 2, data_format = 'channels_first'),
Conv2D(32, (5,5), data_format = 'channels_first'),
Activation('relu'),
ZeroPadding2D(padding = 2, data_format = 'channels_first'),
Conv2D(32, (5,5), data_format = 'channels_first'),
Activation('relu'),
ZeroPadding2D(padding = 2, data_format = 'channels_first'),
Conv2D(32, (5,5), data_format = 'channels_first'),
Activation('relu'),
Flatten(),
Dense(512),
Activation('relu'),
]
上面代碼構(gòu)造了網(wǎng)絡(luò)各個(gè)處理層耕漱,我們把網(wǎng)絡(luò)層作為單獨(dú)對(duì)象放在一個(gè)隊(duì)列中的好處是算色,后面我們可以根據(jù)需要進(jìn)行加載,如果我們想讓網(wǎng)絡(luò)處理能力更強(qiáng)螟够,那么就多加載幾個(gè)網(wǎng)絡(luò)層灾梦,如果希望處理能力小一些峡钓,就可以加載少一些網(wǎng)絡(luò)層,如此能讓網(wǎng)絡(luò)的構(gòu)建更加靈活若河。
接下來(lái)我們通過(guò)調(diào)用上面函數(shù)構(gòu)建整個(gè)網(wǎng)絡(luò):
#encoder.num_planes對(duì)應(yīng)channel
input_shape = (encoder.num_planes, go_board_rows, go_board_cols)
#將網(wǎng)絡(luò)層一層層加進(jìn)來(lái)
network_layers = layers(input_shape)
model = Sequential()
for layer in network_layers:
model.add(layer)
model.add(Dense(num_classes, activation = 'softmax'))
model.compile(loss = 'categorical_crossentropy', optimizer = 'sgd',
metrics = ['accuracy'])
model.summary()
上面代碼生成的就是前面我們展示的網(wǎng)絡(luò)結(jié)構(gòu)能岩,有了網(wǎng)絡(luò)后,我們加載數(shù)據(jù)為網(wǎng)絡(luò)的訓(xùn)練和驗(yàn)證做準(zhǔn)備:
#加載訓(xùn)練數(shù)據(jù)和測(cè)試數(shù)據(jù),這個(gè)過(guò)程相當(dāng)耗時(shí)
processor = GoDataProcessor(encoder = encoder.name())
generator = processor.load_go_data('train', num_games, use_generator = True)
test_generator = processor.load_go_data('test', num_games, use_generator = True)
然后就可以啟動(dòng)網(wǎng)絡(luò)的訓(xùn)練流程了:
#由于數(shù)據(jù)加載過(guò)程耗時(shí)牡肉,這段訓(xùn)練過(guò)程在沒(méi)有足夠算力情況下很難執(zhí)行
model.fi_generator(generator = generator.generate(batch_size, num_classes),
epochs = epochs,
steps_per_epoch = generator.get_num_samples() / batch_size,
validation_data = test_generator.generate(batch_size, num_classes),
validation_step = test_generator.get_num_samples() / batch_size,
callbacks = [
ModelCheckpoint('/content/gdrive/My Drive/GO_RECORD/checkpoints/small_model_epoch{epoch}.h5')
]
)
一個(gè)問(wèn)題在于捧灰,訓(xùn)練過(guò)程非常耗時(shí),在沒(méi)有GPU或相關(guān)算力支持的情況下我們需要等待很久時(shí)間才能完成整改訓(xùn)練流程统锤,幸運(yùn)的是我們可以直接加載已經(jīng)訓(xùn)練好的網(wǎng)絡(luò)參數(shù)毛俏,直接越過(guò)耗時(shí)耗力的訓(xùn)練過(guò)程:
from keras.models import load_model
model = load_model('/content/gdrive/My Drive/GO_RECORD/small_model_epoch_5.h5')
#幸運(yùn)的是,已經(jīng)有訓(xùn)練好的網(wǎng)絡(luò)饲窿,我們可以跳過(guò)煩瑣的訓(xùn)練階段煌寇,直接加載訓(xùn)練好的網(wǎng)絡(luò)參數(shù)以檢驗(yàn)效果
model.evaluate_generator(generator = test_generator.generate(batch_size, num_classes),
steps = test_generator.get_num_samples() / batch_size)
上面代碼中small_model_epoch_5.h5是已經(jīng)訓(xùn)練好的網(wǎng)絡(luò)參數(shù)存儲(chǔ)文件,我們將其直接加載到我們的網(wǎng)絡(luò)結(jié)構(gòu)中從而省略掉訓(xùn)練過(guò)程逾雄,得到最終網(wǎng)絡(luò)后阀溶,我們將其直接應(yīng)用在測(cè)試數(shù)據(jù)上,運(yùn)行后可發(fā)現(xiàn)鸦泳,網(wǎng)絡(luò)對(duì)數(shù)據(jù)預(yù)測(cè)的準(zhǔn)確率達(dá)到85%以上银锻,由于它預(yù)測(cè)的是六段和七段專業(yè)選手的落子,因此這樣的準(zhǔn)確率足以打敗業(yè)余級(jí)別的高水平人類選手做鹰。
更詳細(xì)的講解和代碼調(diào)試演示過(guò)程击纬,請(qǐng)點(diǎn)擊鏈接
新書(shū)上架,請(qǐng)諸位朋友多多支持: