글쓴이: naturale

Quantile normalization

생물정보학에서 자주 사용되는 정규화 방법 중 하나인 quantile normalization이다. Quantile normalization은 비교하려는 샘플들의 분포를 완전히 동일하게 만들고 싶을 때, 또는 기준이 되는 분포(reference distribution)가 있는 경우 샘플들의 분포를 모두 기준 분포와 동일하게 만들고 싶을 때 사용할 수 있다.

복잡한 방식을 사용하는 과정이 아닌지라 쉽게 이해하고 쉽게 써먹을 수 있다.

 

알고리즘

  1. 샘플 내 값을 오름차순 정렬
  2. 아래 과정을 모든 n에 대하여 반복
    1. (기준 분포가 있을 떄)
      1. 모든 샘플의 n 번째 값들을 기준 분포의 n 번째 분위수로 대체
    2. (기준 분포가 없을 때)
      1. 각 샘플의 n 번째 값들을 평균(또는 중간값)
      2. 각 샘플의 n 번째 값들을 모두 2-1.의 값으로 대체
  3. 정렬된 데이터를 원래 데이터의 순서대로 복구

 

이해가 어렵다면 바로 아래 “예시”로 넘어가도 된다.

“값들을 순서대로 나열했을 때 n 번째 값”이 바로 통계학에서의. n 번째 분위수(n-th quantile)의 개념에 해당하고, 각 샘플 분포의 n 번째 분위수를 동일하게 조정해주는 작업이므로 quantile normalization이라는 이름이 붙었다.

딱히 알고리즘이랄 것도 없다. 1.에서 정렬 시 오름차순 대신 내림차순을 써도 아무 상관이 없다. 기준 분포가 없을 때 2-1.에서 평균, 중간값 대신 가중평균 등 자신에게 적합한 대푯값을 사용해도 된다.

 

예시

1. 이해를 위한 간단한 예시

다음 테이블과 같은 세 샘플(A, B, C)의 데이터가 있다고 하자. A, B, C의 분포가 동일해지도록 median quantile normalization해보자. 셀 색상은 원래 데이터의 순서를 표시하기 위해 넣어보았다.

qn_whole

  1. 열 별로 값을 정렬한다.
  2. 정렬된 데이터에서 행 별로 중간값을 계산한다.
  3. 각 행의 값을 2.에서 계산한 중간값으로 바꾼다.
  4. 정렬을 해제하여 기존 데이터와 같은 순서로 돌려준다

 

2. 규모가 좀 더 큰 예시

서로 다른 샘플에 어떤 실험을 한 결과 데이터가 세 세트(A, B, C) 있다고 하자. 세 샘플을 용이하게 비교하기 위해 샘플의 분포를 quantile normalize하여 동일하게 만들어보자.

 

기준 분포가 있을 때

표준정규분포를 기준 분포로 삼는다고 하자.

정규화 전 각 샘플의 분포는 아래와 같다.

A  B   C
0   3.570614    5.015660    6.526778
1   5.965098    1.258324    2.285988
2   3.767857    0.974910    4.374467
3   3.538398    3.362349    1.779624
4   5.178286    1.575602    3.065568
5   3.578033    3.434900    5.086503
6   5.330161    3.431059    3.573926
7   5.002079    4.885904    2.318780
8   5.694074    4.000688    4.615286
9   6.026262    -0.499165   1.999979
before

 

표준정규분포의 101-quantile을 사용하여 quantile normalization하면,

A  B   C
0   -1.000990   1.410420    1.410420
1   0.960838    -1.180947   -1.000990
2   -0.922178   -1.346263   0.162024
3   -1.086568   0.162024    -1.232341
4   0.162024    -0.848716   -0.476577
5   -0.960838   0.263612    0.620918
6   0.289397    0.238000    -0.187224
7   -0.012409   1.232341    -0.922178
8   0.651302    0.682300    0.368003
9   1.042824    -2.330079   -1.132497
after_ref

모든 샘플의 분포가 표준정규분포와 일치하는 것을 확인할 수 있다.

 

기준 분포가 없을 때

기준 분포가 없이 median을 사용하여 정규화하면,

A  B   C
0   -1.000990   1.410420    1.410420
1   0.960838    -1.180947   -1.000990
2   -0.922178   -1.346263   0.162024
3   -1.086568   0.162024    -1.232341
4   0.162024    -0.848716   -0.476577
5   -0.960838   0.263612    0.620918
6   0.289397    0.238000    -0.187224
7   -0.012409   1.232341    -0.922178
8   0.651302    0.682300    0.368003
9   1.042824    -2.330079   -1.132497
after

역시 마찬가지로 모든 샘플의 분포를 동일하게 만들 수 있다.

 

참고

이 코드를 돌리면 예시 2-2를 직접 해볼 수 있다.

웹 폰트 추출기

웹사이트를 둘러보다 마음에 드는 폰트가 있으면 다운로드할 수 있도록 (지나치게) 간단한 툴을 제작해보았다.

폰트 정보가 담긴 CSS stylesheet를 찾아 서버 내 웹폰트 경로를 추출하여 다운로드한다. 폰트 다운로드 링크가 닫혀있는 경우에는 메뉴에 폰트명이 표시되지만 다운받을 수는 없다.

Python 3가 설치되어있다는 전제하에 딱 한 줄이면 사이트에서 폰트를 다운받을 수 있다.

예를 들어 카카오서체를 다운받고 싶다면 카카오서체가 적용되어있는 카카오 같이가치 사이트를 다음과 같이 스크립트에 입력하면 된다.

./extract_webfont.py 'https://together.kakao.com'

툴은 여기에서 다운로드할 수 있다. 아파치 2.0 라이센스로 공개된 스크립트의 확장이다.
서체마다 저작권에 유의하여 사용할 것.


Sequence Motif 등장 확률

전체 n ntd로 이루어진 target RNA 한 가닥이 있다고 하자. 이 RNA는 완전히 랜덤하게 만들어진 가닥이라고 가정한다. 즉, 각 위치에 A, U, G, C가 같은 확률로 존재할 수 있다.

이제 k-mer sequence motif가 하나 있다고 하자. 이 sequence motif는 특정되어 있다.

이 때 Target RNA 가닥에서 이 motif가 한 번이라도 등장할 확률은 얼마일까?

우연한 기회에 이 문제에 대해 생각해보게 되어 여사건의 확률 등의 방법을 생각해보다 이런 방법으로는 쉽게 풀리지 않는다는걸 알게 되었다.

검색을 해봐도 정확한 해답을 제시하는 사람은 많지 않았다 (너무 쉬운 문제여서 인지?). 여기서는 확률의 inclusion-exclusion principle을 이용한 해답을 소개하려 한다.

 

마주서기 문제

본 문제와 비슷하지만 비교적 단순한 문제를 먼저 살펴보자.

