پیاده سازی یک autoencoder ساده در کراس


می خواهیم به نحوه ی پیاده سازی autoencoderها در کراس نگاهی بیندازیم، معماری شبکه عصبی که سعی می کند بازنمایی فشرده ای از داده ی ورودی به دست دهد.

به زبان ساده، یک autoencoder (یا خودرمزنگار)، با سعی بر کپی کردن ورودی به خروجی، بازنمایی یا کدی از داده ی ورودی را خواهد آموخت. اما کاربرد autoencoderها به این سادگی نیست که ورودی روی خروجی کپی شود، مهم‌ترین کاربرد آن یادگیری ساختار نهان (latent space) است که خروجی از روی آن ساخته می‌شود.

روند کلی کار autoencoderها چگونه است؟

یک autoencoder ابتدا توزیع ورودی را به یک تنسور با ابعاد پایین کدگذاری (انکد) می‌کند و اغلب فرم بردار را به خود می‌گیرد. این روند باعث می‌شود که ساختار نهان داده‌ی ورودی تقریب زده شود و آن را latent Representation یا Code/vector می نامند. این قسمت، بخش encoding را تشکیل می‌دهد.

سپس  latent vector توسط بخش decoder کدگشایی خواهد شد تا داده‌ی ورودی اصلی را بازیابی کند.

در نتیجه latent vector یک بازنمایی فشرده با ابعاد پایین از توزیع داده‌ی ورودی به دست می‌دهد. عدم شباهت بین ورودی و خروجی را می‌توان با یک loss function تقریب زد.

چرا از autoencoderها استفاده می‌کنیم؟

این شبکه‌ها یک ابزار کلیدی مهم در درک مفاهیم پیشرفته یادگیری عمیق هستند؛ زیرا این شبکه‌ها یک بازنمایی ابعاد پایین از داده‎‌ی ورودی به ما می‌دهند. همچنین می‌توان از این شبکه‌ها استفاده کرد و عملیات ساختاری روی داده‌ی اصلی ایجاد کرد. عملیاتی چون نویززدایی از تصویر، رنگ آمیزی تصاویر یا سگمنتیشین یا تولید تصاویر جدید برای آموزش بهتر مدل.

ساختار Autoencoder

برای پیاده سازی autoencoder ابتدا باید ساختار آن را به خوبی بشناسیم و بدانیم که از دو بخش جداگانه تشکیل شده است.

1-Encoder

 این بخش، داده‌ی ورودی x را به یک latent vector با ابعاد پایین نگاشت می‌کند. از آنجاییکه latent vector ابعاد پایینی نسبت به ورودی دارد، encoder مجبور می‌شود که تنها مهم‌ترین ویژگی‌های داده‌ی ورودی را یاد بگیرد. برای مثال در مورد ارقام MNIST، ممکن است مهم ترین ویژگی‌ها  ضخامت ارقام، زاویه ی نوشتن یا سبک نوشتن باشد. اینها مهم‌ترین اطلاعات موردنیاز برای نمایش ارقام صفر تا نه هستند.

2-Decoder

این بخش سعی می‌کند که ورودی را از latent vector بازیابی کند. اگرچه latent vector ابعاد پایینی دارد اما سایز کافی و مناسبی دارد و به decoder این امکان را می‌دهد که داده‌ی ورودی را بازیابی کند. هدف decoder آن است که x~ را تا حد ممکن به x نزدیک سازد. در حالت کلی هر دوی encoder و decoder توابع غیرخطی هستند. بعد z معیاری از ویژگی‌های برجسته‌ی ورودی است. ابعاد latent space معمولاً بسیار کوچکتر از ابعاد ورودی است و برای آن است که این بخش محدود شود تا تنها مهم‌ترین و برجسته‌ترین ویژگی های داده ی ورودی را یاد بگیرد.

همچنین به یک Loss function مناسب نیاز است که بررسی کند x ورودی با x ساخته شده‌ی خروجی چقدر متفاوت است. برای مثال، این تابع هزینه می‌تواند MSE (mean square error) باشد:

loss function_autoencoder

مشابه سایر شبکه‌های عصبی، autoencoder تلاش می‌کند که این تابع خطا یا loss را تا حد ممکن در حین آموزش کوچک کند. در شکل زیر یک autoencoder نشان داده شده است. encoder تابعی است که ورودی x  را به یک بردار latent با ابعاد کمتر z فشرده می‌کند. این بردار latent نشانگر مهم‌ترین ویژگی‌های توزیع ورودی است. سپس decoder سعی می‌کند که ورودی اصلی را از بردار latent بازیابی کند.

