🐟书踩坑(一)

废话不多说,俺先讲一下来龙去脉~

今天看鱼书P108页,遇到一个想不通的地方,我先放一波源码:
(不了解的小伙伴看上一篇博文,有源码连接)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 为了导入父目录中的文件而进行的设定
import numpy as np
from common.functions import softmax, cross_entropy_error
from common.gradient import numerical_gradient


class simpleNet:
def __init__(self):
self.W = np.random.randn(2,3)

def predict(self, x):
return np.dot(x, self.W)

def loss(self, x, t):
z = self.predict(x)
y = softmax(z)
loss = cross_entropy_error(y, t)

return loss

x = np.array([0.6, 0.9])
t = np.array([0, 0, 1])

net = simpleNet()

f = lambda w: net.loss(x, t)
dW = numerical_gradient(f, net.W)

print(dW)

没错,这个是一个求神经网络梯度 w 的简单脚本。

如你所见,文件头也import了许多外部函数,下面贴其中一个比较重要的外部函数:

numerical_gradient函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# coding: utf-8
import numpy as np

def numerical_gradient(f, x):
h = 1e-4 # 0.0001
grad = np.zeros_like(x)

it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
while not it.finished:
idx = it.multi_index
tmp_val = x[idx]
x[idx] = float(tmp_val) + h
fxh1 = f(x) # f(x+h)

x[idx] = tmp_val - h
fxh2 = f(x) # f(x-h)
grad[idx] = (fxh1 - fxh2) / (2*h)

x[idx] = tmp_val # 还原值
it.iternext()

return grad

下面讲讲这个坑

小伙子们注意看第一段代码28,29两行:

1
2
f = lambda w: net.loss(x, t)
dW = numerical_gradient(f, net.W)

函数f中lambda表达式没什么问题,注意里面的参数 w ,仔细看,你会发现……卧槽!这不没jb卵用吗…
如果你这么想,那你跟年轻的我有的一拼,too young,too simple

接着看下一行:dW = numerical_gradient(f, net.W)很显然调用了numerical_gradient函数,没毛病。但是,兄弟萌,请仔细看看numerical_gradient函数实现机制,你会发现:

第13行: fxh1 = f(x)
第16行: fxh2 = f(x)
??????????

看不出问题吗?
注意:这里的f是:

1
f = lambda w: net.loss(x, t)

w是伪参数,没卵用,而x是net.W——神经网络的权重组成的数组,这尼玛驴头不对马嘴怎么就传给f了???而且f也不需要参数啊!

我是左思右想一下午,后来有了一个猜想来解释这段代码。

猜想

我猜想fxh1 = f(x) ,fxh2 = f(x)括号里的x对程序根本没卵用,于是我大胆的删掉了x,变成:

第13行: fxh1 = f()
第16行: fxh2 = f()

当然为了保持兼容,也得把lambda表达式那个伪参数删了:

f = lambda : net.loss(x, t)

然后编译运行,结果…tmd就对了,你肯定觉得这是个坑,没错,我开始也觉得是坑,坑死我了。
后来浏览了后面的代码,发现了一些用numerical_gradient函数做测试脚本,其中测试的是一些简单函数的梯度问题,涉及到变量x所以numerical_gradient函数的x要保留。。。。。。相应的,神经网络求梯度就要顺着numerical_gradient函数,没必要再写一个,于是就加了伪参数w保证兼容。

最后俺深刻的明白:实践是检验真理的唯一标准(早试试就不会浪费那么多时间了)

END

文章作者: GeYu
文章链接: https://nuistgy.github.io/2018/09/20/鱼书踩坑(一)/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yu's Blog