 |
|
난이도 : 중급 Grant Ingersoll, Member, Technical Staff, Lucid Imagination
원문 게재일 : 2009 년 9 월 08 일 번역 게재일 : 2009 년 10 월 20 일 학계와 기업에서 많은 연구 예산을 투자하고 있는 분야로서 데이터 및 사용자 입력을 통해 학습하는
지능형 애플리케이션이 활성화되고 있습니다. 많은 그룹의 사람들 중에서 공통점을 찾아내거나 대량의 웹 컨텐츠에
자동으로 태그를 지정하는 등의 작업을 수행하기 위해 그 어느 때보다도 클러스터링, 협업 필터링 및 분류와 같은 기계 학습
기술의 필요성이 부각되고 있습니다. Apache Mahout 프로젝트의 목표는 지능형 애플리케이션을 쉽고 빠르게
개발하는 것입니다. 이 기사에서는 Mahout의 공동 설립자인 Grant Ingersoll이 기계 학습의 기본 개념을 소개한 후
Mahout을 사용하여 문서를 클러스터링하고, 추천 항목을 만들고, 컨텐츠를 구성하는 방법에 대해 설명합니다.
현대 정보 사회에서는 기업 및 개인의 성공에 있어서 방대한 양의 데이터를 효율적으로 빠르게
활용 가능한 정보로 전환할 수 있는 능력의 중요성이 점점 더 높아지고 있다. 하루에 수백, 수천 개의
개인 이메일 메시지를 처리하기 위해 또는 웹 블로그에 있는 방대한 용량의 데이터에서 사용자가 원하는
내용을 찾기 위해서건 상관 없이 데이터를 구성 및 개선할 수 있는 도구의 필요성이 그 어느 때보다도
높아졌다. 바로 이러한 점에서 기계 학습 분야와 이 기사에서 소개하는 프로젝트인 Apache
Mahout(참고자료 참조)에 대한 전제와 전망이 가능한 것이다.
인공 지능의 하위 분야인 기계 학습은 이전 경험을 바탕으로 컴퓨터의 출력을 향상시키는 기술을
주로 다루는 분야이다. 이 분야는 데이터 마이닝과 연관성이 높으며 통계, 확률 이론, 패턴 인식을
비롯한 다양한 영역의 기술을 사용한다. 기계 학습은 새로운 분야는 아니지만 꾸준히 성장하고 있는
분야이다. IBM®, Google, Amazon, Yahoo!, Facebook을 비롯한 여러 대형 회사에서는 자사의
애플리케이션에 기계 학습 알고리즘을 구현해서 활용하고 있다. 그리고 훨씬 더 많은 회사에서는
사용자 및 과거 상황을 바탕으로 유용한 정보를 찾기 위해 애플리케이션에서 기계 학습을 활용하고
있다.
먼저 기계 학습 개념을 간단히 살펴본 후 Apache Mahout 프로젝트의 기능, 역사 및 목표를
살펴보자. 그런 다음 Mahout을 통해 무료로 사용할 수 있는 Wikipedia 데이터 세트를 사용하여
몇 가지 흥미로운 기계 학습 작업을 수행하는 방법에 대해 설명한다.
기계 학습 101
기계 학습은 게임부터 부정 감지 및 주식 시장 분석에 이르는 다양한 분야에서 사용되며 과거 구매
레코드를 기반으로 사용자에게 제품을 제안하는 Netflix, Amazon 등의 시스템이나 지정한 날짜의 유사한
뉴스 기사를 모두 찾아 주는 시스템을 구축하는 데도 사용된다. 웹 페이지를 장르(스포츠, 경제, 전쟁
등)에 따라 자동으로 분류하거나 이메일 메시지를 스팸으로 표시하는 데도 사용된다. 기계 학습을 적용할
수 있는 분야는 이 기사에서 모두 다루기 어려울 정도로 매우 광범위하므로 보다 자세한 내용을 알고
싶다면 참고자료를 참조하기 바란다.
기계 학습에서는 문제 해결을 위해 여러 가지 방법론이 활용되고 있지만 이 기사에서는 그 중에서
가장 일반적으로 사용되고 있고 Mahout에 지원되는 주요 방법론인 지도 및 자율 학습에
중점을 두고 설명한다.
지도 학습은 유효한 입력값을 예측하기 위해 레이블이 지정된 교육용 데이터에서 함수를 유추해 내는 과제가
할당되는 방법이다. 지도 학습의 일반적인 예로는 이메일 메시지를 스팸으로 분류하는 작업, 장르에 따라 웹
페이지에 레이블을 지정하는 작업 및 필기 인식 등이 있다. 지도 학습기를 만들기 위해 많은 알고리즘이 사용되기는 하지만 그 중에서도
신경망, SVM(Support Vector Machines) 및 Naive Bayes 분류기가 가장 일반적으로 사용된다.
자율 학습은 짐작대로, 모범 사례 또는 오류 사례에 관한 예제 없이 데이터를 이해하도록 과제가 할당되는
방법이다. 유사한 입력을 논리 그룹으로 클러스터링하는 경우에 가장 일반적으로 사용되며 가장 유용한 속성에만
집중하거나 경향을 찾아내기 위해 데이터 세트의 차원 수를 줄이는 데도 사용할 수 있다. 자율 학습에
일반적으로 사용되는 방법으로는 k-Means, 계층 클러스터링 및 자기 조직화 지도가 있다.
이 기사에서는 현재 Mahout에 구현되어 있는 세 가지 특정 기계 학습 작업에 대해서만 다루며 이러한
작업은 실제 애플리케이션에서도 매우 일반적으로 사용되고 있다.
우선 이러한 각 작업을 개념 수준에서 자세히 살펴본 후 Mahout에 구현된 모습을 살펴보자.
협업 필터링
협업 필터링(Collaborative filtering, CF)은 등급, 클릭 수 및 구매 수 등과 같은 사용자
정보를 사용하여 다른 사이트 사용자에게 추천 항목을 제공하는 Amazon 등의 여러 기업에서 많이 사용하고
있는 기술이다. CF는 책, 음악, 영화 등의 소비재 품목을 추천하는 데 사용되기도 하지만 여러 작업자가
협력하여 데이터를 간추릴 필요가 있는 다른 애플리케이션에서도 사용되고 있다. 그림
1에서는 Amazon에서 실제로 사용되고 있는 CF를 보여 준다.
그림 1. Amazon에서 사용하고 있는 협업 필터의 예
CF 애플리케이션은 사용자 및 항목 세트를 기반으로 시스템의 현재 사용자에게 추천 항목을
제공한다. 일반적으로 추천 항목은 다음 네 가지 방법으로 생성된다.
- 사용자 기반: 유사한 사용자를 찾아서 항목을 추천한다. 이 방법은 사용자의 동적 특성으로 인해 확장하기가 어렵다.
- 항목 기반: 항목 간의 유사성을 계산하여 추천 항목을 만든다. 일반적으로 항목은 많이 바뀌지 않기 때문에 오프라인으로도 계산할 수 있다.
- Slope-One: 부울 연산자를 사용한 선호도가 아니라 사용자가 등급을 지정할 때 적용 가능한 매우 빠르고 간단한 항목 기반의 추천 방식이다.
- 모델 기반: 사용자와 등급으로 구성된 모델을 기반으로 추천 항목을 제공한다.
모든 CF 방법에서는 결과적으로 사용자와 관련 항목 간의 유사성을 계산하게 되는데 이 유사성은
여러 가지 방법으로 계산할 수 있다. 대부분의 CF 시스템에서는 다양한 측정 기준을 추가할 수 있기
때문에 사용자가 자신의 데이터에 가장 적합한 측정 기준을 선택해서 사용할 수 있다.
클러스터링
데이터 세트가 텍스트이건 문자이건 관계 없이 대량의 데이터 세트가 있을 경우 유사한 항목끼리 그룹화
또는 클러스터링하면 유용한 점이 많다. 예를 들어, 국내 모든 신문에 실린 오늘의
모든 뉴스 중에서 동일한 주제를 다루고 있는 모든 기사가 자동으로 그룹화된다면 관련이 없는 수많은
기사는 들춰볼 필요도 없이 특정 주제의 기사만 선택해서 집중적으로 볼 수 있다. 또 하나 예를 들어
보면 장시간 작동 중인 시스템의 센서에서 발생하는 출력의 경우 출력을 클러스터링하여 정상 작동과 문제
작동을 결정할 수 있다. 왜냐하면 정상 작동은 일정한 범위 내에 모두 포함되지만 비정상 작동은 그
범위에서 벗어나 있기 때문에 쉽게 알 수 있다.
CF와 마찬가지로 클러스터링도 컬렉션 내의 항목 간 유사성을 계산하지만 클러스터링은 유사 항목을 그룹화하는
작업만 수행한다는 점이 다르다. 많은 클러스터링 구현에서는 컬렉션의 항목을 n차원 공간의 벡터로
표현한다. 이러한 벡터가 주어지면 Manhattan Distance, Euclidean Distance 또는 코사인 유사성과 같은
측정법을 사용하여 두 항목 간의 거리를 계산할 수 있다. 그런 다음 가까이 있는 항목을 그룹화하여 실제
클러스터를 계산할 수 있다.
클러스터는 여러 가지 방법으로 계산할 수 있으며 각 방법에는 나름대로의 장단점이 있다. 일부 방법은
작은 클러스터부터 시작하여 큰 클러스터를 만들어 나가는 상향식으로 진행되는 반면 큰 클러스터부터 시작하여 점점
더 작은 클러스터로 분할해 나가는 하향식으로 진행되는 방법도 있다. 두 종류의 방법 모두 사소한 클러스터 표현(한
클러스터 내의 모든 항목 또는 자체 클러스터 내의 모든 항목)으로 분할하기 전의 임의 시점에서 프로세스를
종료하는 기준이 있다. 많이 사용되는 방법으로는 k-Means 및 계층 클러스터링이 있으며 Mahout에는 여러 다양한
클러스터링 방법이 포함되어 있다. 이에 대해서는 나중에 설명한다.
분류
분류(categorization 또는 classification)의 목적은 보이지 않는 문서에 레이블을
지정하여 그룹화하는 것이다. 기계 학습의 많은 분류 방법에서는 문서의 특징과 지정된 레이블을 연결하는
다양한 통계를 계산한 후 나중에 보이지 않는 문서를 분류할 때 사용할 수 있는 모델을 작성한다. 간단한
분류 방법의 예로는 레이블에 연결된 단어와 이 단어가 지정된 레이블에 대해 표시된 횟수를 추적하는 방법이
있다. 그런 다음 새 문서가 분류되면 문서에 있는 단어가 모델에 따라 검색되고 확률이 계산된 후 최상의
결과가 출력되며 이 경우 일반적으로 결과의 정확성에 대한 신뢰도를 나타내는 점수도 함께 표시된다.
분류에 대한 특성에는 단어, 단어에 대한 가중치(예: 빈도 기준), 음성 부분 등이 포함될 수 있으며 실제로
문서와 레이블을 연결하는 데 사용할 수 있는 모든 것이 특성이 될 수 있으며 알고리즘에 통합될 수도 있다.
기계 학습 분야는 너무 방대하기 때문에 이론적인 부분에 대한 설명은 여기까지만 다루고
앞으로는 Mahout과 그 사용법에 대해 살펴보자.
Mahout 소개
Apache Mahout은 ASF(Apache Software Foundation)에서 추진 중인 새로운 오픈 소스 프로젝트로, 확장 가능한
기계 학습 알고리즘을 만드는 것이 주요한 목적이며 Apache 라이센스가 있으면 무료로 사용 가능하다. 2년째에 접어들고
있음에도 불구하고 이 프로젝트는 벌써 첫 번째 공용 릴리스를 발표했다. Mahout에는 클러스터링, 분류, CF 및 진화
프로그래밍을 위한 구현이 포함되어 있다. 게다가 Apache Hadoop 라이브러리를 사용하면 클라우드에서 Mahout을
효과적으로 확장할 수도 있다(참고자료 참조).
Mahout 역사
 |
