六、深度强化学习
最近一段时间,深度学习一直是机器学习(ML)界的时髦词汇。迄今为止,深度学习算法的主要目标是使用 ML 来实现人工通用智能(AGI)(即,在机器中复制人类水平的智能,以解决给定领域的任何问题)。深度学习在计算机视觉、音频处理和文本挖掘方面显示出了有前途的成果。这一领域的进步带来了突破,如自动驾驶汽车。在这一章中,你将了解深度学习的核心概念、进化(感知机到卷积神经网络[CNN])、关键应用和实现。
在过去几年中,已经建立了许多强大而流行的开源库,主要集中在深度学习上(表 6-1 )。
表 6-1
热门深度学习库(截至 2019 年底)
|库名
|
发布年份
|
许可证
|
贡献者数量
|
官方网站
|
| — | — | — | — | — |
| 提亚诺 | Two thousand and ten | 加州大学伯克利分校软件(Berkeley Software Distribution) | Three hundred and thirty-three | http://deeplearning.net/software/theano/
|
| Pylearn2 | Two thousand and eleven | BSD-3 条款 | One hundred and fifteen | http://deeplearning.net/software/pylearn2/
|
| TensorFlow | Two thousand and fifteen | 阿帕奇-2.0 | One thousand nine hundred and sixty-three | http://tensorflow.org
|
| PyTorch | Two thousand and sixteen | 加州大学伯克利分校软件(Berkeley Software Distribution) | One thousand and twenty-three | https://pytorch.org/
|
| 硬 | Two thousand and fifteen | 用它 | Seven hundred and ninety-two | https://keras.io/
|
| mxnet 系统 | Two thousand and fifteen | 阿帕奇-2.0 | Six hundred and eighty-four | http://mxnet.io/
|
| 框架 | Two thousand and fifteen | BSD-2 条款 | Two hundred and sixty-six | http://caffe.berkeleyvision.org/
|
| 千层面 | Two thousand and fifteen | 用它 | Sixty-five | http://lasagne.readthedocs.org/
|
以下是每个库的简短描述(来自表 6-1 )。他们的官方网站提供高质量的文档和例子。如果需要的话,我强烈建议你在完成这一章后访问相应的网站以了解更多信息。
-
这是一个 Python 库,主要由蒙特利尔大学的学者开发。Theano 允许您高效地定义、优化和评估涉及复杂多维数组的数学表达式。它旨在与 GPU 一起工作,并执行有效的符号微分。它快速而稳定,具有广泛的单元测试。
-
TensorFlow :根据官方文档,它是一个使用可扩展 ML 的数据流图进行数值计算的库,由 Google 研究人员开发。它目前正被谷歌产品用于研究和生产。它于 2015 年开源,并在 ML 世界中获得了广泛的欢迎。
-
基于 Theano 的 ML 库,这意味着用户可以使用数学表达式编写新的模型/算法,Theano 将优化、稳定和编译这些表达式。
-
PyTorch:这是一个开源的深度学习平台,提供了从研究原型到生产部署的无缝路径。它具有混合前端、分布式培训的关键特性,允许使用流行的 Python 库,丰富的工具/库生态系统扩展了 PyTorch。
-
Keras :它是一个高级神经网络库,用 Python 编写,可以在 TensorFlow 或 Theano 上运行。它是一个接口,而不是一个端到端的 ML 框架。它是用 Python 编写的,入门简单,高度模块化,简单但足够深入,可以扩展以构建/支持复杂的模型。
-
MXNet :它是由来自 CMU、NYU、新加坡国立大学和麻省理工学院的研究人员合作开发的。它是一个轻量级的、可移植的、灵活的、分布式/移动库,支持多种语言,如 Python、R、Julia、Scala、Go、Javascript 等。
-
Caffe :是伯克利视觉与学习中心用 C++编写的深度学习框架,具有 Python/Matlab 构建能力。
-
Lasagne :它是一个轻量级的库,用于在 Theano 中构建和训练神经网络。
在这一章中,Scikit-learn 和 Keras 库(后端为 TensorFlow 或 Theano)被恰当地使用,因为它们是初学者掌握概念的最佳选择。此外,这些是最广泛使用的 ML 从业者。
注意
关于如何使用 TensorFlow 或 Theano 设置 Keras,已经有足够多的好材料,所以这里不再赘述。另外,记得安装“graphviz”和“pydot-ng”包来支持神经网络的图形视图。本章中的 Keras 代码是在 Linux 平台上构建的;但是,如果正确安装了支持包,它们应该可以在其他平台上正常工作,无需任何修改。
人工神经网络
在进入深度学习的细节之前,我认为简单了解一下人类视觉是如何工作的非常重要。人脑是一个复杂的、相互连接的神经网络,大脑的不同区域负责不同的工作;这些区域是大脑的机器,接收信号并处理它们以采取必要的行动。图 6-1 显示了人脑的视觉通路。
图 6-1
视觉通路
我们的大脑是由一簇称为神经元的小连接单元组成的,神经元相互发送电信号。长期知识由神经元之间的连接强度来表示。当我们看到物体时,光穿过视网膜,视觉信息被转换成电信号。此外,电信号在几毫秒内穿过大脑内不同区域的连接神经元的层级,以解码信号/信息。
当计算机看一幅图像时,背后发生了什么?
在计算机中,图像被表示为一个大型的三维数字阵列。例如,考虑图 6-2 :它是 28 ×
28 ×
1(宽度×
高度×
深度)大小的手写灰度数字图像,产生 784 个数据点。数组中的每个数字都是从 0(黑色)到 255(白色)的整数。在一个典型的分类问题中,模型必须将这个大矩阵转换成单个标签。对于一幅彩色图像,它还有三个颜色通道——每个像素有红、绿、蓝(RGB)——因此同一幅彩色图像的大小为 28×
28×
3 = 2352 个数据点。
图 6-2
手写数字(零)图像和相应的数组
为什么没有一个简单的图像分类模型?
图像分类对计算机来说可能是具有挑战性的,因为存在与图像的表示相关联的各种挑战。如果没有大量的功能工程工作,简单的分类模型可能无法解决大多数问题。让我们了解一些关键问题(表 6-2 )。
表 6-2
图像数据中的视觉挑战
|描述
|
例子
|
| — | — |
| 视点变化 :同一物体可以有不同的朝向。 | |
| 比例和光照变化 :物体大小的变化和像素级的光照水平可以变化。 | |
| 变形/扭曲和组内变化 :非刚体可以以很大的方式变形,在一个类中可以有不同类型的具有不同外观的对象。 | |
| 堵塞 :可能只有一小部分感兴趣的对象可见。 | |
| 背景杂乱 :物体能融入其所处的环境,会使其难以辨认。 | |
感知器——单一人工神经元
受生物神经元的启发,麦卡洛克和皮茨在 1943 年引入了感知机作为人工神经元的概念,这是人工神经网络的基本组成部分。它们不仅以其生物对应体命名,还模仿了我们大脑中神经元的行为(图 6-3 )。
图 6-3
生物与人工神经元
生物神经元有树突接收信号,细胞体处理信号,轴突/轴突末端将信号传递给其他神经元。类似地,人工神经元具有多个输入通道以接受表示为向量的训练样本,以及一个处理级,其中权重(w)被调整以使输出误差(实际与预测)最小化。然后,将结果输入激活函数,以产生输出,例如分类标签。分类问题的激活函数是阈值截止值(标准为 0.5),高于该阈值,分类为 1,否则为 0。让我们看看如何使用 Scikit-learn 实现这一点(清单 6-1 )。
# import sklearn.linear_model.perceptron
from sklearn.linear_model import perceptron
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
# Let's use sklearn to make_classification function to create some test data.
from sklearn.datasets import make_classification
X, y = make_classification(20, 2, 2, 0, weights=[.5, .5], random_state=2017)
# Create the model
clf = perceptron.Perceptron(n_iter=100, verbose=0, random_state=2017, fit_intercept=True, eta0=0.002)
clf.fit(X,y)
# Print the results
print ("Prediction: " + str(clf.predict(X)))
print ("Actual: " + str(y))
print ("Accuracy: " + str(clf.score(X, y)*100) + "%")
# Output the values
print ("X1 Coefficient: " + str(clf.coef_[0,0]))
print ("X2 Coefficient: " + str(clf.coef_[0,1]))
print ("Intercept: " + str(clf.intercept_))
# Plot the decision boundary using custom function 'plot_decision_regions'
plot_decision_regions(X, y, classifier=clf)
plt.title('Perceptron Model Decision Boundry')
plt.xlabel('X1')
plt.ylabel('X2')
plt.legend(loc='upper left')
plt.show()
#----output----
Prediction: [1 1 1 0 0 0 0 1 0 1 1 0 0 1 0 1 0 0 1 1]
Actual: [1 1 1 0 0 0 0 1 0 1 1 0 0 1 0 1 0 0 1 1]
Accuracy: 100.0%
X1 Coefficient: 0.00575308754305
X2 Coefficient: 0.00107517941422
Intercept: [-0.002]
Listing 6-1Example Code for Sklearn Perceptron
注意
单一感知器方法的缺点是它只能学习线性可分函数。
多层感知器(前馈神经网络)
为了解决单一感知器的缺点,提出了多层感知器,通常也称为前馈神经网络。它是多个感知器的组合,以不同的方式连接,并对不同的激活功能进行操作,以实现改进的学习机制。训练样本通过网络向前传播,输出误差向后传播;使用梯度下降法将误差降至最低,该方法将计算网络中所有权重的损失函数(图 6-4 )。
图 6-4
多层感知器表示
多层感知器的简单一级隐藏层的激活函数可以由下式给出:
),其中 x i 为输入,
)为输入层权重,
)为隐藏层权重。
多层神经网络可以有许多隐藏层,其中网络保存训练样本的内部抽象表示。上层将在前几层的基础上构建新的抽象。因此,复杂数据集有更多的隐藏层将有助于神经网络更好地学习。
从图 6-4 中可以看出,MLP(多层感知器)架构至少有三层:输入层、隐藏层和输出层。输入图层的神经元数量将等于要素的总数,在某些库中,还会有一个额外的神经元用于截取/偏移。这些神经元被表示为节点。输出层将具有用于回归模型和二元分类器的单个神经元;否则,它将等于多类分类模型的类标签总数。
请注意,对复杂数据集使用太少的神经元会导致模型不合适,因为它可能无法学习复杂数据中的模式。然而,使用太多的神经元会导致模型过度拟合,因为它有能力捕获可能是噪声或特定于给定训练数据集的模式。因此,为了建立一个有效的多层神经网络,需要回答的关于隐含层的基本问题是:1)隐含层的理想数量是多少?2)隐层神经元的数量应该是多少?
被广泛接受的经验法则是,你可以从一个隐藏层开始,因为有一种理论认为一个隐藏层对于大多数问题来说是足够的。然后在试错的基础上逐渐增加层数,看看精度有没有提高。理想情况下,隐藏层中神经元的数量可以是输入层和输出层中神经元的平均值。
让我们看看 Scikit-learn 库中的 MLP 算法在分类问题上的应用。我们将使用作为 Scikit-learn 数据集一部分的 digits 数据集,该数据集由 1,797 个样本(MNIST 数据集的子集)组成——手写灰度 digits 8×
8 图像。
加载 MNIST 数据
清单 6-2 提供了为训练 MLPClassifier 加载 MNIST 数据的示例代码。MNIST 数字数据是 sklearn 数据集的一部分。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import confusion_matrix
from sklearn.datasets import load_digits
np.random.seed(seed=2017)
# load data
digits = load_digits()
print('We have %d samples'%len(digits.target))
## plot the first 32 samples
to get a sense of the data
fig = plt.figure(figsize = (8,8))
fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)
for i in range(32):
ax = fig.add_subplot(8, 8, i+1, xticks=[], yticks=[])
ax.imshow(digits.images[i], cmap=plt.cm.gray_r)
ax.text(0, 1, str(digits.target[i]), bbox=dict(facecolor='white'))
#----output----
We have 1797 samples
Listing 6-2Example Code for Loading MNIST Data for Training MLPClassifier
Scikit 的关键参数-了解 MLP
让我们来看看调整 Scikit-learn MLP 模型的关键参数。清单 6-3 提供了实现 MLPClassifier 的示例代码。
-
max_iter :这是求解器收敛的最大迭代次数,默认为 200
-
learning _ rate _ init:这是初始学习速率,用于控制更新权重的步长(仅适用于解算器 sgd/adam),默认为 0.001
-
Adam:Diederik Kingma 和 Jimmy Ba 提出的基于随机梯度的优化器,适用于大型数据集
-
lbfgs :属于拟牛顿法家族,适用于小数据集
-
sgd :随机梯度下降
-
解算器 :这是为了权重优化。有三个选项可用,默认为“adam”
-
relu :整流后的线性单位函数,返回 f(x) = max(0,x)
-
逻辑:逻辑 sigmoid 函数,返回 f(x) = 1 / (1 + exp(-x))。
-
identity : No-op 激活,对实现线性瓶颈有用,返回 f(x) = x
-
tanh :双曲正切函数,返回 f(x) = tanh(x)。
-
hidden _ layer _ sizes:你要提供若干个隐藏层和每个隐藏层的神经元。例如,hidden _ layer _ sizes-(5,3,3)表示有三个隐藏层,第一层的神经元数量分别为 5 个、第二层为 3 个、第三层为 3 个。默认值为(100),即一个包含 100 个神经元的隐藏层。
-
激活 :这是一个隐藏层的激活功能;有四种激活功能可供使用;默认为“relu”
建议在建模前对数据进行缩放或归一化,因为 MLP 对要素缩放非常敏感。
# split data to training and testing data
X_train, X_test, y_train, y_test = train_test_split(digits.data, digits.target, test_size=0.2, random_state=2017)
print ('Number of samples in training set: %d' %(len(y_train)))
print ('Number of samples in test set: %d' %(len(y_test)))
# Standardise data, and fit only to the training data
scaler = StandardScaler()
scaler.fit(X_train)
# Apply the transformations to the data
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)
# Initialize ANN classifier
mlp = MLPClassifier(hidden_layer_sizes=(100), activation="logistic", max_iter = 100)
# Train the classifier with the training data
mlp.fit(X_train_scaled,y_train)
#----output----
Number of samples in training set: 1437
Number of samples in test set: 360
MLPClassifier(activation='logistic', alpha=0.0001, batch_size="auto",
beta_1=0.9, beta_2=0.999, early_stopping=False, epsilon=1e-08,
hidden_layer_sizes=(30, 30, 30), learning_rate="constant",
learning_rate_init=0.001, max_iter=100, momentum=0.9,
nesterovs_momentum=True, power_t=0.5, random_state=None,
shuffle=True, solver="adam", tol=0.0001, validation_fraction=0.1,
verbose=False, warm_start=False)
print("Training set score: %f" % mlp.score(X_train_scaled, y_train))
print("Test set score: %f" % mlp.score(X_test_scaled, y_test))
#----output----
Training set score: 0.990953
Test set score: 0.983333
# predict results from the test data
X_test_predicted = mlp.predict(X_test_scaled)
fig = plt.figure(figsize=(8, 8)) # figure size in inches
fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)
# plot the digits: each image is 8x8 pixels
for i in range(32):
ax = fig.add_subplot(8, 8, i + 1, xticks=[], yticks=[])
ax.imshow(X_test.reshape(-1, 8, 8)[i], cmap=plt.cm.gray_r)
# label the image with the target value
if X_test_predicted[i] == y_test[i]:
ax.text(0, 1, X_test_predicted[i], color="green", bbox=dict(facecolor='white'))
else:
ax.text(0, 1, X_test_predicted[i], color="red", bbox=dict(facecolor='white'))
#----output----
Listing 6-3Example Code for Sklearn MLPClassifier
受限玻尔兹曼机
Geoffrey Hinton (2007)提出了一种 RBM 算法,它学习样本训练数据输入的概率分布。它在监督/非监督 ML 的不同领域有着广泛的应用,如特征学习、降维、分类、协同过滤和主题建模。
考虑在第五章的“推荐系统”一节中讨论的电影分级的例子。像《复仇者联盟》、《阿凡达》和《星际穿越》这样的电影与最新的幻想和科幻因素有很强的关联。根据用户评级,RBM 将发现潜在的因素,可以解释电影选择的激活。简而言之,RBM 描述了输入数据集的相关变量之间的可变性,即潜在的较低数量的未观测变量。
能量函数由 E(v,h)=–aTv–bTh–vTWh 给出。
可见输入层的概率函数可以由)给出。
让我们使用 bernoulllirbm 在数字数据集上构建逻辑回归模型,并将其准确性与直接逻辑回归(不使用 bernoulllirbm)模型的准确性进行比较。
让我们通过向左、向右、向下和向上移动 1 个像素来轻推数据集,以旋绕图像(清单 6-4 )。
# Function to nudge the dataset
def nudge_dataset(X, Y):
"""
This produces a dataset 5 times bigger than the original one,
by moving the 8x8 images in X around by 1px to left, right, down, up
"""
direction_vectors = [
[[0, 1, 0],
[0, 0, 0],
[0, 0, 0]],
[[0, 0, 0],
[1, 0, 0],
[0, 0, 0]],
[[0, 0, 0],
[0, 0, 1],
[0, 0, 0]],
[[0, 0, 0],
[0, 0, 0],
[0, 1, 0]]]
shift = lambda x, w: convolve(x.reshape((8, 8)), mode="constant",
weights=w).ravel()
X = np.concatenate([X] +
[np.apply_along_axis(shift, 1, X, vector)
for vector in direction_vectors])
Y = np.concatenate([Y for _ in range(5)], axis=0)
return X, Y
Listing 6-4Function to Nudge the Dataset
BernoulliRBM 假设我们的特征向量的列落在范围 0 到 1 内。但是,MNIST 数据集表示为无符号的 8 位整数,范围在 0 到 255 之间。
定义一个函数将列缩放到范围(0,1)内。scale 函数有两个参数:我们的数据矩阵 X 和一个用于防止被零除错误的 epsilon 值(清单 6-5 )。
# Example adapted from scikit-learn documentation
import numpy as np
import matplotlib.pyplot as plt
from sklearn import linear_model, datasets, metrics
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.neural_network import BernoulliRBM
from sklearn.pipeline import Pipeline
from scipy.ndimage import convolve
# Load Data
digits = datasets.load_digits()
X = np.asarray(digits.data, 'float32')
y = digits.target
X, y = nudge_dataset(X, digits.target)
# Scale the features such that the values are between 0-1 scale
X = (X - np.min(X, 0)) / (np.max(X, 0) + 0.0001)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=2017)
print (X.shape)
print (y.shape)
#----output----
(8985L, 64)
(8985L,)
# Gridsearch for logistic regression
# perform a grid search on the 'C' parameter of Logistic
params = {"C": [1.0, 10.0, 100.0]}
Grid_Search = GridSearchCV(LogisticRegression(), params, n_jobs = -1, verbose = 1)
Grid_Search.fit(X_train, y_train)
# print diagnostic information to the user and grab the
print ("Best Score: %0.3f" % (Grid_Search.best_score_))
# best model
bestParams = Grid_Search.best_estimator_.get_params()
print (bestParams.items())
#----output----
Fitting 3 folds for each of 3 candidates, totalling 9 fits
Best Score: 0.774
[('warm_start', False), ('C', 100.0), ('n_jobs', 1), ('verbose', 0), ('intercept_scaling', 1), ('fit_intercept', True), ('max_iter', 100), ('penalty', 'l2'), ('multi_class', 'ovr'), ('random_state', None), ('dual', False), ('tol', 0.0001), ('solver', 'liblinear'), ('class_weight', None)]
# evaluate using Logistic Regression
and only the raw pixel
logistic = LogisticRegression(C = 100)
logistic.fit(X_train, y_train)
print ("Train accuracy: ", metrics.accuracy_score(y_train, logistic.predict(X_train)))
print ("Test accuracyL ", metrics.accuracy_score(y_test, logistic.predict(X_test)))
#----output----
Train accuracy: 0.797440178075
Test accuracyL 0.800779076238
Listing 6-5Example Code for Using BernoulliRBM with Classifier
让我们对 RBM +逻辑回归模型进行网格搜索—对逻辑回归的 RBM 和 C 上的学习率、迭代次数和组件数量进行网格搜索。
# initialize the RBM + Logistic Regression pipeline
rbm = BernoulliRBM()
logistic = LogisticRegression()
classifier = Pipeline([("rbm", rbm), ("logistic", logistic)])
params = {
"rbm__learning_rate": [0.1, 0.01, 0.001],
"rbm__n_iter": [20, 40, 80],
"rbm__n_components": [50, 100, 200],
"logistic__C": [1.0, 10.0, 100.0]}
# perform a grid search over the parameter
Grid_Search = GridSearchCV(classifier, params, n_jobs = -1, verbose = 1)
Grid_Search.fit(X_train, y_train)
# print diagnostic information to the user and grab the
# best model
print ("Best Score: %0.3f" % (gs.best_score_))
print ("RBM + Logistic Regression parameters")
bestParams = gs.best_estimator_.get_params()
# loop over the parameters
and print each of them out
# so they can be manually set
for p in sorted(params.keys()):
print ("\t %s: %f" % (p, bestParams[p]))
#----output----
Fitting 3 folds for each of 81 candidates, totalling 243 fits
Best Score: 0.505
RBM + Logistic Regression parameters
logistic__C: 100.000000
rbm__learning_rate: 0.001000
rbm__n_components: 200.000000
rbm__n_iter: 20.000000
# initialize the RBM + Logistic Regression classifier with
# the cross-validated parameters
rbm = BernoulliRBM(n_components = 200, n_iter = 20, learning_rate = 0.1, verbose = False)
logistic = LogisticRegression(C = 100)
# train the classifier and show an evaluation report
classifier = Pipeline([("rbm", rbm), ("logistic", logistic)])
classifier.fit(X_train, y_train)
print (metrics.accuracy_score(y_train, classifier.predict(X_train)))
print (metrics.accuracy_score(y_test, classifier.predict(X_test)))
#----output----
0.936839176405
0.932109070673
# plot RBM components
plt.figure(figsize=(15, 15))
for i, comp in enumerate(rbm.components_):
plt.subplot(20, 20, i + 1)
plt.imshow(comp.reshape((8, 8)), cmap=plt.cm.gray_r,
interpolation='nearest')
plt.xticks(())
plt.yticks(())
plt.suptitle('200 components extracted by RBM', fontsize=16)
plt.show()
#----output----
Listing 6-6Example Code for Grid Search with RBM + Logistic Regression
请注意,与没有 RBM 的模型相比,有 RBM 的逻辑回归模型将模型得分提高了 10%以上。
注意
为了进一步实践并获得更好的理解,我建议您在 Scikit-learn 的 Olivetti 人脸数据集上尝试前面的示例代码,该数据集包含 1992 年 4 月至 1994 年 4 月在剑桥美国电话电报公司实验室拍摄的人脸图像。您可以使用olivetti = datasets.fetch_olivetti_faces()
加载数据。
堆叠 RBM 被称为深度信任网络(DBN),这是一种初始化技术。然而,这种技术在 2006-2007 年间很流行,但是已经过时了。所以在 Keras 中没有现成的 DBN 实现。然而,如果你对一个简单的 DBN 实现感兴趣,我推荐你看一看 https://github.com/albertbup/deep-belief-network
,它有 MIT 的许可。
使用 Keras 的 MLP
在 Keras 中,神经网络被定义为一系列层,这些层的容器是序列类。顺序模型是层的线性堆叠;每一层的输出都输入到下一层的输入中。
神经网络的第一层将定义预期的输入数量。激活函数变换来自层中每个神经元的求和信号;同样可以提取并添加到序列中作为一个类似层的对象,称为激活。行动的选择取决于我们试图解决的问题的类型(如回归或二分类或多分类)。
from matplotlib import pyplot as plt
import numpy as np
np.random.seed(2017)
from keras.models import Sequential
from keras.datasets import mnist
from keras.layers import Dense, Activation, Dropout, Input
from keras.models import Model
from keras.utils import np_utils
from IPython.display import SVG
from keras import backend as K
from keras.callbacks import EarlyStopping
from keras.utils.visualize_util import model_to_dot, plot_model
# load data
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape[0], input_unit_size)
X_test = X_test.reshape(X_test.shape[0], input_unit_size)
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
# Scale the values by dividing 255 i.e., means foreground (black)
X_train /= 255
X_test /= 255
# one-hot representation, required for multiclass problems
y_train = np_utils.to_categorical(y_train, nb_classes)
y_test = np_utils.to_categorical(y_test, nb_classes)
print('X_train shape:', X_train.shape)
print(X_train.shape[0], 'train samples')
print(X_test.shape[0], 'test samples')
nb_classes = 10 # class size
# flatten 28*28 images to a 784 vector for each image
input_unit_size = 28*28
# create model
model = Sequential()
model.add(Dense(input_unit_size, input_dim=input_unit_size, kernel_initializer="normal", activation="relu"))
model.add(Dense(nb_classes, kernel_initializer="normal", activation="softmax"))
#----output----
'X_train shape:', (60000, 784)
60000, 'train samples'
10000, 'test samples'
Listing 6-7Example Code for Keras MLP
编译是一个带有预计算步骤的模型,它将我们定义的层序列转换成一系列高效的矩阵转换。它有三个参数:一个优化器、一个损失函数和一个评估指标列表。
与 Scikit-learn 实现不同,Keras 提供了大量的优化器,如 SGD、RMSprop、Adagrad(自适应子梯度)、Adadelta(自适应学习速率)、Adam、Adamax、Nadam 和 TFOptimizer。为了简洁起见,我不会在这里解释这些,但你可以参考官方的 Keras 网站做进一步的参考。
一些标准损失函数是用于回归的“mse ”,用于二元分类的 binary_crossentropy(对数损失),以及用于多分类问题的 categorical _ crossentropy(多类对数损失)。
支持不同类型问题的标准评估指标,您可以向它们传递一个列表进行评估(清单 6-8 )。
# Compile model
model.compile(loss='categorical_crossentropy', optimizer="adam", metrics=['accuracy'])
SVG(model_to_dot(model, show_shapes=True).create(prog='dot', format="svg"))
Listing 6-8Compile the Model
使用反向传播算法训练网络,并根据指定的方法和损失函数优化网络。每个时期可以被划分成批次。
import pandas as pd
# load pima indians dataset
dataset = pd.read_csv('Data/Diabetes.csv')
# split into input (X) and output (y) variables
X = dataset.iloc[:,0:8].values
y = dataset['class'].values # dependent variables
# create model
model = Sequential()
model.add(Dense(12, input_dim=8, kernel_initializer="uniform", activation="relu"))
model.add(Dense(1, kernel_initializer="uniform", activation="sigmoid"))
# Compile model
model.compile(loss='binary_crossentropy', optimizer="adam", metrics=['accuracy'])
SVG(model_to_dot(model, show_shapes=True).create(prog='dot', format="svg"))
# Fit the model
model.fit(X, y, epochs=5, batch_size=10)
# evaluate the model
scores = model.evaluate(X, y)
print("%s: %.2f%%" % (model.metrics_names[1], scores[1]*100))
#----output----
Epoch 1/5
768/768 [==============================] - 0s 306us/step - loss: 0.6737 - acc: 0.6250
Epoch 2/5
768/768 [==============================] - 0s 118us/step - loss: 0.6527 - acc: 0.6510
Epoch 3/5
768/768 [==============================] - 0s 96us/step - loss: 0.6432 - acc: 0.6563
Epoch 4/5
768/768 [==============================] - 0s 109us/step - loss: 0.6255 - acc: 0.6719
Epoch 5/5
768/768 [==============================] - 0s 113us/step - loss: 0.6221 - acc: 0.6706
768/768 [==============================] - 0s 84us/step
acc: 68.75%
Listing 6-10Additional Example to Train Model and Evaluate for Diabetes Dataset
# model training
model.fit(X_train, y_train, validation_data=(X_test, y_test), nb_epoch=5, batch_size=500, verbose=2)
# Final evaluation of the model
scores = model.evaluate(X_test, y_test, verbose=0)
print("Error: %.2f%%" % (100-scores[1]*100))
#----output----
Train on 60000 samples, validate on 10000 samples
Epoch 1/5
- 3s - loss: 0.3863 - acc: 0.8926 - val_loss: 0.1873 - val_acc: 0.9477
Epoch 2/5
- 3s - loss: 0.1558 - acc: 0.9561 - val_loss: 0.1280 - val_acc: 0.9612
Epoch 3/5
- 3s - loss: 0.1071 - acc: 0.9696 - val_loss: 0.1009 - val_acc: 0.9697
Epoch 4/5
- 3s - loss: 0.0800 - acc: 0.9773 - val_loss: 0.0845 - val_acc: 0.9756
Epoch 5/5
- 3s - loss: 0.0607 - acc: 0.9832 - val_loss: 0.0760 - val_acc: 0.9776
Error: 2.24%
Listing 6-9Train Model and Evaluate
自动编码器
顾名思义,自动编码器旨在学习在没有人工干预的情况下自动编码训练样本数据的表示。autoencoder 广泛用于降维和数据去噪(图 6-5 )。
图 6-5
自动编码器
构建自动编码器通常有三个要素:
-
通过非线性函数将输入映射到隐藏表示的编码函数,z = sigmoid (Wx + b)
-
诸如 x’ = sigmoid(W’y + b ')的解码函数,它将映射回具有与 x 相同形状的重构 x ’
-
损失函数,它是一个距离函数,用于测量数据的压缩表示和解压缩表示之间的信息损失。重建误差可以用传统的平方误差||x-z|| 2 来衡量。
我们将使用著名的 MNIST 手写数字数据库,该数据库包含大约 70,000 个手写灰度数字图像样本,从 0 到 9。每幅图像的大小为 28 ×
28,强度等级从 0 到 255 不等,其中 60,000 幅图像附有 0 到 9 的整数标签,其余图像没有标签(测试数据集)。
使用自动编码器降维
清单 6-11 提供了一个使用自动编码器减少维度的示例代码实现。
import numpy as np
np.random.seed(2017)
from keras.datasets import mnist
from keras.models import Model
from keras.layers import Input, Dense
from keras.optimizers import Adadelta
from keras.utils import np_utils
from IPython.display import SVG
from keras import backend as K
from keras.callbacks import EarlyStopping
from keras.utils.visualize_util import model_to_dot
from matplotlib import pyplot as plt
# Load mnist data
input_unit_size = 28*28
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# function to plot digits
def draw_digit(data, row, col, n):
size = int(np.sqrt(data.shape[0]))
plt.subplot(row, col, n)
plt.imshow(data.reshape(size, size))
plt.gray()
# Normalize
X_train = X_train.reshape(X_train.shape[0], input_unit_size)
X_train = X_train.astype('float32')
X_train /= 255
print('X_train shape:', X_train.shape)
#----output----
'X_train shape:', (60000, 784)
# Autoencoder
inputs = Input(shape=(input_unit_size,))
x = Dense(144, activation="relu")(inputs)
outputs = Dense(input_unit_size)(x)
model = Model(input=inputs, output=outputs)
model.compile(loss='mse', optimizer="adadelta")
SVG(model_to_dot(model, show_shapes=True).create(prog='dot', format="svg"))
#----output----
Listing 6-11Example Code for Dimension Reduction Using an Autoencoder
注意,通过在隐藏层中编码到 144,784 维被减小,并且在层 3 中再次使用解码器构造回 784。
model.fit(X_train, X_train, nb_epoch=5, batch_size=258)
#----output----
Epoch 1/5
60000/60000 [==============================] - 8s - loss: 0.0733
Epoch 2/5
60000/60000 [==============================] - 9s - loss: 0.0547
Epoch 3/5
60000/60000 [==============================] - 11s - loss: 0.0451
Epoch 4/5
60000/60000 [==============================] - 11s - loss: 0.0392
Epoch 5/5
60000/60000 [==============================] - 11s - loss: 0.0354
# plot the images from input layers
show_size = 5
total = 0
plt.figure(figsize=(5,5))
for i in range(show_size):
for j in range(show_size):
draw_digit(X_train[total], show_size, show_size, total+1)
total+=1
plt.show()
#----output----
# plot the encoded (compressed) layer image
get_layer_output = K.function([model.layers[0].input],
[model.layers[1].output])
hidden_outputs = get_layer_output([X_train[0:show_size**2]])[0]
total = 0
plt.figure(figsize=(5,5))
for i in range(show_size):
for j in range(show_size):
draw_digit(hidden_outputs[total], show_size, show_size, total+1)
total+=1
plt.show()
#----output----
# Plot the decoded (de-compressed) layer images
get_layer_output = K.function([model.layers[0].input],
[model.layers[2].output])
last_outputs = get_layer_output([X_train[0:show_size**2]])[0]
total = 0
plt.figure(figsize=(5,5))
for i in range(show_size):
for j in range(show_size):
draw_digit(last_outputs[total], show_size, show_size, total+1)
total+=1
plt.show()
#----output----
使用自动编码器对图像去噪
从压缩的隐藏层中发现鲁棒特征是使自动编码器能够从去噪版本或原始图像有效地重建输入的一个重要方面。这由去噪自动编码器解决,它是自动编码器的随机版本。
让我们向数字数据集引入一些噪声,并尝试建立一个模型来对图像进行降噪(清单 6-12 )。
# Introducing noise to the image
noise_factor = 0.5
X_train_noisy = X_train + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=X_train.shape)
X_train_noisy = np.clip(X_train_noisy, 0., 1.)
# Function for visualization
def draw(data, row, col, n):
plt.subplot(row, col, n)
plt.imshow(data, cmap=plt.cm.gray_r)
plt.axis('off')
show_size = 10
plt.figure(figsize=(20,20))
for i in range(show_size):
draw(X_train_noisy[i].reshape(28,28), 1, show_size, i+1)
plt.show()
#----output----
Listing 6-12Example Code for Denoising Using an Autoencoder
#Let's fit a model on noisy training dataset
.
model.fit(X_train_noisy, X_train, nb_epoch=5, batch_size=258)
# Prediction for denoised image
X_train_pred = model.predict(X_train_noisy)
show_size = 10
plt.figure(figsize=(20,20))
for i in range(show_size):
draw(X_train_pred[i].reshape(28,28), 1, show_size, i+1)
plt.show()
#----output----
注意,我们可以调整模型来提高去噪图像的清晰度。
卷积神经网络(CNN)
在图像分类领域,CNN 已经成为构建高效模型的首选算法。CNN 类似于普通的神经网络,只是它明确假设输入是图像,这允许我们将某些属性编码到架构中。然后,这些使得转发功能有效,以实现并减少网络中的参数。神经元以三维方式排列:宽度、高度和深度。
让我们考虑 CIFAR-10(加拿大高级研究所),这是一个标准的计算机视觉和深度学习图像数据集。它由 60,000 张 32×32 像素见方的彩色照片组成,每个像素为 RGB,分为十类,包括常见的物体,如飞机、汽车、鸟类、猫、鹿、狗、青蛙、马、船和卡车。基本上每个图像的大小是 32 ×
32 ×
3(宽度×
高度×
RGB 颜色通道)。
CNN 由四种主要类型的层组成:输入层、卷积层、汇集层和全连接层。
输入层将保存原始像素,因此 CIFAR-10 的图像在输入层将有 32 ×
32 ×
3 个维度。卷积层将计算来自输入层的小局部区域的权重之间的点积,因此,如果我们决定有五个过滤器,则结果减少的维度将是 32 ×
32 ×
5。ReLU 层将应用元素激活函数,这不会影响维度。池层将沿宽度和高度对空间维度进行向下采样,得到 16 ×
16 ×
5 的维度。最后全连通层会计算类得分,得到的维数是单个向量 1 ×
1 ×
10(十个类得分)。这一层的每一个神经元都连接到上一卷的所有数字上(图 6-6 )。
图 6-6
卷积神经网络
下面的示例说明使用 Keras 和 Theano 后端。要使用 Theano 后端启动 Kearas,请在启动 Jupyter 笔记本的同时运行以下命令,“KERAS_BACKEND=theano jupyter notebook
”(列表 6-13 )。
import keras
if K=='tensorflow':
keras.backend.set_image_dim_ordering('tf')
else:
keras.backend.set_image_dim_ordering('th')
from keras.models import Sequential
from keras.datasets import cifar10
from keras.layers import Dense, Dropout, Activation, Conv2D, MaxPooling2D, Flatten
from keras.utils import np_utils
from keras.preprocessing import sequence
from keras import backend as K
from IPython.display import SVG, display
from keras.utils.vis_utils import model_to_dot, plot_model
import numpy as np
np.random.seed(2017)
img_rows, img_cols = 32, 32
img_channels = 3
batch_size = 256
nb_classes = 10
nb_epoch = 4
nb_filters = 10
nb_conv = 3
nb_pool = 2
kernel_size = 3 # convolution kernel size
if K.image_dim_ordering() == 'th':
input_shape = (3, img_rows, img_cols)
else:
input_shape = (img_rows, img_cols, 3)
(X_train, y_train), (X_test, y_test) = cifar10.load_data()
print('X_train shape:', X_train.shape)
print(X_train.shape[0], 'train samples')
print(X_test.shape[0], 'test samples')
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255
Y_train = np_utils.to_categorical(y_train, nb_classes)
Y_test = np_utils.to_categorical(y_test, nb_classes)
#----output----
X_train shape: (50000, 32, 32, 3)
50000 train samples
10000 test samples
# define two groups of layers: feature (convolutions) and classification (dense)
feature_layers = [
Conv2D(nb_filters, kernel_size, input_shape=input_shape),
Activation('relu'),
Conv2D(nb_filters, kernel_size),
Activation('relu'),
MaxPooling2D(pool_size=(nb_pool, nb_pool)),
Flatten(),
]
classification_layers = [
Dense(512),
Activation('relu'),
Dense(nb_classes),
Activation('softmax')
]
# create complete model
model = Sequential(feature_layers + classification_layers)
model.compile(loss='categorical_crossentropy', optimizer="adadelta", metrics=['accuracy'])
SVG(model_to_dot(model, show_shapes=True).create(prog='dot', format="svg"))
#----output----
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_1 (Conv2D) (None, 30, 30, 10) 280
_________________________________________________________________
activation_1 (Activation) (None, 30, 30, 10) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 28, 28, 10) 910
_________________________________________________________________
activation_2 (Activation) (None, 28, 28, 10) 0
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 14, 14, 10) 0
_________________________________________________________________
flatten_1 (Flatten) (None, 1960) 0
_________________________________________________________________
dense_1 (Dense) (None, 512) 1004032
_________________________________________________________________
activation_3 (Activation) (None, 512) 0
_________________________________________________________________
dense_2 (Dense) (None, 10) 5130
_________________________________________________________________
activation_4 (Activation) (None, 10) 0
=================================================================
Total params: 1,010,352
Trainable params: 1,010,352
Non-trainable params: 0
# fit model
model.fit(X_train, Y_train, validation_data=(X_test, Y_test),
epochs=nb_epoch, batch_size=batch_size, verbose=2)
#----output----
Train on 50000 samples, validate on 10000 samples
Epoch 1/4
- 50s - loss: 1.8512 - acc: 0.3422 - val_loss: 1.5729 - val_acc: 0.4438
Epoch 2/4
- 38s - loss: 1.4350 - acc: 0.4945 - val_loss: 1.4312 - val_acc: 0.4832
Epoch 3/4
- 38s - loss: 1.2542 - acc: 0.5566 - val_loss: 1.3300 - val_acc: 0.5191
Epoch 4/4
- 38s - loss: 1.1375 - acc: 0.6021 - val_loss: 1.1760 - val_acc: 0.5760
Let's visualize each layer. Note that we applied ten filters.
# function for Visualization
# visualization
def draw(data, row, col, n):
plt.subplot(row, col, n)
plt.imshow(data)
def draw_digit(data, row, col):
for j in range(row):
plt.figure(figsize=(16,16))
for i in range(col):
plt.subplot(row, col, i+1)
plt.imshow(data[j,:,:,i])
plt.axis('off')
plt.tight_layout()
plt.show()
### Input layer (original image)
show_size = 10
plt.figure(figsize=(16,16))
for i in range(show_size):
draw(X_train[i], 1, show_size, i+1)
plt.show()
#----output----
Listing 6-13CNN Using Keras with Theano Backend on CIFAR10 Dataset
Notice in the following that the hidden layers features are stored in ten filters.
# first layer
get_first_layer_output = K.function([model.layers[0].input],
[model.layers[1].output])
first_layer = get_first_layer_output([X_train[0:show_size]])[0]
print ('first layer shape: ', first_layer.shape)
draw_digit(first_layer, first_layer.shape[0], first_layer.shape[3])
#----output----
# second layer
get_second_layer_output = K.function([model.layers[0].input],
[model.layers[3].output])
second_layers = get_second_layer_output([X_train[0:show_size]])[0]
print ('second layer shape: ', second_layers.shape)
draw_digit(second_layers, second_layers.shape[0], second_layers.shape[3]) #----output----
# third layer
get_third_layer_output = K.function([model.layers[0].input],
[model.layers[4].output])
third_layers = get_third_layer_output([X_train[0:show_size]])[0]
print ('third layer shape: ', third_layers.shape)
draw_digit(third_layers, third_layers.shape[0], third_layers.shape[3])
#----output-----
MNIST 数据集上的 CNN
作为一个额外的例子,让我们看看 CNN 在 digits 数据集上的表现(清单 6-14 )。
import keras
keras.backend.backend()
keras.backend.image_dim_ordering()
# using theano as backend
K = keras.backend.backend()
if K=='tensorflow':
keras.backend.set_image_dim_ordering('tf')
else:
keras.backend.set_image_dim_ordering('th')
from matplotlib import pyplot as plt
%matplotlib inline
import numpy as np
np.random.seed(2017)
from keras import backend as K
from keras.models import Sequential
from keras.datasets import mnist
from keras.layers import Dense, Dropout, Activation, Conv2D, MaxPooling2D, Flatten
from keras.utils import np_utils
from keras.preprocessing import sequence
from keras import backend as K
from IPython.display import SVG, display
from keras.utils.vis_utils import model_to_dot, plot_model
nb_filters = 5 # the number of filters
nb_pool = 2 # window size of pooling
nb_conv = 3 # window or kernel size of filter
nb_epoch = 5
kernel_size = 3 # convolution kernel size
if K.image_dim_ordering() == 'th':
input_shape = (1, img_rows, img_cols)
else:
input_shape = (img_rows, img_cols, 1)
# data
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 1)
X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1)
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255
print('X_train shape:', X_train.shape)
print(X_train.shape[0], 'train samples')
print(X_test.shape[0], 'test samples')
# convert class vectors to binary class matrices
Y_train = np_utils.to_categorical(y_train, nb_classes)
Y_test = np_utils.to_categorical(y_test, nb_classes)
#----output----
'X_train shape:', (60000, 1, 28, 28)
60000, 'train samples'
10000, 'test samples'
# define two groups of layers
: feature (convolutions) and classification (dense)
feature_layers = [
Conv2D(nb_filters, kernel_size, input_shape=input_shape),
Activation('relu'),
Conv2D(nb_filters, kernel_size),
Activation('relu'),
MaxPooling2D(pool_size = nb_pool),
Dropout(0.25),
Flatten(),
]
classification_layers = [
Dense(128),
Activation('relu'),
Dropout(0.5),
Dense(nb_classes),
Activation('softmax')
]
# create complete model
model = Sequential(feature_layers + classification_layers)
model.compile(loss='categorical_crossentropy', optimizer="adadelta", metrics=['accuracy'])
SVG(model_to_dot(model, show_shapes=True).create(prog='dot', format="svg"))
print(model.summary())
#----output----
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_1 (Conv2D) (None, 26, 26, 5) 50
_________________________________________________________________
activation_1 (Activation) (None, 26, 26, 5) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 24, 24, 5) 230
_________________________________________________________________
activation_2 (Activation) (None, 24, 24, 5) 0
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 12, 12, 5) 0
_________________________________________________________________
dropout_1 (Dropout) (None, 12, 12, 5) 0
_________________________________________________________________
flatten_1 (Flatten) (None, 720) 0
_________________________________________________________________
dense_1 (Dense) (None, 128) 92288
_________________________________________________________________
activation_3 (Activation) (None, 128) 0
_________________________________________________________________
dropout_2 (Dropout) (None, 128) 0
_________________________________________________________________
dense_2 (Dense) (None, 10) 1290
_________________________________________________________________
activation_4 (Activation) (None, 10) 0
=================================================================
Total params: 93,858
Trainable params: 93,858
Non-trainable params: 0
model.fit(X_train, Y_train, batch_size=256, epochs=nb_epoch, verbose=2, validation_split=0.2)
#----output----
Train on 48000 samples, validate on 12000 samples
Epoch 1/5
- 15s - loss: 0.6098 - acc: 0.8085 - val_loss: 0.1609 - val_acc: 0.9523
Epoch 2/5
- 15s - loss: 0.2427 - acc: 0.9251 - val_loss: 0.1148 - val_acc: 0.9675
Epoch 3/5
- 15s - loss: 0.1941 - acc: 0.9410 - val_loss: 0.0950 - val_acc: 0.9727
Epoch 4/5
- 15s - loss: 0.1670 - acc: 0.9483 - val_loss: 0.0866 - val_acc: 0.9753
Epoch 5/5
- 15s - loss: 0.1500 - acc: 0.9548 - val_loss: 0.0830 - val_acc: 0.9767
Listing 6-14CNN Using Keras with Theano Backend on MNIST Dataset
层的可视化
# visualization
def draw(data, row, col, n):
plt.subplot(row, col, n)
plt.imshow(data, cmap=plt.cm.gray_r)
plt.axis('off')
def draw_digit(data, row, col):
for j in range(row):
plt.figure(figsize=(8,8))
for i in range(col):
plt.subplot(row, col, i+1)
plt.imshow(data[j,:,:,i], cmap=plt.cm.gray_r)
plt.axis('off')
plt.tight_layout()
plt.show()
# Sample input layer (original image)
show_size = 10
plt.figure(figsize=(20,20))
for i in range(show_size):
draw(X_train[i].reshape(28,28), 1, show_size, i+1)
plt.show()
#----output----
# First layer with 5 filters
get_first_layer_output = K.function([model.layers[0].input], [model.layers[1].output])
first_layer = get_first_layer_output([X_train[0:show_size]])[0]
print ('first layer shape: ', first_layer.shape)
draw_digit(first_layer, first_layer.shape[0], first_layer.shape[3])
#----output----
循环神经网络(RNN)
众所周知,MLP(前馈网络)在顺序事件模型(如概率语言模型)上并不擅长在每个给定点根据前一个单词预测下一个单词。RNN 建筑解决了这个问题。它类似于 MLP,只是它有一个反馈环,这意味着它将以前的时间步骤反馈到当前步骤。这种类型的架构生成序列来模拟情况并创建合成数据。这使得它成为处理序列数据的理想建模选择,如语音文本挖掘、图像字幕、时间序列预测、机器人控制、语言建模等。(图 6-7 )。
图 6-7
循环神经网络
前一步的隐藏层和最终输出被反馈到网络中,并将被用作下一步的隐藏层的输入,这意味着网络将记住过去,并反复预测接下来将发生什么。一般 RNN 体系结构的缺点是,它可能占用大量内存,并且难以针对长期时间依赖性进行训练(即,长文本的上下文在任何给定阶段都应该是已知的)。
长短期记忆(LSTM)
LSTM 是改进的 RNN 体系结构的实现,以解决一般 RNN 的问题,并且它允许远程依赖。它旨在通过线性存储单元获得更好的记忆,这些存储单元由一组用于控制信息流的门单元包围——信息何时应该进入存储器,何时应该忘记,何时应该输出。它在递归分量中不使用激活函数,因此梯度项不会随着反向传播而消失。图 6-8 给出了简单多层感知器与 RNN 和 LSTM 的比较。
图 6-8
简单 MLP 诉 RNN 诉 LSM
请参考表 6-3 来理解关键的 LSTM 组件公式。
表 6-3
LSTM 组件
|LSTM 组件
|
公式
|
| — | — |
| 输入门层:这决定在单元状态中存储哪些值。 | i t =乙状结肠(wIxt+uIht-1+bI |
| 忘记门层:顾名思义,这决定了从单元状态中丢弃什么信息。 | f t =乙状结肠(Wfxt+Ufht-1+bf |
| 输出门层:创建可添加到像元状态的值的向量。 | O t =乙状结肠(Woxt+uIht-1+bo |
| 存储单元状态向量 | ct= fto ct-1+Ito *双曲正切(Wcxt+ucht-1+bc |
让我们看一个 IMDB 数据集的例子,它为电影评论标记了情绪(正面/负面)。这些评论已经过预处理,并被编码为一系列单词索引(清单 6-15 )。
import numpy as np
np.random.seed(2017) # for reproducibility
from keras.preprocessing import sequence
from keras.models import Sequential
from keras.layers import Dense, Activation, Embedding
from keras.layers import LSTM
from keras.datasets import imdb
max_features = 20000
maxlen = 80 # cut texts after this number of words (among top max_features most common words)
batch_size = 32
print('Loading data...')
(X_train, y_train), (X_test, y_test) = imdb.load_data(num_words=max_features)
print(len(X_train), 'train sequences')
print(len(X_test), 'test sequences')
print('Pad sequences (samples x time)')
X_train = sequence.pad_sequences(X_train, maxlen=maxlen)
X_test = sequence.pad_sequences(X_test, maxlen=maxlen)
print('X_train shape:', X_train.shape)
print('X_test shape:', X_test.shape)
#----output----
Loading data...
25000 train sequences
25000 test sequences
Pad sequences (samples x time)
X_train shape: (25000, 80)
X_test shape: (25000, 80)
#Model configuration
model = Sequential()
model.add(Embedding(max_features, 128))
model.add(LSTM(128, recurrent_dropout=0.2, dropout=0.2)) # try using a GRU instead, for fun
model.add(Dense(1))
model.add(Activation('sigmoid'))
# Try using different optimizers and different optimizer configs
model.compile(loss='binary_crossentropy', optimizer="adam", metrics=['accuracy'])
#Train
model.fit(X_train, y_train, batch_size=batch_size, epochs=5, validation_data=(X_test, y_test))
#----output----
Epoch 1/5
25000/25000 [==============================] - 99s 4ms/step - loss: 0.4604 - acc: 0.7821 - val_loss: 0.3762 - val_acc: 0.8380
Epoch 2/5
25000/25000 [==============================] - 86s 3ms/step - loss: 0.3006 - acc: 0.8766 - val_loss: 0.3710 - val_acc: 0.8353
Epoch 3/5
25000/25000 [==============================] - 86s 3ms/step - loss: 0.2196 - acc: 0.9146 - val_loss: 0.4113 - val_acc: 0.8212
Epoch 4/5
25000/25000 [==============================] - 86s 3ms/step - loss: 0.1558 - acc: 0.9411 - val_loss: 0.4733 - val_acc: 0.8116
Epoch 5/5
25000/25000 [==============================] - 86s 3ms/step - loss: 0.1112 - acc: 0.9597 - val_loss: 0.6225 - val_acc: 0.8202
# Evaluate
train_score, train_acc = model.evaluate(X_train, y_train, batch_size=batch_size)
test_score, test_acc = model.evaluate(X_test, y_test, batch_size=batch_size)
print ('Train score:', train_score)
print ('Train accuracy:', train_acc)
print ('Test score:', test_score)
print ('Test accuracy:', test_acc)
#----output----
25000/25000 [==============================] - 37s 1ms/step
25000/25000 [==============================] - 28s 1ms/step
Train score: 0.055540263980031014
Train accuracy: 0.98432
Test score: 0.5643649271917344
Test accuracy: 0.82388
Listing 6-15Example Code for Keras LSTM
迁移学习
根据我们过去的经验,我们人类可以很容易地学会一项新技能。我们的学习效率更高,尤其是当手头的任务与我们过去所做的相似时。例如,根据我们过去的经验,为计算机专业人员学习一种新的编程语言,或者为经验丰富的司机驾驶一种新型车辆是相对容易的。
迁移学习是 ML 中的一个领域,旨在利用解决一个问题时获得的知识来解决一个不同但相关的问题(图 6-9 )。
图 6-9
迁移学习
没有什么比通过示例来理解更好的了,所以让我们在 MNIST 数据集的前 5 个数字(0 到 4)上训练一个简单的两级层 CNN 模型(一个要素层和一个分类层),然后应用迁移学习来冻结要素层,并针对数字 5 到 9 的分类微调密集层(清单 6-16 )。
import numpy as np
np.random.seed(2017) # for reproducibility
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.utils import np_utils
from keras import backend as K
batch_size = 128
nb_classes = 5
nb_epoch = 5
# input image dimensions
img_rows, img_cols = 28, 28
# number of convolutional filters to use
nb_filters = 32
# size of pooling area for max pooling
pool_size = 2
# convolution kernel size
kernel_size = 3
input_shape = (img_rows, img_cols, 1)
# the data, shuffled and split between train and test sets
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# create two datasets
one with digits below 5 and one with 5 and above
X_train_lt5 = X_train[y_train < 5]
y_train_lt5 = y_train[y_train < 5]
X_test_lt5 = X_test[y_test < 5]
y_test_lt5 = y_test[y_test < 5]
X_train_gte5 = X_train[y_train >= 5]
y_train_gte5 = y_train[y_train >= 5] - 5 # make classes start at 0 for
X_test_gte5 = X_test[y_test >= 5] # np_utils.to_categorical
y_test_gte5 = y_test[y_test >= 5] – 5
# Train model for digits 0 to 4
def train_model(model, train, test, nb_classes):
X_train = train[0].reshape((train[0].shape[0],) + input_shape)
X_test = test[0].reshape((test[0].shape[0],) + input_shape)
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255
print('X_train shape:', X_train.shape)
print(X_train.shape[0], 'train samples')
print(X_test.shape[0], 'test samples')
# convert class vectors to binary class matrices
Y_train = np_utils.to_categorical(train[1], nb_classes)
Y_test = np_utils.to_categorical(test[1], nb_classes)
model.compile(loss='categorical_crossentropy',
optimizer='adadelta',
metrics=['accuracy'])
model.fit(X_train, Y_train,
batch_size=batch_size, epochs=nb_epoch,
verbose=1,
validation_data=(X_test, Y_test))
score = model.evaluate(X_test, Y_test, verbose=0)
print('Test score:', score[0])
print('Test accuracy:', score[1])
# define two groups of layers: feature (convolutions) and classification (dense)
feature_layers = [
Conv2D(nb_filters, kernel_size,
padding='valid',
input_shape=input_shape),
Activation('relu'),
Conv2D(nb_filters, kernel_size),
Activation('relu'),
MaxPooling2D(pool_size=(pool_size, pool_size)),
Dropout(0.25),
Flatten(),
]
classification_layers = [
Dense(128),
Activation('relu'),
Dropout(0.5),
Dense(nb_classes),
Activation('softmax')
]
# create complete model
model = Sequential(feature_layers + classification_layers)
# train model for 5-digit classification [0..4]
train_model(model, (X_train_lt5, y_train_lt5), (X_test_lt5, y_test_lt5), nb_classes)
#----output----
X_train shape: (30596, 28, 28, 1)
30596 train samples
5139 test samples
Train on 30596 samples, validate on 5139 samples
Epoch 1/5
30596/30596 [==============================] - 40s 1ms/step - loss: 0.1692 - acc: 0.9446 - val_loss: 0.0573 - val_acc: 0.9798
Epoch 2/5
30596/30596 [==============================] - 37s 1ms/step - loss: 0.0473 - acc: 0.9858 - val_loss: 0.0149 - val_acc: 0.9947
Epoch 3/5
30596/30596 [==============================] - 37s 1ms/step - loss: 0.0316 - acc: 0.9906 - val_loss: 0.0112 - val_acc: 0.9947
Epoch 4/5
30596/30596 [==============================] - 37s 1ms/step - loss: 0.0257 - acc: 0.9928 - val_loss: 0.0094 - val_acc: 0.9967
Epoch 5/5
30596/30596 [==============================] - 37s 1ms/step - loss: 0.0204 - acc: 0.9940 - val_loss: 0.0078 - val_acc: 0.9977
Test score: 0.00782204038783338
Test accuracy: 0.9976649153531816
Transfer existing trained model on 0 to 4 to build model for digits 5 to 9
# freeze feature layers and rebuild model
for layer in feature_layers:
layer.trainable = False
# transfer: train dense layers for new classification task [5..9]
train_model(model, (X_train_gte5, y_train_gte5), (X_test_gte5, y_test_gte5), nb_classes)
#----output----
X_train shape: (29404, 28, 28, 1)
29404 train samples
4861 test samples
Train on 29404 samples, validate on 4861 samples
Epoch 1/5
29404/29404 [==============================] - 14s 484us/step - loss: 0.2290 - acc: 0.9353 - val_loss: 0.0504 - val_acc: 0.9846
Epoch 2/5
29404/29404 [==============================] - 14s 475us/step - loss: 0.0755 - acc: 0.9764 - val_loss: 0.0325 - val_acc: 0.9899
Epoch 3/5
29404/29404 [==============================] - 14s 480us/step - loss: 0.0563 - acc: 0.9828 - val_loss: 0.0326 - val_acc: 0.9881
Epoch 4/5
29404/29404 [==============================] - 14s 480us/step - loss: 0.0472 - acc: 0.9852 - val_loss: 0.0258 - val_acc: 0.9893
Epoch 5/5
29404/29404 [==============================] - 14s 481us/step - loss: 0.0404 - acc: 0.9871 - val_loss: 0.0259 - val_acc: 0.9907
Test score: 0.025926338075212205
Test accuracy: 0.990742645546184
Listing 6-16Example Code for Transfer Learning
注意,对于前五个数字分类器,我们在 5 个时期后获得了 99.8%的测试准确度,并且在转移和微调后,对于后五个数字获得了 99.2%的测试准确度。
强化学习
强化学习是一种基于与环境互动的目标导向的学习方法。目标是让代理在一个环境中行动,以最大化其回报。这里 agent 是一个智能程序,环境是外部条件(图 6-10 )。
图 6-10
强化学习就像教你的狗变戏法
让我们考虑一个预定义的系统的例子,这个系统教狗一个新的技巧,你不需要告诉狗做什么。然而,如果狗做对了,你可以奖励它,如果它做错了,你可以惩罚它。每走一步,它都得记住是什么使它得到奖励或惩罚;这就是通常所说的信用分配问题。类似地,我们可以训练一个计算机代理,使得它的目标是采取行动从状态 st 移动到状态 st+1,并找到行为函数来最大化折扣奖励的期望总和,并将状态映射到行动。根据 Deepmind Technologies 在 2013 年发表的论文,更新状态的 Q 学习规则由下式给出:Q[s,a] new = Q[s,a]prev+α∫(r+γ∫max(s,a)–Q[s,a] prev ,其中
α是学习率,
r 是对最新行动的奖励,
γ是贴现因子,以及
max(s,a)是对最佳行动的新价值的估计。
如果序列 s '在下一个时间步的最优值 Q[s,a]对于所有可能的动作 a '是已知的,那么最优策略是选择动作 a '最大化 r+γ÷max(s,a)-Q[s,a] prev 的期望值。
让我们考虑一个例子,其中一个代理正试图走出迷宫(图 6-11 )。它可以向任意方向移动任意一个方格或区域,如果退出就可以获得奖励。形式化强化问题的最常见方法是将其表示为马尔可夫决策过程。假设代理处于状态 b(迷宫区域),目标是到达状态 f,那么在一个步骤内代理可以从 b 到达 f。让我们为允许代理到达目标状态的节点之间的链接设置 100(否则为 0)的奖励。清单 6-17 提供了 q-learning 的示例代码实现。
图 6-11
左图:五态迷宫。右图:马尔可夫决策过程
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
# defines the reward/link connection graph
R = np.array([[-1, -1, -1, -1, 0, -1],
[-1, -1, -1, 0, -1, 100],
[-1, -1, -1, 0, -1, -1],
[-1, 0, 0, -1, 0, -1],
[ 0, -1, -1, 0, -1, 100],
[-1, 0, -1, -1, 0, 100]]).astype("float32")
Q = np.zeros_like(R)
Listing 6-17Example Code for Q-learning
表中的-1 表示节点之间没有链接。例如,状态‘a’不能转到状态‘b’。
# learning parameter
gamma = 0.8
# Initialize random_state
initial_state = randint(0,4)
# This function returns all available actions
in the state given as an argument
def available_actions(state):
current_state_row = R[state,]
av_act = np.where(current_state_row >= 0)[1]
return av_act
# This function chooses at random which action to be performed within the range
# of all the available actions.
def sample_next_action(available_actions_range):
next_action = int(np.random.choice(available_act,1))
return next_action
# This function updates the Q matrix
according to the path selected and the Q
# learning algorithm
def update(current_state, action, gamma):
max_index = np.where(Q[action,] == np.max(Q[action,]))[1]
if max_index.shape[0] > 1:
max_index = int(np.random.choice(max_index, size = 1))
else:
max_index = int(max_index)
max_value = Q[action, max_index]
# Q learning formula
Q[current_state, action] = R[current_state, action] + gamma * max_value
# Get available actions in the current state
available_act = available_actions(initial_state)
# Sample next action to be performed
action = sample_next_action(available_act)
# Train over 100 iterations, re-iterate the process above).
for i in range(100):
current_state = np.random.randint(0, int(Q.shape[0]))
available_act = available_actions(current_state)
action = sample_next_action(available_act)
update(current_state,action,gamma)
# Normalize the "trained" Q matrix
print ("Trained Q matrix: \n", Q/np.max(Q)*100)
# Testing
current_state = 2
steps = [current_state]
while current_state != 5:
next_step_index = np.where(Q[current_state,] == np.max(Q[current_state,]))[1]
if next_step_index.shape[0] > 1:
next_step_index = int(np.random.choice(next_step_index, size = 1))
else:
next_step_index = int(next_step_index)
steps.append(next_step_index)
current_state = next_step_index
# Print selected sequence of steps
print ("Best sequence path: ", steps)
#----output----
Best sequence path: [2, 3, 1, 5]
摘要
在这一章中,你已经简要地学习了使用人工神经网络的深度学习技术的各种主题,从单感知器开始,到多层感知器,到更复杂形式的深度神经网络,如 CNN 和 RNN。您已经了解了与图像数据相关的各种问题,以及研究人员如何试图模仿人脑来构建模型,这些模型可以分别使用卷积神经网络和循环神经网络来解决与计算机视觉和文本挖掘相关的复杂问题。您还了解了如何使用自动编码器来压缩/解压缩数据或消除图像数据中的噪声。您了解了广受欢迎的 RBN,它可以学习输入数据中的概率分布,使我们能够构建更好的模型。您学习了迁移学习,它帮助我们将知识从一个模型转移到另一个类似的模型。最后,我们简要地看了一个使用 Q-learning 的强化学习的简单例子。恭喜你。你已经到达了掌握机器学习的六步探险的终点。
七、总结
我希望你喜欢六步简化机器学习(ML)探险。您从第 1 步开始了学习之旅,学习了 Python 3 编程语言的核心理念和关键概念。在步骤 2 中,您学习了 ML 历史、高级类别(监督/非监督/强化学习)和构建 ML 系统的三个重要框架(SEMMA、CRISP-DM、KDD 数据挖掘过程)、主要数据分析包(NumPy、Pandas、Matplotlib)及其关键概念,以及不同核心 ML 库的比较。在第 3 步中,您学习了不同的数据类型、关键的数据质量问题以及如何处理它们、探索性分析、监督/非监督学习的核心方法以及它们的示例实现。在第 4 步中,您学习了各种模型诊断技术、过拟合的 bagging 技术、欠拟合的 boosting 技术、集成技术;以及用于构建高效模型的超参数调整(网格/随机搜索)。在步骤 5 中,您了解了文本挖掘过程的概况:数据集合、数据预处理、数据探索或可视化,以及可以构建的各种模型。您还了解了如何构建协作/基于内容的推荐系统来个性化用户体验。在步骤 6 中,您学习了通过感知器的人工神经网络、用于图像分析的卷积神经网络(CNN)、用于文本分析的循环神经网络(RNNs ),以及用于学习强化学习概念的简单玩具示例。这些都是在过去几年中有很大发展的高级主题。
总的来说,你已经学习了广泛的常用 ML 主题;它们中的每一个都带有许多参数来控制和调整模型性能。为了在整本书中保持简单,我要么使用默认参数,要么只向您介绍关键参数(在某些地方)。软件包的创建者已经仔细选择了参数的默认选项,以给出合适的结果来帮助您入门。所以,首先你可以使用默认参数。但是,我建议您探索其他参数,并使用手动/网格/随机搜索来使用它们,以确保模型的健壮性。表 7-1 总结了各种可能的问题类型、示例用例以及您可以使用的潜在 ML 算法。请注意,这只是一个示例列表,而不是一个详尽的列表。
表 7-1
问题类型与潜在的 ML 算法
|问题类型
|
示例使用案例
|
潜在的 ML 算法
|
| — | — | — |
| 预测连续数 | 商店每天/每周的销售额是多少? | 线性回归或多项式回归 |
| 预测连续数的计数类型 | 一个班次需要多少员工?一家新店需要多少停车位? | 泊松分布的广义线性模型 |
| 预测一个事件的概率(对/错) | 交易是欺诈的概率有多大? | 二元分类模型(逻辑回归、决策树模型、boosting 模型、kNN 等。) |
| 从许多可能的事件中预测事件的概率(多类) | 交易高风险/中风险/低风险的概率是多少? | 多类分类模型(逻辑回归、决策树模型、boosting 模型、kNN 等。) |
| 根据相似性对内容进行分组 | 分组相似的客户?分组相似的类别? | k-均值聚类,层次聚类 |
| 降维 | 拥有最大百分比信息的重要维度是什么? | 主成分分析,奇异值分解 |
| 主题建模 | 根据主题或主题结构对文档进行分组? | 潜在狄利克雷分配,非负矩阵分解 |
| 观点挖掘 | 预测与文本相关的情感? | 自然语言工具包(NLTK) |
| 推荐系统 | 向用户推销什么产品/项目? | 基于内容的过滤、协作过滤 |
| 文本分类 | 预测文档属于已知类别的概率? | 循环神经网络(RNN),二元或多类分类模型 |
| 图像分类 | 预测图像属于已知类别的概率。 | 卷积神经网络(CNN),二进制或多类分类模型 |
技巧
对于初学者来说,构建一个高效的模型可能是一项具有挑战性的任务。既然你已经学会了使用什么样的算法,我想给出我的 2 美分清单,让你在开始建模活动时记住。
从问题/假设开始,然后转向数据!
在使用数据制定要实现的目标之前,不要急于理解数据。从一系列问题开始,并与领域专家密切合作,以理解核心问题并构建问题陈述,这是一个很好的实践。这将有助于您选择正确的 ML 算法(监督与非监督),然后继续理解不同的数据源(图 7-1 )。
图 7-1
对数据的疑问/假设
不要从头开始重新发明轮子
ML 开源社区非常活跃;有很多有效的工具可用,而且更多的工具正在被开发/发布。因此,除非需要,否则不要试图在解决方案/算法/工具方面重新发明轮子(图 7-2 )。在冒险从头开始构建之前,尝试了解市场上存在哪些解决方案。
图 7-2
不要多此一举
从简单的模型开始
总是从简单的模型(如回归)开始,因为这些可以很容易地用通俗的语言向任何非技术人员解释(图 7-3 )。这将有助于您和主题专家理解变量关系,并获得对模型的信心。此外,它将极大地帮助您创建正确的特征。仅当您看到模型性能显著提高时,才转向复杂模型。
图 7-3
从一个简单的模型开始
专注于特征工程
相关特性导致高效的模型,而不是更多的特性!请注意,包含大量特征可能会导致过度拟合问题。在模型中包含相关特性是构建高效模型的关键。请记住,特征工程部分是作为一种艺术形式来谈论的,是竞争 ML 的关键区别。正确的配料混合到正确的数量是美味食物的秘密;类似地,将相关/正确的特征传递给 ML 算法是高效模型的秘密(图 7-4 )。
图 7-4
特征工程是一门艺术
当心普通的洗钱进口商
小心处理一些常见的 ML 欺骗,如数据质量问题(如缺失数据、异常值、分类数据、缩放)、分类的不平衡数据集、过拟合和欠拟合。使用第三章中讨论的处理数据质量问题的适当技术和第四章中讨论的技术,如集合技术和超参数调整,以提高模型性能。
快乐的机器学习
我希望这次用简化的六个步骤进行机器学习的探索是值得的,我希望这能帮助你开始一个新的旅程,将它们应用于现实世界的问题。我祝你一切顺利,并在今后的探索中取得成功。