데이터이야기

DB 노하우, 데이터직무, 다양한 인터뷰를 만나보세요.

[머신러닝1기] 화장품 구매 데이터 기반 상품 추천, 당신에게 맞는 화장품을 찾아드립니다

데이터 이야기
작성자
dataonair
작성일
2018-01-03 00:00
조회
9745


화장품 구매 데이터 기반 상품 추천

당신에게 맞는 화장품을 찾아드립니다



인터넷에서 상품을 구매할 때 머신러닝을 기반으로 한 추천 서비스 를 적용하는 사이트들이 늘고 있다. 머신러닝 전문가과정 1기 우수 조는 고객의 성별과 연령, 피부 타입 등을 고려하여 고객에게 맞는 화장품을 추천해주는 모델을 개발하였다. 기초통계를 기반으로 한 모델과 머신러닝을 기반으로 한 추천모델의 2가지를 개발하고 이를 비교해 더 나은 모델을 찾는 과정이었다.



The Challenges

국내 화장품 산업은 중국 시장의 인기를 바탕으로 초고속 성장하여 우리나라 경제의 중추 산업으로 성장하고 있다. 효능과 안전성, 그리고 한류를 바탕으로 온·오프라인과 면세점 등 여러 채널 판매를 통해 연 평균 10% 이상의 성장률을 기록하고 있으며, 기능성 소재 개 발을 통해 지속적인 발전을 이루고 있다. 한편 머신러닝을 기반으로 한 추천 서비스도 최근 각광받고 있다. 예컨대 아마존은 하나의 상품을 추천하면 다른 소비자가 함께 구매한 상품 이나, 이 상품을 본 다음에 구매한 상품을 소개해준다.

머신러닝 전문가 과정 1기 우수조에서 처음에 관심을 가졌던 부분은 딥러닝과 관련된 부분 이었다. 딥러닝을 이용하여 이미지 분석을 통해 추천 서비스를 구현하는 것을 검토하던 중 데이터 수집에 대한 부분과 딥러닝 구동을 위한 하드웨어 자원에 대한 확보가 이슈가 되어 Spark를 이용해 추천 서비스를 구현하는 방향으로 전환하게 되었다.

데이터 수집은 다행히 구성원 중 한 명이 화장품 관련 데이터를 제공하는 게 가능하다고 하 여 해당 회사의 거래내역을 기반으로 데이터 분석과 고객 추천 서비스 구현을 시도해 보는 방향으로 주제를 정하게 되었다. 고객 구매 데이터의 머신러닝 기법 적용을 통해 화장품 판 매에서 고객의 요인별로 적합한 제품을 추천해주는 전략적 마케팅 방안을 제안하려 하는 것이다. 이를 위해 빅데이터 처리 시스템을 활용해 데이터 처리와 분석을 하고, 머신러닝 기법을 활용해 예측모델을 개발하기로 하였다.



The Approach

1) 진행과정별 사용 기술과 접근 방식
먼저 샘플 데이터 탐색에는 SQL(RDBMS, Spark SQL)을 주로 이용하였다. 업무적으로 DB 를 이용한 쿼리 사용을 빈번히 하기 때문에 가장 쉽게 사용하였다. 다만 머신러닝 학습 데 이터는 빅데이터를 기반으로 하는 경우가 많아 RDBMS만으로는 한계가 있다.

데이터 정제와 기초 통계데이터 추출에는 R과 Python을 이용하였다. 시각화에는 TIBCO Spotfire를 이용하여 그래프로 속성별 분포 및 구매 영향도를 확인하였다. 이는 기초 통계 데이터를 그래프 형식으로 한눈에 볼 수 있게 해주는 도구다.

또한 기초 통계 기반 추천 모델은 Spark SQL을 이용하였고, 머신러닝 기반 추천 모델은 Spark MLlib를 이용하여 구현하였다.

폭넓게 사용되는 추천 시스템 설계에 대한 접근 방식 중 하나가 협업 필터링이다. 협업 필 터링은 사용자의 행동이나 선호도에 대한 많은 양의 정보를 수집·분석하고 다른 사용자와 의 유사성을 기반으로 사용자가 좋아할 것으로 예상하는 것을 찾아낸다. 가장 많이 사용되 는 방식인데, 크게 사용자 기반 방식과 아이템 기반 방식 2가지로 나눠진다. 우수조는 사용 자 기반 방식 알고리즘을 이용하였다.

2) 데이터 수집과 탐색
총 50만 9,018건의 고객 ID에서 고객속성(ID, 연령대, 성별, 결혼여부, 피부타입)과 구매정보 (매출일자, 제품 구매수, 매출금액), 상품정보(카테고리, 상품코드)의 데이터를 수집하였다. 구체적으로는 연령대의 경우 10대, 20~25, 25~29, 30~34, 35~39, 40~44, 45~49, 50~54, 60세 이상으로 구분하였으며, 피부타입은 모든 피부, 중건성, 건성, 중성, 지성, 중 지성이 있다. 상품 중 매출이 가장 높은 5개를 탐색하고, 결혼여부와 고객의 연령대, 그리고 성별과 피부 타입 및 매출일자에 따른 매출 분석을 했다.

데이터 분석과정은 보통 데이터 수집과 정제 작업을 거쳐 탐색을 통해 분석을 위한 의미 있 는 속성을 찾아가는 과정을 거친다. 그 중 데이터 수집과 정제 작업에 가장 시간이 많이 걸 리는 것으로 알려져 있다. 다행히 우수조는 잘 정제되어 있는 상태로 데이터를 제공받아서 분석 과정으로 바로 쉽게 들어갈 수 있었다.

3) 추천 모델 구현을 위한 준비
기초통계 기반 추천 모델과 머신러닝 기반 추천 모델의 비교를 통해 성능 향상 여부를 검 증하기로 하였다. 먼저 트레이닝과 확인을 위한 데이터세트를 준비했다. 2013년 1월부터 2015년 12월까지의 구매 내역 데이터를 대상으로 하였다. 환불이나 포인트로 구매한 데이 터(구매건수가 0보다 작거나 같은 건)는 제외하였다. 구매 데이터세트 중 90%는 트레이닝 데이터세트로, 나머지 10%는 확인 데이터세트로 이용했다.



val rawData = sc.textFile("/user/kdata/cosm/cosm-grp-trd-2015.csv")
val allData = rawData.map { line =>
val Array(userId, itemId, count) = line.split('|').map(_.toInt)
Rating(userId, itemId, count)
}.cache()
val Array(trainData, cvData) = allData.randomSplit(Array(0.9, 0.1))
val numOfUsers = trainData.map(row => row.user).distinct().count()
val numOfItems = trainData.map(row => row.product).distinct().count()



테스트 데이터세트는 2016년 1월부터 2016년 10월까지의 구매 내역 데이터를 대상으로 하 였다. 해당 기간 구매 내역을 기반으로 1,000명의 고객 키 정보와 구매한 상품정보를 추출하 였다. 고객 키 정보로 트레이닝한 모델에서 추천 상품 5개를 추출했다. 추천된 상품이 이 테 스트 데이터세트 기간 동안 구매한 상품에 있는지 여부로 추천의 적합성을 판단하였다.



4) 머신러닝 기반 추천 모델
머신러닝 기반 추천 모델은 추천해줄 고객에 대한 기호를 정의하고, 고객과 유사한 기호를 가진 고객 구매 데이터를 기반으로 고객이 흥미를 가질 것으로 보이는 아이템을 검색한 후, 추천 대상에게 검색된 아이템을 추천해주는 모델이다.

우수조의 머신러닝 기반 추천 모델 구현은 Collaborative Filtering(협업 필터링) 방식으로 Spark 내 MLLib의 ALS알고리즘을 이용하여 진행했다. CF알고리즘에서 이용하는 속성은 고객ID, 상품코드, 선호점수(고객이 상품코드별로 구매한 건수)만을 이용하여 알고리즘을 수행하도록 하였다.

전체 데이터는 2013년 1월부터 2016년 10월까지의 구매 내역 데이터로 약 500만 건이다. 그 중 2015년까지 데이터를 9:1의 비율로 Training Data와 Validation Data로 나눴고, 2016 년 데이터는 Test Data로 이용했다. 추천이 잘된 경우 추천 결과의 상위 랭킹에 분리된 데 이터가 존재해야 한다. 추천 결과를 사용자별로 분리한 데이터의 상품리스트와 비교하여 0.0에서 1.0 범위의 값으로 표현한다. 1.0에 가까울수록 높은 추천결과로 판단한다.

