Token dan tokenisasi

Apa itu token dan mengapa komputer memotong teks dengan aturan yang berbeda dari para linguis.

Daftar isi

Token adalah satuan data paling dasar dalam pengolahan teks, dan topik ini membahas cara komputer memotong teks menjadi satuan tersebut. Pemotongan yang tampak sederhana sesungguhnya berisi banyak keputusan, karena tanda baca, kontraksi, singkatan, dan angka tidak selalu terpisah oleh spasi. Tiga cara tokenisasi, yaitu str.split(), word_tokenize dari NLTK, dan tokenizer spaCy, dijalankan pada satu kalimat yang sama sehingga perbedaannya terbaca langsung dari keluaran asli. Setelah membaca topik ini, pemilihan tokenizer dapat dilandasi perilaku yang terukur, bukan kebiasaan.

Token sebagai satuan teks terkecil#

Tokenizer adalah program yang membaca teks mentah dan mengeluarkan deretan token, yaitu potongan teks terkecil yang digunakan oleh tahap-tahap berikutnya. Satu token dapat berupa kata, tanda baca, angka, atau bagian kata seperti n't pada didn't. Teks dipandang berstruktur berlapis, yakni korpus berisi dokumen, dokumen berisi kalimat, dan kalimat berisi token, sehingga token menjadi baris data yang dihitung, disaring, dan diubah menjadi fitur.

Token tidak sama dengan kata. Kalimat yang sama dapat menghasilkan lebih banyak token daripada kata, karena koma, titik, dan tanda tanya dihitung sebagai token tersendiri oleh tokenizer bahasa. Perbedaan ini bukan detail kosmetik, karena model klasik seperti bag of words membaca teks sebagai hitungan token, sehingga keputusan pemotongan menentukan nilai setiap fitur.

Diagram berikut menempatkan tokenisasi sebagai tahap pertama dalam alur mengubah teks menjadi data, sebelum normalisasi dan pembentukan fitur.

Teks mentahTokenisasiNormalisasiFitur modeldokumen apa adanyakalimat menjadi tokenbentuk diseragamkanangka untuk model
Posisi tokenisasi dalam alur teks menjadi data

Alur pada diagram menjadi kerangka seluruh modul ini, sehingga keputusan tokenisasi yang diambil di sini berdampak pada setiap tahap di belakangnya. Kesalahan pemotongan tidak dapat diperbaiki oleh tahap berikutnya, karena tahap-tahap tersebut hanya melihat token yang diberikan.

Mengapa pemotongan teks tidak sepele#

Batas antarkata tidak selalu ditandai spasi, sehingga pemotongan berbasis spasi gagal pada kasus yang muncul di hampir setiap teks nyata. Kontraksi seperti didn't menggabungkan kata kerja dan kata ganti dalam satu rangkaian huruf. Singkatan gelar seperti Dr. menggunakan titik yang sama dengan tanda akhir kalimat. Angka desimal seperti 3.14 menggunakan titik sebagai pemisah bagian, sedangkan rangkaian seperti 3.14-mile menyambungkan angka dan kata melalui hyphen.

Keempat kasus itu menuntut keputusan yang berbeda. Kontraksi lebih informatif bila dipecah menjadi did dan n't, sedangkan titik pada Dr. justru bagian dari singkatannya, bukan akhir kalimat. Titik pada 3.14 harus tetap menempel agar angka terbaca utuh, sedangkan hyphen pada 3.14-mile dapat diperlakukan sebagai pemisah atau sebagai penghubung, tergantung kebutuhan. Pemotongan yang keliru menggeser hitungan token, dan hitungan yang bergeser membuat bobot fitur menyimpang dari isi teks.

Kalimat uji yang digunakan sepanjang topik ini memuat keempat kasus tersebut sekaligus, sehingga setiap tokenizer dibandingkan pada teks yang menantang bagi ketiganya. Kalimat ujinya adalah Dr. Ada didn't finish the 3.14-mile route on time, did she?

Kalimat uji dan tiga cara memotongnya#

Cara paling sederhana memotong teks adalah membaginya pada karakter spasi. Dua pustaka di sandbox menawarkan tokenizer yang lebih sadar bahasa, yaitu word_tokenize pada NLTK dan tokenizer bawaan model spaCy. Ketiganya dijalankan terhadap kalimat uji yang sama, dan keluarannya dibandingkan satu per satu agar perbedaan aturannya terlihat jelas.

Pemotongan dengan str.split()#

Metode split() pada Python memotong string pada setiap rangkaian spasi (whitespace) dan mengembalikan daftar string. Hasilnya diperoleh cepat, tetapi pemotongannya tidak sadar bahasa sama sekali. Jalankan potongan berikut untuk melihat keluaran aslinya.

python
kalimat = "Dr. Ada didn't finish the 3.14-mile route on time, did she?"

token_spasi = kalimat.split()
print(token_spasi)
print("jumlah token:", len(token_spasi))
text Keluaran
['Dr.', 'Ada', "didn't", 'finish', 'the', '3.14-mile', 'route', 'on', 'time,', 'did', 'she?']
jumlah token: 11

Tiga keputusan pemotongan gagal di sini. Koma pada time, dan tanda tanya pada she? masih menempel pada katanya, sehingga keduanya dihitung sebagai bagian dari kata. Kontraksi didn't dan rangkaian 3.14-mile tidak pernah dipecah. Jumlah token yang tercatat adalah 11, padahal kalimat membawa lebih banyak informasi daripada yang dapat terbaca oleh model dari token-token itu.

Pemotongan dengan NLTK#

Fungsi word_tokenize dari modul nltk.tokenize menerapkan aturan tokenisasi yang mengenali tanda baca dan kontraksi. Fungsi ini digunakan luas pada eksperimen NLP klasik karena cepat dan deterministik.

python
from nltk.tokenize import word_tokenize

kalimat = "Dr. Ada didn't finish the 3.14-mile route on time, did she?"

token_nltk = word_tokenize(kalimat)
print(token_nltk)
print("jumlah token:", len(token_nltk))
text Keluaran
['Dr.', 'Ada', 'did', "n't", 'finish', 'the', '3.14-mile', 'route', 'on', 'time', ',', 'did', 'she', '?']
jumlah token: 14

Tanda koma dan tanda tanya kini menjadi token tersendiri, sehingga jumlah token naik menjadi 14. Kontraksi didn't dipecah menjadi did dan n't sesuai aturan khusus NLTK. Rangkaian 3.14-mile tetap satu token karena tokenizer NLTK menganggap hyphen yang menyambungkan angka dan huruf bukan pemisah.

Pemotongan dengan spaCy#

spaCy memuat tokenizer sebagai bagian dari pipeline model. Panggilan spacy.load("en_core_web_sm") mengembalikan objek nlp, dan kalimat yang dilewatkan ke objek tersebut menghasilkan objek Doc yang berisi token-token. Jalankan potongan berikut dan bandingkan keluarannya dengan NLTK.

python
import spacy

nlp = spacy.load("en_core_web_sm")
kalimat = "Dr. Ada didn't finish the 3.14-mile route on time, did she?"

doc = nlp(kalimat)
token_spacy = [token.text for token in doc]
print(token_spacy)
print("jumlah token:", len(token_spacy))
text Keluaran
['Dr.', 'Ada', 'did', "n't", 'finish', 'the', '3.14', '-', 'mile', 'route', 'on', 'time', ',', 'did', 'she', '?']
jumlah token: 16

Keluaran spaCy paling rinci dengan 16 token, karena hyphen dianggap token terpisah sehingga 3.14-mile terpecah menjadi 3.14, -, dan mile. Tanda baca dan kontraksi diperlakukan sama seperti NLTK. Perbedaan pada hyphen menunjukkan bahwa dua tokenizer yang sama-sama sadar bahasa tetap berbeda pada aturan tepinya.

