【Python】LightGBMとtf-idfで自然言語処理モデルを作る方法

Python
スポンサーリンク

自然言語処理の機械学習モデルはBERTが有名ですが、BERTの計算ではGPUが必要です。
しかしGPUは高いので、CPUで簡易的に計算したい場面もありますよね。
そんな場合はLightGBMを使ってモデルを作成しましょう。

ステップは以下のとおりです。
①文章をベクトル化して数値にする
②LightGBMにベクトル化した文章を入れて学習させる

当ブログでは、もし私が機械学習初心者だったらどう勉強するかも解説しています。
>>コスパよく機械学習を勉強するロードマップ

fetch_20newsgroups

import pandas as pd
from sklearn.datasets import fetch_20newsgroups

train_data = fetch_20newsgroups(subset = 'train')
test_data = fetch_20newsgroups(subset = 'test')

train = pd.DataFrame({"text" : train_data["data"], "target" : train_data["target"]})
test = pd.DataFrame({"text" : test_data["data"], "target" : test_data["target"]})

train
fetch20のデータ

scikit-learnにある”fetch_20newsgroups”というデータセットを使います。
“train”が学習用、”test”が評価用のデータです。
“text”の文章から”target”のラベルを予測するモデルを作りましょう。

print(train["text"].values[0])
サンプル文章

このように1行ずつ文章のデータが入っています。

print(sorted(train["target"].unique()))
print(sorted(test["target"].unique()))

# ========== output ==========
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

“target”は0 ~ 19までの20ラベルあります。

tf-idf

from sklearn.feature_extraction.text import TfidfVectorizer

tfidf = TfidfVectorizer(max_features = 200)
X_train = tfidf.fit_transform(train["text"].values)
X_test = tfidf.transform(test["text"].values)

print(X_train.shape, X_test.shape)

y_train = train["target"].values
y_test = test["target"].values
print(y_train.shape, y_test.shape)

# ========== output ==========
# (11314, 200) (7532, 200)
# (11314,) (7532,)

機械学習では、データは数値として入れる必要があります。
なので、文章データを学習させたい場合は、文章を数値に変換しなければいけません。

scikit-learnにある”TfidfVectorizer”を使いましょう。
fit_transform: 変換方法の定義と変換まで実行
transform: 変換だけ実行

本来は学習データしか知らないはずなので、trainのだけでfit_transformをしました。
“max_features”はデータの次元数です。
大きいほどデータの表現力が上がりますが、計算が重くなるので注意しましょう。

モデル作成(LightGBM)

import lightgbm as lgbm
import matplotlib.pyplot as plt

# データ形式の変換
train_set = lgbm.Dataset(X_train, y_train)
test_set = lgbm.Dataset(X_test, y_test)

# パラメータ設定
params = {"objective": "multiclass", # 他クラス分類
          "num_class": 20,           # クラス数
          "verbosity": -1,
          }

# ログ保存用の変数
history = {}

# 学習
model = lgbm.train(
    params = params,
    train_set = train_set,
    valid_sets = [train_set, test_set],
    num_boost_round = 100,
    callbacks = [lgbm.callback.record_evaluation(history), lgbm.callback.early_stopping(10)],
)

plt.plot(history["training"]["multi_logloss"], label = "train")
plt.plot(history["valid_1"]["multi_logloss"], label = "test")
plt.legend()
plt.show()
学習経過

LightGBMの使い方はこちらの記事で解説しているので、割愛します。
“objective”を”multiclass”にして、”num_class”を20にしましょう。
徐々にlossが下がって、70回目くらいで改善が止まっていますね。

予測結果

pred = model.predict(X_test).argmax(axis = 1)
print(pred[:5])
print(y_test[:5])

# ========== output ==========
# [ 3  1 15 16 19]
# [ 7  5  0 17 19]

predictで予測します。
返り値は各ラベルに該当する確率なので、今回は20列あります。

argmaxを使って、最も値の大きい列番号を取りましょう。
5個だけ予測と答えを並べてみると、最後の19だけあっていますね。

from sklearn.metrics import accuracy_score
print(accuracy_score(pred, y_test))

# ========== output ==========
# 0.3961763143919278

正解率は39%でかなり悪いですね。

max_featuresと予測精度

tfidf = TfidfVectorizer(max_features = 1000)
X_train = tfidf.fit_transform(train["text"].values)
X_test = tfidf.transform(test["text"].values)

print(X_train.shape, X_test.shape)

y_train = train["target"].values
y_test = test["target"].values
print(y_train.shape, y_test.shape)

# ========== output ==========
# (11314, 1000) (7532, 1000)
# (11314,) (7532,)

正解率が悪い原因として、tf-idfの表現力が弱かったことが考えられます。
“max_features”を200から1000に上げて列数を増やしてみましょう。

# データ形式の変換
train_set = lgbm.Dataset(X_train, y_train)
test_set = lgbm.Dataset(X_test, y_test)

# パラメータ設定
params = {"objective": "multiclass", # 他クラス分類
          "num_class": 20,           # クラス数
          "verbosity": -1,
          }

# ログ保存用の変数
history = {}

# 学習
model = lgbm.train(
    params = params,
    train_set = train_set,
    valid_sets = [train_set, test_set],
    num_boost_round = 100,
    callbacks = [lgbm.callback.record_evaluation(history), lgbm.callback.early_stopping(10)],
)

plt.plot(history["training"]["multi_logloss"], label = "train")
plt.plot(history["valid_1"]["multi_logloss"], label = "test")
plt.legend()
plt.show()
特徴量を増やした結果

max_features = 200の時と比べてlossが1.5以下まで下がっていますね。

pred = model.predict(X_test).argmax(axis = 1)
print(pred[:5])
print(y_test[:5])
print(accuracy_score(pred, y_test))

# ========== output ==========
# [ 4 12  0  0 19]
# [ 7  5  0 17 19]
# 0.6638343069569835

正解率も66%まで上がりました。
もっと精度を伸ばしたいなら”max_features”をさらに増やすこともできます。
ですが列数が多いほど計算時間が長くなることに注意しましょう。

まとめ

今回はtf-idfとLightGBMで自然言語処理モデルを作る方法を解説しました。
CPUで手軽に実装できる方法なのでぜひ試してみてください!
GPUを持っていてさらに精度を高めたい場合は、BERTを試してみましょう。

なんか適当に独学してるだけで、

どうやって勉強を進めたらいいかわからんな。。。

と悩んでいる人向けに、
もし私が初心者ならどう勉強するかを解説しているので、参考にどうぞ。
>>コスパよく初心者が機械学習を勉強する方法|ロードマップ

コメント

タイトルとURLをコピーしました