你要的答案或许都在这里:小鹏的博客目录

代码下载:Here。

很久以前微信流行过一个小游戏:打飞机,这个游戏简单又无聊。在2017年来临之际,我就实现一个超级弱智的人工智能(AI),这货可以躲避从屏幕上方飞来的飞机。本帖只使用纯Python实现,不依赖任何高级库。

本文的AI基于neuro-evolution,首先简单科普一下neuro-evolution。从neuro-evolution这个名字就可以看出它由两部分组成-neuro and evolution,它是使用进化算法(遗传算法是进化算法的一种)提升人工神经网络的机器学习技术,其实就是用进化算法改进并选出最优的神经网络。

neuro-evolution

定义一些变量:

[python] view plain copy

  1. import math

  2. import random

  3. # 神经网络3层, 1个隐藏层; 4个input和1个output

  4. network = [4, [16], 1]

  5. # 遗传算法相关

  6. population = 50

  7. elitism = 0.2

  8. random_behaviour = 0.1

  9. mutation_rate = 0.5

  10. mutation_range = 2

  11. historic = 0

  12. low_historic = False

  13. score_sort = -1

  14. n_child = 1

定义神经网络:

[python] view plain copy

  1. # 激活函数

  2. def sigmoid(z):

  3. return 1.0/(1.0+math.exp(-z))

  4. # random number

  5. def random_clamped():

  6. return random.random()*2-1

  7. # "神经元"

  8. class Neuron():

  9. def __init__(self):

  10. self.biase = 0

  11. self.weights = []

  12. def init_weights(self, n):

  13. self.weights = []

  14. for i in range(n):

  15. self.weights.append(random_clamped())

  16. def __repr__(self):

  17. return 'Neuron weight size:{}  biase value:{}'.format(len(self.weights), self.biase)

  18. # 层

  19. class Layer():

  20. def __init__(self, index):

  21. self.index = index

  22. self.neurons = []

  23. def init_neurons(self, n_neuron, n_input):

  24. self.neurons = []

  25. for i in range(n_neuron):

  26. neuron = Neuron()

  27. neuron.init_weights(n_input)

  28. self.neurons.append(neuron)

  29. def __repr__(self):

  30. return 'Layer ID:{}  Layer neuron size:{}'.format(self.index, len(self.neurons))

  31. # 神经网络

  32. class NeuroNetwork():

  33. def __init__(self):

  34. self.layers = []

  35. # input:输入层神经元数 hiddens:隐藏层 output:输出层神经元数

  36. def init_neuro_network(self, input, hiddens , output):

  37. index = 0

  38. previous_neurons = 0

  39. # input

  40. layer = Layer(index)

  41. layer.init_neurons(input, previous_neurons)

  42. previous_neurons = input

  43. self.layers.append(layer)

  44. index += 1

  45. # hiddens

  46. for i in range(len(hiddens)):

  47. layer = Layer(index)

  48. layer.init_neurons(hiddens[i], previous_neurons)

  49. previous_neurons = hiddens[i]

  50. self.layers.append(layer)

  51. index += 1

  52. # output

  53. layer = Layer(index)

  54. layer.init_neurons(output, previous_neurons)

  55. self.layers.append(layer)

  56. def get_weights(self):

  57. data = { 'network':[], 'weights':[] }

  58. for layer in self.layers:

  59. data['network'].append(len(layer.neurons))

  60. for neuron in layer.neurons:

  61. for weight in neuron.weights:

  62. data['weights'].append(weight)

  63. return data

  64. def set_weights(self, data):

  65. previous_neurons = 0

  66. index = 0

  67. index_weights = 0

  68. self.layers = []

  69. for i in data['network']:

  70. layer = Layer(index)

  71. layer.init_neurons(i, previous_neurons)

  72. for j in range(len(layer.neurons)):

  73. for k in range(len(layer.neurons[j].weights)):

  74. layer.neurons[j].weights[k] = data['weights'][index_weights]

  75. index_weights += 1

  76. previous_neurons = i

  77. index += 1

  78. self.layers.append(layer)

  79. # 输入游戏环境中的一些条件(如敌机位置), 返回要执行的操作

  80. def feed_forward(self, inputs):

  81. for i in range(len(inputs)):

  82. self.layers[0].neurons[i].biase = inputs[i]

  83. prev_layer = self.layers[0]

  84. for i in range(len(self.layers)):

  85. # 第一层没有weights

  86. if i == 0:

  87. continue

  88. for j in range(len(self.layers[i].neurons)):

  89. sum = 0

  90. for k in range(len(prev_layer.neurons)):

  91. sum += prev_layer.neurons[k].biase * self.layers[i].neurons[j].weights[k]

  92. self.layers[i].neurons[j].biase = sigmoid(sum)

  93. prev_layer = self.layers[i]

  94. out = []

  95. last_layer = self.layers[-1]

  96. for i in range(len(last_layer.neurons)):

  97. out.append(last_layer.neurons[i].biase)

  98. return out

  99. def print_info(self):

  100. for layer in self.layers:

  101. print(layer)

遗传算法:

[python] view plain copy

  1. # "基因组"

  2. class Genome():

  3. def __init__(self, score, network_weights):

  4. self.score = score

  5. self.network_weights = network_weights

  6. class Generation():

  7. def __init__(self):

  8. self.genomes = []

  9. def add_genome(self, genome):

  10. i = 0

  11. for i in range(len(self.genomes)):

  12. if score_sort < 0:

  13. if genome.score > self.genomes[i].score:

  14. break

  15. else:

  16. if genome.score < self.genomes[i].score:

  17. break

  18. self.genomes.insert(i, genome)

  19. # 杂交+突变

  20. def breed(self, genome1, genome2, n_child):

  21. datas = []

  22. for n in range(n_child):

  23. data = genome1

  24. for i in range(len(genome2.network_weights['weights'])):

  25. if random.random() <= 0.5:

  26. data.network_weights['weights'][i] = genome2.network_weights['weights'][i]

  27. for i in range(len(data.network_weights['weights'])):

  28. if random.random() <= mutation_rate:

  29. data.network_weights['weights'][i] += random.random() * mutation_range * 2 - mutation_range

  30. datas.append(data)

  31. return datas

  32. # 生成下一代

  33. def generate_next_generation(self):

  34. nexts = []

  35. for i in range(round(elitism*population)):

  36. if len(nexts) < population:

  37. nexts.append(self.genomes[i].network_weights)

  38. for i in range(round(random_behaviour*population)):

  39. n = self.genomes[0].network_weights

  40. for k in range(len(n['weights'])):

  41. n['weights'][k] = random_clamped()

  42. if len(nexts) < population:

  43. nexts.append(n)

  44. max_n = 0

  45. while True:

  46. for i in range(max_n):

  47. childs = self.breed(self.genomes[i], self.genomes[max_n], n_child if n_child > 0 else 1)

  48. for c in range(len(childs)):

  49. nexts.append(childs[c].network_weights)

  50. if len(nexts) >= population:

  51. return nexts

  52. max_n += 1

  53. if max_n >= len(self.genomes)-1:

  54. max_n = 0

NeuroEvolution:

[python] view plain copy

  1. class Generations():

  2. def __init__(self):

  3. self.generations = []

  4. def first_generation(self):

  5. out = []

  6. for i in range(population):

  7. nn = NeuroNetwork()

  8. nn.init_neuro_network(network[0], network[1], network[2])

  9. out.append(nn.get_weights())

  10. self.generations.append(Generation())

  11. return out

  12. def next_generation(self):

  13. if len(self.generations) == 0:

  14. return False

  15. gen = self.generations[-1].generate_next_generation()

  16. self.generations.append(Generation())

  17. return gen

  18. def add_genome(self, genome):

  19. if len(self.generations) == 0:

  20. return False

  21. return self.generations[-1].add_genome(genome)

  22. class NeuroEvolution():

  23. def __init__(self):

  24. self.generations = Generations()

  25. def restart(self):

  26. self.generations = Generations()

  27. def next_generation(self):

  28. networks = []

  29. if len(self.generations.generations) == 0:

  30. networks = self.generations.first_generation()

  31. else:

  32. networks = self.generations.next_generation()

  33. nn = []

  34. for i in range(len(networks)):

  35. n = NeuroNetwork()

  36. n.set_weights(networks[i])

  37. nn.append(n)

  38. if low_historic:

  39. if len(self.generations.generations) >= 2:

  40. genomes = self.generations.generations[len(self.generations.generations) - 2].genomes

  41. for i in range(genomes):

  42. genomes[i].network = None

  43. if historic != -1:

  44. if len(self.generations.generations) > historic+1:

  45. del self.generations.generations[0:len(self.generations.generations)-(historic+1)]

  46. return nn

  47. def network_score(self, score, network):

  48. self.generations.add_genome(Genome(score, network.get_weights()))

是AI就躲个飞机

[python] view plain copy

  1. import pygame

  2. import sys

  3. from pygame.locals import *

  4. import random

  5. import math

  6. import neuro_evolution

  7. BACKGROUND = (200, 200, 200)

  8. SCREEN_SIZE = (320, 480)

  9. class Plane():

  10. def __init__(self, plane_image):

  11. self.plane_image = plane_image

  12. self.rect = plane_image.get_rect()

  13. self.width = self.rect[2]

  14. self.height = self.rect[3]

  15. self.x = SCREEN_SIZE[0]/2 - self.width/2

  16. self.y = SCREEN_SIZE[1] - self.height

  17. self.move_x = 0

  18. self.speed = 2

  19. self.alive = True

  20. def update(self):

  21. self.x += self.move_x * self.speed

  22. def draw(self, screen):

  23. screen.blit(self.plane_image, (self.x, self.y, self.width, self.height))

  24. def is_dead(self, enemes):

  25. if self.x < -self.width or self.x + self.width > SCREEN_SIZE[0]+self.width:

  26. return True

  27. for eneme in enemes:

  28. if self.collision(eneme):

  29. return True

  30. return False

  31. def collision(self, eneme):

  32. if not (self.x > eneme.x + eneme.width or self.x + self.width < eneme.x or self.y > eneme.y + eneme.height or self.y + self.height < eneme.y):

  33. return True

  34. else:

  35. return False

  36. def get_inputs_values(self, enemes, input_size=4):

  37. inputs = []

  38. for i in range(input_size):

  39. inputs.append(0.0)

  40. inputs[0] = (self.x*1.0 / SCREEN_SIZE[0])

  41. index = 1

  42. for eneme in enemes:

  43. inputs[index] = eneme.x*1.0 / SCREEN_SIZE[0]

  44. index += 1

  45. inputs[index] = eneme.y*1.0 / SCREEN_SIZE[1]

  46. index += 1

  47. #if len(enemes) > 0:

  48. #distance = math.sqrt(math.pow(enemes[0].x + enemes[0].width/2 - self.x + self.width/2, 2) + math.pow(enemes[0].y + enemes[0].height/2 - self.y + self.height/2, 2));

  49. if len(enemes) > 0 and self.x < enemes[0].x:

  50. inputs[index] = -1.0

  51. index += 1

  52. else:

  53. inputs[index] = 1.0

  54. return inputs

  55. class Enemy():

  56. def __init__(self, enemy_image):

  57. self.enemy_image = enemy_image

  58. self.rect = enemy_image.get_rect()

  59. self.width = self.rect[2]

  60. self.height = self.rect[3]

  61. self.x = random.choice(range(0, int(SCREEN_SIZE[0] - self.width/2), 71))

  62. self.y = 0

  63. def update(self):

  64. self.y += 6

  65. def draw(self, screen):

  66. screen.blit(self.enemy_image, (self.x, self.y, self.width, self.height))

  67. def is_out(self):

  68. return True if self.y >= SCREEN_SIZE[1] else False

  69. class Game():

  70. def __init__(self):

  71. pygame.init()

  72. self.screen = pygame.display.set_mode(SCREEN_SIZE)

  73. self.clock = pygame.time.Clock()

  74. pygame.display.set_caption('是AI就躲个飞机')

  75. self.ai = neuro_evolution.NeuroEvolution()

  76. self.generation = 0

  77. self.max_enemes = 1

  78. # 加载飞机、敌机图片

  79. self.plane_image = pygame.image.load('plane.png').convert_alpha()

  80. self.enemy_image = pygame.image.load('enemy.png').convert_alpha()

  81. def start(self):

  82. self.score = 0

  83. self.planes = []

  84. self.enemes = []

  85. self.gen = self.ai.next_generation()

  86. for i in range(len(self.gen)):

  87. plane = Plane(self.plane_image)

  88. self.planes.append(plane)

  89. self.generation += 1

  90. self.alives = len(self.planes)

  91. def update(self, screen):

  92. for i in range(len(self.planes)):

  93. if self.planes[i].alive:

  94. inputs = self.planes[i].get_inputs_values(self.enemes)

  95. res = self.gen[i].feed_forward(inputs)

  96. if res[0] < 0.45:

  97. self.planes[i].move_x = -1

  98. elif res[0] > 0.55:

  99. self.planes[i].move_x = 1

  100. self.planes[i].update()

  101. self.planes[i].draw(screen)

  102. if self.planes[i].is_dead(self.enemes) == True:

  103. self.planes[i].alive = False

  104. self.alives -= 1

  105. self.ai.network_score(self.score, self.gen[i])

  106. if self.is_ai_all_dead():

  107. self.start()

  108. self.gen_enemes()

  109. for i in range(len(self.enemes)):

  110. self.enemes[i].update()

  111. self.enemes[i].draw(screen)

  112. if self.enemes[i].is_out():

  113. del self.enemes[i]

  114. break

  115. self.score += 1

  116. print("alive:{}, generation:{}, score:{}".format(self.alives, self.generation, self.score), end='\r')

  117. def run(self, FPS=1000):

  118. while True:

  119. for event in pygame.event.get():

  120. if event.type == QUIT:

  121. pygame.quit()

  122. sys.exit()

  123. self.screen.fill(BACKGROUND)

  124. self.update(self.screen)

  125. pygame.display.update()

  126. self.clock.tick(FPS)

  127. def gen_enemes(self):

  128. if len(self.enemes) < self.max_enemes:

  129. enemy = Enemy(self.enemy_image)

  130. self.enemes.append(enemy)

  131. def is_ai_all_dead(self):

  132. for plane in self.planes:

  133. if plane.alive:

  134. return False

  135. return True

  136. game = Game()

  137. game.start()

  138. game.run()

AI的工作逻辑

假设你是AI,你首先繁殖一个种群(50个个体),开始的个体大都是歪瓜裂枣(上来就被敌机撞)。但是,即使是歪瓜裂枣也有表现好的,在下一代,你会使用这些表现好的再繁殖一个种群,经过代代相传,存活下来的个体会越来越优秀。其实就是仿达尔文进化论,种群->自然选择->优秀个体->杂交、变异->种群->循环n世代。

ai开始时候的表现:

是AI就躲个飞机-纯Python实现人工智能-LMLPHP图片被拉扁了 sorry

经过几百代之后,ai开始娱乐的躲飞机:

是AI就躲个飞机-纯Python实现人工智能-LMLPHP

已获作者授权转载,原文链接如下:

http://blog.csdn.net/u014365862/article/details/54380422

05-27 06:34