Зеҳн
Омӯзиши мошинӣ ва амиқ

Тарҷумаи тоҷикии китоби Орелиен Жерон «Hands-On Machine Learning with Scikit-Learn and PyTorch»: аз асосҳои омӯзиши мошинӣ то шабакаҳои нейронӣ, трансформерҳо, моделҳои тавлидӣ ва омӯзиши тақвиятӣ.

Агенти Зеҳнӣ · 13 ноябри 2026 · 20 саҳифа · 1 писанд

ИлмБарномасозӣТехнология
Аз аввал хондан

Боби 18. Автоэнкодерҳо, GAN ва моделҳои диффузионӣ

Автоэнкодерҳо шабакаҳои сунъии нейронӣ мебошанд, ки бе назорат, яъне аз маҷмӯи омӯзишии бебарчасп, намояндагиҳои зичи додаҳои вурудиро меомӯзанд. Онҳоро намояндагиҳои ниҳонӣ ё рамзҳо (codings) меноманд. Ин рамзҳо одатан аз додаи вурудӣ хеле камченакаанд, аз ин рӯ автоэнкодер барои кам кардани андозанокӣ (боби 7), махсусан барои намоиши додаҳо, муфид аст. Автоэнкодер ҳамчун ошкорсози хусусиятҳо ҳам амал мекунад ва барои пешомӯзиши беназорати шабакаҳои амиқ истифода мешавад (боби 11). Истифодаи маъмули дигар ошкорсозии ғайримуқаррарӣ аст. Баъзе автоэнкодерҳо модели тавлидӣ низ мебошанд: додаҳои нави тасодуфие месозанд, ки ба омӯзишӣ хеле монанданд. Масалан, баъди омӯзиш дар аксҳои чеҳра чеҳраҳои нав тавлид мекунанд.

Шабакаҳои тавлидии рақобатӣ (Generative Adversarial Networks — GAN) ҳам дода месозанд. Чеҳраҳои онҳо чунон боварибахшанд, ки бовар кардан душвор аст ин одамон вуҷуд надоранд. Дар https://thispersondoesnotexist.com чеҳраҳои StyleGAN-ро худ мебинед. GAN барои фавқултафсилнокӣ, яъне зиёд кардани тафсилнокии тасвир, рангинсозӣ, таҳрири қавии акс, масалан бо заминаи воқеӣ иваз кардани шахси тасодуфан ба кадр даромада, табдили нақша ба акси воқеиятмонанд, пешгӯии кадрҳои видео, афзоишдиҳии маҷмӯи додаҳо, тавлиди матн, аудио ва силсилаи вақтӣ, ёфтани заъфи моделҳои дигар барои қавӣ кардани онҳо ва ғайра истифода шудаанд.

Аммо аз аввали солҳои 2020 моделҳои диффузионӣ асосан ҷойи GAN-ро гирифтанд: тасвирҳои гуногунтару босифаттар месозанд ва омӯзишашон осонтар аст. Дар иҷро хеле сусттаранд, пас барои тавлиди бисёр тез GAN ҳанӯз муфид мебошад.

Автоэнкодер, GAN ва диффузия ҳама бе барчасп омӯзонда шуда, намояндагии ниҳонӣ меомӯзанд, ҳамчун тавлидгар истифода мешаванд ва татбиқҳои монанд доранд. Вале тарзи корашон хеле фарқ мекунад:

  • Автоэнкодер нусха кардани вуруд ба баромадро меомӯзад. Сода менамояд, аммо маҳдудият онро душвор мекунад: андозаи рамзро хурд ё вурудро пурғавғо карда, барқарорсозии нусхаи тозаро меомӯзонем. Ин маҳдудиятҳо нусхабардории бевоситаро манъ мекунанд ва моделро ба омӯхтани намояндагии самаранок маҷбур месозанд. Рамзҳо маҳсули омӯхтани функсияи айният дар шароити маҳдуданд.
  • GAN ду шабака дорад: тавлидгар додаи ба воқеӣ монанд месозад, фарқгузор воқеиро аз сохта ҷудо мекунад. Онҳо дар омӯзиш рақобат мекунанд — омӯзиши рақобатӣ. Тавлидгар ба сохтакори пул ва фарқгузор ба муфаттиши шинохтани пули қалбакӣ монанд аст.
  • Модели диффузионӣ тадриҷан ғавғоро аз тасвир дур кардан меомӯзад. Агар аз тасвири тамоман тасодуфии пурғавғо сар карда, онро такроран иҷро кунед, акси босифати ба додаҳои омӯзишӣ монанд, вале на яксон, пайдо мешавад.

Аввал автоэнкодер ва истифодааш барои кам кардани андозанокӣ, истихроҷи хусусият, пешомӯзиши беназорат ва тавлидро меомӯзем. Баъд GAN-и сода барои акси сохта месозем ва душвориҳои омӯзиши рақобативу роҳҳои ҳалли онҳоро мебинем. Дар охир модели эҳтимолии диффузияи ғавғобардор (DDPM) сохта, омӯзонда ва тасвир тавлид мекунем. Аз автоэнкодер оғоз мекунем!

Намояндагиҳои самараноки додаҳо

Кадом пайдарпайиро осонтар аз ёд мекунед?

40, 27, 25, 36, 81, 57, 10, 73, 19, 68
50, 48, 46, 44, 42, 40, 38, 36, 34, 32, 30, 28, 26, 24, 22, 20, 18, 16, 14

Дар назари аввал якум, чун хеле кӯтоҳтар аст. Аммо дуюм танҳо ададҳои ҷуфти аз 50 то 14 мебошад. Вақте ки қонуниятро дидед, танҳо «ҷуфтҳои камшаванда», оғоз 50 ва анҷом 14-ро дар хотир доред. Агар пайдарпайии бисёр дарозро осон аз ёд мекардем, ба қонуният аҳаммият намедодем. Маҳз маҳдудияти хотира шинохти қонуниятро муфид мекунад. Ҳамин тавр маҳдуд кардани автоэнкодер онро ба кашфу истифодаи нақшҳои додаҳо водор мекунад.

Уилям Чейз ва Ҳерберт Саймон аввали солҳои 1970 робитаи хотира, идрок ва шинохти нақшро омӯхтанд. Шоҳмотбозони моҳир бо панҷ сония дидани тахта ҷойи ҳамаи муҳраҳоро аз ёд мекарданд — барои аксари одамон номумкин менамояд. Вале танҳо барои мавқеъҳои воқеии бозӣ, на ҷойгиркунии тасодуфӣ. Хотираи онҳо аз мо хеле беҳтар нест: аз таҷриба нақшҳои шоҳмотро осонтар мебинанд ва иттилоотро самаранок нигоҳ медоранд.

Автоэнкодер ҳам вурудро ба намояндагии самараноки ниҳонӣ табдил дода, баъд нусхаи ҳарчи наздикро месозад. Ҳамеша ду қисм дорад: энкодер ё шабакаи шинохт, ки рамз месозад, ва декодер ё шабакаи тавлид, ки аз он баромад месозад.

Дар расми 18.1 автоэнкодер персептрони бисёрқабата (MLP)-и одии боби 9 аст. Чун бояд вурудро барқарор кунад, шумораи баромадҳо бо вурудҳо баробар — ин ҷо 3 — аст. Қисми поёнӣ энкодери якқабатаи ду-нейронӣ ва болоӣ декодер аст. Баромадҳоро бозсозӣ (reconstruction) меноманд. Функсияи хароҷот ҳамеша талафоти бозсозӣ дорад, ки фарқи баромаду вурудро ҷазо медиҳад.

Таҷрибаи хотираи шоҳмот

Расми 18.1. Таҷрибаи хотираи шоҳмот (чап) ва автоэнкодери сода (рост).

Чун намояндагии дохилӣ 2D ва вуруд 3D аст, онро автоэнкодери камандоза (undercomplete) меноманд. Вурудро ба рамз бевосита нусха карда наметавонад, аммо баромад бояд нусха бошад. Пас маҷбур мешавад, ки додаҳоро фишурда, хусусиятҳои муҳимро омӯзад ва чизҳои номуҳимро партояд.

Автоэнкодери бисёр содаи камандозаро барои кам кардани андозанокӣ месозем.

PCA бо автоэнкодери хаттии камандоза

Агар фаъолсозӣ танҳо хаттӣ ва талафот MSE бошад, автоэнкодер амалан таҳлили ҷузъҳои асосӣ (PCA)-и боби 7-ро иҷро мекунад.

Код вуруди 3D-ро ба 2D ва боз ба 3D мебарад. Ҳадаф ҳамон вуруд аст, пас поёнравии градиентӣ бояд ҳамвории 2D-и ба дода наздиктаринро, мисли PCA, ёбад:

import torch
import torch.nn as nn

torch.manual_seed(42)
encoder = nn.Linear(3, 2)
decoder = nn.Linear(2, 3)
autoencoder = nn.Sequential(encoder, decoder).to(device)

Аз MLP-ҳои пешина фарқи зиёд надорад, вале:

  • модел ба энкодеру декодер, ҳар кадом як Linear, ҷудо ва пайдарпай гузошта шудааст;
  • баромаду вуруд ҳар ду 3 адад доранд;
  • барои PCA фаъолсозии ғайрихаттӣ нест ва талафот MSE аст, зеро PCA табдили хаттӣ мебошад. Ба зудӣ вариантҳои ғайрихаттиро мебинем.

