Ng深度学习笔记—搭建多层神经网络python实现

搭建多隐层神经网络其实就是在单隐层神经网络的基础上多加了几层隐藏层,此次作业将用python实现L层神经网络的搭建;

其中前L-1层使用Relu激活函数,最后一层使用sigmoid激活函数。

此次作业分为以下几部分:

  1. 模型搭建
  2. 训练模型
  3. 测试模型
  4. 用自己的图片做测试

实验所需文件:

链接: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

一、模型搭建

主要包括以下几步:

  1. 初始化参数
  2. 前向传播
  3. 计算代价
  4. 反向传播
  5. 更新参数

1.初始化参数

此部分包括:

初始化单隐层神经网络参数函数: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"]))

2.前向传播

此部分包括:

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)))

3.计算代价

根据前向传播的输出值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)))

4.反向传播

此部分包括:

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"]))

5.更新参数

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"]))

至此,搭建模型所需的所有功能模块都已完成,下面将这些功能模块组装成完整模型;包括:

两层的神经网络模型:two_layer_model

多层的神经网络模型:L_layer_model

预测函数:predict

并且使用两种模型对训练集和测试集进行了预测,数据集实验文件已经给出,直接使用即可;

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.

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值