1. Deep Learning Framework
Framework
'Framework'란 Frame(틀, 규칙)과 Work(일, 소프트웨어의 목적)의 합성어로, 일정한 틀과 뼈대를 가지고 무언가를 만드는 일을 의미
그렇기에 Deep Learning에서 사용하는 Framework는
'딥러닝을 쓰고 싶은 사람들이 쉽게 사용할 수 있도록 다양한 기능을 구현한 작성 규칙이 있는 라이브러리' 를 의미
DL Framework 종류
- theano
- Tensorflow
- Keras
- PyTorch
theano
- 최초의 딥러닝 라이브러리
- 파이썬 기반이며 CPU 및 GPU의 수치 계산에 매우 유용
- 다만 확장성이 뛰어나지 않고 다중 GPU지원이 부족하다는 단점이 있음
Tensorflow
- 인기있는 DL 라이브러리 중 하나이며, 구글에서 개발되었으며 2015년에 오픈소스로 공개됨
- Python 기반이고 여러 CPU 및 GPU와 모든 플랫폼에서 사용 가능함
- C++과 R과 같은 다른 언어도 지원
- 다만 다른 Framework에 비해 속도가 느린편
Keras
- 효율적인 신경망 구축을 위해 단순화된 인터페이스로 개발됨
- Python 기반이며 배우기 쉽다는 장점이 있음
- 문서화가 제대로 되어 있지 않고 이용자 수가 적은 단점이 존재
PyTorch
- Lua 기반의 딥러닝 Framework로 GPU 처리를 위해 C/C++ 라이브러리와 CUDA를 사용
- Python을 기반으로 한 PyTorch가 최근 큰 인기를 얻음
- 심층 신경망과 강력한 GPU 가속을 가진 Tensor Computing이 포함된 Python 패키지 형태로 제공됨
- 다만 TensorFlow보다는 디테일한 모델링이 불가능함
- 현재는 가장 인기 있는 TensorFLow를 PyTorch가 빠른 속도로 따라잡고 있음
2. ML vs DL
AI (Artificial Intelligence)
- 지능형 기계를 만드는 과학이나 공학의 분야 / 인간의 지능(지적능력, 사고방식) 을 인공적으로 컴퓨터 시스템을 통해 구현한 것
ML (Machine Learning)
- 입력 데이터가 주어졌을 때 답을 유추해 줄 수 있는 최적의 함수를 기계가 찾는 것
- 기존 데이터에 알고리즘을 사용해 모델을 만들어내고, 새로운 데이터에 해당 모델을 적용시켜 패턴을 학습하고 결과를 추론하는 기법
- 데이터를 기반으로 통계적인 신뢰도를 강화
- 예측 오류를 최소화하기 위해 다양한 수학적 기법을 사용
- 데이터 내의 패턴을 스스로 인지하고 신뢰도있는 예측결과를 도출해 내는 함수를 찾는 것
- 머신러닝 개요
DL (Deep Learning)
- 머신러닝의 방법론 중 하나 (비선형 정보처리를 수행하는 계층을 여러 겹으로 쌓아서 학습모델을 구현하는 머신러닝의 한 분야)
- 엄청나게 많은 데이터에서 중요한 패턴을 잘 찾아냄, 규칙도 잘 찾아내고, 의사결정을 잘하게 됨
ML vs DL
- 특성 추출
- 학습 과정
Feature Extraction (특성 추출)
- ML과 DL의 차이 중 하나는 데이터에 접근하는 방식이 다름
- Machine Learning은 주어진 데이터의 특성을 인간이 직접 추출함
- Deep Learning은 컴퓨터가 데이터의 특징을 스스로 추출함
- DL은 ML보다 더 많은 양의 데이터를 요구하기 때문에 훈련 시간이 일반적으로 더 길다
- Feature Extraction: 데이터별로 어떤 특징을 가지고 있는지 찾아내고, 그것을 토대로 데이터를 벡터로 변환하는 과정
Learning Process (학습 과정)
- Machine Learning은 학습 단계와 예측 단계로 나뉘어 학습 단계에서 만든 모델로 예측을 함
- Deep Learning도 크게 다르지 않지만 특성 추출을 하는 과정이 없고 모델을 정의하는 부분이 있음
DL Process
- 데이터 준비 = 데이터 전처리
- 모델 정의
- Neural Network를 형성
- Performance와 Overfitting 사이의 trade-off에서 적절한 모델을 생성
- Model Compile
- Optimizer, Loss function, Activation Function을 선택
- Model Training
- 모델이 한 번에 처리해야 할 Data Sample의 개수 = Batch Size를 결정
- 훈련의 횟수 = Epoch를 설정
- Model Prediction
- 실제로 예측을 진행
3. Deep Learning Terminology
Layer (층)
- Input Layer: Input Features (Input Vector)등의 데이터를 받아들이는 층
- Hidden Layer: 앞의 입력 노드들(Layer)로부터 Input을 받아 Linear Model(가중합)을 계산하고, 이 값을 Activation Function에 적용하여 Output Layer에 전달하는 층
- Output Layer: Neural Network의 최종 결과값이 포함된 층
Weight
- Node간의 연갈 강도
- Weight가 클수록 대응되는 입력(x)의 영향도가 커짐
Bias
- 가중합(Linear Combination)에 더해주는 상수
- 하나의 Neuron에서 Activation Function을 거쳐 최종적으로 출력되는 값을 조절하는 역할
- 민감도로, bias가 클수록 해당 Neuron은 쉽게 활성화됨
Function
- Activation Function: Input 신호를 전달받아 이를 비선형 정보처리하여 출력해 주는 함수
- Loss Function: Weight 학습을 위해 실제 값과 예측값(출력 함수의 결과)간의 오차를 측정하는 함수
Activation Function
- Sigmoid Function
- 하나의 뉴런 or 퍼셉트론이 입력 신호를 선형결합하여 만든 값에 hard threshold(Step Function)를 부드러운 형태의 곡선으로 근사
- 실수 전체의 값을 (0,1) 사이의 실수 값으로 mapping
- logistic regression의 경우 positive class에 대응하는 확률 값으로 해석
- Gradient Vanishing Problem
- $ 0 < \frac{\partial \sigma (x)}{\partial x} = (1-\sigma(x))\sigma(x) \leq \frac {1} {4}, 0<\sigma(x) <1 $, 즉 Gradient의 범위가 $(0, \frac {1} {4}]$ 이므로 Backpropagtion 할 때마다 Gradient값이 점차 작아지는 양상을 보임
- multi-layer neural network에서 sigmoid function의 backpropgation을 수행할 때마다 gradient 값이 0에 수렴
- 앞쪽의 layer의 parameter에 도달하는 gradient 값이 굉장히 작음: 앞쪽의 parameter들의 update가 거의 일어나지 않음
- Hyperbolic Tangent Function
- $tahh(x) = 2 \times sigmoid(2x) -1 = \frac{e^{z} - e^{-z}}{e^{z} + e^{-z}}$
- Sigmoid 함수를 x축으로 $\frac {1} {2}$배 압축 & y축으로 2배 확대 -> y축으로 -1만큼 평행이동
- 치역의 범위가 (-1,1)으로 한정
- Strength
- Zero-centered (average = 0)
- Weakness
- gradient의 범위가 $0< \frac{\partial tanh(x)}{\partial x} = 1 - tanh(x)^{2} = (1+tanh(x))(1-tanh(x)) \leq 1$, $(0,1]$
- backpropagation할 때마다 gradient vanishing 문제를 여전히 가지게 됨
- ReLU Activation (Rectified Linear Unit)
- $f(x) = max(0,x)$
- Strength
- gradient vanishing 문제를 해결 (gradient 값이 계속 1)
- 다른 활성함수에 비해 간단한 연산: computationally efficient
- Weakness
- Not zero-centered output
- 음수 범위에서는 gradient 값이 0: 이후의 gradient가 모두 0이 되는 문제
- Leakly ReLU Activation (Rectified Linear Unit)
- $f(x) = max(\alpha x, x)$, $\alpha$는 매우 작은 값
- ReLU 함수를 사용시 음수 값을 입력받으면 항상 0을 출력해(뉴런이 죽음) 학습 능력이 감소하는 문제를 해결하기 위해 고안됨
- 음수 부분의 기울기는 작은 값 사용 (음수 부분의 기울기는 학습하지 않기 위해서)
- Softmax Activation
- $ p_{j} = \frac {e^{z_{j}}} {\sum_{k=1}^{K} e^{z_{k}}}, j= 1, 2, ..., K$
- Output Vector의 총합이 1이고, 각각 [0.1] 사이의 확률분포를 가지는 Activation Function
- 지수함수 $e^{z_{j}}$를 사용하는 이유
- 미분가능하게 만들기 위해
- 작은 값은 더 작게, 큰 값은 더 크게 scaling하기 위해
Loss Function
- MSE (Mean Squared Error)
- 회귀 문제에서 주로 사용
- Cross - Entropy Error (CEE)
- 분류 문제에서 정답 Label에 One-Hot Encoding을 했을 때만 사용
- Sigmoid + MSE: 매끄럽지 않은 그래프가 출력되는 문제 발생
- Softmax + CEE: 다중 분류 문제에서 주로 사용
DNN (Deep Neural Network)
- Input Layer과 Output Layer 사이에 여러 Hidden Layer로 이루어진 ANN (Artificial Neural Network)
- DNN을 기반으로 목적에 따라 CNN, RNN, RBM, DBN으로 분류됨
- CNN: Convolutional Neural Network
- RNN: Recurrent Neural Network
- RBM: Restricted Boltzmann Machine
- DBN: Deep Belief Network
CNN (Convolutional Neural Network)
- 음성 인식이나 이미지, 영상 인식에서 주로 사용됨
- 다차원 배열 처리에 특화되어 있으며, 다음의 계층으로 구성됨
- Input Layer
- Input Image Data가 최초로 거치는 계층
- Image는 Height, Width, Channel의 3차원 데이터 (Channel은 Image가 grayscale이면 1, Color면 3)
- Convolutional Layer
- Input Image Data에서 특성을 추출하는 역할
- Image 전체를 Stride라는 간격에 따라 순차적으로 이동해 Kernel(Filter)로 이미지의 특성을 추출함
- 이렇게 추출된 결과를 Feature Map 이라 한다
- 외곽에 있는 정보들의 유실을 막기 위해 특정 값으로 채우는 Padding이 있음
- Pooling Layer
- Feature Map의 Dimension을 Down-Sampling하여 연산량을 감소시킴
- 중요한 특성 벡터를 추출함
- Max - Pooling: 대상 영역에서 Maximum 값을 추출
- Average - Pooling: 대상 영역에서 Average 값을 반환
- Fully Connected Layer
- Image를 1-dimension vector로 펼친 후 다음의 층의 노드와 이전 층의 노드가 모두 연결되어 신호를 전달
- 이미지를 분류, 설명하는 데 가장 적합하게 예측
- Output Layer
- Multi-Class Classification의 경우 softmax 함수를 사용
4. Cross - Entropy Loss
$Cross \: Entropy = -\sum_{i=1}^{n} y_{i}log \hat{y}_{i}$
- CEE는 분포간의 차이에 집중함
- 분류 문제에서 정답 Label에 One-Hot Encoding을 했을 때만 사용
- ML/DL Model은 실제로 존재하는 어떤 분포를 잘 묘사해주는 확률 모델을 구하는 것과 같음
- 정답 분포가 신경망으로 구성된 분포인가에 상관없이 신경망을 이용해서 해당 분포를 묘사할 수 있다고 생각함
- 보편 근사 정리(Universal approximation Theorem): 하나의 Hidden Layer를 갖는 ANN은 임의의 연속인 다변수 함수를 원하는 정도의 정확도로 근사할 수 있음
Kullback - Lively Divergence (KLD)
$ D_{KL}(p\parallel q) = \sum_{x}^{}p(x)log\frac{p(x)}{q(x)} = E_{x\sim p}\left [ log \frac{p(x)}{q(x)} \right ]$
- 모델로 만든 분포 q와 실제 분포 p 사이에서 분포간의 차이를 나타낼 때 사용
- x가 실제 분포 P를 따른다: $x \sim p$
CE with Distribution (분포 p,q)
$ D_{KL}(p\parallel q) = \sum_{x}^{}p(x)log\frac{p(x)}{q(x)}$
$= \sum_{x}^{}p(x)logp(x) - \sum_{x}^{}p(x)logq(x) = -H(p) + CE(p,q) = CE(p,q)$
$\therefore D_{KL}(p\parallel q) = CE(p,q)$
- 식 변형을 하면 KL Divergence는 p, q의 Cross - Entropy에서 p의 Entropy를 뺀 값
- 그런데 Classification Problem의 경우 p는 실제 값(고정값 하나)를 갖기 때문에 p의 Entropy $H(p) = 0$가 됨 ($p(x) = 1$)
- 따라서, Classfication Problem의 경우 $D_{KL}(p\parallel q) =CE(p,q)$
CE with Probability (확률 P)
$CE(X_{a}, Y_{a}) = -\sum_{c=1}^{C}P(X_{a}=c)logP(Y_{a}=c) $
$= -P(X_{a}=1)logP(Y_{a}=1) = -logP(Y_{a}=1)$
- Cross - Entropy는 위와 같이 적을 수도 있다
- Classification에서 One- Hot Encoding을 하므로 정답 Label만 1이다
- 따라서 정답 Label이 1인 $P(X_{a}=1)=1$항만 남고, 나머지는 지워진다
Expectation of Random Variable g(X)
$E\left [ g(X) \right ] = \sum g(x)P_{X}(x)$
- $\sum (변수 \times \: 확률)$
- 변수는 합성 form 그대로
- 확률은 Main(원본) 변수의 확률
Jensen Inequality
$f(E_{x\sim p}\left [ x \right ]) \leq E_{x \sim p}\left [ f(x) \right ]$
- 볼록함수 f와 적절한 분포 p에 대해 위의 식이 성립한다
- $f(x) = - log(x)$는 볼록함수이다
$CE(X_{a}, Y_{a}) = - \sum_{c=1}^{C} P(X_{a} = c)log P(Y_{a} = c) = \sum_{c=1}^{C} p(c) (-log q(c)) $
$= \sum_{c=1}^{C}p(c)f(q(c)) = E_{c \sim p}\left [ f(q(c)) \right ] \geq f(E_{c \sim p}\left [ q(c) \right ])$ (Jensen)
$= f(\sum_{c=1}^{C}p(c)q(c))$
$ f(\sum_{c=1}^{C}p(c)q(c)) = f(P(X_{a} =Y_{a}))$
- 위의 식의 마지막 부분은 분포 p와 분포 q가 같은 값을 가질 때의 확률을 의미함
- 즉, 우리가 예측한 Y와 실제 X가 같은 값(c)을 가질 확률이다
- Two Independent Discrete Random Variable X, Y가 있고, 각각이 취할 수 있는 값이 1부터 n이라고 할 때, 두 Random Variable이 같을 확률은 다음과 같다: $P(X = Y) = \sum_{i=1}^{n} P(X = i) \cdot P(Y = i)$
$CE(X_{a}, Y_{a}) \geq f(P(X_{a} = Y_{a}))$
$CE(X_{a}, Y_{a}) \geq -log P(X_{a} = Y_{a})$
$- CE(X_{a}, Y_{a}) \leq log P(X_{a} = Y_{a})$
$ e^{-CE(X_{a}, Y_{a})} \leq P(X_{a} = Y_{a})$
- 위에서부터 식 정리를 해보면 Two Random Variable이 같을 Probability의 Minimum을 Cross - Entropy로 구할 수 있음
- 만약 Cross - Entropy Error = 0.5라면 $ \frac{1}{\sqrt{e}} \approx 0.6065 \leq P(X_{a} = Y_{a})$ 60%의 정답률을 보장할 수 있음
- 추상적인 손실 함수인 Cross - Entropy를 넘어서 하한선을 제시하게 된 것이 Point라 할 수 있다
5. Regularization (Weight Decay) for Overfitting
6. DNN & Activation Function
DNN (Deep Neural Network)
- Input Layer과 Output Layer 사이에 2개 이상의 Hidden Layer로 이루어진 Neural Network
- Input Vector에 Weight를 곱하고 Bias를 더한 뒤 Activation Function을 거쳐 출력됨
Activation Function
- 입력 신호의 총합을 출력 신호로 변환하는 함수 (= 뉴런을 활성화하는 함수)
- 입력 신호의 총합이 활성화를 일으키는지를 정하는 역할을 한다
- Input: Input Vector(Feature) & Weight(Model Parameter)의 선형결합
- Hidden Layer과 Output Layer에 사용하는 Activation Function이 다르다
Hidden Layer Activation
- 연산에 따라 Hidden Layer의 Activation Functio이 다름
- MLP (Multi-Layer Perceptron): ReLU 계열
- CNN (Convolutional Neural Network): ReLU 계열
- RNN (Recurrent Neural Network): Sigmoid, Tanh
Output Layer Activation
- Task에 따라 Output Layer의 Activation Function이 다름
- Regression: 출력 노드 1개 + Linear Activation
- Binary Classification: 출력 노드 1개 + Sigmoid
- Multi-Class Classification: 출력 노드 Class 개수 + Softmax
Sigmoid
$Sigmoid(x) = \sigma (x) = \frac{1}{1 + exp(-x)}$
- Shape
- Input: 차원은 마음대로 (element-wise operation)
- Output: 입력차원과 출력차원의 수가 같음
- Parameters
- x
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import numpy as np
class DenseBlock(nn.Module):
def __init__(self, in_dim, out_dim):
super(DenseBlock, self).__init__()
self.dense = nn.Linear(in_dim, out_dim)
self.act = nn.Sigmoid() # activation Function
def forward(self, x):
out = self.act(self.dense(x))
return out
sigmoid = DenseBlock(1, 1)
sigmoid.forward(torch.arange(5).float().unsqueeze(1))
tensor([[0.7101],
[0.5348],
[0.3504],
[0.2021],
[0.1062]], grad_fn=<SigmoidBackward0>)
Tanh
$tanh(x) = \frac{e^{x} - e^{-x}} {e^{x} + e^{-x}}$
- Shape
- Input: 차원은 마음대로 (element-wise operation)
- Output: 입력차원과 출력차원의 수가 같음
- Parameters
- x
class DenseBlock2(nn.Module):
def __init__(self, in_dim, out_dim):
super(DenseBlock2, self).__init__()
self.dense = nn.Linear(in_dim, out_dim)
self.act = nn.Tanh() # activation Function
def forward(self, x):
out = self.act(self.dense(x))
return out
tanh = DenseBlock2(1, 1)
tanh.forward(torch.arange(5).float().unsqueeze(1))
tensor([[0.7324],
[0.9043],
[0.9678],
[0.9894],
[0.9965]], grad_fn=<TanhBackward0>)
ReLU
$ReLU(x) = (x)^{+} = \max(0, x)$
- Shape
- Input: 차원은 마음대로 (element-wise operation)
- Output: 입력차원과 출력차원의 수가 같음
- Parameters
- inplace: (default: False)
- inplace=True로 설정할 경우, 입력을 따로 저장하지 않고 바로 Operation을 진행하기 때문에 메모리를 소량 절약가능
- 대신 원본 입력이 destroy 되는 점을 주의해서 사용할 것
class DenseBlock3(nn.Module):
def __init__(self, in_dim, out_dim):
super(DenseBlock3, self).__init__()
self.dense = nn.Linear(in_dim, out_dim)
self.act = nn.ReLU() # activation function
def forward(self, x):
out = self.act(self.dense(x))
return out
relu = DenseBlock3(1, 1)
relu.forward(torch.arange(5).float().unsqueeze(1))
tensor([[0.0000],
[0.0000],
[0.0288],
[0.1603],
[0.2918]], grad_fn=<ReluBackward0>)
Leaky ReLU
$Leaky \: ReLU = \begin{cases}
x & \text{ if }\; x \geq 0 \\ negative \: slope \times x
& \text{ if } \; otherwise
\end{cases}$
- Shape
- Input: 차원은 마음대로 (element-wise operation)
- Output: 입력차원과 출력차원의 수가 같음
- Parameters
- negative_slope: 음수 구간에 대하여 음의 기울기를 주는데 이때 기울기의 각도 (default: 1e-2)
- inplace: (default=False)
class DenseBlock4(nn.Module):
def __init__(self, in_dim, out_dim):
super(DenseBlock4, self).__init__()
self.dense = nn.Linear(in_dim, out_dim)
self.act = nn.LeakyReLU()
def forward(self, x):
out = self.act(self.dense(x))
return out
leakyrelu = DenseBlock4(1, 1)
relu.forward(torch.arange(5).float().unsqueeze(1))
tensor([[0.0000],
[0.0000],
[0.0288],
[0.1603],
[0.2918]], grad_fn=<ReluBackward0>)
7. Loss Function
Loss Function (손실 함수)
- 실제값과 예측값 간의 차이(오차)를 비교하기 위한 함수
- Loss Function을 통해 '오차(Loss)'를 구할 수 있고, 이 '오차'를 Optimizer를 통해 Backpropagation을 진행하면서 Model Parameter들(Weight, Bias)을 업데이트
- 이 과정을 반복해서 '오차'를 최소화하는 Model Parameter를 찾는 것이 '학습'의 목표
- MSE (Mean Squared Error): 회귀 문제에서 주로 사용
- Cross - Entropy Loss (CEE): 분류 문제에서 정답 Label에 One-Hot Encoding을 했을 때만 사용
MSE (Mean Squared Error)
$MSE = \frac{1}{N} \sum_{i=1}^{N} (y_{i} - \hat{y}_{i})^{2}$
- Shape
- Input: 차원은 마음대로 (element-wise operation)
- Output: 입력차원과 출력차원의 수가 같음
- Parameters
- reduction: 손실 값을 어떻게 줄일지를 지정하는 함수로, 기본값은 'mean'임
import torch.nn as nn
criterion = nn.MSELoss(reduction='mean')
loss = criterion(output, target)
CEE (Cross-Entropy Loss)
$CEE = - \sum_{i=1}^{n} t_{i} \times log_{e}(y_{i})$
- Shape
- Input: 차원은 마음대로 (element-wise operation)
- Output: 입력차원과 출력차원의 수가 같음
- Parameters
- weight: Class별 가중치를 적용할 경우 사용하는 가중치 Tensor, 기본값은 'None'
- ignore_index: 무시할 Class의 index, 기본값은 -100
- reduction: 손실을 감소(reduce)할 방법을 지정
- 'mean'(평균), 'sum'(합), 'None'(감소하지 않음) 중 하나를 선택
- 기본값은 'mean'
import torch.nn as nn
criterion = nn.CrossEntropyLoss(weight=None, ignore_index=-100, reduction='mean')
loss = criterion(output, target)
7. MNIST Fashion - Code Implementation
- 라이브러리 불러오기
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torchvision import transforms, datasets
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
- M1 Mac에서 GPU 사용
device = torch.device("mps:0" if torch.backends.mps.is_available() else "cpu")
print(f"Using {device} device")
Using mps:0 device
print (f"PyTorch version:{torch.__version__}") # 1.12.1 이상
print(f"MPS 장치를 지원하도록 build 되었는지: {torch.backends.mps.is_built()}") # True 여야 합니다.
print(f"MPS 장치가 사용 가능한지: {torch.backends.mps.is_available()}") # True 여야 합니다.
!python -c 'import platform;print(platform.platform())'
PyTorch version:2.1.2
MPS 장치를 지원하도록 build 되었는지: True
MPS 장치가 사용 가능한지: True
macOS-14.2-arm64-arm-64bit
- 데이터 불러오기
- DataLoader 객체 생성
transform = transforms.Compose([transforms.ToTensor()])
trainset = datasets.FashionMNIST(
root="mnist/data/", train=True, download=True, transform=transform
)
testset = datasets.FashionMNIST(
root="mnist/data/", train=False, download=True, transform=transform
)
batch_size = 64
# Data Loading object define
train_loader = DataLoader(dataset=trainset, batch_size=batch_size)
test_loader = DataLoader(dataset=testset, batch_size=batch_size)
images, labels = next(iter(train_loader))
images.shape, labels.shape
(torch.Size([64, 1, 28, 28]), torch.Size([64]))
- 불러온 데이터 확인
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()
- DNN Model 정의
class DNN(nn.Module):
def __init__(self):
super(DNN, self).__init__()
self.fc1 = nn.Linear(784, 256)
self.fc2 = nn.Linear(256, 128)
self.fc3 = nn.Linear(128, 10)
def forward(self, x):
# input data(image) = x
# x.shape = torch.Size([64, 1, 28, 28])
x = x.view(-1, 784)
# x.shape = torch.Size([64, 784])
x = F.relu(self.fc1(x))
# x.shape = torch.Size([64, 1, 28, 28])
x = F.relu(self.fc2(x))
# x.shape = torch.Size([64, 1, 28, 28])
x = self.fc3(x)
# x.shape = torch.Size([64, 1, 28, 28])
return x
- Loss Function, Optimizer 정의
dnn = DNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(dnn.parameters(), lr=0.01)
print(dnn)
DNN(
(fc1): Linear(in_features=784, out_features=256, bias=True)
(fc2): Linear(in_features=256, out_features=128, bias=True)
(fc3): Linear(in_features=128, out_features=10, bias=True)
)
- Model Training
for epoch in range(10):
running_loss = 0.0
for idx, (inputs, labels) in enumerate(train_loader, start=1):
optimizer.zero_grad()
outputs = dnn(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if idx % 100 == 0:
print(f"Epoch: {epoch+1}, Iter: {idx}, Loss: {running_loss / 2000}")
running_loss = 0.0
Epoch: 1, Iter: 100, Loss: 0.11274098908901214
Epoch: 1, Iter: 200, Loss: 0.10534593224525451
Epoch: 1, Iter: 300, Loss: 0.0916677051782608
Epoch: 1, Iter: 400, Loss: 0.07444168597459794
Epoch: 1, Iter: 500, Loss: 0.06211770743131637
Epoch: 1, Iter: 600, Loss: 0.053908924639225
Epoch: 1, Iter: 700, Loss: 0.048291422337293624
Epoch: 1, Iter: 800, Loss: 0.04505948832631111
Epoch: 1, Iter: 900, Loss: 0.04227367272973061
Epoch: 2, Iter: 100, Loss: 0.03963514050841332
Epoch: 2, Iter: 200, Loss: 0.03892774763703346
Epoch: 2, Iter: 300, Loss: 0.0377043505012989
Epoch: 2, Iter: 400, Loss: 0.036420322865247726
Epoch: 2, Iter: 500, Loss: 0.03534371593594551
Epoch: 2, Iter: 600, Loss: 0.03452681921422482
Epoch: 2, Iter: 700, Loss: 0.03307179023325443
Epoch: 2, Iter: 800, Loss: 0.0329482177644968
Epoch: 2, Iter: 900, Loss: 0.03249312244355679
Epoch: 3, Iter: 100, Loss: 0.031308667823672295
Epoch: 3, Iter: 200, Loss: 0.031194300472736358
Epoch: 3, Iter: 300, Loss: 0.03099349170923233
Epoch: 3, Iter: 400, Loss: 0.03020917384326458
Epoch: 3, Iter: 500, Loss: 0.029420241340994836
Epoch: 3, Iter: 600, Loss: 0.029409253150224687
Epoch: 3, Iter: 700, Loss: 0.028218515291810034
Epoch: 3, Iter: 800, Loss: 0.028444199815392493
Epoch: 3, Iter: 900, Loss: 0.028653488397598268
Epoch: 4, Iter: 100, Loss: 0.02750851745903492
Epoch: 4, Iter: 200, Loss: 0.027589963987469674
Epoch: 4, Iter: 300, Loss: 0.02766475374996662
Epoch: 4, Iter: 400, Loss: 0.027145448565483093
Epoch: 4, Iter: 500, Loss: 0.026435795396566392
Epoch: 4, Iter: 600, Loss: 0.0267858769595623
Epoch: 4, Iter: 700, Loss: 0.025765796557068826
Epoch: 4, Iter: 800, Loss: 0.026108163818717004
Epoch: 4, Iter: 900, Loss: 0.02662244676053524
Epoch: 5, Iter: 100, Loss: 0.025493521004915237
Epoch: 5, Iter: 200, Loss: 0.025620816096663473
Epoch: 5, Iter: 300, Loss: 0.025844009295105934
Epoch: 5, Iter: 400, Loss: 0.025410002455115318
Epoch: 5, Iter: 500, Loss: 0.024744934543967246
Epoch: 5, Iter: 600, Loss: 0.025264536753296853
Epoch: 5, Iter: 700, Loss: 0.024344489380717278
Epoch: 5, Iter: 800, Loss: 0.024727902069687842
Epoch: 5, Iter: 900, Loss: 0.025351004973053932
Epoch: 6, Iter: 100, Loss: 0.02423600558936596
Epoch: 6, Iter: 200, Loss: 0.024362894237041473
Epoch: 6, Iter: 300, Loss: 0.02468249760568142
Epoch: 6, Iter: 400, Loss: 0.024239493906497954
Epoch: 6, Iter: 500, Loss: 0.02359827287495136
Epoch: 6, Iter: 600, Loss: 0.024212505787611006
Epoch: 6, Iter: 700, Loss: 0.023365655660629273
Epoch: 6, Iter: 800, Loss: 0.023749616876244543
Epoch: 6, Iter: 900, Loss: 0.024404321275651456
Epoch: 7, Iter: 100, Loss: 0.02330154873430729
Epoch: 7, Iter: 200, Loss: 0.023423816330730915
Epoch: 7, Iter: 300, Loss: 0.023817730844020845
Epoch: 7, Iter: 400, Loss: 0.023337094768881798
Epoch: 7, Iter: 500, Loss: 0.022725234165787696
Epoch: 7, Iter: 600, Loss: 0.023402464270591734
Epoch: 7, Iter: 700, Loss: 0.022606307789683342
Epoch: 7, Iter: 800, Loss: 0.022971770383417606
Epoch: 7, Iter: 900, Loss: 0.023632540181279183
Epoch: 8, Iter: 100, Loss: 0.022537152357399462
Epoch: 8, Iter: 200, Loss: 0.022644519045948983
Epoch: 8, Iter: 300, Loss: 0.023108271449804305
Epoch: 8, Iter: 400, Loss: 0.022592625133693218
Epoch: 8, Iter: 500, Loss: 0.02200370978564024
Epoch: 8, Iter: 600, Loss: 0.022730184391140936
Epoch: 8, Iter: 700, Loss: 0.02196316412091255
Epoch: 8, Iter: 800, Loss: 0.022315398395061494
Epoch: 8, Iter: 900, Loss: 0.022968250036239625
Epoch: 9, Iter: 100, Loss: 0.02190307091921568
Epoch: 9, Iter: 200, Loss: 0.02201009753346443
Epoch: 9, Iter: 300, Loss: 0.0225182880833745
Epoch: 9, Iter: 400, Loss: 0.02196325647085905
Epoch: 9, Iter: 500, Loss: 0.02140403341501951
Epoch: 9, Iter: 600, Loss: 0.022170505486428738
Epoch: 9, Iter: 700, Loss: 0.02142414051294327
Epoch: 9, Iter: 800, Loss: 0.02177253632247448
Epoch: 9, Iter: 900, Loss: 0.022394394293427468
Epoch: 10, Iter: 100, Loss: 0.021370605804026128
Epoch: 10, Iter: 200, Loss: 0.021461036421358586
Epoch: 10, Iter: 300, Loss: 0.021997104831039907
Epoch: 10, Iter: 400, Loss: 0.021426668494939804
Epoch: 10, Iter: 500, Loss: 0.020882543794810773
Epoch: 10, Iter: 600, Loss: 0.021686403773725032
Epoch: 10, Iter: 700, Loss: 0.020940099701285363
Epoch: 10, Iter: 800, Loss: 0.021298846505582333
Epoch: 10, Iter: 900, Loss: 0.021890796065330505
- Accuracy Evaluation
correct = 0
total = 0
with torch.no_grad():
for images, labels in test_loader:
outputs = dnn(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f"{(correct / total) * 100:.2f}%")
83.24%