텍스트 분석/추천시스템

2024. 5. 6. 19:46책/머신러닝 완벽가이드

텍스트 분석

텍스트 분석 : 비정형 데이터인 텍스트를 분석하는 것   텍스트 분석 수행 프로세스 텍스트 사전 준비작업(텍스트 전처리) -> 피처 벡터화/추출 -> ML 모델 수립 및 학습/예측/평가    NTLK : 파이썬의 가장  대표적인 NLP패키지 Gensim : 토픽 모델링 분야에서 가장 두각을 나타내는 패키지 SoaCy : 뛰어난 수행 성능으로 최근 가장 주목을 받는 NLP 패키지   텍스트 사전 준비 작업(텍스트 전처리) - 텍스트 정규화

  • 클렌징 : 텍스트에서 분석에 오히려 방해가 되는 불필요한 문자, 기호 등을 사전에 제거하는 작업
  • 토큰화 : 문서에서 문장을 분리하는 문장 토큰화, 문장에서 단어를 토큰으로 분리하는 단어 토큰화로 나눌 수 있음
  • 필터링/스톱 워드 제거/철자 수정
  • stemming
  • lemmatizaion   텍스트 토큰화

`from nltk import sent_tokenize text_sample = 'The Matrix is everywhere its all around us, here even in this room. \ You can see it out your window or on your television. \ You feel it when you go to work, or go to church or pay your taxes.' sentences = sent_tokenize(text=text_sample) print(type(sentences),len(sentences)) print(sentences)

<class 'list'> 3 ['The Matrix is everywhere its all around us, here even in this room.', 'You can see it out your window or on your television.', 'You feel it when you go to work, or go to church or pay your taxes.']`

`from nltk import word_tokenize

sentence = "The Matrix is everywhere its all around us, here even in this room." words = word_tokenize(sentence) print(type(words), len(words)) print(words)

<class 'list'> 15 ['The', 'Matrix', 'is', 'everywhere', 'its', 'all', 'around', 'us', ',', 'here', 'even', 'in', 'this', 'room', '.']`

`from nltk import word_tokenize, sent_tokenize

#여러개의 문장으로 된 입력 데이터를 문장별로 단어 토큰화 만드는 함수 생성 def tokenize_text(text):

*# 문장별로 분리 토큰*
sentences = sent_tokenize(text)
*# 분리된 문장별 단어 토큰화*
word_tokens = [word_tokenize(sentence) for sentence in sentences]
return word_tokens

#여러 문장들에 대해 문장별 단어 토큰화 수행. word_tokens = tokenize_text(text_sample) print(type(word_tokens),len(word_tokens)) print(word_tokens)

<class 'list'> 3 [['The', 'Matrix', 'is', 'everywhere', 'its', 'all', 'around', 'us', ',', 'here', 'even', 'in', 'this', 'room', '.'], ['You', 'can', 'see', 'it', 'out', 'your', 'window', 'or', 'on', 'your', 'television', '.'], ['You', 'feel', 'it', 'when', 'you', 'go', 'to', 'work', ',', 'or', 'go', 'to', 'church', 'or', 'pay', 'your', 'taxes', '.']]`   문장을 단어별로 하나씩 토큰화 할 경우 문맥적인 의미를 무시될 수 밖에 없음. 이런 문제를 조금이라도 해결해 보고자 하는 것이 n-gram

`from nltk import ngrams

sentence = "The Matrix is everywhere its all around us, here even in this room." words = word_tokenize(sentence)

all_ngrams = ngrams(words, 2) ngrams = [ngram for ngram in all_ngrams] print(ngrams)

[('The', 'Matrix'), ('Matrix', 'is'), ('is', 'everywhere'), ('everywhere', 'its'), ('its', 'all'), ('all', 'around'), ('around', 'us'), ('us', ','), (',', 'here'), ('here', 'even'), ('even', 'in'), ('in', 'this'), ('this', 'room'), ('room', '.')]` ('the' , 'matrix'),('matrix', 'is')  처럼 n개 단어 크기 윈도우를 만들어 문장의 처음부터 오른쪽으로 움직이면서 토큰화 수행      스톱 워드 제거 스톱 워드는 분석에 큰 의미가 없는 단어를 지칭(ex : is, the, a ,will...)