Tabel berikut merangkum perbedaannya pada potongan kalimat yang menentukan.

Bagian kalimatstr.split()NLTKspaCy
didn'tsatu token utuhdid + n'tdid + n't
3.14-milesatu token utuhsatu token utuh3.14 + - + mile
time,satu token dengan komatime + ,time + ,
Jumlah token111416

Tidak ada satu pun keluaran di atas yang keliru secara mutlak. Ketiganya menerapkan definisi token yang berbeda, dan pilihan yang tepat bergantung pada tahap berikutnya. Pemrosesan berbasis hitungan biasanya memilih tokenizer pustaka, sedangkan pemisahan spasi cukup untuk kebutuhan penghitungan kasar yang tidak memerlukan tanda baca terpisah.

Informasi bawaan dari token spaCy#

Token pada spaCy bukan string biasa, melainkan objek yang membawa atribut turunan. Atribut seperti is_alpha, is_punct, dan like_num menjawab pertanyaan yang sering muncul pada pemfilteran token, misalnya membuang tanda baca atau mengambil angka. Atribut ini dihitung sekali saat tokenisasi dan tersedia tanpa pemrosesan tambahan.

python
import spacy

nlp = spacy.load("en_core_web_sm")
kalimat = "Dr. Ada didn't finish the 3.14-mile route on time, did she?"

doc = nlp(kalimat)
for token in doc:
    if token.text in ("Dr.", "Ada", "3.14", "-"):
        print(token.text, "| is_alpha:", token.is_alpha, "| is_punct:", token.is_punct, "| like_num:", token.like_num)
text Keluaran
Dr. | is_alpha: False | is_punct: False | like_num: False
Ada | is_alpha: True | is_punct: False | like_num: False
3.14 | is_alpha: False | is_punct: False | like_num: True
- | is_alpha: False | is_punct: True | like_num: False

Baris pertama menunjukkan bahwa Dr. tidak murni berupa huruf karena titiknya, tetapi juga bukan tanda baca, sehingga singkatan gelar memang terklasifikasi tersendiri. Token Ada seluruhnya berupa huruf, token 3.14 dikenali sebagai angka melalui like_num, dan token - tercatat sebagai tanda baca. Pemfilteran seperti if not token.is_punct menjadi mungkin tanpa membaca ulang teks aslinya.

Kesalahan umum#

  1. Menghitung kata dengan split() untuk analisis berbasis fitur. Pemisahan spasi menyisakan tanda baca pada token, sehingga hitungan fitur menyimpang. Gunakan tokenizer pustaka ketika hasil pemotongan menjadi fitur.
  2. Menganggap print(doc) menampilkan token. Objek Doc pada spaCy mencetak ulang seluruh teks aslinya. Iterasi token secara eksplisit, misalnya for token in doc, lalu baca token.text.
  3. Mengganti tokenizer di tengah pipeline tanpa mencatatnya. Jumlah dan isi token berubah antartokenizer, sehingga fitur yang dihitung tidak dapat direproduksi. Catat tokenizer dan versi pustakanya bersama kode pemrosesan.

Ringkasan#

  • Token adalah potongan teks terkecil yang dihasilkan tokenizer, dan seluruh tahap pengolahan teks bekerja di atas deretan token tersebut.
  • Pemisahan spasi bukan batas kata, karena kontraksi, singkatan bertitik, angka desimal, dan hyphen menuntut keputusan pemotongan yang berbeda.
  • Pada kalimat uji yang sama, str.split() menghasilkan 11 token, NLTK menghasilkan 14, dan spaCy menghasilkan 16, dengan perbedaan pada tanda baca, kontraksi, dan hyphen.
  • Token spaCy membawa atribut is_alpha, is_punct, dan like_num yang siap digunakan untuk pemfilteran.

Referensi#