数据分析入门系列教程-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()

image.png

原来是有两个并不太正常的点,看起来更加接近动作类别,但是实际上却是爱情类别的电影。


交叉验证

接下来使用交叉验证来寻找最优的 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


image.png

我们把第一幅图像以图片的形式展示了出来,可以依稀的看出是一个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)


image.png

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 制作热力图。


image.png

在该热力图中,颜色越浅,代表变量之间关联性越强。所以在对角线方向上,由于都是同一个变量之间的关联系数,所以都是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()

image.png

如果我们的预测是完全准确的话,图中的所有点都会落到这条直线上,而点偏离直线的距离越大,说明预测结果与实际值偏差越大。

从预测的结果能够看出,由于我们数据集比较小,且强关联的变量过少,所以导致预测的结果并不是十分理想。如果后期能够增加数据量和关联特征数量,那么预测结果准确率也会随着大大增加。

文中的完整代码,可以在这里下载

https://github.com/zhouwei713/DataAnalyse/tree/master/KNN


总结


今天带你完成了三个项目的实战,分别用 KNN 算法实现了两个分类和一个回归问题的处理。在这个过程中,你应该对数据探索,数据可视化,数据规范化等技能有 了一定的体会。

同时你应该也有注意到,我们在拿到一个问题时,并不要急于训练模型,而是要全面的了解数据,并做好充分的数据处理,这样在后面的模型训练时,才会事半功倍。

image.png

能否使用其他的 K 值,比如100,重新跑下手写数字的案例,看看分类器的准确率是多少?

相关文章
|
2月前
|
自然语言处理 小程序 数据挖掘
数据分析实战-Python实现博客评论数据的情感分析
数据分析实战-Python实现博客评论数据的情感分析
146 0
|
3天前
|
数据采集 数据可视化 数据挖掘
Python 与 PySpark数据分析实战指南:解锁数据洞见
Python 与 PySpark数据分析实战指南:解锁数据洞见
|
5天前
|
SQL 数据采集 存储
Hive实战 —— 电商数据分析(全流程详解 真实数据)
关于基于小型数据的Hive数仓构建实战,目的是通过分析某零售企业的门店数据来进行业务洞察。内容涵盖了数据清洗、数据分析和Hive表的创建。项目需求包括客户画像、消费统计、资源利用率、特征人群定位和数据可视化。数据源包括Customer、Transaction、Store和Review四张表,涉及多个维度的聚合和分析,如按性别、国家统计客户、按时间段计算总收入等。项目执行需先下载数据和配置Zeppelin环境,然后通过Hive进行数据清洗、建表和分析。在建表过程中,涉及ODS、DWD、DWT、DWS和DM五层,每层都有其特定的任务和粒度。最后,通过Hive SQL进行各种业务指标的计算和分析。
23 1
Hive实战 —— 电商数据分析(全流程详解 真实数据)
|
12天前
|
SQL 人工智能 自然语言处理
让老板成为数据分析师--ChatGpt链接本地数据源实战测试
本文探究ChatGpt等AI机器人能否帮助老板快速的做数据分析?用自然语言同老板进行沟通,满足老板的所有数据分析的诉求?
|
25天前
|
供应链 搜索推荐 数据挖掘
Pandas实战案例:电商数据分析的实践与挑战
【4月更文挑战第16天】本文通过一个电商数据分析案例展示了Pandas在处理销售数据、用户行为分析及商品销售趋势预测中的应用。在数据准备与清洗阶段,Pandas用于处理缺失值、重复值。接着,通过用户购买行为和商品销售趋势分析,构建用户画像并预测销售趋势。实践中遇到的大数据量和数据多样性挑战,通过分布式计算和数据标准化解决。未来将继续深入研究Pandas与其他先进技术的结合,提升决策支持能力。
|
25天前
|
存储 数据可视化 数据挖掘
实战案例:Pandas在金融数据分析中的应用
【4月更文挑战第16天】本文通过实例展示了Pandas在金融数据分析中的应用。案例中,一家投资机构使用Pandas加载、清洗股票历史价格数据,删除无关列并重命名,将日期设为索引。接着,数据被可视化以观察价格走势,进行基本统计分析了解价格分布,以及计算移动平均线来平滑波动。Pandas的便捷功能在金融数据分析中体现出高效率和实用性。
|
1月前
|
机器学习/深度学习 数据可视化 数据挖掘
利用Python进行数据分析与可视化:从入门到精通
本文将介绍如何使用Python语言进行数据分析与可视化,从基础概念到高级技巧一应俱全。通过学习本文,读者将掌握Python在数据处理、分析和可视化方面的核心技能,为实际项目应用打下坚实基础。
|
18天前
|
机器学习/深度学习 数据可视化 数据挖掘
Python跳水:探索数据分析的深渊
Python跳水:探索数据分析的深渊
22 0
|
13天前
|
机器学习/深度学习 数据采集 算法
Python用逻辑回归、决策树、SVM、XGBoost 算法机器学习预测用户信贷行为数据分析报告
Python用逻辑回归、决策树、SVM、XGBoost 算法机器学习预测用户信贷行为数据分析报告
|
2天前
|
机器学习/深度学习 数据可视化 算法
使用Python进行数据分析的5个必备技巧
【5月更文挑战第9天】本文介绍了Python数据分析的五个关键技巧:1) 使用Pandas进行数据处理和清洗;2) 利用NumPy进行高效数值计算;3) 通过Matplotlib和Seaborn创建可视化图表;4) 使用Scikit-learn执行机器学习任务;5) 在Jupyter Notebook中进行交互式分析和文档分享。这些技巧能提升数据分析的效率和准确性。