过拟合(Overfitting)、欠拟合(Underfitting)及常用解决方法

版权声明:站在巨人的肩膀上学习。 https://blog.csdn.net/zgcr654321/article/details/82958289

上图中左边为欠拟合状态,右边为过拟合状态,中间为很好地拟合的状态。

过拟合(over-fitting):

建立的机器学习模型或深度学习模型在训练数据集中表现过于良好,但在测试数据集中表现不佳。这意味着训练数据中的噪音或者随机波动也被当做概念被模型学习了。而问题就在于这些概念不适用于新的数据,从而导致模型泛化性能的变差。

过拟合的原因:

训练集和测试集数据分布不一致,如训练集数据都在比较小的区间,而在大区间中某个范围内的数据较少,而测试集的数据在这个训练数据较小的区间内数据有很多;

模型过于复杂而样本量不足,如本来一阶线性回归可以很好的拟合,为了减小loss的值,采用更高阶的线性函数,结果在测试数据中反而效果不佳。

解决过拟合的方法:

1、增大数据的训练量;

2、更换模型,降低模型复杂度,减少模型参数的数量。

3、采用正则化方法。正则化方法包括L0正则、L1正则、L2正则等。在机器学习中一般用L2正则

监督机器学习问题的核心就是在规则化参数的同时最小化误差。最小化误差是为了让我们的模型拟合我们的训练数据,而规则化参数是防止我们的模型过分拟合我们的训练数据。

参数太多,会导致我们的模型复杂度上升,容易过拟合,也就是我们的训练误差会很小,但测试数据误差不一定变小。只有保证模型“简单”的基础上最小化训练误差,这样得到的参数才具有好的泛化性能(也就是测试误差也小),而模型的“简单”就是通过规则化来实现。

规则化符合奥卡姆剃刀(Occam's razor)原理。它的思想是:在所有可能选择的模型中,我们应该选择能够很好地解释已知数据并且十分简单的模型。从贝叶斯估计的角度来看,规则化项对应于模型的先验概率。换句话说,规则化是结构风险最小化策略的实现,是在经验风险上加一个正则化项(regularizer)或惩罚项(penalty term)。

范数,是具有“长度”概念的函数。在线性代数及相关的数学领域,范数是一个函数,是矢量空间内的所有矢量赋予非零的正长度或大小。

L0范数是模型参数中非零参数的个数。如果我们用L0范数来规则化一个参数矩阵W的话,就是希望W的大部分元素都是0。即让参数W是稀疏的。

L1范数表示各个参数绝对值之和。也叫“稀疏规则算子”(Lasso regularization)。L1正则化之所以可以防止过拟合,是因为L1范数就是各个参数的绝对值相加得到的,而参数值大小和模型复杂度成正比,因此复杂的模型,其L1范数就大,最终导致损失函数就大,说明这个模型就不够好。

L2范数是指向量各元素的平方和然后求平方根。也叫“岭回归”(Ridge Regression)。我们让L2范数的正则项最小,可以使W的每个元素都很小,都接近于0。越小的参数说明模型越简单,越简单的模型越不容易产生过拟合现象,因此L2范数可以更好地防止过拟合。

4、神经网络中常常采用dropout方法。dropout方法是ImageNet中提出的一种方法,通俗一点讲就是dropout方法在训练的时候让神经元以一定的概率不工作。

下面举一个使用dropout方法解决过拟合的例子:

例子中要使用sklearn 数据库当中的数据,首先安装一下Scikit-learn (sklearn) 模块。

安装命令:python -m pip install scikit-learn

import tensorflow as tf
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelBinarizer
import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
os.environ["CUDA_VISIBLE_DEVICES"] = "0"

digits = load_digits()
# input中的数据采用load_digits数据集中的数据
X = digits.data
# 这是0到9的数字的图片的data
y = digits.target
y = LabelBinarizer().fit_transform(y)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.3)


# X和y分成training data和testing data

