Np-Urのデータ分析教室

オーブンソースデータなどWeb上から入手できるデータを用いて、RとPython両方使って分析した結果を書いていきます

「OK word2vec ! "マジ卍"の意味を教えて」 Pythonでword2vec実践してみた

さて、今回はPythonによるword2vecの実践編です!

word2vecって何??という方は、こちらの記事を一読してから本記事に入って頂くといいかと思います。
www.randpy.tokyo
(こちらとても高評価頂いております。ありがとうございます!!)

上のリンク先でも書きましたが、簡単にまとめると、word2vecを使うことで単語の意味をベクトル表現化することができます。
単語の意味をベクトル表現できると、

  • 近い意味の単語を調べる
  • 単語同士の意味を足したり引いたり

といった嬉しいことが実現できます。

ということで今回は、タイトルにもある通りJK用語の代表格である”マジ卍”という言葉の意味を、word2vecに聞いてみたいと思います。
本当は"マジ卍"の意味を直接JKに聞きたいのですが、JKの知り合いも当然いないですし……まぁここは頭を使ってデータを使えばなんか分かるっしょという流れです笑

とはいいつつ、”マジ卍"の意味を事前に知っておかないと分析後の考察ができないので、一応ネットで先に意味を調べておきました。
"マジ卍"という言葉の意味はコトバンクによると、

うれしさや悲しさといった感情や、かわいさや調子の良さなどの状態を表す言葉。SNS(ソーシャル・ネットワーキング・サービス)などインターネットを中心に広がった。「まじ」は「本気」、「卍」は「やばい」を意味すると推測されているもののルーツは定かではなく、用法や使用される場面は曖昧。

引用元:まじ卍(まじまんじ)とは - コトバンク

う〜ん、なんとも曖昧な言葉ですね…笑
この抽象的表現が分析結果にどのように現れるのか楽しみにしつつ、本記事は以下のような流れで進めていきます。

  • Twitterデータの取得
  • mecabによる分かち書き
  • word2vecで単語の類似度を計算

それでは、さっそく行ってみましょう!!卍

Twitterデータの取得

Twitterデータを使った分析は本ブログでも何度か取り上げており、データの取得方法についても触れていますので、詳しくは以下記事を参考にしてみてください。
www.randpy.tokyo
基本的には以下にあげる4つのkeyを取得できれば、簡単にAPIを叩くことができます。

  • Consumer Key
  • Consumer Secret
  • Access Token
  • Access Token Secret

これらのkeyの取得方法に関しては、以下記事で紹介してます。
www.randpy.tokyo

さて前回はTwitterのrestAPIを叩いて、特定のユーザーや特定の単語が含まれるツイートを取得しましたが、今回は直近のツイートを大量に取得したいので、streaming apiを使っていきます。といっても、使い方自体はrestAPIの時とほぼほぼ変わりません。
streming apiについては、以下ブログで分かりやすくまとめられていましたので、参考にしてみてください。
Twitter Streaming APIについてのメモ

指定するendpointで取得できるデータやオプションが微妙に異なりますが、今回はstatuses/sample.jsonを使っていきたいと思います。
statuses/sample.jsonでは、ランダムサンプリングした最新のタイムラインを取得できます。

以下がツイートデータの取得コードになります。

import json
import requests
from requests_oauthlib import OAuth1
import re

#取得したkeyを定義
access_token = ''
access_token_secret = ''
consumer_key = ''
consumer_key_secret = ''

url = "https://stream.twitter.com/1.1/statuses/sample.json?language=ja"

def normalize_text(text):
    text = re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", text)
    text = re.sub('RT', "", text)
    text = re.sub('お気に入り', "", text)
    text = re.sub('まとめ', "", text)
    text = re.sub(r'[!-~]', "", text)
    text = re.sub(r'[︰-@]', "", text)
    text = re.sub('\u3000',"", text)
    text = re.sub('\t', "", text)
    text = text.strip()
    return text

# OAuth で GET
auth = OAuth1(consumer_key, consumer_key_secret, access_token, access_token_secret)

with open('~/public_text_twitter.tsv','a', encoding='utf-8') as f:
    res = requests.get(url, auth=auth, stream=True)
    for r in res.iter_lines():
        try:
            r_json = json.loads(r)
            text = r_json['text']
            f.write(normalize_text(text) + '\n')
        except:
            continue

normalize_text()関数で、不要な文字列を消去しており、取得したデータを整形してあげてます。
ツイートデータ整形コードは以下を参考にさせてもらいました。正規表現に慣れていないので本当に助かります。
Pythonで余計な文字列を削除する方法

データ整形自体はrawデータ取得後に行っても大丈夫です。というか、そのほうがベターな気がします。
延々と上記コードを走らせて、分析に使うツイートデータ数は60万近く今回取得しました。

形態素解析ツール

日本語は、単語の区切りがないので分析するためには分かち書き(単語区切り)をする必要があります。前回のword cloudの分析の際は、MeCabを使わずにjanomeというPythonのライブラリを使って分かち書きをしましたが、今回はMeCabを使っていきます。

PythonでMeCabを使うには以下の3つのステップが必要です。(ステップ2はなくてもいい)

  1. MeCabのインストール
  2. 辞書のインストール(mecab-ipadic-NEologd)
  3. mecab-python3のインストール(PythonでMeCabを動かすのに必要です)

MeCab導入に関しては、参考になる記事が既に多く出回っているので、例えば以下記事等を参考に環境をセットアップしてみてください。
Python3からMeCabを使う
RとPythonでMeCabを使う環境構築(Windows, Mac) - データ解析備忘録

mecab-ipadic-NEologdの勧め

