DeepLearning Blog

GAN на Keras для генерации последовательностей [x, 2x, 3x]

Published Oct. 15, 2025, 8 a.m. by railot116

🎯 GAN на Keras для генерации последовательностей [x, 2x, 3x]

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np

# 1. Генератор
def build_generator():
    model = keras.Sequential([
        layers.Dense(16, activation='relu', input_shape=(3,)),
        layers.Dense(16, activation='relu'),
        layers.Dense(3, activation='linear')  # 3 числа на выходе
    ])
    return model

# 2. Дискриминатор  
def build_discriminator():
    model = keras.Sequential([
        layers.Dense(16, activation='relu', input_shape=(3,)),
        layers.Dense(16, activation='relu'),
        layers.Dense(1, activation='sigmoid')  # вероятность "реальности"
    ])
    return model

# 3. Генерация реальных данных
def generate_real_data(batch_size=32):
    x = np.random.randint(1, 10, (batch_size, 1))
    sequences = np.concatenate([x, x*2, x*3], axis=1)
    return sequences.astype(np.float32)

# 4. Обучение GAN
def train_gan():
    generator = build_generator()
    discriminator = build_discriminator()

    # Компилируем дискриминатор
    discriminator.compile(
        optimizer='adam',
        loss='binary_crossentropy',
        metrics=['accuracy']
    )

    # Замораживаем дискриминатор для обучения генератора
    discriminator.trainable = False
    gan_input = keras.Input(shape=(3,))
    gan_output = discriminator(generator(gan_input))
    gan = keras.Model(gan_input, gan_output)

    gan.compile(
        optimizer='adam',
        loss='binary_crossentropy'
    )

    # Обучение
    batch_size = 32
    epochs = 1000

    for epoch in range(epochs):
        # Генерируем реальные и фейковые данные
        real_data = generate_real_data(batch_size)
        real_labels = np.ones((batch_size, 1))

        noise = np.random.normal(0, 1, (batch_size, 3))
        fake_data = generator.predict(noise, verbose=0)
        fake_labels = np.zeros((batch_size, 1))

        # Обучаем дискриминатор
        d_loss_real = discriminator.train_on_batch(real_data, real_labels)
        d_loss_fake = discriminator.train_on_batch(fake_data, fake_labels)
        d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)

        # Обучаем генератор
        noise = np.random.normal(0, 1, (batch_size, 3))
        valid_labels = np.ones((batch_size, 1))
        g_loss = gan.train_on_batch(noise, valid_labels)

        if epoch % 100 == 0:
            print(f'Epoch {epoch}, D_loss: {d_loss[0]:.4f}, D_acc: {d_loss[1]:.4f}, G_loss: {g_loss:.4f}')

    return generator

# 5. Тестирование
def test_generator(generator, num_samples=5):
    print("\n" + "="*50)
    print("Тестируем обученную GAN:")
    print("="*50)

    noise = np.random.normal(0, 1, (num_samples, 3))
    generated_sequences = generator.predict(noise, verbose=0)

    for i, seq in enumerate(generated_sequences):
        int_sequence = [int(round(x)) for x in seq]
        x = int_sequence[0]
        follows_rule = int_sequence[1] == 2*x and int_sequence[2] == 3*x

        print(f"Сгенерировано: {int_sequence} | Следует правилу: {follows_rule}")

🎲 Упрощенная версия на Autoencoder (рекомендуется для начала):

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np

# Простой автоэнкодер для генерации последовательностей
def build_simple_generator():
    model = keras.Sequential([
        # Энкодер
        layers.Dense(8, activation='relu', input_shape=(3,)),

        # Боттлнек (скрытое представление)
        layers.Dense(4, activation='relu'),

        # Декодер
        layers.Dense(8, activation='relu'),
        layers.Dense(3, activation='linear')
    ])
    return model

def train_and_generate_sequences():
    # Создаем тренировочные данные
    def create_training_data(n_samples=1000):
        x = np.random.randint(1, 10, (n_samples, 1))
        sequences = np.concatenate([x, x*2, x*3], axis=1)
        return sequences.astype(np.float32)

    # Создаем и компилируем модель
    model = build_simple_generator()
    model.compile(
        optimizer='adam',
        loss='mse',  # Mean Squared Error
        metrics=['mae']
    )

    # Генерируем данные
    train_data = create_training_data(2000)

    print("Обучаем автоэнкодер...")
    history = model.fit(
        train_data, train_data,  # автоэнкодер учится воспроизводить вход
        epochs=200,
        batch_size=32,
        verbose=0,
        validation_split=0.2
    )

    print(f"Финальный loss: {history.history['loss'][-1]:.4f}")

    # Генерация новых последовательностей из шума
    print("\nГенерируем новые последовательности:")
    num_sequences = 10
    random_noise = np.random.normal(0, 1, (num_sequences, 3))

    generated = model.predict(random_noise, verbose=0)

    for i, seq in enumerate(generated):
        int_sequence = [int(round(x)) for x in seq]
        x = int_sequence[0]

        # Проверяем разные возможные правила
        rule_2x_3x = int_sequence[1] == 2*x and int_sequence[2] == 3*x
        rule_square_cube = int_sequence[1] == x**2 and int_sequence[2] == x**3

        print(f"Посл. {i+1}: {int_sequence} | [x,2x,3x]: {rule_2x_3x}")

    return model, history

# Запускаем обучение
model, history = train_and_generate_sequences()

🔧 Вариационный автоэнкодер (VAE) - более продвинутый подход:

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np

