[강좌] 빅데이터 실전 튜토리얼

#이동균 버즈니 리서치 엔지니어

1. 데이터 수집 – 크롤링 소개, Scrapy 및 BeautifulSoup 사용방법
2. 데이터 저장 – EFK 스택 사용방법
3. 데이터 가공 및 분석(1)
4. 데이터 가공 및 분석(2)
5. 데이터 가공 및 분석(3)

연재를 시작하며

‘데이터마이닝(Data Mining)’이란 ‘대규모로 수집된 데이터를 이용해 통계적 규칙이나 패턴을 찾아내는 것’으로 정의할 수 있다. 데이터마이닝이란 단어가 수용하는 범위가 상당히 넓음에도 불구하고 몇 년 전까지 데이터마이닝은 소수 전문가만을 위한 분야였다. 분석대상인 ‘대규모로 저장된 데이터’를 생산하거나 다룰 수 있는 곳이 드물었기 때문이다.

2000년대 후반, 웹 2.0과 SNS에서 시작된 웹의 발전은 꾸준히 이러한 판을 바꿨다. 페이스북, 트위터, 핀터레스트, 인스타그램 등을 통해 수천만 명의 사용자가 하루에 수십 개의 데이터를 생산하는 일이 일상이 됐으며, 해당 서비스들은 오픈API 등을 통해 자신들의 데이터를 (거의) 무료로 제공함으로써 데이터에 대한 접근성이 엄청나게 향상됐다.

데이터에 대한 접근성 향상은 연구자들과 기업의 관심을 끌기 시작했다. 이는 데이터의 수집·분석 기술의 발전에 더욱 힘을 실어줬고, 수많은 오픈소스 라이브러리들이 개발되고 발전할 수 있었다. 그 결과 현재는 조금의 기술만 습득하면 누구나 방대한 데이터를 수집하고 분석할 수 있는 기술적 환경이 조성됐다.

이러한 환경 위에서 모바일 홈쇼핑 포털 앱 ‘홈쇼핑모아’를 운영하는 버즈니는 월 수천만 건의 로그와 데이터를 수집·분석·검색할 수 있는 시스템을 구축해 운영하고 있다. 그 과정에서 수십 개의 오픈소스 라이브러리와 서비스들을 이용했고 결과는 현재 매우 만족스럽다.

이 로그 분석 시스템을 구축하고 운영하면서 얻은 노하우를 일부 공유하고자 한다. 모쪼록 본 글이 데이터마이닝이라는 짙은 안개에 첫발을 내딛는 사람들에게 손전등 정도의 역할은 할 수 있길 바란다.

크롤러 – 데이터를 어떻게 얻을 것인가?

시작하며 데이터마이닝이란 ‘대규모로 수집된 데이터’가 요구된다고 언급한 바 있다. 본 글에서는 ‘대규모’보다 ‘수집’에 초점을 맞춰 글을 풀어나가려고 한다. 방대한 웹의 바다에 흩어진 데이터를 수집하는 프로그램을 ‘크롤러’라고 부르며, 데이터를 직접 생산하는 경우가 아니면 대부분의 경우 크롤러를 작성해 데이터를 수집하게 될 것이다.

수집대상인 데이터의 형태는 크게 정형적 데이터와 비정형적 데이터로 구분할 수 있을 것이다. 웹2.0의 핵심가치 중 하나인 ‘데이터와 뷰의 분리(separate content from presentation)’로 인해 많은 서비스가 REST(Representational State Transfer) API를 제공하고 있다. 이렇게 API를 통해 XML, JSON, Binary 형태로 제공되는 데이터를 정형적 데이터라고 할 수 있다.

반면 웹페이지 형태(HTML)로 제공돼 직접 파싱해야 하는 데이터를 비정형적 데이터라고 할 수 있다. 웹페이지가 바뀜에 따라 데이터의 위치와 형태 및 가져오는 방법이 모두 바뀌기 때문이다. 웹상에 존재하는 모든 데이터가 정형적 데이터라면 좋겠지만, 우리가 익히 경험한 바, 가치 있는 데이터의 상당수는 비정형적 형태로 제공되고 있다. 따라서 본 글에서는 비정형적 데이터를 파싱해 수집하는 간단한 크롤러를 구현해본다.

버즈니 활용사례 – 구글 리뷰 크롤링

버즈니는 국내 모든 홈쇼핑 상품을 모아서 보여주는 ‘홈쇼핑모아’라는 모바일 앱을 주력 서비스로 하고 있다. 모바일 앱에서 리뷰 평점은 고객 신뢰도, 순위 및 다운로드에 영향을 주게 되며 지속 관리해야 할 대상이다.

그런데 일부 사용자는 1점짜리 앱 리뷰를 등록하면서 서비스 건의나 불만을 토로하기도 한다. 따라서 1점짜리 리뷰는 서비스의 필요악으로 지속 모니터링 할 필요가 있다. 하지만 구글에서는 리뷰 목록을 볼 수 있는 API를 따로 제공하지 않고 있다.

그래서 버즈니는 서비스 운영을 위해 리뷰 목록을 확인할 수 있는 페이지 주소를 알아내고, 해당 페이지의 HTML코드를 파싱해 1점짜리 리뷰만 얻어내는 파서를 만든 뒤, 주기적으로 리뷰들을 수집하는 크롤러를 작성해야 했다.