이름의 의미
Mahout은 코끼리를 보살피고 운전하는 사람을 뜻하는 말로 프로젝트에서 확장성과 내결함성을 위해
노란색 코끼리를 로고로 사용하는 Apache Hadoop을 사용하면서 지어진 이름이다.
|
|
Mahout 프로젝트는 Apache Lucene(오픈 소스 검색) 커뮤니티와 관련된 여러 사람에 의해 시작되었는데
이들은 기계 학습에 대한 관심도 높고 클러스터링 및 분류를 위한 공통 기계 학습 알고리즘을 강력하면서도 확장
가능하고 문서화가 잘 이루어진 형태로 구현하고자 하는 열망이 강한 사람들이었다. 이 커뮤니티는 공동
논문인 "Map-Reduce for Machine Learning on Multicore"(참고자료 참조)를 계기로
시작된 이후 발전을 거듭하여 현재는 훨씬 더 다양한 기계 학습 방법론을 다루고 있다. Mahout의 목표는
다음과 같다.
- 사용자나 제공자의 커뮤니티를 구축하고 지원하여 특정 제공자의 참여나 회사 또는 대학의 투자에 비해 훨씬 오랫 동안 코드를 활용하는 것이 목적이다.
- 첨단 연구나 검증되지 않은 기술보다는 실제 환경에서 사용할 수 있는 실질적인 사례에 중점을 둔다.
- 높은 수준의 문서와 예제를 제공한다.
기능
오픈 소스 용어로서는 비교적 새 용어임에도 불구하고 Mahout에는 이미 많은 기능이 들어 있으며
특히, 클러스터링 및 CF와 관련된 기능이 많이 있다. Mahout의 주요 기능은 다음과 같다.
 |
