1. 程式人生 > 其它 >Tensorflow2.0實戰之Auto-Encoder

Tensorflow2.0實戰之Auto-Encoder

autoencoder可以用於資料壓縮、降維,預訓練神經網路,生成資料等等
Auto-Encoder架構

需要完成的工作
需要完成Encoder和Decoder的訓練
例如,Mnist的一張圖片大小為784維,將圖片放到Encoder中進行壓縮,編碼code使得維度小於784維度,之後可以將code放進Decoder中進行重建,可以產生同之前相似的圖片。
Encoder和Decoder需要一起進行訓練。

輸入同樣是一張圖片,通過選擇W,找到資料的主特徵向量,壓縮圖片得到code,然後使用W的轉置,恢復圖片。
我們知道,PCA對資料的降維是線性的(linear),恢復資料會有一定程度的失真。上面通過PCA恢復的圖片也是比較模糊的。
所以,我們也可以把PCA理解成為一個線性的autoencoder,W就是encode的作用,w的轉置就是decode的作用,最後的目的是decode的結果和原始圖片越接近越好。

現在來看真正意義上的Deep Auto-encoder的結構。通常encoder每層對應的W和decoder每層對應的W不需要對稱(轉置)

從上面可以看出,Auto-encoder產生的圖片,比PCA還原的圖片更加接近真實圖片。
接下來我們就來實現這樣的一個Auto-Encoder

實現

匯入必要的第三方庫,以及前期的處理

import os
import numpy as np
from PIL import Image
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import Sequential,layers

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)

這部分為資料集的載入和圖片重建的預處理過程;我們這裡將高的維度降為20,這個引數可以隨意,讀者也可以將其降為10也是可以的。同時這裡我們不再使用label了

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_data=tf.data.Dataset.from_tensor_slices(x_train)
train_data=train_data.shuffle(batchsz*5).batch(batchsz)
test_data=tf.data.Dataset.from_tensor_slices(x_test)
test_data=test_data.batch(batchsz)

接下來我們建立模型
這裡我們使用keras的介面,再建立模型的時,我們需要繼承Keras下的Model
我們先將網路結構搭建出來,這裡有兩個部分,一個是init的初始化方法;另一個是call前向傳播的方法

class AE(keras.Model):  
    def __init__(self):
        super(AE, self).__init__()
        	pass
      def call(self,inputs,training=None):
      	pass

編寫好上述後,我們完成init和call中的方法。
首先編寫Encoder,這裡Encoder將編輯為高維度、抽象的向量

 self.encoder=Sequential([
            layers.Dense(256,activation=tf.nn.relu),
            layers.Dense(128,activation=tf.nn.relu),
            layers.Dense(h_dim)
        ])

我們再編寫Decoders的方法,可以看到同Encoder是相反的過程

        self.decoder=Sequential([
            layers.Dense(128,activation=tf.nn.relu),
            layers.Dense(256,activation=tf.nn.relu),
            layers.Dense(784)
        ])

完成了init的方法後,我們再來寫call中的方法了,
首先使用encoder將輸入的高維度圖片置為低維的,然後再使用decoder還原,
筆者這裡由於上述設定的h_dim為10,同時使用的是FashionMNIST資料集(維度是784),所以encoder將[b,784]-->[b,10],
decoder將[b,10]-->[b,784]

    def call(self, inputs, training=None):
        # encoder-->decoder  [b,784]-->[b,10]
        h=self.encoder(inputs)
        # [b,10]-->[b,784]
        x_hat=self.decoder(h)
        return x_hat

接下來我們可以建立model,再看看model是怎樣的

model=AE()
model.build(input_shape=(None,784))
model.summary()
Model: "ae"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
sequential (Sequential)      multiple                  236436    
_________________________________________________________________
sequential_1 (Sequential)    multiple                  237200    
=================================================================
Total params: 473,636
Trainable params: 473,636
Non-trainable params: 0
_________________________________________________________________

定義優化器
這裡我們就使用Adam優化器,讀者也可以使用SGD,這個無所謂。、

optimizer=tf.optimizers.Adam(lr=lr)

訓練

for epoch in range(200):
    for step,x in enumerate(train_data):
        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))

驗證
這裡需要注意一下,image是一個資料夾,再訓練前,我們需要在程式碼所在路徑下手動新增

        x=next(iter(test_data))
        logits=model(tf.reshape(x,[-1,784])) # trans [0,1]
        x_hat=tf.sigmoid(logits)
        x_hat=tf.reshape(x_hat,[-1,28,28])
        x_concat=tf.concat([x,x_hat],axis=0)
        x_concat=x_concat.numpy()*255
        x_concat=x_concat.astype(np.uint8)
        save_images(x_concat,'image/epoch_%d.png'%epoch)

結果展示:


建議大家動手實踐實踐,共同進步。
筆者水平有限,如有表述不準確的地方還請諒解,有錯誤的地方歡迎大家批評指正。