搭建多隐层神经网络其实就是在单隐层神经网络的基础上多加了几层隐藏层,此次作业将用python实现L层神经网络的搭建;
其中前L-1层使用Relu激活函数,最后一层使用sigmoid激活函数。
此次作业分为以下几部分:
链接:https://pan.baidu.com/s/151BNM_gcKvKVPz14bJmpQg
提取码:06mg
导入相关工具库
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import testCases #参见资料包,或者在文章底部copy
from dnn_utils import sigmoid, sigmoid_backward, relu, relu_backward #参见资料包
import lr_utils #参见资料包,或者在文章底部copy
主要包括以下几步:
此部分包括:
初始化单隐层神经网络参数函数:initialize_parameters
初始化多层神经网络参数:initialize_parameters_deep
def initialize_parameters(n_x,n_h,n_y):
"""
Args:
n_x: 输入层维度
n_h: 中间层维度
n_y: 输出层维度
Returns:
parameters = {
'W1' : W1, 隐藏层参数w
'b1' : b1, 隐藏层参数b
'W2' : W2, 输出层参数w
'b2' : b2 输出层参数b
}
"""
#随机初始化
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
print("==============测试initialize_parameters==============")
parameters = initialize_parameters(3,2,1)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
def initialize_parameters_deep(layers_dims):
"""
Args:
layers_dims: 每一层的维度数组 例如: layers_dims = [200,7,5,2,1],共5层,每层维度分别为200,7,5,2,1
Returns:
parameters: 包含每层的参数 W 和 b
"""
np.random.seed(3)
parameters = {}
L = len(layers_dims)
#初始化1到L层的参数
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['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
print("==============测试initialize_parameters_deep==============")
layers_dims = [5,4,3]
parameters = initialize_parameters_deep(layers_dims)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
此部分包括:
linear_forward:实现线性传播 :Z = np.dot(W, A) + b,并存储相关值
linear_activation_forward:在线性传播之后使用激活函数进行激活得到激活值 A,并存储相关值
L_model_forward:实现整个L层的前向传播
def linear_forward(A,W,b):
"""
Args:
A: 前一层的输出值
W: 当前层的系数矩阵
b: 当前层的偏差值
Returns:
Z: 当前层的线性输出值
cache: 保存 A,W,b 的值,以备反向传播时使用
"""
#线性计算
Z = np.dot(W, A) + b
assert (Z.shape == (W.shape[0], A.shape[1]))
#保存 A,W,b 的值,以备反向传播时使用
cache = (A,W,b)
return Z,cache
print("==============测试linear_forward==============")
A,W,b = testCases.linear_forward_test_case()
Z,linear_cache = linear_forward(A,W,b)
print("Z = " + str(Z))
def linear_activation_forward(A_prev,W,b,activation):
"""
Args:
A_prev: 上一层的输出值
W: 当前层的系数矩阵
b: 当前层的偏差值
activation: 当前层使用的激活函数 可以为 relu 或 sigmoid
Returns:
A: 当前层激活之后的输出值
cache: 保存 A,W,b,Z 这些中间值,供反向传播求导的时候使用
"""
#进行线性计算
Z, linear_cache = linear_forward(A_prev,W,b)
#根据激活函数计算激活之后的值A,并将Z值保存在activation_cache中
if activation == 'sigmoid':
A, activation_cache = sigmoid(Z)
if activation == 'relu':
A, activation_cache = relu(Z)
assert (A.shape == (W.shape[0], A_prev.shape[1]))
#保存linear_cache中的 A,W,b 和 activation_cache 中的 Z
cache = (linear_cache,activation_cache)
return A,cache
print("==============测试linear_activation_forward==============")
A_prev, W,b = testCases.linear_activation_forward_test_case()
A, linear_activation_cache = linear_activation_forward(A_prev, W, b, activation = "sigmoid")
print("sigmoid,A = " + str(A))
A, linear_activation_cache = linear_activation_forward(A_prev, W, b, activation = "relu")
print("ReLU,A = " + str(A))
def L_model_forward(X,parameters):
"""
Args:
X: 输入层数据 X
parameters: 各层的参数
Returns:
AL: 输出层的激活值
caches: 保存了每一层的 A,W,b,Z 这些值,供反向传播求导使用
"""
A = X
caches = []
#计算层数
L = len(parameters) // 2
#开始从1到L-1层进行前向传播,使用relu激活函数
for l in range(1,L):
A_prev = A
A, cache = linear_activation_forward(A_prev,parameters['W'+str(l)],parameters['b'+str(l)],'relu')
caches.append(cache)
#对最后一层使用sigmoid进行前向传播
AL,cache = linear_activation_forward(A,parameters['W'+str(L)],parameters['b'+str(L)],'sigmoid')
caches.append(cache)
assert (AL.shape == (1,X.shape[1]))
return AL,caches
print("==============测试L_model_forward==============")
X,parameters = testCases.L_model_forward_test_case()
AL,caches = L_model_forward(X,parameters)
print("AL = " + str(AL))
print("caches 的长度为 = " + str(len(caches)))
根据前向传播的输出值AL与数据集的标签Y计算代价
def compute_cost(AL,Y):
"""
Args:
AL: 输出层的激活值
Y: 数据集的标签
Returns:
cost: 代价值
"""
m = Y.shape[1]
cost = -np.sum(np.multiply(Y, np.log(AL)) + np.multiply(1-Y, np.log(1-AL)))/m
cost = np.squeeze(cost)
assert (cost.shape == ())
return cost
print("==============测试compute_cost==============")
Y,AL = testCases.compute_cost_test_case()
print("cost = " + str(compute_cost(AL, Y)))
此部分包括:
linear_backward:针对线性计算部分求导
linear_activation_backward:先对激活函数部分求导得出dZ,再结合linear_backward求导得出 dA_prev,dW,db
L_model_backward:实现全部L层的梯度计算
def linear_backward(dZ,cache):
"""
Args:
dZ: 当前层线性计算Z的偏导数
cache: 包括前一层的激活值A_prev,当前层的系数W,当前层的偏差值b
Returns:
dA_prev: 前一层激活值A_prev的导数
dW:当前层W的导数
db:当前层b的导数
"""
A_prev,W,b = cache
#获取数据集的样本数
m = A_prev.shape[1]
#计算当前层W的导数
dW = np.dot(dZ,A_prev.T)/m
#计算当前层b的导数
db = np.sum(dZ,axis=1,keepdims=True)/m
#计算前一层激活值A_prev的导数
dA_prev = np.dot(W.T,dZ)
assert (dW.shape == W.shape)
assert (db.shape == b.shape)
assert (dA_prev.shape == A_prev.shape)
return dA_prev,dW,db
print("==============测试linear_backward==============")
dZ, linear_cache = testCases.linear_backward_test_case()
dA_prev, dW, db = linear_backward(dZ, linear_cache)
print ("dA_prev = "+ str(dA_prev))
print ("dW = " + str(dW))
print ("db = " + str(db))
def linear_activation_backward(dA,cache,activation='relu'):
"""
Args:
dA: 当前层激活值 A的导数
cache: 前向传播时保存的各种值 包括:A,W,b,Z
activation: 当前层使用的激活函数
Returns:
dA_prev: 前一层激活值A_prev的导数
dW:当前层W的导数
db:当前层b的导数
"""
linear_cache,activation_cache = cache
#对Z进行求导
if activation == 'relu':
dZ = relu_backward(dA,activation_cache)
if activation == 'sigmoid':
dZ = sigmoid_backward(dA,activation_cache)
#线性反向传播
dA_prev,dW,db = linear_backward(dZ,linear_cache)
return dA_prev,dW,db
print("==============测试linear_activation_backward==============")
AL, linear_activation_cache = testCases.linear_activation_backward_test_case()
dA_prev, dW, db = linear_activation_backward(AL, linear_activation_cache, activation="sigmoid")
print("sigmoid:")
print("dA_prev = " + str(dA_prev))
print("dW = " + str(dW))
print("db = " + str(db) + "\n")
dA_prev, dW, db = linear_activation_backward(AL, linear_activation_cache, activation="relu")
print("relu:")
print("dA_prev = " + str(dA_prev))
print("dW = " + str(dW))
print("db = " + str(db))
def L_model_backward(AL,Y,caches):
"""
Args:
AL: 输出层的激活值
Y: 数据集的标签
caches: 前向传播存储的各种值
Returns:
grads: 所有层的参数的导数(梯度)
"""
grads = {}
#获取层数
L = len(caches)
#计算最后一层L层的激活值AL的导数
dAL = -np.divide(Y,AL)+np.divide(1-Y,1-AL)
#计算上一层的激活值A的导数,当前层的W的导数,当前层b的导数
grads['dA'+str(L-1)],grads['dW'+str(L)],grads['db'+str(L)] = linear_activation_backward(dAL,caches[L-1],activation='sigmoid')
#计算前L-1层的导数
for l in reversed(range(L-1)):
grads['dA'+str(l)],grads['dW'+str(l+1)],grads['db'+str(l+1)] = linear_activation_backward(grads['dA'+str(l+1)],caches[l],activation='relu')
return grads
print("==============测试L_model_backward==============")
AL, Y_assess, caches = testCases.L_model_backward_test_case()
grads = L_model_backward(AL, Y_assess, caches)
print ("dW1 = "+ str(grads["dW1"]))
print ("db1 = "+ str(grads["db1"]))
print ("dA0 = "+ str(grads["dA0"]))
def update_parameters(parameters,grads,learning_rate):
"""
Args:
parameters: 参数
grads: 各个参数的梯度
learning_rate: 学习率
Returns:
parameters: 更新后的参数
"""
L = len(parameters)//2
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
print("==============测试update_parameters==============")
parameters, grads = testCases.update_parameters_test_case()
parameters = update_parameters(parameters, grads, 0.1)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
def two_layer_model(X,Y,layers_dims,learning_rate=0.0075,num_iterations=3000,print_cost=False,isPlot=True):
"""
Args:
X: 数据集的输入数据
Y: 数据集标签
layers_dims: 每一层的维度数组
learning_rate: 学习率
num_iterations: 迭代次数
print_cost: 是否打印代价值
isPlot: 是否绘制代价曲线
Returns:
parameters: 最终学得的模型参数
"""
np.random.seed(1)
costs = []
grads = {}
#获取每一层的维度
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,activation='relu')
A2, cache2 = linear_activation_forward(A1,W2,b2,activation='sigmoid')
#计算代价
cost = compute_cost(A2,Y)
#反向传播
dA2 = -np.divide(Y,A2)+np.divide(1-Y,1-A2)
dA1,dW2,db2 = linear_activation_backward(dA2,cache2,activation='sigmoid')
dA0,dW1,db1 = linear_activation_backward(dA1,cache1,activation='relu')
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']
#打印代价
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
n_x = train_x.shape[0]
#设置隐藏层的维度为7
n_h = 7
n_y = 1
layers_dims = (n_x,n_h,n_y)
#训练
parameters = two_layer_model(train_x,train_y,layers_dims,num_iterations=2500,print_cost=True,isPlot=True)
def predict(X,y,parameters):
"""
Args:
X: 测试数据集输入值
y: 测试数据集标签
parameters: 模型参数
Returns:
p: 预测结果
"""
m = X.shape[1]
L = len(parameters)//2
p = np.zeros((1,m))
proba,caches = L_model_forward(X,parameters)
for i in range(p.shape[1]):
if proba[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):
"""
Args:
X: 数据集的输入数据
Y: 数据集标签
layers_dims: 每一层的维度数组
learning_rate: 学习率
num_iterations: 迭代次数
print_cost: 是否打印代价值
isPlot: 是否绘制代价曲线
Returns:
parameters: 最终学得的模型参数
"""
np.random.seed(1)
costs = []
#初始化参数
parameters = initialize_parameters_deep(layers_dims)
#开始训练
for i in range(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)
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
#假设模型有5层,输入层的维度为12288,隐藏层的维度分别为20,7,5,输出层的维度为1
layers_dims = [12288, 20, 7, 5, 1]
#训练模型
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)
第 0 次迭代,成本为: 0.6930497356599891
第 100 次迭代,成本为: 0.6464320953428849
第 200 次迭代,成本为: 0.6325140647912678
第 300 次迭代,成本为: 0.6015024920354665
第 400 次迭代,成本为: 0.5601966311605748
第 500 次迭代,成本为: 0.515830477276473
第 600 次迭代,成本为: 0.47549013139433266
第 700 次迭代,成本为: 0.4339163151225749
第 800 次迭代,成本为: 0.40079775362038883
第 900 次迭代,成本为: 0.3580705011323798
第 1000 次迭代,成本为: 0.33942815383664127
第 1100 次迭代,成本为: 0.3052753636196265
第 1200 次迭代,成本为: 0.2749137728213015
第 1300 次迭代,成本为: 0.24681768210614843
第 1400 次迭代,成本为: 0.19850735037466116
第 1500 次迭代,成本为: 0.1744831811255663
第 1600 次迭代,成本为: 0.17080762978096575
第 1700 次迭代,成本为: 0.11306524562164726
第 1800 次迭代,成本为: 0.09629426845937152
第 1900 次迭代,成本为: 0.08342617959726863
第 2000 次迭代,成本为: 0.07439078704319081
第 2100 次迭代,成本为: 0.06630748132267932
第 2200 次迭代,成本为: 0.05919329501038171
第 2300 次迭代,成本为: 0.053361403485605564
第 2400 次迭代,成本为: 0.0485547856287702
准确度为: 1.0
准确度为: 0.72
第 0 次迭代,成本为: 0.715731513413713
第 100 次迭代,成本为: 0.6747377593469114
第 200 次迭代,成本为: 0.6603365433622127
第 300 次迭代,成本为: 0.6462887802148751
第 400 次迭代,成本为: 0.6298131216927773
第 500 次迭代,成本为: 0.606005622926534
第 600 次迭代,成本为: 0.5690041263975135
第 700 次迭代,成本为: 0.519796535043806
第 800 次迭代,成本为: 0.46415716786282285
第 900 次迭代,成本为: 0.40842030048298916
第 1000 次迭代,成本为: 0.37315499216069037
第 1100 次迭代,成本为: 0.3057237457304712
第 1200 次迭代,成本为: 0.2681015284774084
第 1300 次迭代,成本为: 0.23872474827672593
第 1400 次迭代,成本为: 0.20632263257914712
第 1500 次迭代,成本为: 0.17943886927493544
第 1600 次迭代,成本为: 0.15798735818801202
第 1700 次迭代,成本为: 0.14240413012273906
第 1800 次迭代,成本为: 0.12865165997885727
第 1900 次迭代,成本为: 0.11244314998155124
第 2000 次迭代,成本为: 0.08505631034966049
第 2100 次迭代,成本为: 0.05758391198605336
第 2200 次迭代,成本为: 0.04456753454693651
第 2300 次迭代,成本为: 0.03808275166597559
第 2400 次迭代,成本为: 0.034410749018402347
准确度为: 0.9952153110047847
准确度为: 0.78
两层模型代价函数曲线:
多层模型代价函数曲线:
def print_mislabeled_image(classes,X,y,p):
"""
Args:
classes: 字节码文件,用于输出结果
X: 数据集输入值
y: 数据集标签
p: 预测值
Returns:None
"""
#获取预测错误的坐标值
a = y+p
mislabeled_indices = np.asarray(np.where(a==1))
#设置画板大小
plt.rcParams['figure.figsize'] = (40.0,40.0)
#获取预测错误的数量
num_images = len(mislabeled_indices[0])
#打印错误分类的图片
for i in range(num_images):
index = mislabeled_indices[1][i]
plt.subplot(2,num_images,i+1)
plt.imshow(X[:,index].reshape(64,64,3))
plt.axis("off")
plt.title("Prediction:"+classes[int(p[0,index])].decode("utf-8")+"\n Class:"+classes[y[0,index]].decode("utf-8"))
plt.show()
#查看测试集中,错误预测的图片
print_mislabeled_image(classes,test_x,test_y,pred_test)
#用自己的一张图片进行预测
my_image = 'timg.jpg'
my_label_y = [1]
image = np.array(Image.open(my_image).convert("RGB").resize((64, 64)))
my_image = image.reshape((64*64*3,1))
my_predicted_image = predict(my_image,my_label_y,parameters)
plt.imshow(image)
plt.show()
print ("y = " + str(np.squeeze(my_predicted_image)) + ", your L-layer model predicts a \"" + classes[int(np.squeeze(my_predicted_image)),].decode("utf-8") + "\" picture.")
测试结果:y = 1.0, your L-layer model predicts a "cat" picture.