뭔가 이상합니다
들어가기 앞서.
- 이 글은 먼저 YOLO 및 YOLO v2까지 논문을 보시고 나서 읽어야 합니다.
- YOLO 논문에서는 어떻게(How) 작동하는가에 대해서는 열심히 설명하고 있습니다. Grid로 나누어 object의 center가 떨어지는 cell이 object를 detect하는 방식으로 한다, 이런 것들 말이죠. 그런데 왜(Why) grid로 나누는 것이 object detection 분야에서 중요한지에 대해서는 설명하지 않습니다. 개인적으로 저는 이분의 블로그에서 YOLO의 본질에 대해 자세히 이해할 수 있었습니다. grid-wise하게 detection하는 것이 일종의 constraints가 되어 각각의 detector가 특화될 수 있으며, 이것이 object detection에서 굉장히 중요한 부분이라는 것, 그 외에도 논문에서 말하는 "responsible"에 대한 보다 자세한 설명까지 있습니다. 위의 블로그 내용이 굉장히 긴 문서지만 YOLO를 공부하신다면 일독을 강력 추천합니다.
오늘 제가 다루고자 하는 주제는 YOLO의 loss function입니다. 석사 1년 차 때 YOLO를 볼 때에는 대충 그런갑다하고 넘어갔는데, 올해 2년 차가 되고 수식을 자세히 보니까 뭔가 이상하다고 느껴졌습니다. 저기 있는 indicator function은 왜 이렇게 헷갈리고 의미가 뭐지? i와 j는 의미가 뭐더라? 하는 기초적인 질문부터, YOLO v2에서는 loss function을 안 적어주던데 어떻게 구성되나? 확률 값에서 loss를 만드는데 SSE를 써도 되나? 하는 좀 깊이 있는 의문까지 이번 글에서 다뤄볼까 합니다.
먼저 localization loss입니다. 이건 그럭저럭 이해가 됩니다. x, y, w, h를 regression하고자 한다, SSE를 쓴다, 하는 것들이죠. w나 h는 root를 취해서 계산하는 게 좋다는데, 뭐 그렇다고 칩시다.. 여기서 i는 cell의 index이고, j는 bounding box predictor(이다음부터는 detector라고 부르겠습니다)의 index입니다.
우선 저놈의 indicator function이 거슬립니다.
obj라고 적혀있으니까 뭔가 object와 관련이 있을 것 같은데, 이건 사실 논문에서 말하는 "responsible"이냐 아니냐를 나타내는 놈입니다. 이놈은 i번째 cell 안의 2개(오리지날 YOLO는 2개, YOLO v2에서는 5개입니다) detector 중에서 j번째 detector가 responsible이면 1이고, not responsible이면 0입니다. 즉 responsible을 정의하는 데에는 object와 detector 모두 관여합니다. 정확히는, 1) object의 center가 속하는 i번째 cell이면서, 2) 해당 cell의 detector 2개 중 object와 가장 높은 IoU를 갖는 1개 detector가 responsible이 됩니다. responsible은 YOLO에서 굉장히 중요한 개념이며, 보다 자세히 설명은 처음에 언급한 이분의 블로그를 꼭 꼭 읽으세요.
그런갑다하고 넘어갔는데, 가만 보면 이 녀석도 이상합니다. 앞서 언급한 것과 같이, i번째 cell의 j번째 detector가 responsible이면 localization loss를 적용합니다. 여기서 ground truth box의 x값인 x_i가 i로만 index되고, j로는 index되지 않았습니다. cell의 index인 i로 index되었기 때문에 x_i 값은 딱 cell 개수만큼 있습니다. 즉 x_i는 cell 내에서는 1개로 유일하며, cell 내에서 multiple하지 않습니다. 같은 cell 내에서는 x값이 공유된다는 것이죠. cell마다 detector가 2개씩 있지만, 실제로 cell 내에서는 최대 1개의 object만 detection하고자 하는 것입니다.
확실히 이상합니다. 만약 x_i라는 표현을 사용하고자 한다면, cell에 2개 이상의 object center가 속하지 않는다는 가정을 해야 합니다. 즉 ground truth box의 center가 떨어지는 cell은 object마다 반드시 서로 달라야 합니다. 그런데 small object가 많아서 cell 안에 2개의 ground truth box가 있었다면? cell 내에서 1개의 x_i만 가질 수 있으므로 풀지 못합니다. cell 안에서 2개 이상의 object를 표현하기 위해서는,
가 되어야 합니다. 사실 이렇게 i와 j로 index한다면 1개 cell 내에서 detector 개수까지 ground truth box를 커버할 수 있습니다. 그게 더 자연스럽죠.
추측입니다만, 제 생각에는 오리지날 YOLO에서는 cell 내에서 1개의 object만 detect하는 것을 의도하려고 했던 것 같습니다. 그 흔적이 2가지인데, x_ij 대신 x_i라는 표현이 남아있다는 점, 또 class probability(softmax 이후의 결과)가 detector 단위가 아닌 cell 단위로 존재한다는 점입니다. 오리지날 YOLO에서는 cell 내에서 detector 개수는 2개지만, class probability는 cell 내에서 1개입니다... 앞서 언급한 상황처럼 small object가 많아서 cell 안에 2개의 ground truth box가 있는데, 심지어 그게 서로 다른 class였다면 풀지 못합니다. 다행히도 YOLO v2에서는 class probability가 detector마다 있도록 개선되었으며, 이를 고려했을 때 cell 내에서 1개가 아닌 multiple ground truth box가 있어도 풀도록 했을 것으로 보입니다.
이것도 뭔가 이상합니다. YOLO에서는 cell마다 detector가 2개씩 있고, 이 detector가 갖는 게 x의 prediction이니까, cell의 index인 i뿐만 아니라 detector의 index인 j도 적어줘야 될 것 같은데.. 앞서 언급한 가정, 즉 ground truth box의 center가 떨어지는 cell은 object마다 서로 다르다는 가정이 있다면, 이게 틀린 표현은 아닙니다. 물론 굉장히 사람 헷갈리게 하는 표현입니다. 앞서 언급한 것과 같이 responsible은 cell 내에서 가장 높은 IoU를 갖는 1개 detector를 말하므로, detector는 i와 j로 index하지만, 이 중에서 responsible한 detector는 i로만 index해도 됩니다. 물론 cell에 2개 이상의 object center가 속할 수 있음을 반영한다면, 이 역시 i와 j로 index해야 자연스럽습니다.
confidence loss입니다. 이것도 i로만 index하고 있군요..
여기서 confidence score값은 sigmoid 이후라서 0~1인 확률 값입니다. 머신러닝 이론을 기본부터 열심히 하신 분이라면 알겠지만, 범위에 제한이 없는 실수값을 Sum of Squared Error로 loss를 주는 것은 자연스럽지만(가우시안을 상정하기 때문), 0~1 사이의 확률 값의 경우 Binary Cross Entropy를 loss를 주는 것이 자연스럽습니다(베르누이 분포를 상정하기 때문). 나이브하게 생각하면 0~1 사이의 확률 값에도 SSE써도 practical하게는 작동할 것으로 보이긴 합니다. 하지만, 사실 sigmoid한 결과에 SSE쓰면 gradient가 0 되기 쉬우며 바람직한 방법이 아닙니다..
이놈의 SSE를 classification loss에서도 사용합니다.. softmax한 결과에서도 SSE보다 Cross Entropy를 쓰는 것이 올바릅니다. 한편 YOLO v3에서는 class probability를 얻을 때 softmax 대신 sigmoid를 independent하게 사용하고 binary cross entropy를 사용하도록 개선되었다고 합니다. 이처럼 YOLO 및 YOLO v2에서는 이상하게도 모든 결과에서 SSE를 쓰는데, 음.. 좋지 않습니다..
참고로 classification loss에서는 indicator function을 i로만 index합니다. 앞서 언급한 것과 같이 오리지날 YOLO에서 class probability가 cell 당 1개씩 나타나기 때문입니다. YOLO v2에서는 이게 j로도 index되도록 수정되었을 것으로 보입니다.
이건 구글링하다 발견한 한 링크에서 한 유저가 추측한 YOLO v2의 loss function입니다. anchor와 관련된 term은 잘 이해가 안 되지만, 값들을 모두 i, j로 index하고, responsible이라고 표기해주는 등 굉장히 잘 정리했다고 생각합니다.