鱼C论坛

 找回密码
 立即注册
查看: 2459|回复: 6

[经验总结] 监督学习——k近邻算法

[复制链接]
发表于 2019-8-18 21:23:32 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

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

x
本帖最后由 程序员的救赎 于 2019-8-23 15:21 编辑

监督学习

机器学习按照学习方式可以分为三个类别:
        监督学习——label(或者是目标值)指导学习
        无监督学习——在数据中自主学习
        强化学习——环境反馈指导学习

此外,还有一种称为半监督学习的学习方式,介于监督学习和无监督学习之间。



机器学习的两个问题: 分类和回归
        分类问题: 预测类别标签
                举例: 根据鸢尾花的属性,预测花的类别,预测结果和已知的类别比较,根据某种评估指标对模型进行调整(例如准确率),直至最优。
        回归问题: 预测目标值
                举例:根据楼房的属性,预测楼房的价格,预测结果和真正的价格比较,不断调整,以求误差最小。


最简单的监督学习算法——k近邻算法
        核心思想: "物以类聚"——给定测试样本,基于某种距离度量找出训练集中与其最靠近的k个样本,然后跟根据这k个“邻居”的信息来进行预测。
                举例: 给定一个地区的大部分房子的信息和价格和少部分未知价格的房子的属性,应用k近邻算法可选择与要预测的房子最近的k座房子的价格的平均值作为它的房价预测值。

                在分类任务中,k近邻采用“投票法”,即选择k个样本中出现最多的类别标记作为预测类别。
                在回归任务中,k近邻采用“平均法”,即选择k个样本的目标值的平均值(可加权)作为目标预测值

        说明
              1. k的值是由实验决定的,需要根据学习结果的选择最优的k值
              2. k近邻属于“懒惰学习”,没有训练过程,只在收到测试样本的时候才进行处理。


实践

一、 K近邻分类

        导入相关模块
import warnings
import mglearn 
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split

        加载数据集并查看数据
X, y = mglearn.datasets.make_forge()
X[:5]
        array([[ 9.96346605,  4.59676542],
       [11.0329545 , -0.16816717],
       [11.54155807,  5.21116083],
       [ 8.69289001,  1.54322016],
       [ 8.1062269 ,  4.28695977]])
X.shape
(26, 2)
y
array([1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0,
       0, 0, 1, 0])
y.shape
(26,)

        数据集划分
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
        创建模型实例
clf = KNeighborsClassifier(n_neighbors=3)
clf.fit(X_train, y_train)
        评估模型: 精确度
clf.predict(X_test)
array([1, 0, 1, 0, 1, 0, 0])
clf.score(X_test, y_test)
0.8571428571428571
        模型的精度约为86%, 也就是说在测试集中,模型对其中86%样本预测的类别是正确的


二、 Knn分类分析

        1) 决策边界
# plt.subplots(nrows=1, ncols=1, sharex=False,sharey=False, squeeze=True, subplot_kw=None, gridspec_kw=None,  **fig_kw)
fig, axes = plt.subplots(1, 3, figsize=(10, 3)) #  Create a figure and a set of subplots.,一行三列,每个图尺寸为 10 * 3

for n_neighbors, ax in zip([1, 3, 9], axes):
    clf = KNeighborsClassifier(n_neighbors=n_neighbors).fit(X, y)
    # 绘制决策边界
    mglearn.plots.plot_2d_separator(clf,  # 分类器,作用是绘制图像边界
                                    X,  # 数据
                                    fill=True, # 颜色填充
                                    eps=0.5,  #  图像的距离
                                    ax=ax, # ax指定为散点图指定所在的子图
                                    alpha=.4)  # 透明度
    # 标记坐标点
    mglearn.discrete_scatter(X[:, 0], X[:, 1], y, ax=ax)  # X[:, 0]代表X所有样本的第一维度数据
    ax.set_title("{} neighbors(s)".format(n_neighbors)) # 为当前子图设置标题
    ax.set_xlabel("feature 0")
    ax.set_ylabel("feature 1")
    
axes[0].legend(loc=3); # 在第一个子图上画出图例, loc参数设置位置,取值为[0, 10],每一个数字对应一个方位

