본문 바로가기

책/파이토치 트랜스포머를 활용한 자연어 처리와 컴퓨터비전 심층학습

03 파이토치 기초 (6) 퍼셉트론

퍼셉트론

퍼셉트론의 자세한 내용은 이전에 다룬적이 있기 때문에 파이토치에서 퍼셉트론을 구현하는 방법만 다룬다.

2023.09.13 - [책/밑바닥부터 시작하는 딥러닝] - 2장 퍼셉트론

 

2장 퍼셉트론

퍼셉트론이란? 퍼셉트론은 다수의 신호를 입력으로 받아 하나의 신호를 출력한다. 퍼셉트론 신호는 1이나 0 두가지 값을 가질 수 있다. x1, x2는 입력 신호, w1, w2는 가중치, y는 출력신호를 뜻한다.

ai-junha.tistory.com

 

퍼셉트론 모델 실습

데이터셋

 

데이터셋에 perceptron.csv 파일을 사용해 퍼셉트론을 구현해본다. 데이터는 다음과 같은 형태로 제공된다.

데이터가 bool형이기 때문에 CustomDataset 클래스에서 float으로 바꿔주어야 한다.

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.tensor([self.x1[index], self.x2[index]], dtype=torch.float)
        y = torch.tensor([self.y[index]], dtype=torch.float)
        return x, y

    def __len__(self):
        return self.length

train_dataset = CustomDataset(DATA_PATH / "perceptron.csv")
train_dataloader = DataLoader(train_dataset, batch_size=64, shuffle=True, drop_last=True)

 

단층 퍼셉트론

class CustomModel(nn.Module):
    def __init__(self):
        super().__init__()

        self.layer = nn.Sequential(
            nn.Linear(2, 1),
            nn.Sigmoid()
        )

    def forward(self, x):
        x = self.layer(x)
        return x

학습

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(1000):
    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

    cost = cost / len(train_dataloader)

    if (epoch + 1) % 100 == 0:
        print(f"Epoch : {epoch+1:4d}, Cost : {cost:.3f}")

출력 결과

Epoch :  100, Cost : 0.693
Epoch :  200, Cost : 0.692
Epoch :  300, Cost : 0.692
Epoch :  400, Cost : 0.692
Epoch :  500, Cost : 0.692
Epoch :  600, Cost : 0.692
Epoch :  700, Cost : 0.692
Epoch :  800, Cost : 0.692
Epoch :  900, Cost : 0.693
Epoch : 1000, Cost : 0.692

단층 퍼셉트론 구조로 XOR 문제를 해결하려고 한다면, 비용이 더 이상 감소되지 않는 것을 확인할 수 있다.

 

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)

 

출력 결과

---------
tensor([[0.4665],
        [0.4981],
        [0.5023],
        [0.5340]], device='cuda:0')
tensor([[ True],
        [ True],
        [False],
        [False]], device='cuda:0')

모델에 값을 입력했을 때도 출력값이 0.5 내외로 출력돼 학습이 정상적으로 진행되지 않은 것을 확인할 수 있다.

 

다층 퍼셉트론

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

XOR 문제를 해결하기 위해 계층을 하나 더 추가하였다.

 

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.1)

for epoch in range(1000):
    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

    cost = cost / len(train_dataloader)

    if (epoch + 1) % 100 == 0:
        print(f"Epoch : {epoch+1:4d}, Cost : {cost:.3f}")

출력 결과

Epoch :  100, Cost : 0.693
Epoch :  200, Cost : 0.693
Epoch :  300, Cost : 0.693
Epoch :  400, Cost : 0.690
Epoch :  500, Cost : 0.634
Epoch :  600, Cost : 0.187
Epoch :  700, Cost : 0.056
Epoch :  800, Cost : 0.031
Epoch :  900, Cost : 0.021
Epoch : 1000, Cost : 0.016

학습이 진행될수록 비용이 감소하는 것을 확인할 수 있다.

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)

출력 결과

---------
tensor([[0.0150],
        [0.9854],
        [0.9799],
        [0.0135]], device='cuda:0')
tensor([[ True],
        [False],
        [False],
        [ True]], device='cuda:0')

모델에 값을 입력했을 때 출력값이 XOR 게이트의 출력값과 동일하다는 것도 확인할 수 있다.

 

퍼셉트론은 이진 분류 작업에서 여전히 사용되는 간단하고 효율적인 모델이지만, 데이터의 복잡한 패턴을 학습하 ㄹ수 없으며, 선형으로 분리되지 않는 데이터를 분류할 수 없는 등 몇 가지 제한 사항이 있다.