图机器学习(Graph Machine Learning)- 第三章 无监督图学习2 (Unsupervised Graph Learning)- 自编码器

本文介绍了自编码器在无监督图学习中的核心作用,包括基础的AutoEncoder实现、TensorFlow和Keras的结合,以及降噪自编码器增强模型鲁棒性的应用。通过实例演示,展示了如何构建和训练自编码器,以及如何将其扩展到图结构处理,以捕捉二阶和一阶邻近性。

第三章 无监督图学习2 - 自编码器 AutoEncoder


3.2 自编码器 AutoEncoder

自动编码器是一个非常强大的工具,可以有效地帮助数据科学家处理高维数据集。尽管自编码器在大约30年前首次出现,但近年来,随着基于神经网络的算法的普遍兴起,自编码器已经变得越来越普遍。除了让能进行压缩稀疏表示之外,自编码也是生成模型,如著名的生成对抗网络(generative Adversarial Network, GAN) 的的基础。而GAN用Geoffrey Hinton的话来说就是:

“The most interesting idea in the last 10 years in machine learning”

自动编码器是一种输入和输出基本相同的神经网络,但其特征是隐含层中有少量单元。 它是一个经过训练的神经网络,使用显著减少的变量数量和/或自由度来重建其输入。

由于自动编码器不需要标记的数据集,它可以被视为无监督学习和降维技术的一个例子。 然而,不同于其他技术,如主成分分析(PCA)和矩阵分解,由于神经元的非线性激活函数,自动编码器可以学习非线性变换。

在这里插入图片描述

图3.14显示了一个自动编码器的简单示例。 可以看到自动编码器通常是由两部分组成的:

  • 编码器网络,通过一个或多个单元处理输入,并将其映射到一个编码表示,以降低输入的维数(欠完备自动编码器)和/或约束其稀疏性(过完备正则化自动编码器)
  • 解码器网络,从中间层的编码表示中重建输入信号。

然后训练编码器-解码器结构,使整个网络重构输入的能力最小化。为了完全确定一个自编码器,我们需要一个损失函数。输入和输出之间的误差可以使用不同的度量来计算,实际上,在构建自动编码器时,为重构误差选择正确的损失函数是非常关键。

测量重构误差的损失函数通常有均方误差、平均绝对误差、交叉熵和KL散度。

在下面的部分中,我们将展示如何构建一个自编码器,从一些基本概念开始,然后将这些概念应用到图结构中。但在深入讨论之前,我们觉得有必要简要介绍相关框架:TensorFlow和Keras。

3.2.1 TensorFlow和Keras——一个强大的组合

TensorFlow是谷歌在2017年以开源的形式发布的,它现在是一个标准的框架,允许符号计算和差分编程。它基本上允许你构建一个符号结构,描述如何组合输入以产生输出,定义通常称为计算图或有状态数据流图。在这个图中,节点是变量(标量、数组、张量),边表示连接单个操作的输入(边源)到输出(边目标)的操作。

在TensorFlow中,这样的图是静态的(这是与另一个非常流行的框架:torch的主要区别之一),可以通过将数据作为输入输入,清除前面提到的“dataflow”属性来执行。

通过抽象计算,TensorFlow是一个非常通用的工具,可以在多个后端上运行:由cpu、gpu驱动的机器,甚至是由特别设计的处理单元(如tpu)驱动的机器。此外,tensorflow支持的应用程序也可以部署在不同的设备上,从单个和分布式服务器到移动设备。

除了抽象计算之外,TensorFlow还允许对计算图的任何变量进行符号微分,从而产生一个新的计算图,它也可以通过微分产生高阶导数。 这种方法通常被称为符号到符号的导数,它在需要梯度估计(如梯度下降技术)来优化损失函数的情况下确实非常强大。

正如你可能知道的,许多关于参数的优化损失函数的问题是通过反向传播训练任何神经网络的核心。 这当然是TensorFlow在过去几年变得非常流行的主要原因,也是为什么它最初是由谷歌设计和生产的。

深入探究TensorFlow的用法超出了本书的范围,你可以通过专门书籍中的描述找到更多。 在下面的章节中,我们将使用它的一些主要功能,并为您提供构建神经网络的基本工具。

自从上次主要发行版2.x以来,使用TensorFlow构建模型的标准方式是使用Keras API。 Keras本来是一个与TensorFlow有关的外部项目,旨在提供一个通用的、简单的API,以便使用几种不同的编程框架(如TensorFlow、Teano和CNTK)来实现神经网络模型。 它通常抽象了计算图的底层实现,并为你提供了构建神经网络时最常用的层(尽管自定义层也很容易实现)如下:

  • 卷积层
  • 循环层
  • 正则层
  • 损失函数

Keras还发布了了与scikit-learn非常相似的API, scikit-learn是Python生态系统中最流行的机器学习库,这使得数据科学家可以非常容易地在他们的应用程序中构建、训练和集成基于神经网络的模型。

在下一节中,我们将展示如何使用Keras构建和训练一个自动编码器。我们将开始将这些技术应用于图像,以便逐步将关键概念应用于图形结构。

3.2.2 第一个自编码器

我们将以最简单的形式实现一个自编码器,即通过一个经过训练的简单前馈网络来重构其输入。我们将把这个应用到Fashion-MNIST数据集,这是一个类似于著名的MNIST数据集的数据集,MNIST数据集在黑白图像上识别手写数字。

Fashion-MNIST有10个类别,由60k + 10k(训练数据集+测试数据集)个28x28像素的灰度图像组成,代表一种服装(T-shirt, Trouser,Pullover, Dress, Coat, Sandal, Shirt, Sneaker, Bag, and Ankle boot)。与原始MNIST数据集相比,Fashion-MNIST数据集是一个更难的任务,它通常用于基准测试算法。

该数据集已经集成在Keras库中,容易使用以下代码导入:

import tensorflow as tf
from tensorflow.keras.datasets import fashion_mnist
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()

实践中一个常见的做法是将输入图像的灰度级大小调整为1左右(此时激活函数是最有效的),并确保数值数据是单精度(32位)而不是双精度(64位)。这是因为训练神经网络是一个高计算量的过程,通常希望提高速度而不是精度。在某些情况下,精度甚至可以降低到半精度(16位)。我们用以下方法转换输入:

x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.

print (x_train.shape)
print (x_test.shape)
(60000, 28, 28)
(10000, 28, 28)

我们可以通过下面的代码从训练集中绘制一些样本来了解我们正在处理的输入类型:

from matplotlib import pyplot as plt

classes = {
   
   
    0:"T-shirt/top",
    1: "Trouser",
    2: "Pullover",
    3: "Dress",
    4: "Coat",
    5: "Sandal",
    6: "Shirt",
    7: "Sneaker",
    8: "Bag",
    9: "Ankle boot", 
} #类名到0-9之间的映射

n = 6
plt.figure(figsize=(20, 4))
for i in range(n):
    # display original
    ax = plt.subplot(1, n, i + 1)
    plt.imshow(x_test[i])
    plt.title(classes[y_test[i]])
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

plt.show()
# plt.savefig("TrainingSet.png")

在这里插入图片描述
现在我们已经导入了输入,我们可以通过创建编码和解码来构建自编码器网络。我们将使用Keras函数API来实现这一点,它提供了更多的通用性和灵活性。我们首先定义编码网络:

from tensorflow.keras.layers import Flatten, Conv2D, Dropout, MaxPooling2D, UpSampling2D, Input

from tensorflow.keras import Model

input_img = Input(shape=(28, 28, 1))

x = Conv2D(16, (3, 3), activation='relu', padding='same')(input_img)
x = MaxPooling2D((2, 2), padding='same')(x)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
x = MaxPooling2D((2, 2), padding='same')(x)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
encoded = MaxPooling2D((2, 2), padding='same')(x)

# at this point the representation is (4, 4, 8) i.e. 128-dimensional

我们的网络一共三层,每层都是由相同的两层结构块组成:

  • Conv2D,一个应用于输入的二维卷积核,有效地对应于在所有输入神经元之间共享权值。 在应用卷积核后,使用ReLU激活函数对输出进行转换。这个结构被复制到n个隐藏平面,其中n在第一个堆叠层为16,在第二个和第三个堆叠层为8。
  • MaxPooling2D,它通过获取指定窗口(本例中为2x2)上的最大值来对输入进行下采样。

使用Keras API,我们还可以使用Model类来描述这些层是如何转换输入的。

Model(input_img, encoded).summary()
Model: "model"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_1 (InputLayer)        [(None, 28, 28, 1)]       0         
                                                                 
 conv2d (Conv2D)             (None, 28, 28, 16)        160       
                                                                 
 max_pooling2d (MaxPooling2D  (None, 14, 14, 16)       0         
 )                                                               
                                                                 
 conv2d_1 (Conv2D)           (None, 14, 14, 8)         1160      
                                                                 
 max_pooling2d_1 (MaxPooling  (None, 7, 7, 8)          0         
 2D)                                                             
                                                                 
 conv2d_2 (Conv2D)           (None, 7, 7, 8)           584       
                                                                 
 max_pooling2d_2 (MaxPooling  (None, 4, 4, 8)          0         
 2D)                                                             
                                                                 
=================================================================
Total params: 1,904
Trainable params: 1,904
Non-trainable params: 0
________________________________________________________________

可以看到,在编码阶段的最后,我们有一个(4,4,8)张量,它比我们最初的输入(28x28)小6倍多。我们现在可以建立解码器网络了。请注意,编码器和解码器不需要具有相同的结构和/或共享权值:

x = Conv2D(8, (3, 3), activation='relu', padding='same')(encoded)
x = UpSampling2D((2, 2))(x)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
x = UpSampling2D((2, 2))(x)
x = Conv2D(16, (3, 3), activation='relu')(x)
x = UpSampling2D((2, 2))(x)
decoded
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值