Mahoutにパッチを送ってみた

      @issay
自己紹介
• 吉田一星(よしだ いっせい)
• @issay
• 某企業で検索やHadoopに関する仕事をし
  てます
今日は皆既月食
• 11年振りの好条件、次のチャンスは2030
  年1月31日らしい
 – 部分食始まり: 21時54分
 – 皆既食始まり: 23時5分
 – 皆既食最大: 23時31分
 – 皆既食終わり: 23時58分
流れ
• 実装寄りの勉強会ということなの
  で・・・
• Mahoutがどのようにアルゴリズムを
  MapReduceで実装しているかをひたすら解
  説
• 教師あり学習のみ
• 送ったパッチについて紹介
機械学習の並列分散
• 大規模データを扱うために機械学習の並
  列分散は不可欠になりつつある
• 機械学習を代表する学会、NIPSにはBig
  Learning Workshopがある
• 本も発売予定
Mahoutとは?
• Hadoopを使った機械学習ライブラリ
• MapReduceでの並列分散が主目的
 – すべての手法がMapReduceでの分散に対応し
   ているわけではない
• コードの品質はバラツキがある
• ドキュメントが未整備なものが多く、
  コードを読みながら使うのが前提
実装している手法
• リコメンデーション(協調フィルタリン
  グ)
 – ユーザベース
 – アイテムベース
 – オンラインとオフラインの両方可能
 – 様々な類似度をサポートしている
  • Cosine, LLR, Tanimoto, Pearson…
実装している手法
• クラスタリング
 –   K-means / Fuzzy K-means
 –   階層クラスタリング
 –   Canopyクラスタリング
 –   EM アルゴリズム
 –   Mean shift
 –   ディリクレ過程
 –   Latent Dirichlet Allocation
 –   スペクトラルクラスタリング
 –   LSH(Minhash)
実装している手法
• 分類
 – Naïve Bayes / Complemetaly Naïve Bayes
 – ランダムフォレスト
 – 確率的勾配降下法(SGD)
   • ロジスティック回帰
   • Passive Aggressive
 – SGDはMapReduceでの分散に対応していない
   • パッチを送ってみたよ
MapReduce
MapReduceの処理フロー


入力                     出力




       Map    Reduce
MapReduce
例:Tweetの単語をカウントする
以上Twitter高負荷試験終了!
はいはい、バルスバルス
メガ!メガ!
いやー、クジラさん見えた
めがぁぁぁぁぁぁぁぁ
目がぁ
目がー
ばるす
ぬるぽ
バルス!!
バルス!
ヴァルス!!!!!!!!!!!!!!!!!
きたあああああああああああああああああ
バロス
バルス
バルス!!!!!!!!!!
ふたりとも成長しすぎ
30秒前 バズー「僕の左手に手を乗せて」
MapReduce: Mapフェーズ
以上Twitter高負荷試験終了!
はいはい、バルスバルス
メガ!メガ!
いやー、クジラさん見えた          key   <バルス, 1>   value
めがぁぁぁぁぁぁぁぁ
目がぁ                         <バルス, 1>
目がー
ばるす                         <バルス, 1>
ぬるぽ
バルス!!
                            <ぬるぽ, 1>
バルス!
ヴァルス!!!!!!!!!!!!
きたああああああああああああああ            <バルス, 1>
バロス
バルス
バルス!!!!!!!!!!
ふたりとも成長しすぎ
30秒前バズー「僕の左手に手を乗せて」
MapReduce: Shuffleフェーズ
以上Twitter高負荷試験終了!            <ぬるぽ, 1>
はいはい、バルスバルス
メガ!メガ!                       <バルス, 1>
いやー、クジラさん見えた
めがぁぁぁぁぁぁぁぁ
目がぁ                          <バルス, 1>
目がー               Key順にソート
ばるす                          <バルス, 1>
ぬるぽ
バルス!!                        <バルス, 1>
バルス!
ヴァルス!!!!!!!!!!!!             <バルス, 1>
きたああああああああああああああ
バロス                          <バルス, 1>
バルス
バルス!!!!!!!!!!                <バルス, 1>
ふたりとも成長しすぎ
30秒前バズー「僕の左手に手を乗せて」          <バルス, 1>
MapReduce: Shuffleフェーズ
以上Twitter高負荷試験終了!
はいはい、バルスバルス
メガ!メガ!
いやー、クジラさん見えた                   <ぬるぽ, 1>
めがぁぁぁぁぁぁぁぁ
目がぁ
目がー
ばるす
                      <バルス, [1,1,1,1,1,1,1,1,1,…]>
ぬるぽ
バルス!!
バルス!
ヴァルス!!!!!!!!!!!!
きたああああああああああああああ
バロス
                               Keyごとにまと
バルス
                                   める
バルス!!!!!!!!!!
ふたりとも成長しすぎ
30秒前バズー「僕の左手に手を乗せて」
MapReduce: Reduceフェーズ
以上Twitter高負荷試験終了!
はいはい、バルスバルス
メガ!メガ!
いやー、クジラさん見えた
めがぁぁぁぁぁぁぁぁ
目がぁ                   バルス    14594
目がー
ばるす                   ぬるぽ    1
ぬるぽ
バルス!!
バルス!
ヴァルス!!!!!!!!!!!!
きたああああああああああああああ
バロス
バルス                    単語のカウント
バルス!!!!!!!!!!          を足しあわせて
ふたりとも成長しすぎ              結果を出力
30秒前バズー「僕の左手に手を乗せて」
Shuffleって何?
• Shuffleで行われること
 – ソートする
 – 同じキーをまとめる
• Shuffleは抽象的概念?
• Mapタスクと、Reduceタスクで分散ソート
  を行なっているだけ
キーのハッ
 シュ値(デ
フォルト)で            分散ソート
各Reduceタス
クごとに分割                それぞれキーを
                      ソートしReduceタ
                       スクに転送




入力                                      出力




                   Reduceタスク
                    では複数の
            Map     ソート済み      Reduce
                    ファイルの
Mapタスク内のソート
• バッファサイズを超えたら、バッファに
  たまったデータのキーをソートしてファ
  イルに書きだす
• 最後に書きだしたファイルをマージし
  て、Reduceタスクに転送する
というわけで本題
ナイーブベイズ
ナイーブベイズとは?
• 単語wのカテゴリcにおける確率
                 カテゴリcの単語wの頻度の合計
              カテゴリcのすべての単語の頻度の合計


• 文書dのカテゴリcにおける確率
 – 文書dに出てくる単語wの確率の積と定義
  0.07 × 0.1 × 0.01… = 0.000025

• 確率が最も高いカテゴリに分類すればよ
  い
• 単語の相関関係や語順は考慮していない
ナイーブベイズとは?
• 文書dのカテゴリcにおける確率はP(d|c)
 – カテゴリcが与えられた上での文書dの確率
• ほんとはP(c|d)を求めたい
 – 文書dが与えられた上でそれがカテゴリcであ
   る確率
                          カテゴリcの文書数
• そこでベイズの定理               すべての文書数

          P(d|c) P(c)
 P(c|d) =
             P(d)       文書dを分類すると
                        きには考慮しなく
                           ていい
アンダーフロー対策
• 確率を掛け合わせるとすぐにアンダーフ
  ローになってしまう
 0.07 × 0.1 × 0.01…
• Logをとって足し合わせればOK
 Log(0.07) + Log(0.1) + Log(0.01)
ゼロ頻度問題
• 単語の確率が一つでもゼロになると文書
  全体の確率もゼロになってしまう
 0.07 × 0.1 × 0.01 × 0 … = 0
• 単語wがクラスcに出現しないからといっ
  て確率がゼロなわけではない
• スムージングを行う
            カテゴリcの単語wの頻度の合計 + 1
  カテゴリcのすべての単語の頻度の合計 + すべての単語の種類数
さらなる実装の工夫
• Mahoutでは、以下の論文に書かれている
  実装の工夫を行っている
      Tackling the Poor Assumptions of Naive Bayes Text
        Classifiers (2003)
•   単語頻度(TF)の変換
•   文書頻度の逆数(IDF)を掛ける
•   文書長正規化
•   Competent Naïve Bayes
•   カテゴリ正規化
単語頻度(TF)の変換
• 頻出語の影響を減らすため以下の正規化
  を行う
 log(文書dの単語wの頻度 + 1)
文書頻度の逆数(IDF)を掛ける
• 単語のレア度を考慮するため、文書頻度
  の逆数(IDF)を掛ける
 – ある文書にだけ多くその単語が出現するな
   ら、スコアを上げる

                             すべての文書数
log (文書dの単語wの頻度 + 1) × log
                             単語wを含む文書数
文書長正規化
• 文書の長さによってばらつきが出ないよ
  うに正規化を行う

       文書dにおける単語wの重み

    文書dにおける単語の重み全ての二乗の合計
Complement Naïve Bayes
• カテゴリごとの文書数にばらつきがあると精度が
  出ない
• カテゴリcに含まれない文書で学習すればよい
 – カテゴリc以外の文書ならば、数のばらつきが少ない
• カテゴリc以外の全てのカテゴリでの単語wの確率
   全体での単語wの重み合計 - カテゴリcの単語wの重み合計
 全体での全ての単語の重みの合計 - カテゴリcの単語全ての重みの合計

• 確率が最も低いカテゴリに分類すればいい
 – カテゴリc以外での確率が最も低い=カテゴリcでの確
   率が最も高い
カテゴリ正規化
• 最後にカテゴリごとにばらつきが出ない
  ように、カテゴリごとに単語の確率の合
  計で正規化する
• 学習ではなく分類の時に正規化する

                         カテゴリc以外のす
                         べてのカテゴリの
                            確率

       カテゴリcの単語wの確率

     カテゴリcの全ての単語の確率の合計
Mahoutでの実装
• 基本的にはカウント問題
 – Mapper
   • 何かのKeyと、それに対する数値をValueで出力
 – Reducer
   • Valueをすべて足し合わせて出力
• カウントしなければならないものが多いので
  MapReduceを4回行っている
• Mapperで複数の種類のKeyを出力し、Reducer
  では種類ごとに出力先を変えている
• 単語の分割はスペース区切りでしか対応して
  いない
ナイーブベイズの学習
•   BayesFeatureDriver
•   BayesTfIdfDriver
•   BayesWeightSummerDriver
•   CBayesThetaNomalizerDriver(BayesThetaNom
    alizerDriver)
BayesFeatureDriver
• 文書と正解カテゴリを入力とする
• Mapper
  – 単語wの重みを計算する(TF正規化+文書長正規化)
  <Key: カテゴリc+単語w> <Value: 単語wの重みの合計>
  <Key: カテゴリc+単語w> <Value: 1>
  <Key: 単語w> <Value: 1>
  <Key: カテゴリc> <Value: 1>
• Reducer
  –   カテゴリcでの単語wの重みの合計
  –   カテゴリcで単語wを含む文書数
  –   単語wを含むすべての文書数
  –   カテゴリcに属する文書数
BayesTfIdfDriver
• Mapper
  – IDFを計算する
  <Key: カテゴリc+単語w> <Value: 単語wのカテゴリc
    の重みの合計>
  <Key: カテゴリc+単語w> <Value: IDF>
  <Key: 固定キー> <Value: 1>
                               単語の種類数を計
• Reducer                      算しようとしてい
                                   る
  – TFとIDFを掛け合わせる
  カテゴリcでの単語wの重みの合計
  すべての単語の種類数
BayesWeightSummerDriver
• Mapper
  <Key: 単語w> <Value: 単語wのカテゴリcの重みの合計>
  <Key: カテゴリc> <Value: 単語wのカテゴリcの重みの合
    計>
  <Key: 固定キー> <Value: 単語wのカテゴリcの重みの合計
    >

• Reducer
  – すべての文書の単語wの重み合計
  – カテゴリcにおけるすべての単語の重み合計
  – すべての文書のすべての単語の重み合計
BayesThetaNomalizerDriver
• Competent Naïve Bayesの場合は、
  CBayesThetaNomalizerDriverを起動
