当前位置: 首页 > news >正文

图机器学习(6)——图自编码器

图机器学习(6)——图自编码器

    • 0. 自编码器
    • 1. 构建自编码器
    • 2. 去噪自编码器
    • 3. 图自编码器

0. 自编码器

自编码器是一个处理高维数据集的强大工具,近年来,自编码器随着神经网络算法的发展而获得广泛应用。这种工具不仅能压缩稀疏表示,还可作为生成模型的基础架构。
自编码器是一种输入输出相同的特殊神经网络,但在隐藏层中有较少的单元。简言之,它是一个使用显著较少的变量或自由度来重建其输入的神经网络。
由于自编码器不需要标签数据集,因此可以看作是一种无监督学习和降维技术。然而,与主成分分析和矩阵分解等其他技术不同,自编码器可以通过神经元的非线性激活函数学习非线性变换。

自编码器

上图展示了一个简单的自编码器示例。可以看到,自编码器通常由两个核心组件构成:

  • 编码器网络:通过一个或多个隐藏单元处理输入数据,将其映射为维度缩减的编码表示(欠完备自编码器)或施加稀疏约束的表示(过完备正则化自编码器)
  • 解码器网络:从中间层的编码表示重构原始输入信号

整个编码器-解码器结构的训练目标是最小化网络重构输入的误差。完整定义自编码器需要指定损失函数,输入与输出间的误差可通过不同指标衡量——事实上,如何选择恰当的"重构误差"形式是构建自编码器的关键环节。常用的重构误差损失函数包括:均方误差、平均绝对误差、交叉熵和KL散度。
接下来,我们将从基础概念入手,从零开始构建自编码器,并将这些概念应用于图结构数据。

1. 构建自编码器

我们将从基础的自编码器实现开始——即构建一个简单的前馈神经网络来重构其输入。我们将把这个模型应用于 Fashion-MNIST 数据集,Fashion-MNIST 数据集类似于 MNIST 数据集,但包含的是黑白服装图像。
Fashion-MNIST 包含 10 个类别,由 60000 张训练图像和 10000 张测试图像组成,每张都是 28×28 像素的灰度图,分别对应以下服装品类:T恤、裤子、套头衫、连衣裙、外套、凉鞋、衬衫、运动鞋、包包和踝靴。相比原始 MNIST 数据集,Fashion-MNIST 任务更具挑战性,常被用作算法基准测试。

(1) 使用 Keras 导入 Fashion-MNIST 数据集:

(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()

(2) 最佳实践是将输入数据缩放到 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)

(3) 绘制训练集中的样本,以直观了解我们所处理的输入类型:

classes = {0:"T-shirt/top",1: "Trouser",2: "Pullover",3: "Dress",4: "Coat",5: "Sandal",6: "Shirt",7: "Sneaker",8: "Bag",9: "Ankle boot", 
}
n = 6
plt.figure(figsize=(20, 4))
for i in range(n):# display originalax = 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()

在以上代码中,classes 变量定义了整数标签与服装类别的映射关系(例如:0 对应T恤,1 对应裤子,2 对应套头衫等):

数据样本

(4) 完成数据导入后,使用 Keras 函数式 API 构建自编码器网络,该 API 相比顺序式 API 具有更强的灵活性和扩展性。首先定义编码器网络结构:

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)

网络由三层相同模式的堆叠组成,每层包含两个核心组件::

  • Conv2D,二维卷积层,通过权值共享机制处理输入特征。卷积运算后使用 ReLU 激活函数进行非线性变换。第一层设置特征图数为 16,第二层和第三层为 8
  • MaxPooling2D,通过在指定窗口(在本节中为 2x2 )上取最大值来对输入进行下采样

通过 Keras API,我们还可以使用 Model 类查看各层如何变换输入数据:

Model(input_img, encoded).summary()

模型摘要信息如下所示,可以看到,编码阶段最终输出 (4,4,8) 的三维张量,较原始输入的 (28,28) 尺寸实现了六倍以上的压缩率:

