《Python深度学习》第三章笔记

神经网络入门

  • 神经网络的核心组件
    • 模型
    • 损失函数与优化器
  • 神经网络解决基本的分类问题与回归问题
    • 二分类问题(电影评论分类)
      • 准备数据
      • 构建网络
      • 验证
    • 多分类问题(新闻分类)
      • 准备数据
      • 构建网络
      • 验证
      • 重新训练模型
    • 回归问题(预测房价)

神经网络的核心组件

  • ,多个层组合成网络(模型)。
  • 输入数据和相应的目标。
  • 损失函数,用于学习的反馈信号。
  • 优化器,决定学习过程如何进行。

多个层链接在一起组成网络,将输入数据映射为预测值。然后损失函数将这些预测值与目标进行比较,得到损失值,用于衡量网络预测值与预期结果的匹配程度,优化器使用这个损失值来更新网络的权重。

层、网络、损失函数与优化器之间的关系如下图:
《Python深度学习》第三章笔记_第1张图片

神经网络的基本数据结构是。层是一个数据处理模块,将一个或多个输入张量转换为一个或多个输出张量。有些层是无状态的,但大多数的层是有状态的,即层的权重。权重是利用随机梯度下降学到的一个或多个张量,其中包含网络的知识。

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

模型

模型是由层构成的网络,是有向无环图。最常见的例子就是层的线性堆叠,将单一输入映射为单一输出。

常见网络拓扑结构:

  • 双分支( two-branch )网络
  • 多头( multihead )网络
  • Inception模块

Keras定义模型有两种方法:

  • 使用Sequential类,仅用于层的线性堆叠,这是目前最常见的网络架构。
from keras import models
from keras import layers

model=models.Sequential()#使用models.Sequential()定义两层线性堆叠网络
model.add(layers.Dense(32,activation='relu',input_shape=(784,)))#输入数据的预期形状
model.add(layers.Dense(10,activation='softmax'))#没有指定输入层的形状(input_shape),此时他默认等于上一层的输出形状
  • 使用函数API,用于层组成的有向无环图,可以构建任意形式的架构。
input_tensor=layers.Input(shape=(784,))#操纵模型处理数据张量
x=layers.Dense(32,activation='relu')(input_tensor)#将层应用于这个张量
output_tensor=layers.Dense(10,activation='softmax')(x)

model=models.Model(inputs=input_tensor,outputs=output_tensor)

损失函数与优化器

损失函数(loss function)是用来估量你模型的预测值f(x)与真实值Y的不一致程度,它是一个非负实值函数,通常使用L(Y, f(x))来表示,损失函数越小,模型的鲁棒性就越好。

优化器基于损失函数对网络中的参数进行更新。

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

下面是单一损失函数的例子,这也是目前最常见的。

from keras import optimizers
model.compile(optimizers=optimizers.RMSprop(lr=0.001),
				loss='mse',#损失函数|均方差
				metrics=['accuracy'])

选择正确的目标函数对解决问题是非常重要的。对于分类、回归、序列预测等常见问题,你可以遵循一些简单的指导原则来选择正确的损失函数。例如,对于二分类问题,你可以使用二元交叉嫡( binary crossentropy)损失函数;对于多分类问题,可以用分类交叉嫡( categorical crossentropy)损失函数;对于回归问题,可以用均方误差( mean-squared crror)损失函数;对于序列学习问题,可以用联结主义时序分类(CTC,connectionist temporal classification)损失函数,等等。

神经网络解决基本的分类问题与回归问题

二分类问题(电影评论分类)

IMDB数据集包含来自互联网的50000条严重两极分化的评论,该数据被分为用于训练的25000条评论和用于测试的25000条评论,训练集和测试集都包含50%的正面评价和50%的负面评价。该数据集已经经过预处理:评论(单词序列)已经被转换为整数序列,其中每个整数代表字典中的某个单词。

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

#加载数据集
from keras.datasets import imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)