微信截图_20190818203941.png
        可见,随着“邻居”数目的增多,决策边界越来越平滑,而决策边界越平滑,通常对应越简单的模型

        2) 模型精度

from sklearn.datasets import load_breast_cancer 
        加载癌症数据集
cancer = load_breast_cancer()
        查看数据
cancer.feature_names
array(['mean radius', 'mean texture', 'mean perimeter', 'mean area',
       'mean smoothness', 'mean compactness', 'mean concavity',
       'mean concave points', 'mean symmetry', 'mean fractal dimension',
       'radius error', 'texture error', 'perimeter error', 'area error',
       'smoothness error', 'compactness error', 'concavity error',
       'concave points error', 'symmetry error',
       'fractal dimension error', 'worst radius', 'worst texture',
       'worst perimeter', 'worst area', 'worst smoothness',
       'worst compactness', 'worst concavity', 'worst concave points',
       'worst symmetry', 'worst fractal dimension'], dtype='<U23')
cancer.data.shape 
(569, 30)
cancer.data[0]
array([1.799e+01, 1.038e+01, 1.228e+02, 1.001e+03, 1.184e-01, 2.776e-01,
       3.001e-01, 1.471e-01, 2.419e-01, 7.871e-02, 1.095e+00, 9.053e-01,
       8.589e+00, 1.534e+02, 6.399e-03, 4.904e-02, 5.373e-02, 1.587e-02,
       3.003e-02, 6.193e-03, 2.538e+01, 1.733e+01, 1.846e+02, 2.019e+03,
       1.622e-01, 6.656e-01, 7.119e-01, 2.654e-01, 4.601e-01, 1.189e-01])
cancer.target.shape
(569,)
cancer.target
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0,
       1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0,
       1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1,
       1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0,
       0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1,
       1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0,
       0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
       1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1,
       1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0,
       0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0,
       0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0,
       1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1,
       1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1,
       1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
       1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
       1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1,
       1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1])
cancer.target_names  # 0代表肿瘤是恶性的,1代表肿瘤是温和的
array(['malignant', 'benign'], dtype='<U9')
X_train, X_test, y_train, y_test = train_test_split(
    cancer.data,
    cancer.target,
    stratify=cancer.target,  #  根据label进行分层划分数据集,
    random_state=66    
)
        这里说明一下,stratify参数的作用:根据X或者y的数据的分布来对数据集进行划分
np.bincount(cancer.target)
array([212, 357], dtype=int64)
np.bincount(y_test)
array([53, 90], dtype=int64)

        可以看到,两种类别的样本比例都是接近 2:3

        评估指标
               训练精度——学习能力(模型对训练数据的学习能力)
               测试精度——泛化能力(模型面对新数据时的处理能力)

training_accuary = []
test_accuracy = []
neighbors_settings = range(1, 11)
for n_neighbors in neighbors_settings:
    # 构建模型
    clf = KNeighborsClassifier(n_neighbors=n_neighbors)
    clf.fit(X_train, y_train)
    # 记录训练集的精度
    training_accuary.append(clf.score(X_train, y_train))
    test_accuracy.append(clf.score(X_test, y_test))
plt.plot(neighbors_settings, training_accuary, label="training_accuracy")  # x, y, 曲线标签
plt.plot(neighbors_settings, test_accuracy, label='testing_accuracy')  # 这里默认在同一个画布上绘制第二条曲线
plt.ylabel('Accuracy')
plt.xlabel("n_neighbors")
plt.legend()  # 图例
微信截图_20190818204538.png
        从图像可以知道,当k取值为6的时候,泛化能力最好,准确率约为94%



三、 Knn回归

        这里先使用mglearn自带的knn回归可视化结果

        k = 1
mglearn.plots.plot_knn_regression(n_neighbors=1)  # 五角星代表测试点,其预测值等于离它最近的点的值
微信截图_20190818204711.png

        k = 3
mglearn.plots.plot_knn_regression(n_neighbors=3)
微信截图_20190818204751.png
        回归预测
from sklearn.neighbors import KNeighborsRegressor
        在wave数据集中选取40个样本作为数据集