模型架构

接下来,构建解码器网络。需要特别说明的是,解码器网络的结构设计与编码器并无强制对称性要求,二者的权重参数也相互独立:

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 = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x)

在本节中,解码器网络与编码器结构相似,唯一的不同是,将 MaxPooling2D 的下采样操作替换为 UpSampling2D 上采样层——该层通过在指定窗口(本节为 2×2 )内重复输入值,使张量在每个维度上扩大两倍。

(5) 完成编码器-解码器的网络结构定义后,为了完整配置自编码器,我们还需要指定损失函数。此外,为了构建计算图,Keras 还需要知道应该使用的网络权重优化算法。通常,Keras 需要这两部分信息(损失函数和优化器)编译模型:

autoencoder = Model(input_img, decoded)
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')

(6) 接下来,训练自编码器。KerasModel 类提供的 API 类似于 scikit-learn,使用 fit 方法训练神经网络。需要注意的是,由于自编码器的特性,自编码器的输入输出均为相同数据:

autoencoder.fit(x_train, x_train,epochs=50,batch_size=128,shuffle=True,validation_data=(x_test, x_test),callbacks=[TensorBoard(log_dir='/tmp/autoencoder')])

(7) 一旦训练完成,我们可以通过比较输入图像与其重建版本,来检验网络重建输入的能力,通过 KerasModel 类的 predict 方法计算模型输出:

decoded_imgs = autoencoder.predict(x_test)

下图展示了重建后的图像。可以看到,网络在重建未见过的图像方面表现得相当好,网络能较好地重构测试图像(尤其是宏观特征),虽然压缩过程会丢失细节(如T恤图案),但关键特征已被有效捕捉。

重建结果

(8) 将图像经过编码后的嵌入通过 T-SNE 降维至二维平面进行可视化:

from sklearn.manifold import TSNE
import numpy as np
from matplotlib.cm import tab10tsne = TSNE(n_components=2)
emb2d = tsne.fit_transform(embeddings)
x,y = np.squeeze(emb2d[:, 0]), np.squeeze(emb2d[:, 1])
summary =  pd.DataFrame({"x": x, "y": y, "target": y_test, "size": 10})plt.figure(figsize=(10,8))for key, sel in summary.groupby("target"):plt.scatter(sel["x"], sel["y"], s=10, color=tab10.colors[key], label=classes[key])plt.legend()
plt.axis("off")
plt.show()

运行结果如下所示,可以看到 T-SNE 降维坐标(按样本类别着色)清晰呈现了不同服装的聚类效果,部分类别表现出显著分离性

降维结果

但需要注意的是,自动编码器容易过拟合,因为它们往往会完全重建训练图像,而不具备良好的泛化能力。接下来,我们将学习如何防止过拟合,从而构建更加鲁棒的密集表示。

2. 去噪自编码器

自编码器不仅能将稀疏表示压缩为稠密向量,还广泛用于处理信号,去除噪声并提取相关(特征)信号。这在许多应用中非常有用,特别是在识别异常值和离群值时。

(1) 去噪自编码器是基本自编码器的一个变体。如前一节所述,基本自编码器使用相同图像作为输入和输出进行训练,而去噪自编码器则会在输入中注入不同强度的噪声,同时保持目标输出为纯净信号。实现方式之一是为输入添加高斯噪声:

noise_factor = 0.1
x_train_noisy = x_train + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_train.shape) 
x_test_noisy = x_test + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_test.shape) x_train_noisy = np.clip(x_train_noisy, 0., 1.)
x_test_noisy = np.clip(x_test_noisy, 0., 1.)

(2) 训练时使用带噪输入数据,但目标输出仍为原始干净图像:

noisy_autoencoder.fit(x_train_noisy, x_train,epochs=50,batch_size=128,shuffle=True,validation_data=(x_test_noisy, x_test))

