7.2 TF-IDF 기반 아이템 추천

아이템의 TF-IDF 벡터를 구해보고, 사용자의 TF-IDF 벡터 간의 코사인 유사도를 계산하여 추천하는 방법을 살펴봅니다.

7.2.1 수식 살펴보기

여기에서는 TF-IDF를 활용한 콘텐츠 기반 필터링 추천에서 TF-IDF와 코사인 유사도에 대한 수식을 살펴봅니다.

7.2.1.1 단어 빈도와 역문서 빈도(TF-IDF)

문서-단어 행렬(Document-Term Matrix, DTM)

예를 들어, 아이템의 콘텐츠로 아이템에 부여한 주제어(키워드)인 태그 데이터는 이전에 살펴본 평점 데이터와 마찬가지로 사용자와 아이템 사이에서 이루어진 관계와 특성을 나타냅니다. 여기에서는 문서는 아이템, 단어는 아이템에 부여된 태그라고 가정하고 문서-단어 행렬에 대하여 살펴봅니다.

다음 표는 문서(아이템)는 행, 단어(태그)는 열로 나타낸 이원 도수 분포표인 문서-단어 행렬(Document-Term Matrix, DTM)을 나타낸 것입니다.

문서-단어 행렬

${t_1}$ ${t_2}$ $\cdots$ ${t_n}$
${d_1}$ ${w_{11}}$ ${w_{12}}$ $\cdots$ ${w_{1n}}$
${d_2}$ ${w_{21}}$ ${w_{22}}$ $\cdots$ ${w_{2n}}$
$\cdots$ $\cdots$ $\cdots$ $\cdots$ $\cdots$
${d_m}$ ${w_{m1}}$ ${w_{m2}}$ $\cdots$ ${w_{mn}}$

행렬에 존재하는 문서의 집합 $D = { {d_1},{d_2},{d_3}, \cdots ,{d_m}} $를 말뭉치(Corpus)라 하고 말뭉치의 단어 집합 $T = { {t_1},{t_2},{t_3}, \cdots ,{t_n}} $는 단어 사전입니다. 단어 집합은 문장으로 이루어진 경우, 형태소 분석과 같은 표준 자연어 처리 작업을 통해 얻을 수도 있습니다.

단어 빈도와 역문서 빈도(TF-IDF)

문서에서 출현하는 단어의 중요도를 나타내는 단어 빈도와 역문서 빈도인 TF-IDF는 다음과 같이 정의됩니다.

$$ \begin{flalign} \notag \mathrm{tf\textnormal{-}idf}(t_i,d_j,D) = \mathrm{tf} (t_i,d_j) \times \mathrm{idf} (t_i,D) \end{flalign} $$

여기에서, $\mathrm{tf} (t_i,d_j)$는 문서 $d_j$에서 단어 $t_i$의 발생 수로 단어 빈도, $\mathrm{idf} (t_i,D)$는 전체 문서 집합 $D$에서 단어 $t_i$의 역문서 빈도입니다. $\mathrm{tf\textnormal{-}idf}({t_i},{d_j},D)$는 단어 빈도와 역문서 빈도입니다.

단어 빈도인 TF는 다음과 같이 정의됩니다.

$$ \begin{flalign} \notag \mathrm{tf} (t_i,d_j) = w_{t_i,d_j} \end{flalign} $$

여기에서 $w_{{t_i},{d_j}}$는 문서 $d_j$에서 단어 $t_i$의 출현 횟수입니다.

역문서 빈도인 IDF는 다음과 같이 정의됩니다.

$$ \begin{flalign} \notag \mathrm{idf} (t_i,D) & = \log(\frac{|D|}{\mathrm{df} (t_i,D)}) \\ & = \log_{10}(\frac{|D|}{n_{t_i}}) \end{flalign} $$

여기에서 $\left| D \right|$는 전체 문서 집합 $D$의 원소 개수로 전체 문서의 수, $n_{t_i}$는 단어 $t_i$가 출현한 문서의 수입니다.

벡터 길이 정규화(Unit Length Normalization)

문서의 TF-IDF 벡터는 단어 수에 영향을 받지 않도록 하기 위해 유클리디안 거리(Euclidean Norm)가 1인 단위 벡터(Unit Vector)로 정규화합니다(L2 Norm 정규화). 이는 단순하게 말하면 다른 문서의 TF-IDF 벡터와 비교하기 쉽게 하기 위해서입니다.

벡터 ${\bf{x}}$의 ${L_p}$ 노름은 다음과 같이 정의됩니다.

$$ \begin{flalign} L_p & = {\left\| {\bf{x}} \right\|_p} \\ & = \left ( \sum_{i=1}^{n} \left | x^{i} \right |^{2} \right )^{\frac {1} {2}} \end{flalign} $$

여기에서 p는 거리의 차수(degree)로 $p > 0$입니다.

벡터 ${\bf{x}}$의 ${L_2}$ 노름은 다음과 같이 정의됩니다.

$$ \begin{flalign} L_2 & = {\left\| {\bf{x}} \right\|_2} \\ & = (\sum\nolimits _{i=1}^{n} |x_i|^2 )^{\frac {1} {2}} \\ & = \sqrt {\sum\nolimits _{i=1}^{n} {{{\left| {{x_i}} \right|}^2}} } \end{flalign} $$

벡터 길이 정규화(Unit Length Normalization)는 벡터 $\bf{x}$를 유클리드 길이(Euclidean length)로 나누는 것입니다.

벡터 길이 정규화는 다음과 같이 정의됩니다.

$$ \begin{flalign} \bf{x}' & = \frac{\bf{x}}{\left\| {{\bf{x}}} \right\|} \end{flalign} $$
여기에서 $\left\| {\bf{x}} \right\|$는 벡터 $\bf{x}$의 길이(norm)으로 일반적으로 유클리드 노름(L2 norm)을 사용합니다.

7.2.1.2 코사인 유사도

여기에서는 4장에서 살펴본 코사인 유사도를 사용합니다.

두 벡터 ${{\bf{x}}_a}$와 ${{\bf{x}}_b}$간의 코사인 유사도는 다음과 같이 정의됩니다.

$$ \begin{flalign} \notag \mathrm{sim} _{cosine} ({{\bf{x}}_a},{{\bf{x}}_b}) & = \frac{{{\bf{x}}_a^{\rm T}{{\bf{x}}_b}}} {{\lVert{{\bf{x}}_a}\rVert}_2{\lVert{{\bf{x}}_b}\rVert}_2} \\ & = \frac{{\sum\nolimits_1^n {{a_i}{b_i}} }}{{\sqrt {\sum\nolimits_1^n {a_i^2} } \sqrt {\sum\nolimits_1^n {b_i^2} } }} \end{flalign} $$

여기에서 ${\bf{x}}_a^{\rm T}{{\bf{x}}_b} = \sum\nolimits_1^n {{a_i}{b_i}} = {a_1}{b_1} + {a_2}{b_2} + \cdots {a_n}{b_n}$로 두 벡터의 내적(Dot Product), ${\lVert{{\bf{x}}_a} \rVert_2}$와 ${\lVert {{\bf{x}}_b} \rVert_2}$는 각 벡터의 유클리드 노름(L2 Norm)입니다.

7.2.2 직접 계산해보기

이제부터 예제 데이터셋을 사용하여 앞서 살펴본 수식으로 직접 계산해 볼 것입니다. 다음 표는 평점 데이터를 사용자는 행, 아이템은 열로 나타낸 것입니다.

예제 데이터셋의 평점 데이터

사용자/아이템 $i_1$ $i_2$ $i_3$ $i_4$ $i_5$ $i_6$ $i_7$ $i_8$ $i_9$ $i_{10}$
$u_1$ 0.5 5 1 3 3.5 5 2.5
$u_2$ 3 2 3.5 2 4 4 1
$u_3$ 3.5 1 3.5 4 1 3.5
$u_4$ 3 5 0.5 3.5 3.5 4.5
$u_5$ 3 4 2.5 4

다음의 표는 태그 데이터를 사용자는 행, 아이템(문서)은 열로 나타낸 것입니다.

예제 데이터셋의 태그 데이터

아이템사용자 $i_1$ $i_2$ $i_3$ $i_4$ $i_5$ $i_6$ $i_7$ $i_8$ $i_9$ $i_{10}$
$u_1$ {Thriller} {Sci-Fi} {Sci-Fi}
$u_2$ {Mystery, Sci-Fi} {Adventure} {Sci-Fi, Thriller} {Comedy} {Action} {Crime, Sci-Fi, Thriller} {Action, Crime, Drama, Sci-Fi}
$u_3$ {Adventure} {Fantasy} {Horror} {Comedy, Sci-Fi} {Crime, Fantasy} {Crime}
$u_4$ {Fantasy} {Mystery}
$u_5$ {Adventure} {Mystery} {Crime}

예를 들어 사용자 $u_4$가 평가하지 않은 아이템들에 대한 코사인 유사도를 TF-IDF를 사용하여 계산해 봅니다. 이때 사용자 프로필의 평점 임곗값은 3.5 이상으로 가정하여 계산해 봅니다.

7.2.2.1 단어 빈도와 역문서 빈도

예를 들어 아이템 $i_3$에 출현하는 단어 "Sci-Fi"에 대한 단어 빈도와 역문서 빈도를 계산해 봅니다.

단어 빈도

다음 표는 태그 데이터를 문서(아이템)는 행, 단어(태그)는 열로 나타낸 이원 도수 분포표로 나타낸 것입니다.

태그 데이터의 이원 도수 분포표

단어문서 Action Adventure Comedy Crime Drama Fantasy Horror Mystery Sci-Fi Thriller
$i_1$ 2 1 1 1
$i_2$ 1 1
$i_3$ 1 2 1
$i_4$ 1 1
$i_5$ 1 1
$i_6$ 1 1
$i_7$ 2 1 1 1
$i_8$ 1
$i_9$ 1
$i_{10}$ 1 3 1 1

Tip:

문서와 단어의 이원 도수 분포표는 문서-단어 행렬(Document-Term Matrix, DTM)이라고 합니다.

여기에서 단어 "Sci-Fi"가 출현한 문서는 $i_1$, $i_3$, $i_4$, $i_7$, $i_8$, $i_{10}$으로 6이며 단어 빈도는 7입니다.

문서 빈도(DF)와 역문서 빈도(IDF)

이원 도수 분포표에서 보듯이 단어 "Sci-Fi"에 대한 문서 빈도(DF)는 7이 아닌 6입니다. 문서 빈도는 특정 단어가 문서에서 출현한 횟수이기 때문에 문서 $i_3$에서 Sci-Fi가 2회 출현하였지만 6이 됩니다. 역문서 빈도를 구해보면 전체 문서의 수인 10을 문서 빈도 6으로 나눈 값의 상용로그 값인 ${\log _{10}}1.6666666 \approx {\rm{0}}{\rm{.2218487}}$입니다.

$\mathrm{idf} (t_i,D) = {\log _{10}}(\frac{{10}}{6}) = {\log _{10}}(1.6666666) \approx {\rm{0}}{\rm{.2218487}}$

Tip:

단어 빈도 데이터에서 단어 "Sci-Fi"에 대한 문서 빈도(DF)는 7이 아닌 6입니다. 특정 단어가 전체 문서 기준으로 문서에서 출현한 횟수이기 때문에 문서(아이템) 8에서 Sci-Fi가 단어는 2회 출현하였지만 6이 됩니다.

같은 방법으로 모든 단어에 대한 역문서 빈도를 계산해 봅니다. 다음 표는 단어에 대한 역문서 빈도를 나타낸 것입니다.

역문서 빈도 데이터

단어 단어빈도 문서빈도 문서 출현확률 문서 출현확률(역수) 역문서빈도
Action 2 2 0.2 5 0.69897
Adventure 3 2 0.2 5 0.69897
Comedy 2 2 0.2 5 0.69897
Crime 5 2 0.2 5 0.69897
Drama 1 1 0.1 10 1
Fantasy 3 3 0.3 3.3333333 0.5228787
Horror 1 1 0.1 10 1
Mystery 3 3 0.3 3.3333333 0.5228787
Sci-Fi 7 6 0.6 1.6666666 0.2218487
Thriller 3 3 0.3 3.3333333 0.5228787
단어 빈도와 역문서 빈도

문서 $i_3$에 출현하는 단어 "Sci-Fi"에 대한 단어 빈도는 2, 역문서 빈도는 0.2218487입니다. 문서 3의 단어 "Sci-Fi"의 단어 빈도와 역문서 빈도는 단어 빈도에 역문서 빈도를 곱한 0.4436975가 됩니다.

$$\mathrm{tf\textnormal{-}idf}({t_i},{d_j},D) = 2 \times {\rm{0}}{\rm{.2218487}} = {\rm{0}}{\rm{.4436975}}$$

같은 방법으로 모든 단어에 대한 단어 빈도와 역문서 빈도를 계산해 봅니다. 다음 표는 계산된 단어 빈도와 역문서 빈도(TF-IDF) 데이터를 문서는 행, 단어는 열로 나타낸 것입니다.

단어 빈도와 역문서 빈도(TF-IDF)

단어문서 Action Adventure Comedy Crime Drama Fantasy Horror Mystery Sci-Fi Thriller
$i_1$ 1.39794 0.5228787 0.2218487 0.5228787
$i_2$ 0.69897 0.5228787
$i_3$ 1 0.4436975 0.5228787
$i_4$ 0.69897 0.2218487
$i_5$ 0.69897 0.5228787
$i_6$ 0.69897 0.5228787
$i_7$ 1.39794 0.5228787 0.2218487 0.5228787
$i_8$ 0.2218487
$i_9$ 0.5228787
$i_{10}$ 0.69897 2.09691 1 0.2218487

7.2.2.2 벡터 길이 정규화

앞서 계산된 문서의 TF-IDF 벡터를 정규화하여 문서에 포함된 단어 수에 영향을 받지 않도록 합니다.

유클리드 노름

문서 $i_3$의 TF-IDF 벡터는 {1, 0.4436975, 0.5228787}입니다. 문서 $i_3$의 유클리드 노름은 각각의 요소를 제곱한 값의 모두 합한 후 제곱근을 취한 1.2125468이 됩니다.

$${L_2} = \sqrt {\left| {{{\rm{1}}^{\rm{2}}}} \right|{\rm{ + }}\left| {{{0.4436975}^{\rm{2}}}} \right|{\rm{ + }}\left| {{{0.5228787}^{\rm{2}}}} \right|} = {\rm{1}}{\rm{.2125468}}$$

같은 방법으로 모든 문서에 대한 유클리드 노름을 계산해 봅니다. 다음 표는 계산된 유클리드 노름 데이터를 유클리드 노름은 행, 문서는 열로 나타낸 것입니다.

문서의 유클리드 노름 데이터

문서 $i_1$ $i_2$ $i_3$ $i_4$ $i_5$ $i_6$ $i_7$ $i_8$ $i_9$ $i_{10}$
유클리드 노름 1.5969526 0.8729039 1.2125468 0.7333321 0.8729039 0.8729039 1.5969526 0.2218487 0.5228787 2.436146
문서 길이 정규화

여기에서는 이전에 계산한 문서별 TF-IDF 벡터를 해당 벡터의 L2 노름으로 나누어 길이 정규화(Length Normalization)를 합니다. 예를 들어 문서 $i_3$의 단어 "Sci-Fi"에 대하여 길이 정규화 해봅니다.

문서 $i_3$의 단어 빈도와 역문서 빈도(TF-IDF)는 0.4436975, 문서 $i_3$의 유클리드 노름은 1.2125468입니다. 문서 $i_3$의 단어 "Sci-Fi"의 길이 정규화 값은 단어 빈도와 역문서 빈도(TF-IDF) 값을 유클리드 노름으로 나눈 0.365922가 됩니다.

$$\hat w = \frac{{0.4436975}}{{1.2125468}} = {\rm{0}}{\rm{.365922}}$$

같은 방법으로 모든 문서에 대한 TF-IDF 벡터를 길이 정규화해 봅니다. 다음 표는 길이 정규화된 단어 빈도와 역문서 빈도(TF-IDF) 데이터를 문서(아이템)는 행, 단어는 열로 나타낸 것입니다.

길이 정규화된 단어 빈도와 역문서 빈도(TF-IDF) 데이터

문서 $i_1$ $i_2$ $i_3$ $i_4$ $i_5$ $i_6$ $i_7$ $i_8$ $i_9$ $i_{10}$
유클리드 노름 1.5969526 0.8729039 1.2125468 0.7333321 0.8729039 0.8729039 1.5969526 0.2218487 0.5228787 2.436146

7.2.2.3 프로필 유사도 계산과 아이템 추천

이제부터 앞서 계산된 길이 정규화된 TF-IDF값을 가지고 평점 임계치 기반으로 사용자 프로필을 만든 후, 사용자가 평가하지 않은 아이템과의 유사도를 계산해 볼 것입니다. 여기에서 평점 임곗값은 3.5입니다.

사용자 프로필

예를 들어 사용자 $u_4$의 프로필을 만들어봅니다. 다음 표는 사용자 $u_4$의 평점 데이터를 사용자는 행, 아이템은 열로 나타낸 것입니다.

[평점 데이터]

사용자/아이템 $i_1$ $i_2$ $i_4$ $i_6$ $i_8$ $i_9$
$u_4$ 3 5 0.5 3.5 3.5 4.5

여기에서 사용자 $u_4$가 평가한 아이템 중 임곗값 이상의 평점을 가지는 아이템은 {$i_2$, $i_6$, $i_8$, $i_9$}입니다. 다음 표는 임곗값 이상의 평점을 가진 사용자 $u_4$가 평가한 아이템의 길이 정규화된 TF-IDF 데이터를 문서(아이템)는 행, 단어는 열로 나타낸 것입니다.

길이 정규화된 단어 빈도와 역문서 빈도(TF-IDF) 데이터

단어 문서 Action Adventure Comedy Crime Drama Fantasy Horror Mystery Sci-Fi Thriller
$i_2$ 0.8007411 0.5990107
$i_6$ 0.8007411 0.5990107
$i_8$ 1
$i_9$ 1

다음 표는 사용자 $u_4$의 프로필을 나타낸 것입니다.

사용자 $u_4$의 프로필

단어 사용자 Action Adventure Comedy Crime Drama Fantasy Horror Mystery Sci-Fi Thriller
$u_4$ 0.8007411 0.8007411 0.5990107 1.5990107 1

사용자 프로필은 사용자 $u_4$가 평가한 임계치 이상의 평점을 가지는 아이템의 단어 별 길이 정규화된 TF-IDF값의 합계입니다.

아이템 프로필

여기에서 사용자 $u_4$가 평가하지 않은 아이템은 {$i_3$, $i_5$, $i_7$, $i_{10}$}입니다. 다음 표는 사용자 $u_4$가 평가하지 않은 아이템의 프로필을 나타낸 것입니다.

사용자 $u_4$가 평가하지 않은 아이템의 프로필

단어 문서 Action Adventure Comedy Crime Drama Fantasy Horror Mystery Sci-Fi Thriller
$i_3$ 0.8247105 0.365922 0.4312236
$i_5$ 0.8007411 0.5990107
$i_7$ 0.8753798 0.3274228 0.1389201 0.3274228
$i_{10}$ 0.2869163 0.8607489 0.4104844 0.0910655
코사인 유사도 계산

여기에서는 사용자 프로필과 아이템 프로필 간의 유사도를 계산해 봅니다. 예를 들어 사용자 $u_4$와 아이템 $i_3$ 프로필의 코사인 유사도를 계산해 봅니다. 여기에서는 사용자 $u_4$와 아이템 $i_3$의 프로필 데이터 중 어느 한쪽에 결측값이 있는 경우, 0으로 대체하여 유사도를 계산합니다.

다음 표는 사용자 $u_4$와 아이템 $i_3$의 프로필 데이터를 나타낸 것입니다.

사용자 $u_4$와 아이템 $i_3$의 프로필 데이터

단어 프로필 Action Adventure Fantasy Horror Mystery Sci-Fi Thriller
$u_4$ 0.8007411 0.8007411 0.5990107 0 1.5990107 1 0
$i_3$ 0 0 0 0.8247105 0 0.365922 0.4312236

여기에서 단어 (Action , Adventure, Fantasy, Horror, Mystery, Sci-Fi, Thriller)에 대한 사용자 $u_4$의 프로필 데이터는 {0.8007411, 0.8007411, 0.5990107, 0.8247105, 1.5990107, 0.365922, 0.4312236}, 아이템 $i_3$의 프로필 데이터는 {0, 0, 0, 0.8247105, 0, 0.365922, 0.4312236}입니다. 두 프로필의 내적은 $(1 \times 0.365922)$이고 사용자 $u_4$의 유클리드 노름은 $\sqrt {{{0.8007411}^2} + {{0.8007411}^2} + {{0.5990107}^2} + {{1.5990107}^2} + {1^2}} $, 아이템 $i_3$의 유클리드 노름은 $\sqrt {{{0.8247105}^2} + {{0.365922}^2} + {{0.4312236}^2}} $입니다. 두 프로필의 코사인 유사도는 내적을 사용자 $u_4$와 아이템 $i_3$의 유클리드 노름을 곱한 값으로 나눈 0.1604979가 됩니다.

$$\begin{array}{l} \mathrm{sim} (u,v) = \frac{{(1 \times 0.365922)}}{{\sqrt {{{0.8007411}^2} + {{0.8007411}^2} + {{0.5990107}^2} + {{1.5990107}^2} + {1^2}} \times \sqrt {{{0.8247105}^2} + {{0.365922}^2} + {{0.4312236}^2}} }}\ {\rm{ = 0}}{\rm{.1604979}} \end{array}$$

계산 결과

내적 사용자 u4의 유클리드 노름 아이템 i3의 유클리드 노름 유사도
0.365922 2.279917 1.0000001 0.1604979

같은 방법으로 사용자 $u_4$가 평가하지 않은 나머지 아이템에 대한 프로필 유사도를 모두 계산해 봅니다.

사용자 $u_4$가 평가하지 않은 아이템의 프로필 유사도

단어 프로필 Action Adventure Comedy Crime Drama Fantasy Horror Mystery Sci-Fi Thriller 유사도
$u_4$ 0.8007411 0.8007411 0.5990107 0 1.5990107 1 0 0.1604979
$i_3$ 0 0 0 0.8247105 0 0.365922 0.4312236
$u_4$ 0.8007411 0.8007411 0 0.5990107 1.5990107 1 0.1573802
$i_5$ 0 0 0.8007411 0.5990107 0 0
$u_4$ 0.8007411 0.8007411 0 0.5990107 1.5990107 1 0 0.146957
$i_7$ 0 0 0.8753798 0.3274228 0 0.1389201 0.3274228
$u_4$ 0.8007411 0.8007411 0 0 0.5990107 1.5990107 1 0.1407118
$i_{10}$ 0.2869163 0 0.8607489 0.4104844 0 0 0.0910655

7.2.2.4 아이템 추천

사용자 $u_4$가 평가하지 않은 아이템 {$i_3$, $i_5$, $i_7$, $i_{10}$}에 대한 프로필 유사도는 {0.1604979, 0.1573802, 0.146957, 0.1407118}입니다. 예를 들어, 사용자 $u_4$에게 프로필 유사도가 높은 순으로 $N$개($N = 3$)의 아이템을 추천하면, 추천된 아이템은 {$i_3$, $i_5$, $i_7$}이 됩니다. 다음 표는 사용자 $u_4$가 평가하지 않은 아이템을 예측 평점이 높은 순으로 나타낸 것입니다.

사용자 $u_4$가 평가하지 않은 아이템의 프로필 유사도

아이템 아이템 노름 내적 프로필 유사도
$i_3$ 1.0000001 0.365922 0.1604979
$i_5$ 1.0000001 0.3588138 0.1573802
$i_7$ 1 0.3350499 0.146957
$i_{10}$ 1 0.3208112 0.1407118

7.2.3 예제 코드 실행해보기

앞서 "직접 계산하기"에서는 예제 데이터셋의 평점 데이터와 태그 데이터를 사용하여 계산해 보았습니다. 이제부터는 평점 데이터 파일과 태그 데이터 파일, 그리고 예제 코드를 사용하여 "직접 계산하기"의 계산 결과에 대해 한번 더 확인해 볼 것입니다.

"직접 계산하기"의 결과

유사도 계산 사용자 아이템 랭크 유사도
유사도 측도 결측값
코사인 0 대체 $u_4$ $i_3$ 1 0.1604979

7.2.3.1 구현 클래스 살펴보기

