fc2ブログ

RからPythonへの道(15)

 前回からかなりの時間が経ちました。前回はGW前で、その後、機械学習ライブラリのPyCaretにハマっていたこともあり、このシリーズはしばらく休止状態でしたが、久々に復活です!

 今回は「14. 決定木(分類)(2)」について、PythonとRで計算していきたいと思います。教材は『データサイエンス教本』を参考にしました。 データはキノコデータセットで、22個の説明変数、1個の目的変数(食用キノコ(e)か毒キノコ(p)かの分類)の計23個です。以下の4つの説明変数(カテゴリ変数)を用いて解析を行います。

説明変数
 gill-color : ひだの色(12種類)
 gill-attachment : ひだがあるか(4種類、データは内2種類)
 odor : 臭い(9種類)
 cap-color : カサの色(10種類)

目的変数
 classes : 分類(2種類:食用キノコ/毒キノコ)

参考教材では「決定木」で分類する際の理論説明(エントロピーや情報利得)がされていますが、その部分は割愛し、データの「前処理」と「決定木の実行」のみをお話します。

まずは、Pythonのコードです。
# Decision Tree (Classification)
import pandas as pd
import requests
import io
import numpy as np
import matplotlib.pyplot as plt

#--------------------
# data preprocessing
#--------------------

# data read
url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/mushroom/agaricus-lepiota.data'
res = requests.get(url).content
mushroom = pd.read_csv(io.StringIO(res.decode('utf-8')), header=None)
print(mushroom.head())

# dara label
mushroom.columns = ['classes','cap-shape','cap-surface','cap-color','bruises','odor','gill-attachment', 'gill-spacing', 'gill-size','gill-color', 'stalk-shape', 'stalk-root', 'stalk-surface-above-ring','stalk-surface-below-ring', 'stalk-color-above-ring', 'stalk-color-below-ring', 'veil-type', 'veil-color', 'ring-number', 'ring-type', 'spore-print-color', 'population', 'habitat']
print(mushroom.head())
print('data stype:{}'.format(mushroom.shape))
print('missing number:{}'.format(mushroom.isnull().sum().sum()))

# dummy variable
mushroom_dummy = pd.get_dummies(mushroom[['gill-color', 'gill-attachment', 'odor', 'cap-color']])
print(mushroom_dummy.head())

# objective variable
mushroom_dummy['flg'] = mushroom['classes'].map(lambda x:1 if x == 'p' else 0)
print(mushroom_dummy['flg'])

#------------------------------
# data modeling and evaluation
#------------------------------
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix

# data split
X = mushroom_dummy.drop('flg', axis=1)
y = mushroom_dummy['flg']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
#print(X_train.shape)
#print(X_test.shape)

# decision tree model
model = DecisionTreeClassifier(criterion='entropy', max_depth=5, random_state=0) #5 -> 3
model.fit(X_train, y_train)

# Result : Calculation the accuracy and cross tabulation table
print('Accuracy rate(train): {:.3f}'.format(model.score(X_train, y_train)))
print(confusion_matrix(model.predict(X_train), y_train))
print('Accuracy rate(test) : {:.3f}'.format(model.score(X_test, y_test)))
print(confusion_matrix(model.predict(X_test), y_test))

# Graph
from sklearn import tree
import pydotplus
from sklearn.externals.six import StringIO
from IPython.display import Image

dot_data = StringIO()
tree.export_graphviz(model, out_file=dot_data)
graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
graph.write_pdf('DT_result.pdf')
 前処理では25行目で、4つの説明変数(カテゴリ変数)をダミー変数に分けて33個の変数にしました。目的変数(flg)は29行目で毒キノコなら1、食用ならば0に値を割り当てました。
 決定木の実行では、データをtrain : testを7 : 3に分けて、trainデータでモデルを作り、testデータでその予測をしました。また、train、testの双方でモデルの精度(スコア)も評価しました。

 Pythonコードの実行結果は以下の通りです。
# 16行目
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
0 p x s n t p f c n k e e s s w w p w o p k s u
1 e x s y t a f c b k e c s s w w p w o p n n g
2 e b s w t l f c b n e c s s w w p w o p n n m
3 p x y w t p f c n n e e s s w w p w o p k s u
4 e x s g f n f w b k t e s s w w p w o e n a g

# 20行目
classes cap-shape cap-surface cap-color bruises odor ... veil-color ring-number ring-type spore-print-color population habitat
0 p x s n t p ... w o p k s u
1 e x s y t a ... w o p n n g
2 e b s w t l ... w o p n n m
3 p x y w t p ... w o p k s u
4 e x s g f n ... w o e n a g

[5 rows x 23 columns]

# 21行目
data stype:(8124, 23)

# 22行目
missing number:0

# 26行目
gill-color_b gill-color_e gill-color_g gill-color_h gill-color_k ... cap-color_p cap-color_r cap-color_u cap-color_w cap-color_y
0 0 0 0 0 1 ... 0 0 0 0 0
1 0 0 0 0 1 ... 0 0 0 0 1
2 0 0 0 0 0 ... 0 0 0 1 0
3 0 0 0 0 0 ... 0 0 0 1 0
4 0 0 0 0 1 ... 0 0 0 0 0

[5 rows x 33 columns]

# 30行目
0 1
1 0
2 0
3 1
4 0
..
8119 0
8120 0
8121 0
8122 1
8123 0
Name: flg, Length: 8124, dtype: int64

# 51行目
Accuracy rate(train): 0.991
# 52行目
[[2936 52]
[ 0 2698]]
# 53行目
Accuracy rate(test) : 0.992
# 54行目
[[1272 20]
[ 0 1146]]
65行目の決定木の最終出力グラフは以下の通りです。DecisionTreeResult_Python_200531.png 次に、Rのコードです。
# Decision Tree (Classification)
library(dummies)

#--------------------
# data preprocessing
#--------------------

# data read
url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/mushroom/agaricus-lepiota.data'
mushroom = read.csv(url, header = FALSE)
print(head(mushroom))

