实验四、数据挖掘之KNN,Naive Bayes

实验四、数据挖掘之KNN,Naive Bayes

一、实验目的

1. 掌握KNN的原理

2. 掌握Naive Bayes的原理

3. 学会利用KNN与Navie Bayes解决分类问题

二、实验工具

1. Anaconda

2. sklearn

三、实验简介

1. KNN

KNN(K-Nearest Neighbor)工作原理:存在一个样本数据集合,也称为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类对应的关系。输入没有标签的数据后,将新数据中的每个特征与样本集中数据对应的特征进行比较,提取出样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k近邻算法中k的出处,通常k是不大于20的整数。最后选择k个最相似数据中出现次数最多的分类作为新数据的分类。

说明:KNN没有显示的训练过程,它是“懒惰学习”的代表,它在训练阶段只是把数据保存下来,训练时间开销为0,等收到测试样本后进行处理。

2. Navie Bayes

朴素贝叶斯分类器中最核心的便是贝叶斯准则,他用如下的公式表示:

p ( c ∣ x ) = p ( x ∣ c ) p ( c ) p ( x ) p(c|x)= \frac{p(x|c)p(c)}{p(x)} p(cx)=p(x)p(xc)p(c)

在机器学习中,朴素贝叶斯分类器是一个基于贝叶斯定理的比较简单的概率分类器,其中 naive(朴素)是指的对于模型中各个 feature(特征) 有强独立性的假设,并未将 feature 间的相关性纳入考虑中。

朴素贝叶斯分类器一个比较著名的应用是用于对垃圾邮件分类,通常用文字特征来识别垃圾邮件,是文本分类中比较常用的一种方法。朴素贝叶斯分类通过选择 token(通常是邮件中的单词)来得到垃圾邮件和非垃圾邮件间的关联,再通过贝叶斯定理来计算概率从而对邮件进行分类。

四、实验内容

1. 利用KNN对鸢尾花数据进行分类。

(1) 调用数据的方法如下:

from sklearn.datasets import load_iris
iris = load_iris()# 从sklearn 数据集中获取鸢尾花数据。

(2)数据进行KNN分类

from sklearn import datasets
iris = datasets.load_iris() 
#第一步:首先读取Iris数据集资料
#查看数据的规模
print(iris.data.shape)
#查看数据说明,要养成看数据说明的好习惯
print(iris.DESCR)
#由数据描述可知,iris数据集中共有150多鸢尾花,
#每朵花都有四个特征值,并均匀分布在3个不同的亚种。
#第二步:对原始数据进行数据分割
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test=train_test_split(iris.data,iris.target,test_size=0.25,random_state=33)
#第三步:使用KNN分类器训练模型并预测
from sklearn.preprocessing import StandardScaler
ss=StandardScaler()
X_train=ss.fit_transform(X_train)
Xtest=ss.transform(X_test)
#导入knn分类器
from sklearn.neighbors import KNeighborsClassifier 
knc=KNeighborsClassifier()
knc.fit(X_train,y_train)
y_predict=knc.predict(X_test)
#第四步:对KNN分类器的预测性能进行评估
from sklearn.metrics import classification_report 
print('knc的准确率:',knc.score(X_test,y_test))
print(classification_report(y_predict,y_test,target_names=iris.target_names))

运行结果截图

image.png

image.png

2.利用Navie Bayes对鸢尾花数据建模

#通过朴素贝叶斯对鸢尾花数据进行分类 
from sklearn import datasets 
from sklearn.model_selection import train_test_split 
from sklearn.naive_bayes import MultinomialNB, GaussianNB 
import matplotlib.pyplot as plt 
import numpy as np 
import matplotlib as mpl 
from sklearn.preprocessing import StandardScaler 
from sklearn.pipeline import Pipeline 
iris = datasets.load_iris() 
# 加载鸢尾花数据 
iris_x = iris.data 
# 获取数据 
# print(iris_x) 
iris_x = iris_x[:, :2] 
# 取前两个特征值 
# print(iris_x) 
iris_y = iris.target 
# 0, 1, 2 
x_train, x_test, y_train, y_test = train_test_split(iris_x, iris_y, test_size=0.75, random_state=1) 
# 对数据进行分类 一部分最为训练一部分作为测试 
# clf = GaussianNB() 
# ir = clf.fit(x_train, y_train) 
clf = Pipeline([  ('sc', StandardScaler()),('clf', GaussianNB())]) 
# 管道这个没深入理解 所以不知所以然 
ir = clf.fit(x_train, y_train.ravel()) 
# 利用训练数据进行拟合 
# 画图: 
x1_max, x1_min = max(x_test[:, 0]), min(x_test[:, 0]) 
# 取0列特征得最大最小值 
x2_max, x2_min = max(x_test[:, 1]), min(x_test[:, 1]) 
# 取1列特征得最大最小值 
t1 = np.linspace(x1_min, x1_max, 500) 
# 生成500个测试点 
t2 = np.linspace(x2_min, x2_max, 500) 
x1, x2 = np.meshgrid(t1, t2) 
# 生成网格采样点 
x_test1 = np.stack((x1.flat, x2.flat), axis=1) 
y_hat = ir.predict(x_test1) 
# 预测 
mpl.rcParams['font.sans-serif'] = [u'simHei'] 
# 识别中文保证不乱吗 
mpl.rcParams['axes.unicode_minus'] = False 
cm_light = mpl.colors.ListedColormap(['#77E0A0', '#FF8080',
 '#A0A0FF']) 
