[CNN] CNN 구현 완료한듯

이번주는 나름 간섭 덜 받고 개인적인 시간을 가질 수 있었기 때문에
초 집중해서 CNN을 구현했다.

일단 CNN 구조를 보니 Input data와 MLP 사이에 Convolution layer만 끼워넣으면 될 것 같아서 지난번에 완성한 MLP를 재활용 하였다.
(사실 그냥 CNN만을 가지고 size-1 filter로 Fully-connected 부분 충분히 구현 가능하다... 지금 쓰면서 깨달은 사실이지만; ㅜ)

CNN을 짜면서 가장 싫었던 점이 몇 가지가 있는데

1. Machine learning 분야에서 conventional하게 쓰는 dimension 순서를 잘 지켜야한다.

기존 MLP는

Input dimension (MLP): # nodes x # batch
Filter dimension (MLP): # next-layer nodes x # batch

의 2차원이었기 때문에 dimension이 헷갈릴 일이 별로 없었던 것 같다.

하지만 CNN으로 넘어오면 input dimension이랑 filter dimension이 4차원이 되는데 이게 machine learning 분야에서는 보통

Input dimension (CNN): # batch x # height x # width x # channel
Filter dimension (CNN): # height x # width x # channel_in x # channel_out

로 사용하는 것 같다.

작년에 내가 개인적으로 python을 만지작 만지작 거렸을 때가 있었는데 3차원 이상인 변수를 print 하면 맨 끝 두 dimension을 2차원 matrix로 보여줬었던 기억이 있다.
이게 나의 모국어(?)라고 할 수 있는 MATLAB이랑 좀 반대인데, MATLAB 같은 경우에는 3차원 이상인 변수를 print하면 일단 맨 앞 두 dimension을 2차원 matrix로 보여준다.
따라서 나중에라도 CNN training/testing 중에 print 할 필요성이 있을지도 모른다는 생각에 나는 machine learning 분야의 convention을 따르지 않고 # batch x # channel x # height x # width로 코딩을 했다.
또한 filter의 dimension도 내 입맛대로 # channel_out x # channel_in x # height x # width로 코딩을 진행했다.

그런데 tf.nn.conv2d 등의 built-in function들을 사용하려다보니 argument에 넣어줄 때 항상 transpose를 해야하는 번거로움이 생겼다.
물론 transpose를 하지 않고 function argument에서 "NHWC (# batch x # height x # width x # channel" 방식을 "NCHW (# batch x # channel x # height x # width)"로 하겠다고 설정할 수도 있지만 filter의 경우는 반드시 # height x # width x # channel_in x # channel_out을 지켜줘야만 하는 것 같았고 기타 예상치 못한 부분에서도 transpose 하는 것이 상당히 귀찮고 쓸 데 없이 코드가 더러워졌다.

끝내 나는 나의 고집을 접고 machine learning의 convention을 따르게 되었다. ㅜ

2. CNN에서 bias (b) 와 Batch normalization constants들은 channel-wise.

이것 때문에 조금 고민을 많이 하고 search도 해봤는데 일단 결론은 보통 이렇게 진행하여야 하는 것 같다.




참고로 Batch normalization을 사용할 때 기존 bias는 굳이 필요하지 않지만 나는 그냥 넣었다. (별 다른 이유는 없다. 기존 내 코드에서 삭제하기가 귀찮았을 뿐)
필요하지 않은 이유는 Batch normalization의 beta들이 bias의 역할들을 대신 해주기 때문.

아무튼 여기서 말하고 싶었던 것은 먼저 bias는 channel 당 하나의 값으로 설정하는 것.
즉 각 layer에서 initialize 해야하는 bias의 dimension은