`import nltk nltk.download('stopwords') ##########3print('영어 stop words 갯수:',len(nltk.corpus.stopwords.words('english'))) print(nltk.corpus.stopwords.words('english')[:40])

영어 stop words 갯수: 179 ['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're", "you've", "you'll", "you'd", 'your', 'yours', 'yourself', 'yourselves', 'he', 'him', 'his', 'himself', 'she', "she's", 'her', 'hers', 'herself', 'it', "it's", 'its', 'itself', 'they', 'them', 'their', 'theirs', 'themselves', 'what', 'which', 'who', 'whom', 'this']

##########3 import nltk

stopwords = nltk.corpus.stopwords.words('english') all_tokens = [] # 위 예제의 3개의 문장별로 얻은 word_tokens list 에 대해 stop word 제거 Loopfor sentence in word_tokens: filtered_words=[] # 개별 문장별로 tokenize된 sentence list에 대해 stop word 제거 Loopfor word in sentence: #소문자로 모두 변환합니다. word = word.lower() # tokenize 된 개별 word가 stop words 들의 단어에 포함되지 않으면 word_tokens에 추가if word not in stopwords: filtered_words.append(word) all_tokens.append(filtered_words)

print(all_tokens)

[['matrix', 'everywhere', 'around', 'us', ',', 'even', 'room', '.'], ['see', 'window', 'television', '.'], ['feel', 'go', 'work', ',', 'go', 'church', 'pay', 'taxes', '.']]`   Stemming과 Lemmatization

  • 문법적으로 또는 의미적으로 변화하는 단어의 원형을 찾는 것
  • 두 기능 모두 원형 단어를 찾는다는 목적은 유사하지만, Lemmatization이 Stemming보다 정교하며 의미론적인 기반에서 단어의 원형을 찾음

`from nltk.stem import LancasterStemmer stemmer = LancasterStemmer()

print(stemmer.stem('working'),stemmer.stem('works'),stemmer.stem('worked')) print(stemmer.stem('amusing'),stemmer.stem('amuses'),stemmer.stem('amused')) print(stemmer.stem('happier'),stemmer.stem('happiest')) print(stemmer.stem('fancier'),stemmer.stem('fanciest'))

work work work amus amus amus happy happiest fant fanciest ############################# from nltk.stem import WordNetLemmatizer

lemma = WordNetLemmatizer() print(lemma.lemmatize('amusing','v'),lemma.lemmatize('amuses','v'),lemma.lemmatize('amused','v')) print(lemma.lemmatize('happier','a'),lemma.lemmatize('happiest','a')) print(lemma.lemmatize('fancier','a'),lemma.lemmatize('fanciest','a'))

amuse amuse amuse happy happy fancy fancy` WordNeetLemmatizer를 이용한 Lemmatization는 정확한 원형 추출을 위해 단어의 '품사'를 입력해줘야함   BOW(Bag of words) 문서가 가지는 모든 단어를 문맥이나 순서를 무시하고 일괄적으로 단어에 대해 빈도 값을 부여해 피처 값을 추출하는 모델   장점 : 쉽고 빠른 구축 단점 : 문맥 의미 반영 부족(단어의 순서를 고려하지 않기 때문) / 희소 행렬 문제     BOW 피처 벡터화 머신러닝 알고리즘은 일반적으로 숫자형 피처를 데이터로 입력받아 동작하기 때문에 텍스트와 같은 데이터는 머신러닝 알고리즘에 바로 입력할 수 없음. 따라서 텍스트는 특정 의미를 가지는 숫자형 값인 벡터 값으로 변환해야 하는데, 이러한 변환을 피처 벡터화라고 함(피터 벡터화는 넓은 범위의 피처 추출에 포함)   피처 벡터화 방법 -카운트 기반의 벡터화  -TF-IDF 기반의 벡터화   count를 부여하는 경우를 카운트 벡터화라고 하며 카운트 값이 높을수록 중요한 단어로 인식됨. 하지만 카운트만 부여 시 그 문서의 특징을 나타내기보다는 언어의 특성상 문장에서 자주 사용될 수 밖에 없는 단어까지 높은 값을 부여하게 됨. 이러한 문제를 보완하기 위해 TF-IDF 벡터화를 사용하며, 이는 개별 문서에서 자주 나타나는 단어에 높은 가중치를 주되, 모든 문서에서 전반적으로 자주 나타나는 단어에 대해서는 페널티를 주는 방식으로 값을 부여함.   CountVectorizer를 이용한 피처 벡터화 사전 데이터 가공 - 토큰화 - 텍스트 정규화 - 피처 벡터화   희소 행렬  COO 형식

