搭建一个多层神经网络

这是吴恩达-神经网络和深度学习第四周的课后作业,在这里整理一下思路。
最后的模型是用来判断“是否是猫脸”的任意指定层数的神经网络,其中最后一层的激活函数是sigmoid,中间的隐藏层都用relu。
为了获得每一层的w,b参数,我们使用梯度下降优化,步骤如下:

  1. 初始化
    输入层数,输出初始化的parameters字典,包含每一层的w和b
  2. 前向传播
    2.1 线性函数,输入A_prev,W,b,输出Z并保存linear cache(包含A_prev,W,b)
    2.2 综合了一层中的线性函数与激活函数,输入A_prev,W,b和激活函数名,输出A和cache(包含linear cache和activation cache)
    2.3 综合了所有层的线性函数与激活函数,输入X和parameters字典,输出AL和caches列表,这个列表有L个元素,由每一层的cache组成。
  3. 损失函数
    输入AL和Y,计算cost
  4. 后向传播
    4.1 线性函数,输入dZ,cache(这一层的linear cache),输出dA_prev,dW,db。
    4.2 综合了一层中的线性函数与激活函数,输入dA,cache(包含linear cache和activation cache)和激活函数名,输出dA_prev,dW,db。其实4.2与4.1的输出是一样的,但是4.2多了一个由dA计算dZ的环节,相应地参数中也要多一个激活函数名。
    4.3 综合了所有层的线性函数与激活函数。输入AL,Y,caches,输出grads字典,包含所有的参数的梯度
  5. 更新参数
    输入当前参数,grads字典和学习率,输出更新后的参数。

以上五步是完成了一次迭代,迭代足够多的次数后,我们就获得了所需的每一层的参数w和b,于是我们的模型就唯一确定了。我们还可以写一个predict函数来评估准确率,输入测试集的XY和parameters,输出对测试集的X进行辨认的标签集,并打印准确率。
下面是代码:

0.准备工作

import numpy as np
import h5py
import matplotlib.pyplot as plt
from testCases import *#参见资料包,或者在文章底部copy
from dnn_utils import sigmoid, sigmoid_backward, relu, relu_backward #参见资料包
import lr_utils #参见资料包,或者在文章底部copy
np.random.seed(1)

1. 初始化

这里要注意的是,第l层的w[l]在初始化时要除以np.sqrt(第l-1层的节点数),这是为了防止梯度消失或梯度爆炸。我一开始遗漏了这个细节,导致后面的cost下降十分缓慢几乎不能动。
L是layers_dims的元素个数,是包含了输入层的层数,也就是我们通常意义上的“层数”+1。所谓第一层,应该是第一个隐藏层,而不是输入层。

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)
    L=len(layers_dims)
    parameters={}
    
    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

2. 前向传播

2.1 线性函数
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
2.2 综合线性函数与激活函数
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”的字典,我们需要存储它以有效地计算后向传递
    """
    Z,linear_cache=linear_forward(A_prev,W,b)
    
    if activation=="sigmoid":
        A,activation_cache=sigmoid(Z)
    elif activation=="relu":
        A,activation_cache=relu(Z)
    assert(A.shape==(W.shape[0],A_prev.shape[1]))
        
    cache=(linear_cache,activation_cache)
    return A,cache
2.3 综合所有层的线性函数和激活函数
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)
    """
    A=X
    L=len(parameters)//2
    caches=[]
    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

3.损失函数

def compute_cost(AL,Y):
    """
    实施等式(4)定义的成本函数。

    参数:
        AL - 与标签预测相对应的概率向量,维度为(1,示例数量)
        Y - 标签向量(例如:如果不是猫,则为0,如果是猫则为1),维度为(1,数量)

    返回:
        cost - 交叉熵成本
    """
    m=Y.shape[1]
    cost=(-1/m)*np.sum(np.multiply(Y,np.log(AL))+np.multiply(1-Y,np.log(1-AL)))
    cost=np.squeeze(cost)
    assert(cost.shape==())
    return cost

4.后向传播

4.1 线性函数
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(W.shape==dW.shape)
    assert(b.shape==db.shape)
    assert(A_prev.shape==dA_prev.shape)
    
    return dA_prev,dW,db
4.2 综合线性函数与激活函数
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
4.3 综合所有层的线性函数与激活函数

实际上参数AL和Y的唯一作用就是计算dAL,所以也可以认为参数就是dAL和caches。

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) #共有L层(不包括输入层)
    m=AL.shape[1]
    Y = Y.reshape(AL.shape)
    dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))
    
    current_cache=L   #第L层:输入dAL和caches[L],输出dA(L-1),dWL,dbL
    grads["dA"+str(L-1)],grads["dW"+str(L)],grads["db"+str(L)]=linear_activation_backward(dAL,caches[L-1],activation="sigmoid")
    for l in reversed(range (1,L)):#从第1层至第L-1层为relu函数
        grads["dA"+str(l-1)],grads["dW"+str(l)],grads["db"+str(l)]=linear_activation_backward(grads["dA"+str(l)],caches[l-1],activation="relu")
    return grads
                                       

5.更新参数

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

6.综合以上五步完成模型

这个模型会输出学习得到的参数,也可以打印学习过程中不断下降的成本值。

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(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次,成本值为%f."%(i,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)

运行结果如下:

目前迭代了0次,成本值为0.715732.
目前迭代了100次,成本值为0.674738.
目前迭代了200次,成本值为0.660337.
目前迭代了300次,成本值为0.646289.
目前迭代了400次,成本值为0.629813.
目前迭代了500次,成本值为0.606006.
目前迭代了600次,成本值为0.569004.
目前迭代了700次,成本值为0.519797.
目前迭代了800次,成本值为0.464157.
目前迭代了900次,成本值为0.408420.
目前迭代了1000次,成本值为0.373155.
目前迭代了1100次,成本值为0.305724.
目前迭代了1200次,成本值为0.268102.
目前迭代了1300次,成本值为0.238725.
目前迭代了1400次,成本值为0.206323.
目前迭代了1500次,成本值为0.179439.
目前迭代了1600次,成本值为0.157987.
目前迭代了1700次,成本值为0.142404.
目前迭代了1800次,成本值为0.128652.
目前迭代了1900次,成本值为0.112443.
目前迭代了2000次,成本值为0.085056.
目前迭代了2100次,成本值为0.057584.
目前迭代了2200次,成本值为0.044568.
目前迭代了2300次,成本值为0.038083.
目前迭代了2400次,成本值为0.034411.

在这里插入图片描述

7.评估准确性

def predict(X, y, parameters):
    """
    该函数用于预测L层神经网络的结果,当然也包含两层
    
    参数:
     X - 测试集
     y - 标签
     parameters - 训练模型的参数
    
    返回:
     p - 给定数据集X的预测
    """
    m=X.shape[1]
    probas,caches=L_model_forward(X,parameters)
    p=np.zeros((1,m))
    assert(probas.shape==(1,m))
    for i in range(m):
        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
pred_train = predict(train_x, train_y, parameters) #训练集
pred_test = predict(test_x, test_y, parameters) #测试集

结果如下:

准确率=0.9952153110047847
准确率=0.78

8.用自己的图片验证

这我放在当前文件夹下的my_image.jpg
在这里插入图片描述

import scipy
from scipy import ndimage

my_image = "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)

fname =  my_image
image = np.array(ndimage.imread(fname, flatten=False))
my_image = scipy.misc.imresize(image, size=(64,64)).reshape((64*64*3,1))
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.")

结果如下

准确率=1.0
y = 1.0, your L-layer model predicts a "cat" picture.

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值