#
Normalization และ pre-tokenization

ก่อนที่เราจะเจาะลึกเกี่ยวกับอัลกอริทึม 3 แบบ ของ subword tokenization ที่ใช้กับโมเดล Transformer (Byte-Pair Encoding [BPE], WordPiece, และ Unigram) อันดับแรก เราจะมาเรียนเกี่ยวกับขั้นตอน preprocessing ที่ tokenizer ใช้เพื่อจัดแต่งข้อความก่อนการ tokenize หลักกันก่อน

บทนี้จะเป็นภาพรวมระดับสูงของขั้นตอนต่างๆในไปป์ไลน์ tokenization:

ก่อนแยกข้อความออกเป็น subtokens ตัว tokenizer จะดำเนินการสองขั้นตอน คือ normalization และ pre-tokenization

##
Normalization

ขั้นตอน normalization เกี่ยวข้องกับทำความสะอาดข้อมูลทั่วไป เช่น การลบช่องว่างที่ไม่จำเป็นแปลงข้อความเป็นตัวพิมพ์เล็ก และ/หรือ การลบเครื่องหมายเน้นเสียงออก (accents) หากคุณคุ้นเคยกับ Unicode normalization (เช่น NFC หรือ NFKC) นี่ก็เป็นสิ่งที่ tokenizer อาจใช้เช่นกัน

🤗 Transformers `tokenizer` มี attribute ที่เรียกว่า `backend_tokenizer` ที่เราสามารถเรียกใช้ได้ เพื่อเข้าถึง tokenizer พื้นฐานของ 🤗 Tokenizers library:

    from transformers import AutoTokenizer

    tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
    print(type(tokenizer.backend_tokenizer))

    <class 'tokenizers.Tokenizer'>

attribute ชื่อ `normalizer` ของ `tokenizer` object มี method ชื่อ `normalize_str()` ที่เราสามารถใช้เพื่อดูผลลัพธ์ของการ normalization ได้:

    print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))

    'hello how are u?'

ในตัวอย่างนี้ เนื่องจากเราเลือกใช้ checkpoint `bert-base-uncased` การ normalization จึงแปลงข้อความเป็นตัวพิมพ์เล็กและลบเครื่องหมายเน้นเสียงออก

✏️ ลองดูสิ! โหลด tokenizer จาก checkpoint `bert-base-cased` และใช้มันกับ input เดียวกันกับข้างบนนี้ แล้วดูว่าผลลัพธ์ต่างกันอย่างไร ระหว่าง tokenizer เวอร์ชัน cased และ uncased

##
Pre-tokenization

ในหัวข้อถัดไปคุณจะได้เรียนรู้ว่า เราไม่สามารถเทรน tokenizer จาก raw text โดยตรงได้ ก่อนอื่นเราจะต้องแยกข้อความเป็น entity เล็กๆ เช่นแยกออกเป็น คำ ขั้นตอนพวกนี้คือการ pre-tokenization ดังที่คุณเห็นในบทที่ 2 tokenizer แบบ word-based จะแบ่งข้อความเป็นคำ โดยการแบ่งตรงช่องว่าง และ เครื่องหมายวรรคตอน คำที่ได้จะถูกนำมาใช้เป็นขอบเขตของ subtokens ที่ tokenizer เอาไว้ใช้ในการเทรน

สำหรับ fast tokenizer ถ้าหากเราอยากจะดูว่ามันทำอะไรบ้างในขั้นตอน pre-tokenization เราจะใช้ method ชื่อ `pre_tokenize_str()` ของ attribute ชื่อ `pre_tokenizer` จาก `tokenizer` object:

    tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are  you?")

    [('Hello', (0, 5)), (',', (5, 6)), ('how', (7, 10)), ('are', (11, 14)), ('you', (16, 19)), ('?', (19, 20))]

คุณจะเห็นว่าตัว tokenizer มีการเก็บข้อมูลเกี่ยวกับ offsets ด้วย ซึ่งทำให้มันสามารถสร้าง offsets mapping ให้เราได้อย่างที่เห็นในบทที่แล้ว สำหรับข้อความ input ในตัวอย่างนี้ ช่องว่างสองช่อง(หลังคำว่า are) ถูกแทนที่ด้วยหนึ่งช่องว่างเท่านั้น แต่เราจะเห็นว่าค่า offsets ยังนับช่องว่างพวกนี้อยู่ สังเกตค่า offsets ตรง `are` และ `you`

เนื่องจากเราใช้ BERT tokenizer ขั้นตอน pre-tokenization คือการตัดข้อความตรงช่องว่างและเครื่องหมายวรรคตอนเท่านั้น ส่วน tokenizer อื่นๆ อาจจะมีการหลักการตัดคำแบบอื่นได้ ตัวอย่างเช่น ถ้าเราใช้ tokenizer ของ GPT-2:

    tokenizer = AutoTokenizer.from_pretrained("gpt2")
    tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are  you?")

มันจะแบ่งข้อความตรงช่องว่างและเครื่องหมายวรรคตอนเช่นเดียวกัน แต่มันจะยังเก็บข้อมูลเกี่ยวกับช่องว่างไว้และใช้เครื่องหมาย `Ġ` เพื่อแทนช่องว่างพวกนี้ การทำแบบนี้ทำให้เราสามารถกู้คืนช่องว่างพวกนี้ได้ตอนที่เรา decode token เหล่านี้

    [('Hello', (0, 5)), (',', (5, 6)), ('Ġhow', (6, 10)), ('Ġare', (10, 14)), ('Ġ', (14, 15)), ('Ġyou', (15, 19)),
     ('?', (19, 20))]

