Автоенкодер в TensorFlow с пример
⚡ Умно обобщение
Автоенкодерите компресират входните данни в по-малко вътрешно представяне и след това ги възстановяват, като изучават най-важните характеристики без етикети. Това ръководство наслагва четири плътни слоя в TensorFlow и реконструира изображения на коне по CIFAR-10.

Какво е Autoencoder в Deep Learning?
An Автокодер е инструмент за ефективно изучаване на кодиране на данни в без надзор начин. Това е вид изкуствена невронна мрежа което ви помага да научите представянето на набори от данни за намаляване на размерността чрез обучение на невронната мрежа да игнорира шума на сигнала. Това е чудесен инструмент за пресъздаване на входни данни.
С прости думи, машината прави, да речем, изображение и може да създаде тясно свързана картина. Входните данни в този вид невронна мрежа са немаркирани, което означава, че мрежата е способна да се учи без надзор. По-точно, входните данни се кодират от мрежата, за да се фокусират само върху най-критичната характеристика. Това е една от причините, поради които автоенкодерът е популярен за намаляване на размерността. Освен това, автоенкодерите могат да се използват за създаване на генеративни модели за обучение. Например, невронната мрежа може да бъде обучена с набор от лица и след това да може да генерира нови лица.
Как работи TensorFlow Autoencoder?
Целта на автокодера е да създаде приближение на входа, като се фокусира само върху основните характеристики. Може да си помислите защо просто не се научите как да копирате и поставите входа, за да създадете изхода. Всъщност автокодерът е набор от ограничения, които принуждават мрежата да научи нови начини за представяне на данните, различни от простото копиране на изхода.
Типичният автоенкодер се дефинира с вход, вътрешно представяне и изход (апроксимация на входа). Обучението се осъществява в слоевете, свързани с вътрешното представяне. Всъщност има два основни блока от слоеве, които приличат на традиционна невронна мрежа. Малката разлика е, че слоят, съдържащ изхода, трябва да е равен на входа. На диаграмата по-долу оригиналният вход отива в първия блок, наречен енкодер. Това вътрешно представяне компресира (намалява) размера на входа. Във втория блок се извършва реконструкцията на входа. Това е фазата на декодиране.

