Analyse des échecs avec des informations limitées

19

Dans ce défi, vous recevez une quantité limitée d'informations sur un jeu d'échecs particulier et vous devez prédire qui a gagné le jeu .

Vous disposez de deux ensembles de données:

  1. Nombre de pièces (quelles pièces sont encore en vie)
  2. Couleurs du plateau (La couleur des pièces sur le plateau)

Plus important encore, vous ne savez pas où se trouvent les pièces . Vous devez déterminer qui, selon vous, va gagner.

Les jeux sont sélectionnés parmi tous les événements répertoriés sur PGNMentor de 2010 à maintenant. J'ai sélectionné 10% de toutes les positions du plateau de chaque jeu qui se termine par une victoire ou une perte. Les positions du conseil seront toujours au moins 30 coups dans le jeu. Les cas de test peuvent être trouvés ici . (Les victoires blanches sont répertoriées en premier, suivies des victoires noires)

Contribution

Le nombre de pièces sera une chaîne composée d'un caractère pour chaque pièce: king, queen, rook, k night, bishop ou pawn. Les minuscules signifient noir, les majuscules sont blanches. Le tableau sera une chaîne de 64 caractères (8 lignes par 8 colonnes). Breprésente une pièce noire, Wreprésente une pièce blanche et .représente un endroit vide. Échantillon:

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

représenterait le conseil suivant

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

et où les deux couleurs ont 2 évêques, 1 roi, 7 pions, 1 reine, 2 tours

Production

Vous devez renvoyer un nombre à virgule flottante compris entre 0 et 1 (inclus) pour déterminer la probabilité de victoire des blancs. Échantillon:

0.3     (30% chance that white wins)

Plus de détails:

  • Chaque cas de test vaut 1 point. Votre score sera 1 - (1-Output)^2si les blancs gagnent ou 1 - (Output)^2si les noirs gagnent.
  • Votre score final sera la somme de tous les cas de test.
  • Si je pense que les soumissions codent en dur l'entrée, je me réserve le droit de modifier les cas de test. (Si je les change, ils auront le hachage SHA-256 893be4425529f40bb9a0a7632f7a268a087ea00b0eb68293d6c599c6c671cdee)
  • Votre programme doit exécuter des cas de test de manière indépendante. Aucune sauvegarde des informations d'un scénario de test au suivant.
  • Si vous utilisez l'apprentissage automatique, je recommande fortement de vous entraîner sur les 80% des premières données et de tester en utilisant les 20% restants . (Ou quels que soient les pourcentages que vous utilisez). J'utilise les jeux plusieurs fois dans les données, mais je rassemble les mêmes jeux de manière séquentielle.
  • MISE À JOUR: J'ai ajouté plus d'un million de cas de test à des fins de test et d'apprentissage. Ils sont divisés en parties noires et blanches en raison des limites de taille du repo Github.

Bonne chance et amusez-vous bien!

Nathan Merrill
la source
Cette conversation a été déplacée vers le chat .
Dennis
Les nouveaux cas de test contiennent-ils les anciens, ou les deux ensembles sont-ils disjoints?
Fatalize
Je n'ai aucune idée. Je les ai obtenus sur différents sites, il est donc possible qu'ils incluent tous deux le même ensemble de jeux.
Nathan Merrill

Réponses:

8

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

Cette réponse utilise une approche d'apprentissage automatique. Vous devez récupérer la bibliothèque Weka , notamment weka.jaret PackageManager.jar.

Ici, j'utilise un perceptron multicouche comme classificateur; vous pouvez remplacer mlppar n'importe quelle Classifierclasse de Weka pour comparer les résultats.

Je n'ai pas beaucoup bricolé les paramètres du MLP, et je les ai simplement observés (une couche cachée de 50 neurones, 100 époques, 0,2 taux d'apprentissage, 0,1 momentum).

Je seuil la valeur de sortie du MLP, donc la sortie est vraiment 1 ou 0 comme défini dans le défi. De cette façon, le nombre d'instances correctement classées tel qu'imprimé par Weka est directement notre score.

