机器学习原理与实战 | K近邻算法实践

简介: 机器学习原理与实战 | K近邻算法实践
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np


KNN算法中,其算法参数是K,参数选择需要根据数据来决定。K值越大,模型的偏差越大,对噪声数据越不敏感,当K值很大时,可能造成模型欠拟合;K值越小,模型的方差就会越大,当K值太小,就会造成模型过拟合。


K-近邻算法有一些变种,其中之一就是可以增加邻居的权重。默认情况下,在计算距离时,都是使用相同的权重。实际上,我们可以针对不同的邻居指定不同的权重,如距离越近权重越高。这个可以通过指定算法的weights参数来实现。


另外一个变种是,使用一定半径内的点取代距离最近的K个点。在scikit-learn里,RadiusNeighborsClassifier类实现了这个算法的变种。当数据采样不均匀时,该算法变种可以取得更好的性能。


1. KNN实现分类


# from sklearn.datasets.samples_generator import make_blobs
from sklearn.datasets import make_blobs
# 根据中心点随机生成数据
centers = [[-2, 2], [2, 2], [0, 4]]
# centers = [[-2, 0], [2, 0], [0, 4], [0,2]]
X, y = make_blobs(n_samples=100, centers=centers, random_state=0, cluster_std=0.40)
# 画出数据
plt.figure(figsize=(12,8))
c = np.array(centers)
plt.scatter(X[:, 0], X[:, 1], c=y, s=100, cmap='cool');         # 画出样本
plt.scatter(c[:, 0], c[:, 1], s=100, marker='^', c='orange');   # 画出中心点

image.png

使用KNeighborsClassifier来对算法进行训练,下面进行单样本预测


from sklearn.neighbors import KNeighborsClassifier
# 模型训练
k = 5
clf = KNeighborsClassifier(n_neighbors=k)
clf.fit(X, y)
# 进行预测
X_sample = [0, 2]
X_sample = np.array(X_sample).reshape(1, -1)
# 根据两个x坐标预测一个y坐标
y_sample = clf.predict(X_sample)
# 使用kneighbors()方法,把这个样本周围距离最近的5个点取出来。取出来的点是训练样本X里的索引,从0开始计算。
neighbors = clf.kneighbors(X_sample, return_distance=False)
# 其中,这里输出的1表示一个样本,如果是多个样本,则为n,neighbors里面就会存储n个样本距离最近的前k个点
X_sample.shape, X.shape, y.shape, y_sample.shape, neighbors.shape
((1, 2), (100, 2), (100,), (1,), (1, 5))
# 画出示意图
plt.figure(figsize=(12, 8))
plt.scatter(X[:, 0], X[:, 1], c=y, s=100, cmap='cool')    # 样本
plt.scatter(c[:, 0], c[:, 1], s=100, marker='^', c='k')   # 中心点
plt.scatter(X_sample[0][0], X_sample[0][1], marker="x", 
            s=100, cmap='cool')    # 待预测的点
for i in neighbors[0]:
    # 预测点与距离最近的 5 个样本的连线。其中第一个列表是存放x坐标,第二个列表存放y的坐标
    plt.plot([X[i][0], X_sample[0][0]], [X[i][1], X_sample[0][1]], 
             'k--', linewidth=0.6)

image.png

多样本预测,上诉图中只使用了一个样本进行可视化,但是一般来说需要处理多个样本,这里使用同样的方法来可视化结果

# 这里的k设置为5
X_sample = np.random.randint(-2,4,[5, 2])
neighbors = clf.kneighbors(X_sample, return_distance=False)
X_sample.shape, neighbors.shape
((5, 2), (5, 5))
# 画出示意图
plt.figure(figsize=(12, 8))
plt.scatter(X[:, 0], X[:, 1], c=y, s=100, cmap='cool')    # 样本
plt.scatter(c[:, 0], c[:, 1], s=100, marker='^', c='k')   # 中心点
plt.scatter(X_sample[:,0], X_sample[:,1], marker="x", s=100, cmap='cool')    # 待预测的点
for i,neighbors_data in enumerate(neighbors):
    # 预测点与距离最近的 5 个样本的连线。其中第一个列表是存放x坐标,第二个列表存放y的坐标
    for t in neighbors_data:
        plt.plot([X[t][0], X_sample[i][0]], [X[t][1], X_sample[i][1]], 'k--', linewidth=0.6)

image.png

如上图所示,直接根据最近的k个值来决定样本的类别


2. KNN实现回归


# 生成训练样本
n_dots = 40
X = 5 * np.random.rand(n_dots, 1)
y = np.cos(X).ravel()
# 添加一些噪声
y += 0.2 * np.random.rand(n_dots) - 0.1
# 显示图像
plt.scatter(X,y,s=20)
<matplotlib.collections.PathCollection at 0x1db687e7888>

image.png

# 训练模型
from sklearn.neighbors import KNeighborsRegressor
k = 5
# 回归训练
knn = KNeighborsRegressor(k)
knn.fit(X, y)
# 生成足够密集的点并进行预测
T = np.linspace(0, 5, 500).reshape(-1,1)
# 得到密集区间内的预测点
y_pred = knn.predict(T)
# 使用score()方法计算拟合曲线对训练样本的拟合准确性
# knn.score(X, y)
# out: 0.9790114894878551
# 再把这些预测点连起来,构成一条拟合曲线
plt.plot(T,y_pred,c='k')
# 显示原散点图图像
plt.scatter(X,y,s=20,c='r')
<matplotlib.collections.PathCollection at 0x1db6894cdc8>

image.png


3. 示例:KNN实现糖尿病预测


加载数据集

import pandas as pd
data = pd.read_csv('E:/学习/机器学习/b站资料/scikit-learn机器学习/code/datasets/pima-indians-diabetes/diabetes.csv')
data.head(6)

image.png


Pregnancies Glucose BloodPressure SkinThickness Insulin BMI DiabetesPedigreeFunction  Age Outcome
0 6 148 72  35  0 33.6  0.627 50  1
1 1 85  66  29  0 26.6  0.351 31  0
2 8 183 64  0 0 23.3  0.672 32  1
3 1 89  66  23  94  28.1  0.167 21  0
4 0 137 40  35  168 43.1  2.288 33  1
5 5 116 74  0 0 25.6  0.201 30  0
# 可以进一步观察数据集里的阳性和阴性样本的个数:
data.groupby("Outcome").size()
Outcome
0    500
1    268
dtype: int64


把8个特征值分离出来,作为训练数据集,把Outcome列分离出来作为目标值。然后,把数据集划分为训练数据集和测试数据集。

X = data.iloc[:, 0:8]
Y = data.iloc[:, 8]
print('shape of X {}; shape of Y {}'.format(X.shape,Y.shape))
from sklearn.model_selection import train_test_split
X_train,X_test,Y_train,Y_test=train_test_split(X,Y,test_size=0.2)
X_train.shape, X_test.shape, Y_train.shape, Y_test.shape
shape of X (768, 8); shape of Y (768,)
((614, 8), (154, 8), (614,), (154,))


分别使用普通的KNN算法、带权重的KNN算法和指定半径的KNN算法对数据集进行拟合并计算评分:

from sklearn.neighbors import KNeighborsClassifier, RadiusNeighborsClassifier
# 三种模型的构建:k值均为2
k = 5
models = []
models.append(("KNN", KNeighborsClassifier(n_neighbors=k)))
models.append(("KNN with weights", KNeighborsClassifier(n_neighbors=k, weights="distance")))
models.append(("Radius Neighbors", RadiusNeighborsClassifier(n_neighbors=k, radius=500.0)))
# 训练结构存储
results = []
for name, model in models:
    # 训练
    model.fit(X_train, Y_train)
    # 保存测试结果
    results.append((name, model.score(X_test, Y_test)))
for i in range(len(results)):
    print("name: {}; score: {}".format(results[i][0],results[i][1]))
name: KNN; score: 0.7077922077922078
name: KNN with weights; score: 0.7142857142857143
name: Radius Neighbors; score: 0.6493506493506493


怎么样更准确地对比算法准确性呢?一个方法是,多次随机分配训练数据集和交叉验证数据集,然后求模型准确性评分的平均值。scikit-learn提供了KFold和cross_val_score()函数来处理这种问题:


  • KFold把数据集分成10份,其中1份会作为交叉验证数据集来计算模型准确性,剩余的9份作为训练数据集。
  • cross_val_score()函数总共计算出10次不同训练数据集和交叉验证数据集组合得到的模型准确性评分,最后求平均值。
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
results = []
for name, model in models:
    # 把数据集分成10份,其中1份会作为交叉验证数据集来计算模型准确性,剩余的9份作为训练数据集。
    kfold = KFold(n_splits=10)
    # 函数总共计算出10次不同训练数据集和交叉验证数据集组合得到的模型准确性评分
    cv_result = cross_val_score(model, X, Y, cv=kfold)
    results.append((name, cv_result))
for i in range(len(results)):
    # 组合评分去平均值输出
    print("name: {}; cross val score: {}".format(results[i][0],results[i][1].mean()))
