R에서 Hashmap 사용하기

대부분의 컴퓨터언어에는 키로 값을 꺼내올 수 있는 자료구조인 hashmap을 제공합니다.

Python에는 자료구조 딕셔너리 dictionary를 지원합니다.

R에도 env라는 것이 있어서 딕셔너리 형태로 쓸 수는 있지만 R의 environment는 작업 공간으로도 사용되기 때문에 사용하기 복잡하고 부담스럽습니다. 학습장벽도 높은 편입니다.

그런데 다행히도 r2r 패키지라는 것이 있어서 이 패키지를 이용하면 R에서도 hashmap을 쓸 수 있습니다.

R-Object to R-Object Hash Maps

해시 테이블은 효율적인 코딩을 위한 가장 유용한 자료구조 중 하나입니다. 추상적으로 해시 테이블은 고유 키(문자열, 숫자 또는 더 복잡한 객체일 수 있음)로 값을 가르키게 한다고 생각 할 수 있습니다. 계산적 관점에서 볼 때 가장 두드러진 특징은 읽기/쓰기 작업(예: 특정 키 또는 키-값 쌍 저장, 검색 또는 삭제)이 테이블과 관계없이 평균 시간복잡도가 O(1)입니다.

많은 프로그래밍 언어는 해시 테이블이 각각의 방법으로 구현된 것이 제공됩니다. 예를 들면 C++의 std::unordered_map/sets와 Python의 dicts 및 set이 있습니다. 기본 R에서는 해시 테이블에 가장 가까운 객체는 environment입니다. 하지만 environment는사용자의 관점에서 처리하기 다소 번거로울 수 있으며 문자열타입의 키만 지원합니다.

r2r패키지는 기본 R 환경에서 제공하는 environment보다 R에서 쓸 수 있는 더 유연한 해시 테이블 기능을 구현해서 제공합니다.

특히 r2r 해시 테이블은 다음을 지원합니다.

  • 키와 값으로 임의의 R 객체
  • 임의의 키 비교 및 해시 함수
  • 누락된 키 예외에 대한 사용자 정의 가능한 동작(기본값 던지거나 반환)

여기에서는 r2r 해시 테이블에 대한 간단한 실습용 코드를 소개합니다.

우선 r2r 패키지를 로딩합니다.

library(r2r)

기본 사용법 Basic Manipulations

다음을 사용하여 빈 해시맵(해시 테이블)을 만듭니다.

m <- hashmap()

여러 가지 방법으로 m이라는 해시맵에 키-값 쌍을 삽입할 수 있습니다.

m[["key"]] <- "value" 
m[c(1, 2, 3)] <- c("a", "b", "c") 
# Vectorized over keys and values 
m[[c(4, 5, 6)]] <- c("d", "e", "f") 
# Not vectorized