Construction de vecteur de fonction

Je transforme chaque instance d'une chaîne en un vecteur de 76 éléments, où:

  • Les 64 premiers éléments représentent les cellules du tableau, dans le même ordre que dans la chaîne, où se 1trouve une pièce blanche, -1une pièce noire et 0une cellule vide.
  • Les 12 derniers éléments représentent chaque type de pièce (6 par joueur); la valeur de ces éléments est le nombre de pièces de ce type sur le plateau ( 0étant "aucune pièce de ce type"). On pourrait appliquer la normalisation pour réajuster ces valeurs entre -1 et 1 mais ce n'est probablement pas très utile ici.

Nombre d'instances de formation

Si j'utilise tous les cas de test donnés pour former mon classificateur, j'ai réussi à obtenir 6694 (soit 98,6588%) des instances correctement classées . Ce n'est évidemment pas surprenant, car tester un modèle sur les mêmes données que celles utilisées pour le former est beaucoup trop facile (car dans ce cas, il est en fait bon que le modèle soit trop adapté).

En utilisant un sous-ensemble aléatoire de 80% des instances comme données de formation, nous obtenons le chiffre des instances correctement classées 6413 (soit 94,5173%) rapporté dans l'en-tête (bien sûr, puisque le sous-ensemble est aléatoire, vous pouvez obtenir des résultats légèrement différents). Je suis convaincu que le modèle fonctionnerait correctement sur les nouvelles données, car les tests sur les 20% restants des instances (qui n'ont pas été utilisés pour la formation) donnent une classification correcte de 77,0818% , ce qui montre que les modèles se généralisent assez bien (en supposant que les exemples qui nous sont donnés ici sont représentatifs des nouveaux cas de test qui nous seraient donnés).

En utilisant la moitié des instances pour la formation et l'autre moitié pour les tests, nous obtenons 86,7502% sur les données de formation et de test, et 74,4988% sur les données de test uniquement.

la mise en oeuvre

Comme je l'ai dit, ce code nécessite weka.jaret PackageManager.jarde Weka.

On peut contrôler le pourcentage de données utilisées dans l'ensemble de formation avec TRAIN_PERCENTAGE.

Les paramètres du MLP peuvent être modifiés juste en dessous TRAIN_PERCENTAGE. On peut essayer d'autres classificateurs de Weka (par exemple SMOpour les SVM) en remplaçant simplement mlppar un autre classificateur.

Ce programme imprime sur des ensembles de résultats, le premier étant sur l'ensemble complet (y compris les données utilisées pour la formation) qui est le score tel que défini dans ce défi, et le second étant uniquement sur les données qui n'ont pas été utilisées pour la formation.

On entre les données en passant le chemin du fichier le contenant comme argument au programme.

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;
    }
}
Fatalize
la source
Comment encodez-vous l'entrée?
Nathan Merrill
@NathanMerrill Je ne suis pas sûr de comprendre votre question
Fatalize
Comment passez-vous le cas de test en entrée du réseau neuronal? Êtes-vous juste en train de passer dans la chaîne brute?
Nathan Merrill
@NathanMerrill Edité avec un paragraphe sur la construction du vecteur d'entité.
Fatalize
Comment Weka sait-il que vous essayez de prédire le vainqueur?
user1502040
8

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

Mise à jour: l'OP a donné une nouvelle façon de calculer le score de la prédiction pour un cas de test individuel. En utilisant Wolfram Alpha , j'ai tracé les deux ensembles de formules pour voir les différences.

La méthode actuelle incite fortement à produire des probabilités réelles, et pas seulement les extrêmes, 0 et 1, pour lesquels les nouvelles formules donnent le même score maximum qu'auparavant. C'est pourquoi l'algorithme inchangé ci-dessous, a maintenant un meilleur taux de prédiction, en fait un grand taux étant donné sa simplicité.

Cependant, il y a aussi un inconvénient associé aux nouvelles formules, comme expliqué dans 'Edit 1'.


