【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch10 KNN法

系列文章目录

监督学习:参数方法

【学习笔记】 陈强-机器学习-Python-Ch4 线性回归
【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归
【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv)
【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归
【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析
【学习笔记】 陈强-机器学习-Python-Ch8 朴素贝叶斯
【学习笔记】 陈强-机器学习-Python-Ch9 惩罚回归
【课后题练习】 陈强-机器学习-Python-Ch9 惩罚回归(student-mat.csv)

监督学习:非参数方法



前言

本学习笔记 仅为以防自己忘记了,顺便分享给一起学习的网友们参考。如有不同意见/建议,可以友好讨论。

本学习笔记 所有的代码和数据都可以从 陈强老师的个人主页 上下载

参考书目:陈强.机器学习及Python应用. 北京:高等教育出版社, 2021.

数学原理等 详见陈强老师的 PPT


参考了:
网友阡之尘埃Python机器学习07——K近邻


一、K近邻法—非参数法

最简单的非参数方法(nonparametric approach)就是K近邻法(K Nearest Neighbors,简记KNN)

 现实数据大多比较稀疏,给定X ,可能只有很少y的 观测值,甚至连一个y观测值也没有。可以考虑离X最近的K个邻居。

K近邻估计量:以X最近的Ky观测值的平均作为预测值。

使用KNN法的一个前提是,所有特征变量均为数值型;否则,将无法 KNN 计算欧氏距离。

在实践中,一般可用交叉验证 选择最优的K

对于分类问题,采取多数票规则(majority vote rule):以 K个近邻中最常见的类别作为预测。

二、KNN法 Python案例

1. 数据:breast_cancer

使用威斯康辛乳腺癌 (Wisconsin breast cancer) 的数据。此数据来自威斯康辛大学麦迪逊分校医院,包含569位病人的观测值,以及与乳腺癌诊断有关的32个变量(有关肿瘤细胞核的各种特征 )。 响应变量diagnosis取值为0=恶性肿瘤 或 1=良性肿瘤 。

import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer

#载入数据
cancer = load_breast_cancer()
#转为数据框
df = pd.DataFrame(cancer.data, columns=cancer.feature_names)
df['diagnosis'] = cancer.target
#用map():响应变量取值 分别映射
d = {0: 'malignant', 1: 'benign'}
df['diagnosis'] = df['diagnosis'].map(d)
 
df.shape

结果输出: (569, 31)

#查看y的取值分布
df.diagnosis.value_counts(normalize=True)

结果输出:
diagnosis
benign 0.627417
malignant 0.372583
Name: proportion, dtype: float64

#画y(分类)与变量'mean radius'的箱形图
import seaborn as sns
import matplotlib.pyplot as plt

sns.boxplot(x='diagnosis', y='mean radius', data=df)

在这里插入图片描述

笔记:seaborn.boxplot()

seaborn.boxplot 是一个用于绘制箱线图的函数,可视化数据分布的统计特性,如中位数、四分位数和异常值。

#基本语法和参数
import seaborn as sns
import matplotlib.pyplot as plt

seaborn.boxplot(
    data=None, *, x=None, y=None, 
    hue=None, #用于指定数据中的类别变量。数据中的列名,用于在图中添加分组。可以为数据添加色彩层次,以表示不同的子类别。
    order=None, #设定分类变量的顺序。控制 x 轴分类变量的显示顺序。类型: list 或 None
    hue_order=None, #hue的分类顺序。控制hue变量的显示顺序。类型: list 或 None
    notch=False, #是否绘制缺口箱线图,缺口箱线图显示中位数的置信区间。默认为 False。
    width=0.8, #箱体的宽度,默认 0.8。
    palette=None, #设置颜色调色板。
    linewidth=None, #箱线图线条的宽度。
    fliersize=5, #异常值标记的大小。默认 5。
    meanline=False, #是否显示均值线。默认 False
    showmeans=False, #是否显示均值。默认 False
    showcaps=True, #是否显示箱体的端点线。默认 True,
    showbox=True, #是否显示箱体。默认 True
    showfliers=True, #是否显示异常值,默认为 True。
    showmedians=False,  #是否显示中位数线。默认 False
    vert=True, #是否垂直显示箱线图,默认为 True。
    ax=None,  #ax: Matplotlib 的 Axes 对象,可以将图绘制到特定的轴上。
    **kwargs) #其他关键字参数,可以传递给底层的 matplotlib 绘图函数。

