十一、生成模型和 CapsNet
在本章中,我们将介绍一些用于以下方面的方法:
在本章中,我们将讨论如何将生成对抗网络(GAN)用于深度学习领域,其中关键方法是训练图像生成器来挑战鉴别器,并同时训练鉴别器来改进生成器。 可以将相同的方法应用于不同于图像领域。 另外,我们将讨论变分自编码器。
GAN 已被深度学习之父之一 Yann LeCun 定义为“这是深度学习的突破”。 GAN 能够学习如何再现看起来真实的合成数据。 例如,计算机可以学习如何绘制和创建逼真的图像。 这个想法最初是由与蒙特利尔大学 Google Brain 合作的 Ian Goodfellow 提出的,最近由 OpenAI 提出。
通过将其视为类似于艺术伪造的方式,可以很容易地理解 GAN 的关键过程,这是创作被误认为其他人(通常是更著名的艺术家)的艺术品的过程。 GAN 同时训练两个神经网络。
生成器G(Z)
是生成赝品,而鉴别器D(Y)
可以根据对真实艺术品和复制品的观察来判断复制品的逼真度。 D(Y)
接受输入 Y(例如图像),并投票决定输入的真实程度。 通常,接近零的值表示真实,而接近一的值表示伪造。G(Z)
从随机噪声Z
中获取输入,并训练自己以欺骗 D 认为G(Z)
产生的任何东西都是真实的。 因此,训练鉴别器D(Y)
的目标是,从为每个来自真实数据分布的图像最大化D(Y)
,并为每个不来自真实数组的图像最小化D(Y)
,而不是来自真实数据分布的每个图像。 因此,G 和 D 扮演相反的游戏:因此称为对抗训练。 请注意,我们以交替的方式训练 G 和 D,其中它们的每个目标都表示为通过梯度下降优化的损失函数。 生成模型学习如何越来越好地进行伪造,而鉴别模型学习如何越来越好地识别伪造。
鉴别器网络(通常是标准卷积神经网络)试图对输入图像是真实的还是生成的进行分类。 一个重要的新思想是反向传播鉴别器和生成器,以调整生成器的参数,以使生成器可以学习如何在越来越多的情况下欺骗鉴别器。 最后,生成器将学习如何生成与真实图像无法区分的图像:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7J7LVuc8-1681565377458)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/7c949720-9d74-4438-b62f-1e34e72c2ece.png)]
生成器(伪造)- 鉴别器(判断)模型的示例。 鉴别器接收伪造的真实图像
当然,GAN 可以在有两名玩家的游戏中找到平衡点。 为了有效学习,如果一个玩家在下一轮更新中成功下坡,那么相同的更新也必须使另一个玩家也下坡。 想想看! 如果伪造者每次都学会如何愚弄法官,那么伪造者本人就没什么可学的了。 有时,两个玩家最终会达到平衡,但这并不总是可以保证的,因此两个玩家可以长时间继续比赛。 下图提供了双方的示例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hgno7Bli-1681565377459)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/e58a4195-d083-46fb-85e5-7495cee19173.png)]
生成器和鉴别器的收敛示例
我们已经确定生成器学习如何伪造数据。 这意味着它将学习如何创建由网络创建的新合成数据,并且看起来是真实的并且由人类创建。 在讨论有关 GAN 代码的详细信息之前,我想分享使用 GAN 的最新论文(代码可在线获得)的结果。 从文本描述开始合成伪造的图像。 结果令人印象深刻。 第一列是测试集中的真实图像,其他所有列都是从 StackGAN 的 Stage-I 和 Stage-II 中相同的文本描述生成的图像。 YouTube 上有更多示例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ImMGtZjV-1681565377460)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/60422def-cdf2-489b-8974-49708350aea0.png)]
现在,让我们看看 GAN 如何学习伪造 MNIST 数据集。 在这种情况下,它是用于生成器和鉴别器网络的 GAN 和卷积网络的组合。 最初,生成器不会产生任何可理解的东西,但是经过几次迭代,合成的伪造数字变得越来越清晰。 在下图中,通过增加训练时期来对面板进行排序,您可以看到面板之间的质量改进:
改进后的图像如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SAJ9h1o4-1681565377461)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/e716e40e-9737-44bd-abc2-d6cc7b240339.png)]
我们可以在下图中看到进一步的改进:
GAN 最酷的用途之一是对生成器向量Z
的面部进行算术。换句话说,如果我们停留在合成伪造图像的空间中,则可能会看到类似以下内容:[微笑的女人]-[中性的女人] + [中性的男人] = [微笑的男人]
,或类似这样:[戴眼镜的男人] - [戴眼镜的男人] + [戴眼镜的女人] = [戴眼镜的女人]
。 下图取自:《深度卷积生成对抗网络的无监督表示学习》(Alec Radford,Luke Metz,Soumith Chintala,2016)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jX9gEmwS-1681565377461)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/01744226-effd-46ec-9e04-f7668a1379aa.png)]
这个链接提供了 GAN 的其他出色示例。 本文中所有图像均由神经网络生成。 他们不是真实的。 全文可在此处找到。
卧室:经过五个时期的训练后生成的卧室:
生成卧室的示例
专辑封面:这些图像不是真实的,而是由 GAN 生成的。 专辑封面看起来很真实:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UTeNlz40-1681565377462)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/3f94861f-9f24-4b33-92d1-44b413fb0408.png)]
生成专辑封面的示例
Ian J.Goodfellow,Jean Pouget-Abadie,Mehdi Mirza,Bing Xu,David Warde-Farley,Sherjil Ozair,Aaron Courville,Yoshua Bengio 等人撰写的 Generative Adversarial Networks(2014)是更好地理解 GAN 的好论文。 在本秘籍中,我们将学习如何使用以 Generator-Discriminator 架构组织的全连接层网络来伪造 MNIST 手写数字。
此秘籍基于这个页面上可用的代码。
我们按以下步骤进行:
git clone https://github.com/TengdaHan/GAN-TensorFlow
Understanding the difficulty of training deep feedforward neural networks (2009) by Xavier Glorot, Yoshua Bengio, http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.207.2059&rep=rep1&type=pdf
中所述。事实证明,初始化器可让 GAN 更好地收敛:def xavier_init(size):
in_dim = size[0]
xavier_stddev = 1\. / tf.sqrt(in_dim / 2.)
return xavier_stddev
X
的生成器。首先,我们定义尺寸为[100, K = 128]
的矩阵W1
,并根据正态分布对其进行初始化。 注意 100 是Z
的任意值,Z
是我们的生成器使用的初始噪声。 然后,我们定义尺寸为[K = 256]
的偏差B1
。 类似地,我们定义尺寸为[K=128, L=784]
的矩阵W2
和尺寸为[L = 784]
的偏置B2
。 使用步骤 1 中定义的xavier_init
初始化两个矩阵W1
和W2
,而使用tf.constant_initializer()
初始化B1
和B2
。 之后,我们计算矩阵X * W1
之间的乘法,求和B1
的偏差,然后将其传递给 RELU 激活函数以获得fc1
。 然后将该密集层与下一个密集层连接,该密集层是通过将矩阵fc1
与W2
相乘并求和B2
的偏差而创建的。 然后将结果通过 Sigmoid 函数传递。 这些步骤用于定义用于生成器的两层神经网络:def generator(X):
with tf.variable_scope('generator'):
K = 128
L = 784
W1 = tf.get_variable('G_W1', [100, K],
initializer=tf.random_normal_initializer(stddev=xavier_init([100, K])))
B1 = tf.get_variable('G_B1', [K], initializer=tf.constant_initializer())
W2 = tf.get_variable('G_W2', [K, L],
initializer=tf.random_normal_initializer(stddev=xavier_init([K, L])))
B2 = tf.get_variable('G_B2', [L], initializer=tf.constant_initializer())
# summary
tf.summary.histogram('weight1', W1)
tf.summary.histogram('weight2', W2)
tf.summary.histogram('biases1', B1)
tf.summary.histogram('biases2', B2)
fc1 = tf.nn.relu((tf.matmul(X, W1) + B1))
fc2 = tf.matmul(fc1, W2) + B2
prob = tf.nn.sigmoid(fc2)
return prob
X
的鉴别器。原则上,这与生成器非常相似。 主要区别在于,如果参数重用为true
,则调用scope.reuse_variables()
触发重用。 然后我们定义两个密集层。 第一层使用尺寸为[J=784, K=128]
的矩阵W1
,尺寸为[K=128]
的偏差B1
,并且它基于X
与W1
的标准乘积。 将该结果添加到B1
并传递给 RELU 激活函数以获取结果fc1
。 第二个矩阵使用尺寸为[K=128, L=1]
的矩阵W2
和尺寸为[L=1]
的偏差B2
,它基于fc1
与W2
的标准乘积。 将此结果添加到B2
并传递给 Sigmoid 函数:def discriminator(X, reuse=False):
with tf.variable_scope('discriminator'):
if reuse:
tf.get_variable_scope().reuse_variables()
J = 784
K = 128
L = 1
W1 = tf.get_variable('D_W1', [J, K],
initializer=tf.random_normal_initializer(stddev=xavier_init([J, K])))
B1 = tf.get_variable('D_B1', [K], initializer=tf.constant_initializer())
W2 = tf.get_variable('D_W2', [K, L],
initializer=tf.random_normal_initializer(stddev=xavier_init([K, L])))
B2 = tf.get_variable('D_B2', [L], initializer=tf.constant_initializer())
# summary
tf.summary.histogram('weight1', W1)
tf.summary.histogram('weight2', W2)
tf.summary.histogram('biases1', B1)
tf.summary.histogram('biases2', B2)
fc1 = tf.nn.relu((tf.matmul(X, W1) + B1))
logits = tf.matmul(fc1, W2) + B2
prob = tf.nn.sigmoid(logits)
return prob, logits
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import os
import argparse
def read_data():
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("../MNIST_data/", one_hot=True)
return mnist
def plot(samples):
fig = plt.figure(figsize=(8, 8))
gs = gridspec.GridSpec(8, 8)
gs.update(wspace=0.05, hspace=0.05)
for i, sample in enumerate(samples):
ax = plt.subplot(gs[i])
plt.axis('off')
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.set_aspect('equal')
plt.imshow(sample.reshape(28, 28), cmap='Greys_r')
return fig
28 x 28
形状的矩阵X
。 然后,让我们定义大小为 100 的z
噪声向量,这是 GAN 论文中提出的一个常见选择。 下一步是在z
上调用生成器,然后将结果分配给G
。之后,我们将X
传递给鉴别器,而无需重用。 然后,我们将伪造/伪造的G
结果传递给鉴别器,从而重用学习到的权重。 这方面的一个重要方面是我们如何选择鉴别器的损失函数,该函数是两个交叉熵的和:一个交叉熵,一个用于实字符,其中所有真实 MNIST 字符的标签都设置为一个,另一个用于伪造的字符,其中所有伪造的字符的标签都设置为零。 鉴别器和生成器以交替顺序运行 100,000 步。 每 500 步,会从学习到的分布中抽取一个样本,以打印该生成器到目前为止所学的内容。 这就是定义新周期的条件,结果将在下一节中显示。 让我们看看实现我们刚刚描述的代码片段。def train(logdir, batch_size):
from model_fc import discriminator, generator
mnist = read_data()
with tf.variable_scope('placeholder'):
# Raw image
X = tf.placeholder(tf.float32, [None, 784])
tf.summary.image('raw image', tf.reshape(X, [-1, 28, 28, 1]), 3)
# Noise
z = tf.placeholder(tf.float32, [None, 100]) # noise
tf.summary.histogram('Noise', z)
with tf.variable_scope('GAN'):
G = generator(z)
D_real, D_real_logits = discriminator(X, reuse=False)
D_fake, D_fake_logits = discriminator(G, reuse=True)
tf.summary.image('generated image', tf.reshape(G, [-1, 28, 28, 1]), 3)
with tf.variable_scope('Prediction'):
tf.summary.histogram('real', D_real)
tf.summary.histogram('fake', D_fake)
with tf.variable_scope('D_loss'):
d_loss_real = tf.reduce_mean(
tf.nn.sigmoid_cross_entropy_with_logits(
logits=D_real_logits, labels=tf.ones_like(D_real_logits)))
d_loss_fake = tf.reduce_mean(
tf.nn.sigmoid_cross_entropy_with_logits(
logits=D_fake_logits, labels=tf.zeros_like(D_fake_logits)))
d_loss = d_loss_real + d_loss_fake
tf.summary.scalar('d_loss_real', d_loss_real)
tf.summary.scalar('d_loss_fake', d_loss_fake)
tf.summary.scalar('d_loss', d_loss)
with tf.name_scope('G_loss'):
g_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits
(logits=D_fake_logits, labels=tf.ones_like(D_fake_logits)))
tf.summary.scalar('g_loss', g_loss)
tvar = tf.trainable_variables()
dvar = [var for var in tvar if 'discriminator' in var.name]
gvar = [var for var in tvar if 'generator' in var.name]
with tf.name_scope('train'):
d_train_step = tf.train.AdamOptimizer().minimize(d_loss, var_list=dvar)
g_train_step = tf.train.AdamOptimizer().minimize(g_loss, var_list=gvar)
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
merged_summary = tf.summary.merge_all()
writer = tf.summary.FileWriter('tmp/mnist/'+logdir)
writer.add_graph(sess.graph)
num_img = 0
if not os.path.exists('output/'):
os.makedirs('output/')
for i in range(100000):
batch_X, _ = mnist.train.next_batch(batch_size)
batch_noise = np.random.uniform(-1., 1., [batch_size, 100])
if i % 500 == 0:
samples = sess.run(G, feed_dict={z: np.random.uniform(-1., 1., [64, 100])})
fig = plot(samples)
plt.savefig('output/%s.png' % str(num_img).zfill(3), bbox_inches='tight')
num_img += 1
plt.close(fig)
_, d_loss_print = sess.run([d_train_step, d_loss],
feed_dict={X: batch_X, z: batch_noise})
_, g_loss_print = sess.run([g_train_step, g_loss],
feed_dict={z: batch_noise})
if i % 100 == 0:
s = sess.run(merged_summary, feed_dict={X: batch_X, z: batch_noise})
writer.add_summary(s, i)
print('epoch:%d g_loss:%f d_loss:%f' % (i, g_loss_print, d_loss_print))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Train vanila GAN using fully-connected layers networks')
parser.add_argument('--logdir', type=str, default='1', help='logdir for Tensorboard, give a string')
parser.add_argument('--batch_size', type=int, default=64, help='batch size: give a int')
args = parser.parse_args()
train(logdir=args.logdir, batch_size=args.batch_size)
在每个时期,生成器都会进行许多预测(它会生成伪造的 MNIST 图像),鉴别器会在将预测与实际 MNIST 图像混合后尝试学习如何生成伪造的图像。 在 32 个周期之后,生成器学习伪造这组手写数字。 没有人对机器进行编程来编写,但是它学会了如何编写与人类所写的数字没有区别的数字。 请注意,训练 GAN 可能非常困难,因为有必要在两个参与者之间找到平衡。 如果您对该主题感兴趣,我建议您看看从业者收集的一系列技巧。
让我们看一下不同时期的许多实际示例,以了解机器将如何学习以改善其编写过程:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GrTSFPgO-1681565377462)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/62be9039-67e4-474a-a4d3-1b5a5f22f9f6.png)] | ||
周期 0 | 周期 2 | 周期 4 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IrKjAPc7-1681565377463)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/dce02074-88d4-426f-8fb6-3a7af718befd.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0VH5JjJz-1681565377463)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/fe7664aa-20b2-491e-879b-bad57a38c38f.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rck5iSTX-1681565377463)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/64bbaff9-829c-4591-bbcd-cf8d5cfd915c.png)] |
周期 8 | 周期 16 | 周期 32 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1U0BmKrQ-1681565377464)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/acc43ab3-a2c3-460b-bcac-58d579da5951.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YIx4v40V-1681565377464)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/47e5390f-e506-4e24-b024-c36dbf67b0d8.png)] | |
周期 64 | 周期 128 | 周期 200 |
Example of forged MNIST-like characters with a GAN
在本秘籍中,我们将使用一个简单的 GAN,它使用 CNN 来学习如何伪造 MNIST 图像并创建不属于原始数据集的新图像。 这个想法是 CNN 与 GAN 一起使用将提高处理图像数据集的能力。 请注意,先前的方法是将 GAN 与完全连接的网络一起使用,而在此我们重点介绍 CNN。
此秘籍基于这个页面上可用的代码。
我们按以下步骤进行:
git clone https://github.com/TengdaHan/GAN-TensorFlow
Understanding the difficulty of training deep feedforward neural networks (2009) by Xavier Glorot, Yoshua Bengio, http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.207.2059&rep=rep1&type=pdf
中所述。事实证明,初始化器可让 GAN 更好地收敛:def xavier_init(size):
in_dim = size[0]
xavier_stddev = 1\. / tf.sqrt(in_dim / 2.)
# return tf.random_normal(shape=size, stddev=xavier_stddev)
return xavier_stddev
x
,权重w
,偏差b
和给定步幅定义卷积运算。 我们的代码使用标准的tf.nn.conv2d(...)
模块。 请注意,我们使用第 4 章中定义的SAME
填充:def conv(x, w, b, stride, name):
with tf.variable_scope('conv'):
tf.summary.histogram('weight', w)
tf.summary.histogram('biases', b)
return tf.nn.conv2d(x,
filter=w,
strides=[1, stride, stride, 1],
padding='SAME',
name=name) + b
x
,权重w
,偏差b
和给定步幅定义反卷积运算。 我们的代码使用标准的tf.nn.conv2d_transpose(...)
模块。 同样,我们使用'SAME'
填充。def deconv(x, w, b, shape, stride, name):
with tf.variable_scope('deconv'):
tf.summary.histogram('weight', w)
tf.summary.histogram('biases', b)
return tf.nn.conv2d_transpose(x,
filter=w,
output_shape=shape,
strides=[1, stride, stride, 1],
padding='SAME',
name=name) + b
LeakyReLU
,这对于 GAN 是非常有效的激活函数:def lrelu(x, alpha=0.2):
with tf.variable_scope('leakyReLU'):
return tf.maximum(x, alpha * x)
[100, 7 * 7 * 256]
且根据正态分布初始化的矩阵W1
和尺寸为[7 * 7 * 256]
的偏置B1
组成。 该层使用 ReLu 作为激活函数。 在完全连接的层之后,生成器将应用两个反卷积运算 deconv1 和 deconv2,两者的步幅均为 2。 完成第一个 deconv1 操作后,将结果批量标准化。 请注意,第二次反卷积运算之前会出现丢弃,概率为 40%。 最后一个阶段是一个 Sigmoid,用作非线性激活,如下面的代码片段所示:def generator(X, batch_size=64):
with tf.variable_scope('generator'):
K = 256
L = 128
M = 64
W1 = tf.get_variable('G_W1', [100, 7*7*K], initializer=tf.random_normal_initializer(stddev=0.1))
B1 = tf.get_variable('G_B1', [7*7*K], initializer=tf.constant_initializer())
W2 = tf.get_variable('G_W2', [4, 4, M, K], initializer=tf.random_normal_initializer(stddev=0.1))
B2 = tf.get_variable('G_B2', [M], initializer=tf.constant_initializer())
W3 = tf.get_variable('G_W3', [4, 4, 1, M], initializer=tf.random_normal_initializer(stddev=0.1))
B3 = tf.get_variable('G_B3', [1], initializer=tf.constant_initializer())
X = lrelu(tf.matmul(X, W1) + B1)
X = tf.reshape(X, [batch_size, 7, 7, K])
deconv1 = deconv(X, W2, B2, shape=[batch_size, 14, 14, M], stride=2, name='deconv1')
bn1 = tf.contrib.layers.batch_norm(deconv1)
deconv2 = deconv(tf.nn.dropout(lrelu(bn1), 0.4), W3, B3, shape=[batch_size, 28, 28, 1], stride=2, name='deconv2')
XX = tf.reshape(deconv2, [-1, 28*28], 'reshape')
return tf.nn.sigmoid(XX)
true
,则调用scope.reuse_variables()
触发重用。 鉴别器使用两个卷积层。 第一个是批量归一化,而第二个是概率为 40% 的丢弃,然后是批量归一化步骤。 之后,我们得到了一个具有激活函数 ReLU 的致密层,然后是另一个具有基于 Sigmoid 激活函数的致密层:def discriminator(X, reuse=False):
with tf.variable_scope('discriminator'):
if reuse:
tf.get_variable_scope().reuse_variables()
K = 64
M = 128
N = 256
W1 = tf.get_variable('D_W1', [4, 4, 1, K], initializer=tf.random_normal_initializer(stddev=0.1))
B1 = tf.get_variable('D_B1', [K], initializer=tf.constant_initializer())
W2 = tf.get_variable('D_W2', [4, 4, K, M], initializer=tf.random_normal_initializer(stddev=0.1))
B2 = tf.get_variable('D_B2', [M], initializer=tf.constant_initializer())
W3 = tf.get_variable('D_W3', [7*7*M, N], initializer=tf.random_normal_initializer(stddev=0.1))
B3 = tf.get_variable('D_B3', [N], initializer=tf.constant_initializer())
W4 = tf.get_variable('D_W4', [N, 1], initializer=tf.random_normal_initializer(stddev=0.1))
B4 = tf.get_variable('D_B4', [1], initializer=tf.constant_initializer())
X = tf.reshape(X, [-1, 28, 28, 1], 'reshape')
conv1 = conv(X, W1, B1, stride=2, name='conv1')
bn1 = tf.contrib.layers.batch_norm(conv1)
conv2 = conv(tf.nn.dropout(lrelu(bn1), 0.4), W2, B2, stride=2, name='conv2')
bn2 = tf.contrib.layers.batch_norm(conv2)
flat = tf.reshape(tf.nn.dropout(lrelu(bn2), 0.4), [-1, 7*7*M], name='flat')
dense = lrelu(tf.matmul(flat, W3) + B3)
logits = tf.matmul(dense, W4) + B4
prob = tf.nn.sigmoid(logits)
return prob, logits
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import os
import argparse
def read_data():
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("../MNIST_data/", one_hot=True)
return mnist
def plot(samples):
fig = plt.figure(figsize=(8, 8))
gs = gridspec.GridSpec(8, 8)
gs.update(wspace=0.05, hspace=0.05)
for i, sample in enumerate(samples):
ax = plt.subplot(gs[i])
plt.axis('off')
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.set_aspect('equal')
plt.imshow(sample.reshape(28, 28), cmap='Greys_r')
return fig
28 x 28
形状的矩阵X
。 然后,让我们定义大小为 100 的z
噪声向量,这是 GAN 论文中提出的一个常见选择。 下一步是在z
上调用生成器,然后将结果分配给G
。之后,我们将X
传递给鉴别器,而无需重用。 然后,我们将伪造/伪造的G
结果传递给鉴别器,从而重用学习到的权重。 这方面的一个重要方面是我们如何选择鉴别函数的损失函数,该函数是两个交叉熵的和:一个用于实字符,其中所有真实 MNIST 字符的标号都设置为 1,一个用于遗忘字符,其中所有伪造的字符的标签设置为零。 鉴别器和生成器以交替顺序运行 100,000 步。 每 500 步,会从学习到的分布中抽取一个样本,以打印该生成器到目前为止所学的内容。 这就是定义新周期的条件,结果将在下一部分中显示。 训练函数代码段报告如下def train(logdir, batch_size):
from model_conv import discriminator, generator
mnist = read_data()
with tf.variable_scope('placeholder'):
# Raw image
X = tf.placeholder(tf.float32, [None, 784])
tf.summary.image('raw image', tf.reshape(X, [-1, 28, 28, 1]), 3)
# Noise
z = tf.placeholder(tf.float32, [None, 100]) # noise
tf.summary.histogram('Noise', z)
with tf.variable_scope('GAN'):
G = generator(z, batch_size)
D_real, D_real_logits = discriminator(X, reuse=False)
D_fake, D_fake_logits = discriminator(G, reuse=True)
tf.summary.image('generated image', tf.reshape(G, [-1, 28, 28, 1]), 3)
with tf.variable_scope('Prediction'):
tf.summary.histogram('real', D_real)
tf.summary.histogram('fake', D_fake)
with tf.variable_scope('D_loss'):
d_loss_real = tf.reduce_mean(
tf.nn.sigmoid_cross_entropy_with_logits(
logits=D_real_logits, labels=tf.ones_like(D_real_logits)))
d_loss_fake = tf.reduce_mean(
tf.nn.sigmoid_cross_entropy_with_logits(
logits=D_fake_logits, labels=tf.zeros_like(D_fake_logits)))
d_loss = d_loss_real + d_loss_fake
tf.summary.scalar('d_loss_real', d_loss_real)
tf.summary.scalar('d_loss_fake', d_loss_fake)
tf.summary.scalar('d_loss', d_loss)
with tf.name_scope('G_loss'):
g_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits
(logits=D_fake_logits, labels=tf.ones_like(D_fake_logits)))
tf.summary.scalar('g_loss', g_loss)
tvar = tf.trainable_variables()
dvar = [var for var in tvar if 'discriminator' in var.name]
gvar = [var for var in tvar if 'generator' in var.name]
with tf.name_scope('train'):
d_train_step = tf.train.AdamOptimizer().minimize(d_loss, var_list=dvar)
g_train_step = tf.train.AdamOptimizer().minimize(g_loss, var_list=gvar)
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
merged_summary = tf.summary.merge_all()
writer = tf.summary.FileWriter('tmp/'+'gan_conv_'+logdir)
writer.add_graph(sess.graph)
num_img = 0
if not os.path.exists('output/'):
os.makedirs('output/')
for i in range(100000):
batch_X, _ = mnist.train.next_batch(batch_size)
batch_noise = np.random.uniform(-1., 1., [batch_size, 100])
if i % 500 == 0:
samples = sess.run(G, feed_dict={z: np.random.uniform(-1., 1., [64, 100])})
fig = plot(samples)
plt.savefig('output/%s.png' % str(num_img).zfill(3), bbox_inches='tight')
num_img += 1
plt.close(fig)
_, d_loss_print = sess.run([d_train_step, d_loss],
feed_dict={X: batch_X, z: batch_noise})
_, g_loss_print = sess.run([g_train_step, g_loss],
feed_dict={z: batch_noise})
if i % 100 == 0:
s = sess.run(merged_summary, feed_dict={X: batch_X, z: batch_noise})
writer.add_summary(s, i)
print('epoch:%d g_loss:%f d_loss:%f' % (i, g_loss_print, d_loss_print))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Train vanila GAN using convolutional networks')
parser.add_argument('--logdir', type=str, default='1', help='logdir for Tensorboard, give a string')
parser.add_argument('--batch_size', type=int, default=64, help='batch size: give a int')
args = parser.parse_args()
train(logdir=args.logdir, batch_size=args.batch_size)
将 CNN 与 GAN 一起使用可提高学习能力。 让我们看一下不同时期的许多实际示例,以了解机器将如何学习以改善其编写过程。 例如,将以下秘籍中的四次迭代后获得的结果与先前秘籍中的四次迭代后获得的结果进行比较。 你看得到差别吗? 我希望自己可以学习这种艺术!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tX3xoW8y-1681565377465)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/cdd533f8-38c3-47cc-b13b-732c76f9b3f4.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TPZCZf1a-1681565377465)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/f7a2ca78-99d9-4bdd-add9-5edca6c93c43.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GEorEh6Z-1681565377465)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/5e84bb49-e14c-4257-a48f-b8b1d5c37197.png)] |
周期 0 | 周期 2 | 周期 4 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rHdBa2wI-1681565377465)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/5d6e7413-f565-4d12-9fa7-d2a0dfadd338.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4VHLt0id-1681565377465)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/ef066e7c-f6f7-45e9-8887-8e3c6dad0911.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rlyMiUiv-1681565377466)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/fb8753c2-fdfd-428d-95b5-e4561e7bafd3.png)] |
周期 8 | 周期 16 | 周期 32 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WZwAUp5i-1681565377466)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/91c50bd6-ae61-45aa-b8ca-96848c6d6c7c.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YUu39o1P-1681565377466)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/bb7d6347-a6ad-491e-9b3e-e424eb7b95c1.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z2Bl7BJt-1681565377466)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/b6d45453-6794-4503-a35e-51498160dcc2.png)] |
周期 64 | 周期 128 | 周期 200 |
Example of forged MNIST-like with DCGAN
用于伪造 MNIST 图像的相同思想可以应用于其他图像域。 在本秘籍中,您将学习如何使用位于这个链接的包在不同的数据集上训练 DCGAN 模型。 这项工作基于论文《深度卷积生成对抗网络的无监督表示学习》(Alec Radford,Luke Metz,Soumith Chintal,2015 年)。引用摘要:
近年来,通过卷积网络(CNN)进行监督学习已在计算机视觉应用中得到了广泛采用。 相比之下,CNN 的无监督学习受到的关注较少。 在这项工作中,我们希望帮助弥合CNN在有监督学习的成功与无监督学习之间的差距。 我们介绍了一种称为深度卷积生成对抗网络(DCGAN)的 CNN,它们具有一定的架构约束,并证明它们是无监督学习的强大候选者。 在各种图像数据集上进行训练,我们显示出令人信服的证据,即我们深厚的卷积对抗对在生成器和鉴别器中学习了从对象部分到场景的表示层次。 此外,我们将学习到的特征用于新颖的任务-展示了它们作为一般图像表示形式的适用性。
请注意,生成器具有下图所示的架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rH2lWNUw-1681565377467)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/7aa964c6-fb7f-4d74-8578-efcc5b36b500.png)]
请注意,在包装中,相对于原始纸张进行了更改,以避免D
(鉴别器)网络快速收敛,G
(生成器)网络每次D
网络更新都会更新两次。
此秘籍基于这个页面上提供的代码。
我们按以下步骤进行:
git clone https://github.com/carpedm20/DCGAN-tensorflow
python download.py mnist celebA
python main.py --dataset celebA --input_height=108 --train --crop
python main.py --dataset celebA --input_height=108 --crop
$ mkdir data/DATASET_NAME
... add images to data/DATASET_NAME ...
$ python main.py --dataset DATASET_NAME --train
$ python main.py --dataset DATASET_NAME
$ # example
$ python main.py --dataset=eyes --input_fname_pattern="*_cropped.png" --train
生成器学习如何生成名人的伪造图像,鉴别器学习如何将伪造的图像与真实的图像区分开。 两个网络中的每个周期都在竞争以改善和减少损失。 下表报告了前五个时期:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kQMSvXNh-1681565377467)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/fa0f7ae3-102e-4548-91d4-5c2e65a26a4b.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nrs0g4o4-1681565377467)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/fa448cc9-f71f-4c31-b71c-a221ca64ae88.png)] |
周期 0 | 周期 1 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iSnP1EBy-1681565377467)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/3984c5eb-70b6-4164-8d7e-e663a9496d61.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uHnisSE2-1681565377468)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/565f6241-3041-41c6-b457-0cadcf580ca4.png)] |
周期 2 | 周期 3 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eweDMuHm-1681565377468)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/6da758f7-ce4b-4052-ab3f-5a267c45bfec.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EE6t9r4Y-1681565377468)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/778937bc-080a-41e3-800e-e5aa6c8a144b.png)] |
周期 4 | 周期 5 |
Example of forged celebrities with a DCGAN
内容感知填充是摄影师使用的一种工具,用于填充不需要的或丢失的图像部分。论文《具有感知和上下文损失的语义图像修复》使用 DCGAN 进行图像补全,并学习如何填充部分图像。
变分自编码器(VAE)是神经网络和贝叶斯推理两者的最佳结合。 它们是最酷的神经网络,并已成为无监督学习的流行方法之一。 它们是自编码器。 与传统的编码器和自编码器的解码器网络(请参阅第 8 章“自编码器”)一起,它们还具有其他随机层。 编码器网络之后的随机层使用高斯分布对数据进行采样,解码器网络之后的随机层使用伯努利分布对数据进行采样。 像 GAN 一样,可以使用变分自编码器根据经过训练的分布来生成图像和图形。 VAE 允许人们设置潜在的复杂先验,从而学习强大的潜在表示。
下图描述了 VAE。 编码器网络qᵩ(z | x)
逼近真实但棘手的后验分布p(z | x)
,其中x
是 VAE 的输入,z
是潜在表示。 解码器网络p[ϴ](x | z)
将d
维潜在变量(也称为潜在空间)作为其输入,并且分布与P(x)
相同。 从z | x ~ N(μ[z|x], Σ[z|x])
中采样潜在表示z
,解码器网络的输出从x | z ~ N(μ[x|z], Σ[x|z])
中采样x | z
:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-od5zL3w9-1681565377468)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/cc5ebb49-22fd-4e57-9992-6643c79fee21.png)]
自动编码器的编码器-解码器示例。
既然我们已经掌握了 VAE 的基本架构,那么就出现了一个问题,即如何对它们进行训练,因为训练数据的最大可能性和后验密度是很难解决的? 通过最大化日志数据可能性的下限来训练网络。 因此,损耗项包括两个部分:生成损耗,它是通过解码器网络通过采样获得的;以及 KL 发散项,也称为潜在损耗。
生成损失确保解码器生成的图像和用于训练网络的图像相同,而潜在损失确保后验分布qᵩ(z | x)
接近先前的p[ϴ](z)
。 由于编码器使用高斯分布进行采样,因此潜在损失可以衡量潜在变量与单位高斯的匹配程度。
对 VAE 进行训练后,我们只能使用解码器网络来生成新图像。
此处的代码基于 Kingma 和 Welling 的论文自动编码变分贝叶斯,并改编自 GitHub。
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
%matplotlib inline
VariationalAutoencoder
类。 class __init__ method
定义了超参数,例如学习率,批量大小,输入的占位符以及编码器和解码器网络的权重和偏差变量。 它还根据 VAE 的网络架构构建计算图。 在此秘籍中,我们使用 Xavier 初始化来初始化权重。 我们没有定义自己的 Xavier 初始化方法,而是使用tf.contrib.layers.xavier_initializer()
TensorFlow 来完成任务。 最后,我们定义损失(生成和潜在)和优化器操作:class VariationalAutoencoder(object):
def __init__(self, network_architecture, transfer_fct=tf.nn.softplus,
learning_rate=0.001, batch_size=100):
self.network_architecture = network_architecture
self.transfer_fct = transfer_fct
self.learning_rate = learning_rate
self.batch_size = batch_size
# Place holder for the input
self.x = tf.placeholder(tf.float32, [None, network_architecture["n_input"]])
# Define weights and biases
network_weights = self._initialize_weights(**self.network_architecture)
# Create autoencoder network
# Use Encoder Network to determine mean and
# (log) variance of Gaussian distribution in latent
# space
self.z_mean, self.z_log_sigma_sq = \
self._encoder_network(network_weights["weights_encoder"],
network_weights["biases_encoder"])
# Draw one sample z from Gaussian distribution
n_z = self.network_architecture["n_z"]
eps = tf.random_normal((self.batch_size, n_z), 0, 1, dtype=tf.float32)
# z = mu + sigma*epsilon
self.z = tf.add(self.z_mean,tf.multiply(tf.sqrt(tf.exp(self.z_log_sigma_sq)), eps))
# Use Decoder network to determine mean of
# Bernoulli distribution of reconstructed input
self.x_reconstr_mean = \
self._decoder_network(network_weights["weights_decoder"],
network_weights["biases_decoder"])
# Define loss function based variational upper-bound and
# corresponding optimizer
# define generation loss
generation_loss = \
-tf.reduce_sum(self.x * tf.log(1e-10 + self.x_reconstr_mean)
+ (1-self.x) * tf.log(1e-10 + 1 - self.x_reconstr_mean), 1)
latent_loss = -0.5 * tf.reduce_sum(1 + self.z_log_sigma_sq
- tf.square(self.z_mean)- tf.exp(self.z_log_sigma_sq), 1)
self.cost = tf.reduce_mean(generation_loss + latent_loss) # average over batch
# Define the optimizer
self.optimizer = \
tf.train.AdamOptimizer(learning_rate=self.learning_rate).minimize(self.cost)
# Initializing the tensor flow variables
init = tf.global_variables_initializer()
# Launch the session
self.sess = tf.InteractiveSession()
self.sess.run(init)
def _initialize_weights(self, n_hidden_recog_1, n_hidden_recog_2,
n_hidden_gener_1, n_hidden_gener_2,
n_input, n_z):
initializer = tf.contrib.layers.xavier_initializer()
all_weights = dict()
all_weights['weights_encoder'] = {
'h1': tf.Variable(initializer(shape=(n_input, n_hidden_recog_1))),
'h2': tf.Variable(initializer(shape=(n_hidden_recog_1, n_hidden_recog_2))),
'out_mean': tf.Variable(initializer(shape=(n_hidden_recog_2, n_z))),
'out_log_sigma': tf.Variable(initializer(shape=(n_hidden_recog_2, n_z)))}
all_weights['biases_encoder'] = {
'b1': tf.Variable(tf.zeros([n_hidden_recog_1], dtype=tf.float32)),
'b2': tf.Variable(tf.zeros([n_hidden_recog_2], dtype=tf.float32)),
'out_mean': tf.Variable(tf.zeros([n_z], dtype=tf.float32)),
'out_log_sigma': tf.Variable(tf.zeros([n_z], dtype=tf.float32))}
all_weights['weights_decoder'] = {
'h1': tf.Variable(initializer(shape=(n_z, n_hidden_gener_1))),
'h2': tf.Variable(initializer(shape=(n_hidden_gener_1, n_hidden_gener_2))),
'out_mean': tf.Variable(initializer(shape=(n_hidden_gener_2, n_input))),
'out_log_sigma': tf.Variable(initializer(shape=(n_hidden_gener_2, n_input)))}
all_weights['biases_decoder'] = {
'b1': tf.Variable(tf.zeros([n_hidden_gener_1], dtype=tf.float32)),
'b2': tf.Variable(tf.zeros([n_hidden_gener_2], dtype=tf.float32)),'out_mean': tf.Variable(tf.zeros([n_input], dtype=tf.float32)),
'out_log_sigma': tf.Variable(tf.zeros([n_input], dtype=tf.float32))}
return all_weights
def _encoder_network(self, weights, biases):
# Generate probabilistic encoder (recognition network), which
# maps inputs onto a normal distribution in latent space.
# The transformation is parametrized and can be learned.
layer_1 = self.transfer_fct(tf.add(tf.matmul(self.x, weights['h1']),
biases['b1']))
layer_2 = self.transfer_fct(tf.add(tf.matmul(layer_1, weights['h2']),
biases['b2']))
z_mean = tf.add(tf.matmul(layer_2, weights['out_mean']),
biases['out_mean'])
z_log_sigma_sq = \
tf.add(tf.matmul(layer_2, weights['out_log_sigma']),
biases['out_log_sigma'])
return (z_mean, z_log_sigma_sq)
def _decoder_network(self, weights, biases):
# Generate probabilistic decoder (decoder network), which
# maps points in latent space onto a Bernoulli distribution in data space.
# The transformation is parametrized and can be learned.
layer_1 = self.transfer_fct(tf.add(tf.matmul(self.z, weights['h1']),
biases['b1']))
layer_2 = self.transfer_fct(tf.add(tf.matmul(layer_1, weights['h2']),
biases['b2']))
x_reconstr_mean = \
tf.nn.sigmoid(tf.add(tf.matmul(layer_2, weights['out_mean']),
biases['out_mean']))
return x_reconstr_mean
VariationalAutoencoder
类还包含一些辅助函数,用于生成和重建数据并适合 VAE:def fit(self, X):
opt, cost = self.sess.run((self.optimizer, self.cost),
feed_dict={self.x: X})
return cost
def generate(self, z_mu=None):
""" Generate data by sampling from latent space.
If z_mu is not None, data for this point in latent space is
generated. Otherwise, z_mu is drawn from prior in latent
space.
"""
if z_mu is None:
z_mu = np.random.normal(size=self.network_architecture["n_z"])
# Note: This maps to mean of distribution, we could alternatively
# sample from Gaussian distribution
return self.sess.run(self.x_reconstr_mean,
feed_dict={self.z: z_mu})
def reconstruct(self, X):
""" Use VAE to reconstruct given data. """
return self.sess.run(self.x_reconstr_mean,
feed_dict={self.x: X})
def train(network_architecture, learning_rate=0.001,
batch_size=100, training_epochs=10, display_step=5):
vae = VariationalAutoencoder(network_architecture,
learning_rate=learning_rate,
batch_size=batch_size)
# Training cycle
for epoch in range(training_epochs):
avg_cost = 0.
total_batch = int(n_samples / batch_size)
# Loop over all batches
for i in range(total_batch):
batch_xs, _ = mnist.train.next_batch(batch_size)
# Fit training using batch data
cost = vae.fit(batch_xs)
# Compute average loss
avg_cost += cost / n_samples * batch_size
# Display logs per epoch step
if epoch % display_step == 0:
print("Epoch:", '%04d' % (epoch+1),
"cost=", "{:.9f}".format(avg_cost))
return vae
# Load MNIST data in a format suited for tensorflow.
# The script input_data is available under this URL:
#https://raw.githubusercontent.com/tensorflow/tensorflow/master/tensorflow/examples/tutorials/mnist/input_data.py
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
n_samples = mnist.train.num_examples
network_architecture = \
dict(n_hidden_recog_1=500, # 1st layer encoder neurons
n_hidden_recog_2=500, # 2nd layer encoder neurons
n_hidden_gener_1=500, # 1st layer decoder neurons
n_hidden_gener_2=500, # 2nd layer decoder neurons
n_input=784, # MNIST data input (img shape: 28*28)
n_z=2) # dimensionality of latent space
vae = train(network_architecture, training_epochs=75)
x_sample = mnist.test.next_batch(100)[0]
x_reconstruct = vae.reconstruct(x_sample)
plt.figure(figsize=(8, 12))
for i in range(5):
plt.subplot(5, 2, 2*i + 1)
plt.imshow(x_sample[i].reshape(28, 28), vmin=0, vmax=1, cmap="gray")
plt.title("Test input")
plt.colorbar()
plt.subplot(5, 2, 2*i + 2)
plt.imshow(x_reconstruct[i].reshape(28, 28), vmin=0, vmax=1, cmap="gray")
plt.title("Reconstruction")
plt.colorbar()
plt.tight_layout()
以下是上述代码的输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h3n2Eksf-1681565377469)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/c31805df-525c-4d4d-aa6f-4e8f3b2d2037.png)]
MNIST 重建字符的示例
nx = ny = 20
x_values = np.linspace(-3, 3, nx)
y_values = np.linspace(-3, 3, ny)
canvas = np.empty((28*ny, 28*nx))
for i, yi in enumerate(x_values):
for j, xi in enumerate(y_values):
z_mu = np.array([[xi, yi]]*vae.batch_size)
x_mean = vae.generate(z_mu)
canvas[(nx-i-1)*28:(nx-i)*28, j*28:(j+1)*28] = x_mean[0].reshape(28, 28)
plt.figure(figsize=(8, 10))
Xi, Yi = np.meshgrid(x_values, y_values)
plt.imshow(canvas, origin="upper", cmap="gray")
plt.tight_layout()
以下是自编码器生成的 MNIST 类字符的范围:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8dRJKnJc-1681565377469)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/a797224e-349b-4e69-864c-5beae8fe0092.png)]
由自动编码器生成的一系列 MNIST 字符
VAE 学会重建并同时生成新图像。 生成的图像取决于潜在空间。 生成的图像与训练的数据集具有相同的分布。
我们还可以通过在VariationalAutoencoder
类中定义一个转换函数来查看潜在空间中的数据:
def transform(self, X):
"""Transform data by mapping it into the latent space."""
# Note: This maps to mean of distribution, we could alternatively sample from Gaussian distribution
return self.sess.run(self.z_mean, feed_dict={self.x: X})
使用转换函数的 MNIST 数据集的潜在表示如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s5Zn0ESU-1681565377469)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/2bb795f1-19cc-4f46-8c1f-d896d3b0e01c.png)]
VAE 的生成图像取决于潜在空间尺寸。 模糊减少了潜在空间的尺寸,增加了。 分别针对 5 维,10 维和 20 维潜在维度的重构图像如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e04s6cH6-1681565377469)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/7619532a-5c71-4c3f-9a89-4093fede393a.png)]
Kingma 和 Welling 的论文是该领域的开创性论文。 他们会经历完整的架构思维过程以及优雅的数学运算。 对于对 VAE 感兴趣的任何人,必须阅读。
另一个有趣的读物是 Carl Doersch 的论文,变分编码器教程。
Github 链接包含 VAE 的另一种实现,以及来自 Kingma 和 Welling 论文的图像再现。
胶囊网络(或 CapsNets)是一种非常新颖的深度学习网络。 这项技术是在 2017 年 10 月底由 Sara Sabour,Nicholas Frost 和 Geoffrey Hinton 发表的名为《胶囊之间的动态路由》的开创性论文中引入的。 欣顿(Hinton)是深度学习之父之一,因此,整个深度学习社区很高兴看到胶囊技术取得的进步。 确实,CapsNets 已经在 MNIST 分类中击败了最好的 CNN,这真是……令人印象深刻!
那么 CNN 有什么问题? 在 CNN 中,每一层都会以渐进的粒度理解图像。 正如我们在多种秘籍中讨论的那样,第一层将最有可能识别直线或简单的曲线和边缘,而随后的层将开始理解更复杂的形状(例如矩形)和复杂的形式(例如人脸)。
现在,用于 CNN 的一项关键操作是池化。 池化旨在创建位置不变性,通常在每个 CNN 层之后使用它来使任何问题在计算上易于处理。 但是,合并会带来一个严重的问题,因为它迫使我们丢失所有位置数据。 不是很好。 考虑一下脸:它由两只眼睛,一张嘴和一只鼻子组成,重要的是这些部分之间存在空间关系(嘴在鼻子下方,通常在眼睛下方)。 确实,欣顿说:
卷积神经网络中使用的池化操作是一个很大的错误,它运行良好的事实是一场灾难。
从技术上讲,我们不需要位置不变。 相反,我们需要等方差。 等方差是一个奇特的术语,表示我们想了解图像中的旋转或比例变化,并且我们要相应地调整网络。 这样,图像中不同成分的空间定位不会丢失。
那么胶囊网络有什么新功能? 据作者说,我们的大脑有称为胶囊的模块,每个胶囊专门处理特定类型的信息。 尤其是,有些胶囊对于理解位置的概念,尺寸的概念,方向的概念,变形的概念,纹理等非常有用。 除此之外,这组作者还建议我们的大脑具有特别有效的机制,可以将每条信息动态路由到胶囊,这被认为最适合处理特定类型的信息。
因此,CNN 和 CapsNets 之间的主要区别在于,使用 CNN 时,您会不断添加用于创建深度网络的层,而使用 CapsNet 时,您会在另一个内部嵌套神经层。 胶囊是一组神经元,可在网络中引入更多结构。 它产生一个向量来表示图像中实体的存在。 尤其是,欣顿使用活动向量的长度来表示实体存在的概率,并使用其方向来表示实例化参数。 当多个预测结果一致时,更高级别的胶囊就会生效。 对于每个可能的父项,胶囊产生一个额外的预测向量。
现在有了第二项创新:我们将使用跨胶囊的动态路由,并且不再使用池化的原始思想。 较低级别的容器倾向于将其输出发送到较高级别的容器,并且活动向量的标量积很大,而预测来自较低级别的容器。 标量预测向量乘积最大的亲本会增加胶囊键。 所有其他父项都减少了联系。 换句话说,这种想法是,如果较高级别的胶囊同意较低级别的胶囊,则它将要求发送更多该类型的信息。 如果没有协议,它将要求发送更少的协议。 使用协定方法的这种动态路由优于当前的机制(例如最大池),并且根据 Hinton 的说法,路由最终是解析图像的一种方法。 实际上,最大池化忽略了除最大值以外的任何东西,而动态路由根据较低层和较高层之间的协议选择性地传播信息。
第三个差异是引入了新的非线性激活函数。 CapsNet 并未像在 CNN 中那样向每个层添加挤压函数,而是向嵌套的一组层添加了挤压函数。 下图表示了非线性激活函数,它被称为挤压函数(方程式 1):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7b8K4Bo5-1681565377469)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/8458f4ff-3e07-4dc7-999c-f7c2a45ba811.png)]
如欣顿的开创性论文中所示的压缩函数
此外,Hinton 等人表明,经过判别训练的多层胶囊系统在 MNIST 上达到了最先进的表现,并且在识别高度重叠的数字方面比卷积网络要好得多。
论文《胶囊之间的动态路由》向我们展示了简单的 CapsNet 架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7AD6UN2i-1681565377470)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/606af41e-dc21-4840-8303-ecbd138d170c.png)]
简单的 CapsNet 架构
该架构很浅,只有两个卷积层和一个完全连接的层。 Conv1 具有 256 个9×9
卷积核,步幅为 1,并具有 ReLU 激活函数。 该层的作用是将像素强度转换为局部特征检测器的活动,然后将其用作主胶囊的输入。 PrimaryCapsules 是具有 32 个通道的卷积胶囊层。 每个主胶囊包含 8 个卷积单元,其内核为9×9
,步幅为 2。 总计,PrimaryCapsules 具有[32, 6, 6]
胶囊输出(每个输出是 8D 向量),并且[6, 6]
网格中的每个胶囊彼此共享权重。 最后一层(DigitCaps)每位数字类具有一个 16D 胶囊,这些胶囊中的每个胶囊都接收来自下一层中所有其他胶囊的输入。 路由仅发生在两个连续的胶囊层之间(例如 PrimaryCapsules 和 DigitCaps)。
这是我们如何进行秘籍的方法:
git clone https://github.com/naturomics/CapsNet-Tensorflow.git
$ cd CapsNet-Tensorflow
mkdir -p data/mnist
wget -c -P data/mnist \\
http://yann.lecun.com/exdb/mnist/{train-images-idx3-ubyte.gz,train-labels-idx1-ubyte.gz,t10k-images-idx3-ubyte.gz,t10k-labels-idx1-ubyte.gz}
gunzip data/mnist/*.gz
python main.py
# capsLayer.py
#
import numpy as np
import tensorflow as tf
from config import cfg
epsilon = 1e-9
class CapsLayer(object):
''' Capsule layer.
Args:
input: A 4-D tensor.
num_outputs: the number of capsule in this layer.
vec_len: integer, the length of the output vector of a capsule.
layer_type: string, one of 'FC' or "CONV", the type of this layer,
fully connected or convolution, for the future expansion capability
with_routing: boolean, this capsule is routing with the
lower-level layer capsule.
Returns:
A 4-D tensor.
'''
def __init__(self, num_outputs, vec_len, with_routing=True, layer_type='FC'):
self.num_outputs = num_outputs
self.vec_len = vec_len
self.with_routing = with_routing
self.layer_type = layer_type
def __call__(self, input, kernel_size=None, stride=None):
'''
The parameters 'kernel_size' and 'stride' will be used while 'layer_type' equal 'CONV'
'''
if self.layer_type == 'CONV':
self.kernel_size = kernel_size
self.stride = stride
if not self.with_routing:
# the PrimaryCaps layer, a convolutional layer
# input: [batch_size, 20, 20, 256]
assert input.get_shape() == [cfg.batch_size, 20, 20, 256]
capsules = []
for i in range(self.vec_len):
# each capsule i: [batch_size, 6, 6, 32]
with tf.variable_scope('ConvUnit_' + str(i)):
caps_i = tf.contrib.layers.conv2d(input, self.num_outputs,
self.kernel_size, self.stride,
padding="VALID")
caps_i = tf.reshape(caps_i, shape=(cfg.batch_size, -1, 1, 1))
capsules.append(caps_i)
assert capsules[0].get_shape() == [cfg.batch_size, 1152, 1, 1]
# [batch_size, 1152, 8, 1]
capsules = tf.concat(capsules, axis=2)
capsules = squash(capsules)
assert capsules.get_shape() == [cfg.batch_size, 1152, 8, 1]
return(capsules)
if self.layer_type == 'FC':
if self.with_routing:
# the DigitCaps layer, a fully connected layer
# Reshape the input into [batch_size, 1152, 1, 8, 1]
self.input = tf.reshape(input, shape=(cfg.batch_size, -1, 1, input.shape[-2].value, 1))
with tf.variable_scope('routing'):
# b_IJ: [1, num_caps_l, num_caps_l_plus_1, 1, 1]
b_IJ = tf.constant(np.zeros([1, input.shape[1].value, self.num_outputs, 1, 1], dtype=np.float32))
capsules = routing(self.input, b_IJ)
capsules = tf.squeeze(capsules, axis=1)
return(capsules)
j
的当前输出v[j]
,和胶囊i
得出的预测u_hat[j|i]
的标量乘积,即可计算出一致性:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K8fXMYOY-1681565377470)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/3e855c22-8c24-4f2c-a2c2-2ec2abe382ae.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NTSWqzIX-1681565377470)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/ba247207-8cba-4016-9519-5623712ac87e.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UhBarhnz-1681565377470)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/ae5aab27-b9ef-49d7-aa4f-6de8cbd86bfa.png)]
以下方法实现了前面图像中过程 1 中描述的步骤。 注意,输入是来自l
层中 1,152 个胶囊的 4D 张量。 输出是形状为[batch_size, 1, length(v_j)=16, 1]
的张量,表示层l + 1
中胶囊j
的向量输出v[j]
:
def routing(input, b_IJ):
''' The routing algorithm.
Args:
input: A Tensor with [batch_size, num_caps_l=1152, 1, length(u_i)=8, 1]
shape, num_caps_l meaning the number of capsule in the layer l.
Returns:
A Tensor of shape [batch_size, num_caps_l_plus_1, length(v_j)=16, 1]
representing the vector output `v_j` in the layer l+1
Notes:
u_i represents the vector output of capsule i in the layer l, and
v_j the vector output of capsule j in the layer l+1.
'''
# W: [num_caps_j, num_caps_i, len_u_i, len_v_j]
W = tf.get_variable('Weight', shape=(1, 1152, 10, 8, 16), dtype=tf.float32,
initializer=tf.random_normal_initializer(stddev=cfg.stddev))
# Eq.2, calc u_hat
# do tiling for input and W before matmul
# input => [batch_size, 1152, 10, 8, 1]
# W => [batch_size, 1152, 10, 8, 16]
input = tf.tile(input, [1, 1, 10, 1, 1])
W = tf.tile(W, [cfg.batch_size, 1, 1, 1, 1])
assert input.get_shape() == [cfg.batch_size, 1152, 10, 8, 1]
# in last 2 dims:
# [8, 16].T x [8, 1] => [16, 1] => [batch_size, 1152, 10, 16, 1]
u_hat = tf.matmul(W, input, transpose_a=True)
assert u_hat.get_shape() == [cfg.batch_size, 1152, 10, 16, 1]
# line 3,for r iterations do
for r_iter in range(cfg.iter_routing):
with tf.variable_scope('iter_' + str(r_iter)):
# line 4:
# => [1, 1152, 10, 1, 1]
c_IJ = tf.nn.softmax(b_IJ, dim=2)
c_IJ = tf.tile(c_IJ, [cfg.batch_size, 1, 1, 1, 1])
assert c_IJ.get_shape() == [cfg.batch_size, 1152, 10, 1, 1]
# line 5:
# weighting u_hat with c_IJ, element-wise in the last two dims
# => [batch_size, 1152, 10, 16, 1]
s_J = tf.multiply(c_IJ, u_hat)
# then sum in the second dim, resulting in [batch_size, 1, 10, 16, 1]
s_J = tf.reduce_sum(s_J, axis=1, keep_dims=True)
assert s_J.get_shape() == [cfg.batch_size, 1, 10, 16, 16
# line 6:
# squash using Eq.1,
v_J = squash(s_J)
assert v_J.get_shape() == [cfg.batch_size, 1, 10, 16, 1]
# line 7:
# reshape & tile v_j from [batch_size ,1, 10, 16, 1] to [batch_size, 10, 1152, 16, 1]
# then matmul in the last tow dim: [16, 1].T x [16, 1] => [1, 1], reduce mean in the
# batch_size dim, resulting in [1, 1152, 10, 1, 1]
v_J_tiled = tf.tile(v_J, [1, 1152, 1, 1, 1])
u_produce_v = tf.matmul(u_hat, v_J_tiled, transpose_a=True)
assert u_produce_v.get_shape() == [cfg.batch_size, 1152, 10, 1, 1]
b_IJ += tf.reduce_sum(u_produce_v, axis=0, keep_dims=True)
return(v_J)
[batch_size, num_caps, vec_len, 1]
形状的 4D 向量,输出是具有与向量相同形状但被压缩在第三维和第四维中的 4-D 张量。 给定一个向量输入,目标是计算公式 1 中表示的值,如下所示:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R9iNjHMu-1681565377471)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/5f34ce65-7b14-49ba-8306-4363f511eeba.png)]
def squash(vector):
'''Squashing function corresponding to Eq. 1
Args:
vector: A 5-D tensor with shape [batch_size, 1, num_caps, vec_len, 1],
Returns:
A 5-D tensor with the same shape as vector but squashed in 4rd and 5th dimensions.
'''
vec_squared_norm = tf.reduce_sum(tf.square(vector), -2, keep_dims=True)
scalar_factor = vec_squared_norm / (1 + vec_squared_norm) / tf.sqrt(vec_squared_norm + epsilon)
vec_squashed = scalar_factor * vector # element-wise
return(vec_squashed)
build_arch(...)
定义了 CapsNet,如下图所示:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3EfA0bAx-1681565377471)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/23a91cd5-8c50-4e92-82bd-7af58ec34e02.png)]
请注意,本文将重构技术描述为一种正则化方法。 从本文:
我们使用额外的重建损失来鼓励数字囊对输入数字的实例化参数进行编码。 在训练过程中,我们会掩盖除正确数字胶囊外的所有活动向量。
然后,我们使用此活动向量进行重构。
数字胶囊的输出被馈送到解码器,该解码器由三个完全连接的层组成,这些层对像素强度进行建模,如图 2 所示。我们将逻辑单元的输出与像素强度之间的平方差之和最小化。 我们将这种重建损失降低了 0.0005,以使其在训练过程中不会控制保证金损失。 如下实现的方法build_arch(..)
也用于创建解码器:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-POhgRqwc-1681565377471)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/10c4db98-f00c-4813-8452-4fcc86fc5d61.png)]
#capsNet.py
#
import tensorflow as tf
from config import cfg
from utils import get_batch_data
from capsLayer import CapsLayer
epsilon = 1e-9
class CapsNet(object):
def __init__(self, is_training=True):
self.graph = tf.Graph()
with self.graph.as_default():
if is_training:
self.X, self.labels = get_batch_data()
self.Y = tf.one_hot(self.labels, depth=10, axis=1, dtype=tf.float32)
self.build_arch()
self.loss()
self._summary()
# t_vars = tf.trainable_variables()
self.global_step = tf.Variable(0, name='global_step', trainable=False)
self.optimizer = tf.train.AdamOptimizer()
self.train_op = self.optimizer.minimize(self.total_loss, global_step=self.global_step) # var_list=t_vars)
elif cfg.mask_with_y:
self.X = tf.placeholder(tf.float32,
shape=(cfg.batch_size, 28, 28, 1))
self.Y = tf.placeholder(tf.float32, shape=(cfg.batch_size, 10, 1))
self.build_arch()
else:
self.X = tf.placeholder(tf.float32,
shape=(cfg.batch_size, 28, 28, 1))
self.build_arch()
tf.logging.info('Setting up the main structure')
def build_arch(self):
with tf.variable_scope('Conv1_layer'):
# Conv1, [batch_size, 20, 20, 256]
conv1 = tf.contrib.layers.conv2d(self.X, num_outputs=256,
kernel_size=9, stride=1,
padding='VALID')
assert conv1.get_shape() == [cfg.batch_size, 20, 20, 256]# Primary Capsules layer, return [batch_size, 1152, 8, 1]
with tf.variable_scope('PrimaryCaps_layer'):
primaryCaps = CapsLayer(num_outputs=32, vec_len=8, with_routing=False, layer_type='CONV')
caps1 = primaryCaps(conv1, kernel_size=9, stride=2)
assert caps1.get_shape() == [cfg.batch_size, 1152, 8, 1]
# DigitCaps layer, return [batch_size, 10, 16, 1]
with tf.variable_scope('DigitCaps_layer'):
digitCaps = CapsLayer(num_outputs=10, vec_len=16, with_routing=True, layer_type='FC')
self.caps2 = digitCaps(caps1)
# Decoder structure in Fig. 2
# 1\. Do masking, how:
with tf.variable_scope('Masking'):
# a). calc ||v_c||, then do softmax(||v_c||)
# [batch_size, 10, 16, 1] => [batch_size, 10, 1, 1]
self.v_length = tf.sqrt(tf.reduce_sum(tf.square(self.caps2),
axis=2, keep_dims=True) + epsilon)
self.softmax_v = tf.nn.softmax(self.v_length, dim=1)
assert self.softmax_v.get_shape() == [cfg.batch_size, 10, 1, 1]
# b). pick out the index of max softmax val of the 10 caps
# [batch_size, 10, 1, 1] => [batch_size] (index)
self.argmax_idx = tf.to_int32(tf.argmax(self.softmax_v, axis=1))
assert self.argmax_idx.get_shape() == [cfg.batch_size, 1, 1]
self.argmax_idx = tf.reshape(self.argmax_idx, shape=(cfg.batch_size, )) .
# Method 1.
if not cfg.mask_with_y:
# c). indexing
# It's not easy to understand the indexing process with argmax_idx
# as we are 3-dim animal
masked_v = []
for batch_size in range(cfg.batch_size):
v = self.caps2[batch_size][self.argmax_idx[batch_size], :]
masked_v.append(tf.reshape(v, shape=(1, 1, 16, 1)))
self.masked_v = tf.concat(masked_v, axis=0)
assert self.masked_v.get_shape() == [cfg.batch_size, 1, 16, 1]
# Method 2\. masking with true label, default mode
else:
self.masked_v = tf.matmul(tf.squeeze(self.caps2), tf.reshape(self.Y, (-1, 10, 1)), transpose_a=True)
self.v_length = tf.sqrt(tf.reduce_sum(tf.square(self.caps2), axis=2, keep_dims=True) + epsilon)
# 2\. Reconstruct the MNIST images with 3 FC layers
# [batch_size, 1, 16, 1] => [batch_size, 16] => [batch_size, 512]
with tf.variable_scope('Decoder'):
vector_j = tf.reshape(self.masked_v, shape=(cfg.batch_size, -1))
fc1 = tf.contrib.layers.fully_connected(vector_j, num_outputs=512)
assert fc1.get_shape() == [cfg.batch_size, 512]
fc2 = tf.contrib.layers.fully_connected(fc1, num_outputs=1024)
assert fc2.get_shape() == [cfg.batch_size, 1024]
self.decoded = tf.contrib.layers.fully_connected(fc2, num_outputs=784, activation_fn=tf.sigmoid)
loss(..)
方法中实现,该方法包括三个损失,即边际损失,重建损失和总损失:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TgYDAQjf-1681565377472)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/20736fea-ef6c-43ae-9106-602a15f34251.png)]
def loss(self):
# 1\. The margin loss
# [batch_size, 10, 1, 1]
# max_l = max(0, m_plus-||v_c||)^2
max_l = tf.square(tf.maximum(0., cfg.m_plus - self.v_length))
# max_r = max(0, ||v_c||-m_minus)^2
max_r = tf.square(tf.maximum(0., self.v_length - cfg.m_minus))
assert max_l.get_shape() == [cfg.batch_size, 10, 1, 1]
# reshape: [batch_size, 10, 1, 1] => [batch_size, 10]
max_l = tf.reshape(max_l, shape=(cfg.batch_size, -1))
max_r = tf.reshape(max_r, shape=(cfg.batch_size, -1))
# calc T_c: [batch_size, 10]
T_c = self.Y
# [batch_size, 10], element-wise multiply
L_c = T_c * max_l + cfg.lambda_val * (1 - T_c) * max_r
self.margin_loss = tf.reduce_mean(tf.reduce_sum(L_c, axis=1))
# 2\. The reconstruction loss
orgin = tf.reshape(self.X, shape=(cfg.batch_size, -1))
squared = tf.square(self.decoded - orgin)
self.reconstruction_err = tf.reduce_mean(squared)
# 3\. Total loss
# The paper uses sum of squared error as reconstruction error, but we
# have used reduce_mean in `# 2 The reconstruction loss` to calculate
# mean squared error. In order to keep in line with the paper,the
# regularization scale should be 0.0005*784=0.392
self.total_loss = self.margin_loss + cfg.regularization_scale * self.reconstruction_err
a _summary(...)
方法来报告损失和准确率可能会很方便:#Summary
def _summary(self):
train_summary = []
train_summary.append(tf.summary.scalar('train/margin_loss', self.margin_loss))train_summary.append(tf.summary.scalar('train/reconstruction_loss', self.reconstruction_err))
train_summary.append(tf.summary.scalar('train/total_loss', self.total_loss))
recon_img = tf.reshape(self.decoded, shape=(cfg.batch_size, 28, 28, 1))
train_summary.append(tf.summary.image('reconstruction_img', recon_img))
correct_prediction = tf.equal(tf.to_int32(self.labels), self.argmax_idx)
self.batch_accuracy = tf.reduce_sum(tf.cast(correct_prediction, tf.float32))
self.test_acc = tf.placeholder_with_default(tf.constant(0.), shape=[])
test_summary = []
test_summary.append(tf.summary.scalar('test/accuracy', self.test_acc))
self.train_summary = tf.summary.merge(train_summary)
self.test_summary = tf.summary.merge(test_summary)
CapsNet 与最先进的深度学习网络有很大的不同。 CapsNet 并没有添加更多的层并使网络更深,而是使用了浅层网络,其中,胶囊层嵌套在其他层内。 每个胶囊专门用于检测图像中的特定实体,并且使用动态路由机制将检测到的实体发送给父层。 使用 CNN,您必须从许多不同角度考虑成千上万张图像,以便从不同角度识别物体。 Hinton 认为,这些层中的冗余将使胶囊网络能够从多个角度和在不同情况下以 CNN 通常使用的较少数据识别对象。 让我们检查一下 tensorboad 所示的网络:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-24MhxpOb-1681565377472)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/23569842-8cea-4cde-a5ed-39ed16b08d7a.png)]
代码中定义并由 tensorboard 显示的 CapsNet 示例
如下图所示,其结果令人印象深刻。 CapsNet 在以前仅在更深层的网络中才能实现的三层网络上具有较低的测试误差(0.25%)。 基线是具有256, 256-128
个通道的三个卷积层的标准 CNN。 每个都有5 x 5
个内核,步幅为 1。最后一个卷积层后面是两个大小为328, 192
的完全连接的层。 最后一个完全连接的层通过压降连接到具有交叉熵损失的 10 类 softmax 层:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RVcIWBVW-1681565377472)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/19bafff9-7a12-40a4-91ef-68a4f2d51577.png)]
让我们检查保证金损失,重建损失和总损失的减少:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eWqEHHmB-1681565377472)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/2917c003-d3e1-46f7-891a-b4e15197ee89.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g0KttDgF-1681565377473)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/467c0b72-d272-43f9-a763-84f088317f09.png)]
我们还要检查准确率的提高; 经过 500 次迭代,它在 3500 次迭代中分别达到 92% 和 98.46% :
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pv2aup03-1681565377474)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/6007750c-8a79-4239-ad01-5ebaff4f3986.png)]
迭代 | 精度 |
---|---|
500 | 0.922776442308 |
1000 | 0.959735576923 |
1500 | 0.971955128205 |
2000 | 0.978365384615 |
2500 | 0.981770833333 |
3000 | 0.983473557692 |
3500 | 0.984675480769 |
CapsNet 提高准确率的示例
CapsNets 在 MNIST 上可以很好地工作,但是在理解是否可以在其他数据集(例如 CIFAR)或更通用的图像集合上获得相同的令人印象深刻的结果方面,还有很多研究工作要做。 如果您有兴趣了解更多信息,请查看以下内容:
Keras-CapsNet 是可在这个页面上使用的 Keras 实现。
杰弗里·欣顿(Geoffrey Hinton)讨论了卷积神经网络的问题
在本章中,我们将讨论使用分布式 TensorFlow 和云深度学习。 我们将为您提供一些秘籍:
每个 TensorFlow 计算均以图描述。 这允许在结构和操作的放置方面具有自然的灵活性,可以在计算的分布式节点之间拆分操作。 该图可以拆分为多个子图,这些子图分配给服务器集群中的不同节点。
我强烈建议读者看一看大型分布式深度网络(Jeffrey Dean,Greg S.Corrado,Rajat Monga,Kai Chen,Matthieu Devin,Quoc V.Le,Mark Z.Mao,Marc’Aurelio Ranzato,Andrew Senior ,Paul Tucker,Ke Yang 和 Andrew Y. Ng。 NIPS,2012 年)
本文的一项关键结果是证明可以运行分布式随机梯度下降(SGD),其中多个节点在数据碎片上并行工作,并且通过将更新发送到参数服务器,来实现梯度的独立和异步更新。 引用论文摘要:
我们的实验揭示了关于大规模非凸优化的几个令人惊讶的结果。 首先,异步 SGD 很少用于非凸问题,对于训练深度网络非常有效,特别是与 Adagrad 自适应学习率结合使用时。
下图取自论文本身,很好地说明了这一点:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6FjXrRU4-1681565377474)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/b2700755-61ba-44f4-82aa-a60e2dd7f16c.png)]
您应该阅读的另一份文档是白皮书《TensorFlow:异构分布式系统上的大规模机器学习》(MartínAbadi 等人,2015 年 11 月)
考虑其中包含的一些示例,我们可以在下面的图片的左侧看到 TensorFlow 代码的片段,然后将其表示为右侧的图形:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gf5TBxh4-1681565377474)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/b5cffc2b-9734-4179-abfb-71abe9fa4fa5.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ppNstFO1-1681565377475)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/4eaf774e-bf1e-423f-bd04-bbbe56dfe2d4.png)]
通过进行本地计算并在需要时透明地将远程通信节点添加到图,可以在多个节点之间划分图。 下图中对此作了很好的解释,该图仍取自前面提到的论文:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hOGw2dSf-1681565377475)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/bceaef6d-d560-4a58-9f7e-01797bfa3350.png)]
可以通过集中方式(下图的左侧)或分布式方式(右侧)来计算梯度下降和所有主要的优化器算法。 后者涉及一个主进程,该主进程与调配 GPU 和 CPU 的多个工作器进行对话:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QxTVAfid-1681565377475)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/96de5e4c-3af3-4ba3-9afa-6d1ad513b03c.png)]
摘自以下内容的单机和分布式系统结构示例和分布式 TensorFlow 图计算示例
分布式计算既可以是同步的(所有工作程序都在同时更新分片数据上的梯度),也可以是异步的(更新没有同时发生)。 后者通常可以实现更高的可伸缩性,并且在收敛到最佳解决方案方面,较大的图计算仍然可以很好地工作。 同样,这些图片均来自 TensorFlow 白皮书,如果您想了解更多信息,我强烈建议有兴趣的读者阅读本文:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DZpccZd5-1681565377475)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/9f6bb40e-2159-4bf9-84ab-cddfebc6ae9d.png)]
同步和异步数据并行训练的示例
在本秘籍中,我们将学习如何在 GPU 上使用 TensorFlow:执行的操作是在 CPU 或 GPU 上的简单矩阵乘法。
第一步是安装支持 GPU 的 TensorFlow 版本。 正式的 TensorFlow 安装说明是您的起点。 请记住,您需要一个通过 CUDA 或 CuDNN 支持 GPU 的环境。
我们按以下步骤进行:
import sys
import numpy as np
import tensorflow as tf
from datetime import datetime
"gpu"
或"cpu"
)device_name = sys.argv[1] # Choose device from cmd line. Options: gpu or cpu
shape = (int(sys.argv[2]), int(sys.argv[2]))
if device_name == "gpu":
device_name = "/gpu:0"
else:
device_name = "/cpu:0"
tf.device(device_name)
一起使用。 它创建一个新的上下文管理器,告诉 TensorFlow 在 GPU 或 CPU 上执行这些操作with tf.device(device_name):
random_matrix = tf.random_uniform(shape=shape, minval=0, maxval=1)
dot_operation = tf.matmul(random_matrix, tf.transpose(random_matrix))
sum_operation = tf.reduce_sum(dot_operation)
startTime = datetime.now()
with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as session:
result = session.run(sum_operation)
print(result)
4.打印一些调试时间,只是为了验证 CPU 和 GPU 有什么区别
print("Shape:", shape, "Device:", device_name)
print("Time taken:", datetime.now() - startTime)
本秘籍说明了如何将 TensorFlow 计算分配给 CPU 或 GPU。 该代码非常简单,它将用作下一个秘籍的基础。
我们将展示一个数据并行化的示例,其中数据被拆分到多个 GPU 中
此秘籍的灵感来自尼尔·特内霍尔茨(Neil Tenenholtz)撰写的一篇不错的博客文章,该文章可在线获取
我们按以下步骤进行:
# single GPU (baseline)
import tensorflow as tf
# place the initial data on the cpu
with tf.device('/cpu:0'):
input_data = tf.Variable([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.],
[10., 11., 12.]])
b = tf.Variable([[1.], [1.], [2.]])
# compute the result on the 0th gpu
with tf.device('/gpu:0'):
output = tf.matmul(input_data, b)
# create a session and run
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print sess.run(output)
# in-graph replication
import tensorflow as tf
num_gpus = 2
# place the initial data on the cpu
with tf.device('/cpu:0'):
input_data = tf.Variable([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.],
[10., 11., 12.]])
b = tf.Variable([[1.], [1.], [2.]])
# split the data into chunks for each gpu
inputs = tf.split(input_data, num_gpus)
outputs = []
# loop over available gpus and pass input data
for i in range(num_gpus):
with tf.device('/gpu:'+str(i)):
outputs.append(tf.matmul(inputs[i], b))
# merge the results of the devices
with tf.device('/cpu:0'):
output = tf.concat(outputs, axis=0)
# create a session and run
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print sess.run(output)
这是一个非常简单的方法,其中通过将 CPU 用作主对象并将其分配到两个用作分布式工作程序的 GPU,将图分为两部分。 计算结果被收集回 CPU。
在本秘籍中,我们将学习如何在多个服务器之间分配 TensorFlow 计算。 关键假设是工作程序和参数服务器的代码都相同。 因此,每个计算节点的角色都传递给命令行参数。
同样,此秘籍的灵感来自尼尔·特纳霍尔茨(Neil Tenenholtz)撰写的一篇不错的博客文章,该文章可在线获取
我们按以下步骤进行:
192.168.1.1:1111
上,两个工作服务器分别运行在192.168.1.2:1111
和192.168.1.3:1111
上。import sys
import tensorflow as tf
# specify the cluster's architecture
cluster = tf.train.ClusterSpec({'ps': ['192.168.1.1:1111'],
'worker': ['192.168.1.2:1111',
'192.168.1.3:1111']
})
ps
)。# parse command-line to specify machine
job_type = sys.argv[1] # job type: "worker" or "ps"
task_idx = sys.argv[2] # index job in the worker or ps list
# as defined in the ClusterSpec
ps
)和一个 ID。# create TensorFlow Server. This is how the machines communicate.
server = tf.train.Server(cluster, job_name=job_type, task_index=task_idx)
tf.train.replica_device_setter
,可自动将操作分配给设备。# parameter server is updated by remote clients.
# will not proceed beyond this if statement.
if job_type == 'ps':
server.join()
else:
# workers only
with tf.device(tf.train.replica_device_setter(
worker_device='/job:worker/task:'+task_idx,
cluster=cluster)):
# build your model here as if you only were using a single machine
with tf.Session(server.target):
# train your model here
在本秘籍中,我们已经看到了如何创建具有多个计算节点的集群。 节点既可以扮演参数服务器的角色,也可以扮演工作者的角色。
在这两种情况下,根据从命令行收集的参数,执行的代码相同,但是代码的执行不同。 参数服务器仅需要等待,直到工作器发送更新。 请注意,tf.train.replica_device_setter(..)
的作用是将操作自动分配给可用设备,而tf.train.ClusterSpec(..)
则用于集群设置。
可以在这个页面在线获取针对 MNIST 的分布式训练的示例。
此外,请注意,出于效率考虑,您可以决定拥有多个参数服务器。 使用参数,服务器可以提供更好的网络利用率,并且可以将模型扩展到更多并行计算机。 可以分配多个参数服务器。 有兴趣的读者可以看看这里
此秘籍用于以分布式方式训练完整的 MNIST 分类器。 该秘籍的灵感来自这个页面中的博客文章,此处提供了在 TensorFlow 1.2 上运行的代码
此秘籍基于上一个秘籍。 因此,按顺序阅读它们可能会很方便。
我们按以下步骤进行:
import tensorflow as tf
import sys
import time
# cluster specification
parameter_servers = ["pc-01:2222"]
workers = [ "pc-02:2222",
"pc-03:2222",
"pc-04:2222"]
cluster = tf.train.ClusterSpec({"ps":parameter_servers, "worker":workers})
# input flags
tf.app.flags.DEFINE_string("job_name", "", "Either 'ps' or 'worker'")
tf.app.flags.DEFINE_integer("task_index", 0, "Index of task within the job")FLAGS = tf.app.flags.FLAGS
# start a server for a specific task
server = tf.train.Server(
cluster,
job_name=FLAGS.job_name,
task_index=FLAGS.task_index)
# config
batch_size = 100
learning_rate = 0.0005
training_epochs = 20
logs_path = "/tmp/mnist/1"
# load mnist data set
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
if FLAGS.job_name == "ps":
server.join()
elif FLAGS.job_name == "worker":
# Between-graph replication
with tf.device(tf.train.replica_device_setter(
worker_device="/job:worker/task:%d" % FLAGS.task_index,
cluster=cluster)):
# count the number of updates
global_step = tf.get_variable( 'global_step', [], initializer = tf.constant_initializer(0),
trainable = False)
# input images
with tf.name_scope('input'):
# None -> batch size can be any size, 784 -> flattened mnist image
x = tf.placeholder(tf.float32, shape=[None, 784], name="x-input")
# target 10 output classes
y_ = tf.placeholder(tf.float32, shape=[None, 10], name="y-input")
# model parameters will change during training so we use tf.Variable
tf.set_random_seed(1)
with tf.name_scope("weights"):
W1 = tf.Variable(tf.random_normal([784, 100]))
W2 = tf.Variable(tf.random_normal([100, 10]))
# bias
with tf.name_scope("biases"):
b1 = tf.Variable(tf.zeros([100]))
b2 = tf.Variable(tf.zeros([10]))
# implement model
with tf.name_scope("softmax"):
# y is our prediction
z2 = tf.add(tf.matmul(x,W1),b1)
a2 = tf.nn.sigmoid(z2)
z3 = tf.add(tf.matmul(a2,W2),b2)
y = tf.nn.softmax(z3)
# specify cost function
with tf.name_scope('cross_entropy'):
# this is our cost
cross_entropy = tf.reduce_mean(
-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
# specify optimizer
with tf.name_scope('train'):
# optimizer is an "operation" which we can execute in a session
grad_op = tf.train.GradientDescentOptimizer(learning_rate)
train_op = grad_op.minimize(cross_entropy, global_step=global_step)
with tf.name_scope('Accuracy'):
# accuracy
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
# create a summary for our cost and accuracy
tf.summary.scalar("cost", cross_entropy)
tf.summary.scalar("accuracy", accuracy)
# merge all summaries into a single "operation" which we can execute in a session
summary_op = tf.summary.merge_all()
init_op = tf.global_variables_initializer()
print("Variables initialized ...")
sv = tf.train.Supervisor(is_chief=(FLAGS.task_index == 0))
。 同样,使用prepare_or_wait_for_session(server.target)
,主管将等待模型准备就绪可以使用。 请注意,每个工作器将处理不同的批量模型,然后最终模型可用于主管。sv = tf.train.Supervisor(is_chief=(FLAGS.task_index == 0),
begin_time = time.time()
frequency = 100
with sv.prepare_or_wait_for_session(server.target) as sess:
# create log writer object (this will log on every machine)
writer = tf.summary.FileWriter(logs_path, graph=tf.get_default_graph())
# perform training cycles
start_time = time.time()
for epoch in range(training_epochs):
# number of batches in one epoch
batch_count = int(mnist.train.num_examples/batch_size)
count = 0
for i in range(batch_count):
batch_x, batch_y = mnist.train.next_batch(batch_size)
# perform the operations we defined earlier on batch
_, cost, summary, step = sess.run(
[train_op, cross_entropy, summary_op, global_step],
feed_dict={x: batch_x, y_: batch_y})
writer.add_summary(summary, step)
count += 1
if count % frequency == 0 or i+1 == batch_count:
elapsed_time = time.time() - start_time
start_time = time.time()
print("Step: %d," % (step+1),
" Epoch: %2d," % (epoch+1), " Batch: %3d of %3d," % (i+1, batch_count),
" Cost: %.4f," % cost,
"AvgTime:%3.2fms" % float(elapsed_time*1000/frequency))
count = 0
print("Test-Accuracy: %2.2f" % sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
print("Total Time: %3.2fs" % float(time.time() - begin_time))
print("Final Cost: %.4f" % cost)
sv.stop()
print("done")
本秘籍描述了分布式 MNIST 分类器的示例。 在此示例中,TensorFlow 允许我们定义三台机器的集群。 一台充当参数服务器,另外两台机器用作工作器,负责处理不同批次的训练数据。
在本秘籍中,我们将展示如何为 TensorFlow Serving 运行 Docker 容器,这是一组组件以导出经过训练的 TensorFlow 模型并使用标准tensorflow_model_server
为其提供服务。 TensorFlow Serving 服务器发现新导出的模型并运行 gRPC 服务来为其服务。
我们将使用 Docker,并假设您熟悉该系统。 如果没有,请确保查看并安装。 我们要做的是构建 TF 服务版本。
这是我们进行秘籍的方法:
Dockerfile.devel
docker build --pull -t $USER/tensorflow-serving-devel -f Dockerfile.devel
docker run -it $USER/tensorflow-serving-devel
git clone --recurse-submodules https://github.com/tensorflow/serving
cd serving/tensorflow
./configure
cd ..
bazel test tensorflow_serving/...
saved_model_builder
。 然后,大部分工作由SavedModelBuilder()
完成,该工作将已训练模型的快照保存到可靠的存储中。 请注意,此处export_path
是/tmp/mnist_model/
from tensorflow.python.saved_model import builder as saved_model_builder
...
export_path_base = sys.argv[-1]
export_path = os.path.join(
compat.as_bytes(export_path_base),
compat.as_bytes(str(FLAGS.model_version)))
print 'Exporting trained model to', export_path
builder = saved_model_builder.SavedModelBuilder(export_path)
builder.add_meta_graph_and_variables(
sess, [tag_constants.SERVING],
signature_def_map={
'predict_images':
prediction_signature,
signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY:
classification_signature,
},
legacy_init_op=legacy_init_op)
builder.save()
tensorflow_model_server --port=9000 --model_name=mnist --model_base_path=/tmp/mnist_model/
Google 在 2016 年 2 月发布了 TensorFlow Serving,这是一种针对机器学习模型的高性能服务系统,专为生产环境而设计。 截至 2017 年 8 月,谷歌内部有 800 多个使用 TensorFlow Serving 的项目投入生产。
TensoFlow Serving 是一款非常通用的软件,在本秘籍中,我们只是初步探究了潜在用途。 如果您有兴趣进一步了解高级功能。 例如大批量运行或动态加载模型,则应查看这个页面
在本秘籍中,我们将学习如何在 Google Cloud(GCP)上使用 Google Tensorflow。 我们将要审查的示例是经典 MNIST。
看看 GCP 在 GoogleCloud 中的工作方式会很好。 请注意,GCP 提供 300 美元的免费赠金,以开始使用任何 GCP 产品。 此外,在免费试用期间和之后,某些产品针对符合条件的客户可以免费使用。 (优惠如有更改,请参见这个页面。
这是我们进行秘籍的方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-73FV5Zfv-1681565377476)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/d91fcd12-0e0f-49ac-8a2f-976810b3e96d.png)]
单击创建项目时,将显示以下屏幕:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Snww30Tf-1681565377476)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/95c462d7-af82-4b17-bef3-adeaecd81236.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VIhDnJq4-1681565377476)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/41ea2e03-f1d7-4aac-a6e3-33db31053786.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tn1usKNP-1681565377476)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/ed57ff64-af8b-43fb-a692-892811cf9dfb.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IonITbzT-1681565377476)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/848d8c7b-e216-41ff-82f2-17c390720f1e.png)]
gcloud config set compute/zone us-east1-c
gcloud config set project [YOUR_PROJECT_ID]
git clone https://github.com/GoogleCloudPlatform/cloudml-dist-mnist-example
cd cloudml-dist-mnist-example
gcloud compute instances create template-instance \
--image-project ubuntu-os-cloud \
--image-family ubuntu-1604-lts \
--boot-disk-size 10GB \
--machine-type n1-standard-1
gcloud compute ssh template-instance
sudo apt-get update
sudo apt-get -y upgrade \
&& sudo apt-get install -y python-pip python-dev
sudo pip install tensorflow
sudo pip install --upgrade tensorflow
BUCKET="mnist-$RANDOM-$RANDOM"
gsutil mb -c regional -l us-east1 gs://${BUCKET}
sudo ./scripts/create_records.py
gsutil cp /tmp/data/train.tfrecords gs://${BUCKET}/data/
gsutil cp /tmp/data/test.tfrecords gs://${BUCKET}/data/
worker-0
,worker-1
),它们是初始模板实例计算机的克隆。 我们不希望计算机在关闭时删除磁盘,因此这就是我们拥有第一个命令的原因。gcloud compute instances set-disk-auto-delete template-instance \
--disk template-instance --no-auto-delete
gcloud compute instances delete template-instance
gcloud compute images create template-image \
--source-disk template-instance
gcloud compute instances create \
master-0 worker-0 worker-1 ps-0 \
--image template-image \
--machine-type n1-standard-4 \
--scopes=default,storage-rw
./scripts/start-training.sh gs://${BUCKET}
演示脚本将代码移动到每个 VM 并开始分布式计算。 这两个工作程序在公共存储桶上共享的相同 MNIST 数据上并行运行。 计算结束后,脚本将打印训练后的模型的位置。
如果我们不想管理 TensorFlow,那么我们可以决定使用 Google 代表您运行的托管版本。 这是在下一个秘籍中描述的 CloudML 服务。 此外,如果您决定不将 GPU 与 CloudML 一起使用,那么此 URL 是一个不错的起点
CloudML 是 Google 运行的 Tensorflow 的托管版本。 无需自己运行 TF,您只需使用 CloudML 即可,而无需考虑与基础架构和可伸缩性相关的所有问题。
在这里,我们假设您已经创建了一个 Cloud Platform Project,为您的项目启用计费,并启用了 Google Compute Engine 和 Cloud Machine Learning API。 这些步骤类似于先前秘籍中描述的步骤。 此秘籍的灵感来自这个页面中提供的 MNIST 训练代码。
我们继续在 Google CloudML 上运行分布式 TensorFlow:
git clonehttps://github.com/GoogleCloudPlatform/cloudml-dist-mnist-example
cd cloudml-dist-mnist-example
PROJECT_ID=$(gcloud config list project --format "value(core.project)")
BUCKET="${PROJECT_ID}-ml"
gsutil mb -c regional -l us-central1 gs://${BUCKET}
./scripts/create_records.py
gsutil cp /tmp/data/train.tfrecords gs://${BUCKET}/data/
gsutil cp /tmp/data/test.tfrecords gs://${BUCKET}/data/
us-central1
区域中运行 1000 次迭代。 输入数据是从存储桶中提取的,而输出桶将被提交到其他存储桶中。JOB_NAME="job_$(date +%Y%m%d_%H%M%S)"
gcloud ml-engine jobs submit training ${JOB_NAME} \
--package-path trainer \
--module-name trainer.task \
--staging-bucket gs://${BUCKET} \
--job-dir gs://${BUCKET}/${JOB_NAME} \
--runtime-version 1.2 \
--region us-central1 \
--config config/config.yaml \
-- \
--data_dir gs://${BUCKET}/data \
--output_dir gs://${BUCKET}/${JOB_NAME} \
--train_steps 10000
MODEL_NAME=MNIST
gcloud ml-engine models create --regions us-central1 ${MODEL_NAME}
VERSION_NAME=v1
ORIGIN=$(gsutil ls gs://${BUCKET}/${JOB_NAME}/export/Servo | tail -1)
gcloud ml-engine versions create \
--origin ${ORIGIN} \
--model ${MODEL_NAME} \
${VERSION_NAME}
gcloud ml-engine versions set-default --model ${MODEL_NAME} ${VERSION_NAME}
make_request.py
创建request.json
,该脚本从 MNIST 读取数据,执行一次热编码,然后使用格式正确的 json 模式编写功能。gcloud ml-engine predict --model ${MODEL_NAME} --json-instances request.json
CloudML 是使用 Google 运行的托管版本的 TensorFlow 的便捷解决方案。 除了直接照顾基础设施和运营,还可以直接专注于开发机器学习模型。
CloudML 的一项非常酷的功能是能够通过并行运行多个试验来自动调整模型中包含的超参数的能力。 这为您提供了超参数的优化值,从而最大化了模型的预测精度。 如果您有兴趣了解更多信息,那么看看这里
Microsoft Azure 提供了一项名为 Batch AI 的服务,该服务使我们能够在 Azure 虚拟机的群集上运行机器学习模型。
第一步,我们需要一个 Azure 帐户:如果您还没有 Azure 帐户,则可以在此处免费创建一个帐户 。 Azure 向新用户提供为期 30 天的 200 美元信用额度。 此秘籍将遵循 Microsoft Azure 提供的示例,以使用 TensorFlow 分布式在两个 GPU 上运行 MNIST,相关代码可在 Github 上获得
我们按以下步骤进行:
az login
登录 Azure。 它将生成一个代码,并为您提供一个网站地址,将要求您在其中验证您的凭据,一旦网站上的所有步骤都结束,将要求您关闭并验证您的 az 凭据。az group create --name myResourceGroup --location eastus
az configure --defaults group=myResourceGroup
az configure --defaults location=eastus
az
命令创建存储,并根据您的操作系统设置环境变量,有关环境变量及其值的详细信息,请参见这里
wget "https://batchaisamples.blob.core.windows.net/samples/mnist_dataset_original.zip?st=2017-09-29T18%3A29%3A00Z&se=2099-12-31T08%3A00%3A00Z&sp=rl&sv=2016-05-31&sr=b&sig=Qc1RA3zsXIP4oeioXutkL1PXIrHJO0pHJlppS2rID3I%3D" -O mnist_dataset_original.zip
unzip mnist_dataset_original.zip
mnist_replica
wget "https://raw.githubusercontent.com/Azure/BatchAI/master/recipes/TensorFlow/TensorFlow-GPU-Distributed/mnist_replica.py?token=AcZzrcpJGDHCUzsCyjlWiKVNfBuDdkqwks5Z4dPrwA%3D%3D" -O mnist_replica.py
mnist_replica.py
文件。az storage share create --name batchaisample
az storage directory create --share-name batchaisample --name mnist_dataset
az storage file upload --share-name batchaisample --source t10k-images-idx3-ubyte.gz --path mnist_dataset
az storage file upload --share-name batchaisample --source t10k-labels-idx1-ubyte.gz --path mnist_dataset
az storage file upload --share-name batchaisample --source train-images-idx3-ubyte.gz --path mnist_dataset
az storage file upload --share-name batchaisample --source train-labels-idx1-ubyte.gz --path mnist_dataset
az storage directory create --share-name batchaisample --name tensorflow_samples
az storage file upload --share-name batchaisample --source mnist_replica.py --path tensorflow_samples
对于 Linux:
az batchai cluster create -n nc6 -i UbuntuDSVM -s Standard_NC6 --min 2 --max 2 --afs-name batchaisample --afs-mount-path external -u $USER -k ~/.ssh/id_rsa.pub
对于 Windows:
az batchai cluster create -n nc6 -i UbuntuDSVM -s Standard_NC6 --min 2 --max 2 --afs-name batchaisample --afs-mount-path external -u <user_name> -p <password>
job.json
文件中创建作业创建参数:{
"properties": {
"nodeCount": 2,
"tensorFlowSettings": {
"parameterServerCount": 1,
"workerCount": 2,
"pythonScriptFilePath": "$AZ_BATCHAI_INPUT_SCRIPT/mnist_replica.py",
"masterCommandLineArgs": "--job_name=worker --num_gpus=1 --ps_hosts=$AZ_BATCHAI_PS_HOSTS --worker_hosts=$AZ_BATCHAI_WORKER_HOSTS --task_index=$AZ_BATCHAI_TASK_INDEX --data_dir=$AZ_BATCHAI_INPUT_DATASET --output_dir=$AZ_BATCHAI_OUTPUT_MODEL",
"workerCommandLineArgs": "--job_name=worker --num_gpus=1 --ps_hosts=$AZ_BATCHAI_PS_HOSTS --worker_hosts=$AZ_BATCHAI_WORKER_HOSTS --task_index=$AZ_BATCHAI_TASK_INDEX --data_dir=$AZ_BATCHAI_INPUT_DATASET --output_dir=$AZ_BATCHAI_OUTPUT_MODEL",
"parameterServerCommandLineArgs": "--job_name=ps --num_gpus=0 --ps_hosts=$AZ_BATCHAI_PS_HOSTS --worker_hosts=$AZ_BATCHAI_WORKER_HOSTS --task_index=$AZ_BATCHAI_TASK_INDEX --data_dir=$AZ_BATCHAI_INPUT_DATASET --output_dir=$AZ_BATCHAI_OUTPUT_MODEL"
},
"stdOutErrPathPrefix": "$AZ_BATCHAI_MOUNT_ROOT/external",
"inputDirectories": [{
"id": "DATASET",
"path": "$AZ_BATCHAI_MOUNT_ROOT/external/mnist_dataset"
}, {
"id": "SCRIPT",
"path": "$AZ_BATCHAI_MOUNT_ROOT/external/tensorflow_samples"
}],
"outputDirectories": [{
"id": "MODEL",
"pathPrefix": "$AZ_BATCHAI_MOUNT_ROOT/external",
"pathSuffix": "Models"
}],
"containerSettings": {
"imageSourceRegistry": {
"image": "tensorflow/tensorflow:1.1.0-gpu"
}
}
}
}
az batchai job create -n distibuted_tensorflow --cluster-name nc6 -c job.json
Batch AI 自行管理资源,您只需要指定作业,输入的位置以及存储输出的位置即可。 如果在执行作业期间想要查看结果,则可以使用以下命令:
az batchai job stream-file --job-name myjob --output-directory-id stdouterr --name stderr.txt
作业结束后,您可以使用az batchai job delete
和az batchai cluster delete
命令删除作业和群集。
上面我们学习了如何使用 Azure 命令行工具将 Microsoft Azure Batch AI 用于分布式 TensorFlow。 我们也可以使用 Jupyter 笔记本做同样的事情。 这将涉及设置 Azure Active Directory 并进行新的 App 注册。 可以在以下链接上获得详细信息。
Azure BatchAI 也可以与其他 AI 深度学习库一起使用,我们建议您仔细阅读 BatchAI Github 以获取更多详细信息。
Amazon AWS 提供了配备 NVIDIA K8 GPU 的 P2.x 机器。 为了能够使用,第一步再次涉及创建一个 Amazon AWS 账户。 如果还没有,可以使用以下链接创建它。 登录帐户后,仪表板将如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aNAdytxN-1681565377477)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/b32ea001-d1a5-464b-b2ec-a1a43c8d1001.png)]
您可以看到 Amazon AWS 提供了许多服务,但是在这里,我们担心使用 Amazon AWS 进行深度学习。
GPU 仅在 P2 实例创建中可用,并且默认情况下不可用,要获得此服务,您必须通过 AWS 支持筹集更多资源,一旦获得支持,该支持就会显示在右上角, 您将看到一个按钮,创建案例,选择按钮并进行以下选择:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6f9JhFLs-1681565377477)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/1b9d45a6-ea33-4b49-bf7e-028927a57452.png)]
您可以选择提供 p2.xlarge 实例的任何区域。 新的限制值决定了您可以使用的实例的最大数量,请谨慎选择数量,因为该服务不是免费的,并且每小时的费用约为 1 美元。 AWS 大约需要 24 小时才能响应请求。
现在您已经可以使用 AWS 账户和 p2.xlarge 实例,现在就可以从 EC2 仪表板创建自己的实例了。 第一步是选择机器映像,到目前为止,Amazon 提供了预装有深度学习库的特殊机器映像,您可以选择 Ubuntu 或 Linux 版本。 接下来,选择 GPU 以计算实例类型。
您可以使用默认参数查看并启动实例,也可以配置设置,选择存储并配置安全组。 配置安全组很重要,默认情况下已设置 SSH 安全组,但是如果要使用 Jupyter 笔记本,则需要为端口 8888 添加自定义安全组,可以选择从中登录的源。 对于实例,有三种可能性:“自定义”,“随处可见”或“我的 IP”:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DCm6gx66-1681565377477)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/10716413-09c1-49c7-9c5c-207de1610b48.png)]
最后,在启动实例时,将要求您创建一个密钥对,以允许您登录到特定实例,您将需要创建一个密钥对并下载相应的.pem
文件,您将使用文件以便后续记录。
.pem
文件的模式。
chmod 400 <security-keypair-filename>.pem
ssh -i " <security-keypair-filename>.pem" ubuntu@ec2-XXXXXXXXXXXXX.compute-1.amazonaws.com
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4FNh2xpZ-1681565377477)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/97e2ab30-bc18-4894-bdbe-21be3c91b17d.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-606vnSnO-1681565377478)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/ebb26449-ae6a-4899-a75c-995e10a1ef3a.png)]
您可以运行在我们创建的实例上学到的代码。 工作结束后,不要忘记退出,并从仪表板停止实例。 有关价格和使用情况的更多详细信息,请参见这里
AWS 市场上提供了带有预配置库和 API 的大量 Docker 映像和机器映像。 要启动 jupyter 笔记本,请在命令行中使用<jupyter notebook --ip=0.0.0.0 --no-browser>
。 这将导致输出,如下所示:
首次连接以使用令牌登录时,将此 URL 复制/粘贴到浏览器中:
http://0.0.0.0:8888/?token=3156e...
您将 URL 复制并粘贴到浏览器中,就可以开始使用了。
此外,可以通过查看 AWS CloudFormation 简化整个过程。 它使用模板创建和配置 Amazon Web Services 资源。 这样可以简化设置分布式深度学习集群的过程。 有兴趣的读者可以看看这里
深度学习的成功极大地促进了特征工程的工作。 确实,传统的机器学习很大程度上取决于对正确的特征集的选择,而且与选择特定的学习算法相比,这一步骤通常更为重要。 深度学习改变了这种情况。 创建正确的模型仍然非常重要,但是现今的网络对特定特征集的选择已不那么敏感,并且能够自动选择真正重要的特征。
取而代之的是,深度学习的引入使人们更加关注正确的神经网络架构的选择。 这意味着研究人员的兴趣正逐渐从特征工程转向网络工程。 AutoML(元学习)是一个新兴的研究主题,旨在针对给定的学习任务自动选择最有效的神经网络。 换句话说,AutoML 代表学习如何有效学习的一组方法。 考虑例如机器翻译,图像识别或游戏的任务。 通常,模型是由工程师,数据科学家和领域专家组成的团队手动设计的。 如果您认为一个典型的 10 层网络可以具有约10^10
个候选网络,那么您就会知道该过程可能很昂贵,容易出错并且最终不是最佳选择。
解决此问题的关键思想是拥有一个控制器网络,该控制器网络会在给定特定网络输入的情况下,以概率p
提出子模型架构。 对子项进行训练和评估,以解决要解决的特定任务(例如,子项获得的准确率R
)。 该评估R
被传递回控制器,该控制器进而使用R
来改善下一个候选架构。 在此框架下,可以将候选子项到控制器的反馈建模为计算p
的梯度的任务,然后通过R
缩放此梯度。 控制器可以实现为循环神经网络(请参见下图)。 这样做,控制器将倾向于在迭代之后获得更好的R
的架构候选区域,并倾向于给得分不那么高的候选区域分配较低的概率。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kdwAKivP-1681565377478)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/30ccd28f-f08f-4323-aae4-313b11ba8ddb.png)]
例如,控制器循环神经网络可以对卷积网络进行采样。 控制器可以预测许多超参数,例如过滤器高度,过滤器宽度,步幅高度,步幅宽度和一层过滤器的数量,然后可以重复。 每个预测可以由 softmax 分类器执行,然后输入到下一个 RNN 时间步中。 以下图片来自《使用增强学习的神经架构搜索》(Barret Zoph,Quoc V. Le):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hd5dL5F3-1681565377478)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/9986f40c-9051-4483-959c-00a067ed0e2a.png)]
预测超参数是不够的,因为最好定义一组动作以在网络中创建新层。 这特别困难,因为描述新层的奖励函数极有可能是不可区分的,因此无法通过标准技术(例如 SGD)对其进行优化。 解决方案来自强化学习,它包括采用类似于我们的第 9 章,“强化学习”中所述的策略梯度网络。
除此之外,并行性可用于优化控制器 RNN 的参数。 Quoc Le&Barret Zoph 建议采用参数服务器方案,其中我们有一个 S 碎片的参数服务器,用于存储 K 个控制器副本的共享参数。 每个控制器副本都采样了如下图所示的并行训练的不同子架构,如下图所示,取自《使用增强学习的神经架构搜索》(Barret Zoph,Quoc V. Le):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQSEdLBz-1681565377478)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/23d54b43-728d-4230-b0ab-4d219de2c679.png)]
Quoc 和 Barret 将 AutoML 技术用于神经架构搜索应用于 Penn Treebank 数据集,这是语言建模的著名基准。 他们的结果改进了目前被认为是最先进的手动设计网络。 特别是,他们在 Penn Treebank 上实现了 62.4 的测试集困惑,这比以前的最新模型好 3.6 困惑。 同样,在 CIFAR-10 数据集上,该方法可以设计一种新颖的网络架构,与测试集准确率方面最好的人类发明架构旗鼓相当。 提出的 CIFAR-10 模型实现了 3.65 的测试错误率,比使用类似架构方案的最新技术模型好 0.09%,并且快 1.05 倍。
在《用于可伸缩图像识别的学习可传输架构》(Barret Zoph,Vijay Vasudevan,Jonathon Shlens,Quoc V. Le,2017)中,建议在小型数据集上学习架构构造块,然后将其传输到大型数据集。 作者建议在 CIFAR-10 数据集上搜索最佳的卷积层(或单元),然后通过堆叠该单元的更多副本(每个都有其自己的参数),将此学习到的单元应用于 ImageNet 数据集。 准确地说,所有卷积网络都是由结构相同但权重不同的卷积层(或单元)组成的。 因此,将搜索最佳卷积架构简化为搜索最佳单元结构,这样可以更快地将其推广到其他问题。 尽管无法在 ImageNet 上直接学习该单元,但是在已发表的工作中,由学得最好的单元构成的架构可实现 ImageNet 的最新精度为 82.7% top-1 和 96.2% top-5。 该模型的 top-1 准确率比人类发明的最佳架构高 1.2%,而 FLOPS 则减少了 90 亿个,与之前的最新模型相比降低了 28%。 还需要注意的重要一点是,使用 RNN + RL(循环神经网络+强化学习)学习的模型正在击败随机搜索(RL)代表的基线,如本文中所取的图所示。 在 RL 与 RS 中确定的前 5 名和前 25 名模型的平均表现中,RL 始终是赢家:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E7CQbza6-1681565377478)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/c806db28-97c3-409d-8877-d86502c86e12.png)]
可以对元学习系统进行训练以完成大量任务,然后对其元学习新任务的能力进行测试。 这种元学习的一个著名例子是在高级 CNN 章节中讨论的所谓迁移学习,其中网络可以从相对较小的数据集中成功学习基于图像的新任务。 但是,对于诸如语音,语言和文本之类的非视觉领域,没有类似的预训练方案。
《用于快速适应深度网络的模型不可知元学习》(切尔西·芬恩(Chelsea Finn),彼得·阿比尔(Siety Levine),2017 年)提出了一个模型 -不可知论方法的名称为 MAML,它与经过梯度下降训练的任何模型兼容,并且适用于各种不同的学习问题,包括分类,回归和强化学习。 元学习的目标是针对各种学习任务训练模型,以便仅使用少量训练样本即可解决新的学习任务。 元学习器旨在找到一种可以快速(以很少的步骤)并有效地(仅使用几个示例)快速适应各种问题的初始化。 用参数为θ的参数化函数f[0]
表示的模型。 当适应新任务T[i]
时,模型参数θ
变为θ[i']
。 在 MAML 中,使用对任务T[i]
的一个或多个梯度下降更新来计算更新的参数向量θ[i']
。
例如,当使用一个梯度更新时,θ[i'] = θ - α∇[0]L[Ti](f[θ])
,其中L[Ti]
是任务T
的损失函数,而α
是元学习参数。 该图报告了 MAML 算法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9inUds1x-1681565377479)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/30503d87-ad59-4ec7-8e66-2918624faaf7.png)]
MAML 能够在流行的少拍图像分类基准上大大胜过许多现有方法。 旨在从一个或几个概念中学习新概念的镜头很少有挑战性。 例如,《通过概率性程序归纳进行人级概念学习》(Brenden M. Lake,Ruslan Salakhutdinov,Joshua B. Tenenbaum,2015 年),建议人们可以学会从单个图片中识别新颖的两轮车,例如红色框中包含的图片,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VFOU0szK-1681565377479)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/d55d0f5e-ee42-4c09-a213-65fadee76b26.png)]
在 2017 年底,AutoML(或元学习)主题是一个活跃的研究主题,旨在针对给定的学习任务自动选择最高效的神经网络。 目标是学习如何高效,自动地设计网络,从而可以学习特定任务或适应新任务。 主要问题是不能简单地用可微分的损失函数描述网络的设计,因此传统的优化技术不能简单地用于元学习。 因此,已经提出了一些解决方案,包括具有控制器循环网络(RNN)和基于强化学习的奖励策略的思想,以及具有与模型无关的元学习的思想。 两种方法都非常有前途,但是肯定还有很多研究空间。
因此,如果您对一个热门话题感兴趣,那么学习深度学习当然是一个可以考虑作为下一份工作的空间。
连体网络是 Yann LeCun 及其同事在 NIPS 1994 中提出的一种特殊类型的神经网络。 它们背后的基本思想是,像“连体双胞胎”一样,该网络由两个不同的神经网络组成,它们共享相同的架构和权重。
在这里,您可以看到连体架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BGrtYtnK-1681565377479)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/15b0d10f-3abe-4254-87dd-e3cb5ad93494.png)]
在训练阶段,该对网络会显示一个训练对(X[1], X[2])
,其中两个输入不同但相似,例如, X[1] = 他很聪明
,而X[2] = 他是一个聪明人
。 这两个神经网络将产生两个不同的结果。 可以将组合网络视为测量训练对(X[1], X[2])
之间相似度的标量能量函数 , 定义为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LmrUlxGs-1681565377479)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/7ae40c9e-adb2-4c8b-bb4d-1dab0e5c19b0.png)]
连体网络的目标是训练对(X[1], X[2])
之间的能量应小于其他赝品对之间的能量(X[1], X'[2])
。
这可以通过使用对比损失函数进行训练来实现。
在训练阶段,网络被输入一个训练对和一个与之相关的标签,告诉它是一个真正的对或赝品对的网络:(X[1], X[2], Y[i])
第i
个训练样本。 对比损失函数计算如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xQEZmj7V-1681565377480)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/32f13c94-c144-4116-bd64-e97805948eb6.png)]
其中,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uU4PBckp-1681565377480)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/633e200a-ea56-49bd-9669-15c9effa8a56.png)]
和L[G]
是真正对的部分损失, L[I]
是赝品的部分损失对和P
训练样本数。 当偶对是正品时,标签Y
的值为 0;当偶对是赝品时,标签Y
的值为 1。 设计局部损失L[G]
和L[I]
的方式应使对比损耗L(W)
将减少真正对的能量并增加赝品的能量。 这是通过选择部分损失L(G)
单调增加而选择部分损失L(I)
单调减少f
来实现的。 一种可能的选择是使用余弦相似度来计算部分损耗。
使用反向传播算法调整权重。
近年来,连体网络已用于许多应用。 他们在 LeCun 论文中首次使用的是签名验证。 从那时起,出现了许多应用,我们将介绍一些最近的应用:
除此之外,还对连体网络进行了脸部验证/识别。 它们已用于问题解答。
工作示例基于 Github 页面。 此处的代码使用连体网络将手写的 MNIST 数字嵌入到 2D 空间中,属于同一类的数字被嵌入在一起。 该代码包含三个主要文件:
run.py
:它包含执行训练的基本包装。 它使用“梯度下降”算法将对比度损失降至最低。
inference.py
:包含定义 3 层全连接网络的连体类。 代码中两个网络的输出之间的相似性是欧几里得。 然后,使用部分生成损失和部分赝品损失来计算对比损失。
visualize.py
:这只是用于可视化结果的包装。
经过前 100,000 个训练步骤,结果是:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FtY1CAhy-1681565377480)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-1x-dl-cookbook/img/8b5c32c0-e504-4d4d-bdaf-24d3eb4ccef3.png)]
您可以看到相同(标记)的数字一起嵌入 2D 空间。
还有另一个有趣的示例](https://github.com/dhwajraj/deep-siamese-text-similarity) 。
在这里,使用 Tensorflow,训练了深层的连体 LSTM 网络以使用字符嵌入来捕获短语/句子相似性。
Google 服务(例如 Google 搜索(RankBrain),街景,Google 照片和 Google 翻译)有一个共同点:它们都使用 Google 的 Tensor 处理单元或 TPU 进行计算。
您可能在想什么是 TPU,这些服务有什么好处? 所有这些服务都在后台使用最新的机器学习算法,并且这些算法涉及大量计算。 TPU 有助于加速所涉及的神经网络计算。 甚至 AlphaGo,一种在 Go 游戏中击败 Lee Sedol 的深度学习程序,都由 TPU 推动。 因此,让我们看看 TPU 到底是什么。
TPU 是 Google 专门为机器学习而定制的定制专用集成电路(ASIC),是针对 Tensorflow 量身定制的。 它基于 28 纳米工艺构建,运行频率为 700 MHz,运行时消耗 40 W 的能量。 它包装为外部加速卡,可以插入现有的 SATA 硬盘插槽中。 TPU 通过 PCIe Gen 3×16 总线连接到主机 CPU,该总线提供 12.5 GB/s 的有效带宽。
到目前为止,第一代 TPU 的目标是推理,即使用已经训练好的模型。 DNN 的训练通常需要更多时间,但仍在 CPU 和 GPU 上进行。 在 2017 年 5 月的博客文章中宣布的第二代 TPU 都可以训练和推断机器学习模型。
在本书涵盖的所有深度学习模型中,无论学习范例如何,都需要进行三个基本计算:乘法,加法和激活函数的应用。
前两个成分是矩阵乘法的一部分:权重矩阵W
需要与输入矩阵X
相乘W^T · X
; 矩阵乘法在 CPU 上的计算量很大,尽管 GPU 使操作并行化,但仍有改进的余地。
TPU 具有 65,536 个 8 位整数矩阵乘法器单元(MXU),峰值吞吐量为 92 TOPS。 GPU 和 TPU 乘法之间的主要区别在于 GPU 包含浮点乘法器,而 TPU 包含 8 位整数乘法器。 TPU 还包含一个统一缓冲区(UB),用作寄存器的 24 MB SRAM 和一个包含硬接线激活函数的激活单元(AU)。
MXU 是使用脉动数组架构实现的。 它包含一个数组算术逻辑单元(ALU),该数组连接到网状拓扑中的少量最近邻居。 每个数据值仅读取一次,但在流过 ALU 数组时会多次用于不同的操作,而无需将其存储回寄存器。 TPU 中的 ALU 仅以固定模式执行乘法和加法。 MXU 已针对矩阵乘法进行了优化,不适用于通用计算。
每个 TPU 还具有一个片外 8GiB DRAM 池,称为加权存储器。 它具有四个阶段的流水线,并执行 CISC 指令。 到目前为止,TPU 由六个神经网络组成:两个 MLP,两个 CNN 和两个 LSTM。
在高级指令的帮助下对 TPU 进行编程; 下面是一些用于对 TPU 进行编程的指令:
Read_Weights
:从内存读取权重Read_Host_Memory
:从内存中读取数据MatrixMultiply/Convolve
:与数据相乘或卷积并累加结果Activate
:应用激活函数Write_Host_Memory
:将结果写入存储器Google 创建了一个 API 栈,以方便 TPU 编程; 它将来自 Tensorflow 图的 API 调用转换为 TPU 指令。
TPU 提供的优于 GPU 和 CPU 的首要优势是性能。 Google 将 TPU 的性能与运行基准代码(代表 95% 的推理工作量)的服务器级 Intel Haswell CPU 和 NVIDIA K80 GPU 进行了比较。 它发现 TPU 的速度比 NVIDIA GPU 和 Intel CPU 快 15-30 倍。
第二个重要参数是功耗。 降低功耗非常重要,因为它具有双重能源优势:它不仅减少了功耗,而且还通过降低散热成本来散热,从而节省了功耗,从而消除了加工过程中产生的热量。 TPU / CPU 每瓦性能比其他 CPU 和 GPU 配置提高了 30-80 倍。
TPU 的另一个优点是其最小化和确定性的设计,因为它们一次只能执行一个任务。
与 CPU 和 GPU 相比,单线程 TPU 没有任何复杂的微架构功能会消耗晶体管和能量来改善平均情况,但不会消耗 99% 的情况:没有缓存,分支预测,乱序执行, 多处理,推测性预取,地址合并,多线程,上下文切换等。 极简主义是特定领域处理器的优点。
Google 已决定不直接将 TPU 出售给他人; 取而代之的是,将通过 Google 云平台提供 TPU:Cloud TPU Alpha。 Cloud TPU Alpha 将提供高达 180 teraflops 的计算性能和 64 GB 的超高带宽内存。 用户将能够从自定义虚拟机连接到这些 Cloud TPU。
Google 还决定向全球的机器学习研究人员免费提供 1000 个云 TPU 集群,以加快开放式机器学习研究的步伐。 在有限的计算时间内,将授予选定的个人访问权限; 个人可以使用以下链接进行注册。 根据 Google Blog:
“由于 TensorFlow 研究云的主要目标是使整个开放式机器学习研究社区受益,因此,成功的申请人有望做到以下几点:
通过同行评审的出版物,开源代码,博客文章或其他开放媒体与世界分享其 TFRC 支持的研究
与 Google 分享具体的建设性反馈,以帮助我们随着时间的推移改进 TFRC 计划和基础的 Cloud TPU 平台。
想象一下 ML 加速丰富的未来,并基于这种未来开发新的机器学习模型。”