주제
1. LDA(Latent Dirichlet Allocation)
2. 워드 클라우드
3. 자연어 처리
4. 문장 분류 문제
5. 실습
1. LDA(Latent Dirichlet Allocation)
- LDA는 문서에 내재되어 있는 토픽을 찾아가는 알고리즘
- 전체 문서는 여러 문서들의 집합이고, 하나의 문서는 여러 개의 주제로 구성되고, 하나의 주제는 여러 단어들로 구성된다고 가정
- 문서 내에 몇 개의 토픽이 있을지는 사용자가 정의함
- 확률이나 비율의 집합을 분포로 표현한 것 : Dirichlet 분포
- LDA는 문서 표면에 드러나지 않은 숨어있는 토픽의 확률 분포를 가정하고 각 단어를 토픽에 할당하는 분석 방법
1.1 LDA 알고리즘
- 알고리즘을 결정하는 중요한 두 개의 확률값이 존재함 :
- 문서에 어떤 토픽이 들어있는가 P(토픽 t | 문서 d)
- 각 토픽에 어떤 단어가 들어있는가 P(단어 w | 토픽 t)
- P(토픽 t | 문서 d)
- 특정 문서 d에서 토픽 t가 차지하는 비율
- 문서에서 각 토픽이 얼마나 중요한지 나타냄
- P(단어 w | 토픽 t)
- 특정 토픽 t에서 단어 w가 차지하는 비율
- 토픽에 특정 단어가 나타낼 확률
- P(토픽 t | 문서 d, 단어 w)
- 특정 단어가 어떤 문서의 주제에 속할 확률
- 어떤 단어가 문서의 주제와 얼마나 잘 맞는지를 나타냄
- 이 값이 크면 특정 단어가 그 문서의 주제에 매우 밀접한 관련이 있음을 의미
- LDA에서 최종적으로 유추해야하는 값이지만 직접적으로 구하기 어려우니 P(토픽 t | 문서 d) x P(단어 w | 토픽 t)를 한 값과 비례한다고 가정
1.2 알고리즘 적용 과정
- 토픽 개수 K 설정 (사용자가 지정)
- 문서 내 모든 단어에 무작위로 K 토픽 중 하나를 할당함
- 단어 w의 토픽 할당을 결정하기 위해 나머지 단어들의 할당 결과를 활용 P(토픽 t | 문서 d) xP(단어 w | 토픽 t)값을 계산
- 이 값이 제일 커지는 t를 w에 재할당
- 전체 문서의 모든 단어들을 대상으로 연산 진행
- 종료 시점에 도달할 때까지 반복
- 종료 시점 : 더이상의 변화가 없을 때까지 혹은 정해진 업데이트 횟수 도달까지 등
- 최종 결과 분석
- 토픽에 존재하는 단어를 보고 토픽이 의미하는 주제를 사용자가 정의
- 할당된 토픽을 기준으로 문서에 존재하는 토픽을 분석
2. 워드 클라우드
- 텍스트 데이터를 시각적으로 표현한 것
- 문서나 데이터에서 가장 빈번하게 등장하는 단어들을 돋보이게 하는 법
- 자주 등장하는 단어는 더 크고 두드러지게 표시됨

2.1 워드 클라우드 생성 실습
- 데이터는 이전에 사용한 Yelp 리뷰 데이터 사용
- 데이터 전처리는 동일하게 진행
- 모든 토큰을 하나의 리스트에 추가
- WorldCloud를 이용하여 워드 클라우드 생성하고 시각화
from wordcloud import WordCloud
wordcloud = WordCloud(width=800, height=800,
background_color='white',
min_font_size=10,
max_font_size=200).generate(combined_text)
# 시각화
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 8), facecolor=None)
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.tight_layout(pad=0)
plt.show()