สังเกตว่า ช่องว่างสองช่องจะไม่ถูกรวมเป็นหนึ่งช่องแบบใน BERT tokenizer

ในตัวอย่างสุดท้ายนี้ เราจะมาดู T5 tokenizer กัน ซึ่งใช้อัลกอริทึมที่ชื่อ SentencePiece :

    tokenizer = AutoTokenizer.from_pretrained("t5-small")
    tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are  you?")

    [('▁Hello,', (0, 6)), ('▁how', (7, 10)), ('▁are', (11, 14)), ('▁you?', (16, 20))]

คล้ายกับใน GPT-2 tokenizer T5 tokenizer จะเก็บข้อมูลเกี่ยวกับช่องว่าง และแทนที่พวกมันด้วยเครื่องหมายพิเศษ (`_`) แต่มันจะแบ่งตรงช่องว่างเท่านั้น และจะไม่แบ่งตรงเครื่องหมายวรรคตอน สังเกตว่า มันจะเพิ่มช่องว่างตรงต้นประโยคด้วย (ก่อนคำว่า `Hello`) และมันจะไม่นับช่องว่างสองช่องที่อยู่ระหว่าง `are` และ `you`

คุณได้เห็นแล้วว่า tokenizers ต่างๆ ประมวลผลข้อความอย่างไร ตอนนี้เราจะมาดูอัลกอริทึมต่างๆกัน เริ่มที่ SentencePiece ซึ่งเป็นอัลกอริทึมที่ถูกนำมาใช้อย่างกว้างขวาง จากนั้นในอีกสามหัวข้อต่อไป เราจะมาดูเกี่ยวกับอัลกอริทึม 3 แบบ ของ subword tokenization

##
SentencePiece

SentencePiece คืออัลกอริทึมสำหรับการ preprocessing ข้อความ เพื่อนำข้อความพวกนี้ไปใช้ในโมเดลต่างๆที่คุณจะได้เรียนในอีกสามบทถัดจากนี้ จะมันมองข้อความเป็นอักขระ Unicode และแทนที่ช่องว่างด้วยสัญลักษณ์พิเศษ `▁` ถ้าใช้งานร่วมกับ Unigram algorithm (ดูบทที่ 7) มันจะไม่จำเป็นต้องทำขั้นตอน pre-tokenization เลยด้วย ซึ่งมีประโยชน์สำหรับภาษาที่ไม่ได้ใช้ช่องว่างในการแบ่งคำเช่น ภาษาจีนหรือญี่ปุ่น

ความสามารถหลักอีกอย่างของ SentencePiece คือ reversible tokenization (การตัดคำที่แปลงกลับได้): เนื่องจากมันไม่ได้ treat พวกช่องว่างแบบพิเศษ เวลา decode ประโยคที่ตัดแล้วกลับคืน เราสามารถเชื่อม (concatenate)แต่ละ token ได้เลยและ และแทนที่ `_` ด้วยช่องว่าง ผลลัพธ์ก็คือ ข้อความที่ ถูก normalized

อย่างที่คุณได้เห็นก่อนหน้านี้ BERT tokenizer จะลบช่องว่างที่ต่อกันออก ทำให้ตอนรวม token กลับ เราจะไม่ได้ข้อความแบบเดิม

## ภาพรวมของแต่ละอัลกอริทึม

ในบทถัดไป เราจะมาเรียนรู้อย่างละเอียด เกี่ยวกับอัลกอริทึมสามแบบ สำหรับ subword tokenization ได้แก่ BPE (ใช้กับ GPT-2 และ โมเดลอื่นๆ), WordPiece (ใช้กับ BERT), และ Unigram (ใช้กับ T5 และโมเดลอื่นๆ) ก่อนที่จะไปเริ่มกัน เรามาดูภาพรวมของแต่ละอัลกอริทึมกันก่อน คุณสามารถกลับมาดูตารางนี้ใหม่ได้หลังจากที่อ่านบทถัดไปแล้ว เพื่อจะได้เข้าใจมากขึ้น

โมเดล | BPE | WordPiece | Unigram
--- | --- | --- | ---
การเทรน | เริ่มจาก vocabulary ขนาดเล็ก และเรียนกฎในการรวม token เข้าด้วยกัน | เริ่มจาก vocabulary ขนาดเล็ก และเรียนกฎในการรวม token เข้าด้วยกัน | เริ่มจาก vocabulary ขนาดใหญ่ เรียนกฎเพื่อลบ token ออกจาก vocabulary
ขั้นตอนการเทรน | รวม token ถ้ามันเป็นคู่ที่พบบ่อย | รวม token ถ้ามันเป็นคู่ที่มี score ที่ดีที่สุด โดย score คำนวณจากความถี่ของคู่ token นั้น และให้คะแนนสูงถ้าแต่ละ token มีความถี่ต่ำ | ลบ token ออกจาก vocabulary เพื่อทำให้ค่า loss ลดลง โดยที่ค่า loss คำนวณจาก training corpus
สิ่งที่เรียน | กฎในการรวม token (merge rules) และ vocabulary | เรียนแค่ vocabulary | เรียน vocabulary และ score ของแต่ละ token
Encoding | แยกคำออกเป็นตัวอักษร และทำการรวมโดยใช้กฎที่เรียนระหว่างการเทรน | หาคำย่อยที่ยาวที่สุดที่อยู่ใน vocabulary เริ่มจากต้นคำและทำต่อไปเรื่อยๆจนหมดคำ | หาการแบ่งคำที่เหมาะสมที่สุดโดยใช้ score ที่เรียนระหว่างการเทรน

ในบทต่อไปเรามาเรียนเกี่ยวกับ BPE อย่างละเอียดกัน!