Analiza szachowa z ograniczonymi informacjami

19

W tym wyzwaniu otrzymujesz ograniczoną ilość informacji o konkretnej grze w szachy i musisz przewidzieć, kto wygrał .

Otrzymujesz dwa zestawy danych:

  1. Liczy się liczba sztuk (jakie części wciąż żyją)
  2. Kolory planszy (Kolor elementów na planszy)

Co ważniejsze, ty nie wiesz, gdzie znajdują się kawałki . Musisz ustalić, kto Twoim zdaniem wygra.

Gry są wybierane ze wszystkich wydarzeń wymienionych na PGNMentor od 2010 roku do chwili obecnej. Wybrałem 10% wszystkich pozycji na planszy z każdej gry, która kończy się wygraną lub przegraną. Pozycje na planszy będą zawsze mieć co najmniej 30 ruchów w grze. Przypadki testowe można znaleźć tutaj . (Białe wygrane są wymienione jako pierwsze, a następnie czarne wygrane)

Wejście

Liczba sztuk będzie ciągiem składającym się ze znaku dla każdego kawałka: king, queen, rook, k night, bishop lub pawn. Małe litery oznaczają czarny, wielkie litery są białe. Plansza będzie ciągiem 64 znaków (8 rzędów na 8 kolumn). Breprezentuje czarny kawałek, Wreprezentuje biały kawałek i .przedstawia puste miejsce. Próba:

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

reprezentowałby następującą radę

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

i gdzie oba kolory mają 2 Biskupów, 1 Króla, 7 Pionków, 1 Królową, 2 Wieże

Wynik

Musisz zwrócić liczbę zmiennoprzecinkową z zakresu od 0 do 1 (włącznie), aby określić prawdopodobieństwo, że białe wygrywają. Próba:

0.3     (30% chance that white wins)

Więcej szczegółów:

  • Każdy przypadek testowy jest wart 1 punkt. Twój wynik będzie, 1 - (1-Output)^2jeśli biały wygra lub 1 - (Output)^2czarny wygra.
  • Twój końcowy wynik będzie sumą dla wszystkich przypadków testowych.
  • Jeśli uważam, że zgłoszenia są na stałe zakodowane, zastrzegam sobie prawo do zmiany przypadków testowych. (Jeśli je zmienię, będą miały skrót SHA-256893be4425529f40bb9a0a7632f7a268a087ea00b0eb68293d6c599c6c671cdee )
  • Twój program musi uruchamiać przypadki testowe niezależnie. Brak zapisywania informacji z jednego przypadku testowego do drugiego.
  • Jeśli korzystasz z uczenia maszynowego, bardzo polecam szkolenie na temat pierwszych 80% danych, a testowanie z wykorzystaniem pozostałych 20% . (Lub niezależnie od tego, jaki procent użyjesz). Używam tych gier wiele razy w danych, ale zestawiam te same gry sekwencyjnie.
  • AKTUALIZACJA: Dodałem ponad milion przypadków testowych do celów testowych i edukacyjnych. Są one podzielone na części czarno-białe ze względu na ograniczenia rozmiaru repozytorium github.

Powodzenia i miłej zabawy!

Nathan Merrill
źródło
Ta rozmowa została przeniesiona do czatu .
Dennis
Czy nowe przypadki testowe zawierają stare, czy te dwa zestawy są rozłączne?
Fatalize
Nie mam pojęcia. Mam je z różnych stron, więc możliwe, że oba zawierały ten sam zestaw gier.
Nathan Merrill

Odpowiedzi:

8

Java 8 + Weka, 6413 punktów, 94,5%

Ta odpowiedź wykorzystuje podejście uczenia maszynowego. Musisz pobrać bibliotekę Weka , w szczególności weka.jariPackageManager.jar .

Tutaj używam wielowarstwowego perceptronu jako klasyfikatora; możesz zastąpić mlpdowolnymClassifier klasą Weka, aby porównać wyniki.

Niewiele majstrowałem przy parametrach MLP i po prostu spojrzałem im w oczy (jedna ukryta warstwa 50 neuronów, 100 epok, 0,2 szybkości uczenia się, 0,1 pędu).

Przekraczam próg wartości wyjściowej MLP, więc tak naprawdę wyjście wynosi 1 lub 0, jak zdefiniowano w wyzwaniu. W ten sposób liczba poprawnie sklasyfikowanych instancji wydrukowanych przez Weka jest bezpośrednio naszym wynikiem.

Konstrukcja wektora cechy

Przekształcam każdą instancję z łańcucha na wektor 76 elementów, gdzie:

  • Pierwsze 64 elementy reprezentują komórki planszy, w tej samej kolejności, co w ciągu, gdzie 1jest biały kawałek, -1jest czarny kawałek i 0jest pustą komórką.
  • Ostatnie 12 elementów reprezentuje każdy rodzaj elementu (6 na gracza); wartość tych elementów to liczba sztuk tego typu na planszy ( 0czyli „brak części tego typu”). Można zastosować normalizację, aby dopasować te wartości od -1 do 1, ale prawdopodobnie nie jest to tutaj bardzo pomocne.

Liczba wystąpień szkoleniowych

Jeśli wykorzystam wszystkie podane przypadki testowe do wyszkolenia mojego klasyfikatora, udało mi się uzyskać 6694 (tj. 98,6588%) poprawnie sklasyfikowanych instancji . Nie jest to oczywiście zaskakujące, ponieważ testowanie modelu na tych samych danych, których użyłeś do treningu, jest zbyt łatwe (ponieważ w takim przypadku dobrze jest, że model się przerasta).

Używając losowego podzbioru 80% instancji jako danych treningowych, otrzymujemy 6413 (tj. 94,5173%) poprawnie sklasyfikowanych instancji zgłoszonych w nagłówku (oczywiście ponieważ ten podzbiór jest losowy, możesz uzyskać nieco inne wyniki). Jestem pewien, że model działałby przyzwoicie na nowych danych, ponieważ testowanie na pozostałych 20% instancji (które nie były używane do szkolenia) daje 77,0818% poprawnej klasyfikacji, co pokazuje, że modele dość dobrze się generalizują (zakładając, że przedstawione nam przykłady są reprezentatywne dla nowych przypadków testowych, które otrzymalibyśmy).

Wykorzystując połowę instancji do treningu, a drugą połowę do testowania, otrzymujemy 86,7502% zarówno na danych szkoleniowych, jak i testowych oraz 74,4988% tylko na danych testowych.

Realizacja

Jak powiedziałem, ten kod wymaga weka.jari PackageManager.jarod Weka.

Za pomocą można kontrolować procent danych wykorzystywanych w zestawie treningowym TRAIN_PERCENTAGE.

Parametry MLP można zmienić tuż poniżej TRAIN_PERCENTAGE. Można wypróbować inne klasyfikatory Weka (np. SMODla SVM), po prostu zastępując mlpje innym klasyfikatorem.

Ten program drukuje do zestawów wyników, z których pierwszy znajduje się w całym zestawie (w tym dane użyte do treningu), co jest wynikiem zdefiniowanym w tym wyzwaniu, a drugi dotyczy tylko danych, które nie zostały użyte do treningu.

Dane wprowadza się, przekazując ścieżkę pliku zawierającego je jako argument do programu.

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;
    }
}
Fatalizować
źródło
Jak kodujesz dane wejściowe?
Nathan Merrill
@NathanMerrill Nie jestem pewien, czy rozumiem twoje pytanie
Fatalize
Jak przekazujesz przypadek testowy jako dane wejściowe do sieci neuronowej? Czy po prostu podajesz surowy ciąg?
Nathan Merrill
@NathanMerrill Edytowane z akapitem na temat konstrukcji wektora cech.
Fatalize
Skąd Weka wie, że próbujesz przewidzieć zwycięzcę?
user1502040
8

GNU sed + bc, 4336 5074,5 punktów, 64 75%

Aktualizacja: OP dał nowy sposób obliczenia wyniku prognozy dla pojedynczego przypadku testowego. Używając Wolfram Alpha , narysowałem oba zestawy wzorów, aby zobaczyć różnice.

Obecny sposób stanowi silną zachętę do generowania rzeczywistych prawdopodobieństw, a nie tylko skrajności, 0 i 1, dla których nowe formuły dają taki sam maksymalny wynik jak poprzednio. Właśnie dlatego niezmieniony algorytm poniżej ma teraz lepszy współczynnik predykcji, w rzeczywistości świetny współczynnik, biorąc pod uwagę jego prostotę.

Istnieje jednak także wada związana z nowymi formułami, jak wyjaśniono w „Edycji 1”.


Jest to proste oszacowanie oparte jedynie na przewadze / wadach materiałowych, ignorujące faktyczne rozmieszczenie elementów. Byłem ciekawy, jak to się potoczy. Powodem, dla którego używam sed, a nie jakiegoś języka, który może to zrobić w jednym wierszu, jest to, że jest to mój ulubiony język ezoteryczny.

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

Zastosowane standardowe wartości sztuk:

  • 9 - królowa
  • 5 - Gawron
  • 3 - Rycerz
  • 3 - Biskup
  • 1 - pionek
  • 0 - król

Obliczam materiał dla obu stron i odejmuję materiał czarnego od białego. Dane wyjściowe dla każdego przypadku testowego oparte są na tej różnicy w następujący sposób:

  • jeśli różnica> 0, to wynik = 1 (potencjalna biała wygrana)
  • jeśli różnica = 0, to wynik = 0,5 (potencjalne wykorzystanie).

To jest mój jedyny ułamkowy wynik, stąd przyczyna ulepszenia, jak wyjaśniono powyżej.

  • jeśli różnica <0, to wynik = 0 (potencjalna czarna wygrana)

Wskaźnik prognozy dla tej metody wynosił 64%. Teraz jest 75% dzięki nowym formułom.

Początkowo spodziewałem się, że będzie wyższy, powiedzmy 70%, ale jako gracz w szachy sam rozumiem wynik, ponieważ przegrałem tyle gier, gdy miałem +1 / +2, i wygrałem tyle, gdy byłem na dole materiał. Chodzi o rzeczywistą pozycję. (Cóż, teraz mam życzenie!)

Edycja 1: wada

Trywialnym rozwiązaniem jest uzyskanie 0,5 dla każdego przypadku testowego, ponieważ w ten sposób zdobyłeś pół punktu niezależnie od tego, kto wygrał. Dla naszych przypadków testowych oznaczało to łączny wynik 3392,5 punktu (50%).

Ale dzięki nowym formułom 0,5 (co daje wynik, jeśli nie jesteś zdecydowany, kto wygra) jest konwertowane na 0,75 punktu. Pamiętaj, że maksymalny wynik, jaki można uzyskać za przypadek testowy, wynosi 1, co daje 100% zaufania do zwycięzcy. W związku z tym nowy całkowity wynik dla stałej wartości 0,5 wynosi 5088,75 punktów, czyli 75%! Moim zdaniem zachęta jest w tym przypadku zbyt silna.

Ten wynik jest lepszy, choć marginalnie, niż mój algorytm oparty na przewadze materialnej. Powodem tego jest fakt, że algorytm podaje prawdopodobieństwo 1 lub 0 (brak zachęty), zakładane wygrane lub straty, więcej razy (3831) niż daje 0,5 (zachęta), zakładane remisy (2954). Metoda jest w końcu prosta i jako taka nie ma wysokiego odsetka poprawnych odpowiedzi. Przyspieszenie od nowej formuły do ​​stałej 0,5 udaje się sztucznie osiągnąć ten odsetek.

Edycja 2:

Jest znany fakt, wspomniany w książkach szachowych, że zwykle lepiej jest mieć parę biskupów niż parę rycerzy. Jest to szczególnie prawdziwe w środkowej i końcowej fazie gry, gdzie znajdują się przypadki testowe, ponieważ jest bardziej prawdopodobne, że ma otwartą pozycję, w której zwiększa się zasięg biskupa.

Dlatego zrobiłem drugi test, ale tym razem zastąpiłem wartość biskupów z 3 do 3,5. Wartość rycerza pozostała 3. Jest to osobista preferencja, więc nie ustawiłem jej jako domyślnego poddania się. Łączny wynik w tym przypadku wyniósł 4411 punktów (65%). Zaobserwowano tylko 1 punkt procentowy wzrostu.

Dzięki nowym formułom łączny wynik wynosi 4835 punktów (71%). Teraz ważony biskup osiąga gorsze wyniki. Ale efekt jest wyjaśniony, ponieważ metoda ważona daje teraz jeszcze więcej razy zakładane wygrane lub straty (5089), niż zakładane remisy (1696).

seshoumara
źródło
1
+1 za zapewnienie rozsądnego podstawowego rozwiązania. Zastanawiałem się również, jak dobrze by to działało.
Martin Ender
@MartinEnder Dziękujemy. Mój pomysł na zwiększenie wartości biskupa, wspomniany ostatnio, przyniósł jedynie 1% wzrost wskaźnika sukcesu (patrz Aktualizacja 2). Myślę, że standardowe wartości w końcu obejmowały ten efekt.
seshoumara
Hej, zgodnie z komentarzem Xnora, czy miałbyś coś przeciwko, jeśli zmienię punktację na kwadratową różnicę absolutną?
Nathan Merrill
1
Niesamowite. Dziękujemy również za odpowiedź! Zawsze martwię się, że moje trudniejsze pytania nigdy nie otrzymają odpowiedzi.
Nathan Merrill
@NathanMerrill Zaktualizowałem swoją odpowiedź, aby użyć nowej punktacji zgodnie z zapytaniem. Przepraszam za długą analizę, ale naprawdę byłem naprawdę ciekawy.
seshoumara
4

Python 3 - 84,6%, 5275 punktów w zestawie sprawdzania poprawności

Jeśli oszukujemy i wykorzystujemy wszystkie dane, możemy osiągnąć dokładność 99,3% i wynik 6408

Po prostu prosta duża MLP z rezygnacją za pomocą 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)
użytkownik1502040
źródło
Ile danych używasz do treningu, aby uzyskać 84,6%?
Fatalize
Użyłem podziału
90-10,
Hej, dodałem tony więcej przypadków testowych, jeśli jesteś zainteresowany.
Nathan Merrill
2

Python 3 - dokładność 94,3%, 6447 punktów na zestawie walidacyjnym 20% danych

Wykorzystuje 3 sieci neuronowe, regresor najbliższych sąsiadów, losowy las i wzmocnienie gradientu. Prognozy te są łączone z losowym lasem, który również ma dostęp do danych.

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))
użytkownik1502040
źródło
Hej, dodałem tony więcej przypadków testowych, jeśli jesteś zainteresowany.
Nathan Merrill
Woah, wyszedłeś na to.
Robert Fraser,
Zwróć uwagę na odpowiedź Java, że ​​„bije” twój wydaje się zgłaszać% na całym zestawie danych i dostaje tylko 77% na danych, z którymi nie trenował.
Robert Fraser,
0

Python 3 - 4353,25 / 6785 punktów - 64%

Pracowałem nad tym głównie wczoraj. Moja pierwsza gra w golfa i używam pytona dopiero od około tygodnia, więc wybacz mi, jeśli nie wszystko jest zoptymalizowane.

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

Skończyłem na tej samej ścieżce, co odpowiedź seshoumary na początek. Ale duża liczba przypadków testowych, które zawierały nawet liczbę sztuk, sprawiła, że ​​byłem niezadowolony.

Więc przejrzałem cechy, które decydują o tym, kto wygrywa w szachach (sam nie gram) i zauważyłem, że pozycja na planszy, szczególnie kontrola środkowa, jest duża. Tam właśnie wchodzi ten bit.

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

Obie połówki łącznie służą do znalezienia wyniku (0,0, 0,25, 0,50, 0,75, 1,0)

Bardzo interesujące jest to, że ta dodatkowa pozycja na tablicy wydaje się nie zwiększać szansy na odgadnięcie zwycięzcy.

Jeśli upuścisz przypadki testowe w niektórych plikach, oto testowanie.

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

Wiem, że to nie jest wyzwanie golfowe, ale wszelkie wskazówki i porady w tym zakresie są mile widziane!

Strumień danych
źródło
Moja odpowiedź? Masz na myśli odpowiedź seshoumary? Nie musisz też grać w golfa (chyba że chcesz). To nie jest wyzwanie do gry w golfa .
Nathan Merrill
Możesz zapisać wiele bajtów, używając tylko jednoznakowych nazw zmiennych. (Chociaż to tak naprawdę nie ma znaczenia, ponieważ nie jest to golf kodowy)
HyperNeutrino
Woops! Edycja tego teraz. W pracy to właśnie dostaję za szperanie!
Datastream
2
Proszę, nie graj w golfa. Lepiej jest zachować czytelność kodu, gdy nie jest to gra w golfa.
mbomb007
Kontrola środka planszy nie oznacza zajmowania środka planszy, ale zaatakowanie środka planszy. Jeśli chcesz dodać do tego trochę złożoności, może to poprawić Twój wynik.
Nie to, że Charles