본문 바로가기

파이썬 & 머신러닝과 딥러닝

CountVectorizer, Word2Vec, Autoencoder 모델

 

1. CountVectorizer

특징

  • 빈도 기반: CountVectorizer는 텍스트 데이터에서 단어의 빈도를 계산해 벡터로 표현
  • 희소 행렬 생성: 각 단어가 전체 텍스트 내에서 얼마나 자주 등장하는지를 세어, 희소 행렬 형태로 표현
  • 고정된 벡터 크기: 전체 코퍼스(문서 집합)에 있는 고유한 단어 수만큼의 차원을 가지며, 각 단어는 벡터에서 고유한 인덱스를 차지
  • 문서 간 유사도 계산에 유리: 단순한 빈도 계산이므로, 문서 간의 코사인 유사도 등 간단한 연산으로 유사도 계산이 가능
from sklearn.feature_extraction.text import CountVectorizer

box = []
for i in data["서비스명"] + " " + data['소분류']:
    box.append(i)

cv = CountVectorizer()

a = cv.fit_transform(box)
a.toarray()

 

2. Word2Vec

특징

  • 의미 기반 임베딩: Word2Vec은 단어의 의미적 유사성을 반영하여 벡터화. 단어를 벡터 공간에 임베딩하여, 유사한 의미의 단어들이 가까운 벡터로 위치하도록 학습.
  • 사전 학습 필요: Word2Vec은 신경망을 사용하여 코퍼스 내에서 단어를 학습하고, 벡터 공간에서 위치를 조정.
  • 미리 정의된 벡터 크기: Word2Vec 벡터의 차원은 일반적으로 100~300차원으로 설정되며, 텍스트 내 고유한 단어 수와 무관.
  • 단어 간 유사성 계산에 유리: 유사한 단어가 유사한 벡터로 위치하기 때문에 단어 간 유사성이나 의미적 관계를 쉽게 계산할 수 있다.

Word2vec에는 CBOW(Continuous bag of words)와 Skip-gram 두 가지 학습 방식이 있다.

CBOW : 주변 단어(context word)를 통해 중심 단어(center word)를 유추하는 방식

Skip-gram : 중심 단어를 통해 주변 단어를 추출하는 방식

 

버트(BERT) 모델 : 버트모델은 다계층 양방향 트랜스포머 인코더모델로, 문장 간 유사도 측정 및 문장 인코딩과 같은 자연 어 처리 분야에서 많이 사용되고 있는 임베딩모델이다

버트 모델은 Word2vec과 달리 동음이의어나 중의적인 단어에 유연하게 대응할 수 있는 특성을 가진다

버트는 3개의 입력층 레이어를 가지고있다. 토큰 임베딩(token embedding), 세그먼트 임베딩(segment embedding), 포지션 임베딩(position embedding)으로 구성되어 있으며, 문장과 문장은 두 가지 특수 토큰 (CLS, SEP)을 사용해 구분된다. 버트 모델의 입력 데이터는 문장 단위이며, 출력 데이터는 입력 문장에 대한 고정 길이 벡터이다. 단어 단위를 입력으로 사용할 수도있지만, 문맥기반의 임베딩 벡터를 사용 할 수 있는 버트의 이점이없어진다. 본 연구에서는 코사인 유사도 분석을 위한 워드 임베딩 기법을 위해 버트 모델을 사용한다.


Word2Vec + Autoencoder 모델로 구현한 추천 시스템

Autoencoder 모델

: 입력 데이터를 저차원으로 압축했다가 다시 복원하는 과정을 통해 입력 데이터의 특징을 학습하는 비지도 학습 모델

판매자 속성(예: 판매금액, 수수료율, 추가결제금액 등)을 Autoencoder를 사용하여 저차원으로 압축해 유사한 속성을 가진 판매자끼리의 패턴을 학습한 후, 신규 고객에게 가장 유사한 판매자를 추천할 수 있습니다. 이 방식은 딥러닝을 이용해 판매자 특징을 학습하는 접근

 

