본 포스팅은 아래의 자료를 참고하여 작성하였습니다.
다만 처음으로 라이브러리를 다루는 글인만큼, 앞의 서론이 길다.
0 -1 . 왜 pytorch 인가?
tensorflow, keras, pytorch
아마 딥러닝을 이제 공부하기 시작하는 나 같은 사람들이라면, 무슨 딥러닝 라이브러리를 써야할지 그 고민부터 시작할 것이다.
필자는 pytorch를 선택하기로 했으나 이게 최고의 선택이라고 할 수는 없겠다.
대신 왜 pytorch를 선택했는지 그에 대한 이유를 제시할 수는 있다.
- 공부하기에 tf보다 효율적이다
tensorflow는 버전 2를 출시하면서 기존의 코드와 많은 것이 바꼈다. session이 없어진 것을 그 예로 들 수 있겠다. tensorflow가 대거 코드를 바뀌어버림으로써 어느정도 효율성을 찾은 것은 사실이지만, 대신 나같은 뉴비들은 버전 1 역시 공부해야 하는 부담을 지게 되었다. 하지만 그만큼 tf가 작동에 있어서도 효율적이라면 공부량은 감수해야하는 요소이기는 하다. 하여 다른 요소들도 같이 고려해주길 바란다.
한편 pytorch는 코드가 직관적이다. tf의 placeholder, variable, constant.. 새로운 자료형들이 마구 나온다. 그에 반해 pytorch는 numpy를 기반으로 하여 보다 이해하기 쉽다고 들었다.
- 성장중인 torch
tf는 현재 머신러닝 업계 쪽에서는 그 사용률이 대단하며 관련한 자료가 그만큼 많다. 그에 비해 나처럼 이 계열에 뉴비인 torch는 사용률이 그렇게 높지 않으며, 참고할 자료가 다수 없을 수 있다. 그렇지만 최근은 torch로 옮겨가는 움직임이 있다고 하더라. 그렇지만 공식 문서는 torch가 훨씬 간단하게 되어 있다고 했다. 찬찬히 공부하다보면 혼자서 깨달을 수 있지 않을까?
- 자세한 내용을 참고하고 싶은 사람은 아래의 링크로(pytorch에 치우친 자료이기는 하나... )
하지만 객관적인 정보를 얻기 위해서는 무엇보다도 스스로 찾아나서는 게 제일!
그리고 둘 다 공부하는 게 제일!
여하튼 이런 연유로 필자는 pytorch를 선택했으니, 다들 자신에게 맞는 선택을 하기를 바란다.
0 -1 . tensor?
- 데이터의 차원에 대하여
1차원: vector
2차원: matrix
3차원 이상: tensor
tensor를 3차원으로 그렸지만, 그보다 더 높은 차원일 수 있다.
단지 표현의 한계가 있을 뿐이다.
- 텐서 조작
i) tf. view
첫번째로는 view 함수다
.view() 괄호 안에 리스트 형태로 원하는 shape를 입력한다
t = np.array([[[0, 1, 2],
[3, 4, 5]],
[[6, 7, 8],
[9, 10, 11]]])
ft = torch.FloatTensor(t) # (2,2,3) shape를 가진 tensor
print(ft.view([-1, 3]))
print(ft.view([-1, 3]).shape) # (?,3) shape를 가진 tensor
이때 -1은 대충 컴퓨터 너가 계산해서 넣어~라는 뜻이다.
난 2차원 데이터 형태에, shape[1]이 3이기만 하면 되고, 나머지는 컴퓨터를 믿겠다, 이런 맥락이다
원 변수의 shape는 (2,2,3)이였으므로 총 2*2*3=12개의 값을 갖고 있었다.
그런데 12를 ?*3의 형태로 만들고 싶다고 컴퓨터한테 말하면 컴퓨터는 친절하게 계산해준다
그래서 우리는 (4, 3) shape의 데이터를 얻게 된다.
차원을 바꿀때만 그런건 아니다.
위의 tf를 아래처럼 바꿀 수도 있다.
print(tf.view([-1,1,3]))
print(tf.view([-1,1,3]).shape)
이때는 12=?*1*3의 방정식을 만족하는 ?의 값을 컴퓨터가 계산해서 알려준다
간단하다!
shape에서 -1은 하나면 충분하다. 만약에 둘이 되버린다면?
12=?*?*3 을 계산해낼리가 없다.
ii) tf. squeeze
데이터 분석을 하면서 느낀거지만 고차원 데이터는 언제나 불편하다. 직관적이지 않고, 컴퓨터도 힘들어한다.
가능하면 저차원! (실은 적절한 차원)을 원하는데...
그래서 불필요한 차원을 줄이는 함수인 squeeze()가 존재한다.
차원이 1인 경우에는 해당 차원을 제거한다.
ft = torch.FloatTensor([[0], [1], [2]]) # (3*1)으로 2차원의 데이터
print(ft.squeeze())
print(ft.squeeze().shape) # (3)으로 1차원 데이터(벡터)로 축소된다
iii) tf. unsqueeze()
squeeze 함수의 반대로 일한다.
차원이 1인 것을 만들어주는 역할인데, 이 때는 인자를 필요로 한다.
원래의 데이터가 (3)의 차원이였다면 이것을 (1,3)으로 바꿀 것인지 (3,1)로 바꿀 것인지를 정해야 한다는 뜻이다.
ft = torch.Tensor([0, 1, 2])
print(ft.unsqueeze(0))
# (?,?)로 바뀌는데, 이때 인덱스가 0인 곳에 1을 추가하겠다 이말이다 뒤의 ?는 원래 값을 따른다
# 그래서 기존의 (3)이였던 차원이 (1,3)으로 바뀐다
# 관련 라이브러리 임포트
## 3-01. 선형회귀
import torch
import numpy as np
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
torch.manual_seed(1)
이론은 여기서! : hyelimkungkung.tistory.com/5
1. 단순 선형 회귀 manual
변수를 선언하는 방법이 간결하다.
torch.FloatTensor을 통해서 원하는 차원으로 생성할 수 있다.
이때 torch.zeros는 np.zeros라고 생각하면 편하다
numpy처럼 제일 처음 오는 인자 1은 array의 차원이라고 생각하면 된다
즉 우리가 추정해야 하는 변수들을 0으로 초기화하는 것이다
다만 뒤의 requires_grad=True가 읭? 스러운데 이는
이 변수는 학습을 통해 계속 값이 변경되는 변수임을 알려주는 거라고 한다.
hypothesis는 앞서 우리가 봤던 식과 동일하다 H(X)=XW+b
cost 역시 마찬가지
x_train=torch.FloatTensor([[1],[2],[3]])
y_train=torch.FloatTensor([[2],[4],[6]])
W=torch.zeros(1,requires_grad=True)
b=torch.zeros(1,requires_grad=True)
hypothesis=x_train*W+b
cost=torch.mean((hypothesis-y_train)**2)
우리가 만든 데이터를 봤을 때 우리는 금방 알수 있다. 원하는 대로 값이 나온다면 H(X)=2W로 나와야 한다는 것을!
optimizer은 optim 클래스의 인스턴스
2000번 반복한다
optimizer=optim.SGD([W,b],lr=0.01)
nb_epochs=2000
for epoch in range(nb_epochs+1):
hypothesis=x_train*W+b
cost=torch.mean((hypothesis-y_train)**2)
optimizer.zero_grad() # grad는 매번 알아서 초기화가 안되고 누적되기에 우리가 초기화해주어야 한다
cost.backward() # grad의 값을 가져온다
optimizer.step() # 변수들을 업데이트 해준다(learning rate와 grad의 값을 이용해서)
if epoch%100==0:
print('Epoch {:4d}/{} W:{:.3f},b:{:.3f} Cost: {:.6f}'.format(
epoch,nb_epochs,W.item(),b.item(),cost.item()
))
결과는 위와 같다. cost 값은 0으로 줄어들고, W 값은 2로 수렴 잘 했다.
2. Multi Variable 선형 회귀 manual
x_train=torch.FloatTensor([[73,80,75],
[93,88,93],
[89,91,80],
[96,98,100],
[73,66,70]])
y_train=torch.FloatTensor([[152],[185],[180],[196],[142]])
W=torch.zeros((3,1),requires_grad=True)
b=torch.zeros(1,requires_grad=True)
optimizer=optim.SGD([W,b],lr=1e-5)
nb_epochs=20
for epoch in range(nb_epochs+1):
hypothesis=x_train.matmul(W)+b
cost=torch.mean((hypothesis-y_train)**2)
optimizer.zero_grad()
cost.backward()
optimizer.step()
print('Epoch {:4d}/{} hypothesis: {} Cost: {:.6f}'.format(
epoch, nb_epochs, hypothesis.squeeze().detach(), cost.item()
))
3. nn.Module을 이용한 선형 회귀
기타 sklearn에서 머신러닝을 써 본 사람이라면 반가울 대목이다
일일이 모수와 hypothesis를 적어놓지 않아도, 알아서 생성되는 model을 pytorch에서도 이용할 수 있다.
3-1. 단순 선형 회귀
x_train=torch.FloatTensor([[1],[2],[3]])
y_train=torch.FloatTensor([[2],[4],[6]])
model=nn.Linear(1,1)
print(list(model.parameters()))
optimizer=torch.optim.SGD(model.parameters(),lr=0.01)
nb_epochs=2000
for epoch in range(nb_epochs+1):
prediction=model(x_train)
cost=F.mse_loss(prediction,y_train)
optimizer.zero_grad()
cost.backward()
optimizer.step()
if epoch%100==0:
# 100번마다 로그 출력
print('Epoch {:4d}/{} Cost: {:.6f}'.format(
epoch, nb_epochs, cost.item()
))
3-2. Multi variable 선형 회귀
x_train = torch.FloatTensor([[73, 80, 75],
[93, 88, 93],
[89, 91, 90],
[96, 98, 100],
[73, 66, 70]])
y_train = torch.FloatTensor([[152], [185], [180], [196], [142]])
model=nn.Linear(3,1)
print(list(model.parameters()))
optimizer=torch.optim.SGD(model.parameters(),lr=1e-5)
nb_epochs=2000
for epoch in range(nb_epochs+1):
prediction=model(x_train)
cost=F.mse_loss(prediction,y_train)
optimizer.zero_grad()
cost.backward()
optimizer.step()
if epoch%100==0:
print('Epoch {:4d}/{} Cost: {:.6f}'.format(
epoch, nb_epochs, cost.item()
))
4. 미니 배치와 데이터 로드
1회의 epoch를 한 번에 시행하지 않고
데이터를 배치 단위로 나누어 여러번 시행하는 것
## 3 - 06. 미니 배치와 데이터 로드
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader
x_train = torch.FloatTensor([[73, 80, 75],
[93, 88, 93],
[89, 91, 90],
[96, 98, 100],
[73, 66, 70]])
y_train = torch.FloatTensor([[152], [185], [180], [196], [142]])
dataset=TensorDataset(x_train,y_train)
dataloader=DataLoader(dataset,batch_size=2,shuffle=True)
model=nn.Linear(3,1)
optimizer=torch.optim.SGD(model.parameters(),lr=1e-5)
nb_epochs=2000
for epoch in range(nb_epochs+1):
for batch_idx,samples in enumerate(dataloader):
x_train,y_train=samples
prediction=model(x_train)
cost=F.mse_loss(prediction,y_train)
optimizer.zero_grad()
cost.backward()
optimizer.step()
print('Epoch {:4d}/{} Batch {}/{} Cost: {:.6f}'.format(
epoch, nb_epochs, batch_idx+1, len(dataloader),
cost.item()
))
위의 결과물 사진처럼, 한번에 모든 데이터를 이용해서 epoch를 하지 않고
3개의 작은 데이터셋으로 나누어 epoch를 진행했다.
5 - 1. 로지스틱 회귀
y_data를 보면, (0,1)로 이진 변수로 코딩되었음을 알 수 있다.
이때 로지스틱 회귀에서 우리가 이용하는 sigmoid 함수는
1. 직접 설정
2. sigmoid 함수를 이용하는 방법
이 있다.
아래의 코드를 참고하길 바란다.
## 4 - 01 로지스틱 회귀
x_data = [[1, 2], [2, 3], [3, 1], [4, 3], [5, 3], [6, 2]]
y_data = [[0], [0], [0], [1], [1], [1]]
x_train = torch.FloatTensor(x_data)
y_train = torch.FloatTensor(y_data)
W=torch.zeros((2,1),requires_grad=True)
b=torch.zeros(1,requires_grad=True)
hypothesis=1/(1+torch.exp(-(x_train.matmul(W)+b)))
# hypothesis=torch.sigmoid(x_train.matmul(W)+b)
optimizer=optim.SGD([W,b],lr=1)
nb_epochs=1000
for epoch in range(nb_epochs+1):
hypothesis=torch.sigmoid(x_train.matmul(W)+b)
cost=F.binary_cross_entropy(hypothesis, y_train)
optimizer.zero_grad()
cost.backward()
optimizer.step()
if epoch%100==0:
print('Epoch {:4d}/{} Cost: {:.6f}'.format(
epoch, nb_epochs, cost.item()
))
prediction=hypothesis>=torch.FloatTensor([0.5])
print(prediction)
5 - 2. nn.Moudle 을 이용한 로지스틱 회귀
## 4 - 02 nn.Module로 구현하는 로지스틱 회귀
x_data = [[1, 2], [2, 3], [3, 1], [4, 3], [5, 3], [6, 2]]
y_data = [[0], [0], [0], [1], [1], [1]]
x_train = torch.FloatTensor(x_data)
y_train = torch.FloatTensor(y_data)
model = nn.Sequential(
nn.Linear(2, 1), # input_dim = 2, output_dim = 1
nn.Sigmoid() # 출력은 시그모이드 함수를 거친다
)
이후의 최적화 과정은 동일하다.
블로그 하시는 분들 존경합니다...
'딥러닝 > 모두를 위한 딥러닝 2' 카테고리의 다른 글
08 합성곱 신경망 Convolution Neural Network (0) | 2021.04.22 |
---|---|
07 인공지능 뉴런 artificial neural network (0) | 2021.04.01 |
06 소프트맥스 회귀 softmax regression (0) | 2021.03.31 |
02-05 regression 이론 (0) | 2021.03.24 |
02-05. regression (0) | 2021.03.23 |
댓글