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

代码下载: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开始时候的表现:

图片被拉扁了 sorry

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

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

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

是AI就躲个飞机-纯Python实现人工智能的更多相关文章

  1. 深入理解Python中协程的应用机制: 使用纯Python来实现一个操作系统吧!!

    本文参考:http://www.dabeaz.com/coroutines/   作者:David Beazley 缘起: 本人最近在学习python的协程.偶然发现了David Beazley的co ...

  2. 机器学习之线性回归(纯python实现)][转]

    本文转载自:https://juejin.im/post/5a924df16fb9a0634514d6e1 机器学习之线性回归(纯python实现) 线性回归是机器学习中最基本的一个算法,大部分算法都 ...

  3. python实现人工智能Ai抠图功能

    这篇文章主要介绍了python实现人工智能Ai抠图功能,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下 自己是个PS小白,没办法只能通过技术来证明自己. 话不多说, ...

  4. 纯Python模式

    http://crcmod.sourceforge.net/intro.html https://help.aliyun.com/document_detail/85288.html OSS的CRC数 ...

  5. 纯python自研接口自动化脚本更新版本,让小白也能实现0到1万+的接口自动化用例

    查看完整文章点击原文链接:纯python自研接口自动化脚本更新版本,让小白也能实现0到1万+的接口自动化用例 你是否还在用postman\jmeter做接口自动化吗?用python的开源框架[unit ...

  6. django之分页,纯python代码

    Django中分页 py文件代码 """ 自定义分页组件 可以返回分页的数据和分页的HTML代码 """ from django.http ...

  7. 用纯Python实现循环神经网络RNN向前传播过程(吴恩达DeepLearning.ai作业)

    Google TensorFlow程序员点赞的文章!   前言 目录: - 向量表示以及它的维度 - rnn cell - rnn 向前传播 重点关注: - 如何把数据向量化的,它们的维度是怎么来的 ...

  8. 纯Python包发布setup脚本编写示例

    如果你有多个模块需要发布,而它们又存在于多个包中,那么指定整个包比指定模块可能要容易地多.即使你的模块并不在一个包内,这种做法也行的通:你可以告诉Distutils从根包(root package)处 ...

  9. 纯Python综合图像处理小工具(4)自定义像素级处理(剪纸滤镜)

      上一节介绍了python PIL库自带的10种滤镜处理,现成的库函数虽然用起来方便,但是对于图像处理的各种实际需求,还需要开发者开发自定义的滤镜算法.本文将给大家介绍如何使用PIL对图像进行自定义 ...

随机推荐

  1. 处理Ajax请求跨域问题

    ajax跨域的原理 ajax出现请求跨域错误问题,主要原因就是因为浏览器的“同源策略”. CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resourc ...

  2. 反射(hasattr和setattr和delattr)

    反射(hasattr和setattr和delattr) 一.反射在类中的使用 反射就是通过字符串来操作类或者对象的属性, 反射本质就是在使用内置函数,其中反射有以下四个内置函数: hasattr:通过 ...

  3. C#通过窗体应用程序操作数据库(增删改查)

    为了体现面向对象的思想,我们把“增删改查”这些函数封装到一个数据库操作类里: 为了便于窗体程序与数据库之间进行数据交互,我们建一个具有数据库行数据的类,通过它方便的在窗体程序与数据库之间传输数据: 我 ...

  4. Vue其他指令-组件-全局-局部-组件的交互父传子

    v-once指令 once:一旦,当...时候 <!DOCTYPE html> <html lang="zh"> <head> <meta ...

  5. Web 手工测试

    day 1 学习目标: 熟练搭建本地测试环境 掌握熟悉项目的步骤和内容 掌握项目基本的测试流程 基础环境介绍: 项目环境的组成部分: 操作系统 windows win7 win10 Linux Cen ...

  6. USB Reverse Tether (a dirty solution)

    Tether your android phone to your PC using USB cable could share your 3g Internet connection with PC ...

  7. mybatis <where>标签别勿用,否则线上清表

    公司线上发送了清表现象,排除发现: <delete id="scMxdxxxb"> DELETE TABLE <where> <foreach col ...

  8. Cobbler_自动装系统

    Cobbler —自动装系统的操作步骤 Cobbler是一款自动化操作系统安装的实现,与PXE安装系统的区别就是可以同时部署多个版本的系统,而PXE只能选择一种系统. Cobbler 的安装 # 在一 ...

  9. winform 界面加载慢原因分析

    公司新来的开发人员,对winform开发还不是特别精通,在做个性化界面体验的时候容易出现闪烁和加载慢 闪烁的话,通过winform窗体的双缓存来解决在form 窗体中增加如下代码 protected ...

  10. Codeforces1301D Time to Run

    (搬运一下部分官方题解) Description link 或者洛谷link 到时候就有中文翻译了,不过这个题机翻没毛病 Solution 首先这是一道模拟题-- 不要管题目中的循环移动的问题,直接按 ...