神经网络:请不要开始就让我sigmoid(wTx),谢谢!

数据处理 专栏收录该内容
17 篇文章 0 订阅

神经网络:请不要开始就让我sigmoid(wTx),谢谢!

引子

学习机器学习,翻阅了一些神经网络的文章,上来就是sigmoid ( 1 / ( 1 + e x p ( x ) ) 1/(1+exp(x)) 1/(1+exp(x)) )和WTX,让不少初学者摸不着头脑。一些市面上评价很高的机器学习的书也是直接开始sigmoid和收敛性的讨论,想踏进机器学习的门,确实还是少了点让初学者能看懂的资料。本篇只想抛砖引玉一下,希望那些大师们在牛逼地前进中给后人留下一条可以往上爬的线索;不要像中医一样,在中间的知识有了断层而最终越来越难让人读懂。本文拙陋,欢迎批评指正。

摸打滚爬

刚开始看机器学习的时候,真的是乱七八糟。
资料很多,举个例子就是11行实现神经网络的代码:
http://iamtrask.github.io/2015/07/12/basic-python-network/
和100来行就能实现简单8bit数字加法的循环神经网络:
https://iamtrask.github.io/2015/11/15/anyone-can-code-lstm/

惊呼一声:哇!就这么简单!11行代码可以实现2层的神经网络!
第0层输入,第1层隐藏层,第2层输出,每层都来个WTx (或者xW,这里的W指代11行代码神经网络里的syn0和syn1),外面再套一个sigmoid;这是预测阶段,也是Forward阶段。接着是Backward阶段,就是求出误差 Δ L 1 \Delta L1 ΔL1 Δ L 2 \Delta L2 ΔL2然后更新每层的参数。有没有人告诉我为什么WTx,为什么是sigmoid?

网上找找,基本上没有人提;大部分是对每层 Δ \Delta Δ的求解有详细推导说明。于是很长一段时间就是,activiate函数sigmoid随便用,不行了就换个tanh,relu试试,反正不求甚解最后收敛就行了。然而随着神经网络层数的增加,一切变得开始不容易收敛,试来试去发现所有方法都不可行,抓瞎了。

当想要控制一样东西,试来试去都没有效果的时候,最好的方法大概就是把它丢到九霄云外,因为能力还没有达到。所以,出去游玩一趟,拍拍美景,吃吃美食,写写歌曲,画画生活。该干嘛干嘛。

用三脚猫功夫刷图模型

宕开一笔,之后我们再梳理WTx。起初在神经网络之前,是被隐马可夫链吸引到机器学习的。因为以前写编译器的时候都是LL(k)的状态机在大脑里乱跑。既然是状态机,你就给我自己学习,自动把一个顶点连到另一个顶点呗。

马可夫链模型还是比较好理解的,一个状态,跳到另一群状态;对跳到某一个状态进行分析,就是这个状态下,输入的符号对应跳到下一个状态的频率,哪个最高就往哪跳。举起一个栗子:
编译器识别字符串,输入如果是:

hello"world"123

我们假定P是开始,S是字符串,X是其他,那么上面的输入经过识别得到的输出应该是:

XXXXXSSSSSSSXXX

这个例子如果在机器里学习10000遍;就有10000遍从o到"之后字符串开始,系统就会知道大概"是字符串的开始。到一下个1,系统就又会知道大概字符串到这里结束;当然,这个例子里系统会认为1是字符串的结束,而不是"。我们可以看下统计数据:

上一个状态当前状态次数字符
PX10000h
XX60000e, l, o, 2, 3
XS10000"
SS60000w, o, r, l, d, "
SX100001

所以频率N(P->X | h) = 100%, N(X->S | “) = 100%, N(S->X | 1) = 100%,按照经验,如果次数很多,频率可以近似表示概率p(P->X | h) 约等于 N(P->X | h),那么我们就可以把这个数据拿去为其他数据作标记。不过这样标记字符串是按照(”, 1)作开始和结尾的。要想让机器知道"是结尾,就得把所有字符都学习一遍"a, "b, "c, …, "A, … "1, "2, …

不管这个模型好不好,至少我们有了让机器自己学习的初步方法,就是计算频率。

隐马可夫链就是再复杂点,就是让一个观察到的标记可能对应不同的状态。还是用刚才的例子:

hello"world"123
XXXXXSSSSSSSXXX

在S->X上,把所有状态都学习一遍是要精心设计学习的数据的,有没有一劳永逸的办法呢?平时书里的天气那个例子没那么明显,隐状态和观察状态之间重叠比较多,看得有点晕;那么我们把这里的S这个状态分裂成两个隐藏状态S1和S2,就是说S1和S2对应的输出都是S,但是分成两个内部状态。“使X状态变为S1状态,标记字符串开始,就是” -> (X->S1);为了让"标记字符串结束," -> (S1->S2)且 任意字符->(S2->X)。这样隐状态和观察状态的意义就比较清晰了。后面就是找一本隐马可夫链的书,看一看viterbi找最大概率路径算法和Baum-Welch无监督学习算法。

当然,值得一提的是在隐马可夫链中,p(状态2 | 状态1, 符号)不容易算出来,所以很多时候会用到贝叶斯的全概率公式,说白了就是等式变换一下:
p ( 状 态 2 ∣ 状 态 1 , 符 号 ) = p ( 状 态 1 , 符 号 ∣ 状 态 2 ) p ( 状 态 2 ) / p ( 状 态 1 , 符 号 ) p(状态2 | 状态1, 符号) = p(状态1, 符号 | 状态2) p(状态2) / p(状态1, 符号) p(21,)=p(1,2)p(2)/p(1,)

上面扯了这么多,意义何在?在隐马可夫链的学习算法中用到全概率公式的知识,那么这种学习就是生成式;生成式需要大量数据去训练模型。而另一种叫作判别式,就是刚才说的p(状态2 | 状态1, 符号),虽然计算它不容易,但是想个近似的简单方法估计一下还是可能的,所以p(状态2 | 状态1, 符号)的计算在人为定义下和WTx产生了关系,这就是用线性模型去估计概率。

没学会走就开始跑,摔跤是自然

最初看隐马可夫链,直接就用起来了。对,就是那个Baum-Welch算法,管它三七二十一,直接把(word, tag)放在一个pair里,输入,然后训练,很长时间得不到结果…

耐不下性子等学习是个不好的习惯。可是突然就遇上了CRF(Conditional Random Field,条件随机场)。从一篇CRF的PPT里才知道sigmoid使用的由来:
http://www.cedar.buffalo.edu/~srihari/CSE574/Chap13/Ch13.5-ConditionalRandomFields.pdf
简单说来,就是对于把事物obj分成两类A和B的问题,我们一般会用全概率公式:
p ( A ∣ o b j ) = p ( o b j ∣ A ) p ( A ) p ( o b j ) = p ( o b j ∣ A ) p ( A ) p ( o b j ∣ A ) p ( A ) + p ( o b j ∣ B ) p ( B ) p(A | obj) = \frac{p(obj | A) p(A)} {p(obj)} = \frac{p(obj | A) p(A)} {p(obj | A) p(A) + p(obj | B) p(B)} p(Aobj)=p(obj)p(objA)p(A)=p(objA)p(A)+p(objB)p(B)p(objA)p(A)
变形以后:
p ( A ∣ o b j ) = p ( o b j ∣ A ) p ( A ) p ( o b j ∣ A ) p ( A ) + p ( o b j ∣ B ) p ( B ) = 1 1 + p ( o b j ∣ B ) p ( B ) p ( o b j ∣ A ) p ( A ) p(A | obj) = \frac{p(obj | A) p(A)} {p(obj | A) p(A) + p(obj | B) p(B)} = \frac {1} {1 + \frac{p(obj | B) p(B)} {p(obj | A) p(A)}} p(Aobj)=p(objA)p(A)+p(objB)p(B)p(objA)p(A)=1+p(objA)p(A)p(objB)p(B)1
我们再看看sigmoid函数:
s i g m o i d ( x ) = 1 1 + e x p ( x ) sigmoid(x) = \frac {1} {1 + exp(x)} sigmoid(x)=1+exp(x)1
就是了,人为定义:
p ( A ∣ o b j ) ≡ s i g m o i d ( W T x ) p(A|obj) \equiv sigmoid(W^{T}x) p(Aobj)sigmoid(WTx)
所以sigmoid原始是用于二分类问题的。
多分类问题,对于LR(Logistic Regression)一般就要使用softmax:
p ( A ∣ o b j ) ≡ e x p ( x ) Σ Y e x p ( y ) p(A|obj) \equiv \frac{exp(x)}{\Sigma _Y exp(y)} p(Aobj)ΣYexp(y)exp(x)
那么之后隐马科夫链模型也很好表述了:输入是(当前符号,前一个符号,前一个状态),第1层就是得到当前应该跳转到的状态,也就是在输入的前提下,当前符号该分在哪类里;第2层就是将这个状态标记为输出,就是当前确定的这个状态分在哪类,即对应了哪个标记。整个最简的表示的隐马可夫链也可以十几行代码搞定:

def trainer(m, x, y, rate):
   # x = [word] + [prev_word] + [prev_state]
   #   = [1,0,0] + [0,1,0] + [0,0,0,0,1,0,0]
   # y = [tag]
   #   = [0,1,0,0]
   L0 = np.array([x])
   L1 = softmax(np.dot(L0, m.S0))
   L2 = softmax(np.dot(L1, m.S1))
   e2 = np.array([y]) - L2
   dL2 = e2
   e1 = np.dot(dL2, m.S1.T)
   dL1 = e1
   m.S1 += np.dot(L1.T, dL2)*rate
   m.S0 += np.dot(L0.T, dL1)*rate
   return L1, L2

这么一写起来,其实就和最初提供的第二个超链接文章中RNN的形式很像了,这样RNN也就可以很轻松地理解了,可以继续深入RNN了。至于CRF的复杂形式,就是把WTx换成了某个定义好的函数和,称为势函数,并给每个势函数一个权值;这里也很明显了,可以跟小波分析对接上。至此一篇算是建立一个平滑台阶给初入门有点模糊基础并想往机器学习神经网络部分稍微深入一些的人。

痛,就要快乐前进

进入了概率和线性代数链接之后的阶段,就是考虑理解收敛了。连结数值分析的知识吧,二阶牛顿法和嗨森矩阵(竟然发现打出来是这个“嗨“ ovo),各种更快速的拟牛顿法,还有解决非线性拟合而出现的启发式搜索,ACO,PSO,遗传算法,DNA规划…继续奋斗吧!

2016.12.03
J.Y.Liu

  • 8
    点赞
  • 3
    评论
  • 1
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

评论 3 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

prog_6103

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值