`import numpy as np

dense = np.array( [ [ 3, 0, 1 ], [0, 2, 0 ] ] )

from scipy import sparse

# 0 이 아닌 데이터 추출 data = np.array([3,1,2])

# 행 위치와 열 위치를 각각 array로 생성 row_pos = np.array([0,0,1]) col_pos = np.array([0,2,1])

# sparse 패키지의 coo_matrix를 이용하여 COO 형식으로 희소 행렬 생성 sparse_coo = sparse.coo_matrix((data, (row_pos,col_pos)))

print(type(sparse_coo)) print(sparse_coo) dense01=sparse_coo.toarray() print(type(dense01),"\n", dense01)

<class 'scipy.sparse._coo.coo_matrix'> (0, 0) 3 (0, 2) 1 (1, 1) 2 <class 'numpy.ndarray'> [[3 0 1] [0 2 0]]`   CSR 형식

`from scipy import sparse

dense2 = np.array([[0,0,1,0,0,5], [1,4,0,3,2,5], [0,6,0,3,0,0], [2,0,0,0,0,0], [0,0,0,7,0,8], [1,0,0,0,0,0]])

# 0 이 아닌 데이터 추출 data2 = np.array([1, 5, 1, 4, 3, 2, 5, 6, 3, 2, 7, 8, 1])

# 행 위치와 열 위치를 각각 array로 생성 row_pos = np.array([0, 0, 1, 1, 1, 1, 1, 2, 2, 3, 4, 4, 5]) col_pos = np.array([2, 5, 0, 1, 3, 4, 5, 1, 3, 0, 3, 5, 0])

# COO 형식으로 변환 sparse_coo = sparse.coo_matrix((data2, (row_pos,col_pos)))

# 행 위치 배열의 고유한 값들의 시작 위치 인덱스를 배열로 생성 row_pos_ind = np.array([0, 2, 7, 9, 10, 12, 13])

# CSR 형식으로 변환 sparse_csr = sparse.csr_matrix((data2, col_pos, row_pos_ind))

print('COO 변환된 데이터가 제대로 되었는지 다시 Dense로 출력 확인') print(sparse_coo.toarray()) print('CSR 변환된 데이터가 제대로 되었는지 다시 Dense로 출력 확인') print(sparse_csr.toarray())

COO 변환된 데이터가 제대로 되었는지 다시 Dense로 출력 확인 [[0 0 1 0 0 5] [1 4 0 3 2 5] [0 6 0 3 0 0] [2 0 0 0 0 0] [0 0 0 7 0 8] [1 0 0 0 0 0]] CSR 변환된 데이터가 제대로 되었는지 다시 Dense로 출력 확인 [[0 0 1 0 0 5] [1 4 0 3 2 5] [0 6 0 3 0 0] [2 0 0 0 0 0] [0 0 0 7 0 8] [1 0 0 0 0 0]]

#########3print(sparse_csr)

(0, 2) 1 (0, 5) 5 (1, 0) 1 (1, 1) 4 (1, 3) 3 (1, 4) 2 (1, 5) 5 (2, 1) 6 (2, 3) 3 (3, 0) 2 (4, 3) 7 (4, 5) 8 (5, 0) 1

########

dense3 = np.array([[0,0,1,0,0,5], [1,4,0,3,2,5], [0,6,0,3,0,0], [2,0,0,0,0,0], [0,0,0,7,0,8], [1,0,0,0,0,0]])

coo = sparse.coo_matrix(dense3) csr = sparse.csr_matrix(dense3)`     사이킷런 파이프라인 사용 및 GridSearchCV와의 결합 pipeline을 이용하면 피처 벡터화와 ML 알고리즘 학습/예측을 위한 코드 작성을 한 번에 진행할 수 있음 이는 데이터 전처리와 머신러닝 학습 과정을 통일된 API 기반에서 처리할 수 있어 더 직관적인 ML 모델 코드를 생성할 수 있음 또한 수행 시간도 절약 가능   감성분석 문서 내 텍스트가 나타내는 여러 가지 주관적인 단어와 문맥을 기반으로 감성 수치를 계산 하는 방법을 이용함 지도 학습 : 학습 데이터와 타깃 레이블 값을 기반으로 감성 분석 학습을 수행한 뒤 이를 기반으로 다른 데이터의 감석 분석을 예측하는 방법 비지도학습 : 'Lexicon'이라는 일종의 감성 어휘 사전 이용

