基于Python实现的特征选择的遗传算法(GA)

本文介绍了一种基于遗传算法的特征选择方法,并通过Sonar数据集进行了实验验证。该方法能够有效减少特征维度,提高分类准确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

模式识别 特征选择的遗传算法

目录
模式识别 特征选择的遗传算法 0
一、特征选择的遗传算法介绍 1
1.1 特征选择 1
1.2 遗传算法(Genetic Algorithm) 1
二、实验数据集介绍 4
2.1 Iris数据集介绍 4
2.2 Sonar数据集介绍 4
三、实验设置 4
3.1 算法流程 4
1、读取数据集,按照一定比例随机分为测试集、训练集 5
2、编码及种群的初始化: 5
3、开始迭代,直至达到指定的迭代次数: 5
3.2 在数据集上验证遗传算法 6
四、实验结果展示与分析 7
4.1 在iris数据集上验证遗传算法 7
4.2 在sonar数据集上验证遗传算法 8
4.3 总结 10
五、Python代码 10
三、实验设置
3.1 算法流程
本次实验选用的是K近邻法作为分类器,来评价所选特征的好坏,用来计算种群的适应度函数。由于上述算法流程较为简略,在实际编程实现对编码、选择、交叉、变异又使用了一些其他方法,下面具体说明整体流程(由于iris数据集维度较低,故使用sonar数据集具体说明):
1、读取数据集,按照一定比例随机分为测试集、训练集
2、编码及种群的初始化:
对所有特征描述为一个由60个0/1元素组成的列表,代表染色体,0代表该特征没有被选中,1代表该特征被选中。
用户给定选择的数据维度d和种群数目p,将染色体(列表)初始化全0,然后随机将染色体中的d位变为1,这样就从60维特征中随机选择了d维。重复上述过程p次,则可以得到一个有p条染色体的初代种群P(0),并且每条染色体都不尽相同。并随机选定一个最优个体。
3、开始迭代,直至达到指定的迭代次数:
1) 更新每个个体的适应度函数,并更新最优个体、统计种群的平均适应度。计算每个个体的适应度时,根据个体的染色体情况,将没有选中的特征删除(包括训练集和测试集),将数据送入分类器,计算分类准确率作为该个体的适应度函数。分类器使用之前实现的K近邻,根据之前的经验,K取2。
2) 根据适应度函数,对种群进行轮盘赌选择。对种群中的染色体进行采样,由采样出的染色体经过一定的操作繁殖出下一代染色体,组成下一代的种群。这里实用的是轮盘赌的方式进行选择:首先将种群中所有个体的适应度归一化,,将这些适应度值累加起来,得到p个区间,每个区间的长度代表对应染色体的适应度值。从0-1之间取随机数,该数落到哪个区间,就选用哪条染色体。本文转载自http://www.biyezuopin.vip/onews.asp?id=15003为保证种群的染色体数目不变,重复p次,就得到了基于上一代种群适应度的新子代种群。
3)交叉。对一条染色体,按照一定的概率(交叉概率设为0.4),选择是否进行交叉操作。再从种群中随机寻找到另外一条父代染色体,与之进行交叉,为了使交叉之后的染色体特征维数不变,采用了如下方法:
先从一个父代染色体中随机选取一个片段(长度为60/2=30),统计该片段中有几个为1的基因,再从与之匹配的父代染色体中寻找长度相同、1基因数目相同的片段,若找到,则进行交叉操作;若未找到,则寻找下一对父代染色体,直到将所有父代种群遍历完毕。
4) 变异。同样的,对一条染色体,按照一定的概率(变异概率设为0.01),选择是否进行变异操作。为了使变异之后染色体中特征数量不变,又采用了以下方法:
随机从种群中选择一个个体,再随机地选择一个基因进行反转,若该基因由1变为了0,则再随机选一个0变成1,反之也执行同样的操作。直至遍历完种群中所有的个体。
5)重复迭代。在进行完选择、交叉、和变异操作之后,上一代的种群已经变成了新一代的种群。重复上述过程1-4,在遗传算法迭代的过程中,种群中的染色体会趋于所选特征数中的最优解,达到一定的迭代次数 t 后,算法停止,输出最终种群中适应度值最大的染色体,和每次迭代过程中的种群平均适应度,即完成了在 D 维特征中选择d个最优的特征。

# -*- coding: utf-8 -*

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

sonar = pd.read_csv('sonar.all-data',header=None,sep=',')
sonar1 = sonar.iloc[0:208,0:60]
sonar2 = np.mat(sonar1) 


pc = 0.02                            # pc为变异的概率
t = 150   #遗传算法迭代的次数
n = 60    #种群的个体数,要求大于20以保证具有随机性
# d = 30    # d为要选择的特征个数

#遗传算法
def GA(d):
    population = np.zeros((n,60))     # 初始化种群
    for i in range(n):                # 定义种群的个体数为 n
        a = np.zeros(60-d)
        b = np.ones(d)                # 将选择的d维特征定义为个体c中的1
        c = np.append(a,b)
        c = (np.random.permutation(c.T)).T    # 随机生成一个d维的个体
        population[i] = c             # 初代的种群为 population,共有n个个体
        
    #遗传算法的迭代次数为t
    fitness_change = np.zeros(t)
    for i in range(t):
        fitness = np.zeros(n)             # fitness为每一个个体的适应度值
        for j in range(n):
            fitness[j] = Jd(population[j])          # 计算每一个体的适应度值   
        population = selection(population,fitness)  # 通过概率选择产生新一代的种群
        population = crossover(population)          # 通过交叉产生新的个体
        population = mutation(population)           # 通过变异产生新个体
        fitness_change[i] = max(fitness)      #找出每一代的适应度最大的染色体的适应度值
        
    # 随着迭代的进行,每个个体的适应度值应该会不断增加,所以总的适应度值fitness求平均应该会变大
    best_fitness = max(fitness)
    best_people = population[fitness.argmax()]
    
    return best_people,best_fitness,fitness_change,population
    
    


#轮盘赌选择
def selection(population,fitness):
    fitness_sum = np.zeros(n)
    for i in range(n):
        if i==0:
            fitness_sum[i] = fitness[i]
        else:
            fitness_sum[i] = fitness[i] + fitness_sum[i-1]
    for i in range(n):
        fitness_sum[i] = fitness_sum[i] / sum(fitness)
    
    #选择新的种群
    population_new = np.zeros((n,60))
    for i in range(n):
        rand = np.random.uniform(0,1)
        for j in range(n):
            if j==0:
                if rand<=fitness_sum[j]:
                    population_new[i] = population[j]
            else:
                if fitness_sum[j-1]<rand and rand<=fitness_sum[j]:
                    population_new[i] = population[j]
    return population_new
                

#交叉操作
def crossover(population):
    father = population[0:10,:]
    mother = population[10:,:]
    np.random.shuffle(father)       # 将父代个体按行打乱以随机配对
    np.random.shuffle(mother)
    for i in range(10):
        father_1 = father[i]
        mother_1 = mother[i]
        one_zero = []
        zero_one = []
        for j in range(60):
            if father_1[j]==1 and mother_1[j]==0:
                one_zero.append(j)
            if father_1[j]==0 and mother_1[j]==1:
                zero_one.append(j)
        length1 = len(one_zero)
        length2 = len(zero_one)
        length = max(length1,length2)
        half_length = int(length/2)        #half_length为交叉的位数 
        for k in range(half_length):       #进行交叉操作
            p = one_zero[k]
            q = zero_one[k]
            father_1[p]=0
            mother_1[p]=1
            father_1[q]=1
            mother_1[q]=0
        father[i] = father_1               #将交叉后的个体替换原来的个体
        mother[i] = mother_1
    population = np.append(father,mother,axis=0)
    return population
                
            
    
#变异操作
def mutation(population):
    for i in range(n):
        c = np.random.uniform(0,1)
        if c<=pc:
            mutation_s = population[i]
            zero = []                           # zero存的是变异个体中第几个数为0
            one = []                            # one存的是变异个体中第几个数为1
            for j in range(60):
                if mutation_s[j]==0:
                    zero.append(j)
                else:
                    one.append(j)
                    
            if (len(zero)!=0) and (len(one)!=0):
                a = np.random.randint(0,len(zero))    # e是随机选择由0变为1的位置
                b = np.random.randint(0,len(one))     # f是随机选择由1变为0的位置
                e = zero[a]
                f = one[b]
                mutation_s[e] = 1
                mutation_s[f] = 0
                population[i] = mutation_s
            
    return population


#个体适应度函数 Jd(x),x是d维特征向量(1*60维的行向量,1表示选择该特征)
def Jd(x):
    #从特征向量x中提取出相应的特征
    Feature = np.zeros(d)        #数组Feature用来存 x选择的是哪d个特征
    k = 0
    for i in range(60):
        if x[i] == 1:
            Feature[k] = i
            k+=1
    
    #将30个特征从sonar2数据集中取出重组成一个208*d的矩阵sonar3
    sonar3 = np.zeros((208,1))
    for i in range(d):
        p = Feature[i]
        p = p.astype(int)
        q = sonar2[:,p]
        q = q.reshape(208,1)
        sonar3 = np.append(sonar3,q,axis=1)
    sonar3 = np.delete(sonar3,0,axis=1)
    
    #求类间离散度矩阵Sb
    sonar3_1 = sonar3[0:97,:]        #sonar数据集分为两类
    sonar3_2 = sonar3[97:208,:]
    m = np.mean(sonar3,axis=0)       #总体均值向量
    m1 = np.mean(sonar3_1,axis=0)    #第一类的均值向量
    m2 = np.mean(sonar3_2,axis=0)    #第二类的均值向量
    m = m.reshape(d,1)               #将均值向量转换为列向量以便于计算
    m1 = m1.reshape(d,1)
    m2 = m2.reshape(d,1)
    Sb = ((m1 - m).dot((m1 - m).T)*(97/208) + (m2 - m).dot((m2 - m).T)*(111/208)) #除以类别个数
  
    #求类内离散度矩阵Sw
    S1 = np.zeros((d,d))
    S2 = np.zeros((d,d))
    for i in range(97):
        S1 += (sonar3_1[i].reshape(d,1)-m1).dot((sonar3_1[i].reshape(d,1)-m1).T)
    S1 = S1/97
    for i in range(111):
        S2 += (sonar3_2[i].reshape(d,1)-m2).dot((sonar3_2[i].reshape(d,1)-m2).T)
    S2 = S2/111
    
    Sw = (S1*(97/208) + S2*(111/208))
   # Sw = (S1 + S2) / 2
    #计算个体适应度函数 Jd(x)
    J1 = np.trace(Sb)
    J2 = np.trace(Sw)
    Jd = J1/J2
    
    return Jd
    


if __name__ == '__main__':
    
    best_d = np.zeros(59)          # judge存的是每一个维数的最优适应度
    
    
    for d in range(30,31):            # d为从60个特征中选择的特征维数
        best_people,best_fitness,fitness_change,best_population = GA(d)     # fitness_change是遗传算法在迭代过程中适应度变化
        best_d[d-1] = best_fitness     # best是每一维数迭代到最后的最优的适应度,用于比较
        print("在取%d维的时候,通过遗传算法得出的最优适应度值为:%.6f"%(d,best_fitness))
    

    '''
    若要看遗传算法的收敛情况,则看在d=30的情况下的fitness_change就可以
    '''
    
    
    
    #画图
    x = np.arange(0,59,1)
    plt.xlabel('dimension')
    plt.ylabel('fitness')
    plt.ylim((0,0.3))            # y坐标的范围
    plt.plot(x,best_d,'r')
    # plt.savefig("Sonar_best_d.jpg",dpi=2000)
    
    
    x = np.arange(0,t,1)
    plt.xlabel('dimension')
    plt.ylabel('fitness')
    plt.ylim((0,0.1))            # y坐标的范围
    plt.plot(x,fitness_change,'r')
    plt.show()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shejizuopin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值