Showing posts with label Mahout. Show all posts
Showing posts with label Mahout. Show all posts

Wednesday, November 7, 2012

Belief Propagation for music recommendations with Mapreudce And Giraph




I am taking Probabilistic Graphical Models course from Coursera. once class covers inference on markov random field using label propagation, I wanted to see how this algorithm works on real data so applied label propagation on million song dataset to feel it.

here is example of label propagation.

simple arithmetic result to following.

50% from my profile + 40% from X +10% from Z = 60% rock, 40% jazz.


Generalization


Here is actual formulation. here markov random field is represented by factor graph.



Above formulation can be implemented by matrix multiplication using Map/Reduce.

definition


Lets say graph structure as G, and set of Concept C = {c1, c2,...} where each ci is consist of set of vertices in G. ci represent prior per concept i. then we want to calculate pair-wise posterior given G, C.

implementation


iteration 0: CV(|C| x |V|) x G(|V| x |V|) = CV'(|C| x |V|)
iteration 1: CV'(|C| x |V|) x G(|V| x |V|) = CV''(|C| x |V|)
..
..

each iteration is simply matrix multiplication between Concept-Vertices CV matrix and static graph structure G.

This operation quickly becomes huge. so I first implemented this as DistributedRowMatrix in Mahout.

DistributedRowMatrix class provide following APIs.
1. tranpose: this.tranpose()
2. times(DistributedRowMatrix other): this.transpose().times(other)

using above API, label propagation in Map/Reduce becomes following.

1. init Concept-Vertices matrix CV.
2. normalize Graph for convenience if G is not normalized.
3. create CV, G using DistributedRowMatrix class.
4. for # iteration CV = CV x G

Following code demonstrate how DistributedRowMatrix in mahout library becomes handy.

1:    DistributedRowMatrix CV =   
2:    createInitialCV(numItems, getTempPath("initial.class"),   
3:                    getConf());  
4:    for (int i = startIteration; i < iterations; i++) {  
5:     log.info("current iteration: {}", iteration);  
6:     /*  
7:       GNorm is DistributedRowMatrix   
8:       contain vertex-vertex graph structure.  
9:       DistributedRowMatrix.times calculate  
10:      this.transpose().times(other). so transpose itself first.  
11:      */  
12:     CV = CV.transpose().times(GNorm)  
13:    }  


Note that using Map/Reduce for iterative job is inefficient, so why not try Giraph?

in Graph-Parallel environment, problems become following.

1. each vertex has it`s neighbor edges in G.
2. at superstep 0, some vertex has C vector as value([ci:prior, cj:prior...]). if vertex has C vector, then send C to all of it`s neighbors otherwise don`t send it.
3. after superstep 0, all vertex get messages([vertex_id j, C vector]) from each of it`s neighbors.
if current vertex is Vi, and message is [Vj, Cvj] then edge(Vi, Vj) / Vi`s all Edge sum * C is added to Vi`s value Cvi vector. merge all concept-prior vectors sent to each vertex and update value(Cvi).
4. if iteration is not done, send value(Cvi) to all neighbors.

Following code is compute method in VertexProgram to implement above.


@Override  
  public void compute(Iterable<MultiLabelVectorWritable> messages) throws IOException {  
   /*  
    * each vertex has Vector as value.   
    * this Vector consist of [concept_id:probability,....]  
    * MultiLabelVectorWritable to represent   
    * (vertex j which sent this message, vertex j`s value Vector)   
    */  
   long step = getSuperstep();  
   if (step < iteration) {  
    // we still need to compute on this vertex.  
    Vector currentVector = getValue().get();  
    // create new messages from this vertex.  
    MultiLabelVectorWritable newMessage = new MultiLabelVectorWritable();  
    // set message source to this vertex.  
    newMessage.setLabels(new int[]{(int)getId().get()});  
    // vertex value vector [concept_id:probability] is sparse.  
    Vector newMessageVector = new RandomAccessSparseVector(minNonConceptVertexId);  
    // iterate messages sent to this vertex and merge them up to build this vertex`s vector.  
    for (MultiLabelVectorWritable message : messages) {  
     int messageId = message.getLabels()[0];  
     Vector conceptProbs = message.getVector();  
     float weight = getEdgeValue(new LongWritable(messageId)).get();  
     Iterator<Vector.Element> probs = conceptProbs.iterateNonZero();  
     while (probs.hasNext()) {  
      Vector.Element prob = probs.next();  
      int conceptId = prob.index();  
      currentVector.set(conceptId, prob.get() * weight);  
     }  
    }  
    // prunning for absorb  
    Iterator<Vector.Element> iter = currentVector.iterateNonZero();  
    while (iter.hasNext()) {  
     Vector.Element e = iter.next();  
     if (e.get() < gamma) {  
      continue;  
     }  
     newMessageVector.setQuick(e.index(), e.get());  
    }  
    newMessage.setVector(newMessageVector);  
    sendMessageToAllEdges(newMessage);  
   } else {  
    voteToHalt();  
   }  
  }  


I implemented demo using label propagation with open dataset from million song dataset challenge from Kaggle. This demo load taste profile graph data into memory and calculate on the fly rather than using Giraph for demonstration. here is github for Giraph/Mahout code and demo codes.

