【机器学习】多目标遗传算法+模拟退火算法

【python】机器学习 多目标遗传算法

import numpy as np

#  --------------------------------------------
#               参数设置
#  --------------------------------------------
DNA_SIZE = 5  # DNA长度
POP_SIZE = 50  # 初始种群数
CROSSOVER_RATE = 0.95  # 交叉概率
MUTATION_RATE = 0.01  # 变异概率
N_GENERATIONS = 100  # 迭代次数
# --------------------------------------------
SAMPLE_SIZE = 123  # 样本数
x1_BOUND = [100000, 1000000]
x2_BOUND = [0.04, 15]


class SAMPLE():  # 单次样本结构--------------------------------------------
    def __init__(self):
        self.sample = []
        self.decode_data = []
        self.num = SAMPLE_SIZE

    def pop_make(self, dna):
        for i in range(len(dna)):
            self.sample.append([])
        for i in range(len(dna)):  # i:种群数
            for j in range(self.num):  # j:每个种群内的个体数
                self.sample[i].append(dna[i][j::self.num])

    def decode(self, bound):
        for i in self.sample:
            a_sample = []
            for j in i:
                a_price = np.dot(j, 2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (bound[1] - bound[0]) + \
                          bound[0]
                a_sample.append(a_price)
            self.decode_data.append(a_sample)


class GA():  # 遗传算法--------------------------------------------
    def __init__(self):
        # 每次迭代中最好的数据
        self.fitness = []
        self.y1 = []
        self.y2 = []
        self.y3 = []
        self.x1 = []
        self.x2 = []

    # 解码过程 ----------------------------------------------------------------------------------------
    def translateDNA(self, DNA, bound):
        sample = SAMPLE()
        sample.pop_make(DNA)
        sample.decode(bound)
        return sample.decode_data

    # 交叉变异 ----------------------------------------------------------------------------------------
    def crossover_and_mutation(self, DNA, CROSSOVER_RATE=CROSSOVER_RATE):
        new_DNA = []
        for father in DNA:
            child = father
            if np.random.rand() < CROSSOVER_RATE:
                mother = DNA[np.random.randint(POP_SIZE)]
                cross_points = np.random.randint(low=0, high=DNA_SIZE * SAMPLE_SIZE)
                child[cross_points:] = mother[cross_points:]
            if np.random.rand() < MUTATION_RATE:
                mutate_point = np.random.randint(0, DNA_SIZE * SAMPLE_SIZE)
                child[mutate_point] = child[mutate_point] ^ 1
            new_DNA.append(child)
        return new_DNA

    # 计算适应值 --------------------------------------------------------------------------------------
    def get_fitness(self, DNA1, DNA2):
        """
        对DNA解码-->x
        x-->y
        y-->fitness
        show函数:
            记录最大的fitness值,便于画图
            记录x和y,便于添加到附件
        """

        x1 = self.translateDNA(DNA1, x1_BOUND)
        x2 = self.translateDNA(DNA2, x2_BOUND)

        y1 = []
        y2 = []

        fitness = []

        self.show(fitness, x1, x2, y1, y2, y3)
        return fitness, y1, y2, y3

    # 选择适应值高的个体 ------------------------------------------------------------------------------
    def select(self, DNA, DNA2, fitness):
        fitness = fitness - np.min(fitness) + 1e-3
        idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True, p=fitness / fitness.sum())
        return DNA[idx], DNA2[idx]

    # 每次迭代的信息print --------------------------------------------
    def show(self, fitness, x1, x2, y1, y2, y3):
        """
        记录最大的fitness到self中,便于画图(fitness[index])
        将对应的x和y也记录到self中,便于储存到本地(x[index],y[index])
        """
        max_fitness = max(fitness)
        index = fitness.index(max_fitness)
        self.fitness.append(max_fitness)
        self.y1.append(y1[index])
        self.y2.append(y2[index])
        self.y3.append(y3[index])
        self.x1.append(x1[index])
        self.x2.append(x2[index])
        print("银行收益:{:.4f}".format(y1[index]))
        print("流失率总和:{:.4f}".format(y3[index]))

    # 每次迭代记录 ---------------------------------------------------
    def load(self):
        path_1 = r"……"
        path_2 = r"……"
        header = ["……", "……"]
        write_excel(self.x1, path_1)
        write_excel(self.x2, path_2)

    # 30次迭代画图 --------------------------------------------
    def draw(self):
        y1_name = "y1"
        y2_name = "y2"
        save_path = r"C:\Users\Administrator\Desktop\a.png"

        import matplotlib.pyplot as plt
        x = range(1, N_GENERATIONS + 1)
        fig = plt.figure(figsize=(20, 10))
        plt.style.use('seaborn-whitegrid')

        ax1 = fig.add_subplot(311)
        plt.legend(fontsize=20)
        ax1.plot(x, self.fitness, linestyle=':', linewidth=1, marker='d', markersize=5, label="适应度")
        plt.ylabel("适应度", fontsize=20)

        ax2 = fig.add_subplot(211)
        ax2.plot(x, self.y1, linestyle='-.', linewidth=1, marker='+', markersize=5, label=y1_name)
        plt.ylabel(y1_name, fontsize=20)
        plt.legend(fontsize=20)

        ax2 = fig.add_subplot(212)
        ax2.plot(x, self.y3, linestyle='-.', linewidth=1, marker='^', markersize=5, label=y2_name)
        plt.xlabel("迭代次数", fontsize=20)
        plt.ylabel(y2_name, fontsize=20)
        plt.legend(fontsize=20)
        plt.savefig(save_path, bbox_inches='tight', dpi=1000)
        plt.show()


