English Русский 中文 Español Deutsch Português
ディープニューラルネットワーク(その2)予測変数の変換と選択

ディープニューラルネットワーク(その2)予測変数の変換と選択

MetaTrader 5統合 | 4 10月 2017, 07:28
1 304 0
Vladimir Perervenko
Vladimir Perervenko

内容


はじめに

前稿では、入力データと目標変数の取得と準備のさまざまな側面について検討しました。本稿のスクリプトを実行するには、前稿のスクリプトをすべて実行するか、前稿のRStudioアプリケーションの計算結果を読み込む必要があります。

1. 特徴の生成

特徴の生成とは、手元のデータから追加情報を取得する科学(及び技術)です。ここでの目標は、新しいデータを追加することではなく、すでに持っているものを活用することです。新能力によって、データサンプルの新しい特徴を得ることができ、これらの特徴によって、訓練データセットのより正確なラベル化、特性評価、及び分割が可能になります。これにより、より高い精度が得られます。

この過程は次の2つの段階に分けることができます。

  • 変換 - これはシナリオによって、データの正規化、変数の歪みの除去、外れ値の除去、離散化の4種類の変換のいずれかになります。
  • 特徴の生成 - 新しい特徴の生成とは既存の変数から新しい変数を抽出することです。これにより、データセット内の隠れた関係が明らかになります。

1.1. 特徴の変換


1.1.1. 変換


変数の変換とは?

データモデリングにおける変換とは変数のその関数による置き換えです。例えば、変数xxの平方根、立方根または対数に変更できます。言い換えれば、変換とは変数の分布や変数間の関係を変える過程です。

変数の変換がいつ有用であるかを思い出してみましょう。

  • より良い理解のために、変数のスケールを変更したりその値を標準化したい場合 - この変換は、異なるデータのスケールが異なる場合に必要で、分布形状の変化をもたらしません。

  • 変数間の複雑な非直線関係や曲線関係を直線関係に変換する必要がある場合 - これはより鮮明で、より良い予測機能を提供し、そのような場合、散布図を使用して2つの連続変数間の関係を見つけることができます。このような状況では、通常、対数変換が使用されます。

  • より単純な解釈と分析のために、非対称分布を対称分布に変更する必要がある場合 - いくつかのモデル化手法では、変数の正規分布が必要なため、非一様分布に対処する場合には歪度を低減する変換を使用することができます。分布が右への歪みに対しては変数の平方根、立方根または対数をとりますが、左への歪みに対しては二乗/三乗または指数関数を使用して平滑化します。

  • 連続変数を離散変数に変換する必要がある場合 - このような変換の方法は、離散化です。

一般的な変換法は?


変数の変換にはさまざまな方法があります。そのうち平方根、立方根、対数、三角関数とセグメンテーションはすでに言及されました。方法のいくつかを詳しく見て、その利点と欠点を特定しましょう。

  1. 対数 - これは、変数の分布の形状を変更するために使用される一般的な変換法です。これは、通常、右への歪みを減らすために使用されます。この関数は、ゼロと負の値には適用されません。

  2. 平方根/立方根 - この関数は、対数をとるほど強力ではないものの、変数の分布に大きな影響を与えます。立方根の利点は、ゼロと負の値に使用できることです。平方根は正の値とゼロのみに使用できます。

  3. 離散化/ビニング - これは値の分類に使用されます。離散化は、元のデータ、百分位数及び頻度に適しています。分類方法の選択は、データの性質に基づいています。相互依存変数のジョイントセグメンテーションを実行することができます。

データのあらゆる変換は、分布の変化につながります。これを説明するために、2つの変換法の例を使用します。

初期データセットの2つの問題は、外れ値と右への歪みです。アウトライアを削除する方法は既に考察されました。さて、非対称性を最初に除去/減少させてから、外れ値の除去を試みましょう。

手法1

xデータセットの右への強い歪みを取り除くためには、底を2とする対数を取って外れ値を取り除きます。初期データセットの変数の値が1よりもずっと小さく、その中に負の値があるので、精度を高めるために、それぞれに1を加えた変数の対数を取ります。曲線に何が起こるかを見てみましょう。

evalq({x.ln <- apply(x, 2, function(x) log2(x + 1))
       sk.ln <- skewness(x.ln)},
      env)
 > env$sk.ln
               ftlm      stlm      rbci      pcci   v.fatl
Skewness -0.2715663 -2.660613 -4.484301 0.4267873 1.253008
          v.satl   v.rftl     v.rstl    v.ftlm     v.stlm
Skewness 1.83489 2.065224 -0.0343451 -15.62414 0.01529019
            v.pcci
Skewness 0.1811206


stlm、rbci及び v.ftlmの3つの変数では左への歪みが顕著です。v.fatl、v.satl及びv.rftl変数は、依然として右に歪んでいます。他の変数の歪度は均等になっています。このデータセットから外れ値を除去して代入し、変数の歪度と分布を見てみましょう。

evalq({
  foreach(i = 1:ncol(x.ln), .combine = "cbind") %do% {
    remove_outliers(x.ln[ ,i])
  } -> x.ln.out
  colnames(x.ln.out) <- colnames(x.ln)
  },  
env)
evalq({
  foreach(i = 1:ncol(x.ln), .combine = "cbind") %do% {
    capping_outliers(x.ln[ ,i])
  } -> x.ln.cap
  colnames(x.ln.cap) <- colnames(x.ln)
},  
env)
evalq({
  sk.ln.out <- skewness(x.ln.out) 
  sk.ln.cap <- skewness(x.ln.cap)
}, 
env)
> env$sk.ln.out
              ftlm       stlm       rbci        pcci
Skewness -0.119055 -0.3549119 -0.1099921 -0.01476384
              v.fatl      v.satl      v.rftl     v.rstl
Skewness -0.02896319 -0.03634833 -0.06259749 -0.2120127
              v.ftlm      v.stlm      v.pcci
Skewness -0.05819699 -0.01661317 -0.05420077
> env$sk.ln.cap
               ftlm       stlm       rbci        pcci
Skewness -0.1814781 -0.4582045 -0.1658855 -0.02849945
              v.fatl      v.satl     v.rftl     v.rstl
Skewness -0.04336238 -0.04400781 -0.0692754 -0.2269408
              v.ftlm      v.stlm      v.pcci
Skewness -0.06184128 -0.02856397 -0.06258243


両方のデータセット(x.out及びx.cap)のデータはほぼ対称的です。分布は下の図に示されています。

par(mfrow = c(2,2))
boxplot(env$x.ln, 
              main = "x.ln with outliers",
              xlab = "")
boxplot(env$x.ln.out, 
              main = "x.ln.out without outliers",
              xlab = "")
boxplot(env$x.ln.cap, 
              main = "x.ln.cap with imputed outliers",
              xlab = "")
par(mfrow = c(1,1))


x.lnx.ln.out

図1 外れ値を含む/含まないログ変換データ

x.ln.cap

図2 外れ値が補完されたログ変換データ

結果は、1つの例外を除いて、前の変換と非常に似ており、変数の変化の幅が広がっています。

x.ln.capデータフレームを変換してセットの変動と共分散を見てみましょう。

 evalq(x.ln.cap %>% tbl_df() %>% 
        cbind(Data = dataSetClean$Data, .,
              Class = dataSetClean$Class) -> 
        dataSetLnCap, 
      env) 

チャートをプロットします。

require(GGally)
evalq(ggpairs(dataSetLnCap, columns = 2:7, 
              mapping = aes(color = Class),
              title = "PredLnCap1"), 
      env)
evalq(ggpairs(dataSetLnCap, columns = 8:13, 
              mapping = aes(color = Class),
              title = "PredLnCap2"), 
      env)


LnCap1

図3 ログ変換されたデータのパラメータ(その1)

LnCap2

図4 ログ変換されたデータのパラメータ(その2)

手法2 

sin(2*pi*x)関数を使用してデータを変換し、外れ値を除去して補完し、歪度、外れ値の分布、及び変換された変数の共変をグラフで評価します。

evalq({x.sin <- apply(x, 2, function(x) sin(2*pi*x))
      sk.sin <- skewness(x.sin)
      },
env)
#----------
evalq({
  foreach(i = 1:ncol(x.sin), .combine = "cbind") %do% {
    remove_outliers(x.sin[ ,i])
  } -> x.sin.out
  colnames(x.sin.out) <- colnames(x.sin)
},  
env)
#-----------------
evalq({
  foreach(i = 1:ncol(x.sin), .combine = "cbind") %do% {
    capping_outliers(x.sin[ ,i])
  } -> x.sin.cap
  colnames(x.sin.cap) <- colnames(x.sin)
},  
env)
#-----------
evalq({
  sk.sin.out <- skewness(x.sin.out) 
  sk.sin.cap <- skewness(x.sin.cap)
}, 
env) 

これらの変換されたデータセットの歪度はどのくらいでしょうか?

env$sk.sin
                ftlm        stlm        rbci         pcci
Skewness -0.02536085 -0.04234074 -0.00587189 0.0009679463
             v.fatl    v.satl     v.rftl      v.rstl
Skewness 0.03280465 0.5217757 0.05611136 -0.02825112
             v.ftlm     v.stlm     v.pcci
Skewness 0.04923953 -0.2123434 0.01738377
> env$sk.sin.out
                ftlm        stlm        rbci       pcci
Skewness -0.02536085 -0.04234074 -0.00587189 0.03532892
             v.fatl      v.satl      v.rftl      v.rstl
Skewness 0.00360966 -0.02380975 -0.05336561 -0.02825112
               v.ftlm     v.stlm       v.pcci
Skewness 0.0009366441 0.01835948 0.0008843329
> env$sk.sin.cap
                ftlm        stlm        rbci       pcci
Skewness -0.02536085 -0.04234074 -0.00587189 0.03283132
              v.fatl      v.satl      v.rftl      v.rstl
Skewness 0.007588308 -0.02424707 -0.04106469 -0.02825112
              v.ftlm      v.stlm      v.pcci
Skewness 0.007003051 0.009237835 0.002101687

ご覧のように、この変換によってすべてのデータセットが対称になりました。これらのセットがどのように見えるかを見てみましょう。

par(mfrow = c(2, 2))
boxplot(env$x.sin, main = "x.sin with outlier")
abline(h = 0, col = 2)
boxplot(env$x.sin.out, main = "x.sin.out without outlier")
abline(h = 0, col = 2)
boxplot(env$x.sin.cap, main = "x.sin.cap with capping outlier")
abline(h = 0, col = 2)
par(mfrow = c(1, 1))

x.Sin

図5 sin()関数で変換されたデータセット

一見すると、これらのデータセットは以前のもの(初期のものと変換されたもの)よりも見栄えがよくなります。

さて、外れ値が取り除かれた後の変数におけるNAの分布を見たいと思います。

require(VIM)
evalq(a <- aggr(x.sin.out), env)

SinMissAggr

図6 データセットにおけるNAの分布

左のグラフは各変数の未定義データの相対的な数を示しています。右には異なる数のNA(下から上へ)の例の組み合わせが示されています。値は次のとおりです。

> print(env$a)

 Missings in variables:
 Variable Count
     pcci   256
   v.fatl   317
   v.satl   289
   v.rftl   406
   v.ftlm   215
   v.stlm   194
   v.pcci   201

変数でのNAの分布はどうでしょうか?

 par(mfrow = c(3, 4))
evalq(
  foreach(i = 1:ncol(x.sin.out)) %do% {
    barMiss(x.sin.out, pos = i, only.miss = TRUE, 
            main = "x.sin.out without outlier")
  }, env
)
par(mfrow = c(1, 1))

SinMissBar

図7 変数でのNAの分布

観測された変数の値は青、現在の変数の値の異なる範囲内の他の変数のNAの数は赤で示されます。右側のバーは、現在の変数がすべての変数のNAの総数に占める割合を表します。

最後に、補完された外れ値を持つ変換されたデータセットの変動と共分散を見てみましょう。

#---------------
evalq(x.sin.cap %>% tbl_df() %>% 
        cbind(Data = dataSetClean$Data, .,
              Class = dataSetClean$Class) -> 
        dataSetSinCap, 
      env) 
require(GGally)
evalq(ggpairs(dataSetSinCap, columns = 2:7, 
              mapping = aes(color = Class),
              title = "dataSetSinCap1 with capping outlier "), 
      env)
evalq(ggpairs(dataSetSinCap, columns = 8:13, 
              mapping = aes(color = Class),
              title = "dataSetSinCap2 with capping outlier"), 
      env)
#---------------------------


SinCap1

図8 sin()で変換されたデータのパラメータ(その1)

SinCap2

図9 sin()で変換されたデータのパラメータ(その2)


1.1.2. 正規化


ここではニューラルネットワークのためのデータを準備しているので、変数は{-1 .. + 1}の範囲内に収める必要があります。これにはpreProcess()::caret関数がmethod = “spatialSign”で使用されます。あるいは、データは正規化される前に中心に置くかスケーリングすることができます。この過程は非常に単純であり、本稿では考慮しません。

しかし、心に留めておくべきことが1つあります。訓練データセットから得られた正規化のパラメータは、テストセットと検証セットに使用されます。

前回の計算で得られたデータセット(相関性の高い値を削除しないdataSet)をさらに活用するために、 訓練/テスト/検証に分割して標準化せずに (-1,+1) の範囲に収めましょう。

標準化による正規化を実行する際には、正規化パラメータ(平均/中央値、sd/mad)が定義されている場合、補完された外れ値のパラメータも定義する必要があることに留意してください。それらは今後は訓練/テスト/検証に使用されます。本稿前半ではprep.outlier()treatOutlier()の2つの関数を記述しました。それらはこの目的のために設計されています。

操作の順序は下記の通りです。

  1. 訓練で外れ値パラメータを定義する
  2. 訓練で外れ値を除去する
  3. 訓練で標準化パラメータを定義する
  4. 訓練/テスト/検証で外れ値を補完する
  5. 訓練/テスト/検証を正規化する

ここではこのバリアントは考慮されないので、各自でお勉強ください。

データを訓練/テスト/検証に分けます。

 evalq(
{
  train = 1:2000
  val = 2001:3000
  test = 3001:4000
  DT <- list()
  list(clean = data.frame(dataSet) %>% na.omit(), 
       train = clean[train, ], 
       val = clean[val, ], 
       test = clean[test, ]) -> DT
}, env) 

訓練セットの正規化パラメータを定義して訓練/テスト/検証で外れ値を正規化します。

 require(foreach)
evalq(
{
 preProcess(DT$train, method = "spatialSign") -> preproc 
 list(train = predict(preproc, DT$train), 
        val = predict(preproc, DT$val),
        test = predict(preproc, DT$test)
       ) -> DTn
}, 
env) 

下記で訓練セットの総統計を見てみましょう。

 > table.Stats(env$DTn$train %>% tk_xts())
Using column `Data` for date_var.
                     ftlm      stlm      rbci      pcci
Observations    2000.0000 2000.0000 2000.0000 2000.0000
NAs                0.0000    0.0000    0.0000    0.0000
Minimum           -0.5909   -0.7624   -0.6114   -0.8086
Quartile 1        -0.2054   -0.2157   -0.2203   -0.2110
Median             0.0145    0.0246    0.0147    0.0068
Arithmetic Mean    0.0070    0.0190    0.0085    0.0028
Geometric Mean    -0.0316   -0.0396   -0.0332   -0.0438
Quartile 3         0.2139    0.2462    0.2341    0.2277
Maximum            0.6314    0.8047    0.7573    0.7539
SE Mean            0.0060    0.0073    0.0063    0.0065
LCL Mean (0.95)   -0.0047    0.0047   -0.0037   -0.0100
UCL Mean (0.95)    0.0188    0.0333    0.0208    0.0155
Variance           0.0719    0.1058    0.0784    0.0848
Stdev              0.2682    0.3252    0.2800    0.2912
Skewness          -0.0762   -0.0221   -0.0169   -0.0272
Kurtosis          -0.8759   -0.6688   -0.8782   -0.7090
                   v.fatl    v.satl    v.rftl    v.rstl
Observations    2000.0000 2000.0000 2000.0000 2000.0000
NAs                0.0000    0.0000    0.0000    0.0000
Minimum           -0.5160   -0.5943   -0.6037   -0.7591
Quartile 1        -0.2134   -0.2195   -0.1988   -0.2321
Median             0.0015    0.0301    0.0230    0.0277
Arithmetic Mean    0.0032    0.0151    0.0118    0.0177
Geometric Mean    -0.0323   -0.0267   -0.0289   -0.0429
Quartile 3         0.2210    0.2467    0.2233    0.2657
Maximum            0.5093    0.5893    0.6714    0.7346
SE Mean            0.0058    0.0063    0.0062    0.0074
LCL Mean (0.95)   -0.0082    0.0028   -0.0003    0.0033
UCL Mean (0.95)    0.0146    0.0274    0.0238    0.0321
Variance           0.0675    0.0783    0.0757    0.1083
Stdev              0.2599    0.2798    0.2751    0.3291
Skewness          -0.0119   -0.0956   -0.0648   -0.0562
Kurtosis          -1.0788   -1.0359   -0.7957   -0.7275
                   v.ftlm    v.stlm    v.rbci    v.pcci
Observations    2000.0000 2000.0000 2000.0000 2000.0000
NAs                0.0000    0.0000    0.0000    0.0000
Minimum           -0.5627   -0.6279   -0.5925   -0.7860
Quartile 1        -0.2215   -0.2363   -0.2245   -0.2256
Median            -0.0018    0.0092   -0.0015   -0.0054
Arithmetic Mean   -0.0037    0.0036   -0.0037    0.0013
Geometric Mean    -0.0426   -0.0411   -0.0433   -0.0537
Quartile 3         0.2165    0.2372    0.2180    0.2276
Maximum            0.5577    0.6322    0.5740    0.9051
SE Mean            0.0061    0.0065    0.0061    0.0070
LCL Mean (0.95)   -0.0155   -0.0091   -0.0157   -0.0124
UCL Mean (0.95)    0.0082    0.0163    0.0082    0.0150
Variance           0.0732    0.0836    0.0742    0.0975
Stdev              0.2706    0.2892    0.2724    0.3123
Skewness           0.0106   -0.0004   -0.0014    0.0232
Kurtosis          -1.0040   -1.0083   -1.0043   -0.4159

この表によって、変数が対称で、非常に近いパラメータを持っていることがわかります。

次に訓練/テスト/検証セットの変数の分布を見てみましょう。

 boxplot(env$DTn$train %>% 
          dplyr::select(-c(Data, Class)),
        horizontal = T, main = "Train")
abline(v = 0, col = 2)
boxplot(env$DTn$test %>% 
          dplyr::select(-c(Data, Class)),
        horizontal = T, main = "Test")
abline(v = 0, col = 2)
boxplot(env$DTn$val %>% 
          dplyr::select(-c(Data, Class)),
        horizontal = T, main = "Val")
abline(v = 0, col = 2) 

DTn

図10 正規化後の訓練/テスト/検証セットの変数の分布

分布はすべてのセットでほぼ同じです。ここでは訓練セットの変数の相関と共分散を考慮する必要もあります。

require(GGally)
evalq(ggpairs(DTn$train, columns = 2:7, 
              mapping = aes(color = Class),
              title = "DTn$train1 "), 
      env)
evalq(ggpairs(DTn$train, columns = 8:14, 
              mapping = aes(color = Class),
              title = "DTn$train2"), 
      env)

DTn$train1

図11 訓練セット1の変動と共分散

DTn$train2

図12 訓練セット2の変動と共分散

相関の高いデータはなく、分布は詰まっており、外れ値もありません。データはうまく分割できます。stlm変数とv.rstl変数には問題がありますがそれだけです。これについては、予測変数の関連性を評価する際に後で確認します。ここで、これらの予測変数と目標変数の相関関係を見ることができます。

> funModeling::correlation_table(env$DTn$train %>% tbl_df %>%
+                    select(-Data), str_target = 'Class')
   Variable Class
1     Class  1.00
2    v.fatl  0.38
3      ftlm  0.34
4      rbci  0.28
5    v.rbci  0.28
6    v.satl  0.27
7      pcci  0.24
8    v.ftlm  0.22
9    v.stlm  0.22
10   v.rftl  0.18
11   v.pcci  0.08
12     stlm  0.03
13   v.rstl -0.01

名前付き変数はテーブルの最下部にあり、相関係数は非常に小さいです。v.pcci.変数の関連性も検証する必要があります。訓練/テスト/検証セットのv.fatl変数を確認しましょう。
require(ggvis)
evalq(
  DTn$train %>% ggvis(~v.fatl, fill = ~Class) %>% 
  group_by(Class) %>%  layer_densities() %>% 
  add_legend("fill", title = "DTn$train$v.fatl"),
  env)
evalq(
  DTn$val %>% ggvis(~v.fatl, fill = ~Class) %>% 
    group_by(Class) %>%  layer_densities() %>% 
    add_legend("fill", title = "DTn$val$v.fatl"),
  env)
evalq(
  DTn$test %>% ggvis(~v.fatl, fill = ~Class) %>% 
    group_by(Class) %>%  layer_densities() %>% 
    add_legend("fill", title = "DTn$test$v.fatl"),
  env) 

Train$vfatl

図13 正規化後の訓練セットにおけるv.fatal変数の分布

Valid$vfatl

図14 正規化後の有効なセットにおけるv.fatal変数の分布

Test$vfatl

図15 正規化後のテストセットにおけるv.fatal変数の分布

正規化では外れ値や相関の高いデータがない予測変数がよく生成されることは分析によってわかります。大きくは、これは生データの特徴に依存します。


1.1.3. 離散化


離散化とは連続変数値を領域に分割して離散変数に変換する過程を指します。これらの領域の境界はさまざまな方法で設定できます。

分離方法は、目標変数との関係を含まない定量的方法と目標変数の範囲を考慮する方法の2つのグループに分けることができます。

最初のメソッド群はcut2()::Hmisc関数によってほぼ完全にカバーされています。サンプルは、指定された境界線を有する予め設定された数の領域、四分位数、それぞれについて最小限の数の例を有する領域、及び等価な領域に分割することができます。

第2の方法群は、変数を目標変数のレベルと関連する領域に分割するので、より興味深いものです。これらの方法を実現するいくつかのパッケージについて考察してみましょう。

離散化。このパッケージは、トレーナー付きの離散化アルゴリズムのセットです。これは「上から下へ」及び「下から上へ」でグループ化することも可能で離散化のアルゴリズムを実装します。dataSetの例についていくつか考えてみましょう。

最初は、(相関の高い変数を削除せずに)セットを消去し、それを2000/1000/1000の比率で訓練/テスト/検証セットに分割します。

require(discretization)
require(caret)
require(pipeR)
evalq(
  {
    dataSet %>%
    preProcess(.,
               method = c("zv", "nzv", "conditionalX")) %>%
    predict(., dataSet) %>%
    na.omit -> dataSetClean
    train = 1:2000
    val = 2001:3000
    test = 3001:4000
    DT <- list()
    list(train = dataSetClean[train, ], 
         val = dataSetClean[val, ], 
         test = dataSetClean[test, ]) -> DT
  }, 
  env)

最小記述長を使用して離散化を記述するmdlp()::discretization関数を使用します。この関数は、最小記述長を停止規則として、エントロピー基準によって行列の連続属性を離散化します。

evalq(
  pipeline({
    DT$train
    select(-Data)
    as.data.frame()
    mdlp()}) -> mdlp.train, envir = env)

この関数は、2つのスロットを持つリストを返します。それらはcutp(各変数の分割点を持つデータフレーム)とDisc.data(ラベル付き変数を持つデータフレーム)です。

> env$mdlp.train%>%str()
List of 2
 $ cutp     :List of 12
  ..$ : num [1:2] -0.0534 0.0278
  ..$ : chr "All"
  ..$ : num -0.0166
  ..$ : num [1:2] -0.0205 0.0493
  ..$ : num [1:3] -0.0519 -0.0055 0.019
  ..$ : num 0.000865
  ..$ : num -0.00909
  ..$ : chr "All"
  ..$ : num 0.0176
  ..$ : num [1:2] -0.011 0.0257
  ..$ : num [1:3] -0.03612 0.00385 0.03988
  ..$ : chr "All"
 $ Disc.data:'data.frame':      2000 obs. of  13 variables:
  ..$ ftlm  : int [1:2000] 3 3 3 3 3 2 1 1 1 1 ...
  ..$ stlm  : int [1:2000] 1 1 1 1 1 1 1 1 1 1 ...
  ..$ rbci  : int [1:2000] 2 2 2 2 2 2 1 1 1 1 ...
  ..$ pcci  : int [1:2000] 2 2 1 2 2 1 1 2 3 2 ...
  ..$ v.fatl: int [1:2000] 4 4 3 4 3 1 1 2 3 2 ...
  ..$ v.satl: int [1:2000] 1 1 1 2 2 1 1 1 1 1 ...
  ..$ v.rftl: int [1:2000] 1 2 2 2 2 2 2 2 1 1 ...
  ..$ v.rstl: int [1:2000] 1 1 1 1 1 1 1 1 1 1 ...
  ..$ v.ftlm: int [1:2000] 2 2 1 1 1 1 1 1 2 1 ...
  ..$ v.stlm: int [1:2000] 1 1 1 2 2 1 1 1 1 1 ...
  ..$ v.rbci: int [1:2000] 4 4 3 3 2 1 1 2 3 2 ...
  ..$ v.pcci: int [1:2000] 1 1 1 1 1 1 1 1 1 1 ...
  ..$ Class : Factor w/ 2 levels "-1","1": 2 2 2 2 2 1 1 1 1 1 ...

最初のスロットでは何がわかるでしょうか?

目標変数に接続されていない値を持つ3つのラベルなしの変数があります。これらは2、8及び12 (stlm、v.rstl、v.pcci)で、データセットの品質を損なうことなく削除できます。これらの変数は以前は不適切だと定義されていました。

4つの変数を2つのクラスに分け、3つの変数を3つのクラスに分け、2つの変数を4つのクラスに分けます。

訓練セットから得られた分割点を使用して検証/テストセットを分割します。そのためには、訓練セットからラベルのない変数を削除してtrain.dデータフレームに保存します。その後findInterval()関数を使って、以前に得られた分割点を使用して検証/テストセットにラベルをつけます。

evalq(
  {
    mdlp.train$cutp %>% 
    lapply(., function(x) is.numeric(x)) %>%
    unlist -> idx   # bool
    #----train-----------------
    mdlp.train$Disc.data[ ,idx] -> train.d
    #---test------------
    DT$test %>% 
      select(-c(Data, Class)) %>%
      as.data.frame() -> test.d
  
    foreach(i = 1:length(idx), .combine = 'cbind') %do% {
      if (idx[i]) {findInterval(test.d[ ,i], 
                   vec = mdlp.train$cutp[[i]],
                   rightmost.closed = FALSE, 
                   all.inside = F,
                   left.open = F)}
    } %>% as.data.frame() %>% add(1) %>%
      cbind(., DT$test$Class) -> test.d
    colnames(test.d) <- colnames(train.d)
    #-----val-----------------
    DT$val %>% 
      select(-c(Data, Class)) %>%
      as.data.frame() -> val.d
    foreach(i = 1:length(idx), .combine = 'cbind') %do% {
      if (idx[i]) {findInterval(val.d[ ,i], 
                                vec = mdlp.train$cutp[[i]],
                                rightmost.closed = FALSE, 
                                all.inside = F,
                                left.open = F)}
    } %>% as.data.frame() %>% add(1) %>%
      cbind(., DT$val$Class) -> val.d
    colnames(val.d) <- colnames(train.d)
  },env
)


