机器学习—KNN算法-分类及模型选择与调优

KNN算法-分类

样本距离判断:欧氏距离、曼哈顿距离、明可夫斯基距离

KNN算法原理:
        K-近邻算法( K-Nearest Neighbors ,简称 KNN , 根据 K 个邻居样本的类别来判断当前样本的类别 ; 如果一个样本在特征空间中的k 个最相似 ( 最邻近 )样本中的大多数属于某个类别,则该类本也属于这个类别( 近朱者赤近墨者黑 )

        一般情况下,我们需要先指定一个k,当一个新的数据集来临时,我们首先计算这个新的数据跟训练集中的每一个数据的距离,一般使用欧氏距离。然后从中选出距离最近的k个点,这个k一般选取为奇数,方便后面投票决策。在k个点中根据最多的确定新的数据属于哪一类。

KNN算法内容代码

创建数据集x_train, y_train,和一个新的数据x_new, 并将其可视化
import numpy as np
import matplotlib.pyplot as plt

raw_data_x = [[3.39, 2.33],
              [3.11, 1.15],
              [1.38, 3.84],
              [3.53, 4.92],
              [2.24, 2.80],
              [7.42, 4.65],
              [5.71, 3.50],
              [9.12, 2.91],
              [7.79, 3.41],
              [7.98, 0.71]]
raw_data_y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
x_train = np.array(raw_data_x)
y_train = np.array(raw_data_y)

x_new = np.array([8.06, 3.37])
#作散点图
plt.scatter(x_train[y_train==0,0], x_train[y_train==0,1], color='g')
plt.scatter(x_train[y_train==1,0], x_train[y_train==1,1], color='r')
plt.scatter(x_new[0], x_new[1], color='b')
plt.show()

knn基础过程

计算距离
from math import sqrt
distances = [sqrt(np.sum((x_new - x) ** 2)) for x in x_train]
#print(distances)#这一步可以输出计算距离

将距离进行排序,返回的是排序之后的索引位置

nearsest = np.argsort(distances)
print(nearest)

输出结果:

取k个点,假设k=5
k=5
new_y=[y_train[i] for i in nearest[:k]]
print(new_y)

输出结果为:[1, 1, 1, 1, 1]

        根据结果得知,新来的数据距离最近的5个点,都属于1类,所以新来的数据属于第一类
投票
from collections import Counter
Counter(new_y)

输出结果:Counter({1: 5})

当新来的数据距离最近的5个点不是同属于一类时,服从少数服从多数原则,新来的数据属于多数的一类
代码如下:
votes = Counter(new_y)
votes.most_common(1)
y_new = votes.most_common(1)
#most_common(n)函数返回出现次数最多的n个元素及其次数
print(y_new)

使用sklearn中的knn


import numpy as np
from sklearn.neighbors import KNeighborsClassifier

raw_data_x = [[3.39, 2.33],
              [3.11, 1.15],
              [1.38, 3.84],
              [3.53, 4.92],
              [2.24, 2.80],
              [7.42, 4.65],
              [5.71, 3.50],
              [9.12, 2.91],
              [7.79, 3.41],
              [7.98, 0.71]]
raw_data_y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
x_train = np.array(raw_data_x)
y_train = np.array(raw_data_y)

x_new = np.array([8.06, 3.37])
knn_classifier=KNeighborsClassifier(n_neighbors=5)
knn_classifier.fit(x_train,y_train)
y_new = knn_classifier.predict(x_new.reshape(1, -1))#固定1行,列数直接计算好
print(y_new[0])

输出:1

sklearn KNN算法对鸢尾花进行分类

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier

#获取数据
iris=load_iris()
print(iris.data.shape)#(150, 4)
print(iris.feature_names)
#['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
print(iris.target.shape)#(150,)
print(iris.target)#略   ——只有 0 1 2三种值 
print(iris.target_names)#['setosa' 'versicolor' 'virginica']
#划分数据集
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target,
random_state=22)
#标准化
transfer=StandardScaler()

