이번편은 소프트맥스 회귀에 대한 내용을 직접 코드로 구현해보았다. 코드는 위키독스의 'Pytorch로 시작하는 딥러닝'을 참고하였다.
(이전 포스팅 : 2021.08.04 - [파이썬/Pytorch] 딥러닝 - Softmax Regression 이해를 위한 정리 1편)
1. import
<코드>
import torch
import torch.nn as nn #nn. module 사용
import torch.nn.functional as F #softmax 함수 사용
import torch.optim as optim #옵티마이저 사용
torch.manual_seed(1) #랜덤 시드 고정
2. 훈련 데이터 샘플
<코드>
#각 샘플은 4개의 특성을 갖고 있고 총 8개 샘플임
x_train = [[3, 3, 2, 5],
[2, 1, 3, 2],
[3, 1, 3, 4],
[2, 3, 5, 1],
[1, 7, 5, 5],
[9, 6, 3, 3],
[4, 1, 2, 3],
[1, 7, 3, 7]]
#y_train은 각 샘플에 대한 레이블(0,1,2 총 3개의 클래스 존재)
y_train = [2, 2, 2, 1, 1, 1, 0, 0]
위 훈련 데이터를 도식화하면 아래와 같다. 입력값은 총 8개 샘플이며, 샘플 당 각 4개의 특성을 가지고 있다. 실제값의 Class는 총 3개로 각각 0, 1, 2의 값을 가지고 있다. 실제 소프트맥스 회귀 결과를 확인해보면 레이블이 총 3개인(0,1,2)의 확률값이 각각 표시되는데 Pytorch상에서 레이블 숫자 순서대로 표기해준다.
3. Model 및 Optimizer
<코드>
#모델 설정(각 샘플의 input_dim = 4, output_Dim = 3)
model = nn.Linear(4,3)
# optimizer 설정
optimizer = optim.SGD(model.parameters(), lr=0.1)
-. 모델은 입력이 4, 출력이 3차원으로 정의(각 샘플 특성이 4개, 출력 레이블이 3개인 것을 기억하자)
-. 옵티마이저는 model.parameters( )를 가중치 w와 편향 b로 설정하고 학습률을 0.1로 설정
-. model.parameters( )를 리스트로 변환하여 출력해보면 아래와 같다.
<코드>
print('#1')
print(list(model.parameters()))
<결과>
#1
[Parameter containing:
tensor([[ 0.2576, -0.2207, -0.0969, 0.2347],
[-0.4707, 0.2999, -0.1029, 0.2544],
[ 0.0695, -0.0612, 0.1387, 0.0247]], requires_grad=True), Parameter containing:
tensor([ 0.1826, -0.1949, -0.0365], requires_grad=True)]
텐서가 2개가 나오는데 각각 가중치 W와 편향 B를 의미한다. 학습을 하기 전이므로 저 값은 랜덤 시드 고정 값에 따라 발생한 값이다. 학습대상이므로 2개 텐서 모두 requires_grad는 True로 설정되어있다.
4. Epoch 설정 및 학습
<코드>
#학습횟수 Epoch = 1000
for epoch in range(1001):
# 예측값 계산
prediction = model(x_train)
# cost 계산(cross_entorpy 함수는 원핫벡터와 소프트맥스 함수를 포함한다)
cost = F.cross_entropy(prediction, y_train)
# cost 함수 미분 및 가중치 w, b 업데이트
optimizer.zero_grad()
cost.backward()
optimizer.step()
# 20번마다 로그 출력
if epoch % 100 == 0:
print('Epoch {:4d}/{} Cost: {:.6f}'.format(
epoch, nb_epochs, cost.item()
))
<결과1 : 학습에 따른 cost 변화>
Epoch 0/1000 Cost: 1.601916
Epoch 100/1000 Cost: 0.342889
Epoch 200/1000 Cost: 0.290131
Epoch 300/1000 Cost: 0.259507
Epoch 400/1000 Cost: 0.237403
Epoch 500/1000 Cost: 0.219854
Epoch 600/1000 Cost: 0.205167
Epoch 700/1000 Cost: 0.192478
Epoch 800/1000 Cost: 0.181289
Epoch 900/1000 Cost: 0.171291
Epoch 1000/1000 Cost: 0.162271
이전 포스팅에서도 설명했던 내용이지만 소프트맥스 회귀 과정상 실제값을 원핫 벡터 방식을 통해 인코딩하고 소프트맥스 함수 계산 과정을 거쳐 각각의 레이블에 대한 확률값을 계산하게 된다. 위 코드에는 해당 과정이 생략되어 있다. 이유는 cost(손실 비용 함수)를 구현한 cross_entropy( ) 함수가 이 과정들을 모두 포함하고 있기 때문이다.
corss_entropy( )를 사용하지 않고 원-핫 벡터 인코딩과 소프트맥스 함수를 통해 계산하는 방식은 참고 링크에 잘 명시되어있어 참고하면 좋을 것 같다. 사용자 입장에서는 cross_entropy( ) 함수 1개로 많은 것을 해결할 수 있어 유용하다.
결과는 보면 알겠지만 에포크가 반복될 수록 cost를 0으로 줄여나가는 방향으로 W와 B를 학습시키는 것을 확인할 수 있다. 가중치 W와 편향 B로 아래와 같이 출력시켜보았다. (epoch를 200마다 가중치 W와 B가 어떻게 변화하는지 출력)
<결과2 : 학습에 따른 W와 B의 변화>
가중치 W : Parameter containing:
tensor([[ 0.1440, -0.2355, -0.1538, 0.2182],
[-0.3652, 0.3329, -0.0504, 0.2137],
[ 0.0776, -0.0794, 0.1430, 0.0818]], requires_grad=True)
편향 B : Parameter containing:
tensor([ 0.1652, -0.1869, -0.0270], requires_grad=True)
가중치 W : Parameter containing:
tensor([[ 0.1968, 0.0290, -0.7259, 0.5713],
[ 0.1475, 1.1862, 0.3116, -1.3098],
[-0.4880, -1.1973, 0.3531, 1.2522]], requires_grad=True)
편향 B : Parameter containing:
tensor([ 0.2071, -0.4364, 0.1806], requires_grad=True)
가중치 W : Parameter containing:
tensor([[ 0.3610, 0.1810, -0.7812, 0.4384],
[ 0.3145, 1.5408, 0.2820, -1.6970],
[-0.8191, -1.7038, 0.4381, 1.7723]], requires_grad=True)
편향 B : Parameter containing:
tensor([ 0.2352, -0.5509, 0.2670], requires_grad=True)
가중치 W : Parameter containing:
tensor([[ 0.4884, 0.3316, -0.8095, 0.2951],
[ 0.4462, 1.7994, 0.2654, -1.9707],
[-1.0782, -2.1131, 0.4831, 2.1893]], requires_grad=True)
편향 B : Parameter containing:
tensor([ 0.2444, -0.6319, 0.3388], requires_grad=True)
가중치 W : Parameter containing:
tensor([[ 0.5998, 0.4730, -0.8373, 0.1615],
[ 0.5580, 2.0135, 0.2599, -2.1974],
[-1.3014, -2.4686, 0.5163, 2.5496]], requires_grad=True)
편향 B : Parameter containing:
tensor([ 0.2451, -0.6975, 0.4037], requires_grad=True)
가중치 W : Parameter containing:
tensor([[ 0.7005, 0.6048, -0.8661, 0.0389],
[ 0.6576, 2.1999, 0.2597, -2.3961],
[-1.5017, -2.7867, 0.5453, 2.8710]], requires_grad=True)
편향 B : Parameter containing:
tensor([ 0.2416, -0.7538, 0.4635], requires_grad=True)
5. 학습 된 모델에 테스트 데이터 입력해보기
위에 훈련데이터였던 샘플 중 [2,1,3,2]를 입력하여 실제 학습된 모델에 따라 예측값이 어떻게 되는지 확인해보았다. [2,1,3,2]는 위 훈련 데이터에 따르면 결과값이 2로 분류되어야 한다. 코드는 아래와 같이 구현해보았다.
<코드>
test_data = torch.FloatTensor([[2,1,3,2]])
predic_result = model(test_data)
print("예측값")
print(predic_result)
<결과>
예측값
tensor([[-0.2732, -1.2517, 2.0511]], grad_fn=<AddmmBackward>)
결과는 음수와 양수가 섞인 값이 나온다. 위 레이블이었던 class 순서대로 0,1,2에 대한 예측값이다. 소프트맥스 함수 결과에 따르면 각각의 레이블 확률값의 합계가 1이 되어야한다. 근데 결과가 합이 1이 아닐뿐더라 요소 중 -값도 나온다. 이유는 아래 cross_entrophy( ) 함수를 다시보자.
# cost 계산(cross_entorpy 함수는 원핫벡터와 소프트맥스 함수를 포함한다)
cost = F.cross_entropy(prediction, y_train)
cost를 계산할 때는 cross_entropy( ) 함수를 사용하므로써 소프트맥스 함수가 적용되어 학습이 진행된다. 학습은 소프맥스 함수가 적용되어 정상적으로 진행되지만 실제 위에 테스트한 데이터는 model( ) 을 통해 입력하였다. 즉, model에는 소프트맥스 함수가 적용되지 않는 것이다. 따라서, 결과를 제대로 보려면 결과 data인 predic_result를 F.softmax( ) 함수에 입력시켜줘야 한다. 아래 코드를 보며 이해해보자.
<코드>
hy = F.softmax(predic_result, dim=1)
print("예측값(소프트맥스 함수 통과)")
print(hy)
<결과>
예측값(소프트맥스 함수 통과)
tensor([[0.0862, 0.0324, 0.8813]], grad_fn=<SoftmaxBackward>)
모델 예측값을 소프트맥스 함수에 입력한 결과는 각 레이블의 확률값이 출력된다. 위 확률값에 따르면 2가 정답일 확률이 88%이다. 즉, 학습 된 모델이 [2,1,3,2]를 2로 예측했다. (0.0862 + 0.0324 + 0.08813 = 0.9999)
여기서 좀더 궁금한 부분이 생겨 정리하였다. 확률값의 합은 모두 1이어야 하는데 실제 계산해보니 0.9999가 나온다. 이유는 F.softmax( ) 함수에 따른 것이다. 위 입력값에는 음수가 있다. softmax 함수는 음수 입력이 들어오면 사실상 거의 0과 가깝게 바꿔버린다고 한다.(위 예측값과 소프트맥스 함수 결과값을 대조해서 보자.)
한가지 더 정리하자면, 소프트맥스 함수에 입력시키지 않아도 결과는 예측할 수 있다. 위 2개의 결과를 아래와 같이 비교해서 표시해보았다.
0일 확률 | 1일 확률 | 2일 확률 | |
예측값(model(test_data)) | -0.2732 | -1.2517 | 2.0511 |
예측값(F.softmax( )) | 0.0862 | 0.0324 | 0.8813 |
확률 순위 | 2 | 3 | 1 |
표를 보면 2일 확률이 그냥 예측값이나 소프트맥스 함수를 통한 예측값이 동일한 것을 확인할 수 있다.
참고링크 : 위키독스 - 'Pytorch로 시작하는 딥러닝 입문' : Softmax 회귀 편
'코딩 > Deep Learning' 카테고리의 다른 글
[파이썬/Pytorch] 딥러닝 - CNN(Convolutional Neural Network) 2편 : Padding, Pooling (0) | 2021.09.22 |
---|---|
[파이썬/Pytorch] 딥러닝- CNN(Convolutional Neural Network) 1편 (0) | 2021.09.14 |
[파이썬/Pytorch] 딥러닝 - Softmax Regression 이해를 위한 정리 (0) | 2021.08.04 |
[파이썬/Pytorch] 딥러닝 - Logistic Regression 이해를 위한 정리 (0) | 2021.08.01 |