root/lang/python/spam/bayes.py

Revision 31831, 3.9 kB (checked in by rezoo, 3 years ago)

collectionsを使ってある程度スマートに。

Line 
1#!/usr/bin/env python
2#-*- coding:utf-8 -*-
3
4import MeCab, operator, math, collections
5from chi2p import chi2p
6
7class Samples (object):
8        """
9        シリアライズ化のために切り離して使うことにする。
10        """
11        def __init__(self):
12                self.spam_dict    = collections.defaultdict(float) # スパム文章の単語辞書
13                self.nonspam_dict = collections.defaultdict(float) # 非スパム文章の単語辞書
14                self.n_spam       = 0.0 # スパム文章のサンプル数
15                self.n_nonspam    = 0.0 # 非スパム文章のサンプル数
16       
17        def getNumberOfWord(self, word):
18                """
19                Robinson方式で用います。
20                単語wordにおける辞書の出現回数を返します。
21                """
22                return self.spam_dict[word] + self.nonspam_dict[word]
23
24class Bayes (object):
25        LIMIT_MAX = 1.0 - 1.0e-5
26        LIMIT_MIN = 1.0e-5
27        DEFAULT_PERCENTAGE = 0.4
28
29        def __init__(self, samples=None):
30                if samples:
31                        self._s = samples
32                else:
33                        self._s = Samples()
34                self.tagger = MeCab.Tagger("-Ochasen")
35       
36        def addSample(self, sentence, isSpam):
37                """
38                予め判別されたセンテンスを追加し、ベイズ学習器に学習させます。
39                """
40                n_list = self.parseToNoun(sentence)
41                if isSpam:
42                        self._s.n_spam += 1.0
43                        for n in n_list: self._s.spam_dict[n] += 1.0
44                else:
45                        self._s.n_nonspam += 1.0
46                        for n in n_list: self._s.nonspam_dict[n] += 1.0
47       
48        def evaluateByGraham(self, sentence, bias=2.0):
49                """
50                学習機での学習結果を元にスパムかどうかを判定し、スパムである確率を返します。
51                """
52                n_list    = self.parseToNoun(sentence)
53                p_list    = [self.getPercentage(n, bias) for n in n_list]
54                p_spam    = reduce( operator.mul, p_list )
55                p_nonspam = reduce( operator.mul, [1.0-p for p in p_list] )
56                p = p_spam / (p_spam + p_nonspam)
57                return p
58       
59        def evaluateByRobinson(self, sentence, s=1.0, x=0.5):
60                """
61                学習機での学習結果を元にスパムかどうかを判定し、スパムである指標を返します。
62                基本的にRobinson方式のほうがGraham方式よりも優れています。
63                """
64                n_list    = self.parseToNoun(sentence)
65                twoN = 2.0*len(n_list)
66                ds  = [self.getDegreeOfBelief(w,s,x) for w in n_list]
67                dsi = [1.0-d for d in ds]
68                H = chi2p(-2.0*math.log(reduce(operator.mul, ds)), twoN)
69                S = chi2p(-2.0*math.log(reduce(operator.mul, dsi)), twoN)
70                I = (1.0+H-S)/2.0
71                return I
72       
73        def getSamples(self):
74                """
75                学習に使われているサンプルを取得します。
76                """
77                return self._s
78       
79        def getDegreeOfBelief(self, word, s, x):
80                """
81                Robinson方式で用います。
82                p(w_i)における信頼度の具合を返します。
83               
84                x:今まで1度もメールの中に出現していない単語が初めてメールに出現したときに、
85                そのメールがスパムメールである予測確率([0,1])
86                s:xの予測に与える強さ(strength)
87                """
88                n = self._s.getNumberOfWord(word)
89               
90                if n == 0.0:
91                        return s*x / (s+n)
92                else:
93                        return (s*x + n*self.getPercentage(word, 1.0))/(s+n)
94       
95        def getPercentage(self, word, bias):
96                """
97                各単語のスパム確率を返します。
98                """
99                # 各確率の計算
100                pgood = 0.0
101                pbad  = 0.0
102                if (word in self._s.nonspam_dict):
103                        pgood = min( (1.0, self._s.nonspam_dict[word] / self._s.n_nonspam * bias) )
104                if (word in self._s.spam_dict):
105                        pbad  = min( (1.0, self._s.spam_dict[word] / self._s.n_spam) )
106                # スパム確率の算出
107                p = 0.0
108                if (pgood == 0.0 and pbad == 0.0):
109                        p = Bayes.DEFAULT_PERCENTAGE # 判定できないのでデフォルト値を返す
110                else:
111                        p = pbad / (pgood + pbad)
112                        if p < Bayes.LIMIT_MIN   : p = Bayes.LIMIT_MIN # 確率pが0に収束しないためにする。
113                        elif p > Bayes.LIMIT_MAX : p = Bayes.LIMIT_MAX # 同様。
114                return p
115       
116        def parseToNoun(self, sentence):
117                """
118                文章から、名詞から成るリストを返します。
119                """
120                n_list = []
121                m = self.tagger.parseToNode(sentence)
122                while m:
123                        if m.feature.startswith("名詞"):
124                                n_list.append(m.surface)
125                        m = m.next
126                return n_list
Note: See TracBrowser for help on using the browser.