0%

《Python深度学习》笔记

《Python深度学习》笔记
Windows下的环境搭建
英文电子版https://livebook.manning.com/book/deep-learning-with-python/
Keras中文文档:https://keras.io/zh/

Chapter 1 基本概念

  • 首先理清人工智能、机器学习和深度学习的概念和关系,
    人工智能>机器学习>深度学习
    深度学习是机器学习的子集,而机器学习则是人工智能的子集

1.1 人工智能

简洁定义:将通常由人类完成的智力任务自动化。
发展符号主义人工智能(symbolic AI)→机器学习(machine learning)
符号主义人工智能:编写足够多的明确规则来处理知识,用于解决定义明确的逻辑问题,如下国际象棋,但对于图像分类、语音识别等难以给出明确规则的复杂、模糊的问题将无法解决。

1.2 机器学习

机器学习与经典程序设计区别

  • 机器学习将某个任务相关的示例输入机器学习系统,系统从中找到统计结构,最终找到规则将任务自动化,其与经典程序设计不同如上图。
  • 机器学习的三个要素如下:
  • (1)输入数据点。
  • (2)预期输出的示例。
  • (3)衡量算法效果好坏的方法。
  • 机器学习和深度学习的核心问题在于有意义地变换数据 ,即学习输入数据的有用表示(表征数据或将数据编码),如彩色图像可以编码为RGB(红-绿-蓝)格式或HSV(色相-饱和度-明度)格式,在应对不同任务时,不同的表示方式将会产生很大的差异,所以机器学习模型的目的就是为输入数据寻找合适的表示。
  • 定义:在预先定义好的可能性空间中(假设空间),利用反馈信号的指引来寻找输入数据的有用表示。

1.3 深度学习

  • 深度学习强调从连续的中进行学习,这些层对应越来越有意义的表示

  • 模型包含的层数成为模型的深度,这些分层总是通过神经网络(neural network)模型学习得到。

  • 定义:学习数据表示的多级方法,相当于多级信息蒸馏操作。

  • 深度学习工作原理

  • 神经网络每层对输入数据所做的具体操作保存在该层的权重(weight)中,其本质是一串数字,每层实现的变换由其权重来参数化(parameterize),学习即为每层找到一组权重值,使得该网络能够将每个示例输入与其目标正确地一一对应。

  • 输入网络预测值和真实目标值通过损失函数计算一个距离值,利用这个距离值作为反馈信号通过优化器实现反向传播算法来对权重值进行微调,以降低当前示例的损失值,随着示例的增多,损失值逐渐降低,输出值与目标值尽可能接近。如下图。
    深度学习工作原理图

  • 首先了解特征工程的概念,即手动提取出数据有用的表示,即手动为数据设计好的表示层。而深度学习可将这个步骤完全自动化,一次学习所有特征。

  • 深度学习的两个基本特征:第一,通过渐进的逐层的方式形成越来越复杂的表示;第二,对中间这些渐进的表示层同一时间共同进行学习,而不是依次连续学习(贪婪学习),每一层的变化都需要同时考虑上下两层的需要。

1.3 机器学习简史

1.3.1 概率建模

  • 概率建模(probabilistic modeling)是统计学原理在数据分析中的应用。
  • 两种分类方法:朴素贝叶斯算法和logistic回归(logistic regression,简称logreg)

1.3.2 早期神经网络

1.3.3 核方法

  • 核方法(kenel method):另一种机器学习方法,为一组分类算法,支持向量机(SVM,support vector machine)最为出名。
  • SVM通过找出两个不同类别的两组数据点间的良好决策边界(decision boundary)来解决分类问题。
  • SVM分两步来寻找决策边界:
    • 将数据映射到一个新的高维表示,此时决策边界可以用一个超平面来表示(二维数据超平面为一条直线)。
    • 间隔最大化(maximizing the margin):尽量让超平面与每个类别最接近的数据点之间的距离最大化,从而计算出良好决策边界。
  • 将数据映射到一个新的高维表示,需要用到核技巧,其基本思想为:利用核函数(人为选择)在新空间中计算点对之间的距离,从而找到良好的决策超平面。
  • SVM是一种浅层方法,在简单的分类问题上表现出良好的性能,但在大型数据集以及图像分类等感知问题上效果不好。

1.3.4 决策树、随机森林与梯度提升机

  • 决策树(decision tree)是类似于流程图的结构,可以对输入数据点进行分类或根据给定输入来预测输出值。
  • 随机森林(random forest)算法:构建许多决策树,然后将其输出集成在一起,适用于各种浅层学习的非感知类机器学习任务。
  • 梯度提升机(gradient boosting machine):使用梯度提升方法,通过迭代训练新模型来专门解决之前模型的弱点,从而改进任何机器学习模型的效果。

Chapter 2 神经网络的数学基础

2.1 张量(tensor)

  • 定义:又叫多维Numpy数组,作为机器学习的基本数据结构,是一个数据容器,包含的数据几乎总是数值数据,如矩阵是二维张量,张量是矩阵向任意维度的推广[ 张量的维度(dimension)通常叫做(axis)]

2.2 标量(scalar)

  • 定义:仅包含一个数字的张量,又叫标量张量、零维张量、0D张量。在Numpy数组中,一个float32或float64的数字就是一个标量。
  • 标量张量有0个轴(ndim == 0),轴的个数又叫(rank)。如下代码查看一个标量张量的轴的个数:
    import numpy as np

    x = np.array(12)
    print(x.ndim) #轴的个数

    output:
    0

2.3 向量(vector)

  • 定义:数字组成的数组,又叫一维张量或1D张量,只有一个轴,如下:
    import numpy as np

    x = np.array([12, 3, 22, 121, 4])
    print(x.ndim)

    output:
    1
    该向量有5个元素,为5D向量,有1个轴,沿着轴有5个维度。而5D张量有5个轴,沿着某个轴可能有任意个维度。

2.4 矩阵(matrix)

定义:多个向量组成的数组,又叫二维张量或2D张量,有2个轴,如下:

import numpy as np

x = np.array([[12, 3, 22, 121, 4],
[12, 3, 22, 121, 4],
[12, 3, 22, 121, 4]])
print(x.ndim)

output:
2

第一个轴上的元素叫行,[12, 3, 22, 121, 4]是x的第一行,第二个轴上的元素叫列,[12, 12, 12]是x的第一列。

2.5 3D张量与更高维张量

定义:多个矩阵组成的数组,有3个轴,如下:

import numpy as np

x = np.array([[[12, 3, 22, 121, 4],
[12, 3, 22, 121, 4],
[12, 3, 22, 121, 4]],
[[12, 3, 22, 121, 4],
[12, 3, 22, 121, 4],
[12, 3, 22, 121, 4]]])
print(x.ndim)

output:
3

深度学习一般处理0D到4D张量,处理视频数据可能会遇到5D张量。

2.6 张量的关键属性

  • 轴的个数(阶、维度):Python库中为ndim。
  • 形状:张量沿某个轴的维度大小(元素个数),如前面的3D张量的形状为(3, 3, 5)。
  • 数据类型:张量所包含数据的类型,Python库中为dtype,如float32、float64、unit8等,极少数情况有字符(char)张量,Numpy等大多数库都不存在字符串张量,因为张量存储在预先分配的连续内存段中,而字符串长度可变,无法用这种方式存储。

代码示例:

from keras.datasets import mnist
import matplotlib.pyplot as plt

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
print(train_images.shape) #形状
print(train_images.ndim) #轴的个数
print(train_images.dtype) #数据类型

#该3D张量中的第四个数字
digit = train_images[4]
plt.imshow(digit, cmap=plt.cm.binary)
plt.show()

output:
(60000, 28, 28)
3
uint8

#由上可见,train_images是一个由8位整数组成的3维张量,即60000个28*28整数矩阵组成的数组,
#每个矩阵是一张灰度图像。

该3D张量中的第四个数字

2.7 张量切片

类似于Python的切片操作,可用于裁剪图像,示例:

#选择第10~100个数字(不包括100),将其放在形为(90,28,28)的数组中
my_slice = train_images[10:100]
my_slice = train_images[10:100, 0:28, 0:28] #两者等价

#可沿着张量轴在任意两个索引间进行选择,也可使用负索引
my_slice = train_images[, 14:, 14:] #右下角14像素*14像素
my_slice = train_images[, 7:-7, 7:-7] #中心14像素*14像素

2.8 数据批量

  • 所有数据张量的第一个轴称为样本轴0轴(samples axis)。
  • 深度学习模型不会同时处理整个数据集,而是会将数据集拆分成小批量,形如batch = train_images[:128],批量大小为128。
  • 对于这种批量张量,第一个轴为批量轴(batch axis)或批量维度(batch dimension)。

2.9 现实世界中的数据张量

  • 向量数据: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)

2.9.1 向量数据

  • 常见的数据。每个数据点被编码为一个向量,因此一个数据批量就被编码为 2D 张量(即向量组成的数组),其中第一个轴是样本轴,第二个轴是特征轴 (samples, features)。如
  • 人口统计数据集,其中包括每个人的年龄、邮编和收入。每个人可以表示为包含 3 个值的向量,而整个数据集包含 100 000 个人,因此可以存储在形状为 (100000, 3) 的 2D张量中。

2.9.2 时间序列数据或序列数据

  • 当时间对于数据很重要时,将数据存储在带有时间轴的3D张量中。每个样本被编码为一个向量序列(即2D张量),因此一个数据批量就被编码为一个3D张量 (samples, timesteps, features)。如图:
  • 依据惯例,时间轴始终是第二个轴。
  • 例如股票价格数据集:
    要构建一个股票价格数据集:每一分钟,我们将股票的当前价格,前一分钟的最高价格和前一分钟的最低价格保存下来,那么就被编码为一个3D向量。整个交易日就编码为一个有390个3D向量的2D张量(390,3)。250天的数据就编码为3D张量(250,390,3)。

2.9.3 图像数据

  • 图像通常具有三个维度:高度宽度颜色深度。虽然灰度图像(比如 MNIST 数字图像)只有一个颜色通道,因此可以保存在 2D 张量中,但按照惯例,图像张量始终都是 3D 张量,灰度图像的彩色通道只有一维。因此,如果图像大小为 256×256,那么 128 张灰度图像组成的批量可以保存在一个形状为 (128, 256, 256, 1) 的张量中,而 128 张彩色图像组成的批量则可以保存在一个形状为 (128, 256, 256, 3) 的张量中。
  • 图像张量的形状有两种约定:通道在后(channels-last)的约定(在 TensorFlow 中使用)和通道在前(channels-first)的约定(在 Theano 中使用)。
  • Google 的 TensorFlow 机器学习框架将颜色深度轴放在最后: (samples, height, width, color_depth),例如(128, 256, 256, 3) 。
  • Theano将图像深度轴放在批量轴之后:(samples, color_depth, height, width),例如(128, 3, 256, 256)。
  • Keras 框架同时支持这两种格式。

2.9.4 视频数据

  • 视频数据是现实生活中需要用到 5D 张量的少数数据类型之一。视频可以看作一系列帧,每一帧都是一张彩色图像。由于每一帧都可以保存在一个形状为 (height, width, color_depth) 的 3D 张量中,因此一系列帧可以保存在一个形状为 (frames, height, width,color_depth) 的 4D 张量中,而不同视频组成的批量则可以保存在一个 5D 张量中,其形状为(samples, frames, height, width, color_depth)
  • 例如,一个以每秒 4 帧采样的 60 秒 YouTube 视频片段,视频尺寸为 144×256,这个视频共有 240 帧。4 个这样的视频片段组成的批量将保存在形状为 (4, 240, 144, 256, 3)的张量中。总共有 106 168 320 个值!如果张量的数据类型( dtype )是 float32 ,每个值都是32 位,那么这个张量共有 405MB。现实生活中遇到的视频要小得多,因为它们不以float32 格式存储,而且通常被大大压缩,比如 MPEG 格式。

2.10 张量运算——神经网络的“齿轮”

本节和下一节主要是高等数学和线性代数在神经网络中的运用,下面只简单提一下这些数学知识在神经网络中的相关概念。

2.10.1 逐元素运算(element-wise)

  • 运算独立地应用于张量中的每个元素,适合大规模并行实现。如:
  • relu运算:relu(x) == max(x, 0)
  • 四则运算(前提是运算对象形状相同)
  • 在Numpy中可直接进行逐元素运算,由Numpy内置函数交给基础线性代数子程序(BLAS,basic linear algebra subprograms)运算。
    import numpy as np

    z = x + y
    z = np.maximum(x, 0.)

2.10.2 广播(broadcast)

  • 两个形状不同地张量相加,较小地张量会被广播,以匹配较大的张量。如:
  • x形状为(32,10),y形状为(10,),则x+y会为y添加空的第一个轴(广播轴)→(1,10),再沿新轴重复32次→(32,10)。
  • 但以上过程不会在运算中实际发生,只是想象的思维模型。

2.10.3 张量点积(tensor product)

  • 类似于线性代数中矩阵的乘法,Keras和Numpy中使用numpy.dot(x, y)实现。
  • 形如(a, b, c, d).(d, e)->(a, b, c, e),

2.10.4 张量变形(tensor reshaping)

  • 改变张量的行列,但元素总数不变。x.reshape((2,6))
  • 行列互换称为转置(transposition)numpy.transpose(x)

2.10.5 张量运算的几何解释

  • 类似高数中的向量变换以及更高维的延申。
  • 机器学习的内容就是,为复杂的、高度折叠的数据流形式找到简洁地表示。

2.11 神经网络的“引擎”——基于梯度的优化

  • 对每个神经层用下述方法对输入数据进行变换。

    output = relu(dot(W, input) + b)
    • 在这个表达式中,W 和 b 都是张量,均为该层的属性。它们被称为该层的权重(weight)或可训练参数(trainable parameter),分别对应 kernel 和 bias 属性。这些权重包含网络从观察训练数据中学到的信息。
    • 一开始,这些权重矩阵取较小的随机值,这一步叫作随机初始化(random initialization)。当然,W 和 b 都是随机的,relu(dot(W, input) + b) 肯定不会得到任何有用的表示。虽然得到的表示是没有意义的,但这是一个起点。下一步则是根据反馈信号逐渐调节这些权重。这个逐渐调节的过程叫作训练,也就是机器学习中的学习
    • 上述过程发生在一个训练循环(training loop)内,其具体过程如下。必要时一直重复这些步骤。
      • 抽取训练样本x 和对应目标y 组成的数据批量。
      • 在x 上运行网络[这一步叫作前向传播(forward pass)],得到预测值y_pred。
      • 计算网络在这批数据上的损失,用于衡量y_pred 和y 之间的距离。
      • 更新网络的所有权重,使网络在这批数据上的损失略微下降。
    • 最终得到的网络在训练数据上的损失非常小,即预测值y_pred 和预期目标y 之间的距离非常小。

详情参见:https://www.ituring.com.cn/book/tupubarticle/23177

Chapter 3 神经网络入门

  • 这里再次引用第一章的深度学习工作原理图。
    深度学习工作原理图

3.1 层:深度学习的基础组件

  • 是神经网络的基本数据结构,层的状态即层的权重,权重是利用随机梯度下降学到的一个或多个张量。以下是几种不同的层及其应用场景。
  • 密集连接层(densely connected layer):又叫全连接层(fully connected layer)和密集层(dense layer),用于处理保存简单向量的2D张量,形状为 (samples, features),对应Keras的Dense类。
  • 循环层(recurrent layer):用于处理保存序列数据的3D张量,形状为 (samples, timesteps, features),对应Keras的LSTM层。
  • 二维卷积层:用于处理保存图像数据的4D张量,形状为 (samples, height, width, channels),对应Keras的Conv2D。

3.2 模型:层构成的网络

  • 构建深度学习模型就是将相互兼容的多个层拼接在一起,以建立有用的数据变换流程。深度学习模型是层构成的有向无环图。
  • 这里的层兼容性(layer compatibility)指每一层只接受特定形状的输入张量并返回特定形状的输出张量。
  • 一些常见的网络拓扑结构如下:
  • (1)线性网络
  • (2)双分支(two-branch)网络
  • (3)多头(multihead)网络
  • (4)Inception模块

3.3 损失函数与优化器:配置学习过程的关键

  • 损失函数(目标函数):训练过程中将其最小化,能够衡量当前任务是否已成功完成。

  • 优化器:决定如何根据损失函数对网络进行更新,执行随机梯度下降(SGD: stochastic gradient descent)的某个变体。

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

3.4 Keras开发流程

  • 定义训练数据:输入张量和目标张量。
  • 定义层组成的网络(或模型),将输入映射到目标。
  • 配置学习过程:选择损失函数,优化器和需要监控的指标。
  • 调用模型的fit()方法迭代训练数据。
from keras import models,layers
from keras import optimizers

#使用Sequential类定义模型
# model = models.Sequential()
# model.add(layers.Dense(32, activation='relu', input_shape=(784,)))
# model.add(layers.Dense(10, activation='softmax'))

#函数式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)

#编译
model.compile(optimizer=optimizers.RMSprop(lr=0.001), #优化器
loss='mse', #损失函数
metrics=['accuracy']) #评估函数

#迭代训练数据
model.fit(input_tensor, target_tensor, batch_size=128, epochs=10)

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

注意:

  • 隐藏单元越多(更高维的表示空间),网络越能学习更复杂的表示,但这会使网络的计算代价更大,并且可能导致学习到不好的模式(这种模式可以提高训练数据的性能,但不能提高测试数据的性能)。
  • sigmoid函数将任意值压缩到[0, 1]区间内。
  • relu(rectified linear unit,整流线性单元)函数,将所有负值归零。
  • 激活函数:也叫非线性,为了得到更丰富的假设空间,充分利用多层表示的优势。
  • 对于二分类这种最后输出概率值的问题,损失函数最优解为binary_crossentropy(二元交叉熵)。
#二分类问题
from keras.datasets import imdb
from keras import models, layers
import matplotlib.pyplot as plt
import numpy as np

#Step 1 加载数据集

#train_data和train_labels为评论组成的列表,而每条评论为单词索引组成的列表
#test_data和test_labels为0、1组成的列表,代表负面和正面
#num_words=10000表示仅保留训练数据中前10000个最常出现的单词
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)

# word_index = imdb.get_word_index() #将单词映射为整数索引的字典
# reverse_word_index = dict( #键值颠倒,整数索引映射为单词
# [(value, key) for (key, value) in word_index.items()])
# decoded_review = ' '.join( #评论解码,索引减去3,
# [reverse_word_index.get(i - 3, '?') for i in train_data[0]]) #因为0、1、2为保留索引

#Step 2 处理数据

#对列表进行one-hot编码,转化为0、1组成的向量。
#将整数序列编码为二进制矩阵
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) #测试数据向量化
y_train = np.asarray(train_labels).astype('float32') #训练标签向量化
y_test = np.asarray(test_labels).astype('float32') #测试标签向量化

#留出10000个样本作验证集
x_val = x_train[:10000]
partial_x_train = x_train[10000:]
y_val = y_train[:10000]
partial_y_train = y_train[10000:]

#Step 3 定义模型/构建网络

#采用3层全连接层,16为隐藏单元个数,即维度,activation为激活函数
model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, activation='relu')) #relu(rectified linear unit,整流线性单元)函数,将所有负值归零。
model.add(layers.Dense(1, activation='sigmoid')) #sigmoid函数将任意值压缩到[0, 1]区间内,输出一个0,1范围内的概率值

#Step 4 编译模型

#配置优化器,损失函数,评估函数
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['acc'])

#Step 5 训练模型

#使用512个样本组成的小批量,对所有样本进行20次迭代,
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']

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

plt.plot(epochs, loss_values, 'bo', label='Training loss')
plt.plot(epochs, val_loss_values, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.show()

plt.clf()
acc_values = history_dict['acc']
val_acc_values = history_dict['val_acc']

plt.plot(epochs, acc_values, 'bo', label='Training acc')
plt.plot(epochs, val_acc_values, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.show()
训练损失和验证损失训练精度和验证精度
  • 由上图可见,每轮训练损失在降低,训练精度在上升,符合预期,但验证损失和验证精度并非如此,模型在训练数据上表现更好,但不一定在从未见过的数据上表现更好,这种现象成为过拟合(overfit),详见Chapter 4。
  • 下面是一种简单的训练方法:
    model.fit(x_train, y_train, epochs=4, batch_size=512)
    results = model.evaluate(x_test, y_test)

    >>> results
    [0.29506705965518953, 0.884119987487793] #返回loss和acc
  • 用训练好的网络进行预测,得到评论为正面的可能性大小。
    >>> model.predict(x_test)
    array([[ 0.98006207]
    [ 0.99758697]
    [ 0.99975556]
    ...,
    [ 0.82167041]
    [ 0.02885115]
    [ 0.65371346]], dtype=float32)

3.6 新闻分类:多分类问题

  • 单标签、多分类(single-label,multiclass classification):每个数据点只能划分到一个类别。
  • 多标签、多分类(multilabel,multiclass classification):每个数据点能划分到多个类别。

注意:

  • 编码数据中将标签向量化的两种方法:转化为整数张量或进行one-hot编码。注意两种方法所使用的损失函数可能会有所差别。
    • 通过分类编码(也称为one-hot编码),使用categorical_crossentropy作为损失函数对标签进行编码。
    • 将标签编码为整数,使用sparse_categorical_crossentropy作为损失函数对标签进行编码。
  • 如果要在N个类别中对数据点进行分类,则网络最后一层应为大小为N的Dense层。
  • 在单标签,多类分类问题中,网络应以softmax激活结束,这样可以输出在N个类别上的概率分布。
  • 分类交叉熵几乎总是针对多分类问题使用的损失函数,它使网络输出的概率分布与目标的真实分布之间的距离最小化。
  • 如果需要将数据划分为大量类别,则应避免使用较小的中间层,而在网络中导致信息瓶颈(永久地丢失信息)。

本例使用路透社数据集。

#多分类问题
from keras.datasets import reuters
from keras import models, layers
from keras.utils.np_utils import to_categorical
import matplotlib.pyplot as plt
import numpy as np

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


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_train_labels = to_categorical(train_labels)
one_hot_test_labels = to_categorical(test_labels)

#将标签转化为整数张量,对应需改变损失函数为sparse_categorical_crossentropy
# y_train = np.array(train_labels)
# y_test = np.array(test_labels)

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:]

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'))
#单标签,多类分类问题中,网络应以softmax激活结束,这样可以输出在N个类别上的概率分布。

model.compile(optimizer='rmsprop',
loss='categorical_crossentropy', #分类交叉熵
metrics=['acc'])

history = model.fit(partial_x_train,
partial_y_train,
epochs=20,
batch_size=512,
validation_data=(x_val, y_val))

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

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()
训练损失和验证损失训练精度和验证精度

由上图可见,网络在训练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)

output:
[0.9783820241875449, 0.7925200462341309] #返回loss和acc

在新数据上进行评估。

predictions = model.predict(x_test)
print(predictions[0].shape) #(46,)
print(np.sum(predictions[0])) #1.0
print(np.argmax(predictions[0])) #概率最大的类别

word_index = reuters.get_word_index() #将单词映射为整数索引的字典
reverse_word_index = dict( #键值颠倒,整数索引映射为单词
[(value, key) for (key, value) in word_index.items()])
decoded_data = ' '.join( #评论解码,索引减去3,
[reverse_word_index.get(i - 3, '?') for i in test_data[0]]) #因为0、1、2为保留索引
print(decoded_data)
print(test_labels[0])

3.7 预测房价:回归问题

前面两种分类问题目标是预测输入数据点所对应的单一离散的标签,而回归问题预测一个连续值而不是离散的标签,如根据气象数据预测明天的气温。
本例采用波士顿房价数据集,每个样本有多个数据特征,如犯罪率、每个住宅的平均房间数等。目标是房屋价格的中位数,单位千美元。
注意:

  • 本例的每个特征几乎都有不同的取值范围,应当对每个特征值做标准化,如减去特征平均值再除以标准差。
  • 回归问题常用的损失函数为均方误差(MSE,mean squared error),预测值与目标值之差的平方。
  • 回归问题使用的评估指标(metrics)为平均绝对误差(MAE,mean absolute error)预测值与目标值之差的绝对值。
  • 若可用数据很少,使用K折验证可以可靠地评估模型。
  • 若可用的训练数据很少,最好使用隐藏层较少(1到2个)的网络,避免严重的过拟合
#回归问题
from keras.datasets import boston_housing
from keras import models, layers
import matplotlib.pyplot as plt
import numpy as np

(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

mean1 = test_data.mean(axis=0)
test_data -= mean1
std1 = test_data.std(axis=0)
test_data /= std1

#因为要将同一个模型多次实例化,故用一个函数来构建模型
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

#K折验证
k = 4
num_val_samples = len(train_data) // k
# num_epochs = 100
# all_scores = []
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()
# 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)
# mean_scores = np.mean(all_scores)

history = model.fit(partial_train_data, partial_train_targets,
validation_data=(val_data, val_targets),
epochs=num_epochs, batch_size=1, verbose=0)
print(history.history.keys())
mae_history = history.history['val_mae']
all_mae_histories.append(mae_history)

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

#将每个数据点替换为前面数据点的指数移动平均值,以得到光滑曲线。
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.clf()
plt.plot(range(1, len(smooth_mae_history) + 1), smooth_mae_history)
plt.xlabel('Epochs')
plt.ylabel('Validation MAE')
plt.show()
每轮的验证MAE重绘后的每轮的验证MAE

由上图可知,验证MAE在80轮后不再显著降低,之后开始过拟合,调整参数在训练数据上训练参数。

model = build_model()
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)

>>> test_mae_score
2.6759588718414307

Chapter 4 机器学习基础

4.1 机器学习的四个分支

4.1.1 监督学习

最常见的机器学习类型,给定一组样本(通常由人工标注),学习将输入数据映射到已知目标。主要包括分类和回归问题,如光学字符识别、语音识别、图像分类以及语言翻译。另外包括一些主要的变体如下:

  • 分类:对数据进行分类 。
  • 回归:对数据进行拟合 。
  • 序列生成:给定一张图像,预测描述图像的文字。
  • 语法树预测:给定一个句子,预测其分解生成的语法树 。
  • 目标检测:给定一张图像,在图中特定目标的周围画一个边界框。可表示为分类(给定多个边界框,对框内目标进行分类)或分类与回归联合问题(用向量回归来预测边界框的坐标)。
  • 图像分割:给定一张图像,在特定物体上画一个像素级的掩模(mask)。

4.1.2 无监督学习

无监督学习是指在没有目标的情况下寻找输入数据的有趣变换,其目的在于数据可视化、数据压缩、数据去噪或更好地理解数据中的相关性。
具体的方法:

  • 降维(dimensionality reduction)
  • 聚类(clustering)

4.1.3 自监督学习

自监督学习是没有人工标注的标签的监督学习,可以将它看作没有人工参与的监督学习。
主要的例子:

  • 自编码器:其生成的目标就是未经修改的输入。
  • 给定视频中过去的帧来预测下一帧,或者给定文本中前面的词来预测下一个词。

上述两个例子也属于时序监督学习:用未来的输入数据作为监督。

4.1.4 强化学习

4.2 机器学习通用工作流程

4.2.1 定义问题,收集数据集

4.2.2 选择衡量成功的指标

4.2.3 确定评估方法

4.2.4 准备数据

4.2.5 优化模型

4.2.6 模型正则化与调节超参数

Chapter 5 卷积神经网络

一个简单的卷积神经网络示例。

#卷积神经网络
from keras import models, layers
from keras.datasets import mnist
from keras.utils import to_categorical

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
train_images = train_images.reshape((60000, 28, 28, 1))
train_images = train_images.astype('float32') / 255

test_images = test_images.reshape((10000, 28, 28, 1))
test_images = test_images.astype('float32') / 255

train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))

# model.summary()

model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(train_images, train_labels, epochs=5, batch_size=64)

test_loss, test_acc = model.evaluate(test_images, test_labels)

>>> test_acc
0.9919000267982483

Windows下的环境搭建

由于懒得装双系统,虚拟机先不说配置不够,搭起来感觉坑很多,遂直接在Windows系统下搭建。

安装CUDA和cuDNN

CUDA(Compute Unified Device Architecture):NVIDIA用于自家GPU的并行计算框架,本质是一个工具包(ToolKit)。

cuDNN(CUDA Deep Neural Network library):是NVIDIA打造的针对深度神经网络的加速库,是一个用于深层神经网络的GPU加速库。用GPU训练模型,cuDNN不是必须的,但是一般会采用这个加速库。

  1. 查看显卡支持的CUDA版本
    NVIDIA控制面板>帮助>系统信息>组件,如图支持10.2版本

  2. 安装对应版本的CUDA

    下载地址:https://developer.nvidia.com/cuda-downloads

    安装完成后打开cmd,输入命令:nvcc -V,如图

  3. 编译CUDA(暂时未编译)

  4. 安装cuDNN

    下载地址:https://developer.nvidia.com/cudnn

    这里需要注册账户才可以下载,可以看到支持的对应CUDA版本
    将解压后的文件复制到CUDA安装路径中,这里是默认路径
    C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1

安装TensorFlow和Keras

由于本机已经安装过Python3.7,管理多版本Python有点麻烦,故直接使用pip安装而不是Anaconda
在用pip安装前,确保pypi源更换为国内镜像,否则速度超级慢

方法为在C盘的user目录下新建一个pip文件夹,如:C:\Users\xx\pip,在pip文件夹内新建一个pip.ini文件,内容如下:

[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
[install]
trusted-host = https://pypi.tuna.tsinghua.edu.cn

常用国内镜像:

https://pypi.tuna.tsinghua.edu.cn/simple/   # 清华大学
https://mirrors.aliyun.com/pypi/simple/ # 阿里云
https://pypi.douban.com/simple/ # 豆瓣
https://pypi.mirrors.ustc.edu.cn/simple/ # 中国科学技术大学
https://pypi.hustunique.com/ # 华中科技大学

  1. 安装Tensorflow
    pip install tensorflow==2.1.0
    pip install tensorflow-gpu==2.1.0
    安装完成后在python环境中运行
    import tensorflow as tf
    tf.test.is_gpu_available()
    可以查看到GPU信息即安装成功。
    Tensorflow作为Keras的后端,TensorFlow与CUDA和cuDNN各版本对应如下:
  2. 安装Keras
    pip install keras

安装其他组件

matplotlib

python -m pip install matplotlib