Buckets:
| # การสร้าง tokenizer ทีละขั้นตอน | |
| <CourseFloatingBanner chapter={6} | |
| classNames="absolute z-10 right-0 top-0" | |
| notebooks={[ | |
| {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/th/chapter6/section8.ipynb"}, | |
| {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/th/chapter6/section8.ipynb"}, | |
| ]} /> | |
| จากบทก่อนๆ คุณจะเห็นว่า การตัดคำ ประกอบไปด้วยหลายขั้นตอน : | |
| - Normalization (หรือ การปรับข้อความให้เป็นมาตรฐาน หมายถึงการทำความสะอาดข้อความ เช่น ลบช่องว่างหรือเครื่องหมายเน้นเสียง รวมถึงการทำ Unicode normalization และอื่นๆ) | |
| - Pre-tokenization (ขั้นตอนก่อนตัดคำ หมายถึง การแยกข้อความออกเป็นคำๆ) | |
| - ส่ง input เข้าไปในโมเดล (แยกคำที่ได้จากขั้นตอน pre-tokenization ออกเป็นคำย่อยหลายๆคำ) | |
| - Post-processing (ขั้นตอนปรับแต่งผลลัพธ์ เช่น การใส่ token พิเศษของ tokenizer เข้าไปในผลลัพธ์, การสร้าง attention mask และ token type IDs) | |
| เพื่อเป็นการเตือนความจำ มาดูกระบวนการโดยรวมอีกครั้ง : | |
| <div class="flex justify-center"> | |
| <img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/tokenization_pipeline.svg" alt="The tokenization pipeline."> | |
| <img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/tokenization_pipeline-dark.svg" alt="The tokenization pipeline."> | |
| </div> | |
| 🤗 Tokenizers library เป็นเครื่องมือสำหรับช่วยดำเนินการขั้นตอนพวกนี้ โดยคุณสามารถผสมผสานเครื่องมือพวกนี้ตามความต้องการได้ | |
| ในบทนี้เราจะมาเรียนวิธีสร้าง tokenizer ขึ้นมาตั้งแต่ต้น แทนที่จะเทรนจากตัวที่ถูก implement แล้วด้วยข้อมูลใหม่เท่านั้น อย่างที่เราได้ลองทำกันใน[บทที่ 2](/course/chapter6/2) เมื่อจบบทนี้ คุณจะสามารถสร้าง tokenizer แบบใดก็ได้ตามที่คุณต้องการ | |
| <Youtube id="MR8tZm5ViWU"/> | |
| library นี้ ประกอบด้วยส่วนหลักคือ `Tokenizer` class ที่มีส่วนประกอบย่อยสำคัญอื่นๆ แบ่งเป็นหลาย submodules | |
| - `normalizers` ประกอบไปด้วย `Normalizer` หลายประเภทที่คุณสามารถใช้ได้ (รายชื่อทั้งหมดดูได้[ที่นี่](https://huggingface.co/docs/tokenizers/api/normalizers)). | |
| - `pre_tokenizers` ประกอบไปด้วย `PreTokenizer` หลายประเภทที่คุณสามารถใช้ได้ (รายชื่อทั้งหมดดูได้[ที่นี่](https://huggingface.co/docs/tokenizers/api/pre-tokenizers)). | |
| - `models` ประกอบไปด้วย `Model` หลายประเภทที่คุณสามารถใช้ได้ เช่น `BPE`, `WordPiece`, และ `Unigram` (รายชื่อทั้งหมดดูได้[ที่นี่](https://huggingface.co/docs/tokenizers/api/models)). | |
| - `trainers` ประกอบไปด้วย `Trainer` หลายประเภทที่คุณสามารถใช้เพื่อเทรนโมเดลด้วย corpus ที่มีได้ (แต่ละโมเดลจะมีเทรนเนอร์อย่างละตัว; รายชื่อทั้งหมดดูได้[ที่นี่](https://huggingface.co/docs/tokenizers/api/trainers)). | |
| - `post_processors` ประกอบไปด้วย `PostProcessor` หลายประเภทที่คุณสามารถใช้ได้ (รายชื่อทั้งหมดดูได้[ที่นี่](https://huggingface.co/docs/tokenizers/api/post-processors)) | |
| - `decoders` ประกอบไปด้วย `Decoder` หลายประเภทที่ใช้เพื่อ decode ผลลัพธ์จากการ tokenization (รายชื่อทั้งหมดดูได้[ที่นี่](https://huggingface.co/docs/tokenizers/components#decoders)) | |
| คุณสามารถดูรายชื่อของส่วนประกอบสำคัญๆต่างๆได้[ที่นี่](https://huggingface.co/docs/tokenizers/components) | |
| ## การโหลด corpus | |
| ในการเทรน tokenizer ตัวใหม่ เราจะใช้ corpus เล็กๆ เพื่อที่การคำนวณจะได้รวดเร็ว | |
| การเรียกใช้งาน corpus จะทำคล้ายๆกับวิธีที่เราใช้ใน[ตอนต้นของบทนี้](/course/chapter6/2) แต่ครั้งนี้เราจะใช้ชุดข้อมูลชื่อ [WikiText-2](https://huggingface.co/datasets/wikitext) | |
| ```python | |
| from datasets import load_dataset | |
| dataset = load_dataset("wikitext", name="wikitext-2-raw-v1", split="train") | |
| def get_training_corpus(): | |
| for i in range(0, len(dataset), 1000): | |
| yield dataset[i : i + 1000]["text"] | |
| ``` | |
| `get_training_corpus()` เป็น generator ที่จะ yield ข้อมูลในรูปแบบ batch โดยแต่ละ batch ประกอบไปด้วย 1,000 ข้อความ | |
| 🤗 Tokenizers สามารถเทรนได้จากไฟล์ข้อความโดยตรง ดังนั้นเราจะสร้างไฟล์ข้อความ ที่ประกอบไปด้วย ข้อความทั้งหมดจาก WikiText-2 | |
| ```python | |
| with open("wikitext-2.txt", "w", encoding="utf-8") as f: | |
| for i in range(len(dataset)): | |
| f.write(dataset[i]["text"] + "\n") | |
| ``` | |
| ต่อไปเราจะพาคุณสร้าง tokenizer แบบ BERT, GPT-2, and XLNet ของคุณเอง ทีละขั้นตอน | |
| ซึ่งคุณก็จะได้ลองใช้งาน tokenization algorithm ต่างๆที่ได้เรียนมาแล้ว เช่น WordPiece, BPE, and Unigram มาเริ่มจาก BERT กัน | |
| ## สร้าง WordPiece tokenizer ตั้งแต่เริ่มต้น | |
| ในการสร้าง tokenizer โดยใช้ 🤗 Tokenizers library เราจะเริ่มจากการสร้าง `Tokenizer` object จาก `model` และตั้งค่า attribute ต่างๆ ได้แก่ `normalizer`, `pre_tokenizer`, `post_processor`, และ `decoder` ด้วยค่าที่เราต้องการ | |
| ในตัวอย่างนี้ เราจะสร้าง `Tokenizer` ด้วย WordPiece : | |
| ```python | |
| from tokenizers import ( | |
| decoders, | |
| models, | |
| normalizers, | |
| pre_tokenizers, | |
| processors, | |
| trainers, | |
| Tokenizer, | |
| ) | |
| tokenizer = Tokenizer(models.WordPiece(unk_token="[UNK]")) | |
| ``` | |
| จากนั้นเราจะตั้งค่า `unk_token` เพื่อบอกโมเดลว่าให้มัน return ค่าอะไรหากมันเจอตัวอักษรที่มันไม่รู้จัก | |
| ส่วน argument อื่นๆที่เราสามารถตั้งค่าได้ก็คือ `vocab` (แต่เนื่องจากเราจะเทรนโมเดล ทำให้ตอนนี้เรายังไม่ต้องตั้งค่านี้) และ `max_input_chars_per_word` ซึ่งหมายถึง ความยาวสูงสุดของแต่ละคำ (คำที่ยาวกว่าค่านี้จะถูกแบ่งเป็นหลายๆส่วน) | |
| ขั้นตอนแรกคือ normalization เนื่องจาก BERT เป็นโมเดลที่ได้รับความนิยมมาก เราจึงมี `BertNormalizer` เฉพาะ ซึ่งมี option ต่างๆดังนี้: `lowercase`, `strip_accents`, `clean_text` (ลบ control characters และแทนที่ช่องว่างที่อยู่ต่อกันหลายๆอันด้วยช่องว่างเดียว), และ `handle_chinese_chars` (เพิ่มช่องว่างในตัวอักษรจีน) | |
| เราจะมาเลียนแบบ `bert-base-uncased` tokenizer โดยตั้งค่า normalizer ดังนี้ : | |
| ```python | |
| tokenizer.normalizer = normalizers.BertNormalizer(lowercase=True) | |
| ``` | |
| แต่ปกติแล้ว สำหรับ tokenizer ใหม่ คุณอาจจะไม่สามารถใช้ normalizer ที่จาก 🤗 Tokenizers library ได้ ดังนั้นเราจะมาเรียนวิธีการสร้าง BERT normalizer เองกัน | |
| library นี้ มี normalizer เพื่อ `Lowercase` และเพื่อ `StripAccents` ซึ่งคุณสามารถรวมทั้งสองตัวได้โดยใช้ `Sequence` : | |
| ```python | |
| tokenizer.normalizer = normalizers.Sequence( | |
| [normalizers.NFD(), normalizers.Lowercase(), normalizers.StripAccents()] | |
| ) | |
| ``` | |
| เราจะใช้ `NFD` Unicode normalizer ด้วย เพื่อที่จะให้ `StripAccents` สามารถหาสัญลักษณ์ accents ได้ และจะได้ลบพวกมันออก | |
| เพื่อเช็คผลลัพธ์ของ normalizer เราจะใช้ `normalize_str()` method จาก `normalizer` : | |
| ```python | |
| print(tokenizer.normalizer.normalize_str("Héllò hôw are ü?")) | |
| ``` | |
| ```python out | |
| hello how are u? | |
| ``` | |
| > [!TIP] | |
| > **รายละเอียดเพิ่มเติม** ถ้าคุณทดลองใช้งาน normalizer ทั้งสองเวอร์ชันกับข้อความที่มีตัวอักษร unicode `u"\u0085"` คุณจะได้ผลลัพธ์ที่แตกต่างกัน | |
| > อย่างไรก็ตาม เราไม่อยากทำให้เวอร์ชันที่สร้างจาก `normalizers.Sequence` ของเรานั้นซับซ้อนเกินไป เราจึงไม่ใช้ Regex ที่ `BertNormalizer` ใช้เวลาที่ `clean_text` ถูกตั้งค่าเป็น `True` ซึ่งเป็นค่าตั้งต้น | |
| > แต่คุณไม่ต้องกังวลไป เพราะมันยังมีวิธีที่จะทำให้ผลลัพธ์ออกมาเป็นเหมือนกันโดยที่ไม่ต้องใช้ `BertNormalizer` นั่นคือโดยการเพิ่ม `normalizers.Replace` สองครั้ง เข้าไปใน `normalizers.Sequence` | |
| ขั้นตอนต่อไปคือ การ pre-tokenization เราจะใช้ `BertPreTokenizer` ที่ถูกสร้างมาแล้ว : | |
| ```python | |
| tokenizer.pre_tokenizer = pre_tokenizers.BertPreTokenizer() | |
| ``` | |
| หรือจะใช้ตัวที่เราสร้างขึ้นมาเองก็ได้ : | |
| ```python | |
| tokenizer.pre_tokenizer = pre_tokenizers.Whitespace() | |
| ``` | |
| โปรดทราบว่า `Whitespace` pre-tokenizer จะตัดข้อความตรงที่มีช่องว่าง และ รวมถึงตัวสัญลักษณ์ที่ไม่ใช้ตัวอักษร ตัวเลข หรือ underscore หรือพูดอีกแบบก็คือ มันจะแบ่งข้อความตรงที่มีช่องว่างและเครื่องหมายวรรคตอน นั่นเอง | |
| ```python | |
| tokenizer.pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.") | |
| ``` | |
| ```python out | |
| [('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)), | |
| ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))] | |
| ``` | |
| แต่ถ้าคุณต้องการจะแบ่งข้อความตามช่องว่างเท่านั้น ให้ใช้ `WhitespaceSplit` pre-tokenizer : | |
| ```python | |
| pre_tokenizer = pre_tokenizers.WhitespaceSplit() | |
| pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.") | |
| ``` | |
| ```python out | |
| [("Let's", (0, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre-tokenizer.', (14, 28))] | |
| ``` | |
| เหมือนกับใน normalizer เราสามารถใช้ `Sequence` เพื่อรวมหลายๆ pre-tokenizers เข้าด้วยกัน : | |
| ```python | |
| pre_tokenizer = pre_tokenizers.Sequence( | |
| [pre_tokenizers.WhitespaceSplit(), pre_tokenizers.Punctuation()] | |
| ) | |
| pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.") | |
| ``` | |
| ```python out | |
| [('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)), | |
| ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))] | |
| ``` | |
| ขั้นตอนต่อไปใน tokenization pipeline คือการใส่ input เข้าไปในโมเดล | |
| เราได้สร้างโมเดลขึ้นมาแล้ว แต่เรายังคงต้องเทรนมัน ซึ่งเราจะใช้ `WordPieceTrainer` | |
| สิ่งสำคัญที่คุณต้องจำคือ เวลาสร้าง (instantiate) trainer ใน 🤗 Tokenizers คุณจะต้องเพิ่ม token พิเศษต่างๆ เข้าไปในเทรนเนอร์เอง ไม่เช่นนั้น มันจะไม่เพิ่ม token พวกนี้เข้าไปใน vocabulary เพราะว่า token พิเศษไม่ได้อยู่ใน training corpus : | |
| ```python | |
| special_tokens = ["[UNK]", "[PAD]", "[CLS]", "[SEP]", "[MASK]"] | |
| trainer = trainers.WordPieceTrainer(vocab_size=25000, special_tokens=special_tokens) | |
| ``` | |
| นอกจากเราจะสามารถตั้งค่า `vocab_size` และ `special_tokens` แล้ว เรายังสามารถตั้งค่า `min_frequency` (ความถี่ขั้นต่ำของคำที่ต้องมี เพื่อที่จะได้ถูกเพิ่มลงไปใน vocabulary) หรือ `continuing_subword_prefix` (ถ้าหากเราต้องการใช้สัญลักษณ์อื่น ที่ไม่ใช่`##`) ได้อีกด้วย | |
| เราจะเทรนโมเดลโดยรันคำสั่งต่อไปนี้ : | |
| ```python | |
| tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer) | |
| ``` | |
| คุณสามารถใช้ไฟล์ข้อความเพื่อเทรนได้ด้วย (แต่ก่อนอื่น คุณจะต้องสร้างโมเดลขึ้นมาใหม่โดยใช้ `WordPiece` ที่ยังว่างเปล่าอยู่) : | |
| ```python | |
| tokenizer.model = models.WordPiece(unk_token="[UNK]") | |
| tokenizer.train(["wikitext-2.txt"], trainer=trainer) | |
| ``` | |
| สำหรับทั้งสองกรณี คุณสามารถทดลองใช้ tokenizer กับข้อความได้ โดยเรียกใช้ `encode()` method : | |
| ```python | |
| encoding = tokenizer.encode("Let's test this tokenizer.") | |
| print(encoding.tokens) | |
| ``` | |
| ```python out | |
| ['let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.'] | |
| ``` | |
| `encoding` ที่เราได้คือ `Encoding` ที่ประกอบไปด้วยข้อมูลจำเป็นต่างๆสำหรับ tokenizer ได้แก่ `ids`, `type_ids`, `tokens`, `offsets`, `attention_mask`, `special_tokens_mask`, และ `overflowing` | |
| ขั้นตอนสุดท้ายของ pipeline คือ post-processing | |
| เราจะเพิ่ม `[CLS]` ไว้ที่ตอนต้นของผลลัพธ์จากการ tokenize และ `[SEP]` ไว้ที่ตอนท้าย หรือหลังสิ้นสุดประโยค ถ้า input ของเรามีสองประโยค | |
| เราจะใช้ `TemplateProcessor` เพื่อช่วยเราทำ แต่ก่อนอื่นเราต้องหา ID ของ `[CLS]` และ `[SEP]` ก่อน : | |
| ```python | |
| cls_token_id = tokenizer.token_to_id("[CLS]") | |
| sep_token_id = tokenizer.token_to_id("[SEP]") | |
| print(cls_token_id, sep_token_id) | |
| ``` | |
| ```python out | |
| (2, 3) | |
| ``` | |
| เราจะมาสร้าง template ให้กับ `TemplateProcessor` โดยจะต้องกำหนดว่าเราต้องการให้มันประมวลผล input ที่เป็น ประโยคเดี่ยว และ input ที่มีสองประโยคอย่างไร | |
| สำหรับทั้งสองกรณี เราจะต้องกำหนด token พิเศษ เพื่อแทนประโยคขึ้นมา สำหรับประโยคเดี่ยวหรือประโยคแรก เราจะใช้ `$A` ส่วนสำหรับประโยคที่สอง เราจะใช้ `$B` | |
| สำหรับ token พิเศษพวกนี้และประโยค เราจะสร้าง token type ID ขึ้นมา โดยจะเขียน ID นี้หลังเครื่องหมาย colon | |
| template ของ BERT มีรายละเอียดดังนี้ : | |
| ```python | |
| tokenizer.post_processor = processors.TemplateProcessing( | |
| single=f"[CLS]:0 $A:0 [SEP]:0", | |
| pair=f"[CLS]:0 $A:0 [SEP]:0 $B:1 [SEP]:1", | |
| special_tokens=[("[CLS]", cls_token_id), ("[SEP]", sep_token_id)], | |
| ) | |
| ``` | |
| อย่าลืมว่า เราจะต้องให้ข้อมูลโมเดลเกี่ยวกับ ID ของ token พิเศษด้วย เพื่อที่โมเดลจะได้แปลงมันได้อย่างถูกต้อง | |
| เราจะกลับมาดูตัวอย่างก่อนหน้ากัน : | |
| ```python | |
| encoding = tokenizer.encode("Let's test this tokenizer.") | |
| print(encoding.tokens) | |
| ``` | |
| ```python out | |
| ['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.', '[SEP]'] | |
| ``` | |
| ถ้าเราใส่ input ที่เป็นสองประโยค เราจะได้ : | |
| ```python | |
| encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences.") | |
| print(encoding.tokens) | |
| print(encoding.type_ids) | |
| ``` | |
| ```python out | |
| ['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '...', '[SEP]', 'on', 'a', 'pair', 'of', 'sentences', '.', '[SEP]'] | |
| [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1] | |
| ``` | |
| ขั้นตอนสุดท้าย เราจะเพิ่ม decoder เข้าไปใน pipeline : | |
| ```python | |
| tokenizer.decoder = decoders.WordPiece(prefix="##") | |
| ``` | |
| มาดูผลลัพธ์ของ `encoding` กัน : | |
| ```python | |
| tokenizer.decode(encoding.ids) | |
| ``` | |
| ```python out | |
| "let's test this tokenizer... on a pair of sentences." | |
| ``` | |
| เยี่ยมมาก! ตอนนี้เราสามารถบันทึก tokenizer นี้เป็นไฟล์ JSON ได้แล้ว ดังนี้ : | |
| ```python | |
| tokenizer.save("tokenizer.json") | |
| ``` | |
| คุณสามารถโหลดไฟล์นี้ให้เป็น `Tokenizer` object ได้โดยใช้ `from_file()` method : | |
| ```python | |
| new_tokenizer = Tokenizer.from_file("tokenizer.json") | |
| ``` | |
| การจะนำ tokenizer นี้มาใช้ใน 🤗 Transformers เราจะต้อง wrap มันให้เป็น `PreTrainedTokenizerFast` ก่อน | |
| โดยเราใช้ class ปกติ (ถ้า tokenizer ของเรามีโครงสร้างสอดคล้องกันโมเดลหลักเราที่จะใช้งาน) หรือ class ที่ถูกสร้างขึ้นมาแล้ว เช่น `BertTokenizerFast` | |
| ในกรณีที่คุณสร้าง tokenizer ขึ้นมาเองอย่างที่เราได้สอนไว้ข้างต้น คุณจะต้องใช้ตัวเลือกแรก | |
| การจะ wrap tokenizer ของเราให้เป็น `PreTrainedTokenizerFast` เราจะต้องส่งผ่าน tokenizer ของเราเข้าไปเป็น `tokenizer_object` หรือ ส่งผ่านไฟล์ของ tokenizer เป็น `tokenizer_file` | |
| สิ่งสำคัญอีกอย่างคือ คุณจะต้องส่งผ่านข้อมูลเกี่ยวกับ token พิเศษ ต่างๆให้โมเดลเอง เพราะว่า class นี้จะไม่สามารถ infer จาก `tokenizer` object ได้เอง ว่า token ตัวไหนเป็น mask token เช่น `[CLS]` : | |
| ```python | |
| from transformers import PreTrainedTokenizerFast | |
| wrapped_tokenizer = PreTrainedTokenizerFast( | |
| tokenizer_object=tokenizer, | |
| # tokenizer_file="tokenizer.json", # You can load from the tokenizer file, alternatively | |
| unk_token="[UNK]", | |
| pad_token="[PAD]", | |
| cls_token="[CLS]", | |
| sep_token="[SEP]", | |
| mask_token="[MASK]", | |
| ) | |
| ``` | |
| ถ้าคุณใช้ class ที่เฉพาะเจาะจง เช่น `BertTokenizerFast` คุณเพียงแค่ต้องเพิ่มข้อมูลเกี่ยวกับ token พิเศษที่ต่างจากตัวที่โมเดลใช้อยู่แล้ว ในตัวอย่างของเรา เราไม่ได้ใช้ token พิเศษอื่น จึงไม่ต้องเพิ่มอะไร : | |
| ```python | |
| from transformers import BertTokenizerFast | |
| wrapped_tokenizer = BertTokenizerFast(tokenizer_object=tokenizer) | |
| ``` | |
| ตอนนี้เราก็สามารถใช้ tokenizer ที่เราได้สร้างขึ้นมาเอง เหมือนกับ tokenizer ตัวอื่นๆจาก 🤗 Transformers ได้แล้ว นอกจากนั้นคุณยังสามารถเซฟมันได้ โดยใช้ `save_pretrained()` หรืออัพโหลดมันไปที่ Hub โดยใช้ `push_to_hub()` | |
| หลังจากที่เราได้ดูกันแล้วว่าจะสร้าง WordPiece tokenizer อย่างไร เราจะมาดูกันว่า จะทำแบบเดียวกันกับ BPE tokenizer ได้อย่างไร เราจะอธิบายคร่าวๆเท่านั้น เพราะคุณได้เรียนรายละเอียดมาแล้ว และจะพูดถึงแค่ข้อแตกต่างเท่านั้น | |
| ## การสร้าง BPE tokenizer ตั้งแต่เริ่มต้น | |
| เราจะมาสร้าง GPT-2 tokenizer กัน เช่นเดียวกับตัวอย่างของ BERT tokenizer เราจะเริ่มด้วยกัน initialize `Tokenizer` ด้วยโมเดล BPE : | |
| ```python | |
| tokenizer = Tokenizer(models.BPE()) | |
| ``` | |
| เราสามารถสร้างโมเดลนี้ด้วย vocabulary ที่มีอยู่ได้ โดยส่งผ่าน `vocab` และ `merges` แต่เราจะเทรนตั้งแต่ต้น แปลว่าเราไม่จำเป็นต้องทำขั้นตอนนี้ | |
| เราไม่จำเป็นต้องกำหนด `unk_token` เพราะ GPT-2 ใช้ byte-level BPE | |
| นอกจากนั้น GPT-2 ยังไม่ใช้ normalizer อีกด้วย เราจึงจะข้ามขั้นตอนนี้ไปทำ pre-tokenization เลย : | |
| ```python | |
| tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False) | |
| ``` | |
| `add_prefix_space=False` หมายถึงเราจะไม่เพิ่มช่องว่างตรงต้นประโยค (ค่า default จะมีการเพิ่มช่องว่างนี้) | |
| มาดูผลลัพธ์ตัวอย่างของการ pre-tokenization กัน : | |
| ```python | |
| tokenizer.pre_tokenizer.pre_tokenize_str("Let's test pre-tokenization!") | |
| ``` | |
| ```python out | |
| [('Let', (0, 3)), ("'s", (3, 5)), ('Ġtest', (5, 10)), ('Ġpre', (10, 14)), ('-', (14, 15)), | |
| ('tokenization', (15, 27)), ('!', (27, 28))] | |
| ``` | |
| ขั้นตอนต่อไปคือการเทรนโมเดล สำหรับ GPT-2 มันจะมี token พิเศษตัวเดียวคือ end-of-text token (อยู่ตรงท้ายของข้อความ) : | |
| ```python | |
| trainer = trainers.BpeTrainer(vocab_size=25000, special_tokens=["<|endoftext|>"]) | |
| tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer) | |
| ``` | |
| เช่นเดียวกันกับ `WordPieceTrainer` คุณสามารถกำหนด `vocab_size`, `special_tokens`, `min_frequency` ได้ ถ้าหากคุณใช้ end-of-word suffix (เช่น `</w>`) คุณก็สามารถตั้งค่ามันได้ด้วย `end_of_word_suffix` | |
| คุณสามารถเทรนด้วยไฟล์ข้อความได้ด้วย : | |
| ```python | |
| tokenizer.model = models.BPE() | |
| tokenizer.train(["wikitext-2.txt"], trainer=trainer) | |
| ``` | |
| มาดูผลลัพธ์ของการ tokenize ข้อความตัวอย่างกัน : | |
| ```python | |
| encoding = tokenizer.encode("Let's test this tokenizer.") | |
| print(encoding.tokens) | |
| ``` | |
| ```python out | |
| ['L', 'et', "'", 's', 'Ġtest', 'Ġthis', 'Ġto', 'ken', 'izer', '.'] | |
| ``` | |
| เราจะทำการ post-processing แบบ byte-level ให้กับ GPT-2 tokenizer ดังนี้ : | |
| ```python | |
| tokenizer.post_processor = processors.ByteLevel(trim_offsets=False) | |
| ``` | |
| `trim_offsets = False` แปลว่าเราจะไม่เปลี่ยนแปลงค่า offset ของ token ที่ขึ้นต้นด้วย `Ġ` ซึ่งแปลว่าตำแหน่งเริ่มต้นของ offset จะหมายถึง ช่องว่าง และไม่ใช่ตัวอักษรแรกของ token นั้น | |
| มาดูผลลัพธ์ของข้อความที่เราเพิ่งจะ encode กันไป ในตัวอย่างนี้ token ในตำแหน่งที่ 4 คือ `'Ġtest'` : | |
| ```python | |
| sentence = "Let's test this tokenizer." | |
| encoding = tokenizer.encode(sentence) | |
| start, end = encoding.offsets[4] | |
| sentence[start:end] | |
| ``` | |
| ```python out | |
| ' test' | |
| ``` | |
| สุดท้ายเราจะเพิ่มส่วนที่เป็น byte-level decoder : | |
| ```python | |
| tokenizer.decoder = decoders.ByteLevel() | |
| ``` | |
| เช็คดูอีกทีว่าผลลัพธ์ถูกต้องหรือไม่ : | |
| ```python | |
| tokenizer.decode(encoding.ids) | |
| ``` | |
| ```python out | |
| "Let's test this tokenizer." | |
| ``` | |
| เยี่ยมมาก! ตอนนี้คุณสามารถเซฟโมเดลได้ ส่วนขั้นต่อไปคือ เราจะ wrap tokenizer ของเราให้เป็น `PreTrainedTokenizerFast` หรือ `GPT2TokenizerFast` เพื่อจะได้เรียกใช้มันได้ใน 🤗 Transformers | |
| ```python | |
| from transformers import PreTrainedTokenizerFast | |
| wrapped_tokenizer = PreTrainedTokenizerFast( | |
| tokenizer_object=tokenizer, | |
| bos_token="<|endoftext|>", | |
| eos_token="<|endoftext|>", | |
| ) | |
| ``` | |
| หรือ : | |
| ```python | |
| from transformers import GPT2TokenizerFast | |
| wrapped_tokenizer = GPT2TokenizerFast(tokenizer_object=tokenizer) | |
| ``` | |
| ต่อไปเราจะสอนวิธีสร้าง Unigram tokenizer บ้าง | |
| ## การสร้าง Unigram tokenizer ตั้งแต่เริ่มต้น | |
| เราจะสร้าง XLNet tokenizer โดยเริ่มจาก initialize `Tokenizer` ด้วย Unigram model ! | |
| ```python | |
| tokenizer = Tokenizer(models.Unigram()) | |
| ``` | |
| คุณสามารถ initialize มันโดยส่งผ่าน vocabulary ที่มีอยู่เข้าไปด้วย | |
| ในขั้นตอน normalization โมเดล XLNet จะมีการแทนที่ตัวอักษรต่างๆ : | |
| ```python | |
| from tokenizers import Regex | |
| tokenizer.normalizer = normalizers.Sequence( | |
| [ | |
| normalizers.Replace("``", '"'), | |
| normalizers.Replace("''", '"'), | |
| normalizers.NFKD(), | |
| normalizers.StripAccents(), | |
| normalizers.Replace(Regex(" {2,}"), " "), | |
| ] | |
| ) | |
| ``` | |
| โค้ดข้างบนนี้จะแทนที่ <code>``</code> และ <code>''</code> ด้วย <code>"</code> | |
| และถ้ามีช่องว่างที่อยู่ต่อๆกัน มันจะถูกแปลงให้เป็นช่องว่างเดียว และสุดท้ายมันจะลบสัญลักษณ์ accent ออกด้วย | |
| pre-tokenizer ที่ใช้ใน SentencePiece tokenizer คือ `Metaspace` : | |
| ```python | |
| tokenizer.pre_tokenizer = pre_tokenizers.Metaspace() | |
| ``` | |
| มาดูผลลัพธ์ของการ pre-tokenization กับข้อความตัวอย่างกัน : | |
| ```python | |
| tokenizer.pre_tokenizer.pre_tokenize_str("Let's test the pre-tokenizer!") | |
| ``` | |
| ```python out | |
| [("▁Let's", (0, 5)), ('▁test', (5, 10)), ('▁the', (10, 14)), ('▁pre-tokenizer!', (14, 29))] | |
| ``` | |
| ขั้นต่อไปคือการเทรน XLNet มีการใช้ token พิเศษอยู่จำนวนหนึ่ง : | |
| ```python | |
| special_tokens = ["<cls>", "<sep>", "<unk>", "<pad>", "<mask>", "<s>", "</s>"] | |
| trainer = trainers.UnigramTrainer( | |
| vocab_size=25000, special_tokens=special_tokens, unk_token="<unk>" | |
| ) | |
| tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer) | |
| ``` | |
| สิ่งสำคัญเวลาใช้ `UnigramTrainer` คือ อย่าลืมกำหนด argument `unk_token` | |
| นอกจากนั้นคุณยังสามารถตั้งค่า argument อื่นๆที่ต้องใช้กับ Unigram algorithm ได้ด้วย เช่น `shrinking_factor` (ค่าเริ่มต้นคือ 0.75) หรือ `max_piece_length` (ค่าเริ่มต้นคือ 16) | |
| เราสามารถเทรนจากไฟล์ข้อความได้ด้วย : | |
| ```python | |
| tokenizer.model = models.Unigram() | |
| tokenizer.train(["wikitext-2.txt"], trainer=trainer) | |
| ``` | |
| ลอง tokenize ตัวอย่างง่ายๆดู : | |
| ```python | |
| encoding = tokenizer.encode("Let's test this tokenizer.") | |
| print(encoding.tokens) | |
| ``` | |
| ```python out | |
| ['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.'] | |
| ``` | |
| XLNet จะเพิ่ม token พิเศษ `<cls>` ใส่ในตอนท้ายของประโยค และตั้งค่า type ID มันเป็น 2 เพื่อให้มันแตกต่างจาก token อื่น | |
| การทำแบบนี้ถือว่าเป็นการ padding ทางด้ายซ้าย | |
| เพื่อจัดการกับ token พิเศษ เราจะต้องสร้าง template ขึ้นมา ก่อนอื่นเราต้องดูว่า ID ของ `<cls>` และ `<sep>` คืออะไร : | |
| ```python | |
| cls_token_id = tokenizer.token_to_id("<cls>") | |
| sep_token_id = tokenizer.token_to_id("<sep>") | |
| print(cls_token_id, sep_token_id) | |
| ``` | |
| ```python out | |
| 0 1 | |
| ``` | |
| นี่คือตัวอย่างการสร้าง template : | |
| ```python | |
| tokenizer.post_processor = processors.TemplateProcessing( | |
| single="$A:0 <sep>:0 <cls>:2", | |
| pair="$A:0 <sep>:0 $B:1 <sep>:1 <cls>:2", | |
| special_tokens=[("<sep>", sep_token_id), ("<cls>", cls_token_id)], | |
| ) | |
| ``` | |
| เราจะทดลองใช้งานมันโดยการ encode ประโยค input สองประโยค : | |
| ```python | |
| encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences!") | |
| print(encoding.tokens) | |
| print(encoding.type_ids) | |
| ``` | |
| ```python out | |
| ['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.', '.', '.', '<sep>', '▁', 'on', '▁', 'a', '▁pair', | |
| '▁of', '▁sentence', 's', '!', '<sep>', '<cls>'] | |
| [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2] | |
| ``` | |
| จากนั้นตั้งค่า decoder เป็น `Metaspace` : | |
| ```python | |
| tokenizer.decoder = decoders.Metaspace() | |
| ``` | |
| ตอนนี้เราก็เสร็จแล้ว คุณสามารถเซฟ tokenizer และ wrap มันให้เป็น `PreTrainedTokenizerFast` หรือ `XLNetTokenizerFast` ก็ได้ ถ้าคุณต้องการใช้มันใน 🤗 Transformers | |
| สิ่งสำคัญอีกอย่างเวลาใช้ `PreTrainedTokenizerFast` ก็คือเราจะต้องบอก 🤗 Transformers library ว่าเราได้ทำการ padding ทางซ้าย : | |
| ```python | |
| from transformers import PreTrainedTokenizerFast | |
| wrapped_tokenizer = PreTrainedTokenizerFast( | |
| tokenizer_object=tokenizer, | |
| bos_token="<s>", | |
| eos_token="</s>", | |
| unk_token="<unk>", | |
| pad_token="<pad>", | |
| cls_token="<cls>", | |
| sep_token="<sep>", | |
| mask_token="<mask>", | |
| padding_side="left", | |
| ) | |
| ``` | |
| อีกวิธีก็คือ : | |
| ```python | |
| from transformers import XLNetTokenizerFast | |
| wrapped_tokenizer = XLNetTokenizerFast(tokenizer_object=tokenizer) | |
| ``` | |
| ตอนนี้ คุณก็ได้เรียนรู้ขั้นตอนในการสร้าง tokenizer ขึ้นมาเองแล้ว โดยการใช้เครื่องมือจาก 🤗 Tokenizers library และได้เรียนวิธีการนำ tokenizer ของคุณไปใช้ใน 🤗 Transformers อีกด้วย | |
| <EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/th/chapter6/8.mdx" /> |
Xet Storage Details
- Size:
- 37.1 kB
- Xet hash:
- acdd2c240dbbd469026fe798bdfd5d657945873c8e8ddfa9239880f2a408d839
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.