본문 바로가기

모두를 위한 딥런닝 by PyTorch

Lab-06 Softmax Classification

728x90

드디어 part1의 마지막 내용 softmax까지 오게 되었습니다.

여기까지 학습하면 이제 머신러닝에 대한 기본적인 내용은 끝나게 되는데요!

설레는 마음으로 후딱 정리해보도록 하겠습니다!!

이번에도 모모딥 시즌1과 wikidocs를 참고하였습니다.

바로 시작할게요~~~

 

학습 목표

소프트맥스 분류(Softmax Classification)에 대해 알아본다.

핵심 키워드

소프트맥스(Softmax)

크로스 엔트로피(Cross Entropy)


1. 원-핫 인코딩(One-Hot Encoding)

softmax classification에  대하여 이야기하기 전에 먼저 원-핫 인코딩에 대한 부분을 이야기해보려고 합니다. 원-핫 인코딩은 선택해야 하는 선택지의 개수만큼의 차원을 가지면서, 각 선택지의 인덱스에 해당하는 원소에는 1, 나머지 원소는 0의 값을 가지도록 하는 표현 방법입니다. 예를 들어, 빨강, 파랑, 노랑 세 개의 선택지가 있다고 가장해봅시다. 그럼 빨강 = [1, 0, 0] , 파랑 = [0, 1, 0] , 노랑 = [0, 0, 1]로 표현하는 것입니다. 

그럼 왜 굳이 이렇게 표현하는 걸까요?? 여기서 바로 '원-핫 벡터의 무작위성'이라는 개념이 등장하게 됩니다. 예를 들어 빨강, 파랑, 노랑에 순서대로 1,2,3이라 가정해봅시다. 그리고 실제값이 '노랑' 일 때, 빨강의 제곱 오차를 구해보면 아래와 같습니다.

이제 실제값이 ' 파랑' 일 때 빨강의 제곱 오차를 구하면 아래와 같습니다.

즉 빨강은 파랑에 더 가깝다는 의미를 갖게 됩니다. 모델을 만드는 사람은 빨강, 파랑, 노랑에 동등한 관계를 부여하고자 하였지만 이렇게 순서대로 레이블을 하게 되면 균등한 관계가 성립되지 않습니다. 따라서 이를 방지하고자 원-핫 인코딩을 사용하는 것입니다. 원-핫 인코딩을 사용하게 되면 아래와 같은 동일한 결과가 나오게 됩니다.

2. 소프트맥스 회귀(Softmax Regression)

자! 그럼 꽃받침 길이, 꽃받침 넓이, 꽃잎 길이, 꽃잎 넓이라는 4개의 특성으로  setosa, versicolor, virginica라는 3개의 붓꽃 품종 중 어떤 품종인지를 예측하는 문제를 통해 Softmax Regression에 대해 알아보도록 하겠습니다!!

꽃받침 길이 꽃받침 넓이 꽃잎 길이 꽃잎 넓이 품종
5.1 3.5 1.4 0.2 setosa
4.9 3 1.4 0.2 setosa
5.8 2.6 4 1.2 vesicolor
6.7 3 5..2 2.3 virginica
5.6 2.8 4.9 2 virginica

소프트맥스에서 중요한 점은 각 선택지의 합이 총 1이 되어야 한다는 겁니다. 소프트맥스는 각 선택지마다 소수 확률을 할당합니다. 이는 각 선택지가 정답일 확률을 의미합니다. 

이렇게 각 선택지가 정답일 확률을 구해주는 함수가 바로 Softmax function입니다.

여기서 두 가지의 의문점이 생기게 됩니다. 먼저, X입력값들은 어떻게 Softmax function의 입력값이 되는 걸까? 

정답은 바로 W 때문입니다!!!

위 그림에서 알 수 있듯이 W의 차원을 조정하여 (3,1)이 될 수 있도록 하는 것이지요! 

그럼 오차는 어떻게 구하는 걸까요?? 바로 앞서 설명한 '원-핫 인코딩'을 통해 구할 수 있습니다! 

원-핫 인코딩한 실제값과 예측값의 가중치를 구하여 최적의 W, b를 찾는 것이지요! 

그럼 이제 구체적으로 어떻게 Cost를 구하는지 알아보도록 하겠습니다.

 

3. 비용 함수(Cost function)

비용 함수로는 '크로스 엔트로피'를 사용합니다. 비용 함수에 대한 부분은 개념 부분과 PyTorch 실습 부분으로 나눠서 설명하겠습니다!!

   1) 크로스 엔트로피 함수 개념

