[ 이론 ]
1. Linear Regression
회귀 중에서 가장 기본적이면서도 대중적인 선형회귀 모델에 대해 리뷰한다. 선형회귀가 딥러닝의 하위 카테고리는 아니지만 딥러닝의 개념과 사용 기법들을 적용하며 D2L의 초반에 등장하기에 본 3장부터 리뷰를 하기로 하였다. 3장을 학습해야 이후 과정이 쉽기 때문.
x(independent var), y(dependent var)의 관계를 규정하기 위한 모델이며 y = weighted sum of elements in x 로 다시 설명 가능하다.
Linear Regression이 성립되기 위한 가정은 다음과 같이 두가지로 요약할 수 있다.
1. x와 y의 관계가 linear 해야한다.
2. x와 y의 관계의 noise가 well-behaved(=following Gaussian Distribution)해야 한다.
수식으로 나타내면 다음과 같다.
이 때 lienar regression, 즉 관계를 정의하기 위해서는 실제 y에 가장 가깝게 설명하는 w와 b를 찾아야 한다. 이제부터는 이 값을 찾기 위한 내용들이다.
2. Loss Function
loss function의 목적은 fitness를 측정하는 것이다. real과 predicited의 차이를 양의 값으로 수치화하고 이 값이 적을수록 잘 예측했다고 할 수 있다. 가장 유명한 loss function은 Squared error로 위 수치화를 거리(distance)의 개념으로 나타낸다.
example i에 대한 loss 값을 수식화하면 다음과 같다.
전체 loss 값을 수식화하면 다음과 같다. 아래 수식에서의 w,b가 우리가 loss 값의 최소화를 위해 찾아야하는 parameter이다.
조금 더 구체화해서 최적의 w를 찾는 과정은 다음과 같다. loss function도 결국 미분값이 0이 되는 critical point를 하나 갖는 fuction이기에 다음의 과정으로 계산한다.
이런 과정을 거쳐 최적의 하이퍼파라미터를 찾는다면 얼마나 편리할까. 위 과정을 analytically solving이라고도 하는데 이 과정을 거쳐 최적의 값을 찾아가는 것은 당연 deep learning model에서는 불가능하다.
3. Minibatch Stochastic Gradient Descent
그렇기에 deep learning model을 optimizing하기 위한 다른 방법이 필요하다. 이 방법으로 사용되는 것이 gradient descent이다.
정의하면 다음과 같다.
<Def of Gradient Descent>
algorithm that iteratively reducing the error by updating the parameters in the direction that incrementally lowers the the loss function.
이런 gradient descent를 매번 loss function에 대한 미분값으로 모든 데이터에 대해 수행한다면 수행과정이 매우 느릴 것이다. 그래서 하나의 update, 다음 단계의 연산 수행마다 데이터셋에 대해 pass over하는 과정이 필요하다.
그래서 update때 마다 random minibatch를 sampling하며 이 variant를 minibatch stochastic gradient라고 한다.
이 연산과정의 상세는 다음과 같다.
이 때 minibatch sample size와 learning rate은 사전에 임의로 정의된 값으로 training loop에서 학습되는 값이 아니다. 이런 값을 Hyper parameter라고 하고 training loop 종료 후 validation set으로 테스트를 한 후 평가하여 hyper parmeter를 조정하는 과정을 hyper parameter tuning이라고 한다.
위 과정을 거친 후 최적의 w, b의 값을 구할 수 있는데 여전히 이 값은 truly linear and noiseless하다고 할 수 없음을 주의하자. 구한 w,b의 값으로 prediction을 수행한다.
4. Normal Distribution & Squared Loss
웬 갑자기 정규분포? 나도 공부하며 왜 갑자기 등장했을까 싶었다. 아래의 과정을 보면 이해가 된다.
linear regression을 고안한 수학자는 가우스로, 가우스는 또한 정규분포를 발견한 수학자이다. 아래 수식을 보면 linear regression과 정규분포가 매우 관련되어있음을 알 수 있다.
정규분포의 확률계산식이다. 이 때 회귀식에 기존에 못봤던 기호가 하나 들어있는데 관측값에 noise 값이 포함되어 있는 것을 표현한 것이다. 맨 아래 식은 이 noise값이 정규분포를 따른다고 가정한 것이다.
그리고 maximun likelihood에 위해 파라미터 w, b의 최적의 값은 전체 데이터셋의 likelihood의 값을 최대화하는 값이다.
위 식은 직관적이지 않으니 로그변환을 하면 아래와 같은 식이 되고 로그변환을 하더라도 동일하게 maximize하는 지점을 찾으면 된다. 여기서 음의 값을 취한 이유는 optimization은 전통적으로 minimization을 목표로 했기에 여기에 맞추기 위해 negative log likelihood의 값을 최소화하는 지점을 찾는 것으로 표현을 바꾼다.
[ 실습 - 막코드 ]
1. Generating the dataset
실습을 위한 데이터셋을 만든다.
def synthetic_data(w, b, num_examples):
X = torch.normal(0, 1, (num_exmaples, len(w)) ## torch.normal(mean, std, size)
y = torch.matmul(X,w)+b ## y = Xw+b
y += torch.normal(0, 0.01, y.shape)
return X, y.reshape((-1. 1))
true_w = torch.tensor([2,-3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
각 행의 데이터의 feature는 2차원을, label은 1차원의 값을 가진다. 분포의 모양은 다음과 같다.
2. Reading the dataset
위에서 언급한 minibatch를 sampling하는 utility function을 만든다.
def data_iter(batch_size, features, labels):
num_examples = len(features)
indices = list(range(num_exmaples))
random.shuffle(indices) ## to randomly shuffle
for i in range(0, num_examples, batch_size):
batch_indices = torch.tensor(
indices[i:min(i+batch_size, num_examples)])
yield features[batch_indices], labels[batch_indices]
batch_size = 10
for X, y in data_iter(batch_size, features, labels):
print(X, '\n', y)
break
위 for문을 돌려보면 다음과 같이 feature 10개, label 10개가 random sampling된 것을 알 수 있다.
3. Setting
minibathc stochastic gradient를 통해 모델의 파라미터를 optimizing 하기 전 최초의 파라미터를 세팅해야 하며 이를 initializing model parameter라고 부른다. 이 때 update마다 loss function에 대한 gradient를 계산해야 하는데 이를 자동적으로 수행하도록 requires_grad = True를 설정한다.
w = torch.normal(0, 0.01, size = (2,1), requires_grad = True)
b = torch.zeros(1, requires_grad = True)
모델을 정의한다.
## Linear Regression model
def linreg(X,w,b) :
return torch.matmul(X, w) + b
loss function을 정의한다.
## Squared Loss
def squared_loss(y_hat, y) :
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
optimization algorithm을 정의한다. minibatch stochastic gradient를 사용한다.
## Minbatch Stochastice Grdient
def sgd(params, lr, batch_size):
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size ## normalize the step
param.grad.zero_()
여기서 torch.no_grad()는 parameter 조정 작업 중에는 gradient calculation을 진행하지 않기 위해서이다. 이렇게 설정하면 requires_grad = True로 설정해두더라도 gradient 연산을 수행하지 않게 된다. 이는 inferenece나 validation을 할 때 사용하며 no_grad를 통해 gradient를 더 트랙킹하지 않기 때문에 필요한 메모리를 줄이고 연산속도를 증가시킬 수 있다.
4. Training
필요한 function과 파라미터를 정의했기에 이제 training loop를 돌리면 된다. 그 loop에 대해 다시 생각해보면 다음과 같다.
- Initialize parameters (w, b)
- Repeat
- Compute Gradient
- Update Parameter
각각의 iteration마다 minibatch를 얻어 모델을 통과시키고 예측값을 얻는다. 이 예측값과 정답 사이의 loss값을 구하고 backward 방향으로 다시 initiate하고 각각의 gradient 값을 저장한다. 그리고 설정한 optimization algorithm으로 파라미터를 업데이트한다.
lr = 0.03 ## learning rate
num_epochs = 3
net = linreg
loss = squared_loss
for epoch in rnage(num_epochs) :
for X, y in data_iter(batch_size, features, labels):
l = loss(net(X, w, b), y)
l.sum().backward() ## compute gradient on l with respect to X and y
sgd([w, b], lr, batch_size) ## update parameters
with torch.no_grad():
train_l = loss(net(features, w, b), labels)
print(f'epoch {epoch+1}, loss {float(train_l.mean()):f}')
[ 실습 - API ]
막코드와 다르게 API framework를 통해 위 과정을 훨씬 간단하게 수행하는 실습이다.
1,2 . Reading & Generating the dataset
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)
def load_array(data_arrays, batch_size, is_train=True): ## is_train : whether or not data iterator to be shuffled on each epoch
dataset = data.TensorDataset(*data_arrays)
return data.DataLoader(dataset, batch_size, shuffle=is_train)
batch_size = 10
data_iter = load_array((features, labels), batch_size)
위 처럼 훨씬 간단히 수행 가능하며 Dataloader를 사용한다. next(iter(data_iter))를 사용하면 다음 iterator를 불러올 수 있다.
3. Setting
이 과정 또한 훨씬 간단하게 수행할 수 있다.
모델 구조도 간단하게 설정 가능한데 Sequential class를 사용하면 여러개의 연결된 layer를 하나의 container로 담을 수 있다. 데이터가 입력되면 담아놓은 층들을 차례로 거치게 되는 것이다.
현재 linear regression 장에서는 layer가 한개이지만 Sequential class는 굉장히 자주 사용된다. 3장에서는 linear regression으로 그 구현 과정을 설명하기에 nn.Linear로 바로 구현 할 수 있고 이 Linear class는 모든 input이 모든 output에 연결되므로 fully connected layer라고도 한다.
from torch import nn ## 국룰
net = nn.Sequential(nn.Linear(2,1))
이렇게 구현이 되면 net[0]을 통해 첫번째 layer를 호출 할 수 있고 weight.data, bias.data를 통해 파라미터에 접근할 수 있다.
이를 이용해 model parameter를 initializing한다.
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
loss function과 optimization algorithm도 다음과 같이 간단하게 구현 가능하다.
## Loss function
loss = nn.MSELoss()
## Minibatch Stochastic Gradient
trainer = torch.optim.SGD(net.parameters(), lr = 0.03)
4. Training
아래와 같이 간단하게 구현된다.
num_epochs = 3
for epoch in range(num_epochs):
for X, y in data_iter:
l = loss(net(X), y)
trainer.zero_grad()
l.backward() ## Back propagation = calculate gradient
trainer.step() ## Update model parameters
l = loss(net(features), labels)
print(f'epoch {epoch + 1}, loss {l:f}')
## 학습 결과 확인
w = net[0].weight.data
b = net[0].bias.data
다음 게시물에서는 linear neural network의 내용 중 softmax regression에 대해 다루겠다.
'Basic Deep Learning > Dive into Deep Learning 리뷰' 카테고리의 다른 글
[D2L] 4.7 Forward, Backward Propagation & Computational Graph (0) | 2022.07.11 |
---|---|
[D2L] 4.6 Dropout (0) | 2022.07.11 |
[D2L] 4.5 Weight Decay (0) | 2022.07.11 |
[D2L] 4.1~ 3 Multilayer Perceptron (0) | 2022.07.10 |
[D2L] 3-2. Linear Neural Networks (Softmax Regression) (0) | 2022.07.09 |