3. รู้จัก Speech Decoderโดย SpeeChyในบทความที่ 3 นี้ ผมจะเจาะลึกเรื่อง Speech Decoder ซึ่งเป็นหัวใจสำคัญของ Speech recognition ก่อนจะเริ่มขอทบทวน ความจำนิดนึงถึงหน้าที่ของ Speech decoder นะครับ เมื่อรับสัญญาณเสียงเข้ามา Decoder จะทำการสร้าง Word network ที่รวบรวม Word sequence ที่เป็นไปได้ทั้งหมดดังตัวอย่าง ในรูปที่ 1 แต่ละ Path ของ Network จะประกอบด้วย ค่าความน่าจะเป็นของการเกิด Word sequence (คำนวณโดย Language model) และความน่าจะเป็นของการเกิดสัญญาณเสียงนั้น เมื่อกำหนด Word sequence ใดๆ (คำนวณโดย Acoustic model) Decoder จะทำการ Search หา Path ที่ให้ Prob รวมสูงที่สุด Process นี้สามารถเขียนเป็นสมการ ได้ว่า
โดยที่ W คือ Word sequence ที่ต้องการหา, O คือ Observation sequence ของ Input speech, P(O|W) เป็นความน่าจะ เป็นของการเกิด O เมื่อกำหนด W (Acoustic model), และ P(W) เป็นความน่าจะเป็นของการเกิด W (Language model)
Speech decoder หลักๆ ที่ใช้ในการ Recognize มี 3 วิธี
One-pass หมายถึงมีกระบวนการ Search เพียงครั้งเดียว ก็ได้คำตอบเลย ส่วน Multi-pass ก็คือมีการผ่านกระบวนการ Search มากกว่า 1 ครั้งก่อนจะได้คำตอบ ในการ Search ครั้งแรกๆ จะ Output เป็น Hypothesis ที่มี Scope แคบลง เพื่อช่วยในการ Search ในครั้งถัดไป ในบทความที่ 1 และ 2 ผมได้ใช้ Speech decoder ของ HTK ก็คือ Command ที่ชื่อว่า HVite ตัว HVite นี้เป็น Decoder แบบ One-pass Viterbi search เสียดายที่ว่า HVite ณ Version 3 นี้ยัง Support Language model ขนาด 2-gram เท่านั้น (3-gram ยังไม่ได้) ในบทความนี้ผมอยากจะใช้ 3-gram จึงขอนำเสนอ Decoder อีกตัวที่เป็น Open-source ที่สร้างโดย Kyoto University ชื่อว่า JULIUS ซึ่งเป็น Decoder แบบ Two-pass search ใจเย็นๆ นะครับหากว่าเริ่มงงๆ ค่อยๆ ติดตามไปครับ ผมจะค่อยๆ แนะนำการใช้งานจนกระทั่งอธิบายหลักการทำงานของการ Search ทุกวิธี นอกจากนี้ ผมยังอยากจะนำเสนอ Open-source ยอดนิยมอีกตัวซึ่งใช้ในการเตรียม N-gram Language model (แต่เดิมในบทความ ที่ 1 และ 2 ผมเคยใช้ Command ใน HTK ในการสร้าง) ชื่อว่า CMU-SLM โดย Carnegie-Mellon University ในการทดลองของ ผมส่วนใหญ่ ผมจะใช้ HTK สร้าง Acoustic model, CMU-SLM สร้าง Language model, และ JULIUS เป็น Speech decoder นี่แหละครับ คราวนี้มาถึงโจทย์ ในบทความนี้ผมจะขอใช้โจทย์เดิมที่ใช้ในบทความที่ 2 ซึ่งก็คือ Hotel Reservation Task (HRT) และผมจะขอนำ เอา Acoustic model ที่สร้างเสร็จแล้ว (ใครยังไม่เคยสร้างและอยากรู้วิธีสร้าง กลับไปอ่านที่บทความที่ 2 นะครับ) มาใช้เลยในบทความนี้ ส่วน Language model จะสร้างด้วย CMU-SLM โดยเริ่มต้นผมจะมี Text มาให้ มาเริ่มกันเลยนะครับ ขั้นตอนที่ 1
การสร้าง LM ด้วย CMU-SLM toolkitวิธีการใช้งาน CMU-SLM toolkit อย่างละเอียดมีใน Documentation ของตัว Toolkit อยู่แล้ว ในที่นี้ผมจะมาชี้ให้เห็นเฉพาะขั้นตอนหลัก ที่ผมใช้ในการสร้าง N-gram ด้วย CMU-SLM หลักการสร้าง N-gram แสดงไว้ในรูปที่ 2
รูปที่ 3 แสดงหลักการสร้าง N-gram สำหรับ JULIUS ซึ่งผมได้เขียน Script สำเร็จไว้ให้ใน trainlm.sh ดังนี้ครับ
Speech recognition ด้วย JULIUSJULIUS เป็น Speech decoder แบบ Two-pass คือทำการ Search หาผลการรู้จำสองรอบดังแสดงในรูปที่ 4
1st pass จะอาศัย Bigram และ Viterbi search ในการสร้าง Word trellis ซึ่งจะคล้ายกับ Word network ที่รวบรวม Word sequence ที่เป็นไปได้ แต่เพิ่มเติมตัวเลขที่บอกจุดเริ่มต้นและจุดสิ้นสุดของ Candidate word ในทุก Path ทั้งนี้ Path ของ Word sequence ที่มี Prob ตกต่ำกว่าค่า Threshold ที่กำหนด จะไม่อยู่ใน Word trellis เป็นการจำกัดขอบเขต ของการ Search ใน 2nd pass เพื่อลดเวลาในการ Search 2nd pass จะอาศัย Reverse trigram คำนวณ Probability ของ Word sequence ใน Trellis ด้วย Stack search และทำการ Output word sequence ที่ให้ Prob สูงที่สุด ทำนองเดียวกันในขณะที่คำนวณค่า Prob ด้วย Reverse trigram, Path ที่มี Prob รวมต่ำกว่า Threshold ที่กำหนด จะถูกลบออกจากการเป็น Candidate ง่ายมากๆ ครับ การใช้งาน JULIUS มีแค่ใช้คำสั่ง
โดยที่ Option ต่างๆ ของมันจะกำหนดไว้ในไฟล์ jconfig ดังรูปที่ 5
เราสามารถใส่ Parameter ที่ Command line ได้เช่นกัน เช่น
ซึ่งจะ Overwrite parameter ที่ Set ไว้ใน jconfig หากต้องการ Recognize แบบ Batch (Recognize ทีละหลายๆ ไฟล์) ก็สามารถ ทำได้โดย
โดยที่ test.script เก็บ List ของ Input file ที่จะทำการ Recognize และ test.output เป็นผลการ Recognize ผลการ Recognize ของ JULIUS จะมี 2 อันดังแสดงในรูปที่ 6 อันแรก (pass1_best) เป็นผล 1-best หลังจาก 1st pass อันที่สอง (sentence1) เป็นผล 1-best หลังจาก 2nd pass ซึ่งก็คือคำตอบสุดท้ายที่เราต้องการ (หมายเหตุนิดนึงว่า ผลการรู้จำในรูปที่ 6 เป็น สัญลักษณ์ของคำที่ผมกำหนดไว้ใน config/hrt_th2en.table)
Viterbi searchดังที่กล่าวมาแล้ว Word network ของการ Recognize จะมีขนาดใหญ่มากๆ ถึง Infinity จึงเป็นไปไม่ได้ที่จะ Search หมดทุก Path เพื่อหาประโยคที่ให้ Prob สูงที่สุด วิธีหนึ่งที่ใช้คือ Viterbi searchผมขอยกตัวอย่าง Word network ง่ายๆ ดังรูปที่ 7 ณ t ใดๆ Viterbi search จะคำนวณค่า Prob รวมของทุก Path และจะเลือก Search ต่อเฉพาะ Path ที่มี Prob รวมสูงที่สุดดังแสดงในรูปเดียวกันครับ
เพื่อเพิ่มประสิทธิภาพของการ Search มีการพัฒนา Viterbi search 2 แบบ
ข้อดีของ Viterbi search คือ Real time ได้ครับ กล่าวคือ Word network ที่ Viterbi search ไปนั้น สามารถสร้างขึ้นเรื่อยๆ ณ เวลาเดียวกับที่รับสัญญาณเสียงต่อเนื่องเข้ามา การ Search ก็สามารถกระทำได้ไปพร้อมๆ กันทีละ t (ทีละ Observation frame) บาง ครั้งจึงเรียกการ Search แบบนี้ว่า "Frame-synchronous Viterbi beam search" ข้อเสียก็มีครับ มันจะทิ้ง Path ที่ Prob ต่ำเกินกว่าที่กำหนดไป ไม่กลับมาดูอีกเลย ทั้งที่บางครั้ง Path นั้นอาจจะมีค่า Prob รวมสูงขึ้นกว่า Path อื่นได้เมื่อ Search ถึง Word ท้ายๆ
Stack searchในขณะที่ Viterbi search ณ t ใดๆ จะพิจารณา ทุก Path และเลือก Path ที่ Prob สูงในการ Search ต่อ Stack search จะ พิจารณา Path ใด Path หนึ่งทั้ง Path ก่อนจะไปพิจารณา Path อื่นๆณ t ใดๆ Stack search จะเก็บ "Partial path" (ส่วนของ Path ที่ผ่านการ Search แล้ว) ใน Stack เรียงตาม Prob ของ Partial path นั้นๆ และจะดึงเอา Partial path ที่มี Prob สูงที่สุดมาทำการ Expand (เราเรียก Partial path ที่ถูกนำมา Expand ว่า Hypothesis) ในการ Expand จะทำการประกอบ Candidate word ลงไปที่ Path นั้นพร้อมทั้ง Estimate ค่า Prob ของใหม่ด้วยสมการ
โดยที่ ah(t) เป็น Prob ของ Partial path ที่ทำการ Expand, bh(t) เป็น Estimation prob ของ Partial path นี้ที่ประกอบ Candidate word ลงไปแล้ว, fh(t) เป็น Prob ใหม่ที่ได้และ Hypothesis ที่ Expand แล้ว จะถูกนำมาเรียงลำดับใหม่ตาม Prob ใหม่เก็บใน Stack เช่นเดิม Process นี้จะ Iterate จนกระทั่งจบประโยค (สิ้นสุดสัญญาณเสียง) ดูไปเหมือนจะคล้ายๆ กับ Viterbi search นะครับ แต่ข้อแตกต่างมันอยู่ที่การคำนวณค่า Prob ครับ ah(t) ก็ไม่ยากครับ มันคือ Prob รวม (Acoustic & Language modeling) ของ Partial path นั้นๆ แต่ bh(t) นี่สิที่ยาก เพราะว่าในทางทฤษฎี bh(t) ควรจะเป็น Prob ของ Path ส่วนที่เหลือจาก Partial path ไปจนถึงจบประโยค แต่เราไม่มีทางรู้ว่าประโยคจบลงที่ไหน ประโยคยาวกี่คำตั้งแต่ต้นครับ ดังนั้นในทางปฏิบัติ จึงมีวิธีการต่างๆ นานา ในการ Estimate bh(t) เมื่อประกอบ Candidate word ใดๆ ลงไปที่ Partial path อาทิเช่น วิธี "Acoustic model lookahead" ผมไม่ขออธิบายรายละเอียดของวิธีต่างๆ ในการ Estimate bh(t) ณ ที่นี้ครับ เพียงต้องการให้เห็น Idea ของการใช้ Stack search และสามารถไปอ่านเพิ่มเติมหากต้องการศึกษาลึกซึ้งต่อไป ข้อดีของ Stack search คือ Path ที่ดีที่สุด จะไม่หลุดไปจากการ Search ตลอดการ Search (ไม่เหมือนกับ Viterbi ที่จะทิ้ง Path ที่ Prob ต่ำไปในขณะที่ Search) แต่ข้อเสียของ Stack search ก็คือ วิธีการ Estimate bh(t) จะต้องดีพอนั่นเองครับ
Copyright (c) 2004 Chai Wutiwiwatchai, Thai Speech Group |