呆板学习Python实战-第三章-分类标题-1.K近邻算法

[复制链接]
发表于 2026-1-15 12:21:53 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
K近邻算法简介

   K近邻(K-nearest neighbor,KNN)算法是呆板学习算法中一种根本的分类与回归方法,以输入为实例的特性向量,通过盘算新数据与训练数据特性值之间的隔断,然后选取k(k≥1)个隔断中迩来的邻人举行分类判断(投票法)大概回归。如果k=1,那么新数据被简朴的分配给其他近邻的类。
  K近邻算法不包罗显式的学习过程,而是直接举行猜测。实际上它是使用训练数据集对特性向量空间举行分别,作为其分类的“模子”。
     分类标题:分类标题的输出为实例的种别。分类时,对于新的实例,根据其k个迩来邻的训练实例的种别,通过多数表决规则举行猜测。
  回归标题: 回归标题的输出为实例的值。回归时,对于新的实例,取其k个迩来邻的训练实例的匀称值为猜测值。
  3.1.1        原理简介

   K近邻算法的三要素分别是k值选择、隔断度量、和分类决定规则。
  1.k值选择 

   当k=1时,K近邻算法又称为迩来邻算法,此时会将训练会集与x迩来的点的种别作为x的分类。(k值的选择会对K近邻算法的效果产生巨大影响)
  (1)若k值选取的较小,相称于用较小的邻域中的训练实例举行猜测。 

   

  •  只有与输入实例较近的(相似的)训练实例才会对猜测效果起作用。
  • 学习的估计偏差会增大,猜测效果会对近邻的实例点非常敏感。若近邻的训练实例点刚好是噪声,则猜测会堕落。即k值的减小意味着模子团体变复杂,易发生过拟合。
(2)若k值选取的较大,相称于用较大的邻域中的训练实例举行猜测。

   

  • 可以镌汰学习的预估偏差。
  • 学习的近似偏差会增大。这时与输入实例较远的(不相似的)训练实例也会对猜测效果起作用,使猜测产生错误,即k值的增大意味着模子团体变简朴。
  • 当k=N时,无论输入实例是什么,都将其猜测为训练实例中最多的类(即猜测效果是一个常量),此时模子过于简朴,完全忽略了训练实例中大量有效的信息。
(3)应用情况 

   k值一样平常选取一个较小的数,通常接纳交织验证法来选取最优的k值。即比力差别k值时的交织验证匀称偏差率,选择偏差率较最小的谁人k值。比方,选择k=1,2,3…,对每个k=i做多少次交织验证,盘算出匀称偏差率,然后比力并选取最小的谁人。
  2.隔断度量 

   k近邻算法要求数据的全部特性都可以做可比力的量化。若在数据特性中存在非数值的范例,则必须接纳本领将其量化为数值(例:若样本特性中存在颜色,由于颜色是没有隔断可言的,可以将颜色转换为灰度值来盘算隔断)。别的,样本中有多个参数,每个参数都有自己的界说域和取值范围,它们对隔断盘算的影响也就不一样,如取值较大的影响力会盖过取值较小的参数。为了公平,样本参数必须做一些归一化处置惩罚,最简朴的方式就是全部特性的数值都举行归一化处置惩罚。
    特性空间中两个实例点的隔断是两个实例点相似程度的反映,K近邻模子的特性空间一样平常是n维实数向量空间
。实验用的隔断是欧氏隔断,但偶然也可以是其他隔断,如曼哈顿隔断。一样平常的隔断公式表现为
隔断(
)。
  
 

上式中,当p=1时,为哈曼顿隔断(Manhattan distance),即

当p=2时,为欧式隔断(Euclidean distance),即

