본문 바로가기
C.W.K.
Stream
Lesson 04 of 05 · published

배치 모델로 실시간 흉내 내기

~12 min · batch-vs-streaming, localagreement, real-time, preview

Level 0불씨 없음
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"추측을 넣어 놓고 되무를 순 없어. 그러니 더 이상 추측이 아닌 부분만 넣어."

불일치

Whisper 계열 모델은 근본적으로 배치 야. 오디오 덩어리를 주면 전사를 받아. 그런데 좋은 받아쓰기는 스트리밍 처럼 느껴져. 말하는 대로 단어가 나타나니까. 순진하게 다리를 놓으면, 그러니까 '30초 녹음하고, 한 번 전사하고, 붙여넣기' 하면 기술적으론 되지만 죽은 느낌이야. 말하는 동안 아무 반응이 없고, 끝나고 한참 멈춰 있거든. 라이브한 느낌을 내려면 배치 모델 위에 스트리밍을 흉내 내야 해. 조심해서. 배치 모델은 뒤를 더 들으면서 앞 단어에 대한 생각을 계속 바꾸니까.

LocalAgreement

비결은 엔진 위에 앉는 스트리밍 컨트롤러야. 롤링 버퍼를 유지하고, 오디오가 커지면서 배치 모델을 반복 돌리고, 각 새 가설을 이전 것과 비교해. 연달아 돌린 두 패스가 앞부분에서 일치하면, 그 앞부분을 커밋해. 최종으로 보여줘도 될 만큼 안정됐다는 뜻이니까. 아직 안 맞는 꼬리는 프리뷰로만 보여 주고 절대 최종으로 취급 안 해. 그게 LocalAgreement 아이디어야. 연달아 돌린 패스가 일치한 다음에만 텍스트를 확정하고, 아직 확정 안 된 꼬리는 보여만 주고, 안전할 때 문장 경계에서 버퍼를 다듬는 거지.

패스 N:    "보고서를 보내"
패스 N+1:  "보고서를 화요일에 보내"     앞부분 일치 ->
           ^^^^^^^^  커밋됨                "보고서를"
                     ^^^^^^^^^^  아직 꼬리 -> 프리뷰만

# 일치한 앞부분만 커밋. 흔들리는 꼬리는 절대 삽입 안 함.

MVP 규율

말하는 동안 라이브로 타이핑하는 게 어려운 버전이고, 거기서 시작하는 건 함정이야. 솔직한 MVP 는 부분 전사를 오버레이에 프리뷰로 보여 주다가 키를 놓을 때 한 번만 삽입해. 그러면서도 MVP 안에서 '커밋됨' 과 '아직 아님' 은 내부적으로 갈라 놔. 그래야 나중에 진짜 라이브 타이핑을 붙이는 날, 기계는 이미 다 있고 커밋된 텍스트가 어디로 갈지만 바꾸면 되거든. 오버레이 대신 앱으로. 데이터 모델은 어려운 버전에 맞춰 짓고, 배포는 쉬운 버전부터 해.

되무를 수 있는 텍스트를 절대 삽입하지 마. 부분 가설을 넣어 놓고 라이브 앱에서 고치는 건 유저 발밑에서 글자를 지우는 거야. 잘해야 덜컹거리고, 나쁘면 파괴적이지. 안정된 것, 그러니까 패스끼리 일치한 부분만 커밋해. 아직 흔들리는 꼬리는 프리뷰로 두고. 그래야 유저가 글자 고쳐지는 걸 겪지 않고 움직이는 것만 봐.
모델링은 어려운 버전으로, 배포는 쉬운 버전으로. MVP 가 끝에 한 번만 삽입하더라도, 커밋됐는지 아닌지는 내부에서 추적해 둬. 지금은 거의 공짜고, 나중에 라이브 타이핑을 붙일 때 다시 짜는 게 아니라 조금만 고치면 되거든. 싸게 미리 맞춰 두는 쪽이 나중에 시스템을 통째로 다시 짓는 것보다 나아.

Code

일치한 접두부를 커밋, 꼬리는 프리뷰로·swift
struct StreamingState {
    var committed = ""   // 안정: 연속 패스가 일치함
    var tail = ""        // 휘발: 프리뷰로 보임, 아직 삽입 안 함
}

func onNewHypothesis(_ hypo: String, _ state: inout StreamingState) {
    let agreed = longestCommonPrefix(state.committed + state.tail, hypo)
    if agreed.count > state.committed.count {
        state.committed = agreed          // 새로 안정된 텍스트 승격
    }
    state.tail = String(hypo.dropFirst(state.committed.count))
    overlay.show(committed: state.committed, tail: state.tail)
    // MVP: 놓음 때만 insert(state.committed).
    // 나중 라이브-타이핑: 안정되는 대로 커밋 델타 삽입.
}

External links

Exercise

모델이 생각을 바꿀 때마다 부분 가설을 전부 유저 문서에 바로 삽입하면 유저 눈에 뭐가 보일지 적어봐. 그다음 LocalAgreement 버전도 적어봐. 어느 쪽이 유저가 읽고 있던 단어를 지울 수 있어? 그리고 왜 '프리뷰' 가 흔들리는 텍스트를 두기에 안전한 자리야?
Hint
바로 삽입한다는 건 모델이 고칠 때마다 앱이 이미 놓인 단어를 다시 쓴다는 뜻이야. 커서 밑에서 글자가 사라졌다 다시 나타나고. LocalAgreement 는 흔들리는 텍스트를 유저가 '아직 임시구나' 하고 아는 프리뷰 오버레이에 두고, 더 이상 안 바뀌는 것만 진짜 문서에 써. 오버레이가 바로 추측이 틀려도 되는 자리야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.