集合知プログラミングはじめました
はじめましたと書きましたが,実はずいぶん前から読んでました.その結果をちまちまとあげてく予定です.
集合知プログラミング第6章 準備
テキストフィルタリングから始めます.最初は環境を整えるところから.PythonからSQLiteをつかえるようにします.
Pythonをインストール
2.5を入れておくと,標準で使えるライブラリがたくさんあって何かと便利なようです.Finkだと"python25"というパッケージ名になっています."python"というパッケージを入れれば最新バージョンが入るのかもしれませんが,一応手動で入れます.
% sudo apt-get install python25 (ずらずらとメッセージが出てきます.省略) % sudo apt-get install python
これで/sw/bin/python ができました.ls などで確認しておきましょう.ついでにバージョンも確認しておきます.
% ls /sw/bin/python /sw/bin/python % /sw/bin/python -V Python 2.5.1
SQLite3のインストール
Python2.5にはsqlite3のモジュールが標準でついているのですが,肝心のSQLite3そのものが入っていないので,インストール.Finkを使っていれます.便利.
% sudo apt-get install sqlite3
集合知プログラミング第6章 実装
集合知プログラミング第6章のサンプルほぼそのままだけれど.違う点はアイテムがクラスに属する確率をそのままつかって分類するのではなく,その確率の対数を使っている点.小数点以下の桁が大きくなるのは嫌なものですからね.
ソースはこんな感じ.
import re import math import sys import sqlite3 import os class classifier: def __init__(self, getfeatures, filename=None): self.getfeatures = getfeatures self.setdb(filename) def setdb(self, dbfile): self.con = sqlite3.connect(dbfile); try: self.con.execute('create table fc(feature, category, count);') except sqlite3.OperationalError: print("table fc has already been created. skip") try: self.con.execute('create table cc(category, count);') except sqlite3.OperationalError: print("table cc has already been created. skip") def incf(self, f, cat): #self.fc.setdefault(f, {}) #self.fc[f].setdefault(cat, 0) #self.fc[f][cat] += 1 count = self.fcount(f, cat) if(count == 0): self.con.execute("insert into fc values('%s', '%s', 1);"%(f,cat)) else: self.con.execute("update fc set count=%d where feature='%s' and category='%s';"%(count+1,f,cat)) def incc(self, cat): count = self.catcount(cat) if(count == 0): print("insert new category %s into table cc"%(cat)) self.con.execute("insert into cc values('%s', 1);" %(cat)) else: self.con.execute("update cc set count=%d where category='%s';" %(count+1,cat)) def fcount(self, f, cat): result = self.con.execute("select count from fc where feature = '%s' and category = '%s';"%(f,cat)).fetchone() if(result == None): return 0.0 else: return float(result[0]) def catcount(self, cat): #if cat in self.cc: #return float(self.cc[cat]) #return 0 sql = "select count from cc where category = '%s';"%(cat) #print(sql) result = self.con.execute(sql).fetchone() if(result == None): return 0.0 else: return float(result[0]) def totalcount(self): #return sum(self.cc.values()) result = self.con.execute("select sum(count) from cc;").fetchone() if(result == None): return 0.0 else: return result[0] def categories(self): #return self.cc.keys() result = self.con.execute("select category from cc;") return [row[0] for row in result] def train(self, item, cat): features = self.getfeatures(item) for f in features: self.incf(f, cat) self.incc(cat) self.con.commit() def fprob(self, f, cat): if self.catcount(cat) == 0: return 0 return self.fcount(f, cat) / self.catcount(cat) def weightedprob(self, f, cat , prf, weight=1.0, ap=0.5): basicprob = prf(f, cat) totals = sum([self.fcount(f, c) for c in self.categories()]) return (weight * ap + totals * basicprob) / (weight + totals) class naivebayes(classifier): # Modified to use logged value of each features's probability def docprob(self, item, cat): features = self.getfeatures(item) return sum([math.log(self.weightedprob(f, cat, self.fprob)) for f in features]) def catprob(self, cat): if(self.totalcount() == 0): return 0 return self.catcount(cat) / self.totalcount() def prob(self, item, cat): return math.log(self.catprob(cat)) + self.docprob(item, cat) def classify(self, item, default=None): max = 0.0 # This value means "-infinity". max_c = '' for cat in self.categories(): p = self.prob(item, cat) sys.stdout.write("p(%s|item)=%f\n"%(cat,p)) if(max == 0.0 or p > max): max = p max_c = cat return max_c
集合知プログラミング第6章 解説
プログラム概要は下図の通り.分類対象を受け取って,それを特徴に分解するFeature extractorと,その結果を受け取って分類を行うClassifierの二つを実装することになります.
前者はgetwordsという関数として,後者はclassifierというクラスとして実装しています.
このような実装のポイントは二つ.
- feature extractorをclassifierの内部で規定しないことにより様々な特徴抽出関数を利用できるようにしたこと
- 分類アルゴリズムの具体的な実装をサブクラスに任せることにより,さまざまな分類アルゴリズムを利用できるようになっていること
とりあえずラフに解説するとこんな感じです.