name: KNN; cross val score: 0.7265550239234451
name: KNN with weights; cross val score: 0.7265550239234451
name: Radius Neighbors; cross val score: 0.6497265892002735


可以查看一下result的结果:

results
[('KNN',
  array([0.63636364, 0.83116883, 0.7012987 , 0.63636364, 0.71428571,
         0.75324675, 0.74025974, 0.80519481, 0.68421053, 0.76315789])),
 ('KNN with weights',
  array([0.64935065, 0.83116883, 0.68831169, 0.63636364, 0.71428571,
         0.75324675, 0.75324675, 0.79220779, 0.68421053, 0.76315789])),
 ('Radius Neighbors',
  array([0.5974026 , 0.71428571, 0.54545455, 0.5974026 , 0.64935065,
         0.61038961, 0.81818182, 0.67532468, 0.68421053, 0.60526316]))]


可以查看一下models的结果:

models
[('KNN', KNeighborsClassifier()),
 ('KNN with weights', KNeighborsClassifier(weights='distance')),
 ('Radius Neighbors', RadiusNeighborsClassifier(radius=500.0))]


对比了不同的模型效果之后,可以进行训练与结果分析

knn = KNeighborsClassifier(n_neighbors=4)
# 训练
knn.fit(X_train, Y_train)
# 测试
train_score = knn.score(X_train, Y_train)
test_score = knn.score(X_test, Y_test)
print("train score: {}; test score: {}".format(train_score, test_score))
train score: 0.8110749185667753; test score: 0.7142857142857143


从这个输出中可以看到两个问题:


  • 一是对训练样本的拟合情况不佳,这说明算法模型太简单了,无法很好地拟合训练样本
  • 二是模型的准确性欠佳,不到72%的预测准确性


画出学习曲线证实结论


from sklearn.model_selection import ShuffleSplit
from utils import plot_learning_curve
knn = KNeighborsClassifier(n_neighbors=2)
# 多次计算使其平滑
cv = ShuffleSplit(n_splits=10, test_size=0.2, random_state=0)
plt.figure(figsize=(10, 6))
# 这里划分了5次,所以对应五个点
plot_learning_curve(plt, knn, "Learn Curve for KNN Diabetes", 
                    X, Y, ylim=(0.0, 1.01), cv=cv);

image.png



从图中可以看出来,训练样本评分较低,且测试样本与训练样本距离较大,这是典型的欠拟合现象。KNN算法没有更好的措施来解决欠拟合问题,什么KNN算法不是针对这一问题的好模型?下面进行特征选择与可视化出来


特征选择及数据可视化


8个特征,无法在这么高的维度里画出数据,并直观地观察。一个解决办法是特征选择,即只选择2个与输出值相关性最大的特征,这样就可以在二维平面上画出输入特征值与输出值的关系了。scikit-learn在sklearn.feature_selection包里提供了丰富的特征选择方法。可以使用SelectKBest来选择相关性最大的两个特征:

from sklearn.feature_selection import SelectKBest
selector = SelectKBest(k=2)
X_new = selector.fit_transform(X, Y)
X_new[:5]
array([[148. ,  33.6],
       [ 85. ,  26.6],
       [183. ,  23.3],
       [ 89. ,  28.1],
       [137. ,  43.1]])


只使用这2个相关性最高的特征的话,对比不同的KNN模型

results = []
for name, model in models:
    kfold = KFold(n_splits=10)
    # 这里使用了X_new来进行训练
    cv_result = cross_val_score(model, X_new, Y, cv=kfold)
    results.append((name, cv_result))
for i in range(len(results)):
    print("name: {}; cross val score: {}".format(
        results[i][0],results[i][1].mean()))
name: KNN; cross val score: 0.7369104579630894
name: KNN with weights; cross val score: 0.7199419002050581
name: Radius Neighbors; cross val score: 0.6510252904989747


还是普通的KNN模型准确性较高, 实验的结果比使用8个特征还要好


现在我们只有2个特征,可以很方便地在二维坐标上画出所有的训练样本,观察这些数据的分布情况:

plt.figure(figsize=(14, 10))
plt.ylabel("BMI")
plt.xlabel("Glucose")
plt.scatter(X_new[Y==0][:,0],X_new[Y==0][:,1],s=20,c='r',marker='x')
plt.scatter(X_new[Y==1][:,0],X_new[Y==1][:,1],s=20,c='b',marker='x')
plt.title("Data distribution")
Text(0.5, 1.0, 'Data distribution')

image.png


