我目前正在研究国际象棋人工智能。该项目背后的想法是创建一个神经网络,该网络学习如何评估棋盘状态,然后使用蒙特卡洛树搜索遍历下一步以找到“最佳”棋步(由 NN 评估)。
TL;博士
NN 在预测数据集的平均评估时陷入困境,因此无法学习预测棋盘状态的评估。
执行
数据集
数据集是国际象棋游戏的集合。游戏取自官方lichess数据库。仅包含具有评估分数(NN 应该学习)的游戏。这将数据集的大小减少到原始数据的 11% 左右。
数据表示
每一步都是训练网络的数据点。NN 的输入是 12 个大小为 8x8 的数组(所谓的位板),每个数组对应 6x2 不同的块和颜色。使用缩放tanh
函数将移动评估标准化为范围 [-1, 1]。由于许多评估非常接近 0 和 -1/1,因此也删除了其中的一部分,以减少数据集中的变化。
在不放弃评估接近 0 或 -1/1 的一些移动的情况下,数据集将如下所示:
删除一些后,数据集看起来像这样,并且在某一点上不太集中:
NN 的输出是介于 -1 和 1 之间的单个标量值,表示对棋盘状态的评估。-1 意味着棋盘非常受黑人玩家青睐,1 意味着棋盘非常受白人玩家青睐。
def create_training_data(dataset: DataFrame) -> Tuple[np.ndarray, np.ndarray]:
def drop(indices, fract):
drop_index = np.random.choice(
indices,
size=int(len(indices) * fract),
replace=False)
dataset.drop(drop_index, inplace=True)
drop(dataset[abs(dataset[12] / 10.) > 30].index, fract=0.80)
drop(dataset[abs(dataset[12] / 10.) < 0.1].index, fract=0.90)
drop(dataset[abs(dataset[12] / 10.) < 0.15].index, fract=0.10)
# the first 12 entries are the bitboards for the pieces
y = dataset[12].values
X = dataset.drop(12, axis=1)
# move into range of -1 to 1
y = y.astype(np.float32)
y = np.tanh(y / 10.)
return X, y
神经网络
神经网络是使用 Keras 实现的。
CNN 用于从板上提取特征,然后传递到密集网络以减少到评估。这是基于 NN AlphaGo Zero 在其实现中使用的。
CNN 实现如下:
model = Sequential()
model.add(Conv2D(256, (3, 3), activation='relu', padding='same', input_shape=(12, 8, 8, 1)))
for _ in range(10):
model.add(Conv2D(256, (3, 3), activation='relu', padding='same'))
model.add(BatchNormalization())
model.add(Conv2D(128, (3, 3), activation='relu'))
model.add(BatchNormalization())
model.add(Conv2D(128, (3, 3), activation='relu'))
model.add(BatchNormalization())
model.add(Flatten())
model.add(Dense(units=64, activation='relu'))
# model.add(Rescaling(scale=1 / 10., offset=0)) required? Data gets scaled in create_training_data, does the Network learn that/does doing that explicitly help?
model.add(Dense(units=1, activation='tanh'))
model.compile(
loss='mean_squared_error',
optimizer=Adam(learning_rate=0.01),
# metrics=['accuracy', 'mse'] # do these influence training at all?
)
训练
训练是使用 Keras 完成的。使用多组 50k-500k 移动来训练网络。该网络在每个移动集上训练 20 个 epoch,批大小为 64,并且 10% 的移动用于验证。
之后学习率调整为0.001 / (index + 1)
。
for i, chunk in enumerate(pd.read_csv("../dataset/nm_games.csv", header=None, chunksize=100000)):
X, y = create_training_data(chunk)
model.fit(
X,
y,
epochs=20,
batch_size=64,
validation_split=0.1
)
model.optimizer.learning_rate = 0.001 / (i + 1)
问题
NN 目前没有学到任何东西。它在几个 epoch 内收敛到对数据集的平均评估,并且不会根据棋盘状态预测任何内容。
20 个 epoch 后的示例:
数据集评估 | 神经网络评估 | 区别 |
---|---|---|
-0.10164772719144821 | 0.03077016 | 0.13241789 |
0.6967725157737732 | 0.03180310 | 0.66496944 |
-0.3644430935382843 | 0.03119821 | 0.39564130 |
0.5291759967803955 | 0.03258476 | 0.49659124 |
-0.25989893078804016 | 0.03316733 | 0.29306626 |
NN 评估停留在 0.03,这是数据集的近似平均评估。它也卡在那里,没有继续改善。
我试过的
- 增加和减少 NN 大小
- 添加了多达 20 个额外的 Conv2D 层,因为谷歌在他们的实现中也这样做了
- 删除了所有 10 个额外的 Conv2D 层,因为我读到很多 NN 对于数据集来说太复杂了
- 一次训练几天
- 由于 NN 停留在 0.03,并且也没有从那里移动,所以这是浪费的。
- 密集NN代替CNN
- 没有消除 NN 卡住的点,但训练得更快(又名卡住得更快:))
model = Sequential() model.add(Dense(2048, input_shape=(12 * 8 * 8,), activation='relu')) model.add(Dense(2048, activation='relu')) model.add(Dense(2048, activation='relu')) model.add(Dense(1, activation='tanh')) model.compile( loss='mean_squared_error', optimizer=Adam(learning_rate=0.001), # metrics=['accuracy', 'mse'] )
- Sigmoid 激活而不是 tanh 将评估从 -1 到 1 的范围移动到 0 到 1 的范围,但除此之外并没有改变任何关于卡住的情况。
- Epochs、batchsize 和 chunksize 增加和减少所有这些变化都没有显着改变 NN 评估。
- 学习率添加
- 较大的学习率 (0.1) 使 NN 不稳定,每次训练都会收敛到 -1、1 或 0。
- 较小的学习率 (0.0001) 使 NN 收敛较慢,但仍停留在 0.03。
问题
该怎么办?是我遗漏了什么还是有错误?