Skip to content

Whisper ट्रांसक्रिप्ट को एनोटेट कैसे करें

Whisper या WhisperX के आउटपुट को चालू एनोटेशन प्रोजेक्ट में कैसे बदलें: कौन सी आउटपुट फ़ाइल रखें, diarization कब ज़रूरी है, स्पीकर कैसे सौंपे जाएँ, और टाइमिंग बरकरार रखते हुए एक्सपोर्ट कैसे करें।

Whisper आपको समय के साथ बँटे सेगमेंट देता है, और एनोटेशन टूल को उन्हें जैसा है वैसा ही पढ़ लेना चाहिए। .txt के बजाय .json आउटपुट रखें, स्पीकर लेबल चाहिए तो WhisperX चलाएँ, फिर Potato को उस फ़ोल्डर की ओर इशारा कर दें। कोई कन्वर्ज़न स्क्रिप्ट नहीं, कोई फिर से फ़ॉर्मैट करने का चरण नहीं। हर फ़ॉर्मैट का ब्योरा ट्रांसक्रिप्ट फ़ॉर्मैट में है।

Whisper OpenAI का ओपन-सोर्स speech recognition मॉडल है, और जिसके पास भी निपटाने के लिए ऑडियो का ढेर हो, उसके लिए यह पहला कदम बन चुका है। यह जो बनाता है वह समय के साथ बँटे सेगमेंट का एक समूह होता है, जो एनोटेशन प्रोजेक्ट की लगभग सारी ज़रूरत पूरी कर देता है। कमी वहाँ रह जाती है जो “मेरे पास ट्रांसक्रिप्ट हैं” और “एनोटेटर उन्हें लेबल कर रहे हैं” के बीच पड़ता है।

ध्यान दें: Potato ट्रांसक्राइब नहीं करता। ASR उससे पहले चलता है और Potato उसका आउटपुट लेता है। यह मार्गदर्शिका बताती है कि उस पिछले चरण के कौन से चुनाव मायने रखते हैं, पर ट्रांसक्रिप्शन Potato के कुछ भी देखने से पहले ही हो चुका होता है।

Whisper की कौन सी आउटपुट फ़ाइल रखनी चाहिए?

Whisper कई फ़ाइलें लिखता है और यह चुनाव जितना दिखता है उससे ज़्यादा मायने रखता है:

bash
whisper interview_01.mp3 --model medium --output_format json --word_timestamps True
फ़ाइलइसमें क्या हैरखें?
.jsonशुरू और अंत के समय वाले सेगमेंट, चाहें तो हर शब्द की टाइमिंग भीहाँ, यही
.srt / .vttटाइमिंग वाले सेगमेंट, मेटाडेटा नहींहाँ, ठीक चलता है
.tsvमिलीसेकंड में शुरू और अंत, साथ में टेक्स्टहाँ
.txtसिर्फ़ टेक्स्ट, कोई टाइमिंग नहींनहीं, ऑडियो से सिंक करने को कुछ बचता ही नहीं

अगर आपने सिर्फ़ .txt रखी है तो मिलान जा चुका है और मॉडल दोबारा चलाए बिना वह वापस नहीं आएगा। Potato में ट्रांसक्रिप्ट के एक अविभाजित टेक्स्ट ब्लॉक की तरह पहुँचने की सबसे आम वजह यही है।

क्या Whisper स्पीकर के लेबल देता है?

नहीं, और इससे लोग बार-बार चौंकते हैं। सादा Whisper ट्रांसक्राइब करता है, यह नहीं बताता कि बोल कौन रहा है। हर टर्न बिना किसी के नाम के आता है, जो कंटेंट एनोटेशन के लिए ठीक है और स्पीकर से जुड़े किसी भी काम के लिए तकलीफ़देह।

Speaker diarization एक अलग चरण है। इसे सँभालने के तीन तरीके:

WhisperX चलाएँ, जो Whisper के साथ pyannote diarization जोड़ देता है:

bash
whisperx interview_01.mp3 --model medium --diarize --output_format json

इसके आउटपुट में हर सेगमेंट पर एक speaker फ़ील्ड होती है (SPEAKER_00, SPEAKER_01, वग़ैरह) और Potato उसे सीधे पढ़ लेता है।

diarization चालू करके कोई क्लाउड API इस्तेमाल करें। diarize=true के साथ Deepgram, speaker_labels के साथ AssemblyAI, AWS Transcribe, और Rev.ai, चारों स्पीकर लेबल देते हैं और Potato चारों को सीधे पढ़ता है।

हैच किए हुए स्लेटी बैकग्राउंड वाले तीन ट्रांसक्रिप्ट टर्न, हर एक पर Unassigned का लेबल और एक ड्रॉपडाउन तीर।बिना diarization वाले टर्न Unassigned के रूप में दिखते हैं, साथ में स्पीकर चुनने का विकल्प

एनोटेटरों से करवाएँ। जब टर्न बिना diarization के आते हैं, तो हर बबल Unassigned के रूप में एक पिकर के साथ दिखता है। गड़बड़ ऑडियो, एक-दूसरे पर चढ़ती आवाज़ों, या पीछे शोर वाले कमरे में सुनकर बताने वाला इंसान अक्सर अपने आप होने वाले diarization से ज़्यादा सही निकलता है। छोटे संग्रह के लिए यह मजबूरी नहीं, एक वाजिब सौदा है।

डेटा फ़ाइल कैसे बनाएँ?

कन्वर्टर को अपने Whisper आउटपुट फ़ोल्डर पर लगाएँ:

bash
potato transcripts ./whisper_out --media-dir ./audio -o data/interviews.json

ट्रांसक्रिप्ट अपने मीडिया से basename के आधार पर जुड़ते हैं, इसलिए interview_01.json को interview_01.mp3 मिल जाती है। आइटम id फ़ाइल के नाम से बनते हैं, और Whisper का दोहरा एक्सटेंशन interview_01.mp3.json ऐसे सँभाला जाता है कि id interview_01.mp3 न बन जाए।

इस पर टिकने से पहले देख लें कि उसने क्या समझा:

bash
potato transcripts ./whisper_out --dry-run
text
Scanned 3 file(s):
  interview_01.json      Whisper JSON      42 turns    891.4s  undiarized
  interview_02.json      WhisperX JSON     51 turns   1120.8s  2 speaker(s): SPEAKER_00, SPEAKER_01
  interview_03.json      Whisper JSON      38 turns    754.2s  undiarized

3 item(s), 131 turn(s).

जो फ़ाइल plain text या शून्य टर्न बताए, वही आपकी गड़बड़ फ़ाइल है। लगभग हमेशा यह कोई .txt होती है जो .json आउटपुट के बीच आ मिली है।

क्या कन्वर्ज़न वाला चरण छोड़ा जा सकता है?

हाँ। डेटा फ़ाइल सीधे डिस्क पर पड़ी ट्रांसक्रिप्ट फ़ाइलों की ओर इशारा कर सकती है:

json
{"id": "int_001", "conversation": {"audio": "media/int_001.mp3",
                                   "transcript": "media/int_001.srt"}}

Potato फ़ाइल पढ़ता है, उसकी सामग्री से फ़ॉर्मैट पहचानता है, और इंस्टेंस दिखाते समय उसे सामान्य रूप में ढाल देता है। आपके ट्रांसक्रिप्ट किसी डेटा ब्लॉब में पक जाने के बजाय ऐसी फ़ाइलें बने रहते हैं जिनका diff लिया जा सके और जिन्हें दोबारा एक्सपोर्ट किया जा सके।

कॉन्फ़िग कैसा दिखता है?

बनी हुई डेटा फ़ाइल की शक्ल ऐसी होती है:

json
{
  "id": "interview_01",
  "conversation": {
    "audio": "audio/interview_01.mp3",
    "turns": [
      {"turn_id": "t0", "speaker": "SPEAKER_00", "start": 0.0, "end": 6.5,
       "text": "Welcome back."}
    ]
  }
}

और उससे मेल खाता कॉन्फ़िग, जिसे potato transcripts --emit-config आपके लिए छाप देगा:

yaml
annotation_task_name: "Interview Annotation"
task_dir: .
data_files:
  - data/interviews.json
 
item_properties:
  id_key: id
  text_key: conversation
 
instance_display:
  fields:
    - key: conversation
      type: audio_dialogue
      label: "Transcript"
      span_target: true
      display_options:
        show_timestamps: true
        allow_speaker_assignment: auto
 
annotation_schemes:
  - annotation_type: span
    name: topics
    description: "Highlight topic mentions"
    labels:
      - name: policy
      - name: personal

ट्रांसक्रिप्ट के टर्न रंगीन स्पीकर बबल के रूप में, हर टर्न पर प्ले बटन और टाइमस्टैंप, और हर टर्न के नीचे एक इनलाइन लेबल प्रश्न।audio_dialogue डिस्प्ले, जिसमें हर टर्न से एक प्रश्न जुड़ा है

audio_dialogue डिस्प्ले टर्न को ऑडियो से सिंक स्पीकर बबल के रूप में दिखाता है, और हर टर्न पर एक प्ले बटन होता है जो सिर्फ़ वही टर्न बजाता है। span_target: true से span टर्न की सीमाओं के आर-पार जा सकते हैं, और स्पीकर बदलने पर भी offset स्थिर रहते हैं।

अगर आप चाहते हैं कि एनोटेटर कंटेंट लेबल करने के बजाय ट्रांसक्रिप्शन की जाँच करें, तो speech_transcript आपको एरर टैग और सुधार के बॉक्स वाले सेगमेंट कार्ड देता है। एक-दूसरे पर चढ़ती बात और टोकाटाकी वाले काम के लिए voice_interaction दो-ट्रैक टाइमलाइन देता है। दोनों वही फ़ाइलें पढ़ते हैं।

एनोटेटर स्पीकर कैसे सौंपते हैं?

एक रोस्टर तय करें ताकि स्पीकर को स्थिर नाम, रंग और तरफ़ मिले:

yaml
display_options:
  allow_speaker_assignment: auto
  speakers:
    - id: interviewer
      name: "Interviewer"
      color: "#7c3aed"
      side: left
    - id: participant
      name: "Participant"
      color: "#059669"
      side: right

auto पिकर तभी चालू करता है जब लेबल करने के लिए बिना diarization वाले टर्न मौजूद हों। इसे true कर दें तो स्पीकर तब भी बदले जा सकते हैं जब स्रोत ने लेबल दे रखे हों, यानी जब आप खाली जगह भरने के बजाय diarization की ग़लतियाँ सुधार रहे हों।

सौंपे गए स्पीकर एनोटेशन के साथ, एक स्थिर turn id से जुड़कर सहेजे जाते हैं, इसलिए पेज दोबारा लोड होने पर भी बने रहते हैं।

एनोटेशन वापस कैसे निकालें?

JSON, JSONL और CSV एक्सपोर्ट हमेशा की तरह चलते हैं। जब समय का मिलान बचाए रखना हो, तो tiered एनोटेशन को ELAN EAF या Praat TextGrid में एक्सपोर्ट करें:

bash
python -m potato.export --config config.yaml --format eaf --output ./out/
python -m potato.export --config config.yaml --format textgrid --output ./out/

दोनों में आना-जाना दोनों तरफ़ चलता है। Potato EAF और TextGrid को इनपुट के रूप में भी पढ़ता है, इसलिए आप Potato में एनोटेट कर सकते हैं, ELAN या Praat में उसे निखार सकते हैं, और नतीजा वापस पढ़ा सकते हैं।

आगे पढ़ें