编程语言
首页 > 编程语言> > Python 运筹优化9 再次拆解基本的遗传算法

Python 运筹优化9 再次拆解基本的遗传算法

作者:互联网

说明

爹有娘有,不如自个有

成熟的包稍微参考一下,或者在某些场景下对付一下即可。核心的部件还是要自己研发。本篇从0.1开始(因为以前已经写过一篇),构造一个新的可迭代版本。

目标:

内容

先对遗传算法进行一些梳理(和增强)

遗传算法的灵感来自与遗传学与进化论。首先,会出现一批种群(道生一,谁知道为什么有初始种群?),然后种群开始变化与繁殖(一生二),优胜的个体将获得奖励(更多的资源)输掉的个体将只剩下越来越少的资源,缩减规模,甚至灭绝。

简单来说,遗传算法应该包含以下步骤:

1 逐步分析

之前的文章,已经对基础的遗传算法做了一次介绍,当时这篇文章是参考一个国外的文章,作为入门挺不错的。

接下来我将会打乱原来的函数,按逻辑从头到尾过一遍

1.1 数据和场景

实验使用的是sklearn自带的一组数据,很容易获得

import numpy as np
import pandas as pd
import random
import matplotlib.pyplot
%matplotlib inline

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

#import the breast cancer dataset 
from sklearn.datasets import load_breast_cancer
cancer=load_breast_cancer()
df = pd.DataFrame(cancer['data'],columns=cancer['feature_names'])
label=cancer["target"]

In [2]: df.shape
Out[2]: (569, 30)

In [5]: label
Out[5]:
array([0, 0, 0, 0, 0, 0, 0,...]

X数据是一个规整的数值型表格,可以视为数字矩阵,共569行,30列
y数据是一个一维数组,长度和X一致。

1.2 二分类任务

假设数据已经处理干净(通常二分类逻辑回归会使用woe变换,这里不管了)

# 训练
import time
#splitting the model into training and testing set
X_train, X_test, y_train, y_test = train_test_split(df, 
                                                    label, test_size=0.30, 
                                                    random_state=101)
start = time.time()
#training a logistics regression model
logmodel = LogisticRegression(max_iter=10000)
logmodel.fit(X_train,y_train)
predictions = logmodel.predict(X_test)
print("Accuracy = "+ str(accuracy_score(y_test,predictions)))
print('Takes %.2f' %(time.time()-start))

---
Accuracy = 0.9415204678362573
Takes 0.41

建模有时候看起来很简单,我们稍微再细看一些,下面是模型的系数(30列)

In [7]: logmodel.coef_
Out[7]:
array([[ 0.97941119,  0.228494  , -0.44281457,  0.04240152, -0.13129615,
        -0.23586711, -0.39840754, -0.20550894, -0.27029016, -0.03286082,
        -0.04423837,  0.70893065,  0.45604112, -0.09348297, -0.01702533,
        -0.01492817, -0.0620379 , -0.03248521, -0.07882716, -0.0019002 ,
         0.00896633, -0.48931184, -0.09643741, -0.0219997 , -0.24958195,
        -0.49180984, -0.83010242, -0.39272431, -0.7384095 , -0.07693224]])

简单总结一下问题:使用了30个变量,太多。

类似逻辑回归这样的模型,事实上不应该超过10个变量。否则可以想象一下,即使你要向客户解释模型变量就能把嘴说干。而且太多的变量容易导致过拟合,不稳定,细节就不展开了,总之不对。所以有类似AIC、BIC这样的准则来对变量的个数进行惩罚。

变量选择事实上是逻辑回归建模一个非常难的问题

实践上,基于什么前向、后向、逐步(Stepwise)之类的方法其实都不是特别有效。逻辑回归是一个可以、也必须要进行解释的模型。假设构成模型空间的变量事实上可以分为几组/几类,我们希望模式能够从每个类抽出一到两个变量来构成模型的解释变量。

所以逻辑回归建模过程的关键点通常是:

1.3 遗传算法实现

先来将一些概念梳理一下,然后将这个业务问题转换为遗传算法要解决的等价问题。

几个生物学上的概念(不一定准确):

染色体决定了了个体的内在特性,个体对于环境的适应性可以视为基因在环境下的表达(类似条件概率)。

之前在使用geatpy的时候发现,在进行初始化/迭代的过程中,基因位之间的变化都是独立的,这不是很科学。严格来说,一个基因位的变异会使得其他基因位的变化收到限制(条件概率);在现实中,基因位可能会按某种规则进行随机(链式),而不是独立随机。

毕竟我们不是搞生物的,只是借用这个概念而已。所以规范一下说法:

染色体是通过初始化、繁殖(交换基因位)、变异(随机突变)生成, 生成/删除的规则受到:

就目前这个实验而言,可以这么做等价转换

1.4 撸代码

首先有这么几个参数

# 种群大小
size=200
# 基因位(变量)
n_feat=30
# 每次迭代的生存者
n_parents=100
# 产生后代的变异几率
mutation_rate=0.10

函数1:生成初始种群中的个体

def process_get_a_chrome():
    chromosome = np.ones(n_feat,dtype=np.bool)
    chromosome[:int(0.3*n_feat)]=False
    np.random.shuffle(chromosome)
    return chromosome

# size设定了种群大小 = n个个体 = n个染色体 
population = [process_get_a_chrome() for x in range(size)]

函数2:评估一个种群中个体(变量选择)的适应性(准确率)

# 判定每个个体的“适应性”(训练后在测试集上的表现,或者说测试准确率)
def process_score_a_chrome(chromosome, X_train=X_train, X_test=X_test, y_train=y_train, y_test= y_test,logmodel = LogisticRegression(max_iter=10000)):
    logmodel.fit(X_train.iloc[:,chromosome],y_train)
    predictions = logmodel.predict(X_test.iloc[:,chromosome])
    return accuracy_score(y_test,predictions)

# 获取每个个体的适应性评分
time1 = time.time()
scores = [process_score_a_chrome(x) for x in  population]
time2 = time.time()

这步比较花时间,因为每个个体都要评估一遍,每次评估都要拟合一次模型参数(种群规模为200时需要约40秒左右)。加速的方法是将评估过程使用矩阵计算(最好是用GPU)。
如果不能用矩阵计算,也可以使用PM方法,把评估作为任务送到流程里,然后通过多台机器联合计算。应该提升1~2个数量级没有问题。

完整的函数如下:

def iter_many_gen(gens ,population = None,size=200,n_feat=30,n_parents=100,mutation_rate=0.10):
    if population is None:
        population = [process_get_a_chrome() for x in range(size)]
    
    for i in range(gens):
        print('>>>> begin iteration %s:' % i)
        # 获取每个个体的适应性评分
        time1 = time.time()
        scores = [process_score_a_chrome(x) for x in  population]
        time2 = time.time()

        time_spent1 = time2 - time1
        print('Evaluation Fitness Spent:%.2f, Max Score: %.6f' %(time_spent1, max(scores)))

        # 对打分排名
        inds = np.argsort(scores)

        # 当代保留的优秀个体
        cur_win_inds = inds < n_parents
        cur_wins = np.array(population)[cur_win_inds]


        # 均匀分为两组
        np.random.shuffle(cur_wins)

        pair_num = int(n_parents/2)
        groupa = copy.deepcopy(cur_wins[:pair_num])
        groupb = copy.deepcopy(cur_wins[pair_num:])
        # 交换基因位的方式
        # 使用两个随机变量进行交换

        # 1 横向的基因位
        gene_vec = np.random.rand(n_feat)

        # 2 纵向的随机控制器
        cross_control_vec =  np.random.rand(pair_num)
        # 升维以便广播
        cross_control_mat =  cross_control_vec.reshape(pair_num,-1)

        cross_sel_mat = gene_vec <cross_control_mat

        # 将两步分别进行 and 再进行 or进行交换
        child = (groupa&cross_sel_mat) | (groupb & ~cross_sel_mat)


        # 变异 10%
        child1 = copy.deepcopy(child)
        mutation_sel = np.random.rand(*child1.shape) <mutation_rate
        child1[mutation_sel] = ~child1[mutation_sel]


        # 下一代的个体
        nex_gen = np.r_[cur_wins, child, child1]

        time3 = time.time()
        time_spent2 = time3 - time2
        print('Make New Generation Takes:%.2f, Num: %s' %(time_spent2, len(nex_gen)))
        population = nex_gen
    return population

一开始我用种群规模为200跑,结果还是挺有意思的。有几轮迭代没什么效果0.94xxx,然后升高到0.95xxx,维持了几代,然后又掉下来(估计是因为变异),然后我用种群规模为2000跑,慢是慢了点,但是我猜会比较稳定

In [91]: evole_gen = iter_many_gen(20,size=2000,n_parents=1000)
>>>> begin iteration 0:
Evaluation Fitness Spent:396.57, Max Score: 0.953216
Make New Generation Takes:0.00, Num: 2000
>>>> begin iteration 1:
Evaluation Fitness Spent:378.01, Max Score: 0.953216
Make New Generation Takes:0.00, Num: 2000

...

标签:变量,Python,import,运筹,基因,train,time,test,遗传算法
来源: https://blog.csdn.net/yukai08008/article/details/122007174