0. 현재 상황
2024.08.14 - [[Deep daiv.] 복습] - [Deep daiv.] TIL & WIL - 5. 자연어 처리 & 텍스트 처리 (Contd.)
에 나왔던 TF-IDF 에는 한계점들이 분명히 있습니다.
- 단어의 의미 무시
- 단어의 등장 순서 무시
TF-IDF 나 BoW 모두 마찬가지로 단어의 빈도를 중심으로 계산되기 때문에 단어의 실제 의미나 문맥을 파악할 수 없다는 단점이 있습니다.
또한 단어가 문서 내에서 어떤 순서로 등장하는지도 고려하지 않습니다.
예를 들어, 아래의 두 문장이 있을때, TF-IDF 는 동일한 벡터로 계산합니다.
'나는 너를 좋아한다.'
'너는 나를 좋아한다.'
TF-IDF:
나 | 너 | 는 | 를 | 좋아한다 | |
문장 1 | 0.447 | 0.447 | 0.447 | 0.447 | 0.447 |
문장 2 | 0.447 | 0.447 | 0.447 | 0.447 | 0.447 |
1. Word2Vec
위 단점들을 개선하기 위해 생긴 단어 임베딩 방식이 Word2Vec 입니다.
BoW 나 TF-IDF 를 위한 단어 임베딩의 방식은 원-핫 인코딩 방식입니다.
하지만 Word2Vec 에서 사용하는 단어 임베딩 방식은 다른데요,
W2V 은 단어의 의미를 고려해서 단어 벡터를 표현합니다.
쉽게 말하자면 W2V 은 주변 단어를 통해 중심 핵심 단어를 임베딩하는 방식을 사용합니다.
자세한 내용은 아래의 링크에 잘 나와있습니다.
2. W2V 을 활용한 문서 유사도 분석
2024.08.14 - [[Deep daiv.] 복습] - [Deep daiv.] TIL & WIL - 5. 자연어 처리 & 텍스트 처리 (Contd.)
에서 했던 코사인 유사도 방식을 기반으로 유사 문서를 찾아내는 작업을 할건데,
이전에는 TF-IDF 행렬을 기반으로 했다면,
지금은 W2V 을 활용해서 해보겠습니다.
2.1 Mecab
Mecab 은 okt 보다 성능이 좋고 빠르지만 추가적인 설치가 필요하다는 점에서 귀찮습니다(?).
그렇지만 이번에는 okt 대신 Mecab 을 사용해보겠습니다.
!git clone https://github.com/SOMJANG/Mecab-ko-for-Google-Colab.git
cd Mecab-ko-for-Google-Colab
!bash install_mecab-ko_on_colab_light_220429.sh
2.2 사용자 사전 추가
텍스트 전처리를 진행하다 보면 토큰화가 잘못되는 경우가 있습니다. 이럴 때 분석에 활용할 필요가 없다면 불용어로 취급해 제거해버리면 되지만, 꼭 필요한 단어라고 한다면 추가해야 합니다.
okt 등의 라이브러리에서는 이런 과정이 많이 복잡하지만, Mecab 의 경우 사용자 사전을 비교적 쉽게 구축할 수 있도록 만들어 두었습니다.
이를 활용해 분석에 필요한 단어들을 추가하고, 더욱 의미 있는 분석을 진행할 수 있습니다.
cd /content/mecab-ko-dic-2.1.1-20180720
!pip install jamo
from jamo import h2j, j2hcj
def get_jongsung_TF(sample_text):
sample_text_list = list(sample_text)
last_word = sample_text_list[-1]
last_word_jamo_list = list(j2hcj(h2j(last_word)))
last_jamo = last_word_jamo_list[-1]
jongsung_TF = "T"
if last_jamo in ['ㅏ', 'ㅑ', 'ㅓ', 'ㅕ', 'ㅗ', 'ㅛ', 'ㅜ', 'ㅠ', 'ㅡ', 'ㅣ', 'ㅘ', 'ㅚ', 'ㅙ', 'ㅝ', 'ㅞ', 'ㅢ', 'ㅐ,ㅔ', 'ㅟ', 'ㅖ', 'ㅒ']:
jongsung_TF = "F"
return jongsung_TF
def make_user_dic_csv(morpheme_type, word_list, user_dic_file_name):
file_data = []
for word, score in word_list:
jongsung_TF = get_jongsung_TF(word)
line = f"{word},,,{score},{morpheme_type},*,{jongsung_TF},{word},*,*,*,*,*\n"
file_data.append(line)
with open("./user-dic/user-nnp.csv", 'w', encoding='utf-8') as f:
for line in file_data:
f.write(line)
# 사용자 정의 사전 정의
word_list = [('ChatGPT', 0), ('챗GPT', 0), ('오픈AI', 0)]
make_user_dic_csv(morpheme_type="NNP", word_list=word_list, user_dic_file_name='user-nnp.csv')
!bash autogen.sh
!make
!sudo make install
!bash tools/add-userdic.sh
cd ..
3. 전처리
이전에 사용했었던 파리 올림픽 기사 데이터를 사용합니다.
import pandas as pd
import os
path = '/content/drive/MyDrive/Colab Notebooks/강의 데이터'
filename = '최종_파리올림픽_네이버_뉴스_콘텐츠_20240715_20240812.csv'
df = pd.read_csv(os.path.join(path, filename))
그 후 데이터 수집간에 발생했을지도 모를 에러때문에 발생한 결측치를 처리해줍니다.
# 본문에 결측치가 있는 경우 제거
df = df.dropna(subset=['content']).copy()
df['content'].isnull().sum()
# output : 0
또한 중복인 내용이 있는지 확인해주고 있다면 제거해줍니다.(* 기자들의 기사 돌려막기)
# 중복 콘텐츠 내용 개수 확인
df.duplicated(subset=['content']).sum()
# output : 35
# 동일한 본문이 있는 경우 제거
df.drop_duplicates(subset=['content'], inplcae = True)
df.duplicated(subset=['content']).sum()
# output = 0
그 후 명사를 기반으로 토큰화 해줍니다.
# 명사 단어 기반으로 토큰화 하기
df['content_token'] = df['content'].map(lambda x: mecab.nouns(x))
df['content_token']
결과
3.1 문자 정규화
텍스트 데이터를 다루다 보면 동일한 문자가 다르게 표현되어 문제가 발생하는 경우가 종종 있습니다. 예를 들어, 한글에서 '가' 를 나타내는 방법은 두 가지가 있습니다.
하나는 단일 코드포인트로 '가' 를 바로 표현하는 방법이고,
다른 하나는 'ㄱ' 과 'ㅏ' 를 조합해 '가' 를 표현하는 방법입니다.
이처럼 시각적으로는 동일해 보이지만, 컴퓨터 내부에서는 다르게 인식되어 처리될 수 있습니다. 이러한 문제를 해결하기 위해 유니코드 정규화가 필요하며, 다양한 정규화 방식(NFC, NFD, NFKC, NKFD)을 사용할 수 있습니다.
Mecab 이나 Okt 같은 한국어 처리 라이브러리를 사용할 때, 특정 문자가 예상대로 처리되지 않아 분석의 정확도가 떨어질 수 있습니다. 이런 경우, 유니코드 정규화를 통해 입력 데이터를 표준 형식으로 전환하면, 텍스트를 보다 일관되게 처리할 수 있습니다.
또한 사용자 사전을 구축할 때도 정규화된 형태의 문자를 사용하면, 사전의 일관성과 호환성을 높일 수 있어, 더욱 의미있는 분석을 진행할 수 있습니다.
아래의 예시는 한자 '금' 의 유니코드값들 입니다.
import unicodedata
gold1 = '金'
gold2 = '金'
# 유니코드 형태로 불러오기
code1 = hex(ord(gold1))
code2 = hex(ord(gold2))
# 유니코드 결과 비교하기
print("Code points for char1:", code1)
print("Code points for char2:", code2)
# Code points for char1: 0xf90a
# Code points for char2: 0x91d1
문자 정규화를 토큰화 한 데이터에 반영하겠습니다.
# 문자 정규화 반영하기
df['content_token'].map(lambda x: [unicodedata.normalizer('NFC', char) for char in x])
4. W2V 훈련
4.1 W2V 파라미터
https://hoonzi-text.tistory.com/2
전처리를 끝낸 데이터를 활용하여 W2V 를 훈련시키고,
'금메달' 과 가장 유사한 단어를 찾아봅니다.
# Word2Vec 모델 불러오기
from gensim.models import Word2Vec
# Word2Vec 모델 학습시키기
model = Word2Vec(df['content_token'], window = 5, min_count = 10, vector_size = 300)
# Word2Vec 으로 원하는 단어와 유사한 단어 살펴보기
model.wv.similar_by_word('금메달', topn = 20)
결과
유사 문서를 찾기 위해 문서에 대한 벡터를 얻어야합니다.
문서끼리의 코사인 유사도를 기반으로 유사성을 판단해야하기 때문입니다.
이번에는 문서를 나타난 단어들의 W2V 단어 벡터의 평균으로 하겠습니다.
문서를 나타내는 벡터를 계산하는 방식을 개선한다면 더욱 좋은 성능을 기대할 수 있습니다.
우선 문서 벡터를 만들어내는 함수를 만듭니다.
import numpy as np
def get_text_vector(words): # words 는 토큰화된 단어들입니다.
text_vector = []
for word in words:
if word in model.wv: # model.wv 는 word vocabulary, 단어장입니다.
text_vector.append(model.wv[word]) # 단어 벡터들을 모음
if len(text_vector) > 0:
return np.mean(text_vector, axis = 0) # 단어 벡터들의 평균을 문서를 대표하는 벡터로 사용합니다.
else:
return np.zeros(model.vector_size) # 예외 처리
그 후 벡터 행렬을 생성하고 코사인 유사도를 기반으로 유사 기사를 추출합니다.
# 벡터 행렬 생성하기
document_vectors_matrix = np.vstack(df['content_token'].map(get_text_vector))
# np.vstack 은 나오는 벡터들을 vertical(* 수직) 으로 쌓아 행렬을 만드는 메소드입니다.
# 코사인 유사도 계산하기
from sklearn.metrices.pairwise import cosine_similarity
cosine_sim = cosine_similarity(document_vectors_matrix)
# 랜덤 인덱스에 따라 콘텐츠 유사도 계산하기
idx = np.random.randint(len(document_vectors_matrix))
sim_scores = list(enumerate(cosine_sim[idx])
sim_scores = sorted(sim_scores, key = lambda x: x[1], reverse = True)
sim_scores = sim_scores[:6]
post_indices = [i[0] for i in sim_scores]
df.loc['title', post_indices]
배움
문맥을 이용하는 W2V
코사인 유사도
'[Deep daiv.] > [Deep daiv.] 복습' 카테고리의 다른 글
[Deep daiv.] TIL - 7. 딥러닝 (1) | 2024.08.18 |
---|---|
[Deep daiv.] TIL - 2차 특강. 파이프라인과 하이퍼 파라미터 튜닝 (1) | 2024.08.11 |
[Deep daiv.] TIL - 4.1 k-NN 알고리즘과 의사 결정 나무 (1) | 2024.08.11 |
[Deep daiv.] TIL - 4강. 지도 학습(분류) (0) | 2024.08.11 |
[Deep daiv.] TIL - 3.1 차원축소와 클러스터링 (0) | 2024.08.09 |