gimmesilver's blog

Agbird.egloos.com

포토로그



R을 활용한 게임 데이터 분석 #3 - 재현성과 실행 가능성 데이터분석

이 글은 회사 홍보실의 의뢰를 받아 작성한 글인데 총 3편의 시리즈로 되어 있습니다. 아래 글은 제가 원래 작성한 원본이고 회사 블로그에 가시면 홍보실의 '마사지'를 받은 글을 확인하실 수 있습니다. 

원문 링크: http://blog.ncsoft.com/?p=19695 


----------------------------------------------------------------------------------

    이번엔 R와 직접 연관이 있으면서도 좀 더 포괄적인 이야기를 다뤄보도록하겠습니다. 데이터 분석에 있어 가장 중요한 게 뭘까요? 제가 생각하기에 그것은 바로 ‘재현성(reproducibility)’ 과 ‘실행 가능성(actionability)’입니다. 

    재현성은 어떤 분석 결과에 대해 다른 사람이 같은 방법으로 분석했을 때, 같은 결과가 나올 수 있는 것을 말합니다. 데이터 분석이 소위 말하는 ‘데이터과학’이라 불릴 수 있으려면 이 재현성이 반드시 보장돼야 하죠.
    재현성을 충족 시키지 못해 문제가 된 대표적인 사례를 하나 들어볼게요. 2014년, 일반 세포를 줄기 세포로 만드는 법에 대한 논문으로 과학계에 혜성처럼 나타났던 오보카타 하루코 라는 일본 과학자를 기억하시나요? 
기존 과학계를 흔드는 엄청난 결과에, 보기 드문 여성 과학자라는 점이 작용해 언론에서 엄청난 스포트라이트를 받았죠. 하지만 다른 사람들이 논문에 나온 방법대로 실험을 해도 재현이 안 되면서 연구 결과의 진위 여부에 대한 의혹이 불거졌습니다. 결국 조작된 결과임이 들통나면서 ‘일본판 황우석’이라는 오명을 얻은 채 사람들의 관심 밖으로 사라졌죠.
 
