Quiz / ML 기초와 평가 / 전체 문항
GitHub

ML 기초와 평가 전체 문항

워크플로, 편향-분산, 교차검증, 평가 지표. 문항과 선택지별 해설을 글 순서대로 나열했습니다.

48문항 · 글 8편
직접 풀어보기 →

머신러닝은 데이터에서 규칙을 찾는다, 지도·비지도·강화학습

원문 읽기 →
개념 확인구조

전통적인 프로그래밍과 머신러닝은 무엇이 입력이고 무엇이 출력인지가 다르다. 머신러닝 쪽은 어떤 방향인가?

  1. 데이터와 규칙을 넣어 결과를 얻는다.

    그쪽이 전통적인 프로그래밍이다. 사람이 규칙을 써서 넣고 프로그램이 결과를 뱉는 구조라, 스패머가 우회할 때마다 규칙을 손으로 덧붙여야 한다.

  2. 정답데이터와 결과를 넣어 규칙을 얻는다.

    스팸으로 표시된 메일과 정상으로 표시된 메일을 함께 넣으면 모델이 판별 기준을 스스로 만든다. 사람이 하는 일이 규칙을 정하는 쪽에서 데이터를 모으고 정답을 붙이는 쪽으로 옮겨간다.

  3. 규칙과 결과를 넣어 데이터를 얻는다.

    규칙과 결과에서 데이터를 만들어내는 것은 시뮬레이션이나 생성 모델의 일이다. 학습의 목표는 데이터를 만드는 것이 아니라 데이터에 숨은 규칙을 찾는 것이다.

  4. 규칙만 넣어 데이터와 결과를 함께 만들어낸다.

    규칙을 사람이 넣는다면 애초에 학습할 것이 없다. 머신러닝이 하는 일이 바로 그 규칙을 만들어내는 것이다.

해설

전통적인 프로그래밍은 데이터와 규칙을 넣어 결과를 얻고, 머신러닝은 데이터와 결과를 넣어 규칙을 얻는다. 이 한 번의 방향 전환에서 나머지가 따라 나온다. 스팸 필터로 보면 분명하다. 규칙을 손으로 쓰면 스패머가 우회할 때마다 조항이 하나씩 붙고 어느 순간 서로 충돌하기 시작하지만, 레이블이 붙은 메일 수천 통을 넣으면 모델이 판별 기준을 스스로 만든다. 사람의 일은 규칙을 정하는 쪽에서 데이터를 모으고 정답을 붙이는 쪽으로 옮겨간다. 그래서 모델의 품질을 좌우하는 것도 알고리즘보다 데이터 쪽인 경우가 많다.

이 내용 다시 읽기 →
상황 적용구조
supervised.fit(X, y)     # 입력과 정답을 함께 넘긴다
unsupervised.fit(X)      # 입력만 넘긴다. y를 놓을 자리가 없다

비지도학습의 fit에 정답을 넘길 자리가 아예 없다는 것은 무엇을 뜻하는가?

  1. y를 생략하면 라이브러리가 내부에서 레이블을 자동으로 만들어낸다.

    만들어내는 것은 레이블이 아니라 군집 번호나 축 같은 구조다. 그 번호에는 어느 것이 정답이라는 의미가 붙어 있지 않다.

  2. 비지도학습은 정확도를 계산할 수 없으므로 평가 자체가 불가능하다.

    정답과 대조하는 평가는 못 하지만 실루엣 계수나 설명된 분산처럼 구조의 품질을 재는 지표가 따로 있다. 평가의 기준이 바뀌는 것이지 사라지는 것이 아니다.

  3. 정답학습 신호가 정답 레이블이 아니라 데이터 자체의 구조라는 뜻이다.

    지도학습은 레이블이 학습을 감독하므로 레이블이 없으면 아예 성립하지 않는다. 비지도학습은 무엇이 정답인지 묻지 않고 무엇이 서로 비슷한지만 묻기 때문에 입력만으로 돌아간다.

  4. 레이블이 있는 경우에도 비지도학습에 함께 넘기면 성능이 좋아진다.

    넘길 인자 자체가 없으므로 넘길 수 없다. 레이블을 함께 쓰고 싶다면 준지도학습처럼 그 목적으로 설계된 방법을 써야 한다.

해설

지도학습과 비지도학습을 가르는 것은 학습 신호가 무엇이냐다. 지도학습은 레이블이 학습을 감독하므로 레이블이 없으면 성립하지 않는다. 비지도학습은 정답이 무엇인지 묻지 않고 무엇이 서로 비슷한지만 묻기 때문에 입력만으로 돌아가고, 그래서 fit의 시그니처부터 다르다. 이 차이가 실무에서 중요한 이유는 현실에서 쌓이는 데이터 대부분이 레이블 없이 쌓이기 때문이다. 유저 행동 로그, 센서 측정값, 의료 이미지에 사람이 일일이 정답을 붙이려면 비용을 감당할 수 없다. 클러스터링과 차원 축소, 이상 탐지가 데이터를 처음 뜯어보는 단계에서 자주 등장하는 이유가 여기 있다.

이 내용 다시 읽기 →
상황 적용판단
구매 로그 50만 건이 있는데 어떤 레이블도 붙어 있지 않다. 소비 패턴이 비슷한 고객끼리 묶어 타겟팅에 쓰려 한다. 고객군이 몇 개인지, 어떤 성격인지는 아직 모른다.

이 문제는 어느 갈래인가?

  1. 지도학습의 분류다. 고객군이라는 카테고리를 맞히는 문제다.

    분류는 카테고리가 미리 정해져 있고 각 데이터에 그 정답이 붙어 있을 때 쓴다. 여기서는 고객군이 몇 개인지조차 모르므로 맞힐 정답 자체가 없다.

  2. 지도학습의 회귀다. 소비 금액이라는 연속값을 다루기 때문이다.

    데이터에 연속값이 들어 있는 것과 연속값을 예측하는 것은 다르다. 여기서 얻고 싶은 것은 어떤 숫자가 아니라 고객들의 묶음이다.

  3. 정답비지도학습의 클러스터링이다. 정답 없이 무엇이 서로 비슷한지를 묻는다.

    레이블이 없고 얻으려는 것이 데이터의 구조이므로 정확히 이 자리다. 군집 결과를 사람이 해석해 고객군에 이름을 붙이는 것은 그다음 단계이지 학습의 입력이 아니다.

  4. 강화학습이다. 타겟팅 결과가 다음 행동을 바꾸기 때문이다.

    타겟팅을 반복하며 보상으로 전략을 고쳐가는 문제로 설계할 수는 있다. 다만 지금 필요한 것은 이미 쌓인 로그에서 구조를 읽는 일이라 환경도 보상도 없다.

해설

갈래를 고르는 기준은 두 가지다. 손에 있는 데이터가 어떤 형태인지, 그리고 무엇을 얻고 싶은지다. 여기서는 입력만 있고 얻고 싶은 것이 데이터의 구조이므로 클러스터링으로 떨어진다. 실무에서 손에 잡히는 문제 대부분은 지도학습으로 가지만, 그것은 예측 목표가 분명하고 레이블을 어떻게든 확보할 방법이 있을 때다. 비지도학습은 그 앞단에서 데이터를 훑거나 특성을 만들어내는 역할로 붙는 경우가 많다. 여기서도 군집 번호를 새 특성으로 만들어 나중에 지도학습 모델에 넣는 식으로 이어질 수 있고, 그때는 두 갈래가 한 파이프라인 안에 함께 놓인다.

이 내용 다시 읽기 →
상황 적용적용
X_train = np.array([[50], [70], [90], [110], [130]])      # 면적(m²)
y_train = np.array([25000, 33000, 41000, 50000, 59000])   # 집값(만원)

model = LinearRegression().fit(X_train, y_train)
model.predict([[80]])   # array([37350.])

모델이 데이터에서 읽어낸 관계는 무엇인가?

  1. 다섯 점의 평균인 41,600만원을 어떤 입력에도 그대로 내놓는다.

    평균만 내놓는 것은 아무 관계도 배우지 않은 상태이고, 성능의 바닥을 재는 기준 모델이 하는 일이다. 여기서는 80에 대해 37,350이 나왔으므로 입력에 따라 값이 움직인다.

  2. 면적과 값이 비례하므로 80m²는 50m²의 1.6배인 40,000만원이다.

    비례라면 원점을 지나야 하는데 이 직선의 절편은 3,350이다. 425 × 80에 그 절편을 더해야 37,350이 나오고, 비례로 계산하면 2,650만원이 어긋난다.

  3. 정답면적 1m²당 425만원이다. 80m²에 적용하면 37,350만원이 나온다.

    기울기 425는 면적이 1m² 늘 때 값이 얼마나 오르는지를 뜻하고 절편은 3,350이다. 425 × 80 + 3,350 = 37,350으로 출력과 정확히 맞는다. 학습 데이터에 없는 80m²에 답할 수 있는 것이 규칙을 배웠다는 증거다.

  4. 80m²는 학습 데이터에 없으므로 이 모델로는 예측할 수 없다.

    본 적 없는 입력에 답하는 것이 학습의 목적이다. 다만 학습 범위를 크게 벗어난 값에서는 신뢰가 떨어지는데, 80은 50에서 130 사이에 있으므로 안전한 구간이다.

해설

fit에 입력과 정답을 함께 넘긴 것이 전부인데 모델은 면적이 1m² 늘 때 값이 425만원 오른다는 관계를 데이터에서 직접 읽어냈다. 사람이 그 숫자를 알려준 적이 없다는 점이 요점이다. 여기서 눈여겨볼 것이 절편 3,350이다. 이 값이 있어서 면적과 집값의 관계는 비례가 아니라 직선이 되고, 두 배 넓다고 두 배 비싸지지 않는다. 학습 데이터에 없던 80m²에 답을 내놓는 것도 마찬가지로 중요하다. 데이터를 외운 것이 아니라 관계를 잡았기 때문에 가능한 일이고, 다만 학습 범위를 크게 벗어난 입력에서는 그 관계가 유지된다는 보장이 사라진다.

이 내용 다시 읽기 →
분석과 판단판단
레이블 10만 장으로 이미지 분류기를 학습시키면 며칠이면 끝난다. 반면 게임을 두는 에이전트는 자가 대국을 백만 판 단위로 돌려야 사람 수준에 닿는다.

이 경험량 차이는 어디에서 오는가?

  1. 게임은 이미지보다 입력 차원이 높아 계산량이 많기 때문이다.

    바둑판은 이미지 한 장보다 훨씬 작은 입력이다. 비용을 만드는 것은 한 번의 계산 크기가 아니라 필요한 시행의 횟수다.

  2. 정답한 행동이 좋았는지를 결과가 나올 때까지 알 수 없어 필요한 경험이 폭발한다.

    지도학습은 샘플마다 정답이 그 자리에서 주어지지만 강화학습은 판이 끝나야 보상이 돌아온다. 그 사이 수백 수 가운데 어느 것이 좋았는지는 끝까지 알 수 없고, 결과가 좋았던 행동의 비중을 조금씩 늘려갈 뿐이다.

  3. 강화학습에는 레이블이 없어 모델을 평가할 방법이 아예 없기 때문이다.

    누적 보상이라는 분명한 평가 기준이 있다. 평가가 불가능한 것이 아니라 그 평가가 한 수 단위가 아니라 판 단위로만 돌아온다는 것이 문제다.

  4. 자가 대국은 병렬화가 되지 않아 순차적으로만 돌릴 수 있기 때문이다.

    실제로는 여러 판을 동시에 돌린다. 병렬화를 해도 필요한 총 시행 횟수 자체가 줄지 않는다는 것이 이 방식의 대가다.

해설

강화학습은 정답 대신 보상으로 배우고, 그 대가로 시행착오의 횟수를 지불한다. 한 번의 행동이 좋았는지 나빴는지는 결과가 나올 때까지 알 수 없으므로 필요한 경험의 양이 폭발한다. 사람 수준의 바둑에 이르기까지 백만 판 단위의 자가 대국이 필요했던 것이 그 규모다. 여기서 나오는 실무적 판단은 단순하다. 레이블을 확보할 수 있는 문제라면 지도학습이 비교할 수 없이 싸다. 강화학습이 제자리를 찾는 곳은 지금의 선택이 다음 상황을 바꾸는 순차적 의사결정 문제이고, 게임이나 로봇 제어처럼 환경과의 상호작용 자체가 문제의 본질인 자리에 한정된다.

이 내용 다시 읽기 →
분석과 판단판단

규칙을 사람이 쓰지 않고 데이터에서 찾게 되면서 따라 나오는 결과는 무엇인가?

  1. 규칙이 사라졌으므로 모델의 동작을 설명할 방법이 원리적으로 없다.

    규칙이 사라진 것이 아니라 사람이 읽을 수 없는 형태로 모델 안에 들어간 것이다. 특성 중요도나 개별 예측의 기여도를 따로 계산해 설명을 되찾는 방법이 마련되어 있다.

  2. 정답데이터 품질이 곧 모델 품질이 되고 판단 근거를 따로 물어야 한다.

    결과를 정하는 것이 사람이 쓴 규칙에서 데이터로 옮겨갔으므로 데이터가 어긋나면 모델도 어긋난다. 동시에 규칙이 코드에 적혀 있지 않으니 왜 그렇게 판단했는지를 별도의 절차로 캐물어야 한다.

  3. 사람이 할 일이 줄어 데이터 수집과 레이블링도 함께 자동화된다.

    일이 줄어드는 것이 아니라 옮겨간다. 규칙을 쓰던 노력이 데이터를 모으고 정답을 붙이는 쪽으로 이동하고, 그 비용이 프로젝트의 대부분을 차지하는 경우가 흔하다.

  4. 규칙 충돌이 사라지므로 스패머가 우회해도 모델을 고칠 필요가 없다.

    우회 수법이 바뀌면 데이터의 분포가 달라지므로 모델을 다시 학습시켜야 한다. 손보는 대상이 규칙에서 데이터로 바뀌었을 뿐 유지보수가 없어지지는 않는다.

해설

머신러닝은 사람이 규칙을 쓰던 자리에 데이터를 놓는다. 이 한 번의 방향 전환에서 두 가지가 따라 나온다. 결과를 정하는 것이 데이터이므로 데이터의 품질이 곧 모델의 품질이 되고, 규칙을 코드에서 읽을 수 없으니 모델이 왜 그렇게 판단했는지를 따로 물어야 한다. 앞의 사실이 데이터 중심 접근을 낳고 뒤의 사실이 설명 가능성이라는 분야를 낳았다. 세 패러다임의 구분도 무엇을 학습 신호로 쓰는지에 따른 것이지 실무의 칸막이는 아니다. 레이블이 일부만 붙은 데이터를 다루는 준지도학습처럼 경계에 걸친 방식도 있고, 하나의 시스템 안에 셋이 섞여 있기도 하다.

이 내용 다시 읽기 →

문제 정의부터 배포까지, 머신러닝 프로젝트의 전 과정

원문 읽기 →
개념 확인구조

머신러닝 프로젝트의 흐름이 한 방향 직선이 아니라 고리를 이루는 이유는 무엇인가?

  1. 단계마다 실패할 수 있어 같은 단계를 여러 번 반복해야 하기 때문이다.

    같은 단계를 다시 도는 것과 앞 단계로 되돌아가는 것은 다르다. 고리를 만드는 것은 실패한 단계의 재시도가 아니라 뒤에서 얻은 정보가 앞의 결정을 뒤집는 구조다.

  2. 정답평가 결과가 전처리를 바꾸고 배포 후 모니터링이 데이터 수집을 다시 부르기 때문이다.

    성능이 목표에 못 미치면 전처리나 데이터로 되돌아가고, 배포 뒤 성능이 떨어지면 다시 데이터를 모은다. 두 개의 고리가 각각 모델 평가와 모니터링에서 시작한다.

  3. 여러 모델을 비교하려면 학습과 평가를 번갈아 돌려야 하기 때문이다.

    모델 비교는 학습과 평가 단계 안에서 일어나는 일이다. 고리는 그 단계를 넘어 데이터 수집이나 전처리까지 되돌아갈 때 생긴다.

  4. 배포 전에 코드 리뷰와 테스트를 반복해야 하기 때문이다.

    소프트웨어 개발의 반복과는 성격이 다르다. 여기서 되돌아가게 만드는 것은 코드의 결함이 아니라 데이터가 알려주는 새로운 사실이다.

해설

처음에는 데이터 수집에서 배포로 곧게 이어지는 직선을 떠올리기 쉽지만 실제 모양은 두 개의 고리가 붙은 형태다. 평가 결과를 보고 전처리 방식을 바꾸고, 특성이 모자라면 데이터를 더 모으고, 다시 학습시키는 과정이 반복된다. 배포한 뒤에도 성능이 떨어지면 데이터 수집으로 되돌아온다. 머신러닝 개발이 공학보다 실험에 가깝다고 말하는 이유가 여기 있다. 한 번에 끝내는 절차가 아니라 어디로 되돌아갈지를 계속 고르는 절차이기 때문이다. 그래서 막혔을 때 유용한 질문도 무엇을 더 해볼까가 아니라 어느 고리로 돌아가야 하는가다.

이 내용 다시 읽기 →
상황 적용함정
모델 다섯 개를 돌린 뒤 지표별 점수를 표로 정리했다. MAE에서는 랜덤 포레스트가 앞서고 R²에서는 XGBoost가 앞선다. 팀에서 XGBoost를 밀고 싶으니 R²로 보고하자고 한다.

이 결정의 문제는 무엇인가?

  1. R²는 회귀 문제에 쓸 수 없는 지표다.

    R²는 회귀에서 널리 쓰이는 지표다. 문제가 되는 것은 지표의 종류가 아니라 그것을 고른 시점이다.

  2. 정답결과를 보고 지표를 정하면 지표가 모델을 정당화하는 순환 논리가 된다.

    지표는 비즈니스 요구에서 나와야 하고 학습 전에 확정돼야 한다. 순서가 뒤집히면 어떤 모델을 고르든 그 모델이 잘 나오는 지표를 찾을 수 있어 성능 개선을 스스로 속이게 된다.

  3. MAE와 R²는 단위가 다르므로 둘을 함께 보고해야 한다.

    여러 지표를 함께 보는 것은 좋은 습관이지만 그렇게 해도 무엇으로 판정할지는 정해야 한다. 그 판정 기준을 결과를 본 뒤에 고르는 것이 문제의 핵심이다.

  4. 모델 다섯 개로는 지표별 우열을 가리기에 표본이 부족하다.

    모델을 더 돌려도 결과를 보고 지표를 고르는 구조는 그대로다. 오히려 후보가 많아질수록 유리한 지표를 찾기가 쉬워져 문제가 커진다.

해설

평가지표는 학습을 시작하기 전에 정해야 한다. 모델을 여러 개 돌려본 뒤 점수가 잘 나오는 지표를 고르면 지표가 모델을 정당화하는 순환 논리가 되고, 그 순간부터 개선의 근거가 사라진다. 지표는 비즈니스 요구에서 나와야 한다. 아파트 가격 예측에서 MAE를 고른 것도 평균적으로 얼마나 틀리는지를 원 단위 그대로 읽을 수 있어 비즈니스 쪽과 이야기하기 쉽다는 이유였다. 성공 기준도 함께 못박아둔다. 지표와 기준선이 먼저 서 있어야 나중에 나온 숫자가 통과인지 미달인지를 다투지 않고 판정할 수 있다.

이 내용 다시 읽기 →
상황 적용판단
성공 기준은 MAE 2,000만원 이하다. 선형 회귀(베이스라인) MAE 3,240만원 랜덤 포레스트 MAE 1,820만원

복잡한 모델을 쓰기로 결정할 때 근거로 삼아야 할 값은 무엇인가?

  1. 훈련 MAE와 테스트 MAE의 차이다. 그 간격이 좁을수록 좋은 모델이다.

    그 간격은 과적합 여부를 알려주는 값이지 복잡한 모델을 쓸지 말지를 정해주지 않는다. 간격이 좁으면서 성능이 나쁜 모델도 얼마든지 있다.

  2. 학습에 걸린 시간이다. 짧을수록 운영하기 쉬운 모델이다.

    학습 시간은 늘어난 복잡도가 치르는 대가 중 하나일 뿐이다. 그 대가를 넘는 이득이 있는지를 보려면 성능 쪽 숫자가 함께 필요하다.

  3. 테스트 MAE의 절대값이다. 2,000을 넘느냐만 확인하면 된다.

    기준 통과 여부는 필요조건이지 충분조건이 아니다. 베이스라인이 이미 1,900이었다면 같은 1,820이라도 복잡한 모델을 들일 이유가 약해진다.

  4. 정답베이스라인 대비 개선폭이다. 44%가 늘어난 복잡도의 대가를 넘는지를 따진다.

    3,240에서 1,820으로 44% 줄었다는 비교가 여기서 의미 있는 정보다. 기준선이 없으면 1,820이라는 숫자가 좋은 값인지 판단할 방법 자체가 없다.

해설

베이스라인의 역할은 점수를 읽을 자를 만드는 것이다. 3,240에서 1,820으로 44% 줄었다는 비교가 있어야 1,820이라는 숫자에 의미가 생긴다. 기준선이 없으면 어떤 모델의 점수를 봐도 좋은 것인지 판단할 방법이 없다. 여기에 함께 붙는 원칙이 하나 있다. 복잡도가 올라가면 학습 시간, 추론 지연, 해석 가능성, 운영 비용이 모두 나빠지므로 개선폭이 그 대가를 넘어야 복잡한 모델이 정당화된다. 그래서 실전 순서는 늘 같다. 가장 단순한 모델로 먼저 점수를 재두고, 그 값을 기준으로 이후의 모든 변경을 저울질한다.

이 내용 다시 읽기 →
상황 적용구조
baseline = LinearRegression().fit(X_train_scaled, y_train)   # 표준화한 입력

rf = RandomForestRegressor(n_estimators=100,
                           random_state=42).fit(X_train, y_train)   # 원시 입력

랜덤 포레스트에만 표준화하지 않은 입력을 넣은 이유는 무엇인가?

  1. 정답트리는 값의 절대 크기가 아니라 어디서 나눌지만 보므로 스케일에 영향받지 않는다.

    면적을 m²로 재든 평으로 재든 데이터를 같은 순서로 정렬하면 같은 지점에서 갈라진다. 분할 기준값만 단위에 맞춰 바뀔 뿐 만들어지는 트리의 구조는 그대로다.

  2. 랜덤 포레스트가 내부에서 자동으로 스케일링을 수행하기 때문이다.

    내부에서 스케일링하는 단계가 없다. 필요가 없어서 하지 않는 것이지 몰래 해주는 것이 아니다.

  3. 스케일링하면 분할 기준값을 해석할 수 없어 성능이 떨어지기 때문이다.

    해석이 불편해지는 것은 맞지만 성능은 사실상 그대로다. 표준화한 입력을 넣어도 같은 점수가 나온다.

  4. 앙상블이라 여러 트리가 평균을 내면서 스케일 차이가 상쇄되기 때문이다.

    트리 하나만 써도 스케일에 둔감하다. 상쇄되는 것이 아니라 애초에 영향을 받지 않는다.

해설

트리 계열 모델은 특성값의 절대 크기가 아니라 어디서 나눌지만 보기 때문에 스케일에 영향받지 않는다. 면적을 m²로 재든 평으로 재든 데이터의 순서가 같으면 같은 지점에서 갈라지고, 분할 기준값만 단위에 맞춰 바뀐다. 반면 선형 회귀나 거리 기반 모델은 값의 크기를 그대로 계산에 쓰므로 스케일이 다르면 특정 특성이 결과를 지배한다. 표준화가 필요한지 아닌지를 가르는 기준이 여기 있다. 모델이 값의 크기를 직접 쓰느냐, 아니면 순서만 쓰느냐다. 다만 트리에 표준화를 해도 손해는 없으므로, 여러 모델을 같은 파이프라인으로 비교할 때는 그냥 넣어두는 편이 실수가 적다.

이 내용 다시 읽기 →
분석과 판단진단
학습할 때는 StandardScaler로 입력을 표준화한 뒤 모델에 넣었다. 배포할 때는 joblib으로 모델만 저장했고, API는 사용자가 보낸 원시 값을 그대로 predict에 넘긴다. 예외는 발생하지 않는다.

무슨 일이 벌어지는가?

  1. 차원이 맞지 않아 곧바로 예외가 발생하므로 배포 직후 발견된다.

    특성의 개수는 그대로이므로 형태 검사는 통과한다. 값의 스케일만 다를 뿐이라 예외 없이 조용히 지나간다.

  2. 스케일러 없이도 모델이 알아서 값을 정규화하므로 문제가 없다.

    학습된 모델이 갖고 있는 것은 표준화된 공간에서 맞춰진 계수뿐이다. 입력을 되돌려주는 기능은 어디에도 없다.

  3. 정답모델이 표준화된 값을 기대하는데 원시 값이 들어와 예측이 통째로 어긋난다.

    면적 84가 학습 때는 0.3 언저리의 표준화 값이었는데 추론에서는 84가 그대로 들어간다. 계수는 그대로이므로 계산은 되지만 결과는 의미 없는 숫자가 되고, 에러가 나지 않아 한동안 모른 채 지나간다.

  4. 예측값이 조금 부정확해질 뿐 크기의 방향은 대체로 맞는다.

    표준화는 값을 몇 배씩 옮기는 변환이라 조금 어긋나는 수준이 아니다. 특성마다 배율이 달라 순서까지 뒤집힐 수 있다.

해설

학습에 쓴 전처리는 추론에서도 똑같이 재현돼야 한다. 스케일러를 썼다면 그 스케일러도 함께 저장해 같은 순서로 적용해야 하고, 원핫 인코딩을 했다면 컬럼 순서와 범주 목록까지 맞아야 한다. 이 어긋남이 특히 위험한 이유는 예외가 나지 않는다는 데 있다. 특성 개수만 맞으면 계산은 정상적으로 돌아가고 그럴듯한 숫자가 나오므로, 예측이 이상하다는 신고가 들어올 때까지 아무도 모른다. 전처리와 모델을 하나의 Pipeline으로 묶어 통째로 저장하면 이 문제가 구조적으로 생기지 않는다. 학습과 추론이 같은 객체를 쓰게 되므로 재현할 것이 남지 않기 때문이다.

이 내용 다시 읽기 →
분석과 판단진단
아파트 가격 예측 모델을 배포하고 6개월이 지났다. 예측 MAE가 1,820만원에서 3,100만원으로 올랐다. 코드도 모델 파일도 배포 시점 그대로이고 에러 로그도 깨끗하다.

가장 먼저 확인할 것은 무엇인가?

  1. 모델 파일이 손상됐는지 체크섬을 확인한다.

    파일이 손상되면 로딩 단계에서 실패하거나 예측이 완전히 망가진다. 1,820에서 3,100으로 점진적으로 나빠지는 것은 다른 종류의 신호다.

  2. 정답입력 데이터의 분포와 입력-타깃 관계가 변했는지를 본다.

    부동산 시장이 바뀌면 과거 데이터로 배운 관계가 어긋난다. 코드는 그대로인데 성능만 조용히 떨어지는 것이 모델 드리프트의 전형적인 모습이고, 대응은 새 데이터로 다시 학습시키는 것이다.

  3. 코드가 그대로이므로 성능도 그대로여야 한다. 측정 쪽을 의심한다.

    모델의 성능은 코드만으로 정해지지 않는다. 들어오는 데이터가 학습 때와 달라지면 같은 코드가 다른 성능을 낸다.

  4. 테스트 세트를 다시 나눠 평가를 반복한다.

    과거 데이터로 만든 테스트 세트에서는 여전히 1,820이 나온다. 문제는 그 세트가 지금 들어오는 데이터를 더 이상 대표하지 못한다는 데 있다.

해설

입력 데이터의 분포가 변하거나 입력과 타깃의 관계 자체가 변하는 현상을 모델 드리프트라 한다. 코드는 그대로인데 성능만 조용히 떨어지므로 배포 시점에 세워둔 모니터링이 없으면 한참 뒤에야 알게 된다. 그래서 예측 성능과 입력 분포를 계속 재는 모니터링과 새 데이터로 다시 학습시키는 파이프라인이 배포와 한 묶음이어야 한다. 여기서 중요한 것이 무엇을 재느냐다. 정답이 뒤늦게 들어오는 문제라면 성능 지표는 몇 달 뒤에나 계산되므로, 그전에 입력 분포의 변화만으로 경보를 울릴 수 있어야 한다. 배포는 끝이 아니라 마지막 고리의 시작이다.

이 내용 다시 읽기 →

모델 오차를 쪼개면 드러나는 편향-분산 트레이드오프

원문 읽기 →
개념 확인구조

제곱 오차를 편향 제곱, 분산, 노이즈 세 항으로 쪼갰을 때 우리가 손댈 수 없는 것은 무엇이며 그 이유는 무엇인가?

  1. 편향 제곱이다. 모델 구조는 한번 고르면 바꿀 수 없기 때문이다.

    모델 구조야말로 우리가 직접 고르는 것이다. 차수를 올리거나 트리를 깊게 만들면 편향이 곧바로 내려간다.

  2. 정답노이즈 분산이다. 데이터를 만든 과정 자체에 들어 있어 어떤 모델로도 줄일 수 없다.

    y = f(x) + ε에서 ε은 입력으로 설명되지 않는 부분이라 완벽한 모델을 써도 남는다. 총 오차가 절대 내려갈 수 없는 하한선이 여기서 정해진다.

  3. 분산이다. 훈련 데이터가 어떻게 뽑히는지는 우리가 정할 수 없기 때문이다.

    분산은 규제를 걸거나 복잡도를 낮추거나 데이터를 더 모으는 것으로 실제로 줄어든다. 통제할 수 있는 두 항 가운데 하나다.

  4. 셋 다 손댈 수 없다. 분해는 사후에 오차를 설명하는 도구일 뿐이다.

    편향과 분산은 모델 선택과 규제로 직접 움직인다. 셋을 나눠 보는 이유가 바로 어느 쪽을 건드릴지 정하기 위해서다.

