下面进入第三周的学习内容。
神经网络概述
这里将神经网络与Logistic回归进行了一个简单的对比,即神经网络也需要前向传播和后向传播。另外,每一层的参数用右上标的一个方括号表示,即方括号用来区分不同的层;如果右上标是圆括号,则指具体的一个样本。
神经网络表示
下图是一个简单的双层神经网络的表示(即单隐层网络),其中也包括了一些通用的符号标记方法。
计算神经网络的输出
图中给出了第一层隐藏层的输入和输出过程,我们可以通过将参数w进行堆叠化,再与X向量相乘,与b向量相加,得到对应的输出Z^[1]和a^[1]。
更简单的一个解释如下图,可以看到W,a,b,z的维度的相同点与不同点。我们固定输入特征为列向量,则隐藏层参数的维数则为(隐藏层结点个数,特征总数)。
多个样本的向量化
如果没有进行向量化,那么我们要用如下的一个for循环来实现神经网络的前向传播:
如果进行向量化,就像下图红框中标出的,这是就不需要使用for循环了,而是一次性就可以计算所有样本。即我们的关键是,将X向量化,这样也能使得Z,A向量化。
下面我们横向看A矩阵,矩阵A会扫过不同的训练样本,而竖向则经过当前隐藏层的不同单元;再以X矩阵为例,横向对应不同的训练样本,竖向则对应同一样本的不同输入特征(相当于输入层的不同单元)。
形象解释
接下来,对于Z^[1] = W^[1]X + b^[1]这样的向量化做了一个形象的解释。
简单的回顾,可以看到右下角的公式可以一次性计算所有的样本,而不需要使用for循环。但是我们会注意到,仍然有地方需要使用到for循环,即遍历所有的隐藏层来计算对应的输出。
激活函数-Activation functions
神经网络常见的几种激活函数:
这里有几点需要注意的,以及来自经验者的一些经验法则:
- 如果是做二元分类,即输出值为0或1,则Sigmoid函数可以用作输出层的激活函数,其他情况下都不要使用Sigmoid函数。
- 一般的默认激活函数为ReLu(线性修正单元),不过它的一个小缺点是当z小于0时,导数为0,因此有人提出了Leaky ReLu,不过挺少人使用的。
- 搭建神经网络时面临很多选择,如隐藏层单元数、激活函数、如何初始化权重等,当我们不确定哪些选择更有效时,可以使用交叉验证集尝试不同的选择,然后再找到最合适的来使用。
为什么神经网络需要非线性激活函数
如图,如果我们的隐藏层激活函数都使用恒等激活函数(线性激活函数),那么我们最终得到的只是输入的线性组合,即线性隐藏层毫无作用,因为两个线性函数的组合就是线性组合。因此,我们需要使用非线性激活函数才能使神经网络有效。当然,如果我们需要的真实输出y属于实数,那么我们可以在输出层使用线性激活函数,但是隐藏层则绝对不可以(例外是有关于一些压缩机制)。
激活函数的导数
神经网络的梯度下降公式
推导
省略
向量化写法
随机初始化
对于Logistic回归来说,我们可以将权重全部初始化为0,但是如果将神经网络的权重都初始化为0,则是行不通的。从图中可以知道,如果权重均为0,那么同一隐藏层中的两个隐藏层结点完全对称,即完全相同,因此并没有起到神经网络隐藏层的作用。
解决方法是进行随机初始化,而且应该初始化成小的参数。
我们使用np.random.randn(a,b)函数来进行随机初始化,注意到有个0.01的常数。对于浅层神经网络而言,常数设置为0.01也可以;但对于深层神经网络,则应该设置得更小。
本周作业
这周作业是创建一个单隐层神经网络,遇到了两个大坑!!!这两个大坑都是由于遇到了类似(100,)这样的向量,导致程序无法正常运行。今后在遇到bug的时候,第一个要想到这个问题!接下来记录一下整个的创建过程。
数据集
这里就遇到了坑一,我们需要通过reshape函数来确认向量的维度才行。
Logistic Regression
这里不再是上周作业那样自己从头到尾创建,而是直接调用sklearn库中的函数。1
2clf = sklearn.linear_model.LogisticRegressionCV();
clf.fit(X.T, Y.T);
其测试结果如下:
Neural Network model
我们的神经网络模型图形如下:
接下来是整个的网络构建过程。
确定神经网络结构-Defining the neural network structure
1 | def layer_sizes(X, Y): |
初始化模型参数-Initialize the model’s parameters
1 | def initialize_parameters(n_x, n_h, n_y): |
循环过程-The Loop
前向传播过程: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
32
33def forward_propagation(X, parameters):
"""
Argument:
X -- input data of size (n_x, m)
parameters -- python dictionary containing your parameters (output of initialization function)
Returns:
A2 -- The sigmoid output of the second activation
cache -- a dictionary containing "Z1", "A1", "Z2" and "A2"
"""
# Retrieve each parameter from the dictionary "parameters"
### START CODE HERE ### (≈ 4 lines of code)
W1 = parameters["W1"]
b1 = parameters["b1"]
W2 = parameters["W2"]
b2 = parameters["b2"]
### END CODE HERE ###
# Implement Forward Propagation to calculate A2 (probabilities)
### START CODE HERE ### (≈ 4 lines of code)
Z1 = np.dot(W1, X) + b1
A1 = np.tanh(Z1)
Z2 = np.dot(W2, A1) + b2
A2 = sigmoid(Z2)
### END CODE HERE ###
assert(A2.shape == (1, X.shape[1]))
cache = {"Z1": Z1,
"A1": A1,
"Z2": Z2,
"A2": A2}
return A2, cache
计算成本函数: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
26def compute_cost(A2, Y, parameters):
"""
Computes the cross-entropy cost given in equation (13)
Arguments:
A2 -- The sigmoid output of the second activation, of shape (1, number of examples)
Y -- "true" labels vector of shape (1, number of examples)
parameters -- python dictionary containing your parameters W1, b1, W2 and b2
Returns:
cost -- cross-entropy cost given equation (13)
"""
m = Y.shape[1] # number of example
# Compute the cross-entropy cost
### START CODE HERE ### (≈ 2 lines of code)
logprobs = np.multiply(np.log(A2),Y) + np.multiply(np.log(1-A2),1-Y)
cost = -np.sum(logprobs) / m
### END CODE HERE ###
cost = np.squeeze(cost) # makes sure cost is the dimension we expect.
# E.g., turns [[17]] into 17
assert(isinstance(cost, float))
return cost
后向传播: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
32
33
34
35
36
37
38
39
40
41
42
43def backward_propagation(parameters, cache, X, Y):
"""
Implement the backward propagation using the instructions above.
Arguments:
parameters -- python dictionary containing our parameters
cache -- a dictionary containing "Z1", "A1", "Z2" and "A2".
X -- input data of shape (2, number of examples)
Y -- "true" labels vector of shape (1, number of examples)
Returns:
grads -- python dictionary containing your gradients with respect to different parameters
"""
m = X.shape[1]
# First, retrieve W1 and W2 from the dictionary "parameters".
### START CODE HERE ### (≈ 2 lines of code)
W1 = parameters["W1"]
W2 = parameters["W2"]
### END CODE HERE ###
# Retrieve also A1 and A2 from dictionary "cache".
### START CODE HERE ### (≈ 2 lines of code)
A1 = cache["A1"]
A2 = cache["A2"]
### END CODE HERE ###
# Backward propagation: calculate dW1, db1, dW2, db2.
### START CODE HERE ### (≈ 6 lines of code, corresponding to 6 equations on slide above)
dZ2 = A2 - Y
dW2 = np.dot(dZ2, A1.T) / m
db2 = np.sum(dZ2, axis=1, keepdims = True) / m
dZ1 = np.multiply(np.dot(W2.T, dZ2), 1-np.power(A1,2))
dW1 = np.dot(dZ1, X.T) / m
db1 = np.sum(dZ1, axis=1, keepdims = True) / m
### END CODE HERE ###
grads = {"dW1": dW1,
"db1": db1,
"dW2": dW2,
"db2": db2}
return grads
参数更新: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
32
33
34
35
36
37
38
39
40
41def update_parameters(parameters, grads, learning_rate = 1.2):
"""
Updates parameters using the gradient descent update rule given above
Arguments:
parameters -- python dictionary containing your parameters
grads -- python dictionary containing your gradients
Returns:
parameters -- python dictionary containing your updated parameters
"""
# Retrieve each parameter from the dictionary "parameters"
### START CODE HERE ### (≈ 4 lines of code)
W1 = parameters["W1"]
b1 = parameters["b1"]
W2 = parameters["W2"]
b2 = parameters["b2"]
### END CODE HERE ###
# Retrieve each gradient from the dictionary "grads"
### START CODE HERE ### (≈ 4 lines of code)
dW1 = grads["dW1"]
db1 = grads["db1"]
dW2 = grads["dW2"]
db2 = grads["db2"]
## END CODE HERE ###
# Update rule for each parameter
### START CODE HERE ### (≈ 4 lines of code)
W1 -= learning_rate * dW1
b1 -= learning_rate * db1
W2 -= learning_rate * dW2
b2 -= learning_rate * db2
### END CODE HERE ###
parameters = {"W1": W1,
"b1": b1,
"W2": W2,
"b2": b2}
return parameters
整合最终模型-nn_model()
1 | def nn_model(X, Y, n_h, num_iterations = 10000, print_cost=False): |
预测-Predictions
1 | def predict(parameters, X): |
而具体的计算公式可以参考前面的内容。