【代码复现】SMDP

1、算法

关于密度峰值聚类的半监督多示例学习(semi-supervised multi-instance learning,SMDP)的具体介绍在这一篇中已经有了详细的介绍。代码的关键部分也是与论文中一一对应的。

2、代码

2.1 util.py

这一部分主要是一些基本操作,其中包括了:加载数据集、获取训练集与测试集的索引。

import numpy as np
from scipy.io import loadmat


def load_data(path=''):#加载数据集
    data = loadmat(path)['data']#加载数据集,并命名为data
    bags, labels = [], []#创建包与标签数组
    for i in range(len(data)):#将包与标签装入各自数组中
        bags.append(data[i, 0][:, :-1])
        labels.append(data[i, 1][0, 0])
    labels = np.array(labels)#将标签
    return bags, labels


def get_index(num_bags=92, para_k=10, seed=None):#获取索引
    if seed is not None:#若没有指定的随机数
        np.random.seed(seed)#生成指定随机数
    temp_rand_idx=np.random.permutation(num_bags)#随机生成索引

    temp_fold = int(np.ceil(num_bags/para_k))#找到折叠的位置,将数据集分为训练集与测试集两部分
    ret_tr_idx={}#要返回的训练集索引
    ret_te_idx={}#要返回的测试集索引
    for i in range(para_k):
        temp_tr_idx=temp_rand_idx[0:i*temp_fold].tolist()#将0~折叠位置的索引作为训练集索引
        temp_tr_idx.extend(temp_rand_idx[(i+1)*temp_fold:])
        ret_tr_idx[i]=temp_tr_idx
        ret_te_idx[i]=temp_rand_idx[i*temp_fold: (i + 1) * temp_fold].tolist()#剩余索引作为测试集
    return ret_tr_idx,ret_te_idx#返回训练集索引与测试集索引


if __name__=='__main__':
    load_data('')
2.2 DP_TWT.py

这一部分主要是进行Density peaks(DP) clustering algorithm方法的设置,具体原理见本文。这一段代码的关键就在于找到实例密度 ρ \rho ρ以及实例到其master的距离 δ δ δ。最后将以上两个变量相乘得到 λ \lambda λ用于选出聚类中心。

import numpy as np
import math
import time


class DP:
    def __init__(self):
        self.dis_matrix = np.array([])#距离矩阵
        self.num_bags = 0#包数量
        self.d_c , self.n_c = 0,0#
        self.local_density_list = []#实例密度
        self.dis_to_master_list = []#到上司的距离
        self.lambda_list = []#密度与距离之积
        self.center_list = []#聚类中心列表

    def train(self, matrix, r=0.2, u=0.4, kernel='gaussian'):
        '''

        :param matrix: 距离矩阵
        :param r: cut-off distance阶段距离
        :param u: cut-off radio划分比率
        :param kernel: 本次DP聚类所用核函数,为高斯函数
        :return:
        '''
        self.dis_matrix = np.array(matrix)
        self.num_bags = len(self.dis_matrix)
        self.d_c = r * np.max(matrix)
        self.n_c = int(self.num_bags * u)
        self.local_density_list = self.get_local_density_list(kernel)
        self.dis_to_master_list = self.get_dis_to_master_list()

        self.lambda_list = self.get_lambda_list()

        self.center_list = self.get_center_list()

    def get_local_density_list(self, kernel):#用于计算密度
        if kernel == 'gaussian':#如果使用高斯核函数
            temp_local_density_list = np.sum(1 / (np.exp(np.power(self.dis_matrix / self.d_c, 2))), axis=1)
            return temp_local_density_list
        elif kernel == 'cutoff':#如果使用截断核函数
            temp_local_density_list = []
            for i in range(0, self.num_bags):
                temp_local_density_list.append(self.cutoff_kernel(i))
            return temp_local_density_list

    def get_dis_to_master_list(self):
        temp_dis_to_master_list = []#初始化数组
        for i in range(0, self.num_bags):
            density_list = []  # 记录所有局部密度比第i个包更大的包的序号
            i_density = self.local_density_list[i]#计算第i个包的密度
            for j in range(0, self.num_bags):#找其他的包
                if self.local_density_list[j] > i_density:#如果找到一个包密度比第i个包还大的
                    density_list.append(j)#将其记录到数组中
            dis_list = []  # 记录所有局部密度大于第i个包的包与i包的距离
            for k in density_list:#计算该包到密度较大的包之间的距离
                dis_list.append(self.dis_matrix[i, k])#将距离记录到数组中
            if dis_list:
                dis_list.sort()#对距离进行排序
                temp_dis_to_master_list.append(dis_list[0])  # 返回i包与其master的距离
            else:
                temp_dis_to_master_list.append(float('inf'))  # 表示此包为局部密度最大的包
        return np.array(temp_dis_to_master_list)#返回到其上司距离的数组

    def get_lambda_list(self):
        lambda_list = np.multiply(self.local_density_list, self.dis_to_master_list)
        #将距离与密度相乘,得到lambda
        return lambda_list

    def get_center_list(self):
        temp_center_list = np.argsort(self.lambda_list)[-self.n_c:]
        #通过进行排序,并输出对应索引数组
        # [10, 50, 30]
        # np.argsort([10, 30, 50]) -> 0, 2, 1
        return np.array(temp_center_list)

2.3 SMDP.py

这一部分是关键,通过豪斯多夫距离来测量实例之间的距离。通过交叉验证的方式来检测算法的准确度。

import numpy as np
import os
from utils import load_data, get_index
from sklearn.metrics import euclidean_distances as eucl
from tqdm import tqdm
from DP_TWT import DP
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier


class SMDP:
    def __init__(self,path,measure,kernel,r,u,k,n):
        '''

        :param path: 数据路径
        :param measure: 距离测量方式
        :param kernel: 聚类核函数
        :param r:cut-off distance阶段距离
        :param u:cut-off radio划分比率
        :param k:划分训练集与测试集
        :param n:交叉验证次数
        '''
        self.path=path
        self.bags, self.labels = load_data(path)
        self.num_bags=len(self.labels)
        self.measure=measure
        self.kernel=kernel
        self.r,self.u=r,u
        self.k,self.n=k,n
        self.dis_matrix=self.get_dis_matrix()#计算包间距离,并存入距离矩阵中
        dp=DP()#初始化DP聚类
        dp.train(self.dis_matrix, r=self.r, u=self.u, kernel=kernel)#选定核函数,对包进行聚类
        center_idx=dp.center_list#将聚类中心存入索引中
        self.vectors=self.dis_matrix[:,center_idx]#将索引中找到的距离中心在距离矩阵中找出来,并存为向量


    def one_cv(self):#交叉验证操作
            tr_idx_list,te_idx_list=get_index(self.num_bags,k)
            acc_list=[]#准确度存储矩阵
            for i in range(self.k):
                acc=self.run(tr_idx_list[i],te_idx_list[i])#计算本次验证准确度
                acc_list.append(acc)#将结果加入矩阵中
            return np.mean(acc_list)#计算准确度均值

    def n_cv(self):#进行多次交叉验证操作
            acc_list=[]#准确度存储矩阵
            for i in range(self.n):#进行n次操作
                acc = self.one_cv()#进行本次交叉验证
                acc_list.append(acc)#将结果加入矩阵中
            return float(np.mean(acc_list)),float(np.std(acc_list,ddof=1))

    def run(self, tr_idx, te_idx):
            tr_vec = self.vectors[tr_idx]#创建训练数据向量矩阵
            te_vec = self.vectors[te_idx]#创建测试数据向量矩阵
            classfier=KNeighborsClassifier(n_neighbors=3)#使用KNN算法进行预测
            classfier.fit(tr_vec,self.label[tr_idx])
            pred_labels = classfier.predict(te_vec)#得到预测标签
            acc=np.sum(pred_labels==self.label[te_idx]/len(te_idx))#计算预测准确率
            return acc*100#输出结果

    def ave_hausdorff(self, i, j):#平均豪斯多夫距离,计算实例i与实例j之间的距离
            if i==j:
                return 0
            temp_1 = np.sum(np.min(eucl(self.bags[i], self.bags[j]), axis=1))
            temp_2 = np.sum(np.min(eucl(self.bags[j], self.bags[i]), axis=1))
            result = (temp_1 + temp_2) / (self.bags[i].shape[0] + self.bags[j].shape[0])
            return result

    def get_dis_matrix(self):
        dis_path = '../distance/' + self.path.split('/')[-1].split('.')[0] + '_' + self.measure + '.npy'
        if os.path.exists(dis_path):  # 若距离数据路径存在
            print("load computed distance matrix")
            dis_matrix = np.load(dis_path)  # 读取数据
            return dis_matrix
        # 若路径不存在,则必须重新计算
        print('computing distance matrix')
        dis_matrix = np.zeros((self.num_bags, self.num_bags))  # 创建包大小的零矩阵作为距离矩阵
        if self.measure == 'ave_h':  # 若距离度量方式为平均豪斯夫距离
            for i in tqdm(range(self.num_bags), desc='computing...'):  # 循环的同时也显示一个进度条
                for j in range(i, self.num_bags):
                    dis_matrix[i, j] = self.ave_hausdorff(i, j)  # 计算实例间的距离
                    dis_matrix[j, i] = self.ave_hausdorff(i, j)
        np.save(dis_path, dis_matrix)  # 存储到具体路径中
        return dis_matrix  # 返回距离矩阵


if __name__ == '__main__':
    path = ''#数据文件路径
    measure = 'ave_h'#距离度量方式为平均豪斯夫
    kernel = 'gaussian'#密度核函数为高斯核
    r, u = 0.2, 0.6
    k, n = 10, 10  # n次k vc
    smdp = SMDP(path, measure, kernel, r, u, k, n)
    acc, std = smdp.n_cv()
    print(path.split('/')[-1])
    print('$%.1f_{%.1f}$' % (acc, std))

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值