[NLP 10] Embedding 5 : CBOW와 skip-gram Code 비교

방선생·2025년 1월 19일

Natural Language Processing

목록 보기
10/22

CBOW / skip-gram

  • gensim 라이브러리 이용
    • 텍스트 데이터를 벡터로 변환하는데 필요한 기능들을 제공해주고 있는 대표적인 라이브러리
    • 라이브러리 임폴트
      • import gensim
    • Word2Vec 생성자 함수 호출, 객체 생성
      • gensim.models.Word2Vec(sentences=, vector_size=, window=, min_count=, workers=1, sg=)

  • word2vec : 단어 자체를 벡터로 변환하는 클래스 함수
    • CountVectorizer : 문장을 입력받아 임베딩 함 (fit함수에서 토큰화함)
    • word2vec : 문장을 토큰화 한 후에 토큰화 된 리스트들을 임베딩함
    • list of lists of tokens - 문장을 토큰화 한 리스트의 묶음을 한번에 처리가능함

method

  • vector_size : 입력받는 임베딩 벡터의 차원(크기)을 결정 (은닉층의 크기)
  • window : 앞뒤 각각 참조할 문맥단어의 최소 범위 =>
    현대 언어 모델은 문장 전체를 참조함 == 윈도우 값이 클수록 성능이 좋아짐 but 문장 전체의 길이를 판단하여 적절히 조절해야함
  • min_count : 입력되는 반복되는 토큰의 출현 빈도수가 n번 이하면 생략함 (기본값 5)
  • sg : 0 또는 1 선택 =>
    0일 경우 cbow, 1일때 skip-gram사용 함 (기본값 0)
  • seed : 학습과정에 랜덤으로 생성되는 가중치의 초기값
  • epochs : 학습의 반복 횟수
  • workers = 학습을 위한 프로세스 수 (colab=1)



이론 설명은 [NLP 9]를 참고하세요


(이 시리즈의 모든 코드는 코랩환경에서 Python으로 작성하였습니다)

word2vec Code 1 (한글 문장에 대한 유사도 측정)

!pip install kiwipiepy #kiwi 라이브러리 설치
from sklearn.metrics.pairwise import cosine_similarity

# 한글 텍스트 데이터
ko_sentences = ["직원이 무단 퇴사를 했는데 손해 배상 청구할 수 있나요?",
                "무단 퇴사한 직원에 대한 손해 배상 청구가 가능한가요?"]
# 함수 호출
import kiwipiepy
kw = kiwipiepy. Kiwi()
stopword = kiwipiepy.utils.Stopwords()

#토큰화 및 불용어 제거
token_sentence = [] #전체 결과를 저장

for sent in ko_sentences:
  sentence = []
  morphs_list = kw.tokenize(sent, stopwords=stopword) #불용어 제거

  for morph, _, _, _ in morphs_list: #형태소 추출
    sentence.append(morph)

  token_sentence.append(sentence)

print(token_sentence)
# word2vec > 단어(형태소) 사전 생성 + 임베딩 벡터 생성

# 라이브러리 임폴트 및 모델 생성
import gensim
cbow = gensim.models.word2vec.Word2Vec(sentences=token_sentence, vector_size=10, min_count=1, sg = 0, seed=0, epochs=50) #sg = 0 > cbow
sikp_gram = gensim.models.word2vec.Word2Vec(sentences=token_sentence, vector_size=10, min_count=1, sg = 1, seed=0, epochs=50) #sg = 1 > sikp-gram

print(sikp_gram.wv.vectors == cbow.wv.vectors) #달라지기 위한 최소 epochs = 43
  • print(sikp_gram.wv.vectors == cbow.wv.vectors)를 통해 두 모델의 임베딩 벡터의 값이 다르게 형성되는지 확인



gensim.word2vec의 생성된 단어 사전과 임베딩 벡터 확인

  • model.wv에 단어 사전과 벡터가 저장되어있음
  • 단어사전 : model.wv.key_to_index (type : ‘dict’)
  • model.wv['word'] - 특정 단어의 emvbedding vector
  • model.wv.vectors - 전체 단어의 emvbedding vector

  • 문장의 임베딩 벡터의 데이터프레임: 행 == 형태소의 개수, 열 == 벡터 사이즈
  • 특정 단어의 emvbedding vector의 사이즈 == word2vec의 vector_size파라미터 값

df.Series

  • .max() - 행 인덱스 값들의 최대값만 출력
  • .values - 행 인덱스를 제거하고 값만 출력
  • .reshape(행, 열) - 값을 행렬표현으로 변경

판다스 통계값 파라미터

  • min() - 최소 값
  • max() - 최대 값
  • mean() - 평균 값
  • std() - 표준편차 값(standard daviaion)

word2vec Code 2 (cbow 모델)

