Train an Indonesian NER From a Blank SpaCy Model

October 26, 2020
SpaCy NER NLP

So, how we train a Named Entity Recognition model in SpaCy using our own dataset? long story short, though the title is in English, but this time I will write the story in Indonesian, since the model is an Indonesian Named Entity Recognition.

Jadi… bagi yang ingin bikin model untuk keperluan “sequence-tagging” seperti Ekstraksi Entitas dan Pengenalan Entitas, maka biar tidak pusing atau pusing dengan bikin model pakai stack LSTM atau LSTM+CRF seperti kebanyakan yang ada di tutorial-tutorial itu, apalagi pakai embedding kaya word2vec dan glove dan bert fang femes itu kan overkill… dan tentunya bakal lama, ya to ya?

Nah ada yang namanya SpaCy, jadi SpaCy ini pustaka perangkat lunak untuk NLP dari explosion.ai, sudah menyediakan “stacks” seperti kebutuhan pre-proses, tokenisasi, lematisasi, klasifikasi dan lain-lain termasuk ekstraksi entitas. Detail informasi terkait SpaCy bisa dilihat dan dipelajari laman resmi SpaCy. Sampai saat ini, SpaCy belum merilis pre-train model untuk bahasa Indonesia secara resmi, namun tetap kita bisa melakukan fine-tuning atau training model dengan data yang kita miliki, singkatnya cukup siapkan data dengan format yang sesuai tinggal train beberapa iterasi, bisa dipakai.

Kali ini, akan ditulis mengenai instruksi singkat untuk men-train model Ekstrasi dan Pengenalan Entitas dengan SpaCy, untuk dataset saya mendapatkan data dari sini: https://raw.githubusercontent.com/yusufsyaifudin/indonesia-ner/master/resources/ner/data_train.txt dan sini: https://raw.githubusercontent.com/yohanesgultom/nlp-experiments/master/data/ner/training_data.txt matur nuwun maaase…

Data tersebut saya convert ke SpaCy format sehingga berformat seperti ini:

('Menurut Arie jika Aburizal terus memaksakan kehendaknya, bukan tidak mungkin Golkar akan menyusul Partai Persatuan Pembangunan yang terpecah karena konflik internal.', {'entities': [(8, 12, 'PERSON'), (18, 26, 'PERSON'), (77, 83, 'ORGANIZATION'), (98, 126, 'ORGANIZATION')]})

artinya, karakter dari indeks ke 18 hingga 26 merupakan kata yang mengandung Entitas dengan tipe PERSON, karakter indeks ke 77 hingga 83 mengandung Entitas dengan tipe Organisasi, begitu seterusnya.

Pertama, kita panggil terlebih dahulu pustaka-pustaka yang dibutuhkan dan data pembelajaran yang sudah kita persiapkan sebelumnya:

import pickle
import spacy
import random
from spacy.util import minibatch, compounding
from spacy import load, displacy

Pemanggilan data pembelajaran

with open('ner_spacy_fmt_datasets.pickle', 'rb') as f:
    ner_spacy_fmt_datasets = pickle.load(f)

Selanjutnya kita akan buat model dari “model kosong” atau “blank-model” dengan perintah berikut

nlp=spacy.blank("id")

nlp.add_pipe(nlp.create_pipe('ner'))

nlp.begin_training()

Kita ambil data-modul untuk NER dan menyisihkan yang lainnya.

ner=nlp.get_pipe("ner")

pipe_exceptions = ["ner", "trf_wordpiecer", "trf_tok2vec"]

unaffected_pipes = [pipe for pipe in nlp.pipe_names if pipe not in pipe_exceptions]

Kita proses atau ambil label tipe entitas dari data training

for _, annotations in ner_spacy_fmt_datasets:
    for ent in annotations.get("entities"):
        ner.add_label(ent[2])
        break

Untuk data train akan kita random terlebih dahulu baru kemudian kita masukkan ke iterasi training, pada contoh ini kita coba untuk latih model sebanyak 30 kali perulangan.

# TRAINING THE MODEL
with nlp.disable_pipes(*unaffected_pipes):

  # Training for 30 iterations
  for iteration in range(30):

    # shuufling examples  before every iteration
    random.shuffle(ner_spacy_fmt_datasets)
    losses = {}
    # batch up the examples using spaCy's minibatch
    batches = minibatch(ner_spacy_fmt_datasets, size=compounding(4.0, 32.0, 1.001))
    for batch in batches:
        texts, annotations = zip(*batch)
        nlp.update(
                    texts,  # batch of texts
                    annotations,  # batch of annotations
                    drop=0.5,  # dropout - make it harder to memorise data
                    losses=losses,
                )
    
    print("Losses at iteration {}".format(iteration), losses)

Setelah proses pembelajaran model selesai, untuk mencoba model yang telah kita latih dapat menggunakan kode berikut.

# test 
doc = nlp("SELUBUNG yang menyelimuti kasus penembakan yang menewaskan Pendeta Yeremia Zanambani di Kabupaten Intan Jaya, Papua kian terkuak. Hasil investigasi Tim Gabungan Pencari Fakta (TGPF) kasus tersebut menyatakan bahwa penembakan di Intan Jaya diduga dilakukan oleh aparat keamanan.")

print(doc.ents)
print("Entities", [(ent.text, ent.label_) for ent in doc.ents])

# output 
"""
(SELUBUNG, Pendeta Yeremia Zanambani, Kabupaten Intan, Gabungan Pencari Fakta (TGPF, Intan Jaya)
Entities [('SELUBUNG', 'ORGANIZATION'), ('Pendeta Yeremia Zanambani', 'PERSON'), ('Kabupaten Intan', 'LOCATION'), ('Gabungan Pencari Fakta (TGPF', 'ORGANIZATION'), ('Intan Jaya', 'LOCATION')]
"""

Nah, agar supaya nantinya tidak melulu melatih ketika akan digunakan, model yang sudah kita latih dapat kita simpan untuk digunakan di waktu yang akan datang.

from pathlib import Path

output_dir = Path('nlp_id_checkpoint_2020_10_26')
nlp.to_disk(output_dir)
print("Saved model to", output_dir)

Apabila kita telah memiliki model yang telah kita latih sebelumnya, untuk memuat ulang dan menggunakan model tersebut dapat dengan cara seperti di bawah ini

# load existing model 
output_dir = 'nlp_id_checkpoint_2020_10_26'
print("Loading from", output_dir)
nlp_updated = spacy.load(output_dir)

Pengujian dengan model yang kita muat ulang

# contoh 1
doc = nlp_updated("Kementerian Perhubungan tidak mewajibkan rapid test COVID-19 untuk perjalanan darat lintas daerah, kecuali untuk tujuan Bali. Termasuk, dalam periode cuti bersama." )

print("Entities", [(ent.text, ent.label_) for ent in doc.ents])

displacy.render(doc, style="ent")

# contoh 2
doc = nlp_updated("Empat saksi terkait korupsi proyek infrastruktur fiktif yang dikerjakan PT Waskita Karya (Persero) Tbk absen dari panggilan KPK hari ini. Seorang di antaranya mantan Bupati Wakatobi, Hugua." )
print("Entities", [(ent.text, ent.label_) for ent in doc.ents])

displacy.render(doc, style="ent")

Dengan demikian kita sudah memiliki sebuah model yang dapat digunakan untuk melakukan tugas Ekstraksi dan Pengenalan Entitas khusus untuk teks berbahasa Indonesia.

Kira-kira begitu saja ya… biar tidak kebanyakan seperti akan menulis karya tulis atau tugas akhir perkuliahan, untuk kode, “notebook” interaktif dan dataset bisa dilihat kembali secara utuh di gudang kode berbasis kontrol versi di pranala berikut ini: https://github.com/yudanta/indonesia-ner-spacy

Untuk tulisan versi bahasa Inggris mungkin besok ya ada, tapi tidak janji lhooo yaaa…

comments powered by Disqus