def add_layer(inputs, in_size, out_size, layer_name, activation_function=None):
	Weights = tf.Variable(tf.random_normal([in_size, out_size]))
	biases = tf.Variable(tf.zeros([1, out_size]) + 0.1, )
	Wx_plus_b = tf.matmul(inputs, Weights) + biases
	# 使用dropout方法来处理
	Wx_plus_b = tf.nn.dropout(Wx_plus_b, keep_prob)
	if activation_function is None:
		outputs = Wx_plus_b
	# activation_function is None时没有激励函数,是线性关系
	else:
		outputs = activation_function(Wx_plus_b, )
	# activation_function不为None时,得到的Wx_plus_b再传入activation_function再处理一下
	tf.summary.histogram(layer_name + '/outputs', outputs)
	return outputs


keep_prob = tf.placeholder(tf.float32)
# keep_prob是保留概率,即我们要保留的结果所占比例,它作为一个placeholder,在run时传入
# 当keep_prob=1的时候,相当于100%保留,也就是dropout没有起作用。
xs = tf.placeholder(tf.float32, [None, 64])
# None表示不规定样本的数量,64是因为digitsdata中的x都是64个单位8X8
ys = tf.placeholder(tf.float32, [None, 10])
# 每张图片表示一个数字,我们的输出是数字0到9,所以是10个输出

l1 = add_layer(xs, 64, 50, 'l1', activation_function=tf.nn.tanh)
# 建立隐藏层
prediction = add_layer(l1, 50, 10, 'l2', activation_function=tf.nn.softmax)
# 建立输出层

# 搭建分类模型时,loss函数(即最优化目标函数)选用交叉熵函数(cross_entropy)
# 交叉熵用来衡量预测值和真实值的相似程度,如果完全相同,它们的交叉熵等于零。
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), reduction_indices=[1]))
tf.summary.scalar('loss', cross_entropy)
# 定义训练函数,使用梯度下降法训练,0.6是学习效率,通常小于1,minimize(cross_entropy)指要将cross_entropy减小
train_step = tf.train.GradientDescentOptimizer(0.6).minimize(cross_entropy)
# 创建会话,并开始将网络初始化
sess = tf.Session()
merged = tf.summary.merge_all()
# 记录train和test两种data的summary
train_writer = tf.summary.FileWriter("E://TensorBoard//test//traindata", sess.graph)
test_writer = tf.summary.FileWriter("E://TensorBoard//test//testdata", sess.graph)
sess.run(tf.global_variables_initializer())

for i in range(1000):
	sess.run(train_step, feed_dict={xs: X_train, ys: y_train, keep_prob: 0.5})
	if i % 20 == 0:
		train_result = sess.run(merged, feed_dict={xs: X_train, ys: y_train, keep_prob: 1})
		test_result = sess.run(merged, feed_dict={xs: X_test, ys: y_test, keep_prob: 1})
		train_writer.add_summary(train_result, i)
		test_writer.add_summary(test_result, i)
	# 将两个result的数据加载到summary中

为了与过拟合下的情况作对比,我们先修改其中两句:

Wx_plus_b = tf.nn.dropout(Wx_plus_b, keep_prob) #此句先注释掉
sess.run(train_step, feed_dict={xs: X_train, ys: y_train, keep_prob: 0.5})#此句keep_prob先设为1,也就是不dropout

运行结果如下:

可以看到此时testdata和traindata的loss值差别较大,出现过拟合状态。

我们再将上面两句注释改为初始状态,重新运行。

运行结果如下:

此时testdata和traindata的loss值就很接近了,说明基本解决了过拟合问题。

欠拟合(under-fitting):

欠拟合指的是模型在训练和预测时表现都不好的情况。一个欠拟合的机器学习模型不是一个良好的模型

解决欠拟合的方法:

采用更多训练数据继续学习并且尝试更换机器学习算法。

猜你喜欢

转载自blog.csdn.net/zgcr654321/article/details/82958289
今日推荐