1. 단항 논리 회귀
* 분류를 할 때 사용하며 선형 회귀 공식으로부터 나왔기 때문에 논리 회귀라는 이름이 붙여짐
* Sigmoid 함수(Logistic 함수)를 사용하여 정확도를 높임
Sigmoid 함수 |
예측값을 0에서 1사이 값이 되도록 만듬 0에서 1사이의 연속된 값을 출력으로 하기 때문에 보통 0.5(임계값)를 기준으로 구분 |
- import
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
|
- 시드값 고정
torch.manual_seed(2024)
|
- PyTorch 텐서를 사용하여
훈련 데이터를 정의하고 그 형태를 출력
x_train = torch.FloatTensor([[0], [1], [3], [5], [8], [11], [15], [20]])
y_train = torch.FloatTensor([[0], [0], [0], [0], [0], [1], [1], [1]])
print(x_train.shape)
print(y_train.shape)
|
torch.Size([8, 1]) torch.Size([8, 1]) |
x_train = torch.FloatTensor([[0], [1], [3], [5], [8], [11], [15], [20]]):
|
- x_train과 y_train 데이터를 산점도로 시각화
plt.figure(figsize=(8, 5))
plt.scatter(x_train, y_train)
|
|
- 모델 만들기
model = nn.Sequential(
nn.Linear(1, 1),
nn.Sigmoid()
)
print(model)
|
Sequential( (0): Linear(in_features=1, out_features=1, bias=True) (1): Sigmoid() ) |
- 현재 모델의 파라미터들을 리스트로 출력
: 모델의 각 층에 있는 가중치(weight)와 편향(bias)을 볼 수 있음
: 각 파라미터는 텐서(Tensor) 객체로 표시
print(list(model.parameters())) # W: 0.0634, b:6625
|
[Parameter containing: tensor([[0.0634]], requires_grad=True), Parameter containing: tensor([0.6625], requires_grad=True)] |
손실함수 Binary Cross Entropy (BCE) |
Binary Cross Entropy (BCE)는 이진 분류 문제에서 자주 사용되는 손실 함수 논리 회귀에서는 nn.BCELoss() 함수를 사용하여 Loss를 계산 모델이 이진 분류 작업에서 예측한 확률과 실제 레이블 간의 차이를 측정 |
- 훈련 데이터 x_train에 대해 예측을 수행하고,
그 결과를 y_pred 변수에 저장
y_pred = model(x_train)
y_pred
|
tensor([[0.6598], [0.6739], [0.7012], [0.7270], [0.7631], [0.7958], [0.8340], [0.8734]], grad_fn=<SigmoidBackward0>) |
- 예측값 y_pred와 실제 타겟값 y_train 사이의
Binary Cross-Entropy (BCE) 손실을 계산
loss = nn.BCELoss()(y_pred, y_train) loss
|
tensor([[0.6598], [0.6739], [0.7012], [0.7270], [0.7631], [0.7958], [0.8340], [0.8734]], grad_fn=<SigmoidBackward0>) |
- PyTorch를 사용하여 선형 모델을 학습시키는 과정
모델은 이진 교차 엔트로피 손실 함수(nn.BCELoss)를 사용하여 최적화
확률적 경사 하강법(SGD)을 통해 가중치가 업데이트
# 학습
optimizer = optim.SGD(model.parameters(), lr=0.01)
epochs = 1000
for epoch in range(epochs + 1):
y_pred = model(x_train)
loss = nn.BCELoss()(y_pred, y_train)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch % 100 == 0:
print(f'Epoch {epoch}/{epoch} Loss: {loss:.6f}')
|
Epoch 0/0 Loss: 0.836359 Epoch 100/100 Loss: 0.725281 Epoch 200/200 Loss: 0.645149 Epoch 300/300 Loss: 0.579139 Epoch 400/400 Loss: 0.524739 Epoch 500/500 Loss: 0.479715 Epoch 600/600 Loss: 0.442194 Epoch 700/700 Loss: 0.410667 Epoch 800/800 Loss: 0.383936 Epoch 900/900 Loss: 0.361067 Epoch 1000/1000 Loss: 0.341329 |
|
- 주석
더보기
# Optimizer 정의: SGD (Stochastic Gradient Descent)를 사용하여
모델의 파라미터를 업데이트하는 optimizer를 정의 optimizer = optim.SGD(model.parameters(), lr=0.01)
# 전체 데이터셋을 몇 번 반복할지 결정하는 변수
epochs = 1000
# 학습 반복문
for epoch in range(epochs + 1):
# 현재 모델을 사용하여 입력 데이터 x_train에 대한 예측값 y_pred를 계산합니다.
y_pred = model(x_train)
# 이진 교차 엔트로피 손실 함수를 사용하여 예측값 y_pred와 실제 타겟값 y_train 간의 손실을 계산합니다.
loss = nn.BCELoss()(y_pred, y_train)
# optimizer의 gradient를 초기화합니다. (각 배치나 에폭이 시작될 때 사용됩니다.)
optimizer.zero_grad()
# 손실에 대한 역전파를 수행하여 모델의 파라미터에 대한 gradient를 계산합니다.
loss.backward()
# optimizer를 사용하여 계산된 gradient를 기반으로 모델의 파라미터를 업데이트합니다.
optimizer.step()
# 매 100 에폭마다 현재 에폭 번호와 손실을 출력합니다.
if epoch % 100 == 0:
print(f'Epoch {epoch}/{epochs} Loss: {loss:.6f}')
|
- 현재 모델의 파라미터들을 리스트로 출력
: 모델의 각 층에 있는 가중치(weight)와 편향(bias)을 볼 수 있음
: 각 파라미터는 텐서(Tensor) 객체로 표시
print(list(model.parameters())) # W: 0.2019, b: -1.4572
|
[Parameter containing: tensor([[0.2019]], requires_grad=True), Parameter containing: tensor([-1.4572], requires_grad=True)] |
- 학습된 모델을 사용하여 입력 데이터 x_test에 대해 예측값 y_pred를 출력
x_test = torch.FloatTensor([[12]])
y_pred = model(x_test)
print(y_pred)
|
tensor([[0.7243]], grad_fn=<SigmoidBackward0>) |
|
- 예측된 출력 y_pred를 기반으로 임계치(threshold)를 설정하여 이진 분류 결과를 만드는 과정
# 임계치 설정하기
# 0.5보다 크거나 같으면 1
# 0.5보다 작으면 0
y_bool = (y_pred >= 0.5).float()
print(y_bool)
|
tensor([[0.], [1.]]) |
|
2. 다항 논리 회귀 (Multinomial Logistic Regression)
- 종속 변수가 세 개 이상의 클래스 중 하나에 속할 때 사용됩니다.
- 다항 논리 회귀는 소프트맥스 함수와 크로스 엔트로피 손실 함수를 사용하여 클래스 확률을 예측합니다.
- 훈련 데이터를 준비: 입력 데이터 x_train과 레이블 데이터 y_train을
- FloatTensor와 LongTensor로 변환
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 = [0, 0, 0, 1, 1, 1, 2, 2]
x_train = torch.FloatTensor(x_train)
y_train = torch.LongTensor(y_train)
print(x_train.shape)
print(y_train.shape)
|
torch.Size([8, 4]) torch.Size([8]) |
|
- 간단한 신경망 모델을 정의
model = nn.Sequential(
nn.Linear(4, 3)
)
print(model)
|
Sequential( (0): Linear(in_features=4, out_features=3, bias=True) ) |
|
- 학습된 모델을 사용하여 훈련 데이터에 대한 예측값을 계산하고 출력
y_pred = model(x_train)
print(y_pred)
|
tensor([[-0.3467, 0.0954, -0.5403], [-0.3109, -0.0908, -1.3992], [-0.1401, 0.1226, -1.3379], [-0.4850, 0.0565, -2.1343], [-4.1847, 1.6323, -0.7154], [-2.4318, 1.2809, -0.8234], [-4.2877, 1.7462, -0.8999], [-5.1520, 2.1004, -0.9593]], grad_fn=<AddmmBackward0>) |
교차 엔트로피 손실 함수 CrossEntropyLoss |
* 교차 엔트로피 손실 함수는 PyTorch에서 제공하는 손실 함수 중 하나로 다중 클래스 분류 문제에서 사용 * 소프트맥스 함수와 교차 엔트로피 손실 함수를 결합한 형태 * 소프트맥스 함수를 적용 각 클래스에 대한 확률 분포를 얻음 * 각 클래스에 대한 로그 확률을 계산 * 실제 라벨과 예측 확률의 로그 값 간의 차이를 계산 * 계산되 차이의 평균을 계산해서 최종 손실값을 얻음 |
SoftMax |
* 다중 클래스 분류 문제에서 사용되는 함수로 주어진 입력 벡터의 값을 확률 분포로 변환 * 각 클래스에 속한 확률을 계산할 수 있으며 0과 1 사이의 값으로 변환하여 이 값들의 합은 항상 1이 되도록 함 * 주로 다중 클래스 분류 문제에서 출력층에서 사용되는 활성화 함수입니다. * 각 클래스에 대한 확률 분포를 생성하는 함수로, 각 클래스에 대한 확률 값을 반환 일반적으로 모델의 출력을 확률 값으로 해석할 수 있도록 만들어줌 * 각 입력 값에 대해 지수함수를 적용 * 지수 함수를 적용한 모든 값의 합을 계산한 후, 각 지수 합으로 나누어 정규화를 함 * 정규화를 통해 각 값은 0과 1사이의 확률 값으로 출력 |
- 다항 논리 회귀 모델의 손실을 계산하는 과정
다항 논리 회귀에서는 이진 분류 문제와 달리 BCELoss 대신 CrossEntropyLoss를 사용합니다.
CrossEntropyLoss는 소프트맥스 함수가 포함된 손실 함수입니다.
# 다항 논리 회귀에서는 BCELoss() 대신 CrossEntropyLoss()를 사용 # 소프트맥스 함수가 포함
loss = nn.CrossEntropyLoss()(y_pred, y_train)
print(loss)
|
tensor(1.2130, grad_fn=<NllLossBackward0>) |
- 다중 클래스 분류 문제를 해결하기 위해 다항 논리 회귀 모델을 학습하는 과정
여기에서는 손실 함수로 교차 엔트로피 손실(CrossEntropyLoss)을 사용
확률적 경사 하강법(SGD) 최적화 기법을 사용하여 모델의 파라미터를 업데이트
optimizer = optim.SGD(model.parameters(), lr=0.1)
# 학습
epochs = 1000
for epoch in range(epochs + 1):
y_pred = model(x_train)
loss = nn.CrossEntropyLoss()(y_pred, y_train)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch % 100 == 0:
print(f'Epoch {epoch}/{epoch} Loss: {loss:.6f}')
|
|
Epoch: 0/1000 Loss: 1.212971 Epoch: 100/1000 Loss: 0.629261 Epoch: 200/1000 Loss: 0.556415 Epoch: 300/1000 Loss: 0.505015 Epoch: 400/1000 Loss: 0.462015 Epoch: 500/1000 Loss: 0.423341 Epoch: 600/1000 Loss: 0.386988 Epoch: 700/1000 Loss: 0.351581 Epoch: 800/1000 Loss: 0.316010 Epoch: 900/1000 Loss: 0.279698 Epoch: 1000/1000 Loss: 0.247014 |
- 학습된 모델을 사용하여 새로운 입력 데이터에 대한 예측값을 출력
x_test = torch.FloatTensor([[1, 7, 8, 7]])
y_pred = model(x_test)
print(y_pred)
|
tensor([[-10.2333, 0.3633, 5.1844]], grad_fn=<AddmmBackward0>) |
|
- 모델의 예측값에 소프트맥스(softmax) 함수를 적용하여 각 클래스에 대한 확률을 구하기
# 예측값과 확률 구하기
y_prob = nn.Softmax(1)(y_pred)
y_prob
|
tensor([[1.9985e-07, 7.9936e-03, 9.9201e-01]], grad_fn=<SoftmaxBackward0>) |
- 각 클래스에 대한 확률을 출력
print(f'0일 확률: {y_prob[0][0]:.2f}')
print(f'1일 확률: {y_prob[0][1]:.2f}')
print(f'2일 확률: {y_prob[0][2]:.2f}')
|
0일 확률: 0.00 1일 확률: 0.01 2일 확률: 0.99 |
- 모델이 예측한 클래스 : 가장 큰 값의 인덱스를 찾기기
torch.argmax(y_prob, axis=1)
|
tensor([2]) |
3. 경사하강법
경사하강법
|
1. 배치 경사 하강법 |
* 가장 기본적인 경사 하강법( Vanilla Gradient Descent) * 데이터셋 전체를 고려하여 손실함수를 계산 * 한번의 Epoch에 모든 파라미터 업데이트를 단 한번만 수행 * Batch의 개수와 lteration은 1이고 Batch size는 전체 데이터의 개수 * 파라미터 업데이트할 때 한번에 전체 데이터셋을 고려하기 때문에 모델 학습 시 많은 시간과 메모리가 필요하다는 단점 |
2. 확률적 경사 하강법 |
* 확률적 경사 하강법은 배치 경사 하강법이 모델 학습 시 많은 시간과 메모리가 필요하다는 단점을 개선하기위해 제안된 기법 * Batch size를 1로 설정하여 파라미터를 업데이트 하기 때문에 배치 경사 하강법보다 훨씬 빠르고 적은 메모리로 학습이 진행 * 파라미터 값의 업데이트 폭이 불안정하기 때문에 정확도가 낮은 경우가 생길 수 있음 |
3. 미니 배치 경사 하강법 |
* 미니 배치 경사 하강법(Mini-Batch Gradient Descent)은 Batch Size가 1도 전체 데이터 개수도 아닌 경우 * 배치 경사 하강법보다 모델 학습 속도가 빠르고, 확률적 경사 하강법보다 안정적인 장점이 있음 * 딥러닝 분야에서 가장 많이 활용되는 경사 하강법 * 일반적으로 Batch Size를 32,64,128과 같아 2의 n제곱에 해당하는 값으로 사용하는게 보편적 |
경사하강법
|
1. 확률적 경사 하강법 (SGD) |
* 매개변수 값을 조정 시 전체 데이터가 아니라 랜덤으로 선택한 하나의 데이터에 대해서만 계산하는 방법 |
2. 모멘텀 (Momentum) |
* 관성이라는 물리학의 법칙을 응용한 방법 * 경사 하강법에 관성을 더 해줌 * 접선의 기울기에 한 시점 이전의 접선의 기울기값을 일정한 비율만큼 반영 * 언덕에서 공이 내려올 때 중간의 작은 웅덩이에 빠지더라도 관성의 힘으로 넘어서는 효과를 줄 수 있음 |
3. 아다그라드(Adagrad) |
* 모든 매개변수에 동일한 학습률(learning rate)을 적용하는 것은 비효율적이라는 생각에서 만들어진 학습 방법 * 처음에는 크게 학습하다가 조금씩 작게 학습시킴 |
4. 아담(Adam) |
* 모멘텀 + 아다그라드 |
5. AdamW |
Adam이 일부 약점(가중치 감쇠)과 성능향상을 위해 고안 |
4. 와인 데이터
와인 품종 예측해보기
* sklearn.datasets.load_wine 데이터셋은 이탈리아의 같은 지역에서 재배된 세가지 다른 품종으로 만든 와인을 화학적으로 분석한 결과
* 13개의 성분을 분석하여 어떤 와인인지 맞춰보자
* 단, 트레이닝 데이터를 80%, 테스트 데이터를 20%로 하여 사용
* 테스트 데이터의 0번 인덱스가 어떤 와인인지 출력하고 테스트 정확도도 출력
- 와인 데이터셋을 불러와서 x_data와 y_data로 분리
from sklearn.datasets import load_wine
x_data, y_data = load_wine(return_X_y=True, as_frame=True)
x_data
y_data
|
|
- pandas의 DataFrame 형식으로 불러온 와인 데이터셋을
PyTorch의 텐서로 변환
x_data = torch.FloatTensor(x_data.values)
y_data = torch.LongTensor(y_data.values)
print(x_data.shape)
print(y_data.shape)
|
torch.Size([178, 13]) torch.Size([178]) |
- 와인 데이터셋을 훈련 세트와 테스트 세트로 분할
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=0.2, random_state=2024)
print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)
|
torch.Size([142, 13]) torch.Size([142]) torch.Size([36, 13]) torch.Size([36]) |
|
- 신경망 모델을 훈련시키고 평가
# 학습
model = nn.Sequential(
nn.Linear(13, 3)
)
optimizer = optim.Adam(model.parameters(), lr=0.01)
epochs = 1000
for epoch in range(epochs + 1):
y_pred = model(x_train)
loss = nn.CrossEntropyLoss()(y_pred, y_train)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch % 100 == 0:
y_prob = nn.Softmax(1)(y_pred)
y_pred_index = torch.argmax(y_prob, axis=1)
y_train_index = y_train
accuracy = (y_train_index == y_pred_index).float().sum() / len(y_train) * 100
print(f'Epoch {epoch:4d}/{epochs} Loss:{loss: .6f} Accuracy: {accuracy: .2f}%')
|
Epoch 0/1000 Loss: 64.182411 Accuracy: 40.85% Epoch 100/1000 Loss: 0.682099 Accuracy: 82.39% Epoch 200/1000 Loss: 0.385995 Accuracy: 88.73% Epoch 300/1000 Loss: 0.213528 Accuracy: 93.66% Epoch 400/1000 Loss: 0.130488 Accuracy: 94.37% Epoch 500/1000 Loss: 0.109236 Accuracy: 97.89% Epoch 600/1000 Loss: 0.098798 Accuracy: 98.59% Epoch 700/1000 Loss: 0.090906 Accuracy: 98.59% Epoch 800/1000 Loss: 0.084330 Accuracy: 98.59% Epoch 900/1000 Loss: 0.078634 Accuracy: 98.59% Epoch 1000/1000 Loss: 0.073584 Accuracy: 98.59% |
|
- 테스트 데이터(x_test)를 사용하여 모델(model)이 예측한 결과(y_pred)
그 결과를 소프트맥스 함수(nn.Softmax(1))를 통해 확률로 변환한 값(y_prob)을 출력
y_pred = model(x_test)
y_pred[:5]
y_prob = nn.Softmax(1)(y_pred)
y_prob[:5]
|
tensor([[46.2340, 44.3432, 52.4995], [82.5654, 75.1822, 73.2878], [34.3048, 39.0224, 40.0497], [84.3432, 78.8066, 78.6860], [60.5308, 58.4503, 54.4495]], grad_fn=<SliceBackward0>) ----------------------------------------------------------------------------------------- tensor([[1.8966e-03, 2.8630e-04, 9.9782e-01], [9.9929e-01, 6.2117e-04, 9.3427e-05], [2.3503e-03, 2.6299e-01, 7.3466e-01], [9.9262e-01, 3.9111e-03, 3.4667e-03], [8.8719e-01, 1.1079e-01, 2.0275e-03]], grad_fn=<SliceBackward0>) |
- 각 클래스에 대한 확률을 출력
print(f'0번 품종일 확률:{y_prob[0][0]:.2f}')
print(f'1번 품종일 확률:{y_prob[0][1]:.2f}')
print(f'2번 품종일 확률:{y_prob[0][2]:.2f}')
|
0번 품종일 확률: 0.00 1번 품종일 확률: 0.00 2번 품종일 확률: 1.00 |
- 테스트 세트를 사용하여 모델의 정확도를 계산하고 출력하는 과정
y_pred_index = torch.argmax(y_prob,axis=1)
accuracy = (y_test == y_pred_index).float().sum()/len(y_test)*100
print(f'테스트 정확도는 {accuracy:.2f}% 입니다!')
|
테스트 정확도는 97.22% 입니다! |
|
'AI > 딥러닝' 카테고리의 다른 글
06. 비선형 활성화 함수 (0) | 2024.06.20 |
---|---|
05. 딥러닝 (0) | 2024.06.20 |
04. 데이터로더(Data Loader) (0) | 2024.06.20 |
02. 선형 회귀(단항, 다중) | 경사하강법 (0) | 2024.06.18 |
01. 파이토치(Pytorch) (0) | 2024.06.18 |