数据分析入门系列教程-KNN实战

简介: 数据分析入门系列教程-KNN实战

上一节我们完成了 KNN 算法理论的学习,同时还手动写了一个简易版的 KNN 分类器。今天我们来进行 KNN 的实战,看看如何通过 KNN 算法来解决生活中的问题。

在实战之前,我们先来介绍一个概念-超参数。

还记得我们上一节讲到的选择 K 值吗,这里的 K 就是超参。

所谓超参数,就是在机器学习算法模型执行之前需要指定的参数。(调参调的就是超参数) 如KNN 算法中的 K。

与之相对的概念是模型参数,即算法过程中学习的属于这个模型的参数(KNN 中没有模型参数,回归算法有很多模型参数)

如何选择超参数,是机器学习中的永恒问题。调参的方法也很多,例如上节我们提到的交叉验证法。

下面我们就进入实战例子,看看如何用 KNN 算法来解决生活中的问题。

在 sklearn 中使用 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 和 predict 函数

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-Mix 规范化

Min-max 规范化方法是将原始数据变换到[0,1]的空间中,也成为离散标准化。其公式为:

新数值 = (原数值 – 极小值)/ (极大值 – 极小值)

离散标准化保留了原来数据中存在的关系,是消除量纲和数据取值范围影响的最简单方法。这种处理方法的缺点是若数值集中且某个数值很大,则规范化后各值接近于0,并且将会相差不大。

Z-Score 规范化

也称标准差标准化,经过处理的数据的均值为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,重新跑下手写数字的案例,看看分类器的准确率是多少?

相关文章
|
21天前
|
机器学习/深度学习 数据可视化 数据挖掘
使用Python进行数据分析的入门指南
本文将引导读者了解如何使用Python进行数据分析,从安装必要的库到执行基础的数据操作和可视化。通过本文的学习,你将能够开始自己的数据分析之旅,并掌握如何利用Python来揭示数据背后的故事。
|
1月前
|
机器学习/深度学习 数据可视化 数据挖掘
使用Python进行数据分析的入门指南
【10月更文挑战第42天】本文是一篇技术性文章,旨在为初学者提供一份关于如何使用Python进行数据分析的入门指南。我们将从安装必要的工具开始,然后逐步介绍如何导入数据、处理数据、进行数据可视化以及建立预测模型。本文的目标是帮助读者理解数据分析的基本步骤和方法,并通过实际的代码示例来加深理解。
49 3
|
1月前
|
消息中间件 数据挖掘 Kafka
Apache Kafka流处理实战:构建实时数据分析应用
【10月更文挑战第24天】在当今这个数据爆炸的时代,能够快速准确地处理实时数据变得尤为重要。无论是金融交易监控、网络行为分析还是物联网设备的数据收集,实时数据处理技术都是不可或缺的一部分。Apache Kafka作为一款高性能的消息队列系统,不仅支持传统的消息传递模式,还提供了强大的流处理能力,能够帮助开发者构建高效、可扩展的实时数据分析应用。
83 5
|
23天前
|
数据可视化 数据挖掘
R中单细胞RNA-seq数据分析教程 (3)
R中单细胞RNA-seq数据分析教程 (3)
29 3
R中单细胞RNA-seq数据分析教程 (3)
|
1月前
|
SQL 数据挖掘 Python
R中单细胞RNA-seq数据分析教程 (1)
R中单细胞RNA-seq数据分析教程 (1)
37 5
R中单细胞RNA-seq数据分析教程 (1)
|
29天前
|
机器学习/深度学习 数据挖掘
R中单细胞RNA-seq数据分析教程 (2)
R中单细胞RNA-seq数据分析教程 (2)
46 0
R中单细胞RNA-seq数据分析教程 (2)
|
1月前
|
数据采集 数据可视化 数据挖掘
深入浅出:使用Python进行数据分析的基础教程
【10月更文挑战第41天】本文旨在为初学者提供一个关于如何使用Python语言进行数据分析的入门指南。我们将通过实际案例,了解数据处理的基本步骤,包括数据的导入、清洗、处理、分析和可视化。文章将用浅显易懂的语言,带领读者一步步掌握数据分析师的基本功,并在文末附上完整的代码示例供参考和实践。
|
1月前
|
并行计算 数据挖掘 大数据
Python数据分析实战:利用Pandas处理大数据集
Python数据分析实战:利用Pandas处理大数据集
|
2月前
|
数据采集 机器学习/深度学习 数据可视化
深入浅出:用Python进行数据分析的入门指南
【10月更文挑战第21天】 在信息爆炸的时代,掌握数据分析技能就像拥有一把钥匙,能够解锁隐藏在庞大数据集背后的秘密。本文将引导你通过Python语言,学习如何从零开始进行数据分析。我们将一起探索数据的收集、处理、分析和可视化等步骤,并最终学会如何利用数据讲故事。无论你是编程新手还是希望提升数据分析能力的专业人士,这篇文章都将为你提供一条清晰的学习路径。
|
2月前
|
数据挖掘 索引 Python
Python数据分析篇--NumPy--入门
Python数据分析篇--NumPy--入门
40 0
下一篇
DataWorks