对比结果
在上一节中,我们使用了如下超参进行神经网络的训练:
params = HyperParameters(eta=0.1, max_epoch=10, batch_size=1, eps = 1e-5)
我们再把每次checkpoint的W和B的值打印出来:
9 0 437.5399553941636 [[-35.46926435] [399.01136072]] [[252.69305588]]
9 100 420.78580862641473 [[-36.93198181] [400.03047293]] [[251.26503706]]
......
9 900 413.7210407763991 [[-36.67601742] [406.55322285]] [[246.8067483]]
打印结果中每列的含义按顺序是:
- epoch
- iteration
- loss
- w1
- w2
- b
可以看到loss、w1、w2、b的值,每次跳跃都很大,怀疑是学习率过高导致的梯度下降在最优解附近徘徊。所以,我们先把超参修改一下:
params = HyperParameters(eta=0.01, max_epoch=500, batch_size=10, eps=1e-5)
做了三处修改:
- 学习率缩小10倍,变成0.01
- max_epoch扩大50倍,从10变成500,让网络得到充分训练(但实际上不需要这么多次的循环,大家可以自己试验)
- batch_size=10,使用mini-batch批量样本训练,提高精度,减缓个别样本引起的跳跃程度
运行结果:
......
499 89 380.62299460835936 [[-40.2782923 ] [399.34224968]] [[244.14309928]]
499 99 380.5935045560184 [[-40.26440193] [399.39472352]] [[244.3928586]]
可以看到达到了我们的目的,loss、w1、w2、b的值都很稳定。我们使用这批结果做为分析基础。首先列出W和B的训练结果如表5-8,可以看到神经网络得出的值(绝对值)比正规方程的结果大不少。
表5-8 神经网络的训练结果与正规方程法的结果比较
结果 | w1 | w2 | b |
---|---|---|---|
正规方程 | -2.018 | 5.055 | 46.235 |
神经网络 | -40.2 | 399.3 | 244.5 |
比值 | 19.92 | 78.99 | 5.288 |
再列出标准化后的数据,如表5-9。
表5-9 标准化后的特征值
特征 | 地理位置 | 居住面积 |
---|---|---|
最小值 | 2.02 | 40 |
最大值 | 21.96 | 119 |
差值 | 19.94 | 79 |
通过对比我发现,关于W的结果,表5-8最后一行的数据(19.92,78.99),和表5-9最后一行的数据(19.94,79),有惊人的相似之处!这是为什么呢?
还原真实的W,B值
我们唯一修改的地方,就是样本数据特征值的标准化,并没有修改标签值。可以大概猜到W的值和样本特征值的缩放有关系,而且缩放倍数非常相似,甚至可以说一致。下面推导一下这种现象的数学基础。
假设在标准化之前,真实的样本值是X,真实的权重值是W;在标准化之后,样本值变成了X’,训练出来的权重值是W’:
y = x_1 w_1 + x_2 w_2 + b \tag{y是标签值}
z = x_1’ w_1’ + x_2’ w_2’ + b’ \tag{z是预测值}
由于训练时标签值(房价)并没有做标准化,意味着我们是用真实的房价做的训练,所以预测值和标签值应该相等,所以:
y == z
x_1 w_1 + x_2 w_2 + b = x_1’ w_1’ + x_2’ w_2’ + b’ \tag{1}
标准化的公式是:
x’ = {x - x{min} \over x{max}-x_{min}} \tag{2}
为了简化书写,我们令xm=x{max}-x{min},把公式2代入公式1:
x1 w_1 + x_2 w_2 + b = {x_1 - x{1min} \over xm1} w_1’ + {x_2 - x{2min} \over xm2} w_2’ + b’ \ =x_1 \frac{w_1’}{xm_1} + x_2 \frac{w_2’}{xm_2}+b’-\frac{w_1’x{1min}}{xm1}-\frac{w_2’x{2min}}{xm_2} \tag{3}
公式3中,x1,x2是变量,其它都是常量,如果想让公式3等式成立,则变量项和常数项分别相等,即:
w_1 = \frac{w_1’}{xm_1} \tag{4}
w_2 = \frac{w_2’}{xm_2} \tag{5}
b = b’-\frac{w1’x{1min}}{xm1}-\frac{w_2’x{2min}}{xm_2} \tag{6}
下面我们用实际数值代入公式4,5,6:
w1 = {w_1’ \over x{1max}-x_{1min}} = {-40.2 \over (21.96-2.02)} = -2.016 \tag{7}
w2 = {w_2’ \over x{2max}-x_{2min}} = {399.3 \over (119-40)} = 5.054 \tag{8}
b=244.5-(-2.016) \cdot 2.02 - 5.054 \cdot 40=46.412 \tag{9}
可以看到公式7、8、9的计算结果(神经网络的训练结果的变换值)与正规方程的计算结果(-2.018, 5.055, 46.235)基本相同,基于神经网络是一种近似解的考虑,可以认为这种推导是有理论基础和试验证明的。
代码实现
下面的代码实现了公式4,5,6:
# get real weights
def DeNormalizeWeightsBias(net, dataReader):
W_true = np.zeros_like(net.W)
for i in range(W_true.shape[0]):
W_true[i,0] = net.W[i,0] / dataReader.X_norm[i,1]
#end for
B_true = net.B - W_true[0,0] * dataReader.X_norm[0,0] - W_true[1,0] * dataReader.X_norm[1,0]
return W_true, B_true
X_Norm是我们在做标准化时保留下来的样本的两个特征向量的最小值和数值范围(最大值减去最小值)。
修改主程序如下:
if __name__ == '__main__':
......
net.train(reader, checkpoint=0.1)
# inference
W_real, B_real = DeNormalizeWeightsBias(net, reader)
print("W_real=", W_real)
print("B_real=", B_real)
x1 = 15
x2 = 93
x = np.array([x1,x2]).reshape(1,2)
z = np.dot(x, W_real) + B_real
print("Z=", z)
ShowResult(net, reader)
在net.train()方法返回之后,训练好的W和B的值就保存在NeuralNet类的属性里了。然后通过调用DeNormalizeWeightsBias()函数,把它们转换成真实的W_real和B_real值,就好比我们不做标准化而能训练出来的权重值一样。
最后在推理预测时,我们直接使用了np.dot()公式,而没有使用net.inference()方法,是因为在net实例中的W和B是还原前的值,做前向计算时还是会有问题,所以我们直接把前向计算公式拿出来,代入W_real和B_real,就可以得到真实的预测值了。
运行结果
运行上述代码,观察最后部分的打印输出:
......
499 99 380.5934686827507
[[-40.23261123] [399.36389489]] [[244.39118797]]
W= [[-40.23261123]
[399.36389489]]
B= [[244.39118797]]
W_real= [[-2.01737219]
[ 5.05523918]]
B_real= [[46.26647363]]
Z= [[486.14313417]]
把结果与正规方程的结果对比一下,如表5-10。
表5-10 还原后的值与正规方程结果的比较
w1 | w2 | b | |
---|---|---|---|
正规方程 | -2.018 | 5.055 | 46.235 |
神经网络 | -2.017 | 5.055 | 46.266 |
二者几乎一样,可以认为我们成功了!但是这一套代码下来,总觉得有些啰嗦,对于简单的线性问题来说,这么做可以,如果遇到非线性问题,或者深层网络,这么做是不是也可以呢?
代码位置
原代码位置:ch05, Level4
个人代码:DeNormalizeWB**