【问题】:4个好朋友一起出去玩,“事件0”代表“不去”,“事件1”代表“去”,通过前几组数据推测小强第5次去不去?
如花 | 小倩 | 小明 | 小强 |
---|---|---|---|
0 | 0 | 1 | 0 |
1 | 1 | 1 | 1 |
1 | 0 | 1 | 1 |
0 | 1 | 1 | 0 |
1 | 1 | 0 | ? |
【答案】:1
【解答】:通过前面4组数据,不难发现小强“去”与“不去”直接取决于如花。如果如花“去”,则小强“去”;如果如花“不去”,则小强“不去”。
【分析与引入】:这种类似规律探索的问题,我们幼儿园、小学一年级经常遇到。经过我们肉眼观察或者动笔运算找到事物现象的某种规律,进而推测出待求数据的结果。这个短暂的思考过程,我们的大脑(大约1000亿个神经元)在快速找到规律,帮助我们找到答案。如果我们按照这个思路来编写程序,那么无疑是下面这样的:if(如花==1) 小强=1;else 小强=0;但是,如果问题变得复杂,并不是这种死代码可以判断出的,如下图:
如花 | 小倩 | 小明 | 小强 |
---|---|---|---|
1 | 0 | 1 | 1 |
1 | 1 | 0 | 1 |
0 | 0 | 1 | 1 |
0 | 1 | 0 | 0 |
1 | 1 | 0 | ? |
当变成这种复杂情况或者遇到大量的数据集、多特征的数据集,刚刚的代码便得修改,重新通过人脑来寻找新的规律,这个过程会非常的困难!
【总结】:基于神经网络的机器学习相当于“自己会学习的算法”!
不断试错、不断改正、找到正解!
1.初始化权重w1、w2、w3 -------> 2.按照权重计算结果; -------> 3.计算误差; ------->4.根据误差调整权重; -------> 5.重复以上步骤n次; -------> 6.训练完成; ------->7.进行预测
【机器学习的目标】:找系数!
【怎么找】:不断试错
【模型】:就是系数
【训练好的模型】:就是已经找到最佳系数
任何模型都离不开数据集,然而导入数据集的方法有很多种,大多数情况下,数据拿到手是需要先进行分析处理的,因为并不是所有的数据都是有用的,也并不是拿到手的数据就一定是最好的,这一部分也是一门学问,这次不展开描述。由于我们本次例子的数据集很少,因此可以通过这种手动添加的方式载入:
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"代表转置)
由于我们不清楚数据与数据的权重到底是怎么样的?谁与谁的关系更加密切,因此我们先通过random来随机生成一组权重。
np.random.seed(1) //random.seed称为“种子”,让我们生成的随机数一样
weights = 2 * np.random.random((3,1)) - 1 //随机生成权重:3行1列 且数字为-1~+1之间
我们利用刚刚随机生成的权重,来计算z=w1A+w2B+w3*C的值,但是z的值可能很大或者很小,没有一个固定的区域,所以我们可以利用Sigmoid函数【激活函数】(利用了函数本身的性质)来帮助我们将z的值转换成0~1之间的数字,来近而表示概率(这里不做强迫,但是由于概率属于0~1,还是最好这样处理)
for it in range(10000):
z = np.dot(X, weights) //dot()矩阵相乘:为了算出Z
output = 1/(1+np.exp(-z)) //通过激活函数:将z转换到0~1区间
利用上面得到的output与实际的值Y作差,计算出两者的误差error(刚开始的几次错的离谱很正常),我们需要根据这个误差error来计算出下一次的增量,这个时候有的小伙伴可能会想“我直接用error来充当增量不就行了吗?”这是万万不可以的!一个是结果有多大误差,一个是系数有多大误差!因此,我们需要考虑另外一个因素------图像的斜率slope。如图:
这个激活函数的图像,前一段斜率大后一段斜率小,当我们趋近于最佳结果1时,斜率也趋近于0,所以我们越往后,这个增量应该越小,才能防止我们一不小心超过这个最佳值1。这个时候我们通常会引入一个深度学习领域比较常见的算法:“梯度下降算法”
模型训练的初期,误差相对比较大,就相当于图中最右侧的位置,斜率比较大;当模型的训练次数变多,渐渐的离最佳值越来越近,斜率越来越小,当斜率=0时,便是最佳值;但是如果稍不小心,增量过大,便会越过最佳点,离最佳结果越来越远。所以,整个过程与激活函数的斜率有很大关系。【这里的“梯度下降算法”仅仅是了解,要想真正理解这个算法,最好去看看资料实际操作一下】
error = Y - output //计算实际结果与我们正向推测的结果的误差
slope = output * (1 - output) //slope:该激活函数的斜率(在纸上求个导就可以算出整个)
delta = error * slope //增量delta:每次要适当调整的大小
将权重回代,刷新权重。这里的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的增大而变好,但是也存在瓶颈,甚至是错误,因此,我们也要通过实验来找出最佳训练次数。
【上述代码】
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)
【代码的重构】
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