Table of Contents

8. Autoenkodery

8.1. Wprowadzenie

W niniejszym rozdziale zajmiemy się modelami sieci neuronowych zwanymi autoenkoderami. Autoenkodery są to takie modele, które uczą się reprezentacji danych wejściowych w przestrzeni o niższej wymiarowości, zwanej przestrzenią latentną.

Autoenkodery składają się z dwóch głównych części:

  • enkodera, który mapuje dane wejściowe do przestrzeni latentnej,
  • dekodera, który rekonstruuje dane wejściowe z tej przestrzeni.

Pomiędzy tymi dwiema częściami znajduje się warstwa latentna (bottleneck, reprezentacja skompresowana), która jest warstwą o mniejszej liczbie neuronów niż warstwa wejściowa, co wymusza na modelu uczenie się skompresowanej reprezentacji danych.

Celem autoenkodera jest nauczenie się takiej reprezentacji danych, która pozwala na jak najlepszą rekonstrukcję danych wejściowych. Autoenkodery są szeroko stosowane w różnych dziedzinach, takich jak redukcja wymiarowości, detekcja anomalii, generowanie danych oraz transfer stylu.

Przykładowy model autoenkodera może wyglądać następująco:

class AutoencoderModel(SeededRandom? random)
    : BaseModel<float[,], float[,]>(new MeanSquaredErrorLoss(), random)
{
    protected override LayerListBuilder<float[,], float[,]> CreateLayerListBuilder()
    {
        GlorotInitializer initializer = new(Random);

        return
            AddLayer(new DenseLayer(64, new ReLU(), initializer)) // Enkoder
            .AddLayer(new DenseLayer(32, new ReLU(), initializer)) // Warstwa latentna
            .AddLayer(new DenseLayer(64, new ReLU(), initializer)) // Dekoder
            .AddLayer(new DenseLayer(inputSize, new Linear(), initializer)); // Warstwa wyjściowa
    }
}

Listing 8.1. Przykładowa architektura autoenkodera (wygenerowana tutaj przez GitHub Copilot)

8.2. Autoenkodery z warstwami konwolucyjnymi

My posłużymy się nieco bardziej skomplikowanym modelem autoenkodera, który będzie wykorzystywał warstwy konwolucyjne. Przeanalizujemy jego działanie na przykładzie zbioru danych MNIST, a następnie pokażemy, jak można wykorzystać wytrenowany autoenkoder do generowania nowych obrazów.

Definicja tego modelu jest następująca:

class AutoencoderModel(int bottleneckDim, SeededRandom? random, string? modelFilePath = null) 
    : BaseModel<float[,,,], float[,,,]>(null, random, modelFilePath)
{

    private const int InnerChannels = 7;
    private const int ImageInnerSize = 28;
    private Layer<float[,], float[,]>? _bottleneckLayer;
    private Layer<float[,], float[,]>? _firstDecoderLayer;

    protected override LayerListBuilder<float[,,,], float[,,,]> CreateLayerListBuilder()
    {
        ParamInitializer initializer = new GlorotInitializer(Random);

        _bottleneckLayer = new DenseLayer(bottleneckDim, new Linear(), initializer);
        _firstDecoderLayer = new DenseLayer(ImageInnerSize * ImageInnerSize * InnerChannels, new Linear(), initializer);

        return
            // 1. Encoder
            AddLayer(new Conv2DLayer(
                kernels: 14,
                kernelHeight: 5,
                kernelWidth: 5,
                activationFunction: new Tanh4D(),
                paramInitializer: initializer
            ))
            .AddLayer(new Conv2DLayer(
                kernels: 7,
                kernelHeight: 5,
                kernelWidth: 5,
                activationFunction: new Tanh4D(),
                paramInitializer: initializer
            ))
            .AddLayer(new FlattenLayer())

            // 2. Bottleneck
            .AddLayer(_bottleneckLayer)

            // 3. Decoder
            .AddLayer(_firstDecoderLayer)
            .AddLayer(new UnflattenLayer(InnerChannels, ImageInnerSize, ImageInnerSize))
            .AddLayer(new Conv2DLayer(
                kernels: 14,
                kernelHeight: 5,
                kernelWidth: 5,
                activationFunction: new Tanh4D(),
                paramInitializer: initializer
            ))
            .AddLayer(new Conv2DLayer(
                kernels: 1,
                kernelHeight: 5,
                kernelWidth: 5,
                activationFunction: new Tanh4D(),
                paramInitializer: initializer
            ));
    }

    /// <summary>
    /// Gets the encoded representation (latent data) produced by the bottleneck layer of the model.
    /// </summary>
    /// <returns>
    /// A two-dimensional array of floating-point values representing the output of the bottleneck layer.
    /// </returns>
    /// <exception cref="InvalidOperationException">Thrown if the bottleneck layer output is not available.</exception>
    public float[,] GetEncodedRepresentation()
    {
        return _bottleneckLayer?.Output
            ?? throw new InvalidOperationException("Bottleneck layer output is not available.");
    }

    /// <summary>
    /// Forward encoded representation and return the decoded output. This can be used to visualize the output of the
    /// decoder part of the autoencoder based on randomly generated encoded data or to see how the decoder reconstructs
    /// the input data from the encoded (bottleneck) representation.
    /// </summary>
    public float[,,,] Decode(float[,] encoded)
    {
        // We need to pass the encoded data through the first decoder layer and then through the remaining layers of the model.

        if (_firstDecoderLayer is null)
            throw new InvalidOperationException("Decoder layer is not initialized.");

        return InferFromLayer(_firstDecoderLayer, encoded);
    }
}

Listing 8.2. Definicja modelu autoenkodera z warstwami konwolucyjnymi

Note

Powyższy kod w pełnej wersji znajduje się na GitHub.


Created: 2026-05-15

Last modified: 2026-05-15

Title: 8. Autoenkodery

Tags: [C#] [Sieci neuronowe] [Biblioteka] [NeuralNetworks] [MNIST] [CNN] [Convolutional Neural Networks] [Autoencoders] [Autoenkodery]