为什么写这个
我自己学机器学习大概一年了,有了不少感慨,也走了不少弯路。所以我想写一份给新手的教程,让他们可以对机器学习有一个宏观的把握,不会迷茫。
既然是新手教程,在很多方面我都用了通俗易懂的说法,也许牺牲了一些精确,但可以帮助读者快速入门。
要学好这份教程,你需要:
- 高中数学水平和一点点微积分知识。
- 熟悉python语法
- 勤劳的手和热爱创造的心
我写的东西肯定不完美,但如果你能从我写的东西获得一些收获,那我是非常高兴的。
当然,机器学习我觉得重要的是算法而不是实现,所以我会把算法放在一个很重要的位置,至于实现,我选择使用pytorch
,scikit
,opencv
,gensim
等等常见库进行讲解。至于一些底层的关于内存和计算机的知识,以及一些特别复杂的数学内容,我会单独标注出来,给有兴趣的同学学习。
路漫漫其修远兮,希望和大家一起进步。
张量(Tensor)
我们高中都学过向量(Vector),例如,用到分别表示了这个一维的表里不同序号的元素。没错,向量就是一张表。
比如我们可以通过以下代码定义一个向量。
import numpy as np
a = np.array([1,2,3,4])
print(a) #nd.array([1,2,3,4])
在机器学习中,一个向量往往可以描述一个样本(Sample),这是什么意思呢?
比如一个人的三个科目的成绩,就是三个特征(Feature),我们可以用一个三维向量来描述。
当然,不一定多少个特征就多少维的向量,比如我们取一个特征:情绪,有开心,难过,生气三个等级。我们一般使用如下方法来表示这个特征:
为什么不用1,2,3三个数来表示呢,答案很简单,因为这三个情绪不能比较。
举个例子,比如人的成绩是可以比较的,你比我高,我比你低,我们用1,2,3表示不及格,及格,优秀,没有问题。
但情绪只是状态,是不可比较的,高兴>难过说的过去,但是为什么生气大于或者小于难过呢,这是难以解释的,所以我们不这么干。用离散数学的说法,情绪这个集合不是一个偏序集。
说完了一维的表,我们看看二维的例子——矩阵(Matrix),为什么说是二维呢?我们描述里面的元素,要说第几行第几列,也就是通过两个数来定位元素,比如这个矩阵的下标:
矩阵是机器学习的重要部分,基于这个原因后面的文章中我还会引入奇异值分解(SVD),线性空间等等关于高等代数的内容。
下面我们来看看如何在Python定义一个矩阵。
import numpy as np
m = np.array([[1,2],[3,4]])
print(m) #nd.array([[1,2],
# [3,4]])
其实你发现,不就是列表套列表,套了两层嘛。这是个很好的想法!
既然可以套两层,那为什么不可以套3层,4层,n层呢?我们构造出套了层的表,把这种结构叫做维张量!
注意:有的书或者文献把这种结构叫做数组(array),这是个偏向计算机的说法,在本教程里一律使用张量。
一个的维张量里的每个元素可以由个数唯一确定。
为了让读者理解,我们举个例子:
我们可以认为一个三维张量是由一系列写着矩阵的纸片叠起来,第几张纸就是第一维的数是几,剩下两个数决定了这张纸上的矩阵的第几列第几行。没错,三维就是把二维套起来。
那四维呢,无非就是把这些纸张一沓一沓摞起来,相当于又嵌套了一层。
具体一点,看下面这个嵌套列表:
a = [ [[1,2],[3,4]] , [[5,6],[7,8]] ,[[9,10],[11,12]]
第一层嵌套有三个元素[[1,2],[3,4]] , [[5,6],[7,8]] ,[[9,10],[11,12]]
每个第二层嵌套有两个元素,例如[1,2],[3,4]
每个第三层嵌套有两个元素,例如1,2
所以这个张量是的,请务必搞清楚这个关系,我们一般把第几层嵌套叫做第几个轴(axis)。第0个轴就是整个张量的第一层,最后一个轴没有列表套列表结构,只有元素,也就是最里面一层。
请注意,axis0才是第一个轴,对于一些函数,允许传入负数轴表示由内而外选择,例如
axis=-1
表示选择最里面一层。
代价函数(Cost Function)
机器学习大部分情况下只是在寻找一个函数的最小值。——佚名
我们之所以要做机器学习,是为了让一个模型(model)能够很好的拟合实际情况,并表现出某种“智能”。
什么是模型,比如一个简单模型,我看见天气预报说明天下雨的概率大于0.5,那么我就说明天下雨。
这里的下雨概率是天气预报给出的数据,我们用表示这个输入,而我通过输入是否大于0.5来预测出的这个结果记做。谁是模型呢,我就是这个模型,我把输入变成了输出,至于明天是不是真的下雨,又是一组数据,我们记做。很容易猜到和越接近越好。
模型给出的预测和实际结果可能有误差,例如我输出了明天要下雨结果没有下,这就是误差。
所以,我们需要找一种方法来度量这种误差,然后尽量让误差变小,误差和什么有关呢?我们认为只和模型有关。
实际上,由于训练使用的数据不可能包括所有可能取值,不同区间的数据可能误差不一致,例如训练区间误差很小,但是预测效果糟糕,出现了过拟合(Overfitting),但我们在建模的时候,理想的认为误差应该对什么样的输入都一视同仁。
所以我们需要不断优化模型来减小误差?依据什么呢,依据喂给模型的数据,这个过程在机器学习中我们叫做训练(train)。这个反映模型在这一组数据上误差的函数叫做代价函数,我们在代码里一般用loss来指代这个函数。
举个例子,假设我就是一个机器学习模型,我通过观察女朋友的表情来预测她的心情(向量表示法和上一节的一样),她明明生气,也就是,我却以为她开心,也就是,那么我们可以通过计算这两个向量的距离来反映误差大小,距离是.接下来我开始训练(优化函数),我发现她笑里藏刀,可能开心里有点生气,那就两种心情五五开,也就是向量,现在两个向量距离只有了,说明我这个“模型”经过训练,变得更优了。
对于这个过程一定要理解清楚,因为对于大多数有监督学习,都是在进行这个过程。我们后续会用大量例子来进行说明,区别只是不同的代价函数,建模过程,和优化算法。
下篇文章我们会以一个非常简单的例子,学习一个二次曲线并进行预测,让大家深入理解这篇文章里面所说的这些概念,同时介绍一种重要的优化算法,梯度下降。
对于初学者,下面这些内容较为理论化,不感兴趣可以跳过,也可以后期再回头来看。
代价函数的参数实际上有两个,数据和模型,模型可以理解为也是一个函数(从计算机的角度上这个函数包含了许多参数,所以也可以认为是闭包(Closure)),从这个角度看,也就是在给定数据条件下,代价函数是一个泛函(Functional),把模型映射到实数空间上。
定义代价函数。我们一般用预测和实际之间的距离做为这个函数。但事情并非绝对,这一点我们后期会说到。
可怎么定义这个距离呢?你一定会说因为和都是向量,只需要用或者不就可以表明二者的误差了嘛。但实际上并非如此,我们在后期介绍Logist回归和支持向量机的时候,大量使用的是另外一个基于信息量的数学期望——信息熵的代价函数,这说明两个向量之间的距离可以有不同的定义。
在实变函数论里,我们定义了度量空间,也就是集合的任意两个元素都是有距离的,我们用一个函数来输出距离,度量距离的度量函数必须满足:
(1)距离是大于等于0的,即.
(2).(对称性)
(3).(三角不等式)
下面我们给出另外一个概念,范数,这里不想介绍太多实变函数的概念,一个元素的范数是这个元素到的一个映射,记做,你可以理解为某个元素的范数就是这个元素到某个“原点”的绝对距离,既然是距离,去掉显然的对称性,应该就有上面那三条里的两条:
(1).
(2).(三角不等式)
但同时我们如果在线性空间(线性空间的元素叫做向量)上范数的定义满足:
(3)。
那么这个赋范线性空间是度量空间,满足.反之不一定,一个度量空间不一定是赋范空间,你可以理解为这个空间没有原点,无法定义一个决定的距离。
下面我们介绍几种常用的向量的范数:
1-范数:.也就是各分量绝对值之和。
2-范数:.也就是我们常用的欧式距离。
-范数:.也就是取最大分量。
值得一提的是,在线性空间里矩阵也是广义向量,当然可以有自己的范数。
在numpy
等包中我们用函数norm
来计算范数,numpy.linalg
是常用的一个线性代数包,其中就有norm
函数。下面是调用方法,其中ord
是几就是几-范数,对于无穷大,我们传入np.inf
即可。
x_norm=np.linalg.norm(x, ord=None)
下面这个自己写的函数定义了两个向量的欧式距离,我们调用一次试试:
import numpy as np
from numpy.linalg import norm
def distance(x,y):
return norm(x-y,2)
a = np.array([1,2,3])
b = np.array([3,2,1])
print(distance(a,b)) #2.8284271247461903