Python/[코칭스터디 9기] 인공지능 AI 기초 다지기

[인공지능(AI) 기초 다지기] 5. 딥러닝 핵심 기초 (8)

김초송 2023. 3. 12. 23:42

6) Softmax Classification

 

- Discrete Probability Distribution (이산 확률 분포)

  • ex) 주사위, 가위바위보 
  • PDF(확률밀도함수, 정규분포) 는 함수의 면적이 확률값
    point 자체는 확률이 아님, 알 수 없음
  • PMF(확률질량함수, 이산적인 확률 분포) 에서는 어떤 값 = 확률

 

- Softmax

  • max 를 soft 하게 뽑는다 = 합쳐서 1이 되는 값으로 나타냄 = 확률값
    [1, 2, 3] -> max : (0, 0, 1)
                  -> softmax : (0.0900, 0.2447, 0.6652)

  • 가위를 냈을 때 주먹 / 가위 / 보 를 낼 확률
  • 소프트맥스 값은 확률값이 됨

 

- Cross Entropy

  • 두 개의 확률 분포가 주어졌을 때 확률 분포가 얼마나 비슷한지 나타낼 수 있는 수치

  • P : 철수가 가위를 냈을 때 다음에 무엇을 낼지에 대한 확률 분포 함수
  • cross entropy 를 최소화 하면 계속해서 Q2 가 Q1, Q1이 P에 가까워짐
    = 모델의 확률 분포 함수는 점점 P 에 근사함

 

- Cross Entropy Loss (Low-level Implementation)

  • Pθ(x) <- Q(x)
z = torch.rand(3, 5, requires_grad=True) # uniform random : size = (3, 5)
hypothesis = F.softmax(z, dim=1)
print(hypothesis)
'''
tensor([[0.2645, 0.1639, 0.1855, 0.2585, 0.1277],
        [0.2430, 0.1624, 0.2322, 0.1930, 0.1694],
        [0.2226, 0.1986, 0.2326, 0.1594, 0.1868]], grad_fn=<SoftmaxBackward0>)
'''
  • requires_grad=True : gradient 배우게 함
  • 2번째 dimension 에 대해서 softmax 수행
    = [0.2430, 0.1624, 0.2322, 0.1930, 0.1694](이미 수행된 결과값) dimension 에 대해서 softmax 수행 
  • 결과값은 예측값 (= ^y = prediction)
y = torch.randint(5, (3,)).long()
print(y)
'''
tensor([0, 2, 1]) # one hot vector 의 인덱스 값
'''
  • y : 랜덤하게 정답 생성
  • class 갯수 : 5
    sample 갯수 : 3
  • y는 각각 샘플에 대한 정답의 인덱스임
  • 즉, 정답은 hypothesis 의 (0,0), (1, 2), (2, 1) 값 = [0.2645, 0.2322, 0.1986]
y_one_hot = torch.zeros_like(hypothesis)
y_one_hot.scatter_(1, y.unsqueeze(1), 1) # one hot vector 로 나타낸 것
'''
tensor([[1., 0., 0., 0., 0.],
        [0., 0., 1., 0., 0.],
        [0., 1., 0., 0., 0.]])
'''

cost = (y_one_hot * -torch.log(hypothesis)).sum(dim=1).mean()
print(cost)
'''
tensor(1.4689, grad_fn=<MeanBackward0>)
'''
  • 이산 확률 분포 -> one hot vector 로 나타내줌
    |y_one_hot| = (3, 5) = y_one hot 사이즈 = hypothesis 사이즈
  • scatter_ : inplace 연산 (새로 메모리 할당하지 않고 y_one_hot 값이 교체됨)
    (1, y.unsqueeze(1), 1) : dimension 1 에 대해서 y.unsqueeze(1) 을 가지고 1 을 뿌려라
  • |y| = (3, ) -> |y_unsqueeze| = (3, 1)
  • 아까 [0, 2, 1] -> [[0], [2], [1]]
  • log : cross entropy 수식
  • .sum(dim=1) : sum 하면 (3, 1) 사이즈가 됨 -> 두번째 dimension(=dimension 1) 은 사라짐 -> scalar

 

