本篇是自编码器实战,和之前相同的是数据集加载过程,但需要注意不使用标签信息。此处的自定义类,训练过程等都是换汤不换药,但图片重建是我们之前没接触到的,大家可以认真看一下。对于神经网络的学习关键要明白输入是什么,输出是什么,包括数值形状等。
目录
1.数据集加载:
h_dim = 20
batchsz = 512
lr = 1e-3
(x_train, y_train), (x_test, y_test) = keras.datasets.fashion_mnist.load_data()
x_train, x_test = x_train.astype(np.float32) / 255., x_test.astype(np.float32) / 255.
train_db = tf.data.Dataset.from_tensor_slices(x_train)
train_db = train_db.shuffle(batchsz * 5).batch(batchsz)
test_db = tf.data.Dataset.from_tensor_slices(x_test)
test_db = test_db.batch(batchsz)
print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)
本次数据集选用的是Fashion Mnist,和Mnist相比略显复杂,也是黑白图片。
注意,自编码器不需要标签信息,仅规格化数据信息,构建训练集和测试集时也不需要标签信息。
几个预设参数:
h_dim;经过编码降到的维度数
batchsz 和lr 大家应该比较熟悉了,代表分批大小和学习率
2.自定义AE类:
class AE(keras.Model):
def __init__(self):
super(AE, self).__init__()
# Encoders
self.encoder = Sequential([
layers.Dense(256, activation=tf.nn.relu),
layers.Dense(128, activation=tf.nn.relu),
layers.Dense(h_dim)
])
# Decoders
self.decoder = Sequential([
layers.Dense(128, activation=tf.nn.relu),
layers.Dense(256, activation=tf.nn.relu),
layers.Dense(784)
])
定义编码器encoder和解码器decoder。
编码器encoder子网络实现:输入为长度为784的向量,经过三个全连接层,最终得到长度为h_dim的向量,激活函数都选择用relu。
解码器decoder子网络实现:输入为长度为h_dim的向量,经过三个全连接层,最终得到长度为784的向量,代表了28*28图片打平后的向量,激活函数都选择用relu。
def call(self, inputs, training=None):
# [b, 784] => [b, 10]
h = self.encoder(inputs)
# [b, 10] => [b, 784]
x_hat = self.decoder(h)
return x_hat
此处定义了前向传播过程的实现,即将输入经过编码器,而后经过解码器,最终得到重构输出。
model = AE()
model.build(input_shape=(None, 784))
model.summary()
optimizer = tf.optimizers.Adam(lr=lr)
设定一些参数:
设定网络对象为AE()。规定输入规格。规定使用Adam优化器,并指定学习率。
3.具体训练:
for epoch in range(100):
for step, x in enumerate(train_db):
#[b, 28, 28] => [b, 784]
x = tf.reshape(x, [-1, 784])
with tf.GradientTape() as tape:
x_rec_logits = model(x)
rec_loss = tf.losses.binary_crossentropy(x, x_rec_logits, from_logits=True)
rec_loss = tf.reduce_mean(rec_loss)
grads = tape.gradient(rec_loss, model.trainable_variables)
optimizer.apply_gradients(zip(grads, model.trainable_variables))
if step % 100 ==0:
print(epoch, step, float(rec_loss))
循环100轮,将图片打平,构建梯度下降记录器。
将x输入到网络中,得到重构后的x:x_rec_logits,在x和x_rec_logits之间求损失函数。
from_logits=True实际上是隐式调用Softmax激活函数对其进行优化
最终求出来损失函数的平均值,对所有待优化变量进行梯度更新
4.图片重建:
损失函数在此应用中只能在一定程度上评价学习效果的好坏,我们最终希望获得还原度较高的重构图片。因此从测试集中随机采样测试图片,经过自编码器计算得到重建后的图片, 然后将真实图片与重建图片保存为图片阵列,并可视化,方便比对。
x = next(iter(test_db))
logits = model(tf.reshape(x, [-1, 784]))
#print(logits)
x_hat = tf.sigmoid(logits)
#print(x_hat)
# [b, 784] => [b, 28, 28]
x_hat = tf.reshape(x_hat, [-1, 28, 28])
# [b, 28, 28] => [2b, 28, 28]
x_concat = tf.concat([x, x_hat], axis=0)
x_concat = x_hat
x_concat = x_concat.numpy() * 255.
x_concat = x_concat.astype(np.uint8)
save_images(x_concat, 'D:\桌面\文档\结果\结果果trec_epoch_%d.png'%epoch)
我们看一下此部分代码:大家应该还记得我们每次都有初始化数据这一步骤,即将训练集和测试集中的数据规格化到-1~1,因此我们是根据一个-1~1的数据进行神经网络的训练的,最终经过神经网络的训练结果也是一个-1~1的向量。想要可视化表示,我们就需要像素值这一概念,我们此次使用的是Fashion Mnist数据集,是黑白图片,因此像素值是0~255的一个数值。因此我们需要将神经网络的输出结果进行优化,先利用sigmoid激活函数优化到0~1的数值,经过形状转化,图片合并等一系列操作之后,将其*255,最终得到0~255的像素值,使图片可视化出来。
代码来自于《TensorFlow深度学习》-龙龙老师