一个简单神经网络的代码实现

用算法来预测下表的结果:

InputOutput
0 0 10
1 1 11
1 0 11
0 1 10

上表就相当于是训练数据,Output相当于标签(Label)。

import numpy as np

def nonlin(x, deriv=False):
    if True == deriv:
        return x*(1-x)
    return 1 / (1 + np.exp(-x))

train_data = np.array([[0, 0, 1],
                        [0, 1, 1],
                        [1, 0, 1],
                        [1, 1, 1]])

label = np.array([[0, 0, 1, 1]]).T

np.random.seed(1)

weights = np.random.random((3, 1))
weights = 2*weights- 1

for iter in range(1000):
    l0 = train_data
    l1 = nonlin(np.dot(l0, weights))
    
    loss = label - l1
    l1_delta = loss * nonlin(l1, True)

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

print(l1)

结果如下:
在这里插入图片描述
加大循环次数,可以更接近[0, 0, 1, 1]这个结果。

nonlin函数(sigmoid函数),参数deriv为True时,表示求导数,否则将一个实数转化为0到1间的一个数,也可以叫概率。

代码先随机初始化weights矩阵,这个矩阵中的每个元素表示了训练数据每组元素中的各个元素(如第一组(行)[0, 0, 1])与其结果[0](label矩阵的第一个元素)的权重关系。
代码详细解析
拿第一行数据为例:如下所示
在这里插入图片描述

下面对以上代码进行修改:

import numpy as np

def nonlin(x, deriv=False):
    if True == deriv:
        return x*(1-x)
    return 1 / (1 + np.exp(-x))

train_data = np.array([[0, 0, 1],
                        [0, 1, 1],
                        [1, 0, 1],
                        [1, 1, 1]])

label = np.array([[0, 1, 1, 0]]).T  # 仅修改此处

np.random.seed(1)

weights = np.random.random((3, 1))
weights = 2*weights- 1

for iter in range(1000):
    l0 = train_data
    l1 = nonlin(np.dot(l0, weights))
    
    loss = label - l1
    l1_delta = loss * nonlin(l1, True)

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

print(l1)

结果是什么呢?
在这里插入图片描述
期望[0, 1, 1, 0], 也就是我们的算法已经失去了效果。
为什么同样的代码(两层网络),仅修改了一点数据,就出现这么大的差异呢?
我们来对比一个前后两组数据。

InputOutput
0 0 10
1 1 11
1 0 11
0 1 10
InputOutput
0 0 10
1 1 10
1 0 11
0 1 11

其实可以理解为,第一组数据(Input与Output)是一个线性的关系, 所以我们才能用一个矩阵weights来计算三个权重,也就是说这三个权重对四组数据都是有效的,适用于四组数据。
而第二组数据就不是线性的关系了,其关系更为复杂(去掉第三列,然后一,二列异或运算),仅靠两层网络是处理不了的。
我们上面添加的那层仅能处理线性的关系。

解决方法就是再添加一层网络:代码如下:

import numpy as np                                                                   

def nonlin(x, deriv=False):
    if True == deriv:
        return x*(1-x)
    return 1 / (1 + np.exp(-x))

train_data = np.array([[0, 0, 1],
                        [0, 1, 1],
                        [1, 0, 1],
                        [1, 1, 1]])

label = np.array([[0], [1], [1], [0]])

np.random.seed(1)

weights1 = 2 * np.random.random((3, 4)) - 1 
weights2 = 2 * np.random.random((4, 1)) - 1 

for j in range(10000):
    l0 = train_data  # [4, 3]
    l1 = nonlin(np.dot(l0, weights1))  # [4, 4]
    l2 = nonlin(np.dot(l1, weights2))  # [4, 1]

    l2_loss = label - l2  # [4, 1]

    l2_delta = l2_loss * nonlin(l2, True)  # 不是矩阵乘法

    l1_loss = l2_delta.dot(weights2.T)
    l1_delta = l1_loss * nonlin(l1, True)

    weights1 += l0.T.dot(l1_delta)
    weights2 += l1.T.dot(l2_delta)

print(l2)

结果如下:

符合预期。

代码重点应该是在加的l1 = nonlin(np.dot(l0, weights1)) # [4, 4]这一层,这是一个4*4的矩阵。
我们先来看这个矩阵是怎么来的。
它是由train_data与weights1做矩阵乘法的结果。l1的每一个元素等于train_data的相应行(一组)与weights1的相应列,每个元素的权重点乘所得。
注意这里weights1是个3*4的矩阵,而文章开头的代码weights是3*1的。前面说过,前面的weights矩阵中的各个元素可以适用于所有四条数据(线性)。而这里weights1这个3*4的矩阵则是为每组训练数据都准备了一组权重(一共四组)。
也就是说,第二组数据虽然不是线性的关系,但是经过这一层处理后,其结果l1具备线性的关系,可以由我们文章开头的代码逻辑来处理了。可以看到,l2这层的处理与文章开头的代码基本是一样的,只不过l2是把l1作为输入的。
还有l1_loss计算那里,l1不像l2可以用label来计算损失,l1这一层在中间,是我们加上去的,它的损失值是根据l2的损失来计算的,l1是l2的前一层,l2的损失当然要l1来负责了,还要考虑l1与l2之间的权重,比如l1的a, b, c三个位置导致l2损失了0.1, 占比多的,多损失一点,不能a, b, c都损失0.1。

所以我们可以猜测,对于更加复杂的问题,n层网络处理不了,就再加一层,直到提取出来可以当作线性数据处理的结果,再交给类似上面代码总结输出,也就是深度学习(这里是我的猜测,没有查相关定义,可能不准确)。

本文是对该文的个人理解。
如有错误,欢迎指正。

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值