当p=
时,为各维度隔断中的最大值,即

   差别隔断度量所确定的迩来邻点是差别的,须要根据详细应用场景确定使用哪一种度量方式。一样平常情况下,欧氏隔断实用于连续变量,曼哈顿隔断实用于盘算棋盘格局相似性、呆板人避障路径选择、都会蹊径长度盘算等标题。
  3.分类决定规则

    K近邻算法的分类决定通常接纳多数表决规则,即由输入实例的k个相近的训练实例中的多数类决定输入实例的类。也可以基于隔断的远近举行加权投票,隔断越近的样本实例权重越大。
  多数表决规则等价于履历风险最小化,设分类的丧失函数为0-1丧失函数(指猜测值与丧失值不相称为1,否则为0),分类函数为
误分类概率为
  

  给定输入实例
,其最相近的k个样本实例构成聚集
。设涵盖
地区的种别为
(是一个待求的未知量,但它肯定是
之一),则误分类概率为
  

  误分类概率就是训练数据的履历风险,要使误分类概率最小,即履历风险最小,
  就要使
最大。即
  

  3.1.2        算法步调 

   K近邻算法步调如下:
  

  • 输入:训练数据集
    为样本实例,
    为样本实例的种别,
    。给定输入实例
    。
  • 输出:输入实例x的种别y。
  • 算法步调:

    • 根据选取的隔断度量,在T中探求与输入实例x迩来邻的k个样本实例点
      。涵盖这k个样本实例点的邻域记作
      。
    • 从
      中,根据分类决定规则(如多数表决规则)决定输入实例x的种别y:


  此中,
为指示函数,
。上式中,对于
,只有
中的样本点时才思量。
  3.1.3        实战

1.数据集

起首导入包
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from sklearn import neighbors, datasets, model_selection
复制代码
然后给出加载数据的函数
  1. def load_classification_data():
  2.     """
  3.     加载分类模型使用的数据集
  4.     :return: 一个元组,依次为训练样本集、测试样本集、训练样本的标记、测试样本的标记
  5.     """
  6.     # 使用scikit-learn自带的手写识别数据集
  7.     digits = datasets.load_digits()
  8.     X_train = digits.data
  9.     y_train = digits.target
  10.     # 进行分层采样拆分,测试集占1/4
  11.     return model_selection.train_test_split(X_train, y_train, test_size=0.25, random_state=0, stratify=y_train)
  12.    
复制代码
2.sklearn实现

1.k近邻分类

起首使用KNeighborsClassifier,给出如下测试函数:

  1. def test_KNeighborsClassifier(*data):
  2.     """
  3.     测试KNeighborsClassifier分类器的用法
  4.     :param data: 可变参数。第一个参数为训练样本集,第二个参数为测试样本集,第三个参数为训练样本集对应的标记,第四个参数为测试样本集对应的标记
  5.     :return: None
  6.     """
  7.     X_train, X_test, y_train, y_test = data
  8.     # 创建KNN分类器实例
  9.     clf = neighbors.KNeighborsClassifier()
  10.     # 训练模型
  11.     clf.fit(X_train, y_train)
  12.     # 在训练集上做预测
  13.     print("Training score: %f" % model.score(X_train, y_train))
  14.     print("Testing score: %f" % model.score(X_test, y_test))
  15. X_train, X_test, y_train, y_test = load_classification_data()
  16. test_KNeighborsClassifier(X_train, X_test, y_train, y_test)
复制代码
 代码效果:

声明(有效制止部门埋伏的报错): 

   偶然,情况的设置大概导致标题。确保你在一个干净的假造情况中运行代码,可以思量创建一个新的情况并重新安装依靠项。
   存在的标题以及办理办法:

   如果你是在本地运行,而且代码如下,大概会出现以下两种报错
  


   报错:E AttributeError: 'NoneType' object has no attribute 'split'
  (这个报错我忘记截图了,但是这个错误可以通过升级干系库办理)
  办理办法:在 pycharm 的 terminal 输入
  1. pip install --upgrade scikit-learn threadpoolctl
复制代码
升级之后再次运行,然后你就会发现出现下面这个错误(😋)
  

    办理办法:代码如下,然后直接运行即可得到上面的运行效果
  

观察k值以及投票战略对猜测性能的影响,测试代码如下:

  1. def test_KNeighborsClassifier_k_w(data):
  2.     """
  3.     测试KNeighborsClassifier分类器的n_neighbors和weights参数的影响
  4.     :param data: 包含训练和测试样本及其对应标签的元组
  5.     :return: None
  6.     """
  7.     X_train, X_test, y_train, y_test = data
  8.     Ks = np.linspace(1, y_train.size, num=100, endpoint=False, dtype='int')
  9.     weights = ['uniform', 'distance']
  10.     flg = plt.figure()
  11.     ax = flg.add_subplot(1, 1, 1)
  12.     # 绘制不同weights下,不同K值对应的训练和测试准确率
  13.     for weight in weights:
  14.         training_scores = []
  15.         testing_scores = []
  16.         for K in Ks:
  17.             clf = neighbors.KNeighborsClassifier(n_neighbors=K, weights=weight)
  18.             clf.fit(X_train, y_train)
  19.             training_scores.append(clf.score(X_train, y_train))
  20.             testing_scores.append(clf.score(X_test, y_test))
  21.         ax.plot(Ks, training_scores, label='Training score: %s' % weight)
  22.         ax.plot(Ks, testing_scores, label='Testing score: %s' % weight)
  23.     ax.legend(loc='best')
  24.     ax.set_title('KNeighborsClassifier: k and weights')
  25.     ax.set_xlabel('k')
  26.     ax.set_ylabel('score')
  27.     ax.set_ylim([0.0, 1.05])
  28.     plt.show()
复制代码
代码效果: 
   从图中可以看到,
  在使用uniform投票战略的情况下(即投票权重都雷同),分类器随着k的增长,猜测性能稳固降落。这是由于当k增大时,距输入实例较远的训练实例也会对猜测起作用,使猜测发生错误。
  在使用distance投票战略的情况下(即投票权重与隔断成反比),分类器随着k的增长,对测试集的猜测性能相对稳固。这是由于固然k增大时,距输入实例较远的训练实例也会对猜测起作用,但由于隔断较远,其影响小得多(权重很小)。
  

然后观察p值(即隔断函数的情势)对猜测性能的影响,测试函数如下:

  1. def test_KNeighborsClassifier_k_p(data):
  2.     """
  3.     测试KNeighborsClassifier分类器的p参数的影响
  4.     :param data: 包含训练和测试样本及其对应标签的元组
  5.     :return: None
  6.     """
  7.     X_train, X_test, y_train, y_test = data
  8.     Ks = np.linspace(1, y_train.size, endpoint=False, dtype='int')
  9.     Ps = [1, 2, 10]
  10.     flg = plt.figure()
  11.     ax = flg.add_subplot(1, 1, 1)
  12.     # 绘制不同p值下,不同K值对应的训练和测试准确率
  13.     for P in Ps:
  14.         training_scores = []
  15.         testing_scores = []
  16.         for K in Ks:
  17.             clf = neighbors.KNeighborsClassifier(n_neighbors=K, p=P)
  18.             clf.fit(X_train, y_train)
  19.             training_scores.append(clf.score(X_train, y_train))
  20.             testing_scores.append(clf.score(X_test, y_test))
  21.         ax.plot(Ks, training_scores, label='Training score:P=%d' % P)
  22.         ax.plot(Ks, testing_scores, label='Testing score: P=%d' % P)
  23.     ax.legend(loc='best')
  24.     ax.set_title('KNeighborsClassifier: k and p')
  25.     ax.set_xlabel('k')
  26.     ax.set_ylabel('score')
  27.     ax.set_ylim([0.0, 1.05])
  28.     plt.show()
复制代码
  可以看到,参数p对分类器的猜测性能没有太大的影响。由于有
  

  因此,当p=1时,
是
的迩来的点;当p为其他值时,该结论也建立。
  

2.k近邻回归

起首使用KNeighborsRegressor,给出如下测试函数:

  1. def test_KNeighborsRegressor(data):
  2.     """
  3.     测试KNeighborsRegressor的用法
  4.     :param data: 包含训练和测试样本及其对应标签的元组
  5.     :return: None
  6.     """
  7.     X_train, X_test, y_train, y_test = data
  8.     model = neighbors.KNeighborsRegressor()
  9.     model.fit(X_train, y_train)
  10.     print("\nTraining score: %f" % model.score(X_train, y_train))
  11.     print("Testing score: %f" % model.score(X_test, y_test))
复制代码
效果: 

然后观察k值以及投票战略对猜测性能的影响,测试代码如下:

  1. def test_KNeighborsRegressor_k_w(data):
  2.     """
  3.     测试KNeighborsRegressor的k和w参数
  4.     :param data: 包含训练和测试样本及其对应标签的元组
  5.     :return: None
  6.     """
  7.     X_train, X_test, y_train, y_test = data
  8.     Ks = np.linspace(1, y_train.size, num=100, endpoint=False, dtype='int')
  9.     weights = ['uniform', 'distance']
  10.     fig = plt.figure()
  11.     ax = fig.add_subplot(1, 1, 1)
  12.     # 绘制不同weights下,预测得分随n_neighbors变化的曲线
  13.     for weight in weights:
  14.         training_scores = []
  15.         testing_scores = []
  16.         for K in Ks:
  17.             model = neighbors.KNeighborsRegressor(n_neighbors=K, weights=weight)
  18.             model.fit(X_train, y_train)
  19.             training_scores.append(model.score(X_train, y_train))
  20.             testing_scores.append(model.score(X_test, y_test))
  21.         ax.plot(Ks, training_scores, label='Training score: %s' % weight)
  22.         ax.plot(Ks, testing_scores, label='Testing score: %s' % weight)
  23.     ax.legend(loc='best')
  24.     ax.set_xlabel('n_neighbors')
  25.     ax.set_ylabel('score')
  26.     ax.set_ylim(0.0, 1.05)
  27.     ax.set_title('KNeighborsRegressor: k and w')
  28.     plt.show()
复制代码

然后观察p值(即隔断函数的情势)与猜测性能的影响,测试函数如下:
  1. def test_KNeighborsRegressor_k_p(data):
  2.     """
  3.     测试KNeighborsRegressor的k和p参数
  4.     :param data: 包含训练和测试样本及其对应标签的元组
  5.     :return: None
  6.     """
  7.     X_train, X_test, y_train, y_test = data
  8.     Ks = np.linspace(1, y_train.size, endpoint=False, dtype='int')
  9.     Ps = [1, 2, 10]
  10.     fig = plt.figure()
  11.     ax = fig.add_subplot(1, 1, 1)
  12.     # 绘制不同weights下,预测得分随n_neighbors变化的曲线
  13.     for P in Ps:
  14.         training_scores = []
  15.         testing_scores = []
  16.         for K in Ks:
  17.             model = neighbors.KNeighborsRegressor(n_neighbors=K, p=P)
  18.             model.fit(X_train, y_train)
  19.             training_scores.append(model.score(X_train, y_train))
  20.             testing_scores.append(model.score(X_test, y_test))
  21.         ax.plot(Ks, training_scores, label='Training score: p=%d' % P)
  22.         ax.plot(Ks, testing_scores, label='Testing score: p=%d' % P, linestyle='--')
  23.     ax.legend(loc='best')
  24.     ax.set_xlabel('n_neighbors')
  25.     ax.set_ylabel('score')
  26.     ax.set_ylim(0.0, 1.05)
  27.     ax.set_title('KNeighborsRegressor: k and p')
  28.     plt.show()
复制代码
效果:

3.算法实现

   数据选择经典的Iris鸢尾花数据集,下载代码如下:
  (由于是自己在sklearn下载的数据集,部门范围与书上存在收支,如果是按照我的流程来编写的代码请以我的代码为准。
  1. from sklearn.datasets import load_iris
  2. import pandas as pd
  3. # 下载数据集
  4. iris = load_iris()
  5. # 将数据转换为DataFrame
  6. df = pd.DataFrame(data=iris.data, columns=iris.feature_names)
  7. df['target'] = iris.target
  8. # 保存为CSV文件
  9. df.to_csv('iris_dataset.csv', index=False)
复制代码
   提取setosa、versicolor、virginica这3个种别的数据,分别用0、1、2表现,选择sepal length和petal length两个特性,在二维平面作图,代码如下:
  1. import pandas as pd
  2. import matplotlib.pyplot as plt
  3. data = pd.read_csv('iris_dataset.csv', header=0)  # 修改为 header=0
  4. data.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'species']
  5. X = data.iloc[0:150, [0, 2]].values  # 获取 sepal length 和 petal length
  6. y = data.iloc[0:150, 4].values
  7. y[y == 'Iris-setosa'] = 0
  8. y[y == 'Iris-versicolor'] = 1
  9. y[y == 'Iris-virginica'] = 2
  10. X_setosa, y_setosa = X[0:50], y[0:50]
  11. X_versicolor, y_versicolor = X[50:100], y[50:100]
  12. X_virginica, y_virginica = X[100:150], y[100:150]
  13. plt.scatter(X_setosa[:, 0], X_setosa[:, 1], color='red', marker='o', label='setosa')
  14. plt.scatter(X_versicolor[:, 0], X_versicolor[:, 1], color='blue', marker='^', label='versicolor')
  15. plt.scatter(X_virginica[:, 0], X_virginica[:, 1], color='green', marker='s', label='virginica')
  16. plt.xlabel('sepal length')
  17. plt.ylabel('petal length')
  18. plt.legend(loc='upper left')
  19. plt.show()
复制代码
 效果

   接下来将每个种别的全部样天职成训练集(training set)、验证集(validation set)和测试集(test set),各占全部样本的比例分别为60%、20%、20%。代码如下:
  (渴望代码轻便,可以使用我这种写法,不外背面绘图须要用到数据请自行声明)
  1. # 希望代码简洁,可以使用我这种写法,不过后面画图需要用到数据请自行声明
  2. # 训练集
  3. X_train = np.vstack((X_setosa[0:30, :], X_versicolor[0:30, :], X_virginica[0:30, :]))
  4. y_train = np.hstack((y_setosa[0:30], y_versicolor[0:30], y_virginica[0:30]))
  5. # 验证集
  6. X_val = np.vstack((X_setosa[30:40, :], X_versicolor[30:40, :], X_virginica[30:40, :]))
  7. y_val = np.hstack((y_setosa[30:40], y_versicolor[30:40], y_virginica[30:40]))
  8. # 测试集
  9. X_test = np.vstack((X_setosa[40:50, :], X_versicolor[40:50, :], X_virginica[40:50, :]))
  10. y_test = np.hstack((y_setosa[40:50], y_versicolor[40:50], y_virginica[40:50]))
  11. class KNearestNeighbor(object):
  12.     def __init__(self):
  13.         self.X_train = None
  14.         self.y_train = None
  15.     # 训练函数
  16.     def train(self, X, y):
  17.         self.X_train = X
  18.         self.y_train = y
  19.     # 预测函数
  20.     def predict(self, X, k=1):
  21.         num_test = X.shape[0]       # 计算欧氏距离
  22.         num_train = self.X_train.shape[0]
  23.         np.zeros((num_test, num_train))
  24.         d1 = -2 * np.dot(X, self.X_train.T)
  25.         d2 = np.sum(np.square(X), axis=1, keepdims=True)
  26.         d3 = np.sum(np.square(self.X_train), axis=1)
  27.         dists = np.sqrt(d1 + d2 + d3)
  28.         # 根据k值选择可能属于的类别
  29.         y_pred = np.zeros(num_test)
  30.         for i in range(num_test):
  31.             dist_k_min = np.argsort(dists[i])[:k]
  32.             y_kclose = self.y_train[dist_k_min]
  33.             y_pred[i] = np.argmax(np.bincount(y_kclose.tolist()))
  34.         return y_pred
复制代码
   创建一个KnearestNeighbor实例对象,然后在验证集上举行k-hold交织验证。通过多次实验,发现当
时得到的效果最高,为0.97(该数据大概存在错误)。选择完k值后就可以对测试集举行猜测分析了。代码如下:
  1. KNN = KNearestNeighbor()
  2. KNN.train(X_train, y_train)
  3. y_pred = KNN.predict(X_test, k=3)
  4. accuracy = np.mean(y_pred == y_test)
  5. print('Accuracy: {:.2f}'.format(accuracy))
复制代码

    这个报错阐明数据中存在负数,只须要在predict函数中做如下修改即可:
  1.     def predict(self, X, k=1):
  2.         num_test = X.shape[0]
  3.         num_train = self.X_train.shape[0]
  4.         # 计算欧氏距离
  5.         dists = np.zeros((num_test, num_train))
  6.         dists = np.sum(X ** 2, axis=1, keepdims=True) + np.sum(self.X_train ** 2, axis=1) - 2 * np.dot(X,
  7.                                                                                                        self.X_train.T)
  8.         # 将负值置为0,以避免 sqrt 计算无效值
  9.         dists[dists < 0] = 0
  10.         dists = np.sqrt(dists)
  11.         y_pred = np.zeros(num_test)
  12.         for i in range(num_test):
  13.             dist_k_min = np.argsort(dists[i])[:k]
  14.             y_kclose = self.y_train[dist_k_min]
  15.             y_pred[i] = np.argmax(np.bincount(y_kclose))
  16.         return y_pred
复制代码
 修改后:

    末了将猜测效果绘图表现。只选择sepal length和petal length两个特性,代码如下:
  1. # 训练集
  2. X_setosa_train = X_setosa[0:30, :]
  3. X_versicolor_train = X_versicolor[0:30, :]
  4. X_virginica_train = X_virginica[0:30, :]
  5. plt.scatter(X_setosa_train[:, 0], X_setosa_train[:, 1], color='red', marker='o', label='setosa_train')
  6. plt.scatter(X_versicolor_train[:, 0], X_versicolor_train[:, 1], color='green', marker='^', label='versicolor_train')
  7. plt.scatter(X_virginica_train[:, 0], X_virginica_train[:, 1], color='blue', marker='s', label='virginica_train')
  8. # 测试集
  9. X_setosa_test = X_setosa[40:50, :]
  10. X_versicolor_test = X_versicolor[40:50, :]
  11. X_virginica_test = X_virginica[40:50, :]
  12. plt.scatter(X_setosa_test[:, 0], X_setosa_test[:, 1], color='y', marker='o', label='setosa_test')
  13. plt.scatter(X_versicolor_test[:, 0], X_versicolor_test[:, 1], color='y', marker='^', label='versicolor_test')
  14. plt.scatter(X_virginica_test[:, 0], X_virginica_test[:, 1], color='y', marker='s', label='virginica_test')
  15. plt.legend(loc=4)
  16. plt.xlabel('sepal length')
  17. plt.ylabel('petal length')
  18. plt.show()
复制代码
实验效果

3.1.4        实验

1.实验目标

   明确K近邻算法的原理,把握k值选择、隔断度量和分类决定的设置方法,并分别使用sk-learn的干系包、Python语言编程来实现该算法。
  2实验数据


   实验数据为sk-learn自带的手写辨认数据集DigitDataset。该数据集由1797张样本图片构成。每张图片都是一个8×8像素巨细的手写数字位图(如上图所示)。为了使用如许的8×8像素图形,必须起首将其转换为长度为64的特性向量。代码如下:
  1. from sklearn import datasets
  2. import matplotlib.pyplot as plt
  3. # 导入手写数字数据集
  4. digits = datasets.load_digits()
  5. # 显示最后一个手写数字位图
  6. plt.figure(1, figsize=(3, 3))
  7. plt.imshow(digits.images[-1], cmap=plt.cm.gray_r, interpolation='nearest')
  8. plt.show()
复制代码
3.实验要求

   使用python实现如下功能。
  

  • 数据预处置惩罚:重要做好加载数据、交织验证、归一化等功能,以实现数据预处置惩罚。
  • 模子训练:使用python来实现训练算法。
  • 模子验证:编程实现对算法的验证,以评估算法的有效性。
1.数据预处置惩罚  

   对于交织验证的方法,各人可以参考这个:sklearn中文社区-交织验证 
    对于归一化的方法,各人可以参考这篇博客:使用sklearn对数据举行尺度化、归一化以及将数据还原
  1. # 数据预处理
  2. def load_and_preprocess_data():
  3.     # 加载手写数字数据集
  4.     digits = datasets.load_digits()
  5.     # 数据特征:64维向量,每一行为一个样本
  6.     X = digits.data
  7.     # 标签:手写数字的真实分类
  8.     y = digits.target
  9.     # 将数据集划分为训练集和测试集
  10.     X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
  11.     # 对特征进行归一化处理
  12.     scaler = StandardScaler()
  13.     X_train = scaler.fit_transform(X_train)
  14.     X_test = scaler.transform(X_test)
  15.     return X_train, X_test, y_train, y_test, digits
复制代码
  可以从代码中看到,我并没有使用交织验证,直接简朴的加载数据然后举行了归一化处置惩罚,我们可以通过交织验证和归一化处置惩罚来观察二者对模子准确度的影响。
  2.模子训练

  1. # 模型训练
  2. def train_knn_model(X_train, y_train, k):
  3.     knn = KNeighborsClassifier(n_neighbors=k)
  4.     knn.fit(X_train, y_train)
  5.     return knn
复制代码
3.模子验证

  1. # 模型验证
  2. def validate_model(knn, X_test, y_test):
  3.     y_pred = knn.predict(X_test)
  4.     # 评估模型的准确性
  5.     accuracy = accuracy_score(y_test, y_pred)
  6.     print(f"模型的准确率: {accuracy * 100:.2f}%")
  7.     # 混淆矩阵
  8.     conf_matrix = confusion_matrix(y_test, y_pred)
  9.     print("混淆矩阵:\n", conf_matrix)
复制代码
  1. # 主函数
  2. if __name__ == "__main__":
  3.     # 加载和预处理数据
  4.     X_train, X_test, y_train, y_test, digits = load_and_preprocess_data()
  5.     # 使用k=3训练模型
  6.     knn_model = train_knn_model(X_train, y_train, k=3)
  7.     # 验证模型
  8.     validate_model(knn_model, X_test, y_test)
  9.     # 可视化一个手写数字
  10.     plt.figure(figsize=(3, 3))
  11.     plt.imshow(digits.images[-1], cmap=plt.cm.gray_r, interpolation='nearest')
  12.     plt.show()
复制代码

   可以看到终极效果:模子的正确率是96.85%,我们回到数据预处置惩罚,实验差别的数据预处置惩罚对于模子正确率的影响
  4.优化模子

   上文中对于数据预处置惩罚,没有使用交织验证,归一化处置惩罚使用的是StandardScaler函数。通过查询,在sklearn中提供了以下方法举行归一化处置惩罚:
  

  • MinMaxScaler:将数据缩放到指定范围(通常是0到1)。
  • StandardScaler:将数据尺度化为均值为0,尺度差为1的分布。
  • RobustScaler:使用中位数和四分位数举行缩放,对非常值更鲁棒。
  • Normalizer:按行缩放样本,使得每个样本的范数为1。
对于手写数字数据集,我们可以使用StandardScaler大概MinMaxScaler。在使用一些基于隔断的算法(如K近邻算法)时,MinMaxScaler表现更好。
  1. # 数据预处理
  2. def load_and_preprocess_data():
  3.     digits = datasets.load_digits()
  4.     X, y = digits.data, digits.target
  5.     X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
  6.     scaler = MinMaxScaler()
  7.     X_train = scaler.fit_transform(X_train)
  8.     X_test = scaler.transform(X_test)
  9.     return X_train, X_test, y_train, y_test, digits
