В моей последней статье я показал, как мы можем обучить 2 разных GAN на нормальных и аномальных сигналах LFP, и из результатов (которые вы можете получить самостоятельно, используя репозиторий GitHub, указанный в конце этой статьи) было очевидно, что сеть мог подобрать общие черты, однако потребовалось много времени, чтобы определить 2 модели, а затем обучить их на разных наборах данных. Здесь могут пригодиться условные сети. В cGAN, в дополнение к вашему вводу, вы также вводите в сеть метку, обозначающую, из какой метки берется ввод. Чтобы привести пример, вспомните, скажем, набор данных CIFAR-10, включающий изображения с несколькими метками, в дополнение к вектору из скрытого пространства (шум), который подается в первую секцию GAN (генератор), также подается метка в модель, обозначающую, какой результат он ожидает от генератора, который также используется в дискриминаторе, чтобы сообщить дискриминатору, из какого класса должно происходить сгенерированное изображение.

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

Я собираюсь быстро пройтись по основным компонентам создания cGAN. сначала давайте определим генератор, имеющий два входа: скрытый вектор и метку. Обратите внимание, что в этом случае метка представляет собой двоичное число (0 или 1), обозначающее нормальное или ненормальное поведение в указанном порядке.

def make_disc_model(n_inputs=256):
input_data = tf.keras.layers.Input(n_inputs, name = 'input vector')
cond = tf.keras.layers.Input(1, name = 'conditional input')
out_cond = tf.keras.layers.Dense(4, activation='relu', kernel_initializer='he_uniform', name = 'hidden_conditional_layer')(cond)
new_input = tf.keras.layers.concatenate([out_cond, input_data], name = 'mixed_input')
x = tf.keras.layers.Dense(128, activation='relu', kernel_initializer='he_uniform', name = 'hidden1_d')(new_input)
x = tf.keras.layers.Dense(128, activation='relu', kernel_initializer='he_uniform', name = 'hidden2_d')(x)
x = tf.keras.layers.Dense(32, activation='relu', kernel_initializer='he_uniform', name = 'hidden3_d')(x)
output = tf.keras.layers.Dense(1, activation='sigmoid', kernel_initializer='he_uniform', name = 'output')(x)
model = tf.keras.Model(inputs = (input_data, cond), outputs = output, name = 'model_d')
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model

Как вы можете видеть, он принимает в качестве входных данных условный ввод, отображает его в слой с 4 ячейками, а затем объединяет его с вводом 256 выборок (это зависит от вашего приложения). Результатом является вероятность того, что образец является реальным или поддельным. На рис. 1 показано, как выглядит модель.

Следующим шагом будет определение генератора.

def make_gen_model(latent_dim = 5, n_outputs=256):
latent_vec = tf.keras.layers.Input(latent_dim, name = 'latent vector')
cond = tf.keras.layers.Input(1, name = 'conditional input')
out_cond = tf.keras.layers.Dense(4, activation='relu', kernel_initializer='he_uniform', name = 'hidden_conditional_layer')(cond)
new_input = tf.keras.layers.concatenate([out_cond, latent_vec], name = 'mixed_input')
x = tf.keras.layers.Dense(32, activation='relu', kernel_initializer='he_uniform', name = 'hidden1_g', input_shape = (latent_dim+4,))(new_input)
x = tf.keras.layers.Dense(64, activation='relu', kernel_initializer='he_uniform', name = 'hidden2_d')(x)
x = tf.keras.layers.Dense(128, activation='relu', kernel_initializer='he_uniform', name = 'hidden3_d')(x)
output = tf.keras.layers.Dense(n_outputs, activation='linear', name = 'generated_output')(x)
model = tf.keras.Model(inputs = (latent_vec, cond), outputs = output, name = 'g_model')
return model

Опять же, генератор принимает в качестве входных данных скрытый вектор (шум) плюс условный двоичный ввод и выводит выборку. На рис. 2 показана модель.

Наконец, генератор и дискриминатор создадут окончательную модель GAN:

def make_gan_model(generator, discriminator):
discriminator.trainable = False
gen_label, gen_noise = generator.input
gen_output = generator.output
gan_output = discriminator([gen_output, gen_noise])
model = tf.keras.Model([gen_label, gen_noise], gan_output, name = 'gan_mod')
opt = tf.keras.optimizers.Adam(learning_rate=0.0002, beta_1=0.5)
model.compile(loss='binary_crossentropy', optimizer=opt)
return model

На рис. 3 показано, как выглядит модель.

Для них это просто вопрос обучения сети:

def train(g_model, d_model, gan_model, latent_dim, n_epochs=100, n_batch=128):
bat_per_epo = 8
half_batch = int(n_batch / 2)
for i in range(n_epochs):
for j in range(bat_per_epo):
[X_real, labels_real], y_real = generate_real_samples(half_batch)
d_loss1, _ = d_model.train_on_batch([X_real, labels_real], y_real)
[X_fake, labels], y_fake = generate_fake_samples(g_model, latent_dim, half_batch)
d_loss2, _ = d_model.train_on_batch([X_fake, labels], y_fake)
z_input = np.random.randn(n_batch, 5)
labels_input = np.random.randint(0,2,n_batch)
y_gan = np.ones(n_batch)
g_loss = gan_model.train_on_batch([z_input, labels_input], y_gan)
print('>%d, %d/%d, real=%f, fake=%f gan=%f' %
(i+1, j+1, bat_per_epo, d_loss1, d_loss2, g_loss))

Сделав это, следующим будет смотреть на результаты.

Первым делом нужно будет сгенерировать определенное количество выборок из каждого класса, а затем посмотреть, как выглядят графики PSD, поскольку общие характеристики наземной истины, повышенная амплитуда в диапазоне 0–30 Гц, в некоторой степени известны. Рисунок 4 представляет собой график для 4000 образцов, сгенерированных случайным образом, при этом метки также были сгенерированы случайным образом.

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

На рис. 6 показаны графики для реальных данных для каждого класса, так что вы можете сравнить сами.

Вы можете снова увидеть повышенные амплитуды для положительного класса по сравнению с отрицательными классами. На рис. 7 показана гистограмма для этих данных.

Как видите, положительный класс имеет больше данных в сторону более высокой стороны спектра.

Как показано выше, cGAN — довольно хороший метод для создания синтетических данных, отобранных из обоих классов, и его можно использовать в разных аспектах, одним из которых может быть тестирование различных частей программного и аппаратного обеспечения.

В следующих нескольких статьях основное внимание будет уделено тонкой настройке сгенерированных семплов.

Наконец, я сделал простые скрипты, чтобы вы могли обучить их на своих данных и посмотреть, как они будут выглядеть!