해설

제곱 오차는 편향 제곱, 분산, 노이즈 분산의 합으로 정확히 갈라진다. 앞의 둘은 우리가 움직일 수 있지만 노이즈는 데이터를 만든 과정에 들어 있어 어떤 모델로도 줄일 수 없다. 이 하한선이 실무에서 중요한 이유는 언제 멈출지를 알려주기 때문이다. 검증 오차가 어느 선 아래로 내려가지 않는다고 해서 모델이 잘못된 것이 아니라 데이터 자체가 정한 바닥일 수 있다. 게다가 총 오차 곡선은 최저점에서도 그 바닥에 닿지 않는다. 편향 제곱과 분산이 동시에 0이 되는 복잡도는 없기 때문이다. 점수를 더 짜내려고 씨름하기 전에 지금 바닥이 모델의 한계인지 데이터의 한계인지 구분하는 것이 먼저다.

이 내용 다시 읽기 →
상황 적용진단
사인 곡선에 표준편차 0.3 노이즈를 얹은 데이터에 차수만 바꿔가며 다항 회귀를 맞추고 세 항을 따로 쟀다. 노이즈 분산은 0.09다. 차수 Bias² Var 합 + σ² 1 0.1548 0.0161 0.2609 3 0.0031 0.0090 0.1021 5 0.0001 0.0144 0.1044 9 0.0003 0.1066 0.1969 11 0.0348 11.4075 11.5323

차수 5의 편향 제곱이 3보다 작은데도 총 오차는 3이 더 낮다. 이 표가 보여주는 것은 무엇인가?

  1. 차수 5의 측정에 오류가 있다. 편향이 작으면 총 오차도 작아야 한다.

    총 오차는 편향 제곱과 분산의 합이다. 한쪽만 보고 결론을 내릴 수 없고, 차수 5는 편향에서 0.003을 벌고 분산에서 0.005를 잃었다.

  2. 차수 11의 분산 11.4는 계산 오류이므로 표 전체를 신뢰할 수 없다.

    표본 40개에 11차 다항식을 맞추면 설계 행렬이 거의 특이해져 계수가 수백 배로 튄다. 훈련점 사이에서 예측이 크게 요동치므로 이 값은 정상적인 결과다.

  3. 정답편향은 차수 3에서 이미 바닥이고 그 뒤로는 분산 증가가 총 오차를 지배한다.

    차수 3이면 사인 곡선을 표현할 수 있어 편향이 0.0031까지 떨어지고 더 내려갈 자리가 없다. 반면 분산은 5부터 꾸준히 오르다 9를 지나며 폭주하므로, 총합의 최저점은 양쪽이 다 작은 중간에 놓인다.

  4. 차수를 올릴수록 총 오차가 단조롭게 증가한다.

    차수 1에서 3으로 갈 때는 0.2609에서 0.1021로 크게 내려간다. 단조가 아니라 가운데가 낮은 U자 모양이다.

해설

편향 제곱은 차수 3에서 이미 거의 0으로 떨어지고 그 뒤로는 내려갈 자리가 없다. 반면 분산은 차수 5부터 꾸준히 오르다가 9를 지나면서 폭주한다. 그래서 총합이 가장 작은 곳은 양쪽이 다 작은 중간 지점이고, 이 U자 모양이 편향과 분산 트레이드오프의 전부다. 마지막 줄의 분산 11.4가 이상해 보이지만 계산 오류가 아니다. 표본 40개에 11차 다항식을 맞추면 설계 행렬이 거의 특이해져 계수가 수백 배로 튀고 훈련점 사이에서 예측이 크게 요동친다. 훈련셋이 조금만 달라져도 그 요동의 위치와 크기가 통째로 바뀌므로 분산이 그만큼 커진다.

이 내용 다시 읽기 →
상황 적용함정
여러 모델과 하이퍼파라미터를 두 주 동안 돌렸는데 검증 MSE가 0.10 근처에서 더 내려가지 않는다. 데이터를 만든 노이즈의 분산은 0.09다.

이 정체를 어떻게 읽어야 하는가?

  1. 정답노이즈가 정한 바닥에 거의 닿았다. 더 짜낼 여지가 0.01 안팎뿐이다.

    총 오차는 편향 제곱과 분산에 노이즈 분산을 더한 값이므로 0.09 아래로는 어떤 모델도 내려갈 수 없다. 0.10에서 멈춘 것은 모델의 결함이 아니라 데이터가 정한 한계에 근접했다는 뜻이다.

  2. 탐색 공간이 좁았다. 더 복잡한 모델을 넣으면 0.05까지 내려간다.

    복잡도를 올리면 편향이 조금 줄어들 수 있지만 분산이 함께 오른다. 어느 쪽을 조절해도 0.09라는 하한 아래로는 갈 수 없다.

  3. 검증 세트가 작아 점수가 어느 값에 고정된 것처럼 보인다.

    검증 세트가 작으면 값이 흔들리지 한 곳에 머물지 않는다. 여러 설정에서 비슷한 값이 반복해서 나온다는 것은 바닥에 닿았다는 신호에 가깝다.

  4. 노이즈 분산 0.09는 이론값이므로 실제 데이터에서는 의미가 없다.

    데이터를 만든 과정에서 나온 값이라 그대로 하한이 된다. 실제 문제에서 이 값을 모른다는 것이 어려움이지, 하한이 존재하지 않는다는 뜻은 아니다.

해설

총 오차 곡선은 최저점에서도 노이즈 분산 선에 닿지 않는다. 편향 제곱과 분산이 동시에 0이 되는 복잡도가 없기 때문이다. 그러니 검증 오차가 어느 선 아래로 내려가지 않는다고 해서 모델이 잘못됐다는 뜻은 아니고, 데이터 자체가 정한 바닥일 수 있다. 실무에서 어려운 점은 그 바닥이 얼마인지 대개 모른다는 것이다. 그래서 인간 수준 성능이나 같은 데이터를 다룬 기존 시스템의 점수를 기준점으로 세워둔다. 기준점이 없으면 남은 0.01을 짜내려고 몇 주를 쓰게 되는데, 그 노력을 데이터 품질이나 새 특성 쪽에 돌리는 편이 나은 경우가 많다.

이 내용 다시 읽기 →
상황 적용판단
차수 1 다항 회귀에서 표본만 50개에서 5만 개로 천 배 늘렸다. 훈련 MSE의 평균이 0.273에서 0.286으로 오히려 올랐다. 노이즈 분산은 0.09이고, 직선으로는 좁힐 수 없는 편향이 0.196이다.

표본을 늘렸는데 훈련 오차가 오른 이유는 무엇인가?

  1. 표본이 많아질수록 노이즈가 누적되어 전체 오차가 커진다.

    노이즈는 평균 0이라 표본이 늘어도 누적되지 않는다. 오히려 평균을 내면 상쇄되어 추정이 안정된다.

  2. 정답표본이 적을 때는 직선이 그 점들에 조금 맞춰져 낮았고, 늘수록 바닥에 붙는다.

    50개일 때는 직선이 그 특정 점들에 약간 적합해 훈련 오차가 실제 한계보다 낮게 나온다. 표본이 늘면 그런 여지가 사라지고 편향 0.196에 노이즈 0.09를 더한 0.286으로 수렴한다.

  3. 5만 개는 과적합을 일으키는 크기라 훈련 오차가 오른 것이다.

    과적합은 훈련 오차를 낮추고 검증 오차를 올린다. 방향이 반대이고, 애초에 데이터가 많을수록 과적합은 줄어든다.

  4. 계산 과정에서 수치 오차가 누적된 결과다.

    0.273과 0.286의 차이는 수치 오차로 설명되기에 너무 크다. 그리고 0.286이 편향과 노이즈의 합과 정확히 맞아떨어진다는 것이 이론적 근거를 보여준다.

해설

데이터를 더 모으면 웬만한 것이 해결된다는 말은 분산 문제에만 해당한다. 편향은 모델이 표현할 수 있는 함수의 범위에서 오는 것이고 데이터의 양은 그 범위를 넓히지 못한다. 직선으로 사인 곡선을 맞추면 좁힐 수 없는 편향이 0.196이고 여기에 노이즈 0.09를 더한 0.286이 이 모델의 바닥이다. 표본이 적을 때 훈련 오차가 그보다 낮았던 것은 직선이 그 몇 개 점에 조금 맞춰졌기 때문이고, 표본이 늘수록 그 여지가 사라져 바닥에 붙는다. 데이터 수집은 대개 가장 비싸고 오래 걸리는 작업이므로, 착수하기 전에 학습 곡선을 그려 좁힐 갭이 실제로 있는지부터 확인하는 편이 낫다.

이 내용 다시 읽기 →
분석과 판단진단
차수 11 다항 회귀에 Ridge(alpha=1)를 걸고 다시 쟀다. Bias² Var 총 오차 규제 없음 0.0348 11.4075 11.5323 Ridge 0.0015 0.1097 0.2011 차수 3 0.0031 0.0090 0.1021

규제가 분산을 백 분의 일로 줄였는데도 차수 3에 못 미치는 것은 무엇을 말해주는가?

  1. alpha가 부족하다. 더 세게 걸면 차수 3보다 낮아진다.

    alpha를 더 올리면 총 오차가 0.17 안팎까지는 더 내려가지만 거기가 바닥이다. 어떤 강도로도 차수 3의 0.1021에는 닿지 않는다.

  2. Ridge는 다항 회귀에 맞지 않는 규제라 Lasso를 써야 한다.

    Lasso로 바꾸면 일부 계수가 0이 되어 사실상 차수를 낮추는 효과가 나지만, 그것도 규제로 복잡도를 사후에 다듬는 같은 종류의 조치다.

  3. 정답규제는 과한 복잡도를 사후에 다듬을 뿐 맞는 복잡도를 고르는 일을 대신하지 못한다.

    차수는 여전히 11이고 계수 크기만 눌려 훈련점 사이의 요동이 걷힌 상태다. 분산이 크게 줄었지만 0.2011로 차수 3의 0.1021에는 끝내 닿지 못한다.

  4. 규제를 걸면 편향이 반드시 늘어나므로 총 오차가 나빠질 수밖에 없다.

    여기서는 편향도 0.0348에서 0.0015로 함께 내려갔다. 계수 폭주가 편향까지 왜곡하고 있었기 때문이고, 규제가 언제나 편향을 늘리는 것은 아니다.

해설

규제가 분산을 다루는 방식은 복잡도를 깎지 않고 계수 크기를 눌러 훈련점 사이의 요동만 걷어내는 것이다. 그래서 차수는 11 그대로인데 분산이 백 분의 일로 떨어지고, 계수 폭주가 만들던 왜곡이 사라지면서 편향까지 함께 내려간다. 그런데도 총 오차가 차수 3에 닿지 못한다. 규제는 과한 복잡도를 사후에 다듬는 도구이지 맞는 복잡도를 고르는 일을 대신해주지 않는다는 뜻이다. 실무에서 이 구분이 중요해지는 자리는 탐색 순서다. 모델의 크기나 차수를 먼저 대략 맞추고 그 위에서 규제 강도를 조정하는 편이, 과한 모델을 세워놓고 규제로만 끌어내리는 것보다 도달할 수 있는 지점이 낫다.

이 내용 다시 읽기 →
분석과 판단함정
이 데이터에 이미 알맞은 크기인 차수 4 다항 회귀에 Ridge 강도만 바꿔가며 총 오차를 쟀다. 참고로 차수 1 직선의 총 오차는 0.2609다. alpha 없음 0.1 1 100 10000 총 오차 0.1047 0.1067 0.1173 0.1444 0.3084

alpha 10000에서 총 오차가 직선보다도 나빠진 이유는 무엇인가?

  1. 규제가 너무 세면 수치적으로 불안정해져 계산이 발산한다.

    Ridge는 alpha가 클수록 오히려 수치적으로 안정된다. 대각 성분에 더해지는 값이 커져 역행렬 계산이 쉬워지기 때문이다.

  2. 차수 4는 원래 규제가 필요 없는 크기라 alpha를 0으로 두는 것 말고 답이 없다.

    표에서 alpha 0.1의 총 오차가 0.1067로 규제 없음의 0.1047과 거의 같다. 약한 규제는 해가 되지 않고, 문제는 강도를 어디까지 올리느냐다.

  3. 정답계수를 끝까지 누르면 4차 다항식이 거의 평평한 상수 쪽으로 끌려간다.

    규제는 복잡도 손잡이를 왼쪽으로 미는 일이고, 너무 밀면 직선을 지나 상수에 가까워진다. 상수는 직선보다도 표현력이 낮으므로 편향이 차수 1보다 커진다.

  4. 총 오차가 노이즈 분산 0.09보다 크므로 이 실험 자체가 잘못됐다.

    노이즈 분산은 하한이므로 어떤 값이든 그보다 커야 정상이다. 0.3084가 0.09보다 큰 것은 오류의 신호가 아니다.

해설

규제는 복잡도 손잡이를 왼쪽으로 미는 일이다. 너무 밀면 반대편 낭떠러지로 떨어진다. 차수 4는 이 데이터에 이미 알맞은 크기라 규제를 걸 여유분이 없어 alpha를 올리는 족족 편향만 늘고, 10000에서는 총 오차가 차수 1 직선보다도 나빠진다. 계수를 끝까지 누르면 4차 다항식이 직선을 지나 거의 평평한 상수 쪽으로 끌려가기 때문이다. 여기서 얻을 실무 원칙은 단순하다. 규제 강도는 세게 걸수록 안전한 값이 아니라 다른 하이퍼파라미터와 똑같이 검증으로 골라야 하는 값이다. 세게 거는 것이 안전하다는 감각은 분산 쪽만 보고 편향 쪽을 잊었을 때 생긴다.

이 내용 다시 읽기 →

Accuracy 99%가 거짓말일 때 보는 Precision과 Recall

원문 읽기 →
개념 확인구조

Precision과 Recall은 TP를 공유하는데 무엇이 다른가?

  1. Precision은 TP와 TN을 함께 세고 Recall은 TP만 센다.

    두 지표 모두 분자가 TP 하나뿐이다. TN을 쓰는 것은 Accuracy와 Specificity 쪽이다.

  2. 정답분모가 다르다. 예측 Positive 열과 실제 Positive 행을 각각 읽는다.

    Precision은 모델이 양성이라 말한 것 중 진짜가 얼마인지를, Recall은 세상에 있는 양성 중 몇 퍼센트를 건졌는지를 잰다. 같은 TP를 어느 쪽으로 나누느냐만 다르다.

  3. Precision은 임계값에 따라 변하고 Recall은 변하지 않는다.

    둘 다 임계값을 따라 움직인다. 오히려 서로 반대 방향으로 움직이는 것이 두 지표의 핵심 성질이다.

  4. Precision은 이진 분류에만 쓰고 Recall은 다중 분류에도 쓴다.

    둘 다 클래스마다 계산해 다중 분류에도 쓴다. 그때 클래스별 값을 어떻게 합칠지가 Macro와 Micro의 문제가 된다.

해설

주요 지표는 전부 혼동 행렬 네 칸의 조합이고, 각 지표가 표의 어느 방향을 읽느냐가 성격을 정한다. Precision은 예측 Positive 열을 세로로 읽어 모델이 양성이라 말했을 때 그 말을 얼마나 믿을 수 있는지를 잰다. Recall은 실제 Positive 행을 가로로 읽어 세상에 존재하는 양성 중 몇 퍼센트를 건져 올렸는지를 잰다. 둘이 TP를 공유하되 분모가 다르다는 것이 이 두 지표를 헷갈리게 만드는 지점이다. 어느 쪽을 볼지는 문제가 정한다. 스팸 필터는 업무 메일이 사라지는 것이 더 아프니 Precision을, 암 검진은 환자를 돌려보내는 것이 더 아프니 Recall을 앞세운다.

이 내용 다시 읽기 →
상황 적용함정
cm = confusion_matrix(y_true, y_pred)

# 교과서 그림대로 TP가 좌상단이라고 보고 꺼냈다
tp, fn = cm[0][0], cm[0][1]
fp, tn = cm[1][0], cm[1][1]

이 코드가 만드는 문제는 무엇인가?

  1. confusion_matrix는 1차원 배열을 반환하므로 이중 인덱싱에서 예외가 난다.

    2차원 배열을 반환하므로 인덱싱은 정상적으로 동작한다. 값이 엉뚱하게 들어갈 뿐 에러가 나지 않는다는 것이 이 함정의 성격이다.

  2. y_true와 y_pred의 순서가 바뀌어 행과 열이 뒤집힌다.

    인자 순서는 맞게 넘겼다. 문제는 반환된 행렬 안에서 네 칸의 자리를 잘못 짚은 데 있다.

  3. 다중 분류에서는 2×2가 아니므로 인덱스 범위를 벗어난다.

    이진 분류라면 2×2가 맞다. 클래스 수와 무관하게 여기서 어긋나는 것은 레이블 정렬 순서다.

  4. 정답레이블을 오름차순으로 놓아 TN이 좌상단에 오므로 두 지표 자리에 음성 기준 값이 들어간다.

    scikit-learn은 [[TN, FP], [FN, TP]] 형태로 반환하므로 TP가 우하단에 있다. 위 코드는 TN을 TP로 읽으므로 Precision 자리에 NPV가, Recall 자리에 Specificity가 나온다. ravel로 tn, fp, fn, tp 순서를 명시하면 이 실수가 사라진다.

해설

scikit-learn의 confusion_matrix는 레이블을 오름차순으로 놓기 때문에 행렬이 [[TN, FP], [FN, TP]] 형태로 나온다. 교과서 그림은 보통 Positive를 먼저 놓아 TP가 좌상단에 오는데 코드로 뽑으면 TP가 우하단에 있다. 인덱스를 손으로 꺼내 쓸 때 이 순서를 착각하면 Precision 자리에 NPV(음성 예측의 적중률)가, Recall 자리에 Specificity(음성의 재현율)가 나오고, 두 값이 모두 그럴듯한 범위에 있어 한동안 눈치채기 어렵다. 방어는 간단하다. ravel로 풀어 tn, fp, fn, tp 순서로 이름을 붙이거나 애초에 precision_score와 recall_score를 쓰는 것이다. 손으로 꺼낼 일이 있다면 완벽하게 예측한 데이터를 한 번 넣어보고 어느 칸이 채워지는지 확인하는 습관이 도움이 된다.

이 내용 다시 읽기 →
상황 적용판단
샘플 열 개 가운데 양성이 다섯이다. 같은 모델의 임계값만 바꿔 재봤다. threshold 0.25 Precision 5/8 Recall 5/5 threshold 0.65 Precision 3/3 Recall 3/5

이 두 결과에서 읽어야 할 것은 무엇인가?

  1. 정답임계값을 움직이면 두 지표가 반대로 움직이므로 어느 오류를 더 미워할지를 정해야 한다.

    0.25로 내리면 양성을 하나도 놓치지 않는 대신 음성 셋이 딸려 들어오고, 0.65로 올리면 부른 셋이 전부 진짜지만 양성 둘을 놓친다. 두 분포가 완전히 갈라지지 않는 한 이 교환은 피할 수 없다.

  2. 0.65 쪽이 Precision 100%이므로 언제나 그쪽을 골라야 한다.

    Precision만 보면 그렇지만 양성 둘을 놓치고 있다. 암 검진이라면 환자 둘을 돌려보낸 것이라 훨씬 비싼 실수다.

  3. 임계값에 따라 점수가 흔들리므로 모델을 다시 학습시켜야 한다.

    흔들리는 것이 아니라 임계값이 하이퍼파라미터라서 그렇다. 재학습 없이 임계값만 다시 골라도 원하는 균형을 만들 수 있는 경우가 많다.

  4. 두 임계값의 F1을 계산해 높은 쪽을 고르면 판단이 끝난다.

    F1은 두 지표에 같은 무게를 준다는 또 하나의 선택일 뿐이다. 한쪽이 더 중요한 문제라면 Fβ로 기울이거나 아예 다른 지표를 봐야 한다.

해설

확률을 출력하는 모델은 그 값을 임계값과 비교해 양성과 음성을 정하고, 기본값 0.5는 그냥 기본값이다. 임계값을 내리면 Recall이 오르고 Precision이 내려가며 올리면 반대가 된다. 두 분포가 완전히 갈라지지 않는 한 이 교환은 피할 수 없으므로, 지표를 고르는 일은 사실 어느 쪽 오류를 더 아프게 볼 것인가를 정하는 일이다. 여기서 함께 기억할 것은 임계값이 하이퍼파라미터라는 사실이다. 학습이 끝난 모델의 성능은 고정된 하나의 숫자가 아니라 임계값을 따라 움직이는 곡선이고, 배포 직전에 검증 데이터로 임계값을 다시 고르는 것만으로 재학습 없이 원하는 균형을 만들 수 있는 경우가 많다.

이 내용 다시 읽기 →
상황 적용구조
Precision 1.0, Recall 0.01인 모델이 있다. 양성이라 부른 것은 전부 맞혔지만 실제 양성의 1%만 잡았다. 산술평균 0.505 F1 0.02

F1이 조화평균을 쓰는 이유는 무엇인가?

  1. 조화평균이 산술평균보다 계산이 빨라 대규모 평가에 유리하다.

    두 계산의 비용 차이는 없다. 고르는 기준은 속도가 아니라 어느 쪽 값에 끌려가느냐다.

  2. 조화평균은 비율끼리 평균 낼 때만 수학적으로 정의되기 때문이다.

    비율의 산술평균도 정의된다. 실제로 위 표의 0.505가 그 값이고, 문제는 정의 가능성이 아니라 그 값이 상황을 오도한다는 데 있다.

  3. 정답산술평균은 큰 값에 끌려가지만 조화평균은 작은 값에 끌려가기 때문이다.

    한쪽이 0에 가까우면 곱 PR이 먼저 무너져 결과도 함께 무너진다. 양성을 사실상 하나도 못 잡는 모델에 0.505라는 멀쩡한 점수를 주지 않으려면 이 성질이 필요하다.

  4. 조화평균을 쓰면 값이 항상 0과 1 사이로 묶이기 때문이다.

    Precision과 Recall이 이미 0과 1 사이이므로 산술평균도 그 범위를 벗어나지 않는다. 범위가 아니라 범위 안에서 어디에 놓이느냐가 다르다.

해설

산술평균은 큰 값에 끌려가고 조화평균은 작은 값에 끌려간다. 두 값 중 하나가 0에 가까우면 곱이 먼저 무너지기 때문에 결과도 같이 무너지고, 그래서 Precision 1.0에 Recall 0.01인 모델의 F1은 0.02가 된다. 두 값이 비슷하면 두 평균이 거의 같고 벌어질수록 F1이 훨씬 인색해진다. F1은 두 지표에 같은 무게를 준다는 또 하나의 선택이기도 하다. 한쪽이 더 중요하면 Fβ로 기울이는데, β는 Recall을 Precision보다 몇 배 중요하게 볼 것인가를 뜻한다. 암 검진처럼 놓치지 않는 것이 우선이면 F2를, 스팸 필터처럼 헛경보를 줄이는 것이 우선이면 F0.5를 쓴다.

이 내용 다시 읽기 →
분석과 판단진단
사기 탐지 데이터에 음성 9,900건, 양성 100건이 있다. 모델이 200건을 사기로 지목했고 그중 90건이 진짜였다. TPR = 90/100 = 0.90 FPR = 110/9900 ≈ 0.011 Precision = 90/200 = 0.45

ROC 평면에서는 좌상단에 붙은 훌륭한 점인데 Precision은 0.45인 이유는 무엇인가?

  1. TPR과 Precision은 서로 다른 임계값에서 계산된 값이라 직접 견줄 수 없다.

    세 값 모두 같은 예측 결과 하나에서 나왔다. 임계값이 아니라 분모에 무엇을 쓰느냐가 두 관점을 가른다.

  2. 정답FPR은 분모에 TN을 쓰고 Precision은 쓰지 않아, 음성이 많을수록 TN이 FP를 희석한다.

    오탐 110건이 음성 9,900건에 나뉘면 0.011이라는 작은 값이 되지만, 같은 110건을 양성 예측 200건 안에서 보면 절반을 넘는다. 조사 인력을 배정해야 하는 현장에서는 뒤쪽이 진실에 가깝다.

  3. 양성이 100건뿐이라 TPR의 분모가 작아 0.90이 과대평가된 것이다.

    TPR 0.90은 실제로 90건을 잡았다는 정직한 값이다. 낙관을 만드는 것은 분자와 분모가 모두 양성인 TPR이 아니라 음성 쪽 분모가 거대한 FPR이다.

  4. ROC 곡선은 불균형 데이터에 쓸 수 없는 지표라 계산 자체가 무의미하다.

    임계값을 정하기 전 모델끼리 순위 매기는 능력을 비교할 때는 여전히 쓸모가 있다. 다만 그 값을 현장의 부담으로 번역할 때 오해가 생긴다.

해설

AUC가 임계값에 독립적이라는 장점은 그대로 함정이 되기도 한다. 음성이 압도적으로 많으면 FPR의 분모가 커져 어지간한 오탐으로는 눈금이 움직이지 않기 때문이다. 여기서 오탐 110건은 FPR로는 0.011이지만 Precision으로 보면 경보의 절반 이상이 헛것이라는 뜻이 된다. 차이의 원인은 하나다. FPR은 분모에 TN을 쓰고 Precision은 쓰지 않으므로, 음성이 많을수록 TN이 FP를 희석해 ROC 곡선을 낙관적으로 만든다. 양성이 극소수인 문제에서 PR 곡선을 보라는 권고가 여기서 나온다. 보고서에 양성 비율을 함께 적는 습관도 도움이 되는데, 그 숫자가 없으면 읽는 사람이 기준선을 모른 채 값만 보게 된다.

이 내용 다시 읽기 →
분석과 판단판단
두 프로젝트의 결과를 나란히 놓았다. A: 양성 비율 1%, PR-AUC 0.40 B: 양성 비율 40%, PR-AUC 0.60

두 PR-AUC를 어떻게 견줘야 하는가?

  1. 0.60이 크므로 B 쪽 모델이 더 낫다고 보면 된다.

    PR 곡선의 무작위 기준선은 데이터마다 다르다. 절대값만 비교하면 어려운 문제를 푼 쪽이 부당하게 낮게 평가된다.

  2. PR-AUC는 0과 1 사이로 정규화된 값이라 데이터가 달라도 직접 비교할 수 있다.

    범위가 같다는 것과 기준점이 같다는 것은 다르다. 두 지표 모두 0과 1 사이지만 무작위 모델이 얻는 값이 각각 0.01과 0.40으로 크게 다르다.

  3. ROC-AUC로 바꿔 재면 두 값을 곧바로 비교할 수 있다.

    기준선이 0.5로 같아져 비교가 쉬워지는 것은 맞다. 다만 음성이 많은 A에서는 높은 값이 오탐 부담을 반영하지 못하므로, 그 비교가 두 문제의 실제 난이도를 말해주지는 않는다.

  4. 정답무작위 기준선이 양성 비율이므로 0.01 대비 40배와 0.40 대비 1.5배를 견줘야 한다.

    PR 곡선에서 아무 정보 없이 찍는 모델은 양성 비율 높이의 수평선을 그린다. A는 기준선의 40배를 냈고 B는 1.5배에 그쳤으므로, 절대값과 정반대의 결론이 나온다.

해설

PR 곡선의 무작위 기준선은 ROC와 달리 고정되어 있지 않고 양성 비율 그 자체다. 그래서 양성이 1%인 데이터에서 PR-AUC 0.4는 기준선 0.01의 40배이므로 나쁘지 않은 성적이고, 양성이 40%인 데이터의 0.6은 기준선 대비 1.5배에 그친다. 절대값만 보고 판단하면 결론이 뒤집힌다. ROC-AUC는 기준선이 언제나 0.5라 이런 혼동이 없지만 대신 음성이 많은 데이터에서 높은 값이 오탐 부담을 반영하지 못하는 문제를 안고 있어, 어느 쪽도 혼자서는 충분하지 않다. 그래서 불균형 데이터를 보고할 때는 양성 비율을 반드시 함께 적는다. 그 숫자가 없으면 PR-AUC를 읽는 사람이 기준선을 모르고, Accuracy를 읽는 사람은 99%가 어디서 왔는지 모른다.

이 내용 다시 읽기 →

MAE, RMSE, R² 중 무엇으로 회귀 모델을 고를까

원문 읽기 →
개념 확인구조
잔차 다섯 개가 0.5, 1.0, 0.5, 1.0, 4.0이다. 절댓값으로 재면 합 7.0 중 4.0이 57% 제곱으로 재면 합 18.5 중 16.0이 86%

부호를 없앨 때 절댓값과 제곱 중 무엇을 쓰느냐가 만드는 차이는 무엇인가?

  1. 절댓값은 부호를 일부 남기고 제곱은 완전히 없앤다.

    둘 다 부호를 완전히 없앤다. 그래서 남는 차이는 부호가 아니라 크기를 어떻게 가중하느냐다.

  2. 제곱은 계산이 빨라 대용량 데이터에서 유리하다.

    곱셈과 절댓값의 계산 비용 차이는 무시할 수준이다. 제곱을 쓰는 진짜 이유는 미분이 매끄러워 최적화에 편하다는 데 있다.

  3. 정답절댓값은 크기에 비례해 취급하고 제곱은 큰 잔차를 불균형하게 키운다.

    잔차가 2배가 되면 절댓값 기여분은 2배지만 제곱 기여분은 4배다. 그래서 같은 잔차 하나가 절댓값으로는 57%, 제곱으로는 86%를 차지한다.

  4. 절댓값은 이상치를 계산에서 아예 제외하고 제곱은 포함한다.

    절댓값도 이상치를 그대로 더한다. 다만 크기에 비례해서만 더하므로 제곱만큼 압도적인 비중을 주지 않을 뿐이다.

해설

잔차에는 부호가 있어 그대로 평균 내면 상쇄되므로 절댓값을 씌우거나 제곱한다. 사소해 보이는 선택인데 결과가 갈린다. 절댓값은 모든 잔차를 크기에 비례해 취급하고 제곱은 큰 잔차를 불균형하게 키우기 때문이다. 여기서는 잔차 하나가 절댓값 기준으로 합계의 57%지만 제곱 기준으로는 86%를 가져간다. MSE를 최소화하는 학습은 사실상 그 하나짜리 이상치를 줄이는 일에 대부분의 힘을 쓴다는 뜻이다. 제곱을 쓰는 실용적인 이유는 미분이 매끄럽다는 데 있다. 절댓값은 0에서 꺾여 경사하강법에 불편하므로 학습에는 MSE를, 보고에는 단위가 타깃과 같은 RMSE를 쓰는 조합이 흔하다.

이 내용 다시 읽기 →
상황 적용판단
두 모델의 잔차와 점수다. 모델 A 잔차 1, 1, 1, 1, 1 MAE 1.0 RMSE 1.000 모델 B 잔차 0, 0, 0, 0, 3 MAE 0.6 RMSE 1.342 MAE는 B를, RMSE는 A를 더 낫다고 판정한다.

어느 쪽 판정을 따라야 하는가?

  1. 정답문제가 정한다. 큰 실수 한 번이 치명적이면 A, 가끔 크게 틀려도 되면 B다.

    두 판정 모두 계산이 맞다. 절댓값을 씌운 순간 큰 오차와 작은 오차를 같은 눈으로 보겠다고 정한 것이고, 제곱을 씌운 순간 큰 오차 하나가 작은 오차 여럿보다 나쁘다고 정한 것이다.

  2. RMSE가 MAE보다 정교한 지표이므로 언제나 RMSE를 따른다.

    정교함의 차이가 아니라 미워하는 대상의 차이다. 이상치를 노이즈로 보고 흘리고 싶은 문제에서는 MAE 쪽이 더 알맞은 판정을 준다.

  3. 둘의 평균을 내어 종합 점수로 순위를 매긴다.

    단위가 같아도 두 지표는 서로 다른 질문에 답한다. 평균을 내면 어느 질문에도 답하지 못하는 숫자가 나온다.

  4. B의 잔차에 0이 네 개라 계산이 왜곡됐으므로 A가 옳다.

    0인 잔차는 정확히 맞혔다는 뜻이라 왜곡이 아니다. B가 네 건을 완벽하게 맞히고 한 건을 크게 틀렸다는 사실이 그대로 반영된 결과다.

해설

MAE와 RMSE가 서로 다른 모델을 고른다는 것은 지표가 정확도의 등급표가 아니라 어떤 실수를 얼마나 미워할지에 대한 선언이라는 뜻이다. 대부분의 예측이 정확한 대신 가끔 크게 어긋나도 되는 문제라면 B가 낫고, 한 번의 큰 실수가 치명적인 문제라면 A가 낫다. 배송 예상 시간이라면 대체로 정확한 쪽이 낫고, 항공기 부품 수명 예측이라면 한 번의 큰 실수를 막는 쪽이 낫다. 그래서 순서는 지표를 고르고 모델을 학습하는 것이 아니다. 이 문제에서 무엇이 비싼 실수인지 먼저 정하고 그 정의에 맞는 지표를 고르는 쪽이다.

이 내용 다시 읽기 →
상황 적용진단
표본 5개짜리 테스트 세트에서 MAE 1.4, RMSE 1.92가 나왔다. 두 값의 비는 1.37이다. 이 비는 언제나 1과 √n 사이에 있다.

이 비율에서 무엇을 읽어야 하는가?

  1. 잔차가 모두 같은 크기로 고르게 퍼져 있다는 뜻이다.

    그 경우 비가 정확히 1이 된다. 1.37은 1에서 눈에 띄게 떨어져 있으므로 잔차의 크기가 서로 다르다는 신호다.

  2. RMSE 계산에 오류가 있다. 두 값은 언제나 같아야 한다.

    RMSE는 제곱평균이라 산술평균인 MAE보다 작아질 수 없고, 같아지는 것은 모든 잔차의 크기가 똑같을 때뿐이다. 1.92가 1.4보다 큰 것은 정상이다.

  3. 표본이 5개라 비가 √5인 2.24를 넘을 수 없어 해석할 값이 없다.

    상한이 있다는 것은 척도가 있다는 뜻이지 정보가 없다는 뜻이 아니다. 1과 2.24 사이에서 어디에 놓였는지가 곧 잔차 분포의 모양이다.

  4. 정답소수의 큰 오차가 섞여 있다. 상위 잔차를 직접 열어볼 차례다.

    비가 1에 가까우면 잔차가 고르게 퍼져 있고 커질수록 몇 개가 전체를 짊어진다는 뜻이다. 두 지표를 이미 계산했다면 공짜로 얻는 진단이고, 값이 튀면 잔차가 큰 관측을 골라 무엇이 다른지 확인하는 것이 다음 순서다.

해설

RMSE는 항상 MAE 이상이고 두 값이 같아지는 것은 모든 잔차의 크기가 똑같을 때뿐이다. 반대편 끝은 √n으로 잔차 하나가 전부를 짊어질 때 나온다. 그래서 이 비율은 두 지표를 이미 계산했다면 공짜로 얻는 진단 도구다. 1에 가까우면 잔차가 고르게 퍼져 있고, 눈에 띄게 크면 소수의 큰 오차가 섞여 있다는 신호다. 비가 튀었을 때의 다음 순서도 정해져 있다. 잔차 상위 몇 개를 직접 열어 그 관측이 무엇이 다른지 보는 것이다. 데이터 오류일 수도 있고 모델이 다루지 못하는 유형일 수도 있는데, 두 경우의 처방이 서로 다르므로 눈으로 확인하는 것 말고 지름길이 없다.

이 내용 다시 읽기 →
상황 적용함정
샘플 100개에 진짜 유효한 특성은 3개뿐이고 4번째 열부터는 타깃과 아무 관계 없는 난수다. 학습 데이터에서 잰 값이다. 특성 수 R² Adjusted R² 1 0.7063 0.7033 3 0.9452 0.9435 6 0.9457 0.9421 10 0.9465 0.9405

난수 특성을 붙였는데도 R²가 계속 오르는 이유는 무엇인가?

  1. 난수 열에도 우연히 신호가 섞여 있어 설명력이 실제로 올랐다.

    우연한 적합이 조금은 있지만 그것이 원인의 전부는 아니다. 최소제곱에서는 어떤 열을 붙여도 R²가 결코 내려가지 않는다는 것이 구조적으로 보장된다.

  2. 정답계수를 0으로 두면 이전 모델과 같아지므로 결과가 그보다 나빠질 수 없다.

    새 열의 계수를 0으로 두는 것이 항상 후보에 들어 있으므로 최적화가 찾은 해는 최소한 이전만큼은 좋다. 그래서 학습 데이터의 R²로는 특성 수가 다른 모델을 비교할 수 없다.

  3. 샘플 100개가 적어 R²가 불안정하게 흔들린 결과다.

    흔들림이라면 오르내려야 하는데 표에서는 단조롭게 오르기만 한다. 방향이 한쪽으로 정해져 있다는 것이 구조적 원인을 가리킨다.

  4. R²는 특성 수와 무관한 지표이므로 표의 값에 오류가 있다.

    무관하기는커녕 특성이 늘수록 절대 내려가지 않는다. 그 성질을 보정하려고 나온 것이 Adjusted R²다.

해설

최소제곱으로 특성을 추가하면 R²는 절대 줄어들지 않는다. 추가된 특성이 완전한 난수여도 그렇다. 새 열의 계수를 0으로 두면 이전 모델과 똑같아지므로 최적화 결과가 그보다 나쁠 수 없기 때문이다. 그래서 학습 데이터의 R²로는 특성 수가 다른 모델을 비교할 수 없다. Adjusted R²는 특성 수에 비례하는 벌점을 붙여 이를 보정하고, 표에서 유효 특성 3개를 넘어서는 순간부터 내려가기 시작한다. 여기에 붙는 다른 함정도 있다. R²는 음수가 될 수 있는데, 절편을 포함한 최소제곱 모델을 학습 데이터에서 평가하면 0과 1 사이가 보장되므로 음수는 홀드아웃에서만 나온다. 테스트 R²가 음수라면 과적합이나 분포 이동을 알리는 신호다.

이 내용 다시 읽기 →
분석과 판단함정
수요 예측 모델을 MAPE로 평가하고 그 값을 직접 최적화했다. 배포 후 보니 예측이 실제 수요보다 체계적으로 낮게 나와 재고 부족이 반복된다.

MAPE를 목적함수로 쓴 것이 이 치우침과 어떻게 이어지는가?

  1. MAPE는 절댓값을 쓰므로 방향 정보가 사라져 예측이 무작위로 치우친다.

    절댓값 때문이라면 위아래로 고르게 흩어져야 한다. 한 방향으로만 눌리는 것은 분모가 만들어내는 비대칭 때문이다.

  2. 정답과소예측의 오차율에는 100%라는 천장이 있지만 과대예측에는 없다.

    0으로 예측하는 것이 과소 쪽의 최악이라 오차율이 100%를 넘을 수 없다. 반면 실제값의 세 배로 예측하면 200%가 되므로 같은 크기의 실수라도 위로 틀리는 쪽이 더 무겁게 벌점을 받는다.

  3. MAPE는 실제값이 0에 가까울 때 폭발하므로 그 항들이 평균을 끌어내린다.

    0 근처의 폭발은 MAPE의 대표적인 결함이 맞지만 그 항들은 평균을 끌어올린다. 예측을 아래로 미는 것은 별개의 성질이다.

  4. MAPE는 회귀에 쓸 수 없는 지표라 어떤 방향으로든 결과가 왜곡된다.

    타깃이 0에서 충분히 떨어져 있고 비율 오차가 의미 있는 문제라면 MAPE는 유용하다. 지표를 모르는 사람에게도 그대로 전달된다는 것이 큰 장점이다.

해설

MAPE는 오차를 실제값으로 나누므로 위아래가 대칭이 아니다. 과소예측은 0으로 예측하는 것이 최악이라 오차율이 100%를 넘을 수 없지만, 과대예측에는 천장이 없어 실제값의 세 배로 예측하면 200%가 된다. 같은 크기의 실수라도 위로 틀리는 쪽이 더 크게 벌점을 받으니 이 값을 목적함수로 삼으면 예측이 체계적으로 아래로 눌린다. 재고 부족이 재고 과잉보다 비싼 문제에서는 정확히 반대 방향의 치우침이라 손해가 크다. 대안은 두 갈래다. 분모를 실제값과 예측값의 평균으로 바꾼 sMAPE는 값이 200%로 묶이지만 이름과 달리 완전히 대칭은 아니다. 시간 순서가 있다면 순진한 예측의 오차로 나누는 MASE가 분모의 폭발 문제까지 함께 해결한다.

이 내용 다시 읽기 →
분석과 판단적용
5천만 원짜리 집과 10억 원짜리 집을 각각 10%씩 틀렸다. RMSE로 재면 오차가 500만 원과 1억 원이라 7,080만 원 RMSLE로 재면 둘 다 log 1.1 = 0.0953으로 같아 0.0953

RMSLE가 두 집을 같게 취급하는 이유는 무엇인가?

  1. 정답로그의 차가 곧 비의 로그라서 절대 오차가 아니라 비율을 재기 때문이다.

    log(1+y) - log(1+ŷ)는 두 값의 비에 로그를 씌운 것과 같다. 10%씩 틀렸다면 비가 둘 다 1.1이므로 로그 오차가 같아지고, 비싼 집 하나가 지표를 지배하는 일이 사라진다.

  2. 로그를 취하면 큰 값이 압축되어 오차가 작게 보이는 착시가 생긴다.

    착시가 아니라 재는 대상이 바뀐 것이다. 비율 오차가 정말로 같다면 같은 값이 나오는 것이 맞고, 절대 오차를 알고 싶다면 RMSE를 쓰면 된다.

  3. 1을 더해 0으로 나누는 것을 막았기 때문에 두 값이 같아진다.

    1을 더하는 것은 실제값이 0일 때 로그가 정의되지 않는 문제를 피하려는 장치다. 두 집이 같아지는 것과는 다른 이유다.

  4. 제곱근을 씌워 단위를 되돌렸기 때문에 스케일 차이가 사라진다.

    제곱근은 제곱한 값을 원래 크기로 되돌릴 뿐이다. 스케일을 지우는 일은 그 전 단계인 로그 변환이 한다.

해설

로그의 차는 곧 비의 로그이므로 RMSLE는 두 값의 비율이 얼마나 어긋났는지를 잰다. 그래서 5천만 원짜리와 10억 원짜리를 나란히 10%씩 틀렸다면 로그 오차가 둘 다 같고, 같은 두 집을 RMSE로 재면 7,080만 원이 나오는데 그 값의 거의 전부가 비싼 집 하나에서 온다. 집값이나 매출처럼 양수이면서 오른쪽으로 길게 늘어진 타깃에 RMSLE를 쓰는 이유가 이것이다. 함께 알아둘 성질이 하나 더 있다. RMSLE는 과소예측을 더 세게 벌한다. 100을 150으로 예측하면 제곱 로그 오차가 0.162인데 50으로 예측하면 0.467로 2.9배 무겁다. 재고 부족이 재고 과잉보다 비싼 문제처럼 아래로 틀리는 것이 더 위험한 상황에 잘 맞는다.

이 내용 다시 읽기 →

K-Fold에서 TimeSeriesSplit까지, 교차 검증 전략 고르기

원문 읽기 →
개념 확인구조
100건 데이터에 TimeSeriesSplit(n_splits=5)를 적용한 결과다. split 1: 훈련 20개 (0~19), 검증 16개 (20~35) split 2: 훈련 36개 (0~35), 검증 16개 (36~51) split 3: 훈련 52개 (0~51), 검증 16개 (52~67) split 4: 훈련 68개 (0~67), 검증 16개 (68~83) split 5: 훈련 84개 (0~83), 검증 16개 (84~99)

K-Fold와 달리 이 방식이 매 회차마다 쓰지 않고 남기는 데이터는 무엇인가?

  1. 검증 구간보다 앞에 있는 오래된 데이터를 버린다.

    출력을 보면 훈련 구간이 언제나 0에서 시작해 앞쪽을 전부 담고 있다. 버리는 쪽은 앞이 아니라 뒤다.

  2. 정답검증 구간보다 뒤에 있는 데이터를 그 회차에서 쓰지 않는다.

    split 1을 보면 36번 이후 64건이 훈련에도 검증에도 등장하지 않는다. 미래를 보지 않으려면 치러야 하는 대가이고, K-Fold가 매 회차 모든 조각을 쓰는 것과 갈리는 지점이다.

  3. 훈련셋 크기를 회차마다 같게 맞추려고 앞쪽을 잘라낸다.

    훈련 크기가 20에서 84까지 계속 늘어나므로 맞추고 있지 않다. 고정된 것은 훈련이 아니라 검증 구간 16건이다.

  4. 버리는 것 없이 K-Fold와 같은 양을 쓰되 순서만 지킨다.

    같은 양을 쓰면서 순서를 지키는 것은 불가능하다. 마지막 회차를 빼면 언제나 미사용 구간이 남고, 그 구간이 곧 아직 오지 않은 미래다.

해설

TimeSeriesSplit은 미래를 보지 않기 위해 검증 구간보다 뒤에 있는 데이터를 그 회차에서 통째로 버린다. 그 대가는 초반 회차에 집중된다. 첫 회차의 훈련셋은 20건뿐이라 전체의 20%만 보고 학습한 모델을 평가하는 셈이고 점수가 낮게 나오기 쉽다. 회차별 점수를 그냥 평균내면 이 초반 핸디캡이 섞여 들어가므로, 뒤쪽 회차에 무게를 두거나 최소 훈련 크기를 정해두는 편이 실전 성능에 가깝다. 검증 구간이 16건으로 고정되고 훈련만 늘어나는 구조도 함께 기억할 만하다. 이 절차가 흉내 내는 것은 결국 운영이다. 오늘까지의 데이터로 학습해 내일을 예측하고, 하루가 지나면 그 데이터를 훈련에 넣고 다시 학습하는 흐름 그대로다.

이 내용 다시 읽기 →
상황 적용함정
x를 -1에서 1까지 정렬해 만든 100개 점에 sin(3x)와 잡음을 얹고 9차 다항 회귀를 걸었다. KFold(5) 기본값 CV MSE 993 KFold(5, shuffle=True) CV MSE 0.011 같은 데이터, 같은 모델, 같은 폴드 수다.

다섯 자릿수 차이가 난 이유는 무엇인가?

  1. 9차 다항식이 과적합이라 어떤 설정에서도 점수가 불안정하다.

    shuffle을 켠 쪽은 0.011로 안정적이다. 모델의 유연함은 문제를 키우는 조건일 뿐이고, 폭발을 일으킨 방아쇠는 폴드를 자르는 방식이다.

  2. shuffle=True가 검증 폴드 샘플을 훈련에 섞어 넣어 점수가 좋아진 것이다.

    셔플은 폴드에 배정할 순서를 바꿀 뿐 같은 샘플을 양쪽에 넣지 않는다. 각 샘플은 여전히 정확히 한 번만 검증에 쓰인다.

  3. random_state를 고정하지 않아 실행할 때마다 값이 달라졌다.

    기본값 쪽은 셔플을 하지 않으므로 시드와 무관하게 언제나 같은 폴드가 나온다. 시드가 개입할 자리 자체가 없다.

  4. 정답기본값이 shuffle=False라 각 폴드가 연속 구간이 되어 외삽을 하게 됐다.

    x가 정렬돼 있으니 셔플하지 않으면 첫 폴드는 x가 -1 근처인 구간 전체가 된다. 그 구간을 뺀 데이터로 학습한 9차 다항식이 훈련 범위 밖에서 크게 튀어 오르면서 오차가 폭발한다.

해설

KFold와 StratifiedKFold 모두 shuffle이 기본으로 꺼져 있다. 데이터가 어떤 키로 정렬된 채 저장돼 있으면 각 폴드가 연속 구간이 되고, 검증할 때마다 훈련에서 본 적 없는 영역을 외삽하게 된다. 9차 다항식처럼 유연한 모델은 훈련 범위 밖에서 크게 튀어 오르므로 오차가 폭발한다. 다섯 자릿수 차이가 모델이 아니라 데이터의 정렬 상태에서 왔다는 뜻이다. 993이라는 값이 잡음을 새로 뽑을 때마다 수십에서 수천 사이를 오간다는 점도 이 숫자가 모델의 성질이 아니라는 증거다. 시계열이 아니라면 shuffle=True를 명시해두는 편이 안전하고, 정렬 여부를 확인하지 않은 채 기본값에 맡기는 것이 이 함정의 실제 모습이다.

이 내용 다시 읽기 →
상황 적용진단
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)          # 전체 X의 평균과 표준편차
scores = cross_val_score(model, X_scaled, y, cv=5)

# CV 점수는 0.95인데 실전 배포 후에는 0.88 언저리에 머문다

이 코드로 얻은 점수를 믿을 수 없는 이유는 무엇인가?

  1. 정답전체 데이터의 평균과 표준편차에 검증 폴드의 정보가 실려 훈련 폴드로 새어 들어간다.

    fit_transform이 호출되는 순간 아직 검증에 쓰일 샘플들까지 통계량 계산에 참여한다. 실전에서는 아직 오지 않은 데이터의 평균을 알 수 없으므로, 이 점수는 실현 불가능한 조건에서 잰 값이다.

  2. StandardScaler는 분류 문제에 쓸 수 없는 전처리다.

    스케일링은 거리와 기울기를 다루는 분류기에서 오히려 필수에 가깝다. 문제는 전처리의 종류가 아니라 그것을 어디에서 학습시켰느냐다.

  3. cross_val_score가 이미 스케일링된 데이터를 다시 스케일링해 이중 적용된다.

    cross_val_score는 넘겨받은 추정기만 학습시킬 뿐 스스로 전처리를 하지 않는다. 여기서는 model이 스케일러를 갖고 있지 않으므로 변환은 한 번뿐이다.

  4. cv=5는 Stratified가 아니라 폴드마다 클래스 비율이 깨진다.

    분류기를 넘기면서 cv에 정수를 주면 sklearn이 알아서 Stratified K-Fold를 쓴다. 비율은 유지되고 있고 문제는 다른 데 있다.

해설

fit_transform(X)가 전체 데이터의 통계량을 계산하는 순간, 검증 폴드에 들어갈 샘플들의 정보가 그 평균과 표준편차에 실려 훈련 폴드로 새어 들어간다. Pipeline으로 감싸면 각 폴드에서 파이프라인 전체가 훈련 폴드만으로 학습되고 검증 폴드에는 transform만 적용되어 이 문제가 사라진다. 스케일링만의 이야기가 아니다. 결측치 대치, 특성 선택, PCA, 타깃 인코딩, 오버샘플링까지 데이터에서 무언가를 학습하는 모든 단계가 파이프라인 안에 들어가야 한다. 특히 특성 선택이 위험한데, 전체 데이터에서 타깃과 상관 높은 특성을 미리 골라두고 교차 검증을 돌리면 정답을 본 뒤에 시험을 치는 것과 같다. 누수는 에러를 내지 않고 점수를 올려주므로 배포 후에야 드러난다.

이 내용 다시 읽기 →
상황 적용진단
환자 200명에게서 나온 검사 기록 3,000건으로 진단 모델을 만들었다. 한 환자당 평균 15건이다. StratifiedKFold(5)로 교차 검증하니 AUC 0.94가 나왔는데, 새 병원에서 받은 데이터로 재보니 0.71이다.

가장 먼저 의심해야 할 원인은 무엇인가?

  1. 새 병원의 데이터 분포가 달라서 생긴 문제라 교차 검증으로는 애초에 잡을 수 없다.

    분포 차이도 가능한 설명이지만 지금 데이터로 갈라볼 수 있는 원인이 하나 남아 있다. 환자 단위로 폴드를 나눠 다시 재보면 두 가설이 구분된다.

  2. 정답같은 환자의 기록이 훈련과 검증에 갈라져 들어가 모델이 환자를 외웠다.

    한 환자당 15건이므로 무작위로 자르면 같은 환자의 기록이 양쪽에 섞일 확률이 사실상 1이다. 검증 폴드에 이미 본 환자가 있으니 병이 아니라 그 사람의 특징을 외운 것으로도 높은 점수가 나온다.

  3. 폴드 수 5가 부족해 점수가 낙관적으로 나왔다.

    폴드 수를 늘려도 같은 환자가 양쪽에 갈리는 구조는 그대로다. 0.23이나 벌어진 간격을 폴드 수로 설명할 수는 없다.

  4. AUC는 불균형 데이터에서 부풀려지므로 PR-AUC로 다시 봐야 한다.

    지표를 바꾸면 두 숫자가 함께 내려갈 뿐 간격은 남는다. 지표의 선택과 분할의 구조는 서로 다른 문제다.

해설

같은 환자에게서 나온 기록이 훈련셋과 검증셋에 갈라져 들어가면 모델은 병을 배우는 대신 그 환자의 특징을 외운다. 검증 폴드에 이미 본 환자가 있으니 점수가 부풀려지고, 처음 보는 환자만 오는 실전에서 급락한다. 진단의 열쇠는 GroupKFold로 다시 재보는 것이다. 환자 단위로 폴드를 나눈 뒤에도 0.94가 유지되면 원인은 다른 데 있고, 0.7대로 떨어지면 그룹 누수가 맞다. 분포가 다르다는 설명도 가능하지만 이 한 번의 재측정으로 두 가설이 갈린다. 같은 구조가 사용자 세션, 문서에서 잘라낸 문장, 같은 기기에서 나온 로그에서도 반복되므로, 데이터를 받으면 무엇이 반복 관측의 단위인지부터 확인해야 한다.

이 내용 다시 읽기 →
분석과 판단진단
모델 A와 B의 0.01 차이가 진짜인지 확인하려고 5-폴드를 10-폴드로 올렸다. 학습 시간은 두 배가 됐는데 시드를 바꿔가며 잰 평균 점수의 흔들림은 거의 그대로다. Var((1/K)Σsᵢ) = σ²/K + ((K-1)/K)ρσ²

K를 늘렸는데도 평균이 더 안정되지 않은 이유는 무엇인가?

  1. K를 늘리면 편향만 줄어들고 분산은 원래 줄지 않는다.

    ρ가 0이면 첫 항이 σ²/K로 곧장 줄어든다. 독립이기만 하면 K를 키운 만큼 분산이 내려가므로 원래 줄지 않는다는 진술은 틀렸다.

  2. 10-폴드는 검증셋이 작아 점수 하나하나가 더 불안정해진다.

    개별 점수의 분산 σ²이 커지는 것은 사실이지만 식에서 그 항은 K로 나뉘어 완화된다. 바닥을 만드는 것은 나뉘지 않는 둘째 항이다.

  3. 정답훈련셋이 서로 크게 겹쳐 점수들이 함께 움직이기 때문이다.

    10-폴드에서는 회차마다 훈련셋의 90%가 같아 점수들의 상관 ρ가 커진다. 그러면 둘째 항이 ρσ²로 수렴해 K를 아무리 키워도 그 아래로 내려가지 않는다. 계산 비용만 K배로 드는 구간이 생긴다.

  4. cross_val_score가 폴드 수와 무관하게 같은 시드를 쓰기 때문이다.

    시드를 바꿔가며 쟀다고 이미 서술돼 있다. 게다가 시드가 같다면 흔들림 자체를 관측할 수 없었을 것이다.

해설

K개 점수의 평균이 안정적이려면 그 점수들이 서로 독립이어야 한다. K를 키우면 회차마다의 훈련셋이 거의 같아져 점수들이 함께 움직이고, 평균의 분산은 뒷항이 ρσ²로 수렴해 더 내려가지 않는다. LOO가 그 극단이다. 훈련셋들이 한 건씩만 다르니 ρ가 1에 가깝고, 데이터가 1만 건이면 모델을 1만 번 학습해야 한다. 점수를 더 안정시키려면 K를 키우는 대신 서로 다른 시드로 K-Fold를 여러 번 반복하는 편이 낫다. 반복마다 폴드 구성이 새로 짜여 덜 겹치는 점수를 모으게 되고, 유방암 데이터에서 5-폴드 한 번의 2.0%p 흔들림이 10회 반복으로 0.8%p까지 줄어든다. 모델 두 개의 0.01 차이를 판별하려면 이 정도가 필요하다.

이 내용 다시 읽기 →
분석과 판단판단
개발셋에서 교차 검증으로 모델 A를 고르고 잠가둔 테스트셋에서 재니 0.81이 나왔다. 기대보다 낮아 개발셋으로 돌아가 모델 B를 고른 뒤 다시 테스트했더니 0.86이었고, 이 값을 최종 성능으로 보고했다.

이 절차의 문제는 무엇인가?

  1. 모델 B가 A보다 나은지 통계적으로 검정하지 않았다.

    검정을 붙여도 두 값 모두 같은 테스트셋에서 나왔다는 사실은 그대로다. 절차가 이미 어긋난 상태에서 유의성을 재는 것은 순서가 뒤바뀐 일이다.

  2. 테스트셋이 15%로 작아 점수가 불안정하다.

    크기를 키우면 각 점수의 흔들림은 줄지만 두 번 들여다본 데서 오는 치우침은 남는다. 정밀도와 절차의 정당성은 다른 문제다.

  3. 문제없다. 두 모델 모두 개발셋에서 골랐으므로 테스트셋은 여전히 독립이다.

    A에서 B로 갈아탄 결정의 근거가 바로 테스트 점수 0.81이었다. 그 순간 테스트셋이 선택에 개입했으므로 독립이 아니다.

  4. 정답테스트셋이 선택에 쓰이는 순간 검증셋이 된다.

    0.86은 두 후보 가운데 이 테스트셋에서 더 잘 나온 쪽을 고른 값이라 낙관적으로 치우쳐 있다. 후보를 더 늘릴수록 이 치우침이 커지고, 테스트셋이 원래 갖고 있던 값어치는 사라진다.

해설

테스트셋의 값어치는 어떤 선택에도 쓰이지 않았다는 사실 하나에서 나온다. 점수가 마음에 안 들어 다른 모델로 바꾼 순간 그 선택의 근거가 테스트 점수가 되고, 테스트셋은 검증셋으로 강등된다. 두 번째로 얻은 0.86은 두 후보 중 이 데이터에서 더 잘 나온 쪽을 고른 값이라 낙관적으로 치우쳐 있고, 후보를 늘릴수록 치우침이 커진다. 올바른 절차는 처음에 테스트셋을 떼어 잠가두고, 남은 개발셋 안에서만 교차 검증으로 모델과 하이퍼파라미터를 고르고, 결정이 끝나면 개발셋 전체로 재학습한 뒤 마지막에 한 번만 평가하는 것이다. 그 숫자가 기대에 못 미쳐도 그것이 보고할 값이고, 되돌아가고 싶다면 새 테스트셋을 확보해야 한다.

이 내용 다시 읽기 →

하이퍼파라미터 튜닝, Grid Search보다 Random Search가 나은 이유

원문 읽기 →
개념 확인구조

