eg1:将手写数字的灰度图像(28像素*28像素)划分到10个类别中(0~9)
数据集: MINST
包含60000张训练图像和10000张测试图像
1.加载数据集
#加载Keras中的MNIST数据集
from keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
#训练和测试数据查看
train_images.shape #(60000, 28, 28)
len(train_labels) #60000
train_labels #array([5, 0, 4, ..., 5, 6, 8], dtype=uint8)
test_images.shape #(10000, 28, 28)
len(test_labels) #10000
test_labels #array([7, 2, 1, ..., 4, 5, 6], dtype=uint8)
工作流程
- 将训练数据输入到神经网络
- 网络学习将图像和标签关联起来
- 网络对test_imge生成预测 验证结果是否与标签匹配
2.网络架构
#网络架构
from keras import models
from keras import layers
network = models.Sequential() #相当于生成一个容器
network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28,)))
network.add(layers.Dense(10, activation='softmax'))
本例子包含两个Dense层,是密集连接(全连接)的神经层;
第二层是一个10路softmax层,会返回一个由10个概率值(总和为1)的数组;
//每个概率值表示当前数字图像属于10个数字类别之一的概率;
3.编译步骤
network.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
#optimizer:优化
#loss:损失
#metrics:指标(精度)
4.准备数据
#对数据进行预处理,将其变换为网络要求的形状,并缩放到所 有值都在 [0, 1] 区间
train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype('float32') / 255
test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype('float32') / 255
5.准备标签
from keras.utils import to_categorical
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)
6.拟合模型
#拟合模型
#在 Keras 中这一步是通过调用网络的 fit 方法来完成的—— 我们在训练数据上拟合(fit)模型。
network.fit(train_images, train_labels, epochs=5, batch_size=128)
##输出
#Epoch 1/5
#60000/60000 [=============================] - 9s - loss: 0.2524 - acc: 0.9273
#Epoch 2/5
#51328/60000 [=======================>.....] - ETA: 1s - loss: 0.1035 - acc: 0.9692
#Epoch 3/5
#60000/60000 [==============================] - 1s - loss: 0.0687 - acc: 0.9793
#Epoch 4/5
#60000/60000 [==============================] - 1s - loss: 0.0508 - acc: 0.9848
#Epoch 5/5
#60000/60000 [==============================] - 1s - loss: 0.0382 - acc: 0.9890
7.显示损失和精度
test_loss, test_acc = network.evaluate(test_images, test_labels)
8.测试性能
#训练过程中显示了两个数字:一个是网络在训练数据上的损失(loss),另一个是网络在 训练数据上的精度(acc)。我们很快就在训练数据上达到了 0.989(98.9%)的精度。
print('test_acc:', test_acc)
可视化部分:https://www.freesion.com/article/7219811139/
神经网络的数据表示
1.张量
张量也称数据容器,张量是矩阵向任意维度的推广, 张量的维度(dimension)通常叫作轴(axis)。
向量数据:2D 张量,形状为 (samples, features)。
时间序列数据或序列数据:3D 张量,形状为 (samples, timesteps, features)。
图像:4D 张量,形状为 (samples, height, width, channels) 或 (samples, channels, height, width)。
视频:5D 张量,形状为 (samples, frames, height, width, channels) 或 (samples, frames, channels, height, width)。
1、标量(0D 张量)
仅包含一个数字的张量叫作标量(scalar,也叫标量张量、零维张量、0D 张量)。
在 Numpy 中,一个 float32 或 float64 的数字就是一个标量张量(或标量数组)。你可以用 ndim 属性来查看一个 Numpy 张量的轴的个数。标量张量有 0 个轴(ndim == 0)。张量轴的个数也叫作阶(rank)。
import numpy as np
x = np.array(12)
x
#输出array(12)
x.ndim
#输出0
2、向量(1D 张量)
数字组成的数组叫作向量(vector)或一维张量(1D 张量)。
一维张量只有一个轴。维度 (dimensionality)可以表示沿着某个轴上的元素个数(比如 5D 向量),也可以表示张量中轴的个 数(比如 5D 张量)。
x = np.array([12, 3, 6, 14, 7])
x
#输出array([12, 3, 6, 14, 7])
x.ndim
#输出1
#这个向量5个元素,所以被称为5D向量。
#不要把5D向量和5D张量弄混。5D 向量只有一个轴,沿着轴有5个维度,而5D张量有5个轴(沿着每个轴可能有任意个维度)。
一个 Numpy 向量
3、矩阵(2D 张量)
向量组成的数组叫作矩阵(matrix)或二维张量(2D 张量)。
矩阵有 2 个轴(通常叫作行和 列)。你可以将矩阵直观地理解为数字组成的矩形网格。
x = np.array([[5, 78, 2, 34, 0],
[6, 79, 3, 35, 1],
[7, 80, 4, 36, 2]])
x.ndim
#输出2
#第一个轴上的元素叫作行(row),第二个轴上的元素叫作列(column)。在上面的例子中, [5, 78, 2, 34, 0] 是 x 的第一行,[5, 6, 7] 是第一列。
一个Numpy矩阵
4、3D张量与更高维张量
将多个矩阵组合成一个新的数组,可以得到一个 3D 张量,你可以将其直观地理解为数字组成的立方体。
x = np.array([[[5, 78, 2, 34, 0],
[6, 79, 3, 35, 1],
[7, 80, 4, 36, 2]],
[[5, 78, 2, 34, 0],
[6, 79, 3, 35, 1],
[7, 80, 4, 36, 2]],
[[5, 78, 2, 34, 0],
[6, 79, 3, 35, 1],
[7, 80, 4, 36, 2]]])
x.ndim
#输出3
一个Numpy的3D张量
将多个3D张量组合成一个数组,可以创建一个 4D 张量,以此类推。深度学习处理的一般是 0D 到 4D 的张量,但处理视频数据时可能会遇到 5D 张量。
2. 关键属性
张量是由以下三个关键属性来定义的。
-
轴的个数(阶)——例如,3D 张量有 3 个轴,矩阵有 2 个轴。这在 Numpy 等 Python 库中也叫张量的 ndim。
-
形状——这是一个整数元组,表示张量沿每个轴的维度大小(元素个数)。例如,前面矩阵示例的形状为 (3, 5),3D 张量示例的形状为 (3, 3, 5)。向量的形状只包含一个元素,比如 (5,),而标量的形状为空,即 ()。
-
数据类型(在 Python 库中通常叫作 dtype)——这是张量中所包含数据的类型,例如,张量的类型可以是 float32、uint8、float64 等。在极少数情况下,你可能会遇到字符 (char)张量。注意,Numpy(以及大多数其他库)中不存在字符串张量,因为张量存储在预先分配的连续内存段中,而字符串的长度是可变的,无法用这种方式存储。
#首先加载 MNIST 数据集。
from keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
#给出张量 train_images 的轴的个数,即 ndim 属性。
print(train_images.ndim) 3
#形状。
print(train_images.shape) (60000, 28, 28)
#数据类型,即 dtype 属性。
print(train_images.dtype) uint8
#这里 train_images 是一个由8位整数组成的3D张量。更确切地说,它是 60 000个矩阵组成的数组,每个矩阵由 28×28 个整数组成。每个这样的矩阵都是一张灰度图像,元素取值范围为 0~255。
#我们用 Matplotlib 库(Python 标准科学套件的一部分)来显示这个 3D 张量中的第 4 个数字
digit = train_images[4]
import matplotlib.pyplot as plt
plt.imshow(digit, cmap=plt.cm.binary)
plt.show()
MNIST例子中处理的数据
3.切片
张量切片:选择张量特定的元素
#下面这个例子选择第10~100个数字(不包括第 100 个),并将其放在形状为 (90, 28, 28) 的数组中。
my_slice = train_images[10:100]
print(my_slice.shape
#(90, 28, 28)
#它等同于下面这个更复杂的写法,给出了切片沿着每个张量轴的起始索引和结束索引。 注意,: 等同于选择整个轴。
my_slice = train_images[10:100, :, :]
my_slice.shape
#(90, 28, 28)
#等同于前面的例子
my_slice = train_images[10:100, 0:28, 0:28]
my_slice.shape
#(90, 28, 28)
Numpy数组上的张量切片运算
4.批量
深度学习模型会将数据集拆分成小批量
#批量大小为 128。
batch = train_images[:128]
#然后是下一个批量。
batch = train_images[128:256]
#然后是第 n 个批量。
batch = train_images[128 * n:128 * (n + 1)]
#对于这种批量张量,第一个轴(0 轴)叫作批量轴(batch axis)或批量维度(batch dimension)。
MNIST 数据集的一个批量
5.张量运算
https://zhuanlan.zhihu.com/p/144259469
eg2:电影的评论的二分类问题
1.IMDB数据集
IMDB数据集是来自互联网上两极分化严重的5000条评论,其中2500条作为训练集,另外2500条为测试集,训练集和测试集中都包含50%的正面评论和50%的负面评论。
与MINST数据集差不多,IMDB重点评论已经进行过预处理,评论(单词序列)已经转化成整数序列,每个整数代表一个单词。
加载数据集
import keras
from keras.datasets import imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
#num_words=10000:意思是仅保留训练数据前10000个最常出现的单词,低频单词被舍去。
#train_data, train_labels:指评论组成的列表 即:一系列单词;
#test_data, test_labels:都是0,1组成的列表,0代表负面
由于限定为前10000个单词,所有单词索引不超过10000
#将某评论迅速解码成英文单词
# word_index 是一个将单词映射成整数的字典
word_index = imdb.get_word_index()
# 键值颠倒,将整数索引映射为单词
reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
# 解码评论
decoded_review = ' '.join([reverse_word_index.get(i - 3, '?') for i in train_data[0]])
decoded_review
“? this film was just brilliant casting location scenery story direction everyone’s really suited the part they played and you could just imagine being there robert ? is an amazing actor and now the same being director ? father came from the same scottish island as myself so i loved the fact there was a real connection with this film the witty remarks throughout the film were great it was just brilliant so much that i bought the film as soon as it was released for ? and would recommend it to everyone to watch and the fly fishing was amazing really cried at the end it was so sad and you know what they say if you cry at a film it must have been good and this definitely was also ? to the two little boy’s that played the ? of norman and paul they were just brilliant children are often left out of the ? list i think because the stars that play them all grown up are such a big profile for the whole film but these children are amazing and should be praised for what they have done don’t you think the whole story was so lovely because it was true and was someone’s life after all that was shared with us all”
2.准备数据
不能将整数序列直接输入到神经网络,需要将列表转化为张量;
- 填充列表,使其具有相同的长度,转化列表形状,设置网络第一层用于处理整数张量层
- 对列表进行one-hot编码,将其转化成0和1组成的向量
one-hot举例:
序列【3,5】将会被转化成10000维向量,其中只有索引为3或5的元素是1,其他元素都是0;
#将整数序列转化成二进制矩阵
import numpy as np
def vectorize_sequences(sequences, dimension=10000):
# Create an all-zero matrix of shape (len(sequences), dimension)
results = np.zeros((len(sequences), dimension))
for i, sequence in enumerate(sequences):
results[i, sequence] = 1. # 将results[i]的指定索引设为1
return results
# training data
x_train = vectorize_sequences(train_data)
# test data
x_test = vectorize_sequences(test_data)
# 标签向量化
y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')
原始向量:
初始化之后的向量:
3.构建网络
关键问题:
- 网络有多少层;
- 每层有多少隐藏单元;
选择架构
-
两个中间层,每层都有16个隐藏单元;
中间层使用relu作为激活函数; -
第三层输出层,输出一个标量用于预测当前评论的情感;
最后一层使用sigmoid激活函数以输出一个0~1范围内的概率值; -
带有relu激活的全连接层(Dense)的简单堆叠
适合处理输入数据是向量,标签是标量(0和1);
relu(rectified liner unit,整体流线型)可将函数所有的负数归0;
sigmoid:将任意值“压缩”到[0 , 1]区间,其输出值可以看作概率;
Dense(16, activation='relu')
#16是该层隐藏单元的个数,表示维度是16
#每个带有relu激活的Dense层都实现了下列张量的运算
output = relu(dot(W,input) + b)
模型定义
#模型定义
from keras import models
from keras import layers
#创建好容器之后两个中间层一个输出层
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'))
损失函数和优化器
- 对于二分类问题
网络输出是一个概率值(网络最后一层使用 sigmoid 激活函数,仅包含一个单元),那么最好使用 binary_crossentropy(二元交叉熵)损失。
这并不是唯一可行的选择,比如你还可以使用mean_squared_error(均方误差)。但对于输出概率值的模型,交叉熵(crossentropy)往往是最好的选择。交叉熵是来自于信息论领域的概念,用于衡量概率分布之间的距离,在这个例子中就是真实分布与预测值之间的距离。
#编译模型,将优化器、损失函数和指标作为字符串传入
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
#配置优化器
from keras import optimizers
model.compile(optimizer=optimizers.RMSprop(lr=0.001),
loss='binary_crossentropy',
metrics=['accuracy'])
#配置损失函数,自定义损失和指标
from keras import losses
from keras import metrics
model.compile(optimizer=optimizers.RMSprop(lr=0.001),
loss=losses.binary_crossentropy,
metrics=[metrics.binary_accuracy])
4.验证方法
训练测试集
#将原始数据集留出10000个样本作为验证集
x_val = x_train[:10000]
partial_x_train = x_train[10000:]
y_val = y_train[:10000]
partial_y_train = y_train[10000:]
#使用512个样本组成小批量,将模型训练20个轮次,与此同时监控样本的损失和精度;
history = model.fit(partial_x_train,
partial_y_train,
epochs=20,
batch_size=512,
validation_data=(x_val, y_val))
这个对象 成员history,字典;
5.绘制训练损失和验证损失
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
# "bo" is for "blue dot"
plt.plot(epochs, loss, 'bo', label='Training loss')
# b is for "solid blue line"
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
6.绘制训练精度和验证精度
plt.clf() # clear figure
acc_values = history_dict['acc']
val_acc_values = history_dict['val_acc']
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('Loss')
plt.legend()
plt.show()
过拟合:就是在训练样本上的效果越来越好不代表在测试集上的训练效果好;为了防止过拟合可以在第三轮之后停止训练。
7.根据效果重新训练一个模型
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'))
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
model.fit(x_train, y_train, epochs=4, batch_size=512)
results = model.evaluate(x_test, y_test)
训练结果:
8.生成预测结果
9.小结
- 通常需要对原始数据进行大量预处理,以便将其转换为张量输入到神经网络中。单词序列可以编码为二进制向量,但也有其他编码方式。
- 带有 relu 激活的 Dense 层堆叠,可以解决很多种问题(包括情感分类)
- 对于二分类问题(两个输出类别),网络的最后一层应该是只有一个单元并使用 sigmoid激活的 Dense 层,网络输出应该是 0~1 范围内的标量,表示概率值。
- 对于二分类问题的 sigmoid 标量输出,你应该使用 binary_crossentropy 损失函数。
- 无论你的问题是什么,rmsprop 优化器通常都是足够好的选择。这一点你无须担心。
- 随着神经网络在训练数据上的表现越来越好,模型最终会过拟合,并在前所未见的数据上得到越来越差的结果。一定要一直监控模型在训练集之外的数据上的性能。
eg3:新闻的多分类问题
- 单标签、多分类
多个数据点,可以划分到多个类别,每个标签只能属于一个类别; - 多标签、多类别
每个标签可以属于多个类别
1.路透社数据集
路透社数据集,包含许多短新闻及其对应的主题;
是一个简单的分布式文本分类数据集,包括46个主题,也是Keras的一部分
加载数据集
from keras.datasets import reuters
(train_data, train_labels), (test_data, test_labels) = reuters.load_data(num_words=10000)
#num_words=10000:限定为前10000个单词
有8982个训练样本和2246个测试样本
每个样本都是一个整数列表表示单词索引
将索引解码成单词
word_index = reuters.get_word_index()
reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
# Note that our indices were offset by 3
# because 0, 1 and 2 are reserved indices for "padding", "start of sequence", and "unknown".
decoded_newswire = ' '.join([reverse_word_index.get(i - 3, '?') for i in train_data[0]])
decoded_newswire
样本对应的标签是0~45范围内的整数,即:话题索引编号
2.准备数据
数据向量化
#数据向量化
import numpy as np
def vectorize_sequences(sequences, dimension=10000):
results = np.zeros((len(sequences), dimension))
for i, sequence in enumerate(sequences):
results[i, sequence] = 1.
return results
# training data
x_train = vectorize_sequences(train_data)
# test data
x_test = vectorize_sequences(test_data)
#标签项链话
def to_one_hot(labels, dimension=46):
results = np.zeros((len(labels), dimension))
for i, label in enumerate(labels):
results[i, label] = 1.
return results
#training labels
one_hot_train_labels = to_one_hot(train_labels)
#test labels
one_hot_test_labels = to_one_hot(test_labels)
也可以使用keras的内置方法来实现这种操作
from keras.utils.np_utils import to_categorical
one_hot_train_labels = to_categorical(train_labels)
one_hot_test_labels = to_categorical(test_labels)
3.构建网络
与上一个电影评论分类类似,都是试图将文本文字进行分类,但是这个问题有一个新的约束条件:输出类别从2个(0或1)变成46个;
即:输出空间的维度变大;
Dense层堆叠问题
Dense层堆叠起来,每层只能访问上一层的输出信息;
如果某一层丢失了与分类问题相关的信息,那么这些信息就无法从后边找到;
对于上一个二分类问题使用16维度的中间层,但是对于多酚类来说,16维度可能太小,无法区分46个类别,可能永久丢失信息;
网络架构
- 网络的最后一层是大小为46的Dense层;
对于每个输出样本,网络都会输出46维度,每个维度代表不同的输出类别; - 最后一层使用softmax激活函数
MNIST 例子中见过这种用法。网络将输出在 46个不同输出类别上的概率分布——对于每一个输入样本,网络都会输出一个 46 维向量,其中 output[i] 是样本属于第 i 个类别的概率。46 个概率的总和为 1。
#模型定义
from keras import models
from keras import layers
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_crossentropy(分类交叉熵)。它用于衡量两个概率分布之间的距离,这里两个概率分布分别是网络输出的概率分布和标签的真实分布。通过将这两个分布的距离最小化,训练网络可使输出结果尽可能接近真实标签。
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
4.验证方法
#留出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:]
训练网络
#训练网络:共20个伦茨
history = model.fit(partial_x_train,
partial_y_train,
epochs=20,
batch_size=512,
validation_data=(x_val, y_val))
5.可视化精度和损失
#可视化损失
import matplotlib.pyplot as plt
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(loss) + 1)
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
#可视化精度
plt.clf() # clear figure
acc = history.history['acc']
val_acc = history.history['val_acc']
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('Loss')
plt.legend()
plt.show()
6.根据训练效果重新训练模型
由损失和精度效果图,可以看出从第九论开始就过拟合,因此可以将网络定义成9个迭代伦茨,再测试和评估模型
#新网络
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'])
model.fit(partial_x_train,
partial_y_train,
epochs=8,
batch_size=512,
validation_data=(x_val, y_val))
results = model.evaluate(x_test, one_hot_test_labels)
训练效果:
7.对比:完全随机的分类器
import copy
test_labels_copy = copy.copy(test_labels)
np.random.shuffle(test_labels_copy)
float(np.sum(np.array(test_labels) == np.array(test_labels_copy))) / len(test_labels)
0.18477292965271594
8.在新数据集上生成预测结果
可以看出每个向量长度都是46的向量;
向量的所有元素的总和是1
最大的元素就是预测类别,即概率最大的类别;
9.处理标签和损失的另一种类别
- 将标签转换成整数张量
y_train = np.array(train_labels)
y_test = np.array(test_labels)
对于这种编码方法,唯一需要改变的是损失函数的选择;
- 对于之前标签初始化方法的损失函数选择:categorical_crossentropy
- 对于整数标签:sparse_categorical_crossentropy
两者只是接口不同
10.小结
- 如果要对 N 个类别的数据点进行分类,网络的最后一层应该是大小为 N 的 Dense 层。
- 对于单标签、多分类问题,网络的最后一层应该使用 softmax 激活,这样可以输出在 N个输出类别上的概率分布。
- 这种问题的损失函数几乎总是应该使用分类交叉熵。它将网络输出的概率分布与目标的真实分布之间的距离最小化。
- 处理多分类问题的标签有两种方法:
- 通过分类编码(也叫 one-hot 编码)对标签进行编码,然后使用 categorical_crossentropy 作为损失函数。
- 将标签编码为整数,然后使用 sparse_categorical_crossentropy 损失函数。
- 如果你需要将数据划分到许多类别中,应该避免使用太小的中间层,以免在网络中造成信息瓶颈。
eg4:预测房价的回归问题
回归问题,它预测一个连续值而不是离散的标签,例如,根据气象数据
预测明天的气温,或者根据软件说明书预测完成软件项目所需要的时间。
注意 不要将回归问题与 logistic 回归算法混为一谈。令人困惑的是,logistic 回归不是回归算法,而是分类算法。
1.波士顿房价数据集
预测 20 世纪 70 年代中期波士顿郊区房屋价格的中位数,已知当时郊区的一些数据点,比如犯罪率、当地房产税率等。
波士顿数据集的数据点较少:506个,分为404个训练样本和102个测试样本。
输入数据的每个特征(比如犯罪率)都有不同的取值范围。
例如,有些特性是比例,取值范围为 0 ~ 1;有的取值范围为 1~ 12;还有的取值范围为 0~100,等等。
加载数据集
from keras.datasets import boston_housing
(train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()
404个训练样本和102个测试样本,每个样本都要13个特征,比如人均犯罪率、每个住宅的平均房间数等
目标是房屋价格的中位数,单位:千美元
2.准备数据
对每个特征做标准化,即:对输入的每个特征,减去特征平均值,在除去标准差,这样得到的特征平均值是0,标准差是1;
方法:numpy
#数据标准化
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个单元。
训练数据越少,过拟合越严重,较小的网络可以降低过拟合;
#构建网络
from keras import models
from keras import layers
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))
model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
return model
这里网络的最后一层只有一个单元,没有激活,是一个线性层;
这是标量回归:预测单一连续值的回归;
损失函数选择:mse
MSE:均分误差,预测值与目标目标值之差的平分;
MAE:平均误差,预测值与目标值之差的绝对值;
4.K折交叉验证
K折交叉验证:
这种方法将可用数据划分为K个分区(K取4或者5),实例化K个模型,将每一个模型在K-1个分区上训练,剩下一个分区用于评估;
import numpy as np
k = 4
num_val_samples = len(train_data) // k
num_epochs = 100
all_scores = []
for i in range(k):
print('processing fold #', i)
# 准备验证数据,第K个分区数据 # 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)
# 构建Kreas模型(已编译)
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)
这里四个分区的运行结果有一定的差异性,平均分数是比单一结果更可靠的指标------即K折交叉验证的关键;
5.改进训练伦茨
将训练轮次改成500次
from keras import backend as K
# Some memory clean-up
K.clear_session()
num_epochs = 500
all_mae_histories = []
for i in range(k):
print('processing fold #', i)
# Prepare the validation data: data from partition # 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]
# Prepare the training data: data from all other partitions
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)
# Build the Keras model (already compiled)
model = build_model()
# Train the model (in silent mode, 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)
计算每一折的MAE的平均值
average_mae_history = [
np.mean([x[i] for x in all_mae_histories]) for i in range(num_epochs)]
可视化展示
import matplotlib.pyplot as plt
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
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()
再从图中看到在80轮次之后MAE值就不在降低,出现过拟合,改进训练伦茨为80;
# Get a fresh, compiled model.
model = build_model()
# Train it on the entirety of the data.
model.fit(train_data, train_targets,
epochs=80, batch_size=16, verbose=0)
test_mse_score, test_mae_score = model.evaluate(test_data, test_targets)
6.小结
- 回归问题使用的损失函数与分类问题不同。回归常用的损失函数是均方误差(MSE)。
- 同样,回归问题使用的评估指标也与分类问题不同。显而易见,精度的概念不适用于回归问题。常见的回归指标是平均绝对误差(MAE)。
- 如果输入数据的特征具有不同的取值范围,应该先进行预处理,对每个特征单独进行缩放。
- 如果可用的数据很少,使用 K 折验证可以可靠地评估模型。
- 如果可用的训练数据很少,最好使用隐藏层较少(通常只有一到两个)的小型网络,以避免严重的过拟合。