# data label
colnames(mushroom) = c('classes','cap.shape','cap.surface','cap.color','bruises','odor','gill.attachment', 'gill.spacing', 'gill.size','gill.color', 'stalk.shape', 'stalk.root', 'stalk.surface.above.ring','stalk.surface.below.ring', 'stalk.color.above.ring', 'stalk.color.below.ring', 'veil.type', 'veil.color', 'ring.number', 'ring.type', 'spore.print.color', 'population', 'habitat')
print(head(mushroom))
print(paste("data style: ", dim(mushroom)[1], ",", dim(mushroom)[2]))
print(paste("missing number: ", sum(is.na(mushroom))))

# dummy variable
gill.color_dummy = dummy(mushroom$'gill.color', sep = ".")
gill.attachment_dummy = dummy(mushroom$'gill.attachment', sep=".")
odor_dummy = dummy(mushroom$'odor', sep=".")
cap.color_dummy = dummy(mushroom$'cap.color', sep=".")

# objective variable
flg_dummy = ifelse(mushroom$classes == 'p', 1, 0)
print(head(mushroom$classes))
print(head(flg_dummy))

mushroom_dummy = data.frame(cbind(gill.color_dummy, gill.attachment_dummy, odor_dummy, cap.color_dummy, flg_dummy))

#------------------------------
# data modeling and evaluation
#------------------------------
library(rpart)
library(rpart.plot)

# data split
X = mushroom_dummy[, colnames(mushroom_dummy) != "flg_dummy"]
y = mushroom_dummy[, c("flg_dummy")]
train.rate = 0.7 # training data rate
train.index = sample(nrow(mushroom_dummy),nrow(mushroom_dummy) * train.rate)
df_Train = mushroom_dummy[train.index ,]
df_Test = mushroom_dummy[-train.index ,]
cat("train=", nrow(df_Train), "test=", nrow(df_Test), "\n")

# decition tree model
rp = rpart(flg_dummy~., data = df_Train, method = 'class')
summary(rp)

# Predict
pred.rpart.train = predict(rp, df_Train, type = "class")
pred.rpart.test = predict(rp, df_Test, type = "class")

# Result : cross tabulation table
result.train = table(pred.rpart.train, df_Train$flg_dummy)
print(result.train)
result.test = table(pred.rpart.test, df_Test$flg_dummy)
print(result.test)

# Calculation the accuracy
accuracy_prediction_train = sum(diag(result.train)) / sum(result.train)
print(accuracy_prediction_train)
accuracy_prediction_test = sum(diag(result.test)) / sum(result.test)
print(accuracy_prediction_test)

# Graph
rpart.plot(rp , type = 4, extra = 1, digits = 3)
 Rコードの実行結果は以下の通りです。
# 11行目
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20 V21 V22 V23
1 p x s n t p f c n k e e s s w w p w o p k s u
2 e x s y t a f c b k e c s s w w p w o p n n g
3 e b s w t l f c b n e c s s w w p w o p n n m
4 p x y w t p f c n n e e s s w w p w o p k s u
5 e x s g f n f w b k t e s s w w p w o e n a g
6 e x y y t a f c b n e c s s w w p w o p k n g
# 15行目
classes cap.shape cap.surface cap.color bruises odor gill.attachment gill.spacing gill.size gill.color
1 p x s n t p f c n k
2 e x s y t a f c b k
3 e b s w t l f c b n
4 p x y w t p f c n n
5 e x s g f n f w b k
6 e x y y t a f c b n
stalk.shape stalk.root stalk.surface.above.ring stalk.surface.below.ring stalk.color.above.ring
1 e e s s w
2 e c s s w
3 e c s s w
4 e e s s w
5 t e s s w
6 e c s s w
stalk.color.below.ring veil.type veil.color ring.number ring.type spore.print.color population habitat
1 w p w o p k s u
2 w p w o p n n g
3 w p w o p n n m
4 w p w o p k s u
5 w p w o e n a g
6 w p w o p k n g
# 16行目
[1] "data style: 8124 , 23"
# 17行目
[1] "missing number: 0"

# 27行目
[1] p e e p e e
Levels: e p
# 28行目
[1] 1 0 0 1 0 0

# 45行目
train= 5686 test= 2438

# 49行目
Call:
rpart(formula = flg_dummy ~ ., data = df_Train, method = "class")
n= 5686

CP nsplit rel error xerror xstd
1 0.7538518 0 1.00000000 1.00000000 0.013819092
2 0.1085840 1 0.24614820 0.24614820 0.008924162
3 0.1060161 2 0.13756420 0.14636831 0.007065808
4 0.0100000 3 0.03154806 0.03154806 0.003376090

Variable importance
odor.n odor.f odor.l odor.a gill.color.b gill.color.u gill.color.w
42 13 13 11 7 5 5
gill.color.n
4

Node number 1: 5686 observations, complexity param=0.7538518
predicted class=0 expected loss=0.4794231 P(node) =1
class counts: 2960 2726
probabilities: 0.521 0.479
left son=2 (2461 obs) right son=3 (3225 obs)
Primary splits:
odor.n < 0.5 to the right, improve=1714.4280, (0 missing)
odor.f < 0.5 to the left, improve=1123.4170, (0 missing)
gill.color.b < 0.5 to the left, improve= 812.2295, (0 missing)
gill.color.n < 0.5 to the right, improve= 239.1147, (0 missing)
odor.s < 0.5 to the left, improve= 232.5788, (0 missing)
Surrogate splits:
odor.f < 0.5 to the left, agree=0.700, adj=0.307, (0 split)
gill.color.b < 0.5 to the left, agree=0.641, adj=0.171, (0 split)
gill.color.u < 0.5 to the right, agree=0.617, adj=0.116, (0 split)
gill.color.w < 0.5 to the right, agree=0.616, adj=0.113, (0 split)
gill.color.n < 0.5 to the right, agree=0.610, adj=0.098, (0 split)

Node number 2: 2461 observations
predicted class=0 expected loss=0.03494514 P(node) =0.4328174
class counts: 2375 86
probabilities: 0.965 0.035

