인공지능 & Pytorch_입문(Tensor, Backpropagation, Data Loader)
-
python을 기본적으로 사용합니다
-
Gpu을 사용하기 위하여 Goole에서 이용할 수 있는 Colab을 이용해서 실행
인공지능을 위한 pytorch 배우기
1.Tensor(텐서) : pytorch 의 기본단위이며 GPU 계산 을 할 수 있게 해준다 numpy와 상당히 유사하다.
1.1 Tensor 생성 : 라이브러리 불러오기
import torch # Pytorch를 사용하기 위한 기본 라이브러리
import numpy as np #Numpy를 사용하기 위한 기본 라이브러리 np로 줄여서 사용
X = torch.empty(5,4) # 5X4 행렬 생성
print(X)
X = torch.ones(3,3) # 원소가 모두 1인 3X3 행렬 생성
print(X)
X = torch.zeros(2) # 원소가 모두 0인 2행 Vector
print(X)
X = torch.rand(5,6) # 표준정규분포를 따르는 무작위 원소의 5X6 행렬 생성
print(X)
1.2 List, Numpy 배열을 Tensor로 만들기
리스트 = [3,4] # list 생성
넘파이 = np.array([4,50,7]) # numpy 배열 생성
torch.tensor(리스트) # List → Tensor
torch.tensor(넘파이) # Numpy → Tensor
1.3 Tensor의 크기와 타입 확인하기
X.size() #.size()는 Tensor의 크기를 확인할 수 있다.
type(X) # type은 Python에서 사용되는 모든 것들을 종류로 보여준다.
1.4 Tensor의 연산
# 크기가 같아야 한다.
X = torch.rand(2,2) # 2X2 랜덤 행렬
Y = torch.rand(2,2) # 2X2 랜덤 행렬
print("X:",X)
print("Y:",Y)
X+Y # 첫번째 표현
torch.add(X,Y) # 두번째 표현
Y.add_(X) #세번째 표현이며 Y = X+Y 와 같은의미이며 inplace 방식이라고 한다.
print("기존 Y:",Y) # 기존 Y값
Y.add_(X) # Y = X+Y 연산
print("inplace Y:",Y) # 대체된 Y값
1.5 Tensor의 크기 변환
# view 함수를 사용하여 크기를 변환
X = torch.rand(8,8) # 8X8랜덤 행렬
print("size of X:",X.size()) # size 확인
A = X.view(64) # 8X8 행렬을 64개의 Vector로 바꿔준다
print("size of A:",A.size()) # size 확인
B = X.view(-1,4,4)
''' -1은 다른 인자를 우선으로 크기로 맞춘 뒤 남는 부분을 자동으로 맞춰달라는 의미
→ 8X8 행렬을 -1X4X4로 표현하려면 -1자리에는 4가 들어가서 4X4X4 행렬이 되어야 한다. 따라서 -1자리에는 4가 자동으로 맞춰짐
'''
print("size of B:",B.size()) #size확인
1.6 Tensor를 Numpy 만들기
X = torch.rand(4,4) # 4X4 랜덤 행렬
Y = X.numpy() # Tensor → Numpy
print("Y :",Y) # Y값 확인
type(Y) # Y의 타입 확인
1.7 단일 Tensor에서 값 추출
X = torch.ones(1) # 원소가 1인 Vecotr하나 생성
print("X:",X)
print(X.item()) # 원소가 하나일 때만 사용가능하며 tensor안에 하나의 스칼라 값을 가져온다. 텐서(행렬)가 아님
# loss 값을 저장하기 위함
2.Backpropagation(역전파) : 인공 신경망을 최적화 하는 과정에서 미분은 필수적인 요소이다 pytorch는 최적화 과정인 역전파(Backpropagation)를 쉽게 할 수 있도록 자동 미분 계산을 제공한다.
2.1 자동 미분 준비
#라이브러리 불러오기
import torch
# requires_grad=True는 해당 텐서를 기준으로 모든 연산들을 추적할 수 있게 하는 옵션이다.
x = torch.ones(2,2, requires_grad=True) # 2x2 tensor 생성 자동미분
print(x)
y = x+1 # y는 x에 관한 식
z = 2*y**2 # z는 y에 관한 식
result = z.mean() # result는 z에 관한 식이다.
print("y:", y)
print("z:", z)
print("Result:", result)
# grad_fn=..은 추적이 잘 되고 있다는 뜻
2.2 역전파
result.backward() # result를 기준으로 역전파 진행
# 위에 식을 역으로 써내려 가보면
# result = (z_1 +... z_4)/4 → x는 2X2행렬이므로 총 4개의 원소이므로
# z_i = 2 y_i **2
# z_i = 2(x_i+1) **2
# d(result)/dx_i = x_i + 1
print(x)
print(x.grad)
# backward()가 선언 된 변수를 기준으로 미분 d(result)/dx를 계산
# d(result)/dx_i = x_i + 1
그림을 보면 코드를 이해하기 쉽다!
3 데이터 불러오기 : 머신러닝의 근원은 데이터이다. 따라서 데이터의 수집,가공,사용 방법에 따라 모델 성능이 크게 달라질 수 있으며 데이터의 형태는 매우 다양하기 때문에 데이터 전처리를 가장 중요한 단계 중 하나이다.
import torch # 기본 라이브러리
import torchvision # 이미지 관련 라이브러리
import torchvision.transforms as tr # 이미지 전처리 기능들을 제공하는 라이브러리
from torch.utils.data import DataLoader, dataset # 데이터를 모델에 사용할 수 있도록 정리해 주는 라이브러리
import numpy as np # 넘파이 기본 라이브러리
3.1 Pytorch 제공 Data 사용
# https://pytorch.org/vision/stable/transforms.html 에서 여러 전처리 방법 확인 가능.
# tr.Compose 내에 원하는 전처리를 넣어준다.
transf = tr.Compose([tr.Resize(16), tr.ToTensor()]) # 16x16으로 resize 이후 Tensor 타입으로 변환
# Transforms on PIL Image
# Pad, Grayscale, RandomCrop, Normalize ..
# Transfroms on torch. *Tensor - tensor image
# torchvision.transforms.ToPILImage(mode=None)...
# ...
# https://pytorch.org/vision/stable/datasets.html 에서 다양한 이미지 데이터셋을 확인할 수 있다.
# torchvision.datasets에서 제공하는 CIFAR10 데이터를 불러온다.
# root에는 다운로드 받을 경로를 입력→ 자신의 폴더 경로를 선택해야 함
# train=Ture이면 학습 데이터를 불러오고 train=False이면 테스트 데이터를 불러온다.
# 미리 선언한 전처리를 사용하기 위해 transform=transf을 작성.
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transf)
testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transf)
# 일반적으로 데이터셋은 이미지와 라벨이 동시에 들어있는 튜플(tuple) 형태다. (이미지, 라벨)
# trainset[0]은 학습 데이터의 첫 번째 데이터로 이미지 한 장과 라벨 숫자 하나가 저장되어 있다.
# trainset[0][0]은 이미지이며 trainset[0][1]은 라벨이다.
print(trainset[0][0].size())
# 현재 이미지 사이즈는 3x16x16이다. 여기서 3은 채널 수를 말하고 16x16은 이미지의 너비와 높이를 의미한다.
# 일반적인 컬러 사진은 RGB 이미지이기 때문에 채널이 3개 이고 (너비)x(높이)x(채널 수)로 크기가 표현된다.
# 하지만 파이토치에서는 이미지 한 장이 (채널 수)x(너비)x(높이)으로 표현되니 유의하도록 한다.
# DataLoader는 데이터를 미니 배치 형태로 만들어 준다.
# 따라서 배치 사이즈 및 셔플 여부 등을 선택할 수 있다.
trainloader = DataLoader(trainset, batch_size=50, shuffle=True)
testloader = DataLoader(testset, batch_size=50, shuffle=False)
len(trainloader)
# CIFAR10의 학습 이미지는 50,000장이고 배치 사이즈가 50장이므로 1,000은 배치의 개수가 된다.
# 즉 trainloader가 잘 만들어졌다는 것을 단편적으로 알 수 있다.
# iter, next를 이용해 일부 데이터를 확인할 수 있다.
dataiter = iter(trainloader)
images, labels = dataiter.next()
print(images.size())
# 일반적으로 학습 데이터는 4차원 형태로 모델에서 사용된다.
# (배치 크기)x(채널 수)x(너비)x(높이)
3.2 같은 클래스 별로 폴더를 정리한 경우
우선 colab에 마운트 하는 과정이 필요하다.
from google.colab import drive
drive.mount('/content/gdrive')
cd/content/gdrive/My Drive/deeplearningbro/pytorch
# 데이터가 같은 클래스 별로 미리 폴더를 정리 된 경우, ImageFolder의 1줄 선언으로 개인 데이터를 사용할 수 있다.
# 별도의 라벨링이 필요 없으며 폴더 별로 자동으로 라벨링을 한다.
# 예를 들어 class 폴더에 tiger, lion 폴더(./class/tiger와 ./class/lion)를 미리 만든다.
# 다음으로 ImageFolder에 상위 폴더 ./class를 입력하면 이미지와 라벨이 정리 되어 데이터를 불러온다.
transf = tr.Compose([tr.Resize((128, 128)),tr.ToTensor()]) # 128x128 이미지 크기 변환 후 텐서로 만든다.
trainset = torchvision.datasets.ImageFolder(root='./class', transform=transf) # 커스텀 데이터 불러온다.
trainloader = DataLoader(trainset, batch_size=2, shuffle=False) # 데이터를 미니 배치 형태로 만들어 준다.
dataiter = iter(trainloader)
images, labels = dataiter.next()
print(images.size(), labels)
3.3 정형화되지 않은 커스텀 데이터 불러오기 (3.2를 사용하지 못할 때)
- 라벨 별로 아름답게 폴더 정리가 되어 있으면 매우 좋겠지만 그렇지 않은 경우가 매우 많다.
- 다른 작업들과 공유 된 데이터인 경우 폴더를 함부로 정리할 수 없다.
- 이미지 데이터라도 이미지가 아닌 텍스트, 리스트, 배열 등으로 저장 되어 있는 경우도 있다.
#32x32 컬러 이미지와 라벨이 각각 100장이 있다고 가정하다.
train_images = np.random.randint(256,size=(100,32,32,3)) # (이미지 수)x(너비)x(높이)x(채널 수)
train_labels = np.random.randint(2,size=(100,1)) # 라벨 수
# 이미지 전처리 작업이 필요할 경우 openCV와 같은 라이브러리를 이용하여 이 곳에서 작업할 수도 있다.
# 필자는 이 단계에서 전처리하는 것을 선호한다. 그 이유는 torchvision.transforms 라이브러리 보다
# OpenCV, SciPy와 같은 라이브러리가 더 많은 전처리 기술을 제공하며 이미지를 미리 처리해 놓고 전처리 된 이미지를 살펴보면서
# 작업하는 것을 좋아하기 때문이다. 따라서 사용 목적과 편의성에 맞게 본인이 전처리를 어디서 할 지 정하면 될 것이다.
#......
#......
#......
#train_images, train_labels = preprocessing(train_images, train_labels)
#......
#......
#......
print(train_images.shape, train_labels.shape)
"""
from torch.utils.data import Dataset
class MyDataset(Dataset):
def __init__(self):
def __getitem__(self, index):
def __len__(self):
이 양식을 통으로 가지고 다니자!!
"""
class TensorData(Dataset):
def __init__(self, x_data, y_data):
self.x_data = torch.FloatTensor(x_data) # 이미지 데이터를 FloatTensor로 변형
self.x_data = self.x_data.permute(0,3,1,2) # (이미지 수)x(너비)x(높이)x(채널 수) -> (배치 크기)x(채널 수)x(너비)x(높이)
self.y_data = torch.LongTensor(y_data) # 라벨 데이터를 LongTensor로 변형
self.len = self.y_data.shape[0] # 클래스 내의 들어 온 데이터 개수
def __getitem__(self, index):
return self.x_data[index], self.y_data[index] # 뽑아 낼 데이터를 적어준다.
def __len__(self):
return self.len # 클래스 내의 들어 온 데이터 개수
# 파이토치에서는 (배치 크기)x(채널 수)x(너비)x(높이) 데이터가 사용 되므로 원래 데이터 (이미지 수)x(너비)x(높이)x(채널 수)를 변경해야만 한다.
# permute에서 0(이미지 수), 1(너비),2 (높이), 3(채널 수)을 0(이미지 수), 3(채널 수), 1(너비),2 (높이)로 바꿔주는 것이기 때문에
# .permute(0,3,1,2)을 사용하는 것이다.
train_data = TensorData(train_images,train_labels) # 텐서 데이터 불러오기
train_loader = DataLoader(train_data, batch_size=10, shuffle=True) # 미니 배치 형태로 데이터 갖추기