7. Pracical Usage of Convolution Layer - 3,3filter / zero padding/ strides / trade off / 1x1 C. Layer
저번시간 까지...
저번시간에는 Convolution Filter(or kernel)이 왜 3*3 이 best사이즈인지 몰랐다...
Convolutional Filter의 size ? why (3,3)?
이미지를 C.N.N. 에 집어넣으면, fully-connected N.N.(기본 Single- / Mult-Layer N.N. 혹은 D.N.N.)보다 더 효율적으로 연산할 수 있었다.
효율적이라는 것은 locally-connedced한 이미지의 특성을 살림으로써, 거리상 멀리있는 pixel을 연산할 필요가 없었다.
weight도 모든 input feature에 대해서 하나하나 다 생성된 fully-connected에 비해,
공유된 shared weight가 돌아다니면서 연산하기 때문에 weight양도 효율적인 것을 알 수 있었다.
이 Convolutional N.N.이 실제로는 다양한 방식으로 조합할 수 있다.
세로*가로사이즈는 몇 pixel이 좋으며, 등등이다.
이번에는 그러한 여러 옵션 중에 어느 것이 best option인지 한번 알아보자.
C.N.N.은 세로*가로개념이 생겼고, Filter도 마찬가지였다.
그 중 Convoluation Layer (= Convolutional Filter 의 묶음 )의 best사이즈는 3*3이다. 그 이유에 대해서는 아래를 살펴보자.
14년도 VGGNet과 GoogLeNet의 경쟁이었다. image-net challenge에서 우승은 GoogLeNet이 했지만, 실용적이고 더 연구가 많이 되는 것은 VGGNet이었다. VGGNet의 개념이 나오기 전까지는 Convolutional Filter가 크면 큰 ouput feature를 뽑아내고, 작으면 작은 feature를 뽑아낸다고 생각해왔다. 그러나 이러한 개념을 깬 것이 VGGNet이었다.
VGGNet의 개념은 작은 필터(3*3)을 쓰더라도, Layer를 겹쳐서 여러개의 필터를 거치면, 큰 feature를 뽑아낼 수 있다는 것이다.
즉, 이전까지는 5,5의 큰 input feature는 5,5의 큰 filter 하나만 써야지 외곽pixel들의 관계도 연산이 가능한다고 했었지만,
VGGNet에서는 5,5하나도 가능하지만 3,3Filter를 2개 Layer에 걸쳐 결과적으로 외곽pixel들의 관계도 연산이 된다고 한 것이다.
결론적으로, Convolutional Filter에서는 5,5 Filter 1개나 3,3 Filter의 Layer를 2개 겹쳐서 쓰는 것과 커버하는 영역이 똑같다.
그렇다면 5,5 Filter 1개 쓰는게 좋냐? 3,3 Filter 2개 쓰는게 좋냐? 를 물어본다면 결과적으로 3,3 Filter 2개를 쓰는게 더 좋다.
왜냐하면 weight의 개수와 메모리의 관점에서 5 x 5 Filter의 weight의 개수는 총 25개이다.
3 x3 Filter에서는 weight의 개수가 9개이며 2개를 쓴다면 총 18개의 weight를 가진다.
같은 성능을 낸다면, weight가 적어야지 --> 그만큼 연산량도 작아지고, 메모리 효율도 좋아진다.
결론적으로 Convolutional Filter의 Best 세로*가로는 3*3인 것이다.
저번시간에 ZFNet은 각 C. layer별로 Filter들을 시각화 할 수있다고 하였다.
1번째 Layer의 필터들에서는 대게 선형의 feautre을 뽑아내지만,
2번째 Layer의 필터들에서는 원형의 feature, 그리고 3번째 Layer의 필터들은 디테일한 feature들을 뽑아내는 것을 확인할 수 있다.
즉, Convolutional Layer가 늘어날 수록, 뒤에 있는 Layer가 좀 더 고차원적인 feature를 뽑아내는데,
이것은 5,5 fillter 하나쓰는 것보다, 3,3filter를 2개의 Layer로 연산하는 것이 더 성능이 좋다고 할 수 있는 것이다.
5,5 filter 하나쓰는 것보다 3,3 filter로 2개의 Layer로 겹쳐쓰는 것이
1) weight 더 적어서 --> 메모리효율 높이고, 실행속도 높다
2) 시각화해보니 Layer가 많을 수록 더 고차원적으로 뽑아내니 --> 더 고차원적인 feature를 뽑아낸다.
이것이 VGGNet의 결론이다.
결론 : keras에서도 Conv2D의 kernel에 (3,3)을 입력해주면 된다.
Convolution 연산의 단점과 극복방안 : zero padding
convolution연산의 단점은 1,1 연산을 하지 않는 이상, input feature의 size보다 output feature의 size가 작아진다는 것이다.
계속 convolution연산하다보면, 계속 작아지니까 결국 연산이 불가능해지지 않을까 걱정될 수도 있다.
이 때, input과 output의 size를 맞춰주는 것이 zero padding이다.
input의 외곽쪽에다가 모두 0인 padding을 1pixel씩 씌워준다.
input5,5를 3,3 convolution연산하게 되면 output이 3,3으로 작아졌었다. 하지만, 5,5에다가 zero padding을 씌우는 순간
3,3 filter가 가로방향으로 3번만 연산할 수 있었던 것이 5번이 가능해진다.
결과적으로 output이 3,3이 아니라 5,5가 된다.
input과 output의 size를 맞춰주기 위해서는 zero paading을 씌면 되는 것이다.
그 결과 더 작아져서 못쌓았던 Layer를 더 쌓을 수 있게 된다.
실제로 keras에서 사용하기 위해서는 Conv2D( Convolutional Layer)의 옵션으로 padding =인자에 옵션을 넣어주면 된다.
이 때, 2가지 옵션이 있는데, 'same'과 'valid'다. padding='same'옵션이 zero padding이니 이것만 사용하면 된다.
Convolutional Layer를 추가할 때마다 추가해줘야한다.
- Convolutional Layer를 넣는데, filter수 = output_filter_size= output feature의 개수는 64로 지정
- kernel_size = filter의 세로가로사이즈 (3,3)으로 best고정
- padding='same'으로 zeropadding씌움 (layer마다 게속 지정해줘야한다)
이 때, zero padding의 부가효과로서, 최외곽(좌상단 or 우하단 등)에 위치한 중요 feature에 대해서, zero padding으로 연산횟수가 늘어남에 따라 여러번 연산이 되어이 뽑힐 가능성이 높아질 것이다 .
즉, 어떤 이미지에서 중요feature가 외곽쪽에 위치하고 있을 때, 그것을 찾을 확률이 높아진다.
좀 더 좋은 효과가 있지만, 그렇게 큰 효과가 있진 않다.(왜냐하면 중요
결론2 : keras에서도 Conv2D의 kernel에 (3,3)을 + padding='same'을 넣어주면 된다.
strides - Filter가 움직일 때, 몇칸씩 뛰면서 움직이냐? (Pooling의 경우 자기 크기만큼 strides)
Filter는 왼쪽에서 오른쪽 / 위에서 아래로 sliding window를 한다고 했다. 보통은 1pixel씩 움직인다고 했다.
만약, 1칸 뛰던 것을 2칸 뛰게 된다면, 연산이 그만큼 줄어들어서, 좀 더 디테일한 feature를 뽑아낼 수 없게 된다.
그러므로 Convolutional Layer 속 Filter의 strides는 1이 best이다. 그래야 촘촘하게 연산할 수 있다.
우리가 keras를 사용하면서 strides 옵션을 줄 때, 1만 주면 된다.
예외적으로 strides 2를 주는 경우가 있다. 바로 Max Pooling을 대체할 때 쓴다.
실무, 연구쪽에서는 맥스풀링을 안쓰려고 하는 경향이 있다.
왜냐하면 Filter의 strides를 2칸씩 뛰면, 연산횟수가 줄어드는데 ( Filter 2,2일때 정확히 반으로 줌) 맥스풀링을 대체할 수 있다.
그런데, 맥스풀링 대신 Conv2D를 사용하면,
장점) Conv Layer의 strides1로 추가하는 것보다는 연산횟수는 줄어드는 반면에
Maxpooling보다 좀 더 디테일하게 feature를 뽑아냄
단점) 맥스풀링을 버리는 입장에서는,없던 Conv Layer추가로인해 그만큼 filter가 생겨나 weight의 전체 수도 증가해버린다.
맥스풀링은 필요없는 feature를 버리는 개념이었다. 예를 들어서, 고양이로 분류하는 문제에서는 고양이의 귀나 꼬리모양 등이 중요feature이고 대부분은 필요없는 feature로서 작용하기 때문에, 더 효율적일 수 있다.
그러나 다른 예를 들어보자.
이미지를 집어넣고, 유사한 고양이를 생성하는 이미지 generation문제에서는 맥스풀링이 문제가 될 수 있다.
예를 들어서, 고양이의 중요feature는 아니지만, 털같은 디테일한 것까지 버려버리면, 고양이와 유사한 이미지를 generate못하게 된다.
즉, 이미지를 제네레이터하는 문제에서는 필요없다고 feature를 버리는 개념은 말이 안될 수 있는 것이다.
결론적으로, 이미지 제네레이터문제에서는 MaxPooling대신 Conv2D의 stride(2,2)옵션으로 해야한다.
결론3 :
Conv2D의 kernel에 (3,3)을 + padding='same' + strides(1,1)을 넣어주자.
이미지를 분류할 때는, 맥스풀링을 써도 되지만 /
이미지 생성할때만, 맥스풀링 금지 -> strides(2,2)인 Conv2D로 대체하자.
Trade off - C. filter를 늘리느냐? 아니면 C. Layer를 늘리느냐?
하드웨어(리소스)가 제한적이기 때문에, 무한정으로 filter나 Layer를 늘릴 수 없다. 이 때, trade off가 필요하다.
결과적으로 Layer를 깊게 쌓는 것보다, 적당한 Layer를 쌓은 상태에서는 Filter를 더 늘리는 것이 더 좋을 수 있다.
152개의 Layer를 가진 ResNet보다 50개의 Layer + Filter수를 늘린 Wide ResNet이 성능이 좋았다는 결과 때문이다.
결과적으로, 어떤 base모델이 있다고 가정하면, Layer를 1/2로 줄이는 한이 있더라도 Filter수를 2배더 가져가는 것이 더 성능이 좋아진다.
결론4 :
Conv2D로 Convolutional Layer를 짤 때,
kernel에 (3,3) + padding='same' + strides(1,1) + Layer를 줄이더라도 Filter를 늘리는 전략
이미지를 분류할 때는, 맥스풀링을 써도 되지만 / 이미지 생성할때만, 맥스풀링 금지 -> strides(2,2)인 Conv2D로 대체하자.
1X1 (Filter를 가진 ) Convolutional Layer 의 3가지 장점
이미지는 기본적으로 locally-connected라는 현상이 있다. 하나의 픽셀이 주변 픽셀과의 연관관계로 feature를 뽑아낸다고 했다.
이러한 locally-connected현상을 연산하는데 안성맞춤인 N.N.가 바로 Convolutional Layer이다. 왜냐하면 똑같이 주변픽셀과 연산을 하므로
예를 들어, 3,3 C. layer는 각 필터들이, 3,3 중 가운데 1 pixel <-> 주변 8 pixel과의 관계를 연산한다.
그런면에서 보면, 1,1 Convolutional Layer은 필요없는 것 같아보인다.
주변픽셀과 연산을 하지 않으므로, 일반적으로 우리가 생각하는 Convolution연산과는 거리가 있다.
이러한 1 X 1 Convolution 연산은 어떤 곳에 쓰일까?
먼저, 딥러닝모델이 설정된 상황에서, 특정 Conv Layer에 (3,3) filter의 사이즈가 64라고 가정해보자.
여기에 bottle neck을 써볼 것이다.
1. 먼저, input 되는 filter size( 이전 Layer의 output filter개수)를 기존 것(64)의 4배를 늘린다.
( 참고 : 이전레이어의 output filter개수 = input되는 filter_size -> 그것과 동일한 input_filer_size의 filter가 연산후 1로 나옴
- > 1로 연산된 filter들의 총 개수 = output filter_size = Conv. Layer)
여기까지 다합치면 C. Layer)
2. input_filter_size를 4배 늘어난 256로 맞춰서 연산이 되도록해주고
(1,1)filter로 연산하는데, output filter( 1로 연산된 filter들의 총 개수)를 다시 64로 넣어준다.
3. 그다음 C. layer에서는 (3,3)filter으로 연산하도록 하고, output_filter의 갯수를 64개로 넣어준다.
4. 다시 마지막 C. layer (1,1)filter로 output_filter가 256개로 넣어준다.
Bottle neck 전략이란?
1. 256개의 input filter를 64개가 나오도록 (1,1)Filter로 압축(encoding)시킨다(임의로 64개 필터로 조정)
2. 중간에서 (3,3)연산해준다.
3. 마지막에 64개의 input filter가 256개가 나오도록 (1,1)Filter로 뿔려(decoding)시킨다.(임으로 256개 필터로 조정)
> 그림의 왼쪽모델에 비해, (1,1) filter를 이용한 Conv. Layer의 bottle neck 전략은
input_filter를 4배 더 많이 넣을 수 있다. & 연산도 더 효율적이다(weight갯수가 더 작다.)
참고 Conv.Layer weight개수 = input_filter_size(64) * filter size( 3*3) * output_filter_size(64)
shared filter는 (3,3)으로 총 3*3 = 9개이다. 그리고 그 필터개수인 output_filter_size를 곱해준다.
3*3 = 9 * 64
여기서 input_filter_size(intput feature의 갯수라 생각)마다 이 필터가 연산을 하므로,
64개의 각각의 input feature에 대해서
9개의 shared feature가 하나의 필터당 weight갯수고
그 필터가 총 64개(output filter_size)있으니
64* 3*3 * 64개이다.
64*3*3*64 + 64*3*3*64 = weight 73,728 개
>> 256*1*1*64 + 64*3*3*64 + 64*1*1*256 = weight69,632개
2번째 (1,1) Conv 연산의 장점은 fully-connected를 대체할 수 있다는 점이다.
아래는 VGGNet 모델이며, 마지막에는 fully-connected로 계산하는 부분이 있다.
이 VGGNet의 마지막 Conv. Layer에서 --> Fully-connected Layer로 넘어갈 때, output되는 weight수가 순식간에 늘어난다.
VGG모델에서는
앞의 모든 Conv Layer를 다 합친 weight의 수보다 << 첫번째 Fully-connected Layer의 weight가 더 많다.
그래서, 4096짜리 FC layer 대신에 (1,1) Conv Layer로 대체하고 output_filter개수만 4096개로 조정해주면 된다.
input필터의 개수가 512개이므로, output filter 개수는 4096개로, 총 weight개수는 (1*1*512*4096)개 이지만,
FC Layer의 weight개수(7,7,512, 4096)보다 적다.
그래서 FC layer를 (1,1)Conv Layer로 대체하고, weight줄어든 만큼 filter를 늘리거나 layer를 더 쌓는 trade off를 할 수 있다.
마지막 장점은 Dense전략으로서,
fully-connected N.N. = M.L.N.N. 는 input 이미지의 size가 정확히 모델과 일치해야하는 단점을 가지고 있었다.
(MNIST 28*28=784가 정확히 일치해야 들어간다)
그러나 Conv. Layer는 input사이즈는 딱히 중요하지 않다.
대신 input이미지의 사이즈가 줄면 filter를 거쳐 output이미지의 사이즈가 더 줄어들 뿐이다.
VGG모델은 보통 세로*가로 (244, 244) 이미지가 들어간다.
만약 full-connected라면 정확히 244*244 이미지를 안넣어주면--> Conv Layer까지는 연산이 되지만,
정해진 사이즈가 안들어와서 정해진 input이 안들어온 Fully-connected Layer에서는 에러가 날 것이다.
Conv Layer는 인풋이미지의 사이즈와 상관없이 연산이 쭈욱 일어난다. 다만, output사이즈만 달라질 것이다.
예를 들어, train할 때 Fully-connected Layer를 (1,1)Conv L.로 대체했다고 해보자.
input을 (14,14) 로 넣고 (5,5)Conv (10,10 ) -> (2,2) Pooling (5,5) -> (5,5)Conv (1,1) -> (1,1)Conv (1,1)Conv (1,1)Conv -> (1,1)
으로 output (1,1)이 하나 생겨 이것을 가지고 고양인지 개인지 구분할 것이다.
만약 이미지사이즈를 키워서 input을 (16,16)으로 넣어줬다면??
-> 마지막이 Fully-connected였다면 마지막에 에러가 날 것이다.
-> 마지막을 (1,1)Conv Layer로 대체했다면? 에러없이 output이 (2,2)로 좀 더 커질 뿐이다.
AlexNet에서는
이미지(224,224)를 정확한 하나를 넣어주는 것보다.
이미지를 argumentation하여
1) 사이즈를 약간 키워준 뒤,(224,244->256,256)
2) 다시 좌상단 + 우상단 + 좌하단 + 우하단 + 가운데(244,224) 로 Crop한 뒤
5) 5개의 (224,224)를 좌/우반전 시켜 총 10개(224, 224) 넣어주는게 더 성능이 좋았다고 했다.
> 1개가 10개가 되니, test할 때,속도가 10배가 느려지는 단점이 있다.
> 그럴바에는 조금 더 큰 이미지가 들어갈 수 있게
FC Layer -> (1,1) Conv Layer로 대체하고 + 약간 큰 사이즈의 이미지를 넣어주어서,
5개크롭/좌우반전한 효과대신 sliding window방식으로 연산한 결과가 더 좋다.
output사이즈가 좀 더 크게 나온 것은 평균내면 된다.
그러면 (224, 224)원본이미지 맞춰서 넣는 거보다 (256,256)좀더 큰이미지를 넣은다음 평균내는게 더 성능이 더 좋다.
결론 : (1,1) Conv Layer를 쓰는 장점 3가지
1. bottle neck전략 : (3,3) Conv L. 2개쓰는 것보다 (1,1)encoding (3,3) (1,1)decoding Conv. Layer 3개 쓰는 것이 성능이 더 좋다.
2. fully-connected L. 를 (1,1)Conv. Layer로 대체하면 weight수가 줄어 메모리 효율적
3. fully-connected L. 전부 (1,1)Conv. Layer로 대체하면, input이미지의 사이즈가 자유로워져 ->
(train시에는 모델에 맞는 input이미지 크기로 학습)
test시에 좀 더 큰 이미지를 넣어 (2,2)결과가 나온 것을 평균내주면, 똑같은 train모델에 대해서 더 좋은 성능을 가진다.