Node number 3: 3225 observations, complexity param=0.108584
predicted class=1 expected loss=0.1813953 P(node) =0.5671826
class counts: 585 2640
probabilities: 0.181 0.819
left son=6 (296 obs) right son=7 (2929 obs)
Primary splits:
odor.a < 0.5 to the right, improve=436.7978, (0 missing)
odor.l < 0.5 to the right, improve=425.4514, (0 missing)
odor.f < 0.5 to the left, improve=188.9691, (0 missing)
gill.color.n < 0.5 to the right, improve=139.1077, (0 missing)
gill.color.b < 0.5 to the left, improve=123.4467, (0 missing)

Node number 6: 296 observations
predicted class=0 expected loss=0 P(node) =0.05205769
class counts: 296 0
probabilities: 1.000 0.000

Node number 7: 2929 observations, complexity param=0.1060161
predicted class=1 expected loss=0.09866849 P(node) =0.5151249
class counts: 289 2640
probabilities: 0.099 0.901
left son=14 (289 obs) right son=15 (2640 obs)
Primary splits:
odor.l < 0.5 to the right, improve=520.96960, (0 missing)
odor.f < 0.5 to the left, improve= 61.43912, (0 missing)
gill.color.n < 0.5 to the right, improve= 60.45658, (0 missing)
cap.color.w < 0.5 to the right, improve= 50.10237, (0 missing)
gill.color.w < 0.5 to the right, improve= 42.62831, (0 missing)
Surrogate splits:
gill.color.n < 0.5 to the right, agree=0.903, adj=0.021, (0 split)
gill.color.k < 0.5 to the right, agree=0.903, adj=0.017, (0 split)

Node number 14: 289 observations
predicted class=0 expected loss=0 P(node) =0.05082659
class counts: 289 0
probabilities: 1.000 0.000

Node number 15: 2640 observations
predicted class=1 expected loss=0 P(node) =0.4642983
class counts: 0 2640
probabilities: 0.000 1.000

# 57行目
pred.rpart.train 0 1
0 2960 86
1 0 2640

# 59行目
pred.rpart.test 0 1
0 1248 34
1 0 1156

# 63行目
[1] 0.9848751
# 65行目
[1] 0.9860541
68行目の決定木の最終出力グラフは以下の通りです。DecisionTreeResult_R_200531.png PythonとRとで、決定木の結果が同じにならないのはハイパーパラメータを含めた条件が同じでないからと考えています。このハイパーパラメータの調整等は今後、PyCaretの勉強と重ねて、調べてみようと思います。


『RからPythonへの道』バックナンバー
(1) はじめに
(2) 0. 実行環境(作業環境)
(3) 1. PythonからRを使う方法 2. RからPythonを使う方法
(4) 3. データフレーム
(5) 4. ggplot
(6) 5.行列
(7) 6.基本統計量
(8) 7. 回帰分析(単回帰)
(9) 8. 回帰分析(重回帰)
(10) 9. 回帰分析(ロジスティック回帰1)
(11) 10. 回帰分析(ロジスティック回帰2)
(12) 11. 回帰分析(リッジ、ラッソ回帰)
(13) 12. 回帰分析(多項式回帰)
(14) 13. 決定木(分類)(1)
スポンサーサイト



PyCaretを使う(6)

 PyCaretネタで長々と引っ張っていますが、今回は今まで処理していたデータセットの「前処理」についてです。PyCaretの紹介で「欠損データの補完」を自動でやってくれるとの話があったので、試してみたのですが、引っかかってうまく行かない所がありましたので、そのあたりをお話します。

 そもそものデータセットはUCIにあるautosというもので、以前の記事で重回帰分析を行ったものです。
 変数は目的変数(price)を合わせて26個あり、レコード数は205です。このデータセットにはデータ欠損もありますので、PyCaretでどのような自動処理がなされるのか、確認してみました。以下、3つのステップで説明します。

1. 1stステップ:生データのまま処理
 最終的には、目的変数'price'に対し、'horsepower'、'width'、'height'の3つの説明変数で回帰分析しましたが、取り急ぎ、26個の全変数のまま(205行×26列)で処理しました。
 setup関数を実行すると以下の変数の型確認画面が表示されますが、赤枠の変数は正しくは「Numeric」なので、誤認識していることが分かりました。pattern1_variables_200524.pngデータ型を確定するとデータの詳細が表示されますが、「4. Missing Values : False」、つまり「欠損データなし」になっていました。これも誤認識です。pattern1_setup_200524.png調査すると、欠損データに「?」が入っていることが原因と分かりました。このデータセットは欠損値が「NA」ではなく「?」で入っていたため、PyCaretでは欠損値として認識しなかったようです。変数が「Numeric」でなく「Categorical」と判断したのも、「?」があったためと分かりました。pattern1_lacked_data_200524.png
2. 2ndステップ:「?」を消去して「NA」にして処理
 データセット内の「?」を削除して(NAにして)、setup関数を実行すると、1.で「Categorical」と誤認識していた変数が「Numeric」に正しく判断されました。pattern2_variables_200524.pngデータ詳細も「4. Missing Values : True」で「欠損データあり」と判断されました。また、「10. Sampled Data」の26列のデータに対し、11. 12.のTrain/Testデータでは73列と列数が増えていました。これは「Categorical変数」を「Dummy変数」に分割したためですね。レコード数が144から141に減少しているのはなぜ?と思いつつ、この時、原因は良く分かっていませんでした。pattern2_setup_200524.pngこの状態でcompared_model関数を実行した所、結果に見た目で明らかな異常値がありました。何かおかしいと思い、データセットを再確認すると、目的変数のpriceにも欠損値が4個あることが分かりました。さすがに、欠損値があっては学習はできませんよね。

