本文参考该播主实现,需要的文件在博主的文章里:https://blog.csdn.net/u013733326/article/details/79767169
这次我先记录我自己打代码时候的错误,最后把添加注释的代码放到最后;
在初始化b1的时候我发现我没有把b1初始化为参数为0的数组,并且维度设置的也不对,最后导致错误:
Traceback (most recent call last):
File "E:/python/pycharm/week4/main.py", line 329, in <module>
predictions_train = predict(train_x, train_y, parameters) #训练集
File "E:/python/pycharm/week4/main.py", line 317, in predict
probas, caches = L_model_forward(X, parameters)
File "E:/python/pycharm/week4/main.py", line 114, in L_model_forward
AL,cache = linear_activation_forward(A_prev,parameters['W' + str(L)],parameters['b' + str(L)],'sigmoid')
File "E:/python/pycharm/week4/main.py", line 81, in linear_activation_forward
Z,linear_cache = liner_forward(A_prev,W,b)
File "E:/python/pycharm/week4/main.py", line 67, in liner_forward
Z = np.dot(W,A) + b #记住这里使用向量化
File "<__array_function__ internals>", line 6, in dot
ValueError: shapes (1,7) and (12288,209) not aligned: 7 (dim 1) != 12288 (dim 0)
Process finished with exit code 1
代码对比:
正确代码:
W1 = np.random.randn(n_h, n_x) * 0.01
b1 = np.zeros((n_h, 1))
W2 = np.random.randn(n_y, n_h) * 0.01
b2 = np.zeros((n_y, 1))
错误代码:
W1 = np.random.randn(n_h,n_x)*0.01
b1 = np.random.randn(n_h,1)
W2 = np.random.randn(n_y,n_h)*0.01
b2 = np.random.randn(n_y,1)
import numpy as np
import h5py
import matplotlib.pyplot as plt
import testCases #参见资料包,或者在文章底部copy
from dnn_utils import sigmoid, sigmoid_backward, relu, relu_backward #参见资料包
import lr_utils #参见资料包,或者在文章底部copy
np.random.seed(1)
def initialize_parameters(n_x, n_h, n_y):
"""
此函数是为了初始化两层网络参数而使用的函数。
参数:
n_x - 输入层节点数量
n_h - 隐藏层节点数量
n_y - 输出层节点数量
返回:
parameters - 包含你的参数的python字典:
W1 - 权重矩阵,维度为(n_h,n_x)
b1 - 偏向量,维度为(n_h,1)
W2 - 权重矩阵,维度为(n_y,n_h)
b2 - 偏向量,维度为(n_y,1)
"""
W1 = np.random.randn(n_h, n_x) * 0.01
b1 = np.zeros((n_h, 1))
W2 = np.random.randn(n_y, n_h) * 0.01
b2 = np.zeros((n_y, 1))
# 使用断言确保我的数据格式是正确的
assert (W1.shape == (n_h, n_x))
assert (b1.shape == (n_h, 1))
assert (W2.shape == (n_y, n_h))
assert (b2.shape == (n_y, 1))
parameters = {"W1": W1,
"b1": b1,
"W2": W2,
"b2": b2}
return parameters
def initialize_parameters_deep(layers_dims):
"""
此函数是为了初始化多层网络参数而使用的函数。
参数:
layers_dims - 包含我们网络中每个图层的节点数量的列表
返回:
parameters - 包含参数“W1”,“b1”,...,“WL”,“bL”的字典:
W1 - 权重矩阵,维度为(layers_dims [1],layers_dims [1-1])
bl - 偏向量,维度为(layers_dims [1],1)
"""
np.random.seed(3)
parameters = {}
L = len(layers_dims)
for l in range(1, L):
parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) / np.sqrt(
layers_dims[l - 1]) # 用这条语句出来的数据与博客中不符合
# parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) *0.01 #使用这条语句符合博客中的数据
# parameters['b' + str(l)] = np.zeros(layers_dims[l], 1)#np.zeros(layers_dims[l],1),当只有一个()的时候会报错,因为np.zeros(shape)需要给的参数就是一个shape而不是维度
parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))
# 确保我要的数据的格式是正确的
assert (parameters["W" + str(l)].shape == (layers_dims[l], layers_dims[l - 1]))
assert (parameters["b" + str(l)].shape == (layers_dims[l], 1))
return parameters
"""
前向传播函数分为三个步骤:
1.Liner
2.Liner -> Activation , 其中激活函数会使用Relu或者Sigmoid
3.[Linear -> Relu] x (L-1) -> Linear -> Sigmoid(整个模型)
"""
def linear_forward(A, W, b):
"""
实现前向传播的线性部分。
参数:
A - 来自上一层(或输入数据)的激活,维度为(上一层的节点数量,示例的数量)
W - 权重矩阵,numpy数组,维度为(当前图层的节点数量,前一图层的节点数量)
b - 偏向量,numpy向量,维度为(当前图层节点数量,1)
返回:
Z - 激活功能的输入,也称为预激活参数
cache - 一个包含“A”,“W”和“b”的字典,存储这些变量以有效地计算后向传递
"""
Z = np.dot(W, A) + b
assert (Z.shape == (W.shape[0], A.shape[1]))
cache = (A, W, b)
return Z, cache
def linear_activation_forward(A_prev, W, b, activation):
"""
实现LINEAR-> ACTIVATION 这一层的前向传播
参数:
A_prev - 来自上一层(或输入层)的激活,维度为(上一层的节点数量,示例数)
W - 权重矩阵,numpy数组,维度为(当前层的节点数量,前一层的大小)
b - 偏向量,numpy阵列,维度为(当前层的节点数量,1)
activation - 选择在此层中使用的激活函数名,字符串类型,【"sigmoid" | "relu"】
返回:
A - 激活函数的输出,也称为激活后的值
cache - 一个包含“linear_cache”和“activation_cache”的字典,我们需要存储它以有效地计算后向传递
"""
if activation == "sigmoid":
Z, linear_cache = linear_forward(A_prev, W, b)
A, activation_cache = sigmoid(Z)
elif activation == "relu":
Z, linear_cache = linear_forward(A_prev, W, b)
A, activation_cache = relu(Z)
assert (A.shape == (W.shape[0], A_prev.shape[1]))
cache = (linear_cache, activation_cache)
return A, cache
def L_model_forward(X, parameters):
"""
实现[LINEAR-> RELU] *(L-1) - > LINEAR-> SIGMOID计算前向传播,也就是多层网络的前向传播,为后面每一层都执行LINEAR和ACTIVATION
参数:
X - 数据,numpy数组,维度为(输入节点数量,示例数)
parameters - initialize_parameters_deep()的输出
返回:
AL - 最后的激活值
caches - 包含以下内容的缓存列表:
linear_relu_forward()的每个cache(有L-1个,索引为从0到L-2)
linear_sigmoid_forward()的cache(只有一个,索引为L-1)
"""
caches = []
A = X
L = len(parameters) // 2#// 是整除向下取整,因为paremeters里面有W和b,所以整除2 就是隐藏层的个数,我们就需要使用L-1的relu激活函数
for l in range(1, L):#半包围结构取不到L,取到L-1
A_prev = A
A, cache = linear_activation_forward(A_prev, parameters['W' + str(l)], parameters['b' + str(l)], "relu")
caches.append(cache)
AL, cache = linear_activation_forward(A, parameters['W' + str(L)], parameters['b' + str(L)], "sigmoid")
caches.append(cache)
"""为什么是(1,X.shape[1]),AL是一个二分类的输出结果,所以是维度为1 x m 的向量,m就是X.shape[1],就是输入的样本数量"""
assert (AL.shape == (1, X.shape[1])) # AL :sigmoid激活函数的输出,也称为激活后的值,也是Yhat,
return AL, caches
def compute_cost(AL, Y):
"""
实施等式(4)定义的成本函数。
参数:
AL - 与标签预测相对应的概率向量,维度为(1,示例数量)
Y - 标签向量(例如:如果不是猫,则为0,如果是猫则为1),维度为(1,数量)
返回:
cost - 交叉熵成本
"""
m = Y.shape[1]
cost = -np.sum(np.multiply(np.log(AL), Y) + np.multiply(np.log(1 - AL), 1 - Y)) / m
cost = np.squeeze(cost)
assert (cost.shape == ())
return cost
def linear_backward(dZ, cache):
"""
为单层实现反向传播的线性部分(第L层)
参数:
dZ - 相对于(当前第l层的)线性输出的成本梯度
cache - 来自当前层前向传播的值的元组(A_prev,W,b)
返回:
dA_prev - 相对于激活(前一层l-1)的成本梯度,与A_prev维度相同
dW - 相对于W(当前层l)的成本梯度,与W的维度相同
db - 相对于b(当前层l)的成本梯度,与b维度相同
"""
A_prev, W, b = cache
m = A_prev.shape[1]
dW = np.dot(dZ, A_prev.T) / m
db = np.sum(dZ, axis=1, keepdims=True) / m
dA_prev = np.dot(W.T, dZ)
assert (dA_prev.shape == A_prev.shape)
assert (dW.shape == W.shape)
assert (db.shape == b.shape)
return dA_prev, dW, db
def linear_activation_backward(dA, cache, activation="relu"):
"""
实现LINEAR-> ACTIVATION层的后向传播。
参数:
dA - 当前层l的激活后的梯度值
cache - 我们存储的用于有效计算反向传播的值的元组(值为linear_cache,activation_cache)
activation - 要在此层中使用的激活函数名,字符串类型,【"sigmoid" | "relu"】
返回:
dA_prev - 相对于激活(前一层l-1)的成本梯度值,与A_prev维度相同
dW - 相对于W(当前层l)的成本梯度值,与W的维度相同
db - 相对于b(当前层l)的成本梯度值,与b的维度相同
"""
linear_cache, activation_cache = cache
if activation == "relu":
dZ = relu_backward(dA, activation_cache)
dA_prev, dW, db = linear_backward(dZ, linear_cache)
elif activation == "sigmoid":
dZ = sigmoid_backward(dA, activation_cache)
dA_prev, dW, db = linear_backward(dZ, linear_cache)
return dA_prev, dW, db
def L_model_backward(AL, Y, caches):
"""
对[LINEAR-> RELU] *(L-1) - > LINEAR - > SIGMOID组执行反向传播,就是多层网络的向后传播
参数:
AL - 概率向量,正向传播的输出(L_model_forward())
Y - 标签向量(例如:如果不是猫,则为0,如果是猫则为1),维度为(1,数量)
caches - 包含以下内容的cache列表:
linear_activation_forward("relu")的cache,不包含输出层
linear_activation_forward("sigmoid")的cache
返回:
grads - 具有梯度值的字典
grads [“dA”+ str(l)] = ...
grads [“dW”+ str(l)] = ...
grads [“db”+ str(l)] = ...
"""
grads = {}
L = len(caches)
m = AL.shape[1]
Y = Y.reshape(AL.shape)
dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))
current_cache = caches[L - 1]
grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = linear_activation_backward(dAL, current_cache, "sigmoid")
for l in reversed(range(L - 1)):
current_cache = caches[l]
dA_prev_temp, dW_temp, db_temp = linear_activation_backward(grads["dA" + str(l + 2)], current_cache, "relu")
grads["dA" + str(l + 1)] = dA_prev_temp
grads["dW" + str(l + 1)] = dW_temp
grads["db" + str(l + 1)] = db_temp
return grads
def update_parameters(parameters, grads, learning_rate):
"""
使用梯度下降更新参数
参数:
parameters - 包含你的参数的字典
grads - 包含梯度值的字典,是L_model_backward的输出
返回:
parameters - 包含更新参数的字典
参数[“W”+ str(l)] = ...
参数[“b”+ str(l)] = ...
"""
L = len(parameters) // 2 # 整除
"""
一下是博主的代码,我们可以直接写成range(1,L),然后下面就不用写(l + 1)了,因为我们直接从1开始,而不是从0开始,range(start,stop)中start不写,默认就是0
for l in range(L):
parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * grads["dW" + str(l + 1)]
parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * grads["db" + str(l + 1)]
注:运行代码后发现,W1,b1,是相同的,但是W2,b2是有区别的,
W1 = [[-0.59562069 -0.09991781 -2.14584584 1.82662008]
[-1.76569676 -0.80627147 0.51115557 -1.18258802]
[-1.0535704 -0.86128581 0.68284052 2.20374577]]
b1 = [[-0.04659241]
[-1.28888275]
[ 0.53405496]]
W2 = [[-0.5961597 -0.0191305 1.17500122]]
b2 = [[-0.74787095]]
Process finished with exit code 0
但是用博主的代码就和博主运行结果一致
再注:我把range(1,L)改为(1,L+1)结果运行就一致了,少运行一次更新参数,导致更新效果不好,甚至W还变大了
"""
for l in range(L):
parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * grads["dW" + str(l + 1)]
parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * grads["db" + str(l + 1)]
return parameters
#
# def two_layer_model(X, Y, layers_dims, learning_rate=0.0075, num_iterations=3000, print_cost=False, isPlot=True):
# """
# 实现一个两层的神经网络,【LINEAR->RELU】 -> 【LINEAR->SIGMOID】
# 参数:
# X - 输入的数据,维度为(n_x,例子数)
# Y - 标签,向量,0为非猫,1为猫,维度为(1,数量)
# layers_dims - 层数的向量,维度为(n_y,n_h,n_y)
# learning_rate - 学习率
# num_iterations - 迭代的次数
# print_cost - 是否打印成本值,每100次打印一次
# isPlot - 是否绘制出误差值的图谱
# 返回:
# parameters - 一个包含W1,b1,W2,b2的字典变量
# """
# np.random.seed(1)
# grads = {}
# costs = []
# (n_x, n_h, n_y) = layers_dims
#
# """
# 初始化参数
# """
# parameters = initialize_parameters(n_x, n_h, n_y)
#
# W1 = parameters["W1"]
# b1 = parameters["b1"]
# W2 = parameters["W2"]
# b2 = parameters["b2"]
#
# """
# 开始进行迭代
# """
# for i in range(0, num_iterations):
# # 前向传播
# A1, cache1 = linear_activation_forward(X, W1, b1, "relu")
# A2, cache2 = linear_activation_forward(A1, W2, b2, "sigmoid")
#
# # 计算成本
# cost = compute_cost(A2, Y)
#
# # 后向传播
# ##初始化后向传播
# dA2 = - (np.divide(Y, A2) - np.divide(1 - Y, 1 - A2))
#
# ##向后传播,输入:“dA2,cache2,cache1”。 输出:“dA1,dW2,db2;还有dA0(未使用),dW1,db1”。
# dA1, dW2, db2 = linear_activation_backward(dA2, cache2, "sigmoid")
# dA0, dW1, db1 = linear_activation_backward(dA1, cache1, "relu")
#
# ##向后传播完成后的数据保存到grads
# grads["dW1"] = dW1
# grads["db1"] = db1
# grads["dW2"] = dW2
# grads["db2"] = db2
#
# # 更新参数
# parameters = update_parameters(parameters, grads, learning_rate)
# W1 = parameters["W1"]
# b1 = parameters["b1"]
# W2 = parameters["W2"]
# b2 = parameters["b2"]
#
# # 打印成本值,如果print_cost=False则忽略
# if i % 100 == 0:
# # 记录成本
# costs.append(cost)
# # 是否打印成本值
# if print_cost:
# print("第", i, "次迭代,成本值为:", np.squeeze(cost))
# # 迭代完成,根据条件绘制图
# if isPlot:
# plt.plot(np.squeeze(costs))
# plt.ylabel('cost')
# plt.xlabel('iterations (per tens)')
# plt.title("Learning rate =" + str(learning_rate))
# plt.show()
#
# # 返回parameters
# return parameters
# train_set_x_orig , train_set_y , test_set_x_orig , test_set_y , classes = lr_utils.load_dataset()
#
# train_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1).T
# test_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T
#
# train_x = train_x_flatten / 255
# train_y = train_set_y
# test_x = test_x_flatten / 255
# test_y = test_set_y
#
#
# n_x = 12288
# n_h = 7
# n_y = 1
# layers_dims = (n_x,n_h,n_y)
#
# parameters = two_layer_model(train_x, train_set_y, layers_dims = (n_x, n_h, n_y), num_iterations = 2500, print_cost=True,isPlot=True)
def predict(X, y, parameters):
"""
该函数用于预测L层神经网络的结果,当然也包含两层
参数:
X - 测试集
y - 标签
parameters - 训练模型的参数
返回:
p - 给定数据集X的预测
"""
m = X.shape[1]
n = len(parameters) // 2 # 神经网络的层数
p = np.zeros((1, m))
# 根据参数前向传播
probas, caches = L_model_forward(X, parameters)
for i in range(0, probas.shape[1]):
if probas[0, i] > 0.5:
p[0, i] = 1
else:
p[0, i] = 0
print("准确度为: " + str(float(np.sum((p == y)) / m)))
return p
# predictions_train = predict(train_x, train_y, parameters) #训练集
# predictions_test = predict(test_x, test_y, parameters) #测试集
def L_layer_model(X, Y, layers_dims, learning_rate=0.0075, num_iterations=3000, print_cost=False, isPlot=True):
"""
实现一个L层神经网络:[LINEAR-> RELU] *(L-1) - > LINEAR-> SIGMOID。
参数:
X - 输入的数据,维度为(n_x,例子数)
Y - 标签,向量,0为非猫,1为猫,维度为(1,数量)
layers_dims - 层数的向量,维度为(n_y,n_h,···,n_h,n_y)
learning_rate - 学习率
num_iterations - 迭代的次数
print_cost - 是否打印成本值,每100次打印一次
isPlot - 是否绘制出误差值的图谱
返回:
parameters - 模型学习的参数。 然后他们可以用来预测。
"""
np.random.seed(1)
costs = []
parameters = initialize_parameters_deep(layers_dims)
for i in range(0, num_iterations):
AL, caches = L_model_forward(X, parameters)
cost = compute_cost(AL, Y)
grads = L_model_backward(AL, Y, caches)
parameters = update_parameters(parameters, grads, learning_rate)
# 打印成本值,如果print_cost=False则忽略
if i % 100 == 0:
# 记录成本
costs.append(cost)
# 是否打印成本值
if print_cost:
print("第", i, "次迭代,成本值为:", np.squeeze(cost))
# 迭代完成,根据条件绘制图
if isPlot:
plt.plot(np.squeeze(costs))
plt.ylabel('cost')
plt.xlabel('iterations (per tens)')
plt.title("Learning rate =" + str(learning_rate))
plt.show()
return parameters
train_set_x_orig , train_set_y , test_set_x_orig , test_set_y , classes = lr_utils.load_dataset()
train_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1).T
test_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T
train_x = train_x_flatten / 255
train_y = train_set_y
test_x = test_x_flatten / 255
test_y = test_set_y
layers_dims = [12288, 20, 7, 5, 1] # 5-layer model
parameters = L_layer_model(train_x, train_y, layers_dims, num_iterations = 2500, print_cost = True,isPlot=True)
pred_train = predict(train_x, train_y, parameters) #训练集
pred_test = predict(test_x, test_y, parameters) #测试集
def print_mislabeled_images(classes, X, y, p):
"""
绘制预测和实际不同的图像。
X - 数据集
y - 实际的标签
p - 预测
"""
a = p + y
#np.asarray() : 将输入转化为数组
#np.where有两种用法
# 1.np.where(condition,x,y) 当where内有三个参数时,第一个参数表示条件,当条件成立时where方法返回x,当条件不成立时where返回y
# 2.np.where(condition) 当where内只有一个参数时,那个参数表示条件,当条件成立时,where返回的是每个符合condition条件元素的坐标,返回的是以元组的形式
mislabeled_indices = np.asarray(np.where(a == 1))
plt.rcParams['figure.figsize'] = (40.0, 40.0) # set default size of plots设置绘图的默认尺寸,default:默认的;违约(figure.figsize : 图像显示大小)
num_images = len(mislabeled_indices[0])
for i in range(num_images):
index = mislabeled_indices[1][i]
# plt.subplot(2,3,1)也可以简写plt.subplot(231)表示把显示界面分割成2*3的网格。其中,第一个参数是行数,第二个参数是列数,第三个参数表示图形的标号
plt.subplot(2, num_images, i + 1)
plt.imshow(X[:, index].reshape(64, 64, 3), interpolation='nearest')
plt.axis('off')#plt.axis(‘off’) 关闭坐标轴 ;plt.axis(‘square’) 作图为正方形,并且x,y轴范围相同 ; plt.axis(‘equal’) x,y轴刻度等长
plt.title(
"Prediction: " + classes[int(p[0, index])].decode("utf-8") + " \n Class: " + classes[y[0, index]].decode(
"utf-8"))
print_mislabeled_images(classes, test_x, test_y, pred_test)
后续有问题还会补充!!!