これらのセットはどのように見えるでしょうか?

> env$train.d %>% head()
  ftlm rbci pcci v.fatl v.satl v.rftl v.ftlm v.stlm v.rbci Class
1    3    2    2      4      1      1      2      1      4     1
2    3    2    2      4      1      2      2      1      4     1
3    3    2    1      3      1      2      1      1      3     1
4    3    2    2      4      2      2      1      2      3     1
5    3    2    2      3      2      2      1      2      2     1
6    2    2    1      1      1      2      1      1      1    -1
> env$test.d %>% head()
  ftlm rbci pcci v.fatl v.satl v.rftl v.ftlm v.stlm v.rbci Class
1    1    1    1      2      1      1      1      1      2    -1
2    1    1    3      3      1      1      2      2      3    -1
3    1    1    2      2      1      1      1      2      2    -1
4    2    1    2      3      1      1      2      2      3     1
5    2    2    2      3      1      1      1      2      3     1
6    2    2    2      4      1      1      2      2      3     1
> env$val.d %>% head()
  ftlm rbci pcci v.fatl v.satl v.rftl v.ftlm v.stlm v.rbci Class
1    2    2    2      2      2      2      1      2      2     1
2    2    2    2      2      2      2      1      2      2     1
3    2    2    2      3      2      2      1      2      2     1
4    2    2    2      4      2      2      2      2      3     1
5    2    2    2      3      2      2      1      2      2     1
6    2    2    2      3      2      2      2      2      2     1

> env$train.d$v.fatl %>% table()
.
  1   2   3   4 
211 693 519 577 
> env$test.d$v.fatl %>% table()
.
  1   2   3   4 
 49 376 313 262 
> env$val.d$v.fatl %>% table()
.
  1   2   3   4 
 68 379 295 258 

離散データを含むセットの使用は、使用するモデルによって異なります。これがニューラルネットワークである場合、予測変数はダミー変数に変換する必要があります。これらのクラスはこれらの変数でどれだけうまく分けられるでしょうか?それらはどのように目標変数と相関してるのでしょうか?これらの関係を cross-plot()::funModelingで視覚化しましょう。Cross_plotは、入力変数が各入力の各範囲の尤度係数を受け取る目標変数とどのように相関するかを示します。

v.fatlftlm及びv.satlの3つの変数をそれぞれ4、3及び2の範囲に分けて考えてみましょう。チャートをプロットします。

evalq(
  cross_plot(data = train.d, 
             str_input = c("v.fatl", "ftlm", "v.satl"), 
             str_target = "Class", 
             auto_binning = F,
             plot_type = "both"), #'quantity' 'percentual'
  env
  )

Discret 1

図16 v.fatl/クラス変数のクロスプロット

Discret 2

図17 ftlm/クラス変数のクロスプロット

Discret 3

図18 v.satl/クラス変数のクロスプロット

予測変数は目標変数のレベルとよく相関しており、クラス変数のレベルを分ける明確な閾値があることがわかります。

予測変数がどのような場合に目標変数と相関するかを見るために、(最適ではない方法で)等しい領域に単純に分割することができます。前の3つの変数と2つの悪い変数(stlmv.rstl) を訓練セットから10の平等なエリアに分け、それらのクロスプロットを目標変数とで見てみましょう。

evalq(
  cross_plot(
      DT$train  %>% select(-Data) %>%
      select(c(v.satl, ftlm, v.fatl, stlm, v.rstl, Class)) %>%
      as.data.frame(), 
      str_input = Cs(v.satl, ftlm, v.fatl, stlm, v.rstl), 
      str_target = "Class", 
      auto_binning = T,
      plot_type = "both"), #'quantity' 'percentual'
  env
)

これらの変数の5つのグラフをプロットします。

Discret 4

図19 v.satl変数vsクラスのクロスプロット(10領域)

Discret 5

図20 ftlml変数vsクラスのクロスプロット(10領域)

Discret 6

図21 v.fatl変数vsクラスのクロスプロット(10領域)

discret 8

図22 stlm変数vsクラスのクロスプロット(10領域)

Discret 9

図23 v.rstl変数vsクラスのクロスプロット(10領域)

この図から明らかなように、変数を10個の別々の等価領域に分割しても、v.fatl、ftlm及びv.satl変数には、変数のレベルを分割する際の明確な閾値があります。他の2つの変数 (stlm、v.rstl) が何故不適切なのかは明らかです。これは、予測変数の重要性を識別する効率的な方法です。これには本稿の後半で戻ってきます。

さて、入力変数が目標変数とどのように相関するかをベイズ法事後変換率を使用して比較してみましょう。内部の順序を持たないカテゴリ値を比較すると便利です。このためにはbayes_plot::funModeling関数を使用します。v.fatl、ftlm及びv.satl変数をtrain.d、val.d、test.dの各セットから取得します。

#------BayesTrain-------------------
evalq(
  {
    bayesian_plot(train.d, input = "v.fatl", 
                  target = "Class", 
                  title = "Bayesian comparison train$v.fatl/Class",
                  plot_all = F, extra_above = 5, 
                  extra_under = 5)
  },env
)
evalq(
  {
    bayesian_plot(train.d, input = "ftlm", 
                  target = "Class", 
                  title = "Bayesian comparison train$ftlm/Class",
                  plot_all = F, extra_above = 5, 
                  extra_under = 5)
  },env
)
evalq(
  {
    bayesian_plot(train.d, input = "v.satl", 
                  target = "Class", 
                  title = "Bayesian comparison train$v.satl/Class",
                  plot_all = F, extra_above = 5, 
                  extra_under = 5)
  },env
)
#------------Bayesテスト------------------------
evalq(
  {
    bayesian_plot(test.d, input = "v.fatl", 
                  target = "Class", 
                  title = "Bayesian comparison test$v.fatl/Class",
                  plot_all = F, extra_above = 5, 
                  extra_under = 5)
  },env
)
evalq(
  {
    bayesian_plot(test.d, input = "ftlm", 
                  target = "Class", 
                  title = "Bayesian comparison test$ftlm/Class",
                  plot_all = F, extra_above = 5, 
                  extra_under = 5)
  },env
)
evalq(
  {
    bayesian_plot(test.d, input = "v.satl", 
                  target = "Class", 
                  title = "Bayesian comparison test$v.satl/Class",
                  plot_all = F, extra_above = 5, 
                  extra_under = 5)
  },env
)
#-------------BayesVal---------------------------------
evalq(
  {
    bayesian_plot(val.d, input = "v.fatl", 
                  target = "Class", 
                  title = "Bayesian comparison val$v.fatl/Class",
                  plot_all = F, extra_above = 5, 
                  extra_under = 5)
  },env
)
evalq(
  {
    bayesian_plot(val.d, input = "ftlm", 
                  target = "Class", 
                  title = "Bayesian comparison val$ftlm/Class",
                  plot_all = F, extra_above = 5, 
                  extra_under = 5)
  },env
)
evalq(
  {
    bayesian_plot(val.d, input = "v.satl", 
                  target = "Class", 
                  title = "Bayesian comparison val$v.satl/Class",
                  plot_all = F, extra_above = 5, 
                  extra_under = 5)
  },env
)
#------------------------------------------

BayesCorrTrain

図24 訓練セットにおける目標変数と変数のベイジアン比較

BayesCorrVal

図25 検証セットにおける目標変数と変数のベイジアン比較

BayesCorrTest

図26 テストセットにおける目標変数と変数のベイジアン比較

予測変数と目標変数との相関関係は、4つ以上のレベルの変数でより多く横滑りしていることがわかります。この動向は、2つのグループを持つ変数においてはより小さいものです。今後は、2範囲の予測変数のみを使用することによって、モデルの精度がどのように影響を受けるかを調べることが役立ちます。

変数を目標変数のレベルに近似した領域に分割するという同じタスクはsmbinningパッケージを使うことで別の方法で解決できます。ご自身でお確かめください。前稿は、別の興味深い離散化方法を検討しています。これは"RoughSets"パッケージを使って実装できます。

離散化は、予測変数を変換する効率的な方法です。残念ながら、すべてのモデルが因子予測変数で作業できるわけではありません。


1.2. 新しい特徴の生成

変数の作成とは、既存の変数に基づいて新しい変数を作成する過程です。日付(dd-mm-yy)が入力変数であるデータセットを見てみましょう。日、月、年、曜日のように、目標変数とよりよく関連する新しい変数の作成が可能です。このステップは、変数内の隠れた関係を明らかにするために使用されます。

派生変数の作成とは、関数のセットとさまざまな手法を使用して既存の変数から新しい変数を作成する過程を指します。どのような変数を作成するかは、ビジネスアナリストの好奇心、仮説セット、及び理論的知識に依存します。方法の選択は多数です。対数を取る、セグメンテーション、n乗するといったものは、変換方法のほんの一例に過ぎません。 

ダミー変数の作成は、変数を扱うもう一つの一般的な方法です。ダミー変数は通常、カテゴリ変数を数値変数に変換する際に使用されます。カテゴリ変数は0と1の値をとることができます。ダミー変数はNとN-1変数を持つカテゴリ変数の2つ以上のクラスに対して作成できます。

本稿では、アナリストとして毎日遭遇する状況について説明します。以下に、データセットから最大の情報を抽出するいくつかの方法を示します。

  1. 変数としてデータと時刻の値を使用します。新しい変数は、日付と時刻の違いを考慮して作成できます。
  2. 新しい比率と比率を作成します。過去の入力と出力をデータセットに格納するのではなく、その比率を含めることができます。これはより重要な意味を持つかもしれません。
  3. 標準的な変換を使用します。変数の変動と領域を出力とともに見ると、基本的な変換の後に相関が改善するかどうかを見ることができます。最も頻繁に使用される変換は、対数、指数、二乗、及び三角関数のバリエーションです。
  4. 季節性の変数をチェックし、必要な期間(週、月、セッションなど)のモデルを作成します。

月曜日の市場の動きが水曜日と木曜日の動きとは異なっていることは直感的です。つまり、曜日は重要な機能です。時間は、市場にとって同じくらい重要です。これは、このセッションがアジア、ヨーロッパ、アメリカのものであるかを定義します。これらの特徴はどのように定義できるでしょうか?

これにはtimekitパッケージを使います。tk_augment_timeseries_signature()はパッケージの中心的な関数です。これは、初期データセットprの時間ラベルに時間データの行全体を追加して、グループの追加機能とパラメータの両方として役立ちます。これらは何でしょうか?

Index 解決されたインデックスの値
Index.num インデックスの“1970-01-01 00:00:00”からの秒単位の数値
diff 指標の以前の数値との秒差
Year 年、インデックスコンポーネント
half 半分、インデックスコンポーネント
quarter 4分の1、インデックスコンポーネント<
month 月、インデックスコンポーネント(1ベース)
month.xts 月、xtsで実装されているのと同じ0ベースのインデックスコンポーネント
month.lbl 順序付けられた要素としての月ラベル(1月に始まり12月に終わる)
day 日、インデックスコンポーネント
hour 時間、インデックスコンポーネント
minute 分、インデックスコンポーネント
second 秒、インデックスコンポーネント
hour12 12時間スケールの1時間コンポーネント
am.pm 午前 (am) = 1、午後 (pm) = 2
wday 曜日(日曜 = 1、...土曜 = 7)
wday.xts 日、xtsで実装されているのと同じ(日曜 = 0, 土曜 = 6)
wday.lbl 順序付けられた因子としての曜日のラベル(日曜日で始まり土曜日に終わる)
mday 月の日
qday 四半期の日
yday 年の日
mweek 月の週
week 1年の週の番号(日曜日から始まる)
week.iso ISOによる1年間の週数(月曜日から始まる)
week2 2週間の頻度のモジュール
week3 3週間の頻度のモジュール
week4 4週間の頻度のモジュール

最初のデータセットprを取得してtk_augment_timeseries_signature()関数で強化し、mday、wday.lbl、hour変数を初期データセットに追加し、未定義変数(NA)を削除し、曜日別にグループ化します。

evalq(
  {
    tk_augment_timeseries_signature(pr) %>%
    select(c(mday, wday.lbl,  hour)) %>% 
    cbind(pr, .) -> pr.augm
    pr.compl <- pr.augm[complete.cases(pr.augm), ]
    pr.nest <- pr.compl %>% group_by(wday.lbl) %>% nest() 
  },
  env)
> str(env$pr.augm)
'data.frame':   8000 obs. of  33 variables:
 $ Data    : POSIXct, format: "2017-01-10 11:00:00" ...
 $ Open    : num  123 123 123 123 123 ...
 $ High    : num  123 123 123 123 123 ...
 $ Low     : num  123 123 123 123 123 ...
 $ Close   : num  123 123 123 123 123 ...
 $ Vol     : num  3830 3360 3220 3241 3071 ...
 ..................................................
 $ zigz    : num  123 123 123 123 123 ...
 $ dz      : num  NA -0.0162 -0.0162 -0.0162 -0.0162 ...
 $ sig     : num  NA -1 -1 -1 -1 -1 -1 -1 -1 -1 ...
 $ mday    : int  10 10 10 10 10 10 10 10 10 10 ...
 $ wday.lbl: Ord.factor w/ 7 levels "Sunday"<"Monday"<..: 3 3 3 3 3 3 3 3 3 3 ...
 $ hour    : int  11 11 11 11 12 12 12 12 13 13 ...

土曜日のデータを削除して lubridateライブラリを使用しても同じ結果が得られます。

require(lubridate)
evalq({pr %>% mutate(.,
                     wday = wday(Data), #label = TRUE, abbr = TRUE),
                     day = day(Data),
                     hour = hour(Data)) %>%
    filter(wday != "Sat") -> pr1
  pr1.nest <- pr1 %>% na.omit %>% 
    group_by(wday) %>% nest()}, 
  env
)
#-------
str(env$pr1)
'data.frame':   7924 obs. of  33 variables:
 $ Data  : POSIXct, format: "2017-01-10 11:00:00" ...
 $ Open  : num  123 123 123 123 123 ...
 $ High  : num  123 123 123 123 123 ...
 $ Low   : num  123 123 123 123 123 ...
 $ Close : num  123 123 123 123 123 ...
 $ Vol   : num  3830 3360 3220 3241 3071 ...
 ..........................................
 $ zigz  : num  123 123 123 123 123 ...
 $ dz    : num  NA -0.0162 -0.0162 -0.0162 -0.0162 ...
 $ sig   : num  NA -1 -1 -1 -1 -1 -1 -1 -1 -1 ...
 $ wday  : int  3 3 3 3 3 3 3 3 3 3 ...
 $ day   : int  10 10 10 10 10 10 10 10 10 10 ...
 $ hour  : int  11 11 11 11 12 12 12 12 13 13 ...

曜日別にグループ化されたデータは、次のようになります(日曜日= 1、月曜日= 2など)。

> env$pr1.nest
# A tibble: 5 × 2
   wday                  data
  <int>                <list>
1     4 <tibble [1,593 Ч 32]>
2     5 <tibble [1,632 Ч 32]>
3     6 <tibble [1,624 Ч 32]>
4     2 <tibble [1,448 Ч 32]>
5     3 <tibble [1,536 Ч 32]>

さらに、prデータセットからのdL変数とdH変数は、最後の3つのバーで使用することができます。

2. 予測変数の選択

予測変数の重要性を評価する方法と基準は数多くあり、それらのいくつかは前稿で考慮されました。本稿では視覚化に重点を置いているので、予測変数の重要性を高める視覚的方法と分析的方法を比較してみましょう。

2.1. 視覚評価

ここでは smbinningパッケージを使用します。.以前は、funModelingパッケージを使用して予測変数を評価し、関係の視覚が予測変数の関連性を識別する簡単で信頼性の高い方法であることを結論づけました。ここではsmbinning パッケージが正規化され変換されたデータをどのように処理するかをテストし、予測変数の変換がその重要性にどのように影響を与えるかについても説明します。

対数変換、sin変換、tanh変換、正規化されたデータのセットを収集し、これらのセットにおける目標変数と予測変数の依存性を評価します。

データセットの未加工データを(相関性の高いデータを削除することなく)消去してデータセットを訓練/検証/テストに分割してDTセットを設定、取得するというのがプライマリセットの処理のシーケンスです(以下の図を参照)。この後、以下のブロック図に従って、各種の変換のアクションを実行します。すべてを1つのスクリプトにまとめましょう。

前処理

図27 予備処理ブロック図

セットをクリーンして訓練/テスト/検証セットに分割し、不要なデータを削除します。

#----クリーン---------------------
require(caret)
require(pipeR)
evalq(
  {
    train = 1:2000
    val = 2001:3000
    test = 3001:4000
    DT <- list()
    dataSet %>%
      preProcess(., method = c("zv", "nzv", "conditionalX")) %>%
      predict(., dataSet) %>%
      na.omit -> dataSetClean
    list(train = dataSetClean[train, ], 
         val = dataSetClean[val, ], 
         test = dataSetClean[test, ]) -> DT
    rm(dataSetClean, train, val, test)
  }, 
  env)

外れ値を全て処理します。

#------外れ値-------------
evalq({
# 結果の新しいリストを定義する
  DTcap <- list()
# 3つのセットを処理する
  foreach(i = 1:3) %do% {
    DT[[i]] %>% 
# (データ、クラス)の列を削除する
      select(-c(Data, Class)) %>%
# data.frameに変換して一時変数xに格納する
      as.data.frame() -> x
    if (i == 1) {
# 最初の入力に外れ値のパラメータを定義する
      foreach(i = 1:ncol(x), .combine = "cbind") %do% {
        prep.outlier(x[ ,i]) %>% unlist()
      } -> pre.outl
      colnames(pre.outl) <- colnames(x)
    } 
# 5/95 % の代わりに外れ値を代入し、その結果をx.capに格納する
    foreach(i = 1:ncol(x), .combine = "cbind") %do% {
      stopifnot(exists("pre.outl", envir = env))
      lower = pre.outl['lower.25%', i] 
      upper = pre.outl['upper.75%', i]
      med = pre.outl['med', i]
      cap1 = pre.outl['cap1.5%', i] 
      cap2 = pre.outl['cap2.95%', i] 
      treatOutlier(x = x[ ,i], impute = T, fill = T, 
                   lower = lower, upper = upper, 
                   med = med, cap1 = cap1, cap2 = cap2) 
      } %>% as.data.frame() -> x.cap
    colnames(x.cap) <- colnames(x)
    return(x.cap)
  } -> Dtcap
# 不要な変数を削除する
  rm(lower, upper, med, cap1, cap2, x.cap, x)
}, env)

外れ値のないすべてのDtcapセットの変数をlog(x+1)関数で変換します。DTLnリストを、3セットの対数変換された変数で取得します。

#------logtrans------------
evalq({
  DTLn <- list()
  foreach(i = 1:3) %do% {
    DTcap[[i]] %>% 
      apply(., 2, function(x) log2(x + 1)) %>%
      as.data.frame() %>%
      cbind(., Class = DT[[i]]$Class)
  } -> DTLn
},
env)

外れ値のないすべてのDtcapセットの変数をsin(2*pi*x)関数で変換します。DTSinリストを、3セットのsin変換された変数で取得します。

#------sintrans--------------
evalq({
  DTSin <- list()
  foreach(i = 1:3) %do% {
    DTcap[[i]] %>% 
      apply(., 2, function(x) sin(2*pi*x)) %>%
      as.data.frame() %>%
      cbind(., Class = DT[[i]]$Class)
  } -> DTSin
},
env)

外れ値のないすべてのDtcapセットの変数をtanh(x)関数で変換します。DTTanhリストを、3セットのtanh変換された変数で取得します。

#------tanhTrans----------
evalq({
  DTTanh <- list()
  foreach(i = 1:3) %do% {
    DTcap[[i]] %>% 
      apply(., 2, function(x) tanh(x)) %>%
      as.data.frame() %>%
      cbind(., Class = DT[[i]]$Class)
  } -> DTTanh
},
env)

DT、DTLn、DTSin、DTTanhのセットを正規化します。

#------正規化-----------
evalq(
  {
# 正規化のパラメータを定義する
    preProcess(DT$train, method = "spatialSign") -> preproc 
    list(train = predict(preproc, DT$train), 
         val = predict(preproc, DT$val),
         test = predict(preproc, DT$test)
 ) -> DTn
  }, 
  env) 
#--ln---
evalq(
  {
    preProcess(DTLn[[1]], method = "spatialSign") -> preprocLn 
    list(train = predict(preprocLn, DTLn[[1]]), 
         val = predict(preprocLn, DTLn[[2]]),
         test = predict(preprocLn, DTLn[[3]])
    ) -> DTLn.n
  }, 
  env)
#---sin---
evalq(
  {
    preProcess(DTSin[[1]], method = "spatialSign") ->  preprocSin 
    list(train = predict(preprocSin, DTSin[[1]]), 
         val = predict(preprocSin, DTSin[[2]]),
         test = predict(preprocSin, DTSin[[3]])
    ) -> DTSin.n
  }, 
  env)
#-----tanh-----------------
evalq(
  {
    preProcess(DTTanh[[1]], method = "spatialSign") -> preprocTanh 
    list(train = predict(preprocTanh, DTTanh[[1]]), 
         val = predict(preprocTanh, DTTanh[[2]]),
         test = predict(preprocTanh, DTTanh[[3]])
    ) -> DTTanh.n
  }, 
  env)

mdlp::discretization関数を使用して、DTセットを離散化します。

##------離散化----------
#--------preCut---------------------
# カットポイントを定義する
require(pipeR)
require(discretization)
evalq(
  #require(pipeR) 
# 時間がかかる
  pipeline({
    DT$train
    select(-Data)
    as.data.frame()
    mdlp() 
  }) -> mdlp.train, 
  env)
#-------cut_opt----------
evalq(
  {
    DTd <- list()
    mdlp.train$cutp %>% 
# 離散化しなければならない列を定義する
      lapply(., function(x) is.numeric(x)) %>%
      unlist -> idx   # bool
    #----train-----------------
    mdlp.train$Disc.data[ ,idx] -> DTd$train 
    #---test------------
    DT$test %>% 
      select(-c(Data, Class)) %>%
      as.data.frame() -> test.d
# 計算された範囲に従ってデータを再配置する    
    foreach(i = 1:length(idx), .combine = 'cbind') %do% {
      if (idx[i]) {
        findInterval(test.d[ ,i], 
        vec = mdlp.train$cutp[[i]],
        rightmost.closed = FALSE, 
        all.inside = F,
        left.open = F)
        }
    } %>% as.data.frame() %>% add(1) %>%
      cbind(., DT$test$Class) -> DTd$test
    colnames(DTd$test) <- colnames(DTd$train)
    #-----val-----------------
    DT$val %>% 
      select(-c(Data, Class)) %>%
      as.data.frame() -> val.d
# 計算された範囲に従ってデータを再配置する  
    foreach(i = 1:length(idx), .combine = 'cbind') %do% {
      if (idx[i]) {
        findInterval(val.d[ ,i], 
        vec = mdlp.train$cutp[[i]],
        rightmost.closed = FALSE, 
        all.inside = F,
        left.open = F)
        }
    } %>% as.data.frame() %>% add(1) %>%
      cbind(., DT$val$Class) -> DTd$val 
    colnames(DTd$val) <- colnames(DTd$train)
# 片付ける
    rm(test.d, val.d)
  }, 
  env
)

元のDT$trainデータセットに含まれる変数を思い出してみましょう。

require(funModeling)
plot_num(env$DT$train %>% select(-Data), bins = 20)

FSelect 1

図28 DT$trainデータセットにおける変数の分布

前に取得したすべての正規化されたデータセット (Dtn、DTLn.n、DTSin.n、DTTanh.n)の訓練サブセットで関連する予測変数を識別するには、smbinningパッケージの機能を使用します。このパッケージの目標変数は数値でなければならず、値は (0, 1)でなければなりません。必要な変換のための関数を記述しましょう。

#--------------------------------
require(smbinning)
targ.int <- function(x){
  x %>% tbl_df() %>%
  mutate(Cl = (as.numeric(Class) - 1) %>%
           as.integer()) %>%
  select(-Class) %>% as.data.frame()
}

さらに、このパッケージでは、名前にドットを含む変数は受け入れられません。以下の関数は、すべての変数の名前のドットをアンダースコアに変更します。

renamepr <- function(X){
  X %<>% rename(v_fatl = v.fatl,
               v_satl = v.satl,
               v_rftl = v.rftl,
               v_rstl = v.rstl,
               v_ftlm = v.ftlm,
               v_stlm = v.stlm,
               v_rbci = v.rbci,
               v_pcci = v.pcci)
  return(X)
}

関連する予測変数を使ってチャートを計算し、プロットします。

par(mfrow = c(2,2))
#--Ln--------------
evalq({
  df <- renamepr(DTLn.n[[1]]) %>% targ.int
  sumivt.ln.n = smbinning.sumiv(df = df, y = 'Cl')
  smbinning.sumiv.plot(sumivt.ln.n, cex = 0.7)
  rm(df)
}, 
env)
#---Sin-----------------
evalq({
  df <- renamepr(DTSin.n[[1]]) %>% targ.int
  sumivt.sin.n = smbinning.sumiv(df = df, y = 'Cl')
  smbinning.sumiv.plot(sumivt.sin.n, cex = 0.7)
  rm(df)
  }, 
env)
#---norm-------------
evalq({
  df <- renamepr(DTn[[1]]) %>% targ.int
  sumivt.n = smbinning.sumiv(df = df, y = 'Cl')
  smbinning.sumiv.plot(sumivt.n, cex = 0.7)
  rm(df)
  }, 
env)
#-----Tanh----------------
evalq({
  df <- renamepr(DTTanh.n[[1]]) %>% targ.int
  sumivt.tanh.n = smbinning.sumiv(df = df, y = 'Cl')
  smbinning.sumiv.plot(sumivt.tanh.n, cex = 0.7)
  rm(df)
  }, 
env)
par(mfrow = c(1,1))

FSelect 2

図29 正規化されたセットの訓練サブセットの予測変数の重要性

v_fatl、ftlm、v_satl、rbci、v_rbciの5つの予測変数はすべてのセットで強いですが、その順序は異なります。pcci、v_ftlm、v_stlm、v_rftlの4つの予測変数の強さは平均的です。v_pcci及びstlm予測変数は弱いです。変数の分布は各セットで、重要度の順に見ることができます。

env$sumivt.ln.n
     Char     IV               Process
5  v_fatl 0.6823    Numeric binning OK
1    ftlm 0.4926    Numeric binning OK
6  v_satl 0.3737    Numeric binning OK
3    rbci 0.3551    Numeric binning OK
11 v_rbci 0.3424    Numeric binning OK
10 v_stlm 0.2591    Numeric binning OK
4    pcci 0.2440    Numeric binning OK
9  v_ftlm 0.2023    Numeric binning OK
7  v_rftl 0.1442    Numeric binning OK
12 v_pcci 0.0222    Numeric binning OK
2    stlm     NA No significant splits
8  v_rstl     NA No significant splits

最後の3つの変数は破棄できます。このようにして、5つの最も強いものと4つの平均的なものが残されます。最良の変数(IV> 0.1)の名前を定義しましょう。

evalq(sumivt.sin.n$Char[sumivt.sin.n$IV > 0.1] %>% 
        na.omit %>% as.character() -> best.sin.n, 
      env)
