입력층이나 출력층과 달리 은닉층의 뉴런은 사람 눈에는 보이지 않는다.

위의 그림 속 신경망은 3층으로 구성되어있지만 가중치를 갖는 층이 2개이기 때문에 2층 신경망 이라고 한다.

 

y=0 (b+w1x1+w2x2<=0)
y=1 (b+w1x1+w2x2>0)
 

편향(bias)은 하나의 뉴런으로 입력된 모든 값을 다 더한 다음에(가중합이라고 합니다) 이 값에 더 해주는 상수입니다. 이 값은 하나의 뉴런에서 활성화 함수를 거쳐 최종적으로 출력되는 값을 조절하는 역할을 함

 

활성화 함수(activateion function) : 입력 신호의 총합을 출력 신호로 변환하는 함수. 변환된 신호를 다음 뉴런에 전달한다. 입력 신호의 총합이 활성화를 일으키는지를 정하는 역할을 한다.

 

 
a = b+w1x1+w2x2 #가중치가 달린 입력 신호와 편향의 총합
y=h(a) #a를 함수 h()에 넣어 y를 출력

 

3-2 활성화함수


시그모이드 함수 (sigmoid function)

 

신경망에서는 활성화 함수로 시그모이드 함수를 이용하여 신호를 변환하고 그 변환된 신호를 다음 뉴런에 전달한다.

  • 시그모이드 함수 구현하기
    브로드캐스트 기능: 넘파이 배열과 스칼라값의 연산을 넘파이 배열의 원소 각각과 스칼라값의 연산으로 바꿔 수행한다.
import numpy as np
import matplotlib.pylab as plt

def sigmoid(x):
  return 1/(1+np.exp(-x))

x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
#브로드캐스트
#넘파이 배열과 스칼라값의 연산을 넘파이 배열의 원소 각각과 스칼라값의 연산으로 바꿔 수행

plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.show()
 
 

계단함수 (step function)

 

def step_function(x):
  return np.array(x>0, dtype=np.int)
  ##numpy 배열을 인수로 넣을 수 있게 하는 방법
    #x = np.array([-1.0, 1.0, 2.0])
    #y = x>0
    #y를 출력하면 0보다 큰 x값은 True로, 0보다 작거나 같은 값은 False로 나온다.
    #booleaan값을 int형으로 변환시키면 True는 0, False는 1이다.
        
x = np.arange(-5.0, 5.0, 0.1)
y = step_function(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) 
plt.show()
 
 

ReLU함수 (rectified linear unit)

 
그림 7. ReLU 수식

def relu(x):
  return np.maximum(0, x)

x = np.arange(-5.0, 5.0, 0.1)
y= relu(x)
plt.plot(x, y)
plt.show()
 
 
그림 8. ReLU 함수

 

3-3 다차원 배열의 계산


np.ndim(): 배열의 차원 수 확인
배열.shape: 배열의 형상 확인

import numpy as np

#1차원 배열
A = np.array([1,2,3,4])
np.ndim(A)
A.shape #튜플로 반환

#2차원 배열
B = np.array([[1,2], [3,4], [5,6]])
np.ndim(B)
B.shape
'''
[[1 2]
 [3 4]
 [5 6]]
2
(3, 2)
'''
 
 

행렬의 곱

 
 
#위 그림을 파이썬으로 구현
A = np.array([[1,2], [3,4]]) #2*2행렬
B = np.array([[5,6], [7,8]]) #2*2행렬
np.dot(A, B)
'''
(2, 2)
(2, 2)
array([[19, 22],
       [43, 50]])
'''​

 

신경망에서의 행렬 곱

X = np.array([1,2])
X.shape #(2,)

W = np.array([[1,3,5], [2,4,6]])
print(W)
'''
[[1 3 5]
 [2 4 6]]
'''
W.shape #(2,3)

Y = np.dot(X, W)
print(Y)
print(Y.shape)
'''
[ 5 11 17]
(3,)
'''
 

 

3층 신경망 구현하기

 
 

은닉층에서의 가중치 합(가중 신호와 편향의 총합)을 a로 표기하고 활성화 함수 h( )로 변환된 신호를 z로 표현한다.

 

 

1층의 '가중치 부분'을 행렬식으로 간소화하면

 
 
def init_network():
  network = {}
  network['w1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
  network['b1'] = np.array([0.1, 0.2, 0.3])

  network['w2'] = np.array([[0.1, 0.4], [0.2, 0.5],[0.3, 0.6]])
  network['b2'] = np.array([0.1, 0.2])

  network['w3'] = np.array([[0.1, 0.3], [0.2, 0.4]])
  network['b3'] = np.array([0.1, 0.2])
  return network

def forward(network, x):
  w1, w2, w3 = network['w1'], network['w2'], network['w3']
  b1, b2, b3 = network['b1'], network['b2'], network['b3']

  a1 = np.dot(x,w1) + b1
  z1 = sigmoid(a1)
  a2 = np.dot(z1, w2) + b2
  z2 = sigmoid(a2)
  a3 = np.dot(z2, w3) + b3
  z3 = sigmoid(a3)
  return z3
  
network = init_network()

x = np.array([1.0, 0.5])
y = forward(network, x)
print(y) # [0.57855079 0.66736228]

 

3-4. 출력층 설계하기

기계학습 문제는 분류(classification)와 회귀(regression)로 나뉜다.
일반적으로 회귀에는 항등함수를 분류에는 소프트맥스 함수를 출력층의 활성화 함수로 사용한다.

 

항등 함수와 소프트맥스 함수 구현하기

  • 항등함수(identity function): 입력을 그대로 출력
  • 소프트맥스 함수(softmax function):

def softmax(a):
  exp_a = np.exp(a)
  sum_exp_a = np.sum(exp_a)
  y = exp_a / sum_exp_a
  
  return y

a = np.array([0.3, 2.9, 4.0])
print(softmax(a)) # [0.01821127 0.24519181 0.73659691]

 

소프트맥스 함수 구현 시 주의점

지수함수를 사용하는 소프트맥스 함수는 '오버플로'의 문제가 발생해 수치가 '불안정'해질 수 있는 문제점이 있다.

  • 오버플로(overflow) : 표현할 수 있는 수의 범위가 한정되어 너무 큰값은 표현할 수 없다.
  • 소프트맥스 함수 구현 개선

-> 소프트맥스의 지수 함수를 계산할 때 어떤 정수를 더해도(혹은 빼도) 결과는 바뀌지 않는다.
-> C '에 어떤 값을 대입해도 상관없지만 오버플로를 막기 위해 입력 신호 중 최댓값을 이용하는 것이 일반적이다.

 

 
def softmax(a):
  c = np.max(a)
  exp_a = np.exp(a-c) # 오버플로우 대책
  sum_exp_a = np.sum(exp_a)
  y = exp_a / sum_exp_a
  
  return y​

 

소프트맥스 함수의 특징

  • 출력값은 0에서 1.0 사이의 실수이다.
  • 출력의 총합은 1이다. -> 확률로 해석가능 (엄밀히 말하자면 확률은 아님, score라고 말하기도 한다.)
  • 지수함수가 단조 증가 함수 이기 때문에 소프트맥스 함수를 적용해도 각 원소의 대소 관계는 변하지 않는다. 결과적으로 신경망으로 분류할 때는 출력층의 소프트맥스 함수를 생략해도 된다.
                                  •  

'vison_study' 카테고리의 다른 글

2.1 퍼셉트론 (밑바닥부터 딥러닝)  (0) 2021.12.22
Torch tensor to numpy(in use Opencv)  (0) 2021.06.28
Vison Study #2  (0) 2021.06.27
Vison_study #1  (0) 2021.06.27

0. Abstarct

(0-1) Residual Learning Framework 제안

  • 특징 1 : 기존 Neural Network(ex : VGG)보다 Layer 수가 더 많아도 학습(train) 및 최적화(optimize)가 쉬움
  • * 학습 및 최적화가 쉽다 = Loss 값이 쉽게 떨어진다 = 최적의 딥러닝 모델 파라미터를 찾기 쉽다
  • 특징 2 : Residual Learning Framework의 깊이가 깊어져도(=Layer 수가 더 많아져도), 정확도가 꾸준하게 향상
  • * 정확도 : 이미지 내 클래스 분류 정확도 등등...
  • 특징 3 : 기존 Neural Network보다 더 많은  Layer 수를 가져도, 파라미터 수는 더 적음
  • * ResNet-152(layers)는 VGG-19보다 Layer 수는 8배 더 많으나, 더 적은 모델 파라미터 수(lower complexity)를 가짐

 

1. Introduction

(1-1) 네트워크의 깊이는 성능에 중요한 영향을 끼침

  • Deep Neural Network는 low/mid/high level features를 통합하고, 여러 개의 Layer에서 end-to-end 방식으로 class를 분류함

Layer에 따른 Feature map의 변화 도식도

  • 이때 feature의 level은 stacked layers 갯수(= Depth)에 영향을 받음
  • -> Neural Network의 Layer 수가 많아질수록 근사화(Approximation)가 강력해짐
  • * 본문에서는 근사화를 Identity Mapping 표현으로 대체
  • 최근 연구(본문의 참고 문헌 : 41, 44)에 따르면 Neural Network의 Depth는 성능(정확도) 향상에 영향을 줌

(1-2) 네트워크의 깊이가 깊어지면 발생하는 문제 : Gradient Vanishing/Exploding (소멸/발산)

  • Gradient Vanishing, Exploding 문제의 정의는 아래와 같음

Gradient Vanishing & Exploding ( 출처 : 위키독스 ) 

  • 그러나! 이러한 문제는 normalized initialization, iintermediate normalization layers, SGD 등을 통해 대부분 해결

    (1-3) 그럼에도 해결되지 않은 문제 : Degradation (성능 저하)
    • Degradation 문제 : Neural Network의 깊이가 증가할수록, 정확도는 포화되다가 급격하게 정확도 감소
    • -> Layer 수를 증가시키는 것은 딥러닝 모델에 높은 학습 에러값을 발생
    • 이러한 저하 문제는 오버피팅에 의해 발생되는 것이 아니며, 최적화가 쉽지 않다는 것을 보여줌
    • 단순하게 Layer 수를 증가하는 것은 성능 향상을 위한 해결책이 아님
     그림 1. Layer 깊이에 따른 train(좌)/test(우) error 비교 // 출처 : 원문
    • Neural Networks는 Layer의 이(Depth)가 깊어질수록 error가 증가하고, 그림 1에서 그 현상을 볼 수 있음
    • 왼쪽 그림 1 : CIFAR-10 데이터에 대한 Tranning Error
    • 오른쪽 그림 1 : CIFAR-10 데이터에 대한 Test Error
    • 빨간색 그래프 :  56-layer (비교적 깊이가 깊은 네트워크)
    • 노란색 그래프 :  20-layer (비교적 깊이가 얕은 네트워크)
    • * 번외 : iter과 epoch의 차이
    iter과 epoch의 차이


    (1-4) 본문의 핵심 아이디어
    • 핵심 아이디어 : Deep Residual Learing Framework를 소개함으로써 degradation 문제를 해결
    그림 2. 기존 Neural Network's Block VS Residual Network's Block

    • Underlying Mapping (기존 매핑) : 기존 Neural Network의 Output -> H(x)
    • Residual Mapping (잔차 매핑) : H(x) = F(x) + x               // x : Layer's input
    • 본 연구의 가정 : residual mapping이 unreferenced mapping보다 최적화하기 쉬움

    (1-5) Residual Mapping의 구현 : Short Connections
    • residual mapping(=F(x) + x)은 short connection에 의해 구현 가능
    • 그림 2의 오른쪽(residual block)에서 'identity'에 해당하는 곡선이 short connection에 해당
    • Short Connection의 역할 : Layer를 하나 혹은 그 이상을 skip & identity mapping 수행
    • * short connection에 layer 추가가 대신 identity short connection(layer의 입력을 그대로 출력부에 전송)을 사용하면, 추가적인 파라미터 수 혹은 computational complexity에 영향을 거의 주지 않음

    (1-6) 본문의 기여점
    • (1) Layer 수가 많아져도 최적화하기 쉬운 Deep Residual Learning Framework 제안 
    • (2) Layer 수가 많아져도 정확도 향상이 보증
    • (3) 특정한 데이터셋(ex : ImageNet) 뿐만 아니라 다양한 데이터셋(ex : CIFAR-10 등등)에서도 높은 성능을 보이며, 이는 범용적인 아이디어로 증명됨
    • (4) 기존 Network보다 Layer 수가 많아도, 파라미터 수는 적음 (=Lower Complexity)

    2. Related Work
    • * 해당 파트는 제가 전통 컴공생이 아닌 관계로... 깊숙하게 들어가지는 않고, 대략적인 흐름만 케치해보겠습니다!
    2-1. Residual Representations
    • 기존 image recognition에서 VLAD, Fisher Vector 개념이 등장
    VLAD // 출처 : 원문의 참고문헌 18
    • 이 개념을 적용하기 위해 편미분 방정식(PDE : Partial Differential Equations)을 풀어야함 (이미지는 2차원이니깐!)
    • 이 편미분 방정식을 풀기위해 residual 기법이 적용됐으며, 이는 기존의 풀이보다 최적화가 간단해짐

    2-2. Shortcut Connections
    • 기존의 gradient vanishing/exploding 문제를 해결하기 위해 몇몇 중간의 layer를 최종 layer(classifiers)에 연결함으로써 성능 향상을 시도
    • 본문과 비슷한 연구 주제로 "Highway networks // 참고문헌 42, 43"이 있으나, layer 깊이에 따른 성능 향상에 대한 지표를 제시하지 않음

    3. Residual Learning
    • 이 절의 내용은 (1-3), (1-4) 내용과 유사합니다.
    • 내용 요약 : 단순하게 Layer를 쌓는 형태는 identity mapping에 적합하지 않으므로(=Degradation 문제 발생), Residual Mapping을 통해 Degradation 문제를 해결할 것이다! 

    3-2. Identity Mapping by shortcuts
    • Residual Block을 다음과 같이 정의
    Residual Block 도식도
    Residual Block 수식
    Residual Block 수식 변형
    • x, y : Layer의 input, output
    • F(x, {W_i}) : Residual Mapping
    • W_1, W_2 : 첫, 두번째 layer의 가중치(=모델 파라미터)
    • σ : ReLU (Activation Function)
    • * Bias(편향)은 수식의 간략화를 위해 생략

    3-2.-(2) Shortcut Connection
    • F + x : shortcut connection에 의해 구현 가능
    • F와 x의 차원이 다르므로, 차원을 맞춰줘서 element-wise addition(단순한 행렬 합)을 진행해야 함
    • 본문에서는 1*1 conv layer를 활하여 Layer의 중간 output인 F와 차원을 맞춰줌
    W_s : 1*1 conv의 weight
    • 번외 : Residual Block의 Layer 수를 2개, 3개 혹은 그 이상으로 해도 상관 없으나, Layer 수를 1개만 했을 때는 효과가 거의 없었음
    번외에 대한 논문의 내용 // 출처 : 원문
    3-3. Network Architectures
    네트워크 아키텍쳐 예시 (좌 : VGG19, 중 : 34-layer Plaint, 우 : 34-layer Residual) // 출처 : 원문(1) Plain Network
    • VGG-19 아키텍쳐에 영감을 받음 & 3*3 conv filter 사용
    • VGG와의 차이점 1 : Max Pooling 대신 Conv layer에서 Stride 2를 사용하여 Feature Map의 size를 절반으로 감소
    • VGG와의 차이점 2 : Feature Map의 size가 절반으로 감소하면, 필터의 수는 2배로 증가시켜서, 레이어마다의 time complexity를 보존
    • VGG와의 차이점 3 : 위의 테크닉을 적용하여, VGG보다 레이어 수는 15개 더 많지만, 파라미터 수는 VGG19의 18%에 해당

    (2) Residual Network
    • Plain Network에 Shortcut Connection을 
    identity shortcuts을 적용한 수식
    • Identity Shortcuts은 input과 output이 동일한 차원일때 사용 가능
    • 차원의 증가하면, 2가지 옵션을 선택할 수 있음
    • (옵션 1) : 차원 증가를 위해 zero padding 적용 -> identity shortcuts 효과
    • (옵션 2) : 차원 증가를 위해 projection shortcut 적용 -> 1*1 conv를 활용

    3-4. Implementation
    • 데이터 증강을 위해 학습 데이터는 256~480 pixel 중 랜덤하게 resize된 후, 224*224 crop 적용
    • 혹은 horiziontal flip 적용
    horiziontal flip

    • 데이터 전처리를 위해 pixel mean subtracted 적용
    • 각 Conv layer와 activation function 사이에 Batch Normalization 적용
    • mini-batch 256으로 SGD 최적화 사요
    • 학습률은 0.1로 시작하다가, error가 감소되지 않으면 10으로 나눔
    • 학습 횟수 : 60*10^4 iter
    • weight decay : 0.0001
    • momentum : 0.9
    • No Dropout
    <테스트>
    • 테스트 방법 : standard 10-crop testing 적용
    10 crop test

    • multiple scales(224, 256, 384, 480, 640)에서 평균 점수를 사용

    4. Experiments(1) ImageNet Dataset
    • ResNet을 ImageNet 2012 classification dataset에서 top-1, top-5 error rate에 대해 평가(evaluate) 진행
    • * top-1, top-5 error에 대한 깔끔하게 정리된 설명이 얼마 없어서... accuracy로 대체합니다~
    top-1, top-5 accuracy의 정의

    • class 개수 : 1,000개 
    • 학습 데이터 수 : 128만개 (1.28 million)
    • 검증 데이터 수 : 5만개 (50k)
    • 테스트 데이터 수 : 10만개 (100k)

    (2) Architectures for ImageNet
    표 1. Architectures for ImageNet
    • ResNet 아키텍쳐 : conv 3_1, conv 4_1, conv 5_1에서 다운 샘플링 (with stride 2)
    그림 5. Basic Block(좌) VS Bottleneck Block(우)

    • Basic Block (그림 5의 왼쪽) : 한 블록에 stacked된 layer 2개 -> ResNet 18/34
    • Bottleneck Block (그림 5의 오른쪽) : 한 블록에 stacked된 layer 3개 -> ResNet 50/101/152

    (3-1) Plain Network for ImageNet
    그림 4. ImageNet 데이터에 대한 학습, 검증 에러 (Plain Net)

    표 2. ImageNet 검증 데이터에 대한 Top-1 error %

    • 표 2를 보면, PlainNet은 더 깊은 34-layer가 더 얕은 18-layer보다 더 큰 validation error를 가지고 있는 것을 보여줌
    • 그림 4에서도 plain-34가 plain-18보다 더 큰 train/val error를 가지고 있음을 보여줌
    • * 그림 4에서 굵은 선이 val error, 얇은 선이 train error
    • 결론 : plain에서는 degradation 문제가 관찰

    • 가설 1 : Gradient Vanishing에 의해 Degradation 발생
    • 가설 1에 대한 반박 1 : PlainNet은 Batch Normalization에 의해 학습되어, forward propagation은 0이 아닌 분산을 가짐
    • 가설 1에 대한 반박 2 : Backward Propagated Gradients는 BN과 함께 healty norms으로 보임
    • 결론 1 : forward 혹은 backward 신호(값)은 소멸(vanish)되지 않음!! -> Gradient Vanishing은 Degradation의 원인이 아님
    • 정확한 원인은 추후에 연구될 예정...
    • * Batch Normalization이란? : https://sacko.tistory.com/44
     

    문과생도 이해하는 딥러닝 (10) - 배치 정규화

    2017/09/27 - 문과생도 이해하는 딥러닝 (1) - 퍼셉트론 Perceptron 2017/10/18 - 문과생도 이해하는 딥러닝 (2) - 신경망 Neural Network 2017/10/25 - 문과생도 이해하는 딥러닝 (3) - 오차 역전파, 경사하강..

    sacko.tistory.com


    (4-1) Residual Networks for ImageNet
    그림 4. ImageNet 데이터에 대한 학습, 검증 에러 (ResNet)
    표 2. ImageNet 검증 데이터에 대한 Top-1 error %
    • 그림 4, 표 2에서 볼 수 있듯이, ResNet-34는 ResNet-18보다 성능이 향상
    • * Top-1 error 기준, 약 2.8% 향상
    • 이는 ResNet이 Degradation 문제를 해결했으며, Depth가 증가해도 정확도가 향상된다는 점을 보여줌

    • ResNet-34는 Plain-34보다 top-1 error가 약 3.5% 감소함 -> ResNet은 성공적으로 error를 줄일 수 있음
    • 반면, ResNet-18과 plain-18은 정확도가 비슷하지만,  그림 4를 비교하면 수렴 속도는 ResNet-18이 더 빠름
    • * 제 눈에는 둘다 비슷비슷 해보이는데... 논문에선 그렇다고 하니깐... 넘어가봅시다 ㅋㅋ
    • 즉, ResNet은 초기에 빠르게 수렴함으로써 최적화를 용이하게 함

    (5) Identity VS Projection Shortcuts
    표 3. ImageNet 검증 데이터에 대한 error 비율
    • Shortcut Connections에 3가지 옵션을 부여한 후, 성능을 비교
    • 옵션 A 
    • - 차원 증가를 위한 shorcut : zero-padding
    • - 그 외 : identity (parameter-free, 단순히 layer의 input을 더함)
    • 옵션 B 
    • - 차원 증가를 위한 shortcut : projection shortcut
    • - 그 외 : identity (parameter-free, 단순히 layer의 input을 더함)
    • 옵션 C :
    • - 모든 shortcut : projection shortcut
    projection shortcuts 예시 : 차원 수를 맞춰주기 위해 x와 내적되는 가중치 파라미터 존재<비교 1 : 옵션 A vs 옵션 B (표 3 기준)>
    • 옵션 B의 성능이 옵션 A보다 살짝 높음 (top-1 error 기준, 약 0.5%)
    • 이는 옵션 A의 zero-padding으로 추가된 차원이 실제로 residual learning이 없기 때문이라고 추측됨 (값이 0이기 때문)
    <비교 2 : 옵션 B vs 옵션 C (표 3 기준)>
    • 옵션 C의 성능이 옵션 B보다 살짝 높음 (top-1 error 기준, 약 0.3%)
    • 이는 projections shortcut에 의해 추가된 파라미터(W_s) 때문이라고 추측됨
    <표 3을 바탕으로 한 결론>
    • 옵션 A/B/C 간의 성능 차이가 작기 때문에, degradation 문제를 해결하기 위해 projection shortcuts이 필수적이 아니라는 것을 볼 수 있음
    • 따라서 본문의 후반에서는 memory/time complexity와 모델 사이즈를 줄이기 위해, 옵션 C를 사용하지 않을 예정

    (6) Deeper Bottleneck Architectures
    • Basic Block은 Layer가 50층 이상으로 깊어지면, 연산 효율이 좋지 않음
    • 연산 효율을 좋게 하기 위해, bottleneck block을 새로 디자인
    그림 5. Basic Block(좌) VS Bottleneck Block(우) // 출처 : 원문
    • Bottleneck Block은 3개의 Layer를 쌓음 -> 1*1, 3*3, 1*1 convolution으로 구성
    • Bottleneck Block은 Basic Block 보다 Layer 수가 1개 더 많지만, time complexity는 비슷
    • Bottleneck Block에는 옵션 B(차원 증가 shortcut : projection, 그 외 : identity)를 적용
  • (4-2) Plain Networks VS Residual Networks for ImageNet
  • (3-2) Plain Network에서 발생하는 Degradation의 원인 분석
  • 4-1. ImageNet Classification
  • <학습 관련 인자>
  • 3-2.-(1) Residual Blcok
  • 3-1. Residual Learning
  • * 1x1 conv이 연산 효율이 좋은 이유 : https://hwiyong.tistory.com/45
     

    1x1 convolution이란,

    GoogLeNet 즉, 구글에서 발표한 Inception 계통의 Network에서는 1x1 Convolution을 통해 유의미하게 연산량을 줄였습니다. 그리고 이후 Xception, Squeeze, Mobile 등 다양한 모델에서도 연산량 감소를 위해 이..

    hwiyong.tistory.com


    4-2. CIFAR-10 and Analysis
    • class 수 : 10개
    • 학습 데이터 수 : 5만개 (50k) -> 학습 45k, 검증 5k로 split
    • 검증 데이터 수 : 1만개 (10k)

    (2) ResNet Architecture For CIFAR-10
    • 네트워크 input : 32*32 pixel (pixel mean subtracted 전처리 적용)
    • * 이미지 크기가 작기 때문에, 첫번째 레이어는 7*7 -> 3*3 conv layer로 변경
    • 레이어 구성 : 6n + 2 
    • * 6n : 3*3 convolutions
    • * 2 : Global average pooling layer & 10-way Fully-Connected Layer
    ResNet architecture For CIFAR-10
    Global Average Pooling 설명 // 출처 : https://gaussian37.github.io/dl-concept-global_average_pooling/
    • weight decay : 0.0001
    • momentum : 0.9
    • weight initialization 적용
    • Batch Normalization 적용
    • Dropout 미적용
    • mini-batch size : 128
    • GPU 2개 사용
    • 학습률 : 0.1 -> 32k iter에서 0.01, 48k iter에서 0.001, 64k iter에서 학습 종료
    • Data 증강 : 학습 데이터의 각 사이드에 4픽셀 씩 패딩 후, 랜덤하게 32*32 crop

    그림 6. CIFAR-10에 대한 학습/검증 에러 결과 비교 (좌 : plain, 우 : ResNet)
    • n = 3, 5, 7, 9, 18을 적용하여 20, 32, 44, 56, 110 layer를 생성
    • * ex) n=3일 때, 6*n + 2 = 6*3 + 2 = 20 layer
    • CIFAR-10 데이터에서도, plainNet은 layer 수가 증가함에 따라 error가 증가하는 것을 볼 수 있음
    • * 점선 : train error, 굵은 실선 : test error
    • * Plain-110 layer는 error가 60% 이상이여서, 비교 대상에서 제거
    • 반면 ResNet은 Layer 수가 110개까지 증가해도, error가 감소하는 것을 볼 수 있음
    •  ResNet은 데이터셋 종류와 관계없이, 범용적인 알고리즘이라는 것을 증명

    (3) Analysis of Layer Responses
    그림 7. CIFAR-10에 대한 layer responses의 표준 편차(std)
    • 그림 7 layer responses의 표준 편차(std)를 보여줌 (after Batch Normalization, before activation function)
    • * 표준 편차의 정의 : 분산의 제곱근 -> 표준 편차가 작을 수록 평균 값에서 데이터(변량)들의 거리가 가까움
    • 결과 분석 1 : ResNet은 PlainNet보다 전반적으로 std가 작음
    • 결과 분석 2 : 깊은 ResNet(56, 110)이 얕은 ResNet(20)보다 std가 작음
    • 이러한 관찰로 봤을 때, ResNet은 PlainNet보다 최적화가 용이하고, Depth가 깊어져도 성능 향상이 보증

    (4) Exploring Over 1000 Layers
    그림 6. CIFAR-10에 대한 학습/검증 에러 결과 비교
    • n=200으로 하면, 1202-layer가 생성됨
    • * 위 그림 6 역시, 점선이 train error, 굵은 실선이 test error
    • ResNet 110과 1202를 비교했을 때, train error은 거의 유사
    • 그러나 test error는 ResNet 110이 더 낮음
    • ResNet 1202가 ResNet 110 보다 성능이 안좋은 이유는 overfitting이 원인이라고 추측됨
    • 이러한 overfitting은 DropOut등을 통해 해결할 수 있음
    DropOut 


    4-3. Object Detection on PASCAL and MS COCO
    • Object Detection에서 Backbone으로 ResNet을 사용하면, 성능 향상 가능
  • <결과 분석 1 : Depth에 따른 error 비교>
  • <그 외 학습 인자>
  • (1) CIFAR-10 Dataset

