基于遗传算法的人工智能实例之拼图游戏(python实现)

代码实现及理论参考自中国大学mooc人工智能与信息社会(陈斌)

遗传算法及案例描述

所谓遗传算法,是一种受达尔文自然进化理论启发的搜索启发式算法。主要包括了交叉繁殖、自然选择、变异这几个部分。
先来看案例。现在我手头有一张图片。我想让128个半透明的三角形进行位置大小及颜色上的不同排列组合,最终组成下面的滑稽图案,那么要如何做呢?
在这里插入图片描述
我们知道基因是遗传中最基本的单元,可以认为由这128个不同的三角形基因共同组成了一个生物个体。
一个三角形即一个基因的确定因素有三个,分别是:
颜色、三顶点坐标(坐标同时构成了大小和位置)、透明度
在这里插入图片描述
有了这个父代个体后,我们就可以通过遗传算法生成子代。
遗传包括基因复制,即父代基因原封不动复制给子代,还包括交叉,即将来自父母双方的基因组合在一起,得到新的子代
比如对于下面两个父代个体,通过交叉(即交换父代三角形基因),就生成了两个新的子代个体。
在这里插入图片描述
变异就很好理解了,父代在生成子代的时候,父代中的某些三角形基因随机地发生了坐标、颜色、透明度的改变
在这里插入图片描述
最重要的就是自然选择,有利的个体留存下来繁衍后代,不利的个体逐渐被淘汰。也就是说我们看生成的这个图形中是否和给定的滑稽图案相似,那些不像滑稽图案的就被淘汰。
我们可以通过比较每个像素点rgb的差值,值越小就越相似,值越大的就淘汰掉。
在这里插入图片描述
我们规定,如果子代比父代更接近给定的图案,就子代取代后代,反之子代被淘汰。
最终,当适应度函数 f 小于一定的阈值或者循环迭代的次数超过了一定的限制,遗传算法终止。
我们来看这个算法最终运行后的结果:
在这里插入图片描述
当算法遗传到第500代时,已经显示出来一个圆形
在这里插入图片描述
而当繁衍到第29900代时,已经很接近滑稽图案了
在这里插入图片描述
最后总结这个算法,就是通过不断地繁殖和变异,淘汰掉那些与图案相似度不高的个体,这就是遗传算法。

代码运行环境搭建

1.安装mxnet及git

sudo apt-get update
sudo apt-get install -y build-essential git libatlas-base-dev libopencv-dev
git clone --recursive https://github.com/dmlc/mxnet
cd mxnet; make -j4

2.如果git 克隆速度较慢,可以先ctrl+z取消,先执行下面操作,再重新克隆。速度快的可忽略第二步

git config --global http.postBuffer 524288000
sudo vim /etc/hosts

在末尾加入

151.101.185.194 github.global.ssl.fastly.net
192.30.253.113 github.com

在这里插入图片描述
3. pthon接口配置

sudo apt-get install python-numpy 

完成后可以用以下命令进行测试。这里的mxnet路径根据实际情况去修改

python mxnet/example/image-classification/train_mnist.py

接着添加路径以解决No module named XXX

/*
* ~表示我的mxnet克隆在home下面,请根据实际情况修改mxnet路径。
* 比如克隆在了桌面,就是~/Desktop/mxnet
*/ 
export PYTHONPATH=~/mxnet/python
cp -r ~/mxnet/python/mxnet .
cp ~/mxnet/lib/libmxnet.so mxnet/

4.安装minpy

sudo pip install minpy

5.在写好python文件的目录下,新建一个results的文件夹,并准备任意一张图片
在这里插入图片描述
程序会要求输入文件路径,比如文件在当前路径就加上 " ./ "
在这里插入图片描述

代码实现

#coding:utf-8
# -*- coding: utf-8 -*-
#代码参考自中国大学mooc人工智能与信息社会(陈斌)

from PIL import Image, ImageDraw
import os
import gc
import random as r
import minpy.numpy as np

class Color(object):
    '''
    定义颜色的类,这个类包含r,g,b,a表示颜色属性
    '''
    def __init__(self):
        self.r = r.randint(0, 255)
        self.g = r.randint(0, 255)
        self.b = r.randint(0, 255)
        self.a = r.randint(95, 115)


def mutate_or_not(rate):
    '''
    生成随机数,判断是否需要变异
    '''
    return True if rate > r.random() else False


class Triangle(object):
    '''
    定义三角形的类
    属性:
            ax,ay,bx,by,cx,cy:表示每个三角形三个顶点的坐标
            color 			 : 表示三角形的颜色
            img_t			 : 三角形绘制成的图,用于合成图片
    方法:
            mutate_from(self, parent):      从父代三角形变异
            draw_it(self, size=(256, 256)): 绘制三角形
    '''


    max_mutate_rate = 0.08
    mid_mutate_rate = 0.3
    min_mutate_rate = 0.8


    def __init__(self, size=(255, 255)):
        self.ax = r.randint(0, size[0])
        self.ay = r.randint(0, size[1])
        self.bx = r.randint(0, size[0])
        self.by = r.randint(0, size[1])
        self.cx = r.randint(0, size[0])
        self.cy = r.randint(0, size[1])
        self.color = Color()
        self.img_t = None


    def mutate_from(self, parent):
        if mutate_or_not(self.max_mutate_rate):
            self.ax = r.randint(0, 255)
            self.ay = r.randint(0, 255)
        if mutate_or_not(self.mid_mutate_rate):
            self.ax = min(max(0, parent.ax + r.randint(-15, 15)), 255)
            self.ay = min(max(0, parent.ay + r.randint(-15, 15)), 255)
        if mutate_or_not(self.min_mutate_rate):
            self.ax = min(max(0, parent.ax + r.randint(-3, 3)), 255)
            self.ay = min(max(0, parent.ay + r.randint(-3, 3)), 255)

        if mutate_or_not(self.max_mutate_rate):
            self.bx = r.randint(0, 255)
            self.by = r.randint(0, 255)
        if mutate_or_not(self.mid_mutate_rate):
            self.bx = min(max(0, parent.bx + r.randint(-15, 15)), 255)
            self.by = min(max(0, parent.by + r.randint(-15, 15)), 255)
        if mutate_or_not(self.min_mutate_rate):
            self.bx = min(max(0, parent.bx + r.randint(-3, 3)), 255)
            self.by = min(max(0, parent.by + r.randint(-3, 3)), 255)

        if mutate_or_not(self.max_mutate_rate):
            self.cx = r.randint(0, 255)
            self.cy = r.randint(0, 255)
        if mutate_or_not(self.mid_mutate_rate):
            self.cx = min(max(0, parent.cx + r.randint(-15, 15)), 255)
            self.cy = min(max(0, parent.cy + r.randint(-15, 15)), 255)
        if mutate_or_not(self.min_mutate_rate):
            self.cx = min(max(0, parent.cx + r.randint(-3, 3)), 255)
            self.cy = min(max(0, parent.cy + r.randint(-3, 3)), 255)
        # color
        if mutate_or_not(self.max_mutate_rate):
            self.color.r = r.randint(0, 255)
        if mutate_or_not(self.mid_mutate_rate):
            self.color.r = min(max(0, parent.color.r + r.randint(-30, 30)), 255)
        if mutate_or_not(self.min_mutate_rate):
            self.color.r = min(max(0, parent.color.r + r.randint(-10, 10)), 255)

        if mutate_or_not(self.max_mutate_rate):
            self.color.g = r.randint(0, 255)
        if mutate_or_not(self.mid_mutate_rate):
            self.color.g = min(max(0, parent.color.g + r.randint(-30, 30)), 255)
        if mutate_or_not(self.min_mutate_rate):
            self.color.g = min(max(0, parent.color.g + r.randint(-10, 10)), 255)

        if mutate_or_not(self.max_mutate_rate):
            self.color.b = r.randint(0, 255)
        if mutate_or_not(self.mid_mutate_rate):
            self.color.b = min(max(0, parent.color.b + r.randint(-30, 30)), 255)
        if mutate_or_not(self.min_mutate_rate):
            self.color.b = min(max(0, parent.color.b + r.randint(-10, 10)), 255)
        # alpha
        if mutate_or_not(self.mid_mutate_rate):
            self.color.a = r.randint(95, 115)
        # if mutate_or_not(self.mid_mutate_rate):
        #     self.color.a = min(max(0, parent.color.a + r.randint(-30, 30)), 255)
        # if mutate_or_not(self.min_mutate_rate):
        #     self.color.a = min(max(0, parent.color.a + r.randint(-10, 10)), 255)


    def draw_it(self, size=(256, 256)):
        self.img_t = Image.new('RGBA', size)
        draw = ImageDraw.Draw(self.img_t)
        draw.polygon([(self.ax, self.ay),
                      (self.bx, self.by),
                      (self.cx, self.cy)],
                     fill=(self.color.r, self.color.g, self.color.b, self.color.a))
        return self.img_t