x_train=transfer.fit_transform(x_train)
x_test=transfer.transform(x_test)

estimator= KNeighborsClassifier(n_neighbors=7)
estimator.fit(x_train, y_train)

y_predict = estimator.predict(x_test)
print("y_predict:\n", y_predict)
print("直接比对真实值和预测值:\n", y_test == y_predict)

score = estimator.score(x_test, y_test)
print("准确率为:\n", score)

模型的保存与加载

        joblib库提供了用于保存和加载使用NumPy数据结构的Python对象的实用程序,效率很高,对于某些需要大量参数或存储整个数据集的机器学习算法(例如k-最近邻)提供便捷。
                dump()函数是pickle模块中用于对象序列化的函数。它的基本语法如下:
                        pickle.dump(obj, file, protocol=None)
 
                       obj是要被序列化的对象,file是一个类似文件的对象,用于写入序列化后的数据。
import joblib
# 保存模型
joblib.dump(estimator, "my_ridge.pkl")
# 加载模型
estimator = joblib.load("my_ridge.pkl")
#使用模型预测
y_test=estimator.predict([[0.4,0.2,0.4,0.7]])
print(y_test)#[1]

模型选择与调优

在模型调优前第一步,确定模型功能是好是坏;第二步,改进模型的策略,一般来说,模型调优有三个方向:选择更好的算法,调优模型参数,改进数据;第三步,确定什么时候应该停止调优。

一、交叉验证方法

交叉验证是机器学习中一种常用的模型验证技术,用于评估模型的表现,并防止模型过拟合。

核心思想是将数据集划分为多个子集,通过在这些子集之间轮流训练和验证模型,评估模型的泛化能力。

1.保留交叉验证HoldOut(流出法)

               整个数据集被随机地划分为训练集和验证集, 根据经验法则,整个数据集的近 70% 被用作训练集,其余 30% 被用作验证集。
        优点:简单易执行
        缺点:不适用于不平衡数据集以及小数据集 
                此方法是将一部分训练集作为验证集来评估训练模型,由于随机性,对于不平衡的数据集,可能某一类的数据都被采集到了验证集,导致模型不能很好地概括该数据集;在小数据模型中,具有重要特征的数据集可能被采集到了测试集中,模型因没有在这些数据上训练过导致模型欠拟合。

2.K折交叉验证

        为了避免流出法的缺点,我们需要一种既提供充足数据来训练模型又留出足够数据进行验证的方法。即K折交叉验证。

        在K折交叉验证中,数据被划分为k个子集。现在,留出法被重复k次,每次都将k个子集中的一个用作测试集/验证集,其余k-1个子集合并形成训练集。误差估计在所有k次试验中进行平均,以获得我们模型的总体有效性。可以看到,每个数据点都会在验证集中出现一次,而在训练集中出现k-1次。这显著降低了偏差,因为我们使用了大部分数据进行拟合,同时也显著降低了方差,因为大部分数据也用于验证集。交换训练和测试集也增加了这种方法的有效性。作为一般规则和经验证据,通常偏好K = 5或10,但没有固定值,它可以取任何值。

3.分层K折交叉验证Stratified k-fold

        
        在某些情况下,响应变量可能存在较大的不平衡。例如,在涉及房屋价格的数据集中,可能存在大量价格较高的房屋。或者在分类问题的情况下,负样本可能比正样本多几倍。针对这种问题,对K折交叉验证技术进行了轻微的变化,使得每个折叠包含的各个目标类别的样本比例与完整集合相同,或者在预测问题的情况下,所有折叠中的平均响应值大致相等。这种变化也称为分层K折,分层K折交叉验证主要解决训练集和测试集分布不一致的问题。