• 最後のカテゴリ正規化のために使う
• Mapper
  – 単語wのカテゴリcの最終的な確率を計算
  <Key: カテゴリc> <Value: 単語wのカテゴリcの確率>

• Reducer
  – カテゴリcにおけるすべての単語の確率の合計
ランダムフォレスト
決定木とは?

                     身長 > 170cm



        髪の毛の数 < 8万                偏差値 > 40




    年収 > 800万         男           女          男




女               男
ランダムフォレストとは?
• 複数の決定木を用いる
• 分類の場合、それぞれの木の出力するカ
  テゴリの多数決で最終的な出力を決定
• とてもシンプルながら、学習・テストが
  高速かつ精度がいい。素性が多い場合に
  も有利
決定木の構築
• 学習データをブートストラップサンプリング
  し、B組のデータセットを作成する
 – 重複を許したサンプリング
• それぞれのデータセットごとにB組の決定木
  を構築する
 – M個の素性を選択する
 – M個の素性のうち、最も学習データをよく分類す
   る素性と閾値を用いて、枝を分岐
 – 分岐した先の学習データがすべて同じカテゴリに
   属していれば終了
枝の分岐
• 正解カテゴリのエントロピーを計算する
 – びっくり度の期待値、乱雑さを測る指標

                    カテゴリcに属するデータ数
P(Y=y) = カテゴリがcである確率 =
                       すべてのデータ数

      H(Y) = -Σ P(Y=y) log2P(Y=y)
              y
枝の分岐
• 枝を分岐後のエントロピーを計算する

                      ある素性がxであるデータ数
P(X=x) = ある素性がxである確率 =
                       すべてのデータ数

 H(Y|X) = H(Y|X>=t)P(X>=t) + H(Y|X<t)P(X<t)
相互情報量
• 枝を分岐前と分岐後のエントロピーの差が、
  どれだけ良く学習データを分類しているかの
  指標になる
 I(X,Y) = H(Y) – H(Y|X)
• 相互情報量とも呼ばれ、どれだけ2つの確率
  分布が独立に近いかを表す
 独立なときI(X,Y)=0
• 相互情報量が大きい=2つの確率分布に関係
  がある
 – カテゴリがcである確率と、素性がx以上である確
   率により関係があるような素性とxで分類
枝の分岐
• すべての素性について、どこで分割するかをすべ
  ての場合について相互情報量を計算

                    身長をソー            身長 > 156
                      ト

       身長    年収        偏差値   髪の毛の数
       155   300万      30    8万
左に分岐
       156   900万      70    11万
       158   220万      56    12万
右に分岐


• 最も相互情報量の高い素性と、その分割値で分割
Mahoutでの実装
• 決定木の数だけ処理を分散できる
• MapperだけでReducerは使っていない
• ブートストラップサンプリングと、単純
  にデータを分割する2通りが実装されて
  いる
• 数値の大小で枝を分割するのと、すべて
  の数値ごとに枝を分割する2通りが実装
  されている
Mahoutでの実装
• ImMemInputFormat
  – Mapperの数 / 決定木の数 だけ各Mapperに処理
    を割り当てる
  – 学習データを各Mapperの入力にするのではな
    い
• InMemMapper
  – 決定木の数だけmap関数が呼ばれる
    • それぞれの繰り返しで、ブートストラップサンプ
      リングを行って、決定木の構築を行う
ロジスティック回帰
ロジスティック回帰とは?
• ある重みベクトルwからあるクラスcに属
  する確率を予測したい
 – xがサンプル




   2クラスの
     場合
ロジスティック回帰とは?
• 多クラスの場合
            すべてのカテ
            ゴリの合計


カテゴリkに
属する確率
尤度関数
• 正解のカテゴリに属する確率


• 正解のカテゴリに属する確率を掛け合わせたのが
  尤度関数


• この尤度関数ができるだけ大きくするような重み
  wを推測すればよい
 – 正解カテゴリに属する確率が大きければ、正解カテ
   ゴリに分類される
損失関数
• 尤度関数は大きければ大きいほどよかった
  が、小さくなればなるほどよい損失関数を考
  える
• 掛け算は扱いにくいので対数をとる



• 以下の更新式を繰り返し適用していけば、損
  失関数が最小値に近づく
  – この更新を確率的勾配降下法(SGD)という
確率的勾配降下法(SGD)



               最小値にだん
               だん近づく




• 最終的に以下の式を更新すればよい

            学習データの
    学習レート   正解ラベル
Mahoutの実装
• 今まで解説したのはオンラインでの学習
  の手法
 – データを一つづつ見ていってその都度重みを
   更新
 – データをすべてをいっぺんにみて更新する
   バッチ学習もある
• Mahoutに実装されているのもオンライン
  で、MapReduceでの分散には対応していな
  い
Mahoutの実装
• L1/L2正則化に対応している
  – 過学習を防ぐために損失関数にペナルティ項を設ける

                              L1正則化
                             重みが大きな
• FOBOSでペナルティ項も含めてオンラインで更新   値を取り過ぎ
  – 重みがゼロになりやすいように工夫         るとペナル
                                ティ
• Lazy Updateで高速化
  – 0の素性は更新しない
  – 素性が0ではないデータが来た場合、今までゼロだった分を一気に更新
    する
  – スパースなベクトル(ほとどんどゼロなベクトル)の場合有効
• 学習レート
  – 素性ごとと、全体の2種類の学習レートがある
  – それぞれ、更新されるごとに減少させている
Adaptive Logistic Regression
• ハイパーパラメータ(学習レートなど)
  が違う学習器をn個用意
• データ数件を、それぞれの学習器で学習
 – スレッドで並列に学習させている
• 学習結果がよい学習器(尤度の合計がよ
  いもの)上位m件を選択
• 上位m件の学習器と、上位の学習器のパラ
  メータを少しランダムに変化させたn-m個
  の学習器で再度学習を行う
送ったパッチの概要
• オンライン学習をMapReduceで行う
 – 近年オンライン学習を並列分散する手法が多
   く提案されている
 – 最も良い結果が出ているIterative Parameter
   Mixingという手法を実装
• 以下のアルゴリズムに対応
 – Logistic Regression
 – Adaptive Logistic Regression
 – Passive Aggressive
Iterative Parameter Mixing

                       Mapper   Mapper    Mapper
                       重みベクトル   重みベクトル    重みベクトル
                         を更新      を更新       を更新




                                Reducer
                                重みベクトル
                                  を平均
[Mann et al. 09]
[Mcdonald et al. 10]
MapReduce
• Mapper
  – 正解データを入力
  – 前回の繰り返しの重みをMapの初期化処理で読み
    込んでおく
  – オンライン学習で、重みを更新
  Key     スコア(尤度の合計など)
  Value   重み

• Reducer
  – Reducerは一つだけ立ち上げる
  – 重みをスコアで重み付き平均して出力する
• このMapReduceを繰り返す
送ろうパッチ
• Mahoutはパッチを送ってみるにはいいの
  かも
 – MahoutはHadoop本体などと比べて、それほど
   完成されておらず、改善の余地も多い
 – 全体のコード量もHadoop本体と比べて少ない
 – Hadoop / MapReduce使わなくてもJavaで書き
   さえすればいいらしい
Mahoutの問題点
• 日本語のテキストを扱いたい
• MapReduceを繰り返すようなアルゴリズム
  では、MapReduceを立ち上げるコストがか
  かるので非効率
 – クラスタリング
 – グラフ
 – Iterative Parameter Mixing
 …
Iteration効率化
• たくさんのフレームワークが存在する
 – HaLoop
 – MapReduce Online
 – iMapReduce
 – Spark
 – Twister
 – Hadoop ML
 …
Hadoop 0.23
• MapReduce以外のフレームワークをサポー
  ト
 – MPI
 – Spark
 – Graph
 – Hama
• 今後はMapReduce以外や、MapReduceと他
  のフレームワークの組み合わせが増える
  かもしれない
 – MPIとMapReduceをうまく組み合わせたAll
   Reduce
以上!

Mahoutにパッチを送ってみた