구현 과정

  1. 데이터 전처리 및 Word2Vec 임베딩 생성
  • 텍스트 데이터 전처리: 서비스명과 소분류의 텍스트를 불용어 제거 및 특수 문자 제거와 함께 정제하여, 더 깨끗한 데이터를 Word2Vec 학습에 사용합
  • Word2Vec 모델 학습: 전처리된 텍스트 데이터를 Word2Vec으로 학습하여, 각 서비스명과 소분류를 벡터화
  • 벡터 병합: 생성된 Word2Vec 벡터를 판매자 수치적 특징 데이터와 결합하여, 각 판매자의 전체 속성 벡터를 구성
  1. Autoencoder 모델 설계 및 학습
  • 판매자 속성 데이터 준비: Word2Vec으로 생성된 벡터와 수치적 속성을 포함하여 판매자의 전체 속성 벡터를 만듦
  • Autoencoder 모델 설계 및 학습: 더 깊은 네트워크를 사용하거나, 과적합 방지를 위해 검증 데이터와 함께 학습을 진행하여, 최적의 저차원 임베딩을 학습 3.유사도 계산 및 추천 수행
  • Autoencoder 임베딩 추출: 학습된 Autoencoder 모델을 통해 각 판매자의 저차원 임베딩 벡터를 추출
  • 코사인 유사도 계산: 각 판매자의 저차원 임베딩 벡터와 Word2Vec 임베딩 벡터 간 코사인 유사도를 계산하여, 유사도가 높은 판매자들을 확인
  • 추천 순위 생성: 신규 고객에게 유사한 판매자를 추천할 수 있도록, 계산된 유사도에 따라 추천 순위를 생성

1. 데이터 전처리 및 Word2Vec 임베딩 생성

  • 텍스트 데이터 전처리: 서비스명과 소분류의 텍스트 데이터를 정제(특수문자 제거 등)하고 토큰화
  • Word2Vec 모델 학습: 전처리한 텍스트 데이터를 Word2Vec으로 학습하여, 각 서비스명과 소분류를 벡터화(유사한 서비스 간의 관계를 표현하는 벡터 생성)
  • 벡터 병합: 생성된 Word2Vec 벡터를 고객-판매자 매칭 데이터와 병합하여 각 판매자의 서비스명 및 소분류 특징을 추가
from gensim.models import Word2Vec
import pandas as pd

# 데이터 로드
data = pd.read_csv('subcate.csv', low_memory=False)

# 1. 텍스트 데이터 전처리
# 서비스명과 소분류 필드를 문자열로 변환한 후 텍스트 데이터로 결합
data['서비스명'] = data['서비스명'].astype(str)
data['소분류'] = data['소분류'].astype(str)

# Word2Vec 학습을 위해 서비스명과 소분류 텍스트를 결합하여 학습 데이터 준비
text_corpus = data['서비스명'].tolist() + data['소분류'].tolist()
text_corpus = [text.split() for text in text_corpus]  # 각 텍스트를 단어 단위로 분리

# 2. Word2Vec 모델 학습
# 서비스명과 소분류의 텍스트를 Word2Vec 모델로 벡터화하여 유사도를 측정할 수 있게 학습
word2vec_model = Word2Vec(sentences=text_corpus, vector_size=100, window=5, min_count=2, sg=1)

# 학습된 Word2Vec 모델을 저장하거나 필요한 경우 바로 활용 가능
word2vec_model.save("word2vec_service_model")

# 예시: '서비스명'에서 일부 단어의 벡터 출력
#sample_vectors = {word: word2vec_model.wv[word] for word in list(word2vec_model.wv.index_to_key)[:5]}
#print(sample_vectors)

 

