一切图片来自官方PPT网站Index of /slides/2022
代码部分:双层网络(仿射层+ReLu层)_iwill323的博客-CSDN博客
目录
神经网络
神经网络结构
神经网络计算层数的时候是不带输入层的
“3-layer Neural Net”, or “2-hidden-layer Neural Net”
# forward-pass of a 3-layer neural network:
f = lambda x: 1.0/(1.0 + np.exp(-x)) # activation function (use sigmoid)
x = np.random.randn(3, 1) # random input vector of three numbers (3x1)
h1 = f(np.dot(W1, x) + b1) # calculate first hidden layer activations (4x1)
h2 = f(np.dot(W2, h1) + b2) # calculate second hidden layer activations (4x1)
out = np.dot(W3, h2) + b3 # output neuron (1x1)
关于神经网络capacity的设定
神经网络尺寸越大越容易过拟合
使用正则化可以抑制过拟合
官方笔记的建议:The regularization strength is the preferred way to control the overfitting of a neural network. The takeaway is that you should not be using smaller networks because you are afraid of overfitting. Instead, you should use as big of a neural network as your computational budget allows, and use other regularization techniques to control overfitting.
反向传播
计算思想
一般把输入输出当做给定的(也有例外,比如神经风格转化),于是计算的过程就是求解最优权重的过程。反向传播就是为了计算梯度、更新权重。方法是采用计算图分解复杂的计算过程,通过链式法则一步步计算中间变零的梯度,直至计算出所有权重的梯度。
每个节点计算自己的梯度的时候,不需要知道上游的梯度。这样每个节点只需要计算好自己的梯度就行了。
所有节点组成了一个电路。Every gate in a circuit diagram gets some inputs and can right away compute two things: 1. its output value and 2. the local gradient of its output with respect to its inputs.每个节点从上游接受传来的信号(梯度),然后和自己的自变量的梯度相乘,向下游传导。
例子
计算图不是唯一的,可以把相近节点合成一个有意义的计算节点,比如sigmoid
pytorch中的反向传播
理解了上面的思想,再学习pytorch的autograd就更简单了。
如果想在反向传播过程中计算一个参数的梯度,那么就把它的requires_grad属性设置成True。只要对一个参数这么做了,那么由他计算出来的所有因变量requires_grad属性都是True。
pytorch中变量有一个grad_fn属性,用于梯度的计算和传播。用户直接创建的Tensor,其grad_fn是None,通过计算产生的tensor,它的grad_fn不是None(当然还要要求自变量至少有一个requires_grad=True.)。grad_fn指向一个Function对象,这个Function对象知道如何计算正向的函数,以及如何在反向传播步骤中计算导数。长这个样子:
一般不人为定义这个Function对象,因为pytorch直接帮我们定义好了。直接对loss执行loss.backward()就行了。这个操作将调用loss里面的grad_fn这个属性,通过Function对象执行求导的操作。每一个grad_fn有一个next_functions属性,指向下游(对反向传播而言)的Function对象,继续求导,于是梯度信息就不断向下传播,直至计算图的叶子节点。计算出结果以后,将结果保存到他们的 grad这个属性里面。求导结束,所有的叶节点的grad变量都得到了更新
a = torch.tensor([3.14], requires_grad = True)
b = torch.sin(a)
c = 2 * b
d = c + 1
print(d.grad_fn)
# <AddBackward0 object at 0x7fabeda787d0>
print(d.grad_fn.next_functions)
# ((<MulBackward0 object at 0x7fabeda78ad0>, 0), (None, 0)) None对应的是常数
print(d.grad_fn.next_functions[0][0].next_functions)
# ((<SinBackward0 object at 0x7fabeda78ad0>, 0), (None, 0))
print(id(d.grad_fn.next_functions[0][0])==id(c.grad_fn))
# True 说明变量的grad_fn属性是相互连接的,就像电路图里的元器件一样
尤其要注意的是,上下游变量的grad_fn是连通的,这就是计算图的思想(猜的)
向量的反向传播
雅克比矩阵长这个样子(维度和下面的应用差一个转置)
注意,由于z是向量,所以local gradient这时候成了雅克比矩阵,更新下游梯度的时候采用的是矩阵乘积。并且变量关于loss的梯度和原变零的维度是一模一样的。
官方PPT的例子:
有了雅克比矩阵,就可以通过矩阵运算来计算下游梯度,但是这么做运算代价太大:
根据乘法求导的特点,从某种程度上讲,权重和输入分别就是y关于输入和权重的导数。可以跳过雅克比矩阵,直接用上游梯度和权重矩阵来算:
代码来自官方网站 https://cs231n.github.io/optimization-2/
# forward pass
W = np.random.randn(5, 10)
X = np.random.randn(10, 3)
D = W.dot(X)
# now suppose we had the gradient on D from above in the circuit
dD = np.random.randn(*D.shape) # same shape as D
dW = dD.dot(X.T) #.T gives the transpose of the matrix
dX = W.T.dot(dD)
dW和dX的计算公式不用记,因为通过维度匹配就能推出来。
引申
在这里,2022年的PPT比2021年的删去了下面这一部分,我倒是觉得原来的内容挺好的
拓展资料: http://cs231n.stanford.edu/handouts/linear-backprop.pdf