You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于神经网络训练国际象棋数据集时遇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))

错误原因与修复说明

核心错误原因

  1. 批量与单样本不匹配:原forward函数仅支持单个样本,但trainer直接传入整个批量样本列表,导致维度不匹配触发索引越界。
  2. 棋盘解析异常:原board_parser未处理棋盘行中的空格,导致解析后的特征维度不是64(8x8棋盘),与权重矩阵维度不兼容。
  3. 输出目标未数值化:Checkmate字段为字符串类型,无法参与神经网络数值计算。
  4. 权重更新索引错误:update函数中直接使用列表alpha_calcul_hidden进行计算,未取对应索引值,引发类型与索引错误。
  5. 缺失激活函数:原代码未定义activation_function和activation_function_der,运行时会触发未定义错误。

修复要点

  1. 调整训练逻辑:在trainer中遍历每个样本执行前向/反向传播,实现随机梯度下降。
  2. 规范棋盘解析:移除棋盘行中的空格,确保每个样本特征维度为64,并过滤异常数据。
  3. 数值化输出目标:将Checkmate字符串转换为1.0/0.0的数值类型。
  4. 修正权重更新:在update中使用alpha_calcul_hidden[j]对应索引进行权重更新。
  5. 补充激活函数:添加sigmoid激活函数及其导数,适配神经网络的数值计算需求。

内容的提问来源于stack exchange,提问作者Mikael ANAGO

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 04:12:33