printf("ho_tari\n");

ep.46 파이토치(PyTorch) 본문

두산 로보틱스 부트캠프 ROKEY/Computer Vision 교육

ep.46 파이토치(PyTorch)

호타리 2024. 9. 10. 10:01

2024.9.10

 

파이토치(PyTorch)

  • 페이스북이 초기 루아(Lua) 언어로 개발된 토치(Torch)를 파이썬 버전으로 개발하여 2017년도에 공개
  • 초기에 토치(Torch)는 넘파이(NumPy) 라이브러리처럼 과학 연산을 위한 라이브러리로 공개
  • 이후 GPU를 이용한 텐서 조작 및 동적 신경망 구축이 가능하도록 딥러닝 프레임워크로 발전시킴
  • 파이썬답게 만들어졌고, 유연하면서도 가속화된 계산 속도를 제공

파이토치 모듈 구조

파이토치의 구성요소

  • torch: 메인 네임스페이스, 텐서 등의 다양한 수학 함수가 포함
  • torch.autograd: 자동 미분 기능을 제공하는 라이브러리
  • torch.nn: 신경망 구축을 위한 데이터 구조나 레이어 등의 라이브러리
  • torch.multiprocessing: 병럴처리 기능을 제공하는 라이브러리
  • torch.optim: SGD(Stochastic Gradient Descent)를 중심으로 한 파라미터 최적화 알고리즘 제공
  • torch.utils: 데이터 조작 등 유틸리티 기능 제공
  • torch.onnx: ONNX(Open Neural Network Exchange), 서로 다른 프레임워크 간의 모델을 공유할 때 사용

텐서(Tensors)

  • 데이터 표현을 위한 기본 구조로 텐서(tensor)를 사용
  • 텐서는 데이터를 담기위한 컨테이너(container)로서 일반적으로 수치형 데이터를 저장
  • 넘파이(NumPy)의 ndarray와 유사
  • GPU를 사용한 연산 가속 가능

import torch

torch.__version__

텐서 초기화와 데이터 타입

초기화 되지 않은 텐서

x = torch.empty(4,2)    # 쓰레기 값
print(x)

무작위로 초기화된 텐서

x = torch.rand(4,2)     # 랜덤값
print(x)

데이터 타입(dtype)이 long이고, 0으로 채워진 텐서

# int64
x = torch.zeros(4,2, dtype=torch.long)
print(x)

사용자가 입력한 값으로 텐서 초기화

x = torch.tensor([3,2.3])
print(x)

2 x 4 크기, double 타입, 1로 채워진 텐서

x = torch.ones(2,4,dtype=torch.double)
#x = x.new_ones(2,4, dtype=torch.double)
print(x)

x와 같은 크기, float 타입, 무작위로 채워진 텐서

# float32
x = torch.rand_like(x, dtype=torch.float)
print(x)

텐서의 크기 계산

print(x.size())

데이터 타입(Data Type)

# float32
ft = torch.FloatTensor([1,2,3])
print(ft)
print(ft.dtype)

# type casting, ft는 현재 float32
print(ft.short())
print(ft.int())
print(ft.long())

# 정수 int32
it = torch.IntTensor([1,2,3])
print(it)
print(it.dtype)

print(it.float())
print(it.double())
print(it.half())

CUDA Tensors

  • .to 메소드를 사용하여 텐서를 어떠한 장치(cpu, gpu)로도 옮길 수 있음
x = torch.randn(1)
print(x)
print(x.item()) # python scalar 값 얻음. tensor에 하나의 값이 아니라 여러개가 존재한다면 사용 불가능
print(x.dtype)

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(device)
y = torch.ones_like(x, device = device)
print(y)
x = x.to(device)
print(x)
z = x + y
print(z)
print(z.to('cpu', torch.double))

다차원 텐서 표현

0D Tensor(Scalar)

  • 하나의 숫자를 담고 있는 텐서(tensor)
  • 축과 형상이 없음
t0 = torch.tensor(0)
print(t0.ndim)
print(t0.shape)
print(t0)

1D Tensor(Vector)

  • 값들을 저장한 리스트와 유사한 텐서
  • 하나의 축이 존재
t1 = torch.tensor([1,2,3])
print(t1.ndim)
print(t1.shape)
print(t1)

2D Tensor(Matrix)

  • 행렬과 같은 모양으로 두개의 축이 존재
  • 일반적인 수치, 통계 데이터셋이 해당
  • 주로 샘플(samples)과 특성(features)을 가진 구조로 사용

t2 = torch.tensor([[1,2,3],
                   [4,5,6],
                   [7,8,9]])
print(t2.ndim)
print(t2.shape)
print(t2)

3D Tensor

  • 큐브(cube)와 같은 모양으로 세개의 축이 존재
  • 데이터가 연속된 시퀀스 데이터나 시간 축이 포함된 시계열 데이터에 해당
  • 주식 가격 데이터셋, 시간에 따른 질병 발병 데이터 등이 존재
  • 주로 샘플(samples), 타임스텝(timesteps), 특성(features)을 가진 구조로 사용

t3 = torch.tensor([[[1,2,3],
                   [4,5,6],
                   [7,8,9]],
                  [[1,2,3],
                   [4,5,6],
                   [7,8,9]],
                  [[1,2,3],
                   [4,5,6],
                   [7,8,9]]])
print(t3.ndim)
print(t3.shape)
print(t3)

4D Tensor

  • 4개의 축
  • 컬러 이미지 데이터가 대표적인 사례 (흑백 이미지 데이터는 3D Tensor로 가능)
  • 주로 샘플(samples), 높이(height), 너비(width), 컬러 채널(channel)을 가진 구조로 사용

5D Tensor

  • 5개의 축
  • 비디오 데이터가 대표적인 사례
  • 주로 샘플(samples), 프레임(frames), 높이(height), 너비(width), 컬러 채널(channel)을 가진 구조로 사용

텐서의 연산(Operations)

  • 텐서에 대한 수학 연산, 삼각함수, 비트 연산, 비교 연산, 집계 등 제공
import math
a = torch.rand(1,2) * 2 - 1
print(a)
print(torch.abs(a))
print(torch.ceil(a))    # 현재값보다 큰 정수값
print(torch.floor(a))   # 현재값보다 작은 정수값
print(torch.clamp(a,-0.4,0.4))

print(a)
print(torch.min(a))
print(torch.max(a))
print(torch.mean(a))
print(torch.std(a))   # 표준편차
print(torch.prod(a))  # 요소의 곱
print(torch.unique(torch.tensor([1,2,3,1,2,2])))  # 중복값 제거

max와 min은 dim 인자를 줄 경우 argmax와 argmin도 함께 리턴

  • argmax: 최대값을 가진 인덱스
  • argmin: 최소값을 가진 인덱스
x = torch.rand(2,2)
print(x)
print(x.max(dim=0))
print(x.max(dim=1))

print(x)
print('---')
print(x.min(dim=0))
print('---')
print(x.min(dim=1))

x = torch.rand(2,2)
print(x)
y = torch.rand(2,2)
print(y)

torch.add: 덧셈

print(x+y)
print(torch.add(x,y))

결과 텐서를 인자로 제공

result = torch.empty(2,4)
print(result)
torch.add(x,y, out=result)
print(result)

in-place 방식

  • in-place방식으로 텐서의 값을 변경하는 연산 뒤에는 _''가 붙음
  • x.copy_(y), x.t_()
print(x)
print(y)
y.add_(x)   # y.add(x)
print(y)

torch.sub: 뺄셈

print(x)
print(y)
print(x-y)
print(torch.sub(x,y))
print(x.sub_(y))   # y.sub(x)

torch.mul: 곱셉

print(x)
print(y)
print(x * y)
print(torch.mul(x,y))
print(x.mul(y))   # y.mul_(x)

torch.div: 나눗셈

print(x)
print(y)
print(x / y)
print(torch.div(x,y))
print(x.div(y))

torch.mm: 내적(dot product)

print(x)
print(y)
print(torch.matmul(x,y))
z = torch.mm(x,y)
print(z)
print(torch.svd(z))

텐서의 조작(Manipulations)

인덱싱(Indexing): NumPy처럼 인덱싱 형태로 사용가능

x = torch.Tensor([[1,2],
                 [3,4]])
print(x)

print(x[0,0])
print(x[0,1])
print(x[1,0])
print(x[1,1])

print(x[:,0])
print(x[1,:])

view: 텐서의 크기(size)나 모양(shape)을 변경

  • 기본적으로 변경 전과 후에 텐서 안의 원소 개수가 유지되어야 함
  • -1로 설정되면 계산을 통해 해당 크기값을 유추
x = torch.randn(4,5)
print(x)
y = x.view(20)    # 1차원 reshape
print(y)
z = x.view(5,-1)  # 2차원 reshape
print(z)

item: 텐서에 값이 단 하나라도 존재하면 숫자값을 얻을 수 있음

x = torch.randn(1)
print(x)
print(x.item())
print(x.dtype)

스칼라값 하나만 존재해야 item() 사용 가능

x = torch.randn(2)
print(x)
# print(x.item())  # 요소가 하나일 경우에 item 사용 가능
print(x.dtype)

squeeze: 차원을 축소(제거)

tensor = torch.rand(1,3,3)
print(tensor)
print(tensor.shape)

t = tensor.squeeze()   # 한차원 축소 된 3차원에서 2차원으로
print(t)
print(t.shape)

unsqueeze: 차원을 증가(생성)

t = torch.rand(3,3)
print(t)
print(t.shape)

# t는 2차원
tensor = t.unsqueeze(dim=0)   # 0번축 한차원 증가됨 2차원에서 3차원으로
print(tensor)
print(tensor.shape)

# t는 2차원
tensor = t.unsqueeze(dim=2)  # 2번축 한차원 증가됨 2차원에서 3차원으로
print(tensor)
print(tensor.shape)

stack: 텐서간 결합

x = torch.FloatTensor([1,4])
print(x)
y = torch.FloatTensor([2,5])
print(y)
z = torch.FloatTensor([3,6])
print(z)

print(torch.stack([x,y,z]))

cat: 텐서를 결합하는 메소드(concatenate)

  • 넘파이의 stack과 유사하지만, 쌓을 dim이 존재해야함
  • 해당 차원을 늘려준 후 결합
 a = torch.randn(1,3,3)
 print(a)
 b = torch.randn(1,3,3)
 print(b)
 c=torch.cat((a,b),dim=0)
 print(c)
 print(c.size())
 
  c=torch.cat((a,b),dim=1)
 print(c)
 print(c.size())
 
  c=torch.cat((a,b),dim=2)
 print(c)
 print(c.size())

chunk: 텐서를 여러 개로 나눌 때 사용 (몇 개로 나눌 것인가?)

tensor = torch.rand(3,6)
print(tensor)

t1,t2,t3 = torch.chunk(tensor,3,dim=1)
print(t1)
print(t2)
print(t3)

split: chunk와 동일한 기능이지만 조금 다름 (텐서의 크기는 몇인가?)

tensor = torch.rand(3,6)
t1,t2 = torch.split(tensor,3,dim=1)

print(tensor)
print(t1)
print(t2)

torch ↔ numpy

  • Torch Tensor(텐서)를 NumPy array(배열)로 변환 가능
    • numpy()
    • from_numpy()
  • Tensor가 CPU상에 있다면 NumPy 배열은 메모리 공간을 공유하므로 하나가 변하면, 다른 하나도 변함
a = torch.ones(7)
print(a)

b = a.numpy()
print(b)

a.add_(1)
print(a)
print(b)

import numpy as np

a = np.ones(7)
b = torch.from_numpy(a)
np.add(a,1,out=a)  # add_()
print(a)
print(b)

Autograd(자동미분)

  • torch.autograd 패키지는 Tensor의 모든 연산에 대해 자동 미분 제공
  • 이는 코드를 어떻게 작성하여 실행하느냐에 따라 역전파가 정의된다는 뜻
  • backprop를 위해 미분값을 자동으로 계산

requires_grad 속성을 True로 설정하면, 해당 텐서에서 이루어지는 모든 연산들을 추적하기 시작

기록을 추적하는 것을 중단하게 하려면, .detach()를 호출하여 연산기록으로부터 분리

a = torch.randn(3,3)
a = a * 3
print(a)
print(a.requires_grad)

requires_grad_(...)는 기존 텐서의 requires_grad 값을 바꿔치기(in-place)하여 변경

grad_fn: 미분값을 계산한 함수에 대한 정보 저장 (어떤 함수에 대해서 backprop 했는지)

a.requires_grad_(True)
print(a.requires_grad)

b = (a * a).sum()
print(b)
print(b.grad_fn)

기울기(Gradient)

x = torch.ones(3,3,requires_grad=True)
print(x)

y = x+5
print(y)

z = y * y
out = z.mean()
print(z,out)

계산이 완료된 후, .backward()를 호출하면 자동으로 역전파 계산이 가능하고, .grad 속성에 누적됨

print(out)
out.backward()
print(out)

grad: data가 거쳐온 layer에 대한 미분값 저장

print(x)
print(x.grad)

x = torch.randn(3,requires_grad=True)

y = x * 2
while y.data.norm() < 1000:   # 절대값의 합 1000보다 작은 경우 계속 2를 곱함
    y = y * 2

print(y)

v = torch.tensor([0.1,1.0,0.0001], dtype=torch.float)
y.backward(v)

print(x.grad)

with torch.no_grad()를 사용하여 기울기의 업데이트를 하지 않음

기록을 추적하는 것을 방지하기 위해 코드 블럭을 with torch.no_grad()로 감싸면 기울기 계산은 필요없지만, requires_grad=True로 설정되어 학습 가능한 매개변수를 갖는 모델을 평가(evaluate)할 때 유용

print(x.requires_grad)
print((x ** 2).requires_grad)

with torch.no_grad():   # 기울기를 업데이트 하지 않음
    print((x ** 2).requires_grad)

detach(): 내용물(content)은 같지만 require_grad가 다른 새로운 Tensor를 가져올 때

print(x.requires_grad)
y = x.detach()
print(y.requires_grad)
print(x.eq(y).all())

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)

b=a+2

# b는 더하기 연산, 자동으로 더하기 미분이 등록됨
b = a + 2
print(b)

# c는 6에 자승 연산을 함. 자동으로 자승 미분이 등록됨
c = b ** 2
print(c)

out = c.sum()
print(out)

print(out)
out.backward()

a의 grad_fn이 None인 이유는 직접적으로 계산한 부분이 없었기 때문

print(a.data)
print(a.grad)
print(a.grad_fn)

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)

데이터 준비

파이토치에서는 데이터 준비를 위해 torch.utils.data의 Dataset과 DataLoader 사용 가능

from torch.utils.data import Dataset, DataLoader

토치비전(torchvision)은 파이토치에서 제공하는 데이터셋들이 모여있는 패키지

import torchvision.transforms as transforms
from torchvision import datasets

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='/conten/',
                          train=True,download=True,
                          transform = mnist_transform)

testset = datasets.MNIST(root='/conten/',
                          train=False,download=True,
                          transform = mnist_transform)

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 = dataiter.next()
images.shape, labels.shape

torch_image = torch.squeeze(images[0])
torch_image.shape

import matplotlib.pyplot as plt

figure = plt.figure(figsize=(12,6))
cols, rows = 4, 2
for i in range(1, cols*rows+1):
    sample_idx = torch.randint(len(trainset),size=(1,)).item()
    img, label = trainset[sample_idx]
    figure.add_subplot(rows,cols,i)
    plt.title(label)
    plt.axis('off')
    plt.imshow(img.squeeze(),cmap='gray')
plt.show()

신경망 구성

  • 레이어(layer): 신경망의 핵심 데이터 구조로 하나 이상의 텐서를 입력받아 하나 이상의 텐서를 출력
  • 모듈(module): 한 개 이상의 계층이 모여서 구성
  • 모델(model): 한 개 이상의 모듈이 모여서 구성

torch.nn 패키지

주로 가중치(weights), 편향(bias)값들이 내부에서 자동으로 생성되는 레이어들을 사용할 때 사용 (weight값들을 직접 선언 안함)

import torch.nn as nn

nn.Linear 계층 예제

input = torch.randn(128,20)
print(input)

m = nn.Linear(20,30)
print(m)

output = m(input)
print(output)
print(output.size())

nn.Conv2d 계층 예시

input = torch.randn(20,16,50,100)
print(input.size())

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))
print(m)

output = m(input)
print(output.size())

컨볼루션 레이어(Convolution Layers)

nn.Conv2d 예제

  • in_channels: channel의 갯수
  • out_channels: 출력 채널의 갯수
  • kernel_size: 커널(필터) 사이즈
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

weight 확인

weight = layer.weight
weight.shape

weight detach()를 통해 꺼내줘야 numpy()변환이 가능

weight = weight.detach()

weight = weight.numpy()
weight.shape

plt.imshow(weight[0,0,:,:],'jet')
plt.colorbar()
plt.show()

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()
output_arr.shape

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')
plt.show()

풀링 레이어(Pooling layers)

  • F.max_pool2d
    • stride
    • kernel_size
  • torch.nn.MaxPool2d 도 많이 사용
import torch.nn.functional as F

pool = F.max_pool2d(output,2,2)
pool.shape
  • MaxPool Layer는 weight가 없기 때문에 바로 numpy()변환 가능
pool_arr = pool.numpy()
pool_arr.shape

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')
plt.show()

선형 레이어(Linear layers)

1d만 가능하므로 .view()를 통해 1d로 펼쳐줘야함

flatten = input_image.view(1,28*28)
flatten.shape

lin = nn.Linear(784,10)(flatten)
lin.shape

lin

plt.imshow(lin.detach().numpy(),'jet')
plt.colorbar()
plt.show()

비선형 활성화 (Non-linear Activations)

F.softmax와 같은 활성화 함수 등

with torch.no_grad():
    flatten = input_image.view(1,28*28)
    lin = nn.Linear(784,10)(flatten)
    softmax = F.softmax(lin,dim=1)

softmax

np.sum(softmax.numpy())

F.relu

  • ReLU 함수를 적용하는 레이어
  • nn.ReLU로도 사용 가능
inputs = torch.randn(4,3,28,28).to(device)
inputs.shape

layer = nn.Conv2d(3,20,5,1).to(device)
output = F.relu(layer(inputs))
output.shape

신경망 종류

모델 정의

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()))
print(list(model.modules()))

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)
        )

        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(list(model.modules()))

모델 파라미터

손실 함수(Loss function)

  • 예측 값과 실제 값 사이의 오차 측정
  • 학습이 진행되면서 해당 과정이 얼마나 잘 되고 있는지 나타내는 지표
  • 모델이 훈련되는 동안 최소화될 값으로 주어진 문제에 대한 성공 지표
  • 손실 함수에 따른 결과를 통해 학습 파라미터를 조정
  • 최적화 이론에서 최소화 하고자 하는 함수
  • 미분 가능한 함수 사용
  • 파이토치의 주요 손실 함수
    • torch.nn.BCELoss: 이진 분류를 위해 사용
    • torch.nn.CrossEntropyLoss: 다중 클래스 분류를 위해 사용
    • torch.nn.MSELoss: 회귀 모델에서 사용
criterion = nn.MSELoss()
criterion = nn.CrossEntropyLoss()

티마이저(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

학습률 스케줄러(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: 학습이 잘되는지 아닌지에 따라 동적으로 학습률 변화

지표(Metrics)

  • 모델의 학습과 테스트 단계를 모니터링
!pip install torchmetrics

import torchmetrics

preds = torch.randn(10,5).softmax(dim=-1)
target = torch.randint(5,(10,))
print(preds, target)

acc = torchmetrics.functional.accuracy(preds, target)
print(acc)

metric = torchmetrics.Accuracy()

n_batches = 10
for i in range(n_batches):
    preds = torch.randn(10,5).softmax(dim=-1)
    ratget = torch.randint(5,(10,))

    acc = metric(preds, target)
    print(acc)

acc = metric.compute()
print(acc)

선형 회귀 모델(Linear Regression Model)

데이터 생성

X = torch.randn(200,1)*10
y = X + 3 * torch.randn(200,1)
plt.scatter(X.numpy(), y.numpy())
plt.ylabel('y')
plt.xlabel('x')
plt.grid()
plt.show()

모델 정의 및 파라미터

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()))

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()

손실 함수 및 옵티마이저

import torch.optim as optim

criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(),lr=0.001)

모델 학습

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()

FashionMNIST 분류 모델

GPU 설정

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
device

데이터 로드

transform = transforms.Compose([transforms.ToTensor(),
                               transforms.Normalize((0.5,),(0.5,))])
                               
trainset = datasets.FashionMNIST(root = '/content/',
                                 train=True,download=True,
                                 transform=transform)
testset = datasets.FashionMNIST(root = '/content/',
                                 train=False,download=True,
                                 transform=transform)
                                 
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))
images.shape, labels.shape

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))
cols,rows = 4,4
for i in range(1,cols*rows+1):
    image = images[i].squeeze()
    label_idx = labels[i].item()
    label = labels_map[label_idx]

    figure.add_subplot(rows,cols,i)
    plt.title(label)
    plt.axis('off')
    plt.imshow(image,cmap='gray')
plt.show()

모델 정의 및 파라미터

class NeuralNet(nn.Module):
    def __init__(self):
        super(NeuralNet,self).__init__()

        self.conv1 = nn.Conv2d(1,6,3)
        self.conv2 = nn.Conv2d(6,16,3)
        self.fc1 = nn.Linear(16*5*5, 120)
        self.fc2 = nn.Linear(120,84)
        self.fc3 = 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 = self.fc3(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)

params = list(net.parameters())
print(len(params))
print(params[0].size())

input = torch.randn(1,1,28,28)
out = net(input)
print(out)

손실함수와 옵티마이저

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(),lr=0.001,momentum=0.9)

모델 학습

배치수 확인

total_batch = len(train_loader)
print(total_batch)

for epoch in range(10):
    running_loss =0.0
    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 : {}, lter: {}, Loss: {}'.format(epoch+1, i+1, running_loss/2000))
            running_loss = 0.0

모델의 저장 및 로드

  • torch.save: net.state_dict()를 저장
  • torch.load: load_state_dict로 모델을 로드
PATH = './fashion_mnist.pth'
torch.save(net.state_dict(),PATH)

net = NeuralNet()
net.load_state_dict(torch.load(PATH))

net.parameters

모델 테스트

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 = dataiter.next()

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

with torch.no_grad():
    for data in test_loader:
        images, labels = data
        outputs = net(images)
        outputs = net(images)
        _,predicted = torch.max(outputs.data,1)
        total += labels.size(0)
        correct+=(predicted == labels).sum().item()
print(100*correct/total)

딥러닝_파이토치(PyTorch).ipynb
2.08MB
DR-01408_박성호_컴퓨터비전응용_5차시.ipynb
1.52MB