11行Python代码编写神经网络

参考网址:http://iamtrask.github.io/2015/07/12/basic-python-network/

先上代码
X = np.array([ [0,0,1],[0,1,1],[1,0,1],[1,1,1] ])
y = np.array([[0,1,1,0]]).T
syn0 = 2*np.random.random((3,4)) - 1       #随机初始化权重
syn1 = 2*np.random.random((4,1)) - 1       #随机初始化权重
for j in xrange(60000):                    #迭代次数
    l1 = 1/(1+np.exp(-(np.dot(X,syn0))))    #正向传播
    l2 = 1/(1+np.exp(-(np.dot(l1,syn1))))   #正向传播
    l2_delta = (y - l2)*(l2*(1-l2))         #反向传播
    l1_delta = l2_delta.dot(syn1.T) * (l1 * (1-l1))   #反向传播
    syn1 += l1.T.dot(l2_delta)              #更新权重
    syn0 += X.T.dot(l1_delta)               #更新权重
看起来是不是很简单呢,这11行代码就构建了一个3层的神经网络(一个输入、一个输出、一个隐层),下面两张图可能帮你理解上面代码中的公式含义


                         左图:神经网络形式(单一样本)                                             右图:反向传播公式

如果没看懂,没关系,作者进行了分布讲解:
训练反向传播神经网络的目的是为了根据输入预测输出结果
考虑使用给定三个输入数据预测输出数据, 我们可以通过简单地测量输入值和输出值之间的统计信息来解决这个问题。 如果我们这样做,我们将看到最左边的输入列与输出完全相关。 这就是反向传播最简单的作用形式,下面让我们开始求解吧。

(1)2层网络
import numpy as np

# sigmoid 函数,也可以求导用
def nonlin(x,deriv=False):
    if(deriv==True):
        return nonlin(x)*(1-nonlin(x))
    return 1/(1+np.exp(-x))
    
# input dataset
X = np.array([  [0,0,1],
                [0,1,1],
                [1,0,1],
                [1,1,1] ])
    
# output dataset            
y = np.array([[0,0,1,1]]).T

# seed random numbers to make calculation
# deterministic (just a good practice)
np.random.seed(1)

# 随机初始化均值为0的权重,目的是打破对称性,否则更新权重值可能相同
syn0 = 2*np.random.random((3,1)) - 1

for iter in xrange(10000):

    # forward propagation
    l0 = X
    l1 = nonlin(np.dot(l0,syn0))

    # how much did we miss?
    l1_error = y - l1

    # multiply how much we missed by the 
    # slope of the sigmoid at the values in l1 反向传播公式
    l1_delta = l1_error * nonlin(l1,True)

    # update weights
    syn0 += np.dot(l0.T,l1_delta)

print "Output After Training:"
print l1

Output After Training:
[[ 0.00966449]
 [ 0.00786506]
 [ 0.99358898]
 [ 0.99211957]]

变量含义:
结果表明神经网络工作了,最好可以自己编程尝试一下,有几点值得关注:
(1)迭代前后l1的变化
(2)观察nonlin函数,它给出了概率预测
(3)迭代过程中l1_error的变化(是否在逐渐变小呢)
(4)BP的大部分秘密都在下面这行代码中
l1_delta = l1_error * nonlin(l1,True)
(5)所做的一切努力都是为了下面这行代码
syn0 += np.dot(l0.T, l1_delta)

代码逐行解析:

(1)第一行载入numpy函数库

(2)第四行定义sigmoid函数,取值[0,1],用它来表示概率

(3)第五行定义sigmoid导数,如果deriv == True,那么返回out*(1-out) ,out = sigmoid(z)

(4)第十行使用numpy中的矩阵初始化数据,每一行代表一个数据,每一列对应着神经网输入节点,本例中有4个数据,3个输入节点

(5)第十六行初始化输出数据,T代表转置,y具有4行1列,与输入数据相对应。因此对于每个样本,有3个输入,1个输出。

(6)第二十行随机初始化权重,每次训练后仍会随机分布,更利于观察神经网络的变化(打破对称性)。

(7)第二十三行syn0为连接权重,因为只有输入层和输出层,因此只需要一个权重矩阵。其尺寸为3*1是因为我们有3个输入,一个输出。syn0代表了这个神经网络的全部参数,神经网络的性能与输入数据和输出数据无关。

(8)第二十五行开始训练网络,设定训练次数

(9)第二十八行初始层l0就是我们的输入数据,我们有4个数据,更新神经网络将会全部使用(而不是使用某几个),这称为"full batch"。