X, y = mglearn.datasets.make_wave(n_samples=40)
reg = KNeighborsRegressor(n_neighbors=3)
reg.fit(X_train, y_train)
        预测结果
reg.predict(X_test)
array([-0.05396539,  0.35686046,  1.13671923, -1.89415682, -1.13881398,
       -1.63113382,  0.35686046,  0.91241374, -0.44680446, -1.13881398])

        [b]模型评估: R2-score

                决定系数,又叫拟合优度, 回归模型的一种评估指标。拟合优度越大,自变量对因变量的解释程度越高,自变量引起的变动总变动的百分比越高,观察点在回归曲线附近越密集。

                取值的意义

                      0 表示模型效果很差

                      1 表示模型拟合度较好或者是过拟合

                      (0, 1) 表示模型的好坏(针对同一批数据)

                      < 0 说明数据不存在线性关系
reg.score(X_test, y_test)
0.8344172446249604
        分数为0.83,说明模型拟合相对较好。



四、Knn回归分析
# 创建一个画布,包含1行3列的三个子图,每个子图尺寸为15 * 4
fig, axes = plt.subplots(1, 3, figsize=(15, 4))  

# 创建1000个数据点,在[-3, 3]之间均匀分布
line = np.linspace(-3, 3, 1000).reshape(-1, 1) #  numpy.linspace(start, end, num=50)  返回指定间隔内的等间距数字
# np.reshap(row, column) 重塑数组形状,如果输入-1,代表不关心当前维度的形状,这里代表重塑为1列的数组(不在乎有多少行)

for n_neighbors, ax in zip([1, 3, 9], axes):
    
    # 利用1个、3个、9个邻居分别进行预测
    reg = KNeighborsRegressor(n_neighbors=n_neighbors)
    reg.fit(X_train, y_train)
    
    # 作图
    ax.plot(line, reg.predict(line))  # plot一般是绘制折线图,基本参数为 x, y值
    ax.plot(X_train, y_train, "^", c=mglearn.cm2(0), markersize=8)  # c参数设置配色方案,这里采用mglearn的配色方案;
    ax.plot(X_test, y_test, "v", c=mglearn.cm2(1), markersize=8)  # “^”表示采用上三角作为数据点的标记; markersize代表标记大小
    
    # 图像标题
    ax.set_title(
        "{} neighbor(s)\n train score:{:.2f} test score: {:.2f}".format(
            n_neighbors,
            reg.score(X_train, y_train),
            reg.score(X_test, y_test)
        ))
    # 设置坐标轴标签
    ax.set_xlabel("Feature")  
    ax.set_ylabel("Target")   
axes[0].legend(["Model predictions", "Training data/target",
               "Test data/target"], loc='best')  # 位置参数“best”即自适应方式, 这里等价于使用0
;

微信截图_20190818205114.png

        从图中可以看出,仅仅使用单一邻居,训练集中每一个点都对预测结果有着显著的影响,预测结果的图像经过所有数据点。考虑更多的邻居后,预测结果变得较为平滑,但对训练数据的拟合也不一定有利


总结:
                  1. knn模型有两个重要的参数:邻居个数k和距离度量方法。在实践中,一般选择较小的邻居数(例如3或者5),然后进行调节;默认使用欧氏距离,它在很多情况下的效果都比较好。
                2. knn模型比较简单,但面对特征较多或者样本数较多的数据集时,预测速度比较慢,而且对于稀疏数据集的效果较差,所以实际中并不常用,常常作为基准方法用作比较。

       

                3. knn除了可以用来分类和回归外,还可以用来进行异常值检测。

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2019-8-22 12:46:05 | 显示全部楼层
学习了,我还没学到这块~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-8-22 17:35:44 | 显示全部楼层
罗巴乔 发表于 2019-8-22 12:46
学习了,我还没学到这块~

一起学习哈~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-10-23 21:03:33 From FishC Mobile | 显示全部楼层
哈哈,中午看看同道中人了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-16 21:49:28 | 显示全部楼层
加油
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-3-20 22:34:01 | 显示全部楼层

最近比较忙,很久没更新了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-22 09:34:19 | 显示全部楼层
程序员的救赎 发表于 2020-3-20 22:34
最近比较忙,很久没更新了

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-21 23:37

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表