- food, service, good, great 등 음식에 대한 긍정적인 키워드가 주를 이루고 있다.
3. 자연어 처리
- 컴퓨터가 인간의 언어를 이해하고 해석하는데 사용되는 분야
- 글을 활용한 문제를 해결하고 향상된 사용자 경험을 제공하고자 함
3.1 자연어 처리의 다양한 문제
- 텍스트 이해(Text Understanding)
- 질의응답(QA)
- 문장 이해(Reading Comprehension)
- 정보 검색(Information Retrieval)
- 텍스트 생성(Text Generation)
- 문장 생성(Text Generation)
- 요약(Text Summarization)
- 번역(Neural Machine Translation)
- 텍스트 분류 및 태깅(Text Classification & Tagging)
- 문장 분류(Text Classification)
- 개체명 인식(NER, Named Entity Recognition)
- 품사 태깅(POS tagging, Part of Speech tagging)
- 텍스트 관계 추출(Text Relation Extraction)
- 문자 관계 추출(Relation Extraction)
3.2 주요 프레임워크
1. Natural Language Tool Kit (NLTK)
- 전통적인 NLP 기법을 구현한 패키지 모음
- 전처리, 딥러닝 이전의 NLP 방법들이 많이 활용됨
2. PyTorch, TorchText
- 딥러닝에 특화된 머신 러닝 오픈 소스 라이브러리
- TorchText는 NLP에 특화된 내부 라이브러리
- (초기 ~ 최신) 딥러닝 모델을 쉽게 구현할 수 있는 인터페이스 제공
3. HuggingFace
- 자연어 처리에 특화된 커뮤니티 기반의 라이브러리
- 다양한 연구 분야(이미지, 음성, 생성 등)의 연구 성과를 공유 & 활용 가능
- 매우 다양한 연구 결과물이 빠르게 업데이트
- 연구 결과물을 공통된 인터페이스로 강제
4. 문장 분류 문제
- 문장 분류(Sentence Classification) : 텍스트 데이터를 활용해 분류 문제를 푸는 것. 정해진 클래스 중 어떤 클래스에 속하는지를 판단
- 텍스트의 의미를 이해하고 구조화된 방식으로 분류를 하는 것이 목표
- 분류 이외의 다른 복잡한 문제에서 문장 분류에 특화된 기술 모델을 사용
- 하위 문제 :
- 감정 분석 (Sentiment Analysis)
- 주제 분류 (Topic Classification)
- 의도 분석 (Intent Detection)
- 언어 감지 (Language Detection) 등
- 복잡한 문제를 풀기 위한 베이스 모델로 사용
- 텍스트 요약
- 텍스트 생성
- 챗봇 등
4.1 딥러닝 모델을 활용한 문장 분류 접근
1. 순환 신경망(Recurrent Neural Network, RNN)
- 사람이 글을 읽고 이해하는 과정을 모방해 모델을 설계
- 단어를 하나씩 입력받고 이전에 이해한 내용을 바탕으로 새로운 정보를 생성
- 모든 단어를 처리하는 과정까지 반복 순환
- 마지막 생성된 정보를 바탕으로 분류 진행
2. 주의 매커니즘(Attention Mechanism)
- 입력으로 받은 텍스트 정보에서 딥러닝 모델이 주의를 집중할 단어를 자동으로 판단
- 집중된 단어를 바탕으로 NLP 문제를 처리
- 단어의 정보를 모델 스스로 판단. 높은 성능
- 분류 문제를 풀기 위해서는 문장 임베딩 값을 활용
- 정리된 문장의 정보를 바탕으로 원하는 타겟 클래스를 예측
- 중요도가 적용된 전반적인 문장의 의미를 생성
4.2 분류 문제를 넘어
- 분류는 가장 기본이 되는 문제
- 학습을 위한 데이터 준비 입장에서도 분류 데이터가 만들기 쉬움
- 따라서 분류 문제로 데이터의 특성을 익힌 모델을 분류보다 복잡한 문제에 적용하는 상황이 많음
- 이를 전이 학습(Transfer Learning) 혹은 파인-튜닝(Fine-Tuning)이라고 함
5. 실습
5.1 LDA 실습
- Yelp 리뷰 데이터 사용
- LDA의 결과 분석에 단어 해석이 사용되므로 단어의 레벨로 토큰을 설정함
- 데이터 전처리 후 LDA 모델 적용
- Document Term Matrix(DTM) 생성
- 문서-단어 행렬로, 전처리된 데이터에서 각 단어의 빈도를 나타내는 행렬을 뜻함
- 행의 방향으로 문서를 나타내고, 열의 방향으로 단어를 나타냄
- Gensim의 Dictionary Class를 이용하여 어떤 열 번호(index)에 어떤 단어(term)가 들어갈지 정함
- 단어-번호 객체를 활용하여 DTM 생성 (단어 ID, 빈도수) 형태
from gensim import corpora
# Gensim의 Dictionary 객체를 생성
dictionary = corpora.Dictionary(preproc_data)
# 문서-단어 행렬을 생성
corpus = [dictionary.doc2bow(text) for text in preproc_data]
- LDA 적용
- corpus : 전체 텍스트 집합
- num_topics : 선정할 토픽의 수
- id2word : 단어와 인덱스 데이터
- passes : 학습 횟수
from gensim.models import ldamodel
topicK = 3
num_trains = 10
lda_model = ldamodel.LdaModel(corpus,
num_topics=topicK, # 선정한 토픽 수
id2word=dictionary,
passes=num_trains, # 학습 횟수
random_state=42)
- 문서 별 토픽 분포 확인
# 문서 별 토픽 분포 확인
for document in corpus[:5]:
origin_doc = [dictionary[word_idx] for word_idx, word_num in document]
print(f'{origin_doc}에 속한 토픽의 분포는 아래와 같습니다.')
for topic_idx, topic_dist in lda_model[document]:
print(f'{topic_idx} 번째 토픽 : {topic_dist*100:.2f}% 확률')

- 토픽 별 단어 분포 확인
# 토픽 별 단어 분포 확인
for k in range(topicK):
print(f'{k}번째 토픽을 구성하는 단어의 분포는...')
for word, prob in lda_model.show_topic(k, topn=5) :
print(f'{word} : {prob*100:.2f}%', end= ' ')
print()
print()

- LDA 모델 결과 확인
- 각 토픽을 구성하는 단어를 확인하여 분포도를 보고 토픽의 의미는 사람이 지정
# 원문장과 토픽 분포를 보고 해석하기
target_idx = 10
print('원 문장 : ', data[target_idx])
print('전처리 문장 : ', preproc_data[target_idx])
print('문장 내 토픽 분포 : ')
for topic_idx, prob in lda_model[corpus[target_idx]]:
print(f' - {topic_idx}번 토픽 : {prob*100:.2f}%')

- 0번째 토픽 :
- 주요 단어 : service, good, disappoint, like, best, great, place, staff
- 식당 서비스 품질과 고객 경험
- 1번째 토픽 :
- 주요 단어 : food, back, good, service, place, go, time, wait, would
- 음식 품질과 재방문에 관련된 의사 표현
- 2번째 토픽 :
- 주요 단어 : place, great, restaurant, like, salad, delicious, time, get
- 식당 전반의 분위기와 음식의 퀄리티에 대한 내용
5.2 HuggingFace를 이용한 딥러닝 모델의 문장 분류 실습
- HuggingFace에서 SNS 데이터로 학습한 감정 분석 모델을 사용(링크)
- 문장 분류에 기본이 되는 BERT 모델을 개선한 RoBERTa 모델을 기본으로 사용
- 데이터 셋은 Yelp 데이터 사용
- HuggingFace는 특정 모델에 맞는 전처리 코드를 제공함
- Auto : HuggingFace 페이지 이름만으로도 목표하는 것들을 자동으로 가져올 수 있음
- Stop words, Stemming 사용하지 않음
# 모델 가져오기
from transformers import AutoTokenizer
MODEL = f"cardiffnlp/twitter-roberta-base-sentiment-latest"
tokenizer = AutoTokenizer.from_pretrained(MODEL)
- Token Index를 사용 → 토큰과 임베딩 벡터 사이를 이어주는 매핑값
- 장점 : 메모리적 이득(글의 형태가 아닌 숫자의 형태)
- 특정 토큰이 몇번째인지만 약속해두면 됨
- 임베딩 벡터를 모델에 귀속시켜 관리가 용이함
- 인덱스는 임베딩 벡터가 쌓여 있는 순서를 의미
- "Wow... Loved this place." 라는 리뷰를 Pytorch 텐서 형태로 토큰화
tokenized = tokenizer(text, return_tensors='pt')
print(tokenized)

- 각 단어와 특수문자, 시작점의 공백까지 총 9개의 토큰으로 나뉨
- 실제 토큰의 값을 글자로 확인하고 싶다면 conver_ids_tokens 함수 사용
tokenizer.convert_ids_to_tokens(tokenized['input_ids'].tolist()[0])
# 이 모델의 Token은 단어의 시작을 나타내는 글자에도 추가 처리를 진행!
- 모델 호출
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(MODEL)
- index를 바탕으로 임베딩 값 알아보기
- 9개의 토큰이 입력되었고, 각 토큰은 768개짜리 벡터로 변환
# [참고] index를 바탕으로 embedding 값을 알아보기
# 모델의 구조를 바탕으로 embedding 부분만 추론 진행
emb = model.roberta.embeddings(**{'input_ids':tokenized['input_ids']})
print('Embedding 값 : ')
print(emb)
# 9개의 Token이 입력되었고,
# 각 Token은 768짜리 vector로 변환
print('Embedding 출력의 크기 : ', emb.shape)

