水無瀬のプログラミング日記

プログラムの勉強した時のメモ

Slackの会話をWatsonで分析する

はじめに

ある日Watsonを使ってみようと思い何をするか悩んでました。
また別のある日Slackの会話を何かに使えないかと思って、データを抜くことを考えてました。
そしたらなんとSlackのデータは公式で取れるじゃないですか。
※詳細はSlackの公式から
これは組み合わせるしか無いと言うのが今回のテーマ。

やりたいこと

WatsonでSlackの会話を分類。
どのような話題がされているか分析する。
という名目でホントはWatsonとPython使いたいが本音。

使うもの

Watson

使うサービスはWatson Assistant
会話(文章)の意図を分類するのはNLC(Natural Language Classifier)があるけど、
ライトアカウントでは使えなかったのでAssistantを使った。
多分、正しい使い方では無いけど、やりたいことはできるので今回はよしとする。
ちなみにNLU(Natural Language Understanding)は別物。
略称だけ覚えてるとややこしくなるので注意(自分だけかも知れないけど)

Python

Assistantに会話を投げるのはPythonからやる。
後、Slackの履歴を扱うのもやる。
Python使うのは単純に勉強したいから。
cURLでもJSでもJavaでもRubyでもなんでも良いと思う。

やり方

  1. ダウンロードしてきた履歴から分類に活かせそうなチャンネルを選別する
  2. 選別したチャンネルから学習データとして使えそうなデータを選別する
  3. 選別した学習データをAssistantに学習させる
  4. 分類をかける文章をAssistantに投げる
  5. 分類された結果を分析する

チャンネル選別

比較的内容が偏りそうなものを選ぶ。
今回は技術の話とアニメの話と食事の話と+α(完全に身内ネタで例えることも無かったもの)。

学習データ選別

個人的にはWatsonは学習させられるのが最大のメリット&デメリットだと思う。
メリットと感じるのは学習データに特化して頭が良くなるから。
逆にデメリットと感じるのはゴミが混じると順調に頭が悪くなるから。
と、言う理由で学習データを選別する。
ただし、残念ながらその手の話題に詳しく無いので今回はざっくり以下を基準とした。

  1. 10文字以上100文字以下であること
  2. 名詞と動詞が含まれること
  3. URLでないこと
  4. その他いらないと思ったもの(Slack固有のメッセージとか)

1.の理由としては短すぎるのは言葉になっていない可能性があるから。(うん。そうだね。と言った相槌など)
逆に長過ぎるのも学習データとして参考にならなそうだから(おそらく会話のような文章では無いと考えられるから)。
2.の理由は単語のみだけじゃなくてちゃんと日本語っぽいものを選定したかったから。
名詞動詞は形態素解析書けて単純に有る無しだけを見た。(形態素解析の話はまた今度。)
3.はURLなんかあってもしょうがないから。

Assistantに学習させる

Assistantの学習はGUIでできるから、学習データ(CSV形式)を用意する。
生成したプログラムはGitに上がっているのを参照。(そのうちきれいに作り直す)
最終的には以下の用にデータ, Intent名という形にする。

Angularとjsでフロント作るよ言われた,Development
scriptがデフォルトでタイマー起動ができるから1時間おきに走らせてる,Development

ちなみにIntentは会話の意図を指定するところ。
本来の用途のChatbotならパソコンが壊れたんだけど or パソコンの画面がつかないんだけど→故障みたいな意図を設定するんだと思う。
今回は用途が違うから、Angularとjsでフロント作るよ言われた→Development(技術的な話)の様に分類として使う。
AssistantはEntityとか色々設定できるけど今回はこれだけ使う。

詳しい設定は公式ドキュメントがあるからそちらを参照。
この手のドキュメントには珍しく、日本語で書いてある。
※ただし、最新版であるとは限らない。
 仕方ないけど、英語の方が更新早い上に別に同期とか取られて無いと思う。

Assistantに文章を投げる

今回は雑談メインのチャンネルをAssistantに投げる。
全部投げようと思ったらアホほどあったから、一旦自分のデータに絞ることにした。
自分のデータに絞るにはusers.jsonを参照しないといけなくて地味にめんどくさい。
jsでサラッとやってしまった。エラー処理は特に気にしない。
(ホントはこんな書き方しないと思う。。。)
Assistantに投げる処理は公式リファレンスを参照。
コードはGitに。
一応Assistantに投げるテキストは8文字以上 & URLじゃないものにした。

'use strict'

const fs = require('fs');
const usersjson = require('./users.json');
const myId = usersjson.find(user => user.name === 'ユーザー名').id;

const getJsonData = (path) => {
    const jsonData = [];
    fs.readdirSync(path).forEach(file => {
        jsonData.push(JSON.parse(fs.readFileSync(`${path}/${file}`)));
    });    
    return jsonData
}
const getText = (jsonData) => {
    const myText = [];
    jsonData.forEach(data => {
        data.filter(detail => detail.user === myId).map(filtered => filtered.text).forEach(text => myText.push(text));
    });
    return myText;
}
fs.writeFileSync('./myText.txt', getText(getJsonData('チャンネルフォルダ')).join('\n'));

結果を分析する

おそらく数学的な統計学的な正しい分析方法があると思う。
残念ながらその辺りがわからないので、単純にカテゴリ/総数の単純な割合で取得する。
今回は前述の通りの4種類 + Assisに投げなかったテキスト(その他とする)の5種類で分類する。
Assistantの確信度が低いものもその他に振り分けて良いと思ったけど、
想像以上に分類される事がなかったので今回はやめる。 結果は下記の通り。

カテゴリ 分類数 出現率(%)
技術 997 26.47
アニメ 710 18.85
295 7.83
食事 111 2.95
その他 1653 43.89
合計 3766 -

ちょっとした考察もどき

今回その他に分類する条件(Assistantに投げない条件)を8文字未満にしていた。
どうやら想像以上に8文字って打たないらしい。
加えてAssistantの確信度について今回全く見ないでこの結果だったという事は実際はもっと少ない値が出ることになる。
実際Assistantの結果の中には確信度98%です!って言ったハズレの分類や58%くらいですっていうアタリもあった。
という事で次回?に向けてのToDoは大体下記の通り。

ToDo

  • 投げる文字数選定
    少なすぎるのは意味ないから、投げなかったデータから最低値を考える。
    現時点の予想では5.6文字何じゃないかなと
  • Assistantの学習
    今回の学習データは無駄では無いけど、おそらく質としてはあまり良くないと思われる。
    使ったデータをクレンジングするだけでも結構変わると思うからまずはそこから。
    (記号入っている。明らかにジャンル違うとかを消せれば良いかなと)
  • 学習データを均等に用意する
    実はIntentに登録されてるデータ数がまばら。
    現状、技術話題が一番多かった気がするから今回の結果はある種当たり前かもしれない。(データが多い→判別されやすい。)
    単純に分類しやすい話題なだけだったかも知れないけど。
  • ちゃんとテストする(Assistant)
    今回何もテストしないでデータ入れただけなので、次は用意したデータの8割学習、2割テストとかにしたい。

まとめ

一応やりたいことはできた。(Watson使うだけ。Python使うだけ)
データ計測もイマイチだけど、将来的はbotなり、WebなりCLI以外で動くようにしたいなとか考えている。
そもそもなんちゃって解析からレベルを上げるところが大事そう。