`import pandas as pd

review_df = pd.read_csv('./labeledTrainData.tsv', header=0, sep="\t", quoting=3) review_df.head(3)

print(review_df['review'][0])`

`import re

#<br> html 태그는 replace 함수로 공백으로 변환 review_df['review'] = review_df['review'].str.replace('<br />', ' ')

#파이썬의 정규표현식 모듈인 re를 이용해 영어 문자열이 아닌 문자는 모두 공백으로 변환 review_df['review'] = review_df['review'].apply(lambda x : re.sub('[^a-zA-Z]', ' ', x))

from sklearn.model_selection import train_test_split

class_df = review_df['sentiment'] # like x feature_df = review_df.drop(['id', 'sentiment'], axis=1, inplace=False) #like y

X_train,X_test,y_train,y_test = train_test_split(feature_df, class_df, test_size=0.3, random_state=156)

X_train.shape, X_test.shape

((17500, 1), (7500, 1)) ############################# pipeline 객체를 이용한 피처 벡터화, ML 학습/예측/평가

from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer from sklearn.pipeline import Pipeline from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, roc_auc_score

#스톱 워드는 English, filtering, ngram은 (1,2)로 설정해 CountVectorizer 수행#LogisticRegression 의 C는 10으로 설정 pipeline = Pipeline([ ('cnt_vect', CountVectorizer(stop_words='english', ngram_range=(1,2))), ('lr_clf', LogisticRegression(solver='liblinear', C=10))])

#Pipeline 객체를 이용해 fit(),predict()로 학습/예측 수행, predict_prob()는 roc_auc 때문에 수행 pipeline.fit(X_train['review'], y_train) pred = pipeline.predict(X_test['review']) pred_probs = pipeline.predict_proba(X_test['review'])[:,1]

print('예측 정확도는 {0:.4f}, ROC-AUC는 {1:.4f}'.format(accuracy_score(y_test, pred), roc_auc_score(y_test, pred_probs)))

예측 정확도는 0.8859, ROC-AUC는 0.9503 #############################스톱 워드는 English, filtering, ngram은 (1,2)로 설정해 TF-IDF 벡터화 수행#LogisticRegression 의 C는 10으로 설정

pipeline = Pipeline([ ('tfidf_vect', TfidfVectorizer(stop_words='english', ngram_range=(1,2))), ('lr_clf', LogisticRegression(solver='liblinear', C=10))])

pipeline.fit(X_train['review'], y_train) pred = pipeline.predict(X_test['review']) pred_probs = pipeline.predict_proba(X_test['review'])[:,1]

print('예측 정확도는 {0:.4f}, ROC-AUC는 {1:.4f}'.format(accuracy_score(y_test, pred), roc_auc_score(y_test, pred_probs)))

예측 정확도는 0.8936, ROC-AUC는 0.9598

############################`

