정규화: 오버피팅을 늦추고 모델이 학습 데이터로부터 적절한 feature들을 학습하여 일반화 오차를 낮춰줄 수 있는 기법
이때, 힉습을 방해하는 형태로 학습오차는 높아질 수 있음-> 노이즈에 강인한 모델이 됨
1. 가중치 감쇠(weight decay)
: 손실함수의 수정을 통한 정규화 방식
W에 L2노름을 취하여 노드 사이의 관계를 약화시킴
L2 대신 L1노름을 사용할 수 있음.
-> 파이토치 옵티마이저 생성시 설정을 통해 구현
2. 데이터 증강(data augmentation)
: 데이터의 핵심 특징은 간직한 채 노이즈를 더하여 데이터셋 확장하는 정규화 방법
1) 소금과 후추 노이즈 추가
2) 회전과 이동, 뒤집기
3) 생성모델(generative model)을 활용한 데이터 증강->GAN이나 autoencoder 활용
4) 텍스트 증강 기법(단어의 생략, 단어 교환, 단어이동 등)
3. 드롭아웃
: 신경망의 중간에 노이즈 추가: 임의의 노드를 일정 확률로 drop해서 학습에 참여하지 않도록 하는 방법
training에는 각 노드가 1-p의 확률로 드롭아웃이 적용된다. 하지만 추론에서는 모든 노드가 참여하는데,
이때, 가중치 파라미터 W에 p를 곱해주어야 한다.
->파이토치에서는 1/1-p를 드롭아웃 계층 출력값에 곱해주도록 한다!
p는 보통 0.1에서 0.5 사이의 0.1 단위 값으로 튜닝
드롭아웃이 적용되면 비록 일반화 성능이 개선될 수는 있어도 손실 값의 수렴속도가 저하될 수 있고, 학습오차가 증가할 수 있다.
4. 배치정규화(batch normalization)
: 배치정규화 기법은 학습속도 향상하며 성능도 개선
공변량 변화(covariate shift) 문제 해결
배치정규화는 미니배치 분포를 정규화하여 이러한 문제 해결
미니배치를 unit gaussian 분포로 바꾸는 정규표준분포화(standardization)
단, 추론할 때에는 학습과 달리 미니배치의 평균을 구하지 않고 이동평균을 활용하여 정규화
위치는 후자가 더 선호됨. 배치정규화를 활용하면 보통 드롭아웃은 쓰지 않는다!
배치정규화의 경우 튜닝이 필요한 하이퍼파라미터가 추가되지 않는다!
학습과 추론에서의 차이가 있기 때문에
model.train(), model.eval() 을 지정해주어야 한다!
5. 실습
import numpy as np
from copy import deepcopy
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
train=datasets.MNIST(
'../data',train=True, download=True,
transform=transforms.Compose([
transforms.ToTensor(),
])
)
test=datasets.MNIST(
'../data',train=False,
transform=transforms.Compose([
transforms.ToTensor(),
]),
)
x=train.data.float()/255.
y=train.targets
x=x.view(x.size(0),-1)
print(x.shape, y.shape)
input_size=x.size(-1)
output_size=int(max(y))+1
print('input_size: %d, output_size: %d'%(input_size, output_size))
ratios=[.8,.2]
train_cnt=int(x.size(0)*ratios[0])
valid_cnt=int(x.size(0)*ratios[1])
test_cnt=len(test.data)
cnts=[train_cnt, valid_cnt]
print("Train %d / Valid %d / Test %d samples."%(train_cnt, valid_cnt, test_cnt))
indices=torch.randperm(x.size(0))
x=torch.index_select(x, dim=0, index=indices)
y=torch.index_select(y, dim=0, index=indices)
x=list(x.split(cnts, dim=0))
y=list(y.split(cnts, dim=0))
x+=[(test.data.float()/255.).view(test_cnt, -1)]
y+=[test.targets]
for x_i, y_i in zip(x,y):
print(x_i.size(), y_i.size())
class Block(nn.Module):
def __init__(self,
input_size,
output_size,
use_batch_norm=True,
dropout_p=.4):
self.input_size=input_size
self.output_size=output_size
self.use_batch_norm=use_batch_norm
self.dropout_p=dropout_p
super().__init__()
def get_regularizer(use_batch_norm, size):
return nn.BatchNorm1d(size) if use_batch_norm else nn.Dropout(dropout_p)
self.block=nn.Sequential(
nn.Linear(input_size, output_size),
nn.LeakyReLU(),
get_regularizer(use_batch_norm, output_size),
)
def forward(self, x):
y=self.block(x)
return y
class MyModel(nn.Module):
def __init__(self,
input_size,
output_size,
use_batch_norm=True,
dropout_p=.4):
super().__init__()
self.layers=nn.Sequential(
Block(input_size,500, use_batch_norm, dropout_p),
Block(500,400,use_batch_norm, dropout_p),
Block(400,300,use_batch_norm, dropout_p),
Block(300,200,use_batch_norm, dropout_p),
Block(200,100,use_batch_norm, dropout_p),
Block(100,50,use_batch_norm, dropout_p),
nn.Linear(50,output_size),
nn.LogSoftmax(dim=-1),
)
def forward(self,x):
y=self.layers(x)
return y
model=MyModel(input_size, output_size, use_batch_norm=True)
optimizer=optim.Adam(model.parameters())
crit=nn.NLLLoss()
n_epochs=1000
batch_size=256
print_interval=10
lowest_loss=np.inf
best_model=None
early_stop=50
lowest_epoch=np.inf
train_history, valid_history=[],[]
for i in range(n_epochs):
model.train()
indices=torch.randperm(x[0].size(0))
x_=torch.index_select(x[0], dim=0, index=indices)
y_=torch.index_select(y[0], dim=0, index=indices)
x_=x_.split(batch_size, dim=0)
y_=y_.split(batch_size, dim=0)
train_loss, valid_loss=0,0
y_hat=[]
for x_i, y_i in zip(x_, y_):
y_hat_i=model(x_i)
loss=crit(y_hat_i, y_i.squeeze())
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_loss+=float(loss)
train_loss=train_loss/len(x_)
model.eval()
with torch.no_grad():
x_=x[1].split(batch_size, dim=0)
y_=y[1].split(batch_size, dim=0)
valid_loss=0
for x_i, y_i in zip(x_, y_):
y_hat_i=model(x_i)
loss=crit(y_hat_i, y_i.squeeze())
valid_loss+=float(loss)
y_hat+=[y_hat_i]
valid_loss=valid_loss/len(x_)
train_history+=[train_loss]
valid_history+=[valid_loss]
if(i+1)%print_interval==0:
print('Epoch %d: train loss=%.4e valid_loss=%.4e lowest_loss=%.4e'%
(i+1,
train_loss,
valid_loss,
lowest_loss))
if valid_loss <=lowest_loss:
lowest_loss=valid_loss
lowest_epoch=i
best_model=deepcopy(model.state_dict())
else:
if early_stop > 0 and lowest_epoch+early_stop <i+1:
print("There is no improvement during last %d epochs."%early_stop)
break
print("The best validation loss from epoch %d: %.4e"%(lowest_epoch+1,
lowest_loss))
model.load_state_dict(best_model)
plot_from =0
plt.figure(figsize=(20,10))
plt.grid(True)
plt.title("Train / Valid Loss History")
plt.plot(
range(plot_from, len(train_history)), train_history[plot_from:], label="train")
plt.plot(
range(plot_from, len(valid_history)), valid_history[plot_from:], label="validate")
plt.yscale('log')
plt.legend()
plt.show()
test_loss=0
y_hat=[]
model.eval()
with torch.no_grad():
x_=x[-1].split(batch_size, dim=0)
y_=y[-1].split(batch_size, dim=0)
for x_i, y_i in zip(x_, y_):
y_hat_i=model(x_i)
loss=crit(y_hat_i, y_i.squeeze())
test_loss+=loss
y_hat+=[y_hat_i]
test_loss=test=test_loss/len(x_)
y_hat=torch.cat(y_hat, dim=0)
print("Test Loss: %.4e"%test_loss)
correct_cnt=(y[-1].squeeze()==torch.argmax(y_hat, dim=-1)).sum()
total_cnt=float(y[-1].size(0))
print("Test Accuracy: %.4f" % (correct_cnt/total_cnt))
import pandas as pd
from sklearn.metrics import confusion_matrix
pd.DataFrame(confusion_matrix(y[-1], torch.argmax(y_hat, dim=-1)),
index=['true_%d'%i for i in range(10)],
columns=['pred_%d'%i for i in range(10)])
'딥러닝 > 파이토치 딥러닝' 카테고리의 다른 글
19. RNN(순환신경망) (1) | 2024.06.06 |
---|---|
15장. 실무 환경에서의 프로젝트 연습 (0) | 2024.05.23 |
13장. 심층신경망2 (0) | 2024.05.15 |
12장. 오버피팅을 방지하는 방법 (0) | 2024.05.15 |
11장. 최적화 (0) | 2024.05.09 |