> 文档中心 > 智能优化算法——python实现免疫遗传算法的图像拟合

智能优化算法——python实现免疫遗传算法的图像拟合

python实现免疫遗传算法的图像拟合

  • 引言
    • 算法思路
    • 流程图
  • 算法主体
    • 初始化
    • 获取抗体与抗体的相似度
    • 克隆与变异
    • 获取抗体亲和度和抗体浓度
    • 抗体集合更新
    • 拟合过程
    • 训练过程评估
  • 训练效果对比
    • 遗传算法部分拟合图像
    • 免疫遗传算法部分拟合图像
    • 对比总结
  • 完整代码下载

引言

算法思路

免疫算法是仿照生物体中的免疫应答来进行寻优的算法。我们待拟合的问题可以看作抗原,可能的解集抗原看作抗体。当有抗原入侵到生物体中时,免疫系统进行免疫应答。在免疫应答的初期会产生大量的抗体进行应答,随后会对与抗原亲和度高的抗体进行复制保留,对于浓度高的抗体进行复制抑制来保证抗体的多样性。在此过程中同时也会产生新的抗体加入到原本的抗体集中。为了更加直观的介绍免疫遗传算法,我们仍然以图片为例进行免疫遗传算法的实现与拟合。

流程图

在这里插入图片描述

算法主体

预备知识与智能优化算法——python实现使用遗传算法进行图片拟合相同,在此不再赘述,我们现在直接开始算法主体的实现。同样为了方便理解,将代码分割成若干部分介绍,完整代码可以在文末下载。

初始化

初始化与遗传算法的实现相同

def __init__(self, imgPath, saveName="temp", alpha=5, beta=0.5, maxgroup=200, features=100, epochs=1000):self.orignal_img, self.type, self.row, self.col = self.OpenImg(imgPath)    self.max_group = maxgroup    self.saveName = saveName    self.groups = []    self.features = features    self.epochs = epochs    self.group_dict = dict()    self.alpha = alpha    self.beta = beta    if not os.path.exists(saveName):os.mkdir(saveName)    print("初始化...")    for i in range(randint(self.max_group, self.max_group * 2)):g = [] for j in range(self.features):tmp = [[choice(np.linspace(0, self.row, features)), choice(np.linspace(0, self.col, features))] for x in range(3)]     tmp.append("#" + ''.join(choice('0123456789ABCDEF') for x in range(6)))     g.append(tmp.copy()) self.groups.append(g.copy())    self.maxg = self.groups[0]    print("初始化完成!")

获取抗体与抗体的相似度

我们使用structural_similarity函数来比较两个抗体的相似度

def get_antibody_similar(self, g1, g2):    ssim = structural_similarity(np.array(g1), np.array(g2), multichannel=True)    return ssim

克隆与变异

此过程与遗传算法同过程相同

注:在遗传算法中生物繁殖的后代不会与父辈和母辈完全相同,但是在抗体是可以做到完全一致的。所以在breeds函数在遗传算法中代表的是繁殖过程,而在免疫遗传算法的抗体的克隆和变异中,函数breeds代表的是抗体的变异。按照流程来讲免疫算法还应该具有克隆抗体的过程,而这里我们只需保留原有的抗体即可以看作是克隆过程已完成。为了方便对照,所以我们保留了原有的函数名。

