深度学习中的正则化技术(附Python代码)-阿里云开发者社区

开发者社区> 技术小能手> 正文

深度学习中的正则化技术(附Python代码)

简介:
+关注继续查看

数据科学家面临的常见问题之一是如何避免过拟合。你是否碰到过这样一种情况:你的模型在训练集上表现异常好,却无法预测测试数据。或者在一个竞赛中你排在public leaderboard的顶端,但是在最终排名中却落后了几百名?那么这篇文章就是为你而准备的!

(译者注: 在kaggle这样的数据竞赛中, public leaderboard排名是根据一部分测试集来计算的,用于给选手提供及时的反馈和动态展示比赛的进行情况;而private leaderboard是根据测试集的剩余部分计算而来,用于计算选手的最终得分和排名。 通常我们可以把public LB理解为在验证集上的得分,private LB为真正未知数据集上的得分,这样做的目的是提醒参赛者,我们建模的目标是获取一个泛化能力好的模型)

避免过拟合可以提高我们模型的性能。

b7db155d62f4a5122b366c4d08979f73606f25c1

目录

1. 什么是正则化?

2. 正则化如何减少过拟合?

3. 深度学习中的各种正则化技术:

d47e62d2b349aca45e42305ed6714efbe5ed61d9L2和L1正则化
d47e62d2b349aca45e42305ed6714efbe5ed61d9Dropout
d47e62d2b349aca45e42305ed6714efbe5ed61d9数据增强(Data augmentation)
d47e62d2b349aca45e42305ed6714efbe5ed61d9提前停止(Early stopping)

4. 案例:在MNIST数据集上使用Keras的案例研究

1. 什么是正则化?

在深入该主题之前,先来看看这几幅图:

44cce9058f81ffb8003f0584d6a53574bfe01969

之前见过这幅图吗?从左到右看,我们的模型从训练集的噪音数据中学习了过多的细节,最终导致模型在未知数据上的性能不好。

换句话说,从左向右,模型的复杂度在增加以至于训练误差减少,然而测试误差未必减少。如下图所示:

8bf51b4f7f21c6cf7790c28a634048ec684dce43

Source: Slideplayer

如果你曾经构建过神经网络,你就知道它们有多复杂。这也使得它们更容易过拟合。

5a228334ef251f21db1d48827a5422115956aa4c

正则化技术是对学习算法做轻微的修改使得它泛化能力更强。这反过来就改善了模型在未知数据上的性能。

2. 正则化如何减少过拟合?

我们来看一个在训练数据上过拟合的神经网络,如下图所示:

05826d3a5bc40b3e68a4a4138e2a26548e4e14bc

如果你曾经学习过机器学习中的正则化,你会有一个概念,即正则化惩罚了系数。在深度学习中,它实际上惩罚了节点的权重矩阵。

假设我们的正则化系数很高,以至于某些权重矩阵近乎于0:

e9154de49552c893a1861aed6464218f546cbaaf

这会得到一个简单的线性网络,而且在训练数据集上轻微的欠拟合。

如此大的正则化系数并不是那么有用。我们需要对其进行优化从而得到一个拟合良好的模型,正如下图所示:

54d023b5a2d26468fa9f7a028bac2d43ef55eb8c

3. 深度学习中的各种正则化技术

我们已经理解了正则化如何帮助减少过拟合,现在我们将学习一些把正则化用于深度学习的技术。

d47e62d2b349aca45e42305ed6714efbe5ed61d9L1和L2正则化

L1和L2是最常见的正则化类型。它们通过增加一个被称为正则项的额外项来更新成本函数:

Cost function = Loss (say, binary cross entropy) + Regularization term

由于增加了这个正则项,权重矩阵的值减小了,因为这里假定了具有较小权重矩阵的神经网络会导致更简单的模型。因此,它也会在相当程度上减少过拟合。

然而,该正则项在L1和L2中是不同的。

L2中,我们有:

9a52f71f3018c1072e1832ba1f17ba9eed9415e7

这里,lambda是正则参数。它是一个超参数用来优化得到更好的结果。L2正则化也叫权重衰减(weight decay),因重朝着0衰减(但不会0)

在L1中,我有:

d07df5b3bbf9211b9ab7f07abfc09150fbbf6fff

这里,我们惩罚了权重的绝对值。不像L2, 这里的权重是有可能衰减到0的。因此,当我们想压缩模型的时候, L1非常有用,否则会偏向使用L2.

在Keras中,我们可以使用regularizers直接在任意层上应用正则化。

下面是一段样例代码,把L2正则化用于一层之上:

from keras import regularizers

model.add(Dense(64, input_dim=64,

kernel_regularizer=regularizers.l2(0.01)

注意:这里的值0.01是正则化参数的值,即lambda, 它需要被进一步优化。可以使用grid-search的方法来优化它。

同样的,我们也可以采用L1正则化。后文中的案例研究会看到更多细节。

d47e62d2b349aca45e42305ed6714efbe5ed61d9Dropout

这是一种非常有趣的正则化技术。它的效果非常好因此在深度学习领域中最常被使用。

为了理解dropout,假设我们的神经网络结构如下所示:

1d4f9b23e68b86cf7f986844497b3a34fcadc1e5

dropout做什么呢?每次迭代,随机选择一些节点,将它们连同相应的输入和输出一起删掉,如下图:

27f27bca3fff7ef448c55e4c98b636bda04d8194

所以,每一轮迭代都有不同的节点集合,这也导致了不同的输出。它也可以被认为是一种机器学习中的集成技术(ensemble technique)。

集成模型(ensemble models)通常比单一模型表现更好,因为捕获了更多的随机性。同样的,比起正常的神经网络模型,dropout也表现的更好。

选择丢弃多少节点的概率是dropout函数的超参数。如上图所示,dropout可以被用在隐藏层以及输入层。

37d6ef5405d0037363de6f11d38e86f742724eed

由于这些原因,当我们有较大的神经网络时,为了引入更多的随机性,通常会优先使用dropout。

在Keras中,可以使用Keras core layer来实现dropout。下面是对应的Python代码:

from keras.layers.core import Dropout

model = Sequential([

Dense(output_dim=hidden1_num_units, input_dim=input_num_units, activation='relu'),

Dropout(0.25),

Dense(output_dim=output_num_units, input_dim=hidden5_num_units, activation='softmax'),

])

正如你看到的,这里定义丢弃概率为0.25。我们可以使用grid search方法来微调它以获得更好的结果。

d47e62d2b349aca45e42305ed6714efbe5ed61d9数据增强(Data augmentation)

减少过拟合最简单的方式其实是增加训练集的大小。在机器学习中,由于人工标注数据成本过高所以很难增加训练集的大小。

但是,考虑一下如果我们处理的是图像。在这种情况下,有一些方法可以增加训练集的大小——旋转、翻转、缩放、移动等等。下图中,对手写数字数据集进行了一些转换:

29e120f80cbbe65177601980eed2d5cea67858e0

这种技术叫做数据增强。通常会明显改善模型的准确率。为了提高模型预测能力,这种技术可以被视为一种强制性技巧。

在Keras中,我们使用ImageDataGenerator来执行所有这些转换。它有一大堆参数,你可以用它们来预处理你的训练数据。

下面是示例代码:

from keras.preprocessing.image import ImageDataGenerator

datagen = ImageDataGenerator(horizontal flip=True)

datagen.fit(train)

d47e62d2b349aca45e42305ed6714efbe5ed61d9提前停止(Early stopping)

提前停止是一种交叉验证的策略,即把一部分训练集保留作为验证集。当看到验证集上的性能变差时,就立即停止模型的训练。

e073fc7c0e08258afb081db35ddc2604b6e0fbc0

在上图中,我们在虚线处停止模型的训练,因为在此处之后模型会开始在训练数据上过拟合。

在Keras中,使用callbacks函数来应用提前停止。下面是代码:

from keras.callbacks import EarlyStopping

EarlyStopping(monitor='val_err', patience=5)

这里的monitor是表示需要监视的量,‘val_err’代表验证集错误.

Patience表示在该数量的epochs内没有进一步的性能改进后,就停止训练。为了更好地理解,我们再看看上面的图。在虚线之后,每个epoch都会导致一个更高的验证集错误。因此,在虚线之后的5个epoch(因为我们设置patience等于5),由于没有进一步的改善,模型将停止训练。

注意:可能在5个epoch之后(这是一般情况下为patience设定的值)模型再次开始改进,并且验证集错误也开始减少。因此,在调整这个超参数的时候要格外小心。

d47e62d2b349aca45e42305ed6714efbe5ed61d9在MNIST数据集上使用Keras的案例研究

至此,你应该对我们提到的各种技术有了一个理论上的理解。现在我们把这些知识用在深度学习实际问题上——识别数字。下载完数据集之后,你就可以开始下面的代码。首先,我们导入一些基本的库。

%pylab inline

import numpy as np

import pandas as pd

from scipy.misc import imread

from sklearn.metrics import accuracy_score

from matplotlib import pyplot

import tensorflow as tf

import keras

# To stop potential randomness

seed = 128

rng = np.random.RandomState(seed)

现在,加载数据。

423fac4703bd527a463cbc7afa22750e0637220b

现在拿一些图片来看看。

img_name = rng.choice(train.filename)

filepath = os.path.join(data_dir, 'Train', 'Images', 'train', img_name)

img = imread(filepath, flatten=True)

pylab.imshow(img, cmap='gray')

pylab.axis('off')

pylab.show()
0ae3b61fac96897f91d4dbc09a21487c7258971f

#storing images in numpy arrays

temp = []

for img_name in train.filename:

image_path = os.path.join(data_dir, 'Train', 'Images', 'train', img_name)

img = imread(image_path, flatten=True)

img = img.astype('float32')

temp.append(img)

x_train = np.stack(temp)

x_train /= 255.0

x_train = x_train.reshape(-1, 784).astype('float32')

y_train = keras.utils.np_utils.to_categorical(train.label.values)

创建一个验证集,以便优化我们的模型以获得更好的分数。我们将采用70:30的训练集、验证集比例。

split_size = int(x_train.shape[0]*0.7)

x_train, x_test = x_train[:split_size], x_train[split_size:]

y_train, y_test = y_train[:split_size], y_train[split_size:]

首先,我们先建立一个具有5个隐藏层,每层500个节点的简单神经网络。

# import keras modules

from keras.models import Sequential

from keras.layers import Dense

# define vars

input_num_units = 784

hidden1_num_units = 500

hidden2_num_units = 500

hidden3_num_units = 500

hidden4_num_units = 500

hidden5_num_units = 500

output_num_units = 10

epochs = 10

batch_size = 128

model = Sequential([

Dense(output_dim=hidden1_num_units, input_dim=input_num_units, activation='relu'),

Dense(output_dim=hidden2_num_units, input_dim=hidden1_num_units, activation='relu'),

Dense(output_dim=hidden3_num_units, input_dim=hidden2_num_units, activation='relu'),

Dense(output_dim=hidden4_num_units, input_dim=hidden3_num_units, activation='relu'),

Dense(output_dim=hidden5_num_units, input_dim=hidden4_num_units, activation='relu'),

Dense(output_dim=output_num_units, input_dim=hidden5_num_units, activation='softmax'),

])

注意,我们只运行10个epoch,快速检查一下模型的性能。

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

trained_model_5d = model.fit(x_train, y_train, nb_epoch=epochs, batch_size=batch_size, validation_data=(x_test, y_test))
7794eee1a9f708bbb64f4f7eab5fdbf6c0971b70




现在,我们尝试用L2正则化,并检查它是否给出了比简单神经网络更好的性能。

from keras import regularizers

model = Sequential([

Dense(output_dim=hidden1_num_units, input_dim=input_num_units, activation='relu',

kernel_regularizer=regularizers.l2(0.0001)),

Dense(output_dim=hidden2_num_units, input_dim=hidden1_num_units, activation='relu',

kernel_regularizer=regularizers.l2(0.0001)),

Dense(output_dim=hidden3_num_units, input_dim=hidden2_num_units, activation='relu',

kernel_regularizer=regularizers.l2(0.0001)),

Dense(output_dim=hidden4_num_units, input_dim=hidden3_num_units, activation='relu',

kernel_regularizer=regularizers.l2(0.0001)),

Dense(output_dim=hidden5_num_units, input_dim=hidden4_num_units, activation='relu',

kernel_regularizer=regularizers.l2(0.0001)),

Dense(output_dim=output_num_units, input_dim=hidden5_num_units, activation='softmax'),

])

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

trained_model_5d = model.fit(x_train, y_train, nb_epoch=epochs, batch_size=batch_size, validation_data=(x_test, y_test))
e1f20b336a1cefc3aa403e4a9f8505af2ce33239

注意这里lambda的值等于0.0001. 太棒了!我们获得了一个比之前NN模型更好的准确率。

现在尝试一下L1正则化。

## l1

model = Sequential([

Dense(output_dim=hidden1_num_units, input_dim=input_num_units, activation='relu',

kernel_regularizer=regularizers.l1(0.0001)),

Dense(output_dim=hidden2_num_units, input_dim=hidden1_num_units, activation='relu',

kernel_regularizer=regularizers.l1(0.0001)),

Dense(output_dim=hidden3_num_units, input_dim=hidden2_num_units, activation='relu',

kernel_regularizer=regularizers.l1(0.0001)),

Dense(output_dim=hidden4_num_units, input_dim=hidden3_num_units, activation='relu',

kernel_regularizer=regularizers.l1(0.0001)),

Dense(output_dim=hidden5_num_units, input_dim=hidden4_num_units, activation='relu',

kernel_regularizer=regularizers.l1(0.0001)),

Dense(output_dim=output_num_units, input_dim=hidden5_num_units, activation='softmax'),

])

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

trained_model_5d = model.fit(x_train, y_train, nb_epoch=epochs, batch_size=batch_size, validation_data=(x_test, y_test))

这次并没有显示出任何的改善。我们再来试试dropout技术。

## dropout

from keras.layers.core import Dropout

model = Sequential([

Dense(output_dim=hidden1_num_units, input_dim=input_num_units, activation='relu'),

Dropout(0.25),

Dense(output_dim=hidden2_num_units, input_dim=hidden1_num_units, activation='relu'),

Dropout(0.25),

Dense(output_dim=hidden3_num_units, input_dim=hidden2_num_units, activation='relu'),

Dropout(0.25),

Dense(output_dim=hidden4_num_units, input_dim=hidden3_num_units, activation='relu'),

Dropout(0.25),

Dense(output_dim=hidden5_num_units, input_dim=hidden4_num_units, activation='relu'),

Dropout(0.25),

Dense(output_dim=output_num_units, input_dim=hidden5_num_units, activation='softmax'),

])

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

trained_model_5d = model.fit(x_train, y_train, nb_epoch=epochs, batch_size=batch_size, validation_data=(x_test, y_test))

25904e6f79f3c9d274e1ca03d1156ce2c5185976

效果不错!dropout也在简单NN模型上给出了一些改善。

现在,我们试试数据增强。

from keras.preprocessing.image import ImageDataGenerator

datagen = ImageDataGenerator(zca_whitening=True)

# loading data

train = pd.read_csv(os.path.join(data_dir, 'Train', 'train.csv'))

temp = []

for img_name in train.filename:

image_path = os.path.join(data_dir, 'Train', 'Images', 'train', img_name)

img = imread(image_path, flatten=True)

img = img.astype('float32')

temp.append(img)

x_train = np.stack(temp)

X_train = x_train.reshape(x_train.shape[0], 1, 28, 28)

X_train = X_train.astype('float32')

现在,拟合训练数据以便增强。

# fit parameters from data

datagen.fit(X_train)

这里,我使用了zca_whitening作为实参,它突出了每个数字的轮廓,如下图所示:

626fb536565fbefd6a4a9ebe34a411a12c57e816

## splitting

y_train = keras.utils.np_utils.to_categorical(train.label.values)

split_size = int(x_train.shape[0]*0.7)

x_train, x_test = X_train[:split_size], X_train[split_size:]

y_train, y_test = y_train[:split_size], y_train[split_size:]

## reshaping

x_train=np.reshape(x_train,(x_train.shape[0],-1))/255

x_test=np.reshape(x_test,(x_test.shape[0],-1))/255

## structure using dropout

from keras.layers.core import Dropout

model = Sequential([

Dense(output_dim=hidden1_num_units, input_dim=input_num_units, activation='relu'),

Dropout(0.25),

Dense(output_dim=hidden2_num_units, input_dim=hidden1_num_units, activation='relu'),

Dropout(0.25),

Dense(output_dim=hidden3_num_units, input_dim=hidden2_num_units, activation='relu'),

Dropout(0.25),

Dense(output_dim=hidden4_num_units, input_dim=hidden3_num_units, activation='relu'),

Dropout(0.25),

Dense(output_dim=hidden5_num_units, input_dim=hidden4_num_units, activation='relu'),

Dropout(0.25),

Dense(output_dim=output_num_units, input_dim=hidden5_num_units, activation='softmax'),

])

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

trained_model_5d = model.fit(x_train, y_train, nb_epoch=epochs, batch_size=batch_size, validation_data=(x_test, y_test))
6ae770ae5b79c77734d6eb6d4c6cfe72c6e2f6a5

哇!我们在准确率得分上有了一个飞跃。并且好消息是它每次都奏效。我们只需要根据数据集中的图像来选择一个合适的实参。

现在,试一下最后一种技术——提前停止。

from keras.callbacks import EarlyStopping

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

trained_model_5d = model.fit(x_train, y_train, nb_epoch=epochs, batch_size=batch_size, validation_data=(x_test, y_test)

, callbacks = [EarlyStopping(monitor='val_acc', patience=2)])
457a4b87a7c89af1450c0ce2f95118ed5a4220c0

可以看到我们的模型在仅仅5轮迭代后就停止了,因为验证集准确率不再提高了。当我们使用更大值的epochs来运行它时,它会给出好的结果。你可以说它是一种优化epoch值的技术。

结语

我希望现在你已经理解了正则化以及在深度学习模型中实现正则化的不同技术。 无论你处理任何深度学习任务,我都强烈建议你使用正则化。它将帮助你开阔视野并更好的理解这个主题。


原文发布时间为:2018-05-24

本文作者:SHUBHAM JAIN

本文来自云栖社区合作伙伴“数据派THU”,了解相关信息可以关注“数据派THU”。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
【双11背后的技术】基于深度强化学习与自适应在线学习的搜索和推荐算法研究
作者:灵培、霹雳、哲予 1. 搜索算法研究与实践 1.1 背景 淘宝的搜索引擎涉及对上亿商品的毫秒级处理响应,而淘宝的用户不仅数量巨大,其行为特点以及对商品的偏好也具有丰富性和多样性。因此,要让搜索引擎对不同特点的用户作出针对性的排序,并以此带动搜索引导的成交提升,是一个极具挑战性的问题。传统
10205 0
Python编程语言学习:仅需一行代码构造特殊列表之重复元素列表、等差数字列表等之详细攻略
Python编程语言学习:仅需一行代码构造特殊列表之重复元素列表、等差数字列表等之详细攻略
7 0
阿里云机器学习技术分享1——图像识别之TensorFlow实现方法【视频+PPT】
阿里云AI之图像识别技术是如何实现的!?视频+PPT干货奉上 讲师简介:赵昆 阿里巴巴机器学习技术专家 欢迎加入阿里云机器学习大家庭,**钉钉群:11768691** , QQ群:567810612 一、阿里云机器学习之图像识别实践-基础篇: 观看视频:http://cloud.
3323 0
深度学习Trick——用权重约束减轻深层网络过拟合|附(Keras)实现代码
深度学习小技巧,约束权重以降低模型过拟合的可能,附keras实现代码。
1557 0
专访张银奎:要抓住技术发展趋势,只有不断学习和更新自己?
软件是通过指令的组合来指挥硬件,既简单又复杂,是个充满神秘与挑战的世界。而软件调试是帮助人们探索和征服这个神秘世界的有力工具。 今天,作者对软件调试的理解和认识,相比自己写本书第一版时,在广度和深度方面都有了较大的提升。
2020 0
+关注
技术小能手
云栖运营小编~
5951
文章
9
问答
来源圈子
更多
+ 订阅
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载