Python实现ReliefF-based Multi-label Feature Selection文章算法

本博客代码基于如下文章算法思想实现:

Y.P. Cai, M. Yang, Y. Gao, H.J. Yin, ReliefF-based multi-label feature selection, International Journal of Database Theory and Application. 8 (4) (2015) 307318.

该算法是基于非转化的多标记ReliefF特征选择算法,与传统多标记ReliefF算法的区别在于没有从标记转化的角度去实现特征权值计算,避免了转化损失。
具体思想在于使用标记相似性将样本进行分类,可以得出样本的各个类别下样本索引,再根据相似性选择最具相似性的k个样本用来计算距离,根据距离差值判断特征分类能力强弱。

特征权值迭代公式如下:
在这里插入图片描述
由于没有找到文章源代码,且源代码是MATLAB语言,无法用在本人的机器上,所以就根据文章描述将算法实现了一遍,具体代码如下,需要的自取,转载请注明原创。

如有错误,请及时告知本人,谢谢!

"""
    Author:     wzk
    Date:       2020-11-14
    theme:      基于ReliefF的多标记特征选择算法
    From:       Y.P. Cai, M. Yang, Y. Gao, H.J. Yin, ReliefF-based multi-label feature selection,
                    International Journal of Database Theory and Application. 8 (4) (2015) 307–318.
"""
import numpy as np
from collections import defaultdict


class MLReliefF:

    def __init__(self, train_data, train_target, info):
        """
        self.sn = sample nums
        self.fn = feature nums
        self.ln = label nums
        self.k = the nums of k nearest samples
        :param train_data:
        :param train_target:
        :param info: 用于控制变量信息
        """
        self.train_data = train_data
        self.train_target = train_target
        self.k = info.get('k')	# 可以设置自己需要的数字,一般来说或3或5
        self.sn = train_data.shape[0]
        self.fn = train_data.shape[1]
        self.ln = train_target.shape[1]
        pass

    def get_k_nearest_sample(self, random_sample_index):
        """
        对每个随机样本,找出它的k近邻。由于有同类、异类之分,考虑用字典结构记录k近邻索引
        除了找出最近邻,还需要找出随机样本占类别比例
        :param random_sample_index:
        :return:
        """
        nearest_samples_dict = dict()
        rsi = random_sample_index
        # 找出随机样本被标记为1的标记索引,相似性计算需要根据这些标记进行
        same_label_rsi = [i for i in range(self.ln) if self.train_target[rsi][i] == 1]
        # 在label_rsi标记集合上对所有样本是否被标记为1的数量进行排序
        num_sample_label_rsi = [sum(self.train_target[i][same_label_rsi]) for i in range(self.sn)]

        # 找到随机样本同类样本的k个最近邻,不能包含自己
        hit_k_samples = list()
        for i in np.argsort(num_sample_label_rsi)[::-1]:
            if len(hit_k_samples) >= self.k:
                break
            if i == rsi:
                continue
            else:
                hit_k_samples.append(i)
        nearest_samples_dict.update({'same': hit_k_samples})

        # 找出随机样本未被标记为1的标记集合
        diff_label_rsi = [i for i in range(self.ln) if i not in same_label_rsi]
        # diff_label_rsi 中的每个元素都是随机样本的异类,需要在每个异类中找到k个最近邻
        mis_k_sample_dict = defaultdict(list)
        for i in diff_label_rsi:
            mis_k_sample = list()
            for j in np.argsort(num_sample_label_rsi):
                if len(mis_k_sample) >= self.k:
                    break
                if rsi == j:
                    continue
                else:
                    if self.train_target[j, i] == 0:
                        continue
                    else:
                        mis_k_sample.append(j)
            mis_k_sample_dict[i] = mis_k_sample
        nearest_samples_dict.update({'diff': mis_k_sample_dict})

        # 计算随机样本同类样本数量
        same_sample_nums = 0
        for i in range(self.sn):
            for j in same_label_rsi:
                if self.train_target[i, j] == 1:
                    same_sample_nums += 1
                    break

        # 计算每一个异类的样本数量
        diff_sample_nums_dict = dict()
        for i in diff_label_rsi:
            diff_sample_nums = 0
            for j in range(self.sn):
                if self.train_target[j, i] == 1:
                    diff_sample_nums += 1
            diff_sample_nums_dict.update({i: diff_sample_nums})

        return nearest_samples_dict, same_sample_nums, diff_sample_nums_dict
        pass

    def main(self):
        """
        采用基于相似性的办法将标记集合分为多个类,避免转化过程中的信息损失
        1,随机选取一个样本
        2,获得随机样本的标记集合
        3,对标记集合中被标记为1的标记,对其他每个样本,求在这些标记下标记为1的标记数量,按这个数量降序排序
        4,前K个为该随机样本的同类k尽近邻
        5,对标记集合中未被标记为1的每个标记,对其他每个样本,将在该标记下标记为1的样本加入一个类别,在该类别中按之前已经计算出的
            数量排序关系进行升序排序,前k个为该随机样本的某个异类k近邻
        6,第5步中找到了随机样本的每个异类,每个异类中计算它的数量
        7,随机样本的同类数量,即为该随机样本被标记为1的标记下的同样被标记为1的样本的并集
        :return:
        """
        w = 0

        for i in range(self.sn):
            # 对每个随机样本,调用方法获取最近邻索引,同类样本数量,异类样本数量
            nearest_samples_dict, same_sample_nums, diff_sample_nums_dict = self.get_k_nearest_sample(i)

            # 开始计算同类样本间的距离,先计算样本与k最近邻的相似性,再计算距离
            same_dis = 0
            same_cosine_list = list()
            for j in nearest_samples_dict.get('same'):
                cosine_product = np.dot(self.train_data[i], self.train_data[j].T)
                vector_length_product = np.sqrt(np.sum(self.train_data[i] ** 2)) * np.sqrt(np.sum(self.train_data[j] ** 2))
                same_cosine_list.append(cosine_product / vector_length_product)
                pass

            for j in range(self.k):
                # 多特征下的距离计算,直接相减
                dis = np.abs(self.train_data[nearest_samples_dict.get('same')[j]] - self.train_data[j])
                same_dis += (dis * same_cosine_list[j]) / (self.sn * np.mean(same_cosine_list))

            # 开始计算随机样本与每一个异类之间的距离,先计算相似性
            diff_dis_list = list()
            diff_cosine_dict = dict()
            for k, v in nearest_samples_dict.get('diff').items():
                diff_cosine_list = list()
                for j in v:
                    cosine_product = np.dot(self.train_data[i], self.train_data[j].T)
                    vector_length_product = np.sqrt(np.sum(self.train_data[i] ** 2)) * np.sqrt(np.sum(self.train_data[j] ** 2))
                    diff_cosine_list.append(cosine_product / vector_length_product)
                diff_cosine_dict.update({k: diff_cosine_list})

            for k, v in nearest_samples_dict.get('diff').items():
                diff_dis = 0
                for j in range(self.k):
                    dis = np.abs(self.train_data[v[j]] - self.train_data[i])
                    similar = (diff_sample_nums_dict.get(k) / self.sn) / (1 - (same_sample_nums / self.sn))
                    diff_dis += similar * ((dis * diff_cosine_dict.get(k)[j]) / (self.sn * np.mean(diff_cosine_dict.get(k))))
                diff_dis_list.append(diff_dis)
            means_diff_dis = np.mean(diff_dis_list, axis=0)

            w += means_diff_dis - same_dis

        return np.argsort(w)[::-1]
        pass

你可能感兴趣的:(机器学习,python,算法)