神经元的结构

    • 神经元
      • 内部参数的设置
      • 偏移值 b 的作用
      • 神经网络的分层
      • 常用的激活函数

神经元

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

神经元模型可分为:

  • 输入 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]
  • 偏置: 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 的更新过程如下:

整理成一个式子:

最后,就会找到那条将苹果和橘子分类的直线。

偏移值 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 的直线关系,但曲线关系就不能描述了,线性函数只能画出直线来。

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

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

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

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

常用的激活函数有:

  • s i g n sign sign:符号函数,输入值 > 0, s i g n ( x ) = 1 sign(x)=1 sign(x)=1;输出值 < 0, s i g n ( x ) = − 1 sign(x)=-1 sign(x)=1

    应用问题不太关心具体数值,只需要一个分类。

  • s i g m o i d = 1 1 + e − x sigmoid=\frac{1}{1+e^{-x}} sigmoid=1+ex1:输出值在 [ 0 , 1 ] [0,~1] [0,1],平均值是 0.5

    现在只适用于二元分类的输出层(神经网络只能判断是或否),其他方面不如 t a n h tanh tanh

  • t a n h x = s i n h x c o s h x = e x − e − x e x + e − x tanh~x=\frac{sinh~x}{cosh~x}=\frac{e^{x}-e^{-x}}{e^{x}+e^{-x}} tanhx=coshxsinhx=ex+exexex:输出值在 [ − 1 , 1 ] [-1,~1] [1,1],平均值是 0

    因为平均值从 0.5 变成 0,将靠近 0 的输出值传给下层神经网络,效果更好。

    不过俩者在数据规模较大时,神经网络学习速度会很慢。

    学习速度和偏导数大小相关,偏导数就是斜率(变化比例),斜率(变化比例)越大偏导数越大,学习速度越快。

    通过观察俩者的图像发现,当输入值越来越大时,曲线的斜率(变化比例)是越来越小的。

    为了解决这个问题,后来创造了 r e l u relu relu

  • r e l u relu relu:输入为正,斜率(变化比例)很大;输入为负时,就会输出 0,神经元就不会被激活 — 说明同一时间里,只有部分神经元会被激活,从而使得网络很稀疏,进而计算更高效,但没有斜率(变化比例)。

    应用首选,用的最多,但只在隐藏层使用。

    为了解决没有斜率(变化比例)的问题,又创造了一种激活函数:leaky relu。

  • leaky relu:relu 改进,leaky relu 的优点将 0 的梯度去掉,换成一个非0的梯度,比如0.1等,这样把 0 梯度变成一个很小不为 0 的梯度。

    sigmoid – leaky relu 都只能处理二元分类问题(是和否),有时候需要多分类,从二元判断到多元判断

  • softmax:N 元分类的输出层,输出层神经元要有 N 个。