본문 바로가기

SK네트웍스 Family AI캠프 10기/Weekly 회고

[플레이데이터 SK네트웍스 Family AI 캠프 10기] 7주차 회고

7주 차 학습

 

 

Deep Learning

 

인공지능(AI; Artificial Intelligence)

  • 사람의 지능을 만들기 위한 시스템이나 프로그램
  • 머신러닝에 포함되는 기술

 

인공뉴런(Artificial Neuron)

  • $a = b + w_1x_1 + w_2x_2 + \cdots + w_mx_m$

 

인공신경망(Artificial Neural Network)

  • 다수의 인공뉴런이 연결되어 구성된 네트워크
  • 입력층(Input Layer), 은닉층(Hidden Layer), 출력층(Output Layer)으로 구성

 

딥러닝(Deep Learning)

  • 인공신경망의 한 종류로, 은닉층이 많은 신경망
  • 머신러닝이 처리하기 어려운 비정형 데이터 처리

 

손실 함수(Loss Function)

  • Regression

 

경사하강법(Gradient Descent)

  • 기울기를 통해 모델의 파라미터 조정
    • 예측과 실제값을 비교하여 손실을 구함
    • 손실이 작아지는 방향으로 파라미터 조정

 

최적화 알고리즘(Optimizer)

  • 손실 함수를 최소화하기 위해 가중치와 바이어스를 조정
    • SGD
      • 의로 추출한 일부 데이터를 사용
      • 데이터셋이 크고 연산 속도가 중요한 경우에 사용
    • Momentum
      • 이전 기울기(gradient) 정보를 반영하여 가중치(weight) 업데이트
    • RMSProp
      • 이전 업데이트 맥락을 통해 학습률(learning rate) 업데이트
    • Adam
      • Momentum과 RMSProp 결합
      • 데이터셋이 작거나 노이즈가 많은 경우에 사용

 

역전파(Back-Propagation)

  • 손실값을 구해 해당 손실에 관여하는 가중치들을 손실이 작아지는 방향으로 수정

 

 

Deep Learning - PyTorch

 

Tensor 생성

  • torch.tensor()

 

Tensor 속성

  • tensor.shape: tensor의 크기
  • tensor.dtype: tensor의 데이터 타입
  • tensor.device: tensor가 위치한 장치 (CPU - cpu / GPU - cuda)

 

 

Deep Learning - PyTorch(Dataset)

 

DataLoader

  • 데이터셋을 미니배치(batch) 단위로 로드하고, 반복(iteration)할 수 있도록 도와주는 클래스
from torch.utils.data import DataLoader

test_dataloader = DataLoader(test_data, batch_size=64, shuffle=True)

test_features, test_labels = next(iter(test_dataloader))

 

Dataset Class

# 이미지 데이터
import os
from torch.utils.data import Dataset
from torchvision.transforms import ToTensor
from pathlib import Path
from PIL import Image

class CustomDataset(Dataset):			# Dataset 상속
    def __init__(self, data_path:str, transform=None) -> None:
        self.paths = list(Path(data_path).glob('*/*.jpg'))
        
        self.index_to_class = self.__get_classes(data_path)
        self.class_to_index = { class_name: i for i, class_name in enumerate(self.index_to_class) }
        
        self.transform = transform
    
    def __get_classes(self, data_path):
        return sorted(entry.name for entry in os.scandir(data_path) if entry.is_dir())
    
    def __len__(self) -> int:
        return len(self.paths)
    
    def __getitem__(self, index:int):
        feature_path = self.paths[index]
        img = Image.open(feature_path)
        
        target_name = feature_path.parent.stem
        
        if self.transform:
            img = self.transform(img)
        
        return img, self.class_to_index[target_name]

train_path = ("./data/Image Folder")
train_custom_dataset = CustomDataset(data_path=train_path, transform=ToTensor())

len(train_custom_dataset)			# CustomDataset의 magic method - len

feature, target = train_custom_dataset[0]	# CustomDataset의 magic method - getitem
# 타이타닉 데이터
import pandas as pd
import seaborn as sns
from torch.utils.data import Dataset

class CustomDataset(Dataset):			# Dataset 상속
    def __init__(self, df:pd.DataFrame, target:str, transform=None) -> None:
        self.features = df.drop(columns=target)
        self.target = df[target]
        
        self.transform = transform
    
    def __len__(self) -> int:
        return self.features.shape[0]
    
    def __getitem__(self, index:int):
        feature = self.features.iloc[index]
        target = self.target.iloc[index]
        
        if self.transform:
            feature = self.transform(feature)
        
        return feature, target

df_train = sns.load_dataset('titanic')
train_custom_dataset = CustomDataset(df=df_train, target='survived')

len(train_custom_dataset)			# CustomDataset의 magic method - len 호출

feature, target = train_custom_dataset[0]	# CustomDataset의 magic method - getitem 호출

 

 

Deep Learning - PyTorch(Model)

 

Regression

import torch
from torch import nn

# Data
weight = 0.7
bias = 0.3

start = 0
end = 1
step = 0.02

X = torch.arange(start, end, step).unsqueeze(dim=1)
y = weight * X + bias

train_split = int(0.8 * len(X))
X_train, y_train = X[:train_split], y[:train_split]
X_test, y_test = X[train_split:], y[train_split:]

# Model
class LinearRegressionModel(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        
        self.weights = nn.Parameter(
            torch.randn(1, dtype=torch.float),
            requires_grad=True
        )
        
        self.bias = nn.Parameter(
            torch.randn(1, dtype=torch.float),
            requires_grad=True
        )
    
    def forward(self, x):
        pred = self.weights * x + self.bias
        
        return pred

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

model = LinearRegressionModel().to(device)

# Training
loss_fn = nn.L1Loss()

optimizer = torch.optim.SGD(
    params=model.parameters(),
    lr=0.01
)

epochs = 100

for epoch in range(epochs):
    model.train()
    
    y_pred = model(X_train.to(device))
    
    loss = loss_fn(y_pred, y_train.to(device))
    
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
    model.eval()
    
    with torch.inference_mode():
        test_pred = model(X_test.to(device))
        
        test_loss = loss_fn(test_pred, y_test.type(torch.float).to(device))
        
        if epoch % 10 == 0:
            print(f"Epoch: {epoch} | MAE Train Loss: {loss} | MAE Test Loss: {test_loss}")

 

Binary Classification

import pandas
import torch
from sklearn.datasets import make_circles
from sklearn.model_selection import train_test_split

# Data
n_samples = 1000

X, y = make_circles(n_samples, noise=0.03, random_state=42)
X = torch.from_numpy(X).type(torch.float)
y = torch.from_numpy(y).type(torch.float)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)

# Model
class ClassificationModel(nn.Module):
    def __init__(self, input_size=2, output_size=1, hidden_size=512) -> None:
        super().__init__()
        
        self.linear_relu_stack=nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, output_size)
        )
    
    def forward(self, x):
        out = self.linear_relu_stack(x)
        
        return out

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

model = ClassificationModel().to(device)

# Training
loss_fn = nn.BCEWithLogitsLoss()

optimizer = torch.optim.SGD(
    params=model.parameters(),
    lr=0.01
)

def accuracy_fn(y_true, y_pred):
    correct = torch.eq(y_true, y_pred).sum().item()
    
    acc = (correct / len(y_true)) * 100
    
    return acc

epochs = 500

for epoch in range(epochs):
    model.train()
    
    y_pred = model(X_train.to(device)).squeeze()
    loss = loss_fn(y_pred, y_train.to(device))
    
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
    y_pred_prob = torch.round(torch.sigmoid(y_pred))
    acc = accuracy_fn(y_true=y_train, y_pred=y_pred_prob.cpu())
    
    model.eval()
    
    with torch.inference_mode():
        y_pred = model(X_test.to(device)).squeeze()
        
        test_loss = loss_fn(y_pred, y_test.to(device))
        
        y_pred_prob = torch.round(torch.sigmoid(y_pred))
        test_acc = accuracy_fn(y_true=y_test, y_pred=y_pred_prob.cpu())
    
    if epoch % 100 == 0:
        print(f"Epoch: {epoch} | Loss: {loss:.5f}, Accuracy: {acc:.2f} | "\
              f"Test Loss: {test_loss:.5f}, Test Accuracy: {test_acc:.2f}")

 

Multiclass Classification

# Random Seed 고정
import os
import random
import numpy as np
import torch

def reset_seeds(seed=42):
    random.seed(seed)
    os.environ['PYTHONHASHSEED']
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)
    torch.backends.cudnn.deterministic=True
# Dataset
from torchvision import datasets
from torchvision.transforms import ToTensor

train_dataset = datasets.FashionMNIST(
    root='download',
    train=True,
    download=True,
    transform=ToTensor()
)

test_dataset = datasets.FashionMNIST(
    root='download',
    train=False,
    download=True,
    transform=ToTensor()
)
# DataLoader
from torch.utils.data import DataLoader

reset_seed()
batch_size=64

train_dataloader = DataLoader(
    train_dataset,
    batch_size,
    shuffle=True
)

test_dataloader = DataLoader(
    test_dataset,
    batch_size, shuffle=True
)

features, targets = next(iter(train_dataloader))
# Model
from torch import nn

class MulticlassModel(nn.Module):
    def __init__(self, input_size, output_size, hidden_size=32) -> None:
        super().__init__()
        
        self.linear_relu_stack = nn.Sequential(
            nn.Flatten(),
            nn.Linear(input_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, hidden_size*2),
            nn.ReLU(),
            nn.Linear(hidden_size*2, output_size)
        )
    
    def forward(self, x):
        return self.linear_relu_stack(x)

device = 'cuda' if torch.cuda.is_available() else 'cpu'
model = MulticlassModel(1*28*28, len(train_dataset.classes)).to(device)
# Training
from tqdm.auto import tqdm
from torch.optim import SGD
from helper_functions import accuracy_fn

def model_train(model, dataloader, device, loss_fn, optimizer):
    model.train()
    
    train_loss = 0
    
    for feature, target in tqdm(dataloader, desc='Train Loop', leave=False):
        feature = feature.to(device)
        target = target.to(device)
        
        pred = model(feature)
        
        loss = loss_fn(pred, target)
        train_loss += loss.cpu().item()
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    
    return train_loss / len(dataloader)

def model_test(model, dataloader, device, loss_fn, accuracy_fn):
    model.eval()
    
    test_loss, test_accuracy = 0, 0
    
    with torch.inference_mode():
        for feature, target in tqdm(dataloader, desc='Test Loop', leave=False):
            feature = feature.to(device)
            target = target.to(device)
            
            pred = model(feature)
            
            loss = loss_fn(pred, target)
            test_loss += loss.cpu().item()
            
            pred_prob = nn.Softmax(dim=1)(pred).argmax(dim=1)
            test_accuracy += accuracy_fn(pred_prob.cpu(), target.cpu())
    
    return test_loss / len(dataloader), test_accuracy / len(dataloader)

def loss_plot(train_loss, test_loss):
    plt.plot(train_loss, label='Train Loss')
    plt.plot(test_loss, label='Test Loss')
    
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    
    plt.show()

class EarlyStopper(object):
    def __init__(self, trial_num, best_model_path) -> None:
        self.trial_num = trial_num
        self.now_trial = 0
        self.best_loss = np.inf
        self.best_model_path = best_model_path
    
    def get_best_model(self, device):
        return torch.load(self.best_model_path).to(device)
    
    def is_continuable(self, loss, model):
        if loss < self.best_loss:
            self.best_loss = loss
            self.now_trial = 0
            torch.save(model, self.best_model_path)
            return True
        elif self.now_trial < self.trial_num:
            self.now_trial += 1
            return True
        else:
            return False

def training(model, early_stopper:EarlyStopper, train_dataloader, test_dataloader, \
             loss_fn, accuracy_fn, device='cpu', optimizer=None, lr=0.01, epochs=100):
    model = model.to(device)
    
    if optimizer is None:
        optimizer = SGD(params=model.parameters(), lr=lr)
    
    train_losses, test_losses = [], []
    
    for epoch in tqdm(range(epochs), desc='Epoch Loop', leave=True):
        train_loss = model_train(model, train_dataloader, device, loss_fn, optimizer)
        train_losses.append(train_loss)
        
        test_loss, test_accuracy = model_test(model, test_dataloader, device, loss_fn, accuracy_fn)
        test_losses.append(test_loss)
        
        if not early_stopper.is_continuable(test_loss, model):
            break
    
    loss_plot(train_losses, test_losses)

reset_seeds()
model = MulticlassModel(1*28*28, len(train_dataset.classes))
early_stopper = EarlyStopper(trial_num=5, best_model_path='models/best_model.pth')
loss_fn = nn.CrossEntropyLoss()

training(model, early_stopper, train_dataloader, test_dataloader, loss_fn, accuracy_fn, device)

 


7주 차 회고

 

 

Keep

 

매일 / 매주 블로그 및 회고 작성

 매일 / 매주 꾸준히 배운 내용을 정리하고 회고를 작성하며 지나간 시간을 되돌아볼 수 있어서 좋았다. 부족한 부분이 무엇인지 알 수 있어서 블로그 작성하는 것이 나에게 도움이 되는 것 같다.

 

 

Problem

 

수면 부족

 매일 꾸준히 6시에 일어나는 것은 동일하지만 자는 시간이 불규칙하고 나에게 6시간 수면은 짧은 것 같다는 생각을 하게 되었다. 하지만 집에서 해야 할 일들도 있기 때문에 이에 대해서 고민을 해봐야 할 것 같다.

 

 

Try

 

SQLD 시험 준비

 ADsP 시험은 끝났지만 SQLD 시험도 신청했기 때문에 2주 동안 열심히 공부해봐야 할 것 같다. 이왕 열심히 해보고 싶어서 이것저것 일을 많이 벌이긴 해서 하나씩 해결해 나가야 한다.

 

단위 프로젝트

 이번 단위가 끝나가서 단위 프로젝트를 시작하게 되었다. 열심히 하고는 싶지만 이번 단위에 대해서 내가 많이 부족한 것 같아서 더 노력을 해야 할 것 같다.