神经网络实践之正则化

前言

机器学习的应用层面中,已经了解了神经网络中的一些有关实践层面的正则化方法,本篇文章将会尝试利用各种正则化方法优化一个过拟合严重的神经网络。

问题引入:
假设,您刚刚被法国足球公司聘为AI专家。 他们希望您推荐法国守门员应将球踢到的位置,以便法国队的球员可以用头撞球。

为了实现这一问题,首先导入需要用到的python

导入相关库


import numpy as np
import matplotlib.pyplot as plt
import sklearn
import sklearn.datasets
import scipy.io
# 数据可视化图形设置
%matplotlib inline
plt.rcParams['figure.figsize'] = (7.0, 4.0) # 设置图篇默认大小
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

加载数据并可视化显示

加载数据集并可视化的代码如下所示:


def load_2D_dataset():
    data = scipy.io.loadmat('datasets/data.mat')
    train_X = data['X'].T
    train_Y = data['y'].T
    test_X = data['Xval'].T
    test_Y = data['yval'].T

    plt.scatter(train_X[0, :], train_X[1, :], c=np.squeeze(train_Y), s=40, cmap=plt.cm.Spectral);
    
    return train_X, train_Y, test_X, test_Y

train_X, train_Y, test_X, test_Y = load_2D_dataset()

可视化显示如下所示:

如上图所示,每个点对应于足球场上的位置,在该位置上,法国守门员从足球场左侧射出球后,足球运动员用他的头将球击中。

  • 如果这个点使用蓝色表示的,则意味着法国队员能够用他的头部击球。
  • 如果这个点是用蓝色表示的,则意味着其他队的球员能用他的头部击球。

本次任务的目标就是利用神经网络模型,为法国足球队的守门员提供可能的位置。通过数据分析,可以看出,尽管有一点噪音,但是似乎可以利用一条反对角线分割数据。本篇文章,将会使用不同的正则化模型,评估那种模型具有更好的性能。

不使用正则化的模型

神经网络模型的搭建,可以参考一步步构建一个神经网络的文章,构建一个神经网络的代码如下所示:


def model(X, Y, learning_rate = 0.3, num_iterations = 30000, print_cost = True, lambd = 0, keep_prob = 1):
    """
    神经网络的结构: LINEAR->RELU->LINEAR->RELU->LINEAR->SIGMOID.
    
    参数:
    X -- 输入数据的维数 (特征数量, 样本数量)
    Y -- 标签向量(1代表蓝点,0代表红点,数据维数(输出向量大小,样本数量))
    learning_rate --优化的学习率
    num_iterations -- 跌打次数
    print_cost -- 如果设置为正,输出每1000次迭代的损失值
    lambd -- 正则化超参数
    keep_prob - dropout正则化中,保留神经元的概率
    
   返回值:
    parameters -- 返回模型可以被预测的参数
    """
        
    grads = {}
    costs = []                            # to keep track of the cost
    m = X.shape[1]                        # number of examples
    layers_dims = [X.shape[0], 20, 3, 1]
    
    # 初始化参数字典
    parameters = initialize_parameters(layers_dims)

    # 梯度下降的迭代次数

    for i in range(0, num_iterations):

        # 前向传播: LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID.
        if keep_prob == 1:
            a3, cache = forward_propagation(X, parameters)
        elif keep_prob < 1:
            a3, cache = forward_propagation_with_dropout(X, parameters, keep_prob)
        
        # 损失函数
        if lambd == 0:
            cost = compute_cost(a3, Y)
        else:
            cost = compute_cost_with_regularization(a3, Y, parameters, lambd)
            
        # 反向传播
        assert(lambd==0 or keep_prob==1)    # it is possible to use both L2 regularization and dropout, 
                                            # but this assignment will only explore one at a time
        if lambd == 0 and keep_prob == 1:
            grads = backward_propagation(X, Y, cache)
        elif lambd != 0:
            grads = backward_propagation_with_regularization(X, Y, cache, lambd)
        elif keep_prob < 1:
            grads = backward_propagation_with_dropout(X, Y, cache, keep_prob)
        
        # 参数更新
        parameters = update_parameters(parameters, grads, learning_rate)
        
        # 打印出没10000次迭代的损失
        if print_cost and i % 10000 == 0:
            print("Cost after iteration {}: {}".format(i, cost))
        if print_cost and i % 1000 == 0:
            costs.append(cost)
    

以上代码中,设置正则化参数\lambda = 0,也就是不使用正则化参数,可以看出梯度下降的过程和训练集和测试集的精度如下所示:

绘制出决策边界之后,如下图所示:


可以看出,决策边界包含了一些噪声点,模型有可能会造成过拟合现象。

L2正则化

L2正则化的前向传播函数

L2正则化方式是一种能够有效避免过拟合的标准方式,使用L2正则化之后,原来的损失函数:
J = -\frac{1}{m} \sum\limits_{i = 1}^{m} \large{(}\small y^{(i)}\log\left(a^{[L](i)}\right) + (1-y^{(i)})\log\left(1- a^{[L](i)}\right) \large{)} \tag{1}

将会变成如下所示:
J_{regularized} = \small \underbrace{-\frac{1}{m} \sum\limits_{i = 1}^{m} \large{(}\small y^{(i)}\log\left(a^{[L](i)}\right) + (1-y^{(i)})\log\left(1- a^{[L](i)}\right) \large{)} }_\text{cross-entropy cost} + \underbrace{\frac{1}{m} \frac{\lambda}{2} \sum\limits_l\sum\limits_k\sum\limits_j W_{k,j}^{[l]2} }_\text{L2 regularization cost} \tag{2}

其中,l代表神经网络的层数,而k代表神经网络中l层的神经元数量,j代表神经网络中第l-1层的神经元数量

综上,带L2正则项的损失函数计算公式如下所示:


​
def compute_cost_with_regularization(A3, Y, parameters, lambd):
    """
 参数:
    A3 --前向传播的输出 维数为(输出大小,样本数量)
    Y -- 输出标签,维数为(输出大小,样本数量)
    parameters -- 以字典形式存储的一系列模型参数
    
   返回值::
    cost - 正则化之后的损失
    """
    m = Y.shape[1]
    W1 = parameters["W1"]
    W2 = parameters["W2"]
    W3 = parameters["W3"]
    
    cross_entropy_cost = compute_cost(A3, Y)
    L2_regularization_cost = (1/m)*(lambd/2)*(np.sum(np.square(W1))+np.sum(np.square(W2))+np.sum(np.square(W3)))
   
    
    cost = cross_entropy_cost + L2_regularization_cost
    
    return cost
A3, Y_assess, parameters = compute_cost_with_regularization_test_case()
​
print("cost = " + str(compute_cost_with_regularization(A3, Y_assess, parameters, lambd = 0.1)))

L2正则化之后的反向传播函数

实现L2正则化之后,神经网络的损失函数发生了变化,所以其反向传播的实现方式也发生了变化,变化的具体地方就是需要对正则化也需要求导,对正则化进行求导的公式如下所示:

\frac{d}{dW} ( \frac{1}{2}\frac{\lambda}{m} W^2) = \frac{\lambda}{m} \tag{3} W

根据以上公式,正则化之后的反向传播函数的实现代码如下所示:


def backward_propagation_with_regularization(X, Y, cache, lambd):
    """
带有正则化的项的神经网络反向传播函数
    
   参数:
    X -- 输入数据集(样本特征数量,样本大小)
    Y --输出标签向量(输出向量大小,样本大小)
    cache -- 前向传播过程中得到的一系列参数
    lambd -- 正则化参数
    
    返回值:
    gradients --包含各个参数的梯度字典
    """
    
    m = X.shape[1]
    (Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3) = cache
    
    dZ3 = A3 - Y
    

    dW3 = 1./m * np.dot(dZ3, A2.T) + lambd/m*W3

    db3 = 1./m * np.sum(dZ3, axis=1, keepdims = True)
    
    dA2 = np.dot(W3.T, dZ3)
    dZ2 = np.multiply(dA2, np.int64(A2 > 0))
    
    dW2 = 1./m * np.dot(dZ2, A1.T) + lambd/m*W2
    
    db2 = 1./m * np.sum(dZ2, axis=1, keepdims = True)
    
    dA1 = np.dot(W2.T, dZ2)
    dZ1 = np.multiply(dA1, np.int64(A1 > 0))
  
    dW1 = 1./m * np.dot(dZ1, X.T) + lambd/m*W1

    db1 = 1./m * np.sum(dZ1, axis=1, keepdims = True)
    
    gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3,"dA2": dA2,
                 "dZ2": dZ2, "dW2": dW2, "db2": db2, "dA1": dA1, 
                 "dZ1": dZ1, "dW1": dW1, "db1": db1}
    
    return gradients

运行以上代码之后,可以看出,训练集精确度和测试集精确度比较接近,都达到可93%的精度,但是损失值较不使用正则化的模型有所上升。

绘制决策边界,可以看出,较之不使用正则化的模型,有效地减少了过拟合,使得决策边界更为“平滑”,如果正则化参数\lambda变得更大一些,决策边界就会变得更加平滑,但是也会导致模型精度下降,误差变大。

dropout正则化

dropout正则化,也被称之为随机失活正则化,即是在每次迭代的过程中,随机失活一些神经元,也就是将一些神经元的输出变为0,其中,参数keep-prob代表着保留该神经元的概率。

dropout正则化的前向传播

在一个3层的神经网络中,需要对第一个和第二个隐藏层使用dropout正则化,输入层和输出层并不会使用dropout正则化。dropout正则化的前向传播过程可以分为四个步骤实现:

  • 创建一个随机向量D^{[1]} = [d^{[1](1)} d^{[2](2)} ... d^{[l](m)}],其维数大小和矩阵A^{[1]}一样,并且使其随机值在(0,1)之间分布。

  • 对于向量D^{[1]}的每一项而言,使用1-keep_prob的概率设置为0,keep_prob的概率设置为1

  • A^{[1]} = D^{[1]}*A^{[1]},即也就是利用乘法的特性,将D^{[l]} = 0的神经元消除。

  • A^{[1]} / = keep\_prob,确保与没有实现dropout正则化网络有相同的损失值。

综上所述,dropout正则化的前向传播实现,如下代码所示:


def forward_propagation_with_dropout(X, parameters, keep_prob = 0.5):
  
   
    np.random.seed(1)
    
  
    W1 = parameters["W1"]
    b1 = parameters["b1"]
    W2 = parameters["W2"]
    b2 = parameters["b2"]
    W3 = parameters["W3"]
    b3 = parameters["b3"]

    A1 = relu(Z1)

    D1 = np.random.rand(A1.shape[0],A1.shape[1])                                         
    D1 = (D1<keep_prob)                                                                                                                                                                                                                                                                                                                      
    A1 = np.multiply(A1,D1)                                    
    A1 = A1/keep_prob                                     
    Z2 = np.dot(W2, A1) + b2
    A2 = relu(Z2)
  
    D2 = np.random.rand(A2.shape[0],A2.shape[1])                                         
    D2 = (D2<keep_prob)                                      
    A2 = np.multiply(A2,D2)                                    
    A2 = A2/keep_prob                                    
 
    Z3 = np.dot(W3, A2) + b3
    A3 = sigmoid(Z3)
    
    cache = (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3)
    
    return A3, cache

dropout正则化的反向传播过程

dropout正则化的反向传播方法比正向传播的的实现更为简单,可以分为以下两个步骤:

  • 反向传播与正向传播一样,也需要对相同的神经元随机失活,只不过是利用向量D改变dA的值。

  • 与前向传播一样,也需要在每一层除以keep\_prob,只不过是将A变成dA

综上,反向传播的dropout正则化的实现代码如下所示:


def backward_propagation_with_dropout(X, Y, cache, keep_prob):
  
    m = X.shape[1]
    (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3) = cache
    
    dZ3 = A3 - Y
    dW3 = 1./m * np.dot(dZ3, A2.T)
    db3 = 1./m * np.sum(dZ3, axis=1, keepdims = True)
    dA2 = np.dot(W3.T, dZ3)
  
    dA2 = np.multiply(dA2,D2)              
    dA2 = dA2/keep_prob             

    dZ2 = np.multiply(dA2, np.int64(A2 > 0))
    dW2 = 1./m * np.dot(dZ2, A1.T)
    db2 = 1./m * np.sum(dZ2, axis=1, keepdims = True)
    
    dA1 = np.dot(W2.T, dZ2)
   
    dA1 = np.multiply(dA1,D1)            
    dA1 = dA1/keep_prob            
    dZ1 = np.multiply(dA1, np.int64(A1 > 0))
    dW1 = 1./m * np.dot(dZ1, X.T)
    db1 = 1./m * np.sum(dZ1, axis=1, keepdims = True)
    
    gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3,"dA2": dA2,
                 "dZ2": dZ2, "dW2": dW2, "db2": db2, "dA1": dA1, 
                 "dZ1": dZ1, "dW1": dW1, "db1": db1}
    
    return gradients

运行以上代码,经过多次迭代之后的损失值和性能如下所示:

绘制此模型的决策边界,如下所示:


与不使用正则化的模型,使用L2正则化的模型相比,dropout正则化的模型经过多次迭代之后,损失值最低,在训练集上的精度有所下降,但是在测试集精度上升,决策边界在能够保持平滑的同时,分类也更加准确,模型总体上性能更好。

使用dropout正则化的注意事项:

  • 仅仅在训练集上使用dropout正则化,在测试集上不使用。
  • 模型的前向传播和反向传播都要使用dropout正则化。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352

推荐阅读更多精彩内容