본문 바로가기
Study

[NLP] 10. Named Entity Recognition

by Hwanin99 2024. 8. 25.

  • 개체명 인식은 텍스트에서 이름을 가진 개체를 인식하는 기술이다.

1. 개체명 인식 - NLTK

  • nltk 라이브러리에서 제공하는 미리 학습된 개체명 인식 모델을 사용

라이브러리 준비

import nltk

nltk.download('popular')

토큰화 및 품사 태깅

from nltk import word_tokenize,pos_tag,ne_chunk

sentence='James is working at Disney in London'
sentence=pos_tag(word_tokenize(sentence))
print(sentence)
-----------------------------------------------
[('James', 'NNP'), ('is', 'VBZ'), ('working', 'VBG'), ('at', 'IN'), ('Disney', 'NNP'), ('in', 'IN'), ('London', 'NNP')]

개체명 인식

sentence=ne_chunk(sentence)

print(sentence)
---------------------------
(S
  (PERSON James/NNP)
  is/VBZ
  working/VBG
  at/IN
  (ORGANIZATION Disney/NNP)
  in/IN
  (GPE London/NNP))

2. 개체명 인식 - LSTM

  • 사용자가 제공되고 있는 개체명 인식 모델과는 다른 개체명을 정의해 사용하는 것이 필요할 수 있다.
  • 직접 개체명 인식 모델을 구성해 학습하고 사용할 수 있다.

라이브러리 준비

import numpy as np
import urllib.request

from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.utils import to_categorical
from sklearn.model_selection import train_test_split

데이터 준비

  • 공개된 개체명 인식 데이터셋을 사용한다.
  • 해당 데이터는 '단어 - 개체명' 형식으로 이뤄져 있으므로 이를 가공해 데이터셋을 생성해야 한다.
tagged_sentences=[]
sentence=[]

with urllib.request.urlopen('https://raw.githubusercontent.com/Franck-Dernoncourt/NeuroNER/master/neuroner/data/conll2003/en/train.txt') as f:
  for line in f:
    line=line.decode('utf-8')
    if len(line)==0 or line.startswith('-DOCSTART') or line[0]=='\n':
      if len(sentence)>0:
        tagged_sentences.append(sentence)
        sentence=[]
      continue
    splits=line.strip().split(' ')
    word=splits[0].lower()
    sentence.append([word,splits[-1]])

print(len(tagged_sentences))
print(tagged_sentences[0])
--------------------------------------------------------------------
14041
[['eu', 'B-ORG'], ['rejects', 'O'], ['german', 'B-MISC'], ['call', 'O'], ['to', 'O'], ['boycott', 'O'], ['british', 'B-MISC'], ['lamb', 'O'], ['.', 'O']]

데이터 전처리

  • 단어와 개체명 태그를 분리해서 데이터를 구성한다.
sentences,ner_tags=[],[]

for tagged_sentences in tagged_sentences:
  sentence,tag_info=zip(*tagged_sentences)
  sentences.append(list(sentence))
  ner_tags.append(list(tag_info))

 

  • 정제 및 빈도수가 높은 상위 단어들만 추출하기 위해 토큰화 작업을 한다.
max_words=4000
src_tokenizer=Tokenizer(num_words=max_words,oov_token='OOV')
src_tokenizer.fit_on_texts(sentence)

tar_tokenizer=Tokenizer()
tar_tokenizer.fit_on_texts(ner_tags)
vocab_size=max_words
tag_size=len(tar_tokenizer.word_index)+1

print(vocab_size)
print(tag_size)
----------------------------------------
4000
10

 

  • 데이터를 학습에 활용하기 위해 texts_to_sequences()를 통해 데이터를 배열로 변환한다.
x_train=src_tokenizer.texts_to_sequences(sentences)
y_train=tar_tokenizer.texts_to_sequences(ner_tags)

 

  • 학습에 투입할 때는 동일한 길이를 가져야 하므로, 지정해둔 최대 길이에 맞춰 모든 데이터를 동일한 길이로 맞춰준다.
max_len=70
x_train=pad_sequences(x_train,padding='post',maxlen=max_len)
y_train=pad_sequences(y_train,padding='post',maxlen=max_len)

 

  • Trian/Test로 data split 및 one-hot 인코딩을 한다.
x_train,x_test,y_train,y_test=train_test_split(x_train,y_train,test_size=.2,random_state=111)

y_train=to_categorical(y_train,num_classes=tag_size)
y_test=to_categorical(y_test,num_classes=tag_size)

 

  • 최종적으로 생성된 데이터셋의 shape은 다음과 같다.
print(x_train.shape)
print(y_train.shape)
print(x_test.shape)
print(y_test.shape)
-------------------
(8985, 70)
(8985, 70, 10)
(2247, 70)
(2247, 70, 10)

모델 구축 및 학습

from keras.models import Sequential
from keras.layers import Dense,Embedding,LSTM,Bidirectional,TimeDistributed
from keras.optimizers import Adam

model=Sequential()

model.add(Embedding(input_dim=vocab_size,output_dim=128,input_length=max_len,mask_zero=True))
model.add(Bidirectional(LSTM(256,return_sequences=True)))
model.add(TimeDistributed(Dense(tag_size,activation='softmax')))

model.summary()
----------------------------------------------------------------
Model: "sequential_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 embedding_1 (Embedding)     (None, 70, 128)           512000    
                                                                 
 bidirectional_1 (Bidirectio  (None, 70, 512)          788480    
 nal)                                                            
                                                                 
 time_distributed_1 (TimeDis  (None, 70, 10)           5130      
 tributed)                                                       
                                                                 
=================================================================
Total params: 1,305,610
Trainable params: 1,305,610
Non-trainable params: 0
_________________________________________________________________

 

model.compile(loss='categorical_crossentropy',
              optimizer=Adam(0.001),
              metrics=['accuracy'])
model.fit(x_train,y_train,batch_size=128,epochs=3,validation_data=(x_test,y_test))
model.evaluate(x_test,y_test)
-----------------------------
[0.1538139134645462, 0.8327368497848511]

학습한 모델을 통한 예측

  • 예측을 확인하기 위해서 인덱스를 단어로 변환해줄 사전이 필요하다.
idx2word=src_tokenizer.index_word
idx2ner=tar_tokenizer.index_word
idx2ner[0]='PAD'

 

  • 예측 시각화
i=10
y_predicted=model.predict(np.array([x_test[i]]))
y_predicted=np.argmax(y_predicted,axis=-1)
true=np.argmax(y_test[i],-1)

print('{:15}|{:5}|{}'.format('단어','실제값','예측값'))
print('-'*34)

for w,t,pred in zip(x_test[i],true,y_predicted[0]):
  if w!=0:
    print('{:17}:{:7}{}'.format(idx2word[w],idx2ner[t].upper(),idx2ner[pred].upper()))
--------------------------------------------------------------------------------------
단어             |실제값  |예측값
----------------------------------
OOV              :O      O
OOV              :B-LOC  O
OOV              :O      O
OOV              :O      O
OOV              :O      O

'Study' 카테고리의 다른 글

[Computer Vision] 이미지 픽셀 값 조정  (0) 2024.08.29
[Computer Vision] 학습 데이터 디렉토리 클래스 구축  (0) 2024.08.28
[NLP] 9. Sentiment Analysis  (4) 2024.08.25
[NLP] 8. 스팸 메일 분류  (0) 2024.08.25
[NLP] 7. Embedding  (0) 2024.08.21