基于神经网络训练国际象棋数据集时遇IndexError问题求助
国际象棋预测神经网络索引越界错误排查与解决
项目背景
开发基于神经网络的国际象棋预测项目,使用包含对局结果(RES)、是否将死(CHECKMATE)和棋盘状态的数据集训练模型,目标是预测对局结果与将死状态。
数据集示例
RES: 1/2-1/2 CHECKMATE: False FEN: 8/8/8/8/3K2kP/r5N1/1R6/8 b - - 0 62 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . K . . k P r . . . . . N . . R . . . . . . . . . . . . . .
其中RES和CHECKMATE为模型输出目标,棋盘状态为输入特征。
报错信息
训练时触发索引越界错误:
layer_hidden_input = [sum(x * w for x, w in zip(X, weight_one[i])) for i in range(len(weight_one))] IndexError: list index out of range
核心代码(修复后)
import random import math import sys import pickle def network_creator(sizeof_input, sizeof_output, sizeof_hidden): weight_one = [[random.uniform(0, 1) for i in range(sizeof_hidden)] for j in range(sizeof_input)] weight_two = [[random.uniform(0, 1) for i in range(sizeof_output)] for j in range(sizeof_hidden)] return weight_one, weight_two def file_parser(file): chess_states = [] with open(file, 'r') as file: lines = file.readlines() i = 0 while i < len(lines): line = lines[i] if line.startswith("RES:"): res = line.strip().split(' ')[1] checkmate = lines[i + 1].strip().split(' ')[1] fen = lines[i + 2].strip().split(' ')[1] chess = [] i += 3 while i < len(lines) and lines[i].strip() != "": chess.append(lines[i].strip()) i += 1 chess_states.append({ "RES": res, "Checkmate": checkmate, "FEN": fen, "CHESS_Board": chess }) else: i += 1 return chess_states def board_parser(chess_line): my_dict = {'.': 0, 'k': 1, 'r': 2, 'p': 3, 'K': 4, 'b': 5, 'B': 10, 'R': 6, 'Q': 7, 'q': 8, 'P': 9, 'N': 11, 'n': 12} # 移除棋盘行中的空格,确保每个格子对应一个字符 cleaned_line = chess_line.replace(" ", "") new_value = [my_dict[c] for c in cleaned_line if c in my_dict] return new_value def res_parser(res): if res == "1-0": return 1.0 elif res == "0-1": return 0.0 elif res == "1/2-1/2": return 0.5 def extract_data(chess): X_train = [] Y_train = [] for one in chess: # 拼接棋盘行并解析 board_str = "".join(one["CHESS_Board"]) inputs = board_parser(board_str) # 将CHECKMATE转为数值类型 checkmate_val = 1.0 if one["Checkmate"] == "True" else 0.0 expected = (res_parser(one["RES"]), checkmate_val) # 校验输入维度(国际象棋为8x8=64格) if len(inputs) != 64: print(f"跳过异常数据:棋盘维度为{len(inputs)},预期64") continue X_train.append(inputs) Y_train.append(expected) return X_train, Y_train # 定义激活函数及其导数 def activation_function(x): return 1 / (1 + math.exp(-x)) def activation_function_der(x): return x * (1 - x) def forward(X, weight_one, weight_two): # 处理单个样本的前向传播 layer_hidden_input = [sum(x * w for x, w in zip(X, weight_one[i])) for i in range(len(weight_one))] layer_hidden_output = [activation_function(x) for x in layer_hidden_input] layer_output_input = [sum(h * w for h, w in zip(layer_hidden_output, weight_two[i])) for i in range(len(weight_two))] layer_output_output = [activation_function(x) for x in layer_output_input] return layer_hidden_output, layer_output_output def backward(X, y, layer_hidden_output, layer_output_output, weight_two): error_out = [t - p for t, p in zip(y, layer_output_output)] alpha_calcul = [e * activation_function_der(p) for e, p in zip(error_out, layer_output_output)] error_hidden = [sum(alpha_calcul[j] * weight_two[i][j] for j in range(len(alpha_calcul))) for i in range(len(layer_hidden_output))] alpha_calcul_hidden = [e * activation_function_der(h) for e, h in zip(error_hidden, layer_hidden_output)] return alpha_calcul_hidden, alpha_calcul def update(X, alpha_calcul_hidden, alpha_calcul, layer_hidden_output, weight_one, weight_two, lr): # 更新输出层权重 for i in range(len(weight_two)): for j in range(len(weight_two[i])): weight_two[i][j] += layer_hidden_output[i] * alpha_calcul[j] * lr # 更新隐藏层权重,修正索引错误 for i in range(len(weight_one)): for j in range(len(weight_one[i])): weight_one[i][j] += X[i] * alpha_calcul_hidden[j] * lr def weighted_sum(expected_list, predicted_list): # 计算平均损失 total_loss = 0.0 for y, y_pred in zip(expected_list, predicted_list): total_loss += sum((t - p)**2 for t, p in zip(y, y_pred)) / len(y) return total_loss / len(expected_list) def trainer(X, y, epochs=1000, lr=0.1): if not X or not y: print("错误:训练数据为空") return None, None sizeof_input = len(X[0]) sizeof_hidden = 128 # 自定义隐藏层大小,无需与输入维度一致 sizeof_output = len(y[0]) weight_one, weight_two = network_creator(sizeof_input, sizeof_output, sizeof_hidden) for epoch in range(epochs): total_hidden = [] total_output = [] # 遍历每个样本执行训练(随机梯度下降) for x_sample, y_sample in zip(X, y): layer_hidden_output, layer_output_output = forward(x_sample, weight_one, weight_two) total_hidden.append(layer_hidden_output) total_output.append(layer_output_output) alpha_calcul_hidden, alpha_calcul = backward(x_sample, y_sample, layer_hidden_output, layer_output_output, weight_two) update(x_sample, alpha_calcul_hidden, alpha_calcul, layer_hidden_output, weight_one, weight_two, lr) loss = weighted_sum(y, total_output) if epoch % 100 == 0: print(f"Epoch {epoch}, Loss: {loss:.6f}") return weight_one, weight_two def main(argv): ac = len(argv) created_network = None weight_two = None for i in range(ac): if argv[i] == "--new": if i + 3 < ac: try: sizeof_input = int(argv[i + 1]) sizeof_hidden = int(argv[i + 2]) sizeof_output = int(argv[i + 3]) created_network, weight_two = network_creator(sizeof_input, sizeof_output, sizeof_hidden) except: sizeof_input = int(argv[i + 1]) sizeof_output = int(argv[i + 2]) sizeof_hidden = int(math.sqrt(sizeof_input * sizeof_output)) created_network, weight_two = network_creator(sizeof_input, sizeof_output, sizeof_hidden) print(created_network) print(weight_two) elif argv[i] == "--save": if created_network and weight_two and i + 1 < ac: with open(argv[i + 1], 'wb') as f: pickle.dump((created_network, weight_two), f) elif argv[i] == "--load": if i + 1 < ac: with open(argv[i + 1], 'rb') as f: loaded_file = pickle.load(f) print(loaded_file) elif argv[i] == "--train": if i + 1 < ac: fiile = file_parser(argv[i + 1]) X_train, Y_train = extract_data(fiile) trained_one, trained_two = trainer(X_train, Y_train) if __name__ == "__main__": exit(main(sys.argv))
错误原因与修复说明
核心错误原因
- 批量与单样本不匹配:原
forward函数仅支持单个样本,但trainer直接传入整个批量样本列表,导致维度不匹配触发索引越界。 - 棋盘解析异常:原
board_parser未处理棋盘行中的空格,导致解析后的特征维度不是64(8x8棋盘),与权重矩阵维度不兼容。 - 输出目标未数值化:
Checkmate字段为字符串类型,无法参与神经网络数值计算。 - 权重更新索引错误:
update函数中直接使用列表alpha_calcul_hidden进行计算,未取对应索引值,引发类型与索引错误。 - 缺失激活函数:原代码未定义
activation_function和activation_function_der,运行时会触发未定义错误。
修复要点
- 调整训练逻辑:在
trainer中遍历每个样本执行前向/反向传播,实现随机梯度下降。 - 规范棋盘解析:移除棋盘行中的空格,确保每个样本特征维度为64,并过滤异常数据。
- 数值化输出目标:将
Checkmate字符串转换为1.0/0.0的数值类型。 - 修正权重更新:在
update中使用alpha_calcul_hidden[j]对应索引进行权重更新。 - 补充激活函数:添加sigmoid激活函数及其导数,适配神经网络的数值计算需求。
内容的提问来源于stack exchange,提问作者Mikael ANAGO
相关产品推荐
相关产品推荐