train_data和 test_data这两个变量都是评论组成的列表每条评论又是单词索引组成的列表(表示一系列单词)。train_labels和 test_labels都是0和1组成的列表,其中0代表负面评论( negative ),1代表正面评论( positive )。

#将某条评论解码为英文单词
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]])

准备数据

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

  • 填充列表,使其具有相同的长度,再将列表转换成形状为(samples,word_indices)的整数张量,然后网络第一层使用能处理这种整数张量的层。
  • 对列表进行 one-hot编码,将其转换为0和1组成的向量。举个例子,序列[3,5]将会被转换为10 000维向量,只有索引为3和5的元素是1,其余元素都是0。然后网络第一层可以用Dense层,它能够处理浮点数向量数据。
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.  # 索引results矩阵中的位置,赋值为1,全部都是从第0行0列开始的
    return results
x_train = vectorize_sequences(train_data)
x_test = vectorize_sequences(test_data)

构建网络

输入数据是向量,而标签是标量(1和0),带有relu激活的全连接层( Dense)的简单堆叠网络在这种问题上表现很好。
比如Dense (16, activation= ‘relu’ )。传入 Dense层的参数(16)是该层隐藏单元的个数。一个隐藏单元是该层表示空间的一个维度。每个带有relu激活的Dense层都实现了下列张量运算:

output = relu (dot (w, input) + b)

16个隐藏单元对应的权重矩阵w的形状为(input_dimension,16),与w做点积相当于将输入数据投影到16维表示空间中(然后再加上偏置向量b并应用relu运算)。

中间层使用relu作为激活函数,最后一层使用sigmoid激活以输出一个0~1范围内的概率值(表示样本的目标值等于1的可能性,即评论为正面的可能性)。relu ( rectified linear unit,整流线性单元)函数将所有负值归零(见下图1),而 sigmoid 函数则将任意值“压缩”到〔0,1]区间内(见下图2),其输出值可以看作概率值。
《Python深度学习》第三章笔记_第2张图片《Python深度学习》第三章笔记_第3张图片

#标签向量化
y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')
#输入模型
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'))

最后,你需要选择损失函数和优化器。由于你面对的是一个二分类问题,网络输出是一个概率值,那么最好使用binary_crossentropy(二元交叉熵)。对于输出概率值的模型,交叉熵(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])

验证

#留出10000个样本作为验证集
x_val = x_train[:10000]
partial_x_train = x_train[10000:]
y_val = y_train[:10000]
partial_y_train = y_train[10000:]
#训练模型
model.compile(optimizer='rmsprop',
				loss='binary_crossentropy',
				metrics=['acc'])
#返回一个History对象。这个对象有一个成员history,它是一个字典,包含训练过程中的所有数据。
history = model.fit(partial_x_train,
                    partial_y_train,
                    epochs=20,
                    batch_size=512,
                    validation_data=(x_val, y_val))
#绘制训练损失和验证损失
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)

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

《Python深度学习》第三章笔记_第4张图片

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

《Python深度学习》第三章笔记_第5张图片训练损失每轮都在降低,训练精度每轮都在提升。这就是梯度下降优化的预期结果,但验证损失和验证精度并非如此,这就是我们之前说过的情况:模型在训练数据上表现越来越好,但在前所未见的数据上不一定表现得越来越好。产生了过拟合。为了防止过拟合,你可以在3轮之后停止训练。

多分类问题(新闻分类)

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

#加载数据集
from keras.datasets import reuters
(train_data, train_label), (test_data, test_label) = reuters.load_data(num_words=10000)
#将索引解码为新闻文本(非必须)
word_index = reuters.get_word_index()
reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
decoded_newswire = ' '.join([reverse_word_index.get(i-3, '?') for i in train_data[0]])
print(decoded_newswire)

准备数据

#准备数据
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

x_train = vectorize_sequences(train_data)#将训练数据向量化
x_test = vectorize_sequences(test_data)#将测试数据向量化
# one-hot编码方法(keras内置方法)实现标签向量化
#标签的one-hot编码就是将每个标签表示为全零向量,只有标签索引对应的元素为1
from keras.utils.np_utils import to_categorical

one_hot_train_labels = to_categorical(train_label)
one_hot_test_labels = to_categorical(test_label)

构建网络

这个主题分类问题与前面的电影评论分类问题类似,两个例子都是试图对简短的文本片段进行分类。但这个问题有一个新的约束条件:输出类别的数量从2个变为46个。输出空间的维度要大得多。

对于前面用过的 Dense层的堆叠,每层只能访问上一层输出的信息。如果某一层丢失了与分类问题相关的一些信息,那么这些信息无法被后面的层找回,也就是说,每一层都可能成为信息瓶颈。上一个例子使用了16维的中间层,但对这个例子来说16维空间可能太小了,无法学会区分46个不同的类别。这种维度较小的层可能成为信息瓶颈,永久地丢失相关信息。出于这个原因,下面将使用维度更大的层,包含64个单元。

注意单元数不能太多,如果使用包含640个单元的中间层,可能会出现过拟合!

# 定义模型
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'))
  • 网络的最后一层是大小为46的Dense层。这意味着,对于每个输入样本,网络都会输出一个46维向量。这个向量的每个元素(即每个维度)代表不同的输出类别。
  • 最后一层使用了softmax激活。你在MNIST例子中见过这种用法。网络将输出在46个不同输出类别上的概率分布——对于每一个输入样本,网络都会输出一个46维向量,其中 output [i]是样本属于第i个类别的概率。46个概率的总和为1。
# 编译模型
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, #训练20轮次
					batch_size=512, 
					validation_data=(x_val, y_val))

绘制训练损失和验证损失图像、训练精度和验证精度图像如下。
《Python深度学习》第三章笔记_第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'])
history = 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)
#结果 [0.9868815943054715, 0.7862867116928101]
# 80%左右的精度


# 对比随机预测的方式
import copy

test_labels_copy = copy.copy(test_label)
np.random.shuffle(test_labels_copy)
hits_array = np.array(test_label) == np.array(test_labels_copy)
print(float(np.sum(hits_array)) / len(test_label))
#结果 0.18788958147818344
# 20%的精度,可以看出模型的预测效果比随机预测好得多

回归问题(预测房价)

课本例子中要求预测20世纪70年代中期波士顿郊区房屋价格的中位数,已知当时郊区的一些数据点,比如犯罪率、当地房产税率等。

这里用到的数据集与前面两个例子不同,它包含的数据点相对较少,只有506个,分为404个训练样本和102个测试样本。输入数据的每个特征(比如犯罪率)都有不同的取值范围。例如,有些特性是比例,取值范围为(0,1)有的取值范围为(1,12),还有的取值范围为(0,100),等等。

数据集中的每一行数据都是对波士顿周边或城镇房价的情况描述,下面对数据集变量说明下,方便大家理解数据集变量代表的意义。
CRIM: 城镇人均犯罪率
ZN: 住宅用地所占比例
INDUS: 城镇中非住宅用地所占比例
CHAS: 虚拟变量,用于回归分析
NOX: 环保指数
RM: 每栋住宅的房间数
AGE: 1940 年以前建成的自住单位的比例
DIS: 距离 5 个波士顿的就业中心的加权距离
RAD: 距离高速公路的便利指数
TAX: 每一万美元的不动产税率
PTRATIO: 城镇中的教师学生比例
B: 城镇中的黑人比例
LSTAT: 地区中有多少房东属于低收入人群
MEDV: 自住房屋房价中位数(也就是均价)

这里我们对美国加州房价进行预测(因为这刚好是我的作业)

《Python深度学习》第三章笔记_第7张图片《Python深度学习》第三章笔记_第8张图片《Python深度学习》第三章笔记_第9张图片《Python深度学习》第三章笔记_第10张图片《Python深度学习》第三章笔记_第11张图片

补充:K折交叉验证

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

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

如果可用的数据很少,K折验证可以可靠地评估模型。

《Python深度学习》第三章笔记_第12张图片

你可能感兴趣的:(深度学习,python,神经网络)