knn+heatmap

本文档详细介绍了如何运用KNN算法进行多分类任务,首先通过数据预处理,处理少数类样本,然后抽取部分数据并划分训练集和测试集。接着,利用KNN算法进行分类,并对结果进行评价,包括精度、查准率、查全率和F1分数。最后,通过热力图展示了分类报告,提出了未来改进的方向,如拆分算法和评估模块,优化类别不平衡问题的处理。
摘要由CSDN通过智能技术生成

内容介绍


本课题的流程为:抽取一些数据作为数据集,用knn的方式进行多分类,对分类结果进行评价并将评价结果输出

此流程中可以用其他多分类方法方式替换knn算法

ps. 凡是代码段中用 【】 标识符括起来的部分,都需要进行对应的替换

数据处理


因为在作者的数据中,有很多label下的数据只有一条,所以为了减少这些少量数据的干扰,作者将这些数据全部归成一类,类名为’unknown’

将待更改的特征出现的次数作为tmp, 并把 tmp == 1 对应的等待更改的值改为unknown

归类方法如下所示:

def UnknownClass():
    df = pd.read_csv('【C:/Users/待抽取的文件路径.csv】')

    df['tmp'] = df['【待更改的特征】']
    tmp_dict = dict(df['【待更改的特征】'].value_counts())
    df['tmp'] = df['tmp'].replace(tmp_dict)

    df.【待更改的特征】[df['tmp'] == 1] = '【更改后的名字,这里为unknown】'

    # print(df)

    df.to_csv('【C:/Users/更改好的文件路径.csv】')def UnknownClass():
    df = pd.read_csv('【C:/Users/待抽取的文件路径.csv】')

    df['tmp'] = df['【待更改的特征】']
    tmp_dict = dict(df['【待更改的特征】'].value_counts())
    df['tmp'] = df['tmp'].replace(tmp_dict)

    df.【待更改的特征】[df['tmp'] == 1] = '【更改后的名字,这里为unknown】'

    # print(df)

    df.to_csv('【C:/Users/更改好的文件路径.csv】')

referrence:

更改dataframe的值

数据抽取


因为原始数据集很大,所以在本课题中,只抽取部分数据作为数据集,再将抽取后的部分数据分为train set和test set

以下两种方法为从数据集中抽取部分数据,并保存为.csv文件的方法

按比例抽取

def GetPartDataToCSV(PART_RATIO):
    data_file_path = '【C:/Users/待抽取的文件路径.csv】'
    df = pd.read_csv(data_file_path, header=0)
    x = df.mac
    y = df.type_vendor_model
    x_train, x_test, y_train, y_test = train_test_split(x,y,test_size = PART_RATIO)
    data = {"【训练特征名(feature)】":x_test,"【对应的需要分类的类名(label)】":y_test}
    dfpart = pd.DataFrame(data)
    print(dfpart)
    dfpart.to_csv('【C:/Users/抽取好的文件路径.csv】')

按条数随机抽取

是抽取数据的条数

def SampleGetPartDataToCSV(LINES):
    data_file_path = '【C:/Users/待抽取的文件路径.csv】'
    df = pd.read_csv(data_file_path, header=0)
    dfpart = df.sample(LINES)
    print(dfpart)
    dfpart.to_csv('【C:/Users/抽取好的文件路径.csv】')

训练集测试集划分


因为作者的数据集中

type为标签label,也是分类的类名。其格式为字符串类型,需要转换为数字

且mac地址为分类依据的特征feature。其格式为十六进制,需要转换为浮点型

并使用 train_test_split 函数划分训练集和测试集,并通过设置stratify参数处理数据不平衡问题

def GetData(df, TEST_RATIO):
    ''' 将字符串格式的 type 转换为数字类型  '''
    le = preprocessing.LabelEncoder()
    labels = df.type
    labels_encoder = le.fit_transform(labels)

    ''' 将十六进制的 mac地址 转换为float  '''
    macs = df.mac
    features = []
    for mac in macs:
        # print(mac)
        features.append([float.fromhex(mac)])
        
    x_train, x_test, y_train, y_test = train_test_split(features, labels_encoder, test_size = TEST_RATIO, stratify=labels_encoder)

    return x_train, x_test, y_train, y_test

knn


KNN算法的核心思想:

要想确定测试样本属于哪一类,就先寻找所有训练样本中与该测试样本“距离”最近的前K个样本,将这K个样本中大部分样本的类型作为该测试样本的类型。也就是所谓的“近朱者赤近墨者黑”,根据与其最近的k个样本的类型决定其自身的类型。因此K的确定和测算距离的方式是影响样本最终分类准确率的重要因素。

实现:

调用 KNeighborsClassifier 函数实现knn算法

调用 EvaluationFN 函数对knn算法分类的结果进行评价,调用 ClassificationResultPlot 将结果可视化

参考文档:python 实现KNN算法

def KNNOneK(x_train, x_test, y_train, y_test, weightss, k):
    model = KNeighborsClassifier(n_neighbors = k,weights= weightss)
    model.fit(x_train, y_train)

    y_pred = model.predict(x_test)
    sampleClassificationReport = EvaluationFN(y_test, y_pred)
    plot.ClassificationResultPlot(sampleClassificationReport)

评价函数


对于分类问题,常用confusion_matrix(混淆矩阵),用来评估分类的准确性,其定义如下:

defination of confusion_matrix

img:分类模型总体判断的准确率(预测的结果和真实的结果相同的概率,即预测的正确率)

img: 预测为正样本时预测正确的概率(查准率

img: 把正样本预测出来的概率。召回率维持在80%就比较好(查全率

img: 对于某个分类,综合了Precision和Recall的一个判断指标,F1-Score的值是从0到1的,1是最好,0是最差

reference:

confusion_matrix(混淆矩阵)定义

秒懂Confusion Matrix之混淆矩阵详解

机器学习常用模型指标

下面是实现:

主要使用 precision_recall_fscore_support 函数来获得 precision, recall, f1score, support(预测为某个label数据的个数)

def EvaluationFN(y_pred,y_test):
    print("overall evluation result: ")
    accuracy = accuracy_score(y_test, y_pred)
    print("accuracy: ", accuracy)
    
    ''' 如果只想获取一种数据可以采用以下对应的函数  '''
    # precision = precision_recall_fscore_support(y_test, y_pred, warn_for='precision', zero_division='warn')
    # print("precision: ", precision)
    # recall = precision_recall_fscore_support(y_test, y_pred, warn_for='recall', labels = 'pos_label')
    # print("recall: ", recall)
    # f1score = precision_recall_fscore_support(y_test, y_pred, warn_for='f-score', labels = 'array-like')
    # print("f1_score: ", f1score)
    # support = precision_recall_fscore_support(y_test, y_pred, warn_for='f-score', labels='array-like')
    # print("support: ", support)

    ''' 如果想获得多种数据  '''
    precision, recall, f1score, support = precision_recall_fscore_support(
                                            y_test, y_pred, pos_label=1)
    # print("precision: ", precision)
    # print("recall: ", recall)
    # print("f1_score: ", f1score)
    # print("support: ", support)

    labels = GetLabels()
    
	''' 将数据保存为一个dataframe,并将index设置为labels  '''
    data = {'precision': precision,
            'recall': recall, 'f1-score': f1score, 'support': support}
    sampleClassificationReport = pd.DataFrame(data)
    sampleClassificationReport.index = labels
    
    # print(sampleClassificationReport)
    return sampleClassificationReport

作者希望展示的时候可以标注分类的类名,所以需要额外传递label。其中 GetLabels() 用于获得label。

因为划分数据集的时候已经保证train set和test set的种类都是所有类,所以test result label就是总label

而且因为由precision_recall_fscore_support函数得到的评价结果是按顺序排列的,所以只需要对label排序就能一一对应

此处作者使用set去重,用sort排序

def GetLabels():
    data_file_path = '【C:/Users/待抽取的文件路径.csv】'
    df = pd.read_csv(data_file_path, header=0)

    alllabels = df.type_vendor_model

    labels = list(set(alllabels))
    labels.sort()
    # print("labels: ", labels)

    return labels

评价结果可视化(HeatMap


为了将最终的结果更清晰的展示出来,使用heatmap的方式对评价结果进行可视化

其中heatmap模板函数为:

def ShowValues(pc, fmt="%.2f", **kw):
    pc.update_scalarmappable()
    ax = pc.axes
    for p, color, value in zip(pc.get_paths(), pc.get_facecolors(), pc.get_array()):
        x, y = p.vertices[:-2, :].mean(0)
        if np.all(color[:3] > 0.5):
            color = (0.0, 0.0, 0.0)
        else:
            color = (1.0, 1.0, 1.0)
        ax.text(x, y, fmt % value, ha="center", va="center", color=color, **kw)
        
def cm2inch(*tupl):
    inch = 2.54
    if type(tupl[0]) == tuple:
        return tuple(i / inch for i in tupl[0])
    else:
        return tuple(i / inch for i in tupl)
    
def HeatMap(AUC, title, xlabel, ylabel, xticklabels, yticklabels, figure_width=40, figure_height=20,
            correct_orientation=False, cmap='Reds'):
    fig, ax = plt.subplots()
    # c = ax.pcolor(AUC, edgecolors='k', linestyle= 'dashed', linewidths=0.2, cmap='RdBu', vmin=0.0, vmax=1.0)
    c = ax.pcolor(AUC, edgecolors='k', linestyle='dashed', linewidths=0.2, cmap=cmap)

    # put the major ticks at the middle of each cell
    ax.set_yticks(np.arange(AUC.shape[0]) + 0.5, minor=False)
    ax.set_xticks(np.arange(AUC.shape[1]) + 0.5, minor=False)

    # set tick labels
    # ax.set_xticklabels(np.arange(1,AUC.shape[1]+1), minor=False)
    ax.set_xticklabels(xticklabels, minor=False)
    ax.set_yticklabels(yticklabels, minor=False)

    # set title and x/y labels
    plt.title(title)
    plt.xlabel(xlabel)
    plt.ylabel(ylabel)

    # Remove last blank column
    plt.xlim((0, AUC.shape[1]))

    # Turn off all the ticks
    ax = plt.gca()
    for t in ax.xaxis.get_major_ticks():
        t.tick1On = False
        t.tick2On = False
    for t in ax.yaxis.get_major_ticks():
        t.tick1On = False
        t.tick2On = False

    # Add color bar
    plt.colorbar(c)

    # Add text in each cell
    ShowValues(c)

    # Proper orientation (origin at the top left instead of bottom left)
    if correct_orientation:
        ax.invert_yaxis()
        ax.xaxis.tick_top()

        # resize
    fig = plt.gcf()
    # fig.set_size_inches(cm2inch(40, 20))
    # fig.set_size_inches(cm2inch(40*4, 20*4))
    fig.set_size_inches(cm2inch(figure_width, figure_height))

将得到的分类评价结果数据画成 heatmap:

使用 列表生成式 生成 yticklabels 格式为 【label】(【support】)

def PlotClassificationReport(classification_report, title='Classification report ', cmap='RdBu'):
    df = classification_report
    # print(df)

    xlabel = 'Metrics'
    ylabel = 'Classes'
    xticklabels = ['Precision', 'Recall', 'F1-score']
    yticklabels = ['{0} ({1})'.format(list(df.index)[index], sup)
                   for index, sup in enumerate(df.support)]

    figure_width = 25
    figure_height = len(df.support) + 7
    correct_orientation = False
    HeatMap(df[['precision', 'recall', 'f1-score']], title, xlabel,
            ylabel, xticklabels, yticklabels, figure_width, figure_height,
            correct_orientation, cmap=cmap)

封装整个模块,并输出结果热力图:

在本例中,类较多,所以使用 iloc 函数对原 df 进行切分,只保留前10行

def ClassificationResultPlot(sampleClassificationReport):
    PlotClassificationReport(sampleClassificationReport.iloc[:10, :])
    plt.savefig('【结果图名称】.png', dpi=100, format='png', bbox_inches='tight')
    plt.close()

结果如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DmycrlU0-1623721820229)(http://image.huawei.com/tiny-lts/v1/images/4ce5fb8039f0a42187da8f1f040fd2da_995x598.png@900-0-90-f.png)]

reference:

绘制sklearn分类结果图

绘制热力图

matplotlib颜色 推荐:GnBu(蓝绿) 、RdBu(同上)

main函数


data_file_path = '【C:/Users/待抽取的文件路径.csv】'
df = pd.read_csv(data_file_path, header=0)
x_train, x_test, y_train, y_test = GetData(df,TEST_RATIO, 0)
knn.KNNOneK(x_train, x_test, y_train, y_test, '【加权方式,distance/uniform】', 【k值】)

未来改进


  • 目前 evaluate 和 heatmap 的部分都是嵌套在 KNNOneK() 里,未来可以拆出来。可以将此模块重构成两个部分,一个部分是算法,可以添加 knn 以外的多分类算法;另一部分是整个分析流程。
  • 目前的 label 采取了一个偷懒的方式,另还可以创建对应的字典,在计算完评估结果后查字典找到对应的 label
  • 对于类别不均衡的分类模型,采用 macro 方式会有较大的偏差,采用 weighted 方式则可较好反映模型的优劣、
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值