Кружок Программирования

X&O : метрики качества алгоритма

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

Алгоритм устроен так, что оценка рейтинга каждой позиции постоянно уточняется, по мере расчёта дочерних позиций. Пока дочерние позиции не рассчитаны, то для оценки текущей используем количество двоек, троек и четвёрок для каждого из игроков (см. определения тут), помножая их на определённый вес и суммируя, как это определено в Evaluator.c Если же дочерние позиции уже рассчитаны, то выбираем дочерний ход с наиболее высоким рейтингом, меняем его знак на противополжный (т.е. помножаем на -1) и используем в качестве рейтинга текущей позиции. 

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

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

Очевидно, что при достаточно больших значениях N и R, такие значения будут характеризовать уровень брака (чем больше значение, тем хуже алгоритм). При большом значении N, ожидается что узел не должен отбраковываться (иначе получаем ситуацию, когда мы тратим много ресурсов на расчёт неперспективного узла).

 

  LAR 0 FRD LD  LD + FRD
Cull_400_8k / Cull_400_16k 155 / 296 588 / 175 632 / 180 823 / 263 1204 / 326
Cull_2k_8k / Cull_2k_16k 43 / 60 340 / 18 378 / 56 321 / 33 490 / 49
Cull_10k_8k / Cull_10k_16k 6 / 0 47 / 0 51 / 1 30 / 2 36 / 7
Cull_50k_8k / Cull_50k_16k 0 / 0 0 / 0 0 / 0 0 / 1 0 / 1
Cull_250k_8k / Cull_250k_16k 0 / 0 0 / 0 0 / 0 0 / 0 0 / 0
Update frequency 61,4% 51.7% 50% 77,8% 80,8%
Max childs updated 0.18M 1.9M 2.7M 5.8M 5.9M
Update count 51 75 67 524 611
Skip count 32 70 66 149 145
Avg Diff 63 49 55 6 5
Avg Abs Diff 151 131 144 196 224
max path length 25 20 20 13 13
Rating 3311 3817 3817 3339 3203

 

Фичи исследуемой программы:

  • LAR (Legacy Average Rating) -  при вычислении нового рейтинга узла в расчёт берётся также его старое значение (См. Expander.cpp, строка  cursor->rating = (TRating)(0.4*(double)oldRating-0.6*(double)max_rating);).
  • 0  - старая версия кода, но с отключенным LAR.
  • LD (Logariphmic Distribution) -  выбор узла на основе распределения: 50% дочерних узлов у лучшего варианта, 25% у второго, 12.5% у третьего и т.д. (См. функцию Builder::chooseNodeToExpand и этот коммит).
  • FRD (Far Rating Decrease) - если лучший рейтинг дочернего узла слишком велик по модулю, тогда немного уменьшаем его модуль при переносе на родительский узел. Таким образом, с высокорейтинговых узлов берём небольшое пенальти за каждый ход, чтобы повысить мотивацию для выигрывающего игрока выиграть максимально коротким путём, а для проигрывающего - максимально оттянуть окончание игры. (См. строчки №136-137 в Relator.cppif (max_rating < -27000) max_rating += 3; else ...).

 

Метрики:

  • Cull_N_R - наш основной набор метрик, см. выше.
  • Метрики для первого узла, который соответствует единственному ходу крестиков в центр поля. Тут надо понимать, что добавление дочерних узлов вызывает цепную реакцию обновления всех родителей. У непосредственного родителя увеличивается счётчик totalChilds, а также изменяется рейтинг - теперь он считается как рейтинг лучшего дочернего хода, но со знаком "минус", поскольку дочерний ход принадлежит оппоненту. Далее, у всех узлов-прародителей вызывается аналогичное обновление, увеличивающее счётчик totalChilds, а рейтинг может поменятся, только если новый вычисленный рейтинг их дочернего узла признан самым высоким из всех дочерних узлов.
    • Update frequency - как часто обновляется рейтинг первого узла, когда был обновлён рейтинг одного из его дочерних узлов.
    • Max childs updated - какое максимальное значение totalChilds было у первого узла, когда его рейтинг обновился в последний раз.
    • Update count - сколько раз обновился рейтинг первого узла после обновления рейтинга одного из его дочерних узлов.
    • Skip count - сколько раз НЕ обновился рейтинг первого узла после обновления рейтинга одного из его дочерних узлов.
    • Avg Diff - средняя разница между старым и новым рейтингами, при его обновлении.
    • Avg Abs Diff - средняя разница по модулю между старым и новым рейтингами, при его обновлении.
  • max path length - финальная высота дерева (после вычисление 6 миллионов узлов).
  • Rating - финальный рейтинг первого узла (после вычисление 6 миллионов узлов).

 

 

Вывод

 Изначально предполагалось что фича LD улучшит направленность перебора, и качество игры. Но, исходя из полученных значений, об этом сложно сделать однозначный вывод. Возможно, выбранные метрики не являются хорошим показателем, и качество игры нужно испытывать непосредственно, пытаясь обыграть алгоритм. Особое внимание также обращает то, что фича FRD, казалось бы, вообще не должна влиять на силу алгоритма, при расчёте равновесных позиций. Однако, как видим, она имеет сильное влияние на метрики, и на этот счёт требуется отдельный дополнительный анализ.

 

Улучшенные метрики

 

Хотя предыдущий эксперимент не смог дать внятного результата, сдаваться так сразу не стоит. Лучше проявить настоящий исследовательский дух. Ведь уже имея в руках Cull_N_R  метрики, достаточно легко их можно попытаться подстроить, для получения более интересных данных. Во-первых, изменим сетку для значений числа N. Нас более всего интересует пограничный интервал значений, при котором отбраковывание перестаёт происходить. Поэтому выберем значения 2k, 4500, 7k, 10k, 14k. Во-вторых, теперь будем считать узел отбракованным, только если его рейтинг упал ниже числа -1*R. Другими словами, мы теперь не будем работать с абсолютным значением рейтинга, то есть не будем отбраковывать узлы, рейтинг которых вырос выше чем R. В этом случае ожидается что отбраковано будет примерно вдвое меньше узлов чем в предыдущем эксперименте. Однако тут важным моментом является разобраться со случаями, где количество отбракованных узлов было равно единице.

 

  0 FRD LD  LD + FRD
Cull_2k_8k / Cull_2k_16k 167 / 15 199 / 44 271 / 24 338 / 29
Cull_4500_8k / Cull_4500_16k 106 / 1 90 / 4 30 / 2 108 / 15
Cull_7k_8k / Cull_7k_16k 67 / 0 72 / 4 13 / 3 43 / 1
Cull_10k_8k / Cull_10k_16k 26 / 0 23 / 1 13 / 2 15 / 0
Cull_14k_8k / Cull_14k_16k 21 / 0 28 / 0 17 / 1 20 / 8
Update frequency 51,7% 50,3% 77,8% 80,8%
Max childs updated 1.9M 2.7M 5.9M 5.9M
Update count 75 67 524 611
Skip count 70 66 149 145
Avg Diff 49 55 6 5
Avg Abs Diff 131 144 196 224
max path length 20 20 13 13
Rating 3817 3817 3339 3203

 

Выводы

  • Сразу обращает на себя внимание что при отсутствии механизма LD параметр Max childs updated остаётся далеко ниже шести миллионов. Это значит, что рейтинг первого хода перестаёт обновлятся уже после 1.9 М (или 2.7 М, при наличии FRD) вычисленных дочерних узлов. Таким образом, вычисление оставшихся не приводит к уточнению рейтинга текущего (первого) хода, что выглядит нехорошо; вычисление явно начинает идти по какому-то специфическому длинному пути сомнительной нужности, о чём нам также говорит увеличенный max path length.
  • Неожиданно оказывается что FRD слишком существенно влияет на метрики отбраковывания. Хотя этот механизм вовсе не призван на них влиять вообще, а нужен только для того, чтобы выигрывать было выгоднее как можно более коротким путём. Чем короче путь до выигрыша, тем меньше пенальти. Пенальти составляет 3 рейтинговых очка за ход. Поэтому выглядит логичным снизить его до минимального значения - единица, чтобы максимально ограничить его влияние на метрики (а значит и на выбор направлений переборов).
  • Параметр N недостаточно велик, было бы интересно исследовать значения около 20k-30k.

 

Дубль три

 

Итак, изменим фичу FRD на FRD-1. А также выберем другой набор для значений N:

 

  0 FRD-1 LD  LD + FRD-1
Cull_4k_8k / Cull_4k_16k 132 / 1 113 / 9 49 /3 148 / 15
Cull_7k_8k / Cull_7k_16k 67 / 0 72 / 4 13 / 3 43 / 1
Cull_10k_8k / Cull_10k_16k 26 / 0 23 / 1 13 / 2 15 / 0
Cull_14k_8k / Cull_14k_16k 17 / 0 20 / 0 17 / 0 20 / 4
Cull_20k_8k / Cull_20k_16k 4 / 0 8 / 0 0 / 1 0 / 4
Update frequency 51% 50% 77.8% 80.8%
Max childs updated 1.9M 2,7M 5.9M 5.9M
Update count 75 67 524 611
Skip count 70 66 149 145
Avg Diff 49 55 6 5
Avg Abs Diff 131 144 196 224
max path length 20 20 13 13
Rating 3817 3817 3339 3203

 

Вывод

  • LD режим выглядит явно предпочтительнее режима 0. Уже при количестве потомков от 7 тысяч и выше стабильно наблюдается снижение количества брака.
  • FRD по-видимому имеет неприятный side- эффект, заметно увеличивающий количество брака. Возможно, это баг. Эта фича требует дополнительных исследований и доработки.
  • Подобрать хорошие метрики качества - трудная задача, поэтому проще всего её поручить было бы искусственному интеллекту. Вот что ИИ смог рассказать: https://share.google/aimode/RhG5RvLdxIAYT09N4. Как видим, метод "Скорость сходимости и отсечения (Search Efficiency)" хотя и упоминается, но ничего не сказано конкретно об использованном в этой статье методе, хотя метрики Cull_N_R измеряют именно скорость отсечения.

 

 

М.О.