3. 3rdステップ: 目的変数の欠損しているレコードを消去して処理
 目的変数のpriceの欠損値のある4レコード分を削除して再実行しました。Numeric/Categoricalの判断は2ndステップと同様に正常で、データの詳細は以下の通りでした。pattern3_setup_200524.pngレコード数141に減少した理由がここで分かりました。2ndステップで144から141に減少していたのは目的変数の無効データ(欠損データ)の3レコード分が削除されたためでした。また、削除した4レコードとの差の1レコード分は「未知データ」の方に割り当てられていました。
 この状態でcompared_model関数を実行した所、今回は異常値もなく実行できました。
 欠損値処理はNumeric変数ならばデフォルトで「平均値」が割り当てられるとのことでしたが、念のため平均値を割り当てた欠損のないデータセットを作成し、検証するとその通りになっていました。

 今回、PyCaretの「前処理(欠損データ処理)」に着目しました。イレギュラーなデータが入っていない限り、NAデータがデータセットに入っていても、補完を正しく行ってくれそうな感触は得られました。今後も十分検証していこうと思います。

PyCaretを使う(5)

 前回の続きです。今回は前回の回帰モデルの中で、学習結果が良かったモデル「Gradient Boosting Regressor(以下、gbr)」を試し、前回の線形回帰「Linear Regression(以下、lr)」と比較してみようと思います。compared_modelの結果の1番目(No.0)と14番目(No.13)のモデルです。compared_model_200524.png その前に、gbrについて、全く理解していなかったので、ネットで下調べしました。
Gradient Boosting」とは「Boostingモデル」の一つで、弱学習器(予測精度の低いモデル)で学習した後の残差(真値と予測値の差)を求め、その残差にフィットするように次の弱学習器を作成して学習を繰り返しながら(予測誤差を小さくしながら)、最終的に最適モデルを生成する方法。「Boosting」とは「アンサンブル学習」の一種で弱学習器を直列に繋ぎ、各段の学習器は前段で間違いとなったデータに重点を置いて再学習する方法。「アンサンブル学習」とは、複数の学習器で学習させたものを、組み合わせて、1つの学習モデルを生成する方法です。
 詳細な理論や関数の中身は良く分かりませんが、最終的な予測誤差を小さくするために、複数の学習器(モデル)を導入して、それらを組み合わせたモデルというイメージでしょうか?

 gbrモデルを検証したコードは以下の通りです。以前の記事で記載したプログラムリストの29行目以降を置き換えて実行しました。
#'https://takacity.blog.fc2.com/blog-entry-314.html'の29行目以降
# Create a model (Gradient Boosting Regressor)
gbr = create_model('gbr')
print(gbr)

# Tune a Model
tuned_gbr = tune_model('gbr')
print(tuned_gbr)

# Plot a model (Evaluate a model)
evaluate_model(tuned_gbr)

print('evaluate_model')
print('Coefficient of determination: ', tuned_gbr.score(data[['horsepower', 'width', 'height']], data['price']))

# Predict a model
predict_model(tuned_gbr);

# Finalize model for deployment
final_gbr = finalize_model(tuned_gbr)
print(final_gbr)

# Predict a model
predict_model(final_gbr);

print('final_model')
print('Coefficient of determination: ', final_gbr.score(data[['horsepower', 'width', 'height']], data['price']))

# Predict on unseen data
unseen_predictions = predict_model(final_gbr, data=data_unseen)
unseen_predictions

print('Coefficient of determination: ', final_gbr.score(data_unseen[['horsepower', 'width', 'height']], data_unseen['price']))

plt.figure(figsize=(4, 4), dpi=100)
plt.scatter(unseen_predictions['price'], unseen_predictions['Label'], c = 'green')
plt.xlabel("price")
plt.ylabel("predicted value")
plt.xlim([0, 45000])
plt.ylim([0, 45000])
plt.title("True value vs predicted value : Gradient Boosting Regressor")
plt.show()

 プログラムの実行結果をたどって行く前に、未知データのpriceの真値と予測値のグラフを描画すると、以下の通りでした。x軸がpriceの真値、y軸がpriceの予測値なので、y=xになるのが理想的なグラフです。predict_results_200524.png左側がlrモデル、右側がgbrモデルです。グラフのスケールは同じなので、見ただけでgbrモデルが未知データを精度良く予測できていることが分かりました。

 次に、11行目のevaluate_model関数の種々のグラフを見ていきます。
① Residuals Plot(残差プロット)も見た目の通り、右側のgbrが小さいことが分かりました。residuals_plot_200524.png② Prediction Error Plot(予測誤差プロット)も、右側のgbrの誤差が小さいことが分かりました。このデータは未知データではなく既知データの結果です。prediction_error_plot_200524.png
 以下のグラフはgbrモデルの方が直感的に良い結果のように見えますが(③と⑦は同じ結果)、グラフ解釈はこれから勉強しようと思います。取り急ぎ、グラフの比較だけ添付します。

③ Cooks Distance Plot(クック距離プロット)cooks_distance_plot_200524.png④ Recursive Feature Selection(再帰的な特徴選択)recursive_feature_plot_200524.png⑤ Learning Curve(学習曲線)learning_curve_200524.png⑥ Validation Curve(検証曲線)validation_curve_200524.png⑦ Manifold Learning(多様体学習)manifold_learning_200524.png⑧ Feature Importance(特徴の重要性)feature_importance_200524.png
 チューニング前(4行目)とチューニング(ファイナライズ)後(8、21行目)のハイパーパラメータは以下の通り。決定(回帰)木の深さを深くして、学習器を増やしてチューニングしているようですが、これもこれから勉強です・・。hyperparameter_200524.png
 未知データの予測を行う上で、PyCaretを用いれば、種々の機械学習アルゴリズムから最適なものを選択し、ハイパーパラメータまでも自動で調整してくれることが実例を通じで良く分かりました。まだまだ、勉強不足で分からないことばかりなので、実践を繰り返しながら理解していこうと思います。
 次回は、このデータセットの前処理で引っかかった所をお話します。

PyCaretを使う(4)

 前回の続きです。今回は「7.モデルの予測」からお話します。ソースコードは前々回のblogを参照ください。

  7. モデルの予測 ー before (predict_model) ・・ 46行目
  8. モデルを完成(finalize_model) ・・ 49行目
  9. モデルの予測 ー after (predict_model) ・・ 53行目
 10. モデルの保存(save_model) ・・ 67行目
 
 7.のpredict_model関数をtuned_lrモデルに対して実行すると、
