原创

“神经网络”?什么鬼?(一)


1.问题的引出

【问题】:4个好朋友一起出去玩,“事件0”代表“不去”,“事件1”代表“去”,通过前几组数据推测小强第5次去不去?

如花小倩小明小强
0010
1111
1011
0110
110

【答案】:1

【解答】:通过前面4组数据,不难发现小强“去”与“不去”直接取决于如花。如果如花“去”,则小强“去”;如果如花“不去”,则小强“不去”。

【分析与引入】:这种类似规律探索的问题,我们幼儿园、小学一年级经常遇到。经过我们肉眼观察或者动笔运算找到事物现象的某种规律,进而推测出待求数据的结果。这个短暂的思考过程,我们的大脑(大约1000亿个神经元)在快速找到规律,帮助我们找到答案。如果我们按照这个思路来编写程序,那么无疑是下面这样的:if(如花==1) 小强=1;else 小强=0;但是,如果问题变得复杂,并不是这种死代码可以判断出的,如下图:

如花小倩小明小强
1011
1101
0011
0100
110

当变成这种复杂情况或者遇到大量的数据集、多特征的数据集,刚刚的代码便得修改,重新通过人脑来寻找新的规律,这个过程会非常的困难!

【总结】:基于神经网络的机器学习相当于“自己会学习的算法”!

2.神经网络的基本原理

不断试错、不断改正、找到正解!

avatar 1.初始化权重w1、w2、w3 -------> 2.按照权重计算结果; -------> 3.计算误差; ------->4.根据误差调整权重; -------> 5.重复以上步骤n次; -------> 6.训练完成; ------->7.进行预测

【机器学习的目标】:找系数!

【怎么找】:不断试错

【模型】:就是系数

【训练好的模型】:就是已经找到最佳系数

3.导入数据集

任何模型都离不开数据集,然而导入数据集的方法有很多种,大多数情况下,数据拿到手是需要先进行分析处理的,因为并不是所有的数据都是有用的,也并不是拿到手的数据就一定是最好的,这一部分也是一门学问,这次不展开描述。由于我们本次例子的数据集很少,因此可以通过这种手动添加的方式载入:

import numpy as np
X = np.array([[0,0,1], [1,1,1], [1,0,1], [0,1,1]]) //如花、小倩、小明
Y = np.array([[0,1,1,0]]).T                        //小强(".T"代表转置)

4.初始化权重

由于我们不清楚数据与数据的权重到底是怎么样的?谁与谁的关系更加密切,因此我们先通过random来随机生成一组权重。

np.random.seed(1)                          //random.seed称为“种子”,让我们生成的随机数一样
weights = 2 * np.random.random((3,1)) - 1  //随机生成权重:3行1列 且数字为-1~+1之间

5.正向推测与激活函数

我们利用刚刚随机生成的权重,来计算z=w1A+w2B+w3*C的值,但是z的值可能很大或者很小,没有一个固定的区域,所以我们可以利用Sigmoid函数【激活函数】(利用了函数本身的性质)来帮助我们将z的值转换成0~1之间的数字,来近而表示概率(这里不做强迫,但是由于概率属于0~1,还是最好这样处理) avatar

for it in range(10000):       
	z = np.dot(X, weights)    //dot()矩阵相乘:为了算出Z
	output = 1/(1+np.exp(-z)) //通过激活函数:将z转换到0~1区间

6.计算误差

利用上面得到的output与实际的值Y作差,计算出两者的误差error(刚开始的几次错的离谱很正常),我们需要根据这个误差error来计算出下一次的增量,这个时候有的小伙伴可能会想“我直接用error来充当增量不就行了吗?”这是万万不可以的!一个是结果有多大误差,一个是系数有多大误差!因此,我们需要考虑另外一个因素------图像的斜率slope。如图: avatar 这个激活函数的图像,前一段斜率大后一段斜率小,当我们趋近于最佳结果1时,斜率也趋近于0,所以我们越往后,这个增量应该越小,才能防止我们一不小心超过这个最佳值1。这个时候我们通常会引入一个深度学习领域比较常见的算法:“梯度下降算法avatar 模型训练的初期,误差相对比较大,就相当于图中最右侧的位置,斜率比较大;当模型的训练次数变多,渐渐的离最佳值越来越近,斜率越来越小,当斜率=0时,便是最佳值;但是如果稍不小心,增量过大,便会越过最佳点,离最佳结果越来越远。所以,整个过程与激活函数的斜率有很大关系。【这里的“梯度下降算法”仅仅是了解,要想真正理解这个算法,最好去看看资料实际操作一下】

error = Y - output               //计算实际结果与我们正向推测的结果的误差
slope = output * (1 - output)    //slope:该激活函数的斜率(在纸上求个导就可以算出整个)
delta = error * slope            //增量delta:每次要适当调整的大小

7.更新权重

将权重回代,刷新权重。这里的dot()其实是分析三者各自对于结果的影响情况,如果他们压根就没有参与,没有做任何事情,那么之前产生的误差跟他是没有关系的,没必要带上这一部分误差。【这个位置有点难理解】

【提示】:不要忘记weights是3行1列,里面的数字分别代表他们三人的权重。

weights = weights + np.dot(X.T, delta)  //dot(X.T, delta)实际是近一步分析三者的贡献情况

【打印不同训练n次的weights情况】:

//n=100次
[[ 4.62128737]
 [-0.20338466]
 [-2.09325311]]
//n=1000次
 [[ 7.26283009]
 [-0.21614618]
 [-3.41703015]]
//n=10000次
 [[ 9.67299303]
 [-0.2078435 ]
 [-4.62963669]]
//n=50000次
[[11.30926129]
 [-0.20509237]
 [-5.45001623]]

【提示】:预测结果通常会随着训练次数n的增大而变好,但是也存在瓶颈,甚至是错误,因此,我们也要通过实验来找出最佳训练次数。

8.代码汇总

【上述代码】

import numpy as np

X = np.array([[0,0,1], [1,1,1], [1,0,1], [0,1,1]])
Y = np.array([[0,1,1,0]]).T
np.random.seed(1)
weights = 2 * np.random.random((3,1)) - 1

for it in range(10000):
    z = np.dot(X, weights)
    output = 1 / (1 + np.exp(-z))
    error = Y - output
    slope = output * (1 - output)
    delta = error * slope
    weights = weights + np.dot(X.T, delta)
print(weights)

【代码的重构】

9.代码重构

import numpy as np

# 正向传播
def fp(input):
    # 利用矩阵的点乘一次性计算4个temp出来
    temp = np.dot(input, weights)
    # 使用Sigmoid函数【激活函数】,计算出最终的output
    return 1 / (1 + np.exp(-temp))

# 反向传播
def bp(Y, output):
    # 看看我们计算出来的和实际发生的有多大变化
    error = Y - output
    # 计算斜率
    slope = output * (1 - output)
    # 计算增量
    return error * slope

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

np.random.seed(1)
weights = 2 * np.random.random((3,1)) - 1
for it in range(1):
    output = fp(X)
    delta = bp(Y, output)
    # 更新权重
    weights += np.dot(X.T, delta)
print(weights)
print(fp([[1,1,0]]))   //预测新的数据

输出结果

[[ 9.67299303]
 [-0.2078435 ]
 [-4.62963669]]
[[0.9999225]]

【综上所述】:经过10000次训练,小明在[1,1,0]这次情况下:为“1”的可能性为0.9999225

Python
神经网络
  • 作者:李延松(联系作者)
  • 发表时间:2020-07-17 17:27
  • 版本声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 公众号转载:请在文末添加作者公众号二维码
留言