从图中可以看出,在中间数据集密集的区域,阳性样本和阴性样本几乎重叠在一起了,所以比较难以检测


目录
相关文章
|
11天前
|
机器学习/深度学习 人工智能 算法
【手写数字识别】Python+深度学习+机器学习+人工智能+TensorFlow+算法模型
手写数字识别系统,使用Python作为主要开发语言,基于深度学习TensorFlow框架,搭建卷积神经网络算法。并通过对数据集进行训练,最后得到一个识别精度较高的模型。并基于Flask框架,开发网页端操作平台,实现用户上传一张图片识别其名称。
40 0
【手写数字识别】Python+深度学习+机器学习+人工智能+TensorFlow+算法模型
|
14天前
|
机器学习/深度学习 数据采集 人工智能
探索机器学习:从理论到Python代码实践
【10月更文挑战第36天】本文将深入浅出地介绍机器学习的基本概念、主要算法及其在Python中的实现。我们将通过实际案例,展示如何使用scikit-learn库进行数据预处理、模型选择和参数调优。无论你是初学者还是有一定基础的开发者,都能从中获得启发和实践指导。
31 2
|
15天前
|
机器学习/深度学习 数据采集 人工智能
探索机器学习:从理论到实践
【10月更文挑战第35天】在这篇文章中,我们将深入探讨机器学习的世界。我们将从基础理论开始,然后逐步过渡到实际应用,最后通过代码示例来展示如何实现一个简单的机器学习模型。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和见解。
|
22天前
|
机器学习/深度学习 TensorFlow API
机器学习实战:TensorFlow在图像识别中的应用探索
【10月更文挑战第28天】随着深度学习技术的发展,图像识别取得了显著进步。TensorFlow作为Google开源的机器学习框架,凭借其强大的功能和灵活的API,在图像识别任务中广泛应用。本文通过实战案例,探讨TensorFlow在图像识别中的优势与挑战,展示如何使用TensorFlow构建和训练卷积神经网络(CNN),并评估模型的性能。尽管面临学习曲线和资源消耗等挑战,TensorFlow仍展现出广阔的应用前景。
49 5
|
1月前
|
机器学习/深度学习 算法 Java
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
|
1月前
|
机器学习/深度学习 人工智能 算法
揭开深度学习与传统机器学习的神秘面纱:从理论差异到实战代码详解两者间的选择与应用策略全面解析
【10月更文挑战第10天】本文探讨了深度学习与传统机器学习的区别,通过图像识别和语音处理等领域的应用案例,展示了深度学习在自动特征学习和处理大规模数据方面的优势。文中还提供了一个Python代码示例,使用TensorFlow构建多层感知器(MLP)并与Scikit-learn中的逻辑回归模型进行对比,进一步说明了两者的不同特点。
67 2
|
1月前
|
机器学习/深度学习 数据挖掘 Serverless
手把手教你全面评估机器学习模型性能:从选择正确评价指标到使用Python与Scikit-learn进行实战演练的详细指南
【10月更文挑战第10天】评估机器学习模型性能是开发流程的关键,涉及准确性、可解释性、运行速度等多方面考量。不同任务(如分类、回归)采用不同评价指标,如准确率、F1分数、MSE等。示例代码展示了使用Scikit-learn库评估逻辑回归模型的过程,包括数据准备、模型训练、性能评估及交叉验证。
75 1
|
21天前
|
机器学习/深度学习 人工智能 算法
探索机器学习中的决策树算法
【10月更文挑战第29天】本文将深入浅出地介绍决策树算法,一种在机器学习中广泛使用的分类和回归方法。我们将从基础概念出发,逐步深入到算法的实际应用,最后通过一个代码示例来直观展示如何利用决策树解决实际问题。无论你是机器学习的初学者还是希望深化理解的开发者,这篇文章都将为你提供有价值的见解和指导。
|
1月前
|
数据采集 机器学习/深度学习 TensorFlow
声纹识别实战:从数据采集到模型训练
【10月更文挑战第16天】声纹识别技术通过分析个人的语音特征来验证其身份,具有无接触、便捷的特点。本文将带你从零开始,一步步完成声纹识别系统的构建,包括数据采集、音频预处理、特征提取、模型训练及评估等关键步骤。我们将使用Python语言和相关的科学计算库来进行实践。
152 0
|
1月前
|
机器学习/深度学习 算法 数据建模
计算机前沿技术-人工智能算法-生成对抗网络-算法原理及应用实践
计算机前沿技术-人工智能算法-生成对抗网络-算法原理及应用实践
30 0
下一篇
无影云桌面