<만능 세포사기극으로 열도를 들썩이게 만든 오보카타 하루코>

    그런데 사실 위 사례는 워낙 언론의 스포트라이트를 받은 연구였기 때문에 더 크게 회자됐던 것이고, 이 사례뿐만 아니라 학계 전반에 재현성 문제가 만연하면서 ‘재현성위기’라는 용어가 등장하기도 했습니다. 그래서 올해 3월에 미국 통계 학회(America Statistics Association)에서 올바른 데이터 분석을 위한 지침서(https://www.amstat.org/newsroom/pressreleases/P-ValueStatement.pdf)를 발표하기도 했습니다(직접적으로는 p-value의 오남용을 막기 위한 지침이지만 포괄적으로는 재현이 안 되는 잘못된 통계 분석을 방지하기 위한 지침이라볼 수 있습니다). 

    데이터 분석에서도 비슷한 사례가 있습니다. ‘피터 와든’ 이라는 데이터분석가가 페이스북에서 근무하던 당시 페이스북의 친구 관계 네트워크를 분석하던 중 이 연결 정보에 의하면 미국은 7개 지역으로 나눌 수 있다는 내용을 자신의 블로그에 실었습니다(관련링크: https://petewarden.com/2010/02/06/how-to-split-up-the-us/). 그런데 이게 많은 사람들의 흥미를 끌면서 널리 퍼졌고 급기야 뉴욕 타임즈에도 실리게 되었죠. 그러나 한참 후에 피터 와든은 사실 7개 지역으로 구분한 자료는 엄밀한 분석을 통해 나온 것이 아니라 그저 주관적인 감으로 시각화해서 만든 것이라고 고백했습니다(관련링크: https://petewarden.com/2013/07/18/why-you-should-never-trust-a-data-scientist/).즉, 이 분석은 다른 사람에 의해 얼마든지 다른 결과가 나올 수 있는 재현이 안 되는 분석이었던것이죠.

<피터 와든이 만든 페이스북 친구 관계에 의해 구분된 미국 지도>

    한편, 실행 가능성은 말 그대로 실제 활용할 수 있는 분석을 해야함을 뜻합니다. 이 실행 가능성은 재현성과도 크게 상관이 있습니다. 재현이 되지 않는 분석을 활용할 수는 없으니까요. 다만 여기서 더 나아가 재현이 될 뿐만 아니라 서비스에 활용할 수 있는 결과물을 만드는 것이 필요합니다. 아마 실제 업무를 수행하고 계신 데이터 분석가들은 다 공감하시리라 생각합니다. 얼마나 많은 노력을 들여 분석한 결과들이 한낱 단 한번의 상급자 보고를 위한 PPT로 산화하여 사라져갔는지… 
    이렇게 수많은 분석 결과들이 단지 화려한 PPT로 끝나는 이유는 여러 가지 말 못할 이유(!)도 있겠지만 실행 가능성이 고려되지 않은 분석도 책임을 면하기 힘듭니다.

    재현성과 실행 가능성. 그런데 사실 이 두 가지 요소를 만족시키는 것은 굉장히 어려운 일입니다(사실 이 주제를 본격적으로 다루기에는 제 깜냥이 안됩니다). 단지 툴을 잘 활용한다고 해서 충족시킬 수 있는 것도 아니고요. 다만 R이 갖고 있는 프로그래밍 언어적인 특성을 최대한 잘 활용하면 이런 문제를 줄이는데 어느 정도 도움이 될 것이라 생각합니다. 

    저희는 데이터 분석 시, 탐사 분석 단계에서부터 R코드와 여기서 사용한 데이터를 다른 사람이 재현할 수 있도록 코드를 정리합니다.그래서 누구나 담당자가 공유한 R코드와 데이터를 내려 받아서 첫 줄부터 차례로 실행하면서 분석 보고서의 결과와 과정을 그대로 재현할 수 있죠. 일반적인 GUI기반의 분석 도구는 비록 사용은 편리하지만 전체 분석 절차를 체계적으로 정리하기가 어렵습니다. 그러나 R은 모든 것이 코드로 표현되기 때문에 이렇게 분석 결과물 자체가 전체 분석 과정을 담게 되는 것이죠.
    물론 이것은 엄밀한 의미의 재현성과는 약간 다릅니다. 그러나 적어도 이런 식의 정리를 통해 다른 사람들이 분석가의 작업을 동일한 절차대로 재현할 수 있게 하면 해당 작업에 대해 리뷰하며 오류를 검증해 볼 수도 있고더 나아가 기존 분석 작업을 변형해서 새로운 결과를 얻을 수도 있기 때문에 유용합니다.
<재현 가능한 결과물의 나쁜 예>

    또한 2편에서 설명했듯이 R은 예측 결과를 그대로 서비스에 연동하기에 편리한 언어이므로 실행 가능성 측면에서도 매우 유리합니다(오해가있을까 싶어 첨언하자면, 실행 가능성이 단지 구현 상의 문제만은 아닙니다). 심지어 서비스 서버에서 직접 실행하는 모듈 형태로 서비스할 수 있을 뿐만 아니라, R에서 만든 예측 모델을 다른 서비스나 개발 환경에서 사용할 수 있도록 PMML(PredictiveModel Markup Language) 이라는 형식의 데이터로 제공하는 것도 가능하죠. 

    지금까지 요즘 대세인 R의 효용성과, 엔씨소프트 데이터분석팀에서 R을 활용해 어떻게 데이터를 분석하고있는지에 대해 다소 장황하게(?) 설명을 드렸습니다. 
    의도치 않게 글이 길어졌는데, 결론을 아주 깔끔하게 요약해드리자면‘R은 데이터 분석뿐 아니라 그 결과를 활용해 서비스에 적용하는 데에도 아주 훌륭한 도구이다.’라고 할 수 있겠습니다. 

    다시 한번 말씀 드리자면 R은 보기보다 어렵지 않답니다(글이 어려웠다면 그것은 전적으로 글쓴이의 잘못!). 데이터 분석이나 머신 러닝에 관심 있는 분들이라면, 망설이지 말고! 두려움없이! 기초적인 것부터 도전해 보시길 권합니다. (파이팅!!) 

베이지안 A/B 테스트 데이터분석

1. 서론
  최근에 데이터를 활용한 의사 결정이나 서비스가 확산되면서, 주로 웹서비스에서 사용되던 A/B 테스트가 여러 분야로 활용 범위를 넓히고 있다. A/B 테스트 결과를 분석할 때 가장 널리 사용하는 방법은 귀무 가설을 이용한 통계 유의성 검정 기법이다. 피셔에 의해 개발된 이 기법은 가장 널리 사용되는 검정 기법이지만 동시에 여러 가지 문제점이 있어서 최근에는 그 대안으로 베이지안 통계 기법을 사용하는 움직임이 점점 커지고 있다.
  그런데 통계 유의성 검정 방법에 대해서는 정리된 글도 많고 실제 구현된 함수나 코드가 많이 있는 반면 베이지안 통계 기법에 대한 자료는 상대적으로 빈약하며 특히 A/B 테스트에서 베이지안 기법을 이용하는 방법에 대해 잘 구현된 코드나 정리된 결과물을 찾기가 힘들다. 그나마 [1]이나 [2]와 같은 자료가 비교적 잘 설명되어 있긴 하지만 수식 유도 위주로 설명이 되어 있어 이해하기가 어렵고 특정 케이스에 대해서만 구현 코드가 나와 있어서 실전에 바로 사용하기 적절하지 않다.
  그래서 이 글에서는 우선 A/B 테스트 및 통계 유의성 검정 기법에 대해 간략히 소개를 한 후 A/B 테스트에서 베이지안 통계 기법을 이용해서 결과를 확인하는 방법에 대해 구현 방법 위주로 정리하였다.

2. A/B 테스트란 무엇인가
  A/B 테스트는 유저들을 임의로 분류하여 각 그룹에게 서로 다른 상황을 제시하고 그 반응을 확인하는 실험 기법을 말한다. 주로 웹 서비스에서 많이 사용하던 방법인데, 예를 들어 웹 페이지 디자인 시안이 두 개가 있을 때 어떤 유저들에게는 A 시안의 화면을 보여주고 또다른 유저들에게는 B 시안의 화면을 보여준 후 어떤 화면에 더 잘 반응하는지를 측정하는 것이다. 오바마 대통령의 경우 선거 기간 동안 광고 및 홈페이지 디자인에 A/B 테스트를 적극 도입하여 선거 자금 모금 및 지지율 향상에 효과를 봤다고도 알려져 있다.

<그림 1> A/B 테스트의 예

<그림 2> 오바마 대통령 선거 홈페이지의 A/B 테스트 예

  A/B 테스트는 위에서 예로 든 웹 페이지 디자인 뿐만 아니라 다양한 분야에서 활용할 수 있다. 가령 구글이나 페이스북, 넷플릭스 등에서는 자신들의 추천 알고리즘을 개선할 때 개선된 버전이 기존 모델에 비해 실제 얼마나 효과가 좋은지를 평가하기 위해 A/B 테스트를 사용한다.

  최근에는 모바일 게임이나 앱에서도 A/B 테스트를 도입하는 추세이다. 특히 모바일 게임의 경우 정식 출시 전에 일명 소프트론치(soft launch, 기존 PC용 온라인 게임의 CBT와 비슷한 개념으로 특정 지역이나 국가에만 제한적으로 게임을 출시하여 반응을 살펴보는 서비스)라고 부르는 기간을 두고 이 기간동안 제한된 유저에게 게임을 서비스하면서 다양한 A/B 테스트를 통해 게임 밸런스 조절이나 BM 상품 기획에 활용한다. 기존에는 기획자나 디렉터의 감을 믿고 여러 가지 사안들을 결정했다면, 이제는 A/B 테스트를 통해 데이터에 기반한 의사 결정을 하는 것이다(A/B 테스트에 대해 좀 더 자세히 알고 싶은 분은 [3]을 참고하기 바란다).

  한편 테스트를 통해 측정하는 결과값은 크게 두 종류로 나눌 수 있는데, 하나는 흔히 '전환율(conversion rate)'라고 부르는 비율값이고 또 하나는 유저별로 측정된 '정량값'이다. 예를 들어 어떤 구매 아이템에 대해 A/B 테스트를 하기 위해 전체 유저를 A, B 두 그룹으로 나누고 서로 다른 형태의 아이템 패키지를 제시하는 경우를 생각해 보자. 그러면 이 테스트를 통해 우리는 '구매율'과 '구매 금액' 이라는 두 가지 종류의 데이터를 얻을 수 있다. 구매율이 앞서 설명한 '비율값'이고 구매 금액이 '정량값'에 해당한다.
  A/B 테스트를 할 때는 어떤 목표를 세우게 되고 그 목표를 좀 더 잘 충족하는 그룹을 선택하는 것이 이 테스트의 목적이다. 앞의 예로 다시 돌아가 보면, 구매율을 높이고 싶은지 혹은 구매 금액을 높이고 싶은지를 테스트 전에 결정한 후 테스트 기간 동안 목표로 하는 값을 측정하게 된다. 그리고 테스트 결과를 평가할 때 이렇게 측정한 데이터의 종류에 따라 다른 통계 기법을 사용한다.

3. 테스트 결과 평가하기 - 귀무 가설에 의한 통계 유의성 검정 방법
  A/B 테스트를 수행하고 나면 어떤 그룹이 더 좋은지를 평가해야 한다. 얼핏 생각하기에는 단순히 결과 데이터를 비교하면 될 것 같다. 예를 들어 측정 대상이 전환율이라면 두 그룹의 전환율을 측정한 후 어느 그룹의 전환율이 더 높은지 비교하는 것이다. 만약 테스트 대상이 충분히 많고 두 그룹의 전환율 차이가 크다면 단순히 두 값을 비교하더라도 큰 문제는 없다. 하지만 두 그룹 간의 테스트 대상수가 차이가 나거나 전환율의 차이가 미미하다면 확률적으로 우연히 발생한 차이를 실제 어느 한 그룹이 더 효과가 좋은 것으로 오해할 수 있다.
  가령 아무런 조작이나 문제가 없다면 동전을 던졌을 때 앞면과 뒷면이 나올 확률은 일반적으로 둘 다 50% 일 것이다. 하지만 실제 동전을 10번 정도 던져보면 정확히 앞면과 뒷면이 5번씩 나오는 경우보다는 어느 한쪽 면이 1~2번 더 많이 나오는 경우가 많다. 마찬가지로 위 예에 나온 두 그룹이 정말로 전환율에 차이가 있는 것인지 아니면 이런 우연한 오차로 인해 차이가 발생한 것인지 의심이 들 수 있다. 심지어 테스트 결과가 기존에 이미 적용된 사항을 수정 반영해야 하는 방향으로 나왔다면 전환에 따른 비용을 생각해 볼 때 좀 더 확실한 결정 기준이 필요할 것이다.

  이런 문제를 해결하기 위해 널리 사용하는 방법이 귀무 가설을 이용한 통계 유의성 분석(Null Hypothesis Significance Testing)이다. 이것은 둘 혹은 그 이상의 그룹에 대해서 각 그룹의 분포가 같다는 가설(귀무가설)을 세우고 이 가설이 얼마나 맞는지를 측정하는 방식이다. 다시 말하면, 두 그룹이 실제로는 차이가 없는데 관측된 값처럼 차이가 발생할 확률을 계산한다(이것을 'p-value'라고 부른다). 만약 p-value가 매우 낮을 경우 귀무가설을 기각함으로써 두 그룹은 통계적으로도 차이가 있다고 판단한다.
  예를 들어 아래와 같은 경우를 살펴 보자.

테스트 1)
테스트 그룹 실험 참가횟수 이벤트 전환횟수 전환율(%)
A 10000 3300 33
B 500 170 34

테스트 2)
테스트 그룹 실험 참가횟수 이벤트 전환횟수 전환율(%)
A 10000 3300 33
B 500000 160000 32

  단순히 전환율만 비교하면 테스트 1)에서는 B그룹, 테스트 2)에서는 A그룹이 더 높은 전환율을 가지고 있으므로 해당 그룹에서 사용한 방법을 실전 서비스에 적용하면 된다. 그러나 실제 두 테스트에 대해서 통계 유의성 분석을 해보면 아래와 같은 결과가 나온다.

