LabHub
배우기 러닝패스 코스

Debugging in Practice

Working customers and failing ones — erasing candidates with a difference table

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

성공 표본과 실패 표본을 속성별로 갈라 차이표를 만들고, 모든 실패에 있고 어떤 성공에도 없는 값만 후보로 남긴다. 완벽하게 상관하는 가짜 후보를 새 표본과 개입 실험으로 각각 떨어뜨리고, 한 속성으로는 갈리지 않는 상호작용까지 찾아낸다.

왜 중요한가

"어떤 고객은 되고 어떤 고객은 안 됩니다" 는 나쁜 소식처럼 들리지만 사실은 좋은 소식이다. 되는 쪽이 있다는 것은 대조군이 있다는 뜻이다. 실패 로그만 파면 모든 줄이 수상해 보이지만, 성공 로그에도 같은 줄이 있으면 그것은 원인이 아니라 배경이다. 이 실습에서 어려운 것은 표를 만드는 코드가 아니라 후보가 둘 남았을 때 무엇을 하는가다. 같은 날 배포된 두 가지가 언제나 함께 다니면 표본만으로는 구별할 수 없다. 이때 그럴듯한 쪽을 골라 보고하면 절반의 확률로 며칠을 버린다. 가르는 길은 둘뿐이다. 둘이 갈라지는 표본을 더 얻거나, 속성 하나만 바꾸고 나머지를 고정해 결과가 뒤집히는지 보는 것이다. 앞은 관찰이고 뒤는 실험이며, 인과를 말할 수 있는 것은 실험뿐이다. 채점기는 여러분의 결론을 믿지 않는다. 채점기가 원인과 가짜 후보를 매번 다른 자리에 심은 표본을 만들어 여러분의 도구를 실제로 실행하고, 골라낸 후보 집합을 심어 둔 답과 대조한다.

단계

  1. /root/diff/gen_cases.py 를 만들어 실행해 /root/diff/cases.json(240건), cases2.json(120건), cases3.json(180건), repro.py 를 만드세요.
  2. /root/diff/table.json 에 속성별 값마다 실패 건수와 성공 건수를 적은 차이표를 만드세요.
  3. /root/diff/differential.py 를 만들어 모든 실패에 있고 어떤 성공에도 없는 값을 후보로 고르게 하세요.
  4. /root/diff/candidates.json 에 후보 목록과 지운 후보를 근거와 함께 적으세요.
  5. 새 표본을 합쳐 다시 돌리고 /root/diff/verdict.json 에 살아남은 원인 하나와 떨어진 후보, 그 근거가 된 표본 번호를 적으세요.
  6. differential.py 에 짝 분석을 붙이고 /root/diff/pairs.json 에 cases3.json 의 짝 후보를 적으세요.
  7. repro.py 로 속성 하나만 바꿔 결과가 뒤집히는지 확인하고 /root/diff/confirm.json 에 적으세요.
  8. /root/diff/summary.json/root/diff/diff_report.md 에 네 절로 보고하세요.

참고

표본과 재현기 손에 쥐기

/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## 무엇이 갈렸나 ## 무엇을 지웠나 ## 상관인가 원인인가 ## 남은 위험 네 절로 보고하세요.

보고서에 남은 후보만 적으면 다음 사람이 같은 길을 다시 걷습니다. 지운 후보와 지운 근거를 함께 적으세요. 개입 실험의 두 결과도 숫자가 아니라 낱말이지만 증거입니다.