Análise de xadrez com informações limitadas

19

Nesse desafio, você recebe uma quantidade limitada de informações sobre um determinado jogo de xadrez e precisa prever quem ganhou o jogo .

Você recebe dois conjuntos de dados:

  1. Contagem de peças (que peças ainda estão vivas)
  2. Cores do quadro (a cor das peças no quadro)

Mais importante, você não sabe onde as peças estão localizadas . Você precisa determinar quem você acha que vai ganhar.

Os jogos são selecionados de todos os eventos listados no PGNMentor de 2010 até agora. Selecionei 10% de todas as posições do tabuleiro de cada jogo que termina em uma vitória ou uma derrota. As posições no tabuleiro sempre terão pelo menos 30 jogadas no jogo. Os casos de teste podem ser encontrados aqui . (Vitórias brancas são listadas primeiro, seguidas por vitórias pretas)

Entrada

A contagem de peças será uma sequência de caracteres para cada peça: king, queen r, ok n, bkight, ishop ou pawn. Minúsculas significa preto, maiúsculas é branco. O quadro terá uma sequência de 64 caracteres (8 linhas por 8 colunas). Brepresenta uma peça preta, Wrepresenta uma peça branca e .representa um ponto vazio. Amostra:

W..WB......W.BB....W..B..W.WWBBB..W...B....W..BBWWW...BB.W....B.,BBKPPPPPPPQRRbbkpppppppqrr

representaria o seguinte quadro

...B.BB.
.BBBBBBB
.B.B....
B..W....
WWWW.W..
....W.W.
...W..WW
W.....W.

e onde ambas as cores têm 2 Bispos, 1 Rei, 7 Peões, 1 Rainha, 2 Torres

Resultado

Você precisa retornar um número de ponto flutuante entre 0 e 1 (inclusive) para determinar a probabilidade de vitória das brancas. Amostra:

0.3     (30% chance that white wins)

Mais detalhes:

  • Cada caso de teste vale 1 ponto. Sua pontuação será 1 - (1-Output)^2se o branco vencer ou 1 - (Output)^2se o preto vencer.
  • Sua pontuação final será a soma de todos os casos de teste.
  • Se sinto que os envios estão codificando a entrada, reservo-me o direito de alterar os casos de teste. (Se eu alterá-los, eles terão o hash SHA-256 893be4425529f40bb9a0a7632f7a268a087ea00b0eb68293d6c599c6c671cdee)
  • Seu programa deve executar casos de teste de forma independente. Nenhuma informação de salvamento de um caso de teste para o próximo.
  • Se você estiver usando aprendizado de máquina, recomendo o treinamento nos primeiros 80% dos dados e o teste nos 20% restantes . (Ou qualquer porcentagem usada). Uso os jogos várias vezes nos dados, mas montei os mesmos jogos sequencialmente.
  • ATUALIZAÇÃO: adicionei mais de um milhão de casos de teste para fins de teste e aprendizado. Eles são divididos em partes em preto e branco devido aos limites de tamanho de repositório do github.

Boa sorte e divirta-se!

Nathan Merrill
fonte
Esta conversa foi movida para o bate-papo .
Dennis
Os novos casos de teste contêm os antigos ou os dois conjuntos são disjuntos?
Fatalize 29/03
Eu não faço ideia. Comprei-os em sites diferentes, por isso é possível que ambos incluíssem o mesmo conjunto de jogos.
Nathan Merrill

Respostas:

8

Java 8 + Weka, 6413 pontos, 94,5%

Esta resposta usa uma abordagem de aprendizado de máquina. Você precisa recuperar a biblioteca Weka , principalmente weka.jare PackageManager.jar.

Aqui, eu uso um perceptron multicamada como classificador; você pode substituir mlppor qualquer Classifierclasse de Weka para comparar resultados.

Não brinquei muito com os parâmetros do MLP e simplesmente olhei para eles (uma camada oculta de 50 neurônios, 100 épocas, 0,2 taxa de aprendizado, 0,1 momento).

Eu limito o valor de saída do MLP, para que a saída seja realmente 1 ou 0, conforme definido no desafio. Dessa forma, o número de instâncias classificadas corretamente, impressas por Weka, é diretamente nossa pontuação.

Construção de vetor de recurso

Viro cada instância de uma string para um vetor de 76 elementos, em que:

  • Os primeiros 64 elementos representam as células do quadro, na mesma ordem em que a sequência, onde 1é uma peça branca, -1é uma peça preta e 0é uma célula vazia.
  • Os últimos 12 elementos representam cada tipo de peça (6 por jogador); o valor desses elementos é o número de peças desse tipo no quadro ( 0sendo "nenhuma peça desse tipo"). Pode-se aplicar a normalização para reajustar esses valores entre -1 e 1, mas isso provavelmente não é muito útil aqui.

Número de instâncias de treinamento

Se eu usar todos os casos de teste fornecidos para treinar meu classificador, consegui obter 6694 (ou seja, 98,6588%) instâncias classificadas corretamente . Obviamente, isso não é surpreendente, porque testar um modelo com os mesmos dados que você usou para treiná-lo é muito fácil (porque, nesse caso, é realmente bom que o modelo se ajuste demais).

Usando um subconjunto aleatório de 80% das instâncias como dados de treinamento, obtemos a figura de 6413 (ou seja, 94,5173%) instâncias classificadas corretamente relatadas no cabeçalho (é claro que, como o subconjunto é aleatório, você pode obter resultados ligeiramente diferentes). Estou confiante de que o modelo funcionaria decentemente bem em novos dados, porque o teste nos 20% restantes das instâncias (que não foram usadas para treinamento) fornece 77,0818% de classificação correta, o que mostra que os modelos generalizam decentemente bem (assumindo a os casos que recebemos aqui são representativos dos novos casos de teste que receberíamos).

Usando metade das instâncias para treinamento e a outra metade para teste, obtemos 86,7502% nos dados de treinamento e teste e 74,4988% apenas nos dados de teste.

Implementação

Como eu disse, esse código requer weka.jare PackageManager.jarda Weka.

Pode-se controlar a porcentagem de dados usados ​​no conjunto de treinamento TRAIN_PERCENTAGE.

Os parâmetros do MLP podem ser alterados logo abaixo TRAIN_PERCENTAGE. Pode-se tentar outros classificadores de Weka (por exemplo, SMOpara SVMs) simplesmente substituindo mlppor outro classificador.

Esse programa imprime em conjuntos de resultados, sendo o primeiro em todo o conjunto (incluindo os dados usados ​​para treinamento), que é a pontuação definida neste desafio, e o segundo sendo apenas nos dados que não foram usados ​​para treinamento.

Um insere os dados passando o caminho do arquivo que os contém como argumento para o programa.

import weka.classifiers.Classifier;
import weka.classifiers.Evaluation;
import weka.classifiers.functions.MultilayerPerceptron;
import weka.core.Attribute;
import weka.core.DenseInstance;
import weka.core.Instance;
import weka.core.Instances;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;

public class Test {

