본문 바로가기

R/R로 만드는 추천 시스템

사례 연구 : 나만의 추천 시스템 만들기 - 02

반응형

아이템 속성 추출하기

이 실습 초반에 생성한 table_in 객체에는 첫 번째 열에 입력된 코드 값이 A인 행들이 잇으며, 이 행들에는 웹사이트의 영역 번호와 해당 영역을 설명하는 간단한 요약정보가 들어있다. 이러한 행들만 따로 추출하기 위해 table_in 객체의 데이터 타입을 data.table형으로 변환하고, 첫 번재 열의 코드 값이 A인 행들만 따로 분리한다.

table_in <- data.table(table_in)
table_items <- table_in[V1 == "A"]
head(table_items)
   V1   V2 V3                               V4               V5 
1:  A 1277  1           NetShow for PowerPoint          /stream   
2:  A 1253  1              MS Word Development         /worddev   
3:  A 1109  1 TechNet (World Wide Web Edition)         /technet   
4:  A 1038  1   SiteBuilder Network Membership       /sbnmember   
5:  A 1205  1                  Hardware Supprt /hardwaresupport   
6:  A 1076  1           NT Workstation Support    /ntwkssupport   

table_item에 포함된 주요한 열은 다음과 같다.

  • V2 : 웹사이트 영역 번호
  • V4 : 웹사이트 영역 요약 정보
  • V5 : 웹사이트 영역 고유 URL 주소

좀 더 명확한 테이블을 만들기 위해 ㅣㅍㄹ요한 열만 추출하고 이름을 다시 부여한다. 도한 웹사이트 영역 번호를 기준으로 테이블을 정렬한다.

table_items <- table_items[, c(2,4,5), with = FALSE]
setnames(table_items, 1:3, c("id", "description", "url"))
table_items <- table_items[order(id)]
head(table_items)
     id            description      url
1: 1000                 regwiz  /regwiz
2: 1001        Support Desktop /support
3: 1002 End User Produced View  /athome
4: 1003         Knowledge Base      /kb
5: 1004   Microsoft.com Search  /search
6: 1005                 Norway   /norge

우리는 웹사이트 영역들이 갖는 특징을 살펴봐야 한다. 테이블을 보면 두 가지 범주의 웹사이트 영역이 있는 것을 알 수 있다.

  • 마이크로소프트 제품 관련 영역
  • 지리적 위치 관련 영역

테이블에서 지리적 위치가 포함된 행을 찾을 수 잇으면, 나머지는 제품과 관련된 영역으로 간줄할 수 있다. 이를 위해 먼저 category라는 이름의 열을 추가하고, 전체 행에 product라는 공통된 값을 부여한다.

table_items[, category := "product"]

앞서 소개한 countrycode 패키지는 대부분의 국가명이 포함된 codelist 객체를 제공한다. codelist 객체를 사용해 국가명이 포함된 name_countries라는 이름의 벡터를 정의한다. 그리고 table_items의 description 열에 있는 값과 name_countries 벡터의 값을 비교해, 값이 일치하는 모든 행을 region 카테고리로 분류한다.

name_countries <- c(codelist$country.name, "Taiwan", "UK", "Russia", "Venezuela",
                    "Slovenija", "Caribbean", "Netherlands (Holland)", "Europe", "Central America", "MS North Africa")
table_items[description %in% name_countries, category := "region"]

그런데 결과를 자세히 보면 description 열에 국가명과 함게 region이라는 단어가 붙어있는 행들이 있다. 이런 행들은 countrycode 패키지를 이용해 분류해낼 수 없었으므로, grepl 함수를 사용한 정규 표현식을 통해 region 카테고리로 분류한다.

table_items[grepl("Region", description), category := "region"]
head(table_items)
     id            description      url category
1: 1000                 regwiz  /regwiz  product
2: 1001        Support Desktop /support  product
3: 1002 End User Produced View  /athome  product
4: 1003         Knowledge Base      /kb  product
5: 1004   Microsoft.com Search  /search  product
6: 1005                 Norway   /norge  product

이제 웹사이트 영역을 각 category별로 몇 개식 분류했는지 확인해본다.

table_items[, list(n_items = .N), by = category]
   category n_items
1:  product     282
2:   region      12

웹 사이트의 약 80%는 제품과 관련한 영역이고, 나머지 20%는 지역에 관한 영역임을 알 수 있다.

이제 드디어, 추천 모델을 만들 준비가 됐다.

 

모델 만들기

이 절에서는 웹사이트 영역의 내용 정보와 사용자의 조회 정보를 이용해 추천 모델을 만든다. 이 모델은 아이템 기반 협업 필터링(IBCF)과 웹사이트 영역의 내용 정보를 결합한다. 우리는 웹사이트 영역의 특징들을 조합해서 IBCF의 평점 매트릭스에 반영할 것이다. 이 모델은 서로 다른 두 가지 데이터를 통해 만들어진다.

먼저, 3장에서 설명한 방식에 따라 데이터를 학습 데이터와 트레이닝 데이터로 나눈다.

which_train <- sample(x = c(TRUE, FALSE),
                      size = nrow(ratings_matrix),
                      replace = TRUE,
                      prob = c(0.8, 0.2))
recc_data_train <- ratings_matrix[which_train, ]
recc_data_test <- ratings_matrix[!which_train, ]

이제 recommenderlab 라이브러리의 Recommender 함수를 이용해서 IBCF 모델을 만들 수 있다. 평점 매트릭스가 이진(binary)데이터로 이뤄져 있기 때문에 사용자 간 거리 측정 방법을 Jaccard로 지정한다. 자세한 내용은 3장의 '이진 데이터에 대한 협업 필터링' 절에서 설명했다. 나머지 매개변수는 기본값으로 지정한다.

recc_model <- Recommender(data = recc_data_train,
                          method = "IBCF",
                          parameter = list(method = "Jaccard"))

 

IBCF 방식의 추천 시스템은 아이템에 대한 거리 매트릭스를 기반으로 한다. 아이템 간의 거리는 아이템에 대한 조회 정보를 이용해 계산한다. 아이템 간에 같이 구매한 사용자가 많을수록 아이템들이 더 유사하다는 개념이다.

앞서 생성한 모델의 sim 속성에는 웹사이트 영역 간의 거리 매트릭스가 포함돼 있다. 

확인해보자.

class(recc_model@model$sim)
[1] "dgCMatrix"
attr(,"package")
[1] "Matrix"

dim(recc_model@model$sim)
[1] 166 166

유사도 매트릭스는 dcCMatrix 클래스에 속하며, 166x166의 정방 매트릭스 형태다. 이를 image 함수를 사용해 시각화해볼 수 있다.

image(recc_model@model$sim)

다음 그림은 위 코드의 결과다.

아직 웹사이트 영역 번호가 정렬되지 않았으므로 명확한 패턴을 발견할 수 없다. 일단 값의 범위를 살펴본다.

range(recc_model@model$sim)
[1] 0 1

모든 거리가 0에서 1 사이인 것을 확인했다.

우리의 목표는 다음 단계를 통해 거리 매트릭스와 웹사이트 영역에 대한 내용 정보를 결합하는 것이다.

  1. 사용자의 조회 정보를 기반으로 아이템 간의 유사도 매트릭스를 정의한다.
  2. 웹사이트 영역의 내용 정보를 기반으로 아이템 간의 유사도 매트릭스를 정의한다.
  3. 두 매트릭스를 결합한다.

먼저 recc_model을 이용해서, 아이템 간의 조회 유사도 매트릭스를 정의할 수 있다. 처음으로 필요한 것은 dgCMatrix 객체를 매트릭스 타입의 데이터로 변환하는 것이다.

dist_rating <- as(recc_model@model$sim, "matrix")

다음으로 웹사이트 영역에 대한 내용 정보를 기반으로 유사도 매트릭스를 만들기 위해 dist함수를 호출한다. 우리는 category 열에 포함된 정보만 사용하므로, 다음과 같은 방법으로 거리를 계산한다.

  • 두 웹사이트 영역이 같은 카테고리에 속하면 1을 부여한다.
  • 두 웹사이트 영역이 다른 카테고리에 속하면 0을 부요한다.