predict_model(tuned_lr)
以下の結果でした。predict_model_before_res_200517.pngこの結果は、機械学習用に準備したデータ(data:139行×4列)のうちの70%の訓練データ(97行×4列)の全部を使って作成されたモデル(tuned_lr)を使って、残りの30%のテストデータ(42行×4列)を予測したものです。Rで検証した決定係数R2の結果も0.7531432で同じでした。

 次に、8.のfinalize_model関数を実行してモデルを完成させて、再度、9.のpredict_model関数をfinal_lrモデルに対して実行すると、
final_lr = finalize_model(tuned_lr)
predict_model(final_lr)
以下の結果でした。predict_model_after_res_200517.pngこの結果は、機械学習用に準備したデータ(data:139行×4列)の全部を使って作成されたモデル(final_lr)を使って、30%のテストデータ(42行×4列)を予測したものです。テストするデータも学習に使われているので決定係数R2はfinalize_model関数を実行する前よりも大きな値になっています。Rで検証した決定係数R2の結果も0.8367989で同じでした。

 final_lrモデルについて、55〜58行目で回帰係数と決定係数を求めると以下の通りでした。
final_model
regression coefficients: [ 132.41522904 1607.80425251 25.87702066]
regression intercept: -107486.37943882798
Coefficient of determination: 0.781980427237323
この回帰係数や決定係数がどのデータを使って計算されているかを前回と同様に調べてみました。Rで検証した結果、機械学習用に準備したデータ(data:139行×4列)の全部を使って計算されたものでした。
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -107486.38 11650.70 -9.226 5.08e-16 ***
horsepower 132.42 11.68 11.335 < 2e-16 ***
width 1607.80 211.68 7.595 4.57e-12 ***
height 25.88 157.28 0.165 0.87
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3855 on 135 degrees of freedom
Multiple R-squared: 0.782, Adjusted R-squared: 0.7771
F-statistic: 161.4 on 3 and 135 DF, p-value: < 2.2e-16
 モデルが完成したので、未知のデータ(data_unseen)の予測を行い、決定係数R2を求めると以下の通りでした。
print('Coefficient of determination: ', final_lr.score(data_unseen[['horsepower', 'width', 'height']], data_unseen['price']))
Coefficient of determination: 0.6039095463072575
過学習気味だったのか、重回帰モデルが良くなかったのでしょう。

 最後に10.のsave_model関数を実行し、
save_model(final_lr, 'FinalModel_lr_200506')
モデルを保存しました。再度読み出す場合は、load_model関数を実行すれば良しです。

 次回は、同じデータを用いて、学習結果が良かったモデル「Gradient Boosting Regressor」を試してみたいと思います。

PyCaretを使う(3)

 前回の続きです。今回は「4.モデルの作成」からお話します。

  4. モデルの作成(compare_models、create_model) ・・ 27, 30行目
  5. モデルの調整(tune_model) ・・ 33行目
  6. モデルのプロット(評価)(evaluate_model(plot_model)) ・・ 36行目
 
 4.のPyCaretのcompare_models関数を実行すると、機械学習用に準備したデータ(data:139行×4列)のうちの70%の訓練データ(97行×4列)で、k分割交差検証(kのデフォルトは10)を用いて種々のアルゴリズムを自動で比較検証してくれます。
compare_models()
アルゴリズム毎に各種誤差(MAE、MSE、RMSE、RMSLE、MAPE)と決定係数R2が結果として表示されました。この機能は目標とするモデルのヒントを与えてくれるので、大変便利ですね。compared_model_res_200517.png今回の結果は「Gradient Boosting Regressor」というアルゴリズムが総合的に良いという結果でした。上位は「決定木関連」のアルゴリズムが多いような気がします。重回帰(Linear Regression)は14位(番号13)でした。
 1位のアルゴリズムは良く分からないので、次回以降検証したいと思います。今回は、取り急ぎ、重回帰(Linear Regression)のモデルで先に進めたいと思います。create_model関数を実行すると、
lr = create_model('lr')
Linear Regressionモデルの10分割交差検証の結果が表示されました。create_model_res_200517.pngこの平均値が先程の各種アルゴリズムの比較の際の14位(番号13)のデータですね。

 次に、5.のtune_model関数を実行し、モデルのハイパーパラメータの調整を行いますが、この部分もやり方を良く理解できていません。
tuned_lr = tune_model('lr')
結果はcreate_model関数の結果と同じでした。tune_model_res_200517.pngLassoやRidgeのような正則化パラメータのない純粋なLinear Regressionなので「自動」で調整するハイパーパラメータがないということなのでしょう?手動では設定可能?

 次に、6.のevaluate_model関数を実行し、モデルの評価を行いました。
evaluate_model(tuned_lr)
この関数を実行すると、以下のモデル評価に利用できる各種グラフ類が表示できます。

① Hyperparameters(ハイパーパラメータ)
  解析モデルのハイパーパラメータの確認画面。Hyperparameters_200517.png② Residuals Plot(残差プロット)
  横軸に予測値、縦軸に回帰残差をプロットし、残差の分布も表示。ResidualsPlot_200517.png③ Prediction Error Plot(予測誤差プロット)
  真値に対する予測値をプロットし、その誤差を可視化したグラフ。PredictionErrorPlot_200517.png④ Cooks Distance Plot(クック距離プロット)
  分析実行時のデータポイント(データの順番)に対する標準誤差等の影響を可視化したグラフ。CooksDistancePlot_200517.png⑤ Recursive Feature Selection(再帰的な特徴選択)
  変数の個数(次元)に対するスコアを可視化したグラフ。RecursiveFeatureSelection_200517.png⑥ Learning Curve(学習曲線)
  学習回数に対するスコアを可視化したグラフ。 LearningCurve_200517.png⑦ Validation Curve(検証曲線)
  ハイパーパラメーターの値を変えた時のスコアを可視化したグラフ
  今回のLinear Regressionでは変化させるハイパーパラメータがないので、以下のエラーが発生。