- 모델 사용
- 토큰 값으로 나온 input_ids를 key_value 형태로 입력
- 혹은 unpacking 문법을 사용
- 모델 사용법은 보통 모델 페이지에 나와있음!
# 모델을 사용한다면 Token 값으로 나온
# input_ids를 key_value 형태로 넣어주면 됨
output = model(input_ids = tokenized['input_ids'])
# 혹은 사용 예제에서 처럼
# Unpacking 사용도 가능
output = model(**tokenized)
- 모델의 출력 결과가 확률의 형태로 나오지 않으면 해석하기 어려워 softmax 함수를 사용하여 확률의 형태로 변환
# 타겟하는 정보인 점수 값을 가져옴
scores = output[0][0].detach().numpy()
# 점수를 확률의 형태로 변환
# 과정에성 softmax 함수를 사용하고
# 외부 패키지의 함수를 활용
from scipy.special import softmax
scores = softmax(scores)
print(scores)

- 모델의 학습 정보를 확인하기 위해서는 config를 확인하면 된다
- 여기에 id2label를 보면 어떤 인덱스가 어떤 분류인지 확인할 수 있다
- 이 경우 0은 부정적, 1은 중립적, 2는 긍정적이다
# 모델 학습 정보를 확인하기 위한 config 값 로딩
from transformers import AutoConfig
config = AutoConfig.from_pretrained(MODEL)
config

- 최종적으로 확률이 제일 큰 값의 인덱스를 바탕으로 감정을 판단한다
import numpy as np
max_prob_index = np.argmax(scores)
results = config.id2label[max_prob_index]
results

- 위 과정을 함수로 표현하면 다음과 같다 :
# 최종 함수로 표현
def sentiment_analysis(sentence, tokenizer, model, config):
tokenized = tokenizer(sentence, return_tensors='pt')
output = model(input_ids = tokenized['input_ids'])
scores = output[0][0].detach().numpy()
scores = softmax(scores)
max_prob_index = np.argmax(scores)
results = config.id2label[max_prob_index]
return results, max_prob_index
- 다른 리뷰 확인
sent = data['text'][2]
res, idx = sentiment_analysis(sent, tokenizer, model, config)
print(sent)
print('-> ', idx, res)

- 평가 데이터를 생성하고 추론을 진행한 결과를 다음과 같다 :
- 실제로 부정적인 것을 긍정적으로 분류한 것은 1건
- 실제로 긍정적인 것을 부정적이라고 분류한 것은 0건으로 아주 잘 분류했다고 볼 수 있다.

- 정확도는 0.91, F1 score는 0.91로 높은 성능을 보였다.
느낀점
딥러닝 모델을 사용해서 문장 분류 실습을 진행해보니 확실히 이커머스 분야에서 많이 사용될 수 있는 스킬인 것 같았다. 고객 리뷰를 딥러닝 모델을 통해서 분류해낼 수 있으면 일의 효율도 오르고, 결과를 토대로 서비스 개선을 이루어내기 좋을 것 같다. HuggingFace는 처음 접해봤는데 예전에 리액트 라이브러리 찾아서 다운 받던 것처럼 다양한 모델들이 있는 것 같다. 시간날때 한번씩 둘러보면 좋을 것 같다는 생각이 들었다.
'Data Science > TIL (Today I Learned)' 카테고리의 다른 글
| 프로그래머스 데이터분석 데브코스 1기 - 65일차 (0) | 2024.02.23 |
|---|---|
| 프로그래머스 데이터분석 데브코스 1기 - 64일차 (0) | 2024.02.22 |
| 프로그래머스 데이터분석 데브코스 1기 - 62일차 (0) | 2024.02.20 |
| 프로그래머스 데이터분석 데브코스 1기 - 61일차 (0) | 2024.02.19 |
| 프로그래머스 데이터분석 데브코스 1기 - 60일차 (2) | 2024.02.16 |