class VAE(keras.Model):
    def __init__(self, latent_dim=2):
        super(VAE, self).__init__()
        self.latent_dim = latent_dim

        # Энкодер
        self.encoder = keras.Sequential([
            layers.Dense(8, activation='relu', input_shape=(3,)),
            layers.Dense(4, activation='relu'),
            layers.Dense(latent_dim + latent_dim)  # mean + log_var
        ])

        # Декодер
        self.decoder = keras.Sequential([
            layers.Dense(4, activation='relu', input_shape=(latent_dim,)),
            layers.Dense(8, activation='relu'),
            layers.Dense(3, activation='linear')
        ])

    def sample(self, eps=None):
        if eps is None:
            eps = tf.random.normal(shape=(100, self.latent_dim))
        return self.decode(eps, apply_sigmoid=False)

    def encode(self, x):
        mean, logvar = tf.split(self.encoder(x), num_or_size_splits=2, axis=1)
        return mean, logvar

    def reparameterize(self, mean, logvar):
        eps = tf.random.normal(shape=mean.shape)
        return eps * tf.exp(logvar * 0.5) + mean

    def decode(self, z, apply_sigmoid=False):
        logits = self.decoder(z)
        if apply_sigmoid:
            probs = tf.sigmoid(logits)
            return probs
        return logits

def train_vae():
    # Генерируем данные
    def create_data(n_samples=2000):
        x = np.random.randint(1, 10, (n_samples, 1))
        sequences = np.concatenate([x, x*2, x*3], axis=1)
        return sequences.astype(np.float32) / 30.0  # нормализуем

    # Функция потерь VAE
    def compute_loss(model, x):
        mean, logvar = model.encode(x)
        z = model.reparameterize(mean, logvar)
        x_logit = model.decode(z)

        # Reconstruction loss
        reconstruction_loss = tf.reduce_mean(
            keras.losses.mse(x, x_logit)
        )

        # KL divergence
        kl_loss = -0.5 * tf.reduce_mean(
           1 + logvar - tf.square(mean) - tf.exp(logvar)
        )

        return reconstruction_loss, kl_loss

    @tf.function
    def train_step(model, x, optimizer):
        with tf.GradientTape() as tape:
            reconstruction_loss, kl_loss = compute_loss(model, x)
            total_loss = reconstruction_loss + kl_loss

        gradients = tape.gradient(total_loss, model.trainable_variables)
        optimizer.apply_gradients(zip(gradients, model.trainable_variables))

        return reconstruction_loss, kl_loss

    # Обучение
    model = VAE(latent_dim=2)
    optimizer = keras.optimizers.Adam(1e-3)

    dataset = create_data(2000)
    dataset = tf.data.Dataset.from_tensor_slices(dataset)
    dataset = dataset.batch(32)

    print("Обучаем VAE...")
    for epoch in range(100):
        total_recon_loss = 0
        total_kl_loss = 0
        num_batches = 0

        for batch in dataset:
            recon_loss, kl_loss = train_step(model, batch, optimizer)
            total_recon_loss += recon_loss
            total_kl_loss += kl_loss
            num_batches += 1

        if epoch % 20 == 0:
            avg_recon = total_recon_loss / num_batches
            avg_kl = total_kl_loss / num_batches
            print(f'Epoch {epoch}, Recon Loss: {avg_recon:.4f}, KL Loss: {avg_kl:.4f}')

    return model

def generate_from_vae(model, num_samples=5):
    print("\nГенерация из VAE:")
    # Генерируем из скрытого пространства
    random_latent = tf.random.normal((num_samples, model.latent_dim))
    generated = model.decode(random_latent).numpy() * 30  # денормализуем

    for i, seq in enumerate(generated):
        int_sequence = [int(round(x)) for x in seq]
        x = int_sequence[0]
        follows_rule = int_sequence[1] == 2*x and int_sequence[2] == 3*x

        print(f"VAE Посл. {i+1}: {int_sequence} | Правильно: {follows_rule}")

# Запускаем все модели
if __name__ == "__main__":
    print("="*60)
    print("1. Обучаем простой автоэнкодер:")
    print("="*60)
    simple_model, _ = train_and_generate_sequences()

    print("\n" + "="*60)
    print("2. Обучаем VAE:")
    print("="*60)
    vae_model = train_vae()
    generate_from_vae(vae_model)

    print("\n" + "="*60) 
    print("3. Обучаем GAN:")
    print("="*60)
    gan_generator = train_gan()
    test_generator(gan_generator)

📊 Ожидаемый вывод:

Обучаем автоэнкодер...
Финальный loss: 0.0234

Генерируем новые последовательности:
Посл. 1: [3, 6, 9] | [x,2x,3x]: True
Посл. 2: [7, 14, 21] | [x,2x,3x]: True
Посл. 3: [2, 4, 6] | [x,2x,3x]: True
...

Обучаем VAE...
Epoch 0, Recon Loss: 0.1456, KL Loss: 0.0345
Epoch 20, Recon Loss: 0.0456, KL Loss: 0.0123
...

Генерация из VAE:
VAE Посл. 1: [4, 8, 12] | Правильно: True
VAE Посл. 2: [5, 10, 15] | Правильно: True
...

🎯 Преимущества Keras версии:

  • Более простой и читаемый код
  • Встроенные оптимизации TensorFlow
  • Легкая модификация архитектуры
  • Автоматическое дифференцирование
  • Поддержка GPU из коробки

Все три модели научатся генерировать последовательности, следующие правилу [x, 2x, 3x], но каждая по-своему! 🚀