`from nltk.corpus import wordnet as wn

term = 'present'

# 'present'라는 단어로 wordnet의 synsets 생성. synsets = wn.synsets(term) print('synsets() 반환 type :', type(synsets)) print('synsets() 반환 값 갯수:', len(synsets)) print('synsets() 반환 값 :', synsets)

#present.n.01(like 포스번호) 에서 present는 의미, n은 명사(=품사) , #present가 가지는 다양한 의미를 구분하는 번호for synset in synsets : print('##### Synset name : ', synset.name(),'#####') print('POS :',synset.lexname()) print('Definition:',synset.definition()) print('Lemmas:',synset.lemma_names())

import pandas as pd # synset 객체를 단어별로 생성합니다. tree = wn.synset('tree.n.01') lion = wn.synset('lion.n.01') tiger = wn.synset('tiger.n.02') cat = wn.synset('cat.n.01') dog = wn.synset('dog.n.01')

entities = [tree , lion , tiger , cat , dog] similarities = [] entity_names = [ entity.name().split('.')[0] for entity in entities]

# 단어별 synset 들을 iteration 하면서 다른 단어들의 synset과 유사도를 측정합니다. for entity in entities: similarity = [ round(entity.path_similarity(compared_entity), 2) for compared_entity in entities ] similarities.append(similarity)

# 개별 단어별 synset과 다른 단어의 synset과의 유사도를 DataFrame형태로 저장합니다.
similarity_df = pd.DataFrame(similarities , columns=entity_names,index=entity_names) similarity_df

import nltk from nltk.corpus import sentiwordnet as swn

senti_synsets = list(swn.senti_synsets('slow')) print('senti_synsets() 반환 type :', type(senti_synsets)) print('senti_synsets() 반환 값 갯수:', len(senti_synsets)) print('senti_synsets() 반환 값 :', senti_synsets)

import nltk from nltk.corpus import sentiwordnet as swn

father = swn.senti_synset('father.n.01') print('father 긍정감성 지수: ', father.pos_score()) print('father 부정감성 지수: ', father.neg_score()) print('father 객관성 지수: ', father.obj_score()) print('\n') fabulous = swn.senti_synset('fabulous.a.01') print('fabulous 긍정감성 지수: ',fabulous .pos_score()) print('fabulous 부정감성 지수: ',fabulous .neg_score())`   WordNet은 다양한 상황에서 같은 어휘라도 다르게 사용되는 어휘의 시맨틱 정보를 제공하며, 이를 위해 각각의 품사로 구성된 개별 단어를 Synset이라는 개념을 이용해 표현함. Synset은 단순한 하나의 단어가 아니라 그 단어가 가지는 문맥, 시맨틱 정보를 제공하는 WordNet의 핵심 개념임   NTLK의 감성 사전이 감성에 대한 휼륭한 사전 역할을 제공하긴 하지만, 예측 성능은 그리 좋지 못하다는 단점이 존재. 때문에 실무에서는 NLTK 패키지가 아닌 다른 감성 사전을 적용함. 대표적으로 SentiWordNet, VADER, Pattern 감성 사전이 있음   SentiWordNet

  • NLTK 패키지의 WordNet과 유사하게 감성 단어 전용의 WordNet을 구현한 것.
  • WordNet의 Synset별로 3가지 감성 점수를 할당(긍정 감성 지수, 부정 감성 지수, 객관성 지수)   SentiWordNet을 이용한 영화 감상평 감성 분석 순서
  1. 문서를 문장 단위로 분해
  2. 다시 문장을 단어 단위로 토큰화하고 품사 태깅
  3. 품사 태깅된 단어 기반으로 synset 객체와 senti_synset 객체를 생성
  4. senti_synset 객체에서 긍정 감성/부정 감성 지수를 구하고 이를 모두 합산해 특정 임계치 값 이상일 때 긍정 감성으로 , 그렇지 않을 때는 부정 감성으로 결정   VADER  - 주로 소셜 미디어의 텍스트에 대한 감성 분석을 제공하기 위한 패키지   Pattern  - 예측 성능 측면에서 가장 주목받는 패키지       토픽 모델링
  • 문서 집합에 숨어 있는 주제를 찾아내는 것
  • 사람이 수행하는 토픽 모델링은 더 함축적인 의미로 문장을 요약하는 것에 반해, 머신러닝 기반의 토픽 모델은 숨겨진 주제를 효과적으로 표현할 수 있는 중심 단어를 함축적으로 추출 자주 사용되는 기법은 LSA(Latent semantic analysis)와 LDA(Latent dirichlet allocation)임   문서 군집화
  • 비슷한 텍스트 구성의 문서를 군집화하는 것
  • 문서 군집화는 동일한 군집에 속하는 문서를 같은 카테고리 소속으로 분류할 수 있으므로 앞에서 소개한 텍스트 분류 기반의 문서 분류와 유사함. 하지만 텍스트 분류 기반의 문서 분류는 사전에 결정 카테고리 값을 가진 학습 데이터 세트가 필요한 데 반해, 문서 군집화는 학습 데이터 세트가 필요없는 비지도학습 기반으로 동작   문서 유사도 문서 유사도 측정 방법은 코사인 유사도를 사용하며, 벡터와 벡터 간의 유사도를 비교할 때 벡터의 크기보다는 벡터의 상호 방향성이 얼마나 유사한지에 기반함. 즉, 코사인 유사도는 두 벡터 사이의 사잇각을 구해서 얼마나 유사한지 수치로 적용한 것

 

 

 

 

추천 시스템

추천 시스템

콘텐츠 기반 필터링

협업 필터링 -> 최근접 이웃 협업 필터링, 잠재 요인 협업 필터링

콘텐츠 기반 필터링 추천 시스템

  • 사용자가 특정한 아이템을 매우 선호하는 경우, 그 아이템과 비슷한 콘텐츠를 가진 다른 아이템을 추천하는 방식

ex) 특정 영화에 높은 평점을 주었다면 그 영화의 장르, 출연 배우, 감독, 영화 키워드 등의 콘텐츠와 유사한 다른 영화를 추천해 줌

최근접 이웃 협업 필터링

  • 사용자가 아이템에 매긴 평점 정보나 상품 구매 이력과 같은 사용자 행동 양식(User behavior)만을 기반으로 추천을 수행하는 것
  • 협업 필터링은 사용자-아이템 평점 매트릭스를 사용하여 사용자가 평가하지 않은 아이템을 평가한 아이템에 기반하여 예측하는 알고리즘- - 사용자-아이템 평점 매트릭스에서 행(Row)는 개별 사용자, 열(Column)은 개별 아이템으로 구성되며, 각 (행,열) 위치에 해당되는 값이 평점을 나타내는 형태
  • 사용자-아이템 평점 매트릭스에서 행(Row)는 개별 사용자, 열(Column)은 개별 아이템으로 구성되며, 각 (행,열) 위치에 해당되는 값이 평점을 나타내는 형태
  • 다차원 행렬, 희소 행렬의 특성을 가지고 있음

최근접 이웃 협업 필터링 = 메모리(Memory) 협업 필터링 ->사용자 기반(User-User),  아이템 기반(Item-Item)으로 나뉨

사용자 기반 최근접 이웃 방식

  • 특정 사용자와 타 사용자 간의 유사도를 측정한 뒤 가장 유사도가 높은 TOP-N 사용자를 추출해 그들이 선호하는 아이템을 추천하는 것

비슷한 상품을 좋아한다고 해서 사람들의 취향이 비슷하다고 판단하기 어렵고 사용자들이 평점을 매긴 상품의 개수가 많지 않은 것이 일반적이기 때문에 사용자 간의 유사도를 측정하기 어려운 단점이 있음

아이템 기반 최근접 이웃 방식

  • 아이템의 속성과는 상관없이 사용자들이 그 아이템을 좋아하는지, 싫어하는지의 평가 척도가 유사한 아이템을 추천하는 기준이 되는 알고리즘

최근접 이웃 협업 필터링은 대부분 아이템 기반의 알고리즘을 적용

유사도 측정 방법으로는 코사인 유사도를 이용

잠재요인 협업 필터링

  • 사용자-아이템 평점 행렬 속에 숨어 있는 잠재요인을 추출해 추천 예측을 할 수 있게 하는 기법
  • 다차원 희소 행렬인 사용자-아이템 평점 행렬을 저차원 밀집행렬의 사용자-잠재적 요인 행렬, 잠재적 요인-아이템 행렬로 분해하여 두 행렬의 내적을 통해 새로운 예측 사용자-아이템 평점 행렬을 만듦
  • 사용자가 아직 평점을 부여하지 않은 데이터에 대한 예측 평점을 생성할 수 있게 됨
  • 가령 영화 평점 기반의 사용자-아이템 행렬에서는 잠재요인을 영화 장르로 생각할 수 있음
  • 사용자 - 잠재요인 행렬은 사용자의 영화 장르에 대한 선호도, 잠재요인-아이템 행렬은 영화의 장르별 특성 값으로 해석할 수 있음

행렬 분해 이해

  • 행렬 분해 기법으로는 SVD(Singular Vector Decomposition), NMF(Non-Negative Matrix Factorization) 등이 있음
  • R = P*Q.T
  • M 은 총 사용자 수
  • N 은 총 아이템 수
  • K는 잠재 요인의 차원 수
  • R은 M x N 차원의 사용자-아이템 평점 행렬
  • P는 M x K 차원의 사용자-잠재 요인 행렬
  • Q는 N x K 차원의 아이템-잠재 요인 행렬
  • Q.T는 Q의 전치 행렬(K x N 차원)

R 행렬의 u행 사용자와 i열 위치에 있는 평점 데이터를 r(u, i) 라고 하면

