Chuyển đến nội dung chính

Giải mã Mạng Đối nghịch Sinh (GAN)

Tìm hiểu Mạng Đối nghịch Sinh là gì mà không đi sâu vào chi tiết toán học và viết mã một GAN đơn giản có thể tạo ra chữ số!
Đã cập nhật 5 thg 10, 2026  · 9 phút đọc

Khám phá cùng AI

ChatGPTClaudePerplexity

Trong hướng dẫn này, bạn sẽ tìm hiểu Mạng Đối nghịch Sinh (GAN) là gì mà không đi sâu vào chi tiết toán học. Sau đó, bạn sẽ học cách viết mã một GAN đơn giản có thể tạo ra các chữ số!

Phép so sánh

Cách dễ nhất để hiểu GAN là gì là thông qua một phép so sánh đơn giản:

Giả sử có một cửa hàng mua một số loại rượu vang nhất định từ khách hàng để sau đó bán lại.

analogy

Tuy nhiên, có những khách hàng xấu bán rượu vang giả để lấy tiền. Trong trường hợp này, chủ cửa hàng phải có khả năng phân biệt giữa rượu giả và rượu thật.

analogy

Bạn có thể hình dung rằng ban đầu, kẻ làm giả có thể mắc rất nhiều sai sót khi cố gắng bán rượu giả và chủ cửa hàng sẽ dễ dàng nhận ra rượu đó không phải là thật. Do những thất bại này, kẻ làm giả sẽ tiếp tục thử các kỹ thuật khác nhau để mô phỏng rượu thật và một số sẽ cuối cùng thành công. Giờ đây, khi kẻ làm giả biết rằng một số kỹ thuật đã vượt qua được kiểm tra của chủ cửa hàng, hắn có thể tiếp tục cải thiện rượu giả dựa trên những kỹ thuật đó.

Đồng thời, chủ cửa hàng có thể nhận được phản hồi từ các chủ cửa hàng khác hoặc chuyên gia rượu rằng một số chai rượu cô ấy có không phải là nguyên bản. Điều này có nghĩa là chủ cửa hàng sẽ phải cải thiện cách xác định rượu là giả hay thật. Mục tiêu của kẻ làm giả là tạo ra rượu không thể phân biệt với rượu thật, còn mục tiêu của chủ cửa hàng là xác định chính xác rượu có thật hay không.

Sự cạnh tranh qua lại này chính là ý tưởng cốt lõi đằng sau GAN.

Các thành phần của một Mạng Đối nghịch Sinh

Dựa trên ví dụ ở trên, ta có thể xây dựng kiến trúc của một GAN.

gan

Có hai thành phần chính trong GAN: bộ sinh (generator) và bộ phân biệt (discriminator). Chủ cửa hàng trong ví dụ được xem như một mạng phân biệt và thường là một mạng nơ-ron tích chập (vì GAN chủ yếu dùng cho các tác vụ hình ảnh) gán xác suất rằng ảnh là thật.

Kẻ làm giả được gọi là mạng sinh và cũng thường là một mạng nơ-ron tích chập (với các tầng giải tích chập). Mạng này nhận một vector nhiễu và xuất ra một ảnh. Khi huấn luyện mạng sinh, nó học được những vùng nào của ảnh cần cải thiện/thay đổi để bộ phân biệt khó phân biệt hơn giữa ảnh sinh ra và ảnh thật.

Mạng sinh liên tục tạo ra các ảnh ngày càng giống ảnh thật trong khi mạng phân biệt cố gắng xác định sự khác biệt giữa ảnh thật và ảnh giả. Mục tiêu cuối cùng là có một mạng sinh có thể tạo ra ảnh không thể phân biệt với ảnh thật.

Một Mạng Đối nghịch Sinh đơn giản với Keras

Bây giờ bạn đã hiểu GAN là gì và các thành phần chính của nó, chúng ta có thể bắt đầu viết mã một phiên bản rất đơn giản. Bạn sẽ dùng Keras; nếu chưa quen với thư viện Python này, bạn nên đọc hướng dẫn này trước khi tiếp tục. Hướng dẫn này dựa trên GAN được phát triển tại đây.

Việc đầu tiên bạn cần làm là cài đặt các gói sau thông qua pip

  • keras
  • matplotlib
  • tensorflow
  • tqdm

Bạn sẽ dùng matplotlib để vẽ biểu đồ, tensorflow làm thư viện backend cho Keras và tqdm để hiển thị thanh tiến trình đẹp cho mỗi epoch (vòng lặp).

Bước tiếp theo là tạo một script Python. Trong script này, trước tiên bạn cần import tất cả các mô-đun và hàm sẽ dùng. Phần giải thích cho từng cái sẽ được đưa ra khi chúng được sử dụng.

import os
import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm


from keras.layers import Input
from keras.models import Model, Sequential
from keras.layers.core import Dense, Dropout
from keras.layers.advanced_activations import LeakyReLU
from keras.datasets import mnist
from keras.optimizers import Adam
from keras import initializers

Bây giờ bạn muốn thiết lập một số biến:

# Let Keras know that we are using tensorflow as our backend engine
os.environ["KERAS_BACKEND"] = "tensorflow"

# To make sure that we can reproduce the experiment and get the same results
np.random.seed(10)

# The dimension of our random noise vector.
random_dim = 100

Trước khi bắt đầu xây dựng bộ phân biệt và bộ sinh, bạn nên thu thập và tiền xử lý dữ liệu. Bạn sẽ dùng bộ dữ liệu MNIST nổi tiếng gồm tập ảnh các chữ số đơn từ 0 đến 9.

mnist dataset

def load_minst_data():
    # load the data
    (x_train, y_train), (x_test, y_test) = mnist.load_data()
    # normalize our inputs to be in the range[-1, 1]
    x_train = (x_train.astype(np.float32) - 127.5)/127.5
    # convert x_train with a shape of (60000, 28, 28) to (60000, 784) so we have
    # 784 columns per row
    x_train = x_train.reshape(60000, 784)
    return (x_train, y_train, x_test, y_test)

Lưu ý rằng mnist.load_data() là một phần của Keras và cho phép bạn dễ dàng nhập bộ dữ liệu MNIST vào không gian làm việc.

Bây giờ, bạn có thể tạo các mạng sinh và phân biệt. Bạn sẽ dùng bộ tối ưu Adam cho cả hai mạng. Với cả bộ sinh và bộ phân biệt, bạn sẽ tạo một mạng nơ-ron có ba tầng ẩn với hàm kích hoạt là Leaky ReLU. Bạn cũng nên thêm các tầng dropout cho bộ phân biệt để cải thiện khả năng chống quá khớp trên ảnh chưa từng thấy.

# You will use the Adam optimizer
def get_optimizer():
    return Adam(lr=0.0002, beta_1=0.5)

def get_generator(optimizer):
    generator = Sequential()
    generator.add(Dense(256, input_dim=random_dim, kernel_initializer=initializers.RandomNormal(stddev=0.02)))
    generator.add(LeakyReLU(0.2))

    generator.add(Dense(512))
    generator.add(LeakyReLU(0.2))

    generator.add(Dense(1024))
    generator.add(LeakyReLU(0.2))

    generator.add(Dense(784, activation='tanh'))
    generator.compile(loss='binary_crossentropy', optimizer=optimizer)
    return generator

def get_discriminator(optimizer):
    discriminator = Sequential()
    discriminator.add(Dense(1024, input_dim=784, kernel_initializer=initializers.RandomNormal(stddev=0.02)))
    discriminator.add(LeakyReLU(0.2))
    discriminator.add(Dropout(0.3))

    discriminator.add(Dense(512))
    discriminator.add(LeakyReLU(0.2))
    discriminator.add(Dropout(0.3))

    discriminator.add(Dense(256))
    discriminator.add(LeakyReLU(0.2))
    discriminator.add(Dropout(0.3))

    discriminator.add(Dense(1, activation='sigmoid'))
    discriminator.compile(loss='binary_crossentropy', optimizer=optimizer)
    return discriminator

Đã đến lúc kết hợp bộ sinh và bộ phân biệt lại với nhau!

def get_gan_network(discriminator, random_dim, generator, optimizer):
    # We initially set trainable to False since we only want to train either the
    # generator or discriminator at a time
    discriminator.trainable = False
    # gan input (noise) will be 100-dimensional vectors
    gan_input = Input(shape=(random_dim,))
    # the output of the generator (an image)
    x = generator(gan_input)
    # get the output of the discriminator (probability if the image is real or not)
    gan_output = discriminator(x)
    gan = Model(inputs=gan_input, outputs=gan_output)
    gan.compile(loss='binary_crossentropy', optimizer=optimizer)
    return gan

Để đầy đủ, bạn có thể tạo một hàm sẽ lưu lại ảnh được sinh ra sau mỗi 20 epoch. Vì đây không phải trọng tâm của bài học, bạn không cần phải hiểu tường tận hàm này.

