ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • (10) 자연어처리_BERT
    자연어처리 2023. 8. 2. 00:15
    728x90

    1. BERT모델

    • 2018년도 Google의 논문에서 처음 제안된 모델로, Transformer의 인코더 기반의 언어 모델
    • unlabeled data로 부터 pre-train을 진행한 후, 특정 downstream task에 fine-tining를 하는 모델
    • deep bidirectional을 더욱 강조하여 기존의 모델들과의 차별성을 강조
    • 하나의 output layer만을 pre-trained BERT 모델에 추가하여 NLP의 다양한 주요 task(11개)에서 SOTA를달성

    1-1. BERT 모델 개요

    • LM의 pre-training방법은 BERT 이전에도 많이 연구되고 있었고, 실제로 좋은 성능을 내고 있었음
    • 특히 문장 단위의 task에서 두각을 보였는데, 이러한 여구들은 두 문장의 관계를 전체적으로 분석하여 예측하는 것은 목표라 함
    • 문장 뿐만 아니라 토큰 단위의 task (개체명 인식, QA등)에서도 좋은 성능을 보였음
    • downstream 작업에 사전 학습된 벡터 표현을 적용하는 방법 -feature base approach
      • 대표적으로 ELMp
      • pre-trained representiations을 하나의 추가적인 feature로 활용해 사용
        • fine-tunig approach
      • parameters 수는 최소화하고, 모든 pre-trained 파라미터를 조금만 바꿔서 dowmstream 작업을 학습

    일반적으로 language representation을 학습하기 위해 uni-directional 언어 모델을 사용하는데 기존의 위 방법이 사전 학습의 성능을 떨어뜨린다고 주장

    1-2.BERT 모델 구조

    • Pre-training part와 Fine-tuning part로 나눠짐
    • Pre-training 에서는 다양한 pre-training tasks의 unlabeled data를 활용해 파라미터를 설정하고, 이를 바탕으로 학습된 모델은 Fine-tuning에서 downstream task의 labeled data를 이용해 fine-tuning
    • 양방향 Transformer encoder를 여러 층 쌓은 것(multi-later bidirectional Transformer encoder)
      • BERT base: 110M(약 1억1천만) 파라미터
      • BERT large: 340M(약 3억4천만) 파라미터
    • BERT base 모델은 OpenAI의 GPT와의 비교를 위해 파라미터 수를 동일하게 만들어 진행

    1-3. BERT 입/출력

    • 총 3가지의(Token, Segment, Position) Embedding vector를 합쳐서 input으로 사용
    • 모든 input 시퀀스의 첫번째 토큰은 [CLS] 토큰인데, [CLS]토큰과 대응되는 최종 hidden state는 분류 문제를 해결하기 위해 sequence representation들을 함축
    • input 시퀀스는 한 쌍의 문장으로 구성되고, 문장 쌍의 각 문자들을[SEP]토큰으로 분리하고 각 문장이 A문장인지, B문장인지 구분하기 위한 임베딩을 사용
    • Token Embeddings는 WordPiece embedding을 사용
    • input representation은 이러한 대응되는 토큰(segment + token + position)을 전부 더함

    1-4. BERT의 사전 학습

    • MLM: Masked language Modeling
      • input tokens의 일정 비율을 마스킹하고, 마스킹 된 토큰을 예측하는 과정
      • 입력으로 들어온 단어 토큰 중 일부를 [MASK] token으로 바꿔서 학습
      • pre-training과 fine-tuning 사이의 mismatch가 발생([MASK] token이 fine-tuning 과정에서는 나타나지 않기 때문)
      • 위 문제를 해결하기 위해 token에서 추가적인 처리
        • 80%의 경우: token을 [MASK] token으로 바꿈
        • 10%의 경우: token을 random word로 바꿈
        • 10%의 경우: token을 원래 단어 그대로 둠
    • NSP: Next Sentence Prediction
      • 많은 NLP의 downstream task(QA, NLI등) 두 문장 사이의 관계를 이행하는 것이 핵심
      • 문장 A와 B를 선택할 때, 50%는 실제 A의 다음 문장인 B를, 나머지 50%는 랜덤 문장 B를 고름
    • 사전 학습 과정은 많은 데이터를 필요로 하는데, corpus 구축을 위해 BooksCorpus(약 8억개의 단어)와 English Wikipedia(약 25억개의 단어)를 사용
    • Wikipedia는 본문만 사용했고, 리스트, 표, 헤더 등은 무시
    • 긴 인접 시퀀스를 뽑아내기 위해서는 문서 단위의 corpus를 사용하는 것이 문장 단위의 corpus를 사용하는 것 보다 훨씬 유리

    2. BERT 요약

    • ELMo가 pretraining 관점을 제시 햇으면, GPT-1은 transformer 구조에 적용해서 transformer 구조에서 적용해서 ransformer가 pretraining에 효과적이라는 것을 밝히고, BERT가 양방향으로 개선
    • Deep Bidirectional Model을 통해 같은 pretraining 모델로 만든 모든 NLP Task 에서 SOTA를 달성
    • pretraining 모델을 통해 적은 리소스로도 좋은 성능을 낼 수 있음
    • 대신 사전 학습에 많은 시간과 비용이 필요

    3. 간단한 답변 랭킹 모델 만들기

    출처:https://github.com/songys/Chatbot_data

     

    GitHub - songys/Chatbot_data: Chatbot_data_for_Korean

    Chatbot_data_for_Korean. Contribute to songys/Chatbot_data development by creating an account on GitHub.

    github.com

    import urllib.request
    import pandas as pd
     
    urllib.request.urlretrieve('https://raw.githubusercontent.com/songys/Chatbot_data/master/ChatbotData.csv', filename='ChatBotData.csv')
     
    train_dataset = pd.read_csv('ChatBotData.csv')
    print(len(train_dataset))

    train_dataset

    print(train_dataset.isnull().values.any())

    # 데이터셋 중복 제거
    processed_train_dataset = train_dataset.drop_duplicates(['Q']).reset_index(drop=True)
    processed_train_dataset = processed_train_dataset.drop_duplicates(['A']).reset_index(drop=True)
    # 데이터 분포 확인
    # 질문의 최대 길이
    # 질문의 평균길이

    import matplotlib.pyplot as plt

    plt.hist(processed_train_dataset['Q'].str.len(), bins=50)
    plt.xlabel('Length of Q')
    plt.ylabel('Frequency')
    plt.title('Histogram of Q Length')
    plt.show()

    max_length = processed_train_dataset['Q'].str.len().max()
    avg_length = processed_train_dataset['Q'].str.len().mean()

    print("최대 길이:", max_length)
    print("평균 길이:", avg_length)

    # 답변의 최대 길이
    # 답변의 평균 길이

    plt.hist(processed_train_dataset['A'].str.len(), bins=50)
    plt.xlabel('Length of A')
    plt.ylabel('Frequency')
    plt.title('Histogram of A Length')
    plt.show()


    # A열의 최대 길이 및 평균 길이 계산
    max_length = processed_train_dataset['A'].str.len().max()
    avg_length = processed_train_dataset['A'].str.len().mean()

    print("최대 길이:", max_length)
    print("평균 길이:", avg_length)

    import random
     
    question_list = list(train_dataset['Q'])
    answer_list = list(train_dataset['A'])
     
    response_candidates = random.sample(answer_list,500)
     
    response_candidates[:10]

    !pip install kobert-transformers
     
    import torch
    from kobert_transformers import get_kobert_model, get_distilkobert_model
     
    model = get_kobert_model()
     
    model.eval()
     
    input_ids = torch.LongTensor([[31,51,99],[15,5,0]])
    attention_mask = torch.LongTensor([[1,1,1],[1,1,0]])
    token_type_ids = torch.LongTensor([[0,0,1],[0,1,0]])
    output = model(input_ids, attention_mask, token_type_ids)
     

     

    from kobert_transformers import get_tokenizer
     
    tokenizer = get_tokenizer()
    tokenizer.tokenize('[CLS] 한국어 모델을 공유합니다. [SEP]')

    tokenizer.convert_tokens_to_ids(['[CLS]', '▁한국', '어', '▁모델', '을', '▁공유', '합니다', '.', '[SEP]'])

    import numpy as np
    from sklearn.metrics.pairwise import cosine_similarity
     
    def get_cls_token(sentence):
      model.eval()
      tokenized_sent = tokenizer(
          sentence,
          return_tensors = 'pt',
          truncation = True,
          add_special_tokens = True,
          max_length = 128
      )

      input_ids = tokenized_sent['input_ids']
      attention_mask = tokenized_sent['attention_mask']
      token_type_ids = tokenized_sent['token_type_ids']

      with torch.no_grad():
        output = model(input_ids, attention_mask, token_type_ids)

      cls_output = output[1]
      cls_token = cls_output.detach().cpu().numpy()

      return cls_token
     
    def predict(query, candidates):
      candidates_cls = []

      for cand in candidates:
        cand_cls = get_cls_token(cand)
        candidates_cls.append(cand_cls)

      candidates_cls = np.array(candidates_cls).squeeze(axis=1)

      query_cls = get_cls_token(query)
      similarity_list = cosine_similarity(query_cls, candidates_cls)

      target_ids = np.argmax(similarity_list)

      return candidates[target_ids]
     
    query = '너 요즘 바뻐?'
    query_cls_hidden = get_cls_token(query)
    print(query_cls_hidden)
    print(query_cls_hidden.shape)

    sample_query = '너 요즘 바뻐?'
    sample_candidates = ['바쁘면 가라','아니 별로 안바뻐','3인조 여성 그룹','오늘은 여기까지']

    predicted_answer = predict(query, sample_candidates)
    print(f'결과: {predicted_answer}')

     

    댓글

Designed by Tistory.