!https://blog.kakaocdn.net/dn/zWtFU/btsC4J8mUPw/v6913Bp2XhkYpDPChVIkO0/img.png

  • 사용자-아이템 평점 행렬의 미정 값을 포함한 모든 평점 값은 행렬 분해로 얻어진 P 행렬과 Q.T 행렬의 내적을 통해 예측 평점으로 다시 계산할 수 있음
  • 행렬 분해는 주로 SVD 방식을 사용하지만 원본행렬에 널(NaN) 값이 있을 경우 확률적경사하강법(SGD) 이나 ALS 를 이용하여 SVD 수행

확률적 경사 하강법을 이용한 행렬 분해

  • P 와 Q 행렬로 계산된 예측 R 행렬 값이 실제 R 행렬 값과 가장 최소의 오류를 가질 수 있도록 반복적인 비용 함수 최적화를 통해 P와 Q 를 유추해내는 것
  1. P와 Q 를 임의의 값을 가진 행렬로 설정
  2. P와 Q.T값을 곱해 예측 R 행렬을 계산하고 예측 R 행렬과 실제 R 행렬에 해당하는 오류 값을 계산
  3. 이 오류 값을 최소화 할 수 있도록 P 와 Q 행렬을 적절한 값으로 각각 업데이트
  4. 만족할 만한 오류 값을 가질 때까지 2, 3번 작업을 반복하면서 P와 Q 값을 업데이트해 근사화
  • 사용자-아이템 평점 행렬의 경우 행렬 분해를 위해서 오류 최소화와 L2규제를 반영한 비용 함수를 사용
  • L2 규제를 반영해 실제 R 행렬값과 예측 R 행렬값의 차이를 최소화 하는 방향성을 가지고 P 행렬과 Q 행렬에 업데이트 값을 반복적으로 수행하면서 최적의 예측 R 행렬을 구하는 것이 SGD 기반의 행렬분해

정리

추천 시스템은 매출 향상으로 이어지는 많은 사례로 인하여 많은 기업들이 추천 시스템의 예측 성능을 향상시키기 위해 알고리즘과 데이터 수집에 노력을 기울이고 있음

콘텐츠 기반 필터링은 아이템을 구성하는 여러 가지 콘텐츠 중 사용자가 좋아하는 콘텐츠를 필터링하여 이에 맞는 아이템을 추천하는 방식임(사용자가 좋아하는 영화를 분석한 뒤, 이 영화의 콘텐츠와 유사한 다른 영화를 추천).

다양한 요소를 결합하여, 하나의 콘텐츠 특징으로 피처 벡터화한 뒤에 이들 피처 벡터와 가장 유사한 다른 피처 벡터를 가진 영화를 추천하는 것

협업 필터링은 최근접 이웃 협업 필터링과 잠재 요인 필터링으로 나뉨.

최근접 이웃 협업 필터링은 다시 사용자 기반과 아이템 기반으로 나뉘며, 이중 아이템 기반이 더 많이 사용됨.

아이템 기반 최근접 이웃 방식은 특정 아이템과 가장 근접하게 유사한 다른 아이템들을 추천하는 방식임.

이 유사도의 기준이 되는 것은 사용자들의 아이템에 대한 평가를 벡터화한 값임.

잠재 요인 협업 필터링은 많은 추천 시스템에서 활용하는 방식임.

사용자-아이템 평점 행렬 데이터에 숨어 있는 잠재 요인을 추출하여 사용자가 아직 평점을 매기지 않은 아이템에 대한 평점을 예측하여 이를 추천에 반영하는 방식임.

이렇게 잠재 요인을 추출하기 위해 다차원의 사용자-아이템 평점 행렬을 저차원의 사용자-잠재요인, 아이템-잠재요인 행렬로 분해하는데, 이러한 기법을 행렬 분해라고 함.

파이썬의 추천 시스템 패키지 중 하나인 surprise는 사이킷런과 유사한 API를 지향하며, 간단한 API만을 이용해 파이썬 기반에서 추천 시스템을 구현해 줌.

' > 머신러닝 완벽가이드' 카테고리의 다른 글

군집화  (0) 2024.05.06
회귀 / 차원축소  (0) 2024.05.06
분류  (0) 2024.05.06
추천 시스템  (1) 2024.01.07
텍스트 분석  (1) 2024.01.06