RoBERTa: 자기 지도 NLP 시스템 사전학습을 위한 최적화된 방법
사전 학습된 언어 모델은 자연어 처리(NLP)에서 놀라운 성공을 거두었으며, 지도 학습에서 사전 학습 후 파인튜닝으로 이어지는 패러다임 전환을 이끌었습니다. 언어 모델링의 목표는 주석이 없는 텍스트의 이력이 주어졌을 때 시퀀스의 다음 토큰을 예측하는 것입니다. 여기서 주석이 없는 텍스트란 특정 정보로 라벨링되지 않은 대량의 원시 텍스트 데이터를 의미합니다. BERT(Bidirectional Encoder Representations from Transformers)는 한 번에 하나의 단어만 보는 것이 아니라 전체 문장을 살펴봄으로써 양방향 언어 이해를 가능하게 하여 이 개념의 새로운 기준을 세웠습니다. 의미를 더 잘 이해하기 위해 앞뒤 단어를 함께 고려합니다.
그러나 BERT에서 발견된 주요 문제는 학습 부족이었으며, 이는 NLP 작업에서의 성능을 제한했습니다. 이러한 한계로 인해 연구자들은 RoBERTa와 같은 후속 모델에서 개선된 학습 방법론을 탐구하게 되었습니다.
RoBERTa(A Robustly Optimized BERT Pretraining Approach)는 BERT의 한계를 해결하도록 설계된 개선 버전입니다. RoBERTa는 다양한 NLP 문제 전반에서 성능을 향상시키는 몇 가지 핵심 개선 사항을 도입했습니다.
이 글에서는 RoBERTa 개발에 사용된 방법론과 기술, 학습 접근 방식, 달성한 결과, 그리고 그것이 열어 주는 잠재적인 향후 연구 기회에 대해 논의합니다.
BERT(Bidirectional Encoder Representations from Transformers) 간단 소개
RoBERTa를 이해하려면 그 전신인 BERT와 BERT가 직면했던 과제를 이해하는 것이 중요합니다. BERT는 양방향 트랜스포머의 개념을 도입하여 딥러닝 모델이 이전에는 달성할 수 없었던 방식으로 문맥을 이해할 수 있게 했습니다.
하지만 BERT가 실제로 어떻게 작동하는지 이해하려면 이를 자세히 살펴볼 필요가 있습니다. 따라서 그 아키텍처와 학습 방식에 대해 더 자세히 알아보겠습니다.
BERT의 아키텍처와 사전 학습 과정
BERT의 아키텍처는 Devlin et al. in 2018이 소개한 트랜스포머를 기반으로 합니다. 문장에서 서로 다른 단어들의 중요도를 가중하기 위해 다층 양방향(왼쪽에서 오른쪽 및 오른쪽에서 왼쪽) 인코더를 사용합니다.
BERT 아키텍처
그림 1: BERT 아키텍처
단어는 임베딩 레이어의 임베딩 표현에서 시작합니다. 임베딩은 고차원 공간에서 단어 또는 토큰의 밀집 벡터(수치 표현)입니다. 임베딩 레이어는 입력 단어를 이러한 밀집 벡터로 변환하는 신경망의 첫 번째 레이어입니다. 모든 레이어는 이전 레이어의 단어 표현에 대해 멀티헤드 어텐션 계산을 수행하여 새로운 중간 표현을 생성합니다. 이러한 모든 중간 표현은 동일한 크기입니다. 위 그림에서 E1은 임베딩 표현, T1은 최종 출력, Trm은 토큰의 중간 표현입니다.
BERT의 사전 학습 과정에는 두 가지 주요 작업이 포함됩니다:
마스크드 언어 모델링(MLM)
다음 문장 예측(NSP)
언어 모델링은 단어의 시퀀스가 주어졌을 때 다음 단어를 예측합니다. 그러나 MLM 작업에서는 모든 다음 토큰을 예측하는 대신 입력 토큰의 일정 비율을 무작위로 마스킹하고, 마스킹된 토큰만 예측합니다. 반면 NSP 작업은 주어진 두 문장이 원문에서 서로 이어지는지를 예측하는 이진 분류 작업입니다. 이 접근 방식은 모델이 문장 간의 관계를 이해하는 데 도움이 됩니다.
Next Sentence Prediction
그림 2: Next Sentence Prediction
BERT의 사전 학습 과정에는 잠재력을 최대한 발휘하지 못하게 하는 한계가 있었습니다. 주요 문제 중 하나는 MLM 작업에서 정적 마스킹 패턴을 사용한 것이었습니다. BERT에서는 훈련 중 한 번 단어가 마스킹되면 모든 반복에서 동일한 단어들이 마스킹되었습니다. 이러한 정적 접근 방식은 모델이 다양한 마스킹 시나리오를 접하는 것을 제한하여 일반화 능력을 감소시켰습니다. 또한 훈련 데이터의 크기와 기간도 제한적이었습니다.
이러한 한계로 인해 BERT는 법률 또는 의료 텍스트 분석과 같은 도메인 특화 작업, 그리고 감정 분석이나 다단계 추론과 같은 복잡한 언어 이해 시나리오에서 성능이 일관되지 않았습니다. 이러한 단점을 해결하려면 추가적인 연구와 개발이 필요합니다.
RoBERTa의 등장
RoBERTa 개발의 주요 동기는 BERT의 사전 학습 과정에서 관찰된 문제를 해결하는 것이었습니다. 그러나 RoBERTa는 24개 레이어, 1024개의 은닉 유닛, 16개의 어텐션 헤드로 구성되어 총 3억 5,500만 개의 파라미터를 갖는 Bert Large와 동일한 핵심 아키텍처를 유지합니다.
RoBERTa가 해결하고자 한 과제는 다음과 같습니다.
정적 마스킹 패턴: BERT의 정적 마스킹은 제한된 훈련 시나리오로 이어져 과적합을 유발했습니다. 과적합은 모델이 훈련 데이터에서는 매우 잘 수행하지만 테스트 데이터(새 데이터)에서는 성능이 저하될 때 발생합니다. RoBERTa는 동적 마스킹을 도입하여 각 epoch마다 새로운 마스크를 생성하고 새 데이터를 처리하는 능력을 향상시켰습니다.
불충분한 훈련 데이터: BERT의 훈련은 약 16 GB의 텍스트 데이터셋을 기반으로 했으며, 이는 언어의 전체 다양성을 포착하기에 충분하지 않습니다. RoBERTa는 다양한 출처에서 가져온 160 GB 이상의 텍스트를 사용하여 훈련 데이터를 확장했습니다.
짧은 훈련 기간: 원래 BERT 모델은 비교적 짧은 기간 동안 훈련되었지만, RoBERTa는 훈련 기간을 크게 연장하여 더 많은 epoch와 반복을 수행했습니다.
작은 배치 크기와 보수적인 학습률: BERT의 사전 학습은 비교적 작은 배치 크기와 보수적인 학습률을 사용했으며, 이는 안전하지만 제한적인 접근 방식이었습니다. 반면 RoBERTa는 더 큰 배치 크기와 더 공격적인 학습률을 실험했으며, 이는 더 나은 모델 성능으로 이어졌습니다.
RoBERTa란 무엇이며 어떻게 작동하나요?
RoBERTa(A Robustly Optimized BERT Pretraining Approach)는 성능 향상을 위해 BERT의 한계를 해결하도록 설계된 개선된 버전입니다. 이는 자연어 처리 작업에서 성능을 향상시키는 BERT의 훈련 과정에 대한 수정 사항을 도입했습니다.
이러한 수정 사항은 다음과 같습니다.
동적 마스킹
NSP 제거
더 큰 훈련 데이터와 연장된 기간
배치 크기 증가
바이트 텍스트 인코딩
동적 마스킹
BERT의 아키텍처에서, 사전 학습 중 BERT는 마스킹된 토큰의 일정 비율을 예측하려고 시도함으로써 Masked Language Modeling(MLM)을 수행했다는 것을 기억할 수 있습니다. 이렇게 선택된 마스킹 토큰은 각 반복에서 동일(정적)이었습니다.
RoBERTa는 동적 마스킹(dynamic masking)이라는 약간 더 나은 접근 방식을 구현하여 이 문제를 해결했습니다. 이 접근 방식은 학습 중 시퀀스가 BERT에 전달될 때마다 마스킹 방식을 변경합니다. 이러한 변화는 학습 데이터를 더 다양하게 만들어 모델이 더 넓은 범위의 언어 패턴을 학습할 수 있게 했습니다. 그 결과 RoBERTa는 문맥에 대한 더 깊은 이해를 발전시켰고, 이는 다운스트림 작업에서의 성능을 향상시켰습니다.
정적 마스킹 vs. 동적 마스킹
그림 3: 정적 마스킹 vs. 동적 마스킹
RoBERTa를 개발한 연구자들은 학습 데이터를 10번 복제하여 정적 마스킹 접근 방식도 다시 구현했습니다. 이 복제를 통해 각 시퀀스는 40번의 학습 에폭 동안 열 가지 다른 방식으로 마스킹될 수 있었습니다. 따라서 각 학습 시퀀스는 학습 중 동일한 마스크로 네 번 관찰되었습니다. 정적 마스킹의 재구현을 원래 BERT 모델과 비교했을 때 성능은 유사했습니다. 그러나 동적 마스킹은 정적 마스킹보다 약간 더 나은 성능을 보입니다.
정적 마스킹과 동적 마스킹 간 비교
그림 4: 정적 마스킹과 동적 마스킹 간 비교
다음 문장 예측(NSP)의 제거
RoBERTa의 또 다른 핵심 수정 사항은 NSP 작업의 제거였습니다. NSP에서 모델은 문장 쌍을 입력받고 두 번째 문장이 원문에서 첫 번째 문장 뒤에 이어졌는지 예측해야 했습니다. 그러나 RoBERTa 개발 과정에서 NSP 작업이 다운스트림 작업에서 모델 성능에 최소한으로만 기여한다는 사실이 밝혀졌습니다.
RoBERTa는 FULL-SENTENCES 접근 방식(여러 문서의 시퀀스를 패킹)을 사용합니다. 모델에 대한 각 입력은 하나 이상의 문서에서 연속적으로 샘플링된 전체 문장으로, 최대 시퀀스 길이인 512 토큰까지 패킹됩니다. 이를 통해 모델은 장거리 의존성을 더 효과적으로 학습할 수 있습니다.
NSP가 있는 BERT와 없는 BERT
그림 5: NSP가 있는 BERT와 없는 BERT
또한 연구자들은 언어 모델을 위한 입력 데이터를 형식화하는 네 가지 다른 방법을 살펴보았습니다. 그들은 두 가지 주요 형식인 원래의 SEGMENT-PAIR와 SENTENCE-PAIR를 비교했습니다. 두 형식 모두 NSP를 사용하지만, SENTENCE-PAIR 형식은 쌍 대신 단일 문장을 사용합니다. 연구자들은 단일 문장을 사용하면 텍스트에서 멀리 떨어진 단어들 간의 연결을 이해하는 데 어려움을 겪기 때문에 작업에서 모델 성능이 저하된다는 것을 발견했습니다.
다음으로, 그들은 NSP 방법 없이 모델을 학습시키고 DOC-SENTENCES라는 문서의 텍스트 블록을 사용하여 모델을 테스트했습니다. 대부분의 경우, 이는 원래 BERT보다 더 나은 성능을 보였으며 NSP 손실을 사용하지 않아도 모델 성능에 해를 끼치지 않았습니다.
단일 문서의 텍스트(DOC-SENTENCES) 방법은 여러 문서의 텍스트(FULL-SENTENCES)보다 약간 더 좋았습니다. 그러나 DOC-SENTENCES는 학습 중 서로 다른 배치 크기를 초래했기 때문에, 그들은 나머지 실험에서 FULL-SENTENCES를 사용하기로 결정했습니다. 이 선택은 그들의 결과를 다른 관련 연구의 결과와 비교하기 더 쉽게 만들었습니다.
BOOK CORPUS와 WIKIPEDIA로 사전 학습된 기본 모델의 결과
그림 6: BOOK CORPUS와 WIKIPEDIA로 사전 학습된 기본 모델의 결과
더 큰 학습 데이터와 연장된 학습 기간
아마도 가장 직접적인 최적화이지만 결코 덜 중요하지 않은 것은, 단순히 더 많은 데이터로 더 오랫동안 학습하는 것입니다. BERT는 약 16GB의 텍스트 데이터셋으로 학습되었고, RoBERTa는 160GB가 넘는 텍스트를 사용했습니다.
RoBERTa 데이터셋:
BOOK-CORPUS 및 WIKIPEDIA: BERT와 동일합니다.
CC-NEWS: RoBERTa는 CC-NEWS라는 새로운 데이터셋을 도입했으며, 이는 현대적 언어와 광범위한 주제, 스타일, 관점을 특징으로 하는 대규모 뉴스 데이터셋입니다.
추가 데이터셋: RoBERTa는 위 데이터셋들을 논문에 설명된 세 가지 다른 비공개 데이터셋과 결합하여 다운스트림 작업의 성능을 향상시킵니다.
더 큰 학습 데이터를 사용하고, 학습 기간을 연장하며, 더 많은 에폭과 반복을 실행함으로써 RoBERTa는 원래 보고된 BERT 결과(Devlin et al. in 2018)와 비교해 다운스트림 작업에서 4-6%의 큰 향상을 보입니다.
BERT 대비 RoBERTa 결과
그림 7: BERT 대비 RoBERTa 결과
배치 크기 증가
신경망 기계 번역 분야의 과거 연구는 매우 큰 미니 배치로 학습하면 마스크드 언어 모델링의 퍼플렉시티를 개선할 수 있음을 보여주었습니다. 이 접근 방식은 학습률을 적절히 높였을 때 최종 작업 성능을 향상시킵니다(Ott et al., 2018). 퍼플렉시티는 언어 모델의 성능을 측정하는 데 사용되는 지표입니다. 이는 모델이 시퀀스에서 다음 단어를 예측할 때 가지는 불확실성(확률 분포)을 정량화합니다. 예를 들어, 퍼플렉시티가 낮을수록 예측이 더 정확합니다.
이 개념을 바탕으로 RoBERTa는 학습 중 훨씬 더 큰 미니 배치를 사용합니다. BERT가 백만 스텝 동안 256개 시퀀스의 배치 크기를 사용한 반면, RoBERTa는 이를 학습률 값 1e-3으로 31k 스텝 동안 8k개 시퀀스로 늘렸습니다. 그러나 최적 성능은 125k 스텝 동안 2k개 시퀀스에서 나타났습니다.
BERT와 RoBERTa의 퍼플렉시티 및 최종 작업 성능 비교
그림 8: BERT와 RoBERTa의 퍼플렉시티 및 최종 작업 성능 비교
바이트 텍스트 인코딩
RoBERTa는 또한 바이트 수준 Byte Pair Encoding(BPE)을 사용하여 어휘 외(OOV) 단어를 처리하는 대안적인 방법을 도입했습니다. BERT는 30K 단위의 문자 수준 Byte-Pair Encoding(BPE) 어휘를 사용했습니다. RoBERTa는 추가 입력 전처리나 토큰화 없이 이를 50K 서브워드 단위의 바이트 수준 BPE 어휘로 확장합니다. BERT와 비교해, 이 방법은 RoBERTa가 unknown 토큰을 사용하지 않고 거의 모든 단어나 서브워드를 인코딩할 수 있게 했습니다.
바이트 텍스트 인코딩
그림 9: 바이트 텍스트 인코딩
실험 결과
BERT(Devlin et al. 2019)를 수정한 후, RoBERTa는 NLP 벤치마크에서 테스트되었습니다. 세 가지 벤치마크인 GLUE, SQuAD, RACE에서의 성능을 논의하겠습니다.
GLUE (General Language Understanding Evaluation): NLU(자연어 이해) 작업을 위한 9개 데이터셋 모음입니다.
SQuAD (Stanford Question Answering Dataset): 질의응답 시스템을 학습하고 평가하기 위해 설계된 데이터셋입니다. 이는 Wikipedia 문서 집합에 대해 사람이 제기한 실제 질문들로 구성되며, 각 질문의 답은 해당 문서 내의 특정 텍스트 범위입니다.
RACE (Reading Comprehension from Examinations): 영어 시험에서 수집된 대규모 독해 데이터셋입니다.
GLUE 데이터셋에서의 테스트 결과
RoBERTa는 두 가지 파인튜닝 설정을 사용하여 테스트되었습니다. 첫 번째 설정(단일 작업)에서 RoBERTa는 각 GLUE 작업에 대해 해당 작업의 훈련 데이터만 사용하여 10 에포크 동안 별도로 파인튜닝되었습니다. 이는 9개의 GLUE 작업 모두에서 최첨단 결과를 달성했으며, BERT와 XLNet을 모두 능가했습니다. 두 번째 설정(앙상블)에서 RoBERTa는 GLUE 리더보드에서 88.5를 기록하여 XLNet이 세운 이전 최첨단 성능과 일치했습니다. RoBERTa는 9개의 GLUE 작업 중 4개인 MNLI, QNLI, RTE, STS-B에서 새로운 최첨단 결과를 세웠습니다.
GLUE에서의 결과
그림 10: GLUE에서의 결과
SQuAD
RoBERTa는 SQuAD 훈련 데이터를 사용하여 파인튜닝되었습니다. SQuAD v1.1에서 RoBERTa는 XLNet이 세운 최첨단 성능과 일치합니다. SQuAD v2.0에서 RoBERTa는 89.4의 F1 점수를 달성하여 데이터 증강 없이 단일 모델에 대한 새로운 최첨단 성능을 세웠습니다. 이는 RoBERTa가 SQuAD 특화 기법을 전혀 사용하지 않고 SQuAD 데이터로만 파인튜닝되었다는 점을 고려하면 특히 인상적입니다.
SQuAD에서의 결과
그림 11: SQuAD에서의 결과
RACE 데이터셋에서의 테스트 결과
RACE 는 영어 시험 지문을 기반으로 모델이 객관식 질문에 답해야 하는 까다로운 독해 벤치마크입니다. 이 벤치마크는 모델이 복잡한 지문을 이해하고 문맥을 바탕으로 정답을 추론해야 하기 때문에 특히 어렵습니다. RoBERTa는 RACE 벤치마크에서 83.2%의 정확도를 달성했으며 새로운 최첨단 기준을 세웠습니다.
RACE 테스트 세트에서의 결과
그림 12: RACE 테스트 세트에서의 결과
감성 분석을 위한 RoBERTa 파인튜닝
표준 벤치마크에서의 성능을 넘어, RoBERTa는 감성 분석과 같은 특정 작업을 파인튜닝하는 데에도 매우 효과적인 것으로 입증되었습니다. 파인튜닝은 사전 훈련된 모델을 가져와 더 작은 작업별 데이터셋으로 훈련하여 특정 작업에 맞게 적응시키는 것입니다. 이 기사에서 사용된 소스 코드는 Hugging Face에서 가져온 것입니다.
데이터셋 가져오기 및 준비
Kaggle Competition에서 제공되는 데이터셋을 사용하고 데이터 덤프의 첫 번째 csv 파일인 train.tsv만 참조하겠습니다.
Python 라이브러리 가져오기:
!pip install transformers==3.0.2
이 단계에서 스크립트를 실행하는 데 필요한 라이브러리와 모듈을 가져옵니다.
# Importing the libraries needed
import pandas as pd
import numpy as np
import torch
import transformers
import json
from tqdm import tqdm
from torch.utils.data import Dataset, DataLoader
from transformers import RobertaModel, RobertaTokenizer
import logging
logging.basicConfig(level=logging.ERROR)
# Setting up the device for GPU usage
from torch import cuda
device = 'cuda' if cuda.is_available() else 'cpu'
데이터셋 준비
먼저 데이터셋을 로드하고 Pandas 패키지를 사용하여 읽습니다.
train = pd.read_csv('train.tsv', delimiter='\t')
new_df = train[['Phrase', 'Sentiment']]
몇 가지 변수를 정의해 보겠습니다.
MAX_LEN: 입력 텍스트의 최대 길이(256).
TRAIN_BATCH_SIZE and VALID_BATCH_SIZE: 훈련 및 검증을 위한 배치 크기(각각 8 및 4).
LEARNING_RATE: 모델의 학습률(1e-05).
Tokenizer: RobertaTokenizer 인스턴스는 텍스트 데이터를 전처리하는 데 사용됩니다.
SentimentData 클래스는 사용자 지정 데이터셋 클래스를 구현하며, 데이터 프레임, 토크나이저, 최대 길이를 입력으로 받습니다. 텍스트 데이터를 전처리하여 입력 ID, 어텐션 마스크, 토큰 타입 ID, 대상 감성 레이블을 포함하는 딕셔너리를 반환합니다.
DataLoader 클래스는 학습 및 테스트 세트에 대한 데이터 로더를 생성하며, 학습 및 검증 중에 데이터를 배치 단위로 반복합니다.
# Defining some key variables that will be used later on in the training
MAX_LEN = 256
TRAIN_BATCH_SIZE = 8
VALID_BATCH_SIZE = 4
LEARNING_RATE = 1e-05
tokenizer = RobertaTokenizer.from_pretrained('roberta-base', truncation=True, do_lower_case=True)
class SentimentData(Dataset):
def __init__(self, dataframe, tokenizer, max_len):
self.tokenizer = tokenizer
self.data = dataframe
self.text = dataframe.Phrase
self.targets = self.data.Sentiment
self.max_len = max_len
def __len__(self):
return len(self.text)
def __getitem__(self, index):
text = str(self.text[index])
text = " ".join(text.split())
inputs = self.tokenizer.encode_plus(
text,
None,
add_special_tokens=True,
max_length=self.max_len,
pad_to_max_length=True,
return_token_type_ids=True
)
ids = inputs['input_ids']
mask = inputs['attention_mask']
token_type_ids = inputs["token_type_ids"]
return {
'ids': torch.tensor(ids, dtype=torch.long),
'mask': torch.tensor(mask, dtype=torch.long),
'token_type_ids': torch.tensor(token_type_ids, dtype=torch.long),
'targets': torch.tensor(self.targets[index], dtype=torch.float)
}
train_size = 0.8
train_data=new_df.sample(frac=train_size,random_state=200)
test_data=new_df.drop(train_data.index).reset_index(drop=True)
train_data = train_data.reset_index(drop=True)
print("FULL Dataset: {}".format(new_df.shape))
print("TRAIN Dataset: {}".format(train_data.shape))
print("TEST Dataset: {}".format(test_data.shape))
training_set = SentimentData(train_data, tokenizer, MAX_LEN)
testing_set = SentimentData(test_data, tokenizer, MAX_LEN)
train_params = {'batch_size': TRAIN_BATCH_SIZE,
'shuffle': True,
'num_workers': 0
}
test_params = {'batch_size': VALID_BATCH_SIZE,
'shuffle': True,
'num_workers': 0
}
training_loader = DataLoader(training_set, **train_params)
testing_loader = DataLoader(testing_set, **test_params)
미세 조정을 위한 신경망 생성
RobertaClass를 사용하여 신경망을 생성할 것입니다.
이 네트워크는 Roberta Language model을 포함하고, 그 뒤에 dropout이 이어지며, 마지막으로 최종 출력을 얻기 위한 선형 레이어가 있습니다.
데이터는 데이터셋에 정의된 대로 Roberta Language model에 입력됩니다.
최종 레이어 출력은 모델 예측의 정확도를 결정하기 위해 감성 카테고리와 비교됩니다.
Model이라는 네트워크 인스턴스를 시작할 것입니다. 이 인스턴스는 학습에 사용됩니다.
class RobertaClass(torch.nn.Module):
def __init__(self):
super(RobertaClass, self).__init__()
self.l1 = RobertaModel.from_pretrained("roberta-base")
self.pre_classifier = torch.nn.Linear(768, 768)
self.dropout = torch.nn.Dropout(0.3)
self.classifier = torch.nn.Linear(768, 5)
def forward(self, input_ids, attention_mask, token_type_ids):
output_1 = self.l1(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
hidden_state = output_1[0]
pooler = hidden_state[:, 0]
pooler = self.pre_classifier(pooler)
pooler = torch.nn.ReLU()(pooler)
pooler = self.dropout(pooler)
output = self.classifier(pooler)
return output
model = RobertaClass()
model.to(device)
모델 미세 조정
데이터셋을 로드하고 준비하며 모델을 생성하는 모든 노력을 거친 후입니다. 이 과정에서 아마도 더 쉬운 단계일 것입니다.
여기서는 위의 훈련 데이터셋에서 지정된 수의 에포크 동안 모델을 훈련하는 훈련 함수를 정의합니다. 에포크는 전체 데이터가 네트워크를 몇 번 통과할지를 정의합니다.
# Creating the loss function and optimizer
loss_function = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(params = model.parameters(), lr=LEARNING_RATE)
def calcuate_accuracy(preds, targets):
n_correct = (preds==targets).sum().item()
return n_correct
# Defining the training function on the 80% of the dataset for tuning the distilbert model
def train(epoch):
tr_loss = 0
n_correct = 0
nb_tr_steps = 0
nb_tr_examples = 0
model.train()
for _,data in tqdm(enumerate(training_loader, 0)):
ids = data['ids'].to(device, dtype = torch.long)
mask = data['mask'].to(device, dtype = torch.long)
token_type_ids = data['token_type_ids'].to(device, dtype = torch.long)
targets = data['targets'].to(device, dtype = torch.long)
outputs = model(ids, mask, token_type_ids)
loss = loss_function(outputs, targets)
tr_loss += loss.item()
big_val, big_idx = torch.max(outputs.data, dim=1)
n_correct += calcuate_accuracy(big_idx, targets)
nb_tr_steps += 1
nb_tr_examples+=targets.size(0)
if _%5000==0:
loss_step = tr_loss/nb_tr_steps
accu_step = (n_correct*100)/nb_tr_examples
print(f"Training Loss per 5000 steps: {loss_step}")
print(f"Training Accuracy per 5000 steps: {accu_step}")
optimizer.zero_grad()
loss.backward()
# # When using GPU
optimizer.step()
print(f'The Total Accuracy for Epoch {epoch}: {(n_correct*100)/nb_tr_examples}')
epoch_loss = tr_loss/nb_tr_steps
epoch_accu = (n_correct*100)/nb_tr_examples
print(f"Training Loss Epoch: {epoch_loss}")
print(f"Training Accuracy Epoch: {epoch_accu}")
return
EPOCHS = 1
for epoch in range(EPOCHS):
train(epoch)
출력:
그림 13- 모델 훈련 세부 정보
모델 성능 검증
검증 단계에서는 보지 못한 데이터(테스트 데이터셋)를 모델에 전달합니다. 이 단계는 모델이 보지 못한 데이터에서 얼마나 잘 수행되는지를 결정하며, 이 데이터는 데이터셋 생성 단계에서 분리된 train.tsv의 20%입니다.
검증 단계는 새롭고 보지 못한 데이터에 대한 모델의 일반화 능력을 확인합니다. 모델의 가중치는 업데이트되지 않았습니다. 최종 출력만 실제 값과 비교되며, 이는 이후 모델의 정확도를 계산하는 데 사용됩니다.
valid 함수는 검증 단계를 구현하며, 모델과 테스트 로더를 입력으로 받습니다. 이 함수는 모델을 평가 모드로 설정하고, 올바른 예측과 잘못된 예측을 추적하기 위한 변수를 초기화한 뒤, 테스트 로더를 반복합니다. 각 배치에 대해 손실과 정확도를 계산하고, 5000단계마다 그리고 에포크가 끝날 때 검증 손실과 정확도를 출력합니다. 마지막으로 에포크 정확도를 반환합니다.
def valid(model, testing_loader):
model.eval()
n_correct = 0; n_wrong = 0; total = 0; tr_loss=0; nb_tr_steps=0; nb_tr_examples=0
with torch.no_grad():
for _, data in tqdm(enumerate(testing_loader, 0)):
ids = data['ids'].to(device, dtype = torch.long)
mask = data['mask'].to(device, dtype = torch.long)
token_type_ids = data['token_type_ids'].to(device, dtype=torch.long)
targets = data['targets'].to(device, dtype = torch.long)
outputs = model(ids, mask, token_type_ids).squeeze()
loss = loss_function(outputs, targets)
tr_loss += loss.item()
big_val, big_idx = torch.max(outputs.data, dim=1)
n_correct += calcuate_accuracy(big_idx, targets)
nb_tr_steps += 1
nb_tr_examples+=targets.size(0)
if _%5000==0:
loss_step = tr_loss/nb_tr_steps
accu_step = (n_correct*100)/nb_tr_examples
print(f"Validation Loss per 100 steps: {loss_step}")
print(f"Validation Accuracy per 100 steps: {accu_step}")
epoch_loss = tr_loss/nb_tr_steps
epoch_accu = (n_correct*100)/nb_tr_examples
print(f"Validation Loss Epoch: {epoch_loss}")
print(f"Validation Accuracy Epoch: {epoch_accu}")
return epoch_accu
acc = valid(model, testing_loader)
print("Accuracy on test data = %0.2f%%" % acc)
출력:
그림 14- 모델 검증 세부 정보
보시다시피, 이 모델은 주어진 샘플의 올바른 카테고리를 69.47%의 정확도로 예측하며, 이는 더 많은 학습을 통해 더욱 개선될 수 있습니다.
향후 연구 기회
RoBERTa의 성공은 NLP와 모델 최적화 연구에 새로운 문을 열었습니다. 다음은 몇 가지 잠재적인 연구 기회입니다:
데이터셋 확장
더 큰 데이터셋과 더 긴 학습 시간은 RoBERTa를 개선했습니다. 향후 연구에서는 시간이 지남에 따라 점진적으로 더 큰 데이터셋으로 모델을 학습시키는 동적 데이터셋 확장을 조사할 수 있습니다. 이 접근 방식은 모델이 새로운 데이터로부터 어떻게 학습하는지, 그리고 데이터가 확장됨에 따라 계속 개선될 수 있는지 이해하는 데 도움이 될 수 있습니다.
멀티태스크 파인튜닝
RoBERTa의 성능은 고급 멀티태스크 파인튜닝 절차를 탐구함으로써 더욱 개선될 수 있습니다. 현재는 여러 작업을 동시에 학습하지 않아도 많은 일에 뛰어납니다. 그러나 연구자들은 파인튜닝 단계에서 여러 작업을 통합하는 것이 다양한 벤치마크 전반의 성능을 어떻게 향상시킬 수 있는지 조사할 수 있습니다.
사전 학습 목표 탐구
RoBERTa에서 NSP 목표를 제거하고 정적 마스킹 대신 동적 마스킹 접근 방식을 사용한 것은 언어 모델을 가르치는 더 나은 방법이 있는지에 대한 의문을 제기합니다. 향후 연구는 RoBERTa가 작업을 얼마나 잘 이해하고 완료하는지에 영향을 미치는 다양한 사전 학습 방법을 탐구할 수 있습니다.
결론
RoBERTa 논문은 NLP 분야에 몇 가지 중요한 기여를 합니다. 저자들은 학습 데이터의 마스킹 패턴을 동적으로 변경하고, 더 긴 시퀀스로 학습하며, NSP 작업을 제거하는 것이 다운스트림 작업에서 성능을 향상시킬 수 있음을 발견했습니다.
또한 더 큰 배치 크기와 학습 데이터셋을 사용하는 것도 이러한 개선에 기여합니다. RoBERTa는 GLUE를 위한 멀티태스크 파인튜닝이나 SQuAD를 위한 추가 데이터 없이도 GLUE, RACE, SQuAD에서 최첨단 결과를 달성합니다.
더 나아가, RoBERTa와 BERT 간의 논쟁은 단지 어떤 모델이 더 나은지에 관한 것이 아닙니다. 이는 계산 자원, 모델 성능, 그리고 이러한 모델 배포의 윤리적 함의 사이의 트레이드오프를 이해하는 것에 관한 것입니다. BERT에 비해 RoBERTa가 이룬 발전은 NLP 분야가 결코 정적이지 않으며, 더 발전되고 복잡한 언어 처리 능력으로 나아가는 길을 열어 준다는 것을 보여줍니다.
추가 자료
계속 읽기

Zilliz Cloud Update: Smarter Autoscaling for Cost Savings, Stronger Compliance with Audit Logs, and More
What's new in Zilliz Cloud? Smarter autoscaling with scale-down, audit logs GA, enhanced SSO, and Milvus 2.6 in Private Preview.

Why Context Engineering Is Becoming the Full Stack of AI Agents
Discover how context engineering unifies prompts, RAG, and tools to build smarter, production-ready AI agents powered by Milvus.

Proactive Monitoring for Vector Database: Zilliz Cloud Integrates with Datadog
we're excited to announce Zilliz Cloud's integration with Datadog, enabling comprehensive monitoring and observability for your vectorDB deployments.