n쌍의 부부가 있다. 한 쪽에는 남편끼리, 다른 쪽에는 부인끼리 늘어 설 때, 적어도 한 쌍의 부부가 서로 마주보고 설 확률은 얼마일까?

이 문제 역시 여사건의 확률을 이용해서 풀기는 쉽지 않다 (재귀함수를 만들게 된다). 대신 inclusion-exclusion principle을 사용해서 풀어보자.

사건 A_i를 “i번째 부부가 서로 마주보고 서는 사건”이라고 하자. 그러면

\begin{array}{lcl} &&P(A_i) = \dfrac{(n-1)!}{n!} \text{, } i=1, ..., n \\ &&P(A_i \cap A_j) = \dfrac{(n-2)!}{n!} \text{, } i < j \\ && ... \\ &&P(A_1 \cap A_2 \cap ... \cap A_n) = \dfrac{1}{n!} \end{array}

이므로

\begin{array}{lcl} P(\cup_{i=1}^{n} A_i) &=& \sum_{i=1}^{n}P(A_i) - \sum_{i<j}P(A_i \cap A_j) \\ & & + ... + (-1)^{n+1}\sum_{i<j<...<l}P(A_i \cap A_j \cap ... \cap A_l) \\ &=& \sum_{i=1}^{n} (-1)^{i+1} \dfrac{1}{i!} \end{array}

이다.

 

본 문제

사건 A_i \text{, } i=1, ..., m를 “i번째 위치에 motif가 등장하는 사건”이라고 하자. 단 mnk로 나눈 몫이다. 그러면

\begin{array}{lcl} &&P(A_i) = \dfrac{4^{n-k}}{4^n} \text{, } i \leq n-(k-1) \\ &&P(A_i \cap A_j) = \dfrac{4^{n-2k}}{4^n} \text{, } i<j \text{ and } i, j \leq n-2(k-1) \\ &&P(A_i \cap A_j \cap A_k) = \dfrac{4^{n-3k}}{4^n} \text{, } i<j<k\text{ and } i, j, k \leq n-3(k-1) \\ &&... \\ &&P(A_1 \cap A_2 \cap ... \cap A_m) = \dfrac{4^{n-mk}}{4^n} \end{array}

이므로 마주서기 문제에서와 같은 방법으로

P(\cup_{i=1}^{m} A_i) = \sum_{i=1}^{m} (-1)^{i+1} \binom{n-i(k-1)}{i} 4^{-ki}

임을 알 수 있다.

 

이 방법으로 1~500 ntd의 random target sequence에 3, 4, 7-mer fixed motif가 존재할 확률을 그래프로 나타내보면 다음과 같다.

Thu Oct 25 00:15:46 2018

 


참고

 

 

FASTA 압축기 만들기

DNA 시퀀스를 다루는 사람들에겐 FASTA/FASTQ라는 이름만큼 익숙한 포맷이 또 없을 것이다. 시퀀싱된 염기서열을 저장하는, 가히 표준이라 할 수 있을만큼 널리 사용되는 포맷이다. FASTA 포맷은 샘플 이름과 샘플 시퀀스로 이루어져있다. 예를 들어 “sample1”, “sample2″이라는 이름의 샘플이 각각 “ATGCATGC”, “TTTTTTTTT”라는 시퀀스로 시퀀싱되었다면 아래와 같은 FASTA 포맷의 파일로 표현할 수 있다.

>sample1
ATGCATGC
>sample2
TTTTTTTTT

FASTA 포맷의 가장 큰 장점은 굳이 포맷이라고 이름붙이는 것이 어색해 보일 정도의 간결성과 직관성에 있다. Parsing 작업이 굳이 필요하지 않으며, 필요하더라도 아주 쉽게 코딩할 수 있다 (물론 Biopython등 상용화된 패키지를 이용하는 것이 제일 편하다).

굳이 단점을 꼽자면 용량 측면에서의 비효율성이 있을 것이다. 각 베이스를 베이스 그대로 표기하여 직관성을 얻은 대신 하나의 베이스마다 8비트(1바이트)의 메모리를 할당해야하는 것이다.

비록 그렇게 큰 용량은 아니지만, 빠르고 원활한 시퀀스 파일 공유를 위해 FASTA 압축 알고리즘을 짜보면 어떨까 싶어 재미삼아 몇 가지 코딩을 해봤다.

이 글은 “새로운”(?) FASTA용 무손실 압축 알고리즘을 짜보고 싶다는 생각으로 시작했지만 결국 상용화된 프로그램이 짱이라는 결론에 이르는, 컴퓨터 비전공자의 분투를 담은 글이다.

1. Look-and-Say 수열을 이용한 압축 알고리즘 (.say format)

Look-and-say 수열은 현재 항 숫자의 종류와 갯수를 읽고, 읽은 내용으로 다음 항을 결정하는 수열이다. 수열의 첫 항이 1일 때, “1이 한 개 있다”고 읽을 수 있다. “1이 한 개(1)”이므로 이 수열의 둘째 항은 11이된다. 둘째 항은 11이므로 “1이 두 개 있다”고 읽을 수 있다. “1이 두 개(2)”이므로 셋째 항은 12가 된다. 이 과정을 계속해서 반복하면 다음과 같은 수열이 만들어진다.

[1]: 1
[2]: 11
[3]: 12
[4]: 1121
[5]: 122111
...

베르베르의 소설 ‘개미’에도 등장하여 우리나라에선 ‘개미 수열’이라는 이름으로도 알려져 있다. 바로 이 수열을 시퀀스 데이터 압축에 사용하면 어떨까 싶었다.

  • 1바이트(8비트) 중 3비트는 베이스 종류를 나타내는 데에 사용한다.
    • 000은 A, 001은 T, 010은 G, 011은 G, 100은 N, 101은 U
    • 110과 111은 비워두었다.
  • 나머지 5바이트는 반복수를 나타내는 데에 사용한다.
    • 00000 ~ 11111, 즉 1부터 32까지의 반복수를 표현할 수 있다.
    • 32번 이상 반복되는 베이스는 1바이트로 표현할 수 없다. 두 개 이상의 바이트를 써야한다.

예를 들어 AAAAAAATGGGGGGGGG라는 시퀀스는 이 방법으로 3 바이트로 표현할 수 있다.

[-1 byte-] [-2 byte-] [-3 byte-]
[00000110] [00100000] [01001000]

같은 시퀀스를 FASTA로 저장하려면 17바이트가 필요하다.

