Архитектура нейронной сети для Гомоку
Эта нейронная сеть предназначена для игры в Гомоку, где нужно ставить фишки на поле 15x15. Она умеет:
- Предлагать лучший ход (policy)
- Оценивать позицию (value)
Как сеть "видит" поле
На вход подаётся поле 15x15 с двумя каналами:
- Первый канал — позиции текущего игрока
- Второй канал — позиции противника
Свёрточные слои (извлечение признаков)
conv1 = register_module("conv1",
torch::nn::Conv2d(torch::nn::Conv2dOptions(2, 64, 3).padding(1)));
conv2 = register_module("conv2",
torch::nn::Conv2d(torch::nn::Conv2dOptions(64, 64, 3).padding(1)));
conv3 = register_module("conv3",
torch::nn::Conv2d(torch::nn::Conv2dOptions(64, 128, 3).padding(1)));
Эти слои анализируют поле. Каждый слой ищет всё более сложные паттерны:
- conv1 — простые формы (линии, соседние клетки)
- conv2 — комбинации (угрозы, пары)
- conv3 — сложные структуры (почти выигрышные позиции)
В Conv2dOptions первые два параметра - это количество входных и выходных каналов. Третий параметр определяет размер сканирующего ядра (3 на 3). Padding=1 позволяет сохранять размер поля 15x15. Первому слою приходят на вход два канала - это позиции игрока, который должен походить следующим (первый канал, ноль либо еденица для каждой из 225 клеток) и во втором канале точно также передаются позиции игрока который ходил только что. Первый слой имеет 64 выхода, которые поступают на второй слой, и т.д. (см. схему).
Policy head (выбор хода)
policy_conv = register_module("policy_conv",
torch::nn::Conv2d(torch::nn::Conv2dOptions(128, 2, 1)));
policy_fc = register_module("policy_fc",
torch::nn::Linear(2 * 15 * 15, 225));
Этот блок отвечает за выбор хода:
- Сжимает информацию
- Преобразует её в 225 значений (по одному на каждую клетку)
Value head (оценка позиции)
value_conv = register_module("value_conv",
torch::nn::Conv2d(torch::nn::Conv2dOptions(128, 1, 1)));
value_fc1 = register_module("value_fc1",
torch::nn::Linear(15 * 15, 64));
value_fc2 = register_module("value_fc2",
torch::nn::Linear(64, 1));
Этот блок оценивает позицию:
- -1 — проигрыш
- 0 — равная позиция
- +1 — выигрыш
Прямой проход (forward)
std::pair<torch::Tensor, torch::Tensor> forward(torch::Tensor x) {
x = torch::relu(conv1->forward(x));
x = torch::relu(conv2->forward(x));
x = torch::relu(conv3->forward(x));
auto p = policy_conv->forward(x);
p = p.view({p.size(0), -1});
p = policy_fc->forward(p);
auto v = torch::relu(value_conv->forward(x));
v = v.view({v.size(0), -1});
v = torch::relu(value_fc1->forward(v));
if (this->is_training()) {
v = torch::dropout(v, 0.15, true);
}
v = torch::tanh(value_fc2-> forward(v));
return {p, v};
}
Что происходит:
- Сеть анализирует поле
- Policy предлагает ходы
- Value оценивает позицию
- Dropout уменьшает переобучение
Итог
Сеть одновременно думает "куда ходить" и "насколько всё хорошо". Это делает её гораздо сильнее простых алгоритмов.
Схема обучения
Описание функций и шагов обучения:
🔹 Self-play: партия нейронки против легаси или самой себя, собираем статистику ходов.
🔹 Forward Pass: сеть выдаёт вероятность каждого хода (policy logits) и оценку позиции (value).
🔹 Candidate Selection (TREE TRAIN): выбираются ходы по totalDirectChilds и фильтрам, вычисляется spread, dominance и определяется strongWinner. AdaptiveK выбирается на основе числа прямых дочерних узлов.
🔹 Policy Loss: сравнивает предсказанные вероятности с целевыми (из дерева/рейтинга ходов) и добавляет энтропийное сглаживание.
🔹 Value Loss: MSE между предсказанной оценкой позиции и целевой (nodeRating или результат партии).
🔹 Total Loss: комбинированный loss с коэффициентом beta и L2-регуляризацией весов. Корректируется для X/O ходов.
🔹 Backprop: градиентный спуск по Total Loss обновляет веса нейросети.
🔹 🔁 Процесс повторяется для каждой позиции партии и каждой self-play игры.
🔹 Важно: Warning "not existed node" появляется из-за exploration: нейронка выбирает редкий ход, которого нет в дереве легаси. Это нормально и не критично для обучения.
🔹 strongWinner обычно коррелирует с totalDirectChilds, показывая явного лидера среди кандидатов.
