Machine LearningMachine Learning & Recommendation & NLP & DLkeras深度学习模型

Keras深度学习实践2—二分类、多分类和标量回归问题简单实现

2019-04-20  本文已影响0人  小可哥哥V

内容参考以及代码整理自“深度学习四大名“著之一《Python深度学习》

一、简介

这次我们会基于keras来实现深度学习的三类常见问题的简单实现,他们是:

二、电影评论分类:二分类问题

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

1.数据加载

IMDB数据集也内置于Keras库。它已经经过预处理:评论(单词序列)已经被转换为正数序列,其中的整数代表某个单词。所以可以用以下代码获取数据集:

from keras.datasets import imdb

(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)

但是由于数据集需要从国外网站下载,所以我采用了本地加载的方式,其中load_local方法是从keras源码中拆分出来,用于处理下载数据的过程,详细可以去github看完整实现

"""
@加载数据集, num_words=10000是仅保留训练数据中前10000个最常出现的单词
本应该使用: (train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
但因为下载地址是国外网站经常断线,所以自己下载数据文件后,用自己的load_local方式加载数据
"""
(train_data, train_labels), (test_data, test_labels) = load_local(num_words=10000)

2.准备数据

整数序列不能直接输入到神经网络,需要将列表转换为张量。转换方法有一下两种。

这里我们选择one-hot将序列编码为二进制矩阵, 我们使用下面方法:

def vectorize_sequences(squences, dimension=10000):
    """
    @函数功能:将序列向量化,初始化全0的序列,在单词索引对应的位置上置1
    """
    resluts = np.zeros((len(squences), dimension))
    for i, sequence in enumerate(squences):
        resluts[i, sequence] = 1
    return resluts

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')

数据准备完成,现在可以输入到神经网络中。

3.构建网络

对于这个二分类问题,输入的数据是向量,而标签是标量1和0。有一类网络在这种问题上表现的很好,就是带有relu激活的全连接层(Dense)的简单堆叠。比如,Dense(16, activation='relu')。这是一个具有16个隐藏单元的全连接层。

16个隐藏单元对应的权重矩阵W的形状为(input_dimension, 16), 与W做点积相当于将输入数据投影到16位表示统建中。你可以将空间的维度直观理解为“网络学习内部表示时所拥有的自由度”。隐藏单元越多(即更高维的表示空间),网络能学到更复杂的表示,但网络的计算代价也会变得更大,而且可能会导致学到不好的模式(这种模式会提高训练数据上的性能,但不会提高测试数据的性能)。

对于评论分类的问题,我们选择这样的网络:

三层网络

构建网络:

""" 构建神经网络 
有两个中间层,每层16个隐藏单元
最后输出层使用sigmoid激活函数
"""
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'))

另外,我们还要选择算是损失函数和优化器。二分类问题最好选择是binary_crossentropy(二元交叉熵)损失。也可以选择均方误差。

""" 编译网络 
因为是二分类问题,所以使用二元交叉熵函数作为损失函数,评价标准还是准确性
"""
model.compile(optimizer=optimizers.RMSprop(lr=0.001), loss=losses.binary_crossentropy, metrics=[metrics.binary_accuracy])

3.验证网络

首先我们要留出验证集:

"""留出一部分验证集"""
x_val = x_train[:10000]
partial_x_train = x_train[10000:]

然后训练模型:

"""训练网络"""
history = model.fit(partial_x_train, partial_y_train, epochs=20, batch_size=512, validation_data=(x_val, y_val))

为了衡量网络,我们来绘制训练精度和验证精度:

def show_acc(history):
    """ 绘制精度曲线 """
    plt.clf()
    history_dict = history.history
    acc = history_dict['binary_accuracy']
    val_acc = history_dict['val_binary_accuracy']

    epochs = range(1, len(val_acc) + 1)

    plt.plot(epochs, acc, 'bo', label='Training acc')
    plt.plot(epochs, val_acc, 'b', label='Validation acc')
    plt.xlabel('Epochs')
    plt.ylabel('Acc')
    plt.legend()

    plt.show()
训练精度和验证损失

三、新闻分类:多分类问题

我们这次将构建一个网络,将路透社新闻划分为46个互斥的主题。这是一个单标签,多分类问题。如果每个数据点可以被划分到多个主题,那么它就是一个多标签、多分类问题。

1.数据集介绍和加载

这次我们使用的是路透社数据集,它包含许多短新闻和对应的主题。每个主题都至少有10个样本。首先我们还是加载数据。依然是使用本地加载的方式。

(train_data, train_labels), (test_data, test_labels) = load_local(num_words=10000)

与IMDB数据集一样,参数num_words=10000将数据限定为10000个最常出现的单词。训练样本数8982,测试样本数2246。每个样本都是一个整数列表:

>>>train_data[10]
[1, 245, 273, 207, 156, 53, 74, 160, 26, 14, 46, 296, 26, 39, 74, 2979,
3554, 14, 46, 4689, 4329, 86, 61, 3499, 4795, 14, 61, 451, 4329, 17, 12]

2.数据准备

首先还是现将数据向量化:

""" 训练数据向量化 """
x_train = vectorize_sequences(train_data)
y_train = vectorize_sequences(test_data)

然后我们要将标签向量化,主要有两种方式:可以将标签列表转换为整数张量,或者使用one-hot编码。keras内置了方法可以实现这个操作:

""" one-hot处理标签 """
one_hot_train_labels = to_categorical(train_labels)
one_hot_test_labels = to_categorical(train_labels)

3.构建网络:

""" 构建神经网络 
有两个中间层,每层64个隐藏单元
多分类问题,最后输出层使用softmax激活函数
"""
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'))

编译模型,选择的损失函数是categorical_crossentroy(分类交叉熵)。

""" 编译模型 """
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])

4.验证网络:

进行网络验证,并画出精度曲线

""" 留出验证集 """
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))

""" 展示结果 """
show_acc(history)

5.在新数据上生成预测结果:

predictions = model.predict(x_test)

predictions中每个元素都是长度为46的向量。

>>> predictions[0].shape
(46,)

这个向量所有元素的总和为1:

>>> np.sum(predictions[0])
1.0

最大元素就是预测类别,即概率最大的类别:

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

四、预测房价: 回归问题

回归问题预测一个连续值而不是离散的标签,例如,根据气象数据预测明天气温,或者根据软件说明书预测完成软件所需要的时间。

1.数据集介绍

波士顿房价数据集包含当时郊区的一些数据点,比如犯罪率、当地房地产税等。这个数据集的数据点相对较少,只有506个、分为404个训练样本和102个测试样本。输入数据的每个特征(比如犯罪率)都有不同的取值范围。例如,有些特征是比例,取值范围为01;有的取值范围112;还有的取值范围为0~100。每个样本都有13个数值特征,比如人均犯罪率、每个住宅的平均房间数、高速公路可达性等。预测的目标是房屋的中位数。

数据加载:

(train_data, train_targets), (test_data, test_targets) = load_local()

2.准备数据

各个数据特征的取值范围差异很大,这样不利于学习,对于这样的数据,普遍采用最佳实践是对每个特征做标准化,即对于输入数据的每个特征,减去特征平均值,再除以标准差,这样得到的特征平均值为0,标准差为1。

"""数据标准化: 减去平均值,再除以标准差,这样得到数据平均值为0,标准差为1"""
mean = train_data.mean(axis=0)
train_data -= mean
std = train_data.std(axis=0)
train_data /= std
test_data -= mean
test_data /= std

3.构建网络

由于训练样本很少,我们将使用一个非常小的网络,其中包含两个隐藏层,每层有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))
    """损失函数使用的均方误差,评价网络则使用的是平均绝对误差(MAE)"""
    model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
    return model

网络的最后一层只有一个单元,没有激活,是一个线性层。这是标量回归的典型设置。添加激活函数会限制输出范围。注意:编译网络用的是mse损失函数,即均方误差,预测值与目标值之差的平方。这是回归问题常用的损失函数。评价网络用的指标是平均绝对误差。它是预测值与目标值之差的绝对值。

4.使用K折验证网络

由于数据点很少,验证集会非常小。验证的分数可能会有很大的波动,这取决与你选择的验证集合训练集。也就是说,验证集的划分方式可能造成验证分数上有很大的误差,这样就无法对模型进行可靠的评估。这就需要使用K折验证,这种方法通常将数据划分为K个分区,实例化K个相同的模型,将每个模型在K-1个分群上训练,并在剩下的一个分区上进行评估。

3折交叉验证
"""
这里使用K值验证法,这是对小数据量网络处理的方式
"""
k = 4
num_val_samples = len(train_data) // k
num_epochs = 100
all_mae_histories = []
for i in range(k):
    print('processing fold #', i)
    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()
    history = model.fit(partial_train_data, partial_train_targets,
                        validation_data=(val_data, val_targets),
                        epochs=num_epochs, batch_size=16, verbose=0)
    mae_history = history.history['val_mean_absolute_error']
    all_mae_histories.append(mae_history)

然后计算所有轮次K折验证的平均值

"""计算所有伦才中MAE的平均值"""
average_mae_history = [np.mean([x[i] for x in all_mae_histories]) for i in range(num_epochs)]

绘制验证分数图,这里对数据进行了平滑,将每个数据点替换为前面数据点的指数移动平均值。并且舍弃了前10个结果,因为训练初期,训练误差较大,影响我们观察曲线趋势

def smooth_curve(points, factor=0.9):
    """
    将每个数据点替换为前面数据点的指数移动平均值,以得到到光滑的曲线
    :param points:
    :param factor:
    :return:
    """
    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


def show_mae(average_mae_history):
    """
    绘制验证,对数据平滑,并且删除前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()
MAR曲线

五、小结
使用Keras解决了机器学习的三个任务:二分类问题、多分类问题、标量回归问题。要点如下:

查看完整代码,请看: https://github.com/ubwshook/MachineLearning

上一篇下一篇

猜你喜欢

热点阅读