Help us understand the problem. What is going on with this article?

Twitter 誹謗中傷撃退マシン(最強版)

前にQiitaに書いた「Twitter 誹謗中傷撃退マシン」の記事は出来が良かったのにLGTMが伸び悩んでたため、疑問に思っておりましたところ。いちいち誹謗中傷ワードを配列に入れるのを面倒くさがられたのではと思いDeep Learningの力を借りました。
本記事の目的は前回同様

SNSの誹謗中傷をテクノロジーの力で救うこと

です
では、行ってみましょう!!!

事前知識

前回の記事で事前知識を学習して進めてください
Twitter 誹謗中傷撃退マシン

誹謗中傷識別AIを作ろう

word2vec, RNN(LSTM)を使ってモデルを作ります。データは"umich-sentiment-train.txt"という評判分析によく使われるデータセットを使います。

word2vecモデル

Kerasを使って作成します。word2vecとは一言で言うと人間の言葉をベクトル(数字)におくアルゴリズムです。

    word2vec_model = Sequential()
    word2vec_model.add(Embedding(input_dim=vocab_size, output_dim=EMBEDDING_SIZE,
                                 embeddings_initializer='glorot_uniform',
                                 input_length=WINDOW_SIZE * 2))
    word2vec_model.add(Lambda(lambda x: K.mean(x, axis=1), output_shape=(EMBEDDING_SIZE,)))
    word2vec_model.add(Dense(vocab_size, kernel_initializer='glorot_uniform', activation='softmax'))

    word2vec_model.compile(loss='categorical_crossentropy', optimizer="adam", metrics=["accuracy"])
    word2vec_model.fit(Xtrain, ytrain, batch_size=BATCH_SIZE, epochs=NUM_EPOCHS, validation_data=(Xtest, ytest))
    # evaluate
    word2vec_score, word2vec_acc = word2vec_model.evaluate(Xtest, ytest, batch_size=BATCH_SIZE)
    print("word2vec Test score: {:.3f}, accuracy: {:.3f}".format(word2vec_score, word2vec_acc))
    # get embedding_weights
    embedding_weights = word2vec_model.layers[0].get_weights()[0]

RNN(LSTM)モデル

RNN(LSTM)とは時系列データを扱うのに特化したAIモデルです。株価予測・機械翻訳とかにも応用されてます。

    rnn_model = Sequential()
    rnn_model.add(Embedding(vocab_size, EMBEDDING_SIZE, input_length=MAX_SENTENCE_LENGTH,
                            weights=[embedding_weights], trainable=True))
    rnn_model.add(Dropout(0.5))
    rnn_model.add(LSTM(HIDDEN_LAYER_SIZE, dropout=0.5, recurrent_dropout=0.5))
    rnn_model.add(Dense(1))
    rnn_model.add(Activation("sigmoid"))

    rnn_model.compile(loss="binary_crossentropy", optimizer="adam",
                      metrics=["accuracy"])
    rnn_model.fit(Xtrain, ytrain, batch_size=BATCH_SIZE, epochs=NUM_EPOCHS, validation_data=(Xtest, ytest))
    # evaluate
    rnn_score, rnn_acc = rnn_model.evaluate(Xtest, ytest, batch_size=BATCH_SIZE)
    print("rnn Test score: {:.3f}, accuracy: {:.3f}".format(rnn_score, rnn_acc))
    # save model
    rnn_model.save(os.path.join(DATA_DIR, "sentence_analyzing_rnn.hdf5"))
rnn_model.add(Embedding(vocab_size, EMBEDDING_SIZE, input_length=MAX_SENTENCE_LENGTH,
                            weights=[embedding_weights], trainable=True))

ここで先ほどのword2vecで取得した重りを使い学習を進め"sentence_analyzing_rnn.hdf5"として保存します。

「Twitter API + 誹謗中傷識別AI」合体

これらを前回記事にした誹謗中傷撃退マシンと合体させます。

# coding=utf-8
import collections
import os
import json
import nltk
import codecs
from requests_oauthlib import OAuth1Session
from keras.models import Sequential, load_model
from keras.preprocessing import sequence

# 認証処理
CK = 'YOUR OWN'
CS = 'YOUR OWN'
AT = 'YOUR OWN'
ATS = 'YOUR OWN'
twitter = OAuth1Session(CK, CS, AT, ATS)

# ツイート検索エンドポイント
url = 'https://api.twitter.com/1.1/search/tweets.json'
# ユーザーブロックエンドポイント
url2 = 'https://api.twitter.com/1.1/blocks/create.json'
# Setting parameter
DATA_DIR = "./data"
MAX_FEATURES = 2000
MAX_SENTENCE_LENGTH = 40

if os.path.exists(os.path.join(DATA_DIR, "sentence_analyzing_rnn.hdf5")):
    # Read training data and generate word2index
    maxlen = 0
    word_freqs = collections.Counter()
    with codecs.open(os.path.join(DATA_DIR, "umich-sentiment-train.txt"), "r", 'utf-8') as ftrain:
        for line in ftrain:
            label, sentence = line.strip().split("\t")
            try:
                words = nltk.word_tokenize(sentence.lower())
            except LookupError:
                print("Englisth tokenize does not downloaded. So download it.")
                nltk.download("punkt")
                words = nltk.word_tokenize(sentence.lower())
            maxlen = max(maxlen, len(words))
            for word in words:
                word_freqs[word] += 1

    vocab_size = min(MAX_FEATURES, len(word_freqs)) + 2
    word2index = {x[0]: i + 2 for i, x in enumerate(word_freqs.most_common(MAX_FEATURES))}
    word2index["PAD"] = 0
    word2index["UNK"] = 1
    # load model
    model = load_model(os.path.join(DATA_DIR, "sentence_analyzing_rnn.hdf5"))
    # エンドポイントへ渡すパラメーター
    target_account = '@hoge '  # リプライされたアカウント
    keyword = '@' + target_account + 'exclude:retweets'  # RTは除外

    params = {
        'count': 50,  # 取得するtweet数
        'q': keyword,  # 検索キーワード
    }
    req = twitter.get(url, params=params)

    if req.status_code == 200:
        res = json.loads(req.text)
        for line in res['statuses']:
            target_text = line['text'].replace(target_account, "")
            test_words = nltk.word_tokenize(target_text.lower())
            test_seqs = []
            for test_word in test_words:
                if test_word in word2index:
                    test_seqs.append(word2index[test_word])
                else:
                    test_seqs.append(word2index["UNK"])

            Xsent = sequence.pad_sequences([test_seqs], maxlen=MAX_SENTENCE_LENGTH)
            ypred = model.predict(Xsent)[0][0]
            if ypred < 0.5:
                params2 = {'user_id': line['user']['id']}  # ブロックするユーザー
                req2 = twitter.post(url2, params=params2)

                if req2.status_code == 200:
                    print("Blocked !!")
                else:
                    print("Failed2: %d" % req2.status_code)
    else:
        print("Failed: %d" % req.status_code)
else:
    print ("AI model doesn't exist")

# 注意:Twitter APIは1週間以上前のリプを検索ヒットさせることができない
# 注意:明らかな攻撃的なリプはそもそもヒットしない

まずモデルを走らせるために単語をIDにおく必要があるのでword2indexを作成する。
リプライされたアカウントを指定してメッセージを受け取りAIに通す。
悪口だと0,褒め言葉だと1に近くなるよう分類される。
そして、閾値0.5未満だと悪口としてそのアカウントをブロックする。

総括

日本語は単語ID化も難しい上に良質なデータセットもないため、本プログラムは英語のみの対応となっています。
このプログラムが少しでも世の中に役立つことを切に祈ってます。

ちなみにこれが私のTwitter垢です。気軽にメッセージどうぞ!
https://twitter.com/downtownakasiya

pigretyasushi
細々とプログラミングをやってるものです。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away