自然言語処理で分類問題をやってみた
CountVectorizer と TfidVectorizer を使って自然言語処理の分類問題をやってみました。
scikit-learn の 20newsgroup のデータセット【英語】を使っています。
コードはGoogle Colabはこちら、GitHubはこちら。
データセット
見やすいようにラベル名を追加したDataFrameに加工します。
dataset = fetch_20newsgroups(shuffle=True, random_state=SEED)
name_list = list(dataset.target_names)
df = pd.DataFrame(dataset.data)
df.columns = ["text"]
df["test_num"] = dataset.target
df["test"] = [name_list[i] for i in df["test_num"]]
Count Vectorizer
単語の出現回数をベクトル化する処理を行います。
vec_count = CountVectorizer()
train = vec_count.fit_transform(df["text"])
インデックスごとに[1, 1, 0, 2, 0, 0...]といったゼロを多く含むベクトルが作成されます。
そのため、メモリ削減のため疎行列となっている点を抑えましょう。
type(train)
出力:
scipy.sparse.csr.csr_matrix
単語ごとにカウントするため、かなり学習に時間がかかります。
X_train, X_test, y_train, y_test = train_test_split(train, df["test_num"], test_size=TEST_RATE, random_state=SEED)
model = linear_model.RidgeClassifier()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print(f1_score(y_test, y_pred, average = 'micro'))
f1スコア: 0.86
処理時間: 325.2秒
20個のマルチクラス分類ではありますが、CountVectorizerでもf1スコアは86%とかなりいい結果となりました。
処理が重いのが玉に瑕ですね。
Tfidf Vectorizer
TF(単語の出現頻度)とIDF(単語のレア度)を組み合わせたTF-IDF(索引語頻度逆文書頻度)をベクトル化する処理を行います。
vec_tfidf = TfidfVectorizer()
train = vec_tfidf.fit_transform(df["text"])
X_train, X_test, y_train, y_test = train_test_split(train, df["test_num"], test_size=TEST_RATE, random_state=SEED)
model = linear_model.RidgeClassifier()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print(f1_score(y_test, y_pred, average = 'micro'))
CountVectorizerと違い、意味はないにも関わらず出現頻度が多い単語に引っ張られなくなるため、精度が向上しています。
また、CountVectorizerに比べ圧倒的に処理が早くなっています。
f1スコア: 0.92
処理時間: 6.8秒
参考1 混合行列+ヒートマップ
混合行列をヒートマップ表示すると、特にマルチクラスの場合はどのクラスの精度が良いのかわかりやすいです。
plt.figure(figsize=(9, 9))
sns.heatmap(pd.DataFrame(confusion_matrix(y_test, y_pred)), annot=True, square=True)
参考2 TruncatedSVD で次元削減+Count Vectorizer
PCAと違い、TruncatedSVDによる主成分分析は疎行列のままでも使えるので便利です。
PCAとは異なり、この推定量は特異値分解を計算する前にデータを中央に配置しません。
これは、スパース行列を効率的に処理できることを意味します。
Google 翻訳
vec_count = CountVectorizer()
train = vec_count.fit_transform(df["text"])
for i in [10, 100, 500, 1000]:
start = time.time()
svd = TruncatedSVD(n_components=i, random_state=SEED)
feature = svd.fit_transform(train)
X_train, X_test, y_train, y_test = train_test_split(feature, df["test_num"], test_size=TEST_RATE, random_state=SEED)
model = linear_model.RidgeClassifier()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
n_components別の処理時間、精度は以下のとおり。
経過時間 | f1スコア | |
---|---|---|
10 | 1.6秒 | 0.14 |
100 | 8.2秒 | 0.50 |
500 | 35.9秒 | 0.76 |
1000 | 75.6秒 | 0.82 |
次元削減なし | 325.2秒 | 0.86 |
n_components別のヒートマップを見ると、クラスごとの精度の変化がわかります。
以上になります、最後までお読みいただきありがとうございました。
Discussion