Hyper Parameter값을 조절해가면서 추천 결과값이 가장 좋은 Parameter를 선정한다. AUC(Area Under the Curve)값이 가장 잘 나오는 Hyper Parameter값(rank, iterations,lambda, alpha)을 찾아가며 테스트를 수행했다.



//2016년 테스트 고객데이타를 1000명으로 한정
val testUsersInfo = testModelMap.take(1000)
//추천결과정보: 고객별 고객그룹에 대해 Training모델에서 추천하는 상품코드 리스트와
고객이 2016년 구매한 상품코드
// 리스트가 몇개나 일치하는 여부
var recommInfoList = ArrayBuffer.empty[List[Int]]
var recommProductMap = new mutable.HashMap[Int, Int] testUsersInfo.foreach {
testUserInfo => val custKey = testUserInfo._1
val custGrpSeq = testUserInfo._2._1
val buyProductSet = testUserInfo._2._2
val recommProductSet = trainingModelMap.get(custGrpSeq).get
val intersectSet = recommProductSet.intersect(buyProductSet)
val intersectCnt = intersectSet.size
recommInfoList.append(List(custKey, intersectCnt))
}
println("rate of recommend: %.2f".format(recommInfoList.filter(_(1) > 0).length * 1.0 /
recommInfoList.length * 1.0))
println("rate of recommend(cnt): %.2f".format(recommInfoList.map(_(1) / 5.0).sum /



추천 모델에 대한 테스트는 대상자 1,000명을 대상으로 모델에서 추천해주는 상품 5가지 가 2016년 구매 내역 데이터에서 얼마만큼 실제로 구매되었는지에 대한 비율로 추천 결과 를 판단하였다. 추천 테스트 결과 추천된 상품이 하나라도 있는 경우는 15%였다. 5개 추천 된 상품 중 몇 개가 있는지 판단하는 것은 3%였다. 5) 기초통계 기반 추천 모델 화장품 추천 시 일반적으로 베스트상품 추천이 가장 많이 이용되고 있다. 기초통계 기반 추 천모델은 머신러닝 기반 추천과 비교하여 어떤 모델이 더 나은 성능을 보이는지에 대한 비 교 모델로서 구현하였다. 데이터 속성 중 구매에 가장 영향도가 높은 것으로 예측되는 고객연령대, 성별, 피부타입과 구매계절 4가지 속성으로 고객 구매 데이터를 그룹화하여 총 528개 그룹을 군집화하였고, 각 그룹에 대해 가장 많이 팔리는 제품 Top5를 선정하였다. 이후 추천 대상 고객을 계절, 연령, 성별, 피부에 따라 분류하고 분류된 그룹의 Top 5 상품을 추천하였다.



val trainingModelGroup = spark.sql("SELECT sex, skinType, age, season,
productCode, sum(Count) as ncount FROM customer_group group by sex, skinType,
age, season, productCode order by sex, skinType, age, season, ncount desc")
// Training DataSet을 고객그룹별로 묶어 상품코드 나열(최대 5개 상품까지)
// Map(Seq(sex, skinType, age, season) => Set(productCode))
var trainingModelMap = new mutable.HashMap[Seq[Int], mutable.Set[Int]]
trainingModelGroup.collect().foreach { row =>
val keySeq = Seq(row.getAs[String](0).toInt, row.getAs[String](1).toInt, row.getAs[Int]
(2), row.getAs[Int](3))
val valueSet = trainingModelMap.get(keySeq)
if (valueSet.isEmpty) {
trainingModelMap.put(keySeq, mutable.Set(row.getAs[Int](4)))
} else {
if (valueSet.get.size < 5) {
trainingModelMap.put(keySeq, valueSet.get + row.getAs[Int](4))
}
}
}
trainingModelMap.take(10)



기초통계 기반 추천 모델의 데이터 세트는 머신러닝 기반 추천 모델과 동일하게 2015년까 지의 데이터를 Training Data로 이용하였으며, 동일한 방식으로 1,000명의 대상자를 선정하 여 추천한 상품 5가지가 2016년도에 얼마나 구매되었는지 비율을 측정하였다



The Outcome

이번 프로젝트를 통해 고객 정보 기반 화장품 추천 시스템을 기초통계 기반 추천모델과 머 신러닝 기반 추천모델로 만들어보았다. 이 두 모델을 만드는데 이용하지 않은 2016년 고객 구매 데이터로 고객 구매를 예측해보니, 기초통계 기반 추천모델은 12%, 머신러닝 기반 추 천모델은 15%의 예측도를 보여 머신러닝 기반 추천모델이 3%의 개선 효과를 가져왔다. 머 신러닝 기반 추천모델을 적용할 경우 더 의미 있는 매출 신장이 이뤄질 것으로 기대된다.

우수조는 화장품 추천 서비스라는 명확한 주제를 가지고 프로젝트를 진행했다. 또한 주제 에 해당하는 화장품 판매와 관련된 업무를 하는 구성원이 있어 데이터 확보를 비교적 쉽게 이뤘을 뿐만 아니라, 그를 통해 구성원들이 관련 비즈니스를 비교적 쉽게 이해하고 프로젝 트에 접근할 수 있었다. 아울러 구성원들이 데이터 분석에 대해 어느 정도의 역량을 가지고 있었고 이번 주제와 유사한 프로젝트 경험을 가지고 있는 구성원도 있어서 프로젝트 진행 에 많은 도움이 되었다.

다만 머신러닝 기반 추천 모델을 구현할 때, 원래는 Spark2.0버전으로 python을 이용하여 구현할 계획이었다. 그러나 추천 알고리즘 구현을 위한 참조 샘플이 Spark1.6버전에 scala 로 되어 있어 이번에 구현한 내용도 거기에 맞춰 구현되었다. 향후 Spark2.0버전에 맞춰 python으로 변환하여 다시 구현할 계획이다.

또한 구성원들은 진행과정에서 추천 결과에 대한 평가를 어떻게 할 것인지를 고민했다. 추 천 결과 평가는 결국 추천한 상품을 고객이 얼마나 구매할 것인지로 평가하는 것이다. 이 프로젝트를 진행할 때는 이미 화장품을 구매한 과거의 데이터를 기반으로 추천과 추천결과 에 대한 평가를 해야 했기에, 구성원들은 제대로 된 결과 평가를 구현이 이뤄지지 않았다고 본다. 그래서 추천모델에 대한 테스트 방법도 개선하고자 한다. 단순히 추천한 상품이 구매 내역에 있는지를 가지고 판단하는 수준을 벗어나서, 향후 실무에 적용하거나 다른 추천 서 비스를 구현할 때는 상품 추천을 통해 전체적으로 매출향상에 얼마나 기여했는지를 판단할 수 있는 모델을 만들어 적용해보고자 한다.



def recommend(sc: SparkContext, rawData: RDD[String],rawTestData: RDD[String]): Unit = {
//2015년 까지 Data이용 Model 생성
val allData = rawData.map { line =>
val Array(userId, itemId, count) = line.split('|').map(_.toInt)
Rating(userId, itemId, count)
}.cache()
val model = ALS.trainImplicit(allData, 50, 5, 1.0, 1.0) allData.unpersist()
//2016년 이용자 데이타로 추천 (5개씩 추천 item 선정) - 사용자가 많아 1000명으로 한정
val testUsersInfo = getTestUsersInfo(rawTestData).take(1000)
var recommInfoList = ArrayBuffer.empty[List[Int]]
testUsersInfo.foreach { testUserInfo =>
val userId = testUserInfo._1
val buyItemList = testUserInfo._2
//userFeatures에 사용자가 있는 경우만 예측가능
if (!model.userFeatures.lookup(userId).isEmpty) {
val recommRatings = model.recommendProducts(userId, 5)
val recommProducts = recommRatings.map(_.product).toSet
val intersectCnt = buyItemList.intersect(recommProducts).size
recommInfoList.append(List(userId, intersectCnt))
}
}
println("rate of recommend: %.2f".format(recommInfoList.filter(_(1) > 0).length * 1.0 / recommInfoList.length * 1.0))
println("rate of recommend(cnt): %.2f".format(recommInfoList.map(_(1) / 5.0).sum / recommInfoList.length * 1.0))



출처 : 한국데이터진흥원

제공 : 데이터 전문가 지식포털 DBguide.net