> env$best.sin.n
[1] "v_fatl" "ftlm"   "rbci"   "v_rbci" "v_satl" "pcci"  
[7] "v_ftlm" "v_stlm" "v_rftl"

v_fatl及びftlm変数をもっと詳しくみてみましょう。

evalq({
    df <- renamepr(DTTanh.n[[1]]) %>% targ.int
    x = 'v_fatl'
    y = 'Cl'
    res <- smbinning(df = df, 
                        y = y,
                        x = x) 
  #res$ivtable # 集計及び情報値
  #res$iv # 情報値
  #res$bands # ビンまたはバンド
  #res$ctree  # partykitの決定木
  par(mfrow = c(2,2))
  sub = paste0(x, "  vs  ", y) #rbci vs Cl"
  boxplot(df[[x]]~df[[y]],
          horizontal = TRUE, 
          frame = FALSE, col = "lightblue",
          main = "Distribution")
  mtext(sub,3) #ftlm
  smbinning.plot(res, option = "dist",
                 sub = sub) #"pcci vs Cl")
  smbinning.plot(res, option = "goodrate", #"badrate"
                 sub = sub) #"pcci vs Cl")
  smbinning.plot(res, option = "WoE",
                 sub = sub) #"pcci vs Cl")
  par(mfrow = c(1, 1))
}, env)

FSelect 3

図30 v_fatl変数の範囲とCl目標変数の関係

resオブジェクトには、有用な情報とともに、目標変数に最適に接続された範囲に変数を分割する点が含まれています。ここでの場合、4つの範囲があります。

> env$res$cuts
[1] -0.3722 -0.0433  0.1482

ftlm変数にも同じ計算をしてチャートをプロットします。

FSelect 4

図31 ftlm変数とCl目標変数の接続範囲

範囲の分割点は下記です。

> env$res$cuts
[1] -0.2084 -0.0150  0.2216

分割点によって、セット内の変数を離散化し、次の項目がどれだけ異なるかを見ることができます。

  • smbinning::smbinning f関数を使用して定義された変数とmdlp::discretization関数を使用して定義された重要な変数
  • 変数の範囲への分割

mdlp::discretization DTd関数で離散化されたデータセットはすでに1つあります。同じことを試みるのですが、今回はsmbinning::smbinning関数を訓練サブセットに対してのみ使用します。

分割点を定義します。

evalq({
  res <- list()
  DT$train %>% renamepr() %>% targ.int() -> df
  x <- colnames(df)
  y <- "Cl"
  foreach(i = 1:(ncol(df) - 1)) %do% {
    smbinning(df, y = y, x = x[i])
  } -> res
  res %>% lapply(., function(x) x[1] %>% is.list) %>%
    unlist -> idx
}, env) 

DT$train:サブセットを離散化します。

evalq({
  DT1.d <- list()
  DT$train %>% renamepr() %>% 
    targ.int() %>% select(-Cl) -> train
  foreach(i = 1:length(idx), .combine = 'cbind') %do% {
    if (idx[i]) {
      findInterval(train[ ,i], 
                   vec = res[[i]]$cuts,
                   rightmost.closed = FALSE, 
                   all.inside = F,
                   left.open = F)
    }
  } %>% as.data.frame() %>% add(1) %>%
    cbind(., DT$train$Class) -> DT1.d$train
  colnames(DT1.d$train) <- colnames(train)[idx] %>%
    c(., 'Class')
},
env)

重要度が0.1より大きい最良の変数を昇順に特定します。

evalq({
  DT$train %>% renamepr() %>% targ.int() -> df
  sumivt.dt1.d = smbinning.sumiv(df = df, y = 'Cl')
  sumivt.dt1.d$Char[sumivt.dt1.d$IV > 0.1] %>% 
    na.omit %>% as.character() -> best.dt1.d
  rm(df)
}, 
env)

DTd$train:セットの分割変数のチャートをプロットします。

require(funModeling)
plot_num(env$DTd$train)

FSelect 5

図32 mdlp関数で離散化されたDT$trainセットの変数

DT1.dのすべての変数のグラフとDT1.dの最良の変数のグラフを以下に示します。

plot_num(env$DT1.d$train)

FSelect 6

図33 smbinning関数で離散化されたDT1.d$trainセットの変数

plot_num(env$DT1.d$train[ ,env$best.dt1.d])

FSelect 7

図34 smbinning関数で離散化されたDT1.d$trainセットの変数

グラフから何がわかるでしょうか?重要と定義された変数はどちらの場合も同じですが、範囲に分割するのは異なります。どのモデルがモデルに対してより良い予測を与えるかをテストしなければなりません。

2.2. 分析的評価

さまざまな基準による予測変数の重要性を特定するための分析手法は数多くあります。そのうちのいくつかは以前に考えました。さて、ここで予測変数の選択に対する珍しいアプローチをテストしたいと思います。

使うのはvarbvsパッケージです。varbvs関数には、変数を選択するベイジアンモデルをインストールするための高速アルゴリズムと、結果(または目標変数)が線形回帰またはロジスティック回帰でモデル化されるベイジアン係数の計算の実装が含まれます。これらのアルゴリズムは、"Scalable variational inference for Bayesian variable selection in regression, and its accuracy in genetic association studies"(回帰におけるベイジアン変数選択のためのスケーラブルな変分推論と遺伝的関連研究におけるその精度)(P. CarbonettoとM. Stephens、Bayesian Analysis 7、2012、ページ73-108)で説明されている変分近似に基づいています。このソフトウェアは、100万を超える変数と数千のサンプルを持つ大規模なデータセットでの作業に使用されました。

varbvs()関数は行列を受け取り、目標変数は入力データとして数値ベクトル(0,1)を受け取ります。この方法を使用して、正規化されたデータ DTTanh.n$trainを使用して、どの予測変数が重要であると定義するかをテストしてみましょう。

require(varbvs)
evalq({
  train <- DTTanh.n$train %>% targ.int() %>%  as.matrix()
  fit <- varbvs(X = train[ ,-ncol(train)] , 
                Z = NULL,
                y = train[ ,ncol(train)] %>% as.vector(),
                "binomial", 
                logodds = seq(-2,-0.5,0.1),
                optimize.eta = T,
                initialize.params = T,
                verbose = T, nr = 100
                )
  print(summary(fit))
}, env)

Welcome to           --       *                              *               
VARBVS version 2.0.3 --       |              |               |               
large-scale Bayesian --       ||           | |    |          || |     |   |  
variable selection   -- |     || | |    |  | ||  ||        |||| ||    |   || 
****************************************************************************
Copyright (C) 2012-2017 Peter Carbonetto.
See http://www.gnu.org/licenses/gpl.html for the full license.
Fitting variational approximation for Bayesian variable selection model.
family:     binomial   num. hyperparameter settings: 16 
samples:    2000       convergence tolerance         1.0e-04
variables:  12         iid variable selection prior: yes 
covariates: 0          fit prior var. of coefs (sa): yes 
intercept:  yes        fit approx. factors (eta):    yes 
Finding best initialization for 16 combinations of hyperparameters.
-iteration-   variational    max.   incl variance params
outer inner   lower bound  change   vars   sigma      sa
 0016 00018 -1.204193e+03 6.1e-05 0003.3      NA 3.3e+00
Computing marginal likelihood for 16 combinations of hyperparameters.
-iteration-   variational    max.   incl variance params
outer inner   lower bound  change   vars   sigma      sa
 0016 00002 -1.204193e+03 3.2e-05 0003.3      NA 3.3e+00
Summary of fitted Bayesian variable selection model:
family:     binomial   num. hyperparameter settings: 16
samples:    2000       iid variable selection prior: yes
variables:  12         fit prior var. of coefs (sa): yes
covariates: 1          fit approx. factors (eta):    yes
maximum log-likelihood lower bound: -1204.1931
Hyperparameters: 
        estimate Pr>0.95             candidate values
sa          3.49 [3.25,3.6]          NA--NA
logodds    -0.75 [-1.30,-0.50]       (-2.00)--(-0.50)
Selected variables by probability cutoff:
>0.10 >0.25 >0.50 >0.75 >0.90 >0.95 
    3     3     3     3     3     3 
Top 5 variables by inclusion probability:
  index variable   prob PVE coef*  Pr(coef.>0.95)
1     1     ftlm 1.0000  NA 2.442 [+2.104,+2.900]
2     4     pcci 1.0000  NA 2.088 [+1.763,+2.391]
3     3     rbci 0.9558  NA 0.709 [+0.369,+1.051]
4    10   v.stlm 0.0356  NA 0.197 [-0.137,+0.529]
5     6   v.satl 0.0325  NA 0.185 [-0.136,+0.501]
*See help(varbvs) about interpreting coefficients in logistic regression.

ご覧のように、5つの最良の予測変数(ftlm、pcci、rbci、v.stlm、v.satl)が特定されています。これらは、異なる順序で他の重要度の重みを持つ、以前に識別されたトップ10に入っています。すでにモデルがあるので、検証セットとテストセットでどのような結果が得られるかを確認します。

下記は検証セットです。