Il s'agit d'une estimation simple basée uniquement sur l'avantage / l'inconvénient matériel, en ignorant le placement réel des pièces. J'étais curieux de savoir comment cela fonctionnera. La raison pour laquelle j'utilise sed, et non une langue qui peut le faire en une seule ligne, c'est parce que c'est ma langue ésotérique préférée.

/:/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

Valeurs standard des pièces utilisées:

  • 9 - Reine
  • 5 - Tour
  • 3 - Chevalier
  • 3 - Évêque
  • 1 - Pion
  • 0 - Roi

Je calcule la matière des deux côtés et soustrais la matière du noir à celle du blanc. La sortie de chaque scénario de test est basée sur cette différence comme suit:

  • si différence> 0, alors sortie = 1 (gain blanc potentiel)
  • si différence = 0, alors sortie = 0,5 (tirage potentiel).

C'est ma seule sortie fractionnaire, d'où la raison de l'amélioration comme expliqué ci-dessus.

  • si différence <0, alors sortie = 0 (gain noir potentiel)

Le taux de prédiction pour cette méthode était de 64%. Maintenant, c'est 75% avec les nouvelles formules.

Au départ, je m'attendais à ce qu'il soit plus élevé, disons 70%, mais en tant que joueur d'échecs moi-même, je peux comprendre le résultat, car j'ai perdu tellement de parties quand j'étais +1 / +2, et j'ai gagné autant quand j'étais en Matériel. Tout dépend de la position réelle. (Eh bien, maintenant j'ai réalisé mon souhait!)

Modifier 1: l'inconvénient

La solution triviale est de produire 0,5 pour chaque test, car de cette façon, vous avez marqué un demi-point, peu importe qui a gagné. Pour nos cas de test, cela signifiait un score total de 3392,5 points (50%).

Mais avec les nouvelles formules, 0,5 (qui est une sortie que vous donneriez si vous êtes indécis qui gagne) est converti en 0,75 points. N'oubliez pas que le score maximum que vous pouvez recevoir pour un scénario de test est de 1, pour une confiance de 100% dans le gagnant. En tant que tel, le nouveau score total pour une sortie constante de 0,5 est de 5088,75 points, soit 75%! À mon avis, l'incitation est trop forte pour ce cas.

Ce score est meilleur, bien que marginalement, que mon algorithme basé sur les avantages matériels. La raison en est que l'algorithme donne une probabilité de 1 ou 0 (aucune incitation), des gains ou des pertes présumés, plus de fois (3831) qu'il n'en donne 0,5 (incitation), des tirages présumés (2954). La méthode est simple à la fin, et en tant que telle, elle n'a pas un pourcentage élevé de réponses correctes. Le coup de pouce de la nouvelle formule à la constante 0,5, parvient à atteindre ce pourcentage, artificiellement.

Modifier 2:

C'est un fait connu, mentionné dans les livres d'échecs, qu'il est généralement préférable d'avoir une paire d'évêque qu'une paire de chevalier. Cela est particulièrement vrai dans la phase intermédiaire à la fin du jeu, où se trouvent les cas de test, car il est plus susceptible d'avoir une position ouverte où la portée d'un évêque est augmentée.

J'ai donc fait un deuxième test, mais cette fois j'ai remplacé la valeur des évêques de 3 à 3,5. La valeur du chevalier est restée 3. C'est une préférence personnelle, donc je n'en ai pas fait ma soumission par défaut. Le score total dans ce cas était de 4411 points (65%). Une augmentation d'un point de pourcentage seulement a été observée.

Avec les nouvelles formules, le score total est de 4835 points (71%). Maintenant, l'évêque pondéré est sous-performant. Mais, l'effet est expliqué parce que la méthode pondérée donne maintenant encore plus de fois les gains ou les pertes présumés (5089) que les tirages présumés (1696).

seshoumara
la source
1
+1 pour fournir une solution de base raisonnable. Je me demandais également dans quelle mesure cela fonctionnerait.
Martin Ender
@MartinEnder Merci. Mon idée d'augmenter la valeur de l'évêque, mentionnée la dernière fois, n'a produit qu'une augmentation de 1% du taux de réussite (voir la mise à jour 2). Je pense que les valeurs standard incluaient cet effet après tout.
seshoumara
Hé, selon le commentaire de xnor, cela vous dérangerait-il que je change le score pour qu'il soit la différence absolue au carré?
Nathan Merrill
1
Impressionnant. Merci aussi d'avoir répondu! Je crains toujours que mes questions les plus difficiles n'obtiennent jamais de réponse.
Nathan Merrill
@NathanMerrill J'ai mis à jour ma réponse pour utiliser le nouveau score comme demandé. Désolé pour la longue analyse, mais j'étais vraiment très curieux.
seshoumara
4

Python 3 - 84,6%, 5275 points sur un ensemble de validation

Si nous trichons et utilisons toutes les données, nous pouvons atteindre une précision de 99,3% et un score de 6408

Juste un grand MLP simple avec abandon en utilisant 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
la source
Combien de données utilisez-vous pour la formation pour obtenir le chiffre de 84,6%?
Fatalize
J'ai utilisé un partage 90-10 comme indiqué dans le code
user1502040
Hé, j'ai ajouté une tonne de cas de test supplémentaires si vous êtes intéressé.
Nathan Merrill
2

Python 3 - 94,3% de précision, 6447 points sur un ensemble de validation de 20% des données

Utilise 3 réseaux de neurones, un régresseur des voisins les plus proches, une forêt aléatoire et un boost de gradient. Ces prédictions sont combinées avec une forêt aléatoire qui a également accès aux données.

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
la source
Hé, j'ai ajouté une tonne de cas de test supplémentaires si vous êtes intéressé.
Nathan Merrill
Woah tu es sorti sur ça.
Robert Fraser
Notez la réponse java ici que "bat" le vôtre semble rapporter% sur l'ensemble de données et n'obtient que 77% sur les données avec lesquelles il ne s'est pas entraîné.
Robert Fraser
0

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

J'ai donc travaillé là-dessus surtout hier. Mon premier article sur le golf, et je n'utilise le python que depuis une semaine environ, alors pardonnez-moi si tout n'est pas optimisé.

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

J'ai fini par suivre le même chemin que la réponse de Seshoumara pour commencer. Mais le grand nombre de cas de test qui comptaient même des comptages de pièces me laissait insatisfait.

J'ai donc googlé les traits qui dictent qui gagne aux échecs (je ne joue pas le jeu moi-même) et j'ai remarqué que la position du plateau, en particulier le contrôle central, est grande. C'est là que ce bit entre en jeu.

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

Ces deux moitiés combinées sont utilisées pour trouver le score (0,0, 0,25, 0,50, 0,75, 1,0)

Très intéressant que cette position supplémentaire ne semble pas augmenter les chances de deviner le gagnant.

Si vous déposez les cas de test dans certains fichiers, voici le test.

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))

Je sais que ce n'est pas un défi de golf, mais tous les trucs ou conseils à cet égard sont appréciés!

Flux de données
la source
Ma réponse? Vous voulez dire la réponse de Seshoumara? De plus, vous n'avez pas besoin de jouer au golf (sauf si vous le souhaitez). Ce n'est pas un défi de code-golf .
Nathan Merrill
Vous pouvez enregistrer de nombreux octets en utilisant uniquement des noms de variable à un caractère. (Même si cela n'a pas vraiment d'importance car ce n'est pas du golf de code)
HyperNeutrino
Woops! Modifier cela maintenant. Au travail, c'est ce que j'obtiens pour l'écrémage!
Datastream
2
Veuillez ne pas jouer au golf. Il vaut mieux garder le code lisible quand ce n'est pas du code-golf.
mbomb007
Le contrôle du milieu du plateau ne signifie pas occuper le milieu du plateau, mais attaquer le milieu du plateau. Si vous vouliez ajouter une certaine complexité à cela, cela pourrait améliorer votre score.
Pas que Charles