if __name__ == "__main__":
    DNA1 = np.random.randint(2, size=(POP_SIZE, DNA_SIZE * SAMPLE_SIZE))
    DNA2 = np.random.randint(2, size=(POP_SIZE, DNA_SIZE * SAMPLE_SIZE))

    ga = GA()
    for i in range(N_GENERATIONS):
        print("迭代第%d次-------------------------------------------- " % (i + 1))
        DNA1 = np.array(ga.crossover_and_mutation(DNA1, CROSSOVER_RATE))
        DNA2 = np.array(ga.crossover_and_mutation(DNA2, CROSSOVER_RATE))
        fitness, y1, y2, y3 = ga.get_fitness(DNA1, DNA2)
        DNA1, DNA2 = ga.select(DNA1, DNA2, fitness)
    ga.draw()
    ga.load()

【python】机器学习 遗传算法

简单思路就是:

        DNA = np.random.randint(2, size=(POP_SIZE, DNA_SIZE * NUM))
        for i in range(N_GENERATIONS):
            将DNA分为NUM个矩阵,矩阵的每一行翻译为一个数字
            DNA变异交叉
            计算适应度
            物竞天择

代码:

"""
        --------------------------------------------
            DNA_SIZE:       DNA长度,长度越长精度越高
            POP_SIZE:       初始种群数
            CROSSOVER_RATE: 交叉概率
            MUTATION_RATE:  变异概率
            N_GENERATIONS:  迭代次数
            X1_BOUND:       x1自变量范围
            X2_BOUND:       x2自变量范围
            NUM:             自变量数量
        --------------------------------------------
"""

import numpy as np

# 初始化群体----------------------------------------------------------------------------------------
DNA_SIZE = 5  # DNA长度,x1,x2各5个,长度越长精度越高
POP_SIZE = 50  # 初始种群数
CROSSOVER_RATE = 0.95  # 交叉概率
MUTATION_RATE = 0.01  # 变异概率
N_GENERATIONS = 30  # 迭代次数
X1_BOUND = [-1, 2]  # x1自变量范围
X2_BOUND = [-1, 2]  # x2自变量范围


# 定义函数----------------------------------------------------------------------------------------
def function(x1, x2):
    # y = f(x1,x2)
    return y


class GA():
    # 解码过程 ----------------------------------------------------------------------------------------
    def translateDNA(self, DNA):
        # DNA表示种群矩阵,一行表示一个二进制编码表示的DNA,矩阵的行数为种群数目
        x1_DNA = DNA[:, 1::2]  # 前DNA_SIZE位表示x1 (1::2:从下标1开始,步长2)
        x2_DNA = DNA[:, ::2]   # 后DNA_SIZE位表示x2

        # DNA:(POP_SIZE,DNA_SIZE)*(DNA_SIZE,1) --> (POP_SIZE,1)
        # # 转二进制 --> 除2的SIZE次方 --> 摆开到bound间
        x1 = x1_DNA.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (X1_BOUND[1] - X1_BOUND[0]) + \
             X1_BOUND[0]
        x2 = x2_DNA.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (X2_BOUND[1] - X2_BOUND[0]) + \
             X2_BOUND[0]
        return x1, x2

    # 交叉过程 ----------------------------------------------------------------------------------------
    def crossover_and_mutation(self, DNA, CROSSOVER_RATE=CROSSOVER_RATE):
        new_DNA = []
        for father in DNA:  # 遍历种群中的每一个个体,将该个体作为父亲
            child = father  # 孩子先得到父亲的全部基因(这里我把一串二进制串的那些0,1称为基因)
            if np.random.rand() < CROSSOVER_RATE:  # 产生子代时不是必然发生交叉,而是以一定的概率发生交叉
                mother = DNA[np.random.randint(POP_SIZE)]  # 再种群中选择另一个个体,并将该个体作为母亲
                cross_points = np.random.randint(low=0, high=DNA_SIZE * 2)  # 随机产生交叉的点
                child[cross_points:] = mother[cross_points:]  # 孩子得到位于交叉点后的母亲的基因
            self.mutation(child)  # 每个后代有一定的机率发生变异
            new_DNA.append(child)
        return new_DNA

    # 变异过程 ----------------------------------------------------------------------------------------
    def mutation(self, child, MUTATION_RATE=MUTATION_RATE):
        if np.random.rand() < MUTATION_RATE:  # 以MUTATION_RATE的概率进行变异
            mutate_point = np.random.randint(0, DNA_SIZE * 2)  # 随机产生一个实数,代表要变异基因的位置
            child[mutate_point] = child[mutate_point] ^ 1  # 将变异点的二进制为反转

    # 计算适应值 ----------------------------------------------------------------------------------------
    def get_fitness(self, DNA):
        x1, x2 = self.translateDNA(DNA)
        pred = function(x1, x2)
        return (pred - np.min(pred)) + 1e-3
    '''
    减去最小的适应度是为了防止适应度出现负数,
    通过这一步fitness的范围为[0, np.max(pred)-np.min(pred)],
    最后在加上一个很小的数防止出现为0的适应度
    '''

    # 选择适应值高的个体 ----------------------------------------------------------------------------------------
    def select(self, DNA, fitness):  # nature selection wrt DNA's fitness
        idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True, p=fitness / fitness.sum())
        return DNA[idx]