다음 쿼리는 위의 설명에서 언급한 [[ 및 [ 연산자 간의 차이점을 설명합니다.

m[["key"]]
#> [1] "value"

m[c(1, 2, 3)]
#> [[1]]
#> [1] "a"
#> 
#> [[2]]
#> [1] "b"
#> 
#> [[3]]
#> [1] "c"
m[[c(1, 2, 3)]]
#> NULL

m[c(4, 5, 6)]
#> [[1]]
#> NULL
#> 
#> [[2]]
#> NULL
#> 
#> [[3]]
#> NULL
m[[c(4, 5, 6)]]
#> [1] "d" "e" "f"

단일 요소 삽입 및 쿼리는 제네릭 insert() 및 query()를 통해 수행할 수도 있습니다.

insert(m, "user", "vgherard") # Modifies `m` in place
query(m, "user")
#> [1] "vgherard"

세트 Sets

해시 테이블 외에도 단순히 키를 저장하는 해시 세트를 만들 수도 있습니다.

s <- hashset()
insert(s, 1)
s[[2]] <- T # equivalent to insert(s, 2)
s[c(1, 2, 3)]
#> [[1]]
#> [1] TRUE
#> 
#> [[2]]
#> [1] TRUE
#> 
#> [[3]]
#> [1] FALSE

키와 값의 타입 Key and value types

키와 값으로 사용할 수 있는 개체 유형에는 제한이 없습니다.

예를 들면 다음과 같이 모델을 키로 사용할 수도 있습니다.

m[[ lm(wt ~ mpg, mtcars) ]] <- list("This is my fit!", 840)
m[[ lm(wt ~ mpg, mtcars) ]]
#> [[1]]
#> [1] "This is my fit!"
#> 
#> [[2]]
#> [1] 840
m[[ lm(cyl ~ mpg, mtcars) ]]
#> NULL

기본값 설정하기 Setting default values

누락된 키에 대한 기본값을 설정할 수 있습니다.

다음은 그 예입니다.

m <- hashmap(default = 0)

이 기능은 카운터를 만드는 데 유용합니다.

objects <- list(1, 1, "1", FALSE, "1", 1)
for (object in objects)
    m[[object]] <- m[[object]] + 1
m[["1"]]
#> [1] 2

누락된 키를 참조하려고 할 때 exception을 throw할 수 있습니다.

m <- hashmap(on_missing_key = "throw")
tryCatch(m[["Missing key"]], error = function(cnd) "Oops!")
#> [1] "Oops!"

커스텀 키 비교와 해시 함수 Using custom key comparison and hash functions

hashmaps와 hashmaps는 기본적으로 base::identical()을 사용하여 키를 비교합니다.

m <- hashmap()
m[[1]] <- "double"
m[["1"]] <- "character"
m[[1]]
#> [1] "double"

이 동작은 키 비교 기능을 명시적으로 제공하여 변경할 수 있습니다. 올바르게 작동하게 하려면 동등한 키에 대해 동일한 해시를 생성하는 해시 함수를 명시적으로 제공해야 합니다.

이를 수행하는 간단한 방법은 다음 예제와 해시맵을 생성할 때 키에 전처리 함수를 정의할 수 있습니다.

m <- hashmap(key_preproc_fn = Arg)

키가 하나의 복소수라고 가정할 때 복소수 평면에서 방향이 같을 때 두 개의 키를 동등하게 간주합니다. 복소수 벡터의 방향은 R 함수 Arg()를 사용해서 구할 수 있기 때문에 Arg함수를 사용하는 걸이 이 경우에는 맞습니다. 생성자의 key_preproc_fn 인수를 통해 이러한 방식으로 키를 사전 처리하도록 해시맵에 지정할 수 있습니다.

의도한 대로 잘 작동하는지 확인해 봅니다.

m[list(1, 1 + 1i, 1i)] <- list("EAST", "NORTH-EAST", "NORTH")
m[[10]]
#> [1] "EAST"
m[[100i]]
#> [1] "NORTH"
m[[2 + 2i]]
#> [1] "NORTH-EAST"

참고: https://cran.r-project.org/web/packages/r2r/vignettes/r2r.html

Confusion Matrix 혼동 행렬

Confusion Matrix(컨퓨전 매트릭스)는 한국어로 혼동행렬이라고 부릅니다.

혼동행렬이라는 단어가 어색해서 흔히 영어 발음 그대로 컨퓨전 매트릭스라고 부릅니다.

Confusion matrix는 기계학습 모델 중에서 분류 모델의 성능을 측정하고 학인하기 위한 간단한 계산식과 표입니다. 기계학습의 분류모델의 성능 측정을 위한 지표 계산법 이라고 기억하시면 됩니다.

인터뷰나 멘토링을 할 때 기계학습 초보자인지 아닌지를 확인하는 가장 쉬운 방법은 Confusion Matrix를 설명하라고 하거나 어떤 모델에 대한 설명을 한 후 Confusion Matrix에서 어떤 것을 보는게 좋을지에 대해서 물어보고 대답을 어찌하는지 보면 확인할 수 있습니다.

Confusion Matrix에 대해서 잘 모른다면 기계학습에 대한 지식이 아예 없다고도 볼 수 있습니다.

흔히 Confusoin matrix는 위의 그림에서 좌상단에 있는 조그만 박스를 말하는데 기계학습을 하다가 보통 Precision, Recall과 같은 지표 이름이 나오면 Confusion matrix라고 생각하면 됩니다.

위의 표가 행렬처럼 생겨서 혼동행렬이라고 하는것인데

이름이 왜 혼동이라는 단어가 들어가서 혼동행렬인지 단어의 뜻을 이해하면 저게 왜 복잡한지 알 수 있습니다.

기계학습 모델의 문제는 “그 모델이 왜필요하며 어떤 문제를 해결하기 위해서 필요한가 ”에 따라서 어떤 지표를 봐야 하는지를 결정해야 하고 어떤 지표와 어떤 지표가 상관관계가 있는지 상호충돌(trade off)인지도 잘 이해하지 않으면 지표를 보는 것(해석하는 요령)이 어렵습니다.

그래서 혼동행렬이라고 부릅니다.

Precision을 중요하게 봐야 하는 모델, Recall을 중요하게 봐야 하는 모델, 둘 다 봐야하는 모델 등 각기 다른데 모델의 각 지표에 대한 중요도와 우선순위는 모델이 처한 상황과 산업, 모델이 해결해야 하는 문제에 따라 다릅니다.

어쨌든 혼동행렬에서 중요한 기본 4개의 지표는

  • TP: True Positive. 모델이 긍정이라고 한 것중에 정답도 긍정인 것
  • FP: False Positive. 모델은 긍정이라고 했지만 정답은 부정인 것
  • FN: False Positive. 모델이 부정이라고 한 것중에 정답이 긍정인것
  • TN: True Negative. 모델이 부정이라고 한 것중에 실제로 정답도 부정인 것

TP, TP, FN, TN은 모두 카운트한 값으로 빌드한 모델을 테스트 데이터에 대해서 실험해서 나온 결과로 구하게 됩니다.

위의 기본 지표도 상당히 헷갈리는데 간단하게 이해하려면 True와 False는 기계(모델)이 한 행위가 맞다 틀리다를 표현하는 것이고 Positive와 Netagive는 모델이 데이터 레코드를 어떻게 판별했는가를 나타냅니다.

True Positive라면 모델은 데이터레코드를 긍정이라고 판단했고 True이기 때문에 모델이 긍정이라고 한 것이 맞다! 라는 뜻입니다.

False Negative라면 모델은 데이터 레코드를 부정으로 판단했고 False이기 때문에 모델이 그렇게 판단한 것이 틀렸다는 의미입니다. 풀어서 설명하면 False Negative는 모델이 부정이라고 했지만 사실 정답은 긍정인것이라 모델이 네가티브로 판별한 것은 틀렸다는 의미입니다.

그 뒤에 2차 지표를 구하게 되는데

이것들이 precision, Recall, Accuracy, F1-score, F-beta score 같은 것들입니다.

이 지표들은 Confusion Matrix안에 있는 4개의 기초 카운트값으로 계산해서 구할 수 있고 보통 비율값으로 계산이 됩니다.

RFM 분석, RFM Analysis, 리테일 고객 분석하기

RFM에 대해서 간단하게 정리한 것을 올립니다.
더 복잡한 분석기법이나 모델링 방법은 다른 포스트에 올려보겠습니다.

RFM 정의

RFM은 마케팅 분석 방법 중 하나입니다.
온라인 리테일(물건을 판매하는 사업)에서 고객 군집을 고객의 최종 방문수, 유휴시간, 소비금액을 기준으로 나누고 각 군집이 어떻게 유지되고 변화하는지에 따라서 현재 비즈니스 상태를 파악하고 문제가 있다면 어떻게 대응해야 할지를 판단하는데 쓰는 방벘입니다.

온라인 리테일이라면 쿠팡, 위메프, 티몬, 지마켓, 11번가, SSG 등을 생각하면 쉬울 것입니다.

RFM은 디지털마케팅 기법을 이용해서 기업을 성장시키는 그로쓰해킹 (Growth Hacking)에서도 자주 쓰이는 분석지표입니다. 그래서 디지털 마케터, 비즈니스 데이터 분석가라면 반드시 알아야 하는 분석 방법입니다.

RFM의 용어 정리

RFM – Recency, Frequency, Monetary 의 각각 앞 글자를 따서 RFM이라고 부릅니다.

비슷한 것으로 RFD가 있으나 Monetary가 Duration으로 바뀐것 외에는 동일합니다. Duration는 체류기간입니다.

RFD – Recency, Frequency, Duration

RFM에서 고객의 방문 또는 구매 주기의 관점에서 볼 때 구매액은 크게 중요하지 않고 그 보다는 체류시간 같은 것이 더 중요한 비즈니스에서는 머니테리 대신 듀레이션을 사용하기도 합니다.

체류시간이 중요한 비즈니스는 게임입니다. 게임의 체류시간은 어떤 면에서는 게임에서 인앱구매를 하는 것 보다 더 중요할 수 있으며 정액제 게임이고 게임내 구매가 없는 경우라면 활성유저가 많아야 게임이 흥하기 때문입니다. 어쨌든 RFD 분석도 RFM 분석과 같은 것입니다.

RFM과 마케팅 전략

RFM을 한국어로 번역하면 최신성, 빈발성, 통화성(체류성) 으로 해석할 수 있으나 한국어로 번역하면 매우 어색하므로 그대로 리센시, 프리퀀시, 머니테리로 부릅니다.

광고주가 그들의 관점에서 고객(Customer)을 세그멘테이션하고 프로파일링하고 고객의 가치를 판단하고 마케팅 전략을 세우는 데 활용하는 방법입니다.

앞서 말했듯이 리테일 비즈니스 업체 (소비재 물건을 판매하는 업체) 또는 온라인 서비스 업체(넷플릭스, 유튜브, 멜론 같은)가 고객을 분할해서 구분하고 관리하는 오래된 고객 관리 및 분석 기법입니다.

RFM 계산하기

RFM은 고객에게 R, F, M 각각의 점수를 부여하고 그 다음에 점수들을 다시 몇개의 적은 그룹으로 묶은 뒤에 구분한 그룹들을 관리하게 됩니다.

R 점수

R(Recency)은 오늘로 부터 최종 방문한 날까지의 날짜수를 계산합니다. 10일전에 왔다면 R은 10이 됩니다. R점수는 적을 수록 좋은 것입니다.

F 점수

F(Frequency)는 고객이 가입 또는 최초방문 또는 최초구매일로부터 지금까지 구매한 수입니마. 구매한 횟수 일수도 있고 구매한 물건 수 일수도 있지만 보통은 트랜잭션을 기준으로 합니다. 그러니까 결제를 한 번했다면 물건을 여러번 사도 1회로 카운트합니다. F는 높을 수록 좋습니다.

M 점수

M(Moneytary)는 구매한 금액의 총액입니다. F와 마찬가지로 지금까지 구매한 트랜잭션의 금액을 모두 더하면 됩니다. 반품이나 결제취소는 할 수 있다면 계산에서 빼야합니다.

RFM의 점수 분할 척도

R, F, M을 각각 기본 5점 척도로 분할해서 총 125가지 유형으로 고객을 중규모(세그먼트 수가 100을 넘지 않음) 분할해서 관리 계획을 세우고 대책을 만들어 정복합니다.

R, F, M은 각각 척도를 주고 양적점수에 의한 순서로 등분할 하는데 (Quantile) 척도는 3 ~ 5점 등으로 분석하는 사람과 업체에 따라 다르게 할 수 있으나 기본 5점으로 분할하는 예가 가장 많습니다.

아까 구한 R값을 기준으로 역순정렬해서 퀀타일로 고객그룹을 5개로 균등분할해서 각 고객에게 R점수를 1 ~ 5점으로 부여합니다. R은 숫자가 낮을 수록 좋은 것이기 때문에 역순정렬합니다.

F값은 순 정렬해서 역시 순서대로 5개로 균등분할해서 F점수를 부여하고 M도 마찬가지로 순정렬해서 5개로 부여합니다.

예를들어 지금까지 가입한 고객이 총 100명이라면 R=1은 20, R=2도 20, R=3도 20, R=4도 20, R=5도 20명씩이 됩니다. F와 M도 동일하게 각 점수별로 20명씩이됩니다.

퀀타일은 어떤 기준값을 가지고 정렬을 해서 순서대로 균등(또는 최대한 균등

표 – 5점 척도에 의한 RFM 분할

RecencyFrequencyMonetary
555
444
333
222
111

위와 같이 되는데 중요한 것은 나뉘는 그룹이 총 5 x 5 x 5 = 125개 일것 같지만 실제로는 125개가 안될 수도 있습니다.

R=1, F=5, M=5와 같은 특정 조합에 해당하는 고객이 한명도 없을 수 있기 때문입니다.

어쨌든 이렇게 해서 고객들은 최소 5개에서 최대 125개의 그룹으로 나눕니다. 보통 100개 이상의 그룹에 모두 고객이 존재하게 되는데 이건 관리하기 너무 많고 인사이트를 얻기에도 힘듭니다. 그래서 이 그룹들을 다시 관리하기 좋게 의미와 이름을 부여해서 나누고 관리합니다.

캠페인의 전환율을 RFM으로 파악하는 법

캠페인의 전환률이 매우 낮다면 그 이유는 적절한 고객(Customer)에게 타겟팅하지 않았기 때문입니다. 고객 세그먼트는 각각 동일한 캠페인에도 다르게 반응하기 때문입니다.

그래서 고객을 구매 행동에 따라 다르게 분류해야 할 필요가 있고 분류된 세그먼트별로 다른 접근 방법으로 다르게 접근해야 합니다.

충성고객에게는 상품이 형편없지만 않으면 구매를 합니다. 하지만 비충성 고객에게는 매리트가 충분히 있는 비교적 가격 경쟁력이 높으면서 구매 부담이 없는 제품을 캠페인으로 보여주지 않으면 클릭하지 않습니다.

RFM은 행동 기반의 고객 세그먼트를 위해 이미 증명된 마케팅 모델입니다. RFM은 거래 내역(transaction history)으로 얼마나 최근에, 얼마나 자주, 얼마나 많은 돈을 지불했는지로 고객을 그루핑(grouping) 합니다.

RFM은 파라미터 3개를 사용하는 이유는 고객 가치평가는 1개의 파라미터만으로는 부족할 수 있기 때문입니다.

RFM의 3개의 척도중에서 보통은 가장 좋은 고객은 가장 많은 금액을 지불한 고객이라는 데는 대부분 동의하지만 그 고객이 아주 오래전에 딱 한 번 구매를 했고 더 이상 우리 제품을 사용하지 않는다면 최고의 고객이 아닐 가능성이 큽니다.

그래서 RFM은 3개의 고객 속성으로 고객을 순위화(rank)합니다.

고객이 최근에 구매했다면 더 높은 점수를 주고

고객이 빈번하게 구매했다면 더 높은 점수를 주고

고객이 많은 돈을 지불했다면 더 높은 점수를 줍니다.

그리고 3가지 점수를 병합해서 RFM 점수를 만듭니다.

그런 후에 고객을 RFM(Recency – Frequency – Monetary) 점수로 서로 다른 그룹으로 분할할 수 있다. 반대로 말하면 동질 그룹으로 묶을 수 있게 됩니다.

고객을 여러 다른 세그먼트 체계로 분할 수 있지만 보통 11개 정도로 분할하는 것을 추천하고 있습니다.

RFM 세그먼트 분할표1

#세그먼트 (Customer Segment) 별칭한글 별칭활동 (Activity)대응 방안 (Actionable Tip)
1Champions챔피온Bought recently, buy often and spend the most!Reward them. Can be early adopters for new products. Will promote your brand.
2Loyal Customers로얄(충성고객)Spend good money with us often. Responsive to promotions.Upsell higher value products. Ask for reviews. Engage them.
3Potential Loyalist잠재 로얄 (잠재적 충성고객)Recent customers, but spent a good amount and bought more than once.Offer membership/loyalty program, recommend other products.
4Recent Customers단골 고객Bought most recently, but not often.Provide onboarding support, give them early success, start building a relationship.
5Promising유망 고객Recent shoppers, but haven’t spent much.Create brand awareness, offer free trials
6Customers Needing Attention주목해야 할 고객Above average recency, frequency and monetary values. May not have bought very recently though.Make limited time offers, Recommend based on past purchases. Reactivate them.
7About To Sleep휴면 직전 고객Below average recency, frequency and monetary values. Will lose them if not reactivated.Share valuable resources, recommend popular products / renewals at discount, reconnect with them.
8At Risk떠나면 위험한 고객Spent big money and purchased often. But long time ago. Need to bring them back!Send personalized emails to reconnect, offer renewals, provide helpful resources.
9Can’t Lose Them붙잡아야 할 고객Made biggest purchases, and often. But haven’t returned for a long time.Win them back via renewals or newer products, don’t lose them to competition, talk to them.
10Hibernating동면 고객Last purchase was long back, low spenders and low number of orders.Offer other relevant products and special discounts. Recreate brand value.
11Lost떠난 고객Lowest recency, frequency and monetary scores.Revive interest with reach out campaign, ignore otherwise.

RFM 세그먼트 분할표2

통계학적 RFM Analysis를 위해서 예제로 제시한 RFM 점수와 세그먼트 분할표

SEGMENTDESCRIPTIONRFM
ChampionsBought recently, buy often and spend the most4 – 54 – 54 – 5
Loyal CustomersSpend good money. Responsive to promotions2 – 43 – 44 – 5
Potential LoyalistRecent customers spent good amount, bought more than once3 – 51 – 31 – 3
New CustomersBought more recently, but not often4 – 5< 2< 2
PromisingRecent shoppers, but haven’t spent much3 – 4< 2< 2
Need AttentionAbove-average recency, frequency & monetary values3 – 43 – 43 – 4
About To SleepBelow average recency, frequency & monetary values2 – 3< 3< 3
At RiskSpent big money, purchased often but a long time ago< 32 – 52 – 5
Can’t Lose ThemMade big purchases and often, but a long time ago< 24 – 54 – 5
HibernatingLow spenders, low frequency, purchased a long time ago2 – 32 – 32 – 3
LostLowest recency, frequency & monetary scores< 2< 2< 2

이 세그먼트 분할 기준으로 여러 세그먼트에 중복 분류되는 고객군이 있을 수 있으므로 다소 문제가 있을 수 있다.

문제점

  • 조건 적용에 순서에 따라 분할되지 않은 세그먼트가 발생하므로 범위에 따른 조건의 적용 순서를 재배열 해야함
  • 조건에 포함되지 않는 고객들이 있음
    • 모든 고객들이 꼭 1개의 세그먼트에 포함되어야 하는가에 대한 결정 문제가 있음
    • 고객들이 2개 이상의 세그먼트에 겹쳐서 포함되어도 괜찮은가에 대한 결정 문제가 있음
    • 세그먼트간 경곗값이 크지 않으면 세그먼트를 합치거나 중복해야 할 수 있음

코멘트

  • RFM 그룹에 따라 광고배너의 내러티브를 변경하는 것이 더 나을 수 있음
    • 저희의 최고의 고객이십니다. 고객님이 지금 가글을 한 병 사시면 한 병 더 드립니다. 뭐 이런식으로? 해줘야 한다.

RFM 점수 계산

RFM점수는 다음의 3가지 행동 데이터를 기반으로 계산합니다.

  • Recency (R): 마지막으로 구매한 후에 오늘까지 몇일이 지났는가?
  • Frequency (F): 지금까지 전체 구매(거래)가 몇번 있었는가?
  • Monetary (M): 지금까지 전체 구매한 금액은 얼마인가?

위의 값을 계산한 후에 1 ~ 5점의 척도로 바꾸면 RFM 점수(RFM score)가 된다.

RFM 데이터 처리 흐름 Data processing workflow

비닝 binning

Percentile 또는 Quantile 기반으로 개체수에 따른 등분할 합니다. 동일하거나 거의 비슷한 수가 되도록 분할하는 것입니다.

이것은 상대적 분할이기 때문에 절대 기준이라고생각해서는 안됩니다. RFM은 상대적으로 좋은 고객인지 위험한 고객인지를 아는 것이지 절대적으로 좋은 고객 안좋은 고객을 알아내는 지표는 아닙니다.

특히 업종별로는 고객의 구매 관련 행동 패턴의 양상이 다르므로 비교하는데 사용하기 어렵지만 같은 업종끼리라면 비슷한 업종의 회사의 RFM 고객 세그먼트는 비교대상이 됩니다.

RFM의 문제점

프로파일링 문제1

세그먼트를 분할하고 분할 세그먼트에 네이밍 또는 정의(definition)을 사람에 의해서 해석해야 하는 문제. 샘플이 있으므로 샘플 이용해 시도한 후에 정의하는 방법을 알아낼 수 있습니다.

프로파일링 문제2

세그먼트 분할 후 각 세그먼트의 분포와 개체수를 확인하고 나서 문제를 알게 되는데 문제의 원인을 알기 위해서는 고객사 현업의 경험 지식이 필요합니다.

프로파일링 문제3

세그먼트에 대한 분할 예제가 있으나 주관적(업체별)일 수 있어 공통된 세그멘테이션 방법으로 진행해도 무리가 없을지에 대한 판단이 필요

  •  우선 공통 기준으로 세그먼트를 모두 나눈 뒤 업종별 또는 광고주별로 세그먼트의 분할이 어떻게 되었는지 성과는 어떠했는지, 변화는 어떻게 되어 왔는지 구분해서 비교 리포트를 작성한다.
  • 작성한 후 세그먼트 분할 기준을 각각 따로 할지 공통으로 할지 재결정한다. 가능하다면 공통 기준으로 가는 것이 비교 분석을 하기 편리하므로 그에 대한 가중치를 주고 결정을 하도록 한다.
  • 타일을 분할할 때 타일간의 대푯값의 차이가 없는 경우 타일 수를 조절해야 할 수 있는데 조절하지 않고 대푯값의 차이와 분포의 현상태만 나열해 둔다. 

RFM 세그먼트 활용 방법

간단하게는 RFM세그먼트를 약 3가지로 활용할 수 있을 것으로 기대할 수 있습니다.

현재 사업의 상태를 파악할 수 있습니다.

  • 비즈니스가 망해가고 있는지 잘되고 있는지 조치가 필요한지를 알 수 있습니다.

비즈니스 개선을 위해서 집중해야 할 세그먼트를 알아낼 수 있고 세그먼트를 공략하기 위한 캠페인 진행 방법을 선택할 수 있습니다.

  • 어떤 세그먼트에 문제가 있는지? 고객 리텐션이 필요한지, 이탈 방지가 필요한지, 신규 고객 입수가 필요한지, 기존 고객의 구매 활성화가 필요한지 등
  • 위의 해석에 따라 캠페인 상품을 깔 맞춰서 제안할 수 있고 진행을 시도할 수 있다.

역으로 특정 캠페인에 적합한 세그먼트를 찾아내서 데이터 교환으로 활용할 수 있습니다.

  • 어떤 업체의 어떤 세그먼트는 어떤 다른 업체에게는 좋은 세그먼트가 될 수 있다.
  • 통신사에서 요금제를 새로 구매한 고객 세그먼트는 핸드폰 악세사리를 판매하는 광고에 활용한다던가 (이런 것을 스토리라인 캠페인이라고 부름)

참고 자료

글을 쓰면 2D 일러스트레이션을 생성해 주는 – AI Stable Diffusion Demo


허핑페이스에 공개되었다고 합니다.

제목대로 영어로 생성할 이미지에 대한 설명을 적으면 생성을 해줍니다.

이런 종류의 AI는 이미 이전에도 많이 나와있습니다만 이 AI는 품질이 상당히 좋다는 평가입니다.

실제로도 가능하니 테스트 해보세요.

🔗접속주소: https://huggingface.co/spaces/stabilityai/stable-diffusion

Python MySQL 쿼리 결과를 dict로 가져오기 – Get dictionary from query result of MySQL

Python에서 MySQL의 DB결과를 가져올 때

cursor.fetchall()를 사용하면 MySQL결과는 보통 리스트(list) 타입으로 가져오게 되서 쿼리에서 선택한 컬럼의 번호를 주고 꺼내와야 합니다.

컬럼이 몇개 안되면 괜찮지만 컬럼이 많거나 쿼리가 복잡하면 꺼내와서 짝을 맞추는 것이 어려우니 딕셔너리 타입으로 받으며 좋습니다.
보통은 다음과 같이 하지만

rows = cursor.fetchall()
for row in rows:
  id = row[0]
  name = row[1]
  print(id, name)

딕셔너리로 사용하기는 다음과 같이 쓰는 것을 말합니다.

rows = cursor.fetchall()
for row in rows:
  id = row['id']
  name = row['name']
  print(id, name)

다음과 같이 cursor를 생성할 때 옵션을 지정하면 쓸 수 있습니다.

import pymysql
connection = pymysql.connect(db="test")
cursor = connection.cursor(pymysql.cursors.DictCursor)
cursor. Execute("SELECT ...")

위의 단점은 데이터를 가져올 때 딕셔너리에 키를 다 넣으므로 데이터의 사이즈가 커진다는 것입니다 경우에 따라서 복잡하더라도 필드의 순번으로 데이터를 꺼내오는 것이 나은 경우가 있습니다.

무분별하게 사용하지 않도록 고민은 한 번 해야합니다.

LibLinear – 멀티코어 SVM 라이브러리 multicore SVM library

sVM 전통적으로 여전히 로버스트한 분류 문제를 해결하는데 쓰는 알고리즘입니다.

하지만 SVM의 문제점은

  • 빌드된 모델이 왜 그렇게 만들어졌는지 알 수 없는 블랙박스 모형이라는 것과
  • 멀티코어로 병렬처리해서 모델을 빌드하기 어렵다

입니다.

첫번째 문제는 어쩔 수 없다고 해도 멀티코어는 이제 해결이 된 것이 있습니다.

아래 링크에 있는 liblinear입니다.

https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/multicore-liblinear/

github 주소는 이곳입니다.

https://github.com/yesimon/LIBLINEAR

간단한 기여도 계산 함수

기여도

기여도란 어떤 성과지표에서 어떤 부분집합이 전체의 성과지표에 얼마나 형향을 주었는지 계산하는 것입니다. 리프트(lift)라고도 합니다. 리프트는 알고리즘이나 계산마다 각기 다르므로 일반적으로 말하는 리프트가 이것과 동일한 것은 아닙니다.

비율값이 지표인 경우에 총 지표를 만드는데 구성된 멤버 중 하나의 기여도가 얼마인지를 알아내는 간단한 함수입니다. 비율값이 아닌 지표에서의 기요도는 그냥 전체에서 해당 멤버가 차지하는 비중을 계산하면 되기 때문에 계산할 것이 없습니다.

비율값이 KPI인 경우를 예를 하나 들면 강고 배너 캠페인 5개를 운영하고 나서 전체 CTR과 각 배너별 CTR이 있을 때 배너별로 기여도를 계산하거나 하는 경우입니다.

비율값이 KPI인 경우에 기여도 계산이 어려운 이유

위의 표와 같은 5개의 캠페인이 있는데 클릭과 노출이 다 다릅니다. 이때 캠페인1과 캠페인2는 모두 CTR 0.5로 50%씩 동일합니다. 그런데 캠페인2는 캠페인1에 비해 노출과 클릭이 각각 모두 10배 많습니다.

상식적으로 생각할 때는 이런 경우 캠페인1이 기여도가 더 높아야 하는 것입니다.
기여도 계산에서 이것이 반영되야 합니다.

코드

코드는 R로 되어 있습니다. 하려고 했던 것은 calc_contrib_origin이라는 함수에서 계산할 때 분모가 0이 되는 문제로 기여도가 아예 계산이 안되는 것을 어떻게 해결할 것인가입니다.

이때 비율을 계산하는 것이기 때문에 분모가 0이 안되도록 하는 것이 중요한데 이 부분이 생각보다 까다롭습니다.

# 원래 함수
calc_contrib_origin <- function(total_z, total_c, z, c) {
  ((total_z / total_c) - ((total_z - z) / (total_c - c)))
}
 
# 멍청한 함수 v0
calc_contrib_mod1 <- function(total_z, total_c, z, c) {
  frac <- total_c / c
  ((total_z / total_c) - ((z*frac) / (c*frac)))
}
 
# 멍청한 함수 v1
calc_contrib_mod1 <- function(total_v1, total_v2, v1, v2) {
  frac1 <- total_v1 / total_v2
  frac2 <- total_v2 / total_v1
  if ((total_v1 == v1) && (total_v2 == v2)) {
    cat("case 1\n")
    (total_v1 / total_v2)
    right <- 0
  }
  else if ((total_v1 != v1) && (total_v2 == v2)) {
    cat("case 2\n")
    (total_v1 / total_v2) - ((total_v1 - (frac2 * v1)) / v2)
    right <- ((total_v1 - (frac2 * v1)) / v2)
  }
  else if ((total_v1 == v1) && (total_v2 != v2)) {
    print("case 3\n")
    (total_v1 / total_v2) - (v1 / (total_v2 - (v2 * frac1)))
    right <- (v1 / (total_v2 - (v2 * frac1)))
  } else {  #
    cat("case 4\n")
    (total_v1 / total_v2) - ((total_v1 - v1) / (total_v2 - v2))
    right <- ((total_v1 - v1) / (total_v2 - v2))
  }
  left <- (total_v1 / total_v2)
  cat(paste0(left, " - ", right, "\n"))
  return(left - right)
}
 
# 멍청한 함수 v2
calc_contrib_mod2 <- function(total_v1, total_v2, v1, v2) {
  # frac1 <- total_v1 / v1
  # frac2 <- total_v2 / v2
  frac1 <- total_v1 / total_v2
  frac2 <- total_v2 / total_v1
  # 기준값
  if ((total_v1 == v1) && (total_v2 == v2)) {
    cat("case 1\n")
    (total_v1 / total_v2)
    right <- 0
  }
  # 분모가 0인 케이스
  else if ((total_v1 != v1) && (total_v2 == v2)) {
    cat("case 2\n")
    #total_v1 / (total_v2 - v2)
    right <- (v1 / (total_v2 - (v2 * frac1)))
  }
  # 분자가 0인 케이스
  else if ((total_v1 == v1) && (total_v2 != v2)) {
    print("case 3\n")
    right <- total_v1 / (total_v2 - v2)
  } else {  #
    cat("case 4\n")
    (total_v1 / total_v2) - ((total_v1 - v1) / (total_v2 - v2))
    right <- ((total_v1 - v1) / (total_v2 - v2))
  }
  left <- (total_v1 / total_v2)
  cat(paste0(left, " - ", right, "\n"))
  return(left - right)
}
 
# 덜 멍청한 함수 v1
calc_contrib_mod1 <- function(total_z, total_c, z, c) {
  (((total_z + 1) / (total_c + 1)) - ((total_z - z + 1) / (total_c - c + 1)))
}
 
# 덜 멍청한 함수 v2
calc_contrib_mod2 <- function(total_z, total_c, z, c) {
  lb <- 1 - 1 / (total_c + 1)
  rb <- 1 - (total_z + 1) / 1
  print(c(lb, rb, rb - lb))
  ( (((total_z + 1) / (total_c + 1)) - ((total_z - z + 1) / (total_c - c + 1))) ) / (rb - lb)
}
 
calc_contrib_mod <- calc_contrib_mod1
 
# ※ 전체 비율에서 항목값들이 존재하지 않았을 경우를 계산하는 것이 전부
# ※ 따라서 전체 비율을 계산하고 대상 항목의 수치를 포함하지 않은 것의 비율을 계산해서 뺀다
 
# 간단한 테스트
calc_contrib_mod(50, 100, 49, 60)
calc_contrib_mod(50, 100, 50, 60)
calc_contrib_mod(50, 100, 50, 59)
 
# 계산검증1. (o)
calc_contrib_mod(200, 250, 202, 250) # 아래 보다는 높아야 함 (o)
calc_contrib_mod(200, 250, 201, 250) # 아래 보다는 높아야 함 (o)
calc_contrib_mod(200, 250, 200, 250) # 기여도 100% (기준 기여도와 동일) (o)
calc_contrib_mod(200, 250, 199, 250) # 위의 것 보다는 낮아야 함 (o)
calc_contrib_mod(200, 250, 198, 250) # 위의 것 보다는 낮아야 함 (o)
 
calc_contrib_mod1(200, 250, 200, 0)
 
# 계산검증1-1 (o)
calc_contrib_mod(200, 250, 101, 50) # 아래 것 보다는 높아야 함 (o)
calc_contrib_mod(200, 250, 100, 50) # 아래 것 보다는 높아야 함 (o)
calc_contrib_mod(200, 250, 100, 51) # 위의 것 보다 낮아야 함 (o)
 
# 계산검증2 (o)
calc_contrib_mod(250, 200, 251, 200) # 기준기여도 보다 높거나 같아야 함
calc_contrib_mod(250, 200, 250, 200) # 기여도 100% (기준 기여도와 동일)
calc_contrib_mod(250, 200, 249, 200) # 위의 것 보다 낮아야 함 (o)
calc_contrib_mod(250, 200, 248, 200) # 위의 것 보다 낮아야 함 (o)
calc_contrib_mod(250, 200, 247, 200) # 위의 것 보다 낮아야 함 (o)
 
# 계산검증3 (o) 점진적으로 높아지는
calc_contrib_mod(250, 200, 50, 100)
calc_contrib_mod(250, 200, 100, 100)
calc_contrib_mod(250, 200, 100, 50)
calc_contrib_mod(250, 200, 100, 2)
calc_contrib_mod(250, 200, 100, 1)
 
# 계산검증4 (o) 점진적으로 낮아지는
calc_contrib_mod(250, 200, 100, 1)
calc_contrib_mod(250, 200, 100, 2)
calc_contrib_mod(250, 200, 100, 99)
calc_contrib_mod(250, 200, 100, 100)
calc_contrib_mod(250, 200, 100, 101)
calc_contrib_mod(250, 200, 100, 102)
 
# 계산검증5 (o). 다운 방향으로 기여도가 떨어져야함
calc_contrib_mod(110, 110, 100, 1)
calc_contrib_mod(110, 110, 100, 2)
calc_contrib_mod(110, 110, 100, 99)
calc_contrib_mod(110, 110, 100, 100)
calc_contrib_mod(110, 110, 100, 101)
calc_contrib_mod(110, 110, 100, 102)
 
# 계산검증6 (오류)
calc_contrib_mod(110, 110, 110, 1) # 아래것 보다 좋음 (o)
calc_contrib_mod(110, 110, 110, 2) # 아래것 보다 좋음 (o)
calc_contrib_mod(110, 110, 110, 99) # 아래것 보다 좋음 (o)
calc_contrib_mod(110, 110, 110, 100) # 아래것 보다 좋음 (o)
calc_contrib_mod(110, 110, 110, 101) # 아래것 보다 좋음 (o)
calc_contrib_mod(110, 110, 110, 102) # 아래것 보다 좋음 (o)
calc_contrib_mod(110, 110, 110, 109) # 아래것 보다 좋음 (x)
calc_contrib_mod(110, 110, 110, 110) # 기준
calc_contrib_mod(110, 110, 109, 109)
calc_contrib_mod(110, 110, 108, 108)
 
calc_contrib_mod(110, 110, 110, 111) # ※ 입력이 잘못된 케이스.
calc_contrib_mod(110, 110, 110, 112) # ※ 입력이 잘못된 케이스.
 
# 계산검증7 (x)
# 항목값이 가분수가 되면 결과값이 마이너스가 되는데
# 이것들이 기준값과 진분수인값들과 차이가 생긴다.
calc_contrib_mod(210, 110, 110, 1) # 아래것 보다 좋음 (o)
calc_contrib_mod(210, 110, 110, 2) # 아래것 보다 좋음 (o)
calc_contrib_mod(210, 110, 110, 99) # 아래것 보다 좋음 (o)
calc_contrib_mod(210, 110, 110, 100) # 아래것 보다 좋음 (o)
calc_contrib_mod(210, 110, 110, 101) # 아래것 보다 좋음 (o)
calc_contrib_mod(210, 110, 110, 102) # 아래것 보다 좋음 (o)
calc_contrib_mod(210, 110, 110, 109) # 아래것 보다 좋음 (x)
calc_contrib_mod(210, 110, 110, 110)
calc_contrib_mod(210, 110, 100, 110) # 위 보다는 안좋음
calc_contrib_mod(210, 110, 1, 110) # 위 보다는 안좋음
 
# -- 비교 (origin함수) (x)
calc_contrib_origin(210, 110, 110, 1) # 아래것 보다 좋음 (o)
calc_contrib_origin(210, 110, 110, 2) # 아래것 보다 좋음 (o)
calc_contrib_origin(210, 110, 110, 99) # 아래것 보다 좋음 (o)
calc_contrib_origin(210, 110, 110, 100) # 아래것 보다 좋음 (o)
calc_contrib_origin(210, 110, 110, 101) # 아래것 보다 좋음 (o)
calc_contrib_origin(210, 110, 110, 102) # 아래것 보다 좋음 (o)
calc_contrib_origin(210, 110, 110, 109) # 아래것 보다 좋음 (x)
calc_contrib_origin(210, 110, 110, 110)
calc_contrib_origin(210, 110, 100, 110) # 위 보다는 안좋음
calc_contrib_origin(210, 110, 1, 110) # 위 보다는 안좋음
 
# 계산검증8. (o)
calc_contrib_mod(110, 210, 1, 110)
calc_contrib_mod(110, 210, 109, 110) # 위 보다는 좋음
calc_contrib_mod(110, 210, 110, 110) # 위 보다는 좋음
 
# origin 함수 확인
calc_contrib_origin(110, 210, 109, 110) # 위 보다는 좋음
calc_contrib_origin(110, 210, 110, 110) # 위 보다는 좋음
 
# 심1
calc_contrib_origin(110, 210, 109, 110)
calc_contrib_origin(210, 110, 110, 109)
 
# 산수 노트
at <- 50
bt <- 100
a <- 30
b <- 15
(at / bt) - ((at - a) / (bt - b))
(at / bt) - ((at - a) * (bt - b)^-1)
(2 - 3)^2
2^2 + -2*(2*3) + 3^2
(2 - 3)^-1
# a²-2ab+b² = (a-b)²

골 프로그래밍 – Goal Programming with Excel

골 프로그래밍은 제목만 봐서는 직감적으로 알기 어려울 수 있습니다.
최대한 목표에 가깜게 하는 조건을 찾아주는 선형최적화 방법입니다.
원래는 프로그래밍이나 별도의 최적화 도구를 사용하는데 최근에는 엑셀로도 많이 작업합니다.

첨부한 링크를 보시고 잘 따라해보면 어렵지 않습니다. 다른 포스트에서 자세한 설명을 하기로 하고 이 포스트에서는 간단한 개요만 설명드립니다.

이하 평어체로 씁니다.

개요

엑셀에는 해 찾기(solver, linear programming) 기능이 있다.
선형계획법(Linear Optimization)을 엑셀로 구현한 것이다.
하지만 단순 선형계획법은 단일 타겟에 대한 최적화만 할 수 있기 때문에 다중 타겟을 최적화하기 위해서는 목표계획법(goal programming)을 사용해야한다.

목표 계획법 (Goal Programming) 또는 MLOP (Multi Linear Optimization Programming)

difference 값을 추가 도입해서 여러 개의 목표함수를 하나로 만든다.
그 후 그 하나의 함수를 최소화하는 최적조합을 simplex 알고리즘으로 찾아낸다.

엑셀 및 코드

엑셀로도 goal programming을 할 수 있는 것으로 알려져 있다.
엑셀로 goal programming을 시도해서 각 조건에서 최적 선택이나 최적 점을 찾는데 사용한다.
이후 Python 또는 R로 코드로 포팅할 수 있다.

쓸모

  • “비융율과 전환율을 각각 최대로 맞출 수 있는 rqs 또는 cic의 연속 노출 지점을 찾으라” 같은 것
  • 내가 돌봐주는 광고주에 impression-capping을 하려고 한다. ci_c 값은 얼마가 최적값(optima)일까?
  • 새로 들어온 광고주의 일예산은 얼마가 적당할까?

참고자료

잘 설명된 슬라이드를 참고한다.

https://sceweb.sce.uhcl.edu/helm/SENG5332DecisionAlysys/my_files/TableContents/Module-13/ch07.ppt

프론트에서 활용하기

Excel로 할 수 있으면 R과 Python, Javascript를 이용해서도 “Goal programming”을 할 수 있다.

최적값 찾는 기능을 서비스에 넣거나 배치 프로세스에서 자동으로 설정하게 할 수 있다.

따라해보기

단순 “해 찾기” Linear programming

“해 찾기”를 먼저 해본다. (“Goal programming”이 아님)

이것은 매우 쉽다. (바보가 아니면 다 할 수 있다)

cost_rate와 roas를 7:3의 가중치로 최대화하는 rqs의 연속 선택 시퀀스를 30개 이내에서 찾으라. 단 선택을 한다면 rqs=0부터 시작해야 한다.

local target 계산은 가중조화평균(아무짝에도 쓸모 없는 것이지만 테스트해본다)

패널티 트릭 Penalty trick

골 프로그래밍의 핵심은 패널티를 주기 위해 선형식 디자인을 어떻게 하는 가 이다.

연습을 해보지 않으면 디자인하는 것이 매우 헷갈리기 때문에 디자이닝에 대한 훈련이 되어 있어야 하고 고민도 깊이 해야 한다.

CTR이 높고 CVR이 낮은 것과 CTR이 낮고 CVR이 높은 것 중 어느 것이 좋은가?

광고 캠페인을 운영하다보면 비슷한 또는 동일한 캠페인인데 매체 또는 DSP업체 성과가 다음과 같이 다른 경우가 있습니다.

  • CTR은 높지만 CVR이 낮은 것
  • CVR은 높지만 CTR이 낮은 것

CTR과 CVR은 단순하게는 DSP의 타겟팅 능력과 매체의 품질에 가장 큰 영향을 받습니다만
그럼에도 불구하고 둘 중 어느 것이 좋은가를 기술적으로 볼 때

  • 광고업체(매체와 DSP모두)의 입장에서는 1번이 좋습니다.
  • 광고주의 입장에서는 2번이 좋습니다.

아직도 광고비 과금을 CPC(클릭당 과금) 방식으로 많이하기 때문입니다. 클릭율이 높으면 과금율이 높아지므로 매체( CPC매체인 경우)와 광고회사에게 좋습니다.

하지만 여기에 생각할 부분이 더 있습니다.

CPC가 만약 가변인 경우입니다. 즉 몇몇 광고 업체처럼 가변 CPC로 KPI에 따라 목표치에 최대한 근접하게 광고를 운영한다고 하면 생각할 것이 많아집니다.

CTR이 낮아도 광고요금이 상대적으로 높을 수 있고 CTR이 높아도 광고요금이 낮을 수 있습니다. 즉 CTR만으로는 광고의 성과를 정확하게 알아낼 수 없습니다.

결국 cVR로 마찬가지가 됩니다. 클릭 후에 오디언스의 액션에 따라 전환되는 비율을 측정하는데 CVR이 조금 낮더라도 전환수가 많다면 CVR이 높아도 전환수가 많지 않은 것 보다는 좋습니다.

CPC가 만약 고정이라면

그래도 CTR이 높은 경우가 사실은 더 유리합니다.

CVR은 원래 CTR보다 높게 형성되지만 보통 물건의 품질, 가격경쟁력, 브랜드파워에 따라 달라집니다. 그럼에도 불구하고 cTR이 높아져서 전환수가 많아지는 것이 전환률이 높은 것보다는 광구주 측이의 매출이나 이윤이장에서는 유리합니다.

결론

많은 경우에 CTR이 높고 CVR이 낮은 것이 더 좋습니다. 그 반데의 케이스보다는 유리합니다. 하지만 항상 그런 것은 아닙니다.

쉽게 이해하는 XGboost

요약

XGboost는 기계학습에서 사용하는 결정 트리(Decision Tree)라는 계열의 알고리즘 중 하나입니다. 중요한 특징으로는 분산 컴퓨팅으로 기계학습 모델을 빌드 할 수 있습니다.

즉 어려대의 서버로 대량의 학습데이터를 사용해서 결정트리 기계학습 모델을 만들 수 있게 해주는 기계학습 프레임워크(알고리즘)입니다.

결정 트리 (Decision Tree)

결정나무라고도 번역하는데 이게 느낌이 너무 이상해서 대부분 디씨젼트리 또는 결정트리라고 부릅니다.

결정트리 (Decision Tree)의 계보는 CART부터 시작해서 밑에 그림과 같습니다. 뒤에 LightGBM아 몇개가 더 있습니다만 XGboost까지의 계보는 저렇습니다.

여기서 아마 역사적으로 가장 잘 알려진 것은 “랜덤포레스트”일 것입니다.

랜럼포레스는 결정트리에 배깅 기법을 추가한 것이고
GBM은 결정트리에 부스팅 기법을 추가한 것입니다.

GBM은 분류 알고리즘이라면 어떤 알고리즘이라도 사용할 수 있지만 결정트리가 가장 쓰기 편하고 좋기 때문에 GBM은 결정트리를 주로 씁니다.

XGBoost: A Scalable Tree Boosting System

개요

  • XGBoost = eXtream Gradient Boosting
  • Gradient Boosted Decision Tree의 분산 컴퓨팅을 위한 새 구현체
  • 정확히 설명하면 GBM(Gradient Boosting Machine)의 분산 환경을 구현체

잘 알려지지 않았지만 GBRT (Gradient Boosting Decision Tree)는 정말 성능이 좋은 알고리즘이지만 모델 빌드속도가 매우 느리고 분산 노드를 이용해서 빌드 속도를 단축시키는 것이 가능하지 않다는 문제가 있습니다. GBRT는 정확도를 쥐어짜듯이 끌어내면서도 과적합(오버피팅)이 심하게 되지 않는 장점이 있습니다.

GBRT의 문제점은 학습데이터가 많아지고 자질(feature)가 많아질 수록 빌드속도가 늘어나고 한대의 컴퓨터에서 처리할 수 없는 메모리를 사용해야 하면 빌드를 하지 못합니다.

XGboost는 그 문제를 해결해놓은 것입니다.
이 문제를 해결했기 때문에 GBRT를 이용해서 대량의 학습데이터로 성능을 최대한 뽑아내는 모델을 빌드할 수 있게 되었습니다.

논문 Paper

읽어보면 좋습니다만 좀 어렵습니다.
https://arxiv.org/abs/1603.02754

유용한 정보

XGboost는 monothonic 제약을 지원합니다. 예측값이 항상 과거 보다는 미래의 값이 크거나 같아야 하는 경우를 말합니다.

사용상 문제 Issue

아직 몇 가지 문제도 있고 그렇습니다만 일반적으로 쓰는 데는 큰 문제가 없습니다.

그리고 카테고리 변수를 사용하지 못하는 큰 문제가 있습니다.

참조

다른 자료들도 참조하세요.

https://brunch.co.kr/@snobberys/137

LightGBM

LightGBM은 결정 트리(Decision Tree) 계열의 알고리즘 중에서는 현재까지의 가장 좋은 알고리즘입니다. 그렇다고 해서 이 알고리즘이 xgboost나 gbdt에 비해서 항상 성능이 좋다는 말은 아닙니다.

마이크로소프트(Microsoft)에서 만들었습니다.

GBM은 Gradient Boosting Machine의 약자이고 Light는 가볍고 빠르다는 뜻입니다.
그러면 “GBDT (GBRT)나 XGboost는 무겁운가?” 라고 묻는다면.

네. 그렇습니다. 하지만 XGBoost가 쓸모 없다는 말은 아닙니다.

lightGBM의 대략의 특징입니다.

특징

  1. 범주형 변수를 차원으로 올리는 더미 변환 또는 피폿(캐스팅)을 하지 않아도 된다.
  2. XGboost 보다 적은 데이터로 더 정확한 모델을 만들 수 있다.
  3. XGboost 보다 더 모델 빌드가 빠르다.

기존의 다른 알고리즘의 문제점과 개선한 방법

  • 느린 모델 빌드 속도
    • GBDT(Gradient Boosted Decision Tree; 또는 GBRT) 계열은 직렬 연산 알고리즘으로 병렬처리가 불가능하다.
    • GBDT는 엔트로피 계산(Information gain)을 위한 변수의 구간 탐색이 매우 빈번하다. 
    • 여러 개의 트리가 필요하지만 Gradient boosting(그래디언트 부스팅)을 하기 위해서는 병렬 처리를 할 수 없다. 어차피 트리를 병렬로 생성할 수 없기 때문에 가능하지 않다.
  • 모델 빌드 속도 개선 방법1
    • XGboost와 같이 변수에 대해서 히스토그램 색인을 만들고 학습 데이터를 병렬 분산, 중복 적재해서 연산력을 위해서 데이터 탐색 속도를 줄인다.
    • 즉 병렬처리를 하지 못하므로 직렬 처리에서 시간 소모가 가장 많은 부분에 연산자원을 과투입하고 데이터 전처리를 해둔 뒤 빌드 시간을 줄인다.
  • 모델 빌드 속도 개선 방법2
    • 학습데이터를 샘플링하는데 Gradient가 급격한 구간의 데이터들의 샘플 수를 줄이고 완만한 구간의 샘플을 늘려서 샘플의 능력 발현을 최대로 활용한다.
  • 범주형 변수 지원의 문제점
    • XGboost가 범주형 변수를 지원하지 못하기 때문에 문제가 많은데 범주형 변수를 지원하기 위해서 어쩌고저쩌고 하는 알고리즘(알고리즘이 매우 어려워서 여기서는 이름도 쓰지 않겠음)을 개발해서 적용했다.

장점

  • 매우 큰 학습데이터를 빌드하는데 시간 소모가 드라마틱하게 줄어들지만 예측 성능은 떨어지지 않는다.
  • 범주형 변수를 지원한다.
  • 사용하기 매우 편하다.
  • Microsoft가 만들었다.

단점

  • GPU로 추가 성능 개선을 할 여지가 없다.
  • Tensorflow를 비롯한 여러 프레임워크중에서 지원하지 않는 것이 많다.
  • 범주형 변수의 오토 레이블링(레벨링)을 지원하지 않는다. 범주형 변수를 모두 integer로 변환해야 한다.
  • Microsoft가 만들었다. 그래서 관리를 잘 해줄 것이라는 기대와 믿음이 있다.

Python패키지가 있으므로 주피터노트북에서 불러써도 됩니다.

Centos에 C++을 위한 build-essential 설치하기

Python 패키지 중에는 설치할 때 C++ 작성된 코어 모듈을 컴파일해서 설치하는 것들이 종종있습니다. 이런 것을 설치하려면 C++ 컴파일을 할 수 있는 컴파일러와 빌드도구를 설치해줘야합니다.

Ubuntu에서는 C++ 컴파일러를 포함해서 여러가지 개발 도구를 설치할 때 다음과 같이 합니다.

sudo apt-get install build-essential

Centos는 다음과 같이 하면 됩니다.

sudo yum groupinstall 'Development Tools'

MAB (Multi Armed Bandit) – 광고 플랫폼의 캠페인 노출 최적화

MAB는 기계학습 강화학습의 일종입니다. 완전한 강화학습은 아니지만 포함해서 취급합니다.

MAB는 “엠에이비”, “멀리암드밴딧”이라고 발음합니다.

“팔 여러 개 달린 산적” “Multi Armed Bandit”은 슬롯머신의 별명인데 이 알고리즘은 이름처럼 “어떤 슬롯 머신의 팔을 당겨야 돈을 딸 수 있는가?” 와 같은 문제를 풀기위한 방법입니다.

엄밀한 의미의 강화학습에 포함되지 않지만 상당히 간단하고 쓸만하고 강화학습의 개념을 익히기 좋기 때문에 강화학습을 설명할 때 가장 먼저 설명하는 것이기도 합니다.

여러 대의 슬롯 머신이 있고 이 슬롯 머신 중 어떤 것의 레버를 당겨야 돈을 딸 수 있는가를 푸는 문제입니다. 한 번에 1대의 슬롯머신의 레버를 당기고 계속해서 반복합니다.

이 문제의 전제 조건이 있는데 한 번에 하나의 슬롯머신의 팔을 당길 수 있다는 것입니다.

그래서 동시에 모든 슬롯머신의 팔을 당겨서 그리고 여러번 당겨서 어떤 슬롯머신이 돈을 딸 학률이 높은지 알아낼 수 없습니다.

그래서 한 번에 하나씩만 선택해서 돈을 최대한 많이 따는 것이 이 문제의 푸는 목적입니다.

복잡한 공식은 여기에 안 적겠습니다. 구글에서 찾아보시면 수식과 코드가 다 있습니다.

첫번째 방법. Greedy 욕심쟁이

모든 슬롯머신에 순차적으로 한 번씩 팔을 내려봅니다. 그래서 돈을 못땄다면 다시 한 번씩 다 팔을 내려봅니다.

몇번을 수행한 후에 딴 돈이 가장 많은 슬롯머신에게 계속해서 몰빵합니다.

이게 그리디(Greedy, 탐욕스러운) 방식입니다. 단순하면서도 조금 무식한 방법입니다.

두번째 방법. epsilon

Greedy 방법을 사용하되 무작정 사용하지 않고 랜덤으로 팔을 당길 확률을 정해놓습니다.

만약 50%의 확률로 랜덤을 고르겠다고 하면 한 번은 지금까지 가장 돈을 많이 딴 슬롯머신을 당기고 한 번은 랜덤으로 아무것이나 고르는 방법입니다.

그나마 다른 것들에게 기회를 준다는 것 때문에 낫습니다.

epsilon이라는 이름은 랜덤으로 고를 확률값을 epsilon이라고 이름을 붙여서 부르기 때문입니다.

세번째 방법. UCB(Upper-Confidence-Bound)

위의 epsilon에서 약간의 공식을 주어서 랜덤 찬스가 왔을 때 무조건 랜덤으로 어떤 슬롯머신을 팔을 다기지 않고 덜 뽑혔던 슬롯머신에 가중치를 두어서 더 뽑아서 팔을 내려줍니다.

네번째 방법. Tompson sampling

톰슨 샘플링은 설명을 하면 조금 복잡해지는데 확률 분포 중 하나인 베타분포를 이용해서 확률이 가장 높은 것을 선택하는 것입니다.

베타분포 함수에 선택된 횟수와 돈을 딴 횟수를 입력하면 베타분포를 각각 구할 수 있고 그 베타분포를 확률 분포로 이용해서 값을 구하면 선택할 것을 찾을 수 있습니다.

저장된 데이터를 이용할 수 있는 장점이 있고 UCB 보다 성능이 조금 더 좋아서 온라인 추천 시스템에서 많이 이용되고 있습니다.

A/B 테스트와 MAB의 관계

A/B 테스트는 통계학의 실험계획법 중 하나 인데 2개 또는 2개 이상의 그룹을 동일한 수(최대한 비슷한 수) 만큼 각각 분할해서 한쪽에만 다른 처리를 해서 두 그룹의 차이를 보는 방법입니다

온라인에서는 흔히 버킷테스트라고 하는 방법입니다.

예를 들어 광고배너가 있는데 원래 배너는 테두리가 하얀색인데 테두리를 빨간색으로 바꿨을 때 사람들이 어떤 것을 클릭을 더 많이 하는지 알아 보고 싶을 때 같은 경우에 합니다.

A/B 테스트가 오랫동안 사용한 방법이기 때문에 잘 알려져 있지만 문제는 두 그룹을 방해받지 않게 불한하는 방법이 상당히 어렵고 두 그룹의 차이를 알아보는 방법이 데이터의 양상과 원래 데이터의 특성에 따라 여러가지 통계적인 방법을 써야하는 데 실수로 잘못된 방법으로 확인을 했다고 하더라도 그 실수를 알아내기 어렵다는 문제가 있습니다.

A/B 테스트를 하는 것은 많이 어렵지 않지만 A/B테스트의 결과를 해석하는 것은 매우 숙련된 통계학자가 필요하고 시간도 많이 걸립니다.

그래서 A/B 테스트를 하지 않고 각각 반응을 그대로 볼 수 있는 어떤 환경이 있다면 그 환경에서는 각각 매번의 결과에 따라서 결과가 좋은 것에 점수를 더 줘서 그것을 선택하게 만드는 방법을 쓰자는 것입니다.

그래서 MAB는 온라인 시스템의 추천시스템이나 평가에 굉장히 적합합니다.

광고시스템과 MAB

한 번에 5개의 제품을 동시에 보여지는 광고 이미지가 있다고 가정합니다.

사용자 별, 또는 사용자 그룹별로 어떤 제품에 더 관심을 가지는 지를 보고 클릭을 많이 하는 제품을 MAB에 의해서 더 많이 노출한다고 하겠습니다.

흔히 쓰는 방법이지만 이게 문제가 좀 있습니다.

  • 선택할 제품이 매우 많은 경우에는 못합니다. 아마도 제품의 카테고리가 있고 그것들 중에 가장 잘 팔리는가 하는 전략을 취할 수 있지만 상식적으로 좋은 방법은 아닐 것입니다.
  • 선호도는 계절성 효과, 요일 효과, 캠페인에 피로도에 따라 달라집니다. 슬롯머신 처럼 확률이 안변한다는 가정을 두기가 좀 어렵습니다. 변동이 너무 많습니다.
  • 또 가중치를 변경하는 것 때문에 생기는 문제가 파생적으로 생기는데
    • 쿠키로 인해 신규 및 재이용자의 분포에 영향을 미칩니다.
    • 변화에 대한 적응이 느리기 때문에 인해 관성때문에 결과가 왜곡될 수 있습니다.

아주 단순한 경우에만 사용이 가능하며 복잡한 시스템으은 오히려 결과를 왜곡할 수 있습니다.

저렇게 선택한 것이 여전히 가장 좋은 방법 또는 그리 좋은 선택이 아닐 수도 있겠지만 그 자체를 확실하게 확인 못합니다. 이건 다른 알고리즘도 동일한 문제이긴 합니다만.

티스토리(Tistory) 카테고리 체계

카카오(옜날 다음)에서 운영하는 블로그 플랫폼 티스토리에는 간단한 카테고리가 체계가 있습니다.

이 카테고리 체계는 블로거별로 설정을 하게 되어 있는데 복잡하지는 않습니다.

이런 체계를 보면 콘텐트를 어떻게 관리하는지 확인하는데 도움이 됩니다.

티스토리는 카테고리가 단촐한 것 같습니다. 2단계 분류 체계를 가지고 있는데 2단계 분류 체계를 가진 것 치고는 많은 편은 아닙니다.

쓸모가 있을지는 모르겠지만 그냥 올려봅니다.

라이프, life
- 일상다반사
- 해외생활
- 연애·결혼
- 육아
- 생활정보
- 반려동물
- 취미
- 사진
- 요리
- 자동차
- 인테리어
- 패션뷰티
- 건강
- 군대
여행·맛집, travel
- 국내여행
- 해외여행
- 캠핑·등산
- 맛집
- 카페·디저트
문화·연예, culture
- TV
- 스타
- 영화
- 음악
- 책
- 만화·애니
- 공연·전시·축제
- 창작
IT, it
- IT 인터넷
- 모바일
- 게임
- 과학
- IT 제품리뷰
스포츠, sports
- 스포츠일반
- 축구
- 야구
- 농구
- 배구
- 골프
시사, current
- 정치
- 사회
- 교육
- 국제
- 경제
- 경영·직장
이벤트, event

pycurl 윈도우 버전 설치하기 – install pycurl on Windows

Python 패키지 중에는 설치할 때 C/C++ 소스를 빌드해서 설치하는 것들이 있는데 이런 패키지들은 Windows에서 설치할 때 잘 안되는 경우가 많습니다.

빌드 시스템을 다 설치해야 하고 설치하고 난 후에도 설정을 맞추기가 어렵습니다.

특히 Linux에만 지원하는 특정 패키지를 사용해서 의존성이 높은 것들은 더더욱 그렇습니다.

이런 패키지 중에 비공식적으로 Windows용을 지원하는 사이트가 있습니다.

Pycurl도 설치가 가능합니다. 자신의 Python버전과 아켜텍쳐에 맞는 것을 선택해서 Windows에 있는 pip로 설치하면 됩니다.

https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycurl