tidymodels
tidymodels は統一的なインターフェースでモデリングを行うための、いくつかのパッケージをまとめたもの。
rsample:訓練データ/テストデータの分割recipe:データの前処理(変数変換、サンプリングなど)parsnip:モデリングyardstic:モデルの精度評価
データの分割 : rsample パッケージ
Train/Test 分割 : initial_split()
set.seed(10)
dm_split <- rsample::initial_split(data, prop = 3/4, strata = NULL, breaks = 4, ...)
# 時系列データの分割の場合
# dm_split <- initial_time_split(data, prop = 3/4, lag = 0, ...)
# 訓練データとテストデータのと取り出し
train_df <- rsample::training(dm_split)
test_df <- rsample::testing(dm_split)
prop: 訓練データの割合strata: 層化サンプリングに使用する変数を指定する。(ここで指定した変数の値の頻度分布は、訓練データとテストデータで同じになる)breaks: 整数スカラー。層化サンプリングしたい変数が連続変数の時に、連続地をいくつのビンに分けるか指定する。lag: テストと訓練の間にラグを含めるための値。これはラグのある予測変数が使用される場合に便利です。
交差検証のための分割 : vfold_cv()
K交差検証
rsample::vfold_cv(data, v = 10, repeats = 1, strata = NULL, breaks = 4, ...)
data: データフレームv: CVの分割数 (fold数)repeats: CVの分割を何回繰り返すか。総計算回数はv*repeatsになる。strata: 層化サンプリング。CV分割の時に、fold間で割合を維持したい変数を指定する(例えば目的変数がカテゴリ変数の時に指定する)breaks: 整数スカラー。層化サンプリングしたい変数が連続変数の時に、連続地をいくつのビンに分けるか指定する。
グループK交差検証
rsample::group_vfold_cv(data, group = NULL, v = NULL, ...)
data: データフレームgroup: グルーピングに使いたい変数名1つv: データを分割したい数、NULLの場合はグルーピング変数のユニークな値の数になる
Foldのデータを参照する
各Foldのデータを取り出すには以下の関数を使用する
analysis(): 訓練データを参照するassessment(): 検証データを参照する
vfold_cv() 関数の出力値は vfold_cv オブジェクト、これは実際にはネストされたデータフレームである。
df_cv <- rsample::vfold_cv(data, v = 10, repeats = 1)
> class(df_cv)
[1] "vfold_cv" "rset" "tbl_df" "tbl" "data.frame"
ある1つの訓練・検証データのペアを取り出す
train <- rsample::analysis(df_cv$splits[[1]])
validation <- rsample::assessment(df_cv$splits[[1]])
リピートなしの分割
> df_cv <- rsample::vfold_cv(dm_info_train_df, strata = "month", v = 2, repeats = 1)
> df_cv
# 2-fold cross-validation using stratification
# A tibble: 2 x 2
splits id
<list> <chr>
1 <rsplit [363/364]> Fold1
2 <rsplit [364/363]> Fold2
リピートありの分割
> df_cv <- rsample::vfold_cv(dm_info_train_df, strata = "month", v = 2, repeats = 2)
> df_cv
# 2-fold cross-validation repeated 2 times using stratification
# A tibble: 4 x 3
splits id id2
<list> <chr> <chr>
1 <rsplit [363/364]> Repeat1 Fold1
2 <rsplit [364/363]> Repeat1 Fold2
3 <rsplit [363/364]> Repeat2 Fold1
4 <rsplit [364/363]> Repeat2 Fold2
前処理 : recipes パッケージ
訓練や新規データへのモデルの適用の前に実施する変数変換やデータサンプリングの手順をレシピオブジェクトとして定義する。
recipes パッケージで実施する前処理とは、特徴量作成というよりも、対数変換や標準化などデータの本質的な意味は変えないが、アルゴリズムの学習を適切に行えるようにするための処理のことを指す。
そのため、この前処理は特徴量作成の後モデリングの直前に実施する。
レシピオブジェクトの作成 : recipe()
最初に、変数加工のためのパラメタ(標準化のための平均と分散など)を決めるために使う訓練データ(一般には訓練データ)
そのデータに含まれる目的変数と説明変数をフォーミュラとして与える。
モデル式(目的変数と説明変数)を指定
recipes::recipe(x, formula = NULL, ..., vars = NULL, roles = NULL)
x,data: 変数加工のためのパラメタ(標準化のための平均と分散など)を決めるために使う訓練データformula: モデル式、目的変数と説明変数の指定。log(x)とかx:yなどの関数を含めてはいけない。マイナス符号をつけるのもダメ。vars: 変数名を格納した文字列ベクトルroles:varsと同じ長さの文字列ベクトル。各変数の役割を指定する。"outcome", "predictor", "case_weight", or "ID"他の値でもいい
mod_rec <-
df_rental %>%
recipes::recipe(formula = Y ~ Area)
各前処理ステップの定義 : step_*()
データに対する加工は step_*() 関数を使う
dplyr
step_filterFilter rows using dplyrstep_arrangeSort rows using dplyrstep_mutateAdd new variables using ‘mutate’step_mutate_atMutate multiple columnsstep_renameRename variables by namestep_rename_atRename multiple columnsstep_sliceFilter rows by position using dplyr
数値データ変換
step_centerCentering numeric datastep_cutCut a numeric variable into a factorstep_discretizeDiscretize Numeric Variablesstep_normalizeCenter and scale numeric datastep_logLogarithmic Transformationstep_logitLogit Transformationstep_BoxCoxBox-Cox Transformation for Non-Negative Datastep_hyperbolicHyperbolic Transformationsstep_inverseInverse Transformationstep_invlogitInverse Logit Transformationstep_rangeScaling Numeric Data to a Specific Rangestep_reluApply (Smoothed) Rectified Linear Transformationstep_scaleScaling Numeric Datastep_sqrtSquare Root Transformationstep_YeoJohnsonYeo-Johnson Transformation
カテゴリ変数 factor
step_factor2stringConvert Factors to Stringsstep_integerConvert values to predefined integersstep_bin2factorCreate a Factors from A Dummy Variablestep_novelSimple Value Assignments for Novel Factor Levelsstep_num2factorConvert Numbers to Factorsstep_otherCollapse Some Categorical Levelsstep_ordinalscoreConvert Ordinal Factors to Numeric Scoresstep_relevelRelevel factors to a desired levelstep_string2factorConvert Strings to Factorsstep_unknownAssign missing categories to “unknown”step_unorderConvert Ordered Factors to Unordered Factors
変数取捨選択
-
step_shuffleShuffle Variables -
step_rmGeneral Variable Filter -
step_zvZero Variance Filter -
step_nzvNear-Zero Variance Filter -
step_corrHigh Correlation Filter -
step_lincombLinear Combination Filter
#変数追加
step_countCreate Counts of Patterns using Regular Expressionsstep_ratioRatio Variable Creation
時系列
step_dateDate Feature Generatorstep_holidayHoliday Feature Generatorstep_lagCreate a lagged predictor
サンプリング
step_downsampleDown-Sample a Data Set Based on a Factor Variablestep_sampleSample rows using dplyrstep_upsampleUp-Sample a Data Set Based on a Factor Variable
統計モデリング
step_dummyDummy Variables Creationstep_interactCreate Interaction Variablesstep_interceptAdd intercept (or constant) columnstep_regexCreate Dummy Variables using Regular Expressions
地理情報
step_geodistDistance between two locationsstep_spatialsignSpatial Sign Preprocessing
欠損値
step_naomitRemove observations with missing values
欠損値補完 : imputation
step_knnimputeImputation via K-Nearest Neighborsstep_lowerimputeImpute Numeric Data Below the Threshold of Measurementstep_meanimputeImpute Numeric Data Using the Meanstep_medianimputeImpute Numeric Data Using the Medianstep_modeimputeImpute Nominal Data Using the Most Common Valuestep_impute_linearImputation of numeric variables via a linear model.step_bagimputeImputation via Bagged Treesstep_rollimputeImpute Numeric Data Using a Rolling Window Statistic
抽出? : Extraction
step_icaICA Signal Extractionstep_kpcaKernel PCA Signal Extractionstep_kpca_polyPolynomial Kernel PCA Signal Extractionstep_kpca_rbfRadial Basis Function Kernel PCA Signal Extractionstep_nnmfNNMF Signal Extractionstep_pcaPCA Signal Extractionstep_plsPartial Least Squares Feature Extraction
その他
-
step_bsB-Spline Basis Functions -
step_nsNatural Spline Basis Functions -
step_polyOrthogonal Polynomial Basis Functions -
step_windowMoving Window Functions -
step_classdistDistances to Class Centroids -
step_depthData Depths -
step_isomapIsomap Embedding -
step_profileCreate a Profiling Version of a Data Set
サンプリング・ステップ
# サンプリングを含むレシピを作成 (prep()しない)
sampling_recipe <-
recipes::recipe(Y ~ ., data = train_df) %>%
# 目的変数 Y の値に基づいてサンプリングする
# 訓練データに対してはサンプリングするが
# テストデータや新規データに対してはサンプリングしない場合は skip = TRUE を指定する。
# この時点でサンプリングのための乱数 seed は固定される
themis::step_downsample(Y, under_ratio = 0.5, skip = TRUE) %>%
# prep() を実行すると skip = TRUE を指定したステップも実行される
# retain = TRUE で、最初に渡した train_df にレシピが適用されたデータを
# レシピオブジェクトの中に保持する
recipes::prep(retain = TRUE)
# サンプリングが適用されたデータの作成方法
# 1. レシピオブジェクトの中に存在する
# レシピ適用済みデータを取り出す
sampled_train_df <-
sampling_recipe$template
# 2. レシピオブジェクトの中に存在する
# レシピ適用済みデータを bake(newdata=NULL) で取り出す
sampled_train_df <-
sampling_recipe %>%
bake(newdata=NULL)
# 注意:bake() に新データを渡して新データにレシピを適用したときには、サンプリングステップ は実行されない。(正確は `skip = TRUE` が指定されたステップは実行されない)
NOT_sampled_test_df <-
sampling_recipe %>%
bake(newdata=test_df) # newdata で渡したデータにはサンプリングは適用されない
サンプリングを含んだクロスバリデーション
クロスバリデーションをするときには、Validation データにはサンプリングを実施しないが、学習データに対してはサンプリングを実施したい。
# CVのために(サンプリングをされていない)訓練データを分割する
df_cv <-
rsample::vfold_cv(train_df, v = 5, repeats = 1)
# サンプリングを含むレシピを作成 ()
# 注意するのはここでは prep() しないということ
sampling_recipe <-
recipes::recipe(Y ~ ., data = train_df) %>%
# テストデータや新規データに対してはサンプリングしない場合は skip = TRUE を指定する。
themis::step_downsample(Y, under_ratio = 0.5, skip = TRUE)
# このレシピを使ってクロスバリデーションをする場合には、ここでは prep() しない
# おそらく rsample::vfold_cv() の中で prep() される
# CV を実行する
cv_result_df <-
tune::tune_grid(
object = rnd_forest_model, # parsnip で作成したモデルオブジェクト
preprocessor = sampling_recipe,
resamples = df_cv,
grid = params_grid_df, # 探索したいパラメータの値が格納されたデータフレーム
metrics = yardstick::metric_set(yardstick::pr_auc, yardstick::roc_auc),
control = tune::control_grid(verbose = TRUE)
)
skip=TRUE について
skip=TRUE が指定されたステップは、 recipes::prep(retain = TRUE) を実行したときには適用されるが、recipes::bake(newdata = new_df) を実行したときには適用されない。
次のように考えてもよいかもしれない
recipes::prep()は訓練データ作成用にレシピを実行する。recipes::bake()は新データ・テストデータ・検証データ作成用にレシピを実行する
skip = TRUE を指定された処理は訓練データを作成するときには使用されるが、新データやテストデータや検証データに対しては使用されない。不均衡データ対策のためなど、目的変数を使ったサンプリングを実施するステップに対しては skip = TRUE を指定する。
step_*() の中での変数の指定の仕方
step_log(all_predictors(), all_outcomes())
dplyrの starts_with() contains() 等も使える
prep() パラメータを持つステップの訓練・更新
rec_trained <-
prep(mod_rec, retain = TRUE, verbose = TRUE)
retain: 処理済みの訓練データをオブジェクト内に保持する (templateスロット)。後からステップを追加したいが、既存のステップの再トレーニングを避けたい場合に良いアイデア。skip = FALSEオプションを使用しているステップがある場合は、retain = TRUEを使用することをお勧めします。training: データ処理のパラメタ(標準化のための平均と分散とか)の訓練のために使うデータフレームを別に指定する場合fresh:TRUEなら、新たに訓練データを与えてパラメータを更新する。同時に、trainingに新たな訓練データを与えること。fresh=TRUEは前にprep()したstep_*()の方も更新する。fresh=FALSEなら まだprep()されていないstep_*()を更新する。
レシピを新しいデータに適用する bake()
以前は bake() は新データにレシピを適用する。 juice() はレシピ内に保持されたデータに対してレシピを適用するという意味だったが。今は juice() の代わりに bake(newdata=NULL) を使うことが推奨されている。
モデリング : parsnip パッケージ
まずは目的に合わせてアルゴリズムを指定して、モデルオブジェクトを作成する。
parsnip は異なるパッケージのアルゴリズムを同じインターフェースで扱えるようにする。
モデルオブジェクトの作成
以下のアルゴリズムが使用できる
parsnip::linear_reg()parsnip::decision_tree()parsnip::rand_forest()
それぞれのアルゴリズムで具体的にどのパッケージを使うかは parsnip::set_engine() で指定する。この関数はさらに使用するパッケージの機械学習モデルに追加で渡す引数を指定することもできる。
rf_model <-
parsnip::linear_reg() %>%
parsnip::set_engine("ranger", , num.threads = 10, seed = 42, importance = "permutation)
Thanks @ailich. Yea, that may be a confusion point. I would condider to add the explanation on the Chapter on the LogicalVector.
I really appreciate this kinds of advice to make the book sensible for person who are not familiar to C++.
線形回帰 : parsnip::linear_reg()
linear_regression_model <-
parsnip::linear_reg() %>%
parsnip::set_engine("lm")
決定木 : parsnip::decision_tree()
# 分類木
decision_tree_model <-
parsnip::decision_tree(
mode = "classification",
cost_complexity = cost_complexity,
tree_depth = tree_depth,
min_n = min_n)
決定木とランダムフォレストの実行の仕方 (rpart, ranger) は以下のサイトが参考になる
https://www.marketechlabo.com/r-decision-tree/
ランダムフォレスト : parsnip::rand_forest()
parsnip::rand_forest(mode = "unknown", mtry = NULL, trees = NULL, min_n = NULL)
## S3 method for class 'rand_forest'
update(
object,
parameters = NULL,
mtry = NULL,
trees = NULL,
min_n = NULL,
fresh = FALSE,
...
)
- mode : Possible values for this model are “unknown”, “regression”, or “classification”.
- mtry: The number of predictors that will be randomly sampled at each split when creating the tree models.
- trees: The number of trees contained in the ensemble.
- min_n: The minimum number of data points in a node that are required for the node to be split further.
エンジン
parsnip::set_engine("ranger", num.threads = 10, seed = 42, importance = "permutation")
set_engine()
- R: “ranger” (the default) or “randomForest”
- Spark: “spark”
変数重要度
ranger で変数重要度を計算させる場合は parsnip::set_engine() に以下のいずれかを指定する
importance = 'impurity'importance = 'permutation'
モデル学習
モデル学習の実行 : fit()
lm_fit <- # 学習済みモデルオブジェクト
lm_mod %>%
fit(width ~ initial_volume * food_regime, # 目的変数と説明変数をフォーミュラで指定
data = data_mart_df
)
学習済みモデルオブジェクトの内容を確認する
tidy(lm_fit)
# 線形回帰の学習済みオブジェクトを tidy() で処理した出力を dotwhisker::dwplot() に入力して結果を見る
tidy(lm_fit) %>%
dotwhisker::dwplot(dot_args = list(size = 2, color = "black"),
whisker_args = list(color = "black"),
vline = geom_vline(xintercept = 0, colour = "grey50", linetype = 2))
予測 : predict
mean_pred <-
predict(
object = lm_fit,
new_data = new_points,
type = "conf_int"
)
-
object: 学習済みモデルオブジェクトmodel_fitクラス -
new_data: 予測を適用するデータ -
type: 予測で出力する値のタイプ"numeric", "class", "prob", "conf_int", "pred_int", "quantile", or "raw"、指定しない場合はモデルのmodeに合わせて適切に選ばれる -
opts:type = "raw "のときに使用される引数の値を指定したリスト -
...: その他の引数level:typeが"conf_int"または"pred_int"の時、ここでlevel=0.95のように区間の値を指定する。std_error:typeが"conf_int"and"pred_int"のときに、誤差の値も出力するか指定する。TRUEなら出力する。FALSEなら出力しない。デフォルトはFALSE
add the standard error of fit or prediction (on the scale of the linear predictors) for types of “conf_int” and “pred_int”. Default value is FALSE.
quantile: the quantile(s) for quantile regression (not implemented yet)time: the time(s) for hazard probability estimates (not implemented yet)
ハイパーパラメータ・チューニング : tune()
https://note.com/tqwst408/n/n2483a75d82a0
finetune パッケージを使うと精度が低いと考えられるハイパラの値については繰り返しを削減して計算効率を上げることができる。
クロスバリデーションのデータの分割
# Partitioning data for CV
df_cv <- rsample::vfold_cv(sampled_train_df, v = 5)
機械学習アルゴリズムとチューニングしたいハイパーパラメータを指定する
parsnip パッケージでモデルオブジェクトを作成する際に、交差検証で探索したいハイパーパラメータを選ぶ。そのために、探索したいパラメータの値として tune::tune() を指定する。
# モデルオブジェクトの作成と
# チューニングするハイパーパラメータの指定
rnd_forest_model <-
parsnip::rand_forest(
mode = "classification",
trees = tune::tune(),
min_n = tune::tune(),
mtry = tune::tune()
) %>%
parsnip::set_engine("ranger", num.threads = 10, seed = 42)
CVの並列化
あるパラメタ値について、CVの各Foldの計算を並列化するために foreach パッケージを使用している。そのためには foreach のバックエンドの設定をする必要がある。
# コア数を調べる
n.cores <- parallel::detectCores()
# クラスタを作成
my.cluster <- parallel::makeCluster(
n.cores, # 並列数は基本的にFold数と同じにする。Fold数よりも多くても意味がない。
type = "PSOCK" # Mac/Linux の場合は "FORK" にするそちらのほうが効率が良い
)
# 作成したクラスタを確認
print(my.cluster)
# 作成したクラスタを foreach に登録する
doParallel::registerDoParallel(cl = my.cluster)
# 登録されたか確認する
foreach::getDoParRegistered()
# クラスタを停止するとき
parallel::stopCluster(my.cluster)
グリッドサーチ: tune::tune_grid()
探索したいパラメータの範囲を指定した後で、その範囲の中からランダム・規則的に100個の点を抽出する。
# 探索するパラメータの範囲を指定した後で、
# その範囲の中からランダムに100個の点を抽出する
params_grid_df <-
list(
min_n = dials::min_n(range = c(100, 1000)),
mtry = dials::mtry(range = c(3, 5)),
trees = dials::trees(range = c(100, 1000))
) %>%
tune::parameters() %>%
# ランダムグリッドの場合
dials::grid_random(size = 100)
# レギュラーグリッドの場合
#dials::grid_regular(size = 100)
# クロスバリデーションの実行
cv_result_df <-
tune::tune_grid(
object = rnd_forest_model,
preprocessor = y ~ .,
resamples = df_cv,
grid = params_grid_df,
metrics = yardstick::metric_set(yardstick::pr_auc, yardstick::roc_auc),
control = tune::control_grid(verbose = TRUE)
)
object:parsnipパッケージで作成したモデルオブジェクト、あるいは 内部でモデルを保持したworkflows::workflow()で作成したワークフローオブジェクトpreprocessor: モデルフォーミュラ、あるいは、recipes::recipe()で作成したレシピオブジェクトresamples:rsetオブジェクト、rsample::vfold_cv()の出力結果param_info: 探索したいハイパーパラメータの値の範囲、dials::parameters()オブジェクト、あるいはNULLgrid: 1.param_info=NULLの時、探索したい具体的なパラメータの値が記録されたデータフレーム(カラム名はハイパーパラメータ名と一致する)、 2. 正の整数値、param_infoに値が渡されたとき (param_infoの範囲からgridで指定された数のハイパーパラメータ点が探索される)metrics: 計算したい精度指標をyardstick::metric_set()を使って指定する。NULLなら自動で選ばれるcontrol: チューニングをいじるために使われるオブジェクト。control = tune::control_grid(verbose = TRUE)でCVの経過を出力する(Sys.setlocale(locale="English")をセットしないと一部文字化けする)。
ベイジアン最適化 : tune::tune_bayes()
# 探索するパラメータの範囲を指定する
# その範囲の中からランダムに100個の点する
params_grid_df <-
list(
min_n = dials::min_n(range = c(100, 1000)),
mtry = dials::mtry(range = c(3, 5)),
trees = dials::trees(range = c(100, 1000))
) %>%
tune::parameters()
# クロスバリデーションの実行
cv_result_df <-
tune::tune_grid(
object = rnd_forest_model,
preprocessor = y ~ .,
resamples = df_cv,
grid = params_grid_df,
metrics = yardstick::metric_set(yardstick::pr_auc, yardstick::roc_auc),
control = tune::control_grid(verbose = TRUE)
)
tune_bayes(
object,
...,
preprocessor,
resamples,
iter = 10,
param_info = NULL,
metrics = NULL,
objective = exp_improve(),
initial = 5,
control = control_bayes()
)
object:parsnipで作成したモデルオブジェクトか workflows::workflow()....: オプション、内部で使われるGPfit::GP_fit()に渡される引数 (主にはcorr引数)preprocessor: モデルフォーミュラかrecipes::recipe()で作成したレシピresamples:rset()オブジェクトrsample::vfold_cv()の返値とかiter: 探索の繰り返しの最大回数param_info: 探索したいパラメタの範囲を指定するdials::parameters()オブジェクト。NULLの場合は他の引数に基づいてパラメターセットが生成される。metrics: 精度評価指標yardstick::metric_set()オブジェクト。複数の指標を与えた場合には最初の指標が最適化される。objective: どのメトリックを最適化すべきかを示す文字列、または 獲得関数(acquisition function)オブジェクト(これなら複数の精度指標を組み合わせた最適化ができる??)。initial: 初期値(tune_grid()の結果と同様の形式、というかユーザーが事前計算したtune_grid()の結果をtune_bayes()に渡す)。あるいは正の整数(この場合は内部で自動でtune_grid()をしてから計算する)。最初に渡す結果の数はパラメタの数よりも多いほうが良い。control:control_bayes()で作成した control オブジェクト
tune::fit_resamples()
CVのなかの1つの計算は tune::fit_resamples() が使われている??
## S3 method for class 'model_spec'
fit_resamples(
object,
preprocessor,
resamples,
...,
metrics = NULL,
control = control_resamples()
)
## S3 method for class 'workflow'
fit_resamples(
object,
resamples,
...,
metrics = NULL,
control = control_resamples()
)
クロスバリデーションの結果の確認
tune::collect_metrics(cv_result_df) %>%
filter(.metric == "pr_auc") %>%
arrange(desc(mean))
ベストのパラメターで再学習
best_param <-
cv_result_df %>%
select_best(metric = "pr_auc") %>%
select(-.config)
model_best <- update(rnd_forest_model, best_param)
fitted_model <- fit(model_best,
formula = flag_detected_by_viirs ~ .,
data = sampled_train_df)
精度検証 : yardstick
yardstick::roc_auc()yardstick::pr_auc()yardstick::roc_curve()yardstick::pr_curve()yardstick::gain_curve()yardstick::lift_curve()