(Value Error): Estimator not supported in Validation Curve Plot.
⑧ Manifold Learning(多様体学習)
  非線形な変換による次元圧縮を可視化したグラフ。ManifoldLearning200517.png⑨ Feature Importance(特徴の重要性)
  各変数の重要性(寄与)を可視化したグラフ。FeatureImportance_200517.pngこれらのグラフは個別に「plot_model関数」を使っても出力できます。見慣れたグラフもありますが、初めて見るグラフもあり、詳細はこれから勉強しようと思います。

 次に、38行目のコードを実行すると、
print('type: ', type(tuned_lr))
結果は以下の通りで、scikit−learnのLinear Regressionを実行していることが分かりました。
type:  <class 'sklearn.linear_model._base.LinearRegression'>
40〜43行目で回帰係数と決定係数を求めると以下の通りでした。
evaluate_model
regression coefficients: [ 101.84567556 1943.8778975 81.17295392]
regression intercept: -130000.98509375166
Coefficient of determination: 0.7650871956159978
 この回帰係数や決定係数がどのデータを使って計算されているかを調べてみました。Rで検証した結果、機械学習用に準備したデータ(data:139行×4列)のうちの70%の訓練データ(97行×4列)の全部を使って計算されたものでした。
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -130000.99 14838.34 -8.761 8.53e-14 ***
horsepower 101.85 14.80 6.883 6.74e-10 ***
width 1943.88 257.02 7.563 2.74e-11 ***
height 81.17 184.64 0.440 0.661
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3917 on 93 degrees of freedom
Multiple R-squared: 0.77, Adjusted R-squared: 0.7626
F-statistic: 103.8 on 3 and 93 DF, p-value: < 2.2e-16
 長くなりましたので、続きの「7. モデルの予測」からは次回にお話します。

PyCaretを使う(2)

 前回の続きです。今回は「RからPythonへの道(9)」の重回帰分析でお話したデータセットを用いて、PyCaretで解析してみました。データセット内の多くの変数の中からhorsepower、width、heightの3つの数値データを説明変数として、priceを予測しました。

 PyCaretの実力がよく分からないので、まずは何も考えずに前処理もしていないデータセットを入れて、priceの予測を試みました。しかし、結論から言うと、データセットを読み込ませただけでは、自動の前処理(欠損データ処理等)がうまく機能しませんでした。前処理のマニュアル(チュートリアル)をじっくり読んだわけでないので、見落としている設定等があるのかもしれません・・。この前処理がうまく行かなかったパターンについては次回以降お話します。

 最終的には、説明変数3個、目的変数1個の欠損のないデータセットに加工して、実行させました。今回は、この最後までコードが実行できた最終パターンについてをお話します。実際にJupyter-Notebookで作成したコードは以下の通りです。
import numpy as np
import pandas as pd

# data read
auto = pd.read_csv('auto_raw_blankdelete2.csv')
auto = auto[["price", "horsepower", "width", "height"]]
auto.head()

# Data split
data_randomize = auto.sample(frac=1, random_state=0).reset_index(drop=True) # random sort
data = data_randomize.sample(frac=0.7, random_state=0).reset_index(drop=True) # train data ratio = 0.7
data_unseen = data_randomize.drop(data.index).reset_index(drop=True) # test data
#data.to_csv('auto_seen_blankdelete2.csv')
#data_unseen.to_csv('auto_unseen_blankdelete2.csv')

print(auto.head())
print(data.head())
print(data_unseen.head())
print('Data for Modeling: ' + str(data.shape))
print('Unseen Data For Predictions: ' + str(data_unseen.shape))

# Setting up environment in PyCaret
from pycaret.regression import *
regression1 = setup(data = data, target = 'price', session_id=100)

# Comparing All models
compare_models()

# Create a model (Linear Regression)
lr = create_model('lr')

# Tune a Model
tuned_lr = tune_model('lr')

# Plot a model (Evaluate a model)
evaluate_model(tuned_lr)

print('type: ', type(tuned_lr))

print('evaluate_model')
print('regression coefficients: ', tuned_lr.coef_)
print('regression intercept: ', tuned_lr.intercept_)
print('Coefficient of determination: ', tuned_lr.score(data[['horsepower', 'width', 'height']], data['price']))

# Predict a model
predict_model(tuned_lr)

# Finalize model for deployment
final_lr = finalize_model(tuned_lr)
print(final_lr)

# Predict a model
predict_model(final_lr)

print('final_model')
print('regression coefficients: ', final_lr.coef_)
print('regression intercept: ', final_lr.intercept_)
print('Coefficient of determination: ', final_lr.score(data[['horsepower', 'width', 'height']], data['price']))

# Predict on unseen data
unseen_predictions = predict_model(final_lr, data=data_unseen)
unseen_predictions

print('Coefficient of determination: ', final_lr.score(data_unseen[['horsepower', 'width', 'height']], data_unseen['price']))

# Save the model
save_model(final_lr, 'FinalModel_lr_200506')
プログラムフローは以下の通りです。
  1. データ読み込み ・・ 5行目
  2. データ分割 ・・ 11, 12行目
  3. 環境のセットアップ(setup) ・・ 24行目
  4. モデルの作成(compare_models、create_model) ・・ 27, 30行目
  5. モデルの調整(tune_model) ・・ 33行目
  6. モデルのプロット(評価)(evaluate_model(plot_model)) ・・ 36行目
  7. モデルの予測 ー before (predict_model) ・・ 46行目
  8. モデルを完成(finalize_model) ・・ 49行目
  9. モデルの予測 ー after (predict_model) ・・ 53行目
 10. モデルの保存(save_model) ・・ 67行目

3.以降がPyCaretの関数を実行しています。以下、細かく実行結果を見ていきます。

 1では、欠損データを削除し、説明変数3個、目的変数1個に加工したデータファイル(auto_raw_blankdelete2.csv)を読み込みました。rawdata_200510.png 2では、読み込みデータにランダムソートかけた後に、70%を機械学習にかけるデータ(data)、30%を未知の評価データ(data_unseen)としました。data_200510.png 3のPyCaretのSetup関数を実行すると、データ型(数値変数、カテゴリ変数)が自動で判断されます。priceが目的変数(Label)で、horsepower、width、heightの3つが説明変数(Numeric)と判断され、表示後、一旦停止しました。setup_200510.png問題なければ、下に出てくる入力枠内でリターンキーを押します。確定させた後の結果表示は以下の通りです。data_info_200510.png前処理を行った際の欠損データの有無、数値・カテゴリデータの各データ型の数、k分割交差検証用の訓練・テストデータの数などが表示されます。41項目ありますが、それぞれが何を意味しているかについては今後詳細を勉強して行きたいと思います。

 長くなりましたので、次回は続きの「4.モデルの作成」からお話します。

