Keras – Автокодировщик, отличающийся от кодировщика + декодировщика

Вопрос или проблема

Я строю 1D автоэнкодер на основе CNN в Keras, следуя совету в этом вопросе на SO, где кодировщик и декодировщик отделены. Моя цель – повторно использовать декодировщик, как только автоэнкодер будет обучен. Центральный слой моего автоэнкодера – это Dense слой, потому что я хотел бы изучить его позже.

Моя проблема в том, что если я скомпилирую и обучу весь автоэнкодер, записанный как Decoder()Encoder()(x), где x является входом, я получаю другое предсказание, когда я делаю

autoencoder.predict(training_set)

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

from tensorflow.keras.layers import Input, Dense, BatchNormalization, Flatten, Lambda, Activation, Conv1D, MaxPooling1D, UpSampling1D, Reshape
from tensorflow.keras.models import Model
from tensorflow.keras import optimizers
from tensorflow.keras.layers import GaussianNoise
import keras.backend as K
from tensorflow.keras.layers import Add

import tensorflow as tf

import scipy.io
import sys
import matplotlib.pyplot as plt
import numpy as np
import copy


training = # некоторый обучающий набор, 1500 образцов по 501 точке каждый
testing = # некоторый тестовый набор, 500 образцов по 501 точке каждый

# изменение формы для CNN
training = np.reshape(training, [1500, 501, 1])
testing = np.reshape(testing, [500, 501, 1])


# нормализация входа
X_mean = training.mean()
oscillations -= X_mean
X_std = training.std()
training /= X_std


copy_of_test = copy.copy(testing)
testing -= X_mean
testing /= X_std

### МОДЕЛЬ ###

def Encoder():
    encoder_input = Input(batch_shape=(None, 501, 1))  
    e1 = Conv1D(256,3, activation='tanh', padding='valid')(encoder_input)
    e2 = MaxPooling1D(2)(e1)
    e3 = Conv1D(32,3, activation='tanh', padding='valid')(e2)
    e4 = MaxPooling1D(2)(e3)
    e5 = Flatten()(e4)
    encoded = Dense(32,activation = 'tanh')(e5)
    return Model(encoder_input, encoded)


def Decoder():
    encoded_input = Input(shape=(32,))  
    encoded_reshaped = Reshape((32,1))(encoded_input)
    d1 = Conv1D(32, 3, activation='tanh', padding='valid', name="decod_conv1d_1")(encoded_reshaped)
    d2 = UpSampling1D(2, name="decod_upsampling1d_1")(d1)
    d3 = Conv1D(256, 3, activation='tanh', padding='valid', name="decod_conv1d_2")(d2)
    d4 = UpSampling1D(2, name="decod_upsampling1d_2")(d3)
    d5 = Flatten(name="decod_flatten")(d4)
    d6 = Dense(501, name="decod_dense1")(d5)
    decoded = Reshape((501,1), name="decod_reshape")(d6)
    return Model(encoded_input, decoded)


# определяем входные данные для модели:
x = Input(batch_shape=(None, 501, 1))
y = Input(shape=(32,))

# создаем модель:
autoencoder = Model(x, Decoder()(Encoder()(x)))

# компилируем модель:
autoencoder.compile(optimizer="adam", loss="mse")
for layer in autoencoder.layers: print(K.int_shape(layer.output))


epochs = 100
batch_size = 100
validation_split = 0.2
# обучаем модель
history = autoencoder.fit(x = training, y = training,
                    epochs=epochs,
                    batch_size=batch_size,
                    validation_split=validation_split)

# Кодировщик
encoder = Model(inputs=x, outputs=Encoder()(x), name="encoder")
print('enc:')
for layer in encoder.layers: print(K.int_shape(layer.output))
features = encoder.predict(training) # признаки

# Декодировщик
decoder = Model(inputs=y, outputs=Decoder()(y), name="decoder")
print('dec:')
for layer in decoder.layers: print(K.int_shape(layer.output))
score = decoder.predict(features) # 
score = np.squeeze(score)    

predictions = autoencoder.predict(training)
predictions = np.squeeze(predictions)

# строим один случайный пример
# score должно быть равно predictions!
# потому что score получается от обученного декодировщика, действующего на закодированных признаках, в то время как predictions получаются от автоэнкодера, действующего на обучающем наборе 
plt.plot(score[100], label="eD")
plt.plot(predictions[100], label="AE")
plt.legend()
plt.show()
plt.close()

Когда вы выполняете autoencoder.fit, ваша модель чему-то обучается, и ваши веса изменяются для модели кодировщика и модели декодировщика.
После этого, создавая ваш encoder/Decoder, как “encoder = Model(inputs=x, outputs=Encoder()(x), name=”encoder”)”, вы создаете другую модель кодировщика с другими весами, вы должны использовать одни и те же векторные веса.
Проверьте, равны ли “autoencoder.layers[1].get_weights()” и “encoder.get_weights()”, тогда вы узнаете, что происходит в модели. (аналогичная ситуация и для декодировщика). Вы можете проверить это с помощью следующего кода:

for i in range(len(autoencoder.layers[1].weights)):
    print((autoencoder.layers[1].get_weights()[i]==encoder.get_weights()[i]).all())

Ответ или решение

Различие между Автоэнкодером и Отдельными Кодировщиком и Декодировщиком в Keras

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

Понимание Архитектуры Автоэнкодера

Автоэнкодеры состоят из двух основных частей: кодировщика и декодировщика. Кодировщик преобразует входные данные в компактное представление (код), а декодировщик восстанавливает исходные данные из этого кода. В вашем случае отлаженные компоненты Keras (Encoder и Decoder) встраиваются в общий модельный блок автоэнкодера.

Проблема Взаимосвязи Моделей

Когда вы компилируете и обучаете модель autoencoder, вы фактически обучаете веса как кодировщика, так и декодировщика одновременно. Когда вы создаёте отдельные модели с помощью следующего кода:

encoder = Model(inputs=x, outputs=Encoder()(x), name="encoder")

вы создаёте новую модель, основанную на кодировщике, который использует его собственные веса, несовпадающие с весами, обученными в контексте всего автоэнкодера. Это и есть причина, по которой encoder.predict(training) даёт отличные предсказания по сравнению с autoencoder.predict(training).

Как Это Исправить?

Чтобы убедиться, что ваши кодировщик и декодировщик используют те же веса, что и в основной модели автоэнкодера, вы должны ссылаться на те же экземпляры моделей вместо создания новых. Вы можете это сделать следующим образом:

encoder_weights = autoencoder.layers[1].get_weights()
encoder.set_weights(encoder_weights)

decoder_weights = autoencoder.layers[2].get_weights()
decoder.set_weights(decoder_weights)

Это гарантирует, что ваши encoder и decoder будут использовать прямо те же веса, что и в полном автоэнкодере после обучения.

Другие Важные Моменты

  1. Проверка Весов: Если вы хотите проверить связь между весами моделей, вы можете использовать следующий код:

    for i in range(len(autoencoder.layers[1].weights)):
       print((autoencoder.layers[1].get_weights()[i] == encoder.get_weights()[i]).all())

    Это даст вам понимание того, совпадают ли веса.

  2. Согласованность Предсказаний: После того как вы установили веса, вы должны увидеть согласованность между предсказаниями:

    features = encoder.predict(training)
    score = decoder.predict(features)
    predictions = autoencoder.predict(training)
    
    assert np.array_equal(score, predictions), "Предсказания не совпадают!"

    Это будет подтверждением, что ваши модели функционально идентичны.

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...