Дар ҳамон додаҳои сунъии 3D-и боби 7 меомӯзонем:

from torch.utils.data import DataLoader, TensorDataset

X_train = [...]  # сохтани додаҳои 3D, мисли боби 7
train_set = TensorDataset(X_train, X_train)  # вуруд ҳамон ҳадаф аст
train_loader = DataLoader(train_set, batch_size=32, shuffle=True)

X_train ҳам вуруд ва ҳам ҳадаф аст. Бо train()-и боби 10 меомӯзонем; дафтари боб варианти муфассалтар бо чопу арзёбии ҳар давр дорад:

import torchmetrics

optimizer = torch.optim.NAdam(autoencoder.parameters(), lr=0.2)
mse = nn.MSELoss()
rmse = torchmetrics.MeanSquaredError(squared=False).to(device)
train(autoencoder, optimizer, mse, train_loader, n_epochs=20)

Баъд танҳо энкодер 3D-ро ба 2D фишурда мекунад:

codings = encoder(X_train.to(device))

Расми 18.2 додаҳои аслии 3D ва рамзҳои қабати пинҳониро нишон медиҳад. Модел ҳамвории беҳтаринро ёфта, ҳарчи бештари дисперсияро нигоҳ дошт — монанди PCA.

PCA-и тақрибӣ бо автоэнкодери хаттии камандоза

Расми 18.2. PCA-и тақрибӣ бо автоэнкодери хаттии камандоза.

Эзоҳ. Автоэнкодерро омӯзиши худназоратӣ ҳам шуморидан мумкин аст: усули назоратшаванда, вале барчаспҳо худкор сохта мешаванд — ин ҷо ҳамон вуруд мебошанд.

Автоэнкодерҳои чандқабата

Автоэнкодер метавонад чанд қабати пинҳонӣ дошта бошад: stacked ё deep autoencoder. Қабатҳои бештар рамзи мураккабтар меомӯзанд. Вале аз ҳад тавоно накунед: агар энкодер ҳар вурудро ба як адади дилхоҳ тасвир кунад ва декодер амали баръаксро иҷро намояд, чунин модел додаҳои омӯзиширо комил бозмесозад, аммо намояндагии муфид ё умумисозии хуб намеомӯзад.

Меъморӣ одатан нисбат ба қабати марказии рамз симметрӣ, мисли сэндвич, аст. Барои Fashion MNIST: 784 → 128 → 32 → 128 → 784. Ҳамаи қабатҳои пинҳонӣ фаъолсозӣ, масалан ReLU, доранд (расми 18.3).

Автоэнкодери чандқабата

Расми 18.3. Автоэнкодери чандқабата.

Татбиқ бо PyTorch

Мисли MLP-и амиқ месозем:

stacked_encoder = nn.Sequential(
    nn.Flatten(),
    nn.Linear(1 * 28 * 28, 128), nn.ReLU(),
    nn.Linear(128, 32), nn.ReLU(),
)
stacked_decoder = nn.Sequential(
    nn.Linear(32, 128), nn.ReLU(),
    nn.Linear(128, 1 * 28 * 28), nn.Sigmoid(),
    nn.Unflatten(dim=1, unflattened_size=(1, 28, 28))
)
stacked_ae = nn.Sequential(stacked_encoder, stacked_decoder).to(device)

Энкодер тасвири хокистарии якканалаи 28 × 28-ро ба вектори 784 ҳамвор карда, аз Linear-и 128 ва 32 бо ReLU мегузаронад. Барои ҳар тасвир рамзи 32-ченака медиҳад. Декодер баръакс 32→128→784 карда, ба 1 × 28 × 28 бармегардонад. Sigmoid-и охир қимати пикселро 0–1 мекунад.

Fashion MNIST-ро бо TorchVision гирифта, мисли боби 10 ба train_data, valid_data, test_data ҷудо ва бо ҳадафи баробари вуруд MSE-ро кам кунед. Ин машқи хуб аст. Ҳадафҳоро иваз карданро фаромӯш накунед: автоэнкодер меомӯзонем, на таснифгар. Агар душвор шавад, дафтари бобро бинед.

Намоиши бозсозиҳо

Барои санҷиши сифат вуруд ва баромадро муқоиса кунед: фарқ набояд аз ҳад зиёд бошад. Чанд акси тасдиқӣ ва нусхаҳои онҳоро мекашем:

import matplotlib.pyplot as plt

def plot_image(image):
    plt.imshow(image.permute(1, 2, 0).cpu(), cmap="binary")
    plt.axis("off")

def plot_reconstructions(model, images, n_images=5):
    images = images[:n_images]
    with torch.no_grad():
        y_pred = model(images.to(device))

    fig = plt.figure(figsize=(len(images) * 1.5, 3))
    for idx in range(len(images)):
        plt.subplot(2, len(images), 1 + idx)
        plot_image(images[idx])
        plt.subplot(2, len(images), 1 + len(images) + idx)
        plot_image(y_pred[idx])

X_valid = torch.stack([x for x, _ in valid_data])
plot_reconstructions(stacked_ae, X_valid)
plt.show()

Аксҳои аслӣ

Расми 18.4. Аксҳои аслӣ (боло) ва бозсозиҳо (поён).

Бозсозиҳо шинохташавандаанд, вале иттилооти зиёд гум кардаанд. Шояд омӯзиши бештар, энкодеру декодери тавонотар ё рамзи калонтар лозим бошад. Ҳоло ҳаминро барои татбиқҳо мегирем.

Ошкорсозии ғайримуқаррарӣ бо автоэнкодер

Агар тасвир аз додаҳои омӯзишӣ фарқ кунад — берун аз тақсимот (out of distribution) бошад — бозсозӣ аксар вақт бад мешавад. Расми 18.5 рақамҳои MNIST-ро бо модели дар Fashion MNIST омӯхташуда бозмесозад: баромад аз вуруд хеле фарқ дорад. MSE баланд мешавад. Остона муайян кунед; тасвири талафоти бозсозиаш аз он баландро ғайримуқаррарӣ ҳисоб кунед.

Тасвирҳои берун аз тақсимот бад бозсозӣ мешаванд

Расми 18.5. Тасвирҳои берун аз тақсимот бад бозсозӣ мешаванд.

Ҳамин! Акнун татбиқи дигар.

Намоиши маҷмӯи Fashion MNIST

Автоэнкодери камандоза андозанокиро кам мекунад, вале одатан мустақим то 2 ё 3 андоза хуб намерасад: барои бозсозии дуруст андозаи кофӣ лозим аст. Пас одатан воситаи бевоситаи визуализатсия нест. Аммо маҷмӯи азимро хуб коркард мекунад. Аввал андозаро то сатҳи муносиб кам, баъд усули дигари боби 7-ро барои намоиш истифода баред.

Fashion MNIST-ро аввал энкодер то 32 ва баъд t-SNE-и Scikit-Learn то 2 мебарад:

from sklearn.manifold import TSNE

with torch.no_grad():
    X_valid_compressed = stacked_encoder(X_valid.to(device))

tsne = TSNE(init="pca", learning_rate="auto", random_state=42)
X_valid_2D = tsne.fit_transform(X_valid_compressed.cpu())

Намоиш:

y_valid = torch.tensor([y for _, y in valid_data])

plt.scatter(X_valid_2D[:, 0], X_valid_2D[:, 1], c=y_valid, s=10, cmap="tab10")
plt.show()

Расми 18.6 бо иловаи баъзе тасвирҳо зеботар шудааст. t-SNE гурӯҳҳои ба синфҳо муносиб ёфт, ҳар синф ранги ҷудо дорад. Бо seed, дода ё платформаи дигар натиҷа хеле фарқ карда метавонад.

Намоиши Fashion MNIST бо автоэнкодер ва баъд t-SNE

Расми 18.6. Намоиши Fashion MNIST бо автоэнкодер ва баъд t-SNE.

Пешомӯзиши беназорат бо автоэнкодери чандқабата

Тавре дар боби 11 дидем, барои вазифаи назоратшавандаи мураккаби камбарчасп қабатҳои поёнии модели вазифаи монандро такроран истифода бурдан мумкин аст. Онҳо хусусиятҳои поёнсатҳро аллакай омӯхтаанд ва модели нав додаи камтар мехоҳад.

Агар маҷмӯи калон доред, вале аксараш бебарчасп, аввал автоэнкодерро дар ҳамаи додаҳо омӯзонед. Баъд қабатҳои поён, яъне энкодерро барои вазифаи воқеӣ гирифта, бо қисми барчаспдор меомӯзонед. Расми 18.7 ҳаминро барои тасниф нишон медиҳад. Бо барчаспи хеле кам қабатҳои пешомӯхташуда, ҳадди ақал поёнтаринҳоро, ях кардан мумкин аст.

Пешомӯзиши беназорат бо автоэнкодер

Расми 18.7. Пешомӯзиши беназорат бо автоэнкодер.

Эзоҳ. Додаи бебарчаспи зиёд ва барчаспдори кам маъмул аст. Миллионҳо аксро скрипт арзон мегирад, аммо боэътимод барчаспгузорӣ, масалан «зебо ё не», одатан инсон мехоҳад. Ин вақт ва маблағ мегирад, пас чанд ҳазор ё камтар намунаи барчаспдор муқаррарӣ аст. Ҳоло истифодаи AI-и тавоно барои барчаспгузорӣ ҳам зиёд мешавад.

