개발자로 후회없는 삶 살기

CV PART.Faster RCNN 본문

[AI]/[네이버 BoostCamp | 학습기록]

CV PART.Faster RCNN

몽이장쥰 2023. 3. 28. 00:59

서론

※ 이 포스트는 다음 강의의 학습이 목표임을 밝힙니다.

https://www.youtube.com/playlist?list=PLpkj8RKr48wZAx6jXEcpOQca5A1yCoNJr 

 

[개정판] 딥러닝 컴퓨터 비전 완벽 가이드

 

www.youtube.com

 

본론

-  faster rcnn의 이해

이제 객체 탐지에 필요한 모델을 다 딥러닝에 넣은 모델이 나왔습니다. 처음에 SS를 통해서 ROI를 구했는데 RPN이라는 딥러닝으로 바꾸고 그 뒤는 Fast RCNN과 비슷합니다. 이제 비로소 딥러닝 만으로 객체 탐지를 하게 됩니다.

 

 

=> 구조

feature map을 구하고 두 군데로 갑니다. 하나는 RPN으로 가서 객체가 있을 만한 곳을 찾습니다. 이 ROI를 ROi pool하고 뒤에는 fast rcnn과 똑같습니다. loss가 rpn으로 가는 end to end 학습이 되게 됩니다. 

 

 

-> 이슈

근데 이슈가 있습니다. SS는 이미지를 딱 넣으면 segment를 해서 ROI가 나옵니다. 데이터로 feature map이라는 픽셀값, Target으로 GT box만 있는데 이걸 딥러닝으로는 ROI를 못 구합니다. 그래서 앵커 박스를 도입합니다.

 

 앵커박스를 셀별로 다 쌓습니다. 처음에는 앵커 박스는 그냥 쓸모없는 박스일 뿐입니다. 근데 이걸로 데이터를 가지고 학습을 하면서 최적의 값을 찾아야합니다. > 처음에 박스는 아무것도 아닌겁니다. 데이터를 가지고 이 앵커 박스를 학습을 시킬 수 있는 네트웤을 만들자는게 RPN입니다.

 

- 앵커 박스 구성

하나의 셀에 9개의 다른 크기의 앵커 박스를 둡니다. 객체의 형태가 다르기 때문에 서로 다른 크기의 앵커 박스를 둔 것입니다. 정사각형 만 앵커박스로 둔다면 길쭉한 사람은 못 잡을 것이기 때문입니다. 또한 앵커 박스가 하나만 있으면 자동차 안에 있는 사람은 못 찾는습니다. 그래서 셀에 중심을 기준으로 9개의 서로 다른 크기의 앵커박스를 뒀습니다. 하나의 객체 안에 다른 비율의 객체가 있어도 객체 탐지를 할 수 있습니다.

 

-> 이미지와 feature map에서 앵커박스 매핑

앵커 박스는 원본 이미지에 한 게 아니고 피쳐맵에 하는 것입니다. featuremap이 60 x 40 x 512라면 60 x 40의 셀이 있을 것이고 각 셀에 앵커 박스가 있습니다. 

 

ex) 앵커박스 시각화

38 x 50의 feature map이 있다면 모든 그리드 셀마다 앵커 박스를 가운데 포인트에 맞춰서 그리게 됩니다. 이걸 모든 셀에 대해 그리면 앵커박스가 막 겹칠 것입니다. 최종적으로는 이런 사이즈로 앵커 박스가 그려집니다.(설명을 위해 이미지인 거지 map입니다.)

 

- RPN 네트웍 구성

 

RPN이 Region Proposal을 하는 과정을 보겠습니다. 40 x 50 x 512의 feature map이 있습니다. 여기에 3 x 3 x 512에 padd을 적용한 conv를 적용해서 사이즈는 안 바뀌는 연산을 합니다. 그렇게 40 x 50 x 512가 그대로 나오고 이거를 1 x 1 conv를 해서 Foreground냐 background냐를 끄짚어냅니다. 여기에 1 x 1을 하면서 sigmoid를 해서 오브젝트인가 아닌가를 바로 뽑아냅니다. (있냐 없냐지 소맥 클래스 다중 분류가 아닙니다. RPN은 있냐 없냐만 나타냅니다. 추천하는 녀석이니깐!) > 그니깐 1 x 1 x 9 x 2의 출력이 나오는 것입니다.

> 1 x 1은 파라미터를 대폭 줄이면서 차원축소를 하는 역할을 합니다. 1 x 1이라서 size는 변하지 않고 depth만 변합니다. depth는 앵커 박스 수입니다. 1 x 1 conv를 해서 9개의 출력을 내게 한 것입니다.

 

-> 코드

인자로 base(피쳐 맵), 앵커 수(9) 를 받아서 3 x 3 x 512의 필터 padding 해서 base에 적용해서 x를 뽑고 이거를 다시 1 x 1을 하는데 이번엔 depth가 9이고 활성화함수가 바로 sig입니다. sig로 바로 이진 분류로 FG냐 BG냐를 구한다는 건데 3 x 3를 한 거에 1 x 1을 하면 40 x 50 x 9가 됩니다. 9개가 뭐를 의미하냐면 이전에 그리드에 앵커가 9개가 있다고 했습니다. 

 

> 40 x 50 x 9만큼의 앵커가 있는 것이고 feature map 입력 크기와 40 x 50 x 9의 크기가 같다는 건 40 x 50 셀의 각 9개의 앵커박스가 있다는 것이고 40 x 50 x 9가 18000개의 앵커 박스별로 개별적으로 BG냐 FG냐를 판단을 하는 것입니다. 

그리고 이번에는 actv를 linear로 회귀를 하는데 40 x 50의 셀에 4 x 9로 9개의 앵커 박스의 bbox를 나타냅니다. 이거로 reg을 다 돌려버리는 것입니다.

 

- RPN bbox reg

rpn에서 있을 만한 위치를 찾아내는 식입니다. RPN은 cls와 bbox를 한다고 했으니 입력이 들어오면 40 x 50 x 9로 각 셀의 9개의 앵커 박스가 BG냐 FG냐를 구하는 것이고 reg로 앵커박스의 bbox를 구합니다. (질문 : 아니 앵커 박스가 이미 있는데 bbox reg를 왜 하나요? -> 앵커 박스와 대응하여 GT와의 차이를 동일시하기 위해서입니다.)

> bbox는 fast rcnn bbox reg과 같습니다. 원래 SS의 자리에 앵커 박스가 있고 그게 P입니다. 예측 박스가 있고 그게 G다 GT가 아닙니다. g햇을 GT를 예측하는 그런 개념으로 본다면 앵커 박스 w에 dx 값을 곱한 값에 px를 더합니다. px에서 Gx로 가기 위해서 그 거리의 차가 있을 것이고 그 거리의 차이를 pw x dx라고 한 것이고 이게 '예측되는 차이'이고 실제로 찾으려는 값이 dx입니다.

 

 

정확히 얘기해보자면 우리가 찾는 것은 G가 아니고 dx입니다. 거리차가 얼마냐 되느냐로 앵커 박스를 레퍼런스로 이용해서 실제 GT(G 아님)와 예측 박스의 차이와 앵커 박스와 GT와의 차이를 동일하게 가져갈 수 있는 것이 목적입니다.

> 앵커박스와 GT와의 차이만큼 GT와 에측 박스의 차이를 동일하게 가져갈 수 있다면 똑같은 거 아니야!!입니다. 그 차이가 pw x dp로 예측값 px에 더해져서 좋은 예측값을 찾아가게 되는 것입니다.

> 그러면 실제 차이(T)는 Gx가 GT x이고 Px가 앵커 박스로 그걸 pw로 나눈 값입니다. 즉, GT와 앵커 박스의 차이(A)가 실제 차이이다. GT와 예측 박스의 차이(B)가 목적 차이입니다. 우리는 A와 B가 동일시되도록 하는 것이 목적입니다.

 

 

=> pos neg 앵커 박스

위의 GT와 예측 박스의 차이(B)가 목적 차이입니다. 우리는 A와 B가 동일시되도록 하는 것이 목적입니다. 하지만 모든 앵커 박스에 적용하지는 않습니다. pos는 앵커 박스와 가장 높은 앵커 박스, 0.7이상은 pos이고 0.3이하는 neg이고 0.3~0.7은 제외입니다. 이 pos와 neg를 가지고만 학습을 시킵니다. neg는 부정이니 loss에 포함을 시키기 위해 학습에 사용합니다.

> 이것과 앞의 내용을 연결해보자면 pos 앵커박스에만 bbox reg이 적용이 됩니다. 

 

pos 앵커 박스가 있을 때 pos 앵커박스와 GT와의 차이가 예측과 GT와의 차이가 동일하게 되야한다고 학습을 합니다. 왜 이 차이가 동일하게 되면 좋을까요? 레퍼런스라고 말했는데 가령  GT와 POS 앵커만큼의 차이가 있다면 우리가 예측하는 예측박스와 POS 앵커박스의 차이가 있을 겁니다. 그러면 이 두 차이가 완벽하게 동일해진다면 우리의 예측이 GT와 비슷하게 될 것입니다.

> 사실 GT와 POS 앵커 박스의 차이는 고정값입니다. 그 좌표 차이를 계산하는 것이고 그 좌표 차이와 최대한 비슷하게 되도록 reg을 학습하는 것입니다.

 

- 앵커 박스의 출력

cls와 bbox가 나옵니다. 이게 obj이냐 아니냐는 .9면 obj야 0.4면 obj가 아니야를 나옵니다. 

 

- loss 함수

1) i는 앵커이다. 0 1 2 3 4 5 6 7 8번 앵커
2) pi는 앵커 i가 obj일 확률
3) pi햇은 앵커 i가 GT obj일 여부로 POS를 1 nes를 0

 

1) ti는 앵커 i와 예측 좌표와의 차이
2) ti햇은 앵커 i와 GT와의 좌표 차이
3) ti - ti햇은 RPN의 bbox reg로 스무스 L1 loss를 합니다. 앞에 Pi햇이 붙으니 POS 앵커만 reg를 적용하겠다는 것입니다.

 

obj 유무 cls는 pos, neg 앵커 박스를 다 씁니다. 그리고 N으로 loss가 너무 커지는 것을 막기 위해 정규화를 합니다. 근데 reg가 아무리 작아져도 cls 로스가 커지면 답이 없습니다. 근데 cls에 obj보다 obj 아닌게 당연히 더 많습니다. 이미지에 back이 더 많습니다. 그래서 벨런스를 맞추기 위해 미니 배치 샘플링으로 pos와 neg 개수를 맞췄다고 합니다.

 

- 학습

RPN 학습하고 Faster RCNN 학습하고 번갈아서 학습합니다. 일단 RPN부터 학습하고 뒷 부분을 학습합니다. 뒷부분 loss를 가지고 다시 RPN을 fine tuning합니다. 

 

-> 구현한 소스코드 분석

train_frcnn을 보면 faster rcnn 학습코드가 있습니다.  model_rpn이 있는데 어떻게 학습을 시켰냐면 처음에 X, Y에 앵커에 대한 좌표, 레이블 등이 다 들어오고 들어오면 batch로 학습을 돌립니다. predict_on_batch를 가지고 RPN을 예측합니다. 여기서 obj에 대한 위치를 잡습니다.

 

 

그걸 bbox reg을 하기 위해 pos, neg 샘플을 구합니다. 그 다음에 cls(Faster RCNN)를 학습합니다. 이런식으로 RPN 학습하고 cls 학습하고 이를 반복하는 것입니다.

Comments