본문 바로가기
프로그램/SKT FLY AI

SKT FLY AI : 21일차 - 딥러닝 파이토치 시작

by hsloth 2023. 7. 24.

오늘부터 5주차 수업이 시작되었다.
딥러닝 파이토치 책으로 파이토치를 배우는 것 같다!

Setting


  • 일단 anaconda를 linux에 설치했다 (wsl)
  • nvidia에서 window용 드라이버를 설치했다.

용어 정리


  • MLP (멀티 레이어 퍼셉트론)
  • 신경망의 층을 많이 쌓을 수 록 XOR같이 다양한
  • 입력층, 은닉층, 출력층, 가중치
  • 바이어스 : 가중합에 더해주는 상수로, 하나의 뉴런에서 활성화 함수를 거쳐 최종적으로 출력되는 값을 조절하는 역할을 한다.
  • 가중합, 전달 함수 : 가중치와 신호의 곱을 합한 것
  • 활성화 함수 : 신호를 입력받아 이를 적절히 처리하여 출력해 주는 함수
  • 손실 함수 : 가중치 학습을 위해 출력 함수의 결과와 실제 값 간의 오차를 측정하는 함수
    • 학습 중에 학습이 얼마나 잘 되고 있는지 평가하기 위한 지표
    • 손실 함수마다 평가의 정도가 다르다고 생각하면 된다.

활성화 함수


  • 활성화 함수를 왜 사용할까?
    • 활성화 함수란 출력 값을 활성화를 일으키게 할 것인가를 정하고, 그 값을 부여하는 함수
    • 그렇다면 출력 한다 / 출력 안한다 만 결정하면 되는데 왜 시그모이드 같은 이상한 0~1 사이에 값을 출력하게끔하는 함수가 존재하는 걸까?
      • 도메인 마다 출력하는 값이 한다/안한다 일 수도 있고 확률 값일 수도 있기 때문에 사용하는 활성화 함수가 다르다. (일단 내가 생각하기로는 그렇다)
  • 시그모이드 함수
  • tanh 함수
  • ReLU 함수
  • Leaky ReLU
  • softmax 함수

손실 함수


  • 손실 함수를 왜 사용할까?
    • 얼마나 실제 답과 다른지 알려주는 함수
    • 그래디언트에 반영하려고 (많이 틀리면 많이 반영하고, 조금 틀리면 조금 반영하려고)
  • 평균 제곱 오차(MSE)
  • 크로스 엔트로피 오차(CEE)

딥러닝 문제점


  • 은닉층이 많을 수록 다음 세가지 문제가 발생
    • 과적합
      • 해결 : 드롭아웃
    • 기울기 소멸 문제 : 은닉층이 많은 신경망에서 주로 발생하는데, 출력층에서 은닉층으로 전달되는 오차가 크게 줄어들어 학습이 더디게 되는 문제
      • 해결 : 시그모이드나 tanh대신 ReLU함수를 사용하면 해결
    • 성능이 나빠지는 문제 : 경사 하강법은 손실 함수의 비용이 최소가 되는 지점을 찾을 때까지 기울기가 낮은 쪽으로 계속 이동시키는 과정을 반복하는데, 이 때 성능이 나빠지는 문제 발생
      • 해결 : 확률적 경사 하강법과 미니 배치 경사 하강법 사용

경사 하강법 종류


배치 경사 하강법

  • 전체 데이터셋에 대한 오류를 구한 후 기울기를 한 번만 계산하여 모델의 파라미터를 업데이트
  • 전체 훈련 데이터셋에 대해 가중치는 편미분하는 방법

확률적 경사 하강법

  • 임의로 선택한 데이터에 대해 기울기를 계산하는 방법으로 적은 데이터를 사용하므로 빠른 계산 가능
  • 파라미터 변경 폭이 불안정하고, 때로는 배치 경사 하강법보다 정확도가 낮을 수 있지만 속도가 빠르다.

미니 배치 경사 하강법

  • 전체 데이터셋을 미니 배치 여로 개로 나누고, 미니 배치 한 개마다 기울기를 구한 후 그것의 평균 기울기를 이용하여 모델을 업데이트해서 학습하는 방법
  • 전체 데이터를 계산하는 것보다 빠르며, 확률적 경사 하강법보다 안정적이다.

옵티마이저


  • 확률적 경사 하강법의 파라미터 변경 폭이 불안정한 문제를 해결하기 위해 학습 속도와 운동량을 조정하는 옵티마이저를 적용할 수 있다.

속도를 조정하는 방법