(10)第二十九行正向传播过程,也就是预测过程。首先是根据连接权重计算输出(l0*sy0),(4*3)dot (3*1)= (4*1)。再对四个样本计算出的结果使用sigmoid函数预测其为1的概率。

(11)第三十二行计算每个结果的预测结果和实际结果相差多少,就是误差

(12)第三十六行就是反向传播公式了,用来更新权重系数(最重要的),分a,b部分进行讲解

a.首先看看nonlin(l1,True),它代表sigmoid函数的梯度:

可以看到当x->0时,数值较大,越偏离0,这个数值越小

b.再来看这行代码

 l1_delta = l1_error * nonlin(l1, True)

l1_error是4*1,nonlin(l1,True)是4*1,*为对应元素相乘,l1_delta仍为4*1。它这种做法的含义在于减小高可信样本的预测误差。什么意思呢?当某一样本预测结果l1接近1或者0时都代表着神经网络高度肯定这是个正例或者反例,即再下次更新时该样本的误差权重应该低一些(对更新weight系数作用较小),其表现在nonlin(l1,True)返回的值较低,若l1接近0.5,即判断正例的概率为0.5,完全不确定嘛,应该加大下次更新时其误差权重(对更新weight系数作用较大),表现在nonlin(l1,True)返回值较大。

(13)第三十九行更新神经网络,首先看单一样本

更新公式:

weight_update = input_value * l1_delta
从左到右进行更新,第一个元素就是1*l1_delta,这个更新值不会使得新的SYN0(1)偏离9.5太远,为什么呢?因为预测值为0.99,说明神经网络充分自信,此时显然l1_error、nonlin(l1,True)都比较小,因此不会怎么改变原始权重系数。由于更新时会使用所有样本,过程为求和取平均,因此实际的更新过程如下:

 因此,简单点说,反向神经网络更新过程就是根据样本预测的正确性,不断调整样本误差的权重,即更新weights时不同样本的作用权重不同,直至网络尽量拟合所有样本。


(2)复杂一点的问题(3层网络)
网络如果只有输入层和输出层的话(2层)之没有办法解决非线性问题的,例如非常简单异或问题:
因此要引入多层神经网络(包含隐层),例如本文开头的那段小程序对应的网络结构啦。


正向传播的过程就是输入4*3的数据,乘上第一层的权重矩阵3*4,得到隐层矩阵l1(4*3)dot(3*4) = (4*4),如上图所示,再乘第二层权重矩阵(4*1),得到输出l2(4*4)dot(4*1) = (4*1),当然都没考虑sigmoid函数,只是表明这个过程。

对于反向传播过程,如果你已经理解了两层网络的权重更新过程,三层也是相似,每一层权重更新都和其更深一层的误差息息相关。
代码:
import numpy as np

def nonlin(x,deriv=False):
	if(deriv==True):
	    return x*(1-x)

	return 1/(1+np.exp(-x))
    
X = np.array([[0,0,1],
            [0,1,1],
            [1,0,1],
            [1,1,1]])
                
y = np.array([[0],
			[1],
			[1],
			[0]])

np.random.seed(1)

# randomly initialize our weights with mean 0
syn0 = 2*np.random.random((3,4)) - 1
syn1 = 2*np.random.random((4,1)) - 1

for j in xrange(60000):

	# Feed forward through layers 0, 1, and 2
    l0 = X
    l1 = nonlin(np.dot(l0,syn0))
    l2 = nonlin(np.dot(l1,syn1))

    # how much did we miss the target value?
    l2_error = y - l2
    
    if (j% 10000) == 0:
        print "Error:" + str(np.mean(np.abs(l2_error)))
        
    # in what direction is the target value?
    # were we really sure? if so, don't change too much.
    l2_delta = l2_error*nonlin(l2,deriv=True)

    # how much did each l1 value contribute to the l2 error (according to the weights)?
    l1_error = l2_delta.dot(syn1.T)
    
    # in what direction is the target l1?
    # were we really sure? if so, don't change too much.
    l1_delta = l1_error * nonlin(l1,deriv=True)

    syn1 += l1.T.dot(l2_delta)
    syn0 += l0.T.dot(l1_delta)

Error:0.496410031903
Error:0.00858452565325
Error:0.00578945986251
Error:0.00462917677677
Error:0.00395876528027
Error:0.00351012256786

翻译中包含了一些自己的理解~
因此如果有问题的话,敬请指正!

  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值