Descargue el conjunto de datos en la misma ruta que el siguiente código.
El primero es lr_utils.py que carga el conjunto de datos:
import numpy as np
import h5py
def load_dataset():
train_dataset = h5py.File('datasets/train_catvnoncat.h5', "r")
train_set_x_orig = np.array(train_dataset["train_set_x"][:]) # your train set features
train_set_y_orig = np.array(train_dataset["train_set_y"][:]) # your train set labels
test_dataset = h5py.File('datasets/test_catvnoncat.h5', "r")
test_set_x_orig = np.array(test_dataset["test_set_x"][:]) # your test set features
test_set_y_orig = np.array(test_dataset["test_set_y"][:]) # your test set labels
classes = np.array(test_dataset["list_classes"][:]) # the list of classes
train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))
test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))
return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes
Entonces dnn_utils.py es una función de cálculo directo y derivación inversa de algunas funciones de activación:
import numpy as np
def sigmoid(Z):
"""
Implements the sigmoid activation in numpy
Arguments:
Z -- numpy array of any shape
Returns:
A -- output of sigmoid(z), same shape as Z
cache -- returns Z as well, useful during backpropagation
"""
A = 1/(1+np.exp(-Z))
cache = Z
return A, cache
def sigmoid_backward(dA, cache):
"""
Implement the backward propagation for a single SIGMOID unit.
Arguments:
dA -- post-activation gradient, of any shape
cache -- 'Z' where we store for computing backward propagation efficiently
Returns:
dZ -- Gradient of the cost with respect to Z
"""
Z = cache
s = 1/(1+np.exp(-Z))
dZ = dA * s * (1-s)
assert (dZ.shape == Z.shape)
return dZ
def relu(Z):
"""
Implement the RELU function.
Arguments:
Z -- Output of the linear layer, of any shape
Returns:
A -- Post-activation parameter, of the same shape as Z
cache -- a python dictionary containing "A" ; stored for computing the backward pass efficiently
"""
A = np.maximum(0,Z)
assert(A.shape == Z.shape)
cache = Z
return A, cache
def relu_backward(dA, cache):
"""
Implement the backward propagation for a single RELU unit.
Arguments:
dA -- post-activation gradient, of any shape
cache -- 'Z' where we store for computing backward propagation efficiently
Returns:
dZ -- Gradient of the cost with respect to Z
"""
Z = cache
dZ = np.array(dA, copy=True) # just converting dz to a correct object.
# When z <= 0, you should set dz to 0 as well.
dZ[Z <= 0] = 0
assert (dZ.shape == Z.shape)
return dZ
Finalmente, el modelo.py para propagación hacia adelante y hacia atrás:
import numpy as np
import h5py
import matplotlib.pyplot as plt
import testCases
from dnn_utils import sigmoid, sigmoid_backward, relu, relu_backward
import lr_utils
np.random.seed(1)
# 初始化w,b
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["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
# 根据上一层的A,计算这层的W、b、Z
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
# 根据Z计算A
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
# 前向传播(循环每层进行上面两个函数的先后调用),隐藏层使用relu激活,输出层使用sigmoid激活
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
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)
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
# 计算loss
def compute_cost(AL, Y):
"""
实施等式定义的成本函数。
参数:
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
# 根据dZ计算出dW、db、上一层的dA
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
# 计算出dZ
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
# 反向传播,遍历每层先后调用上面两个函数,计算出梯度(dA、dW、db),隐藏层使用relu激活,输出层使用sigmoid激活
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 # 整除
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 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) # 计算loss
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
# 使用最终得到的参数,进行检测
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
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 # reshape成(高*宽*3, 数据集数量)的shape形式
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, 7, 1] # 定义网络每层的神经元数量
# 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) # 测试集
Primero prueba con una sola capa oculta, es decir, una entrada de 12288, una capa oculta de 7 unidades y una salida, los resultados de la operación son los siguientes:
第 0 次迭代,成本值为: 0.6728793556302541
第 100 次迭代,成本值为: 0.5285752698925862
第 200 次迭代,成本值为: 0.45818007835609126
第 300 次迭代,成本值为: 0.3876529946069491
第 400 次迭代,成本值为: 0.35164669272927007
第 500 次迭代,成本值为: 0.2553231097200821
第 600 次迭代,成本值为: 0.23603737202533007
第 700 次迭代,成本值为: 0.3376668584929485
第 800 次迭代,成本值为: 0.1548196123106569
第 900 次迭代,成本值为: 0.141659188493233
第 1000 次迭代,成本值为: 0.10701830419308975
第 1100 次迭代,成本值为: 0.08937626940589842
第 1200 次迭代,成本值为: 0.07708861476226071
第 1300 次迭代,成本值为: 0.06575199523065513
第 1400 次迭代,成本值为: 0.05419505617926541
第 1500 次迭代,成本值为: 0.0467123535778884
第 1600 次迭代,成本值为: 0.041145328072405674
第 1700 次迭代,成本值为: 0.03667123529185284
第 1800 次迭代,成本值为: 0.03291411594344512
第 1900 次迭代,成本值为: 0.029733852228226203
第 2000 次迭代,成本值为: 0.027066871217302946
第 2100 次迭代,成本值为: 0.02472223470679393
第 2200 次迭代,成本值为: 0.022700977329167062
第 2300 次迭代,成本值为: 0.02091443251056507
第 2400 次迭代,成本值为: 0.019358608557795146
准确度为: 1.0
准确度为: 0.76
La tasa de precisión en el conjunto de prueba es 0,76, que es más alta que el 0,7 de un solo nodo.
A continuación, pruebe con una red de 5 capas (3 capas ocultas), es decir, 12288 entradas, 20 unidades en la primera capa, 7 unidades en la segunda capa, 5 unidades en la tercera capa y 1 salida. El resultado de la operación es el siguiente:
第 0 次迭代,成本值为: 0.715731513413713
第 100 次迭代,成本值为: 0.6747377593469114
第 200 次迭代,成本值为: 0.6603365433622128
第 300 次迭代,成本值为: 0.6462887802148751
第 400 次迭代,成本值为: 0.6298131216927773
第 500 次迭代,成本值为: 0.606005622926534
第 600 次迭代,成本值为: 0.5690041263975134
第 700 次迭代,成本值为: 0.519796535043806
第 800 次迭代,成本值为: 0.46415716786282285
第 900 次迭代,成本值为: 0.40842030048298916
第 1000 次迭代,成本值为: 0.37315499216069026
第 1100 次迭代,成本值为: 0.30572374573047106
第 1200 次迭代,成本值为: 0.2681015284774083
第 1300 次迭代,成本值为: 0.2387247482767269
第 1400 次迭代,成本值为: 0.20632263257914718
第 1500 次迭代,成本值为: 0.1794388692749363
第 1600 次迭代,成本值为: 0.15798735818801823
第 1700 次迭代,成本值为: 0.14240413012274733
第 1800 次迭代,成本值为: 0.1286516599788995
第 1900 次迭代,成本值为: 0.11244314998168348
第 2000 次迭代,成本值为: 0.08505631034989446
第 2100 次迭代,成本值为: 0.057583911986215625
第 2200 次迭代,成本值为: 0.04456753454701475
第 2300 次迭代,成本值为: 0.03808275166601415
第 2400 次迭代,成本值为: 0.03441074901842744
准确度为: 0.9952153110047847
准确度为: 0.78
La precisión de la predicción en el conjunto de prueba es de 0,78, que es superior a la de una sola capa oculta.
También puede usar las imágenes que encuentre para la predicción, tome la siguiente imagen como ejemplo:
el código de predicción es el siguiente:
fname = "my_image.jpg" # change this to the name of your image file
my_label_y = [1] # the true class of your image (1 -> cat, 0 -> non-cat)
import imageio
from PIL import Image
num_px = train_set_x_orig.shape[1]
image = np.array(imageio.imread(fname))
my_image = np.array(Image.fromarray(image).resize((num_px,num_px))).reshape((1, num_px*num_px*3)).T # 转化为与网络输入格式相匹配的shape
my_predicted_image = predict(my_image, my_label_y, parameters)
plt.imshow(image)
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.")
resultado de la operación:
准确度为: 1.0
y = 1.0, your L-layer model predicts a "cat" picture.
Desde la perspectiva de la precisión, la predicción es exitosa y el resultado es un gato. Obviamente, la imagen es un gato.