Python 教師あり学習 ロジスティック回帰をScikitーlearnで試す

AI
スポンサーリンク

ロジスティック回帰について

ロジスティック回帰は、教師あり学習のアルゴリズムの一つで、回帰となっていますが、主に二値分類問題に適用されます。ロジスティック回帰は、線形回帰と同様に、特徴量と目的変数の関係をモデル化しますが、目的変数が確率(0から1の範囲の値)を取る点が異なります。以下は、ロジスティック回帰の主な特徴です。

  1. ロジスティック回帰では、シグモイド関数(別名ロジスティック関数)を使用して、線形結合の結果を0から1の範囲に変換します。これにより、確率として解釈できる予測値を得られます。

  2. ロジスティック回帰は、二値分類問題に適用されることが一般的です。予測された確率がある閾値(通常は0.5)を超える場合、正クラス(1)に分類され、それ以外の場合は負クラス(0)に分類されます。

  3. モデルのパラメータは、最尤推定法によって学習されます。この方法では、訓練データに対するモデルの予測確率が最大となるようにパラメータを調整します。

  4. 過学習を防ぐために、ロジスティック回帰モデルに正則化項(L1正則化、L2正則化など)を追加することができます。正則化項はモデルの複雑さを制限し、汎化性能を向上させる効果があります。

  5. ロジスティック回帰は、多クラス分類問題にも適用できます。これを実現する方法の一つは、一対多(One-vs-Rest)アプローチで、複数の二値分類器を組み合わせて多クラス分類を行います。また、多項ロジスティック回帰(別名ソフトマックス回帰)を用いることもできます。

ただし、ロジスティック回帰は線形モデルであるため、非線形な関係を表現する能力には限界があります。そのため、より複雑な問題や非線形なデータ構造に対処する必要がある場合、他のアルゴリズム(例えばニューラルネットワークや決定木ベースのアルゴリズム)が適切であることがあります。

また、ロジスティック回帰は特徴量のスケールに敏感です。そのため、モデルのパフォーマンスを向上させるために、データの前処理(例えば、正規化や標準化)が重要です。特徴量選択や次元削減技術(主成分分析やt-SNEなど)も、ロジスティック回帰モデルの性能を向上させる役割を果たすことがあります。

ロジスティック回帰は、そのシンプルさと解釈性の高さから、多くの実用的な問題に対して有用なモデルとして利用されています。さまざまな業界で広く使われており、金融、医療、マーケティングなどの分野で、顧客の購買行動や病気の診断、広告の効果予測などを行うために使用されています。

 

あやめの分類をロジスティック回帰で分類

あやめの花弁の長さと幅、ガクの長さと幅をセンチメートル単位で観測してあやめの品種の分類を行います。

データセットはScikit-learnで用意された学習用のデータセットを使います。 従ってデータの前処理は必要ありません。

ライブラリの読み込み

必要なライブラリやモジュールを読み込みます。

import sys
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

データの読み込み

irisのデータセットは scikit-learn の datasets モジュールに含まれている学習用サンプルです。 これはscikit-learn のload_iris() で読み込みできます。

from sklearn.datasets import load_iris
iris_dataset = load_iris()

データの内訳

Bunchクラスは、scikit-learnなどの一部のライブラリで使われる便利なデータ構造です。Bunchは、属性アクセスを持つ辞書のようなオブジェクトで、データとそのメタデータを一緒に保持することができます。Bunchクラスは、独自のデータ構造を作成せずに、より柔軟で簡単に属性へのアクセスを提供することができます。

Bunchは通常のPython辞書と同様に初期化されますが、属性アクセスを使って値にアクセスできます。

iris_dataset.keys()でiris_datasetが持つキーの一覧を確認できます。

print("key of iris_dataset: n{}".format(iris_dataset.keys()))

表示結果

key of iris_dataset: 
dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names', 'filename'])

表示結果のように6つのデータがリスト状態で格納されています。

iris_dataset が持つキーの意味

    • data : 説明変数
    • target : 目的変数(教師データ)
    • target_names : 品種 [‘setosa’ ‘versicolor’ ‘virginica’]
    • DESCR : データの説明
    • feature_names :
        • 「sepal length (cm)」:ガクの長さ
        • 「sepal width (cm)」 :ガクの幅
        • 「petal length (cm)」:花弁の長さ
        • 「petal width (cm)」:花弁の幅
    • filename : ファイル情報

データの取り出し方

キーを指定してキーで指定したデータの値を取り出します。

次のコードではfeature_names(説明変数)の項目名と、target_namesで目的変数となる「あやめの品種名」を取り出して表示しています。

