我们在之前所学习的模型大多都是有监督学习,而Auto-Encoders是无监督的,我们先来理解有监督、无监督的概念。
有监督学习(Supervised Learning):对具有概念标记(分类)的训练样本进行学习,以尽可能对样本集外的数据进行标记(分类)预测,相当于有明确的分类目标。有监督学习可分为回归和分类。
通俗的来说:有监督学习就是训练样本的标记信息是已知的,我们完成一个分类任务时,我们是知道要分为哪些类的,只是对数据进行提取属性再直接分类就好。
我们之前所学算法的都是进行的有监督学习。而现实中的问题也有很多的没有明确的分类目标的。
无监督学习(Unsupervised Learning):对没有概念标记(分类)的训练样本进行学习,以发现训练样本集中的结构性知识,所有的标记(分类)都是未知的。无监督学习的典型就是聚类。
通俗的来说:无监督学习就是训练样本的标记信息是未知的,目标是通过对无标记训练样本的学习来揭示数据的内在性质和规律。再通俗来讲就是,当我们想要完成一个分类任务时,我们完全不知道应该分为几类,具体的类别也是未知的。
Auto-Encoders就是无监督的,它的目标是重建自己,它是一个特殊的全连接层,输入和输出的维度是一样的,这样能保证自己能够重建。中间有一个neck(脖子),这样既可以升维也可以降维,这里降到两维的好处是,二维的图片是可视化的,不仅已经降维,而且在空间中还保证了语义的相关性(通过无监督的聚类可以发现)。
在之前我们都是使用PCA降维,PCA在高维数据中寻找方差最大的方向,只选择方差最大的轴。然而,PCA具有线性特性,这对特征维数的提取有很大的限制。Auto-Encoders比PCA降维的效果要好。
如果只在像素级别的重建,便不能发现一些更加深层次的特征,网络可能会记住一些特征,为了防止这种情况出现,我们可以在原输入图片后加入随机噪声累加到原图片上:
在训练的时候随机对某些连接进行断开(通过将该连接的w设置为0),相当于将下一层的得到的信息添加干扰,那么将会迫使网络尽可能的提升还存在的连接的表征能力,降低对多个神经元的依赖程度:
在原始的AutoEncoders中,没有呈现出原有数据的分布,有可能生成的数据是一样的,Adversarial AutoEncoders额外的添加了一个Discriminator(鉴别器),我们希望生成的Z符合真实的Z‘的分布,将真实的和生成的都送到鉴别器计算差距,如果属于希望的分布就输出为1,否则输出为0:
import os
import tensorflow as tf
import numpy as np
from tensorflow import keras
from tensorflow.keras import Sequential, layers
from PIL import Image
from matplotlib import pyplot as plt
tf.random.set_seed(22)
np.random.seed(22)
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
assert tf.__version__.startswith('2.')
def save_images(imgs, name): #把多张图片存到一张图片里去
new_im = Image.new('L', (280, 280))
index = 0
for i in range(0, 280, 28):
for j in range(0, 280, 28):
im = imgs[index]
im = Image.fromarray(im, mode='L')
new_im.paste(im, (i, j))
index += 1
new_im.save(name)
h_dim = 20 #目标维度,将图片的维度最终压缩到这里
batchsz = 512
lr = 1e-3 #learning rate
#加载数据集
(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.
# we do not need label
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)
class AE(keras.Model): #建立Auto-Encoders模型
def __init__(self):
super(AE, self).__init__()
#创建容器,包括两个容器:Encoders,Decoders
# 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)
])
#前向传播过程
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) #优化器
#训练
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, ' loss:',float(rec_loss))
# evaluation
x = next(iter(test_db))
logits = model(tf.reshape(x, [-1, 784]))
x_hat = tf.sigmoid(logits) #(0~1)
# [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) #转化为numpy的图片保存格式
save_images(x_concat, 'ae_images/rec_epoch_%d.png'%epoch)