はじめに
Discordサーバーを運営していると時々、ルール通りに文言を投稿しない人が出てきますよね。
その都度指摘してはまた別の人が繰り返しまた、指摘は繰り返し...(以下略)
※今回の記事は自分の知識整理・備忘録として記載しています。
ある日
何をしたの?
チャンネルで下記のような文言にそっているかDiscordボットで監視をして、その通りでなければ書き直すように指摘するボットを作りました。
作品名:
キャラの正確な名前:
英語の綴り:
希望する内容:
その他要望:
フローチャート
何の技術を使ったの?
ngram・LogisticRegression・TfidfVectorizer
使った言語は?
Python
実装
学習データ
下記のような形のデータを用意する。依頼だと思われるmsgに対しては1とラベル付けした。
※出所は自分のDiscordサーバーに投稿されている1万3500件程度のデータを人力でラベル付けした。
msg | req |
---|---|
よろしくお願いします。 | 0 |
AIについての学習のため参加しました。 | 0 |
忙しい所悪い!この辺って依頼してもいいっすか?? | 1 |
走っている少年の絵を描いてもらってもいいですか? | 1 |
... | ... |
データ読み込み
texts = get_csv_column_as_list('dataset.csv', 'msg')
is_requests = get_csv_column_as_list('dataset.csv', 'req')
is_requests = list(map(int, is_requests))
データフレーム作成
from janome.tokenizer import Tokenizer
# Janome初期化
t = Tokenizer()
def tokenize_text(text):
tokens = [
token.surface
for token in t.tokenize(text)
if token.part_of_speech.split(',')[0] in ['名詞', '動詞', '形容詞']
]
return " ".join(tokens)
df = pd.DataFrame({
'text': texts,
'is_request': is_requests
})
df['tokenized_text'] = df['text'].apply(tokenize_text)
全メッセージを形態素解析で品詞分解する。細かく分けることで、日本語の依頼っぽい文章の検出をやりやすくする。
パイプラインでまとめる
# パイプライン定義
pipeline = Pipeline([
('tfidf', TfidfVectorizer(ngram_range=(1, 2), max_features=1000)),
('clf', LogisticRegression(random_state=42, solver='liblinear', class_weight='balanced'))
])
tfidfの箇所
一段目のtfidfは文章(文字列)を数値のベクトルに変換することを目的としている。
・TfidfVectorizer
:単語の出現頻度を元にTF-IDFベクトルを作るクラス。
・ngram_range
:1-gram(単語)と2-gram(隣接する2単語の組)の両方を特徴量として使う。
例えば
「猫が好き」⇒「猫」「が」「好き」と「猫が」「が好き」に分けられる。
・max_features
:TF-IDF特徴量のうち上位何個までを使うか
clfの箇所
二段目のclfはtfidfでベクトル化されたテキストを使って、分類を行うことを目的としている。
LogisticRegression
:2クラス(または多クラス)の分類に使えるモデル。
random_state
:乱数シード値。本記事のプログラムは再現性を保つために固定する。
solver='liblinear'
:小~中規模のデータに適した計算アルゴリズム
class_weight='balanced'
:クラスに偏りがある場合、自動的にバラスを取ることで少数クラスを無視されにくくする。
クロスバリデーションによる分割
# スコア指標
scoring = {
'precision_macro': 'precision_macro',
'recall_macro': 'recall_macro',
'f1_macro': 'f1_macro',
'accuracy': 'accuracy'
}
# クロスバリデーション(5分割)
cv_results = cross_validate(
pipeline,
df['tokenized_text'],
df['is_request'],
cv=5,
scoring=scoring,
return_train_score=False
)
プログラムを言葉で説明すると、事前に定義したpipelineを使って、データであるdf['tokenized_text']
(入力)とdf['is_request']
(正解ラベル)を用いて、5分割で交差検証(cv=5)を行い、精度を計測する。
・scoring
:学習モデルの評価指標を複数指定できる。今回の場合、適合率・再現率・F1スコア・精度が出力される。
・return_train_score=False
:テスト側だけを評価する。Trueにすると、学習データ・テストデータの両方を評価する。
5分割で交差検証とは
データを5つに分割して、それを5回繰り返して学習と評価を行う。
※各回で、1つをテスト、残り4つを訓練に使う。
5分割交差検証結果
precision_macro | recall_macro | f1_macro | accuracy |
---|---|---|---|
0.615 | 0.838 | 0.654 | 0.913 |
再現率が0.838と依頼の見逃しは少ないが、適合率が0.6程度なので、依頼と予測したが実際は違った(誤検出)もまぁまぁあるという結果に。まだまだ改善の余地はありそう。
動作確認
依頼系のメッセージだが、形式通りのもの(ボットが反応しないのが正解)
次回もあるかも?
参考文献
・ChatGPT