![見出し画像](https://assets.st-note.com/production/uploads/images/79831018/rectangle_large_type_2_8521718abdbcda8bc8bea12e38db3975.png?width=1200)
Pythonコードの復習(4) - Tensorflow + Keras、データの整形 & ニューラルネットワークを用いたペンギンの多値分類
今回は、前回に引き続き機械学習の基礎であるニューラルネットを用いた多値分類、およびデータを学習に掛けるための整形の仕方について考察していきたいと思います。
今回学んだこと
・Irisデータを代替しよう、という運動とその理由
・パーマーペンギンのデータセットを視覚化→分析に掛けるまでのデータ整形
・Pandasを使ったデータ整形
・SKLearnによる標準化
・多値分類を行う際の活性化関数・最適化関数・誤差関数・評価指標の設定
Irisデータとペンギンデータ
多値分類の例題として有名なIris データは著名な統計学者であるロナルド・フィッシャーによって集計されましたが、このデータは「優生学年鑑」("Annals of Eugenics")という現在では疑似科学とみなされている学術誌に掲載されていたことから一部では使用を控える風潮が起こっています。そこで提案されているのが、Palmer Station Antarctica LTERによって取集されたパーマーペンギンの分類と各種説明変数を集めたデータです。
![](https://assets.st-note.com/img/1654136184801-KxONxfOUCp.png?width=1200)
Irisデータを使うのが良いことなのかどうかは置いておくとして、今回はこのパーマーペンギンをニューラルネットを用いて多値分類するというのを目指そうと思います。
データの視覚化と整形を考案
まず、Pandasを使ってペンギンの.csvデータを読み込みます。図示化してラベルデータや欠損値をどうするかを検討しましょう。
#tensorflowのバックエンドとしてkerasを用いる
#訓練・テストデータの分割にはsklearnを用いる
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras import optimizers
import seaborn as sns
import keras
from sklearn.model_selection import train_test_split
from sklearn.utils import shuffle
#ペンギンのCSVを読み込む。
data = pd.read_csv("penguins.csv")
#Pandasによる視覚化。最初・最後の数行がどのような構造かを確認する
#欠損値を除外する - .dropna()
df = pd.DataFrame(data)
df = df.dropna()
df.head(10)
print(df.shape)
sns.pairplot(df)
plt.show()
![](https://assets.st-note.com/img/1654136610820-TEZcoOnCUy.png?width=1200)
![](https://assets.st-note.com/img/1654136613247-OCe1tCuPGi.png?width=1200)
目的変数であるペンギンの種類はAdelie, Chinstrap, Gentooの3種に、そして説明変数には出身島・性別といったラベルデータとその他の数値データが混在しています。今回は目的変数をワンホットエンコーディングを施して2階テンソルの形に、説明変数の内ラベルデータとなっているものにラベルエンコーディングを施して数値データに直すという方法で分析していきたいと思います。
説明変数のラベルエンコーディング
種族、出身島、性別をラベルエンコードし、観測年を列から削除します。
観測年をデータから除外するのは、個人的に「時系列データと目的変数との相関は極めて低い」と考えたためです。
ラベルエンコーディングを施すには、SKLearnに実装されているメソッドを使用します。
#sklearnのラベルエンコーダーを用いる
from sklearn.preprocessing import LabelEncoder
LEncord = LabelEncoder()
df["species"] = LEncord.fit_transform( df["species"] )
df["island"] = LEncord.fit_transform( df["island"] )
df["sex"] = LEncord.fit_transform( df["sex"] )
#.dropを使ってyearを列から削除する
df = df.drop("year", axis = 1)
df.head(10)
![](https://assets.st-note.com/img/1654139049321-Ai7K60S8Gl.png?width=1200)
目的変数のワンホットエンコーディング
次に、目的変数と説明変数を分割します。目的変数はpandas.get_dummiesというメソッドを用いてワンホットベクトルを2階テンソルに整形した行列とします。
#目的変数と説明変数を分割。目的変数はワンホットベクトルを2階テンソルにした形とする
df_enc = df["species"]
df_t = pd.get_dummies(df_enc)
df_t.head(10)
df_x = df = df.drop("species", axis = 1)
df_x.head(10)
![](https://assets.st-note.com/img/1654139462698-fK18zrd7D1.png?width=1200)
説明変数の標準化・Numpy行列に変換
説明変数の分布と数値に着目してみると、分布は一様でない凸型の分布、数値は出身島が0-2、くちばしの長さが約40、体重が約3000-4000と単純な比較に適した単位ではないことが分かります。このような数値同士を比較に適したデータに成型するには、標準化を用いるのが手っ取り早いと判断することができます。今回はPandasの.applyメソッドを用いて、平均0・標準偏差1の数値に説明変数を変換します。
#.apply - 事前処理。説明変数をラムダ値に応じて正規化・標準化する処理が行える
df_x = df_x.apply(lambda x: (x - x.mean())/x.std(), axis = 0)
df_x.head(10)
![](https://assets.st-note.com/img/1654139887955-5VNf30jaA2.png?width=1200)
最後に、説明変数と目的変数をNumpy行列に変換し機械学習を行うための準備を整えます。これでペンギンデータを多値分類問題のフレームワークに当てはめることができます。
x = df_x.to_numpy()
t = df_t.to_numpy()
#float64をfloat32に変換する
#6つの説明変数から、3つの目的変数を算出する問題に帰着
x = x.astype("float32")
t = t.astype("float32")
print("x's shape:", x.shape)
print("t's shape:", t.shape)
学習・予測
#モデルの構築。後にウェイト・バイアスに適切な初期化をした場合なども検証する
model = Sequential()
#隠れ層、出力層のユニット数と活性化関数を定義
#入力(3)→隠れ(6)→隠れ(6)→隠れ(3)
model.add(Dense(6, activation = "sigmoid"))
model.add(Dense(6, activation = "sigmoid"))
model.add(Dense(3, activation = "softmax"))
#最適化関数はSGD。あとでADAMを用いる場合も検証する
optimizer = optimizers.SGD(learning_rate=0.1)
#モデルを実装。最適化関数、損失関数、評価指標を順に指定する
model.compile(optimizer = optimizer, loss = "categorical_crossentropy", metrics = ["accuracy"])
#エポック数
epoch = 1000
#訓練用データ、テスト用データに分割する
x_train, x_test, t_train, t_test = train_test_split(x, t, test_size = 0.25)
#学習を行う
history = model.fit( x_train, t_train, epochs = epoch, batch_size = 32, verbose=1, validation_data = (x_test, t_test))
![](https://assets.st-note.com/img/1654154280824-J8qvg76DJZ.png?width=1200)
エポック数が120ほどで訓練性能がほぼ100%になりました。ラベルエンコーディングを行えば、数値データでないラベルが含まれている場合でも極めて正確な予測ができるようになります。
次回は目的変数を回帰分析を使って求める場合、もしくはTensorflow・Kerasの応用に進んでいきたいと思います。楽しみですね!