该方法通常适用于大规模数据集,此时过拟合噪声的风险较低。对于小规模数据集,为避免网络"记忆"静态噪声模式(即学习带噪图像到干净图像的固定映射),可以使用 GaussianNoise 层动态注入随机噪声。通过这种方式,每个训练 epoch 注入的噪声会动态变化,从而防止网络学习训练集上的固定噪声模式。为此,需要调整网络结构:

input_img = Input(shape=(28, 28, 1))
noisy_input = GaussianNoise(0.1)(input_img)
x = Conv2D(16, (3, 3), activation='relu', padding='same')(noisy_input)

不同之处在于,噪声输入从静态噪声(训练过程中不变)转变为动态变化(每个 epoch 重新生成),从而有效阻止网络记忆噪声模式。
GaussianNoise 层是正则化层的一种,这类层通过引入随机扰动来抑制神经网络过拟合。GaussianNoise 层使得模型更具鲁棒性,能够更好地进行泛化,避免自编码器学习恒等函数。
Dropout 层是另一种常见的正则化层,它以概率 p 随机置零部分输入,并将剩余输入缩放 1/(1-p) 倍,从而保持所有单元的总激活强度(统计上)不变。
Dropout 层相当于随机“关闭”层之间的某些连接,以减少输出对特定神经元的依赖。需要注意的是,正则化层只在训练过程中起作用,而在测试时,会自动退化为恒等映射。
在下图中,我们比较了噪声输入(输入)在未正则化训练的网络和带有 GaussianNoise 层的网络中的重建效果。可以看到,(例如比较裤子的图像)带有正则化的模型通常能够更有效地进行重建,并且能够重建无噪声的输出。

重建效果对比

正则化层通常用于处理容易过拟合的深度神经网络,并能够学习自编码器的恒等函数。通常会引入 Dropout 层或高斯噪声层,重复由正则化层和可学习层组成的类似模式,这种结构通常称为堆叠去噪层。

3. 图自编码器

理解了自编码器的基本概念后,我们就可以将这一框架应用于图结构。虽然网络结构仍然可以分解为“编码器-解码器”架构,并在中间嵌入低维表示,但在处理图数据时,损失函数的定义需要格外谨慎。首先,我们需要调整重构误差,使其能够适应图结构的特性。为此,我们首先引入一阶邻接和高阶邻接的概念。
将自编码器应用于图结构时,网络的输入和输出应当是图的某种表示,例如邻接矩阵。此时,重构损失可以定义为输入矩阵与输出矩阵之差的 Frobenius 范数。然而,将自编码器应用于图结构和邻接矩阵时,会出现两个关键问题:

  • 边的存在表示两个顶点之间存在关系或相似性,但边的缺失通常并不代表顶点之间的不相似性
  • 邻接矩阵极为稀疏,因此模型自然会倾向于预测 0,而不是正值。

为了应对图结构的这些特殊性,在定义重构损失时,我们需要对非零元素的预测误差施加更大的惩罚,而对零元素的误差惩罚较小。这可以通过以下损失函数实现:
L2nd=∑i=1n∥(X~i−Xi)⊙bi∥L_{2nd}=∑_{i=1}^n∥(\tilde X_i−X_i)⊙b_i∥ L2nd=i=1n(X~iXi)bi
其中,⊙⊙ 表示 Hadamard 逐元素乘积,如果节点 iiijjj 之间存在边,则 bijb_{ij}bij1,否则为 0。该损失函数确保共享邻域(即邻接向量相似)的顶点在嵌入空间中也彼此接近。因此,上述公式能够自然地在重构图中保持二阶邻接。
另一方面,我们还可以在重构图中增强一阶邻接,使得相连的节点在嵌入空间中彼此靠近。这一目标可以通过以下损失函数实现:
L1th=∑i,j=1nSij∣∣yj−yi∣∣22L_{1th}=\sum_{i,j=1}^nS_{ij}||y_j-y_i||_2^2 L1th=i,j=1nSij∣∣yjyi22
其中,yiy_iyiyjy_jyj 分别是节点 iiijjj 在嵌入空间中的表示。该损失函数迫使相邻节点在嵌入空间中彼此接近。具体来说,如果两个节点紧密相连(即 SijS_{ij}Sij 较大),那么它们在嵌入空间中的距离 ∥yj−yi∥∥y_j−y_i∥yjyi 应当较小,以保持损失函数的值较低。
这两个损失函数可以结合成一个统一的损失函数。此外,为了防止过拟合,可以添加一个与权重系数范数成正比的正则化损失:
Ltotal=L2nd+α⋅L1st+v⋅Lreg=L2nd+α⋅L1st+v⋅∥W∥F2L_{total}=L_{2nd}+α\cdot L_{1st}+v\cdot L_{reg}=L_{2nd}+α\cdot L_{1st}+v\cdot ∥W∥_F^2 Ltotal=L2nd+αL1st+vLreg=L2nd+αL1st+vWF2
其中,WWW 表示网络中所有的权重。上述公式由 Wang 等人于 2016 年提出,现称为结构深度网络嵌入 (Structural Deep Network Embedding, SDNE)。
尽管上述损失函数可以用 TensorFlowKeras 实现,但我们可以直接在 GEM 包中使用该网络的实现。提取节点嵌入:

G=nx.karate_club_graph()
sdne=SDNE(d=2, beta=5, alpha=1e-5, nu1=1e-6, nu2=1e-6,K=3,n_units=[50, 15,], rho=0.3, n_iter=10,xeta=0.01,n_batch=100,modelfile=['enc_model.json','dec_model.json'],weightfile=['enc_weights.hdf5','dec_weights.hdf5'])
sdne.learn_embedding(G)
embeddings = m1.get_embedding()

虽然这类图自编码器功能强大,但在处理大型图数据时会面临挑战。因为在大型网络中,自编码器的输入是邻接矩阵,其每行元素数量与图中节点数相同(可能达到数百万甚至数千万量级)。

http://www.lryc.cn/news/588648.html

相关文章:

  • 【电脑】显卡(GPU)的基础知识
  • 【轨物方案】当补贴退潮,光伏电站如何回归价值本质?
  • MySQL数据库----函数
  • 【PTA数据结构 | C语言版】二叉树前序序列化
  • 跨平台游戏引擎 Axmol-2.7.1 发布
  • git起步
  • 微信小程序翻书效果
  • 《汇编语言:基于X86处理器》第8章 高级过程(1)
  • 基于UDP/IP网络游戏加速高级拥塞控制算法(示意:一)
  • 力扣-使用双指针的方法的题们(持续更新中。。。
  • 一、CV_图像分类简介
  • Typecho插件开发:优化文章摘要处理短代码问题
  • 基于redis的分布式锁 lua脚本解决原子性
  • 银河麒麟服务器版挂载镜像文件
  • sqli-labs靶场通关笔记:第18-19关 HTTP头部注入
  • exe直接传输会导致文件损坏
  • 【html常见页面布局】
  • AI应用服务
  • Axios 完整功能介绍和完整示例演示
  • 分布式全局唯一ID生成:雪花算法 vs Redis Increment,怎么选?
  • gRPC实战指南:像国际快递一样调用跨语言服务 —— 解密Protocol Buffer与HTTP/2的完美结合
  • TCP可靠性设计的核心机制与底层逻辑
  • Java基础(八):封装、继承、多态与关键字this、super详解
  • Java全栈工程师面试实录:从电商系统到AIGC的层层递进
  • 通用综合文字识别联动 MES 系统:OCR 是数据流通的核心
  • 在百亿流量面前,让“不存在”无处遁形——Redis 缓存穿透的极限攻防实录
  • 【Ubuntu22.04】repo安装方法
  • 1.2 vue2(组合式API)的语法结构以及外部暴露
  • 如何把手机ip地址切换到外省
  • 【深度学习优化算法】06:动量法