Fork me on GitHub

TF-char9-overfitting

TF-char9-overfitting

本章中主要讲解的是关于过拟合以及如何处理过拟合问题

$\color{red}{泛化能力}$:从训练集上学习到数据的真实模型,从而在未知的测试集上也能表现的良好的能力。

独立假设同分布:训练集和测试集都是采自同一个数据分布,且采的样本是相互独立地。

模型的容量

模型的容量或者说表达能力指的是模型拟合复杂函数的能力。一个评价指标是:模型的假设空间大小,即模型可以表示的函数集合的大小。

  • 假设空间越大越完备,从假设空间中搜索出逼近真实模型的函数的可能性也就越大。
  • 反之,若假设空间非常受限,很难找到逼近真实模型的函数。

过大的假设空间会增加搜索难度和计算代价,其中可能包含了大量表达能力过强的函数,能够将训练样本的观测误差也学习进来,从而伤害了模型的泛化能力。

过拟合与欠拟合

过拟合overfitting:当模型的容量过大,网络模型不仅在训练集上表现良好,同时也将额外的训练误差也学习到了,导致模型在未知的样本上表现不佳,也就是泛化能力偏弱。

欠拟合underfitting:模型在训练集和测试集上的表现都不是很好。

现代深度神经网络中过拟合现象非常容易出现,主要是因为神经网络的表达能力非常 强,很容易就出现了神经网络容量偏大的现象。下面是几种常见的$\color{red}{抑制过拟合}$的方法。

数据集划分

将数据集划分成训$\color{red}{练集、验证集和测试集}$。

训练集:用于训练模型参数

测试集:用于检验模型的泛化能力,测试集中样本不参与模型的训练,防止模型记住数据的特征,损害模型的泛化能力。

验证集和超参数

由于测试集的性能不能作为模型训练 的反馈,而我们需要在模型训练时能够挑选出较合适的模型超参数,判断模型是否过拟合等。

验证集:用于选择模型的超参数(模型选择),主要功能包含:

  1. 根据验证集的性能来调整学习率、权值衰减系数、训练次数等
  2. 根据验证集的性能来判断是过拟合还是欠拟合
  3. 根据验证集的性能来重新调整网络拓扑结构

三者常用的划分比例为6:2:2

提前停止

当观测到过拟合现象时,可以从新设计网络模型的容量,使得模型的容量降低,从而减轻或去除过拟合现象

  • 降低网络的层数
  • 降低网络的参数量
  • 添加假设空间的约束等

当观测到欠拟合现象时,可以尝试增大网络的容量

  • 加深网络的层数
  • 增加网络的参数量
  • 尝试更复杂的网络结构

当发现验证准确率连续多个Epoch没有下降时,可以预测已经达到了最适合的epoch(对训练集的所有样本循环迭代一次叫做一个 Epoch)的附近,从而提前停止训练

模型设计

网络的层数和采纳数量是网络容量的重要指标。如果是过拟合,需要减少网络的层数;如果是发现模型欠拟合,需要增大网络的容量。

正则化

模型的实际容量是可以四核网络参数的优化更新而产生变化的。通过限制网络的参数的稀疏性,可以啦约束网络的是实际容量

这种约束的实现一般是通过在损失函数上加上额外的参数稀疏性惩罚项

未加约束项
$$
\text { Minimize } \mathcal{L}\left(f_{\theta}(\boldsymbol{x}), y\right),(\boldsymbol{x}, y) \in \mathbb{D}^{\text {train }}
$$
加上约束项
$$
\text { Minimize } \mathcal{L}\left(f_{\theta}(x), y\right)+\lambda * \Omega(\theta),(x, y) \in \mathbb{D}^ \text { train }
$$
其中$\Omega$ 表示的是网络参数$\theta$的稀疏性约束函数,通过$L$范数来实现
$$
\Omega(\theta)=\sum_{\theta_{i}}\left|\theta_{i}\right|_{l}
$$
新的优化目标除了最小化原来的损失函数外,还需要约束网络参数$\theta$ 的稀疏性。权重关系通过超参数$\lambda$ 来平衡

  • 较大:网络的稀疏性更重要
  • 较小:网络的训练误差更重要