<그림 3> 위 두 예제에 대한 통계 유의성 검정 결과

  전통적으로 귀무 가설을 이용한 통계 유의성 검정에서는 p-value가 0.05(혹은 좀 더 보수적으로는 0.01)보다 작아야 통계적으로 유의미하다고 판단한다. 그런데 위 스크린샷을 보면 테스트 2)는 0.03으로 다소 낮은 값이 나온 반면 테스트 1)의 경우 0.05보다 훨씬 높은 값이 나왔다. 이것은 쉽게 말해 두 그룹의 차이가 없을 가능성이 높다는 뜻이다. 따라서 이런 경우에는 비록 어떤 한 그룹이 전환율이 더 높게 측정되더라도 실험 결과를 수용하지 않는 것이 좋다.

4. 통계 유의성 검정 방법의 문제점
  그런데 이렇게 귀무 가설에 의한 통계 유의성 검정 방법은 여러 가지 문제점을 갖고 있다. 이에 대해서는 이미 많은 통계 학자나 데이터 분석가들이 잘 정리해 놓은 글이 있기 때문에 다시 언급하는 것이 불필요해 보이지만 간략하게 언급하자면, 가장 큰 문제는 분석 결과가 이해하기 힘들고 직관적이지 못하다는 점이다.
  실제 사용자가 A/B 테스트를 통해 알고 싶은 것은 '어떤 그룹이 더 좋은가?' 인데 통계 유의성 검정이 알려 주는 것은 '두 그룹의 분포가 같을 것이라는 귀무 가설을 기각해도 될까?' 이다. 한마디로 말해 (비록 관련성이 있긴 하지만) 원래 문제와 다른 문제에 대한 답을 찾는 것이다. 이는 직접적인 답을 찾기 힘든 상황에서 어쩔 수 없이 통계학자들이 택한 일종의 편법이다. 따라서 해당 내용을 잘 알고 있는 통계 전문가라면 상관없지만 그렇지 못한 사람들에게는 많은 오해를 줄 수 있다. 특히 흔히 하는 오해가 p-value의 의미를 마치 '이 실험 결과가 틀릴 확률'이라고 생각하는 것이다(물론 아니다). 따라서 많은 통계 전문가들이 이러한 p-value에 대한 오해의 심각성을 지적하고 있으며, 이 외에도 몇 가지 문제 때문에 극단적으로는 더 이상 p-value를 사용하지 말자는 주장도 나오고 있다(이와 관련해서는 [4]에 잘 정리되어 있다).

5. 베이지안 확률 이용하기
  이렇듯 기존에 사용하던 통계 유의성 검정 방법의 문제점 때문에 최근에는 베이지안 확률을 이용한 A/B 테스트 평가 방법이 널리 퍼지고 있다. 이것은 베이지안 이론을 이용해서 어떤 그룹이 다른 그룹보다 실험 목표가 되는 측정치(구매율이나 구매금액)가 더 높게 나올 확률을 직접적으로 구하는 방식이다. 따라서 그 결과가 직관적이라는 장점이 있다.

5.1. 기본 개념
  통계학에서 확률을 계산하는 방법은 크게 두 가지가 있다. 하나는 모든 가능한 경우의 수(혹은 관찰된 전체 사건의 수)에서 원하는 경우의 수(혹은 확률을 계산하고자 하는 사건의 수)가 차지하는 비율이다. 동전의 앞면이 나올 확률이나 주사위에서 눈금 3이 나올 확률 등을 계산할 때 보통 이 방식을 이용해 계산한다. 이것을 '빈도주의 확률'이라고 부른다. 또 다른 방법은 베이즈 이론을 이용해 계산하는 확률이다. 베이즈 이론은 어떤 확률을 계산할 때 '사전 확률'과 '조건부 확률'을 이용해서 계산하는 방법인데 공식은 아래와 같다.
  빈도주의 확률에 비해 수식도 복잡할 뿐더러 '사전 확률'이나 '조건부 확률' 같은 개념은 다소 생소한 개념이기 때문에 베이지안 확률을 직관적으로 이해하기는 어렵다. 그럼에도 불구하고 베이지안 이론이 널리 사용되는 이유는 (다소 어거지로 느껴질 수는 있겠지만) 어떤 경우에도 확률을 계산할 수 있기 때문이다.

  가령 앞서 예로 든 동전 던지기를 생각해 보자. 내가 현재 어떤 동전을 갖고 있는데 이 동전을 던졌을 때 앞면이 나올 확률을 계산하고 싶다. 빈도주의 확률을 계산하려면 동전을 여러 차례 던진 후 앞면이 나온 횟수를 세보면 된다. 그런데 오차가 있을 수 있기 때문에 실제 확률이 50%라고 하더라도 앞면이 정확하게 시도 횟수의 절반 만큼 나오리라는 보장은 없다. 그럼 몇 번을 던져야 할까? 10번? 100번? 1000번? 설령 1000번을 던지더라도 앞면이 정확히 500번 나오지는 않을 것이다. 다만 10번을 던졌을 때 보다는 근사적으로 절반에 가까운 확률이 나올 것이다. 반대로 얘기해서 시행 횟수가 매우 적다면 정확한 확률을 구하는 것이 불가능하다. 따라서 빈도주의에서는 시행 횟수가 충분히 많아야 정확한 확률을 구할 수 있다. 그래서 빈도주의 통계기법에서는 시행 횟수에 따라 현재 구한 확률이 얼마나 실제 확률에 가까운지를 추정하는 신뢰 구간이나 p-value 같은 수치를 따로 측정해서 제시한다(2장에서 소개한 통계 유의성 분석이 바로 이 빈도주의 확률을 이용한 방법이다).

  반면 베이지안 확률에서는 매우 적은 시도만으로도 확률을 비교적 정확히 계산할 수 있다. 단, 이를 위해선 사전 확률을 잘 정해야 한다. 물론 우리는 정확한 사전 확률을 모르기 때문에 이건 말그대로 이상적인 조건이다. 그럼에도 불구하고 이 방법이 쓸만한 이유는 실전에서는 많은 시도는 할 수 없지만 사전 확률은 대략적으로 추정할 수 있는 상황이 그 반대의 상황인 아무런 배경 지식이 없이 많은 시도를 할 수 있는 상황에 비해 더 많기 때문이다.
  위 동전 던지기 역시 마찬가지이다. 우리는 동전을 던졌을 때 앞면이 나올 확률이 대개의 경우 50% 라는 것을 이미 알고 있다. 따라서 내가 지금 실험하려는 동전에 대해서 굳이 100번을 던질 필요없이 몇 번만 던져보면 이 동전의 앞면이 나올 확률을 비교적 정확하게 계산할 수 있다.
  심지어 빈도주의 확률로는 계산할 수 없는 확률까지도 계산할 수 있다. A/B 테스트에서 구하고자 하는 확률도 바로 이런 예 중 하나이다(그래서 빈도주의에서는 직접적으로 확률을 계산하는 대신 유의성 검정이라는 기법을 사용하는 것이다).

5.2. 베이지안 확률 계산 방법 - 전환율
  먼저 전환율에 대해서 A/B 테스트의 실험 그룹들에 대한 베이지안 확률을 계산하는 방법을 살펴 보겠다. 2장에서 예로 든 테스트 1) 을 다시 생각해 보자.

테스트 1)
테스트 그룹 실험 참가횟수 이벤트 전환횟수 전환율(%)
A 10000 3300 33
B 500 170 34

  여기서 구하려는 값은 각 그룹별로 '어떤 그룹의 전환율이 다른 그룹보다 높을 확률'이다. 즉, 'A그룹의 전환율이 B그룹보다 높을 확률'과 'B그룹의 전환율이 A그룹보다 높을 확률'이다. 이것을 각각 P(A>B), P(B>A)라고 하겠다. 빈도주의 확률에서는 이것을 구할 수 없다. 하지만 베이지안 확률에서는 아래와 같은 공식을 통해 확률을 추정할 수 있다.


  위 식에서 P(a|A) 와 P(b|B) 는 각각 'A그룹과 B그룹의 테스트 결과를 관찰한 상태에서 전환율 a, b가 발생할 조건부 확률'을 의미한다. 이것은 R에서 dbeta 함수와 pbeta 함수를 이용해서 아래와 같이 구할 수 있다.

prob.a.better.b <- integrate(function(x) {
dbeta(x, ca+1, ta-ca+1) * pbeta(x, cb+1, tb-cb+1)
}, 0, 1)

prob.b.better.a <- integrate(function(x) {
dbeta(x, cb+1, tb-cb+1) * pbeta(x, ca+1, ta-ca+1)
}, 0, 1)

  여기서 ta와 ca는 각각 A그룹의 실험 참가횟수와 이벤트 전환횟수를, tb와 cb는 B그룹의 실험 참가횟수와 이벤트 전환횟수를 의미한다. 그리고 integrate 는 적분함수인데 첫번째 인자로 주어진 함수에 대해서 두번째와 세번째 인자 값의 범위 내에서 적분을 계산한다. 만약 테스트 그룹이 A와 B외에 더 있다면 추가되는 그룹에 대해서 pbeta 함수를 추가로 곱해주면 된다. 위 방법을 이용하여 최종적으로 전환율에 대한 각 그룹별 베이지안 확률을 계산하는 R 코드는 아래와 같다.

bayesian.stat.for.rate <-function (data) {
k <- nrow(data)
result <- numeric(k)
for (i in (1:k)) {
idx <- (1:k)[-i]
f <- function(z) {
r <- dbeta(z,
data[i, 'conversion'] + 1,
data[i, 'trial'] - data[i, 'conversion'] + 1)
for (j in idx) {
r <- r * pbeta(z,
   data[j, 'conversion'] + 1,
   data[j, 'trial'] - data[j, 'conversion'] + 1)
}
return(r)
}
result[i] = integrate(f, 0, 1)$value
}
return(result)
}

  위 코드를 이용해서 테스트 1)과 2)에 대해서 각 그룹별 확률을 계산하면 아래와 같이 나온다.

테스트 1)
테스트 그룹 실험 참가횟수 이벤트 전환횟수 전환율(%) 베이지안확률(%)
A 10000 3300 33 31
B 500 170 34 68

테스트 2)
테스트 그룹 실험 참가횟수 이벤트 전환횟수 전환율(%) 베이지안확률(%)
A 10000 3300 33 98
B 500000 160000 32 2

  테스트 1)에서는 비록 B가 더 전환율이 높긴 하지만 정말로 A그룹보다 전환율이 높을 확률이 68%에 불과한 반면 테스트 2)의 경우에는 A그룹의 전환율이 B보다 더 높을 확률이 98%나 된다. 한 가지 눈여겨 봐야할 점은 결과적으로 (정확하게 해석만 한다면) 통계 유의성 검정에 의한 판단 결과와 동일한 결론을 내리게 된다는 점이다. 실제 빈도주의 확률과 베이지안 확률은 적절한 조건(예를 들면, 충분한 시행 횟수)만 주어지면 동일한 결과가 나온다. 다만 앞서 언급했듯이 베이지안 확률은 p-value를 이용한 유의성 검정에 비해 좀 더 직관적이며 오용할 가능성이 더 낮다.

5.3. 베이지안 확률 계산 방법 - 정량값
  정량값에 대한 확률 계산도 기본적인 개념은 5.2의 전환율 공식과 거의 유사하다. 한가지 차이가 있다면 5.2에서는 비율이기 때문에 이항 분포에 기반하여 확률을 계산했다는 점과 적분을 할 때 0~1까지의 범위로 계산을 했다는 점이다.
  정량값의 경우 해당 테스트 데이터의 확률 분포에 기반한 계산이 필요하다. 여기서는 단순함을 위해 측정하는 데이터가 정규분포라고 가정하겠다. 정규분포로 가정할 경우 위에서 사용한 dbeta 와 pbeta 함수 대신 dnorm 과 pnorm 함수를 사용하면 된다. 그리고 이에 맞게 입력 데이터는 시행 횟수와 전환 횟수 대신 각 그룹에서 측정한 값의 평균과 표준편차가 필요하며 마지막으로 적분을 할 때는 0 ~ 1 대신 -Inf ~ Inf 를 주어야 한다(각각 R에서 음의 무한대와 양의 무한대값을 의미한다). 이렇게 수정된 계산 코드는 아래와 같다.

bayesian.stat.for.value <-function (data) {
k <- nrow(data)
result <- numeric(k)
for (i in (1:k)) {
idx <- (1:k)[-i]
f <- function(z) {
r <- dnorm(z, data[i, 'mean'], data[i, 'std.dev'])
for (j in idx) {
r <- r * pnorm(z, data[j, 'mean'], data[j, 'std.dev'])
}
return(r)
}
result[i] = integrate(f, -Inf, Inf)$value
}
return(result)
}


5.4. R 코드 개선하기
  위 두 개의 R코드는 간단한 테스트 상황에서는 잘 동작하지만 실전에서 사용할 때 두 가지 문제가 있다. 첫째, 전환율을 계산할 때 시행 횟수와 전환 횟수가 매우 큰 경우에는 integrate 함수가 정확한 적분값을 계산하지 못한다. 이것은 정밀도 문제 때문에 생기는 현상인데 가령 아래와 같은 경우 bayesian.stat.for.rate 함수는 엉뚱한 값을 출력한다.

<그림 4> 정밀도 문제로 인한 확률 계산 오류 예

  비록 integrate 함수에는 rel.tol 과 abs.tol 이라는 파라미터가 있어서 이를 통해 정밀도를 개선할 수 있긴 하지만 한계가 있다. 따라서 실전에서 만약 수십만 명이 넘는 유저에 대해서 테스트를 수행한다면 위 코드로는 정확한 베이지안 확률을 계산하지 못한다. 이 문제를 개선하기 위한 방법 중 하나로 적분을 직접 구현하는 방법이 있다. 아래 코드는 integrate 함수 대신 적분을 직접 구현한 코드이다.

bayesian.stat.for.rate <-function (data, resolution = 0.001) {
k <- nrow(data)
result <- numeric(k)
for (i in (1:k)) {
idx <- (1:k)[-i]
f <- function(z) {
r <- dbeta(z,
data[i, 'conversion'] + 1,
data[i, 'trial'] - data[i, 'conversion'] + 1)
for (j in idx) {
r <- r * pbeta(z,
   data[j, 'conversion'] + 1,
   data[j, 'trial'] - data[j, 'conversion'] + 1)
}
return(r)
}

#result[i] = integrate(f, 0, 1)$value
xs <- seq(0, 1, resolution)
temp <- numeric(length(xs))
for (x in 1:length(xs)) {
temp[x] <- f(xs[x])
}
result[i] <- mean(temp)

}
return(result)
}

  위 코드를 이용해서 아까 오류가 있었던 데이터를 다시 계산해 보면 <그림 5>와 같이 정상적으로 확률이 계산된다. 참고로 수정된 코드에 추가된 파라미터인 resolution의 값을 더 작게할수록 정밀도가 향상되기 때문에 더 정확한 확률이 계산되지만 대신 계산하는데 걸리는 시간은 더 길어진다.