아다그라드(Adagrad, Adaptive gradient)

  • 가중치의 업데이트 횟수에 따라 학습률을 조정하는 방법
  • 많이 변화하지 않는 변수들의 학숩률은 크게하고, 많이 변화하는 변수들의 학습률은 작게한다.
  • 많이 변화한 변수는 최적 값에 근접 햇을 것이라는 가정하에 작은 크기로 이동하면서 세밀하게 값을 조정하고, 적게 변화한 변수들은 학습률을 크게 하여 빠르게 오차 값을 줄인다.
  • 파라미터마다 다른 학습률을 주기 위해 G함수를 추가. G 값은 이전 G 값의 누적(기울기 크기의 누적)이다.
  • 기울기가 크면 G값이 커지기 때문에 학습률은 작아진다. (수식은 알아서 찾아보자)

아다델타 (Adadelta, Adaptive delta)

  • 아다그라드에서 G 값이 커짐에 따라 학습이 멈추는 문제를 해결하기 위해 등장한 방법
  • 아다그라드 수식에서 학습률을 D함수(가중치 변화량 크기를 누적한 값)로 변환했기 때문에 학습률에 대한 하이퍼파라미터가 필요하지 않다.

RMSPropo

  • 생략

운동량을 조정하는 방법

모멘텀(Momentum)

  • 경사 하강법과 마찬가지로 매번 기울기를 구하지만, 가중치를 수정하기 전에 이전 수정 방향을 참고하여 같은 방향으로 일정한 비율만 수정하는 방법
  • 수정이 지그재그로 이루어지는 현상이 줄어들고, 이전 이동 값을 고려하여 일정 비율만큼 다음 값을 결정하므로 관성 효과를 얻을 수 있다.
  • 모멘텀은 SGD(확률적 경사 하강법)와 함께 사용된다.네스테로프 모멘텀
  • 생략

속도와 운동량에 대한 혼용 방법

아담 (Adam, Adaptive Moment Estimation)

  • 생략

딥러닝 알고리즘

심층 신경망


  • 입력층과 출력층 사이에 다수의 은닉층을 포함하는 인공 신경망
  • 다양한 비선형적 관계를 학습할 수 있다는 장점이 있다.
  • 하지만, 학습을 위한 연산량이 많고, 기울기 소멸 문제가 말생할 수 있다.
    • 해결 : 드롭아웃, 렐루함수, 배치 정규화 등

합성곱 신경망


  • 함성곱층(convolutional layer)과 풀링층(pooling layer)을 포함하는 이미지 처리 성능이 좋은 인공 신경망 알고리즘
  • 영상 및 사진이 포함된 이미지 데이터에서 객체를 탐색하거나 객체 위치를 찾아내는 데 유용
  • LeNet-5, AlexNet 등
  • 층을 깊게 쌓은 신경망 : VGG, GoogLeNet, ResNet
  • 기존 신경망과 비교하여 다른 점
    • 각 층의 입출력 형상을 유지
    • 이미지의 공간 정보를 유지하면서 인접 이미지와 차이가 있는 특징을 효과적으로 인식
    • 복수 필터로 이미지의 특징을 추출하고 학습
    • 추출한 이미지의 특징을 모으고 강화하는 풀링층이 있다.
    • 필터를 공유 파라미터로 사용하기 때문에 일반 인공 신경망과 비교하여 학습 파라미터가 매우 적다.

순환 신경망


  • 시계열 데이터(음악, 영상 등) 같은 시간 흐름에 따른 변화하는 데이터를 학습하기 위한 인공 신경망
  • 순환은 자기 자신을 참조한다는 것으로, 현재 결과가 이전 결과와 연관이 있다는 의미이다.
  • 특징
    • 시간성을 가진 데이터가 많다.
    • 시간성 정보를 이용하여 데이터의 특징을 잘 다룬다.
    • 시간에 따라 내요잉 변하므로 데이터는 동적이고, 길이가 가변적이다. (위의 두개는 길이가 일정해야함)
    • 매우 긴 데이터를 처리하는 연구가 활발히 진행되는 중이다.
  • 기울기 소멸 문제로 학습이 제대로 되지 않는 문제가 있다.
    • 해결 : LSTM(Long-Short Term Memory)
  • 자연어 처리 분야와 궁합이 잘 맞는다.
    • 언어 모델링, 텍스트 생성, 자동 번역, 음성 인식, 이미지 캡션 생성

파이토치

  • 텐서플로우는 한자. 파이토치는 한글이라고 생각하면 된다.

파이토치의 특징 및 장점

  • 파이토치 : GPU에서 텐서 조작 및 동적 신경망 구축이 가능한 프레임워크
  • 텐서(Tensor)
    • 텐서는 파이토치의 데이터 형태
    • 텐서는 단일 데이터 형식으로 된 자료들의 다차원 행렬
    • 텐서는 간단한 명령어(변수 뒤에 .cuda()를 추가)를 사용해서 GPU로 연산을 수행할 수 있다.

