1. 数据归一化
1.1 医疗事故?——之前的kNN算法哪里出了问题?
在之前讲kNN算法时我们举过的肿瘤的例子中,有一个问题,也许很多读者没有考虑过。
回顾一下,kNN算法的第一步是求最为邻近的k个点,也就是要先求每个数据点与待预测的数据点的距离。我们仍然以p=2的明可夫斯基距离(欧拉距离)为例。肿瘤的实例中,数据点的两个坐标值是发现时间和肿瘤大小,我们所要求的其实就是这样一个表达式的值并进行大小比较。
为了后续表达简单,我们将上式简写如下:
好了,新的病人来了,做个检查吧。
哔~~~
- 肿瘤直径:140mm
- 发现时间:0.8年
嗯,是时候检验一下我们kNN算法的功力了。简单点,我们假设原本的数据点只有2个,k=1。来看一下原本的两个数据点:
- 肿瘤1
- 肿瘤直径:150mm
- 发现时间:1年
- 肿瘤2
- 肿瘤直径:139mm
- 发现时间:5年
好吧,你聪明的,告诉我,你选1还是选2?
虽然我不懂医学,数据也都是我编的,我也不知道这样的直径和时间是否合理。但是,同样不懂医学的你,我相信和我一样,肯定选1嘛。
肿瘤1和这个新肿瘤差了两个多月,长大了10个毫米,讲道理应该已经十分相似咯。肿瘤2多长了4个年头还不如这新肿瘤大,肯定不能选嘛。
好吧,姑且认为你和我达成了共识,anyway,我们亲手打造的kNN算法不这么觉得。
算距离嘛,我们也会。我们来看看kNN算法会发生什么。
纳尼?D2更近?
可别乱来,要出医疗事故的!
想到哪里出问题了吗?如果还没有发现,我来问你,你算出来的D1D2的单位是啥?
你也许一拍脑门:哦!换成国际单位制就好了吧!高中物理老师讲过的,时间的国际单位是s,长度的国际单位是m。140mm=0.14m,0.8 yr = 0.8*12 mon=0.8*12*30 days=0.8*12*30*24 h=0.8*12*30*24*60 min=0.8*12*30*24*60*60 s……(黑人问号
你又摸了摸后脑勺,想了想大学老师讲过的量纲分析,写下了这样的式子:
写到这里我们恍然大悟。哦!根本不是国际单位不国际单位的问题,就算化成火星单位,两个分量的单位不一样,也不可能从根号里开得出来呀。
所以,我们之前根本就是在乱搞。我们有一件非常重要的事情没有做,就是把两个分量的量纲消去。否则,我们计算的所谓的距离,根本没有意义,它是会随着单位的选取而任意变化的。
1.2 亡羊补牢,为时未晚——数据归一化
- “院长,我知道错了……”
- “少废话,今晚之前把修正过的kNN算法发给我。”
- “啊!谢谢院长不杀之恩!”
1.2.0 啥叫归一化?
好吧,趁我们胡乱搞出来的kNN算法还没有害人之前,赶紧改吧。
刚刚,我们提到要消去各分量的量纲。最容易的办法,也就是我们今天的第一个主题——归一化。
咦,感觉在哪里听过这个词?概统?大物?数学分析?
好吧,其实差不多都是同一件事情啦。都是为了消除掉量纲在某些场合的影响。
常用的归一化方法有两种:最值归一化,分布(均值方差)归一化。
我们先不纠结于这两种方法本身,我们先来想一个问题:如何消去量纲?
如果原本的量纲是长度量纲,除掉一个同样是长度量纲的量不就好了嘛。原本的量纲是质量量纲,除掉一个同样是质量量纲的量也就消去了量纲呀。
所以,不难想到,最简单的办法,就是除以一个相同量纲的量。
那么,问题就变成了,除数应该如何选取?
有人说我们随便除一个常数好了。长度嘛,那我就除以地球的直径!
笔者数理基础不够扎实,的确没办法从理论的高度分析这样做的不合理性。但是,脑力不够,脑洞来凑!我们可以直观地想一想,这么做,其实没有什么意义嘛,虽然表面上量纲消掉了,但本质上,我们只是把它的单位,变成了“地球个数”。除完地球直径后得数是2,就表明长度是 2个地球并排放 那么长嘛。
说到这里,我们又意识到了一件事情:消掉量纲,只是表面现象;真正关键的,是要将各个分量映射到同一个度量范围下,比如0~1之间,而不是随便除一个相同量纲的量这么simple。
这也就是所谓归“一”化的真谛。
(当然,我们也可以归二化,归十化,归一百万化,甚至归地球半径化,归π化……只要所有分量用的是同一套标准就好了。只是,1是实数单位嘛,用1当然是最简洁的方案)
想通了这一点,我们就来具体探讨两种归一化方法。
1.2.1 最值归一化
我们现在想把一组数据映射到区间 [0,1]. 也就是说:最大值趋于1,最小值趋于0
如果说到这里还觉得无从入手,我们可以回头再看一眼上一行中笔者标为粗体的词语:映射。
再进一步说,数集上的映射……不就是函数嘛。也就是说,我们只要找一个函数,在给定的定义域 [min, max] 下,值域是 [0, 1] ,问题就解决了。
最简单的函数,当然是一次函数啦。画个图:
如果初中数学还没被记忆的洪流冲进太平洋,这张图应该很容易理解吧。呐……
如果高中数学还依稀在脑海里回响,这条直线的方程,大概也很容易写出来。
化简:
咦,这个公式好像就可以拿来做归一化了哟。
没错,这就是最值归一化的归一公式。我们需要的参数,就是数据的最大和最小值。经过这样的映射,得到的结果即全部进入区间 [0, 1] .
拿到了公式,代码实现就非常自然了。参考代码如下:
(i) 向量归一化(数据集只有一项指标时,例如肿瘤实例中只考虑肿瘤大小而不考虑发现时间)
import numpy as np x = (x - np.min(x)) / (np.max(x) - np.min(x))
(ii) 矩阵归一化(即有若干指标参与距离计算时)
for i in range(0,n): X[:,i] = (X[:,i]-np.min(X[:,i])) / (np.max(X[:,i] - np.min(X[:,i])))
1.2.2 分布归一化
学会了最值归一化后,好像得到了全世界。似乎对于各种指标,套用一下最值归一化公式,问题立马解决。
学生考试成绩?除个100再说。
啊,初中数学啊,那就除120咯。
高中语文?好吧,150,不能再多了.
emmm……高考改革,语文提分……好,180就180,反正你总有个满分吧,有多少我除多少。
哈?取消分数限制啦?看阅卷老师心情?(原谅笔者的巨型脑洞)那没办法了……万一老师任性打了个10100次方分,这最大值除下来哪里还得了啊!众所周知浮点数的存储是IEEE754,除完10100铁定是0了啊。
好吧,是时候想个新招术了。
我们站在数理统计的角度考虑,这一堆数据,其实就是一堆样本点嘛。一堆样本点,就会有一个分布咯。至少中心极限定理告诉我们,它很可能近似服从正态分布。
哎,说到正态分布,不知道读者有没有突然想到一个词叫标准化。
N(0,1) 就是标准正态分布啦。标准化的好处之一,就是可以消除量纲。
想到这里,我们有理由茅塞顿开——不论它到底是什么分布,但是只要知道了期望和方差,我们就可以把它标准化啊!虽然标准化变量不能保证落在(0, 1)之间,甚至不能保证落在(-1, 1)之间,但以最自然的分布——正态分布为例,至少,(-1, 1)之间的概率是68.3%, (-2, 2)之间的概率是95.4%,(-3,3)之间的概率是99.7%,也就是说,绝大多数数据点被限制在了一个相同的范围内,落在范围外的,其实正是那些阻止我们使用最值归一化的极端数据。
因此,我们可以用这种分布标准化的方法,对数据指标进行归一化。公式:
代码:
for i in range(0,n): X2[:,i]=(X2[:,i]-np.mean(X2[:,i])) / np.std(X2[:,i])
1.2.3 归一化的细节和库函数
在介绍归一化的库函数之前,我们有必要先来明确一件事情:测试数据集进行归一化时,应该使用训练数据集的最值或分布参数。
比如,在前面的kNN算法中,将原始数据集分为80%的训练数据,20%的测试数据。以均值方差归一化为例。训练数据有均值mean_train, 方差var_train;测试数据有均值mean_test,方差var_test。
请注意,mean_test和var_test是没有意义的。一方面,我们对两组数据应当作完全相同的归一化操作,如果所作的归一化操作不一致,也就失去了归一化的意义。另一方面,测试数据是我们在检验算法效果时人为构造的一组数据,但在实际应用时,数据未必成组出现,当只有个别数据输入(甚至只有一个)数据输入时,自然没有办法使用输入数据的均值和方差进行归一化。
当然,这些只是表象。这些现象背后的本质是,一个算法的实现不应该依托于输入的数据,而是应该在输入数据进入之前就已经确定。作为机器学习,算法输出的结果当然与数据密切相关,但这里所说的数据,是指训练数据,是事前供给机器进行学习使用的数据,或者说,是当前要进行处理的数据输入之前所有已经输入、处理过的数据,而(至少在这个例子中)不应包括当前正要处理的输入数据。
所以,我们使用的均值和方差,应当是mean_train和var_train。
好了,废话说完了,下面给出sklearn库中的归一化解决方案。
sklearn中提供了一个类:StandardScaler。利用这个类的方法,我们就可以对数据进行归一化。下面是官方文档的介绍:
http://lijiancheng0614.github.io/scikit-learn/modules/generated/sklearn.preprocessing.StandardScaler.html
2. kNN算法小结
至此,我们以kNN算法为例,介绍了机器学习中的一些基本概念。下面,我们稍稍总结一下kNN算法本身。
KNN的主要优点有:
- 理论成熟,思想简单,既可以用来做分类也可以用来做回归
- 天然解决多分类问题,也可用于回归问题
- 和朴素贝叶斯之类的算法比,对数据没有假设,准确度高,对异常点不敏感
- 由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合
KNN的主要缺点有:
- 计算量大,效率低。即使优化算法,效率也不高。
- 高度数据相关,样本不平衡的时候,对稀有类别的预测准确率低
- 相比决策树模型,KNN模型可解释性不强
- 维度灾难:随着维度的增加,“看似相近”的两个点之间的距离越来越大,而knn非常依赖距离