노준탁의 applied AI, ML system, 연구 노트.
이 사이트에서 다루는 내용:
- 논문 리뷰 (가정 → failure mode → 내가 바꿨을 부분)
- Applied ML system 설계 노트
- Reproducible experiments
노준탁의 applied AI, ML system, 연구 노트.
이 사이트에서 다루는 내용:
이 글은 한국어 전화 Voice Agent 현장 노트 7부작 중 7부이자 마지막 편이다. 내가 리드했던 프로젝트 — 공공 서비스 전화 상담용 실시간 한국어 voice agent(STT → LLM → TTS) — 를 만들면서 겪은 일을 정리해 왔다. STT 평가 → Rule vs 모델 → Latency → 조용한 실패 → Guardrail → 평가 지표 → 단순화. 5부에서는 dialog layer의 규칙이 어떻게 쌓였는지를, 6부에서는 평가가 왜 계속 그 규칙들에 점수를 몰아줬는지를 다뤘다. 이번 글은 그 규칙들을 걷어낸 이야기다. ...
이 글은 한국어 전화 Voice Agent 현장 노트 7부작 중 6부다. 내가 리드했던 프로젝트 — 공공 서비스 전화 상담용 실시간 한국어 voice agent(STT → LLM → TTS) — 를 만들면서 겪은 일을 정리한다. STT 평가 → Rule vs 모델 → Latency → 조용한 실패 → Guardrail → 평가 지표 → 단순화. 5부에서는 실패한 QA row 하나마다 dialog layer에 규칙이 하나씩 쌓인 과정을 다뤘다. 이번 글은 그 규칙들에 계속 점수를 몰아준 평가 이야기다. ...
이 글은 한국어 전화 Voice Agent 현장 노트 7부작 중 5부다. 내가 리드했던 프로젝트 — 공공 서비스 전화 상담용 실시간 한국어 voice agent(STT → LLM → TTS) — 를 만들면서 겪은 일을 정리한다. 1부는 STT 평가가 나를 속인 방식, 2부는 rule이 모델을 이기는 곳, 3부는 latency, 4부는 exception 한 번 없이 조용히 실패한 것들을 다뤘다. 이번 글은 dialog layer 이야기다. 그 안의 규칙이 실패한 QA row 하나마다 하나씩 불어난 과정을 따라간다. 3월 25일 하루에만 dialog service에 commit을 32개 했다. 첫 commit이 00:56, 마지막이 16:59였고, 그중 26개의 제목이 “Fix"로 시작한다. 11:00에는 한 요청 유형의 첫 안내 멘트를 바꿨다. 정작 물어야 할 질문 앞에 주의 사항이 세 문장이나 붙어 있던 것을 짧고 자연스러운 질문 하나로 줄였다. 그리고 11:07에 되돌렸다. Commit message에는 “original detailed greeting for QA compatibility"로 돌아간다고 적혀 있다. 전화로 들으면 짧은 쪽이 분명 나았다. 그런데도 진 이유는 하나, 정답지가 옛 멘트를 기준으로 쓰여 있었기 때문이다. (이 commit message를 다시 읽는 건 꽤 민망한 일이었다.) ...
이 글은 한국어 전화 Voice Agent 현장 노트 7부작 중 4부다. 내가 리드했던 프로젝트 — 공공 서비스 전화 상담용 실시간 한국어 voice agent(STT → LLM → TTS) — 를 만들면서 겪은 일을 정리한다. 1부는 STT 평가가 나를 어떻게 속였는지, 2부는 rule이 모델을 이기는 곳, 3부는 latency를 다뤘다. 이번에는 exception 한 번 던지지 않고 조용히 실패한 것들 이야기다. 웹 앱이 망가지면 누군가는 500 페이지라도 본다. Voice agent가 망가지면 전화를 건 사람은 아무것도 듣지 못한다. 침묵에 대고 “여보세요?“를 두어 번 하다가, 몇 초 기다리고, 끊는다. Stack trace는 caller에게 닿지 않고, 많은 경우 나한테도 닿지 않는다. ...
이 글은 한국어 전화 Voice Agent 현장 노트 7부작 중 3부다. 내가 리드했던 프로젝트 — 공공 서비스 전화 상담용 실시간 한국어 voice agent(STT → LLM → TTS) — 를 만들면서 겪은 일을 정리한다. 1부는 STT 평가가 나를 어떻게 속였는지, 2부는 rule이 모델을 이기는 지점을 다뤘다. 이번 글은 latency 이야기다: 시간이 어디로 새고 있었는지, 그리고 실제로 효과가 있었던 처방이 왜 거의 한 번도 “더 작은 모델 쓰기"가 아니었는지. 첫 latency 작업에 들어가기 전, 응답 하나가 end-to-end로 평균 4.5초 걸렸다. 전화에서 4.5초는 “느리다"가 아니라 “고장 났다"로 들린다. 그 정도 침묵이면 발신자는 전화가 끊겼나 싶어 “여보세요?“를 하고, 하필 agent가 막 대답을 시작하는 순간에 말이 겹친다. ...
이 글은 한국어 전화 Voice Agent 현장 노트 7부작 중 2부다. 내가 리드했던 프로젝트 — 공공 서비스 전화 상담용 실시간 한국어 voice agent(STT → LLM → TTS) — 를 만들면서 겪은 일을 정리한다. 1부에서는 STT 평가가 나를 어떻게 속였는지를 다뤘다. 이번에는 LLM을 둘러싼 텍스트 이야기다: 들어오고 나가는 숫자, STT가 거의 맞게 듣는 이름, 그리고 LLM 호출이 latency를 감수할 만한 자리. 3월의 어느 금요일 새벽 2시 17분, TTS normalizer 버그 하나를 고쳤다. 27,000원 같은 금액을 “이, 칠, 공, 공, 공 원"처럼 한 자리씩 읽어 버리는 버그였다. 그리고 30분쯤 뒤, 한국어 숫자 읽는 rule을 손으로 짜는 건 그만두고 그 일을 LLM에게 넘겼다. 어차피 한국어는 내 regex보다 LLM이 더 잘하니까. (새벽 세 시를 앞둔 사람의 논리였다.) ...
이 글은 한국어 전화 Voice Agent 현장 노트 7부작 중 1부다. 내가 리드했던 프로젝트 — 공공 서비스 전화 상담용 실시간 한국어 voice agent(STT → LLM → TTS) — 를 만들면서 겪은 일을 정리한다. Whisper fine-tuning 시리즈가 모델을 어떻게 학습했는지를 다뤘다면, 이번 시리즈는 그 모델이 실제 통화를 만났을 때 벌어진 일에서 시작한다. 작년 말, 내 Whisper fine-tuning 곡선은 더 바랄 게 없을 만큼 예뻤다. 공개 한국어 8 kHz 전화망 corpus(AI Hub의 저음질 전화망 음성인식 데이터)의 held-out split에서 라운드마다 기록이 갱신됐다: CER 9.0% → 6.3% → 5.5% → fine-tuned large-v3-turbo의 3.8%. 손대지 않은 large-v3-turbo는 6.5%였다. 스스로 잡은 목표가 4% 미만이었으니 3.8%면 통과다. (그때는 꽤 뿌듯했다.) ...
“high bias는 underfitting, high variance는 overfitting"은 다들 왼다. 그런데 실제 학습 결과를 보고 내 모델이 둘 중 뭔지, 그리고 뭘 해야 하는지를 말할 수 있는 사람은 훨씬 적다. 이 글은 둘 다 다룬다: 먼저 bias–variance 분해를 쓸모 있을 만큼 탄탄하게, 그다음 실제 모델의 과적합을 진단하는 실무 플레이북 — learning curve, train/validation gap, cross-validation, 그리고 함정들. 해결책을 다룬 L1/L2·dropout 글의 진단 편이다. 분해 타깃 \(y = f(x) + \varepsilon\), 노이즈 \(\mathbb{E}[\varepsilon]=0\), \(\text{Var}(\varepsilon)=\sigma^2\)라 하자. 무작위 데이터셋으로 모델 \(\hat f\)를 학습시키고, 한 점 \(x\)에서의 기대 squared error를 — 노이즈와 어떤 training set을 뽑았는지의 무작위성 둘 다에 대해 평균 내어 — 보면: ...
Dropout은 두 아이디어를 붙여놓은 것이다: 학습 때 유닛을 무작위로 꺼버리고, 추론 때는 조용히 전부 다시 켠다. 흥미로운 지점은 (1) 유닛을 무작위로 끄는 게 왜 일반화를 개선하는가, 그리고 (2) “전부 다시 켠다"가 공짜가 아니라는 것 — 보정을 안 하면 activation이 잘못된 스케일로 나온다. 이 스케일링을 틀리는 건 딥러닝에서 가장 흔한 버그 중 하나라, 이 글은 둘 다 L1/L2 글과 같은 엔지니어링 깊이로 짚는다. Dropout이 하는 일 학습 중 dropout layer는 각 유닛의 activation \(a\)를, 독립적으로, keep probability \(p\)로 유지하거나 \(1-p\)로 0으로 만든다. 형식적으로는 유닛마다 mask \(m \sim \text{Bernoulli}(p)\)를 뽑아 \(m \cdot a\)를 출력한다. ...
“L1은 weight를 sparse하게 만들고, L2는 weight를 작게 만든다” Regularization이란 무엇인가 용량(capacity)이 충분한 모델은 noise를 포함한 데이터를 외워버려서 training loss를 0까지 떨어뜨릴 수 있다. (overfitting) Linear model에서 이건 계수가 크고 서로 상쇄되는 값으로 폭발하는 형태로 나타난다. 이는 OLS 해 \(\hat{\mathbf{w}} = (X^\top X)^{-1}X^\top y\)의 증폭 효과 때문이다. \(X^\top X\)에 작은 eigenvalue(= 데이터에서 분산이 거의 없는 방향, 예컨대 거의 동일한 두 feature)가 있으면 그 역행렬엔 큰 eigenvalue가 생기고, \(y\)의 작은 노이즈가 그 방향으로 투영되며 큰 계수로 증폭된다. 구체적으로 \(x_1 \approx x_2\)라면, \(y\)를 설명하는 데는 \(w_1=1,\,w_2=0\)이면 충분하지만 마지막 노이즈까지 fit하기 위해 weight에 \(w_1=1000,\,w_2=-999\) 같은 값이 나타난다. \(x_1 - x_2\)는 거의 0인 방향이라, 큰 계수를 곱해야 노이즈만큼의 미세한 출력이 나오기 때문이다. 큰 \(|\mathbf{w}|\)는 함수가 노이즈에 맞추려 뒤틀린 증거다. ...