먼저 예제 코드에서 사용하는 TF-IDF 콘텐츠 기반 필터링이 구현된 TermFrequencyInverseDocumentFrequencyParams 클래스와 TermFrequencyInverseDocumentFrequency 클래스를 살펴봅니다. TermFrequencyInverseDocumentFrequencyParams 클래스는 Apache Spark ML 패키지의 추상 클래스인 JavaParams 클래스를 상속받는 CommonParams 클래스를 구현하고 있는 클래스로 TermFrequencyInverseDocumentFrequency 클래스의 생성자에 매개변수를 전달합니다. TermFrequencyInverseDocumentFrequency 클래스는 AbstractRecommender 클래스를 상속받아 평점 데이터(사용자/아이템/평점)를 사용자 기반 또는 아이템 기반의 추천 결과로 변환하는 recommend 메서드를 구현하고 있는 협업 필터링 추천을 처리하는 추천기(Recommender)입니다.

다음 그림은 UML 클래스 다이어그램 표기법을 사용하여 TermFrequencyInverseDocumentFrequencyParams 클래스와 TermFrequencyInverseDocumentFrequency 클래스를 클래스 다이어그램으로 나타낸 것입니다.

Download

[UML 클래스 다이어그램]

TermFrequencyInverseDocumentFrequencyParams 클래스는 콘텐츠 기반 필터링 추천을 위해 필요한 매개변수의 설정이나 기본값 변경이 필요한 경우에는 필요에 따라 다음 코드와 같이 빌더 패턴을 사용하여 인스턴스를 생성할 수 있습니다.

TermFrequencyInverseDocumentFrequencyParams params =
    new TermFrequencyInverseDocumentFrequencyParams()
        .setSimilarityMeasure(SimilarityMeasure.get("COSINE").invoke(true, false))
        .setThreshold(3.5)
        .setTermCol("tag")
        .setVerbose(false)
        .setUserCol("user")
        .setItemCol("item")
        .setRatingCol("rating")
        .setOutputCol("score");

TermFrequencyInverseDocumentFrequencyParams 클래스의 인스턴스에 설정 가능한 매개변수는 다음과 같습니다.

주요 매개변수

매개변수 유형 필수여부 기본값 설명
similarityMeasure UserDefinedFunction O - 유사도 계산을 위한 UserDefinedFunction 호출 열거형 유형(“cosine”: 코사인, “pearson”: 피어슨 등)
threshold Double O - 평점 임곗값
termCol String term 태그 데이터의 단어 컬럼명(기본값: “term”)
verbose boolean false 처리과정에 대한 정보를 출력할 것인지 여부 체크(기본값: false).
userCol String user 평점 데이터의 사용자 컬럼명(기본값: “user”)
itemCol String item 평점 데이터의 아이템 컬럼명(기본값: “item”)
ratingCol String rating 평점 데이터의 평점 컬럼명(기본값: “rating”)
outputCol String “score” 출력 컬럼명(기본값: “score”)으로 정규화된 평점

TermFrequencyInverseDocumentFrequency 클래스는 다음의 코드와 같이 생성된 TermFrequencyInverseDocumentFrequencyParams 클래스의 인스턴스를 생성자의 인자로 전달받아 인스턴스를 생성한 후 recommend 메서드를 사용하여 평점 데이터와 태그 데이터를 입력받아 프로필 유사도 높은 순의 Top-N 아이템 추천 데이터로 변환할 수 있습니다.

TermFrequencyInverseDocumentFrequencyParams params =
    new TermFrequencyInverseDocumentFrequencyParams()
        .setSimilarityMeasure(SimilarityMeasure.get("COSINE").invoke(true, false))
        .setThreshold(3.5)
        .setTermCol("tag")
        .setVerbose(false)
        .setUserCol("user")
        .setItemCol("item")
        .setRatingCol("rating")
        .setOutputCol("score");

TermFrequencyInverseDocumentFrequency recommender = new TermFrequencyInverseDocumentFrequency(params);

Dataset<Row> recommendedItemDS = recommend(ratingDS, termDS, 10, "u4");

TermFrequencyInverseDocumentFrequency 클래스의 recommend 메서드에 설정 가능한 매개변수는 다음과 같습니다.

주요 매개변수

매개변수 유형 필수여부 기본값 설명
ratings Dataset O - Spark의 Dataset 유형인 평점 데이터셋
terms Dataset O - Spark의 Dataset 유형인 태그 데이터셋
topN Integer O - 추천 아이템 수
userId Object O - 추천 받을 사용자 ID