크롤러 작성하기

URI 알아내기

원하는 데이터가 있는 URI를 알아내는 것은 휴리스틱(경험에서 나오는 직관)에 의존해야 하는 경우가 많다. 이 경우도 예외는 아니다. 크롬 개발자도구의 Network탭을 켜고 구글 앱 페이지의 리뷰 섹션에서 다음 페이지로 계속 넘기다보면 계속 새로운 데이터가 로딩되고 있는 것을 확인할 수 있다. Network탭의 XHR 헤더 요청을 보면 페이지에서 AJAX로 요청하는 URI들을 확인할 수 있는데, 여기서 확인할 수 있는 주소는 아래와 같다.

▲ 구글 개발자 모드 화면

해당 요청에 대한 Headers탭을 보면 POST 요청이라는 사실과 요청 시 전달한 form-data 목록을 확인할 수 있다. 해당 내용을 반영해 파이썬 코드로 요청을 날려본다.

▲ POST 요청 파이썬 코드

결과가 JSON 문자열 형태로 잘 들어오는 것을 확인할 수 있다. 해당 결과를 파이썬에서 쓸 수 있는 객체로 변환해보자. 단, 5개의 쓰레기 문자열이 채워져 있으므로, 해당 부분을 슬라이싱 하는 것만 주의하면 된다.

▲ JSON으로 변환하는 코드


HTML 파싱하기

데이터 내용을 살펴보면 각 리뷰에 대한 HTML코드들을 리스트 형태로 반환하고 있다. HTML코드를 파싱하는 파서가 필요한 순간이다. XML/HTML 파서는 다양한 라이브러리가 있지만, 본 글에서는 가장 유명한 라이브러리 중 하나인 BeautifulSoup4(BS4)를 사용해보겠다. 설치방법은 극히 간단하므로 본 글에서는 다루지 않겠다.

HTML문서를 파싱하는 방법은 DOM 트리를 구성하는 일반적인 방식과 정규표현식을 이용하는 방법이 있다. BeautifulSoup에서는 DOM 트리를 구성해서 처리하고 있으며, 그에 따라 노드를 탐색하는 방식도 정할 수 있게 된다.

노드를 탐색하는 방식은 크게 2개로, XPath를 이용하는 방식과 CSS 셀렉터(selector)를 이용하는 방식이다. XML 데이터가 넘쳐나던 예전에는 XPath를 이용해 노드를 탐색하는 것이 일반적이었지만, 최근 풀스택(full-stack)개발자가 늘어나면서 CSS 셀렉터를 이용해 탐색하는 일이 많아지고 있다. 대부분 라이브러리가 CSS 셀렉터 탐색을 지원하며, 본 글에서도 해당 방법을 이용한다.

soup.prettify() 명령을 통해 해당 html 코드를 출력하고 분석해보면,
1. ‘.single-review’ 라는 엘리먼트 아래에 개별 리뷰 내용이 다 들어있고
2. ‘.currennt-rating’에 리뷰 점수가, ‘.review-body’에 리뷰 내용이 들어있다

는 사실을 확인할 수 있다. 해당 내용을 바탕으로 BS4의 CSS 셀렉터로 엘리먼트를 탐색해 데이터를 가져오는 파서를 작성하면 다음과 같다.

▲ CSS셀렉터를 통해 리뷰내용·점수를 가져오는 코드


파서를 크롤러로 동작시키기

크롤러란 자동화된 방법으로 데이터를 수집하는 프로그램이다. 즉, 우리가 만든 파서가 자동으로 실행되면서 데이터를 수집한다면 크롤러가 되는 것이다. 이제 작성한 파서가 주기적으로 실행되면서 1점짜리 리뷰를 찾아 로그를 남기도록 해보자.

▲ 데모나이즈 부분 코드

지금까지 아주 간단한 형태의 파서와 크롤러를 만들어봤다. 크롤러의 전체 코드는http://github.com/haandol/review_crawler 에서 확인할 수 있다. 위의 내용을 다 이해했다면 어떠한 형태의 비정형 데이터를 만나도 원하는 형태로 가공하고 수집할 수 있을 것이다.

마지막으로, 본 글에서 다루지는 않았지만, 크롤러의 핵심은 구현이 아니라 운영에 있다고 해도 과언이 아니다. 단순 헤더체크 문제 또는 데이터 정확성 검증 문제부터, 쿼리 횟수 제한 문제, 자바스크립트를 이용한 비동기 마크업 생성, 동일 페이지에 대한 다중 크롤러 운영 시 중복 수집 및 공유자원(Race Conditon) 문제까지 정말 다양한 문제들이 발생할 수 있다. 이러한 문제의 해결방법은 경험을 통해서만 얻을 수 있다.

원하는 데이터가 보인다면 지금부터 크롤러를 만들어보자. 자신이 만들고 운영한 크롤러 개수만큼 전문가에 가까워져 있을 것이다.

1. 데이터 수집 – 크롤링 소개, Scrapy 및 BeautifulSoup 사용방법
2. 데이터 저장 – EFK 스택 사용방법
3. 데이터 가공 및 분석(1)
4. 데이터 가공 및 분석(2)
5. 데이터 가공 및 분석(3)

지난 글에서는 빅데이터 분석의 시작으로 파서와 크롤러를 만들어 원하는 데이터를 수집하는 방법을 알아봤다. 이미 수집한 데이터를 우리가 갖고 있는 멋들어진 알고리즘으로 분석하기 전에, 잠깐 다음과 같은 상황을 생각해보자.

현재 우리는 목적에 맞는 분석 알고리즘을 갖췄으며, 모든 데이터는 평소 익숙하게 사용하던 RDBMS에 저장한 상태다. 어느 날 갑자기 수집하는 데이터의 종류가 늘어나거나 변경돼 데이터의 스키마가 변경된다면 어떨까? 며칠에 한 번씩 없던 컬럼이 생기거나 있던 컬럼이 없어진다면? 컬럼의 데이터 형태가 변경된다면 어떨까? 아마 데이터 분석과 스키마 유지보수를 하느라 하루가 어떻게 지나가는지도 모를 것이다.

또, 사업이 급속도로 성장하는 통에 분석을 요구하는 데이터의 종류가 하루가 다르게 늘어난다면? 데이터가 하루에도 최소 수 기가바이트씩 쌓이고 있다면? 변화무쌍한 스키마 덕에 분석에 필요한 데이터를 검색하는데 수 십초씩 걸린다면 어떨까? 필시 데이터 시각화 도구와 대시보드를 유지보수를 위한 전담팀을 꾸려야 할 수도 있다.

일견 극단적인 상황들만 나열한 것처럼 보이지만 사실 위에 언급한 일들은 상당히 일반적이다. 일주일에도 몇 번씩 벌어지곤 하는 다양한 데이터 재앙을 ‘EFK 스택’을 이용해 효과적으로 극복해오고 있다.

이번 글에서는 EFK 스택을 이루고 있는 ▲데이터 저장소이자 검색엔진인 ‘엘라스틱서치(ElasticSearch)’ ▲로그 파싱/수집기 ‘플루언티드(Fluentd)’ ▲데이터 시각화도구이자 엘라스틱서치의 대시보드인 ‘키바나(Kibana)’를 소개하고, ‘엔진X(NginX)’ 웹서버를 위한 로그 수집 시스템을 직접 구축해보도록 한다.
‘EFK 스택’이란

시작하면서 잠깐 언급했듯 EFK 스택은 엘라스틱서치, 플루언티드, 키바나의 3개 툴로 이뤄져있으며, 각각의 첫 글자를 따서 만든 합성어다.

엘라스틱서치는 루신(Lucene)을 기반으로 한 분산 검색 시스템으로, ‘쿼리를 통한 질의가 가능한 JSON 문서 기반 NoSQL 저장소’라고 설명할 수 있다. 엘라스틱서치를 사용하는 가장 큰 이유는 설정과 사용의 용이함이다. 설치에서 실행까지 5분도 걸리지 않으며, 각 엘라스틱서치 노드의 설정파일에서 true, false 값만 몇 개 조정해주면 마스터노드, 데이터노드, 서치노드로 구성된 클러스터를 구축할 수 있다.

최고의 오픈소스 검색엔진인 루신을 기반으로 하고 있으므로 루신의 고급 검색기능 외에도 인덱스에 대한 멀티테넌시나 JSON을 통한 쿼리 DSL(Query DSL), 퍼컬레이션(Percolation) 같은 기능들도 추가로 제공하고 있으며, 플러그인을 통해 원하는 기능을 어렵지 않게 추가할 수 있다.

플루언티드는 로그 수집기로, 사용자의 로그를 다양한 형태로 입력받아 JSON 포맷으로 변환한 뒤 다양한 형태의 출력방법을 통해 쌓을 수 있도록 해준다. 일반적으로 플루언티드 대신 로그스태시(Logstash)를 이용한 ‘ELK 스택’이 더 많이 알려졌는데, 로그스태시가 엘라스틱서치, 키바나와 같은 회사인 엘라스틱(Elastic)의 제품군에 들어가기 때문이다. 둘의 차이에 대해 아래 표에 간단히 정리해봤다.

▲ 로그스태시 vs 플루언티드

ELK와 EFK 중 어떠한 스택을 사용해도 무방하지만, 최근에는 운영과 설정상의 단순화를 위해 ELK보다 EFK 스택을 선택하는 추세다. 본 글에서도 상대적으로 설정이 간단하고 직관적인 플루언티드를 이용해 로그 분석 시스템을 구축해본다.

마지막으로 키바나는 데이터 시각화 도구로, 엘라스틱서치에 쌓인 데이터를 시각적으로 볼 수 있도록 대시보드를 제공한다. SQL의 WHERE 절 모양의 간단한 형태의 질의부터 JSON을 이용한 복잡한 쿼리 DSL 질의를 통해 사용자가 원하는 형태의 데이터를 쉽게 볼 수 있도록 도와준다.
버즈니 활용사례 – 로그 분석 시스템

국내 모든 홈쇼핑 상품을 한눈에 모아 볼 수 있는 ‘홈쇼핑모아’를 서비스 중인 버즈니에서도 사용자 로그 분석 시스템, 검색 시스템, 실시간 모니터링 시스템 등에 EFK 스택을 적극적으로 활용하고 있다.

로그 분석 시스템의 경우 하루에 수 기가씩 쌓이는 사용자의 요청을 4대의 웹서버를 통해 수집한 뒤 플루언티드를 통해 엘라스틱서치에 전달하고, 해당 정보를 추천 푸시 대상 추출이나 검색어 추천 등에 이용하고 있으며, 데이터의 부정확성이나 이상패턴을 파악해 슬랙(Slack)을 통해 알람을 해주는 실시간 모니터링 서비스와도 연동하고 있다.

이뿐만 아니라 키바나를 통해 누구든 즉시 서비스의 모든 데이터를 쉽게 확인할 수 있게 되면서 마케팅팀에서도 프로모션이나 마케팅 효과를 수치와 그래프를 통해 직접 확인할 수 있게 돼 효율적인 리소스 사용이 가능해졌다.

▲ 버즈니 로그 분석 시스템 구조도

EFK 설치 및 설정

EFK를 설치하고 엔젠X 로그 데이터를 플루언티드를 이용해 엘라스틱서치에 쌓는 방법에 대해서 알아보도록 한다. 설치와 설정은 리눅스-우분투(Ubuntu) 환경에서 진행된다고 가정하겠다. 각 도구의 설치는 너무 간단하다. 다운받고 압축을 풀면 바로 실행할 수 있다. 웹상에도 수많은 한글 문서가 있으므로 본 글에서는 설치에 대해서는 다루지 않겠다. 대신 로그스태시, 플럼(Flume) 등 다른 도구로 언제든 대체할 수 있으며, 독특한 구조로 돼있는 플루언티드의 개념 설명과 설정에 대해 좀 더 자세히 다뤄본다.

플루언티드 설치 및 설정

플루언티드는 로그 파서 겸 수집기로, 플러그인 방식으로 입력, 전달, 출력을 제어할 수 있다. 직관적인 구조로 설정이 매우 간편하지만, 로그스태시에 비해 유연성이 부족하기 때문에 복잡한 형태의 데이터를 수집해야 할 때는 로그스태시를 이용하는 것이 나을 수도 있다.

버즈니에서는 엔진X 로그 및 자체 규격의 서비스 로그를 수집하는데 해당 로그들의 형태가 정형화돼있고 크게 복잡하지도 않기 때문에 플루언티드를 사용해 로그를 수집하고 있으며, 운영 면에서 상당히 만족스럽게 생각하고 있다.

▲ 플루언티드 구조

플루언티드의 이벤트 처리방식은 독특하지만 이해하기 어렵진 않다. 플루언티드에서 모든 데이터는 태그를 갖고 있고 데이터를 처리하는 각 단계는 태그를 기준으로 구분된다. 각 단계를 지날 때마다 데이터에 달려있던 태그를 수정할 수 있으며, 이를 이용해 다양한 형태의 데이터를 효과적으로 가공할 수 있다. 실제 사용할 설정파일을 보면서 흐름을 따라가 보자.

▲ 플루언티드 설정파일

설정파일의 섹션은 source와 match로 나눠진다. 모든 섹션은 플러그인을 이용해 처리되며, 각 섹션의 type 필드를 통해 어떠한 플러그인을 사용할지 선언해줄 수 있다.

먼저 source는 데이터를 입력받을 방법을 지정하는 부분으로, 여기서는 tail 플러그인을 통해 입력을 받는다. tail 플러그인은 파일의 마지막 10줄을 읽어 들이는데, 리눅스의 ‘tail -F’와 마찬가지로 추가된 내용을 지속적으로 추적해 읽어 들인다.

이때 입력받은 데이터에는 source 섹션의 tag 필드에 의해 grep이라는 태그가 달리며, 데이터는 이제 grep 태그를 처리하는 10~15번 라인의 match 섹션으로 전달된다. 해당 섹션에서 사용하는 플러그인은 fluent-plugin-grep이라는 서드파티 플러그인으로, 따로 설치해야 한다.

플루언티드의 플러그인 설치 역시 간단하다. ‘gem install fluent-plugin-grep’(td-agent를 쓴다면 ‘td-agent install fluent-plugin-grep’)만 입력하면 바로 설치가 완료된다. grep 플러그인은 JSON 데이터에서 지정한 문자열이 포함된 키값(key-value)을 필터링해준다. 예제에서는 key가 path이며, value에 ‘/static’으로 시작하는 데이터는 삭제해준다.

grep 플러그인의 처리가 끝난 데이터의 태그는 이제 es.ouput으로 변경되며, 13~18번 라인의 es로 시작하는 모든 태그를 처리하는 match 섹션으로 데이터가 전달된다. 해당 섹션은 엘라스틱서치 플러그인을 통해 ‘localhost:9200’에 설치된 엘라스틱서치에 데이터를 쌓는다.

위 설정파일로 플루언티드를 실행하고, 설정파일이 있는 폴더에 실제 엔진X 로그파일을 nginx.log.1이라는 이름으로 복사해 넣으면 플루언티드가 자동으로 추가된 파일을 인식하고 처리하는 것을 확인할 수 있다.

▲ 플루언티드 통해 쌓인 엔진X 데이터를 키바나로 확인하는 화면

참고: 테스트용 더미데이터 쌓기

엔진X 로그파일이 없다면 cUrl을 이용해 데이터를 직접 추가할 수 있다.

▲ cUrl 이용해 customer 인덱스에 문서 추가

http로 입력하는 방식이 편하긴 하지만, 하나하나 입력하기에는 시간과 노력이 너무나 아깝다. 좀 더 복잡하고 많은 더미데이터를 자동으로 추가할 방법이 없을까? 만약 현재 작업환경에 노드JS(Node.js)와 NPM이 설치돼있다면 makelogs 프로그램을 이용할 수 있다. ‘npm install -g makelogs’ 명령을 통해 설치할 수 있으며, 이를 이용하면 복잡한 더미데이터를 원하는 만큼 자동으로 생성할 수 있다.

▲ makelogs 이용해 더미데이터 쌓기

파이썬(Python)으로 데이터 제어하기

마지막으로 엘라스틱서치에 쌓은 데이터를 파이썬을 통해 객체로 가져와보자. 파이썬 환경에서 엘라스틱서치의 데이터를 가져오는 방법으로는 크게 urllib, pycurl, requests 등의 라이브러리를 이용해 직접 http 요청을 생성하는 방법과, 엘라스틱서치에서 공식적으로 지원하는 파이썬 라이브러리를 설치해 사용하는 방법이 있다.

개인적으로는 공식 라이브러리를 이용하는 것을 추천하는데, 코드 양과 가독성에서 현격한 차이를 보이기 때문이다. 아래 그림에서 볼 수 있듯이, py-elasticsearch 라이브러리를 이용하면 단 2줄의 코드로 데이터를 쌓고 가져올 수 있다.

▲ py-elasticsearch 이용해 엘라스틱서치에 데이터 쌓고 가져오기

이번 글에서는 엘라스틱서치, 플루언티드, 키바나로 이뤄진 EFK 스택을 이용해 엔진X 데이터를 쌓고, 파이썬을 이용해 쌓아둔 데이터들을 가져오는 방법에 대해 알아봤다.

솔라(Solr), 스핑크스(Sphinx) 등 엘라스틱서치를 대체할 수 있는 다양한 공개SW 솔루션들이 존재하고 있지만, 최근 검색과 관련된 모든 분야에서 엘라스틱서치가 빠지지 않고 언급되는 이유는 쉬운 사용성 때문일 것이다.

엘라스틱서치를 사용하면 5분 만에 검색엔진을 바로 이용할 수 있고, 각 노드마다 설정 몇 개만 조정해주면 멋지게 동작하는 분산 검색 클러스터를 가질 수 있으며, 여기에 도커(Docker)나 베이그런트(Vagrant)와 같은 가상화 기술을 이용하면 클러스터의 노드를 줄이고 늘이는 것조차 자동화할 수 있다.

엘라스틱서치, 플루언티드에 대해 간단한 플러그인도 만들어보고, 엘라스틱서치 클러스터의 노드도 늘렸다 줄였다 하다보면 하루 수십 기가 정도의 데이터는 더 이상 ‘빅(big)’하게 느껴지지 않게 될 것이다.

1. 데이터 수집 – 크롤링 소개, Scrapy 및 BeautifulSoup 사용방법
2. 데이터 저장 – EFK 스택 사용방법
3. 데이터 가공 및 분석 (1)
4. 데이터 가공 및 분석 (2)
5. 데이터 가공 및 분석 (3)

데이터 분석의 기본적인 단계는 크게 다음의 4단계로 구분할 수 있다.
1. 데이터 수집 및 저장
2. 분석 알고리즘 선정
3. 분석 알고리즘에 맞춰 데이터 가공
4. 분석 알고리즘에 의한 처리(기계학습 등)

우리는 현재 데이터 수집 및 저장 능력을 갖춘 상태다. 그럼 바로 분석 알고리즘을 선정하는 단계로 가면 될까? 사실은 그렇지 않다. 현실 세계에서 분석 알고리즘의 선정은 대부분 군집화(Clustering), 분류(Classification), 예측(Regression)이라는 범주 중 하나를 선정하게 된다.

각 범주는 우리가 만나게 될 문제의 종류만큼이나 다양한 알고리즘을 포함하고 있고, 우리는 이러한 알고리즘 중 몇 가지를 선택해 다양한 데이터로 실험하게 되며, 이러한 과정을 통해 우리가 원하는 목표에 가장 부합하는 알고리즘과 데이터 형태를 찾을 수 있다.

이때 각 알고리즘은 학습에 요구되는 데이터의 종류나 형태가 다른 경우가 많다. 따라서 다양한 알고리즘을 실험하기 위해서는 데이터를 원하는 형태로 가공할 수 있는 능력이 필수적이라고 할 수 있다.

이번 글에서는 우리가 수집한 데이터를 원하는 형태로 가공하는 방법(특히 벡터와 매트릭스)을 알아보고,Word2Vec 알고리즘을 이용해 영화배우와 영화감독 간의 간단한 의미 분석 도구를 작성해보도록 하겠다.

본 글에서 사용된 코드는 https://github.com/haandol/kobis 에서 확인할 수 있으며, 데이터는 영화진흥위원회에서 제공하는 KOBIS 영화정보를 크롤링해 예제로 사용했다.
Pandas