# Create a wall of generated MNIST images
def plot_generated_images(epoch, generator, examples=100, dim=(10, 10), figsize=(10, 10)):
    noise = np.random.normal(0, 1, size=[examples, random_dim])
    generated_images = generator.predict(noise)
    generated_images = generated_images.reshape(examples, 28, 28)

    plt.figure(figsize=figsize)
    for i in range(generated_images.shape[0]):
        plt.subplot(dim[0], dim[1], i+1)
        plt.imshow(generated_images[i], interpolation='nearest', cmap='gray_r')
        plt.axis('off')
    plt.tight_layout()
    plt.savefig('gan_generated_image_epoch_%d.png' % epoch)

Bạn đã viết xong phần lớn mạng của mình. Việc còn lại là huấn luyện mạng này và xem các ảnh bạn tạo ra.

def train(epochs=1, batch_size=128):
    # Get the training and testing data
    x_train, y_train, x_test, y_test = load_minst_data()
    # Split the training data into batches of size 128
    batch_count = x_train.shape[0] / batch_size

    # Build our GAN netowrk
    adam = get_optimizer()
    generator = get_generator(adam)
    discriminator = get_discriminator(adam)
    gan = get_gan_network(discriminator, random_dim, generator, adam)

    for e in xrange(1, epochs+1):
        print '-'*15, 'Epoch %d' % e, '-'*15
        for _ in tqdm(xrange(batch_count)):
            # Get a random set of input noise and images
            noise = np.random.normal(0, 1, size=[batch_size, random_dim])
            image_batch = x_train[np.random.randint(0, x_train.shape[0], size=batch_size)]

            # Generate fake MNIST images
            generated_images = generator.predict(noise)
            X = np.concatenate([image_batch, generated_images])

            # Labels for generated and real data
            y_dis = np.zeros(2*batch_size)
            # One-sided label smoothing
            y_dis[:batch_size] = 0.9

            # Train discriminator
            discriminator.trainable = True
            discriminator.train_on_batch(X, y_dis)

            # Train generator
            noise = np.random.normal(0, 1, size=[batch_size, random_dim])
            y_gen = np.ones(batch_size)
            discriminator.trainable = False
            gan.train_on_batch(noise, y_gen)

        if e == 1 or e % 20 == 0:
            plot_generated_images(e, generator)

if __name__ == '__main__':
    train(400, 128)

Sau khi huấn luyện 400 epoch, bạn có thể xem các ảnh đã được sinh. Nhìn vào ảnh tạo ra sau epoch đầu tiên, bạn sẽ thấy chúng chưa có cấu trúc rõ ràng; sau 40 epoch, các chữ số bắt đầu hình thành; và cuối cùng, ảnh sau 400 epoch cho thấy các chữ số rõ ràng dù vẫn còn vài cái khó nhận dạng.

Kết quả sau 1 epoch Kết quả sau 40 epoch Kết quả sau 400 epoch
!images1 images2 images3

Đoạn mã này mất khoảng 2 phút mỗi epoch trên CPU và đó là lý do chính đoạn mã này được chọn. Bạn có thể thử nghiệm bằng cách dùng nhiều epoch hơn và thêm nhiều (và khác) tầng cho cả bộ sinh và bộ phân biệt. Tuy nhiên, khi dùng các kiến trúc phức tạp và sâu hơn, thời gian chạy cũng sẽ tăng nếu bạn chỉ dùng CPU. Dù vậy, đừng để điều đó cản trở bạn thử nghiệm!

Kết luận

Chúc mừng, bạn đã hoàn thành hướng dẫn này, trong đó bạn đã học những điều cơ bản về Mạng Đối nghịch Sinh (GAN) theo cách trực quan! Bạn cũng đã triển khai mô hình đầu tiên với sự trợ giúp của thư viện Keras. Nếu muốn biết thêm về học sâu với Python, hãy cân nhắc khóa học Deep Learning in Python của DataCamp.

Chủ đề
Học Sâu
Trí tuệ Nhân tạo
Machine Learning

Tìm hiểu thêm về Python và Deep Learning

Khóa học

Nhập môn Deep Learning với Python

4 giờ
264.7K
Tìm hiểu các kiến thức cơ bản về mạng nơ-ron và cách xây dựng mô hình học sâu bằng Keras 2.0 trong Python.
Xem chi tiếtRight Arrow
Bắt Đầu Khóa Học
Xem thêmRight Arrow
Liên quan

blog

Claude Opus 4.6: Tính năng, Điểm chuẩn, Bài kiểm tra thực hành và hơn thế nữa

Mô hình mới nhất của Anthropic dẫn đầu ở mã hóa tác tử và lập luận phức tạp. Thêm vào đó, nó có cửa sổ ngữ cảnh 1M.
Matt Crabtree's photo

Matt Crabtree

10 phút

Xem ThêmXem Thêm