sigmoid and softmax

Published

January 19, 2023

Sigmoid

import numpy as np
def sigmoid(x): return 1/(1+np.exp(-x))
import matplotlib.pyplot as plt
def plot_function(f, tx=None, ty=None, title=None, min=-2, max=2, figsize=(6,4)):
    x = torch.linspace(min,max, 100)
    fig,ax = plt.subplots(figsize=figsize)
    ax.plot(x,f(x))
    if tx is not None: ax.set_xlabel(tx)
    if ty is not None: ax.set_ylabel(ty)
    if title is not None: ax.set_title(title)
import torch
plot_function(torch.sigmoid, min=-4, max=4)

Softmax

범주가 3개 이상인 타깃에 대해 손실을 계산하기 위해서는 어떡해야 할까요?

우선은 이진 분류에서 사용할 수 있었던 출력층 함수 시그모이드를 다른 함수로 대체해야 합니다.

가상의 이미지 여섯장에 대한 활성 두 개가 존재한다고 가정하겠습니다.

사실, 두 범주 중 하나를 선택하는 일에는 하나의 활성만 존재해도 괜찮습니다. 특정 threshold(주로 0.5)를 기준으로 보다 낮은 값은 0, 높은 값은 1로 치환할 수 있습니다.

다만 여기서는 설명을 위해 2개의 활성으로 나타냈습니다.

acts = torch.rand((6,2))*2
acts
tensor([[0.1107, 1.0475],
        [1.9075, 1.7639],
        [0.8810, 0.9155],
        [0.0714, 0.4593],
        [1.1286, 0.0719],
        [1.0509, 1.0387]])

위에서 알아본 torch.sigmoid 함수를 통해 0에서 1사이의 값으로 만들 수 있습니다.

acts.sigmoid()
tensor([[0.5276, 0.7403],
        [0.8707, 0.8537],
        [0.7070, 0.7141],
        [0.5178, 0.6129],
        [0.7556, 0.5180],
        [0.7409, 0.7386]])

다만 두 활성의 합이 1이 아니기 때문에, 함수는 작동하나 의미적으로 무용한 변환이 되었습니다.

실제적으로 시그모이드 함수를 사용하기 위해서는, 두 활성 간의 차이를 시그모이드 변환 하는 것이 합리적입니다.

(acts[:,0]-acts[:,1]).sigmoid()
tensor([0.2815, 0.5358, 0.4914, 0.4042, 0.7421, 0.5031])

위 변환의 의미는 0번 째 열의 레이블임을 확신하는 정도와 1번 째 열의 레이블임을 확신하는 정도의 차이라고 말할 수 있습니다.

소프트맥스 함수는 다중 레이블에서 정확히 위와 같은 작업을 수행합니다.

def softmax(x): return exp(x) /exp(x).sum(dim=1, keepdim=True)
plot_function(np.exp, min=-4, max=4)

지수함수(exp, np.exp)는 모든 x값에 대응하는 양수 y값을 가집니다.

지수변환된 값 중에서 가장 큰 변환값을 가지는 레이블로 선택합니다.

sm_acts = torch.softmax(acts, dim=1) # 열 기준 torch.softmax

레이블이 3개인 경우를 가정하고, 이때 손실은 어떻게 계산하는지 알아보겠습니다.

acts = torch.rand((6,3))*2
acts
tensor([[1.9065, 0.6177, 1.7182],
        [0.8220, 1.4084, 0.5280],
        [0.6282, 0.6837, 1.5685],
        [1.3450, 0.9478, 0.8547],
        [1.2426, 0.9204, 1.4648],
        [1.1413, 1.3553, 1.3754]])

실제 타겟값은 아래와 같다고 해보겠습니다.

targ = torch.tensor([2,1,2,0,0,1])
sm_acts = torch.softmax(acts, dim=1)
sm_acts
tensor([[0.4753, 0.1310, 0.3937],
        [0.2823, 0.5074, 0.2104],
        [0.2165, 0.2289, 0.5545],
        [0.4377, 0.2942, 0.2681],
        [0.3363, 0.2437, 0.4200],
        [0.2855, 0.3536, 0.3608]])
idx = range(6)
sm_acts[idx, targ]
tensor([0.3937, 0.5074, 0.5545, 0.4377, 0.3363, 0.3536])
-sm_acts[idx, targ]
tensor([-0.3937, -0.5074, -0.5545, -0.4377, -0.3363, -0.3536])
import torch.nn.functional as F
F.nll_loss(sm_acts, targ, reduction='none')
tensor([-0.3937, -0.5074, -0.5545, -0.4377, -0.3363, -0.3536])
F.nll_loss(sm_acts, targ, reduction='sum')
tensor(-2.5833)

만약 예측이 targ과 반대 방향으로 많이 빗나간다면 어떤 값이 나올까요?

targ = torch.tensor([1,2,0,2,1,0])
F.nll_loss(sm_acts, targ, reduction='none')
tensor([-0.1310, -0.2104, -0.2165, -0.2681, -0.2437, -0.2855])
F.nll_loss(sm_acts, targ, reduction='sum')
tensor(-1.3551)

예측이 타겟과 잘 맞는 경우 -2.58 정도였으나, 빗겨나간 경우 -1.35 정도로 손실값이 커졌습니다.