ROKEY/Machine Learning

[CV] 인공지능 개론(3) 목적함수(Objective Function), 손실함수(Loss Function), 비용함수(Cost Function)

dvl.hyeon_ 2025. 2. 26. 21:00
반응형

목적함수(Objective Function)

 

▪️가장 큰 범위의 개념으로, 모델을 학습하면서 최적화(최소/최대)하고 싶어하는 함수

손실함수(Loss Function)

 

▪️개별 샘플(데이터 한 개)에 대한 오차를 계산하는 함수

▪️모델이 예측한 값과 실제 값 사이의 차이를 측정

  • AE (Absolute Error)
  • SE (Squared Error)
  • RSE (Root Squared Error)

비용함수(Cost Function)

 

▪️손실 함수의 평균값을 계산하여 전체 모델 성능을 평가하는 함수

▪️비용 함수는 전체 데이터셋을 기준으로 함

  • MSE (Mean Squared Error)
  • MAE (Mean Absolute Error)
  • BCE (Binary Cross Entropy)

✅ 선형 회귀(Linear Regression) 손실 함수

더보기

💡선형 회귀

▪️연속적인 숫자값을 예측하는 회귀 모델

▪️입력 변수(특징, $X$)와 출력 변수(타겟, $Y$) 사이의 관계를 선형 함수로 모델링하는 것

$$Y=wX+b$$

 

💡학습 과정

▪️손실 함수로 평균 제곱 오차(MSE) 사용

▪️경사 하강법 등을 이용하여 가중치 $w$와 절편 $b$를 최적화

1️⃣Linear Hidden Layers 정의

import torch

def linear(x, w, b):
  y = torch.matmul(x, w) + b
  return y

x = torch.FloatTensor([[1, 2, 3], [2, 4, 6]])

# Hidden Layer 1
w1 = torch.rand(3, 4)
b1 = torch.ones(1, 4)
h1 = linear(x, w1, b1)

# Hidden Layer 2
w2 = torch.rand(4, 3)
b2 = torch.ones(1, 3)
h2 = linear(h1, w2, b2)

 

2️⃣회귀 분석(single data)

input = torch.randn(1, requires_grad=True)
target = torch.randn(1)

loss = nn.MSELoss() # class
optimizer = optim.SGD([input], lr=0.02)

for epoch in range(100):
  loss_result = loss(input, target)
  optimizer.zero_grad() # 기울기 초기화: 이전 step에서 쌓아놓은 파라미터 변화량(기울기)을 0으로 초기화
  loss_result.backward() # 기울기 계산: 그래프 노드에 대한 현재 텐서의 기울기 계산
  optimizer.step() # 기울기 업데이트: optimizer에게 손실함수를 최소화할 수 있도록 파라미터 위탁

 

3️⃣회귀 분석(batch data)

x = torch.arange(-3, 3, 0.1)
y = 2*x + 1
y1 = 2*x + 1 + torch.randn(x.shape)

a = torch.randn(1, requires_grad=True)
b = torch.randn(1, requires_grad=True)

lr = 0.01
loss = nn.MSELoss()
optimizer = optim.SGD([a, b], lr=lr)

n_epochs = 100

for epoch in range(n_epochs):
  y_hat = a*x + b
  loss_result = loss(y_hat, y1)
  if epoch % 10 == 0:
    print("epoch: ", epoch, "a: ", a.item(), "b: ",
            b.item(), "loss: ", loss_result.item())
  optimizer.zero_grad()
  loss.backward()
  optimizer.step()

 

✅예측 함수의 내부 구조

1️⃣레이어 함수 정의

l1 = nn.Linear(784, 128) # 첫 번째 선형 함수
l2 = nn.Linear(128, 10)  # 두 번째 선형 함수

relu = nn.ReLU(inplace=True) # 활성화 함수: 비선형성 추가 역할

2️⃣입력 텐서로부터 출력 텐서 계산

inputs = torch.randn(100, 784)

m1 = l1(inputs)
m2 = relu(m1)
outputs = l2(m2)

3️⃣위의 과정을 합성 함수로 정의

net = nn.Sequential(
      l1, 
      relu, 
      l2
)

output = net(inputs)

✅이진 분류(Binary Classification) 비용 함수

더보기

💡이진 분류

▪️두 개의 클래스로 데이터를 분류하는 지도 학습 문제

 

👉🏻로지스틱 회귀(Logistic Regression)

▪️선형 회귀에 시그모이드(sigmoid)를 적용하여 출력 범위를 [0,1]로 제한

$$P(Y=1∣ X)=σ(wX+b)={1 \over 1+e^{−wX−b}}$$

 

💡학습 과정

▪️손실 함수로 이진 크로스 엔트로피(Binary Cross Entropy) 사용

▪️확률값이 0.5 이상이면 클래스 1, 0.5 미만이면 클래스 0으로 예측

▪️경사 하강법으로 최적화

1️⃣시그모이드(sigmoid) 함수

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)

fig, axes = plt.subplots(figsize=(4, 4))
plt.plot(x, y)
plt.grid(linestyle = ":")
plt.hlines(0.5, -5, 5, colors="r", linestyles="dashed")
plt.show()

x = np.arange(-5.0, 5.0, 0.1)
y1 = sigmoid(0.5*x)
y2 = sigmoid(x)
y3 = sigmoid(2*x)

fig, axes = plt.subplots(figsize=(4, 4))
plt.plot(x, y1, 'r', linestyle='--') # W의 값이 0.5일때
plt.plot(x, y2, 'g') # W의 값이 1일때
plt.plot(x, y3, 'b', linestyle='--') # W의 값이 2일때
plt.plot([0,0],[1.0,0.0], ':') # 가운데 점선 추가
plt.title('Sigmoid Function')
plt.show()

2️⃣이진 분류 함수의 손실 함수와 비용 함수

x_data = [[1, 2], [2, 3], [3, 1], [4, 3], [5, 3], [6, 2]]
y_data = [[0], [0], [0], [1], [1], [1]]
x_train = torch.FloatTensor(x_data)
y_train = torch.FloatTensor(y_data)

# 파라미터 초기화
W = torch.zeros((2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)

# 시그모이드 함수
def prob(x, w, b):
  y = 1/(1+torch.exp(-(torch.matmul(x,w) + b)))
  return y
  
prob = prob(x_train, W, b)

# 비용 함수 = 모든 데이터 샘플의 손실함수 평균
# 손실함수: −(ylog(y_hat)+(1−y)log(1−(y_hat)))
losses = -(y_train * torch.log(prob) + (1-y_train) * torch.log(1-prob))
cost = losses.mean()

✅ 다중 분류(Multinomial Classification) 비용 함수

더보기

💡다중 분류

▪️세 개 이상의 클래스로 데이터를 분류하는 지도 학습 문제

 

👉🏻소프트맥스 회귀(Softmax Regression)

▪️출력값이 여러 개(class 개수)이며, 각 클래스에 속할 확률을 구하는 방식

▪️소프트맥스 함수 활용

▪️$$P(Y=k∣X)= {e^{z_k} \over \sum\limits_{j=1}^C e^{z_j}}$$

  • $C$: 클래스 개수
  • $z_k$: 클래스 $k$에 대한 점수(가중합)

💡학습 과정

▪️손실 함수로 다중 클래스 크로스 엔트로피(Categorical Cross Entropy) 사용

▪️출력값 중 가장 높은 확률을 가진 클래스를 예측값으로 선택

1️⃣소프트맥스 함수

def softmax(x):
  y = np.exp(x)/np.sum(np.exp(x))
  return y
  
 x = torch.rand(1, 4)
 
 prob = softmax(x.numpy())

 

2️⃣소프트맥스를 이용한 비용함수 구현

# 출력값(원-핫 벡터) 만들기
x = torch.rand(3, 5, requires_grad=True)
prob = F.softmax(x, dim=1)

# 정답값
y = torch.randint(5, (3,))

#
y_one_hot = torch.zeros_like(x)
y_one_hot.scatter_(dim = 1, index = y.unsqueeze(dim = 1), value = 1)

# 비용함수
(-y_one_hot * torch.log(F.softmax(x, dim=1))).sum(dim=1).mean()

# 방법1
torch.log(F.softmax(x, dim=1))

# 방법2
F.log_softmax(x, dim=1)

# 비용함수
# (1) 크로스 엔트로피 손실 함수 수식
(y_one_hot * -F.log_softmax(x, dim=1)).sum(dim=1).mean()

# (2) Negative Log Likelihood Loss(NLL Loss) 
# 정답 레이블에 해당하는 손실만 선택하여 평균을 계산하는 함수
F.nll_loss(F.log_softmax(x, dim=1), y)

# (3) 소프트맥스 + 크로스 엔트로피
# 내부적으로 log_softmax() 포함
F.cross_entropy(x, y)

 

3️⃣실제 예시

# 임의 학습 데이터 초기화
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)

y_one_hot = torch.zeros(8, 3)
y_one_hot.scatter_(1, y_train.unsqueeze(1), 1)

# 모델 초기화
W = torch.zeros((4, 3), requires_grad=True)
b = torch.zeros((1, 3), requires_grad=True)

# 옵티마이저 설정
lr = 0.1
optimizer = optim.SGD([W, b], lr=lr)

nb_epochs = 100
for epoch in range(nb_epochs + 1):
    # H(x) 계산
    hypothesis = F.softmax(x_train.matmul(W) + b, dim=1)
    cost = (y_one_hot * -torch.log(hypothesis)).sum(dim=1).mean()

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

    if epoch % 10 == 0:
        print('Epoch {:4d}/{} Cost: {:.6f}'.format(
            epoch, nb_epochs, cost.item()
        ))
반응형