def exchange(self, father, mother)->[]:    # 交换    # 随机生成互换个数    min_locate = min(len(father), len(mother))    n = randint(0, int(randint(25, 100) / 100 * min_locate))    # 随机选出多个位置    selected = sample(range(0, min_locate), n)    # 交换内部    for s in selected: father[s], mother[s] = mother[s], father[s]    # 交换尾部    locat = randint(0, min_locate)    fhead = father[:locat].copy()    mhead = mother[:locat].copy()    ftail = father[locat:].copy()    mtail = mother[locat:].copy()    # print(fhead, ftail, mhead, mtail)    fhead.extend(mtail)    father = fhead    mhead.extend(ftail)    mother = mhead    return [father, mother]def mutation(self, gen):    # 突变    # 随机生成变异个数    n = int(randint(1, 100) / 1000 * len(gen))    selected = sample(range(0, len(gen)), n)    for s in selected: tmp = [[choice(np.linspace(0, self.row, 100)), choice(np.linspace(0, self.col, 100))] for x in     range(3)] tmp.append("#" + ''.join(choice('0123456789ABCDEF') for x in range(6))) gen[s] = tmp    return gendef move(self, gen):    # 易位    exchage = int(randint(1, 100) / 1000 * len(gen))    for e in range(exchage): sec1 = randint(0, len(gen) - 1) sec2 = randint(0, len(gen) - 1) gen[sec1], gen[sec2] = gen[sec2], gen[sec1]    return gendef add(self, gen):    # 增加    n = int(randint(1, 100) / 1000 * len(gen))    for s in range(n): tmp = [[choice(np.linspace(0, self.row, self.features)), choice(np.linspace(0, self.col, self.features))] for x in range(3)] tmp.append("#" + ''.join(choice('0123456789ABCDEF') for x in range(6))) gen.append(tmp)    return gendef cut(self, gen):    # 减少    n = int(randint(1, 100) / 1000 * len(gen))    selected = sample(range(0, len(gen)), n)    g = []    for gp in range(len(gen)): if gp not in selected:    g.append(gen[gp])    return gdef variation(self, gen):    # 变异    gen = self.mutation(gen.copy())    gen1 = self.move(gen.copy())    gen2 = self.add(gen1.copy())    gen3 = self.cut(gen2.copy())    return [gen, gen1, gen2, gen3]def breeds(self, father, mother):    # 繁殖    new1, new2 = self.exchange(father.copy(), mother.copy())    # 变异    new3, new4, new5, new6 = self.variation(father.copy())    new7, new8, new9, new10 = self.variation(mother.copy())    return [new1, new2, new3, new4, new5, new6, new7, new8, new9, new10]

获取抗体亲和度和抗体浓度

这里传进来的参数g_img是各个抗体的图片格式,通过两层循环获取抗体亲浓度和抗体亲和度

def get_aff(self, g_img):    # 抗体浓度和抗体亲和度(抗体与抗体间的亲和度和抗体与抗原的亲和度)    group_len = len(g_img)    den = np.zeros((group_len, group_len))    aff = np.zeros(group_len)    for i in range(group_len): for j in range(i, group_len):     if self.get_antibody_similar(g_img[i], g_img[j]) > 0.75:  den[i][j] += 1  den[j][i] += 1 aff[i] = self.getSimilar(g_img[i])    return aff, den.sum(axis=0) / group_len

抗体集合更新

首先将抗体转化成图片格式,再求出抗体浓度和抗体亲和度,根据抗体亲和度更新抗体集合。

其中对于抗体集合的更新由抗体浓度den和抗体亲和度aff决定。

假设我们将这个决定方式规定为激励算子sim,我们做以下假设:
s i m = a f f − A ∗ d e n sim=aff-A*den sim=affAden
这里的A我们多思考一些,根据抗体更新的实质我们可以看出,最开始的时候抗体和抗原的亲和度十分的低,其值都十分随机,这时影响优化效果的因素与亲和度aff相关性要比浓度den相关性大得多,这时就应该很小,而当他接近完全拟合时浓度den的相关性就变得更高了。由直觉可知,A的增长应该呈指数级

假设我们要得到取值范围在0-maxn的A,我们可以由以下推导
假 设 : 0 ≤ea − 1 ≤ m a x n = > 1 ≤ea ≤ m a x n + 1 = > l n 1 ≤ a ≤ l n ( m a x n + 1 ) = >e l n 1 ≤ea ≤e l n ( m a x n + 1 )假设:0 \leq e^a-1 \leq maxn \\ =>1 \leq e^a \leq maxn +1 \\ =>ln1 \leq a \leq ln(maxn+1) \\ =>e^{ln1} \leq e^a \leq e ^{ln(maxn+1)} :0ea1maxn=>1eamaxn+1=>ln1aln(maxn+1)=>eln1eaeln(maxn+1)

由此如果我们要得到取值范围在minn-maxn之间的A,再考虑到拟合的轮次我们可以得到
A =e l n ( m a x n + 1 − m i n n ) m a x _ e p o c h − e p o c h + 1 − 1 + m i n n A=e^{\frac{ln(maxn+1-minn)}{max\_epoch - epoch+1}}-1+minn A=emax_epochepoch+1ln(maxn+1minn)1+minn
通过这个公式我们可以实现对于A参数的动态调整,当然这里也可以换成其他表达式或者设成一个定值(定值也可以看作一个特殊的表达式)

def eliminated(self, groups, epoch):    g_img = []    for g in groups: g_img.append(self.to_image(g.copy()))    aff, den = self.get_aff(g_img)    sim = aff - (np.e  (np.log(self.alpha+1-self.beta) / (self.epochs - epoch + 1)) - 1 + self.beta) * den    sim = sim.tolist()    self.group_dict.clear()    self.group_dict = {key: value for key, value in enumerate(sim)}    self.group_dict = {key: value for key, value in    sorted(self.group_dict.items(), key=lambda item: item[1], reverse=True)}    g = []    Acc = 0    Den = 0    Sim = 0    for key in list(self.group_dict.keys())[:self.max_group]: if Acc == 0 and Den == 0 and Sim == 0:     Acc, Den, Sim = aff[key], den[key], sim[key] g.append(groups[key].copy())    return g, Acc, Den, Sim

拟合过程

拟合过程和遗传算法类似,只不过增添了一个创建新抗体的过程。

def fit(self):    self.groups, _, _, _ = self.eliminated(self.groups, 1)    for cur in range(self.epochs): # 繁殖过程 breed_n = randint(self.max_group // 2, self.max_group) g_f = np.abs(np.array(list(self.group_dict.values()))) p = g_f / np.sum(g_f) for i in range(breed_n):     f = np.random.choice(list(self.group_dict.keys()), p=p.ravel())     m = np.random.choice(list(self.group_dict.keys()), p=p.ravel())     if f < self.max_group and m < self.max_group:  self.groups.extend(self.breeds(self.groups[int(f)].copy(), self.groups[int(m)].copy())) for i in range(randint(0, self.max_group // 2)):     g = []     for j in range(self.features): tmp = [[choice(np.linspace(0, self.row, self.features)), choice(np.linspace(0, self.col, self.features))] for      x in range(3)] tmp.append("#" + ''.join(choice('0123456789ABCDEF') for x in range(6))) g.append(tmp.copy())     self.groups.append(g.copy()) # 淘汰 self.groups, Acc, Den, Sim = self.eliminated(self.groups.copy(), cur+1) print("Epochs :", cur+1, " Acc:", Acc, " Den:", Den, " Sim:", Sim) with open("Log.txt", 'a') as f:     f.write(str(Acc))     f.write(" ")     f.write(str(Den))     f.write(" ")     f.write(str(Sim))     f.write("\n") if cur % 100 == 0:     self.draw_image(self.groups[0], cur) if Acc >= 0.95:     break    self.draw_image(self.groups[0], "End")

训练过程评估

我们可以用以下代码进行画图评估

Acc = []Den = []Sim = []with open('Log.txt') as f:    for s in f.readlines(): s = s.strip() s1 = s.split(" ") Acc.append(float(s1[0])) Den.append(float(s1[1])) Sim.append(float(s1[2]))import matplotlib.pyplot as pltplt.plot(range(len(Acc)), Acc, label='Acc')plt.plot(range(len(Den)), Den, label='Den')plt.plot(range(len(Sim)), Sim, label='Sim')plt.legend()plt.show()

训练效果对比

我们以小蓝鸟为例进行拟合情况的对比
在这里插入图片描述

遗传算法部分拟合图像

在这里插入图片描述

免疫遗传算法部分拟合图像

在这里插入图片描述

对比总结

我们可以首先来观察一下遗传算法的拟合效果,可以看出有很多轮都得到相似的结果,这样显然浪费了大量的时间去拟合,其原因是随着不断地更新,所有的个体都会向着同一方向优化,这时难免会出现大量相似的个体,而免疫遗传算法引入抗体浓度这一概念,可以使得浓度高的抗体被抑制,从而保障了个体特征的多样性。不过由于引入了计算抗体浓度这一过程,每一轮拟合要比遗传算法慢,但他有效的避免了大量相似个体的存在,所以总起来讲会大大减少拟合需要的论次数。完全拟合的时间会比遗传算法早。

完整代码下载

CSDN下载地址:https://download.csdn.net/download/DuLNode/85110172
GitHub下载地址:https://github.com/AiXing-w/Python-Intelligent-Optimization-Algorithms

怎么买股票