python实现免疫算法,并绘制训练过程

免疫算法同遗传算法相似,不过子代是克隆出来的,而不是交叉,并且引入了抗体间亲和度的概念,算出抗体适应度之后,我们还需要减去抗体间亲和度,从而使得结果不容易陷入局部最优。

注意代码里的n是仅仅x的位数,基因里有x有y,所以基因型实际长度是2n

fitness_weight和concentration_weight这两个参数有兴趣的可以自己调调大小,看看变化

主要公式:激励度 = a*适应度+b*浓度

激励度是抗体克隆时,谁激励度大谁就能更容易被克隆

适应度就是抗体带入函数的结果归一化后的值

浓度这里时抗体间亲和度,用的海明距离。

a = fitness_weight,b = concentration_weight,注意一般b为负数,

采用抗体间亲和度可以给一些离群的抗体更多机会。

代码如下,训练结果建议自己运行,此处不放置结果视频

import numpy as np
import matplotlib.pyplot as plt
import random
import time  # 暂停用的,方便我录像,你们不需要


# 所用的函数
def Function(x_data, y_data):
    """
    :param x_data: x数值
    :param y_data: y数值
    :return: 输出表达式计算出的z
    """
    # 本来想找个能可视化捏函数,给表达式的方法,在matlab绘图中发现这个表达式长得不错,就直接用了。
    return 3 * (1 - x_data) ** 2 * np.exp(-x_data ** 2 - (y_data + 1) ** 2) - 10 * (
            x_data / 5 - x_data ** 3 - y_data ** 5) * np.exp(
        -x_data ** 2 - y_data ** 2) - np.exp(-(x_data + 1) ** 2 - y_data ** 2)


def Get_Grid():  # 生成坐标网格
    """
    :return: 返回Function的x,y,z
    """
    # 生成坐标网格
    x = np.linspace(-4, 4, 100)  # 坐标轴是-3~3,100个均匀分布,为了个体不跑到图片外,修改至-4~4
    y = np.linspace(-4, 4, 100)
    x, y = np.meshgrid(x, y)  # 按刚刚的坐标轴生成二维矩阵
    z = np.array(Function(x, y))  # 调用生成函数,获得y值
    return x, y, z


def Get_Random_gene(number, n):  # 随机生成基因型
    """
    :param number: 生成个数
    :param n: x的总位数
    :return: 生成的族群
    """
    return np.random.randint(0, 2, size=(number, n + n))


def Plot_Draw_F(fig, x, y, z):  # 绘图,重新绘制F的图像,返回引用
    """
    :param fig:     窗口的引用
    :param x:
    :param y:
    :param z:
    :return:    axes_3d,画布引用
    """
    fig.clf()
    axes_3d = fig.add_subplot(111, projection='3d')
    cmap = plt.cm.viridis  # 设定变色的颜色,可选项:viridis, plasma, inferno, magma, cividis 等
    norm = plt.Normalize(vmin=-5, vmax=5)  # 颜色变化范围,不设置就是按z轴最大最小,
    img_f = axes_3d.plot_surface(x, y, z, rstride=1, cstride=1, alpha=0.75, cmap=cmap, norm=norm)  # 绘制3D图
    # 长得还是有点抽象,一会发一下三视图,就能知道函数大概形状了
    # 添加颜色条
    cbar = fig.colorbar(img_f, ax=axes_3d)
    cbar.set_label('Color')
    # 设置坐标轴范围和标签
    axes_3d.set_xlim(-4, 4)
    axes_3d.set_ylim(-4, 4)
    axes_3d.set_zlim(-10, 10)
    axes_3d.set_xlabel('X')
    axes_3d.set_ylabel('Y')
    axes_3d.set_zlabel('Z')
    return axes_3d


def Plot_Scatter(ax, plot_gene_data, plot_z, colour):  # 根据解码后数据绘制种群的散点图
    """
    :param ax: 画布引用
    :param plot_z: 计算出的z值
    :param plot_gene_data: 全部基因型转码后的数据
    """
    for i in range(len(plot_z)):
        ax.scatter(plot_gene_data[i][0], plot_gene_data[i][1], plot_z[i], c=colour, marker='o')
    # 刷新图形
    plt.draw()
    plt.pause(1e-3)


# 解码,将全部二进制基因型数据转换为数值
def Decoding(data, n, point):  # 输入的分别是要解码的列表,x,y,的位数,小数位数
    """
    :param data: 要解码的列表,[[x符号,x整数部分,x整数部分,x整数部分,x小数部分,x小数部分,······y符号,y整数部分,x整数部分,x整数部分,y小数部分,y小数部分,],]
    :param n: x的总位数
    :param point: 小数位数
    :return: 二进制基因型数据转换的数值
    """
    # 在这个例子中,x,y的取值范围为-3~3,整数刚好整2位,加一位符号位,加上小数部分就-4~4了(为了不跑到图像外,修改一下图像范围),
    # 小数部分不用太多,整个8位,就差不多够了,所以前11位x,后11位y,正负只看第一个符号,1正0负
    # [x符号,x整数部分,x整数部分,x整数部分,x小数部分,x小数部分,······y符号,y整数部分,x整数部分,x整数部分,y小数部分,y小数部分,]
    decode_data = []
    for i in data:  # 遍历每个个体,转码
        x = Decoding_to_decimal(i[0:n], n, point)
        y = Decoding_to_decimal(i[n:], n, point)
        decode_data.append([x, y])
    return decode_data


