1.はじめに
機械学習について学習した内容をまとめました.
内容は以下の通りです.
・線形回帰モデル
・非線形回帰モデル
・ロジスティック回帰モデル
・主成分分析(PCA:Principal Component Analysis)
・サポートベクターマシン(SVM:Support Vector Machine)
※PCAは教師なし学習,それ以外は教師あり学習である.
2.線形回帰モデル
・まず回帰とは
→入力$ x $(離散値あるいは連続値)から出力$ y $(連続値)を予測すること
・線形回帰とは
→直線の式を使って$ y $を予測する機械学習手法(または統計的手法)
以下のような式における最適な$ w $を求めることで線形回帰を行う.
\sum_{i=1}^{m} w_ix_i + w_0
$ w $は重み,またはパラメータと呼ばれ,最小二乗法により求められる(推定).
・最小二乗法
→学習データの平均二乗誤差関数を最小とするパラメータを探索する手法
→平均二乗誤差関数の勾配が0になる点を求めることでパラメータの推定を行う.
平均二乗誤差関数(MSE:Mean Squared Error)
MSE=\frac{1}{N}\sum_{i=1}^{N} (\hat{y_i} - y_i)^2
・課題
ボストンの住宅データセットを線形回帰モデルで分析
→部屋数が4で犯罪率が0.3の物件はいくらになるか?
コード
from sklearn.linear_model import LinearRegression
from sklearn.datasets import load_boston
from pandas import DataFrame
import numpy as np
# ボストンデータを"boston"というインスタンスにインポート
boston = load_boston()
# 説明変数らをDataFrameへ変換
df = DataFrame(data=boston.data, columns = boston.feature_names)
# 目的変数をDataFrameへ追加
df['PRICE'] = np.array(boston.target)
# 最初の5行を表示
print(df.head(5))
# カラムを指定(犯罪率,部屋数)してデータを表示
print(df[['CRIM', 'RM']].head())
# 説明変数(犯罪率)
data = df.loc[:, ['CRIM', 'RM']].values
# 目的変数(住宅価格)
target = df.loc[:, 'PRICE'].values
# 線形回帰オブジェクト生成
model = LinearRegression()
# fit関数でパラメータを推定
model.fit(data, target)
pred = model.predict([[0.3, 4]])
print(f"結果:{pred}")
結果
結果:4.240079558512296
したがって,部屋数が4で犯罪率が0.3の物件は4240ドルという予測となった.
3.非線形回帰モデル
・非線形回帰とは
→直線的な式では予測できないようなデータ構造に対し,
非線形関数を用いることで,予測精度を上げることができる.
・基底展開法
→回帰関数として,基底関数と呼ばれる既知の非線形関数と
パラメータベクトルの線型結合を使用
→wの係数$ x $を非線形関数Φ($ x $)に変換して用いる.
→非線形関数例:$ x^2やx^3,log(x) $など
式は以下のようになる.
y_i=w_0 + \sum_{i=1}^{m} w_i\phi_i(x_i)
・未学習(Underfitting)と過学習(Overfitting)
未学習:訓練データに対し,誤差が小さくならないような状態
対策→モデルの表現力が低いため,表現力の高いモデルを利用する.
過学習:訓練データに対しては誤差が非常に小さいが,
未知なるデータ(テストデータ)に対しては誤差が大きくなるような状態
対策1 → 訓練データ数の増量
対策2 → 不要な基底関数(変数)を削除して表現力を抑止
対策3 → 正則化法を利用して表現力を抑止
・正則化法(罰則化法)
→「モデルの複雑さに伴い,その値が大きくなる正則化項(罰則項)を課した関数」を最小化
正則化項:形状によっていくつもの種類があり,それぞれ推定量の性質が異なる.
L2ノルム→Lidge推定量
L1ノルム→Lasso推定量
・演習
非線形回帰モデルで分析
→サンプルでは4次関数であったが,5次関数で行ってみる.
→データ数はサンプル時の2倍である200個のデータで回帰を行う.
コード
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
#seaborn設定
sns.set()
#背景変更
sns.set_style("darkgrid", {'grid.linestyle': '--'})
#大きさ(スケール変更)
sns.set_context("paper")
n=200
def true_func(x):
z = 1-48*x+218*x**2-315*x**3+145*x**5
return z
def linear_func(x):
z = x
return z
# 真の関数からノイズを伴うデータを生成
# 真の関数からデータ生成
data = np.random.rand(n).astype(np.float32)
data = np.sort(data)
target = true_func(data)
# ノイズを加える
noise = 0.5 * np.random.randn(n)
target = target + noise
# ノイズ付きデータを描画
plt.scatter(data, target)
plt.title('NonLinear Regression')
plt.legend(loc=2)
ノイズ付きの200個のデータを以下の式で生成した.
y = 1-48x+218x^2-315^3+145x^5
まずは線形回帰を行ってみる.
from sklearn.linear_model import LinearRegression
clf = LinearRegression()
data = data.reshape(-1,1)
target = target.reshape(-1,1)
clf.fit(data, target)
p_lin = clf.predict(data)
plt.scatter(data, target, color='blue', label='data')
plt.plot(data, p_lin, color='darkorange', marker='', linestyle='-', linewidth=3, markersize=6, label='linear regression')
plt.legend()
print(clf.score(data, target))
このように線形回帰では精度があまり高くないと考えられる.
次に非線形回帰を行った.
from sklearn.kernel_ridge import KernelRidge
clf = KernelRidge(alpha=0.0002, kernel='rbf')
clf.fit(data, target)
p_kridge = clf.predict(data)
plt.scatter(data, target, color='blue', label='data')
plt.plot(data, p_kridge, color='orange', linestyle='-', linewidth=3, markersize=6, label='kernel ridge')
plt.legend()
このように,複雑なデータ構造を線形回帰ではあまりできなかった予測が,
非線形回帰を行うことで,かなり高い精度で予測できていることが分かる.
4.ロジスティック回帰モデル
・名前に回帰とあるが,分類のための回帰モデル
・確率という連続値を予測することで,分類を行う.
・シグモイド関数が用いられる.
・シグモイド関数
→0~1の出力をとる非線形関数である.
式を以下に示す.
\sigma(x) = \frac{1}{1+e^{-ax}}
続いて,ロジスティック回帰の式は以下になる.
$ i $番目のデータを与えた時のシグモイド関数の出力を,i番目のデータが$ Y=1 $になる確率とする.
P(Y=1|x)=\sigma(\sum_{i=1}^{m} w_ix_i + w_0)
・最尤推定
→結果が2通りしかないベルヌーイ分布で用いられる.
→ある分布を考えた時,そのパラメータ(既知)によって,生成されるデータは変化する.
→データからそのデータを生成したであろう尤もらしい分布(パラメータ)を推定したい.
→尤度関数を最大化するようなパラメータを選ぶ推定方法を最尤推定という.
尤度関数を以下に示す.
L(w)=\prod_{i=1}^{n}\hat{y_i}^{y_i}(1-\hat{y_i})^{1-y_i}
この式のままでは,確率0~1の値が掛け算されていき,だんだん小さい値となってしまう.
この問題を解決するために,対数をとることにしたのが次の式である.
E(w) = -\log{L(w)} = -\sum_{i=1}^{n}(y_i\log{\hat{y}}+(1−y_i)\log(1−\hat{y_i}))
・勾配降下法(Gradient descent)
→反復学習によりパラメータを逐次的に更新するアプローチの一つ
とあるパラメータ$ w $を更新していく式を以下に示す.
→$ η $は学習率と呼ばれるハイパーパラメータでモデルのパラメータの収束しやすさを調整
\begin{align}
w^{t+1} &= w^t - \eta\frac{\partial{E(w)}}{\partial{w}} \\
\end{align}
勾配が正であれば重みを減らし,負であれば重みを増やす仕組みとなっている.
この反復学習により$ E(w) $が最小値となるようなパラメータ$ w $を探索する.
※パラメータを更新するのに$ N $個全てのデータに対する和を求める必要がある.
→$ N $が巨大になった時,データをメモリに載せる容量が足りない.
→計算時間が莫大になる.
上記のような問題を解決するために用いられる手法を確率的勾配降下法と呼ぶ.
→データを1つずつランダムに選択してパラメータを更新する.
・課題
タイタニックの乗客データを利用しロジスティック回帰モデルを作成
→年齢が30歳で男の乗客は生き残れるか?
コード
from google.colab import drive
drive.mount('/content/drive')
#from モジュール名 import クラス名(もしくは関数名や変数名)
import pandas as pd
from pandas import DataFrame
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.linear_model import LogisticRegression
#matplotlibをinlineで表示するためのおまじない (plt.show()しなくていい)
%matplotlib inline
# titanic data csvファイルの読み込み
titanic_df = pd.read_csv('/content/drive/MyDrive/Colab Notebooks/study_ai_ml_google/data/titanic_train.csv')
# ファイルの先頭部を表示し、データセットを確認する
titanic_df.head(5)
#予測に不要と考えるからうをドロップ (本当はここの情報もしっかり使うべきだと思っています)
titanic_df.drop(['PassengerId', 'Name', 'Ticket', 'Cabin','Parch','SibSp','Embarked'], axis=1, inplace=True)
#一部カラムをドロップしたデータを表示
titanic_df.head()
#nullを含んでいる行を表示
titanic_df[titanic_df.isnull().any(1)].head(10)
#Ageカラムのnullを中央値で補完
titanic_df['AgeFill'] = titanic_df['Age'].fillna(titanic_df['Age'].mean())
#再度nullを含んでいる行を表示 (Ageのnullは補完されている)
titanic_df[titanic_df.isnull().any(1)]
#AgeFillの欠損値を埋めたので
titanic_df = titanic_df.drop(['Age'], axis=1)
#性別の数値化
titanic_df['Gender'] = titanic_df['Sex'].map({'female': 0, 'male': 1}).astype(int)
titanic_df.head(3)
#いらないデータ削除
titanic_df = titanic_df.drop(['Pclass', 'Sex', 'Fare'], axis=1)
titanic_df.head()
#運賃と性別のリストを作成
data = titanic_df.loc[:, ["AgeFill", "Gender"]].values
#生死フラグのみのリストを作成
label = titanic_df.loc[:,["Survived"]].values
#ロジスティック回帰のインスタンスを生成し、学習する
model = LogisticRegression()
model.fit(data, label)
#30歳男性の生死を予測
model.predict([[30,1]])
model.predict_proba([[30,1]])
結果
ロジスティック回帰により,30歳男性について
・死者である確率は約80.7%
・生存者である確率は約19.3%
→死者と予測された.
5.主成分分析(PCA)
多変量データの持つ構造をより少数個の指標に圧縮するための教師なし学習である.
次元の呪いという言葉もあり,次元を削減することでメリットが得られる.
その一つとして,2次元,3次元にまで圧縮すると,可視化できることが挙げられる.
係数ベクトルが変われば,線形変換後の値が変化
→情報量の大きさを分散と捉える.
→線形変換後の変数の分散が最大となるような射影軸を探索する.
・寄与率:第$ k $主成分の分散の全分散に対する割合(第$ k $主成分が持つ情報量の割合)
・累積寄与率:第1~$ k $主成分まで圧縮した際の情報損失量の割合
・課題
乳がん検査データを利用しロジスティック回帰モデルを作成
→32次元のデータを2次元上に次元圧縮した際,うまく判別できるかを確認
コード
from google.colab import drive
drive.mount('/content/drive')
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegressionCV
from sklearn.metrics import confusion_matrix
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
%matplotlib inline
cancer_df = pd.read_csv('/content/drive/My Drive/Colab Notebooks/study_ai_ml/data/cancer.csv')
print('cancer df shape: {}'.format(cancer_df.shape))
cancer_df
cancer_df.drop('Unnamed: 32', axis=1, inplace=True)
cancer_df
# 目的変数の抽出
y = cancer_df.diagnosis.apply(lambda d: 1 if d == 'M' else 0)
# 説明変数の抽出
X = cancer_df.loc[:, 'radius_mean':]
# 学習用とテスト用でデータを分離
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
# 標準化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# ロジスティック回帰で学習
logistic = LogisticRegressionCV(cv=10, random_state=0)
logistic.fit(X_train_scaled, y_train)
# 検証
print('Train score: {:.3f}'.format(logistic.score(X_train_scaled, y_train)))
print('Test score: {:.3f}'.format(logistic.score(X_test_scaled, y_test)))
print('Confustion matrix:\n{}'.format(confusion_matrix(y_true=y_test, y_pred=logistic.predict(X_test_scaled))))
以下の結果では,32次元でも高い精度を持っていることが分かる.
pca = PCA(n_components=30)
pca.fit(X_train_scaled)
plt.bar([n for n in range(1, len(pca.explained_variance_ratio_)+1)], pca.explained_variance_ratio_)
PCAで30次元までに削減を行った.
以下のグラフは寄与率を高い順に並べたものである.
3番目の主成分(第3主成分)までで約70%の情報量が含まれていることが分かる.
# 次元数2まで圧縮
pca = PCA(n_components=2)
X_train_pca = pca.fit_transform(X_train_scaled)
print('X_train_pca shape: {}'.format(X_train_pca.shape))
# X_train_pca shape: (426, 2)
# 寄与率
print('explained variance ratio: {}'.format(pca.explained_variance_ratio_))
# explained variance ratio: [ 0.43315126 0.19586506]
# 散布図にプロット
temp = pd.DataFrame(X_train_pca)
temp['Outcome'] = y_train.values
b = temp[temp['Outcome'] == 0]
m = temp[temp['Outcome'] == 1]
plt.scatter(x=b[0], y=b[1], marker='o') # 良性は○でマーク
plt.scatter(x=m[0], y=m[1], marker='^') # 悪性は△でマーク
plt.xlabel('PC 1') # 第1主成分をx軸
plt.ylabel('PC 2') # 第2主成分をy軸
結果
おおよそのデータは判別ができると考えられるが,
一部のデータは2次元上では分類できないと考えられる.
→2次元に圧縮したことで,元の次元から約35%の情報が損失したからだと考えられる.
6.サポートベクターマシーン(SVM)
・二値分類問題に適した手法(確率は出せない)
→回帰問題や多値分類問題にも応用が可能
・1990年代に提案され,2000年代前半に急速に発展した手法.
・汎化性能の高さや応用分野の広さから,データ分析の現場において注目されている.
→最近のkaggleでは優勝者がサポートベクター回帰(SVR)を使用していた.
手法としては,境界線(直線,または平面,超平面)を引いて分類を行う.
各グループの境界線に最も近いデータ(サポートベクトル)同士の距離をマージンと呼ぶ.
このマージンを最大化することが目的である.
・ハードマージン
二次元における直線の方程式は次のように表される.
ax+by+c = 0
また,三次元における平面の方程式は次のように表される.
ax+by+cz+d = 0
この式の$ a,b,c,d $を$ w $とし,$ y,z $を$ x $とすると機械学習っぽい式となる.
w_1x_1+w_2x_2+w_3x_3+w_0 = 0
また,n次元における境界線(分類境界)の式は次のように定義できる.
\sum_{i=1}^{n}w_ix_i+w_0 = \boldsymbol{w}^T\boldsymbol{x}+b = 0
これを符号関数(1か-1を返す)として次のように定義する.
f(x) = \boldsymbol{w}^T\boldsymbol{x}+b = 1
f(x) = \left\{
\begin{array}{ll}
1 & (f(x) \ > \ 0) \\
-1 & (f(x) \ < \ 0)
\end{array}
\right.
$ w $のノルムは,次のように定義される.
\|\boldsymbol{w}\| = \sqrt{w_1^2 + w_2^2 + … + w_n^2}
そこで,$ n $次元空間上の1点と超平面との距離は以下の式で表される.
d = \frac{|\boldsymbol{w}^T\boldsymbol{x}+b|}{\|\boldsymbol{w}\|}
よって,マージンMは次のようになる.
M=\frac{\boldsymbol{w}^T\boldsymbol{x}_{+}+b}{\|\boldsymbol{w}\|}=\frac{-(\boldsymbol{w}^T\boldsymbol{x}_{-}+b)}{\|\boldsymbol{w}\|}
$ w $のノルムを最小化することでマージンMを最大化できる.
よって,SVMの目的関数は以下になる.
\min_{w,b} \frac{1}{2}\|\boldsymbol{w}\|
このように,線形分離が可能であることを前提とした分類のことをハードマージンと呼ぶ.
・ソフトマージン
・完全に線形分離が不可能な場合のタスクをソフトマージンと呼ぶ.
スラック変数ξを導入することで誤分類を許容する.
→ハイパーパラメータである正則化系数Cでどれくらい許容するのかを調整する.
・非線形分離
線形分離ではうまく分類できないケースは当然ある.
そのようなデータに対して特徴ベクトルを非線形変換し,
その空間での線形分類を行う手法をカーネルトリックと呼ぶ.
→入力データを高次元に拡張することで線形分離が可能になる.
・演習①
生成したデータでハードマージン,ソフトマージン,非線形分離を実装してみる.
コード(ハードマージン)
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
def gen_data():
x0 = np.random.normal(size=50).reshape(-1, 2) - 2.
x1 = np.random.normal(size=50).reshape(-1, 2) + 2.
X_train = np.concatenate([x0, x1])
ys_train = np.concatenate([np.zeros(25), np.ones(25)]).astype(np.int)
return X_train, ys_train
X_train, ys_train = gen_data()
plt.scatter(X_train[:, 0], X_train[:, 1], c=ys_train)
t = np.where(ys_train == 1.0, 1.0, -1.0)
n_samples = len(X_train)
# 線形カーネル
K = X_train.dot(X_train.T)
eta1 = 0.01
eta2 = 0.001
n_iter = 500
H = np.outer(t, t) * K
a = np.ones(n_samples)
for _ in range(n_iter):
grad = 1 - H.dot(a)
a += eta1 * grad
a -= eta2 * a.dot(t) * t
a = np.where(a > 0, a, 0)
#予測
index = a > 1e-6
support_vectors = X_train[index]
support_vector_t = t[index]
support_vector_a = a[index]
term2 = K[index][:, index].dot(support_vector_a * support_vector_t)
b = (support_vector_t - term2).mean()
xx0, xx1 = np.meshgrid(np.linspace(-5, 5, 100), np.linspace(-5, 5, 100))
xx = np.array([xx0, xx1]).reshape(2, -1).T
X_test = xx
y_project = np.ones(len(X_test)) * b
for i in range(len(X_test)):
for a, sv_t, sv in zip(support_vector_a, support_vector_t, support_vectors):
y_project[i] += a * sv_t * sv.dot(X_test[i])
y_pred = np.sign(y_project)
# 訓練データを可視化
plt.scatter(X_train[:, 0], X_train[:, 1], c=ys_train)
# サポートベクトルを可視化
plt.scatter(support_vectors[:, 0], support_vectors[:, 1],
s=100, facecolors='none', edgecolors='k')
# 領域を可視化
#plt.contourf(xx0, xx1, y_pred.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
# マージンと決定境界を可視化
plt.contour(xx0, xx1, y_project.reshape(100, 100), colors='k',
levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])
# マージンと決定境界を可視化
plt.quiver(0, 0, 0.1, 0.35, width=0.01, scale=1, color='pink')
コード(ソフトマージン)
x0 = np.random.normal(size=50).reshape(-1, 2) - 1.
x1 = np.random.normal(size=50).reshape(-1, 2) + 1.
x_train = np.concatenate([x0, x1])
y_train = np.concatenate([np.zeros(25), np.ones(25)]).astype(np.int)
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
#学習
X_train = x_train
t = np.where(y_train == 1.0, 1.0, -1.0)
n_samples = len(X_train)
# 線形カーネル
K = X_train.dot(X_train.T)
C = 1
eta1 = 0.01
eta2 = 0.001
n_iter = 1000
H = np.outer(t, t) * K
a = np.ones(n_samples)
for _ in range(n_iter):
grad = 1 - H.dot(a)
a += eta1 * grad
a -= eta2 * a.dot(t) * t
a = np.clip(a, 0, C)
#予測
index = a > 1e-8
support_vectors = X_train[index]
support_vector_t = t[index]
support_vector_a = a[index]
term2 = K[index][:, index].dot(support_vector_a * support_vector_t)
b = (support_vector_t - term2).mean()
xx0, xx1 = np.meshgrid(np.linspace(-4, 4, 100), np.linspace(-4, 4, 100))
xx = np.array([xx0, xx1]).reshape(2, -1).T
X_test = xx
y_project = np.ones(len(X_test)) * b
for i in range(len(X_test)):
for a, sv_t, sv in zip(support_vector_a, support_vector_t, support_vectors):
y_project[i] += a * sv_t * sv.dot(X_test[i])
y_pred = np.sign(y_project)
# 訓練データを可視化
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
# サポートベクトルを可視化
plt.scatter(support_vectors[:, 0], support_vectors[:, 1],
s=100, facecolors='none', edgecolors='k')
# 領域を可視化
plt.contourf(xx0, xx1, y_pred.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
# マージンと決定境界を可視化
plt.contour(xx0, xx1, y_project.reshape(100, 100), colors='k',
levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])
・非線形分離
factor = .2
n_samples = 50
linspace = np.linspace(0, 2 * np.pi, n_samples // 2 + 1)[:-1]
outer_circ_x = np.cos(linspace)
outer_circ_y = np.sin(linspace)
inner_circ_x = outer_circ_x * factor
inner_circ_y = outer_circ_y * factor
X = np.vstack((np.append(outer_circ_x, inner_circ_x),
np.append(outer_circ_y, inner_circ_y))).T
y = np.hstack([np.zeros(n_samples // 2, dtype=np.intp),
np.ones(n_samples // 2, dtype=np.intp)])
X += np.random.normal(scale=0.15, size=X.shape)
x_train = X
y_train = y
plt.scatter(x_train[:,0], x_train[:,1], c=y_train)
def rbf(u, v):
sigma = 0.8
return np.exp(-0.5 * ((u - v)**2).sum() / sigma**2)
X_train = x_train
t = np.where(y_train == 1.0, 1.0, -1.0)
n_samples = len(X_train)
# RBFカーネル
K = np.zeros((n_samples, n_samples))
for i in range(n_samples):
for j in range(n_samples):
K[i, j] = rbf(X_train[i], X_train[j])
eta1 = 0.01
eta2 = 0.001
n_iter = 5000
H = np.outer(t, t) * K
a = np.ones(n_samples)
for _ in range(n_iter):
grad = 1 - H.dot(a)
a += eta1 * grad
a -= eta2 * a.dot(t) * t
a = np.where(a > 0, a, 0)
#予測
index = a > 1e-6
support_vectors = X_train[index]
support_vector_t = t[index]
support_vector_a = a[index]
term2 = K[index][:, index].dot(support_vector_a * support_vector_t)
b = (support_vector_t - term2).mean()
xx0, xx1 = np.meshgrid(np.linspace(-1.5, 1.5, 100), np.linspace(-1.5, 1.5, 100))
xx = np.array([xx0, xx1]).reshape(2, -1).T
X_test = xx
y_project = np.ones(len(X_test)) * b
for i in range(len(X_test)):
for a, sv_t, sv in zip(support_vector_a, support_vector_t, support_vectors):
y_project[i] += a * sv_t * rbf(X_test[i], sv)
y_pred = np.sign(y_project)
# 訓練データを可視化
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
# サポートベクトルを可視化
plt.scatter(support_vectors[:, 0], support_vectors[:, 1],
s=100, facecolors='none', edgecolors='k')
# 領域を可視化
plt.contourf(xx0, xx1, y_pred.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
# マージンと決定境界を可視化
plt.contour(xx0, xx1, y_project.reshape(100, 100), colors='k',
levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])
・演習②
4.の課題にて行ったタイタニック問題について
→線形SVM,非線形SVMでも分類を行ってみる.
コード
#from モジュール名 import クラス名(もしくは関数名や変数名)
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn import svm
from sklearn.metrics import accuracy_score
%matplotlib inline
# titanic data csvファイルの読み込み
titanic_df = pd.read_csv('/content/drive/MyDrive/Colab Notebooks/study_ai_ml_google/data/titanic_train.csv')
# ファイルの先頭部を表示し、データセットを確認する
print(titanic_df.head(5))
#予測に不要と考えるからうをドロップ (本当はここの情報もしっかり使うべきだと思っています)
titanic_df.drop(['PassengerId', 'Name', 'Ticket', 'Cabin','Parch','SibSp','Embarked'], axis=1, inplace=True)
#一部カラムをドロップしたデータを表示
print(titanic_df.head())
#nullを含んでいる行を表示
print(titanic_df[titanic_df.isnull().any(1)].head(10))
#Ageカラムのnullを中央値で補完
titanic_df['AgeFill'] = titanic_df['Age'].fillna(titanic_df['Age'].mean())
#再度nullを含んでいる行を表示 (Ageのnullは補完されている)
# titanic_df[titanic_df.isnull().any(1)]
#AgeFillの欠損値を埋めたので
titanic_df = titanic_df.drop(['Age'], axis=1)
#性別の数値化
titanic_df['Gender'] = titanic_df['Sex'].map({'female': 0, 'male': 1}).astype(int)
titanic_df.head(3)
#いらないデータ削除
titanic_df = titanic_df.drop(['Pclass', 'Sex', 'Fare'], axis=1)
titanic_df.head()
#運賃と性別のリストを作成
data = titanic_df.loc[:, ["AgeFill", "Gender"]].values
#生死フラグのみのリストを作成
label = titanic_df.loc[:,["Survived"]].values
label = label.ravel()
print(len(label))
#ロジスティック回帰のインスタンスを生成し、学習する
model_logi = LogisticRegression()
model_logi.fit(data, label)
#線形SVMのインスタンスを生成し、学習する
model_L_SVM = svm.SVC(kernel='linear')
model_L_SVM.fit(data, label)
#線形SVMのインスタンスを生成し、学習する
model_H_SVM = svm.SVC(kernel='rbf')
model_H_SVM.fit(data, label)
#訓練データでどれくらい学習できたかを正答率で確認
pred_train1 = model_logi.predict(data)
pred_train2 = model_L_SVM.predict(data)
pred_train3 = model_H_SVM.predict(data)
score1 = accuracy_score(label, pred_train1)
score2 = accuracy_score(label, pred_train2)
score3 = accuracy_score(label, pred_train3)
print("訓練データ正答率")
print(f"ロジスティック回帰:{score1}")
print(f"線形SVM :{score2}")
print(f"非線形SVM :{score3}\n")
#30歳男性の生死を予測
pred1 = model_logi.predict([[30,1]])
pred2 = model_L_SVM.predict([[30,1]])
pred3 = model_H_SVM.predict([[30,1]])
print(f"30歳男性の予測結果")
print(f"ロジスティック回帰:{pred1}")
print(f"線形SVM :{pred2}")
print(f"非線形SVM :{pred3}")
結果
今回は非線形SVMより線形SVM,ロジスティック回帰の方がよく学習できていた.
線形SVMとロジスティック回帰の正答率が一緒であることが少し気になった.
また,30歳男性の生死については3つの手法とも死者と判定された.
参考
・SVMについては,こちらの動画がとても分かりやすかったです.
https://www.youtube.com/watch?v=cNEhKEb9-JU