본문 바로가기

깃허브

bert ner 예제 코드

반응형
!git clone https://github.com/ukairia777/tensorflow-bert-ner.git

위코드의 역활은 데이터셋을 가져오는 코드이다. 

위에있는 코드로https://github.com/ukairia777/tensorflow-bert-ner.git  이라는 url에서 가져온다.

 

%ls

 

실행결과

sample_data/ tensorflow-bert-ner/

이코드는 현재 내가 파일을 건드는 위치를 파악하는 코드이다.

%cd tensorflow-bert-ner/

이코드는tensorflow-bert-ner/ 로 현제 내가 있는 디렉토리의 위치를 바꾸는 코드이다.

 

!pip install transformers
!pip install seqeval

위코드는 모듈을 설치하는 코드이다.

첫번째 transformers는 BERT를 사용하려면 필요한 모듈이다.

아래의 seqeval은 tensorflow를 사용하려면 필요한 코드이다.

최근 코랩 파이썬버전이 업데이트 되면서 설치해야함

 

#(1라이브러리 임포트

 

import glob, re
import numpy as np
import codecs
import os
import json
from tqdm import tqdm
import tensorflow as tf
from seqeval.metrics import f1_score, classification_report
from transformers import shape_list, BertTokenizer, TFBertModel

 

 

#)2데이터 받기

train_dataset = glob.glob('data/train_data/*.txt')
test_dataset = glob.glob('data/validation_data/*.txt')

 

여기서 와일드카드인 '*' 표시가 있다. 이코드는 문자의 갯수나 이름상관없이 모든경우를 의미한다.

한마디로 data/train_data아래에있는 코든 .txt 파일을 가져오는 코드이다.

 

#)3텍스트 파일읽는 함수 정의

def read_ner_data(dataset):

  tagged_sentences = []
  sentence = []

  for file_name in tqdm(dataset):
    f = open(file_name, 'r', encoding='utf-8-sig')
    for line in f:
      if len(line)==0 or line[:2]=="##" or line[0]=="\n":
          if len(sentence) > 0:
              tagged_sentences.append(sentence)
              sentence = []
          continue
      splits = line.split('\t') # 공백을 기준으로 속성을 구분한다.
      splits[-1] = re.sub(r'\n', '', splits[-1]) # 줄바꿈 표시 \n을 제거한다.
      word = splits[0]
      sentence.append([word, splits[-1]]) # 단어와 개체명 태깅만 기록한다.

  return tagged_sentences

 

#)4 텍스트 파일 읽는 코드 실행

train_tagged_sentences = read_ner_data(train_dataset)
test_tagged_sentences = read_ner_data(test_dataset)

 

#)5 텍스트 파일 보기 

train_tagged_sentences[0]

 

 

#6 문장과 라벨을 분리하는 코드

def split_sentence_and_label(tagged_sentences):
  index = 0
  sentences = []
  ner_tags = []

  for tagged_sentence in tqdm(tagged_sentences):
    sentence = []
    ner_tag = []
    for word, label in tagged_sentence:
      if word == '_' or word == '\xad':
        continue
      sentence.append(word)
      ner_tag.append(label)

    assert len(sentence) == len(ner_tag), "Error with input length {} vs {}".format(len(sentence), len(ner_tag))

    sentences.append(sentence)
    ner_tags.append(ner_tag)

  return sentences, ner_tags

 

#)7 문장과 라벨을 분리하는 코드 실행

train_sentences, train_labels = split_sentence_and_label(train_tagged_sentences)
test_sentences, test_labels = split_sentence_and_label(test_tagged_sentences)
print(train_sentences[:3])
print(train_labels[:3])

#)8 겹치는 라벨을 제거하여서 보기

labels = set(tag for train_label in train_labels for tag in train_label)
print(labels)

 

이것이 이중 리스트가 아니라면  set()을 사용하면 된다 그런데 지금은 이중리스트이기 때문에 하나의리스트로 만들어준다음에 set을 하는 것이다.

#)9 라벨을 key:value형태로 만들어주기

tag_to_index = {tag: index for index, tag in enumerate(labels)}
index_to_tag = {index: tag for index, tag in enumerate(labels)}
print(tag_to_index)
print(index_to_tag)

#)10 개채명 태깅의 수 출력

tag_size = len(tag_to_index)
print('개체명 태깅 정보의 개수 :',tag_size)

#)11 BERT 토크나이저 설정

tokenizer = BertTokenizer.from_pretrained("klue/bert-base")

이코드는 한국어를 미리 학습한 모델로 벤치마크에서 만든 모델이다.

 

#)12 한국어를 숫자로 표현하는 함수정의

def convert_examples_to_features(examples, labels, max_seq_len, tokenizer,
                                 pad_token_id_for_segment=0, pad_token_id_for_label=-100):

    cls_token = tokenizer.cls_token
    sep_token = tokenizer.sep_token
    pad_token_id = tokenizer.pad_token_id

    input_ids, attention_masks, token_type_ids, data_labels = [], [], [], []

    for example, label in tqdm(zip(examples, labels), total=len(examples)):
        tokens = []
        labels_ids = []
        for one_word, label_token in zip(example, label):
            subword_tokens = tokenizer.tokenize(one_word)
            tokens.extend(subword_tokens)
            labels_ids.extend([tag_to_index[label_token]]+ [pad_token_id_for_label] * (len(subword_tokens) - 1))

        special_tokens_count = 2
        if len(tokens) > max_seq_len - special_tokens_count:
            tokens = tokens[:(max_seq_len - special_tokens_count)]
            labels_ids = labels_ids[:(max_seq_len - special_tokens_count)]

        tokens += [sep_token]
        labels_ids += [pad_token_id_for_label]

        tokens = [cls_token] + tokens
        labels_ids = [pad_token_id_for_label] + labels_ids


        input_id = tokenizer.convert_tokens_to_ids(tokens)
        attention_mask = [1] * len(input_id)
        padding_count = max_seq_len - len(input_id)
        input_id = input_id + ([pad_token_id] * padding_count)
        attention_mask = attention_mask + ([0] * padding_count)
        token_type_id = [pad_token_id_for_segment] * max_seq_len
        label = labels_ids + ([pad_token_id_for_label] * padding_count)

        assert len(input_id) == max_seq_len, "Error with input length {} vs {}".format(len(input_id), max_seq_len)
        assert len(attention_mask) == max_seq_len, "Error with attention mask length {} vs {}".format(len(attention_mask), max_seq_len)
        assert len(token_type_id) == max_seq_len, "Error with token type length {} vs {}".format(len(token_type_id), max_seq_len)
        assert len(label) == max_seq_len, "Error with labels length {} vs {}".format(len(label), max_seq_len)

        input_ids.append(input_id)
        attention_masks.append(attention_mask)
        token_type_ids.append(token_type_id)
        data_labels.append(label)

    input_ids = np.array(input_ids, dtype=int)
    attention_masks = np.array(attention_masks, dtype=int)
    token_type_ids = np.array(token_type_ids, dtype=int)
    data_labels = np.asarray(data_labels, dtype=np.int32)

    return (input_ids, attention_masks, token_type_ids), data_labels

 

 

#)13 개체명 인식에 사용되는 BERT

class TFBertForTokenClassification(tf.keras.Model):
    def __init__(self, model_name, num_labels):
        super(TFBertForTokenClassification, self).__init__()
        self.bert = TFBertModel.from_pretrained(model_name, from_pt=True)
        self.classifier = tf.keras.layers.Dense(num_labels,
                                                kernel_initializer=tf.keras.initializers.TruncatedNormal(0.02),
                                                name='classifier')

    def call(self, inputs):
        input_ids, attention_mask, token_type_ids = inputs
        outputs = self.bert(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
        all_output = outputs[0]
        prediction = self.classifier(all_output)

        return prediction

#)14 손실함수를 정의 하는 코드

def compute_loss(labels, logits):

  loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(
            from_logits=True, reduction=tf.keras.losses.Reduction.NONE)
  active_loss = tf.reshape(labels, (-1,)) != -100
  reduced_logits = tf.boolean_mask(tf.reshape(logits, (-1, shape_list(logits)[2])), active_loss)
  labels = tf.boolean_mask(tf.reshape(labels, (-1,)), active_loss)

  return loss_fn(labels, reduced_logits)

 

 

#)15 모델 학습하는 코드

model.fit(
    X_train, y_train, epochs=3, batch_size=32,

)

#16) 학습할 데이터

sent1 = '오리온스는 리그 최정상급 포인트가드 김동훈을 앞세우는 빠른 공수전환이 돋보이는 팀이다'
sent2 = '하이신사에 속한 섬들도 위로 솟아 있는데 타인은 살고 있어요'
sent3 = '유원준 연구원은 심심해서 탐앤탐스에서 커피를 마시면서 BERT 기반의 개체명 인식기를 만들었다.'

 

# 17 학습하기

test_samples = [sent1, sent2, sent3]
result_list = ner_prediction(test_samples, max_seq_len=128, tokenizer=tokenizer, lang='ko')
result_list
반응형

'깃허브' 카테고리의 다른 글

bert를 통한 NER 학습 전처리  (0) 2024.04.23