Практикум по нейронным сетям для шахмат №2
08 Апр 2025 03:17 #122
booot76 wrote:
Причин может быть много. "самая правильная" версия - ту что керас дает.
Алекс, пока я тут у себя ищу ошибки, расскажите пожалуйста о эндшпильных таблицах, и давайте построим реальную таблицу.
Я думаю всем будет познавательно и интересно услышать это от знающего человека.
Практикум по нейронным сетям для шахмат №2
08 Апр 2025 08:36 #123
А что ж о них рассказывать? Вы ж уже принцип их построения сами руками щупали на неподвижном короле . Все примерно то же самое и на "настоящих" эндшпильных таблицах. Другое дело что там гораздо больше обьемы данных (растут экспоненциально с увеличением фигур на доске) и чтобы оптимизировать время их вычисления и занимаемое место в памяти применяют различные трюки, связанные с симметрией доски. Например : в самом топорном случае наши короли образуют 64*64=4096 комбинаций взаимного расположения , включая нелегальные. Однако можно использовать тот факт, что в беспешечных эндшпилях много симметрий и одного короля (допустим слабейшего) можно ставить всего лишь в треугольник, образуемый полями a1-d1-d4 (10 полей), а у другого короля перебрать руками все легальные его положения при каждом из этих 10 расположений первого короля. Если еще обратить внимание на тот факт, что при положении слабейшего короля на главной диагонали (a1 b2 c3 d4) возникает еще одна симметрия , то общее количество необходимых нам легальных комбинаций двух королей составляет всего 462 (я уже щупал их руками). Вместо 4096 - 462. Почти в 9 раз меньше. Соответственно в 9 раз быстрее и в 9 раз меньше требования к памяти. С расположением остальных фигур можно уже даже не париться (хотя Налимов и там парится и многое исключает, делая файлы еще меньшими).
Соответственно:
3- фигурные безпешечные эндшпили (KRK KQK) у нас будут занимать примерно 2х30 тысяч позиций (для таблиц с ходом белых и ходом черных)
4- фигурные безпешечные эндшпили ( там интересны KRKN KRKB KBNK KBBK и, конечно, вишенка на торте KQKR) будут занимать 2х2 миллиона позиций
5-фигурные , соответственно, 2х120 миллионов позиций Все это легко строится даже без оптимизация по расчет на компах с 512 МБ памяти.
6-фигурки будут занимать уже 2ч8 миллиардов позиций. И вот тут уже для учебного примера , похоже, предел. Тут уже надо будет оптимизировать, чтобы хотя бы половину памяти освободить. Но даже в этом случае память для расположения 8 миллиардов позиций надо иметь. Учитывая что в разных метриках надо от 1 до 2 байтов на позицию, то это либо комп с 8 либо с 16 ГБ. Если же есть 32 ГБ, то тогда вообще не паримся и используем тот же алгоритм что и для младших. Дальше уже нужен высший пилотаж, чтобы вычисление производить по кусочкам, подгружая и сохраняя их на диск. Тоже не то, чтобы сложно, но времени придется закопать много.
Ну и метрик для эндшпильных таблиц существует несколько :
DTM - ранги "до мата". Использует Налимов. таблицы будет показывать оптимальную игру до чистого мата на доске. Требует 2 байта на позицию в 6-фигурках и самые сложные в вычислении. Там замысловатые есть выигрыши в 250+ ходов (500+полуходов).
DTC - ранги до перехода в младший эндшпиль (для беспешечных - до взятия фигуры противника). Тоже не сахар, но строить проще.
DTZ50 - ранги до "правила 50 ходов". Показывают выигрыш с точки зрения спортивных правил. Строятся до правила 50 ходов - все остальное ничья. Тут хватит и 1 байта на позицию.
Конечно для многофигурных лучше использовать многопоточные вычисление. Задача хорошо распараллеливается на потоки, если , конечно, уместить все таблицы в память, чтобы все потоки имели к ней доступ и опрашивали одновременно.
Практикум по нейронным сетям для шахмат №2
09 Апр 2025 13:44 #125
Добавил Sigmoid в реализацию от Автор: Andrew M. Omutov
Warning: Spoiler![ Click to expand ][ Click to hide ]
interface
uses
System.SysUtils;
...
function MatrixSigmoid(Value : MatrixElement): MatrixElement ;
begin
MatrixSigmoid := 1 / (1 + Exp(-Value));
end;
function GetMatrixSigmoid(MPtr : MatrixPtr) : MatrixPtr;
var
TempPtr : MatrixPtr;
i,j : integer;
begin
if MPtr <> nil then with MPtr^ do begin
TempPtr:= CreateMatrix(MatrixRow,MatrixCol);
if TempPtr = nil then begin
GetMatrixSigmoid:= nil;
Exit;
end;
for i:= 1 to MatrixRow do
for j:= 1 to MatrixCol do
SetMatrixElement(TempPtr,i,j,MatrixSigmoid(GetMatrixElement(MPtr,i,j)));
GetMatrixSigmoid:= TempPtr;
end else GetMatrixSigmoid:= nil;
end;
Вроде работает ...
CoLab Part of the message is hidden for the guests. Please log in or register to see it.
Warning: Spoiler![ Click to expand ][ Click to hide ]
Практикум по нейронным сетям для шахмат №2
09 Апр 2025 16:10 #126
Ну,значит, поздравляю Дошли до конца. Все же советую вместо чужой реализации написать элементарную собственную функцию перемножения матриц на десяток строчек по моему образцу.
Теперь можете в Делфи пробовать как работает сетка. Формально весь путь разработки в этом учебном примере мы прошли до конца и достаточно быстро, как я и обещал.
Практикум по нейронным сетям для шахмат №2
10 Апр 2025 03:47 #127
booot76 wrote:
Ну,значит, поздравляю Дошли до конца
Я, честно сказать запутался, что то не то наворочил
Не добился, что бы в 3 вариантах получать один и тот же ответ.
1 вариант на основе загруженной обученной модели (CoLab)
2 вариант расчет вручную (CoLab)
3 вариант расчет вручную (Delphi)
Пока не найду ошибки - двигаться дальше не могу
Практикум по нейронным сетям для шахмат №2
10 Апр 2025 08:35 #129
booot76 wrote:
Хм... А где не совпадает с керасом? Вроде ж на скринах было совпадение по 3 ручным примерам? Или я не так понял?
Это был только первый слой для одной позиции, а после 3 слоев какаято ерунда.
Я вот смотрю может где то вместо A*B получилось B*A.
В общем то надо сначала все проверять.
У меня так.
1. Керас передает в делфи, побайтно и одновременно сохранил все матрицы в txt файл для контроля преобразования байтов в float в делфи
2. Делфи принимает байты и преобразует во float и попутно в txt, чтобы сравнить визаульно матрицы весов и биосов.
3. Потом numpy принимает txt от делфи и вручную вычисляет всю сеть.
4. Делфи тоже вычисляет
В Итоге получилась фигня
Прим
Я сейчас смотрю как сравнить две матрицы, грубо говоря одну смотришь в керас, другую в делфи.
Никак нельзя сравнить
Даже как текст, точность разная
Практикум по нейронным сетям для шахмат №2
10 Апр 2025 10:31 #131
Да уж. потому и говорил что это достаточно противное занятие. Надо действительно послойно сравнивать. Расхождение в "копейках" будет - там какие-то особенности представления и округления вещественных чисел. Я у себя еще когда-то процедуру в питоне сделал перемножения матриц, с ней сравнил керас и только после совпадения уже послойно результаты процедуры сравнивал с Делфи. Чтобы удобнее было послойно сравнивать и видеть где ошибка.
Практикум по нейронным сетям для шахмат №2
11 Апр 2025 09:37 #134
Сделал!
Во всех 3 вариантах Один результат.
Warning: Spoiler![ Click to expand ][ Click to hide ]
Правда надо протестировать в разных случаях и добавить в GUI, чтобы подвигать фигурами.
Ну и вот окончательный вариант(без ошибок)
Warning: Spoiler![ Click to expand ][ Click to hide ]
import tensorflow as tf
import keras
import numpy as np
import sys
from keras.layers import Input,Dense
from keras.models import Sequential
from keras.src.legacy.saving import legacy_h5_format
print('tensorflow:' + tf.__version__)
print('keras:' + keras.__version__)
print('numpy:' + np.__version__)
x = np.zeros((223944, 192))
y = np.zeros(223944)
pd = np.zeros((1, 192))
b0 = np.zeros((1, 256))
d0 = np.zeros((192,256))
b1 = np.zeros((1, 16))
d1 = np.zeros((256,16))
b2 = np.zeros((1, 1))
d2 = np.zeros((16,1))
f = open('/content/drive/MyDrive/Colab Notebooks/training_data.txt', 'r')
i = -1
while True:
lines=f.readline()
if not lines:
f.close()
break
mylist=lines.split()
wk=int(mylist[0])
wq=int(mylist[1])
bk=int(mylist[2])
i = i + 1
x[i][wk] = 1
x[i][wq+64] = 1
x[i][bk+128] = 1
res=int(mylist[3])
# 1-(Rang/100)
# res = 255 - невозможная,
# 254 уже мат на доске ход черных,
# 253 - пат на доске ход черных,
# 252 битая ничья ход черных и они забирают незащищенного ферзя,
# 0 - ничья, остальное - количество полуходов до мата
y[i] = 1 - (res/100)
if (res == 252) or (res == 253) or (res == 0):
y[i] = 0
if (res == 254):
y[i] = 1
model = legacy_h5_format.load_model_from_hdf5('/content/drive/MyDrive/Colab Notebooks/model_150.h5', custom_objects={'mse' : 'mse'})
def sigmoid(x):
return 1 / (1 + np.exp(-x))
f = open('/content/drive/MyDrive/Colab Notebooks/pos_dat.txt', 'r')
i = -1
while True:
lines=f.readline()
mylist=lines.split()
#print(mylist)
if not lines:
f.close()
break
for j in range(0,len(mylist)):
i = i + 1
if (int(mylist[j]) == 1):
pd[0][i] = 1
i = -1
f = open('/content/drive/MyDrive/Colab Notebooks/b0.txt', 'r')
while True:
lines=f.readline()
mylist=lines.split()
#print(mylist)
if not lines:
f.close()
break
for j in range(0,len(mylist)):
i = i + 1
b0[0][i] = np.asarray((mylist[j]).replace(',', '.')).astype(np.float32)
i = 0
z = 0
f = open('/content/drive/MyDrive/Colab Notebooks/d0.txt', 'r')
while True:
lines=f.readline()
mylist=lines.split()
#print(mylist)
if not lines:
f.close()
break
newarr = np.array_split(mylist, 4)
for n in range(0,4):
#print(newarr[n])
#d0[i][z] = i
d0[i][z] = np.asarray((newarr[n][0]).replace(',', '.')).astype(np.float32)
z = z + 1
if (z==256):
z = 0
i = i + 1
i = -1
f = open('/content/drive/MyDrive/Colab Notebooks/b1.txt', 'r')
while True:
lines=f.readline()
mylist=lines.split()
#print(mylist)
if not lines:
f.close()
break
for j in range(0,len(mylist)):
i = i + 1
b1[0][i] = np.asarray((mylist[j]).replace(',', '.')).astype(np.float32)
i = 0
z = 0
f = open('/content/drive/MyDrive/Colab Notebooks/d1.txt', 'r')
while True:
lines=f.readline()
mylist=lines.split()
#print(mylist)
if not lines:
f.close()
break
newarr = np.array_split(mylist, 4)
for n in range(0,4):
#print(newarr[n])
#d0[i][z] = i
d1[i][z] = np.asarray((newarr[n][0]).replace(',', '.')).astype(np.float32)
z = z + 1
if (z==16):
z = 0
i = i + 1
i = -1
f = open('/content/drive/MyDrive/Colab Notebooks/b2.txt', 'r')
while True:
lines=f.readline()
mylist=lines.split()
#print(mylist)
if not lines:
f.close()
break
for j in range(0,len(mylist)):
i = i + 1
b2[0][i] = np.asarray((mylist[j]).replace(',', '.')).astype(np.float32)
i = 0
z = 0
f = open('/content/drive/MyDrive/Colab Notebooks/d2.txt', 'r')
while True:
lines=f.readline()
mylist=lines.split()
#print(mylist)
if not lines:
f.close()
break
newarr = np.array_split(mylist, 4)
for n in range(0,4):
#print(newarr[n])
#d0[i][z] = i
d2[z][i] = np.asarray((newarr[n][0]).replace(',', '.')).astype(np.float32)
z = z + 1
if (z==16):
z = 0
i = i + 1
np.set_printoptions(threshold=sys.maxsize)
lay0=np.dot(np.array([x[165657]]),d0)
lay0=np.add(lay0,b0)
layer0 = sigmoid(lay0)
lay1=np.dot(layer0,d1)
lay1=np.add(lay1,b1)
layer1 = sigmoid(lay1)
lay2=np.dot(layer1,d2)
lay2=np.add(lay2,b2)
layer2 = sigmoid(lay2)
print(layer2)
print('---')
print(model.predict(np.array([x[165657]])))
print('END')
Warning: Spoiler![ Click to expand ][ Click to hide ]
Практикум по нейронным сетям для шахмат №2
11 Апр 2025 16:43 #135
Правильно ли я понимаю то, как выбрать ход на основании ответа сети.
1.Генерируем ходы белого ферзя
2.Делаем ход ферзем.
3.Запрашиваем сеть об оценке новой позиции
4.Отменяем ход ферзем
5.Переходим к п.2
6.Выбираем ход с наибольшей оценкой.
Практикум по нейронным сетям для шахмат №2
12 Апр 2025 10:02 #137
Да - такое может быть. Сеть все-таки не так точна как эндшпильные таблицы чтобы берез перебора играть безошибочно. Но все же проверьте в керасе еще раз в этих позициях оценки ходов ферзя и действительно ли сеть дает максимальные оценки не самым сильным продолжениям.
Если да - то тут уже придется писать перебор на какую-то глубину. И уж потом оценку из нейросети. Ничего точнее эндшпильных таблиц еще не придумали
Но я даже рад, что нужно сделать перебор на какую то глубину, что то вроде minimax/
minimax(player,board)
if(game over in current board position)
return winner
children = all legal moves for player from this board
if(max's turn)
return maximal score of calling minimax on all the children
else (min's turn)
return minimal score of calling minimax on all the children
Немного прикоснувшись к магии нейросети на простом примере, благодаря booot76, стало чуть более понятно дальнейшее движение человеческой мысли в программировании шахматных движков
Практикум по нейронным сетям для шахмат №2
13 Апр 2025 18:12 #141
booot76 wrote:
Что бы вам было еще интересно на тему?
Конечно мне было бы очень интересно узнать, как вы реализовали у себя в движке нейросеть.
Может быть попробуем простой движок с нейросетью?
Ну пусть это будет не движок, а какой нибудь bot двигающий фигуры.
Прим.
Неприкосновенного короля оставим в покое.
PS
Поэкспериментировал с разными штуками - Dropout и BatchNormalization()
from keras.layers import Input,Dense,Dropout
...
model = Sequential()
model.add(Input(shape=(192,)))
model.add(Dense(256, activation='sigmoid'))
model.add(Dropout(0.4))
...
Warning: Spoiler![ Click to expand ][ Click to hide ]
#patience: Количество периодов без улучшений, после которых обучение будет остановлено. Значение по умолчанию равно 0.
#verbose: режим детализации, 0 или 1. Режим 0 отключен, а режим 1 отображает сообщения, когда выполняется действие обратного вызова. Значение по умолчанию равно 0.
callbacks = [
keras.callbacks.EarlyStopping(monitor="loss", min_delta=0.0001, patience=10, verbose=1)
]
...
model.summary()
model.fit(x, y, epochs=400, callbacks=callbacks, shuffle=True)
Практикум по нейронным сетям для шахмат №2
14 Апр 2025 07:02 #142
Ну, вроде как , неприкосновенного мы и вдоль и поперек уже обьездили. Эндшпильные таблицы оставляем пока на потом. Теперь к движку. Дело в том, что даже простейший движок (с протоколом обмена для шахматных оболочек) это достаточно сложная и сбалансированная система. Я могу помочь в создании и прикручивании к движку нейросети (хотя это тоже сама по себе очень кропотливая и "вредная" задача), но для этого надо, чтобы какой-то простейший движок уже был. В котором сейчас оценочная функция какая-то другая, а вместо нее мы поэтапно внедряем нейросеть. Тогда эту задачку можно нарезать на куски и прожевать. Просто создавать вообще ВЕСЬ движок с нуля это , блин, надолго. Основная проблема для решения в полноценном движке это то самое NNUE. Которое сложно как в понимании так и в реализации. И если в понимании я помогу достаточно просто , обьяснняя на пальцах, то вот в Делфи реализация NNUE средствами Intel SIMD (AVX2) противна до безобразия. Там до сих пор нет в компиляторе реализации ассемблера с этими командами. У себя в движке (можете посмотреть файл Unn.pas) я ручками шестнадцатиричные коды ассемблерных команд вставляю. Их - понимает . Это как лобзиком в скале статую выпиливать.
Что же касается бота, то тут несколько проще может (это в самом первом приближении движок). В этом случае большинство функций движка обрезаются под корень и остается по сути :
1. Функция полного перебора минимаксом на какое-то маленькое количество полуходов (3-5-7) с самой простой моделью форсированной игры. Без всяких дополнительных техник перебора и хеш-таблиц.
2. На основе моего движка мы генерируем миллионов 100-200 позиций (для бота - с головой) и на их основе делаем нейросетку очень похожую на ту что мы уже освоили.
3. прикручиваем одно к другому и получаем в принципе вполне себе играющего бота. В этом случае для относительно небольшого количества позиций (в маленьком переборе их немного) мы сможем реализовать NNUE в учебном виде даже в паскале. Будет сильно медленнее, но для понимания что и как, для простой отладки и для последующего штурма уже в ассемблере будет очень неплохой шаг. Скорее всего этого будет достаточно для бота, который сможет обыграть большинство любителей.
Практикум по нейронным сетям для шахмат №2
14 Апр 2025 07:04 #143
Что касается "штук", то их конечно в нейросетях много . Весь вопрос как их потом вытащить в Делфи, чтобы сравнить с результатами питона. Нам же эти сети потом не в питоне по хорошему использовать надо?
Практикум по нейронным сетям для шахмат №2
14 Апр 2025 07:07 #144
booot76 wrote:
но для этого надо, чтобы какой-то простейший движок уже был
Простейший движок есть, я писал его по примеру Максима Коржа.
Правда прошло уже какое то время после его написания и надо что то вспомнить.
Просто потом я переключился на решение шахматных задач, в общем то сейчас моя прога достаточно неплохо их решает
Warning: Spoiler![ Click to expand ][ Click to hide ]
Практикум по нейронным сетям для шахмат №2
14 Апр 2025 07:19 #145
Отлично. Можем взять этот движок за основу. А могу я , опять, же задней левой какой-то учебный пример движка с генератором ходов на MAILBOX и простым перебором смастерить. Потом для генерации эндшпильных таблиц теоретически может пригодиться. Пока все эти битборды нам не нужны. Когда есть понимание что делать и как проверять все примочки можно добавлять быстро и качественно. А пока понимания нет любая чужая теория, даже самая передовая, будет непонятной.
Практикум по нейронным сетям для шахмат №2
14 Апр 2025 07:31 #147
Начинать всегда надо с начала. С ревизии что есть в движке. Для наших целей там должно быть следующее :
1. Работающий генератор ходов.
2. Модель минимаксного перебора на заданное количество полуходов. С реализацией обнаружения мата.
3. Простейшая модель форсированной игры (после минимаксного перебора).
4. Какая-то совсем уж элементарная оценка позиции. Хотя бы в виде материального соотношения.
Все это движку уже позволяет двигать фишки и даже находить простые комбинации (решать задачи на мат).
Что пока НЕ нужно :
1. Хештаблицы (с ними гемора много при отладке)
2. Навороченые техники перебора (Null-Move, Negascout, отсечения и т.п.) Будет просто "шуметь" и мешать.
Критерием проведенной ревизии есть рабочий прототип движка, управляя которым вы можете задать ему количество полуходов для перебора и отследить в отладчике как он по узлам основного минимакса переходит в модель форсированной игры и там оценивает конечную позицию самой простейшей функцией оценки. При этом находит мат (в 1, 2 , 3 хода в хависимости от полуходов для перебора которые вы ему даете).
Практикум по нейронным сетям для шахмат №2
14 Апр 2025 13:18 #149
Просмотрел исходник. Конечно, черт ногу сломит (процедуры разного уровня интеграции все свалены в кучу), но в целом у вас в принципе все кубики из которых состоит движок имеются. Много замечаний по реализации (особенно вопросы к использованию хештаблиц), но от этого всего можно отталкиваться. У вас там даже perft есть и вы даже нахождениями магик-множителей для генерирования ходов дальнобойных фигур озаботились - я оценил и поулыбался . Если perft тесты для известных позиций у вас выдают правильные цифры, то постепенно можем в этом всем порядок навести , в кучу собрать и нейросеть прикрутить.
Практикум по нейронным сетям для шахмат №2
14 Апр 2025 13:44 #150
Либо.... с нуля методологически верно начать писать простой, хорошо откомментированный движок постепенно, изначально нацеленный на использование нейросетей. У вас там половина исходников - инициализация разных битбордов и прочих сложных структур. Но ими следует заниматься только тогда, когда ВСЕ системы движка планируется писать максимально оптимизированно "на скорость". У вас же , условно, половина деталек в машине сейчас от болида формулы-1, а половина - от велосипеда. Ни разогнаться, ни на лифте домой отвезти