关于梯度消失以及梯度爆炸

上一篇介绍了梯度下降法,对于一些浅层的神经网络来说,可以很容易看到效果,因为每一次迭代,我们都可以将误差降低。而对于深层次的神经网络来说,容易出现一个问题,那就是梯度消失以及梯度爆炸。

来看看这两个问题是怎么产生的。

以下内容参考了哈工大翻译的神经网络教程

为了弄清楚为何会出现消失的梯度,来看看一个极简单的深度神经网络:每一层都只有一个单一的神经元。下图就是有三层隐藏层的神经网络:

简单神经网络

这里,w_1,w_2... 是权重,而 b_1,b_2... 是偏置,C 则是某个代价函数。第j个神经元的输出 ,其中a_j=f(z_j)是激活函数,z_j = w_j*a_{j-1}+b_j 是神经元的带权输入,\delta_{i}为对应节点的残差。

为了揭示梯度消失和梯度爆炸的问题,我们计算\frac{\partial C}{\partial b_1},看看会发生什么。

接下来是计算过程。

结合上一篇文章【从梯度下降到反向传播(附计算例子)】的式子(4)、(6):

    \[ \frac{\partial J(W,b)}{\partial b_{i}^{l}}=\delta _{i}^{(l+1)}\qquad(4) \]

    \[ \delta _{i}^{(l)} = (\sum_{j=1}^{s_{l+1}}W_{ji}^{(l)}\delta_{j}^{(l+1)})\cdot {f}'(z_{i}^{(l)})\qquad(6) \]

式子(4)里面的J(W,b),也就是我们上面所说的代价函数C

由式子(4),可以得到:

    \[ \frac{\partial C}{\partial b_1}=\delta_2 \]

由式子(6),继续进行迭代计算

    \[ \begin{aligned} \frac{\partial C}{\partial b_1}&=\delta_2\\ &={f}'(z_1)w_2\delta_3\\ &={f}'(z_1)w_2{f}'(z_2)w_3\delta_4\\ &={f}'(z_1)w_2{f}'(z_2)w_3{f}'(z_3)w_4\delta_5 \end{aligned} \]

最后的delta_5,就是最后一个节点的残差,由链式求导法则可知:

    \[ delta_5= \frac{\partial C}{\partial a_4}{f}'(z_4) \]

代入上式,得到最终结果:

    \[ \frac{\partial C}{\partial b_1}={f}'(z_1)w_2{f}'(z_2)w_3{f}'(z_3)w_4\frac{\partial C}{\partial a_4}{f}'(z_4) \]

除了最后一项,该表达式是一系列形如w_j{f}'(z_j)的乘积,我们假设,这里的激活函数f用的是sigmoid函数,sigmoid的图像如下:

sigmoid函数图像

我们关注的是它的导数,其导数的图像为:

sigmoid函数导数图像

该导数在{f}'(0)=\frac{1}{4}时达到最高。现在,如果我们使用标准方法来初始化网络中的权重,那么会使用一个均值0标准差为1的高斯分布。因此所有的权重通常会满足\left | w_j \right |<1。有了这些信息,我们发现会有w_j{f}'(z_j)< \frac{1}{4}。并且在我们进行了所有这些项的乘积时,最终结果肯定会指数级下降:项越多,乘积的下降的越快。 这,就是梯度消失出现的原因。 同样的,如果我们选择不同的权重初始化方法、以及不同的激活函数,我们也有可能得到w_j{f}'(z_j)>1的结果,经过多层累乘,梯度会迅速增长,造成梯度爆炸。

因此,不稳定的梯度问题(梯度消失和梯度爆炸)产生的原因是:在前面的层上的梯度是来自后面的层上项的乘积。

当存在过多的层次时,就出现了内在本质上的不稳定场景。唯一让所有层都接近相同的学习速度的方式是所有这些项的乘积都能得到一种平衡。如果没有某种机制或者更加本质的保证来达成平衡,那网络就很容易不稳定了。简而言之,真实的问题就是神经网络受限于不稳定梯度的问题。所以,如果我们使用标准的基于梯度的学习算法,在网络中的不同层会出现按照不同学习速度学习的情况。

为了解决这个问题,有很多的策略,比如,nlp领域常用的lstm模型,能够解决梯度消失的问题。之后会继续介绍。

从梯度下降到反向传播(附计算例子)

梯度下降法(Gradient Descent)是神经网络的核心方法,用于更新神经元之间的权重,以及每一层的偏置;反向传播算法(Back-Propagation Algorithm)则是一种快速计算梯度的算法,从而能够使得梯度下降法得到有效的应用。

以前刚开始看神经网络的教程,一堆数学的公式、字母,看得头发昏。这学期上了模式分类的课,老师的ppt里面有计算的例子,随手算一算这些例子,再回过头去理解梯度下降和反向传播,就很容易了。所以今天我将结合具体的计算例子来谈谈它们。

在以下内容进行之前,你最好对神经网络里面的各个参数有个了解,特别是关于权重W的表达方式,不然下标容易搞混,具体可以参看【ufldl的神经网络教程】

先来直观的感受下这两个概念在神经网络里面的地位。

梯度下降法

所谓梯度,就是指向标量场增长最快的方向。

对于一个神经网络而言,我们的目标是为了找到最适合的权重,使得最终的输出和我们预期的输出相差不大,也就是说,问题可以转化为,找到适当的权重值,使得最终误差最小。而为了使得最终误差最小,我们就得利用梯度下降法,对连接每一个神经元的边的权重进行迭代更新,更新后的权重构成的神经网络,误差变小,多次迭代,直到我们满意(误差小于一个阈值)。

反向传播算法

利用梯度下降法,每次更新权重如下:

    \[  W_{ij}^l=W_{ij}^{l}-\alpha \frac{\partial J(W,b)}{\partial W_{ij}^{l}}\qquad (1) \]

    \[ b_{i}=b_{i}^{l}-\alpha \frac{\partial J(W,b)}{\partial b_{i}^{l}}\qquad (2) \]

其中,α为学习率,J(W,b)是我们定义的损失函数,通常是J(W,b)=\frac{1}{2}\left \| output - y \right \|^2,output为我们使用当前的权重计算出来的输出,y为训练数据的输出,用这个函数可以度量损失、误差。

从上面的式子可以知道,我们只要对每条边Wij计算出对应的\frac{\partial J(W,b)}{\partial W_{ij}^{l}},以及对每个偏置bi计算出对应的\frac{\partial J(W,b)}{\partial b_{i}^{l}},就可以对权重和偏置进行更新了。

反向传播算法,就是用来计算\frac{\partial J(W,b)}{\partial W_{ij}^{l}}\frac{\partial J(W,b)}{\partial b_{i}^{l}}完整的反向传播算法可以看这里,无非就是链式求导法则的应用,别被公式吓到了。下面的式子,就不给出推导过程了。

在反向传播算法里面,我们定义一个残差的概念,每一个节点都有一个残差,我们用\delta _{i}^{(n_{l})}表示第nl层,的第i个节点的残差,它的计算公式如下:

    \[ \frac{\partial J(W,b)}{\partial z_{i}^{(n_{l})}} \]

其中的z_{i}^{(n_{l})},是nl-1层网络对第nl层,第i个节点的输入和。

有了残差的这个概念,我们计算\frac{\partial J(W,b)}{\partial W_{ij}^{l}}\frac{\partial J(W,b)}{\partial b_{i}^{l}}就很方便了,经过链式求导法则的推导,我们最终可以得到以下计算公式:

    \[\frac{\partial J(W,b)}{\partial W_{ij}^{l}}=a_{j}^{(l)}\delta _{i}^{(l+1)}\qquad(3) \]

    \[ \frac{\partial J(W,b)}{\partial b_{i}^{l}}=\delta _{i}^{(l+1)}\qquad(4) \]

其中,a_{j}^{(l)})是使用当前权重和偏置前向计算得出的第l层、第j个输出值。

在这里停一下,我们把问题捋一捋。现在问题就转化为,只要我们能够计算到每一个节点的残差值\delta _{i}^{(n_{l})},那么根据(3)和(4),我们就可以计算出每一个\frac{\partial J(W,b)}{\partial W_{ij}^{l}}\frac{\partial J(W,b)}{\partial b_{i}^{l}},有了它们,就可以用(3)和(4)更新权重了。

所以,问题就转化为了求每一个节点的残差。以下的(5)、(6)两个式子,就解释了反向传播算法为什么要叫做反向传播算法。先直接给出公式。

对于最后一层输出层,残差为:

    \[\delta _{i}^{(n_{l})} = -(y_{i}-a_{i}^{(n_{l})})\cdot {f}'(z_{i}^{(nl)})\qquad(5) \]

其中y_{i}是训练样本(x,y)的第i个输出值,a_{i}^{(n_{l})})是使用当前权重和偏置前向计算得出的第l层(也就是这种情况下所说的输出层)、第i个输出值,z_{i}^{(nl)}则是nl-1层网络对第nl层,第i个节点的输入和。

有了最后一层各个节点的残差值,就可以利用它们计算前一层各个节点的残差值了,这也就是反向传播算法的精髓所在,计算公式如下:

    \[\delta _{i}^{(l)} = (\sum_{j=1}^{s_{l+1}}W_{ji}^{(l)}\delta_{j}^{(l+1)})\cdot {f}'(z_{i}^{(l)})\qquad(6) \]

式子(6)看上去有点复杂,我直接用文字描述一下:第l层的第i个节点A的残差=【【第l+1层所有和A有连接的节点的残差】乘以对应连接权重,最后求和】乘以节点A的激活函数的导数。

似乎越描越黑。没关系,最后,来个计算的例子,就会明白了。

反向传播算法计算例子

给出如下一个三层的神经网络(为了演绎计算过程,这个神经网络没有设置偏置b,如遇到有偏置的情况,也可以利用以上(1)-(6)的公式计算,是类似的。),并且假设f(a)=a(即这个函数的导数是1),损失函数为J(W,b)=\frac{1}{2}\left \| output - y \right \|^2,目标值为0.5,学习率α=0.5:

三层神经网络

我们来演绎一下,如何利用反向传播算法来更新权重。

首先用前向传播计算出每一个节点的值:

    \[ z_{1}^{2} = 0.35 \cdot 0.1 + 0.9 \cdot 0.8 = 0.755 \]

    \[ a_{1}^{2} = {f}(z_{1}^{2}) = 0.755 \]

    \[ z_{2}^{2} = 0.35 \cdot 0.4 + 0.9 \cdot 0.6 = 0.68 \]

    \[ a_{2}^{2} = {f}(z_{2}^{2}) = 0.68 \]

    \[ z_{1}^{3} = 0.3 \cdot 0.755 + 0.9 \cdot 0.68 = 0.8385 \]

    \[ a_{1}^{3} = {f}(z_{1}^{3}) = 0.8385\qquad (7) \]

计算这5个节点的残差(事实上第一层的残差不需要计算,我们也可以得到结果了,但为了演绎公式,我下面还是进行了计算)。

先从最后一个节点(输出节点)开始,由式子(5),得:

    \[ \delta _{1}^{(n_3)} = -(y_{1}-a_{1}^{(3)})\cdot {f}'(z_{1}^{(n3)}) \\ = -(0.5-0.8385)\cdot 1 = 0.3385 \\ \]

然后是倒数第二层,由式子(6),得:

    \[ \delta _{1}^{(2)} = (\sum_{j=1}^{1}W_{j1}^{(2)}\delta_{j}^{(3)})\cdot {f}'(z_{1}^{(2)})\\ =W_{11}^{(2)}\delta_{1}^{(3)}\cdot {f}'(z_{1}^{(2)})\\ =0.3\cdot0.3385\cdot1\\ =0.10155 \]

    \[ \delta _{2}^{(2)} = (\sum_{j=1}^{1}W_{j2}^{(2)}\delta_{j}^{(3)})\cdot {f}'(z_{2}^{(2)})\\ =W_{12}^{(2)}\delta_{1}^{(3)}\cdot {f}'(z_{2}^{(2)})\\ =0.9\cdot0.3385\cdot1\\ =0.30465\\ \]

最后是倒数第三层,也就是第一层,其实第一层是不用计算的,但是为了演示公式,这里还是计算一下第一层的第一个节点的残差,第二个节点就不算了。由式子(6),得:

    \[ \delta _{1}^{(1)} = (\sum_{j=1}^{2}W_{j1}^{(1)}\delta_{j}^{(2)})\cdot {f}'(z_{1}^{(1)})\\ =(W_{11}^{(1)}\delta_{1}^{(2)}+W_{21}^{(1)}\delta_{2}^{(2)})\cdot {f}'(z_{1}^{(1)})\\ =(0.1\cdot0.10155+0.4\cdot0.30465)\cdot1\\ =0.132015 \]

计算好所需要的残差\delta _{1}^{(n_3)},\delta _{1}^{(2)}\delta _{2}^{(2)}之后,我们就可以计算\frac{\partial J(W,b)}{\partial W_{ij}^{l}}了。

由式子(3),我们计算所有损失函数对W的偏导:

    \[ \frac{\partial J(W,b)}{\partial W_{11}^{1}}=a_{1}^{(1)}\delta _{1}^{(2)}\\ =0.35\cdot 0.10155\\ =0.0355425 \]

    \[ \frac{\partial J(W,b)}{\partial W_{21}^{1}}=a_{1}^{(1)}\delta _{2}^{(2)}\\ =0.35\cdot 0.30465\\ =0.1066275 \]

    \[ \frac{\partial J(W,b)}{\partial W_{12}^{1}}=a_{2}^{(1)}\delta _{1}^{(2)}\\ =0.9\cdot 0.10155\\ =0.091395 \]

    \[ \frac{\partial J(W,b)}{\partial W_{22}^{1}}=a_{2}^{(1)}\delta _{2}^{(2)}\\ =0.9\cdot 0.30465\\ =0.274185 \]

    \[ \frac{\partial J(W,b)}{\partial W_{11}^{2}}=a_{1}^{(2)}\delta _{1}^{(3)}\\ =0.755\cdot 0.3385\\ =0.2555675 \]

    \[ \frac{\partial J(W,b)}{\partial W_{12}^{2}}=a_{2}^{(2)}\delta _{1}^{(3)}\\ =0.68\cdot 0.3385\\ =0.23018 \]

之后,就可以更新权重了。

    \[ W_{11}^1=W_{11}^{1}-\alpha \frac{\partial J(W,b)}{\partial W_{11}^{1}} \\ =0.1 - 0.5\cdot0.0355425\\ =0.08222875 \]

    \[ W_{21}^1=W_{21}^{1}-\alpha \frac{\partial J(W,b)}{\partial W_{21}^{1}} \\ =0.4 - 0.5\cdot0.1066275\\ =0.34668625 \]

    \[ W_{12}^1=W_{12}^{1}-\alpha \frac{\partial J(W,b)}{\partial W_{12}^{1}} \\ =0.8 - 0.5\cdot0.091395\\ =0.7543025 \]

    \[ W_{22}^1=W_{22}^{1}-\alpha \frac{\partial J(W,b)}{\partial W_{22}^{1}} \\ =0.6 - 0.5\cdot0.274185\\ =0.4629075 \]

    \[ W_{11}^2=W_{11}^{2}-\alpha \frac{\partial J(W,b)}{\partial W_{11}^{2}} \\ =0.3 - 0.5\cdot0.2555675\\ =0.17221625 \]

    \[ W_{12}^2=W_{12}^{2}-\alpha \frac{\partial J(W,b)}{\partial W_{12}^{2}} \\ =0.9 - 0.5\cdot0.23018\\ =0.78491 \]

权重更新完毕,我们来验证一下效果是否有提升:

    \[ \begin{aligned} output &= a_{1}^3\\ &={f}(z_{1}^3)\\ &=f(0.17221625\cdot{f}(z_{1}^2)+0.78491\cdot{f}(z_2^2))\\ &=0.17221625\cdot{z}_{1}^2+0.78491\cdot{z}_2^2\\ &=0.17221625\cdot(0.35\cdot 0.08222875+0.9\cdot 0.7543025)\\&+0.78491\cdot(0.35\cdot0.34668625+0.9\cdot0.4629075)\\ &\approx 0.1219 + 0.4222\\ &=0.5441 \end{aligned} \]

目标值是0.5,权重未更新的时候,我们算出输出值为0.8385(计算过程在式子(7)),现在更新权重过后,算出来的输出值是0.5441,显然效果提升了,之前做的工作是有用的!

正则表达式在python中的使用

正则表达式,Regular Expression,是平时编程中经常用到的一种技术,用于匹配字符串。在爬虫、自然语言处理、文本分析里面经常会用到。严格来说,它可以看作是一种特殊的“编程语言”了,搞懂它,无论是java,python,php,都能轻松的应用。

关于正则表达式,教程有一大堆,也算是一项基本技能吧,想要熟练的掌握,那就得多写,说到底也是一种工具罢了。下面这些教程和文章都讲的比较清楚:

菜鸟教程:http://www.runoob.com/regexp/regexp-syntax.html

30分钟入门:http://deerchao.net/tutorials/regex/regex.htm

关于正则表达式,我这里不多说,有了正则表达式的基础,我就来整理一下python下正则表达式的用法,主要用例子来说明,争取做到拿来就能用。

在python中,正则表达式模块是re模块,使用直接import re即可。

1.基本函数

1.1 re.match函数

1.1.1 语法

re.match(pattern, string, flags=0)

1.1.2 参数

参数 描述
pattern 匹配的正则表达式
string 要匹配的字符串。
flags 标志位,用于控制正则表达式的匹配方式,如下表

可以使用多个标志位,用或连起来就可以,比如re.I | re.M,貌似也是这两个标志位比较常用。

标志位 描述
re.I 使匹配对大小写不敏感
re.L 做本地化识别(locale-aware)匹配
re.M 多行匹配,影响 ^ 和 $
re.S 使 . 匹配包括换行在内的所有字符
re.U 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。

1.2 re.search函数

1.2.1 语法

re.search(pattern, string, flags=0)

1.2.2 参数

re.match

1.3 re.findall函数

返回匹配的所有结果,是一个list

1.4 处理返回对象

1.4.1 用法

使用re.match或者re.search方法,如果没有匹配,返回None,如果匹配到了,则返回一个对象,可以通过以下方法对对象进行操作。

返回对象属性方法

1.5 match和search的区别

re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。

2. python例子

2.1 抓取网页中所有超链接

用这个正则表达式可以匹配超链接:<a.*?href=.*?<\/a>

(注释:这里,.是另一个元字符,匹配除了换行符以外的任意字符。*同样是元字符,不过它代表的不是字符,也不是位置,而是数量——它指定*前边的内容可以连续重复使用任意次以使整个表达式得到匹配。因此,.*连在一起就意味着任意数量的不包含换行的字符。)

代码如下:

import re
import urllib
url = "http://www.lookfor404.com"
result = urllib.urlopen(url).read()
reg = re.compile("<a.*?href=.*?<\/a>") #编译正则表达式

#findall方法
urls = reg.findall(s) #找到所有超链接
#和以下句子等价
#urls = re.findall(reg,s)
print len(urls) #打印超链接数量,urls是一个list

2.2 匹配网页标题

以我的博客为例,匹配网页标题,正则表达式我设置为:<title>.*?</title>,代码如下:

import re
import urllib
url = "http://www.lookfor404.com"
result = urllib.urlopen(url).read().decode('utf-8')
reg = re.compile("<title>.*?</title>") #编译正则表达式

#search方法
title = re.search(reg,result) #找到标题,匹配到第一个就行
#和以下句子等价
#title = reg.search(result)
print title.group(0) #打印匹配的内容

最后输出的结果是:
<title>找不到的博客 – 李鹏飞在这里写字</title>

2.3 找到特定的内容

仍然是2.2的例子,这次我不要html标签,只要标题,代码如下,其实只是正则表达式里面加了个括号:<title>(.*?)</title>

import re
import urllib
url = "http://www.lookfor404.com"
result = urllib.urlopen(url).read().decode('utf-8')
reg = re.compile("<title>(.*?)</title>") #编译正则表达式

#search方法
title = re.search(reg,result) #找到标题,匹配到第一个就行
#和以下句子等价
#title = reg.search(result)
print title.group(0) #打印匹配的内容
print print title.group(1) #打印匹配的内容,去除html

输出两行结果:
<title>找不到的博客 – 李鹏飞在这里写字</title>
找不到的博客 – 李鹏飞在这里写字

而在2.2中,如果执行print print title.group(1)则会出错。

2.4 匹配菜单内容

抓取网页里面的所有目录,查看网页源代码,我用这样的正则表达式来匹配:<li id=”menu-item-.*?<a href=.*?>(.*?)</a></li>
,其中的(.*?)就是我要提取的文字。代码如下:

import re
import urllib
url = "http://www.lookfor404.com"
result = urllib.urlopen(url).read().decode('utf-8')
reg = re.compile('
<li id="menu-item-.*?<a href=.*?>(.*?)</a></li>

') #编译正则表达式

#findall方法
menu = re.findall(reg,result) #找到标题,匹配到第一个就行
#和以下句子等价
#menu = reg.findall(result)
#打印menu
for item in menu:
    print item

最后输出如下:

程序输出

似乎输出的不完整,哦,因为有多级菜单,li标签里面还有li标签,这里只抓取了嵌套在最里面的菜单,仅作为用法例子,就不深究了。

以上例子属于比较常用的例子,并没有涉及到一些复杂的用法,之后碰到更多再继续更新上来。

apache为网站配置证书以支持https访问

腾讯云的主机有一个不错的服务,那就是可以免费申请证书,使网站支持https访问。众所周知,现在支持https访问是大趋势,安全性提升了很多。今天在腾讯云申请了一下证书,一下子就审核通过了。下面记录一下我在apache2下配置https的过程。

1.在腾讯云申请证书

地址:https://www.qcloud.com/product/ssl

直接为你的网站申请证书即可:域名型 DV SSL 证书免费申请!

免费版DVSSL证书

2.下载证书

等待腾讯的审核,不一会就会收到短信了。审核通过,就可以下载证书了。

下载地址:https://console.qcloud.com/ssl

下载是一个压缩包,里面有三个文件夹,分别对应三种服务器的证书文件:

三种证书文件

3.安装mod_ssl
如果apache没有安装ssl,则需要安装:
yum install mod_ssl openssl
安装完毕之后,在/etc/httpd/conf.d下,会生成一个ssl.conf文件。
4.在apache下配置https

登陆服务器,将证书文件夹apache里面的三个文件上传到/etc/httpd/conf下,即以下三个文件:

apache证书文件

编辑/etc/httpd/conf.d/ssl.conf

根据你的网站情况,在代码块内,修改基本配置,和80端口的虚拟主机是类似的。只是其中SSLCertificateFileSSLCertificateKeyFileSSLCertificateChainFile则对应步骤2中的3个证书文件:


DocumentRoot “/var/www/html”
ServerName www.domain.com
SSLEngine on
SSLCertificateFile /usr/local/apache/conf/2_www.domain.com.crt
SSLCertificateKeyFile /usr/local/apache/conf/3_www.domain.com.key
SSLCertificateChainFile /usr/local/apache/conf/1_root_bundle.crt

重新启动 Apache:service httpd restart

现在就可以用https访问了。

注:如果出现以下错误:

Starting httpd: (98)Address already in use: make_sock: could not bind to address 0.0.0.0:443
no listening sockets available, shutting down
Unable to open logs
[FAILED]

则可能是443端口被占用,用命令netstat -lnp|grep 443来查看是什么程序占用了这个端口,我的是因为装了vpnserver,所以关掉就行了。

官方文档:https://www.qcloud.com/document/product/400/4143

 

神经网络和深度学习的学习路线

从去年暑假开始接触神经网络,自己研究的方向是基于深度学习的自然语言处理,在这个过程中,接触了许多教程、论文、编程框架,断断续续,今天把前段时间学习过的内容、学习路线稍微总结一下,也为刚刚接触神经网络的朋友提供一些可借鉴的参考。

入门级教程

Neural Network and Deep Learning

说到神经网络入门的教程,首推哈工大SCIR的微信公众号的一个教程,这是他们团队从国外翻译过来的:【《神经网络与深度学习》连载】,已经连载更新完毕。如果你英文程度不错,也可以直接阅读英文原版【Neural Network and Deep Learning

这个教程的好处在于,它有实打实的源码例子供我们运行:https://github.com/mnielsen/neural-networks-and-deep-learning

它的例子是针对手写字体识别的,数据集是经典的MNIST数据。

UFLDL教程

UFLDL,全称为Unsupervised Feature Learning and Deep Learning,即无监督的特征学习和深度学习。这个教程是斯坦福大学出品,吴恩达主持出品,良心保证,有中英文。

英文教程地址:http://ufldl.stanford.edu/wiki/index.php/UFLDL_Tutorial

中文教程地址:http://ufldl.stanford.edu/wiki/index.php/UFLDL教程

内容比较多、比较杂,我个人是重点看了【神经网络】、【反向传导算法】、【softmax回归】以及【处理大型图像】的内容,【处理大型图像】主要讲的是卷积神经网络CNN,而CNN也能应用于自然语言处理。

总之,哈工大和斯坦福的这两个教程我是结合着一起看的,入门也花了点时间,结合代码看可能会理解更透彻。

自然语言处理教程

用深度学习来弄自然语言处理,首先要向量化文本,这里比较出名的是google出品的word2vec。它可以根据你给的大量语料,训练出一种词语表达方式,用一个几百维的向量来表示一个词语,这样,就方便了词语的数学化表征。

52NLP给出了用word2vec训练中英文维基百科的详细步骤:http://www.52nlp.cn/中英文维基百科语料上的Word2Vec实验

这个博客挺好的,有很多自然语言处理相关的文章。

深度学习教程

有了神经网络的基础,就可以继续看深度学习的模型了。基于自然语言处理,我看了cnn和rnn、lstm和gru这些模型。

CNN模型

我看了这篇论文:《Convolutional Neural Networks for Sentence Classification》,里面有实验,代码在这里:https://github.com/yoonkim/CNN_sentence,跑的是关于影评句子情感分类的例子。需要用到训练好的word2vec数据,https://code.google.com/archive/p/word2vec/ 有的下载,即页面上【Pre-trained word and phrase vectors】,是300维的、谷歌用100万亿左右单词的新闻预料训练好的词向量。

网上很多中文教程基本上都是翻译这篇论文的,可以结合着一起看。

RNN模型

rnn模型是公认的比较适合自然语言处理的模型。

首推【The Unreasonable Effectiveness of Recurrent Neural Networks】这篇博文,一个简单的rnn就能生成效果良好的语言模型,即,通过你给出的语料,训练出和你语料风格类似的句子、语言。作者用他的模型,训练了莎士比亚的作品、linux源码等等,都有令人惊讶的效果。

对应的源码在这里:https://github.com/karpathy/char-rnn  是用lua实现的,如果你对python比较熟悉,可以看:https://gist.github.com/karpathy/d4dee566867f8291f086 (貌似要翻墙)。由于是这个模型是char by char的,直接处理中文可能有问题,我作了一些更改,可以跑中文,训练了一下唐诗,挺好玩的,下次贴上来。

其次可以看RNN的系列教程:【RECURRENT NEURAL NETWORKS TUTORIAL】,有RNN,LSTM和GRU,也有对应的代码实现:https://github.com/dennybritz/rnn-tutorial-rnnlm ,是基于python的,有theano的实现过程。

另外,关于LSTM,【Understanding LSTM Networks】 这篇博文讲的很棒。

深度学习框架

我平时用python比较多,所以接触的框架基本上是python的。

theano

官方文档:http://deeplearning.net/software/theano/

配置是个麻烦事,多搜索,多尝试,总能配成功。

我对这个框架还不是很熟悉,它的编程模式和一般的不太一样,不过熟悉了这种框架之后,能很方便的对你的模型进行修改。

TensorFlow

官方文档:https://www.tensorflow.org/

中文文档:http://www.tensorfly.cn/

谷歌出品,没用过,暂不评价。不过强大。

keras

theano和tensorflow的高级封装,用python实现,适合快速实现模型。

官方文档:https://keras.io/

中文文档:http://keras-cn.readthedocs.io/en/latest/

 

还有很多别的框架,就不一一介绍了。

通过softether实现外网远程桌面连接校园网电脑

由于很多资料都放在了实验室的电脑(win10)上,而校园网是内网,回家之后脱离了校园网就不好办了,无法远程访问电脑。万般无奈,就想着用著名的的softether来做个内网的映射,加上一台公网服务器,就可以实现远程访问了。

公网的服务器可以到阿里云或者腾讯云搞一台,针对学生有优惠,我就是腾讯云,每个月1元,当然带宽只有1M。

下面记录一下折腾过程。

(我的应用场景:在家里用win10的电脑,需要连接在校园网内网实验室的win10电脑,用一台centos6服务器当作中转server)

首先要下载如下软件:

Softether-client
Softether-server-for-windows
Softether-server-for-linux
Softether-servermanager

需要下载的软件

服务器端设置

在服务器下安装依赖:
yum -y install gcc zlib-devel openssl-devel readline-devel ncurses-devel

将文件softether-vpnserver-v4.21-9613-beta-2016.04.24-linux-x64-64bit.tar.gz 上传到服务器(版本可能不同,我下载的是2016年11月9号的版本)

解压:
tar -zxvf softether-vpnserver-v4.21-9613-beta-2016.04.24-linux-x64-64bit.tar.gz

解压完,会有一个vpnserver的文件夹:

vpnserver文件夹

进入这个文件夹:
cd vpnserver

输入安装命令:
./.install.sh

然后会问你要不要看用户须知,键入1回车查看,2不看。不要选择2,选了就会中止安装。

会问你3次,每次都同意(输入1回车)就好。

安装成功。

softether服务端安装成功

还是要在刚才的vpnserver文件夹下,输入以下命令开启vpn:
./vpnserver start

屏幕显示:

The SoftEther VPN Server service has been started.

则开启成功。

另外,关闭命令是:
./vpnserver stop

然后,赶紧设置一下密码:
./vpncmd
出现以下提示,选1。
设置密码

之后如下图,回车即可,即默认设置server为本台服务器,端口8888。
softether设置默认server

出现下图,继续回车:
继续回车

然后输入ServerPasswordSet,继续设置密码:
继续设置密码

最后键入exit,退出,linux服务器端配置完毕。

目标windows主机配置

同样,要先安装softether server的windows版,默认安装即可:
softether-server的windows版

安装完毕,会弹出管理器,可以看到,它默认添加了本地的主机:
softether-server桌面管理器

(如果没有这个管理器,可以到网上去下载Softether-servermanager,默认应该是有的)

然后点击“新设置”,把刚才linux服务器添加进来,在主机名填写服务器的公网ip就行,右下角输入刚才服务器端设置的管理密码:
添加linux服务器

连接上刚刚设置的这个linux上的server,关掉弹出的设置界面,点击“创建虚拟HUB”:
创建虚拟HUB

设置一下名称和密码:
新的虚拟HUB

创建完毕后,管理这个hub,点击管理用户,然后点左下角新建一个用户,设置账号密码:
新建HUB用户

添加用户成功。

仍然在管理hub的界面,找到右下角的虚拟NAT和虚拟DHCP服务器:
虚拟NAT和虚拟DHCP服务器

点击SecureNAT配置,Mac地址会自动填充,其它的和下图设置一样即可:
SecureNAT配置

然后要“启用SecureNAT”。

退出,连接本地的server,会让你设置密码:
设置localhost密码

然后管理localhost下的default hub,找到左下角的“管理级联连接”:
管理级联连接

新建一个连接,填写刚才的linux服务器的信息、新建的那个hub,以及新建的用户名密码。然后点击“在线”,开启它:
在线新建连接

回到default hub的管理,点击右下角的“虚拟NAT和虚拟DHCP服务器”,还是点击SecureNAT配置,除了mac地址默认不用改,其它如图设置:
再次设置SecureNAT

然后要“启用SecureNAT”。搞定

外网连接内网电脑

到外网电脑上,安装softether-client。

打开client,添加新的vpn连接:
softether-client添加新的vpn

输入新建的vpn名,创建完成之后,双击这个vpn:
双击vpn

填写配置,连接到linux和对应的hub上,填写之前设置的用户和密码。

然后连接这个vpn,就成功了。现在你就可以上校园网了,远程也可以连接到学校的电脑了。(直接用windows自带的远程桌面连接,连接内网ip地址都可以了)

记近期看的两部电影

昨天晚上,关于豆瓣,炸了。

事情的原由是这样的:《中国电影报》12月27日发布题为“豆瓣电影评分,面临信用危机”的文章,随后人民日报客户端转发了该文,并将标题改为“豆瓣、猫眼电影评分面临信用危机,恶评伤害电影产业”。

然后大家就炸了。人民日报、广电总局、央媒统统这些体制内的机器轮番被舆论轰炸,《豆瓣,挺住》一文刷爆朋友圈……后来的反转,人民日报评论部赶紧出来说《中国电影,要有容得下“一星”的肚量》,人民日报海外版发文《自信的国度应该容下无数个豆瓣》。似乎是,人民日报想赶紧出来说,我不想日人民了。

关于电影,大家的话语权越来越多,每个人都有意见,我看这是压不住了。

哦,记两部最近在电影院看的电影。

不见罗曼,只有迪克

“不见罗曼,只有迪克。”看到《罗曼蒂克消亡史》的这个评论笑出声来。彼时的我和尊在一阵寒风凛冽后等待前面还有几十桌的牛肉火锅,无聊间刷起了豆瓣的影评,消化一下刚刚没看懂的电影。

但不得不承认,这是一部很有导演个人特色的电影,也是一部能称得上优秀的国产电影。所谓的“中国版教父”的确是个不大不小的噱头,它没有“I believe in America”的开头,也没有michael corleone在参加饭局枪杀对桌前的演技爆发,但是它有葛大爷直起来的腰板,还有美的不可一世的阿娇。

我无语,是因为看完的沉重感,这部电影实在是不适合两个好基友2年没见然后一起打完实况之后观看。可能吃吃爆米花,看个《长城》会更好。

我后来发现,它的影片英文名the wasted times有点意思。如果看漏了s,那是观影感受,如果没看漏,那就去拥抱沉重的历史长河吧。

用张嘉佳的镜头,摆渡王家卫的过去

我觉得去看这部电影的人分几种期待:1.都市鸡汤烹饪者张嘉佳会以怎样一种姿态将文字拍成电影。2.王家卫梁朝伟金城武再来重庆森林走一趟?3.吃个爆米花笑哈哈。

差评就不多说啦,网上的评价一大堆。

看的粤语版,所谓粤语版,就是将一部分国语配音重新配成了粤语,梁朝伟全程话痨旁白,还夹杂着他的一些粤语口头禅,《无间道》是谁在敲打我窗,《东成西就》香肠嘴,《重庆森林》的凤梨都是明显的致敬。稍稍有点勾起重庆森林给我的启蒙。

用张嘉佳的镜头,摆渡王家卫的过去,大失所望也好,强颜欢笑也罢,都化成了信息流啦。

当我们在看电影的时候,我们在看些什么。

centos下apache+django配置多个网站

之前就已经写过一篇《在linux服务器上部署django项目》,在那篇博文里,我用的操作系统是ubuntu的,而且没有配置域名,直接在一台服务器下放的一个网站,而且用的是mod_wsgi的全局模式。今天的这篇,我将使用mod_wsgi的daemon mode,而且配置多个网站–一个域名对应多个网站。

我的环境:

centos6.7
Apache/2.2.15
python2.7.12
django1.9.7
mod_wsgi4.5.3

本文的前提是环境已经配好了,如果没有配好,可以参考《在linux服务器上部署django项目》。

接下来,以2个django项目,1个php项目为例,进行网站的配置。

2个django项目的地址:

/mywebsite/django1
/mywebsite/django2

php项目的地址

/mywebsite/myphp

使用mod_wsgi的daemon mode

这个例子,我们将第一个django项目配置到服务器上,使用mod_wsgidaemon mode

对应的httpd.conf文件应该这么写:

<VirtualHost *:80>
     ServerName lookfor404.com           # 域名 
     WSGIDaemonProcess django1 python-path=/mywebsite/django1
     WSGIProcessGroup django1
     WSGIScriptAlias / /mywebsite/django1/yourproject/wsgi.py   #在 /mywebsite/django1 下,project和app在同一级目录
    <Directory /mywebsite/django1/yourproject>
       <Files wsgi.py>
          Order Deny,Allow
          Allow from All
       </Files>
    </Directory>

   #别忘了静态文件配置,这个和一般的php项目是类似的
    Alias /static "/your/path/collectedstatic"
    <Directory "/your/path/collectedstatic">
        Order deny,allow
        Allow from all
    </Directory>
</VirtualHost>

但是据测试,有时候可能会出现错误,在log里面提示如下:

Permission denied: mod_wsgi (pid=10458): Unable to connect to WSGI daemon process ‘lookfor404’ on ‘/etc/httpd/logs/wsgi.10453.0.1.sock’ as user with uid=11.

逛了半天的stackoverflow,最终看到一个靠谱的答案提到这个网址,
https://code.google.com/archive/p/modwsgi/wikis/ConfigurationIssues.wiki#Location_Of_UNIX_Sockets

解决方法就是,在virtualhost外面加多一句
WSGISocketPrefix /var/run/wsgi

一个域名对应多个网站

接下来,看看怎么用一个域名配置多个网站。

比如,我想要的效果是这样的:访问lookfor404.com/django1,进入django1这个项目;访问lookfor404.com/django2,进入django2这个项目;访问lookfor404.com/myphp,进入myphp这个项目。同样的,直接看配置文件。为了看起来清楚,我把静态文件设置先忽略了。

#----------django1项目配置
<VirtualHost *:80>
     ServerName lookfor404.com           # 域名 
     WSGIDaemonProcess django1 python-path=/mywebsite/django1
     WSGIProcessGroup django1
     #注意,以下发生变化,即第二个参数为访问的路径
     WSGIScriptAlias /django1 /mywebsite/django1/yourproject/wsgi.py   
    <Directory /mywebsite/django1/yourproject>
       <Files wsgi.py>
          Order Deny,Allow
          Allow from All
       </Files>
    </Directory>
</VirtualHost>

#----------django2项目配置
<VirtualHost *:80>
     ServerName lookfor404.com           # 域名 
     WSGIDaemonProcess django2 python-path=/mywebsite/django2
     WSGIProcessGroup django2
     #如下,即访问url为lookfor404.com/django2
     WSGIScriptAlias /django2 /mywebsite/django2/yourproject/wsgi.py   
    <Directory /mywebsite/django2/yourproject>
       <Files wsgi.py>
          Order Deny,Allow
          Allow from All
       </Files>
    </Directory>
</VirtualHost>

#----------myphp项目配置
<VirtualHost *:80>
     ServerName lookfor404.com   # 域名 
     DocumentRoot /mywebsite/myphp
     <IfModule alias_module>
          #设置访问url为lookfor404.com/myphp
          Alias /myphp "/mywebsite/myphp"
         <Directory "/mywebsite/myphp">
          AllowOverride all
          Order allow,deny
          Allow from all
         </Directory>
      </IfModule>
</VirtualHost>

总结来说,这种配置适用于你只有一个域名,而你有多个网站程序,而且你不使用子域名。对于django项目,通过WSGIScriptAlias的第一个参数就可以设定访问的域名子路径了,对于php项目,使用Alias即可解决问题。

另外,多个域名多个网站这种情况就不说了,直接在servername里设置就行。

django项目出现WSGI Bad Bad Request (400) 错误

最后还要说一下这个错误,明明之前一切配置都顺利,用ip访问也没问题,但是一用域名访问就出错了,原来是新版django的问题,需要到setting.py里面设置可访问的域名:

ALLOWED_HOSTS = [
‘lookfor404.com’, # 只允许主域名
‘.lookfor404.com’, # 允许主域名以及子域名
‘.lookfor404.com.’, # 允许FQDN以及子域名
]

然后重启一下apache就行了。

 

在Centos6.7中将python升级成2.7版本

在Centos6.7中将python升级成2.7版本,网上有一大堆文章,但是要知道,没有永远正确的配置,每台机子,每个软件版本可能都不一样,所以要针对自己的问题,记录,找出对应的原因,才能以不变应万变啊。

不废话了,我这里记录一下在64位centos6.7中将python2.6.6升级为python2.7.12的过程。

1.安装devtoolset

devtoolset可以帮助我们解决gcc编译的问题,而在yum中,我们直接使用groupinstall,就能把工具组给安装了,很方便,输入以下命令:

yum groupinstall "Development tools"

2.安装编译Python需要的包:

yum install zlib-devel
yum install bzip2-devel
yum install openssl-devel
yum install ncurses-devel
yum install sqlite-devel

3.下载python2.7.12:

wget https://www.python.org/ftp/python/2.7.12/Python-2.7.12.tgz

4.解压:

tar -xvf Python-2.7.12.tgz

然后进入该目录:

cd Python-2.7.12

5.开始编译安装三部曲

./configure --prefix=/usr/local --enable-shared
make
make install

如果遇到错误:

error while loading shared libraries: libpython2.7.so.1.0: cannot open shared object file: No such file or directory

那就新建一个文件:

vim /etc/ld.so.conf.d/python2.7.conf

加入以下内容:

/usr/local/lib

保存退出后运行命令读取配置:

ldconfig

然后再重新编译安装三部曲即可。

看到网上很多教程有说,安装完新版本的python,系统还是用的默认的旧版本,所以需要更改软连接,还有yum的设置。但在我这里并没有遇到,我是可以直接用了,而且yum也正常。可能和python的版本有关,如果你在安装完之后出现,运行python,发现版本仍然是默认的版本,那么你就需要更改软连接了,更改完软连接之后,要检查yum是否正常。

6.安装pip

在windows下,装完python2.7.12会默认把pip也装上。但在linux上有所不同,至少我在完成以上步骤之后,运行pip并没有反应。所以还需要安装pip。直接用yum神器:

yum install python-pip

7.安装python-devel

输入命令安装就行:

yum install python-devel

这就基本上就完成了python的升级。

centos+apache安装wordpress

上一篇《折腾记录:将wordpress搬家到腾讯云》讲到wordpress服务器搬家的坑,但是没有细说怎么安装,这一篇就记录一下怎么在centos上面安装使用wordpresss吧。以下的过程是基于centos6.7的,如软件安装有版本出入(ubuntu里面的apache是apache2,不是httpd),需要自行调整。

1.安装apache

在centos下,一般都使用yum来管理安装包,很方便。安装apache非常简单,输入以下命令即可:

yum install httpd
yum install httpd-devel

开启apache服务:

service httpd start

默认会显示如下提示:

Starting httpd: httpd: Could not reliably determine the server’s fully qualified domain name, using 127.0.0.1 for ServerName

此时需要去更改配置:

vim /etc/httpd/conf/httpd.conf

#ServerName www.example.com:80改为ServerName localhost:80

保存,退出,重启apache服务
service httpd restart

最后再设置一个开机自动启动:
chkconfig httpd on

ok,在浏览器输入服务器的ip,显示如下页面即为安装成功:

apache安装成功

2.安装mysql

输入命令安装mysql-server、mysql和mysql-devel:

yum install -y mysql-server mysql mysql-devel

安装成功,如下图:

mysql安装完成

查看一下mysql的版本:

rpm -qi mysql-server

mysql版本-1

启动mysql服务:

service mysqld start

设置mysql的超级管理员root的密码,引号不要删,引号内为你的密码:
/usr/bin/mysqladmin -u root password 'new-password'

或者在mysql登陆状态下,输入:
use mysql;
update user set password=password('密码') where user='root';
flush privileges;

设置mysql开机启动:
chkconfig mysqld on

如果你还需要在远程的软件操作mysql,那就要开启远程操作权限,在mysql命令行下,输入:
grant all privileges on *.* to 'root'@'%' identified by '密码' with grant option;
flush privileges;

3.安装php

输入命令:

yum install php

安装php

安装php-mysql:
yum install php-mysql

安装常用的php组件:
yum install php-gd php-imap php-ldap php-odbc php-pear php-xml php-xmlrpc

安装常用的php组件

重启apache:
service httpd restart

搞定。

4.安装配置wordpress

4.1创建wordpress数据库

先创建一个数据库,用来存放wordpress的数据。可以在navicat-for-mysql里面创建,也可以在登陆mysql之后,用命令行创建(指定utf-8字符集):
CREATE DATABASE `wordpress` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

创建个专门的用户管理这个数据库(username和password改为你要的用户名和密码):
CREATE USER 'username'@'localhost' IDENTIFIED BY 'password';

将wordpress数据库的所有管理权限交给这个用户:
GRANT ALL ON wordpress.* TO ‘username’@’localhost’;

ok

4.2下载、安装、配置wordpress以及数据库

下载wordpress,可以去官网下载,然后上传到服务器,也可以直接用命令下载,我这里用命令下载(以下是英文版,中文版的话将url改成https://cn.wordpress.org/latest-zh_CN.tar.gz):
wget http://wordpress.org/latest.tar.gz --no-check-certificate

解压到指定的目录下:
tar -zvxf latest.tar.gz -C /website/wp-test

cd进入解压后的文件目录,将wp-config-sample.php复制、重命名为wp-config.php:
cp wp-config-sample.php wp-config.php

修改wp-config.php:
vim wp-config.php

更改数据库的相关设置,更改红色字体处为你自己的数据库设置:

/** 数据库名 */
define(‘DB_NAME’, ‘wordpress‘);

/** mysql用户名 */
define(‘DB_USER’, ‘username‘);

/** 该用户对应的密码 */
define(‘DB_PASSWORD’, ‘password‘);

/** 主机名,默认无需修改 */
define(‘DB_HOST’, ‘localhost’);

/** 字符集 */
define(‘DB_CHARSET’, ‘utf8‘);

/** The Database Collate type. Don’t change this if in doubt. */
define(‘DB_COLLATE’, ”);

4.3apache虚拟主机设置

新建一个虚拟主机配置:
vim /etc/httpd/conf.d/virtual.conf

输入以下配置,记得要先将域名解析到主机的ip地址来:

##wordpress目录
DocumentRoot /website/wp-test/wordpress

ServerName lookfor404.com #你的域名

ServerAlias www.lookfor404.com #域名别名

#开启rewrite功能
<Directory “/website/wp-test/wordpress”>

AllowOverride ALL

Order allow,deny

Allow from all

 

以上配置完成,访问域名,即可开始安装过程。
wordpress安装过程

设置一下账号密码,就可以开启wordpress了。