버트(BERT) 파인튜닝 간단하게 해보자

 

작년 말에 GluonNLP 0.6버전 개발에 활발하게 참여하였는데, 그중에서 사용자들이 편리하게 사용할만한 부분에 대해 소개하기 위해 글을 써봤다. 다들 버트, 버트 하는데, 어떻게 사용할지 모를 분들에게 도움이 될 것이라 예상해 본다.

이 글은 MXNet-Gluon 기반으로 설명이 된다. 최근 훌륭한 한글 자료가 인터넷에 나왔으니 관심 있으신 분들은 먼저 참고하시길 바란다. 

버트(BERT)

인간은 직접 혹은 간접 경험을 통해 특정 상황이나 문제에 대한 가장 효과적인 해결책을 찾을 수 있는 지혜를 얻을 수 있다. 그러한 이유 때문에 경험을 중요시 여기며 모든 경험을 할수는 간접 경험의 하나인 독서를 강조하곤 한다. NLP 영역에서 인기있는 기술인 언어모델 전이학습은 바로 이러한 컨셉을 적용한 학습전략이다. 언어모델이 어떠한 방식으로 사용될지 확실치 않더라도 미리 양질의 방대한 양의 학습데이터로 미리 학습을 시켜두고 이렇게 학습된 모델을 간단하게 튜닝해 다른 목적으로 활용하는 것이다.

버트(BERT)는 현존하는 가장 강력한 NLP 언어모델로 다양한 NLP테스크에서 가장 좋은 성능을 보여주고 있다.

이 글에서는 버트를 기반으로 대표적인 한글 코퍼스인 네이버 무비리뷰 분류기 성능을 높여보도록 하겠다. 이글에서는 예측 성능에 대한 이야기 보다는 얼마나 간단하게 버트를 한글 코퍼스에 활용할 수 있는지에 대해서 중점적으로 이야기해보겠다. 기존의 버트관련 좋은 글이 많이 나왔기 때문에 버트에 대한 자세한 설명은 해당글을 참고하길 바란다.

데이터 전처리

버트가 기존의 방법론 대비 활용하기 어려운 주요한 이유중에 하나는 아래와 같이 다양한 입력을 받아야되기 때문이다. 대부분 구현체들은 이들에 대한 일반화를 하지 않아 별도의 모형을 구축할때 구현이 까다롭다.

입력 데이터

버트는 입력 문장에 대해서 아래와 같은 작업을 요구한다.

  1. 각 토큰의 Vocabulary 인덱스를 추출해 이를 정해진 길이의 벡터로 생성.
  2. 두 문장 혹은 하나의 문장이 들어올 수 있기 때문에 이들을 구분하기 위한 토큰 타입 벡터 생성
  3. 유효 길이 벡터

토큰 인덱스 : ‘[CLS] is this jack ##son ##ville ? [SEP] no it is not .[SEP]’

토큰 타입: 0 0 0 0 0 0 0 0 1 1 1 1 1 1

유효길이: 14

토큰인덱스는 Token Embedding을 생성하는데 필요하고, 토큰 타입은 Sentence Embedding, 유효길이는 내부적으로 여러 연산을 하는데 필요하다. Positional Embedding은 입력의 길이 정보만 알수 있다면 학습/추론시 자동 생성 가능한 벡터이다.

GluonNLP는 위 작업을 매우 편하게 진행해주는 함수(BERTSentenceTransform)를 제공하고 있다.

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

먼저 적절한 버트 모델을 로딩한다(구글에서 공개한 multilingual 모델을 사용해야 한글 문제에 적용할 수 있다).

bert_base, vocabulary = nlp.model.get_model('bert_12_768_12',
                                             dataset_name='wiki_multilingual_cased',
                                             pretrained=True, ctx=ctx, use_pooler=True,
                                             use_decoder=False, use_classifier=False)
ds = gluon.data.SimpleDataset([['나 보기가 역겨워', '김소월']])
tok = nlp.data.BERTTokenizer(vocab=vocabulary, lower=False)
trans = nlp.data.BERTSentenceTransform(tok, max_seq_length=10)
list(ds.transform(trans)
[(array([    2,  8982,  9356, 47869,  9566,     3,  8935, 22333, 38851,
             3], dtype=int32),
  array(10, dtype=int32),
  array([0, 0, 0, 0, 0, 0, 1, 1, 1, 1], dtype=int32))]

입력에 대한 전처리 부분을 제외하고 우리가 진행해야 될 부분은 무비리뷰에 대한 긍/부정 레이블을 처리하는 것이다. 모든 학습 데이터는 배치단위로 입/출력이 정의되기 때문에 입력 데이터 처리와 레이블 처리를 동시에 배치 출력하기 위해 Dataset 클래스를 정의하면 아래와 같다. Dataset 클래스는 PyTorch의 Dataset과 동일한 형태를 띈다.

class BERTDataset(Dataset):
    def __init__(self, dataset, sent_idx, label_idx, bert_tokenizer, max_len,
                 pad, pair):
        transform = nlp.data.BERTSentenceTransform(
            bert_tokenizer, max_seq_length=max_len, pad=pad, pair=pair)
        sent_dataset = gluon.data.SimpleDataset([[
            i[sent_idx],
        ] for i in dataset])
        self.sentences = sent_dataset.transform(transform)
        self.labels = gluon.data.SimpleDataset(
            [np.array(np.int32(i[label_idx])) for i in dataset])

    def __getitem__(self, i):
        return (self.sentences[i] + (self.labels[i], ))

    def __len__(self):
        return (len(self.labels))

위에서 로딩한 버트 모델은 인코더 부분이다. 인코더 위에 분류기를 붙여야 긍/부정을 학습할 수 있을 것이다. 버트 논문에서는 하나의 문장이 입력될때 분류기는 아래와 같이 구성하는걸 제안하고 있다.

모든 버트 모델은 첫 클래스 레이블을 pooler 라고 네이밍 하고 있고 이 pooler 결과를 받아 FC(fully connected) 레이어 하나를 추가해 간단하게 구성한다.

class BERTClassifier(nn.Block):
    def __init__(self,
                 bert,
                 num_classes=2,
                 dropout=None,
                 prefix=None,
                 params=None):
        super(BERTClassifier, self).__init__(prefix=prefix, params=params)
        self.bert = bert
        with self.name_scope():
            self.classifier = nn.HybridSequential(prefix=prefix)
            if dropout:
                self.classifier.add(nn.Dropout(rate=dropout))
            self.classifier.add(nn.Dense(units=num_classes))

    def forward(self, inputs, token_types, valid_length=None):
        _, pooler_out = self.bert(inputs, token_types, valid_length)
        return self.classifier(pooler_out)                               

나머지 부분은 일반적인 학습 모듈과 크게 다르지 않다. 아래는 학습 로그를 찍어본 것이다.

[Epoch 1 Batch 50/2344] loss=8.4847, lr=0.0000026681, acc=0.556
[Epoch 1 Batch 100/2344] loss=7.6343, lr=0.0000053362, acc=0.612
….
[Epoch 1 Batch 2250/2344] loss=4.5963, lr=0.0000422197, acc=0.805
[Epoch 1 Batch 2300/2344] loss=4.2460, lr=0.0000419234, acc=0.806
Test Acc : 0.84662
[Epoch 2 Batch 50/2344] loss=4.4179, lr=0.0000413664, acc=0.846
[Epoch 2 Batch 100/2344] loss=4.4742, lr=0.0000410702, acc=0.843
….
[Epoch 2 Batch 2200/2344] loss=3.4934, lr=0.0000286265, acc=0.869
[Epoch 2 Batch 2250/2344] loss=3.5244, lr=0.0000283302, acc=0.869
[Epoch 2 Batch 2300/2344] loss=3.1572, lr=0.0000280339, acc=0.869
Test Acc : 0.86312
[Epoch 3 Batch 50/2344] loss=3.4649, lr=0.0000274769, acc=0.885
[Epoch 3 Batch 100/2344] loss=3.4550, lr=0.0000271806, acc=0.885
….
[Epoch 3 Batch 2250/2344] loss=2.8383, lr=0.0000144406, acc=0.901
[Epoch 3 Batch 2300/2344] loss=2.4659, lr=0.0000141443, acc=0.901
Test Acc : 0.86686
[Epoch 4 Batch 50/2344] loss=2.6485, lr=0.0000135873, acc=0.919
[Epoch 4 Batch 100/2344] loss=2.6904, lr=0.0000132911, acc=0.916
….
[Epoch 4 Batch 2250/2344] loss=2.3454, lr=0.0000005511, acc=0.924
[Epoch 4 Batch 2300/2344] loss=2.0258, lr=0.0000002548, acc=0.925
Test Acc : 0.87136

성능

일반적으로 네이버 무비 리뷰 문제로 모델을 생성시 83~85% 정도의 정확도를 보인다고 알려져 있다. 간단하게 4 에폭 파인튜닝 후 최종 성능은 0.871이다. 물론 더 학습할 경우 성능이 오를 가능성이 있으니 좀더 학습해 보는 것도 괜찮을 것이다.

테스크(한국어) 특화 pre-training의 필요성

위 성능은 테스크 특화의 아무런 튜닝을 하지 않은 상황에서 좋은 성능이나, 버트를 쓰지 않아도 달성 가능한 성능(fasttext + LSTM + Attention)으로 고무적인 성능은 아니다. 이런 성능의 주된 이유는 한국어 특화된 버트 모형을 사용하지 않아서이다. 필자의 경험으로 한국어만 학습한 버트 모형을 해당 문제에 적용했을때 이 문제에서 0.90 이상의 정확도를 보임을 경험했고, 학습(pre-training)을 지속할수록 성능이 올라갈 수 있음을 확인했다. 이는 자신의 테스크에 잘 동작하는 버트 모형의 학습(pre-training)이 필요할 수 있음을 시사하고 있고, 아직 공개적인 한국어 버트모델이 나오지 않은 상황에서 더 필요한 작업이라 볼 수 있다.

마치며

이 글에서는 GluonNLP에서 제공하는 버트 모형을 이용해 간단하게 한국어 관련 모델을 학습해 봤다. GluonNLP는 버트를 간단하게 로딩하는 인터페이스를 제공하고 있고, 이들이 요구하는 형태로 데이터를 전처리 하는 API를 제공하고 있어 활용하기 복잡한 버트 모형을 다양한 문제에 간단하게 활용 가능하게 한다. 이곳에서 활용한 전체 코드는 여기에 공개해 두었다.

 

[출처] http://freesearch.pe.kr/archives/4963

 

 

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
공지 오라클 기본 샘플 데이터베이스 졸리운_곰 2014.01.02 86984
공지 [SQL컨셉] 서적 "SQL컨셉"의 샘플 데이타 베이스 SAMPLE DATABASE of ORACLE 가을의 곰을... 2013.02.10 79249
공지 [G_SQL] Sample Database 가을의 곰을... 2012.05.20 95988
27 [DB modeling, DB 모델링] [DB] DB 설계 과정 file 졸리운_곰 2025.03.10 1545
26 [DB modeling, DB 모델링] [DATABASE] 기본키(PK), 외래키(FK) file 졸리운_곰 2025.01.29 1904
25 [DB modeling, DB 모델링] 바쁜 데이터 전문가를 위한 7가지 무료 데이터베이스 다이어그래밍 도구 : 7 free database diagramming tools for busy data folks file 졸리운_곰 2025.01.19 1038
24 [DB modeling, DB 모델링] ERD 다이어그램 그리는 방법 file 졸리운_곰 2024.10.29 1409
23 [데이터베이스 모델링] 정규화와 응집도에 대한 고찰 file 졸리운_곰 2024.08.10 1743
22 [데이터베이스 모델링] [SQL] 데이터베이스 관계(Relationship) 기본 (1:1, 1:N, N:N 테이블 생성 예시 포함) file 졸리운_곰 2024.07.12 2106
21 [데이터베이스 모델링] 테이블정의양식 file 졸리운_곰 2024.02.23 1234
20 [데이터베이스 모델링] DAsP - 물리 데이터 모델링 [논리-물리 모델 변환] file 졸리운_곰 2022.05.07 1898
19 [데이터베이스 모델링] 관계형 데이터 모델링 하향식과 상향식 졸리운_곰 2022.05.07 1823
18 [데이터베이스 모델링] 모델링 IE 표기법 (까치발, 까마귀발, crow-feet) file 졸리운_곰 2022.04.26 2163
17 설문 조사를위한 데이터베이스 설계 file 졸리운_곰 2020.09.05 5733
16 [dbguide] 프로세스모델링 작성 절차 (데이터베이스 시스템분석설계 프로세스 모델링) file 졸리운_곰 2020.07.27 1780
15 [dbguide] 데이터모델링 작성 절차 (데이터베이스 모델링 절차) file 졸리운_곰 2020.07.27 1787
14 데이터 품질진단 절차 및 기법 file 졸리운_곰 2020.07.26 2062
13 [번역] 데이터 구조와 설계 — 튜토리얼 file 졸리운_곰 2020.07.26 1814
12 EA( Enterprise Architecture) 전사 아키텍처 file 졸리운_곰 2020.07.25 1790
11 데이터품질관리지침Ver[1].2.1.pdf file 졸리운_곰 2020.07.25 1846
10 테이블 설계의 기초 졸리운_곰 2020.07.25 2131
9 데이터베이스 디자인의 기초 file 졸리운_곰 2020.07.25 1689
8 Enterprise Data Model for Logistics file 졸리운_곰 2019.06.16 1267
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED