PHPで機械学習(その2):ツイート内容を学習

(1/1)
PHP で機械学習(その 1):ツイートを取得し DB 格納」の続きである。
今回は、収集したデータ(ツイート)を単語に分解し、学習させた結果をデータベース(SQLite)に登録していく。

機械学習にはいくつかのアルゴリズムがあるが、ここでは実装が簡単な単純ベイズ分類器を用いることにする。学習方法は簡単で、ユーザー別の単語の出現回数をデータベースに登録していけばいい。

(2021 年 7 月 11 日)PHP8 対応,リファラ・チェック改良

目次

サンプル・プログラムの実行例

PHPで機械学習(その2):ツイート内容を学習

サンプル・プログラム

圧縮ファイルの内容
BayesClassifier.phpサンプル・プログラム
pahooTwitterAPI.phpTwitter APIを利用するクラス pahooTwitterAPI。
使い方は「PHPでTwitterに投稿(ツイート)する」などを参照。include_path が通ったディレクトリに配置すること。

サンプル・プログラムの流れ

PHPで機械学習(その2):ツイート内容を学習
PHP で機械学習(その 1):ツイートを取得し DB 格納」に加え、メソッド learnTweets が学習の中心を担う。

学習処理では TwitterAPI は使わず、テーブル $table_tweets に登録されたツイートを取りだし、形態素解析ツール MeCab を使って単語に分解していく。MeCab については「PHP で MeCab のユーザー辞書を作成する」で紹介している。

準備

0055: //SQLite DBファイル名;各自の環境に合わせて変更すること
0056: define('DBFILE', './sqlite/usertimelines.sqlite3');
0057: 
0058: //MeCab実行プログラム;各自の環境に合わせて変更すること
0059: define('MECAB', 'C:\Program Files (x86)\MeCab\bin\mecab.exe');

MeCab の実行プログラムは定数 MECAB に、ユーザー辞書は FILE_UDIC_MECAB に定義しておく。

登録されたツイートのうち未学習のものをまとめて取り出す

