140 lines
5.4 KiB
Python
140 lines
5.4 KiB
Python
from keras.layers import *
|
|
from keras.models import Model
|
|
|
|
|
|
'''The dual residual architecture is the strongest
|
|
of the architectures tested by DeepMind for AlphaGo
|
|
Zero. It consists of an initial convolutional block,
|
|
followed by a number (40 for the strongest, 20 as
|
|
baseline) of residual blocks. The network is topped
|
|
off by two "heads", one to predict policies and one
|
|
for value functions.
|
|
'''
|
|
def dual_residual_network(input_shape, blocks=20):
|
|
inputs = Input(shape=input_shape)
|
|
first_conv = conv_bn_relu_block(name="init")(inputs)
|
|
res_tower = residual_tower(blocks=blocks)(first_conv)
|
|
policy = policy_head()(res_tower)
|
|
value = value_head()(res_tower)
|
|
return Model(inputs=inputs, outputs=[policy, value])
|
|
|
|
|
|
'''The dual convolutional architecture replaces residual
|
|
blocks from the dual residual architecture with batch-normalized
|
|
convolution layers. The default block size is 12.
|
|
'''
|
|
def dual_conv_network(input_shape, blocks=12):
|
|
inputs = Input(shape=input_shape)
|
|
first_conv = conv_bn_relu_block(name="init")(inputs)
|
|
conv_tower = convolutional_tower(blocks=blocks)(first_conv)
|
|
policy = policy_head()(conv_tower)
|
|
value = value_head()(conv_tower)
|
|
return Model(inputs=inputs, outputs=[policy, value])
|
|
|
|
|
|
''' In the separate residual architecture policy and value
|
|
head don't share a common "tail", i.e. there's two sets of
|
|
residual blocks for policy and value networks, respectively.
|
|
'''
|
|
def separate_residual_network(input_shape, blocks=20):
|
|
inputs_pol = Input(shape=input_shape)
|
|
first_conv_pol = conv_bn_relu_block(name="init")(inputs_pol)
|
|
res_tower_pol = residual_tower(blocks=blocks)(first_conv_pol)
|
|
policy = policy_head()(res_tower_pol)
|
|
policy_model = Model(inputs=inputs_pol, outputs=policy)
|
|
|
|
inputs_val = Input(shape=input_shape)
|
|
first_conv_val = conv_bn_relu_block(name="init")(inputs_val)
|
|
res_tower_val = residual_tower(blocks=blocks)(first_conv_val)
|
|
value = value_head()(res_tower_val)
|
|
value_model = Model(inputs=inputs_val, outputs=value)
|
|
|
|
return policy_model, value_model
|
|
|
|
|
|
'''The separate convolutional network is structurally identical
|
|
to the separate residual network, except that residual blocks
|
|
are replaced by convolutional blocks.
|
|
'''
|
|
def separate_conv_network(input_shape, blocks=20):
|
|
inputs_pol = Input(shape=input_shape)
|
|
first_conv_pol = conv_bn_relu_block(name="init")(inputs_pol)
|
|
conv_tower_pol = convolutional_tower(blocks=blocks)(first_conv_pol)
|
|
policy = policy_head()(conv_tower_pol)
|
|
policy_model = Model(inputs=inputs_pol, outputs=policy)
|
|
|
|
inputs_val = Input(shape=input_shape)
|
|
first_conv_val = conv_bn_relu_block(name="init")(inputs_val)
|
|
conv_tower_val = convolutional_tower(blocks=blocks)(first_conv_val)
|
|
value = value_head()(conv_tower_val)
|
|
value_model = Model(inputs=inputs_val, outputs=value)
|
|
|
|
return policy_model, value_model
|
|
|
|
|
|
def conv_bn_relu_block(name, activation=True, filters=256, kernel_size=(3,3),
|
|
strides=(1,1), padding="same", init="he_normal"):
|
|
def f(inputs):
|
|
conv = Conv2D(filters=filters,
|
|
kernel_size=kernel_size,
|
|
strides=strides,
|
|
padding=padding,
|
|
kernel_initializer=init,
|
|
data_format='channels_first',
|
|
name="{}_conv_block".format(name))(inputs)
|
|
batch_norm = BatchNormalization(axis=1, name="{}_batch_norm".format(name))(conv)
|
|
return Activation("relu", name="{}_relu".format(name))(batch_norm) if activation else batch_norm
|
|
return f
|
|
|
|
|
|
def residual_block(block_num, **args):
|
|
def f(inputs):
|
|
res = conv_bn_relu_block(name="residual_1_{}".format(block_num), activation=True, **args)(inputs)
|
|
res = conv_bn_relu_block(name="residual_2_{}".format(block_num) , activation=False, **args)(res)
|
|
res = add([inputs, res], name="add_{}".format(block_num))
|
|
return Activation("relu", name="{}_relu".format(block_num))(res)
|
|
return f
|
|
|
|
|
|
def residual_tower(blocks, **args):
|
|
def f(inputs):
|
|
x = inputs
|
|
for i in range(blocks):
|
|
x = residual_block(block_num=i)(x)
|
|
return x
|
|
return f
|
|
|
|
def convolutional_tower(blocks, **args):
|
|
def f(inputs):
|
|
x = inputs
|
|
for i in range(blocks):
|
|
x = conv_bn_relu_block(name=i)(x)
|
|
return x
|
|
return f
|
|
|
|
|
|
def policy_head():
|
|
def f(inputs):
|
|
conv = Conv2D(filters=2,
|
|
kernel_size=(3, 3),
|
|
strides=(1, 1),
|
|
padding="same",
|
|
name="policy_head_conv_block")(inputs)
|
|
batch_norm = BatchNormalization(axis=1, name="policy_head_batch_norm")(conv)
|
|
activation = Activation("relu", name="policy_head_relu")(batch_norm)
|
|
return Dense(units= 19*19 +1, name="policy_head_dense")(activation)
|
|
return f
|
|
|
|
|
|
def value_head():
|
|
def f(inputs):
|
|
conv = Conv2D(filters=1,
|
|
kernel_size=(1, 1),
|
|
strides=(1, 1),
|
|
padding="same",
|
|
name="value_head_conv_block")(inputs)
|
|
batch_norm = BatchNormalization(axis=1, name="value_head_batch_norm")(conv)
|
|
activation = Activation("relu", name="value_head_relu")(batch_norm)
|
|
dense = Dense(units= 256, name="value_head_dense", activation="relu")(activation)
|
|
return Dense(units= 1, name="value_head_output", activation="tanh")(dense)
|
|
return f |