zoukankan      html  css  js  c++  java
  • 《Python深度学习》第三章阅读笔记

    第3章 神经网络入门

    层是神经网络的基本数据结构

    不同的张量格式与不同的数据处理类型需要用到不同的层。例如,简单的向量数据保存在形状为 (samples, features) 的 2D 张量中,通常用密集连接层[densely connected layer,也叫全连接层(fully connected layer)或密集层(dense layer),对应于 Keras 的 Dense 类]来处理。序列数据保存在形状为 (samples, timesteps, features) 的 3D 张量中,通常用循环层(recurrent layer,比如 Keras 的 LSTM 层)来处理。图像数据保存在 4D 张量中,通常用二维卷积层(Keras 的 Conv2D )来处理。

    模型

    模型是层构成的网络

    选定了网络拓扑结构,意味着将可能性空间(假设空间)限定为一系列特定的张量运算,将输入数据映射为输出数据。然后,你需要为这些张量运算的权重张量找到一组合适的值。

    损失函数与优化器

    具有多个输出的神经网络可能具有多个损失函数(每个输出对应一个损失函数)。但是,梯度下降过程必须基于单个标量损失值。因此,对于具有多个损失函数的网络,需要将所有损失函数取平均,变为一个标量值。

    对于二分类问题,你可以使用二元交叉熵(binary crossentropy)损失函数;对于多分类问题,可以用分类交叉熵(categorical crossentropy)损失函数;对于回归
    问题,可以用均方误差(mean-squared error)损失函数;对于序列学习问题,可以用联结主义时序分类(CTC,connectionist temporal classification)损失函数

    实例1:电影评论二分类问题

    本节使用 IMDB 数据集,它包含来自互联网电影数据库(IMDB)的 50 000 条严重两极分化的评论。数据集被分为用于训练的 25 000 条评论与用于测试的 25 000 条评论,训练集和测试集都包含 50% 的正面评论和 50% 的负面评论。

    IMDB 数据集内置于 Keras 库。它已经过预处理:评论(单词序列)已经被转换为整数序列,其中每个整数代表字典中的某个单词。

    输入数据是向量,而标签是标量(1 和 0),换句话说这是个二分类问题, 带 有 relu 激活的全连接层( Dense ) 的简单堆叠网络在这一问题上表现的很好

    一个Dense层的实例可能是这个样子的:Dense(16, activation='relu') ,其中传入 Dense 层的参数(16)是该层隐藏单元的个数。一个隐藏单元(hidden unit)是该层表示空间的一个维度。可以将表示空间的维度直观地理解为“网络学习内部表示时所拥有的自由度”。隐藏单元越多(即更高维的表示空间),网络越能够学到更加复杂的表示,但网络的计算代价也变得更大,而且可能会导致学到不好的模式(这种模式会提高训练数据上的性能,但不会提高测试数据上的性能)。

    对于这种 Dense 层的堆叠,需要确定以下两个关键架构:网络有多少层; 每层有多少个隐藏单元。

    在这个例子中,我们选择使用两个中间层,每层都有 16 个隐藏单元;第三层输出一个标量,预测当前评论的情感

    中间层使用 relu 作为激活函数,最后一层使用 sigmoid 激活以输出一个 0~1 范围内的概率值(表示样本的目标值等于 1 的可能性,即评论为正面的可能性)。

    relu (rectified linear unit, 整流线性单元)函数将所有负值归零,而 sigmoid 函数则将任意值“压缩”到 [0,1] 区间内,其输出值可以看作概率值。

    需要注意,如果没有 relu 等激活函数(也叫非线性), Dense 层将只包含两个线性运算——点积和加法,这是由于只有output = dot(W, input) + b这种运算,这样 Dense 层就只能学习输入数据的线性变换(仿射变换):该层的假设空间是从输入数据到 16 位空间所有可能的线性变换集合。这种假设空间非常有限,无法利用多个表示层的优势,因为多个线性层堆叠实现的仍是线性运算,添加层数并不会扩展假设空间。为了得到更丰富的假设空间,从而充分利用多层表示的优势,你需要添加非线性或激活函数。 relu 是深度学习中最常用的激活函数,但还有许多其他函数可选,它们都有类似的奇怪名称,比如 prelu 、 elu 等。

    我们还需要选择损失函数和优化器。由于面对的是一个二分类问题,网络输出是一个概率值(网络最后一层使用 sigmoid 激活函数,仅包含一个单元),那么最好使用 binary_crossentropy (二元交叉熵)损失。这并不是唯一可行的选择,比如你还可以使用 mean_squared_error (均方误差)。但对于输出概率值的模型,交叉熵(crossentropy)往往是最好的选择。交叉熵是来自于信息论领域的概念,用于衡量概率分布之间的距离,在这个例子中就是真实分布与预测值之间的距离。

    该流程的代码如下(顺带一提,这个Matplotlib和Matlab的画图可真像,不管是语法还是画出来的图的模样,都很像,绝对是亲兄弟hhh):

    from keras.datasets import imdb
    from keras import models
    from keras import layers
    import numpy as np
    import matplotlib.pyplot as plt
    
    # 对向量进行one-hot编码
    def vectorize_sequences(sequences, dimension=10000):
        results = np.zeros((len(sequences), dimension))
        for i, sequence in enumerate(sequences):
            results[i, sequence] = 1.
        return results
    
    (train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
    x_train = vectorize_sequences(train_data)                #将训练数据向量化
    x_test = vectorize_sequences(test_data)                    #将测试数据向量化
    y_train = np.asarray(train_labels).astype('float32')    #将标签向量化
    y_test = np.asarray(test_labels).astype('float32')        #将标签向量化
    
    #向网络中添加层
    model = models.Sequential()
    model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
    model.add(layers.Dense(16, activation='relu'))
    model.add(layers.Dense(1, activation='sigmoid'))
    
    # 将原始训练数据留出10000个样本作为验证集
    x_val = x_train[:10000]
    partial_x_train = x_train[10000:]
    y_val = y_train[:10000]
    partial_y_train = y_train[10000:]
    
    # 将优化器、损失函数和指标作为字符串传入(rmsprop、binary_crossentropy 和 accuracy 都是 Keras 内置的一部分)
    model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc'])
    
    # 现在使用 512 个样本组成的小批量,将模型训练 20 个轮次(即对 x_train 和 y_train 两个张量中的所有样本进行 20 次迭代)
    # 与此同时,监控在留出的 10 000 个样本上的损失和精度。验证数据传入validation_data 参数
    history = model.fit(partial_x_train, partial_y_train, epochs=20, batch_size=512, validation_data=(x_val, y_val))
    
    # History 对象有一个成员 history ,它是一个字典,包含训练过程中的所有数据,利用该字典可以进行绘图
    history_dict = history.history 
    loss_values = history_dict['loss']
    val_loss_values = history_dict['val_loss']
    acc = history_dict['acc']
    val_acc = history_dict['val_acc']
    epochs = range(1, len(loss_values) + 1)
    
    # 绘制训练损失和验证损失
    plt.figure(1)
    plt.plot(epochs, loss_values, 'bo', label='Training loss')                # bo表示蓝色圆点
    plt.plot(epochs, val_loss_values, 'b', label='Validation loss')            # b表示蓝色
    plt.title('Training and validation loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()
    
    # 绘制训练精度和验证精度
    plt.figure(2)
    plt.plot(epochs, acc, 'bo', label='Training acc')
    plt.plot(epochs, val_acc, 'b', label='Validation acc')
    plt.title('Training and validation accuracy')
    plt.xlabel('Epochs')
    plt.ylabel('Accuracy')
    plt.legend()
    plt.show()

    运行程序,结果如下图所示:

    这个我还是比较有经验的,一眼就看出这是过拟合了(在训练集上训练效果很好,在测试集上训练效果差得离谱)

    综合研判上面的两张图,我们可以考虑在第4轮迭代的时候就停止训练,修改代码如下:

    from keras.datasets import imdb
    from keras import models
    from keras import layers
    import numpy as np
    import matplotlib.pyplot as plt
    
    # 对向量进行one-hot编码
    def vectorize_sequences(sequences, dimension=10000):
        results = np.zeros((len(sequences), dimension))
        for i, sequence in enumerate(sequences):
            results[i, sequence] = 1.
        return results
    
    (train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
    x_train = vectorize_sequences(train_data)                #将训练数据向量化
    x_test = vectorize_sequences(test_data)                    #将测试数据向量化
    y_train = np.asarray(train_labels).astype('float32')    #将标签向量化
    y_test = np.asarray(test_labels).astype('float32')        #将标签向量化
    
    #向网络中添加层
    model = models.Sequential()
    model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
    model.add(layers.Dense(16, activation='relu'))
    model.add(layers.Dense(1, activation='sigmoid'))
    
    # 将原始训练数据留出10000个样本作为验证集
    x_val = x_train[:10000]
    partial_x_train = x_train[10000:]
    y_val = y_train[:10000]
    partial_y_train = y_train[10000:]
    
    # 将优化器、损失函数和指标作为字符串传入(rmsprop、binary_crossentropy 和 accuracy 都是 Keras 内置的一部分)
    model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc'])
    
    # 为防止过拟合,我们仅进行4轮迭代,迭代数量太少,这也就没必要画图了
    model.fit(partial_x_train, partial_y_train, epochs=4, batch_size=512, validation_data=(x_val, y_val))
    results = model.evaluate(x_test, y_test)
    print(results)

    运行程序,得到的results如下:

    第二个数(0.87508)是精确度(accuracy),可以看到进行4轮迭代的这个网络精确度达到了87%,感觉还可以吼吼吼

    也可以用这个网络来进行预测,执行如下python语句即可:

    model.predict(x_test)

     关于evaluate和predict两个函数,可以参见这篇博客:https://blog.csdn.net/DoReAGON/article/details/88552348,其实就是一个是知道x和y,进行验证,一个是只给x,预测y

    对该例子进行的总结:

    • 通常需要对原始数据进行大量预处理,以便将其转换为张量输入到神经网络中。比如说单词序列可以编码为二进制向量
    • 带有 relu 激活的 Dense 层堆叠,可以解决很多种问题(包括情感分类)
    • 对于二分类问题(两个输出类别),网络的最后一层应该是只有一个单元并使用 sigmoid激活的 Dense 层,网络输出应该是 0~1 范围内的标量,表示概率值。
    • 对于二分类问题的 sigmoid 标量输出,应该使用 binary_crossentropy 损失函数。
    • 无论你的问题是什么, rmsprop 优化器通常都是足够好的选择。这一点无须担心。
    • 随着神经网络在训练数据上的表现越来越好,模型最终会过拟合,并在前所未见的数据上得到越来越差的结果。一定要一直监控模型在训练集之外的数据上的性能。

    实例2:新闻分类(单标签、多分类问题)

    每个数据点只能被划分为一个类别,所以是单标签;一共有很多类别,所以是多分类

    本节使用路透社数据集,它包含许多短新闻及其对应的主题,由路透社在 1986 年发布。它是一个简单的、广泛使用的文本分类数据集。它包括 46 个不同的主题:某些主题的样本更多,但训练集中每个主题都有至少 10 个样本。

    将标签向量化有两种方法:你可以将标签列表转换为整数张量,或者使用 one-hot 编码。在这个例子中,标签的 one-hot 编码就是将每个标签表示为全零向量,
    只有标签索引对应的元素为 1。(Keras内置了这个操作)

    Dense 层的堆叠,每层只能访问上一层输出的信息。如果某一层丢失了与分类问题相关的一些信息,那么这些信息无法被后面的层找回,也就是说,每一层都可能成为信息瓶颈。上一个例子使用了 16 维的中间层,但对这个例子来说 16 维空间可能太小了,无法学会区分 46 个不同的类别。这种维度较小的层可能成为信息瓶颈,永久地丢失相关信息。即当我们试图将大量信息(这些信息足够恢复 46 个类别的分割超平面)压缩到维度很小的中间空间时,网络虽然能够将大部分必要信息塞入这个四维表示中,但并不是全部信息。出于这个原因,在这个例子中使用维度更大的层,包含 64 个单元。

    对于这个例子,最好的损失函数是 categorical_crossentropy (分类交叉熵)。它用于衡量两个概率分布之间的距离,这里两个概率分布分别是网络输出的概率分布和标签的真实分布。通过将这两个分布的距离最小化,训练网络可使输出结果尽可能接近真实标签。

    程序如下:

    from keras.datasets import reuters
    from keras.utils.np_utils import to_categorical
    from keras import models
    from keras import layers
    import numpy as np
    import matplotlib.pyplot as plt
    
    # 对向量进行one-hot编码
    def vectorize_sequences(sequences, dimension=10000):
        results = np.zeros((len(sequences), dimension))
        for i, sequence in enumerate(sequences):
            results[i, sequence] = 1.
        return results
    
    (train_data, train_labels), (test_data, test_labels) = reuters.load_data(num_words=10000)
    
    #将训练数据和测试数据向量化(one-hot编码)
    x_train = vectorize_sequences(train_data)
    x_test = vectorize_sequences(test_data)
    
    #将训练标签和测试标签向量化(one-hot编码)
    one_hot_train_labels = to_categorical(train_labels)
    one_hot_test_labels = to_categorical(test_labels)
    
    # 构建网络
    # 网络的最后一层是大小为46的Dense层,对于每个输入样本,网络都会输出一个46维向量,这个向量的每个元素代表不同的类别
    # 最后一层使用了 softmax 激活。这样,网络将输出在 46个不同输出类别上的概率分布
    # 对于每一个输入样本,网络都会输出一个 46 维向量,其中 output[i] 是样本属于第 i 个类别的概率。46 个概率的总和为 1
    model = models.Sequential()
    model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
    model.add(layers.Dense(64, activation='relu'))
    model.add(layers.Dense(46, activation='softmax'))
    
    # 损失函数采用分类交叉熵
    model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
    
    # 在训练数据中留1000个样本作为验证集
    x_val = x_train[:1000]
    partial_x_train = x_train[1000:]
    y_val = one_hot_train_labels[:1000]
    partial_y_train = one_hot_train_labels[1000:]
    
    # 开始训练网络
    history = model.fit(partial_x_train, partial_y_train, epochs=20, batch_size=512, validation_data=(x_val, y_val))
    
    # 绘制训练损失、验证损失、训练精度、验证精度
    history_dict = history.history 
    loss_values = history_dict['loss']
    val_loss_values = history_dict['val_loss']
    acc = history_dict['acc']
    val_acc = history_dict['val_acc']
    epochs = range(1, len(loss_values) + 1)
    # 绘制训练损失和验证损失
    plt.figure(1)
    plt.plot(epochs, loss_values, 'bo', label='Training loss')                # bo表示蓝色圆点
    plt.plot(epochs, val_loss_values, 'b', label='Validation loss')            # b表示蓝色
    plt.title('Training and validation loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()
    # 绘制训练精度和验证精度
    plt.figure(2)
    plt.plot(epochs, acc, 'bo', label='Training acc')
    plt.plot(epochs, val_acc, 'b', label='Validation acc')
    plt.title('Training and validation accuracy')
    plt.xlabel('Epochs')
    plt.ylabel('Accuracy')
    plt.legend()
    plt.show()

    运行程序,结果如下图所示:

     为防止过拟合,我们在9轮迭代的时候停止,修改程序代码如下:

    from keras.datasets import reuters
    from keras.utils.np_utils import to_categorical
    from keras import models
    from keras import layers
    import numpy as np
    import matplotlib.pyplot as plt
    
    # 对向量进行one-hot编码
    def vectorize_sequences(sequences, dimension=10000):
        results = np.zeros((len(sequences), dimension))
        for i, sequence in enumerate(sequences):
            results[i, sequence] = 1.
        return results
    
    (train_data, train_labels), (test_data, test_labels) = reuters.load_data(num_words=10000)
    
    #将训练数据和测试数据向量化(one-hot编码)
    x_train = vectorize_sequences(train_data)
    x_test = vectorize_sequences(test_data)
    
    #将训练标签和测试标签向量化(one-hot编码)
    one_hot_train_labels = to_categorical(train_labels)
    one_hot_test_labels = to_categorical(test_labels)
    
    # 构建网络
    # 网络的最后一层是大小为46的Dense层,对于每个输入样本,网络都会输出一个46维向量,这个向量的每个元素代表不同的类别
    # 最后一层使用了 softmax 激活。这样,网络将输出在 46个不同输出类别上的概率分布
    # 对于每一个输入样本,网络都会输出一个 46 维向量,其中 output[i] 是样本属于第 i 个类别的概率。46 个概率的总和为 1
    model = models.Sequential()
    model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
    model.add(layers.Dense(64, activation='relu'))
    model.add(layers.Dense(46, activation='softmax'))
    
    # 损失函数采用分类交叉熵
    model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
    
    # 在训练数据中留1000个样本作为验证集
    x_val = x_train[:1000]
    partial_x_train = x_train[1000:]
    y_val = one_hot_train_labels[:1000]
    partial_y_train = one_hot_train_labels[1000:]
    
    # 为防止过拟合,我们在9轮迭代的时候停止
    model.fit(partial_x_train, partial_y_train, epochs=9, batch_size=512, validation_data=(x_val, y_val))
    results = model.evaluate(x_test, one_hot_test_labels)
    print(results)

    运行该程序,结果如下:

     可以看到,准确率有79%,还阔以

    训练好的神经网络也可以用来预测:

    predictions = model.predict(x_test)      # predictions 中的每个元素都是长度为 46 的向量,这个向量的所有元素总和为 1。

    其中最大的元素就是预测类别,即概率最大的类别。

    >>> np.argmax(predictions[0])

    4

    针对标签,我们还可以用其他的编码方式:将其转换为整型张量

    y_train = np.array(train_labels)
    y_test = np.array(test_labels)

    这种情况下,唯一需要改变的是损失函数的选择。对于整数标签,损失函数应该使用sparse_categorical_crossentropy 

    对该案例进行的总结:

    • 如果要对 N 个类别的数据点进行分类,网络的最后一层应该是大小为 N 的 Dense 层。
    • 对于单标签、多分类问题,网络的最后一层应该使用 softmax 激活,这样可以输出在 N个输出类别上的概率分布。
    • 这种问题的损失函数几乎总是应该使用分类交叉熵。它将网络输出的概率分布与目标的真实分布之间的距离最小化。
    • 处理多分类问题的标签有两种方法:通过分类编码( 也叫 one-hot 编码 ) 对标签进行编码, 然后使用 categorical_crossentropy 作为损失函数;如果将标签编码为整数,则使用 sparse_categorical_crossentropy 损失函数。
    • 如果你需要将数据划分到许多类别中,应该避免使用太小的中间层,以免在网络中造成信息瓶颈。

    预测房价:回归问题

    本节将要预测 20 世纪 70 年代中期波士顿郊区房屋价格的中位数,已知当时郊区的一些数据点,比如犯罪率、当地房产税率等。本节用到的数据集与前面两个例子有一个有趣的区别。它包含的数据点相对较少,只有 506 个,分为 404 个训练样本和 102 个测试样本。输入数据的每个特征(比如犯罪率)都有不同的取值范围。例如,有些特性是比例,取值范围为 0~1;有的取值范围为 1~12;还有的取值范围为 0~100,等等。

    取值范围差异很大的数据输入到神经网络前应该进行标准化:对于输入数据的每个特征(输入数据矩阵中的列),减去特征平均值,再除以标准差,这样得到的特征平均值为0,标准差为1

    为了在调节网络参数(比如训练的轮数)的同时对网络进行评估,你可以将数据划分为训练集和验证集,正如前面例子中所做的那样。但由于数据点很少,验证集会非常小(比如大约100 个样本)。因此,验证分数可能会有很大波动,这取决于你所选择的验证集和训练集。也就是说,验证集的划分方式可能会造成验证分数上有很大的方差,这样就无法对模型进行可靠的评估。

    在这种情况下,最佳做法是使用 K 折交叉验证(见图 3-11)。这种方法将可用数据划分为 K个分区(K 通常取 4 或 5),实例化 K 个相同的模型,将每个模型在 K - 1 个分区上训练,并在剩下的一个分区上进行评估。模型的验证分数等于 K 个验证分数的平均值。

     编写代码如下:

    from keras.datasets import boston_housing
    from keras import models
    from keras import layers
    import numpy as np
    
    # 由于样本数量很少,我们将使用一个非常小的网络,其中包含两个隐藏层,每层64个单元
    # 一般来说,训练数据越少,过拟合会越严重,而较小的网络可以降低过拟合
    def build_model():
        model = models.Sequential()
        model.add(layers.Dense(64, activation='relu', input_shape=(train_data.shape[1],)))
        model.add(layers.Dense(64, activation='relu'))
        model.add(layers.Dense(1))                # 网络的最后一层只有一个单元,没有激活,是一个线性层,这是标量回归的典型设置
        # 采用mse损失函数,即均方误差(预测值与目标值之差的平方)
        # 监控指标为平均绝对误差(MAE,mean absolute error)。它是预测值与目标值之差的绝对值
        model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
        return model
    
    (train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()
    
    # 数据标准化
    mean = train_data.mean(axis=0)
    train_data -= mean
    std = train_data.std(axis=0)
    train_data /= std
    test_data -= mean
    test_data /= std
    
    # K折验证
    k = 4
    num_val_samples = len(train_data) // k
    num_epochs = 100
    all_scores = []
    
    for i in range(k):
        print('processing fold #', i)
        # 准备验证数据:第k个分区的数据
        val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples]        
        val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples]
        # 准备训练数据:其他所有分区的数据
        partial_train_data = np.concatenate([train_data[:i * num_val_samples], 
            train_data[(i + 1) * num_val_samples:]], axis=0)
        partial_train_targets = np.concatenate([train_targets[:i * num_val_samples], 
            train_targets[(i + 1) * num_val_samples:]], axis=0)
        # 构建模型
        model = build_model()
        # verbose=0代表静默模式
        model.fit(partial_train_data, partial_train_targets, epochs=num_epochs, batch_size=1, verbose=0)
        # 在验证数据上评估模型
        val_mse, val_mae = model.evaluate(val_data, val_targets, verbose=0)
        all_scores.append(val_mae)
    
    print(all_scores)
    print(np.mean(all_scores))

    接下来,我们让训练时间更长一点(500轮迭代),并保存每轮的验证分数记录,修改代码如下:

    from keras.datasets import boston_housing
    from keras import models
    from keras import layers
    import numpy as np
    import matplotlib.pyplot as plt
    
    # 由于样本数量很少,我们将使用一个非常小的网络,其中包含两个隐藏层,每层64个单元
    # 一般来说,训练数据越少,过拟合会越严重,而较小的网络可以降低过拟合
    def build_model():
        model = models.Sequential()
        model.add(layers.Dense(64, activation='relu', input_shape=(train_data.shape[1],)))
        model.add(layers.Dense(64, activation='relu'))
        model.add(layers.Dense(1))                # 网络的最后一层只有一个单元,没有激活,是一个线性层,这是标量回归的典型设置
        # 采用mse损失函数,即均方误差(预测值与目标值之差的平方)
        # 监控指标为平均绝对误差(MAE,mean absolute error)。它是预测值与目标值之差的绝对值
        model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
        return model
    
    (train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()
    
    # 数据标准化
    mean = train_data.mean(axis=0)
    train_data -= mean
    std = train_data.std(axis=0)
    train_data /= std
    test_data -= mean
    test_data /= std
    
    # K折验证
    k = 4
    num_val_samples = len(train_data) // k
    num_epochs = 500
    all_mae_histories = []
    
    for i in range(k):
        print('processing fold #', i)
        # 准备验证数据:第k个分区的数据
        val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples]        
        val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples]
        # 准备训练数据:其他所有分区的数据
        partial_train_data = np.concatenate([train_data[:i * num_val_samples], 
            train_data[(i + 1) * num_val_samples:]], axis=0)
        partial_train_targets = np.concatenate([train_targets[:i * num_val_samples], 
            train_targets[(i + 1) * num_val_samples:]], axis=0)
        # 构建模型
        model = build_model()
        # 进行训练,verbose=0代表静默模式
        history = model.fit(partial_train_data, partial_train_targets, 
            validation_data=(val_data, val_targets),
            epochs=num_epochs, batch_size=1, verbose=0)
        mae_history = history.history['val_mean_absolute_error']
        all_mae_histories.append(mae_history)
    
    # 计算所有轮次中的K折验证分数平均值
    # for x in all_mae_histories意思是每一折的结果(共计4个,是个500长度的数组)
    # 在此基础上,固定一个轮次i,求四个x[i]的平均值
    average_mae_history = [np.mean([x[i] for x in all_mae_histories]) for i in range(num_epochs)]
    
    # 绘制验证分数的变化图
    plt.plot(range(1, len(average_mae_history) + 1), average_mae_history)
    plt.xlabel('Epochs')
    plt.ylabel('Validation MAE')
    plt.show()

    运行程序,结果如下图所示

    因为纵轴的范围较大,且数据方差相对较大,所以难以看清这张图的规律。我们来重新绘制一张图:

    删除前 10 个数据点,因为它们的取值范围与曲线上的其他点不同,并将每个数据点替换为前面数据点的指数移动平均值,以得到光滑的曲线。

    将曲线变光滑的相关函数代码如下:

    def smooth_curve(points, factor=0.9):
        smoothed_points = []
        for point in points:
            if smoothed_points:
                previous = smoothed_points[-1]
                smoothed_points.append(previous * factor + point * (1 - factor))
            else:
                smoothed_points.append(point)
        return smoothed_points

    将画图部分代码修改成如下:

    # 去掉前10个数据,将数据光滑处理
    smooth_mae_history = smooth_curve(average_mae_history[10:])
    
    # 绘制验证分数的变化图
    plt.plot(range(1, len(smooth_mae_history) + 1), smooth_mae_history)
    plt.xlabel('Epochs')
    plt.ylabel('Validation MAE')
    plt.show()

    重新运行代码,结果如下图所示:

    从图中可以看出,验证 MAE 在 80 轮后不再显著降低,之后就开始过拟合,因此我们可以将迭代轮数控制在80,重新运行程序

    案例总结:

    • 回归问题使用的损失函数与分类问题不同。回归常用的损失函数是均方误差(MSE)。同样,回归问题使用的评估指标也与分类问题不同,常见的回归指标是平均绝对误差(MAE)。
    • 如果输入数据的特征具有不同的取值范围,应该先进行预处理,对每个特征单独进行缩放。
    • 如果可用的数据很少,使用 K 折验证可以可靠地评估模型。
    • 如果可用的训练数据很少,最好使用隐藏层较少(通常只有一到两个)的小型网络,以避免严重的过拟合。
  • 相关阅读:
    sentinel-initFunc&控制台
    Sentinel-FlowSlot
    Sentinel-AuthoritySlot&SystemSlot&LogSlot
    Sentinel-DegradeSlot
    Sentinel-ClusterBuilderSlot
    Sentinel-NodeSelectorSlot
    Sentinel整体架构
    Recyclers对象池设计
    加密算法的使用场景
    FastDFS分布式
  • 原文地址:https://www.cnblogs.com/Briddle-ch/p/13268629.html
Copyright © 2011-2022 走看看