우리는 거리 매트릭스를 생성했기 때문에 이를 유사도 매트릭스로 변환한다. 거리가 0에서 1 사이이므로, 1 - dist()를 적용해 기준을 바꿔준다.

dist_category <- table_items[, 1 - dist(category == "product")]
class(dist_category)
[1] "dist"

앞서 생성한 dist_category 객체의 클래스는 dist이므로, as 함수를 이용해 매트릭스 형태로 쉽게 변환할 수 있다. 

dist_category <- as(dist_category, "matrix")

이제 dist_category 매트릭스의 차원 크기를 dist_ratings 매트릭스와 비교해본다.

dim(dist_category)
[1] 294 294
dim(dist_rating)
[1] 166 166

dist_category 테이블은 모든 웹사이트 영역 간의 유사도 매트릭스를 포함하고 있으므로, 조회 내역이 적은 아이템 등을 제거하고 유사도 매트릭스를 생성한 data_ratings 보다 많은 행과 열이 있다.

 

dist_category와 dist_ratings를 결합하려면 행과 열의 수가 동일해야 하며, 두 테이블의 정렬 기준이 같아야 한다. 다음 과정을 거쳐서 웹사이트의 영역 번호를 기준으로 두 테이블을 매칭시킬 수 있다.

  1. 두 매트릭스의 행과 이름과 열 이름에 웹사이트 영역 번호가 들어가 있는지 확인한다.
  2. dist_ratings에서 행과 열 이름을 추출한다.
  3. dist_ratings에서 추출한 이름에 따라 dist_category를 정렬하고 일치하는 항목만 추출한다.

dist_ratings테이블에는 이미 행과 열 이름이 들어있다. 앞서 생성한 table_items로부터 웹사이트 영역 번호를 호출해 dist_category 객체의 행과 열 이름에 붙여준다.

rownames(dist_category) <- table_items[, id]
colnames(dist_category) <- table_items[, id]

이제는 dist_ratings에서 웹사이트 영역 번호를 추출해 dist_category에서 같은 번호를 가진 웹사이트 영역만 분리한다.

vector_items <- rownames(dist_rating)
dist_category <- dist_category[vector_items, vector_items]

두 매트릭스가 일치하는지 확인해본다.

identical(dim(dist_category), dim(dist_rating))
[1] TRUE
identical(rownames(dist_category), rownames(dist_rating))
[1] TRUE
identical(colnames(dist_category), colnames(dist_rating))
[1] TRUE

모든 것이 같다. dist_category의 값을 그림을 통해 다시 살펴본다.

image(dist_category)

다음 그림은 위 코드의 결과다.

매트릭스에는 0 또는 1의 값만 들어있고, 웹사이트 영역은 두 개의 카테고리로만 분류되므로 명확한 패턴을 발견할 수 있다. 또한 매트릭스가 대칭적이다.

이제 두 테이블을 결합해야 하며, 결합 방식으로는 두 매트릭스 간 평점의 가중평균을 이용한다. dist_category 매트릭스에서는 아이템끼리 카테고리가 일치하는지만 고려했으므로 가중치를 비교적 낮게 잡는 것이 좋다. 예를 들어 가중치를 25%로 설정한다.

weight_category <- 0.25
dist_tot <- dist_category * weight_category + dist_rating * (1 - weight_category)

그림을 사용해 dist_tot 매트릭스를 살펴본다.

image(dist_tot)

다음 그림은 위 코드의 결과다.

매우 유사한 아이템을 나타내는 점들이 보인다. 게다가 배경에서 dist_category의 패턴을 볼 수 있다.

이제 recc_model 내에 새 유사도 매트릭스를 포함시킬 수 있다. 이를 위해 dist_tot 객체의 데이터 타입을 dgCMatrix로 변환하고 recc_model에 삽입한다.

recc_model@model$sim <- as(dist_tot, "dgCMatrix")

 

다음 글에서 계속 작성하겠습니다.

반응형