딥러닝 공부하기

[딥러닝] 개체명 인식 NER 초간단 실습

차근차근 디지털 2023. 12. 21. 01:41

개체명 인식(Named Entity Recognition, NER) 이란?

자연어 처리(NLP) 분야에서 텍스트에서 중요한 개체명을 인식하고 분류하는 전처리 작업

 개체명의 예로는, 사람, 장소, 조직명 등이 있습니다.

중요한 개체명을 파악하고 인식해야 문맥에 맞는 올바른 답을 내릴 수 있기 때문에 개체명 인식 작업은 중요합니다.

 

► NER의 특성은?

각 형태소마다 개체인지 아닌지, 어떤 개체명을 가졌는지를 반환해야하기 때문에 model을 만들 때, return_sequence = True 를 사용합니다.

 

※ 실습의 간편성과 이해도를 위해 3개의 문장으로 실습하였습니다. 학습 성능이 좋은게 이상합니다. 저는 단지 NER을 실습해보기 위해 글을 썼지 모델 성능이 목적이 아닙니다. 언어 모델은 기본적으로 정말 정말 정말 대량의 데이터를 가지고 학습합니다. 언어 모델의 성능은 데이터양과 질, 모델의 종류, 하이퍼파라미터 등에 좌우됩니다. 

 

 

1. 관련 라이브러리 불러오기

import re
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM, Bidirectional, Embedding, Activation
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from sklearn.model_selection import train_test_split
import numpy as np

 

2. 데이터 input과 label 로 나누기

tagged_sentences = [[('eu', 'B-ORG'),
  ('rejects', 'O'),
  ('german', 'B-MISC'),
  ('call', 'O'),
  ('to', 'O'),
  ('boycott', 'O'),
  ('british', 'B-MISC'),
  ('lamb', 'O'),
  ('.', 'O')],
 [('peter', 'B-PER'), ('blackburn', 'I-PER')],
 [('brussels', 'B-LOC'), ('1996-08-22', 'O')]]

inputs, labels =[], [] 
for pairs in tagged_sentences:
    words, tags = zip(*pairs)
    inputs.append(list(words))
    labels.append(list(tags))

print(inputs[0])
print(labels[0])

 

3. 토큰화하고 사전 만들기

MAX_WORDS = 100

entity_tokenizer = Tokenizer(num_words=MAX_WORDS, oov_token='<OOV>')
entity_tokenizer.fit_on_texts(inputs)

tag_tokenizer = Tokenizer()
tag_tokenizer.fit_on_texts(labels)

vocab_size = entity_tokenizer.num_words + 1      
tag_size = len(tag_tokenizer.word_index) + 1       

print('단어 집합의 크기 : {}'.format(vocab_size))
print('개체명 태깅 정보 집합의 크기 : {}'.format(tag_size))
print(entity_tokenizer.word_index)
print(tag_tokenizer.word_index)

 

4. 사전을 활용해서 문자를 숫자로 바꾸고, 패딩하기

inputs_seq = entity_tokenizer.texts_to_sequences(inputs)
labels_seq = tag_tokenizer.texts_to_sequences(labels)

MAX_LENGTH = 10
inputs_padded = pad_sequences(inputs_seq, maxlen=MAX_LENGTH, padding='post')
labels_padded = pad_sequences(labels_seq, maxlen=MAX_LENGTH, padding='post')

# 문장 길이 시각화로 파악하여 MAX_LENGTH 결정하기
# print('샘플의 최대 길이 : {}'.format(max([len(w) for w in inputs])))
# print('샘플의 평균 길이 : {:4f}'.format(np.mean([len(w) for w in inputs])))
# plt.hist([len(s) for s in inputs], bins=50)
# plt.xlabel('length of samples')
# plt.ylabel('number of samples')
# plt.show()

 

# inputs_onehot = to_categorical(inputs_padded, tag_size)
# labels_onehot = to_categorical(labels_padded, tag_size)
# 원핫 인코딩하면 컴파일할 때 
# model.compile(loss='categorical_crossentropy', optimizer=Adam(0.001), metrics=['accuracy'])
# 안했으면 sparse_categorical_crossentropy

 

5. model 빌딩하고 컴파일하기

model = Sequential()
model.add(Embedding(vocab_size, 128))
model.add(Bidirectional(LSTM(256, return_sequences=True)))
model.add(Dense(tag_size, activation='softmax'))

model.compile(loss='sparse_categorical_crossentropy', optimizer=Adam(0.001), metrics=['accuracy'])
model.summary()

 

6. 모델 학습하기

model.fit(inputs_padded, labels_padded , batch_size=128, epochs=10)

 

7. 테스트 데이터가 있어서 평가할 때

# scores = model.evaluate(X_test_padded, y_test_padded, verbose=0)
# print(f"{model.metrics_names[1]}: {scores[1] * 100}")

 

8. 새로운 데이터 넣고 추론해보기

test_sample = ["EU gave German call to take British people"]
index2word =entity_tokenizer.index_word
index2tag = tag_tokenizer.index_word

test_sample_tokenized = entity_tokenizer.texts_to_sequences(test_sample)
test_sample_padded = pad_sequences(test_sample_tokenized, maxlen=MAX_LENGTH, padding='post')
y_predicted = model.predict(test_sample_padded)
y_pred = y_predicted.argmax(axis=-1)

for i in range(len(test_sample_tokenized)):
    for word, tag in zip([index2word.get(x, '?') for x in test_sample_tokenized[i]], 
                                  [index2tag.get(y, '?') for y in y_pred[i]]):
        if word != '<OOV>' and word != '?' and tag !='?':
            print(f'{word} :  {tag.upper()}')