ОТКЛЮЧЕНИЕ ИЗОБРАЖЕНИЙ С ПОМОЩЬЮ АВТОЭНКОДЕРОВ

Автоэнкодер - это нейронная сеть, используемая для уменьшения размерности; то есть для выбора и извлечения признаков. С точки зрения обработки изображений, мы можем обучить автоэнкодер выполнять за нас автоматическую предварительную обработку изображений. Denoising - одно из классических приложений автоэнкодера при обработке изображений. Процесс шумоподавления удаляет нежелательный шум, который искажает истинный сигнал. Это хорошо известный процесс, и я просто хотел испытать и испытать его на себе в дни COVID-19.

Я использовал набор данных Labeled Faces in the Wild (LFW), который доступен здесь или здесь.

Давайте импортируем то, что мы используем:

import sys
import tensorflow as tf
import keras, keras.layers as L, keras.backend as K
import numpy as np
from sklearn.model_selection import train_test_split
from lfw_dataset import load_lfw_dataset
%matplotlib inline
import matplotlib.pyplot as plt
import download_utils
import keras_utils
import numpy as np
from keras_utils import reset_tf_session

Python уже имеет встроенный набор функций для загрузки набора данных LFW. Так что загружать данные было легко.

Эти изображения без шума. Для своего эксперимента я добавлю немного шума к изображениям и буду следовать очень распространенному пути, добавляя стандартный нормальный гауссовский шум. Вот наша простая функция:

def apply_gaussian_noise(X,sigma=0.1):
    noise = np.random.normal(0, sigma, X.shape)
    return X + noise

Давайте создадим наш автоэнкодер (кодировщик и декодер) с глубокой структурой нейронной сети с прямой связью.

# load images
X, attr = load_lfw_dataset(use_raw=True, dimx=32, dimy=32)
shape = X.shape[1:]
# divided by 255 for normalization and -0.5 is for  centralization
X = X / 255.0 - 0.5
# Divide our dataset in to train and test parts.I got the ratio as 0.3.
X_train, X_test = train_test_split(X, test_size=0.3, random_state=42

Часть кодировщика довольно стандартна, мы складываем сверточные и объединяющие слои и заканчиваем плотным слоем, чтобы получить представление желаемого размера. Самое сложное - определить, на сколько нам нужно нырять. Чтобы найти ответ на этот вопрос, я провел несколько поисков и прочитал несколько статей. Использование функции активации elu также рекомендуется для автоэнкодера шумоподавления. (До этой работы я никогда не слышал об этой функции активации. Я также провел несколько поисков по ней. Я призываю вас сделать то же самое).

Создать декодер не составит труда, если вы уже знаете, что такое кодировщик. Потому что это полная противоположность. (Это должно быть). Операция свертки с шагом приводит к понижению дискретизации. транспонирование свертки противоположно операции свертки. (для детальной информации здесь отличная средняя статья)

def autoencoder(shape, code_size):

    # encoder part 
    encoder = keras.models.Sequential()
    encoder.add(L.InputLayer(shape)) #for input   
    #first convolution layer
    encoder.add(L.Conv2D(32, kernel_size=(3, 3), padding='same', activation='elu'))
    #first pooling layer.
    encoder.add(L.MaxPooling2D(pool_size=(2, 2), padding='same'))
    #second convolution layer
    encoder.add(L.Conv2D(64, kernel_size=(3, 3), padding='same', activation='elu'))
    #second pooling layer.    
    encoder.add(L.MaxPooling2D(pool_size=(2, 2), padding='same'))
    #third convolution layer    
    encoder.add(L.Conv2D(128, kernel_size=(3, 3), padding='same', activation='elu'))
    encoder.add(L.MaxPooling2D(pool_size=(2, 2), padding='same'))
    encoder.add(L.Conv2D(256, kernel_size=(3, 3), padding='same', activation='elu'))
    encoder.add(L.MaxPooling2D(pool_size=(2, 2), padding='same'))
    encoder.add(L.Flatten()) # to make it one dimensional                 
    encoder.add(L.Dense(code_size))
# decoder
    decoder = keras.models.Sequential()
    decoder.add(L.InputLayer((code_size,)))
    decoder.add(L.Dense(2*2*256))
    decoder.add(L.Reshape((2,2,256)))
    decoder.add(L.Conv2DTranspose(filters=128, kernel_size=(3, 3), strides=2, activation='elu', padding='same'))
    decoder.add(L.Conv2DTranspose(filters=64, kernel_size=(3, 3), strides=2, activation='elu', padding='same'))
    decoder.add(L.Conv2DTranspose(filters=32, kernel_size=(3, 3), strides=2, activation='elu', padding='same'))
    decoder.add(L.Conv2DTranspose(filters=3, kernel_size=(3, 3), strides=2, activation=None, padding='same'))

В этой части мне помогли статьи, которые я нашел. После нескольких испытаний я обнаружил, что 25 итераций идеально подходят для достижения оптимального значения потерь. Процесс полностью эмпирический. На каждой итерации я добавлял гауссовский шум к нашим тренировочным и тестовым данным, используя функцию шума, которую мы создали выше. Я использовал среднеквадратичную ошибку (MSE) в качестве типа потерь и оптимизатор Адама в качестве функции потерь. Их, конечно, можно изменить. Можно использовать другую функцию потерь и тип потерь, но эта пара наиболее предпочтительна.

s = reset_tf_session() # reset session
ITER=25
# we use bigger code size here for better quality
encoder, decoder = build_deep_autoencoder(shape, code_size=512)
assert encoder.output_shape[1:]==(512,), "encoder must output a code of required size"
inp = L.Input(shape)
code = encoder(inp)
reconstruction = decoder(code)
autoencoder = keras.models.Model(inp, reconstruction)
autoencoder.compile('adamax', 'mse')
for i in range(ITER): 
    X_train_noise = apply_gaussian_noise(X_train)
    X_test_noise = apply_gaussian_noise(X_test)
    
    autoencoder.fit(x=X_train_noise, y=X_train, epochs=1,
                    validation_data=[X_test_noise, X_test],
                    callbacks=[keras_utils.TqdmProgressCallback()],
                    verbose=0)

В конце концов, добавленного нами шума не было видно в результате. Чтобы визуализировать результаты в сравнении с исходными, мы создаем функцию, как показано ниже:

def visualize(img,encoder,decoder):
    code = encoder.predict(img[None])[5])# 5 is an arbitrary select
    denoised= decoder.predict(code[None])[5])
    plt.subplot(1,3,1)
    plt.title("Original Img + Gaussian Noise")
    show_image(img)
    plt.subplot(1,3,2)
    plt.title("Code")
    plt.imshow(code.reshape([code.shape[-1]//2,-1]))
    plt.subplot(1,3,3)
    plt.title("Denoised")
    show_image(denoised)
    plt.show()
X_test_noise = apply_gaussian_noise(X_test)
denoising_mse = autoencoder.evaluate(X_test_noise, X_test, verbose=0)
print("Denoising MSE:", denoising_mse)
for i in range(5): # print 5 images
    img = X_test_noise[i]
    visualize(img,encoder,decoder)

Фактически, это исследование показало нам, что мы можем очистить все данные, которые, как нам известно, содержат шум, с помощью автокодировщика. Более того, тип данных не обязательно должен быть изображением. Может быть, нужно сосредоточиться на том, чтобы получить этот шум. У него уже есть специальное название в среде машинного обучения: Обнаружение аномалий. Итак, давайте поэкспериментируем с этим в другое время.

Буду признателен, если вы поделитесь со мной своими комментариями, отзывами и критикой.

Спасибо.