上一节,我们构造了如下结构的神经网络:
Layer (type) Output Shape Param #
=================================================================
conv2d_59 (Conv2D) (None, 192, 19, 19) 91392
_________________________________________________________________
conv2d_60 (Conv2D) (None, 192, 19, 19) 331968
_________________________________________________________________
conv2d_61 (Conv2D) (None, 192, 19, 19) 331968
_________________________________________________________________
conv2d_62 (Conv2D) (None, 192, 19, 19) 331968
_________________________________________________________________
conv2d_63 (Conv2D) (None, 192, 19, 19) 331968
_________________________________________________________________
conv2d_64 (Conv2D) (None, 192, 19, 19) 331968
_________________________________________________________________
conv2d_65 (Conv2D) (None, 192, 19, 19) 331968
_________________________________________________________________
conv2d_66 (Conv2D) (None, 192, 19, 19) 331968
_________________________________________________________________
conv2d_67 (Conv2D) (None, 192, 19, 19) 331968
_________________________________________________________________
conv2d_68 (Conv2D) (None, 192, 19, 19) 331968
_________________________________________________________________
conv2d_69 (Conv2D) (None, 192, 19, 19) 331968
_________________________________________________________________
conv2d_70 (Conv2D) (None, 192, 19, 19) 331968
_________________________________________________________________
conv2d_71 (Conv2D) (None, 1, 19, 19) 193
_________________________________________________________________
flatten_5 (Flatten) (None, 361) 0
_________________________________________________________________
dense_7 (Dense) (None, 256) 92672
_________________________________________________________________
dense_8 (Dense) (None, 1) 257
=================================================================
Total params: 3,836,162
Trainable params: 3,836,162
Non-trainable params: 0
它将作为工具,用于分析环境,以便帮助Agent做出正确选择。我们将构造一个Agent对象,真正的主角是它,它将执行我们制定的策略算法,然后不断调教网络,让它深入分析环境特性,以便提供准确的数据给Agent做决策。
首先要做的是使用上一节说明的棋盘编码方法对大量棋盘数据进行编码后训练网络,其基本流程如下:
接下来我们首先加载训练网络所需要的数据,这些数据跟我们前面拥有从围棋服务器上下载的棋盘数据一模一样:
f = open('AlphaGoEncoder', 'rb')
my_encoder = pickle.load(f)
f = open('GoDataProcessor', 'rb')
my_processor = pickle.load(f)
rows, cols = 19, 19
num_classes = rows * cols
num_games = 10000
generator = my_processor.load_go_data('train', num_games, use_generator = True)
test_generator = my_processor.load_go_data('test', num_games, use_generator = True)
为了不用把相关类的代码加载到本地文件,我将它们序列化成二进制文件,使用时直接读取,要不然我们需要在本地添加很多相关类的实现代码,这会造成本地代码凌乱从而难以控制,在运行上面代码时,要在本地目录建立一个名为"data"的文件夹,因为load_go_data会将围棋服务器上的棋盘数据下载到该文件夹下。接下来我们初始化上一节完成的神经网络:
#代码用于参考,我们没有算力因此很难训练出网络,所以我们直接加载已经训练好的网络
input_shape = (my_encoder.num_planes, rows, cols)
alphago_s1_policy = alphago_model(input_shape, is_policy_net = True)
alphago_s1_policy.compile('sgd', 'categorical_crossentropy', metrics = ['accuracy'])
epochs = 200
batch_size = 128
#先用数据训练网络,让网络具备一定的下棋能力
alphago_s1_policy.fit_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_steps = test_generator.get_num_samples() / batch_size,
callbacks = [ModelCheckpoint('alphago_s1_policy_(epoch).h5')])
如果没有GPU硬件,那么我们就不可能有足够的算力去训练网络,因此上面代码运行时将很难在短时间内结束,为了节省时间,我们使用下面代码将已经训练好的网络直接加载:
model.save('alphago_networks.h5')
已经训练好的网络其所有参赛存储在alphago_networks.h5文件中,之间加载已经训练好的网络,如此我们就不用耗费大量的时间在网络训练上。上面的网络使用了人类棋手生成的棋盘数据进行训练,因此已经具备了一定的下棋能力,但是还不够强大,远远达不到AlphaGo的地步,要实现AlphaGo的水平,网络必须能够自我对弈,然后不断提升,这个过程分三步走,首先创建两个Agent,每个Agent都包含上面训练好的网络,2,让这两个Agent相互对弈,Agent利用上面网络来进行落子预测;3,收集对弈结果,用这些结果再次训练网络;4,评估训练结果。
我们在前面章节已经展示过两个机器人自我对弈的过程,本节大同小异,只不过对弈过程的处理要比前面章节复杂得多。我们的Agent需要在一个不断变化的环境中提升自己,这个环境就是棋盘上落子变化。一次episode就相当于两个机器人下完一盘棋,如果我们的agent赢了,那么这盘棋中Agent每次落子获得的回报就是1,如果输了,那么每次落子的回报就是-1,如此agent就可以从每次落子中获得环境的反馈。
接下来我们看看Agent的实现:
class PolicyAgent():
def __init__(self, model, encoder):
self.model = model #该变量对应上面训练的网络
self.encoder = encoder #对应48层棋盘编码
def select_move(self, game_state):#根据棋盘情况选择落子
board_tensor = self.encoder.encode(game_state)
X = np.np.array([board_tensor])
move_probs = self.model.predict(X)[0] #利用网络预测下一步落子,返回每一步落子概率
move_probs = clip_probs(move_probs) #过滤掉那些概率过高或过低的步骤
num_moves = self.encoder.board_width * self.encoder.board_height
candidates = np.arange(num_moves)
ranked_moves = np.random.choice(candidates, num_moves, replace = False, p = move_probs) #对所有落子步骤根据其概率进行抽样
for point_idx in ranked_moves:
point = self.encoder.decode_point_index(point_idx)
move = Move.play(point)
#判断当前落子是否合理
is_valid = game_state.is_valid_move(move)
is_an_eye = is_point_an_eye(game_state.board, point, game_state.next_player)
if is_valid and (not is_an_eye):
return Move.play(point)
return Move.pass_turn()
接下来我们要实现的是Agent能自我对弈。首先我们针对Agent所在的"环境"定义一个数据结构,"环境"包含三要素,一是棋盘,二是落子,三是回报。
class ExperienceBuffer:
def __init__(self, states, actions, rewards):
self.states = states
self.actions = actions
self.rewards = rewards
我们让上面定义的两个PolicyAgent自我对弈,然后记录下对弈过程中每一步落子,当对弈结束后,如果我方Agent胜利,那么我方所有落子步骤对应的回报就是1,如果失败了,我方每一次落子步骤对应的回报为-1,这个记录过程我们用一个专门类来实现:
class ExperienceCollector:
def __init__(self):
self.states = []
self.actions = []
self.rewards = []
self.current_episode_states = []
self.current_episode_actions = []
def begin_episode(self):
self.current_episode_states = []
self.current_episode_actions = []
def record_decision(self, state, action):
#记录Agent在给定棋盘下的每次落子
self.current_episode_states.append(state)
self.current_episode_actions.append(action)
def complete_episode(self, reward):
#在对弈结束后根据输赢状况给每一步落子赋予对应回馈
num_states = len(self.current_episode_states)
self.states += self.current_episode_states
self_actions += self.current_episode_actions
#如果赢了,每部落子获得回馈1,输了每步落子获得回馈-1
self.rewards += [reward for _ in range(num_states)]
self.current_episode_states = []
self.current_episode_actions = []
def to_buffer(self):
return ExperienceBuffer(states = np.array(self.states),
actions = np.array(self.actions),
rewards = np.array(self.rewards))
我们后面会让两个PolicyAgent相互博弈,然后使用上面ExperienceCollector来记录博弈过程中每一步的落子结果。根据最终结果,该类会对我们的Agent所下的每一步棋进行评分,如果Agent赢了,那么它会给每一步棋赋予1分,因为结果赢了,表明Agent对于棋盘状况做出的每一步反应就是正确的,因此应该获得环境的正回馈,如果输了,那么每一步棋得-1分,因为最终输了表明Agent对棋盘做出的反应是错误的,因此应该获得负反馈。
上面收集到的数据可以用于训练Agent下棋策略,增强其落子智能,这恰恰是AlphaGo的精髓所在,这部分代码的实现我们将在下一节给出。
更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:
新书上架,请诸位朋友多多支持: