机器学习
最简单的中文机器学习教程
novakon 2016-12-14

我之前在论坛发过一些机器学习的资源,但是这些资源比较难直接下手,必须要先学高数和概率论。那么我今天就把复杂的机器学习算法,写成高中数学形式。

问题:我有1000张猫的照片,1000张狗的照片,请训练一个系统,令其能够分辨猫和狗。

假如每张照片的大小是32x32,那么一张照片就有1024个像素,每个像素的亮度从0到1可变。我们把这1024个像素标记为x0, x1, x2....x1023. 这样,我们就用1024个变量表示了一张输入图片。


这样就可以定义一个1024元函数:y0,y1 = f(x0,x1,x2.......x1023),其中y0是图片为猫的概率p(猫),y1是图片为狗的概率p(狗)。

我们的任务是,找到合适的函数 f(),使得计算f(猫的图片)得到 y0>y1,计算f(狗的图片)得到 y1>y0. 如果找到了这个函数f(),我们就可以计算 f(某张图片) = f(x0,x1,x2....x1023),如果得到的结果[y0,y1]中 y0>y1,比如[0.9, 0.3]或者[1.4, 0.8]或者[3.2, 1.6],就可以确定这张图片是猫。

所谓机器学习的训练过程,就是寻找函数f()的过程。

为了简化描述,我们把y0和y1称为二维矢量Y,把x0,x1,x2...x1023称为1024维矢量X。

表达式就可以简化为: Y = f(X)。

在下文中,我用大写字母表示矢量和矩阵。矢量和矩阵都是表示一组变量的简便方式(比如用矢量X来表示x0...x1023),是《线性代数》的教学内容。这是本教程涉及的唯一大学内容。

把图片中的1024个像素,转换成两个代表概率的数字,这中间的计算过程一定是非常复杂的。对于人脑和肉眼来说,猫或者狗的影像先通过晶状体汇聚到视网膜,再通过视神经传递到大脑的视神经中枢,再经过几次传递,最终得出猫或者狗的结论。这么复杂的神经传递过程,怎么用数学表达呢?人工智能领域的研究者提出,可以用神经元函数模拟神经元的行为。

下面是一个真正的人类神经元。


刺激从dendrites(树突)传入神经元,然后从axon(轴突)传出。

怎么用数学公式描述它的特性呢?下面我介绍目前机器学习领域最常用的神经元函数。


a = max(w0 * x0 + w1 * x1 + w2 * x2...+ w1023 * x1023 + b, 0)

或者简写为

a = max(W * X + b, 0)

其中a是神经元的输出,X(也就是x0...x1023)是神经元的输入,W(分别是w0...w1023)是神经元的权重,b是神经元的偏置,max是最大值函数。

我们注意到,每个神经元的输入可以有很多,但是输出连接只有一个,这和大脑中神经元的特性是类似的。

权重w的作用,是控制每个输入x对神经元的影响。如果w为负,则每个输入x对神经元的贡献就是负数。通过调节每一个w的大小,我们就可以让神经元对不同的输入x,产生不同的反应。如果某个w等于零,那么对应的输入x对后面的神经元就没有影响,相当于这个神经连接断掉了。

调节权重W(以及偏置b)的过程,称为学习或者训练。

神经元的输出有一个max函数,如果计算结果为正就直接输出,如果计算结果小于0就输出0。这是因为人类神经元只能输出正的刺激信号(脉冲),不能输出负的刺激信号。这只是一种直观的说法,实际上选择这个函数的原因非常复杂,可以去看geoff hinton的教程,里面有更加详细的数学解释。

由于f()的输出是两个实数,我们至少需要两个神经元。这两个神经元和图像的1024个输入都是相连接的。


这样我们就实现了函数f()。上图中,两个神经元的输入权重w的序号重复了,为了明确,我们把第一个神经元的偏置和权重分别称为 b0, w0_0, w0_1, w0_2... 把第二个神经元的偏置和权重分别称为b1, w1_0, w1_1, w1_2...

于是f()的计算方法如下:

y0 = a0 = max(x0 * w0_0 + x1 * w0_1 + x2 * w0_2 ...+ x1023 * w0_1023 + b0, 0)

y1 = a1 = max(x0 * w1_0 + x1 * w1_1 + x2 * w1_2 ...+ x1023 * w1_1023 + b1, 0)

如果把x0...x1023称为X,把w0_0...w0_1023称为W0,把w1_0...w1_1023称为W1,上式可以简写为:

y0 = max(X * W0 + b0, 0)

y1 = max(X * W1 + b1, 0)

如果把y0...y1称为Y,把W0和W1称为W(这时W就有1024 * 2=2048个元素了,是一个1024 * 2尺寸的矩阵),b0和b1称为B,上式可以简写为:

Y = f(X) = max(X * W + B, 0)

于是,研究者们把W设为2048个随机数,把B设为两个随机数,然后给函数f()输入一张猫的图片。

f(X) = f(猫的图片) = max(猫的图片 * W + B) = [0.5, 0.5]

结果是y0 = 0.5, y1 = 0.5,也就是我们的神经网络分不清这张图片到底是更像猫还是更像狗。这是很正常的,因为我们的W和B是随机设定的。

(由于W和B都是函数f()的参数,下面我们把W和B统称为W。)

研究者随后对W进行了一点随机微调,并重新计算f(猫的图片)。如果调完之后y0增长了或者y1下降了,就说明调节有效;如果调完之后y1增长了或者y0下降了,就说明调的方向错了。

经过一段时间的调节,研究者发现这样很慢,W一共有2048个元素,每调一次,输出只动一点点,要调到什么时候?于是研究者就想了一个办法:用一个函数E来计算误差的大小。

首先,对输出[y0, y1]应用softmax函数。

$$\sigma(z) _{j}={\frac {e^{z _{j}}}{\sum _{k=1}^{K}e^{z _{k}}}}, j=1,2...K$$

用高中数学来写就是 softmax(Y) = softmax([y0,y1]) = [e^y0, e^y1] / (e^y0 + e^y1)

= [e^y0 / (e^y0 + e^y1), e^y1 / (e^y0 + e^y1)]

经过softmax函数之后,y0和y1的相对大小关系不变,但是他们的和保证等于1。比如说原来求得的猫狗概率是[1.2, 0.6],softmax函数之后就得到[0.65, 0.35]。

然后定义误差函数E:

Y = f(X)

E = - log(softmax(Y)) * y_true

E = - log(softmax(f(X))) * y_true

其中 y_true 代表的是输入图像的真正类别,当输入为猫时为[1,0],当输入为狗时为[0,1]。

我们注意到,因为softmax的结果都在0和1之间,应用log函数之后会得到一个负数,softmax所得的结果越小,log得到负数的绝对值就越大。如果softmax后所得结果接近零,log之后会得到一个非常大的负数。前面再加个负号,就得到一个非常大的正数。

所以E越大,说明我们的判断结果错得越离谱。这是一个很合适的误差函数。

当输入为猫时,输出y0(相对于y1)越小,误差函数E的值就越大,如果输出y0远小于y1,说明错的很严重,误差函数E的值会非常大。我们调节W的时候,就可以以E为参考。

上面讲到,研究者想了一个加速的办法。要怎样调节W,才能使得误差E不断降低呢?对了,可以利用E对W的导数。

我们在高中学过y对x的导数,可以写作$$\frac{dy}{dx}$$


上图中x0处的导数,也就是绿色切线的斜率,是个负数,大概是 -2 。如果把点x0朝导数所指的下坡方向移动,y的值会越来越小。就像下面这样:


如果用公式来描述这个过程:

$$x_1 = x_0 - \frac{dy}{dx}(x_0)$$ $$x_2 = x_1 - \frac{dy}{dx}(x_1)$$ $$x_3 = x_2 - \frac{dy}{dx}(x_2)$$

这相当于不断执行下面的操作:

$$x = x - \frac{dy}{dx}(x)$$

这种【按照y对x在x点的导数来调节x,使得y越来越小】的方法,称为导数下降法。

上面y对x的图像中,y是一维的,我们在一维函数上寻找最小值。如果y是二维的,有两个参数呢?下面是一张2维平面上的图像:


上图中 theta0 和 theta1就是待调节的参数,而J(theta0,theta1)是误差函数。图中演示了两个点是如何通过梯度下降法,找到J()的局部最低点的。

原题中的W是2048个变量,E对W的导数要怎么写?难道要写2048条公式吗?

$$\frac{dE}{dw_{0,0}},\frac{dE}{dw_{0,1}}, ... ,\frac{dE}{dw_{1,1023}}$$

上面这种写法太繁琐了(而且也不正确)。通常我们这样写:

$$\frac{\partial E}{\partial w_{ij}}(W)$$

其中i=0,1 j=0,1,2...1023 称为E对\(w_{ij}\)在W处的偏导数。

之前抛物线函数的例子中,函数y上点x处的斜率是一个数字。而这次函数E上点W的斜率,共有2048个数字,可以记作一个2048维矢量:

$$\left(\frac{\partial E}{\partial w_{0,0}}(W),\frac{\partial E}{\partial w_{0,1}}(W), ... ,\frac{\partial E}{\partial w_{1,1023}}(W)\right)$$

上面这个矢量简称函数E在W处的梯度,它和之前所说的y在x处的斜率是一个意思,只不过由很多个数组成。上面的梯度可以简写为:

$$\nabla E(W)=\left({\frac {\partial E}{\partial w_{0,1}}}(W),\ldots ,{\frac {\partial f}{\partial w_{1,1023}}}(W)\right)$$

因此,只要不断执行:

$$W = W - \alpha \nabla E(W)$$

就可以使得E的值越来越小(假定E是个比较凸的函数)。这种【按照W对E在W处的偏导数来调节W,使得E越来越小】的方法,称为梯度下降法。梯度下降法的一个变种,随机梯度下降法,是目前机器学习领域应用最广泛的优化方法。

其中α是一个可调的量,称为学习率,它决定下降的速度和稳定性。

至此,我们可以把整个训练过程概括为:

  1. 确定Y = f(X)的形式,为了与人脑架构匹配,我们在这里用的是神经网络函数,函数的参数(偏置和权重)为W和B,以下将它们统一称为W;
  2. 对于给定的一批训练图片X(1000张猫,1000张狗),以及当前的W,计算误差 E = -log(softmax(f(X))) * y_true,其中 y_true 是代表对应图片猫狗分类的标签。
  3. 使用梯度下降法,根据E对W在W处的偏导调节W
  4. 重复第2步,直到误差E不再下降为止。
  5. 用经过训练的f()函数,输入猫狗图片并观察其输出,统计正确率。

为了让例子显得简单,我们只使用了2个神经元,而且从输入直接到输出,中间没有隐藏层,最终得到的分类效果是很差的。

如果你想玩这种最简单的神经网络,可以直接访问google的tensorflow神经网络演示页面,那里的分类器虽然不能分类猫和狗,但可以做一些同样有趣的事情。http://playground.tensorflow.org/


在实际的视觉分类应用中,神经元的数量会多得多,而且会一层叠一层(警告:以下是大学内容),比如上面就是一个四层的神经网络,有两个隐藏层。

通过增加层数,网络可以表达更加复杂的非线性关系,从而能更好地概括输入数据中隐含的复杂关系。

对于视觉应用,每一层的输入,也不是直接取输入像素的值,而是用一组二维模板,对像素的值分别进行二维卷积,再叠加求和,层和层之间还有pooling(求局部最大值并组成更小的新图像)操作。


(LeNet架构,它20年前就学会了怎样分辨人类手写的数字,准确率超过99%)

使用二维卷积和pooling,可以令图像中关键特征获得位移无关性(比如猫的脸在图像中可能会左右移动,这不应该影响最终判断的结果)。

读到这里的同学,以上介绍完了机器学习的基本概念和方法,谢谢你们。如果你觉得这太简单了,下一步的两个可选的教程分别是Andrew Ng的 Machine Learning,以及Geoff Hinton的Neural Networks for Machine Learning

[修改于 2 年前 - 2016-12-15 00:20:51]

2016-12-14 21:53:34
novakon(作者)
1楼

补充一点:

上面提到的矩阵与矢量乘法,比如W * X,用星号表示逐项相乘,是为了保证高中数学不超纲。

实际在大学的数学书中,如果X是一个1024维矢量,W是一个1024 * 2尺寸的矩阵,这种运算通常写成:

$$X^TW$$

原文中单层神经元函数因此可以记作:

$$max(X^TW+B,0)$$

常见问题:

  • 我想要很多猫和狗的照片

    请去这里领取:https://www.kaggle.com/c/dogs-vs-cats

  • 神经网络的函数这么复杂,我不是很懂矩阵运算(线性代数)和求偏导(高等数学)怎么办?

    你只需要知道基本的矩阵概念(比如图像是二维矩阵,彩色图像是三维矩阵),以及怎么用python语言操纵矩阵的元素就可以了。谷歌开源的机器学习框架tensorflow就是用来做矩阵计算流程优化的,你可以用它拼接出神经网络,它有办法帮你设置好每一层的权重矩阵,并且帮你优化计算流程。神经网络函数、ReLU函数(就是max(x,0))、softmax函数都是tensorflow内置的。tensorflow可以根据你拼接出的计算流程,帮你求W对E的偏导,也就是说除了处理数据和设计架构,你什么都不用做。它有很多教程可以帮助你学习怎么使用,你应该花一些时间阅读。Github上的Keras框架把tensorflow包了一层,操作起来更简单,前提是你必须知道底下发生了什么。

  • 感觉卷积神经网络比普通神经网络还复杂

    tensorflow也支持卷积神经网络的构建,你大概知道原理就行了,不需要自己写二维卷积代码(很难写对的)。关于卷积神经网络(ConvNet,又称CNN)原理方面的内容,可以阅读Yann LeCun 发表于 2000年以前的论文。

  • 玩机器学习能提高我的数学水平吗?

    我认为是绝对不可能的。如果你数学不好,最好先学数学,千万别碰这个。

[修改于 2 年前 - 2016-12-14 22:54:21]

2楼
真的很简单,收藏起来
2016-12-16 01:49:45
4楼
感谢分享。。。

想参与大家的讨论?现在就 登录 或者 注册

{{submitted?"":"投诉"}}
请选择违规类型:
{{reason.description}}
支持的图片格式:jpg, jpeg, png