以下のgithubページで、インストール方法、更新方法が記載されています。
github.com
デフォルトの辞書を使ってもいいですが、mecab-ipadic-neologdを使うことで、(ある程度)新単語や固有表現に対応することができるので、基本的にはこちらの辞書を入れていただくのが良いのではと思います。

特に今回扱うツイートデータは、新語や固有表現が多く混入しているテキストデータになるので、なおさらmecab-ipadic-neologdを使ったほうがいいでしょう。

MeCabでデータ整形

さて、これでMeCabを使う環境は整ったので、さっそく先程取得したデータを分かち書きしていきましょう。

#encoding:utf-8
import MeCab
import pandas as pd
import unicodedata

#データ インポート
df = pd.read_csv(open('~/public_text_twitter.tsv','rU'), sep='\t', names=['text'], encoding='utf-8',engine='c')
text_lists = df['text'].unique().tolist()

#分かち書き
mt = MeCab.Tagger("-Ochasen -d '/path/mecab-ipadic~") #自分がインストールした辞書を指定

with open('~/public_text_splited.txt', 'w', encoding='utf-8') as f:
    for text in text_lists:
        tmp_lists = []
        text = unicodedata.normalize('NFKC',str(text))
        if 'まじ卍' in text:
            text = text.replace('まじ卍','マジ卍')
        if 'マジ卍' in text:
            text_splited = text.split('マジ卍')
            for i, text in enumerate(text_splited):
                node = mt.parseToNode(text)
                while node:
                    if node.feature.startswith('名詞') or node.feature.startswith('形容詞'):
                        tmp_lists.append(node.surface)
                    node = node.next
                if i != len(text_splited)-1:
                    tmp_lists.append('マジ卍')
        else:
            node = mt.parseToNode(text)
            while node:
                if node.feature.startswith('名詞') or node.feature.startswith('形容詞'):
                    tmp_lists.append(node.surface)
                node = node.next
        f.write(' '.join(tmp_lists) + '\n')

mt = MeCab.Tagger(~)で今回インストールした辞書を指定してあげます。

さて、今回は"マジ卍"という言葉のベクトルを作りたいわけですが、普通に分かち書きするとマジと卍が分かれてしまうので、少し工夫が必要です。

まずは、"まじ卍"と"マジ卍"の表記ゆれを直す処理(今回は"マジ卍"に統一)をしてあげます。
その後、"マジ卍"で文書を一旦分割してあげて、分かち書き後の単語と"マジ卍"をリストに追加するようにしています。

また、node.feature.startswith()で、単語の品詞を指定をしており、今回は名詞と形容詞のみを使っています。
こうして、分かち書きした文書を一旦テキストファイルに保存してあげれば下準備は終了です。

gensimによるword2vec

gensimというライブラリを使うことで簡単にword2vecを行うことができます。
ライブラリをまだ入れていない方は、pipで入れときましょう。

pip install gensim

さて、以下が分析コードになります。

from gensim.models import word2vec

sentences = word2vec.LineSentence('~/public_text_splited.txt')
model = word2vec.Word2Vec(sentences,
                          sg=1,         #0: CBOW, 1: skip-gram
                          size=300,     # ベクトルの次元数
                          window=5,    # 入力単語からの最大距離
                          min_count=5,  # 単語の出現回数でフィルタリング
                          )

model.most_similar(positive='マジ卍', topn=20)

word2vec.LineSenteceで先程保存した分かち書きした文書を読み込みます。

word2vecの学習の際のオプションに関しては、以下公式サイトを参考にしてみてください。
gensim: models.word2vec – Deep learning with word2vec

most_similarのメソッドを使うことで、指定した単語と似ている単語を抽出してくれます。
(距離の近さはコサイン類似度で計算)
ということで、"マジ卍"と距離の近い上位20単語を見てみましょう。
f:id:gl2000-sans:20180202140933p:plain

ふむふむ。"ワロエナイ"、"サイテー"という言葉からネガティブな感情表現を表現していることが推察されます。

なるほど!ワロエナイとかサイテーとか使うときに、私も「マジ卍」と使えばよいのですね!
「今週毎日残業させられてるけど、うちの会社マジ卍じゃない?」
どうですか?使いこなせていますか?笑
(あ、念のため。別に私が属する会社が残業が多いわけじゃないですよ!あくまで例文です!)

ただし、その他の単語を見ると、固有名詞が多く含まれており、正直よくわからないですね……。
おそらく、一緒に使われている単語との距離が近くなっているせいかもしれません。
まだまだ改良の余地ありそうです。

可視化

ここはおまけみたいな部分ですが、t-SNEを使って300次元のベクトルを、2次元上に可視化してみたいと思います。
全ての単語だと量が多すぎてよく分からない+t-SNEが終わらないので、マジ卍と似ている単語数百個分を使って可視化してみます。
f:id:gl2000-sans:20180202145443p:plain

なかなかカオスですね。ただ卍という言葉自体が抽象度の高い表現なので、これはこれである意味正解な気がしてきました。

なお、可視化コード含めて、今回のコードは以下のgithubにあげております。
github.com

終わりに

結果はともかく、word2vecの面白さは伝わったのではないかと思います。
ツイートデータは、いわゆるノイズが多いテキストデータになるので、データのクレンジングがうまくできていないこと、あとは文書自体が一つ一つ短いというのも今回の分析結果に影響しているかなと。

その辺りは、今後もツイッターデータは使っていくと思うので、だんだんと洗練していきたいですね。

なお、R実践記事も公開しましたのでそちらも是非ご覧ください。こちらもとても面白い結果になりました!
www.randpy.tokyo