Rabu, 17 Mei 2017

Natural Language Processing - Information Extraction using NLTK

Mengekstraksi pengetahuan dari teks tak terstruktur dengan mengidentifikasi referensi entitas nama serta hubungan antar entitas Artikel ini dibuat untuk memenuhi tugas besar pada mata  kuliah pilihan Topik khusus - Data and Knowledge tahun ajaran 2016/2017. Seperti yang terlihat pada judul, artikel ini akan membahas Information Extraction yang mengekstraksi pengetahuan dari teks tak terstruktur dengan mengidentifikasi referensi entitas nama serta hubungan antar entitas (Named Entity Recognition). Yang kemudin keterhubungan antar entitas tersebut divisualisasikan ke dalam bentuk graf. 

Dalam artikel ini akan menyinggung text mining, karena dalam proses pengekstraksian text tak berstruktur tersebut sistem akan kesulitan jika langsung membaca begitu banyak kata, angka, simbol, serta singkatan. Nah, aktivitas text mining (preprocessing) inilah yang merubah data tak terstruktur menjadi data khusus, proses yang dilalui adalah:

  1. Tokenizing, tahap tokenizing / parsing adalah tahap pemotongan string input berdasarkan tiap kata yang menyusunnya. Selain itu, spasi digunakan untuk memisahkan antar kata tersebut.
  2. Stopwords, Stopword adalah kata-kata yang tidak deskriptif yang dapat dibuang dalam pendekatan bag-of-words. Contoh stopword adalah “yang”, “dan”, “di”, “dari” dan lain – lain. Pada tahapan ini masukkan ke sistem adalah kumpulan kata hasil dari proses tokenizing.
  3. Stemming, Stemming yang dilakukan pada tahapan dalam preprocessing ini adalah mentransformasi kata-kata yang terdapat dalam suatu cerita ke kata-kata akarnya (root word).
Setelah preprocessing selesai, barulah masuk ke Natural Language Processing (NLP) yang dalam bahasa indonesia disebut Pemrosesan Bahasa Alami yang merupakan kecerdasan buatan dan bahasa yang berkaitan dengan interaksi antara komputer dan bahasa alami manusia. Aktivitas yang dilakukan adalah:

  1. Part of speech tagging, sebuah sistem yang memberikan label kata secara otomatis pada suatu kalimat. Misalkan, ada kalimat "saya makan nasi" dan ada label KG=kata ganti, VV=kata kerja, NN=kata benda. Sistem akan menerima input berupa kalimat tersebut, outputnya adalah: saya/KG makan/VV nasi/NN
  2. Chunking, adalah apa yang ingin kita keluarkan dari chunk. Alasan mengapa dilakukan proses chunk adalah ketika sitem tidak hanya mengambil semua yang diinginkan, tetapi juga mengambil beberapa hal yang tidak diinginkan. Kita bisa menambahkan aturan chunker.
  3. Named Entity Recognition, berguna untuk segera mengetahui topik diskusi (dalam hal ini isu dalam artikel). Kita bisa menemukan hampir semua entitas yang bernama, bahkan kita bisa mencari yang spesifik. NLTK dapat mengenali entitas bernama yang umum, bahkan dapat mengenali lokasi, nama, jumlah uang, tanggal, dan lainnya.
And, that's it. Keenam langkah diatas yang akan dieksekusi dalam tugas ini. Disini saya menggunakan python, kenapa? Karena python multiplatform yang dapat dijalankan pada platform Windows, Linux/Unix, dan Mac OS X, dan juga library NLTK sudah tersedia. Langsung kita mulai tutorialnya, yang paling awal tentu kamu harus menyiapkan IDE/toolbox/compiler python dan disini saya menggunakan anaconda, kalian juga bisa menggunakan python



Saya akan menjelaskan secara bertahap, mulai dari preprocessing (tokenizing, stopwords dan stemming) lanjut tagging, chunking, NER dan paling akhir seluruhnya akan disatukan menjadi satu file code siap eksekusi.  Oh ya, kita juga harus menyiapkan data set kita berupa artikel dalam bentuk text. Inilah yang menjadi inputan sistem.


Import package






Preprocessing

  • Tokenizing



from nltk.tokenize import sent_tokenize, word_tokenize
EXAMPLE_TEXT = "Hello Mr. Smith, how are you doing today? The weather is great, and Python is awesome. The sky is pinkish-blue. You shouldn't eat cardboard."
print(sent_tokenize(EXAMPLE_TEXT))
print(word_tokenize(EXAMPLE_TEXT))

