[Pytorch]실습
카테고리: Pytorch
1. Autograd(자동미분) 복습
1) 정의
torch.autograd
패키지는 Tensor의 모든 연산에 대해 자동 미분 제공- 이는 코드를 어떻게 작성하여 실행하느냐에 따라 역전파가 정의된다는 뜻
- 역전파를 위해 미분값을 자동으로 계산
requires_grad
속성을True
로 설정하면, 해당 텐서에서 이루어지는 모든 연산들을 추적하기 시작- 기록을 추적하는 것을 중단하게 하려면,
.detach()
를 호출하여 연산기록으로부터 분리
import torch
a = torch.rand(3,3)
a = a * 3
print(a)
print(a.requires_grad)
tensor([[0.7729, 2.8212, 1.8116],
[1.5490, 1.3230, 0.7801],
[2.7811, 2.3541, 2.1352]])
False
2) 기울기(Gradient)
x = torch.ones(3,3,requires_grad = True)
print(x)
y = x + 5
print(y)
print(y.requires_grad)
z = y*y
out = z.mean()
print(z)
print(out)
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], requires_grad=True)
-------------------------------------------------
tensor([[6., 6., 6.],
[6., 6., 6.],
[6., 6., 6.]], grad_fn=<AddBackward0>)
True
-------------------------------------------------
[36., 36., 36.],
[36., 36., 36.]], grad_fn=<MulBackward0>)
tensor(36., grad_fn=<MeanBackward0>)
-------------------------------------------------
계산이 완료된 후, backward()
를 호출하면 자동으로 역전파 계산이 가능하고, .grad
속성에 누적됨
print(out)
out.backward()
tensor(36., grad_fn=<MeanBackward0>)
grad
: data가 거쳐온 layer에 대한 미분값 저장
print(x)
print(x.grad)
print(y)
print(y.grad)
## x, x.grad
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], requires_grad=True)
tensor([[1.3333, 1.3333, 1.3333],
[1.3333, 1.3333, 1.3333],
[1.3333, 1.3333, 1.3333]])
## y, y.grad
tensor([[6., 6., 6.],
[6., 6., 6.],
[6., 6., 6.]], grad_fn=<AddBackward0>)
None
x = torch.randn(3, requires_grad = True)
y = x*2
while y.data.norm() < 1000:
y = y*2
print(y)
v = torch.tensor([0.1, 1.0, 0.0001], dtype = torch.float)
y.backward(v)
print(x.grad)
tensor([-1502.9976, -581.3363, 240.9863], grad_fn=<MulBackward0>)
tensor([2.0480e+02, 2.0480e+03, 2.0480e-01])
with torch.no_grad()
를 사용하여 기울기의 업데이트를 하지 않음- 기록을 추적하는 것을 방지하기 위해 코드 블럭을
with torch.no_grad()
로 감싸면 기울기 계산은 필요없지만,requires_grad=True
로 설정되어 학습 가능한 매개변수를 갖는 모델을 평가(evaluate)할 때 유용 - 모델을 평가할 때는 모델 자체를 업데이트 하면 안되니 기울기 계산하지 않고, 현재 있는 상태에서 평가할 때는 no_grad()를 넣어줘야 함
import torch
print(x.requires_grad)
print((x**2).requires_grad)
with torch.no_grad():
print((x**2).requires_grad) ## 여기서는 False나옴
True
True
False
detach()
: 내용물(content)은 같지만 require_grad
가 다른 새로운 Tensor를 가져올 때 사용
print(x.requires_grad)
y = x.detach()
print(y.requires_grad)
print(x.eq(y).all()) ## eq(): equal, x와 y가 같니?
True
False
tensor(True)
3) 자동 미분 흐름 예제
- 계산 흐름 $a \rightarrow b \rightarrow c \rightarrow out $
- 그럼 $\quad \frac{\partial out}{\partial a}$ = ?
backward()
를 통해 $a \leftarrow b \leftarrow c \leftarrow out $을 계산하면 $\frac{\partial out}{\partial a}$값이a.grad
에 채워짐
a = torch.ones(2,2)
print(a)
a = torch.ones(2,2, requires_grad = True)
print(a)
print(a.data)
print(a.grad)
print(a.grad_fn)
tensor([[1., 1.],
[1., 1.]])
---------------------------------------
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
---------------------------------------
tensor([[1., 1.],
[1., 1.]])
None
None
b = a + 2
print(b.data)
c = b**2
print(c.data)
out = c.sum()
print(out)
tensor([[9., 9.],
[9., 9.]])
tensor(36., grad_fn=<SumBackward0>)
tensor(36., grad_fn=<SumBackward0>)
- a의
grad_fn
이 None인 이유는 직접적으로 계산한 부분이 없었기 때문
print(a.data)
print(a.grad)
print(a.grad_fn) ## None: a의 미분을 직접적으로 계산한 부분 없음
print(b.data)
print(b.grad)
print(b.grad_fn)
print(c.data)
print(c.grad)
print(c.grad_fn)
print(out.data)
print(out.grad)
print(out.grad_fn)
tensor([[1., 1.],
[1., 1.]])
tensor([[6., 6.],
[6., 6.]])
None
-------------------------------------------
tensor([[3., 3.],
[3., 3.]])
None
<AddBackward0 object at 0x7f6f0d459a60>
-------------------------------------------
tensor([[9., 9.],
[9., 9.]])
None
<PowBackward0 object at 0x7f6f0d459eb0>
-------------------------------------------
tensor(36.)
None
<SumBackward0 object at 0x7f6f0d459220>
2. 신경망 구성해보기
1) 데이터 준비
파이토치에서는 데이터 준비를 위해 torch.utils.data
의 Dataset
과 DataLoader
사용 가능하다.
Dataset
에는 다양한 데이터셋이 존재 (MNIST, FashionMNIST, CIFAR10, …)- Vision Dataset: https://pytorch.org/vision/stable/datasets.html
- Text Dataset: https://pytorch.org/text/stable/datasets.html
- Audio Dataset: https://pytorch.org/audio/stable/datasets.html
DataLoader
와Dataset
을 통해batch_size
,train
여부,transform
등을 인자로 넣어 데이터를 어떻게 load할 것인지 정해줄 수 있다.
import torch
import torchvision
from torch.utils.data import Dataset, DataLoader
import torchvision.transforms as transforms
from torchvision import datasets
토치비전(torchvision
)은 파이토치에서 제공하는 데이터셋들이 모여있는 패키지
transforms
: 전처리할 때 사용하는 메소드(https://pytorch.org/docs/stable/torchvision/transforms.html)transforms
에서 제공하는 클래스 이외는 일반적으로 클래스를 따로 만들어 전처리 단계를 진행-
DataLoader
의 인자로 들어갈transform
을 미리 정의할 수 있고,Compose
를 통해 리스트 안에 순서대로 전처리 진행 ToTensor
()를 하는 이유는torchvision
이 PIL Image 형태로만 입력을 받기 때문에 데이터 처리를 위해서 Tensor형으로 변환 필요
mnist_transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize(mean = (0.5, ), std = (1.0, ))])
------------------------------------------------------------------------
trainset = datasets.MNIST(root = '/content/',
download = True,
train = True,
transform =mnist_transform)
testset = datasets.MNIST(root = '/content/',
download = True,
train = False,
transform =mnist_transform)
Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to /content/MNIST/raw/train-images-idx3-ubyte.gz
100%
9912422/9912422 [00:00<00:00, 21731101.17it/s]
Extracting /content/MNIST/raw/train-images-idx3-ubyte.gz to /content/MNIST/raw
Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz to /content/MNIST/raw/train-labels-idx1-ubyte.gz
100%
28881/28881 [00:00<00:00, 682477.68it/s]
Extracting /content/MNIST/raw/train-labels-idx1-ubyte.gz to /content/MNIST/raw
Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz to /content/MNIST/raw/t10k-images-idx3-ubyte.gz
100%
1648877/1648877 [00:00<00:00, 5918891.39it/s]
Extracting /content/MNIST/raw/t10k-images-idx3-ubyte.gz to /content/MNIST/raw
Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz to /content/MNIST/raw/t10k-labels-idx1-ubyte.gz
100%
4542/4542 [00:00<00:00, 223463.99it/s]
Extracting /content/MNIST/raw/t10k-labels-idx1-ubyte.gz to /content/MNIST/raw
DataLoader
는 데이터 전체를 보관했다가 실제 모델 학습을 할 때batch_size
크기만큼 데이터를 가져옴
train_loader = DataLoader(trainset, batch_size = 8, shuffle = True, num_workers = 2)
test_loader = DataLoader(testset, batch_size = 8, shuffle = False, num_workers = 2)
dataiter = iter(train_loader)
images, labels = next(dataiter)
print(images.shape, labels.shape)
## [8,1,28,28]: 28x28 이미지인데, 1이면 흑백, 8 = 8개 = batchsize
## 즉, 크기가 28x28인 흑백 이미지가 8개
torch_image = torch.squeeze(images[0])
print(torch_image.shape) # 차원 축소됨. 0번째 차원 날라감 = 1 날라감
(torch.Size([8, 1, 28, 28]), torch.Size([8]))
torch.Size([28, 28])
import matplotlib.pyplot as plt
figure = plt.figure(figsize = (12,6))
columns, rows = 4,2
for i in range(1, columns * rows + 1):
sample_idx = torch.randint(len(trainset), size = (1,)).item()
img, label = trainset[sample_idx]
figure.add_subplot(rows, columns, i)
plt.title(label)
plt.axis('off')
plt.imshow(img.squeeze(), cmap = 'gray')
2) 신경망 구성
- 레이어(layer): 신경망의 핵심 데이터 구조로 하나 이상의 텐서를 입력받아 하나 이상의 텐서를 출력
- 모듈(module): 한 개 이상의 계층이 모여서 구성
- 모델(model): 한 개 이상의 모듈이 모여서 구성
torch.nn
패키지- 주로 가중치(weights), 편향(bias)값들이 내부에서 자동으로 생성되는 레이어들을 사용할 때 사용 (
weight
값들을 직접 선언 안함) - https://pytorch.org/docs/stable/nn.html
- 주로 가중치(weights), 편향(bias)값들이 내부에서 자동으로 생성되는 레이어들을 사용할 때 사용 (
Linear Layer
nn.Linear
계층 예제
import torch.nn as nn
input = torch.rand(128,20)
print(input)
print("----------------------------------------------------------------------------")
model = nn.Linear(20,30) ## (input_feature, output_feature)
print(model)
print("----------------------------------------------------------------------------")
output = model(input)
print("----------------------------------------------------------------------------")
print(output)
print(output.size())
tensor([[0.0741, 0.5181, 0.5499, ..., 0.8257, 0.8955, 0.0808],
[0.7822, 0.8137, 0.5497, ..., 0.0058, 0.9777, 0.0643],
[0.4558, 0.9138, 0.7020, ..., 0.1283, 0.5074, 0.8316],
...,
[0.9362, 0.2198, 0.5683, ..., 0.6776, 0.4641, 0.6829],
[0.7784, 0.3584, 0.4910, ..., 0.6288, 0.2116, 0.5324],
[0.8038, 0.5861, 0.2872, ..., 0.1152, 0.7582, 0.8966]])
----------------------------------------------------------------------------
Linear(in_features=20, out_features=30, bias=True)
----------------------------------------------------------------------------
----------------------------------------------------------------------------
tensor([[-0.6023, 0.2802, -0.0368, ..., 0.1032, 0.4914, -0.2984],
[ 0.0908, -0.0125, 0.0343, ..., 0.3796, 0.2376, -0.5433],
[-0.2045, -0.0195, -0.2661, ..., 0.3265, 0.4362, -0.3936],
...,
[-0.3677, -0.0852, -0.2084, ..., 0.0845, 0.2574, -0.5173],
[-0.1548, 0.0879, -0.1322, ..., 0.2853, 0.1115, -0.4794],
[ 0.1217, 0.3638, -0.0713, ..., 0.4999, 0.1304, -0.5946]],
grad_fn=<AddmmBackward0>)
torch.Size([128, 30])
Convolution Layer
nn.Conv2d
예제
in_channels
: channel의 갯수out_channels
: 출력 채널의 갯수kernel_size
: 커널(필터) 사이즈
input = torch.randn(20,16,50,100)
print(input.shape)
torch.Size([20,16,50,100])
m = nn.Conv2d(16,33,3,stride = 2)
m = nn.Conv2d(16,33,(3,5), stride = (2,1), padding = (4,2))
m = nn.Conv2d(16,33,(3,5), stride = (2,1), padding = (4,2), dilation = (3,1))
# nn.Conv2d(in_channels = 16, out_channels = 33, kernel_size = 3, stride = 2)
# nn.Conv2d(in_channels = 16, out_channels = 33, kernel_size = (2,1), padding = (4,2))
print(m)
output = m(input)
print(output.size())
torch.Size([20, 16, 50, 100])
torch.Size([20, 16, 50, 100])
Conv2d(16, 33, kernel_size=(3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1))
torch.Size([20, 33, 26, 100])
nn.Conv2d(in_channels = 1, out_channels = 20, kernel_size = 5, stride = 1)
layer = nn.Conv2d(1,20,5,1).to(torch.device('cpu'))
layer
Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
Weight
확인, weight
는 detach()
를 통해 꺼내줘야 numpy()
변환이 가능
weight = layer.weight
print(weight.shape)
weight = weight.detach()
weight = weight.numpy()
print(weight.shape)
plt.imshow(weight[0,0,:,:], 'jet')
plt.colorbar()
plt.show()
print(weight[0,0,:,:])
weight = weight.detach()
(20, 1, 5, 5)
[[ 0.14788686 -0.07691999 -0.06958671 0.0327538 -0.12588926]
[-0.15326653 -0.06465175 0.0382885 0.16026531 0.08553481]
[ 0.14748965 -0.11287172 -0.01223607 -0.05822499 0.04035332]
[-0.14469357 -0.16859543 0.00449383 0.08313598 -0.0606976 ]
[ 0.16758244 0.14283665 0.13446307 0.15948279 0.01430113]]
print(images.shape)
print(images[0].size())
input_image = torch.squeeze(images[0])
print(input_image.size())
input_data = torch.unsqueeze(images[0], dim = 0)
print(input_data.size())
output_data = layer(input_data)
output = output_data.data # 레이어를 통과시킨 데이터들만 모아서 넘파이 어레이로 바꾸고 모양을 본 것임
output_arr = output.numpy()
print(output_arr.shape)
## Visualization
plt.figure(figsize= (15,30))
plt.subplot(131)
plt.title('input')
plt.imshow(input_image, 'gray')
plt.subplot(132)
plt.title('Weight')
plt.imshow(weight[0,0,:,:], 'jet')
plt.subplot(133)
plt.title("Output")
plt.imshow(output_arr[0,0,:,:], 'gray')
torch.Size([8, 1, 28, 28])
torch.Size([1, 28, 28])
torch.Size([28, 28])
torch.Size([1, 1, 28, 28])
(1, 20, 24, 24)
Pooling Layer
F.max_pool2d
stride
kernel_size
torch.nn.MaxPool2d
도 많이 사용
import torch.nn.functional as F
pool = F.max_pool2d(output, 2,2) ## maxpooling()
pool.shape
torch.Size([1, 20, 12, 12])
MaxPool Layer는 weight가 없기 때문에 바로 numpy()
이 가능하다!!
pool_arr = pool.numpy()
print(output.shape)
print(pool_arr.shape)
## [1,20,24,24] -> [1,20,12,12] : output에 대해서 2개를 기준으로 max값만 저장
torch.Size([1, 20, 24, 24])
(1, 20, 12, 12)
plt.figure(figsize= (10,15))
plt.subplot(121)
plt.title('input')
plt.imshow(input_image, 'gray')
plt.subplot(122)
plt.title('Output')
plt.imshow(pool_arr[0,0,:,:], 'gray')
신경망 종류
모델 정의
nn.Module
상속 클래스 정의nn.Module
을 상속받는 클래스 정의__init__()
: 모델에서 사용될 모듈과 활성화 함수 등을 정의forward()
: 모델에서 실행되어야 하는 연산을 정의
class Model(nn.Module):
def __init__(self, inputs):
super(Model, self).__init__()
self.layer = nn.Linear(inputs, 1)
self.activation = nn.Sigmoid()
def forward(self, x):
x = self.layer(x)
x = self.activation(x)
return x
model = Model(1)
print(list(model.children())) ## children으로 call하는 순간 눈으로 확인가능
print()
print(list(model.modules())) ## 모듈에 linea이고, sigmoid가 들어가 있는지 확인 가능
[Linear(in_features=1, out_features=1, bias=True), Sigmoid()]
[Model(
(layer): Linear(in_features=1, out_features=1, bias=True)
(activation): Sigmoid()
), Linear(in_features=1, out_features=1, bias=True), Sigmoid()]
nn.Sequential
을 이용한 신경망 정의nn.Sequential
객체로 그 안에 각 모듈을 순차적으로 실행__init__()
에서 사용할 네트워크 모델들을nn.Sequential
로 정의 가능forward()
에서 실행되어야 할 계산을 가독성 높게 작성 가능
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.layer1 = nn.Sequential(
nn.Conv2d(in_channels = 3, out_channels = 64, kernel_size = 5),
nn.ReLU(inplace = True),
nn.MaxPool2d(2)
)
## 1번 layer가 sequential이니 순차적으로 실행되는데
## 먼저 convolution layer를 거치고, ReLU라는 Activiation function을 지난다음
## maxpooling을 한다
self.layer2 = nn.Sequential(
nn.Conv2d(in_channels = 64, out_channels = 30, kernel_size = 5),
nn.ReLU(inplace = True),
nn.MaxPool2d(2)
)
self.layer3 = nn.Sequential(
nn .Linear(in_features = 30*5*5, out_features = 10, bias = True),
nn.ReLU(inplace = True),
)
def forward(self, x):
x = self.layer1(x)
x = self.layer2(x)
x = x.view(x.shape[0], -1)
x = self.layer3(x)
return x
model = Model()
print(list(model.children()))
print('------------------------------------------------------------------------------------------')
print(list(model.modules()))
[Sequential(
(0): Conv2d(3, 64, kernel_size=(5, 5), stride=(1, 1))
(1): ReLU(inplace=True)
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
), Sequential(
(0): Conv2d(64, 30, kernel_size=(5, 5), stride=(1, 1))
(1): ReLU(inplace=True)
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
), Sequential(
(0): Linear(in_features=750, out_features=10, bias=True)
(1): ReLU(inplace=True)
)]
------------------------------------------------------------------------------------------
[Model(
(layer1): Sequential(
(0): Conv2d(3, 64, kernel_size=(5, 5), stride=(1, 1))
(1): ReLU(inplace=True)
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(layer2): Sequential(
(0): Conv2d(64, 30, kernel_size=(5, 5), stride=(1, 1))
(1): ReLU(inplace=True)
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(layer3): Sequential(
(0): Linear(in_features=750, out_features=10, bias=True)
(1): ReLU(inplace=True)
)
), Sequential(
(0): Conv2d(3, 64, kernel_size=(5, 5), stride=(1, 1))
(1): ReLU(inplace=True)
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
), Conv2d(3, 64, kernel_size=(5, 5), stride=(1, 1)), ReLU(inplace=True), MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False), Sequential(
(0): Conv2d(64, 30, kernel_size=(5, 5), stride=(1, 1))
(1): ReLU(inplace=True)
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
), Conv2d(64, 30, kernel_size=(5, 5), stride=(1, 1)), ReLU(inplace=True), MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False), Sequential(
(0): Linear(in_features=750, out_features=10, bias=True)
(1): ReLU(inplace=True)
), Linear(in_features=750, out_features=10, bias=True), ReLU(inplace=True)]
3. 모델 파라미터
1) 손실 함수(Cost Funtion)
- 예측 값과 실제 값 사이의 오차 측정
- 학습이 진행되면서 해당 과정이 얼마나 잘 되고 있는지 나타내는 지표
- 모델이 훈련되는 동안 최소화될 값으로 주어진 문제에 대한 성공 지표
- 손실 함수에 따른 결과를 통해 학습 파라미터를 조정
- 최적화 이론에서 최소화 하고자 하는 함수
- 미분 가능한 함수 사용
- 파이토치의 주요 손실 함수
torch.nn.BCELoss
: 이진 분류를 위해 사용torch.nn.CrossEntropyLoss
: 다중 클래스 분류를 위해 사용torch.nn.MSELoss
: 회귀 모델에서 사용
criterion = nn.MSELoss()
criterion = nn.CrossEntropyLoss()
2) 옵티마이저(Optimizer)
- 손실 함수를 기반으로 모델이 어떻게 업데이트되어야 하는지 결정 (특정 종류의 확률적 경사 하강법 구현)
- optimizer는
step()
을 통해 전달받은 파라미터를 모델 업데이트 - 모든 옵티마이저의 기본으로
torch.optim.Optimizer(params, defaults)
클래스 사용 zero_grad()
를 이용해 옵티마이저에 사용된 파라미터들의 기울기를 0으로 설정torch.optim.lr_scheduler
를 이용해 에포크(epochs)에 따라 학습률(learning rate) 조절- 파이토치의 주요 옵티마이저:
optim.Adadelta
,optim.Adagrad
,optim.Adam
,optim.RMSprop
,optim.SGD
3) 학습률 스케줄러(Learning rate scheduler)
- 학습시 특정 조건에 따라 학습률을 조정하여 최적화 진행
- 일정 횟수 이상이 되면 학습률을 감소(decay)시키거나 전역 최소점(global minimum) 근처에 가면 학습률을 줄이는 등
- 파이토치의 학습률 스케줄러 종류
optim.lr_scheduler.LambdaLR
: 람다(lambda) 함수를 이용해 그 결과를 학습률로 설정optim.lr_scheduler.StepLR
: 단계(step)마다 학습률을 감마(gamma) 비율만큼 감소optim.lr_scheduler.MultiStepLR
:StepLR
과 비슷하지만 특정 단계가 아니라 지정된 에포크에만 감마 비율로 감소optim.lr_scheduler.ExponentialLR
: 에포크마다 이전 학습률에 감마만큼 곱함optim.lr_scheduler.CosineAnnealingLR
: 학습률을 코사인(cosine) 함수의 형태처럼 변화시켜 학습률일 커지기도 하고 작아지기도 함optim.lr_scheduler.ReduceLROnPlateau
: 학습이 잘되는지 아닌지에 따라 동적으로 학습률 변화
4) 지표(Metrics)
- 모델의 학습과 테스트 단계를 모니터링
!pip install torchmetrics
Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/
Collecting torchmetrics
Downloading torchmetrics-0.11.3-py3-none-any.whl (518 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 518.6/518.6 KB 10.5 MB/s eta 0:00:00
Requirement already satisfied: typing-extensions in /usr/local/lib/python3.8/dist-packages (from torchmetrics) (4.5.0)
Requirement already satisfied: torch>=1.8.1 in /usr/local/lib/python3.8/dist-packages (from torchmetrics) (1.13.1+cu116)
Requirement already satisfied: packaging in /usr/local/lib/python3.8/dist-packages (from torchmetrics) (23.0)
Requirement already satisfied: numpy>=1.17.2 in /usr/local/lib/python3.8/dist-packages (from torchmetrics) (1.22.4)
Installing collected packages: torchmetrics
Successfully installed torchmetrics-0.11.3
import torchmetrics
preds = torch.randn(10,5).softmax(dim = -1) ## softmax값을 통과한 예측값과
target = torch.randint(5, (10,)) ## 랜덤하게 만든 값과 얼마나 비슷하니?
print(preds, target)
acc = torchmetrics.functional.accuracy(preds, target, task="multiclass", num_classes=5)
print(acc)
tensor([[0.3291, 0.1527, 0.1217, 0.0663, 0.3301],
[0.2401, 0.0895, 0.2365, 0.1314, 0.3025],
[0.1900, 0.0677, 0.3601, 0.2837, 0.0985],
[0.0551, 0.4757, 0.2383, 0.1886, 0.0423],
[0.0773, 0.2609, 0.5159, 0.1181, 0.0278],
[0.4987, 0.1455, 0.0842, 0.0480, 0.2236],
[0.6345, 0.0376, 0.0616, 0.0420, 0.2242],
[0.3481, 0.2354, 0.2752, 0.1273, 0.0139],
[0.2529, 0.1980, 0.0559, 0.2323, 0.2609],
[0.0468, 0.3254, 0.0636, 0.4104, 0.1538]]) tensor([4, 1, 3, 4, 2, 2, 2, 1, 2, 3])
tensor(0.3000)
metric = torchmetrics.Accuracy(task="multiclass", num_classes=5)
n_batches = 10
for i in range(n_batches):
preds = torch.randn(10,5).softmax(dim = -1)
target = torch.randint(5, (10,))
acc = torchmetrics.functional.accuracy(preds, target, task="multiclass", num_classes=5)
print(acc)
tensor(0.3000)
tensor(0.1000)
tensor(0.3000)
tensor(0.2000)
tensor(0.1000)
tensor(0.1000)
tensor(0.2000)
tensor(0.1000)
tensor(0.1000)
tensor(0.)
4. 선형 회귀 모델(Linear Regression Model)
1) 데이터 생성
X = torch.randn(200, 1) * 10
y = X + 3*torch.randn(200,1)
plt.scatter(X.numpy(), y.numpy())
plt.xlabel("X")
plt.ylabel("y")
plt.grid()
plt.show()
2) 모델 정의 및 파라미터
class LinearRegressionModel(nn.Module):
def __init__(self):
super(LinearRegressionModel, self).__init__()
self.linear = nn.Linear(1,1)
def forward(self, x):
pred = self.linear(x)
return pred
model = LinearRegressionModel()
print(model)
print(list(model.parameters()))
LinearRegressionModel(
(linear): Linear(in_features=1, out_features=1, bias=True)
)
[Parameter containing:
tensor([[0.2249]], requires_grad=True), Parameter containing:
tensor([0.9877], requires_grad=True)]
w, b = model.parameters()
w1,b1 = w[0][0].item(), b[0].item()
x1 = np.array([-30,30])
y1 = w1*x1 +b1
plt.plot(x1,y1, 'r')
plt.scatter(X,y)
plt.grid()
plt.show()
3) 손실 함수 및 Optimizer
import torch.optim as optim
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr = 0.001)
4) 모델 학습
epochs = 100
losses = []
for epoch in range(epochs):
optimizer.zero_grad()
y_pred = model(X)
loss = criterion(y_pred, y)
losses.append(loss.item())
loss.backward()
optimizer.step()
plt.plot(range(epochs), losses)
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.show()
w1,b1 = w[0][0].item(), b[0].item()
x1 = np.array([-30,30])
y1 = w1*x1 +b1
plt.plot(x1,y1, 'r')
plt.scatter(X,y)
plt.grid()
plt.show()
5. Fashion MNIST 분류 모델
먼저 GPU가 동작하는지 설정부터 확인한다.
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(device)
cuda # GPU ok!!
1) 데이터 로드
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.5,),(0.5,))])
trainset = datasets.FashionMNIST("/content/",
train = True, download = True,
transform = transform)
testset = datasets.FashionMNIST("/content/",
train = False, download = True,
transform = transform)
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz to /content/FashionMNIST/raw/train-images-idx3-ubyte.gz
100%
26421880/26421880 [00:01<00:00, 24872885.09it/s]
Extracting /content/FashionMNIST/raw/train-images-idx3-ubyte.gz to /content/FashionMNIST/raw
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz to /content/FashionMNIST/raw/train-labels-idx1-ubyte.gz
100%
29515/29515 [00:00<00:00, 273950.97it/s]
Extracting /content/FashionMNIST/raw/train-labels-idx1-ubyte.gz to /content/FashionMNIST/raw
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz to /content/FashionMNIST/raw/t10k-images-idx3-ubyte.gz
100%
4422102/4422102 [00:00<00:00, 7719615.32it/s]
Extracting /content/FashionMNIST/raw/t10k-images-idx3-ubyte.gz to /content/FashionMNIST/raw
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz to /content/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz
100%
5148/5148 [00:00<00:00, 285472.41it/s]
Extracting /content/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz to /content/FashionMNIST/raw
train_loader = DataLoader(trainset, batch_size = 128, shuffle = True, num_workers = 2)
test_loader = DataLoader(testset, batch_size = 128, shuffle = False, num_workers = 2)
images, labels = next(iter(train_loader))
print(images.shape, labels.shape)
(torch.Size([128, 1, 28, 28]), torch.Size([128]))
labels_map = {
0: 'T-Shirt',
1: 'Trouser',
2: 'Pullover',
3: 'Dress',
4: 'Coat',
5: 'Sandal',
6: 'Shirt',
7: 'Sneaker',
8: 'Bag',
9: 'Ankle Boot',
}
figure = plt.figure(figsize = (12,12))
columns, rows = 4,4
for i in range(1, columns * rows + 1):
image = images[i].squeeze()
label_idx = labels[i].item()
label = labels_map[label_idx]
figure.add_subplot(rows, columns, i)
plt.title(label)
plt.axis('off')
plt.imshow(image, cmap = "gray")
plt.show()
2) 모델 정의 및 파라미터
class NeuralNet(nn.Module):
def __init__(self):
super(NeuralNet, self).__init__()
self.conv1 = nn.Conv2d(1,20,3)
self.conv2 = nn.Conv2d(20,40,3)
self.fc1 = nn.Linear(40*5*5, 240)
self.fc2 = nn.Linear(240, 120)
self.fc3 = nn.Linear(120, 84)
self.fc4 = nn.Linear(84,10)
def forward(self, x):
x = F.max_pool2d(F.relu(self.conv1(x)), (2,2))
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, self.num_flat_features(x))
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = F.relu(self.fc3(x))
x = self.fc4(x)
return x
def num_flat_features(self, x):
size = x.size()[1:]
num_features = 1
for s in size:
num_features *= s
return num_features
net = NeuralNet()
print(net)
NeuralNet(
(conv1): Conv2d(1, 20, kernel_size=(3, 3), stride=(1, 1))
(conv2): Conv2d(20, 40, kernel_size=(3, 3), stride=(1, 1))
(fc1): Linear(in_features=1000, out_features=240, bias=True)
(fc2): Linear(in_features=240, out_features=120, bias=True)
(fc3): Linear(in_features=120, out_features=84, bias=True)
(fc4): Linear(in_features=84, out_features=10, bias=True)
)
params = list(net.parameters())
print(len(params))
print(params[0].size())
input = torch.randn(1,1,28,28)
output = net(input)
print(output)
12
torch.Size([20, 1, 3, 3])
tensor([[ 0.0967, 0.0541, 0.0478, -0.0208, -0.1028, 0.0862, -0.0289, -0.0761,
-0.0289, 0.0543]], grad_fn=<AddmmBackward0>)
3) 손실함수와 Optimizer
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr = 0.001, momentum = 0.9) # lr = learning rate
4) 모델 학습
- 배치수 확인
total_batch = len(train_loader)
print(total_batch) # 469
for epoch in range(5):
running_loss = 0.0 # running loss 초기화
for i, data in enumerate(train_loader, 0):
inputs, labels = data
optimizer.zero_grad()
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if i % 100 ==99:
print("Epoch: {}, Iter: {}, Loss: {}".format(epoch + 1, i + 1, running_loss/2000))
469
Epoch: 1, Iter: 100, Loss: 0.11515839791297913
Epoch: 1, Iter: 200, Loss: 0.23012689578533171
Epoch: 1, Iter: 300, Loss: 0.34492256307601926
Epoch: 1, Iter: 400, Loss: 0.4594321113824844
Epoch: 2, Iter: 100, Loss: 0.1136441946029663
Epoch: 2, Iter: 200, Loss: 0.22612131297588348
Epoch: 2, Iter: 300, Loss: 0.335495311498642
Epoch: 2, Iter: 400, Loss: 0.4343601332306862
Epoch: 3, Iter: 100, Loss: 0.058922575682401654
Epoch: 3, Iter: 200, Loss: 0.10821666920185088
Epoch: 3, Iter: 300, Loss: 0.15261925032734872
Epoch: 3, Iter: 400, Loss: 0.19333434492349624
Epoch: 4, Iter: 100, Loss: 0.03691924387216568
Epoch: 4, Iter: 200, Loss: 0.07250896961987019
Epoch: 4, Iter: 300, Loss: 0.1064168707728386
Epoch: 4, Iter: 400, Loss: 0.13989421248435974
Epoch: 5, Iter: 100, Loss: 0.03280985563993454
Epoch: 5, Iter: 200, Loss: 0.06475198629498481
Epoch: 5, Iter: 300, Loss: 0.09624279655516148
Epoch: 5, Iter: 400, Loss: 0.12776464824378492
5) 모델의 저장 및 로드
torch.save
:net.state_dict()
를 저장torch.load
:load_state_dict
로 모델을 로드
PATH = './fashion_mnist.pth'
torch.save(net.state_dict(), PATH)
net = NeuralNet()
print(net.load_state_dict(torch.load(PATH)))
print(net.parameters)
<All keys matched successfully>
<bound method Module.parameters of NeuralNet(
(conv1): Conv2d(1, 20, kernel_size=(3, 3), stride=(1, 1))
(conv2): Conv2d(20, 40, kernel_size=(3, 3), stride=(1, 1))
(fc1): Linear(in_features=1000, out_features=240, bias=True)
(fc2): Linear(in_features=240, out_features=120, bias=True)
(fc3): Linear(in_features=120, out_features=84, bias=True)
(fc4): Linear(in_features=84, out_features=10, bias=True)
)>
6) 모델 테스트
def imshow(image):
image = image / 2 + 0.5
npimg = image.numpy()
fig = plt.figure(figsize = (16, 8))
plt.imshow(np.transpose(npimg, (1,2,0)))
plt.show()
import torchvision
dataiter = iter(test_loader)
images, labels = next(dataiter)
imshow(torchvision.utils.make_grid(images[:6]))
outputs = net(images)
_, predicted = torch.max(outputs, 1)
print(predicted)
print(''.join('{},'.format(labels_map[int(predicted[j].numpy())]) for j in range(6)))
correct = 0
total = 0
tensor([9, 2, 1, 1, 2, 1, 2, 2, 5, 7, 4, 5, 5, 3, 4, 1, 2, 2, 8, 0, 2, 7, 7, 5,
1, 2, 2, 3, 9, 4, 8, 8, 3, 6, 8, 0, 7, 5, 7, 9, 0, 1, 3, 9, 2, 5, 2, 1,
2, 2, 2, 2, 5, 2, 4, 2, 8, 4, 8, 0, 7, 7, 8, 5, 1, 1, 6, 3, 7, 8, 7, 0,
2, 6, 4, 3, 1, 2, 8, 4, 1, 8, 5, 9, 5, 0, 3, 2, 0, 2, 5, 3, 4, 7, 1, 8,
0, 1, 4, 2, 3, 4, 7, 6, 7, 8, 5, 9, 9, 4, 6, 5, 7, 0, 5, 2, 8, 4, 7, 2,
0, 0, 9, 9, 0, 0, 8, 4])
Ankle Boot,Pullover,Trouser,Trouser,Pullover,Trouser,
with torch.no_grad(): ## test, 평가하는거지 gradient 업데이트 안함
for data in test_loader:
images, labels = data
outputs = net(images)
_, predicted = torch.max(outputs.data, 1) ## 각 열마다 최댓값의 위치를 예측
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(100*correct / total)
76.35
참고로 _, predicted = torch.max(outputs.data, 1)
이 부분에서, 언더바(_,
) 이 부분은 출력값을 저장하지 않겠다는 것이다. 즉, torch.max
는 최댓값과 최댓값의 위치를 산출해주는데, 여기서 최댓값을 필요 없으므로 받이 않겠다는 것이다. 따라서 언더바로 처리하여 해당 출력ㄱ밧은 저장하지 않겠다는 의미이다. 즉, predictted에 최댓값의 위치만 저장하겠다는 의미이다.
그리고 _, predicted = torch.max(outputs.data, 1)
이 부분에서 마찬가지로 .data
를 사용하는 이유는 역전파 계산이 필요없기 때문에 데이터만 사용한다는 의미이다.
댓글 남기기