크로스 엔토로 피 함수는 아래와 같이 생겼습니다. Yj는 j번째 인덱스의 원-핫 벡터를 Pj는 샘플 데이터가 j번째 클래스일 확률을 나타냅니다.

 

크로스 엔트로피 함수를 비용 함수로 사용할 수 있는 이유는 정답일 때는 값이 0이 되고 오답일 때는 값이 커지기 때문입니다. 예를 들어 1을 가지는 원-핫 벡터에 대하여 정확하게 예측을 한다면 -1 log(1)=0이 됩니다. 하지만 정확하게 예측하지 못한다면 -1 log(0)=-∞가 됩니다. 즉, 정답일 때는 cost가 작아지지만 오답인 경우 cost가 증가하게 되는 것이지요.

이를 n개의 전체 데이터에 대한 평균을 구한다고 하면 최종적으로 다음과 같습니다.

여기서 눈치채신 분도 있겠는데요. 사실 이 크로스 엔토로피 함수는 저희가 이미 배웠던 함수입니다!! 바로 이진 분류기에서의 크로스 엔트로피 함수와 동일합니다!!! 

왜 이렇게 되는지는 한번 천천히 고민해보시면 좋을 것 같습니다!!

   2) 크로스 엔트로피 함수 구현

import torch
import torch.nn.functional as F

      2.1 Low Level

z = torch.rand(3, 5, requires_grad=True) # 임의의 (3x5) 행렬
hypothesis = F.softmax(z, dim=1) # 각 행의 합이 각각 1이 되어야함으로 dim=1
print(hypothesis)
# 3개의 샘플에 대하여 5개의 클래스 중 어떤 클래스가 정답인지 예측
tensor([[0.1362, 0.2075, 0.1987, 0.2365, 0.2211],
        [0.2009, 0.2840, 0.1621, 0.1492, 0.2039],
        [0.3049, 0.1849, 0.1731, 0.1832, 0.1539]], grad_fn=<SoftmaxBackward>)

여기까지가 이제 예측값을 구한 것입니다. 이제 원-핫 벡터를 만들어보겠습니다.

y = torch.randint(5,(3,)).long() # y는 0~4 중 사이즈가 (3,)인 행렬
print(y)

tensor([2, 4, 2])
y_one_hot=torch.zeros_like(hypothesis) # hypothesis와 같은 size의 원소가 모두 0인 행렬
y_one_hot.scatter_(1, y.unsqueeze(1),1) # dim=1 에 대하여 y.unsqueeze(1)의 위치에 숫자 1을 대입
print(y_one_hot)
# 원-핫 벡터
tensor([[0., 0., 1., 0., 0.],
        [0., 0., 0., 0., 1.],
        [0., 0., 1., 0., 0.]])

이제 원-핫 벡터까지 준비가 됐으니 cost를 구해보도록 하겠습니다!!!

# Cost
cost=(y_one_hot*-torch.log(hypothesis)).sum(dim=1).mean()
print(cost)
tensor(1.6532, grad_fn=<MeanBackward1>)

      2.2 High Level

F.softmax() + torch.log() = F.log_softmax()

torch.log(F.softmax(z,dim=1))
tensor([[-1.2127, -1.4941, -1.8359, -1.9517, -1.7335],
        [-1.8912, -1.8358, -1.6516, -1.3775, -1.4038],
        [-2.3011, -1.5023, -1.6726, -1.3380, -1.4823]], grad_fn=<LogBackward>)

이를 F.log_softmax()로 간략하게 구할 수 있습니다.

F.log_softmax(z, dim=1)
tensor([[-1.2127, -1.4941, -1.8359, -1.9517, -1.7335],
        [-1.8912, -1.8358, -1.6516, -1.3775, -1.4038],
        [-2.3011, -1.5023, -1.6726, -1.3380, -1.4823]],
       grad_fn=<LogSoftmaxBackward>)

 F.log_softmax() + F.nll_loss() = F.cross_entropy()

(y_one_hot * -F.log_softmax(z, dim=1)).sum(dim=1).mean() # log_softmax() 적용
tensor(1.6471, grad_fn=<MeanBackward1>)

여기서 log_softmax()를 제외한 나머지 식을 F.nll_loss()로 대체할 수 있다.

F.nll_loss(F.log_softmax(z, dim=1), y)
tensor(1.6471, grad_fn=<NllLossBackward>)

마지막으로 원-핫 벡터를 구하는 과정을 포함하여 비용 함수를 구하는 전 과정을 F.cross_entropy()로 구할 수 있다. 즉,

F.cross_entropy는 비용 함수에 소프트맥스 함수까지 포함하고 있다.

3. 소프트맥스 회귀(Softmax Regression)  구현하기

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

torch.manual_seed(1)
x_train = [[1, 2, 1, 1],
           [2, 1, 3, 2],
           [3, 1, 3, 4],
           [4, 1, 5, 5],
           [1, 7, 5, 5],
           [1, 2, 5, 6],
           [1, 6, 6, 6],
           [1, 7, 7, 7]]
y_train = [2, 2, 2, 1, 1, 1, 0, 0]
x_train = torch.FloatTensor(x_train)
y_train = torch.LongTensor(y_train)

   3.1 Low level

W = torch.zeros((4, 3), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
# optimizer 설정
optimizer = optim.SGD([W, b], lr=0.1)

nb_epochs = 1000
for epoch in range(nb_epochs + 1):

    # Cost 계산
    z = x_train.matmul(W) + b
    cost = F.cross_entropy(z, y_train)

    # cost로 H(x) 개선
    optimizer.zero_grad()
    cost.backward()
    optimizer.step()

    # 100번마다 로그 출력
    if epoch % 100 == 0:
        print('Epoch {:4d}/{} Cost: {:.6f}'.format(
            epoch, nb_epochs, cost.item()
        ))
Epoch    0/1000 Cost: 1.032776
Epoch  100/1000 Cost: 0.712104
Epoch  200/1000 Cost: 0.637315
Epoch  300/1000 Cost: 0.590370
Epoch  400/1000 Cost: 0.553425
Epoch  500/1000 Cost: 0.521421
Epoch  600/1000 Cost: 0.492227
Epoch  700/1000 Cost: 0.464692
Epoch  800/1000 Cost: 0.438060
Epoch  900/1000 Cost: 0.411747
Epoch 1000/1000 Cost: 0.385264

   3.2 High level(nn.module)

class SoftmaxClassifierModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(4, 3) # Output이 3!

    def forward(self, x):
        return self.linear(x)
model = SoftmaxClassifierModel()
# optimizer 설정
optimizer = optim.SGD(model.parameters(), lr=0.1)

nb_epochs = 1000
for epoch in range(nb_epochs + 1):

    # H(x) 계산
    prediction = model(x_train)

    # cost 계산
    cost = F.cross_entropy(prediction, y_train)

    # cost로 H(x) 개선
    optimizer.zero_grad()
    cost.backward()
    optimizer.step()

    # 20번마다 로그 출력
    if epoch % 100 == 0:
        print('Epoch {:4d}/{} Cost: {:.6f}'.format(
            epoch, nb_epochs, cost.item()
        ))

 

Epoch    0/1000 Cost: 1.366217
Epoch  100/1000 Cost: 0.722726
Epoch  200/1000 Cost: 0.637564
Epoch  300/1000 Cost: 0.578576
Epoch  400/1000 Cost: 0.527363
Epoch  500/1000 Cost: 0.479315
Epoch  600/1000 Cost: 0.432701
Epoch  700/1000 Cost: 0.386693
Epoch  800/1000 Cost: 0.340930
Epoch  900/1000 Cost: 0.295757
Epoch 1000/1000 Cost: 0.255350

모듈을 사용했을 때의 cost가 더 낮게 나오는데 모듈을 사용해서 그런 건지는 잘 모르겠네요....


이렇게!! part1에서 ML 개념에 대한 정리가 모두 끝났습니다!!

이제 뒤에 Tip과 MINIST 부분이 있는데

이건 간단하게 정리하고 넘어갈게요!!

얼른 딥러닝에 대한 알고리즘들을 배워보고 싶거든요 ㅠㅠ

이렇게 하나씩 정리를 하며 공부를 하려니 힘들긴 하지만

그래도 머릿속에 정리도 되고 훨씬 좋은 것 같아요 ㅎㅎ

이 강의를 모두 완강하는 그날까지 열심히 달려가 보겠습니다!!

아자아자!!!!

 

반응형

'모두를 위한 딥런닝 by PyTorch' 카테고리의 다른 글

Lab 07-2 DNN  (0) 2021.09.05
Lab-07-1 DNN  (0) 2021.08.24
Lab-05 Logistic Regression  (0) 2021.08.12
Lab-04-2 Minibatch Gradient Descent  (0) 2021.08.09
Lab-04-1 Multivariable Linear regression  (0) 2021.08.08