--33.개체명 인식(NER).ipynb--
Named Entitiy Recognition
개체명 인식(NER) : 문장 내에 포한된 어떤 단어가 인물, 장소, 날짜 등을 의미하는 단어인지 인식하는 것
개체명 인식기 : 딥러닝 모델이나 확률모델 등을 이용해서 문장에서 개체명을 인식하는 프로그램
Beginning, Inside, Outside
BIO 표기 예
"오늘부터 샤닐 길동은 삼성 전자에 근무합니다"
| 토큰 | BIO 태그 |
|---|---|
| 오늘 | B-Date |
| 부터 | O |
| 샤닐 | B-Person |
| 길동 | I-Person |
| 은 | O |
| 삼성 | B-Company |
| 전자 | I-Company |
| 에 | O |
| 근무 | O |
| 합니다 | O |
'''
; 한편, AFC챔피언스리그 E조에 속한 포항 역시 대회 8강 진출이 불투명하다 .
$한편, AFC챔피언스리그 <E조:OG>에 속한 포항 역시 대회 8강 진출이 불투명하다 .
1 한편 NNG O
1 , SP O
2 AFC SL O
2 챔피언스 NNG O
2 리그 NNG O
3 E SL B_OG
3 조 NNG I
3 에 JKB O
4 속하 VV O
4 ㄴ ETM O
5 포항 NNP O
6 역시 MAJ O
7 대회 NNG O
8 8강 NNG O
9 진출 NNG O
9 이 JKS O
10 불투명 NNG O
10 하 VV O
10 다 EC O
11 . SF O
'''
None
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
import tensorflow as tf
from tensorflow import keras
tf.keras.utils.set_random_seed(42)
tf.config.experimental.enable_op_determinism()
from tensorflow.keras import preprocessing
from sklearn.model_selection import train_test_split
base_path = r'/content/drive/MyDrive/dataset/chatbot'
def read_file(file_name) :
sents = []
with open(file_name, 'r', encoding='utf-8') as f :
lines = f.readlines()
for idx, l in enumerate(lines) :
if l[0] == ';' and lines[idx + 1][0] == '' and lines[idx - 1][0] == ';' :
continue
elif l[0] == '\n' :
sents.append(this_sent)
else :
this_sent.append(tuple(l.split()))
return sents
corpus = read_file(os.path.join(base_path, 'train.txt'))
corpus
sentences, tags = [], []
for t in corpus : # 말뭉치에서 문장 하나씩 -> t
sentence, bio_tag = [], []
for w in t : # 문장에서 단어 토큰 하나씩 -> w
sentence.append(w[1]) # 단어
bio_tag.append(w[3]) # BIO 태그
sentences.append(sentence)
tags.append(bio_tag)
len(sentences), len(tags) # 문장의 개수
print(sentences[0])
print(tags[0])
print("샘플 크기 : \n", len(sentences))
print("0번째 샘플 문장 시퀀스 : \n", sentences[0])
print("0번째 샘플 bio 태그 : \n", tags[0])
print("샘플 문장 시퀀스 최대 길이 :", max(len(l) for l in sentences))
print("샘플 문장 시퀀스 평균 길이 :", (sum(map(len, sentences))/len(sentences)))
sent_tokenizer = preprocessing.text.Tokenizer(oov_token='OOV') # 첫번째 인덱스에는 OOV 사용
sent_tokenizer.fit_on_texts(sentences)
tag_tokenizer = preprocessing.text.Tokenizer(lower=False) # 태그정보는 소문자 변환하지 않는다.
tag_tokenizer.fit_on_texts(tags)
sent_tokenizer.word_index
tag_tokenizer.word_index
vocab_size = len(sent_tokenizer.word_index) + 1 # padding 추가 때문에 +1
tag_size = len(tag_tokenizer.word_index) + 1
print("BIO 태그 사전크기 : ", tag_size)
print("단어 사전크기 : ", vocab_size)
tag_tokenizer.index_word
x_train = sent_tokenizer.texts_to_sequences(sentences)
y_train = tag_tokenizer.texts_to_sequences(tags)
print(x_train[0])
print(y_train[0])
index_to_word = sent_tokenizer.index_word
index_to_ner = tag_tokenizer.index_word
index_to_ner
index_to_ner[0] = 'PAD'
index_to_ner
max_len = 40 # 위에서 확인한 시퀀스 길이 평균보다 좀 크게 잡음
x_train = preprocessing.sequence.pad_sequences(x_train, padding='post', maxlen=max_len)
y_train = preprocessing.sequence.pad_sequences(y_train, padding='post', maxlen=max_len)
x_train, x_test, y_train, y_test = \
train_test_split(x_train, y_train, test_size=0.2, random_state=0)
x_train.shape, x_test.shape
y_train[0]
y_test[0]
tag_size
y_train = tf.keras.utils.to_categorical(y_train, num_classes=tag_size)
y_test = tf.keras.utils.to_categorical(y_test, num_classes=tag_size)
y_train.shape
y_train[0]
print("학습 샘플 시퀀스 형상 : ", x_train.shape)
print("학습 샘플 레이블 형상 : ", y_train.shape)
print("테스트 샘플 시퀀스 형상 : ", x_test.shape)
print("테스트 샘플 레이블 형상 : ", y_test.shape)
vocab_size, max_len
"""
개체명 인식 모델
Embedding ---> bi-LSTM --> Dense (8)
(NER 태그 분류)
"""
None
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Embedding, Dense, TimeDistributed, Dropout, Bidirectional
from tensorflow.keras.optimizers import Adam
model = Sequential()
model.add(Embedding(input_dim=vocab_size, output_dim=30, input_length=max_len, mask_zero=True))
model.add(Bidirectional(LSTM(200, return_sequences=True, dropout=0.5, recurrent_dropout=0.25)))
model.add(TimeDistributed(Dense(tag_size, activation='softmax')))
model.compile(loss='categorical_crossentropy', optimizer=Adam(0.01), metrics=['accuracy']) # learning_rate = 0.01
model.fit(x_train, y_train, batch_size=128, epochs=10)
model.evaluate(x_test, y_test)
def sequences_to_tag(sequences): # sequences: 예측한 NER
result = []
for sequence in sequences:
temp = []
for pred in sequence:
pred_index = np.argmax(pred)
temp.append(index_to_ner[pred_index].replace('PAD', 'O'))
result.append(temp)
return result
y_predicted = model.predict(x_test) # (711, 40) -> model -> (711, 40, 8)
pred_tags = sequences_to_tag(y_predicted) # 예측한 NER
test_tags = sequences_to_tag(y_test) # 실제 NER
print(pred_tags[0])
print(test_tags[0])
!pip install seqeval
from seqeval.metrics import f1_score, classification_report
print(classification_report(test_tags, pred_tags))
f1_score(test_tags, pred_tags)
def pred_ner(sentence) :
word_to_index = sent_tokenizer.word_index
new_sentence = sentence.split()
new_x = []
for w in new_sentence :
try :
new_x.append(word_to_index.get(w, 1))
except KeyError :
# 모르는 단어의 경우 OOV
new_x.append(word_to_index['OOV'])
print('입력문장의 시퀀스: ', new_x)
new_padded_seqs = preprocessing.sequence.pad_sequences([new_x], padding='post', value=0, maxlen=max_len)
p = model.predict(np.array([new_padded_seqs[0]]))
p = np.argmax(p, axis=-1) # 예측된 NER 인덱스값 추출
print("{:10} {:5}".format("단어", "예측한 NER"))
print("-" * 50)
for w, pred in zip(new_sentence, p[0]) :
print("{:10} {:5}".format(w, index_to_ner[pred]))
pred_ner("삼성전자 출시한 스마트폰 오늘 애플 도전장 내밀다.")
pred_ner("오늘 10시에 잠실야구장에서 LG와 두산 맞대결한다")