Map-Reduce의 몇 가지 용어
Map-Reduce는 Google의 주도로 Apache Hadoop 프로젝트에서 구현된 분산 프로그래밍
API이다. 병렬 계산 작업을 설명하기 위한 잘 정의된 API인 Map-Reduce를 통해 프로그래머는
분산 파일 시스템과 관련된 병렬화 문제를 쉽게 해결할 수 있다. 참고자료에서
자세한 정보를 확인할 수 있다.
|
|
- Taste CF. Taste는 SourceForge의 Sean Owen에 의해 시작된 CF를 위한 오픈 소스 프로젝트로 2008년에 Mahout으로 귀속되었다.
- k-Means, fuzzy k-Means, Canopy, Dirichlet 및 Mean-Shift를 포함한 여러 가지 Map-Reduce 사용 클러스터링 구현.
- Distributed Naive Bayes 및 Complementary Naive Bayes 분류 구현.
- 진화 프로그래밍을 위한 분산 적합성 함수 기능.
- 행렬 및 벡터 라이브러리.
- 위 모든 알고리즘의 예제.
Mahout 시작하기
Mahout은 비교적 쉽게 시작하고 실행할 수 있다. 시작하려면 먼저 다음과 같은 프로그램을 설치해야 한다.
Mahout 및 해당 종속성 사본이 포함된 이 기사의 샘플 코드도 필요하다(다운로드
참조). 다음 단계에 따라 샘플 코드를 설치한다.
unzip sample.zip
cd apache-mahout-examples
ant install
단계 3에서는 필요한 Wikipedia 파일을 다운로드하고 코드를 컴파일한다. 이 기사에서는 약 2.5GB의
Wikipedia 파일을 사용하므로 대역폭에 따라 다운로드 시간이 오래 걸릴 수도 있다.
추천 엔진 빌드하기
Mahout은 현재 빠르고 유연한 CF용 엔진인 Taste 라이브러리를 통해 추천 엔진을 빌드할 수 있는 도구를
제공한다. Taste는 사용자 기반 및 항목 기반 추천을 지원하며 추천 항목을 만들 수 있는 여러 가지 선택
사항과 사용자가 정의할 수 있는 인터페이스도 함께 제공한다. Taste는 User,
Item 및 Preference와 작동하는 다음과 같은 5가지
주요 구성 요소로 구성되어 있다.
DataModel: User, Item 및 Preference를 위한 저장소
UserSimilarity: 두 사용자 간의 유사성을 정의하는 인터페이스
ItemSimilarity: 두 항목 간의 유사성을 정의하는 인터페이스
Recommender: 추천 항목을 제공하기 위한 인터페이스
UserNeighborhood: Recommender에 의해 사용될 수 있는 유사한 사용자의 인접성을 계산하기 위한 인터페이스
이러한 구성 요소와 해당 구현을 사용하면 실시간 기반 추천 또는 오프라인 추천을 위한 복잡한 추천
시스템을 빌드할 수 있다. 실시간 기반 추천은 주로 수천 명의 사용자만을 처리할 수 있지만 오프라인
추천에서는 훨씬 더 큰 규모로 확장할 수 있다. Taste에는 Hadoop을 활용하여 추천 항목을 오프라인으로
계산할 수 있는 도구도 포함되어 있다. 이는 수많은 사용자, 항목 및 선호도가 포함된 대용량 시스템의
요구 사항을 충족할 수 있는 매우 효과적인 방법이다.
간단한 추천 시스템을 빌드하는 과정을 설명하려면 약간의 사용자, 항목 및 등급이 필요하기 때문에 이 기사에서는
샘플 코드와 함께 소스에 포함된 cf.wikipedia.GenerateRatings의 코드를 사용하여 Wikipedia
문서(Taste-speak의 Item)에 대한 대용량의 User 및 Preference
세트를 무작위로 생성한 후 이 세트에 특정 주제(Abraham Lincoln)에 대해 직접 작성한 등급 세트를 추가하여 샘플에
포함된 최종 recommendations.txt 파일을 작성했다. 이 방법은 CF가 특정 주제에 관심이 있는 사용자를 해당 주제와 관련된
다른 문서로 안내하는 방법을 보여 준다. 예제 데이터에는 컬렉션의 모든 기사에 무작위로 등급을 지정한 990명의 임의 사용자(0
- 989의 레이블 지정)와 컬렉션에서 Abraham Lincoln이라는 문구가 포함된 17편의 기사 중 하나 이상의 기사에 등급을
지정한 10명의 사용자(990 - 999의 레이블 지정)가 있다.
 |