process_content()

Output
  • Stopwords



from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize

example_sent = "This is a sample sentence, showing off the stop words filtration."
stop_words = set(stopwords.words('english'))
word_tokens = word_tokenize(example_sent)
filtered_sentence = []
for w in word_tokens:
    if w not in stop_words:
        filtered_sentence.append(w)
print(word_tokens)
print(filtered_sentence)

process_content()

Output:

  • Stemming
from nltk.stem import PorterStemmer
from nltk.tokenize import sent_tokenize, word_tokenize

ps = PorterStemmer()
new_text = "It is important to by very pythonly while you are pythoning with python. All pythoners have pythoned poorly at least once."
tokenized = word_tokenize(new_text)
for w in tokenized:
    print(ps.stem(w))


Output:




Natural Language Processing

  • Part of speech (POS) tagging

import nltk
from nltk.stem import PorterStemmer
from nltk.tokenize import sent_tokenize, word_tokenize

new_text = "It is important to by very pythonly while you are pythoning with python. All pythoners have pythoned poorly at least once."
tokenized = word_tokenize(new_text)

def process_content():
 try:
  for w in tokenized:
   words = nltk.word_tokenize(w)
   tagged = nltk.pos_tag(words)
   print(tagged)
 except Exception as e:
  print(str(e))

process_content()

Output:



  • Chunking
import nltk
from nltk.corpus import state_union
from nltk.tokenize import sent_tokenize, word_tokenize

new_text = "The current President of United States is Donald Trump, replacing Barrack Obama."
tokenized = sent_tokenize(new_text)
def process_content():
    try:
        for i in tokenized:
            words = nltk.word_tokenize(i)
            tagged = nltk.pos_tag(words)
            chunkGram = r"""Chunk: {**+?}"""
            chunkParser = nltk.RegexpParser(chunkGram)
            chunked = chunkParser.parse(tagged)
            
            print(chunked)
            for subtree in chunked.subtrees(filter=lambda t: t.label() == 'Chunk'):
                print(subtree)
            chunked.draw()
    except Exception as e:
        print(str(e))

process_content()

Output:






  • Named Entity Recognition
import re
import nltk

string = "Tolong hubungi Budi dari IBM segera di nomor 021-7365423 atau 0213-73652411 atau melalui email john@ibm.co.id. Thanks,Yayok"

def ie_preprocess(document):
    sentences = nltk.sent_tokenize(document)
    sentences = [nltk.word_tokenize(sent) for sent in sentences]
    sentences = [nltk.pos_tag(sent) for sent in sentences]
    return sentences

def extract_phone_numbers(string):
 r = re.compile(r'(\d{3}[-\.\s]??\d{7}|\d{4}[-\.\s]??\d{7}|\d{3}[-\.\s]??\d{8}|\d{4}[-\.\s]??\d{8})')
 return r.findall(string)
 
def extract_email_addresses(string):
    r = re.compile(r'[\w\.-]+@[\w\.-]+')
    return r.findall(string)

def extract_names(document):
    names = []
    sentences = ie_preprocess(document)
    for tagged_sentence in sentences:
        for chunk in nltk.ne_chunk(tagged_sentence):
            if type(chunk) == nltk.tree.Tree:
                if chunk.label() == 'PERSON':
                    names.append(' '.join([c[0] for c in chunk]))
    return names

def extract_organizations(document):
    organizations = []
    sentences = ie_preprocess(document)
    for tagged_sentence in sentences:
        for chunk in nltk.ne_chunk(tagged_sentence):
            if type(chunk) == nltk.tree.Tree:
                if chunk.label() == 'ORGANIZATION':
                    organizations.append(' '.join([c[0] for c in chunk]))
    return organizations


numbers = extract_phone_numbers(string)
emails = extract_email_addresses(string)
names = extract_names(string)
organizations = extract_organizations(string)
print numbers
print emails
print names
print organizations

process_content()

Output:





Build graph

import networkx as nx
import matplotlib.pyplot as plt

def draw_graph(graph):
 # create directed networkx graph
 G=nx.Graph()
 
 # add edges
 G.add_edges_from(graph)
 # graph_pos = nx.shell_layout(G)
 # graph_pos = nx.spectral_layout(G)
 graph_pos = nx.spring_layout(G)
 # graph_pos = nx.random_layout(G)
 
 # draw nodes, edges and labels
 nx.draw_networkx_nodes(G, graph_pos, node_size=1000, node_color='blue', alpha=0.3)
 
 # we can now added edge thickness and edge color
 nx.draw_networkx_edges(G, graph_pos, width=2, alpha=0.3, edge_color='green')
 nx.draw_networkx_labels(G, graph_pos, font_size=12, font_family='sans-serif')
 
 edge_labels = {('Hambalang','Andi'): 1,('Hambalang','Wafid Muharram'): 1,('Andi','Andi Malarangeng'): 2,('Andi Malarangeng','Wafid Muharram'): 7,('Wafid Muharram', 'Andi'): 2}
 nx.draw_networkx_edge_labels(G, graph_pos, edge_labels=edge_labels)
 
 plt.show()

graph = [
        ('Hambalang','Andi'),('Hambalang','Wafid Muharram'),('Andi','Andi Malarangeng'),('Andi Malarangeng','Wafid Muharram'),('Wafid Muharram','Andi')
    ]
 
draw_graph(graph)

process_content()

Output:


Named Entity Recognition with data set


Topik yang saya angkat untuk tugas ini adalah berita mengenai Antasari Azhar dan Susilo Bambang Yudhoyono. Saya mengambil 10 artikel terkait dengan judul serta tanggal terbit yang berbeda-beda. Bagaimana hasilnya? Yuk, kita liat.



import regex as re
import nltk

def remove_non_ascii(text):
    return ''.join(i for i in text if ord(i) < 128)
    
#preprocessing
def ie_preprocess(document):
    sentences = nltk.sent_tokenize(document)
    sentences = [nltk.word_tokenize(sent) for sent in sentences]
    sentences = [nltk.pos_tag(sent) for sent in sentences]
    return sentences
#pengekstraksian nama dalam artikel
def extract_names(document):
    names = []
    sentences = ie_preprocess(document)
    for tagged_sentence in sentences:
        for chunk in nltk.ne_chunk(tagged_sentence):
            if type(chunk) == nltk.tree.Tree:
                if chunk.label() == 'PERSON':
                    names.append(' '.join([c[0] for c in chunk]))
    return names
entity = []
#Loop untuk read 10 artikel
for i in range(0, 9):
  file = open(str(i) + ".txt", "r")
  string = remove_non_ascii(file.read())
  names = extract_names(string)
  names = list(set(names))
  entity.append(names)

print(entity)

process_content()


disini

Output:

Setelah itu, copy output yang sudah didapat dan paste pada source code build graph. Silahkan lihat source code berikut, dan /*insert here*/ direplace dengan data yg dicopy tadi.


import networkx as nx
import matplotlib.pyplot as plt

def draw_graph(graph, label):
    # create directed networkx graph
    G = nx.Graph()

    # add edges
    G.add_edges_from(graph)
    graph_pos = nx.shell_layout(G)
    #graph_pos = nx.spectral_layout(G)
    #graph_pos = nx.spring_layout(G)
    #graph_pos = nx.random_layout(G)
    # draw nodes, edges and labels
    nx.draw_networkx_nodes(G, graph_pos, node_size=1000, node_color='blue', alpha=0.3)
    # we can now added edge thickness and edge color
    nx.draw_networkx_edges(G, graph_pos, width=2, alpha=0.3, edge_color='green')
    nx.draw_networkx_labels(G, graph_pos, font_size=12, font_family='sans-serif')
    nx.draw_networkx_edge_labels(G, graph_pos, edge_labels=label)
    plt.show() 
    
entity = [[/*insert here*/]]
graph = []
label = {}
temp = {}
for item in entity:
    for i in item:
        for j in item:
            if i != j:
                val = (i, j)
                if val in temp:
                    temp[val] = temp[val] + 1
                    if temp[val] > 1:
                        label[val] = temp[val]
                        graph.append(val)
                else:
                    temp[val] = 1

draw_graph(graph, label)

process_content()

Dan jangan lupa install software module "networkx" yang gunanya memanipulasi graf. Di step terakhir ini saya pakai anaconda promt saja untuk install networkx (hanya perlu mengetik "pip install networkx") dan run file diatas juga pakai anaconda promt (biar bisa dizoom).



Output:

Source code : https://github.com/nayadmk/Information_extraction

Referensi: