提交 4fa135eb 编写于 作者: W wizardforcel

2020-07-11 16:44:16

上级 20d6bd61

要显示的变更太多。

To preserve performance only 1000 of 1000+ files are displayed.
# 第 1 章 探索和转换数据
TensorFlow 是用于使用数据流图进行数值计算的开源软件库。 图中的节点表示数学运算,而图的边缘表示在它们之间传递的多维数据数组(张量)。
该库包含各种函数,使您能够实现和探索用于图像和文本处理的最先进的卷积神经网络(CNN)和循环神经网络(RNN)架构。 由于复杂的计算以图的形式排列,因此 TensorFlow 可用作框架,使您能够轻松开发自己的模型并将其用于机器学习领域。
它还能够在从 CPU 到移动处理器(包括高度并行的 GPU 计算)的大多数异构环境中运行,并且新的服务架构可以在所有指定选项的非常复杂的混合环境中运行:
![Exploring and Transforming Data](img/00002.jpg)
\ No newline at end of file
# TensorFlow 的主要数据结构 -- 张量
TensorFlow 的数据管理基于张量。 张量是来自数学领域的概念,并且是作为向量和矩阵的线性代数项的概括而开发的。
专门讨论 TensorFlow 时,张量只是在张量对象中建模的带类型的多维数组,带有其他操作。
## 张量属性 -- 阶数,形状和类型
如前所述,TensorFlow 使用张量数据结构表示所有数据。 任何张量都具有静态类型和动态尺寸,因此您可以实时更改张量的内部组织。
张量的另一个特性是,只有张量类型的对象才能在计算图中的节点之间传递。
现在让我们看一下张量的属性是什么(从现在开始,每次使用张量这个词时,我们都将引用 TensorFlow 的张量对象)。
### 张量阶数
张量阶数表示张量的维度方面,但与矩阵阶数不同。 它表示张量所处的维数,而不是行/列或等效空间中张量扩展的精确度量。
秩张量等于向量,秩张量是矩阵。 对于第二张量,您可以使用语法 t [i,j]访问任何元素。 对于三阶张量,您将需要使用 t [i,j,k]来寻址元素,依此类推。
在下面的示例中,我们将创建一个张量,并访问其分量之一:
```py
>>> import tensorflow as tf
>>> tens1 = tf.constant([[[1,2],[2,3]],[[3,4],[5,6]]])
>>> print sess.run(tens1)[1,1,0]
5
```
这是三阶张量,因为在包含矩阵的每个元素中都有一个向量元素:
| 秩 | 数学实体 | 代码定义示例 |
| --- | --- | --- |
| 0 | 标量 | `scalar = 1000` |
| 1 | 向量 | `vector = [2, 8, 3]` |
| 2 | 矩阵 | `matrix = [[4, 2, 1], [5, 3, 2], [5, 5, 6]]` |
| 3 | 3 阶张量 | `tensor = [[[4], [3], [2]], [[6], [100], [4]], [[5], [1], [4]]]` |
| n | n 阶张量 | ... |
### 张量形状
TensorFlow 文档使用三种符号约定来描述张量维数:阶数,形状和维数。 下表显示了它们之间的相互关系:
| 秩 | 形状 | 维度数量 | 例 |
| --- | --- | --- | --- |
| 0 | `[]` | 0 | `4` |
| 1 | `[D0]` | 1 | `[2]` |
| 2 | `[D0,D1]` | 2 | `[6, 2]` |
| 3 | `[D0,D1,D2]` | 3 | `[7, 3, 2]` |
| n | `[D0,D1,... Dn-1]` | d | 形状为`[D0, D1, ..., Dn-1]`的张量。 |
在下面的示例中,我们创建一个样本阶数三张量,并打印其形状:
![Tensor shape](img/00003.jpg)
### 张量数据类型
除了维数外,张量还具有固定的数据类型。 您可以将以下任意一种数据类型分配给张量:
| 数据类型 | Python 类型 | 描述 |
| --- | --- | --- |
| `DT_FLOAT` | `tf.float32` | 32 位浮点。 |
| `DT_DOUBLE` | `tf.float64` | 64 位浮点。 |
| `DT_INT8` | `tf.int8` | 8 位有符号整数。 |
| `DT_INT16` | `tf.int16` | 16 位有符号整数。 |
| `DT_INT32` | `tf.int32` | 32 位有符号整数。 |
| `DT_INT64` | `tf.int64` | 64 位有符号整数。 |
| `DT_UINT8` | `tf.uint8` | 8 位无符号整数。 |
| `DT_STRING` | `tf.string` | 可变长度字节数组。 张量的每个元素都是一个字节数组。 |
| `DT_BOOL` | `tf.bool` | 布尔。 |
## 创建新的张量
我们可以创建自己的张量,也可以从著名的 numpy 库派生它们。 在以下示例中,我们创建一些 numpy 数组,并对其进行一些基本数学运算:
```py
import tensorflow as tf
import numpy as np
x = tf.constant(np.random.rand(32).astype(np.float32))
y= tf.constant ([1,2,3])
```
### 从 numpy 到张量,以及反向
TensorFlow 可与 numpy 互操作,通常`eval()`函数调用将返回一个 numpy 对象,准备与标准数值工具一起使用。
### 提示
我们必须注意,张量对象是操作结果的符号句柄,因此它不保存其包含的结构的结果值。 因此,我们必须运行`eval()`方法来获取实际值,该值等于`Session.run(tensor_to_eval)`
在此示例中,我们构建了两个 numpy 数组,并将它们转换为张量:
```py
import tensorflow as tf #we import tensorflow
import numpy as np #we import numpy
sess = tf.Session() #start a new Session Object
x_data = np.array([[1.,2.,3.],
[3.,2.,6.]]) # 2x3 matrix
x = tf.convert_to_tensor(x_data, dtype=tf.float32) #Finally, we create the tensor, starting from the fload 3x matrix
```
#### 有用的方法
`tf.convert_to_tensor`:此函数将各种类型的 Python 对象转换为张量对象。 它接受 tensorobjects,numpy 数组,Python 列表和 Python 标量。
## 完成工作 -- 与 TensorFlow 交互
与大多数 Python 模块一样,TensorFlow 允许使用 Python 的交互式控制台:
![Getting things done - interacting with TensorFlow](img/00004.jpg)
与 Python 的解释器和 TensorFlow 库轻松交互
在上图中,我们调用 Python 解释器(通过简单地调用 Python)并创建常量类型的张量。 然后我们再次调用它,Python 解释器显示张量的形状和类型。
我们还可以使用 IPython 解释器,该解释器将允许我们采用与笔记本样式工具(例如 Jupyter)更兼容的格式:
![Getting things done - interacting with TensorFlow](img/00005.jpg)
IPython 提示
在谈论以交互方式运行 TensorFlow 会话时,最好使用`InteractiveSession`对象。
与普通的`tf.Session`类不同,`tf.InteractiveSession`类将自身安装为构造时的默认会话。 因此,当您尝试评估张量或运行操作时,将不需要传递`Session`对象来指示它所引用的会话。
\ No newline at end of file
# k 最近邻
k 最近邻(k-nn)是一种简单的经典聚类方法,它将很好地介绍此类技术,着眼于每个样本的附近,并假设每个新样本都应属于的类别。 已经知道的数据点。
![k-nearest neighbors](img/00029.jpg)
## k 最近邻的力学
k-nn 可以在我们的多种配置中实现,但是在本章中,我们将使用“半监督”方法。 我们将从一定数量的已分配样本开始,稍后我们将根据训练集的特征猜测集群成员。
![Mechanics of k-nearest neighbors](img/00030.jpg)
最近邻算法
在上图中,我们可以看到该算法的细分。 可以通过以下步骤进行总结:
1. 我们将先前已知的样本放在数据结构上。
2. 然后,我们读取要分类的下一个样本,并计算从新样本到训练集的每个样本的欧几里得距离。
3. 我们通过根据欧几里得距离选择最近的样本的类别来确定新元素的类别。 k-nn 方法需要对 k 个最接近的样本进行投票。
4. 我们重复该过程,直到没有剩余的样本为止。
### k-nn 的优缺点
这种方法的优点是:
* 简单; 无需调整参数
* 没有正规训练; 我们只需要更多的训练实例来改进模型
缺点:
* 计算昂贵(必须计算点与每个新样本之间的所有距离)
## 有用库的实用示例
在以下各节中,我们将讨论一些有用的库。
### matplotlib 绘图库
数据绘图是数据科学学科不可或缺的一部分。 因此,我们需要一个非常强大的框架来绘制结果。 对于此任务,我们没有在 TensorFlow 中实现的通用解决方案,我们将使用 matplotlib 库。
在 matplotlib 站点(`http://matplotlib.org/`)中,定义为:
> “ matplotlib 是一个 Python 2D 绘图库,它以各种硬拷贝格式和跨平台的交互式环境生成出版物质量的图形。”
#### 合成样本的数据绘图
在此示例中,我们将生成一个包含 100 个随机数的列表,生成样本图,并将结果保存在图形文件中:
```py
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
with tf.Session() as sess:
fig, ax = plt.subplots()
ax.plot(tf.random_normal([100]).eval(), tf.random_normal([100] ).eval(),'o')
ax.set_title('Sample random plot for TensorFlow')
plt.savefig("result.png")
```
这是结果图像:
![Sample synthetic data plotting](img/00031.jpg)
使用 TensorFlow 和 matplotlib 生成的示例图
### 提示
为了查看 scikit 数据集模块的更一般的解释,请参考 [matplotlib.org](http://matplotlib.org/)
### scikit-learn 数据集模块
TensorFlow 当前未实现用于轻松生成合成数据集的方法。 因此,我们将使用`sklearn`库作为帮助程序。
#### 关于 scikit-learn 库
[从其网站](http://scikit-learn.org/stable/)
> “ scikit-learn(以前为 scikits.learn)是针对 Python 编程语言的开源机器学习库。它具有各种分类,回归和聚类模型,旨在与 Python 数字和科学库 NumPy 和 SciPy 互操作。”
在此示例中,我们将使用数据集模块,该模块处理许多众所周知的合成和现场提取的数据集的生成和加载。
### 提示
为了查看 scikit 数据集模块的更一般的解释,请参考[此链接](http://scikit-learn.org/stable/datasets/)
### 合成数据集类型
我们将使用一些生成的数据集类型:
![Synthetic dataset types](img/00032.jpg)
Blob,圆和月亮数据集类型
### Blob 数据集
该数据集是测试简单聚类算法的理想选择。 不会出现问题,因为数据是一致地分组的,并且类别的分离很明确。
#### 采用的方法
以下方法用于所采用的方法:
```py
sklearn.datasets.make_blobs(n_samples=100, n_features=2, centers=3, cluster_std=1.0, center_box=(-10.0, 10.0), shuffle=True, random_state=None)
```
在这里,`n_samples`是数据总数,`n_features`是数据的列数或特征数,`centers`是中心列表或许多随机中心,`cluster_std`是标准偏差,`center_box`是随机生成中心时每个聚类中心的边界框,`shuffle`指示是否必须对样本进行混洗,`random_state`是随机种子。
### 圆圈数据集
这是在其他圆圈中具有圆圈的数据集。 这是一个非线性的,可分离的问题,因此需要通过非线性模型来解决。 这排除了诸如 k 均值的简单算法。 在本章中,我们将尝试使用它来阐明观点。
#### 采用的方法
以下方法用于所采用的方法:
```py
sklearn.datasets.make_circles(n_samples=100,shuffle=True,noise=None, random_state=None,factor=0.8)
```
在这里,`n_samples`是数据总数,`shuffle`表示是否必须对样本进行混洗,`noise`是要应用于循环数据的随机量的数量,`random_state`是随机种子,并且`factor`是圆之间的比例因子。
### 月亮数据集
这是另一个非线性问题,但是具有另一种类型的类分离,因为没有诸如圆环之类的闭合。
\ No newline at end of file
# 项目 1 -- 在合成数据集上进行 k 均值聚类
## 数据集说明和加载
在本章中,我们将使用生成的数据集,这些数据集经过特殊设计以具有特殊的属性。 目标属性中的两个是类别线性分离的可能性以及是否存在明显分离的群集。
### 生成数据集
通过这些行,我们创建了数据结构,其中将包含用于解决方案的所有元素,即:
```py
centers = [(-2, -2), (-2, 1.5), (1.5, -2), (2, 1.5)]
data, features = make_blobs (n_samples=200, centers=centers, n_features = 2, cluster_std=0.8, shuffle=False, random_state=42)
```
通过 matplotlib 绘制数据集图:
```py
ax.scatter(np.asarray(centers).transpose()[0], np.asarray(centers).transpose()[1], marker = 'o', s = 250)
plt.plot()
```
## 模型架构
points 变量包含数据集点的 2D 坐标,质心变量将包含组中心点的坐标,`cluster_assignments`变量包含每个数据元素的质心索引。
例如,`cluster_assignments[2] = 1`表示`data[2]`数据点属于具有中心重心`1`的群集。 重心`1`的位置位于`centroids[1]`中。
```py
points=tf.Variable(data)
cluster_assignments = tf.Variable(tf.zeros([N], dtype=tf.int64))
centroids = tf.Variable(tf.slice(points.initialized_value(), [0,0], [K,2]))
```
然后,我们可以使用 matplotlib 绘制这些质心的位置:
```py
fig, ax = plt.subplots()
ax.scatter(np.asarray(centers).transpose()[0], np.asarray(centers).transpose()[1], marker = 'o', s = 250)
plt.show()
```
![Model architecture](img/00033.jpg)
初始中心播种
## 损失函数描述和优化器循环
然后,我们将对所有质心进行 N 份复制,对每个点进行 K 份复制,对每个点进行 N x K 份复制,因此我们可以针对每个维度计算出每个点与每个质心之间的距离:
```py
rep_centroids = tf.reshape(tf.tile(centroids, [N, 1]), [N, K, 2])
rep_points = tf.reshape(tf.tile(points, [1, K]), [N, K, 2])
sum_squares = tf.reduce_sum(tf.square(rep_points - rep_centroids),
reduction_indices=2)
```
然后,我们对所有维度执行总和,并获得最低总和的索引(这将是分配给每个点的质心或聚类的索引):
```py
best_centroids = tf.argmin(sum_squares, 1)
```
质心也将使用完整源代码中定义的`bucket:mean`函数进行更新。
## 停止条件
这是新质心和分配不变的停止条件:
```py
did_assignments_change = tf.reduce_any(tf.not_equal(best_centroids, cluster_assignments))
```
在这里,我们使用`control_dependencies`来计算是否需要更新质心:
```py
with tf.control_dependencies([did_assignments_change]):
do_updates = tf.group(
centroids.assign(means),
cluster_assignments.assign(best_centroids))
```
## 结果描述
程序执行后,我们得到以下输出:
![Results description](img/00034.jpg)
这是一轮迭代后质心变化的汇总图,其中绘制了从算法生成的原始聚类。
在下图中,我们针对这种明显分离的情况表示了 k 均值算法在应用中的不同阶段:
![Results description](img/00035.jpg)
每次迭代的质心变化
## 完整源代码
以下是完整的源代码:
```py
import tensorflow as tf
import numpy as np
import time
import matplotlib
import matplotlib.pyplot as plt
from sklearn.datasets.samples_generator import make_blobs
from sklearn.datasets.samples_generator import make_circles
DATA_TYPE = 'blobs'
# Number of clusters, if we choose circles, only 2 will be enough
if (DATA_TYPE == 'circle'):
K=2
else:
K=4
# Maximum number of iterations, if the conditions are not met
MAX_ITERS = 1000
start = time.time()
centers = [(-2, -2), (-2, 1.5), (1.5, -2), (2, 1.5)]
if (DATA_TYPE == 'circle'):
data, features = make_circles(n_samples=200, shuffle=True, noise= 0.01, factor=0.4)
else:
data, features = make_blobs (n_samples=200, centers=centers, n_features = 2, cluster_std=0.8, shuffle=False, random_state=42)
fig, ax = plt.subplots()
ax.scatter(np.asarray(centers).transpose()[0], np.asarray(centers).transpose()[1], marker = 'o', s = 250)
plt.show()
fig, ax = plt.subplots()
if (DATA_TYPE == 'blobs'):
ax.scatter(np.asarray(centers).transpose()[0], np.asarray(centers).transpose()[1], marker = 'o', s = 250)
ax.scatter(data.transpose()[0], data.transpose()[1], marker = 'o', s = 100, c = features, cmap=plt.cm.coolwarm )
plt.plot()
points=tf.Variable(data)
cluster_assignments = tf.Variable(tf.zeros([N], dtype=tf.int64))
centroids = tf.Variable(tf.slice(points.initialized_value(), [0,0], [K,2]))
sess = tf.Session()
sess.run(tf.initialize_all_variables())
rep_centroids = tf.reshape(tf.tile(centroids, [N, 1]), [N, K, 2])
rep_points = tf.reshape(tf.tile(points, [1, K]), [N, K, 2])
sum_squares = tf.reduce_sum(tf.square(rep_points - rep_centroids),
reduction_indices=2)
best_centroids = tf.argmin(sum_squares, 1)
did_assignments_change = tf.reduce_any(tf.not_equal(best_centroids, cluster_assignments))
def bucket_mean(data, bucket_ids, num_buckets):
total = tf.unsorted_segment_sum(data, bucket_ids, num_buckets)
count = tf.unsorted_segment_sum(tf.ones_like(data), bucket_ids, num_buckets)
return total / count
means = bucket_mean(points, best_centroids, K)
with tf.control_dependencies([did_assignments_change]):
do_updates = tf.group(
centroids.assign(means),
cluster_assignments.assign(best_centroids))
changed = True
iters = 0
fig, ax = plt.subplots()
if (DATA_TYPE == 'blobs'):
colourindexes=[2,1,4,3]
else:
colourindexes=[2,1]
while changed and iters < MAX_ITERS:
fig, ax = plt.subplots()
iters += 1
[changed, _] = sess.run([did_assignments_change, do_updates])
[centers, assignments] = sess.run([centroids, cluster_assignments])
ax.scatter(sess.run(points).transpose()[0], sess.run(points).transpose()[1], marker = 'o', s = 200, c = assignments, cmap=plt.cm.coolwarm )
ax.scatter(centers[:,0],centers[:,1], marker = '^', s = 550, c = colourindexes, cmap=plt.cm.plasma)
ax.set_title('Iteration ' + str(iters))
plt.savefig("kmeans" + str(iters) +".png")
ax.scatter(sess.run(points).transpose()[0], sess.run(points).transpose()[1], marker = 'o', s = 200, c = assignments, cmap=plt.cm.coolwarm )
plt.show()
end = time.time()
print ("Found in %.2f seconds" % (end-start)), iters, "iterations"
print "Centroids:"
print centers
print "Cluster assignments:", assignments
```
这是观察算法机制的最简单情况。 当数据来自真实世界时,这些类通常没有那么清晰地分开,并且标记数据样本更加困难。
## 圆合成数据上的 k 均值
对于圆图,我们观察到这种数据表征并不容易用一组简单的方法表示。 如图所示,两个圆要么共享一个质心的位置,要么真的很接近,因此我们无法预测明确的结果:
![k-means on circle synthetic data](img/00036.jpg)
圆型数据集
对于此数据集,我们仅使用两个类来确保了解此算法的主要缺点:
![k-means on circle synthetic data](img/00037.jpg)
k 均值应用于圆形综合数据集
如我们所见,初始中心向样本数量最集中的区域漂移,因此将数据线性划分。 这是我们现阶段使用的简单模型的局限性之一。 为了处理非线性可分离性样本,我们可以尝试本章范围以外的其他统计方法,例如基于密度的带噪应用空间聚类(DBSCAN)。
\ No newline at end of file
# 项目 2 -- 综合数据集上的最近邻
在这个项目中,我们将加载一个数据集,使用该数据集,以前的算法(k 均值)在分离类时遇到问题。
## 数据集生成
该数据集是第一个示例中具有两个类的相同循环类数据集,但是这次我们将通过增加一些噪声(从`0.01``0.12`)来增加错误概率:
```py
data, features = make_circles(n_samples=N, shuffle=True, noise=0.12,factor=0.4)
```
这是生成的训练数据图:
![Dataset generation](img/00038.jpg)
## 模型架构
将保留数据的变量只是原始数据和测试列表,其中将包含计算出的测试数据类:
```py
data, features = make_circles(n_samples=N, shuffle=True, noise= 0.12, factor=0.4)
tr_data, tr_features= data[:cut], features[:cut]
te_data,te_features=data[cut:], features[cut:]
test=[]
```
## 损失函数说明
在聚类中,我们将使用函数来优化为欧式距离,与第 1 章,探索和转换数据相同。 它是在集群分配循环上计算的,获取从新点到现有训练点的距离,要求最小值的索引,然后使用该索引搜索最近的邻居的类:
```py
distances = tf.reduce_sum(tf.square(tf.sub(i , tr_data)),reduction_indices=1)
neighbor = tf.arg_min(distances,0)
```
## 停止条件
在这个简单的示例中,一旦访问了测试分区的所有元素,我们将完成操作。
## 结果描述
这是测试数据类分布的图形,在这里我们可以看到清晰分开的类。 我们可以观察到,至少在此有限的数据集范围内,此方法比非重叠,斑点优化,k 均值方法更好。
![Results description](img/00039.jpg)
## 完整源代码
以下是完整的源代码:
```py
import tensorflow as tf
import numpy as np
import time
import matplotlib
import matplotlib.pyplot as plt
from sklearn.datasets.samples_generator import make_circles
N=210
K=2
# Maximum number of iterations, if the conditions are not met
MAX_ITERS = 1000
cut=int(N*0.7)
start = time.time()
data, features = make_circles(n_samples=N, shuffle=True, noise= 0.12, factor=0.4)
tr_data, tr_features= data[:cut], features[:cut]
te_data,te_features=data[cut:], features[cut:]
test=[]
fig, ax = plt.subplots()
ax.scatter(tr_data.transpose()[0], tr_data.transpose()[1], marker = 'o', s = 100, c = tr_features, cmap=plt.cm.coolwarm )
plt.plot()
sess = tf.Session()
sess.run(tf.initialize_all_variables())
for i, j in zip(te_data, te_features):
distances = tf.reduce_sum(tf.square(tf.sub(i , tr_data)),reduction_indices=1)
neighbor = tf.arg_min(distances,0)
test.append(tr_features[sess.run(neighbor)])
print test
fig, ax = plt.subplots()
ax.scatter(te_data.transpose()[0], te_data.transpose()[1], marker = 'o', s = 100, c = test, cmap=plt.cm.coolwarm )
plt.plot()
end = time.time()
print ("Found in %.2f seconds" % (end-start))
print "Cluster assignments:", test
```
\ No newline at end of file
# 总结
在本章中,我们简单地概述了一些我们可以实现的最基本的模型,但是尝试在解释中尽可能地详细。
从现在开始,我们将能够生成综合数据集,从而使我们能够快速测试模型对于不同数据配置的适当性,从而评估它们的优缺点,而不必加载具有大量未知特征的模型。
此外,我们已经实现了第一个迭代方法并测试了收敛性,该任务将以类似的方式在后续章节中继续进行,但是将使用更精细,更精确的方法。
在下一章中,我们将使用线性函数解决分类问题,并且首次使用训练集中的先前数据来学习其特征。 这是监督学习技术的目标,通常对于解决许多现实生活中的问题更有用。
\ No newline at end of file
# 第 3 章 线性回归
在本章中,我们将开始应用机器学习项目中使用的所有标准步骤,以便使用一条使误差和损失函数最小化的线来拟合先前给定的数据。
在上一章中,我们看到了范围有限和许多可能解决方案的问题。 这些类型的模型还与定性评估类型相关,即基于先前的标签为样本分配标签。 通常在与社会领域有关的问题中发现该结果。
我们还可能对预测(先前建模的)函数的确切数字输出值感兴趣。 这种方法类似于物理领域,可用于在事先了解一系列历史值的情况下预测温度或湿度或某种商品的价值,这称为回归分析。
在线性回归的情况下,我们在输入变量和输出变量之间寻找线性关系表示的确定关系。
\ No newline at end of file
# 单变量线性建模函数
如前所述,在线性回归中,我们尝试找到一个线性方程,以最小化数据点和建模线之间的距离。
此关系可以用以下标准线性函数表示:
![Univariate linear modelling function](img/00040.jpg)
模型函数采用以下形式:
在这里,`ss0``bias`是截距,`x`的函数值为零,`ss1`是建模线的斜率。 变量`x`通常被称为自变量,`y`被称为因变量,但它们也可以分别称为回归变量和响应变量。
## 样本数据生成
在下面的示例中,我们将基于`ss0` = `2.0`的线,加上最大幅度为`0.4`的垂直噪声,生成近似样本随机分布。
```py
In[]:
#Indicate the matplotlib to show the graphics inline
%matplotlib inline
import matplotlib.pyplot as plt # import matplotlib
import numpy as np # import numpy
trX = np.linspace(-1, 1, 101) # Linear space of 101 and [-1,1]
#Create The y function based on the x axis
trY = 2 * trX + np.random.randn(*trX.shape) * 0.4 + 0.2
plt.figure() # Create a new figure
plt.scatter(trX,trY) #Plot a scatter draw of the random datapoints
# Draw one line with the line function
plt.plot (trX, .2 + 2 * trX)
```
结果图将如下所示:
![Sample data generation](img/00041.jpg)
加噪声线性采样和线性函数
\ No newline at end of file
# 成本函数的确定
与所有机器学习技术一样,我们必须确定一个误差函数,我们需要将其最小化,这表明解决问题的适当性。
用于线性回归的最常用的`cost`函数称为最小二乘。
## 最小二乘
为了计算函数的最小二乘误差,我们通常会寻找一种测量点与建模线的接近程度的方法。 因此,我们定义了一个函数,用于测量每个元组`x[n]``y[n]`与建模线的对应值之间的距离。
对于 2D 回归,我们有一个数字元组`(X[0],Y[0]),(X[1],Y[1])...(X[n],Y[n])`的列表,通过最小化以下函数,可以找到`β[0]``β[1]`的值:
![Least squares](img/00042.jpg)
简单来说,求和代表预测值与实际值之间的欧几里得距离之和。
进行运算的原因是,平方误差的总和为我们提供了一个唯一且简单的全局数,预期数与实数之差为我们提供了适当的距离,平方幂为我们提供了一个正数,这会惩罚一个整数。 多于线性的时尚。
\ No newline at end of file
# 最小化成本函数
下一步是设置最小化`cost`函数的方法。 在线性演算中,定位极小值任务的基本特征之一被简化为计算函数的导数并寻找其零点。 为此,该函数必须具有导数,最好是凸的。 可以证明最小二乘函数符合这两个条件。 这对于避免已知的局部极小问题非常有用。
![Minimizing the cost function](img/00043.jpg)
损失函数表示
## 最小二乘的一般最小值
我们尝试解决的问题(最小二乘)可以用矩阵形式表示:
![General minima for least squares](img/00044.jpg)
在此,`J`是成本函数,具有以下解决方案:
![General minima for least squares](img/00045.jpg)
在本章中,我们将使用迭代方法梯度下降,该方法将在以后的章节中以更通用的方式使用。
## 迭代方法 -- 梯度下降
梯度下降本身就是一种迭代方法,并且是机器学习领域中最常用的优化算法。 考虑到可以用它优化的参数组合的复杂性,它结合了简单的方法和良好的收敛速度。
2D 线性回归从具有随机定义的权重或线性系数乘数的函数开始。 定义第一个值后,第二步是以以下形式应用递归函数:
![Iterative methods - gradient descent](img/00046.jpg)
在该方程式中,我们可以轻松推导该方法的机理。 我们从一组初始系数开始,然后朝函数最大变化的相反方向移动。 `α`变量被称为 step,将影响我们在梯度搜索方向上移动最小的距离。
最后一步是可选地测试迭代之间的更改,并查看更改是否大于 epsilon 或检查是否达到了迭代次数。
如果函数不是凸函数,建议使用随机值多次运行梯度下降,然后选择成本值最低的系数。 在非凸函数的情况下,梯度下降最终以最小值出现,这可能是局部的。 因此,对于非凸函数,结果取决于初始值,建议将它们随机设置多次,并在所有解决方案中选择成本最低的解决方案。
\ No newline at end of file
# 示例部分
现在让我们讨论有用的库和模块。
## TensorFlow 中的优化器方法 -- 训练模块
训练或参数优化阶段是机器学习工作流程的重要组成部分。
为此,TensorFlow 具有一个`tf.train`模块,该模块是一组对象的帮助程序,致力于实现数据科学家所需的各种不同优化策略。 此模块提供的主要对象称为优化器。
### `tf.train.Optimizer`类
`Optimizer`类允许您为`loss`函数计算梯度并将其应用于模型的不同变量。 在最著名的算法子类中,我们找到了梯度下降,Adam 和 Adagrad。
关于该类的一个主要提示是`Optimizer`类本身无法实例化。 子类之一。
如前所述,TensorFlow 允许您以符号方式定义函数,因此梯度也将以符号方式应用,从而提高了结果的准确率以及要应用于数据的操作的通用性。
为了使用`Optimizer`类,我们需要执行以下步骤:
1. 创建具有所需参数的`Optimizer`(在这种情况下为梯度下降)。
```py
opt = GradientDescentOptimizer(learning_rate= [learning rate])
```
2.`cost`函数创建一个调用`minimize`方法的操作。
```py
optimization_op = opt.minimize(cost, var_list=[variables list])
```
`minimize`方法具有以下形式:
```py
tf.train.Optimizer.minimize(loss, global_step=None, var_list=None, gate_gradients=1, aggregation_method=None, colocate_gradients_with_ops=False, name=None)
```
主要参数如下:
* `loss`:这是一个张量,其中包含要最小化的值。
* `global_step``Optimizer`工作后,此变量将增加 1。
* `var_list`:包含要优化的变量。
### 提示
实际上,`optimize`方法结合了对`compute_gradients()``apply_gradients()`的调用。 如果要在应用梯度之前对其进行处理,请显式调用`compute_gradients()``apply_gradients()`,而不要使用此函数。 如果我们只想进行一步训练,就必须以`opt_op.run().`的形式执行`run`方法
### 其他优化器实例类型
以下是其他`Optimizer`实例类型:
* `tf.train.AdagradOptimizer`:这是一种基于参数频率的自适应方法,学习率单调下降。
* `tf.train.AdadeltaOptimizer`:这是对 Adagrad 的改进,它的学习率没有下降。
* `tf.train.MomentumOptimizer`:这是一种适应性方法,可解决尺寸之间的不同变化率。
* 并且还有其他更具体的参数,例如`tf.train.AdamOptimizer``tf.train.FtrlOptimizer``tf.train.RMSPropOptimizer`
\ No newline at end of file
# 示例 1 -- 单变量线性回归
现在,我们将在一个项目中工作,在该项目中,我们将应用前面几页中简要介绍的所有概念。 在此示例中,我们将创建一个近似线性分布; 之后,我们将创建一个回归模型,该模型试图拟合线性函数以最小化误差函数(由最小二乘法定义)。
给定一个新样本,该模型将使我们能够预测输入值的结果。
## 数据集说明
对于此示例,我们将生成一个包含线性函数并添加噪声的合成数据集:
```py
import TensorFlow as tf
import numpy as np
trX = np.linspace(-1, 1, 101)
trY = 2 * trX + np.random.randn(*trX.shape) * 0.4 + 0.2 # create a y value which is approximately linear but with some random noise
```
使用这些线,我们可以将线表示为散点图和理想线函数。
```py
import matplotlib.pyplot as plt
plt.scatter(trX,trY)
plt.plot (trX, .2 + 2 * trX)
```
![Dataset description](img/00047.jpg)
生成的样本和原始线性函数无噪声
## 模型架构
1. 现在,我们创建一个变量来保存`x``y`轴中的值。 然后,我们将模型定义为`X`和权重`w`的乘积。
2. 然后,我们生成一些变量,并为其分配初始值以启动模型:
```py
In[]:
X = tf.placeholder("float", name="X") # create symbolic variables
Y = tf.placeholder("float", name = "Y")
```
3. 现在,我们通过将`name_scope`声明为`Model`来定义模型。 此作用域将其包含的所有变量分组,以形成具有同类实体的唯一实体。 在此范围内,我们首先定义一个函数,该函数接收`x`轴坐标,权重(斜率)和偏差的变量。 然后,我们创建一个新变量`objects,`来保存不断变化的参数,并使用`y_model`变量实例化该模型:
```py
with tf.name_scope("Model"):
def model(X, w, b):
return tf.mul(X, w) + b # just define the line as X*w + b0
w = tf.Variable(-1.0, name="b0") # create a shared variable
b = tf.Variable(-2.0, name="b1") # create a shared variable
y_model = model(X, w, b)
```
在仪表板上,您可以看到我们一直在收集的损失函数的图像。 在图部分中,放大模型时,您可以看到求和与乘法运算,参数变量`b0``b1`以及应用于模型的梯度运算,如下所示:
![Model architecture](img/00048.jpg)
## 成本函数描述和优化器循环
1.`Cost Function`中,我们创建了一个新的范围以包括该组的所有操作,并使用先前创建的`y_model`来说明用于计算损失的计算出的`y`轴值。
```py
with tf.name_scope("CostFunction"):
cost = (tf.pow(Y-y_model, 2)) # use sqr error for cost
```
2. 为了定义选择的`optimizer,`,我们初始化一个`GradientDescentOptimizer`,步骤将是`0.01`,这似乎是收敛的合理起点。
```py
train_op = tf.train.GradientDescentOptimizer(0.05).minimize(cost)
```
3. 现在是时候创建会话并初始化要保存在 TensorBoard 中进行查看的变量了。 在此示例中,我们将为每个迭代保存一个标量变量以及最后一个样本的误差结果。 我们还将图结构保存在文件中以供查看。
```py
sess = tf.Session()
init = tf.initialize_all_variables()
tf.train.write_graph(sess.graph,
'/home/ubuntu/linear','graph.pbtxt')
cost_op = tf.scalar_summary("loss", cost)
merged = tf.merge_all_summaries()
sess.run(init)
writer = tf.train.SummaryWriter('/home/ubuntu/linear',
sess.graph)
```
4. 对于模型训练,我们将目标设置为 100 次迭代,然后将每个样本发送到梯度下降的`train`操作。 每次迭代后,我们绘制建模线并将最后一个误差的值添加到`summary`中。
```py
In[]:
for i in range(100):
for (x, y) in zip(trX, trY):
sess.run(train_op, feed_dict={X: x, Y: y})
summary_str = sess.run(cost_op, feed_dict={X: x, Y: y})
writer.add_summary(summary_str, i)
b0temp=b.eval(session=sess)
b1temp=w.eval(session=sess)
plt.plot (trX, b0temp + b1temp * trX )
```
结果图如下: 我们可以看到初始行如何迅速收敛为更合理的结果:
![Cost function description and Optimizer loop](img/00049.jpg)
放大 CostFunction 范围后,我们可以看到幂和减法运算以及书面摘要,如下图所示:
![Cost function description and Optimizer loop](img/00050.jpg)
## 停止条件
## 结果描述
现在让我们检查参数结果,打印`w``b`变量的`run`输出:
```py
printsess.run(w) # Should be around 2
printsess.run(b) #Should be around 0.2
2.09422
0.256044
```
现在是时候再次以图形方式查看数据和建议的最后一行。
```py
plt.scatter(trX,trY)
plt.plot (trX, testb + trX * testw)
```
![Results description](img/00051.jpg)
## 使用 TensorBoard 查看结果
现在,让我们回顾一下保存在 TensorBoard 中的数据。
为了启动 TensorBoard,您可以转到 logs 目录并执行以下行:
```py
$ tensorboard --logdir=.
```
TensorBoard 将加载事件和图形文件,并且将在`6006`端口上监听。 然后,您可以从浏览器转到`localhost:6000`,然后查看 TensorBoard 仪表板,如下图所示:
![Reviewing results with TensorBoard](img/00052.jpg)
## 完整源代码
以下是完整的源代码:
```py
import matplotlib.pyplot as plt # import matplotlib
import numpy as np # import numpy
import tensorflow as tf
import numpy as np
trX = np.linspace(-1, 1, 101) #Create a linear space of 101 points between 1 and 1
trY = 2 * trX + np.random.randn(*trX.shape) * 0.4 + 0.2 #Create The y function based on the x axis
plt.figure() # Create a new figure
plt.scatter(trX,trY) #Plot a scatter draw of the random datapoints
plt.plot (trX, .2 + 2 * trX) # Draw one line with the line function
get_ipython().magic(u'matplotlib inline')
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
trX = np.linspace(-1, 1, 101)
trY = 2 * trX + np.random.randn(*trX.shape) * 0.4 + 0.2 # create a y value which is approximately linear but with some random noise
plt.scatter(trX,trY)
plt.plot (trX, .2 + 2 * trX)
X = tf.placeholder("float", name="X") # create symbolic variables
Y = tf.placeholder("float", name = "Y")
withtf.name_scope("Model"):
def model(X, w, b):
returntf.mul(X, w) + b # We just define the line as X*w + b0
w = tf.Variable(-1.0, name="b0") # create a shared variable
b = tf.Variable(-2.0, name="b1") # create a shared variable
y_model = model(X, w, b)
withtf.name_scope("CostFunction"):
cost = (tf.pow(Y-y_model, 2)) # use sqr error for cost function
train_op = tf.train.GradientDescentOptimizer(0.05).minimize(cost)
sess = tf.Session()
init = tf.initialize_all_variables()
tf.train.write_graph(sess.graph, '/home/ubuntu/linear','graph.pbtxt')
cost_op = tf.scalar_summary("loss", cost)
merged = tf.merge_all_summaries()
sess.run(init)
writer = tf.train.SummaryWriter('/home/ubuntu/linear', sess.graph)
fori in range(100):
for (x, y) in zip(trX, trY):
sess.run(train_op, feed_dict={X: x, Y: y})
summary_str = sess.run(cost_op, feed_dict={X: x, Y: y})
writer.add_summary(summary_str, i)
b0temp=b.eval(session=sess)
b1temp=w.eval(session=sess)
plt.plot (trX, b0temp + b1temp * trX )
printsess.run(w) # Should be around 2
printsess.run(b) #Should be around 0.2
plt.scatter(trX,trY)
plt.plot (trX, sess.run(b) + trX * sess.run(w))
```
![Full source code](img/00053.jpg)
![Full source code](img/00051.jpg)
\ No newline at end of file
# 处理计算工作流程 -- TensorFlow 的数据流程图
TensorFlow 的数据流图是模型计算如何工作的符号表示:
![Handling the computing workflow - TensorFlow's data flow graph](img/00006.jpg)
在 TensorBoard 上绘制的简单数据流图表示
简而言之,数据流图是一个完整的 TensorFlow 计算,表示为一个图,其中节点是操作,边是操作之间的数据流。
通常,节点执行数学运算,但也表示连接以输入数据或变量,或推出结果。
边缘描述节点之间的输入/输出关系。 这些数据边仅传输张量。 节点被分配给计算设备,并且一旦它们进入边缘上的所有张量都可用,就会异步并行执行。
所有运算都有一个名称,并表示一个抽象计算(例如,矩阵求逆或乘积)。
## 计算图构建
通常在库用户创建张量和模型将支持的操作时构建计算图,因此无需直接构建`Graph()`对象。 Python 张量构造函数,例如`tf.constant()`,会将必要的元素添加到默认图。 TensorFlow 操作也会发生同样的情况。
例如,`c = tf.matmul(a, b)`创建一个`MatMul`类型的操作,该操作将张量`a``b`作为输入并产生`c`作为输出。
### 有用的操作对象方法
* `tf.Operation.type`:返回操作的类型(例如`MatMul`
* `tf.Operation.inputs`:返回代表操作输入的张量对象列表
* `tf.Graph.get_operations()`:返回图中的操作列表
* `tf.Graph.version`:返回图的自动数字版本
## 馈送
TensorFlow 还提供了一种馈送机制,可将张量直接修补到图中的任何操作中。
提要用张量值临时替换操作的输出。 您将提要数据作为`run()`调用的参数提供。 提要仅用于传递给它的运行调用。 最常见的用例涉及通过使用`tf.placeholder()`创建特定的操作,使其指定为`feed`操作。
## 变量
在大多数计算中,图执行多次。 大多数张量都无法通过图的一次执行而幸存。 但是,变量是一种特殊的操作,它可以将句柄返回到持久可变的张量,该张量在图执行过程中仍然存在。 对于 TensorFlow 的机器学习应用,模型的参数通常存储在变量中保存的张量中,并在运行模型的训练图时进行更新。
### 变量初始化
要初始化变量,只需使用张量作为参数调用`Variable`对象构造函数。
在此示例中,我们使用`1000`零数组初始化了一些变量:
```py
b = tf.Variable(tf.zeros([1000]))
```
## 保存数据流程图
数据流图是使用 Google 的协议缓冲区编写的,因此以后可以使用多种语言进行读取。
### 图序列化语言 -- 协议缓冲区
协议缓冲区是一种不依赖语言,不依赖平台的可扩展机制,用于序列化结构化数据。 首先定义数据结构,然后可以使用专门生成的代码来使用多种语言进行读写。
#### 有用的方法
`tf.Graph.as_graph_def(from_version=None, add_shapes=False)`:返回此图的序列化`GraphDef`表示形式。
参数:
* `from_version`:如果设置了此选项,它将返回带有从该版本添加的节点的`GraphDef`
* `add_shapes`:如果`true`,则向每个节点添加一个 shape 属性
### 建立图的示例
在此示例中,我们将构建一个非常简单的数据流图,并观察生成的 protobuffer 文件的概述:
```py
import tensorflow as tf
g = tf.Graph()
with g.as_default():
import tensorflow as tf
sess = tf.Session()
W_m = tf.Variable(tf.zeros([10, 5]))
x_v = tf.placeholder(tf.float32, [None, 10])
result = tf.matmul(x_v, W_m)
print g.as_graph_def()
```
生成的 protobuffer(摘要)为:
```py
node {
name: "zeros"
op: "Const"
attr {
key: "dtype"
value {
type: DT_FLOAT
}
}
attr {
key: "value"
value {
tensor {
dtype: DT_FLOAT
tensor_shape {
dim {
size: 10
}
dim {
size: 5
}
}
float_val: 0.0
}
}
}
}
...
node {
name: "MatMul"
op: "MatMul"
input: "Placeholder"
input: "Variable/read"
attr {
key: "T"
value {
type: DT_FLOAT
}
}
...
}
versions {
producer: 8
}
```
\ No newline at end of file
# 示例 2 -- 多元线性回归
在此示例中,我们将处理涉及多个变量的回归问题。
这将基于 1993 年波士顿某些郊区不同价格的研究数据集。 它最初包含 13 个变量以及该处房产的平均价格。
与原始文件相比,文件中唯一的变化是删除了一个变量`(b)`,该变量在种族上对不同的郊区进行了概述。
除此之外,我们将选择一些我们认为具有线性条件可以建模的良好条件的变量。
## 有用的库和方法
本部分包含一个有用的库列表,我们将在此示例中以及本书其余部分中 TensorFlow 之外的部分中使用这些库,以帮助解决我们将要解决的各种问题。
### Pandas 库
当我们想快速读取并获得有关正常大小的数据文件的提示时,创建读取缓冲区和其他附加机制可能会减少开销。 这是熊猫当前的现实生活用例之一。
这是[ Pandas 网站](http://pandas.pydata.org/)的摘录:
> “ Pandas 是 BSD 许可的开放源代码库,为 Python 提供了高表现,易于使用的数据结构和数据分析工具。”
熊猫的主要特征如下:
* 它具有 CSV 和文本文件,MS Excel,SQL 数据库甚至面向科学的 HDF5 格式的读写文件功能。
* CSV 文件加载例程自动识别列标题并支持更直接的列寻址
* 数据结构自动转换为 NumPy 多维数组
## 数据集说明
数据集以 CSV 文件表示,我们将使用 Pandas 库打开它。
数据集包含以下变量:
* `CRIM`:按城镇划分的人均犯罪率
* `ZN`:划定面积超过 25,000 平方英尺的住宅用地的比例。
* `INDUS`:每个城镇的非零售业务英亩比例
* `CHAS`:查尔斯河虚拟变量(如果区域限制河流,则为 1;否则为 0)
* `NOX`:一氧化氮浓度(百万分之几)
* `RM`:每个住宅的平均房间数
* `AGE`:1940 年之前建造的自有住房的比例
* `DIS`:到五个波士顿就业中心的加权距离
* `RAD`:径向公路的可达性指数
* `TAX`:每 10,000 美元的全值财产税率
* `PTRATIO`:按城镇划分的师生比率
* `LSTAT`:人口状况降低%
* `MEDV`:自有住房的中位数价值,以$ 1000 为单位
在这里,我们有一个简单的程序,它将读取数据集并创建数据的详细说明:
```py
import tensorflow.contrib.learn as skflow
fromsklearn import datasets, metrics, preprocessing
import numpy as np
import pandas as pd
df = pd.read_csv("data/boston.csv", header=0)
printdf.describe()
```
这将输出数据集变量的统计摘要。 前六个结果如下:
```py
CRIM ZN INDUS CHAS NOX RM \
count 506.000000 506.000000 506.000000 506.000000 506.000000 506.000000
mean 3.613524 11.363636 11.136779 0.069170 0.554695 6.284634
std 8.601545 23.322453 6.860353 0.253994 0.115878 0.702617
min 0.006320 0.000000 0.460000 0.000000 0.385000 3.561000
25% 0.082045 0.000000 5.190000 0.000000 0.449000 5.885500
50% 0.256510 0.000000 9.690000 0.000000 0.538000 6.208500
75% 3.677082 12.500000 18.100000 0.000000 0.624000 6.623500
max 88.976200 100.000000 27.740000 1.000000 0.871000 8.780000
```
![Dataset description](img/00054.jpg)
## 模型架构
在此示例中,我们将使用的模型很简单,但是几乎包含了处理更复杂模型所需的所有元素。
在下图中,我们看到了整个设置的不同参与者:模型,CostFunction 和梯度。 TensorFlow 真正有用的功能是能够自动微分模型和函数。
![Model architecture](img/00055.jpg)
在这里,我们可以找到上一节中表示的变量的定义:`w``b`和模型线性方程。
```py
X = tf.placeholder("float", name="X") # create symbolic variables
Y = tf.placeholder("float", name = "Y")
withtf.name_scope("Model"):
w = tf.Variable(tf.random_normal([2], stddev=0.01), name="b0") # create a shared variable
b = tf.Variable(tf.random_normal([2], stddev=0.01), name="b1") # create a shared variable
def model(X, w, b):
returntf.mul(X, w) + b # We just define the line as X*w + b0
y_model = model(X, w, b)
```
![Model architecture](img/00056.jpg)
## 损失函数说明和优化器循环
在此示例中,我们将使用常用的均方误差,但是这次使用了多变量; 因此我们应用 reduce_mean 来收集不同维度上的误差值:
```py
withtf.name_scope("CostFunction"):
cost = tf.reduce_mean(tf.pow(Y-y_model, 2)) # use sqr error for cost function
train_op = tf.train.AdamOptimizer(0.1).minimize(cost)
```
![Loss function description and Optimizer loop](img/00057.jpg)
```py
for a in range (1,10):
cost1=0.0
fori, j in zip(xvalues, yvalues):
sess.run(train_op, feed_dict={X: i, Y: j})
cost1+=sess.run(cost, feed_dict={X: i, Y: i})/506.00
#writer.add_summary(summary_str, i)
xvalues, yvalues = shuffle (xvalues, yvalues)
```
## 停止条件
停止条件将仅由针对所有数据样本训练参数来确定外循环中确定的周期数。
## 结果描述
结果如下:
```py
1580.53295174
[ 2.25225258 1.30112672]
[ 0.80297691 0.22137061]
1512.3965525
[ 4.62365675 2.90244412]
[ 1.16225874 0.28009811]
1495.47174799
[ 6.52791834 4.29297304]
[ 0.824792270.17988272]
...
1684.6247849
[ 29.71323776 29.96078873]
[-0.68271929 -0.13493828]
1688.25864746
[ 29.78564262 30.09841156]
[-0.58272243 -0.08323665]
1684.27538102
[ 29.75390816 30.13044167]
[-0.59861398 -0.11895057]
```
从结果中我们可以看到,在训练的最后阶段,建模线同时基于以下系数:
`price = 0.6 x Industry + 29.75`
`price = 0.1 x Age + 30.13`
## 完整源代码
以下是完整的源代码:
```py
import matplotlib.pyplot as plt
import tensorflow as tf
import tensorflow.contrib.learn as skflow
from sklearn.utils import shuffle
import numpy as np
import pandas as pd
df = pd.read_csv("data/boston.csv", header=0)
printdf.describe()
f, ax1 = plt.subplots()
plt.figure() # Create a new figure
y = df['MEDV']
for i in range (1,8):
number = 420 + i
ax1.locator_params(nbins=3)
ax1 = plt.subplot(number)
plt.title(list(df)[i])
ax1.scatter(df[df.columns[i]],y) #Plot a scatter draw of the datapoints
plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0)
X = tf.placeholder("float", name="X") # create symbolic variables
Y = tf.placeholder("float", name = "Y")
with tf.name_scope("Model"):
w = tf.Variable(tf.random_normal([2], stddev=0.01), name="b0") # create a shared variable
b = tf.Variable(tf.random_normal([2], stddev=0.01), name="b1") # create a shared variable
def model(X, w, b):
return tf.mul(X, w) + b # We just define the line as X*w + b0
y_model = model(X, w, b)
with tf.name_scope("CostFunction"):
cost = tf.reduce_mean(tf.pow(Y-y_model, 2)) # use sqr error for cost function
train_op = tf.train.AdamOptimizer(0.001).minimize(cost)
sess = tf.Session()
init = tf.initialize_all_variables()
tf.train.write_graph(sess.graph, '/home/bonnin/linear2','graph.pbtxt')
cost_op = tf.scalar_summary("loss", cost)
merged = tf.merge_all_summaries()
sess.run(init)
writer = tf.train.SummaryWriter('/home/bonnin/linear2', sess.graph)
xvalues = df[[df.columns[2], df.columns[4]]].values.astype(float)
yvalues = df[df.columns[12]].values.astype(float)
b0temp=b.eval(session=sess)
b1temp=w.eval(session=sess)
for a in range (1,10):
cost1=0.0
for i, j in zip(xvalues, yvalues):
sess.run(train_op, feed_dict={X: i, Y: j})
cost1+=sess.run(cost, feed_dict={X: i, Y: i})/506.00
#writer.add_summary(summary_str, i)
xvalues, yvalues = shuffle (xvalues, yvalues)
print (cost1)
b0temp=b.eval(session=sess)
b1temp=w.eval(session=sess)
print (b0temp)
print (b1temp)
#plt.plot (trX, b0temp + b1temp * trX )
```
\ No newline at end of file
# 总结
在本章中,我们使用 TensorFlow 的训练实用程序构建了第一个具有标准损失函数的完整模型。 我们还建立了一个多元模型来说明多个维度来计算回归。 除此之外,我们使用 TensorBoard 在训练阶段观察变量的行为。
在下一章中,我们将开始使用非线性模型,通过它我们将更接近神经网络领域,这是 TensorFlow 的主要支持领域,其效用提供了巨大价值。
\ No newline at end of file
# 第 4 章 逻辑回归
在上一章中,我们已经看到了一种将现实的一部分建模为线性函数的方法,该函数具有独立变量,并且偏差最小化了误差函数。
除了某些非常明确定义的问题(预期结果是连续的变量和函数)之外,这种特殊的分析还不够。
但是,如果我们面对具有定性因变量的数据,将会发生什么? 例如,是否存在确定的特征; 受试者有金色的头发吗? 病人以前有病吗?
这些是我们将在本章中处理的问题。
\ No newline at end of file
# 问题描述
线性回归旨在解决的问题不是基于连续函数的值预测,这一次,我们想知道样本属于确定类别的可能性。
在本章中,我们将依靠线性模型的一般化来解决回归问题,但最终目标是解决分类问题,我们必须应用标签或将观察集中的所有元素分配给预定义的组。
![Problem description](img/00058.jpg)
在上图中,我们可以看到如何对旧问题和新问题进行分类。 第一个(线性回归)可以想象为价值不断增长的连续体。
另一个是基于`x`值的输出只能具有两个不同值的域。 在第二张图的特定情况下,我们可以看到对其中一个选项的特定偏向极端:在左侧,偏向 0 `y`值,在右侧偏向某个值。 共 1。
鉴于即使在进行回归从而寻找连续值的情况下,这种项也可能有些棘手,实际上,最终目标是为具有离散变量的分类问题建立预测。
此处的关键是要了解我们将获得与类有关的项目的概率,而不是完全离散的值。
\ No newline at end of file
# sigmoid 函数的前身 -- Logit 函数
在研究逻辑函数之前,我们将回顾该函数所基于的原始函数,并为其提供一些更一般的属性。
本质上,当我们谈论`logit`函数时,我们正在使用随机变量`p`的函数,更具体地说,是与伯努利分布相对应的函数。
## 伯努利分布
在解释理论细节之前,值得注意的是伯努利分布是一个随机变量,它具有:
* 取值为 0 且失败概率为`q = 1 - p`
* 取值为 1,成功概率为`p`
可以表示如下(对于具有伯努利分布的随机变量`X`):
![Bernoulli distribution](img/00059.jpg)
这是一种概率分布,它将以二元选项的形式表示事件的发生概率,就像我们要表示自己的变量(特征的存在,事件的发生,现象的因果关系等)一样。
## 链接函数
在尝试建立广义线性模型时,我们要从线性函数开始,并从因变量开始,获取到概率分布的映射。
由于选项具有二元性质,因此通常选择的分布是最近提到的 Bernoulli 分布,而倾向于 logistic 函数的链接函数是`logit`函数。
## Logit 函数
我们可以利用的可能变量之一是`p`等于 1 的几率的自然对数。 此函数称为`logit`函数:
![Logit function](img/00060.jpg)
我们也可以将`logit`函数称为对数奇数函数,因为对于给定的概率`p`,我们正在计算赔率的对数`(p/1-p)`
![Logit function](img/00061.jpg)
因此,正如我们可以直观地推断出的那样,用自变量的组合替换`X`,无论它们的值是什么,用从负无穷大到无穷大的任何出现替换`X`,我们将响应缩放到`0``1`
## Logit 反函数的重要性
假设我们计算`logit`函数的逆。 这将使我们编写以下函数:
![The importance of the logit inverse](img/00062.jpg)
此函数是`sigmoid`函数。
\ No newline at end of file
# sigmoid 函数
逻辑函数将帮助我们在新的回归任务中表示二元类别。
在下图中,您将找到`sigmoid`函数的图形表示:
![The logistic function](img/00063.jpg)
逻辑函数或 Sigmoid 的图形表示
## Logistic 函数作为线性建模概括
逻辑函数`δ(t)`定义如下:
![Logistic function as a linear modeling generalization](img/00064.jpg)
该方程式的正常解释是`t`代表一个简单的自变量。 但是,我们将改进此模型,并假定`t`是单个解释变量`x`的线性函数(对 t 是多个解释变量的线性组合的情况进行类似处理)。
然后,我们将`t`表示为:
![Logistic function as a linear modeling generalization](img/00065.jpg)
### 最终估计的回归方程
因此,我们从以下等式开始:
![Final estimated regression equation](img/00066.jpg)
使用所有这些元素,我们可以计算回归方程,这将为我们提供回归概率:
![Final estimated regression equation](img/00067.jpg)
下图将显示如何将从任意范围的映射最终转换为范围`[0, 1]`,该范围可以解释为表示事件发生的概率 p:
![Final estimated regression equation](img/00068.jpg)
什么影响会改变线性函数的参数? 它们是将更改`sigmoid`函数的中心斜率和从零开始的位移的值,从而使其可以更精确地减小回归值与实际数据点之间的误差。
## 逻辑函数的属性
函数空间中的每条曲线都可以通过可能适用的目标来描述。 对于 logistic 函数,它们是:
* 根据一个或多个独立变量对事件的概率`p`进行建模。 例如,鉴于先前的资格,被授予奖品的可能性。
* 对确定的观测值进行估计(这是回归部分)`p`,与事件未发生的可能性有关。
* 预测自变量变化对二元响应的影响。
* 通过计算某项属于确定类别的概率对观察进行分类。
### 损失函数
在上一节中,我们看到了近似的`p^`函数,该函数将对样本属于特定类别的概率进行建模。 为了衡量我们对解的近似程度,我们将寻找精心选择的损失函数。
该损失函数表示为:
![Loss function](img/00069.jpg)
该损失函数的主要特性是它不会以类似的方式惩罚误差,当误差增加到远远超过 0.5 时,误差惩罚因子会渐近增长。
## 多类应用 -- softmax 回归
到目前为止,我们仅针对两个类的情况进行分类,或者以概率语言对事件发生概率`p`进行分类。
在要决定两个以上类别的情况下,有两种主要方法: 一对一,一对全。
* 第一种技术包括计算许多模型,这些模型代表每个类别相对于所有其他类别的概率。
* 第二个由一组概率组成,其中我们代表一个类别相对于所有其他类别的概率。
* 第二种方法是`softmax`回归的输出格式,它是 n 个类的逻辑回归的概括。
因此,为了训练样本,我们将使用句柄`y(i)ε{1,...,K},`将二元标签`( y(i)ε{0,1})`更改为向量标签,其中`K`是类别数,标签 Y 可以采用`K`不同的值, 而不是只有两个。
因此,对于这种特定技术,给定测试输入`X`,我们想要针对`k=1,...,K`的每个值估计`P``y=k|x`)的概率。 `softmax`回归将输出`K`维向量(其元素总和为 1),从而为我们提供了`K`估计的概率。
在下图中,我们表示在单类和多类逻辑回归的概率映射上发生的映射:
![Multiclass application - softmax regression](img/00070.jpg)
### 成本函数
`softmax`函数的成本函数是自适应的交叉熵函数,该函数不是线性的,因此对大阶函数差异的惩罚要比对小阶函数的惩罚更大。
![Cost function](img/00071.jpg)
在这里,`c`是类别编号,`I`是各个训练样本索引,`yc`对于期望的类别为 1,对于其余类别为 0。
扩展这个方程,我们得到以下结果:
![Cost function](img/00072.jpg)
### 迭代方法的数据标准化
正如我们将在以下各节中看到的那样,对于逻辑回归,我们将使用`gradient descent`方法来最小化成本函数。
![Data normalization for iterative methods](img/00073.jpg)
此方法对特征数据的形式和分布非常敏感。
因此,我们将进行一些预处理,以便获得更好,更快的收敛结果。
我们将把这种方法的理论原因留给其他书籍,但我们将总结其原因,即通过归一化可以平滑误差表面,使迭代`gradient descent`更快地达到最小误差。
### 输出的单热表示
为了将`softmax`函数用作回归函数,我们必须使用一种称为单热编码的编码。 这种编码形式只是将变量的数字整数值转换为数组,其中将值列表转换为数组列表,每个数组的长度与该列表的最大值相同,并且每个数组的表示方式是在值的索引上添加 1,其余元素保持为 0。
例如,这将是单热编码形式的列表[1、3、2、4]的表示形式:
```py
[[0 1 0 0 0]
[0 0 0 1 0]
[0 0 1 0 0]
[0 0 0 0 1]]
```
\ No newline at end of file
# 示例 1 -- 单变量 logistic 回归
在第一个示例中,我们将使用单变量 logistic 回归(患者年龄)来估计心脏病的概率。
## 有用的库和方法
从 0.8 版开始,TensorFlow 提供了一种生成热点的方法。 用于此生成的函数是`tf.one_hot`,其形式如下:
```py
tf.one_hot(indices, depth, on_value=1, off_value=0, axis=None, dtype=tf.float32, name=None)
```
此函数生成通用的单热编码数据结构,该结构可以指定值,生成轴,数据类型等。
在生成的张量中,索引的指示值将采用`on_value`(默认值为`1`),其他值将具有`off_value`(默认`0`)。
`Dtype`是生成的张量的数据类型; 默认值为`float32`
depth 变量定义每个元素将具有多少列。 我们假设它在逻辑上应该为`max(indices) + 1`,但也可以将其切掉。
### TensorFlow 的 softmax 实现
在 TensorFlow 中应用`softmax`回归的方法包括`tf.nn.log_softmax, with the following form:`
```py
tf.nn.log_softmax(logits, name=None)
```
在这里,参数为:
* `logits`:张量必须为以下类型之一:`float32``float64` 形状为`[batch_size, num_classes]`的 2D
* `name`:操作的名称(可选)
此函数返回具有与`logits`相同类型和形状的张量。
## 数据集说明和加载
我们将讨论的第一种情况是我们要拟合逻辑回归的方法,仅测量一个变量,并且只有两个可能的结果。
### CHDAGE 数据集
对于第一个简单的示例,我们将使用一个非常简单且经过研究的数据集,该数据集以在书中出版而闻名。 应用逻辑回归第三版,David W. Hosmer Jr.,Stanley Lemeshow,Rodney X. Sturdivant,作者:Wiley。
列出`age`的年限(AGE),以及对心脏病风险因素进行假设性研究的 100 名受试者是否患有严重冠心病(CHD)的证据。 该表还包含一个标识符变量(ID)和一个年龄组变量(AGEGRP)。 结果变量是 CHD,它用`0`值编码以表示不存在 CHD,或用`1`编码以指示其存在于个体中。 通常,可以使用任何两个值,但是我们发现使用零和一最为方便。 我们将此数据集称为 CHDAGE 数据。
#### CHDAGE 数据集格式
CHDAGE 数据集是一个两列的 CSV 文件,我们将从外部仓库下载该文件。
在第 1 章(探索和转换数据)中,我们使用了本机 TensorFlow 方法来读取数据集。 在本章中,我们将使用一个互补且流行的库来获取数据。
进行此新添加的原因是,鉴于数据集只有 100 个元组,实际上只需要一行读取即可,而且`pandas`库提供了免费但简单但强大的分析方法 。
因此,在该项目的第一阶段,我们将开始加载 CHDAGE 数据集的实例,然后将打印有关数据的重要统计信息,然后进行预处理。
在对数据进行一些绘制之后,我们将构建一个由激活函数组成的模型,该激活函数将在特殊情况下成为`softmax`函数,在特殊情况下它将变为标准逻辑回归。 那就是只有两个类别(疾病的存在与否)。
#### 数据集加载和预处理实现
首先,我们导入所需的库,并指示所有`matplotlib`程序都将内联(如果我们使用 Jupyter):
```py
>>> import pandas as pd
>>> import numpy as np
>>> %matplotlib inline
>>> import matplotlib.pyplot as plt
```
然后,我们读取数据并要求`pandas`检查有关数据集的重要统计信息:
```py
>>> df = pd.read_csv("data/CHD.csv", header=0)
>>> print df.describe()
```
```py
age chd
count 100.000000 100.00000
mean 44.380000 0.43000
std 11.721327 0.49757
min 20.000000 0.00000
25% 34.750000 0.00000
50% 44.000000 0.00000
75% 55.000000 1.00000
max 69.000000 1.000000
```
然后,我们继续绘制数据以了解数据:
```py
plt.figure() # Create a new figure
plt.scatter(df['age'],df['chd']) #Plot a scatter draw of the random datapoints
```
![Dataset loading and preprocessing implementation](img/00074.jpg)
## 模型架构
在这里,我们从以下变量开始,描述将在其中构建模型元素的代码部分:
```py
learning_rate = 0.8 #Learning speed
batch_size = 100 #number of samples for the batch
display_step = 2 #number of steps before showing progress
```
在这里,我们为图创建初始变量和占位符,即单变量`x``y`浮点值:
```py
x = tf.placeholder("float", [None, 1]) # Placeholder for the 1D data
y = tf.placeholder("float", [None, 2]) # Placeholder for the classes (2)
```
现在,我们将创建线性模型变量,随着模型拟合的进行,将对其进行修改和更新:
```py
W = tf.Variable(tf.zeros([1, 2]))
b = tf.Variable(tf.zeros([2]))
```
最后,我们将对线性函数应用`softmax`操作来构建激活函数:
```py
activation = tf.nn.softmax(tf.matmul(x, W) + b)
```
## 损失函数描述和优化器循环
在这里,我们仅将互相关函数定义为`loss`函数,并定义`optimizer`操作,即`gradient descent`。 以下各章将对此进行解释; 现在,您可以看到它是一个黑框,它将改变变量,直到损失最小:
```py
cost = tf.reduce_mean(-tf.reduce_sum(y*tf.log(activation), reduction_indices=1))
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)
#Iterate through all the epochs
for epoch in range(training_epochs):
avg_cost = 0\.
total_batch = 400/batch_size
# Loop over all batches
for i in range(total_batch):
# Transform the array into a one hot format
temp=tf.one_hot(indices = df['chd'].values, depth=2, on_value = 1, off_value = 0, axis = -1 , name = "a")
batch_xs, batch_ys =(np.transpose([df['age']])-44.38)/11.721327, temp
# Fit training using batch data
sess.run(optimizer, feed_dict={x: batch_xs.astype(float), y: batch_ys.eval()})
# Compute average loss, suming the corrent cost divided by the batch total number
avg_cost += sess.run(cost, feed_dict={x: batch_xs.astype(float), y: batch_ys.eval()})/total_batch
```
## 停止条件
一旦根据训练周期对数据进行了训练,该过程将简单地停止。
## 结果描述
这将是程序的输出:
```py
Epoch: 0001 cost= 0.638730764
[ 0.04824295 -0.04824295]
[[-0.17459483 0.17459483]]
Epoch: 0002 cost= 0.589489654
[ 0.08091066 -0.08091066]
[[-0.29231569 0.29231566]]
Epoch: 0003 cost= 0.565953553
[ 0.10427245 -0.10427245]
[[-0.37499282 0.37499279]]
Epoch: 0004 cost= 0.553756475
[ 0.12176144 -0.12176143]
[[-0.43521613 0.4352161 ]]
Epoch: 0005 cost= 0.547019333
[ 0.13527818 -0.13527818]
[[-0.48031801 0.48031798]]
```
### 跨周期拟合函数的表示
在下图中,我们表示了拟合函数在不同周期之间的进展:
![Fitting function representations across epochs](img/00075.jpg)
## 完整源代码
这是完整的源代码:
```py
import pandas as pd
import numpy as np
get_ipython().magic(u'matplotlib inline')
import matplotlib.pyplot as plt
import tensorflow as tf
df = pd.read_csv("data/CHD.csv", header=0)
# Parameters
learning_rate = 0.2
training_epochs = 5
batch_size = 100
display_step = 1
sess = tf.Session()
b=np.zeros((100,2))
# tf Graph Input
x = tf.placeholder("float", [None, 1])
y = tf.placeholder("float", [None, 2])
# Create model
# Set model weights
W = tf.Variable(tf.zeros([1, 2]))
b = tf.Variable(tf.zeros([2]))
# Construct model
activation = tf.nn.softmax(tf.matmul(x, W) + b)
# Minimize error using cross entropy
cost = tf.reduce_mean(-tf.reduce_sum(y*tf.log(activation), reduction_indices=1)) # Cross entropy
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost) # Gradient Descent
# Initializing the variables
init = tf.initialize_all_variables()
# Launch the graph
with tf.Session() as sess:
tf.train.write_graph(sess.graph, './graphs','graph.pbtxt')
sess.run(init)
writer = tf.train.SummaryWriter('./graphs', sess.graph)
#Initialize the graph structure
graphnumber=321
#Generate a new graph
plt.figure(1)
#Iterate through all the epochs
for epoch in range(training_epochs):
avg_cost = 0\.
total_batch = 400/batch_size
# Loop over all batches
for i in range(total_batch):
# Transform the array into a one hot format
temp=tf.one_hot(indices = df['chd'].values, depth=2, on_value = 1, off_value = 0, axis = -1 , name = "a")
batch_xs, batch_ys = (np.transpose([df['age']])-44.38)/11.721327, temp
# Fit training using batch data
sess.run(optimizer, feed_dict={x: batch_xs.astype(float), y: batch_ys.eval()})
# Compute average loss, suming the corrent cost divided by the batch total number
avg_cost += sess.run(cost, feed_dict={x: batch_xs.astype(float), y: batch_ys.eval()})/total_batch
# Display logs per epoch step
if epoch % display_step == 0:
print "Epoch:", '%05d' % (epoch+1), "cost=", "{:.8f}".format(avg_cost)
#Generate a new graph, and add it to the complete graph
trX = np.linspace(-30, 30, 100)
print (b.eval())
print (W.eval())
Wdos=2*W.eval()[0][0]/11.721327
bdos=2*b.eval()[0]
# Generate the probabiliy function
trY = np.exp(-(Wdos*trX)+bdos)/(1+np.exp(-(Wdos*trX)+bdos) )
# Draw the samples and the probability function, whithout the normalization
plt.subplot(graphnumber)
graphnumber=graphnumber+1
#Plot a scatter draw of the random datapoints
plt.scatter((df['age']),df['chd'])
plt.plot(trX+44.38,trY) #Plot a scatter draw of the random datapoints
plt.grid(True)
#Plot the final graph
plt.savefig("test.svg")
```
### 图形表示
使用 TensorBoard 实用程序,我们将看到操作链。 请注意,在一半的操作图中,我们定义了主要的全局操作(“小数点”)以及应用于其余项的梯度操作,这是进行`loss`函数最小化所必需的。 这是接下来几章要讨论的主题。
![Graphical representation](img/00076.jpg)
\ No newline at end of file
# 示例 2 -- 使用 skflow 的单变量 logistic 回归
在此示例中,我们将探索单变量示例域,但是这次我们将使用来自新库的帮助,该库为我们简化了模型构建,称为`skflow`
## 有用的库和方法
在机器学习库领域中,有很多选择。 最知名的之一是`sklearn`,我们在第 2 章聚类中讨论过。
在 TensorFlow 发布之后的很早,一个新的贡献库就出现了,叫做`skflow`,其主要目的是模拟`sklearn`的界面和工作流程,在这个 TensorFlow 会话环境中工作更简洁。
在下面的示例中,我们将使用`skflow`界面重复先前回归的分析。
在示例中,我们还将看到 skflow 如何为回归模型自动生成详细且组织良好的图,只需将日志目录设置为参数即可。
## 数据集说明
使用`pandas`库,数据集加载阶段与前面的示例相同:
```py
import pandas as pd
df = pd.read_csv("data/CHD.csv", header=0)
print df.describe()
```
## 模型架构
这是`my_model`的代码段:
```py
def my_model(X, y):
return skflow.models.logistic_regression(X, y)
X1 =a.fit_transform(df['age'].astype(float))
y1 = df['chd'].values
classifier = skflow.TensorFlowEstimator(model_fn=my_model, n_classes=2)
```
在这里,我们可以使用`softmax`分类器查看逻辑回归阶段的详细视图:
![Model architecture](img/00077.jpg)
![Model architecture](img/00078.jpg)
## 结果描述
```py
score = metrics.accuracy_score(df['chd'].astype(float), classifier.predict(X))
print("Accuracy: %f" % score)
```
输出结果可观(为了简化模型)74%的准确率:
```py
Accuracy: 0.740000
```
## 完整源代码
这是完整的源代码:
```py
import tensorflow.contrib.learn as skflow
from sklearn import datasets, metrics, preprocessing
import numpy as np
import pandas as pd
df = pd.read_csv("data/CHD.csv", header=0)
print df.describe()
def my_model(X, y):
return skflow.models.logistic_regression(X, y)
a = preprocessing.StandardScaler()
X1 =a.fit_transform(df['age'].astype(float))
y1 = df['chd'].values
classifier = skflow.TensorFlowEstimator(model_fn=my_model, n_classes=2)
classifier.fit(X1,y1 , logdir='/tmp/logistic')
score = metrics.accuracy_score(df['chd'].astype(float), classifier.predict(X))
print("Accuracy: %f" % score)
```
\ No newline at end of file
# 总结
在本章中,我们学习了一种新的建模技术,即逻辑函数,并从一种简单的分类任务入手。
我们还学习了一种通过`pandas`库读取基于文本的数据的新方法。
此外,我们还看到了与`skflow`库一起使用的经典工作流的一种补充方法。
在下一章中,我们将开始处理更复杂的架构,并进入 TensorFlow 库擅长的领域:训练,测试和最终实现神经网络以解决实际问题。
\ No newline at end of file
# 第 5 章 简单的前馈神经网络
神经网络确实是 Tensorflow 擅长的机器学习领域。 可以用它实现多种类型的架构和算法,以及结合了符号引擎的其他优点,这实际上将有助于训练更复杂的设置。
在本章中,我们开始利用高表现原语的功能来解决大量支持输入变量的日益复杂的问题。
在本章中,我们将介绍以下主题:
* 神经网络的初步概念
* 非线性综合函数回归的神经网络项目
* 利用非线性回归预测汽车燃油效率的项目
* 学习葡萄酒的分类和多分类
\ No newline at end of file
# 运行我们的程序 -- 会话
客户端程序通过创建会话与 TensorFlow 系统交互。 会话对象表示将在其中运行计算的环境。 Session 对象开始为空,并且当程序员创建不同的操作和张量时,它们将被自动添加到 Session 中,在调用`Run()`方法之前,该对象不会进行任何计算。
`Run()`方法采用一组需要计算的输出名称,以及一组可选的张量,以代替节点的某些输出输入到图中。
如果调用此方法,并且命名操作依赖于某些操作,则 Session 对象将执行所有这些操作,然后继续执行命名操作。
这条简单的线是创建会话所需的唯一一行:
```py
s = tf.Session()
Sample command line output:
tensorflow/core/common_runtime/local_session.cc:45]Localsessioninteropparallelism threads:6
```
\ No newline at end of file
# 初步概念
为了将简单的框架构建到神经网络的组件和架构中,我们将对原始概念进行简单明了的构建,这些原始概念为当前,复杂而多样的神经网络格局铺平了道路。
## 人工神经元
人工神经元是一种数学函数,被视为真实生物神经元的模型。
它的主要特征是它接收一个或多个输入(训练数据),并对它们求和以产生输出。 此外,通常对总和进行加权(权重和偏差),然后将总和传递给非线性函数(激活函数或传递函数)。
## 原始示例 -- 感知器
感知器是实现人工神经元的最简单方法之一,并且它的算法可以追溯到 1950 年代,最早是在 1960 年代实现的。
从本质上讲,它是一种学习二分类函数的算法,该算法将一个实数映射为两个数:
![Original example - the Perceptron](img/00079.jpg)
下图显示了单层感知器
### 感知机算法
感知器的简化算法为:
1. 用随机分布初始化权重(通常为低值)
2. 选择一个输入向量并将其呈现给网络,
3. 为指定的输入向量和权重值计算网络的输出 y'。
4. The function for a perceptron is:
![Perceptron algorithm](img/00080.jpg)
5. 如果 y'≠y,则通过添加更改`Δw = yx[i]`修改所有连接`w[i]`
6. 返回步骤 2。
## 神经网络层
单层感知器可以推广到彼此连接的多个层,但是仍然存在问题;表示函数是输入的线性组合,并且感知器只是一种线性分类器,不可能正确拟合非线性函数。
![Neural network layers](img/00081.jpg)
## 神经网络激活函数
仅靠单变量线性分类器,神经网络的学习表现就不会那么好。 甚至机器学习中的一些轻微复杂的问题都涉及多个非线性变量,因此开发了许多变体来替代感知器的传递函数。
为了表示非线性模型,可以在激活函数中使用许多不同的非线性函数。 这意味着神经元将对输入变量的变化做出反应的方式发生变化。 实际上,最常用的激活函数是:
* Sigmoid: 规范的激活函数,对于计算分类属性的概率具有很好的属性。
![Neural Network activation functions](img/00082.jpg)
* Tanh: 与 Sigmoid 非常相似,但其值范围是`[-1,1]`而不是`[0,1]`
![Neural Network activation functions](img/00083.jpg)
* Relu: 这称为整流线性单元,其主要优点之一是它不受“梯度消失”问题的影响,该问题通常存在于网络的第一层,趋向于 0 或很小的 epsilon 值:
![Neural Network activation functions](img/00084.jpg)
## 梯度和反向传播算法
当我们描述感知器的学习阶段时,我们描述了根据权重对最终误差的“责任”来按比例调整权重的阶段。
在这个复杂的神经元网络中,误差的责任将分布在整个架构中应用于数据的所有函数之间。
因此,一旦我们计算了总误差,并且将整个函数应用于原始数据,我们现在就必须尝试调整方程式中的所有变量以将其最小化。
正如 Optimization 领域所研究的那样,我们需要知道的是能够使该误差最小化的是损失函数的梯度。
鉴于数据要经过许多权重和传递函数,因此必须通过链式法则来解决合成函数的梯度。
## 最小化损失函数:梯度下降
让我们看下图以了解损失函数:
![Minimizing loss function: Gradient descent](img/00085.jpg)
### 神经网络问题的选择 -- 分类与回归
神经网络可用于回归问题和分类问题。 架构上的共同差异在于输出层:为了能够带来实数为基础的结果,不应应用诸如 Sigmoid 之类的标准化函数,这样我们就不会将变量的结果更改为许多可能的类别值之一,获得了可能的连续结果。
## 有用的库和方法
在本章中,我们将使用 TensorFlow 和实用程序库中的一些新实用程序,这些是最重要的实用程序:
### TensorFlow 激活函数
TensorFlow 导航中最常用的函数:
* `tf.sigmoid(x)`:标准 S 形函数
* `tf.tanh(x)`:双曲正切
* `tf.nn.relu(features)`:Relu 传递函数
TensorFlow 导航的其他函数:
* `tf.nn.elu(features)`:计算指数线性:如果`< 0`则为`exp(features) - 1`,否则为`features`
* `tf.nn.softsign(features)`:计算 softsign:`features / (abs(features) + 1)`
* `tf.nn.bias_add(value, bias)`:为值增加偏差
### TensorFlow 损失优化方法
TensorFlow 损失优化方法如下所述:
* `tf.train.GradientDescentOptimizer(learning_rate, use_locking, name)`:这是原始的梯度下降方法,仅具有学习率参数
* `tf.train.AdagradOptimizer(learning_rate, initial_accumulator_value, use_locking, name)`:此方法使学习率适应参数的频率,从而提高了最小搜索稀疏参数的效率
* `tf.train.AdadeltaOptimizer(learning_rate, rho, epsilon, use_locking, name)`:这是改良的 AdaGrad,它将限制频繁参数的累积到最大窗口,因此它考虑了一定数量的步骤,而不是整个参数历史记录。
* `tf.train.AdamOptimizer tf.train.AdamOptimizer.__init__(learning_rate, beta1, beta2, epsilon, use_locking, name)`:此方法在计算梯度时会添加一个因子,该因子对应于过去梯度的平均值,等同于动量因子。 因此,来自自适应矩估计的亚当这个名字。
### Sklearn 预处理实用程序
让我们看一下以下 Sklearn 预处理实用程序:
* `preprocessing.StandardScaler()`:数据集的正则化是许多机器学习估计器的普遍要求,因此,为了使收敛更加直接,数据集将必须更像是标准正态分布,即具有零均值和单位方差的高斯曲线。 在实践中,我们通常会忽略分布的形状,而只是通过删除每个特征的平均值来变换数据以使其居中,然后通过将非恒定特征除以它们的标准偏差来缩放它。 对于此任务,我们使用 StandardScaler,它实现了前面提到的任务。 它还存储转换,以便能够将其重新应用于测试集。
* `StandardScaler``fit_transform()`:简单地将数据调整为所需格式。 StandardScaler 对象将保存转换变量,因此您将能够取回非规格化数据。
* `cross_validation.train_test_split`:此方法将数据集分为训练和测试段,我们只需要提供分配给每个阶段的数据集的百分比即可。
\ No newline at end of file
# 第一个项目 -- 非线性合成函数回归
人工神经网络示例通常包含绝大多数分类问题,但实际上有大量应用可以表示为回归。
用于回归的网络架构与用于分类问题的网络架构没有很大不同:它们可以采用多变量输入,也可以使用线性和非线性激活函数。
在某些情况下,唯一必要的情况是仅在层的末尾删除类似于 S 形的函数,以允许出现所有选项。
在第一个示例中,我们将对一个简单的,有噪声的二次函数进行建模,并将尝试通过单个隐藏层网络对其进行回归,并查看我们可以多么接近地预测从测试总体中得出的值。
## 数据集说明和加载
在这种情况下,我们将使用生成的数据集,该数据集与第 3 章的线性回归中的数据集非常相似。
我们将使用常见的 Numpy 方法生成二次函数,然后添加随机噪声,这将有助于我们了解线性回归如何推广。
核心样本创建例程如下:
```py
import numpy as np
trainsamples = 200
testsamples = 60
dsX = np.linspace(-1, 1, trainsamples + testsamples).transpose()
dsY = 0.4* pow(dsX,2) +2 * dsX + np.random.randn(*dsX.shape) * 0.22 + 0.8
```
## 数据集预处理
该数据集在生成时不需要进行预处理,并且具有良好的属性,例如居中并具有-1,1 x 样本分布。
## 建模架构 -- 损失函数描述
此设置的损耗将简单地用均方根误差表示,如下所示:
```py
cost = tf.pow(py_x-Y, 2)/(2)
```
## 损失函数优化器
在这种情况下,我们将使用 Gradient Descent 成本优化器,可以通过以下代码调用该优化器:
```py
train_op = tf.train.AdamOptimizer(0.5).minimize(cost)
```
## 准确率和收敛性测试
`predict_op = tf.argmax(py_x, 1)`
```py
cost1 += sess.run(cost, feed_dict={X: [[x1]], Y: y1}) / testsamples
```
### 示例代码
让我们看一下下面显示的示例代码:
```py
import tensorflow as tf
import numpy as np
from sklearn.utils import shuffle
%matplotlib inline
import matplotlib.pyplot as plt
trainsamples = 200
testsamples = 60
#Here we will represent the model, a simple imput, a hidden layer of sigmoid activation
def model(X, hidden_weights1, hidden_bias1, ow):
hidden_layer = tf.nn.sigmoid(tf.matmul(X, hidden_weights1)+ b)
return tf.matmul(hidden_layer, ow)
dsX = np.linspace(-1, 1, trainsamples + testsamples).transpose()
dsY = 0.4* pow(dsX,2) +2 * dsX + np.random.randn(*dsX.shape) * 0.22 + 0.8
plt.figure() # Create a new figure
plt.title('Original data')
plt.scatter(dsX,dsY) #Plot a scatter draw of the datapoints
```
![Example code](img/00086.jpg)
```py
X = tf.placeholder("float")
Y = tf.placeholder("float")
# Create first hidden layer
hw1 = tf.Variable(tf.random_normal([1, 10], stddev=0.1))
# Create output connection
ow = tf.Variable(tf.random_normal([10, 1], stddev=0.0))
# Create bias
b = tf.Variable(tf.random_normal([10], stddev=0.1))
model_y = model(X, hw1, b, ow)
# Cost function
cost = tf.pow(model_y-Y, 2)/(2)
# construct an optimizer
train_op = tf.train.GradientDescentOptimizer(0.05).minimize(cost)
# Launch the graph in a session
with tf.Session() as sess:
tf.initialize_all_variables().run() #Initialize all variables
for i in range(1,100):
dsX, dsY = shuffle (dsX.transpose(), dsY) #We randomize the samples to mplement a better training
trainX, trainY =dsX[0:trainsamples], dsY[0:trainsamples]
for x1,y1 in zip (trainX, trainY):
sess.run(train_op, feed_dict={X: [[x1]], Y: y1})
testX, testY = dsX[trainsamples:trainsamples + testsamples], dsY[0:trainsamples:trainsamples+testsamples]
cost1=0.
for x1,y1 in zip (testX, testY):
cost1 += sess.run(cost, feed_dict={X: [[x1]], Y: y1}) / testsamples
if (i%10 == 0):
print "Average cost for epoch " + str (i) + ":" + str(cost1)
```
## 结果描述
这是不同周期的结果的副本。请注意,由于这是一个非常简单的函数,因此即使第一次迭代也具有非常好的结果:
```py
Average cost for epoch 1:[[ 0.00753353]]
Average cost for epoch 2:[[ 0.00381996]]
Average cost for epoch 3:[[ 0.00134867]]
Average cost for epoch 4:[[ 0.01020064]]
Average cost for epoch 5:[[ 0.00240157]]
Average cost for epoch 6:[[ 0.01248318]]
Average cost for epoch 7:[[ 0.05143405]]
Average cost for epoch 8:[[ 0.00621457]]
Average cost for epoch 9:[[ 0.0007379]]
```
\ No newline at end of file
# 第二个项目 -- 使用非线性回归建模汽车的燃油效率
在此示例中,我们将进入一个区域,其中神经网络可提供大部分附加价值; 解决非线性问题。 为了开始这一旅程,我们将基于几个变量对几种汽车模型的燃油效率建模一个回归模型,该变量可以更好地用非线性函数表示。
## 数据集说明和加载
对于这个问题,我们将分析一个非常著名的,标准的,格式正确的数据集,该数据集将使我们能够分析一个多变量问题:根据离散和连续的一些相关变量来猜测汽车的 mpg。
这可以被认为是一个玩具,并且有些过时了,但是它将为更复杂的问题铺平道路,并且具有已经被众多书目分析的优势。
属性信息
该数据集具有以下数据列:
* `mpg`:连续
* `cylinders`:多值离散
* `displacement`:连续
* `horsepower`:连续
* `weight`:连续
* `acceleration`:连续
* `model year:`多值离散
* `origin`:多值离散
* `car name`:字符串(将不使用)
我们将不对数据进行详细的分析,但是我们可以非正式地推断出所有连续变量都与增加或减少目标变量相关:
![Dataset description and loading](img/00087.jpg)
## 数据集预处理
对于此任务,我们将使用来自 sklearn 的上述缩放器对象:
* `scaler = preprocessing.StandardScaler()`
* `X_train = scaler.fit_transform(X_train)`
## 建模架构
我们将要构建的是一个前馈神经网络,具有多变量输入和简单输出:
![Modeling architecture](img/00088.jpg)
## 收敛性测试
```py
score = metrics.mean_squared_error(regressor.predict(scaler.transform(X_test)), y_test)
print('MSE: {0:f}'.format(score))
```
## 结果描述
```py
Step #99, avg. train loss: 182.33624
Step #199, avg. train loss: 25.09151
Step #300, epoch #1, avg. train loss: 11.92343
Step #400, epoch #1, avg. train loss: 11.20414
Step #500, epoch #1, avg. train loss: 5.14056
Total Mean Squared Error: 15.0792258911
```
```py
%matplotlib inline
import matplotlib.pyplot as plt
import pandas as pd
from sklearn import datasets, cross_validation, metrics
from sklearn import preprocessing
from tensorflow.contrib import skflow
# Read the original dataset
df = pd.read_csv("data/mpg.csv", header=0)
# Convert the displacement column as float
df['displacement']=df['displacement'].astype(float)
# We get data columns from the dataset
# First and last (mpg and car names) are ignored for X
X = df[df.columns[1:8]]
y = df['mpg']
plt.figure() # Create a new figure
for i in range (1,8):
number = 420 + i
ax1.locator_params(nbins=3)
ax1 = plt.subplot(number)
plt.title(list(df)[i])
ax1.scatter(df[df.columns[i]],y) #Plot a scatter draw of the datapoints
plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0)
# Split the datasets
X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y,
test_size=0.25)
# Scale the data for convergency optimization
scaler = preprocessing.StandardScaler()
# Set the transform parameters
X_train = scaler.fit_transform(X_train)
# Build a 2 layer fully connected DNN with 10 and 5 units respectively
regressor = skflow.TensorFlowDNNRegressor(hidden_units=[10, 5],
steps=500, learning_rate=0.051, batch_size=1)
# Fit the regressor
regressor.fit(X_train, y_train)
# Get some metrics based on the X and Y test data
score = metrics.mean_squared_error(regressor.predict(scaler.transform(X_test)), y_test)
print(" Total Mean Squared Error: " + str(score))
```
\ No newline at end of file
# 第三个项目 -- 学习葡萄酒分类:多类分类
在本节中,我们将使用更复杂的数据集,尝试根据产地对葡萄酒进行分类。
## 数据集说明和加载
该数据包含对来自意大利同一地区但来自三个不同品种的葡萄酒进行化学分析的结果。 分析确定了三种葡萄酒中每种所含 13 种成分的数量。
数据变量:
*
* 苹果酸
*
* 灰的碱度
*
* 总酚
* 黄酮
* 非类黄酮酚
* 花青素
* 色彩强度
* 色调
* 稀释酒的 OD280 / OD315
* 脯氨酸
要读取数据集,我们将仅使用提供的 CSV 文件和熊猫:
```py
df = pd.read_csv("./wine.csv", header=0)
```
![Dataset description and loading](img/00089.jpg)
## 数据集预处理
随着 csv 上的值从 1 开始,我们将归一化带有偏差的值:
```py
y = df['Wine'].values-1
```
对于结果,我们将这些选项表示为一个数组的热门列表:
```py
Y = tf.one_hot(indices = y, depth=3, on_value = 1., off_value = 0., axis = 1 , name = "a").eval()
```
我们还将预先洗净值:
```py
X, Y = shuffle (X, Y)
scaler = preprocessing.StandardScaler()
X = scaler.fit_transform(X)
```
## 建模架构
这个特定的模型将由一个单层,全连接的神经网络组成:
* `x` = `tf.placeholder(tf.float32, [None, 12])`
* `W` = `tf.Variable(tf.zeros([12, 3]))`
* `b` = `tf.Variable(tf.zeros([3]))`
* `y` = `tf.nn.softmax(tf.matmul(x, W) + b)`
## 损失函数说明
我们将使用交叉熵函数来衡量损失:
```py
y_ = tf.placeholder(tf.float32, [None, 3])
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
```
## 损失函数优化器
同样,将使用“梯度下降”方法来减少损失函数:
```py
train_step = tf.train.GradientDescentOptimizer(0.1).minimize(cross_entropy)
```
## 收敛测试
在收敛性测试中,我们将每个良好的回归均转换为 1,将每个错误的回归均转换为 0,然后获取值的平均值来衡量模型的准确率:
```py
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print(accuracy.eval({x: Xt, y_: Yt}))
```
## 结果描述
如我们所见,随着历时的发展,我们具有可变精度,但是它总是优于 90%的精度,具有 30%的随机基数(如果我们生成 0 到 3 之间的随机数来猜测结果)。
```py
0.973684
0.921053
0.921053
0.947368
0.921053
```
## 完整源代码
让我们看一下完整的源代码:
```py
sess = tf.InteractiveSession()
import pandas as pd
# Import data
from tensorflow.examples.tlutorials.mnist import input_data
from sklearn.utils import shuffle
import tensorflow as tf
from sklearn import preprocessing
flags = tf.app.flags
FLAGS = flags.FLAGS
df = pd.read_csv("./wine.csv", header=0)
print (df.describe())
#df['displacement']=df['displacement'].astype(float)
X = df[df.columns[1:13]].values
y = df['Wine'].values-1
Y = tf.one_hot(indices = y, depth=3, on_value = 1., off_value = 0., axis = 1 , name = "a").eval()
X, Y = shuffle (X, Y)
scaler = preprocessing.StandardScaler()
X = scaler.fit_transform(X)
# Create the model
x = tf.placeholder(tf.float32, [None, 12])
W = tf.Variable(tf.zeros([12, 3]))
b = tf.Variable(tf.zeros([3]))
y = tf.nn.softmax(tf.matmul(x, W) + b)
# Define loss and optimizer
y_ = tf.placeholder(tf.float32, [None, 3])
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
train_step = tf.train.GradientDescentOptimizer(0.1).minimize(cross_entropy)
# Train
tf.initialize_all_variables().run()
for i in range(100):
X,Y =shuffle (X, Y, random_state=1)
Xtr=X[0:140,:]
Ytr=Y[0:140,:]
Xt=X[140:178,:]
Yt=Y[140:178,:]
Xtr, Ytr = shuffle (Xtr, Ytr, random_state=0)
#batch_xs, batch_ys = mnist.train.next_batch(100)
batch_xs, batch_ys = Xtr , Ytr
train_step.run({x: batch_xs, y_: batch_ys})
cost = sess.run (cross_entropy, feed_dict={x: batch_xs, y_: batch_ys})
# Test trained model
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print(accuracy.eval({x: Xt, y_: Yt}))
```
\ No newline at end of file
# 总结
在本章中,我们已经开始着手实现 TensorFlow 能力的真正替代物:神经网络模型。
我们还看到了在回归和分类任务中使用简单神经网络,简单生成模型和实验模型的情况。
在下一章中,我们将以卷积神经网络的形式提高新架构的知识以及将神经网络范式应用于其他知识领域(例如计算机视觉)的方式。
\ No newline at end of file
# 第 6 章 卷积神经网络
卷积神经网络是当前使用的许多最高级模型的一部分。 它们被用于许多领域,但是主要的应用领域是图像分类和特征检测领域。
我们将在本章中介绍的主题如下:
* 了解卷积函数和卷积网络如何工作以及构建它们的主要操作类型
* 将卷积运算应用于图像数据并学习一些应用于图像的预处理技术,以提高方法的准确率
* 使用 CNN 的简单设置对 MNIST 数据集的数字进行分类
* 使用应用于彩色图像的 CNN 模型对 CIFAR 数据集的真实图像进行分类
\ No newline at end of file
# 卷积神经网络的起源
新认知加速器是福岛教授在 1980 年发表的论文中介绍的卷积网络的前身,并且是一种能容忍位移和变形的自组织神经网络。
这个想法在 1986 年再次出现在原始反向传播论文的书本中,并在 1988 年被用于语音识别中的时间信号。
最初的设计后来在 1998 年通过 LeCun 的论文将基于梯度的学习应用于文档识别中进行了审查和改进,该论文提出了 LeNet-5 网络,该网络能够对手写数字进行分类。 与其他现有模型相比,该模型显示出更高的表现,尤其是在 SVM 的几种变体上,SVM 是出版年份中表现最高的操作之一。
然后在 2003 年对该论文进行了概括,论文为图像解释的层次神经网络。 但是,总的来说,我们将使用 LeCun 的 LeNet 论文架构的近似表示。
## 卷积入门
为了理解在这些类型的操作中应用于信息的操作,我们将从研究卷积函数的起源开始,然后我们将解释如何将此概念应用于信息。
为了开始跟踪操作的历史发展,我们将开始研究连续域中的卷积。
### 连续卷积
此操作的最初使用来自 18 世纪,并且可以在原始应用上下文中表示为将两个按时出现的特征混合在一起的操作。
从数学上讲,它可以定义如下:
![Continuous convolution](img/00090.jpg)
当我们尝试将此操作概念化为算法时,可以在以下步骤中解释前面的方程式:
1. 翻转信号:这是变量的`(-τ)`部分。
2. 移动它:这是由`g(τ)``t`求和因子给出的。
3. 乘以:这是`f``g`的乘积。
4. 积分结果曲线:这是较不直观的部分,因为每个瞬时值都是积分的结果。
![Continuous convolution](img/00091.jpg)
### 离散卷积
卷积可以转换为离散域,并以离散项描述离散函数:
![Discrete convolution](img/00092.jpg)
## 核和卷积
在离散域中应用卷积的概念时,经常会使用内核。
内核可以定义为`nxm`维矩阵,通常是在所有维上长的几个元素,通常是`m = n`
卷积运算包括将对应的像素与内核相乘,一次一个像素,然后将这些值相加,以便将该值分配给中央像素。
然后将应用相同的操作,将卷积矩阵向左移动,直到访问了所有可能的像素。
在以下示例中,我们有一个包含许多像素的图像和一个大小为`3x3`的内核,这在图像处理中特别常见:
![Kernels and convolutions](img/00093.jpg)
## 卷积运算的解释
回顾了连续场和离散场的卷积运算的主要特征之后,现在让我们看一下该运算在机器学习中的用途。
卷积核突出或隐藏模式。 根据受过训练的(或在示例中,手动设置)参数,我们可以开始发现参数,例如不同尺寸的方向和边缘。 我们也可能通过诸如模糊内核之类的方法覆盖一些不必要的细节或离群值。
正如 LeCun 在他的基础论文中所述:
> “卷积网络可以看作是合成自己的特征提取器。”
卷积神经网络的这一特性是相对于以前的数据处理技术的主要优势。 我们可以非常灵活地确定已确定数据集的主要组成部分,并通过这些基本构件的组合来表示其他样本。
## 在 TensorFlow 中应用卷积
TensorFlow 提供了多种卷积方法。 规范形式通过`conv2d`操作应用。 让我们看一下此操作的用法:
```py
tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu, data_format, name=None)
```
我们使用的参数如下:
* `input`:这是将对其应用操作的原始张量。 它具有四个尺寸的确定格式,默认尺寸顺序如下所示。
* `[batch, in_height, in_width, in_channels]`: Batch is a dimension that allows you to have a collection of images. This order is called `NHWC`. The other option is `NCWH`.
例如,单个`100x100` 像素彩色图像将具有以下形状:
```py
[1,100,100,3]
```
* `filter`:这是代表`kernel``filter`的张量。 它有一个非常通用的方法:
```py
[filter_height, filter_width, in_channels, out_channels]
```
* `strides`:这是四个`int`张量数据类型的列表,这些数据类型指示每个维度的滑动窗口。
* `Padding`:可以是`SAME``VALID``SAME`将尝试保留初始张量尺寸,但`VALID`将允许其增长,以防计算输出大小和填充。
* `use_cudnn_on_gpu`:这指示是否使用`CUDA GPU CNN`库来加速计算。
* `data_format`:这指定数据的组织顺序(NHWC 或 NCWH)。
### 其他卷积运算
TensorFlow 提供了多种应用卷积的方法,如下所示:
* `tf.nn.conv2d_transpose`:这适用于`conv2d`的转置(梯度),并用于反卷积网络中
* `tf.nn.conv1d`:给定 3D 输入和`filter`张量,这将执行 1D 卷积
* `tf.nn.conv3d`:给定 5D 输入和`filter`张量,这将执行 3D 卷积
### 示例代码 -- 将卷积应用于灰度图像
在此示例代码中,我们将读取 GIF 格式的灰度图像,该图像将生成一个三通道张量,但每个像素具有相同的 RGB 值。 然后,我们将张量转换为真实的灰度矩阵,应用`kernel`,并在 JPEG 格式的输出图像中检索结果。
### 注意
请注意,您可以调整`kernel`变量中的参数以观察图像变化的影响。
以下是示例代码:
```py
import tensorflow as tf
#Generate the filename queue, and read the gif files contents
filename_queue = tf.train.string_input_producer(tf.train.match_filenames_once("data/test.gif"))
reader = tf.WholeFileReader()
key, value = reader.read(filename_queue)
image=tf.image.decode_gif(value)
#Define the kernel parameters
kernel=tf.constant(
[
[[[-1.]],[[-1.]],[[-1.]]],
[[[-1.]],[[8.]],[[-1.]]],
[[[-1.]],[[-1.]],[[-1.]]]
]
)
#Define the train coordinator
coord = tf.train.Coordinator()
with tf.Session() as sess:
tf.initialize_all_variables().run()
threads = tf.train.start_queue_runners(coord=coord)
#Get first image
image_tensor = tf.image.rgb_to_grayscale(sess.run([image])[0])
#apply convolution, preserving the image size
imagen_convoluted_tensor=tf.nn.conv2d(tf.cast(image_tensor, tf.float32),kernel,[1,1,1,1],"SAME")
#Prepare to save the convolution option
file=open ("blur2.jpg", "wb+")
#Cast to uint8 (0..255), previous scalation, because the convolution could alter the scale of the final image
out=tf.image.encode_jpeg(tf.reshape(tf.cast(imagen_convoluted_tensor/tf.reduce_max(imagen_convoluted_tensor)*255.,tf.uint8), tf.shape(imagen_convoluted_tensor.eval()[0]).eval()))
file.close()
coord.request_stop()
coord.join(threads)
```
### 样本内核结果
在下图中,您可以观察到参数的变化如何影响图像的结果。 第一张图片是原始图片。
滤镜类型为从左到右,从上到下模糊,底部 Sobel(从上到下搜索边的一种滤镜),浮雕(突出显示拐角边)和轮廓(概述图像的外部边界)。
![Sample kernels results](img/00094.jpg)
## 二次采样操作-合并
在 TensorFlow 中通过称为池的操作执行二次采样操作。 这个想法是应用一个(大小不一的)内核并提取内核覆盖的元素之一,其中`max_pool``avg_pool`是最著名的一些元素,它们仅获得最大和平均值。 应用内核的元素。
在下图中,您可以看到将`2x2`内核应用于单通道 16x16 矩阵的操作。 它只是保持其覆盖的内部区域的最大值。
![Subsampling operation - pooling](img/00095.jpg)
可以进行的合并操作的类型也有所不同。 例如,在 LeCun 的论文中,应用于原始像素的运算必须将它们乘以一个可训练的参数,并添加一个额外的可训练`bias`
### 下采样层的属性
二次采样层的主要目的与卷积层的目的大致相同。 减少信息的数量和复杂性,同时保留最重要的信息元素。 它们构建了基础信息的紧凑表示。
### 不变性
下采样层还允许将信息的重要部分从数据的详细表示转换为更简单的表示。 通过在图像上滑动滤镜,我们将检测到的特征转换为更重要的图像部分,最终达到 1 像素的图像,该特征由该像素值表示。 相反,此属性也可能导致模型丢失特征检测的局部性。
### 下采样层实现表现
下采样层的实现要快得多,因为未使用的数据元素的消除标准非常简单。 通常,它只需要进行几个比较。
### 在 TensorFlow 中应用池化操作
首先,我们将分析最常用的`pool`操作`max_pool`。 它具有以下签名:
```py
tf.nn.max_pool(value, ksize, strides, padding, data_format, name)
```
此方法类似于`conv2d`,参数如下:
* `value`:这是`float32`元素和形状(批量长度,高度,宽度,通道)的 4D 张量。
* `ksize`:这是一个整数列表,代表每个维度上的窗口大小
* `strides`:这是在每个尺寸上移动窗口的步骤
* `data_format`:设置数据尺寸
* `ordering`:NHWC 或 NCHW
* `padding``VALID``SAME`
### 其他池化操作
* `tf.nn.avg_pool`:这将返回每个窗口的平均值的缩减张量
* `tf.nn.max_pool_with_argmax`:这将返回`max_pool`张量和具有`max_value`的平展索引的张量
* `tf.nn.avg_pool3d`:此操作使用类似立方的窗口执行`avg_pool`操作; 输入有额外的深度
* `tf.nn.max_pool3d`:执行与(`...`)相同的操作,但应用`max`操作
### 示例代码
在以下示例代码中,我们将采用原始格式:
```py
import tensorflow as tf
#Generate the filename queue, and read the gif files contents
filename_queue = tf.train.string_input_producer(tf.train.match_filenames_once("data/test.gif"))
reader = tf.WholeFileReader()
key, value = reader.read(filename_queue)
image=tf.image.decode_gif(value)
#Define the coordinator
coord = tf.train.Coordinator()
def normalize_and_encode (img_tensor):
image_dimensions = tf.shape(img_tensor.eval()[0]).eval()
return tf.image.encode_jpeg(tf.reshape(tf.cast(img_tensor, tf.uint8), image_dimensions))
with tf.Session() as sess:
maxfile=open ("maxpool.jpg", "wb+")
avgfile=open ("avgpool.jpg", "wb+")
tf.initialize_all_variables().run()
threads = tf.train.start_queue_runners(coord=coord)
image_tensor = tf.image.rgb_to_grayscale(sess.run([image])[0])
maxed_tensor=tf.nn.avg_pool(tf.cast(image_tensor, tf.float32),[1,2,2,1],[1,2,2,1],"SAME")
averaged_tensor=tf.nn.avg_pool(tf.cast(image_tensor, tf.float32),[1,2,2,1],[1,2,2,1],"SAME")
maxfile.write(normalize_and_encode(maxed_tensor).eval())
avgfile.write(normalize_and_encode(averaged_tensor).eval())
coord.request_stop()
maxfile.close()
avgfile.close()
coord.join(threads)
```
在下图中,我们首先看到原始图像和缩小尺寸的图像,然后是`max_pool`,然后是`avg_pool`。 如您所见,这两个图像看起来是相等的,但是如果我们绘制它们之间的图像差异,我们会发现,如果取最大值而不是均值(始终小于或等于均值),则会有细微的差异。
![Sample code](img/00096.jpg)
## 提高效率 - dropout 操作
在大型神经网络训练过程中观察到的主要优点之一是过拟合,即为训练数据生成非常好的近似值,但为单点之间的区域发出噪声。
在过拟合的情况下,该模型专门针对训练数据集进行了调整,因此对于一般化将无用。 因此,尽管它在训练集上表现良好,但是由于缺乏通用性,因此它在测试数据集和后续测试中的表现很差。
因此,引入了 dropout 操作。 此操作将某些随机选择的权重的值减小为零,从而使后续层为零。
这种方法的主要优点是,它避免了一层中的所有神经元同步优化其权重。 随机分组进行的这种适应避免了所有神经元都收敛到相同的目标,从而使适应的权重解相关。
在 dropout 应用中发现的第二个属性是隐藏单元的激活变得稀疏,这也是理想的特性。
在下图中,我们表示了原始的完全连接的多层神经网络以及具有链接的 dropout 的关联网络:
![Improving efficiency - dropout operation](img/00097.jpg)
### 在 TensorFlow 中应用 dropout 操作
为了应用`dropout`操作,TensorFlows 实现了`tf.nn.dropout`方法,其工作方式如下:
```py
tf.nn.dropout (x, keep_prob, noise_shape, seed, name)
```
参数如下:
* `x`:这是原始张量
* `keep_prob`:这是保留神经元的概率以及乘以其余节点的因子
* `noise_shape`:这是一个四元素列表,用于确定尺寸是否将独立应用归零
#### 示例代码
在此样本中,我们将对样本向量应用 dropout 操作。 Dropout 还可以将 Dropout 传输到所有与架构相关的单元。
在下面的示例中,您可以看到将 dropout 应用于`x`变量的结果,其归零概率为 0.5,并且在未发生这种情况的情况下,值加倍(乘以 1 / 1.5, dropout 概率):
![Sample code](img/00098.jpg)
显然,大约一半的输入已被清零(选择此示例是为了显示概率不会总是给出预期的四个零)。
可能使您感到惊讶的一个因素是应用于非放置元素的比例因子。 这项技术用于维护相同的网络,并在训练时将`keep_prob`设为 1,将其恢复到原始架构。
## 卷积类型层构建方法
为了构建卷积神经网络层,存在一些通用的实践和方法,可以在构建深度神经网络的方式中将其视为准规范。
为了促进卷积层的构建,我们将看一些简单的实用函数。
### 卷积层
这是卷积层的一个示例,它连接一个卷积,添加一个`bias`参数总和,最后返回我们为整个层选择的激活函数(在这种情况下,`relu`操作很常见)。
```py
def conv_layer(x_in, weights, bias, strides=1):
x = tf.nn.conv2d(x, weights, strides=[1, strides, strides, 1], padding='SAME')
x = tf.nn.bias_add(x_in, bias)
return tf.nn.relu(x)
```
### 下采样层
通常可以通过维持层的初始参数,通过`max_pool`操作来表示下采样层:
```py
def maxpool2d(x, k=2):
return tf.nn.max_pool(x, ksize=[1, k, k, 1], strides=[1, k, k, 1],
padding='SAME')
```
\ No newline at end of file
# 示例 1 -- MNIST 数字分类
在本节中,我们将首次使用最知名的模式识别数据集中的一个。 它最初是为了训练神经网络来对支票上的手写数字进行字符识别而开发的。
原始数据集有 60,000 个不同的数字用于训练和 10,000 个用于测试,并且在使用时是原始使用的数据集的子集。
在下图中,我们显示了 LeNet-5 架构,这是有关该问题发布的第一个著名的卷积架构。
在这里,您可以看到层的尺寸和最后的结果表示:
![Example 1 - MNIST digit classification](img/00099.jpg)
## 数据集说明和加载
MNIST 是易于理解和阅读但难以掌握的数据集。 当前,有很多好的算法可以解决这个问题。 在我们的案例中,我们将寻求建立一个足够好的模型,以使其与 10%的随机结果相去甚远。
为了访问 MNIST 数据集,我们将使用为 TensorFlow 的 MNIST 教程开发的一些实用工具类。
这两条线是我们拥有完整的 MNIST 数据集所需的全部工作。
在下图中,我们可以看到数据集对象的数据结构的近似值:
![Dataset description and loading](img/00100.jpg)
通过此代码,我们将打开并探索 MNIST 数据集:
![Dataset description and loading](img/00101.jpg)
要打印字符(在 Jupyter Notebook 中),我们将重塑表示图像的线性方式,形成`28x28`的方矩阵,分配灰度色图,并使用以下行绘制所得的数据结构:
```py
plt.imshow(mnist.train.images[0].reshape((28, 28), order='C'), cmap='Greys', interpolation='nearest')
```
下图显示了此行应用于不同数据集元素的结果:
![Dataset description and loading](img/00102.jpg)
## 数据集预处理
在此示例中,我们将不进行任何预处理; 我们只会提到,仅通过使用线性变换的现有样本(例如平移,旋转和倾斜的样本)扩展数据集示例,就可以实现更好的分类评分。
## 建模架构
在这里,我们将研究为该特定架构选择的不同层。
它开始生成带有名称的权重字典:
```py
'wc1': tf.Variable(tf.random_normal([5, 5, 1, 32])),
'wc2': tf.Variable(tf.random_normal([5, 5, 32, 64])),
'wd1': tf.Variable(tf.random_normal([7*7*64, 1024])),
'out': tf.Variable(tf.random_normal([1024, n_classes]))
```
对于每个权重,还将添加一个`bias`以说明常数。
然后我们定义连接的层,一层又一层地集成:
```py
conv_layer_1 = conv2d(x_in, weights['wc1'], biases['bc1'])
conv_layer_1 = subsampling(conv_layer_1, k=2)
conv_layer_2 = conv2d(conv_layer_1, weights['wc2'], biases['bc2'])
conv_layer_2 = subsampling(conv_layer_2, k=2)
fully_connected_layer = tf.reshape(conv_layer_2, [-1, weights['wd1'].get_shape().as_list()[0]])
fully_connected_layer = tf.add(tf.matmul(fully_connected_layer, weights['wd1']), biases['bd1'])
fully_connected_layer = tf.nn.relu(fully_connected_layer)
fully_connected_layer = tf.nn.dropout(fully_connected_layer, dropout)
prediction_output = tf.add(tf.matmul(fully_connected_layer, weights['out']), biases['out'])
```
## 损失函数说明
损失函数将是交叉熵误差函数的平均值,该函数通常是用于分类的 softmax 函数。
```py
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(pred, y))
```
## 损失函数优化器
对于此示例,我们将使用改进的`AdamOptimizer`,其学习率可配置,我们将其定义为 0.001。
```py
optimizer = tf.train.AdamOptimizer
(learning_rate=learning_rate).minimize(cost)
```
## 准确率测试
准确率测试计算标签和结果之间比较的平均值,以获得`0``1`之间的值。
```py
correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
```
## 结果说明
此示例的结果简洁明了,并且假设我们仅训练 10,000 个样本,则准确率不是一流的,但与十分之一的随机采样结果明显分开:
```py
Optimization Finished!
Testing Accuracy: 0.382812
```
## 完整源代码
以下是源代码:
```py
import tensorflow as tf
%matplotlib inline
import matplotlib.pyplot as plt
# Import MINST data
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)
# Parameters
learning_rate = 0.001
training_iters = 2000
batch_size = 128
display_step = 10
# Network Parameters
n_input = 784 # MNIST data input (img shape: 28*28)
n_classes = 10 # MNIST total classes (0-9 digits)
dropout = 0.75 # Dropout, probability to keep units
# tf Graph input
x = tf.placeholder(tf.float32, [None, n_input])
y = tf.placeholder(tf.float32, [None, n_classes])
keep_prob = tf.placeholder(tf.float32) #dropout (keep probability)
#plt.imshow(X_train[1202].reshape((20, 20), order='F'), cmap='Greys', interpolation='nearest')
# Create some wrappers for simplicity
def conv2d(x, W, b, strides=1):
# Conv2D wrapper, with bias and relu activation
x = tf.nn.conv2d(x, W, strides=[1, strides, strides, 1], padding='SAME')
x = tf.nn.bias_add(x, b)
return tf.nn.relu(x)
def maxpool2d(x, k=2):
# MaxPool2D wrapper
return tf.nn.max_pool(x, ksize=[1, k, k, 1], strides=[1, k, k, 1],
padding='SAME')
# Create model
def conv_net(x, weights, biases, dropout):
# Reshape input picture
x = tf.reshape(x, shape=[-1, 28, 28, 1])
# Convolution Layer
conv1 = conv2d(x, weights['wc1'], biases['bc1'])
# Max Pooling (down-sampling)
conv1 = maxpool2d(conv1, k=2)
# Convolution Layer
conv2 = conv2d(conv1, weights['wc2'], biases['bc2'])
# Max Pooling (down-sampling)
conv2 = maxpool2d(conv2, k=2)
# Fully connected layer
# Reshape conv2 output to fit fully connected layer input
fc1 = tf.reshape(conv2, [-1, weights['wd1'].get_shape().as_list()[0]])
fc1 = tf.add(tf.matmul(fc1, weights['wd1']), biases['bd1'])
fc1 = tf.nn.relu(fc1)
# Apply Dropout
fc1 = tf.nn.dropout(fc1, dropout)
# Output, class prediction
out = tf.add(tf.matmul(fc1, weights['out']), biases['out'])
return out
# Store layers weight & bias
weights = {
# 5x5 conv, 1 input, 32 outputs
'wc1': tf.Variable(tf.random_normal([5, 5, 1, 32])),
# 5x5 conv, 32 inputs, 64 outputs
'wc2': tf.Variable(tf.random_normal([5, 5, 32, 64])),
# fully connected, 7*7*64 inputs, 1024 outputs
'wd1': tf.Variable(tf.random_normal([7*7*64, 1024])),
# 1024 inputs, 10 outputs (class prediction)
'out': tf.Variable(tf.random_normal([1024, n_classes]))
}
biases = {
'bc1': tf.Variable(tf.random_normal([32])),
'bc2': tf.Variable(tf.random_normal([64])),
'bd1': tf.Variable(tf.random_normal([1024])),
'out': tf.Variable(tf.random_normal([n_classes]))
}
# Construct model
pred = conv_net(x, weights, biases, keep_prob)
# Define loss and optimizer
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(pred, y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
# Evaluate model
correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
# Initializing the variables
init = tf.initialize_all_variables()
# Launch the graph
with tf.Session() as sess:
sess.run(init)
step = 1
# Keep training until reach max iterations
while step * batch_size < training_iters:
batch_x, batch_y = mnist.train.next_batch(batch_size)
test = batch_x[0]
fig = plt.figure()
plt.imshow(test.reshape((28, 28), order='C'), cmap='Greys',
interpolation='nearest')
print (weights['wc1'].eval()[0])
plt.imshow(weights['wc1'].eval()[0][0].reshape(4, 8), cmap='Greys', interpolation='nearest')
# Run optimization op (backprop)
sess.run(optimizer, feed_dict={x: batch_x, y: batch_y,
keep_prob: dropout})
if step % display_step == 0:
# Calculate batch loss and accuracy
loss, acc = sess.run([cost, accuracy], feed_dict={x: batch_x,
y: batch_y,
keep_prob: 1.})
print "Iter " + str(step*batch_size) + ", Minibatch Loss= " + \
"{:.6f}".format(loss) + ", Training Accuracy= " + \
"{:.5f}".format(acc)
step += 1
print "Optimization Finished!"
# Calculate accuracy for 256 mnist test images
print "Testing Accuracy:", \
sess.run(accuracy, feed_dict={x: mnist.test.images[:256],
y: mnist.test.labels[:256],
keep_prob: 1.})
```
\ No newline at end of file
# 示例 2 -- 使用 CIFAR10 数据集进行图像分类
在此示例中,我们将研究图像理解中使用最广泛的数据集之一,该数据集用作简单但通用的基准。 在此示例中,我们将构建一个简单的 CNN 模型,以了解解决此类分类问题所需的一般计算结构。
## 数据集说明和加载
该数据集包含 40,000 个`32x32`像素的图像,代表以下类别:飞机,汽车,鸟类,猫,鹿,狗,青蛙,马,船和卡车。 在此示例中,我们将只处理 10,000 个图像包中的第一个。
以下是您可以在数据集中找到的一些图像示例:
![Dataset description and loading](img/00103.jpg)
## 数据集预处理
我们必须对原始数据集进行一些数据结构调整,首先将其转换为`[10000, 3, 32, 32]`多维数组,然后将通道维移动到最后一个顺序。
```py
datadir='data/cifar-10-batches-bin/'
plt.ion()
G = glob.glob (datadir + '*.bin')
A = np.fromfile(G[0],dtype=np.uint8).reshape([10000,3073])
labels = A [:,0]
images = A [:,1:].reshape([10000,3,32,32]).transpose (0,2,3,1)
plt.imshow(images[14])
print labels[11]
images_unroll = A [:,1:]
```
## 建模架构
在这里,我们将定义我们的建模函数,该函数是一系列卷积和池化操作,并使用最终的平坦层和逻辑回归来确定当前样本的分类概率。
```py
def conv_model (X, y):
X= tf. reshape(X, [-1, 32, 32, 3])
with tf.variable_scope('conv_layer1'):
h_conv1=tf.contrib.layers.conv2d(X, num_outputs=16, kernel_size=[5,5], activation_fn=tf.nn.relu)#print (h_conv1)
h_pool1=max_pool_2x2(h_conv1)#print (h_pool1)
with tf.variable_scope('conv_layer2'):
h_conv2=tf.contrib.layers.conv2d(h_pool1, num_outputs=16, kernel_size=[5,5], activation_fn=tf.nn.relu)
#print (h_conv2)
h_pool2=max_pool_2x2(h_conv2)
h_pool2_flat = tf.reshape(h_pool2, [-1,8*8*16 ])
h_fc1 = tf.contrib.layers.stack(h_pool2_flat, tf.contrib.layers.fully_connected ,[96,48], activation_fn=tf.nn.relu )
return skflow.models.logistic_regression(h_fc1,y)
```
## 损失函数说明和优化器
以下是函数:
```py
classifier = skflow.TensorFlowEstimator(model_fn=conv_model, n_classes=10, batch_size=100, steps=2000, learning_rate=0.01)
```
### 训练和准确率测试
使用以下两个命令,我们开始使用图像集对模型进行拟合并生成训练后模型的评分:
```py
%time classifier.fit(images, labels, logdir='/tmp/cnn_train/')
%time score =metrics.accuracy_score(labels, classifier.predict(images))
```
## 结果描述
结果如下:
| 参数 | 结果 1 | 结果 2 |
| --- | --- | --- |
| CPU 时间 | 用户 35 分钟 6 秒 | 用户 39.8 秒 |
| 系统 | 1 分钟 50 秒 | 7.19 秒 |
| 总时间 | 36 分钟 57 秒 | 47 秒 |
| 墙上时间 | 25 分钟 3 秒 | 32.5 秒 |
| 准确率 | 0.612200 | |
### 完整源代码
以下是完整的源代码:
```py
import glob
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
import tensorflow.contrib.learn as skflow
from sklearn import metrics
from tensorflow.contrib import learn
datadir='data/cifar-10-batches-bin/'
plt.ion()
G = glob.glob (datadir + '*.bin')
A = np.fromfile(G[0],dtype=np.uint8).reshape([10000,3073])
labels = A [:,0]
images = A [:,1:].reshape([10000,3,32,32]).transpose (0,2,3,1)
plt.imshow(images[15])
print labels[11]
images_unroll = A [:,1:]
def max_pool_2x2(tensor_in):
return tf.nn.max_pool(tensor_in, ksize= [1,2,2,1], strides= [1,2,2,1], padding='SAME')
def conv_model (X, y):
X= tf. reshape(X, [-1, 32, 32, 3])
with tf.variable_scope('conv_layer1'):
h_conv1=tf.contrib.layers.conv2d(X, num_outputs=16, kernel_size=[5,5], activation_fn=tf.nn.relu)#print (h_conv1)
h_pool1=max_pool_2x2(h_conv1)#print (h_pool1)
with tf.variable_scope('conv_layer2'):
h_conv2=tf.contrib.layers.conv2d(h_pool1, num_outputs=16, kernel_size=[5,5], activation_fn=tf.nn.relu)
#print (h_conv2)
h_pool2=max_pool_2x2(h_conv2)
h_pool2_flat = tf.reshape(h_pool2, [-1,8*8*16 ])
h_fc1 = tf.contrib.layers.stack(h_pool2_flat, tf.contrib.layers.fully_connected ,[96,48], activation_fn=tf.nn.relu )
return skflow.models.logistic_regression(h_fc1,y)
images = np.array(images,dtype=np.float32)
classifier = skflow.TensorFlowEstimator(model_fn=conv_model, n_classes=10, batch_size=100, steps=2000, learning_rate=0.01)
%time classifier.fit(images, labels, logdir='/tmp/cnn_train/')
%time score =metrics.accuracy_score(labels, classifier.predict(images))
```
\ No newline at end of file
# 总结
在本章中,我们了解了最先进的神经网络架构的组成部分之一:卷积神经网络。 使用此新工具,我们可以处理更复杂的数据集和概念抽象,因此我们将能够了解最新的模型。
在下一章中,我们将使用另一种新形式的神经网络以及更新的神经网络架构的一部分:循环神经网络。
\ No newline at end of file
此差异已折叠。
# 第 7 章 循环神经网络和 LSTM
回顾我们对更传统的神经网络模型的了解后,我们发现训练阶段和预测阶段通常以静态方式表示,其中输入作为输入,而我们得到输出,但我们不仅考虑了事件发生的顺序。与到目前为止回顾的预测模型不同,循环神经网络的预测取决于当前的输入向量以及先前的输入向量。
我们将在本章中介绍的主题如下:
* 了解循环神经网络的工作原理以及构建它们的主要操作类型
* 解释在更高级的模型(例如 LSTM)中实现的想法
* 在 TensorFlow 中应用 LSTM 模型来预测能耗周期
* 撰写新音乐,从 J.S Bach 的一系列研究开始
\ No newline at end of file
# 循环神经网络
知识通常不会从虚无中出现。 许多新的想法是先前知识的结合而诞生的,因此这是一种有用的模仿行为。 传统的神经网络不包含任何将先前看到的元素转换为当前状态的机制。
为了实现这一概念,我们有循环神经网络,即 RNN。 可以将循环神经网络定义为神经网络的顺序模型,该模型具有重用已给定信息的特性。 他们的主要假设之一是,当前信息依赖于先前的数据。 在下图中,我们观察到称为单元的 RNN 基本元素的简化图:
![Recurrent neural networks](img/00104.jpg)
单元的主要信息元素是输入(`Xt`),状态和输出(`ht`)。 但是正如我们之前所说,单元没有独立的状态,因此它还存储状态信息。 在下图中,我们将显示一个“展开”的 RNN 单元,显示其从初始状态到输出最终`h[n]`值的过程,中间有一些中间状态。
![Recurrent neural networks](img/00105.jpg)
一旦我们定义了单元的动态性,下一个目标就是研究制造或定义 RNN 单元的内容。 在标准 RNN 的最常见情况下,仅存在一个神经网络层,该神经网络层将输入和先前状态作为输入,应用 tanh 操作,并输出新状态`h(t+1).`
![Recurrent neural networks](img/00106.jpg)
这种简单的设置能够随着周期的过去而对信息进行汇总,但是进一步的实验表明,对于复杂的知识而言,序列距离使得难以关联某些上下文(例如,建筑师知道设计漂亮的建筑物)似乎是一种简单的结构, 请记住,但是将它们关联所需的上下文需要增加顺序才能将两个概念关联起来。 这也带来了爆炸和消失梯度的相关问题。
## 梯度爆炸和消失
循环神经网络的主要问题之一发生在反向传播阶段,鉴于其递归性质,误差反向传播所具有的步骤数与一个非常深的网络相对应。 梯度计算的这种级联可能在最后阶段导致非常不重要的值,或者相​​反,导致不断增加且不受限制的参数。 这些现象被称为消失和爆炸梯度。 这是创建 LSTM 架构的原因之一。
## LSTM 神经网络
长短期内存(LSTM)是一种特定的 RNN 架构,其特殊的架构使它们可以表示长期依赖性。 而且,它们是专门为记住长时间的信息模式和信息而设计的。
## 门操作 -- 基本组件
为了更好地理解 lstm 单元内部的构造块,我们将描述 LSTM 的主要操作块:gate 操作。
此操作基本上有一个多元输入,在此块中,我们决定让一些输入通过,将其他输入阻塞。 我们可以将其视为信息过滤器,并且主要有助于获取和记住所需的信息元素。
为了实现此操作,我们采用了一个多元控制向量(标有箭头),该向量与具有 S 型激活函数的神经网络层相连。 应用控制向量并通过 S 型函数,我们将得到一个类似于二元的向量。
我们将用许多开关符号来表示此操作:
![The gate operation - a fundamental component](img/00107.jpg)
定义了二元向量后,我们将输入函数与向量相乘,以便对其进行过滤,仅让部分信息通过。 我们将用一个三角形来表示此操作,该三角形指向信息行进的方向。
![The gate operation - a fundamental component](img/00108.jpg)
LSTM 单元格的一般结构
在下面的图片中,我们代表了 LSTM Cell 的一般结构。 它主要由上述三个门操作组成,以保护和控制单元状态。
此操作将允许丢弃(希望不重要)低状态数据,并且将新数据(希望重要)合并到状态中。
![The gate operation - a fundamental component](img/00109.jpg)
上一个图试图显示一个 LSTM Cell 的运行中发生的所有概念。
作为输入,我们有:
* 单元格状态将存储长期信息,因为它从一开始就从单元格训练的起点进行优化的权重,并且
* 短期状态`h(t)`,将在每次迭代中直接与当前输入结合使用,因此,其状态将受输入的最新值的影响更大
作为输出,我们得到了结合所有门操作的结果。
## 操作步骤
在本节中,我们将描述信息将对其操作的每个循环步骤执行的所有不同子步骤的概括。
### 第 1 部分 -- 设置要忘记的值(输入门)
在本节中,我们将采用来自短期的值,再加上输入本身,并且这些值将由多元 S 型表示的二元函数的值设置。 根据输入和短期记忆值,S 形输出将允许或限制一些先前的知识或单元状态中包含的权重。
![Part 1 - set values to forget (input gate)](img/00110.jpg)
### 第 2 部分 -- 设置要保留的值,更改状态
然后是时候设置过滤器了,该过滤器将允许或拒绝将新的和短期的内存合并到单元半永久状态。
因此,在此阶段,我们将确定将多少新信息和半新信息合并到新单元状态中。 此外,我们最终将通过我们一直在配置的信息过滤器,因此,我们将获得更新的长期状态。
为了规范新的和短期的信息,我们通过具有`tanh`激活的神经网络传递新的和短期的信息,这将允许在正则化(`-1,1`)范围内提供新信息。
![Part 2 - set values to keep, change state](img/00111.jpg)
### 第 3 部分 -- 输出已过滤的单元状态
现在轮到短期状态了。 它还将使用新的和先前的短期状态来允许新信息通过,但是输入将是长期状态,点乘以 tanh 函数,再一次将输入标准化为(`-1,1`)范围。
![Part 3 - output filtered cell state](img/00112.jpg)
## 其他 RNN 架构
通常,在本章中,假设 RNN 的领域更为广泛,我们将集中讨论 LSTM 类型的循环神经网络单元。 例如,还采用了 RNN 的其他变体,并为该领域增加了优势。
* 具有窥孔的 LSTM:在此网络中,单元门连接到单元状态
* Gate Recurring Unit:这是一个更简单的模型,它结合了忘记门和输入门,合并了单元的状态和隐藏状态,因此大大简化了网络的训练
## TensorFlow LSTM 有用的类和方法
在本节中,我们将回顾可用于构建 LSTM 层的主要类和方法,我们将在本书的示例中使用它们。
### 类`tf.nn.rnn_cell.BasicLSTMCell`
此类基本的 LSTM 递归网络单元,具有遗忘偏差,并且没有其他相关类型(如窥孔)的奇特特性,即使在不应影响的阶段,它也可以使单元查看所得状态。
以下是主要参数:
* `num_units`:整数,LSTM 单元的单元数
* `forget_bias`:浮动,此偏差(默认为`1`)被添加到忘记门,以便允许第一次迭代以减少初始训练步骤的信息丢失。
* `activation`:内部状态的激活函数(默认为标准`tanh`
### 类`MultiRNNCell`(`RNNCell`)
在将用于此特定示例的架构中,我们将不会使用单个单元来考虑历史值。 在这种情况下,我们将使用一组连接的单元格。 因此,我们将实例化`MultiRNNCell`类。
```py
MultiRNNCell(cells, state_is_tuple=False)
```
这是`multiRNNCell`的构造函数,此方法的主要参数是单元格,它将是我们要堆栈的`RNNCells`的实例。
![class MultiRNNCell(RNNCell)](img/00113.jpg)
### `learning.ops.split_squeeze(dim, num_split, tensor_in)`
此函数将输入拆分为一个维度,然后压缩拆分后的张量所属的前一个维度。 它需要切割的尺寸,切割方式的数量,然后是张量的切割。 它返回相同的张量,但缩小一维。
\ No newline at end of file
# 示例 1 -- 能耗数据的单变量时间序列预测
在此示例中,我们将解决回归域的问题。 我们将要处理的数据集是一个周期内对一个家庭的许多功耗量度的汇总。 正如我们可以推断的那样,这种行为很容易遵循以下模式(当人们使用微波炉准备早餐时,这种行为会增加,醒来后的电脑数量会有所增加,下午可能会有所减少,而到了晚上,一切都会增加。 灯,从午夜开始直到下一个起床时间减少为零)。
因此,让我们尝试在一个示例案例中对此行为进行建模。
## 数据集说明和加载
在此示例中,我们将使用 [Artur Trindade](https://archive.ics.uci.edu/ml/datasets/ElectricityLoadDiagrams20112014) 的电力负荷图数据集。
这是原始数据集的描述:
> 数据集没有缺失值。 每 15 分钟以 kW 为单位的值。 要以 kWh 为单位转换值,必须将值除以 4。每一列代表一个客户端。 在 2011 年之后创建了一些客户。在这些情况下,消费被视为零。 所有时间标签均以葡萄牙语小时为单位。 但是,整天呈现 96 个小节(24 * 15)。 每年 3 月的时间更改日(只有 23 小时),所有时间点的凌晨 1:00 和 2:00 之间均为零。 每年 10 月的时间变更日(有 25 个小时),上午 1:00 和凌晨 2:00 之间的值合计消耗两个小时。
为了简化我们的模型描述,我们仅对一位客户进行了完整的测量,并将其格式转换为标准 CSV。 它位于本章代码文件夹的数据子文件夹中
使用以下代码行,我们将打开并表示客户的数据:
```py
import pandas as pd
from matplotlib import pyplot as plt
df = pd.read_csv("data/elec_load.csv", error_bad_lines=False)
plt.subplot()
plot_test, = plt.plot(df.values[:1500], label='Load')
plt.legend(handles=[plot_test])
```
![Dataset description and loading](img/00114.jpg)
我看一下这种表示形式(我们看一下前 1500 个样本),我们看到了一个初始瞬态状态,可能是在进行测量时可能出现的状态,然后我们看到了一个清晰的高,低功耗水平的循环。
从简单的观察中,我们还可以看到冰柱或多或少是 100 个样本的,非常接近该数据集每天的 96 个样本。
## 数据集预处理
为了确保反向传播方法更好的收敛性,我们应该尝试对输入数据进行正则化。
因此,我们将应用经典的缩放和居中技术,减去平均值,然后按最大值的底数进行缩放。
为了获得所需的值,我们使用熊猫`describe()`方法。
```py
Load
count 140256.000000
mean 145.332503
std 48.477976
min 0.000000
25% 106.850998
50% 151.428571
75% 177.557604
max 338.218126
```
![Dataset preprocessing](img/00115.jpg)
## 建模架构
在这里,我们将简要描述将尝试对电力消耗变化进行建模的架构:
最终的架构基本上由 10 个成员串联的 LSTM 多单元组成,该单元的末尾具有线性回归或变量,对于给定的历史记录,它将线性单元数组输出的结果转换为最终的实数。 值(在这种情况下,我们必须输入最后 5 个值才能预测下一个)。
```py
def lstm_model(time_steps, rnn_layers, dense_layers=None):
def lstm_cells(layers):
return [tf.nn.rnn_cell.BasicLSTMCell(layer['steps'],state_is_tuple=True)
for layer in layers]
def dnn_layers(input_layers, layers):
return input_layers
def _lstm_model(X, y):
stacked_lstm = tf.nn.rnn_cell.MultiRNNCell(lstm_cells(rnn_layers), state_is_tuple=True)
x_ = learn.ops.split_squeeze(1, time_steps, X)
output, layers = tf.nn.rnn(stacked_lstm, x_, dtype=dtypes.float32)
output = dnn_layers(output[-1], dense_layers)
return learn.models.linear_regression(output, y)
return _lstm_model
```
下图显示了主要模块,随后由学习模块进行了补充,您可以在其中看到 RNN 阶段,优化器以及输出之前的最终线性回归。
![Modelling architecture](img/00116.jpg)
在这张图片中,我们看了 RNN 阶段,在那里我们可以观察到各个 LSTM 单元的级联,输入的挤压以及该学习包所添加的所有互补操作。
![Modelling architecture](img/00117.jpg)
然后,我们将使用回归器完成模型的定义:
```py
regressor = learn.TensorFlowEstimator(model_fn=lstm_model(
TIMESTEPS, RNN_LAYERS, DENSE_LAYERS), n_classes=0,
verbose=2, steps=TRAINING_STEPS, optimizer='Adagrad',
learning_rate=0.03, batch_size=BATCH_SIZE)
```
## 损失函数说明
对于损失函数,经典回归参数均方误差将:
```py
rmse = np.sqrt(((predicted - y['test']) ** 2).mean(axis=0))
```
## 收敛性测试
在这里,我们将为当前模型运行拟合函数:
```py
regressor.fit(X['train'], y['train'], monitors=[validation_monitor], logdir=LOG_DIR)
```
并将获得以下内容(很好)! 错误率。 我们可以做的一项工作是避免对数据进行标准化,并查看平均误差是否相同(注意:不是,差很多)
这是我们将获得的简单控制台输出:
```py
MSE: 0.001139
```
这是生成的损耗/均值图形,它告诉我们误差在每次迭代中如何衰减:
![Convergency test](img/00118.jpg)
## 结果描述
现在我们可以得到真实测试值和预测值的图形,在图形中我们可以看到平均误差表明我们的递归模型具有很好的预测能力:
![Results description](img/00119.jpg)
## 完整源代码
以下是完整的源代码:
```py
import numpy as np
import pandas as pd
import tensorflow as tf
from matplotlib import pyplot as plt
from tensorflow.python.framework import dtypes
from tensorflow.contrib import learn
import logging
logging.basicConfig(level=logging.INFO)
from tensorflow.contrib import learn
from sklearn.metrics import mean_squared_error
LOG_DIR = './ops_logs'
TIMESTEPS = 5
RNN_LAYERS = [{'steps': TIMESTEPS}]
DENSE_LAYERS = None
TRAINING_STEPS = 10000
BATCH_SIZE = 100
PRINT_STEPS = TRAINING_STEPS / 100
def lstm_model(time_steps, rnn_layers, dense_layers=None):
def lstm_cells(layers):
return [tf.nn.rnn_cell.BasicLSTMCell(layer['steps'],state_is_tuple=True)
for layer in layers]
def dnn_layers(input_layers, layers):
return input_layers
def _lstm_model(X, y):
stacked_lstm = tf.nn.rnn_cell.MultiRNNCell(lstm_cells(rnn_layers), state_is_tuple=True)
x_ = learn.ops.split_squeeze(1, time_steps, X)
output, layers = tf.nn.rnn(stacked_lstm, x_, dtype=dtypes.float32)
output = dnn_layers(output[-1], dense_layers)
return learn.models.linear_regression(output, y)
return _lstm_model
regressor = learn.TensorFlowEstimator(model_fn=lstm_model(TIMESTEPS, RNN_LAYERS, DENSE_LAYERS), n_classes=0,
verbose=2, steps=TRAINING_STEPS, optimizer='Adagrad',
learning_rate=0.03, batch_size=BATCH_SIZE)
df = pd.read_csv("data/elec_load.csv", error_bad_lines=False)
plt.subplot()
plot_test, = plt.plot(df.values[:1500], label='Load')
plt.legend(handles=[plot_test])
print df.describe()
array=(df.values- 147.0) /339.0
plt.subplot()
plot_test, = plt.plot(array[:1500], label='Normalized Load')
plt.legend(handles=[plot_test])
listX = []
listy = []
X={}
y={}
for i in range(0,len(array)-6):
listX.append(array[i:i+5].reshape([5,1]))
listy.append(array[i+6])
arrayX=np.array(listX)
arrayy=np.array(listy)
X['train']=arrayX[0:12000]
X['test']=arrayX[12000:13000]
X['val']=arrayX[13000:14000]
y['train']=arrayy[0:12000]
y['test']=arrayy[12000:13000]
y['val']=arrayy[13000:14000]
# print y['test'][0]
# print y2['test'][0]
#X1, y2 = generate_data(np.sin, np.linspace(0, 100, 10000), TIMESTEPS, seperate=False)
# create a lstm instance and validation monitor
validation_monitor = learn.monitors.ValidationMonitor(X['val'], y['val'],
every_n_steps=PRINT_STEPS,
early_stopping_rounds=1000)
regressor.fit(X['train'], y['train'], monitors=[validation_monitor], logdir=LOG_DIR)
predicted = regressor.predict(X['test'])
rmse = np.sqrt(((predicted - y['test']) ** 2).mean(axis=0))
score = mean_squared_error(predicted, y['test'])
print ("MSE: %f" % score)
#plot_predicted, = plt.plot(array[:1000], label='predicted')
plt.subplot()
plot_predicted, = plt.plot(predicted, label='predicted')
plot_test, = plt.plot(y['test'], label='test')
plt.legend(handles=[plot_predicted, plot_test])
```
\ No newline at end of file
此差异已折叠。
# 总结
在本章中,我们回顾了一种最新的神经网络架构,即循环神经网络,从而完善了机器学习领域主流方法的全景。
在下一章中,我们将研究在最先进的实现中出现的不同的神经网络层类型组合,并涵盖一些新的有趣的实验模型。
\ No newline at end of file
# 第 8 章 深度神经网络
在本章中,我们将回顾机器学习,深度神经网络中最先进的技术,也是研究最多的领域之一。
\ No newline at end of file
# 深度神经网络定义
这是一个新闻技术领域蓬勃发展的领域,每天我们都听到成功地将 DNN 用于解决新问题的实验,例如计算机视觉,自动驾驶,语音和文本理解等。
在前几章中,我们使用了与 DNN 相关的技术,尤其是在涉及卷积神经网络的技术中。
出于实际原因,我们将指深度学习和深度神经网络,即其中层数明显优于几个相似层的架构,我们将指代具有数十个层的神经网络架构,或者复杂结构的组合。
\ No newline at end of file
# 穿越时空的深度网络架构
在本节中,我们将回顾从 LeNet5 开始在整个深度学习历史中出现的里程碑架构。
## LeNet 5
在 1980 年代和 1990 年代,神经网络领域一直保持沉默。 尽管付出了一些努力,但是架构非常简单,并且需要大的(通常是不可用的)机器力量来尝试更复杂的方法。
1998 年左右,在 Bells 实验室中,在围绕手写校验数字分类的研究中,Ian LeCun 开始了一种新趋势,该趋势实现了所谓的“深度学习-卷积神经网络”的基础,我们已经在第 5 章,简单的前馈神经网络中对其进行了研究。
在那些年里,SVM 和其他更严格定义的技术被用来解决这类问题,但是有关 CNN 的基础论文表明,与当时的现有方法相比,神经网络的表现可以与之媲美或更好。
\ No newline at end of file
# Alexnet
经过几年的中断(即使 LeCun 继续将其网络应用到其他任务,例如人脸和物体识别),可用结构化数据和原始处理能力的指数增长,使团队得以增长和调整模型, 在某种程度上被认为是不可能的,因此可以增加模型的复杂性,而无需等待数月的训练。
来自许多技术公司和大学的计算机研究团队开始竞争一些非常艰巨的任务,包括图像识别。 对于以下挑战之一,即 Imagenet 分类挑战,开发了 Alexnet 架构:
![Alexnet](img/00125.jpg)
Alexnet 架构
## 主要功能
从其第一层具有卷积运算的意义上讲,Alexnet 可以看作是增强的 LeNet5。 但要添加未使用过的最大池化层,然后添加一系列密集的连接层,以建立最后的输出类别概率层。 视觉几何组(VGG)模型
图像分类挑战的其他主要竞争者之一是牛津大学的 VGG。
VGG 网络架构的主要特征是它们将卷积滤波器的大小减小到一个简单的 3x3,并按顺序组合它们。
微小的卷积内核的想法破坏了 LeNet 及其后继者 Alexnet 的最初想法,后者最初使用的过滤器高达 11x11 过滤器,但复杂得多且表现低下。 过滤器大小的这种变化是当前趋势的开始:
![Main features](img/00126.jpg)
VGG 中每层的参数编号摘要
然而,使用一系列小的卷积权重的积极变化,总的设置是相当数量的参数(数以百万计的数量级),因此它必须受到许多措施的限制。
## 原始的初始模型
在由 Alexnet 和 VGG 主导的两个主要研究周期之后,Google 凭借非常强大的架构 Inception 打破了挑战,该架构具有多次迭代。
这些迭代的第一个迭代是从其自己的基于卷积神经网络层的架构版本(称为 GoogLeNet)开始的,该架构的名称让人想起了始于网络的方法。
## GoogLenet(Inception V1)
![GoogLenet (Inception V1)](img/00127.jpg)
1 启动模块
GoogLeNet 是这项工作的第一个迭代,如下图所示,它具有非常深的架构,但是它具有九个链式初始模块的令人毛骨悚然的总和,几乎没有或根本没有修改:
![GoogLenet (Inception V1)](img/00128.jpg)
盗梦空间原始架构
与两年前发布的 Alexnet 相比,它是如此复杂,但它设法减少了所需的参数数量并提高了准确率。
但是,由于几乎所有结构都由相同原始结构层构建块的确定排列和重复组成,因此提高了此复杂架构的理解和可伸缩性。
## 批量归一化初始化(V2)
2015 年最先进的神经网络在提高迭代效率的同时,还存在训练不稳定的问题。
为了理解问题的构成,首先我们将记住在前面的示例中应用的简单正则化步骤。 它主要包括将这些值以零为中心,然后除以最大值或标准偏差,以便为反向传播的梯度提供良好的基线。
在训练非常大的数据集的过程中,发生的事情是,经过大量训练示例之后,不同的值振荡开始放大平均参数值,就像在共振现象中一样。 我们非常简单地描述的被称为协方差平移。
![Batch normalized inception (V2)](img/00129.jpg)
有和没有批量归一化的表现比较
这是开发批归一化技术的主要原因。
再次简化了过程描述,它不仅包括对原始输入值进行归一化,还对每一层上的输出值进行了归一化,避免了在层之间出现不稳定性之前就开始影响或漂移这些值。
这是 Google 在 2015 年 2 月发布的改进版 GoogLeNet 实现中提供的主要功能,也称为 Inception V2。
\ No newline at end of file
# Inception v3
快进到 2015 年 12 月,Inception 架构有了新的迭代。 两次发行之间月份的不同使我们对新迭代的开发速度有了一个想法。
此架构的基本修改如下:
* 将卷积数减少到最大 3x3
* 增加网络的总体深度
* 在每一层使用宽度增加技术来改善特征组合
下图说明了如何解释改进的启动模块:
![Inception v3](img/00130.jpg)
Inception V3 基本模块
这是整个 V3 架构的表示形式,其中包含通用构建模块的许多实例:
![Inception v3](img/00131.jpg)
Inception V3 总体图
\ No newline at end of file
# 总结
在本章中,我们学习了可应用于数据的主要数据结构和简单操作,并对计算图的各个部分进行了简要总结。
这些操作将成为即将出现的技术的基础。 通过这些类,数据科学家可以在查看当前数据的总体特征之后,确定类的分离或调整特征是否足够清晰,或者直接使用更复杂的工具,从而决定是否使用更简单的模型。
在下一章中,我们将开始构建和运行图,并使用本章中介绍的某些方法来解决问题。
\ No newline at end of file
# 残差网络(ResNet)
残差网络架构于 2015 年 12 月出现(与 Inception V3 几乎同时出现),它带来了一个简单而新颖的想法:不仅使用每个构成层的输出,还将该层的输出与原始输入结合。
在下图中,我们观察到 ResNet 模块之一的简化​​视图。 它清楚地显示了卷积层堆栈末尾的求和运算,以及最终的 relu 运算:
![Residual Networks (ResNet)](img/00132.jpg)
ResNet 一般架构
模块的卷积部分包括将特征从 256 个值减少到 64 个值,一个保留特征数的 3x3 过滤层以及一个从 64 x 256 个值增加 1x1 层的特征。 在最近的发展中,ResNet 的使用深度还不到 30 层,分布广泛。
## 其他深度神经网络架构
最近开发了很多神经网络架构。 实际上,这个领域是如此活跃,以至于我们每年或多或少都有新的杰出建筑外观。 最有前途的神经网络架构的列表是:
* SqueezeNet:此架构旨在减少 Alexnet 的参数数量和复杂性,声称减少了 50 倍的参数数量
* 高效神经网络(Enet):旨在构建更简单,低延迟的浮点运算数量,具有实时结果的神经网络
* Fractalnet:它的主要特征是非常深的网络的实现,不需要残留的架构,将结构布局组织为截断的分形
\ No newline at end of file
此差异已折叠。
# 总结
在本章中,我们一直在学习不同的深度神经网络架构。
我们了解了如何构建近年来最著名的架构之一 VGG,以及如何使用它来生成可转换艺术风格的图像。
在下一章中,我们将使用机器学习中最有用的技术之一:图形处理单元。 我们将回顾安装具有 GPU 支持的 TensorFlow 所需的步骤并对其进行训练,并将执行时间与唯一运行的模型 CPU 进行比较。
\ No newline at end of file
# 第 9 章 大规模运行模型 -- GPU 和服务
到目前为止,我们一直在运行在主机的主 CPU 上运行的代码。 这意味着最多使用所有不同的处理器内核(低端处理器使用 2 或 4 个内核,高级处理器使用多达 16 个内核)。
在过去的十年中,通用处理单元(GPU)已成为所有高表现计算设置中无处不在的部分。 它的大量固有并行度非常适合于高维矩阵乘法以及机器学习模型训练和运行所需的其他运算。
尽管如此,即使拥有真正强大的计算节点,也存在许多任务,即使是最强大的单个服务器也无法应对。
因此,必须开发一种训练和运行模型的分布式方法。 这是分布式 TensorFlow 的原始功能。
在本章中,您将:
* 了解如何发现 TensorFlow 可用的计算资源
* 了解如何将任务分配给计算节点中的任何不同计算单元
* 了解如何记录 GPU 操作
* 了解如何不仅在主主机中而且在许多分布式单元的集群中分布计算
\ No newline at end of file
# TensorFlow 上的 GPU 支持
TensorFlow 对至少两种计算设备具有本机支持:CPU 和 GPU。 为此,它为支持的每种计算设备实现每个操作的一个版本:
![GPU support on TensorFlow](img/00136.jpg)
## 记录设备放置和设备能力
在尝试执行计算之前,TensorFlow 允许您记录所有可用资源。 这样,我们只能将操作应用于现有的计算类型。
### 查询计算能力
为了获取机器上计算元素的日志,我们可以在创建 TensorFlow 会话时使用`log_device_placement`标志,方法是:
```py
python
>>>Import tensorflow as tf
>>>sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
```
这是命令的输出:
![Querying the computing capabilities](img/00137.jpg)
选择 GPU 来运行代码
此长输出主要显示了所需的不同`CUDA`库的加载,然后显示了名称(`GRID K520`)和 GPU 的计算能力。
## 选择用于计算的 CPU
如果我们有可用的 GPU,但仍想继续使用 CPU,则可以通过`tf.Graph.device`方法选择一个。
方法调用如下:
```py
tf.Graph.device(device_name_or_function) :
```
该函数接收处理单元字符串,返回处理单元字符串的函数或不返回处理单元字符串,并返回分配了处理单元的上下文管理器。
如果参数是一个函数,则每个操作都将调用此函数来决定它将在哪个处理单元中执行,这是组合所有操作的有用元素。
### 设备命名
为了指定在指定设备时我们指的是哪个计算单元,TensorFlow 使用以下格式的简单方案:
![Device naming](img/00138.jpg)
设备 ID 格式
设备标识示例包括:
* `"/cpu:0"`:计算机的第一个 CPU
* `"/gpu:0"`:您计算机的 GPU(如果有)
* `"/gpu:1"`:计算机的第二个 GPU,依此类推
可用时,如果没有相反指示,则使用第一个 GPU 设备。
\ No newline at end of file
# 示例 1 -- 将操作分配给 GPU
在此示例中,我们将创建两个张量,将现有 GPU 定位为默认位置,并将在配置了 CUDA 环境的服务器上执行张量总和(您将在附录 A-库安装和其他中学习安装该张量) 提示)。
![Example 1 - assigning an operation to the GPU](img/00139.jpg)
在这里,我们看到常量和求和操作都是在`/gpu:0`服务器上构建的。 这是因为 GPU 是可用时首选的计算设备类型。
\ No newline at end of file
# 示例 2 -- 并行计算 Pi
该示例将作为并行处理的介绍,实现 Pi 的蒙特卡洛近似。
蒙特卡洛(Monte Carlo)利用随机数序列执行近似。
为了解决这个问题,我们将抛出许多随机样本,因为我们知道圆内的样本与正方形上的样本之比与面积比相同。
![Example 2 - calculating Pi number in parallel](img/00140.jpg)
随机区域计算技术
计算假设概率分布均匀,则分配的样本数与图形的面积成比例。
我们使用以下比例:
![Example 2 - calculating Pi number in parallel](img/00141.jpg)
Pi 的面积比例
从上述比例,我们可以推断出圆中的样本数/正方形的样本数也是`0.78`
另一个事实是,我们可以为计算生成的随机样本越多,答案就越近似。 这是在增加 GPU 数量时会给我们带来更多样本和准确率。
我们做的进一步减少是我们生成(X,Y)坐标,范围从(0..1),因此随机数生成更直接。 因此,我们需要确定样本是否属于圆的唯一标准是`distance = d &lt; 1.0`(圆的半径)。
## 解决方案实现
该解决方案将基于 CPU。 它将管理服务器中拥有的 GPU 资源(在本例中为`4`),然后我们将接收结果,并进行最终的样本求和。
### 提示
注意:此方法的收敛速度非常慢,为 O(n &lt;sup class="calibre32"&gt;1/2&lt;/sup&gt; ),但由于其简单性,将作为示例。
![Solution implementation](img/00142.jpg)
计算任务时间表
在上图中,我们看到了计算的并行行为,即样本生成和主要活动计数。
## 源代码
源代码如下:
```py
import tensorflow as tf
import numpy as np
c = []
#Distribute the work between the GPUs
for d in ['/gpu:0', '/gpu:1', '/gpu:2', '/gpu:3']:
#Generate the random 2D samples
i=tf.constant(np.random.uniform(size=10000), shape=[5000,2])
with tf.Session() as sess:
tf.initialize_all_variables()
#Calculate the euclidean distance to the origin
distances=tf.reduce_sum(tf.pow(i,2),1)
#Sum the samples inside the circle
tempsum = sess.run(tf.reduce_sum(tf.cast(tf.greater_equal(tf.cast(1.0,tf.float64),distances),tf.float64)))
#append the current result to the results array
c.append( tempsum)
#Do the final ratio calculation on the CPU
with tf.device('/cpu:0'):
with tf.Session() as sess:
sum = tf.add_n(c)
print (sess.run(sum/20000.0)*4.0)
```
\ No newline at end of file
此差异已折叠。
# 示例 3 -- 分布式 Pi 计算
在此示例中,我们将更改视角,从一台具有多个计算资源的服务器变为一台具有多个资源的服务器集群。
分布式版本的执行将具有不同的设置,如下图所示:
![Example 3 - distributed Pi calculation](img/00146.jpg)
分布式协调运行
## 服务器脚本
该脚本将在每个计算节点上执行,这将生成一批样本,并通过可用服务器的数量增加生成的随机数的数量。 在这种情况下,我们将使用两台服务器,并假设我们在本地主机中启动它们,并在命令行中指示索引号。 如果要在单独的节点中运行它们,则只需替换 ClusterSpec 定义中的本地主机地址(如果希望它更具代表性,则可以替换名称)。
该脚本的源代码如下:
```py
import tensorflow as tf
tf.app.flags.DEFINE_string("index", "0","Server index")
FLAGS = tf.app.flags.FLAGS
print FLAGS.index
cluster = tf.train.ClusterSpec({"local": ["localhost:2222", "localhost:2223"]})
server = tf.train.Server(cluster, job_name="local", task_index=int(FLAGS.index))
server.join()
```
在 localhost 中执行此脚本的命令行如下:
```py
python start_server.py -index=0 #Server task 0
python start_server.py -index=1 #Server task 1
```
这是其中一台服务器的预期输出:
![Server script](img/00147.jpg)
单个服务器启动命令行
## 客户端脚本
然后,我们获得了客户端脚本,该脚本将向集群成员发送随机数创建任务,并将执行最终的 Pi 计算,几乎与 GPU 示例相同。
## 完整源代码
源代码如下:
```py
import tensorflow as tf
import numpy as np
tf.app.flags.DEFINE_integer("numsamples", "100","Number of samples per server")
FLAGS = tf.app.flags.FLAGS
print ("Sample number per server: " + str(FLAGS.numsamples) )
cluster = tf.train.ClusterSpec({"local": ["localhost:2222", "localhost:2223"]})
#This is the list containing the sumation of samples on any node
c=[]
def generate_sum():
i=tf.constant(np.random.uniform(size=FLAGS.numsamples*2), shape=[FLAGS.numsamples,2])
distances=tf.reduce_sum(tf.pow(i,2),1)
return (tf.reduce_sum(tf.cast(tf.greater_equal(tf.cast(1.0,tf.float64),distances),tf.int32)))
with tf.device("/job:local/task:0"):
test1= generate_sum()
with tf.device("/job:local/task:1"):
test2= generate_sum()
#If your cluster is local, you must replace localhost by the address of the first node
with tf.Session("grpc://localhost:2222") as sess:
result = sess.run(tf.cast(test1 + test2,tf.float64)/FLAGS.numsamples*2.0)
print(result)
```
\ No newline at end of file
此差异已折叠。
此差异已折叠。
# 总结
在本章中,我们回顾了 TensorFlow 工具箱中的两个主要元素,以在高表现环境中实现我们的模型,无论是在单服务器还是分布式集群环境中。
在下一章中,我们将查看有关如何在各种环境和工具下安装 TensorFlow 的详细说明。
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
# 使用 TensorFlow 构建机器学习项目中文版
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册