# 测试分类的颜色 
cm_dark = mpl.colors.ListedColormap(['g', 'r', 'b']) 
# 样本点的颜色 
plt.figure(facecolor='w') 
plt.pcolormesh(x1, x2, y_hat.reshape(x1.shape),cmap=cm_light) 
# y_hat 25000个样本点的画图, 
plt.scatter(x_test[:, 0], x_test[:, 1], 
edgecolors='k', s=50, c=y_test, cmap=cm_dark) 
# 测试数据的真实的样本点(散点) 参数自行百度 
plt.xlabel(u'花萼长度', fontsize=14) 
plt.ylabel(u'花萼宽度', fontsize=14) 
plt.title(u'GaussianNB对鸢尾花数据的分类结果', fontsize=18) 
plt.grid(True) 
plt.xlim(x1_min, x1_max) 
plt.ylim(x2_min, x2_max) 
plt.show() 
y_hat1 = ir.predict(x_test) 
result = y_hat1 == y_test 
print(result) 
acc = np.mean(result) 
print('准确度为: %.2f%%' % (100 * acc)) 

运行结果截图

image.png

3.不使用sklearn中的分类方法,自己编写KNN程序(建议用python语言),并对鸢尾花数据进行分类。

# -*- coding: utf-8 -*-
"""
Created on Fri Apr 22 08:32:23 2022

@author: zhenkai
"""

import numpy as np 
import pandas as pd 
from sklearn import datasets
iris = datasets.load_iris()
# 从sklearn 数据集中获取鸢尾花数据。 
#读取数据集,header参数来指定参数标题的行,默认为0,
#第一行,如果没有标题使用None 
#data = iris.data 
data = pd.read_csv('iris.csv',header=0) 
#对文本进行处理,将Species列的文本映射成数值类型 
data['species'] = data['species'].map({
    
    'virginica':0,'setosa':1,'versicolor':2}) 
data.head(20) 
#显示末尾行数 
data.tail(20) 
#随机显示,默认为1条 
data.sample(10) 
#删除不需要的列 
#data.drop("id",axis=1,inplace=True) 
#重复值检查,any(),一旦有重复值,就返回True 
data.duplicated().any() 
#删除重复的数据 
data.drop_duplicates(inplace=True) 
#查看各类别的数据条数 
data['species'].value_counts() 
#编写KNN类 
"""使用python实现K近邻算法"""
class KNN: 
    def __init__(self,k):  
        self.k = k   
    """
    训练方法  
    Parameters  
    ----  
    X:类似数组类型,list,ndarray……形状:[样本的数量,特征的数量]  
    y:类似数组类型,形状为[样本数量]  每个样本的目标值,也是就是标签  
    """   
#将X转换成ndarray类型,如果X已经是ndarray则不进行转换  
    def fit(self, X, y):
        self.X = np.asarray(X)  
        self.y = np.asarray(y)  
    """
    根据参数传递的样本,对样本数据进行预测,返回预测之后的结果   
    Parameters  
    ----  
    X:类似数组类型,list,ndarray……形状:[样本的数量,特征的数量]   
    Return
    ----  result:数类型,预测的结果。  
    """
    #将测试的X转换为ndarray结构  
    def predict(self, X):  
        X = np.asarray(X)  
        result = []   
        for x in X:  
            #ndarray相减为对应元素相减,测试的X的每一行与self.X 相减  
            #求欧氏距离:每个元素都取平方值  
            dis = np.sqrt(np.sum((x - self.X) ** 2,axis = 1))  
            #求最近的k个点的距离,sort()排序不适用,因为排序后打乱了顺序  
            #argsort(),返回每个元素在排序之前原数组的索引  
            index = dis.argsort()  
            #取前k个元素,距离最近的k的元素  
            index = index[:self.k]  
            #返回数组中每个元素出现的次数,元素必须是非负整数  
            count = np.bincount(self.y[index])  
            #返回ndarray之最大的元素的索引,该索引就是我们判定的类别  
            result.append(count.argmax())  
        return np.asarray(result)
    """
    根据参数传递的样本,对样本数据进行预测
    (考虑权重,使用距离的倒数作为权重),返回预测之后的结果   
    Parameters  
    ----  
    X:类似数组类型,list,ndarray……形状:[样本的数量,特征的数量]   
    Return  
    ----  
    result:数类型,预测的结果。  
    """  
    def predict2(self, X):  
        X = np.asarray(X)#将测试的X转换为ndarray结构  
        result = []   
        for x in X:  
            #ndarray相减为对应元素相减,测试的X的每一行与self.X 相减  
            #求欧氏距离:每个元素都取平方值  
            dis = np.sqrt(np.sum((x - self.X) ** 2,axis = 1))  
            #求最近的k个点的距离,sort()排序不适用,因为排序后打乱了顺序  
            #argsort(),返回每个元素在排序之前原数组的索引  
            index = dis.argsort()  
            #取前k个元素,距离最近的k的元素  
            index = index[:self.k]  
            #返回数组中每个元素出现的次数,元素必须是非负整数,
            #【使用weight考虑权重,权重为距离的倒数】  
            count = np.bincount(self.y[index],weights=1/dis[index])  
            #返回ndarray之最大的元素的索引,该索引就是我们判定的类别  
            result.append(count.argmax())  
        return np.asarray(result)  
#数据集拆分成训练集和测试集 
#1、提取每个类别鸢尾花的数量 
t0 = data[data['species']==0] 
t1 = data[data['species']==1] 
t2 = data[data['species']==2] 
#打乱顺序,random_state ,记住打乱的顺序 
t0 = t0.sample(len(t0),random_state=0) 
t1 = t1.sample(len(t1),random_state=0) 
t2 = t2.sample(len(t2),random_state=0) 
train_X = pd.concat([t0.iloc[:40,:-1],t1.iloc[:40,:-1],t2.iloc[:40,:-1]],axis=0)
train_Y = pd.concat([t0.iloc[:40,-1],t1.iloc[:40,-1],t2.iloc[:40,-1]],axis=0)
test_X = pd.concat([t0.iloc[40:,:-1],t1.iloc[40:,:-1],t2.iloc[40:,:-1]],axis=0)
test_Y = pd.concat([t0.iloc[40:,-1],t1.iloc[40:,-1],t2.iloc[40:,-1]],axis=0) 
#进行训练与测试 
knn = KNN(k=3) 
#进行训练 
knn.fit(train_X,train_Y) 
#进行测试 
result = knn.predict(test_X) 
# display(result) 
# display(test_Y) 
#查看预测结果 
display(np.sum(result == test_Y)) 
#对计算结果进行可视化展示 
import matplotlib as mpl 
import matplotlib.pyplot as plt 
#设置matplotlib 支持中文显示 
mpl.rcParams['font.family'] = 'SimHei' 
#设置字体为黑体 
mpl.rcParams['axes.unicode_minus'] = False 
#设置在中文字体是能够正常显示负号(“-”) 
#设置画布大小 
plt.figure(figsize=(10,10)) 
#挑选维度进行散点图显示 
#绘制训练集的散点图'Iris-virginica':0,'Iris-setosa':1,
#'Iris-versicolor':2 
plt.scatter(x=t0['sepal_length'][:40],y=t0['petal_length']
[:40],color='r',label='virginica') 
plt.scatter(x=t1['sepal_length'][:40],y=t1['petal_length']
[:40],color='g',label='setosa') 
plt.scatter(x=t2['sepal_length'][:40],y=t2['petal_length']
[:40],color='b',label='versicolor') 
#绘制测试集数据,使用不同的图案显示预测正确和错误的结果 
right = test_X[result == test_Y]
wrong = test_X[result != test_Y] 
plt.scatter(x=right['sepal_length'],y=right['petal_length'],color='c',marker='x',label='right') 
plt.scatter(x=wrong['sepal_length'],y=wrong['petal_length'],color='m',marker='>',label='wrong') 
#显示额外信息 
plt.xlabel("花萼长度") 
plt.ylabel("花瓣长度") 
plt.title("KNN分类显示结果") 
plt.legend(loc="best") 
plt.show() 

运行截图如下:

image.png

4.(选做) 不使用sklearn中的分类方法,自己编写Navie Bayes程序(建议用python语言),并对鸢尾花数据进行分类。

# -*- coding: utf-8 -*-
"""
Created on Fri Apr 22 09:31:10 2022

@author: zhenkai
"""

from sklearn import datasets
from sklearn.model_selection import GridSearchCV,train_test_split,cross_val_score
iris = datasets.load_iris()#下载鸢尾花数据集
data_x = iris["data"]#特征x
# print(data_x)
data_y = iris["target"]#类别y
#将每一列特征划分成几份区间,标准化
num_1=5#每个特征分成五份,我这里是每个特征都分成5个区间,也可以分开。不过代码自己改
def standard_feature(feature_col,num):
    max_0 = max(data_x[:,feature_col])
    min_0 = min(data_x[:,feature_col])
    width_0 = (max_0-min_0)/num
    for j in range(len(data_x[:,feature_col])):
        for i in range(1, num + 1):
            if min_0+(i-1)*width_0<=data_x[j,feature_col]<=min_0+i*width_0:
                data_x[j,feature_col] =i
                # print(data_x[j,feature])
                break
x_col_num = len(data_x[0])#获取列数及特征数目
def get_pb(one_feature,col,x_train):
    '''
    one_feature在该x_train数据集的col列的概率
    :param one_feature:查找的特征是啥
    :param col: 列
    :param x_train:
    :return: 返回该特征的概率
    '''
    fea_sum = 0
    for i in x_train[:,col]:
        if i == one_feature:
            fea_sum+=1
    col_all = len(x_train)
    p_b1 =fea_sum/col_all
    # if p_b1 == 0:
    #     print("第几列第几个特征个数为0",col,one_feature,len(x_train))#如果当你把x特征分的太细会出现有些特征测试集有但训练集没有。看len(x_train)当这个等于训练集总数第78行会除数为0
    return p_b1
for i in range(x_col_num):#data_x将所有特征标准化
    standard_feature(i,num_1)
x_train,x_test,y_train,y_test = train_test_split(data_x,data_y,test_size=0.3)#拆分成测试集,训练集
print("训练集样本数量",len(x_train))
print("测试集样本数量",len(x_test))

test_PB_list =[]#
for row_i in range(len(x_test)):
    P_B = 1
    for col_i in range(x_col_num):
        one_pb = get_pb(x_test[row_i,col_i],col_i,x_train)
        P_B *= one_pb
    #经过for循环得到每个测试集样本的P(B)
    test_PB_list.append(P_B)
print("test_PB_list元素个数",len(test_PB_list))
y =3
y_index_list = []#分别存放对应y的全部训练集的全部列索引,该列表元素的列表索引对应y
for y_num in range(y):
    one_y_index_list =[]
    for y_index in range(len(y_train)):
        if y_train[y_index] == y_num:
            one_y_index_list.append(y_index)
    y_index_list.append(one_y_index_list)
print("训练集中每类拥有索引个数",*[len(a) for a in y_index_list])
y_num_list = []
for y_num in range(y):
    one_y_num = 0
    for y_index in range(len(y_train)):
        if y_train[y_index] == y_num:
            one_y_num+=1
    y_num_list.append(one_y_num)
print("训练集每类拥有个数",y_num_list)
test_y_predict =[]#测试集预测Y
for test_row in range(len(x_test)):#test_row为测试集每行样本,每行样本都需要计算分类为各个y的概率,哪个概率最大,说明就是哪一类
    final_y_P = []
    for y_index in range(y):
        x_train_yindex = x_train[y_index_list[y_index],:]
        P_BinA = 1
        for col_i in range(x_col_num):
            one_pb = get_pb(x_test[test_row, col_i], col_i, x_train_yindex)
            P_BinA *= one_pb
        PAinB=(y_num_list[y_index]/len(y_train))*P_BinA/test_PB_list[test_row]
        final_y_P.append(PAinB)
    belong_class = final_y_P.index(max(final_y_P))
    test_y_predict.append(belong_class)
print(test_y_predict)
print(list(y_test))
predict_right_num = 0
for i in range(len(test_y_predict)):
    if test_y_predict[i] == y_test[i]:
        predict_right_num+=1
probability = predict_right_num/len(test_y_predict)
print("预测正确概率",probability)

运行结果截图

image.png

五、实验总结(写出本次实验的收获,遇到的问题等)

KNN计算步骤
1)算距离:给定测试对象,计算它与训练集中的每个对象的距离
2)找邻居:圈定距离最近的k个训练对象,作为测试对象的近邻
3)做分类:根据这k个近邻归属的主要类别,来对测试对象分类 在实验过程中发现knn样本不平衡容易导致结果错误,而且计算量较大

猜你喜欢

转载自blog.csdn.net/qq_51302626/article/details/124337969