こんにちは。2010/09/26(日)、第7回データマイニング+WEB勉強会@東京に参加してきました。
- ATND http://atnd.org/events/8164
- 主催者@hamadakoichiさんによる勉強会まとめ(発表資料一覧/Togetter) http://d.hatena.ne.jp/hamadakoichi/20100926/p1
この勉強会の特徴について
双方向の議論が多く、参加者の多くの方が発言するのが大きな特徴です。またIT系以外の方が参加される異業種交流ができる貴重な勉強回の一つです。内容については「理論〜詳細な実装〜実務」と幅広いものとなってます。
開始時の自己紹介、終了時の振り返り、で少なくとも全員2回ずつ発言する必要がありますw
今回も会場を提供してくださったニフティ・エンジニアサポート様に感謝!また、次回の勉強会は11/14(日)開催予定です。
今回は発表しました
以下発表資料です。
はじめてでもわかるベイズ分類器 -基礎からMahout実装まで-
機会学習も統計も素人ですが、素人なりに勉強した結果と、MahoutというHadoop(またはElastic MapReduce)上で動作する分類器の使い方を解説しています。ベイズ分類器の学習・評価データにはWikipediaのデータを利用してます。
参加者をまとめたホワイトボード
プレゼン内で紹介した本とRubyのサンプルコード
「この本持っている人います?」の質問に7割近くの人が手を挙げたのが印象的でした。この「集合知プログラミング」の6章では、Pythonによるナイーブベイズ分類器の実装を解説していますが、それを私が使い慣れているRubyで実装してみました。
まず使い方から
# 分類器生成 n = NaiveBayes.new # 学習 n.train('Nobody owns the water.','good') n.train('the quick rabbit jumps fences','good') n.train('buy pharmaceuticals now','bad') n.train('make quick money at the online casino','bad') n.train('the quick brown fox jumps','good') # 推定 puts n.classify("quick rabbit",default="unknown") puts n.classify("quick money",default="unknown") # 閾値を与える n.setthreshold("bad",3.0) # 推定 puts n.classify("quick money",default="unknown") |
分類器のコード
# 特徴抽出クラス class GetWordFeature def get_feature(doc) words = doc.split(' ') words.map{|w| w.downcase}.select{|w| w.length < 20 && w.length > 2 }.uniq end end # 分類器の基底クラス class Classifier def initialize(f) @fc,@cc,@feature = {},{},f end def incf(f,cat) @fc[f] ||= {} @fc[f][cat] ||= 0.0 @fc[f][cat] = @fc[f][cat] + 1.0 end def incc(cat) @cc[cat] ||= 0.0 @cc[cat] = @cc[cat] + 1.0 end def fcount(f,cat) @fc[f] ||= {} @fc[f][cat] ||= 0.0 @fc[f][cat] end def catcount(cat) @cc[cat] ? @cc[cat].to_f : 0.0 end def totalcount sum = 0 @cc.each_value {|value| sum += value} return sum end def categories @cc.keys end # 条件付き確率 Pr(A|B) def fprob(f,cat) return 0 if catcount(cat) == 0 || fcount(f,cat) == nil fcount(f,cat) / catcount(cat) end # 重み付き確率 def weightedprob(f, cat, weight=1.0, ap=0.5) # 通常の条件付き確率 basicprob = fprob(f,cat) # この特徴がすべてのカテゴリ中に存在する確率 totals = 0 categories.each do |c| totals = totals + fcount(f,c) if fcount(f,c) end # 重み付けした平均計算 ((weight*ap)+(totals*basicprob)) / (weight+totals) end def train(item, cat) features = @feature.get_feature(item); features.each do |f| incf(f, cat) end incc(cat) end end # 単純ベイズ分類器 class NaiveBayes < Classifier def initialize super(GetWordFeature.new) @thresholds = {} end # P(B|A):アイテム全体の与えられたカテゴリでの確率を求める(掛け合わせる) def docprob(item,cat) features = @feature.get_feature(item) p = 1.0 features.each do |f| ep = weightedprob(f,cat) p = p * ep end return p end def prob(item,cat) catprob = catcount(cat) / totalcount docprob = docprob(item,cat) return docprob * catprob end def setthreshold(cat,t) @thresholds[cat] = t end def getthreshold(cat) @thresholds[cat] || 1.0 end def classify(item, default=nil) probs,max,best = {},0.0,nil categories.each do |c| probs[c] = prob(item,c) if probs[c] > max max = probs[c] best = c end end probs.each_key do |c| # (2番目に確率の高いもの * 閾値)と比較 second = probs[c].to_f * getthreshold(best) return default if second > probs[best] end return best end end |
以上です!