7.2.3.2 예제 코드 살펴보기

여기에서는 "직접 계산하기"의 계산 결과를 확인하기 위해 JUnit으로 작성된 예제 클래스의 소스 코드를 살펴봅니다. 다음은 r4tings-recommender 라이브러리에 포함된 TF-IDF 콘텐츠 기반 필터링 추천을 위한 구현 클래스인 TermFrequencyInverseDocumentFrequency 클래스를 이용하여 "직접 계산하기"의 계산 결과를 확인하기 위한 TermFrequencyInverseDocumentFrequencyTest 클래스의 TermFrequencyInverseDocumentFrequencyExamples는 테스트 메서드입니다.

TermFrequencyInverseDocumentFrequencyTest.java

소스 코드에서 볼 수 있듯이 대부분 코드는 테스트를 처리하기 위한 것이지만 기본 흐름은 매우 단순합니다. 먼저 ❶ 매개변수를 바꿔가면서 실행하고 결과를 확인하기 위한 CSV 형식의 인자 목록을 설정합니다( 22~24행). 반복 실행되는 TermFrequencyInverseDocumentFrequencyExamples 메서드에서는 테스트 인자들을 매개변수로 받습니다.

다음으로 ❷ Parquet 유형의 평점 데이터와 태그 데이터를 읽어 들이고(35~36행) ❸ 콘텐츠 기반 필터링 추천을 위해 필요한 매개변수를 설정합니다(38행~43행). 다음으로 ❹ TermFrequencyInverseDocumentFrequency 클래스의 인스턴스를 생성하고(45행) ❺ 평점 데이터와 태그 데이터, 추천 아이템 수, 그리고 추천받을 사용자 ID를 전달하여 아이템을 추천합니다 (48행).

마지막으로 ❻ "직접 계산하기"에서 계산해 본 결과에 해당하는 기댓값(Expected)과 예제 코드의 실행 결과인 실제 값(Actual)의 소수 자릿수 7자리까지 비교합니다(63행).

7.2.3.3 실행 결과 확인하기

여기에서는 앞서 살펴본 테스트 클래스인 TermFrequencyInverseDocumentFrequencyTest 클래스의 테스트 메서드인 TermFrequencyInverseDocumentFrequencyExamples의 실행 결과를 살펴봅니다. 다음과 같이 명령줄 인터페이스(CLI, Command line interface)에서 빌드 도구인 Gradle Wrapper로 TermFrequencyInverseDocumentFrequencyTest 클래스의 테스트 메서드인 TermFrequencyInverseDocumentFrequencyExamples를 실행해 봅니다.

./gradlew :recommender-examples:test --tests com.r4tings.recommender.examples.ch07.TermFrequencyInverseDocumentFrequencyTest.termFrequencyInverseDocumentFrequencyExamples

테스트 메서드가 실행되면 직접 계산하기에서 다룬 결과를 확인하기 위해 설정한 테스트 인자 목록을 테스트 메서드의 매개변수에 대입해가며 반복 실행되어 결과가 출력됩니다.

다음은 반복 실행 테스트를 위한 테스트 인자 목록입니다

테스트 인자 목록

데이터 경로 유사도 계산 평점 임계치 Top-N 사용자 아이템 랭크 프로필 유사도
평점 데이터 태그 데이터 유사도 측도 결측값 0 대체 상세출력
dataset/r4tings/ratings.parquet dataset/r4tings/tags.parquet COSINE true false 3.5 5 $u_4$ $i_3$ 1 0.1604979

예제 코드의 실행 결과는 다음과 같습니다.

예제 코드 실행 결과더보기

❶ TF-IDF를 사용한 콘텐츠 기반 필터링 아이템 추천에 대한 테스트 케이스가 성공적으로 종료(PASSED)되었음을 확인할 수 있습니다. 또한, 실행 결과는 "직접 계산하기"에서 계산해 본 내용을 쉽게 확인하기 위해 처리 과정에서 파생된 상세 출력 정보도 함께 보여주고 있습니다.

한글:4528 영어:6411 숫자:2090

추천 시스템: 워크북

Comments

Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please follow Rust's code of conduct. This comment thread directly maps to a discussion on GitHub, so you can also comment there if you prefer.

Instead of authenticating the giscus application, you can also comment directly on GitHub.