概要
from sklearn.feature_extraction.text
のvectorizerの種類やオプションの指定によって、カテゴリ分類の精度がどのように変わるかを実験しました。具体的には、以下の要因について比較しました。
要因 | 水準数 | 水準 |
---|---|---|
vectorizer | 3 | CountVectorizer, TfidfVectorizer, HashingVectorizer |
tokenizer | 2 | char, word |
ngram_range | 3 | (1,1), (1,3), (3,3) |
binary | 2 | True,False |
動機
sklearnでテキスト分類を使うときにvectorizerやオプションの指定をどうしたらいいかよくわからなかったので、感覚を掴むために実験しました。
結果
精度
教師データとしてlivedoorニュースコーパスから抽出したタイトルとカテゴリのペア7367件(カテゴリ数9)を用いました。半数のデータを使ってナイーブベイズで学習した分類モデルの精度を残りのデータで評価する、という試行を各学習条件に対して100回ずつ行いました。結果を以下に示します。
精度の平均値が最も高かったのは、(vectorizer, tokenizer, ngram_range, binary) = (CountVectorizer , word, (1,3), True)の条件で、平均値は0.812でした。精度の平均値が最も低かったのは、(vectorizer, tokenizer, ngram_range, binary) = (HashingVectorizer, word, (3,3), False)の条件で、平均値は0.812でした。
要因別で見ると、vectorizerについては、HashingVectorizerがCountVectorizer、TfidfVectorizerよりも全体的に小さいです。tokenizerとngram_rangeは強めの交互作用がありそうで、(tokenizer, ngram_range)=(char, (1,1))または(word, (3,3))のときに精度が低くなっていました。binaryはTrueのほうが全体的に精度が高いですが、影響は相対的に小さめにみえます。
vectorizer, tokenizer, ngram_range, binaryの4要因について対応ありの分散分析の結果を示します。対応は、試行ごとに用いた学習データの対応です。
Anova
=========================================================================
F Value Num DF Den DF Pr > F
-------------------------------------------------------------------------
vectorizer 53443.2497 2.0000 198.0000 0.0000
tokenizer 1898.8320 1.0000 99.0000 0.0000
ngram_range 29846.5163 2.0000 198.0000 0.0000
binary 3226.5334 1.0000 99.0000 0.0000
vectorizer:tokenizer 538.8225 2.0000 198.0000 0.0000
vectorizer:ngram_range 2122.0846 4.0000 396.0000 0.0000
tokenizer:ngram_range 78421.4171 2.0000 198.0000 0.0000
vectorizer:binary 133.0737 2.0000 198.0000 0.0000
tokenizer:binary 1403.4966 1.0000 99.0000 0.0000
ngram_range:binary 1179.3318 2.0000 198.0000 0.0000
vectorizer:tokenizer:ngram_range 1968.6917 4.0000 396.0000 0.0000
vectorizer:tokenizer:binary 576.0826 2.0000 198.0000 0.0000
vectorizer:ngram_range:binary 688.8808 4.0000 396.0000 0.0000
tokenizer:ngram_range:binary 955.2296 2.0000 198.0000 0.0000
vectorizer:tokenizer:ngram_range:binary 930.2602 4.0000 396.0000 0.0000
=========================================================================
すべての要因の主効果および交互作用が有意でした。
F値に注目すると、tokenizerとngram_rangeの交互作用、vectorizerの主効果が目立って大きく、ngram_rangeの主効果は交互作用がより強く出ているのでとばすと、binaryの主効果が続いています。ただしbinaryの主効果はそれより上位の効果に比べればかなり小さいです。
グラフからうける印象に沿った結果となっていることがわかります。
下位検定については、グラフを見れば雰囲気はなんとなくわかるので、今回は省略します。
時間
精度評価の際に、分類機の学習にかかる時間も計測していましたので、その結果を以下に示します。
時間はまず、外れ値はありますが、すべての条件における殆どの試行が(1回あたり)0.2秒以下となっており、試行を数百回、数千回と繰り返すケースでなければ、あまり意識する必要はなさそうです。
一応、学習時間を比較してみると、ngram_rangeの影響が強く、特にngram_rangeが(1,3)や(3,3)のときに学習時間が長くなる傾向がありそうです。またbinaryもTrueのときに学習時間が長くなる傾向がありそうです。
精度のときと同様に、vectorizer, tokenizer, ngram_range, binaryの4要因対応あり分散分析の結果を示します。
Anova
========================================================================
F Value Num DF Den DF Pr > F
------------------------------------------------------------------------
vectorizer 27.3369 2.0000 198.0000 0.0000
tokenizer 63.1572 1.0000 99.0000 0.0000
ngram_range 1095.7163 2.0000 198.0000 0.0000
binary 661.9470 1.0000 99.0000 0.0000
vectorizer:tokenizer 21.8070 2.0000 198.0000 0.0000
vectorizer:ngram_range 105.7113 4.0000 396.0000 0.0000
tokenizer:ngram_range 46.4642 2.0000 198.0000 0.0000
vectorizer:binary 11.3311 2.0000 198.0000 0.0000
tokenizer:binary 9.2247 1.0000 99.0000 0.0031
ngram_range:binary 56.8929 2.0000 198.0000 0.0000
vectorizer:tokenizer:ngram_range 21.8805 4.0000 396.0000 0.0000
vectorizer:tokenizer:binary 19.7996 2.0000 198.0000 0.0000
vectorizer:ngram_range:binary 14.5234 4.0000 396.0000 0.0000
tokenizer:ngram_range:binary 1.4395 2.0000 198.0000 0.2395
vectorizer:tokenizer:ngram_range:binary 9.6528 4.0000 396.0000 0.0000
========================================================================
tokenizer, ngram_range, binaryの三重交互作用を除くすべての交互作用と主効果が有意でした。
F値が大きいのはngram_rangeの主効果、binaryの主効果の2つで、グラフの印象と一致する結果となっています。
考察
tokenizerとngram_range
精度に関して最も影響が強かったのは、tokenizerとngram_rangeの交互作用でした。具体的には、(tokenizer, ngram_range)=(char, (1,1)) or (word, (3,3))のときに精度が低いという結果でした。(char, (1,1))は文字単位のユニグラム、(word, (3,3))は単語単位のトリグラムです。それぞれトークン化の単位として細かすぎ、荒すぎたため、精度が悪くなったと考えられます。
上記以外のtokenizerとngram_rangeの組み合わせに関しては、(word, (1,3))が少しだけ高いといえば高いですが、概ね似たような精度でした。
個人的にはそこまで精度が変わらないのであれば、形態素解析辞書を使わずに済む、(char, (3,3))が一番手軽で便利だと思いました。
なお、荒い方に関しては、より多くの学習データがある状況だったり、分類対象が長文(今回は記事タイトルで比較的短め)のケースでは、高精度が出ていた可能性もあるため、あくまで、今回用いた学習データ、評価データにおいてはの話となります。学習データの量や一文の長さと、トークン化の粗さの関係については機会があれば、別途検証してみたいです。
vectorizer
vectorizerはtokenizerとngram_rangeの交互作用の次に影響が強かったです。
水準の差をみると、CountVectorizerとTfidfVectorizerは前者のほうが少し精度がいいですが、ほぼ同程度、HashingVectorizerが相対的に少し低めという結果でした。
HashingVectorizerについては、算出のロジックをよく理解できていないうえ、ナイーブベイズ分類器似入力するために負値がでないようにするパラメータ設定を行ったりしているので、他2つと正当な比較ができているのかはよくわかりません。
CountVectorizerがTfidfVectorizerと同等以上の精度になっているのは少し意外でした。Tfidfは単語の登場頻度などを考慮した「気の利いた」ベクトル化になっているため、精度にいい影響があるかと思っていました。結果から考えれば、ナイーブベイズの学習自体が単語の登場頻度等を考慮した計算であるため、Tfidfとある意味で役割がかぶっており、入力値はシンプルなカウントベクトルのほうが適していたということなのかもしれません。ナイーブベイズ以外で学習したときにどういう傾向がでるのか気になります。
binary
影響は小さいものの、binary=Trueのときには、全体的に精度が高くなる傾向がありました。binaryオプションがTrueのとき、一つの文に単語が複数回登場していても1回のみの登場として扱われるようになります(HashingVectorizerの場合、意味合いが少し違うかもしれないのですが深く理解できていません)。分類タスクにおいては単語が何文書に登場したかが重要であり、各文書に何回登場したかという情報を含めること(つまりbinary=False)は、ノイズになるということなのかもしれません。
制限事項
今回用いたデータはライブドアニュースコーパスの記事タイトルとカテゴリです。また、学習モデルはナイーブベイズに固定しています。ベクトル化においては、注目したパラメータ以外(例えばトークンとして採用する登場回数の最小、最大しきい値やstop_wordsなどの設定)はデフォルト値を使用しています。ただしHashingVectorizerはn_featuresは学習時間短縮のため、デフォルト値(2^20)ではなく2^16としており、ナイーブベイズ学習の妨げとなる負値を登場させないため、alternative_signをTrueにしています。tokenizer=wordの条件では、分かち書きのためmecabおよび辞書としてmecab-ipa-neologdを使用しています。これらの状況以外では、結果が変わる可能性があります。
次に実装するとしたら
最も精度が高かったのは単語で分かち書きする条件でしたが、分かち書きは辞書の影響もうけるので、思考停止である程度精度高く実装したい場合は、(vectorizer, tokenizer, ngram_range, binary) = (CountVectorizer, char, (3,3), True)の条件が、良さそうな気がします。
データ数が多かったり入力が長文であることが期待できるなら単語分割にしたり、ngram_rangeを大きくすることを試し見ても良いかもしれません。
方法
再現用に実験、分析に使ったコードを記載します。ノートブック(一部ターミナル)での実行を想定しています。
環境
ターミナルで実行
% sw_vers
ProductName: macOS
ProductVersion: 11.6.1
BuildVersion: 20G224
% python -V
Python 3.8.6
インストール
pip install scikit-learn
pip install mecab-python3
pip install pingouin
pip install scipy
pip install statsmodels
pip install matplotlib
データの用意
ターミナルで実行
curl -O "https://www.rondhuit.com/download/ldcc-20140209.tar.gz" #macの場合
tar zxvf ldcc-20140209.tar.gz
以下はpythonで実行
import os
import pandas as pd
path = os.path.join("text")
categories, titles, contents = [], [], []
dirs = [ v for v in os.listdir(path) if os.path.isdir(os.path.join(path, v)) ]
for dir in dirs:
files = [os.path.join(path,dir, v) for v in os.listdir(os.path.join(path,dir)) if v.startswith(dir)]
for file in files:
with open(file, "r", encoding="utf-8") as f:
lines = f.read().splitlines()
categories.append(dir)
titles.append(lines[2])
contents.append(" ".join(lines[3:]))
df = pd.DataFrame({
"category": categories,
"title": titles,
"content": contents
})
df.to_csv("livedoor_news.tsv", sep="\t", index=False,header=None)
データの読み込み
import pandas as pd
df = pd.read_csv("livedoor_news.tsv", sep="\t", names=["label", "title", "text"])
df.head()
label title text
0 movie-enter 【DVDエンター!】誘拐犯に育てられた女が目にした真実は、孤独か幸福か 2005年11月から翌2006年7月まで読売新聞にて連載された、直木賞作家・角田光代による...
1 movie-enter 藤原竜也、中学生とともにロケット打ち上げに成功 「アンテナを張りながら生活をしていけばいい」 2月28日、映画『おかえり、はやぶさ』(...
2 movie-enter 『戦火の馬』ロイヤル・プレミアにウィリアム王子&キャサリン妃が出席 3月2日より全国ロードショーとなる、スティーブン・スピルバーグの待望の監督最新作『戦火の馬...
3 movie-enter 香里奈、女子高生100人のガチンコ質問に回答「ラーメンも食べる」 女優の香里奈が18日、都内で行われた映画『ガール』(5月26日公開)の女子高生限定試写会に...
4 movie-enter ユージの前に立ちはだかったJOY「僕はAKBの高橋みなみを守る」 5日、東京・千代田区の内幸町ホールにて、映画『キャプテン・アメリカ/ザ・ファースト・アベン...
学習用の関数の定義
以下の関数は、与えられたvectorizer, modelを用いて、100回学習試行をし、試行ごとの精度と所要時間のリストをします。
#vectorizerをimport
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer, HashingVectorizer
#multinomialnbをimport
from sklearn.naive_bayes import MultinomialNB
# accuracy_scoreをimport
from sklearn.metrics import accuracy_score
# train_test_splitをimport
from sklearn.model_selection import train_test_split
# timeをimport
import time
# 100回学習して、試行ごとの精度と所要時間のリストを返す
def calc_accuracy(texts, labels, model, vectorizer):
X = vectorizer.fit_transform(texts)
scores, times = [], []
for random_state in range(100):
X_train, X_test, y_train, y_test = train_test_split(X, labels, test_size=0.5, shuffle=True, random_state=random_state, stratify=labels)
start = time.time()
model.fit(X_train, y_train)
end = time.time()
times.append(end - start)
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
scores.append(accuracy)
return scores, times
以下の関数は日本語テキストをmecabで分かち書きしたリストにして返します。
mecabの辞書はデフォルトのものを使用しており、筆者の環境ではmecab-ipadic-neologdです。
import MeCab
mecab = MeCab.Tagger("-Owakati")
def parse_text(text):
return mecab.parse(text).strip().split()
条件ごとの学習結果の取得
#vectorizerをimport
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer, HashingVectorizer
#multinomialnbをimport
from sklearn.naive_bayes import MultinomialNB
scores, times = [],[]
models, vectorizers = [], []
tokenizers, ngram_ranges, binaries = [],[],[]
random_states = []
# ngram_rangeとbinaryだけはループ変数として用意し、その他の条件はループの中で直接書く。
for ngram_range in [(1,1),(1,3),(3,3)]: # ngram_rangeの水準パターン
for binary in [True, False]: # binaryの水準パターン
#ループの進捗確認用の出力
print("ngram_range: {}, binary: {}".format(ngram_range, binary))
# ナイーブベイズモデルを宣言
model = MultinomialNB()
i=-1
# 進捗確認用の出力
i+=1; print(i)
# CountVectorizer、tokenizerはmecab
vectorizer = CountVectorizer(tokenizer=parse_text, ngram_range=ngram_range, binary=binary)
s, t = calc_accuracy(df.title, df.label, model, vectorizer)
scores += s
times += t
models += ["MultinomialNB"] * len(s)
vectorizers += ["CountVectorizer"] * len(s)
tokenizers += ["mecab-neologd"] * len(s)
ngram_ranges += ["({},{})".format(ngram_range[0], ngram_range[1])] * len(s)
binaries += [binary] * len(s)
random_states += list(range(100))
i+=1; print(i)
# CountVectorizer、tokenizerはchar
vectorizer = CountVectorizer(analyzer="char", ngram_range=ngram_range, binary=binary)
s, t = calc_accuracy(df.title, df.label, model, vectorizer)
scores += s
times += t
models += ["MultinomialNB"] * len(s)
vectorizers += ["CountVectorizer"] * len(s)
tokenizers += ["char"] * len(s)
ngram_ranges += ["({},{})".format(ngram_range[0], ngram_range[1])] * len(s)
binaries += [binary] * len(s)
random_states += list(range(100))
i+=1; print(i)
# TfidfVectorizer、tokenizerはmecab
vectorizer = TfidfVectorizer(tokenizer=parse_text, ngram_range=ngram_range, binary=binary)
s, t = calc_accuracy(df.title, df.label, model, vectorizer)
scores += s
times += t
models += ["MultinomialNB"] * len(s)
vectorizers += ["TfidfVectorizer"] * len(s)
tokenizers += ["mecab-neologd"] * len(s)
ngram_ranges += ["({},{})".format(ngram_range[0], ngram_range[1])] * len(s)
binaries += [binary] * len(s)
random_states += list(range(100))
i+=1; print(i)
# TfidfVectorizer、tokenizerはchar
vectorizer = TfidfVectorizer(analyzer="char", ngram_range=ngram_range,binary=binary)
s, t = calc_accuracy(df.title, df.label, model, vectorizer)
scores += s
times += t
models += ["MultinomialNB"] * len(s)
vectorizers += ["TfidfVectorizer"] * len(s)
tokenizers += ["char"] * len(s)
ngram_ranges += ["({},{})".format(ngram_range[0], ngram_range[1])] * len(s)
binaries += [binary] * len(s)
random_states += list(range(100))
i+=1; print(i)
# HashingVectorizer、tokenizerはmecab
vectorizer = HashingVectorizer(tokenizer=parse_text, ngram_range=ngram_range, alternate_sign=False, n_features=2**16, binary=binary)
s, t = calc_accuracy(df.title, df.label, model, vectorizer)
scores += s
times += t
models += ["MultinomialNB"] * len(s)
vectorizers += ["HashingVectorizer"] * len(s)
tokenizers += ["mecab-neologd"] * len(s)
ngram_ranges += ["({},{})".format(ngram_range[0], ngram_range[1])] * len(s)
binaries += [binary] * len(s)
random_states += list(range(100))
i+=1; print(i)
# HashingVectorizer、tokenizerはchar
vectorizer = HashingVectorizer(analyzer="char", ngram_range=ngram_range, alternate_sign=False,n_features=2**16,binary=binary)
s, t = calc_accuracy(df.title, df.label, model, vectorizer)
scores += s
times += t
models += ["MultinomialNB"] * len(s)
vectorizers += ["HashingVectorizer"] * len(s)
tokenizers += ["char"] * len(s)
binaries += [binary] * len(s)
ngram_ranges += ["({},{})".format(ngram_range[0], ngram_range[1])] * len(s)
random_states += list(range(100))
結果の保存
#結果のデータフレーム化
df_accuracy = pd.DataFrame({
"model": models,
"vectorizer": vectorizers,
"tokenizer": tokenizers,
"ngram_range": ngram_ranges,
"binary": binaries,
"random_state": random_states,
"accuracy": scores,
"time": times
})
#結果の保存
df_accuracy.to_csv("df_accuracy.tsv", index=False, sep="\t")
結果の読み込み、確認
#結果の読み込み
df_accuracy = pd.read_csv("df_accuracy.tsv", sep="\t")
#結果の出力
df_accuracy.head()
要約情報の取得、保存
分析には直接関係ないですが、各条件の平均値、標準偏差を数値で確認しておきます。
# 結果の概要の表示
# accuracyのmeanを取得
df_describe = df_accuracy.groupby(["model", "vectorizer", "tokenizer", "ngram_range","binary"],as_index=False)["accuracy"].mean()
# accuracyの列名をaccuracy_meanに変更
df_describe.columns = ["model", "vectorizer", "tokenizer", "ngram_range", "binary","accuracy_mean"]
# accuracyのstdを取得
df_describe["accuracy_std"] = df_accuracy.groupby(["model", "vectorizer", "tokenizer", "ngram_range","binary"],as_index=False)["accuracy"].std()["accuracy"]
# timeのmeanを取得
df_describe["time_mean"] = df_accuracy.groupby(["model", "vectorizer", "tokenizer", "ngram_range","binary"],as_index=False)["time"].mean()["time"]
# timeのstdを取得
df_describe["time_std"] = df_accuracy.groupby(["model", "vectorizer", "tokenizer", "ngram_range","binary"],as_index=False)["time"].std()["time"]
# 結果の保存
df_describe.to_csv("df_describe.tsv",sep="\t",index=False)
# 結果の表示
df_describe.head()
model vectorizer tokenizer ngram_range binary accuracy_mean accuracy_std time_mean time_std
0 MultinomialNB CountVectorizer char (1,1) False 0.709704 0.006653 0.013404 0.003324
1 MultinomialNB CountVectorizer char (1,1) True 0.723350 0.006072 0.020566 0.013112
2 MultinomialNB CountVectorizer char (1,3) False 0.797581 0.005588 0.059319 0.007868
3 MultinomialNB CountVectorizer char (1,3) True 0.807527 0.005507 0.090673 0.020877
4 MultinomialNB CountVectorizer char (3,3) False 0.798833 0.005596 0.041194 0.005625
グラフの表示保存
accuracyに関する箱ひげ図を作成します。
#accuracyの全結果
import matplotlib.pyplot as plt
fig = df_accuracy.boxplot(column="accuracy", by=["vectorizer", "tokenizer", "ngram_range","binary"],rot=90, figsize=(10,5))
fig.set_ylabel("accuracy")
fig.set_ylim(0,1)
plt.savefig("./accuracy_boxplot.png", bbox_inches="tight", pad_inches=0.1)
plt.show()
timeに関する箱ひげ図を作成します。
fig = df_accuracy.boxplot(column="time", by=["vectorizer", "tokenizer", "ngram_range","binary"],rot=90, figsize=(10,5))
fig.set_ylabel("time (sec) per single train")
fig.set_ylim(0,1)
plt.savefig("./time_boxplot.png", bbox_inches="tight", pad_inches=0.1)
plt.show()
分散分析
必要ライブラリを読み込みます。
import statsmodels.api as sm
from statsmodels.formula.api import ols
import pandas as pd
import numpy as np
import statsmodels.stats.anova as anova
accuracyについての分散分析を実行します。
aov = anova.AnovaRM(df_accuracy, "accuracy", "random_state",within=["vectorizer", "tokenizer", "ngram_range","binary"])
result = aov.fit()
print(result)
timeについての分散分析を実行します。
aov = anova.AnovaRM(df_accuracy, "time", "random_state",within=["vectorizer", "tokenizer", "ngram_range","binary"])
result = aov.fit()
print(result)
下位検定
結果では触れていませんが、下位検定をする場合は以下で可能です。
モジュールをimportします。
#対応ありt検定のモジュールをimport
from scipy import stats
from statsmodels.stats.multicomp import pairwise_tukeyhsd
import pingouin as pg
主効果に関する下位検定(t検定)をします。
#vectorizerのt検定
posthocs = pg.pairwise_ttests(dv='accuracy', within='vectorizer', subject='random_state', padjust="bonf",data=df_accuracy)
print(posthocs.round(3))
#tokenizerのt検定
posthocs = pg.pairwise_ttests(dv='accuracy', within='tokenizer', subject='random_state', padjust="bonf",data=df_accuracy)
print(posthocs.round(3))
#ngram_rangeのt検定
posthocs = pg.pairwise_ttests(dv='accuracy', within='ngram_range', subject='random_state', padjust="bonf",data=df_accuracy)
print(posthocs.round(3))
#binaryのt検定
posthocs = pg.pairwise_ttests(dv='accuracy', within='binary', subject='random_state', padjust="bonf",data=df_accuracy)
print(posthocs.round(3))
Contrast A B Paired Parametric \
0 vectorizer CountVectorizer HashingVectorizer True True
1 vectorizer CountVectorizer TfidfVectorizer True True
2 vectorizer HashingVectorizer TfidfVectorizer True True
T dof alternative p-unc p-corr p-adjust BF10 hedges
0 300.048 99.0 two-sided 0.0 0.0 bonf 5.785e+143 20.653
1 132.300 99.0 two-sided 0.0 0.0 bonf 1.019e+109 7.593
2 -206.300 99.0 two-sided 0.0 0.0 bonf 6.997e+127 -12.589
Contrast A B Paired Parametric T dof \
0 tokenizer char mecab-neologd True True 43.576 99.0
alternative p-unc BF10 hedges
0 two-sided 0.0 5.158e+62 3.985
Contrast A B Paired Parametric T dof alternative \
0 ngram_range (1,1) (1,3) True True -216.416 99.0 two-sided
1 ngram_range (1,1) (3,3) True True 48.683 99.0 two-sided
2 ngram_range (1,3) (3,3) True True 247.933 99.0 two-sided
p-unc p-corr p-adjust BF10 hedges
0 0.0 0.0 bonf 7.548e+129 -15.472
1 0.0 0.0 bonf 1.649e+67 5.106
2 0.0 0.0 bonf 4.502e+135 20.772
Contrast A B Paired Parametric T dof alternative p-unc \
0 binary False True True True -56.803 99.0 two-sided 0.0
BF10 hedges
0 3.563e+73 -1.222
ngram_rangeとtokenizerの単純主効果の分析をします。
#ngram_rangeとtokenizerの単純主効果分析
posthocs = pg.pairwise_ttests(dv='accuracy', within=['tokenizer', 'ngram_range'], subject='random_state', padjust="bonf",data=df_accuracy)
print(posthocs.round(3))
Contrast tokenizer A B Paired \
0 tokenizer - char mecab-neologd True
1 ngram_range - (1,1) (1,3) True
2 ngram_range - (1,1) (3,3) True
3 ngram_range - (1,3) (3,3) True
4 tokenizer * ngram_range char (1,1) (1,3) True
5 tokenizer * ngram_range char (1,1) (3,3) True
6 tokenizer * ngram_range char (1,3) (3,3) True
7 tokenizer * ngram_range mecab-neologd (1,1) (1,3) True
8 tokenizer * ngram_range mecab-neologd (1,1) (3,3) True
9 tokenizer * ngram_range mecab-neologd (1,3) (3,3) True
Parametric T dof alternative p-unc p-corr p-adjust BF10 \
0 True 43.576 99.0 two-sided 0.0 NaN NaN 5.158e+62
1 True -216.416 99.0 two-sided 0.0 0.0 bonf 7.548e+129
2 True 48.683 99.0 two-sided 0.0 0.0 bonf 1.649e+67
3 True 247.933 99.0 two-sided 0.0 0.0 bonf 4.502e+135
4 True -229.806 99.0 two-sided 0.0 0.0 bonf 2.677e+132
5 True -173.003 99.0 two-sided 0.0 0.0 bonf 2.368e+120
6 True -7.464 99.0 two-sided 0.0 0.0 bonf 2.666e+08
7 True -69.816 99.0 two-sided 0.0 0.0 bonf 1.301e+82
8 True 257.836 99.0 two-sided 0.0 0.0 bonf 2.078e+137
9 True 356.651 99.0 two-sided 0.0 0.0 bonf 1.29e+151
hedges
5 -20.525
6 -0.607
7 -4.692
8 32.934
9 37.888
おわりに
scikit-learnでのテキスト分類を最近勉強し始めたのですが、パラメータが多く、何をどう設定すればよいかあまりわからない状態でした。
必ずしも一般性のある結果ではありませんが、各種パラメータが精度にどう影響を与えるのかの感覚がつかめた気がしたので良かったです。
異なる学習モデルやコーパスで学習する検証も機会があればやってみたいです。
参考
scikit-learnのドキュメント。特に、CountVectorizer, TfidfVectorizer, HashingVectorizerに関する部分。
https://scikit-learn.org/stable/modules/classes.html#module-sklearn.feature_extraction.text
対応あり分散分析のパッケージ
https://www.statsmodels.org/dev/generated/statsmodels.stats.anova.AnovaRM.html
pairwise_ttest用のパッケージ
https://pingouin-stats.org/generated/pingouin.ttest.html