본문 바로가기

명사 美 비격식 (무리 중에서) 아주 뛰어난[눈에 띄는] 사람[것]

Personal/SK 네트웍스 AI 캠프

SK 네트웍스 AI 캠프 - 3_초거대언어모델(LLM) - Day54_합성곱 신경망(CNN)의 구조와 연산 원리 + GAN 기반 이미지 생성과 학습 방법

합성곱 신경망(CNN)의 구조와 연산 원리

 

여러 미디어를 동시 제공하는 멀티미디어를 모달리티 간 검색 변환해  크로스모달화한 뒤 여러 모달리티를 의미적 통합하는 멀티모달로 만들어 여러 작업을 하나의 모델이 수행하는 멀티테스크를 하는것에 대해 배보자 .

 

 


크로스모달 Cross-modal

한 모달리티 정보를 다른 모달리티와 연결하거나 변환하는 기술. 텍스트로 이미지를 검색하거나 이미지에 맞는 설명문 검색, 음성을 텍스트로 변환하거나 텍스트를 음성으로 변환하거나 문장으로 영상을 검색하는 등의 예시가 있다. 매핑과 변환이 핵심이다. 

https://standout.tistory.com/1934

 

Modality 모달리티란? : 정렬 Alignment (Contrastive Learning, Cross Attention), 융합 (초기, 후기, 중간)

모달리티 Modality정보를 표현하거나 전달하는 방식, 양식 모달리티 정렬 Modality Alignment 서로다른 데이터가 의미상 어떤 관계에 있는지 대응시키는 과정예를 들어 이미지안의 특정 영역과 문장토

standout.tistory.com

 

 

멀티모달의 학습기술

입력 데이터와 정답을 함께 제공항여 학습하는 지도학습과

데이터 자체에서 학습 신호를 만드는 방식의 자기지도 학습, 

의미가 관련된 데이터 쌍은 가깝게, 관련 없는 데이터 쌍은 멀게 배치하도록 학습하는 대조학습

입력의 일부를 숨기고 복원하게 하는 방식의 마스킹 학습

입력 모달리티를 기반으로 다른 형태의 데이터를 생성하는 생성학습이 있다.

https://standout.tistory.com/1871

 

멀티모달 모델, OpenAI가 개발한 CLIP과 DALL·E

CLIP과 DALL·E는 멀티모달 모델 -CLIP (Contrastive Language–Image Pre-training)OpenAI가 개발한 멀티모달 AI 모델텍스트와 이미지를 함께 학습해 두 데이터의 의미를 연결한다. 이미지와 가장 잘 맞는 문장을

standout.tistory.com

 


멀티모달 Multimodal 

모빌리티: 정보가 표현되거나 인간이 정보를 받아들이는 감각적 형식적 유형. 텍스트, 이미지, 음성, 영상, 표/차트, 센서, 포인트 클라우드, 동작/제스처

텍스트는 의미를 직접 표현하는데 강점이 있고, 이미지는 공간적 관계를 표현하는데 유리하며 음성은 말의 내용뿐 아니라 억양, 감정, 화자의 상태를 포함할 수 있다. 멀티모달 모델은 각 데이터의 장점을 결합해 단일 모달리티만으로는 얻기 어려운 정보를 추론한다.  여러 모달리티를 동시에 활용해 하나의 표현을 마니들고 바탕으로 학습하거나 추론하는 기술, 

* 사물을 여러 카메라 각도에서 촬영한 경우 이미지라는 동일 모달리티에 속하나 여러 시점의 정보를 활용하는 멀티뷰 데이터로 볼 수 있다. 

 

멀티미디어 Multimedia

텍스트, 이미지, 음성, 영상 등 여러 표현 형식이 하나의 콘텐츠에 포함된것. 강의는 영상, 음성, 자막, 그림을 함께 제공한다. 멀티미디어는 정보의 표현방식에 가깝고 멀티모달 인공지능이 이정보들을 의미적으로 연결해 추론하는것을 말한다. 

 

멀티모달 인공지능 Multimodal Artificial Intelligence

멀티모달로 데이터를 함꼐 입력받아 이들 사이의 관계를 학습하고 통합적으로 이해, 추론, 생성하는 기술

 

유니모달 인공지능Unimodal AI

한종류의 데이터만 입력받아 처리하는 인공지능

특정 작업에서 높은 성능을 낼 수 있지만 다른 형태의 정보를 함께 고려핮못한다. 


일반 신경망으로 이미지를 처리할때 2차원 이미지를 1차원 벡터로 변환해야하고 이를 평탄화라고 한다. 

평탄화를 수행하며 이미지에 존재하던 가로와 세로의 공간적 관계가 약해져 원래 서로 인접한 픽셀도 신경망에서는 단순히 벡터의 서로 다른 위치로 처리된다. 또 이미지를 약 1000개의 뉴런과 완전연결하면 필요한 가중치의 수는 편향을 제외하더라도 약 1억 5천개의 파라미터로 증가해 메모리 사용량이 커지고 학습시간이 길어지며 과적합 가능성이 증가하며 이미지 공간 구조를 충분히 활용하지 못하고 객체 위치가 조금만 달라져도 다른 입력 패턴으로 처리되는등의 문제가 생긴다. 

 

CNN Convolutional Neural Network

합성곱 신경망

이미지와 같이 격자 구조를 가지는 데이터를 처리하는데 적합한 인공신경망

이미지의 가로세로채널 구조를 유지하면서 주변 픽셀 사이의 관계를 분석하고 분류나 탐지에 필요한 특징을 자동으로 학습한다.  CNN의 중요한 특징은 사람이 이미지의 특징을 직접 정의하지않아도 학습과정에서 필요한 특징을 자동으로 찾는다는점.

CNN의 뉴런은 이전 계층의 모든 값과 연결되고 입력데이터의 작은 영역만 관찰하는 작은 지역을 중심으로 분석하는 지역적 연결으로 동작하며 입력 이미지에서 영향을 받는 영역을 수용영역이라고한다. 

