重庆制作网站速成班,网页上上传wordpress,浩子文wordpress,房屋设计平面图效果图第3章 神经网络入门 本章包括以下内容#xff1a; 神经网络的核心组件 Keras 简介 建立深度学习工作站 使用神经网络解决基本的分类问题与回归问题 本章的目的是让你开始用神经网络来解决实际问题。你将进一步巩固在第 2 章第一个示例中学到的知识#xff0c;还会将学到的…第3章 神经网络入门 本章包括以下内容 神经网络的核心组件 Keras 简介 建立深度学习工作站 使用神经网络解决基本的分类问题与回归问题 本章的目的是让你开始用神经网络来解决实际问题。你将进一步巩固在第 2 章第一个示例中学到的知识还会将学到的知识应用于三个新问题这三个问题涵盖神经网络最常见的三种使用场景二分类问题、多分类问题和标量回归问题。
本章将进一步介绍神经网络的核心组件即层、网络、目标函数和优化器还会简要介绍Keras它是贯穿本书的 Python 深度学习库。你还将建立深度学习工作站安装好 TensorFlow和Keras并支持GPU。
最后我们将用三个介绍性示例深入讲解如何使用神经网络解决实际问题这三个示例分别是 将电影评论划分为正面或负面二分类问题 将新闻按主题分类多分类问题 根据房地产数据估算房屋价格回归问题 学完本章你将能够使用神经网络解决简单的机器问题比如对向量数据的分类问题和回归问题。然后你就可以从第 4 章开始建立对机器学习更加具有原则性、理论性的理解。
3.1 神经网络剖析
前面几章介绍过训练神经网络主要围绕以下四个方面。 层多个层组合成网络或模型。 输入数据和相应的目标。 损失函数即用于学习的反馈信号。 优化器决定学习过程如何进行。 你可以将这四者的关系可视化如图 3-1 所示多个层链接在一起组成了网络将输入数据映射为预测值。然后损失函数将这些预测值与目标进行比较得到损失值用于衡量网络预测值与预期结果的匹配程度。优化器使用这个损失值来更新网络的权重。 我们来进一步研究层、网络、损失函数和优化器。
3.1.1 层深度学习的基础组件
我们在第 2 章中介绍过神经网络的基本数据结构是层。层是一个数据处理模块将一个或多个输入张量转换为一个或多个输出张量。有些层是无状态的但大多数的层是有状态的即层的权重。权重是利用随机梯度下降学到的一个或多个张量其中包含网络的知识。
不同的张量格式与不同的数据处理类型需要用到不同的层。
例如
1简单的向量数据保存在形状为 (samples, features) 的 2D 张量中通常用密集连接层densely connected layer也叫全连接层fully connected layer或密集层dense layer对应于 Keras 的 Dense 类来处理。
2序列数据保存在形状为 (samples, timesteps, features) 的 3D 张量中通常用循环层recurrent layer比如 Keras 的 LSTM 层来处理。
3图像数据保存在 4D 张量中通常用二维卷积层Keras 的 Conv2D来处理。
你可以将层看作深度学习的乐高积木Keras 等框架则将这种比喻具体化。在 Keras 中构建深度学习模型就是将相互兼容的多个层拼接在一起以建立有用的数据变换流程。这里层兼容性layer compatibility具体指的是每一层只接受特定形状的输入张量并返回特定形状的输出张量。看看下面这个例子。
from keras import layers
layer layers.Dense(32, input_shape(784,))
我们创建了一个层只接受第一个维度大小为 784 的 2D 张量第 0 轴是批量维度其大小没有指定因此可以任意取值作为输入。这个层将返回一个张量第一个维度的大小变成了 32。
因此这个层后面只能连接一个接受 32 维向量作为输入的层。使用 Keras 时你无须担心兼容性因为向模型中添加的层都会自动匹配输入层的形状例如下面这段代码。
from keras import models
from keras import layers
model models.Sequential()
model.add(layers.Dense(32, input_shape(784,)))
model.add(layers.Dense(32))
其中第二层没有输入形状input_shape的参数相反它可以自动推导出输入形状等于上一层的输出形状。
3.1.2 模型层构成的网络
深度学习模型是层构成的有向无环图。最常见的例子就是层的线性堆叠将单一输入映射为单一输出。但随着深入学习你会接触到更多类型的网络拓扑结构。
一些常见的网络拓扑结构如下。 双分支two-branch网络 多头multihead网络 Inception 模块 网络的拓扑结构定义了一个假设空间hypothesis space。你可能还记得第 1 章里机器学习的定义“在预先定义好的可能性空间中利用反馈信号的指引来寻找输入数据的有用表示。”
选定了网络拓扑结构意味着将可能性空间假设空间限定为一系列特定的张量运算将输入数据映射为输出数据。然后你需要为这些张量运算的权重张量找到一组合适的值。
选择正确的网络架构更像是一门艺术而不是科学。虽然有一些最佳实践和原则但只有动手实践才能让你成为合格的神经网络架构师。后面几章将教你构建神经网络的详细原则也会帮你建立直觉明白对于特定问题哪些架构有用、哪些架构无用。
3.1.3 损失函数与优化器配置学习过程的关键
一旦确定了网络架构你还需要选择以下两个参数。
损失函数目标函数——在训练过程中需要将其最小化。它能够衡量当前任务是否已成功完成。
优化器——决定如何基于损失函数对网络进行更新。它执行的是随机梯度下降SGD的某个变体。
具有多个输出的神经网络可能具有多个损失函数每个输出对应一个损失函数。但是梯度下降过程必须基于单个标量损失值。因此对于具有多个损失函数的网络需要将所有损失函数取平均变为一个标量值。
选择正确的目标函数对解决问题是非常重要的。网络的目的是使损失尽可能最小化因此如果目标函数与成功完成当前任务不完全相关那么网络最终得到的结果可能会不符合你的预期。想象一下利用 SGD 训练一个愚蠢而又无所不能的人工智能给它一个蹩脚的目标函数“将所有活着的人的平均幸福感最大化”。为了简化自己的工作这个人工智能可能会选择杀死绝大多数人类只留几个人并专注于这几个人的幸福——因为平均幸福感并不受人数的影响。这可能并不是你想要的结果请记住你构建的所有神经网络在降低损失函数时和上述的人工智能一样无情。因此一定要明智地选择目标函数否则你将会遇到意想不到的副作用。
幸运的是对于分类、回归、序列预测等常见问题你可以遵循一些简单的指导原则来选择正确的损失函数。
例如对于二分类问题你可以使用二元交叉熵binary crossentropy损失函数
对于多分类问题可以用分类交叉熵categorical crossentropy损失函数
对于回归问题可以用均方误差mean-squared error损失函数
对于序列学习问题可以用联结主义时序分类CTCconnectionist temporal classification损失函数等等。
只有在面对真正全新的研究问题时你才需要自主开发目标函数。在后面几章里我们将详细说明对于各种常见任务应选择哪种损失函数。
3.2 Keras 简介
本书的代码示例全都使用 Keras 实现。Keras 是一个 Python 深度学习框架可以方便地定义和训练几乎所有类型的深度学习模型。Keras 最开始是为研究人员开发的其目的在于快速实验。
Keras 具有以下重要特性。 相同的代码可以在 CPU 或 GPU 上无缝切换运行 。 具有用户友好的 API 便于快速开发深度学习模型的原型。 内置支持卷积网络用于计算机视觉、循环网络用于序列处理以及二者的任意组合 。 支持任意网络架构多输入或多输出模型、层共享、模型共享等。这也就是说Keras 能够构建任意深度学习模型无论是生成式对抗网络还是神经图灵机。 Keras 基于宽松的 MIT 许可证发布这意味着可以在商业项目中免费使用它。它与所有版本的 Python 都兼容截至 2017 年年中从 Python 2.7 到 Python 3.6 都兼容。
Keras 已有 200 000 多个用户既包括创业公司和大公司的学术研究人员和工程师也包括研究生和业余爱好者。Google、Netflix、Uber、CERN、Yelp、Square 以及上百家创业公司都在用 Keras 解决各种各样的问题。Keras 还是机器学习竞赛网站 Kaggle 上的热门框架最新的深度学习竞赛中几乎所有的优胜者用的都是 Keras 模型如图 3-2 所示。 3.2.1 Keras、TensorFlow、Theano 和 CNTK
Keras 是一个模型级model-level的库为开发深度学习模型提供了高层次的构建模块。它不处理张量操作、求微分等低层次的运算。相反它依赖于一个专门的、高度优化的张量库来完成这些运算这个张量库就是 Keras 的后端引擎backend engine。Keras 没有选择单个张量库并将 Keras 实现与这个库绑定而是以模块化的方式处理这个问题见图 3-3。因此几个不同的后端引擎都可以无缝嵌入到 Keras 中。目前Keras 有三个后端实现TensorFlow 后端、Theano 后端和微软认知工具包CNTKMicrosoft cognitive toolkit后端。未来 Keras 可能会扩展到支持更多的深度学习引擎。 TensorFlow、CNTK 和 Theano 是当今深度学习的几个主要平台。Theano 由蒙特利尔大学的MILA 实验室开发TensorFlow 由 Google 开发CNTK 由微软开发。你用 Keras 写的每一段代码都可以在这三个后端上运行无须任何修改。也就是说你在开发过程中可以在两个后端之间无缝切换这通常是很有用的。例如对于特定任务某个后端的速度更快那么我们就可以无缝切换过去。我们推荐使用 TensorFlow 后端作为大部分深度学习任务的默认后端因为它的应用最广泛可扩展而且可用于生产环境。
通过 TensorFlow或 Theano、CNTKKeras 可以在 CPU 和 GPU 上无缝运行。在 CPU 上运行时TensorFlow 本身封装了一个低层次的张量运算库叫作 Eigen在 GPU 上运行时TensorFlow封装了一个高度优化的深度学习运算库叫作 NVIDIA CUDA 深度神经网络库cuDNN。
3.2.2 使用 Keras 开发概述
你已经见过一个 Keras 模型的示例就是 MNIST 的例子。典型的 Keras 工作流程就和那个例子类似。
(1) 定义训练数据输入张量和目标张量。
(2) 定义层组成的网络或模型将输入映射到目标。
(3) 配置学习过程选择损失函数、优化器和需要监控的指标。
(4) 调用模型的 fit 方法在训练数据上进行迭代。
定义模型有两种方法一种是使用 Sequential 类仅用于层的线性堆叠这是目前最常见的网络架构另一种是函数式 APIfunctional API用于层组成的有向无环图让你可以构建任意形式的架构。
前面讲过这是一个利用 Sequential 类定义的两层模型注意我们向第一层传入了输入数据的预期形状。
from keras import models
from keras import layers
model models.Sequential()
model.add(layers.Dense(32, activationrelu, input_shape(784,)))
model.add(layers.Dense(10, activationsoftmax))
下面是用函数式 API 定义的相同模型。
input_tensor layers.Input(shape(784,))
x layers.Dense(32, activationrelu)(input_tensor)
output_tensor layers.Dense(10, activationsoftmax)(x)
model models.Model(inputsinput_tensor, outputsoutput_tensor)
利用函数式 API你可以操纵模型处理的数据张量并将层应用于这个张量就好像这些层是函数一样。
注意 第 7 章有关于函数式 API 的详细指南。在那之前我们的代码示例中只会用到 Sequential 类。
一旦定义好了模型架构使用 Sequential 模型还是函数式 API 就不重要了。接下来的步骤都是相同的。
配置学习过程是在编译这一步你需要指定模型使用的优化器和损失函数以及训练过程中想要监控的指标。下面是单一损失函数的例子这也是目前最常见的。
from keras import optimizers
model.compile(optimizeroptimizers.RMSprop(lr0.001),lossmse,metrics[accuracy])
最后学习过程就是通过 fit() 方法将输入数据的 Numpy 数组和对应的目标数据传入模型这一做法与 Scikit-Learn 及其他机器学习库类似。
model.fit(input_tensor, target_tensor, batch_size128, epochs10)
在接下来的几章里你将会在这些问题上培养可靠的直觉哪种类型的网络架构适合解决哪种类型的问题如何选择正确的学习配置如何调节模型使其给出你想要的结果我们将在3.4~3.6 节讲解三个基本示例分别是二分类问题、多分类问题和回归问题。
3.3 建立深度学习工作站
在开始开发深度学习应用之前你需要建立自己的深度学习工作站。虽然并非绝对必要但强烈推荐你在现代 NVIDIA GPU 上运行深度学习实验。某些应用特别是卷积神经网络的图像处理和循环神经网络的序列处理在 CPU 上的速度非常之慢即使是高速多核 CPU 也是如此。即使是可以在 CPU 上运行的深度学习应用使用现代 GPU 通常也可以将速度提高 5 倍或 10 倍。如果你不想在计算机上安装 GPU也可以考虑在 AWS EC2 GPU 实例或 Google 云平台上运行深度学习实验。但请注意时间一长云端 GPU 实例可能会变得非常昂贵。
无论在本地还是在云端运行最好都使用 UNIX 工作站。虽然从技术上来说可以在 Windows上使用 KerasKeras 的三个后端都支持 Windows但我们不建议这么做。在附录 A 的安装说明中我们以安装了 Ubuntu 的计算机为例。如果你是 Windows 用户最简单的解决方案就是安装 Ubuntu双系统。这看起来可能有点麻烦但从长远来看使用 Ubuntu 将会为你省去大量时间和麻烦。
注意使用 Keras 需要安装 TensorFlow、CNTK 或 Theano如果你希望能够在三个后端之间来回切换那么可以安装三个。本书将重点介绍 TensorFlow并简要介绍一下 Theano不会涉及 CNTK。
3.3.1 Jupyter 笔记本运行深度学习实验的首选方法
Jupyter 笔记本是运行深度学习实验的好方法特别适合运行本书中的许多代码示例。它广泛用于数据科学和机器学习领域。笔记本notebook是 Jupyter Notebook 应用生成的文件可以在浏览器中编辑。它可以执行 Python 代码还具有丰富的文本编辑功能可以对代码进行注释。笔记本还可以将冗长的实验代码拆分为可独立执行的短代码这使得开发具有交互性而且如果后面的代码出现问题你也不必重新运行前面的所有代码。
我们推荐使用 Jupyter 笔记本来上手 Keras虽然这并不是必需的。你也可以运行独立的Python 脚本或者在 IDE比如 PyCharm中运行代码。本书所有代码示例都以开源笔记本的形式提供你可以在本书网站上下载https://www.manning.com/books/deep-learning-with-python。
3.3.2 运行 Keras两种选择
想要在实践中使用 Keras我们推荐以下两种方式。
使用官方的 EC2 深度学习 Amazon 系统映像AMI并在 EC2 上以 Jupyter 笔记本的方式运行 Keras 实验。如果你的本地计算机上没有 GPU你可以选择这种方式。附录 B 给出了详细指南。
在本地 UNIX 工作站上从头安装。然后你可以运行本地 Jupyter 笔记本或常规的 Python 代码库。如果你已经拥有了高端的 NVIDIA GPU可以选择这种方式。附录 A 给出了基于 Ubuntu 的详细安装指南。
我们来详细看一下这两种方式的优缺点。
3.3.3 在云端运行深度学习任务优点和缺点
如果你还没有可用于深度学习的 GPU即最新的高端 NVIDIA GPU那么在云端运行深度学习实验是一种简单又低成本的方法让你无须额外购买硬件就可以上手。如果你使用Jupyter 笔记本那么在云端运行的体验与在本地运行完全相同。截至 2017 年年中最容易上手深度学习的云产品肯定是 AWS EC2。附录 B 给出了在 EC2 GPU 实例上运行 Jupyter 笔记本的详细指南。
但如果你是深度学习的重度用户从长期来看这种方案是难以持续的甚至几个星期都不行。EC2 实例的价格很高附录 B 推荐的实例p2.xlarge 实例计算能力一般在 2017 年年中的价格是每小时 0.90 美元。与此相对的是一款可靠的消费级 GPU 价格在 1000~1500 美元——这个价格一直相当稳定而这种 GPU 的性能则在不断提高。如果你准备认真从事深度学习那么应该建立具有一块或多块 GPU 的本地工作站。
简而言之EC2 是很好的上手方法。你完全可以在 EC2 GPU 实例上运行本书的代码示例。但如果你想成为深度学习的高手那就自己买 GPU。
3.3.4 深度学习的最佳 GPU
如果你准备买一块 GPU应该选择哪一款呢首先要注意一定要买 NVIDIA GPU。NVIDIA 是目前唯一一家在深度学习方面大规模投资的图形计算公司现代深度学习框架只能在 NVIDIA 显卡上运行。截至 2017 年年中最容易上手深度学习的云产品肯定是 AWS EC2。附录 B 给出了在 EC2 GPU 实例上运行 Jupyter 笔记本的详细指南。 截至 2017 年年中我们推荐 NVIDIA TITAN Xp 为市场上用于深度学习的最佳显卡。如果预算较少你也可以考虑 GTX 1060。如果你读到本节的时间是在 2018 年或更晚请花点时间在网上查找最新的推荐因为每年都会推出新的模型。 从这一节开始我们将认为你的计算机已经安装好 Keras 及其依赖最好支持 GPU。在继续阅读之前请确认已经完成此步骤。阅读附录中的详细指南还可以在网上搜索进一步的帮助。安装 Keras 及常见的深度学习依赖的教程有很多。
下面我们将深入讲解 Keras 示例。
3.4 电影评论分类二分类问题
二分类问题可能是应用最广泛的机器学习问题。在这个例子中你将学习根据电影评论的文字内容将其划分为正面或负面。
3.4.1 IMDB 数据集
本节使用 IMDB 数据集它包含来自互联网电影数据库IMDB的 50 000 条严重两极分化的评论。数据集被分为用于训练的 25 000 条评论与用于测试的 25 000 条评论训练集和测试集都包含 50% 的正面评论和 50% 的负面评论。
为什么要将训练集和测试集分开因为你不应该将训练机器学习模型的同一批数据再用于测试模型模型在训练数据上的表现很好并不意味着它在前所未见的数据上也会表现得很好而且你真正关心的是模型在新数据上的性能因为你已经知道了训练数据对应的标签显然不再需要模型来进行预测。例如你的模型最终可能只是记住了训练样本和目标值之间的映射关系但这对在前所未见的数据上进行预测毫无用处。下一章将会更详细地讨论这一点。
与 MNIST 数据集一样IMDB 数据集也内置于 Keras 库。它已经过预处理评论单词序列已经被转换为整数序列其中每个整数代表字典中的某个单词。
下列代码将会加载 IMDB 数据集第一次运行时会下载大约 80MB 的数据。
代码清单 3-1 加载 IMDB 数据集
from keras.datasets import imdb
(train_data, train_labels), (test_data, test_labels) imdb.load_data(num_words10000)
参数 num_words10000 的意思是仅保留训练数据中前 10 000 个最常出现的单词。低频单词将被舍弃。这样得到的向量数据不会太大便于处理。
train_data 和 test_data 这两个变量都是评论组成的列表每条评论又是单词索引组成的列表表示一系列单词。train_labels 和 test_labels 都是 0 和 1 组成的列表其中 0代表负面negative1 代表正面positive。 train_data[0]
[1, 14, 22, 16, ... 178, 32]train_labels[0]
1
由于限定为前 10 000 个最常见的单词单词索引都不会超过 10 000。 max([max(sequence) for sequence in train_data])
9999
下面这段代码很有意思你可以将某条评论迅速解码为英文单词。
# 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]])
print(train_labels[0], train_labels[0])
# 打印训练数据形状
print(train_data.shape, train_data.shape)
train_labels[0] 1
train_data.shape (25000,)
print(train_data[0]:, train_data[0])
train_data[0]: [1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32]
print(word_index.get(this):, word_index.get(this))
print(len(word_index):, len(word_index))
print(word_index[this]:, word_index[this])
print(word_index[film]:, word_index[film])
print(word_index[fawn]:, word_index[fawn])print(reverse_word_index[11]:, reverse_word_index[11])
word_index.get(this): 11
len(word_index): 88584
word_index[this]: 11
word_index[film]: 19
word_index[fawn]: 34701
reverse_word_index[11]: this
3.4.2 准备数据
你不能将整数序列直接输入神经网络。你需要将列表转换为张量。转换方法有以下两种。
填充列表使其具有相同的长度再将列表转换成形状为 (samples, word_indices)的整数张量然后网络第一层使用能处理这种整数张量的层即 Embedding 层本书后面会详细介绍。
对列表进行 one-hot 编码将其转换为 0 和 1 组成的向量。举个例子序列 [3, 5] 将会被转换为 10 000 维向量只有索引为 3 和 5 的元素是 1其余元素都是 0。然后网络第一层可以用 Dense 层它能够处理浮点数向量数据。
下面我们采用后一种方法将数据向量化。为了加深理解你可以手动实现这一方法如下所示。 代码清单 3-2 将整数序列编码为二进制矩阵 #代码清单 3-2 将整数序列编码为二进制矩阵# 定义vectorize_sequences函数
def vectorize_sequences(sequences, dimension10000):results np.zeros((len(sequences), dimension))# enumeratefor i, sequence in enumerate(sequences):results[i, sequence] 1. # 将 results[i] 的指定索引设为 1# print(i, sequence)# print(results)return resultsx_train0 vectorize_sequences(train_data[0:3])
print(x_train0) [[0. 1. 1. ... 0. 0. 0.][0. 1. 1. ... 0. 0. 0.][0. 1. 1. ... 0. 0. 0.]] x_train vectorize_sequences(train_data)
x_test vectorize_sequences(test_data) # 将训练数据向量化
x_train vectorize_sequences(train_data)
# 将测试数据向量化
x_test vectorize_sequences(test_data)
print(x_train:, x_train)
print(x_test:, x_test) x_train: [[0. 1. 1. ... 0. 0. 0.][0. 1. 1. ... 0. 0. 0.][0. 1. 1. ... 0. 0. 0.]...[0. 1. 1. ... 0. 0. 0.][0. 1. 1. ... 0. 0. 0.][0. 1. 1. ... 0. 0. 0.]]
x_test: [[0. 1. 1. ... 0. 0. 0.][0. 1. 1. ... 0. 0. 0.][0. 1. 1. ... 0. 0. 0.]...[0. 1. 1. ... 0. 0. 0.][0. 1. 1. ... 0. 0. 0.][0. 1. 1. ... 0. 0. 0.]] 你还应该将标签向量化。 y_train np.asarray(train_labels).astype(float32)
y_test np.asarray(test_labels).astype(float32)
print(y_train:, y_train)
print(y_test:, y_test)
print(y_train.shape, y_train.shape) y_train: [1. 0. 0. ... 0. 1. 0.]
y_test: [0. 1. 1. ... 0. 0. 0.]
y_train.shape (25000,) # enumerate的测试
seasons [Spring, Summer, Fall, Winter]
print(seasons:, seasons)
print(list(enumerate(seasons)):, list(enumerate(seasons)))
print(list(enumerate(seasons, start1)):, list(enumerate(seasons, start1))) seasons: [Spring, Summer, Fall, Winter]
list(enumerate(seasons)): [(0, Spring), (1, Summer), (2, Fall), (3, Winter)]
list(enumerate(seasons, start1)): [(1, Spring), (2, Summer), (3, Fall), (4, Winter)]现在可以将数据输入到神经网络中。 3.4.3 构建网络 输入数据是向量而标签是标量1 和 0这是你会遇到的最简单的情况。有一类网络在这种问题上表现很好就是带有 relu 激活的全连接层Dense的简单堆叠比如 Dense(16, activationrelu)。 传入 Dense 层的 参数16是该层隐藏单元的个数 。一个 隐藏单元 hidden unit是该层 表示空间的一个维度。我们在第 2 章讲过每个带有 relu 激活的 Dense 层都实现了下列张量 运算 output relu(dot(W, input) b) 16 个隐藏单元对应的权重矩阵 W 的形状为 (input_dimension, 16) 与 W 做点积相当于 将输入数据投影到 16 维表示空间中然后再加上偏置向量 b 并应用 relu 运算。你可以将表示空间的维度直观地理解为“ 网络学习内部表示时所拥有的自由度 ”。隐藏单元越多即更高维的表示空间网络越能够学到更加复杂的表示但网络的计算代价也变得更大而且可能会导致学到不好的模式这种模式会提高训练数据上的性能但不会提高测试数据上的性能。 对于这种 Dense 层的堆叠你需要确定以下两个关键架构 网络有多少层 每层有多少个隐藏单元。 第 4 章中的原则将会指导你对上述问题做出选择。现在你只需要相信我选择的下列架构 两个中间层每层都有 16 个隐藏单元 第三层输出一个标量预测当前评论的情感。 中间层使用 relu 作为激活函数最后一层使用 sigmoid 激活以输出一个 0~1 范围内的概率 值表示样本的目标值等于 1 的可能性即评论为正面的可能性。 relurectified linear unit 整流线性单元 函数将所有负值归零见图 3-4 而 sigmoid 函数则将任意值“压缩”到 [0, 1] 区间内 见图 3-5 其输出值可以看作概率值 。 3-6 显示了网络的结构。代码清单 3-3 是其 Keras 实现与前面见过的 MNIST 例子类似。 代码清单 3-3 模型定义 from keras import models
from keras import layersmodel models.Sequential()
model.add(layers.Dense(16, activationrelu, input_shape(10000,)))
model.add(layers.Dense(16, activationrelu))
model.add(layers.Dense(1, activationsigmoid)) 什么是激活函数为什么要使用激活函数 如果没有 relu 等激活函数也叫 非线性 Dense 层将只包含两个线性运算——点积和加法 output dot(W, input) b 这样 Dense 层就只能学习输入数据的 线性变换仿射变换 该层的假设空间是从输入数据到 16 位空间所有可能的线性变换集合。这种假设空间非常有限无法利用多个表示层的优势因为多个线性层堆叠实现的仍是线性运算添加层数并不会扩展假设空间。 为了得到更丰富的假设空间从而充分利用多层表示的优势你 需要添加非线性或激活函数 。 relu 是深度学习中最常用的激活函数但还有许多其他函数可选它们都有类似的奇怪名称比如 prelu 、 elu 等。 最后你需要选择损失函数和优化器 。由于你面对的是一个二分类问题网络输出是一个概率值网络最后一层使用 sigmoid 激活函数仅包含一个单元那么最好使用 binary_ crossentropy二元交叉熵损失 。这并不是唯一可行的选择比如你还可以使用 mean_ squared_error 均方误差。但对于输出概率值的模型 交叉熵crossentropy 往往是最好的选择。交叉熵是来自于信息论领域的概念用于衡量概率分布之间的距离在这个例子中就 是真实分布与预测值之间的距离。 下面的步骤是用 rmsprop 优化器 和 binary_crossentropy 损失函数 来配置模型。注意我们还在训练过程中监控精度。 代码清单 3-4 编译模型
# 选择损失函数和优化器
model.compile(optimizerrmsprop,lossbinary_crossentropy,metrics[accuracy])
上述代码将优化器、损失函数和指标作为字符串传入这是因为 rmsprop、binary_ crossentropy 和 accuracy 都是 Keras 内置的一部分。
有时你可能希望配置自定义优化器的参数或者传入自定义的损失函数或指标函数。前者可通过向 optimizer 参数传入一个优化器类实例来实现如代码清单 3-5 所示后者可通过向 loss 和 metrics 参数传入函数对象来实现如代码清单 3-6 所示。
代码清单 3-5 配置优化器
model.compile(optimizeroptimizers.RMSprop(lr0.001),lossbinary_cross-entropy,metrics[accuracy]) 代码清单 3-6 使用自定义的损失和指标 model.compile(optimizeroptimizers.RMSprop(lr0.001),losslosses.binary_crossentropy,metrics[metrics.binary_accuracy]) 3.4.4 验证你的方法 为了在训练过程中监控模型在前所未见的数据上的精度你需要将原始训练数据留出 10 000 个样本作为验证集。 # 代码清单 3-7 留出验证集
x_val x_train[:10000]
partial_x_train x_train[10000:]
y_val y_train[:10000]
partial_y_train y_train[10000:] 现在使用 512 个样本组成的小批量 将模型训练 20 个轮次 即对 x_train 和 y_train 两个张量中的所有样本进行 20 次迭代 。 与此同时你还要监控在留出的 10 000 个样本上的损失和精度。你可以通过将验证数据传入 validation_data 参数来完成。 # 代码清单 3-8 训练模型
model.compile(optimizerrmsprop,lossbinary_crossentropy,metrics[acc])# 使用 512 个样本组成的小批量将模型训练 20 个轮次即对 x_train 和 y_train 两
# 个张量中的所有样本进行 20 次迭代
# 在 CPU 上运行每轮的时间不到 2 秒训练过程将在 20 秒内结束。
# 每轮结束时会有短暂的停顿因为模型要计算在验证集的 10 000 个样本上的损失和精度。history model.fit(partial_x_train,partial_y_train,epochs20,batch_size512,validation_data(x_val, y_val))# 调用 model.fit() 返回了一个 History 对象
# 这个对象有一个成员 history它是一个字典包含训练过程中的所有数据
# 字典中包含 4 个条目对应训练过程和验证过程中监控的指标 在 CPU 上运行每轮的时间不到 2 秒训练过程将在 20 秒内结束。每轮结束时会有短暂的停顿因为模型要计算在验证集的 10 000 个样本上的损失和精度。 注意调用 model.fit() 返回了一个 History 对象。这个对象有一个成员 history它是一个字典包含训练过程中的所有数据。我们来看一下。 history_dict history.history
print(history_dict.keys()) dict_keys([loss, acc, val_loss, val_acc]) loss_values history_dict[loss]
val_loss_values history_dict[val_loss]print(history_dict:, history_dict)
print(loss_values:, loss_values)
print(val_loss_values:, val_loss_values) history_dict: {loss: [0.5311576724052429, 0.3295612335205078, 0.24616199731826782, 0.20007984340190887, 0.16878725588321686, 0.14065328240394592, 0.12178117036819458, 0.10707651078701019, 0.08816918730735779, 0.08290471136569977, 0.0661439448595047, 0.05832947418093681, 0.04871470853686333, 0.041972383856773376, 0.03593316674232483, 0.03029720112681389, 0.02458023838698864, 0.020768452435731888, 0.020750146359205246, 0.012916498817503452], acc: [0.7743333578109741, 0.8935999870300293, 0.9193999767303467, 0.9332000017166138, 0.9430000185966492, 0.9547333121299744, 0.9611999988555908, 0.9675999879837036, 0.9740666747093201, 0.9751999974250793, 0.9829333424568176, 0.9851999878883362, 0.9885333180427551, 0.9905333518981934, 0.9923999905586243, 0.9939333200454712, 0.9965333342552185, 0.9972666501998901, 0.9962666630744934, 0.9993333220481873], val_loss: [0.3976058065891266, 0.3222864866256714, 0.29301297664642334, 0.28684449195861816, 0.27952781319618225, 0.29999497532844543, 0.2950637638568878, 0.3080497980117798, 0.3298104405403137, 0.3424592912197113, 0.36206743121147156, 0.38678547739982605, 0.3946729898452759, 0.42827942967414856, 0.43978291749954224, 0.4857790470123291, 0.485530823469162, 0.5184621810913086, 0.5329005122184753, 0.5609933137893677], val_acc: [0.8687000274658203, 0.878600001335144, 0.8820000290870667, 0.8851000070571899, 0.8870000243186951, 0.8798999786376953, 0.8855999708175659, 0.8835999965667725, 0.8826000094413757, 0.8754000067710876, 0.8733000159263611, 0.8718000054359436, 0.8756999969482422, 0.8697999715805054, 0.8726000189781189, 0.8718000054359436, 0.8708999752998352, 0.8723999857902527, 0.8716999888420105, 0.8705000281333923]}loss_values: [0.5311576724052429, 0.3295612335205078, 0.24616199731826782, 0.20007984340190887, 0.16878725588321686, 0.14065328240394592, 0.12178117036819458, 0.10707651078701019, 0.08816918730735779, 0.08290471136569977, 0.0661439448595047, 0.05832947418093681, 0.04871470853686333, 0.041972383856773376, 0.03593316674232483, 0.03029720112681389, 0.02458023838698864, 0.020768452435731888, 0.020750146359205246, 0.012916498817503452]val_loss_values: [0.3976058065891266, 0.3222864866256714, 0.29301297664642334, 0.28684449195861816, 0.27952781319618225, 0.29999497532844543, 0.2950637638568878, 0.3080497980117798, 0.3298104405403137, 0.3424592912197113, 0.36206743121147156, 0.38678547739982605, 0.3946729898452759, 0.42827942967414856, 0.43978291749954224, 0.4857790470123291, 0.485530823469162, 0.5184621810913086, 0.5329005122184753, 0.5609933137893677] 字典中包含 4 个条目对应训练过程和验证过程中监控的指标。在下面两个代码清单中我们将使用 Matplotlib 在同一张图上绘制训练损失和验证损失见图 3-7以及训练精度和验证精度见图 3-8 。请注意由于网络的随机初始化不同你得到的结果可能会略有不同。 print(代码清单 3-9 绘制训练损失和验证损失___结果)
import matplotlib.pyplothistory_dict history.history
loss_values history_dict[loss]
val_loss_values history_dict[val_loss]epochs range(1, len(loss_values) 1)# bo 表示蓝色圆点 # b 表示蓝色实线
plt.plot(epochs, loss_values, bo, labelTraining loss)
plt.plot(epochs, val_loss_values, b, labelValidation loss)
plt.title(Training and validation loss)
plt.xlabel(Epochs)
plt.ylabel(Loss)
plt.legend()
plt.show() import matplotlib.pyplotmatplotlib.pyplot.clf()
acc history_dict[acc]
val_acc history_dict[val_acc]
matplotlib.pyplot.plot(epochs, acc, bo, labelTraining acc)
matplotlib.pyplot.plot(epochs, val_acc, b, labelValidation acc)
matplotlib.pyplot.title(Training and validation accuracy)
matplotlib.pyplot.xlabel(Epochs)
matplotlib.pyplot.ylabel(Accuracy)
matplotlib.pyplot.legend()
matplotlib.pyplot.show() 如你所见 训练损失每轮都在降低 训练精度每轮都在提升。这就是梯度下降优化的预期结果——你想要最小化的量随着每次迭代越来越小。 但验证损失和验证精度并非如此它们似乎在第四轮达到最佳值。这就是我们之前警告过的一种情况模型在训练数据上的表现越来越好但在前所未见的数据上不一定表现得越来越好。准确地说你看到的是 过拟合overfit 在第二轮之后你对训练数据过度优化最终学到的表示仅针对于训练数据无法泛化到训练集之外的数据。 在这种情况下为了防止过拟合你可以在 3 轮之后停止训练。通常来说你可以使用许多方法来降低过拟合我们将在第 4 章中详细介绍。 我们从头开始训练一个新的网络训练 4 轮然后在测试数据上评估模型。 from keras.datasets import imdb
from keras import models
from keras import layers
from keras import optimizers
import numpy as np(train_data, train_labels), (test_data, test_labels) imdb.load_data(num_words10000)# 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]])# 定义vectorize_sequences函数
def vectorize_sequences(sequences, dimension10000):results np.zeros((len(sequences), dimension))for i, sequence in enumerate(sequences):results[i, sequence] 1.return resultsx_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.compile(optimizerrmsprop,lossbinary_crossentropy,metrics[accuracy])
# 模型定义
model.add(layers.Dense(16, activationrelu, input_shape(10000,)))
model.add(layers.Dense(16, activationrelu))
model.add(layers.Dense(1, activationsigmoid))x_val x_train[:10000]
partial_x_train x_train[10000:]
y_val y_train[:10000]partial_y_train y_train[10000:]# 编译模型
model.compile(optimizerrmsprop,lossbinary_crossentropy,metrics[accuracy])# 仅作四次训练
model.fit(x_train, y_train, epochs4, batch_size512)
results model.evaluate(x_test, y_test)print(results, results)
print(model.predict(x_test):,model.predict(x_test))49/49 [] - 1s 7ms/step - loss: 0.4647 - accuracy: 0.8133
Epoch 2/4
49/49 [] - 0s 6ms/step - loss: 0.2754 - accuracy: 0.9021
Epoch 3/4
49/49 [] - 0s 6ms/step - loss: 0.2135 - accuracy: 0.9230
Epoch 4/4
49/49 [] - 0s 5ms/step - loss: 0.1821 - accuracy: 0.9348
782/782 [] - 1s 830us/step - loss: 0.3018 - accuracy: 0.8806
results [0.3018262982368469, 0.8806399703025818] 这种相当简单的方法得到了 88% 的精度。利用最先进的方法你应该能够得到接近 95% 的精度。 3.4.5 使用训练好的网络在新数据上生成预测结果 训练好网络之后你希望将其用于实践。你可以用 predict 方法来得到评论为正面的可能性大小。 print(model.predict(x_test):,model.predict(x_test)) model.predict(x_test): [[0.23568308][0.9997094 ][0.9133028 ]...[0.08991385][0.07172912][0.647347 ]] 如你所见网络对某些样本的结果非常确信大于等于 0.99 或小于等于 0.01但对其他结果却不那么确信 0.6 或 0.4 。 3.4.6 进一步的实验 通过以下实验你可以确信前面选择的网络架构是非常合理的虽然仍有改进的空间。 前面使用了两个隐藏层。你可以尝试使用一个或三个隐藏层然后观察对验证精度和测 试精度的影响。 # 模型定义
model.add(layers.Dense(16, activationrelu, input_shape(10000,)))
model.add(layers.Dense(16, activationrelu))
model.add(layers.Dense(16, activationrelu))
model.add(layers.Dense(1, activationsigmoid)) 49/49 [] - 1s 6ms/step - loss: 0.4853 - accuracy: 0.8112
Epoch 2/4
49/49 [] - 0s 6ms/step - loss: 0.2759 - accuracy: 0.9028
Epoch 3/4
49/49 [] - 0s 6ms/step - loss: 0.2136 - accuracy: 0.9216
Epoch 4/4
49/49 [] - 0s 5ms/step - loss: 0.1766 - accuracy: 0.9360
782/782 [] - 1s 825us/step - loss: 0.2884 - accuracy: 0.8860
results [0.2884432375431061, 0.8860399723052979] 可见损失下降精度增加 尝试使用更多或更少的隐藏单元比如 32 个、 64 个等。 # 模型定义
model.add(layers.Dense(32, activationrelu, input_shape(10000,)))
model.add(layers.Dense(16, activationrelu))
# model.add(layers.Dense(16, activationrelu))
model.add(layers.Dense(1, activationsigmoid)) results [0.3072710633277893, 0.877560019493103] 尝试使用 mse 损失函数代替 binary_crossentropy。 尝试使用 tanh 激活这种激活在神经网络早期非常流行代替 relu。 3.4.7 小结 下面是你应该从这个例子中学到的要点。 通常需要对原始数据进行大量预处理以便将其转换为张量输入到神经网络中。单词序列可以编码为二进制向量但也有其他编码方式。 带有 relu 激活的 Dense 层堆叠可以解决很多种问题包括情感分类你可能会经常用到这种模型。 对于二分类问题两个输出类别网络的最后一层应该是只有一个单元并使用 sigmoid激活的 Dense 层网络输出应该是 0~1 范围内的标量表示概率值。 对于二分类问题的 sigmoid 标量输出你应该使用 binary_crossentropy 损失函数。 无论你的问题是什么rmsprop 优化器通常都是足够好的选择。这一点你无须担心。 随着神经网络在训练数据上的表现越来越好模型最终会过拟合并在前所未见的数据上得到越来越差的结果。一定要一直监控模型在训练集之外的数据上的性能。 3.5 新闻分类多分类问题 上一节中我们介绍了如何用密集连接的神经网络将向量输入划分为两个互斥的类别。但如果类别不止两个要怎么做 本节你会构建一个网络将路透社新闻划分为 46 个互斥的主题 。因为有多个类别所以这是 多分类multiclass classification 问题的一个例子。 因为每个数据点只能划分到一个类别所以更具体地说这是 单标签、多分类 single-label, multiclass classification问题的一个例子。如果每个数据点可以划分到多个类别主题那它就是一个 多标签、多分类 multilabel,multiclass classification 问题。 3.5.1 路透社数据集 本节使用 路透社数据集 它包含许多短新闻及其对应的主题由路透社在 1986 年发布。它是一个简单的、广泛使用的文本分类数据集。它包括 46 个不同的主题某些主题的样本更多但训练集中每个主题都有至少 10 个样本。 与 IMDB 和 MNIST 类似路透社数据集也内置为 Keras 的一部分。我们来看一下。 # 路透社数据集,有 8982 个训练样本和 2246 个测试样本
(train_data, train_labels), (test_data, test_labels) reuters.load_data(num_words10000)print(len(train_data):, len(train_data))
print(len(test_data):, len(test_data)) len(train_data): 8982
len(test_data): 2246 与 IMDB 数据集一样参数 num_words10000 将数据限定为前 10 000 个最常出现的单词。 我们有 8982 个训练样本和 2246 个测试样本。 IMDB 评论一样每个样本都是一个整数列表表示单词索引。 print(train_data[10], train_data[10]) 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]