4.其它验证

                
去除 p 交叉验证
留一交叉验证
                

      去除p交叉验证方法将p个数据点从训练数据中剔除,即如果原始样本中有n个数据点,则使用n-p个样本来训练模型,而p个点则用作验证集。这种操作对原始样本可以以这种方式分离的所有组合进行重复,然后对所有试验进行平均误差,以得出整体效果。这种方法在某种意义上是穷举的,因为它需要对所有可能的组合进行训练和验证,并且对于较大的p,它可能会变得计算不可行。

这种方法的一个特例是当p = 1时。这被称为留一出交叉验证。这种方法通常优于前一种方法,因为它不需要进行密集的计算,可能的组合数量等于原始样本中的数据点数或n

蒙特卡罗交叉验证
时间序列交叉验证

5.API

           
strat_k_fold=sklearn.neighbors.KNeighborsClassifier(n_splits=5, shuffle=True, random_state=42)
n_splits 划分为几个折叠
shuffle 是否在拆分之前被打乱 ( 随机化 ),False 则按照顺序拆分
random_state 随机因子
   
indexs=strat_k_fold.split(X,y)
返回一个可迭代对象 , 一共有 5 个折叠 , 每个折叠对应的是训练集和测试集的下标
然后可以用 for 循环取出每一个折叠对应的 X y 下标来访问到对应的测试数据集和训练数据集 以及测试目标集和训练目标集
for train_index, test_index in indexs:
        X[train_index] y[train_index] X[test_index ] y[test_index ]
说明 : 普通 K 折交叉验证和分层 K 折交叉验证的使用是一样的 只是引入的类不同
from sklearn.model_selection import KFold
使用时只是 KFold 这个类名不一样其他代码完全一样

6.示例

from sklearn.datasets import load_iris
from sklearn.model_selection import StratifiedKFold
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
#加载数据

iris=load_iris()
x=iris.data
y=iris.target
#初始化分层K折交叉验证
# n_splits划分为几个折叠
# shuffle是否在拆分之前被打乱(随机化),False则按照顺序拆分
# random_state随机因子
strat_k_fold=StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# 创建一个K近邻分类器实例
knn = KNeighborsClassifier(n_neighbors=7)
# 进行交叉验证
accuracies = []
for train_index, test_index in strat_k_fold.split(x, y):
    print(train_index, test_index)
    X_train, x_test = x[train_index], x[test_index]
    y_train, y_test = y[train_index], y[test_index]
# 数据预处理(标准化)
    scaler = StandardScaler()
    X_train_scaled = scaler.fit_transform(X_train)
    X_test_scaled = scaler.transform(x_test)
# 使用K近邻分类器进行训练
    knn.fit(X_train_scaled, y_train)
# 输出每次折叠的准确性得分
    score = knn.score(x_test,y_test)
    print(score)
    accuracies.append(score)#把分数添加到外面列表中
print(sum(accuracies)/len(accuracies))#平均得分
#使用StratifiedKFold来创建5个折叠,每个折叠中鸢尾花数据集的类别分布与整体数据集的分布一致。然
# 后我们对每个折叠进行了训练和测试,计算了分类器的准确性。

二、超参数搜索

        超参数是在建立模型时用于控制算法行为的参数。这些参数不能从常规训练过程中获得。在对模型进行训练之前,需要对它们进行赋值。

