Algorithme génétique / réseau neuronal jouer au serpent ne s'améliore pas

j'essaie de créer un algorithme génétique pour former un réseau neuronal, dans le but de jouer au jeu du serpent.

le problème que j'ai, c'est que la condition physique des générations ne s'améliore pas, soit qu'elle reste immobile à la condition physique à laquelle on pourrait s'attendre de ne pas donner d'entrée dans le jeu, soit qu'elle ne s'aggrave qu'après la première génération. Je pense que c'est un problème avec le réseau neuronal, mais je ne sais pas ce que c'est.

Réseau De Neurones le programme d'installation

24 Noeuds D'Entrée

2 couches Cachées

8 noeuds par couche

4 noeuds de sortie (un pour chaque direction que le serpent peut prendre)

l'entrée est un tableau de toutes les directions que le serpent peut voir. Pour chaque direction il vérifie à quelle distance la distance est à un mur, un fruit, ou lui-même. Le résultat final est un tableau avec un la longueur de 3*8 = 24.

les poids et les biais sont des flotteurs aléatoires entre -1 et 1, générés lorsque le réseau est créé.

configuration de L'algorithme génétique

taille de la Population: 50000

Parents choisis par génération: 1000

garder le haut par génération: 25000 (nouvelle variable, voir de meilleurs résultats)

chance de Mutation par enfant: 5%

(j'ai essayé de nombreux rapports de taille différents, bien que je ne suis pas encore sûr ce qu'est un ratio typique.)

j'utilise le croisement à point unique. Chaque groupe de poids et de biais est croisé entre les parents et transmis aux enfants (un enfant pour chaque "version" du crossover).

j'utilise ce que je pense être la sélection à la roulette pour sélectionner les parents, je vais poster la méthode exacte ci-dessous.

la valeur adaptative d'un serpent est calculée avec: age * 2**score (plus d'info dans update), où l'âge est le nombre de tours que le serpent a survécu, et le score est la quantité de fruits récoltés.

Détails

voici quelques pseudo-codes pour essayer de résumer Comment mon algorithme génétique (devrait) fonctionner:

pop = Population(size=1000)

while True:  # Have yet to implement a 'converged' check
    pop.calc_fitness()

    new_pop = []

    for i in range(n_parents):

        parent1 = pop.fitness_based_selection()
        parent2 = pop.fitness_based_selection()

        child_snake1, child_snake2 = parent1.crossover(parent2)

        if rand() <= mutate_chance:
            child_snake.mutate()

        new_pop.append(child_snake1, child_snake2)

    pop.population = new_pop

    print(generation_statistics)
    gen += 1

Voici la méthode que j'utilise pour sélectionner un parent:

def fitness_based_selection(self):
    """
    A slection process that chooses a snake, where a snake with a higher fitness has a higher chance of being
    selected
    :return: The chosen snake's brain
    """
    sum_fitnesses = sum(list([snake[1] for snake in self.population]))

    # A random cutoff digit.
    r = randint(0, sum_fitnesses)

    current_sum = 0

    for snake in self.population:
        current_sum += snake[1]
        if current_sum > r:
            # Return brain of chosen snake
            return snake[0]

Il est intéressant de noter que self.population est une liste de serpents, où chaque serpent est une liste contenant le NeuralNet qui le contrôle, et le fitness que le réseau a atteint.

et voici la méthode pour obtenir une sortie d'un réseau à partir d'une sortie de jeu, car je soupçonne qu'il peut y avoir quelque chose que je fais mal ici:

def get_output(self, input_array: np.ndarray):
    """
    Get output from input by feed forwarding it through the network

    :param input_array: The input to get an output from, should be an array of the inputs
    :return: an output array with 4 values of the shape 1x4
    """

    # Add biases then multiply by weights, input => h_layer_1, this is done opposite because the input can be zero
    h_layer_1_b = input_array  + self.biases_input_hidden1
    h_layer_1_w = np.dot(h_layer_1_b, self.weights_input_hidden1)
    h_layer_1 = self.sigmoid(h_layer_1_w)  # Run the output through a sigmoid function

    # Multiply by weights then add biases, h_layer_1 => h_layer_2
    h_layer_2_w = np.dot(h_layer_1, self.weights_hidden1_hidden2)
    h_layer_2_b = h_layer_2_w + self.biases_hidden1_hidden2
    h_layer_2 = self.sigmoid(h_layer_2_b)

    # Multiply by weights then add biases, h_layer_2 => output
    output_w = np.dot(h_layer_2, self.weights_hidden2_output)
    output_b = output_w + self.biases_hidden2_output

    output = self.sigmoid(output_b)
    return output

quand on exécute le réseau neuronal manuellement, avec une version graphique du jeu activée, il est clair que le réseau ne change presque jamais de direction plus d'une fois. Cela me confond, car j'étais sous l'impression que si tous les poids et les biais sont générés au hasard, l'entrée serait traitée au hasard et donner une sortie aléatoire, à la place la sortie semble changer une fois au premier tour du jeu, puis Ne jamais changer de manière significative à nouveau.

lors de l'exécution de l'algorithme génétique, le plus haut fitness de chaque génération dépasse à peine le fitness que l'on attendrait d'un serpent sans entrée (dans ce cas 16), ce qui je suppose est corrélé à la question avec le réseau neural. Quand il dépasse, les prochaines générations reviendront à 16 de nouveau.

toute aide concernant son problème serait appréciée immensément, je suis encore nouveau dans ce domaine, et je le trouve vraiment intéressant. Je répondrai volontiers à tous les détails si besoin est. Mon code complet peut être trouvé ici si quelqu'un ose plonger dans cela.

mise à Jour:

j'ai changé quelques petites choses:

  • fixe la génération de poids / biais, auparavant ils produisaient seulement entre 0 et 1.
  • J'ai modifié ma méthode de crossover pour rendre deux enfants par jeu de les parents au lieu d'un seul.
  • a changé la fonction fitness pour être seulement égale à l'âge du serpent (à des fins de test)
  • a changé les variables de population

maintenant l'algorithme fonctionne mieux, la première génération trouve habituellement un serpent avec une valeur de fitness de 14-16, ce qui signifie que le serpent fait des virages pour éviter la mort, mais il ne descend presque toujours que de là. Le premier serpent aura effectivement atteint une tactique de tourner quand près de les bords est et nord/sud, mais jamais le bord ouest. Après la première génération, la condition physique a tendance à empirer, éventuellement en redescendant au niveau le plus bas possible. Je ne sais pas ce qui va mal, mais j'ai le sentiment que c'est peut-être quelque chose de grand que j'ai négligé.

mise à Jour #2:

J'ai pensé que je pourrais aussi bien mentionner certaines choses que j'ai essayé qui did not:

  • changé les noeuds par caché couche de 8 à 16, cela a fait les serpents effectuer considérablement pire.
  • a permis au serpent de se transformer de nouveau en lui-même, ce qui a également fait les serpents effectuer pire.
  • Large (je pense qu'ils sont grands, vous ne savez pas ce qu'est un standard de la pop est la taille de.) population d'environ 1 000 000, avec environ 1 000 parents, aucun changement positif.
  • 16 ou 32 noeuds par couche cachée, avaient apparemment peu ou pas d'impact.
  • fixe la fonction de mutation pour attribuer correctement les valeurs entre -1 et 1, pas d'impact perceptible.

mise à Jour #3:

j'ai changé certaines choses et j'ai commencé à voir de meilleurs résultats. Tout d'abord j'ai arrêté les fruits de la fraie pour simplifier le processus d'apprentissage, et au lieu de donner aux serpents une fitness qui était égale à leur âge (combien de tours/cadres ils ont survécu), et après avoir éteint la normalisation de la matrice d'entrée j'ai obtenu un serpent avec une fitness de 300! 300 est l'âge maximum d'un serpent peut avoir avant de mourir vieux âge.

cependant le problème existe toujours en ce qu'après les deux premières générations la condition physique va chuter, les 1-5 premières générations peuvent avoir une condition physique de 300 (parfois ils ne le font pas, et ont une condition physique faible à la place, mais je suppose que c'est à la taille de la population.), mais après cela le fitness des générations descendra vers le bas à ~20-30 et restera là.

aussi, si je rallume les fruits, les serpents ont de nouveau des malformations abyssales.Parfois, la première génération réaliser un serpent capable de se déplacer en boucle et donc obtenir une fitness de 300 sans ramasser les fruits, mais ce n'est presque jamais transféré à la génération suivante.

23
demandé sur Ben Wo 2018-05-12 22:22:50

2 réponses

j'ai remarqué que dans votre pseudo, lors de la création de chaque nouvelle génération, la génération des parents est complètement éliminée et seule la génération des enfants est retenue. Cela peut naturellement conduire à abaisser les niveaux de condition physique, car rien ne garantit que la progéniture aura des niveaux de condition physique comparables à ceux de la génération mère. Afin de s'assurer que les niveaux de fitness ne diminuent pas, vous devez soit fusionner les générations de parents et d'enfants et tailler les membres les plus faibles (que je recommande), ou vous pouvez exiger que la progéniture-fonction génératrice produit des descendants à moins que les parents (par de nombreux essais et erreurs).


si vous décidez de vous concentrer sur le générateur de progéniture, une façon de (un peu) garantir une meilleure progéniture est de mettre en œuvre la reproduction asexuée en ajoutant simplement une petite quantité de bruit à chaque vecteur de poids. Si le niveau de bruit est assez faible, vous pouvez générer la progéniture améliorée avec un taux de succès jusqu'à 50%. Des niveaux de bruit plus élevés, cependant, permettent une amélioration plus rapide et ils aident à sauter hors de l'optima locale, même si elles ont des taux de succès inférieurs à 50%.

4
répondu Default picture 2018-05-27 22:14:36

vous mutez seulement 5% de la population, pas 5% du "génome". Cela signifie que votre population sera fixée incroyablement rapidement - https://en.wikipedia.org/wiki/Fixation_ (population_genetics)