FASTA 포맷에 비교한 .said 포맷은 장점은 다음 두 가지로 요약할 수 있다.

  1. Homopolymer가 많은 시퀀스일수록 압축 효율이 높아진다. poly A region과 같은 시퀀스에서는 비교도 안될 정도로 작은 용량의 파일을 만드는 것을 확인할 수 있다.
  2. 반복이 거의 없더라도 같은 시퀀스가 저장된 FASTA보다는 작은(<=) 파일을 만든다. 이는 한 베이스가 반복되지 않는 경우에도 FASTA에서와 같이 1바이트 용량만을 차지하기 때문이다.
    반복수를 (대체로) 늘려주는 Burrows-Wheeler transformation을 사용한 후 `.said` 압축을 하면 좀 더 좋은 압축률을 얻을 수 있지 않을까? 했는데 생각만큼 크게 개선되진 않았다.

이 스크립트를 사용하면 .said 포맷으로 FASTA 변환할 수 있다: [압축, 압축해제]

# COMPRESS
python fa2said.py input.fa
# DECOMPRESS
python said2fa.py input.said

2. 한 베이스를 2 비트만으로 표현하기 (.two format)

genome / CDS 시퀀스는 생각보다 반복이 그리 많지 않았다. .said format으로는 높은 압축률을 얻기 어려운 상황이다. 그래서 아예 반복수를 기록하지 않고 한 베이스가 차지하는 용량을 극도로 줄이면 어떨까 싶었다.

압축 할 시퀀스에 A, T, G, C 네 종류의 베이스밖에 없다고 가정하면 (2^2개 이므로) 2비트만 사용하면 한 베이스를 인코딩할 수 있다. 한 베이스를 표현하는 데에 8비트를 사용해야 했던 FASTA 포맷에 비해 용량을 무려 1/4로 줄일 수 있는 것이다. 이렇게 압축한 파일 포맷을 .two 포맷이라고 부르도록 하자.

ATGCAATC의 경우 .two 포맷으론 2바이트(16비트)로 인코딩할 수 있다.

A T G C    A A T C
[00011011] [00000111]

이 스크립트를 사용하면 FASTA파일을 .two 포맷으로 압축할 수 있다: [압축, 압축해제]

# COMPRESS
python fa2two.py input.fa
# DECOMPRESS
python two2fa.py input.say

맺으며

이 기회에 야매로 압축 알고리즘을 공부하면서 첫 번째 시도(.said)가 run-length encoding이라 불리는 가장 원시적인 압축 알고리즘이라는 것을 알았다.

두 번째 시도 (.two)는 Huffman coding과 상당히 닮은 구석이 있다. 차이점은 DNA/RNA의 경우 인코딩하려는 데이터의 종류(A, T, U, G 또는 C, 추가로 N)가 이미 알려져있다는 것이다. DNA의 경우 데이터의 종류가 4개(A, T, G, C) 뿐이기 때문에 사람이 직접 2비트만으로 모든 데이터를 인코딩할 수 있었다.

두 번째 시도는 생물정보학계의 영웅 Jim Kent가 과거에 이미 .2bit라는 이름으로 상용화해서 쓰이고 있는 포맷이었다 [Jim Kent’s Repo]. C로 짜인 코드여서 내 것보다 훨씬 빠르게 압축할 수 있다. .2bit 포맷은 원하는 샘플의 시퀀스만 가져올 수도 있도록 각 샘플의 해시 딕셔너리도 함께 저장한다.

DNA 시퀀스와 달리 데이터 종류가 알려져 있지 않은 경우엔 Huffman coding을 사용할 수 있다. Huffman coding은 binary tree를 사용해서 자동으로 최적의 인코딩을 찾아준다. .two는 binary tree를 사용하지 않고 사람이 직접 인코딩 딕셔너리를 정해주는 특수한 변형이라고 할 수 있겠다.

.zip 압축에 사용되는 Lempel-Ziv 알고리즘은 인코딩 딕셔너리가 Huffman coding과 같이 고정되어 있지 않고 현재까지 압축한 데이터에 따라 유동적으로 변한다. 비슷한 패턴이 반복해서 나타나는 파일에 있어서 효율적인 압축을 가능케 한다. 압축을 풀기 위해서는 데이터 인코딩에 사용한 binary tree의 정보를 어떤 형태로든 압축된 결과 파일과 함께 저장해야하는 Huffman coding과 달리, 압축된 데이터 그 자체에 인코딩 딕셔너리가 포함된 것과 마찬가지이기 때문에 별도의 정보를 저장할 필요도 없다.

코딩한 결과물 자체는 성능 면에서나 속도 면에서나 무쓸모하지만 그 과정에서 많이 배웠다.

참고

Weakly-Supervised localization: 뉴럴넷은 어디를 바라보고 있는가

ConvNet이 이미지를 다루는 데 강력한 성능을 보이기는 하지만, layer가 깊어지면 깊어질수록 이미지의 어떤 특징을 보고 판단을 내리는지를 파악하기란 쉽지 않다.

이미지 하나를 인풋으로 넣고 layer를 차례차례 통과하면서 activation이 어떻게 변하는지를 살펴볼 수도 있겠지만 VGG와 같은 모형처럼 레이어가 매우 깊어지거나 GoogLeNet, ResNet처럼 특수한 모듈 구조를 사용, 또는 residual connection을 사용하는 네트워크에서는 뉴럴넷이 이미지의 어느 부분을 중요하게 생각하는지의 정보를 얻기가 힘들어진다.

블랙박스인 듯 보이는 뉴럴넷(특히 ConvNet)의 내부를 조금이나마 투명하게 드러내려는 시도는 꾸준히 있어왔다. 구글에서도 최근 “Inceptionism”이라는 이름으로 재미난 연구 결과를 블로그에 공개한 바 있다.

여기서는 이보다 뉴럴넷이 “어디를 집중해서 보고 있는지”를 제시한 아주 단순하고도 유용한 방법을 살펴보고자 한다. 바로 weakly-supervised localization의 시초격 되는 방법인데, localizer 모형에게 이미지에 어떤 사물이 있는지만을 학습시키면 이미지에 어떤 사물이 있는지는 물론, 이미지의 어느 위치에 그 사물이 있는지 까지 꽤나 정확하게 예측할 수 있다.

지도학습으로 학습시키긴 했지만 학습 과정에서 알려주지 않은 사물의 위치까지 학습한다는 점에서 이러한 방식을 weakly-supervised라고 이름 붙였다. 해당 논문은 재작년에 ArXiv에 올라온 것으로 발표 당시 큰 주목을 받았었다.

 

Architecture

이 논문에서 제시한 Weakly-supervised localizer의 구조는 매우 단순하다. 어떻게 localizer를 만드는지 그 과정을 하나하나 짚어보자.

스크린샷 2018-06-17 오후 10.12.16.png

0. (Pre-trained) CNN model

CNN classifier를 가져온다. 이 classifier는 이미지를 보고 이미지에 상응하는 class label을 예측해야한다. 밑바닥부터 학습시켜도 되지만 pre-trained 모형을 사용하면 좀 더 빠르게 좋은 성능을 낼 수 있다.