    public static void main(String[] arg) {

        final double TRAIN_PERCENTAGE = 0.5;

        final String HIDDEN_LAYERS = "50";
        final int NB_EPOCHS = 100;
        final double LEARNING_RATE = 0.2;
        final double MOMENTUM = 0.1;

        Instances instances = parseInstances(arg[0]);
        instances.randomize(new java.util.Random(0));
        Instances trainingSet = new Instances(instances, 0, (int) Math.floor(instances.size() * TRAIN_PERCENTAGE));
        Instances testingSet = new Instances(instances, (int) Math.ceil(instances.size() * TRAIN_PERCENTAGE), (instances.size() - (int) Math.ceil(instances.size() * TRAIN_PERCENTAGE)));

        Classifier mlp = new MultilayerPerceptron();
        ((MultilayerPerceptron) mlp).setHiddenLayers(HIDDEN_LAYERS);
        ((MultilayerPerceptron) mlp).setTrainingTime(NB_EPOCHS);
        ((MultilayerPerceptron) mlp).setLearningRate(LEARNING_RATE);
        ((MultilayerPerceptron) mlp).setMomentum(MOMENTUM);


        try {
            // Training phase
            mlp.buildClassifier(trainingSet);
            // Test phase
            System.out.println("### CHALLENGE SCORE ###");
            Evaluation test = new Evaluation(trainingSet);
            test.evaluateModel(mlp, instances);
            System.out.println(test.toSummaryString());
            System.out.println();
            System.out.println("### TEST SET SCORE ###");
            Evaluation test2 = new Evaluation(trainingSet);
            test2.evaluateModel(mlp, testingSet);
            System.out.println(test2.toSummaryString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Instances parseInstances(String filePath) {
        ArrayList<Attribute> attrs = new ArrayList<>(); // Instances constructor only accepts ArrayList
        for(int i = 0 ; i < 76 ; i++) {
            attrs.add(new Attribute("a" + String.valueOf(i)));
        }
        attrs.add(new Attribute("winner", new ArrayList<String>(){{this.add("white");this.add("black");}}));
        Instances instances = new Instances("Rel", attrs, 10);
        instances.setClassIndex(76);

        try {
            BufferedReader r = new BufferedReader(new FileReader(filePath));
            String line;
            String winner = "white";
            while((line = r.readLine()) != null) {
                if(line.equals("White:")) {
                    winner = "white";
                } else if(line.equals("Black:")) {
                    winner = "black";
                } else {
                    Instance instance = new DenseInstance(77);
                    instance.setValue(attrs.get(76), winner);
                    String[] values = line.split(",");
                    for(int i = 0 ; i < values[0].length() ; i++) {
                        if(values[0].charAt(i) == 'B') {
                            instance.setValue(attrs.get(i), -1);
                        } else if(values[0].charAt(i) == 'W') {
                            instance.setValue(attrs.get(i), 1);
                        } else {
                            instance.setValue(attrs.get(i), 0);
                        }
                    }
                    // Ugly as hell
                    instance.setValue(attrs.get(64), values[1].length() - values[1].replace("k", "").length());
                    instance.setValue(attrs.get(65), values[1].length() - values[1].replace("q", "").length());
                    instance.setValue(attrs.get(66), values[1].length() - values[1].replace("r", "").length());
                    instance.setValue(attrs.get(67), values[1].length() - values[1].replace("n", "").length());
                    instance.setValue(attrs.get(68), values[1].length() - values[1].replace("b", "").length());
                    instance.setValue(attrs.get(69), values[1].length() - values[1].replace("p", "").length());
                    instance.setValue(attrs.get(70), values[1].length() - values[1].replace("K", "").length());
                    instance.setValue(attrs.get(71), values[1].length() - values[1].replace("Q", "").length());
                    instance.setValue(attrs.get(72), values[1].length() - values[1].replace("R", "").length());
                    instance.setValue(attrs.get(73), values[1].length() - values[1].replace("N", "").length());
                    instance.setValue(attrs.get(74), values[1].length() - values[1].replace("B", "").length());
                    instance.setValue(attrs.get(75), values[1].length() - values[1].replace("P", "").length());

                    instances.add(instance);
                }
            }
        } catch (Exception e) { // who cares
            e.printStackTrace();
        }
        return instances;
    }
}
Fatalizar
fonte
Como você codifica a entrada?
22417 Nathan Merrill
@NathanMerrill eu não tenho certeza se entendi sua pergunta
Fatalize
Como você está passando o caso de teste como entrada para a rede neural? Você está apenas passando a string bruta?
Nathan Merrill
@NathanMerrill Editado com um parágrafo na construção do vetor de recursos.
Fatalize 22/03
Como a weka sabe que você está tentando prever o vencedor?
user1502040
8

GNU sed + bc, 4336 5074,5 pontos, 64 75%

Atualização: o OP forneceu uma nova maneira de calcular a pontuação da previsão para um caso de teste individual. Usando Wolfram Alpha , plotei os dois conjuntos de fórmulas para ver as diferenças.

A maneira atual traz um forte incentivo para gerar probabilidades reais, e não apenas os extremos 0 e 1, para os quais as novas fórmulas dão a mesma pontuação máxima de antes. É por isso que o algoritmo inalterado abaixo agora tem uma melhor taxa de previsão, de fato uma grande taxa, dada sua simplicidade.

No entanto, também há uma desvantagem associada às novas fórmulas, conforme explicado em 'Editar 1'.


Essa é uma estimativa simples baseada apenas na vantagem / desvantagem do material, ignorando a colocação real das peças. Fiquei curioso para saber como isso funcionaria. A razão pela qual uso sed, e não uma linguagem que possa fazer isso em uma linha, é porque é a minha linguagem esotérica favorita.

/:/d                                             # delete the two headers
s:.*,::                                          # delete board positions
s:$:;Q9,R5,B3,N3,P1,K0,q-9,r-5,b-3,n-3,p-1,k-0:  # add relative piece value table
:r                                               # begin replacement loop
s:([a-Z])((.*)\1([^,]+)):\4+\2:                  # table lookup: letter-value repl.
tr                                               # repeat till last piece
s:;.*::                                          # delete value table
s:.*:echo '&0'|bc:e                              # get material difference: bc call
/^0$/c0.5                                        # print potential draw score
/-/c0                                            # print potential black win score
c1                                               # print potential white win score

Valores padrão da peça usados:

  • 9 - Rainha
  • 5 - Torre
  • 3 - Cavaleiro
  • 3 - Bispo
  • 1 - Peão
  • 0 - Rei

Calculo o material dos dois lados e subtraio o material preto do branco. A saída para cada caso de teste é baseada nessa diferença da seguinte maneira:

  • se diferença> 0, saída = 1 (potencial vitória branca)
  • se diferença = 0, saída = 0,5 (consumo potencial).

Esta é a minha única saída fracionada, daí o motivo da melhoria, conforme explicado acima.

  • se diferença <0, saída = 0 (vitória potencial em preto)

A taxa de previsão para este método foi de 64%. Agora são 75% com as novas fórmulas.

Inicialmente, esperava que fosse maior, digamos 70%, mas, como jogador de xadrez, consigo entender o resultado, já que perdi muitos jogos quando tinha +1 / +2 e venci muitos quando estava em baixo. material. É tudo sobre a posição real. (Bem, agora eu tenho meu desejo!)

Editar 1: a desvantagem

A solução trivial é produzir 0,5 para cada caso de teste, pois dessa forma você pontuou meio ponto, independentemente de quem ganhou. Para nossos casos de teste, isso significou uma pontuação total de 3392,5 pontos (50%).

Mas com as novas fórmulas, 0,5 (que é um resultado que você daria se não souber quem vence) é convertido em 0,75 pontos. Lembre-se de que a pontuação máxima que você pode receber para um caso de teste é 1, para 100% de confiança no vencedor. Assim, a nova pontuação total para uma saída constante de 0,5 é 5088,75 pontos, ou 75%! Na minha opinião, o incentivo é muito forte para este caso.

Essa pontuação é melhor, embora marginalmente, do que o meu algoritmo baseado em vantagens materiais. A razão para isso é porque o algoritmo fornece uma probabilidade de 1 ou 0 (sem incentivo), vitórias ou perdas presumidas, mais vezes (3831) do que 0,5 (incentivo), empates assumidos (2954). O método é simples no final e, como tal, não possui uma alta porcentagem de respostas corretas. O aumento da nova fórmula para constante 0,5 consegue atingir artificialmente essa porcentagem.

Edição 2:

É um fato conhecido, mencionado nos livros de xadrez, que geralmente é melhor ter um par de bispos do que um par de cavaleiros. Isso é especialmente verdadeiro no estágio intermediário ao final do jogo, onde estão os casos de teste, pois é mais provável que haja uma posição aberta onde o alcance de um bispo é aumentado.

Por isso, fiz um segundo teste, mas desta vez substituí o valor dos bispos de 3 para 3,5. O valor do cavaleiro permaneceu 3. Essa é uma preferência pessoal, por isso não fiz a minha submissão padrão. A pontuação total neste caso foi de 4411 pontos (65%). Apenas um aumento de 1 ponto percentual foi observado.

Com as novas fórmulas, a pontuação total é de 4835 pontos (71%). Agora, o bispo ponderado tem um desempenho inferior. Mas, o efeito é explicado porque o método ponderado agora oferece ainda mais vezes ganhos ou perdas presumidos (5089) do que empates assumidos (1696).

seshoumara
fonte
11
+1 por fornecer uma solução de linha de base razoável. Eu também estava me perguntando o quão bem isso seria.
Martin Ender
@MartinEnder Obrigado. Minha ideia de aumentar o valor do bispo, mencionada na última vez, produziu apenas um aumento na taxa de sucesso de 1% (consulte a Atualização 2). Eu acho que os valores padrão incluíram esse efeito, afinal.
seshoumara
Ei, de acordo com o comentário de xnor, você se importaria se eu mudasse a pontuação para ser a diferença absoluta ao quadrado?
Nathan Merrill
11
Impressionante. Além disso, obrigado por responder! Eu sempre me preocupo que minhas perguntas mais difíceis nunca recebam resposta.
Nathan Merrill
@NathanMerrill Atualizei minha resposta para usar a nova pontuação, conforme solicitado. Desculpe pela longa análise, mas fiquei realmente curioso.
seshoumara
4

Python 3 - 84,6%, 5275 pontos em um conjunto de validação

Se enganarmos e usarmos todos os dados, podemos alcançar uma precisão de 99,3% e uma pontuação de 6408

Apenas um MLP grande e simples com desistência usando Keras

import collections
import numpy as np
import random

import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras.layers.noise import GaussianDropout
from keras.optimizers import Adam

np.random.seed(0)
random.seed(0)

def load_data():
    with open('test_cases.txt', 'r') as f:
        for line in f:
            yield line.split(',')

def parse_data(rows):
    black_pieces = "kpbkrq"
    white_pieces = black_pieces.upper()
    for i, row in enumerate(rows):
        if len(row) >= 2:
            board = row[0]
            board = np.array([1 if c == 'W' else -1 if c == 'B' else 0 for c in board], dtype=np.float32)
            pieces = row[1]
            counts = collections.Counter(pieces)
            white_counts = np.array([counts[c] for c in white_pieces], dtype=np.float32)
            black_counts = np.array([counts[c] for c in black_pieces], dtype=np.float32)
            yield (outcome, white_counts, black_counts, board)
        else:
            if 'White' in row[0]:
                outcome = 1
            else:
                outcome = 0

data = list(parse_data(load_data()))
random.shuffle(data)
data = list(zip(*data))
y = np.array(data[0])
x = list(zip(*data[1:]))
x = np.array([np.concatenate(xi) for xi in x])

i = len(y) // 10

x_test, x_train = x[:i], x[i:]
y_test, y_train = y[:i], y[i:]

model = Sequential()
model.add(Dense(512, activation='relu', input_shape=(76,)))
model.add(GaussianDropout(0.5))
model.add(Dense(512, activation='relu'))
model.add(GaussianDropout(0.5))
model.add(Dense(512, activation='relu'))
model.add(GaussianDropout(0.5))
model.add(Dense(1, activation='sigmoid'))

model.compile(loss='mean_squared_error', optimizer=Adam())

use_all_data = False

x_valid, y_valid = x_test, y_test

if use_all_data:
    x_train, y_train = x_test, y_test = x, y
    validation_data=None
else:
    validation_data=(x_test, y_test)

batch_size = 128

history = model.fit(x_train, y_train, batch_size=batch_size, epochs=50, verbose=1, validation_data=validation_data)

y_pred = model.predict_on_batch(x_test).flatten()
y_class = np.round(y_pred)
print("accuracy: ", np.sum(y_class == y_test) / len(y_test))

score = np.sum((y_pred - (1 - y_test)) ** 2) * (len(y) / len(y_test))
print("score: ", score)
user1502040
fonte
Quantos dados você usa no treinamento para obter o valor de 84,6%?
Fatalize 22/03
Eu usei uma divisão de
90 a
Hey, eu adicionei uma tonelada mais casos de teste se você estiver interessado.
Nathan Merrill
2

Python 3 - precisão de 94,3%, 6447 pontos em um conjunto de validação de 20% dos dados

Usa 3 redes neurais, um regressor de vizinhos mais próximos, uma floresta aleatória e aumento de gradiente. Essas previsões são combinadas com uma floresta aleatória que também tem acesso aos dados.

import collections
import numpy as np
import numpy.ma as ma
import random

import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, BatchNormalization, Activation, Conv2D, Flatten
from keras.layers.noise import GaussianDropout
from keras.callbacks import EarlyStopping
from keras.optimizers import Adam
from sklearn.neighbors import KNeighborsRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.ensemble import GradientBoostingRegressor
import tensorflow

tensorflow.set_random_seed(1)
np.random.seed(1)
random.seed(1)

def load_data():
    with open('test_cases.txt', 'r') as f:
        for line in f:
            yield line.split(',')

def parse_data(rows):
    black_pieces = "kqrnbp"
    white_pieces = black_pieces.upper()
    for i, row in enumerate(rows):
        if len(row) >= 2:
            board = row[0]
            board = np.array([1 if c == 'W' else -1 if c == 'B' else 0 for c in board], dtype=np.float32)
            pieces = row[1]
            counts = collections.Counter(pieces)
            white_counts = np.array([counts[c] for c in white_pieces], dtype=np.float32)
            black_counts = np.array([counts[c] for c in black_pieces], dtype=np.float32)
            yield (outcome, white_counts, black_counts, board)
        else:
            if 'White' in row[0]:
                outcome = 1
            else:
                outcome = 0

data = list(parse_data(load_data()))
random.shuffle(data)
data = list(zip(*data))
y = np.array(data[0])
x = list(zip(*data[1:]))
conv_x = []
for white_counts, black_counts, board in x:
    board = board.reshape((1, 8, 8))
    white_board = board > 0
    black_board = board < 0
    counts = [white_counts, black_counts]
    for i, c in enumerate(counts):
        n = c.shape[0]
        counts[i] = np.tile(c, 64).reshape(n, 8, 8)
    features = np.concatenate([white_board, black_board] + counts, axis=0)
    conv_x.append(features)
conv_x = np.array(conv_x)
x = np.array([np.concatenate(xi) for xi in x])
s = x.std(axis=0)
u = x.mean(axis=0)
nz = s != 0
x = x[:,nz]
u = u[nz]
s = s[nz]
x = (x - u) / s

i = 2 * len(y) // 10

x_test, x_train = x[:i], x[i:]
conv_x_test, conv_x_train = conv_x[:i], conv_x[i:]
y_test, y_train = y[:i], y[i:]

model = Sequential()

def conv(n, w=3, shape=None):
    if shape is None:
        model.add(Conv2D(n, w, padding="same"))
    else:
        model.add(Conv2D(n, w, padding="same", input_shape=shape))
    model.add(BatchNormalization())
    model.add(Activation('relu'))

conv(128, shape=conv_x[0].shape) 
conv(128)
conv(128)
conv(128)
conv(128)
conv(128)
conv(128)
conv(128)
conv(128)
conv(128)
conv(2, w=1)
model.add(Flatten())
model.add(GaussianDropout(0.5))
model.add(Dense(256))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(GaussianDropout(0.5))
model.add(Dense(1))
model.add(BatchNormalization())
model.add(Activation('sigmoid'))

model.compile(loss='mse', optimizer=Adam())

model5 = model

model = Sequential()
model.add(Dense(50, input_shape=(x.shape[1],)))
model.add(Activation('sigmoid'))
model.add(Dense(1))
model.add(Activation('sigmoid'))

model.compile(loss='mse', optimizer=Adam())

model0 = model

model = Sequential()
model.add(Dense(1024, input_shape=(x.shape[1],)))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(GaussianDropout(0.5))
model.add(Dense(1024))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(GaussianDropout(0.5))
model.add(Dense(1024))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(GaussianDropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))

model.compile(loss='mse', optimizer=Adam())

model4 = model

use_all_data = False

x_valid, y_valid = x_test, y_test

if use_all_data:
    x_train, y_train = x_test, y_test = x, y
    validation_data=None
else:
    validation_data=(x_test, y_test)

def subsample(x, y, p=0.9, keep_rest=False):
    m = np.random.binomial(1, p, size=len(y)).astype(np.bool)
    r = (x[m,:], y[m])
    if not keep_rest:
        return r
    m = ~m
    return r + (x[m,:], y[m])

epochs=100

x0, y0, x_valid, y_valid = subsample(conv_x_train, y_train, keep_rest=True)
model5.fit(x0, y0, epochs=epochs, verbose=1, validation_data=(x_valid, y_valid), callbacks=[EarlyStopping(patience=1)])

x0, y0, x_valid, y_valid = subsample(x_train, y_train, keep_rest=True)
model0.fit(x0, y0, epochs=epochs, verbose=1, validation_data=(x_valid, y_valid), callbacks=[EarlyStopping(patience=1)])

x0, y0, x_valid, y_valid = subsample(x_train, y_train, keep_rest=True)
model4.fit(x0, y0, epochs=epochs, verbose=1, validation_data=(x_valid, y_valid), callbacks=[EarlyStopping(patience=1)])

model1 = RandomForestRegressor(n_estimators=400, n_jobs=-1, verbose=1)
model1.fit(*subsample(x_train, y_train))

model2 = GradientBoostingRegressor(learning_rate=0.2, n_estimators=5000, verbose=1)
model2.fit(*subsample(x_train, y_train))

model3 = KNeighborsRegressor(n_neighbors=2, weights='distance', p=1)
model3.fit(*subsample(x_train, y_train))

models = (model0, model1, model2, model3, model4, model5)

model_names = [
    "shallow neural net",
    "random forest",
    "gradient boosting",
    "k-nearest neighbors",
    "deep neural net",
    "conv-net",
    "ensemble"
]

def combine(predictions):
    clip = lambda x: np.clip(x, 0, 1)
    return clip(np.array([y.flatten() for y in predictions]).T)

def augment(x, conv_x):
    p = combine([m.predict(x) for m in models[:-1]] + [models[-1].predict(conv_x)])
    return np.concatenate((x, p), axis=1)

model = RandomForestRegressor(n_estimators=200, n_jobs=-1, verbose=1)
model.fit(augment(x_train, conv_x_train), y_train)

def accuracy(prediction):
    class_prediction = np.where(prediction > 0.5, 1, 0)
    return np.sum(class_prediction == y_test) / len(y_test)

predictions = [m.predict(x_test).flatten() for m in models[:-1]] + [models[-1].predict(conv_x_test).flatten()]+ [model.predict(augment(x_test, conv_x_test))]

for s, p in zip(model_names, predictions):
    print(s + " accuracy: ", accuracy(p))

def evaluate(prediction):
    return np.sum(1 - (prediction - y_test) ** 2) * (len(y) / len(y_test))

for s, p in zip(model_names, predictions):
    print(s + " score: ", evaluate(p))
user1502040
fonte
Hey, eu adicionei uma tonelada mais casos de teste se você estiver interessado.
Nathan Merrill
Woah, você saiu nisso.
Robert Fraser
Observe a resposta java aqui que "bate" a sua parece relatar% em todo o conjunto de dados e obtém apenas 77% nos dados com os quais não treinou.
Robert Fraser
0

Python 3 - 4353,25 / 6785 pontos - 64%

Então, eu trabalhei nisso principalmente ontem. Meu primeiro post de golfe, e só uso python há uma semana, então me perdoe se nem tudo estiver otimizado.

def GetWhiteWinPercent(a):
finalWhiteWinPercent=0
i=a.index(',')

#position
board=a[:i]
blackBoardScore=0
whiteBoardScore=0
for r in range(i):
    if board[r] == 'B': blackBoardScore += abs(7 - (r % 8))
    if board[r] == 'W': whiteBoardScore += r % 8
if   whiteBoardScore > blackBoardScore: finalWhiteWinPercent += .5
elif whiteBoardScore < blackBoardScore: finalWhiteWinPercent += .0
else: finalWhiteWinPercent+=.25

#pieces
pieces=a[i:]
s = {'q':-9,'r':-5,'n':-3,'b':-3,'p':-1,'Q':9,'R':5,'N':3,'B':3,'P':1}
pieceScore = sum([s.get(z) for z in pieces if s.get(z) != None])
if   pieceScore < 0: finalWhiteWinPercent += 0
elif pieceScore > 0: finalWhiteWinPercent += .5
else: finalWhiteWinPercent += .25

return finalWhiteWinPercent

Acabei no mesmo caminho que a resposta de seshoumara para começar. Mas o grande número de casos de teste que tinham contagens de peças me deixou insatisfeito.

Por isso, pesquisei no Google traços que determinam quem está ganhando no xadrez (eu não jogo) e notei que a posição do tabuleiro, especificamente o controle central, é grande. É aí que entra essa parte.

for r in range(i):
    if board[r] == 'B': blackBoardScore += abs(7 - (r % 8))
    if board[r] == 'W': whiteBoardScore += r % 8
if   whiteBoardScore > blackBoardScore: finalWhiteWinPercent += .5
elif whiteBoardScore < blackBoardScore: finalWhiteWinPercent += .0
else: finalWhiteWinPercent+=.25

As duas metades combinadas são usadas para encontrar a pontuação (0,0, 0,25, 0,50, 0,75, 1,0)

Muito interessante que essa posição extra no tabuleiro não pareça aumentar a chance de adivinhar o vencedor.

Se você soltar os casos de teste em alguns arquivos, aqui está o teste.

whiteWins=0
blackWins=0
totalWins=0
for line in open('testcases2.txt','r'):
    totalWins += 1
    blackWins += 1 - GetWhiteWinPercent(line)
for line in open('testcases.txt','r'):
    totalWins += 1
    whiteWins += GetWhiteWinPercent(line)

print(str(whiteWins+blackWins) +'/'+str(totalWins))

Sei que este não é um desafio de golfe, mas qualquer dica ou conselho a esse respeito é apreciado!

Datastream
fonte
Minha resposta? Você quer dizer a resposta de seshoumara? Além disso, você não precisa jogar golfe (a menos que queira). Este não é um desafio do código-golfe .
Nathan Merrill
Você pode salvar muitos bytes usando apenas nomes de variáveis ​​de um caractere. (Embora ele realmente não importa, porque isso não é o código-golf)
HyperNeutrino
Woops! Editando isso agora. No trabalho, é isso que recebo por skimming!
Datastream 22/03
2
Por favor, não jogue golfe. É melhor manter o código legível quando não é um código de golfe.
mbomb007
O controle do meio do tabuleiro não significa ocupar o meio do tabuleiro, mas atacar o meio do tabuleiro. Se você quiser adicionar um pouco de complexidade, isso pode melhorar sua pontuação.
Não que Charles