PyCaretを使う(1)

 先月下旬にネット記事で「PyCaret」という機械学習のモデル設計・評価を迅速にできる「無償ライブラリ」があると知りました。PyCaretはシンプルさ、使いやすさ、低コード環境で市民データサイエンティストに「機械学習」を提供する目的でMoez Ali氏により作られたもので、Jupyter NotebookやGoogle Colab等の推奨アプリで動かすものです。

 前処理の欠損値の補完、カテゴリカルデータの変換、複数の機械学習モデルの比較、評価、ハイパーパラメータの調整など、すべてを自動化してくれるとのことです。そんなことができて、「無償」なんて、ホンマかいな、とんでもないことだなと思ったのが素直な感想です。超高額で個人ユーザには到底手が出ない「Data○obot」みたいなことを、この無償ライブラリはしてくれるんだなぁと感動を覚えつつ、GWの長い外出しない休みを利用して、PyCaretのWebサイトから情報をいろいろ収集しました。

 分析モデルのアルゴリズムは現時点(20/05/06現在)で以下の70種類。
  Classification - 18
  Regression - 25
  Clustering - 9
  Anomaly Detection - 12
  Natural language Processing - 5
  Association Rule Mining Module - 1
具体的には以下の通り。Classification_200506.pngRegression_200506.pngClustering_200506.pngAnomalyDetection_200506.pngNatureLanguageProcessing_200506.pngDataRo○otにはアルゴリズムの種類では到底届きませんが、私自身、このすべてのアルゴリズムを把握しているわけではないので、十分過ぎるほどですね。今後もアルゴリズムの数は増えていくのではないかと期待しています。
 個人的に期待しているのは、要精査ですが、前処理が自動で行える点と、モデルを自動で複数比較できることにより、本命モデルの気付きを得られる点です。後者はこんな感じです。PyCaret_All.png
 以前からData○obotのようなAuto MLには大変興味を持っていました。データ解析モデルを作成するのに試行錯誤を繰り返し、時間がいくらあっても足りない状況を経験し続けています。ただ、Auto MLって、金額が高すぎて、会社でも簡単に予算が出ないですね・・。個人はなおさら・・。そのような意味で、この「PyCaret」でどの程度のことができるか分かりませんが、使い方次第ではデータ解析業務の時短には明らかに効果があるのではないかと思いました。今後、どこか大きな会社に買収され、有償にならないことを祈りつつ、いろいろ触りまくってみようと思います。

次回は、以前処理したことのある「重回帰分析」を例に、PyCaretで実行してみようと思います。

Ubuntuを18.04から20.04にUpgradeする

 先日(5/1)、RaspberryPi4でUbuntu20.04を動かしてみましたので、現在のUbuntu18.04マシン(HP製ノートPC)も20.04にUpgradeすることにしました。
 今年のGWは、どこにも外出できずに、気分も冴えないので、システムを刷新することで、少しでも気分をリフレッシュしたいものです。

 手順はUbuntuのサイトに記載がありましたので、その通りに行いました。18.04、19.10からはUSBメモリ等のインストールメディアの準備をしなくてもUpgradeができるんですね。大変便利!

 Upgrade前のOSは「Ubuntu 18.04.4 LTS」です。ubuntu1804.png① 「ソフトウェアとアップデート」を起動させ、Ubuntuの新バージョンの通知を「長期サポート(LTS)版」にしておきます。UbuntuUpgrade200504_1.png② ①の状態で、「ALT+F2」でコマンド実行画面を表示させ、
update-manager -c -d
を実行しました。UbuntuUpgrade200504_2.png③ 20.04が入手可能とのことなので、「アップグレード」ボタンを押しました。UbuntuUpgrade200504_3.pngあとは指示に従って進むだけです。元の設定ファイルを置き換えますか等、何度か聞いてきますが、置き換えずにそのまま進みました。
④ リリースノートの表示が出ますが、「アップグレード」を選択すると、UbuntuUpgrade200504_4.pngアップグレードの準備が始まります。UbuntuUpgrade200504_5.png⑤ サードパーティ関連のメッセージは「閉じる」で先に進めました。UbuntuUpgrade200504_6.png⑥ 「アップグレードを開始」ボタンを押すと、アップグレードがスタートしました。UbuntuUpgrade200504_7.png⑦ 最後に不要なパッケージを削除するかを聞いてくるので、「削除」を選択しました。UbuntuUpgrade200504_8.png⑧ アップグレードの最後は再起動です。「すぐに再起動」ボタンを押しました。UbuntuUpgrade200504_9.png
 Upgradeの総時間は1時間程度でした。Ubuntu 20.04 LTS Focal Fossaに生まれ変わり、壁紙も新たにFossa(マダガスカルマングース科の動物)になり、気分も刷新できました。フォルダがグレー調なのが渋いですね。ubuntu2004.png
 Upgrade後、念のため18.04の時にインストールしたアプリを動かしてみましたが、問題なく動作しました。環境もそのまま移行できたようです。しばらく、新鮮味がなくなるまで、新たな気分で使っていきたいと思います。

RaspberryPi4にUbuntu20.04をセットアップする(2)

 前回の続きです。今回は、SSHでリモート接続し、Desktop版をインストールするまでのお話です。Ubuntuの公式サイトの「4. Boot Ubuntu Server」以降の手順です。

 SSH接続は公式サイトの手順通りに行かなかったので、試行錯誤しながら、以下の流れで進めました。
 まず、手順にはRaspiのIPアドレスを知るために、「Arpコマンド」を実行しなさいとありますが、うまく行きませんでした。SSHconnection1__200502.png検出されたのは自宅ファイルサーバとして利用している有線LAN接続のRaspi2でした。Wi-Fi接続した今回のRaspi4は確認できず・・。以前にもRaspbianをセットアップした際に似たような経験があるんですよね・・。

 結局、Raspi4に直接接続した「HDMIディスプレイ画面」で確認することにしました。ifconfigコマンドを実行したのですが、インストールされていないとのことで、以下のコマンドで「net-tools」をubuntuにインストールしました。
