一、TensorFlow 入门
在本章中,我们将介绍一些基本的秘籍,以便了解 TensorFlow 的工作原理以及如何访问本书的数据和其他资源。
到本章结束时,您应该了解以下内容:
谷歌的 TensorFlow 引擎有一种解决问题的独特方式。这种独特的方式使我们能够非常有效地解决机器学习问题。机器学习几乎用于生活和工作的所有领域,但一些更着名的领域是计算机视觉,语音识别,语言翻译,医疗保健等等。我们将介绍了解 TensorFlow 如何运行的基本步骤,并最终在本书后面构建生产代码技术。这些基础知识对于理解本书其余部分的秘籍非常重要。
起初,TensorFlow 中的计算可能看起来不必要地复杂化。但有一个原因:由于 TensorFlow 如何处理计算,开发更复杂的算法相对容易。该秘籍将指导我们完成 TensorFlow 算法的伪代码。
目前,TensorFlow 在 Linux,macOS 和 Windows 上受支持。本书的代码已经在 Linux 系统上创建和运行,但也应该在任何其他系统上运行。该书的代码可在 GitHub 以及 Packt 仓库中找到。
在本书中,我们只关注 TensorFlow 的 Python 库包装器,尽管 TensorFlow 的大多数原始核心代码都是用 C++ 编写的。本书将使用 Python 3.6+ 和 TensorFlow 1.10.0+。虽然 TensorFlow 可以在 CPU 上运行,但是如果在 GPU 上处理,大多数算法运行得更快,并且在具有 Nvidia Compute Capability v4.0+(推荐 v5.1)的显卡上支持。
TensorFlow 的流行 GPU 是 Nvidia Tesla 架构和具有至少 4 GB 视频 RAM 的 Pascal 架构。要在 GPU 上运行,您还需要下载并安装 Nvidia CUDA 工具包以及版本 5.x+。
本章中的一些秘籍将依赖于当前安装的 SciPy,NumPy 和 scikit-learn Python 包。这些随附的包也包含在 Anaconda 包中。
在这里,我们将介绍 TensorFlow 算法的一般流程。大多数秘籍将遵循以下大纲:
import tensorflow as tf
data = tf.nn.batch_norm_with_global_normalization(...)
learning_rate = 0.01
batch_size = 100
iterations = 1000
float32
。 TensorFlow 还提供float64
和float16
。请注意,用于精度的更多字节会导致算法速度变慢,但使用较少会导致精度降低。请参阅以下代码:a_var = tf.constant(42)
x_input = tf.placeholder(tf.float32, [None, input_size])
y_input = tf.placeholder(tf.float32, [None, num_classes])
y = mx + b
):y_pred = tf.add(tf.mul(x_input, weight_matrix), b_matrix)
loss = tf.reduce_mean(tf.square(y_actual - y_pred))
with tf.Session(graph=graph) as session:
...
session.run(...)
...
Note that we can also initiate our graph with:
session = tf.Session(graph=graph)
session.run(...)
在 TensorFlow 中,我们必须先设置数据,变量,占位符和模型,然后才能告诉程序训练和更改变量以改进预测。 TensorFlow 通过计算图完成此任务。这些计算图是没有递归的有向图,这允许计算并行性。为此,我们需要为 TensorFlow 创建一个最小化的损失函数。 TensorFlow 通过修改计算图中的变量来实现此目的。 TensorFlow 知道如何修改变量,因为它跟踪模型中的计算并自动计算变量梯度(如何更改每个变量)以最小化损失。因此,我们可以看到进行更改和尝试不同数据源是多么容易。
有关 TensorFlow 的更多介绍和资源,请参阅官方文档和教程:
张量是 TensorFlow 用于在计算图上操作的主要数据结构。我们可以将这些张量声明为变量和/或将它们作为占位符提供。要做到这一点,首先,我们必须学习如何创建张量。
tensor
是指广义向量或矩阵的数学术语。如果向量是一维的并且矩阵是二维的,则张量是 n 维的(其中n
可以是 1,2 或甚至更大)。
当我们创建一个张量并将其声明为变量时,TensorFlow 会在我们的计算图中创建几个图结构。同样重要的是要指出,仅通过创建张量,TensorFlow 不会向计算图中添加任何内容。 TensorFlow 仅在运行初始化变量的操作后执行此操作。有关更多信息,请参阅下一节有关变量和占位符的内容。
在这里,我们将介绍我们可以在 TensorFlow 中创建张量的主要方法:
1.固定张量:
zero_tsr = tf.zeros([row_dim, col_dim])
ones_tsr = tf.ones([row_dim, col_dim])
filled_tsr = tf.fill([row_dim, col_dim], 42)
constant_tsr = tf.constant([1,2,3])
请注意,
tf.constant()
函数可用于将值广播到数组中,通过编写tf.constant(42, [row_dim, col_dim])
来模仿tf.fill()
的行为。
zeros_similar = tf.zeros_like(constant_tsr)
ones_similar = tf.ones_like(constant_tsr)
请注意,由于这些张量依赖于先前的张量,我们必须按顺序初始化它们。尝试一次初始化所有张量将导致错误。有关变量和占位符,请参阅下一节末尾的“更多”小节。
linspace()
输出和range()
输出非常相似。请参阅以下函数:linear_tsr = tf.linspace(start=0, stop=1, start=3)
得到的张量具有[0.0,0.5,1.0]的序列。请注意,此函数包含指定的停止值。有关更多信息,请参阅以下函数:
integer_seq_tsr = tf.range(start=6, limit=15, delta=3)
结果是序列[6,9,12]。请注意,此函数不包括限制值。
randunif_tsr = tf.random_uniform([row_dim, col_dim], minval=0, maxval=1)
注意,这种随机均匀分布来自包含minval
但不包括maxval
(minval >= x < maxval
)的区间。
要从正态分布中获取随机抽取的张量,可以运行以下代码:
randnorm_tsr = tf.random_normal([row_dim, col_dim], mean=0.0, stddev=1.0)
有时候我们想要生成在某些范围内保证的正常随机值。truncated_normal()
函数总是在指定均值的两个标准偏差内选择正常值:
runcnorm_tsr = tf.truncated_normal([row_dim, col_dim], mean=0.0, stddev=1.0)
我们可能也对随机化数组条目感兴趣。要做到这一点,有两个函数可以帮助我们:random_shuffle()
和random_crop()
。以下代码执行此操作:
shuffled_output = tf.random_shuffle(input_tensor)
cropped_output = tf.random_crop(input_tensor, crop_size)
在本书的后面,我们将有兴趣随机裁剪大小(高度,宽度,3)的图像,其中有三种颜色光谱。要修复cropped_output
中的大小,您必须在该大小中为其指定最大大小:
cropped_image = tf.random_crop(my_image, [height/2, width/2, 3])
一旦我们决定如何创建张量,我们也可以通过在Variable()
函数中包含张量来创建相应的变量,如下所示(下一节将详细介绍):
my_var = tf.Variable(tf.zeros([row_dim, col_dim]))
我们不仅限于内置函数:我们可以使用convert_to_tensor()
函数将任何 NumPy 数组转换为 Python 列表,或将常量转换为张量。注意,如果我们希望概括函数内部的计算,该函数也接受张量作为输入。
占位符和变量是在 TensorFlow 中使用计算图的关键工具。我们必须了解它们之间的区别以及何时最好地利用它们对我们有利。
与数据最重要的区别之一是它是占位符还是变量。变量是算法的模型参数,TensorFlow 跟踪如何更改这些参数以优化算法。占位符是允许您提供特定类型和形状的数据的对象,或者取决于计算图的结果,例如计算的预期结果。
创建变量的主要方法是使用Variable()
函数,该函数将张量作为输入并输出变量。这只是声明,我们仍然需要初始化变量。初始化是将变量与相应方法放在计算图上的内容。以下是创建和初始化变量的示例:
my_var = tf.Variable(tf.zeros([2,3]))
sess = tf.Session()
initialize_op = tf.global_variables_initializer()
sess.run(initialize_op)
要在创建和初始化变量后查看计算图是什么样的,请参阅此秘籍的以下部分。占位符只是保持数据的位置以输入图。占位符从会话中的feed_dict
参数获取数据。要将占位符放入图中,我们必须对占位符执行至少一个操作。在下面的代码片段中,我们初始化图,将x
声明为占位符(预定义大小),并将y
定义为x
上的标识操作,它只返回x
。然后,我们创建数据以提供给x
占位符并运行身份操作。代码如下所示,结果图如下:
sess = tf.Session()
x = tf.placeholder(tf.float32, shape=[2,2])
y = tf.identity(x)
x_vals = np.random.rand(2,2)
sess.run(y, feed_dict={x: x_vals})
# Note that sess.run(x, feed_dict={x: x_vals}) will result in a self-referencing error.
值得注意的是,TensorFlow 不会在馈送字典中返回自引用占位符。换句话说,在下图中运行
sess.run(x, feed_dict={x: x_vals})
将返回误差。
将变量初始化为零张量的计算图如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tiYSQoax-1681566813062)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/9aed31ea-b420-4fb7-9ed3-c05a7a15d926.png)]
图 1:变量
在这里,我们只用一个变量就可以看到计算图的详细信息,并将其全部初始化为零。灰色阴影区域是对所涉及的操作和常数的非常详细的视图。细节较少的主要计算图是右上角灰色区域之外的较小图。有关创建和可视化图的更多详细信息,请参阅第 10 章的第一部分,将 TensorFlow 转换为生产。类似地,可以在下图中看到将 NumPy 数组送入占位符的计算图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RHMzUkVg-1681566813063)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/3cb49bac-be51-46c3-ac43-589c2f04b799.png)]
图 2:初始化占位符的计算图
灰色阴影区域是对所涉及的操作和常数的非常详细的视图。细节较少的主要计算图是右上角灰色区域之外的较小图。
在计算图运行期间,我们必须告诉 TensorFlow 何时初始化我们创建的变量。虽然每个变量都有一个initializer
方法,但最常用的方法是使用辅助函数,即global_variables_initializer()
。此函数在图中创建一个初始化我们创建的所有变量的操作,如下所示:
initializer_op = tf.global_variables_initializer()
但是如果我们想根据初始化另一个变量的结果来初始化变量,我们必须按照我们想要的顺序初始化变量,如下所示:
sess = tf.Session()
first_var = tf.Variable(tf.zeros([2,3]))
sess.run(first_var.initializer)
second_var = tf.Variable(tf.zeros_like(first_var))
# 'second_var' depends on the 'first_var'
sess.run(second_var.initializer)
了解 TensorFlow 如何与矩阵一起工作对于通过计算图来理解数据流非常重要。
值得强调的是矩阵在机器学习(以及一般数学)中的重要性。大多数机器学习算法在计算上表示为矩阵运算。本书未涉及矩阵属性和矩阵代数(线性代数)的数学背景,因此强烈建议读者充分了解矩阵以适应矩阵代数。
许多算法依赖于矩阵运算。 TensorFlow 为我们提供了易于使用的操作来执行此类矩阵计算。对于以下所有示例,我们首先通过运行以下代码来创建图会话:
import tensorflow as tf
sess = tf.Session()
我们将按如下方式处理秘籍:
zeros()
,ones()
,truncated_normal()
等函数指定二维形状。 TensorFlow 还允许我们使用diag()
函数从一维数组或列表创建对角矩阵,如下所示:identity_matrix = tf.diag([1.0, 1.0, 1.0])
A = tf.truncated_normal([2, 3])
B = tf.fill([2,3], 5.0)
C = tf.random_uniform([3,2])
D = tf.convert_to_tensor(np.array([[1., 2., 3.],[-3., -7., -1.],[0., 5., -2.]]))
print(sess.run(identity_matrix))
[[ 1\. 0\. 0.]
[ 0\. 1\. 0.]
[ 0\. 0\. 1.]]
print(sess.run(A))
[[ 0.96751703 0.11397751 -0.3438891 ]
[-0.10132604 -0.8432678 0.29810596]]
print(sess.run(B))
[[ 5\. 5\. 5.]
[ 5\. 5\. 5.]]
print(sess.run(C))
[[ 0.33184157 0.08907614]
[ 0.53189191 0.67605299]
[ 0.95889051 0.67061249]]
print(sess.run(D))
[[ 1\. 2\. 3.]
[-3\. -7\. -1.]
[ 0\. 5\. -2.]]
请注意,如果我们再次运行
sess.run(C)
,我们将重新初始化随机变量并最终得到不同的随机值。
print(sess.run(A+B))
[[ 4.61596632 5.39771316 4.4325695 ]
[ 3.26702736 5.14477345 4.98265553]]
print(sess.run(B-B))
[[ 0\. 0\. 0.]
[ 0\. 0\. 0.]]
Multiplication
print(sess.run(tf.matmul(B, identity_matrix)))
[[ 5\. 5\. 5.]
[ 5\. 5\. 5.]]
值得注意的是,matmul()
函数具有参数,用于指定是否在乘法之前转置参数或每个矩阵是否稀疏。
请注意,未明确定义矩阵除法。虽然许多人将矩阵划分定义为乘以逆,但与实数除法相比,它基本上是不同的。
print(sess.run(tf.transpose(C)))
[[ 0.67124544 0.26766731 0.99068872]
[ 0.25006068 0.86560275 0.58411312]]
同样,值得一提的是,重新初始化为我们提供了与以前不同的值。
print(sess.run(tf.matrix_determinant(D)))
-38.0
print(sess.run(tf.matrix_inverse(D)))
[[-0.5 -0.5 -0.5 ]
[ 0.15789474 0.05263158 0.21052632]
[ 0.39473684 0.13157895 0.02631579]]
只有当矩阵是对称正定时,逆方法才基于 Cholesky 分解。如果矩阵不是对称正定,那么它基于 LU 分解。
print(sess.run(tf.cholesky(identity_matrix)))
[[ 1\. 0\. 1.]
[ 0\. 1\. 0.]
[ 0\. 0\. 1.]]
print(sess.run(tf.self_adjoint_eig(D))
[[-10.65907521 -0.22750691 2.88658212]
[ 0.21749542 0.63250104 -0.74339638]
[ 0.84526515 0.2587998 0.46749277]
[ -0.4880805 0.73004459 0.47834331]]
注意,self_adjoint_eig()
函数输出第一行中的特征值和剩余向量中的后续向量。在数学中,这被称为矩阵的特征分解。
TensorFlow 为我们提供了开始使用数值计算并将这些计算添加到图中的所有工具。对于简单的矩阵运算,这种表示法可能看起来很重。请记住,我们正在将这些操作添加到图中,并告诉 TensorFlow 哪些张量运行这些操作。虽然现在看起来似乎很冗长,但它有助于我们理解后面章节中的符号,这种计算方式将使我们更容易实现目标。
现在,我们必须了解我们可以添加到 TensorFlow 图的其他操作。
除了标准算术运算之外,TensorFlow 还为我们提供了更多我们应该了解的操作以及如何在继续操作之前使用它们。同样,我们可以通过运行以下代码来创建图会话:
import tensorflow as tf
sess = tf.Session()
TensorFlow 对张量有标准操作,即add()
,sub()
,mul()
和div()
。请注意,除非另有说明,否则本节中的所有操作都将按元素评估输入:
div()
和相关函数的一些变体。div()
返回与输入相同的类型。这意味着如果输入是整数,它确实返回了分区的底线(类似于 Python2)。要返回 Python3 版本,它在分割之前将整数转换为浮点数并始终返回浮点数,TensorFlow 提供truediv()
函数,如下所示:print(sess.run(tf.div(3, 4)))
0
print(sess.run(tf.truediv(3, 4)))
0.75
floordiv()
函数。请注意,这仍然会返回一个浮点数,但它会向下舍入到最接近的整数。这个函数如下:print(sess.run(tf.floordiv(3.0,4.0)))
0.0
mod()
。此函数返回除法后的余数。它如下:print(sess.run(tf.mod(22.0, 5.0)))
2.0
cross()
函数实现。请记住,交叉乘积仅针对两个三维向量定义,因此它只接受两个三维张量。以下代码说明了这种用法:print(sess.run(tf.cross([1., 0., 0.], [0., 1., 0.])))
[ 0\. 0\. 1.0]
abs() | 输入张量的绝对值 |
ceil() | 输入张量的向上取整函数 |
cos() | 输入张量的余弦函数 |
exp() | 输入张量的基于e指数 |
floor() | 输入张量的向下取整函数 |
inv() | 输入张量的乘法逆(1 / x) |
log() | 输入张量的自然对数 |
maximum() | 两个张量的逐元素最大值 |
minimum() | 两个张量的逐元素最小值 |
neg() | 输入张量的反转 |
pow() | 第一个张量元素的第二个张量元素次幂 |
round() | 输入张量的舍入 |
rsqrt() | 输入张量的平方根倒数 |
sign() | 返回 -1,0 或 1,具体取决于张量的符号 |
sin() | 输入张量的正弦函数 |
sqrt() | 输入张量的平方根 |
square() | 输入张量的平方 |
digamma() | Psi 函数,lgamma()函数的导数 |
erf() | 张量的逐元素高斯误差函数 |
erfc() | 张量的互补误差函数 |
igamma() | 较低正则化的不完全伽玛函数 |
igammac() | 较高正则化的不完全伽马函数 |
lbeta() | Beta 函数绝对值的自然对数 |
lgamma() | 伽玛函数绝对值的自然对数 |
squared_difference() | 两个张量之间差异的平方 |
重要的是要知道我们可以使用哪些函数,以便我们可以将它们添加到我们的计算图中。我们将主要关注前面的函数。我们还可以生成许多不同的自定义函数作为前面的组合,如下所示:
# Tangent function (tan(pi/4)=1)
print(sess.run(tf.tan(3.1416/4.)))
1.0
如果我们希望向我们未在此处列出的图添加其他操作,我们必须从前面的函数创建自己的操作。以下是我们之前未使用的操作示例,我们可以将其添加到图中。我们选择使用以下代码添加自定义多项式函数3x^2 - x + 10
:
def custom_polynomial(value):
return tf.sub(3 * tf.square(value), value) + 10
print(sess.run(custom_polynomial(11)))
362
激活函数是神经网络近似非线性输出并适应非线性特征的关键。他们将非线性运算引入神经网络。如果我们小心选择了哪些激活函数以及放置它们的位置,它们是非常强大的操作,我们可以告诉 TensorFlow 适合和优化。
当我们开始使用神经网络时,我们将定期使用激活函数,因为激活函数是任何神经网络的重要组成部分。激活函数的目标只是调整权重和偏差。在 TensorFlow 中,激活函数是作用于张量的非线性操作。它们是以与先前的数学运算类似的方式运行的函数。激活函数有很多用途,但主要的概念是它们在对输出进行归一化的同时在图中引入了非线性。使用以下命令启动 TensorFlow 图:
import tensorflow as tf
sess = tf.Session()
激活函数存在于 TensorFlow 中的神经网络(nn
)库中。除了使用内置激活函数外,我们还可以使用 TensorFlow 操作设计自己的函数。我们可以导入预定义的激活函数(import tensorflow.nn as nn
)或显式,并在函数调用中写入nn
。在这里,我们选择明确每个函数调用:
max(0,x)
。它是连续的,但不是平滑的。它看起来如下:print(sess.run(tf.nn.relu([-3., 3., 10.])))
[ 0\. 3\. 10.]
max(0,x)
函数嵌套到min()
函数中来实现。 TensorFlow 具有的实现称为 ReLU6 函数。这被定义为min(max(0,x),6)
。这是硬 sigmoid 函数的一个版本,并且计算速度更快,并且不会消失(无穷小接近零)或爆炸值。当我们在第 8 章,卷积神经网络和第 9 章,循环神经网络中讨论更深层的神经网络时,这将派上用场。它看起来如下:print(sess.run(tf.nn.relu6([-3., 3., 10.])))
[ 0\. 3\. 6.]
1 / (1 + exp(-x))
。 Sigmoid 函数不经常使用,因为它倾向于在训练期间将反向传播项置零。它看起来如下:print(sess.run(tf.nn.sigmoid([-1., 0., 1.])))
[ 0.26894143 0.5 0.7310586 ]
我们应该知道一些激活函数不是以零为中心的,例如 sigmoid。这将要求我们在大多数计算图算法中使用之前将数据归零。
(exp(x) - exp(-x)) / (exp(x) + exp(-x))
。此激活函数如下:print(sess.run(tf.nn.tanh([-1., 0., 1.])))
[-0.76159418 0\. 0.76159418 ]
softsign
函数也可用作激活函数。该函数的形式是x / (|x| + 1)
。softsign
函数应该是符号函数的连续(但不是平滑)近似。请参阅以下代码:print(sess.run(tf.nn.softsign([-1., 0., -1.])))
[-0.5 0\. 0.5]
softplus
函数,是 ReLU 函数的流畅版本。该函数的形式是log(exp(x) + 1)
。它看起来如下:print(sess.run(tf.nn.softplus([-1., 0., -1.])))
[ 0.31326166 0.69314718 1.31326163]
当输入增加时,
softplus
函数变为无穷大,而softsign
函数变为 1.然而,当输入变小时,softplus
函数接近零,softsign
函数变为 -1。
x < 0
其他x
,则形式为exp(x) + 1
。它看起来如下:print(sess.run(tf.nn.elu([-1., 0., -1.])))
[-0.63212055 0\. 1\. ]
这些激活函数是我们将来可以在神经网络或其他计算图中引入非线性的方法。重要的是要注意我们的网络中我们使用激活函数的位置。如果激活函数的范围在 0 和 1(sigmoid)之间,则计算图只能输出 0 到 1 之间的值。如果激活函数在内部并隐藏在节点之间,那么我们想要知道当我们传递它们时,范围可以在我们的张量上。如果我们的张量被缩放到平均值为零,我们将希望使用一个激活函数来保持尽可能多的方差在零附近。这意味着我们想要选择激活函数,例如双曲正切(tanh)或 softsign。如果张量都被缩放为正数,那么我们理想地选择一个激活函数来保留正域中的方差。
以下是两个绘图,说明了不同的激活函数。下图显示了 ReLU,ReLU6,softplus,指数 LU,sigmoid,softsign 和双曲正切函数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lNR0V2s0-1681566813063)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/2a1256eb-1993-4e62-a561-48577ebcfec2.png)]
图 3:softplus,ReLU,ReLU6 和指数 LU 的激活函数
在这里,我们可以看到四个激活函数:softplus,ReLU,ReLU6 和指数 LU。这些函数在零的左边展平并线性增加到零的右边,但 ReLU6 除外,其最大值为 6:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ve7xHOtM-1681566813063)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/d2167d3b-96f9-46d1-87c4-79d0839b3745.png)]
图 4:Sigmoid,双曲正切(tanh)和 softsign 激活函数
这是 sigmoid,双曲正切(tanh)和 softsign 激活函数。这些激活函数都是平滑的,具有S n
形状。请注意,这些函数有两个水平渐近线。
对于本书的大部分内容,我们将依赖数据集的使用来适应机器学习算法。本节介绍如何通过 TensorFlow 和 Python 访问每个数据集。
一些数据源依赖于外部网站的维护,以便您可以访问数据。如果这些网站更改或删除此数据,则可能需要更新本节中的以下某些代码。您可以在作者的 GitHub 页面上找到更新的代码。
在 TensorFlow 中,我们将使用的一些数据集构建在 Python 库中,一些将需要 Python 脚本下载,一些将通过互联网手动下载。几乎所有这些数据集都需要有效的互联网连接,以便您可以检索它们。
from sklearn import datasets
iris = datasets.load_iris()
print(len(iris.data))
150
print(len(iris.target))
150
print(iris.data[0]) # Sepal length, Sepal width, Petal length, Petal width
[ 5.1 3.5 1.4 0.2]
print(set(iris.target)) # I. setosa, I. virginica, I. versicolor
{0, 1, 2}
import requests
birthdata_url = 'https://github.com/nfmcclure/tensorflow_cookbook/raw/master/01_Introduction/07_Working_with_Data_Sources/birthweight_data/birthweight.dat'
birth_file = requests.get(birthdata_url)
birth_data = birth_file.text.split('\r\n')
birth_header = birth_data[0].split('\t')
birth_data = [[float(x) for x in y.split('\t') if len(x)>=1] for y in birth_data[1:] if len(y)>=1]
print(len(birth_data))
189
print(len(birth_data[0]))
9
from keras.datasets import boston_housing
(x_train, y_train), (x_test, y_test) = boston_housing.load_data()
housing_header = ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV']
print(x_train.shape[0])
404
print(x_train.shape[1])
13
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/"," one_hot=True)
print(len(mnist.train.images))
55000
print(len(mnist.test.images))
10000
print(len(mnist.validation.images))
5000
print(mnist.train.labels[1,:]) # The first label is a 3
[ 0\. 0\. 0\. 1\. 0\. 0\. 0\. 0\. 0\. 0.]
.zip
文件并获取垃圾邮件文本数据,如下所示:import requests
import io
from zipfile import ZipFile
zip_url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/00228/smsspamcollection.zip'
r = requests.get(zip_url)
z = ZipFile(io.BytesIO(r.content))
file = z.read('SMSSpamCollection')
text_data = file.decode()
text_data = text_data.encode('ascii',errors='ignore')
text_data = text_data.decode().split('\n')
text_data = [x.split('\t') for x in text_data if len(x)>=1]
[text_data_target, text_data_train] = [list(x) for x in zip(*text_data)]
print(len(text_data_train))
5574
print(set(text_data_target))
{'ham', 'spam'}
print(text_data_train[1])
Ok lar... Joking wif u oni...
import requests
import io
import tarfile
movie_data_url = 'http://www.cs.cornell.edu/people/pabo/movie-review-data/rt-polaritydata.tar.gz'
r = requests.get(movie_data_url)
# Stream data into temp object
stream_data = io.BytesIO(r.content)
tmp = io.BytesIO()
while True:
s = stream_data.read(16384)
if not s:
break
tmp.write(s)
stream_data.close()
tmp.seek(0)
# Extract tar file
tar_file = tarfile.open(fileobj=tmp, mode="r:gz")
pos = tar_file.extractfile('rt-polaritydata/rt-polarity.pos')
neg = tar_file.extractfile('rt-polaritydata/rt-polarity.neg')
# Save pos/neg reviews (Also deal with encoding)
pos_data = []
for line in pos:
pos_data.append(line.decode('ISO-8859-1').encode('ascii',errors='ignore').decode())
neg_data = []
for line in neg:
neg_data.append(line.decode('ISO-8859-1').encode('ascii',errors='ignore').decode())
tar_file.close()
print(len(pos_data))
5331
print(len(neg_data))
5331
# Print out first negative review
print(neg_data[0])
simplistic , silly and tedious .
32 x 32
像素)。有 10 种不同的目标类别(飞机,汽车,鸟类等)。 CIFAR-10 是包含 60,000 张图像的子集。训练集中有 50,000 个图像,测试集中有 10,000 个。由于我们将以多种方式使用此数据集,并且因为它是我们较大的数据集之一,因此我们不会在每次需要时运行脚本。要获取此数据集,请导航至此链接并下载 CIFAR-10 数据集。我们将在相应的章节中介绍如何使用此数据集。import requests
shakespeare_url = 'http://www.gutenberg.org/cache/epub/100/pg100.txt'
# Get Shakespeare text
response = requests.get(shakespeare_url)
shakespeare_file = response.content
# Decode binary into string
shakespeare_text = shakespeare_file.decode('utf-8')
# Drop first few descriptive paragraphs.
shakespeare_text = shakespeare_text[7675:]
print(len(shakespeare_text)) # Number of characters
5582212
import requests
import io
from zipfile import ZipFile
sentence_url = 'http://www.manythings.org/anki/deu-eng.zip'
r = requests.get(sentence_url)
z = ZipFile(io.BytesIO(r.content))
file = z.read('deu.txt')
# Format Data
eng_ger_data = file.decode()
eng_ger_data = eng_ger_data.encode('ascii',errors='ignore')
eng_ger_data = eng_ger_data.decode().split('\n')
eng_ger_data = [x.split('\t') for x in eng_ger_data if len(x)>=1]
[english_sentence, german_sentence] = [list(x) for x in zip(*eng_ger_data)]
print(len(english_sentence))
137673
print(len(german_sentence))
137673
print(eng_ger_data[10])
['I' won!, 'Ich habe gewonnen!']
当在秘籍中使用这些数据集之一时,我们将引用您到本节并假设数据以上一节中描述的方式加载。如果需要进一步的数据转换或预处理,那么这些代码将在秘籍本身中提供。
以下是我们在本书中使用的数据资源的其他参考:
在本节中,您将找到对学习和使用 TensorFlow 有很大帮助的其他链接,文档资源和教程。
在学习如何使用 TensorFlow 时,有助于知道在哪里寻求帮助或指针。本节列出了运行 TensorFlow 和解决问题的资源。
以下是 TensorFlow 资源列表:
在本章中,我们将介绍 TensorFlow 如何运作的关键组件。然后,我们将它们组合在一起以创建一个简单的分类器并评估结果。到本章结束时,您应该了解以下内容:
现在我们已经介绍了 TensorFlow 如何创建张量,并使用变量和占位符,我们将介绍如何在计算图中对这些对象进行操作。由此,我们可以设置一个简单的分类器,看看它的表现如何。
此外,请记住,本书中的当前和更新代码可以在 GitHub 上在线获取。
现在我们可以将对象放入计算图中,我们将介绍对这些对象起作用的操作。
要启动图,我们加载 TensorFlow 并创建一个会话,如下所示:
import tensorflow as tf
sess = tf.Session()
在这个例子中,我们将结合我们学到的东西并将列表中的每个数字提供给图中的操作并打印输出:
首先,我们宣布我们的张量和占位符。在这里,我们将创建一个 NumPy 数组来提供给我们的操作:
import numpy as np
x_vals = np.array([1., 3., 5., 7., 9.])
x_data = tf.placeholder(tf.float32)
m_const = tf.constant(3.)
my_product = tf.multiply(x_data, m_const)
for x_val in x_vals:
print(sess.run(my_product, feed_dict={x_data: x_val}))
上述代码的输出如下:
3.0
9.0
15.0
21.0
27.0
本节中的代码在计算图上创建数据和操作。下图是计算图的样子:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hfSHVMfE-1681566813064)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/c92d9c7b-a07f-48c5-9717-01ced228ffe9.png)]
图 1:x_data
占位符以及乘法常数输入到乘法运算中
在本文中,我们将学习如何在同一计算图上放置多个操作。
了解如何将操作链接在一起非常重要。这将在计算图中设置分层操作。对于演示,我们将占位符乘以两个矩阵,然后执行加法。我们将以三维 NumPy 数组的形式提供两个矩阵:
import tensorflow as tf
sess = tf.Session()
同样重要的是要注意数据在通过时如何改变形状。我们将输入两个大小为3 x 5
的 NumPy 数组。我们将每个矩阵乘以一个大小常数5 x 1,
,这将产生一个大小为3 x 1
的矩阵。然后我们将其乘以1 x 1
矩阵,再次产生3 x 1
矩阵。最后,我们在最后添加3 x 1
矩阵,如下所示:
my_array = np.array([[1., 3., 5., 7., 9.],
[-2., 0., 2., 4., 6.],
[-6., -3., 0., 3., 6.]])
x_vals = np.array([my_array, my_array + 1])
x_data = tf.placeholder(tf.float32, shape=(3, 5))
m1 = tf.constant([[1.], [0.], [-1.], [2.], [4.]])
m2 = tf.constant([[2.]])
a1 = tf.constant([[10.]])
prod1 = tf.matmul(x_data, m1)
prod2 = tf.matmul(prod1, m2)
add1 = tf.add(prod2, a1)
for x_val in x_vals:
print(sess.run(add1, feed_dict={x_data: x_val}))
[[ 102.]
[ 66.]
[ 58.]]
[[ 114.]
[ 78.]
[ 70.]]
我们刚刚创建的计算图可以使用 TensorBoard 进行可视化。 TensorBoard 是 TensorFlow 的一个功能,它允许我们可视化计算图和这些图中的值。与其他机器学习框架不同,这些功能是本机提供的。要了解如何完成此操作,请参阅第 11 章中的 TensorBoard 秘籍中的可视化图,更多内容使用 TensorFlow。以下是我们的分层图如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-usXCeT6J-1681566813064)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/e14ff4a8-12d8-42ba-968f-87b50c1afc6d.png)]
图 2:向上传播到图时的数据大小
在通过图运行数据之前,我们必须声明数据形状并知道操作的结果形状。这并非总是如此。可能有一两个我们事先不知道的维度,或者一些可能变化的维度。为实现此目的,我们将可以改变(或未知)的维度或维度指定为值None
。例如,要使先前的数据占位符具有未知数量的列,我们将编写以下行:
x_data = tf.placeholder(tf.float32, shape=(3,None))
这允许我们打破矩阵乘法规则,但我们仍然必须遵守乘法常数必须具有相同行数的事实。当我们将数据输入图时,我们可以动态生成或重新整形x_data
。当我们以不同批次大小的多批次提供数据时,这将在后面的章节中派上用场。
虽然使用
None
作为大小允许我们使用可变大小的大小,但在填充大小时始终建议尽可能明确。如果我们将大小标准化为固定大小,那么我们应该明确地将该大小写为大小。建议将None
用作维度,以限制数据的批量大小(或我们一次计算的数据点数)。
现在我们已经介绍了多个操作,我们将介绍如何连接具有通过它们传播的数据的各个层。
在本文中,我们将介绍如何最好地连接各种层,包括自定义层。我们将生成和使用的数据将代表小型随机图像。最好通过一个简单的例子来理解这种类型的操作,看看我们如何使用一些内置层来执行计算。我们将探索的第一层称为移动窗口。我们将在 2D 图像上执行小的移动窗口平均值,然后第二层将是自定义操作层。
在本节中,我们将看到计算图可能变得庞大且难以查看。为了解决这个问题,我们还将介绍命名操作和创建层范围的方法。首先,加载numpy
和tensorflow
,然后使用以下命令创建图:
import tensorflow as tf
import numpy as np
sess = tf.Session()
我们按如下方式处理秘籍:
4 x 4
像素图像。我们将在四个方面创建它;第一个和最后一个维度的大小为 1。请注意,某些 TensorFlow 图像函数将在四维图像上运行。这四个维度是图像编号,高度,宽度和通道,为了使其成为一个具有一个通道的图像,我们将两个维度设置为1
,如下所示:x_shape = [1, 4, 4, 1]
x_val = np.random.uniform(size=x_shape)
x_data = tf.placeholder(tf.float32, shape=x_shape)
4 x 4
图像上创建一个移动窗口平均值,我们将使用一个内置函数,它将在形状窗口2 x 2
上收敛一个常量。我们将使用的函数是conv2d()
;此函数在图像处理和 TensorFlow 中非常常用。此函数采用窗口的分段产品和我们指定的过滤器。我们还必须在两个方向上指定移动窗口的步幅。在这里,我们将计算四个移动窗口平均值:左上角,右上角,左下角和右下角四个像素。我们通过创建2 x 2
窗口并在每个方向上具有长度2
的步幅来实现这一点。为取平均值,我们将2 x 2
窗口用0.25
的常数卷积,如下:my_filter = tf.constant(0.25, shape=[2, 2, 1, 1])
my_strides = [1, 2, 2, 1]
mov_avg_layer= tf.nn.conv2d(x_data, my_filter, my_strides,
padding='SAME', name='Moving_Avg_Window')
请注意,我们还使用函数的
name
参数命名此层Moving_Avg_Window
。 为了计算卷积层的输出大小,我们可以使用下面的公式: Output = (W - F + 2P) / S + 1
,其中W
是输入大小,F
是过滤器大小,P
是零填充,并且S
是步幅。
2 x 2
输出上运行。自定义函数将首先将输入乘以另一个2 x 2
矩阵张量,然后为每个条目添加 1。在此之后,我们取每个元素的 sigmoid 并返回2 x 2
矩阵。由于矩阵乘法仅对二维矩阵进行操作,因此我们需要删除大小为1
的图像的额外维度。 TensorFlow 可以使用内置的squeeze()
函数执行此操作。在这里,我们定义新层: def custom_layer(input_matrix):
input_matrix_sqeezed = tf.squeeze(input_matrix)
A = tf.constant([[1., 2.], [-1., 3.]])
b = tf.constant(1., shape=[2, 2])
temp1 = tf.matmul(A, input_matrix_sqeezed)
temp = tf.add(temp1, b) # Ax + b
return tf.sigmoid(temp)
with tf.name_scope('Custom_Layer') as scope:
custom_layer1 = custom_layer(mov_avg_layer)
4 x 4
图像来替换占位符并告诉 TensorFlow 运行图,如下所示:print(sess.run(custom_layer1, feed_dict={x_data: x_val}))
[[ 0.91914582 0.96025133]
[ 0.87262219 0.9469803 ]]
通过命名操作和层范围,可视化绘图看起来更好。我们可以折叠和展开自定义层,因为我们在命名范围内创建了它。在下图中,请参阅左侧的折叠版本和右侧的展开版本:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9nG29oYh-1681566813064)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/16011597-1adf-4b5c-b12e-38eed5a3e596.png)]
图 3:具有两层的计算图
第一层名为Moving_Avg_Window
。第二个是名为Custom_Layer
的操作集合。它在左侧折叠并在右侧展开。
损失函数对于机器学习算法非常重要。它们测量模型输出与目标(真值)值之间的距离。在这个秘籍中,我们在 TensorFlow 中展示了各种损失函数实现。
为了优化我们的机器学习算法,我们需要评估结果。评估 TensorFlow 中的结果取决于指定损失函数。损失函数告诉 TensorFlow 预测与期望结果相比有多好或多坏。在大多数情况下,我们将有一组数据和一个目标来训练我们的算法。损失函数将目标与预测进行比较,并给出两者之间的数值距离。
对于这个秘籍,我们将介绍我们可以在 TensorFlow 中实现的主要损失函数。
要了解不同损失函数的运行方式,我们将在此秘籍中绘制它们。我们将首先启动一个计算图并加载matplotlib
,一个 Python 绘图库,如下所示:
import matplotlib.pyplot as plt
import tensorflow as tf
x_vals = tf.linspace(-1., 1., 500)
target = tf.constant(0.)
l2_y_vals = tf.square(target - x_vals)
l2_y_out = sess.run(l2_y_vals)
TensorFlow 具有 L2 范数的内置形式,称为
nn.l2_loss()
。这个函数实际上是 L2 范数的一半。换句话说,它与前一个相同,但除以 2。
l1_y_vals = tf.abs(target - x_vals)
l1_y_out = sess.run(l1_y_vals)
delta
,它决定了它的陡峭程度。我们将绘制两种形式,delta1 = 0.25
和delta2 = 5
,以显示差异,如下所示:delta1 = tf.constant(0.25)
phuber1_y_vals = tf.multiply(tf.square(delta1), tf.sqrt(1\. +
tf.square((target - x_vals)/delta1)) - 1.)
phuber1_y_out = sess.run(phuber1_y_vals)
delta2 = tf.constant(5.)
phuber2_y_vals = tf.multiply(tf.square(delta2), tf.sqrt(1\. +
tf.square((target - x_vals)/delta2)) - 1.)
phuber2_y_out = sess.run(phuber2_y_vals)
现在,我们继续讨论分类问题的损失函数。分类损失函数用于在预测分类结果时评估损失。通常,我们的类别类型的输出是 0 到 1 之间的实数值。然后,我们选择截止值(通常选择 0.5)并且如果数字高于截止值,则将结果分类为该类别。在这里,我们考虑分类输出的各种损失函数:
x_vals
)和target
。我们将保存输出并在下一节中绘制它们。使用以下内容:x_vals = tf.linspace(-3., 5., 500)
target = tf.constant(1.)
targets = tf.fill([500,], 1.)
1
,因此我们的预测越接近 1,损失值越低:hinge_y_vals = tf.maximum(0., 1\. - tf.multiply(target, x_vals))
hinge_y_out = sess.run(hinge_y_vals)
xentropy_y_vals = - tf.multiply(target, tf.log(x_vals)) - tf.multiply((1\. - target), tf.log(1\. - x_vals))
xentropy_y_out = sess.run(xentropy_y_vals)
xentropy_sigmoid_y_vals = tf.nn.sigmoid_cross_entropy_with_logits_v2(logits=x_vals, labels=targets)
xentropy_sigmoid_y_out = sess.run(xentropy_sigmoid_y_vals)
weight = tf.constant(0.5)
xentropy_weighted_y_vals = tf.nn.weighted_cross_entropy_with_logits(logits=x_vals, targets=targets, pos_weight=weight)
xentropy_weighted_y_out = sess.run(xentropy_weighted_y_vals)
unscaled_logits = tf.constant([[1., -3., 10.]])
target_dist = tf.constant([[0.1, 0.02, 0.88]])
softmax_xentropy = tf.nn.softmax_cross_entropy_with_logits_v2(logits=unscaled_logits, labels=target_dist)
print(sess.run(softmax_xentropy))
[ 1.16012561]
unscaled_logits = tf.constant([[1., -3., 10.]])
sparse_target_dist = tf.constant([2])
sparse_xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=unscaled_logits, labels=sparse_target_dist)
print(sess.run(sparse_xentropy))
[ 0.00012564]
以下是如何使用matplotlib
绘制回归损失函数:
x_array = sess.run(x_vals)
plt.plot(x_array, l2_y_out, 'b-', label='L2 Loss')
plt.plot(x_array, l1_y_out, 'r--', label='L1 Loss')
plt.plot(x_array, phuber1_y_out, 'k-.', label='P-Huber Loss (0.25)')
plt.plot(x_array, phuber2_y_out, 'g:', label='P-Huber Loss (5.0)')
plt.ylim(-0.2, 0.4)
plt.legend(loc='lower right', prop={'size': 11})
plt.show()
我们得到以下图作为上述代码的输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ARVkIoCo-1681566813065)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/8475c505-e8c7-49ba-a1d1-b6764a1b45b0.png)]
图 4:绘制各种回归损失函数
以下是如何使用matplotlib
绘制各种分类损失函数:
x_array = sess.run(x_vals)
plt.plot(x_array, hinge_y_out, 'b-''', label='Hinge Loss''')
plt.plot(x_array, xentropy_y_out, 'r--''', label='Cross' Entropy Loss')
plt.plot(x_array, xentropy_sigmoid_y_out, 'k-.''', label='Cross' Entropy Sigmoid Loss')
plt.plot(x_array, xentropy_weighted_y_out, g:''', label='Weighted' Cross Enropy Loss (x0.5)')
plt.ylim(-1.5, 3)
plt.legend(loc='lower right''', prop={'size''': 11})
plt.show()
我们从前面的代码中得到以下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KYAF6ijX-1681566813065)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/ac1fb199-5d83-474a-aad9-efeefb7e4caf.png)]Figure 5: Plots of classification loss functions
这是一个总结我们描述的不同损失函数的表:
损失函数 | 任务 | 优点 | 缺点 |
---|---|---|---|
L2 | 回归 | 更稳定 | 不太强大 |
L1 | 回归 | 更强大 | 不太稳定 |
伪 Huber | 回归 | 更强大,更稳定 | 还有一个参数 |
Hinge | 分类 | 创建 SVM 中使用的最大边距 | 受到异常值影响的无限损失 |
交叉熵 | 分类 | 更稳定 | 无限损失,不那么强大 |
剩余的分类损失函数都与交叉熵损失的类型有关。交叉熵 sigmoid 损失函数用于未缩放的对率,并且优于计算 sigmoid 然后交叉熵,因为 TensorFlow 具有更好的内置方式来处理数字边缘情况。 softmax 交叉熵和稀疏 softmax 交叉熵也是如此。
这里描述的大多数分类损失函数用于两类预测。通过对每个预测/目标上的交叉熵项求和,可以将其扩展到多个类。
评估模型时还需要考虑许多其他指标。以下列出了一些需要考虑的事项:
模型指标 | 描述 |
---|---|
R 平方(确定系数) | 对于线性模型,这是因变量的方差比例,由独立数据解释。对于具有大量特征的模型,请考虑使用调整后的 R 平方。 |
均方根误差 | 对于连续模型,它通过平均平方误差的平方根来测量预测与实际之间的差异。 |
混淆矩阵 | 对于分类模型,我们查看预测类别与实际类别的矩阵。一个完美的模型具有沿对角线的所有计数。 |
召回 | 对于分类模型,这是所有预测阳性的真阳性分数。 |
精确 | 对于分类模型,这是所有实际阳性的真阳性分数。 |
F-得分 | 对于分类模型,这是精度和召回的调和平均值。 |
使用 TensorFlow 的一个好处是它可以跟踪操作并根据反向传播自动更新模型变量。在本文中,我们将介绍如何在训练机器学习模型时将此方面用于我们的优势。
现在,我们将介绍如何以最小化损失函数的方式更改模型中的变量。我们已经学会了如何使用对象和操作,并创建了测量我们的预测和目标之间距离的损失函数。现在,我们只需告诉 TensorFlow 如何通过我们的计算图反向传播误差来更新变量并最小化损失函数。这是通过声明优化函数完成的。一旦我们声明了一个优化函数,TensorFlow 将通过并计算出图中所有计算的反向传播项。当我们输入数据并最小化损失函数时,TensorFlow 将相应地修改图中的变量。
对于这个秘籍,我们将做一个非常简单的回归算法。我们将从正态分布中抽取随机数,均值为 1,标准差为 0.1。然后,我们将通过一个操作来运行数字,这将是它们乘以变量A
。由此,损失函数将是输出和目标之间的 L2 范数,其总是值 10。理论上,A 的最佳值将是数字 10,因为我们的数据将具有平均值 1。
第二个例子是一个非常简单的二分类算法。在这里,我们将从两个正态分布N(-1,1)
和N(3,1)
生成 100 个数字。来自N(-1, 1)
的所有数字将在目标等级 0 中,并且来自N(3, 1)
的所有数字将在目标等级 1 中。用于区分这些数字的模型将是翻译的 Sigmoid 函数。换句话说,模型将是sigmoid(x + A)
,其中A
是我们将适合的变量。从理论上讲,A 将等于 -1。我们得到这个数字是因为如果m1
和m2
是两个正常函数的平均值,那么加到它们以将它们等距离转换为零的值将是 - (m1 + m2) / 2
。我们将在第二个例子中看到 TensorFlow 如何达到该数字。
虽然指定良好的学习率有助于算法的收敛,但我们还必须指定一种优化。从前两个例子中,我们使用标准梯度下降。这是通过GradientDescentOptimizer()
TensorFlow 函数实现的。
以下是回归示例的工作原理:
numpy
和tensorflow
数值 Python 包:import numpy as np
import tensorflow as tf
sess = tf.Session()
A
变量:x_vals = np.random.normal(1, 0.1, 100)
y_vals = np.repeat(10., 100)
x_data = tf.placeholder(shape=[1], dtype=tf.float32)
y_target = tf.placeholder(shape=[1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[1]))
my_output = tf.mul(x_data, A)
Loss
函数:loss = tf.square(my_output - y_target)
my_opt = tf.train.GradientDescentOptimizer(learning_rate=0.02)
train_step = my_opt.minimize(loss)
init = tf.global_variable_initializer()
sess.run(init)
There is a lot of theory on which learning rates are best. This is one of the harder things to figure out in machine learning algorithms. Good papers to read about how learning rates are related to specific optimization algorithms are listed in the See also section at the end of this recipe.
x
和y
条目并通过图提供。 TensorFlow 将自动计算损失,并略微改变A
偏差以最小化损失:for i in range(100):
rand_index = np.random.choice(100)
rand_x = [x_vals[rand_index]]
rand_y = [y_vals[rand_index]]
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
if (i + 1) % 25 == 0:
print('Step #' + str(i+1) + ' A = ' + str(sess.run(A)))
print('Loss = ' + str(sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})))
# Here is the output:
Step #25 A = [ 6.23402166]
Loss = 16.3173
Step #50 A = [ 8.50733757]
Loss = 3.56651
Step #75 A = [ 9.37753201]
Loss = 3.03149
Step #100 A = [ 9.80041122]
Loss = 0.0990248
现在,我们将介绍简单分类示例的代码。如果我们先重置图,我们可以使用相同的 TensorFlow 脚本。请记住,我们将尝试找到一个最佳平移A
,它将两个分布转换为原点,而 sigmoid 函数将两个分为两个不同的类:
from tensorflow.python.framework import ops
ops.reset_default_graph()
sess = tf.Session()
N(-1, 1)
和N(3, 1)
中提取数据。我们还将生成目标标签,数据占位符和偏差变量A
:x_vals = np.concatenate((np.random.normal(-1, 1, 50), np.random.normal(3, 1, 50)))
y_vals = np.concatenate((np.repeat(0., 50), np.repeat(1., 50)))
x_data = tf.placeholder(shape=[1], dtype=tf.float32)
y_target = tf.placeholder(shape=[1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(mean=10, shape=[1]))
我们将
A
初始化为大约 10 的值,远离理论值-1。我们这样做的目的是为了说明算法如何从 10 的值收敛到最佳值 -1。
my_output = tf.add(x_data, A)
expand_dims()
函数为输出添加额外维度。在下一节中,我们将讨论如何在训练中使用可变大小的批次。现在,我们将再次使用一个随机数据点:my_output_expanded = tf.expand_dims(my_output, 0)
y_target_expanded = tf.expand_dims(y_target, 0)
A
:init = tf.initialize_all_variables()
sess.run(init)
nn.sigmoid_cross_entropy_with_logits()
的神经网络包中,TensorFlow 为我们提供了这一函数。如前所述,它希望参数具有特定的维度,因此我们必须相应地使用扩展的输出和目标:xentropy = tf.nn.sigmoid_cross_entropy_with_logits( my_output_expanded, y_target_expanded)
my_opt = tf.train.GradientDescentOptimizer(0.05)
train_step = my_opt.minimize(xentropy)
A
变量。每 200 次迭代,我们将打印出A
的值和损失:for i in range(1400):
rand_index = np.random.choice(100)
rand_x = [x_vals[rand_index]]
rand_y = [y_vals[rand_index]]
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
if (i + 1) % 200 == 0:
print('Step #' + str(i+1) + ' A = ' + str(sess.run(A)))
print('Loss = ' + str(sess.run(xentropy, feed_dict={x_data: rand_x, y_target: rand_y})))
Step #200 A = [ 3.59597969]
Loss = [[ 0.00126199]]
Step #400 A = [ 0.50947344]
Loss = [[ 0.01149425]]
Step #600 A = [-0.50994617]
Loss = [[ 0.14271219]]
Step #800 A = [-0.76606178]
Loss = [[ 0.18807337]]
Step #1000 A = [-0.90859312]
Loss = [[ 0.02346182]]
Step #1200 A = [-0.86169094]
Loss = [[ 0.05427232]]
Step #1400 A = [-1.08486211]
Loss = [[ 0.04099189]]
有关回顾和解释,对于这两个示例,我们执行了以下操作:
A
,但第一个分类算法有一个偏差项来找到数据中的分裂。如前所述,优化算法对学习率的选择很敏感。重要的是要以简洁的方式总结这种选择的效果:
学习率大小 | 优点缺点 | 用途 |
---|---|---|
较小的学习率 | 收敛速度较慢但结果更准确 | 如果解决方案不稳定,请先尝试降低学习率 |
学习率更高 | 不太准确,但收敛速度更快 | 对于某些问题,有助于防止解决方案停滞不前 |
有时,标准梯度下降算法会显着卡住或减速。当优化卡在马鞍的平坦点时,可能会发生这种情况。为了解决这个问题,还有另一种算法考虑了动量项,它增加了前一步骤的梯度下降值的一小部分。 TensorFlow 内置了MomentumOptimizer()
函数。
另一种变体是为我们模型中的每个变量改变优化器步骤。理想情况下,我们希望为较小的移动变量采取较大的步骤,为较快的变化变量采取较短的步骤。我们不会深入研究这种方法的数学,但这种思想的常见实现称为 Adagrad 算法。该算法考虑了变量梯度的整个历史。 TensorFlow 中的函数称为AdagradOptimizer()
。
有时候,Adagrad 会过早地强调梯度为零,因为它考虑了整个历史。解决方法是限制我们使用的步数。这样做称为 Adadelta 算法。我们可以使用AdadeltaOptimizer()
函数来应用它。
还有一些不同的梯度下降算法的其他实现。对于这些,我们会让读者参考 TensorFlow 文档。
有关优化算法和学习率的一些参考,请参阅以下文章和文章:
虽然 TensorFlow 根据反向传播更新我们的模型变量,但它可以同时操作从一个基准观察到一大批数据的任何事物。在一个训练示例上操作可以使得学习过程非常不稳定,而使用太大的批次可能在计算上是昂贵的。选择正确类型的训练对于使我们的机器学习算法融合到解决方案至关重要。
为了使 TensorFlow 计算反向传播的可变梯度,我们必须测量样本或多个样本的损失。随机训练一次只适用于一个随机抽样的数据 - 目标对,就像我们在上一个秘籍中所做的那样。另一种选择是一次放置大部分训练样例并平均梯度计算的损失。训练批次的大小可以一次变化,直到并包括整个数据集。在这里,我们将展示如何将先前的回归示例(使用随机训练)扩展到批量训练。
我们将首先加载numpy
,matplotlib
和tensorflow
,然后启动图会话,如下所示:
import matplotlib as plt
import numpy as np
import tensorflow as tf
sess = tf.Session()
我们按如下方式处理秘籍:
batch_size = 20
None
,第二个维度是批次中的数据点数。我们可以明确地将它设置为 20,但我们可以推广并使用None
值。同样,正如第 1 章,TensorFlow 入门中所述,我们仍然需要确保维度在模型中运行,这不允许我们执行任何非法矩阵操作:x_vals = np.random.normal(1, 0.1, 100)
y_vals = np.repeat(10., 100)
x_data = tf.placeholder(shape=[None, 1], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[1,1]))
matmul()
函数中以正确的顺序输入矩阵:my_output = tf.matmul(x_data, A)
loss
函数会发生变化,因为我们必须采用批次中每个数据点的所有 L2 损失的平均值。我们通过将先前的损失输出包装在 TensorFlow 的reduce_mean()
函数中来实现:loss = tf.reduce_mean(tf.square(my_output - y_target))
my_opt = tf.train.GradientDescentOptimizer(0.02)
train_step = my_opt.minimize(loss)
init = tf.global_variables_initializer()
sess.run(init)
loss_batch = []
for i in range(100):
rand_index = np.random.choice(100, size=batch_size)
rand_x = np.transpose([x_vals[rand_index]])
rand_y = np.transpose([y_vals[rand_index]])
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
if (i + 1) % 5 == 0:
print('Step #' + str(i+1) + ' A = ' + str(sess.run(A)))
temp_loss = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
print('Loss = ' + str(temp_loss))
loss_batch.append(temp_loss)
A
的值有一个额外的维度,因为它现在必须是一个 2D 矩阵:Step #100 A = [[ 9.86720943]]
Loss = 0\.
批量训练和随机训练的优化方法和收敛性不同。找到一个好的批量大小可能很困难。为了了解批量与随机指标之间的收敛程度如何不同,建议读者将批量大小更改为各种级别。以下是保存和记录训练循环中随机损失的代码。只需在上一个秘籍中替换此代码:
loss_stochastic = []
for i in range(100):
rand_index = np.random.choice(100)
rand_x = [x_vals[rand_index]]
rand_y = [y_vals[rand_index]]
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
if (i + 1) % 5 == 0:
print('Step #' + str(i+1) + ' A = ' + str(sess.run(A)))
temp_loss = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
print('Loss = ' + str(temp_loss))
loss_stochastic.append(temp_loss)
下面是为同一回归问题生成随机和批量损失图的代码:
plt.plot(range(0, 100, 5), loss_stochastic, 'b-', label='Stochastic Loss')
plt.plot(range(0, 100, 5), loss_batch, 'r--', label='Batch' Loss, size=20')
plt.legend(loc='upper right', prop={'size': 11})
plt.show()
我们得到以下绘图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GpgNaZqr-1681566813065)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/3d734ed6-f40b-44e6-a890-7324ac20a592.png)]
图 6:在 100 次迭代中绘制的随机损失和批量损失(批量大小为 20)。请注意,批次损失更加平滑,随机损失更加不稳定。
训练类型 | 优点 | 缺点 |
---|---|---|
随机 | 随机性可能有助于摆脱局部的最小值。 | 通常,需要更多迭代才能收敛。 |
批量 | 更快地找到最小值。 | 需要更多资源来计算。 |
在本节中,我们将结合到目前为止所示的所有内容,并为鸢尾数据集创建分类器。
鸢尾数据集在第 1 章,TensorFlow 入门中使用数据源秘籍中有更详细的描述。我们将加载这些数据并制作一个简单的二元分类器来预测花是否是山鸢尾的种类。需要说明的是,这个数据集有三个种类,但我们只能预测一种花是单一种,是否是一种花,给我们一个二元分类器。我们将首先加载库和数据,然后相应地转换目标。
我们按如下方式处理秘籍:
matplotlib
,因为我们想在之后绘制结果行:import matplotlib.pyplot as plt
import numpy as np
from sklearn import datasets
import tensorflow as tf
sess = tf.Session()
x-value
中的第三和第四个条目:iris = datasets.load_iris()
binary_target = np.array([1\. if x==0 else 0\. for x in iris.target])
iris_2d = np.array([[x[2], x[3]] for x in iris.data])
None
作为第一个维度:batch_size = 20
x1_data = tf.placeholder(shape=[None, 1], dtype=tf.float32)
x2_data = tf.placeholder(shape=[None, 1], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[1, 1]))
b = tf.Variable(tf.random_normal(shape=[1, 1]))
请注意,我们可以通过使用
dtype=tf.float32
来减少浮点数的字节来提高算法的表现(速度)。
x2 = x1 * A + b
的形式,如果我们想要找到该行上方或下方的点,我们会在插入等式x2 - x1 * A - b
时看到它们是高于还是低于零。我们将通过取该方程的 sigmoid 并从该方程预测 1 或 0 来实现。请记住,TensorFlow 具有内置 sigmoid 的loss
函数,因此我们只需要在 sigmoid 函数之前定义模型的输出:my_mult = tf.matmul(x2_data, A)
my_add = tf.add(my_mult, b)
my_output = tf.sub(x1_data, my_add)
sigmoid_cross_entropy_with_logits()
函数添加 sigmoid 交叉熵损失函数:xentropy = tf.nn.sigmoid_cross_entropy_with_logits(my_output, y_target)
0.05
作为我们的学习率:my_opt = tf.train.GradientDescentOptimizer(0.05)
train_step = my_opt.minimize(xentropy)
init = tf.global_variables_initializer()
sess.run(init)
for i in range(1000):
rand_index = np.random.choice(len(iris_2d), size=batch_size)
rand_x = iris_2d[rand_index]
rand_x1 = np.array([[x[0]] for x in rand_x])
rand_x2 = np.array([[x[1]] for x in rand_x])
rand_y = np.array([[y] for y in binary_target[rand_index]])
sess.run(train_step, feed_dict={x1_data: rand_x1, x2_data: rand_x2, y_target: rand_y})
if (i + 1) % 200 == 0:
print('Step #' + str(i+1) + ' A = ' + str(sess.run(A)) + ', b = ' + str(sess.run(b)))
Step #200 A = [[ 8.67285347]], b = [[-3.47147632]]
Step #400 A = [[ 10.25393486]], b = [[-4.62928772]]
Step #600 A = [[ 11.152668]], b = [[-5.4077611]]
Step #800 A = [[ 11.81016064]], b = [[-5.96689034]]
Step #1000 A = [[ 12.41202831]], b = [[-6.34769201]]
[[slope]] = sess.run(A)
[[intercept]] = sess.run(b)
x = np.linspace(0, 3, num=50)
ablineValues = []
for i in x:
ablineValues.append(slope*i+intercept)
setosa_x = [a[1] for i,a in enumerate(iris_2d) if binary_target[i]==1]
setosa_y = [a[0] for i,a in enumerate(iris_2d) if binary_target[i]==1]
non_setosa_x = [a[1] for i,a in enumerate(iris_2d) if binary_target[i]==0]
non_setosa_y = [a[0] for i,a in enumerate(iris_2d) if binary_target[i]==0]
plt.plot(setosa_x, setosa_y, 'rx', ms=10, mew=2, label='setosa')
plt.plot(non_setosa_x, non_setosa_y, 'ro', label='Non-setosa')
plt.plot(x, ablineValues, 'b-')
plt.xlim([0.0, 2.7])
plt.ylim([0.0, 7.1])
plt.suptitle('Linear' Separator For I.setosa', fontsize=20)
plt.xlabel('Petal Length')
plt.ylabel('Petal Width')
plt.legend(loc='lower right')
plt.show()
我们的目标是仅使用花瓣宽度和花瓣长度在山鸢尾点和其他两个物种之间拟合一条线。如果我们绘制点和结果线,我们看到我们已经实现了这个:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DEImofrE-1681566813066)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/922f9297-3d22-4eff-a854-a613cead1b35.png)]
图 7:花瓣宽度与花瓣长度的山鸢尾和其它鸢尾的图;实线是我们在 1000 次迭代后实现的线性分离器
虽然我们实现了用一条线分隔两个类的目标,但它可能不是分离两个类的最佳模型。在第 4 章,支持向量机中,我们将讨论支持向量机,它是在特征空间中分离两个类的更好方法。
我们已经学会了如何在 TensorFlow 中训练回归和分类算法。在此之后,我们必须能够评估模型的预测,以确定它的效果。
评估模型非常重要,每个后续模型都将采用某种形式的模型评估。使用 TensorFlow,我们必须将此函数构建到计算图中,并在我们的模型进行训练时和/或完成训练后调用它。
在训练期间评估模型可以让我们深入了解算法,并可以提供调试,改进或完全更改模型的提示。虽然训练期间的评估并不总是必要的,但我们将展示如何使用回归和分类进行评估。
训练结束后,我们需要量化模型对数据的执行方式。理想情况下,我们有一个单独的训练和测试集(甚至是验证集),我们可以在其上评估模型。
当我们想要评估模型时,我们希望在大批数据点上进行评估。如果我们已经实现了批量训练,我们可以重用我们的模型来对这样的批次进行预测。如果我们实现了随机训练,我们可能必须创建一个可以批量处理数据的单独评估器。
如果我们在
loss
函数中包含对模型输出的转换,例如sigmoid_cross_entropy_with_logits()
,我们必须在计算精度计算的预测时考虑到这一点。不要忘记将此包含在您对模型的评估中。
我们要评估的任何模型的另一个重要方面是它是回归还是分类模型。
回归模型试图预测连续数。目标不是类别,而是所需数量。为了评估这些针对实际目标的回归预测,我们需要对两者之间的距离进行综合测量。大多数情况下,有意义的损失函数将满足这些标准。此秘籍向您展示如何将之前的简单回归算法更改为打印出训练循环中的损失并在结束时评估损失。例如,我们将在本章的先前实现反向传播秘籍中重新审视并重写我们的回归示例。
分类模型基于数字输入预测类别。实际目标是 1 和 0 的序列,我们必须衡量我们与预测的真实程度。分类模型的损失函数通常对解释模型的运行情况没有帮助。通常,我们需要某种分类准确率,这通常是正确预测类别的百分比。对于此示例,我们将使用本章中先前实现反向传播秘籍的分类示例。
首先,我们将展示如何评估简单回归模型,该模型简单地适应目标的常数乘法,即 10,如下所示:
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
sess = tf.Session()
x_vals = np.random.normal(1, 0.1, 100)
y_vals = np.repeat(10., 100)
x_data = tf.placeholder(shape=[None, 1], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
batch_size = 25
train_indices = np.random.choice(len(x_vals), round(len(x_vals)*0.8), replace=False)
test_indices = np.array(list(set(range(len(x_vals))) - set(train_indices)))
x_vals_train = x_vals[train_indices]
x_vals_test = x_vals[test_indices]
y_vals_train = y_vals[train_indices]
y_vals_test = y_vals[test_indices]
A = tf.Variable(tf.random_normal(shape=[1,1]))
loss
函数和优化算法。我们还将初始化模型变量A
。使用以下代码:my_output = tf.matmul(x_data, A)
loss = tf.reduce_mean(tf.square(my_output - y_target))
my_opt = tf.train.GradientDescentOptimizer(0.02)
train_step = my_opt.minimize(loss)
init = tf.global_variables_initializer()
sess.run(init)
for i in range(100):
rand_index = np.random.choice(len(x_vals_train), size=batch_size) rand_x = np.transpose([x_vals_train[rand_index]])
rand_y = np.transpose([y_vals_train[rand_index]])
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
if (i + 1) % 25 == 0:
print('Step #' + str(i+1) + ' A = ' + str(sess.run(A)))
print('Loss = ' + str(sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})))
Step #25 A = [[ 6.39879179]]
Loss = 13.7903
Step #50 A = [[ 8.64770794]]
Loss = 2.53685
Step #75 A = [[ 9.40029907]]
Loss = 0.818259
Step #100 A = [[ 9.6809473]]
Loss = 1.10908
mse_test = sess.run(loss, feed_dict={x_data: np.transpose([x_vals_test]), y_target: np.transpose([y_vals_test])})
mse_train = sess.run(loss, feed_dict={x_data: np.transpose([x_vals_train]), y_target: np.transpose([y_vals_train])})
print('MSE' on test:' + str(np.round(mse_test, 2)))
print('MSE' on train:' + str(np.round(mse_train, 2)))
MSE on test:1.35
MSE on train:0.88
对于分类示例,我们将做一些非常相似的事情。这一次,我们需要创建我们自己的精确度函数,我们可以在最后调用。其中一个原因是我们的损失函数内置了 sigmoid,我们需要单独调用 sigmoid 并测试它以查看我们的类是否正确:
from tensorflow.python.framework import ops
ops.reset_default_graph()
sess = tf.Session()
batch_size = 25
x_vals = np.concatenate((np.random.normal(-1, 1, 50), np.random.normal(2, 1, 50)))
y_vals = np.concatenate((np.repeat(0., 50), np.repeat(1., 50)))
x_data = tf.placeholder(shape=[1, None], dtype=tf.float32)
y_target = tf.placeholder(shape=[1, None], dtype=tf.float32)
train_indices = np.random.choice(len(x_vals), round(len(x_vals)*0.8), replace=False)
test_indices = np.array(list(set(range(len(x_vals))) - set(train_indices)))
x_vals_train = x_vals[train_indices]
x_vals_test = x_vals[test_indices]
y_vals_train = y_vals[train_indices]
y_vals_test = y_vals[test_indices]
A = tf.Variable(tf.random_normal(mean=10, shape=[1]))
my_output = tf.add(x_data, A)
init = tf.initialize_all_variables()
sess.run(init)
xentropy = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(my_output, y_target))
my_opt = tf.train.GradientDescentOptimizer(0.05)
train_step = my_opt.minimize(xentropy)
for i in range(1800):
rand_index = np.random.choice(len(x_vals_train), size=batch_size)
rand_x = [x_vals_train[rand_index]]
rand_y = [y_vals_train[rand_index]]
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
if (i+1)%200==0:
print('Step #' + str(i+1) + ' A = ' + str(sess.run(A)))
print('Loss = ' + str(sess.run(xentropy, feed_dict={x_data: rand_x, y_target: rand_y})))
Step #200 A = [ 6.64970636]
Loss = 3.39434
Step #400 A = [ 2.2884655]
Loss = 0.456173
Step #600 A = [ 0.29109824]
Loss = 0.312162
Step #800 A = [-0.20045301]
Loss = 0.241349
Step #1000 A = [-0.33634067]
Loss = 0.376786
Step #1200 A = [-0.36866501]
Loss = 0.271654
Step #1400 A = [-0.3727718]
Loss = 0.294866
Step #1600 A = [-0.39153299]
Loss = 0.202275
Step #1800 A = [-0.36630616]
Loss = 0.358463
float32
并取平均值。这将产生准确率值。我们将为训练集和测试集评估此函数,如下所示:y_prediction = tf.squeeze(tf.round(tf.nn.sigmoid(tf.add(x_data, A))))
correct_prediction = tf.equal(y_prediction, y_target)
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
acc_value_test = sess.run(accuracy, feed_dict={x_data: [x_vals_test], y_target: [y_vals_test]})
acc_value_train = sess.run(accuracy, feed_dict={x_data: [x_vals_train], y_target: [y_vals_train]})
print('Accuracy' on train set: ' + str(acc_value_train))
print('Accuracy' on test set: ' + str(acc_value_test))
Accuracy on train set: 0.925
Accuracy on test set: 0.95
matplotlib
使用两个单独的直方图可视化模型和数据的方法:A_result = sess.run(A)
bins = np.linspace(-5, 5, 50)
plt.hist(x_vals[0:50], bins, alpha=0.5, label='N(-1,1)', color='white')
plt.hist(x_vals[50:100], bins[0:50], alpha=0.5, label='N(2,1)', color='red')
plt.plot((A_result, A_result), (0, 8), 'k--', linewidth=3, label='A = '+ str(np.round(A_result, 2)))
plt.legend(loc='upper right')
plt.title('Binary Classifier, Accuracy=' + str(np.round(acc_value, 2)))
plt.show()
这导致绘图显示两个单独数据类的直方图中两个类的预测最佳分隔符。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xXFTemXm-1681566813066)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/0f077787-e4de-475b-a0c2-960c1e4a73ad.png)]
图 8:数据和最终模型的可视化。两个正常值以 -1 和 2 为中心,使理论最佳分割为 0.5。在这里,模型发现最接近该数字的最佳分割。
在本章中,我们将介绍涉及线性回归的秘籍。我们从用矩阵求解线性回归的数学公式开始,然后继续使用 TensorFlow 范例实现标准线性回归和变量。我们将涵盖以下领域:
线性回归可能是统计学,机器学习和一般科学中最重要的算法之一。它是最广泛使用的算法之一,了解如何实现它及其各种风格非常重要。线性回归优于许多其他算法的优点之一是它是非常可解释的。我们最终得到一个数字,用于直接表示该特征如何影响目标或因变量的每个特征。在本章中,我们将介绍线性回归是如何经典实现的,然后继续讨论如何在 TensorFlow 范例中最好地实现它。
在这个秘籍中,我们将使用 TensorFlow 用矩阵逆方法求解二维线性回归。
线性回归可以表示为一组矩阵方程,比如Ax = b
。在这里,我们感兴趣的是求解矩阵x
中的系数。如果我们的观察矩阵(设计矩阵)A
不是正方形,我们必须要小心。解决x
的解决方案可以表示为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wTU8ZoPN-1681566813066)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/cef9855e-d719-434e-ac00-fa8ab4b351db.png)]
为了证明确实如此,我们将生成二维数据,在 TensorFlow 中解决它,并绘制结果。
我们按如下方式处理秘籍:
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
sess = tf.Session()
x_vals = np.linspace(0, 10, 100)
y_vals = x_vals + np.random.normal(0, 1, 100)
A
矩阵,它将是x
数据列和全 1 的列。然后,我们从y
数据创建b
矩阵。使用以下代码:x_vals_column = np.transpose(np.matrix(x_vals))
ones_column = np.transpose(np.matrix(np.repeat(1, 100)))
A = np.column_stack((x_vals_column, ones_column))
b = np.transpose(np.matrix(y_vals))
A
和b
矩阵转换为张量,如下所示:A_tensor = tf.constant(A)
b_tensor = tf.constant(b)
tA_A = tf.matmul(tf.transpose(A_tensor), A_tensor)
tA_A_inv = tf.matrix_inverse(tA_A)
product = tf.matmul(tA_A_inv, tf.transpose(A_tensor))
solution = tf.matmul(product, b_tensor)
solution_eval = sess.run(solution)
slope = solution_eval[0][0]
y_intercept = solution_eval[1][0]
print('slope: ' + str(slope))
print('y_intercept: ' + str(y_intercept))
slope: 0.955707151739
y_intercept: 0.174366829314
best_fit = []
for i in x_vals:
best_fit.append(slope*i+y_intercept)
plt.plot(x_vals, y_vals, 'o', label='Data')
plt.plot(x_vals, best_fit, 'r-', label='Best fit line', linewidth=3)
plt.legend(loc='upper left')
plt.show()
我们得到前面代码的图,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3P04Sb0B-1681566813066)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/eaa2fd52-cdc8-42d9-a43c-c18a9dd445a5.png)]
图 1:数据点和通过矩阵逆方法获得的最佳拟合线
与之前的秘籍或本书中的大多数秘籍不同,此处的解决方案仅通过矩阵运算找到。我们将使用的大多数 TensorFlow 算法都是通过训练循环实现的,并利用自动反向传播来更新模型变量。在这里,我们通过实现将模型拟合到数据的直接解决方案来说明 TensorFlow 的多功能性。
我们在这里使用了一个二维数据示例来显示与数据拟合的图。值得注意的是,用于求解系数的公式 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wYgUiXzw-1681566813067)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/32e2728d-bb84-491b-a4e1-e28108f5fff1.png)] 将根据需要扩展到数据中的许多特征(除非存在任何共线性问题)。
对于这个秘籍,我们将实现一个用于线性回归的矩阵分解方法。具体来说,我们将使用 Cholesky 分解,TensorFlow 中存在相关函数。
在大多数情况下,实现前一个秘籍中的逆方法在数值上效率低,尤其是当矩阵变得非常大时。另一种方法是分解A
矩阵并对分解执行矩阵运算。一种方法是在 TensorFlow 中使用内置的 Cholesky 分解方法。
人们对将矩阵分解为更多矩阵如此感兴趣的一个原因是,所得到的矩阵将具有允许我们有效使用某些方法的保证属性。 Cholesky 分解将矩阵分解为下三角矩阵和上三角矩阵,比如L
和L'
,使得这些矩阵是彼此的转置。有关此分解属性的更多信息,有许多可用资源来描述它以及如何到达它。在这里,我们将通过将其写为LL'x = b
来解决系统Ax = b
。我们首先解决Ly = b
的y
,然后求解L'x = y
得到我们的系数矩阵x
。
我们按如下方式处理秘籍:
A
矩阵和b
矩阵:import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from tensorflow.python.framework import ops
ops.reset_default_graph()
sess = tf.Session()
x_vals = np.linspace(0, 10, 100)
y_vals = x_vals + np.random.normal(0, 1, 100)
x_vals_column = np.transpose(np.matrix(x_vals))
ones_column = np.transpose(np.matrix(np.repeat(1, 100)))
A = np.column_stack((x_vals_column, ones_column))
b = np.transpose(np.matrix(y_vals))
A_tensor = tf.constant(A)
b_tensor = tf.constant(b)
A^T A
:tA_A = tf.matmul(tf.transpose(A_tensor), A_tensor)
L = tf.cholesky(tA_A)
tA_b = tf.matmul(tf.transpose(A_tensor), b)
sol1 = tf.matrix_solve(L, tA_b)
sol2 = tf.matrix_solve(tf.transpose(L), sol1)
请注意,TensorFlow 函数
cholesky()
仅返回分解的下对角线部分。这很好,因为上对角矩阵只是下对角矩阵的转置。
solution_eval = sess.run(sol2)
slope = solution_eval[0][0]
y_intercept = solution_eval[1][0]
print('slope: ' + str(slope))
print('y_intercept: ' + str(y_intercept))
slope: 0.956117676145
y_intercept: 0.136575513864
best_fit = []
for i in x_vals:
best_fit.append(slope*i+y_intercept)
plt.plot(x_vals, y_vals, 'o', label='Data')
plt.plot(x_vals, best_fit, 'r-', label='Best fit line', linewidth=3)
plt.legend(loc='upper left')
plt.show()
绘图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NpZoY504-1681566813067)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/57a190f8-110c-4cd2-902f-3668dc603f65.png)]
图 2:通过 Cholesky 分解获得的数据点和最佳拟合线
如您所见,我们得出了与之前秘籍非常相似的答案。请记住,这种分解矩阵然后对碎片执行操作的方式有时会更加高效和数值稳定,尤其是在考虑大型数据矩阵时。
虽然使用矩阵和分解方法非常强大,但 TensorFlow 还有另一种解决斜率和截距的方法。 TensorFlow 可以迭代地执行此操作,逐步学习最小化损失的线性回归参数。
在这个秘籍中,我们将遍历批量数据点并让 TensorFlow 更新斜率和y
截距。我们将使用内置于 scikit-learn 库中的鸢尾花数据集,而不是生成的数据。具体来说,我们将通过数据点找到最佳线,其中x
值是花瓣宽度,y
值是萼片长度。我们选择了这两个,因为它们之间似乎存在线性关系,我们将在最后的绘图中看到。我们还将在下一节中详细讨论不同损失函数的影响,但对于这个秘籍,我们将使用 L2 损失函数。
我们按如下方式处理秘籍:
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from sklearn import datasets
from tensorflow.python.framework import ops
ops.reset_default_graph()
sess = tf.Session()
iris = datasets.load_iris()
x_vals = np.array([x[3] for x in iris.data])
y_vals = np.array([y[0] for y in iris.data])
learning_rate = 0.05
batch_size = 25
x_data = tf.placeholder(shape=[None, 1], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[1,1]))
b = tf.Variable(tf.random_normal(shape=[1,1]))
y = Ax + b
:model_output = tf.add(tf.matmul(x_data, A), b)
0.05
作为我们的学习率:loss = tf.reduce_mean(tf.square(y_target - model_output))
init = tf.global_variables_initializer()
sess.run(init)
my_opt = tf.train.GradientDescentOptimizer(learning_rate)
train_step = my_opt.minimize(loss)
loss_vec = []
for i in range(100):
rand_index = np.random.choice(len(x_vals), size=batch_size)
rand_x = np.transpose([x_vals[rand_index]])
rand_y = np.transpose([y_vals[rand_index]])
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
temp_loss = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
loss_vec.append(temp_loss)
if (i+1)%25==0:
print('Step #' + str(i+1) + ' A = ' + str(sess.run(A)) + ' b = ' + str(sess.run(b)))
print('Loss = ' + str(temp_loss))
Step #25 A = [[ 2.17270374]] b = [[ 2.85338426]]
Loss = 1.08116
Step #50 A = [[ 1.70683455]] b = [[ 3.59916329]]
Loss = 0.796941
Step #75 A = [[ 1.32762754]] b = [[ 4.08189011]]
Loss = 0.466912
Step #100 A = [[ 1.15968263]] b = [[ 4.38497639]]
Loss = 0.281003
[slope] = sess.run(A)
[y_intercept] = sess.run(b)
best_fit = []
for i in x_vals:
best_fit.append(slope*i+y_intercept)
plt.plot(x_vals, y_vals, 'o', label='Data Points')
plt.plot(x_vals, best_fit, 'r-', label='Best fit line', linewidth=3)
plt.legend(loc='upper left')
plt.title('Sepal Length vs Petal Width')
plt.xlabel('Petal Width')
plt.ylabel('Sepal Length')
plt.show()
plt.plot(loss_vec, 'k-')
plt.title('L2 Loss per Generation')
plt.xlabel('Generation')
plt.ylabel('L2 Loss')
plt.show()
此代码生成以下拟合数据和损失图。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-18aoJlI7-1681566813067)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/e3f807db-f6b3-4808-8b2e-990ed0c444b8.png)]
图 3:来自鸢尾数据集的数据点(萼片长度与花瓣宽度)重叠在 TensorFlow 中找到的最佳线条拟合。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2oU3jpIn-1681566813067)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/3d3d7762-7528-4101-bd3b-8175f11e0c83.png)]
图 4:用我们的算法拟合数据的 L2 损失;注意损失函数中的抖动,可以通过较大的批量大小减小抖动,或者通过较小的批量大小来增加。
Here is a good place to note how to see whether the model is overfitting or underfitting the data. If our data is broken into test and training sets, and the accuracy is greater on the training set and lower on the test set, then we are overfitting the data. If the accuracy is still increasing on both test and training sets, then the model is underfitting and we should continue training.
找到的最佳线不保证是最合适的线。最佳拟合线的收敛取决于迭代次数,批量大小,学习率和损失函数。随着时间的推移观察损失函数总是很好的做法,因为它可以帮助您解决问题或超参数变化。
了解损失函数在算法收敛中的作用非常重要。在这里,我们将说明 L1 和 L2 损失函数如何影响线性回归中的收敛。
我们将使用与先前秘籍中相同的鸢尾数据集,但我们将更改损失函数和学习率以查看收敛如何变化。
我们按如下方式处理秘籍:
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from sklearn import datasets
sess = tf.Session()
iris = datasets.load_iris()
x_vals = np.array([x[3] for x in iris.data])
y_vals = np.array([y[0] for y in iris.data])
batch_size = 25
learning_rate = 0.1 # Will not converge with learning rate at 0.4
iterations = 50
x_data = tf.placeholder(shape=[None, 1], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[1,1]))
b = tf.Variable(tf.random_normal(shape=[1,1]))
model_output = tf.add(tf.matmul(x_data, A), b)
loss_l1
),如下所示:loss_l1 = tf.reduce_mean(tf.abs(y_target - model_output))
init = tf.global_variables_initializer()
sess.run(init)
my_opt_l1 = tf.train.GradientDescentOptimizer(learning_rate)
train_step_l1 = my_opt_l1.minimize(loss_l1)
loss_vec_l1 = []
for i in range(iterations):
rand_index = np.random.choice(len(x_vals), size=batch_size)
rand_x = np.transpose([x_vals[rand_index]])
rand_y = np.transpose([y_vals[rand_index]])
sess.run(train_step_l1, feed_dict={x_data: rand_x, y_target: rand_y})
temp_loss_l1 = sess.run(loss_l1, feed_dict={x_data: rand_x, y_target: rand_y})
loss_vec_l1.append(temp_loss_l1)
if (i+1)%25==0:
print('Step #' + str(i+1) + ' A = ' + str(sess.run(A)) + ' b = ' + str(sess.run(b)))
plt.plot(loss_vec_l1, 'k-', label='L1 Loss')
plt.plot(loss_vec_l2, 'r--', label='L2 Loss')
plt.title('L1 and L2 Loss per Generation')
plt.xlabel('Generation')
plt.ylabel('L1 Loss')
plt.legend(loc='upper right')
plt.show()
在选择损失函数时,我们还必须选择适合我们问题的相应学习率。在这里,我们将说明两种情况,一种是首选 L2,另一种是首选 L1。
如果我们的学习率很小,我们的收敛会花费更多时间。但是如果我们的学习速度太大,我们的算法就会遇到问题从不收敛。下面是当学习率为 0.05 时,鸢尾线性回归问题的 L1 和 L2 损失的损失函数图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P0bdRUn6-1681566813068)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/bf44e506-bc88-41c2-b566-ddf2fe68bbd6.png)]
图 5:鸢尾线性回归问题的学习率为 0.05 的 L1 和 L2 损失
学习率为 0.05 时,似乎 L2 损失是首选,因为它会收敛到较低的损失。下面是我们将学习率提高到 0.4 时的损失函数图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LbgISEgU-1681566813068)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/729d53ef-d7e2-4068-8ae7-2fc6e91c572b.png)]
图 6:鸢尾线性回归问题的 L1 和 L2 损失,学习率为 0.4;请注意,由于 y 轴的高比例,L1 损失不可见
在这里,我们可以看到高学习率可以在 L2 范数中超调,而 L1 范数收敛。
为了理解正在发生的事情,我们应该看看大学习率和小学习率如何影响 L1 范数和 L2 范数。为了使这个可视化,我们查看两个规范的学习步骤的一维表示,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TxezJnvf-1681566813068)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/f15420de-a851-4e7f-8b87-434e1eba5e2d.png)]
图 7:学习率越来越高的 L1 和 L2 范数会发生什么
在这个秘籍中,我们将实现戴明回归,这意味着我们需要一种不同的方法来测量模型线和数据点之间的距离。
戴明回归有几个名字。它也称为总回归,正交距离回归(ODR)和最短距离回归。
如果最小二乘线性回归最小化到线的垂直距离,则戴明回归最小化到线的总距离。这种类型的回归可以最小化y
和x
值的误差。
请参阅下图进行比较:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s8G0k7Zm-1681566813068)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/7b0c20a1-43c3-4a07-9785-2f7a5706ad6b.png)]
图 8:常规线性回归和戴明回归之间的差异;左边的线性回归最小化了到线的垂直距离,右边的变形回归最小化了到线的总距离
要实现戴明回归,我们必须修改损失函数。常规线性回归中的损失函数使垂直距离最小化。在这里,我们希望最小化总距离。给定线的斜率和截距,到点的垂直距离是已知的几何公式。我们只需要替换此公式并告诉 TensorFlow 将其最小化。
我们按如下方式处理秘籍:
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from sklearn import datasets
sess = tf.Session()
iris = datasets.load_iris()
x_vals = np.array([x[3] for x in iris.data])
y_vals = np.array([y[0] for y in iris.data])
batch_size = 50
x_data = tf.placeholder(shape=[None, 1], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[1,1]))
b = tf.Variable(tf.random_normal(shape=[1,1]))
model_output = tf.add(tf.matmul(x_data, A), b)
y = mx + b
和一个点(x0, y0)
,两者之间的垂直距离可以写成如下:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lGXMMZqu-1681566813069)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/9537f564-ab2b-4b6a-936e-2432e21e2859.png)]
deming_numerator = tf.abs(tf.sub(y_target, tf.add(tf.matmul(x_data, A), b)))
deming_denominator = tf.sqrt(tf.add(tf.square(A),1))
loss = tf.reduce_mean(tf.truediv(deming_numerator, deming_denominator))
init = tf.global_variables_initializer()
sess.run(init)
my_opt = tf.train.GradientDescentOptimizer(0.1)
train_step = my_opt.minimize(loss)
loss_vec = []
for i in range(250):
rand_index = np.random.choice(len(x_vals), size=batch_size)
rand_x = np.transpose([x_vals[rand_index]])
rand_y = np.transpose([y_vals[rand_index]])
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
temp_loss = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
loss_vec.append(temp_loss)
if (i+1)%50==0:
print('Step #' + str(i+1) + ' A = ' + str(sess.run(A)) + ' b = ' + str(sess.run(b)))
print('Loss = ' + str(temp_loss))
[slope] = sess.run(A)
[y_intercept] = sess.run(b)
best_fit = []
for i in x_vals:
best_fit.append(slope*i+y_intercept)
plt.plot(x_vals, y_vals, 'o', label='Data Points')
plt.plot(x_vals, best_fit, 'r-', label='Best fit line', linewidth=3)
plt.legend(loc='upper left')
plt.title('Sepal Length vs petal Width')
plt.xlabel('petal Width')
plt.ylabel('Sepal Length')
plt.show()
我们得到上面代码的以下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XGPoGCFc-1681566813069)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/e53cec07-65ac-4af7-8a26-f9d5a1c27642.png)]
图 9:对鸢尾数据集进行戴明回归的解决方案
戴明回归的方法几乎与常规线性回归相同。关键的区别在于我们如何衡量预测和数据点之间的损失。而不是垂直损失,我们对y
和x
值有垂直损失(或总损失)。
当我们假设
x
和y
值中的误差相似时,使用这种类型的回归。根据我们的假设,我们还可以根据误差的差异在距离计算中缩放x
和y
轴。
还有一些方法可以限制系数对回归输出的影响。这些方法称为正则化方法,两种最常见的正则化方法是套索和岭回归。我们将介绍如何在本文中实现这两个方面。
套索和岭回归与常规线性回归非常相似,除了我们添加正则化项以限制公式中的斜率(或部分斜率)。这可能有多种原因,但一个常见的原因是我们希望限制对因变量产生影响的特征。这可以通过在损失函数中添加一个取决于我们的斜率值A
的项来实现。
对于套索回归,如果斜率A
超过某个值,我们必须添加一个能大大增加损失函数的项。我们可以使用 TensorFlow 的逻辑运算,但它们没有与之关联的梯度。相反,我们将使用称为连续重阶函数的阶梯函数的连续近似,该函数按比例放大到我们选择的正则化截止值。我们将展示如何在此秘籍中进行套索回归。
对于岭回归,我们只是在 L2 范数中添加一个项,这是斜率系数的缩放 L2 范数。这种修改很简单,并在本秘籍末尾的“更多”部分中显示。
我们按如下方式处理秘籍:
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from sklearn import datasets
from tensorflow.python.framework import ops
ops.reset_default_graph()
sess = tf.Session()
iris = datasets.load_iris()
x_vals = np.array([x[3] for x in iris.data])
y_vals = np.array([y[0] for y in iris.data])
batch_size = 50
learning_rate = 0.001
x_data = tf.placeholder(shape=[None, 1], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[1,1]))
b = tf.Variable(tf.random_normal(shape=[1,1]))
model_output = tf.add(tf.matmul(x_data, A), b)
0.9
设定了套索回归的截止值。这意味着我们希望将斜率系数限制为小于0.9
。使用以下代码:lasso_param = tf.constant(0.9)
heavyside_step = tf.truediv(1., tf.add(1., tf.exp(tf.multiply(-100., tf.subtract(A, lasso_param)))))
regularization_param = tf.mul(heavyside_step, 99.)
loss = tf.add(tf.reduce_mean(tf.square(y_target - model_output)), regularization_param)
init = tf.global_variables_initializer()
sess.run(init)
my_opt = tf.train.GradientDescentOptimizer(learning_rate)
train_step = my_opt.minimize(loss)
0.9
。使用以下代码:loss_vec = []
for i in range(1500):
rand_index = np.random.choice(len(x_vals), size=batch_size)
rand_x = np.transpose([x_vals[rand_index]])
rand_y = np.transpose([y_vals[rand_index]])
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
temp_loss = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
loss_vec.append(temp_loss[0])
if (i+1)%300==0:
print('Step #' + str(i+1) + ' A = ' + str(sess.run(A)) + ' b = ' + str(sess.run(b)))
print('Loss = ' + str(temp_loss))
Step #300 A = [[ 0.82512331]] b = [[ 2.30319238]]
Loss = [[ 6.84168959]]
Step #600 A = [[ 0.8200165]] b = [[ 3.45292258]]
Loss = [[ 2.02759886]]
Step #900 A = [[ 0.81428504]] b = [[ 4.08901262]]
Loss = [[ 0.49081498]]
Step #1200 A = [[ 0.80919558]] b = [[ 4.43668795]]
Loss = [[ 0.40478843]]
Step #1500 A = [[ 0.80433637]] b = [[ 4.6360755]]
Loss = [[ 0.23839757]]
我们通过在线性回归的损失函数中添加连续的 Heaviside 阶跃函数来实现套索回归。由于阶梯函数的陡峭性,我们必须小心步长。步长太大而且不会收敛。对于岭回归,请参阅下一节中所需的更改。
对于岭回归,我们将损失ss
函数更改为如下:
ridge_param = tf.constant(1.)
ridge_loss = tf.reduce_mean(tf.square(A))
loss = tf.expand_dims(tf.add(tf.reduce_mean(tf.square(y_target - model_output)), tf.multiply(ridge_param, ridge_loss)), 0)
弹性网络回归是一种回归类型,通过将 L1 和 L2 正则化项添加到损失函数,将套索回归与岭回归相结合。
在前两个秘籍之后实现弹性网络回归应该是直截了当的,因此我们将在鸢尾数据集上的多元线性回归中实现这一点,而不是像以前那样坚持二维数据。我们将使用花瓣长度,花瓣宽度和萼片宽度来预测萼片长度。
我们按如下方式处理秘籍:
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from sklearn import datasets
sess = tf.Session()
x
数据的每个元素将是三个值的列表而不是一个。使用以下代码:iris = datasets.load_iris()
x_vals = np.array([[x[1], x[2], x[3]] for x in iris.data])
y_vals = np.array([y[0] for y in iris.data])
x
数据占位符的大小规范,取三个值而不是一个,如下所示:batch_size = 50
learning_rate = 0.001
x_data = tf.placeholder(shape=[None, 3], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[3,1]))
b = tf.Variable(tf.random_normal(shape=[1,1]))
model_output = tf.add(tf.matmul(x_data, A), b)
elastic_param1 = tf.constant(1.)
elastic_param2 = tf.constant(1.)
l1_a_loss = tf.reduce_mean(tf.abs(A))
l2_a_loss = tf.reduce_mean(tf.square(A))
e1_term = tf.multiply(elastic_param1, l1_a_loss)
e2_term = tf.multiply(elastic_param2, l2_a_loss)
loss = tf.expand_dims(tf.add(tf.add(tf.reduce_mean(tf.square(y_target - model_output)), e1_term), e2_term), 0)
init = tf.global_variables_initializer()
sess.run(init)
my_opt = tf.train.GradientDescentOptimizer(learning_rate)
train_step = my_opt.minimize(loss)
loss_vec = []
for i in range(1000):
rand_index = np.random.choice(len(x_vals), size=batch_size)
rand_x = x_vals[rand_index]
rand_y = np.transpose([y_vals[rand_index]])
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
temp_loss = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
loss_vec.append(temp_loss[0])
if (i+1)%250==0:
print('Step #' + str(i+1) + ' A = ' + str(sess.run(A)) + ' b = ' + str(sess.run(b)))
print('Loss = ' + str(temp_loss))
Step #250 A = [[ 0.42095602]
[ 0.1055888 ]
[ 1.77064979]] b = [[ 1.76164341]]
Loss = [ 2.87764359]
Step #500 A = [[ 0.62762028]
[ 0.06065864]
[ 1.36294949]] b = [[ 1.87629771]]
Loss = [ 1.8032167]
Step #750 A = [[ 0.67953539]
[ 0.102514 ]
[ 1.06914485]] b = [[ 1.95604002]]
Loss = [ 1.33256555]
Step #1000 A = [[ 0.6777274 ]
[ 0.16535147]
[ 0.8403284 ]] b = [[ 2.02246833]]
Loss = [ 1.21458709]
plt.plot(loss_vec, 'k-')
plt.title('Loss per Generation')
plt.xlabel('Generation')
plt.ylabel('Loss')
plt.show()
我们得到上面代码的以下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W5AMZU9L-1681566813069)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/12a7c632-d523-401b-b9bd-769b8b765f67.png)]
图 10:在 1,000 次训练迭代中绘制的弹性净回归损失
这里实现弹性网络回归以及多元线性回归。我们可以看到,利用损失函数中的这些正则化项,收敛速度比先前的秘籍慢。正则化就像在损失函数中添加适当的项一样简单。
对于这个秘籍,我们将实现逻辑回归来预测样本人群中低出生体重的概率。
逻辑回归是将线性回归转换为二元分类的一种方法。这是通过将线性输出转换为 Sigmoid 函数来实现的,该函数将输出在 0 和 1 之间进行缩放。目标是零或一,表示数据点是在一个类还是另一个类中。由于我们预测 0 和 1 之间的数字,如果预测高于指定的截止值,则预测被分类为类值 1,否则分类为 0。出于此示例的目的,我们将指定截断为 0.5,这将使分类像舍入输出一样简单。
我们将用于此示例的数据将是从作者的 GitHub 仓库获得的低出生体重数据。我们将从其他几个因素预测低出生体重。
我们按如下方式处理秘籍:
request
库,因为我们将通过超链接访问低出生体重数据。我们还发起了一个会议:import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import requests
from sklearn import datasets
from sklearn.preprocessing import normalize
from tensorflow.python.framework import ops
ops.reset_default_graph()
sess = tf.Session()
birth_weight_file = 'birth_weight.csv'
# Download data and create data file if file does not exist in current directory
if not os.path.exists(birth_weight_file):
birthdata_url = 'https://github.com/nfmcclure/tensorflow_cookbook/raw/master/01_Introduction/07_Working_with_Data_Sources/birthweight_data/birthweight.dat'
birth_file = requests.get(birthdata_url)
birth_data = birth_file.text.split('\r\n')
birth_header = birth_data[0].split('\t')
birth_data = [[float(x) for x in y.split('\t') if len(x)>=1] for y in birth_data[1:] if len(y)>=1]
with open(birth_weight_file, 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(birth_header)
writer.writerows(birth_data)
# Read birth weight data into memory
birth_data = []
with open(birth_weight_file, newline='') as csvfile:
csv_reader = csv.reader(csvfile)
birth_header = next(csv_reader)
for row in csv_reader:
birth_data.append(row)
birth_data = [[float(x) for x in row] for row in birth_data]
# Pull out target variable
y_vals = np.array([x[0] for x in birth_data])
# Pull out predictor variables (not id, not target, and not birthweight)
x_vals = np.array([x[1:8] for x in birth_data])
train_indices = np.random.choice(len(x_vals), round(len(x_vals)*0.8), replace=False)
test_indices = np.array(list(set(range(len(x_vals))) - set(train_indices)))
x_vals_train = x_vals[train_indices]
x_vals_test = x_vals[test_indices]
y_vals_train = y_vals[train_indices]
y_vals_test = y_vals[test_indices]
def normalize_cols(m, col_min=np.array([None]), col_max=np.array([None])):
if not col_min[0]:
col_min = m.min(axis=0)
if not col_max[0]:
col_max = m.max(axis=0)
return (m-col_min) / (col_max - col_min), col_min, col_max
x_vals_train, train_min, train_max = np.nan_to_num(normalize_cols(x_vals_train))
x_vals_test = np.nan_to_num(normalize_cols(x_vals_test, train_min, train_max))
请注意,在缩放数据集之前,我们将数据集拆分为训练和测试。这是一个重要的区别。我们希望确保测试集完全不影响训练集。如果我们在分裂之前缩放整个集合,那么我们不能保证它们不会相互影响。我们确保从训练组中保存缩放以缩放测试集。
x_data
占位符的大小为[None, 7]
。batch_size = 25
x_data = tf.placeholder(shape=[None, 7], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[7,1]))
b = tf.Variable(tf.random_normal(shape=[1,1]))
model_output = tf.add(tf.matmul(x_data, A), b)
loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(model_output, y_target))
init = tf.global_variables_initializer()
sess.run(init)
my_opt = tf.train.GradientDescentOptimizer(0.01)
train_step = my_opt.minimize(loss)
prediction = tf.round(tf.sigmoid(model_output))
predictions_correct = tf.cast(tf.equal(prediction, y_target), tf.float32)
accuracy = tf.reduce_mean(predictions_correct)
loss_vec = []
train_acc = []
test_acc = []
for i in range(1500):
rand_index = np.random.choice(len(x_vals_train), size=batch_size)
rand_x = x_vals_train[rand_index]
rand_y = np.transpose([y_vals_train[rand_index]])
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
temp_loss = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
loss_vec.append(temp_loss)
temp_acc_train = sess.run(accuracy, feed_dict={x_data: x_vals_train, y_target: np.transpose([y_vals_train])})
train_acc.append(temp_acc_train)
temp_acc_test = sess.run(accuracy, feed_dict={x_data: x_vals_test, y_target: np.transpose([y_vals_test])})
test_acc.append(temp_acc_test)
plt.plot(loss_vec, 'k-')
plt.title('Cross' Entropy Loss per Generation')
plt.xlabel('Generation')
plt.ylabel('Cross' Entropy Loss')
plt.show()
plt.plot(train_acc, 'k-', label='Train Set Accuracy')
plt.plot(test_acc, 'r--', label='Test Set Accuracy')
plt.title('Train and Test Accuracy')
plt.xlabel('Generation')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.show()
这是迭代和训练和测试精度的损失。由于数据集仅为 189 次观测,因此随着数据集的随机分裂,训练和测试精度图将发生变化。第一个数字是交叉熵损失:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RvTYHVDR-1681566813069)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/0efa3298-7046-432d-8393-ca1765b44175.png)]
图 11:在 1,500 次迭代过程中绘制的交叉熵损失
第二个图显示了训练和测试装置的准确率:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KO0IYaLB-1681566813070)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/a6f50907-4950-402e-b327-053a02deb6b3.png)]Figure 12: Test and train set accuracy plotted over 1,500 generations
本章将介绍有关如何在 TensorFlow 中使用,实现和评估支持向量机(SVM)的一些重要秘籍。将涵盖以下领域:
本章中先前涵盖的逻辑回归和大多数 SVM 都是二元预测变量。虽然逻辑回归试图找到最大化距离的任何分离线(概率地),但 SVM 还尝试最小化误差,同时最大化类之间的余量。通常,如果问题与训练示例相比具有大量特征,请尝试逻辑回归或线性 SVM。如果训练样本的数量较大,或者数据不是线性可分的,则可以使用具有高斯核的 SVM。
另外,请记住本章的所有代码都可以在 Github 和 Packt 仓库中找到。
SVM 是二分类的方法。基本思想是在两个类之间找到二维的线性分离线(或更多维度的超平面)。我们首先假设二元类目标是 -1 或 1,而不是先前的 0 或 1 目标。由于可能有许多行分隔两个类,我们定义最佳线性分隔符,以最大化两个类之间的距离:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VwoJbYof-1681566813070)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/bc479121-6689-4836-9221-2f694919ce64.png)]
图 1
给定两个可分类o
和x
,我们希望找到两者之间的线性分离器的等式。左侧绘图显示有许多行将两个类分开。右侧绘图显示了唯一的最大边际线。边距宽度由2 / ||A||
给出。通过最小化A
的 L2 范数找到该线。
我们可以编写如下超平面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4FLfWZZT-1681566813070)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/d459df75-35ba-46f7-b3a0-2e6d934d5636.png)]
这里,A
是我们部分斜率的向量,x
是输入向量。最大边距的宽度可以显示为 2 除以A
的 L2 范数。这个事实有许多证明,但是对于几何思想,求解从 2D 点到直线的垂直距离可以提供前进的动力。
对于线性可分的二元类数据,为了最大化余量,我们最小化A
,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-injRxuHI-1681566813070)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/c8bd7a37-6085-4843-8d01-34b9877ffe39.png)]的 L2 范数。我们还必须将此最小值置于以下约束条件下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3DNdvXCF-1681566813070)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/35825b20-6e93-4136-acb4-f51128ed4c7f.png)]
前面的约束确保我们来自相应类的所有点都在分离线的同一侧。
由于并非所有数据集都是线性可分的,因此我们可以为跨越边界线的点引入损失函数。对于n
数据点,我们引入了所谓的软边际损失函数,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MPSfKPMp-1681566813071)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/068234be-29b7-4890-96ef-afd771e361f6.png)]
请注意,如果该点位于边距的正确一侧,则乘积y[i](Ax[i] - b)
始终大于 1。这使得损失函数的左手项等于 0,并且对损失函数的唯一影响是余量的大小。
前面的损失函数将寻找线性可分的线,但允许穿过边缘线的点。根据α
的值,这可以是硬度或软度量。α
的较大值导致更加强调边距的扩大,而α
的较小值导致模型更像是一个硬边缘,同时允许数据点跨越边距,如果需要的话。
在本章中,我们将建立一个软边界 SVM,并展示如何将其扩展到非线性情况和多个类。
对于此示例,我们将从鸢尾花数据集创建线性分隔符。我们从前面的章节中知道,萼片长度和花瓣宽度创建了一个线性可分的二分类数据集,用于预测花是否是山鸢尾(I)。
要在 TensorFlow 中实现软可分 SVM,我们将实现特定的损失函数,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zOGiOLH9-1681566813071)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/11e1a288-7294-4c7c-bbee-37e854bbf309.png)]
这里,A
是部分斜率的向量,b
是截距,x[i]
是输入向量,y[i]
是实际类,(-1 或 1),α
是软可分性正则化参数。
我们按如下方式处理秘籍:
scikit-learn
数据集库。使用以下代码:import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from sklearn import datasets
要为此练习设置 scikit-learn,我们只需要输入
$pip install -U scikit-learn
。请注意,它也安装了 Anaconda。
sess = tf.Session()
iris = datasets.load_iris()
x_vals = np.array([[x[0], x[3]] for x in iris.data])
y_vals = np.array([1 if y==0 else -1 for y in iris.target])
train_indices = np.random.choice(len(x_vals), round(len(x_vals)*0.8), replace=False)
test_indices = np.array(list(set(range(len(x_vals))) - set(train_indices)))
x_vals_train = x_vals[train_indices]
x_vals_test = x_vals[test_indices]
y_vals_train = y_vals[train_indices]
y_vals_test = y_vals[test_indices]
A
变量将采用2x1
形状,因为我们有两个预测变量:萼片长度和花瓣宽度。要进行此设置,我们使用以下代码:batch_size = 100
x_data = tf.placeholder(shape=[None, 2], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[2,1]))
b = tf.Variable(tf.random_normal(shape=[1,1]))
model_output = tf.subtract(tf.matmul(x_data, A), b)
l2_norm = tf.reduce_sum(tf.square(A))
alpha = tf.constant([0.1])
classification_term = tf.reduce_mean(tf.maximum(0., tf.subtract(1., tf.multiply(model_output, y_target))))
loss = tf.add(classification _term, tf.multiply(alpha, l2_norm))
prediction = tf.sign(model_output)
accuracy = tf.reduce_mean(tf.cast(tf.equal(prediction, y_target), tf.float32))
my_opt = tf.train.GradientDescentOptimizer(0.01)
train_step = my_opt.minimize(loss)
init = tf.global_variables_initializer()
sess.run(init)
loss_vec = []
train_accuracy = []
test_accuracy = []
for i in range(500):
rand_index = np.random.choice(len(x_vals_train), size=batch_size)
rand_x = x_vals_train[rand_index]
rand_y = np.transpose([y_vals_train[rand_index]])
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
temp_loss = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
loss_vec.append(temp_loss)
train_acc_temp = sess.run(accuracy, feed_dict={x_data: x_vals_train, y_target: np.transpose([y_vals_train])})
train_accuracy.append(train_acc_temp)
test_acc_temp = sess.run(accuracy, feed_dict={x_data: x_vals_test, y_target: np.transpose([y_vals_test])})
test_accuracy.append(test_acc_temp)
if (i+1)%100==0:
print('Step #' + str(i+1) + ' A = ' + str(sess.run(A)) + ' b = ' + str(sess.run(b)))
print('Loss = ' + str(temp_loss))
Step #100 A = [[-0.10763293]
[-0.65735245]] b = [[-0.68752676]]
Loss = [ 0.48756418]
Step #200 A = [[-0.0650763 ]
[-0.89443302]] b = [[-0.73912662]]
Loss = [ 0.38910741]
Step #300 A = [[-0.02090022]
[-1.12334013]] b = [[-0.79332656]]
Loss = [ 0.28621092]
Step #400 A = [[ 0.03189624]
[-1.34912157]] b = [[-0.8507266]]
Loss = [ 0.22397576]
Step #500 A = [[ 0.05958777]
[-1.55989814]] b = [[-0.9000265]]
Loss = [ 0.20492229]
x
值分成山鸢尾和其它鸢尾,如下所示:[[a1], [a2]] = sess.run(A)
[[b]] = sess.run(b)
slope = -a2/a1
y_intercept = b/a1
x1_vals = [d[1] for d in x_vals]
best_fit = []
for i in x1_vals:
best_fit.append(slope*i+y_intercept)
setosa_x = [d[1] for i,d in enumerate(x_vals) if y_vals[i]==1]
setosa_y = [d[0] for i,d in enumerate(x_vals) if y_vals[i]==1]
not_setosa_x = [d[1] for i,d in enumerate(x_vals) if y_vals[i]==-1]
not_setosa_y = [d[0] for i,d in enumerate(x_vals) if y_vals[i]==-1]
plt.plot(setosa_x, setosa_y, 'o', label='I. setosa')
plt.plot(not_setosa_x, not_setosa_y, 'x', label='Non-setosa')
plt.plot(x1_vals, best_fit, 'r-', label='Linear Separator', linewidth=3)
plt.ylim([0, 10])
plt.legend(loc='lower right')
plt.title('Sepal Length vs Petal Width')
plt.xlabel('Petal Width')
plt.ylabel('Sepal Length')
plt.show()
plt.plot(train_accuracy, 'k-', label='Training Accuracy')
plt.plot(test_accuracy, 'r--', label='Test Accuracy')
plt.title('Train and Test Set Accuracies')
plt.xlabel('Generation')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.show()
plt.plot(loss_vec, 'k-')
plt.title('Loss per Generation')
plt.xlabel('Generation')
plt.ylabel('Loss')
plt.show()
以这种方式使用 TensorFlow 来实现 SVD 算法可能导致每次运行的结果略有不同。其原因包括随机训练/测试集拆分以及每个训练批次中不同批次点的选择。此外,在每一代之后慢慢降低学习率是理想的。
得到的图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NlwfUGcR-1681566813071)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/9c53587e-f2d7-4e1f-9af9-1bde936d24da.png)]
图 2:最终线性 SVM 与绘制的两个类别拟合
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jW6NO9wQ-1681566813071)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/fb331bce-9d8e-4d8e-928e-8446022dac2c.png)]
图 3:迭代测试和训练集精度;我们确实获得 100% 的准确率,因为这两个类是线性可分的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vmpx8h0a-1681566813072)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/adb5aaf5-d575-4e93-a84c-7f709afcdcb6.png)]
图 4:超过 500 次迭代的最大边际损失图
在本文中,我们已经证明使用最大边际损失函数可以实现线性 SVD 模型。
SVM 可用于拟合线性回归。在本节中,我们将探讨如何使用 TensorFlow 执行此操作。
可以将相同的最大边际概念应用于拟合线性回归。我们可以考虑最大化包含最多(x
,y
)点的边距,而不是最大化分隔类的边距。为了说明这一点,我们将使用相同的鸢尾数据集,并表明我们可以使用此概念来拟合萼片长度和花瓣宽度之间的线。
相应的损失函数类似于:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1MiHrztw-1681566813072)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/fa49eabe-ad86-4314-8e13-ed19e7e20b27.png)]
这里,ε
是边距宽度的一半,如果一个点位于该区域,则损失等于 0。
我们按如下方式处理秘籍:
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from sklearn import datasets
sess = tf.Session()
iris = datasets.load_iris()
x_vals = np.array([x[3] for x in iris.data])
y_vals = np.array([y[0] for y in iris.data])
train_indices = np.random.choice(len(x_vals), round(len(x_vals)*0.8), replace=False)
test_indices = np.array(list(set(range(len(x_vals))) - set(train_indices)))
x_vals_train = x_vals[train_indices]
x_vals_test = x_vals[test_indices]
y_vals_train = y_vals[train_indices]
y_vals_test = y_vals[test_indices]
对于此示例,我们将数据拆分为训练集和测试集。将数据拆分为三个数据集也很常见,其中包括验证集。我们可以使用此验证集来验证我们在训练它们时不会过拟合模型。
batch_size = 50
x_data = tf.placeholder(shape=[None, 1], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[1,1]))
b = tf.Variable(tf.random_normal(shape=[1,1]))
model_output = tf.add(tf.matmul(x_data, A), b)
ε = 0.5
。请记住,epsilon 是我们的损失函数的一部分,它允许软边距而不是硬边距:epsilon = tf.constant([0.5])
loss = tf.reduce_mean(tf.maximum(0., tf.subtract(tf.abs(tf.subtract(model_output, y_target)), epsilon)))
my_opt = tf.train.GradientDescentOptimizer(0.075)
train_step = my_opt.minimize(loss)
init = tf.global_variables_initializer()
sess.run(init)
train_loss = []
test_loss = []
for i in range(200):
rand_index = np.random.choice(len(x_vals_train), size=batch_size)
rand_x = np.transpose([x_vals_train[rand_index]])
rand_y = np.transpose([y_vals_train[rand_index]])
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
temp_train_loss = sess.run(loss, feed_dict={x_data: np.transpose([x_vals_train]), y_target: np.transpose([y_vals_train])})
train_loss.append(temp_train_loss)
temp_test_loss = sess.run(loss, feed_dict={x_data: np.transpose([x_vals_test]), y_target: np.transpose([y_vals_test])})
test_loss.append(temp_test_loss)
if (i+1)%50==0:
print('-----------')
print('Generation: ' + str(i))
print('A = ' + str(sess.run(A)) + ' b = ' + str(sess.run(b)))
print('Train Loss = ' + str(temp_train_loss))
print('Test Loss = ' + str(temp_test_loss))
Generation: 50
A = [[ 2.20651722]] b = [[ 2.71290684]]
Train Loss = 0.609453
Test Loss = 0.460152
-----------
Generation: 100
A = [[ 1.6440177]] b = [[ 3.75240564]]
Train Loss = 0.242519
Test Loss = 0.208901
-----------
Generation: 150
A = [[ 1.27711761]] b = [[ 4.3149066]]
Train Loss = 0.108192
Test Loss = 0.119284
-----------
Generation: 200
A = [[ 1.05271816]] b = [[ 4.53690529]]
Train Loss = 0.0799957
Test Loss = 0.107551
[[slope]] = sess.run(A)
[[y_intercept]] = sess.run(b)
[width] = sess.run(epsilon)
best_fit = []
best_fit_upper = []
best_fit_lower = []
for i in x_vals:
best_fit.append(slope*i+y_intercept)
best_fit_upper.append(slope*i+y_intercept+width)
best_fit_lower.append(slope*i+y_intercept-width)
plt.plot(x_vals, y_vals, 'o', label='Data Points')
plt.plot(x_vals, best_fit, 'r-', label='SVM Regression Line', linewidth=3)
plt.plot(x_vals, best_fit_upper, 'r--', linewidth=2)
plt.plot(x_vals, best_fit_lower, 'r--', linewidth=2)
plt.ylim([0, 10])
plt.legend(loc='lower right')
plt.title('Sepal Length vs Petal Width')
plt.xlabel('Petal Width')
plt.ylabel('Sepal Length')
plt.show()
plt.plot(train_loss, 'k-', label='Train Set Loss')
plt.plot(test_loss, 'r--', label='Test Set Loss')
plt.title('L2 Loss per Generation')
plt.xlabel('Generation')
plt.ylabel('L2 Loss')
plt.legend(loc='upper right')
plt.show()
上述代码的图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DTa4ONFf-1681566813072)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/9e656a1b-1414-49d5-a4e8-e2e2d1907737.png)]
图 5:鸢尾数据上有 0.5 个边缘的 SVM 回归(萼片长度与花瓣宽度)
以下是训练迭代中的训练和测试损失:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zZmKiUpM-1681566813072)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/281870d6-9835-4fdd-8db0-8e861aa474d5.png)]
图 6:训练和测试集上每代的 SVM 回归损失
直觉上,我们可以将 SVM 回归看作是一个函数,试图尽可能多地在2ε
宽度范围内拟合点。该线的拟合对该参数有些敏感。如果我们选择太小的ε
,算法将无法适应边距中的许多点。如果我们选择太大的ε
,将会有许多行能够适应边距中的所有数据点。我们更喜欢较小的ε
,因为距离边缘较近的点比较远的点贡献较少的损失。
先前的 SVM 使用线性可分数据。如果我们分离非线性数据,我们可以改变将线性分隔符投影到数据上的方式。这是通过更改 SVM 损失函数中的核来完成的。在本章中,我们将介绍如何更改核并分离非线性可分离数据。
在本文中,我们将激励支持向量机中核的使用。在线性 SVM 部分,我们用特定的损失函数求解了软边界。这种方法的另一种方法是解决所谓的优化问题的对偶。可以证明线性 SVM 问题的对偶性由以下公式给出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YzOkTYPz-1681566813073)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/a311abb6-b9c9-4aef-9cac-88438abb5879.png)]
对此,以下适用:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ov1LE5R7-1681566813073)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/6b8e7a37-0f7a-40e2-9135-838f6ea38141.png)]
这里,模型中的变量将是b
向量。理想情况下,此向量将非常稀疏,仅对我们数据集的相应支持向量采用接近 1 和 -1 的值。我们的数据点向量由x[i]
表示,我们的目标(1 或 -1)y[i]
表示。
前面等式中的核是点积x[i] · y[j]
,它给出了线性核。该核是一个方形矩阵,填充了数据点i, j
的点积。
我们可以将更复杂的函数扩展到更高的维度,而不是仅仅在数据点之间进行点积,而在这些维度中,类可以是线性可分的。这似乎是不必要的复杂,但我们可以选择一个具有以下属性的函数k
:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lsIdQNbd-1681566813073)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/eb618f1d-e0d3-49a7-bbec-c8e5ab9049d4.png)]
这里, k
被称为核函数。更常见的核是使用高斯核(也称为径向基函数核或 RBF 核)。该核用以下等式描述:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GwTLCKE9-1681566813073)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/91bc4d05-6bc2-4b0d-85ac-70a964eae981.png)]
为了对这个核进行预测,比如说p[i]
,我们只需在核中的相应方程中用预测点替换,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-arMgVsrJ-1681566813073)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/284cc0b5-9a95-4a09-bf23-39776df87409.png)]
在本节中,我们将讨论如何实现高斯核。我们还将在适当的位置记下在何处替换实现线性核。我们将使用的数据集将手动创建,以显示高斯核更适合在线性核上使用的位置。
我们按如下方式处理秘籍:
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from sklearn import datasets
sess = tf.Session()
x
和y
值以用于绘图目的。为此,请使用以下代码:(x_vals, y_vals) = datasets.make_circles(n_samples=500, factor=.5, noise=.1)
y_vals = np.array([1 if y==1 else -1 for y in y_vals])
class1_x = [x[0] for i,x in enumerate(x_vals) if y_vals[i]==1]
class1_y = [x[1] for i,x in enumerate(x_vals) if y_vals[i]==1]
class2_x = [x[0] for i,x in enumerate(x_vals) if y_vals[i]==-1]
class2_y = [x[1] for i,x in enumerate(x_vals) if y_vals[i]==-1]
b
。对于 SVM,我们倾向于需要更大的批量大小,因为我们需要一个非常稳定的模型,该模型在每次训练生成时都不会波动很大。另请注意,我们为预测点添加了额外的占位符。为了可视化结果,我们将创建一个颜色网格,以查看哪些区域最后属于哪个类。我们这样做如下:batch_size = 250
x_data = tf.placeholder(shape=[None, 2], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
prediction_grid = tf.placeholder(shape=[None, 2], dtype=tf.float32)
b = tf.Variable(tf.random_normal(shape=[1,batch_size]))
gamma = tf.constant(-50.0)
dist = tf.reduce_sum(tf.square(x_data), 1)
dist = tf.reshape(dist, [-1,1])
sq_dists = tf.add(tf.subtract(dist, tf.multiply(2., tf.matmul(x_data, tf.transpose(x_data)))), tf.transpose(dist))
my_kernel = tf.exp(tf.multiply(gamma, tf.abs(sq_dists)))
注意
add
和subtract
操作的sq_dists
行中广播的使用。 另外,请注意线性核可以表示为my_kernel = tf.matmul(x_data, tf.transpose(x_data))
。
tf.negative()
函数最小化损失函数的负值,而不是最大化。我们使用以下代码完成此任务:model_output = tf.matmul(b, my_kernel)
first_term = tf.reduce_sum(b)
b_vec_cross = tf.matmul(tf.transpose(b), b)
y_target_cross = tf.matmul(y_target, tf.transpose(y_target))
second_term = tf.reduce_sum(tf.multiply(my_kernel, tf.multiply(b_vec_cross, y_target_cross)))
loss = tf.negative(tf.subtract(first_term, second_term))
rA = tf.reshape(tf.reduce_sum(tf.square(x_data), 1),[-1,1])
rB = tf.reshape(tf.reduce_sum(tf.square(prediction_grid), 1),[-1,1])
pred_sq_dist = tf.add(tf.subtract(rA, tf.multiply(2., tf.matmul(x_data, tf.transpose(prediction_grid)))), tf.transpose(rB))
pred_kernel = tf.exp(tf.multiply(gamma, tf.abs(pred_sq_dist)))
prediction_output = tf.matmul(tf.multiply(tf.transpose(y_target),b), pred_kernel)
prediction = tf.sign(prediction_output-tf.reduce_mean(prediction_output))
accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.squeeze(prediction), tf.squeeze(y_target)), tf.float32))
为了实现线性预测核,我们可以编写
pred_kernel = tf.matmul(x_data, tf.transpose(prediction_grid))
。
my_opt = tf.train.GradientDescentOptimizer(0.001)
train_step = my_opt.minimize(loss)
init = tf.global_variables_initializer()
sess.run(init)
x
数据两次以获得对点的预测,如下所示:loss_vec = []
batch_accuracy = []
for i in range(500):
rand_index = np.random.choice(len(x_vals), size=batch_size)
rand_x = x_vals[rand_index]
rand_y = np.transpose([y_vals[rand_index]])
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
temp_loss = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
loss_vec.append(temp_loss)
acc_temp = sess.run(accuracy, feed_dict={x_data: rand_x,
y_target: rand_y,
prediction_grid:rand_x})
batch_accuracy.append(acc_temp)
if (i+1)%100==0:
print('Step #' + str(i+1))
print('Loss = ' + str(temp_loss))
Step #100
Loss = -28.0772
Step #200
Loss = -3.3628
Step #300
Loss = -58.862
Step #400
Loss = -75.1121
Step #500
Loss = -84.8905
x_min, x_max = x_vals[:, 0].min() - 1, x_vals[:, 0].max() + 1
y_min, y_max = x_vals[:, 1].min() - 1, x_vals[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
np.arange(y_min, y_max, 0.02))
grid_points = np.c_[xx.ravel(), yy.ravel()]
[grid_predictions] = sess.run(prediction, feed_dict={x_data: x_vals,
y_target: np.transpose([y_vals]),
prediction_grid: grid_points})
grid_predictions = grid_predictions.reshape(xx.shape)
plt.contourf(xx, yy, grid_predictions, cmap=plt.cm.Paired, alpha=0.8)
plt.plot(class1_x, class1_y, 'ro', label='Class 1')
plt.plot(class2_x, class2_y, 'kx', label='Class -1')
plt.legend(loc='lower right')
plt.ylim([-1.5, 1.5])
plt.xlim([-1.5, 1.5])
plt.show()
plt.plot(batch_accuracy, 'k-', label='Accuracy')
plt.title('Batch Accuracy')
plt.xlabel('Generation')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.show()
plt.plot(loss_vec, 'k-')
plt.title('Loss per Generation')
plt.xlabel('Generation')
plt.ylabel('Loss')
plt.show()
为了简洁起见,我们将仅显示结果图,但我们也可以单独运行绘图代码并查看损失和准确率。
以下屏幕截图说明了线性可分离拟合对我们的非线性数据有多糟糕:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bSuZeo0Y-1681566813074)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/3dc4f147-d736-4b8d-a4af-e004a6f306fc.png)]
图 7:非线性可分离数据上的线性 SVM
以下屏幕截图显示了高斯核可以更好地拟合非线性数据:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uNeJIL8p-1681566813074)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/942b3cec-7ca0-42d5-bc2b-7fddb0a4d4e4.png)]Figure 8: Non-linear SVM with Gaussian kernel results on non-linear ring data
如果我们使用高斯核来分离我们的非线性环数据,我们会得到更好的拟合。
有两个重要的代码需要了解:我们如何实现核,以及我们如何为 SVM 双优化问题实现损失函数。我们已经展示了如何实现线性和高斯核,并且高斯核可以分离非线性数据集。
我们还应该提到另一个参数,即高斯核中的伽马值。此参数控制影响点对分离曲率的影响程度。通常选择小值,但它在很大程度上取决于数据集。理想情况下,使用交叉验证等统计技术选择此参数。
对于新点的预测/评估,我们使用以下命令:
sess.run(prediction, feed_dict:{x_data: x_vals, y_data: np.transpose([y_vals])})
。此评估必须包括原始数据集(x_vals
和y_vals
),因为 SVM 是使用支持向量定义的,由哪些点指定在边界上或不是。
如果我们这样选择,我们可以实现更多核。以下是一些更常见的非线性核列表:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-90714whP-1681566813074)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/3233ad1f-7336-40e8-b9de-895eb041bb5b.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T1cbNmcz-1681566813074)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/bdb676cb-da55-47f7-a656-2a0406ff4ab3.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zGkuLQ51-1681566813075)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/5d9c1c9e-e1ce-4497-9fc4-5f7be14ce1b3.png)]
对于此秘籍,我们将应用非线性核来拆分数据集。
在本节中,我们将在实际数据上实现前面的高斯核 SVM。我们将加载鸢尾数据集并为山鸢尾创建分类器(与其它鸢尾相比)。我们将看到各种伽马值对分类的影响。
我们按如下方式处理秘籍:
scikit-learn
数据集,以便我们可以加载鸢尾数据。然后,我们将启动图会话。使用以下代码:import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from sklearn import datasets
sess = tf.Session()
x
和y
值(以便以后绘图),如下所示:iris = datasets.load_iris()
x_vals = np.array([[x[0], x[3]] for x in iris.data])
y_vals = np.array([1 if y==0 else -1 for y in iris.target])
class1_x = [x[0] for i,x in enumerate(x_vals) if y_vals[i]==1]
class1_y = [x[1] for i,x in enumerate(x_vals) if y_vals[i]==1]
class2_x = [x[0] for i,x in enumerate(x_vals) if y_vals[i]==-1]
class2_y = [x[1] for i,x in enumerate(x_vals) if y_vals[i]==-1]
b
,如下所示:batch_size = 100
x_data = tf.placeholder(shape=[None, 2], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
prediction_grid = tf.placeholder(shape=[None, 2], dtype=tf.float32)
b = tf.Variable(tf.random_normal(shape=[1,batch_size]))
gamma = tf.constant(-10.0)
dist = tf.reduce_sum(tf.square(x_data), 1)
dist = tf.reshape(dist, [-1,1])
sq_dists = tf.add(tf.subtract(dist, tf.multiply(2., tf.matmul(x_data, tf.transpose(x_data)))), tf.transpose(dist))
my_kernel = tf.exp(tf.multiply(gamma, tf.abs(sq_dists)))
# We now compute the loss for the dual optimization problem, as follows:
model_output = tf.matmul(b, my_kernel)
first_term = tf.reduce_sum(b)
b_vec_cross = tf.matmul(tf.transpose(b), b)
y_target_cross = tf.matmul(y_target, tf.transpose(y_target))
second_term = tf.reduce_sum(tf.multiply(my_kernel, tf.multiply(b_vec_cross, y_target_cross)))
loss = tf.negative(tf.subtract(first_term, second_term))
rA = tf.reshape(tf.reduce_sum(tf.square(x_data), 1),[-1,1])
rB = tf.reshape(tf.reduce_sum(tf.square(prediction_grid), 1),[-1,1])
pred_sq_dist = tf.add(tf.subtract(rA, tf.mul(2., tf.matmul(x_data, tf.transpose(prediction_grid)))), tf.transpose(rB))
pred_kernel = tf.exp(tf.multiply(gamma, tf.abs(pred_sq_dist)))
prediction_output = tf.matmul(tf.multiply(tf.transpose(y_target),b), pred_kernel)
prediction = tf.sign(prediction_output-tf.reduce_mean(prediction_output))
accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.squeeze(prediction), tf.squeeze(y_target)), tf.float32))
my_opt = tf.train.GradientDescentOptimizer(0.01)
train_step = my_opt.minimize(loss)
init = tf.initialize_all_variables()
sess.run(init)
loss_vec = []
batch_accuracy = []
for i in range(300):
rand_index = np.random.choice(len(x_vals), size=batch_size)
rand_x = x_vals[rand_index]
rand_y = np.transpose([y_vals[rand_index]])
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
temp_loss = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
loss_vec.append(temp_loss)
acc_temp = sess.run(accuracy, feed_dict={x_data: rand_x,
y_target: rand_y,
prediction_grid:rand_x})
batch_accuracy.append(acc_temp)
x
,y
点的网格并评估我们在所有这些点上创建的预测函数,如下所示:x_min, x_max = x_vals[:, 0].min() - 1, x_vals[:, 0].max() + 1
y_min, y_max = x_vals[:, 1].min() - 1, x_vals[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
np.arange(y_min, y_max, 0.02))
grid_points = np.c_[xx.ravel(), yy.ravel()]
[grid_predictions] = sess.run(prediction, feed_dict={x_data: x_vals,
y_target: np.transpose([y_vals]),
prediction_grid: grid_points})
grid_predictions = grid_predictions.reshape(xx.shape)
plt.contourf(xx, yy, grid_predictions, cmap=plt.cm.Paired, alpha=0.8)
plt.plot(class1_x, class1_y, 'ro', label='I. setosa')
plt.plot(class2_x, class2_y, 'kx', label='Non-setosa')
plt.title('Gaussian SVM Results on Iris Data')
plt.xlabel('Petal Length')
plt.ylabel('Sepal Width')
plt.legend(loc='lower right')
plt.ylim([-0.5, 3.0])
plt.xlim([3.5, 8.5])
plt.show()
以下是对四种不同伽玛值(1,10,25 和 100)的山鸢尾结果的分类。注意伽玛值越高,每个单独点对分类边界的影响越大:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vv0jEvNY-1681566813075)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/21a6bf2b-18fd-407c-9854-bca30ac02a84.png)]
图 9:使用具有四个不同伽马值的高斯核 SVM 的山鸢尾的分类结果
我们还可以使用 SVM 对多个类进行分类,而不仅仅是两个类。在本文中,我们将使用多类 SVM 对鸢尾数据集中的三种类型的花进行分类。
通过设计,SVM 算法是二元分类器。但是,有一些策略可以让他们在多个类上工作。两种主要策略称为“一对一”,“一对剩余”。
一对一是一种策略,其中为每个可能的类对创建二分类器。然后,对具有最多投票的类的点进行预测。这可能在计算上很难,因为我们必须为k
类创建k!/(k - 2)!2!
个分类器。
实现多类分类器的另一种方法是执行一对一策略,我们为k
类的每个类创建一个分类器。点的预测类将是创建最大 SVM 边距的类。这是我们将在本节中实现的策略。
在这里,我们将加载鸢尾数据集并使用高斯核执行多类非线性 SVM。鸢尾数据集是理想的,因为有三个类(山鸢尾,弗吉尼亚和杂色鸢尾)。我们将为每个类创建三个高斯核 SVM,并预测存在最高边界的点。
我们按如下方式处理秘籍:
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from sklearn import datasets
sess = tf.Session()
x
和y
值分开,以便最后进行绘图。使用以下代码:iris = datasets.load_iris()
x_vals = np.array([[x[0], x[3]] for x in iris.data])
y_vals1 = np.array([1 if y==0 else -1 for y in iris.target])
y_vals2 = np.array([1 if y==1 else -1 for y in iris.target])
y_vals3 = np.array([1 if y==2 else -1 for y in iris.target])
y_vals = np.array([y_vals1, y_vals2, y_vals3])
class1_x = [x[0] for i,x in enumerate(x_vals) if iris.target[i]==0]
class1_y = [x[1] for i,x in enumerate(x_vals) if iris.target[i]==0]
class2_x = [x[0] for i,x in enumerate(x_vals) if iris.target[i]==1]
class2_y = [x[1] for i,x in enumerate(x_vals) if iris.target[i]==1]
class3_x = [x[0] for i,x in enumerate(x_vals) if iris.target[i]==2]
class3_y = [x[1] for i,x in enumerate(x_vals) if iris.target[i]==2]
y_target
占位符现在具有[3, None]
的大小,我们的模型变量b
将被初始化为[3, batch_size]
。使用以下代码:batch_size = 50
x_data = tf.placeholder(shape=[None, 2], dtype=tf.float32)
y_target = tf.placeholder(shape=[3, None], dtype=tf.float32)
prediction_grid = tf.placeholder(shape=[None, 2], dtype=tf.float32)
b = tf.Variable(tf.random_normal(shape=[3,batch_size]))
gamma = tf.constant(-10.0)
dist = tf.reduce_sum(tf.square(x_data), 1)
dist = tf.reshape(dist, [-1,1])
sq_dists = tf.add(tf.subtract(dist, tf.multiply(2., tf.matmul(x_data, tf.transpose(x_data)))), tf.transpose(dist))
my_kernel = tf.exp(tf.multiply(gamma, tf.abs(sq_dists)))
x^T · x
等操作跨越额外维度,我们创建一个函数来扩展这样的矩阵,将矩阵重新整形为转置,然后在额外维度上调用 TensorFlow 的batch_matmul
。使用以下代码:def reshape_matmul(mat):
v1 = tf.expand_dims(mat, 1)
v2 = tf.reshape(v1, [3, batch_size, 1])
return tf.batch_matmul(v2, v1)
model_output = tf.matmul(b, my_kernel)
first_term = tf.reduce_sum(b)
b_vec_cross = tf.matmul(tf.transpose(b), b)
y_target_cross = reshape_matmul(y_target)
second_term = tf.reduce_sum(tf.multiply(my_kernel, tf.multiply(b_vec_cross, y_target_cross)),[1,2])
loss = tf.reduce_sum(tf.negative(tf.subtract(first_term, second_term)))
reduce_sum
函数并且不要在所有三个 SVM 预测中减少,因此我们必须告诉 TensorFlow 不要用第二个索引参数对所有内容求和。使用以下代码:rA = tf.reshape(tf.reduce_sum(tf.square(x_data), 1),[-1,1])
rB = tf.reshape(tf.reduce_sum(tf.square(prediction_grid), 1),[-1,1])
pred_sq_dist = tf.add(tf.subtract(rA, tf.multiply(2., tf.matmul(x_data, tf.transpose(prediction_grid)))), tf.transpose(rB))
pred_kernel = tf.exp(tf.multiply(gamma, tf.abs(pred_sq_dist)))
sign()
。由于我们正在实现一对一策略,因此预测是具有最大输出的分类器。为此,我们使用 TensorFlow 的内置argmax()
函数,如下所示:prediction_output = tf.matmul(tf.mul(y_target,b), pred_kernel)
prediction = tf.arg_max(prediction_output-tf.expand_dims(tf.reduce_mean(prediction_output,1), 1), 0)
accuracy = tf.reduce_mean(tf.cast(tf.equal(prediction, tf.argmax(y_target,0)), tf.float32))
my_opt = tf.train.GradientDescentOptimizer(0.01)
train_step = my_opt.minimize(loss)
init = tf.global_variables_initializer()
sess.run(init)
loss_vec = []
batch_accuracy = []
for i in range(100):
rand_index = np.random.choice(len(x_vals), size=batch_size)
rand_x = x_vals[rand_index]
rand_y = y_vals[:,rand_index]
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
temp_loss = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
loss_vec.append(temp_loss)
acc_temp = sess.run(accuracy, feed_dict={x_data: rand_x, y_target: rand_y, prediction_grid:rand_x})
batch_accuracy.append(acc_temp)
if (i+1)%25==0:
print('Step #' + str(i+1))
print('Loss = ' + str(temp_loss))
Step #25
Loss = -2.8951
Step #50
Loss = -27.9612
Step #75
Loss = -26.896
Step #100
Loss = -30.2325
x_min, x_max = x_vals[:, 0].min() - 1, x_vals[:, 0].max() + 1
y_min, y_max = x_vals[:, 1].min() - 1, x_vals[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
np.arange(y_min, y_max, 0.02))
grid_points = np.c_[xx.ravel(), yy.ravel()]
grid_predictions = sess.run(prediction, feed_dict={x_data: rand_x,
y_target: rand_y,
prediction_grid: grid_points})
grid_predictions = grid_predictions.reshape(xx.shape)
plt.contourf(xx, yy, grid_predictions, cmap=plt.cm.Paired, alpha=0.8)
plt.plot(class1_x, class1_y, 'ro', label='I. setosa')
plt.plot(class2_x, class2_y, 'kx', label='I. versicolor')
plt.plot(class3_x, class3_y, 'gv', label='I. virginica')
plt.title('Gaussian SVM Results on Iris Data')
plt.xlabel('Petal Length')
plt.ylabel('Sepal Width')
plt.legend(loc='lower right')
plt.ylim([-0.5, 3.0])
plt.xlim([3.5, 8.5])
plt.show()
plt.plot(batch_accuracy, 'k-', label='Accuracy')
plt.title('Batch Accuracy')
plt.xlabel('Generation')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.show()
plt.plot(loss_vec, 'k-')
plt.title('Loss per Generation')
plt.xlabel('Generation')
plt.ylabel('Loss')
plt.show()
然后我们得到以下绘图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uBuuoduZ-1681566813075)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/42907158-e660-4a92-a2da-897722139ec5.png)]
图 10:在鸢尾数据集上的伽马为 10 的多类(三类)非线性高斯 SVM 的结果
我们观察前面的屏幕截图,其中显示了所有三个鸢尾类,以及为每个类分类的点网格。
本文中需要注意的重点是我们如何改变算法以同时优化三个 SVM 模型。我们的模型参数b
有一个额外的维度可以考虑所有三个模型。在这里,我们可以看到,由于 TensorFlow 处理额外维度的内置函数,算法扩展到多个类似算法相对容易。
下一章将介绍最近邻方法,这是一种用于预测目的的非常强大的算法。
本章将重点介绍最近邻方法,以及如何在 TensorFlow 中实现它们。我们将首先介绍这些方法,然后我们将说明如何实现各种形式。本章将以地址匹配和图像识别的示例结束。
在本章中,我们将介绍以下内容:
最近邻方法植根于基于距离的概念思想。我们认为我们的训练设定了一个模型,并根据它们与训练集中的点的接近程度对新点进行预测。一种简单的方法是使预测类与最接近的训练数据点类相同。但由于大多数数据集包含一定程度的噪声,因此更常见的方法是采用一组k-
最近邻的加权平均值。该方法称为 K 最近邻(KNN)。
给定具有相应目标(y[1], y[2]....y[n]
)的训练数据集(x[1],x[2].....x[n]
),我们可以通过查看一组最近邻来对点z
进行预测。实际的预测方法取决于我们是进行回归(连续y[i]
)还是分类(离散y[i]
)。
对于离散分类目标,可以通过最大投票方案给出预测,通过到预测点的距离加权:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Vm01fEg-1681566813075)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/f6815331-7ba6-4be4-9719-eeeca8f4dd94.png)]
我们这里的预测f(z)
是所有类别j
的最大加权值,其中从预测点到训练点的加权距离i
由φ(d[ij])
给出。如果点i
在类j.
中,l[ij]
只是一个指示器函数如果点i
在类j
中,则指示器函数取值 1,如果不是,则取值 0 另外,k
是要考虑的最近点数。
对于连续回归目标,预测由最接近预测的所有k
点的加权平均值给出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h0tiKWAJ-1681566813076)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/7bd24242-3c82-47ee-b114-7aeb5922e317.png)]
很明显,预测很大程度上取决于距离度量的选择d
。
距离度量的常用规范是 L1 和 L2 距离,如下所示:
我们可以选择许多不同规格的距离指标。在本章中,我们将探讨 L1 和 L2 指标,以及编辑和文本距离。
我们还必须选择如何加权距离。对距离进行加权的直接方法是距离本身。远离我们预测的点应该比较近点的影响小。最常见的权重方法是通过距离的归一化逆。我们将在下一个秘籍中实现此方法。
注意,KNN 是一种聚合方法。对于回归,我们正在执行邻居的加权平均。因此,预测将不那么极端,并且与实际目标相比变化较小。这种影响的大小将由算法中邻居的数量
k
决定。
我们将通过实现最近邻来预测住房价值来开始本章。这是从最近邻开始的好方法,因为我们将处理数字特征和连续目标。
为了说明如何在 TensorFlow 中使用最近邻进行预测,我们将使用波士顿住房数据集。在这里,我们将预测邻域住房价值中位数作为几个特征的函数。
由于我们考虑训练集训练模型,我们将找到预测点的 KNN,并将计算目标值的加权平均值。
我们按如下方式处理秘籍:
requests
模块从 UCI 机器学习库加载必要的波士顿住房数据:import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import requests
sess = tf.Session()
requests
模块加载数据:housing_url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/housing/housing.data'
housing_header = ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV']
cols_used = ['CRIM', 'INDUS', 'NOX', 'RM', 'AGE', 'DIS', 'TAX', 'PTRATIO', 'B', 'LSTAT']
num_features = len(cols_used)
# Request data
housing_file = requests.get(housing_url)
# Parse Data
housing_data = [[float(x) for x in y.split(' ') if len(x)>=1] for y in housing_file.text.split('n') if len(y)>=1]
MEDV
,这是房屋组的中值。我们也不会使用ZN
,CHAS
和RAD
特征,因为它们没有信息或二元性质:y_vals = np.transpose([np.array([y[13] for y in housing_data])])
x_vals = np.array([[x for i,x in enumerate(y) if housing_header[i] in cols_used] for y in housing_data])
x_vals = (x_vals - x_vals.min(0)) / x_vals.ptp(0)
x
和y
值分成训练和测试集。我们将通过随机选择大约 80% 的行来创建训练集,并将剩下的 20% 留给测试集:train_indices = np.random.choice(len(x_vals), round(len(x_vals)*0.8), replace=False)
test_indices = np.array(list(set(range(len(x_vals))) - set(train_indices)))
x_vals_train = x_vals[train_indices]
x_vals_test = x_vals[test_indices]
y_vals_train = y_vals[train_indices]
y_vals_test = y_vals[test_indices]
k
值和批量大小:k = 4
batch_size=len(x_vals_test)
x_data_train = tf.placeholder(shape=[None, num_features], dtype=tf.float32)
x_data_test = tf.placeholder(shape=[None, num_features], dtype=tf.float32)
y_target_train = tf.placeholder(shape=[None, 1], dtype=tf.float32)
y_target_test = tf.placeholder(shape=[None, 1], dtype=tf.float32)
distance = tf.reduce_sum(tf.abs(tf.subtract(x_data_train, tf.expand_dims(x_data_test,1))), reduction_indices=2)
注意,也可以使用 L2 距离函数。我们将距离公式改为
distance = tf.sqrt(tf.reduce_sum(tf.square(tf.subtract(x_data_train, tf.expand_dims(x_data_test,1))), reduction_indices=1))
。
top_k()
函数,该函数返回张量中最大值的值和索引。由于我们想要最小距离的指数,我们将找到k
- 最大负距离。我们还将声明目标值的预测和均方误差(MSE):top_k_xvals, top_k_indices = tf.nn.top_k(tf.negative(distance), k=k)
x_sums = tf.expand_dims(tf.reduce_sum(top_k_xvals, 1),1)
x_sums_repeated = tf.matmul(x_sums,tf.ones([1, k], tf.float32))
x_val_weights = tf.expand_dims(tf.divide(top_k_xvals,x_sums_repeated), 1)
top_k_yvals = tf.gather(y_target_train, top_k_indices)
prediction = tf.squeeze(tf.batch_matmul(x_val_weights,top_k_yvals), squeeze_dims=[1])
mse = tf.divide(tf.reduce_sum(tf.square(tf.subtract(prediction, y_target_test))), batch_size)
num_loops = int(np.ceil(len(x_vals_test)/batch_size))
for i in range(num_loops):
min_index = i*batch_size
max_index = min((i+1)*batch_size,len(x_vals_train))
x_batch = x_vals_test[min_index:max_index]
y_batch = y_vals_test[min_index:max_index]
predictions = sess.run(prediction, feed_dict={x_data_train: x_vals_train, x_data_test: x_batch, y_target_train: y_vals_train, y_target_test: y_batch})
batch_mse = sess.run(mse, feed_dict={x_data_train: x_vals_train, x_data_test: x_batch, y_target_train: y_vals_train, y_target_test: y_batch})
print('Batch #' + str(i+1) + ' MSE: ' + str(np.round(batch_mse,3)))
Batch #1 MSE: 23.153
bins = np.linspace(5, 50, 45)
plt.hist(predictions, bins, alpha=0.5, label='Prediction')
plt.hist(y_batch, bins, alpha=0.5, label='Actual')
plt.title('Histogram of Predicted and Actual Values')
plt.xlabel('Med Home Value in $1,000s')
plt.ylabel('Frequency')
plt.legend(loc='upper right')
plt.show()
然后我们将获得直方图,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EeDqAblx-1681566813076)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/9900e23a-473d-4a23-8864-30cfcc69e691.png)]
图 1:KNN 的预测值和实际目标值的直方图(其中k=4
)
一个难以确定的是k
的最佳值。对于上图和预测,我们将k=4
用于我们的模型。我们之所以选择这个,是因为它给了我们最低的 MSE。这通过交叉验证来验证。如果我们在k
的多个值上使用交叉验证,我们将看到k=4
给我们一个最小的 MSE。我们在下图中说明了这一点。绘制预测值的方差也是值得的,以表明它会随着我们平均的邻居越多而减少:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WshbdZHz-1681566813076)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/7320b5e5-e41d-4431-9ef1-7fa0b43a5187.png)]
图 2:各种k
值的 KNN 预测的 MSE。我们还绘制了测试集上预测值的方差。请注意,随着k
的增加,方差会减小。
使用最近邻算法,模型是训练集。因此,我们不必在模型中训练任何变量。唯一的参数k
是通过交叉验证确定的,以最大限度地减少我们的 MSE。
对于 KNN 的加权,我们选择直接按距离加权。还有其他选择我们也可以考虑。另一种常见方法是通过反平方距离加权。
最近邻比处理数字更通用。只要我们有一种方法来测量特征之间的距离,我们就可以应用最近邻算法。在本文中,我们将介绍如何使用 TensorFlow 测量文本距离。
在本文中,我们将说明如何在字符串之间使用 TensorFlow 的文本距离度量,Levenshtein 距离(编辑距离)。这将在本章后面重要,因为我们扩展了最近邻方法以包含带有文本的特征。
Levenshtein 距离是从一个字符串到另一个字符串的最小编辑次数。允许的编辑是插入字符,删除字符或用不同的字符替换字符。对于这个秘籍,我们将使用 TensorFlow 的 Levenshtein 距离函数edit_distance()
。值得说明这个函数的用法,因为它的用法将适用于后面的章节。
请注意,TensorFlow 的
edit_distance()
函数仅接受稀疏张量。我们必须创建我们的字符串作为单个字符的稀疏张量。
import tensorflow as tf
sess = tf.Session()
'bear'
和'beer'
之间的编辑距离。首先,我们将使用 Python 的list()
函数从我们的字符串创建一个字符列表。接下来,我们将从该列表中创建一个稀疏的 3D 矩阵。我们必须告诉 TensorFlow 字符索引,矩阵的形状以及我们在张量中想要的字符。之后,我们可以决定是否要使用总编辑距离(normalize=False)
或标准化编辑距离(normalize=True)
,我们将编辑距离除以第二个单词的长度:hypothesis = list('bear')
truth = list('beers')
h1 = tf.SparseTensor([[0,0,0], [0,0,1], [0,0,2], [0,0,3]],
hypothesis, [1,1,1])
t1 = tf.SparseTensor([[0,0,0], [0,0,1], [0,0,1], [0,0,3],[0,0,4]], truth, [1,1,1])
print(sess.run(tf.edit_distance(h1, t1, normalize=False)))
[[ 2.]]
TensorFlow 的文档将两个字符串视为提议(假设)字符串和基础事实字符串。我们将在这里用
h
和t
张量继续这个表示法。函数SparseTensorValue()
是一种在 TensorFlow 中创建稀疏张量的方法。它接受我们希望创建的稀疏张量的索引,值和形状。
bear
和beer
与另一个单词beers
进行比较。为了达到这个目的,我们必须复制beers
以获得相同数量的可比词:hypothesis2 = list('bearbeer')
truth2 = list('beersbeers')
h2 = tf.SparseTensor([[0,0,0], [0,0,1], [0,0,2], [0,0,3], [0,1,0], [0,1,1], [0,1,2], [0,1,3]], hypothesis2, [1,2,4])
t2 = tf.SparseTensor([[0,0,0], [0,0,1], [0,0,2], [0,0,3], [0,0,4], [0,1,0], [0,1,1], [0,1,2], [0,1,3], [0,1,4]], truth2, [1,2,5])
print(sess.run(tf.edit_distance(h2, t2, normalize=True)))
[[ 0.40000001 0.2 ]]
hypothesis_words = ['bear','bar','tensor','flow']
truth_word = ['beers'']
num_h_words = len(hypothesis_words)
h_indices = [[xi, 0, yi] for xi,x in enumerate(hypothesis_words) for yi,y in enumerate(x)]
h_chars = list(''.join(hypothesis_words))
h3 = tf.SparseTensor(h_indices, h_chars, [num_h_words,1,1])
truth_word_vec = truth_word*num_h_words
t_indices = [[xi, 0, yi] for xi,x in enumerate(truth_word_vec) for yi,y in enumerate(x)]
t_chars = list(''.join(truth_word_vec))
t3 = tf.SparseTensor(t_indices, t_chars, [num_h_words,1,1])
print(sess.run(tf.edit_distance(h3, t3, normalize=True)))
[[ 0.40000001]
[ 0.60000002]
[ 0.80000001]
[ 1\. ]]
SparseTensorValue()
而不是稀疏张量。首先,我们将创建一个从单词列表创建稀疏张量的函数:def create_sparse_vec(word_list):
num_words = len(word_list)
indices = [[xi, 0, yi] for xi,x in enumerate(word_list) for yi,y in enumerate(x)]
chars = list(''.join(word_list))
return(tf.SparseTensorValue(indices, chars, [num_words,1,1]))
hyp_string_sparse = create_sparse_vec(hypothesis_words)
truth_string_sparse = create_sparse_vec(truth_word*len(hypothesis_words))
hyp_input = tf.sparse_placeholder(dtype=tf.string)
truth_input = tf.sparse_placeholder(dtype=tf.string)
edit_distances = tf.edit_distance(hyp_input, truth_input, normalize=True)
feed_dict = {hyp_input: hyp_string_sparse,
truth_input: truth_string_sparse}
print(sess.run(edit_distances, feed_dict=feed_dict))
[[ 0.40000001]
[ 0.60000002]
[ 0.80000001]
[ 1\. ]]
在这个秘籍中,我们展示了我们可以使用 TensorFlow 以多种方式测量文本距离。这对于在具有文本特征的数据上执行最近邻非常有用。当我们执行地址匹配时,我们将在本章后面看到更多内容。
我们应该讨论其他文本距离指标。这是一个定义表,描述了两个字符串s1
和s2
之间的其他文本距离:
名称 | 描述 | 公式 |
---|---|---|
汉明距离 | 相同位置的相等字符的数量。仅在字符串长度相等时有效。 | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4wy8hFiJ-1681566813077)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/3f9a30b1-0aff-4c8d-b1ca-f00744f177cf.png)],其中I是相等字符的指示函数。 |
余弦距离 | k差异的点积除以k差异的 L2 范数。 | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DOKF3Txi-1681566813077)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/0015e197-fc53-491a-82a0-9d1acfc4b795.png)] |
雅克卡距离 | 共同的字符数除以两个字符串中的字符总和。 | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LHxVz1kB-1681566813077)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/b9646b5b-e1cf-4fc1-9099-9dd1c9813b07.png)] |
在处理具有多个特征的数据观察时,我们应该意识到特征可以在不同的尺度上以不同的方式缩放。在这个方案中,我们将考虑到这一点,以改善我们的住房价值预测。
扩展最近邻算法很重要,要考虑不同缩放的变量。在这个例子中,我们将说明如何缩放不同变量的距离函数。具体来说,我们将距离函数作为特征方差的函数进行缩放。
加权距离函数的关键是使用权重矩阵。用矩阵运算写的距离函数变为以下公式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ltJfzReg-1681566813077)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/4b4a519d-305e-4b3b-85b9-2b05a51aaeb7.png)]
这里,A
是一个对角线权重矩阵,我们将用它来缩放每个特征的距离度量。
在本文中,我们将尝试在波士顿住房价值数据集上改进我们的 MSE。该数据集是不同尺度上的特征的一个很好的例子,并且最近邻算法将受益于缩放距离函数。
我们将按如下方式处理秘籍:
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import requests
sess = tf.Session()
housing_url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/housing/housing.data'
housing_header = ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV']
cols_used = ['CRIM', 'INDUS', 'NOX', 'RM', 'AGE', 'DIS', 'TAX', 'PTRATIO', 'B', 'LSTAT']
num_features = len(cols_used)
housing_file = requests.get(housing_url)
housing_data = [[float(x) for x in y.split(' ') if len(x)>=1] for y in housing_file.text.split('\n') if len(y)>=1]
y_vals = np.transpose([np.array([y[13] for y in housing_data])])
x_vals = np.array([[x for i,x in enumerate(y) if housing_header[i] in cols_used] for y in housing_data])
x
值缩放到 0 到 1 之间,最小 - 最大缩放:x_vals = (x_vals - x_vals.min(0)) / x_vals.ptp(0)
weight_diagonal = x_vals.std(0)
weight_matrix = tf.cast(tf.diag(weight_diagonal), dtype=tf.float32)
k
,最近邻的数量,并使批量大小等于测试集大小:train_indices = np.random.choice(len(x_vals), round(len(x_vals)*0.8), replace=False)
test_indices = np.array(list(set(range(len(x_vals))) - set(train_indices)))
x_vals_train = x_vals[train_indices]
x_vals_test = x_vals[test_indices]
y_vals_train = y_vals[train_indices]
y_vals_test = y_vals[test_indices]
k = 4
batch_size=len(x_vals_test)
y
- 目标:x_data_train = tf.placeholder(shape=[None, num_features], dtype=tf.float32)
x_data_test = tf.placeholder(shape=[None, num_features], dtype=tf.float32)
y_target_train = tf.placeholder(shape=[None, 1], dtype=tf.float32)
y_target_test = tf.placeholder(shape=[None, 1], dtype=tf.float32)
batch_matmul()
函数在批量大小中执行批量矩阵乘法:subtraction_term = tf.subtract(x_data_train, tf.expand_dims(x_data_test,1))
first_product = tf.batch_matmul(subtraction_term, tf.tile(tf.expand_dims(weight_matrix,0), [batch_size,1,1]))
second_product = tf.batch_matmul(first_product, tf.transpose(subtraction_term, perm=[0,2,1]))
distance = tf.sqrt(tf.batch_matrix_diag_part(second_product))
top_k()
函数执行此操作。由于此函数返回最大值,并且我们想要最小距离,因此我们返回最大的负距离值。然后,我们将预测作为顶部k
邻居的距离的加权平均值:top_k_xvals, top_k_indices = tf.nn.top_k(tf.neg(distance), k=k)
x_sums = tf.expand_dims(tf.reduce_sum(top_k_xvals, 1),1)
x_sums_repeated = tf.matmul(x_sums,tf.ones([1, k], tf.float32))
x_val_weights = tf.expand_dims(tf.div(top_k_xvals,x_sums_repeated), 1)
top_k_yvals = tf.gather(y_target_train, top_k_indices)
prediction = tf.squeeze(tf.batch_matmul(x_val_weights,top_k_yvals), squeeze_dims=[1])
mse = tf.divide(tf.reduce_sum(tf.square(tf.subtract(prediction, y_target_test))), batch_size)
num_loops = int(np.ceil(len(x_vals_test)/batch_size))
for i in range(num_loops):
min_index = i*batch_size
max_index = min((i+1)*batch_size,len(x_vals_train))
x_batch = x_vals_test[min_index:max_index]
y_batch = y_vals_test[min_index:max_index]
predictions = sess.run(prediction, feed_dict={x_data_train: x_vals_train, x_data_test: x_batch, y_target_train: y_vals_train, y_target_test: y_batch})
batch_mse = sess.run(mse, feed_dict={x_data_train: x_vals_train, x_data_test: x_batch, y_target_train: y_vals_train, y_target_test: y_batch})
print('Batch #' + str(i+1) + ' MSE: ' + str(np.round(batch_mse,3)))
Batch #1 MSE: 21.322
bins = np.linspace(5, 50, 45)
plt.hist(predictions, bins, alpha=0.5, label='Prediction')
plt.hist(y_batch, bins, alpha=0.5, label='Actual')
plt.title('Histogram of Predicted and Actual Values')
plt.xlabel('Med Home Value in $1,000s')
plt.ylabel('Frequency')
plt.legend(loc='upper right')
plt.show()
我们将获得前面代码的以下直方图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cucNSBge-1681566813077)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/5d0b3e2a-6c96-4934-9065-a903836c40e9.png)]
图 3:波士顿数据集上预测房屋价值和实际房屋价值的两个直方图;这一次,我们为每个特征不同地缩放了距离函数
我们通过引入一种缩放每个特征的距离函数的方法来减少测试集上的 MSE。在这里,我们通过特征标准偏差的因子来缩放距离函数。这提供了更准确的测量视图,其中测量哪些点是最近的邻居。由此,我们还将顶部k
邻域的加权平均值作为距离的函数,以获得住房价值预测。
该缩放因子还可以用于最近邻距离计算中的向下加权或向上加权的特征。这在我们比某些特征更信任某些特征的情况下非常有用。
现在我们已经测量了数值和文本距离,我们将花一些时间学习如何将它们组合起来测量具有文本和数字特征的观察之间的距离。
最近邻是一种用于地址匹配的好算法。地址匹配是一种记录匹配,其中我们在多个数据集中具有地址并且想要匹配它们。在地址匹配中,我们可能在地址,不同城市或不同的邮政编码中存在拼写错误,但它们可能都指向相同的地址。在地址的数字和字符组件上使用最近邻算法可以帮助我们识别实际上相同的地址。
在此示例中,我们将生成两个数据集。每个数据集将包含街道地址和邮政编码。但是,一个数据集在街道地址中存在大量拼写错误。我们将非拼写数据集作为我们的黄金标准,并将为每个拼写错误地址返回一个地址,该地址最接近字符串距离(对于街道)和数字距离(对于邮政编码)的函数。
代码的第一部分将侧重于生成两个数据集。然后,代码的第二部分将运行测试集并返回训练集中最接近的地址。
我们将按如下方式处理秘籍:
import random
import string
import numpy as np
import tensorflow as tf
10
地址组成(但它可以运行更多):n = 10 street_names = ['abbey', 'baker', 'canal', 'donner', 'elm']
street_types = ['rd', 'st', 'ln', 'pass', 'ave']
rand_zips = [random.randint(65000,65999) for i in range(5)]
numbers = [random.randint(1, 9999) for i in range(n)]
streets = [random.choice(street_names) for i in range(n)]
street_suffs = [random.choice(street_types) for i in range(n)]
zips = [random.choice(rand_zips) for i in range(n)]
full_streets = [str(x) + ' ' + y + ' ' + z for x,y,z in zip(numbers, streets, street_suffs)]
reference_data = [list(x) for x in zip(full_streets,zips)]
def create_typo(s, prob=0.75):
if random.uniform(0,1) < prob:
rand_ind = random.choice(range(len(s)))
s_list = list(s)
s_list[rand_ind]=random.choice(string.ascii_lowercase)
s = ''.join(s_list)
return s
typo_streets = [create_typo(x) for x in streets]
typo_full_streets = [str(x) + ' ' + y + ' ' + z for x,y,z in zip(numbers, typo_streets, street_suffs)]
test_data = [list(x) for x in zip(typo_full_streets,zips)]
sess = tf.Session()
test_address = tf.sparse_placeholder( dtype=tf.string)
test_zip = tf.placeholder(shape=[None, 1], dtype=tf.float32)
ref_address = tf.sparse_placeholder(dtype=tf.string)
ref_zip = tf.placeholder(shape=[None, n], dtype=tf.float32)
zip_dist = tf.square(tf.subtract(ref_zip, test_zip))
address_dist = tf.edit_distance(test_address, ref_address, normalize=True)
1
的相似性,当它们非常不同时,我们想要0
附近。对于拉链距离,我们可以通过获取距离,从最大值减去,然后除以距离的范围来实现。对于地址相似性,由于距离已经在0
和1
之间缩放,我们只需从 1 中减去它以获得相似性:zip_max = tf.gather(tf.squeeze(zip_dist), tf.argmax(zip_dist, 1))
zip_min = tf.gather(tf.squeeze(zip_dist), tf.argmin(zip_dist, 1))
zip_sim = tf.divide(tf.subtract(zip_max, zip_dist), tf.subtract(zip_max, zip_min))
address_sim = tf.subtract(1., address_dist)
address_weight = 0.5
zip_weight = 1\. - address_weight
weighted_sim = tf.add(tf.transpose(tf.multiply(address_weight, address_sim)), tf.multiply(zip_weight, zip_sim))
top_match_index = tf.argmax(weighted_sim, 1)
def sparse_from_word_vec(word_vec):
num_words = len(word_vec)
indices = [[xi, 0, yi] for xi,x in enumerate(word_vec) for yi,y in enumerate(x)]
chars = list(''.join(word_vec))
# Now we return our sparse vector
return tf.SparseTensorValue(indices, chars, [num_words,1,1])
reference_addresses = [x[0] for x in reference_data]
reference_zips = np.array([[x[1] for x in reference_data]])
sparse_ref_set = sparse_from_word_vec(reference_addresses)
for i in range(n):
test_address_entry = test_data[i][0]
test_zip_entry = [[test_data[i][1]]]
# Create sparse address vectors
test_address_repeated = [test_address_entry] * n
sparse_test_set = sparse_from_word_vec(test_address_repeated)
feeddict={test_address: sparse_test_set,
test_zip: test_zip_entry,
ref_address: sparse_ref_set,
ref_zip: reference_zips}
best_match = sess.run(top_match_index, feed_dict=feeddict)
best_street = reference_addresses[best_match[0]]
[best_zip] = reference_zips[0][best_match]
[[test_zip_]] = test_zip_entry
print('Address: ' + str(test_address_entry) + ', ' + str(test_zip_))
print('Match : ' + str(best_street) + ', ' + str(best_zip))
我们将得到以下结果:
Address: 8659 beker ln, 65463
Match : 8659 baker ln, 65463
Address: 1048 eanal ln, 65681
Match : 1048 canal ln, 65681
Address: 1756 vaker st, 65983
Match : 1756 baker st, 65983
Address: 900 abbjy pass, 65983
Match : 900 abbey pass, 65983
Address: 5025 canal rd, 65463
Match : 5025 canal rd, 65463
Address: 6814 elh st, 65154
Match : 6814 elm st, 65154
Address: 3057 cagal ave, 65463
Match : 3057 canal ave, 65463
Address: 7776 iaker ln, 65681
Match : 7776 baker ln, 65681
Address: 5167 caker rd, 65154
Match : 5167 baker rd, 65154
Address: 8765 donnor st, 65154
Match : 8765 donner st, 65154
在像这样的地址匹配问题中要弄清楚的一个难点是权重的值以及如何缩放距离。这可能需要对数据本身进行一些探索和洞察。此外,在处理地址时,我们应该考虑除此处使用的组件之外的组件。我们可以将街道号码视为街道地址的独立组成部分,甚至可以包含其他组成部分,例如城市和州。
处理数字地址组件时,请注意它们可以被视为数字(具有数字距离)或字符(具有编辑距离)。由您决定选择哪个。请注意,如果我们认为邮政编码中的拼写错误来自人为错误而不是计算机映射错误,我们可以考虑使用邮政编码的编辑距离。
为了了解拼写错误如何影响结果,我们鼓励读者更改拼写错误函数以进行更多拼写错误或更频繁的拼写错误,并增加数据集大小以查看此算法的工作情况。
最近邻也可用于图像识别。图像识别数据集的问题世界是 MNIST 手写数字数据集。由于我们将在后面的章节中将此数据集用于各种神经网络图像识别算法,因此将结果与非神经网络算法进行比较将会很棒。
MNIST 数字数据集由数千个大小为28×28
像素的标记图像组成。虽然这被认为是一个小图像,但它对于最近邻算法总共有 784 个像素(或特征)。我们将通过考虑最近的k
邻居(k=4
,在该示例中)的模式预测来计算该分类问题的最近邻预测。
我们将按如下方式处理秘籍:
import random
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from PIL import Image
from tensorflow.examples.tutorials.mnist import input_data
sess = tf.Session()
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
单热编码是更适合数值计算的分类值的数值表示。这里,我们有 10 个类别(数字 0-9),并将它们表示为长度为 10 的 0-1 向量。例如,类别 0 由向量
1,0,0,0,0,0
表示,类别 1 用0,1,0,0,0,0
表示,依此类推。
train_size = 1000
test_size = 102
rand_train_indices = np.random.choice(len(mnist.train.images), train_size, replace=False)
rand_test_indices = np.random.choice(len(mnist.test.images), test_size, replace=False)
x_vals_train = mnist.train.images[rand_train_indices]
x_vals_test = mnist.test.images[rand_test_indices]
y_vals_train = mnist.train.labels[rand_train_indices]
y_vals_test = mnist.test.labels[rand_test_indices]
k
值和批量大小:k = 4
batch_size=6
x_data_train = tf.placeholder(shape=[None, 784], dtype=tf.float32)
x_data_test = tf.placeholder(shape=[None, 784], dtype=tf.float32)
y_target_train = tf.placeholder(shape=[None, 10], dtype=tf.float32)
y_target_test = tf.placeholder(shape=[None, 10], dtype=tf.float32)
distance = tf.reduce_sum(tf.abs(tf.subtract(x_data_train, tf.expand_dims(x_data_test,1))), reduction_indices=2)
请注意,我们也可以使用以下代码来改变距离函数:
distance = tf.sqrt(tf.reduce_sum(tf.square(tf.subtract(x_data_train, tf.expand_dims(x_data_test,1))), reduction_indices=1))
。
k
图像并预测模式。该模式将在单热编码索引上执行,计数最多:top_k_xvals, top_k_indices = tf.nn.top_k(tf.negative(distance), k=k)
prediction_indices = tf.gather(y_target_train, top_k_indices)
count_of_predictions = tf.reduce_sum(prediction_indices, reduction_indices=1)
prediction = tf.argmax(count_of_predictions)
num_loops = int(np.ceil(len(x_vals_test)/batch_size))
test_output = []
actual_vals = []
for i in range(num_loops):
min_index = i*batch_size
max_index = min((i+1)*batch_size,len(x_vals_train))
x_batch = x_vals_test[min_index:max_index]
y_batch = y_vals_test[min_index:max_index]
predictions = sess.run(prediction, feed_dict={x_data_train: x_vals_train, x_data_test: x_batch, y_target_train: y_vals_train, y_target_test: y_batch})
test_output.extend(predictions)
actual_vals.extend(np.argmax(y_batch, axis=1))
accuracy = sum([1./test_size for i in range(test_size) if test_output[i]==actual_vals[i]])
print('Accuracy on test set: ' + str(accuracy))
Accuracy on test set: 0.8333333333333325
actuals = np.argmax(y_batch, axis=1)
Nrows = 2
Ncols = 3
for i in range(len(actuals)):
plt.subplot(Nrows, Ncols, i+1)
plt.imshow(np.reshape(x_batch[i], [28,28]), cmap='Greys_r')
plt.title('Actual: ' + str(actuals[i]) + ' Pred: ' + str(predictions[i]), fontsize=10)
frame = plt.gca()
frame.axes.get_xaxis().set_visible(False)
frame.axes.get_yaxis().set_visible(False)
结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EC6Yi8jJ-1681566813078)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/37d1bd73-254f-4a86-b749-9d0b476f388b.png)]
图 4:我们运行最近邻预测的最后一批六个图像。我们可以看到,我们并没有完全正确地获得所有图像。
给定足够的计算时间和计算资源,我们可以使测试和训练集更大。这可能会提高我们的准确率,也是防止过拟合的常用方法。另外,请注意,此算法需要进一步探索理想的k
值进行选择。可以在数据集上进行一组交叉验证实验后选择k
值。
我们还可以使用最近邻算法来评估用户看不见的数字。有关使用此模型评估用户输入数字的方法,请参阅在线仓库。
在本章中,我们探讨了如何使用 KNN 算法进行回归和分类。我们讨论了距离函数的不同用法,以及如何将它们混合在一起。我们鼓励读者探索不同的距离度量,权重和k
值,以优化这些方法的准确率。