hello, robot

모델은 결국 숫자 덩어리예요. 학습은 원하는 출력이 나올 때까지 그 안의 숫자를 조금씩 바꾸는 일이고요. 코드를 짜는 게 아니라 숫자를 맞춰 가는 과정이라는 감각이 먼저 잡혀야 해요.

그런데 실제로 코드를 돌리면 가장 자주 만나는 건 학습이 안 되는 문제가 아니에요. 배열 형태가 안 맞는 에러죠.

층마다 형태가 변해요

신경망은 층이 여러 겹이고, 입력 배열이 층을 지날 때마다 형태가 바뀌어요.

이미지를 예로 들면 높이, 너비, 채널 세 축으로 들어와요. 합성곱 층을 지나면 채널이 늘고 공간이 줄어요. 필터를 여러 개 쓰니 채널이 늘고, 가장자리 처리 방식에 따라 높이와 너비가 줄죠. 풀링을 지나면 공간이 더 줄고요.

그러다 마지막에 일반적인 층으로 넘어가려면 3차원을 1차원으로 펴야 해요. 높이 곱하기 너비 곱하기 채널만큼의 긴 벡터가 되죠.

여기서 에러가 제일 많이 나요. 펴기 전 형태를 잘못 계산하면 다음 층이 기대하는 입력 크기와 안 맞거든요. 그리고 그 계산은 앞의 모든 층을 거슬러 올라가야 나와요.

형태를 추적할 수 있으면 대부분 끝나요

그래서 습관 하나가 값을 해요. 각 층을 지난 뒤 형태를 찍어 보는 것이요.

에러 메시지는 대개 “이만큼을 기대했는데 저만큼이 왔다”고 알려 줘요. 두 숫자를 보고 어느 층에서 어긋났는지 역산할 수 있으면 그 자리에서 끝나요. 못 하면 층을 하나씩 지우며 이분 탐색을 하게 되고요.

배치 축이 특히 헷갈려요. 맨 앞에 붙는 축이 배치 크기인데, 이게 있을 때와 없을 때를 섞으면 에러 메시지가 이상하게 읽혀요. 한 장짜리 이미지를 넣으면서 배치 축을 안 만들어 주면 채널을 배치로 해석해 버리는 식이죠.

반복문 대신 배열 통째로

계산 쪽 감각도 하나 짚고 갈게요.

수치 계산 라이브러리의 존재 이유는 배열 통째 연산이에요. 원소마다 반복문을 돌리는 대신 배열끼리 더하고 곱하면, 그 연산이 더 낮은 층에서 처리돼 훨씬 빨라요.

이게 습관이 되면 코드 모양이 달라져요. 반복문이 보이면 배열 연산으로 바꿀 수 있는지부터 보게 되죠. 로봇 데이터도 대부분 배열이라 이 감각이 그대로 쓰여요.

학습 프레임워크의 배열은 이 라이브러리의 배열과 거의 같은 인터페이스에 가속기 지원과 자동 미분이 붙은 거예요. 서로 오가는 변환도 한 줄이고요. 그래서 앞의 것을 익히면 뒤의 것이 낯설지 않아요.

0으로 깔면 학습이 안 돼요

학습 시작 전에 가중치를 무슨 값으로 채울지가 생각보다 중요해요.

전부 0으로 깔면 학습이 아예 안 돼요. 이유가 흥미로워요. 같은 층의 모든 뉴런이 똑같은 값을 갖고 똑같이 움직이거든요. 입력이 같고 가중치가 같으니 출력이 같고, 갱신량도 같아요. 영원히 구분이 안 되니 뉴런을 여러 개 둔 의미가 사라져요.

그래서 무작위로 깔아 대칭을 깨요. 그런데 아무 크기나 되는 게 아니에요.

너무 크게 깔면 층을 지날수록 값이 커져 폭발하고, 너무 작게 깔면 층을 지날수록 줄어 사라져요. 층이 깊을수록 이 효과가 누적되니 심해지고요.

그래서 평균 0에 작은 표준편차를 쓰는데, 여기서 한 발 더 나가면 층 크기에 맞춰 분산을 정하는 방법들이 표준이에요. 입력이 많은 층은 더 작게 깔아야 합이 커지지 않거든요. 이름 붙은 초기화 방법들이 하는 일이 이거예요.

학습이 안 될 때 물어볼 순서

정리하면 문제가 두 종류로 갈려요.

에러가 나는 문제는 대개 형태예요. 층을 지나며 차원이 어떻게 변하는지 추적하면 끝나요. 이쪽은 시간이 짧게 걸리고 원인이 명확해요.

에러는 없는데 학습이 안 되는 문제가 어려워요. 손실이 안 줄거나, 줄다가 발산하거나, 발산도 안 하는데 성능이 안 나오죠. 여기서 초기화, 학습률, 데이터 전처리가 후보로 올라와요.

두 번째 종류에서 먼저 확인할 게 하나 있어요. 아주 작은 데이터 몇 개만으로 과적합이 되는지 보는 거예요. 열 개 샘플로 손실이 0에 가까워지면 모델과 학습 경로 자체는 정상이니, 문제는 데이터나 일반화 쪽이에요. 그것조차 안 되면 모델이나 학습 설정에 결함이 있는 거고요.

이 한 번의 확인이 후보를 절반으로 갈라요. 앞 글에서 본 어느 층의 문제인지부터 가르는 그 사고와 같은 구조예요.

미분이 왜 학습에 필요한지는 활성화 함수의 미분 가능성과 시그모이드 기울기 소실에, 손으로 만든 숫자를 모델이 못 알아보는 이야기는 MNIST 학습은 됐는데 직접 쓴 숫자만 틀리는 전처리 불일치에 있어요.