class Canvas(object):
    '''
    定义每一张图片的类
    属性:
            mutate_rate	 : 变异概率
            size		 : 图片大小
            target_pixels: 目标图片像素值
    方法:
            add_triangles(self, num=1)      : 在图片类中生成num个三角形
            mutate_from_parent(self, parent): 从父代图片对象进行变异
            calc_match_rate(self)			: 计算环境适应度
            draw_it(self, i)				: 保存图片
    '''


    mutate_rate = 0.01
    size = (256, 256)
    target_pixels = []


    def __init__(self):
        self.triangles = []
        self.match_rate = 0
        self.img = None


    def add_triangles(self, num=1):
        for i in range(0, num):
            triangle = Triangle()
            self.triangles.append(triangle)


    def mutate_from_parent(self, parent):
        flag = False
        for triangle in parent.triangles:
            t = triangle
            if mutate_or_not(self.mutate_rate):
                flag = True
                a = Triangle()
                a.mutate_from(t)
                self.triangles.append(a)
                continue
            self.triangles.append(t)
        if not flag:
            self.triangles.pop()
            t = parent.triangles[r.randint(0, len(parent.triangles) - 1)]
            a = Triangle()
            a.mutate_from(t)
            self.triangles.append(a)


    def calc_match_rate(self):
        if self.match_rate > 0:
            return self.match_rate
        self.match_rate = 0
        self.img = Image.new('RGBA', self.size)
        draw = ImageDraw.Draw(self.img)
        draw.polygon([(0, 0), (0, 255), (255, 255), (255, 0)], fill=(255, 255, 255, 255))
        for triangle in self.triangles:
            self.img = Image.alpha_composite(self.img, triangle.img_t or triangle.draw_it(self.size))    
        # 与下方代码功能相同,此版本便于理解但效率低
        # pixels = [self.img.getpixel((x, y)) for x in range(0, self.size[0], 2) for y in range(0, self.size[1], 2)]
        # for i in range(0, min(len(pixels), len(self.target_pixels))):
        #     delta_red   = pixels[i][0] - self.target_pixels[i][0]
        #     delta_green = pixels[i][1] - self.target_pixels[i][1]
        #     delta_blue  = pixels[i][2] - self.target_pixels[i][2]
        #     self.match_rate += delta_red   * delta_red   + \
        #                        delta_green * delta_green + \
        #                        delta_blue  * delta_blue
        arrs = [np.array(x) for x in list(self.img.split())]    # 分解为RGBA四通道
        for i in range(3):                                      # 对RGB通道三个矩阵分别与目标图片相应通道作差取平方加和评估相似度
            self.match_rate += np.sum(np.square(arrs[i]-self.target_pixels[i]))[0]

    def draw_it(self, i):
        #self.img.save(os.path.join(PATH, "%s_%d_%d_%d.png" % (PREFIX, len(self.triangles), i, self.match_rate)))
        self.img.save(os.path.join(PATH, "%d.png" % (i)))


def main():
        global LOOP, PREFIX, PATH, TARGET, TRIANGLE_NUM
        # 声明全局变量
        img = Image.open(TARGET).resize((256, 256)).convert('RGBA')
        size = (256, 256)
        Canvas.target_pixels = [np.array(x) for x in list(img.split())]
        # 生成一系列的图片作为父本,选择其中最好的一个进行遗传
        parentList = []
        for i in range(20):
            print('正在生成第%d个初代个体' % (i))
            parentList.append(Canvas())
            parentList[i].add_triangles(TRIANGLE_NUM)
            parentList[i].calc_match_rate()
        parent = sorted(parentList, key=lambda x: x.match_rate)[0]
        del parentList
        gc.collect()
        # 进入遗传算法的循环
        i = 0
        while i < 30000:
            childList = []
            # 每一代从父代中变异出10个个体
            for j in range(10):
                childList.append(Canvas())
                childList[j].mutate_from_parent(parent)
                childList[j].calc_match_rate()
            child = sorted(childList, key=lambda x: x.match_rate)[0]
            # 选择其中适应度最好的一个个体
            del childList
            gc.collect()
            parent.calc_match_rate()
            if i % LOOP == 0:
                print ('%10d parent rate %11d \t child1 rate %11d' % (i, parent.match_rate, child.match_rate))
            parent = parent if parent.match_rate < child.match_rate else child
            # 如果子代比父代更适应环境,那么子代成为新的父代
            # 否则保持原样
            child = None
            if i % LOOP == 0:
                # 每隔LOOP代保存一次图片
                parent.draw_it(i)
                #print(parent.match_rate)
                #print ('%10d parent rate %11d \t child1 rate %11d' % (i, parent.match_rate, child.match_rate))
            i += 1


'''
定义全局变量,获取待处理的图片名
'''
NAME = input('请输入原图片文件名:')
LOOP = 100
PREFIX = NAME.split('/')[-1].split('.')[0]  # 取文件名
PATH = os.path.abspath('.')  # 取当前路径
PATH = os.path.join(PATH,'results')
TARGET = NAME  # 源图片文件名
TRIANGLE_NUM = 256  # 三角形个数

if __name__ == '__main__':
    #print('开始进行遗传算法')
    main()

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值