print(f"特徴量の名前: {iris_dataset['feature_names']}")
print(f"アイリスの種類: {iris_dataset['target_names']}")

表示結果

特徴量の名前: ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
アイリスの種類: ['setosa' 'versicolor' 'virginica']

次に説明変数を取り出します。取り出し方は辞書と同じ方法でキーを指定してとりだします。

iris_dataset['data']

説明変数のデータをスライス

説明変数の個別の値を確認したい場合は次のコードのようにします。

[:5]の5は行数を表します。: の前は0が省略されています。つまり最初の行からindex番号4までの行を表示する意味です。 全て確認する場合は、iris_dataset[‘data’] とします。

print(f"5件だけデータ表示: n{iris_dataset['data'][:5]}")

表示結果

5件だけデータ表示: 
[[5.1 3.5 1.4 0.2]
 [4.9 3.  1.4 0.2]
 [4.7 3.2 1.3 0.2]
 [4.6 3.1 1.5 0.2]
 [5.  3.6 1.4 0.2]]

 

Pandasでデータ観察

iris_datasetをPandas DataFrame型にするとエクセルのような表(行列)で管理されていることがわかります。次のコードは説明変数のデータをPandasのDataFrame型にして表示させています。

data_pd = pd.DataFrame(iris_dataset['data'], columns=iris_dataset['feature_names'])
data_pd.head()

表示結果

  sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2

 

データの形状(各次元のサイズ)と型の確認

NumPy配列ndarrayの形状(各次元のサイズ)はshape属性で確認します。 何行何列の行列になっているか知ることができます。

print(f"Shape of data: {iris_dataset['data'].shape}")

表示結果

Shape of data: (150, 4)

データが150用意されて、特徴量が4種類あることがわかります。

iris_datasetのデータは numpy.ndarray型になっています。

print(f"Type of data: {type(iris_dataset['data'])}")
Type of data: <class 'numpy.ndarray'>

データ分布の状態を確認

targetはラベル(アイリスの種類を数値化した値)を見ることができます。 これがつまり教師データになるものです。

ラベルを表示してみると0から2までの数値で表現された3種類のアイリスのデータが、グループ分けされた状態になって格納されていることがわかります。

例えば、訓練データ80%とテストデータ20%に分割する場合、このようなデータの並びになっていると、テストデータはsetosaばかりなどと偏ったデータになります。そのため、ランダムにデータを分ける必要が出てきます。

print(f"Target:n{iris_dataset['target']}")
Target:
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2]

データの可視化

説明変数をそれぞれ組み合わせて尚且つ3分類した結果を色分けした散布図に描きます。

seabornを使うと簡単にそのようなグラフを描くことができます。品種ごとに色分けされていますので、説明変数の組み合わせでどのように分類できるか分かります。

説明変数をどのように組み合わせても、Setosa(ピンク色)が分離されやすいことがわかります。

df = pd.DataFrame(iris_dataset.data, columns=iris_dataset.feature_names)
df['target'] = iris_dataset.target
sns.pairplot(df, hue='target')

訓練データとテストデータの分離

sklearn には訓練データとテストデータを分離する train_test_split()という便利な関数が用意されています。 この関数を使うとデータはNumpy配列の状態でセットされます。

X_trainは学習用データ、X_testはテスト用データ、y_trainは学習用のラベル(正解データ)、y_testはテスト用のラベル(正解データ)が入ります。

この関数の初期値では、擬似乱数でデータをシャッフルした後に、訓練データを75%、テストデータを25%に分けてくれます。

random_state=0はシードを0にしています。

引数
test_size 小数の場合 0.0 〜 1.0 の間で指定。整数の場合レコード件数。デフォルト値として 0.25
train_size 小数の場合トレーニングデータの割合を 0.0 〜 1.0 の間で指定。整数の場合はレコード件数。指定しなかった場合や None を設定した場合はデータセット全体から test_size を引いた分のサイズ
random_state 乱数生成のシードとなる整数または、RandomState インスタンスを設定。指定しなかった場合は、Numpy のnp.random を用いて乱数をセット
shuffle データを分割する前にランダムに並び替えを行なうかどうかをTrue または False で指定。False に設定した場合、stratify を None に設定しなければいけません。(デフォルト値: True)
stratify Stratified Sampling (層化サンプリング) を行なう場合に、クラスを示す行列を設定します。 (デフォルト値: None)

層化抽出法とは

