自编码器深度分析+定制特征描述子构建初探

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shenziheng1/article/details/84532212

1. 关于tailored 特征描述子

        自从深度学习的繁荣,利用自编码器、孪生网络、对抗网络采用数据驱动的形式设计tailored 特征描述子 成为了计算机视觉领域发展的重要推动力, 这不仅大大削弱了特征工程的压力,而且降低了相关领域学者对于数学基础的要求。 本博文重点在于介绍自编码器在tailored feature方面的潜力。

2. 什么是自编码器(Autoencoder)初步探索

        自动编码器实质是Encode+Decode的过程,实际可以理解为是一种数据压缩+数据重建算法,其中数据的压缩和解压缩函数是数据相关的、有损的、从样本中自动学习的。在大部分提到自动编码器的场合,压缩和解压缩的函数是通过神经网络实现的。

  • 自动编码器是数据相关的(data-specific 或 data-dependent),这意味着自动编码器只能压缩那些与训练数据类似的数据,这维学习某一领域的tailored feature提供了可能。比如,使用人脸训练出来的自动编码器在压缩别的图片,比如树木时性能很差,因为它学习到的特征是与人脸相关的
  • 自动编码器是有损的,意思是解压缩的输出与原来的输入相比是退化的,这潜在有利于去除数据中不一致、非紧致的噪声
  • 自动编码器是从数据样本中自动学习的,这意味着很容易对指定类的输入训练出一种特定的编码器,而不需要完成任何新工作。

        搭建一个自动编码器需要完成下面三样工作:搭建编码器,搭建解码器,设定一个损失函数,用以衡量由于压缩而损失掉的信息。编码器和解码器一般都是参数化的方程,并关于损失函数可导,典型情况是使用神经网络。编码器和解码器的参数可以通过最小化损失函数而优化,例如SGD。自编码器是一个自监督的算法,并不是一个无监督算法。自监督学习是监督学习的一个实例,其标签产生自输入数据。要获得一个自监督的模型,你需要一个靠谱的目标跟一个损失函数,仅仅把目标设定为重构输入可能不是正确的选项。基本上,要求模型在像素级上精确重构输入不是机器学习的兴趣所在,学习到高级的抽象特征才是。事实上,当主要任务是分类、定位之类的任务时,那些对这类任务而言的最好的特征基本上都是重构输入时的最差的那种特征。

        目前自编码器的应用主要有两个方面,第一是数据去噪,第二是为进行可视化而降维。配合适当的维度和稀疏约束,自编码器可以学习到比PCA等技术更有意思的数据投影。对于2D的数据可视化,t-SNE(读作tee-snee)或许是目前最好的算法,但通常还是需要原数据的维度相对低一些。所以,可视化高维数据的一个好办法是首先使用自编码器将维度降低到较低的水平(如32维),然后再使用t-SNE将其投影在2D平面上。

3. 几种典型自编码器调研


        自编码是神经网络的一种,经过训练后能尝试将输入复制到输出。自编码器内部有一个隐藏层 h,可以产生编码(code)表示输入。该网络可以看作由两部分组成:一个由函数 h = f(x) 表示的编码器和一个生成重构的解码器 r = g(h)。如果一个自编码器只是简单地学会将处处设置为 g(f(x)) = x,那么这个自编码器就没什么特别的用处。相反,我们不应该将自编码器设计成输入到输出完全相等。这通常需要向自编码器强加一些约束,使它只能近似地复制,并只能复制与训练数据相似的输入。这些约束强制模型考虑输入数据的哪些部分需要被优先复制,因此它往往能学习到数据的有用特性。

  • 欠完备自编码器

从自编码器获得有用特征的一种方法是限制 h的维度比 x 小,这种编码维度小于输入维度的自编码器称为欠完备undercomplete自编码器。学习欠完备的表示将强制自编码器捕捉训练数据中最显著的特征。学习过程可以简单地描述为最小化一个损失函数L(x,g(f(x))),其中 L 是一个损失函数,惩罚g(f(x)) 与 x 的差异,如均方误差。当解码器是线性的且 L 是均方误差,欠完备的自编码器会学习出与 PCA 相同的生成子空间。这种情况下,自编码器在训练来执行复制任务的同时学到了训据的主元子空间。如果编码器和解码器被赋予过大的容量,自编码器会执行复制任务而捕捉不到任何有关数据分布的有用信息。

  • 正则自编码器

正则自编码器使用的损失函数可以鼓励模型学习其他特性(除了将输入复制到输出),而不必限制使用浅层的编码器和解码器以及小的编码维数来限制模型的容量。这些特性包括稀疏表示、表示的小导数、以及对噪声或输入缺失的鲁棒性。即使模型容量大到足以学习一个无意义的恒等函数,非线性且过完备的正则自编码器仍然能够从数据中学到一些关于数据分布的有用信息。

  • 稀疏自编码器

稀疏自编码器简单地在训练时结合编码层的稀疏惩罚 Ω(h) 和重构误差:L(x,g(f(x))) + Ω(h),其中 g(h) 是解码器的输出,通常 h 是编码器的输出,即 h = f(x)。稀疏自编码器一般用来学习特征,以便用于像分类这样的任务。稀疏正则化的自编码器必须反映训练数据集的独特统计特征,而不是简单地充当恒等函数。以这种方式训练,执行附带稀疏惩罚的复制任务可以得到能学习有用特征的模型。

  • 去噪自编码器

去噪自编码器(denoisingautoencoder, DAE)最小化L(x,g(f(˜ x))),其中 ˜ x 是被某种噪声损坏的 x 的副本。因此去噪自编码器必须撤消这些损坏,而不是简单地复制输入。它是一类接受损坏数据作为输入,并训练来预测原始未被损坏数据作为输出的自编码器。DAE 的训练准则(条件高斯p(x | h))能让自编码器学到能估计数据分布得分的向量场 (g(f(x)) − x) ,这是 DAE 的一个重要特性。

4. 使用Keras建立简单的自编码器

利用全连接层构建简单的自编码器如下:

代码和效果和下图所示:

from keras.layers import Input, Dense
from keras.models import Model
from keras.datasets import mnist
import numpy as np
import matplotlib.pyplot as plt
 
(x_train, _), (x_test, _) = mnist.load_data()
 
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
x_train = x_train.reshape((len(x_train), np.prod(x_train.shape[1:])))
x_test = x_test.reshape((len(x_test), np.prod(x_test.shape[1:])))
print(x_train.shape)
print(x_test.shape)
 
encoding_dim = 32
input_img = Input(shape=(784,))
 
encoded = Dense(encoding_dim, activation='relu')(input_img)
decoded = Dense(784, activation='sigmoid')(encoded)
 
autoencoder = Model(inputs=input_img, outputs=decoded)
encoder = Model(inputs=input_img, outputs=encoded)
 
encoded_input = Input(shape=(encoding_dim,))
decoder_layer = autoencoder.layers[-1]
 
decoder = Model(inputs=encoded_input, outputs=decoder_layer(encoded_input))
 
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
 
autoencoder.fit(x_train, x_train, epochs=50, batch_size=256, 
                shuffle=True, validation_data=(x_test, x_test))
 
encoded_imgs = encoder.predict(x_test)
decoded_imgs = decoder.predict(encoded_imgs)
 