vocab_cbow = cbow.wv.key_to_index
print(vocab_cbow)
print("-"*80)

print(cbow.wv.vectors)
# cbow(epochs = 50)

# 결과를 저장할 빈 리스트 생성
embeddings1_cbow = []
embeddings2_cbow = []

# 첫번쨰 문장 > 형태소 별 임베딩 벡터 추출 및 저장
for morph in token_sentence[0]:
  morph_embedding = cbow.wv[morph]
  embeddings1_cbow.append(morph_embedding)

# 두번쨰 문장 > 형태소 별 임베딩 벡터 추출 및 저장
for morph in token_sentence[1]:
  morph_embedding = cbow.wv[morph]
  embeddings2_cbow.append(morph_embedding)

print(embeddings1_cbow)
print(f'\n{"-"*80}\n')
print(embeddings2_cbow)
# 문장 내 형태소 별 임베딩 벡터 > 데이터프레임 생성

import pandas as pd #라이브러리 임폴트

# 문장의 임베딩 벡터 > 데이터프레임 생성
df_cbow1 = pd.DataFrame(data=embeddings1_cbow)
df_cbow2 = pd.DataFrame(data=embeddings2_cbow)

#행 == 형태소의 개수, 열 == 벡터 사이즈
print(df_cbow1)
print(f'\n{"-"*80}\n')
print(df_cbow2)
# 최대 값을 이용하여 문장별 임베딩 행렬 생성

# 데이터 프레임 > 컬럼별 최대 값 추출 > df.max().values > 2차원 배열 생성
max_matrix_cbow1 = df_cbow1.std().values.reshape(1, 10)
max_matrix_cbow2 = df_cbow2.std().values.reshape(1, 10)

print(max_matrix_cbow1)
print(max_matrix_cbow2)

#코사인 유사도 계산
cbow_cs = cosine_similarity(max_matrix_cbow1, max_matrix_cbow2)
print(cbow_cs)

word2vec Code 3 (skip-gram 모델)

vocab_sg = sikp_gram.wv.key_to_index
print(vocab_sg)
print("-"*80)

print(sikp_gram.wv.vectors)
# skip-gram(epochs = 50)

# 결과를 저장할 빈 리스트 생성
embeddings1_sg = []
embeddings2_sg = []

# 첫번쨰 문장 > 형태소 별 임베딩 베거 추출 및 저장
for morph in token_sentence[0]:
  morph_embedding = sikp_gram.wv[morph]
  embeddings1_sg.append(morph_embedding)

# 두번쨰 문장 > 형태소 별 임베딩 베거 추출 및 저장
for morph in token_sentence[1]:
  morph_embedding = sikp_gram.wv[morph]
  embeddings2_sg.append(morph_embedding)

print(embeddings1_sg)
print(f'\n{"-"*80}\n')
print(embeddings2_sg)
# 문장 내 형태소 별 임베딩 벡터 > 데이터프레임 생성

import pandas as pd #라이브러리 임폴트

# 문장의 임베딩 벡터 > 데이터프레임 생성
df_sg1 = pd.DataFrame(data=embeddings1_sg)
df_sg2 = pd.DataFrame(data=embeddings2_sg)

#행 == 형태소의 개수, 열 == 벡터 사이즈
print(df_sg1)
print(f'\n{"-"*80}\n')
print(df_sg2)
# 최대 값을 이용하여 문장별 임베딩 행렬 생성

# 데이터 프레임 > 컬럼별 최대 값 추출 > df.max().values > 2차원 배열 생성
max_matrix_sg1 = df_sg1.std().values.reshape(1, 10)
max_matrix_sg2 = df_sg2.std().values.reshape(1, 10)

print(max_matrix_sg1)
print(max_matrix_sg2)

#코사인 유사도 계산
sg_cs = cosine_similarity(max_matrix_sg1, max_matrix_sg2)
print(sg_cs)

epochs(학습)의 의미

  • 정답을 예측하는 진행하면서 에러(손실)이 발생함
  • 진행할수록 손실(E)의 값들의 평균을 냄 > 이 평균값(E)이 줄어드는 방향으로 가중치를 업데이트함
  • 손실(E)는 주로 교차 엔트로피 손실로 계산되며, 모델이 문맥과 중심 단어 간의 관계를 얼마나 잘 예측하고 있는지를 측정함
  • 최종 손실(E)를 최소화 하기 위해 역전파와 경사 하강법을 사용하여 가중치를 업데이트함
  • epochs의 값(학습의 횟수)만큼 이 과정을 반복함

+ 좋은 학습 모델 - 학습하는 난이도가 높을수록 성능이 좋아짐 (더 많은 업데이트와 데이터를 필요로 할 수록 좋은 모델)








참고자료

pip gensim 라이브러리

gensim 라이브러리 코드 (GitHub)

profile
AI & Robotics

0개의 댓글