こんにちはアドバンストテクノロジー部の@y-matsushitaです。
今回は機械学習を使った取り組みとして、手始めにfastTextを使ったテキストの分類について触れたいと思います。
fastTextとはFacebookが提供する単語のベクトル化とテキスト分類をサポートした機械学習ライブラリです。
fastTextという名前の通り動作が軽く早いのが特徴です。試しに使ってみたところ精度も良好で動作も軽かったのでご紹介させていただきます!
今回は試しに様々な情報が入り混じったTwitterの投稿内容を分類して「美容系」「エンタメ系」「暮らし系」情報の3パターンに分類してみます。
なお今回の記事ではPython 3.6.1を使用します。
fastTextを使ってできること
まず最初にfastTextを使った結果をお見せします。
『分類前』が処理前で『分類後』がfastTextを使って分類した結果です。
『分類前』
緑:美容系 ,
青:エンタメ系 ,
赤:暮らし系
上記のように分類前は色々なジャンルの投稿内容が入り混じった状態です。
これをfastTextで分類すると以下のような形になります。
→『分類後(美容系)』
→『分類後(エンタメ系)』
→『分類後(暮らし系)』
上記のように概ね文章を「美容系」「エンタメ系」「暮らし系」などに分類することができました。 *1
このように文章からカテゴリごとに自動分類したり、スパム的な投稿内容を検知したりもできます。
作成するモデル次第でかなり応用が効きそうな感じです。
また、単語のベクトル表現をつくることで、
SNSユーザの投稿内容からおすすめを紹介するレコメンド機能なども活用事例の代表です。
fastTextをインストール
fastTextはGitHubからダウンロード可能です。
以下のコマンドでダウンロードすると実行できるようになります。
$ git clone https://github.com/facebookresearch/fastText.git $ cd fastText $ make $ pip install cython $ pip install fasttext
学習用のテキストを用意する
学習用のテキストは過去にTwitterに投稿された「美容系」と「エンタメ系」と「暮らし系」のワードを含むツイートから作成します。
TwitterのAPI Keyを用意
Twitterの内容はAPIから取得します。
TwitterのAPI Keyを持っていない場合は予め用意する必要があります。
Twitter Application Management
MeCabをインストール
日本語は英語とは異なり単語同士がスペースで区切られていないため、分かち書きをする必要があります。
今回はMeCabを利用しました。
mecab-python3
MeCabをインストールしていない場合、以下のコマンドでインストールを行います。
$ brew install mecab mecab-ipadic $ pip install mecab-python3
投稿の取得からテキスト出力までは以下のコードで行います。
TwitterのAPI Keyと取得したいカテゴリが含まれるキーワードを入力して取得します。
tweet_get.py
import re import json import MeCab from requests_oauthlib import OAuth1Session CK = "(用意したConsumer Keyを入力)" CS = "(用意したConsumer Secretを入力)" AT = "(用意したAccess Tokenを入力)" AS = "(用意したAccess Token Secretを入力)" API_URL = "https://api.twitter.com/1.1/search/tweets.json?tweet_mode=extended" KEYWORD = "芸能 OR アニメ OR 漫画 OR TV OR ゲーム" #エンタメ系のキーワードを入力 CLASS_LABEL = "__label__1" def main(): tweets = get_tweet() #ツイートを取得 surfaces = get_surfaces(tweets) #ツイートを分かち書き write_txt(surfaces) #ツイートを書き込み def get_tweet(): """ TwitterからKEYWORDに関連するツイートを取得 """ params = {'q' : KEYWORD, 'count' : 100} twitter = OAuth1Session(CK, CS, AT, AS) req = twitter.get(API_URL, params = params) results = [] if req.status_code == 200: # JSONをパース tweets = json.loads(req.text) for tweet in tweets['statuses']: results.append(tweet['full_text']) return results else: # エラー print ("Error: %d" % req.status_code) def get_surfaces(contents): """ 文書を分かち書きし単語単位に分割 """ results = [] for row in contents: content = format_text(row) tagger = MeCab.Tagger('') tagger.parse('') surf = [] node = tagger.parseToNode(content) while node: surf.append(node.surface) node = node.next results.append(surf) return results def write_txt(contents): """ 評価モデル用のテキストファイルを作成する """ try: if(len(contents) > 0): fileNema = CLASS_LABEL + ".txt" labelText = CLASS_LABEL + ", " f = open(fileNema, 'a') for row in contents: # 空行区切りの文字列に変換 spaceTokens = " ".join(row); result = labelText + spaceTokens + "\n" # 書き込み f.write(result) f.close() print(str(len(contents))+"行を書き込み") except Exception as e: print("テキストへの書き込みに失敗") print(e) def format_text(text): ''' ツイートから不要な情報を削除 ''' text=re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", text) text=re.sub(r'@[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", text) text=re.sub(r'&[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", text) text=re.sub(';', "", text) text=re.sub('RT', "", text) text=re.sub('\n', " ", text) return text if __name__ == '__main__': main()
取得後のテキストのフォーマットは以下の様に、見出しに__label__*
と分類用のラベルを付けています。
この場合、__label__1
に「美容系」、__label__2
に「エンタメ系」、__label__3
に「暮らし系」を設定しています。
分類ごとにCLASS_LABELとキーワードを変えて実行します。
__label__1,美容 室 の カラー と 自宅 で の カラー リング の 違い と は 。 。 。 __label__1,はちみつ に は 、 優れ た 保 湿 力 が あり ます 。 美容 に 優れ た 効果 が あり ます 。
__label__2,【 名 台詞 】 「 あきらめ たら そこ で 試合 終了 です よ … ? 」 某 人気 漫画 より __label__2,主 に アニメ の 絵 や 漫画 を 描い たり 、 ブログ など の 活動 を し て い ます 。 pixiv で 漫画 を 投稿 し 、 ブログ で 発表 を 繰り返し て ます 良けれ ば 話しかけ て ください
__label__3,うち の ペット の 犬 が 食後 に 家 の 中 で 穴 を 掘ろ う と する ん だ けど 、 どうして だろ う 。 __label__3,もうすぐ 修学旅行 ! 高校 生活 の ひとつ で ある 行事 楽しん で き ま ー す
今回は約3000件*3の投稿内容を用意しました。
上記のコードでは一度に100件までしか取得できないため、
何回か時間を置いて繰り返し実行してデータをためてください。
取得直後のテキストはカテゴリごとにバラバラの状態なので、CATコマンドで結合させましょう。
$ cat __label__1.txt __label__2.txt __label__3.txt > model.txt
テキストからモデルを生成する
取得した「model.txt」からfastTextのモデルを生成します。
生成は以下のコードで行います。変換自体は1行で完結します。
learning.py
import sys import fasttext as ft argvs = sys.argv input_file = argvs[1] output_file = argvs[2] classifier = ft.supervised(input_file, output_file)
input_fileに学習用のテキストファイル名、
output_fileに生成後のモデル名を設定します。
$ python learning.py model.txt model
生成が終わるとmodel.binというモデルが出力されます。
文章の分類
fastTextにモデルと新たに判断したいテキストを渡すと、
判定結果の分類とその判定が下される確率が取得できます。
判定結果が入力した文章に近いほど確率の値が高くなります。
prediction.py
import sys import fasttext as ft import MeCab class predict: def __init__(self): # モデル読み込み self.classifier = ft.load_model('model.bin') def get_surfaces(self, content): """ 文書を分かち書き """ tagger = MeCab.Tagger('') tagger.parse('') surfaces = [] node = tagger.parseToNode(content) while node: surfaces.append(node.surface) node = node.next return surfaces def tweet_class(self, content): """ ツイートを解析して分類を行う """ words = " ".join(self.get_surfaces(content)) estimate = self.classifier.predict_proba([words], k=3)[0][0] if estimate[0] == "__label__1,": print('美容系', estimate[1]) elif estimate[0] == "__label__2,": print('エンタメ系', estimate[1]) elif estimate[0] == "__label__3,": print('暮らし系', estimate[1]) if __name__ == '__main__': pre = predict() pre.tweet_class("".join(sys.argv[1:]))
実行結果
以下のようにコマンド入力し最後に判定するテキスト内容を貼り付けて実行します。
$ python prediction.py "判定するテキスト"
試しにTwitterから学習に使用していない適当なツイートで判定を行います。
python prediction.py "化粧水・乳液・美容液がひとつになった基礎化粧品が本日発売開始" 美容系 0.998047
python prediction.py "10月○日より劇場版公開予定!東京で舞台挨拶が行われました" エンタメ系 0.654297
python prediction.py "お買い得な生鮮食品を毎日お届け。格安情報はこちら。" 暮らし系 0.8125
結構いい感じに分類してくれている気がします。
また取得した投稿3000件*3を学習用、100件*3を検証用に別で用意し実験したところ、 96.333%の精度で分類できました。*2
分類が難しい点
試していく中で分類がうまくいってないところがあったのでご紹介します。
・のび太「あったかいふとんでぐっすりねる。こんな楽しいことがほかにあるか。」
(エンタメ系 0.443359 暮らし系 0.439453 美容系 0.115234)
エンタメ系も入ってますが、かなり暮らし系寄りですね。
登場人物のセリフとかになると分類はかなり難しくなりそうです。
またこのセリフにはありませんが健康などについて触れると、美容系も高くなってきます。
・メジャーリーグでも活躍した某野球選手が美容整形をカミングアウト!スタジオは大騒然!
(美容系 0.996094 エンタメ系 1.95313e-08 暮らし系 1.95313e-08)
こちらは美容整形という部分に大きく反応して美容系に寄ってしまってます。
学習に使用したテキスト次第で特定のキーワードが強くなりすぎてしまうのかもしれません。
まとめ
fastTextを使って文章を「美容系」、「エンタメ系」、「暮らし系」に分類しました。
文章の分類だけでなくネガポジ判定や特定の単語に似たワードを抽出するなどにも使えるので、活用の幅は多そうです。
思いの外、簡単に実装できたのでチャレンジしてみてはいかがでしょうか!