목차
[딥러닝 파이토치 교과서]와 [인프런 '처음하는 딥러닝과 파이토치(Pytorch) 부트캠프[데이터과학 Part3]'] 를 바탕으로 공부한 내용을 정리한 글입니다.
DNN pytorch 실습
- pytorch 모델 용어 정리
- 계층(layer): 모듈 또는 모듈을 구성하는 한 개의 계층을 의미함 (예: 선형 계층, Linear Layer)
- 모듈(module): 한 개 이상의 계층이 모여 구성된 것. 모듈이 모여서 새로운 모듈 구성 가능
- 모델(model): 최종적인 네트워크. 한 개의 모듈이 모델이 될 수도 있고, 여러 개의 모듈이 하나의 모델이 될 수도 있음
- torch.nn과 nn.Module
- torch.nn 네임스페이스는 신경망을 구성하는데 필요한 모든 구성 요소 제공
- 모든 PyTorch 모듈은 nn.Module 의 하위 클래스(subclass) 임
- 모든 PyTorch 신경망 모델은 nn.Module 을 상속받은 하위 클래스로 정의함
- init 에서 신경망 계층 초기화 필요
- forward() 메서드에서 입력 데이터에 대한 연산 정의 필요
- nn.Linear 클래스
- 지금까지는 Linear Layer 와 PyTorch 기반 신경망 모듈 구현 방법을 이해하기 위해, Linear Layer 를 PyTorch 의 신경망 모듈 클래스로 구현해본 것임
- y = torch.matmul(x, self.W) + self.b 이거 자체로 하나로 만든게 nn.linear
1) 당뇨병 환자의 데이터 기반 실습
당뇨병 환자의 데이터 기반, 해당 환자의 1년 후 병의 진전된 정도를 선형 회귀 모델로 예측
import numpy as np
import pandas as pd
from sklearn.datasets import load_diabetes
diabetes_data = load_diabetes()
# 데이터 세트에 대한 전체 설명
print (diabetes_data.DESCR)
x = torch.from_numpy(np.array(diabetes_data.data[:, :-1], dtype=np.float32)) #맨마지막꺼 제외하고,
y = torch.from_numpy(np.array(diabetes_data.data[:, [-1]], dtype=np.float32))
#shape
print('shape of x is : ',x.shape)
print('shape of y is : ',y.shape)
#실행결과
.. _diabetes_dataset:
Diabetes dataset
----------------
Ten baseline variables, age, sex, body mass index, average blood
pressure, and six blood serum measurements were obtained for each of n =
442 diabetes patients, as well as the response of interest, a
quantitative measure of disease progression one year after baseline.
**Data Set Characteristics:**
:Number of Instances: 442
:Number of Attributes: First 10 columns are numeric predictive values
:Target: Column 11 is a quantitative measure of disease progression one year after baseline
:Attribute Information:
- age age in years
- sex
- bmi body mass index
- bp average blood pressure
- s1 tc, total serum cholesterol
- s2 ldl, low-density lipoproteins
- s3 hdl, high-density lipoproteins
- s4 tch, total cholesterol / HDL
- s5 ltg, possibly log of serum triglycerides level
- s6 glu, blood sugar level
Note: Each of these 10 feature variables have been mean centered and scaled by the standard deviation times the square root of `n_samples` (i.e. the sum of squares of each column totals 1).
Source URL:
<https://www4.stat.ncsu.edu/~boos/var.select/diabetes.html>
For more information see:
Bradley Efron, Trevor Hastie, Iain Johnstone and Robert Tibshirani (2004) "Least Angle Regression," Annals of Statistics (with discussion), 407-499.
(<https://web.stanford.edu/~hastie/Papers/LARS/LeastAngle_2002.pdf>)
shape of x is : torch.Size([442, 9])
shape of y is : torch.Size([442, 1])
import torch
import torch.nn as nn
import torch.nn.functional as F
class LinearRegressionModel(nn.Module):
def __init__(self, input_dim, output_dim):
super().__init__()
self.linear = nn.Linear(input_dim, output_dim)
def forward(self, x):
return self.linear(x)
model = LinearRegressionModel(x.size(1), y.size(1)) #각각 9와 1!
learning_rate = 0.01
nb_epochs = 10000
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate) #mini batch 사이즈 정하는건 원래 이거말고 별도의 코드로 지정하는거임.
for epoch in range(nb_epochs + 1):
y_pred = model(x) #여기선 그냥 x 한번에 넣음. # 예측
loss = F.mse_loss(y_pred, y)# 오차 계산
optimizer.zero_grad() #기존 iteration에 영향 미치지 않도록
loss.backward() #backward에서 편미분 값 계산하고
optimizer.step() #각각의 값 업데이트.
print(loss)
for param in model.parameters():
print (param)
#실행결과
tensor(0.0018, grad_fn=<MseLossBackward0>)
Parameter containing:
tensor([[ 0.1019, -0.1559, 0.2950, 0.2156, 0.0759, 0.0119, 0.0955, 0.2390,
-0.1119]], requires_grad=True)
Parameter containing:
tensor([6.5645e-11], requires_grad=True)
*예측값과 실제값 분포 비교
df = pd.DataFrame(torch.cat([y, y_pred], dim=1).detach().numpy(), columns=["y", "y_pred"])
#torch의 cat은 텐서끼리 합칠 수 있는 기능. dim을 0으로 하면 밑에 행으로 붙여지고, 1이니까 옆에 열로 붙여짐.
#detach는 위에 연산과 연결되어 있기 때문에 혹시몰라 떼어둠.
import plotly.graph_objects as go
import plotly.offline as pyo # jupyter notebook 에서 보여지도록 설정하는 부분 (가끔 안나올 때, 이 명령을 하면 됨)
pyo.init_notebook_mode()
fig = go.Figure()
fig.add_trace(go.Scatter(x=df.index, y=df['y'], mode='markers', name='y'))
fig.add_trace(go.Scatter(x=df.index, y=df['y_pred'], mode='markers', name='y_pred'))
fig.update_layout(yaxis_range=[-0.5,0.5])
fig.show()
→ 머신러닝에서는 eda, 전처리 많이 해야 성능이 잘 나올 수 있었는데,
딥러닝은 알아서 패턴을 찾아서 예측해주기 때문에 eda가 줄어들었다.
2) fashion mnist 데이터로 DNN 실습하기
- CPU 혹은 GPU 장치 확인, fashion_mnist 데이터셋 내려받기
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
train_dataset = torchvision.datasets.FashionMNIST("./딥러닝 파이토치 교과서", download=True, transform=
transforms.Compose([transforms.ToTensor()]))
test_dataset = torchvision.datasets.FashionMNIST("./딥러닝 파이토치 교과서", download=True, train=False, transform=
transforms.Compose([transforms.ToTensor()]))
- 분류에 사용될 클래스 정의
labels_map = {0 : 'T-Shirt', 1 : 'Trouser', 2 : 'Pullover', 3 : 'Dress', 4 : 'Coat', 5 : 'Sandal', 6 : 'Shirt',
7 : 'Sneaker', 8 : 'Bag', 9 : 'Ankle Boot'}
fig = plt.figure(figsize=(8,8));
columns = 4;
rows = 5;
for i in range(1, columns*rows +1):
img_xy = np.random.randint(len(train_dataset)); #np.random.randint()는 이산형 분포를 갖는 데이터에서 무작위 표본을 추출할 때 사용함.
img = train_dataset[img_xy][0][0,:,:] #train_dataset을 이용한 3차원 배열을 생성. train_dataset에서 [img_xy][0][0,:,:]에 해당하는 요소 값을 가져오겠다. (examp[2][0][3]처럼)
fig.add_subplot(rows, columns, i)
plt.title(labels_map[train_dataset[img_xy][1]])
plt.axis('off')
plt.imshow(img, cmap='gray')
plt.show()
np.random.randint()는 이산형 분포를 갖는 데이터에서 무작위 표본을 추출할 때 사용함. 즉, np.random.randint(len(train_dataset))은 0 ~ (train_dataset 길이) 값을 갖는 분포에서 랜덤한 숫자 한 개를 생성하라는 의미.
- np.random.rand(8) -> 0~1 사이의 정규표준분포 난수를 행렬로 (1x8) 출력
- np.random.randn(4,2) -> 평균이 0이고, 표준편차가 1인 가우시안 정규분포 난수를 행렬로 (4X2) 출력
- 심층 신경망 모델 생성
class FashionDNN(nn.Module):
def __init__(self):
super(FashionDNN, self).__init__() #--1, FashionDNN이라는 부모(super) 클래스를 상속받겠다.
self.fc1 = nn.Linear(in_features=784, out_features=256) #--2
self.drop = nn.Dropout(0.25) #--3
self.fc2 = nn.Linear(in_features=256, out_features = 128)
self.fc3 = nn.Linear(in_features=128, out_features = 10)
def forward(self,input_data): #--4
out = input_data.view(-1, 784) #--5 #여기서 flatten?
out = F.relu(self.fc1(out))#--6
out = self.drop(out)
out = F.relu(self.fc2(out))
out = self.fc3(out)
return outclass 형태의 모델은 항상 torch.nn.Module을 상속받음.
**init**()은 객체가 갖는 속성 값을 초기화하는 역할을 하며, 객체가 생성될 때 자동 호출됨,(객체를 생성할 때 호출하면 실행되는 초기화 함수) <br/><br/>
- class 형태의 모델은 항상 torch.nn.Module을 상속받음. init()은 객체가 갖는 속성 값을 초기화하는 역할을 하며, 객체가 생성될 때 자동 호출됨,(객체를 생성할 때 호출하면 실행되는 초기화 함수)
- nn은 딥러닝 모델 구성에 필요한 모듈이 모여 있는 패키지이며, Linear는 단순 선형 회귀 모델을 만들 때 사용함. nn(입력의 크기, 출력의 크기) 실제로 데이터 연산이 진행되는 forward() 부분에는 첫 번째 파라미터(입력의 크기) 값만 넘겨주게 되며, 두 번째 파라미터(출력의 크기)에서 정의된 크기가 forward() 연산의 결과가 됨.
- torch.nn.Dropoout(p)는 p만큼의 비율로 텐서의 값이 0이 되고, 0이 되지 않는 값들은 기존 값에 (1/(1-p))만큼 곱해져 커짐. ex) p=0.3이면, 전체 값 중 0.3의 확률로 0이 된다는 것이며, 0이 되지 않는 0.7에 해당하는 값은 (1/(1-0.7)) 만큼 커짐.
- forward()함수는 모델이 학습 데이터를 입력받아서 순전파 학습을 진행시키며, 반드시 forward라는 이름의 함수여야함. 즉, forward()는 모델이 학습 데이터를 입력받아서 순전파 연산을 진행하는 함수이며, 객체를 데이터와 함께 호출하면 자동으로 실행됨. 순전파 연산이란, H(x) 식에 입력된 x로부터 예측된 y를 얻는 것. H(x) = sigmoid(x1w1 + x2w2 +b)처럼 표현된 수식임. H(x)는 각 모델에서 사용하는 활성화 함수 및 입력 값에 따라 다름.
- 파이토치에서 사용하는 뷰는 넘파이의 reshape과 같은 역할로 텐서의 크기를 변경해주는 역할을 함. input_data.view(-1, 784)는 input_data를 (?, 784)의 2차원 텐서 크기로 변경하라는 것. -1은 파이토치에 맡기겠다는 것.
- 활성화 함수를 지정할 때는 다음 두 가지 방법이 가능함.
- F.relu() : forward() 함수에서 정의
- nn.Relu() : init() 함수에서 정의
** pytorch의 drop out
nn모델에서 over fitting 방지하기 위한 방법 중 하나임. 모델의 일부 뉴런을 무작위로 제거하여 학습을 진행함.
→ 모델이 특정 뉴런에만 의지하지 않고 다양한 특성을 학습하도록 유도.
p만큼의 비율로 텐서의 값이 0이 된다는게, p 비율 만큼의 뉴런이 무작위로 제거되는 것.
단순히 L2 규제 사용하는 것보다 효과적으로 모델의 표현력을 제한함. 모든 층에 dropout 적용하는 것보다 일부 층에만 dropout 적용하는게 효과적일 수 있음.
** 클래스와 함수
함수의 호출은 특정 작업만 수행할 뿐 그 결괏값을 계속 사용하기 위해서는 반드시 어딘가에 따로 그 값을 저장해야만함. 즉, 함수를 포함한 프로그램 코드의 일부를 재사용하기 위해서는 해당 함수 뿐만 아니라 데이터가 저장되는 변수까지도 한꺼번에 관리해야함.
=> 함수 뿐만 아니라 관련된 변수까지도 한꺼번에 묶어서 관리하고 재사용할 수 있게 해주는 것이 클래스임. 클래스는 함수와 다르게 여러 객체를 저장할 수 있고, 그 객체들이 독립적으로 연산되는 듯
- 심층 신경망에서 필요한 파라미터 정의
learning_rate = 0.001;
model = FashionDNN();
model.to(device)
criterion = nn.CrossEntropyLoss(); # 분류문제에서 사용하는 손실함수
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate); #옵티마이저를 위한 경사하강법은 Adam 사용.
print(model)
FashionDNN(
(fc1): Linear(in_features=784, out_features=256, bias=True)
(drop): Dropout(p=0.25, inplace=False)
(fc2): Linear(in_features=256, out_features=128, bias=True)
(fc3): Linear(in_features=128, out_features=10, bias=True)
)
** 옵티마이저
확률적 경사하강법의 파라미터 변경 폭이 불안정한 문제를 해결하기 위해 학습 속도와 운동량을 조정하는 옵티마이저 적용
- 아담 : 모멘텀과 알엠에스프롭의 장점을 결합한 경사하강법으로, 알엠에스프롭의 특징인 기울기의 제곱을 지수평균한 값과 모멘텀 특징인 v(i)를 수식에 활용함. 즉, 알엠에스프롭의 G함수와 모멘텀의 v(i)를 사용하여 가중치를 업뎃함. optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
- 모멘텀 : 운동량을 조정하는 방법으로 진짜 길 같은 곳으로만 이동. 매번 기울기를 구하지만, 가중치를 수정하기 전에 이전 수정방향(+, -)을 참고하여 같은 방향으로 일정한 비율만 수정하는 방법임. 수정이 양의 방향과 음의 방향으로 순차적으로 일어나는 지그재그 현상이 줄어들고, 이전 이동 값을 고려하여 일정 비율만큼 다음 값을 결정하므로 관성 효과를 얻을 수 있음. SGD 모멘텀은 확률적 경사하강법에서 기울기를 속도로 대체하여 사용함. 이전 속도의 일정 부분 반영. 즉, 이전에 학습했던 속도와 현재 기울기를 반영해서 가중치 구함.
- 알엠에스프롭 : 아다그라드의 G(i) 값이 무한히 커지는 것을 방지하고자 제안된 방법임. G 값이 너무 크면 학습률이 작아져 학습이 안될 수 있으므로, 사용자가 r값을 이용하여 학습률 크기를 비율로 조정할 수 있도록함.
- 심층 신경망을 이용한 모델 학습
#심층 신경망을 이용한 모델 학습
num_epochs = 5
count = 0
loss_list = [] #--1
iteration_list = []
accuracy_list = []
predictions_list = []
labels_list = []
for epoch in range(num_epochs):
for images, labels in train_loader: #for 구문으로 레코드(행, 가로줄)를 하나씩 가져옴. 변수 두개 지정하면 레코드에서 요소 2개 꺼내옴
images, labels = images.to(device), labels.to(device) #모델과 데이터가 동일한 장치 (CPU or GPU)에 있어야함. 위 코드 .to(device) 참고
train = Variable(images.view(100, 1, 28, 28)) ### --2
#받아온 이미지를 Input Tensor: (𝑁,𝐶𝑖𝑛,𝐻𝑖𝑛,𝑊𝑖𝑛) 이렇게 만드는 것. 그래서 batch 사이즈가 100. 입력 채널수는 1, 데이터는 28 x 28
labels = Variable(labels)
outputs = model(train)
loss = criterion(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
count += 1
if not (count % 50): #count를 50으로 나누었을 때 나머지가 0이 아니라면 실행
total = 0
correct = 0
for images, labels in test_loader:
images, labels = images.to(device), labels.to(device)
labels_list.append(labels)
test = Variable(images.view(100, 1, 28, 28))
outputs = model(test)
predictions = torch.max(outputs, 1)[1].to(device)
predictions_list.append(predictions)
correct += (predictions == labels).sum()
total += len(labels)
accuracy = correct * 100 / total # 분류 문제에서 정확도는 전체 예측에 대한 정확한 예측의 비율.
loss_list.append(loss.data) #--1'
iteration_list.append(count)
accuracy_list.append(accuracy)
if not (count % 500): #count를 500으로 나누었을 때 나머지가 0 이아니라면 그 이후 iteration 출력
print("Iteration: {}, Loss: {}, Accuracy: {}%".format(count, loss.data, accuracy))
Iteration: 500, Loss: 0.5435527563095093, Accuracy: 82.97000122070312%
Iteration: 1000, Loss: 0.4892081022262573, Accuracy: 84.47000122070312%
Iteration: 1500, Loss: 0.3896807134151459, Accuracy: 84.55999755859375%
Iteration: 2000, Loss: 0.28641635179519653, Accuracy: 85.80999755859375%
Iteration: 2500, Loss: 0.27654707431793213, Accuracy: 86.0199966430664%
Iteration: 3000, Loss: 0.3137684166431427, Accuracy: 86.51000213623047%
1, 1' . 비어 있는 배열이나 행렬만들고, append 메서드를 이용하여 데이터를 하나씩 추가함.
- Autograd는 자동미분을 수행하는 파이토치의 핵심 패키지로, 자동 미분에 대한 값을 저장하기 위해 테이프(tape) 사용. 순전파(forward) 단계에서 테이프는 수행하는 모든 연산을 저장함. 그리고 역전파(backward) 단계에서 저장된 값들을 꺼내서 사용함. 즉, Autograd는 Variable 을 사용해서 역전파를 위한 미분 값을 자동으로 계산해줌.
분류 문제에서 클래스 3개일 때 주의해야할 점
- 정확도가 80이상일 때, 모든 클래스가 동등하게 고려된건지, 특정 클래스의 분류가 높았던 것인지에 대해 알 수 없음에 유의
- 정확도가 90이상일 때, 100개 데이터 중 90개가 하나의 클래스에 속할 경우 90의 정확도는 높다할 수 없음 즉, 모든 데이터를 특정 클래스에 속한다 예측해도 90의 예측 결과가 나오기 때문에 데이터 특성에 따라 정확도 잘 판단해야함.
'Deep Learning & AI > DNN' 카테고리의 다른 글
Classification 문제를 위한 softmax 핵심 이해 (0) | 2024.02.02 |
---|---|
Adam Optimizer (0) | 2024.02.02 |
Neural Network and Backpropagation (0) | 2023.12.29 |