<그림 5> 정밀도 문제가 수정된 결과 화면

  또 다른 문제점은 정량값 확률을 계산하는 bayesian.stat.for.value 함수의 문제인데, 만약 확률을 계산할 그룹의 정량값의 분포가 정규분포와 크게 다르다면(예를 들어 멱함수 분포) 확률이 부정확하게 계산될 수 있다. 이 문제를 해결하려면 해당 그룹의 데이터 분포에 맞게 dnorm과 pnorm 함수 호출 부분을 수정해줘야 한다. 특히 만약 데이터의 분포가 모수적 방법으로 표현할 수 없는 분포라면 비모수적 기법(non-parametric method)을 이용해 확률 분포를 계산해 줘야 한다(이에 대해서는 차후에 따로 다루도록 하겠다).

6. 맺음말
  A/B 테스트는 데이터에 기반한 의사 결정의 가장 대표적인 사례로써 널리 사용되고 있다. 특히 최근에는 기존의 통계 유의성 검정에 기반한 테스트 방법의 문제점을 극복하기 위해 베이지안 기법을 도입하는 추세이다. 이 글에서는 베이지안 A/B 테스트의 기본 개념 및 구현 방법에 대해 개략적으로 정리하였다.
  한편, 이미 A/B 테스트를 적극적으로 도입하여 활용하던 업계 전문가를 중심으로 이것의 문제점이나 한계점을 지적하며 그 대안을 제시하는 경우가 있다. 이에 대해서는[5]와 [6]을 참고하기 바란다.

7. 참고 자료
[1] www.evanmiller.org, "Formulas for Bayesian A/B Testing,http://www.evanmiller.org/bayesian-ab-testing.html
[2] engineering.richrelevance.com, "Bayesian A/B Tests - RichRelevance Engineering Blog : RichRelevance Engineering Blog,http://engineering.richrelevance.com/bayesian-ab-tests/
[3] www.boxnwhis.kr, "A/B 테스팅이란,http://www.boxnwhis.kr/2015/01/29/a_b_testing.html
[4] www.ibric.org, "[바이오토픽] 미국 통계학회, P값의 오용(誤用)을 경고하는 성명서 발표 > BRIC," http://www.ibric.org/myboard/read.php?Board=news&id=270293


기계 학습과 코딩의 종말 #1 - 논리회로와 뉴럴넷 데이터분석

1. 서론
  얼마 전 wired 에서 'Soon we won't program computers. We'll train them like dogs'라는 글을 읽었습니다. 기존에는 모든 알고리즘을 사람이 직접 코딩했다면 앞으로는 기계가 알고리즘을 스스로 만들도록 훈련시키는 것이 기존 프로그래밍을 대체할 것이라는 내용입니다. Wired는 약 8년 전에 이미 'The end of theory'라는 글로 재미를 본 적이 있는데 이번에도 상당히 논쟁을 불러일으킬만한 글을 실었네요.
  저는 충분히 가능한 시나리오라고 생각합니다. 반대 의견들을 보면 프로그래밍은 단순한 규칙이 아니다, 먼 미래에 가능하겠지만 아직 시기상조다 등의 의견들이 있는데 바둑의 사례를 생각해 보더라도 그다지 먼 미래는 아닐 것 같습니다. 어떤 이는 알고리즘같이 명료한 로직을 어떻게 훈련을 통해 학습시키냐는 의문을 갖습니다. 여기서는 바로 이 부분에 대해 한번 살펴 보고자 합니다.

2. 논리회로
  우리가 프로그래밍이라고 부르는 것들은 어떤 단순한 명령들로 이뤄진 일련의 과정입니다. 이것을 흔히 '알고리즘'이라고 부르죠. 그런데 이런 알고리즘 역시 좀 더 미시적인 관점에서 보면 매우 단순한 논리 연산의 조합으로 표현 가능합니다. 아마 컴퓨터 관련 학과를 전공하신 분들이라면 '논리 회로', '컴퓨터 구조', '운영체제', '컴파일러' 등의 수업을 들으셨을 것입니다. 이 수업들이 포괄적으로 알려 주는 것은 '0과 1로 표현되는 단순한 전기 신호를 조합해서 어떻게 우리는 복잡한 작업을 수행할 수 있는가' 입니다.

  우선 아무리 복잡한 프로그램도 프로그래밍 언어가 제공하는 문법 내에서 모두 표현 가능합니다. 가장 복잡한 언어 중 하나인 scala의 경우 keyword 가 70여개 정도 되고, C언어는 30개가 좀 넘습니다. 이런 keyword와 그 언어가 제공하는 문법을 잘 조합해서 우리는 몇 십만 줄이 넘는 대규모 코딩을 통해 워드도 만들고 엑셀도 만들고 웹 서비스도 제공하고 하는 것이죠. 그리고 이런 프로그램 소스는 다시 좀 더 단순한 문법으로 된 어셈블리어나 기계어로 변환됩니다. 이 단계에서는 아무리 복잡한 알고리즘이나 웹 서비스도 '저장하기', '불러오기', '비교하기', '건너뛰기' 정도의 단순 동작의 조합으로 치환됩니다.
  이런 저수준 로직은 다시 더 단순한 구조로 표현될 수 있습니다. 우리가 흔히 '비트 연산' 혹은 '논리 연산' 이라고 부르는 AND, OR, XOR, NOT 등이 그것입니다. 가령 두 개의 이진수를 비교하는 연산을 논리회로로 표현하면 아래와 같습니다.

<그림1> 1bit 비교연산기의 논리회로

  위 회로에서 A와 B에 0 혹은 1 값을 넣으면 출력으로 A가 B보다 큰지, 작은지 혹은 같은지에 따라 세 개의 출력값 중 하나만 1이 되고 나머지는 0이 출력됩니다. 이와 비슷하게 어셈블리 수준에서 표현되는 명령어들은 논리 연산의 조합으로 모두 표현할 수 있습니다. 실제 디지털 회로들은 이런 논리 연산의 조합을 구현한 하드웨어이며 여기에 입력으로 여러 개의 0와 1의 조합을 주면 원하는 결과가 출력되는 것이죠.

  결국 모든 알고리즘은 위와 같은 아주 단순한 논리 연산을 이리 저리 조합하여 표현할 수 있습니다. 혹시 FPGA(Field Programmable Gate Array)를 해보신 분은 쉽게 납득하실 수 있을 것 같네요. FPGA를 이용하면 엄청나게 많은 수의 NAND(혹은 NOR) 소자의 조합만으로 복잡한 계산기나 임베디드 로직을 구성하여 하나의 칩으로 만들 수 있습니다.

<그림 2> FPGA는 왼쪽 그림처럼 모든 연결 조합이 가능하게 구성된 논리 연산자들을 이용해서,
 오른쪽 그림처럼 원하는 형태의 논리 회로를 구성할 수 있는 반도체 소자입니다.

  물론 현실에서는 대개의 경우 그 조합의 규모와 복잡도가 매우 크기 때문에 중간 중간 적절한 추상화와 계층화를 통해 구현됩니다. 우리가 접하는 하드웨어나 소프트웨어는 ALU나 레지스터, 제어 유닛 등으로 추상화 되거나 OS의 커널, API, 각종 라이브러리 등으로 계층화되기 때문에 우리는 실제 엄청나게 복잡한 로직을 좀 더 간결한 추상 구조로 이해하고 구현할 수 있는 것이죠.
  그런데 이런 추상화가 매우 잘되어 있기 때문에 사람들은 이런 착각을 합니다. '알고리즘과 같이 복잡하고 심오한 개념은 기계가 이해할 수 없을 거야!' 하지만 실상 프로그램이나 알고리즘은 기계가 이해할 수 있을만큼 충분히 단순한 로직만을 활용하여 인간이 도저히 수용하기 힘든 규모의 복잡한 조합을 통해 구현되어 있습니다.

3. Neural network
  위에서 설명한 FPGA나 소프트웨어 프로그램은 사람이 디자인하고 구현한 결과물입니다. 디지털 회로를 설계하는 것도 사람이고 프로그래밍을 하는 것도 사람이죠. 그런데 wired의 기사에서는 이걸 기계가 스스로 구현할 수 있을 것이라고 주장하고 있습니다. 단지 사람은 기계가 로직을 구현할 수 있게끔 '훈련'을 시키면 된다는 것이죠. 다소 터무니없는 주장이라 치부할 수 있는데 이게 이론적으로 가능할 것이라 사람들이 생각하게 된 것은 바로 'deep learning' 때문입니다. 실상 deep learning은 neural network의 advanced 버전이기 때문에 여기서는 앞으로 용어를 neural network 라고 하겠습니다.
  neural network는 일종의 non-linear regression function 입니다. 아주 기본적인 형태는 logistic regression과 똑같이 생겼습니다. 이를 테면 이렇게 생겼죠.

g(x) = 1 / (1 + e^(-x))
f(x) = g(a0 + a1*x1 + a2*x2 + a3*x3 + ... + an *xn)
= 1 / (1 + e^-(a0 + a1*x1 + a2*x2 + a3*x3 + ... + an*xn))

  제가 '이를 테면' 이라는 표현을 사용한 이유는 모든 neural network이 저렇게 생기진 않아서입니다(학습 대상이 어떠냐에 따라 적용하는 비선형 함수(위 식에서 g(x))를 다르게 지정할 수 있습니다). 암튼 위 식은 logistic regression과 똑같습니다. 그런데 neural network는 이걸 좀 더 복잡하게 확장할 수 있습니다. 이를 테면 아래처럼 확장이 가능합니다(아래 식에서 g(x)함수는 위와 동일).

f(k) = g(a0 + a1*k1 + a2*k2 + a3*k3 + ... am*km)
k1 = h1(x) = g(b0 + b1*x1 + b2*x2 + b3*x3 + ... bn*xn)
k2 = h2(x) = g(c0 + c1*x1 + c2*x3 + c3*x3 + ... cn*xn)
...
km = hm(x) = g(d0 + d1*x1 + d2*x3 + d3*x3 + ... dn*xn)

  위 식을 그림으로 표현하면 아래와 같습니다. neural network에서 전 단계의 함수 결과값들은 가중치를 곱한 후 더해서 다음 단계의 g(x) 함수의 입력값으로 들어갑니다. logistic regression은 아래 그림에서 가운데 layer가 없는 형태라고 생각하시면 됩니다.

<그림 3> neural network diagram

  neural network에서는 가운데에 있는 layer(즉, k1, k2, k3, ... km)를 hidden layer라고 부릅니다. 겉으로 드러나지 않은 숨겨진 함수들이기 때문이죠. 마치 2장에서 설명한 추상화 구조와 비슷합니다. 겉으로는 단순히 프로그램만 노출되지만 내부적으로는 라이브러리, API, 어셈블리 명령어 등이 계층적으로 추상화된 구조와 유사합니다. 게다가 neural network는 이 hidden layer를 여러 개로 구성할 수도 있습니다(실제 상용 서비스에 적용되는 neural network는 수십 개 이상의 hidden layer를 구성하는 경우도 있다고 합니다).

  대체 이걸로 무슨 일을 할 수 있을까 싶습니다만 이 구조를 이용하면 이론적으로 어떤 논리 구조도 표현이 가능합니다. 예를 들어 2장에서 소개한 AND, OR, NOT 은 각각 아래와 같이 표현할 수 있습니다.

AND(x1, x2) = g(-30 + 20*x1 + 20*x2) = 1 / (1 + e^(30 -20*x1 - 20*x2))
OR(x1, x2) = g(-10 + 20*x1 + 20*x2) = 1 / (1 + e^(10 - 20*x1 - 20*x2))
NOT(x1) = g(10 - 20*x1) = 1 / (1 + e^(-10 + 20*x1))

<그림 4> 왼쪽부터 각각 AND, OR, NOT 에 대한 neural network diagram

  x1, x2에 각각 0 혹은 1을 넣고 위 식을 계산해 보면 AND 함수는 x1과 x2가 모두 1일 때만 1에 가까운 값이 나오고 나머지 경우에는 0에 가까운 값이 나옵니다. OR함수는 x1, x2가 모두 0일 때만 0에 가까운 값이 나오고 나머지 경우에는 1에 가까운 값이 나오며, 마지막으로 NOT은 x1과 반대의 값에 가까운 값이 나오죠.
  여기까지는 별로 대단할 것이 없지만 이제 이 단순한 구조를 조합하면 좀 더 복잡한 논리 연산도 만들 수 있습니다. 예를 들어 NAND나 XOR 함수는 위에 함수를 조합하여 아래와 같이 표현 가능합니다(수식으로 표현하기는 다소 복잡하니 diagram으로만 표현했습니다).

NAND(x1, x2) = NOT(AND(x1, x2))
XOR(x1, x2) = NOT(OR(AND(x1, x2), AND(NOT(x1), NOT(x2))))

<그림 5> NAND(왼쪽)와 XOR(오른쪽)에 대한 neural network diagram

  이렇듯 모든 논리 연산은 neural network 로 표현할 수 있습니다. neural network의 강력한 점은 바로 이렇게 여러 개의 hidden layer를 통해 어떤 복잡한 논리 연산도 모두 표현 가능하다는 점입니다(마치 FPGA처럼요). 그리고 이것을 통해 다음과 같은 추론이 가능합니다. '모든 프로그램 로직은 neural network로 표현이 가능하다.'

<그림 6> 오른쪽 그림처럼 neural network는 많은 layer와 unit을 이용하면 복잡한 로직도 표현 가능합니다. 
(왼쪽의 FPGA 구조와 왠지 비슷해 보이지 않나요?)

  그런데 위 수식에서 각 입력값의 가중치(diagram에서 화살표에 적혀있는 숫자들)는 어떻게 정해야 할까요? 바로 이 가중치를 적절하게 찾는 과정이 기계 학습에서 말하는 '훈련(training)'입니다. 서론에서 언급한 wired의 글에서는 이제 프로그래밍은 이 훈련으로 대체될 것이라고 얘기했죠. 만약 훈련을 통해 내가 원하는 로직이 되도록 가중치를 자동으로 찾을 수 있다면 기계 학습을 통해 자동으로 프로그래밍을 하는 것이 가능해 보입니다. 그렇다면 이 훈련은 어떻게 해야 할까요? 이 훈련 과정에 대해서는 다음 글에서 다루겠습니다.

R을 활용한 게임 데이터 분석 #2 - 데이터 분석 과정 데이터분석

이 글은 회사 홍보실의 의뢰를 받아 작성한 글인데 총 3편의 시리즈로 되어 있습니다. 아래 글은 제가 원래 작성한 원본이고 회사 블로그에 가시면 홍보실의 '마사지'를 받은 글을 확인하실 수 있습니다. 

원문 링크: http://blog.ncsoft.com/?p=18870 


----------------------------------------------------------------------

    엔씨소프트 데이터분석팀에서 진행하는 분석 프로젝트는 크게 1) 탐사분석 2) 예측 모델링 3) 시스템 혹은 서비스 개발 단계로 나눌 수 있습니다.
    1단계인 탐사 분석은 말 그대로 초기 분석 방향조차 정해지지 않은 단계에서 다양한 탐사를 위해, 혹은 예측 분석에 앞서 예측 대상이 갖고 있는 패턴이나 특징을 찾는것이죠. 최근에 수행한 ‘진성 유저 지표 프로젝트’를 예로 들면, 진성 유저가 무엇인지 구체적인 기준을 정하기 위해현재 유저들의 패턴이나 활동 현황을 탐사하는 과정이 이에 해당합니다. 각 유저들의 플레이 시간과 레벨, 주 활동 지역 등의 분포를 보거나좀 더 복잡하게는 직업별, 레벨별 잔존율이나 ARPU(*유저1명이 특정 기간 동안 지출한 평균 금액)를 비교하거나 반대로 ARPU별 레벨 분포나 주 활동 지역, 보유 아이템 등을 확인하는 등등이 모두 탐사 분석이죠. 
    과거에는 OLAP라는 시스템을 이용해서 탐사 분석을 했습니다. OLAP는 미리 다양한 분석 상황을 큐브 형태로 구축한 후 분석하는 시스템을 말합니다. 따라서 이 큐브에 들어갈 데이터마트(datamart)가 굉장히 잘 구축된 상황에서는 굉장히 효율적으로작업을 할 수 있죠. 
<OLAP은 뭐 이런 겁니다…쿨럭>

    제조업이나 금융업처럼 도메인의 특성상 데이터 종류의 변화가 크지 않은 분야나, 비즈니스 변화가 빠르지 않았던 과거에는 이렇게 OLAP을 이용한 방법이 효과적이었죠. 그러나 요즘처럼 변화가 휙휙 이루어지는 시대, 그리고 특히 게임처럼 몇 개월 단위로 대규모 콘텐츠 업데이트가 이뤄지는 분야에서는 탐사 분석 전에 모든 가능한 데이터를 OLAP로 구성해서 활용하는 건 현실적으로 불가능하죠. 아마 OLAP을구축해본 DBA분들이라면 dimension이 추가될 때마다 OLAP을 재구성하느라 밤을 새본 경험이 한 두 번씩은 있을 것 같네요…
    또 애로사항 중 하나는 데이터를 분석하는 사람과 데이터를 추출하는 사람이 분리돼 있는 경우가 많다는 거죠. 이럴 땐 아래와 같은 상황을 종종 볼 수 있습니다.

분석 담당자가 분석을 하다가 다른 데이터가 추가로 필요해서 추출 담당자에게 데이터를 추가로 요청 -> 추출 담당자가 전달한 데이터를 분석가가 확인 후 ‘어, 내가 요청한 거랑 데이터가 다르네? 다시 뽑아줘요’ -> 추출 담당자는 ‘님아,요청을 애초에 제대로 했어야죠’  -> 그럼 분석가는 ‘아니당신이 제대로 이해 못한 거잖아’ … (무한반복)

    위의 과정이 몇 번이고 반복되며 시간만 속절없이 흐르고…그럼 결국 ‘아놔 저 사람이랑 짜증나서 일 못 하겠네!’하며 ‘술이나 마시러 가자!’ 뭐 이렇게 되는 거죠. 
<분석가와 DBA도 역시 마찬가지…>

    이런 사태를 미연에 방지하기 위해, 데이터분석팀에서는 Hive라고 하는 시스템을 이용해 분석가들이 데이터를 직접 추출하고 R에서 다양한 탐사 분석을 수행합니다. 처음엔 데이터 추출까지 해야 하니 일이 늘어난 것처럼 느껴질지 모르지만, 조금만 익숙해지면 오히려 불필요한 시행 착오를 줄일 수 있어 효과적일 뿐만 아니라 데이터에 대한 신뢰도가 높아져 정확한 분석이 가능합니다. 더 나아가 리눅스 명령어 라인 툴인 Bash shell도 활용하고 있죠. 분석가들이 좀 더 효율적으로 일하려면 서버에서 기본적인 데이터 처리 및 자동화를 직접 할 수 있어야 하기 때문입니다. 비록 최근에 다양하고 세련된 여러 가지 분석 솔루션들이 나오고 있지만 제 생각에는 효율성이나 유연성 측면에서 볼 때 아직까지는 Hive + Bash shell + R 조합을 능가하지는 못하는 것 같습니다. (*물론 익숙하다는 전제 하예요.) 
 <데이터 분석궁극의 조합 - hive, bash, R>

    또한 효과적인 탐사 분석을 위해서는 적절한 시각화가 매우 중요합니다. 앞서 언급했듯, R은 이런 데이터 시각화를 통한 탐사 분석에 아주 강력한 장점을 갖고 있어서 이 단계에서 가장 중요한 역할을 수행하죠.  
<R을 이용한 여러가지 데이터 시각화 예>

    탐사 분석을 통해 예측 대상 및 패턴이 어느 정도 파악되면 이제 예측 모델링을 진행합니다. 보통 데이터 마이닝 혹은 기계 학습이라고 하는 작업들이 여기에 해당하죠. 
    진성 유저 지표 프로젝트의 경우, 유저들을 유형별로 분류하기 위한 분류 모델을 만드는 작업입니다. R에는 다양한 데이터 마이닝 알고리즘이 있고, 사용법도 어렵지 않죠. 그래서 데이터분석팀에서는 예측 모델을 생성하고그 성능을 측정하는 작업을 모두 R에서 수행합니다. 
<기계 학습 결과는 여러 가지 방식으로 성능을 측정합니다>

    마지막으로 전 단계에서 만든 예측 모델을 실제 서비스에 적용할 수 있도록 서비스 개발에 돌입합니다. 진성 유저 지표 서비스를 예로 들자면, 먼저 유저들의 플레이 데이터를 예측 모델에서 사용할 수 있는 형태로 정제하는 데이터 전처리 모듈, 이 데이터로 각 유저들이 어떤 유형인지를 기계 학습을 통해 자동으로 분류하는 모듈, 분류 결과를 서비스하는 모듈을 만들게 되는데 이중 기계 학습모듈을 R로 개발합니다.                                          
<진성 유저분류 과정을 (아주아주아주아주)간략하게 그림으로 표현하면 이렇습니다.>

    R은 통계 분석용 도구이기도 하지만, 동시에 프로그래밍 언어이기 때문에 이렇게 시스템 모듈을 개발하는 것도 가능하죠. 특히 탐사 분석이나 예측 분석에서 사용한 코드를 상당 부분 재활용할 수 있는데, 이 점은 실전에서 큰 장점이 됩니다. 왜냐하면 분석가가 만든 코드를 개발자가 다시 포팅(*기존의 코드를다른 컴퓨팅 환경에서 동작할 수 있도록 수정하는 것)할 필요가 없기 때문이죠. 포팅 과정에서 잘못 구현하거나 버그가 발생할 가능성이 크게 낮아지는 것은 물론이고요. 
    이렇듯 R은 단순히 시각화를 통한 탐사 분석이나 보고서에 쓸 차트를그리는 용도뿐 아니라, 예측 모델을 만들고 이를 실전 서비스로 구현하는 데까지 전반적으로 활용되고 있습니다. 다음 편에서는 데이터 분석을 할 때 필요한 두 가지 요소에 대해 다뤄보도록 할게요. 
<데이터 프로덕트 개발 전 과정에서 핵심 도구로 사용하는 R>

