Вместо этого я пытался тренировать ее с помощью головоломок, но до сих пор не знаю, как и нужно ли это будет работать... Моя текущая реализация этого использует базу данных головоломок lichess... Кроме того, потому что я не знаю, следует ли мне добавлять дополнительную информацию в move_to_int или нет, потому что каждый раз, когда я это делал, я сталкивался с размером метки или ошибки размера тензора... если я не добавлю, на моем компьютере появится синяя сцена смерти, что, я думаю, связано с оперативной памятью, потому что у меня ее только 8, и хотя я использую только 20% ее для обучения ИИ...
Мой код тренировки следующий:
Загрузить данные:
Код: Выделить всё
LIMIT_OF_GAMES = 1000000
curr_games = 0
def load_pgn(file_path):
games = []
global curr_games
with open(file_path, "r") as pgn_file:
while True:
game = pgn.read_game(pgn_file)
if game is None:
break
games.append(game)
curr_games += 1
if curr_games >= LIMIT_OF_GAMES:
break
return games
files = [file for file in os.listdir("../Database") if file.endswith(".pgn")]
LIMIT_OF_FILES = min(len(files), 21)
games = []
i = 1
for file in tqdm(files):
games.extend(load_pgn(f"../Database/{file}"))
if i == LIMIT_OF_FILES or curr_games >= LIMIT_OF_GAMES:
break
i += 1
Код: Выделить всё
from auxiliary_functs import create_input_for_nn, encode_moves
X, y = create_input_for_nn(games)
print(f"Number of samples: {len(y)}")
y, move_to_int = encode_moves(y)
num_classes = len(move_to_int)
X = torch.tensor(X,dtype=torch.float32)
y = torch.tensor(y,dtype=torch.long)
Код: Выделить всё
from dataset import ChessDataset
from chess_model import ChessModel
# Create Dataset and DataLoader
dataset = ChessDataset(X, y)
dataloader = DataLoader(dataset, batch_size=64, shuffle=True)
# Check for GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f'Using device: {device}')
# Model Initialization
model = ChessModel(num_classes=num_classes).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.0001)
# Load the model state
if os.path.exists("../model/pytorch/pytorch/chess_model_pytorch.pth"):
model.load_state_dict(torch.load("../model/pytorch/chess_model_pytorch.pth"))
print("Loaded model from disk.")
# Load the optimizer state
if os.path.exists("../model/pytorch/chess_optimizer_pytorch.pth"):
optimizer.load_state_dict(torch.load("../model/pytorch/chess_optimizer_pytorch.pth"))
print("Loaded optimizer state from disk.")
Код: Выделить всё
import pickle
num_epochs = 50
for epoch in range(num_epochs):
start_time = time.time()
model.train()
running_loss = 0.0
for inputs, labels in tqdm(dataloader):
inputs, labels = inputs.to(device), labels.to(device) # move data to GPU
optimizer.zero_grad()
outputs = model(inputs) # Raw logits
# Compute loss
loss = criterion(outputs, labels)
loss.backward()
# Gradient clipping
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
running_loss += loss.item()
end_time = time.time()
epoch_time = end_time - start_time
minutes: int = int(epoch_time // 60)
seconds: int = int(epoch_time % 60)
print(f"Epoch {epoch+1}/{num_epochs} - Loss: {running_loss / len(dataloader):.4f} - Time: {minutes}m {seconds}s")
if not os.path.exists("../model/pytorch"):
os.makedirs("../model/pytorch")
torch.save(model.state_dict(), "../model/pytorch/chess_model_pytorch.pth")
torch.save(optimizer.state_dict(), "../model/pytorch/chess_optimizer_pytorch.pth")
with open("../model/pytorch/move_to_int", "wb") as file:
pickle.dump(move_to_int, file)
Код: Выделить всё
import torch.nn as nn
class ChessModel(nn.Module):
def __init__(self, num_classes):
super(ChessModel, self).__init__()
# conv1 -> relu -> conv2 -> relu -> flatten -> fc1 -> relu -> fc2
self.conv1 = nn.Conv2d(14, 64, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(64, 128, kernel_size=3, padding=1)
self.flatten = nn.Flatten()
self.fc1 = nn.Linear(8 * 8 * 128, 256)
self.fc2 = nn.Linear(256, num_classes)
self.relu = nn.ReLU()
# Initialize weights
nn.init.kaiming_uniform_(self.conv1.weight, nonlinearity='relu')
nn.init.kaiming_uniform_(self.conv2.weight, nonlinearity='relu')
nn.init.xavier_uniform_(self.fc1.weight)
nn.init.xavier_uniform_(self.fc2.weight)
def forward(self, x):
x = self.relu(self.conv1(x))
x = self.relu(self.conv2(x))
x = self.flatten(x)
x = self.relu(self.fc1(x))
x = self.fc2(x) # Output raw logits
return x
Код: Выделить всё
import numpy as np
from chess import Board
import os
import pickle
def board_to_matrix(board: Board):
"""
Converts a chess board to a matrix representation.
"""
# 8x8 board, 12 number of unique pieces
# 13th board for legal moves (where we can move)
# 14th board for legal moves from (where the piece is)
matrix = np.zeros((14, 8, 8))
piece_map = board.piece_map()
for square, piece in piece_map.items():
row, col = divmod(square, 8)
piece_type = piece.piece_type - 1
piece_color = 0 if piece.color else 6
matrix[piece_type + piece_color, row, col] = 1
legal_moves = board.legal_moves
for move in legal_moves:
to_square = move.to_square
from_square = move.from_square
row_to, col_to = divmod(to_square, 8)
row_from, col_from = divmod(from_square, 8)
matrix[12, row_to, col_to] = 1
matrix[13, row_from, col_from] = 1
return matrix
def create_input_for_nn(games):
X = []
y = []
for game in games:
board = game.board()
for move in game.mainline_moves():
X.append(board_to_matrix(board))
y.append(move.uci())
board.push(move)
return np.array(X, dtype=np.float32), np.array(y)
def encode_moves(moves):
move_to_int = {move: i for i, move in enumerate(set(moves))}
return (
np.array([move_to_int[move] for move in moves], dtype=np.float32),
move_to_int,
)
Подробнее здесь: https://stackoverflow.com/questions/789 ... -late-game