TODO: since test set for this data is opend(competition is over), I will measure truncated mean average precision to evaluation label propagation.



Wednesday, October 17, 2012

movie recommendation demo with matrix factorization

I was experimenting with Graphlab and Mahout for Matrix Factorization these days.

Matrix factorization transform both items and users to the same latent factor space so they can be compared directly.

Even though Mahout and Graphlab is great tool for matrix factorization, these are designed for batch process. to get recommendations for new users who rate existing movies in rate matrix, following two steps are necessary.

1) transform user-rating vector to user-latent feature vector.
2) compare all movie-latent feature vectors with 1) and calculate scores.


this demo ask user to rate movies and do 1), 2) step.

most of work is just glue codes from Mahout with Jetty. 

check out this and feel free to give me any feedback.

Update: added label propagation to find serendipities. since the training data is small enough(1.7 million user, 40 K movies,  19 million edge), just load training data into memory.

Todo: I will update with evaluation metric(RMSE, MAP, Precision-Recall) after running batch jobs for this dataset using mahout/Graphlab for ALS, Giraph for label propagation.
also, add item-based cf as baseline to compare result

Sunday, January 8, 2012

ALS-WR

Alternating-Least-Squares with Weighted-lambda-Regularization(ALS-WR)

Purpose

ALS-WR 는 model-based 방식으로 원래 matrix R을 iterate하면서 U, M 두개의 matrix로 factorize한다.
R = (user, item, rate)형식의 matrix로 U X I의 사이즈를 가진다.
원래는 R안에 대부분의 R(i, j)는 다 비어 있다. Collaborative Filtering의 목적은 이 비어 있는 R(i,j)들을 어텋게 예측 할 거 인가이다.

Background

CF는 크게 3가지로 나뉜다.
방식장점단점
memory-based결과이해 쉬움, 구현 쉬움, centent frer사람이 매기는 rating에 섞인 noise에 대처 미흡, spare한 data에선 performance down, new user/new item에 no result, not very scalable.
model-basedrate자체보다는 training set의 pattern이용, sparse data에 비교적 강함, 직관적 결과이해model building에 비용 큼, scalability 와 performance간의 trade off, reduction model때문에 원래 useful data can be lost
hybridmemory-based + model-based비용큼

Introduction

시작하기 전에 matrix factorization에 대한 설명(?정확히는 svd개념설명)으로 링크를 달아 놓았다.
ALS-WR는 model-based방식으로써 원래 rating matrix R(user x item)을 least squared error 를 최소로 하는 U(user x hidden feature), M(item x hidden feature)로 factorize하는 알고리즘이다.
말은 거창한데, 결국 어떤 cost function F(U, M) = square error part + regularization part 을 최소로 하는 U, M을 찾는 문제이다.
자세한 식은 논문을 참조 하고, 이해한 대로만 의견을 추가하겠다.
앞에 square error part는 (실제 rate R(i, j) - 예측 rate R^(i, j))를 error라고 정의하고, 이 error^2들의 합을 의미한다.
Regularization은 machine learning 분야에서 자주 사용 되는 용어로써, model이 overfit하는 것을 방지하는데 도움을 주는 방식이다.
overfit이 모냐
위의 식에서 전체 cost는 각각의 data들과 예측 곡선 y = ax + b와의 거리들의 제곱의 합이다. 그럼 위의 optimization problem에서 cost 를 최소화 하려면 1차식이 아닌 y = ax^50 + bx^49.... 식의 높은 차원식(곡선이 될것이다?) 을 사용하면 될것이다. 문제는 굉장히 복잡한 식을 써서 training set에서의 cost를 최소로 만들어 봐야 training set을 잘 대변 할 수 있지는 않다. training set을 잘 대변 하는 식을 찾아야 test set에서도 잘 작동하는데, training에 특화된 높은 차원식은 보통 test set에서는 높은 error를 보인다. 이를 이 높은 차수의 식은 overfit되었다고 한다.
다시 말해 cost를 최소로 하는 U, M을 찾는것이 문제이고, 이때 overfit을 방지하기 위해 regularization part를 추가 한 것이다.

algorithm(아주간략)

ALS-WR는 M을 고정하고, U를 optimize하고, U를 고정하고 M을 optimize하는 일을 한 번의 iteration으로 한다.
처음 M은 평균 rating들에서 작은 random number만큼의 차이가 나는 임의의 rate로 initialize되고 iteration을 거치면서 M과 U를 optimize한다.

Recommendation

결국 최종적으로 R^(i, j)를 어텋게 예측 하느냐는 다음과 같다.
ALS-WR를 통해서 R => U, M으로 factorize된다. 다른말로 U는 user를 hidden feature space로, M은 item을 hidden feature space로 project하여 hidden feature space에서의 similarity를 측정하여
user의 item에 예상 R^(i, j)를 계산 하게 된다.
R' = U x transpose(M)이 된다.

Test

mahout-0.6-snapshot을 이용하여 test 해보았다. Cluster는 8 core, 16G서버 10대

Iteration #Hidden Feature #RMSERunning time
20300.91670810377906462 hour