isPowerfulBlog
[NLP] 다양한 문서에서 중요한 키워드 추출하기 본문
다양하고 유의미한 문제를 생성하기 위해 문서에서 중요한 키워드를 추출해야 했다.
키워드 추출이라 하면... 아래와 같은 방식들이 떠오를 수 있다.
- TF로 가장 많이 등장하는 키워드 추출하기
- TF-IDF 문서 전체에서의 비중은 적으나 특정 문서에서 많이 등장하는 키워드를 추출하기
- NER(Named Entity Recognition) 모델을 사용해서 개체 인식하기
- GPT(LLM)한테 중요한 키워드 뽑아달라고 하기
- GPT랑 TF-IDF 어떻게 엮어보기
하지만 주어진 현재 상황은 이렇다.
- 문서의 언어가 정해져있지 않다. (주로 영어/한글 이겠지만)
- 문서의 양이 크다. (강의안을 문서로 넣는다고 생각해야한다)
- 모든 문서가 특정 도메인에 특화되어있지 않다. (일반화가 잘 된 방식을 사용해야한다.)
- 아니 문서가 특정 도메인에 너무 특화되어있을 수도 있다. (그게 어떤 도메인인지는 알 수 없다.)
주어진 상황을 고려해서 가장 적절한 방식을 선택해보자
1. TF로 가장 많이 등장하는 키워드 추출하기
가장 많이 등장한 키워드 == 가장 중요한 키워드
라고 할 수 있나?
당장 '나', '너', '우리' 이런 단어들만 생각해봐도 중요하지 않은데 많이 등장하는 케이스가 대다수다.
2. TF-IDF 문서 전체에서의 비중은 적으나 특정 문서에서 많이 등장하는 키워드를 추출하기
TF-IDF는 특정 문서에서 자주 등장하면서도 전체 문서 집합에서는 드물게 나타나는 단어에 높은 점수를 부여하는 방식이다. 벡터로 계산한다.
일단 대용량 데이터를 처리하는데 속도가 빠르고 (딥러닝에 비하면 훨씬 가볍다), TF보다 유의미하다.
빠르게 처리해야하고, 키워드 선택 방식이 나름 유의미하고, 이 부분의 정확성이 너무 크리티컬하게 중요한 부분까진 아니라서(?)
가장 괜찮은 방법이라고 생각이 든다.
3. NER(Named Entity Recognition) 모델을 사용해서 개체 인식하기
개체명 인식이라는 것만 보고 아 이거다!! 싶었는데 이거 아니었다.
일반화된 NER 모델을 쓴다면, 강의안 같이 특정 도메인에 특화된 문서의 경우 개체를 아예 인식하지 못한다. -> output으로 나오는 키워드가 없어져벌임...
특정 도메인에 특화된 NER 모델은 당연히! 못 쓴다. 우리는 어떤 문서가 들어올지 모르는 서비스를 제작 중이기 때문이다.
4. GPT(LLM)한테 중요한 키워드 뽑아달라고 하기
일단 문서들을 다 탐색하려면 그 모든 문서들을 쪼개어 gpt한테 쿼리를 날려야하는데, 토큰 소모가 너무 크다.
그리고 그렇게 잘 하지도 못한다. 그냥 룰베이스랑 별 차이 없음.
5. GPT랑 TF-IDF 어떻게 엮어보기
TF-IDF로 추출한 키워드를 gpt로 뿔려서 어쩌구,... claude와 함께 다양한 상상을 해봤는데,
실질적으로 필요가 없다.
문서 내에서 키워드를 뽑고,
키워드로 그 문서의 조각을 가져오려고 하는 상황이기 때문이다.
새로운 키워드를 '생성'할 필요가 없음.
결론은 TF-IDF
- GPT같이 콜 비용이 발생하지 않는다. 그치만 현재 태스크에서는 유의미한 성능 차이가 없다.
- 딥러닝을 도커에 직접 올려 돌릴 필요 없다. GPU 안 써도 되고, CPU에서 속도 걱정할 필요 없다.
- 간편하다.
- 오히려 어줍잖게 맥락을 고려하려 하지 않아서 특정 도메인에 너무 특화된 문서에도 일반적으로 대응할 수 있다.
위와 같은 이유들로 TF-IDF로 결정했다. 역시 튜닝의 끝은 순정이라고...
TF-IDF로 문서 내에서 주요 키워드 추출하기
를 하기에 앞서서 TF-IDF에 대해서 알고가자~
TF-IDF란?
이하 Glaude 답변-
TF-IDF(Term Frequency-Inverse Document Frequency)는 정보 검색과 텍스트 마이닝에서 널리 사용되는 가중치 계산 방법입니다. 이 방법은 문서 내에서 단어의 중요도를 평가하는 데 사용됩니다.
TF-IDF는 두 가지 개념을 결합한 것입니다:
1. TF(Term Frequency):
- TF는 특정 문서에서 해당 단어가 얼마나 자주 등장하는지를 나타냅니다.
- 문서 내에서 단어의 빈도를 계산합니다.
- 단어의 빈도가 높을수록 해당 문서에서 중요한 단어일 가능성이 높아집니다.
2. IDF(Inverse Document Frequency):
- IDF는 해당 단어가 전체 문서 집합에서 얼마나 드물게 등장하는지를 나타냅니다.
- 전체 문서 수를 해당 단어가 등장한 문서 수로 나눈 후, 그 값에 로그를 취합니다.
- 단어가 여러 문서에서 자주 등장할수록 IDF 값은 작아지고, 특정 문서에서만 등장할수록 IDF 값은 커집니다.
TF-IDF 값은 TF와 IDF의 곱으로 계산됩니다:
TF-IDF(단어, 문서) = TF(단어, 문서) * IDF(단어)
- 특정 문서에서 자주 등장하는 단어는 TF 값이 높아집니다.
- 전체 문서 집합에서 드물게 등장하는 단어는 IDF 값이 높아집니다.
따라서 TF-IDF는 특정 문서에서 자주 등장하면서도 전체 문서 집합에서는 드물게 나타나는 단어에 높은 점수를 부여합니다. 이러한 단어는 해당 문서의 주제나 특징을 잘 나타내는 키워드로 간주될 수 있습니다.
TF-IDF는 문서의 유사성 비교, 문서 분류, 검색 시스템에서의 랭킹 등 다양한 분야에서 활용됩니다. 이를 통해 문서의 핵심 내용을 파악하고, 문서 간의 유사성을 측정하며, 검색 결과의 관련성을 평가할 수 있습니다.
TF-IDF는 간단하면서도 효과적인 방법으로, 큰 규모의 문서 집합에서도 효율적으로 적용할 수 있습니다. 다만 단어의 의미나 문맥을 고려하지 않는다는 한계가 있어, 보다 정교한 분석이 필요한 경우에는 Word2Vec이나 BERT와 같은 단어 임베딩 기술과 함께 사용되기도 합니다.
TF-IDF
전체 문서에서는 비중이 적지만 특정 문서에서 많이 출현하는 키워드를 찾아야한다.
문서 쪼개기
이를 위해 하나의 문서를 작은 문서 조각들로 쪼개고 하나의 리스트에 모아서 준비한다. -> [LangChain] Text Splitter로 긴 문서를 작은 Chunk로 쪼개기
text_data = []
for doc in split_doc_list:
text_data.append(doc.page_content)
문서 토큰화 하기
어떤 토크나이저를 쓸까? 일단 nltk를 쓰자!
nltk는 다국어를 지원하는 토크나이저이다. 가장 일반적으로 많이 쓴다.
NLTK 환경설정
다만 이 nltk를 쓰기 위해서는 도커 파일에 다음과 같은 내용을 추가해야한다.
# for our nltk data folder
ENV NLTK_DATA /usr/share/nltk_data
# download punkt
RUN python3.10 -m nltk.downloader punkt -d /usr/share/nltk_data
# download stopwords
RUN python3.10 -m nltk.downloader stopwords -d /usr/share/nltk_data
NLTK 토크나이징
아래처럼 문서 조각들을 돌면서 토크나이징, 불용어 제거를 해줄 수 있다.
import string
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords as en_stopwords
for text in text_data:
tokens = word_tokenize(text)
en_stop_words = set(en_stopwords.words('english'))
cleaned_tokens = [token.lower() for token in tokens if token.isalnum() and token.lower() not in en_stop_words and token not in string.punctuation]
이렇게! 해주면 정제된 토큰들을 모을 수 있다.
그런데! nltk는 다국어를 지원하긴 하지만 한국어를 잘 하지는 못 한다.
그럼 한글 문서의 경우 따로 처리를 해줘야하지 않을까?
한글 문서인건 어떻게 판단할까?
문서 언어 감지하기
langdetect
라는 라이브러리가 있다! 이걸 이용해서 문서의 언어를 판별할 수 있다.
이하 Claude 설명-
angdetect는 텍스트의 언어를 감지하기 위해 나이브 베이즈 분류기(Naive Bayes classifier)를 사용합니다. 이 분류기는 각 언어의 특징적인 n-gram 빈도를 기반으로 언어를 판별합니다.
langdetect
를 적용해보자!
from langdetect import detect
cleaned_text_data = []
for text in text_data:
language = detect(text)
if language == 'ko':
# 한글 문서인 경우
...
else:
# 영어 문서인 경우
tokens = word_tokenize(text)
en_stop_words = set(en_stopwords.words('english'))
cleaned_tokens = [token.lower() for token in tokens if token.isalnum() and token.lower() not in en_stop_words and token not in string.punctuation]
cleaned_text_data.append(' '.join(cleaned_tokens))
이렇게 간단하게 문서의 언어를 감지할 수 있다.
그럼 이제 한글 문서에 대해서는 어떤 토크나이저를 사용할 수 있을까?
한글 토크나이저 선택하기
작년에 들었던 빅데이터최신기술 수업에서, 강승식 교수님 파트에서는 거의 자연어 처리 위주로 배웠었다.
수업에서 자유 주제로 프로젝트 및 소논문을 작성하는 과제가 있었고, 나는 그때 형태소 분석기를 비교하는 것을 주제로 선정했다.
당시에는 그냥.. 했던거 같은데 지금 다시 찾아볼 줄이야!! -> [GitHub]감성 분석을 위한 토크나이징 및 워드 임베딩 기법 탐색
)
이렇게 직관적인 성능 비교와 속도비교를 해놨었다. Mecab아니면 Okt가 가장 괜찮다.
... 라고 정확하게 1년 전의 내가 지금의 나한테 편지를 써놨었다... 미쳤다
과거의 나의 의견을 참고해서 Mecab
을 쓰기로 결정~!
Mecab 토크나이징
메캅을 사용하는 코드는 nltk처럼 간단하다!
from konlpy.tag import Mecab
mecab = Mecab()
tokens = mecab.morphs(text)
ko_stop_words = set([word.strip() for word in ko_stopwords.split('\n')]) # 이건 직접 모아서 썼다.
cleaned_tokens = [token for token in tokens if token not in ko_stop_words]
Mecab 환경설정
문제는 mecab 사용을 위한 환경 설정이다...
진짜 무슨 토크나이저 하나 쓰겠다고 환경 설정에 개 고생을 고생을 했다.
발생한 에러들...
- error: cannot guess build type; you must specify one
- E: Unable to locate package openjdk-8-jdk
- E: Unable to locate package openjdk-11-jdk
- aarch64 configure: error: cannot guess build type; you must specify one
- ERROR: Could not build wheels for mecab-python, which is required to install pyproject.toml-based projects
최종 해결...
RUN apt-get update && apt-get install -y \
g++ \
openjdk-17-jdk
# Mecab 설치
RUN python3.10 -m pip install konlpy JPype1-py3
RUN bash mecab_install.sh
RUN python3.10 -m pip install mecab-python3
이 내용들을 도커파일에 추가했다.
이 과정에서 다양한 이슈들이 있었다. (공식문서, Claude 다 에러 남)
- https://github.com/konlpy/konlpy/issues/429
- https://acdongpgm.tistory.com/m/289
이 두 글과 claude의 답변을 참고했다.
TF-IDF 적용하기
영어/한글 언어를 감지하고, 알맞는 토크나이저로 토큰화 하고 불용어를 제거해서 cleaned_text_data
를 잘 준비했다면, 이제 TF-IDF 계산을 하면 된다.
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(cleaned_text_data)
feature_names = vectorizer.get_feature_names_out()
일단 잘 정제한 텍스트들이 담긴 리스트 cleaned_text_data
를 TfidfVectorizer
를 이용하여 TF-IDF 값들의 행렬로 만들어준다.feature_names
는 TF-IDF 행렬의 각 열이 어떤 단어에 해당하는지를 나타내는 리스트이다.
tfidf_scores = tfidf_matrix.sum(axis=0).A1
top_scores = tfidf_scores.argsort()[-top_n:][::-1]
keywords = [feature_names[i] for i in top_scores]
잘 계산해둔 tfidf_matrix들을 가지고tfidf_matrix.sum(axis=0)
해서 TF-IDF 행렬의 각 열(단어)의 합을 계산한다. 이는 각 단어의 전체 문서에서의 중요도를 나타낸다..A1
로 결과를 1차원 배열로 변환한다.
마지막으로 tfidf_scores에서 값이 큰 순서로 top_n개의 인덱스를 추출한다.
끝!
References
- Claude 내돈내산^.^
- https://github.com/konlpy/konlpy/issues/429 -> Mecab ubuntu 22.04에서 설치 에러 이슈
- https://acdongpgm.tistory.com/m/289 -> Mecab install script 수정
- https://github.com/noooey/exploring-for-sentiment-analysis -> 한글 형태소 분석기 비교
'AI' 카테고리의 다른 글
[LangChain] LLM으로 문서 요약하기 (summarization) (0) | 2024.04.23 |
---|---|
[LangChain] Text Splitter로 긴 문서를 작은 Chunk로 쪼개기 (0) | 2024.04.19 |
[LLM] LangChain 🦜🔗 (0) | 2023.11.07 |
[프로젝트] Anti-aginGAN for CAT based StyleGAN2 (0) | 2023.02.20 |
[Anaconda] 아나콘다 기본 명령어 (0) | 2023.01.24 |