前に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