Schachanalyse mit begrenzten Informationen

19

Bei dieser Herausforderung erhalten Sie eine begrenzte Menge an Informationen zu einer bestimmten Partie Schach und müssen vorhersagen, wer die Partie gewonnen hat .

Sie erhalten zwei Datensätze:

  1. Stückzahl (Welche Stücke leben noch)
  2. Brettfarben (Die Farbe der Stücke auf dem Brett)

Noch wichtiger ist, dass Sie nicht wissen, wo sich die Teile befinden . Sie müssen bestimmen, wer Ihrer Meinung nach gewinnen wird.

Die Spiele werden aus allen Events ausgewählt, die von 2010 bis jetzt auf PGNMentor gelistet sind . Ich habe 10% aller Brettpositionen aus jedem Spiel ausgewählt, das mit einem Gewinn oder einer Niederlage endet. Brettpositionen werden immer mindestens 30 Züge im Spiel sein. Die Testfälle finden Sie hier . (Weiße Gewinne werden zuerst aufgeführt, gefolgt von schwarzen Gewinnen)

Eingang

Die Stückzahl ist eine Zeichenfolge, die aus einem Zeichen für jedes Stück besteht: king, queen, rook, k night, bishop oder awn p. Kleinbuchstaben bedeuten schwarz, Großbuchstaben sind weiß. Die Tafel besteht aus einer Zeichenfolge von 64 Zeichen (8 Zeilen x 8 Spalten). Brepräsentiert ein schwarzes Stück, Wrepräsentiert ein weißes Stück und .repräsentiert eine leere Stelle. Probe:

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

würde das folgende Board vertreten

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

und wo beide Farben 2 Bischöfe, 1 König, 7 Bauern, 1 Dame, 2 Türme haben

Ausgabe

Sie müssen eine Gleitkommazahl zwischen 0 und 1 (einschließlich) zurückgeben, um zu bestimmen, wie wahrscheinlich es ist, dass Weiß gewinnt. Probe:

0.3     (30% chance that white wins)

Mehr Details:

  • Jeder Testfall ist 1 Punkt wert. Ihre Punktzahl ist, 1 - (1-Output)^2wenn Weiß gewinnt oder 1 - (Output)^2wenn Schwarz gewinnt.
  • Ihre endgültige Punktzahl ist die Summe aller Testfälle.
  • Wenn ich der Meinung bin, dass Einsendungen die Eingabe fest codieren, behalte ich mir das Recht vor, die Testfälle zu ändern. (Wenn ich sie ändere, haben sie den SHA-256-Hash 893be4425529f40bb9a0a7632f7a268a087ea00b0eb68293d6c599c6c671cdee)
  • Ihr Programm muss Testfälle unabhängig ausführen. Keine Speicherung von Informationen von einem Testfall zum nächsten.
  • Wenn Sie maschinelles Lernen verwenden, empfehle ich dringend, die ersten 80% der Daten zu trainieren und die restlichen 20% zu testen . (Oder welche Prozentsätze Sie verwenden). Ich benutze die Spiele mehrmals in den Daten, aber ich setze die gleichen Spiele nacheinander zusammen.
  • UPDATE: Ich habe über eine Million Testfälle zu Test- und Lernzwecken hinzugefügt . Sie werden aufgrund der Größenbeschränkungen für Github-Repos in schwarze und weiße Teile aufgeteilt.

Viel Glück und hab Spaß!

Nathan Merrill
quelle
Diese Unterhaltung wurde in den Chat verschoben .
Dennis
Enthalten die neuen Testfälle die alten oder sind die beiden Sätze nicht miteinander verbunden?
Fatalize
Ich habe keine Ahnung. Ich habe sie von verschiedenen Seiten bekommen, daher ist es möglich, dass beide die gleichen Spiele enthielten.
Nathan Merrill

Antworten:

8

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

Diese Antwort verwendet einen maschinellen Lernansatz. Sie müssen die Weka- Bibliothek abrufen , insbesondere weka.jarund PackageManager.jar.

Hier verwende ich ein mehrschichtiges Perzeptron als Klassifikator; Sie können mlpmit jeder ClassifierKlasse von Weka ersetzen , um die Ergebnisse zu vergleichen.

Ich habe nicht viel an den Parametern des MLP herumgebastelt und sie einfach in Augenschein genommen (eine verborgene Schicht aus 50 Neuronen, 100 Epochen, 0,2 Lernrate, 0,1 Impuls).

Ich schwelle den Ausgabewert des MLP ein, so dass die Ausgabe tatsächlich entweder 1 oder 0 ist, wie in der Abfrage definiert. Auf diese Weise ist die Anzahl der korrekt klassifizierten Instanzen, wie sie von Weka gedruckt wurden, direkt unsere Punktzahl.