Татбиқи он душвор нест: аввал автоэнкодерро дар ҳамаи додаҳои омӯзишӣ, ҳам бо барчасп ва ҳам бе он, меомӯзонед ва баъд дар асоси энкодери он шабакаи навро дар қисми барчаспдор меомӯзонед.

Акнун чанд усули омӯзишро мебинем.

Муштарак кардани вазнҳо

Дар автоэнкодери симметрӣ вазни декодерро ба транспозашудаи энкодер пайвастан маъмул аст. Вазнҳо тақрибан нисф, омӯзиш тез ва хатари аз ҳад мутобиқшавӣ кам мешавад. Агар N қабат, бе ҳисоб кардани вуруд, ва W_L вазни қабати L бошад, қабатҳои декодер чунинанд:

WL = Wᵀ(N−L+1), барои L=N/2+1, …, N.

Ин ҷо қабати 1 аввалини пинҳонӣ, N/2 рамз ва N баромад аст. Варианти модели пешина бо вазнҳои муштарак:

import torch.nn.functional as F

class TiedAutoencoder(nn.Module):
    def __init__(self):
        super().__init__()
        self.enc1 = nn.Linear(1 * 28 * 28, 128)
        self.enc2 = nn.Linear(128, 32)
        self.dec1_bias = nn.Parameter(torch.zeros(128))
        self.dec2_bias = nn.Parameter(torch.zeros(1 * 28 * 28))

    def encode(self, X):
        Z = X.view(-1, 1 * 28 * 28)  # ҳамворкунӣ
        Z = F.relu(self.enc1(Z))
        return F.relu(self.enc2(Z))

    def decode(self, X):
        Z = F.relu(F.linear(X, self.enc2.weight.t(), self.dec1_bias))
        Z = torch.sigmoid(F.linear(Z, self.enc1.weight.t(), self.dec2_bias))
        return Z.view(-1, 1, 28, 28)  # барқарор кардани шакл

    def forward(self, X):
        return self.decode(self.encode(X))

Модел бо тақрибан нисфи параметрҳо хатои бозсозии камтар мегирад.

Омӯзиши як автоэнкодер дар ҳар навбат

Ба ҷойи якбора омӯзондани тамоми шабака, ҳар дафъа як автоэнкодери камқабата омӯзонда, баъд болои ҳам мегузорем (расми 18.8). Ин омӯзиши ҳарисонаи қабат ба қабат (greedy layerwise training) аст.

Аввал автоэнкодери якум вурудро бозмесозад. Бо энкодери он тамоми маҷмӯаро фишурда карда, маҷмӯи нав мегирем. Автоэнкодери дуюм дар он меомӯзад. Баъд ҳамаи энкодерҳоро пайдарпай ва декодерҳоро ба тартиби баръакс мегузорем. Метавон ин корро идома дода, модели хеле амиқ сохт.

Омӯзиши як автоэнкодер дар ҳар марҳила

Расми 18.8. Омӯзиши як автоэнкодер дар ҳар марҳила.

Яке аз ангезаҳои мавҷи омӯзиши амиқ кашфи Ҳинтон ва ҳамкорон дар соли 2006 буд: пешомӯзиши беназорати қабат ба қабат имконпазир аст. Онҳо мошинҳои маҳдуди Болтсман (RBM)-ро истифода карданд — https://homl.info/extra-anns. Соли 2007 Йошуа Бенҷио ва ҳамкорон нишон доданд, ки автоэнкодер ҳам хуб аст. Чанд сол ин роҳи асосии самараноки омӯзиши шабакаи амиқ буд, то усулҳои боби 11 омӯзиши якбораро осон карданд.

Автоэнкодер танҳо шабакаи зич нест; конволютсионӣ ҳам мешавад.

Автоэнкодерҳои конволютсионӣ

Барои тасвири ғайрихеле хурд шабакаҳои зич хуб нестанд; CNN-и боби 12 муносибтар аст. Барои пешомӯзиш ё кам кардани андоза автоэнкодери конволютсионӣ месозем. Энкодер CNN-и одии конволютсия ва pooling аст: баландию паҳноро кам, каналҳоро зиёд мекунад. Декодер баръакс андозаи фазоиро калон ва умқро то асл кам мекунад. Конволютсияи транспозашуда, ё upsampling ва конволютсияи одӣ, муносиб аст. Барои Fashion MNIST:

conv_encoder = nn.Sequential(
    nn.Conv2d(1, 16, kernel_size=3, padding="same"), nn.ReLU(),
    nn.MaxPool2d(kernel_size=2),  # 16 × 14 × 14
    nn.Conv2d(16, 32, kernel_size=3, padding="same"), nn.ReLU(),
    nn.MaxPool2d(kernel_size=2),  # 32 × 7 × 7
    nn.Conv2d(32, 64, kernel_size=3, padding="same"), nn.ReLU(),
    nn.MaxPool2d(kernel_size=2),  # 64 × 3 × 3
    nn.Conv2d(64, 32, kernel_size=3, padding="same"), nn.ReLU(),
    nn.AdaptiveAvgPool2d((1, 1)), nn.Flatten())  # 32

conv_decoder = nn.Sequential(
    nn.Linear(32, 16 * 3 * 3),
    nn.Unflatten(dim=1, unflattened_size=(16, 3, 3)),
    nn.ConvTranspose2d(16, 32, kernel_size=3, stride=2), nn.ReLU(),
    nn.ConvTranspose2d(32, 16, kernel_size=3, stride=2, padding=1,
                       output_padding=1), nn.ReLU(),
    nn.ConvTranspose2d(16, 1, kernel_size=3, stride=2, padding=1,
                       output_padding=1), nn.Sigmoid())

conv_ae = nn.Sequential(conv_encoder, conv_decoder).to(device)

RNN ва меъмориҳои дигар ҳам мумкинанд; намуна дар дафтар аст.

То ин ҷо автоэнкодери одӣ, чандқабата ва конволютсионӣ, омӯзиши якбора ё қабатӣ ва татбиқҳои кам кардани андоза, ғайримуқаррарӣ ва пешомӯзишро дидем. То ҳол барои хусусияти муфид рамзро хурд кардем. Маҳдудиятҳои дигар имкон медиҳанд рамз баробари вуруд ё калонтар — overcomplete, яъне аз ҳад пурра — бошад. Акнун ғавғобардор, камзич ва вариатсиониро мебинем.

Автоэнкодерҳои ғавғобардор

Роҳи содаи омӯзиши хусусияти муфид: ба вуруд ғавғо илова ва вурудҳои аслии тоза-ро ҳадаф кунед. Ғоя аз солҳои 1980, аз ҷумла рисолаи магистрии Янн Лекун дар соли 1987, вуҷуд дошт. Паскал Венсан ва ҳамкорон соли 2008 истихроҷи хусусият ва соли 2010 автоэнкодерҳои ғавғобардори чандқабатаро пешниҳод карданд.

Ғавғо метавонад Гауссии иловашаванда ё хомӯшкунии тасодуфии вурудҳо, мисли dropout-и боби 11, бошад (расми 18.9).

Автоэнкодери ғавғобардор бо ғавғои Гауссӣ

Расми 18.9. Автоэнкодери ғавғобардор бо ғавғои Гауссӣ (чап) ё dropout (рост).

Татбиқи dropout сода аст: ҳамон автоэнкодер бо Dropout дар вуруди энкодер, ки танҳо ҳангоми омӯзиш фаъол аст. Рамзро дигар он қадар хурд кардан шарт нест, зеро ғавғо худи вазифаро душвор мекунад:

dropout_encoder = nn.Sequential(
    nn.Flatten(),
    nn.Dropout(0.5),
    nn.Linear(1 * 28 * 28, 128), nn.ReLU(),
    nn.Linear(128, 128), nn.ReLU(),
)
dropout_decoder = nn.Sequential(
    nn.Linear(128, 128), nn.ReLU(),
    nn.Linear(128, 1 * 28 * 28), nn.Sigmoid(),
    nn.Unflatten(dim=1, unflattened_size=(1, 28, 28))
)
dropout_ae = nn.Sequential(dropout_encoder, dropout_decoder).to(device)

Эзоҳ. Ба MLM-и BERT-и боби 15 монанд аст: бозсозии вуруди ниқобшуда, гарчанде BERT ба энкодеру декодери алоҳида ҷудо нест.

Расми 18.10 тасвирҳои нисфи пикселҳояшон хомӯш ва бозсозиҳоро нишон медиҳад. Модел ҳатто ҷузъиёти дар вуруд набударо, монанди болои пойафзоли аз ҳама рост, тахмин мекунад. Ғайр аз визуализатсия ва пешомӯзиш, онро барои тоза кардани ғавғои тасвир ҳам содаю самаранок истифода мебаранд.

Тасвирҳои пурғавғо

Расми 18.10. Тасвирҳои пурғавғо (боло) ва бозсозиҳо (поён).

Автоэнкодерҳои камзич

Маҳдудияти дигари муфид камзичӣ (sparsity) аст: ҳадди иловагии талафот шумораи нейронҳои фаъоли рамзро кам мекунад. Ҳар вуруд аз шумораи ками фаъолшавӣ ифода мешавад ва ҳар нейрон одатан хусусияти муфидеро мегирад. Агар танҳо чанд калима дар моҳ гуфтан метавонистед, эҳтимол маънодорро интихоб мекардед!

Роҳи сода: дар қабати рамз sigmoid барои қиматҳои 0–1, рамзи калон, масалан 256 воҳид, ва мунтазамсозии ℓ₁-и фаъолшавиҳо. Ҷамъи қиматҳои мутлақи рамз бо гиперпараметри камзичӣ ба талафот илова мешавад. Он рамзҳоро ба 0 мебарад, аммо бозсозӣ талаб мекунад, ки ақаллан баъзеашон ғайрисифр монанд. ℓ₁ бар хилофи ℓ₂ хусусиятҳои муҳимтаринро нигоҳ медорад ва нолозимҳоро нест мекунад, на ин ки ҳамаро танҳо хурд кунад.

Роҳи дигар, ки аксар вақт беҳтар аст, миёнаи фаъолшавии ҳар нейрон дар баста ва ҷазои фарқ аз ҳадаф, масалан 10%, мебошад. Баста хеле хурд набошад, то миёна боэътимод бошад. Агар миёна 0,3 ва ҳадаф 0,1 бошад, бояд камтар фаъол шавад. Метавон (0,3−0,1)² илова кард, вале дивергенсияи Куллбак–Лейблер (KL)-и боби 4 градиентҳои қавитар ва натиҷаи беҳтар медиҳад (расми 18.11).

Талафоти камзичӣ бо ҳадафи p=0,1

Расми 18.11. Талафоти камзичӣ бо ҳадафи p=0,1.

Барои ду тақсимоти дискретии P ва Q, D_KL(P∥Q) бо муодилаи 18.1 ҳисоб мешавад.

Муодилаи 18.1. Дивергенсияи Куллбак–Лейблер.

D_KL(P ∥ Q) = Σᵢ P(i) log(P(i) / Q(i))

Дар ин ҷо p эҳтимолияти мақсадноки фаъолшавӣ ва q миёнаи воқеии фаъолшавии нейрон дар баста аст. Формула ба муодилаи 18.2 сода мешавад.

Муодилаи 18.2. KL байни камзичии ҳадаф p ва воқеӣ q.

D_KL(p ∥ q) = p log(p / q) + (1 − p) log((1 − p) / (1 − q))

Барои ҳисоб ҳам бозсозӣ ва ҳам рамз лозим аст. forward() namedtuple-и output ва codings медиҳад:

from collections import namedtuple

AEOutput = namedtuple("AEOutput", ["output", "codings"])

class SparseAutoencoder(nn.Module):
    def __init__(self):
        super().__init__()
        self.encoder = nn.Sequential(
            nn.Flatten(),
            nn.Linear(1 * 28 * 28, 128), nn.ReLU(),
            nn.Linear(128, 256), nn.Sigmoid())
        self.decoder = nn.Sequential(
            nn.Linear(256, 128), nn.ReLU(),
            nn.Linear(128, 1 * 28 * 28), nn.Sigmoid(),
            nn.Unflatten(dim=1, unflattened_size=(1, 28, 28)))

    def forward(self, X):
        codings = self.encoder(X)
        output = self.decoder(codings)
        return AEOutput(output, codings)

Эзоҳ. Функсияҳои омӯзишу арзёбӣ шояд бояд namedtuple-ро дастгирӣ кунанд. Масалан дар evaluate_tm() баъди даъвати модел y_pred = y_pred.output илова кунед.

Талафот:

def mse_plus_sparsity_loss(y_pred, y_target, target_sparsity=0.1,
                           kl_weight=1e-3, eps=1e-8):
    p = torch.tensor(target_sparsity, device=y_pred.codings.device)
    q = torch.clamp(y_pred.codings.mean(dim=0), eps, 1 - eps)
    kl_div = p * torch.log(p / q) + (1 - p) * torch.log((1 - p) / (1 - q))
    return mse(y_pred.output, y_target) + kl_weight * kl_div.sum()

MSE ва KL-и вазндор ҷамъ мешаванд. kl_weight қувваи камзичиро идора мекунад: хеле калон бошад, ҳадаф риоя мегардад, вале бозсозӣ вайрон мешавад; хеле хурд бошад, хусусияти ҷолиб намеомӯзад. eps аз тақсим ба сифр пешгирӣ мекунад.

Омӯзиш:

torch.manual_seed(42)
sparse_ae = SparseAutoencoder().to(device)
optimizer = torch.optim.NAdam(sparse_ae.parameters(), lr=0.002)
train(sparse_ae, optimizer, mse_plus_sparsity_loss, train_loader, n_epochs=10)

Баъди омӯзиши Fashion MNIST миёнаи фаъолшавии рамз ба тақрибан 10% мерасад.

Маслиҳат. Рамзҳои камзич аксар вақт тафсиршавандаанд. Ҳамаи тасвирҳоеро кашед, ки компоненти n-уми рамзашон аз ҳадди муайян, масалан персентили 90-ум, баландтар аст. Шояд ҳама пойафзол ё хусусияти муштарак дошта бошанд.

Автоэнкодерҳои вариатсионӣ

Variational Autoencoder (VAE)-ро Дидерик Кингма ва Макс Веллинг соли 2013 пешниҳод карданд ва он зуд маъмул шуд.

Ду фарқи асосӣ:

  • Эҳтимолӣ аст: ҳатто баъди омӯзиш баромад қисман тасодуфист. Ғавғобардор тасодуфро танҳо дар омӯзиш истифода мебурд.
  • Тавлидӣ аст: намунаҳои нави ба маҷмӯи омӯзишӣ монанд месозад.

Сохтори сэндвичии энкодер–декодер боқӣ мемонад (расми 18.12). Аммо энкодер ба ҷойи рамзи мустақим миёнаи μ ва инҳирофи стандартии σ медиҳад. Рамзи воқеӣ аз Гаусси N(μ,σ²) тасодуфан гирифта мешавад ва декодер онро бозмесозад. Дар рости расм рамз ба μ наздик, вале айнан он нест.

Ҳатто агар вуруд тақсимоти мураккаб дошта бошад, талафот рамзҳоро ба абри нуқтаҳои Гауссии бисёрченака дар фазои ниҳонӣ наздик мекунад. Бартарии муҳим: баъди омӯзиш аз Гаусси сода рамзи нав гирифта, decode мекунед — намунаи нав омода!

VAE

Расми 18.12. VAE (чап) ва гузаштани як намуна аз он (рост).

Эзоҳ. Намунагирии мустақим паҳнкунии баръакси одиро мегусалонад. Ҳилаи азнавпараметрсозӣ (reparameterization trick) истифода мешавад: ε ~ N(0,1) гирифта, μ + σ ⊗ ε ҳисоб мекунем; ⊗ зарби элементӣ аст. Ба намунагирии N(μ,σ²) баробар, вале қисмҳои тасодуфӣ ва муайян ҷудо шудаанд ва градиент аз μ/σ ба энкодер меравад. Градиентҳо тасодуфӣ, вале тахминҳои беғаразанд; тасодуфият дар омӯзиш миёна мешавад.

Талафот ду қисм дорад: бозсозӣ, масалан MSE, ва талафоти ниҳонӣ, KL байни тақсимоти рамз ва Гаусси дилхоҳ. Ғавғои Гауссӣ иттилооти ба рамз гузарандаро маҳдуд мекунад ва математика нисбат ба автоэнкодери камзич мураккабтар аст. Хушбахтона, формула сода мешавад (муодилаи 18.3). Тафсилоти пурра дар мақолаи аслӣ ё дастури хуби соли 2016-и Карл Дёрш аст.

Муодилаи 18.3. Талафоти ниҳонии VAE.

ℒ = −½ Σᵢ₌₁ⁿ [1 + log(σᵢ²) − σᵢ² − μᵢ²]

ℒ талафот, n андозаи рамз, μ_i ва σ_i миёнаву инҳирофи компоненти i-ум мебошанд. Энкодер векторҳои μ ва σ-ро медиҳад.

Одатан ба ҷойи σ γ=log(σ²), яъне логарифми дисперсияро, мебароранд. Муодилаи 18.4 ин шаклро медиҳад: ададан устувортар ва омӯзиш тезтар мешавад.

Муодилаи 18.4. Талафоти VAE бо γ=log(σ²).

ℒ = −½ Σᵢ₌₁ⁿ [1 + γᵢ − exp(γᵢ) − μᵢ²]

VAE-и Fashion MNIST бо ҳамин ҳила:

VAEOutput = namedtuple("VAEOutput",
                       ["output", "codings_mean", "codings_logvar"])

class VAE(nn.Module):
    def __init__(self, codings_dim=32):
        super(VAE, self).__init__()
        self.codings_dim = codings_dim
        self.encoder = nn.Sequential(
            nn.Flatten(),
            nn.Linear(1 * 28 * 28, 128), nn.ReLU(),
            nn.Linear(128, 2 * codings_dim))  # миёна ва logvar
        self.decoder = nn.Sequential(
            nn.Linear(codings_dim, 128), nn.ReLU(),
            nn.Linear(128, 1 * 28 * 28), nn.Sigmoid(),
            nn.Unflatten(dim=1, unflattened_size=(1, 28, 28)))

    def encode(self, X):
        return self.encoder(X).chunk(2, dim=-1)  # (mean, logvar)

    def sample_codings(self, codings_mean, codings_logvar):
        codings_std = torch.exp(0.5 * codings_logvar)
        noise = torch.randn_like(codings_std)
        return codings_mean + noise * codings_std

    def decode(self, Z):
        return self.decoder(Z)

    def forward(self, X):
        codings_mean, codings_logvar = self.encode(X)
        codings = self.sample_codings(codings_mean, codings_logvar)
        output = self.decode(codings)
        return VAEOutput(output, codings_mean, codings_logvar)

VAEOutput бозсозӣ, μ ва γ-ро нигоҳ медорад. Энкодер ду баробари рамз мебарорад, чун параметрҳои тақсимот, на худи рамз, лозиманд. chunk() ба миёна ва logvar ҷудо мекунад. torch.exp(0.5 * codings_logvar) σ медиҳад. randn_like() бо ҳамон шакл, дастгоҳ ва навъи дода аз Гаусси стандартӣ мегирад. Ғавғо ба σ зарб ва μ илова мешавад — ҳамон азнавпараметрсозӣ. Декодер бозсозӣ медиҳад ва forward() ҳамаи заруриҳои талафотро бармегардонад.

Ҷамъи MSE ва KL:

def vae_loss(y_pred, y_target, kl_weight=1.0):
    output, mean, logvar = y_pred
    kl_div = -0.5 * torch.sum(1 + logvar - logvar.exp() - mean.square(), dim=-1)
    return F.mse_loss(output, y_target) + kl_weight * kl_div.mean() / 784

KL аввал дар андозаи охири ҳар намуна ҷамъ ва баъд дар баста миёна мешавад. MSE ҳам дар баста ва ҳам 784 пиксел миёна аст, пас KL-ро ба 784 тақсим мекунем, то миқёсҳо мувофиқ шаванд.

torch.manual_seed(42)
vae = VAE().to(device)
optimizer = torch.optim.NAdam(vae.parameters(), lr=1e-3)
train(vae, optimizer, vae_loss, train_loader, n_epochs=20)

Тавлиди аксҳои Fashion MNIST

Аз Гаусси миёнааш 0 ва дисперсияаш 1 рамз гирифта, decode мекунем:

torch.manual_seed(42)
vae.eval()
codings = torch.randn(3 * 7, vae.codings_dim, device=device)
with torch.no_grad():
    images = vae.decode(codings)

Расми 18.13 21 акси ҳосилшударо нишон медиҳад.

Тасвирҳои Fashion MNIST-и тавлидкардаи VAE

Расми 18.13. Тасвирҳои Fashion MNIST-и тавлидкардаи VAE.

Аксарият боварибахш, вале андаке хираанд. Баъзе хуб нестанд, аммо модел танҳо чанд дақиқа омӯхтааст; бо қабатҳои конволютсионӣ беҳтар мешавад.

VAE интерполятсияи маъноӣ ҳам медиҳад. Омехтани пикселҳои ду акс мисли болои ҳам гузоштан аст. Ба ҷойи он рамзҳоро омехта кунед: ду рамзи тасодуфӣ, қиматҳои байни онҳо ва decode-и ҳамаи онҳо пайдарпайии тадриҷии аз як либос ба либоси дигарро медиҳад:

codings = torch.randn(2, vae.codings_dim)  # рамзи оғоз ва анҷом
n_images = 7
weights = torch.linspace(0, 1, n_images).view(n_images, 1)
codings = torch.lerp(codings[0], codings[1], weights)
with torch.no_grad():
    images = vae.decode(codings.to(device))

Интерполятсияи маъноӣ

Расми 18.14. Интерполятсияи маъноӣ.

VAE вариантҳои тақсимоти ниҳонии дигар дорад. Яке аз муҳимтаринҳо дискретӣ аст.

Автоэнкодери вариатсионии дискретӣ

dVAE ба VAE монанд, вале рамзҳояш пайваста не, дискретӣ мебошанд. Ҳар рамз вектори d адади бутуни аз 0 то k−1 аст, ки k шумораи кодҳои имконпазир мебошад. Бо k=10, d=6 10⁶=1 миллион рамзи гуногун, масалан [3,0,3,9,1,4], дорем. dVAE барои токенсозии вуруди пайвастаи трансформер, монанди BEiT ва DALL·E-и боби 16, муфид аст.

Роҳи табиӣ иваз кардани Гаусс ба тақсимоти категориявӣ аст:

  1. Энкодер ба ҷойи миёнаву дисперсия логитҳои [d,k] медиҳад. Масалан барои d=2, k=3: [[1.2,−0.8,0.5],[−1.3,0.4,0.3]].
  2. Намунагирии категориявӣ ҳосилапазир нест, пас ҳилаи дигари азнавпараметрсозӣ — Gumbel-softmax — лозим аст. F.gumbel_softmax() тақриби ҳосилапазири интихобро медиҳад. Кодҳои интихобшуда дар мисол метавонанд [0,2] бошанд.

Эзоҳ. Тақсимоти Гумбел барои модели максимуми намунаҳои тақсимоти дигар, масалан хатари обхезии дарё дар 10 сол, истифода мешавад. Ба логит ғавғои Gumbel илова ва argmax гиред, аз ҷиҳати математикӣ намунагирии категориявӣ ҳосил мешавад. Argmax ҳосилапазир нест, пас дар гузариши баръакс бо softmax тақриб мешавад.

Ғоя соли 2016 қариб ҳамзамон аз ду гурӯҳи мустақил — DeepMind/Оксфорд ва Google/Кембриҷ/Стэнфорд — пешниҳод шуд.

Татбиқи dVAE:

DiscreteVAEOutput = namedtuple("DiscreteVAEOutput",
                               ["output", "logits", "codings_prob"])

class DiscreteVAE(nn.Module):
    def __init__(self, coding_length=32, n_codes=16, temperature=1.0):
        super().__init__()
        self.coding_length = coding_length
        self.n_codes = n_codes
        self.temperature = temperature
        self.encoder = nn.Sequential([...])  # [coding_length, n_codes]
        self.decoder = nn.Sequential([...])  # [1, 28, 28]

    def forward(self, X):
        logits = self.encoder(X)
        codings_prob = F.gumbel_softmax(logits, tau=self.temperature, hard=True)
        output = self.decoder(codings_prob)
        return DiscreteVAEOutput(output, logits, codings_prob)

Меъмории энкодеру декодер бо [...] кӯтоҳ шудааст; онро дар дафтари боб бинед. hard=True дар гузариши пешрав векторҳои one-hot-и интихобшуда, дар баръакс тақриби нармро истифода мекунад; функсия бевосита вектори ID-и бутун не, намояндагии one-hot медиҳад. Ҳарорат логитҳоро пеш аз softmax миқёс медиҳад. Пасттар бошад, тақсимоти нарм ба one-hot наздиктар аст; бо hard=True ин бештар ба градиент таъсир мекунад. Одатан аз 1 оғоз ва то 0,1 тадриҷан кам мешавад.

Талафот MSE ва KL-и вазндор аст, вале prior акнун категориявии якнавохт, ҳар код 1/k, мебошад. Чун log(1/k)=−log(k), чунин менависем:

def d_vae_loss(y_pred, y_target, kl_weight=1.0):
    output, logits, _ = y_pred
    log_probs = F.log_softmax(logits, -1)  # бе log(0) = −inf
    codings_prob = log_probs.exp()
    k = logits.new_tensor(logits.size(-1))  # ҳамон дастгоҳ ва навъи дода
    kl_div = (codings_prob * (log_probs + k.log())).sum(dim=(1, 2))
    return F.mse_loss(output, y_target) + kl_weight * kl_div.mean() / 784

Ҳароратро дар ҳалқаи омӯзиш кам кунед:

model.temperature = 1 - 0.9 * epoch / n_epochs

Барои тавлид кодҳои якнавохти бутун гирифта, one-hot ва decode мекунем:

codings = torch.randint(0, d_vae.n_codes,
                        (n_images, d_vae.coding_length), device=device)
codings_prob = F.one_hot(codings, num_classes=d_vae.n_codes).float()
with torch.no_grad():
    images = d_vae.decoder(codings_prob)

Роҳи дигари маъмул квантизатсияи векторӣ (VQ-VAE) аст, ки DeepMind соли 2017 пешниҳод кард. Энкодер d embedding-и e-ченака медиҳад. Ба ҷойи намунагирӣ, ҳар embedding ба наздиктарин сатри матритсаи омӯзишпазири [k,e], ё codebook — китоби кодҳо, харита мешавад. Индексҳо кодҳои бутунанд. Онҳо боз бо ҳамон матритса embedding ва ба декодер дода мешаванд.

Ивазкунӣ бо наздиктарин сатр ҳосилапазир нест. Дар гузариши баръакс онро гӯё функсияи айният ҳисоб мекунанд ва градиент мустақим мегузарад: баҳодиҳандаи ростгузар (straight-through estimator, STE). Ин тахмин мекунад, ки градиент дар атрофи embedding-и энкодер ва наздиктарин embedding-и codebook монанд аст.

Маслиҳат. VQ-VAE-ро дуруст татбиқ кардан душвортар аст. Китобхонаи https://github.com/lucidrains/vector-quantize-pytorch ёрӣ медиҳад. Бартарӣ — омӯзиши устувортар ва кодҳои каме тафсиршавандатар.

dVAE дар акси хурд хуб, вале дар калон заифтар аст: ҷузъиёти маҳаллӣ хуб, сохтори умумӣ номувофиқ мешавад. Барои беҳбудӣ тамоми маҷмӯи омӯзиширо бо dVAE ба пайдарпайии ададҳои бутун табдил диҳед ва дар он трансформери NTP омӯзонед. dVAE гӯё луғат, трансформер грамматикаро меомӯзад. Барои акси нав аввал трансформер рамз месозад, баъд декодери dVAE онро ба тасвир мебарад.

Ин ду марҳила идораи тавлид-ро ҳам осон мекунад. Ҳангоми омӯзиши трансформер тавсифи матнии аксро пешванди рамзҳо диҳед: модел ба тавсиф шартнок (conditioned) мешавад ва токени дурустро пешгӯӣ мекунад. Баъд танҳо тавсифи акси дилхоҳро медиҳед. DALL·E-и аввал маҳз ҳамин тавр кор мекард.

Дар амал энкодеру декодер аксар вақт конволютсионӣ ва рамз шабакаи дученака аст, ки барои трансформер ҳамвор мешавад. Масалан, [256,32,32] — 32 × 32 ҷой бо embedding-и 256, ё 256 логити Gumbel-softmax дар ҳар ҷой. Баъди интихоби код тасвир шабакаи 32 × 32 адади бутун, дар мисоли codebook-и 256-воҳидӣ аз 0 то 255, мешавад. Трансформер 1024 код месозад, ба шабака мегузоред, бо векторҳои codebook иваз ва decode мекунед.

Маслиҳат. Барои сифати бештар чанд dVAE-ро, ҳар кадом бо шабакаи хурдтар, якҷо омӯзондан мумкин аст: VAE-и иерархӣ (HVAE). Энкодерҳо пайдарпай ва декодерҳо баръакс меоянд. Талафот як бозсозӣ ва чанд KL, якто барои ҳар dVAE, аст.

Акнун GAN: омӯзишаш душвортар, вале натиҷааш метавонад хеле хуб бошад.

Шабакаҳои тавлидии рақобатӣ — GAN

Ян Гудфеллоу ва ҳамкоронаш соли 2014 GAN-ро пешниҳод карданд. Ғоя зуд таваҷҷуҳ гирифт, вале ҳалли душвориҳои омӯзиш чанд сол хост. Мисли бисёр ғояҳои хуб, баъди фаҳмидан сода менамояд: ду шабакаро рақиб кунед, то якдигарро беҳтар созанд. Расми 18.15 ду ҷузъро нишон медиҳад:

Тавлидгар (generator). Рамзи тасодуфӣ, одатан Гауссиро, гирифта, дода, аксар вақт тасвир, мебарорад. Рамз намояндагии ниҳонии акси оянда аст. Мисли декодери VAE вектори нав→акси нав медиҳад, вале омӯзишаш дигар аст.

Фарқгузор (discriminator). Акси сохтаи generator ё воқеии маҷмӯаро гирифта, ҳақиқӣ ё қалбакӣ буданро муайян мекунад.

Шабакаи тавлидии рақобатӣ

Расми 18.15. Шабакаи тавлидии рақобатӣ.

Ҳадафҳо муқобиланд: фарқгузор сохтаро меёбад, тавлидгар фиреб медиҳад. Азбаски ду шабака ду ҳадаф доранд, омӯзиш одӣ нест. Ҳар давр ду марҳила:

  1. Омӯзиши фарқгузор. Бастаи акси воқеӣ ва ҳамон қадар акси сохта. Барчаспи сохта 0 ва воқеӣ 1; як қадами BCE. Танҳо вазнҳои фарқгузор нав мешаванд.
  2. Омӯзиши тавлидгар. Бастаи нави танҳо акси сохта ба фарқгузор меравад, вале ҳадафҳо ҳама 1 — воқеӣ — мебошанд. Мехоҳем фарқгузор иштибоҳ кунад. Вазнҳои фарқгузор ях ва танҳо generator нав мешавад.

Эзоҳ. Тавлидгар худ ҳеҷ акси воқеиро намебинад! Танҳо градиентҳои аз фарқгузор гузашта мегирад. Фарқгузор беҳтар шавад, ин сигнали ғайримустақим дар бораи воқеият бештар маълумот медиҳад ва тавлидгар пеш меравад.

Барои Fashion MNIST generator-и decoder-монанд ва discriminator-и таснифоти дуӣ месозем:

codings_dim = 32
generator = nn.Sequential(
    nn.Linear(codings_dim, 128), nn.ReLU(),
    nn.Linear(128, 256), nn.ReLU(),
    nn.Linear(256, 1 * 28 * 28), nn.Sigmoid(),
    nn.Unflatten(dim=1, unflattened_size=(1, 28, 28))).to(device)
discriminator = nn.Sequential(
    nn.Flatten(),
    nn.Linear(1 * 28 * 28, 256), nn.ReLU(),
    nn.Linear(256, 128), nn.ReLU(),
    nn.Linear(128, 1), nn.Sigmoid()).to(device)

Функсияи нави омӯзиш:

def train_gan(generator, discriminator, train_loader, codings_dim, n_epochs=20,
              g_lr=1e-3, d_lr=5e-4):
    criterion = nn.BCELoss()
    generator_opt = torch.optim.NAdam(generator.parameters(), lr=g_lr)
    discriminator_opt = torch.optim.NAdam(discriminator.parameters(), lr=d_lr)
    for epoch in range(n_epochs):
        for real_images, _ in train_loader:
            real_images = real_images.to(device)
            pred_real = discriminator(real_images)
            batch_size = real_images.size(0)
            ones = torch.ones(batch_size, 1, device=device)
            real_loss = criterion(pred_real, ones)
            codings = torch.randn(batch_size, codings_dim, device=device)
            fake_images = generator(codings).detach()
            pred_fake = discriminator(fake_images)
            zeros = torch.zeros(batch_size, 1, device=device)
            fake_loss = criterion(pred_fake, zeros)
            discriminator_loss = real_loss + fake_loss
            discriminator_opt.zero_grad()
            discriminator_loss.backward()
            discriminator_opt.step()

            codings = torch.randn(batch_size, codings_dim, device=device)
            fake_images = generator(codings)
            for p in discriminator.parameters():
                p.requires_grad = False
            pred_fake = discriminator(fake_images)
            generator_loss = criterion(pred_fake, ones)
            generator_opt.zero_grad()
            generator_loss.backward()
            generator_opt.step()
            for p in discriminator.parameters():
                p.requires_grad = True

Ду оптимизатори ҷудо дорем. Дар марҳилаи аввал акси воқеӣ бо ҳадафи 1 ва сохта бо 0 ҳисоб мешавад. Барои сохта detach() муҳим аст, то генератор градиент нагирад. real_loss + fake_loss фарқгузорро беҳтар мекунад.

Дар марҳилаи дуюм detach() нест, зеро бояд generator омӯзад. Параметрҳои discriminator бо requires_grad=False ях мешаванд, вале амалиёташ градиентро ба вуруди сохта мегузаронад. Ҳадафи 1 generator-ро фиребдиҳандатар мекунад. Баъди қадам discriminator боз омӯзишпазир мешавад.

Тавлид баъди омӯзиш:

generator.eval()
codings = torch.randn(n_images, codings_dim, device=device)
with torch.no_grad():
    generated_images = generator(codings)

Дар охири даври якум аллакай ба Fashion MNIST-и хеле пурғавғо монанд аст (расми 18.16).

Тасвирҳои GAN баъди як даври омӯзиш

Расми 18.16. Тасвирҳои GAN баъди як даври омӯзиш.

Аммо аксар вақт дигар чандон беҳтар намешавад ва дар баъзе даврҳо гӯё фаромӯш мекунад. Чаро?

Душвориҳои омӯзиши GAN

Тавлидгару фарқгузор дар бозии рақобатӣ якдигарро мағлуб кардан мехоҳанд. Дар назарияи бозӣ мувозинати Нэш, ба номи Ҷон Нэш, ҳолатест, ки ҳеҷ бозигар бо танҳо иваз кардани стратегияи худ, агар дигарон бетағйир бошанд, фоида намегирад. Масалан ҳама аз чап меронанд: танҳо ба рост гузаштан фоида надорад. Ҳама аз рост ронанд, мувозинати дигар аст. Оғозу динамика натиҷаро муайян мекунанд. Стратегияҳо метавонанд фаъол ҳам бошанд: дарранда медавад, сайд мегурезад ва ҳеҷ кадом бо қатъ кардан беҳтар намешавад.

Муаллифони GAN нишон доданд, ки дар шароити назариявии модели онҳо мувозинати дилхоҳ вақте аст, ки generator тақсимоти воқеиро комилан тақлид мекунад ва discriminator дигар танҳо 50/50 тахмин мекунад. Ин умедбахш аст, вале кафолати расидани омӯзиши амалӣ ба мувозинат нест.

