本章包括:
- 构建一个端到端的应用程序来训练和运行一个围棋机器人
- 在前端运行来对抗你的机器人
- 让你的机器人在本地与其他机器人对抗。
- 部署到在线围棋服务器
到目前为止,你已经知道如何为围棋落子预测去构建和训练一个强大的深度学习模型,但是你如何将它集成到一个与对手玩游戏的应用程序中?训练神经网络工作只是构建端到端应用程序的一部分,不管你是自我对弈,还是让你的机器人与其他机器人竞争,这个模型必须集成成一个可以使用的引擎。
在本章中,您将构建一个简单的围棋模型服务器和两个前端。首先,我们为您提供一个HTTP前端,您可以用来对抗您的机器人。然后,我们介绍围棋文本协议(GTP),这是一种广泛使用的协议,围棋AI可以互相进行对抗,比如你可以挑战GNUGo或Pachi,这是两个免费提供的基于GTP的围棋程序。最后,我们将向您展示如何在AmazonWebServices(AWS)上部署围棋机器人并将其与在线围棋服务器(OGS)连接。这样做可以让你的机器人在真正的游戏中与世界各地的其他机器人和人类玩家竞争,获得相应的排名。为了做到这一切,你需要完成以下任务:
- 构建一个围棋落子预测-你在第6章和第7章中训练的神经网络需要集成到一个框架中,允许你在游戏中使用它们。在第8.1节中,我们将按照第3章中得到的随机落子AI的概念创建深度学习机器人
- 提供一个图形界面---作为人类,我们需要这个界面可以方便地对抗围棋机器人。在8.2节中,我们将为您配备一个有趣的届满让你可以与AI进行对抗。
- 把AI部署到云上----如果你的计算机中没有强大的GPU,你就不会得到训练强大的围棋机器人。幸运的是,大多数的云都提供GPU实例,但即使你有足够强大的GPU进行训练,你仍然可能希望把你以前训练过的模型托管在服务器上。在第8.3节中,我们将向您展示如何托管,要了解更多的细节可以去看附录D。
- 与其他AI对弈----人类喜欢使用图形和其他界面,但对于机器人来说,习惯上通过标准化的协议进行通信。在第8.4节中,我们将向您介绍通用围棋文本协议(GTP)。下面两点是重要的组成部分:
1.与其他机器人对弈-----后你将为你的机器人建立一个GTP前端,让它与8.5节中的其他程序进行对抗。我们将教你 如何让你的机器人在本地与另外两个围棋程序进行比赛,去看看你的AI有多好。
2.在联机围棋服务器上部署机器人-----在第8.6节中,我们将向您展示如何在联机围棋平台上部署机器人,以便让其 他机器人可以和你的机器人竞争。这样,您的机器人甚至可以得到排名,所有这些我们将在最后一节展示给您。因 为大部分材料都是技术性的,你可以附录E中找到大量细节。
8.1 创建一个深度学习的落子预测AI
现在,您已经有了所有的构建块来为围棋数据构建一个强大的神经网络,让我们将这些网络集成到一个为它们服务的代理中。回顾第三章的概念,我们将其定义实现select_move方法为当前游戏状态选择下一个落子点的类。让我们使用Keras模型和围棋盘Encoder去编写DeepLearningAgent(将此代码放入dlgo中的agent模块中的predict.py中)
from dlgo.agent.base import Agent
from dlgo.agent.helpers import is_point_true_eye
from dlgo import goboard
from dlgo import Encoder
class DeepLearningAgent(Agent):
def __init__(self, model, encoder):
super().__init__()
self.model = model
self.encoder = encoder
接着,您将使用编码器将棋盘撞他转换为特征,然后使用该模型去预测下一步落子。实际上,您将使用该模型去计算所有可能的概率分布。
# 返回整个棋盘预测概率分布
def predict(self, game_state):
encoded_state = self.encoder.encode(game_state)
input_tensor = np.array([encoded_state])
return self.model.predict(input_tensor)[0]
def select_move(self,game_state):
num_moves = self.encoder.board_width*self.encoder*board_height
# 获得预测概率分布
move_probs = predict(game_state)
接下来,您可以稍微改变存储在move_probs中的概率分布。首先,计算所有值的三次方,以大幅增加可能和不可能落子点之间的距离。你希望最好的落子点能尽可能挑选频繁,然后使用一种叫做裁剪的技巧,它可以防止落子概率太接近0或1。这是通过定义一个极小的正值,ε=0.000001,设置值小于ε到ε,值大于1-ε到1-ε。然后,对得到的值进行规范化,再次得到概率分布
# 大幅拉开可能点与非可能点之间的距离
move_probs = move_probs ** 3
small_data = 1e-6
# 防止落子概率卡在0或1
move_probs = np.clip(move_probs,small_data,1-small_data)
# 重新得到另一个概率分布
move_probs = move_probs/sum(small_data)
您进行此转换是因为您希望根据它们的概率从这个分布采样落子点。(另一个可行的策略不是抽样调查,而是始终采取分布的最大值)。你不采取第二种做法的好处是有时会选择其他可能有用的落子
# 把概率转成一个排序列表
candidates = np.arange(num_moves)
# 按照落子概率进行采样,不允许取相同的落子
ranked_moves = np.random.choice(
candidates, num_moves, replace=False, p=move_probs)
# 从最高层开始,找到一个有效的落子,不会减少视野空间
for point_index in ranked_moves:
point = self.encoder.decode_point_index(point_index)
if game_state.is_valid(goboard.Move.play(point)) and \
not is_point_an_eye(game_state.board, point, game_state.current_player):
return goboard.Move.play(point)
return goboard.Move.pass_turn()
你训练了一个深度学习模型,并创建了一个代理,然后你坚持下去。稍后,这个代理将被反序列化成了服务,这样人类玩家或其他机器人就可以对抗它。要执行序列化步骤,就需要序列化Keras的格式。当您持久化Keras模型时,它将以HD