2. 处理变量

#在加载数据集时同时返回特征数据 (X) 和目标数据 (y)。
X, y = load_breast_cancer(return_X_y=True)

#全样本分组
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
    X, y, stratify=y, test_size=100, random_state=1)

#标准化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X_train)
X_train_s = scaler.transform(X_train)
X_test_s = scaler.transform(X_test)

#检验标准化的均值&标准差
print('训练集的均值',np.mean(X_train_s, axis=0)) #应该=0
print('训练集的标准差',np.std(X_train_s, axis=0)) #应该=1
print('测试集的均值',np.mean(X_test_s, axis=0))
print('测试集的标准差',np.std(X_test_s, axis=0))

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/c5d93bae016846a5adbf06c3dccedd56.png3

3. 进行K=5的KNN估计

#进行K=5的KNN估计
from sklearn.neighbors import KNeighborsClassifier #分类的KNN
model = KNeighborsClassifier(n_neighbors=5) #在预测时考虑最近的5个邻居(neighbors)。
model.fit(X_train_s, y_train)  #拟合
#预测
pred = model.predict(X_test_s)
#计算混淆矩阵
pd.crosstab(y_test, pred, 
            rownames=['Actual'], 
            colnames=['Predicted'])
#用测试数据评估模型:准确率
model.score(X_test_s, y_test)

结果输出: 在这里插入图片描述
0.97 预测准确率为97%

笔记:KNeighborsClassifier ()

KNeighborsClassifier 是 Scikit-Learn 中用于 K-最近邻分类的一个类。K-最近邻分类器通过计算测试样本与训练集中所有样本之间的距离,选择距离最近的 k 个训练样本,根据这 k 个邻居的标签来进行预测。常用的距离度量方法包括欧氏距离、曼哈顿距离等。

#基本语法和参数
from sklearn.neighbors import KNeighborsClassifier

KNeighborsClassifier(
    n_neighbors=5, #选择的邻居数目,默认为 5。
    weights='uniform', #权重函数,默认为 'uniform'。'uniform' 表示所有邻居的权重相同,'distance' 表示距离越近的邻居权重越大。
    algorithm='auto', #用于计算最近邻的算法。algorithm: {'auto', 'ball_tree', 'kd_tree', 'brute'}。'auto' 选择最适合的算法,'ball_tree' 使用 Ball Tree,'kd_tree' 使用 KD Tree,'brute' 使用暴力搜索。
    p=2, #距离度量的参数,默认为 2。p=1 使用曼哈顿距离,p=2 使用欧氏距离。
    metric='minkowski', #距离度量函数,默认为 'minkowski'。可以使用其他度量方法:'euclidean' 或 'manhattan',或传入自定义距离函数。
    n_jobs=None) #并行计算的作业数,默认为 None。设置为 -1 使用所有可用的核心进行计算。

K-最近邻分类器适用于:
· 小规模数据集:因为它需要存储所有训练样本,计算开销较大。
· 不需要显式训练的情况:KNN 是一种懒惰学习算法,没有显式的训练过程,只在预测时才进行计算。
优点:
简单易懂,易于实现和解释。
不需要训练过程,适应性强。
缺点:
× 在大数据集上计算成本高,尤其是当 k 较大时。
× 对数据噪声敏感,可能需要额外的预处理步骤来提高性能。

4.手动(for循环)寻找最优的K

#手动寻找最优的K
scores = []
ks = range(1, 51) #定义一个范围对象 ks,包含从 1 到 50 的整数。
for k in ks:
    model = KNeighborsClassifier(n_neighbors=k)
    model.fit(X_train_s, y_train)
    score = model.score(X_test_s, y_test)
    scores.append(score)
print('准确率的最大值:',max(scores)) #预测准确率的最大值

index_max = np.argmax(scores) #找“准确率的最大值”的索引位置
print(index_max) #最佳准确率对应的 k 值在 ks 中的索引位置。
print(f'Optimal K: {ks[index_max]}') #最优的K

结果输出: 准确率的最大值: 0.97
2
Optimal K: 3

1) 画出 准确率与K 的关系图

#画出 准确率与K 的关系图
plt.plot(ks, scores, 'o-') #'o-' 表示数据点将以圆圈标记(o),并用实线连接(-)。
plt.xlabel('K')
plt.axvline(ks[index_max], linewidth=1, linestyle='--', color='k')
plt.ylabel('Accuracy')
plt.title('KNN:K v.s. accuracy')
plt.tight_layout()

**结果输出:**
由上图可知,由多个K值的准确率并列最大值(97%)。

2) 画出 错误率与K 的关系图

#画不同k的错误率
errors = 1 - np.array(scores)
plt.plot(ks, errors, 'o-')
plt.xlabel('K')
plt.axvline(ks[index_max], linewidth=1, linestyle='--', color='k')
plt.ylabel('Error Rate')
plt.title('KNN:k v.s error')
plt.tight_layout()

在这里插入图片描述

3) 用1/k度量KNN模型复杂度

#用1/k度量KNN模型复杂度
errors = 1 - np.array(scores)
ks_inverse = 1 / np.array(ks)
plt.plot(ks_inverse, errors, 'o-')
plt.xlabel('1/K')
plt.ylabel('Error Rate')
plt.title('KNN: 1/k v.s errors')
plt.tight_layout()

在这里插入图片描述
用1/k作为模型复杂度度量,错误率呈现U型曲线

5.10折交叉验证法选择最优K

#10折交叉验证选择最优超参数K
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import GridSearchCV
#定义K的网格
param_grid = {'n_neighbors': range(1, 51)}
#定义10折分层随机分组
kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=1)
#进行交叉验证
model = GridSearchCV(KNeighborsClassifier(), param_grid, cv=kfold)
model.fit(X_train_s, y_train)
#得到 最优参数K
print(model.best_params_)
#准确率
model.score(X_test_s, y_test)

结果输出: {‘n_neighbors’: 12} 最优K=12
0.96 K=12时,测试集的预测准确率96%(< 97%)

三、课后题

UCI Machine Learnig Repository的印第安纳糖尿病数据 PimaIndianasDiabetes.csv,响应变量为diabetes(取值pos或neg,表示是否有糖尿病),其余8个特征变量均为数值型。

(1)载入数据,考察其形状与前5个观测值

import numpy as np
import pandas as pd

#读取CSV文件的路径
csv_path = r'D:\桌面文件\Python\【陈强-机器学习】MLPython-PPT-PDF\MLPython_Data\PimaIndiansDiabetes.csv'
Diabetes = pd.read_csv(csv_path)

print(Diabetes.shape)
Diabetes.head()

结果输出: (768, 9)
在这里插入图片描述

(2)展示数据的统计特征,并考察响应变量diabetes的分布

Diabetes.describe()
#查看y的取值分布
print(Diabetes.diabetes.value_counts(normalize=True))

结果输出:
在这里插入图片描述
diabetes
neg 0.651042
pos 0.348958
Name: proportion, dtype: float64

(3)根据diabetes的取值,画变量mass(body mass index)的箱形图

#画y(分类)的箱型图
import seaborn as sns
import matplotlib.pyplot as plt
sns.boxplot(x='diabetes', y='mass', 
            data=Diabetes, 
            palette="Set2",
            notch=True)

在这里插入图片描述

(4)使用random_state=0,通过分层抽样,随机选择200个观测值作为测试集

#特征数据 (X) 和目标数据 (y)。
X = Diabetes.drop('diabetes', axis=1)
y = Diabetes['diabetes']

#全样本分组
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
    X, y, stratify=y, test_size=200, random_state=0)

(5)将所有特征变量标准化,使得训练集的均值变为0,标准差为1

#标准化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X_train)
X_train_s = scaler.transform(X_train)
X_test_s = scaler.transform(X_test)