n = 10  # how many digits we will display
plt.figure(figsize=(20, 4))
for i in range(n):
    ax = plt.subplot(2, n, i + 1)
    plt.imshow(x_test[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
 
    ax = plt.subplot(2, n, i + 1 + n)
    plt.imshow(decoded_imgs[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
plt.show()

Dense784-Dense32-Dense784结果如下,其损失loss=0.1044:

Dense784-Dense256-Dense64-Dense256-Dense784结果如下,其损失loss=0.0879:

  • 稀疏自编码器、深层自编码器

如果我们对隐层单元施加稀疏性约束的话,会得到更为紧凑的表达,只有一小部分神经元会被激活。在Keras中,可以通过添加一个activity_regularizer达到对某层激活值进行约束的目的。encoded = Dense( encoding_dim, activation='relu', activity_regularizer = regularizers.activity_l1(10e-5) )(input_img)。深度-稀疏自编码器如下:

Note:在keras中存在3个正则化技巧,分别是权重正则化。偏置正则化、激活正则化

from keras.layers import Input, Dense
from keras import regularizers
from keras.models import Model
from keras.datasets import mnist
import numpy as np
import matplotlib.pyplot as plt
 
# only need images, this is self-supervised process
(x_train, _), (x_test, _) = mnist.load_data()
 
x_train = x_train.astype('float32') / 255. # normalization
x_test = x_test.astype('float32') / 255.
x_train = x_train.reshape((len(x_train), np.prod(x_train.shape[1:])))
x_test = x_test.reshape((len(x_test), np.prod(x_test.shape[1:])))
print(x_train.shape)
print(x_test.shape)
 
input_img = Input(shape=(784,))
encoded = Dense(128, 
                activation='relu',
                #activity_regularizer = regularizers.l1(0.000001)
                )(input_img)
encoded = Dense(64, 
                activation='relu',
                #activity_regularizer = regularizers.l1(0.00001)
                )(encoded)
decoded_input = Dense(32, 
                      activation='relu', 
                      #activity_regularizer = regularizers.l1(0.00001)
                      )(encoded)
decoded = Dense(64, activation='relu')(decoded_input)
decoded = Dense(128, activation='relu')(decoded)
decoded = Dense(784, activation='sigmoid')(decoded)
 
autoencoder = Model(inputs=input_img, outputs=decoded)
encoder = Model(inputs=input_img, outputs=decoded_input)
 
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
autoencoder.fit(x_train, x_train, epochs=50, batch_size=256, 
                shuffle=True, validation_data=(x_test, x_test))
 
encoded_imgs = encoder.predict(x_test)
decoded_imgs = autoencoder.predict(x_test)
 
n = 10  # how many digits we will display
plt.figure(figsize=(20, 6))
for i in range(n):
    # original images
    ax = plt.subplot(3, n, i + 1)
    plt.imshow(x_test[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

    ax = plt.subplot(3, n, i + 1 + n)
    plt.imshow(encoded_imgs[i].reshape(8, 4))
    #plt.gray()
    plt.set_cmap('jet')
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    
    ax = plt.subplot(3, n, i + 1 + n + n)
    plt.imshow(decoded_imgs[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
plt.show()

Dense784-Dense128-Dense64-Dense32-Dense64-Dense128-Dense784 (无正则化)结果显示,loss=0.1144:

Dense784-Dense128-Dense64-Dense32-Dense64-Dense128-Dense784 (激活正则化1e-6)结果显示,loss=0.1396:

  • 卷积自编码器:用卷积层构建自编码器

当输入是图像时,使用卷积神经网络是更好的。卷积自编码器的编码器部分由卷积层和MaxPooling层构成,MaxPooling负责空域下采样。而解码器由卷积层和上采样层构成。

from keras.layers import Input, Convolution2D, MaxPooling2D, UpSampling2D
from keras.models import Model
from keras.datasets import mnist
from keras.callbacks import TensorBoard
import matplotlib.pyplot as plt
 
# only need images, this is self-supervised process
(x_train, _), (x_test, _) = mnist.load_data()
 
x_train = x_train.astype('float32') / 255. # normalization
x_test = x_test.astype('float32') / 255.
x_train = x_train.reshape((len(x_train), 28, 28, 1))
x_test = x_test.reshape((len(x_test), 28, 28, 1))
print(x_train.shape)
print(x_test.shape)

input_img = Input(shape=(28, 28, 1))
 
x = Convolution2D(16, (3, 3), activation='relu', padding='same')(input_img)
x = MaxPooling2D((2, 2), padding='same')(x)
x = Convolution2D(8, (3, 3), activation='relu', padding='same')(x)
x = MaxPooling2D((2, 2), padding='same')(x)
x = Convolution2D(8, (3, 3), activation='relu', padding='same')(x)
encoded = MaxPooling2D((2, 2), padding='same')(x)
 
x = Convolution2D(8, (3, 3), activation='relu', padding='same')(encoded)
x = UpSampling2D((2, 2))(x)
x = Convolution2D(8, (3, 3), activation='relu', padding='same')(x)
x = UpSampling2D((2, 2))(x)
x = Convolution2D(16, (3, 3), activation='relu')(x)
x = UpSampling2D((2, 2))(x)
decoded = Convolution2D(1, (3, 3), activation='sigmoid', padding='same')(x)

encoder = Model(inputs=input_img, outputs=encoded) 
autoencoder = Model(inputs=input_img, outputs=decoded)
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
 

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

encoded_imgs = encoder.predict(x_test)
decoded_imgs = autoencoder.predict(x_test)


n = 10  # how many digits we will display
plt.figure(figsize=(20, 6))
for i in range(n):
    # original images
    ax = plt.subplot(3, n, i + 1)
    plt.imshow(x_test[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

    ax = plt.subplot(3, n, i + 1 + n)
    plt.imshow(encoded_imgs[i].reshape(16, 8))
    #plt.gray()
    plt.set_cmap('jet')
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    
    ax = plt.subplot(3, n, i + 1 + n + n)
    plt.imshow(decoded_imgs[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
plt.show()

结果显示:

 

  • 使用自动编码器进行图像去噪

我们把训练样本用噪声污染,然后使解码器解码出干净的照片,以获得去噪自动编码器。首先我们把原图片加入高斯噪声,然后把像素值clip到0~1。

from keras.layers import Input, Convolution2D, MaxPooling2D, UpSampling2D
from keras.models import Model
from keras.datasets import mnist
from keras.callbacks import TensorBoard
import matplotlib.pyplot as plt
 
# only need images, this is self-supervised process
(x_train, _), (x_test, _) = mnist.load_data()
 
x_train = x_train.astype('float32') / 255. # normalization
x_test = x_test.astype('float32') / 255.
x_train = x_train.reshape((len(x_train), 28, 28, 1))
x_test = x_test.reshape((len(x_test), 28, 28, 1))
print(x_train.shape)
print(x_test.shape)

input_img = Input(shape=(28, 28, 1))
 
x = Convolution2D(16, (3, 3), activation='relu', padding='same')(input_img)
x = MaxPooling2D((2, 2), padding='same')(x)
x = Convolution2D(8, (3, 3), activation='relu', padding='same')(x)
x = MaxPooling2D((2, 2), padding='same')(x)
x = Convolution2D(8, (3, 3), activation='relu', padding='same')(x)
encoded = MaxPooling2D((2, 2), padding='same')(x)
 
x = Convolution2D(8, (3, 3), activation='relu', padding='same')(encoded)
x = UpSampling2D((2, 2))(x)
x = Convolution2D(8, (3, 3), activation='relu', padding='same')(x)
x = UpSampling2D((2, 2))(x)
x = Convolution2D(16, (3, 3), activation='relu')(x)
x = UpSampling2D((2, 2))(x)
decoded = Convolution2D(1, (3, 3), activation='sigmoid', padding='same')(x)

encoder = Model(inputs=input_img, outputs=encoded) 
autoencoder = Model(inputs=input_img, outputs=decoded)
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
 

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

encoded_imgs = encoder.predict(x_test)
decoded_imgs = autoencoder.predict(x_test)


n = 10  # how many digits we will display
plt.figure(figsize=(20, 6))
for i in range(n):
    # original images
    ax = plt.subplot(3, n, i + 1)
    plt.imshow(x_test[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

    ax = plt.subplot(3, n, i + 1 + n)
    plt.imshow(encoded_imgs[i].reshape(16, 8))
    #plt.gray()
    plt.set_cmap('jet')
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    
    ax = plt.subplot(3, n, i + 1 + n + n)
    plt.imshow(decoded_imgs[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
plt.show()

去噪效果显示,loss=0.1028:

博文参考:

https://www.cnblogs.com/happylion/p/4209570.html

https://blog.csdn.net/marsjhao/article/details/73480859

猜你喜欢

转载自blog.csdn.net/shenziheng1/article/details/84532212