def Decoding_to_decimal(data, n, point):
    # 仅一个x或y的转换
    integer_len = n - point
    decimal_data = 0
    for i in range(1, integer_len):  # 整数部分 2^n n=0,1,2···
        decimal_data += data[i] * 2 ** (integer_len - i - 1)
    for i in range(point):  # 小数部分 1/2^n n = 1,2,3···
        decimal_data += data[i + integer_len] / 2 ** (i + 1)
    return (data[0] * 2 - 1) * decimal_data


def Get_gene_z(data):  # 根据解码后数据,计算z值
    """
    :param data: 全部基因型转码后的数据
    :return: z值,z最大值,z最小值
    """
    data_z = []
    max_z = -float("inf")
    min_z = float("inf")
    for i in range(len(data)):
        data_z.append(Function(data[i][0], data[i][1]))
        if data_z[i] > max_z:
            max_z = data_z[i]
        if data_z[i] < min_z:
            min_z = data_z[i]
    return data_z, max_z, min_z


def Get_Fitness(data, max_data, min_data, maximum):  # 计算适应度,这里用z的归一化加次方
    """
    :param data: 需要计算的z值列表
    :param max_data: z最大值
    :param min_data: z最小值
    :param maximum: 数值较大适应度高?
    :return: 适应度列表
    """
    gap = max_data - min_data  # 最大最小值的差距
    if (maximum):
        fitness = [((i - min_data) / gap) for i in data]  # 归一化
    else:
        fitness = [((max_data - i) / gap) for i in data]  # 归一化
    return fitness


def Get_Concentration(data, n):  # 计算抗体间亲和度,这里用海明距离
    # 海明距离: 看所有位上的值,一样亲和度+1,
    lendata = len(data)
    concentration = []
    for i in range(lendata):
        concentration.append(0)
        for j in data:
            for k in range(n + n):
                if data[i][k] == j[k]:
                    concentration[i] += 1
        concentration[i] /= lendata * (n + n)
    return concentration


def Get_Incentive(fitness, concentration, fitness_weight, concentration_weight):  # 计激励,激励=a*适应度-b*浓度
    lenfitness = len(fitness)
    data_array = [(fitness[i] * fitness_weight + concentration[i] * concentration_weight) for i in range(lenfitness)]
    min_value = np.min(data_array)
    max_value = np.max(data_array)
    return (data_array - min_value) / (max_value - min_value)


def Inheritance(parents, n):  # 克隆时,变异
    child = []  # 生出的孩子
    for i in range(n + n):  # 遍历每个基因点
        child.append(parents[0][i])  # 继承一个基因
        # 较高概率突变
        if random.random() < 0.05:
            child[i] = random.randint(0, 1)
    if random.random() < 0.01:  # 小概率全逆置
        child.reverse()
    return child


def Clone(number, data, n, incentive):  # 让抗体克隆到原先族群大小
    """
    :param number: 族群大小
    :param data:生育前的抗体基因
    :param n: x的总位数
    :param point: 小数位数
    :param incentive:激励度
    :return: 克隆完成的抗体
    """
    new_data = []
    initial_len = len(data)  # 初始个数
    if initial_len < 1:
        print("种族没人")
    for i in range(number):  # 克隆够了就停下
        parents = random.choices(data, weights=incentive)  # 根据激励随机选择一个抗体克隆
        new_data.append(Inheritance(parents, n))  # 克隆的抗体添加进族群
    return new_data


def Immunity_train(fig, gene_data, number, n, point, loop, x, y, z, fitness_weight=1, concentration_weight=-0.1,
                   maximum=True, ):  # 免疫算法训练,带过程绘制
    """
    :param fig: 窗口引用
    :param gene_data: 基因型
    :param number: 族群大小
    :param n: x的总位数
    :param point: 小数位数
    :param fitness_weight 相似度系数
    :param concentration_weight 浓度系数
    :param maximum: 是否求函数最大值,默认是
    :return: 最终的族群
    """
    new_gene = gene_data  # 开始的输入就是新族群
    for i in range(loop):  # 最大训练loop轮
        #
        gene_data = new_gene
        decode_data = Decoding(gene_data, n, point)  # 解码
        data_z, max_z, min_z = Get_gene_z(decode_data)  # 计算z
        fitness = Get_Fitness(data_z, max_z, min_z, maximum)
        concentration = Get_Concentration(gene_data, n)
        incentive = Get_Incentive(fitness, concentration, fitness_weight, concentration_weight)  # 求适应度
        ax = Plot_Draw_F(fig, x, y, z)  # 绘画出函数
        Plot_Scatter(ax, decode_data, data_z, "blue")  # 绘制全部个体
        if max_z - min_z < 1e-2:  # 认为训练完毕
            break
        # 开始克隆
        new_gene = Clone(number, gene_data, n, incentive)  # 抗体根据激励克隆到原先数目
    return new_gene


if __name__ == "__main__":
    # 建立窗口
    fig = plt.figure()
    # 生成坐标网格
    x, y, z = Get_Grid()
    plt.pause(1)  # 方便录像用,开窗口后等1秒再出图,你们建议删去
    # 参数设置
    number = 100  # 种群初始大小
    point = 15  # 小数位数
    n = point + 3  # x或y长度
    loop = 100  # 最大训练轮数
    gene_data = Get_Random_gene(number, n)  # 获得初始抗体基因
    gene_end = Immunity_train(fig, gene_data, number, n, point, loop, x, y, z,maximum=True)  # 免疫训练
    # 显示图形,完成后不消失
    plt.show()

 python实现免疫算法,并绘制训练过程_第1张图片

你可能感兴趣的:(python,算法,开发语言)