Шахматный анализ с ограниченной информацией


19

В этом задании вам предоставляется ограниченный объем информации о конкретной игре в шахматы, и вам необходимо предсказать, кто выиграл игру .

Вам предоставляется два набора данных:

  1. Количество штук (Какие фигуры еще живы)
  2. Цвет доски (цвет фигур на доске)

Что еще более важно, вы не знаете, где находятся части . Вы должны определить, кто, по вашему мнению, победит.

Игры выбираются из всех событий, перечисленных в PGNMentor с 2010 года по настоящее время. Я выбрал 10% всех позиций на доске из каждой игры, которая заканчивается выигрышем или проигрышем. Позиции доски всегда будут составлять не менее 30 ходов. Тестовые случаи можно найти здесь . (Белые победы указаны первыми, затем черные победы)

вход

Отсчет часть будет строка , состоящая из символа для каждой части: kИнг, queen, rООК, к nIGHT, bishop или pтентом. Нижний регистр означает черный, верхний регистр - белый. Доска будет строкой из 64 символов (8 строк на 8 столбцов). Bпредставляет черный кусок, Wпредставляет белый кусок и .представляет собой пустое место. Образец:

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

будет представлять следующую доску

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

и где оба цвета имеют 2 слона, 1 короля, 7 пешек, 1 королеву, 2 грача

Выход

Вам необходимо вернуть число с плавающей точкой от 0 до 1 (включительно), чтобы определить, насколько вероятно, что белые выиграют. Образец:

0.3     (30% chance that white wins)

Больше деталей:

  • Каждый тестовый пример стоит 1 балл. Ваша оценка будет, 1 - (1-Output)^2если белые выиграют, или 1 - (Output)^2если черные выиграют.
  • Ваш окончательный результат будет суммой во всех тестовых случаях.
  • Если я чувствую, что материалы жестко закодированы, я оставляю за собой право изменять контрольные примеры. (Если я их поменяю, у них будет хеш SHA-256 893be4425529f40bb9a0a7632f7a268a087ea00b0eb68293d6c599c6c671cdee)
  • Ваша программа должна запускать тестовые случаи независимо. Нет сохранения информации из одного теста в другой.
  • Если вы используете машинное обучение, я настоятельно рекомендую тренироваться на первых 80% данных, а тестирование - на оставшихся 20% . (Или какой процент вы используете). Я использую игры несколько раз в данных, но я последовательно соединяю одни и те же игры.
  • ОБНОВЛЕНИЕ: я добавил более миллиона тестовых случаев для тестирования и обучения. Они разделены на черные и белые части из-за ограничений размера репозитория github.

Удачи и приятного времяпровождения!


Этот разговор был перемещен в чат .
Денис

Содержат ли новые тестовые наборы старые или эти два набора не пересекаются?
Fatalize

Я понятия не имею. Я получил их с разных сайтов, поэтому возможно, что они включали в себя один и тот же набор игр.
Натан Меррилл

Ответы:


8

Java 8 + Weka, 6413 баллов, 94,5%

Этот ответ использует подход машинного обучения. Вам нужно получить библиотеку Weka , особенно weka.jarи PackageManager.jar.

Здесь я использую многослойный персептрон в качестве классификатора; Вы можете заменить mlpлюбой Classifierкласс Weka для сравнения результатов.

Я не слишком возился с параметрами MLP, а просто присматривал за ними (один скрытый слой из 50 нейронов, 100 эпох, 0,2 скорости обучения, 0,1 импульса).

Я пороговое значение выходного значения MLP, так что выход на самом деле либо 1 или 0, как определено в задаче. Таким образом, количество правильно классифицированных экземпляров, напечатанных Weka, является непосредственно нашей оценкой.

Характеристика векторного построения

Я превращаю каждый экземпляр из строки в вектор из 76 элементов, где:

  • Первые 64 элемента представляют ячейки доски в том же порядке, что и в строке, где 1белая фигура, -1черная фигура и 0пустая ячейка.
  • Последние 12 элементов представляют каждый тип фигуры (по 6 на игрока); значение этих элементов - это количество фигур этого типа на доске ( 0«нет фигур этого типа»). Можно применить нормализацию, чтобы переопределить эти значения между -1 и 1, но это, вероятно, не очень полезно здесь.

Количество тренировочных экземпляров

Если я использую все тесты, приведенные для обучения моего классификатора, мне удалось получить 6694 (т.е. 98,6588%) правильно классифицированных экземпляров . Это, очевидно, неудивительно, потому что тестировать модель на тех же данных, которые вы использовали для ее обучения, слишком просто (потому что в этом случае на самом деле хорошо, что модель подходит больше).

Используя случайное подмножество из 80% экземпляров в качестве обучающих данных, мы получаем 6413 (т.е. 94,5173%) правильно классифицированных значений экземпляров, о которых сообщается в заголовке (конечно, поскольку подмножество является случайным, вы можете получить немного другие результаты). Я уверен, что модель будет неплохо работать на новых данных, потому что тестирование на оставшихся 20% случаев (которые не использовались для обучения) дает 77,0818% правильной классификации, которая показывает, что модели неплохо обобщают (при условии, что примеры, которые мы приводим здесь, являются репрезентативными для новых тестовых случаев, которые нам будут предоставлены)

Используя половину экземпляров для обучения и другую половину для тестирования, мы получаем 86,7502% как для данных обучения, так и для тестирования, и 74,4988% только для данных теста.

Реализация

Как я уже сказал, этот код требует weka.jarиPackageManager.jar от Weka.

Можно контролировать процент данных, используемых в обучающем наборе, с помощью TRAIN_PERCENTAGE .

Параметры MLP можно изменить чуть ниже TRAIN_PERCENTAGE. Можно попробовать другие классификаторы Weka (например, SMOдля SVM), просто заменивmlp другим классификатором.

Эта программа печатает наборы результатов, первый из которых относится ко всему набору (включая данные, использованные для обучения), который является результатом, определенным в этом задании, а второй - только для данных, которые не использовались для обучения.

Каждый вводит данные, передавая путь к файлу, содержащему их, в качестве аргумента программы.

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;
    }
}

Как вы кодируете вход?
Натан Меррилл

@NathanMerrill Я не уверен, что понимаю ваш вопрос
Fatalize

Как вы проходите тестовый набор в качестве входных данных для нейронной сети? Вы просто передаете необработанную строку?
Натан Меррилл

@NathanMerrill Отредактировано с абзацем о векторном построении объекта.
Fatalize

Откуда нам известно, что вы пытаетесь предсказать победителя?
user1502040

8

GNU sed + bc, 4336 5074,5 балла, 64 75%

Обновление: ОП дала новый способ вычисления оценки прогноза для отдельного тестового случая. Используя Wolfram Alpha , я построил оба набора формул, чтобы увидеть различия.

Текущий способ дает сильный стимул для вывода фактических вероятностей, а не только крайностей, 0 и 1, для которых новые формулы дают тот же максимальный результат, что и раньше. Вот почему неизменный алгоритм, приведенный ниже, теперь имеет лучшую скорость предсказания, фактически большую скорость, учитывая его простоту.

Однако есть также недостаток, связанный с новыми формулами, как объяснено в «Редактировании 1».


Это простая оценка, основанная только на материальном преимуществе / недостатке, игнорирующая фактическое размещение фигур. Мне было любопытно, как это будет работать. Причина, по которой я использую sed, а не какой-то язык, который может сделать это в одной строке, заключается в том, что это мой любимый эзотерический язык.

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

Используются стандартные значения штучного товара:

  • 9 - королева
  • 5 - Ладья
  • 3 - Рыцарь
  • 3 - Епископ
  • 1 - пешка
  • 0 - король

Я рассчитываю материал для обеих сторон и вычитаю материал черного из материала белого. Выходные данные для каждого теста основаны на этой разнице следующим образом:

  • если разница> 0, то выход = 1 (потенциальная победа белых)
  • если разница = 0, то выход = 0,5 (потенциальное потребление).

Это мой единственный неполный вывод, поэтому причина улучшения, как объяснено выше.

  • если разница <0, то выход = 0 (потенциальный черный выигрыш)

Уровень прогнозирования для этого метода составил 64%. Теперь это 75% с новыми формулами.

Первоначально я ожидал, что он будет выше, скажем, на 70%, но сам как шахматист я могу понять результат, поскольку я проиграл так много игр, когда мне было +1 / +2, и выиграл так много, когда я был в материал. Это все о реальной позиции. (Ну, теперь я получил мое желание!)

Редактировать 1: недостаток

Тривиальное решение - вывести 0.5 для каждого тестового случая, потому что таким образом вы набрали пол-очка независимо от того, кто победил. Для наших тестовых случаев это означало общую оценку 3392,5 балла (50%).

Но с новыми формулами 0.5 (это результат, который вы дадите, если не определитесь, кто победит), преобразуется в 0,75 балла. Помните, что максимальный балл, который вы можете получить за тестовый случай, равен 1, при 100% уверенности в победителе. Таким образом, новый общий балл для постоянных 0,5 результата составляет 5088,75 балла или 75%! На мой взгляд, стимул слишком силен для этого случая.

Этот показатель лучше, хотя и незначительно, чем мой алгоритм, основанный на материальных преимуществах. Причина этого заключается в том, что алгоритм дает вероятность 1 или 0 (без стимула), предполагаемых выигрышей или проигрышей, больше раз (3831), чем дает 0,5 (стимулы), предполагаемых ничьих (2954). В конце концов, метод прост, и поэтому он не имеет большого процента правильных ответов. Увеличение от новой формулы до константы 0,5 позволяет достичь этого процента искусственно.

Изменить 2:

В шахматных книгах упоминается известный факт, что обычно лучше иметь пару слонов, чем пару коней. Это особенно верно в промежуточной стадии игры, где проходят тестовые задания, поскольку более вероятно иметь открытую позицию, в которой диапазон слона увеличивается.

Поэтому я сделал второй тест, но на этот раз я заменил значение слонов с 3 до 3,5. Ценность рыцаря осталась 3. Это личное предпочтение, поэтому я не сделал его своим заданием по умолчанию. Общая оценка в этом случае составила 4411 баллов (65%). Наблюдалось только увеличение на 1 процентный пункт.

С новыми формулами общая оценка составляет 4835 баллов (71%). Теперь взвешенный епископ уступает. Но эффект объясняется тем, что взвешенный метод теперь дает даже больше предполагаемых выигрышей или проигрышей (5089), чем предполагаемые ничьи (1696).


1
+1 за предоставление разумного базового решения. Мне также было интересно, насколько хорошо это будет работать.
Мартин Эндер

@MartinEnder Спасибо. Моя идея об увеличении ценности епископа, упомянутая в прошлый раз, привела только к увеличению успеха на 1% (см. Обновление 2). Я думаю, что стандартные значения все же включают этот эффект.
Сешумара

Эй, согласно комментарию xnor, вы не возражаете, если я изменит счет на квадрат абсолютной разницы?
Натан Меррилл

1
Потрясающие. Также спасибо за ответ! Я всегда волнуюсь, что мои более сложные вопросы никогда не получат ответа.
Натан Меррилл

@NathanMerrill Я обновил свой ответ, чтобы использовать новую оценку в соответствии с просьбой. Извините за долгий анализ, но мне действительно было очень любопытно.
Сешумара

4

Python 3 - 84,6%, 5275 баллов за проверочный набор

Если мы будем обманывать и использовать все данные, мы сможем достичь точности 99,3% и оценки 6408.

Просто большой MLP с пропуском с использованием 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)

Сколько данных вы используете для обучения, чтобы получить показатель 84,6%?
Fatalize

Я использовал 90-10 сплит, как показано в коде
user1502040

Эй, я добавил тонну больше тестовых случаев , если вы заинтересованы.
Натан Меррилл

2

Python 3 - точность 94,3%, 6447 баллов на проверочном наборе 20% данных

Использует 3 нейронные сети, регрессор ближайших соседей, случайный лес и усиление градиента. Эти прогнозы сочетаются со случайным лесом, который также имеет доступ к данным.

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

Эй, я добавил тонну больше тестовых случаев , если вы заинтересованы.
Натан Меррилл

Вау, ты вышел на это.
Роберт Фрейзер

Обратите внимание, что ответ java здесь, который «бьет» ваш, кажется, сообщает% по всему набору данных и только 77% получает данные, с которыми он не работал.
Роберт Фрейзер

0

Питон 3 - 4353,25 / 6785 баллов - 64%

Так что я работал над этим в основном вчера. Мой первый пост в гольф, и я использую python только неделю или около того, так что простите, если не все оптимизировано.

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

Я оказался на том же пути, что и ответ Сешумара. Но большое количество тестовых случаев, в которых было даже количество кусочков, оставило меня недовольным.

Таким образом, я погуглил черты, которые определяют, кто выигрывает в шахматы (я сам не играю в эту игру), и заметил, что позиция доски, особенно центральное управление, велика. Вот где этот бит входит.

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

Обе эти две половины вместе используются для нахождения оценки (0,0, 0,25, 0,50, 0,75, 1,0)

Очень интересно, что эта дополнительная позиция на доске не увеличивает шансов угадать победителя.

Если вы поместите тестовые наборы в несколько файлов, вот тестирование.

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

Я знаю, что это не вызов для гольфа, но любые советы или рекомендации в этом отношении приветствуются!


Мой ответ? Ты имеешь ввиду ответ сешумара? Кроме того, вам не нужно играть в гольф (если вы не хотите). Это не вызов для игры в гольф .
Натан Меррилл

Вы можете сохранить много байтов, используя только однозначные имена переменных. (Хотя это не имеет значения, потому что это не код-гольф)
большого

Woops! Редактировать это сейчас. На работе это то, что я получаю за скимминг!
Datastream

2
Пожалуйста, не играйте в гольф. Лучше, чтобы код читался, когда это не код-гольф.
mbomb007

Контроль середины доски означает не захватывание середины доски, а нападение на середину доски. Если вы хотите добавить к этому некоторую сложность, это может улучшить ваш счет.
Не то, чтобы Чарльз
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.