كلمة الاستيقاظ / رصد الكلمات المفتاحية
تشغّل وحدة SpeechWakeWord راصد كلمات مفتاحية على الجهاز: تسجّل قائمة من العبارات، تُمرّر أجزاء الصوت، فتتلقّى الاكتشافات. مبني على مُحوِّل Zipformer للبثّ من icefall (3.49M معامل، Apache-2.0)، مُصرَّف إلى CoreML مع palettization بنمط INT8.
نقطة التحقّق المُرفقة هي التهيئة الدقيقة لـ KWS على gigaspeech. تستلزم الكلمات المفتاحية بغير الإنجليزية تهيئة دقيقة منفصلة في icefall وإعادة تصدير.
البنية المعمارية
| المرحلة | التفاصيل |
|---|---|
| fbank | متوافق مع kaldi (25 ms / 10 ms، نافذة Povey، 80 mel bins، high_freq=-400، دون CMVN) |
| Encoder | Zipformer2 سببي بستّ مراحل (128-dim)، 45 إطار mel دخل → 8 إطارات خرج (40 ms / إطار) — 3.3 MB INT8 |
| Decoder | مُحوِّل دون حالة، مفردات BPE-500، حجم سياق 2 — 525 KB FP16 |
| Joiner | إسقاط خطّي + tanh — 160 KB INT8 |
| فكّ الترميز | Beam search المُعدَّل (beam=4) على ContextGraph بأسلوب Aho-Corasick من الكلمات المفتاحية للمستخدم |
الحجم المُصرَّف على القرص: ~4 MB إجمالًا (encoder.mlmodelc + decoder.mlmodelc + joiner.mlmodelc). الذاكرة وقت التشغيل: ~6 MB بما في ذلك مخابئ حالة الـ encoder.
الأداء
| المقياس | القيمة | ملاحظات |
|---|---|---|
| RTF (CPU + Neural Engine) | 0.04 | 26× الزمن الحقيقي على شرائح M |
| الاستدعاء (12 كلمة مفتاحية) | 88% | LibriSpeech test-clean، 158 منطوقًا إيجابيًا |
| الإيجابيات الكاذبة / منطوق | 0.27 | 60 منطوقًا سلبيًا |
| CoreML INT8 مقابل PyTorch FP32 | 99% | توافق في الإصدار |
القيم الافتراضية المضبوطة: acThreshold=0.15، contextScore=0.5، numTrailingBlanks=1. مدعوم تجاوز هذه القيم لكلّ كلمة مفتاحية على حدة.
استخدام CLI
صيغة العبارة الصرفة (BPE الجشع — تعمل جيدًا للكلمات الشائعة):
speech wake recording.wav --keywords "hey soniqo"
speech wake recording.wav --keywords "hey soniqo:0.15:0.5" "cancel"
الصيغة المُرمَّزة مسبقًا (أسلوب sherpa-onnx — مُوصى به حين تعرف التفكيك الدقيق الذي درّب عليه النموذج):
# Format: "phrase|piece1 piece2 ...:threshold:boost"
speech wake recording.wav \
--keywords "LIGHT UP|▁ L IGHT ▁UP:0.25:2.0"
# Multiple keywords + JSON output
speech wake recording.wav \
--keywords "LIGHT UP|▁ L IGHT ▁UP:0.25:2.0" \
"LOVELY CHILD|▁LOVE LY ▁CHI L D:0.25:2.0" \
--json
أو ملفّ كلمات مفتاحية، إدخال واحد في كلّ سطر (# للتعليقات):
speech wake recording.wav --keywords-file keywords.txt
واجهة Swift
import SpeechWakeWord
// Load the model with your keyword list.
let detector = try await WakeWordDetector.fromPretrained(
keywords: [
KeywordSpec(phrase: "hey soniqo", acThreshold: 0.15, boost: 0.5),
KeywordSpec(phrase: "cancel")
]
)
// Streaming: push chunks, consume detections as they fire.
let session = try detector.createSession()
for chunk in micAudioChunks { // Float32 @ 16 kHz
for detection in try session.pushAudio(chunk) {
print("[\(detection.time(frameShiftSeconds: 0.04))s] \(detection.phrase)")
}
}
// Batch: single shot over a full buffer.
let detections = try detector.detect(audio: samples, sampleRate: 16000)
KeywordSpec
| الحقل | المعنى |
|---|---|
phrase | عبارة العرض، مثل "hey soniqo". تُستخدم أيضًا مصدرًا لترميز BPE الجشع عندما يكون tokens هو nil. |
acThreshold | الاحتمال الصوتي الوسطي المطلوب على المقطع المتطابق. 0 ← استخدام القيمة الافتراضية المضبوطة (0.15). |
boost | تعزيز السياق لكلّ token. القيم الموجبة تسهّل إطلاق العبارة. 0 ← القيمة الافتراضية المضبوطة (0.5). |
tokens | قائمة قِطَع BPE صريحة اختيارية. حين لا تكون nil، يبحث الراصد عن كلّ قطعة في tokens.txt للنموذج ويتجاوز مُرمِّز BPE الجشع. |
tokens المُرمَّزة مسبقًامفردات KWS الخاصّة بـ icefall هي BPE بأحرف كبيرة. قد يختار الترميز الجشع لعبارة ما تفكيك BPE يختلف عمّا دُرِّب النموذج على إصداره — تُرمَّز "LIGHT UP" جشعًا إلى ▁LI GHT ▁UP، بينما تفكيك التدريب هو ▁ L IGHT ▁UP. حين يفوّت الرصد على كلام مُولَّد بـ TTS أو على قراءة نقيّة تطابقات بديهية، جرّب الصيغة المُرمَّزة مسبقًا بأسلوب sherpa-onnx.
تنزيلات النموذج
| النموذج | المعاملات | الحجم | HuggingFace |
|---|---|---|---|
| KWS-Zipformer-3M | 3.49M | ~4 MB | aufklarer/KWS-Zipformer-3M-CoreML-INT8 |
التكامل مع خطّ الأنابيب
تُتيح الوحدة بروتوكول WakeWordProvider الذي يحاكي StreamingVADProvider، بحيث يمكن لخطّ أنابيب صوتيّ أن يضبط التفعيل عبر VAD أو كلمة الاستيقاظ أو كليهما. يغلّف WakeWordStreamingAdapter راصدًا مُحمَّلًا + جلسة واحدة في كائن provider قابل لإعادة الاستخدام.
let adapter = try WakeWordStreamingAdapter(detector: detector)
// pipeline.configure(wakeWord: adapter)
الكود المصدري
- Sources/SpeechWakeWord — وحدة Swift
- docs/models/kws-zipformer.md — ملاحظات معماريّة
- docs/inference/wake-word.md — خطّ أنابيب الاستدلال
- المصدر الأصلي: وصفة KWS في k2-fsa/icefall / pkufool/keyword-spotting-models