if __name__ == "__main__":
    DNA = np.random.randint(2, size=(POP_SIZE, DNA_SIZE * 2))
    ga = GA()
    for i in range(N_GENERATIONS):
        x1, x2 = ga.translateDNA(DNA)
        DNA = np.array(ga.crossover_and_mutation(DNA, CROSSOVER_RATE))
        fitness = ga.get_fitness(DNA)
        DNA = ga.select(DNA, fitness)

【python】机器学习 模拟退火算法

简单的思路是:

T = T0
        while T > Tf:
            random随机产生iter个x,即X=[x0,x1,x2,x3,...]
            循环iter次:
                对于xi,求fi
                基于xi,产生新值x'
                    x'=x+T*(random()-random())
                M-H判断是否接受新值
                    计算f(x'),如果f(x')比fi更符合条件,接受
                    否则:
                        random()<exp(△f/T),接受
                        否则:拒绝
            基于此时的X,计算F=[f0,f1,f2,f3,……]
            取最符合条件的f_best
            T = T * alpha

代码:

"""
        --------------------------------------------
            x:      所求变量
            f:      f(x)为目标函数
            iter:   内循环值
            T0:    初始温度
            Tf:     终止温度
            alpha:  降温系数
        --------------------------------------------        
"""

import math
from random import random


def Function(x, y):
    res = 4 * x ** 2 - 2.1 * x ** 4 + x ** 6 / 3 + x * y - 4 * y ** 2 + 4 * y ** 4
    return res


class SA:
    def __init__(self, iter=100, T0=100, Tf=0.01, alpha=0.99):
        self.iter = iter    # 内循环迭代次数,即为L =100
        self.alpha = alpha  # 降温系数,alpha=0.99
        self.T0 = T0        # 初始温度T0为100
        self.Tf = Tf        # 温度终值Tf为0.01
        self.T = T0         # 当前温度
        self.x = [random() * 11 - 5 for i in range(iter)]  # 随机生成100个x的值
        self.y = [random() * 11 - 5 for i in range(iter)]  # 随机生成100个y的值
        self.most_best = []

    # 扰动产生新解的过程----------------------------------------------------------------------------------------
    def generate_new(self, x, y):
        while True:
            x_new = x + self.T * (random() - random())
            y_new = y + self.T * (random() - random())
            if (-5 <= x_new <= 5) & (-5 <= y_new <= 5):
                break  # 重复得到新解,直到产生的新解满足约束条件
        return x_new, y_new

    # Metropolis准则----------------------------------------------------------------------------------------
    def Metrospolis(self, f, f_new):
        if f_new <= f:
            return 1
        else:
            p = math.exp((f - f_new) / self.T)
            if random() < p:
                return 1
            else:
                return 0

    # 获取最优目标函数值----------------------------------------------------------------------------------------
    def best(self):
        f_list = []  # f_list数组保存每次迭代之后的值
        for i in range(self.iter):
            f = Function(self.x[i], self.y[i])
            f_list.append(f)
        f_best = min(f_list)

        idx = f_list.index(f_best)
        return f_best, idx  # f_best,idx分别为在该温度下,迭代L次之后目标函数的最优解和最优解的下标

    # 主函数----------------------------------------------------------------------------------------
    def run(self):
        while self.T > self.Tf:
            for i in range(self.iter):
                f = Function(self.x[i], self.y[i])  # f为迭代一次后的值
                x_new, y_new = self.generate_new(self.x[i], self.y[i])  # 产生新解
                f_new = Function(x_new, y_new)  # 产生新值
                if self.Metrospolis(f, f_new):  # 判断是否接受新值
                    self.x[i] = x_new  # 如果接受新值,则把新值的x,y存入x数组和y数组
                    self.y[i] = y_new
            # 迭代L次记录在该温度下最优解
            ft, _ = self.best()
            # 温度按照一定的比例下降(冷却)
            self.T = self.T * self.alpha

        # 得到最优解
        f_best, idx = self.best()
        print(f"F={f_best}, x={self.x[idx]}, y={self.y[idx]}")


if __name__ == "__main__":
    SA().run()

posted @ 2024-01-18 20:17  jijfurhg  阅读(135)  评论(0)    收藏  举报