复制代码

   从效果可以显着看出使用 MinMaxScaler举行归一化处置惩罚模子正确度更高。
    到现在为止,我们还没有使用交织验证,通过查询,在sklearn中提供了以下方法举行交织验证:
  

  • KFold:将数据集分成k个折叠,每次用一个折叠作为验证集,别的作为训练集。
  • StratifiedKFold:雷同于KFold,但保持每个折叠中的类比例,以确保分类标题的代表性。
  • LeaveOneOut (LOO):每次只留一个样本作为验证集,实用于小数据集。
  • GroupKFold:用于分组数据的交织验证,确保同一组的数据不会出现在训练和验证会集。
  • TimeSeriesSplit:专为时间序列数据计划,保存时间序次。
对于手写数字数据集我们可以使用以下两种交织验证的方法:
  

  • KFold恰当平常的分割和评估。
  • StratifiedKFold更好,由于它可以保持每个数字类的比例,确保模子在每个种别上都得到精良的评估。
   使用 StratifiedKFold 的代码如下,如果使用KFold,只须要修改干系代码为kf即可
  1. kf = KFold(n_splits=5, shuffle=True, random_state=42)
复制代码
得到效果在代码下方,可以看出StratifiedKFold 的效果更好
  1. import numpy as np
  2. from sklearn.datasets import load_digits
  3. from sklearn.model_selection import StratifiedKFold
  4. from sklearn.preprocessing import MinMaxScaler
  5. from sklearn.neighbors import KNeighborsClassifier
  6. from sklearn.metrics import accuracy_score
  7. # 加载数据集
  8. digits = load_digits()
  9. X, y = digits.data, digits.target
  10. # 应用MinMaxScaler
  11. scaler = MinMaxScaler()
  12. X_scaled = scaler.fit_transform(X)
  13. # 设置StratifiedKFold
  14. skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
  15. accuracies = []
  16. # 进行交叉验证
  17. for train_index, test_index in skf.split(X_scaled, y):
  18.     X_train, X_test = X_scaled[train_index], X_scaled[test_index]
  19.     y_train, y_test = y[train_index], y[test_index]
  20.     knn = KNeighborsClassifier(n_neighbors=3)  # 选择k值
  21.     knn.fit(X_train, y_train)
  22.     y_pred = knn.predict(X_test)
  23.     accuracy = accuracy_score(y_test, y_pred)
  24.     accuracies.append(accuracy)
  25. # 输出平均准确率
  26. print(accuracies)
  27. print(f'Average Accuracy: {np.mean(accuracies):.5f}')
复制代码
  StratifiedKFold
  

    KFlod 
  

    在最初的数据预处置惩罚中,我提到可以参考sklearn中文社区中的方法,使用cross_val_score函数进一步优化代码。仅用两行代码便可到达上文的效果,得到效果与上述划一。
  1. # 进行交叉验证
  2. knn = KNeighborsClassifier(n_neighbors=3)
  3. scores = cross_val_score(knn, X_scaled, y, cv=skf, scoring='accuracy')
  4. # 输出平均准确率
  5. print(scores)
  6. print(f'Average Accuracy: {np.mean(scores):.5f}')
复制代码
  现在模子已经优化的差不多了,我们可以先辈行一个数据陈诉,并盘算模子的正确率、准确率、召回率、F1值。
  在validate_model函数中添加代码(得到效果不一样是由于我把测试集巨细调为了0.5):
  1. print("分类报告:\n", classification_report(y_test, y_pred))
复制代码


   到这里已经大功告成了。还剩下能优化的地方就是数据可视化了,这里我提供两个方向给各人:
  

  • 绘制测试样本的图片,而且在标题输出样本的猜测值与真实值;
  • 绘制肴杂矩阵的图片,提供每个分类的真实猜测情况。
效果图如下,仅供参考:
  测试样本:

肴杂矩阵: 

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表