K-近邻算法
1.1 什么是K-近邻(KNN)算法
学习目标
- 知道什么是K近邻算法,及其特点
- 掌握K-近邻算法的基本原理
1 K近邻算法的特点
- k-近邻算法,也叫KNN算法(K-Nearest Neihbor,KNN),是一个非常适合入门的算法,拥有如下特性:
- 适用于分类问题,尤其是二分类,当然也可以用来预测回归问题
- 思想极度简单,应用数学知识少(近乎为零)
- 对于很多不擅长数学的小伙伴十分友好,KNN算法几乎用不到数学专业知识
- 机器学习算法的hello world算法
- 效果好
- 虽然算法简单,但效果也不错
- 缺点也是存在的,后面会进行讲解
- 作为第一个算法,首先归纳总结下解决监督学习中分类问题的一般方法和步骤。
2 解决分类问题的一般方法
根据目标的不同将监督学习任务分为了分类预测问题及回归预测问题。
监督学习任务的基本流程和架构:
(1)首先准备数据,可以是视频、音频、文本、图片等等
(2)抽取所需要的一些列特征,形成特征向量(Feature Vectors)
(3)将这些特征向量连同标记(Label)一并送人机器学习算法中,训练出一个预测模型(Predictive Model)
(4)然后,采用同样的特征提取方法作用于新数据,得到用于测试的特征向量
(5)接下来,使用预测模型对这些待测的特征向量进行预测并得到结果(Expected Model)
(6)最后,评估模型的准确性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LN94B2rF-1630935537437)(./img/e5b43be5c96c33382005f5d21745c3c0.jpg)]
注:分类是常见的监督学习问题,最基础的就是二分类问题,即判断是非,从两个类别中选择一个作为预测结果。
3 什么是K-近邻算法
- 上图中的数据点是分布在一个特征空间中
- 横轴表示肿瘤大小,纵轴表示发现时间
- 恶性肿瘤用蓝色表示,良性肿瘤用红色表示
- 新来了一个病人(下图绿色的点),如何判断新来的病人(即绿色点)是良性肿瘤还是恶性肿瘤?
-
k-近邻算法的做法如下:
-
取一个值k=3(k值后面介绍,现在可以理解为算法的使用者根据经验取的最优值)
-
在所有的点中找到距离绿色点最近的三个点
-
让最近的点所属的类别进行投票
- 最近的三个点都是蓝色的,所以该病人对应的应该也是蓝色,即恶性肿瘤。
-
-
本质:如两个样本足够相似,那他们两个大概率属于同一类别
- 选择K个样本判断相似:如果只看一个已知样本,可能不准确
- 如果K个样本中大多数属于同一个类别,则被预测的样本就很可能属于对应的类别
- 如何衡量“相似性”:依靠距离衡量
-
再举例:
- 上图中和绿色的点距离最近的点包含两个红色和一个蓝色,此处红色点和蓝色点的数量比为2:1,则绿色点为红色的概率最大,最后判断结果为良性肿瘤。
通过上述例子不难发现,K近邻算法善于解决监督学习中的分类问题
小结
-
K-近邻算法属于哪类算法?可以用来解决监督学习中的分类问题
-
算法的思想:通过K个最近的已知分类的样本来判断未知样本的类别
1.2 K-近邻算法实现分类
学习目标
- 掌握K近邻算法中,距离计算的方法
1 绘制基本图像
import numpy as np
import matplotlib.pyplot as plt
#定义特征值
raw_data_x=[[3.3144558 , 2.33542461],
[3.75497175, 1.93856648],
[1.38327539, 3.38724496],
[3.09203999, 4.47090056],
[2.58593831, 2.13055653],
[7.41206251, 4.80305318],
[5.912852 , 3.72918089],
[9.21547627, 2.8132231 ],
[7.36039738, 3.35043406],
[7.13698009, 0.40130301]]
#定义目标值
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)
#利用matplotlib绘制图像
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.show()
2 加入预测点,绘制图像
x=np.array([8.093607318,3.365731514])
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[0],x[1],color='b')
plt.show()
-
预测蓝色点属于哪一个类别,需要找到距离蓝色点最近的K个邻居,找到K个邻居需要计算距离:
-
欧式距离:任意空间下两个点的对应维度的坐标值相减的平方和再开方
-
欧式距离公式:
-
平面中两点之间的欧式距离:
( x ( a ) − x ( b ) ) 2 + ( y ( a ) − y ( b ) ) 2 \Large \sqrt{(x^{(a)}-x^{(b)})^2+(y^{(a)}-y^{(b)})^2} (x(a)−x(b))2+(y(a)−y(b))2
-
立体中两点之间的距离:
( x ( a ) − x ( b ) ) 2 + ( y ( a ) − y ( b ) ) 2 + ( z ( a ) − z ( b ) ) 2 \Large \sqrt{(x^{(a)}-x^{(b)})^2+(y^{(a)}-y^{(b)})^2+(z^{(a)}-z^{(b)})^2} (x(a)−x(b))2+(y(a)−y(b))2+(z(a)−z(b))2
-
任意维度下的欧拉距离:
( x 1 ( a ) − x 1 ( b ) ) 2 + ( x 2 ( a ) − x 2 ( b ) ) 2 + . . . + ( x n ( a ) − x n ( b ) ) 2 \Large \sqrt{(x_{1}^{(a)}-x_{1}^{(b)})^2+(x_{2}^{(a)}-x_{2}^{(b)})^2+...+(x_{n}^{(a)}-x_{n}^{(b)})^2} (x1(a)−x1(b))2+(x2(a)−x2(b))2+...+(xn(a)−xn(b))2
-
简化后:
∑ i = 1 n ( x i ( a ) − x i ( b ) ) 2 \Large \sqrt{\sum_{i=1}^{n}(x_{i}^{(a)}-x_{i}^{(b)})^2} i=1∑n(xi(a)−xi(b))2
-
-
3 实现距离计算
# np.sqrt() 开方
# np.square() 平方
distances=np.sqrt(np.sum(np.square(X_train-x),axis=1))
distances
# 结果展示
# array([4.88894892, 4.56733605, 6.71036641, 5.12221381, 5.64447297,
# 1.59072213, 2.2108345 , 1.25054208, 0.7333695 , 3.11495939])
# 对计算的距离进行排序
nearest = np.argsort(distances)
nearest
# 结果展示,对索引下标排序
# array([8, 7, 5, 6, 9, 1, 0, 3, 4, 2])
4 定义k值,得出计算结果
k=6
# 获取距离最近的前k个数据
# y_train[i],通过i索引下标获取具体的y值
topK_y = [y_train[i] for i in nearest[:k]]
topK_y
# [1, 1, 1, 1, 1, 0]
# 统计每个y值出现的次数
from collections import Counter
votes = Counter(topK_y)
votes
# Counter({1: 5, 0: 1})
# 获取出现次数最多的那个y的取值
predict_y = votes.most_common(1)[0][0]
predict_y
# 1
votes.most_common(1)
# [(1, 5)]
5 K-近邻算法的特点
-
回顾机器学习算法的工作流程:
- 首先准备X_train,y_train交给机器学习算法
- 算法对数据进行拟合形成模型
- 输入样例交给模型进行预测,得到输出结果
-
knn算法有没有得到模型?
- knn算法没有得到模型,它是机器学习中唯一一个不需要训练过程的算法
- 没有模型的算法
- 为了和其他算法统一,可以认为数据集就是模型本身
小结
- 判断K-近邻的时候,使用欧式距离进行距离计算
- K-近邻算法实际上并没有模型
1.3 sklearn中knn算法的应用
学习目标
- 掌握通过调用sklearn中的API实现K-近邻分类
1 sklearn简介
- sklearn 是一个开源的机器学习Python库,底层计算基于Numpy,Scipy等科学计算库
- sklearn 提供了大部分常用的机器学习相关API,包括特征工程,模型训练, 以及模型评估
2 在sklearn中实现KNN算法
from sklearn.neighbors import KNeighborsClassifier
# 创建knn算法的分类器实例
# n_neighbors=6,指定选取邻居的个数
knn_classifier = KNeighborsClassifier(n_neighbors=6)
# 拟合训练数据
knn_classifier.fit(X_train,y_train)
# 将样本维度变为二维,X_train,y_train为二维数组
x1 = x.reshape(1, -1)
x1
# array([[8.09360732, 3.36573151]])
# 利用knn算法进行预测
y_predict = knn_classifier.predict(x1)
# 得出预测结果
y_predict[0]
# 1
3 模仿Sklearn重新整合前面的代码
- 整合为python代码,记做:knn.py
import numpy as np
from math import sqrt
from collections import Counter
class KNNClassifier:
def __init__(self, k):
"""初始化kNN分类器"""
assert k >= 1, "k must be valid"
self.k = k
self._X_train = None
self._y_train = None
def fit(self, X_train, y_train):
"""根据训练数据集X_train和y_train训练kNN分类器"""
assert X_train.shape[0] == y_train.shape[0], \
"the size of X_train must be equal to the size of y_train"
assert self.k <= X_train.shape[0], \
"the size of X_train must be at least k."
self._X_train = X_train
self._y_train = y_train
return self
def predict(self, X_predict):
"""给定待预测数据集X_predict,返回表示X_predict的结果向量"""
assert self._X_train is not None and self._y_train is not None, \
"must fit before predict!"
assert X_predict.shape[1] == self._X_train.shape[1], \
"the feature number of X_predict must be equal to X_train"
y_predict = [self._predict(x) for x in X_predict]
return np.array(y_predict)
def _predict(self, x):
"""给定单个待预测数据x,返回x的预测结果值"""
assert x.shape[0] == self._X_train.shape[1], \
"the feature number of x must be equal to X_train"
distances = [sqrt(np.sum((x_train - x) ** 2))
for x_train in self._X_train]
nearest = np.argsort(distances)
topK_y = [self._y_train[i] for i in nearest[:self.k]]
votes = Counter(topK_y)
return votes.most_common(1)[0][0]
def __repr__(self):
return "KNN(k=%d)" % self.k
- 将knn.py文件放入到和jupyter的代码相同的目录的knn目录下,利用Jupyter notebook的命令来加载knn.py
#利用%run 加载KNN.py文件
%run knn/knn.py
#创建实例
knn_clf = KNNClassifier(k=6)
#拟合训练集
knn_clf.fit(X_train,y_train)
#预测输入样例
y_predict = knn_clf.predict(x1)
#得出预测结果
y_predict[0]
小结
- Sklearn是最常用的机器学习Python开源库
- 通过Sklearn中的KNeighborsClassifier可以实现使用K-近邻算法进行分类
1.4 数据集的划分
学习目标
- 知道为什么要划分数据集
- 掌握如何使用sklearn进行数据集划分
1 为什么要划分数据集
- 上面的案例,我们训练数据的过程如下:
- 将所有的数据都作为训练数据,训练出一个模型
- 每当得到一个新的数据,则计算新数据到训练数据的距离,预测得到新数据的类别
- 存在问题:
- 如何评估模型的好坏?
- 模型使用所有数据训练,使用哪些数据来进行模型评估?
- 结论:不能将所有数据集全部用于训练,划分数据集为了验证模型效果
2 如何划分数据集
-
一般将数据中的70%-80%作为训练数据,将剩余的作为测试数据
-
训练数据用于训练模型,测试数据用于判断模型好坏
-
下面SKlearn中自带的鸢尾花数据集来实现训练集测试集的划分
鸢尾花数据集简介
- Iris 鸢尾花数据集是一个经典数据集,在统计学习和机器学习领域都经常被用作示例
- 数据集内包含 3 类共 150 条记录,每类各 50 个数据
- 每条记录都有 4 项特征:花萼长度、花萼宽度、花瓣长度、花瓣宽度
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rH6B1Frb-1630935537441)(img/iris1.png)]
- 可以通过这4个特征预测鸢尾花卉属于(iris-setosa, iris-versicolour, iris-virginica)中的哪一品种
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MDrn5kNA-1630935537442)(img/iris2.png)]
-
代码:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
#获取sklearn中的内置数据集
iris = datasets.load_iris()
#获取特征值和目标值
X = iris.data
y = iris.target
#将长度X对应的索引值进行随机排列
shuffle_indexes = np.random.permutation(len(X))
#定义测试机的比例
test_ratio = 0.2
#得到测试集对应的数据量
test_size = int(len(X) * test_ratio)
#获取测试集的索引和训练集的索引
test_indexes = shuffle_indexes[:test_size]
train_indexes = shuffle_indexes[test_size:]
#获取训练数据集
X_train = X[train_indexes]
y_train = y[train_indexes]
#获取测试数据集
X_test = X[test_indexes]
y_test = y[test_indexes]
3 sklearn中的train_test_split
- sklearn中的train_test_split可以方便的将数据划分成训练集和测试集
from sklearn.model_selection import train_test_split
# test_size=0.2,可以指定比例,也可以指定数量
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2)
小结
- 在模型训练中,会将数据划分为训练集和测试集,目的是为了验证模型效果
- 在sklearn中可以通过sklearn.model_selection中train_test_split函数完成训练集测试集的划分
1.5 分类算法的评估
学习目标
- 知道如何对分类算法进行评估
- 掌握sklearn对分类算法进行评估的API的用法
1 分类算法的评估
- 如何评估分类算法?
- 利用训练好的模型使用测试集的特征值进行预测
- 将预测结果和测试集的目标值比较,计算预测正确的百分比
- 这个百分比就是准确率 accuracy, 准确率越高说明模型效果越好
import numpy as np
from sklearn import datasets
#加载鸢尾花数据
iris = datasets.load_iris()
- 使用SKlearn的KNeighborsClassifier进行分类预测
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
#训练集 测试集划分
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2)
# 创建KNN分类器对象 近邻数为6
knn_clf = KNeighborsClassifier(n_neighbors=6)
#训练集训练模型
knn_clf.fit(X_train,y_train)
#使用训练好的模型进行预测
y_predict = knn_clf.predict(X_test)
- 计算准确率:
sum(y_predict==y_test)/y_test.shape[0]
2 sklearn中的accuracy_score
代码:
#计算准确率
from sklearn.metrics import accuracy_score
#方式1:
accuracy_score(y_test,y_predict)
#方式2:
knn_clf.score(X_test,y_test)
小结
- 分类算法可以通过计算准确率(accuracy_score)来评估模型的好坏
- 准确率比较了模型预测的结果和测试集的真实标签,计算正确预测的结果占所有测试集数据的比例, 比例越高说明模型效果越好
1.6 归一化和标准化
学习目标
- 知道为什么要进行归一化、标准化
- 掌握归一化、标准化的计算方法及sklearn中对应API的使用
- 掌握归一化、标准化的适用场景
1 为什么要进行归一化、标准化
-
问题:
观察如下样本数据,我们有两个样本,每个样本有两个特征,一个是肿瘤大小,一个是肿瘤的发现时间。
如利用欧式距离计算上述两个样本的距离,由于两个特征的值差距较大,距离结果的值大小由发现时间决定。
肿瘤大小(厘米) 发现时间(天) 样本1 1 200 样本2 6 100 如果此时修改发现时间的单位为年,则最终距离结果又由肿瘤大小决定。
肿瘤大小(厘米) 发现时间(年) 样本1 1 0.55年(200天) 样本2 6 0.27年(100天) 综上所述,我们在训练模型时,如果特征之间的值差距较大,不能直接传入模型,需要对数据做归一化处理。
-
作用:将所有数据映射到同一尺度,使不同单位或量级的特征能够进行比较和加权。
2 归一化和标准化的计算方法
-
归一化(Normalization):把所有数据**映射到(0,1)**之间
-
计算流程: x s c a l e = x − x m i n x m a x − x m i n \Large x_{scale}=\frac{x-x_{min}}{x_{max}-x_{min}} xscale=xmax−xminx−xmin
- 先计算所有样本的每个特征的最大值和最小值计算出来
- 用每个样本的特征值减去对应的最小值 x − x m i n x-x_{min} x−xmin,
- 再计算最大值和最小值的差值 x m a x − x m i n x_{max}-x_{min} xmax−xmin
- 最后将两个结果进行除法操作,得到最终的特征归一化结果 x s c a l e x_{scale} xscale。
-
适用情况:分布有明显边界
举例:学生分数0-100有明显边界,图片像素点的值0-255有明显边界。
-
缺点:受异常值(outlier)影响较大
观察公式,结果受 x m a x x_{max} xmax和 x m i n x_{min} xmin影响严重
-
-
标准化(standardization):把所有数据利用均值和标准差进行转换,均值为0,标准差为1
-
x
s
c
a
l
e
=
x
−
x
m
e
a
n
S
\Large x_{scale}=\frac{x-x_{mean}}{S}
xscale=Sx−xmean
- 利用每个特征值减去特征值均值再除以特征值的标准差得到标准化的结果
- 适用情况:数据分布没有明显边界
- 优点:不容易受到极端数据值的影响
-
x
s
c
a
l
e
=
x
−
x
m
e
a
n
S
\Large x_{scale}=\frac{x-x_{mean}}{S}
xscale=Sx−xmean
3 代码实现
-
加载数据
import numpy as np import matplotlib.pyplot as plt #加载数据 data = np.loadtxt('data/data1.txt', delimiter='\t') # 读取数据 #可视化查看数据分布情况 plt.scatter(data[:,0],data[:,1]) plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BYQVR47l-1630935537444)(./img/32img.png)]
-
数据归一化
data_minmax = data.copy() #对两列特征进行归一化 min0 = np.min(data_minmax[:,0]) max0 = np.max(data_minmax[:,0]) data_minmax[:,0]=(data_minmax[:,0]-min0)/(max0-min0) min1 = np.min(data_minmax[:,1]) max1 = np.max(data_minmax[:,1]) data_minmax[:,1]=(data_minmax[:,1]-min1)/(max1-min1) #数据可视化 plt.scatter(data_minmax[:,0],data_minmax[:,1]) plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o3uqy1sV-1630935537446)(./img/33img.png)]
- 从上图可以看出,数据经过归一化之后,两个特征的分布都落在0~1之间
-
标准化实现
data_std = data.copy() # 计算每一列特征的均值和方差 mean0 = np.mean(data_std[:,0]) std0=np.std(data_std[:,0]) mean1 = np.mean(data_std[:,1]) std1=np.std(data_std[:,1]) #对每一列特征分别进行标准化转换 data_std[:,0] = (data_std[:,0]-mean0)/std0 data_std[:,1] = (data_std[:,1]-mean1)/std1 #绘图显示 plt.scatter(data_std[:,0],data_std[:,1]) plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NZhDYdbe-1630935537447)(./img/34img.png)]
- 从上图中可以看出, 经过标准化转换之后,两个特征的大小都在0附近(均值为0,标准差为1),量纲统一
4 sklearn中的归一化API
-
在sklearn中 提供了归一化和标准化的相关API
- from sklearn.preprocessing import MinMaxScaler
- from sklearn.preprocessing import StandardScaler
-
sklearn中的归一化API的使用方法
- Scalar类中有三个方法 fit,transform,fit_transform
- 调用fit方法时会计算每个特征关键信息(均值,标准差,极值等)并保存
- 调用transform时,会使用fit方法保存的关键信息(均值,标准差,极值等)进行归一化/标准化转换
- fit_transform, 相当于fit 和 transform一起调用
-
训练集和测试集的归一化
- 是否要对训练数据和测试数据分别做归一化/标准化操作?
- 利用训练集得到均值和方差:
- X_train.fit()
- 训练集和测试集均利用训练集得到的均值和方差来进行归一化处理
- 调用transform的时候,使用训练集的关键信息(均值,方差,极值等)进行归一化/标准化进行转换
- x_test-mean_train/std_train
- 不能使用 X_test.fit()
- 测试数据的目的是为了模拟真实环境,真实环境下很有可能无法计算均值和方差(仅预测一个样本)
代码实现:
import numpy as np from sklearn import datasets iris = datasets.load_iris() X = iris.data y = iris.target # 划分训练集和测试集,random_state=1,指定随机数种子为1,数据不再随机 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,random_state=1) #进行归一化处理 from sklearn.preprocessing import MinMaxScaler mmScaler = MinMaxScaler() mmScaler.fit(X_train) #转换训练集和测试集 X_train1 = mmScaler.transform(X_train) X_test1 = mmScaler.transform(X_test) #对比准确率 from sklearn.neighbors import KNeighborsClassifier knn_clf = KNeighborsClassifier(n_neighbors=3) knn_clf.fit(X_train1,y_train) knn_clf.score(X_test1,y_test)
-
练习 :适用SKlearn提供的标准化API对数据进行处理
提示:
from sklearn.preprocessing import StandardScaler
小结
- 为什么要进行归一化、标准化
- 归一化、标准化,是对特征进行平移和缩放,将所有特征的量纲变换到相同的大小范围内
- 当不同特征的量纲(大小)不一致时,先对特征进行归一化、标准化可以避免量纲大小不同对模型造成影响
- 归一化、标准化计算方法和API
- x s c a l e = x − x m i n x m a x − x m i n \Large x_{scale}=\frac{x-x_{min}}{x_{max}-x_{min}} xscale=xmax−xminx−xmin
- x s c a l e = x − x m e a n S \Large x_{scale}=\frac{x-x_{mean}}{S} xscale=Sx−xmean
- from sklearn.preprocessing import MinMaxScaler
- from sklearn.preprocessing import StandardScaler
- 归一化、标准化的适用场景
- 归一化:有固定取值范围, 且数据中无异常值
- 标准化:无固定取值范围,不受异常值影响
1.7 超参数与交叉验证
学习目标
- 知道超参数的概念
- 知道交叉验证的概念
- 掌握使用SKlearn进行交叉验证和超参数搜索的方法
1 超参数的概念
-
超参数概念:
在前面案例中,介绍knn算法时,k的取值为随意定义,但传什么值才是最优的呢,这样的问题在机器学习中就是超参数问题。
定义:超参数就是在运行机器学习算法之前需要指定值的参数
-
超参数和模型参数概念对比:
超参数:在算法运行前需要指定的参数
模型参数:算法过程中学习的参数(后面介绍到其它算法时再详细介绍)
- knn算法中没有模型参数
-
knn中的k就是典型的超参数
-
寻找最优的超参数
- 领域知识:在不同的知识领域中,最优的超参数值不一样
- 经验数值:在处理问题时,存在一些较好的经验数值,可以借鉴。通常机器学习中会对超参数设置默认数值,这个值即为经验数值。sklearn中knn算法中的k值默认为5。
- 实验搜索:在处理具体问题中,经验数值不适用,则需要使用此项操作,来得到不同的超参数。
2 寻找最优的超参数案例
- 使用KNN算法进行分类
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
# 划分训练集测试集
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2)
# 训练模型
knn_classifier = KNeighborsClassifier(n_neighbors=6)
knn_classifier.fit(X_train,y_train)
#得到评分数据
knn_classifier.score(X_test,y_test)
- 可以通过调整k值,尝试提高模型表现:
best_score = 0.0
best_k = -1
for k in range(1,11):
knn_classifier = KNeighborsClassifier(n_neighbors=k)
knn_classifier.fit(X_train,y_train)
score = knn_classifier.score(X_test,y_test)
print("k={}, score={}".format(k, score))
if score > best_score:
best_k = k
best_score = score
print("best_k =",best_k)
print("best_score =",best_score)
-
除了调整K值之外,k-近邻算法还有哪些参数可以调整?
-
K近邻判断结果的两种方式:
-
方式1:
距离绿色点最近的三个点中,两个点均为蓝色,则结果绿色点预测为蓝色。
存在问题:绿色点距离红色点比蓝色点近,可以将距离也作为判定因素考虑进去,这就是另外一种k-近邻算法的计算方式
-
方式2:
考虑距离,一般将距离的倒数作为判定依据:
红色为1,蓝色为:1/3+1/4=7/12,则判断结果为红色
-
-
方式2的优点:解决平票问题
- 当绿色点的附近三个点均为不同颜色的点时,则为平票
- 此时考虑距离就可以很好的解决最终结果的选择问题。
-
在Sklearn的KNeighborsClassifier的API中,可以通过调整weights参数来靴子上述两种方式
-
参数weights默认为uniform,不考虑距离
-
如传入distance,则考虑距离。
-
考虑距离的代码实现:
best_score = 0.0
best_k = -1
best_method = None
for method in ["uniform","distance"]:
for k in range(1,11):
knn_classifier = KNeighborsClassifier(n_neighbors=k,weights=method)
knn_classifier.fit(X_train,y_train)
score = knn_classifier.score(X_test,y_test)
if score > best_score:
best_k = k
best_score = score
best_method = method
print("best_method =",best_method)
print("best_k =",best_k)
print("best_score =",best_score)
3 SKlearn中的超参数搜索与交叉验证
-
什么是网格搜索(Grid Search)
-
Sklearn中寻找最佳的超参数的组合的过程称为网格搜索
-
什么是交叉验证(cross validation)
-
交叉验证:将拿到的训练数据,分为训练和验证集。以下图为例:
- 将数据分成10份,其中一份作为验证集(k折交叉验证)
- 经过10次(组)的测试,每次都更换不同的验证集。即得到10组模型的结果,取平均值作为最终结果。又称10折交叉验证。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ecum7djL-1630935537448)(img/31.png)]
-
-
为什么需要交叉验证
-
交叉验证目的:为了让被评估的模型更加准确可信
-
交叉验证-网格搜索API:
- sklearn.model_selection.GridSearchCV(estimator, param_grid=None,cv=None)
- 解释:对估计器的指定参数值进行详尽搜索
- 参数:
- estimator:估计器对象
- param_grid:估计器参数(dict){“n_neighbors”:[1,3,5]}
- cv:指定几折交叉验证
- 方法:
- fit:输入训练数据
- score:准确率
- 结果分析:
- best_score_:在交叉验证中验证的最好结果
- best_estimator_:最好的参数模型
- cv_results_:每次交叉验证后的验证集准确率结果和训练集准确率结果
- sklearn.model_selection.GridSearchCV(estimator, param_grid=None,cv=None)
-
使用GridSearchCV对鸢尾花案例进行调优:
#导入划分数据集、交叉验证、网格搜索的接口
from sklearn.model_selection import train_test_split,GridSearchCV
#导入预估器类(模型)接口
from sklearn.neighbors import KNeighborsClassifier
#导入特征工程的标准化接口
from sklearn.preprocessing import StandardScaler
#导入获取数据集接口
from sklearn.datasets import load_iris
#1.获取数据集:
iris=load_iris()
x=iris.data
y=iris.target
#2.数据基本处理---划分数据集:
x_train,x_test,y_train,y_test=train_test_split(x,y,random_state=22)
#3.特征工程:标准化
#实例化一个转换器类:
transfer=StandardScaler()
#调用fit_transform
x_train=transfer.fit_transform(x_train)
x_test=transfer.transform(x_test)
#4.KNN预估流程
#4.1 实例化预估器类
estimator=KNeighborsClassifier()
#4.2 模型选择与调优——网格搜索和交叉验证
#准备要调的超参数
param_dict={'n_neighbors':[1,3,5],'weights':['uniform','distance']}
estimator=GridSearchCV(estimator,param_grid=param_dict,cv=3)
#4.3 fit数据进行训练
estimator.fit(x_train,y_train)
#5.评估模型效果
#方法a:对比预算结果和真实值
y_predict=estimator.predict(x_test)
print("比对预测结果和真实值:\n", sum(y_predict==y_test)/y_test.shape[0])
#方法b:直接计算准确率
score=estimator.score(x_test,y_test)
print("直接计算准确率:\n", score)
print('\n')
#查看交叉验证的最好结果
print("在交叉验证中验证的最好结果:\n", estimator.best_score_)
print("最好的参数模型:\n", estimator.best_params_)
- 查看交叉验证的最终结果
print("在交叉验证中验证的最好结果:\n", estimator.best_score_)
print("最好的参数模型:\n", estimator.best_params_)
显示结果:
在交叉验证中验证的最好结果: 0.9732100521574205 最好的参数模型: {'n_neighbors': 5, 'weights': 'uniform'}
小结
- 超参数
- 在训练模型时,需要调整的参数为超参数
- 超参数调整是模型调优的重要手段
- 交叉验证
- 将数据进一步划分为训练集、验证集和测试集
- 通过训练集,验证集调参,通过测试集评估模型,这样的评估结果更加准确
- 掌握使用SKlearn进行交叉验证和超参数搜索的方法
- sklearn.model_selection.GridSearchCV
- 参数:
- estimator:估计器对象
- param_grid:估计器参数(dict){“n_neighbors”:[1,3,5]}
- cv:指定几折交叉验证
1.8 案例:预测facebook签到位置
1 项目描述
本次比赛的目的是预测一个人将要签到的地方。
为了本次比赛,Facebook创建了一个虚拟世界,其中包括10公里*10公里共100平方公里的约10万个地方。
对于给定的坐标集,您的任务将根据用户的位置,准确性和时间戳等预测用户下一次的签到位置。
数据被制作成类似于来自移动设备的位置数据。
2 数据集介绍
数据介绍:
文件说明 train.csv, test.csv
row id:签入事件的id
x y:坐标
accuracy: 准确度,定位精度
time: 时间戳
place_id: 签到的位置,这也是你需要预测的内容
3 步骤分析
- 对于数据做一些基本处理(这里所做的一些处理不一定达到很好的效果,我们只是简单尝试,有些特征我们可以根据一些特征选择的方式去做处理)
- 1 缩小数据集范围 DataFrame.query()
- 2 选取有用的时间特征
- 3 将签到位置少于n个用户的删除
- 分割数据集
- 标准化处理
- k-近邻预测
具体步骤:
# 1.获取数据集
# 2.基本数据处理
# 2.1 缩小数据范围
# 2.2 选择时间特征
# 2.3 去掉签到较少的地方
# 2.4 确定特征值和目标值
# 2.5 分割数据集
# 3.特征工程 -- 特征预处理(标准化)
# 4.机器学习 -- knn+cv
# 5.模型评估
4 代码实现
- 1.获取数据集
from sklearn.model_selection import GridSearchCV
import pandas as pd
# 1、获取数据集
facebook = pd.read_csv("data/train.csv")
- 2.基本数据处理
# 2.基本数据处理
# 2.1 缩小数据范围
facebook_data = facebook.query("x>2.0 & x<2.5 & y>2.0 & y<2.5")
# 2.2 选择时间特征
facebook_data['time'] = pd.to_datetime(facebook_data['time'],unit='s')
facebook_data['day'] = facebook_data['time'].dt.day
facebook_data['hour'] = facebook_data['time'].dt.hour
facebook_data['weekday'] = facebook_data['time'].dt.weekday
# 2.3 去掉签到较少的地方
place_count = facebook_data.groupby("place_id").count()
place_count = place_count[place_count["row_id"]>3]
facebook_data = facebook_data[facebook_data["place_id"].isin(place_count.index)]
# 2.4 确定特征值和目标值
x = facebook_data[["x", "y", "accuracy", "day", "hour", "weekday"]]
y = facebook_data["place_id"]
# 2.5 分割数据集
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=22)
- 3.特征工程–特征预处理(标准化)
# 3.特征工程--特征预处理(标准化)
# 3.1 实例化一个转换器
transfer = StandardScaler()
# 3.2 调用fit_transform
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
- 4.机器学习–knn+cv
# 4.机器学习--knn+cv
# 4.1 实例化一个估计器
estimator = KNeighborsClassifier()
# 4.2 调用gridsearchCV
param_grid = {"n_neighbors": [1, 3, 5, 7, 9]}
estimator = GridSearchCV(estimator, param_grid=param_grid, cv=5)
# 4.3 模型训练
estimator.fit(x_train, y_train)
- 5.模型评估
# 5.模型评估
# 5.1 基本评估方式
score = estimator.score(x_test, y_test)
print("最后预测的准确率为:\n", score)
y_predict = estimator.predict(x_test)
print("最后的预测值为:\n", y_predict)
print("预测值和真实值的对比情况:\n", y_predict == y_test)
# 5.2 使用交叉验证后的评估方式
print("在交叉验证中验证的最好结果:\n", estimator.best_score_)
print("最好的参数模型:\n", estimator.best_estimator_)
print("每次交叉验证后的验证集准确率结果和训练集准确率结果:\n",estimator.cv_results_)
小结
-
K近邻算法的优缺点
-
优点
- 天然能够解决多分类问题
- 思想简单,效果强大
- 使用k-近邻算法还可以解决回归问题(from sklearn.neighbors import KNeighborsRegressor)
由于回归问题是计算一个连续性的数值,而k-近邻算法计算的距离结果也是一个连续性的数值。
如图:可以将绿色的点距离较近的三个点的距离值加权平均,得到绿色点的回归结果
-
缺点:
-
效率低下:如果训练集有m个样本,n个特征,则预测每一个新的数据,需要O(m*n)的时间复杂度
优化
-
高度敏感数据相关:所有的机器学习算法均与高度敏感数据相关,但是k-近邻算法尤为突出。
当特征中出现两个错误值,就足以让k-近邻算法的准确率大大下降。
-
预测结果不具有可解释性:仅仅通过距离较近来判断类别,结果没有可解释性。
-
-
-
K-近邻(KNN)算法是最简单的机器学习算法
- 通过“距离” 计算,找到预测样本的最近的K个邻居
- 根据K个邻居的类别来判断预测样本的类别
-
可以通过sklearn封装的KNeighborsClassifier对象,来实现K-近邻算法的分类预测
-
在训练模型之前,要将数据划分为训练集和测试集
-
K-近邻算法中,K个近邻中参数K为超参数, 可以通过调整超参数来获取最优的模型
-
sklearn中的GridSearchCV可以帮助方便的进行超参数搜索,并且进行交叉验证
-
在使用K-近邻算法进行模型训练之前,需要将数据进行归一化/标准化,目的是为了统一特征的量纲,避免量纲的差异对模型带来影响