bias dimension: [해당 layer의 # channel_out x 1]

로 설정하면 된다는 말이다.
그럼 나중에 convolution layer을 거친 Z에 더해질 때는 broadcast 되어서 더해지게 된다. (위 그림 참조)
사실 100% 확신은 안드는데 구글링을 몇 번 해보니까 bias는 이렇게 channel-wise로 적용해주는 사례가 많은 것 같다.

Batch normalization도 마찬가지다.
convolution layer를 거친 Z의 moments (평균과 분산) 의 shape도 보통 channel-wise로 뽑아내는 경우가 많은 것 같다.
이 말은 moments를 구할 때 batch, height, width에 대해 계산하여

moments dimension: [해당 layer의 # channel_out x 1]

를 만들어야한다는 말이다.
channel 별로 moments를 구할 것.


마지막으로 지난번 MLP에서 사용했던 "Signs" image data set을 가지고 실험한 결과 몇 가지를 써본다.
가장 간단하게 LeNet-5 모양을 좀 가져왔는데 input dimension이 좀 맞지 않아 자칭 'input-dimension-adapation' convolution layer를 앞에 넣어줬다.
LeNet-5는 32 x 32 input을 가정한 architecture이기 때문에 이를 맞춰주기 위해 batch의 channel_in은 그대로 유지하되 height와 width가 32 x 32로 되는 filter를 적용시켜줬다.
그리고 마지막에 Fully-connected는 일단 내 맘대로 258, 128로 해봤다.

Data set:
- "Signs"

Model parameters:
- Minibatch = 1024
- Epochs = 200, 400
- Learning rate = 0.0001
- Optimizer = Adam
- 3-layer CNN with {'Input-adaptation-layer', LeNet-5's 2 convolution layers}
- 3-layer MLP with {258 ReLU, 128 ReLU, 6 Softmax}

1. BN_conv: False, BN_feed: False

| Epoch 199 | cost=0.531891 | acc_train=81.944444 | acc_val=67.500000 |
Total elapsed time is  68.64218747545397 sec
| Epoch 399 | cost=0.100913 | acc_train=98.148148 | acc_val=74.166667 |
Total elapsed time is  129.82077073192315 sec

2. BN_conv: False, BN_feed: True

| Epoch 199 | cost=0.547317 | acc_train=94.722222 | acc_val=65.000000 |
Total elapsed time is  71.85675950959545 sec
| Epoch 399 | cost=0.346352 | acc_train=100.000000 | acc_val=70.833333 |
Total elapsed time is  136.3215634186229 sec

3. BN_conv: True, BN_feed: False

| Epoch 199 | cost=0.174747 | acc_train=97.037037 | acc_val=69.166667 |
Total elapsed time is  81.29189465426197 sec
| Epoch 399 | cost=0.038457 | acc_train=99.814815 | acc_val=73.333333 |
Total elapsed time is  155.29268397464816 sec

4. BN_conv: True, BN_feed: True

| Epoch 199 | cost=0.537950 | acc_train=95.925926 | acc_val=66.666667 |
Total elapsed time is  86.35207472318483 sec
| Epoch 399 | cost=0.386235 | acc_train=99.629630 | acc_val=68.333333 |
Total elapsed time is  158.88546037864938 sec

일단 learning은 어느 정도 잘 되는 것 같다.
다행히도.
(단순하지만 cost가 줄어들고 accuracy가 올라가면 된 것이 아닐까..)

지난번에 실험했던 결과를 다시 보니 MLP {1024 ReLU, 1024 ReLU, 6 Softmax}에 Batch normalization을 적용하지 않았을 때는 learning이 거의 안됐었다.
하지만 이번 CNN으로 learning 해보니 Batch normalization을 사용하지 않고도 (1번 실험) 충분히 어느 정도의 learning이 이루어진다는 것을 확인할 수 있었다.
다만 training data에 overfit된 것 같은데 2, 3, 4번 실험 처럼 Batch normalization을 여러 방법으로 입혀도 잡히지가 않았다.
Dropout을 Fully-connected 부분에 좀 넣어줘야 하는걸지도 모르겠다.

추후에 좀 여러 parameter tuning을 통해 한번 test data에 대한 accuracy도 끌어올릴 수 있도록 해야겠다.

Comments

Post a Comment