機械学習のベイジアンフィルタでVBを分類してみる

# はじめに
これは、[Visual Basic Advent Calendar 2022](https://qiita.com/advent-calendar/2022/vb)の24日目の記事となります。

昨年、ネタ探しに Yahooのリアルタイム検索で「[VB OR VBA OR VBS OR "Visual Basic" OR VisualBasic](https://search.yahoo.co.jp/realtime/search?p=VB+OR+VBA+OR+VBS+OR+%22Visual+Basic%22+OR+VisualBasic&ei=UTF-8&ifr=tl_sc)」を検索してみたという書きました。
VBと書いてあってVisual Basicの略名かと思ったら、ウィルスバスターだったとかってありますよね。
VBなんて簡易な略名なんで当然ですが、Visual Basicに関連しない項目が出てくるわけですね。

https://qiita.com/yaju/items/e0d3522cb4a0a9991ef1

これを。機械学習のベイジアンフィルタで分類してみようという試みです。
本来は、VB.NET + ML.NETあたりでやるべきなのですが、ML.NETは不勉強なので実装はPythonです。

最初に言っておくとトレーニング量が足りないので、結果は良くないです。

# ベイジアンフィルタとは
ベイジアンフィルタは、迷惑メールフィルタの仕組みとして広く知られている機械学習処理のアルゴリズムで、膨大な言葉の組合せで表現される自然言語の文章の分類に、その真価が発揮されます。
今回はベイジアンフィルタの中でもナイーブベイズ(Naive Bayes)というアルゴリズムを使用します。ナイーブは「ばからしいほど単純」という意味があるため「単純ベイズ分類器」とも呼びます。しかし、単純とはいえ馬鹿にできない分類性能を持ちます。

https://yaju3d.hatenablog.jp/entry/2016/10/31/222307

# 実装環境
* Google Colaboratory
* Python 3.8.16
* Janome 0.4.2 形態素解析ライブラリー

## Janomeインストール
```py
!pip install janome

Installing collected packages: janome
Successfully installed janome-0.4.2
```

# 実装
参考記事はPython 2.7だったので、Python 3用に修正しました。
print文の括弧と、sys.maxint→sys.maxsizeを修正

```py
import math
import sys

from janome.tokenizer import Tokenizer

def split(doc, word_class=["形容詞", "形容動詞", "感動詞", "副詞", "連体詞", "名詞", "動詞"]):
    t = Tokenizer()
    tokens = t.tokenize(doc)
    word_list = []
    for token in tokens:
        word_list.append(token.surface)
    return [word for word in word_list]

def getwords(doc):
    words = [s.lower() for s in split(doc)]
    return tuple(w for w in words)
```

```py
class NaiveBayes:
    # コンストラクタ
    def __init__(self):
        self.vocabularies = set() # 単語の集合
        self.wordcount = {}       # {category : { words : n, ...}}
        self.catcount = {}        # {category : n}

    # 訓練フェーズ：単語のカウントアップ
    def wordcountup(self, word, cat):
        self.wordcount.setdefault(cat, {})
        self.wordcount[cat].setdefault(word, 0)
        self.wordcount[cat][word] += 1
        self.vocabularies.add(word) # 重複を除く

    # 訓練フェーズ：カテゴリのカウントアップ
    def catcountup(self, cat):
        self.catcount.setdefault(cat, 0)
        self.catcount[cat] += 1

    # 訓練
    def train(self, doc, cat):
        word = getwords(doc)
        for w in word:
            self.wordcountup(w, cat)
        self.catcountup(cat)

    # 推定フェーズ：分類
    def classifier(self, doc):
        best = None # 最適なカテゴリ
        max = -sys.maxsize
        word = getwords(doc)
        
        # カテゴリ毎に確率の対数を求める
        for cat in self.catcount.keys():
            prob = self.score(word, cat)
            if prob > max:
                max = prob
                best = cat

        return best

    # 推定フェーズ：スコア計算
    def score(self, word, cat):
        score = math.log(self.priorprob(cat))
        for w in word:
            score += math.log(self.wordprob(w, cat))
        return score

    # 推定フェーズ：catの生起確率 P(cat)
    def priorprob(self, cat):
        return float(self.catcount[cat]) / sum(self.catcount.values())

    # 推定フェーズ：あるカテゴリの中に単語が登場した回数
    def incategory(self, word, cat):
        if word in self.wordcount[cat]:
            return float(self.wordcount[cat][word])
        return 0.0

    # 推定フェーズ：条件付き確率 P(word|cat)（補正つき）
    def wordprob(self, word, cat):
        prob = \
            (self.incategory(word, cat) + 1.0) / \
                  (sum(self.wordcount[cat].values()) + \
                   len(self.vocabularies) * 1.0)
        return prob
```

トレーニング

```py
if __name__ == "__main__":
    nb = NaiveBayes()

    nb.train(u'''VBA（Visual Basic for Applications）はマイクロソフト(Microsoft)のExcelやAccessなどのOffice製品で利用できるプログラミング言語です。''', 'Visual Basic')
    nb.train(u'''VB.NETはMicrosoft社が開発したオブジェクト指向言語です。.NET framework上での開発に用いられ、Windows関連の開発に重用されてきました。''', 'Visual Basic')
    nb.train(u'''すあだのプロデュースするバーチャルYouTuberの一人で、FaceRigのサンプル素材の「ヘーゼルおばあさん」をそのまま使用している。 ゲーム実況を中心に、CMパロディ、替え歌、声真似など多彩な内容の動画を配信している。''', 'バーチャルおばあちゃん')
    nb.train(u'''心拍数や歩数などの活動データでキャラクターを育成・進化させるウェアラブル端末型玩具『バイタルブレス』。 そんな『バイタルブレス』から「デジタルモンスター」が登場、VBラボ、VBコイン''', u'バイタルブレス')
    nb.train(u'''ウイルスバスターとは、トレンドマイクロ社が開発・販売している、コンピューターウィルスなどの不正プログラムからパソコンを保護するためのウィルス対策ソフトの名称''', u'ウイルスバスター')
    nb.train(u'''ビタミンB群とは、ビタミンB1・B2・B6・B12、ナイアシン、パントテン酸、葉酸、ビオチンの8種類を指します。''', u'ビタミンB')
    nb.train(u'''ベンチャー企業とは、革新的な技術・製品・サービスを開発し、イノベーションを生み出す企業であり、設立数年程度の若い企業''', u'ベンチャー企業')
    nb.train(u'''バーチャルボーイ（VIRTUAL BOY）は、任天堂が発売した3Dゲーム機。横井軍平が発案。略称「VB」。その外見から「赤い眼鏡」とも呼称された。1995年7月21日発売。''', u'バーチャルボーイ')
    nb.train(u'''VB-AUDIO Virtual Cable とは、仮想オーディオデバイスと呼ばれるソフト（ドライバー）で、バーチャルオーディオケーブルとして動作するバーチャルオーディオ機器です。この仮想オーディオデバイスを使えば、サウンドカードなどを買い足さなくても PC に再生・録音デバイスを簡単に増やすことができるので、とても重宝されています。''', u'VB-Audio')
    nb.train(u'''仮想通貨イーサリアムの共同創設者 ヴィタリック・ブテリン（VB）は、ロシア系カナダ人のプログラマ、起業家。主に暗号通貨イーサリアムの考案者として知られる。2011年に父親からビットコインについて聞き、ビットコインの話題を取り扱う雑誌『Bitcoin Magazine』の編集を始める。''', u'ETHウォレット')
    nb.train(u'''VisualBoyAdvance（ビジュアル ボーイ アドバンス）とは、ゲームボーイ、ゲームボーイカラー、ゲームボーイアドバンスのゲームをプレイすることが可能なエミュレータ''', u'ETHウォレット')
    nb.train(u'''VOCAL BATTLE AUDITION（ボーカル・バトル・オーディション）は、芸能事務所LDHが主催したオーディションである。18歳以上の男性を対象としたEXILEの新ボーカルオーディションの開催を発表''', u'VOCAL BATTLE AUDITION')
```

## 結果
```py
words = u'近くならお手伝いできるのに～ ACCESS VBAもExcel VBAもたいして変わらんし'
print(u'%s => 推定カテゴリ: %s' % (words , nb.classifier(words)))

=> 推定カテゴリ: Visual Basic
```

ExcelとAccessが含まれているので、正しく判断されたようです。

```py
words = u'Excel VBAの文法基礎はあるのに実務でマクロがうまく作れない方に共通しているのは「整理が不十分」ということに尽きます。'
print(u'%s => 推定カテゴリ: %s' % (words , nb.classifier(words)))

=> 推定カテゴリ: バーチャルおばあちゃん
```

残念、Excelは含まれているのに、バーチャルおばあちゃんになってしまった。マクロとかをトレーニングに追加すればいいかも。

```py
words = u'ありがとうございます。 VB.NETはVBAに近いのでしょうか？'
print(u'%s => 推定カテゴリ: %s' % (words , nb.classifier(words)))

=> 推定カテゴリ: Visual Basic
```

VB.NETが含まれているので、正しく判断されたようです。

# 最後に
トレーニング(nb.train)をたくさん読ませれば、認識精度がよくなるはずです。
nb.train部分を、別ファイルから読み込むようにして大量にトレーニングするように作り込めばいいです。

メリークリスマス！