#author("2023-12-10T20:41:08+09:00","default:cmdsadmin","cmdsadmin")
//【このページは現在作成中です】
#author("2024-05-07T10:38:26+09:00","default:cmdsadmin","cmdsadmin")

* 第6回:教師あり学習:分類モデル [#l704774c]

#contents

* 6.1 教師あり学習・分類 [#x5e7b9d3]

[[第5回]]に教師あり学習・回帰を扱いました.教師あり学習には他に''分類''もあります.

** 分類の概念的理解 [#j222383c]

◇ 広い意味を持つ分類 (classification) 

CENTER:&ref(./classification.png,40%);~
CENTER:図1:生物分類学的階級【[[Mykinso>https://lab.mykinso.com/chisiki/classification/]]】

大辞林より

+ ある基準に従って,物事を似たものどうしにまとめて分けること
+(論理学)物事を徹底的に区分し,類種系列の形をとった体系を形成すること

◇ 教師あり学習・分類 (supervised learning - classification) 

+ 「あるデータがどのクラスに属するかの予測」を指すものである【[[Smiley>https://aismiley.co.jp/ai_news/supervised-learning/#:~:text=%E5%88%86%E9%A1%9E%E3%81%AF%E3%80%8C%E3%81%82%E3%82%8B%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8C,%E3%81%8C%E5%8F%AF%E8%83%BD%E3%81%AB%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82]]】
+ アルゴリズムを用いて,テスト・データを特定のカテゴリーに正確に割り当てることである.データセット内の特定のエンティティを認識し,そのエンティティがどのようにラベルを付けられる,または定義されるべきかについて,何らかの結論を導き出そうとする【[[IBM>https://www.ibm.com/jp-ja/topics/supervised-learning]]】
+ 推論で出力された予測値により,事前に定義された複数の分類カテゴリー(=機械学習では基本的に「クラス:class」と呼ぶ)の中のどれに最も該当するかを判別すること【[[@IT>https://atmarkit.itmedia.co.jp/ait/articles/2006/08/news031.html]]】

CENTER:&ref(./sample_000.png,60%);~
CENTER:図2:アヤメ分類問題のクラス別に概要を見てみる

** 教師あり学習「分類」と他手法の比較 [#e45485c9]

◇ 教師あり学習「分類」と「回帰」の違い 

+ 対象のデータが属するクラスを予測するときは分類,値を予測するときは回帰となる 【[[Avintonジャパン>https://avinton.com/academy/classification-regression/#:~:text=%E3%81%BE%E3%81%A8%E3%82%81,%E3%81%A8%E3%81%8D%E3%81%AF%E5%9B%9E%E5%B8%B0%E3%81%A8%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82]]】
+  「非連続値つまり離散値を使って振り分けるか,連続値を使って別の数値を予測するか」である.分類は振り分けるもの,回帰は数値を予測するものである【[[AINOW>https://ainow.ai/2022/02/11/263089/#i-3]]】

◇ 教師あり学習「分類」と教師なし学習「クラスタリング」の違い 

+ 答えをもとに学習したデータを用いるのが「分類」,データをもとに特徴を学習していくのが「クラスタリング」である【[[Smiley>https://aismiley.co.jp/ai_news/clustering/]]】
+ クラス分類は対象になる分類を見つけるというニュアンス,クラスタリングはデータそれぞれを集団化するというニュアンスである【[[オルタナティブ・ブログ>https://blogs.itmedia.co.jp/takafumi/2015/09/post_3.html]]】

* 6.2 分類モデルの選定 [#x6f2a08b]

** モデルの特徴からデータの種類と目的を考える [#a5238c99]

CENTER:&ref(./classification_models.png,40%);~
CENTER:図3:一般的な分類モデルの特徴【[[MONOist>https://monoist.itmedia.co.jp/mn/articles/2004/07/news017.html]]】

- モデルは何を達成しようとしているのか
- どのくらいのデータがあり,どのようなタイプのデータなのか
- どのくらいの詳細が必要なのか
- ストレージは制限要因になるのか

CENTER:&ref(./models_flowchart.png,40%);~
CENTER:図4:モデル選定のための流れ

** 【ハンズオン6-1】ロジスティック回帰 (logistic regression) [#zbeffbdf]

[[第3回]]では,決定木分析を用いて,アヤメの種類の分類モデルを作成した.この節で紹介する''ロジスティック回帰''でも,決定木と同様に分類の予測モデルを作ることができる

アヤメの分類問題に関し,[[Qiita>https://qiita.com/hikaru_/items/3d64af35769235471d9c]]にもk-近傍法(k-NN)やサポートベクターマシン(SVM)の実装例があり,ご都合の良い際にご参照ください

- [[Google Drive:https://drive.google.com/]]にアクセスして,Google Colabを新規作成してください
- 名前を ''handson6-1.ipynb'' とします
- 下記のコードを貼り付けて実行してください

◇ STEP 0 準備

 # Pandasをインポート
 import pandas as pd
 
 # train_test_splitをインポート(データの分割用)
 from sklearn.model_selection import train_test_split

◇ STEP 1 データの読み込み

 #アヤメデータの取得
 df = pd.read_csv("https://www2.cmds.kobe-u.ac.jp/~masa-n/dshandson/iris-sample.csv")
 
 #確認
 df
 
【実行結果】
&ref(./output_001.png,60%);~

◇ STEP 2 欠損値を穴埋めする

 # 平均値による欠損値の穴埋め
 df_mean = df.mean()
 train2 = df.fillna(df_mean)
 
 # 特徴量と正解データに分割
 x = train2.loc[:,:'花びらの幅']
 t = train2['品種']
 
 # 特徴量の標準化
 from sklearn.preprocessing import StandardScaler
 sc = StandardScaler()
 new = sc.fit_transform(x)

◇ STEP 3 訓練データと検証用データを分割する

 # 訓練データとテストデータに分割
 x_train, x_test, y_train, y_test = train_test_split(new, t, test_size = 0.2, random_state = 0)

◇ STEP 4 ロジスティック回帰による学習

 from sklearn.linear_model import LogisticRegression
 
 model = LogisticRegression(random_state = 0, C = 0.1, multi_class = 'auto', solver = 'lbfgs')

☆☆ ''モデル変数 = LogisticRegression(C = 値,multi_class = 'auto', solver = 'lbfgs')''
- Cは正則化項の重み定数の逆数
- 3グループ以上の分類では multi_class = 'auto' と指定(指定しなくても,警告は出るがエラーにはならない)
- solver = 'lbfgs' については入門書のレベルを超えるので省略するが,指定しないと警告が出る

◇ STEP 5 正解率を確認する

 model.fit(x_train, y_train)
 print(model.score(x_train, y_train))
 model.score(x_test, y_test)

【実行結果】

 0.925
 0.9333333333333333

◇ STEP 6 係数を確認する

 model.coef_ # 係数の確認

【実行結果】
&ref(./output_002.png,50%);~

◇ STEP 7 新規データで予測する

 x_new = [[1, 2, 3, 4]] # 新規データ
 model.predict(x_new) # 新規データで予測

【実行結果】

 array(['Iris-virginica'], dtype=object)

◇ STEP 8 確率の予測結果を確認する

 model.predict_proba(x_new)

【実行結果】
&ref(./output_003.png,50%);~

☆☆ ''モデル変数.predict_proba( 2次元データ )''
- 2次元データにはリストや,データフレームが指定できる
- 結果の順番は,''モデル変数 .classes_'' 確認できる

* 6.3 分類モデルの学習 [#j3c3c37f]

** 予測精度を上げるための考え方 [#pad50e6e]

*** 不均衡データ (imbalanced data) [#cd440ae5]

実際の業務では,正解データの件数の比率に大きな差がある場合も多々ある.このようなデータを''不均衡データ''と呼ぶ.モデルの作成と学習時に,不均衡データの考慮が必要

◇ 不均衡データで対応するようなモデルの例:決定木(DecisionTreeClassifier)

CENTER:&ref(./sample_001.png,50%);~
CENTER:図5:モデルの作成と学習 - 不均衡データの考慮

''class_weight = 'balanced' という引数を指定すると

- 決定木の分岐条件を考える際に,比率の大きいデータの影響を小さくして,反対に比率の小さいデータの影響度を大きくする
-- → 単純なデータ数では不均衡でも,分岐条件を考える際の影響度という点では均一になるため,予測性能の良いモデルを作れる可能性が高くなる

☆☆ 不均衡データに対処する決定木モデルの作成

''tree.DecisionTreeClassifier( class_weight = 'balanced' )''

※ class_weight 引数は不均衡データのときに指定する.決定木モデルだけでなく,他の分類モデルでも同様に指定できる.例,LogisticRegression( class_weight = 'balanced' )

*** バイアス,バリアンス,ノイズ [#yd8f7c21]

[[第5回]]で皆さんに実際に体験して頂いたように,過学習(overfitting)とは「訓練データでは予測と実際の誤差が少ないのに,未知のテストデータでは,誤差が大きくなる現象」である

◇ 誤差が生じている不適切な状況を考えると,以下の2通りがある

CENTER:&ref(./sample_002.png,50%);~
CENTER:図6:モデルの結果に誤差が生じている不適切な状況

+ 予測結果自体は密集しているが,根本的に予測結果の平均値が予測すべき値から遠く離れたところにある(''バイアス''が高いという)
+ 予測結果の平均は予測すべき値に近いが,予測結果自体にばらつきが大きく生じている(''バリアンス''が高いという)

◇ 正解データにおける平均値からのばらつきを''ノイズ''と呼ぶ

☆☆ バイアス・バリアンス分解

''実際と予測の誤差 = バイアス + バリアンス + ノイズ''

- ノイズは,必ず発生してしまうどうしようもない誤差である
- 残りのバイアスとバリアンスを下げるようにモデルチューニングやデータ加工をする

** 【ハンズオン6-2】ランダムフォレスト (random forest) [#jf0d3307]

これから話すランダムフォレストは決定木の上位互換である.皆さんは[[第3回]]で体験して頂いた決定木を振り返って,左右2つに分岐していくフローチャートを「木」と呼ぶ

フォレストのことは「森」である.ランダムフォレストは,たくさんの決定木を作成し,それぞれの木に予測させ,その結果の多数決で最終結果を求めるという手法である

ランダムフォレストは通常の決定木に比べて過学習(overfitting)を防ぐことができる

以下を従って,ランダムフォレストの実装からモデルを理解してみましょう

- [[Google Drive:https://drive.google.com/]]にアクセスして,Google Colabを新規作成してください
- 名前を ''handson6-2.ipynb'' とします
- 下記のコードを貼り付けて実行してください

◇ STEP 0 Pandasなどのモジュールを読み込む

 # モジュールの読み込み
 import pandas as pd
 from sklearn.model_selection import train_test_split
 %matplotlib inline

◇ STEP 1 データを読み込む

 df = pd.read_csv('https://www.es4.eedept.kobe-u.ac.jp/~chensinan/share/Survived.csv') # csvファイルの読み込み
 # 確認する
 df.head(2)

【実行結果】
&ref(./output_004.png,60%);~

CENTER:&ref(./column_exp_01.png,50%);~
CENTER:図7:各列の意味

このデータを利用して,それぞれの乗客がこの客船の沈没事故に遭遇した場合に,無事生還できるかどうかを予測しましょう
- 乗客の特徴から沈没時に生存か死亡かに分類するモデルを作成する
- その過程で,どのような特徴を持つ人が生き残れたかを考察する

◇ STEP 2 欠損値を穴埋める

 jo1 = df['Pclass'] == 1
 jo2 = df['Survived'] == 0
 jo3 = df['Age'].isnull()
 df.loc[(jo1) & (jo2) & (jo3), 'Age'] = 43
 
 jo2 = df['Survived'] == 1
 df.loc[(jo1) & (jo2) & (jo3), 'Age'] = 35
 
 jo1 = df['Pclass'] == 2
 jo2 = df['Survived'] == 0
 jo3 = df['Age'].isnull()
 df.loc[(jo1) & (jo2) & (jo3), 'Age'] = 26
 
 jo2 = df['Survived'] == 1
 df.loc[(jo1) & (jo2) & (jo3), 'Age'] = 20
 
 jo1 = df['Pclass'] == 3
 jo2 = df['Survived'] == 0
 jo3 = df['Age'].isnull()
 df.loc[(jo1) & (jo2) & (jo3), 'Age'] = 43
 
 jo2 = df['Survived'] == 1
 df.loc[(jo1) & (jo2) & (jo3), 'Age'] = 35

◇ STEP 3 文字データの列を数値に変換する

 # 特徴量として利用する列のリスト
 col = ['Pclass', 'Age', 'SibSp', 'Parch', 'Fare']
 
 x = df[col]
 t = df['Survived']
 
 # Sex列は文字の列なのでダミー変数化
 dummy = pd.get_dummies(df['Sex'], drop_first = True)
 x = pd.concat([x, dummy], axis = 1)
 x.head(2)

【実行結果】
&ref(./output_005.png,60%);~

◇ STEP 4 ランダムフォレストの選定

 # ランダムフォレストのインポート
 from sklearn.ensemble import RandomForestClassifier
 x_train, x_test, y_train, y_test = train_test_split(x, t, test_size = 0.2, 
 random_state = 0)
 model = RandomForestClassifier(n_estimators = 200, random_state = 0)

☆☆ モデル変数

モデル変数 = RandomForestClassifier(n_estimators=〇, random_state=〇, max_depth=△)

※ n_estimatorsで作成する木の数を指定

※ 木の深さの最大値は,すべての木で共通

◇ STEP 5 モデルの学習

 model.fit(x_train, y_train)
 
 print(model.score(x_train, y_train))
 print(model.score(x_test, y_test))

【実行結果】

 0.9887640449438202
 0.8715083798882681

◇ STEP 6 単純な決定木分類と比較する

 from sklearn import tree
 model2 = tree.DecisionTreeClassifier(random_state = 0)
 model2.fit(x_train, y_train)
 
 print(model2.score(x_train, y_train))
 print(model2.score(x_test, y_test))

【実行結果】

 0.9887640449438202
 0.8156424581005587

◇ STEP 7 特徴量の重要度を確認する

 importance = model.feature_importances_ # 特徴量重要度
 
 # 列との対応がわかりやすいようにシリーズ変換
 pd.Series(importance, index = x_train.columns)

【実行結果】

 Pclass    0.079546
 Age       0.323012
 SibSp     0.045682
 Parch     0.032854
 Fare      0.265573
 male      0.253334
 dtype: float64

** 【ハンズオン6-2の継続】アンサンブル学習 (ensemble learning) [#k7aef812]

一方で決定木だけでなく,さまざまな予測モデルを作成して,最終的に1つの予測結果を出す手法のことを''アンサンブル学習''と呼ぶ

アンサンブル学習は,大きくわけて''バギング''と''ブースティング''という手法に類別することができる

*** バギング [#e5504a12]

- 訓練データをブートストラップサンプリングすることで,訓練データのデータセットをいくつか作り,それぞれモデルに学習させるものである
-- バギング手法の「モデルが全部決定木版」がランダムフォレストである

*** ブースティング [#d1d05e4c]
- モデルを1つずつ順番に学習させていく.
-- 最初のモデルの学習を終えたら次のモデルで学習を始める

ブースティングの手法の中に最も基本的なのは''アダブースト''である
- 大量のモデルを生成し,1つずつ逐次的に学習を進めていく
- 各モデルは,前のモデルと学習結果を共有して学習を行う
- 学習後の予測は,各モデルの予測性能に応じて,重み付き多数決を行う

【Pythonハンズオン6-2】の継続として,以下を行う

◇ STEP 8 アダブーストを実装する

 # アダブーストのインポート
 from sklearn.ensemble import AdaBoostClassifier
 
 # ベースとなるモデル
 from sklearn.tree import DecisionTreeClassifier
 
 x_train, x_test, y_train, y_test = train_test_split(x, t,
 test_size = 0.2, random_state = 0)
 # 最大の深さ5の決定木を何個も作っていく
 base_model = DecisionTreeClassifier(random_state = 0,
 max_depth = 5)
 
 # 決定木を500個作成
 model = AdaBoostClassifier(n_estimators = 500,
 random_state = 0, base_estimator = base_model)
 model.fit(x_train,y_train) # 学習
 
 print(model.score(x_train, y_train)) # 訓練データの正解率
 print(model.score(x_test, y_test)) # テストデータの正解率

【実行結果】

 0.9887640449438202
 0.8100558659217877

☆☆ モデルの作成

''変数=AdaBoostClassifier(n_estimator=モデル数,random_state=数値,base_model=決定木などのモデル)''

※ ベースモデルには,決定木やロジスティック回帰等を指定することができる(一般的には決定木を利用)

* 6.4 分類モデルの評価 [#a02649f4]

[[第3回]]のモデルの評価では,皆さんは精度(Accuracy)と混同行列(confution_matrix)を扱って頂いた.その他さまざまなな性能評価の指標もある.

** 【ハンズオン6-3】適合率(precision),再現率(recall),F値(f1-score) [#i6e724dd]

【ハンズオン6-2】における客船の沈没事故のデータを利用し続き,分類の予測性能評価の実装から手法を理解してみましょう

- [[Google Drive:https://drive.google.com/]]にアクセスして,Google Colabを新規作成してください
- 名前を ''handson6-3.ipynb'' とします
- 下記のコードを貼り付けて実行してください

◇ STEP 1 データの準備

 # モジュールの読み込み
 import pandas as pd
 from sklearn.model_selection import train_test_split
 
 # データの準備
 df = pd.read_csv('https://www.es4.eedept.kobe-u.ac.jp/~chensinan/share/Survived.csv')
 df = df.fillna(df.mean())
 
 x = df[['Pclass', 'Age']]
 t = df['Survived']
 
 x_train, x_test, y_train, y_test = train_test_split(x, t, test_size = 0.2, random_state = 0)

◇ STEP 2 モデルの準備

 # モデルの準備
 from sklearn import tree
 
 model = tree.DecisionTreeClassifier(max_depth = 2,
 random_state = 0)
 model.fit(x_train, y_train)

◇ STEP 3 適合率,再現率,F値を一括で計算

''classification_report''関数で再現率と適合率を一括で計算してみましょう

 from sklearn.metrics import classification_report
 
 pred = model.predict(x_test)
 out_put = classification_report(y_pred = pred, y_true = y_test)
 print(out_put)

【実行結果】
&ref(./output_006_.png,60%);~

☆☆ 適合率(precision)

CENTER:&ref(./precision.png,50%);~
CENTER:図8:雨と予測した日数のうち実際に雨がふった日数の比率

☆☆ 再現率(recall)

CENTER:&ref(./recall.png,50%);~
CENTER:図9:実際に雨は降った件数のうち,雨が降ると予測した件数の比率

☆☆ F値(f1-score)
- 適合率と再現率の平均と解釈できる指標で,F値とも呼ばれている

◇ STEP 4 classification_report関数にパラメータ引数を指定
- ''output_dict = True''で戻り値をディクショナリ型で出力

 out_put = classification_report(y_pred = pred, y_true = y_test,
 output_dict = True)
 
 # out_putをデータフレームに変換
 pd.DataFrame(out_put)

【実行結果】
&ref(./output_007_.png,60%);~

他指標には,特異度,偽陽性率,ROC曲線,AUC(曲線下面積)等もある

// * 6.5 より複雑な分類モデル [#t3bf0c88]
// ** XGBoost (eXtreme Gradient Boosting) [#w1958cb9]
// ** LightGBM (light gradient boosting machine) [#p9c450f4]

//* 6.6 演習 [#kdc876ac]
//- [[第6回/演習]]


トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS