Hola, soy Miguel y esta vez les traigo otro artículo.
Índice
Comprender cómo se pueden usar las redes neuronales para resolver problemas indirectamente
En la mayoría de los motores de ajedrez, un algoritmo de búsqueda junto con una función heurística le da a la IA del ajedrez la idea principal de los mejores movimientos para jugar. La mayor parte de la programación y la mayor parte del «cerebro» detrás de esto es la función heurística.
¿Qué quiero decir con función heurística? La función heurística se refiere a una función que toma ciertas medidas del tablero de ajedrez, le da a cada medida un cierto peso y finalmente calcula un valor numérico de la ventaja de cada jugador. La función heurística en el ajedrez generalmente considera cosas básicas como la estructura de peones, el control del centro y la seguridad del Rey, pero también puede incluir cálculos más complejos como el tempo y oportunidades para emplear diferentes tácticas.
Un jugador de ajedrez experimentado con una competencia decente en programación podría construir una buena función heurística. Desafortunadamente, no soy un jugador de ajedrez tan experimentado. Decidí usar una red neuronal para crear una función heurística para mí.
Concepto
El concepto del programa utiliza una red neuronal para evaluar la placa, que luego se ajusta con el algoritmo de búsqueda, que verifica todas las posiciones futuras y encuentra el valor más alto, una especie de árbol mínimo-máximo.
Programa
Paso 1- Acceder a los datos
from pandas import read_csv import numpy as np import chess import os df = read_csv('C:\Users\v_sim\Desktop\Files\Data\chess.csv') df = df[df['winner']!='draw'] moves = df['moves'].values[:100] winner = df['winner'].values X = [] y = []
Este script extrae el archivo csv de mi computadora e importa las importaciones necesarias para que el programa funcione. Pandas es para la extracción de datos csv, numpy para la manipulación de matrices, ajedrez para un tablero de ajedrez ya hecho y listas vacías para representar los valores X e y de la red.
Paso 2- Requisitos previos de datos
def make_matrix(board): pgn = board.epd() foo = [] pieces = pgn.split(" ", 1)[0] rows = pieces.split("/") for row in rows: foo2 = [] for thing in row: if thing.isdigit(): for i in range(0, int(thing)): foo2.append('.') else: foo2.append(thing) foo.append(foo2) return foodef translate(matrix,chess_dict): rows = [] for row in matrix: terms = [] for term in row: terms.append(chess_dict[term]) rows.append(terms) return rowschess_dict = { 'p' : [1,0,0,0,0,0,0,0,0,0,0,0], 'P' : [0,0,0,0,0,0,1,0,0,0,0,0], 'n' : [0,1,0,0,0,0,0,0,0,0,0,0], 'N' : [0,0,0,0,0,0,0,1,0,0,0,0], 'b' : [0,0,1,0,0,0,0,0,0,0,0,0], 'B' : [0,0,0,0,0,0,0,0,1,0,0,0], 'r' : [0,0,0,1,0,0,0,0,0,0,0,0], 'R' : [0,0,0,0,0,0,0,0,0,1,0,0], 'q' : [0,0,0,0,1,0,0,0,0,0,0,0], 'Q' : [0,0,0,0,0,0,0,0,0,0,1,0], 'k' : [0,0,0,0,0,1,0,0,0,0,0,0], 'K' : [0,0,0,0,0,0,0,0,0,0,0,1], '.' : [0,0,0,0,0,0,0,0,0,0,0,0], }
Este script es capaz de transformar el tablero de la clase en un tablero de ajedrez codificado en un solo uso. Esto se hace primero accediendo a la forma epd del tablero, luego dividiéndolo en filas y finalmente traduciendo todos los cuadrados usando un diccionario.
Paso 3- Crear conjunto de datos
for game in moves: index = list(moves).index(game) all_moves = game.split() total_moves = len(all_moves) if winner[index] == 'black': game_winner = -1 else: game_winner = 1 board = chess.Board() for i in range(len(all_moves)): board.push_san(all_moves[i]) value = game_winner * (i/total_moves) matrix = make_matrix(board.copy()) rows = translate(matrix,chess_dict) X.append([rows]) y.append(value) X = np.array(X).reshape(len(X),8,8,12) y = np.array(y) X.shape
Lo crea o no, aquí es donde ocurre toda la magia. En lugar de utilizar la estructura y el desarrollo de peones para calcular la ventaja de un jugador, la red neuronal saltará directamente al jaque mate.
El conjunto de datos que he cargado incluye más de 14.000 partidas de ajedrez, dando información detallada sobre las aperturas y los movimientos jugados. Los datos que sintetizaré para la red intentarán calcular el valor heurístico, aprendiendo el valor “¿Cuántos movimientos más hasta el jaque mate para el jugador X?”. Aprende este patrón a través de miles de juegos hasta que tiene una sólida comprensión de la ventaja del jugador.
Paso 4- Inicializar la red neuronal
from keras import callbacks, optimizers from keras.layers import (LSTM, BatchNormalization, Dense, Dropout, Flatten, TimeDistributed) from keras.layers.convolutional import Conv2D, MaxPooling2D from keras.models import Sequential, load_model, model_from_json model = Sequential() model.add(Conv2D(filters=64, kernel_size=1, activation='relu', input_shape=(8,8,12))) model.add(MaxPooling2D()) model.add(Conv2D(filters=24, kernel_size=1, activation='relu')) model.add(MaxPooling2D()) model.add(Conv2D(filters=10, kernel_size=1, activation='relu')) model.add(Flatten()) model.add(BatchNormalization()) model.add(Dense(1,activation = 'tanh'))model.predict(X)
Esta es una red neuronal convolucional básica, además del uso de la tangente hiperbólica al final del ciclo de propagación hacia adelante. En este caso se utiliza la tangente hiperbólica, ya que los valores que se atribuyen a cada tablero pueden estar entre -1 y 1, lo que se ajusta perfectamente al rango de la tangente hiperbólica.
Paso 5- Entrenamiento de la red
from matplotlib import pyplot as plt model.compile(optimizer='Nadam', loss='mse') dirx = 'C:\\Users\\v_sim\\Desktop\\Files\\Programs\\ML\\Best Models' os.chdir(dirx) h5 = 'chess' + '_best_model' + '.h5' checkpoint = callbacks.ModelCheckpoint(h5, monitor='loss', verbose=0, save_best_only=True, save_weights_only=True, mode='auto', period=1) es = callbacks.EarlyStopping(monitor='loss', mode='min', verbose=1, patience=5000/10) callback = [checkpoint,es] json = 'chess' + '_best_model' + '.json' model_json = model.to_json() with open(json, "w") as json_file: json_file.write(model_json) print('Training Network...') history = model.fit(X,y,epochs = 1000,verbose = 2,callbacks = callback) plt.plot(history.history['loss'])
Esta es la configuración de entrenamiento simple para el aprendizaje automático con modelos secuenciales de keras.
Paso 6- Observe las evaluaciones
randint = np.random.randint(1,len(moves)) randint2 = np.random.randint(1,len(moves[randint].split())) board = chess.Board() for i in range(randint2): board.push_san(moves[randint].split()[i]) matrix = make_matrix(board.copy()) rows = translate(matrix,chess_dict) print('Board Evaluation:',model.predict([rows])[0][0]) board
Este paso es opcional, es solo para ver si las evaluaciones de su red son lógicas. Cuando la red neuronal se entrena con los parámetros de entrenamiento anteriores, estos son algunos de los resultados:
Esta evaluación es correcta. Las blancas están en gran desventaja, moviendo su dama blanca a una casilla en la que el caballo de c6 puede tomarla. Esto le da al negro una gran ventaja material.
Tenga en cuenta que la escala va entre -1 y 1, -1 es un jaque mate para las negras y 1 es un jaque mate para las blancas
La razón de este valor exacto no está clara, pero la red neuronal es una caja negra: no tiene reglas claras y definidas que siga para calcular este valor.
Esta evaluación muestra que el bot comprende el concepto de desarrollo y cómo el desarrollo puede conducir a jaque mate. Las blancas tienen el control absoluto del centro, con apoyo de torre a lo largo de la columna electrónica.
El caballo tiene un fuerte puesto de avanzada en el centro, mirando al peón en f7. Con el apoyo correcto, el ataque al peón de f7 podría resultar en una bifurcación en la dama y la roca. Además, ningún peón puede atacar la noche, a menos que el caballo en f6 se aleje.
Paso 7- Algoritmo de búsqueda profunda
import chess flatten = lambda l: [item for sublist in l for item in sublist] def search_positions(board,depth): #legal_moves = str(boards[depth][board].legal_moves) [38:-2].replace(',','').split() depth_list = [] for i in range(depth+1): depth_list.append([]) depth_list[0].append(board) for layer in depth_list: layer_set = [] try: stet = flatten(layer) except: stet = layer for i in range(len(stet)): legal_moves = str(stet[i].legal_moves) [38:-2].replace(',','').split() legal_moveset = [] for move in legal_moves: neo_board = stet[i].copy() neo_board.push_san(move) legal_moveset.append(neo_board) layer_set.append(legal_moveset) if depth_list.index(layer)+1 == len(depth_list): break depth_list[depth_list.index(layer)+1] = layer_set return depth_list boards = search_positions(chess.Board(),2)
No voy a profundizar demasiado en cómo funciona mi algoritmo de búsqueda profunda, pero simplemente usa los movimientos legales de la biblioteca python-chess y las funciones básicas de la lista para crear un árbol de movimientos posibles.
Como solo estoy usando una profundidad de 2, solo necesito evaluar el último conjunto de movimientos. Al considerar el peor escenario para cada movimiento, verifique cuál tiene el mejor escenario en el peor de los casos, que es por lo tanto el mejor movimiento en esta circunstancia.
Para evaluar el tablero sin usar otras complejas herramientas de iteración, se me ocurrió una aplicación interesante de la estructura «try:, except:» que se indexa en la siguiente lista, hasta que se encuentra un tablero de ajedrez:
def evaluate(lst): for i in range(len(lst)): try: matrix = make_matrix(lst[i]) rows = translate(matrix,chess_dict) lst[i] = model.predict([rows]) except: evaluate(lst[i]) return lstevaluation = evaluate(boards[-1])
Guay, ¿verdad?
maximum = 0 for term in evaluation: if np.mean(term) > maximum: maximum = np.mean(term) index = evaluation.index(term)
Conclusión
Este guión final le muestra el mejor movimiento para las circunstancias dadas.
Creo que este programa es un caso interesante, donde las redes neuronales no son el factor de resolución definitivo, sino un trampolín hacia la solución final del problema del aprendizaje automático. Creo que la gente subestima la capacidad de las redes neuronales para sortear obstáculos intermedios complejos que impiden alcanzar la meta.
Espero que más personas utilicen las redes neuronales como paso intermedio en proyectos más complicados.
Gracias por leer este artículo.
Añadir comentario