NER (Named Entity Recognition) dengan anaGo (Python Keras)

Update 6 Juni 2018: Anago mengupdate versi packagenya dan tidak compatible dengan versi sebelumnya.  Jika ingin sesuai posting ini,  install dengan versi lama: pip3 install anago==0.0.5.   Saya belum eksplorasi versi anago yang terakhir.

Update: paper yang saya+istri buat tentang ini

Sebelumnya saya sudah membahas NER Bahasa Indonesia dengan Stanford NER.  Kelemahan dari StanfordNER adalah lisensinya  dan menggunakan Java.  Posting ini akan membahas NER dengan Python-Keras dengan library anaGo (https://github.com/Hironsan/anago)  yang menggunakan teknik biLSTM+CRF dan berlisensi MIT yang lebih longgar.  Lib NLTK walaupun menyediakan NER untuk Bahasa Inggris, tetapi tidak menyediakan cara yang mudah untuk men-training bahasa lain.

Untuk training, saya menggunakan  data https://github.com/yohanesgultom/nlp-experiments/blob/master/data/ner/training_data.txt  yang dikonversi ke format BIO. Jadi misalnya

DKI LOCATION
Jakarta LOCATION

perlu diubah menjadi

DKI B-LOC
Jakarta I-LOC

Tambahkan file validasi (saya ambil dari data training, jangan lupa harus diacak terlebih dulu urutannya).

Catatan: penggunaan awalan label “B-” dan “I-” seperti B-LOC, I-LOC, B-PER, I-PER, B-ORG, I-ORG dan B-MISC, I-MISC wajib di anaGo karena di codenya mendeteksi “B-” dan “I-“. Sedangkan untuk bagian belakangnya bebas.

Karena teknik ini dasarnya sequence labeling bisa saja kita manfaatkan untuk task yang lain, misalnya untuk mendeteksi subyek dan obyek sehingga labelnya menjadi B-SUB, I-SUB, B-OBJ, I-OBJ dsb.

Selanjutnya install tensorflow dan keras (catatan saya tentang ini) lalu anago (bisa dengan pip3 anago).

Buat code berikut untuk training membuat model:

import anago
from anago.reader import load_data_and_labels
import os
import numpy as np
import random as rn

namaDir = "/media/yudiwbs/data/"

namaFileTrain = namaDir + "train.txt"
namaFileValid = namaDir + "valid.txt"
namaFileTest = namaDir + "test.txt"

# karena hasil tdk konsisten, random seednya diisi manual
os.environ['PYTHONHASHSEED'] = '0'
np.random.seed(42)
rn.seed(12345)
import tensorflow as tf
from keras import backend as K
tf.set_random_seed(1234)

# atur parameternya disini
model = anago.Sequence(char_emb_size=25, word_emb_size=100, char_lstm_units=25,
              word_lstm_units=100, dropout=0.5, char_feature=True, crf=True,
              batch_size=20, optimizer='adam', learning_rate=0.001,lr_decay=0.9,
              clip_gradients=5.0, max_epoch=30, early_stopping=True, patience=3,train_embeddings=True, max_checkpoints_to_keep=5, log_dir=None)

model.train(x_train, y_train, x_valid, y_valid)

print("\n\nEvaluasi Test:")
model.eval(x_test, y_test)

words = 'Budi Martami kuliah di UPI yang berlokasi di Bandung'.split()
print(model.analyze(words))

words = 'PDIP yang dikawal Megawati menang dalam Pilkada DKI Jakarta'.split()
print(model.analyze(words))

Hasilnya sebagai berikut:


{'entities': [{'score': 1.0, 'beginOffset': 0, 'text': 'Budi Martami', 'type': 'MISC', 'endOffset': 2}, {'score': 1.0, 'beginOffset': 4, 'text': 'UPI', 'type': 'LOC', 'endOffset': 5}, {'score': 1.0, 'beginOffset': 8, 'text': 'Bandung', 'type': 'LOC', 'endOffset': 9}], 'words': ['Budi', 'Martami', 'kuliah', 'di', 'UPI', 'yang', 'berlokasi', 'di', 'Bandung']}

{'entities': [{'score': 1.0, 'beginOffset': 0, 'text': 'PDIP', 'type': 'ORG', 'endOffset': 1}, {'score': 1.0, 'beginOffset': 3, 'text': 'Megawati', 'type': 'PER', 'endOffset': 4}, {'score': 1.0, 'beginOffset': 6, 'text': 'Pilkada DKI', 'type': 'MISC', 'endOffset': 8}, {'score': 1.0, 'beginOffset': 8, 'text': 'Jakarta', 'type': 'LOC', 'endOffset': 9}], 'words': ['PDIP', 'yang', 'dikawal', 'Megawati', 'menang', 'dalam', 'Pilkada', 'DKI', 'Jakarta']}

{'entities': [], 'words': ['Ibu', 'pergi', 'ke', 'pasar']}

“Budi Martami” dideteksi sebagai MISC (harusnya PERSON)
“UPI” dideteksi sebagai LOC (lokasi) (harusnya ORG)
“Bandung” sudah benar dideteksi sebagai LOC

“PDIP” sudah cocok dideteksi ORG
“Megawati”  juga sudah benar sebagai PERSON
“Pilkada DKI”  benar sebagai MISC (DKI bukan sebagai LOC di frasa ini)
“Jakarta” harusnya masuk ke frase “Pilkada DKI Jakarta” sebagai MISC, bukan LOC.

“Ibu pergi ke pasar”  sudah benar karena tidak ada named entity didalamnya.

Data trainingnya memang banyak membahas tentang berita politik sehinggga  lebih akurat untuk kalimat kedua dibandingkan yang pertama. Nanti data trainingnya perlu ditambah lebih banyak lagi.

Hasil terbaik yang saya dapatkan adalah F1 = 0.6837. Masih rendah,  mungkin bisa ditingkatkan dengan menggunakan pretrained Word2Vec atau GloVe

Update: untuk menyimpan model, gunakan:

 model.save(directory) 

parameternya bukan file tapi directory. Sedangkan untuk me-load model:

model = anago.Sequence().load(namaDirModel) 

Update: F1 naik menjadi 0.69-0.70 saat menggunakan Word2Vec dari data Wikipedia Bhs Indonesia (posting tentang w2vec).

Untuk menambahkan word embedding saat training:

import gensim

embeddings =  gensim.models.Word2Vec.load(namaFileW2Vec)
model = anago.Sequence(..param lain.., embeddings=embeddings)

Jangan lupa parameter word_emb_size disamakan dengan model embeddings-nya.

Update: menggunakan glove, F1 naik ke 0.71-0.72 (untuk vector size 50)
Code:

from anago.reader import load_data_and_labels,load_glove
embeddings = load_glove(namaFileVec)
model = anago.Sequence(..param lain.., embeddings=embeddings)

Update: ada bug di package Anago. Parameter patience ternyata di hardcode. Berikut perbaikannnya:

Di file metrics tambahkan parameter patience_nb

function def get_callbacks(log_dir=None, valid=(), tensorboard=True, eary_stopping=True, patience_nb=3):

lalu di code berikut passing parameter ini (tadinya di hardcode 3)

    if eary_stopping:
        callbacks.append(EarlyStopping(monitor='f1', patience=patience_nb, mode='max'))

lalu di trainer tambahkan parameter:

        # Prepare callbacks
        callbacks = get_callbacks(log_dir=self.checkpoint_path,
                                  tensorboard=self.tensorboard,
                              eary_stopping=self.training_config.early_stopping,
                                  patience_nb=self.training_config.patience,
                                  valid=(valid_steps, valid_batches, self.preprocessor))

di trainer juga optimizer adam sepertinya di hardcode.

=== END UPDATE ===

28 tanggapan untuk “NER (Named Entity Recognition) dengan anaGo (Python Keras)”

    1. Saya juga dapat dari mahasiswa istri, perlu minta ijin juga ke dia (karena mungkin ada tambahan/edit). Menurut saya buat saja script kecil untuk mentransformasi format.

  1. mohon maaf mau bertanya pak untuk pembuatan script untuk mentranformasi ke format BIO nya ada ketentuan gt gak pak ? karena saya udah membuat format BIO nya banyak yang ga ke deteksi pak. trims pak sebelumnya

      1. tedeteksi pak hanya saja yang di deteksi tidak sesuai pak (kebanyakan tidak sesuainya pak )

  2. saya coba 1000 data per document training testing dan valid , di train bisa pak tapi hasil f1:0.00 itu datanya yang kurang banyak atau gimana ya?

    1. bisa terlalu sedikit, bisa juga karena salah tag. Coba setidaknya 10rb untuk data training. Data training biasanya jauh lebih besar dari valid dan test. Saya menggunakan 80:10:10 untuk train:test:valid

  3. Selamat malam pak. Saya coba mengguanakan data training dari terjemahan surat al quran berbahasa indonesia. untuk data yang digunakan sekitar 6000 tag. tapi pas di uji. f1 : 0.0 dan masih belum bisa prediksi entitas. Kira kira masalahnya dimana ya pak? Terimakasih

  4. selamat pagi , pak saya sudah ikuti saran bapak tapi f1 tetap 0.0 , bagaimana yah ?

      1. punten pak tanya lagi datanya sudah saya kirim ke bapak apa sudah di cek sebelumnya , saya masih menggunakan versi yang sebelum anago update terakhir kali

      2. Setelah saya lihat, anda mengirimnya cuma file saja ya tanpa keterangan apapun. Itu sih seperti anda datang ke meja saya, lempar dokumen lalu pergi hehe. Saya banyak dapat email dengan attachment, jika emailnya kosong tanpa keterangan apapun ya langsung saya skip saja.

      3. Oh iya pak maaf sebelumnya , mungkin sayakurang memperhatikan :D, untuk update terbaru cara untuk updatenya seperti apa ya pak saya sudah coba install versi baru tapi tidak bisa import anago.utilsnya

  5. Selamat sore pak saya ingin bertanya mengenai pembuatan format BIO Encoding.

    Misalkan saya punya data “Apple Iphone 4S 16Gb”. Dan saya ingin mengcustom sendiri pembuatan NERnya dengan seperti dibawah ini :

    Apple Brand
    Iphone ModelName
    4S ModelName
    16GB Storage

    Diatas saya mencoba referensi disini https://dataturks.com/projects/Mohan/Best%20Buy%20E-commerce%20NER%20dataset

    Untuk mengubah kedalam format BIO Encoding harus di tambah huruf B dan I-nya. Nah yang saya masih agak bingung untuk format datanya tersebut apakah kebawah seperti contoh diatas atau kesamping seperti referensi yang ini https://github.com/yohanesgultom/nlp-experiments/blob/master/data/ner/training_data.txt ?

    Terus saya juga ingin tanya untuk pelabelannya apakah ada yang bisa secara otomatis sistem mendeteksi tanpa menggunakan data training?

    Terima kasih pak sebelumnya

    1. Masalah format BIO, tergantung dari library yang digunakan. Untuk pelabelan otomatis, coba googling tentang unsupervised sequence labeling, tapi itu bakal jadi topik tersendiri.

      1. Berarti jika menggunakan library anaGO format datanya seperti diatas yaa pak tinggal ditambah B dan I kemudian huruf selanjutnya bebas yaa pak?

        terus format penulisannya perkata ditulis kebawah dan diberi satu tab untuk keterangan ner-nya yaa pak?

  6. Pak maaf saya mau tanya lagi, untuk pretrained word2vec atau glove itu apakah datanya sudah dilabelkan atau belum yaa pak? Terus untuk data training, data testing dan data validasi berarti sudah dilabelkan semua dengan format diatas kan yaa pak? dan tinggal dirandom aja posisinya?

    1. namanya pretrained ya sudah jadi vektor (model), data inputnya sudah tidak digunakan lagi. Tapi kalau yang anda maksudkan anda mau buat word2ved/glove model, jawabannya anda tidak perlu melabeli.

      Pertanyaan kedua, training, testing, validasi ini yang mana? ner? Hati-hati jangan dirandom per kata, tapi di random per kalimat. Urutan kata jangan sampai hilang.

  7. Pak maaf mau tanya, untuk mengganti menjdi B-LOC dan I-LOC dll itu berdasarkan apa ya untuk B dan I nya?

    1. B itu token awal (BEGIN) dan I adalah token isi (INSIDE). Jadi kalau ada kata “Presiden Indonesia” maka Presiden B-ORG dan Indonesia I-ORG.

Tinggalkan komentar