動く顧客と動かない顧客 — 差分表で候補を消す
한국어 원문으로 표시합니다.
목표
성공 표본과 실패 표본을 속성별로 갈라 차이표를 만들고, 모든 실패에 있고 어떤 성공에도 없는 값만 후보로 남긴다. 완벽하게 상관하는 가짜 후보를 새 표본과 개입 실험으로 각각 떨어뜨리고, 한 속성으로는 갈리지 않는 상호작용까지 찾아낸다.
왜 중요한가
"어떤 고객은 되고 어떤 고객은 안 됩니다" 는 나쁜 소식처럼 들리지만 사실은 좋은 소식이다. 되는 쪽이 있다는 것은 대조군이 있다는 뜻이다. 실패 로그만 파면 모든 줄이 수상해 보이지만, 성공 로그에도 같은 줄이 있으면 그것은 원인이 아니라 배경이다. 이 실습에서 어려운 것은 표를 만드는 코드가 아니라 후보가 둘 남았을 때 무엇을 하는가다. 같은 날 배포된 두 가지가 언제나 함께 다니면 표본만으로는 구별할 수 없다. 이때 그럴듯한 쪽을 골라 보고하면 절반의 확률로 며칠을 버린다. 가르는 길은 둘뿐이다. 둘이 갈라지는 표본을 더 얻거나, 속성 하나만 바꾸고 나머지를 고정해 결과가 뒤집히는지 보는 것이다. 앞은 관찰이고 뒤는 실험이며, 인과를 말할 수 있는 것은 실험뿐이다. 채점기는 여러분의 결론을 믿지 않는다. 채점기가 원인과 가짜 후보를 매번 다른 자리에 심은 표본을 만들어 여러분의 도구를 실제로 실행하고, 골라낸 후보 집합을 심어 둔 답과 대조한다.
단계
- /root/diff/gen_cases.py 를 만들어 실행해 /root/diff/cases.json(240건), cases2.json(120건), cases3.json(180건), repro.py 를 만드세요.
- /root/diff/table.json 에 속성별 값마다 실패 건수와 성공 건수를 적은 차이표를 만드세요.
- /root/diff/differential.py 를 만들어 모든 실패에 있고 어떤 성공에도 없는 값을 후보로 고르게 하세요.
- /root/diff/candidates.json 에 후보 목록과 지운 후보를 근거와 함께 적으세요.
- 새 표본을 합쳐 다시 돌리고 /root/diff/verdict.json 에 살아남은 원인 하나와 떨어진 후보, 그 근거가 된 표본 번호를 적으세요.
- differential.py 에 짝 분석을 붙이고 /root/diff/pairs.json 에 cases3.json 의 짝 후보를 적으세요.
- repro.py 로 속성 하나만 바꿔 결과가 뒤집히는지 확인하고 /root/diff/confirm.json 에 적으세요.
- /root/diff/summary.json 과 /root/diff/diff_report.md 에 네 절로 보고하세요.
참고
- 표본 형식: 각 건은
{"case_id": …, "outcome": "ok"|"fail", "region": …, "client_build": …, "encoding": …, "auth_mode": …, "device": …, "endpoint": …, "window": …}입니다. case_id 와 outcome 을 뺀 나머지가 속성입니다. - 도구 계약:
python3 /root/diff/differential.py --cases <파일> [--cases <파일2> …] [--pairs] --out <json>은 cases·failures·successes·candidates·eliminated 를 담은 JSON 한 덩어리를 표준출력에 냅니다.--pairs를 주면 single_candidates 와 pair_candidates 도 담습니다. - 후보의 정의: 모든 실패 표본에 있고 어떤 성공 표본에도 없는
속성=값문자열입니다. 표기는encoding=utf-8-sig처럼 등호로 붙입니다. - 지운 후보의 정의: 모든 실패에 있지만 성공 하나라도에 있는 값입니다. 후보가 아니라는 근거가 성공 표본에 있습니다.
- 짝 후보의 정의: 두 값이 함께 모든 실패에 있고, 그 둘을 함께 가진 성공이 하나도 없는 경우입니다. 단독으로 이미 후보인 값이 낀 짝은 새로운 정보가 아니므로 뺍니다.
- 재현기 계약:
python3 /root/diff/repro.py --region … --client-build … --encoding … --auth-mode … --device … --endpoint … --window …는 ok 또는 fail 한 낱말을 냅니다. 같은 조합에는 언제나 같은 답을 냅니다. - 흔한 실수: 실패 표본만 모으기, 후보가 둘인데 그럴듯한 쪽 고르기, 지운 후보를 기록하지 않기, 개입 실험에서 두 가지를 한꺼번에 바꾸기.
- 이 실습의 가정: 표본의 속성 목록은 게이트웨이가 남긴 것으로 고정입니다. 현장에서는 이 목록을 정하는 일부터가 조사의 일부입니다.
표본과 재현기 손에 쥐기
/root/diff/gen_cases.py 를 만들어 실행해 /root/diff/cases.json(240건)과 cases2.json(120건), cases3.json(180건), repro.py 를 만드세요.
현장에서 손에 쥐는 것은 결론이 아니라 표본입니다. 이 스크립트를 그대로 저장해 실행하면 됩니다. 만들어진 cases.json 을 잠깐 열어 성공과 실패가 어떤 모양으로 적혀 있는지 보세요.
속성별 차이표 만들기
/root/diff/table.json 에 failures·successes 와 attributes 를 적으세요. attributes 는 속성 이름마다 값별로 {"fail": 건수, "ok": 건수} 를 담습니다. case_id 와 outcome 은 속성이 아닙니다.
표가 먼저입니다. 표 없이 눈으로 훑으면 '실패에 많이 보이는 값' 과 '실패에만 있는 값' 을 구별하지 못합니다. 속성 이름은 표본에서 그대로 읽어 오세요 — 손으로 적으면 하나를 빠뜨리고, 빠뜨린 속성은 영영 후보가 되지 않습니다.
후보를 고르는 도구 만들기
/root/diff/differential.py 를 만들어 모든 실패에 있고 어떤 성공에도 없는 속성=값 을 candidates 로, 모든 실패에 있지만 성공에도 있는 값을 eliminated 로 내게 하세요. --cases 는 여러 번 줄 수 있어야 합니다.
집합 연산 두 번이면 끝납니다. 실패 표본들의 교집합을 구하고, 성공 표본들의 합집합을 뺍니다. 뺄셈에서 사라진 값이 eliminated 입니다. 사람이 표를 눈으로 훑는 대신 이 두 줄을 쓰는 이유는, 속성이 스무 개가 되어도 절차가 같기 때문입니다.
후보와 지운 후보를 근거와 함께 적기
/root/diff/candidates.json 에 candidates(후보 목록)와 eliminated(지운 후보) 를 적으세요. eliminated 의 각 항목은 {"attribute": "속성=값", "reason": "…"} 이고, reason 에는 그 값이 나온 성공 표본의 건수가 숫자로 들어가야 합니다.
지운 것을 적어 두는 이유는 다음 사람이 같은 길을 다시 걷지 않게 하기 위해서입니다. '모든 실패에 token 인증이 있었다' 는 사실은 성공 몇 건에도 token 이 있었다는 사실과 함께 놓일 때만 뜻이 생깁니다. 그 숫자를 표에서 가져오세요.
새 표본으로 가짜 후보 떨어뜨리기
cases.json 과 cases2.json 을 함께 넣어 다시 돌리고 /root/diff/verdict.json 에 cause(살아남은 원인 하나), eliminated_by_new_batch(떨어진 후보), evidence_case_ids(근거가 된 cases2.json 의 건 번호)를 적으세요.
표본만으로 둘을 구별할 수 없었던 이유는 자료에 둘을 가르는 조합이 없었기 때문입니다. 새 표본에 그 조합이 들어 있습니다. 근거가 되는 건은 두 종류입니다 — 떨어질 속성을 가졌는데 성공한 건, 또는 그 속성이 없는데 실패한 건입니다.
한 속성으로 갈리지 않는 경우
differential.py 에 --pairs 를 붙여 짝 후보를 찾게 하고, cases3.json 에 돌려 /root/diff/pairs.json 에 single_candidates 와 pair_candidates 를 적으세요. 짝은 두 값을 담은 목록으로 적습니다.
단독 후보가 하나도 없는 표본이 있습니다. 두 값이 함께일 때만 실패하기 때문입니다. 실패 전부에 있는 값들 중 두 개씩 짝지어, 그 둘을 함께 가진 성공이 하나도 없는 짝을 찾으세요. 이미 단독으로 후보인 값이 낀 짝은 새로운 정보가 아닙니다.
속성 하나만 바꿔 뒤집히는지 보기
/root/diff/confirm.json 에 claim(주장하는 원인), base(기준 속성 조합 일곱 개), flipped_attribute, base_outcome, flipped_outcome 을 적으세요. base 와 뒤집은 조합은 정확히 한 속성만 달라야 하고 두 결과는 서로 달라야 합니다.
표본은 관찰이고 재현기는 실험입니다. 나머지를 전부 고정한 채 주장하는 속성 하나만 바꿔 결과가 뒤집히면, 그 속성은 상관이 아니라 원인입니다. 두 가지를 한꺼번에 바꾸면 어느 쪽이 뒤집었는지 알 수 없습니다.
지운 것까지 한 장으로 보고하기
/root/diff/summary.json 에 week1_cases·week1_candidates·cause·eliminated_by_new_batch·pair_only_tenant·intervention 을 적고, /root/diff/diff_report.md 에 ## 무엇이 갈렸나 ## 무엇을 지웠나 ## 상관인가 원인인가 ## 남은 위험 네 절로 보고하세요.
보고서에 남은 후보만 적으면 다음 사람이 같은 길을 다시 걷습니다. 지운 후보와 지운 근거를 함께 적으세요. 개입 실험의 두 결과도 숫자가 아니라 낱말이지만 증거입니다.