作者 | Japson
来源 | 木东居士
在上一篇文章《机器学习的敲门砖:kNN算法(中)》中,我们借助kNN分类算法,学习了如下知识点:
但是在前面的实验中,我们都忽略了相当关键的一步,数据归一化。本篇文章,我们可以学习数据归一化对算法的影响及其实现。最后,作为kNN算法的收尾,我们会总结算法的优缺点以及优化思路。
1.1 为什么要数据归一化
在实际应用中,样本的不同特征的单位不同,会在求距离时造成很大的影响。比如:在两个样本中肿瘤大小的分别为1cm和5cm,发现时间分别为100天和200天,那么在求距离时,时间差为100、大小差为4,那么其结果会被时间所主导,因为肿瘤大小的差距太小了。但是如果我们把时间用年做单位,0.27年与0.55年的差距又远小于肿瘤大小的差距,结果又会被大小主导了。
我们发现,在量纲不同的情况下,以上的情况,不能反映样本中每一个特征的重要程度。这就需要数据归一化了。
一般来说,我们的解决方案是:把所有的数据都映射到同一个尺度(量纲)上。
一般来说,常用的数据归一化有两种:
x_{scale} = \frac {x - x_{min}} {x_{max} - x_{min}}
x_{scale} = \frac {x - x_{mean}} {S}
1.2 最值归一化实现
为了了解最值归一化的代码实现,我们可以创建100个随机数,然后对其进行最值归一化。
import numpy as np# 创建100个随机数x = np.random.randint(0,100,size=100)# 最值归一化(向量)# 最值归一化公式,映射到0,1之间(x - np.min(x)) / (np.max(x) - np.min(x))# 最值归一化(矩阵)# 0~100范围内的50*2的矩阵X = np.random.randint(0,100,(50,2))# 将矩阵改为浮点型X = np.array(X, dtype=float)# 最值归一化公式,对于每一个维度(列方向)进行归一化。# X[:,0]第一列,第一个特征X[:,0] = (X[:,0] - np.min(X[:,0])) / (np.max(X[:,0]) - np.min(X[:,0]))# X[:,1]第二列,第二个特征X[:,1] = (X[:,1] - np.min(X[:,1])) / (np.max(X[:,1]) - np.min(X[:,1]))# 如果有n个特征,可以写个循环:for i in range(0,2): X[:,i] = (X[:,i]-np.min(X[:,i])) / (np.max(X[:,i] - np.min(X[:,i])))
下面我们可以简单地绘制样本,并使用np.mean()/np.std()来计算其均值和方差
import matplotlib.pyplot as plt# 简单绘制样本,看横纵坐标plt.scatter(X[:,0],X[:,1]) plt.show()
1.3 均值方差归一化实现
同样地,为了了解均值方差归一化的代码实现,我们可以创建100个随机数,然后对其进行均值方差归一化。
X2 = np.array(np.random.randint(0,100,(50,2)),dtype=float)# 套用公式,对每一列做均值方差归一化for i in range(0,2): X2[:,i]=(X2[:,i]-np.mean(X2[:,i])) / np.std(X2[:,i])
下面我们可以简单地绘制样本
plt.scatter(X2[:,0],X2[:,1]) plt.show()
计算其均值/方差
np.mean(X2[:,0]) np.std(X2[:,1])
1.4 Sklearn中的归一化
首先我们来看一个在实际使用归一化时的一个小陷阱。
我们在建模时要将数据集划分为训练数据集&测试数据集。
训练数据集进行归一化处理,需要计算出训练数据集的均值mean_train和方差std_train。
问题是:我们在对测试数据集进行归一化时,要计算测试数据的均值和方差么?
答案是否定的。在对测试数据集进行归一化时,仍然要使用训练数据集的均值train_mean和方差std_train。这是因为测试数据是模拟的真实环境,真实环境中可能无法得到均值和方差,对数据进行归一化。只能够使用公式(x_test - mean_train) / std_train
并且,数据归一化也是算法的一部分,针对后面所有的数据,也应该做同样的处理.
因此我们要保存训练数据集中得到的均值和方差。
在sklearn中专门的用来数据归一化的方法:StandardScaler。
下面我们加载鸢尾花数据集
import numpy as npfrom sklearn import datasetsfrom sklearn.model_selection import train_test_split iris = datasets.load_iris() X = iris.data y = iris.target X_train,X_test,y_train,y_test = train_test_split(iris.data,iris.target,test_size=0.2,random_state=666)
使用数据归一化的方法:
from sklearn.preprocessing import StandardScaler standardScaler = StandardScaler()# 归一化的过程跟训练模型一样standardScaler.fit(X_train) standardScaler.mean_ standardScaler.scale_ # 表述数据分布范围的变量,替代std_# 使用transformX_train_standard = standardScaler.transform(X_train) X_test_standard = standardScaler.transform(X_test)
如此就能输出归一化后的数据了。
1.5 自己实现均值方差归一化
同样地,我们仿照sklearn的风格,可以自己实现一下均值方差归一化的方法。
我们在之前的工程中创建processing.py:
import numpy as npclass StandardScaler: def __init__(self): self.mean_ = None self.scale_ = None def fit(self, X): """根据训练数据集X获得数据的均值和方差""" assert X.ndim == 2, "The dimension of X must be 2" # 求出每个列的均值 self.mean_ = np.array([np.mean(X[:,i] for i in range(X.shape[1]))]) self.scale_ = np.array([np.std(X[:, i] for i in range(X.shape[1]))]) return self def tranform(self, X): """将X根据StandardScaler进行均值方差归一化处理""" assert X.ndim == 2, "The dimension of X must be 2" assert self.mean_ is not None and self.scale_ is not None, \ "must fit before transform" assert X.shape[1] == len(self.mean_), \ "the feature number of X must be equal to mean_ and std_" # 创建一个空的浮点型矩阵,大小和X相同 resX = np.empty(shape=X.shape, dtype=float) # 对于每一列(维度)都计算 for col in range(X.shape[1]): resX[:,col] = (X[:,col] - self.mean_[col]) / self.scale_[col] return resX
KNN的主要优点有:
KNN的主要缺点有:
大家感觉一万维貌似很多,但实际上就是100*100像素的黑白灰图片。
以上就是关于kNN算法的总结。
你是不是以为这一篇就两节内容就结束了?没想到吧!下面还有一波干货:kNN优化之KD树。
K近邻法的重要步骤是对所有的实例点进行快速k近邻搜索。如果采用线性扫描(linear scan),要计算输入点与每一个点的距离,时间复杂度非常高。因此在查询操作是,使用kd树。
3.1 kd树的原理
kd树是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构,且kd树是一种二叉树,表示对k维空间的一个划分。
k-d tree是每个节点均为k维样本点的二叉树,其上的每个样本点代表一个超平面,该超平面垂直于当前划分维度的坐标轴,并在该维度上将空间划分为两部分,一部分在其左子树,另一部分在其右子树。即若当前节点的划分维度为d,其左子树上所有点在d维的坐标值均小于当前值,右子树上所有点在d维的坐标值均大于等于当前值,本定义对其任意子节点均成立。
3.2 kd树的构建
常规的k-d tree的构建过程为:
对于构建过程,有两个优化点:
例子:
采用常规的构建方式,以二维平面点(x,y)的集合(2,3),(5,4),(9,6),(4,7),(8,1),(7,2) 为例结合下图来说明k-d tree的构建过程:
上述的构建过程结合下图可以看出,构建一个k-d tree即是将一个二维平面逐步划分的过程。
需要注意的是,对于每次切分,都是循环顺序选择维度的,二维是:x->y->x…;三维则是:x->y->z->x…。
下面从三维空间来看一下k-d tree的构建及空间划分过程。首先,边框为红色的竖直平面将整个空间划分为两部分,此两部分又分别被边框为绿色的水平平面划分为上下两部分。最后此4个子空间又分别被边框为蓝色的竖直平面分割为两部分,变为8个子空间,此8个子空间即为叶子节点。
# points为实例点集合,depth深度,为用来确定取维度的参数def kd_tree(points, depth): if 0 == len(points): return None # 指定切分维度,len(points[0])是数据的实际维度,这样计算可以保证循环 cutting_dim = depth % len(points[0]) # 切分点初始化 medium_index = len(points) # 对所有的实例点按照指定维度进行排序,itemgetter用于获取对象哪些维度上的数据,参数为需要获取的数据在对象中的序号 points.sort(key=itemgetter(cutting_dim)) # 将该维度的中值点作为根节点 node = Node(points[medium_index]) # 对于左子树,重复构建(depth+1) node.left = kd_tree(points[:medium_index], depth + 1) # 对于右子树,重复构建(depth+1) node.right = kd_tree(points[medium_index + 1:], depth + 1) return node
3.3 kd树的检索
kd树的检索是KNN算法至关重要的一步,给定点p,查询数据集中与其距离最近点的过程即为最近邻搜索。
如在构建好的k-d tree上搜索(3,5)的最近邻时,对二维空间的最近邻搜索过程作分析。首先从根节点(7,2)出发,将当前最近邻设为(7,2),对该k-d tree作深度优先遍历。以(3,5)为圆心,其到(7,2)的距离为半径画圆(多维空间为超球面),可以看出(8,1)右侧的区域与该圆不相交,所以(8,1)的右子树全部忽略。接着走到(7,2)左子树根节点(5,4),与原最近邻对比距离后,更新当前最近邻为(5,4)。以(3,5)为圆心,其到(5,4)的距离为半径画圆,发现(7,2)右侧的区域与该圆不相交,忽略该侧所有节点,这样(7,2)的整个右子树被标记为已忽略。遍历完(5,4)的左右叶子节点,发现与当前最优距离相等,不更新最近邻。所以(3,5)的最近邻为(5,4)。
3.4 sklearn中的KDTree
Sklearn中有KDTree的实现,仅构建了一个二维空间的k-d tree,然后对其作k近邻搜索及指定半径的范围搜索。多维空间的检索,调用方式与此例相差无多。
import numpy as npfrom matplotlib import pyplot as pltfrom matplotlib.patches import Circlefrom sklearn.neighbors import KDTree np.random.seed(0) points = np.random.random((100, 2)) tree = KDTree(points) point = points[0]# kNNdists, indices = tree.query([point], k=3) print(dists, indices)# query radiusindices = tree.query_radius([point], r=0.2) print(indices) fig = plt.figure() ax = fig.add_subplot(111, aspect='equal') ax.add_patch(Circle(point, 0.2, color='r', fill=False)) X, Y = [p[0] for p in points], [p[1] for p in points] plt.scatter(X, Y) plt.scatter([point[0]], [point[1]], c='r') plt.show()
图像化展示:
到这里,我们kNN算法就算告一段落了。我们回顾一下:
在《机器学习的敲门砖:kNN算法(上)》中,我们了解了非常适合入门机器学习的算法:k近邻算法。
我们学习了kNN算法的流程,并且在jupyter notebook上手动实现了代码,并且在外部也进行了封装。最后我们学习了sklearn中的kNN算法。
由此我们引出了疑问:即如何评价模型的好坏。
在《机器学习的敲门砖:kNN算法(中)》中,我们使用训练数据集和测试数据集来判断模型的好坏,给出并实现accurcay这一分类问题常用指标,计算出accuracy分类精准度。最后我们再探寻超参数的选择对模型的影响。并使用网格搜索算法搜索出最佳超参数组。
在本篇中,我们学习了数据归一化对算法的影响及其实现。作为kNN算法系列的收尾,我们总结算法的优缺点。并在最后详细阐述了kNN优化算法之一的“KDTree”。
相信大家通过这三篇的学习,已经初步了解了机器学习中最简单朴素的算法。现在有很多机器学习的文章笔记,开篇都是玄之又玄的损失函数、梯度下降、L1正则L2正则云云,实属劝退最佳法宝。但是我们也发现,其实机器学习并没有想象中的那么抽象,我们也可以通过代码的方式来对其中的概念进行理解。
当然,此三篇文章,包括以后的系列文章,为本人的学习笔记,或称之为“集注”,是在各位老师前辈基础上总结归纳而来,拾人牙慧矣。因参考甚多,故不能一一标注出处,还请见谅。
数据分析咨询请扫描二维码
在准备数据分析师面试时,掌握高频考题及其解答是应对面试的关键。为了帮助大家轻松上岸,以下是10个高频考题及其详细解析,外加 ...
2024-12-20互联网数据分析师是一个热门且综合性的职业,他们通过数据挖掘和分析,为企业的业务决策和运营优化提供强有力的支持。尤其在如今 ...
2024-12-20在现代商业环境中,数据分析师是不可或缺的角色。他们的工作不仅仅是对数据进行深入分析,更是协助企业从复杂的数据信息中提炼出 ...
2024-12-20随着大数据时代的到来,数据驱动的决策方式开始受到越来越多企业的青睐。近年来,数据分析在人力资源管理中正在扮演着至关重要的 ...
2024-12-20在数据分析的世界里,表面上的技术操作只是“入门票”,而真正的高手则需要打破一些“看不见的墙”。这些“隐形天花板”限制了数 ...
2024-12-19在数据分析领域,尽管行业前景广阔、岗位需求旺盛,但实际的工作难度却远超很多人的想象。很多新手初入数据分析岗位时,常常被各 ...
2024-12-19入门数据分析,许多人都会感到“难”,但这“难”究竟难在哪儿?对于新手而言,往往不是技术不行,而是思维方式、业务理解和实践 ...
2024-12-19在如今的行业动荡背景下,数据分析师的职业前景虽然面临一些挑战,但也充满了许多新的机会。随着技术的不断发展和多领域需求的提 ...
2024-12-19在信息爆炸的时代,数据分析师如同探险家,在浩瀚的数据海洋中寻觅有价值的宝藏。这不仅需要技术上的过硬实力,还需要一种艺术家 ...
2024-12-19在当今信息化社会,大数据已成为各行各业不可或缺的宝贵资源。大数据专业应运而生,旨在培养具备扎实理论基础和实践能力,能够应 ...
2024-12-19阿里P8、P9失业都找不到工作?是我们孤陋寡闻还是世界真的已经“癫”成这样了? 案例一:本硕都是 985,所学的专业也是当红专业 ...
2024-12-19CDA持证人Louis CDA持证人基本情况 我大学是在一个二线城市的一所普通二本院校读的,专业是旅游管理,非计算机非统计学。毕业之 ...
2024-12-18最近,知乎上有个很火的话题:“一个人为何会陷入社会底层”? 有人说,这个世界上只有一个分水岭,就是“羊水”;还有人说,一 ...
2024-12-18在这个数据驱动的时代,数据分析师的技能需求快速增长。掌握适当的编程语言不仅能增强分析能力,还能帮助分析师从海量数据中提取 ...
2024-12-17在当今信息爆炸的时代,数据分析已经成为许多行业中不可或缺的一部分。想要在这个领域脱颖而出,除了热情和毅力外,你还需要掌握 ...
2024-12-17数据分析,是一项通过科学方法处理数据以获取洞察并支持决策的艺术。无论是在商业环境中提升业绩,还是在科研领域推动创新,数据 ...
2024-12-17在数据分析领域,图表是我们表达数据故事的重要工具。它们不仅让数据变得更加直观,也帮助我们更好地理解数据中的趋势和模式。相 ...
2024-12-16在当今社会,我们身处着一个飞速发展、变化迅猛的时代。不同行业在科技进步、市场需求和政策支持的推动下蓬勃发展,呈现出令人瞩 ...
2024-12-16在现代商业世界中,数据分析师扮演着至关重要的角色。他们通过解析海量数据,为企业战略决策提供有力支持。要有效完成这项任务, ...
2024-12-16在当今数据爆炸的时代,数据分析师是组织中不可或缺的导航者。他们通过从大量数据中提取可操作的洞察力,帮助企业在竞争激烈的市 ...
2024-12-16