0812: /**
0813:  * 登録されたツイートのうち未学習のものをまとめて取り出す
0814:  * @param   string $userID ユーザーID
0815:  * @param   int $count 取り出す最大件数
0816:  * @return  array(取り出したツイート, ツイート件数)
0817: */
0818: function takeTweet($userID$count) {
0819:     $sql_select = 'SELECT * FROM ' . $this->table_tweets . ' WHERE user_id=:user_id AND flag<>1';
0820:     $sql_update = 'UPDATE ' . $this->table_tweets . ' set flag=:flag WHERE id=:id';
0821: 
0822:     //DB取得
0823:     $stmt = $this->pdo->prepare($sql_select);
0824:     $stmt->bindValue(':user_id', $userIDPDO::PARAM_STR);
0825:     $ret = $stmt->execute();
0826:     $cnt = 0;
0827:     $text = '';
0828:     while ($row = $stmt->fetch()) {
0829:         $text .= $row['description'];
0830:         $stmt2 = $this->pdo->prepare($sql_update);
0831:         $stmt2->bindValue(':id', $row['id'], PDO::PARAM_STR);
0832:         $stmt2->bindValue(':flag', 1, PDO::PARAM_INT);
0833:         $ret = $stmt2->execute();
0834:         $cnt++;
0835:         if ($cnt >= $countbreak;
0836:     }
0837:     $msg = 'Success SQLite: take ' .$cnt . ' tweets.';
0838:     $this->putAppLog($msg, 1, __LINE____FUNCTION__);
0839: 
0840:     return array($text$cnt);
0841: }

メソッド takeTweet は、テーブル $table_tweets に登録されたツイートから、未学習(flag が 1 以外の値のレコード)のものを取り出す。
取り出したら、flag を立てておく。

テキストを単語ベクトルに変換

0790: /**
0791:  * テキストを単語ベクトルに変換
0792:  * @param   string $text テキスト
0793:  * @param   array  $vectors 単語ベクトルを格納する配列
0794:  * @return  int 分解した単語数
0795: */
0796: function text2vector($text, &$vectors) {
0797:     $words = array();
0798:     $cnt = 0;
0799:     $this->getWords($text$words);       //単語分割
0800:     foreach ($words as $word) {
0801:         if (! $this->isword($word))   continue;
0802:         if (isset($vectors[$word[0]])) {
0803:             $vectors[$word[0]]++;
0804:         } else {
0805:             $vectors[$word[0]] = 1;
0806:             $cnt++;
0807:         }
0808:     }
0809:     return $cnt;
0810: }

メソッド text2vector は、与えられたテキストを MeCab に食わせて単語に分解する。

学習する単語か否かは、メソッド isword で判定している。
この部分を変更することで、学習結果が変わってくる。

単語ベクトル情報を更新

0843: /**
0844:  * 単語ベクトル情報を更新する
0845:  * @param   string $userID ユーザーID
0846:  * @param   array  $vectors 単語ベクトル情報
0847:  * @return  bool TRUE/FALSE
0848: */
0849: function updateVectors($userID$vectors) {
0850:     $sql_select = 'SELECT count FROM ' . $this->table_vectors . ' WHERE user_id=:user_id AND word=:word';
0851:     $sql_insert = 'INSERT INTO ' . $this->table_vectors . ' (user_id, word, count, dt) VALUES (:user_id, :word, :count, :dt)';
0852:     $sql_update = 'UPDATE ' . $this->table_vectors . ' set count=:count, dt=:dt WHERE user_id=:user_id AND word=:word';
0853: 
0854:     foreach ($vectors as $word=>$count) {
0855:         $stmt = $this->pdo->prepare($sql_select);
0856:         $stmt->bindValue(':user_id', $userIDPDO::PARAM_STR);
0857:         $stmt->bindValue(':word',    $word,   PDO::PARAM_STR);
0858:         $ret = $stmt->execute();
0859:         $row = $stmt->fetch();
0860: 
0861:         //DB追加
0862:         if (! isset($row['count'])) {
0863:             $stmt2 = $this->pdo->prepare($sql_insert);
0864:             $stmt2->bindValue(':user_id', $userIDPDO::PARAM_STR);
0865:             $stmt2->bindValue(':word',    $word,   PDO::PARAM_STR);
0866:             $stmt2->bindValue(':count',   $count,  PDO::PARAM_INT);
0867:             $stmt2->bindValue(':dt', date(DATE_W3Ctime()), PDO::PARAM_STR);
0868:             $ret = $stmt2->execute();
0869:             if ($ret < 1) {
0870:                 $this->error = TRUE;
0871:                 $this->errmsg = 'Error SQLite: ' . $word . ' -- cannot insert vectors.';
0872:                 $this->putAppLog($this->errmsg, 0, __LINE____FUNCTION__);
0873:                 return FALSE;
0874:             }
0875:         //DB更新
0876:         } else {
0877:             $stmt2 = $this->pdo->prepare($sql_update);
0878:             $stmt2->bindValue(':user_id', $userIDPDO::PARAM_STR);
0879:             $stmt2->bindValue(':word',    $word,   PDO::PARAM_STR);
0880:             $stmt2->bindValue(':count', $row['count'] + $countPDO::PARAM_INT);
0881:             $stmt2->bindValue(':dt', date(DATE_W3Ctime()), PDO::PARAM_STR);
0882:             $ret = $stmt2->execute();
0883:             if ($ret < 1) {
0884:                 $this->error = TRUE;
0885:                 $this->errmsg = 'Error SQLite: ' . $word . ' -- cannot update vectors.';
0886:                 $this->putAppLog($this->errmsg, 0, __LINE____FUNCTION__);
0887:                 return FALSE;
0888:             }
0889:         }
0890:     }
0891:     return TRUE;
0892: }

メソッド updateVectors は、ユーザー毎、単語ごとに単語の出現回数をカウントし、テーブル $table_vectors に書き込んでいく。

参考サイト

(この項おわり)
header