일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 부스트캠프 AI테크
- ndarray
- 카테고리분포 MLE
- Python 특징
- type hints
- scatter
- pivot table
- 표집분포
- 딥러닝
- namedtuple
- Python
- Comparisons
- Operation function
- Numpy
- seaborn
- BOXPLOT
- dtype
- subplot
- VSCode
- Array operations
- 최대가능도 추정법
- boolean & fancy index
- python 문법
- Python 유래
- unstack
- Numpy data I/O
- 정규분포 MLE
- 가능도
- linalg
- groupby
- Today
- Total
또르르's 개발 Story
[12-2] Optimizers using PyTorch 본문
1️⃣ 설정
matplotlib 3.3.0을 설치합니다.
!pip install matplotlib==3.3.0
필요한 모듈을 import 합니다.
import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
%matplotlib inline
%config InlineBackend.figure_format='retina'
print ("PyTorch version:[%s]."%(torch.__version__))
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print ("device:[%s]."%(device))
2️⃣ Dataset
데이터(n_data)는 10000개로 설정하고 data를 생성합니다.
n_data = 10000
x_numpy = -3+6*np.random.rand(n_data,1) # uniform 분포 10000 * 1로 만듦
y_numpy = np.exp(-(x_numpy**2))*np.cos(10*x_numpy) + 3e-2*np.random.randn(n_data,1)
plt.figure(figsize=(8,5)) # 가로 8인치, 세로 5인치
plt.plot(x_numpy,y_numpy,'r.',ms=2) # ms는 markersize(마커크기)
plt.show()
x_torch = torch.Tensor(x_numpy).to(device) # tensor로 변경
y_torch = torch.Tensor(y_numpy).to(device)
print ("Done.")

3️⃣ Optimizer model
1) MLP model
기본적인 MLP 모델입니다. 다만 layer들을 하나씩 만들어서 self.layers list를 추가한 후, concat 하는 방식을 사용했습니다. 이렇게 사용하는 이유는 concat 할 때 layer이름을 정해서 넣기 위해서이며, 가독성이 올라갑니다.
class Model(nn.Module):
def __init__(self,name='mlp',xdim=1,hdims=[16,16],ydim=1):
super(Model, self).__init__()
self.name = name
self.xdim = xdim
self.hdims = hdims
self.ydim = ydim
self.layers = []
prev_hdim = self.xdim
for hdim in self.hdims:
self.layers.append(nn.Linear(prev_hdim, hdim, bias=True))
self.layers.append(nn.Tanh()) # activation
prev_hdim = hdim
# Final layer (without activation)
self.layers.append(nn.Linear(prev_hdim,self.ydim,bias=True))
# Concatenate all layers
self.net = nn.Sequential()
for l_idx,layer in enumerate(self.layers):
layer_name = "%s_%02d"%(type(layer).__name__.lower(),l_idx)
self.net.add_module(layer_name,layer)
self.init_param() # initialize parameters
def init_param(self):
for m in self.modules():
if isinstance(m,nn.Conv2d): # init conv
nn.init.kaiming_normal_(m.weight)
nn.init.zeros_(m.bias)
elif isinstance(m,nn.Linear): # lnit dense
nn.init.kaiming_normal_(m.weight)
nn.init.zeros_(m.bias)
def forward(self,x):
return self.net(x)
print ("Done.")
여기서 self.layers에 넣는데 Linear -> Tanh (activation)을 계속 넣어줍니다. model의 default의 경우 hdims은 [16, 16]이므로 "16 layer 1번"과 "16 layer 2번" 두 번이 돌아갑니다.
self.layers = []
prev_hdim = self.xdim
for hdim in self.hdims:
self.layers.append(nn.Linear(prev_hdim, hdim, bias=True))
self.layers.append(nn.Tanh()) # activation
prev_hdim = hdim
# Final layer (without activation)
self.layers.append(nn.Linear(prev_hdim,self.ydim,bias=True))
Concat을 할 때는 network를 sequential 하게 하고 layer name을 넣어줍니다. 그런 후, add_module을 사용해서 layer_name과 layer를 모듈에 넣어서 concat 합니다.
# Concatenate all layers
self.net = nn.Sequential()
for l_idx,layer in enumerate(self.layers):
layer_name = "%s_%02d"%(type(layer).__name__.lower(),l_idx)
self.net.add_module(layer_name,layer)
2) Optimizer 설정
모델은 SGD, Momentum, Adam 3가지를 사용합니다. 모델 객체를 만들고 optimizer에 parameter와 LEARNING_RATE를 넣어줍니다.
LEARNING_RATE = 1e-2
# Instantiate models
model_sgd = Model(name='mlp_sgd',xdim=1,hdims=[64,64],ydim=1).to(device)
model_momentum = Model(name='mlp_momentum',xdim=1,hdims=[64,64],ydim=1).to(device)
model_adam = Model(name='mlp_adam',xdim=1,hdims=[64,64],ydim=1).to(device)
# Optimizers
loss = nn.MSELoss()
optm_sgd = optim.SGD(model_sgd.parameters(), lr=LEARNING_RATE)
optm_momentum = optim.SGD(model_momentum.parameters(), lr=LEARNING_RATE, momentum=0.9)
optm_adam = optim.Adam(model_adam.parameters(), lr=LEARNING_RATE)
print ("Done.")
SGD는 optim.SGD에 parameters와 learning rate를 넣어줍니다.
optm_sgd = optim.SGD(model_sgd.parameters(), lr=LEARNING_RATE)
Momentum은 optim.SGD에 parameters와 learning rate, momentum을 추가로 넣어줍니다.
optm_momentum = optim.SGD(model_momentum.parameters(), lr=LEARNING_RATE, momentum=0.9)
Adam은 optim.Adam에 parameters와 learning rate를 넣어줍니다.
optm_adam = optim.Adam(model_adam.parameters(), lr=LEARNING_RATE)
3) Check Parameters
Parameter를 출력합니다. 다만, MLP 모델에서 layer list 방법을 사용해서 name을 넣었기 때문에 훨씬 직관적으로 알 수 있습니다.
np.set_printoptions(precision=3) # numpy float 출력옵션 변경
n_param = 0
for p_idx,(param_name,param) in enumerate(M.named_parameters()):
param_numpy = param.detach().cpu().numpy() # detach() : 기존 Tensor에서 gradient 전파가 안되는 텐서 생성
n_param += len(param_numpy.reshape(-1)) # 한줄로 만들어줌
print ("[%d] name:[%s] shape:[%s]."%(p_idx,param_name,param_numpy.shape))
print (" val:%s"%(param_numpy.reshape(-1)[:5]))
print ("Total number of parameters:[%s]."%(format(n_param,',d'))) # 총 parameter 수
[0] name:[net.linear_00.weight] shape:[(64, 1)].
val:[0.348 0.964 0.831 1.934 1.524]
[1] name:[net.linear_00.bias] shape:[(64,)].
val:[0. 0. 0. 0. 0.]
[2] name:[net.linear_02.weight] shape:[(64, 64)].
val:[ 0.031 -0.118 0.106 0.028 0.254]
[3] name:[net.linear_02.bias] shape:[(64,)].
val:[0. 0. 0. 0. 0.]
[4] name:[net.linear_04.weight] shape:[(1, 64)].
val:[-0.117 -0.249 0.115 0.152 0.088]
[5] name:[net.linear_04.bias] shape:[(1,)].
val:[0.]
Total number of parameters:[4,353].
4️⃣ Train
MAX_ITER,BATCH_SIZE,PLOT_EVERY = 1e4,64,500
model_sgd.init_param()
model_momentum.init_param()
model_adam.init_param()
model_sgd.train()
model_momentum.train()
model_adam.train()
for it in range(int(MAX_ITER)):
r_idx = np.random.permutation(n_data)[:BATCH_SIZE] # permutation은 deep copy shuffle
batch_x,batch_y = x_torch[r_idx],y_torch[r_idx]
# Update with Adam
y_pred_adam = model_adam.forward(batch_x)
loss_adam = loss(y_pred_adam,batch_y)
optm_adam.zero_grad()
loss_adam.backward()
optm_adam.step()
# Update with Momentum
y_pred_momentum = model_momentum.forward(batch_x)
loss_momentum = loss(y_pred_momentum,batch_y)
optm_momentum.zero_grad()
loss_momentum.backward()
optm_momentum.step()
# Update with SGD
y_pred_sgd = model_sgd.forward(batch_x)
loss_sgd = loss(y_pred_sgd,batch_y)
optm_sgd.zero_grad()
loss_sgd.backward()
optm_sgd.step()
# Plot
if ((it%PLOT_EVERY)==0) or (it==0) or (it==(MAX_ITER-1)):
with torch.no_grad(): # no gradient update
y_sgd_numpy = model_sgd.forward(x_torch).cpu().detach().numpy()
y_momentum_numpy = model_momentum.forward(x_torch).cpu().detach().numpy()
y_adam_numpy = model_adam.forward(x_torch).cpu().detach().numpy()
plt.figure(figsize=(8,4))
plt.plot(x_numpy,y_numpy,'r.',ms=4,label='GT')
plt.plot(x_numpy,y_sgd_numpy,'g.',ms=2,label='SGD')
plt.plot(x_numpy,y_momentum_numpy,'b.',ms=2,label='Momentum')
plt.plot(x_numpy,y_adam_numpy,'k.',ms=2,label='ADAM')
plt.title("[%d/%d]"%(it,MAX_ITER),fontsize=15)
plt.legend(labelcolor='linecolor',loc='upper right',fontsize=15)
plt.show()
print ("Done.")

(생략)

(생략)

✅ sgd는 왜 못 맞췄을까?
위의 결과를 보면 SGD는 정답을 아직 못 맞힌 것을 볼 수 있습니다.
그렇다면 다른 Optimizer는 어떻게 정답에 빠르게 도달했을까요?
Momentum은 이전의 그레디언트를 활용에서 다음에 사용하겠다는 개념입니다.
Mini-batch를 사용했기 때문에 이전의 얻어진 그레이언트 정보를 현재에 반영하면서 데이터를 더 많이 반영하는 효과를 받게 됩니다.
Adam은 답이라고 생각한 파라미터에서는 확 늘리고, 답이라고 생각하지 않은 파라미터에서는 확 줄이는 방법을 사용해서 정답에 빠르게 가까워집니다.
또한, loss는 square loss를 사용했기 때문에 많이 틀리는 곳을 많이 맞추게 되고, 적게 틀리는 곳은 적게 맞추게 되는 효과(제곱이기 때문에)로 빠르게 target에 가까워질 수 있습니다.
r_idx는 permutation을 통해 10000 이하의 데이터를 랜덤으로 batch_size만큼의 array로 생성합니다.
이후, x_torch에 "r_idx array 위치"에 해당하는 값을 가지고 옵니다. (y_torch도 마찬가지입니다.)
r_idx = np.random.permutation(n_data)[:BATCH_SIZE] # permutation은 deep copy shuffle
batch_x,batch_y = x_torch[r_idx],y_torch[r_idx]
Optimizer를 forward 후 loss를 구하고 update 합니다.
# Update with Adam
y_pred_adam = model_adam.forward(batch_x)
loss_adam = loss(y_pred_adam,batch_y)
optm_adam.zero_grad()
loss_adam.backward()
optm_adam.step()
model에서 forward 해서 나온 값을 복사해서 numpy 형태로 변경합니다.
(detach() : 기존 Tensor에서 gradient 전파가 안 되는 텐서 생성)
y_sgd_numpy = model_sgd.forward(x_torch).cpu().detach().numpy()
y_momentum_numpy = model_momentum.forward(x_torch).cpu().detach().numpy()
y_adam_numpy = model_adam.forward(x_torch).cpu().detach().numpy()
plt로 출력합니다.
plt.figure(figsize=(8,4))
plt.plot(x_numpy,y_numpy,'r.',ms=4,label='GT')
plt.plot(x_numpy,y_sgd_numpy,'g.',ms=2,label='SGD')
plt.plot(x_numpy,y_momentum_numpy,'b.',ms=2,label='Momentum')
plt.plot(x_numpy,y_adam_numpy,'k.',ms=2,label='ADAM')
plt.title("[%d/%d]"%(it,MAX_ITER),fontsize=15)
plt.legend(labelcolor='linecolor',loc='upper right',fontsize=15)
plt.show()
'부스트캠프 AI 테크 U stage > 실습' 카테고리의 다른 글
[13-3] Google Image Data 다운로드 (0) | 2021.02.04 |
---|---|
[13-2] Colab에서 DataSet 다루기 (강아지 DataSet) (0) | 2021.02.04 |
[13-1] CNN using PyTorch (0) | 2021.02.04 |
[11-3] MLP using PyTorch (0) | 2021.02.02 |
[02-1] Git 사용법 (0) | 2021.01.20 |