Feature-Vektorkonstruktion

Ich wandle jede Instanz von einem String in einen Vektor mit 76 Elementen um, wobei:

  • Die ersten 64 Elemente repräsentieren die Zellen der Tafel in derselben Reihenfolge wie in der Zeichenfolge, in der 1es sich um ein weißes Stück, -1ein schwarzes Stück und 0eine leere Zelle handelt.
  • Die letzten 12 Elemente repräsentieren jede Art von Figur (6 pro Spieler); Der Wert dieser Elemente ist die Anzahl der Teile dieses Typs auf der Tafel ( 0"kein Teil dieses Typs"). Man könnte eine Normalisierung anwenden, um diese Werte zwischen -1 und 1 wiederherzustellen, aber dies ist hier wahrscheinlich nicht sehr hilfreich.

Anzahl der Trainingsinstanzen

Wenn ich alle angegebenen Testfälle verwende, um meinen Klassifikator zu trainieren, habe ich es geschafft, 6694 (dh 98,6588%) korrekt klassifizierte Instanzen zu erhalten . Dies ist offensichtlich nicht verwunderlich, da das Testen eines Modells mit denselben Daten, die Sie zum Trainieren verwendet haben, viel zu einfach ist (da in diesem Fall das Modell eigentlich gut überpasst).

Unter Verwendung einer zufälligen Untermenge von 80% der Instanzen als Trainingsdaten erhalten wir die 6413 (dh 94,5173%) korrekt klassifizierte Instanzzahl , die im Header angegeben ist (da die Untermenge zufällig ist, erhalten Sie möglicherweise leicht abweichende Ergebnisse). Ich bin zuversichtlich, dass das Modell bei neuen Daten anständig gut funktionieren würde, da das Testen der verbleibenden 20% der Instanzen (die nicht für das Training verwendet wurden) eine korrekte Klassifizierung von 77,0818% ergibt Die hier angegebenen Beispiele sind repräsentativ für die neuen Testfälle, die wir erhalten würden.

Wenn die Hälfte der Instanzen für das Training und die andere Hälfte für das Testen verwendet wird, erhalten wir 86,7502% für die Trainings- und Testdaten und 74,4988% nur für die Testdaten.

Implementierung

Wie gesagt, dieser Code benötigt weka.jarund PackageManager.jarvon Weka.

Mit können Sie den Prozentsatz der im Trainingssatz verwendeten Daten steuern TRAIN_PERCENTAGE.

Die Parameter des MLP können direkt darunter geändert werden TRAIN_PERCENTAGE. Man kann andere Klassifikatoren von Weka ausprobieren (zB SMOfür SVMs), indem man sie einfach durch einen mlpanderen Klassifikator ersetzt.

Dieses Programm druckt in Ergebnissätzen, wobei sich der erste auf den gesamten Satz (einschließlich der für das Training verwendeten Daten) bezieht, bei dem es sich um die in dieser Challenge festgelegte Punktzahl handelt, und der zweite auf die Daten, die nicht für das Training verwendet wurden.

Man gibt die Daten ein, indem man den Pfad der Datei, die sie enthält, als Argument an das Programm übergibt.

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;
    }
}
Tödlich
quelle
Wie verschlüsselt man die Eingabe?
Nathan Merrill
@ NathanMerrill Ich bin nicht sicher, ob ich Ihre Frage verstehe
Fatalize
Wie übergeben Sie den Testfall als Eingabe für das neuronale Netzwerk? Übergeben Sie gerade die rohe Schnur?
Nathan Merrill
@ NathanMerrill Bearbeitet mit einem Absatz über die Feature-Vektorkonstruktion.
Fatalize
Woher weiß Weka, dass Sie versuchen, den Gewinner vorherzusagen?
user1502040
8

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

Update: Das OP gab eine neue Möglichkeit, die Punktzahl der Vorhersage für einen einzelnen Testfall zu berechnen. Mit Wolfram Alpha habe ich beide Formelsätze gezeichnet, um die Unterschiede zu erkennen.

Der derzeitige Weg bietet einen starken Anreiz, tatsächliche Wahrscheinlichkeiten auszugeben, und nicht nur die Extreme 0 und 1, für die die neuen Formeln die gleiche maximale Punktzahl wie zuvor liefern. Dies ist der Grund, warum der unten stehende unveränderte Algorithmus jetzt eine bessere Vorhersagerate aufweist, in der Tat eine große Rate aufgrund seiner Einfachheit.

Mit den neuen Formeln ist jedoch auch ein Nachteil verbunden, wie in 'Edit 1' erläutert.


Dies ist eine einfache Schätzung, die nur auf dem Materialvorteil / -nachteil basiert und die tatsächliche Platzierung der Teile ignoriert. Ich war gespannt, wie sich das entwickeln wird. Der Grund, warum ich sed verwende und nicht irgendeine Sprache, die dies in einer Zeile kann, ist, dass es meine Lieblingsesoteriksprache ist.

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

Verwendete Standardstückwerte:

  • 9 - Königin
  • 5 - Turm
  • 3 - Ritter
  • 3 - Bischof
  • 1 - Bauer
  • 0 - König

Ich berechne das Material für beide Seiten und subtrahiere das Material von Schwarz von dem von Weiß. Die Ausgabe für jeden Testfall basiert auf diesem Unterschied wie folgt:

  • Wenn die Differenz> 0 ist, dann ist die Ausgabe = 1 (potentieller weißer Gewinn).
  • Ist die Differenz = 0, dann ist der Ausgang = 0,5 (Potentialverlust).

Dies ist mein einziger Teil der Ausgabe, daher der Grund für die oben erläuterte Verbesserung.

  • Wenn die Differenz <0 ist, dann ist die Ausgabe = 0 (potenzieller schwarzer Gewinn).

Die Prognoserate für diese Methode betrug 64%. Jetzt sind es 75% mit den neuen Formeln.

Ich hatte anfangs mit einem Anstieg gerechnet, sagen wir mal 70%, aber als Schachspieler kann ich das Ergebnis nachvollziehen, da ich mit +1 / +2 so viele Partien verloren habe und so viele gewonnen habe, als ich unterlegen war Material. Es geht nur um die tatsächliche Position. (Nun, jetzt habe ich meinen Wunsch bekommen!)

Edit 1: der Nachteil

Die einfache Lösung besteht darin, für jeden Testfall 0,5 auszugeben, da Sie auf diese Weise einen halben Punkt erzielt haben, unabhängig davon, wer gewonnen hat. Für unsere Testfälle bedeutete dies eine Gesamtpunktzahl von 3392,5 Punkten (50%).

Aber mit den neuen Formeln wird 0,5 (was eine Ausgabe ist, die Sie geben würden, wenn Sie nicht wissen, wer gewinnt) in 0,75 Punkte umgewandelt. Denken Sie daran, dass die maximale Punktzahl, die Sie für einen Testfall erhalten können, 1 ist, um 100% Vertrauen in den Gewinner zu haben. Die neue Gesamtpunktzahl für eine konstante Ausgabe von 0,5 beträgt also 5088,75 Punkte oder 75%! Meiner Meinung nach ist der Anreiz für diesen Fall zu stark.

Diese Punktzahl ist, wenn auch geringfügig, besser als mein Algorithmus, der auf materiellen Vorteilen basiert. Der Grund dafür ist, dass der Algorithmus eine Wahrscheinlichkeit von 1 oder 0 (kein Anreiz), angenommene Gewinne oder Verluste, mehr Male (3831) als 0,5 (Anreiz), angenommene Unentschieden (2954) gibt. Die Methode ist letztendlich einfach und hat daher keinen hohen Prozentsatz an korrekten Antworten. Der Boost von der neuen Formel auf konstant 0,5 schafft es, diesen Prozentsatz künstlich zu erreichen.

Bearbeiten 2:

Es ist eine bekannte Tatsache, die in Schachbüchern erwähnt wird, dass es normalerweise besser ist, ein Läuferpaar als ein Ritterpaar zu haben. Dies gilt insbesondere für die mittlere bis letzte Phase des Spiels, in der die Testfälle vorliegen, da es wahrscheinlicher ist, dass eine Position offen ist, in der die Reichweite eines Bischofs erhöht wird.

Deshalb habe ich einen zweiten Test gemacht, aber dieses Mal habe ich den Wert der Bischöfe von 3 auf 3,5 geändert. Der Wert des Ritters blieb 3. Dies ist eine persönliche Präferenz, daher habe ich ihn nicht zu meiner Standard-Vorlage gemacht. Die Gesamtpunktzahl betrug in diesem Fall 4411 Punkte (65%). Es wurde nur ein Anstieg von 1 Prozentpunkt beobachtet.

Mit den neuen Formeln beträgt die Gesamtpunktzahl 4835 Punkte (71%). Jetzt schneidet der gewichtete Läufer schlechter ab. Der Effekt wird jedoch erklärt, da die gewichtete Methode jetzt noch mehr Male angenommene Gewinne oder Verluste liefert (5089) als angenommene Unentschieden (1696).

