부호 하나를 뒤집어 놓고 그걸 통찰이라 불렀던 날
한 백테스트가 내가 바라던 것과 거의 정확히 정반대인 승률을 들고 돌아온 적이 있다. 모델이 대체로 맞기를 바랐던 자리에서, 그것은 대체로 틀렸다 — 그것도 무작위로 틀린 게 아니라, 깔끔하고 대칭적으로 틀려서, 마치 일부러 지려고 애쓰는 것 같았다.
왜 그런지 묻는 대신, 나는 한 시간 정도는 기발하게 느껴졌던 무언가를 했다. 로직을 거기에 맞춰 뒤집었다. 모델이 안정적으로 틀린다면, 그것이 말하는 것의 정반대로 하면 안정적으로 맞을 것이라고 추론했다. 작은 변경 하나로, 지던 규칙이 서류상으로는 이기는 규칙이 되었다. 나는 발견을 했다며 스스로를 축하했다.
그것은 발견이 아니었다. 한 글자짜리 수정으로 나 자신에게 거짓말을 하기 시작한 순간이었다.
정확하게 틀린 그 숫자
수상했던 것은 결과가 나빴다는 점이 결코 아니었다. 나쁜 결과는 정상이고, 그게 일의 대부분이다. 수상했던 것은 그것이 그토록 깔끔한, 거울상의 방식으로 나빴다는 점이었다 — 돌이켜 보면, 정말로 “거꾸로” 된 전략이라기보다는 그 아래에서 훨씬 더 평범한 무언가가 망가졌다는 것을 가리킬 수 있는 종류의 대칭이었다.
하지만 대칭이 쉬운 승리를 가리킬 때 그것은 유혹적이다. 지저분하게 나쁜 결과는 디버깅을 하게 만든다. 깔끔하게 뒤집힌 결과는 지름길을 속삭인다: 너는 생각보다 가까이 와 있어 — 그냥 돌려놓기만 하면 돼.
한 글자짜리 수정
그래서 나는 그것을 돌려놓았다. 조건을 반대로 했다 — 부등호 하나를 뒤집었다 — 그러자 지고 있던 바로 그 로직이 이제 백테스트에서 이겼다. 나는 변경을 커밋하며, 문제보다 한 수 위에 선 것 같은 기분이었다. 수정 전체가 이 문장보다 작았다.
그 작음이 경고였어야 했다. 진짜 엣지는 찾기 어렵고 유지하기는 더 어렵다. 뒤집힌 단 한 글자가 실패를 성공으로 바꾸는 것처럼 보일 때, 정직한 가정은 네가 숨겨진 진실을 찾았다는 것이 아니다. 네가 숨겨진 버그를 찾았고, 단지 그 버그의 어느 쪽에 서 있을지를 바꾼 것뿐이라는 가정이다.
부호를 뒤집는 게 천재처럼 느껴진 이유
그 논리는 거의 거부할 수 없다: 예측기가 일관되게 틀린다면, 그것을 뒤집으면 일관되게 맞게 된다. 역(逆)천재. 역발상의 금광. 안정적으로 틀리는 모델을 거꾸로 읽기만 하면 되는데, 누가 마다하겠는가?
그 추론의 결함은 일관되게라는 단어에 있다. 그것은 모델이 실제로 무언가를 예측하고 있다고 — 단지 방향만 틀렸을 뿐이라고 — 가정한다. 그리고 그 가정이야말로 내가 한 번도 확인하지 않은 단 하나의 것이었다.
실제로 망가져 있던 것
모델은 거꾸로 된 게 아니었다. 그것은 거의 예측을 하지 못하고 있었다. 출력은 퇴화해 있었다 — 좁고 정보 없는 범위로 짓눌려, 무엇을 보여주든 거의 같은 말을 하고 있었다. 그것은 신호를 학습한 뒤 엉뚱한 방향을 가리킨 게 아니었다. 신호를 학습하는 데 실패했고, 의미 없는 약간의 치우침과 함께 노이즈에 가까운 무언가를 내놓고 있었다.
대체로 뒷면이 나온 짧은 시행은 안정적이고 활용 가능한 편향을 증명하지 못한다 — 그것은 그저 망가졌거나 신뢰할 수 없는 동전일 수도 있다. 노이즈 신호를 읽는 방식을 뒤집는다고 그 신호가 정보를 갖게 되지는 않는다. 그저 노이즈에 새 라벨을 붙일 뿐이다.
뒤집기가 오히려 더 나쁘게 만들었다
이 부분이 결국 백테스트가 내게 인정하게 만든 대목이다. 로직을 뒤집은 뒤, 성능은 거울처럼 위로 솟아 좋은 무언가가 되지 않았다. 그것은 여전히 나빴고 — 일부 지표로는 더 나빠졌다. 뒤집을 것이 아무것도 없었다. 나는 노이즈를 가져다 뒤집어 놓고, 다시 노이즈를 얻었을 뿐이다, 이제 의도성이라는 의상을 걸친 채로.
나를 영리하게 느끼게 했던 그 짧고 아름다운 결과는, 내가 전에 글로 쓴 적 있는 것과 같은 종류의 신기루였다: 유일하게 중요했던 질문을 던지지 못하도록 딱 그만큼만 너를 치켜세우는 숫자 — 모델이 도대체 무언가를 학습하기는 했는가?
진짜 버그는 내 안에 있었다
그 한 글자짜리 뒤집기는 사실 한 번도 코드 변경이 아니었다. 그것은 범주 오류였다. 나는 증상 — 나쁜 승률 — 을 마치 내가 튜닝할 수 있는 전략인 것처럼 다뤘지만, 그것은 사실 내가 내리기를 거부한 진단이었다. 모델의 출력이 왜 그렇게 보이는지 묻는 대신, 나는 숫자가 초록색으로 바뀔 때까지 백테스트에 맞춰 부등호 하나를 최적화했다.
그것이 가장 순수하고 가장 인간적인 형태의 과적합이다. 과적합하는 데 천 개의 파라미터가 필요하지 않다. 네가 이해하는 원인이 아니라 네가 원하는 결과에 근거해 부호를 고를 의향만 있다면, 단 하나의 부호로도 할 수 있다.
놀라운 결과는 답이 아니라 질문이다
여기서 내가 얻은 규율은 말하기는 쉽지만 진짜로 실천하기는 어렵다: 결과가 네가 예상한 것의 정반대로 나오면, 그것은 답이 아니라 질문이다. 유혹은 숫자가 옳아 보이게 만드는 가장 작은 변경을 향해 손을 뻗는 것이다 — 부호를 뒤집고, 규칙을 반전시키고, 비교를 바꿔치기하는 것. 그 가장 작은 변경이야말로 흔히 가장 위험한 것인데, 그것은 겉모습은 고치면서 원인은 완전히 손대지 않은 채로 두기 때문이다.
이제 무언가가 내 앞에서 뒤집힐 때, 나는 우아한 반전에 저항하고 대신 못생긴 질문을 던지려 한다: “어떻게 이 숫자가 옳은 방향을 가리키게 만들까?”가 아니라 “여기에 도대체 진짜 신호가, 어느 방향으로든 가리키는 게 있기는 한가?”라고. 흔히 아직은 없다. 그리고 뒤집힌 부호는 그것을 만들어낼 수 없다.
거꾸로 읽은 지는 모델은 여전히 지는 모델이다. 뒤집기가 바꾸는 유일한 것은 네가 그 사실을 알아차리기까지 걸리는 시간뿐이다.
— 신호도 없고, 수익도 없고, 투자 자문도 아니다.