AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
띄어쓰기·숫자·문장부호 차이를 오류로 보지 않고, 오류를 종류별로 구분합니다
한국어 STT CER: 띄어쓰기·숫자·문장부호 차이를 오류로 세지 않고, 오류를 종류별로 분해 | Korean ASR CER that doesn't count spacing, punctuation or number notation (3개 = 세 개) as errors, and breaks errors down by type.
개발 도구Python · HTMLGitHub @Choi-kwan-u온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗한국어 · English
한국어 STT용 CER: 글자가 아니라 읽기가 같은지로 채점합니다. 띄어쓰기·문장부호·숫자 표기(3개 = 세 개)·영문 약어(AI = 에이아이) 차이는 오류로 세지 않습니다.
설치 없이 브라우저에서 써보기 → 정답과 STT 출력을 붙여넣으면 기존 CER, kocer CER, 오류 분해가 바로 나옵니다.
위 그림의 막대 하나를 내 모델 출력으로 한 줄에 얻습니다. 기존 CER은 그대로 두고, 그 오류가 어디서 왔는지 나눠 보여줍니다.
기존 CER은 정답과 받아쓰기를 글자 그대로 비교한 값입니다. 띄어쓰기와 문장부호도 글자로 셉니다. jiwer의
cer()와 Hugging Faceevaluate의cer가 이렇게 계산하고, 아래 출력의raw CER이 이 값입니다.
>>> import kocer
>>> print(kocer.breakdown(refs, hyps)) # whisper-large-v3, Zeroth test 457발화
raw CER 6.88% → kocer CER 2.90% (1,753 raw errors, 457 pairs, kocer 0.5.0)
띄어쓰기 704 40% ████████████████
문장부호 226 13% █████
숫자·영문 263 15% ██████
진짜 오류 560 32% █████████████
정답이 "세 시에 만나"인데 모델이 "3시에 만나"라고 받아쓰면, 맞은 걸까요? 사람이 보면 맞았습니다. 그런데 기존 CER은 글자가 다르니 틀렸다고 셉니다. 띄어쓰기와 문장부호를 빼고 재는 nlptutti 같은 한국어 도구도 숫자 표기 차이는 그대로 오류로 셉니다.
이게 진짜 문제가 되는 건 모델을 비교할 때입니다. 숫자를 아라비아로 쓰는 모델과 한글로 쓰는 모델이 있으면, 정답 전사가 어느 쪽으로 적혀 있느냐에 따라 이기는 쪽이 바뀝니다. 실력이 아니라 숫자 쓰는 습관을 비교하게 되는 겁니다.
실제로 확인한 사례입니다. 같은 두 모델의 같은 받아쓰기 결과를, 정답의 숫자 표기만 바꿔서 채점했습니다.
| 정답 전사의 숫자 표기 | 띄어쓰기·문장부호를 뺀 CER의 결론 | kocer의 결론 |
|---|---|---|
아라비아 숫자 (3시) | 새 모델이 더 좋다 | 새 모델이 더 좋다 |
한글 (세 시) | 새 모델이 더 나쁘다 | 새 모델이 더 좋다 |
띄어쓰기와 문장부호를 빼고 재도, 정답을 어떻게 적었느냐에 따라 정반대 결론이 나옵니다. kocer는 같은 말을 다르게 적은 것을 맞은 것으로 보기 때문에, 어느 쪽 정답으로 재도 같은 결론이 나옵니다.
그래서 비슷한 모델끼리, 특히 새 버전이 숫자 쓰는 습관을 바꿨을 때 필요합니다. 모델끼리 실력 차이가 크면 어떤 도구로 재도 순위는 거의 같습니다. 차이가 우연으로 설명되는지는 kocer.compare()로 확인할 수 있습니다.
(3시)/(세 시)처럼 두 가지로 적어 두어서, 같은 음성에 대해 두 정답을 만들 수 있습니다.3시면 새 모델이 8.50%p 좋고, 세 시면 4.13%p 나쁩니다.3시면 새 모델이 2.36%p 좋고, 세 시여도 2.41%p 좋습니다.3시면 새 모델이 6.58%p 좋고, 세 시면 0.47%p 차이로 구분되지 않습니다. kocer로는 3.77%p, 3.66%p로 둘 다 새 모델이 좋습니다.자세한 내용은 benchmarks/에 있습니다.
pip install kocer # 순수 파이썬, 의존성 없음
pip install "kocer[fast]" # rapidfuzz로 수십 배 빠름. 많이 재거나 긴 글을 잴 때 권장
import kocer
kocer.cer("사과 세 개 주세요", "사과 3개 주세요") # 0.0
kocer.cer(refs, hyps) # 리스트는 코퍼스 단위 (jiwer와 같은 집계)
r = kocer.measure("세 시 삼십 분", "3시 30분")
r.cer, r.raw_cer # 0.0, 0.625 (정규화 후, 정규화 전)
print(kocer.breakdown(refs, hyps)) # 오류를 종류별로 분해 (위 출력)
print(kocer.compare(refs, {"새 모델": new, "이전 모델": old})) # 모델 비교: 순위와 차이의 95% 신뢰구간
kocer.cer("유니스트 입학", "UNIST 입학", aliases={"UNIST": "유니스트"}) # 단어처럼 읽는 약어
kocer.cer("(7시)/(일곱 시)에 만나", "7시에 만나") # 0.0 AI-Hub 이중 표기는 양쪽 다 정답
kocer.cer("b/ 그거+ 그거 *좋아", "그거 그거 좋아", aihub=True) # 0.0 AI-Hub 원본 전사를 정제 없이
kocer ref.txt hyp.txt # 줄 단위 쌍. raw CER, kocer CER, 오류 분해
kocer ref.txt hyp.txt --diff # 틀린 글자만 색으로 표시
kocer ref.txt new.txt old.txt # 결과 파일이 둘 이상이면 모델 비교
kocer.wer(ref, hyp)
r = kocer.measure("세 시 삼십 분", "3시 30분")
r.ops # {'S': 0, 'D': 0, 'I': 0}
r.alignment # [('H', '세', '세'), ...]
r.chosen_ref # 숫자 후보 중 실제로 채택된 정답
r.chosen_hyp # '세시 삼십분'
r.ref_len # 5: 분모. 정답을 정규화한 글자 수로, 결과가 무엇이든 같다
b = kocer.breakdown(refs, hyps)
b.spacing, b.punctuation, b.numbers_letters, b.remaining # 합이 b.raw_errors
b.shares # 비율
b.cer # {'raw':…, 'nospace':…, 'nopunct':…, 'kocer':…}
c = kocer.compare(refs, {"A": hyps_a, "B": hyps_b, "C": hyps_c}, n_boot=2000, seed=0)
c.cer # 시스템별 CER
c.difference("A", "B") # (CER A − CER B, 95% 하한, 상한). 문장 단위 paired bootstrap, 순서 무관
c.significant("A", "B") # 차이가 0이 아닌가
c.ranks["A"] # (순위 하한, 상한, 1위 확률)
print(c) # 표로 출력. numpy가 있으면 빠르게, 없으면 순수 파이썬으로 계산
kocer.cer("엘지 트윈스", "LG 트윈스") # 0.0 대문자 연속은 철자 읽기
kocer.cer("케이비오 리그", "KBO 리그") # 0.0
jiwer, nlptutti, kocer는 모두 같은 편집거리로 CER을 계산합니다. 다른 건 무엇을 오류로 세느냐입니다. jiwer는 띄어쓰기·문장부호도 오류로 세고, nlptutti는 그 둘은 무시하지만 숫자·영문 표기 차이는 셉니다. kocer는 숫자와 영문 약어 읽기까지 같은 말로 봅니다.
| 정답 | STT 결과 | jiwer | nlptutti | kocer |
|---|---|---|---|---|
| 네, 알겠습니다. | 네 알겠습니다 | 0.222 | 0.000 | 0.000 |
| 사과 세 개 주세요 | 사과 3개 주세요 | 0.200 | 0.143 | 0.000 |
| 세 시 삼십 분 | 3시 30분 | 0.625 | 0.600 | 0.000 |
| 이천이십사 년 십이 월 | 2024년 12월 | 0.750 | 0.778 | 0.000 |
| 에이아이 모델 | AI 모델 | 0.571 | 0.667 | 0.000 |
| 사과 세 개 주세요 | 사과 네 개 주세요 | 0.100 | 0.143 | 0.143 |
| 세 시 삼십 분 | 3시 13분 | 0.625 | 0.600 | 0.400 |
마지막 두 줄은 진짜 인식 오류이고, kocer도 오류로 셉니다. jiwer 4.0.0, nlptutti 0.0.0.24, kocer 0.5.0, 모두 기본 설정.
개체명·키워드 평가나 품질 게이트(기준 점수로 CI 막기)가 필요하면 nlptutti가 낫습니다. 두 도구를 같이 써도 됩니다.
| jiwer | nlptutti | kocer | |
|---|---|---|---|
| 띄어쓰기·문장부호 무시 | 직접 설정 | 기본 | 기본 |
| 숫자 읽기 차이 무시 (3개 = 세 개) | 없음 | 없음 | 기본 |
| 영문 약어 읽기 차이 무시 (AI = 에이아이) | 없음 | 없음 | 기본, 사용자 사전 지원 |
| 정규화 전·후 CER을 한 번에 반환 | 없음 | 없음 | raw_cer, cer |
| 오류를 띄어쓰기·문장부호·숫자·진짜 오류로 분해 | 없음 | 없음 | breakdown() |
| 여러 모델 비교, 쌍별 신뢰구간 | 없음 | 있음 | compare() |
| 품질 게이트 (기준 점수로 CI 막기) | 없음 | 있음 | 없음 |
| 개체명·키워드 오류율 | 없음 | 있음 | 없음 |
| 지원 지표 | WER, CER, MER, WIL, WIP | CER, WER, CRR | CER, WER |
| 필수 의존성 | rapidfuzz, click | jiwer | 없음 |
오십 퍼센트 / 50%도 jiwer와 nlptutti는 1.000, kocer는 0.000입니다.
kocer는 글자가 같은지가 아니라 읽으면 같은 말인지를 잽니다. 띄어쓰기와 문장부호를 지우고, 숫자·영문 약어·단위는 가능한 읽기 중 CER이 가장 낮은 것을 고릅니다. 모든 단계는 끌 수 있고, 정규화 전 CER(raw_cer)을 항상 같이 돌려줍니다. 정규화가 실제 오류를 숨길 수 있고, 자막처럼 표기 자체가 결과물인 경우도 있으니 두 값을 같이 보고하세요.
| 단계 | 기본 | 끄기 | 예 |
|---|---|---|---|
| 유니코드 NFKC, 폭 없는 문자 제거 | 켬 | - | 자모 분리 입력, 전각 AI = AI, ㎞ = km, BOM·\u200b 제거 |
| 문장부호 제거 | 켬 | punct=False | 네, 알겠습니다. = 네 알겠습니다 |
| 필러 제거 (음, 어, 그) | 끔 | fillers=True | 실제 단어 "그"도 지워질 수 있어 기본은 끔 |
| 영문 약어·단위 | 켬 | letters=False | AI = ai = 에이아이, CEO = 씨이오, NASA = 나사, 50% = 오십 퍼센트 = 50프로, 5m = 오 미터, 5㎡ = 오 제곱미터, 100W = 백 와트, 2GB = 이 기가바이트, C4 = 씨포 |
| 숫자 후보 전개 | 켬 | numbers=False | 3개 = 세 개, 3층 = 삼 층, 3:30 = 세 시 삼십 분, -5도 = 영하 오 도, $100 = 백 달러, 6월 = 유월, 1만 = 만, 1/2 = 이분의 일 |
| AI-Hub 이중 표기 | 켬 | dual=False | (7시)/(일곱 시) = 7시 = 일곱 시 |
| AI-Hub 잡음 태그·표시 제거 | 끔 | aihub=True로 켬 | b/, n/, 어/, +, *, (NO:), /(noise), @웃음 제거. 가림 태그 @이름1, &이름&은 남김(한계 참고). 일반 텍스트의 C++를 지키려고 기본은 끔 |
| 공백 제거 | CER만 | - | WER은 공백 유지. WER은 숫자 후보도 기본으로 끔(3개 → 는 어절 수가 바뀜) |
normalize=False면 jiwer와 같은 순수 CER을 돌려줍니다(앞뒤 공백만 뗌).
숫자: 정답 하나가 아니라 후보 집합. 숫자는 조수사에 따라 읽기가 달라서(3개 = 세 개, 3층 = 삼 층) 규칙 하나로 정답을 만들 수 없습니다. 숫자마다 {원문 숫자, 한자어 읽기, 고유어 읽기, 자릿수 읽기} 후보를 만들고 CER이 최소가 되는 조합을 고릅니다.
35명 = 삼십오 명 = 서른다섯 명).010 = 공일공 = 영일영, 2024 = 이공이사)도 후보입니다. 두세 자리 숫자의 자릿수 읽기는 조수사가 붙지 않을 때만 받습니다: 34일과 삼 사 일(3~4일)은 다른 말입니다.점만 받고 구어 쩜은 받지 않습니다. 6월 = 유월, 10월 = 시월, 1번째 = 첫 번째.계산식. CER = (치환 + 삭제 + 삽입) / 정답 글자 수. 정규화는 정답과 결과에 같은 순서로 적용하고, 공백을 뺀 글자 단위로 셉니다. 분모(ref_len)는 정답을 적힌 그대로 정규화한 글자 수이고 후보 선택과 무관하므로, 모델이 숫자를 어떻게 썼든 같은 문장은 같은 분모로 채점됩니다(이중 표기는 철자 쪽 길이). raw_cer의 분모는 공백을 포함해 다르니 둘을 비율로 비교하지 말고, 오류 개수가 필요하면 breakdown()을 쓰세요. 결과는 항상 같습니다(결정적).
공개 한국어 ASR 7개 설정을 Zeroth, FLEURS, Common Voice 27, KsponSpeech(합계 7,418발화)에서 돌리고, 같은 출력을 네 단계로 채점했습니다. AI-Hub 전화 음성(1,936발화)에서도 따로 확인했습니다.
일 세기 전/1세기 전).모델별 점수, 단계별 CER, 신뢰구간, 디코딩 설정, 재현 스크립트는 benchmarks/에 있습니다. KsponSpeech와 전화 음성 결과는 한국지능정보사회진흥원(NIA)의 AI-Hub에서 제공한 '한국어 음성', '저음질 전화망 음성인식' 데이터를 활용했고, 약관상 숫자 표만 공개합니다.
| 모델 | Zeroth | FLEURS | Common Voice 27 | KsponSpeech clean / other | 전화 (HR) |
|---|---|---|---|---|---|
| TeamUNIVA/qwen3_asr_1.7b_ko | 2.32 | 4.19 | 5.97 | 6.08 / 6.46 | 5.69 |
| TeamUNIVA/qwen3_asr_1.7b_ko_beta | 2.42 | 4.25 | 6.05 | 6.80 / 7.23 | 9.05 |
| BuzzASR/korean | 3.08 | 4.28 | 3.88 | 10.06 / 9.29 | 5.31 |
| openai/whisper-large-v3 | 2.90 | 4.24 | 5.80 | 11.06 / 11.85 | 4.97 |
| Qwen/Qwen3-ASR-1.7B | 2.90 | 4.25 | 6.61 | 8.69 / 8.57 | 10.69 |
| SenseVoiceSmall (itn 켬) | 2.40 | 6.81 | 9.68 | 13.26 / 13.77 | 21.29 |
| SenseVoiceSmall (itn 끔) | 2.41 | 6.76 | 9.72 | 13.07 / 13.67 | 21.49 |
단위 %, 총 오류 / 총 정답 글자. KsponSpeech와 전화는 철자 정답 기준. 전화는 AI-Hub 저음질 전화망 검증셋의 HR 분야(구직·교육 문의 통화) 1,936발화입니다. 모든 데이터셋에서 이기는 모델은 없고, 각 모델은 학습 데이터와 가까운 데이터셋에서 강합니다(BuzzASR은 Common Voice, TeamUNIVA는 KsponSpeech로 보임). 전화 음성에서는 whisper-large-v3가 가장 강했고, 낭독체(FLEURS, Common Voice)로 파인튜닝한 BuzzASR은 원래 모델인 whisper-large-v3를 넘지 못했습니다(원인은 확인하지 않음). 디코딩은 모두 greedy이고, BuzzASR을 모델카드 권장 설정(반복 억제)으로 다시 돌리면 다섯 데이터셋 모두 오히려 나빠집니다(자세히).
aliases로 넘기세요.%, 길이·넓이·부피(m, km, m2 …), 무게(g, kg, t), L, 전력(W, kWh), 용량(KBTB), 주파수(HzGHz), ℃·℉, 통화 기호 $ ₩ € £ ¥만 읽습니다. 분수는 1/2 꼴만 읽고 날짜(2026/10/29)는 읽지 않습니다. 조수사 사전도 완전하지 않습니다. tests/cases.tsv에 케이스를 추가하는 PR을 환영합니다.@이름1, &이름&)는 aihub=True로도 지우지 않습니다. 지우면 모델이 받아쓴 실제 이름이 전부 삽입 오류가 되기 때문입니다. 그런 문장은 빼고 재세요.kocer[fast]로 재세요(8,200자 한 덩어리 0.03초). rapidfuzz 없이 순수 파이썬으로는 정렬이 글자 수의 제곱에 비례해서, 수천 자를 한 덩어리로 넣으면 느리고 메모리를 많이 씁니다. 그때는 문장 단위로 나눠 넣으세요.점수를 적을 때는 버전을 함께 적어 주세요. 정규화 규칙이 버전마다 바뀌어 같은 출력도 점수가 다를 수 있습니다. 0.4.0에서 분모와 후보 규칙이, 0.5.0에서 후보 탐색이 바뀌었으니 다른 마이너 버전의 점수와 섞지 마세요(CHANGELOG). 0.5.0부터 채점 규칙은 동결되어 있고, 점수가 바뀌는 수정은 마이너 버전을 올려서만 합니다. raw_cer도 같이 적으면 다른 도구로 잰 숫자와 비교할 수 있습니다.
CER 6.88% (raw) / 2.90% (kocer 0.5.0)
레포 오른쪽의 "Cite this repository"에서 인용 형식을 복사할 수 있습니다(CITATION.cff).
@software{kocer,
author = {Choi, Kwan-u},
title = {kocer: reading-based CER/WER for Korean speech recognition},
year = {2026},
version = {0.5.1},
url = {https://github.com/Choi-kwan-u/kocer}
}
cer()·wer() 시그니처가 이것을 따릅니다.도움이 됐다면 ⭐ 스타를 눌러 주세요. 다른 한국어 STT 연구자들이 이 도구를 찾는 데 도움이 됩니다.
세 개