Моделът ще актуализира теглата чрез минимизиране на функцията за загуба. Моделът се наказва, ако изходът на реконструкцията е различен от входа.
Конкретно, представете си картина с размер 50×50 (т.е. 2,500 пиксела) и невронна мрежа само с един скрит слой, съставен от сто неврона. Обучението се извършва върху карта с характеристики, която е двадесет и пет пъти по-малка от входа. Това означава, че мрежата трябва да намери начин да реконструира 2,500 пиксела само с вектор от неврони, равен на 100.
Пример за подреден автоенкодер
В този урок за автоенкодер ще научите как да използвате подреден автоенкодер. Архитектурата е подобна на традиционна невронна мрежа. Входните данни отиват към скрит слой, за да бъдат компресирани или да се намали размерът им, след което достигат до слоевете за реконструкция. Целта е да се получи изходно изображение, възможно най-близко до оригинала. Моделът трябва да се научи да постига задачата си при набор от ограничения, т.е. с по-ниска размерност.
В днешно време автоенкодерите в дълбокото обучение се използват главно за премахване на шум от изображение. Представете си изображение с драскотини; човек все още е в състояние да разпознае съдържанието. Идеята на автоенкодера за премахване на шум е да добави шум към изображението, за да принуди мрежата да научи модела зад данните.
Другото полезно семейство Autoencoder Дълбоко обучение е вариационен автоенкодер. Този тип мрежа може да генерира нови изображения. Представете си, че обучавате мрежа с изображение на мъж; такава мрежа може да генерира нови лица.
Таблицата по-долу поставя натрупания автоенкодер, изграден в този урок, до другите семейства, които вероятно ще срещнете, така че ограничението, което всеки от тях добавя, е лесно за сравнение.
| Тип автоенкодер | Добавено е ограничение | Типична употреба |
|---|---|---|
| Недовършено / подредено | Кодиращият слой е по-тесен от входния | Намаляване на размерността, функция extracАЦИ |
| Обезшумяване | Добавен е шум към входа, чиста цел | Почистване на изображението и сигнала |
| Оскъден | Наказание за броя на активните единици | Интерпретируеми, частично базирани характеристики |
| Вариационен | Кодиращият слой научава разпределение на вероятностите | Генериране на нови проби |
Как да изградите автоенкодер с TensorFlow
В този урок ще научите как да изградите подреден автоенкодер, за да реконструирате изображение.
Ще използвате набора от данни CIFAR-10, който съдържа 60 000 цветни изображения с размер 32×32. Наборът от данни Autoencoder вече е разделен на 50 000 изображения за обучение и 10 000 за тестване. Има до десет класа:
- Самолет
- Автомобил
- Птица
- Котка
- Елен
- Куче
- Жаба
- Кон
- Адрес за доставка
- Truck
Трябва да изтеглите изображенията от Страница с набор от данни CIFAR-10 и разархивирайте архива. Папката cifar-10-batches-py съдържа пет партиди данни с по 10000 изображения всяка в произволен ред.
Преди да изградите и обучите вашия модел, трябва да приложите известна обработка на данни. Ще продължите както следва:
- Импортирайте данните
- Преобразувайте данните в черно-бял формат
- Добавете всички партиди
- Конструирайте набора от данни за обучение
- Конструирайте визуализатор на изображения
Бележка към версията: Кодът в този урок е насочен към TensorFlow 1.x. В TensorFlow 2 модулът tf.contrib вече не съществува, а заместителите, сесиите и инициализиращият се итератор се намират под tf.compat.v1. Извикването на tf.compat.v1.disable_v2_behavior() след импортирането е най-бързият начин да се изпълнят списъците непроменени.
Предварителна обработка на изображението
Стъпка 1) Импортирайте данните
Според официалния уебсайт, можете да качите данните със следния код. Кодът на Autoencoder ще зареди данните в речник с данните и етикета. Обърнете внимание, че кодът е функция.
import numpy as np import tensorflow as tf import pickle def unpickle(file): import pickle with open(file, 'rb') as fo: dict = pickle.load(fo, encoding='latin1') return dict
Стъпка 2) Преобразувайте данните в черно-бял формат
За простота ще преобразувате данните в скала на сивото. Тоест само с едно измерение срещу три за цветно изображение. Повечето от невронните мрежи работят само с едно измерение.
def grayscale(im): return im.reshape(im.shape[0], 3, 32, 32).mean(1).reshape(im.shape[0], -1)
Стъпка 3) Добавете всички партиди
След като и двете функции са създадени и наборът от данни е зареден, можете да напишете цикъл за добавяне на данните в паметта. Ако проверите внимателно, разархивираният файл с данните се нарича data_batch_ с номер от 1 до 5. Можете да прегледате файловете в цикъл и да ги добавите към data.
Когато тази стъпка е завършена, конвертирате цветните данни във формат на сивата скала. Както можете да видите, формата на данните е 50000 и 1024. 32*32 пиксела вече са изравнени до 1024.
# Load the data into memory data, labels = [], [] ## Loop over the b for i in range(1, 6): filename = './cifar-10-batches-py/data_batch_' + str(i) open_data = unpickle(filename) if len(data) > 0: data = np.vstack((data, open_data['data'])) labels = np.hstack((labels, open_data['labels'])) else: data = open_data['data'] labels = open_data['labels'] data = grayscale(data) x = np.matrix(data) y = np.array(labels) print(x.shape) (50000, 1024)
Забележка: Променете './cifar-10-batches-py/data_batch_' на действителното местоположение на вашия файл. Например за Windows машина, пътят може да бъде име на файл = 'E:\cifar-10-batches-py\data_batch_' + str(i)
Стъпка 4) Конструирайте набора от данни за обучение
За да направите обучението по-бързо и лесно, ще обучавате модел само върху изображенията на конете. Конете носят етикет 7 в данните за етикета. Както е посочено в документацията на набора от данни CIFAR-10, всеки клас съдържа 5000 изображения. Можете да отпечатате формата на данните, за да потвърдите, че има 5,000 изображения с 1024 колони, както е показано в примерната стъпка на TensorFlow Autoencoder по-долу.
horse_i = np.where(y == 7)[0]
horse_x = x[horse_i]
print(np.shape(horse_x))
(5000, 1024)
Стъпка 5) Конструирайте визуализатор на изображения
Накрая създавате функция за начертаване на изображенията. Ще ви е необходима тази функция, за да отпечатате реконструираното изображение от автокодера.
Лесен начин за отпечатване на изображения е да използвате функцията imshow() от библиотеката Matplotlib. Обърнете внимание, че трябва да конвертирате формата на данните от 1024 на 32*32 (т.е. формат на изображение).
# To plot pretty figures %matplotlib inline import matplotlib import matplotlib.pyplot as plt def plot_image(image, shape=[32, 32], cmap = "Greys_r"): plt.imshow(image.reshape(shape), cmap=cmap,interpolation="nearest") plt.axis("off")
Функцията приема 3 аргумента:
- Изображение: входът
- Форма: списък, размерът на изображението
- Cmap: изберете цветовата карта. По подразбиране, сиво
Можете да опитате да начертаете първото изображение в набора от данни. Трябва да видите човек на кон.
plot_image(horse_x[1], shape=[32, 32], cmap = "Greys_r")
Извикването връща миниатюрата в сивата скала по-долу и потвърждава, че преобразуването от 1024 стойности обратно до изображение с размер 32×32 е правилно.
Задайте оценител на набор от данни
Добре, сега, след като наборът от данни е готов за употреба, можете да започнете да използвате TensorFlow. Преди да изградите модела, използвайте оценителя на набора от данни на TensorFlow, за да захранвате мрежата.
Ще изградите набор от данни с оценител TensorFlow. За да освежите ума си, трябва да използвате:
- от_тензорни_срезове
- повтарям
- партида
Пълният код за изграждане на набора от данни е:
dataset = tf.data.Dataset.from_tensor_slices(x).repeat().batch(batch_size)
Обърнете внимание, че x е заместител с форма [None,n_inputs]. Първото измерение е зададено на None, защото броят на изображенията, подадени към мрежата, е равен на размера на пакета, който се определя само по време на изпълнение. За подробности, моля, вижте урока на линейна регресия с TensorFlow.
След това трябва да създадете итератора. Без този ред код никакви данни няма да преминат през тръбопровода.
iter = dataset.make_initializable_iterator() # create the iteratorfeatures = iter.get_next()
Сега, когато конвейерът е готов, можете да проверите дали първото изображение е същото като преди (т.е. човек на кон).
Задавате размера на пакета на 1, защото искате да подадете към набора от данни само едно изображение. Можете да видите размерността на данните с print(sess.run(features).shape). Тя е равна на (1, 1024). 1 означава, че всеки път се подава едно изображение с 1024 стойности. Ако размерът на пакета е зададен на две, тогава през конвейера ще преминат две изображения. Не променяйте размера на пакета, в противен случай ще се получи грешка, защото само едно изображение може да отиде към функцията plot_image().
## Parameters n_inputs = 32 * 32 BATCH_SIZE = 1 batch_size = tf.placeholder(tf.int64) # using a placeholder x = tf.placeholder(tf.float32, shape=[None,n_inputs]) ## Dataset dataset = tf.data.Dataset.from_tensor_slices(x).repeat().batch(batch_size) iter = dataset.make_initializable_iterator() # create the iterator features = iter.get_next() ## Print the image with tf.Session() as sess: # feed the placeholder with data sess.run(iter.initializer, feed_dict={x: horse_x, batch_size: BATCH_SIZE}) print(sess.run(features).shape) plot_image(sess.run(features), shape=[32, 32], cmap = "Greys_r") (1, 1024)
Конвейерът връща същия райдер, който е бил нанесен директно от матрицата NumPy, което доказва, че заместителят, итераторът и размерът на партидата са свързани правилно.
Изградете мрежата
Време е да изградим мрежата. Ще обучите подреден автоенкодер, тоест мрежа с множество скрити слоеве.
Вашата мрежа ще има един входен слой с 1024 точки, т.е. 32×32, формата на изображението.
Енкодерният блок ще има един горен скрит слой с 300 неврона и централен слой със 150 неврона. Декодерният блок е симетричен на енкодера. Можете да визуализирате мрежата на диаграмата по-долу. Обърнете внимание, че можете да променяте стойностите на скритите и централните слоеве.
Изграждането на автоенкодер е много подобно на всеки друг модел на дълбоко обучение.
Ще изградите модела, като следвате тези стъпки:
- Определете параметрите
- Дефинирайте слоевете
- Определете архитектурата
- Дефинирайте оптимизацията
- Стартирайте модела
- Оценете модела
В предишния раздел научихте как да създадете конвейер за захранване на модела, така че няма нужда да създавате набора от данни отново. Ще конструирате автоенкодер с четири слоя. Използвате инициализацията на Ксавие. Това е техника за задаване на началните тегла според дисперсията както на входа, така и на изхода. Накрая използвате активиращата функция ELU. Регуляризирате функцията на загуба с L2 регуларизатор.
Стъпка 1) Дефиниране на параметрите
Първата стъпка предполага да се определи броят на невроните във всеки слой, скоростта на обучение и хиперпараметърът на регулатора.
Преди това импортирате частичната функция. Това е по-добър метод за дефиниране на параметрите на плътните слоеве. Кодът по-долу определя стойностите на архитектурата на автоенкодера. Както беше посочено по-горе, енкодерът има два слоя, с 300 неврона в първия слой и 150 във втория слой. Техните стойности се съхраняват в n_hidden_1 и n_hidden_2.
Трябва да дефинирате скоростта на обучение и хиперпараметъра L2. Стойностите се съхраняват в learning_rate и l2_reg.
from functools import partial ## Encoder n_hidden_1 = 300 n_hidden_2 = 150 # codings ## Decoder n_hidden_3 = n_hidden_1 n_outputs = n_inputs learning_rate = 0.01 l2_reg = 0.0001
Техниката за инициализация на Xavier се извиква с обекта xavier_initializer от estimator contrib. В същия estimator можете да добавите регуларизатора с l2_regularizer.
## Define the Xavier initialization xav_init = tf.contrib.layers.xavier_initializer() ## Define the L2 regularizer l2_regularizer = tf.contrib.layers.l2_regularizer(l2_reg)
Бележка към версията: tf.contrib беше премахнат в TensorFlow 2. Директните заместители са tf.keras.initializers.GlorotUniform() за xavier_initializer() и tf.keras.regularizers.l2() за l2_regularizer().
Стъпка 2) Дефиниране на слоевете
Всички параметри на плътните слоеве са зададени; можете да опаковате всичко в променливата dense_layer, като използвате обектния partial. След това dense_layer използва ELU активирането, Xavier инициализацията и L2 регуларизацията при всяко извикване.
## Create the dense layer
dense_layer = partial(tf.layers.dense,
activation=tf.nn.elu,
kernel_initializer=xav_init,
kernel_regularizer=l2_regularizer)
Стъпка 3) Дефиниране на архитектурата
Ако погледнете диаграмата на архитектурата, ще забележите, че мрежата подрежда три слоя с изходен слой. В кода по-долу свързвате съответните слоеве. Например, първият слой изчислява скаларното произведение между входните матрични характеристики и матрицата, съдържаща 300-те тегла. След като скаларното произведение бъде изчислено, изходът отива към активиращата функция ELU. Изходът става вход на следващия слой, ето защо го използвате за изчисляване на hidden_2 и така нататък. Умножението на матриците е едно и също за всеки слой, защото използвате една и съща активираща функция. Обърнете внимание, че последният слой, изходи, не прилага активираща функция. Това има смисъл, защото това е реконструираният вход.
## Make the mat mul hidden_1 = dense_layer(features, n_hidden_1) hidden_2 = dense_layer(hidden_1, n_hidden_2) hidden_3 = dense_layer(hidden_2, n_hidden_3) outputs = dense_layer(hidden_3, n_outputs, activation=None)
Стъпка 4) Дефиниране на оптимизацията
Последната стъпка е да се конструира оптимизаторът. Използвате средноквадратичната грешка (MSE) като функция на загубата. Ако си спомняте урока за линейна регресия, знаете, че MSE се изчислява с разликата между прогнозирания изход и реалния етикет. Тук етикетът е характеристиката, защото моделът се опитва да реконструира входа. Следователно, искате средната стойност на сумата от квадратите на разликата между прогнозирания изход и входа. С TensorFlow можете да кодирате функцията на загубата, както следва:
loss = tf.reduce_mean(tf.square(outputs - features))
След това трябва да оптимизирате функцията на загубата. Използвате оптимизатора Adam, за да изчислите градиентите. Целевата функция е да се минимизира загубата.
## Optimize
loss = tf.reduce_mean(tf.square(outputs - features))
optimizer = tf.train.AdamOptimizer(learning_rate)
train = optimizer.minimize(loss)
Още една настройка преди обучение на модела. Искате да използвате размер на партида от 150, тоест да захранвате конвейера със 150 изображения на всяка итерация. Трябва да изчислите броя на итерациите ръчно. Това е тривиално да се направи:
Ако искате да предавате по 150 изображения всеки път и знаете, че в набора от данни има 5000 изображения, броят на итерациите е равен на 5000, разделено на 150. В Python Можете да изпълните следните кодове и да се уверите, че изходът е 33:
BATCH_SIZE = 150 ### Number of batches : length dataset / batch size n_batches = horse_x.shape[0] // BATCH_SIZE print(n_batches) 33
Стъпка 5) Стартирайте модела
Не на последно място, обучете модела. Обучавате модела със 100 епохи. Тоест, моделът ще види изображенията 100 пъти, докато оптимизира теглата.
Вече сте запознати с кодовете за обучение на модел в TensorFlow. Малката разлика е в това, че данните се предават по канал (pipe), преди да се изпълни обучението. По този начин моделът се обучава по-бързо.
Интересувате се от отпечатване на загубата след десет епохи, за да видите дали моделът научава нещо (т.е. загубата намалява). Обучението отнема от 2 до 5 минути, в зависимост от хардуера на вашата машина.
## Set params n_epochs = 100 ## Call Saver to save the model and re-use it later during evaluation saver = tf.train.Saver() with tf.Session() as sess: sess.run(tf.global_variables_initializer()) # initialise iterator with train data sess.run(iter.initializer, feed_dict={x: horse_x, batch_size: BATCH_SIZE}) print('Training...') print(sess.run(features).shape) for epoch in range(n_epochs): for iteration in range(n_batches): sess.run(train) if epoch % 10 == 0: loss_train = loss.eval() # not shown print("\r{}".format(epoch), "Train MSE:", loss_train) #saver.save(sess, "./my_model_all_layers.ckpt") save_path = saver.save(sess, "./model.ckpt") print("Model saved in path: %s" % save_path) Training... (150, 1024) 0 Train MSE: 2934.455 10 Train MSE: 1672.676 20 Train MSE: 1514.709 30 Train MSE: 1404.3118 40 Train MSE: 1425.058 50 Train MSE: 1479.0631 60 Train MSE: 1609.5259 70 Train MSE: 1482.3223 80 Train MSE: 1445.7035 90 Train MSE: 1453.8597 Model saved in path: ./model.ckpt
Стъпка 6) Оценете модела
След като вашият модел е обучен, е време да го оцените. Трябва да импортирате тестовия набор от файла /cifar-10-batches-py/.
test_data = unpickle('./cifar-10-batches-py/test_batch') test_x = grayscale(test_data['data']) #test_labels = np.array(test_data['labels'])
Забележка: За Windows машина, кодът става test_data = unpickle(r”E:\cifar-10-batches-py\test_batch”)
Можете да опитате да отпечатате изображение 13, което е кон.
plot_image(test_x[13], shape=[32, 32], cmap = "Greys_r")
Графиката потвърждава, че тестовата партида е заредена правилно и че изображение 13 принадлежи към класа, върху който е обучен моделът.
За да оцените модела, ще използвате стойността на пикселите на това изображение и ще видите дали енкодерът може да реконструира същото изображение след свиването му до 1024 пиксела. Обърнете внимание, че дефинирате функция за оценка на модела върху различни изображения. Моделът би трябвало да работи по-добре само върху коне.
Функцията приема два аргумента:
- df: Импортиране на тестовите данни
- image_number: посочва кое изображение да се импортира
Функцията е разделена на три части:
- Преоформете изображението до правилното измерение, т.е. 1, 1024
- Захранете модела с невидяното изображение, кодирайте/декодирайте изображението
- Отпечатайте реалното и реконструирано изображение
def reconstruct_image(df, image_number = 1): ## Part 1: Reshape the image to the correct dimension i.e 1, 1024 x_test = df[image_number] x_test_1 = x_test.reshape((1, 32*32)) ## Part 2: Feed the model with the unseen image, encode/decode the image with tf.Session() as sess: sess.run(tf.global_variables_initializer()) sess.run(iter.initializer, feed_dict={x: x_test_1, batch_size: 1}) ## Part 3: Print the real and reconstructed image # Restore variables from disk. saver.restore(sess, "./model.ckpt") print("Model restored.") # Reconstruct image outputs_val = outputs.eval() print(outputs_val.shape) fig = plt.figure() # Plot real ax1 = fig.add_subplot(121) plot_image(x_test_1, shape=[32, 32], cmap = "Greys_r") # Plot estimated ax2 = fig.add_subplot(122) plot_image(outputs_val, shape=[32, 32], cmap = "Greys_r") plt.tight_layout() fig = plt.gcf()
След като функцията за оценка е дефинирана, можете да разгледате реконструираното изображение номер тринадесет.
reconstruct_image(df =test_x, image_number = 13)
INFO:tensorflow:Restoring parameters from ./model.ckpt
Model restored.
(1, 1024)
Фигурата поставя оригиналното тестово изображение отляво, а изходът от автоенкодера отдясно, така че загубата на фини детайли след компресията от 1024 до 150 е лесна за преценка.