層化抽出法は、不均衡なクラス分布を持つデータセットで特に有用です。不均衡データセットでは、あるクラスのサンプル数が他のクラスのサンプル数よりもはるかに少ない場合があります。このようなデータセットをランダムに分割すると、少数クラスのサンプルが偏った分布を持つ訓練データやテストデータになる可能性があります。層化抽出法を使用することで、各クラスの比率が一貫して保持されるため、より正確なモデル評価と学習が可能になります。

訓練データとテストデータをtrain_test_split()で作成するコード例

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
  iris_dataset['data'], iris_dataset['target'], random_state=0
)

print(f"X_train shape: {X_train.shape}")
print(f"y_train shape: {y_train.shape}")
print(f"X_test shape: {X_test.shape}")
print(f"y_test shape: {y_test.shape}")
X_train shape: (112, 4)
y_train shape: (112,)
X_test shape: (38, 4)
y_test shape: (38,)

トレーニングデータが112、テストデータが38に分けられたことがわかります。

ロジスティック回帰のハイパーパラメータ

パラメータC

学習する識別境界線を教師データに対する分類間違いに対してどのくらい厳しくするかの指標。デフォルト値は1.0 Cが大きいと精度が上がりますが、過学習に陥る可能性が高くなります。

パラメータmulti_class

多クラス分類でモデルの動作を次のように決めます。

    • ovr : クラスに対して「属する・属さない」の2値で答える場合に適しています。
    • multinomial : 「どれくらい属する可能性があるか」を扱う問題に適しています。

パラメータrandom_state

モデルを学習する際にデータをランダムな順番で処理していきますが、その順番を制御するものです。 シードです。

パラメータpenalty

モデルの複雑さに対するペナルティーを表します。 値はL1とL2があり、通常はL2を選びます。

さて、次のコードが訓練データでロジスティック回帰を使いながら学習するコードです。

これまで色々説明してきましたが、実はたったこれだけのコードが一番重要な機械学習をするためのコードです。

from sklearn.linear_model import LogisticRegression
logistic_regression = LogisticRegression()

logistic_regression.fit(X_train, y_train)

精度計算

学習の結果モデルが出来上がり、このモデルを使用することで予測結果の正解率がわかります。

print(f"Train set score: {logistic_regression.score(X_train, y_train):.2f}")
print(f"Test set score: {logistic_regression.score(X_test, y_test):.2f}")
Train set score: 0.98
Test set score: 0.97

予測

モデルが出来上がると、新たなデータを使って品種を予測することができます。

次の例では、野生のアイリスを見つけてガクと花弁の長さと幅を調べてその品種を予測を行います。

    • ガクの長さ : 5センチ
    • ガクの幅 : 2.9センチ
    • 花弁の長さ : 1センチ
    • 花弁の幅 : 0.2センチ
new_data_scaled = [[5, 2.9, 1, 0.2]]
prediction = logistic_regression.predict(new_data_scaled)
print(f"Prediction: {prediction}")
print(f"Predicted target name: {iris_dataset['target_names'][prediction]}")
Prediction: [0]
Predicted target name: ['setosa']

 

主なハイパーパラメータの使い方

2クラス分類と多クラス分類の選択はハイパーパラメータで調整

2クラス分類と多クラス分類の選択は LogisticRegression() メソッドの multi_class ハイパーパラメータで行います。

今回使用したサンプルコードでは LogisticRegression() をインスタンス化するときに引数は全てデフォルト引数を使用しています。multi_classを使用することで明示的にOVR(one-vs-rest)かmultinomialを選択することができます。デフォルト値はautoですからScikit-learnが自動でどちらかを使用しています。

multi_class {‘auto’、 ‘ovr’、 ‘multinomial’}、default = ‘auto’

    • ovr : 1対その他ロジスティック回帰
    • multinomial : 多項ロジスティック回帰

別のハイパーパラメータ solver= ‘liblinear’ (最適化問題で使用するアルゴリズム)の場合は ‘ovr’を選択し、それ以外の場合は ‘multinomial’を選択します。

今回は特に何も指定していませんので、multi_class=’auto’が選択された状態です。

ペナルティ

ペナルティを加えることができます。ペナルティを加えるとバリアンスを削減することが可能になります。

ペナルティに種類は次の通りです。

penalty{‘l1’, ‘l2’, ‘elasticnet’, ‘none’}, default=’l2’

ペナルティとはつまり正則化のことで、L1ノルムとL2ノルムが主に用いられます。

正則化強度 C

ペナルティで指定した正則化の強度を指定するものです。ペナルティの種類を指定してない場合はデフォルトのL2ノルムが使われます。Cの値を大きくし過ぎると過学習になりますのでCの値で精度を調整するのは最終調整くらいにとどめておくと良いでしょう。

 

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