#-----------------
evalq({
  val <- DTTanh.n$val %>% targ.int() %>%
    as.matrix()
  y = val[ ,ncol(val)] %>% as.vector()
  pr <- predict(fit, X = val[ ,-ncol(val)] , 
                Z = NULL)
  
}, env)
cm.val <- confusionMatrix(table(env$y, env$pr))
> cm.val
Confusion Matrix and Statistics

   
      0   1
  0 347 204
  1 137 312
                                          
               Accuracy : 0.659           
                 95% CI : (0.6287, 0.6884)
    No Information Rate : 0.516           
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.3202          
 Mcnemar's Test P-Value : 0.0003514       
                                          
            Sensitivity : 0.7169          
            Specificity : 0.6047          
         Pos Pred Value : 0.6298          
         Neg Pred Value : 0.6949          
             Prevalence : 0.4840          
         Detection Rate : 0.3470          
   Detection Prevalence : 0.5510          
      Balanced Accuracy : 0.6608          
                                          
       'Positive' Class : 0  

結果は全く印象的ではありません。下記はテストセットです。

evalq({
  test <- DTTanh.n$test %>% targ.int() %>%
    as.matrix()
  y = test[ ,ncol(test)] %>% as.vector()
  pr <- predict(fit, X = test[ ,-ncol(test)] , 
                Z = NULL)
  
}, env)
cm.test <- confusionMatrix(table(env$y, env$pr))
> cm.test
Confusion Matrix and Statistics

   
      0   1
  0 270 140
  1 186 404
                                        
               Accuracy : 0.674         
                 95% CI : (0.644, 0.703)
    No Information Rate : 0.544         
    P-Value [Acc > NIR] : < 2e-16       
                                        
                  Kappa : 0.3375        
 Mcnemar's Test P-Value : 0.01269       
                                        
            Sensitivity : 0.5921        
            Specificity : 0.7426        
         Pos Pred Value : 0.6585        
         Neg Pred Value : 0.6847        
             Prevalence : 0.4560        
         Detection Rate : 0.2700        
   Detection Prevalence : 0.4100        
      Balanced Accuracy : 0.6674        
                                        
       'Positive' Class : 0  

結果はほぼ同じです。これは、モデルが再訓練されておらず、データが一般化していることを意味します。

よって、varbvsによるとftlm、pcci、rbci、v.stlm、v.satlが最高となります。 


2.3. ニューラルネットワーク

ここではニューラルネットワークを研究しているので、ニューラルネットワークがどの予測変数を最も重要なものとして選択するかをテストしましょう。

これにはFCNN C ++ライブラリのコアプログラムのインターフェイスを提供するFCNN4Rパッケージを使用します。FCNNはニューラルネットワークの全く新しい表現に基づいており、効率、モジュール性、拡張性を意味します。FCNN4Rは、標準学習(誤差逆伝播法、Rprop、シミュレーテッドアニーリング、確率的勾配)と刈り込みアルゴリズム(最小マグニチュード、Optimal Brain Surgeon)を可能にしますが、私はこのパッケージがとりわけ効率的な計算エンジンであると考えています。

ユーザは、ネットワークを復元する機能(重みや過剰なニューロンの削除、入力データの並べ替え、ネットワークの統合)と共に、クイックグラジエントメソッドを使用してアルゴリズムを簡単に実装できます。

ネットワークは、どのようなプログラムソリューションにも統合できるように、C関数にエクスポートできます。

2つの隠れ層を持つ完全接続ネットワークを作成します。各層のニューロンの数は入力= 12(予測変数の数)、出力= 1です。+/- 0.17の範囲のランダムな重みでニューロンを動かします。ニューラルネットワークの各層(入力層を除く)の活性化関数を c( "tanh"、 "tanh"、 "sigmoid")に設定します。訓練/テスト/検証セットを準備します。

以下のスクリプトは、この一連の操作を実行します。

require(FCNN4R)
evalq({
mlp_net(layers = c(12, 8, 5, 1), name = "n.tanh") %>%
  mlp_rnd_weights(a = 0.17) %>% 
  mlp_set_activation(layer = c(2, 3, 4), 
  activation = c("tanh", "tanh", "sigmoid"), #"threshold", "sym_threshold",
                                            #"linear", "sigmoid", "sym_sigmoid",
                                            #"tanh", "sigmoid_approx",
                                            #"sym_sigmoid_approx"), 
                 slope = 0) -> Ntanh #show() 
#-------
train <- DTTanh.n$train %>% targ.int() %>% as.matrix()
test <- DTTanh.n$test %>% targ.int() %>%  as.matrix()
val <- DTTanh.n$val %>% targ.int() %>% as.matrix()
}, env)

rprop訓練メソッドを使用します。tol - 訓練の最高レベル(達した場合訓練を停止してエラーとする)、max_ep - 訓練の最大エポック数(越された場合訓練を停止する)、l2reg - 正則化係数の定数を設定します。これらのパラメータを使用してネットワークを訓練し、どのネットワーク及び訓練エラーが発生しているかを視覚的に評価します。

evalq({
  tol <- 1e-1
  max_ep = 1000
  l2reg = 0.0001
net_rp <- mlp_teach_rprop(Ntanh, 
                          input = train[ ,-ncol(train)], 
                          output = train[ ,ncol(train)] %>% as.matrix(),
                          tol_level = tol, 
                          max_epochs = max_ep, 
                          l2reg = l2reg,
                          u = 1.2, d = 0.5, 
                          gmax = 50, gmin = 1e-06, 
                          report_freq = 100)
}, env)
plot(env$net_rp$mse, t = "l", 
     main = paste0("max_epochs =", env$max_ep, " l2reg = ", env$l2reg))

NN1

図35 ニューラルネットワーク訓練におけるエラー

evalq(mlp_plot(net_rp$net, FALSE), envir = env)

NN2

図36 ニューラルネットワークの構造

刈り込み

最小値の刈り込みは、使いやすいアルゴリズムです。ここでは、絶対値が最小である重みが各ステップでオフになります。このアルゴリズムは、各ステップでネットワークリレーをほぼ必要とし、最適ではない結果をもたらします。

evalq({
  tol <- 1e-1
  max_ep = 1000
  l2reg = 0.0001
  mlp_prune_mag(net_rp$net, 
                input = train[ ,-ncol(train)], 
                output = train[ ,ncol(train)] %>% as.matrix(),
                tol_level = tol,  
                max_reteach_epochs = max_ep, 
                report = FALSE,
                plots = TRUE) -> net_rp_prune
  
}, env)

NN3

図37 刈り込みされたニューラルネットワーク

ニューラルネットワークの特定の構造、初期設定、活性化関数、学習誤差では、構造(12,2,1,1 )で十分なことがわかります。ニューラルネットワークはどの予測変数を選択したのでしょうか?

evalq(
  best <- train %>% tbl_df %>%  select(c(1,5,7,8,10,12)) %>% colnames(),
           env)
env$best
[1] "ftlm"   "v.fatl" "v.rftl" "v.rstl" "v.stlm"
[6] "v.pcci"

v.rstl及びv.pcci変数は前に定義した最良の9変数中には存在しません。

ここでは、ニューラルネットワークが独立して自動的に重要な予測変数を選択できることを示したということを強調したいと思います。この選択は、予測変数だけでなく、ネットワークの構造及びパラメータによっても左右されます。

頑張って実験してください。

終わりに

次の部分では、セットからノイズの例を削除する方法、入力のサイズを小さくする方法、及び元のデータを訓練/検証/テスト
に分割する方法について説明します。

適用

1. FeatureTransformation.R、FeatureSelect.R、FeatureSelect_analitic.R、FeatureSelect_NN.Rスクリプトと本稿Part_1のRDataスクリプトの作業を示す図はGit /Part_IIからダウンロードできます。


MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/3507

グラフィカルインターフェイスXI:テーブルセル内のテキストエディットボックスとコンボボックス(ビルド15) グラフィカルインターフェイスXI:テーブルセル内のテキストエディットボックスとコンボボックス(ビルド15)
このライブラリアップデートでは、テーブルコントロール(CTableクラス)に新しいオプションが追加されます。テーブルセル内のコントロールのラインアップが拡張され、今回はテキストエディットボックスとコンボボックスが追加されます。また、このアップデートでは、実行中にMQLアプリケーションのウィンドウのイズを変更する機能も導入されています。
ターミナル間のデータ交換にクラウドストレージサービスを使用 ターミナル間のデータ交換にクラウドストレージサービスを使用
クラウド技術の普及が進んでいます。 今日では、有料と無料のストレージサービスから選択することができます。 トレードで使用することは可能でしょうか? 本稿では, クラウドストレージサービスを利用してターミナル間でのデータ交換を行う技術を提案します。
ディープニューラルネットワーク(その3)サンプル選択と次元削減 ディープニューラルネットワーク(その3)サンプル選択と次元削減
本稿は、ディープニューラルネットワークに関する一連の記事の続きです。ここでは、ニューラルネットワークの訓練データの準備に当たってのサンプルの選択(ノイズ除去)、入力データの次元数の削減、及びデータセットの訓練/検証/テストセットへの分割を検討します。
MetaTrader5の任意のシンボルでトレーディングアイデアをテスト! MetaTrader5の任意のシンボルでトレーディングアイデアをテスト!
カスタムシンボルを作成すると、トレーディングシステムと金融相場分析に役立ちます。 今日ではトレーダーは、無数のチャートやテストトレード戦略をプロットすることができます。