머신 러닝 해킹 데이터분석

  아마 제목만 보고 어떤 사람은 머신 러닝을 이용해서 해킹하는 방법에 대한 글이라고 짐작했을지 모르겠다. 그러나 여기서 다루고자 하는 주제는 반대로 머신 러닝을 해킹하는 방법에 대한 것이다. 얼마 전 arXiv에서 아주 재미있는 논문을 하나 읽었다(https://arxiv.org/abs/1602.02697). 논문의 제목은 'Practical Black-Box Attacks against Deep Learning Systems using Adversarial Examples.' 말 그대로 (기계 학습 시스템이 학습하고 있는 내용에) 적대적인 사례들을 이용해서 시스템을 공격하는 기법에 대한 논문이다.

  아래 그림을 보자.
<해당 논문의 Figure 2>


  위 그림에서 윗줄에 있는 이미지들이 원본 이미지이며 아랫줄에 있는 이미지들은 논문에서 제안한 기법으로 변형한 이미지이다. 사람이 보기에는 크게 달라진 것이 없지만, 원본 이미지를 잘 인식하던 시스템이 변형된 이미지에 대해서는 잘못 인식하고 있다. 이것은 시스템에서 학습을 통해 획득한 모델 파라미터들이 변형된 이미지의 입력값에 대해서는 갑자기 전혀 다른 값을 출력하기 때문인데 일종의 과적합(overfitting) 현상이다.
  딥러닝처럼 복잡한 비선형 모델은 일반적으로 bias error가 낮은 대신 variance error 가 크다. 따라서 많은 데이터로 학습을 잘 할 경우 매우 정확도가 높은 모델을 만들 수 있지만 한편으로는 모델이 미처 감안하지 못한 특정 조건에 대해서는 전혀 엉뚱한 값을 가질 수 있다.
  비록 딥러닝을 실전에 적용할 때는 다양한 튜닝과 많은 데이터를 이용해 이런 overfitting 을 해결하고 있지만 모든 소프트웨어가 그렇듯 기계 학습 역시 완벽한 모델을 만들 수 없기 때문에 이런 과적합 포인트가 낮은 확률이나마 존재할 것이다. 그리고 이 논문은 원본 데이터의 변형을 최소화하면서 이런 모델의 취약점(과적합 포인트)을 찾아 공격하는 기법을 제안하고 있으며 전체 알고리즘은 아래와 같다.
<해당 논문의 Figure 3>

  학습 모델의 취약점을 정확히 파악하려면 해당 모델의 구조(함수식과 파라미터값)을 알아야 한다. 그러나 딥 러닝 모델의 경우 대개 많은 단계의 layer와 unit을 갖는 nueral network 구조를 갖기 때문에 실상 모델을 생성한 사람도 그 구조를 파악하는 것이 거의 불가능한 black box 이다. 그렇기 때문에 데이터를 조금씩 변형해 가며 정답과 다른 응답을 주는 데이터를 찾는 소위 '찔러보기'를 할 수 밖에 없다.
  그런데 실전에서 이렇게 하게 되면 시간도 오래 걸릴 뿐더러 그 동안 공격 대상자가 가만히 있을리 없기 때문에 실용성이 떨어진다. 그래서 논문에서는 대상 모델과 동일한 값을 응답하는 복제 모델을 생성한 후 이 모델에 대해 취약점 찾기를 한 후 복제 모델이 잘못된 값을 응답하는 데이터를 찾아 이것으로 원본 모델을 공격하는 기법을 제안하고 있다.

  이 때 복제 모델을 이용할 때 다음과 같은 두 가지 문제가 있다.
  첫 째, 원본 모델의 학습 알고리즘이나 구조를 모르는 상태에서 어떻게 복제 모델을 만들 것인가 하는 문제가 있다. 다시 말하면 딥러닝을 쓰더라도 세부 알고리즘은 어떤 걸 쓰는지, neural network 에서 레이어는 몇 단계이고 unit은 몇 개를 쓰는지, activation function은 어떤 걸 사용하는지, 데이터 전처리는 어떻게 하고 있는지 등의 정보가 없으면 정확한 복제 모델을 만들 수 없다. 그런데 논문에서는 내부 구조가 다른 모델이라 하더라도 동일한 오류를 낼 수 있다는 것을 실험을 통해 검증하였다. 즉, 복제 모델이 원본 모델과 구조가 다르더라도 학습 데이터에 대해서 같은 결과를 보인다면 오류도 동일하게 발생할 가능성이 높다는 것이다.
  둘 째, 복제 모델을 학습하기 위한 데이터를 어떻게 확보할 것인가 하는 문제가 있다. 재미있게도 이 논문에서는 원본 시스템에 대한 질의를 통해 학습 데이터를 확보하는 방법을 사용했다. 보통 공격 대상은 일반에 노출된 질의용 API 가 있으니 이를 통해 직접 학습할 데이터를 질의하여 그 응답을 학습 라벨로 사용하는 것이다. 다만 이 때 지나치게 많은 질의를 할 경우 공격 의도가 노출될 수 있기 때문에, 논문에서는 이를 방지하기 위해 질의를 최소화하면서 효과적으로 모델을 복제할 수 있는 기법(위 그림의 마지막 모듈에 있는 Jacobian-based Dataset Augmentation)을 제안하고 있다.

  마지막으로 위 기법을 이용해서 실전 서비스에 실험해 보니 오탐률이 최대 84.24% 였다고 한다.

  이 논문을 읽고 든 생각인데 머신 러닝 해킹은 크게 두 가지 방식으로 나눌 수 있을 것 같다.
  하나는 위 논문처럼 학습 모델이 잘못 응답하는 데이터를 찾아 공격하는 방식이다. 자율 주행 자동차가 대중화된다면 위 예에 나온 것처럼 도로 표지판을 기계가 잘못 인식하도록 (하지만 사람은 눈치채지 못할 만큼만) 살짝 변형하여 사고를 유발하는 공격을 상상해 볼 수 있겠다.
  또 하나는 모델의 학습 알고리즘이 갖는 취약점을 이용해 원래의 의도와 다르게 학습시키는 공격이다. 최근에 마이크로소프트에서 공개했던 채팅봇을 생각해 보자. 이 채팅봇은 사람들과의 대화를 통해 계속 학습하는 시스템이었는데 악의적인 사람들에 의해 인종 차별적인 발언을 학습하여 결국 서비스가 중단되었다. 이 사건은 일종의 해프닝으로 끝났지만 우리가 사용하거나 앞으로 사용할 대다수의 기계 학습 시스템은 이처럼 온라인 학습을 통해 진화하는 형태가 될 것이다. 따라서 취약점을 이용해 공격자의 의도대로 학습시키는 해킹을 충분히 상상해 볼 수 있다. 이를 테면, 경쟁 업체의 온라인 쇼핑몰 추천 알고리즘의 취약점을 공격하여 엉뚱한 상품들을 추천하도록 하는 공격을 생각해 볼 수 있겠다.

  최근 기계학습이 크게 발전하면서 여러 분야에 폭넓게 적용되고 있다. 인터넷이 발전하면서 피싱, 온라인 계좌 해킹, 악성 코드, 랜섬 웨어 등 다양한 해킹 공격이 사회 문제가 되고 있는 현재 상황을 생각해 보면, 조만간 기계 학습에 대한 보안 문제 역시 심각하게 대두될 가능성이 매우 높다. 그리고 내 생각에는 기계 학습 시스템에 대한 해킹은 기존 해킹보다 그 파급력이나 위험성이 훨씬 클 것이다.

  결국 근래 IT 보안이 크게 화두가 되고 있는 것처럼 앞으로 기계 학습 보안 역시 중요한 문제가 될 것이다. 지금은 보안 업계 및 학계가 '머신 러닝을 보안 분야에서 어떻게 활용할 것이가?'에 주로 집중하고 있는데 앞으로는 '머신 러닝의 보안은 어떻게 할 것인가?' 라는 질문에 대해서도 답할 준비를 해야겠다.

1 2 3 4 5 6 7 8 9 10 다음