파이썬(Python)으로 데이터 분석을 할 때 빠지지 않고 등장하는 라이브러리가 바로 Pandas와 Numpy다. Numpy는 과학 분야에서 자주 다루게 되는 다차원배열에 대한 연산을 빠르게(거의 C 코드에서 배열을 다루는 속도로) 처리할 수 있게 하는 라이브러리이며, Pandas는 Numpy를 기반으로 해 더 추상화된 다양한 편의기능들을 제공하는 라이브러리라고 생각하면 된다.

본 글에서는 Pandas에서 제공하고 있는 수많은 기능을 하나하나 언급하기보다는, 실제 데이터를 가공하는 간단한 예제를 기반으로 자주 사용하는 기능들만 소개할까 한다. 백문이 불여일견이라고 했으니 코드를 보면서 설명하겠다.

▲ <그림 1> Pandas에서 CSV를 읽어 들여 DataFrame으로 만들기

CSV(Comma Separated Values)는 몇 가지 필드를 쉼표(,)로 구분한 텍스트 데이터 및 텍스트 파일로 다양한 곳에서 자주 쓰는 형태다. Pandas에서는 이렇게 자주 사용되는 파일 형태에 대해 손쉽게 Pandas 데이터 객체로 읽어 들일 수 있도록 함수를 제공하고 있으며, 예제에서 사용하는 CSV 외에도 SQL 쿼리 결과(read_sql), 엑셀 파일(read_excel), HTML 파일(read_html) 등이 대표적인 지원 형태다.

이렇게 읽은 데이터는 데이터프레임(DataFrame, 이하 DF)이라는 Pandas데이터형으로 저장이 되는데, DF는 일단 RDBMS에서 사용하는 테이블 구조를 파이선 객체로 옮겨놓은 것이라고 생각하면 된다. DF도 테이블과 마찬가지로 행(row), 과 열(column)로 구성됐으며, 각 행을 가져올 수 있는 주키(Primary Key: PK)가 존재한다. DF에서는 주키를 인덱스(Index)라고 부른다.

열과 행에 접근하는 방법은 아주 쉽다. 열에 접근할 때는 열 이름을 키로 해 파이썬 사전형(dictionary)처럼 사용하면 된다. 행에 접근할 때는 인덱스를 이용해 접근해야 한다. <그림 1>의 데이터를 보면 id 열 왼쪽에 0, 1, 2, 3… 등이 인덱스다. 행에 접근할 때도 인덱스를 이용하는 부분만 다를 뿐 마찬가지로 인덱스 이름을 키로 해 파이썬 사전형처럼 사용하면 된다. 이렇게 행과 열에 접근하는 방법은 <그림 2>와 같다.

▲ <그림 2> DataFrame 객체의 행과 열에 접근하기

만약 인덱스를 id로 바꾸고 싶다면 어떻게 하면 될까? reindex 명령을 통해 인덱스를 변경할 수 있다. 아래 <그림3>에서 보듯 reindex를 이용할 때 columns 파라미터에 원하는 열의 이름을 배열로 넘겨줌으로써 불필요한 열들은 빼버릴 수도 있다.

▲ <그림 3> reindex 이용해 데이터 인덱스와 열 변경하기

그런데 <그림 3>에서 첫 번째 행의 actor에 보이는 NaN은 값이 존재하지 않는다(NULL)는 의미다. 만약 값이 존재하지 않는 열을 갖고 있는 행을 제거하고 싶다면 어떻게 해야 할까? dropna 명령을 통해 해당 행들을 쉽게 제거할 수 있다.

▲ <그림 4> dropna로 값이 없는 열을 포함한 행들 제거

이제 데이터가 기본적인 형태는 갖췄으니 조건에 맞춰 데이터를 뽑아내보자. ‘박찬욱’ 감독이 director인 영화의 actor들은 어떻게 확인할 수 있을까? 다양한 방법으로 가져올 수 있겠지만 가장 간단한 2가지 방법만 알아본다.

먼저 RDBMS에 익숙한 이들은 ‘group by’를 떠올릴 텐데, DF도 해당 명령을 제공하고 있다. 함수 이름도 groupby다. groupby(‘director’)를 통해 director별로 묶어낸 DataFrameGroupBy 객체를 얻을 수 있다. 이 객체는 get_group이라는 함수를 통해 묶인 개별 그룹을 반환한다. <그림 5>와 같이 get_group(‘박찬욱’)으로 박찬욱이 director인 행들을 가져올 수 있다. 다른 방법으로는 df.director의 조건문 기능을 이용하는 방법이 있다.

df.director == ‘박찬욱’ 이라는 명령을 통해 해당 조건에 맞춰 전체 행 크기만큼의 True 또는 False로 채워져 있는 배열(Series)을 반환한다. DF에 해당 배열을 키값으로 넣으면 True 값인 행만 얻을 수 있다.

▲ <그림 5> ‘박찬욱’ 감독의 영화제목만 가져오기

다음 절에서 다룰 Word2Vec에 학습시키기 위해 데이터 형태를 ‘영화제목 영화감독 배우1 배우2 배우3…’ 형태로 가공해보겠다.

▲ <그림 6> Word2Vec에 학습시키기 위해 데이터 형태 변환

Word2Vec

Word2vec은 이름 그대로 단어(word)를 수십~수백 차원의 벡터(vector)로 변환해 단어의 의미를 추론하는 알고리즘이다. 이 알고리즘은 내부적으로 CBOW(Contiguous Bag Of Words) 와 SGNS(Skip-Gram model with Negative Sampling)이라는 2개의 신경망 모델을 이용해 단어(Vocabulary)를 학습하게 된다.

각 모델은 이름에서 언급된 Bag of Words, Skip-gram, Negative sampling 외에도 워드 임베딩(word embedding), 신경망(Neural net) 등을 따로 언급해야 설명할 수 있으므로, 본 글에서는 내부알고리즘과 구현에 대해서는 다루지 않겠다. 이런 다소 복잡해 보이는 알고리즘을 적용한 결과는 상당히 놀랍다. Word2Vec을 이용하면 ‘프랑스 – 파리 + 서울 = 한국’, ‘왕 – 남자 + 여자 = 여왕’ 같은 신기한 결과를 얻을 수 있다.

이번 절에서는 <그림 6>에서 얻은 결과를 Word2vec에 학습시켜 다양한 결과를 얻으려 한다. 먼저 Word2vec을 이용하기 위해 gensim 서드파티 라이브러리를 PIP를 통해 설치한다. gensim은 자연어를 벡터로 변환하는데 필요한 대부분의 편의기능을 제공하고 있는 라이브러리로, 이후 글에서도 계속 사용할 예정이니 시간 날 때 문서를 읽어보는 것도 좋다.

gensim은 Word2vec을 포함하고 있으며, 이를 사용하기 위해서는 단순히 모듈을 임포팅하면 된다. 학습하는 과정도 굉장히 간단한데 <그림 7>과 같이 word2vec.Text8Corpus를 이용해 텍스트 파일에서 문장들을 가져온 뒤 word2vec.Word2vec에 문장들을 전달함으로써 학습시킬 수 있다.

Word2vec은 몇 백만 건 단위로 학습시켜야 제대로 분석할 수 있다고 하지만, 본 글에서는 KOBIS에서 제공하는 영화정보 중 약 3만 건의 영화에 대해 학습시켜봤다. 백만 개 남짓의 단어이기 때문에 몇 분 안에 학습이 됐다. 논문에 따르면 8억 개 단어를 학습하는데 약 하루 정도 걸린다고 한다.

▲ <그림 7> gensim 통한 Word2vec 임포팅 및 문장 학습시키기

학습이 끝났으니 쿼리를 넣어 결과를 확인해보자. <그림 8>에서 2가지 쿼리를 예로 넣어봤다.
1. ‘올드보이’ + ‘박찬욱’ – ‘스릴러’ = ?
2. ‘크리스토퍼놀란’ + ‘크리스찬베일’ – ‘히스레저’ = ?

▲ <그림 8> Word2vec 학습된 모델에 쿼리 넣기

학습된 모델을 이용해 쿼리를 실행한 결과는 다음과 같다.
1. ‘올드보이’ + ‘박찬욱’ – ‘스릴러’ = ‘미쓰홍당무’
2. ‘크리스토퍼놀란’ + ‘크리스찬베일’ – ‘히스레저’ = ‘다크나이트라이즈’

Word2vec으로 학습시키는데 권장되는 데이터양보다 많이 부족한 상태였지만 상당히 그럴듯한 결과를 얻을 수 있었다.

이번 글에서는 Pandas, Numpy를 이용해 데이터를 가공하는 방법과 Word2Vec 라이브러리를 통해 단어 간의 관계를 알아보는 간단한 의미 분석 툴을 작성해봤다. 이제 우리는 어떠한 분석 알고리즘을 선정하더라도 당황하지 않게 됐다. 해당 알고리즘이 요구하는 형태가 무엇이든 해당 모양으로 원본 데이터를 가공할 수 있는 능력을 갖췄기 때문이다.

데이터와 분석 알고리즘이 모두 갖춰졌다면 우리가 할 일은 별로 없다. 시간과의 싸움만이 남았을 뿐이다. 다만, 시작하며 언급했듯이 실제 환경에서 분석 알고리즘의 선정은 크게 군집화(Clustering), 분류(Classification), 예측(Regression)이라는 범주 중 하나를 선택하게 된다. 범주를 정하는 것은 대부분의 경우 꽤 명확하므로 크게 어렵지 않지만, 범주내의 어떤 알고리즘을 어떤 데이터 형태를 입력값으로 설정해 사용(또는 학습)할 것인가는 많은 경험을 통해 얻을 수 있는 직관이 없다면 굉장히 난해한 경우가 많다.

즉, 데이터 분석의 전문가가 되기 위해서는 여러 가지 알고리즘을 다양한 모양의 데이터로 학습시켜보고 어떤 경우에 최적의 결과를 얻을 수 있는지 지속적으로 실험할 필요가 있으며, 앞으로의 글도 실제 환경에서 사용하는 예제를 통해 단순화시킨 이론을 소개하고 이를 토대로 실용적인 경험을 쌓는데 중점을 두고자 한다.

——————————————————————

Scrap:

버즈니는 스타트업으로 출발할 때부터 공개SW와 밀접한 관계를 맺어왔다. 저자본의 스타트업인 만큼 비용효율적인 방법이 요구됐고, 가능성을 타진하기 위해 선보이는 각종 서비스를 적은 인력으로 관리하기 위해서도 공개SW 도입이 필요했다는 것이 남상협 버즈니 대표의 설명이다.