가져온 CNN classifier에서 말단의 fully connected layer를 모두 제거한다. 즉 가장 깊이 있는 CONV layer가 최말단 레이어가 되도록 한다.

1. CONV layer

0.의 CNN classifier의 최말단 CONV layer 뒤에 CONV layer를 하나 추가한다. 논문에서는 1024개의 filter를 가지는 CONV layer를 추가했다. 여기서도 filter 갯수는 1024라고 가정하자.

이 레이어의 출력은 1024개의 feature map (depth=1024)이 된다.

2. Global Average Pooling (GAP)

1024개의 feature map을 크기가 1024인 벡터로 변환시킨다. 다시 말해 feature map 하나당 수치 하나로 변환시킨다.

이 과정에서 global average pooling (GAP)을 사용한다. GAP layer는 pool size가 input feature map의 크기와 같은, 즉 이미지 전체에서 딱 하나의 값(평균값)만을 pooling하는 레이어이다.

이 논문 이전에 global max pooling (GMP)를 사용한 weakly-supervised localization 방법이 공개되었었는데 GMP는 가장 activation이 강한 region의 정보만을 가져오기 때문에 사물의 경계선을 파악하는 데에 그쳤다고 한다. 사물의 위치 전체를 localization하는 데에는 모든 activation의 정보를 평균내는 GAP가 더 적합하다.

GAP layer의 출력은 각 feature map(\text{width} \times \text{height} \times 1024)의 평균값의 벡터(1 \times 1 \times 1024)가 된다.

3. Fully connected layer

fully connected layer를 GAP layer 뒤에 하나 추가한다.

이 dense layer의 입력은 GAP layer의 출력, 즉 크기 1 \times 1 \times 1024의 벡터이고 출력은 예측하려는 class label의 갯수이다. Weight matrix W의 모양은 1024 \times \text{(\#classes)}가 된다.

4. Class Activation Map (CAM) 계산

1.의 CONV layer에서의 feature map과 3.의 dense layer에서의 weight W를 사용해서 class activation map (CAM)을 계산한다.

Label=c인 class의 activation map CAM_c는 다음과 같이 계산한다.

CAM_c = \sum_{k=1}^{1024}{w_{k, c}f_k}

여기서 w_{k, c}k번째 GAP 벡터에서 class c로 이어지는 가중치값(Wkc열 원소)이고, f_k는 1.에서 추가한 CONV layer의 k번째 feature map이다.

이렇게 구한 CAM_c는 해당 이미지가 클래스 c에 속한다고 판단내릴 때 ConvNet이 이미지의 어떤 부분을 보고있었는지의 정보를 가지고 있다.

CAM_c의 값이 큰 부분이 클래스 c에 속하는 사물의 위치라고 ConvNet이 판단내린 것이다.

 

응용

Weakly-supervised localization은 여러 방면에 응용될 수 있다. Semantic image segmentation을 위한 데이터를 만드는 데에도 사용될 수 있고, ConvNet이 보고있는 지점을 파악할 수 있다는 점에서 ConvNet이 이미지로부터 학습한 feature를 사람이 직관적으로 이해할 수 있도록 시각화하는 데에도 사용될 수 있다.

이미 학습된 모형을 사용해서 간단히 만들 수 있다는 큰 장점이 있어 여러 분야에 매우 쉽게 응용할 수 있다. 비록 localizer가 아닌 classifier에 비해서는 classification 성능이 떨어지지만 그 차이가 크지 않으며 localization 기능까지 덤으로 추가할 수 있다.

여기서는 직접 간단한 ConvNet을 만들어 두 가지 작업에 응용해보았다.

1) Facial emotion recognition

학습과 예측에 사용한 데이터는 다음과 같다. [Kaggle]

  • Feature: 48 \times 48 크기의 흑백 얼굴 사진.
  • Target: 사진 속 인물의 표정(감정). 7가지 중 하나이다(neutral, fear, happy, sad, surprise, angry, disgust).
  • train set: 25120 imgs / test set: 5382 imgs

이 데이터를 학습해서 사람 표정의 어떤 부분을 보고 감정을 유추하는지를 알아보려 했다.

모형 구조와 학습에 사용한 하이퍼 파라미터는 이 노트북[1, 2]을 참고하시길 바란다.

결과를 살펴보면 꽤 그럴듯하다.

fer-1

주로 행복한 표정은 입 모양을 보고 판단하는 듯하다. 입모양이 크고 반달 모양이면 happy라고 판단할 확률이 크게 올라간다. 놀란 표정은 눈동자의 크기를 보고 판단하고 무표정은 이목구비의 전체적 형태를 보고 판단하는 듯하다.

fer-2

이 예시에서는 True class는 FEAR로 예측에는 실패했지만  SURPRISE라고 판단하는 것도 틀린 판단은 아닐 수 있다는 것을 보여준다. ConvNet이 무엇을 보고있는지를 주의 깊게 봐야하는 이유다.

2) Chest X-Ray (Pneumonia)

의료 이미지를 분석하는 경우엔 올바르게 질병 또는 정상상태를 분류하는 것도 중요하지만, 이미지의 어떤 부분(환부)을 보고 그런 판단을 내렸는지를 살펴보는 것이 매우 중요하다. 어디까지나 최종 판단은 의료 분야의 전문 종사자가 내리는 것인 만큼 전문가가 판단을 내리는 데에 도움이 되도록 뉴럴넷이 판단한 환부의 위치까지 표시해 준다면 더욱 유용할게다.

학습과 예측에 사용한 데이터는 다음과 같다. [Kaggle]

  • Feature: 다양한 크기의 흉부 엑스레이 사진.
  • Target: 폐렴인지 정상인지. [PNEUMONIA / NORMAL]
  • train set: 5216 imgs / test set: 624 imgs

데이터 학습이 용이하도록 모든 사진을 가로 256 픽셀, 세로 200 픽셀로 resize한 후 모형에 입력시켰다. 모형 구조와 학습에 사용한 하이퍼 파라미터는 이 노트북을 참고하시길 바란다 (실수로 zero-padding을 하지 않아서 이미지의 가장자리 부분의 정보 손실이 일어났다).

81%의 꽤 높은 정확도로 test set을 올바르게 진단할 수 있었다.

 

xray-1

폐렴 환자의 흉부 엑스레이 사진에서는 주로 좌폐와 우폐 사이의 공간을 유심히 살펴본 것으로 보인다.

xray-2.png

 

반면 정상 환자의 사진에서는 옆구리 부분의 흉골, 양쪽 폐 아래의 공간을 중요하게 본 듯 하다.

임상 전문가라면 뉴럴넷이 살펴본 위치를 보고 더 많은 아이디어를 얻을 수 있지 않을까?

 

참고

 

 

 

 

 

 

Batch Normalization 이해하기

현대적인 딥러닝 모델을 디자인할 거의 항상 빠지지 않고 쓰이는 테크닉들이 있다. 하나는 recurrent 구조 (LSTM, Attention)이고 다른 하나는 batch normalization (BatchNorm)이다. LSTM과 attention 대해서는 recurrent neural net 다루면서 자세히 살펴보도록 하고 이번 글에서는 학습 과정에서 뉴럴넷을 안정시켜주는 표준화 기법 하나인 batch normalization 대해 다뤄보겠다.

 

  • 기존 방법의 문제점
  • BatchNorm
    • 알고리즘
    • 테스트할 때
    • BN layer
  • TensorFlow 구현

 

기존 방법의 문제점

BatchNorm이 어떤 의미를 가지는지를 알기 위해서는 BatchNorm이 고안되기 이전의 딥러닝 모형 초기화 및 학습 과정 표준화 과정을 둘러볼 필요가 있다.

뉴럴넷이 안정적으로 잘 학습되기 위해서는 입력층에 넣을 인풋과 각 층의 weight를 표준화할 필요가 있다. BatchNorm이 고안되기 전에는 두 가지 방법을 주로 사용했는데, 이전 포스트[1, 2]에서 각각의 방법을 간단히 다룬 바 있다. 간단히 복기하자면 이렇다: (1) 인풋은 centering scaling하고 (2) 인풋 뉴런 n개인 층의 weight \div \sqrt{n/2}로 표준화한다. 단순한 방법이지만 표준화하지 않은 입력, 가중치값을 사용했을 때에 비해 더 빨리, 더 좋은 성능으로 수렴하는 것을 경험적으로 확인할 수 있다.

여기서 중요한 문제가 발생한다. 입력층에 넣는 인풋은 표준화할 수 있다. 뉴럴넷에 넣기 전에 우리가 원하는 방식으로 원하는 만큼 preprocessing을 하면 된다. 그 결과 입력층의 input distribution은 항상 비슷한 형태로 유지가 되고 안정적으로 가중치 학습을 진행할 수 있다.

e18489e185b3e1848fe185a6e1848ee185b53.png

그러나 은닉층은 인풋의 분포가 학습이 진행됨에 따라 계속 변한다. 은닉층은 이전 레이어의 activation f(XW)을 입력으로 받는다. 학습 과정에서 가중치 W의 값이 W^\prime로 업데이트되면 이전 레이어의 activation 또한 f(XW^\prime)로 바뀌게 된다. 은닉층의 입장에서는 인풋 값의 분포가 계속 널뛰는 것이나 마찬가지이다. 입력 분포의 형태가 유지되지 않으므로 학습도 잘 진행되지 않는다. 그라디언트 값이 큰 학습 초기일수록 문제가 더 심각해진다.

스케치

 

Batch Normalization

알고리즘

바로 위에서 언급한 문제를 internal covariate shift라고 한다. 그대로 입력층보다 깊은, 내부에 있는(internal) 층의 입력값, 공변량(covariate) 고정된 분포를 갖지 않고 이리저리 움직인다(shift) 의미이다. BatchNorm 바로 internal covariate shift 해결하는 테크닉이다.

[1]

은닉층의 입력도 표준화한다면 안정적으로 깊은 레이어의 가중치도 학습시킬 수 있을 것이다. “은닉층의 입력을 표준화한다는 것은 곧이전 층의 출력(raw activation)을 표준화한다는 의미와 같다.

딥러닝은 거의 항상 전체 샘플을 mini batch로 나누어 학습하고 가중치를 업데이트하므로 이전 층의 raw activation을 표준화할때도 각 batch마다 따로 표준화하면 된다.

스케치

이와 같이 각각의 minibatch 평균 \mu_{\mathcal{B}} = \frac{1}{m} \sum_i {x_iw_i} 표준편차 \sigma_{\mathcal{B}} = \frac{1}{m} \sum_i {(x_iw_i - \mu_{\mathcal{B}})^2} 표준화한 activation a_s = f(\frac{XW_1 - \mu_{\mathcal{B}}}{\sigma_{\mathcal{B}}}) 은닉층 B 입력으로 사용하면 은닉층 B 입력은 고정된 분포를 따른다.

쉬워도 너무 쉽다. 이렇게만 하면 될 것 같지만..

[1 문제점]

문제가 가지 있다. 이렇게 은닉층의 입력을 표준화하면 gradient update 과정에서 bias(편향)값이 무시된다. [1]만을 사용해서 표준화한다고 그라디언트 업데이트 과정을 자세히 살펴보자. Raw activation a_r = wx + b라고 E(a_r) = \frac{1}{n} \sum_i a_{r_{i}}이므로

  1. 그라디언트를 계산한다.
    • \Delta b \propto - {\partial L}/{\partial b},  where L is a loss function.
  2. 편향(과 가중치)을 업데이트한다.
    • b \gets b + \Delta b
  3. 편향을 업데이트한 이후의 raw activation:
    • a_r ^\prime = wx + (b + \Delta b)
  4. [1] 이용해서 센터링만 raw activation:
    • \begin{array}{lcl} a_{r_{centered}} ^\prime &=& a_r ^\prime - E(a_r ^\prime) \\ &=& \{(wx + b) + \Delta b\} - \{ E[wx + b] + \Delta b \} \\ &=& (wx + b) - E[wx + b] \end{array}

Bias b 업데이트 \Delta b 완벽하게 캔슬되었다. 초기 편향값에서 이상 업데이트가 되지 않는 것이다. 종류의 파라미터 w, b 사용했는데 파라미터 w 가지만 사용하는 단순한 모형으로 irreversible하게 변환된 것이다.

이 때문에 b 대신 편향의 역할을 할 파라미터를 추가해야한다. 이 파라미터는 그라디언트 업데이트 과정에서 무시되어서는 안된다.

다른 문제도 있다. raw activation 분포를 고정시키는 것은 좋지만 항상 N(0, 1) 고정시킬 필요는 없다. 적절하게 scaling, shifting activation \gamma \cdot \frac{a_r - \mu_{\mathcal{B}}}{\sigma_{\mathcal{B}}} + \beta 사용하는 것이 학습에 도움될 수도 있다.

형태의 activation 사용할 경우 필요하다면 표준화를 되돌릴 수도 있다. \gamma = \sigma_{\mathcal{B}}, \beta = \mu_{\mathcal{B}} \gamma \cdot \frac{a_r - \mu_{\mathcal{B}}}{\sigma_{\mathcal{B}}} + \beta = a_r이기 때문이다.

[2]

위의 문제를 극복하기 위해 표준화한 scaling shifting raw activation, 즉

a_{BN} = \gamma \cdot \frac{XW_1 - \mu_{\mathcal{B}}}{\sigma_{\mathcal{B}}} + \beta

activation function f 입력으로 사용한다. 은닉층 B 입력으로는 f(a_{BN}) 사용한다. 방법을 BatchNorm이라고 한다. \gamma, \beta 파라미터로 학습 과정에서 업데이트되는 값이다.

BatchNorm 장점이 꽤나 많은데

  • bias 업데이트를 무시하지 않는다. \beta bias처럼 행동한다. \beta 업데이트는 표준화해도 캔슬되지 않는다.
  • 은닉층마다 적절한 input distribution 가질 있다. scaling factor \gamma shifting factor \beta 사용해서 적절한 모양으로 입력분포를 조정할 있다.
  • 필요한 경우 표준화를 하지 않을 수도 있다. 위에서 언급한 \gamma = \sigma_{\mathcal{B}}, \beta = \mu_{\mathcal{B}} 경우이다.
  • Activation 값을 적당한 크기로 유지하기 때문에 vanishing gradient 현상을 어느정도 막아준다. 덕분에 tanh, softmax같은 saturating nonlinearity 사용해도 문제가 생긴다.
  • batch-wise로 계산하기 때문에 컴퓨팅하기 용이하다.
  • 위의 장점들을 모두 가지면서, 동시에 층마다 입력 분포를 특정 형태로 안정시켜서 internal covariate shift 방지할 있다.
  • 입력 분포가 안정되므로 학습시 손실함수가 더 빨리, 더 좋은 값으로 수렴한다.
  • 초기 learning rate를 크게 설정해도 안정적으로 수렴한다고 한다.
  • Weak regularizer로도 작용한다고 한다.

이쯤 되면 거의 만능이다.

테스트

지금까지 다룬 내용은 모두 학습 과정에서 일어나는 일들이다. 학습 과정에서는 raw activation minibatch mean, stdev 표준화하면 됐었다. 그런데 학습을 마치고 테스트(또는 evaluation, inference) 때에는 minibatch mean, stdev 존재하지 않는다.

테스트 과정에서는 대신 전체 training data mean, stdev 사용해서 BatchNorm 한다. 전체 training data mean, stdev 번에 계산하기에는 메모리의 제약이 있으므로, minibatch statistic 평균낸 값을 대신 사용한다.

, n개의 minibatch 있을 ,

\hat{\mu} = \frac{1}{n} \sum_i {\mu_{\mathcal{B}}^{(i)}}
\hat{\sigma} = \frac{1}{n} \sum_i {\sigma_{\mathcal{B}}^{(i)}}

Minibatch statistic 따로 저장할 필요 없이 학습 과정에서 moving average \hat{\mu}, \hat{\sigma} 계산하면 된다. Exponential moving average 사용해도 좋다.

i번째 minibatch statistic 각각 \mu_{\mathcal{B}}^{(i)}, \sigma_{\mathcal{B}}^{(i)}라고 ,

\hat{\mu} \gets \alpha \hat{\mu} + (1-\alpha) \mu_{\mathcal{B}}^{(i)}
\hat{\sigma} \gets \alpha \hat{\sigma} + (1-\alpha) \sigma_{\mathcal{B}}^{(i)}

BatchNorm layer

ReLU activation 뉴럴넷의 레이어로 나타낼 있듯 BatchNorm 또한 레이어로 표현할 있다. BN layer raw activation activation function 사이에 위치한다. Convolutional layer에 BatchNorm을 적용하고 싶을 때에도 동일하게 raw feature map과 ReLU layer 사이에 BN layer를 추가하면 된다.

e18489e185b3e1848fe185a6e1848ee185b57.png

BN layer mini batch raw activations a_r 입력받아 아래와 같은 연산을 수행하여 다음 레이어(activation function f) 전달한다.

BN_{\gamma, \beta}(a_r) = \gamma \cdot \frac{a_r - \mu_{\mathcal{B}}}{\sigma_{\mathcal{B}}} + \beta

또한 테스트 사용하기 위해 학습 과정에서 minibatch statistic exponential moving average(또는 그냥 MA) minibatch마다 업데이트한다.

 

TensorFlow 구현

구글에서 고안한 방법답게 TensorFlow에 이 내용들이 친절히 함수로 구현되어 있다. tf.nn.batch_normalization, tf.contrib.slim.batch_norm를 쓰면 간단히 위 알고리즘을 모형 구축에 사용할 수 있다.

tf.nn.batch_normalization을 사용할 경우, minibatch statistic의 EMA를 계산하는 코드를 따로 작성해야 한다.

tf.contrib.slim.batch_norm를 사용할 경우 is_training 옵션을 True로 주면 자동으로 EMA를 계산해서 저장하고, False로 주면 저장된 EMA 값으로 activation을 표준화한다.

TF-Slim 레이어에도 쉽게 적용시킬 수 있다.

import tensorflow as tf
import tensorflow.contrib.slim as slim

bn_params = {"decay": .9,
             "updates_collections": None,
             "is_training": tf.placeholder(tf.bool)}
net = slim.fully_connected(input, 1024,
                           normalizer_fn=slim.batch_norm,
                           normalizer_params=bn_params)

Convolutional layer에도 마찬가지다.

net = slim.conv2d(input, 64, [5,5], padding="SAME",
                  normalizer_fn=slim.batch_norm,
                  normalizer_params=bn_params)

 

 

참고

ConvNet (CNN) 이해하기

지난 시리즈에서는 뉴럴넷 중 가장 단순한 형태인 MLP, dense (fully connected) layer만을 사용하는 뉴럴넷과 이를 학습시키는 방법에 대해서 살펴보았다. MLP를 사용하면 효과적으로 비선형 함수를 모사할 수 있었고 이 덕분에 기존의 선형함수 기반 모형에 비해 훌륭한 성능을 낼 수 있었다.

(1) 선형 분류기
(2) 퍼셉트론과 신경망
(3) 학습에는 왕도가 없다
(4) 디테일

그러나 실제 데이터를 올바르게 분류하거나 회귀할 때에는 이보다는 복잡한 구조가 필요한 경우가 있다. 다음의 몇 가지 상황을 생각해보자.

1) MLP에서는 별도의 feature engineering 없이 raw feature 곧바로 입력층에 넣었었다. 붓꽃 데이터, 또는 데이터의 차원이 크지 않은 경우에는 raw feature를 전부 사용해도 모형의 파라미터 수가 크지 않았기 때문에 학습 과정에서 문제가 생기지 않았고 성능도 좋았지만, 이미지와 같은 데이터에서는 문제가 달라진다.

이미지 데이터를 예시로 들어보자. 가로 48픽셀, 세로 48픽셀의 컬러(RGB) 이미지 데이터를 생각해보자. 이미지의 각각의 픽셀을 하나의 feature 사용해서 MLP 입력층에 넣을 있을 것이다. 이렇게 feature engineering 없이 픽셀의 raw value 전부 입력으로 넣는다면, 뉴럴넷이 감당해야 하는 feature 차원수는 48 \times 48 \times 3 = 6912 된다. 겨우 가로세로 48픽셀밖에 안되는 저화질 이미지였는데도 feature 차원이 7000 달한다. Feature 갯수가 이렇게 많아지게 되면 컴퓨팅 속도가 저하되고 오버피팅의 위험이 생길 있다.

2) MLP에서는 샘플이 등장한 맥락을 고려하지 않았다. , 샘플들이 정렬되어 있는 순서가 중요하지 않았다. 그러나 주식 일별 종가, 문장 속 단어와 같은 데이터에서는 각 샘플의 앞 뒤로 어떤 샘플들이 있었는지의 정보(‘문맥’)도 중요하다. 이 경우엔 MLP만으로는 충분히 좋은 성능을 발휘할 수 없다.

이번 시리즈에서는 전통적인 dense layer 외에 이미지 등의 데이터를 처리하기 위해 새로이 고안된 뉴럴넷의 레이어에 대해서 살펴보고자 한다. 시작은 convolutional neural network (CNN; ConvNet) 구성하는 레이어들이다.

 

  • 2-D CONV layer
    • Convolution 연산
    • 수학/신호처리학적 의미
  • RELU layer
  • POOL layer
  • 일반적인 ConvNet 구조
  • ConvNet 개선하기
    • 1×1 convolution (InceptionNet)
    • Dense CONV 표현하기
    • 변형된 convolution

 

Convolutional (CONV) layer

앞의 예시에서 이미지의 픽셀을 그대로 feature 사용하는 것은 좋지 않은 방법이라는 것을 언급했었다. 대신 이미지로부터 독특한 특성만을 뽑아내어 feature 사용한다면 좋은 성능을 있을 것이다. 과정을 feature extraction이라고 하고, 이미지에서 feature extractor 동작하는 레이어가 바로 convolutional layer이다.

Convolutional layer 가지 가정을 전제로 한다.

  1. Local connectivity: 이전 레이어의 모든 뉴런과 연결되는 MLP에서와는 달리 CONV layer는 이전 레이어의 뉴런 중 일부와만 연결된다. 즉 이미지의 모든 픽셀과 연결되는 대신 이미지 중 일부분의 픽셀과만 연결된다.
  2. Spatial invariance: 이미지의 한 부분의 데이터 분포는 다른 부분에서의 데이터 분포와 다르지 않다. , 이미지의 분포적인 특성은 어느 부위에서든 동일하다고 가정하는 것이다.

Dense layer에서 입력 샘플을 1차원의 벡터 데이터로 보았다면, Convolutional layer 입력 샘플을 3차원의 텐서(Tensor) 데이터로 인식한다. Convolutional layer 입력 3차원 데이터를 적절히 변형해서 또다른 3차원 데이터를 출력한다.

스케치

Convolutional layer 하는 일을 자세히 살펴보자.

  1. 먼저 이미지에서 특징적인 부분을 추출하는 데에 사용될 필터(또는 커널)가 정의되어야 한다. 필터 크기는 이미지 크기보다 작거나 같다. 필터는 인풋 이미지를 시작부터 끝까지 차례차례 훑으면서(마치 sliding window처럼) 이미지 픽셀 값에 필터 값을 곱해 더한 값을 출력한다.
  2. 필터가 이미지를 얼마나 자세히 훑으면서 지나갈지를 결정해야한다. 이것을 stride라고 한다. Stride 1로 정의된 경우, 필터는 이미지 위를 한 번에 한 픽셀씩 이동하면서 자세히 훑는다. Stride 10으로 정의된 경우, 필터는 이미지 위를 한 번에 10픽셀씩 이동하면서 대충 훑는다. (가로방향 stride와 세로방향 stride를 다르게 설정할 수도 있다)

스케치

f_{ij}는 필터의 ij열 값, x_{ij}는 인풋 이미지의 ij열 값이다.

convolutional layer는 필터를 일정 간격씩 이미지 위에서 이동시키면서 필터 값을 이미지 픽셀값에 곱하여 더한 값을 출력한다. 출력된 feature map의 픽셀 하나는 인풋 이미지 중 3×3만큼의 정보를 바탕으로 결정된 값이다. feature map receptive field 크기는 3×3이 된다는 뜻이다. 인풋 이미지 중 3×3만큼을보고‘ Feature map의 픽셀 하나의 값을 계산했다는 의미다.

애니메이션으로 살펴보면 이해가 쉽다. 파란색은 인풋 이미지, 초록색은 필터이다.

1*VVvdh-BUKFh2pwDD0kPeRA@2x

초록색 필터가 이미지를 칸씩(stride=1) 돌아다니면서 곱의 합을 출력하는 것을 있다. 이렇게 출력된 (convolved feature 또는 feature map; 붉은색) 추출된 이미지의 특징이라고 생각하여 feature 사용할 있다.

필터 하나 하나의 feature map 만들어지므로, 필터 n개를 사용하는 CONV layer 출력은 depth=n 갖는 텐서가 된다. 만약 32×32짜리 컬러(depth=3) 이미지 하나가 필터 10개를 사용하는 CONV layer 통과하면 출력 depth 10 된다.

1*BSLjlJf31gj98ABJMCt3-g@2x

수학/신호처리학에서의 cross-correlation

주의해야할 것은 수학에서 정의하는 convolution 연산과는 다른 연산이라는 것이다. 오히려 뉴럴넷에서의 convolution 연산은 수학/신호처리에서의 cross-correlation 같다.

신호처리에서 cross-correlation은 두 신호간의 유사성을 잴 때에 사용되는 연산이라고 한다. 때문에 뉴럴넷에서의 convolution convolution filter input image 사이의 유사성을 측정해서 출력하는 연산이라고 생각할 수 있다. 필터와 이미지의 패턴이 유사하다면 절대값이 큰 값을, 그렇지 않다면 작은 값을 반환하는 것이다.

예를 들어, 아래처럼 우상에서 좌하로 향하는 사선 모양의 convolution filter는 이미지 내에서 사선 모양이 있는 부분(주로 이미지의 윤곽선)을 보면 큰 값을 출력한다. 아래 돌고래 이미지에서 우상좌하 사선 모양의 윤곽선이 도드라지게 추출된 것을 확인할 수 있다.

실제 ConvNet에서는 필터를 우리가 직접 정해주지 않고 알고리즘이 데이터를 바탕으로 문제 해결에 적합한 형태가 되도록 학습해나간다.

Zero padding

이미지에 필터를 적용할 때, 아무리 촘촘히(stride=1) convolution을 해도 출력된 feature map의 가로세로 크기는 원본 이미지에 비해 반드시 작아질 수 밖에 없다. 원본 이미지와 가로세로 크기가 같은 feature map을 얻기 위해서는 필터를 적용하기 전에 입력 이미지에 처리를 해주어야 한다.

1*W2D564Gkad9lj3_6t9I2PA@2x

단순하고도 널리 사용되는 방법이 바로 zero padding, 즉 원본 이미지의 상하좌우를 0값으로 둘러싸는(padding) 방법이다. 아무 정보가 없는 0값으로 이미지를 패딩함으로써 feature map의 가로세로 크기를 인풋과 같게 유지하면서 convolution layer를 깊게 쌓을 수 있다.

 

ReLU layer

CONV layer 통해서 이미지로부터 추출한 feature map에서 양의 값만을 활성화시키는 레이어다. ReLU layer에서는 feature map에서 특히나 두드러지는 특징을 다시 추출한다.

사실 별도의 layer라고 칭하기는 뭣하고, CONV layer activation function으로 ReLU 사용한 것이라 이해하면 쉽다.

output_19_0

ReLU activation 적용 후 우상좌하 사선 모양의 윤곽선이 매우 잘 추출되었다.

 

POOL layer

수많은 이미지를 처리하는 과정에서 필연적으로 맞딱뜨릴 수 밖에 없는 것이 바로 메모리 문제다. 특히나 CONV layer는 원본 이미지의 가로세로 크기를 유지하면서 depth를 수백~수천 수준으로 키우기 때문에 메모리를 많이 먹는 레이어다. 이때문에 이미지와 feature map의 가로세로 크기를, 특징은 유지하되 적절한 수준으로 다운샘플링하여 메모리를 절약할 필요가 있다. 다운샘플링 방법 중 ConvNet에서는 pooling을 사용한다.

가장 빈번히 사용되는 풀링은 max pooling이다. Max pooling pool region 내에서 가장 수치가 위치의 값만을 가져온다.

1*GksqN5XY8HPpIddm5wzm7A

2×2 pool size, stride 2 max pooling 적용한 모습이다. 2×2 pool region 내에서 가장 수치가 값만을 샘플링한 것을 있다.

Weakly-supervised localization 등의 문제를 해결할 때에는 average pooling 사용하기도 한다. Average pooling 해당 pool region 내의 모든 값을 평균내어 가져온다.

돌고래 이미지의 예시에 2×2 max pooling (stride 2) 적용한 결과, 아래와 같이 feature map 특징은 유지하면서 이미지 크기를 1/4 축소할 있었다.

output_20_0

 

ConvNet 일반적인 구조

일반적으로 ConvNet의 구조는 다음과 같이 표현할 수 있다.

\{(CONV + RELU) \times n + POOL \} \times m + (Dense \times k)

여기서 CONV + RELU + POOL feature extractor 역할을 하고 Dense layer classifier 또는 regressor 역할을 한다. 이미지 분류 문제를 예시로 들자면, CONV + RELU + POOL 인풋 이미지로부터 분류에 사용할만한 특징적인 feature 추출하고 Dense layer 추출된 feature 입력받아 이미지를 적당한 카테고리로 분류한다.

이 때 얕은(입력층에 가까운) CONV layer일수록 단순한 특성윤곽선, 음영 등을 추출하고 깊은(출력층에 가까운) CONV layer일수록 복잡한 특성동물 모양, 특정 색상 조합 등을 추출한다.

2012 ILSVRC에서 우승한 AlexNet이 유행시킨 구조라 할 수 있겠다. 딥러닝을 사용해서 효과적으로 이미지를 분석할 수 있다는 것을 보여준 첫 사례다.

1*qyc21qM0oxWEuRaj-XJKcw

그림에서 있듯, 5개의 CONV layer 3개의 dense layer 사용하여 최종적으로 1000 카테고리에 대한 softmax 값을 출력한다. AlexNet 대해서는 이후 기회가 자세히 다루도록 한다.

 

ConvNet 개선하기

1×1 convolution

어느 모형에서나 파라미터 수가 지나치게 많아지는 것은 overfitting 컴퓨팅 속도 저하를 의미하기에 좋지 않다. convolution 연산에 사용되는 파라미터 (필터값 ) 줄이기 위해서 1×1, 1xn 또는 nx1 convolution 사용할 있다.

n \times n \times 3 형태의 인풋 이미지에 receptive field 3×3 되도록, 128개의 필터를 사용해서 convolution 한다고 가정해보자. 단순히 3×3 filter 사용할 수도 있다. 경우 필요한 파라미터 수는 3 \times 3 \times 3 \times 128 = 3456개이다.

반면 1×1 > 1×3 > 3×1 순서로 convolution 한다면? 필요한 파라미터 수는 (3 \times 1 \times 1 \times 128) + (1 \times 1 \times 3 \times 128) + (1 \times 3 \times 1 \times 128) = 384 + 384 + 384 = 1152 뿐이다. 같은 크기의 receptive field 유지하면서 파라미터 수를 거의 1/3 수준으로 줄였다!

inception-v4-fig-5

구글에서 개발한 GoogLeNet에서 바로 이 방법을 활용한 Inception 구조를 사용한다.

Dense layer CONV layer 표현하기

Dense layer CONV layer 차이점은 local connection 뿐이다. Dense layer에서는 이전 레이어의 모든 뉴런이 다음 레이어의 모든 뉴런과 연결되어 있는 반면(그래서 fully connected layer라고도 불린다), CONV layer에서는 이전 레이어의 일부분만이 다음 레이어의 뉴런과 연결되어 있다.

그럼 CONV layer에서 이전 레이어의 모든 뉴런을 다음 레이어와 연결시킨다면? 즉 인풋 이미지의 일부분에만 필터를 적용시키지 않고 이미지 전체에 이미지와 동일한 크기의 필터를 적용시킨다면? Dense layer와 동일한 연산을 하는 CONV layer를 만들 수 있다. 계산해보면 파라미터 수도 일치한다.

스케치

Dense layer CONV layer로 대체함으로써 인풋 이미지 크기를 고정하지 않아도 된다는 이익을 얻을 수 있다. 사전에 정의된 크기의 이미지를 인풋하지 않으면 오류를 뿜는 Dense layer와는 달리, CONV layer는 사전 정의된 것보다 큰 이미지를 인풋해도 오류 없이 연산을 해낸다. (다만 3차원 텐서 형태로 출력하기는 한다)

1*wRkj6lsQ5ckExB5BoYkrZg

Fully Convolutional Network (FCN)이 바로 이 구조를 처음 제안한 모형이다.

특수한 Convolution

외에도 위에서 정의한 것과는 다른, 서로다른 문제에 특화된 특수한 convolution 사용할 수도 있다. 예를 들어 atrous convolution 구멍이 숭숭 뚫린 듯한 모양의 필터를 사용해서 receptive field 더욱 향상시킨다. Deconvolution Convolution 연산을 역으로 수행해 추출된 feature map 원본 이미지에 가까운 형태로 되돌린다.

특수한 convolution 연산들에 대해서는  페이지를 참고하면 좋다.

 

참고