임의 조작 데이터
이 기사의 예제에는 필자가 임의로 조작한 데이터가 들어 있다. 즉, Abraham Lincoln에 대한 정보에
관심이 많은 10명의 실제 사용자를 시뮬레이션하여 필자가 직접 모든 등급을 지정했다. 따라서 데이터
자체와 값보다는 예제에서 설명하고자 하는 기본 개념을 이해하는 데 중점을 두기를 바란다. 실제 데이터가
필요한 경우에는 미네소타 대학의 GroupLens 프로젝트와 Taste 문서에서 필요한 데이터를 얻을 수
있다(참고자료 참조). 필자는 모든 예제에서 단일 데이터를 사용하기 위해
필요한 데이터를 직접 만들었다. |
|
먼저 recommendations.txt에 있는 등급 세트를 기반으로 사용자에 대한 추천 항목을 작성하는
방법을 보여 준다. Taste를 사용하는 대부분의 경우와 마찬가지로 가장 먼저 수행할 단계는 추천
항목이 포함된 데이터를 로드한 후 DataModel에 저장하는 것이다. Taste에는
파일 및 데이터베이스와 함께 작동할 수 있는 DataModel의 여러 다양한
구현이 있다. 이 예제에서는 간단히 FileDataModel 클래스를 사용한다. 이
클래스의 각 행은 사용자 ID, 항목 ID, 선호도의 형식으로 구성되며 여기서, 사용자 ID와 항목 ID는 모두
문자열이고 선호도는 double 형식이다. 모델이 마련된 후에는 UserSimilarity
구현을 선언하여 사용자를 비교하는 방법을 Taste에 알려 주어야 한다. 사용한 UserSimilarity
구현에 따라 사용자에 대한 명시적 설정 없이 선호도를 유추하는 방법도 Taste에 알려 주어야 한다. Listing
1에서는 이러한 모든 단어를 코드에 넣는다. (샘플 코드의 cf.wikipedia.WikipediaTasteUserDemo에
전체 Listing이 들어 있다.)
Listing 1. 모델 작성하기 및 사용자 유사성 정의하기
//create the data model
FileDataModel dataModel = new FileDataModel(new File(recsFile));
UserSimilarity userSimilarity = new PearsonCorrelationSimilarity(dataModel);
// Optional:
userSimilarity.setPreferenceInferrer(new AveragingPreferenceInferrer(dataModel));
|
Listing 1에서는 두 변수 간의 상관 관계를 측정하는 PearsonCorrelationSimilarity를
사용하며 그 외에도 UserSimilarity 측정을 사용할 수 있다. 유사성 측정 방법은
표시되는 데이터 유형 및 테스트에 따라 달라진다. 이러한 데이터에 사용하기 위해 주제를 가장 잘 보여 주는
가장 적합한 테스트의 조합을 찾아냈다. 유사성 측정 방법의 선택에 대한 자세한 정보는 Mahout 웹 사이트(참고자료
참조)에서 확인할 수 있다.
예제를 완성하기 위해 UserNeighborhood와 Recommender를
생성한다. UserNeighborhood는 필자의 사용자와 유사한 사용자를 식별하여 Recommender에
전달하면 Recommender가 등급이 지정된 추천 항목 목록을 작성하는 작업을 수행한다. Listing 2에서는 이를 구현한 코드를 보여 준다.
Listing 2. 추천 항목 생성하기
//Get a neighborhood of users
UserNeighborhood neighborhood =
new NearestNUserNeighborhood(neighborhoodSize, userSimilarity, dataModel);
//Create the recommender
Recommender recommender =
new GenericUserBasedRecommender(dataModel, neighborhood, userSimilarity);
User user = dataModel.getUser(userId);
System.out.println("-----");
System.out.println("User: " + user);
//Print out the users own preferences first
TasteUtils.printPreferences(user, handler.map);
//Get the top 5 recommendations
List<RecommendedItem> recommendations =
recommender.recommend(userId, 5);
TasteUtils.printRecs(recommendations, handler.map);
|
명령행에서 샘플이 있는 디렉토리로 이동한 후 ant user-demo를 실행하여
전체 예제를 실행할 수 있다. 이 명령을 실행하면 Lincoln을 좋아하는 가상 사용자 995에 대한 선호도와
추천 항목이 인쇄된다. Listing 3에서는 ant user-demo를 실행한 출력을 보여 준다.
Listing 3. 사용자 추천 항목 출력
[echo] Getting similar items for user: 995 with a neighborhood of 5
[java] 09/08/20 08:13:51 INFO file.FileDataModel: Creating FileDataModel
for file src/main/resources/recommendations.txt
[java] 09/08/20 08:13:51 INFO file.FileDataModel: Reading file info...
[java] 09/08/20 08:13:51 INFO file.FileDataModel: Processed 100000 lines
[java] 09/08/20 08:13:51 INFO file.FileDataModel: Read lines: 111901
[java] Data Model: Users: 1000 Items: 2284
[java] -----
[java] User: 995
[java] Title: August 21 Rating: 3.930000066757202
[java] Title: April Rating: 2.203000068664551
[java] Title: April 11 Rating: 4.230000019073486
[java] Title: Battle of Gettysburg Rating: 5.0
[java] Title: Abraham Lincoln Rating: 4.739999771118164
[java] Title: History of The Church of Jesus Christ of Latter-day Saints
Rating: 3.430000066757202
[java] Title: Boston Corbett Rating: 2.009999990463257
[java] Title: Atlanta, Georgia Rating: 4.429999828338623
[java] Recommendations:
[java] Doc Id: 50575 Title: April 10 Score: 4.98
[java] Doc Id: 134101348 Title: April 26 Score: 4.860541
[java] Doc Id: 133445748 Title: Folklore of the United States Score: 4.4308662
[java] Doc Id: 1193764 Title: Brigham Young Score: 4.404066
[java] Doc Id: 2417937 Title: Andrew Johnson Score: 4.24178
|
Listing 3의 결과를 보면 시스템에서 다양한 수준의 신뢰도를 가지고 있는 여러 기사를 추천했다는 것을 알 수
있다. 실제로 이러한 각 항목의 등급은 사용자 995가 아닌 Lincoln을 좋아하는 다른 사용자가 부여한 값이다. 다른
사용자에 대한 결과를 보려면 명령행에서 -Duser.id=USER-ID 매개변수를 제공하면
된다. 여기서, USER-ID는 0과 999
사이의 숫자이다. -Dneighbor.size=X의를 전달하여 인접성의 크기를 변경할 수도
있다. 여기서, X는 0보다 큰 정수이다. 실제로 인접성 크기를 10으로
변경하면 임의 사용자가 인접해 있기 때문에 매우 다른 결과가 발생한다. 인접 사용자와 공통 항목을 보려면 명령행에
-Dcommon=true를 추가한다.
사용자 범위에 해당하지 않는 숫자를 입력한 경우에는 NoSuchUserException이
발생한다. 실제로 애플리케이션에서는 새 사용자가 시스템에 들어올 때 수행할 작업을 처리해야 한다. 예를 들어,
가장 인기 있는 10개의 기사, 임의로 선택한 기사 또는 "유사성이 없는" 기사를 표시할 수 있으며 작업을 전혀
수행하지 않을 수도 있다.
앞에서 언급한 대로 사용자 기반 방법은 확장되지 않기 때문에 이 경우에는 항목-항목 기반 방법을 사용하는
것이 효과적이다. 다행스럽게도 Taste에서는 항목-항목 방법을 쉽게 사용할 수 있다. Listing 4에서 볼 수 있듯이
항목-항목 유사성을 작성하여 실행하는 기본 코드는 비교적 단순하다.
Listing 4. 항목-항목 유사성 예제(cf.wikipedia.WikipediaTasteItemItemDemo)
//create the data model
FileDataModel dataModel = new FileDataModel(new File(recsFile));
//Create an ItemSimilarity
ItemSimilarity itemSimilarity = new LogLikelihoodSimilarity(dataModel);
//Create an Item Based Recommender
ItemBasedRecommender recommender =
new GenericItemBasedRecommender(dataModel, itemSimilarity);
//Get the recommendations
List<RecommendedItem> recommendations =
recommender.recommend(userId, 5);
TasteUtils.printRecs(recommendations, handler.map);
|
Listing 1에서와 마찬가지로 추천 사항 파일을 이용해 DataModel을
작성하지만 이번에는 UserSimilarity 인스턴스를 인스턴스화하지 않고 드물게 발생하는 이벤트를
처리하는 LogLikelihoodSimilarity를 사용하여 ItemSimilarity를
작성한다. 그런 다음 ItemSimilarity를 ItemBasedRecommender에 제공하여
추천 항목을 요구한다. 정말 간단한 프로세스이다. 샘플 코드의 이 부분은 ant item-demo 명령을
통해 실행할 수 있다. 이제 이러한 계산을 오프라인으로 수행하도록 시스템을 설정할 수 있으며 다른 ItemSimilarity
측정을 살펴볼 수도 있다. 이 예제의 데이터는 무작위로 구성되어 있기 때문에 추천 사항이 예상과 다를 수 있다는 점에
유의해야 한다. 실제로 일반적인 측정 방법 중에는 데이터가 부족하여 적절한 추천 사항을 제공할 수 없을 때 측정이 종료되는
경우가 많으므로 이러한 무작위성은 테스트 결과를 평가하고 다양한 유사성 측정을 수행하는 데 많은 도움이 된다.
새 사용자 예제를 다시 한번 보면, 사용자가 항목을 탐색한 이후에는 사용자 선호도가 없는 상태에서 수행할 때 발생하는
문제를 훨씬 쉽게 해결할 수 있다. 이 경우 항목-항목 계산을 활용하여 ItemBasedRecommender에
현재 항목과 가장 유사한 항목을 요청할 수 있다. Listing 5에서는 이를 구현한 코드를 보여 준다.
Listing 5. 유사 항목 데모(cf.wikipedia.WikipediaTasteItemRecDemo)
//create the data model
FileDataModel dataModel = new FileDataModel(new File(recsFile));
//Create an ItemSimilarity
ItemSimilarity itemSimilarity = new LogLikelihoodSimilarity(dataModel);
//Create an Item Based Recommender
ItemBasedRecommender recommender =
new GenericItemBasedRecommender(dataModel, itemSimilarity);
//Get the recommendations for the Item
List<RecommendedItem> simItems
= recommender.mostSimilarItems(itemId, numRecs);
TasteUtils.printRecs(simItems, handler.map);
|
명령행에서 ant sim-item-demo를 실행하여 Listing 5를 실행할
수 있다. Listing 4와 Listing 5의 유일한 차이점은 추천 사항 대신
입력 항목과 가장 유사한 항목을 요청한다는 것뿐이다.
지금까지 Taste를 자세히 살펴보았다. Taste에 대한 자세한 정보는 Taste 문서와 mahout-user@lucene.apache.org
메일링 목록(참고자료 참조)에서 확인할 수 있다. 이제 Mahout의 일부 클러스터링 기능을 활용하여
유사한 기사를 찾는 방법에 대해 알아보자.
Mahout으로 클러스터링하기
Mahout에서는 여러 가지 클러스터링 알고리즘 구현을 지원하며, 이러한 구현은 모두 Map-Reduce로
작성되어 있으며 각 구현마다 고유한 목표와 조건이 있다.
- Canopy: 빠른 클러스터링 알고리즘이며 다른 클러스터링 알고리즘의 초기 시드를 작성하는 데도 사용된다.
- k-Means(및 fuzzy k-Means): 이전 반복의 중심 또는 중앙으로부터 항목이 떨어져 있는
거리를 기반으로 항목을 k개의 클러스터로 클러스터링한다.
- Mean-Shift: 클러스터 수를 미리 알 필요가 없으며 임의 형태의 클러스터를 생성할 수 있는 알고리즘이다.
- Dirichlet: 가능성이 있는 여러 모델을 기반으로 하는 클러스터가 제공되므로 특정 클러스터 보기를 조기에 결정하지 않아도 된다는 장점이 있다.
실제로 알고리즘의 이름과 구현은 알고리즘의 결과처럼 중요하지 않다. 여기에서는 이를 염두에 두고 k-Means의
작동 방법만 살펴볼 것이며 나머지 다른 알고리즘은 직접 살펴보기 바란다. 각 알고리즘에는 효과적인 실행을 위해
필요한 고유 요구 사항이 있다는 점에 유의해야 한다.
Mahout을 사용하여 데이터를 클러스터링하는 대략적인 단계는 다음과 같다.
- 입력을 준비한다. 텍스트를 클러스터링하는 경우에는 텍스트를 숫자 표현으로 변환해야 한다.
- Mahout에 있는 여러 Hadoop-ready 드라이버 프로그램 중 하나를 사용하여 선택한 클러스터링 알고리즘을 실행한다.
- 결과를 평가한다.
- 필요한 경우 위 단계를 반복한다.
무엇보다도 먼저 클러스터링 알고리즘을 실행하려면 처리에 적합한 형식으로 구성된 데이터가 있어야
한다. 기계 학습에서는 데이터를 벡터(특징 벡터라고도 함)로 표현하기도 한다. 클러스터링에서
벡터는 데이터를 나타내는 가중치의 배열이다. 이 기사에서는 Wikipedia 문서를 기반으로 생성된 벡터를
사용하여 클러스터링하는 방법을 보여 준다. 하지만 이러한 벡터는 센서 데이터나 사용자 프로파일과 같은
다른 영역의 데이터에서도 생성할 수 있다. Mahout에는 DenseVector와
SparseVector라는 두 가지 Vector 표현이
있다. 좋은 성능을 얻으려면 데이터에 따라 적합한 구현을 선택해야 한다. 일반적으로 텍스트 기반 문제는
밀도가 높지 않으므로 SparseVector를 선택하는 것이 좋지만 대부분의
벡터 값이 0이 아닌 경우에는 DenseVector가 적합하다. 확실하지 않은 경우에는
데이터의 서브세트를 대상으로 두 방법을 모두 시도해서 빠르게 작동하는 방법을 선택하면 된다.
Wikipedia 컨텐츠에서 벡터를 생성하려면 다음을 수행한다.
- 컨텐츠를 Lucene으로 인덱싱하여 벡터를 생성할 필드에 대한 용어 벡터를 저장한다. 이에 대한 내용은
기사의 범위를 벗어나므로 자세히 설명하지는 않겠지만 Lucene에 대한 약간의 참고자료와 함께 간단한 힌트만
제공한다. Lucene에는 Wikipedia 파일 덤프를 읽고서 Lucene에서 인덱싱할 문서를 생성할 수 있는
EnWikiDocMaker
클래스(Lucene의 contrib/benchmark 패키지)가 있다.
- Mahout의
utils 모듈에 있는 org.apache.mahout.utils.vectors.lucene.Driver
클래스를 사용하여 Lucene 인덱스로부터 벡터를 작성한다. 이 드라이버 프로그램에는 벡터 작성과 관련된 여러
옵션이 있다. Mahout wiki 페이지 Creating Vectors from Text에서 이에 대한 자세한 정보를 볼 수 있다(참고자료 참조).
이러한 두 단계를 실행하면 Mahout 시작하기 섹션에서 다운로드한
n2.tar.gz와 같은 파일이 생성된다. 이 n2.tar.gz 파일은 앞에서 ant install
메소드를 실행하여 자동으로 다운로드된 Wikipedia "청크" 파일에 있는 모든 문서를 인덱싱하여 생성된
벡터로 구성되어 있다. 이러한 벡터는 유클리드 정규(또는 L2 정규, 참고자료
참조)를 사용하여 정규화된다. Mahout에서는 다양한 방법으로 벡터를 작성하여 최상의 결과를 얻을 수
있는 벡터를 찾을 수 있다.
 |
결과 평가하기
클러스터 결과는 다양한 방법으로 평가할 수 있다. 많은 사람들은 단순히 수동 검사 및 임시 테스트부터
시작하지만 만족스러운 결과를 얻으려면 여러 판단 기준을 사용하는 골드 스탠다드(gold standard)를
개발하는 것과 같은 심도 깊은 평가 기술을 사용할 필요가 있다. 결과를 평가하는 방법에 대한 자세한
정보는 참고자료에서 확인할 수 있다. 이 기사의 예제에서는 수동 검사를
통해 클러스터링된 일부 결과가 실제로 유효한지 여부를 확인했다. 하지만 실제 프로덕션 과정이었다면 훨씬
더 엄격한 프로세스를 적용했을 것이다.
|
|
벡터 섹터가 마련되었으므로 이제 k-Means 클러스터링 알고리즘을 실행할 단계이다. Mahout에는 k-Means 알고리즘을 위한
KMeansDriver를 비롯하여 모든 클러스터링 알고리즘을 위한 드라이버 프로그램이 있다. ant
k-means를 실행해 보면 알 수 있듯이 이 드라이버는 Hadoop을 사용하지 않고 독립형 프로그램을 실행하는 것처럼
쉽게 사용할 수 있다. build.xml에서 Ant k-means 대상을 보면 KMeansDriver의 인수에 대한
자세한 정보를 확인할 수 있다. 프로세스가 완료되면 ant dump 명령을 사용하여 결과를 출력할
수 있다.
독립형 모드에서 성공적으로 실행한 후에는 Hadoop의 분산 모드에서도 실행할 수 있다. 이를 위해서는
샘플 코드의 hadoop 디렉토리에 있는 Mahout Job JAR가 필요하다. 이 Job JAR에는 Hadoop에 쉽게 로드할
수 있도록 모든 패키지와 종속성이 단일 JAR 파일로 패키징되어 있다. 또한 Hadoop 0.20을 다운로드하여
Hadoop 튜토리얼의 지침에 따라 가상 분산 모드(즉, 한 항목으로 구성된 클러스터)에서 먼저 실행해 본 후 전체
분산 모드에서 실행한다. 자세한 정보는 Hadoop 웹 사이트 및 리소스와 IBM 클라우드 컴퓨팅 리소스(참고자료
참조)에서 확인할 수 있다.
Mahout으로 컨텐츠 분류하기
Mahout에서는 현재 베이즈 통계를 기반으로 컨텐츠를 분류하는 두 가지 관련 방법을
지원한다. 첫 번째 방법은 간단한 Map-Reduce 사용 Naive Bayes 분류기이다. Naive Bayes
분류기는 데이터에 대한 매우 단순한 가정(잘못된 가정일 수도 있음)이 완전히 독립되어
있음에도 불구하고 빠르면서도 상당히 정확한 것으로 알려져 있다. Naive Bayes 분류기는
클래스별 교육용 예제의 크기가 각기 다르거나 데이터의 개별성이 낮은 경우 중단되기도
한다. 두 번째 방법은 Complementary Naive Bayes라고 하며 단순성과
속도를 유지하는 동시에 Naive Bayes 방법과 관련된 일부 문제를 해결하려고 시도한다. 하지만
이 기사에서는 Naive Bayes 방법만 살펴본다. 왜냐하면 이 방법만으로도 Mahout의 전반적인
문제와 입력을 확인할 수 있기 때문이다.
간단히 말해서 Naive Bayes 분류기는 두 부분으로 구성된 프로세스로 특정 문서 및 범주와 연관된
특징(단어)을 추적하는 부분과 이 정보를 이용하여 보이지 않는 새 컨텐츠의 범주를 예측하는 부분으로
구성되어 있다. 훈련이라고 하는 첫 번째 단계에서는 이미 분류된 컨텐츠의 예제를 검사하여
모델을 만든 다음 각 단어가 특정 컨텐츠와 연관된 가능성을 추적한다. 분류라고 하는 두 번째
단계에서는 훈련 중에 작성된 모델과 보이지 않는 새 문서의 컨텐츠를 베이즈 이론과 함께 사용하여
전달된 문서의 범주를 예측한다. 따라서 Mahout의 분류기를 실행하려면 먼저 모델을 훈련한 다음 이
모델을 사용하여 새 컨텐츠를 분류해야 한다. 다음 섹션에서는 Wikipedia 데이터 세트를 사용하여 이
작업을 수행하는 방법을 보여 준다.
Naive Bayes 분류기 실행하기
훈련기와 분류기를 실행하려면 먼저 훈련할 문서 세트와 테스트할 문서 세트를 설정하는 간단한 준비
작업을 수행해야 한다. ant prepare-docs를 실행하여 Wikipedia 파일(install
대상을 통해 다운로드한 파일)을 준비할 수 있다. 이렇게 하면 Mahout 예제에 포함된 WikipediaDatasetCreatorDriver
클래스를 사용하여 Wikipedia 입력 파일이 분할된다. 문서는 관심 범주 중 하나와 일치하는 범주가 있는지
여부에 따라 분할된다. 유효한 Wikipedia 범주(또는 Wikipedia 범주의 하위 문자열)라면 관심 범주가 될
수 있다. 예를 들어, 이 예제에는 Science와 History라는 두 범주가 있다. 따라서 science나 history라는
단어가 포함된(정확히 일치하지 않아도 됨) 범주가 있는 모든 Wikipedia 범주가 해당 범주의 다른 문서와 함께
버켓에 저장된다. 또한 각 문서에는 토큰이 지정되고 정규화되면서 구두점, Wikipedia 마크업 및 이 작업에 필요하지
않는 기타 기능이 제거된다. 최종 결과는 범주 이름이 레이블로 지정된 단일 파일에 한 줄에 한 문서씩 저장된다.
이는 Mahout에서 기대하는 입력 형식이다. 이와 마찬가지로 ant prepare-test-docs를
실행하면 테스트 문서에 대해 동일한 작업이 수행된다. 테스트 및 훈련 문서는 겹치지 않아야 하며, 겹칠 경우
왜곡된 결과가 발생할 수 있다. 이론적으로는 훈련 문서를 테스트에 사용하면 완벽한 결과가 발생해야 하지만
실제 환경에서는 그렇지 않을 가능성이 높다.
훈련 및 테스트 세트를 설정했으므로 이제 ant train 대상을 통해 TrainClassifier
클래스를 실행할 차례이다. 이 작업을 수행하면 Mahout 및 Hadoop에서 대량의 로깅 정보가 생성된다. 앞의
작업이 완료되면 ant test가 샘플 테스트 문서를 가져온 후 훈련 중에 작성된
모델을 사용하여 문서를 분류한다. Mahout에서 이러한 테스트의 출력은 혼동 행렬이라는 데이터
구조이다. 혼동 행렬은 각 범주에 대해 올바르게 분류된 결과의 수와 잘못 분류된 결과의 수를 설명한다.
위 내용을 요약하자면 다음과 같은 단계를 실행하여 분류 결과를 생성할 수 있다.
ant prepare-docs
ant prepare-test-docs
ant train
ant test
이러한 단계를 모두 실행하면(Ant 대상 classifier-example은 하나의 호출로
모든 결과를 캡처함) Listing 6과 같은 요약 및 혼동 행렬이 생성된다.
Listing 6. history 및 science에 대한 Bayes 분류기의 실행 결과
[java] 09/07/22 18:10:45 INFO bayes.TestClassifier: history
95.458984375 3910/4096.0
[java] 09/07/22 18:10:46 INFO bayes.TestClassifier: science
15.554072096128172 233/1498.0
[java] 09/07/22 18:10:46 INFO bayes.TestClassifier: =================
[java] Summary
[java] -------------------------------------------------------
[java] Correctly Classified Instances : 4143
74.0615%
[java] Incorrectly Classified Instances : 1451
25.9385%
[java] Total Classified Instances : 5594
[java]
[java] =======================================================
[java] Confusion Matrix
[java] -------------------------------------------------------
[java] a b <--Classified as
[java] 3910 186 | 4096 a = history
[java] 1265 233 | 1498 b = science
[java] Default Category: unknown: 2
|
중간 프로세스의 결과는 기본 디렉토리의 wikipedia 디렉토리에 저장된다.
이제 결과 세트를 보면서 어떤 결과가 발생했는지 살펴보자. 요약 결과를 보면 약 75%가
올바르게 분류되었고 25%가 잘못 분류되었음을 알 수 있다. 얼핏 보아 이 결과는 무작위로
추측했을 때보다는 좋기 때문에 상당히 좋은 결과라고 할 수 있다. 하지만 좀 더 자세히
살펴보면 History 예측의 경우 매우 좋은 약 95%의 정확도를 보여 주지만 Science 예측의
경우에는 기대 이하의 수준인 약 15%의 정확도를 보여 준다. 이러한 결과가 나타난 이유를
찾기 위해 훈련을 위한 입력 파일을 간단히 보면 Science보다 History의 예제가 훨씬 더
많다(파일 크기가 거의 두 배임)는 것을 알 수 있으며 이는 잠재적으로 문제가 될 수 있다.
테스트를 위해 -Dverbose=true 옵션을 ant test에
추가할 수 있다. 이렇게 하면 각 테스트 입력에 대한 정보와 올바르게 레이블이 지정되었는지 여부가 표시된다. 이
출력을 자세히 살펴보면 문서를 조사하여 잘못 분류된 이유를 알아낼 수 있는 단서를 찾아낼 수 있다. 또한
다양한 입력 매개변수와 추가 Science 데이터를 사용하여 모델을 다시 훈련하면서 향상된 결과를 얻을 수 있는지
확인할 수 있다.
모델 훈련을 위해 선택할 특징도 충분히 고려해야 한다. 이 기사의 예제에서는 Apache Lucene의
WikipediaTokenizer를 사용하여 원래 문서에 토큰을 지정했지만 토큰이
잘못 지정되었을 수도 있는 공통 용어나 정크 용어를 제거하는 작업은 수행하지 않았다. 필자가 이
분류기를 프로덕션에서 사용했다면 성능을 최대한 끌어올리기 위해 입력 및 기타 설정을 훨씬 더 자세히
조사했을 것이다.
Science 결과가 요행이 아니었는지 확인하기 위해 필자는 다른 범주 세트 즉, Republicans와 Democrats도
실행해 보았다. 이 경우 필자는 새 문서가 Republicans 또는 Democrats에 대한 것인지 여부를 예측하려고
한다. 필자는 사용자가 직접 실행해 볼 수 있도록 src/test/resources에 repubs-dems.txt를 작성했다. 그리고
나서 다음과 같은 명령을 통해 분류 단계를 실행했다.
ant classifier-example -Dcategories.file=./src/test/resources/repubs-dems.txt -Dcat.dir=rd |
두 개의 -D 값은 단순히 범주 파일과 중간 결과를 저장할 wikipedia
디렉토리 아래의 디렉토리의 이름을 가리킨다. Listing 7에서는 이 실행의 결과로 생성된 요약 및 혼동
행렬을 보여 준다.
Listing 7. Republicans 및 Democrats에 대한 Bayes 분류기의 실행 결과
[java] 09/07/23 17:06:38 INFO bayes.TestClassifier: --------------
[java] 09/07/23 17:06:38 INFO bayes.TestClassifier: Testing:
wikipedia/rd/prepared-test/democrats.txt
[java] 09/07/23 17:06:38 INFO bayes.TestClassifier: democrats 70.0
21/30.0
[java] 09/07/23 17:06:38 INFO bayes.TestClassifier: --------------
[java] 09/07/23 17:06:38 INFO bayes.TestClassifier: Testing:
wikipedia/rd/prepared-test/republicans.txt
[java] 09/07/23 17:06:38 INFO bayes.TestClassifier: republicans 81.3953488372093
35/43.0
[java] 09/07/23 17:06:38 INFO bayes.TestClassifier:
[java] Summary
[java] -------------------------------------------------------
[java] Correctly Classified Instances : 56 76.7123%
[java] Incorrectly Classified Instances : 17 23.2877%
[java] Total Classified Instances : 73
[java]
[java] =======================================================
[java] Confusion Matrix
[java] -------------------------------------------------------
[java] a b <--Classified as
[java] 21 9 | 30 a = democrats
[java] 8 35 | 43 b = republicans
[java] Default Category: unknown: 2
|
최종 결과는 이전 예제의 결과와 마찬가지로 정확도를 보여 주지만 좀 더 효과적으로
두 범주 중 하나를 결정했다는 것을 알 수 있다. 입력 문서가 포함된 wikipedia/rd/prepared
디렉토리를 간단히 살펴보면 훈련 예제의 관점에서 두 훈련 파일의 균형이 훨씬 더 잘 잡혀 있다는
것을 알 수 있다. History 또는 Science 훈련 세트에 비해 각 파일의 크기가 훨씬 작기 때문에
History/Science 실행에 비해 전체적으로 예제의 수가 적다는 것도 알 수 있다. 전반적으로 결과의
균형이 현저하게 향상되었다. 훈련 세트의 규모가 더 크면 Republicans와 Democrats의 차이가 더
커질 수 있으며 이는 곧, 그렇지 않다고 하더라도 한쪽 그룹이 Wikipedia의 메시지에 관심이 더 많다는
것을 의미할 수 있다. 하지만 이에 대한 결정은 정치 전문가에게 맡겨 두겠다.
지금까지 독립형 모드에서 분류를 실행하는 방법을 살펴보았으므로 다음 단계에서는 클라우드에
대한 코드를 Hadoop 클러스터에서 실행할 수 있다. 클러스터링 코드 작업을 수행할 때와 마찬가지로 Mahout
Job JAR가 필요하다. 그리고 앞에서 설명한 모든 알고리즘은 Map-Reduce에서 사용할 수 있으며 Hadoop
튜토리얼에서 설명한 작업 제출 프로세스에서 실행할 때 작동해야 한다.
Mahout의 미래
Apache Mahout은 단 1년 만에 클러스터링, 분류 및 CF를 위한 중요한 기능을 통해 큰 성과를 거두기는 했지만
아직까지도 성장할 여지가 많이 남아 있다. 가까운 장래에 분류에 대한 무작위 결정 포레스트, 연관 규칙,
문서 내 항목 식별을 위한 LDA(Latent Dirichlet Allocation), HBase 및 기타 보조 저장소 옵션을 사용한
추가 분류 옵션 등과 관련된 Map-Reduce 구현을 볼 수 있을 것이다. 이러한 새 구현 외에도 데모, 문서 및
버그 수정도 개선될 것이다.
마지막으로 실제 mahout이 코끼리의 힘과 능력을 활용하는 것처럼 Apache Mahout을 사용하면
노란색 코끼리 즉, Apache Hadoop의 장점과 기능을 효율적으로 활용할 수 있다. 나중에 컨텐츠를
클러스터링, 분류 또는 추천할 필요가 있으면 특히, 대량의 컨텐츠의 경우 Apache Mahout을 고려하자.
감사의 인사
이 기사를 검토하고 소중한 의견을 제안해 준 동료 Mahout Committer인 Ted Dunning과 Sean Owen에게 감사의 뜻을 전한다.
다운로드 하십시오 | 설명 | 이름 | 크기 | 다운로드 방식 |
|---|
| Sample code | j-mahout.zip | 90MB | HTTP |
|---|
참고자료 교육
제품 및 기술 얻기
토론
필자소개  | 
|  | Grant Ingersoll은 Lucid Imagination의 기술팀을 결성한 장본인인 동시에 기술팀의 일원이기도
하다. 그의 프로그래밍 관심 분야는 정보 검색, 기계 학습, 텍스트 분류, 추출 등이다. Apache Mahout
기계 학습 프로젝트의 공동 설립자일 뿐만 아니라 Apache Lucene 및 Apache Solr 프로젝트에서 Committer
및 연사로서도 활약하고 있다. 자연 언어 처리를 위한 오픈 소스 도구에 대해 설명하는 Taming Text(Manning,
근간)의 공동 저자이기도 하다. |
기사에 대한 평가
 |
| 이 문서 북마킹 하기
|
|