sudo apt install net-tools
再度、ifconfigコマンドを実行すると以下の通りで、Wi-Fi接続しているRaspiのIPアドレスが「192.168.11.13」であることが分かりました。SSHconnection2_200502.png 気持ちを新たにSSH接続を試みると、今度は以下のようなNGが・・。SSHconnection3_200502.pngSSH接続許可がubuntu側でできていないようです・・。そこで以下の調査を実施しました。ubuntuの端末から以下のコマンドを実行。
apt search ssh-Server
SSHconnection_check1_200502.pngopenssh-serverがインストール済みであることを確認しました。次に、sshの設定を確認すべく、「/etc/ssh/sshd_config」ファイルをnanoエディタで編集し、
PermitRootLogin no
を追記して、SSHconnection_check2_200502.pngsshサービスを起動させました。
sudo /etc/init.d/ssh start

 再度、気持ちを新たにSSH接続を試みると、今度はうまく行きました。SSHconnection4_200502.png
 SSH接続がうまく行ったので、次は手順5の「Install a desktop」です。Desktop版は、xubuntu、lubuntu、kubuntuの3種類から選択できますが、一番軽量なlubuntuをインストールすることにしました。
 インストール中に以下の「Configuring gdm3」画面が出てきますので、「OK」とDesktopSetting1_200502.png「gdm3」を選択しました。DesktopSetting2_200502.png
 インストール後にUbuntuを再起動させると、良く見慣れたグラフィカルなログイン画面が表示され、DesktopRun1_200502.pngGUIのデスクトップ(デフォルトはアイコン非表示)が起動しました。DesktopRun2_200502.pngMacからリモートデスクトップ接続も可能でした。この画面もアイコンはデフォルトは非表示だったので、表示させました。RemoteDesktopFromMac_200502.pngリモート接続していない時と背景画像が異なるのが気にはなりますが、動作上は支障ありませんでした。

 Raspi4にubuntu20.04を無事にセットアップし、リモート接続もできるようになりました。通常のPCと同じようにいろいろなことをやらせてみようと思います。

RaspberryPi4にUbuntu20.04をセットアップする(1)

 ネット記事でRaspberry pi 4に「Ubuntu 20.04 LTS(最新版)」を入れることができるとありましたので、早速試してみました。3月、4月は超多忙でRaspi4を購入したものの、あまり触れていなかったので、ちょうど良いテーマが出てきた感じです。GWですが、新型コロナウィルスのため外出もできない状態なので、時間は十分あります・・。

 手順はUbuntuの公式サイト内にありましたので、それに従って行いました。SDカードへのセットアップ作業はMacBook Airを使いました。

① 手順1の「Overview」で必要なものをチェックします。すでにRaspbianでRaspi4は使っていたので、SDカードを除いて準備済みです。SDカードは新規にUbuntu用を作成するようにし、SDカードを差し替えるだけでRaspbianに戻せるようにしました。

② 手順2の「Prepare the SD Card」では、セットアップに使うMacのもの「Raspberry Pi Imager for macOS」を選択し、「imager.dmg」をダウンロードしました。Imager_download1.png③ imager.dmgを実行するとImager_download2.pngRaspberry Pi Imager.appをApplicationsフォルダにインストールする指示が表示されますので、その通り実行しました。Imager_run1.png次に、アプリケーション(Applications)フォルダ内の「Raspberry Pi Imager.app」を実行させると、Imager_run2.png警告が出ますが、そのまま「開く」で、SDカードのセットアップをスタートさせました。Imager_run3.png④ Raspberry Pi Imagerが立ち上がりました。OSを選択し、SDカードに書き込んでくれるアプリです。SDcard_install1.pngまず、Operating Systemの「CHOOSE OS」ボタンを押して、希望のOS(Ubuntu 20.04 LTS)を選びました。Raspbianをはじめ、他のOSも色々選択できそうですね。ここでセットアップするのはUbuntuの「Server版」なのですが、Desktop版はサーバを起動させた後に、ターミナルからコマンドをたたいてDesktop版をインストールする手順です。SDcard_install2.png次に、SD cardの「CHOOSE SD CARD」ボタンを押して、SDcard_install3.pngセットアップしたいSDカードを選択しました。SDcard_install4.pngOSとSDカードを設定できたら、「WRITE」ボタンを押してセットアップ開始です。SDcard_install5.png警告が出ますが、パスワードを入力して、「OK」で、セットアップが終わるまで、しばらく待ちます。SDcard_install6.png大変楽にセットアップが完了しました。MacでRaspi用の起動ディスクを今まで何度か作成しましたが、結構面倒なんですよね。このアプリを使うと大変便利で感動モノです・・。

⑤ セットアップされたディスクのボリューム名はsystem-bootで中身は以下の通りです。system-boot_disk.png⑥ 次に、手順3の「Wi-Fi or Ethernet」に記載の通り、ネットワーク設定を行いました。Wi-Fiで立ち上げるべく、network-configファイルを以下の通り、編集しました。network_setting.png⑦ Raspi4本体に、キーボード、マウス、HDMIディスプレイ、電源ケーブルを接続し、電源ONしました。無事に起動し、初回ログイン時のパスワード変更を行い(デフォルトはID、Passwordともにubuntu)、Ubuntu_run1.png正常に起動することができました。Ubuntu_run2.png
次回は、SSHでリモート接続し、Desktop版をインストールする手順をお話します。

ご訪問者数

(Since 24 July, 2016)

タグクラウド


プロフィール

Dr.BobT

Author: Dr.BobT
興味のおもむくままに生涯考え続けるエンジニアでありたい。

月別アーカイブ

メールフォーム

名前:
メール:
件名:
本文: