TensorFlow深度学习实战——稀疏自编码器详解与实现
前言
稀疏自编码器 (Sparse Autoencoder
) 是一种自编码器 (Autoencoder) 变体,旨在通过对隐藏层的激活进行稀疏化约束,使模型学习到更具有代表性的特征。它是自编码器的一种扩展,除了保持自编码器常规的编码与解码过程外,稀疏自编码器还通过限制隐藏层神经元的激活数量,迫使模型仅在少数激活的神经元上进行表示,从而达到稀疏表示的效果。
1. 稀疏自编码器
1.1 引入稀疏自编码器
我们在使用自编码器重建手写数字图像一节中实现的自编码器近似于一个恒等网络,它只是简单地重建输入。自编码器的重点是在像素级别重建图像,唯一的约束是瓶颈层中的单元数。像素级重建主要是一种压缩机制,并不一定保证网络能从数据集中学习到抽象特征,可以通过添加进一步的约束来确保网络从数据集中学习到抽象特征。
在稀疏自编码器中,在重建损失中添加一个稀疏惩罚项,用于确保瓶颈层中的单元在任何给定时间内只激活较少的单元,我们可以在编码器层中添加稀疏惩罚。
1.2 稀疏自编码器的核心思想
稀疏自编码器的目标是在编码器的隐藏层(潜空间表示)上施加一个稀疏性约束,使得大部分神经元在大多数输入样本上保持“非激活”状态,也就是说,只有少数神经元会被激活。这样,模型能够学习到更加紧凑和高效的特征表示。
2. 构建稀疏自编码器
(1) 在编码器的全连接层中添加额外参数 activity_regularizer
用于施加稀疏性约束:
import numpy as np
import tensorflow as tf
import tensorflow.keras as K
import matplotlib.pyplot as plt
from tensorflow.keras import regularizers
batch_size = 256
max_epochs = 50
learning_rate = 1e-3
momentum = 8e-1
hidden_dim = 128
original_dim = 784
(x_train, _), (x_test, _) = K.datasets.mnist.load_data()
x_train = x_train / 255.
x_test = x_test / 255.
x_train = x_train.astype(np.float32)
x_test = x_test.astype(np.float32)
x_train = np.reshape(x_train, (x_train.shape[0], 784))
x_test = np.reshape(x_test, (x_test.shape[0], 784))
training_dataset = tf.data.Dataset.from_tensor_slices(x_train).batch(batch_size)
class SparseEncoder(K.layers.Layer):
def __init__(self, hidden_dim):
super(SparseEncoder, self).__init__()
self.hidden_layer = K.layers.Dense(units=hidden_dim,
activation=tf.nn.relu, activity_regularizer=regularizers.l1(10e-5))
def call(self, input_features):
activation = self.hidden_layer(input_features)
return activation
class SparseDecoder(K.layers.Layer):
def __init__(self, hidden_dim, original_dim):
super(SparseDecoder, self).__init__()
self.output_layer = K.layers.Dense(units=original_dim, activation=tf.nn.relu)
def call(self, encoded):
activation = self.output_layer(encoded)
return activation
class SparseAutoencoder(K.Model):
def __init__(self, hidden_dim, original_dim):
super(SparseAutoencoder, self).__init__()
self.loss = []
self.encoder = SparseEncoder(hidden_dim=hidden_dim)
self.decoder = SparseDecoder(hidden_dim=hidden_dim, original_dim=original_dim)
def call(self, input_features):
encoded = self.encoder(input_features)
reconstructed = self.decoder(encoded)
return reconstructed
activity_regularizer
旨在减少网络层的输出,使用稀疏惩罚减少全连接层的权重和偏置,以确保输出尽可能小。TensorFlow
支持三种类型的 activity_regularizer
:
l1
:激活计算为绝对值的总和l2
:激活计算为平方值的总和l1_l2
:激活计算为l1
和l2
项的加权和
(2) 与普通自编码相比,稀疏自编码器的其余代码不变,仅更改编码器,就可以将原始自编码器修改为稀疏自编码器:
def loss(preds, real):
return tf.reduce_mean(tf.square(tf.subtract(preds, real)))
def train(loss, model, opt, original):
with tf.GradientTape() as tape:
preds = model(original)
reconstruction_error = loss(preds, original)
gradients = tape.gradient(reconstruction_error, model.trainable_variables)
gradient_variables = zip(gradients, model.trainable_variables)
opt.apply_gradients(gradient_variables)
return reconstruction_error
def train_loop(model, opt, loss, dataset, epochs=20):
for epoch in range(epochs):
epoch_loss = 0
for step, batch_features in enumerate(dataset):
loss_values = train(loss, model, opt, batch_features)
epoch_loss += loss_values
model.loss.append(epoch_loss)
print('Epoch {}/{}. Loss: {}'.format(epoch + 1, epochs, epoch_loss.numpy()))
model = SparseAutoencoder(hidden_dim=hidden_dim, original_dim=original_dim)
opt = tf.keras.optimizers.Adam(learning_rate=1e-2)
train_loop(model, opt, loss, training_dataset, epochs=max_epochs)
plt.plot(range(max_epochs), model.loss)
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.show()
number = 10 # how many digits we will display
plt.figure(figsize=(20, 4))
for index in range(number):
# display original
ax = plt.subplot(2, number, index + 1)
plt.imshow(x_test[index].reshape(28, 28), cmap='gray')
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
# display reconstruction
ax = plt.subplot(2, number, index + 1 + number)
plt.imshow(model(x_test)[index].numpy().reshape(28, 28), cmap='gray')
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.show()
3. 显式添加正则化项
此外,也可以在损失函数中显式地添加稀疏性的正则化项。为此,需要实现一个稀疏性正则化函数。如果 m m m 是输入模式的总数,则可以定义量 ρ ^ \hat \rho ρ^ 衡量每个隐藏层单元的平均激活次数。核心思想是添加约束 ρ ^ \hat \rho ρ^,使其等于稀疏性参数 ρ \rho ρ,以在损失函数中添加稀疏性的正则化项,使得损失函数变为均方差与正则化稀疏性参数之和。
如果 ρ ^ \hat \rho ρ^ 偏离了 ρ \rho ρ,正则化项会惩罚网络。标准方法是使用 Kullback-Leibler
(KL
) 散度来衡量 ρ \rho ρ 和 ρ ^ \hat \rho ρ^ 之间的差异。
KL
散度 D K L D_{KL} DKL 是一个非对称的度量,用于衡量两个分布之间的差异,在本节中为 ρ \rho ρ 和 ρ ^ \hat \rho ρ^。当 ρ \rho ρ 和 ρ ^ \hat \rho ρ^ 相等时,差异为零;否则,随着 ρ ^ \hat \rho ρ^ 从 ρ \rho ρ 偏离,差异值单调增加:
D K L ( ρ ∣ ∣ ρ ^ ) = ρ l o g ρ ρ ^ + ( 1 − ρ ) l o g 1 − ρ 1 − ρ ^ D_{KL}(\rho || \hat \rho)=\rho log\frac {\rho}{\hat \rho}+(1-\rho)log\frac{1-\rho}{1-\hat \rho} DKL(ρ∣∣ρ^)=ρlogρ^ρ+(1−ρ)log1−ρ^1−ρ
将其加入损失函数中,以隐式地包括稀疏项。为稀疏项 ρ \rho ρ 固定一个常数值,并通过编码器输出计算 ρ ^ \hat \rho ρ^。
相关链接
TensorFlow深度学习实战(1)——神经网络与模型训练过程详解
TensorFlow深度学习实战(2)——使用TensorFlow构建神经网络
TensorFlow深度学习实战(3)——深度学习中常用激活函数详解
TensorFlow深度学习实战(4)——正则化技术详解
TensorFlow深度学习实战(5)——神经网络性能优化技术详解
TensorFlow深度学习实战(6)——回归分析详解
TensorFlow深度学习实战(7)——分类任务详解
TensorFlow深度学习实战(8)——卷积神经网络
TensorFlow深度学习实战(9)——构建VGG模型实现图像分类
TensorFlow深度学习实战(15)——编码器-解码器架构
TensorFlow深度学习实战(16)——注意力机制详解
TensorFlow深度学习实战(17)——主成分分析详解
TensorFlow深度学习实战(18)——K-means 聚类详解
TensorFlow深度学习实战(19)——受限玻尔兹曼机
TensorFlow深度学习实战(20)——自组织映射详解
TensorFlow深度学习实战(21)——Transformer架构详解与实现
TensorFlow深度学习实战(22)——从零开始实现Transformer机器翻译
TensorFlow深度学习实战(23)——自编码器详解与实现