← 返回全部文章

神经网络学习 - 1. 线性到非线性

[ ML ]
[ AI ] [ Neural Network Learning ]

一个神经网络其实十分简单:

A Neuron:

z=w1x1+w2x2++wnxn+bz=w_1x_1+w_2x_2+\cdots+w_nx_n+b

The activation function:

a=σ(z)a=\sigma(z)

但这个神经元并非生物意义上的神经元,只是受到大脑的启发。一个神经元接收多个输入(X),最终加权和输出。

W就像生物神经元的轴突,对所有的input neurons一一进行计算。

图灵:机器能否思考?

人脑由数百亿个神经元组成,神经元之间通过突触彼此连接,并依靠电信号和化学信号传递信息。一个神经元接收到来自其他神经元的刺激后,会在细胞内部形成电位变化;当这种变化达到一定阈值时,神经元就会被激活,并把信号继续传递给下一个神经元。

为什么物质性的神经活动会产生思想、感受和自我意识?

为什么一个由物质构成的系统,能够回过头来认识世界,甚至认识它自己?

从微观的神经元活动,到宏观的思想和意识,中间似乎跨越了一道至今没有被完全解释的鸿沟。

1950年,英国数学家 Alan Turing 发表了著名论文 Computing Machinery and Intelligence。

在这篇论文开头,他提出了一个后来极其著名的问题:

Can machines think?\text{Can machines think?}

图灵并没有试图直接定义“思考”或“智能”。因为这些概念太抽象,也太容易陷入哲学争论。

他换了一种更实际的方式:如果一台机器在对话中表现得和人类无法区分,那么我们是否可以认为它具有某种智能?

这个思想后来被称为 图灵测试。

图灵在人工智能领域真正重要的贡献,不是给出了智能的最终定义,而是把“机器是否能够思考”变成了一个可以被讨论、被测试、被工程化的问题。

MP神经元模型

复杂的智能是否可从简单的计算单元共同作用下涌现而出?

1943年,Warren McCulloch 和 Walter Pitts 提出了一个早期的人工神经元数学模型,后来常被称为 MP神经元模型。

这个模型非常简单:它接收多个输入,把它们加起来,然后通过一个j简单的阈值判断是否输出 1。

它接收多个输入:

x1,x2,x3,,xnx_1, x_2, x_3, \dots, x_n

在最简单的 MP神经元中,每个输入通常只有两种状态:

xi0,1x_i \in {0, 1}

神经元会把所有输入加起来,并和一个阈值进行比较。

如果输入总和达到阈值,就输出 1;否则输出 0。

y={1,i=1nxiθ 0,i=1nxi<θy = \begin{cases} 1, & \sum_{i=1}^{n} x_i \geq \theta \ 0, & \sum_{i=1}^{n} x_i < \theta \end{cases}

如果这个神经元受到刺激总量超过阈值,神经元就被激活;如果没有超过阈值,神经元就不被激活。

MP神经元可以表示某些逻辑运算,比如 AND、OR、NOT。

但MP神经元却并没有学习能力。

Perceptron感知机

1958年,Frank Rosenblatt 提出了 Perceptron,感知机。

感知机可以看作 MP神经元的升级版。它仍然接收多个输入,但每个输入前多了一个权重的计算。

感知机的输入可以写成:

x=(x1,x2,x3,,xn)x = (x_1, x_2, x_3, \dots, x_n)

权重可以写成:

w=(w1,w2,w3,,wn)w = (w_1, w_2, w_3, \dots, w_n)

感知机会先计算输入的加权和:

z=w1x1+w2x2+w3x3++wnxn+bz = w_1x_1 + w_2x_2 + w_3x_3 + \dots + w_nx_n + b

也可以写成向量形式:

z=wx+bz = w \cdot x + b

其中:

wx=i=1nwixiw \cdot x = \sum_{i=1}^{n} w_i x_i

偏置b可以理解为调整神经元激活难度的参数。 如果偏置较大,神经元更容易被激活;如果偏置较小,神经元更难被激活。

感知机的输出可以写成:

y={1,wx+b0 0,wx+b<0y = \begin{cases} 1, & w \cdot x + b \geq 0 \ 0, & w \cdot x + b < 0 \end{cases}

[COMMON] 这里使用的是一个阶跃函数,也可以写成:

y=f(wx+b)y = f(w \cdot x + b)

其中:

f(z)={1,z0 0,z<0f(z) = \begin{cases} 1, & z \geq 0 \ 0, & z < 0 \end{cases}

感知机的几何意义

感知机本质上是在寻找一个分类边界。

对于二维输入:

x=(x1,x2)x = (x_1, x_2)

感知机的判断条件是:

w1x1+w2x2+b=0w_1x_1 + w_2x_2 + b = 0

拟合了一条直线。

更一般地,在高维空间中:

wx+b=0w \cdot x + b = 0

表示一个超平面。

Perceptron on hyperplane

感知机能解决的问题,本质上是可以被一条直线、一个平面,或者一个高维超平面分开的分类问题。

感知机的局限

单层感知机只能解决线性可分问题。

但现实中很多问题并不是线性可分的。最简单的经典问题XOR就并不是线性可分的。

XOR

XOR 的输入和输出为:

(0,0)0(0,0) \rightarrow 0 (1,0)1(1,0) \rightarrow 1 (0,1)1(0,1) \rightarrow 1 (1,1)0(1,1) \rightarrow 0

单层感知机虽然可以学习一些简单的分类边界,但它无法表达更复杂的非线性关系。

1969年,Marvin Minsky 和 Seymour Papert 在《Perceptrons》一书中系统分析了感知机的能力边界。

但问题并没有真正结束。 如果单层感知机无法解决非线性问题,那么一个自然的想法就是:能不能把多个神经元叠起来,形成多层网络?

多层感知机MLP

| 从线性到非线性

破局的关键不是放弃神经元,而是把神经元连接成多层结构。第一层神经元先提取中间特征,后面的神经元再根据这些特征做判断。这样,网络就不再只能画一条直线,而是可以组合出更复杂的决策边界。

但如果没有激活函数,每一层都只是线性变换:

a(1)=W(1)x+b(1)a^{(1)} = W^{(1)}x + b^{(1)} a(2)=W(2)a(1)+b(2)a^{(2)} = W^{(2)}a^{(1)} + b^{(2)}

把第一层代入第二层,可以得到:

a(2)=W(2)(W(1)x+b(1))+b(2)a^{(2)} = W^{(2)}(W^{(1)}x + b^{(1)}) + b^{(2)}

展开:

a(2)=W(2)W(1)x+W(2)b(1)+b(2) a^{(2)}=W^{(2)}W^{(1)}x+W^{(2)}b^{(1)}+b^{(2)}

等同于:

a(2)=Wx+ba^{(2)} = W'x + b'

多层线性变换叠加之后,本质上仍然只是一个线性变换。

非线性激活函数打破了这种限制。 它让神经网络不再只是画一条直线、一个平面或一个超平面,而是能够表达弯曲的、分段的、复杂的函数关系。

激活函数

阶跃函数

在早期感知机中,神经元使用的是阶跃函数:

f(z)={1,z0 0,z<0f(z)= \begin{cases} 1, & z \geq 0 \ 0, & z < 0 \end{cases}

其中:

z=wx+bz = w \cdot x + b

阶跃函数的作用很直观:如果输入信号足够强,神经元就输出 1;如果输入信号不够强,神经元就输出 0。做出二元判断。

Sigmoid

Sigmoid 函数可以写成:

σ(z)=11+ez\sigma(z)=\frac{1}{1+e^{-z}}

[COMMON] 它会把任意实数压缩到 0 和 1 之间:

0<σ(z)<10 < \sigma(z) < 1

[INFERRED] 这让它很适合表示“概率感”或者“激活程度”。 [COMMON] 但 Sigmoid 在输入很大或很小时,梯度会变得非常小。 [INFERRED] 这会导致深层网络训练变慢,也就是常说的梯度消失问题。

Tanh

Tanh 函数可以写成:

tanh(z)=ezezez+ez\tanh(z)=\frac{e^z-e^{-z}}{e^z+e^{-z}}

[COMMON] 它会把输入压缩到 -1 和 1 之间:

1<tanh(z)<1-1 < \tanh(z) < 1

[INFERRED] 相比 Sigmoid,Tanh 的输出以 0 为中心,在一些场景下更容易优化。 [COMMON] 但它同样可能在输入绝对值很大时出现梯度变小的问题。

ReLU

ReLU 函数可以写成:

ReLU(z)=max(0,z)\text{ReLU}(z)=\max(0,z)

也就是:

ReLU(z)={z,z>0 0,z0\text{ReLU}(z)= \begin{cases} z, & z > 0 \ 0, & z \leq 0 \end{cases}

[INFERRED] ReLU 的思想非常简单:如果输入是正数,就保留它;如果输入是负数,就直接变成 0。 [COMMON] ReLU 计算简单,在正数区间梯度稳定。 [KNOWN] 现代深度神经网络中,ReLU 及其变体被广泛使用。 [INFERRED] 它的重要性不只是“函数形式简单”,而是它让深层网络更容易训练。

前向传播

| 前向传播:信息如何一层层流动

[COMMON] 有了权重、偏置和激活函数之后,一个神经元就不再只是简单的开关,而变成了一个可以进行连续计算的单元。

[COMMON] 对于第 ll 层神经网络,前向传播可以写成:

z(l)=W(l)a(l1)+b(l)z^{(l)} = W^{(l)}a^{(l-1)} + b^{(l)} a(l)=f(z(l))a^{(l)} = f(z^{(l)})

[COMMON] 其中,a(l1)a^{(l-1)} 表示上一层的输出,W(l)W^{(l)} 表示当前层的权重矩阵,b(l)b^{(l)} 表示当前层的偏置,ff 表示激活函数,a(l)a^{(l)} 表示当前层的输出。

[INFERRED] 也就是说,每一层都会先对上一层传来的信息做一次线性变换,再通过激活函数加入非线性,最后把结果传递给下一层。

[INFERRED] 这个过程可以理解为:上一层把信息传给下一层,下一层先对信息进行加权组合,再通过激活函数决定哪些信息应该继续传递。

[COMMON] 如果网络有多层,那么这个过程会不断重复:

xa(1)a(2)y^x \rightarrow a^{(1)} \rightarrow a^{(2)} \rightarrow \cdots \rightarrow \hat{y}

[INFERRED] 这就是所谓的前向传播。它像是一条从输入到输出的信息流:数据从输入层进入,经过隐藏层的不断变换,最后在输出层得到预测。

损失函数

[COMMON] 前向传播只能告诉我们模型的预测结果,但不能告诉我们这个预测有多好。

[COMMON] 为了衡量预测值和真实值之间的差距,我们需要引入损失函数。

[COMMON] 假设模型预测值是:

y^\hat{y}

真实标签是:

yy

[COMMON] 对于回归问题,常见的损失函数是均方误差MSE:

L=12(y^y)2L = \frac{1}{2}(\hat{y}-y)^2

[INFERRED] 损失函数越大,说明模型预测得越差;损失函数越小,说明模型预测得越接近真实答案。

梯度下降

那么损失函数对所有参数的梯度可以写成一个向量:

L=(Lw1,Lw2,,Lb)\nabla L = \left( \frac{\partial L}{\partial w_1}, \frac{\partial L}{\partial w_2}, \cdots, \frac{\partial L}{\partial b} \right)

这个向量中的每一项,都表示损失函数对某一个参数的偏导数。

梯度下降的参数更新公式是:

θθηLθ\theta \leftarrow \theta - \eta \frac{\partial L}{\partial \theta}

其中:θ\theta表示模型参数,LL表示损失函数,η\eta表示学习率,

θ=W(1),b(1),W(2),b(2),,W(L),b(L)\theta = {W^{(1)}, b^{(1)}, W^{(2)}, b^{(2)}, \dots, W^{(L)}, b^{(L)}}

梯度告诉我们:当参数 (θ)(\theta) 稍微变化时,损失函数 (L)(L) 会往哪个方向变化,以及变化得有多快。

如果梯度是正的,说明参数往增大的方向移动会让损失变大。

如果梯度是负的,说明参数往增大的方向移动会让损失变小。

Good Learning Resources