Душвории калон фурӯпошии режим (mode collapse) аст: гуногунии тавлид кам мешавад. Фарз кунед, generator пойафзолро аз дигар синфҳо беҳтар месозад. Бо он бештар фиреб дода, боз бештар пойафзол мебарорад ва боқимондаро фаромӯш мекунад. Discriminator ҳам танҳо сохтаи пойафзол дида, дигар синфҳоро фаромӯш мекунад. Вақте онро хуб шинохт, generator ба курта мегузарад ва пойафзолро фаромӯш мекунад. Ҳамин тавр модел байни чанд синф давр мезанад ва ҳеҷ кадомро хуб намеомӯзад. Фурӯпошӣ метавонад шакли соддатар ҳам гирад: баъзе синфҳо умуман нопадид мешаванд. Дар таҷрибаи мо (расми 18.17) бо як тухмии тасодуфӣ generator баъди 20 давр пойафзол қариб намесохт — тақрибан 4% намунаҳо, ба ҷойи 30% дар маҷмӯи омӯзишӣ, — вале бо тухмии дигар ҳамаи синфҳо пайдо шуданд.

Фурӯпошии режим

Расми 18.17. Фурӯпошии режим: бо як тухмӣ пойафзол қариб тавлид намешавад (боло); бо тухмии дигар ҳамаи синфҳо ҳастанд (поён).

Рақобати пайваста параметрҳоро ларзон ва ноустувор мекунад. Омӯзиш аввал хуб ва баъд ногаҳон вайрон мешавад. Омилҳои зиёд ба динамика таъсир мекунанд, бинобар ин GAN ба гиперпараметрҳо хеле ҳассос ва танзимаш серзаҳмат аст.

Аз соли 2014 мақолаҳои зиёд талафоти нав ё усулҳои устувориро пешниҳод карданд; мақолаи соли 2018-и Google самаранокии баъзе талафотҳоро зери савол гузошт. Ду усули маъмул:

  • Такрори таҷриба (experience replay): аксҳои сохтаи даврҳои гузашта дар replay buffer нигоҳ дошта, куҳнатарҳо тадриҷан дур мешаванд. Discriminator аз воқеӣ ва сохтаи buffer меомӯзад, на танҳо аз generator-и ҳозира. Аз ҳад мутобиқшавӣ ба тавлидҳои охир кам мешавад.
  • Фарқгузории minibatch: монандии аксҳои тамоми баста чен ва ба discriminator дода мешавад. Он бастаи қалбакии якрангро осон рад мекунад ва generator ба гуногунӣ маҷбур мешавад.

Соҳа пешрафти зиёд дошт: DCGAN-ҳои конволютсионӣ, намуна дар дафтар; GAN-и тадриҷан афзоянда барои тафсилнокии баланд; StyleGAN барои идораи нозуки тавлид. Ояндааш равшан менамуд. Аммо диффузия ҳам акси аъло, ҳам омӯзиши устувортар ва гуногунии зиёд овард ва GAN ба канор рафт. Ба он мегузарем.

Моделҳои диффузионӣ

Ғояҳо солҳо вуҷуд доштанд, вале шакли муосирро Яша Сол-Дикстейн ва ҳамкорони Стэнфорд ва UC Berkeley соли 2015 расмӣ карданд. Аз механикаи оморӣ раванди ба паҳн шудани қатраи шир дар чой монандро гирифтанд. Ҳадаф омӯзиши раванди баръакс аст: аз омехтаи пурра ширро тадриҷан «ҷудо кардан». Тасвирҳо умедбахш буданд, вале GAN он вақт ҳам воқеитар ва ҳам тезтар буд, пас таваҷҷуҳ камтар шуд.

Соли 2020 Ҷонатан Ҳо ва ҳамкорон аз UC Berkeley Denoising Diffusion Probabilistic Model (DDPM)-ро сохтанд, ки тасвири хеле воқеӣ медод. Соли 2021 Алекс Никол ва Прафулла Дҳаривал аз OpenAI онро таҳлил ва беҳтар карданд. Акнун DDPM аз GAN бо омӯзиши осонтар, гуногунии бештар ва сифати баландтар пеш гузашт. Камбудии асосӣ тавлиди бисёр суст нисбат ба GAN ё VAE аст.

Акси гурбаро x₀ меномем (дар расми 18.18 ба ҷойи гурба футболкаи Fashion MNIST аст). Дар ҳар қадами t ғавғои Гауссии миёнааш 0 ва дисперсияаш β_t, як скаляр, илова мекунем. Барои ҳар пиксел ғавғо мустақил ва ҳамтақсим аст — изотропӣ. x₁, x₂ ва ғайра ҳосил мешаванд, то гурба пурра гум шавад. Қадами охир T аст. DDPM-и аслӣ T=1000 ва ҷадвали тадриҷии ғавғо дошт; такмилёфта T=4000 ва тағйири сусттар дар оғозу анҷом. Ин раванди пешрав аст.

Раванди пешрав q ва баръакс p

Расми 18.18. Раванди пешрав q ва баръакс p.

Ҳар қадам пикселҳо ҳам андаке хурд мешаванд. Омили хурдшавӣ √(1−βₜ) аст. Пас миёна тадриҷан ба 0 меравад, мисли такроран ба 0,99 зарб кардан. Дисперсияи қисми мавҷуда ба 1−βₜ зарб ва β_t-и ғавғо илова мешавад. Ҳолати устувор (1−βₜ)×1 + βₜ = 1 аст. Бо зиёд шудани ғавғо тақсимот ба Гаусси стандартӣ наздик мешавад.

Муодилаи 18.5 тақсимоти шартии q(x_t|x_(t−1))-ро ҳамчун Гаусс бо миёнаи x_(t−1) зарби ҳамон омил ва ковариатсияи β_t I медиҳад. I матритсаи айният аст, яъне ғавғо изотропӣ бо дисперсияи β_t. Ин навъи навишт дар мақолаҳои ML маъмул аст.

Муодилаи 18.5. Тақсимоти эҳтимолии раванди пешрави диффузия.

q(xₜ | xₜ₋₁) = 𝒩(xₜ; √(1 − βₜ) xₜ₋₁, βₜI)

Миёнабур ҳаст: x_t-ро аз x₀ бе ҳисоб кардани ҳамаи ҳолатҳои мобайнӣ гирифтан мумкин аст. Ҷамъи Гауссҳои мустақил боз Гаусс аст, пас ҳамаи ғавғо якбора илова мешавад. Таъриф мекунем:

αₜ = 1−βₜ

α̅ₜ = α₁ × α₂ × … × αₜ

Муодилаи 18.6 ва коди баъдӣ ҳамин миёнабурро истифода мебаранд.

Муодилаи 18.6. Миёнабури диффузияи пешрав.

q(xₜ | x₀) = 𝒩(xₜ; √α̅ₜ x₀, (1 − α̅ₜ)I)

яъне xₜ = √α̅ₜ x₀ + √(1 − α̅ₜ) ε, ε ~ 𝒩(0, I)

Ҳадаф гум кардани гурба нест, балки гурбаҳои нав аст! Модел аз x_t ба x_(t−1) гузаштан меомӯзад. Он на танҳо филтри маҳаллӣ, балки аз додаҳо пикселҳои воқеӣ месозад. Агар дар аксҳои зиёди гурба омӯзонем, аз Гаусси холис тадриҷан гурбаи нав пайдо мешавад.

Татбиқи раванди пешрав

Аввал ҷадвали дисперсия лозим. Ҳиссаи дисперсияи аз акси аслӣ боқимонда ҳар қадам ба α_t ва баъди t қадам ба α̅_t зарб мешавад. Мехоҳем ин «сигнали гурба» аз 1 то 0 тадриҷан коҳиш ёбад. DDPM-и такмилёфта ҷадвали муодилаи 18.7-ро истифода мебарад (расми 18.19).

Муодилаи 18.7. Ҷадвали дисперсияи раванди пешрав.

βₜ = 1 − α̅ₜ / α̅ₜ₋₁

α̅ₜ = f(t) / f(0)

f(t) = cos²(((t/T + s) / (1 + s)) · (π / 2))

s адади хурдест, то β_t дар наздикии 0 хеле хурд набошад; мақола s=0,008 гирифт. β_t ба ҳадди аксари 0,999 маҳдуд мешавад, то наздики T ноустувор нашавад.

Ҷадвали ғавғои β_t ва дисперсияи сигнали боқимондаи α̅_t

Расми 18.19. Ҷадвали ғавғои β_t ва дисперсияи сигнали боқимондаи α̅_t.

def variance_schedule(T, s=0.008, max_beta=0.999):
    t = torch.linspace(0, T, T + 1)
    f = torch.cos((t / T + s) / (1 + s) * torch.pi / 2) ** 2
    alpha_bars = f / f[0]
    betas = (1 - (f[1:] / f[:-1])).clamp(max=max_beta)
    betas = torch.cat([torch.zeros(1), betas])  # индексгузории осон
    alphas = 1 - betas
    return alphas, betas, alpha_bars

T = 4000
alphas, betas, alpha_bars = variance_schedule(T)

Барои омӯзиши баръакс аксҳои ғавғодори қадамҳои гуногун лозиманд. Аз x₀ ва t чунин мегирем:

def forward_diffusion(x0, t):
    eps = torch.randn_like(x0)  # ғавғои бемиқёс ҳадаф мешавад
    xt = alpha_bars[t].sqrt() * x0 + (1 - alpha_bars[t]).sqrt() * eps
    return xt, eps

Модел ҳам x_t ва ҳам t мехоҳад. Синфи дорои to() барои GPU:

class DiffusionSample(namedtuple("DiffusionSampleBase", ["xt", "t"])):
    def to(self, device):
        return DiffusionSample(self.xt.to(device), self.t.to(device))

Печонандаи Fashion MNIST пикселҳоро ба −1…+1 мебарад — ихтиёрӣ, вале одатан беҳтар — ва ғавғо илова мекунад. Ҳадаф eps-и бемиқёс аст, пеш аз зарб ба √(1−α̅_t):

class DiffusionDataset:
    def __init__(self, dataset):
        self.dataset = dataset

    def __getitem__(self, i):
        x0, _ = self.dataset[i]
        x0 = (x0 * 2) - 1
        t = torch.randint(1, T + 1, size=[1])
        xt, eps = forward_diffusion(x0, t)
        return DiffusionSample(xt, t), eps

    def __len__(self):
        return len(self.dataset)

train_set = DiffusionDataset(train_data)
train_loader = DataLoader(train_set, batch_size=32, shuffle=True)

Чаро акси аслӣ не, ғавғо пешгӯӣ мешавад? Аввал, таҷриба нишон дод, ки устувортар ва босифаттар аст. Дуюм, Гаусс математикаи содатар медиҳад: KL-и ду Гаусси дорои ковариатсияи муносиб ба масофаи квадратии миёнаҳо вобаста мешавад ва MSE-и сода, тезу устувор истифода мешавад.

Сохтан ва омӯзонидани DDPM

Ҳар меъморие мумкин аст, ки DiffusionSample гирифта, тасвири ҳамшакли вуруд, яъне ғавғои ҳар пикселро, диҳад. Муаллифон U-Net-и тағйирёфта истифода карданд, монанд ба FCN-и сегментатсияи боби 12. CNN аввал фазоро хурд ва баъд калон мекунад; байни сатҳҳои мувофиқи поёнрав ва болобар пайвастҳои ҷаҳишӣ ҳастанд. t бо рамзгузории синусии собит, мисли мавқеи Transformer, рамзгузорӣ мешавад. Дар ҳар сатҳ ин вектор аз Linear гузашта, ба U-Net ворид мегардад. MHA ҳам дар чанд сатҳ ҳаст. Код дар дафтар дароз аст ва меъмориҳои дигар ҳам кор мекунанд:

class DiffusionModel(nn.Module):  # тафсилоти пурра дар дафтар
    def __init__(self, T=T, embed_dim=64):
        [...]  # сохтани ҳамаи модулҳои U-Net

    def forward(self, sample):
        [...]  # пешгӯии ғавғои ҳар тасвир

Дар ин ҷо MAE аз MSE беҳтар кор мекунад; талафоти Huber, ки ба он наздик аст, низ мувофиқ аст — ҳаминро мегирем:

diffusion_model = DiffusionModel().to(device)
huber = nn.HuberLoss()
optimizer = torch.optim.NAdam(diffusion_model.parameters(), lr=3e-3)
train(diffusion_model, optimizer, huber, train_loader, n_epochs=20)

Баъди омӯзиш x_T-ро аз Гаусси стандартӣ мегирем ва бо муодилаи 18.8 x_(T−1), баъд 3999 маротибаи дигар то x₀ меравем. Агар хуб омӯзонда бошад, x₀ акси Fashion MNIST мешавад.

Муодилаи 18.8. Як қадами баръакси DDPM.

xₜ₋₁ = (1 / √αₜ) · (xₜ − (βₜ / √(1 − α̅ₜ)) · ε_θ(xₜ, t)) + √βₜ z

ε_θ(xₜ,t) ғавғои пешгӯии модел, θ параметрҳо ва z ғавғои Гауссии стандартӣ аст. Дар қадами охир (t=1) z=0 мегиранд, то ба x₀ ғавғо илова нашавад. Пас раванди баръакс тасодуфӣ буда, иҷроҳои гуногун аксҳои гуногун медиҳанд.

Тезонидани тавлид бо DDIM

4000 қадам барои як акс хеле суст аст. Чанд моҳ баъди DDPM муҳаққиқони Стэнфорд Denoising Diffusion Implicit Model (DDIM)-ро пешниҳод карданд. Он ба ҷойи якқадамӣ чанд қадамро якбора мегузарад. Омӯзиш ҳамон аст, пас DDPM-и омӯзондашударо такроран истифода мебарем.

Муодилаи 18.9. Чанд қадами баръакс бо DDIM.

x̂₀  = (xₜ − √(1 − α̅ₜ) · ε_θ(xₜ, t)) / √α̅ₜ,
xₚ  = √α̅ₚ · x̂₀ + √(1 − α̅ₚ − σₜ²) · ε_θ(xₜ, t) + σₜ · z,
σₜ² = η² · ((1 − α̅ₚ) / (1 − α̅ₜ)) · (1 − α̅ₜ / α̅ₚ)
  • ε_θ, θ ва z ҳамон маъноҳои боло доранд.
  • p ҳар қадами пеш аз t, масалан p=t−50, мебошад.
  • η тасодуфиятро аз 0 — пурра муайян баъди интихоби ғавғои аввал — то 1 — шакли ба DDPM монанд — идора мекунад.

Функсияи тавлид бо DDIM:

def generate_ddim(model, batch_size=32, num_steps=50, eta=0.85):
    model.eval()
    with torch.no_grad():
        xt = torch.randn([batch_size, 1, 28, 28], device=device)
        times = torch.linspace(T - 1, 0, steps=num_steps + 1).long().tolist()
        for t, t_prev in zip(times[:-1], times[1:]):
            t_batch = torch.full((batch_size, 1), t, device=device)
            sample = DiffusionSample(xt, t_batch)
            eps_pred = model(sample)
            x0 = ((xt - (1 - alpha_bars[t]).sqrt() * eps_pred)
                  / (alpha_bars[t].sqrt()))
            abar_t_prev = alpha_bars[t_prev]
            variance = (eta**2 * (1 - abar_t_prev) / (1 - alpha_bars[t])
                        * (1 - alpha_bars[t] / abar_t_prev))  # σ_t² дар муодилаи 18.9
            sigma_t = variance.sqrt()
            pred_dir = (1 - abar_t_prev - sigma_t**2).clamp(min=0).sqrt() * eps_pred
            noise = torch.randn_like(xt)
            xt = abar_t_prev.sqrt() * x0 + pred_dir + sigma_t * noise

        return torch.clamp((xt + 1) / 2, 0, 1)

X_gen_ddim = generate_ddim(diffusion_model, num_steps=500)

Акнун тавлид, мувофиқи мисол, чанд сония мегирад (расми 18.20). Аксҳо ҳанӯз олиҷаноб нестанд, вале модел дар Fashion MNIST танҳо чанд дақиқа омӯхтааст. Бо додаи калонтар ва чанд соат омӯзиш натиҷа беҳтар мешавад.

Тасвирҳои диффузияи бо DDIM тезонидашуда

Расми 18.20. Тасвирҳои диффузияи бо DDIM тезонидашуда.

Диффузияи ниҳонӣ ва Stable Diffusion

Аз соли 2020 диффузия хеле пеш рафт. Робин Ромбах ва ҳамкорон декабри 2021 моделҳои диффузияи ниҳонӣ (latent diffusion)-ро пешниҳод карданд. Ба ҷойи пиксел, диффузия дар фазои хурди ниҳонӣ мегузарад. Автоэнкодери қавӣ ҳар акси омӯзиширо фишурда мекунад, диффузия дар он кор мекунад ва декодер рамзи ниҳоиро ба акс бармегардонад. Тавлид хеле тез, омӯзишу хароҷот кам ва сифат баланд мешавад.

Муҳаққиқон шартноккунии тавлид-ро бо матн, тасвир ва вурудҳои дигар ҳам мутобиқ карданд. Акси дилхоҳро бо промпт роҳнамоӣ мекунед. Шартнокӣ бо тасвири вурудӣ outpainting, яъне идомаи акс берун аз канор, ва inpainting, яъне пур кардани ҷойҳои холӣ ё ивазшаванда, медиҳад.

Августи 2022 Stable Diffusion (SD), модели қавии пешомӯхташудаи диффузияи ниҳонӣ, аз ҳамкории LMU Munich, Stability AI, Runway ва дастгирии EleutherAI/LAION бо вазнҳо ва коди дастрас баромад. Бо он акс дар чанд сония, ҳатто дар баъзе ноутбукҳо, ройгон тавлид мешавад; суръати воқеӣ аз сахтафзор вобаста аст.

Бо Hugging Face Diffusers варианти turbo-ро гирифта, орангутани китобхон месозем:

from diffusers import AutoPipelineForText2Image

pipe = AutoPipelineForText2Image.from_pretrained(
    "stabilityai/sd-turbo", variant="fp16", dtype=torch.float16)
pipe.to(device)
prompt = "A closeup photo of an orangutan reading a book"
torch.manual_seed(26)
image = pipe(prompt=prompt, num_inference_steps=1, guidance_scale=0.0).images[0]

Расми 18.21. Тасвири Stable Diffusion бо китобхонаи Diffusers.

Имкониятҳо беинтиҳоанд!

Дар боби оянда ба шохаи тамоман дигари омӯзиши амиқ — омӯзиши амиқи тақвиятӣ — мегузарем.

пешина
аз 20
навбатӣ