파라미터와 하이퍼파라미터를 가르는 기준은 무엇인가?

  1. 값의 개수다. 파라미터는 수백만 개지만 하이퍼파라미터는 열 개 안팎이다.

    대체로 그런 경향이 있을 뿐 기준이 되지는 못한다. 결정 트리의 파라미터는 몇십 개뿐일 수 있고, 신경망의 하이퍼파라미터는 층 수와 각 층의 폭까지 세면 수십 개가 된다.

  2. 값의 종류다. 파라미터는 실수이고 하이퍼파라미터는 정수나 문자열이다.

    learning_rate와 subsample은 실수인 하이퍼파라미터다. 자료형은 탐색할 때 분포를 고르는 데 쓰일 뿐 둘을 가르지 않는다.

  3. 최적화 대상 여부다. 하이퍼파라미터는 최적화하지 않고 기본값을 쓴다.

    탐색기를 붙여 최적화하는 대상이 바로 하이퍼파라미터다. 자동으로 최적화되지 않을 뿐 최적화하지 않는 값은 아니다.

  4. 정답정해지는 시점이다. 학습 중에 모델이 찾는 쪽이 파라미터다.

    파라미터는 학습 과정에서 옵티마이저가 데이터를 보며 찾고, 하이퍼파라미터는 학습을 시작하기 전에 사람이 못박는다. 선형 회귀의 가중치는 앞이고 랜덤 포레스트의 트리 개수는 뒤다.

해설

두 개념을 가르는 것은 정해지는 시점이다. 파라미터는 학습 과정에서 옵티마이저가 데이터를 보며 찾는 값이라 선형 회귀의 가중치, 신경망 각 층의 가중치 행렬, 결정 트리 각 노드의 분기 기준이 여기 속한다. 하이퍼파라미터는 학습을 시작하기 전에 사람이 정하는 값이고 학습 절차 자체를 규정한다. 실질적인 차이는 자동 장치가 있느냐다. 파라미터에는 경사 하강법 같은 옵티마이저가 있지만 하이퍼파라미터에는 그런 장치가 없어서, 값을 넣고 학습시키고 점수를 보는 절차를 사람이 반복해야 한다. 탐색 전략이라는 것이 필요해지는 지점이 바로 여기이고, 그래서 튜닝은 알고리즘 문제가 아니라 예산 배분 문제가 된다.

이 내용 다시 읽기 →
상황 적용판단
하이퍼파라미터가 두 개인데 그중 하나만 성능을 실제로 좌우한다. 어느 쪽이 중요한지는 미리 모른다. 학습 9회라는 같은 예산을 3 × 3 격자와 9회 무작위 샘플링에 각각 썼다.

중요한 축에서 시도하게 되는 서로 다른 값은 각각 몇 개인가?

  1. 둘 다 9개다. 예산이 같으므로 시도한 값의 개수도 같다.

    격자는 같은 값을 되풀이한다. 중요한 축의 세 값 각각을 덜 중요한 축의 세 위치에서 세 번씩 시도하므로 서로 다른 값은 셋뿐이다.

  2. 정답격자 3개, 무작위 9개다. 격자는 나머지 여섯 번을 이미 본 값의 반복에 쓴다.

    격자를 중요한 축으로 사영하면 점 아홉 개가 세 자리에 겹쳐 쌓인다. 무작위는 매번 새 값을 뽑으므로 어느 축으로 사영해도 아홉 개가 흩어져 남는다.

  3. 격자 9개, 무작위 3개다. 격자가 공간을 빠짐없이 덮기 때문이다.

    격자가 덮는 것은 두 축의 조합 아홉 개이지 한 축의 값 아홉 개가 아니다. 사영해서 세어보면 방향이 정반대다.

  4. 격자 3개, 무작위 3개다. 무작위도 결국 비슷한 영역에 몰린다.

    연속 분포에서 뽑으면 같은 값이 두 번 나올 확률이 사실상 0이다. 몰릴 수는 있어도 값 자체는 아홉 개가 모두 다르다.

해설

격자는 모든 축을 동등하게 취급한다. 3 × 3 격자에서 중요한 축의 값은 세 개뿐이고, 나머지 여섯 번의 학습은 이미 본 세 값을 덜 중요한 축의 다른 위치에서 되풀이하는 데 쓰인다. 무작위 샘플링은 아홉 번 모두 다른 값을 뽑으므로 어느 축으로 사영해도 아홉 개가 남는다. Bergstra와 Bengio가 2012년에 정리한 관찰이 이것이다. 탐색 공간의 명목 차원이 높아도 성능을 실제로 좌우하는 유효 차원은 낮고, 그 축이 어디인지는 미리 모른다. 격자는 그 축을 맞히지 못하면 예산을 통째로 낭비하지만 무작위는 어느 축이든 시도 횟수만큼을 남긴다. 실전 기준은 단순하다. 파라미터가 셋 이하이고 후보 값이 명확하면 격자, 그 밖에는 무작위가 기본이다.

이 내용 다시 읽기 →
상황 적용함정
trial.suggest_float('lr', 1e-3, 1.0)             # 균등
trial.suggest_float('lr', 1e-3, 1.0, log=True)   # 로그 균등

# 학습률이 쓸모 있는 영역은 대개 0.001~0.1 사이다

위쪽처럼 균등 분포로 학습률을 뽑으면 무엇이 문제인가?

  1. 균등 분포는 음수를 뽑을 수 있어 학습률로 쓸 수 없다.

    하한이 1e-3으로 지정돼 있으므로 음수가 나올 수 없다. 범위 자체는 정상이고 문제는 그 안에서 표본이 놓이는 위치다.

  2. 샘플이 0.001 근처에 몰려 큰 값을 시도하지 못한다.

    방향이 반대다. 균등 분포에서는 큰 값 쪽이 넓은 구간을 차지하므로 절반이 0.5 이상에서 나오고, 작은 값 쪽이 비어버린다.

  3. 정답0.001에서 0.01 구간이 전체 범위의 1%도 되지 않아 샘플이 거의 떨어지지 않는다.

    그 구간의 폭은 0.009이고 전체 범위는 약 1.0이라 비중이 0.9%다. 쓸모 있는 영역의 상당 부분에 표본이 닿지 않는 채로 예산의 절반이 0.5 이상에서 소모된다.

  4. log=True를 주면 탐색 범위가 1e-3에서 1.0이 아니라 -3에서 0으로 바뀐다.

    log=True는 뽑는 방식만 바꾸고 반환되는 값은 여전히 1e-3에서 1.0 사이다. 로그 공간에서 균등하게 뽑아 원래 스케일로 되돌릴 뿐이다.

해설

0.001에서 1.0까지 균등하게 뽑으면 0.001에서 0.01 사이가 전체 범위의 1%도 되지 않아 표본이 거의 떨어지지 않고, 절반은 0.5 이상에서 나온다. 학습률이 실제로 쓸모 있는 영역이 대개 0.001에서 0.1 사이라는 점을 생각하면 예산의 대부분을 쓸모없는 구간에 버리는 셈이다. 로그 균등으로 바꾸면 자릿수마다 비슷한 밀도로 탐색하므로 세 자릿수에 걸친 이 범위에서 각 자릿수가 약 3분의 1씩을 가져간다. 학습률, 규제 강도, weight decay처럼 여러 자릿수에 걸친 값은 예외 없이 로그다. 탐색기를 무엇으로 고르든 이 설정이 틀려 있으면 좋은 영역에 표본이 닿지 않으므로, 탐색 공간의 설계가 탐색기의 선택보다 앞선다.

이 내용 다시 읽기 →
상황 적용진단
RandomizedSearchCV(n_iter=200, cv=5)를 돌려 best_score_ = 0.912를 얻었고 이 값을 최종 성능으로 보고했다. 배포 후 실제 성능은 0.87 언저리에 머문다. 데이터 분포에는 변화가 없다.

이 간격을 가장 잘 설명하는 것은 무엇인가?

  1. n_iter=200으로는 부족해 최적 조합을 아직 찾지 못했다.

    더 좋은 조합을 못 찾았다면 보고 점수가 낮게 나와야 한다. 여기서는 보고 점수가 실제보다 높으므로 탐색 부족으로는 방향이 맞지 않는다.

  2. best_score_는 훈련 점수라 검증 점수와 다른 값이다.

    best_score_는 최적 조합의 교차 검증 평균 점수다. 훈련 점수라면 애초에 CV를 돌릴 이유가 없다.

  3. 정답200개 중 검증 폴드에서 가장 잘 나온 하나를 골랐으므로 낙관적 편향이 섞여 있다.

    고르는 데 쓴 점수를 성능으로 보고한 것이라 검증 데이터에 맞춘 만큼이 그 값에 실려 있다. 조합을 많이 시도할수록 편향이 커지므로 n_iter를 200으로 올린 것이 이 간격도 함께 키웠다.

  4. cv=5는 폴드가 적어 점수가 불안정하다.

    불안정하면 위아래로 흔들려야 하는데 한 방향으로만 높게 나왔다. 흔들림이 아니라 치우침이므로 폴드 수로는 설명되지 않는다.

해설

best_score_는 하이퍼파라미터를 고르는 데 이미 쓰인 점수다. 수백 개 조합 가운데 검증 폴드에서 가장 잘 나온 하나를 골랐으니 그 값에는 검증 데이터에 맞춘 만큼의 낙관적 편향이 섞여 있고, 조합을 많이 시도할수록 편향이 커진다. Nested CV는 고르는 루프와 재는 루프를 분리해 이 편향을 없앤다. 바깥 5-Fold가 평가를 맡고 안쪽 3-Fold가 튜닝을 맡으면 바깥 Fold마다 조합을 새로 찾으므로, 각 테스트 점수는 자기 자신이 튜닝에 쓰이지 않은 값이 된다. 비용이 폴드 수의 곱이라 만만치 않으므로 실무에서는 역할을 나눈다. 성능을 보고하고 모델 후보를 비교할 때는 Nested CV를 쓰고, 배포할 최종 모델은 전체 데이터에 일반 CV로 튜닝해 학습한다.

이 내용 다시 읽기 →
분석과 판단확장적용
HalvingRandomSearchCV를 factor=3으로 돌리면 라운드가 이렇게 진행된다. 라운드 남은 후보 후보당 데이터 1 100 1배 2 34 3배 3 12 9배 4 4 27배

이 절약이 성립하려면 무엇이 참이어야 하는가?

  1. 후보 수가 factor의 거듭제곱이어야 라운드가 깔끔하게 나뉜다.

    100은 3의 거듭제곱이 아닌데도 34, 12, 4로 잘 나뉜다. 나머지는 올림으로 처리되므로 후보 수에 제약이 없다.

  2. 정답적은 자원에서의 순위가 자원을 늘린 뒤에도 대체로 유지된다는 것이다.

    1라운드에서 하위 66개를 버리는 판단이 전체 데이터로 학습했을 때의 순위와 어긋나면 좋은 후보를 초반에 잃는다. 이 방법이 아끼는 계산은 전부 그 가정 위에 서 있다.

  3. 모든 후보가 같은 자원에서 평가되어야 비교가 공정하다는 것이다.

    자원을 다르게 주는 것이 이 방법의 핵심이다. 같은 자원을 주면 그냥 무작위 탐색이 되고 절약할 것이 없어진다.

  4. 탐색 공간이 연속이어야 자원을 단계적으로 나눌 수 있다는 것이다.

    나누는 대상은 탐색 공간이 아니라 학습에 쓰는 데이터나 반복 횟수다. 범주형 하이퍼파라미터만 있어도 이 방법은 그대로 작동한다.

해설

Successive Halving은 후보 전부를 적은 자원으로 한 번씩 평가하고 하위를 탈락시키면서 살아남은 후보에만 자원을 늘려준다. 이 절약이 성립하려면 전제가 하나 필요하다. 적은 자원에서 나쁘게 나온 후보가 자원을 늘려도 여전히 나쁠 것이라는 가정이다. 이 가정이 깨지는 경우가 실제로 있다. 규제가 약한 조합은 데이터가 적을 때 과적합으로 밀렸다가 데이터가 늘면 앞서기도 하고, 학습률이 작은 설정은 라운드를 적게 주면 아직 수렴하지 못한 상태로 평가된다. 그래서 초기 자원을 너무 작게 잡으면 좋은 후보를 1라운드에서 잃고, min_resources를 지나치게 낮추지 않는 것이 실무의 방어책이다. 예산이 빠듯할 때 쓰는 방법이지 기본값으로 삼을 방법은 아니다.

이 내용 다시 읽기 →
분석과 판단판단
학습 한 번에 3초 걸리는 랜덤 포레스트에 Optuna를 붙였더니 같은 시도 횟수에서 Random Search와 최종 점수가 거의 같았다. 반면 학습 한 번에 40분 걸리는 모델에서는 Optuna가 훨씬 적은 시도로 같은 점수에 도달했다.

이 차이를 만드는 것은 무엇인가?

  1. 랜덤 포레스트는 하이퍼파라미터가 적어 대리 모델이 학습되지 않는다.

    축이 두세 개여도 대리 모델은 정상적으로 세워진다. 오히려 저차원일수록 대리 모델이 목적 함수를 더 잘 흉내 낸다.

  2. TPE 샘플러가 트리 계열 모델에는 맞지 않는다.

    샘플러가 다루는 것은 하이퍼파라미터 공간이지 모델의 내부 구조가 아니다. 어떤 모델을 튜닝하든 같은 방식으로 작동한다.

  3. 시도 횟수가 같으면 어떤 탐색기를 써도 결과가 같아진다.

    40분짜리 모델에서 Optuna가 더 적은 시도로 같은 점수에 닿았다는 서술이 이 진술을 반박한다. 갈림은 탐색기가 아니라 시도 하나의 가격에서 온다.

  4. 정답베이지안 최적화가 아끼는 것은 시도 하나다. 학습이 싸면 아낄 것이 적다.

    3초짜리 학습이라면 그냥 무작위로 300번 돌리는 편이 대리 모델을 세우고 제안을 받는 것보다 빠르고, 샘플러 자체의 오버헤드가 학습 시간을 넘어서기도 한다. 시도 하나가 비쌀수록 이 방법의 이점이 커진다.

해설

베이지안 최적화가 아끼는 것은 시도 횟수다. 지금까지의 결과로 대리 모델을 세우고 획득 함수가 다음 지점을 고르므로, 무작위로 뽑는 것보다 적은 시도로 좋은 영역에 닿는다. 그런데 이 이점은 시도 하나가 비쌀 때만 값을 한다. 학습이 3초면 무작위로 여러 번 돌리는 편이 대리 모델을 세우고 제안을 받는 것보다 빠르고, 샘플러 자체의 오버헤드가 학습 시간을 넘어서기도 한다. 함께 짚어둘 것이 병렬화다. 무작위 탐색은 시도가 서로 독립이라 코어 수만큼 그냥 나눠 돌릴 수 있지만, 베이지안은 앞의 결과를 봐야 다음을 고르므로 완전한 병렬화가 어렵다. 코어가 넉넉하고 학습이 싸다면 무작위 쪽이 실측 시간에서 앞서는 경우가 흔하다.

이 내용 다시 읽기 →

학습 곡선과 오차 분석으로 다음에 무엇을 할지 정한다

원문 읽기 →
개념 확인구조
학습 곡선을 그렸더니 훈련 에러 18%, 검증 에러 20%에서 두 곡선이 만나 평평해졌다. 훈련 데이터를 두 배로 늘려도 두 값이 그대로다.

이 모델은 어떤 상태인가?

  1. 정답높은 편향이다. 두 곡선이 높은 곳에 붙어 훈련 세트조차 못 맞힌다.

    훈련 에러가 18%라는 것은 모델이 이미 본 데이터에서도 다섯 개 중 하나를 틀린다는 뜻이다. 표현력이 모자라 아직 배우지 못한 상태이고, 두 곡선이 수렴해 평평해진 것이 그 신호다.

  2. 높은 분산이다. 검증 에러가 훈련 에러보다 높기 때문이다.

    검증 에러가 더 높은 것은 거의 언제나 그렇다. 분산을 가르는 것은 높낮이가 아니라 간격의 크기인데 여기서는 2%p에 그친다.

  3. 이상적이다. 두 곡선의 간격이 2%p로 좁게 만났다.

    간격은 좁지만 만나는 높이가 20%다. 이상적인 곡선은 낮은 에러에서 좁은 간격으로 만나야 하므로 높이 조건이 빠져 있다.

  4. 데이터가 부족하다. 두 배로는 모자라니 열 배로 늘려야 한다.

    두 곡선이 이미 수렴해 평평해졌다는 것이 데이터를 늘려도 소용없다는 증거다. 늘려서 좁힐 간격 자체가 남아 있지 않다.

해설

학습 곡선을 읽을 때 보는 것은 두 곡선의 높이와 간격 둘뿐이다. 여기서는 둘 다 높은 곳에 붙어 있고 데이터를 늘려도 움직이지 않으므로 모델의 표현력이 모자란 상태다. 이 진단에서 곧장 결론이 나온다. 데이터를 더 모으는 것은 도움이 되지 않고 손댈 곳은 모델 복잡도, 규제 완화, 피처 추가 쪽이다. 여기서 놓치기 쉬운 것이 피처다. 모델이 단순해서가 아니라 입력에 신호가 없어서 못 맞히는 경우가 많고, 그럴 때는 모델을 XGBoost로 갈아 끼우는 것보다 상호작용 항이나 도메인 피처를 넣는 쪽이 효과가 크다. 신경망이라면 층과 뉴런을 키우는 것과 더 오래 학습시키는 것을 먼저 구분해야 한다.

이 내용 다시 읽기 →
상황 적용판단
인간 수준 에러가 1%인 문제에서 두 모델을 재봤다. 사례 A 사례 B 훈련 에러 5% 8% 검증 에러 10% 9% 회피 가능한 편향 4% 7% 분산 5% 1%

검증 에러가 10%와 9%로 비슷한데 다음 수가 정반대로 갈리는 이유는 무엇인가?

  1. A의 검증 에러가 1%p 더 높아 더 나쁜 모델이므로 손볼 곳이 많다.

    1%p 차이는 두 모델의 우열을 가르기에도 미미하고, 무엇을 고칠지를 정해주지도 않는다. 처방을 가르는 것은 검증 에러의 크기가 아니라 그것이 어떤 성분으로 쪼개지느냐다.

  2. B는 훈련 에러가 8%로 높아 학습이 덜 됐을 뿐이라 에포크를 늘리면 A와 같아진다.

    학습이 덜 된 것과 표현력이 모자란 것은 다르다. 에포크로 해결되는 경우도 있지만 그 판단은 훈련 에러가 아직 내려가는 중인지를 보고 해야 하고, 처방이 A와 같아질 이유도 없다.

  3. 정답A는 분산이 5%로 크고 B는 회피 가능한 편향이 7%로 커서 성분이 다르다.

    A는 외운 쪽이 문제라 규제를 올리거나 데이터를 더 모아야 하고, B는 아직 못 배운 쪽이 문제라 모델을 키워야 한다. 두 처방이 정확히 반대여서 진단 없이 고르면 절반의 확률로 뒷걸음질이 된다.

  4. 인간 수준 에러가 1%로 같으므로 사실 두 사례의 처방도 같다.

    기준점이 같다는 것은 두 사례를 같은 자로 잴 수 있다는 뜻일 뿐이다. 그 자로 재고 나면 오히려 차이가 선명해진다.

해설

검증 에러 하나만 보면 두 사례가 거의 같아 보인다. 인간 수준 에러를 기준점으로 놓고 쪼개면 완전히 다른 그림이 나온다. 회피 가능한 편향은 훈련 에러에서 인간 수준 에러를 뺀 값이고 분산은 검증 에러에서 훈련 에러를 뺀 값인데, A는 4%와 5%, B는 7%와 1%다. A는 외운 쪽이 문제라 규제와 데이터로 잡고 B는 못 배운 쪽이 문제라 모델을 키운다. 두 처방이 정확히 반대라서 진단 없이 하나를 고르면 절반의 확률로 뒷걸음질이 된다. 여기서 기준점의 값어치가 드러난다. 인간 수준 성능이나 단순 모델 점수가 없으면 훈련 에러 5%가 좋은 값인지 나쁜 값인지 말할 방법이 없고, 편향과 분산을 숫자로 나눌 수도 없다.

이 내용 다시 읽기 →
상황 적용진단
환자의 퇴원 여부를 예측하는 모델이 검증 정확도 99%를 낸다. 전처리는 Pipeline으로 감싸 폴드 안에서만 학습시켰다. 피처 목록은 나이, 진단코드, 입원일수, 퇴원처리일자다.

가장 먼저 의심해야 할 것은 무엇인가?

  1. 정답퇴원처리일자는 예측 시점에 알 수 없는 값이다.

    퇴원이 일어난 뒤에야 생기는 값이므로 모델은 그 열 하나만 보고 정답을 맞힌다. 배포하면 그 칸이 비어 있어 아무것도 하지 못하고, 검증 점수가 유난히 좋을 때 가장 먼저 볼 자리가 여기다.

  2. 입원일수와 나이의 상관이 높아 다중공선성이 생겼다.

    다중공선성은 계수 해석을 흔들 뿐 정확도를 99%까지 올려주지 않는다. 게다가 트리 계열이라면 영향이 거의 없다.

  3. 검증 세트가 작아 99%가 우연히 나왔다.

    우연이라면 폴드마다 값이 크게 흔들려야 한다. 교차 검증에서 안정적으로 99%가 나온다면 우연으로 설명되지 않는다.

  4. 진단코드의 카디널리티가 높아 과적합됐다.

    과적합이면 훈련 점수만 높고 검증 점수는 떨어져야 한다. 검증에서 99%가 나오는 것은 과적합이 아니라 정답이 새어 들어왔다는 신호다.

해설

타깃 누수는 정답에서 파생된 정보가 피처에 섞여 들어간 상태다. 퇴원처리일자는 퇴원이 일어난 뒤에야 생기는 값이라 예측하려는 시점에는 존재하지 않는다. 모델은 그 열 하나로 정답을 맞히므로 검증 정확도가 99%까지 오르고, 배포하면 그 값이 비어 있어 아무 쓸모가 없다. 전처리 누수와 결정적으로 다른 점은 파이프라인으로 막을 수 없다는 것이다. Pipeline은 폴드마다 훈련 부분으로만 fit해주지만 애초에 들어가면 안 되는 열을 걸러주지는 않는다. 방어는 사람이 한다. 피처 목록을 한 줄씩 훑으며 예측 시점에 이 값을 실제로 알 수 있는지 물어야 하고, 실전에서 가장 비싼 실패는 성능이 낮은 모델이 아니라 배포 전까지 성능이 높아 보였던 모델이다.

이 내용 다시 읽기 →
상황 적용적용
정확도 75%인 스팸 분류기의 오분류 100건을 유형별로 나눴다. 약물 광고 스팸 35건 35% 피싱 이메일 25건 25% 프로모션 이메일 20건 20% 비영어 스팸 15건 15% 기타 5건 5%

약물 광고 스팸을 완벽하게 잡는 피처를 만들면 무엇을 얼마나 얻는가?

  1. 35%p다. 정확도가 75%에서 110%가 되지는 않으니 100%로 올라간다.

    35%는 전체가 아니라 오분류 안에서의 비중이다. 단위를 오해하면 개선 폭을 네 배로 부풀려 잡게 된다.

  2. 정답오분류의 최대 35%이고, 정확도로는 약 8.75%p다.

    에러가 전체의 25%이고 그중 35%가 사라지므로 0.25 × 0.35 = 0.0875다. 이 표가 알려주는 것은 각 유형의 개선 상한이고, 비영어 스팸에 같은 노력을 쓰면 최대 15%까지만 열린다.

  3. 알 수 없다. 유형별 비중과 실제 개선 효과는 서로 무관하다.

    비중이 곧 상한이다. 그 유형을 완벽하게 잡아도 다른 유형의 오분류는 그대로 남으므로 35%를 넘을 수 없다.

  4. 100%다. 가장 큰 유형을 잡으면 나머지 유형도 함께 개선된다.

    유형이 다르다는 것은 실패 원인이 다르다는 뜻이다. 약물 키워드 피처가 비영어 스팸이나 피싱을 함께 잡아줄 이유가 없다.

해설

오차 분석 표가 알려주는 것은 각 유형의 개선 상한이다. 약물 광고 스팸이 오분류의 35%를 차지하므로 그 유형을 완벽하게 잡아도 줄일 수 있는 에러는 35%가 최대다. 여기서 단위를 헷갈리기 쉽다. 정확도가 75%면 에러가 25%이고 그중 35%가 사라지는 것이라 정확도 상승은 8.75%p다. 이 표의 값어치는 어디에 시간을 쓸지 정해준다는 데 있다. 비영어 스팸에 두 주를 쓰면 최대 15%까지지만 약물 광고 쪽에 쓰면 35%까지 열린다. 자동화된 지표로는 이 분해가 나오지 않으므로 틀린 사례를 직접 눈으로 넘겨보는 수밖에 없고, 그 과정에서 라벨 오류나 구별 불가능한 입력 같은 다른 문제도 함께 드러난다.

이 내용 다시 읽기 →
분석과 판단함정
이미지에는 회전과 좌우 반전, 텍스트에는 동의어 대체로 데이터를 늘려 효과를 봤다. 같은 방식으로 정형 데이터도 늘리려고 나이를 셋 올리고 소득을 5% 흔든 행을 만들어 붙이려 한다.

정형 데이터에서 이 방식이 위험한 이유는 무엇인가?

  1. 정답라벨이 보존된다는 보장이 없다. 흔든 행이 현실에 있을 조합인지 알 수 없다.

    이미지와 텍스트의 증강이 통하는 것은 뒤집힌 고양이가 여전히 고양이라는 불변성을 미리 알기 때문이다. 정형 데이터에는 기댈 불변성이 없어서, 나이와 근속연수처럼 묶인 열을 따로 흔들면 모순된 행이 만들어진다.

  2. 연속형 값을 변형하면 부동소수점 오차가 누적된다.

    5%를 곱하는 정도로 문제가 될 수치 오차는 생기지 않는다. 위험은 계산 정밀도가 아니라 만들어진 행의 의미에 있다.

  3. 행 수가 늘어 학습 시간과 교차 검증 비용이 커진다.

    비용은 늘지만 그것은 증강의 일반적인 대가이지 정형 데이터만의 문제가 아니다. 이미지 증강도 같은 비용을 치른다.

  4. 증강은 원본과 같은 분포를 만들 뿐이라 정보가 늘지 않아 언제나 무의미하다.

    이미지와 텍스트에서 실제로 효과를 본다고 문제에 이미 적혀 있다. 증강이 더하는 것은 새 정보가 아니라 모델이 무시해도 되는 변형에 대한 힌트다.

해설

증강이 통하는 근거는 변형이 라벨을 바꾸지 않는다는 사전 지식이다. 좌우로 뒤집힌 고양이는 여전히 고양이고 동의어로 바꾼 문장의 감성은 그대로라는 것을 우리가 알고 있어서 쓸 수 있다. 정형 데이터에는 기댈 불변성이 없다. 나이를 셋 올리고 소득을 5% 흔든 행이 현실에 존재할 수 있는 조합인지, 그 조합에서 라벨이 그대로인지 보장할 방법이 없고, 나이와 근속연수처럼 서로 묶인 열을 따로 흔들면 모순된 행이 만들어진다. 그래서 정형 데이터의 증강은 SMOTE처럼 소수 클래스의 결정 경계 근처를 메우는 좁은 용도로 쓴다. 그마저도 교차 검증 폴드 안에서 훈련 부분에만 적용해야 한다. 밖에서 미리 늘려두면 원본에서 파생된 행이 검증 폴드로 새어 든다.

이 내용 다시 읽기 →
분석과 판단판단
정형 데이터 3만 건짜리 새 프로젝트에서 처음부터 신경망을 붙들고 이 주째 학습 발산을 잡고 있다. 아직 어떤 모델로도 첫 검증 점수를 낸 적이 없다.

여기서 무엇이 잘못됐는가?

  1. 정형 데이터에는 신경망을 쓰면 안 되므로 XGBoost로 갈아타야 한다.

    정형 데이터에서 부스팅이 앞서는 경우가 많은 것은 맞지만 금지되는 것은 아니다. 10만 건이 넘으면 딥러닝도 시도할 만하고, 문제는 모델의 선택이 아니라 그것을 첫 모델로 삼았다는 순서에 있다.

  2. 학습률을 먼저 튜닝했어야 한다. 발산은 결국 학습률 문제다.

    발산을 잡는 것 자체가 목표가 되어버린 상황이다. 학습률을 맞춰 발산을 멈춰도 진단할 기준점이 없다는 문제는 그대로 남는다.

  3. 데이터 3만 건으로는 신경망이 발산하므로 데이터를 먼저 모아야 한다.

    발산은 데이터 양이 아니라 학습률이나 초기화, 스케일링에서 온다. 게다가 데이터를 더 모아야 하는지는 학습 곡선을 그려봐야 알 수 있고 지금은 그릴 점수조차 없다.

  4. 정답첫 모델의 목표는 점수가 아니라 끝까지 돌아가는 파이프라인이다.

    데이터를 읽고 나누고 학습시켜 점수를 기록하는 경로가 한 번은 완성돼야 진단이 시작된다. 학습 곡선에는 점수가, 오차 분석에는 예측이 필요한데 발산하는 모델은 둘 다 내놓지 못한다.

해설

첫 모델의 목표는 좋은 점수가 아니라 끝까지 돌아가는 파이프라인이다. 데이터를 읽고 나누고 학습시켜 점수를 기록하는 경로가 한 번이라도 완성돼야 그다음 진단이 가능해진다. 학습 곡선을 그리려면 점수가 필요하고 오차 분석을 하려면 예측이 필요한데 발산하는 모델에서는 둘 다 나오지 않는다. 이 주째 발산을 잡고 있다는 것은 아직 측정 체계가 없다는 뜻이고, 측정이 없으면 무엇을 고쳐야 할지 정할 방법도 없다. 로지스틱 회귀로 10분 만에 첫 점수를 내고 나면 그 숫자가 기준점이 되어 이후의 모든 변경을 견줄 수 있다. 정형 데이터에서 신경망이 나쁜 선택이라는 뜻은 아니다. 다만 그것이 첫 모델일 이유는 없다.

이 내용 다시 읽기 →

이 페이지는 문항 품질을 점검하는 용도로도 씁니다. 한 화면에서 전부 훑어야 중복된 문항, 쏠린 난이도, 소거법으로 풀리는 선택지가 보입니다.

Thank You for Visiting My Blog, Have a Good Day 😆