赞
踩
公众号后台回复“图书“,了解更多号主新书内容
作者:周萝卜
来源:萝卜大杂烩
上一节我们完成了 KNN 算法理论的学习,同时还手动写了一个简易版的 KNN 分类器。今天我们来进行 KNN 的实战,看看如何通过 KNN 算法来解决生活中的问题。
在实战之前,我们先来介绍一个概念-超参数。
还记得我们上一节讲到的选择 K 值吗,这里的 K 就是超参。
所谓超参数,就是在机器学习算法模型执行之前需要指定的参数。(调参调的就是超参数) 如KNN 算法中的 K。
与之相对的概念是模型参数,即算法过程中学习的属于这个模型的参数(KNN 中没有模型参数,回归算法有很多模型参数)
如何选择超参数,是机器学习中的永恒问题。调参的方法也很多,例如上节我们提到的交叉验证法。
下面我们就进入实战例子,看看如何用 KNN 算法来解决生活中的问题。
上一节我只是简单的介绍了 sklearn,并创建了一个 KNN 的分类器,今天我们就具体来看看如何使用 sklearn 中的 KNN 分类器。
上节我们已经知道,要想使用 sklearn 的 KNN 算法,我们要先导入相关的包
from sklearn.neighbors import KNeighborsClassifier
然后再实例化该类即可
knn = KNeighborsClassifier()
KNeighborsClassifier 类是有几个构造参数的,我们来逐一看下
n_neighbors:默认值是5,即为 K 的值
weights:权重值,默认为uniform,取值可以是 uniform、distance,也可以是用户自己定义的函数。uniform 是均等的权重,就说所有的邻近点的权重都是相等的。distance 是不均等的权重,距离近的点比距离远的点的影响大。
algorithm:快速 K 近邻搜索算法,默认参数为 auto,可以理解为算法自己决定合适的搜索算法。此外我们也可以自行指定搜索算法 ball_tree、kd_tree、brute 等,一般使用默认即可。
fit 函数是用来通过特征矩阵,分类标识,让分类器进行拟合,如:
knn.fit(X_train, y_train)
predict 函数用于返回预测结果,如:
predict_y = knn.predict(X_test)
了解了如何在 sklearn 中使用 KNN 后,我们再通过两个例子,来加深理解。
本节所有数据集
https://github.com/zhouwei713/DataAnalyse/tree/master/KNN
先导入数据集,查看数据集整体概况
- import pandas as pd
- df = pd.read_csv("movie_dataset.txt")
- print(df.info())
- print(df.head())
- >>>
- <class 'pandas.core.frame.DataFrame'>
- RangeIndex: 31 entries, 0 to 30
- Data columns (total 4 columns):
- movie_name 31 non-null object
- fighting_lens 31 non-null int64
- kissing_lens 31 non-null int64
- movie_types 31 non-null object
- dtypes: int64(2), object(2)
- memory usage: 808.0+ bytes
- None
- movie_name fighting_lens kissing_lens movie_types
- 0 龙争虎斗 101 0 动作
- 1 蜀山 120 2 动作
- 2 致青春 3 30 爱情
- 3 这个杀手不太冷 78 2 动作
- 4 白蛇 20 50 爱情
数据集中总共有31条数据,且并不存在缺失值。
下面开始对数据集做一些转换
电影类型是文字描述的,这不利于我们判别分类,可以将“动作”替换为0,“爱情”替换为1。
- def trans(x):
- if x == '动作':
- x = 0
- else:
- x = 1
- return x
- df['movie_types'] = df['movie_types'].apply(trans)
- print(df.head())
- >>>
- movie_name fighting_lens kissing_lens movie_types
- 0 龙争虎斗 101 0 0
- 1 蜀山 120 2 0
- 2 致青春 3 30 1
- 3 这个杀手不太冷 78 2 0
- 4 白蛇 20 50 1
这里也可以直接使用 lambda 函数做转换,一行代码搞定
df['movie_types'] = df['movie_types'].apply(lambda x: 0 if x == '动作' else 1)
- feature = df[['fighting_lens', 'kissing_lens']].values
- label = df['movie_types'].values
- X_train, X_test, y_train, y_test = train_test_split(feature, label, random_state=2002) # 划分训练集和测试集
- knn = KNeighborsClassifier() # 创建 KNN 分类器
- knn.fit(X_train, y_train)
- predict_y = knn.predict(X_test)
- print("KNN 准确率", accuracy_score(y_test, predict_y))
- >>>
- KNN 准确率 0.75
发现准确率并不是很高,说明默认的 K 值取5可能不是最优解
再通过散点图来直观的查看下,各个类别的分布情况
- import seaborn as sns
- import matplotlib.pyplot as plt
- sns.scatterplot(x='fighting_lens', y='kissing_lens', hue='movie_types', data=df)
- plt.show()
原来是有两个并不太正常的点,看起来更加接近动作类别,但是实际上却是爱情类别的电影。
接下来使用交叉验证来寻找最优的 K 值
- X_train_new = X_train[:18]
- X_train_validation = X_train[18:]
- for k in range(1, 15, 2):
- knn = KNeighborsClassifier(n_neighbors=k)
- knn.fit(X_train, y_train)
- predict_y = knn.predict(X_test)
- print("K为%s的准确率" % k, accuracy_score(y_test, predict_y))
- >>>
- K为1的准确率 0.75
- K为3的准确率 0.75
- K为5的准确率 0.75
- K为7的准确率 0.875
- K为9的准确率 0.875
- K为11的准确率 0.875
- K为13的准确率 0.875
可以看到,由于本数据集较小,K 取不同值时准确率变化的比较奇怪。不过还是可以得出,当 K 值取7时,基本已经是最优的 K 值了。
使用 sklearn 自带的手写数字数据集,它包括了1797幅数字图像,每幅图像大小是8*8像素。
首先还是导入数据集,并查看数据
- from sklearn import datasets
- import matplotlib.pyplot as plt# 加载数据
- digits = datasets.load_digits()
- data = digits.data
- # 查看整体数据
- print(data.shape)
- # 查看第一幅图像
- print(digits.images[0])
- # 第一幅图像代表的数字含义
- print(digits.target[0])
- # 将第一幅图像显示出来
- plt.gray()
- plt.imshow(digits.images[0])
- plt.show()
- >>>
- (1797, 64)
- [[ 0. 0. 5. 13. 9. 1. 0. 0.]
- [ 0. 0. 13. 15. 10. 15. 5. 0.]
- [ 0. 3. 15. 2. 0. 11. 8. 0.]
- [ 0. 4. 12. 0. 0. 8. 8. 0.]
- [ 0. 5. 8. 0. 0. 9. 8. 0.]
- [ 0. 4. 11. 0. 1. 12. 7. 0.]
- [ 0. 2. 14. 5. 10. 12. 0. 0.]
- [ 0. 0. 6. 13. 10. 0. 0. 0.]]
- 0
我们把第一幅图像以图片的形式展示了出来,可以依稀的看出是一个0,同时该分类的标注(target)也是0。
在正式处理数据之前,我们先来看一个概念-数据规范化
那么什么是数据规范化呢
数据规范化是数据挖掘的一项基本工作,之所以称之为基本,是因为不同评价指标往往具有不同的量纲,数值间的差别可能很大,不进行处理可能会影响到数据分析的结果。为了消除指标之间的量纲和取值范围差异的影响,需要进行标准化处理,将数据按照比例进行缩放,使之落入一个特定的区域,便于进行综合分析。同时数据规范化对于基于距离的算法尤为重要。
数据规范化又分为如下几种
Min-max 规范化方法是将原始数据变换到[0,1]的空间中,也成为离散标准化。其公式为:
新数值 = (原数值 – 极小值)/ (极大值 – 极小值)
离散标准化保留了原来数据中存在的关系,是消除量纲和数据取值范围影响的最简单方法。这种处理方法的缺点是若数值集中且某个数值很大,则规范化后各值接近于0,并且将会相差不大。
也称标准差标准化,经过处理的数据的均值为0,标准差为1。转化公式为:
新数值 = (原数值 – 均值)/ 标准差
该种标准化方式是当前使用最多的规范化方法。
就是通过移动小数点的位置来进行规范化,小数点移动多少位取决于属性 A 的取值中的最大绝对值。
由于我们使用的 KNN 算法正是基于距离的,所以要做数值规范化,可以采用 Z-Score 规范化
- train_x, test_x, train_y, test_y = train_test_split(data, digits.target, random_state=2002)
- # 采用 Z-Score 规范化
- ss = preprocessing.StandardScaler()
- train_ss_x = ss.fit_transform(train_x)
- test_ss_x = ss.transform(test_x)
这里要注意,对于训练数据,是使用 fit_transform 函数来转换,而对于测试数据,则是使用 transform 函数来转换。两者的区别为,fit_transform 相当于是 fit + transform,训练数据作为一个基准数据集,先进行 fit,然后再把 fit (拟合)过的数据分别应用到训练数据和测试数据上,进行 transform 操作。
接下来就是我们已经学习过的知识,创建 KNN 分类器,并验证模型准确率
- knn = KNeighborsClassifier()
- knn.fit(train_ss_x, train_y)
- predict_y = knn.predict(test_ss_x)
- print("KNN 准确率: %.4lf" % accuracy_score(test_y, predict_y))
- >>>
- KNN 准确率: 0.9689
可以看到,使用默认的 K 为5的情况下,准确率还是很好的。
在上一节中,我也说过,KNN 不仅可以解决分类问题,同样也可以解决回归问题,下来我们就先来看看什么是分类与回归。
其实回归问题和分类问题的本质一样,都是针对一个输入做出一个输出预测,其区别在于输出变量的类型。
分类:给定一个新的模式,根据训练集推断它所对应的类别(如:+1,-1),是一种定性输出,也叫离散变量预测。
回归:给定一个新的模式,根据训练集推断它所对应的输出值(实数)是多少,是一种定量输出,也叫连续变量预测。
举个例子:预测明天的气温是多少度,这是一个回归任务;预测明天是阴、晴还是雨,就是一个分类任务。
预测价格,很明显是一系列连续的变量,所以这个问题就属于回归问题。
先来看下数据
- import pandas as pd
- import matplotlib.pyplot as plt
- import numpy as np
- import seaborn as sns
- df = pd.read_csv('knn-regression.csv')
- print(df)
Brand:车子的品牌
Type:是车子的类别
Color:车子的颜色
Construction Year:车子生产时间
Odometer:车子行驶的里程
Ask Price:价格,也就是我们需要预测的值
Days Until MOT 和 HP:都是未知的数据列
对于 type 这一列,虽然它是数值型,但是1.0,1.1等都是代表的一种类别,所以我们可以采用独热编码的方式,把该列数据转换一下。如果你不记得独热编码了,可以到前面“数据清洗”一节回顾下。
对于 color 这一列,由于它的数值是 green,red 等字符,也需要采用独热编码,转换成0,1类型数据。
独热编码 Type 和 Color 列
- df_new = pd.get_dummies(df, columns=['Type'])
- df_new = pd.get_dummies(df_new, columns=['Color'])
- print(df_new)
- >>>
- Brand Construction Year Odometer Ask Price Days Until MOT HP \
- 0 Peugeot 106 2002 166879 999 138 60
- 1 Peugeot 106 1998 234484 999 346 60
- 2 Peugeot 106 1997 219752 500 -5 60
- 3 Peugeot 106 2001 223692 750 -87 60
- 4 Peugeot 106 2002 120275 1650 356 59
- 5 Peugeot 106 2003 131358 1399 266 60
- 6 Peugeot 106 1999 304277 799 173 57
- 7 Peugeot 106 1998 93685 1300 0 75
- 8 Peugeot 106 2002 225935 950 113 60
- 9 Peugeot 106 1997 252319 650 133 75
- 10 Peugeot 106 1998 220000 700 82 50
- 11 Peugeot 106 1997 212000 700 75 60
- 12 Peugeot 106 2003 255134 799 197 60 Type_1.0 Type_1.1 Type_1.4 Color_black Color_blue Color_green \
- 0 1 0 0 0 1 0
- 1 1 0 0 0 1 0
- 2 0 1 0 1 0 0
- 3 0 1 0 0 0 0
- 4 0 1 0 0 0 0
- 5 0 1 0 0 0 0
- 6 0 1 0 0 0 1
- 7 0 0 1 0 0 1
- 8 0 1 0 0 0 0
- 9 0 0 1 0 0 1
- 10 1 0 0 1 0 0
- 11 0 1 0 1 0 0
- 12 0 1 0 1 0 0 Color_grey Color_red Color_white
- 0 0 0 0
- 1 0 0 0
- 2 0 0 0
- 3 0 1 0
- 4 1 0 0
- 5 0 1 0
- 6 0 0 0
- 7 0 0 0
- 8 0 0 1
- 9 0 0 0
- 10 0 0 0
- 11 0 0 0
- 12 0 0 0
可以看到,原来的 Type 和 Color 两列都没有了,取而代之的是对应 Type_1.0 和 Color_black 等列
我们可以通过函数 value_counts() 来查看每一列值的分布情况
- for col in df.columns:
- print(df[col].value_counts())
- >>>
- Peugeot 106 13
- Name: Brand, dtype: int64
- 1.1 8
- 1.0 3
- 1.4 2
- Name: Type, dtype: int64
- black 4
- green 3
- red 2
- blue 2
- grey 1
- white 1
- Name: Color, dtype: int64
- 1998 3
- 1997 3
- 2002 3
- 2003 2
- 1999 1
- 2001 1
- Name: Construction Year, dtype: int64
- 252319 1
- 131358 1
- 304277 1
- 234484 1
- 120275 1
- 225935 1
- 220000 1
- 93685 1
- 219752 1
- 223692 1
- 166879 1
- 255134 1
- 212000 1
- Name: Odometer, dtype: int64
- 799 2
- 700 2
- 999 2
- 750 1
- 650 1
- 1300 1
- 950 1
- 1399 1
- 500 1
- 1650 1
- Name: Ask Price, dtype: int64
- 133 1
- 266 1
- 346 1
- -87 1
- 82 1
- 113 1
- 173 1
- 75 1
- 138 1
- 197 1
- -5 1
- 356 1
- 0 1
- Name: Days Until MOT, dtype: int64
- 60 8
- 75 2
- 59 1
- 57 1
- 50 1
- Name: HP, dtype: int64
对于第一列 Brand,它只有一个值,也就是说在所有的数据中,该列都是相同的,即对我们的预测是不会产生任何影响,可以删除
df_new.drop(['Brand'], axis=1, inplace=True)
- matrix = df_new.corr()
- plt.subplots(figsize=(8, 6))
- sns.heatmap(matrix, square=True)
- plt.show()
corr 函数是用来计算任意两个变量之间的关联系数的,计算出关联系数后,使用 seaborn 的 heatmap 制作热力图。
在该热力图中,颜色越浅,代表变量之间关联性越强。所以在对角线方向上,由于都是同一个变量之间的关联系数,所以都是1,颜色最浅。
同时还能看出,与 Ask Price 关联性最强的几个变量为 Construction Year,Days Until MOT 和 Color_grey 三个变量,即我们可以选取这三个变量为训练特征,来训练模型。
导入相关包
- from sklearn.neighbors import KNeighborsRegressor
- from sklearn.model_selection import train_test_split
- from sklearn import preprocessing
- from sklearn.preprocessing import StandardScaler
- import numpy as np
还记得,在使用 KNN 做分类时,导入的库为 KNeighborsClassifier,现在做回归,使用的库为 KNeighborsRegressor。
选择特征和预测值
- X = df_new[['Construction Year', 'Days Until MOT', 'Color_grey']]
- y = df_new['Ask Price'].values.reshape(-1, 1)
- X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=33)
只选取了关联性比较强的三个特征
reshape 是用来转换数据为指定行列数矩阵的函数,比如 reshape(2, 3)就是转换为2行3列矩阵。那么(-1, 1)是用来干嘛的呢,就是说,需要把数据转换为1列,但是多少行是不确定的,就用-1来占用。
所以我们看一下 y,应该是一个拥有1列,n 行的矩阵数据
- print(y)
- >>>
- [[ 999]
- [ 999]
- [ 500]
- [ 750]
- [1650]
- [1399]
- [ 799]
- [1300]
- [ 950]
- [ 650]
- [ 700]
- [ 700]
- [ 799]]
数据规范化
仍然使用 Z-Score 规范化来规范数据
- ss = StandardScaler()
- X_train_ss = ss.fit_transform(X_train)
- X_test_ss = ss.transform(X_test)
模型训练及预测
- knn = KNeighborsRegressor(n_neighbors=2)
- knn.fit(X_train_ss, y_train)
- y_pred = knn.predict(X_test_ss)
由于预测的结果是一些连续的数值,我们可以采用可视化的方式来查看预测情况
用散点图展示预测值与实际值,再辅以中心线
- plt.scatter(y_pred, y_test)
- plt.xlabel("Prediction")
- plt.ylabel("Real Value")
- diag = np.linspace(500, 1200, 100)
- plt.plot(diag, diag, '-r')
- plt.show()
如果我们的预测是完全准确的话,图中的所有点都会落到这条直线上,而点偏离直线的距离越大,说明预测结果与实际值偏差越大。
从预测的结果能够看出,由于我们数据集比较小,且强关联的变量过少,所以导致预测的结果并不是十分理想。如果后期能够增加数据量和关联特征数量,那么预测结果准确率也会随着大大增加。
文中的完整代码,可以在这里下载
https://github.com/zhouwei713/DataAnalyse/tree/master/KNN
今天带你完成了三个项目的实战,分别用 KNN 算法实现了两个分类和一个回归问题的处理。在这个过程中,你应该对数据探索,数据可视化,数据规范化等技能有 了一定的体会。
同时你应该也有注意到,我们在拿到一个问题时,并不要急于训练模型,而是要全面的了解数据,并做好充分的数据处理,这样在后面的模型训练时,才会事半功倍。
能否使用其他的 K 值,比如100,重新跑下手写数字的案例,看看分类器的准确率是多少?
- ◆ ◆ ◆ ◆ ◆
- 麟哥新书已经在京东上架了,我写了本书:《拿下Offer-数据分析师求职面试指南》,目前京东正在举行100-40活动,大家可以用相当于原价5折的预购价格购买,还是非常划算的:
-
- 数据森麟公众号的交流群已经建立,许多小伙伴已经加入其中,感谢大家的支持。大家可以在群里交流关于数据分析&数据挖掘的相关内容,还没有加入的小伙伴可以扫描下方管理员二维码,进群前一定要关注公众号奥,关注后让管理员帮忙拉进群,期待大家的加入。
-
- 管理员二维码:
-
-
- 猜你喜欢
-
- ● 麟哥拼了!!!亲自出镜推荐自己新书《数据分析师求职面试指南》● 厉害了!麟哥新书登顶京东销量排行榜!● 笑死人不偿命的知乎沙雕问题排行榜
- ● 用Python扒出B站那些“惊为天人”的阿婆主!● 你相信逛B站也能学编程吗
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。