内容:

  1. 传统的手工调参——在传统的调参过程中,我们通过训练算法手动检查随机超参数集,并选择符合我们目标的最佳参数集,但没办法确保得到最佳的参数组合且耗时。

  2. 网格搜索——网格搜索是一种基本的超参数调优技术。它类似于手动调优,为网格中指定的所有给定超参数值的每个排列构建模型,评估并选择最佳模型。它会尝试超参数的每一个组合,并根据交叉验证得分选择最佳组合,导致GridsearchCV非常慢。

  3. 随机搜索——使用随机搜索代替网格搜索的动机是,在许多情况下,所有的超参数可能不是同等重要的。随机搜索从超参数空间中随机选择参数组合,参数由n_iter给定的固定迭代次数的情况下选择。实验证明,随机搜索的结果优于网格搜索。但不能保证给出最好的参数组合。

  4. 贝叶斯搜索——贝叶斯优化属于一类优化算法,称为基于序列模型的优化(SMBO)算法。这些算法使用先前对损失f的观察结果,以确定下一个(最优)点来抽样f。该算法大致可以概括如下:(1)使用先前评估的点X1*:n*,计算损失f的后验期望。(2)在新的点X的抽样损失f,从而最大化f的期望的某些方法。该方法指定f域的哪些区域最适于抽样。重复这些步骤,直到满足某些收敛准则。 在搜索空间的维度增加时,需要更多的样本。

        

三、sklearn API

                
class sklearn . model_selection . GridSearchCV ( estimator , param_grid )
说明:
同时进行交叉验证 ( CV ) 、和网格搜索 ( GridSearch ) GridSearchCV 实计上也是一个估计器
( estimator ) ,同时它有几个重要属性:
        best_params_ 最佳参数
        best_score_ 在训练集中的准确率
        best_estimator_ 最佳估计器
        cv_results_ 交叉验证过程描述
        best_index_最佳 k 在列表中的下标
参数:
        estimator: scikit - learn 估计器实例
        param_grid : 以参数名称( str )作为键,将参数设置列表尝试作为值的字典
                示例: { "n_neighbors" : [ 1 , 3 , 5 , 7 , 9 , 11 ]}
        cv : 确定交叉验证切分策略 , 值为 :
                ( 1 ) None 默认 5
                ( 2 ) integer 设置多少折
                如果估计器是分类器,使用 " 分层 k- 折交叉验证 (StratifiedKFold)" 。在所有其他情况下,使用KFold。

代码示例

# 用KNN算法对鸢尾花进行分类,添加网格搜索和交叉验证
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
def knn_iris_gscv():
# 1)获取数据
        iris = load_iris()
# 2)划分数据集
        x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target,
random_state=22)
# 3)特征工程:标准化
        transfer = StandardScaler()
        x_train = transfer.fit_transform(x_train)
        x_test = transfer.transform(x_test)
# 4)KNN算法预估器, 这里就不传参数n_neighbors了,交给GridSearchCV来传递
        estimator = KNeighborsClassifier()
# 加入网格搜索与交叉验证, GridSearchCV会让k分别等于1,2,5,7,9,11进行网格搜索偿试。cv=10
# 表示进行10次交叉验证
        estimator = GridSearchCV(estimator, param_grid={"n_neighbors": [1, 3, 5, 7,9, 11]}, cv=10)
        estimator.fit(x_train, y_train)
# 5)模型评估
# 方法1:直接比对真实值和预测值
        y_predict = estimator.predict(x_test)
        print("y_predict:\n", y_predict)
        print("直接比对真实值和预测值:\n", y_test == y_predict)
# 方法2:计算准确率
        score = estimator.score(x_test, y_test)
        print("在测试集中的准确率为:\n", score) #0.9736842105263158
# 最佳参数:best_params_
        print("最佳参数:\n", estimator.best_params_) #{'n_neighbors': 3}, 说明k=3时最好
# 最佳结果:best_score_
        print("在训练集中的准确率:\n", estimator.best_score_) #0.9553030303030303
# 最佳估计器:best_estimator_
        print("最佳估计器:\n", estimator.best_estimator_) #
        KNeighborsClassifier(n_neighbors=3)
# 交叉验证结果:cv_results_
        print("交叉验证过程描述:\n", estimator.cv_results_)
#最佳参数组合的索引:最佳k在列表中的下标
        print("最佳参数组合的索引:\n",estimator.best_index_)
#通常情况下,直接使用best_params_更为方便
        return None
knn_iris_gscv()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值