单神经元

人类大脑神经元细胞的树突接收来自外部的多个强度不同的刺激,并在神经元细胞体内进行处理,将其转化为一个输出结果,功能抽象图示:

神经元模型可分为:

  • 输入 X, X = [ x 1 ,   x 2 ,   x 3 ] X = [x_{1},~x_{2},~x_{3}] X=[x1, x2, x3]
  • 内部参数权重 W,对每个输入值都给一个权重, w = [ w 1 ∗ x 1 ,   w 2 ∗ x 2 ,   w 3 ∗ x 3 ] w = [w_{1}*x_{1}, ~w_{2}*x_{2},~w_{3}*x_{3}] w=[w1x1, w2x2, w3x3]
  • 内部参数偏移值 b,这个是一个常数,具体作用我一句话说不清
  • 输出 y, 输出 y = 权重 w i ∗ 输入值 x i + 偏移值 b 输出 y = 权重 w_{i}* 输入值x_{i} + 偏移值 b 输出y=权重wi输入值xi+偏移值b
  • 激活函数 f:激活函数种类很多,可确保输出值 y 在 0 和 1 之间,方便决策

e.g. 结合一个买房例子:

  • 输入: X = [ 房屋面积 100 ,  房屋价格 100 万 ,  社区评分 5.0 ] X = [房屋面积 100,~房屋价格 100万,~社区评分 5.0] X=[房屋面积100, 房屋价格100, 社区评分5.0]
  • 权重: W = [ 0.4 , 0.5 , 0.6 ] W = [0.4, 0.5, 0.6] W=[0.4,0.5,0.6],面积占0.4,价格占0.5,社区评分占0.6
  • 偏置: b = 100 b = 100 b=100(其他所有因素,如市场需求供应程度等等)
  • 输出: y = 0.4 ∗ 100 + 0.5 ∗ 100 w + 0.6 ∗ 5.0 + 100 y = 0.4*100 + 0.5*100w + 0.6*5.0 + 100 y=0.4100+0.5100w+0.65.0+100
  • 激活函数: f ( y ) f(y) f(y)

本质上,神经元做的事情就是按照自己的权重参数把输入值相加,再加入偏移值,形成一个输出值, y = w x + b y=wx+b y=wx+b

如果激活后的输出值 f ( y ) f(y) f(y) 大于阈值 0.5 就买这个房,否则不买。
 


内部参数的设置

神经元的内部参数,包括权重 W 和偏移值 b,都是可调的(开始时我们会随机初始化)。

用数据训练神经网络的过程,就是调整更新各个神经元的内部参数的过程。神经网络的结构在训练中不变,是其中神经元的参数决定了神经网络的功能。

反复学习是刺激神经元,相当于加大权重的确定程度(不是加大权重的大小)。

一开始神经元给这个输入数据的权重是0.9,但这是一个随机的分配,有很大的不确定性。

随着训练的加深,神经网络越来越相信这个权重应该是0.11,参数稳定在这里。

数值,增大或者减小了不重要,关建是确定性大大增加了。

对比到人,这就好比篮球,训练的目的不是让投篮的用力越来越大,而是越来越准确。

相当于大脑神经元之间的连接越来越稳固,经常在一起激发的两个神经元会“长”在一起,TA们之间的电信号会更强,电信号强对应参数更确定。
 


偏移值 b 的作用

这得从一个问题说起,如何让计算机具有分辨物体的能力?


在一个二维的平面上,红色的 x 代表苹果,蓝色的 o 代表橘子。

我们有一些苹果、橘子的数据特征,现在出现了一个绿色的未知样本(苹果 or 橘子),如何通过建立一个模型来预测分类。

为了让计算机识别二维平面上的数据,我们可以在这个平面上画一条直线,如 © 图,就用这条直线作分类线。

现在的思路是用已知的数据特征,来训练这条直线。

  • 直线方程: y = w x + b y = wx+b y=wx+b

现在我们的问题是如何找到这条直线的参数斜率和截距 ( w , b ) (w, b) (w,b)

最朴素的思路,随机初始化 ( w , b ) (w, b) (w,b),暴力枚举这对参数,如下图所示:


一开始的思路是猜,不过我们不能完全凭运气,我们可以寻找一些算法策略(如迭代)来优化这个猜,不断逼近正确答案,加速学习过程,避免无效的重复。

迭代策略,需要俩个评价标准:

  • 终止条件:如何判断直线是否把俩类样本分开
  • 迭代方向:如何用错分样本,来更新直线的参数

以下会涉及一些高中数学直线方面的知识,也可以参考:《计算几何》直线部分第一节。

