Python 深度学习(一)(3)https://developer.aliyun.com/article/1512030
自编码器总结
自编码器是强大的无监督学习算法,在异常检测或特征工程等领域越来越受欢迎,使用中间层的输出作为特征来训练监督模型,而不是使用原始输入数据。
无监督意味着在训练过程中不需要指定标签或地面真相。只要网络有足够的能力学习和表示内在的存在关系,它们就可以处理输入的任何数据。这意味着我们可以设定编码层的大小(减少的维度m),但根据隐藏层的数量和大小来获得不同的结果。
如果我们正在构建一个自动编码器网络,我们希望在避免错误表示的同时实现稳健性,但同时不要通过较小的顺序层压缩信息来限制网络的容量。
除噪声、收缩和自动编码器都是解决这些问题的很好的技术。
添加噪声通常更简单,而且不会在损失函数中增加复杂性,这会导致更少的计算。另一方面,嘈杂的输入使梯度变得不稳定,并且为了获得更好的特征而丢弃部分信息。
收缩自动编码器非常擅长使模型对训练分布的小偏差更加稳定。因此,它是减少误报的一个很好的选择。缺点是一种反效果,它会增加重构误差以减少敏感性。
稀疏自动编码器可能是最完整的解决方案。对于大型数据集来说,它计算成本最高,但由于梯度是确定的,它可以在二阶优化器的情况下提供很好的稳定性和低重构误差。
不管你做出什么选择,采用正则化技术都是强烈推荐的。它们都带有超参数需调整,我们将在相应的Tuning部分中看到如何优化。
除了迄今为止描述的技术外,值得一提的是变分自动编码器,它似乎是正则化自动编码器的最终解决方案。变分自动编码器属于生成模型类别。它不仅学习了最好地描述训练数据的结构,还学习了潜在单位高斯分布的参数,这些参数可以最好地再现输入数据。最终的损失函数将是重构误差和重构的潜在变量之间的 KL 散度的总和。编码器阶段将生成由均值和标准差向量组成的代码。从代码中,我们可以表征潜在分布参数,并通过从该分布中采样重构原始输入。
受限玻尔兹曼机
- 在 90 年代初,神经网络基本上已经过时。机器学习研究的大部分内容是关于其他技术,如随机森林和支持向量机。只有一个隐藏层的神经网络表现不如这些其他技术,而且人们认为训练更深的神经网络太困难。
- 兴趣再次高涨于神经网络,由 2004 年由Geoffrey Hinton领导的研究团队率先使用受限玻尔兹曼机(RBM)取得一系列突破,创造了具有多层的神经网络;他们将这种方法称为深度学习。在 10 年内,深度学习从一种小众技术发展到主导每一个人工智能竞赛。RBM 是这一巨大突破的一部分,使得 Hinton 和其他人在多种图像和语音识别问题上取得世界纪录成绩。
- 在这一部分中,我们将研究 RBM 的工作原理,如何实现它们以及如何将它们结合成深度信念网络。
一台受限玻尔兹曼机看起来很像是神经网络的一个单层。有一组输入节点与另一组输出节点相连:
图 1。受限玻尔兹曼机
输出节点被激活的方式也与自编码器完全相同。每个输入节点和输出节点之间有一个权重,每个输入节点的激活乘以这个权重映射矩阵,然后应用偏置向量,并且每个输出节点的总和将通过一个 sigmoid 函数。
使得受限玻尔兹曼机与众不同的是激活代表的内容、我们对它们的思考方式以及它们的训练方式。首先,当谈论 RBM 时,我们不是谈论输入和输出层,而是将层称为可见层和隐藏层。这是因为在训练时,可见节点代表我们已知的信息,而隐藏节点将旨在代表生成可见数据的一些变量。这与自编码器形成对比,自编码器的输出层不再明确地代表任何东西,只是通过信息传递的一种受限空间。
学习受限玻尔兹曼机的权重基础于统计物理学,并使用基于能量的模型(EBM)。在这些模型中,每个状态都经历一个能量函数,它与状态发生概率相关。如果能量函数返回一个高值,我们期望这种状态不太可能发生,很少发生。相反,能量函数的低结果意味着一个更稳定的状态,会更频繁发生。
一个很好的直观思考能量函数的方式是想象将大量的弹跳球扔进一个箱子中。起初,所有的球都具有很高的能量,因此会弹得很高。这里的状态是所有球的位置和它们关联速度的一个时间点快照。当球在弹跳时,这些状态将会非常短暂;它们只会存在片刻,因为球的移动范围很大,很不可能再次出现。但是当球开始平静下来,当能量离开系统时,一些球将开始越来越静止。这些状态一旦发生一次就稳定了,一旦发生就不会停止。最终,当球停止弹跳并且都变成静止时,我们有一个完全稳定的状态,具有很高的概率。
- 以应用于受限波尔兹曼机的例子,考虑学习一组蝴蝶图像的任务。我们在这些图像上训练我们的 RBM,并且希望它对任何蝴蝶图像分配低能量值。但是当给出来自不同集合的图像,比如汽车时,它会给它分配一个高能量值。相关的对象,如蛾子、蝙蝠或鸟,可能具有中等能量值。
- 如果我们定义了一个能量函数,那么给定状态的概率就如下所示:
- 在这里,v 是我们的状态,E 是我们的能量函数,Z 是分区函数;v 的所有可能配置的总和定义如下:
霍普菲尔德网络和波尔兹曼机
- 在我们进一步讨论受限波尔兹曼机之前,让我们简要谈谈霍普菲尔德网络;这应该有助于我们对如何到达受限波尔兹曼机有更多的理解。霍普菲尔德网络也是基于能量的模型,但与受限波尔兹曼机不同,它只有可见节点,并且它们都是相互连接的。每个节点的激活始终为-1 或+1。
图 2. 霍普菲尔德网络,所有输入节点都相互连接。 - 在运行霍普菲尔德网络(或 RBM)时,您有两个选项。第一个选项是您可以将每个可见节点的值设置为您正在触发的数据项的相应值。然后,您可以触发连续的激活,在每次激活时,每个节点的值都根据其连接到的其他可见节点的值进行更新。另一个选项是仅随机初始化可见节点,然后触发连续的激活,以产生其已经训练过的数据的随机示例。这通常被称为网络做白日梦。
- 下一个时间步的每个可见节点的激活定义如下:
- 在这里,W 是一个矩阵,定义了时间步骤 t 时每个节点 v 之间的连接强度。然后对 a 应用阈值规则,得到 v 的新状态:
- 节点之间的权重 W 可以是正的也可以是负的,在激活时会导致节点相互吸引或排斥。霍普菲尔德网络还有一个连续变体,它只是用 tanh 函数替换了阈值函数。
- 该网络的能量函数如下:
- 用矩阵表示,如下所示:
- 方程中的是因为我们要遍历每对 i 和 j,因此重复计算每个连接(当 i=1 且 j=2 时,然后当 i=2 且 j=1 时又计算一次)。
- 这里可能出现的问题是:为什么只有可见节点的模型?我会给它激活,然后触发一些状态更新。但是这个新状态给我提供了什么有用的信息呢?能量基模型的特性在这里变得有趣。不同的 W 配置将改变与状态 v 相关的能量函数。如果我们将网络状态设置为具有高能量函数的东西,即不稳定状态(想象一下许多弹跳的球);网络会在连续的迭代中移动到一个稳定状态。
- 如果我们对数据集训练霍普菲尔德网络,学习得到一个对数据集中每个条目都有低能量的 W,然后我们可以从数据中创建一个损坏的样本,比如,通过随机交换一些输入的正负状态。因为损坏使得这些样本不太可能是原数据集的成员,所以这些损坏的样本可能现在处于高能量状态。如果我们激活网络的可见节点上的损坏样本,运行网络的更多迭代直到达到低能量状态;那么网络有很大的可能性已经重构了原始未损坏的模式。
- 这导致 Hopfield 网络的一个用途是拼写纠正;你可以在单词库上对其进行训练,其中包含单词中使用的字母作为输入。然后,如果给出一个拼写错误的单词,它可能能够找到正确的原始单词。Hopfield 网络的另一个用途是作为内容寻址内存。计算机内存和人类内存之间的一个重要区别是,计算机内存是用地址存储的。如果计算机想要检索内存,它必须知道存储它的确切位置。另一方面,人类记忆可以给出该记忆的部分内容,该内容的特性可以用来恢复其余部分。例如,如果我需要记住我的密码,我知道我正在寻找的内容以及该内容的属性,一个四位数;我的大脑利用这一点返回值。
- Hopfield 网络允许您存储内容寻址内存,这导致一些人推测人类记忆系统可能像 Hopfield 网络一样运作,人类的梦境是学习权重的尝试。
- Hopfield 网络的最后一个用途是,它可以用于解决优化任务,例如旅行推销员任务。可以定义能量函数来表示要优化的任务的成本,网络的节点表示要优化的选择。同样,只需最小化网络权重的能量函数即可。
Boltzmann 机器
- Boltzmann 机器也被称为随机 Hopfield 网络。在 Hopfield 网络中,节点激活是基于阈值设置的;但在 Boltzmann 机器中,激活是随机的。Boltzmann 机器中的节点值始终设置为 +1 或 -1。节点处于状态 +1 的概率定义如下:
- 这里,a[i] 是针对 Hopfield 网络定义的该节点的激活。
为了学习我们的 Boltzmann 机器或 Hopfield 网络的权重,我们希望最大化给定 W 的数据集的可能性,这简单地是每个数据项的可能性的乘积:
这里,W 是权重矩阵,x*^((n))* 是大小为 N 的数据集 x 的第 n 个样本。现在让我们用来自我们的 Boltzmann 机器的实际可能性替换 :
这里,Z 如下方程所示:
如果您查看我们能量函数和 Z 的原始定义,那么x’应该是基于概率分布p(x)的每个可能配置的x。我们现在的模型中有 W 的一部分,因此分布将更改为。不幸的是,如果不是完全棘手的,至少是计算成本太高,无法计算的。我们需要在所有可能的 W 的所有可能的 x 的配置中进行计算。
计算这种难以处理的概率分布的一种方法是所谓的蒙特卡罗采样。这涉及从分布中取大量样本,并使用这些样本的平均值来近似真实值。我们从分布中取的样本越多,它的准确性就越高。假设无限数量的样本将完全符合我们想要的数量,而 1 将是一个非常差的近似值。
由于概率的乘积可能变得非常小,因此我们将使用对数概率;另外,让我们也包括Z的定义:
这里,x’是从网络学习的概率分布中获取的网络状态样本。如果我们对节点 i 和 j 之间的单个权重取这个梯度,它看起来像这样:
这里,在所有 N 个样本中只是节点 i 和 j 之间的相关性。另一种写法是对所有 N 个样本,对于每个权重i和j,可以写成这样:
这个方程可以理解为学习的两个阶段,被称为正相和负相或者,更具诗意地说,醒和睡眠。在正相中,根据我们所给的数据增加权重。在负相中,,我们从模型中根据当前权重抽取样本,然后将权重远离该分布。这可以被认为是减少模型生成的项目的概率。我们希望我们的模型尽可能地反映数据,因此我们希望减少模型生成的选择。如果我们的模型产生的图像与数据完全相同,那么这两个术语将互相抵消,达到平衡。
玻尔兹曼机和霍普菲尔德网络可用于优化和推荐系统等任务。它们需要大量的计算资源。必须测量每个节点之间的相关性,然后对模型进行每一步训练时的蒙特卡洛样本的范围。此外,它可以学习的模式种类有限。如果我们在图像上训练以学习形状,它无法学习位置不变的信息。图像左侧的蝴蝶与图像右侧的蝴蝶完全不同。在第五章图像识别中,我们将看一下卷积神经网络,它提供了这个问题的解决方案。
受限玻尔兹曼机
受限玻尔兹曼机与玻尔兹曼机相比进行了两项改变:第一是添加了隐藏节点,每个节点都连接到每个可见节点,但彼此不连接。 第二是删除了可见节点之间的所有连接。这导致在给定隐藏层的情况下,可见层中的每个节点都是条件独立的。给定可见层后,隐藏层中的节点也是条件独立的。我们现在还将向可见和隐藏节点添加偏置项。玻尔兹曼机也可以在每个节点上训练有偏置项,但这在等式中被忽略了以便简化符号。
- 由于我们拥有的数据只针对可见单元,我们的目标是通过训练找到隐藏单元的配置,当与可见单元结合时,可以导致低能态。在我们的受限玻尔兹曼机中,状态x现在是可见和隐藏节点的完整配置。因此,我们将能量函数参数化为 E(v, h)。它现在看起来像这样:
- 在这里,a 是可见节点的偏置向量,b 是隐藏节点的偏置向量,W 是可见和隐藏节点之间的权重矩阵。此处, 是这两个向量的点积,等价于。现在我们需要对新能量函数计算出的偏置和权重进行梯度下降。
- 由于层之间的条件独立性,我们现在有这个:
- 这两个定义将用于归一化常数 Z。由于我们不再有可见节点之间的连接,我们的 发生了很大的变化:
- 在这里,i 遍历每个可见节点,j 遍历每个隐藏节点。如果我们对不同参数取梯度,那么最终你会得到这个:
与以前一样,是通过从分布中取蒙特卡洛样本来近似的。这最后三个方程给出了我们迭代地训练给定数据集的所有参数的完整方法。训练将是通过这些梯度以某个学习速率更新我们的参数的情况。
从概念层面上再次说明这里发生了什么是值得的。v 表示可见变量,即我们正在学习的来自世界的数据。h 表示隐藏变量,即我们将训练以生成可见变量的变量。隐藏变量并不明确地代表任何东西,但通过训练和最小化系统中的能量,它们最终应该找到我们正在查看的分布的重要组成部分。例如,如果可见变量是一系列电影,如果一个人喜欢这部电影,则其值为 1,如果不喜欢,则为 0,那么隐藏变量可能会表示电影的流派,如恐怖片或喜剧片,因为人们可能有流派偏好,所以这是一种编码人们口味的有效方式。
如果我们随机生成隐藏变量的样本,然后基于此激活可见变量,那么它应该给我们一个看起来合理的电影口味集。同样,如果我们将可见变量设置为在隐藏和可见节点的连续激活过程中的随机电影选择,那么它应该使我们找到一个更合理的选择。
在 TensorFlow 中的实现
现在我们已经通过了数学,让我们看看它的实现是什么样子的。为此,我们将使用 TensorFlow。TensorFlow 是一个谷歌开源数学图形库,用于深度学习很受欢迎。它没有内置的神经网络概念,比如网络层和节点,这是一个更高级别的库,比如 Keras 才有;它更接近于像 Theano 这样的库。之所以选择它,是因为能够直接处理网络底层的数学符号,使用户能够更好地理解他们在做什么。
TensorFlow 可以直接通过 pip
安装,使用命令 pip install tensorflow
安装 CPU 版本,或者如果您有 NVidea GPU 启用的机器,则使用命令 pip install tensorflow-gpu
安装 GPU 版本。
我们将构建一个小型的受限玻尔兹曼机,并对其进行 MNIST 手写数字集的训练。我们将比可见节点少的隐藏节点数,这将迫使 RBM 学习输入中的模式。训练的成功将通过网络在经过隐藏层后重构图像的能力来衡量;为此,我们将使用原始图像与我们的重构之间的均方误差。完整的代码示例在 GitHub 仓库 github.com/DanielSlater/PythonDeepLearningSamples
的 restricted_boltzmann_machine.py
文件中。
由于 MNIST 数据集被如此广泛地使用,TensorFlow 有一种很好的内置方式来下载和缓存 MNIST 数据集。只需简单地调用以下代码即可完成:
from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets("MNIST_data/")
这将把所有 MNIST 数据下载到 "MNIST_data/"
目录下的 MNIST_data
文件夹中,如果还没有。mnist
对象有 train
和 test
属性,允许您访问 NumPy 数组中的数据。MNIST 图像都是 28x28 大小的,即每个图像有 784 个像素。我们将为我们的 RBM 需要每个像素一个可见节点:
input_placeholder = tf.placeholder("float", shape=(None, 784))
TensorFlow 中的占位符对象表示在使用期间将传递到计算图中的值。在这种情况下,input_placeholder
对象将保存我们给它的 MNIST
图像的值。"float"
指定了我们将传递的值的类型,shape
定义了维度。在这种情况下,我们想要 784 个值,每个像素一个,None
维度用于批处理。有一个 None 维度意味着它可以是任何大小;因此,这将允许我们发送可变大小的 784 长度的数组的批次:
weights = tf.Variable(tf.random_normal((784, 300), mean=0.0, stddev=1./784))
tf.variable
表示计算图上的变量。这是前述方程中的 W。传递给它的参数是变量值应该如何首先初始化的方式。在这里,我们将其初始化为一个大小为 784x300 的正态分布,即可见节点到隐藏节点的数量:
hidden_bias = tf.Variable(tf.zeros([300])) visible_bias = tf.Variable(tf.zeros([784]))
这些变量将是我们前述方程中的 a
和 b
;它们被初始化为全部从值为 0 开始。现在我们将编写网络的激活:
hidden_activation = tf.nn.sigmoid(tf.matmul(input_placeholder, weights) + hidden_bias)
这代表了在前述方程中隐藏节点的激活,。应用 sigmoid
函数后,这个激活可以被放入二项分布中,以便隐藏层中的所有值都变为 0 或 1,概率由给定;但事实证明,RBM 的训练与原始概率一样好。因此,没有必要通过这种方式复杂化模型:
visible_reconstruction = tf.nn.sigmoid(tf.matmul(hidden_activation, tf.transpose(weights)) + visible_bias)
现在我们有了可见层的重构,。根据方程的规定,我们给它 hidden_activation
,从中我们得到了可见层的样本:
final_hidden_activation = tf.nn.sigmoid(tf.matmul(visible_reconstruction, weights) + hidden_bias)
现在我们计算我们需要的最终样本,来自我们的visible_reconstruction
的隐藏节点的激活。这相当于方程中的 。我们可以继续使用连续迭代的隐藏和可视激活来从模型中获取一个更加无偏的样本。但只进行一次旋转就足够用于训练:
Positive_phase = tf.matmul(tf.transpose(input_placeholder), hidden_activation) Negative_phase = tf.matmul(tf.transpose(visible_reconstruction), final_hidden_activation)
现在我们计算正相位和负相位。第一阶段是我们的 input_placeholder
中的样本和第一个 hidden_activation
之间的相关性,。然后,负相位获取 visible_reconstruction
,和final_hidden_activation
之间的相关性,:
LEARING_RATE = 0.01 weight_update = weights.assign_add(LEARING_RATE * (positive_phase – negative_phase))
在我们的weights
变量上调用assign_add
会创建一个操作,当运行时,会将给定的数量添加到变量中。这里,0.01 是我们的学习率,我们通过它来缩放正相位和负相位:
visible_bias_update = visible_bias.assign_add(LEARING_RATE * tf.reduce_mean(input_placeholder - visible_reconstruction, 0)) hidden_bias_update = hidden_bias.assign_add(LEARING_RATE * tf.reduce_mean(hidden_activation - final_hidden_activation, 0))
现在我们创建缩放隐藏和可视偏置的操作。这些也会被我们的 0.01 学习率缩放:
train_op = tf.group(weight_update, visible_bias_update, hidden_bias_update)
调用tf.group
创建一个新操作,当调用时会同时执行所有的操作参数。我们总是希望同时更新所有的权重,所以创建一个单独的操作是有意义的:
loss_op = tf.reduce_sum(tf.square(input_placeholder - visible_reconstruction))
这个loss_op
将为我们提供有关我们的训练效果的反馈,使用 MSE。请注意,这仅用于信息;不会针对此信号进行反向传播。如果我们想将这个网络作为一个纯自动编码器来运行,我们将在这里创建一个优化器,并激活它以最小化loss_op
:?
session = tf.Session() session.run(tf.initialize_all_variables())
然后,我们创建一个将用于运行计算图的会话对象。调用tf.initialize_all_variables()
时,所有内容都会初始化到图中。如果你在 GPU 上运行 TensorFlow,这是硬件首先被接口化的地方。现在我们已经为 RBM 创建了每一步,让我们经历几个时代的 MNIST 运行,并看看它学到了多少:
current_epochs = 0 for i in range(10): total_loss = 0 while mnist.train.epochs_completed == current_epochs: batch_inputs, batch_labels = mnist.train.next_batch(100) _, reconstruction_loss = session.run([train_op, loss_op], feed_dict={input_placeholder: batch_inputs}) total_loss += reconstruction_loss print("epochs %s loss %s" % (current_epochs, reconstruction_loss)) current_epochs = mnist.train.epochs_completed
每次我们调用mnist.train.next_batch(100)
,就会从mnist
数据集中检索 100 张图片。在每个时代结束时,mnist.train.epochs_completed
会增加 1,所有训练数据都会重新洗牌。如果你运行这个,你可能会看到类似这样的结果:
epochs 0 loss 1554.51 epochs 1 loss 792.673 epochs 2 loss 572.276 epochs 3 loss 479.739 epochs 4 loss 466.529 epochs 5 loss 415.357 epochs 6 loss 424.25 epochs 7 loss 406.821 epochs 8 loss 354.861 epochs 9 loss 410.387 epochs 10 loss 313.583
现在我们可以通过在mnist
数据上运行以下命令来看看图像重构是什么样的:
reconstruction = session.run(visible_reconstruction, feed_dict={input_placeholder:[mnist.train.images[0]]})
这里有一些使用 300 个隐藏节点的重建图像的示例:
图 3. 使用不同数量的隐藏节点对受限玻尔兹曼机进行数字的重构
正如您所见,使用 300 个隐藏节点,不到像素数量的一半,它仍然可以几乎完美地重建图像,只有边缘周围有一点模糊。但是随着隐藏节点数量的减少,重建的质量也会降低。将隐藏节点减少到只有 10 个时,重建的图像可能会产生对于人眼来说看起来像是错误的数字,例如图 3 中的 2 和 3。
深信度网络
如果我们想象我们的 RBM 正在学习一组生成我们可见数据的潜在变量,并且我们感到好奇,我们可能会想知道:我们是否可以学习第二层生成隐藏层潜在变量的潜在变量?答案是肯定的,我们可以将先前训练的 RBM 堆叠在一起,以便学习有关可见数据的二阶、三阶、四阶等信息。这些连续的 RBM 层使网络能够学习越来越不变的表示底层结构:
图 4 深信度网络,包含许多链接的 RBM
这些堆叠的 RBM 被称为深度信度网络,是 Geoffrey Hinton 在他的 2002 年论文《通过最小化对比散度训练专家产品》中首次在 MNIST 上取得突破性成果时使用的深度网络。他发现有用的确切技术是在数据上训练连续的 RBM,每一层的尺寸都只稍微减小。一旦一个层训练到重建误差不再改善的程度,它的权重就被冻结,然后在其上堆叠一个新的 RBM,并再次训练直到误差率收敛。一旦整个网络训练完毕,最后添加一个监督层,以将最终 RBM 的隐藏层映射到数据的标签。然后使用整个网络的权重构建标准的深度前馈神经网络,使得这些预先计算的深度信度网络的权重能够通过反向传播进行更新。
起初,这些方法效果很好,但随着时间的推移,用于训练标准前馈网络的技术已经改进,RBMs 不再被认为是图像或语音识别的最佳方法。它们还存在一个问题,就是由于其两阶段性质,它们的训练速度可能会慢得多。但是它们在诸如推荐系统和纯无监督学习等方面仍然非常受欢迎。此外,从理论上讲,使用能量模型来学习深度表示是一种非常有趣的方法,并且为许多可以建立在该方法之上的扩展留下了大门。
摘要
在本章中,我们看到了许多实际深度学习实现核心的两种最强大的技术:自动编码器和受限玻尔兹曼机。
对于它们两个,我们都从一个隐藏层的浅层示例开始,并且探索了如何将它们堆叠在一起形成一个深度神经网络,能够自动学习高层次和分层次的特征,而不需要显式的人类知识。
它们都有类似的目的,但有一点小小的实质性差别。
自动编码器可以被看作是我们用来压缩数据的压缩过滤器,以保留其中最具信息量的部分,并能够确定性地重构原始数据的近似。自动编码器是对维度约简和非线性压缩的优雅解决方案,绕过了主成分分析(PCA)技术的限制。自动编码器的优点是,它们可以用作进一步分类任务的预处理步骤,其中每个隐藏层的输出是数据信息表示的可能级别之一,或者是其去噪和恢复版本。另一个巨大的优点是利用重构误差作为一个单点与其余组的不相似性的度量。这样的技术广泛用于异常检测问题,其中我们观察到的内容与内部表示之间的关系是恒定的和确定性的。在时间变化关系或取决于可观察维度的情况下,我们可以分组和训练不同的网络,以便适应,但一旦训练完成,网络就假设这些关系不受随机变化的影响。
另一方面,RBM 使用随机方法对样本进行采样和调整权重,以最小化重构误差。直觉可能是存在一些可见的随机变量和一些隐藏的潜在属性,目标是找出这两组之间的联系。举个例子,在电影评分的情况下,我们可以有一些隐藏的属性,比如电影类型,以及一些随机的观察,比如评分和/或评论。在这样的拓扑结构中,我们还可以将偏差项看作是调整每部电影不同内在流行度的一种方式。如果我们让用户从由哈利·波特、阿凡达、指环王、角斗士和泰坦尼克号组成的集合中评价他们喜欢哪部电影,我们可能会得到一个结果网络,其中两个潜在单元可能代表科幻电影和奥斯卡获奖电影:
可能的 RBM 示例,仅绘制与权重明显不同于 0 的链接。
虽然科幻和奥斯卡获奖的属性是确定性的(实际上,它们是电影的属性),但用户的评级受到概率方式的影响。学习到的权重是表征电影评分的概率分布的参数(例如,哈利·波特获得五星),假设用户喜欢特定的类型(例如,科幻)。
在这种关系不确定的情况下,我们更倾向于使用受限玻尔兹曼机(RBM)而不是自编码器。
总的来说,无监督特征学习是一种非常强大的方法,可以用最少的知识和人为干预来丰富特征工程。
根据一些基准测试([Lee, Pham and Ng, 2009] 和 [Le, Zhou and Ng, 2011])的结果,用于衡量不同特征学习技术准确性的,已经证明无监督特征学习相对于目前的最新技术有所提高。
不过,还存在一些挑战。如果您有一些知识,最好不要将其丢弃。我们可以在初始化阶段以先验的形式嵌入这些知识,在这一阶段我们可以手工设计网络拓扑和初始状态。
此外,由于神经网络本身已经很难解释,并且大多数时候被视为黑匣子,因此至少了解输入特征可能有所帮助。在我们的无监督特征学习中,我们希望直接使用原始数据。因此,理解模型的工作原理变得更加困难。
我们在本书中不会涉及这些问题。我们认为现在下结论还为时过早,深度学习的进一步发展以及人们和企业处理这些应用的方式将会趋于稳定和可靠。