Seshoumara
quelle
1
+1 für die Bereitstellung einer angemessenen Basislösung. Ich habe mich auch gefragt, wie gut das abschneiden würde.
Martin Ender
@ MartinEnder Danke. Meine zuletzt erwähnte Idee, den Wert des Bischofs zu erhöhen, führte nur zu einer Steigerung der Erfolgsquote um 1% (siehe Update 2). Ich denke, die Standardwerte haben diesen Effekt doch mit einbezogen.
Seshoumara
Hey, nach xnors Kommentar, würde es Ihnen etwas ausmachen, wenn ich die Wertung so ändere, dass sie der quadratische absolute Unterschied ist?
Nathan Merrill
1
Genial. Danke auch für die Antwort! Ich mache mir immer Sorgen, dass meine schwierigeren Fragen nie beantwortet werden.
Nathan Merrill
@ NathanMerrill Ich habe meine Antwort aktualisiert, um die neue Wertung wie gewünscht zu verwenden. Entschuldigung für die lange Analyse, aber ich war in der Tat sehr neugierig.
Seshoumara
4

Python 3 - 84,6%, 5275 Punkte in einem Validierungssatz

Wenn wir alle Daten betrügen und verwenden, können wir eine Genauigkeit von 99,3% und eine Punktzahl von 6408 erzielen

Nur eine einfache große MLP mit Dropout mit 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
quelle
Wie viele Daten verwenden Sie für das Training, um den Wert von 84,6% zu erhalten?
Fatalize
Ich habe einen 90-10-Split verwendet, wie im Code gezeigt
user1502040
Hey, ich habe eine Tonne mehr Testfälle hinzugefügt, wenn Sie interessiert sind.
Nathan Merrill
2

Python 3 - 94,3% Genauigkeit, 6447 Punkte bei einem Validierungssatz von 20% der Daten

Verwendet 3 neuronale Netze, einen Regressor des nächsten Nachbarn, eine zufällige Gesamtstruktur und eine Gradientenverstärkung. Diese Vorhersagen werden mit einer zufälligen Gesamtstruktur kombiniert, die auch Zugriff auf die Daten hat.

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
quelle
Hey, ich habe eine Tonne mehr Testfälle hinzugefügt, wenn Sie interessiert sind.
Nathan Merrill
Woah, du bist da rausgegangen.
Robert Fraser
Beachten Sie die Java-Antwort hier, dass "Beats" Ihre scheinen,% auf den gesamten Datensatz zu melden und nur 77% auf die Daten zu erhalten, mit denen es nicht trainiert hat.
Robert Fraser
0

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

Also habe ich gestern meistens daran gearbeitet. Mein erster Beitrag zum Golfen, und ich benutze Python erst seit ungefähr einer Woche. Verzeihen Sie mir, wenn nicht alles optimiert ist.

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

Ich endete auf dem gleichen Weg wie Seshoumaras Antwort. Aber die große Anzahl von Testfällen mit geraden Stückzahlen hat mich unzufrieden gemacht.

Also habe ich Eigenschaften gegoogelt, die bestimmen, wer im Schach gewinnt (ich spiele das Spiel nicht selbst), und festgestellt, dass die Brettposition, insbesondere die Zentralkontrolle, groß ist. Hier kommt dieses Bit ins Spiel.

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

Beide Hälften werden kombiniert, um die Punktzahl zu ermitteln (0,0, 0,25, 0,50, 0,75, 1,0).

Sehr interessant, dass diese zusätzliche Boardposition die Chance, den Gewinner zu erraten, nicht zu erhöhen scheint.

Wenn Sie die Testfälle in einige Dateien ablegen, finden Sie hier das Testen.

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

Ich weiß, dass dies keine Golfherausforderung ist, aber alle Tipps oder Ratschläge in dieser Hinsicht sind willkommen!

Datenstrom
quelle
Meine Antwort? Du meinst Seshoumara Antwort? Außerdem müssen Sie nicht Golf spielen (es sei denn, Sie möchten). Dies ist keine Code-Golf- Herausforderung.
Nathan Merrill
Sie können viele Bytes speichern, indem Sie nur aus einem Zeichen bestehende Variablennamen verwenden. (Obwohl es nicht wirklich wichtig ist, weil dies kein Code-Golf ist)
HyperNeutrino
Woops! Bearbeiten Sie das jetzt. Bei der Arbeit bekomme ich das zum Überfliegen!
Datenstrom
2
Bitte nicht Golf spielen. Es ist besser, den Code lesbar zu halten, wenn es kein Code-Golf ist.
mbomb007
Die Kontrolle über die Mitte des Brettes bedeutet nicht, die Mitte des Brettes zu besetzen, sondern die Mitte des Brettes anzugreifen. Wenn Sie dem etwas Komplexität hinzufügen möchten, kann dies Ihre Punktzahl verbessern.
Nicht dass Charles