#检验标准化的均值&标准差
print('训练集的均值',np.mean(X_train_s, axis=0))
print('训练集的标准差',np.std(X_train_s, axis=0))
print('测试集的均值',np.mean(X_test_s, axis=0))
print('测试集的标准差',np.std(X_test_s, axis=0))

结果输出: 训练集的均值 [ 2.81464992e-17 -3.12738880e-17 3.42058150e-16 1.00076442e-16
-1.25095552e-17 -1.23531858e-16 7.19299424e-17 -2.25171994e-16]
训练集的标准差 [1. 1. 1. 1. 1. 1. 1. 1.]
测试集的均值 [-0.15863673 -0.02582328 -0.05719617 0.05870488 0.21886311 0.01932983
0.01403775 -0.13609468]
测试集的标准差 [0.94154255 1.03549433 1.03518333 0.87786465 1.05631569 1.10852183
0.91401169 0.93400616]

(6)进行KNN估计(K=10)

#KNN分类
from sklearn.neighbors import KNeighborsClassifier
model = KNeighborsClassifier(n_neighbors=10)
model.fit(X_train_s, y_train)  

(7)在测试集中预测,展示混淆矩阵,并计算预测准确率

#预测
pred = model.predict(X_test_s)
#计算混淆矩阵
pd.crosstab(y_test, pred, 
            rownames=['Actual'], 
            colnames=['Predicted'])
#测试集准确率
print(model.score(X_test_s, y_test))

结果输出:
在这里插入图片描述
0.715

(8)针对K=1,…,20,使用for循环,寻找使测试集预测准确率最大的K值

#手动寻找最优的K
scores = []
ks = range(1, 21)
for k in ks:
    model = KNeighborsClassifier(n_neighbors=k)
    model.fit(X_train_s, y_train)
    score = model.score(X_test_s, y_test)
    scores.append(score)
print('准确率的最大值:',max(scores)) #预测准确率的最大值

index_max = np.argmax(scores) #找“准确率的最大值”的索引位置
print(index_max)
print(f'Optimal K: {ks[index_max]}') #最优的K

结果输出: 准确率的最大值: 0.775
16
Optimal K: 17

(9)画图展示K与测试集预测准确率的关系

#画出 准确率与K 的关系图
plt.plot(ks, scores, 'o-')
plt.xlabel('K')
plt.axhline(max(scores), linewidth=1, linestyle='--', color='r')
plt.axvline(ks[index_max], linewidth=1, linestyle='--', color='k')
plt.ylabel('Accuracy')
plt.title('KNN:K v.s. accuracy')
plt.tight_layout()

(10)画图展示K与测试集预测 错分率的关系

#画不同k的错误率
errors = 1 - np.array(scores)
plt.plot(ks, errors, 'o-')
plt.xlabel('K')
plt.axhline(min(errors), linewidth=1, linestyle='--', color='r')
plt.axvline(ks[index_max], linewidth=1, linestyle='--', color='k')
plt.ylabel('Error Rate')
plt.title('KNN:k v.s error')
plt.tight_layout()

在这里插入图片描述

(11)画图展示1/K与测试集预测 错分率的关系

#用1/k度量KNN模型复杂度
errors = 1 - np.array(scores)
ks_inverse = 1 / np.array(ks)
plt.plot(ks_inverse, errors, 'o-')
plt.xlabel('1/K')
plt.ylabel('Error Rate')
plt.title('KNN: 1/k v.s errors')
plt.tight_layout()

在这里插入图片描述

(12)在训练集中进行10折交叉验证,选择最优K,并计算测试集预测准确率

#10折交叉验证选择最优超参数K
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import GridSearchCV

param_grid = {'n_neighbors': range(1, 21)}
kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=1)
model = GridSearchCV(KNeighborsClassifier(), param_grid, cv=kfold)
model.fit(X_train_s, y_train)
 
#最优K
print(model.best_params_)
 
#最优K时的准确率
model.score(X_test_s, y_test)

结果输出: {‘n_neighbors’: 17}
0.775

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值