Spaces:
Sleeping
Sleeping
한국어 TTS 사람 청취평가 — 의원 접수대 100문장 × TTS 2종 블라인드 평가
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +145 -0
- README.md +84 -6
- app.py +338 -0
- audio/manifest.json +0 -0
- audio/mms-ko/clinic-000.wav +0 -0
- audio/mms-ko/clinic-001.wav +0 -0
- audio/mms-ko/clinic-002.wav +0 -0
- audio/mms-ko/clinic-003.wav +0 -0
- audio/mms-ko/clinic-004.wav +0 -0
- audio/mms-ko/clinic-005.wav +3 -0
- audio/mms-ko/clinic-006.wav +3 -0
- audio/mms-ko/clinic-007.wav +0 -0
- audio/mms-ko/clinic-008.wav +0 -0
- audio/mms-ko/clinic-009.wav +0 -0
- audio/mms-ko/clinic-010.wav +3 -0
- audio/mms-ko/clinic-011.wav +3 -0
- audio/mms-ko/clinic-012.wav +0 -0
- audio/mms-ko/clinic-013.wav +3 -0
- audio/mms-ko/clinic-014.wav +0 -0
- audio/mms-ko/clinic-015.wav +3 -0
- audio/mms-ko/clinic-016.wav +3 -0
- audio/mms-ko/clinic-017.wav +3 -0
- audio/mms-ko/clinic-018.wav +3 -0
- audio/mms-ko/clinic-019.wav +3 -0
- audio/mms-ko/clinic-020.wav +3 -0
- audio/mms-ko/clinic-021.wav +0 -0
- audio/mms-ko/clinic-022.wav +3 -0
- audio/mms-ko/clinic-023.wav +0 -0
- audio/mms-ko/clinic-024.wav +3 -0
- audio/mms-ko/clinic-025.wav +3 -0
- audio/mms-ko/clinic-026.wav +3 -0
- audio/mms-ko/clinic-027.wav +3 -0
- audio/mms-ko/clinic-028.wav +3 -0
- audio/mms-ko/clinic-029.wav +3 -0
- audio/mms-ko/clinic-030.wav +0 -0
- audio/mms-ko/clinic-031.wav +3 -0
- audio/mms-ko/clinic-032.wav +0 -0
- audio/mms-ko/clinic-033.wav +3 -0
- audio/mms-ko/clinic-034.wav +0 -0
- audio/mms-ko/clinic-035.wav +3 -0
- audio/mms-ko/clinic-036.wav +3 -0
- audio/mms-ko/clinic-037.wav +0 -0
- audio/mms-ko/clinic-038.wav +3 -0
- audio/mms-ko/clinic-039.wav +0 -0
- audio/mms-ko/clinic-040.wav +3 -0
- audio/mms-ko/clinic-041.wav +3 -0
- audio/mms-ko/clinic-042.wav +3 -0
- audio/mms-ko/clinic-043.wav +3 -0
- audio/mms-ko/clinic-044.wav +0 -0
- audio/mms-ko/clinic-045.wav +3 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,148 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
audio/mms-ko/clinic-005.wav filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
audio/mms-ko/clinic-006.wav filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
audio/mms-ko/clinic-010.wav filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
audio/mms-ko/clinic-011.wav filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
audio/mms-ko/clinic-013.wav filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
audio/mms-ko/clinic-015.wav filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
audio/mms-ko/clinic-016.wav filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
audio/mms-ko/clinic-017.wav filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
audio/mms-ko/clinic-018.wav filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
audio/mms-ko/clinic-019.wav filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
audio/mms-ko/clinic-020.wav filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
audio/mms-ko/clinic-022.wav filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
audio/mms-ko/clinic-024.wav filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
audio/mms-ko/clinic-025.wav filter=lfs diff=lfs merge=lfs -text
|
| 50 |
+
audio/mms-ko/clinic-026.wav filter=lfs diff=lfs merge=lfs -text
|
| 51 |
+
audio/mms-ko/clinic-027.wav filter=lfs diff=lfs merge=lfs -text
|
| 52 |
+
audio/mms-ko/clinic-028.wav filter=lfs diff=lfs merge=lfs -text
|
| 53 |
+
audio/mms-ko/clinic-029.wav filter=lfs diff=lfs merge=lfs -text
|
| 54 |
+
audio/mms-ko/clinic-031.wav filter=lfs diff=lfs merge=lfs -text
|
| 55 |
+
audio/mms-ko/clinic-033.wav filter=lfs diff=lfs merge=lfs -text
|
| 56 |
+
audio/mms-ko/clinic-035.wav filter=lfs diff=lfs merge=lfs -text
|
| 57 |
+
audio/mms-ko/clinic-036.wav filter=lfs diff=lfs merge=lfs -text
|
| 58 |
+
audio/mms-ko/clinic-038.wav filter=lfs diff=lfs merge=lfs -text
|
| 59 |
+
audio/mms-ko/clinic-040.wav filter=lfs diff=lfs merge=lfs -text
|
| 60 |
+
audio/mms-ko/clinic-041.wav filter=lfs diff=lfs merge=lfs -text
|
| 61 |
+
audio/mms-ko/clinic-042.wav filter=lfs diff=lfs merge=lfs -text
|
| 62 |
+
audio/mms-ko/clinic-043.wav filter=lfs diff=lfs merge=lfs -text
|
| 63 |
+
audio/mms-ko/clinic-045.wav filter=lfs diff=lfs merge=lfs -text
|
| 64 |
+
audio/mms-ko/clinic-047.wav filter=lfs diff=lfs merge=lfs -text
|
| 65 |
+
audio/mms-ko/clinic-048.wav filter=lfs diff=lfs merge=lfs -text
|
| 66 |
+
audio/mms-ko/clinic-049.wav filter=lfs diff=lfs merge=lfs -text
|
| 67 |
+
audio/mms-ko/clinic-050.wav filter=lfs diff=lfs merge=lfs -text
|
| 68 |
+
audio/mms-ko/clinic-051.wav filter=lfs diff=lfs merge=lfs -text
|
| 69 |
+
audio/mms-ko/clinic-052.wav filter=lfs diff=lfs merge=lfs -text
|
| 70 |
+
audio/mms-ko/clinic-053.wav filter=lfs diff=lfs merge=lfs -text
|
| 71 |
+
audio/mms-ko/clinic-054.wav filter=lfs diff=lfs merge=lfs -text
|
| 72 |
+
audio/mms-ko/clinic-055.wav filter=lfs diff=lfs merge=lfs -text
|
| 73 |
+
audio/mms-ko/clinic-056.wav filter=lfs diff=lfs merge=lfs -text
|
| 74 |
+
audio/mms-ko/clinic-057.wav filter=lfs diff=lfs merge=lfs -text
|
| 75 |
+
audio/mms-ko/clinic-061.wav filter=lfs diff=lfs merge=lfs -text
|
| 76 |
+
audio/mms-ko/clinic-063.wav filter=lfs diff=lfs merge=lfs -text
|
| 77 |
+
audio/mms-ko/clinic-064.wav filter=lfs diff=lfs merge=lfs -text
|
| 78 |
+
audio/mms-ko/clinic-066.wav filter=lfs diff=lfs merge=lfs -text
|
| 79 |
+
audio/mms-ko/clinic-067.wav filter=lfs diff=lfs merge=lfs -text
|
| 80 |
+
audio/mms-ko/clinic-068.wav filter=lfs diff=lfs merge=lfs -text
|
| 81 |
+
audio/mms-ko/clinic-069.wav filter=lfs diff=lfs merge=lfs -text
|
| 82 |
+
audio/mms-ko/clinic-070.wav filter=lfs diff=lfs merge=lfs -text
|
| 83 |
+
audio/mms-ko/clinic-071.wav filter=lfs diff=lfs merge=lfs -text
|
| 84 |
+
audio/mms-ko/clinic-072.wav filter=lfs diff=lfs merge=lfs -text
|
| 85 |
+
audio/mms-ko/clinic-073.wav filter=lfs diff=lfs merge=lfs -text
|
| 86 |
+
audio/mms-ko/clinic-074.wav filter=lfs diff=lfs merge=lfs -text
|
| 87 |
+
audio/mms-ko/clinic-075.wav filter=lfs diff=lfs merge=lfs -text
|
| 88 |
+
audio/mms-ko/clinic-077.wav filter=lfs diff=lfs merge=lfs -text
|
| 89 |
+
audio/mms-ko/clinic-078.wav filter=lfs diff=lfs merge=lfs -text
|
| 90 |
+
audio/mms-ko/clinic-079.wav filter=lfs diff=lfs merge=lfs -text
|
| 91 |
+
audio/mms-ko/clinic-080.wav filter=lfs diff=lfs merge=lfs -text
|
| 92 |
+
audio/mms-ko/clinic-081.wav filter=lfs diff=lfs merge=lfs -text
|
| 93 |
+
audio/mms-ko/clinic-082.wav filter=lfs diff=lfs merge=lfs -text
|
| 94 |
+
audio/mms-ko/clinic-083.wav filter=lfs diff=lfs merge=lfs -text
|
| 95 |
+
audio/mms-ko/clinic-084.wav filter=lfs diff=lfs merge=lfs -text
|
| 96 |
+
audio/mms-ko/clinic-086.wav filter=lfs diff=lfs merge=lfs -text
|
| 97 |
+
audio/mms-ko/clinic-087.wav filter=lfs diff=lfs merge=lfs -text
|
| 98 |
+
audio/mms-ko/clinic-089.wav filter=lfs diff=lfs merge=lfs -text
|
| 99 |
+
audio/mms-ko/clinic-090.wav filter=lfs diff=lfs merge=lfs -text
|
| 100 |
+
audio/mms-ko/clinic-091.wav filter=lfs diff=lfs merge=lfs -text
|
| 101 |
+
audio/mms-ko/clinic-092.wav filter=lfs diff=lfs merge=lfs -text
|
| 102 |
+
audio/mms-ko/clinic-098.wav filter=lfs diff=lfs merge=lfs -text
|
| 103 |
+
audio/qwen3-tts-1.7b/clinic-001.wav filter=lfs diff=lfs merge=lfs -text
|
| 104 |
+
audio/qwen3-tts-1.7b/clinic-002.wav filter=lfs diff=lfs merge=lfs -text
|
| 105 |
+
audio/qwen3-tts-1.7b/clinic-006.wav filter=lfs diff=lfs merge=lfs -text
|
| 106 |
+
audio/qwen3-tts-1.7b/clinic-008.wav filter=lfs diff=lfs merge=lfs -text
|
| 107 |
+
audio/qwen3-tts-1.7b/clinic-009.wav filter=lfs diff=lfs merge=lfs -text
|
| 108 |
+
audio/qwen3-tts-1.7b/clinic-010.wav filter=lfs diff=lfs merge=lfs -text
|
| 109 |
+
audio/qwen3-tts-1.7b/clinic-011.wav filter=lfs diff=lfs merge=lfs -text
|
| 110 |
+
audio/qwen3-tts-1.7b/clinic-013.wav filter=lfs diff=lfs merge=lfs -text
|
| 111 |
+
audio/qwen3-tts-1.7b/clinic-014.wav filter=lfs diff=lfs merge=lfs -text
|
| 112 |
+
audio/qwen3-tts-1.7b/clinic-015.wav filter=lfs diff=lfs merge=lfs -text
|
| 113 |
+
audio/qwen3-tts-1.7b/clinic-016.wav filter=lfs diff=lfs merge=lfs -text
|
| 114 |
+
audio/qwen3-tts-1.7b/clinic-017.wav filter=lfs diff=lfs merge=lfs -text
|
| 115 |
+
audio/qwen3-tts-1.7b/clinic-018.wav filter=lfs diff=lfs merge=lfs -text
|
| 116 |
+
audio/qwen3-tts-1.7b/clinic-019.wav filter=lfs diff=lfs merge=lfs -text
|
| 117 |
+
audio/qwen3-tts-1.7b/clinic-020.wav filter=lfs diff=lfs merge=lfs -text
|
| 118 |
+
audio/qwen3-tts-1.7b/clinic-022.wav filter=lfs diff=lfs merge=lfs -text
|
| 119 |
+
audio/qwen3-tts-1.7b/clinic-024.wav filter=lfs diff=lfs merge=lfs -text
|
| 120 |
+
audio/qwen3-tts-1.7b/clinic-025.wav filter=lfs diff=lfs merge=lfs -text
|
| 121 |
+
audio/qwen3-tts-1.7b/clinic-026.wav filter=lfs diff=lfs merge=lfs -text
|
| 122 |
+
audio/qwen3-tts-1.7b/clinic-027.wav filter=lfs diff=lfs merge=lfs -text
|
| 123 |
+
audio/qwen3-tts-1.7b/clinic-028.wav filter=lfs diff=lfs merge=lfs -text
|
| 124 |
+
audio/qwen3-tts-1.7b/clinic-029.wav filter=lfs diff=lfs merge=lfs -text
|
| 125 |
+
audio/qwen3-tts-1.7b/clinic-030.wav filter=lfs diff=lfs merge=lfs -text
|
| 126 |
+
audio/qwen3-tts-1.7b/clinic-031.wav filter=lfs diff=lfs merge=lfs -text
|
| 127 |
+
audio/qwen3-tts-1.7b/clinic-032.wav filter=lfs diff=lfs merge=lfs -text
|
| 128 |
+
audio/qwen3-tts-1.7b/clinic-033.wav filter=lfs diff=lfs merge=lfs -text
|
| 129 |
+
audio/qwen3-tts-1.7b/clinic-034.wav filter=lfs diff=lfs merge=lfs -text
|
| 130 |
+
audio/qwen3-tts-1.7b/clinic-036.wav filter=lfs diff=lfs merge=lfs -text
|
| 131 |
+
audio/qwen3-tts-1.7b/clinic-037.wav filter=lfs diff=lfs merge=lfs -text
|
| 132 |
+
audio/qwen3-tts-1.7b/clinic-038.wav filter=lfs diff=lfs merge=lfs -text
|
| 133 |
+
audio/qwen3-tts-1.7b/clinic-039.wav filter=lfs diff=lfs merge=lfs -text
|
| 134 |
+
audio/qwen3-tts-1.7b/clinic-040.wav filter=lfs diff=lfs merge=lfs -text
|
| 135 |
+
audio/qwen3-tts-1.7b/clinic-041.wav filter=lfs diff=lfs merge=lfs -text
|
| 136 |
+
audio/qwen3-tts-1.7b/clinic-042.wav filter=lfs diff=lfs merge=lfs -text
|
| 137 |
+
audio/qwen3-tts-1.7b/clinic-043.wav filter=lfs diff=lfs merge=lfs -text
|
| 138 |
+
audio/qwen3-tts-1.7b/clinic-045.wav filter=lfs diff=lfs merge=lfs -text
|
| 139 |
+
audio/qwen3-tts-1.7b/clinic-046.wav filter=lfs diff=lfs merge=lfs -text
|
| 140 |
+
audio/qwen3-tts-1.7b/clinic-048.wav filter=lfs diff=lfs merge=lfs -text
|
| 141 |
+
audio/qwen3-tts-1.7b/clinic-049.wav filter=lfs diff=lfs merge=lfs -text
|
| 142 |
+
audio/qwen3-tts-1.7b/clinic-050.wav filter=lfs diff=lfs merge=lfs -text
|
| 143 |
+
audio/qwen3-tts-1.7b/clinic-051.wav filter=lfs diff=lfs merge=lfs -text
|
| 144 |
+
audio/qwen3-tts-1.7b/clinic-052.wav filter=lfs diff=lfs merge=lfs -text
|
| 145 |
+
audio/qwen3-tts-1.7b/clinic-053.wav filter=lfs diff=lfs merge=lfs -text
|
| 146 |
+
audio/qwen3-tts-1.7b/clinic-054.wav filter=lfs diff=lfs merge=lfs -text
|
| 147 |
+
audio/qwen3-tts-1.7b/clinic-055.wav filter=lfs diff=lfs merge=lfs -text
|
| 148 |
+
audio/qwen3-tts-1.7b/clinic-056.wav filter=lfs diff=lfs merge=lfs -text
|
| 149 |
+
audio/qwen3-tts-1.7b/clinic-057.wav filter=lfs diff=lfs merge=lfs -text
|
| 150 |
+
audio/qwen3-tts-1.7b/clinic-058.wav filter=lfs diff=lfs merge=lfs -text
|
| 151 |
+
audio/qwen3-tts-1.7b/clinic-061.wav filter=lfs diff=lfs merge=lfs -text
|
| 152 |
+
audio/qwen3-tts-1.7b/clinic-062.wav filter=lfs diff=lfs merge=lfs -text
|
| 153 |
+
audio/qwen3-tts-1.7b/clinic-063.wav filter=lfs diff=lfs merge=lfs -text
|
| 154 |
+
audio/qwen3-tts-1.7b/clinic-064.wav filter=lfs diff=lfs merge=lfs -text
|
| 155 |
+
audio/qwen3-tts-1.7b/clinic-065.wav filter=lfs diff=lfs merge=lfs -text
|
| 156 |
+
audio/qwen3-tts-1.7b/clinic-066.wav filter=lfs diff=lfs merge=lfs -text
|
| 157 |
+
audio/qwen3-tts-1.7b/clinic-067.wav filter=lfs diff=lfs merge=lfs -text
|
| 158 |
+
audio/qwen3-tts-1.7b/clinic-068.wav filter=lfs diff=lfs merge=lfs -text
|
| 159 |
+
audio/qwen3-tts-1.7b/clinic-069.wav filter=lfs diff=lfs merge=lfs -text
|
| 160 |
+
audio/qwen3-tts-1.7b/clinic-070.wav filter=lfs diff=lfs merge=lfs -text
|
| 161 |
+
audio/qwen3-tts-1.7b/clinic-071.wav filter=lfs diff=lfs merge=lfs -text
|
| 162 |
+
audio/qwen3-tts-1.7b/clinic-072.wav filter=lfs diff=lfs merge=lfs -text
|
| 163 |
+
audio/qwen3-tts-1.7b/clinic-073.wav filter=lfs diff=lfs merge=lfs -text
|
| 164 |
+
audio/qwen3-tts-1.7b/clinic-074.wav filter=lfs diff=lfs merge=lfs -text
|
| 165 |
+
audio/qwen3-tts-1.7b/clinic-075.wav filter=lfs diff=lfs merge=lfs -text
|
| 166 |
+
audio/qwen3-tts-1.7b/clinic-076.wav filter=lfs diff=lfs merge=lfs -text
|
| 167 |
+
audio/qwen3-tts-1.7b/clinic-077.wav filter=lfs diff=lfs merge=lfs -text
|
| 168 |
+
audio/qwen3-tts-1.7b/clinic-078.wav filter=lfs diff=lfs merge=lfs -text
|
| 169 |
+
audio/qwen3-tts-1.7b/clinic-079.wav filter=lfs diff=lfs merge=lfs -text
|
| 170 |
+
audio/qwen3-tts-1.7b/clinic-080.wav filter=lfs diff=lfs merge=lfs -text
|
| 171 |
+
audio/qwen3-tts-1.7b/clinic-081.wav filter=lfs diff=lfs merge=lfs -text
|
| 172 |
+
audio/qwen3-tts-1.7b/clinic-082.wav filter=lfs diff=lfs merge=lfs -text
|
| 173 |
+
audio/qwen3-tts-1.7b/clinic-083.wav filter=lfs diff=lfs merge=lfs -text
|
| 174 |
+
audio/qwen3-tts-1.7b/clinic-084.wav filter=lfs diff=lfs merge=lfs -text
|
| 175 |
+
audio/qwen3-tts-1.7b/clinic-086.wav filter=lfs diff=lfs merge=lfs -text
|
| 176 |
+
audio/qwen3-tts-1.7b/clinic-087.wav filter=lfs diff=lfs merge=lfs -text
|
| 177 |
+
audio/qwen3-tts-1.7b/clinic-089.wav filter=lfs diff=lfs merge=lfs -text
|
| 178 |
+
audio/qwen3-tts-1.7b/clinic-090.wav filter=lfs diff=lfs merge=lfs -text
|
| 179 |
+
audio/qwen3-tts-1.7b/clinic-091.wav filter=lfs diff=lfs merge=lfs -text
|
| 180 |
+
audio/qwen3-tts-1.7b/clinic-092.wav filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -1,13 +1,91 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: pink
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version:
|
| 8 |
-
python_version: '3.13'
|
| 9 |
app_file: app.py
|
| 10 |
pinned: false
|
|
|
|
| 11 |
---
|
| 12 |
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: 한국어 TTS 사람 청취평가
|
| 3 |
+
emoji: 🎧
|
| 4 |
+
colorFrom: indigo
|
| 5 |
colorTo: pink
|
| 6 |
sdk: gradio
|
| 7 |
+
sdk_version: 5.49.1
|
|
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
+
license: cc-by-nc-4.0
|
| 11 |
---
|
| 12 |
|
| 13 |
+
# 한국어 TTS 사람 청취평가
|
| 14 |
+
|
| 15 |
+
합성음을 **블라인드로 듣고 받아쓰기 + 0~10점**을 남기는 앱.
|
| 16 |
+
[Open Ko-S2S Leaderboard](https://huggingface.co/spaces/baryonlabs/open-ko-s2s-leaderboard)
|
| 17 |
+
의 TTS 발음 지표를 사람 귀로 검증하기 위해 만들었다.
|
| 18 |
+
|
| 19 |
+
## 왜 만들었나
|
| 20 |
+
|
| 21 |
+
리더보드의 TTS 발음 점수는 합성음을 ASR(`faster-whisper-large-v3-turbo`)로
|
| 22 |
+
되받아쓴 CER 이다. RTX 4090 실측에서 이 지표가 **발음이 아니라 숫자 표기**를
|
| 23 |
+
재고 있다는 것이 드러났다.
|
| 24 |
+
|
| 25 |
+
| 구분 | n | CER |
|
| 26 |
+
| --- | ---: | ---: |
|
| 27 |
+
| Qwen3-TTS 전체 (기존 20문장) | 20 | 11.08% |
|
| 28 |
+
| ├ 숫자 표기 문장 | 9 | **23.67%** |
|
| 29 |
+
| └ 숫자 없는 문장 | 11 | **0.50%** |
|
| 30 |
+
|
| 31 |
+
`삼십 분` → `30분`, `십이 퍼센트` → `12%` 처럼 **발음은 정확한데 표기가 달라서**
|
| 32 |
+
CER 이 올랐다. 20문장 중 실제 발음 오류는 1건뿐이었다.
|
| 33 |
+
|
| 34 |
+
허용 표기(`variants`) 최소거리 채점을 도입해 이 편향을 걷어내자 숫자문장 CER 이
|
| 35 |
+
23.67% → 3.09% 로 떨어졌다.
|
| 36 |
+
|
| 37 |
+
| 모델 | 전체 | 숫자문장 | 숫자없음 |
|
| 38 |
+
| --- | ---: | ---: | ---: |
|
| 39 |
+
| Qwen3-TTS-12Hz-1.7B-CustomVoice | **1.50%** | 3.09% | 1.02% |
|
| 40 |
+
| MMS-TTS-Korean | 6.28% | 7.74% | 5.84% |
|
| 41 |
+
|
| 42 |
+
**그래도 자동 지표만으로는 확정할 수 없다.** 보정이 과한지 부족한지, 남은
|
| 43 |
+
오차가 정말 발음 문제인지는 사람이 들어봐야 안다. 그래서 이 앱을 만들었다.
|
| 44 |
+
|
| 45 |
+
## 무엇을 듣는가
|
| 46 |
+
|
| 47 |
+
**의원(1차 의료기관) 접수대 100문장 × TTS 2종 = 200 샘플.**
|
| 48 |
+
|
| 49 |
+
음성봇이 가장 먼저 투입되는 구간이 병원 접수대인데, 이 도메인의 공개 한국어
|
| 50 |
+
TTS 발음 벤치마크가 없다(CoreaSpeech 는 범용·gated, 상용 데이터는 비공개).
|
| 51 |
+
문장은 실제 한국어 의료 코퍼스에서 어휘를 채굴하되 **창구 발화로 새로 썼다** —
|
| 52 |
+
원본은 환자 질문이거나 시험 문항이라 그대로 쓸 수 없다.
|
| 53 |
+
|
| 54 |
+
범주는 접수·안내·수납·서류·응대 각 20문장이고, 한국어 TTS 가 실제로 틀리는
|
| 55 |
+
지점(한자어 연음 52, 숫자+단위 23, 고유명사 15, 받침 연음 11, 외래어 10)을
|
| 56 |
+
의도적으로 심었다.
|
| 57 |
+
|
| 58 |
+
## 측정하는 것
|
| 59 |
+
|
| 60 |
+
- **사람 CER** — 받아쓴 내용 vs 원문. 숫자 표기를 정규화한 값과 원문 그대로의
|
| 61 |
+
값을 함께 기록해서, **둘의 차이가 곧 표기가 만드는 오차의 크기**다.
|
| 62 |
+
- **주관 점수(0~10)** — 자연스러움·명료도.
|
| 63 |
+
|
| 64 |
+
리더보드의 `tts_naturalness` 는 이름과 달리 발음 정확도이고 UTMOS 는 미구현이다.
|
| 65 |
+
사람 점수가 이 부문의 첫 실제 자연스러움 데이터가 된다.
|
| 66 |
+
|
| 67 |
+
## 설계
|
| 68 |
+
|
| 69 |
+
- **블라인드**: 모델명과 정답 문장을 제출 전에는 감춘다. 정답을 먼저 보여주면
|
| 70 |
+
받아쓰기가 베끼기가 되어 명료도 측정이 무의미해진다.
|
| 71 |
+
- **표기 통제**: 사람도 `삼십 분` 을 듣고 `30분` 이라 적는다. 안내로 한글 표기를
|
| 72 |
+
요청하고, 채점에서도 숫자를 정규화하며, 참조에 허용 표기가 있으면 그중 최소
|
| 73 |
+
거리를 쓴다. 리더보드 `metrics.py` 와 같은 원리를 사람 평가에도 일관 적용했다.
|
| 74 |
+
- **출제**: 아직 안 들은 항목 우선, 그 안에서 응답 수가 적은 항목 우선.
|
| 75 |
+
|
| 76 |
+
## 저장
|
| 77 |
+
|
| 78 |
+
`HF_TOKEN`(write) 시크릿을 설정하면 응답이 데이터셋 repo 에 누적된다.
|
| 79 |
+
없으면 컨테이너 로컬에만 임시 저장되며 화면에 경고가 표시된다 —
|
| 80 |
+
조용히 유실되지 않게 하기 위함이다.
|
| 81 |
+
|
| 82 |
+
| 변수 | 기본값 | 설명 |
|
| 83 |
+
| --- | --- | --- |
|
| 84 |
+
| `HF_TOKEN` | (없음) | write 권한 토큰. 없으면 임시 저장 |
|
| 85 |
+
| `EVAL_DATASET_REPO` | `baryonlabs/ko-tts-human-eval-votes` | 응답 누적 대상 |
|
| 86 |
+
|
| 87 |
+
## 오디오
|
| 88 |
+
|
| 89 |
+
`audio/manifest.json` 에 (모델, 문장, 범주, 난이도 태그, 허용 표기, 파일) 매핑이
|
| 90 |
+
있다. 모든 음성은 RTX 4090 에서 리더보드 하네스와 동일한 백엔드로 생성했다 —
|
| 91 |
+
Qwen3-TTS 는 `faster-qwen3-tts`(ggml), MMS 는 transformers `VitsModel`.
|
app.py
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""한국어 TTS 사람 청취평가 — 블라인드 받아쓰기 + 0~10점.
|
| 2 |
+
|
| 3 |
+
왜 필요한가
|
| 4 |
+
-----------
|
| 5 |
+
Open Ko-S2S 리더보드의 TTS 발음 지표는 합성음을 ASR 로 되받아쓴 CER 이다.
|
| 6 |
+
4090 실측에서 이 지표가 발음이 아니라 **숫자 표기**를 재고 있다는 것이 드러났다.
|
| 7 |
+
|
| 8 |
+
(기존 20문장 세트, 표기 보정 없음)
|
| 9 |
+
Qwen3-TTS 전체 CER 11.08%
|
| 10 |
+
├ 숫자 표기 문장(n=9) 23.67% ← ASR 이 "삼십 분"을 "30분"으로 씀
|
| 11 |
+
└ 숫자 없는 문장(n=11) 0.50% ← 발음은 사실상 완벽
|
| 12 |
+
|
| 13 |
+
허용 표기(variants)를 도입해 이 편향을 걷어내자 숫자문장 CER 이 23.67% → 3.09%
|
| 14 |
+
로 떨어졌다(의원 접수대 100문장 기준). 남은 것이 진짜 발음 오차다.
|
| 15 |
+
|
| 16 |
+
(의원 100문장, variants 적용)
|
| 17 |
+
Qwen3-TTS 전체 1.50% 숫자 3.09% 숫자없음 1.02%
|
| 18 |
+
MMS-TTS 전체 6.28% 숫자 7.74% 숫자없음 5.84%
|
| 19 |
+
|
| 20 |
+
다만 이 보정이 옳은지, 남은 오차가 정말 발음 문제인지는 자동 지표로 확정할 수
|
| 21 |
+
없다. 사람 귀가 유일한 기준선이라 이 앱으로 human CER 과 주관 점수를 모은다.
|
| 22 |
+
|
| 23 |
+
설계
|
| 24 |
+
----
|
| 25 |
+
- **블라인드**: 어떤 모델의 음성인지, 정답 문장이 무엇인지 제출 전에는 감춘다.
|
| 26 |
+
정답을 먼저 보여주면 받아쓰기가 베끼기가 되어 명료도 측정이 무의미해진다.
|
| 27 |
+
- **표기 통제**: 사람도 "삼십 분"을 듣고 "30분"이라 적는다. 그래서 안내로
|
| 28 |
+
한글 표기를 요청하고, 채점 시에도 숫자를 정규화해 표기 차이를 제거한다.
|
| 29 |
+
- **저장**: HF_TOKEN 시크릿이 있으면 데이터셋 repo 로 누적, 없으면 로컬 파일로
|
| 30 |
+
받아 두고 화면에 경고한다(조용히 유실되지 않게).
|
| 31 |
+
"""
|
| 32 |
+
|
| 33 |
+
from __future__ import annotations
|
| 34 |
+
|
| 35 |
+
import json
|
| 36 |
+
import os
|
| 37 |
+
import random
|
| 38 |
+
import re
|
| 39 |
+
import unicodedata
|
| 40 |
+
import uuid
|
| 41 |
+
from datetime import datetime, timezone
|
| 42 |
+
from pathlib import Path
|
| 43 |
+
|
| 44 |
+
import gradio as gr
|
| 45 |
+
|
| 46 |
+
ROOT = Path(__file__).resolve().parent
|
| 47 |
+
AUDIO_DIR = ROOT / "audio"
|
| 48 |
+
MANIFEST = json.loads((AUDIO_DIR / "manifest.json").read_text(encoding="utf-8"))
|
| 49 |
+
ITEMS = MANIFEST["items"]
|
| 50 |
+
# 도메인: 의원(1차 의료기관) 접수대 100문장 × TTS 2종 = 200 샘플.
|
| 51 |
+
# 음성봇이 가장 먼저 투입되는 구간이라 이 도메인의 발음이 실사용을 좌우한다.
|
| 52 |
+
|
| 53 |
+
DATASET_REPO = os.environ.get("EVAL_DATASET_REPO", "baryonlabs/ko-tts-human-eval-votes")
|
| 54 |
+
HF_TOKEN = os.environ.get("HF_TOKEN")
|
| 55 |
+
LOCAL_LOG = Path(os.environ.get("EVAL_LOCAL_LOG", "/tmp/ko_tts_votes.jsonl"))
|
| 56 |
+
|
| 57 |
+
# ── 채점 ────────────────────────────────────────────────────────────────
|
| 58 |
+
|
| 59 |
+
# 한글 수사 → 숫자. 사람도 ASR 도 "삼십 분"을 "30분"으로 적을 수 있어서, 표기
|
| 60 |
+
# 차이가 명료도 점수를 오염시키지 않도록 양쪽을 같은 형태로 만든 뒤 비교한다.
|
| 61 |
+
_SINO = {"영": 0, "공": 0, "일": 1, "이": 2, "삼": 3, "사": 4, "오": 5,
|
| 62 |
+
"육": 6, "칠": 7, "팔": 8, "구": 9}
|
| 63 |
+
_UNIT = {"십": 10, "백": 100, "천": 1000}
|
| 64 |
+
_NATIVE = {"한": 1, "두": 2, "세": 3, "네": 4, "다섯": 5, "여섯": 6, "일곱": 7,
|
| 65 |
+
"여덟": 8, "아홉": 9, "열": 10, "스물": 20, "서른": 30, "마흔": 40,
|
| 66 |
+
"쉰": 50, "예순": 60, "일흔": 70, "여든": 80, "아흔": 90}
|
| 67 |
+
_PCT = re.compile(r"퍼센트|프로")
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def _sino_to_int(chunk: str):
|
| 71 |
+
"""'삼십사' → 34. 해석 불가하면 None."""
|
| 72 |
+
total, current, seen = 0, 0, False
|
| 73 |
+
for ch in chunk:
|
| 74 |
+
if ch in _SINO:
|
| 75 |
+
current = _SINO[ch]
|
| 76 |
+
seen = True
|
| 77 |
+
elif ch in _UNIT:
|
| 78 |
+
total += (current or 1) * _UNIT[ch]
|
| 79 |
+
current = 0
|
| 80 |
+
seen = True
|
| 81 |
+
else:
|
| 82 |
+
return None
|
| 83 |
+
return total + current if seen else None
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def normalize_numbers(text: str) -> str:
|
| 87 |
+
"""숫자 표기를 통일한다: 아라비아 숫자·퍼센트 기호·한글 수사를 한 형태로."""
|
| 88 |
+
t = _PCT.sub("%", text)
|
| 89 |
+
t = re.sub(r"(\d),(?=\d{3})", r"\1", t) # 4,500 → 4500
|
| 90 |
+
t = t.replace("m", "미터").replace("M", "미터")
|
| 91 |
+
|
| 92 |
+
for word, val in sorted(_NATIVE.items(), key=lambda kv: -len(kv[0])):
|
| 93 |
+
t = t.replace(word, str(val))
|
| 94 |
+
|
| 95 |
+
def repl(m):
|
| 96 |
+
v = _sino_to_int(m.group(0))
|
| 97 |
+
return str(v) if v is not None else m.group(0)
|
| 98 |
+
|
| 99 |
+
t = re.sub(r"[영공일이삼사오육칠팔구십백천]{1,8}", repl, t)
|
| 100 |
+
return t
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def normalize(text: str, fold_numbers: bool = True) -> str:
|
| 104 |
+
t = unicodedata.normalize("NFC", (text or "").strip())
|
| 105 |
+
if fold_numbers:
|
| 106 |
+
t = normalize_numbers(t)
|
| 107 |
+
t = re.sub(r"[^\w가-힣%]", "", t)
|
| 108 |
+
return t
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def _edit_distance(a: str, b: str) -> int:
|
| 112 |
+
if not a:
|
| 113 |
+
return len(b)
|
| 114 |
+
prev = list(range(len(b) + 1))
|
| 115 |
+
for i, ca in enumerate(a, 1):
|
| 116 |
+
cur = [i]
|
| 117 |
+
for j, cb in enumerate(b, 1):
|
| 118 |
+
cur.append(min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (ca != cb)))
|
| 119 |
+
prev = cur
|
| 120 |
+
return prev[-1]
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def char_error_rate(ref: str, hyp: str, fold_numbers: bool = True) -> float:
|
| 124 |
+
r, h = normalize(ref, fold_numbers), normalize(hyp, fold_numbers)
|
| 125 |
+
if not r:
|
| 126 |
+
return 0.0
|
| 127 |
+
return _edit_distance(r, h) / len(r)
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
# ── 저장 ────────────────────────────────────────────────────────────────
|
| 131 |
+
|
| 132 |
+
def _scheduler():
|
| 133 |
+
"""HF_TOKEN 이 있으면 데이터셋 repo 로 누적 저장하는 스케줄러를 만든다."""
|
| 134 |
+
if not HF_TOKEN:
|
| 135 |
+
return None
|
| 136 |
+
try:
|
| 137 |
+
from huggingface_hub import CommitScheduler
|
| 138 |
+
|
| 139 |
+
LOCAL_LOG.parent.mkdir(parents=True, exist_ok=True)
|
| 140 |
+
return CommitScheduler(
|
| 141 |
+
repo_id=DATASET_REPO, repo_type="dataset", folder_path=LOCAL_LOG.parent,
|
| 142 |
+
path_in_repo="data", every=5, token=HF_TOKEN, private=False,
|
| 143 |
+
)
|
| 144 |
+
except Exception as exc: # 저장 실패를 조용히 삼키지 않는다
|
| 145 |
+
print(f"[warn] CommitScheduler 초기화 실패: {exc}")
|
| 146 |
+
return None
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
SCHEDULER = _scheduler()
|
| 150 |
+
STORAGE_NOTE = (
|
| 151 |
+
f"✅ 응답이 `{DATASET_REPO}` 데이터셋에 누적됩니다."
|
| 152 |
+
if SCHEDULER
|
| 153 |
+
else "⚠️ **HF_TOKEN 시크릿이 없어 응답이 이 컨테이너에만 임시 저장됩니다.** "
|
| 154 |
+
"Space 재시작 시 사라집니다. Settings → Secrets 에 write 권한 HF_TOKEN 을 넣어주세요."
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def save_vote(record: dict) -> None:
|
| 159 |
+
LOCAL_LOG.parent.mkdir(parents=True, exist_ok=True)
|
| 160 |
+
line = json.dumps(record, ensure_ascii=False)
|
| 161 |
+
if SCHEDULER is not None:
|
| 162 |
+
with SCHEDULER.lock:
|
| 163 |
+
with LOCAL_LOG.open("a", encoding="utf-8") as f:
|
| 164 |
+
f.write(line + "\n")
|
| 165 |
+
else:
|
| 166 |
+
with LOCAL_LOG.open("a", encoding="utf-8") as f:
|
| 167 |
+
f.write(line + "\n")
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
def load_votes() -> list[dict]:
|
| 171 |
+
if not LOCAL_LOG.exists():
|
| 172 |
+
return []
|
| 173 |
+
out = []
|
| 174 |
+
for line in LOCAL_LOG.read_text(encoding="utf-8").splitlines():
|
| 175 |
+
if line.strip():
|
| 176 |
+
try:
|
| 177 |
+
out.append(json.loads(line))
|
| 178 |
+
except json.JSONDecodeError:
|
| 179 |
+
continue
|
| 180 |
+
return out
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
# ── 출제 ────────────────────────────────────────────────────────────────
|
| 184 |
+
|
| 185 |
+
def pick_item(seen_ids: list[str]) -> dict:
|
| 186 |
+
"""아직 안 들은 것 우선, 그 안에서 응답 수가 적은 것 우선."""
|
| 187 |
+
counts = {}
|
| 188 |
+
for v in load_votes():
|
| 189 |
+
counts[v["item_id"]] = counts.get(v["item_id"], 0) + 1
|
| 190 |
+
pool = [it for it in ITEMS if it["id"] not in seen_ids] or ITEMS
|
| 191 |
+
fewest = min(counts.get(it["id"], 0) for it in pool)
|
| 192 |
+
return random.choice([it for it in pool if counts.get(it["id"], 0) == fewest])
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
def start(seen_ids):
|
| 196 |
+
item = pick_item(seen_ids or [])
|
| 197 |
+
return (
|
| 198 |
+
str(AUDIO_DIR / item["audio"]),
|
| 199 |
+
item,
|
| 200 |
+
gr.update(value="", interactive=True),
|
| 201 |
+
gr.update(value=5, interactive=True),
|
| 202 |
+
gr.update(visible=False, value=""),
|
| 203 |
+
gr.update(visible=True),
|
| 204 |
+
gr.update(visible=False),
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
def _best_cer(item, heard, fold_numbers):
|
| 209 |
+
"""참조에 허용 표기(variants)가 있으면 오류가 가장 적은 것을 택한다.
|
| 210 |
+
|
| 211 |
+
`삼십 분` / `30분` 처럼 발음이 같고 표기만 다른 형태를 하나만 정답으로 쓰면,
|
| 212 |
+
그 표기를 적은 평가자만 유리해진다. KsponSpeech 가 이중전사를 제공하는 것과
|
| 213 |
+
같은 이유다.
|
| 214 |
+
"""
|
| 215 |
+
refs = item.get("variants") or [item["ref"]]
|
| 216 |
+
return min(char_error_rate(r, heard, fold_numbers=fold_numbers) for r in refs)
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
def submit(item, heard, score, seen_ids, rater_id):
|
| 220 |
+
if not item:
|
| 221 |
+
return (gr.update(), gr.update(visible=True, value="먼저 '다음 문장'을 눌러주세요."),
|
| 222 |
+
gr.update(), gr.update(), seen_ids)
|
| 223 |
+
if not (heard or "").strip():
|
| 224 |
+
return (gr.update(), gr.update(visible=True, value="⚠️ 들린 내용을 입력해주세요."),
|
| 225 |
+
gr.update(visible=True), gr.update(visible=False), seen_ids)
|
| 226 |
+
|
| 227 |
+
cer_folded = _best_cer(item, heard, True)
|
| 228 |
+
cer_raw = _best_cer(item, heard, False)
|
| 229 |
+
record = {
|
| 230 |
+
"vote_id": str(uuid.uuid4()),
|
| 231 |
+
"rater_id": rater_id,
|
| 232 |
+
"ts": datetime.now(timezone.utc).isoformat(),
|
| 233 |
+
"item_id": item["id"],
|
| 234 |
+
"model_key": item["model_key"],
|
| 235 |
+
"model_name": item["model_name"],
|
| 236 |
+
"sentence_id": item.get("sentence_id", item.get("id")),
|
| 237 |
+
"category": item.get("category"),
|
| 238 |
+
"has_number": item.get("has_number"),
|
| 239 |
+
"ref": item["ref"],
|
| 240 |
+
"heard": heard.strip(),
|
| 241 |
+
"score": int(score),
|
| 242 |
+
"human_cer": round(cer_folded, 4),
|
| 243 |
+
"human_cer_raw": round(cer_raw, 4),
|
| 244 |
+
}
|
| 245 |
+
save_vote(record)
|
| 246 |
+
|
| 247 |
+
seen = (seen_ids or []) + [item["id"]]
|
| 248 |
+
reveal = (
|
| 249 |
+
f"### 제출 완료\n"
|
| 250 |
+
f"**���답** : {item['ref']}\n\n"
|
| 251 |
+
f"**입력** : {heard.strip()}\n\n"
|
| 252 |
+
f"| 지표 | 값 |\n| --- | --- |\n"
|
| 253 |
+
f"| 사람 CER (숫자 정규화) | **{cer_folded * 100:.1f}%** |\n"
|
| 254 |
+
f"| 사람 CER (원문 그대로) | {cer_raw * 100:.1f}% |\n"
|
| 255 |
+
f"| 내 점수 | {int(score)} / 10 |\n\n"
|
| 256 |
+
f"두 CER 이 다르면 그 차이가 곧 **표기 때문에 생긴 오차**입니다.\n\n"
|
| 257 |
+
f"누적 응답 {len(load_votes())}개 · 계속하려면 **다음 문장**"
|
| 258 |
+
)
|
| 259 |
+
return (gr.update(visible=True, value=reveal), gr.update(visible=False, value=""),
|
| 260 |
+
gr.update(visible=False), gr.update(visible=True), seen)
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
def leaderboard():
|
| 264 |
+
votes = load_votes()
|
| 265 |
+
if not votes:
|
| 266 |
+
return "아직 응답이 없습니다. 몇 개 평가하면 여기에 모델별 집계가 나옵니다."
|
| 267 |
+
agg = {}
|
| 268 |
+
for v in votes:
|
| 269 |
+
a = agg.setdefault(v["model_name"], {"n": 0, "score": 0.0, "cer": 0.0, "cer_raw": 0.0,
|
| 270 |
+
"n_num": 0, "cer_num": 0.0})
|
| 271 |
+
a["n"] += 1
|
| 272 |
+
a["score"] += v["score"]
|
| 273 |
+
a["cer"] += v["human_cer"]
|
| 274 |
+
a["cer_raw"] += v["human_cer_raw"]
|
| 275 |
+
if v.get("has_number"):
|
| 276 |
+
a["n_num"] += 1
|
| 277 |
+
a["cer_num"] += v["human_cer"]
|
| 278 |
+
rows = sorted(agg.items(), key=lambda kv: kv[1]["cer"] / kv[1]["n"])
|
| 279 |
+
out = ["| 모델 | 응답수 | 평균 점수 (0~10) | 사람 CER | 사람 CER(원문) | 숫자문장 CER |",
|
| 280 |
+
"| --- | ---: | ---: | ---: | ---: | ---: |"]
|
| 281 |
+
for name, a in rows:
|
| 282 |
+
n = a["n"]
|
| 283 |
+
num = f"{a['cer_num'] / a['n_num'] * 100:.2f}%" if a["n_num"] else "—"
|
| 284 |
+
out.append(f"| {name} | {n} | {a['score'] / n:.2f} | "
|
| 285 |
+
f"{a['cer'] / n * 100:.2f}% | {a['cer_raw'] / n * 100:.2f}% | {num} |")
|
| 286 |
+
out.append(f"\n총 {len(votes)}개 응답 · 평가자 {len({v['rater_id'] for v in votes})}명")
|
| 287 |
+
out.append("\n※ '사람 CER'은 숫자 표기를 정규화한 값, '원문'은 적힌 그대로입니다. "
|
| 288 |
+
"둘의 차이가 표기 관습이 만드는 오차의 크기입니다.")
|
| 289 |
+
return "\n".join(out)
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
INTRO = f"""# 🎧 한국어 TTS 사람 청취평가
|
| 293 |
+
|
| 294 |
+
합성음을 듣고 **들리는 대로** 받아쓴 뒤 0~10점을 매겨주세요.
|
| 295 |
+
어떤 모델인지, 정답 문장이 무엇인지는 제출 후에 공개됩니다.
|
| 296 |
+
|
| 297 |
+
**받아쓰기 요령**
|
| 298 |
+
- 들린 그대로 적어주세요. 못 알아들은 부분은 비워두거나 `?` 로 두면 됩니다.
|
| 299 |
+
- **숫자는 들린 대로 한글로** 적어주세요. (`30분`이 아니라 `삼십 분`)
|
| 300 |
+
자동 채점이 숫자 표기 때문에 발음 오류를 과대평가하는 문제를 확인하려는 것이 이 평가의 목적입니다.
|
| 301 |
+
|
| 302 |
+
**0~10점 기준** — 0: 전혀 못 알아들음 · 5: 알아듣지만 어색함 · 10: 사람 발화와 구분 안 됨
|
| 303 |
+
|
| 304 |
+
{STORAGE_NOTE}
|
| 305 |
+
"""
|
| 306 |
+
|
| 307 |
+
with gr.Blocks(title="한국어 TTS 사람 청취평가") as demo:
|
| 308 |
+
seen_state = gr.State([])
|
| 309 |
+
item_state = gr.State(None)
|
| 310 |
+
rater_state = gr.State(lambda: str(uuid.uuid4())[:8])
|
| 311 |
+
|
| 312 |
+
gr.Markdown(INTRO)
|
| 313 |
+
|
| 314 |
+
with gr.Tab("평가하기"):
|
| 315 |
+
audio = gr.Audio(label="합성음 (모델명 비공개)", interactive=False, autoplay=False)
|
| 316 |
+
heard_box = gr.Textbox(label="들린 대로 받아쓰기", lines=2,
|
| 317 |
+
placeholder="예) 오늘 회의는 오후 세 시 삼십 분에 시작합니다")
|
| 318 |
+
score_slider = gr.Slider(0, 10, value=5, step=1, label="자연스러움·명료도 (0~10)")
|
| 319 |
+
warn = gr.Markdown(visible=False)
|
| 320 |
+
with gr.Row():
|
| 321 |
+
submit_btn = gr.Button("제출", variant="primary", visible=False)
|
| 322 |
+
next_btn = gr.Button("다음 문장", variant="secondary")
|
| 323 |
+
reveal_box = gr.Markdown(visible=False)
|
| 324 |
+
|
| 325 |
+
with gr.Tab("집계"):
|
| 326 |
+
board = gr.Markdown()
|
| 327 |
+
gr.Button("새로고침").click(leaderboard, outputs=board)
|
| 328 |
+
demo.load(leaderboard, outputs=board)
|
| 329 |
+
|
| 330 |
+
next_btn.click(start, inputs=[seen_state],
|
| 331 |
+
outputs=[audio, item_state, heard_box, score_slider,
|
| 332 |
+
reveal_box, submit_btn, next_btn])
|
| 333 |
+
submit_btn.click(submit,
|
| 334 |
+
inputs=[item_state, heard_box, score_slider, seen_state, rater_state],
|
| 335 |
+
outputs=[reveal_box, warn, submit_btn, next_btn, seen_state])
|
| 336 |
+
|
| 337 |
+
if __name__ == "__main__":
|
| 338 |
+
demo.launch()
|
audio/manifest.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
audio/mms-ko/clinic-000.wav
ADDED
|
Binary file (97.3 kB). View file
|
|
|
audio/mms-ko/clinic-001.wav
ADDED
|
Binary file (96.8 kB). View file
|
|
|
audio/mms-ko/clinic-002.wav
ADDED
|
Binary file (92.7 kB). View file
|
|
|
audio/mms-ko/clinic-003.wav
ADDED
|
Binary file (83 kB). View file
|
|
|
audio/mms-ko/clinic-004.wav
ADDED
|
Binary file (73.8 kB). View file
|
|
|
audio/mms-ko/clinic-005.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6815668616b4c51a978aaf5dd496dbb35061704d159d1d2803f550d38def04b0
|
| 3 |
+
size 115756
|
audio/mms-ko/clinic-006.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c05f00b34667d4e39990ae3d4e855b1f6f6cee111307ff7929d06b04384d3054
|
| 3 |
+
size 156716
|
audio/mms-ko/clinic-007.wav
ADDED
|
Binary file (90.7 kB). View file
|
|
|
audio/mms-ko/clinic-008.wav
ADDED
|
Binary file (94.8 kB). View file
|
|
|
audio/mms-ko/clinic-009.wav
ADDED
|
Binary file (89.1 kB). View file
|
|
|
audio/mms-ko/clinic-010.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8fce024f9bf594ceddea4715d2a7d1b783fbf416fdfd734b6134f0e459ed6976
|
| 3 |
+
size 157740
|
audio/mms-ko/clinic-011.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8c9e4d1c0ef81a5fe661f6a79cc9d501dc65eccf397d43dda2b8f0d71f508007
|
| 3 |
+
size 121388
|
audio/mms-ko/clinic-012.wav
ADDED
|
Binary file (95.8 kB). View file
|
|
|
audio/mms-ko/clinic-013.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d05a0c5a3ae3b47cda05bc3ba2c4f8d12c139c63a3b75fbc4a9040397fb39555
|
| 3 |
+
size 104492
|
audio/mms-ko/clinic-014.wav
ADDED
|
Binary file (74.8 kB). View file
|
|
|
audio/mms-ko/clinic-015.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:05d3322252fa24d9d80f4ddd6b0a7dd95985543525ae81ba8f430b64366759d6
|
| 3 |
+
size 143404
|
audio/mms-ko/clinic-016.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b973d92470c10ba915eb0db71358797463dfb320ef3baa1b2fa0027bc6c19c0d
|
| 3 |
+
size 116780
|
audio/mms-ko/clinic-017.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ca5a1b559eec6ea2873d5f352dc19211fd1e8c9b92ca190a49c879c0ed079524
|
| 3 |
+
size 123948
|
audio/mms-ko/clinic-018.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:13e46ee36fe4301a30f842050b32136865dc1b8a0cbc24e37eef153fce68c3af
|
| 3 |
+
size 110636
|
audio/mms-ko/clinic-019.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:afaaf8b608afd793b7a0005fc50946779dfc20016feef8648367c187a4ddb8eb
|
| 3 |
+
size 104492
|
audio/mms-ko/clinic-020.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bcd1f1586d290dd0a915c1d6f9722fb441482e2d612e02cdfbcfb42cbdea82e5
|
| 3 |
+
size 134700
|
audio/mms-ko/clinic-021.wav
ADDED
|
Binary file (85 kB). View file
|
|
|
audio/mms-ko/clinic-022.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:61ab236547ccb20583a7fbaebfe966cfe76b920c091cc34715902b1083ba223d
|
| 3 |
+
size 101932
|
audio/mms-ko/clinic-023.wav
ADDED
|
Binary file (99.9 kB). View file
|
|
|
audio/mms-ko/clinic-024.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:001de9f7f5ebd6fadec8ad87c94a7644f322cc87dfbd6a31b43b63efbf4ff0be
|
| 3 |
+
size 129068
|
audio/mms-ko/clinic-025.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:54a4232d1e7f452fc0a8605b3e0641d9233b5bdc95809bad545e56af246aa675
|
| 3 |
+
size 105516
|
audio/mms-ko/clinic-026.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:31bdcc0026d4215121ca6746387d56b7ca858fe454c8b6572cb600a13f7e3023
|
| 3 |
+
size 113196
|
audio/mms-ko/clinic-027.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4be15d9601b3f3acb3b4f9b7d4c56d7197fb54ad1d704eb20c0b3c57f334389d
|
| 3 |
+
size 125996
|
audio/mms-ko/clinic-028.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b82160df823ef86d6820e0fd4b5dc8f216a152a9f180e0e743e946bc3e36d4ab
|
| 3 |
+
size 100908
|
audio/mms-ko/clinic-029.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ea746041f6792dbe20fd5b0a0cd97878660b78ef085e86e9d392bf3053057d63
|
| 3 |
+
size 140332
|
audio/mms-ko/clinic-030.wav
ADDED
|
Binary file (98.9 kB). View file
|
|
|
audio/mms-ko/clinic-031.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d52b34c6f7ebf8c3b0fb9363fc4fa15c38d081ffc9e26234a732972f902296f6
|
| 3 |
+
size 136236
|
audio/mms-ko/clinic-032.wav
ADDED
|
Binary file (77.4 kB). View file
|
|
|
audio/mms-ko/clinic-033.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:077ea36cf67ddb7bf21c8ebc8a18fa0e80fe67ec63b56cf08e919a854e7959b7
|
| 3 |
+
size 129068
|
audio/mms-ko/clinic-034.wav
ADDED
|
Binary file (99.9 kB). View file
|
|
|
audio/mms-ko/clinic-035.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a01a5b50550936c06f1cf40c6d024ed161dade6e3995525ed598c0845c9665e4
|
| 3 |
+
size 122412
|
audio/mms-ko/clinic-036.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4bff161dde9706603b6484a63e79495a0892e5b9c9050839715907ec1562b980
|
| 3 |
+
size 152620
|
audio/mms-ko/clinic-037.wav
ADDED
|
Binary file (93.7 kB). View file
|
|
|
audio/mms-ko/clinic-038.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:80d7275f1fa7adc61dc187c2e172bbf6b5133df53720ddd2688c285b9f939ad5
|
| 3 |
+
size 130092
|
audio/mms-ko/clinic-039.wav
ADDED
|
Binary file (99.4 kB). View file
|
|
|
audio/mms-ko/clinic-040.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:02d39f9c18128fdf8450efd5ec5f34aedd3461733ecca8e0294e09a62b1e94b4
|
| 3 |
+
size 102956
|
audio/mms-ko/clinic-041.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d025f26978011902bd3a262122f1d3a8b74e2b1d4820f4a6e26d4563bd409162
|
| 3 |
+
size 117804
|
audio/mms-ko/clinic-042.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0857abe22ca7419205e35b1e4582cdc0efe0e0ab30dfe0553b7bfb7af74ac800
|
| 3 |
+
size 116268
|
audio/mms-ko/clinic-043.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8f6f591afefe37e580621b9450d5ef3cf60846d1e1e10d681e32f4614638d6af
|
| 3 |
+
size 112684
|
audio/mms-ko/clinic-044.wav
ADDED
|
Binary file (74.8 kB). View file
|
|
|
audio/mms-ko/clinic-045.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fdaa71608237f085ec93fd3a5c908e8e92872adec6538a75779e3716da6fd378
|
| 3 |
+
size 122412
|