
- Desta vez, o jogo "Snake" é selecionado.
- Uma biblioteca para a rede Go foi criada.
- O princípio da aprendizagem, dependendo da "profundidade" da memória, é encontrado.
- Um servidor foi escrito para o jogo entre desenvolvedores.
A essência do jogo
Talvez muitas pessoas se lembrem do jogo "Snake", que era um aplicativo padrão nos telefones Nokia. Sua essência é a seguinte: uma cobra se move pelo campo, diminuindo se não encontrar comida ou aumentando se encontrar. Se uma cobra colide com um obstáculo, ela morre.
Mudei um pouco as regras: a cobra não morre se cair, mas simplesmente para, continuando a diminuir. Além disso, a cobra pode ser dividida ao meio. Se a cobra tem uma célula no corpo e ela não consegue encontrar comida em 10 movimentos, ela morre, transformando-se em comida.
Vamos treinar o bot que controla a cobra. Se a cobra se dividir, o bot receberá outra cobra no controle, que por sua vez também poderá se dividir.
O experimento com as cobras do biólogo cibernético Mikhail Tsarkov é tomado como base.
Rede neural
Como parte da tarefa, uma biblioteca para a rede neural foi escrita na linguagem Go. Estudando o trabalho da rede neural, uso o diário em vídeo
foo52ru e o livro de Tariq Rashid - Criar uma rede neural.
A função
CreateLayer(L []int)
cria uma rede neural com o número necessário de camadas e seu tamanho. Em cada camada, exceto a última, um neurônio de deslocamento é adicionado. Alimentamos dados para a primeira camada e obtemos o resultado da última camada.
Um exemplo:
CreateLayer([]int{9, 57, 3, 1})
Aqui criamos uma rede neural com nove entradas. Duas camadas ocultas de 57 e 3 neurônios e um neurônio para obter o resultado. Os neurônios de deslocamento são adicionados automaticamente pelo sinal de adição àqueles que definimos.
A biblioteca permite que você:
- Envie dados para a entrada da rede.
- Obtenha o resultado acessando a última camada.
- Peça as respostas certas e conduza o treinamento ajustando os pesos dos laços.
Os pesos iniciais das ligações são dados por valores aleatórios próximos de zero. Para a ativação, usamos a função logística.
Bot training
O bot recebe um campo quadrado 9x9 na entrada, no meio do qual está a cabeça de uma cobra. Assim, nossa rede neural terá 81 entradas. A ordem das células alimentadas na entrada não importa. Durante o treinamento, a rede “descobrirá” a si mesma, onde está localizado.
Para indicar obstáculos e outras cobras, usei valores de -1 a 0 (não incluso). As células vazias foram designadas com um valor de 0,01 e comida 0,99.
Na saída da rede neural, 5 neurônios foram usados para ações:
- mova para a esquerda ao longo do eixo X;
- para a direita;
- o eixo y;
- para baixo;
- dividido ao meio.
O movimento do bot foi determinado pelo neurônio, que tem o maior valor na saída.
Etapa 0. Randomizador
Primeiro, um randomizador de bot foi criado. Então eu chamo um bot que anda aleatoriamente. É necessário verificar a eficácia da rede neural. Com o treinamento adequado, uma rede neural deve superá-la facilmente.
Etapa 1. Aprendendo sem memória
Após cada movimento, ajustamos os pesos das ligações para o neurônio de saída que indicou o valor mais alto. Não tocamos outros neurônios de saída.
Os seguintes valores foram dados para o treinamento:
- comida encontrada: 0.99
- fez um movimento em qualquer direção: 0,5
- perdeu uma célula do corpo sem encontrar comida (10 movimentos são dados para isso): 0.2
- fica parado (atinge um obstáculo ou fica preso): 0,1
- parado, possuindo uma célula do corpo: 0,01
Após esse treinamento, os bots rapidamente começaram a vencer o randomizador, e eu estabeleci a tarefa: criar bots que os superassem.
Teste A / B
Para realizar essa tarefa, foi criado um programa que divide as cobras em duas partes, dependendo da configuração da rede neural. No campo, foram produzidas 20 cobras de cada configuração.
Todas as cobras controladas por um bot tinham a mesma rede neural. Quanto mais cobras em sua administração e mais frequentemente elas enfrentavam tarefas diferentes, mais rápido o treinamento era. Se, por exemplo, uma cobra aprendeu a evitar impasses ou se dividiu ao meio quando chegou a um beco sem saída, então automaticamente todas as cobras desse bot adquiriram essas habilidades.
Alterando a configuração da rede neural, você pode obter bons resultados, mas isso não é suficiente. Para melhorar ainda mais o algoritmo, decidi usar a memória para vários movimentos.
Etapa 2. Aprendendo com memória
Para cada bot, criei uma memória para 8 movimentos. O estado do campo e o movimento sugerido pelo bot foram registrados na memória. Depois disso, fiz ajustes nos pesos para todos os oito estados que precederam a mudança. Para isso, usei um único fator de correção, independente da profundidade da viagem. Assim, cada movimento levou ao ajuste dos pesos não uma vez, mas oito.
Como esperado, os bots de memória começaram a superar rapidamente os que treinavam sem memória.
Etapa 3. Diminua o coeficiente de correção, dependendo da profundidade da memória
Em seguida, tentei reduzir o fator de correção, dependendo da profundidade da memória. Para a última jogada realizada, foi estabelecido o maior coeficiente de ajuste dos pesos. No curso que o precedeu, o fator de correção diminuiu e assim por diante na memória.

Uma diminuição linear no coeficiente de correção, dependendo da profundidade da memória, levou ao fato de que novos bots começaram a superar aqueles que usavam um único coeficiente.
Em seguida, tentei usar a redução logarítmica do fator de correção. O coeficiente diminuiu pela metade, dependendo da profundidade da memória de cada movimento. Assim, movimentos que foram feitos "há muito tempo" tiveram um impacto significativamente menor no aprendizado do que movimentos "novos".
Os bots com redução logarítmica no coeficiente de correção começaram a derrotar os bots com uma relação linear.
Servidor para bots
Como se viu, melhorar o nível de "bombeamento" de bots pode ser infinito. E decidi criar um servidor no qual os desenvolvedores pudessem competir entre si (independentemente da linguagem de programação) escrevendo um algoritmo eficaz para o Snakes.
Protocolo
Para obter autorização, você precisa enviar uma solicitação GET para o diretório "jogo" e especificar um nome de usuário, por exemplo:
.../game/?user=masterdak
Em vez de "...", você precisa especificar o endereço do site e a porta em que o servidor está implantado.
Em seguida, o servidor emitirá uma resposta no formato JSON indicando a sessão:
{"answer":"Hellow, masterdak!","session":"f4f559d1d2ed97e0616023fb4a84f984"}
Depois disso, você pode solicitar um mapa e as coordenadas da cobra no campo, adicionando uma sessão à solicitação:
.../game/?user=masterdak&session=f4f559d1d2ed97e0616023fb4a84f984
O servidor exibirá algo como isto:
{ "answer": "Sent game data.", "data": { "area": [ ["... ..."] ], "snakes": [ { "num": 0, "body": [ { "x": 19, "y": 24 }, { "x": 19, "y": 24 }, { "x": 19, "y": 24 } ], "energe": 4, "dead": false } ] } }
O campo da
área indicará o estado do campo de jogo com os seguintes valores:
0
Isso será seguido por uma matriz com cobras que estão sob seu controle.
O corpo da cobra está na matriz do
corpo . Como você pode ver, todo o corpo da cobra (incluindo a cabeça - a primeira célula) no início está na mesma posição "x": 19, "y": 24. Isso se deve ao fato de que, no início do jogo, as cobras saem do buraco, definido por uma célula no campo . Além disso, as coordenadas do corpo e da cabeça serão diferentes.
As estruturas a seguir (um exemplo em Go) definem todas as opções de resposta do servidor:
type respData struct { Answer string Session string Data struct { Area [][]int Snakes []struct { Num int Body []Cell Energe int Dead bool } } } type Cell struct { X int Y int }
Em seguida, você precisa enviar a movimentação que a cobra faz adicionando a solicitação GET, por exemplo:
...&move=u
u - significa comando;
d - baixo;
l - para a esquerda;
r - para a direita;
/ - metade.
O comando para várias cobras (por exemplo, para sete) terá a seguinte aparência:
...&move=ud/urld
Um personagem - um time. A resposta deve conter um comando para todas as cobras que estão sob seu controle. Caso contrário, algumas cobras podem não receber um comando e continuarão com a ação antiga.
O campo é atualizado em intervalos de 150 ms. Se nenhum comando for recebido dentro de 60 segundos, o servidor fechará a conexão.
Referências
Para evitar o efeito de habra, para quem estiver interessado em ver, envie-me uma mensagem. Em resposta, enviarei o endereço IP do meu servidor. Ou você pode implantar seu servidor usando o código-fonte do programa.
Não sou especialista em programação nem em redes neurais. Portanto, eu posso cometer erros. Eu espalhei o código "como está". Ficaria feliz se desenvolvedores mais experientes mostrarem os erros cometidos.
- Biblioteca para a rede neural junto com o jogo "Tic Tac Toe"
- Snake Master - Servidor
- Mestre das Cobras - Bot
- Snakeworld2
UPDFaça o upload temporário
do endereço IP do servidor . Agora, apenas um randomizador de bot (SnakeBot0) é lançado lá. Espero que o servidor não seja tão rápido.