Python implements the immune algorithm and draws the training process

The immune algorithm is similar to the genetic algorithm, but the offspring are cloned instead of crossed, and the concept of affinity between antibodies is introduced. After calculating the antibody fitness, we also need to subtract the affinity between antibodies, so that the result It is not easy to fall into local optimum.

Note that n in the code is only the number of digits of x. There are x and y in the gene, so the actual length of the genotype is 2n

If you are interested in these two parameters, fitness_weight and concentration_weight, you can adjust them yourself and see the changes.

Main formula: motivation = a*fitness+b*concentration

When the incentive degree is antibody cloning, whoever has a higher incentive degree can be cloned more easily.

Fitness is the normalized value of the result brought into the function by the antibody.

The concentration here is the affinity between antibodies, and the Hamming distance is used.

a = fitness_weight, b = concentration_weight, note that b is generally a negative number,

Using inter-antibody affinity can give some outlier antibodies more opportunities.

The code is as follows. It is recommended to run the training results by yourself. The result video is not placed here.

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()

 

Guess you like

Origin blog.csdn.net/weixin_58196051/article/details/133861110
Recommended