이에 따라 버즈니는 설립 초기부터 ▲RDBMS ‘포스트그레SQL(PostgreSQL)’ ▲NoSQL DB ‘몽고DB(MongoDB)’ ▲인메모리 기반 키값 스토어 ‘레디스(Redis)’ ▲메모리 캐싱 시스템 ‘멤캐시드(Memcached)’ ▲메시지 큐 ‘래빗MQ(RabbitMQ)’ ▲비동기 작업 큐 ‘셀러리(Celery)’ ▲웹 프레임워크 ‘장고(Django)’ 및 ‘팔콘(Falcon)’ 등을 지속 도입 및 활용해왔다.

현재 주력서비스로 자리한 ‘홈쇼핑모아’의 개발 및 서비스에도 공개SW가 활용됐음은 물론이다. 메인DB로 쓰고 있는 ‘포스트그레SQL’은 직접 서버에서 운영할 뿐 아니라 AWS(아마존웹서비스) 클라우드 버전을 적극 활용해 관리 소요를 줄이고 있다. ‘레디스’는 사용자 관리 세션에, ‘멤캐시드’는 검색결과 캐싱을 통한 빠른 전달에 쓰이고 있고, 사용자가 관심 있어 하는 상품에 대한 푸시메시지 관련 프로세스는 ‘래빗MQ’와 ‘셀러리’로 진행된다.

나아가 ‘홈쇼핑모아’에는 기계학습(머신러닝)을 활용한 서비스도 적용됐다. 6개 홈쇼핑사의 상품분류 카테고리 분류방식이 다소 차이가 있어, 이 데이터들을 통합해 서비스하기 위해서는 자체적인 하나의 기준으로 재정리해 제공할 필요가 있었기 때문이다.

이를 위해 버즈니는 일반적으로 사용되는 자연어 분석 기반 분류가 아닌, 딥러닝(deep learing)을 통한 이미지 분석 기반 분류방식을 채택키로 했다. ▲딥러닝 개발 프레임워크 ‘까페(Caffe)’ ▲딥러닝 GPU 트레이닝SW 디지츠(Digits) 등을 적용, 상품의 이미지를 분석해 자동으로 카테고리가 지정될 수 있게 했다. 이를 통해 분류과정에서 상품코드나 영문상품명으로 인한 오류를 감소시킨 것이다.
공개SW 기반 ‘EFK’로 모니터링부터 마케팅까지

▲ ‘홈쇼핑모아’ EFK 시스템 구조도

‘홈쇼핑모아’에 구축된 공개SW 기반 시스템 가운데 핵심은 ‘EFK’다. ▲검색엔진 ‘엘라스틱서치(ElasticSearch)’ ▲로그 수집기 ‘플루언티드(Fluentd)’ ▲데이터 시각화 ‘키바나(Kibana)’의 머리글자를 조합한 것으로, 기존 ‘ELK’ 중 ‘로그스태시(Logstash)’ 대신 ‘플루언티드’를 도입 및 커스마이징해 데이터 파싱에서 사용성을 높였다는 것이 이동균 버즈니 리서치엔지니어의 설명이다.

‘EFK’ 시스템을 통해서는 데이터를 ‘플루언티드’로 수집·가공, 이를 ‘엘라스틱서치’로 색인, 이후 ‘키바나’를 통해 다양한 통계로 표현할 수 있다. 지난해 상용SW인 ‘스플렁크(Splunk)’를 도입하는 대신 2개월 만에 구축했으며, 각종 플러그인으로 기능을 구현하고 필요한 플러그인은 직접 제작해 추가하기도 했다. 또 검색기술 전문회사답게 학계에서 논의되고 있는 방법론도 반영했다.

이 시스템은 현재 실시간 모니터링, 마케팅 성과분석, 홈쇼핑상품 통합검색 등 여러 곳에 쓰이고 있다. 6개 홈쇼핑에서 생성되는 데이터를 검색하고, 실시간으로 시스템 프로세스를 모니터링하며, 이를 바탕으로 다양한 데이터를 분석해 활용한다. 특히, 사내 마케팅팀이나 디자인팀에서도 간단한 교육을 통해 직접 로데이터를 원하는 형태로 가공하고 활용할 수 있어, 데이터 주도적인 업무환경 조성이 가능해졌다.

버즈니는 지속적인 커스터마이징을 통해 ‘EFK’ 시스템을 강화해나가고 있으며, 특히 데이터 인텔리전스를 가미하기 위해 사용자 행동패턴 분석을 기계학습으로 수행하고 있다. 이를 위해 ‘테아노(Theano)’, ‘사이킷(Scikit)’, ‘까페(Caffe)’ 등을 이용하고 있으며, 향후 해당 기능을 ‘EFK’에 쉽게 적용할 수 있도록 발전시킬 계획이다. 아울러, 서버 설정 및 서비스 배포 자동화를 위해 시범적으로 도입했던 ‘도커(Docker)’를 앞으로 다양한 곳에 적용할 예정이다.

▲ ‘홈쇼핑모아’ 이용 현황 분석

REF.:

http://www.comworld.co.kr/news/articleView.html?idxno=48776

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s