벡터, 행렬, 텐서

  • 벡터 : 1차원
  • 행렬 : 2차원
  • 텐서 : 3차원 이상

파이토치의 아키텍쳐


파이토치 API

  • torch : GPU를 지원하는 텐서 패키지
  • torch.autograd : 자동 미분 패키지
  • torch.nn : 신경망 구축 및 훈련 패키지
  • torch.multprocessing : 파이썬 멀티프로세싱 패키지
  • torch.utils : DataLoader 및 기타 유틸리티를 제공하는 패키지

텐서 생성 및 변환

import torch

print(torch.tensor([[1,2],[3,4]])) # 2차원 형태의 텐서 생성
print(torch.tensor([[1,2],[3,4]], device='cuda:0')) # GPU에 텐서 생성
print(torch.tensor([[1,2],[3,4]], dtype=torch.float64)) # dtype을 이용하여 텐서 생성 

temp = torch.tensor([[1,2],[3,4]])
print(temp.numpy()) # 텐서를 ndarray로 변환

temp = torch.tensor([[1,2],[3,4]], device="cuda:0")
print(temp.to("cpu".numpy()) # GPU상의 텐서를 CPU로 변환한 후 ndarray로 변환

텐서 인덱스 조작

  • 텐서 자료형
    • torch.FloatTensor: 32비트의 부동 소수점
    • torch.DoubleTensor : 64비트의 부동 소수점
    • torch.LongTensor : 64비트의 부호가 있는 정수

temp = torch.FloatTensor([1,2,3,4,5,6,7]) # 1차원 벡터 생성
print(temp[0]) # 인덱스로 접근
print(temp[1:3]) # 슬라이스로 접근

길이가 같은 벡터간 뺄셈가능 (v - w)

차원 조작


temp = torch.tensor([[1,2],[3,4]])

print(temp.shape)
print(temp.view(4,1)) # 2x2행렬을 4x1로 변경
print(temp.view(-1)) # 2x2행렬을 1차원 벡터로 변형
print(temp.view(1, -1)) -1은 ?라고 보면 된다. 따라서 (1, ?)으로, 다른 차원으로부터 해당 값을 유추하겠다는 의미. temp의 원소 개수를 유지한 채 (1, ?) 형태를 만들려고 하므로 (1, 4)가 된다.

데이터 준비


import pandas as pd
import torch
data = pd.read_csv('../class2.csv')

x = torch.from_numpy(data['x'].values).unsqueeze(dim=1).float() # csv파일의 x칼럼의 값을 넘파이 배열로받아 tensor로 바꾸어 준다.
y = torch.from_numpy(data['y'].values).unsqueeze(dim=1).float()

커스텀 데이터셋을 만들어서 사용

  • DataLoader객체는 학습에 사용될 데이터 전체를 보관했다가 모델 학습을 할 때 배치 크기만큼 데이터를 꺼내서 사용한다. 이때 주의할 것은 데이터를 미리 잘라 놓는 것이 아니라 내부적으로 iterator에 포함된 인덱스를 이용하여 배치 크기만큼 데이터를 반환한다.

import pandas as pd
import torch
from torch.utils.data import Dataset
from torch.utils.data import DataLoader

class CustomDataset(Dataset):
    def __init__(self, cvs_file):
        self.label = pd.read_cvs(csv_file)

    def __len__(self): # 전체 데이터셋의 크기를 반환
        return len(self.label)

    def __getitem__(self, idx): # 전체 x와 y 데이터 중에 해당 idx번째의 데이터를 가져온다.
        sample = torch.tensor(self.label.iloc[idx,0:3]).int()
        label = torch.tensor(self.label.iloc[idx,3]).int()
        return sample, label

tensor_dataset = CustomDataset('../covtype.csv') # 데이터셋으로 covtype.csv를 사용한다
dataset = DataLoader(tensor_dataset, batch_size=4, shuffle=True) # 데이터셋을 torch.utils.data.DataLoader에 파라미터로 전달

파이토치에서 제공하는 데이터셋 사용

  • pip install requests

import torchvision.transforms as transforms

mnist_transform = transforms.Compse([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (1.0,))
]) # 평균이 0.5, 표준편차가 1.0이 되도록 데이터의 분포를 조정

from torchvision.datasets import MNIST
import requests
download_root = '../chap02/data/MNIST_DATASET' # 내려받을 경로 지정

train_dataset = MNIST(download_root, transform=mnist_transform, train=True, download=True)
valid_dataset = MNIST(download_root, transform=mnist_transform, train=False, download=True)
test_dataset = MNIST(download_root, transform=mnist_transform, train=False, download=True)

모델


