机器学习-Dry Beans 分类实现过程

1.写在前面

前段时间收到进入导师团队的考核,考核内容是一个多元分类问题,具体内容是Dry Bean,前几天刚刚学习了机器学习的一点点内容,感觉对自己来说还是很有难度的,今日写此博客,记录下自己的实验过程(本想过段时间在写,想想还是写了吧,有些事情拖着拖着就忘了= . =),希望能够帮到你,写的不好,请多多指教!

拿到题目以后,先去看了看相关的文献,名为《Multiclass classification of dry beans using computer vision and machine learing techniques》,文中讲道使用MLP、KNN、SVM、DT实现,给出了实现结果等等。读完之后,先去学了KNN,发现还是比较简单的,然后看了MLP,一头雾水(不会调参,准确率在40%左右,博客内就不写了),然后学了DT,SVM未学,以后学了会补上。

2.数据预处理

数据集中一共10000多条数据,每个数据包含16个特征,1个标签(该条数据对应的种子类别),一共有7类种子。
每个特征都为定距数据,即:取值范围为连续取值的数值数据。
部分特征是通过其他特征计算出来。(这让我想到 线性相关
各类种子的个数如下:
Seker(2027), Barbunya(1322), Bombay(522), Cali(1630), Dermosan(3546), Horoz(1928) ,Sira(2636)
下面是各特征的最小值、最大值、平均值、标准差:

机器学习-Dry Beans 分类实现过程
从表中可以看出,不同特征之间, 数据的量级差别较大,数据范围跨度差别很大,面积的最大值达到了254616,而一些特征的最大值还不到1.0, 数据值域很小。(埋个伏笔,后边算法改进会用到)

; 3.KNN算法实现

一个样本在特征空间中的k个最近邻的样本中的大多数都属于某一个类别,则该样本也属于这个类别。其中k表示最近邻居的个数(距离计算使用欧氏距离)。

机器学习-Dry Beans 分类实现过程
(该图片来源于他人博客,参考链接附在文末)
机器学习-Dry Beans 分类实现过程

1.算法实现思路

1.将数据随机划分训练集和测试集

2.计算测试集中单条记录与训练集数据之间的 欧式距离

3.将计算的距离进行由小到大排序

4.找出距离最小的前k个值

5.计算找出的值中每类种频次,少数服从多数原则,返回频次最高的类别,即为该种子的类别

2.代码实现

import openpyxl
import random
import numpy as np
import operator
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report

def openfile(filename):
"""
    打开数据集,进行数据处理
    :param filename:文件名
    :return:特征集数据、标签集数据
"""

    readbook = openpyxl.load_workbook(filename)

    sheet = readbook['Dry_Beans_Dataset']

    n_samples = sheet.max_row - 1

    n_features = sheet.max_column - 1

    data = np.empty((n_samples, n_features))

    target = np.empty((n_samples,), dtype=np.object)

    index = 0
    for i in sheet.values:
        if (index != 0):
            data[index - 1] = np.asarray(i[0:-1], dtype=np.object)
            target[index - 1] = np.asarray(i[-1], dtype=np.object)
            index += 1
        else:
            index += 1

    return data, target

def random_number(data_size):
"""
    该函数使用shuffle()打乱一个包含从0到数据集大小的整数列表。因此每次运行程序划分不同,导致结果不同

    改进:
    可使用random设置随机种子,随机一个包含从0到数据集大小的整数列表,保证每次的划分结果相同。

    :param data_size: 数据集大小
    :return: 返回一个列表
"""

    number_set = []
    for i in range(data_size):
        number_set.append(i)

    random.shuffle(number_set)

    return number_set

def split_data_set(data_set, target_set, rate=0.1):
"""
    说明:分割数据集,默认数据集的10%是测试集

    :param data_set: 数据集
    :param target_set: 标签集
    :param rate: 测试集所占的比率
    :return: 返回训练集数据、测试集数据、训练集标签、测试集标签
"""

    train_size = int((1 - rate) * len(data_set))

    data_index = random_number(len(data_set))

    x_train = data_set[data_index[:train_size]]

    x_test = data_set[data_index[train_size:]]

    y_train = target_set[data_index[:train_size]]

    y_test = target_set[data_index[train_size:]]
    return x_train, x_test, y_train, y_test

def data_diatance(x_test, x_train):
"""
    :param x_test: 测试集
    :param x_train: 训练集
    :return: 返回计算的距离
"""

    distances = np.sqrt(sum((x_test - x_train) ** 2))
    return distances

def knn(x_test, x_train, y_train, k):
"""
    :param x_test: 测试集数据
    :param x_train: 训练集数据
    :param y_train: 训练集标签
    :param k: 邻居数
    :return: 返回一个列表包含预测结果
"""

    predict_result_set = []

    train_set_size = len(x_train)

    distances = np.array(np.zeros(train_set_size))

    for i in x_test:
        for indx in range(train_set_size):

            distances[indx] = data_diatance(i, x_train[indx])

        sorted_dist = np.argsort(distances)

        class_count = {}

        for i in range(k):

            sort_label = y_train[sorted_dist[i]]
            sort_label = (str)(sort_label)

            count = class_count.get(sort_label, 0) + 1
            class_count[sort_label] = count

        sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)

        predict_result_set.append(sorted_class_count[0][0])

    return predict_result_set

def knnChangeK(x_test_item, x_train, y_train, min_k, max_k):
"""
    传入k的范围 [min_k,max_k)
    :param x_test_item: 单个测试记录
    :param x_train: 训练集数据
    :param y_train: 训练接标签
    :param min_k: 最小k值
    :param max_k: 最大k值
    :return: 该单个测试记录对应不同k值下的预测结果
"""

    predict_result_set = []

    train_set_size = len(x_train)

    distances = np.array(np.zeros(train_set_size))

    for indx in range(train_set_size):

        distances[indx] = data_diatance(x_test_item, x_train[indx])

    sorted_dist = np.argsort(distances)

    class_count = {}

    for k in range(min_k, max_k):
        for index in range(k):

            sort_label = y_train[sorted_dist[index]]
            sort_label = (str)(sort_label)

            count = class_count.get(sort_label, 0) + 1
            class_count[sort_label] = count

        sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)

        predict_result_set.append((int)(sorted_class_count[0][0]))

    return predict_result_set

def score(predict_result_set, y_test):
"""
    :param predict_result_set: 预测结果列表
    :param y_test: 测试集标签
    :return: 返回测试集精度
"""
    count = 0
    for i in range(0, len(predict_result_set)):
        if predict_result_set[i] == y_test[i]:
            count += 1

    score = count / len(predict_result_set)

    return score

def convertNameToCode(name):
"""
    根据名字转换成相应的代码
    :param name: 姓名
    :return: 编码
"""
    if name == "SEKER":
        return 0
    if name == "BARBUNYA":
        return 1
    if name == "BOMBAY":
        return 2
    if name == "CALI":
        return 3
    if name == "DERMASON":
        return 4
    if name == "HOROZ":
        return 5
    if name == "SIRA":
        return 6

if __name__ == "__main__":
    filename = r'Dry_Bean_Dataset.xlsx'
    bean_dataset = openfile(filename)

    feature = bean_dataset[0]

    target = bean_dataset[1]

    for i in range(len(target)):
        target[i] = convertNameToCode(target[i])

    x_train, x_test, y_train, y_test = split_data_set(feature, target)

    x = []
    y = []
    result = []
    min_k = 20
    max_k = 21

    for i in range(max_k - min_k):
        result.append([])

    for i in x_test:

        x_test_item_result_list = knnChangeK(i, x_train, y_train, min_k, max_k)
        for j in range(len(x_test_item_result_list)):
            result[j].append(x_test_item_result_list[j])

    for i in range(len(result)):
        accuracy = score(result[i], y_test)
        x.append(i + min_k)
        y.append(accuracy)

    print(x)
    print(y)
    plt.plot(x, y)
    plt.xlabel('k-value')
    plt.ylabel('accuracy-value')
    plt.title(u'result map')
    plt.show()

    print("输出混淆矩阵")
    conf_mat = confusion_matrix(y_test.astype('int'), result[0])
    print(conf_mat)

    target_names = ['SEKER', 'BARBUNYA', 'BOMBAY', 'CALI', 'DERMASON', 'HOROZ', 'SIRA']
    report = classification_report(y_test.astype('int'), result[0], target_names=target_names)
    print(report)

代码终于调通了,输入了k=10,准确率在73%左右,显然准确率不高!
试了试k取值从1~1000,绘制出来准确率曲线,结果是这样的:

机器学习-Dry Beans 分类实现过程
整体趋势是下降的,而且准确率也不高,然后有测了测k取1~20,结果如下图所示:
机器学习-Dry Beans 分类实现过程
预测率的整体变化趋势为逐渐下降,降到64%左右,K=1时,取值虽然最高,但是预测准确率不高!
显然这样是不行的!这样交上去不就凉了。。,下面针对数据集的特点,进行优化

3.对算法的优化

因为不同的特征之间数据的量级差距比较大,而且一些特征的值域范围非常大(面积、周长等特征),很可能导致在算法计算的过程中,将数据取值较小、值域范围较小的特征给忽略掉!
基于上述情况,决定采用均值方差归一化进行优化。(使用StandardScaler)
我用了现成的库。
具体何时均值方差归一化有 两种方式:

1:划分训练集和测试集之前,进行均值方差归一化
2:划分训练集和测试集之后,在分别进行均值方差归一化
(我测了测,第一种方式准确率要高一些,但并不意味着这样做更好!!)

加了这段代码


    scaler = StandardScaler()

    feature = scaler.fit_transform(feature)

归一化完成后,取K=10,预测准确率达到了92.43%,明显提高!

4.实验结果

测试k从1~100,准确率变化情况,如图所示:

机器学习-Dry Beans 分类实现过程
图中显示出在K取10~25之间,取到最大值约为92.8%
测试K从1~40之间,结果如图所示:
机器学习-Dry Beans 分类实现过程
最值时K的取值在10~15之间
由于训练集、测试集不同的原因、图中显示出准确率比刚刚的高,其实每次的测量结果,准确率取最大值时,K的取值都不一定一样,甚至差的比较大,那么如何选取最优的K值,了解到需要用到 交叉验证。(本人尚未实现)

然后我取了K是12,结果是这样的:
混淆矩阵

机器学习-Dry Beans 分类实现过程
从混淆矩阵中看出,Dermason和Sira在进行分类时容易分类错误,Bombay预测较准确
机器学习-Dry Beans 分类实现过程
准确率为92.21%,效果还行 ^ – ^

; 4.决策树(DT)实现

本数据集中所包含的特征全为连续数据(定距数据),在构造决策树过程中,需要对特征进行离散化处理(了解到可以用二分法),选择最优的划分属性,离散化后,根据信息熵、信息增益 ,构建决策二叉树。
连续数据离散化处理,参考链接:https://blog.csdn.net/u012328159/article/details/79396893
代码如下:

import numpy as np
import random
from sklearn.tree import DecisionTreeClassifier
from sklearn import tree
import openpyxl
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report

def bean_type(s):
    it = {'SEKER': 0, 'BARBUNYA': 1, 'BOMBAY': 2, 'CALI': 3, 'DERMASON': 4, 'HOROZ': 5, 'SIRA': 6}
    return it[s]

iris_feature = 'Area', 'Perimeter', 'MajorAxisLength', 'MinorAxisLength', 'AspectRation', 'Eccentricity', 'ConvexArea', 'EquivDiameter', 'Extent', 'Solidity', 'roundness', 'Compactness', 'ShapeFactor1', 'ShapeFactor2', 'ShapeFactor3', 'ShapeFactor4'

if __name__ == "__main__":
    iris_dataset = openfile('Dry_Bean_Dataset.xlsx')

    x = iris_dataset[0]

    y = iris_dataset[1]
    for i in range(len(y)):
        temp_label = (str)(y[i])
        y[i] = bean_type(temp_label)

    feature_names = ['Area', 'Perimeter', 'MajorAxisLength', 'MinorAxisLength', 'AspectRation', 'Eccentricity',
                     'ConvexArea', 'EquivDiameter', 'Extent', 'Solidity', 'roundness', 'Compactness', 'ShapeFactor1',
                     'ShapeFactor2', 'ShapeFactor3', 'ShapeFactor4']

    feature_name = ['Perimeter', 'MajorAxisLength', 'MinorAxisLength', 'Compactness', 'ShapeFactor1']

    choice = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

    x_train, x_test, y_train, y_test = split_data_set(x, y, 0.2)

    clf = DecisionTreeClassifier(criterion='entropy', splitter='random', min_samples_split=50)

    x_train_features = x_train[:, choice]
    x_test_features = x_test[:, choice]

    dt_clf = clf.fit(x_train_features, y_train.astype('int'))

    predict_result = dt_clf.predict(x_test_features)

    y_test = y_test.reshape(-1)

    c = np.count_nonzero(predict_result == y_test)
    print('\t总数目数目:', len(x_test))
    print('\t预测正确数目:', c)
    print('\t准确率: %.2f%%' % (100 * float(c) / float(len(y_test))))

    print("输出混淆矩阵")
    conf_mat = confusion_matrix(y_test.astype('int'), predict_result)
    print(conf_mat)

    target_names = ['SEKER', 'BARBUNYA', 'BOMBAY', 'CALI', 'DERMASON', 'HOROZ', 'SIRA']
    report = classification_report(y_test.astype('int'), predict_result, target_names=target_names)
    print(report)

    f = open('dry_bean_tree_16_features_2.dot', 'w')
    tree.export_graphviz(dt_clf, feature_names=feature_names, out_file=f)

(部分函数在KNN算法里有,唉,其实可以把公共的方法,单独写在一个类里的,太菜了,回头学学吧 =.=)
DecisionTreeClassifier,参考链接:https://blog.csdn.net/qq_41577045/article/details/79844709
所有特征一股脑全弄进去,准确率到了90.38%,但是我对其内的调参、具体实现过程,还是非常模糊的。有待加强学习。

初学机器学习,很多地方都还不知道,写的不对的地方,欢迎评论!!

参考链接:
KNN:
https://www.jb51.net/article/172682.htm
https://zhuanlan.zhihu.com/p/76682561
https://blog.csdn.net/wzyaiwl/article/details/90549391
决策树:
https://blog.csdn.net/u012328159/article/details/70184415
https://blog.csdn.net/qq_41577045/article/details/79844709

Original: https://blog.csdn.net/qq_44913173/article/details/116672480
Author: Today.zje
Title: 机器学习-Dry Beans 分类实现过程

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/665810/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球