حال فرض کنید می‌خواهیم یک شبکه autoencoder ساده برای دیتاست MNIST بسازیم. به این ترتیب x ارقام دیتاست MNIST خواهد بود که ابعادی به صورت 28*28*1=784 دارد. encoder، ورودی را به یک z با ابعاد پایین تبدیل می‌کند که می‌تواند بردار latent 16 بعدی باشد. سپس decoder سعی می‌کند که ورودی را از z بازیابی کند.

پیاده سازی یک autoencoder در کراس برای ارقام MNIST

برای پیاده سازی یک autoencoder ساده در کراس برای ارقام MNIST به صورت زیر عمل می‌کنیم:

 

Python

from keras.layers import Dense, Input
from keras.layers import Conv2D, Flatten
from keras.layers import Reshape, Conv2DTranspose
from keras.models import Model
from keras.datasets import mnist
from keras.utils import plot_model
from keras import backend as K
import numpy as np
import matplotlib.pyplot as plt
# load MNIST dataset
(x_train, _), (x_test, _) = mnist.load_data()
# reshape to (28, 28, 1) and normalize input images
image_size = x_train.shape[1]
x_train = np.reshape(x_train, [-1, image_size, image_size, 1])
x_test = np.reshape(x_test, [-1, image_size, image_size, 1])
x_train = x_train.astype(‘float32’) / 255
x_test = x_test.astype(‘float32’) / 255
# network parameters
input_shape = (image_size, image_size, 1)
batch_size = 32
kernel_size = 3
latent_dim = 16
# encoder/decoder number of CNN layers and filters per layer
layer_filters = [32, 64]
# build the autoencoder model
# first build the encoder model
inputs = Input(shape= input_shape, name =’encoder_input’)
x = inputs
for filters in layer_filters:
    x = Conv2D(filters=filters,
               kernel_size=kernel_size,
               activation=’relu’,
               strides=2,
               padding=’same’)(x)
# shape info needed to build decoder model
# so we don’t do hand computation
# the input to the decoder’s first
# Conv2DTranspose will have this shape
# shape is (7,7,64) which is processed by
# the decoder back to (28,28, 1)
shape = K.int_shape(x)
# generate latent vector
x = Flatten()(x)
latent = Dense(latent_dim, name=’latent_vector’)(x)
# instantiate encoder model
encoder = Model(inputs, latent, name=’encoder’)
encoder.summary()
# build the decoder model
latent_inputs = Input(shape = (latent_dim,), name=’decoder_input’)
# use the shape (7, 7, 64) that was earlier saved
x = Dense(shape[1]*shape[2]*shape[3])(latent_inputs)
# from vector to suitable shape for transposed conv
x = Reshape((shape[1], shape[2], shape[3]))(x)
# stack of Conv2DTranspose (64)-Conv2DTranspose(32)
for filters in layer_filters[::-1]:
  x = Conv2DTranspose(filters=filters, kernel_size = kernel_size, activation = ‘relu’, strides=2, padding=’same’)(x)
#reconstruct the input
outputs = Conv2DTranspose(filters=1, kernel_size=kernel_size, activation=’sigmoid’, padding=’same’, name=’decoder_output’)(x)
#instantiate decoder model
decoder = Model(latent_inputs, outputs, name=’decoder’)
decoder.summary()
#autoencoder = encoder+ decoder
#instantiate autoencoder model
autoencoder = Model(inputs, decoder(encoder(inputs)), name=’autoencoder’)
autoencoder.summary()
# Mean Square Error (MSE) loss function, Adam optimizer
autoencoder.compile(loss=’mse’, optimizer=’adam’)
# train the autoencoder
autoencoder.fit(x_train, x_train, validation_data = (x_test, x_test), epochs=1, batch_size = batch_size)
# predict the autoencoder output from test data
x_decoded = autoencoder.predict(x_test)
برای دیکد کردن تصاویر بازسازی شده از کد زیر استفاده می کنیم

Python

# display the 1st 8 test input and decoded images
imgs = np.concatenate([x_test[:8], x_decoded[:8]])
imgs = imgs.reshape((4, 4, image_size, image_size))
imgs = np.vstack([np.hstack(i) for i in imgs])
plt.figure()
plt.axis(‘off’)
plt.title(‘Input: 1st 2 rows, Decoded: last 2 rows’)
plt.imshow(imgs, interpolation=’none’, cmap=’gray’)
plt.show()
خروجی را به صورت زیر نمایش خواهد داد
همانطور که مشخص است این تصاویر تولید شده بسیار شبیه به دیتاست اصلی هستند.
در دوره ی جدید که به زودی برگزار خواهد شد به توضیح این شبکه و سایر شبکه های مولد خواهیم پرداخت

دیدگاه ها

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

code