机器学习:过拟合、神经网络Dropout

过拟合


过拟合现象

机器学习中,过拟合现象就是训练模型高度适用于训练集,而对测试集或未知数据集效果不好的情况。表现为训练集过度拟合具有高准确率,而测试集的准确率明显低于测试集。


防止过拟合

防止过拟合的方法有:增加数据集,正则化方法以及Dropout方法。

1. 增加数据集

  • 数据挖掘中,数据量越多,对模型参数调整就越准确。多的数据往往比好的训练模型要重要,因此,增加数据集,能明显的提高准确率、模型的可靠性以及防止过拟合。

2. 正则化方法

  • 在代价函数中加入一个正则项,例如:
    C = C 0 + λ 2 n w w 2

    其中, C 表示新的代价函数, C 0 表示原来的代价函数,比如二次代价函数; λ 表示可调参数用来调节正则项的重要性, n 表示数据集大小; w 表示权值。
  • 在进行权值更新时,多考虑一个正则项。即要使得代价函数变小,则要求正则项变小。从而使得接近于0的权值变得几乎等于0,继而削弱某些神经元的影响,减小了网络的复杂程度。

3. Dropout

训练的过程中,使得部分神经元工作,部分神经元不工作。而在测试的时候,激活所有的神经元。可以尝试用下图来理解:

这里写图片描述


Dropout实例(在Tensorflow框架下实现神经网络)

以UCI的鸢尾花数据集Iris Data Set为例,在tensorflow中创建一个神经网络对iris.data进行分类。并使用dropout防止过拟合。

1.没使用Dropout之前

  • python代码
import tensorflow as tf

def min_max(data, new_min=0.0, new_max=1.0):
    """定义最小-最大规范化函数"""
    min_max_data = []
    for value in data:
        value = (value-min(data)) / (max(data)-min(data)) * (new_max - new_min) + new_min  # 最小最大规范化公式
        value = round(value, 2)   # 保留两位小数
        min_max_data.append(value)
    return min_max_data


def data_pre(filename):
    """数据预处理"""
    with open(filename,'r') as f:
        content = f.read()
    iris_list = content.strip().split('\n')
    label_data = []

    labels = ['Iris-setosa', 'Iris-versicolor', 'Iris-virginica']   # 三种鸢尾花标签

    # 鸢尾花4个属性值列表
    sepal_length = []
    sepal_width = []
    petal_length = []
    petal_width = []

    for iris in iris_list:
        iris_data = iris.split(',')
        sepal_length.append(float(iris_data[0]))
        sepal_width.append(float(iris_data[1]))
        petal_length.append(float(iris_data[2]))
        petal_width.append(float(iris_data[2]))

        tmp_list = [0,0,0]   # 表示类别的小列表
        if iris_data[-1] in labels:
            index = labels.index(iris_data[-1])
            tmp_list[index] = 1
        label_data.append([tmp_list])

    # 对4个属性进行规范化
    norm_sepal_length = min_max(sepal_length)
    norm_sepal_width = min_max(sepal_width)
    norm_petal_length = min_max(petal_length)
    norm_petal_width = min_max(petal_width)

    attribute_data = []
    for i in range(len(norm_petal_length)):
        a_list = [[norm_sepal_length[i], norm_sepal_width[i], norm_petal_length[i], norm_petal_width[i]]]
        attribute_data.append(a_list)

    return attribute_data, label_data