'논문' 카테고리의 다른 글

YOLOX 논문리뷰  (0) 2022.05.09
Pose Estimation - HRNet 논문리뷰  (0) 2022.05.02

2-1. 퍼셉트론(Perceptron)


퍼셉트론(perceptron)은 프랑크 로젠블라트(Fank Rosenblatt)가 1957년에 고안안 알고리즘이다. 이 퍼셉트론

퍼셉트론(perceptron)은 프랑크 로젠블라트(Fank Rosenblatt)가 1957년에 고안한 알고리즘이다. 이 퍼셉트론이 바로 신경망(딥러닝)의 기원이 되는 알고리즘이다.

퍼셉트론은 다수의 신호(흐름이 있는)를 입력으로 받아 하나의 신호를 출력한다. 퍼셉트론은 이 신호를 입력으로 받아 '흐른다/안 흐른다'(1 또는 0)이라는 정보를 앞으로 전달한다.

 
그림 1. 입력이 2개인 퍼셉트론
 

위의 그림에서,

  • x_1​과 x_2 ​는 입력 신호, y​는 출력 신호, w_1​과 w_2​는 가중치(weight)를 의미한다.
  • 원을 뉴런 또는 노드라고 부른다.
  • 입력 신호가 뉴런에 보내질 때는 각각 고유한 가중치가 곱해진다(w_1x_1,w_2,x_2​).
  • 뉴런에서 전달 받은 신호의 총합이 임계값 θ를 넘을 때만 1을 출력

 

2-2. 단순한 논리 회로


A. AND 게이트

 

그림 2. AND게이트 진리표
그림 3. AND 단층 퍼셉트론 시각화

위의 표는 AND게이트의 진리표이며 이 AND게이트를 퍼셉트론으로 표현해보자. 이를 위해서는 ​ w_1, w_2, θ 의 값을 적절하게 정해야 한다.

예를 들어 , (w_1, w_2, θ) = (0.5, 0.5, 0.7) 일 때, AND 게이트의 조건을 만족한다.

 

B. NAND 게이트와 OR 게이트

NAND 게이트는 Not AND를 의미하며 AND 게이트의 출력을 반대로한 것과 같다.

(w_1, w_2, θ) = (-0.5, -0.5, -0.7)

그림 4. NAND게이트 진리표
그림 5. OR게이트 NAND게이트 퍼셉트론 시각화

 

OR 게이트는 입력 신호 중 하나 이상이 1이면 출력이 1이 되는 논리 회로다.

그림 6. OR게이트 진리표

 

C. XOR 게이트

XOR 게이트는 베타적 논리합 이라는 논리 회로다.

 
그림 7. XOR게이트 진리표

그림 8. XOR게이트 퍼셉트론 시각화
그림 9. XOR게이트 논리 회로도
그림 10. 논리 회로도의 진리표

그림 8에서 보이는 것과 같이 기존의 게이트에서 보이지 않던 비선형적인 성질이 보인다. 따라서 하나의 게이트로 구현할 수 없어서 기존의 게이트를 조합하는 형식으로 구현하였다.

 

'vison_study' 카테고리의 다른 글

Chap3. 신경망 (밑바닥부터 딥러닝)  (0) 2021.12.27
Torch tensor to numpy(in use Opencv)  (0) 2021.06.28
Vison Study #2  (0) 2021.06.27
Vison_study #1  (0) 2021.06.27

요즘 최근 vision 모델에서 사용하는 Transformer 모델을 공부하기전에 transformer를 공부하기위해 정리한다. ( 물론 자연어쪽도 나름 공부하기 위해서 정리를 시작하려고 한다 내가 보기 위함)

 

1. Transformer

트랜스포머(Transformer)는 2017년 구글이 발표한 논문인 "Attention is all you need"에서 나온 모델로 기존의 seq2seq의 구조인 인코더-디코더를 따르면서도, 논문의 이름처럼 어텐션(Attention)만으로 구현한 모델입니다. 이 모델은 RNN을 사용하지 않고, 인코더-디코더 구조를 설계하였음에도 성능도 RNN보다 우수하다는 특징을 갖고있습니다.

 

2. 트랜스포머(Transformer)의 주요 하이퍼파라미터

시작에 앞서 트랜스포머의 하이퍼파라미터를 정의합니다. 각 하이퍼파라미터의 의미에 대해서는 뒤에서 설명하기로하고, 여기서는 트랜스포머에는 이러한 하이퍼파라미터가 존재한다는 정도로만 이해해보겠습니다. 아래에서 정의하는 수치는 트랜스포머를 제안한 논문에서 사용한 수치로 하이퍼파라미터는 사용자가 모델 설계시 임의로 변경할 수 있는 값들입니다.

dmodel = 512
트랜스포머의 인코더와 디코더에서의 정해진 입력과 출력의 크기를 의미합니다. 임베딩 벡터의 차원 또한 dmodel이며, 각 인코더와 디코더가 다음 층의 인코더와 디코더로 값을 보낼 때에도 이 차원을 유지합니다. 논문에서는 512입니다.

num_layers = 6
트랜스포머에서 하나의 인코더와 디코더를 층으로 생각하였을 때, 트랜스포머 모델에서 인코더와 디코더가 총 몇 층으로 구성되었는지를 의미합니다. 논문에서는 인코더와 디코더를 각각 총 6개 쌓았습니다.

num_heads = 8
트랜스포머에서는 어텐션을 사용할 때, 1번 하는 것 보다 여러 개로 분할해서 병렬로 어텐션을 수행하고 결과값을 다시 하나로 합치는 방식을 택했습니다. 이때 이 병렬의 개수를 의미합니다.

dff = 2048
트랜스포머 내부에는 피드 포워드 신경망이 존재하며 해당 신경망의 은닉층의 크기를 의미합니다. 피드 포워드 신경망의 입력층과 출력층의 크기는 dmodel입니다.

3. 트랜스포머(Transformer)

트랜스포머는 RNN을 사용하지 않지만 기존의 seq2seq처럼 인코더에서 입력 시퀀스를 입력받고, 디코더에서 출력 시퀀스를 출력하는 인코더-디코더 구조를 유지하고 있습니다. 다만 다른 점은 인코더와 디코더라는 단위가 N개가 존재할 수 있다는 점입니다.

이전 seq2seq 구조에서는 인코더와 디코더에서 각각 하나의 RNN이 t개의 시점(time-step)을 가지는 구조였다면 이번에는 인코더와 디코더라는 단위가 N개로 구성되는 구조입니다. 트랜스포머를 제안한 논문에서는 인코더와 디코더의 개수를 각각 6개를 사용하였습니다.

위의 그림은 인코더와 디코더가 6개씩 존재하는 트랜스포머의 구조를 보여줍니다. 이 책에서는 인코더와 디코더가 각각 여러 개 쌓여있다는 의미를 사용할 때는 알파벳 s를 뒤에 붙여 encoders, decoders라고 표현하겠습니다.

위의 그림은 인코더로부터 정보를 전달받아 디코더가 출력 결과를 만들어내는 트랜스포머 구조를 보여줍니다. 디코더는 마치 기존의 seq2seq 구조처럼 시작 심볼 <sos>를 입력으로 받아 종료 심볼 <eos>가 나올 때까지 연산을 진행합니다. 이는 RNN은 사용되지 않지만 여전히 인코더-디코더의 구조는 유지되고 있음을 보여줍니다.

트랜스포머의 내부 구조를 조금씩 확대해가는 방식으로 트랜스포머를 이해해봅시다. 우선 인코더와 디코더의 구조를 이해하기 전에 트랜스포머의 입력에 대해서 이해해보겠습니다. 트랜스포머의 인코더와 디코더는 단순히 각 단어의 임베딩 벡터들을 입력받는 것이 아니라 임베딩 벡터에서 조정된 값을 입력받는데 이에 대해서 알아보기 위해 입력 부분을 확대해보겠습니다.

 

4. Transformer Architecture

우선 논문에서 제시한 transformer의 기본 architecture는 아래와 같습니다.

위 구조가 바로 Transformer 모델의 구조입니다. Encoder-Decoder 구조를 갖는다는 것 외에는 모르는 단어가 상당히 많은데, Self-Attention, Multi-head Attention, Positional Encoding 등 Transformer 모델의 구성요소와 동작원리에 대해서 알아보도록 하죠.

 

5. Self-Attention

먼저 Dot-product attention에 대해서 알아보자.

각 value가 key에 매칭 되어 있다고 하자. Python의 Dictionary를 떠올려도 좋습니다.
※ dict{key1 : {value1}, key2 : {value2}, ..., keyt : {value_t}}로 구성된 dictionary가 있다고 생각하자구요.

어떤 query가 들어오면 dot-product attention은 value의 convex sum을 출력한다.

이 때 weight들은 query와 key의 dot-product를 0~1 사이 값으로 normalize한 값과 같다.
즉, query와 key가 비슷하면 weight가 높게, 유사성이 낮으면 weight는 낮으며, 모든 weight의 합은 1이 된다.

 

전혀 새로운 개념이 아니다. Attention 포스팅에서다룬 내용도 s를 query로 가지고 key가 h1, ..., ht였던 dot-product attention과 동일하다. 그럼 self-attention은 어떤 함수일까?

 

바로 key query, value 동일한 집합에 있는 것이다. 예를 들면 s_t와 s_1, ..., s_t간의 각각의 유사성을 convex sum형태로 표현한 것이다. 위 과정을 그림으로 나타내면 아래와 같다.

[출처 : Peter Bloem - Transformer]

위 예제에서 w_ij의 의미는 key x_i와 query x_j의 weight를 의미한다.
즉, y2는 x1-x2, x2-x2, x3-x2, x4-x2를 비교해 각 value들을 convex sum한 결과가 된다.

그런데 이 대로는 사실 쓸모가 없다. 왜냐하면 w_ij는 sequence(x1, ..., x4)만 있으면 자동으로 계산되는 상수나 마찬가지기 때문에다. 그래서 '학습'을 위해서 학습될 수 있는 parameter가 필요하다. (parameterize)

 

그래서 query, key, value에 각각 파라미터 Wq, Wk, Wv를 적용하면 위와 같이 표현할 수 있다. 이렇게 하면 학습을 통해 적절한 파라미터를 찾아서 자기 자신의 self-attention 을 학습할 수 있게 된다. 

이렇게 했을 때 어떤 효과가 있을까? 

위 예시는 'The', 'animal' ... 'tired' 스스로 self attention 한 결과이다. 예를 들면 'it'은 어떤 단어와 유사성이 가장 높았는지 self attention을 계산한 결과 it은 animal과 가장 높은 유사성을 가진 것을 알 수 있는데, 이런 효과도 대표적인 self-attention의 효과다.

6. Scaled Dot-Product Attention

Scaled Dot-Product Attention은 앞에서 배운 Dot-product attention과 거의 동일하다.

 

Dot-Product Attention은 기존 방법에 Key의 차원 D_k를 나눠서 scaling한 것이다.
(self attention이라면 Q, K, V의 차원이 모두 같다.)

※ 논문에서는 통상 Key의 차원이 매우 높기 때문에 (예를 들어 책 한권에 나오는 모든 단어가 key가 된다.) scaling을 하지 않으면 weight vector가 잘 계산되지 않는다고 한다.

지금까지 배운 것을 한번 시뮬레이션 해보자.

 

I, am, a, student라는 단어가 주어졌을 때 각각을 어떤 숫자로 vectorized 시켰다고 하자.

이 vector를 Wq, Wk, Wv로 parameterize시키면 I, am, a, student 각각의 Q, K, V 벡터를 얻을 수 있다.

 

그리고 'I'의 query를 각 단어에 scaled dot product self-attention을 해보자.

'I'의 query와 'I', 'am', 'a', 'student'의 key의 dot-product를 구하고 scaling 해 주면 각 단어들간의 유사도(Attention score)가 계산된다.

 

이 attention score에 softmax를 취해 합이 1인 값으로 바꿔보니 'I'는 'I'와 'student'간 유사도 높은 것을 확인할 수 있다. 이 값들을 모두 더하면 Attention value (혹은 Context vector)가 최종적으로 도출되게 된다.

7. Multi-Head Attention

앞서 Q, K, V를 Linear transform으로 parameterized 시켜 학습할 수 있게 만들었었다. 그런데 1개씩의 파라미터로 모델이 충분한 복잡성을 못 가질 수도 있기 때문에 여러번 Linear transform 시켜줄 수 있다. 이 개념이 Multi-head attention이다.
※ CNN에서 1개 이미지를 1개 필터로 Conv.하는 것이 아니라 여러개의 필터를 사용해서 많은 feature map을 뽑는 것과 동일하다.

Multi-head attention은 위에서 언급한 것 처럼 학습 파라미터 수를 늘려 모델의 복잡도를 올리는 방법이고 num_head는 하이퍼파라미터가 된다. (논문에서는 num_head = 8로 설정했다.)

 

Multi-head attention을 앞서 보여줬던 예제에 적용하면 위 그림과 같다. 동일한 과정을 num_head만큼 수행하는 것을 활인할 수 있다.

Encoder 그림의 가장 핵심인 Multi-head self-attention을 이해했다면 인코더의 80%는 이해한 것이다. Position Encoding이나 Position-wise Feedfoward net은 encoder, decoder 모두에서 사용되는 기법이니 곧 설명할 예정이다.

 

다시 한번 전체 모습을 살펴보면 Transformer는 3개의 attention을 가지고 있다.

  • Encoder self-attention : Encoder에 입력된 단어들의 self-attention
  • Decoder self-attention : Decoder에서 예측한 단어들의 self-attention
  • Encoder-Decoder attention :
    당연히 [10-2. Attention] 과 같이 입력 문장과 예측 문장간의 Attention이 필요하다.
    이 때 Q는 decoder에서, K, V는 encoder의 output에서 온다.

우리는 위 3가지 중 1, 2번째인 self-attention의 원리에 대해서 알게된 것이다.
그리고 Encoder-Decoder attention은 [10-2. Attention]과 동일하다.

그럼 이제 Encoder, Decoder에서 공통적으로 가지고 있는 Position-wise FFNN과 Positional Encoding에 대해서만 알면 필요한 기술은 모두 배운것이다. 

 

8. Position-wise Feedforward Networks

Transformer 모델은 Encoder-Decoder로 나눠져 있긴 하지만, Encoder, Decoder자체가 마치 hidden layer 처럼 여러층을 쌓을 수 있다. 이 때 Encoder layer를 Encoder #1, #2 이렇게 표현해 보자.

 

 

그리고 각 인코더는 위 그림과 같이 Fully Connected layer들로 이어져 있다.

위 식에서 x는 multi-head self-attention에서 나온 matrix이다.

즉, transformer model의 FFN은 1개 은닉층을 가지는 neural net이며, 논문에서는 hidden layer의 크기를 2048로 설정했다.

 

이 때 모델의 복잡도가 너무 올라가는 것을 고려한 skip connection과 layer normalization이 적용된다. 

  • skip connection은 CNN 포스트의 ResNet과 정확히 같은 개념이며,
  • layer normalization역시 CNN 포스트의 Layer normalization과 동일한 개념이기 때문에 자세한 설명은 생략하도록 한다.

9. Positional Encoding

앞의 과정을 상기해보면, Transformer 모델은 input sequence의 단어를 마치 python의 dictionary형태로 저장해놓고 self attention을 수행하는 것을 알 수 있다. 그런데 여기서 문제점은 각 단어의 벡터가 key로 등록은 되는데 몇 번째 단어인지는 알 수가 없다.
예를 들어 위 그림에서 'am'을 key값으로 찾을 순 있지만 2번째 있는 단어라는 것은 알 수가 없는 것이다.

그래서 Transformation model에서는 Positional Encoding 값을 embedding(vectorized word)에 더해줌으로써 위치 정보를 만들어 준다. 이 과정을 시각화 하면 아래와 같다.

 

위 그림을 보면 오른쪽에 있는 단어일 수록 positional encoding 값도 오른쪽이 큰 것을 알 수 있다. 어떻게 해야 이런 값을 만들 수 있을까?

위 식은 단어위 위치 pos와 각 단어의 벡터의 index i가 주어졌을 때 Positional Encoding(PE)값을 구하는 방법이다. 식으로는 이해가 어려우니 예제를 한번 살펴보자.

 

위 예제는 각 단어를 4차원으로 embedding 한 것을 가정한 것이다. (논문에는 128차원으로 embedding 했다.) 그리고 각 단어 vector의 i번째 차원값은 sin과 cos의 변환하면서 계산되고, 계산 결과 위 그림처럼 출력된다. 이게 어떤 의미를 가질까? PE의 단순한 예를 하나 더 살펴보자.

 

만약 우리가 10진수를 2진수로 표현한다고 가정해 보자. 2진수의 자릿수를 10개 (=차원)으로 나타낸다고 하면 위 그림과 같이 변환할 수 있다.

i=1의 값은 1, 0, 1, 0 순으로 빠르게 변하고, i=2의 값은 1, 1, 0, 0, 1, 1 과 같이 조금 늦게, i=3인 값은 1, 1, 1, 1, 0, , ... 더 늦게 변화하는 것을 확인할 수 있다. 

즉, word를 이런 방식으로 표현하면 만약 같은 단어가 2번 나왔더라도 어디쯤 위치한 word인지 알 수가 있다.  

 

위 그림은 126차원으로 embedding된 50개의 단어을 Positional Encoding 시킨 결과이다.

우리는 각 단어의 PE된 vector가 어떤 값을 갖는지는 알 필요가 없다. 하지만 컴퓨터에게는 이 vector로 하여금 각 단어의 위치정보를 알 수 있게 해준다. (이 위치정보를 알게 해주는것이 핵심!!)

 

지금까지 각 구성요소에 대해서 각각 살펴봤는데 이제 전체그림으로 조립해보자.

  • vectorized 된 단어들이 parameter를 가진 embedding으로 입력된다.
  • embedding된 단어가 위치 정보를 담을 수 있도록 Positional Encoding 된다.
  • 각 단어의 vector가 Self-Attention + FFNN 되는데, 일종의 "입력된 문장을 이해하는 과정"으로 볼 수 있다. 그리고 인코더는 num_layers만큼 쌓을 수 있다.
    ※ CNN으로 치면 Conv, Pooling을 반복하는 과정이다.
  • Encoder에서 출력된 Context vector를 통해 Decoder에서 단어를 예측하는데, 이 때는 Self-Attention이 아니라 Encoder-Decoder Attention을 수행한다.
  • 그리고 Decoder에서 예측된 단어들은 매번 Self-Attention을 통해, 일종의 "예측된 문장을 이해하는 과정"을 반복한다.

 

출처 : https://wikidocs.net/31379

 

1) 트랜스포머(Transformer)

* 이번 챕터는 앞서 설명한 어텐션 메커니즘 챕터에 대한 사전 이해가 필요합니다. 트랜스포머(Transformer)는 2017년 구글이 발표한 논문인

wikidocs.net

 

https://sonsnotation.blogspot.com/2020/11/10-3-transformer-model.html

 

[머신러닝/딥러닝] 10-3. Transformer Model

 

sonsnotation.blogspot.com

https://mangastorytelling.tistory.com/entry/%EB%94%A5%EB%9F%AC%EB%8B%9D%EC%9D%84%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9E%90%EC%97%B0%EC%96%B4-%EC%B2%98%EB%A6%AC-%EC%9E%85%EB%AC%B8-1701-%ED%8A%B8%EB%9E%9C%EC%8A%A4%ED%8F%AC%EB%A8%B8Transformer

 

[딥러닝을이용한 자연어 처리 입문] 1701 트랜스포머(Transformer)

- 2017년 구글이 발표한 논문인 "Attention is all you need"에서 나온 모델로 기존의 seq2seq의 구조인 인코더-디코더를 따르면서도, 논문의 이름처럼 어텐션(Attention)만으로 구현한 모델 - 이 모델은 RNN을..

mangastorytelling.tistory.com

 

https://blog.naver.com/PostView.nhn?blogId=winddori2002&logNo=222008003445&parentCategoryNo=1&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView 

 

[바람돌이/딥러닝] Transformer 이론 및 개념(Attention is all you need 리뷰)

안녕하세요. 오늘은 transformer에 대해 정리하겠습니다. 해당 내용은 'Attention is all you need&#...

blog.naver.com

https://engineering-ladder.tistory.com/73

 

Attention & Transformer의 개념

 이번 글에서는 시계열 분석에서 State-of-Art라고 할 수 있는, Transforemer에 대해 정리해보고자 한다. 일반적으로 시계열 분석이라 하면, Recurrent Neural Network (RNN)이나 Long-Short Term Memory (LSTM)..

engineering-ladder.tistory.com

https://123okk2.tistory.com/164

 

자연어 처리 : 트랜스포머

출처 : 동빈나 유튜브 www.youtube.com/watch?v=AA621UofTUA&t=44s *해당 글은 학습을 목적으로 위의 포스팅 내용 중 일부 내용만을 요약하여 작성한 포스팅입니다.  상세한 내용 및 전체 내용 확인은 위의

123okk2.tistory.com

 

'nlp_study' 카테고리의 다른 글

딥러닝 어텐션 (Attention)  (0) 2022.01.12

딥러닝에서 어떤 모델을 사용하든 모델을 분석하는 과정을 train code를 사용하거나 test code를 사용하는 와중에 값을 확인할 때, 일부 vison 모델에서는 결과값을 opencv로 확인하고 싶을 때의 과정을 정리했다.

 

여기서 사용한 모델은 Yolact모델이다. (Yolact 모델 정리는 추후 backbone정리가 마무리가 되면 진행할 예정.. fpn과정의 연산이 모듈형태로 foward연산이 자동으로 실행돼서 연산과정을 확인할 수 없어서 시간이 조금 걸릴것으로 예상됨)

 

여기서는 mask가 씌워지는 결과는 각각 확인하고 싶었다.

 

 

뭔가 마스크라는 변수에 15개(object)의 텐서가 들어가 있다 현재 이 연산은 Tensor형태로 gpu에서 연산중이니 opencv에서 확인하기 위해서는 변환하는 과정이 있어야 한다.
gpu -> cpu(메모리에 값을 복사) -> byte() float형태의 값을 integer값으로 변환해줘야 opencv에서 사용가능 -> numpy (tensor값을 numpy로 변환해야 opencv에서 확인가능)

 

# 실제 코드
(masks[0]* 255).byte().cpu().numpy()
cv2.imshow("test1",(masks[0]* 255).byte().cpu().numpy())
cv2.waitKey(0)

 

  • 변환결과
  •  
  • 변과결과 확인
  •  

마스킹 형태가 잘 나온걸 확인할 수 있다.

 

p.s
train이나 test중간의 값을 확인하기 하다가 보면 tensor값의 경우 torch.Size([4, 3, 32, 32])
이런식으로 channel값이 바뀌어 있는 경우가 있는데 이 경우에는 opencv 형식으로 변환해줘야 한다.

# torch.Size([4, 3, 32, 32]) 일 때 train_t = np.transpose(train, (0, 2, 3, 1)) train_t.shape

 

출력
torch.Size([4, 32, 32, 3])
​

'vison_study' 카테고리의 다른 글

Chap3. 신경망 (밑바닥부터 딥러닝)  (0) 2021.12.27
2.1 퍼셉트론 (밑바닥부터 딥러닝)  (0) 2021.12.22
Vison Study #2  (0) 2021.06.27
Vison_study #1  (0) 2021.06.27

github 주소 : https://github.com/ceccocats/tkDNN

 

ceccocats/tkDNN

Deep neural network library and toolkit to do high performace inference on NVIDIA jetson platforms - ceccocats/tkDNN

github.com

 

Yolov4-tiny 모델을 tensorRT로 전환해서 사용하려고 하니 본래 사용하던 github에서는 지원이 안돼서 다른 github에서 사용하는 tkDNN모델로 사용하게 됨.

버전과는 상관없이 위의 종속성에 해당하는 종류는 모두 있어야 함

환경 변수

  • CUDA 11.1
  • CUDNN 8.0.1
  • TENSORRT 7.2.2.2
  • OPENCV 3.4.5
  • yaml-cpp 0.5.5 ( sudo apt install libyaml-cpp-dev)

빌드과정

 

$ mkdir build
$ cd build
$ cmake ..
[ 여기서 만약에 에러가 발생한다면 ]
$ vi CMakeLists.txt ( path tkDNN/CMakeLists.txt )
맨위에 추가

set(CUDA_nvinfer_LIBRARY "/usr/local/cuda-11.1/TensorRT-7.2.3.4/lib/libnvinfer.so")
set(NVINFER_INCLUDES "/usr/local/cuda-11.1/TensorRT-7.2.3.4/include")
set(CUDA_cublas_device_LIBRARY "/usr/local/cuda-11.1/lib64/libcublas.so")​

esc + wq! (vi 종료)
다시 진행

  • $ cmake ..
  • $ make -j 8 ( 8은 cpu thread 갯수 )
  • 빌드 완료

 

Custom dataset으로 trt변환후 사용하기
{tkDNN}/tests/darknet/yolov4tiny.cpp (사용하고자 하는 backbone의 {model}.cpp에서

std::string bin_path = "/home/john/PycharmProjects/tkDNN_trt/darknet";
std::string cfg_path = "/home/john/PycharmProjects/tkDNN_trt/tests/darknet/cfg/yolo4tiny_fire.cfg";
std::string name_path = "/home/john/PycharmProjects/tkDNN_trt/fire.names";
std::string wgs_path = bin_path + "/layers";
이 부분이 맞는지 확인해야 함!

 

또 {tkDNN}/tests/darknet/cfg/{need_model}.cfg ⇒ 학습 때 사용한 cfg를 복붙해도 됨
  • 이 이후에 다시 빌드하는 과정을 거치면 된다.
  • ./test_{need_model} ⇒ 실행하면 trt가 튀어나옴 (에러시에는 검색하거나 디버그 모드로 다시 실행)
  • ./demo darknet_fp32.rt ../demo/yolo_test.mp4 y

'딥러닝모델' 카테고리의 다른 글

Multi object Tracking Study - FairMOT-2  (0) 2021.06.27
Multi object Tracking Study - FairMOT  (0) 2021.06.27
Yolov4-tiny darknet build train test  (0) 2021.06.27
Yolov5 Train 및 TensorRT  (0) 2021.06.27

gz압축하기


명령어

 

$ gzip {압축 파일명}

 

파일 압축하기

abc.png를 gz으로 압축한다면 아래와 같은 명령어를 사용하면 된다.

$ gzip abc.png

수행 결과로 abc.png는 없어지고, abc.gz 압축 파일이 생성된다.

gz은 여러개의 파일을 하나로 압축하는 용도가 아니다.
여러개의 파일을 압축하시려면 tar, zip, 7z 압축을 사용하는걸 추천드린다.

gz 압축 풀기


명령어

$ gzip -d {압축 파일명}.gz

gzip으로 압축을 푸는 옵션으로 -d 를 주면 된다. -d는 decompress의 줄임 표현이다.

gz 설치하기


gzip은 리눅스에 기본으로 설치되어 있으나, 혹시 gzip 명령어를 찾을 수 없다고 나오면 아래 명령어로 설치하면 된다.

우분투에서 unzip 설치

$ apt-get install gzip

 

포인터 소개 (Introduction to pointer)
변수는 값을 보유하고 있는 메모리 조각의 이름이라는 것을 배웠다. 프로그램이 변수를 인스턴스화 할때 사용 가능한 메모리 주소가 변수에 자동으로 할당되고, 변수에 할당된 값은 이 메모리 주소에 저장된다.

 

int x;
CPU가 위 문장을 실행하면 RAM의 메모리 조각이 따로 설정된다. 예를 들어 변수 x에 메모리 위치 140이 할당되었다고 가정해보자. 프로그램에서 변수 x를 표현식 또는 명령문으로 접근할 때마다 값을 얻으려면 메모리 위치 140을 찾아야 한다.
변수의 좋은 점은 우리가 어떤 특정한 메모리 주소가 할당되는지 걱정할 필요가 없다는 것이다. 지정된 식별자로 변수를 참조하면 컴파일러에서 이 이름을 할당된 메모리 주소로 변환한다.
하지만 이 접근법에는 몇 가지 제한 사항이 있으며, 이에 대해서는 앞으로 배울 내용에서 살펴보겠다.

주소 연산자 (&) (The address-of operator (&))

주소 연산자 &를 사용하면 변수에 할당된 메모리 주소를 확인할 수 있다.

 

#include <iostream> 
int main()
{ 
	int x = 5; 
    std::cout << x << '\n'; // print the value of variable x 
    std::cout << &x << '\n'; // print the memory address of variable x 
    return 0; 
} 
// prints: 
// 5 
// 0027FEA0

 

역참조 연산자 (*) (The dereference operator (*))

변수의 주소를 얻는 것 자체로는 그다지 유용하지 않다.

역참조 연산자(*)를 사용하면 특정 주소에서 값에 접근할 수 있다.

 

#include <iostream> 
int main() 
{ 
    int x = 5; 
    std::cout << x << '\n'; // print the value of variable x 
    std::cout << &x << '\n'; // print the memory address of variable x 
    std::cout << *&x << '\n'; /// print the value at the memory address of variable x 
    return 0; 
 } 
 // prints: 
 // 5 
 // 0027FEA0 
 // 5

 

포인터 (Pointer)

주소 연산자(&)와 역참조 연산자(*)와 함께 이제 포인터에 관해 이야기할 수 있다.

포인터는 어떠한 값을 저장하는 게 아닌 메모리 주소를 저장하는 변수다.

포인터(pointer)는 C++ 언어에서 가장 혼란스러운 부분 중 하나로 여겨지지만, 알고 보면 놀랍게도 간단한다.



간단하게 포인터에 대해서도 알아 보았으니 밑으로는 함수형과 구조체에서 간단하게 포인터를 사용하는 예제를 알아보자.

#include <iostream>
#include <cstddef>
void doSomething(double *ptr) // 메모리의 값이 복사됨
{
std::cout << "address of pointer varaibable in doSomething()" << &ptr << std::endl;

if (ptr != nullptr) {

// do something useful
std::cout << *ptr << std::endl;
}
else {
// do nothing with ptr 
std::cout << "NULL ptr, do nothing" << std::endl;
}
}

int main()
{

double *ptr{ nullptr }; // modern c++ memoery1

doSomething(ptr);
doSomething(nullptr);

double d = 123.4;

doSomething(&d);

ptr = &d;

doSomething(ptr);

std::nullptr_t nptr; 
// null pointer만 넣을 수 있음 혹시나 null pointer만 받아야하는 경우 사용하게 될 것 같음

std::cout << "address of pointer varaibable in main()" << &ptr << std::endl; 
// 현재 여기서 사용하는 메모리 주소와 위의 주석처리 되어 있는 memory1의 메모리주소와 다름
// 복사되어서 사용하기 때문에 값은 같으나 엄연히 메모리 주소가 다르다.

return 0;

}
#include <iostream>

using namespace std;

//void printArray(int array[])
// array처럼 보이지만 내부적으로는 포인터로 동작
void printArray(int *array)
// array처럼 보이지만 내부적으로는 포인터로 동작

{
	cout << sizeof(array) << endl;
	cout << *array << endl;

	*array = 100;
}

int main()
{
	int array[5] = { 9, 7, 5, 3, 1 };

	//cout << array[0] << " " << array[1] << endl;
	//cout << array << endl;
	//cout << &array[0] << endl;

	//cout << *array << endl;

	////char name[] = "jackjack";
	////cout << *name << endl;
	////

	int *ptr = array;
	//cout << &ptr << endl;

	//cout << ptr << endl;
	//cout << *ptr << endl;

	cout << sizeof(array) << endl; // 20
	cout << sizeof(ptr) << endl; // 20

	printArray(array); // 8
	
	cout << array[0] <<" "<< *array << endl; 
    // 함수 안에서 포인터 변수값을 변경해주면 함수 밖에서도 영향을 미침
	
    return 0;
} 

 

구조체와 함수를 활용한 예시

 

#include <iostream>

using namespace std;


struct MyStruct
{
	int array[5] = { 9,7,5,3,1 };

};

void doSomething(MyStruct ms)
{
	cout << sizeof(ms.array) << endl;
}
void pdoSomething(MyStruct *ms)
{
	cout << sizeof((*ms).array) << endl;
}
int main()
{
	MyStruct ms;
	cout << ms.array[0] << endl;
	cout << sizeof(ms.array[0]) << endl;

	cout << sizeof(ms.array) << endl;

	doSomething(ms);
	pdoSomething(&ms);

	return 0;
}

'c++(언어)' 카테고리의 다른 글

c++ 패키지 관리 vcpkg  (0) 2021.06.27

+ Recent posts