2. Autoencoder 모델 설계 및 학습

  • 판매자 속성 데이터 준비: 판매금액, 수수료율, 추가결제금액, 평점, 판매수 등 주요 수치 데이터를 준비, Word2Vec으로 생성된 텍스트 임베딩도 속성에 추가하여 판매자의 전체적인 속성 벡터를 형성
  • Autoencoder 구조 설계: 입력 데이터를 저차원 벡터로 압축하는 Autoencoder를 설계, 입력 레이어에서 시작하여 점점 더 적은 노드 수로 압축하고, 중앙에 저차원 임베딩 벡터가 형성되도록
  • Autoencoder 학습: 판매자 데이터를 사용하여 모델을 학습, 이 과정에서 모델은 비슷한 속성을 가진 판매자끼리 유사한 저차원 임베딩을 형성
from gensim.models import Word2Vec
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense
from tqdm import tqdm

# 데이터 준비 및 전처리 코드 (이전과 동일)
data = pd.read_csv('subcate.csv', low_memory=False)

numeric_features = ['판매금액', '수수료율', '추가결제금액', '평점', '판매수']
X_numeric = data[numeric_features]

data['서비스명'] = data['서비스명'].astype(str)
data['소분류'] = data['소분류'].astype(str)

# 기존에 학습된 Word2Vec 모델을 로드
word2vec_service_model = Word2Vec.load("word2vec_service_model")

# 서비스명과 소분류의 단어들을 벡터로 변환하여 평균 벡터를 생성
service_vectors = [
    np.mean(
        [word2vec_service_model.wv[word] for word in f"{row['서비스명']} {row['소분류']}".split() 
         if word in word2vec_service_model.wv], 
        axis=0
    ) if any(word in word2vec_service_model.wv for word in f"{row['서비스명']} {row['소분류']}".split())
    else np.zeros(word2vec_service_model.vector_size)
    for _, row in data.iterrows()
]

# Word2Vec 벡터를 수치적 특징과 결합
X_word2vec = np.array(service_vectors)
X = np.hstack((X_numeric, X_word2vec))

# 데이터 스케일링
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Autoencoder 모델 정의
input_dim = X_scaled.shape[1]
input_layer = Input(shape=(input_dim,))
encoded = Dense(64, activation='relu')(input_layer)
encoded = Dense(32, activation='relu')(encoded)
decoded = Dense(64, activation='relu')(encoded)
output_layer = Dense(input_dim, activation='linear')(decoded)

autoencoder = Model(inputs=input_layer, outputs=output_layer)
autoencoder.compile(optimizer='adam', loss='mse', metrics = ['mae'])

# 모델 학습: fit 메서드 사용
epochs = 20
batch_size = 256

losses = []

# tqdm을 이용해 fit 과정 진행 상황 표시
with tqdm(total=epochs, desc="Training Progress") as pbar:
    for epoch in range(epochs):
        history = autoencoder.fit(X_scaled, X_scaled, epochs=1, batch_size=batch_size, verbose=0)
        avg_epoch_loss = history.history['loss'][0]
        pbar.update(1)  # 에포크 단위로 진행 바 업데이트
        pbar.set_postfix({'Epoch': epoch + 1, 'Loss': f"{avg_epoch_loss:.4f}"})
        losses.append(avg_epoch_loss)
#  Autoencoder 모델의 학습 손실 값(loss)을 시각화
import matplotlib.pyplot as plt

plt.plot(losses)
plt.show()

 

3.유사도 계산 및 추천 수행

  • Autoencoder 임베딩 추출: 학습된 Autoencoder 모델을 통해 각 판매자 데이터에 대해 저차원 임베딩 벡터를 추출
  • 코사인 유사도 계산: 각 판매자의 저차원 임베딩 벡터와 Word2Vec 임베딩 벡터 간 코사인 유사도를 계산하여, 유사도가 높은 판매자들을 확인
  • 추천 순위 생성: 신규 고객에게 유사한 판매자를 추천할 수 있도록, 계산된 유사도에 따라 추천 순위를 생성
  • 사용자에게 입력 받은 검색어로 추천 서비스 출력 후 그 서비스 관련 다른 서비스들도 추천 받을 수 있음
from gensim.models import Word2Vec
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
import pandas as pd

# Word2Vec 모델 로드 (이미 학습된 모델 사용)
word2vec_model = Word2Vec.load("word2vec_service_model")

# 검색어 기반 추천 함수 (중복 서비스명 제외)
def recommend_sellers_by_keyword(search_keyword, top_n=5):
    # 검색어를 분리하여 Word2Vec 벡터화
    search_words = search_keyword.split()
    keyword_vector = np.mean([word2vec_model.wv[word] for word in search_words if word in word2vec_model.wv], axis=0)
    
    # 검색어와 모든 서비스명 간 유사도 계산
    similarities = cosine_similarity([keyword_vector], X_word2vec).flatten()
    top_indices = np.argsort(-similarities)  # 유사도가 높은 순서로 정렬
    
    # 유사도에 기반한 추천 결과 생성 (중복 서비스명 제외)
    recommendations = []
    seen_services = set()  # 중복 서비스명 걸러내기 위한 집합
    for idx in top_indices:
        service_name = data.iloc[idx]['서비스명']
        if service_name not in seen_services:
            seen_services.add(service_name)
            recommendations.append({
                "서비스명": service_name,
                "판매자": data.iloc[idx]['판매자'],
                "유사도": similarities[idx]
            })
        if len(recommendations) >= top_n:
            break
    return recommendations

# 특정 서비스와 유사한 추가 추천 생성 함수 (자기 자신 제외)
def find_related_services(service_name, related_n=3):
    related_recommendations = []
    seen_services = set()  # 중복 서비스명 걸러내기 위한 집합

    # 선택된 서비스명에 해당하는 벡터 찾기
    service_index = data[data['서비스명'] == service_name].index[0]
    service_vector = X_word2vec[service_index]
    service_similarities = cosine_similarity([service_vector], X_word2vec).flatten()
    related_indices = np.argsort(-service_similarities)[1:]  # 자기 자신 제외한 유사한 서비스 순서

    # 유사도가 높은 다른 서비스 추천
    for idx in related_indices:
        related_service_name = data.iloc[idx]['서비스명']
        if related_service_name not in seen_services and related_service_name != service_name:
            seen_services.add(related_service_name)
            related_recommendations.append({
                "서비스명": related_service_name,
                "판매자": data.iloc[idx]['판매자'],
                "유사도": service_similarities[idx]
            })
        if len(related_recommendations) >= related_n:
            break

    return related_recommendations

# 검색어와 추가 추천을 받을 서비스 선택
search_keyword = input("검색어를 입력하세요: ")  # 검색어 입력
print("\n검색어:", search_keyword)

# 검색어와 유사한 상위 서비스 및 판매자 추천
keyword_recommendations = recommend_sellers_by_keyword(search_keyword)

print("\n검색어와 유사한 상위 5개 서비스 및 판매자 추천:")
for i, rec in enumerate(keyword_recommendations, 1):
    print(f"{i}. 서비스명: {rec['서비스명']}, 판매자: {rec['판매자']}, 유사도: {rec['유사도']:.4f}")

# 사용자에게 유사한 추가 서비스를 추천받고 싶은 서비스 선택 요청
selected_service_index = int(input("\n유사한 추가 서비스를 추천받고 싶은 서비스 번호를 선택하세요 (1-5): ")) - 1
selected_service_name = keyword_recommendations[selected_service_index]["서비스명"]

# 선택된 서비스와 유사한 추가 서비스 추천
print(f"\n'{selected_service_name}'와 유사한 추가 서비스 추천:")
related_recommendations = find_related_services(selected_service_name)
for i, rec in enumerate(related_recommendations, 1):
    print(f"{i}. 서비스명: {rec['서비스명']}, 판매자: {rec['판매자']}, 유사도: {rec['유사도']:.4f}")