모델 정의

  • 단순 신경망을 정의하는 방법
  • nn.Module을 상속받지 않는 매우 단순한 모델을 만들 때 사용
  • 구현이 쉽고 단순
  • model = nn.Linear(in_features=1, out_features=1, bias=True)
  • 계층 : 모듈 또는 모듈을 구성하는 한 개의 계층으로 합성곱층, 선형 계층 등이 있다.
  • 모듈 : 한 개 이상의 계층이 모여서 구성된 것으로, 모듈이 모여 새로운 모듈을 만들수도 있다.
  • 모델 : 최종적으로 원하는 네트워크로, 한 개의 모듈이 모델이 될 수도 있다.

nn.Module()을 상속하여 정의하는 방법


class MLP(Module):
    def __init__(self, inputs):
        super(MLP, self).__init__()
        self.layer = Linear(inputs, 1) # 계층 정의
        self.activation = Sigmoid() # 활성화 함수 정의

def forward(self, X):
    X = self.layer(X)
    X = self.activation(X)
    return X

Sequential 신경망을 정의하는 방법

  • nn.Sequential

import torch.nn as nn

class MLP(nn.Module):
    def __init__(self):
        suer(MLP, 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 = MLP()
print(list(model.children()))
print(list(model.modules()))

모델 파라미터 정의


from torch.optim import optimizer

criterion - torch.nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9(
scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer=optimizer, lr_lambda=lambda epoch: 0.95*epoch)

for epoch in range(1, 100+1): # 에포크 수만큼 데이터를 반복하여 처리
    for x, y in dataloader:
        optimizer.rezo_grad()
loss_fn(model(x), y).backward()
optimizer.step()
scheduler.step()

모델 훈련


for epoch in range(100):
    yhat = model(x_train)
    loss = criterion(yhat, y_train)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

모델 평가

  • pip install torchmetrics

함수를 이용하여 모델 평가


import torch
import torchmetrics

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

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

모듈을 이용하여 모델 평가


import torch
import torchmetrics

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 = metric(preds, target)
    print(f"Accuracy on all data: {acc}") # 현재 배치에서 모델 평가

acc = metric.compute()
print(f"Accuracy on all data: {acc}") # 모든 배치에서 모델 평가

훈련과정 모니터링

  • pip install tensorboard

import torch
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter("../chap02/tensorboard") # 모니터링에 필요한 값들이 저장될 위치

for epoch in range(num_epochs):
    model.train() # 학습 모드로 전환 (dropout=True)
    batch_loss = 0.0

    for i, (x, y) in enumerate(dataloader):
        x, y = x.to(device).float(), y.to(device).float()
        outputs = model(x)
        loss = criterion(outputs, y)
        writer.add_scalar("Loss", loss, epoch) # 스칼라 값(오차)를 기록
        optimizer.zero_grad()
        loss.backward()
        optimizer.setup()

writer.close() # SummaryWriter가 더 이상 필요하지 않으면 close() 메서드 호출
  • 텐서 보드 실행 명령
    • tensorboard --logdir=../chap02/tensorboard --port=6006
    • 이제 웹 브라우저에서 http://localhost:6006을 입력하면 모니터링 웹 페이지가 열린다.

합성곱 신경망

합성곱층

  • 합성곱 신경망은 이미지나 영상을 처리하는 데 유용하다.
  • 이미지분석은 배열을 1차원으로 펼쳐서 각 픽셀에 가중치를 곱하여 은닉층으로 전달하게 되는데, 이렇게 하면 데이터의 공간적 구조를 무시하게 된다. 그래서 이것을 방지하려고 도입된 것이 하성곱층이다.

합성곱 신경망 구조

  • 합성곱 신경망은 합성곱층과 풀링층을 거치면서 입력 이미지의 주요 특성 벡터를 추출한다.
  • 추출된 주요 특성 벡터들은 완전연결층을 거치면서 1차원 벡터로 변환되며, 마지막으로 출력층에서 활성화 함수인 소프트맥스 함수를 사용하여 최조 결과가 출력된다.

5개의 계층으로 구성된다.

  1. 입력층
  2. 합성곱층
  3. 풀링층
  4. 완전연결층
  5. 출력층

입력층

  • 입력 이미지 데이터가 최초로 거치게 되는 계층
  • 이미지는 높이, 너비, 채널의 값을 갖는 3차원 데이터이다.
  • 채널은 이미지가 그레이 스케일이면 1, RGO면 3을 갖는다.

합성곱층

  • 입력 데이터에서 특성을 추출하는 역할을 수행한다,
  • 입력 이미지가 들어왔을 때 이미지에 대한 특성을 감지하기 위해 커널이나 필터를 사용해서 모든 영역을 훑으면서 특성을 추출하는데, 이 결과물이 특성 맵이다.
  • 이때 스트라이드라는 지정된 간격에 따라 순차적으로 이동한다.

외워야됨

  • 합성곱층의 출력 데이터 : W2 = (W1-F+2P)/S+1

풀링층

  • 특성 맵의 차원을 다운 샘플링하여 연산량을 감소시키고, 주요한 특성 벡터를 추출하여 학습을 효과적으로 할 수 있게 한다.
  • 다운 샘플링 = 이미지 크기 축소
  • 차원이 축소된다.
  • 최대 풀링 : 대상 영역에서 최댓값 추출
  • 평균 풀링 : 대상 영역에서 평균을 반환

완전연결층

  • 합성곱층과 풀링층을 거치면서 차원 축소된 특성 맵은 최종적으로 완결연걸층으로 전달된다.
  • 이미지를 3차원 벡터에서 1차원 벡터로 펼친다.

출력층

  • 소프트맥스 활성화 함수가 사용되는데, 입력받은 값을 0~1사이의 값으로 출력한다.
  • 이미지가 각 레이블에 속할 확률 값이 출력되며, 이때 가장 높은 확률 값을 갖는 레이블이 최종 값으로 선정된다.
  • 알아두면 좋은 명령어

model = nn.DataParallel(net) # gpu를 개수만큼 분산해서 모델 학습하게 하는 명령어


## 합성곱 신경망 맛보기

```python

# 라이브러리 호출
import numpy as np
import matplotlib.pyplot as plt

import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.nn.functional as F

import torchvision
import torchvision.transforms as transforms # 데이터 전처리를 위해 사용하는 라이브러리
from torch.utils.data import Dataset, DataLoader

# cpu 혹은 gpu 장치 확인
device = torch.device("cuda:0" if torch.cuda.is_available() else 'cpu')

# gpu사용
## 하나의 gpu사용시
device = torch.device("cuda:0" if torch.cuda.is_available() else 'cpu')
model = Net()
model.to(device)

## 다수의 GPU 사용시
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = Net()
if torch.cuda.device_count() > 1:
    model = nn.DataParallel(net)
model.to(device)

# fashion_mnist 데이터셋 내려받기
train_dataset = torchvision.datasets.FashionMNIST("../chap05/data", download=True, transform=transforms.Compose([transforms.ToTensor()]))

test_dataset = torchvision.datasets.FashionMNIST("../chap05/data", download=True, train=False, transform=transforms.Compose([transforms.ToTensor()]))

# fashion_mnist 데이터를 데이터로더에 전달
### 데이터로더는 배치사이즈만큼 데이터를 가져오는 아이임
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=100)

test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=100)

# 분류에 사용될 클래스 정의
labels_map = {0 : 'T-Shirt', 1 : 'Trouser', 2: 'Pullover', 3: 'Dress', 4: 'Coat', 5: 'Sandal', 6 : 'Shirt', 7: 'Sneaker', 8 : 'Bag', 9: 'Ankle Boot'}

fig = plt.figure(figsize=(8,8));

columns = 4;
rows = 5;

for i in range(1, columns*rows +1):
    img_xy = np.random.randint(len(train_dataset));
    img = train_dataset[img_xy][0][0,:,:]
    fig.add_subplot(rows, columns, i)
    plt.title(labels_map[train_dataset[img_xy][1]])
    plt.axis('off')
    plt.imshow(img, cmap='gray')
plt.show()

# 심층 신경망 모델 생성
class FashionDNN(nn.Module):
    def __init__(self):
        super(FashionDNN,self).__init__()
        self.fc1 = nn.Linear(in_features=784,out_features=256)
        self.drop = nn.Dropout2d(0.25)
        self.fc2 = nn.Linear(in_features=256,out_features=128)
        self.fc3 = nn.Linear(in_features=128,out_features=10)

    def forward(self,input_data):
        out = input_data.view(-1, 784)
        out = F.relu(self.fc1(out))
        out = self.drop(out)
        out = F.relu(self.fc2(out))
        out = self.fc3(out)
        return outlearing_rate = 0.001;

# 심층 신경망에서 필요한 파라미터 정의
model = FashionDNN(); # 이건 딥 뉴럴 네트워크인듯?!
model.to(device)

criterion = nn.CrossEntropyLoss();
optimizer = torch.optim.Adam(model.parameters(), lr=learing_rate);
print(model)

learing_rate = 0.001;
model = FashionDNN();
model.to(device)

criterion = nn.CrossEntropyLoss();
optimizer = torch.optim.Adam(model.parameters(), lr=learing_rate);
print(model)

# 심층 신경망을 이용한 모델 학습
num_epochs = 5
count = 0
loss_list = []
iteration_list = []
accuracy_list = []

predictions_list = []
labels_list = []

for epoch in range(num_epochs):
    for images, labels in train_loader:
        images, labels = images.to(device), labels.to(device) # GPU쓰려고 to(device)를 해주는 듯

        train = Variable(images.view(100, 1, 28, 28)) # Variable을 활용해서 역전파를 위한 미분 값을 자동으로 계산해준다. 자동 미분을 계산하기 위해서는 torch.autograd 패키지 안에 있는 Variable을 이용해야 동작한다.
        labels = Variable(labels)

        outputs = model(train) # 학습 데이터를 모델에 적용
        loss = criterion(outputs, labels)
        optimizer.zero_grad() # zero_grad() method는 optimizer에 연결된 parameter들의 gradient를 0으로 만든다. # 이번 step에서 쌓아놓은 파라미터들의 변화량을 0으로 초기화하여 다음 step에서는 다음 step에서만의 변화량을 구하도록 함
        loss.backward() # 변화된 정도를 계산한다.# 역전파 단계(backward pass), 파라미터들의 에러에 대한 변화도를 계산하여 누적함
        optimizer.step() # argument로 전달받은 파라미터를 업데이트 한다. # optimizer에게 loss function를 효율적으로 최소화 할 수 있게 파라미터 수정 위탁

        count +=1

        if not (count % 50): # count를 50으로 나누었을 때 나머지가 0이 아니라면 실행
            total = 0
            correct = 0
            for images, labels in test_loader:
                images, labels = images.to(device), labels.to(device)
                labels_list.append(labels)
                test = Variable(images.view(100, 1, 28, 28))
                outputs = model(test)
                predictions = torch.max(outputs, 1)[1].to(device)
                predictions_list.append(predictions)
                correct += (predictions == labels).sum()
                total += len(labels)

            accuracy = correct * 100 / total
            loss_list.append(loss.data)
            iteration_list.append(count)
            accuracy_list.append(Accuracy)

        if not (count % 500):
            print("Iteration: {}, Loss: {}, Accuracy: {}%".format(count, loss.data, accuracy))

nn.DataParallel을 사용할 경우 배치 크기가 알아서 각 GPU로 분배되는 방식으로 작동한다. 따라서 GPU 수만큼 배치 크기도 늘려 주어야 합니다.

  • 패딩 = 덧대기
  • 합성곱 신경망 정의
    class FashionCNN(nn.Module):  
    def **init**(self):  
    super(fashionCNN, self).**init**()  
    self.layer1 = nn.Sequential(  
    nn.Conv2d(in\_channels=1, out\_channels=32, kernel\_size=3, padding=1),  
    nn.BatchNorm2d(32), # 피처맵의 정규화를 위해서 존재한다. 다만, 비효율적이다. 전체에 대해서는 정규화를 못하지만, Batch단위로 정규화를 하면, 전체 데이터를 돌리지 않고 배치 사이즈 만큼만 돌려서 그나마 효율적으로 할 수 있다. 어느정도 효과가 있다고 한다.  
    nn.ReLU(),  
    nn.MaxPool2d(kernel\_size=2, stride=2)  
    )  
    self.layer2 = nn.Sequential(  
    nn.Conv2d(in\_channels=32, out\_channels=64, kernel\_size=3),  
    nn.BatchNorm2d(64),  
    nn.ReLU(),  
    nn.MaxPool2d(2)  
    )  
    self.fc1 = nn.Linear(in\_features=64_6_6, out\_features=600)  
    self.drop = nn.Dropout2d(0.25)  
    self.fc2 = nn.Linear(in\_features=600, out\_features=120)  
    self.fc3 = nn.Linear(in\_features120, out\_features=10)
    
    

def forward(self, x):
out = self.layer1(x)
out = self.layer2(out)
out = out.view(out.size(0), -1)
out = self.fc1(out)
out = self.drop(out)
out = self.fc2(out)
out = self.fc3(out)
return out





# 전이학습


## 특성 추출 기법
---

### 특성 추출
- 데이터셋으로 사전 훈련된 모델을 가져온 후 마지막에 완전 연결층 부분만 새로 만드는 것
- 학습할 때는 마지막 완전연결층(이미지의 카테고리를 결정하는 부분)만 학습하고 나머지 계층들은 학습되지 않도록 한다.


- pip install opencv-python

xcb관련 오류가 발생한다면 터미널에 다음 명령어를 입력해보자...

export QT_QPA_PLATFORM=offscreen



```python

# 라이브러리 호출
import os
import time
import copy
import glob
import cv2
import shutil

import torch
import torchvision
import torchvision.transforms as transforms
import torchvision.models as models
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader

import matplotlib.pyplot as plt

data_path = '../chap05/data/catanddog/train' # 이미지 데이터가 위치한 경로

transform = transforms.Compose( # 데이터 전처리
                [
                    transforms.Resize([256,256]),
                    transforms.RandomResizedCrop(224),
                    transforms.RandomHorizontalFlip(),
                    transforms.ToTensor()
])

train_dataset = torchvision.datasets.ImageFolder(data_path, transform=transform)

train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=32, num_workers=8, shuffle=True)

print(len(train_dataset))

# 학습에 사용될 이미지 출력
samples, labels = iter(train_loader).next() # 이게 안되면 next(iter(train_loader))로 해보자.

classes = {0: 'cat', 1:'dog'}

fig = plt.figure(figsize=(16,24))

for i in range(24): # 24개의 이미지 데이터 출력
    a = fig.add_subplot(4,6,i+1)
    a.set_title(classes[labels[i].item()])
    a.axis('off')
    a.imshow(np.transpose(samples[i].numpy(), (1,2,0)))
plt.subplots_adjust(bottom=0.2, top=0.6, hspace=0)
  • 사전 훈련된 모델의 파라미터 학습 유무 지정(즉, 전이학습)
def set\_parameter\_requires\_grad(model, feature\_extracting=True):  
if feature\_extracting:  
for param in model.parameters():  
param.requires\_grad = False

set\_parameter\_requires\_grad(resnet18)

ResNet18에 완전연결층 추가

resnet18.fc = nn.Linear(512, 2) # 2는 클래스가 두 개라는 의미

모델의 파라미터 값 확인

for name, param in resnet18.named\_parameters(): # model.named\_parameters()는 모델에 접근하여 파라미터 값들을 가져올 때 사용  
if param.requires\_grad:  
print(name, param.data)

모델 객체 생성 및 손실 함수 정의

model = models.resnet18(pretrained=True) # 모델의 객체 생성

for param in model.parameters(): # 모델의 합성곱층 가중치 고정  
param.requires\_grad = False

model.fc = torch.nn.Linear(512, 2)

for param in model.fc.parameters(): # 완전연결층은 학습  
param.requires\_grad = True

optimizer = torch.optim.Adam(model.fc.parameters())  
cost = torch.nn.CrossEntropyLoss() # 손실 함수 정의  
print(model)

모델 학습을 위한 함수 생성

def train\_model(model, dataloaders, criterion, optimizer, device, num\_epochs=13, is\_Train=True):  
since = time.time() # 컴퓨터의 현재 시각을 구하는 함수  
acc\_history = \[\]  
loss\_history = \[\]  
best\_acc = 0.0
for epoch in range(num_epochs):
    print('Epoch {}/{}'.format(epoch, num_epochs-1))
    print('-' * 10)

    running_loss = 0.0
    running_corrects = 0

    for inputs, labels in dataloaders: # 데이터로더에 전달된 데이터만큼 반복
        inputs = inputs.to(device)
        labels = labels.to(device)

        model.to(device)
        optimizer.zero_grad() # 기울기를 0으로 설정
        outputs = model(inputs) # 순전파 학습
        loss = criterion(outputs, labels)
        _, preds = torch.max(outputs, 1)
        loss.backward() # 역전파 학습
        optimizer.step()

        running_loss += loss.item() * input.size(0) # 출력 결과와 레이블의 오차를 계산한 결과를 누적하여 저장
        running_corrects += torch.sum(preds == labels.data) # 출력 결과와 레이블이 동일한지 확인한 결과를 누적하여 저장

    epoch_loss = running_loss / len(dataloaders.dataset) # 평균 오차 계산
    epoch_acc = running_corrects.double() / len(dataloaders.dataset) # 평균 정확도 계산

    print('Loss: {:.4f} Acc: {:.4f}'.format(epoch_loss, epoch_acc))

    if epoch_acc > best_acc:
        best_acc = epoch_acc

acc_history.append(epoch_acc.item())
loss_history.append(epoch_loss)
torch.save(model.state_dict(), os.path.join('../chap05/data/catanddog/', '{0:0=2d}.pth'.format(epoch))) # 모델 재사용을 위해 저장

print()
time\_elapsed = time.time() - since # 실행시간계산  
print('Training complete in {:.0f}m {:.0f}s'.format(time\_elapsed // 60, time\_elapsed % 60))

print('Best Acc: {:4f}'.format(best\_acc))

return acc\_history, loss\_history # 모델의 정확도와 오차를 반환

파라미터 학습 결과를 옵티마이저에 전달

param\_to_update = \[\]

for name, param in resnet18.named\_parameters():  
if param.requires\_grad == True:  
params\_to\_update.append(param) # 파라미터 학습 결과를 저장  
print('\\t', name)

optimizer = optim.Adam(params\_to\_update) # 학습 결과를 옵티마이저에 전달

모델 학습

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  
criterion - nn.CrossEntropyLoss() # 손실 함수 지정  
train_acc_hist, train_loss_hist = train\_model(resnet18, train_loader, criterion, optimizer, device)

테스트 데이터 호출 및 전처리

test_path = '../chap05/data/catanddog/test'

transform = transforms.Compose(  
[  
transforms.Resize(224),  
transforms.CenterCrop(224),  
transforms.ToTensor(),  
])

test_dataset = torchvision.datasets.ImageFolder(  
root=test_path,  
transform=transform  
)

test_loader = torch.utils.data.DataLoader(  
test_dataset,  
batch\_size=32,  
num_workers=1,  
shuffle=True  
)

print(len(test_dataset))

테스트 데이터 평가 함수 생성

def eval_model(model, dataloaders, device):  
since = time.time()  
acc_history = []  
best_acc = 0.0


saved_models = glob.glob('../chap05/data/catanddog/' + '*.pth')
saved_models.sort() # 불러온 .pth 파일들을 정렬
print('saved_model', saved_models)

for model_path in saved_models:
    print('Loading model', model_path)

    model.load_state_dict(torch.load(model_path))
    model.eval() # dropout이 꺼진다.
    model.to(device)
    running_corrects = 0

    for inputs, labels in dataloaders: # 테스트 반복
        inputs = inputs.to(device)
        labels = labels.to(device)

        with torch.no_grad(): # autograd를 사용하지 않겠다는 의미
            outputs = model(inputs) # 데이터를 모델에 적용한 결과를 outputs에 저장

        _, preds = torch.max(outputs.data, 1)
        preds[preds >= 0.5] = 1 # torch.max로 출력된 값이 0.5보다 크면 올바르게 예측
        preds[preds < 0.5] = 0 # torch.max로 출력된 값이 0.5보다 작으면 틀리게 예측
        running_corrects += preds.eq(labels.cpu()).int().sum()

    epoch_acc = running_corrects.double() / len(dataloaders.dataset) # 테스트 데이터의 정확도 계산
    print('Acc: {:.4f}'.format(epoch_acc))

    if epoch_acc > best_acc:
        best_acc = epoch_acc

    acc_history.append(epoch_acc.item())
    print()

time_elapsed = time.time() - since
print('Validation complete i {:.0f}m {:.0f}s'.format(time_elapsed / 60, time_elapsed % 60))
print('Best Acc: {:4f}'.format(best_acc))

return acc_history # 계산된 정확도 반환

테스트 데이터를 평가 함수에 적용

val_acc_hist = eval_model(resnet18, test_loader, device)

훈련과 테스트 데이터의 정확도를 그래프로 확인

plt.plot(train_acc_hist)  
plt.plot(val_acc_hist)  
plt.show()

훈련 데이터의 오차에 대한 그래프 확인

plt.plot(train_loss_hist)  
plt.show()

예측 이미지 출력을 위한 전처리 함수

def im_convert(tensor):  
image = tensor.clone().detach().numpy()  
image = image.transpose(1, 2, 0)  
image = image * (np.array((0.5,0.5,0.5)) + np.array((0.5,0.5,0.5)))  
image = image.clip(0, 1)  
return image

개와 고양이 예측 결과 출력

classes = {0: 'cat', 1:'dog'} # 개와 고양이 두 개에 대한 레이블

dataiter = iter(test_loader) # 테스트 데이터셋 가져옴  
images, labels = dataiter.next() # 테스트 데이터셋에서 이미지와 레이블을 분리하여 가져온다.  
output = model(images)  
_, preds = torch.max(output, 1)

fig = plt.figure(figsize=(25,4))  
for idx in np.arrange(20):  
ax = fig.add_subplot(2, 10, idx+1, xticks=[], yticks=[])  
plt.imshow(im_convert(images[idx]) # 이미지 출력을 위해 전에서 정의한 im_convert 함수를 적용  
a.set_title(classes[labels\[i].item()])  
ax.set_title("{}({})".format(str(classes[preds[idx].item()]), str(classes[labels[idx].item()])), color=("green" if preds[idx]==labels[idx] else "red"))

plt.show()  
plt.subplots_adjust(bottom=0.2, top=0.6, hspace=0)

'프로그램 > SKT FLY AI' 카테고리의 다른 글

SKT FLY AI : 24일차  (0) 2023.07.27
SKT FLY AI : 22일차  (0) 2023.07.25
SKT FLY AI : 20일차  (0) 2023.07.22
SKT FLY AI : 19일차  (0) 2023.07.21
SKT FLY AI : 18일차  (0) 2023.07.19