CNN은 하나의 커널을 이미지의 모든 위치에 반복적으로 적용하며 동일한 커널의 왼쪽, 가운데, 오른쪽 등에 적용하며 동일한 특징이 이미지 어느 위치에 나타나더라도 같은커널로 검출 할 수 있는 가중치 공유를 통한 파라미터 수 감소, 계산량 감소, 위치에 대한 대응력 향상, 동일 특징의 반복검출이 가능하게 한다. 

CNN은 여러 하비성곱을 통화하면서 단순한 특징을 조합해 복잡한 특징을 만들어 초기계층에서는 선이나 모서리와 같은 저수준 특징을 학습하고 깊은 계층에서는 얼굴이나 자동차처럼 의미가 있는 고수준 특징을 학습한다. 

https://standout.tistory.com/1744

 

CNN 이란?: CNN Concolutional Neural Network 합성곱 신경망, 이미지의 특징을 자동으로 찾자!

CNN Concolutional Neural Network 합성곱 신경망이미지, 영상, 패턴인식에 사용된다. 기존 신경망은 이미지 처리에 비효율적이었다 .고양이 사진을 숫자로 펼치면 수십만개 픽셀이 되고 일반신경망은 파

standout.tistory.com

 

 

커널: 이미지에서 특정한 패턴을 검축하는 작은 가중치 해애렬

특성맵: 커널 하나를 입력 전체에 적용한 결과를 특성맵. 커널마다 서로 다른 특징을 학 습한다. 

풀링: 특성 맵의 가로와 세로 크기를 줄이는 연산  지정된 영역에서 가장 큰 값을 선택하는 최대풀링, 지정된 영역값의 평균을 계산하는 평균 풀링, 모든 공간 값을 평균해 하나의 값으로 변환하는 글로벌 평균 풀링이 있다. 

 

 

Attension

입력 데이터의 모든 부분을 동일하게 처리하지않고 현재 작업에 중요한 부분에 더 큰 가중치를 부여하는 기술

Self Attension는 동일한 모달리티 내부의 관계를 학습해 텍스트 단어와 단어사이의 관계, 이미지 패치와 패치사이의 관계, 영상 프레임과 프레임 사이의 관계를 학습한다.

Cross Attention은 서로 다른 모달리티 사이의 관계를 학습해 자동차 토큰을 가져다 이미지에 자동차가 있는 영역에 높은 가중치를 부여한다. 

https://standout.tistory.com/1846

 

Attention 메커니즘만을 이용하여 문장을 처리하는 딥러닝 모델 Transformer (feat.Self-Attention, Multi-Head A

Attention기계번역에서 현재 단어 하나만 보는게 아니라 주변 단어 전체를 함께 고려해 번역하는것이 Seq2Seq 모델의 가장 큰 개선점이었다. Query, Key, Value 세가지 벡터를 이용해 현재 필요한 정보가

standout.tistory.com

 

 

Transformer

Attention을 중심으로 구성된 긴경망 구조로 멀티모달 환경에서는 텍스트 토큰, 이미지패치, 음성 구간, 영상 프레임을 모두 일종의 토큰으로 변환한뒤 관계를 학습할 수 있다.  Transformer는 장거리 관계학습이 가능하고 병렬처리 및 서로 다른 종류의 토큰을 통합할 수 있어 대규모 사전학습에 적합하고 Cross Attention을 통한 모달리티 연결이 가능해 멀티 모달 처리에 적합하다. 

https://standout.tistory.com/1846

 

Attention 메커니즘만을 이용하여 문장을 처리하는 딥러닝 모델 Transformer (feat.Self-Attention, Multi-Head A

Attention기계번역에서 현재 단어 하나만 보는게 아니라 주변 단어 전체를 함께 고려해 번역하는것이 Seq2Seq 모델의 가장 큰 개선점이었다. Query, Key, Value 세가지 벡터를 이용해 현재 필요한 정보가

standout.tistory.com

 

 

 

위내용을 정리해보자. 

이미지는 cnn합성곱을 통해 feature로, 텍스트는 transformer를 통해 text feature를 얻어내지고

modality alignment후 cross attention해 feature fusion을 거쳐 shared representation해 멀티테스크인 classification 및 text generation을 할 수 있다 .

             이미지                    텍스트
                │                        │
              CNN                  Transformer
                │                        │
          Image Feature           Text Feature
                │                        │
                └───── Modality Alignment ─────┐
                                               │
                                       Cross Attention
                                               │
                                       Feature Fusion
                                               │
                                 Shared Representation
                         ┌──────────────┴──────────────┐
                         │                             │
                 Classification                 Text Generation
                  (Multi-task)                 (Multi-task)

 

 

 

이미지와 텍스트를 입력받아 분류와 문장생성을 동시에 수행하는 멀티모달예시를 확인해보자 .

import torch
import torch.nn as nn
import torchvision.models as models

####################################################
# CNN Image Encoder
####################################################

class ImageEncoder(nn.Module):

    def __init__(self):
        super().__init__()

        resnet = models.resnet18(weights=None)

        self.backbone = nn.Sequential(
            *list(resnet.children())[:-1]
        )

        self.fc = nn.Linear(512,256)

    def forward(self,x):

        x=self.backbone(x)
        x=x.flatten(1)

        return self.fc(x)


####################################################
# Transformer Text Encoder
####################################################

class TextEncoder(nn.Module):

    def __init__(self):

        super().__init__()

        self.embedding=nn.Embedding(30000,256)

        encoder=nn.TransformerEncoderLayer(
            d_model=256,
            nhead=8,
            batch_first=True
        )

        self.encoder=nn.TransformerEncoder(
            encoder,
            num_layers=4
        )

    def forward(self,text):

        x=self.embedding(text)

        # Self-Attention
        x=self.encoder(x)

        return x.mean(1)


####################################################
# Cross Attention
####################################################

class CrossModalAttention(nn.Module):

    def __init__(self):

        super().__init__()

        self.attn=nn.MultiheadAttention(
            embed_dim=256,
            num_heads=8,
            batch_first=True
        )

    def forward(self,image_feature,text_feature):

        image=image_feature.unsqueeze(1)
        text=text_feature.unsqueeze(1)

        aligned,_=self.attn(
            query=text,
            key=image,
            value=image
        )

        return aligned.squeeze(1)


####################################################
# Feature Fusion
####################################################

class FeatureFusion(nn.Module):

    def __init__(self):

        super().__init__()

        self.fc=nn.Sequential(

            nn.Linear(512,512),

            nn.ReLU(),

            nn.Linear(512,256)

        )

    def forward(self,image,text):

        x=torch.cat([image,text],dim=1)

        return self.fc(x)


####################################################
# Multi-task Heads
####################################################

class Classifier(nn.Module):

    def __init__(self):

        super().__init__()

        self.fc=nn.Linear(256,10)

    def forward(self,x):

        return self.fc(x)


class TextGenerator(nn.Module):

    def __init__(self):

        super().__init__()

        self.fc=nn.Linear(256,30000)

    def forward(self,x):

        return self.fc(x)


####################################################
# Multimodal Model
####################################################

class MultiModalModel(nn.Module):

    def __init__(self):

        super().__init__()

        self.image_encoder=ImageEncoder()
        self.text_encoder=TextEncoder()

        self.cross_attention=CrossModalAttention()

        self.fusion=FeatureFusion()

        self.classifier=Classifier()
        self.generator=TextGenerator()

    def forward(self,image,text):

        ####################################################
        # CNN
        ####################################################
        image_feature=self.image_encoder(image)

        ####################################################
        # Transformer + Self-Attention
        ####################################################
        text_feature=self.text_encoder(text)

        ####################################################
        # Modality Alignment
        ####################################################
        aligned=self.cross_attention(
            image_feature,
            text_feature
        )

        ####################################################
        # Feature Fusion
        ####################################################
        fused=self.fusion(
            aligned,
            text_feature
        )

        ####################################################
        # Multi-task Learning
        ####################################################
        classification=self.classifier(fused)

        generation=self.generator(fused)

        return classification,generation

 

 

 

 

 

 

 

 

GAN 기반 이미지 생성과 학습 방법 Generative Adversarial Network

생성자와 판별자 두개의 신경망이 서로 경쟁하며 학습해 실제와 매우 유사한 이미지를 생성하는 딥러닝 모델

적대적 학습

Random Noise 무작위 난수 벡터를 generator가 입력받아 새로운 이미지를 생성하고 이 Fask data를 Real Data와 함께 Discrimivator 가 받아 각 이미지가 진짜인지 가까인지 확률을 출력한다 . 이 최종출력 Output을 통해 Feedbackloop으로 generator를 개선하는 과정이 이루어진다. 대표적인 GAN 모델로는 GAN, DCGAN, Confitional GAN, Pix2Pix, CycleGAN, SRGAN, StyleGAN, StyleGAN2, StyleGAN3, BigGAN 등이 있다. 

GAN은 매우 사실적인 이미지를 생성할 수 있어 예술, 창작, 데이터증강, 영상, 게임그래픽 등다양한 분야에 활용되나 학습이 불안정하여 생성자와 판별자의 균형을 맞추기가 어렵고 학습시간이 길고하이퍼파라미터에 민감하다. 

 

https://standout.tistory.com/1791

 

상대적 적대신경망 GAN게임 & 순환 일관성이 보장되는 포토샵신경망 사이클 GAN

앞서 resNet은 출력을 처음부터 만드는게 아닌 입력에서 얼마나 수정할지만 학습하는 잔차학습이라했다 .https://standout.tistory.com/1773 ResNet, 역전파를 그대로 사용하되 Residual Connection을 추가한 CNNResN

standout.tistory.com

 

 

 

GAN (2014): 가장 기본모델, 생성자(Generator)와 판별자(Discriminator)가 경쟁하며 학습

핵심:Noise → Generator → Fake Image → Discriminator 판별

# Generator

z = torch.randn(batch, 100)

fake_image = Generator(z)


# Discriminator

real_score = Discriminator(real_image)

fake_score = Discriminator(fake_image)


# Loss

D_loss = BCE(real_score,1) + BCE(fake_score,0)

G_loss = BCE(fake_score,1)



DCGAN (2015): CNN(합성곱 신경망)을 GAN에 적용해 이미지 생성 품질과 학습 안정성을 크게 향상

핵심: Linear 대신 CNN 사용

import torch
import torch.nn as nn


class DCGAN_Generator(nn.Module):

    def __init__(self):

        super().__init__()


        self.model = nn.Sequential(

            # Noise
            # (100,1,1)
            nn.ConvTranspose2d(
                100,
                512,
                kernel_size=4,
                stride=1,
                padding=0
            ),

            nn.BatchNorm2d(512),

            nn.ReLU(True),



            # 4x4 → 8x8

            nn.ConvTranspose2d(
                512,
                256,
                kernel_size=4,
                stride=2,
                padding=1
            ),

            nn.BatchNorm2d(256),

            nn.ReLU(True),



            # 8x8 → 16x16

            nn.ConvTranspose2d(
                256,
                128,
                kernel_size=4,
                stride=2,
                padding=1
            ),

            nn.BatchNorm2d(128),

            nn.ReLU(True),



            # 16x16 → 32x32

            nn.ConvTranspose2d(
                128,
                64,
                kernel_size=4,
                stride=2,
                padding=1
            ),

            nn.BatchNorm2d(64),

            nn.ReLU(True),



            # 32x32 → 64x64

            nn.ConvTranspose2d(
                64,
                3,
                kernel_size=4,
                stride=2,
                padding=1
            ),

            nn.Tanh()

        )


    def forward(self,z):

        return self.model(z)



Conditional GAN (CGAN, 2014): 클래스나 텍스트 등 조건을 함께 입력해 원하는 조건에 맞는 이미지를 생성

핵심은 기존 GAN의 입력인 Noise(z)에 조건 정보(label)를 추가하는 것

import torch
import torch.nn as nn


# =========================
# Generator
# =========================

class Generator(nn.Module):

    def __init__(self):

        super().__init__()

        # 숫자 조건 embedding
        self.label_embedding = nn.Embedding(
            10,      # 0~9 숫자
            10
        )


        self.model = nn.Sequential(

            nn.Linear(
                100 + 10,   # noise + label
                256
            ),

            nn.LeakyReLU(0.2),


            nn.Linear(
                256,
                512
            ),

            nn.LeakyReLU(0.2),


            nn.Linear(
                512,
                784
            ),

            nn.Tanh()

        )


    def forward(self, noise, label):

        # label → vector
        label_vector = self.label_embedding(label)


        # noise + label 결합
        x = torch.cat(
            [
                noise,
                label_vector
            ],
            dim=1
        )


        image = self.model(x)


        return image.view(
            -1,
            1,
            28,
            28
        )



Pix2Pix (2017): 짝(pair) 데이터 기반 이미지 변환하는 스케치를 사진으로, 흑백을 컬러로 변환작업이 가능

Conditional GAN의 발전 형태로 CGAN이 조건(label)을 넣었다면 Pix2Pix는 이미지 자체를 조건으로 넣는다.

import torch
import torch.nn as nn


class Pix2PixGenerator(nn.Module):

    def __init__(self):

        super().__init__()


        # Encoder

        self.encoder1 = nn.Sequential(

            nn.Conv2d(
                3,
                64,
                4,
                2,
                1
            ),

            nn.LeakyReLU(0.2)

        )


        self.encoder2 = nn.Sequential(

            nn.Conv2d(
                64,
                128,
                4,
                2,
                1
            ),

            nn.BatchNorm2d(128),

            nn.LeakyReLU(0.2)

        )



        # Decoder

        self.decoder1 = nn.Sequential(

            nn.ConvTranspose2d(
                128,
                64,
                4,
                2,
                1
            ),

            nn.BatchNorm2d(64),

            nn.ReLU()

        )


        self.decoder2 = nn.Sequential(

            nn.ConvTranspose2d(
                128,
                3,
                4,
                2,
                1
            ),

            nn.Tanh()

        )



    def forward(self,x):

        # Encoder

        e1 = self.encoder1(x)

        e2 = self.encoder2(e1)


        # Decoder

        d1 = self.decoder1(e2)


        # Skip Connection
        # encoder feature + decoder feature

        x = torch.cat(
            [
                d1,
                e1
            ],
            dim=1
        )


        output = self.decoder2(x)


        return output



CycleGAN (2017): 짝이 없는(unpaired) 데이터로 이미지 변환하는 말↔얼룩말, 여름↔겨울처럼 대응 데이터 없이 변환작업 가능
CycleGAN은 Generator 2개 + Discriminator 2개를 사용한다.

# ==========================================
# CycleGAN 핵심 구현 예제 (PyTorch)
# Horse <-> Zebra 이미지 변환 구조
# ==========================================

import torch
import torch.nn as nn


# -----------------------------
# Residual Block
# -----------------------------

class ResidualBlock(nn.Module):

    def __init__(self, channels):

        super().__init__()

        self.block = nn.Sequential(

            nn.Conv2d(
                channels,
                channels,
                3,
                1,
                1
            ),

            nn.InstanceNorm2d(channels),

            nn.ReLU(inplace=True),


            nn.Conv2d(
                channels,
                channels,
                3,
                1,
                1
            ),

            nn.InstanceNorm2d(channels)

        )


    def forward(self, x):

        return x + self.block(x)



# -----------------------------
# Generator
# A -> B
# B -> A
# -----------------------------

class Generator(nn.Module):

    def __init__(self):

        super().__init__()

        self.model = nn.Sequential(

            # Encoder

            nn.Conv2d(
                3,
                64,
                7,
                1,
                3
            ),

            nn.InstanceNorm2d(64),

            nn.ReLU(),


            nn.Conv2d(
                64,
                128,
                3,
                2,
                1
            ),

            nn.ReLU(),


            nn.Conv2d(
                128,
                256,
                3,
                2,
                1
            ),

            nn.ReLU(),



            # Residual Blocks

            ResidualBlock(256),
            ResidualBlock(256),
            ResidualBlock(256),



            # Decoder

            nn.ConvTranspose2d(
                256,
                128,
                3,
                2,
                1,
                output_padding=1
            ),

            nn.ReLU(),


            nn.ConvTranspose2d(
                128,
                64,
                3,
                2,
                1,
                output_padding=1
            ),

            nn.ReLU(),


            nn.Conv2d(
                64,
                3,
                7,
                1,
                3
            ),

            nn.Tanh()

        )


    def forward(self, x):

        return self.model(x)



# -----------------------------
# PatchGAN Discriminator
# -----------------------------

class Discriminator(nn.Module):

    def __init__(self):

        super().__init__()


        self.model = nn.Sequential(

            nn.Conv2d(
                3,
                64,
                4,
                2,
                1
            ),

            nn.LeakyReLU(
                0.2
            ),


            nn.Conv2d(
                64,
                128,
                4,
                2,
                1
            ),

            nn.InstanceNorm2d(128),

            nn.LeakyReLU(
                0.2
            ),


            nn.Conv2d(
                128,
                256,
                4,
                2,
                1
            ),

            nn.InstanceNorm2d(256),

            nn.LeakyReLU(
                0.2
            ),


            nn.Conv2d(
                256,
                1,
                4,
                1,
                1
            )

        )


    def forward(self, x):

        return self.model(x)



# -----------------------------
# Model 생성
# -----------------------------

# Horse -> Zebra

G_AB = Generator()

# Zebra -> Horse

G_BA = Generator()


# Discriminator

D_A = Discriminator()

D_B = Discriminator()



# -----------------------------
# Loss
# -----------------------------

adversarial_loss = nn.MSELoss()

cycle_loss = nn.L1Loss()



# -----------------------------
# Example Input
# -----------------------------

horse = torch.randn(
    1,
    3,
    256,
    256
)


zebra = torch.randn(
    1,
    3,
    256,
    256
)



# -----------------------------
# Forward
# -----------------------------

# Horse -> Zebra

fake_zebra = G_AB(
    horse
)


# Zebra -> Horse

fake_horse = G_BA(
    zebra
)



# -----------------------------
# Cycle Consistency
# -----------------------------

# Horse -> Zebra -> Horse

recovered_horse = G_BA(
    fake_zebra
)


# Zebra -> Horse -> Zebra

recovered_zebra = G_AB(
    fake_horse
)



cycle_loss_value = (

    cycle_loss(
        recovered_horse,
        horse
    )

    +

    cycle_loss(
        recovered_zebra,
        zebra
    )

)



# -----------------------------
# Adversarial Loss Example
# -----------------------------

fake_prediction = D_B(
    fake_zebra
)


real_target = torch.ones_like(
    fake_prediction
)


gan_loss = adversarial_loss(
    fake_prediction,
    real_target
)



# 최종 Generator Loss

generator_loss = (

    gan_loss

    +

    10 * cycle_loss_value

)


print(
    "CycleGAN Generator Loss:",
    generator_loss.item()
)



SRGAN (2017): 초해상도(Super Resolution) 전용으로 저해상도 이미지를 고해상도로 복원한다.

# ==========================================
# SRGAN 핵심 구현 예제 (PyTorch)
# Low Resolution -> High Resolution
# ==========================================


import torch
import torch.nn as nn


# ==========================================
# Residual Block
# ==========================================

class ResidualBlock(nn.Module):

    def __init__(self, channels):

        super().__init__()


        self.block = nn.Sequential(

            nn.Conv2d(
                channels,
                channels,
                3,
                1,
                1
            ),

            nn.BatchNorm2d(
                channels
            ),

            nn.PReLU(),


            nn.Conv2d(
                channels,
                channels,
                3,
                1,
                1
            ),

            nn.BatchNorm2d(
                channels
            )

        )


    def forward(self,x):

        return x + self.block(x)



# ==========================================
# Upsampling Block
# PixelShuffle 사용
# ==========================================

class UpsampleBlock(nn.Module):

    def __init__(self, channels):

        super().__init__()


        self.block = nn.Sequential(

            nn.Conv2d(
                channels,
                channels * 4,
                3,
                1,
                1
            ),

            nn.PixelShuffle(
                2
            ),

            nn.PReLU()

        )


    def forward(self,x):

        return self.block(x)



# ==========================================
# Generator
# LR Image -> HR Image
# ==========================================

class SRGenerator(nn.Module):

    def __init__(self):

        super().__init__()



        # Feature Extraction

        self.conv1 = nn.Sequential(

            nn.Conv2d(
                3,
                64,
                9,
                1,
                4
            ),

            nn.PReLU()

        )



        # Residual Blocks

        self.residual = nn.Sequential(

            ResidualBlock(64),

            ResidualBlock(64),

            ResidualBlock(64),

            ResidualBlock(64),

            ResidualBlock(64)

        )



        # Upscale

        self.upsample = nn.Sequential(

            UpsampleBlock(64),

            UpsampleBlock(64)

        )



        # Output

        self.output = nn.Conv2d(

            64,

            3,

            9,

            1,

            4

        )



    def forward(self,x):

        x1 = self.conv1(x)


        x2 = self.residual(x1)


        x = x1 + x2


        x = self.upsample(x)


        return torch.tanh(
            self.output(x)
        )



# ==========================================
# Discriminator
# HR Image 판별
# ==========================================

class SRDiscriminator(nn.Module):

    def __init__(self):

        super().__init__()


        self.model = nn.Sequential(

            nn.Conv2d(
                3,
                64,
                3,
                1,
                1
            ),

            nn.LeakyReLU(
                0.2
            ),



            nn.Conv2d(
                64,
                128,
                3,
                2,
                1
            ),

            nn.BatchNorm2d(
                128
            ),

            nn.LeakyReLU(
                0.2
            ),



            nn.Conv2d(
                128,
                256,
                3,
                2,
                1
            ),

            nn.BatchNorm2d(
                256
            ),

            nn.LeakyReLU(
                0.2
            ),



            nn.Flatten(),


            nn.Linear(
                256*64*64,
                1024
            ),

            nn.LeakyReLU(
                0.2
            ),



            nn.Linear(
                1024,
                1
            ),

            nn.Sigmoid()

        )


    def forward(self,x):

        return self.model(x)



# ==========================================
# Model 생성
# ==========================================


G = SRGenerator()

D = SRDiscriminator()



# ==========================================
# Example Input
# ==========================================

# Low Resolution Image
# 64x64

low_resolution = torch.randn(
    1,
    3,
    64,
    64
)



# High Resolution Target
# 256x256

high_resolution = torch.randn(
    1,
    3,
    256,
    256
)



# ==========================================
# Generator Forward
# ==========================================


fake_high_resolution = G(
    low_resolution
)



print(
    fake_high_resolution.shape
)



# ==========================================
# Adversarial Loss
# ==========================================


fake_result = D(
    fake_high_resolution
)


real_label = torch.ones_like(
    fake_result
)


gan_loss = nn.BCELoss()(
    fake_result,
    real_label
)



# ==========================================
# Content Loss
# VGG Feature 비교 개념
# ==========================================


content_loss = nn.MSELoss()(
    fake_high_resolution,
    high_resolution
)



# ==========================================
# SRGAN 최종 Loss
# ==========================================


generator_loss = (

    content_loss

    +

    0.001 * gan_loss

)


print(
    "SRGAN Loss:",
    generator_loss.item()
)



StyleGAN (2018): 스타일을 단계별로 제어해 얼굴 생성 품질을 크게 향상하고 스타일 조절 가능

# ==========================================
# StyleGAN 핵심 구현
# Mapping Network
# AdaIN
# Style Generator
# ==========================================


import torch
import torch.nn as nn



# ==========================================
# 1. Mapping Network
# z -> w
# ==========================================


class MappingNetwork(nn.Module):

    def __init__(self):

        super().__init__()


        self.network = nn.Sequential(

            nn.Linear(
                512,
                512
            ),

            nn.LeakyReLU(0.2),


            nn.Linear(
                512,
                512
            ),

            nn.LeakyReLU(0.2),


            nn.Linear(
                512,
                512
            )

        )


    def forward(self,z):

        w = self.network(z)

        return w



# ==========================================
# 2. AdaIN
# Style 적용
# ==========================================


class AdaIN(nn.Module):

    def __init__(self, channels):

        super().__init__()


        self.style_scale = nn.Linear(
            512,
            channels
        )


        self.style_bias = nn.Linear(
            512,
            channels
        )


    def forward(
        self,
        feature,
        style
    ):


        batch,channel,_,_ = feature.shape


        # Instance Normalization

        mean = feature.mean(
            [2,3],
            keepdim=True
        )


        std = feature.std(
            [2,3],
            keepdim=True
        )


        normalized = (

            feature - mean

        ) / (

            std + 1e-8

        )



        # Style vector 적용

        scale = self.style_scale(
            style
        ).view(
            batch,
            channel,
            1,
            1
        )


        bias = self.style_bias(
            style
        ).view(
            batch,
            channel,
            1,
            1
        )



        return (
            scale * normalized
            +
            bias
        )



# ==========================================
# 3. Style Block
# ==========================================


class StyleBlock(nn.Module):

    def __init__(
        self,
        channels
    ):

        super().__init__()


        self.conv = nn.Conv2d(
            channels,
            channels,
            3,
            1,
            1
        )


        self.adain = AdaIN(
            channels
        )


        self.activation = nn.LeakyReLU(
            0.2
        )


    def forward(
        self,
        x,
        w
    ):


        x = self.conv(x)


        x = self.adain(
            x,
            w
        )


        return self.activation(x)



# ==========================================
# 4. StyleGAN Generator
# ==========================================


class StyleGenerator(nn.Module):

    def __init__(self):

        super().__init__()



        # Learned Constant

        self.constant = nn.Parameter(
            torch.randn(
                1,
                512,
                4,
                4
            )
        )



        self.block1 = StyleBlock(
            512
        )


        self.upsample = nn.Upsample(
            scale_factor=2
        )


        self.block2 = StyleBlock(
            256
        )


        self.to_rgb = nn.Conv2d(
            256,
            3,
            1
        )



    def forward(
        self,
        w
    ):


        batch = w.size(0)



        # 4x4 Learned Constant

        x = self.constant.repeat(
            batch,
            1,
            1,
            1
        )



        # Style 적용

        x = self.block1(
            x,
            w
        )



        # 해상도 증가

        x = self.upsample(
            x
        )



        x = self.block2(
            x,
            w
        )



        image = self.to_rgb(
            x
        )


        return torch.tanh(
            image
        )



# ==========================================
# Model 생성
# ==========================================


mapping = MappingNetwork()


generator = StyleGenerator()



# ==========================================
# Example
# ==========================================


# Random Noise

z = torch.randn(
    1,
    512
)



# Noise -> Style

w = mapping(
    z
)



# Style -> Image

image = generator(
    w
)



print(
    image.shape
)



StyleGAN2 (2019): StyleGAN 개선해 생성 품질 향상, 아티팩트 감소, 더 자연스러운 얼굴 생성한다.

# ==========================================
# StyleGAN2 핵심 구현
# Mapping Network
# Modulated Convolution
# Weight Demodulation
# ==========================================


import torch
import torch.nn as nn
import torch.nn.functional as F



# ==========================================
# 1. Mapping Network
# z -> w
# ==========================================


class MappingNetwork(nn.Module):

    def __init__(self):

        super().__init__()


        self.network = nn.Sequential(

            nn.Linear(
                512,
                512
            ),

            nn.LeakyReLU(0.2),


            nn.Linear(
                512,
                512
            ),

            nn.LeakyReLU(0.2),


            nn.Linear(
                512,
                512
            )

        )


    def forward(self,z):

        return self.network(z)



# ==========================================
# 2. Modulated Convolution
# StyleGAN2 핵심
# ==========================================


class ModulatedConv2d(nn.Module):

    def __init__(
        self,
        in_channels,
        out_channels,
        kernel_size
    ):

        super().__init__()


        self.weight = nn.Parameter(

            torch.randn(
                1,
                out_channels,
                in_channels,
                kernel_size,
                kernel_size
            )

        )


        self.style = nn.Linear(
            512,
            in_channels
        )


        self.padding = kernel_size // 2



    def forward(
        self,
        x,
        w
    ):


        batch = x.size(0)


        # Style Vector 생성

        style = self.style(w)


        # modulation

        style = style.view(
            batch,
            1,
            -1,
            1,
            1
        )


        weight = self.weight * style



        # Weight Demodulation

        demod = torch.rsqrt(

            weight.pow(2)
            .sum(
                [2,3,4]
            )
            +
            1e-8

        )


        weight = weight * demod.view(
            batch,
            -1,
            1,
            1,
            1
        )



        # Batch별 convolution

        x = x.view(
            1,
            -1,
            x.size(2),
            x.size(3)
        )


        weight = weight.view(
            batch * weight.size(1),
            weight.size(2),
            weight.size(3),
            weight.size(4)
        )


        output = F.conv2d(

            x,

            weight,

            padding=self.padding,

            groups=batch

        )


        output = output.view(

            batch,

            -1,

            output.size(2),

            output.size(3)

        )


        return output



# ==========================================
# 3. StyleGAN2 Block
# ==========================================


class StyleGAN2Block(nn.Module):

    def __init__(
        self,
        channels
    ):

        super().__init__()


        self.conv = ModulatedConv2d(

            channels,

            channels,

            3

        )


        self.activation = nn.LeakyReLU(
            0.2
        )


    def forward(
        self,
        x,
        w
    ):


        x = self.conv(
            x,
            w
        )


        return self.activation(x)



# ==========================================
# 4. StyleGAN2 Generator
# ==========================================


class StyleGAN2Generator(nn.Module):

    def __init__(self):

        super().__init__()



        # Learned Constant

        self.constant = nn.Parameter(

            torch.randn(
                1,
                512,
                4,
                4
            )

        )


        self.block1 = StyleGAN2Block(
            512
        )


        self.upsample = nn.Upsample(
            scale_factor=2,
            mode="bilinear"
        )


        self.block2 = StyleGAN2Block(
            512
        )


        self.to_rgb = nn.Conv2d(

            512,

            3,

            1

        )



    def forward(
        self,
        w
    ):


        batch = w.size(0)


        x = self.constant.repeat(

            batch,

            1,

            1,

            1

        )



        # Style 적용

        x = self.block1(
            x,
            w
        )


        # 해상도 증가

        x = self.upsample(
            x
        )


        x = self.block2(
            x,
            w
        )


        image = self.to_rgb(
            x
        )


        return torch.tanh(
            image
        )



# ==========================================
# 실행 예시
# ==========================================


mapping = MappingNetwork()


generator = StyleGAN2Generator()



# Random Noise

z = torch.randn(
    1,
    512
)



# Noise -> Style

w = mapping(
    z
)



# Style -> Image

image = generator(
    w
)



print(
    image.shape
)



StyleGAN3 (2021): 위치 변화에 일관성 유지해 이미지 이동·회전 시 발생하던 왜곡을 개선한다.

# ==========================================
# StyleGAN3 핵심 구조
# Alias-Free Generator
# ==========================================


import torch
import torch.nn as nn
import torch.nn.functional as F



# ==========================================
# 1. Mapping Network
# Noise -> Style
# ==========================================


class MappingNetwork(nn.Module):

    def __init__(self):

        super().__init__()


        self.network = nn.Sequential(

            nn.Linear(
                512,
                512
            ),

            nn.LeakyReLU(0.2),


            nn.Linear(
                512,
                512
            ),

            nn.LeakyReLU(0.2),


            nn.Linear(
                512,
                512
            )

        )


    def forward(self,z):

        return self.network(z)




# ==========================================
# 2. Fourier Feature Input
# StyleGAN3 핵심
# ==========================================


class FourierInput(nn.Module):

    def __init__(self):

        super().__init__()


        self.freq = nn.Parameter(

            torch.randn(
                512,
                4,
                4
            )

        )


    def forward(
        self,
        batch
    ):


        x = self.freq.unsqueeze(0)


        return x.repeat(
            batch,
            1,
            1,
            1
        )




# ==========================================
# 3. Alias Free Convolution
# 필터 적용
# ==========================================


class AliasFreeConv(nn.Module):

    def __init__(
        self,
        channels
    ):

        super().__init__()


        self.conv = nn.Conv2d(

            channels,

            channels,

            3,

            1,

            1

        )


        self.activation = nn.LeakyReLU(
            0.2
        )


    def forward(self,x):


        # Anti Aliasing Filter

        x = F.avg_pool2d(

            x,

            kernel_size=2,

            stride=1,

            padding=1

        )


        x = self.conv(x)


        return self.activation(x)



# ==========================================
# 4. Style Modulation
# ==========================================


class StyleModulation(nn.Module):

    def __init__(
        self,
        channels
    ):

        super().__init__()


        self.style = nn.Linear(

            512,

            channels

        )



    def forward(
        self,
        x,
        w
    ):


        scale = self.style(w)


        scale = scale.unsqueeze(
            -1
        ).unsqueeze(
            -1
        )


        return x * scale




# ==========================================
# 5. StyleGAN3 Generator
# ==========================================


class StyleGAN3Generator(nn.Module):


    def __init__(self):

        super().__init__()


        self.input = FourierInput()



        self.conv1 = AliasFreeConv(
            512
        )


        self.style1 = StyleModulation(
            512
        )



        self.upsample = nn.Upsample(

            scale_factor=2,

            mode="bilinear",

            align_corners=False

        )



        self.conv2 = AliasFreeConv(
            512
        )


        self.style2 = StyleModulation(
            512
        )


        self.to_rgb = nn.Conv2d(

            512,

            3,

            1

        )



    def forward(
        self,
        w
    ):


        batch = w.size(0)


        # Fourier Feature 생성

        x = self.input(
            batch
        )



        # Style 적용

        x = self.style1(
            x,
            w
        )


        x = self.conv1(
            x
        )



        # 해상도 증가

        x = self.upsample(
            x
        )



        x = self.style2(
            x,
            w
        )


        x = self.conv2(
            x
        )



        image = self.to_rgb(
            x
        )


        return torch.tanh(
            image
        )




# ==========================================
# 실행
# ==========================================


mapping = MappingNetwork()


generator = StyleGAN3Generator()



# Random Noise

z = torch.randn(
    1,
    512
)



# z -> w

w = mapping(
    z
)



# w -> Image

image = generator(
    w
)



print(
    image.shape
)



BigGAN (2018): 대규모 데이터와 대형 모델 사용해 ImageNet 수준의 고품질·고해상도 이미지 생성한다.

# ==========================================
# BigGAN 핵심 구현
# Conditional GAN
# Class Embedding
# Spectral Normalization
# Self Attention
# ==========================================


import torch
import torch.nn as nn
import torch.nn.functional as F



# ==========================================
# 1. Conditional Batch Normalization
# Class 정보 반영
# ==========================================


class ConditionalBatchNorm(nn.Module):

    def __init__(
        self,
        channels,
        num_classes
    ):

        super().__init__()


        self.bn = nn.BatchNorm2d(
            channels,
            affine=False
        )


        self.gamma = nn.Embedding(
            num_classes,
            channels
        )


        self.beta = nn.Embedding(
            num_classes,
            channels
        )



    def forward(
        self,
        x,
        label
    ):


        x = self.bn(x)


        gamma = self.gamma(
            label
        )


        beta = self.beta(
            label
        )


        gamma = gamma.unsqueeze(
            2
        ).unsqueeze(
            3
        )


        beta = beta.unsqueeze(
            2
        ).unsqueeze(
            3
        )


        return gamma*x + beta




# ==========================================
# 2. Self Attention
# ==========================================


class SelfAttention(nn.Module):

    def __init__(
        self,
        channels
    ):

        super().__init__()


        self.query = nn.Conv2d(
            channels,
            channels//8,
            1
        )


        self.key = nn.Conv2d(
            channels,
            channels//8,
            1
        )


        self.value = nn.Conv2d(
            channels,
            channels,
            1
        )


        self.gamma = nn.Parameter(
            torch.zeros(1)
        )



    def forward(self,x):

        batch,channel,height,width = x.shape


        q = self.query(x)

        k = self.key(x)

        v = self.value(x)



        q = q.view(
            batch,
            -1,
            height*width
        )


        k = k.view(
            batch,
            -1,
            height*width
        )


        attention = torch.bmm(
            q.transpose(1,2),
            k
        )


        attention = F.softmax(
            attention,
            dim=-1
        )



        v = v.view(
            batch,
            channel,
            height*width
        )


        out = torch.bmm(
            v,
            attention.transpose(1,2)
        )


        out = out.view(
            batch,
            channel,
            height,
            width
        )


        return x + self.gamma*out





# ==========================================
# 3. BigGAN Generator Block
# ==========================================


class BigGANBlock(nn.Module):

    def __init__(
        self,
        in_channels,
        out_channels,
        num_classes
    ):

        super().__init__()



        self.bn1 = ConditionalBatchNorm(
            in_channels,
            num_classes
        )


        self.bn2 = ConditionalBatchNorm(
            out_channels,
            num_classes
        )


        self.conv1 = nn.Conv2d(
            in_channels,
            out_channels,
            3,
            1,
            1
        )


        self.conv2 = nn.Conv2d(
            out_channels,
            out_channels,
            3,
            1,
            1
        )


        self.skip = nn.Conv2d(
            in_channels,
            out_channels,
            1
        )



    def forward(
        self,
        x,
        label
    ):


        identity = self.skip(x)


        x = self.bn1(
            x,
            label
        )


        x = F.relu(x)


        x = self.conv1(x)


        x = self.bn2(
            x,
            label
        )


        x = F.relu(x)


        x = self.conv2(x)



        return x + identity




# ==========================================
# 4. BigGAN Generator
# ==========================================


class BigGANGenerator(nn.Module):

    def __init__(
        self,
        num_classes=1000
    ):

        super().__init__()



        self.embedding = nn.Embedding(
            num_classes,
            128
        )



        self.fc = nn.Linear(
            512,
            4*4*512
        )


        self.block1 = BigGANBlock(
            512,
            256,
            num_classes
        )


        self.block2 = BigGANBlock(
            256,
            128,
            num_classes
        )


        self.attention = SelfAttention(
            128
        )


        self.output = nn.Conv2d(
            128,
            3,
            3,
            1,
            1
        )



    def forward(
        self,
        noise,
        label
    ):


        x = self.fc(
            noise
        )


        x = x.view(
            -1,
            512,
            4,
            4
        )



        x = F.interpolate(
            x,
            scale_factor=2
        )


        x = self.block1(
            x,
            label
        )



        x = F.interpolate(
            x,
            scale_factor=2
        )


        x = self.block2(
            x,
            label
        )


        x = self.attention(
            x
        )


        image = torch.tanh(
            self.output(x)
        )


        return image




# ==========================================
# 5. BigGAN Discriminator
# ==========================================


class BigGANDiscriminator(nn.Module):

    def __init__(
        self,
        num_classes=1000
    ):

        super().__init__()


        self.embedding = nn.Embedding(
            num_classes,
            128
        )


        self.model = nn.Sequential(

            nn.utils.spectral_norm(

                nn.Conv2d(
                    3,
                    64,
                    3,
                    2,
                    1
                )

            ),


            nn.LeakyReLU(0.2),


            nn.utils.spectral_norm(

                nn.Conv2d(
                    64,
                    128,
                    3,
                    2,
                    1
                )

            ),


            nn.LeakyReLU(0.2),


            nn.Flatten()

        )



        self.fc = nn.Linear(
            128*64*64,
            1
        )



    def forward(
        self,
        image,
        label
    ):


        x = self.model(
            image
        )


        return self.fc(x)





# ==========================================
# 실행 예제
# ==========================================


G = BigGANGenerator()

D = BigGANDiscriminator()



# Noise

z = torch.randn(
    1,
    512
)



# ImageNet Class
# 예: dog class

label = torch.tensor(
    [207]
)



# 생성

fake_image = G(
    z,
    label
)



# 판별

score = D(
    fake_image,
    label
)



print(
    fake_image.shape
)


print(
    score.shape
)




 

 

TTS Text-to-Speech

컴퓨터가 텍스트를 사람의 음성으로 변환하여 읽어주는 인공지능 기술

사람이 직접 문서를읽지않아도 컴퓨터가 내용을 음성으로 전달할 수 있도록 한다. 

textinput 문장혹은 답변을 입력받아 text normalization 읽기쉬운 형태로 변환해 기계가 숫자나 기호를 2026년을 이천이십육년등으로 부자연스럽게 읽도록한다.  

초기 TTS는 사람이 하나하나 규칙을 수천~ 수만개를 작성했다. 이를 Rule-based Text Normalization라하는데 요즘은 딥러닝 기반 Text Normalization을 많이 사용한다 . 요즘은 Transformer(또는 T5/BART 같은 Seq2Seq 모델) 이 Text Normalization을 수행한다.

TTS는 보통 내부모델을 사용하기 때문에 Google, Microsoft, Amazon Polly, Azure Speech, ElevenLabs, 네이버 CLOVA 등의 TTS는 Text Normalization 모델을 공개하지 않는다. 

내부 Neural Text Normalization (ByT5 계열로 알려져 있으나 비공개)인 Google TTS
공개 Text Normalization 라이브러리 오픈소스  NVIDIA NeMo 등이 있는데 실제로실행가능한 공개 예시를 보여주려면 NVIDIA NeMo 예제가 가장 적합하다 .

from nemo_text_processing.text_normalization.normalize import Normalizer

normalizer = Normalizer(
    input_case="cased",
    lang="ko"
)

text = "오늘은 2026년 7월 30일입니다."

normalized = normalizer.normalize(text)

print(normalized)

 

 

 

위와 반대로는 STT가 있다. Speech - to - text

사람의 음성을 입력받아 텍스트로 변환하는 AI 기술

현재 STT 모델로는 Whisper (OpenAI), Wav2Vec2, Conformer, DeepSpeech 가 있고 Whisper는 실제 서비스에서 많이 사용하는 STT 모델이다.

# ==========================================
# STT Speech To Text 예제
# Whisper 모델 사용
# ==========================================


import whisper



# ==========================================
# 1. 모델 로드
# ==========================================

model = whisper.load_model(
    "base"
)



# ==========================================
# 2. 음성 파일 입력
# ==========================================

audio_file = "speech.mp3"



# ==========================================
# 3. STT 실행
# ==========================================

result = model.transcribe(
    audio_file,
    language="ko"
)



# ==========================================
# 4. Text 출력
# ==========================================

text = result["text"]


print(
    "인식 결과:",
    text
)