Posted:      Updated:

AI 스터디를 하며 ‘파이토치 트랜스포머를 활용한 자연어 처리와 컴퓨터비전 심층학습’ 교재를 정리한 글입니다.

퍼셉트론(Perceptron)

인공 신경망의 한 종류로 출력이 0 또는 1인 작업을 의미하는 이진 분류 작업에 사용되는 간단한 모델
신경 세포(Neuron)가 신호를 전달하는 방식과 유사하게 구현됨

TLU(Threshold Logic Unit) 형태로, 계단 함수를 적용해 결과 반환
입력값($x$)과 노드의 가중치 곱한 값을 모두 더했을 때 임곗값보다 크면 1 출력, 작으면 0 출력
여러 입력값 입력 시 0이나 1 또는 -1에서 1 사이의 값 출력

단층 퍼셉트론(Single Layer Perceptron)

하나의 계층을 갖는 모델로, 입력을 통해 데이터가 전달되고 입력값($x$)은 각각의 가중치와 함께 노드에 전달됨
전달된 입력값($x$)과 가중치를 곱한 값이 활성화 함수에 전달
활성화 함수에서 출력값($\hat{y}$)이 계산되고 이 값을 손실 함수에 실젯값($y$)과 함께 연산해 가중치 변경

$AND, OR, NAND$는 쉽게 구현 가능하지만, $XOR$ 게이트처럼 하나의 기울기로 표현하기 어려운 단층은 사용이 어려움

import torch
import pandas as pd
from torch import nn, optim
from torch.utils.data import Dataset, DataLoader
class CustomDataset(Dataset):
    def __init__(self, file_path):
        df = pd.read_csv(file_path)
        self.x1 = df.iloc[:, 0].values
        self.x2 = df.iloc[:, 1].values
        self.y = df.iloc[:, 2].values
        self.length = len(df)

    def __getitem__(self, index):
        x = torch.FloatTensor([self.x1[index], self.x2[index]])
        y = torch.FloatTensor([self.y[index]])
        return x, y

    def __len__(self):
        return self.length
class CustomModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.layer = nn.Sequential(
            nn.Linear(2, 1),
            nn.Sigmoid()
        )

    def forward(self, x):
        return self.layer(x)
train_dataset = CustomDataset("datasets/perceptron.csv")
train_dataloader = DataLoader(train_dataset, batch_size=64, shuffle=True, drop_last=True)
device = "cuda" if torch.cuda.is_available() else "cpu"
model = CustomModel().to(device)
criterion = nn.BCELoss().to(device)
optimizer = optim.SGD(model.parameters(), lr=0.01)
for epoch in range(10000): # XOR 문제에서 비용이 감소되지 않음
    cost = 0.0
    for x, y in train_dataloader:
        x = x.to(device)
        y = y.to(device)
        
        output = model(x)
        loss = criterion(output, y)
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        cost += loss.item()
        
    cost = cost / len(train_dataloader)
    
    if (epoch + 1) % 1000 == 0:
        print(f"Epoch: {epoch+1:4d}, Cost: {cost:.3f}")
with torch.no_grad():
    model.eval()
    inputs = torch.FloatTensor([[0, 0], [0, 1], [1, 0], [1, 1]]).to(device)
    outputs = model(inputs)
    print("----------")
    print(outputs)
    print(outputs <= 0.5)

출력

Epoch: 1000, Cost: 0.692
Epoch: 2000, Cost: 0.692
Epoch: 3000, Cost: 0.692
Epoch: 4000, Cost: 0.692
Epoch: 5000, Cost: 0.692
Epoch: 6000, Cost: 0.692
Epoch: 7000, Cost: 0.693
Epoch: 8000, Cost: 0.692
Epoch: 9000, Cost: 0.692
Epoch: 10000, Cost: 0.692
----------
tensor([[0.4675],
        [0.5001],
        [0.5038],
        [0.5364]], device='cuda:0')
tensor([[ True],
        [False],
        [False],
        [False]], device='cuda:0')

다층 퍼셉트론(Multi-Layer Perceptron, MLP)

단층 퍼셉트론을 여러개 쌓아 은닉층 생성
은닉층이 한 개 이상인 퍼셉트론 구조
은닉층을 2개 이상 연결하면 심층 신경망(Deep Neural Network, DNN)이라고 함

역전파 과정을 통해 모든 노드의 가중치와 편향을 수정해 오차가 작아지는 방향으로 학습 진행

  1. 입력층부터 출력층까지 순전파 진행
  2. 출력값(예측값)과 실젯값으로 오차 계산
  3. 오차를 퍼셉트론의 역방향으로 보내면서 입력된 노드의 기여도 측정
  4. 입력층에 도달할 때까지 노드의 기여도 측정
  5. 모든 가중치에 최적화 알고리즘 수행

이진 분류 작업에서 사용되는 간단하고 효율적인 모델
데이터의 복잡한 패턴을 학습할 수 없으며, 선형으로 분리되지 않는 데이터를 분류할 수 없음

import torch
import pandas as pd
from torch import nn, optim
from torch.utils.data import Dataset, DataLoader
class CustomDataset(Dataset):
    def __init__(self, file_path):
        df = pd.read_csv(file_path)
        self.x1 = df.iloc[:, 0].values
        self.x2 = df.iloc[:, 1].values
        self.y = df.iloc[:, 2].values
        self.length = len(df)

    def __getitem__(self, index):
        x = torch.FloatTensor([self.x1[index], self.x2[index]])
        y = torch.FloatTensor([self.y[index]])
        return x, y

    def __len__(self):
        return self.length
class CustomModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.layer1 = nn.Sequential(
            nn.Linear(2, 2),
            nn.Sigmoid()
        )
        self.layer2 = nn.Sequential(
            nn.Linear(2, 1),
            nn.Sigmoid()
        )

    def forward(self, x):
        x = self.layer1(x)
        x = self.layer2(x)
        return x
train_dataset = CustomDataset("datasets/perceptron.csv")
train_dataloader = DataLoader(train_dataset, batch_size=64, shuffle=True, drop_last=True)
device = "cuda" if torch.cuda.is_available() else "cpu"
model = CustomModel().to(device)
criterion = nn.BCELoss().to(device)
optimizer = optim.SGD(model.parameters(), lr=0.01)
for epoch in range(10000): 
    cost = 0.0
    for x, y in train_dataloader:
        x = x.to(device)
        y = y.to(device)
        
        output = model(x)
        loss = criterion(output, y)
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        cost += loss.item()
        
    cost = cost / len(train_dataloader)
    
    if (epoch + 1) % 1000 == 0:
        print(f"Epoch: {epoch+1:4d}, Cost: {cost:.3f}")
with torch.no_grad():
    model.eval()
    inputs = torch.FloatTensor([[0, 0], [0, 1], [1, 0], [1, 1]]).to(device)
    outputs = model(inputs)
    print("----------")
    print(outputs)
    print(outputs <= 0.5)

출력

Epoch: 1000, Cost: 0.693
Epoch: 2000, Cost: 0.693
Epoch: 3000, Cost: 0.693
Epoch: 4000, Cost: 0.690
Epoch: 5000, Cost: 0.645
Epoch: 6000, Cost: 0.461
Epoch: 7000, Cost: 0.229
Epoch: 8000, Cost: 0.055
Epoch: 9000, Cost: 0.030
Epoch: 10000, Cost: 0.021
----------
tensor([[0.0196],
        [0.9835],
        [0.9712],
        [0.0169]], device='cuda:0')
tensor([[ True],
        [False],
        [False],
        [ True]], device='cuda:0')

Categories:

댓글남기기