- Cross Entropy Loss with torch.nn.functional (High-level Implementation)

# low level
torch.log(F.softmax(z, dim=1))

# high level
F.log_softmax(z, dim=1)
# low level
(y_one_hot * -torch.log(hypothesis)).sum(dim=1).mean()

# high level
F.nll_loss(F.log_softmax(z, dim=1), y)

F.cross_entropy(z, y)
  • nll = Negative Log Likelihood
  • -, .sum(dim=1).mean() = nll_loss
  • cross_entropy = log_softmax + nll_loss
    -> softmax 를 포함함
  • 뉴럴 네트워크에서 확률값을 뱉어줘야 함 -> softmax 이전의 값을 필요할 때가 있음
  • 예측하는 과정에서 확률값을 구하기 위해서 다시 softmax 를 취해야 할 수도 있음

 

- Training Example

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)
  • |x_train| = (m, 4)
    |y_train| = (m, )
  • 4차원의 vector 를 받아서 어떤 class 인지 예측 
  • y_train : one hot vector 로 나타냈을 때 1이 있는 인덱스 값 -> discrete 하기 때문에
# Low-level Cross Entropy

# 모델 초기화
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 계산 
    hypothesis = F.softmax(x_train.matmul(W) + b, dim=1) # matmul or .mm or @
    y_one_hot = torch.zeros_like(hypothesis)
    y_one_hot.scatter_(1, y_train.unsqueeze(1), 1)
    cost = (y_one_hot * -torch.log(F.softmax(hypothesis, dim=1))).sum(dim=1).mean()
    
    # Cost 로 H(x) 계산
    optimizer.zero_grad()
    cost.backward() # loss 값 backpropagation
    optimizer.step() # gradient descent
    
    # 100번 마다 log 출력
    if epoch % 100 == 0:
        print("Epoch {:4d}/{} Cost: {:.6f}".format(
            epoch, nb_epochs, cost.item() 
        ))
  • # samples = m (number of samples)
    # classes = 3
    dim = 4 (입력 벡터)
  • 하나의 linear layer 만 (4에서 3으로 가는 linear layer)
  • cost.item() : loss 값 -> 점점 떨어지는 것 확인
# F.cross_entropy

# 모델 초기화
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번 마다 log 출력
    if epoch % 100 == 0:
        print("Epoch {:4d}/{} Cost: {:.6f}".format(
            epoch, nb_epochs, cost.item()
        ))
  • one hot vector 만드는 과정 생략
  • optimizer.step() : gradient descent 를 통해서 cross entropy 함수(=loss)를 minimize 함
    = 확률 분포 P 에 근사

 

- Higher Implementation with 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)
  • linear layer : 4개의 input을 받아서 3개의 class에 대한 확률값
  • forward (=진행) : linear layer 통과
  • |x| = (m, 4) : m = 샘플 숫자
    -> return 값 (linear layer 통과한 후 값) : (m, 3)
# 모델 초기화
W = torch.zeros((4, 3), requires_grad=True)
b = torch.zeros(1, requires_grad=True)

# 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()
    
    # 100번 마다 log 출력
    if epoch % 100 == 0:
        print("Epoch {:4d}/{} Cost: {:.6f}".format(
            epoch, nb_epochs, cost.item()
        ))
  • prediction : model 을 통과시킴
    -> |x_train| = (m, 4)
         |prediction| = (m, 3)
  • y_train : index 값 (아직 one hot vector X)
    |y_train| = (m, )
  • cost 는 scalar 값
  • model.parameters() 에 gradient descent 채워짐
  • Logistic Regression 과 매우 유사 (0 / 1  두 가지 class 만 있는 discrete classification)
  • Softmax Classification 은 multinomial 확률 분포  
  • binary 할 때는 logistic 함수를 쓰고 = BCE(binary cross entropy) loss + sigmoid
  • 여러 개 class 가 있다면  = CE + softmax