这是个真实的故事。
"
从前在海岸边有一群扇贝在悠哉游哉地生活繁衍着。它们自然是衣食不愁,连房子也有了着落。它们担忧的只有一件事:每隔一段时间,总有一个人来挖走它们之中的一部分。当然啦,挖回去干什么这大家都知道。但扇贝们不知道的是,这人的家族图腾是Firefox的图标,所以他总是选择那些贝壳花纹长得比较不像Firefox图标的扇贝。
这种状况持续了好几十万代。大家应该也猜到扇贝们身上发生什么事情了:它们的贝壳上都印着很像Firefox图标的图案。
"
这是一个遗传算法应用的例子之一,遗传算法有着广泛的用途,比如训练人工神经网络的参数集等等。
那么如何用遗传学算法来生成一张图片呢?
首先我们得清楚遗传学算法到底是什么。遗传算法是一种解决问题的方法。它模拟大自然中种群在选择压力下的演化,从而得到问题的一个近似解。
如果我们要实现一个遗传算法,就要模拟生物的自然进化过程。在中学的课堂上我们已经知道,生物之所以会进化是因为有自身的变异和环境的选择,所以生存下来的种群总是最适应化境的。所以要实现遗传算法,我们就要生成一个种群,让他们变异,选择其中最适应的个体,再让选择出来的个体互相交换他们的基因,产生下一代。
那么如何表示两张图片之间的差异呢,我们知道图片是有一些像素点构成的,那么我们只要把两张图片相应位置的像素点值相减再累加不就能表示两张图片之间的差异了吗。
所以选择的步骤就是选择那些和目标图片的差值较小的个体。
知道了这个那么自然就能想到,变异就是将某一些像素点的值递增或递减一个特定值。
而至于交换基因,我们选择单点交叉的方法,就是对两个个体a和b随机选择图片的某个像素点位置x,在x位置和x位置之前使用a的像素值,在x之后使用b的像素值。
讲了这么多相信大体的思想也明白了,既然明白了思想实现起来就不是问题,在下面我贴出了自己的实现,诸位可以拷回去自己实验一下,用法就是新建一个文件夹,把这些代码拷到那个文件夹下然后随便起个什么名字,再在同一个文件夹下放入一张要生成的图片并改名为"test1.png",注意一定要是png格式的。我写的代码比较烂,我自己都不敢看,要是哪位仁兄实在是觉得懒得写代码又想修改我的代码来实现更快的进化,请在代码对应的位置进行修改(我已经用注释标注了)。
另外程序会定时保存数据(会将数据保存在同文件夹下的“quanju_v.tmp”文件中,请不要删除)所以不用担心数据丢失的问题,而且每隔五代会生成一张图片,这样你就可以随时看到进化的过程了。
要选择规格较小的图片进行实验,不然一代的进化都要很久更别提,千千万万代了。
from PIL import Image as im
from os import path
from math import log
from random import randint
from copy import deepcopy
import pickle as pk
#全局变量
quanju_v = {}
#定义图片预处理函数
def process_pic(pic_name):
print("开始预处理图片")
#获得图片对象
img = im.open(pic_name)
#获得图片的规格
img_color = []
img_width,img_height = img.size
i = 1
for x in range(img_height):
img_color_tmp = []
for y in range(img_width):
#获得图片像素的rgb信息
r,g,b = img.getpixel((y,x))[:3]
#将rgb信息转为10进制数字
img_color_tmp.append((r,g,b,r+g+b))
img_color.append(img_color_tmp)
print("预处理图片结束")
return img_color,img.size
#随机基因的函数
def rand_genes(size):
print("图片规格为",size,sep=" ")
print("正在初始化随机基因")
width,height = size
genes = []
for i in range(100):
gene = []
for x in range(height):
row = []
for y in range(width):
a = randint(0,255)
b = randint(0,255)
c = randint(0,255)
row.append([a,b,c,a+b+c])
gene.append(row)
genes.append([gene,0])
print("随机基因初始化完成")
return genes
#定义适应度计算函数
def forecast(genes):
print("开始处理基因")
sum_sum = 0
for i,gene in enumerate(genes):
sum_ = 0
for j,row in enumerate(gene[0]):
for k,col in enumerate(row):
_a,_b,_c,_d = data[j][k]
a,b,c,d = col
det_d = abs(_d-d)
sum_ += (abs(_a-a) + abs(_b-b) + abs(_c-c))*det_d
genes[i][1] = sum_
sum_sum += sum_
for i,gene in enumerate(genes):
genes[i][1] = genes[i][1]/sum_sum
print("正在排序基因")
genes.sort(key=lambda x:x[1])
print("基因处理完成")
return
#基因变异函数
def variation(genes,size):
rate = 0.5
print("开始变异")
for i,gene in enumerate(genes):
for x,row in enumerate(gene[0]):
for y,col in enumerate(row):
if randint(1,100)/100 <= rate:
#图片由 r g b 三种颜色混合而成 变异就是改变他们的值
#a b c 分别对应 r_ g_ b_ 改变的值 可自行修改
#r g b 的最大值为255
#------------------------------请修改这里-------------------------------------#
a = [-1,1][randint(0,1)]*randint(3,10)
b = [-1,1][randint(0,1)]*randint(3,10)
c = [-1,1][randint(0,1)]*randint(3,10)
#------------------------------请修改这里-------------------------------------#
genes[i][0][x][y][0] += a
genes[i][0][x][y][0] += b
genes[i][0][x][y][0] += c
genes[i][0][x][y][3] += a+b+c
print("变异结束")
return
def merge(gene1,gene2,size):
width,height = size
x = randint(0,height-1)
y = randint(0,width-1)
new_gene = deepcopy(gene1[0][:x])
new_gene = [new_gene,0]
new_gene[0][x:] = deepcopy(gene2[0][x:])
new_gene[0][x][:y] = deepcopy(gene1[0][x][:y])
return new_gene
#定义选择函数
def select(genes,size):
print("这是选择环节 我们会选取种群中按适应度排名的前 三分之二")
seek = int(len(genes)*2/3)
i = 0
back_seek = seek+1
while i<seek:
genes[back_seek] = merge(genes[i],genes[i+1],size)
back_seek += 1
i += 2
print("选择结束")
#生成图片的函数
def genera_pic(gene,genera):
print("生成第",genera,"代的图片",sep=" ")
num = gene[1]
gene = gene[0]
img = im.open("test1.png")
for y,row in enumerate(gene):
for x,col in enumerate(row):
a,b,c,d = col
img.putpixel((x,y),(a,b,c))
img.save(str(genera)+"-"+str(num)[:4]+"-.png")
#--------------------------------程序初始化模块--------------------------------#
print("正在初始化程序")
try:
with open("quanju_v.tmp","rb") as fd:
quanju_v = pk.load(fd)
data = quanju_v["data"]
size = quanju_v["size"]
genes = quanju_v["genes"]
except:
data,size = process_pic("test1.png")
quanju_v["data"] = data
quanju_v["size"] = size
quanju_v["genes"] = rand_genes(size)
quanju_v["genera"] = 1
genes = deepcopy(quanju_v["genes"])
print("程序初始化完成")
#--------------------------------程序初始化模块--------------------------------#
#----------------------------------程序主体-----------------------------------#
def main():
global quanju_v
global genes
global size
genera = quanju_v["genera"]
while True:
print("这是第*******",genera,"*******代基因",sep = " ")
variation(genes,size)
forecast(genes)
variation(genes,size)
select(genes,size)
if genera % 20 == 0:
quanju_v["genes"] = genes
quanju_v["genera"] = genera
genes = deepcopy(quanju_v["genes"])
with open("quanju_v.tmp","wb") as fd:
pk.dump(quanju_v,fd)
if (genera-1) % 5 == 0:
genera_pic(genes[0],genera)
for i in range(10):
print("第*",i,"*个基因适应度为 ---------->",genes[i][1],sep=" ")
genera += 1
#----------------------------------程序主体-----------------------------------#
#---------------------------------程序数据保存模块------------------------------#
#if not path.exists("quanju_v.tmp"):
def save_data():
global quanju_v
print("文件存储中")
with open("quanju_v.tmp","wb") as fd:
pk.dump(quanju_v,fd)
print("文件储存完成")
#---------------------------------程序数据保存模块------------------------------#
#main()
try:
main()
except:
save_data()
可以看到后面的时候进化的速度明显变慢的,因为我在进化的过程中更改了变异函数导致变异率下降,直接影响了进化的速率。