161강까지가 무엇을 해야 하는지였습니다. 이제 그것을 반복 가능하게 만듭니다.
자료 정제는 한 번 하고 끝나는 일이 아닙니다. 다음 달에 새 자료가 들어오고, 모형을 다시 학습하고, 누군가 결과를 재현해 달라고 합니다. 그때마다 사람이 눈으로 보고 판단하면 같은 자료에 다른 처리가 적용됩니다.
이 강의는 네 가지를 다룹니다.
| 물음 | 답의 형태 |
|---|---|
| 순서를 바꾸면 결과가 달라지는가 | 대부분 달라집니다. 순서를 고정합니다 |
| 언제 계산해야 하는가 | 훈련부에서만 정하고 나머지에는 적용만 합니다 |
| 무엇을 검사해야 하는가 | 단계 사이마다 계약을 둡니다 |
| 어떻게 재현하는가 | 적합에서 정한 값과 검사 기록을 함께 남깁니다 |
둘째 줄이 가장 비싼 항목입니다. 문제 2에서 보겠지만, 라벨이 동전 던지기인 자료에서도 정확도 를 만들어 낼 수 있습니다. 오류가 아니라 순서 하나를 잘못 놓아서 생기는 일이며, 검증 성능이 좋게 나오므로 아무도 의심하지 않습니다.
문제. 결측이 퍼센트, 값 인 오염이 퍼센트 있는 자료입니다.
(1) 이상치 제거와 결측 대치의 순서를 바꿔 결과를 비교하세요.
(2) 같은 단계를 여러 번 적용하면 어떻게 되는지 확인하세요.
생각의 실마리. 두 단계 모두 자료 전체를 보고 값을 정합니다. 대치는 평균을 보고, 이상치 판정은 중앙값과 퍼짐을 봅니다. 앞 단계가 자료를 바꾸면 뒤 단계가 보는 값도 바뀝니다.
풀이. (1) 검산 결과입니다.
| 순서 | 남은 행 | 평균 | 대치에 쓴 값 |
|---|---|---|---|
| 이상치 제거 뒤 대치 | |||
| 대치 뒤 이상치 제거 |
두 결과의 평균 차이가 입니다. 참 평균이 인데 뒤쪽이 만큼 위로 밀려 있습니다.
이유는 대치에 쓴 값에 있습니다. 이상치를 먼저 빼면 평균이 인데, 빼지 않으면 값 이 섞여 가 됩니다. 그 값으로 개의 빈 칸을 채웠으니 오염이 칸으로 퍼진 것입니다.
행 수도 다릅니다. 순서를 바꾸면 남는 행이 개 차이 납니다.
어느 쪽이 옳은가는 자료가 정하지 않습니다. 다만 하나를 골라 고정해야 하며, 고정하지 않으면 같은 자료를 두 사람이 처리해 다른 답을 냅니다.
(2) 같은 단계를 반복 적용해 봅니다. 이상치가 하나도 없는 깨끗한 정규 자료에 시그마 제거를 여섯 번 반복합니다.
| 적용 횟수 | 남은 행 | 직전 대비 버린 행 | 처음 대비 남은 비율 |
|---|---|---|---|
첫 적용에서 개를 버립니다. 정규분포에서 시그마 밖은 약 퍼센트이므로 정상적인 값들입니다.
그런데 그것을 버리고 나면 표준편차가 줄어듭니다. 좁아진 기준으로 다시 재면 또 밖으로 나가는 점이 생기고, 그래서 두 번째에 개를 더 버립니다.
이 단계는 멱등이 아닙니다. 몇 번 돌렸는지가 결과를 바꾸므로, 실수로 두 번 실행하면 다른 자료가 나옵니다. 다섯 번째부터 멈추지만 그때는 이미 개를 버린 뒤입니다.
이 문제에서 배우는 것. 파이프라인의 단계는 순수 함수처럼 다뤄야 합니다. 순서를 바꿔도 되는지, 두 번 돌려도 되는지가 단계마다 정해져 있고 문서에 적혀 있어야 합니다. 그리고 멱등이 아닌 단계는 경계값을 밖에서 주입하는 방식으로 바꿀 수 있습니다. 훈련부에서 한 번 계산한 경계를 저장해 두고 그것으로 자르면, 몇 번을 돌려도 같은 결과가 나옵니다. 문제 4의 적합과 적용의 분리가 여기서도 답입니다.
바로 확인 1.
확인 1-1. 검산에서 순서를 바꾼 두 결과의 평균 차이를 쓰세요.
답. 이며 대치에 쓴 값이 와 로 달랐기 때문입니다.
확인 1-2. 멱등의 뜻을 식으로 쓰세요.
답. 이며 여러 번 적용해도 결과가 같다는 뜻입니다.
확인 1-3. 깨끗한 자료에서도 시그마 제거가 행을 버리는 이유를 쓰세요.
답. 정규분포에서 시그마 밖이 약 퍼센트이므로 정상값이 걸리기 때문입니다.
문제. 표본 개, 특징 개이고 라벨은 동전 던지기입니다. 참 정확도는 입니다.
(1) 특징을 나누기 전에 전체로 고른 경우의 교차검증 정확도를 구하세요.
(2) 각 폴드의 훈련부에서만 고른 경우와 비교하세요.
(3) 표준화에서도 같은 일이 일어나는지 확인하세요.
생각의 실마리. 라벨과 특징이 아무 관계도 없으므로 무엇을 해도 정확도는 근처여야 합니다. 그런데 특징이 개면, 순전히 우연으로 라벨과 잘 맞는 특징이 몇 개는 있습니다. 그 몇 개를 어디에서 골랐는지 생각해 봅니다.
풀이. (1)(2) 검산 결과입니다.
| 특징을 언제 고르는가 | 교차검증 정확도 | 참값 와의 차이 |
|---|---|---|
| 나누기 전에 전체로 | ||
| 각 폴드의 훈련에서만 |
라벨이 동전 던지기인데 정확도가 입니다.
무슨 일이 일어났는지 단계별로 봅니다.
| 단계 | 무슨 일 |
|---|---|
| 특징 개에서 라벨과 잘 맞는 개를 고릅니다 | 이때 모든 자료의 라벨을 봤습니다 |
| 자료를 다섯 조각으로 나눕니다 | 이미 늦었습니다 |
| 한 조각을 빼고 학습해 그 조각을 맞힙니다 | 그 조각의 라벨을 보고 뽑은 특징입니다 |
교차검증이 지켜야 할 것을 그 앞 줄에서 이미 어겼습니다. 검증 조각은 "본 적 없는 자료"여야 하는데, 특징 선택이 이미 그것을 봤습니다.
이 정직한 값입니다. 각 폴드의 훈련부에서만 고르면 정확도가 로 돌아옵니다. 두 방법의 차이 가 순전히 순서에서 나온 가짜 성능입니다.
(3) 표준화에서도 같은 일이 규모만 작게 일어납니다. 표본 개를 절반씩 나누고 번 되풀이했습니다.
| 표준화 기준 | 검증부 평균의 절댓값 |
|---|---|
| 전체로 계산 | |
| 훈련부로만 계산 |
전체로 계산하면 검증부의 평균이 에 배만큼 더 붙습니다. 검증부가 자기를 재는 기준을 만드는 데 참여했기 때문입니다.
실전에서는 검증부의 평균을 알 수 없습니다. 배포된 모형이 만나는 자료는 아직 존재하지 않으므로, 검증부가 훈련부의 기준으로 얼마나 어긋나는지가 진짜로 알고 싶은 값입니다. 전체로 표준화하면 그 어긋남이 절반으로 감춰집니다.
이 문제에서 배우는 것. 규칙은 하나입니다. 모든 값은 훈련부에서만 정하고 검증부에는 적용만 합니다. 그리고 이 규칙을 어기기가 아주 쉽습니다. 자료를 불러와서 정리하고 나누는 것이 자연스러운 작업 순서인데, 정리 안에 "전체를 보고 정하는 것"이 하나라도 있으면 그 순간 누출입니다. 160강 심화 4에서 본 누출이 시간이 아니라 분할을 넘어 일어나는 모습이며, 파이프라인을 쓰는 가장 큰 이유가 이 순서를 구조로 강제하는 것입니다.
바로 확인 2.
확인 2-1. 검산에서 두 방법의 정확도를 쓰세요.
답. 전체로 고르면 이고 훈련부에서만 고르면 입니다.
확인 2-2. 라벨이 무작위인데도 정확도가 높게 나온 이유를 쓰세요.
답. 검증 조각의 라벨까지 보고 우연히 잘 맞는 특징을 골랐기 때문입니다.
확인 2-3. 전처리 계산의 규칙을 한 줄로 쓰세요.
답. 모든 값은 훈련부에서만 정하고 나머지에는 적용만 합니다.
문제. 평균 , 표준편차 인 자료에 값 인 점이 하나 있습니다.
(1) 척도 변환 셋을 적용해 정상값이 어떻게 되는지 구하세요.
(2) 오른쪽으로 긴 꼬리를 가진 자료에 분포 변환을 적용하세요.
생각의 실마리. 척도 변환은 어떤 기준으로 나누느냐의 문제입니다. 161강 문제 4에서 본 것처럼 기준을 무엇으로 재느냐가 이상치 앞에서 갈립니다.
풀이. (1) 검산 결과입니다. 이상치 하나를 뺀 정상값만 봅니다.
| 변환 | 정상값의 범위 폭 | 정상값 표준편차 | 이상치의 값 |
|---|---|---|---|
| 표준화 | |||
| 최소최대 | |||
| 로버스트 |
최소최대가 가장 심하게 무너집니다. 정상값 개가 전부 구간 안에 들어갑니다.
분모를 이상치가 정하기 때문입니다. 가 이면 근처의 값들은 모두 근처가 되어 서로 구별되지 않습니다.
표준화는 그보다 낫지만 역시 눌립니다. 이상치가 없었다면 표준편차가 인데 있어서 이 되었고, 그만큼 정상값의 퍼짐이 로 줄었습니다.
로버스트 척도만 정상값의 퍼짐을 지킵니다. 중앙값과 사분위 범위로 나누므로 이상치가 분모에 들어가지 않으며, 정상값 표준편차 은 원래의 상대적 퍼짐 그대로입니다. 이상치는 라는 큰 값으로 남아 눈에 띄게 됩니다.
(2) 분포를 바꾸는 변환입니다.
| 변환 | 왜도 | 첨도 | 중앙값 |
|---|---|---|---|
| 원자료 | |||
| 로그 | |||
| 제곱근 |
로그가 왜도를 거의 으로 만듭니다. 원자료가 로그정규였으니 당연한 결과이며, 첨도도 정규의 값 에 맞습니다. 제곱근은 절반쯤만 고칩니다.
변환은 공짜가 아닙니다.
| 대가 | 내용 |
|---|---|
| 해석이 바뀝니다 | 로그를 취하면 계수의 뜻이 차이가 아니라 배수가 됩니다 |
| 정의역이 좁아집니다 | 이나 음수에는 로그를 쓸 수 없습니다 |
| 상수 선택이 결과를 바꿉니다 | 의 를 무엇으로 두느냐가 남습니다 |
셋째 줄이 조용한 함정입니다. 을 쓰는 관행이 있는데, 의 크기가 이면 을 더하는 것은 아무 일도 아니고 가 규모이면 전부를 바꿉니다. 단위에 따라 결과가 달라지는 처리는 단위를 함께 적어 두어야 합니다.
이 문제에서 배우는 것. 변환을 고르는 것은 무엇을 지킬지 고르는 일입니다. 최소최대는 범위를 로 지키는 대신 상대적 거리를 버리고, 로그는 대칭성을 얻는 대신 해석을 바꿉니다. 어느 것도 자료를 개선하지 않으며 성질을 맞바꿀 뿐입니다. 그래서 "일단 표준화하고 본다" 같은 기본값은 없고, 뒤에 무엇을 할지에 따라 앞의 변환이 정해집니다.
바로 확인 3.
확인 3-1. 최소최대가 이상치에 가장 약한 이유를 쓰세요.
답. 분모인 최댓값에서 최솟값을 뺀 값을 이상치가 직접 정하기 때문입니다.
확인 3-2. 검산에서 이상치가 표준화의 표준편차를 얼마나 키웠는지 쓰세요.
답. 에서 으로 약 배 키웠습니다.
확인 3-3. 에서 가 왜 문제인지 쓰세요.
답. 의 크기에 따라 의 영향이 완전히 달라지므로 단위에 따라 결과가 바뀝니다.
문제. 적재부터 재집계까지 다섯 단계의 파이프라인을 만듭니다.
(1) 단계마다 계약을 적고 위반을 검사하세요.
(2) 적합과 적용을 나누어 각 단계가 무엇을 저장해야 하는지 정하세요.
생각의 실마리. 160강 문제 3에서 조인 뒤 합계가 이상하면 먼저 행 수를 보라고 했습니다. 그것을 사람이 기억해서 하는 대신 파이프라인이 스스로 하게 만듭니다. 각 단계가 행 수를 어떻게 바꿔도 되는지를 미리 적어 두면 됩니다.
풀이. (1) 검산 결과입니다.
| 단계 | 행 | 열 | 결측 칸 | 계약 |
|---|---|---|---|---|
| . 적재 | 없음 | |||
| . 결측 행 제거 | 행 감소 허용 | |||
| . 파생 열 추가 | 행 불변 | |||
| . 배송 표 조인 | 행 불변 | |||
| . 주문별 재집계 | 행 감소 허용 |
계약 위반이 건입니다. 단계에서 행이 에서 로 늘었습니다.
이것이 160강 문제 3의 팬아웃입니다. 배송이 여러 건인 주문이 복제되어 행이 늘었고, 그 상태로 금액을 합하면 매출이 부풀어 있습니다.
검사 한 줄이 그 자리에서 잡았습니다. 계약이 없었다면 이 오류는 보고서의 숫자가 이상하다는 이야기가 나올 때까지 발견되지 않습니다.
검사를 파이프라인 밖이 아니라 단계 사이에 두는 이유가 이것입니다. 마지막에 한 번 검사하면 무엇이 틀렸는지는 알아도 어디에서 틀렸는지는 모릅니다.
(2) 각 단계를 적합과 적용으로 나눕니다.
| 단계 | 적합에서 정하는 값 | 적용에서 하는 일 |
|---|---|---|
| 결측 대치 | 훈련부의 평균 | 그 값으로 채웁니다 |
| 표준화 | 훈련부의 평균과 표준편차 | 그 값으로 나눕니다 |
| 범주 부호화 | 훈련부에 나온 범주 목록 | 없던 범주는 기타로 보냅니다 |
| 이상치 경계 | 훈련부의 중앙값과 MAD | 그 경계로 자릅니다 |
적합은 훈련부에서 한 번, 적용은 모든 자료에 합니다. 문제 2의 규칙이 구조로 굳어진 모습입니다.
그리고 적합에서 정한 값은 반드시 저장합니다. 배포된 모형이 새 자료 한 건을 받았을 때 그 한 건의 평균으로 표준화할 수는 없습니다. 훈련부에서 정한 값이 남아 있어야 같은 변환을 재현합니다.
셋째 줄이 실무에서 자주 터집니다. 훈련부에 없던 범주가 운영에서 들어오는데, 미리 정해 두지 않으면 그 자리에서 파이프라인이 멈추거나 조용히 이상한 값을 냅니다. 없던 범주를 어디로 보낼지는 적합할 때 미리 정하는 결정입니다.
이 문제에서 배우는 것. 파이프라인은 코드의 모음이 아니라 계약의 모음입니다. 각 단계가 무엇을 받아 무엇을 내놓는지, 행과 열과 결측이 어떻게 변해도 되는지가 정해져 있고 매 실행마다 검사됩니다. 158강 문제 5에서 스키마가 실행되는 계약이라고 한 것이 파이프라인 전체로 확장된 것이며, 검사하지 않는 계약은 없는 것과 같다는 결론도 그대로입니다.
바로 확인 4.
확인 4-1. 검산에서 계약을 위반한 단계와 그 이유를 쓰세요.
답. 단계 배송 표 조인이며 일대다 조인의 팬아웃으로 행이 에서 로 늘었습니다.
확인 4-2. 검사를 단계 사이에 두는 이유를 쓰세요.
답. 마지막에만 검사하면 어디에서 틀렸는지 알 수 없기 때문입니다.
확인 4-3. 적합에서 정한 값을 저장해야 하는 이유를 쓰세요.
답. 배포 뒤 새 자료 한 건에도 같은 변환을 재현해야 하기 때문입니다.
문제. 파이프라인이 믿을 만한지 세 가지로 검사합니다.
(1) 같은 입력에서 같은 출력이 나오는지 확인하세요.
(2) 자료 자체의 규칙을 검사하세요.
(3) 들어오는 자료의 분포가 움직였는지 감시하세요.
생각의 실마리. 파이프라인이 오류 없이 끝났다는 것은 아무것도 보장하지 않습니다. 161강까지 본 모든 사고가 오류 없이 일어났습니다. 끝났는지가 아니라 무엇이 나왔는지를 봐야 합니다.
풀이. (1) 같은 입력을 두 번 넣어 봅니다. 대치에 무작위 잡음을 쓰는 파이프라인입니다.
| 실행 | 검사값 | 첫 실행과 같은가 |
|---|---|---|
| 첫 실행 seed | 참 | |
| 다시 seed | 참 | |
| seed | 거짓 |
시드를 고정하면 같은 값이 나오고 바꾸면 달라집니다. 161강 문제 3의 확률적 회귀 대치처럼 무작위를 쓰는 단계가 있으면 시드가 파이프라인의 입력 중 하나입니다.
검사값을 남기는 것이 요점입니다. 출력 전체를 저장할 필요 없이 요약 하나만 남겨 두면, 코드를 고친 뒤 결과가 변했는지 한 줄로 알 수 있습니다. 바꾸려던 것만 바뀌었는지 확인하는 회귀 검사입니다.
(2) 자료 자체에 규칙을 겁니다. 158강 문제 5의 제약이 그대로 돌아옵니다.
| 규칙 | 위반 건수 |
|---|---|
| 기본키 유일성 | |
| 값이 음수가 아닐 것 | |
| 값이 이하일 것 | |
| 결측이 없을 것 |
위반이 있으면 파이프라인을 멈춥니다. 통과시키면 아래 모든 단계가 오염되고, 오염된 결과는 오염되었다고 표시되지 않습니다.
(3) 들어오는 자료가 예전과 같은지 봅니다. 훈련 때의 분포와 새 자료의 분포를 겹쳐 최대 차이를 잽니다.
| 새 자료 | 최대 분포 차이 | 판정 |
|---|---|---|
| 변화 없음 | 정상 | |
| 평균 이동 | 정상 | |
| 평균 이동 | 경보 | |
| 퍼짐 배 | 경보 |
두 누적분포의 세로 최대 간격이며, 평균이 움직이든 퍼짐이 움직이든 잡아냅니다.
기준을 무엇으로 둘지는 표본 크기가 정합니다. 자료가 아주 많으면 실무적으로 아무 의미 없는 차이도 통계적으로 유의해지므로, 검정의 값이 아니라 차이의 크기 자체로 기준을 잡습니다.
분포 이동이 왜 중요한가는 분명합니다. 모형은 훈련 자료의 분포를 가정하고 만들어졌으므로, 입력이 옮겨 가면 정확도가 조용히 떨어집니다. 라벨은 한참 뒤에야 들어오므로 성능이 나빠진 것을 아는 데 몇 달이 걸리는데, 입력 분포는 오늘 볼 수 있습니다.
이 문제에서 배우는 것. 파이프라인의 산출물은 자료만이 아닙니다. 적합에서 정한 값, 각 단계의 검사 기록, 출력의 검사값, 입력 분포의 요약이 함께 남아야 합니다. 161강 심화 5에서 민감도 분석과 처리 이력 기록을 이야기했는데, 그것이 가능하려면 이 기록들이 있어야 합니다. 왜 이 숫자가 나왔는지 나중에 되짚을 수 없는 파이프라인은 결과를 검증할 수 없고, 검증할 수 없는 결과는 결과가 아닙니다.
바로 확인 5.
확인 5-1. 파이프라인에 무작위 단계가 있을 때 무엇을 입력으로 취급해야 하는지 쓰세요.
답. 난수 시드를 입력으로 취급하고 기록해야 합니다.
확인 5-2. 검사값을 남기면 무엇을 할 수 있는지 쓰세요.
답. 코드를 고친 뒤 의도한 것만 바뀌었는지 한 줄로 확인할 수 있습니다.
확인 5-3. 분포 이동 감시가 라벨 기반 성능 감시보다 빠른 이유를 쓰세요.
답. 입력은 오늘 볼 수 있고 라벨은 한참 뒤에야 들어오기 때문입니다.
| 성질 | 뜻 | 어기면 |
|---|---|---|
| 결정성 | 같은 입력에 같은 출력 | 재현이 안 됩니다 |
| 멱등 | 실행 횟수가 결과를 바꿉니다 | |
| 교환 | 순서를 바꿔도 같음 | 순서를 고정해야 합니다 |
| 순수 | 밖의 상태를 안 봄 | 어제와 오늘 결과가 다릅니다 |
| 척도 변환 | 기준 | 이상치에 |
|---|---|---|
| 표준화 | 평균과 표준편차 | 약합니다 |
| 최소최대 | 최솟값과 최댓값 | 가장 약합니다 |
| 로버스트 | 중앙값과 사분위 범위 | 강합니다 |
| 순위 변환 | 순서만 | 가장 강하나 간격을 버립니다 |
| 검사 | 무엇을 보는가 | 언제 |
|---|---|---|
| 계약 검사 | 행, 열, 결측의 변화 | 단계마다 |
| 규칙 검사 | 유일성, 범위, 참조 | 적재 직후 |
| 회귀 검사 | 출력 검사값 | 코드를 고칠 때 |
| 분포 감시 | 입력의 누적분포 | 배치마다 |
| 자주 하는 실수 | 바로잡기 |
|---|---|
| 나누기 전에 표준화합니다 | 훈련부에서 적합하고 나머지에 적용합니다 |
| 나누기 전에 특징을 고릅니다 | 검산에서 정확도가 부풀었습니다 |
| 이상치 제거를 여러 번 돌립니다 | 멱등이 아니라 돌릴수록 깎입니다 |
| 최소최대로 이상치가 있는 자료를 씁니다 | 정상값이 전부 뭉칩니다 |
| 적합에서 정한 값을 안 남깁니다 | 배포 때 같은 변환을 못 만듭니다 |
| 마지막에만 검사합니다 | 어디에서 틀렸는지 모릅니다 |
| 무작위 시드를 안 적습니다 | 같은 결과가 두 번 안 나옵니다 |
문제 6. 멱등의 정의를 식으로 쓰세요.
답. 입니다.
문제 7. 검산에서 순서를 바꾼 두 결과의 평균과 차이를 쓰세요.
답. 과 이며 차이는 입니다.
문제 8. 순서에 따라 결과가 갈린 원인을 쓰세요.
답. 대치에 쓴 값이 이상치를 포함하느냐에 따라 와 로 달랐기 때문입니다.
문제 9. 깨끗한 정규 자료에 시그마 제거를 반복하면 몇 번째에 멈추는지 검산 값으로 쓰세요.
답. 다섯 번째부터 더 버리지 않으며 그때까지 개를 버렸습니다.
문제 10. 특징 선택 누출의 정확도를 검산 값으로 쓰세요.
답. 전체로 고르면 이고 훈련부에서만 고르면 입니다.
문제 11. 라벨이 무작위인데 정확도가 높게 나온 이유를 쓰세요.
답. 검증 조각의 라벨까지 보고 우연히 잘 맞는 특징을 골랐기 때문입니다.
문제 12. 표준화 누출의 크기를 검산 값으로 쓰세요.
답. 검증부 평균의 절댓값이 에서 으로 절반이 됩니다.
문제 13. 최소최대 변환이 검산에서 무엇을 만들었는지 쓰세요.
답. 정상값 개가 전부 안에 뭉쳤습니다.
문제 14. 로버스트 척도가 이상치에 강한 이유를 쓰세요.
답. 중앙값과 사분위 범위로 나누어 이상치가 분모에 들어가지 않기 때문입니다.
문제 15. 검산에서 로그 변환 뒤의 왜도와 첨도를 쓰세요.
답. 왜도 , 첨도 로 정규에 가깝습니다.
문제 16. 검산에서 계약을 위반한 단계와 행 수 변화를 쓰세요.
답. 단계 배송 표 조인이며 에서 로 늘었습니다.
문제 17. 적합과 적용을 나누는 이유를 쓰세요.
답. 훈련부에서만 값을 정하고 나머지에는 적용만 해야 누출이 없기 때문입니다.
문제 18. 분포 이동 감시에서 무엇을 재는지 식으로 쓰세요.
답. 두 누적분포의 세로 최대 간격 입니다.
심화 1. 파이프라인을 나무로 보는 관점을 정리하세요.
파이프라인을 단계의 줄로 생각하기 쉬운데, 실제로는 방향 있는 그래프입니다.
가지가 갈라지고 다시 합쳐집니다. 주문 표와 사용자 표가 각각 정제된 뒤 조인에서 만나므로, 어느 쪽이 먼저 끝나야 하는지가 그래프에 적혀 있습니다.
| 표현 | 얻는 것 |
|---|---|
| 의존 그래프 | 무엇을 다시 계산해야 하는지 압니다 |
| 위상 정렬 | 실행 순서가 정해집니다 |
| 순환 검사 | 서로를 기다리는 구조를 막습니다 |
첫째 줄이 실무의 이득입니다. 정제 단계 하나를 고쳤을 때 그 아래만 다시 돌리면 됩니다. 전체를 다시 돌리는 것과 몇 시간이 차이 납니다.
그리고 그래프가 있으면 병렬이 공짜로 나옵니다. 서로 의존하지 않는 가지는 동시에 돌려도 되며, 그 판정을 그래프가 대신합니다.
순환이 있으면 안 됩니다. 가 를 필요로 하고 가 를 필요로 하면 어느 것도 시작할 수 없습니다. 자료 파이프라인에서 순환은 대개 낟알을 잘못 잡은 신호이며, 두 단계가 사실은 하나여야 하는 경우가 많습니다.
심화 2. 배치와 스트리밍에서 같은 변환을 어떻게 맞추는지 정리하세요.
같은 특징을 두 번 구현하면 반드시 어긋납니다.
훈련은 배치로 하고 추론은 실시간으로 합니다. "지난 일 주문 건수" 같은 특징을 배치에서는 SQL로, 실시간에서는 다른 코드로 만들면, 미묘하게 다른 값이 나옵니다.
| 어긋나는 곳 | 예 |
|---|---|
| 경계 처리 | 일에 오늘을 넣는가 |
| 시간대 | 자정을 어디 기준으로 자르는가 |
| 늦게 도착한 자료 | 배치는 포함하고 실시간은 못 봅니다 |
이것을 훈련과 서빙의 어긋남이라 부릅니다. 검증 성능은 좋은데 실제 성능이 나쁜 흔한 원인이며, 모형이 아니라 특징이 다른 것이라 모형을 아무리 고쳐도 낫지 않습니다.
해법은 정의를 한 곳에 두는 것입니다. 특징 저장소가 그 역할을 하며, 배치와 실시간이 같은 정의를 읽어 각자 계산합니다.
어긋남을 재는 방법도 있습니다. 같은 시점의 자료를 두 경로로 각각 계산해 값을 비교하는 검사를 정기적으로 돌립니다. 문제 5의 회귀 검사를 두 구현 사이에 적용한 것입니다.
심화 3. 자료의 계보를 왜 기록하는지 정리하세요.
어떤 숫자가 이상할 때 물어야 할 것은 하나입니다. 이 값은 어디에서 왔는가.
계보는 그 답을 자동으로 만들어 둡니다.
| 방향 | 답하는 질문 |
|---|---|
| 거슬러 올라감 | 이 보고서 숫자는 어느 원본에서 왔는가 |
| 내려감 | 이 원본이 틀렸다면 무엇이 오염되었는가 |
둘째 줄이 사고가 났을 때 필요합니다. 원본 하나에 오류가 있었다는 것을 알았을 때, 그것을 쓴 모든 산출물을 찾아 다시 계산해야 합니다. 계보가 없으면 사람이 기억으로 찾으며, 반드시 빠뜨립니다.
개인정보 규정도 같은 것을 요구합니다. 어떤 사람의 자료를 지워 달라는 요청이 오면 그 자료가 흘러 들어간 모든 곳을 알아야 합니다.
계보는 열 수준까지 내려가야 쓸모가 있습니다. "이 표가 저 표에서 왔다"는 것만으로는 부족하고, 어느 열이 어느 열에서 어떤 변환으로 왔는지가 있어야 영향 범위를 좁힐 수 있습니다.
심화 4. 파이프라인을 어떻게 시험하는지 정리하세요.
코드를 시험하는 방법이 자료 파이프라인에도 그대로 옵니다.
| 시험 | 무엇을 보는가 |
|---|---|
| 단위 시험 | 단계 하나가 작은 입력에서 맞는 값을 내는가 |
| 속성 시험 | 어떤 입력에도 성립해야 할 성질이 성립하는가 |
| 골든 시험 | 정해 둔 입력에서 정해 둔 출력이 나오는가 |
둘째 줄이 자료 처리에 특히 잘 맞습니다. 구체적인 예를 만드는 대신 성질을 적습니다.
문제 4의 계약이 바로 이 성질이며, 무작위로 만든 입력 수백 개에 대해 자동으로 검사할 수 있습니다.
골든 시험에는 함정이 하나 있습니다. 출력이 바뀌면 시험이 깨지는데, 의도한 변경일 때 골든 파일을 그냥 갱신하는 습관이 들면 시험이 아무 일도 하지 않게 됩니다. 갱신할 때마다 무엇이 왜 바뀌었는지 확인하는 것이 시험의 실체입니다.
시험용 자료를 만드는 것도 일입니다. 운영 자료를 그대로 쓰면 개인정보 문제가 생기고, 완전히 무작위로 만들면 실제 자료의 지저분함이 빠집니다. 실제 자료에서 구조만 뽑아 값을 바꾼 합성 자료가 절충안입니다.
심화 5. 파이프라인을 다시 돌릴 수 있게 만드는 조건을 정리하세요.
과거 시점의 결과를 다시 만들 수 있어야 합니다. 잘못을 고쳤을 때, 감사를 받을 때, 논문을 재현할 때 필요합니다.
셋이 모두 필요하며 자료가 가장 어렵습니다.
| 요소 | 고정하는 방법 |
|---|---|
| 코드 | 버전 관리의 커밋 |
| 설정 | 설정 파일도 함께 버전 관리 |
| 자료 | 덮어쓰지 않고 시점별로 쌓음 |
셋째 줄이 설계를 바꿉니다. 원본 표를 그때그때 갱신하면 어제의 결과를 다시 만들 수 없습니다. 대신 들어온 자료를 시점과 함께 쌓아 두고 파이프라인이 시점을 인자로 받으면, 언제의 상태든 재현됩니다.
저장 비용이 그 대가입니다. 그런데 자료 저장은 싸고 재현 불가능한 사고는 비쌉니다.
160강 심화 4의 시점 기준 조인이 여기에 기댑니다. 예측 시점의 상태를 알려면 그 시점의 자료가 남아 있어야 하며, 원본을 갱신해 버린 시스템에서는 누출 없는 훈련 자료를 만들 방법이 아예 없습니다.
심화 6. 이 강의가 어디에 쓰이는지 정리하세요.
| 분야 | 쓰임 | 이어지는 강의 |
|---|---|---|
| 기술통계 | 정제된 자료에서 요약을 냅니다 | 163강 |
| 탐색과 시각화 | 같은 그림을 다시 그릴 수 있습니다 | 168강 |
| 조사 설계 | 무응답 처리 규칙을 미리 정합니다 | 176강 |
| 모형 학습 | 훈련과 검증의 경계를 지킵니다 | 문제 2 |
넷째 줄이 이 강의의 값어치를 정합니다. 모형의 성능을 퍼센트 올리는 데 몇 주가 들지만, 누출 하나가 성능을 퍼센트 부풀립니다. 검산의 과 가 그 크기입니다.
그리고 부풀린 성능은 배포하기 전까지 드러나지 않습니다. 검증 자료로 잡을 수 없는 종류의 오류이므로, 순서를 구조로 강제하는 것 말고는 막을 방법이 없습니다. 이것이 파이프라인을 쓰는 이유이며, 편해서가 아니라 혼자서는 지킬 수 없는 규칙을 기계가 지키게 하려는 것입니다.
정답.
| 기호 | 읽는 법 | 뜻 |
|---|---|---|
| 멱등 | idempotent | 여러 번 적용해도 결과가 같습니다 |
| 결정성 | deterministic | 같은 입력에 같은 출력을 냅니다 |
| 누출 | leakage | 알 수 없던 정보가 학습에 섞입니다 |
| 적합 | fit | 훈련부에서 변환에 쓸 값을 정합니다 |
| 적용 | transform | 정한 값으로 자료를 바꿉니다 |
| 표준화 | standardization | 평균을 빼고 표준편차로 나눕니다 |
| 최소최대 | min-max scaling | 범위를 로 옮깁니다 |
| 로버스트 척도 | robust scaling | 중앙값과 사분위 범위를 씁니다 |
| 왜도 | skewness | 분포가 한쪽으로 치우친 정도입니다 |
| 첨도 | kurtosis | 꼬리가 두꺼운 정도이며 정규는 입니다 |
| 계약 | contract | 단계가 지켜야 할 조건입니다 |
| 계보 | lineage | 값이 어디에서 왔는지의 기록입니다 |
| 골든 시험 | golden test | 정해 둔 출력과 맞는지 봅니다 |
| 분포 이동 | distribution shift | 입력 분포가 훈련 때와 달라집니다 |
| 어긋남 | training-serving skew | 훈련과 서빙의 특징이 다릅니다 |
다음은 163강 중심과 퍼짐입니다. 여기까지가 01단원 데이터를 손에 넣기였고, 다음 단원부터 그 자료를 요약합니다.
이미 절반은 만났습니다. 161강 문제 5에서 평균과 중앙값이 오염 앞에서 갈리는 것을 보았고, 문제 3에서 척도 변환이 그 둘 중 무엇에 기대는지 보았습니다. 다음 강의는 그것을 처음부터 정리합니다.
그리고 새 질문이 하나 나옵니다. 어떤 요약값을 골라야 하는지가 자료의 모양에 달려 있는데, 모양을 보려면 이미 요약을 했어야 합니다. 이 순환을 어떻게 끊는지가 기술통계의 첫 주제입니다.
import numpy as np
rng = np.random.default_rng(20260830)
def pw(s, n):
k = n - sum(2 if ord(c) > 0x2FFF else 1 for c in str(s))
return str(s) + " " * max(k, 0)
def rw(s, n):
k = n - sum(2 if ord(c) > 0x2FFF else 1 for c in str(s))
return " " * max(k, 0) + str(s)
def drop_out(v):
mo = np.median(v)
mad = np.median(np.abs(v - mo)) * 1.4826
return v[np.abs(v - mo) <= 3 * mad]
# --- 문제 1: 같은 입력이 같은 출력을 내는가 -----------------------------
print(" 같은 두 단계를 순서만 바꿔 적용해 봅니다")
n1 = 20000
base = rng.normal(10.0, 2.0, n1)
base[:200] = 80.0
miss = rng.random(n1) < 0.10
x1 = np.where(miss, np.nan, base)
ob = ~np.isnan(x1)
clean = drop_out(x1[ob])
ra = np.concatenate([clean, np.full(int(miss.sum()), clean.mean())])
filled = np.where(np.isnan(x1), np.nanmean(x1), x1)
rb = drop_out(filled)
print(" %s %s %s %s" % (pw("순서", 30), rw("남은 행", 10),
rw("평균", 12), rw("대치에 쓴 값", 14)))
print(" %s %10d %12.6f %14.6f"
% (pw("이상치 제거 뒤 대치", 30), len(ra), float(ra.mean()), float(clean.mean())))
print(" %s %10d %12.6f %14.6f"
% (pw("대치 뒤 이상치 제거", 30), len(rb), float(rb.mean()), float(np.nanmean(x1))))
print(" 두 결과의 평균 차이는 %.6f 입니다" % abs(float(ra.mean()) - float(rb.mean())))
print(" 두 단계는 교환되지 않습니다. 대치에 쓰는 값이 이상치를 포함하느냐가 갈립니다")
print(" 같은 단계를 두 번 적용하면 같은 결과가 나오는지도 확인해야 합니다")
print(" 깨끗한 정규 자료에 3 시그마 제거를 반복합니다")
print(" %s %s %s %s" % (pw("적용 횟수", 12), rw("남은 행", 10),
rw("직전 대비 버린 행", 18), rw("처음 대비 남은 비율", 20)))
z = rng.normal(0, 1, 100000)
prev = len(z)
for t in range(1, 7):
mu, sd = z.mean(), z.std()
z = z[np.abs(z - mu) <= 3 * sd]
print(" %s %10d %18d %20.6f"
% (pw(str(t), 12), len(z), prev - len(z), len(z) / 100000))
prev = len(z)
print(" 이상치가 없는 자료인데도 적용할 때마다 계속 깎여 나갑니다")
print(" f(f(x)) 가 f(x) 와 같지 않습니다. 이런 단계는 몇 번 돌렸는지가 결과를 바꿉니다")
print(" 파이프라인의 단계는 멱등이거나, 아니면 실행 횟수를 기록해야 합니다")
# --- 문제 2: 전처리를 언제 계산하는가 -----------------------------------
print(" 라벨과 아무 관계도 없는 무작위 특징 5000 개로 분류를 해 봅니다")
n2, p2, K, REP = 100, 5000, 5, 60
print(" 표본 %d 개, 특징 %d 개, 라벨은 동전 던지기입니다. 참 정확도는 0.5 입니다"
% (n2, p2))
acc_bad, acc_good = [], []
for _ in range(REP):
X = rng.normal(0, 1, (n2, p2))
yb = rng.integers(0, 2, n2) * 2 - 1
fold = rng.permutation(n2) % K
cb = np.abs(X.T @ yb)
top_all = np.argsort(cb)[-20:]
hb, hg = [], []
for f in range(K):
tr, te = fold != f, fold == f
s_all = np.sign(X[:, top_all].T @ yb)
pb = np.sign(X[np.ix_(te, top_all)] @ s_all)
hb.append(float((pb == yb[te]).mean()))
ct = np.abs(X[tr].T @ yb[tr])
top_tr = np.argsort(ct)[-20:]
s_tr = np.sign(X[np.ix_(tr, top_tr)].T @ yb[tr])
pg = np.sign(X[np.ix_(te, top_tr)] @ s_tr)
hg.append(float((pg == yb[te]).mean()))
acc_bad.append(np.mean(hb))
acc_good.append(np.mean(hg))
print(" %s %s %s" % (pw("특징을 언제 고르는가", 28), rw("교차검증 정확도", 18),
rw("참값 0.5 와의 차이", 20)))
print(" %s %18.6f %20.6f"
% (pw("나누기 전에 전체로", 28), float(np.mean(acc_bad)), float(np.mean(acc_bad)) - 0.5))
print(" %s %18.6f %20.6f"
% (pw("각 폴드의 훈련에서만", 28), float(np.mean(acc_good)), float(np.mean(acc_good)) - 0.5))
print(" 라벨이 동전 던지기인데 앞의 방법은 정확도가 0.5 를 훌쩍 넘습니다")
print(" 검증 자료의 라벨을 이미 보고 특징을 골랐기 때문입니다. 이것이 누출입니다")
print(" 전체로 고르면 5000 개 중 우연히 잘 맞는 20 개가 뽑힙니다")
print(" 그 20 개는 검증 자료에서도 잘 맞습니다. 그 자료를 보고 뽑았으니 당연합니다")
print(" 표준화도 같은 문제를 만듭니다. 규모는 작지만 방향은 같습니다")
n3, tr_n, R3 = 200, 100, 4000
ba, bb = [], []
for _ in range(R3):
X3 = rng.normal(5.0, 3.0, n3)
va = X3[tr_n:]
ba.append(abs(float(((va - X3.mean()) / X3.std()).mean())))
bb.append(abs(float(((va - X3[:tr_n].mean()) / X3[:tr_n].std()).mean())))
print(" 표본 %d 개를 절반씩 나누고 %d 번 되풀이했습니다" % (n3, R3))
print(" %s %s %s" % (pw("표준화 기준", 24), rw("검증부 평균의 절댓값", 22),
rw("0 에서 떨어진 정도", 20)))
print(" %s %22.6f %20.6f" % (pw("전체로 계산", 24), float(np.mean(ba)), float(np.mean(ba))))
print(" %s %22.6f %20.6f" % (pw("훈련부로만 계산", 24), float(np.mean(bb)), float(np.mean(bb))))
print(" 전체로 계산하면 검증부 평균이 %.4f 배로 0 에 더 붙습니다"
% (float(np.mean(ba)) / float(np.mean(bb))))
print(" 검증부가 자기 기준을 만드는 데 참여했기 때문입니다. 실제보다 좋아 보입니다")
print(" 규칙은 하나입니다. 모든 값은 훈련부에서만 정하고 검증부에는 적용만 합니다")
# --- 문제 3: 변환은 무엇을 바꾸는가 -------------------------------------
print(" 이상치가 하나 있는 자료에 척도 변환 셋을 적용합니다")
n4 = 20000
v4 = rng.normal(50.0, 10.0, n4)
v4[0] = 5000.0
zsc = (v4 - v4.mean()) / v4.std()
mm = (v4 - v4.min()) / (v4.max() - v4.min())
q1, q3 = np.quantile(v4, 0.25), np.quantile(v4, 0.75)
rb2 = (v4 - np.median(v4)) / (q3 - q1)
print(" %s %s %s %s" % (pw("변환", 20), rw("정상값의 범위 폭", 18),
rw("정상값 표준편차", 16), rw("이상치의 값", 14)))
for nm, w in [("z 표준화", zsc), ("최소최대", mm), ("로버스트", rb2)]:
body = w[1:]
print(" %s %18.6f %16.6f %14.4f"
% (pw(nm, 20), float(body.max() - body.min()), float(body.std()), float(w[0])))
print(" 최소최대는 이상치가 상한을 정하므로 정상값이 좁은 구간에 뭉칩니다")
print(" 정상값 %d 개 중 %d 개가 [0, 0.02] 안에 들어갑니다"
% (len(mm) - 1, int((mm[1:] <= 0.02).sum())))
print(" z 표준화는 이상치가 표준편차를 키워 정상값을 눌러 놓습니다")
print(" 이상치가 없었다면 표준편차가 %.4f 인데 있어서 %.4f 입니다"
% (float(v4[1:].std()), float(v4.std())))
print(" 로버스트 척도만 정상값의 퍼짐을 그대로 둡니다. 이상치는 큰 값으로 남습니다")
print(" 분포를 바꾸는 변환도 봅니다. 오른쪽으로 긴 꼬리를 가진 자료입니다")
lg = np.exp(rng.normal(0, 1, n4))
print(" %s %s %s %s" % (pw("변환", 18), rw("왜도", 12), rw("첨도", 12), rw("중앙값", 12)))
for nm, w in [("원자료", lg), ("로그", np.log(lg)), ("제곱근", np.sqrt(lg))]:
c = (w - w.mean()) / w.std()
print(" %s %12.4f %12.4f %12.4f"
% (pw(nm, 18), float((c ** 3).mean()), float((c ** 4).mean()), float(np.median(w))))
print(" 로그가 왜도를 거의 0 으로 만듭니다. 원래 로그정규였으니 당연합니다")
print(" 변환은 공짜가 아닙니다. 로그를 취하면 계수의 뜻이 배수로 바뀝니다")
print(" 그리고 0 이나 음수가 있으면 로그를 쓸 수 없습니다. 더해 주면 그 값이 결과를 바꿉니다")
# --- 문제 4: 파이프라인을 어떻게 설계하는가 -----------------------------
print(" 단계마다 계약을 적어 두고 지키는지 검사합니다")
n5 = 10000
key = np.arange(n5)
amt = rng.normal(100.0, 20.0, n5)
amt[rng.random(n5) < 0.07] = np.nan
steps = []
cur_rows, cur_cols, cur_null = n5, 3, int(np.isnan(amt).sum())
steps.append(("0. 적재", cur_rows, cur_cols, cur_null, "-"))
keep = ~np.isnan(amt)
steps.append(("1. 결측 행 제거", int(keep.sum()), 3, 0, "행 감소 허용"))
steps.append(("2. 파생 열 추가", int(keep.sum()), 4, 0, "행 불변"))
dup = np.concatenate([key[keep], key[keep][:1500]])
steps.append(("3. 배송 표 조인", len(dup), 5, 0, "행 불변"))
steps.append(("4. 주문별 재집계", len(np.unique(dup)), 5, 0, "행 감소 허용"))
print(" %s %s %s %s %s" % (pw("단계", 20), rw("행", 8), rw("열", 6),
rw("결측 칸", 8), "계약"))
prev_rows = None
viol = []
for nm, r, c, nu, con in steps:
print(" %s %8d %6d %8d %s" % (pw(nm, 20), r, c, nu, con))
if con == "행 불변" and prev_rows is not None and r != prev_rows:
viol.append((nm, prev_rows, r))
prev_rows = r
print(" 계약 위반 %d 건입니다" % len(viol))
for nm, a1, b1 in viol:
print(" %s에서 행이 %d 에서 %d 로 늘었습니다" % (nm, a1, b1))
print(" 3 단계가 160강 문제 3 의 팬아웃입니다. 행 수 한 줄로 그 자리에서 잡힙니다")
print(" 검사는 파이프라인 밖이 아니라 단계 사이에 둡니다. 멀어질수록 원인을 못 찾습니다")
print(" 단계는 적합과 적용을 나눕니다. 적합은 훈련부에서 한 번, 적용은 모든 자료에 합니다")
print(" %s %s %s" % (pw("단계", 18), pw("적합에서 정하는 값", 26), "적용에서 하는 일"))
for nm, f, t in [("결측 대치", "훈련부의 평균", "그 값으로 채웁니다"),
("표준화", "훈련부의 평균과 표준편차", "그 값으로 나눕니다"),
("범주 부호화", "훈련부에 나온 범주 목록", "없던 범주는 기타로 보냅니다"),
("이상치 경계", "훈련부의 중앙값과 MAD", "그 경계로 자릅니다")]:
print(" %s %s %s" % (pw(nm, 18), pw(f, 26), t))
print(" 적합에서 정한 값을 저장하지 않으면 배포 때 같은 변환을 재현할 수 없습니다")
# --- 문제 5: 파이프라인을 어떻게 검증하는가 -----------------------------
print(" 같은 입력에서 같은 출력이 나오는지 검사합니다")
def run(seed, data):
r = np.random.default_rng(seed)
d = data + 0.0
m = np.isnan(d)
d[m] = np.nanmedian(data) + r.normal(0, float(np.nanstd(data)), int(m.sum()))
return d
def csum(v):
q = np.round(v.astype(np.float64), 8)
b = (q * 1000).astype(np.int64)
return int(np.sum(b * (np.arange(len(b)) % 97 + 1)) % 1000000007)
d0 = rng.normal(0, 1, 5000)
d0[rng.random(5000) < 0.05] = np.nan
print(" %s %s %s" % (pw("실행", 26), rw("검사값", 16), rw("첫 실행과 같은가", 18)))
c1 = csum(run(7, d0))
for nm, s5 in [("첫 실행 seed 7", 7), ("다시 seed 7", 7), ("seed 8", 8)]:
c = csum(run(s5, d0))
print(" %s %16d %18s" % (pw(nm, 26), c, str(c == c1)))
print(" 시드를 고정하면 같은 값이 나옵니다. 고정하지 않으면 매번 다른 자료가 만들어집니다")
print(" 자료 자체에 규칙을 걸어 두는 검사도 함께 돌립니다. 158강 문제 5 의 제약입니다")
tv = np.array([1, 2, 2, 4, 5, -3, 7, 8], dtype=np.int64)
rules = [("기본키 유일성", len(tv) - len(np.unique(tv))),
("값이 음수가 아닐 것", int((tv < 0).sum())),
("값이 100 이하일 것", int((tv > 100).sum())),
("결측이 없을 것", 0)]
print(" %s %s" % (pw("규칙", 24), rw("위반 건수", 12)))
for nm, k in rules:
print(" %s %12d" % (pw(nm, 24), k))
print(" 위반이 있으면 파이프라인을 멈춥니다. 통과시키면 아래 모든 단계가 오염됩니다")
print(" 들어오는 자료가 예전과 같은지도 봅니다. 분포가 움직이면 모형이 조용히 낡습니다")
ref = rng.normal(0, 1, 40000)
print(" %s %s %s" % (pw("새 자료", 24), rw("최대 분포 차이", 16), rw("판정", 10)))
grid = np.linspace(-5, 5, 2001)
Fr = np.searchsorted(np.sort(ref), grid, side="right") / len(ref)
for nm, sh, sc in [("변화 없음", 0.0, 1.0), ("평균 0.1 이동", 0.1, 1.0),
("평균 0.5 이동", 0.5, 1.0), ("퍼짐 1.5 배", 0.0, 1.5)]:
nw = np.sort(rng.normal(sh, sc, 40000))
Fn = np.searchsorted(nw, grid, side="right") / len(nw)
ks = float(np.max(np.abs(Fn - Fr)))
print(" %s %16.6f %10s" % (pw(nm, 24), ks, "경보" if ks > 0.05 else "정상"))
print(" 기준은 표본 크기로 정합니다. 크면 아주 작은 차이도 통계적으로 유의해집니다")
print(" 파이프라인의 산출물은 자료만이 아닙니다. 적합에서 정한 값과 검사 기록이 함께 남습니다")
print(" 그것이 남아야 왜 이 숫자가 나왔는지 나중에 되짚을 수 있습니다")
# 같은 두 단계를 순서만 바꿔 적용해 봅니다
# 순서 남은 행 평균 대치에 쓴 값
# 이상치 제거 뒤 대치 19789 10.024050 10.024050
# 대치 뒤 이상치 제거 19694 10.109855 10.714847
# 두 결과의 평균 차이는 0.085805 입니다
# 두 단계는 교환되지 않습니다. 대치에 쓰는 값이 이상치를 포함하느냐가 갈립니다
# 같은 단계를 두 번 적용하면 같은 결과가 나오는지도 확인해야 합니다
# 깨끗한 정규 자료에 3 시그마 제거를 반복합니다
# 적용 횟수 남은 행 직전 대비 버린 행 처음 대비 남은 비율
# 1 99751 249 0.997510
# 2 99700 51 0.997000
# 3 99693 7 0.996930
# 4 99692 1 0.996920
# 5 99692 0 0.996920
# 6 99692 0 0.996920
# 이상치가 없는 자료인데도 적용할 때마다 계속 깎여 나갑니다
# f(f(x)) 가 f(x) 와 같지 않습니다. 이런 단계는 몇 번 돌렸는지가 결과를 바꿉니다
# 파이프라인의 단계는 멱등이거나, 아니면 실행 횟수를 기록해야 합니다
# 라벨과 아무 관계도 없는 무작위 특징 5000 개로 분류를 해 봅니다
# 표본 100 개, 특징 5000 개, 라벨은 동전 던지기입니다. 참 정확도는 0.5 입니다
# 특징을 언제 고르는가 교차검증 정확도 참값 0.5 와의 차이
# 나누기 전에 전체로 0.921833 0.421833
# 각 폴드의 훈련에서만 0.506167 0.006167
# 라벨이 동전 던지기인데 앞의 방법은 정확도가 0.5 를 훌쩍 넘습니다
# 검증 자료의 라벨을 이미 보고 특징을 골랐기 때문입니다. 이것이 누출입니다
# 전체로 고르면 5000 개 중 우연히 잘 맞는 20 개가 뽑힙니다
# 그 20 개는 검증 자료에서도 잘 맞습니다. 그 자료를 보고 뽑았으니 당연합니다
# 표준화도 같은 문제를 만듭니다. 규모는 작지만 방향은 같습니다
# 표본 200 개를 절반씩 나누고 4000 번 되풀이했습니다
# 표준화 기준 검증부 평균의 절댓값 0 에서 떨어진 정도
# 전체로 계산 0.056281 0.056281
# 훈련부로만 계산 0.113344 0.113344
# 전체로 계산하면 검증부 평균이 0.4965 배로 0 에 더 붙습니다
# 검증부가 자기 기준을 만드는 데 참여했기 때문입니다. 실제보다 좋아 보입니다
# 규칙은 하나입니다. 모든 값은 훈련부에서만 정하고 검증부에는 적용만 합니다
# 이상치가 하나 있는 자료에 척도 변환 셋을 적용합니다
# 변환 정상값의 범위 폭 정상값 표준편차 이상치의 값
# z 표준화 2.050830 0.275261 135.9551
# 최소최대 0.014974 0.002010 1.0000
# 로버스트 5.574866 0.748256 369.5910
# 최소최대는 이상치가 상한을 정하므로 정상값이 좁은 구간에 뭉칩니다
# 정상값 19999 개 중 19999 개가 [0, 0.02] 안에 들어갑니다
# z 표준화는 이상치가 표준편차를 키워 정상값을 눌러 놓습니다
# 이상치가 없었다면 표준편차가 10.0215 인데 있어서 36.4073 입니다
# 로버스트 척도만 정상값의 퍼짐을 그대로 둡니다. 이상치는 큰 값으로 남습니다
# 분포를 바꾸는 변환도 봅니다. 오른쪽으로 긴 꼬리를 가진 자료입니다
# 변환 왜도 첨도 중앙값
# 원자료 7.0843 111.8791 1.0019
# 로그 -0.0055 3.0768 0.0019
# 제곱근 1.8945 10.4116 1.0010
# 로그가 왜도를 거의 0 으로 만듭니다. 원래 로그정규였으니 당연합니다
# 변환은 공짜가 아닙니다. 로그를 취하면 계수의 뜻이 배수로 바뀝니다
# 그리고 0 이나 음수가 있으면 로그를 쓸 수 없습니다. 더해 주면 그 값이 결과를 바꿉니다
# 단계마다 계약을 적어 두고 지키는지 검사합니다
# 단계 행 열 결측 칸 계약
# 0. 적재 10000 3 775 -
# 1. 결측 행 제거 9225 3 0 행 감소 허용
# 2. 파생 열 추가 9225 4 0 행 불변
# 3. 배송 표 조인 10725 5 0 행 불변
# 4. 주문별 재집계 9225 5 0 행 감소 허용
# 계약 위반 1 건입니다
# 3. 배송 표 조인에서 행이 9225 에서 10725 로 늘었습니다
# 3 단계가 160강 문제 3 의 팬아웃입니다. 행 수 한 줄로 그 자리에서 잡힙니다
# 검사는 파이프라인 밖이 아니라 단계 사이에 둡니다. 멀어질수록 원인을 못 찾습니다
# 단계는 적합과 적용을 나눕니다. 적합은 훈련부에서 한 번, 적용은 모든 자료에 합니다
# 단계 적합에서 정하는 값 적용에서 하는 일
# 결측 대치 훈련부의 평균 그 값으로 채웁니다
# 표준화 훈련부의 평균과 표준편차 그 값으로 나눕니다
# 범주 부호화 훈련부에 나온 범주 목록 없던 범주는 기타로 보냅니다
# 이상치 경계 훈련부의 중앙값과 MAD 그 경계로 자릅니다
# 적합에서 정한 값을 저장하지 않으면 배포 때 같은 변환을 재현할 수 없습니다
# 같은 입력에서 같은 출력이 나오는지 검사합니다
# 실행 검사값 첫 실행과 같은가
# 첫 실행 seed 7 1068685 True
# 다시 seed 7 1068685 True
# seed 8 2681809 False
# 시드를 고정하면 같은 값이 나옵니다. 고정하지 않으면 매번 다른 자료가 만들어집니다
# 자료 자체에 규칙을 걸어 두는 검사도 함께 돌립니다. 158강 문제 5 의 제약입니다
# 규칙 위반 건수
# 기본키 유일성 1
# 값이 음수가 아닐 것 1
# 값이 100 이하일 것 0
# 결측이 없을 것 0
# 위반이 있으면 파이프라인을 멈춥니다. 통과시키면 아래 모든 단계가 오염됩니다
# 들어오는 자료가 예전과 같은지도 봅니다. 분포가 움직이면 모형이 조용히 낡습니다
# 새 자료 최대 분포 차이 판정
# 변화 없음 0.004400 정상
# 평균 0.1 이동 0.042675 정상
# 평균 0.5 이동 0.197875 경보
# 퍼짐 1.5 배 0.099975 경보
# 기준은 표본 크기로 정합니다. 크면 아주 작은 차이도 통계적으로 유의해집니다
# 파이프라인의 산출물은 자료만이 아닙니다. 적합에서 정한 값과 검사 기록이 함께 남습니다
# 그것이 남아야 왜 이 숫자가 나왔는지 나중에 되짚을 수 있습니다