▷ 오늘 학습 계획: 파이토치 강의
- Tensor type: Variable과 Constant 포함
data = [[1,2], [3,4]] data_tensor = torch.tensor(data) arr = np.array([[1,2], [3,4]]) arr_tensor = torch.tensor(arr) arr_from_numpy = torch.from_numpy(arr)
속성값 확인
data_tensor.shape data_tensor.size()
torch를 numpy 배열로 바꾸기
data_tensor.numpy() arr_from_numpy.numpy() # torch.from_numpy(values).float() numpy → float
특정한 값의 Tensor 생성
torch.arange(10) torch.ones(5), torch.zeros(5) torch.ones_like(data_tensor) # tensor 입력하기 torch.linspace(0, 10, 5) torch.logspace(0, 10, 5)
난수 생성
torch.rand(5) # 균등 분포 torch.randn(5) # Normal 분포 torch.randint(10, size=(5,)) # seed 조절하기 torch.manual_seed(0)
data type
torch.randint(10, size=(5,), dtype=torch.float32) a = torch.randint(10, size=(5,)) print(a.type(torch.float64)) # a의 값 자체가 변하는게 아님. type이 변화된 a를 return
GPU 사용하기
- tensorflow에서는 사용 가능한 gpu가 있으면 자동으로 사용됨
torch.cuda.is_available() # True/False
1) device 설정하기
# device 설정 x = torch.ones(2, 2, device='cuda') # 여러개의 GPU 중에 할당할 GPU 정하기 # nvidia-smi 명령으로 GPU 번호를 찾을 수 있다. x = torch.ones(2, 2, device = 'cuda:0') # device 객체 입력 x = torch.ones(2, 2, device = torch.device('cuda'))
2) tensor_var.cuda()
a = torch.rand(10) a = a.cuda()
3) tensor_var.to(device)
a = torch.rand(2) a = a.to('cuda') a = a.to(torch.device('cuda'))
- 자주 사용되는 방법
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') a = torch.rand(2) a = a.to(device)
기본 연산
x = torch.arange(0,5) y = torch.arange(1,6) x + y torch.add(x,y) x.add(y)
여러가지 연산
- torch.abs: 절댓값
- torch.sign: 부호
- torch.round
- torch.ceil
- torch.floor
- torch.squre: 제곱
- torch.sqrt: 제곱근
- torch.maximum
- torch.minimum
- torch.cumsum
- torch.cumprod: 누적곱
차원 축소 연산
- PyTorch는 기본이 reduce 연산
행렬 연산
- torch.matmul(torch.mm): 내적
- torch.linalg.inv: 역행렬
크기 및 차원 바꾸기
- torch.reshape
- .view → torch.view로는 쓰지 않는다.
(view 함수가 있어서 expand_dim과 같은 함수는 필요 없다.)- torch.transpose
- torch.squeeze: 차원 축소
- torch.unsqueeze: 차원 늘리기
텐서 나누기, 합치기
- torch.chunk, torch.split
torch.chunk(c, 3, dim=1) # 3개로 나누기 torch.split(c, 2, dim=0) # 2개씩 나누기
- torch.cat, torch.stack, torch.tile
torch.cat([a,b], dim=0)
함수 끝에 '_' → inplace 명령(예외 있음)
RuntimeError
transpose 하고 view 했을 때 데이터의 물리적 위치와 index가 일치하지 않으면 발생 → contiguous() 함수를 호출하거나 reshape이전 결과값과 같은 shape로 맞추는 명령
# a를 b와 같은 shape로 a.view_as(b) a.reshape_as(b)
autograd
PyTorch에서 핵심적인 기능을 담당하는 하부 패키지로, 텐서의 연산에 대해 자동으로 미분값을 구해주는 기능을 한다.
- 텐서 자료를 생성할 때, requires_grad 인수를 True로 설정 하거나 .requires_grad_(True) 실행
- .backward() 함수: 자동으로 미분값을 계산, requires_grad 인수가 True로 설정된 변수의 grad 속성의 값을 갱신
- retain_graph: 미분을 연산하기 위해 사용했던 임시 그래프를 유지 할 것인지 설정(기본값 False)
- torch.autograd.grad() 함수로 tf.GradientTape처럼 사용 가능
- .detach() 함수나 with torch.no_grad()를 이용하면 계산이 멈춘다.
torch.nn.BCELoss(이진 분류)
torch.nn.CrossEntropyLoss(다중 클래스 분류)
torch.nn.MSELoss(회귀)
- loss값 출력
# 1 loss.detach().numpy() # 2 with torch.no_grad(): print(loss.numpy()) # 3 loss.item()
- assign 대신 data에 접근해서 값 수정
tensor.data
- optimizer
opt = torch.optim.Adam([w,b], lr=0.0001) opt.step() opt.zero_grad() # 0으로 초기화
PyTorch는 TensorFlow와 이미지를 표현하는데 차이가 있다.
- TensorFlow: (batch, height, width, channel)
- PyTorch: (batch, channel, height, width)
모델 정의
from torch import nn import torch.nn.functional as F
class Net(nn.Module): def __init__(self): super(Net, self).__init__() # forward에서 사용되는 것을 미리 정의 def forward(self, x): pass # 실제 모델의 연산
- PyTorch에서는 model을 Training 모드로 변경 후 Training 할 수 있다.
import torch.optim as optim opt = optim.SGD(model.parameters(), 0.03)
for epoch in epochs: # 모델 학습 model.train() for batch_idx, (data, target) in enumerate(train_loader): data, target = data.to(device), target.to(device) opt.zero_grad() # grad 초기화 output = model(data) loss = F.nll_loss(output, target) # 예측값, 정답 순서로 loss.backward() opt.step() # 모델 evaluation model.eval() test_loss = 0 with torch.no_grad(): for data, target in test_loader: output = model(data) test_loss += F.nll_loss(output, target).item() test_loss /= (len(test_loader.dataset) // batch_size)
- nn.Sequential
- Sub-class of nn.Module
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = nn.Sequential( nn.Linear(784, 15), nn.Sigmoid(), nn.Linear(15, 10), nn.Sigmoid() )
# model.children() # model.modules()
# pip install torchsummary import torchsummary torchsummary.summary(model, (784,))
Learning Rate Scheduler
- LambdaLR, StepLR, MultiStepLR, ExponentialLR, CosineAnnealingLR, ReduceLROnPlateau
from torch.optim.lr_scheduler import ReduceLROnPlateau optimizer = optim.Adam(model.parameters(), lr = 0.03) scheduler = ReduceLROnPlateau(optimizer, mode='min', verbose=True)
def train_loop(dataloader, model, loss_fn, optimizer, scheduler, epoch): model.train() size = len(dataloader) for batch, (x, y) in enumerate(dataloader): # batch size, image height, image width, image channel x, y = x.to(device), y.to(device) pred = model(x) loss = loss_fn(pred, y) optimizer.zero_grad() loss.backward() optimizer.step() if batch % 100 == 0: loss = loss.item() print(f'Epoch {epoch}: [{batch}/{size}] loss: {loss.item()}') scheduler.step(loss) return loss.item()
for epoch in range(10): loss = train_loop(train_loader, model, F.nll_loss, optimizer, scheduler, epoch) print(f'Epoch: {epoch} loss: {loss}')
weight만 저장
torch.save(model.state_dict(), 'model_weights.pth') model.load_state_dict(torch.load('model_weights.pth')
weight, 구조 함께 저장
torch.save(model, 'model.pth') model = torch.load('model.pth')
save, load, resume
checkpoint_path = 'checkpoint.pth' torch.save({ 'epoch' : epoch, 'model_state_dict' : model.state_dict(), 'optimizer_state_dict' : optimizer.state_dict(), 'loss' : loss }, checkpoint_path)
model = ResNet().to(device) optimizer = optim.SGD(model.parameters(), lr = 0.003) checkpoint = torch.load(checkpoint_path) # checkpoint.keys() model.load_state_dict(checkpoint['model_state_dict']) epoch = checkpoint['epoch'] optimizer.load_state_dict(checkpoint['optimizer_state_dict']) loss = checkpoint['loss']
ImageFolder
- 로컬에 있는 이미지 데이터셋을 불러옴(디렉토리 구조가 같아야 함)
import torch from torchvision import datasets, transforms
train_dataset = datasets.ImageFolder( root = train_dir, transform=transforms.Compose([transforms.ToTensor(), transforms.Normalize(0.5, 0.5]) train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=32, shuffle=True)
Custom dataset
- dataset sub-class: torch.utils.data.Dataset 상속 받아서 데이터셋 구현
from PIL import Image class Dataset(torch.utils.data.Dataset): def __init__(self, data_paths, transform): super(Dataset).__init__() self.data_paths = data_paths self.transform = transforms def __len__(self,): return len(self.data_paths) def __getitem__(self, idx): path = self.data_paths[idx] image = Image.open(path) label_name = path.split('.png')[0].split('_')[-1].strip() label = label_list.index(label_name) if self.transform: image = self.transform(image) return image, label
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') train_loader = torch.utils.data.DataLoader( Dataset(train_paths, transform = transforms.ToTensor()), batch_size = 32, shuffle = True )
Transforms
- 이미지 데이터 변환
transforms.Resize((32,32))(image)
transforms.RandomCrop(size=(64, 64))(image) # 정해진 사이즈에 맞춰서 랜덤으로 이미지의 조각을 보여준다.
transforms.ColorJitter(brightness=1)(image)
transforms.Grayscale()(image)
transforms.Pad(padding=(20,10))(image)
transforms.RandomAffine(degrees=90)(image)
transform_list = [ transforms.RandomAffine(degrees=90), transforms.Pad(padding=(20,10)) ] # transform_list에서 지정해 놓은 설정을 50%의 확률로 실행한다. transforms.RandomApply(transform_list, p=0.5)(image) # transform_list에서 지정해 놓은 설정에서 랜덤으로 하나 선택 transforms.RandomChoice(transform_list)(image)
transforms.RandomHorizontalFlip(p=0.5)(image)
▷ 다음주 학습 계획: OpenCV 강의
[이 글은 제로베이스 데이터 취업 스쿨의 강의 자료 일부를 발췌하여 작성되었습니다.]