给定 A 、 B 、 C A、B、C ABC 就能确定一条直线,满足这个方程的 ( x ,   y ) (x, ~y) (x, y) 构成一条直线。

那在平面上其他点 ( x 1 ,   y 1 ) (x_{1}, ~y_{1}) (x1, y1),代入这个式子,要么大于 0,要么小于 0。

直线把样本分开,其实就是让某一类的数据点满足到直线的距离大于 0,同时让另一类的点到直线的距离小于 0。

由于我们不用关心点到直线距离的具体数值,因此采用一个符号函数 sign 将:

  • 距离大于零的点,标记成 1
  • 距离小于零的点,标记成 -1

现在如果我们给这些已知的数据带上标记,例如,O(+1) X(-1), 那么我们现在要做的就是要根据已有的数据点,来寻找这样的一条直线,使所有的点都符合它自己本身的标记。

关键的问题是计算机是如何知道自己做的好不好呢?

我们必须再定义一个指示标记,让计算机自己了解它是否正确,这个指标被称为损失函数,损失函数用来评价直线的预测值和真实值不一样的程度,损失函数越好,通常模型的性能越好

我们人类学习同理,如果一个人一直不停的学,但是不验证自己的学习成果,那么有可能学的方向或者学习方法是错误的,不停的学但结果都白学了。要验证学习成果,就要判断预测结果是否准确,损失函数就是做这个的。

直线的损失函数很简单,既然我们希望直线的输出结果 s i g n ( a x + b y + c ) sign(ax+by+c) sign(ax+by+c) 尽量满足 O(+1) X(-1)

我们把数据自带的标签记作 y,则直线的损失函数就变成:

  • L o s s ( s i g n ( a x + b y + c ) , y ) = m a x ( − y ∗ s i g n ( a x + b y + c ) ,   0 ) Loss(sign(ax+by+c), y) = max(-y*sign(ax+by+c), ~0) Loss(sign(ax+by+c),y)=max(ysign(ax+by+c), 0)

这是由于所有被正确分类的样本,无论是橘子 O 还是苹果 X, y ∗ s i g n ( a x + b y + c ) y*sign(ax+by+c) ysign(ax+by+c) 都是 1,增加一个负号,再和零取最大值,则表明那些被正确分类的样本没有让直线产生损失。

只有那些错分样本,会出现 − y ∗ s i g n ( a x + b y + c ) > 0 -y*sign(ax+by+c) >0 ysign(ax+by+c)>0,他们会让感知机猜测的当前直线产生损失,损失越大,这条直线就越不舒服,它必须进行变化。

现在我们应该已经清楚,对于能够用直线分开的数据样本而言,这条之间一定能够找到, 而且满足条件的直线产生的代价是 0。

下图是更新过程:

s i g n ( a x + b y + c ) sign(ax+by+c) sign(ax+by+c) 简记为 h ( x ) h(x) h(x)

根据直线 W 与点 P 的关系, 参数 W 的更新过程如下:

  • w 表示决策边界的权重,而 p 表示某个特定的数据点。

  • 权重的更新规则取决于【数据点的实际类别 y】 和【模型预测的类别 h(x)】。

  • 当实际类别 y=1 但模型预测 h(x)=-1 时,表示模型预测错误,需要调整权重以纠正错误,因此 w_new(新的权重)等于 w_old(旧的权重)加上点 p 的向量(+p)。

    苹果被错误地判定为橘子,这意味着决策边界需要调整以更准确地反映苹果的特征。在权重 w 中,将增加与该苹果相对应的特征值,从而使类似特征的未来数据点更可能被分类为苹果。

    所以我们向权重 w 添加一些值(+p),增加对苹果特征(如红色)的重视,从而在下一次面对类似的水果时,机器会更有可能判断它是苹果。

  • 当实际类别 y=-1 但模型预测 h(x)=1 时,同样表示模型预测错误,权重也需要调整,所以 w_new 等于 w_old 减去点 p 的向量(-p)。

    橘子被错误地判定为苹果,则需要减少相应的权重,以便模型更倾向于将这样的特征组合分类为橘子。

    从权重 w 中减去一些值(-p),减少对红色的重视,增加对橘色的重视,这样机器在下次见到橘子特征(如橘色)时,更可能判断它是橘子。

  • 如果预测是正确的,那么权重不需要更新,因此 w_new = w_old。

整理成一个式子:

每次机器在分类水果时犯错,我们就调整一次权重 w。

随着时间的推移,这个权重 w 会被调整得越来越准确。

偏移值 b 的作用,因为是一个直线方程,一条直线如果参数 b = 0,直线就只能在原点上,就做不到二维空间中任何位置的直线,也就不能进行分类了。

 


神经网络的分层

单个神经元的计算效果不好,但是神经网络依靠网络效应就会非常智能,比如下图的猫跟狗识别正确率会大大提高。

神经网络,从左到右分为三层,每一个圆点代表一个神经元。

  • 第零层是 “输入层”,代表输入的数据。
  • 第一层叫 “隐藏层”,所谓深度学习就是中间不只有一个隐藏层。
  • 第二层是 “输出层”,得到一个输出值。

数据输入进来,经过隐藏层各个神经元的一番处理,再把信号传递给输出层,输出层神经元再处理一番,最后作出判断。

对于多神经元网络,拆分看就是一个个单独的神经元,神经元网络的计算就是重复单神经元的计算。

和神经元相同,神经网络预测的准确与否,由权重 w w w 和偏移值 b b b 决定,所以神经网络学习的目的就是找到合适的 w w w b b b

可是为什么神经网络长这样?

  • 用分层解决复杂性,这也是工程师的普遍思维

凡很大的问题,都是分层管理的:

  • 如中国有十几亿人口,我们的国家是分层管理的,从乡到县,从县到厅,从厅到省,从省到国家,都是一级一级管理的 — 如果没有这样一个清晰的机构,一千个人管理起来就很费劲,一会儿这儿出问题,会像一个救火队长一样冲过去了。

  • 如十几亿晶体管集成的CPU,亿万个开关组成各种各样的逻辑门,逻辑门再组成运算器,这种结构的好处是,在每一层上搞设计,您都只需考虑紧挨着的下一层,最后程序员小哥哥只需要对一块 CPU,而不是十几亿个晶体管编程。

  • 如卷积神经网络做人脸识别,每一个卷积层识别一种特定规模的图形模式,后面一层只要在前面一层的基础上进行识别。


第一层,是先从像素点中识别一些小尺度的线条结构,像垂直条纹、水平条纹、斑点、颜色从亮到暗等等各种小结构。

第二层,是根据第一层识别出来的小尺度结构识别像眼睛、耳朵、嘴之类的局部器官。

第三层,才是根据这些局部器官识别人脸。

其中每一层的神经网络从前面一层获得输入,经过深度学习之后再输出到后面一层,从小结构上看出更大、更复杂也更多的结构来,点 -> 线 -> 面。
 


常用的激活函数

神经网络其实就是线性函数 y = w x + b y=wx+b y=wx+b,函数就是一条直线,能处理的问题也只是线性函数可以处理的问题。

在二维平面可以描述 x − y x-y xy 的直线关系,但曲线关系就不能描述了,线性函数只能画出直线来。

可以证明,无论用什么样的直线,都无法将上图中的两类样本分开。

激活函数是非线性函数,不同的激活函数的样子不同,但都可以表示曲线。

而且神经网络层次越多,非线性函数叠加也会越多,产生的曲线就会越来越复杂。

线性函数加上激活函数就可以让神经网络处理各种问题了。

最初你看,只有一条波浪线,后来在波浪线上继续叠加波浪,随着波浪不断变多,红色的线条越来越接近黑色横杠的样子 — 可曲可直。

常用的激活函数有:

  1. s i g n sign sign

    • 解决的问题:用于简单的分类问题,只关心分类标签而不关心具体数值。
    • 特点:当输入值大于0时,输出1;小于0时,输出-1。用于二元分类,输出非常明确。
    • 应用场景:适用于决策树和其他基于阈值的模型,特别是在处理简单的是/否决策问题时效果良好。
  2. s i g m o i d sigmoid sigmoid

    • 解决的问题:适用于二元分类的输出层,输出值在0到1之间,平均值是0.5。
    • 特点:将输入值映射到一个固定范围内(0到1),非常适合二分类问题,但在其他方面不如 t a n h tanh tanh
    • 应用场景:主要用于逻辑回归模型,处理概率估计问题,如在医学诊断和信贷评分中的应用。
  3. t a n h tanh tanh

    • 解决的问题:使输出值平均值为0,有助于下一层神经网络的学习。
    • 特点:输出值在-1到1之间,平均值为0。和sigmoid相比,靠近0的输出更有利于神经网络学习。
    • 应用场景:适用于处理中心化数据的问题,如图像和声音处理中的特征归一化。
  4. r e l u relu relu

    • 解决的问题:加快神经网络的学习速度,使得网络更稀疏,计算更高效。
    • 特点:正输入有大的梯度,负输入输出0。在隐藏层中非常流行,但在负值时没有梯度。
    • 应用场景:广泛应用于深度学习中,特别是卷积神经网络和深度前馈网络。
  5. L e a k y   r e l u Leaky ~relu Leaky relu

    • 解决的问题:解决了 r e l u relu relu函数在负输入时没有梯度的问题。
    • 特点:是 r e l u relu relu的改进版本,为负输入提供了一个小的非零梯度。
    • 应用场景:用于解决ReLU激活函数中的死神经元问题,适合于需要避免梯度消失问题的模型,如某些深度神经网络。
  6. s o f t m a x softmax softmax

    • 解决的问题:用于多元分类问题,可处理多于两个分类的情况。
    • 特点:将输出转换为概率分布,每个类别都有一个对应的概率,总和为1,适用于输出层的多元分类。
    • 应用场景:常用于神经网络的输出层,处理多类别分类问题,如在自然语言处理和图像识别中的应用。
  7. Mish

    • 解决的问题:提供ReLU的优点,同时避免负输入的激活值完全消失。
    • 特点:是一种平滑的激活函数,允许负值的小激活,可以帮助提高深度神经网络的性能。
    • 应用场景:适合于需要高度非线性和避免梯度消失问题的深度学习模型,如高级图像识别系统。
  8. P-ReLU (参数化 ReLU)

    • 解决的问题:提供ReLU的优点,同时对负输入允许一定的激活。
    • 特点:是ReLU的变体,它允许负输入有一个可学习的小斜率,使模型可以适应不同的数据集。
    • 应用场景:适用于各种深度学习模型,特别是在数据集具有高度变化时,如自适应系统和个性化推荐系统。
  9. R-ReLU (随机化 ReLU)

    • 解决的问题:增加网络的稳健性,减少过拟合。
    • 特点:是P-ReLU的随机版本,在训练期间给负输入的斜率引入随机性,增加了模型的正则化。
    • 应用场景:适合于需要增强模型泛化能力和减少过拟合的场景,如在复杂的图像或语音识别任务中。
  10. ELU (指数化线性单元)

    • 解决的问题:减少ReLU的死神经元问题,同时保持非线性。
    • 特点:对于正输入,它类似于ReLU,而对于负输入,则使用指数函数,使得负值接近于-1而不是0。
    • 应用场景:适用于需要处理死神经元问题且数据分布不均匀的深度学习模型,如在某些类型的网络优化中。
  11. Maxout

    • 解决的问题:提供ReLU族激活函数的所有优点,同时消除了它们的一些缺点。
    • 特点:是一个分段线性函数,可以近似任何凸函数,具有ReLU函数的所有优点但没有其缺点。
    • 应用场景:适用于需要极大灵活性和适应性的模型,如在复杂任务中的深度学习网络。
  12. ReLU6

    • 解决的问题:为ReLU设置一个最大值,以控制激活函数的输出范围。
    • 特点:是ReLU的变体,将输出限制在0到6之间。它在一些移动网络中很受欢迎,因为它可以帮助网络的量化。
    • 应用场景:特别适用于移动设备和嵌入式系统中的神经网络,因为它有助于降低计算复杂性和模型的存储需求。
  13. SwiGLU

    • 问题解决:SwiGLU 旨在通过组合线性单元和 sigmoid 函数来提高模型的非线性表达能力,同时避免了传统激活函数(如ReLU)的某些限制(例如不可微分或梯度消失问题)。
    • 特点:SwiGLU 由线性单元和 sigmoid 函数的乘积组成。这种结构允许网络更有效地学习复杂的函数映射。
    • 应用场景:主要用于需要高度非线性表达能力的深度学习模型,如深度神经网络、自然语言处理任务中的变压器模型。
  14. GeGLU

    • 问题解决:GeGLU 是在 GLU(Gated Linear Unit)基础上的扩展,旨在提供更复杂的激活方式,以便更好地捕获数据中的非线性关系。
    • 特点:GeGLU 将 GLU 的输出通过额外的非线性激活函数(通常是GELU)处理,增加了模型的表达能力。
    • 应用场景:同样适用于复杂的深度学习任务,尤其是在处理语言模型和其他序列数据任务中效果显著。
  15. GeLU

    • 问题解决:GeLU 旨在结合线性和非线性激活函数的优点,通过引入高斯分布的概念来提高神经网络的表达能力。
    • 特点:GeLU 提供了一种平滑的非线性激活方式,它在接近零的值附近更加平滑,可以缓解梯度消失问题。
    • 应用场景:GeLU 广泛用于深度学习中,尤其是在变压器架构(如BERT、GPT系列)中效果显著,适用于各种自然语言处理和计算机视觉任务。
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