데이터 과학 Data Science/자연어처리 NLP

[NLP] Fasttext 한국어에서 써보고 하이퍼파라미터 튜닝까지!

허니비 honeybee 2023. 1. 17. 17:59

필자는 지금 다음 주 나갈 대회 준비를 위해 여러가지 자연어 분류기 알고리즘들을 공부중이다. 사실 오늘 소개할 fasttext 라는 알고리즘 역시 딥러닝 알고리즘이기 때문에 그 안에 있는 복잡한 수식이나 원리는 강의를 들어도... 뭔 말인지.. 잘 이해가 안 가는 것 같다.. 이론에 대한 내용은 다른 블로거들을 참고하시고! 나는 내가 이해되는 수준에서 쉽고 간단하게 원리를 설명해 보고, 사용했던 모듈들과 소스코드를 집중적으로  공유하겠다

 

 

1. 모델 원리 (안 궁금하면 넘어가셔도 됩니다..) 

 

우선 Fasttext 는 Word2Vec  의 확장판 모델이라고 생각하면 편하다.  Fasttext 를 이해하기 위해 Word2vec 을 먼저 설명하자면, Word2vec 은 주변 단어를 예측하는 과정에서 적절한 단어를 압축하는 방법을 이용한 알고리즘이다. x 라는 어떤 단어가 인코더에 들어가서 벡터화되고 압축된 후(z) , 디코더를 통해 다시 복원할 때 적절한 단어만을 선택하는 방법을 취했다 (사실 내가 써놓고도 무슨말인지 이해가 잘.... ) 

 

 

여튼 이 짓거리를 Word2Vec 은 단어 하나하나를 기준으로 했다면, Fasttext 는 단어를 더 작게 쪼개서 단어 안에 있는 subword 들을 벡터화한다는 차이점이 있다. 

 

예를 들어, Fasttext 는 apple 이란 단어에 글자 단위 파라미터인 n을 3으로 입력하면 다음과 같이 단어를 쪼개고, 쪼개진 단어 + 원본 단어가 함께 벡터화 되어 학습된다. 

 

# n = 3
<ap, app, ppl, ple, le> <apple>

 

따라서 Fasttext 알고리즘은 다음과 같은 장점들이 있다 

 

 

 

- 한국어처럼 조사, 접사, 어간, 어미 등이 발달해 다양한 파생어와 합성어가 있는 언어의 유사성을 판단하는데 유리함 

 

- 사전에 등록이 되어있지 않은 신조어나, 문장 내 출현 빈도가 낮은 단어들에 상대적으로 덜 민감하게 반응한다 (더 잘 학습한다)

 

 

 

 

오늘은 이 알고리즘을 기반으로 분류모델을 만드는 실습을 해보도록 하겠음

 

 

 

 


 

 

2. 문제 정의 

 

우선 지금 해야하는 일은 카페의 메뉴이름들을 학습해서 그 메뉴가 음료인지, 식품인지, 혹은 사이드메뉴나 옵션같은 추가메뉴인지 구분하는 것이다. 샘플 데이터셋은 다음과 같고, 라벨은 총 세 가지, "음료", "식품", "추가" 로 구성되어 있다. 

 

 

현재 가지고 있는 라벨들의 비중은 은 이 정도. 편중이 약간 있고 학습 데이터셋 자체도 아주 작지만 일단 실습을 해보겠다. 

 

 

 

 

 


 

 

 

3. 소스코드 

 

 

- 학습시킬 데이터는 문장 단위가 아니라 단어 단위이기 때문에, 전처리는 생략했다. 만약 메뉴명에.. 빽다방의 "앗!메리카노" 라던가 한국어로 쓴 영어라던가.. 뭐 여타 등등이 있다면 regex 를 이용해서 잘 처리해주도록 하자. 

 

 

 

 

3.1. 필요 라이브러리 import 

pip install fasttext

from sklearn.model_selection import train_test_split
import fasttext

 

 

 

3.2. 학습용 시리즈 만들기 

 

fasttext 모델을 학습시킬때는, 학습할 데이터 앞부분에 __label__ 달아주면 그걸 인덱스로 인식한다. 그래서 __label__ 과 메뉴이름, 가격이 함께 들어가 있는 새로운 시리즈를 하나 만들어주었다 . 

 

df['label_prefix'] = '__label__' + df['sample_label'].astype(str) 
df['label_prefix_menu_price'] = df['label_prefix'].astype(str) + ' ' + df['MENU_FULL_NM'] + ' '+df['price'].astype(str)
df.head(5)

 

 

 

3.3  split 

 

학습용 데이터셋과 테스트 데이터셋을 나눠준다. 그리고 각각의 파일로 저장해주면 된다. (인덱스와 헤더도 불포함시켰다) 앞서 __label__  처리를 했기 때문에 타깃을 지정하는 것이 필요하지 않음 

 

train, test = train_test_split(df, test_size=0.2)

train.to_csv("nlp_fasttext.train", columns=["label_prefix_menu_price"], index=False, header=False)
test.to_csv("nlp_fasttext.test", columns=["label_prefix_menu_price"], index=False, header=False)

 

 

 

3.4. 학습 

 

 

train 과 test 모델을 각각 모듈에 넣어주면 학습이 끝. 이름답게 학습 속도가 무지하게 빠르다. 아무리 데이터가 작다지만 1초만에 학습을 다 했다니? 

 

import fasttext

model = fasttext.train_supervised(input="nlp_fasttext.train")
model.test("nlp_fasttext.test")

 

 

 

그냥 아무것도 안하고 ~ 쌩으로 학습 시켰더니 이런 결과가 나왔다. 첫번째 파라미터는 학습 데이터셋의 사이즈, 두번째와 세 번째는 각각 정확도와 재현율이다. 이제 이걸 보고 모델링 부분을 개선하던가, 전처리를 개선하던가, 데이터를 개선하면서 성능을 올려주면 되는데,.. 나는 귀찮으니 자동 하이퍼파라미터 튜닝을 통해 성능을 올려보겠다. 

 

 

일단 문제가 어떤 부분에서 있는지 알아봤다. 

 

 

일단 model.predict 를 이용해 각각의 데이터를 넣어보며 모델이 데이터를 뭘로 분류했는지를 봤다. 

model.predict("아메리카노")

#(('__label__음료',), array([0.57294673]))

model.predict("말차라떼")

#(('__label__음료',), array([0.69885886]))

model.predict("건포도 스콘")

#(('__label__음료',), array([0.49984229]))

model.predict("휘핑크림 추가")

#(('__label__음료',), array([0.69885886]))

 

음 그래.. 라벨에 편중이 있어서 그런지 음료는 잘 예측하는데 식품과 추가는 예측을 못하고 있다. 

 

 

 

 

다음으로 model.get_nearest_neighbors 를 이용해 제시된 단어와 가장 유사한 아이들을 꼽아봤다 

model.get_nearest_neighbors("아메리카노")

[(0.8332238793373108, '좋은데이'),
 (0.8332054615020752, '청포도에이드'),
 (0.833166241645813, '진로'),
 (0.833151638507843, '잉글리쉬블랙퍼스트'),
 (0.8331462740898132, '쿠앤크버블라떼(only'),
 (0.833143413066864, '피치'),
 (0.8330790400505066, '소주(처음처럼)'),
 (0.8330461382865906, '카프리(병)'),
 (0.8330454230308533, '복숭아이스티'),
 (0.8330187797546387, '흑당버블라떼(only')]
 
 
 model.get_nearest_neighbors("스콘")
 
 [(0.687182605266571, '베이글칩'),
 (0.6865730285644531, '바닐라&초코'),
 (0.6862233281135559, '핑크딸기'),
 (0.6862021088600159, '코코아'),
 (0.6861957311630249, '민트파르페'),
 (0.6860238313674927, '무화과'),
 (0.6857982277870178, '</s>'),
 (0.6857110857963562, '러브마카롱'),
 (0.685520589351654, '비스킷슈'),
 (0.6854957938194275, '허니버터')]

역시 음료보다 식품을 넣었을 때 약간 애매한 단어들을 가져다 주는 것 같다. 

 

 

 

 

3.5 하이퍼파라미터 튜닝 

 

이번에는 저걸 토대로 하이퍼파라미터 튜닝을 해보자. 튜닝을 위해서는 순서대로 train 데이터셋 / validation 데이터셋 / 기준 스코어와 라벨 메트릭을 모델 학습시킬 때 넣어주면 된다. 복잡한 과정 필요 없어서 너무 좋다. 역시 주커버거다

 

 

다시 validation 까지 포함해서 데이터셋을 나눠주고, 저장하고, 튜닝하고  학습을 시켰더니 

#3개로 다시 split
train, test = train_test_split(df, test_size=0.2)
train, validation = train_test_split(train, test_size=0.2)

#저장
train.to_csv("nlp_fasttext.train", columns=["label_prefix_menu_price"], index=False, header=False)
validation.to_csv("nlp_fasttext.validation", columns=["label_prefix_menu_price"], index=False, header=False)
test.to_csv("nlp_fasttext.test", columns=["label_prefix_menu_price"], index=False, header=False)

#튜닝 + 학습
model = fasttext.train_supervised(input='nlp_fasttext.train', autotuneValidationFile='nlp_fasttext.validation', autotuneMetric="f1:__label__음료")

#test
model.test("nlp_fasttext.test") #(100, 0.85, 0.85)

 

두둥 ~ 정확도가 무려 40프로나 향상되었다

 

 

 

고작 100개 학습해서 저 정도의 정확도라니

 

정말 무서운 알고리즘인 것 같다 

 

 

 

모듈을 써서 나온 output 도 성공적 

 

 

 

 

 

참고자료: https://youtu.be/Cq_pbQYO3M8

fasttext 를 계속 공부해 보고 싶었는데, 한국어로 된 괜찮은 참고 자료가 없어서 유튜브에 올라와 있는 어떤 인도인... 의 영상을 보고 공부해서 작성해봤다. 

 

 

썸네일

728x90
반응형