def run_tensorflow_nn(attribute_data, label_data):
    """使用tensorflow训练神经网络"""
    # 定义两个placeholder分别储存输入值向量x和输出值向量y
    x = tf.placeholder(tf.float32,[1,4])
    y = tf.placeholder(tf.float32,[1,3])

    # 创建神经网络,包含两个隐含层,输入层有4个神经元对应4个属性值,输出层为3个神经元对应3个类别值

    # 初始化第一层权值和偏置值
    W1 = tf.Variable(tf.truncated_normal([4, 200], stddev=0.1))  # 用截断的正态分布,标准差为0.1
    b1 = tf.Variable(tf.zeros([200])+0.1)
    # 第一层输出
    L1 = tf.nn.tanh(tf.matmul(x,W1) + b1)

    # 初始化第二层权值和偏置值
    W2 = tf.Variable(tf.truncated_normal([200, 80], stddev=0.1))
    b2 = tf.Variable(tf.zeros([80])+0.1)
    # 第二层输出
    L2 = tf.nn.tanh(tf.matmul(L1,W2) + b2)

    # 初始化输出层权值和偏置值
    W3 = tf.Variable(tf.truncated_normal([80,3], stddev=0.1))
    b3 = tf.Variable(tf.zeros([3])+0.1)
    # 输出层输出
    output = tf.nn.softmax(tf.matmul(L2, W3) + b3)  # 多分类问题输出层神经元激活函数用softmax()函数

    # 定义交叉熵代价函数
    loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=output))

    # 使用梯度下降法
    train_step = tf.train.GradientDescentOptimizer(0.2).minimize(loss)

    # 定义判断结果变量,储存在布尔型列表中, tf.argmax() 返回列表的最大值所在的索引值
    correct_output = tf.equal(tf.argmax(y,1), tf.argmax(output,1))  # tf.equal()比较里面两个参数是否相等,返回True和False

    # 求准确计数
    accuracy = tf.cast(correct_output, tf.float32)   # tf.cast()把布尔型转换为32位浮点型

    # 进行训练
    with tf.Session() as sess:
        # 初始化变量
        init = tf.global_variables_initializer()
        sess.run(init)
        for epoch in  range(5): # 循环5个周期
            for i in range(len(attribute_data[:100])):
                train_x = attribute_data[i]
                train_y = label_data[i]
                sess.run(train_step, feed_dict={x:train_x, y:train_y})

            # 计算训练集准确率
            train_count = []
            for i in range(len(attribute_data[:100])):
                train_x = attribute_data[i]
                train_y = label_data[i]
                train_count.append(sess.run(accuracy, feed_dict={x:train_x, y:train_y})[0])

            train_acc = sum(train_count) / len(train_count)
            # print(train_acc)

            # 计算测试集准确率
            test_count = []
            for i in range(len(attribute_data[100:])):
                test_x = attribute_data[i]
                test_y = label_data[i]
                # sess.run(train_step, feed_dict={x: test_x, y: test_y})
                test_count.append(sess.run(accuracy, feed_dict={x: test_x, y: test_y})[0])
            test_acc = sum(test_count) / len(test_count)
            print("第" + str(epoch+1) + "个训练周期训练集的准确率为:{}%".format(train_acc*100)+", ","测试集的准确率为:{}%".format(test_acc*100),'\n')


if __name__ == "__main__":

    filename = 'iris.data'
    attribute_data, label_data = data_pre(filename)
    run_tensorflow_nn(attribute_data, label_data)
  • 运行结果
1个训练周期训练集的准确率为:50.0%,  测试集的准确率为:0.0%2个训练周期训练集的准确率为:50.0%,  测试集的准确率为:0.0%3个训练周期训练集的准确率为:91.0%,  测试集的准确率为:82.0%4个训练周期训练集的准确率为:100.0%,  测试集的准确率为:100.0%5个训练周期训练集的准确率为:100.0%,  测试集的准确率为:100.0% 

2. 使用Dropout之后

  • 代码
import tensorflow as tf

def min_max(data, new_min=0.0, new_max=1.0):
    """定义最小-最大规范化函数"""
    min_max_data = []
    for value in data:
        value = (value-min(data)) / (max(data)-min(data)) * (new_max - new_min) + new_min  # 最小最大规范化公式
        value = round(value, 2)   # 保留两位小数
        min_max_data.append(value)
    return min_max_data


def data_pre(filename):
    """数据预处理"""
    with open(filename,'r') as f:
        content = f.read()
    iris_list = content.strip().split('\n')
    label_data = []

    labels = ['Iris-setosa', 'Iris-versicolor', 'Iris-virginica']   # 三种鸢尾花标签

    # 鸢尾花4个属性值列表
    sepal_length = []
    sepal_width = []
    petal_length = []
    petal_width = []

    for iris in iris_list:
        iris_data = iris.split(',')
        sepal_length.append(float(iris_data[0]))
        sepal_width.append(float(iris_data[1]))
        petal_length.append(float(iris_data[2]))
        petal_width.append(float(iris_data[2]))

        tmp_list = [0,0,0]   # 表示类别的小列表
        if iris_data[-1] in labels:
            index = labels.index(iris_data[-1])
            tmp_list[index] = 1
        label_data.append([tmp_list])

    # 对4个属性进行规范化
    norm_sepal_length = min_max(sepal_length)
    norm_sepal_width = min_max(sepal_width)
    norm_petal_length = min_max(petal_length)
    norm_petal_width = min_max(petal_width)

    attribute_data = []
    for i in range(len(norm_petal_length)):
        a_list = [[norm_sepal_length[i], norm_sepal_width[i], norm_petal_length[i], norm_petal_width[i]]]
        attribute_data.append(a_list)

    return attribute_data, label_data

def run_tensorflow_nn(attribute_data, label_data):
    """使用tensorflow训练神经网络"""
    # 定义两个placeholder分别储存输入值向量x和输出值向量y
    x = tf.placeholder(tf.float32,[1,4])
    y = tf.placeholder(tf.float32,[1,3])

    # 创建神经网络,包含两个隐含层,输入层有4个神经元对应4个属性值,输出层为3个神经元对应3个类别值

    # 初始化第一层权值和偏置值
    W1 = tf.Variable(tf.truncated_normal([4, 200], stddev=0.1))  # 用截断的正态分布,标准差为0.1
    b1 = tf.Variable(tf.zeros([200])+0.1)
    # 第一层输出
    L1 = tf.nn.tanh(tf.matmul(x,W1) + b1)

    # 初始化第二层权值和偏置值
    W2 = tf.Variable(tf.truncated_normal([200, 80], stddev=0.1))
    b2 = tf.Variable(tf.zeros([80])+0.1)
    # 第二层输出
    L2 = tf.nn.tanh(tf.matmul(L1,W2) + b2)

    # 初始化输出层权值和偏置值
    W3 = tf.Variable(tf.truncated_normal([80,3], stddev=0.1))
    b3 = tf.Variable(tf.zeros([3])+0.1)
    # 输出层输出
    output = tf.nn.softmax(tf.matmul(L2, W3) + b3)  # 多分类问题输出层神经元激活函数用softmax()函数

    # 定义交叉熵代价函数
    loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=output))

    # 使用梯度下降法
    train_step = tf.train.GradientDescentOptimizer(0.2).minimize(loss)

    # 定义判断结果变量,储存在布尔型列表中, tf.argmax() 返回列表的最大值所在的索引值
    correct_output = tf.equal(tf.argmax(y,1), tf.argmax(output,1))  # tf.equal()比较里面两个参数是否相等,返回True和False

    # 求准确计数
    accuracy = tf.cast(correct_output, tf.float32)   # tf.cast()把布尔型转换为32位浮点型

    # 进行训练
    with tf.Session() as sess:
        # 初始化变量
        init = tf.global_variables_initializer()
        sess.run(init)

        for epoch in  range(5): # 循环5个周期
            for i in range(len(attribute_data[:100])):
                train_x = attribute_data[i]
                train_y = label_data[i]
                sess.run(train_step, feed_dict={x:train_x, y:train_y})

            # 计算训练集准确率
            train_count = []
            for i in range(len(attribute_data[:100])):
                train_x = attribute_data[i]
                train_y = label_data[i]
                train_count.append(sess.run(accuracy, feed_dict={x:train_x, y:train_y})[0])

            train_acc = sum(train_count) / len(train_count)
            # print(train_acc)

            # 计算测试集准确率
            test_count = []
            for i in range(len(attribute_data[100:])):
                test_x = attribute_data[i]
                test_y = label_data[i]
                # sess.run(train_step, feed_dict={x: test_x, y: test_y})
                test_count.append(sess.run(accuracy, feed_dict={x: test_x, y: test_y})[0])
            test_acc = sum(test_count) / len(test_count)
            print("第" + str(epoch+1) + "个训练周期训练集的准确率为:{}%".format(train_acc*100)+", ","测试集的准确率为:{}%".format(test_acc*100),'\n')


if __name__ == "__main__":

    filename = 'iris.data'
    attribute_data, label_data = data_pre(filename)
    run_tensorflow_nn(attribute_data, label_data)
  • 运行结果
1个训练周期训练集的准确率为:50.0%,  测试集的准确率为:0.0%2个训练周期训练集的准确率为:51.0%,  测试集的准确率为:2.0%3个训练周期训练集的准确率为:98.0%,  测试集的准确率为:96.0%4个训练周期训练集的准确率为:100.0%,  测试集的准确率为:100.0%5个训练周期训练集的准确率为:100.0%,  测试集的准确率为:100.0% 
  • 总结:

使用Dropout相比于之前,在前三个周期测试集的表现要稍微好点。但也不是太明显-.-,就当为了实现Dropout一个例子吧。在第4个周期以后,使用dropout与否,训练集和测试集的准确率都能达到100%。可能由于iris.data数据集比较好(噪声和离群点少),神经网络模型能达到最优分类的效果。
另外,本例代码还能够优化,Tensorflow水很深,继续学习吧。。


参考资料

讲师Ben: 炼数成金课程——深度学习框架Tensorflow学习与应用
UCI Machine Learning Repository : Iris Data Set

猜你喜欢

转载自blog.csdn.net/weixin_40170902/article/details/80092553