三种正则化方式

  1. $L_0$正则化:指采用 $L_0$范数作为稀疏性惩罚项$\Omega(\theta)$的正则化方式

$$
\Omega(\theta)=\sum_{\theta_{i}}\left|\theta_{i}\right|_{0}
$$

该范数表示的非零元素的个数。

  1. 采用 $L_1$范数作为稀疏性惩罚项$\Omega(\theta)$的正则化方式,称之为$L_1$正则化

$$
\Omega(\theta)=\sum_{\theta_{i}}\left|\theta_{i}\right|_{1}
$$

该范数表示的是所有元素的绝对值之和。

1
2
3
4
w1 = tf.random.normal([4, 2])
w2 = tf.random.normal([4,3])

loss_reg = tf.reduce_sum(tf.match.abs(w1)) + tf.reduce_sum(tf.match.abs(w2)) # 绝对值之和
  1. 采用 $L_2$范数作为稀疏性惩罚项$\Omega(\theta)$的正则化方式,称之为$L_2$正则化

$$
\Omega(\theta)=\sum_{\theta_{i}}\left|\theta_{i}\right|_{2}
$$

1
loss_reg = tf.reduce_sum(tf.square(w1)) + tf.reduce_sum(tf.square(w2))  # 平方之和

正则化系数$\lambda$的取值一般是:0.000001,0.001,0.003,0.1,0.3,1,3

先尝试较小的正则化系数,观察是否出现过拟合现象;再逐渐增大网络参数稀疏性,提高泛化能力。过大的正则化系数可能导致网络不收敛,需要具体调节。

Dropout

Dropout通过随机断开神经网络的连接,较少每次训练时收集参与计算的模型的参数;在测试的时候,Dropout会恢复所有的连接,保证模型获得最好的性能。

1
2
3
x = tf.nn.dropout(x, rate=0.5)
# 将Dropout当做一个层使用
model.add(layers.Dropout(rate=0.5))

img

数据增强Data Aumentation

增加数据集合大小是解决过拟合最重要的途径之一。通过数据增强加护可以增加训练的样本数量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. 缩放
def preprocess(x, y):
x = tf.ii.read_file(x) # x 为图片路径
x = tf.image.decode_jpeg(x, channels=3)
x = tf.image.resize(x, [244,244]) # 进行图片的缩放

# 2. 翻转
x = tf.image.random_flip_left_right(x) # 水平翻转
x = tf.image.random_flip_up_down(x) # 垂直翻转

# 3. 旋转
x = tf.image.rot90(x, 2) # 旋转2个90度

# 4. 裁剪
x = tf.image.resize(x, [244,244]) # 先缩放
x = tf.image.random_crop(x, [244,244,3]) # 再随机裁剪

实战

构建数据集

采样1000个样本数据,同时添加标准差为0.25的高斯噪声数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 导入数据集生成工具
from sklearn.datasets import make_moons
# 从 moon 分布中随机采样 1000 个点,并切分为训练集-测试集
X, y = make_moons(n_samples = N_SAMPLES, noise=0.25, random_state=100) X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size = TEST_SIZE, random_state=42)

# make_plot函数绘制出样本X和标签y的分布图
def make_plot(X, y, plot_name, file_name, XX=None, YY=None, preds=None):
plt.figure()
# sns.set_style("whitegrid")
axes = plt.gca()
axes.set_xlim([x_min,x_max])
axes.set_ylim([y_min,y_max])
axes.set(xlabel="$x_1$", ylabel="$x_2$")
# 根据网络输出绘制预测曲面
if(XX is not None and YY is not None and preds is not None):
plt.contourf(XX, YY, preds.reshape(XX.shape), 25, alpha = 0.08,
cmap=cm.Spectral)
plt.contour(XX, YY, preds.reshape(XX.shape), levels=[.5],cmap="Greys",vmin=0, vmax=.6)

# 绘制正负样本
markers = ['o' if i == 1 else 's' for i in y.ravel()]
mscatter(X[:, 0], X[:, 1], c=y.ravel(), s=20,cmap=plt.cm.Spectral, edgecolors='none', m=markers)

# 保存矢量图
plt.savefig(OUTPUT_DIR+"/"+file_name)

# 绘制数据集分布
make_plot(X, y, None, "dataset.svg")

img

网络层数的影响

进行5次训练,构建网络层数n+2层的全连接层;通过Adam优化器训练500Epoch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for i in range(5):
model = Sequential() # 创建容器
model.add(Dense(8, input_dim=2, activation='relu')) # 创建第一层
for _ in range(n): # 添加n层,总共n+2层
model.add(Dense(32, activation='relu'))
model.add(Dense(1, activation='sigmoid')) # 最后1层

model.compile(loss='binary_crossentropy', optimizer='addm',metrics=['accuracy']) # 模型装载与训练
history = model.fit(X_train, y_train, epochs=N_EPOCHS, verbose=1)

# 绘制不同的层数的网络决策边界曲线
preds = model.predict_classes(np.c_[XX.ravel(), YY.ravel()])
title = "网络层数({})".format(n)
file = "网络容量%f.png"%(2+n*1)
make_plot(X_train, y_train, title, file, XX, YY, preds)

Dropout影响

  • 进行5次试验,每次试验使用7层的全连接网络进行训练
  • 插入了0-4Dropout
  • 通过Adam优化器训练 500Epoch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for n in range(5):  # 构建5种不同数量的Dropout层的网络
model = Sequential() # 构建容器
model.add(Dense(8, input_dim=2, activation='relu')) # 创建第一层
count = 0
for _ in range(5): # 固定层数为5
model.add(Dense(64, activation='relu'))
if counter < n: # 添加n个的dropout层
counter += 1
model.add(layers.Dropout(rate=0.5))

model.add(Dense(1, activation='sigmoid')) # 输出层
model.compile(loss='binary_crossentropy', optimizer="adam", metrics=['accuracy']) # 模型装配与训练
history = model.fit(X_train, y_train, epochs=N_EPOCHS, verbose=1)
preds = model.predict_classes(np.c_[XX.ravel(), YY.ravel()])
title = "Dropout({})".format(n)
file = "Dropout%f.png"%(n)
make_plot(X_train, y_train, title, file,XX, YY, preds)

正则化影响

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def build_model_regularization(_lambda):
# 创建带有正则项的神经网络
model = Sequential()
model.add(Dense(8, input_dim=2, activation='relu'))
model.add(Dense(256, activation='relu', kernel_regularizer=regularizers.12(_lamnbda)))
model.add(Dense(256, activation='relu', kernel_regularizer=regularizers.12(_lamnbda)))
model.add(Dense(256, activation='relu', kernel_regularizer=regularizers.12(_lamnbda)))

model.add(Dense(1, activation='sigmoid')) # 输出层
model.compile(loss='binary_crossentropy', optimizer="adam", metrics="[accuracy]") # 模型装配
return model

# 保持网络结构不变,通过调整正则系数来测试网络的训练效果
for _lambda in [1e-5, 1e-3, 1e-1, 0.12, 0.13]: # 设置不同的正则化系数
model = build_model_regularization(_lambda) # 创建带有正则化系数的模型
history = model.fit(X_train, y_train, epochs=N_EPOCHS, verbose=1) # 模型训练
layer_index = 2
plot_title = "正则化-[lambda = {}]".format(str(_lambda))
file_name = "正则化_" + str(_lambda)
# 绘制网络权值范围图
plot_weights_matrix(model, layer_index, plot_title, file_name)
# 绘制不同正则化系数的决策边界曲线
preds = model.predict_classes(np.c_[XX.ravel(), YY.ravel()])
title = "正则化{}".format(_lambda)
file = "正则化%f.svg" %_lambda
make_plot(X_train, y_train, title, file,XX, YY, preds)

本文标题:TF-char9-overfitting

发布时间:2020年05月10日 - 22:05

原始链接:http://www.renpeter.cn/2020/05/10/TF-char9-overfitting.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Coffee or Tea