机器学习(五)k-近邻算法
一. k-近邻算法简介
该算法可以算得上是最简单的分类算法了。他的原理也很简单。举一个比较通俗的例子,在生活当中,我们经常说:物以类聚,人以群分。假设,我们认识了一个人,你想调查这个人到底是什么样子的性格,有什么样子的性格爱好。那么你就可以看看他的密友,或者亲戚。那么大概率会是这个结果:朋友是什么样子的人,他也是那个样子的人。
这便是k近邻算法的一个通俗的解释。在实际运用当中,k近邻算法也是用来判断一个未知的样本属于什么类型的。判断方式,就是看这个未知样本和哪个已知样本比较相近。比如下面这个例子:
我们有若干个已知样本和他们的类型,现在,统计出来了一个新的电影,我们不知道他是什么类型,但是知道这个电影当中有18个打斗镜头,有90个接吻镜头,我们该怎么判断这个电影的类型?
其实,根据人的直观观察,大体就能猜出这是一个爱情电影。因为他接吻镜头很多,与前三行那几个爱情电影比较相近。但是,放到机器学习里面,你该如何判断这个“相近程度”呢?这便是k-近邻算法所涉及的问题。
1.1 k-近邻算法定义:
k-近邻算法,也简称为:KNN算法。其定义如下:
如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。
1.2 k-近邻算法的距离:
也就是我们开头所说的“相近程度”。实际上,关于这个距离的度量,有很多算法,其中最简单的莫过于欧氏距离:
除了欧氏距离,常见的还有:
二.k-近邻算法的使用
2.1 API介绍
在skleanr当中,为我们直接提供了K-近邻算法的接口:
sklearn.neighbors.KNeighborsClassifier(n_neighbors=5,algorithm='auto')
它的参数含义如下:
-
n_neighbors:int,可选(默认= 5),k_neighbors查询默认使用的邻居数
-
algorithm:{‘auto’,‘ball_tree’,‘kd_tree’,‘brute’},可选用于计算最近邻居的算法:‘ball_tree’将会使用 BallTree,‘kd_tree’将使用 KDTree。‘auto’将尝试根据传递给fit方法的值来决定最合适的算法。 (不同实现方式影响效率)。
这当中,brute代表暴力求解。至于什么是kd-tree, 什么事ball-tree。暂时先不作要求
2.2 案例
假设我们有一个虚拟城市,这个城市的市民每到一个地方都要签到。现在我们有一个.csv文件,里面记录了这个城市市民签到的信息。让你去预测一下,如果这个城市来了一个新人,那这个人最有可能在哪个地方签到。
这个.csv文件的字段如下:
row_id, x, y, accuracy, time, place_id
其中row-id 代表行号
(x,y)代表一个坐标
accuracy代表精确值
time为入住时间(这个是一个时间戳,而不是我们常用的时间格式)
place_id 代表入住地点的id号
重点:
无论是做什么样子的案例,大体都应该遵循以下步骤:
-
1、数据集的处理
-
2、分割数据集
-
3、对数据集进行标准化
-
4、estimator流程进行分类预测
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
import pandas as pd
def knn():
"""
K-近邻预测用户签到位置
:return:None
"""
# 读取数据
data = pd.read_csv("./Data/train.csv")
# 处理数据(对应上述的步骤1)
# 1、缩小数据,查询数据范围
data = data.query("x > 1.0 & x < 1.25 & y > 2.5 & y < 2.75")
# 处理时间的数据,把原数据里面的时间戳给改成以秒为单位的时间
time_value = pd.to_datetime(data['time'], unit='s')
# 把日期格式转换成 字典格式
time_value = pd.DatetimeIndex(time_value)
# 构造一些特征
data['day'] = time_value.day
data['hour'] = time_value.hour
data['weekday'] = time_value.weekday
"""
很多时候,我们都需要把不需要的属性,对结果形成干扰的部分给删除掉,一方面不需要,另一方面,可以提高运行效率
"""
# 把时间戳特征删除
data = data.drop(['time'], axis=1)
# 把签到数量少于或者等于3个目标位置删除(太少了,对结果形成不了太多的影响)
place_count = data.groupby('place_id').count()
tf = place_count[place_count.row_id > 3].reset_index()
data = data[data['place_id'].isin(tf.place_id)]
# 取出数据当中的特征值和目标值,即签到地点的相关数据
y = data['place_id']
# 注意:sklearn,axis=0代表列,但是在pandas当中,axis=1代表列
x = data.drop(['place_id'], axis=1)
# 进行数据的分割训练集合测试集(对应上面的步骤2)
#x_train: 训练数据集,x_test:测试数据集,y_train:训练集tag,y_test:测试集tag
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.25)
# 特征工程(标准化)(对应上面的步骤3)
std = StandardScaler()
# 对测试集和训练集的特征值进行标准化
x_train = std.fit_transform(x_train)
x_test = std.transform(x_test)
# 进行算法流程
knn = KNeighborsClassifier()
# fit, predict,score (对应上面的step4)
knn.fit(x_train, y_train)
# 得出预测结果
y_predict = knn.predict(x_test)
print("预测的目标签到位置为:", y_predict)
# 得出准确率
print("预测的准确率:", knn.score(x_test, y_test))
if __name__ == "__main__":
knn()
三、总结
k-近邻算法算是最简单的分类算法,他有如下特点:
-
优点:简单,易于理解,易于实现,无需估计参数,无需训练
-
缺点:k-近邻算法是一种懒惰算法,对测试样本分类时的计算量大,内存开销大;必须指定K值,K值选择不当则分类精度不能保证
- k取值过小,则无法有效应对异常点影响
- k值取过大,则导致计算量过大,且容易受最近数据太多影响,回到开头的人以群分的例子,就好比说,你想调查一个人,但是你既找了他的密友,也找了很多和他并没有太多接触的人,那么,你就很难了解这个人到底是什么人了。
-
k-近邻算法适用于小数据场景