rTensorパッケージでCP分解します

はじめに

rTensorパッケージでテンソルが扱えます. テンソル分解がカジュアルにできるみたいなので, やってみます.

library(rTensor)
library(dplyr)
library(data.table)

テンソルを作ります. ただの$10 \times 10 \times 10$の行列です.

## 10*10*10のランダム行列. 
tnsr = rand_tensor(c(10,10,10))
dim(tnsr)
## [1] 10 10 10
tnsr[1,,] %>% head
##             [,1]       [,2]       [,3]        [,4]       [,5]       [,6]
## [1,] -0.10989573 -0.6265818 -2.0916756  0.31137992 -0.3134700 -0.3940752
## [2,]  0.05192558 -0.7936545 -1.5180346 -0.09190168 -1.1009563 -0.5621084
## [3,] -0.80533618  0.4233875 -0.1505925  1.42189925 -0.2229974 -0.4453390
## [4,]  1.48624101  1.0886220 -0.8166705  1.02952628 -0.4824636  1.5717277
## [5,] -0.38562592 -0.2718807 -0.8710523  0.42915660 -0.4413121  0.9732894
## [6,]  1.40585687 -1.6608281  0.1760089  1.20052504 -0.7318101  0.2225002
##             [,7]       [,8]        [,9]      [,10]
## [1,]  0.28949019 -0.2976226  0.42875127  0.8912003
## [2,] -0.04568931  0.3761937 -0.69481918 -0.4356397
## [3,] -2.55287894 -0.3943557  0.83498729 -1.9720734
## [4,] -1.61535856 -0.8417616 -0.84380331 -0.5470340
## [5,] -0.88587324  0.8281418 -0.96395344 -0.8099592
## [6,]  1.09711133  0.9889217  0.05147289  0.8568847
## cp分解する. 
## (長さ10のベクトル3つの外積的なもの) * 個数で分解 . 
cp_d = tnsr %>% cp(num_components = 20, max_iter = 50)
## 最適化の様子. イテレーションを増やすと, 誤差が減少してサチったら終了. 
plot(cp_d$all_resids)

plot of chunk unnamed-chunk-4

## 近似されたテンソル
ested_tnsr = cp_d$est

## 誤差(フロベニウスノルムの意味で)
fnorm(tnsr - ested_tnsr) 
## [1] 13.35722
## 相対誤差
fnorm(tnsr-ested_tnsr) / fnorm(tnsr) 
## [1] 0.4242693
## 要素ごとの差の平方. つまりこういうこと. 
(vec(tnsr) - vec(ested_tnsr)) %>% norm(type="2")
## [1] 13.35722

要素数1000のテンソルを, 長さ10のベクトル3つ のセットを20個, 要素数600個で表現しています.

フロベニウスノルムの意味で, 相対誤差は0.4242693 ですから, 60\%くらい表現できています.

...ランダム行列だから, あまり精度が良くないです.

どこかの方向に系列相関みたいなものがあると, 分解の性能が上がるんでしょうね?

それなりに規則性のあるテンソルをCP分解

規則性+乱数でそれっぽい規則性のあるテンソルを作ってみました.

これを分解するとどうなるか.

tnsr = (rep(c(1,2,3,4,5), 200) + runif(1000)) %>% matrix(nrow = 10, ncol=100) %>% k_fold(m=1, modes=c(10,10,10))
tnsr[,,1]
## Numeric Tensor of 2 Modes
## Modes:  10 10 
## Data: 
##          [,1]     [,2]     [,3]     [,4]     [,5]     [,6]     [,7]
## [1,] 1.977912 1.592352 1.751577 1.919782 1.128350 1.095718 1.923054
## [2,] 2.832013 2.201249 2.021149 2.394813 2.889224 2.363003 2.199207
## [3,] 3.569429 3.347121 3.434156 3.039923 3.434483 3.825411 3.990404
## [4,] 4.014528 4.353050 4.989336 4.819384 4.226619 4.354252 4.224828
## [5,] 5.973573 5.857869 5.596037 5.135240 5.072900 5.915412 5.817669
## [6,] 1.994246 1.852058 1.895662 1.984037 1.119141 1.172718 1.662140
##          [,8]     [,9]    [,10]
## [1,] 1.250355 1.541253 1.640730
## [2,] 2.784215 2.645533 2.974132
## [3,] 3.045487 3.673341 3.607136
## [4,] 4.241428 4.318183 4.013139
## [5,] 5.960047 5.593795 5.031396
## [6,] 1.823369 1.043004 1.786552
tnsr[,1,]
## Numeric Tensor of 2 Modes
## Modes:  10 10 
## Data: 
##          [,1]     [,2]     [,3]     [,4]     [,5]     [,6]     [,7]
## [1,] 1.977912 1.183941 1.323842 1.159533 1.376909 1.969593 1.298717
## [2,] 2.832013 2.153190 2.719060 2.944885 2.891101 2.830155 2.145121
## [3,] 3.569429 3.175928 3.747487 3.712435 3.734527 3.554649 3.533597
## [4,] 4.014528 4.989490 4.828018 4.425896 4.316834 4.259725 4.528159
## [5,] 5.973573 5.488283 5.738033 5.570344 5.335877 5.856295 5.503114
## [6,] 1.994246 1.773513 1.870900 1.187811 1.799442 1.085443 1.896670
##          [,8]     [,9]    [,10]
## [1,] 1.982583 1.308314 1.432265
## [2,] 2.055190 2.013339 2.867969
## [3,] 3.256531 3.836053 3.062788
## [4,] 4.834423 4.590931 4.094526
## [5,] 5.459149 5.038334 5.689247
## [6,] 1.489703 1.664815 1.265861
tnsr[1,,]
## Numeric Tensor of 2 Modes
## Modes:  10 10 
## Data: 
##          [,1]     [,2]     [,3]     [,4]     [,5]     [,6]     [,7]
## [1,] 1.977912 1.183941 1.323842 1.159533 1.376909 1.969593 1.298717
## [2,] 1.592352 1.308714 1.990185 1.575203 1.271251 1.316647 1.103767
## [3,] 1.751577 1.778080 1.591474 1.653344 1.866224 1.149900 1.015758
## [4,] 1.919782 1.264667 1.741575 1.087628 1.582124 1.652955 1.893580
## [5,] 1.128350 1.108795 1.850966 1.173437 1.080490 1.184825 1.232319
## [6,] 1.095718 1.949160 1.402980 1.195867 1.336911 1.363439 1.328639
##          [,8]     [,9]    [,10]
## [1,] 1.982583 1.308314 1.432265
## [2,] 1.292177 1.760049 1.554163
## [3,] 1.285426 1.210320 1.466397
## [4,] 1.211951 1.852784 1.644511
## [5,] 1.774093 1.254849 1.845321
## [6,] 1.843852 1.268672 1.619393

これならどうでしょうか.

## cp分解する. 
## (長さ10のベクトル3つの外積的なもの) * 個数で分解 . 
cp_d = tnsr %>% cp(num_components = 5, max_iter = 50)
## 最適化の様子. イテレーションを増やすと, 誤差が減少してサチったら終了. 
plot(cp_d$all_resids)

plot of chunk unnamed-chunk-7

## 近似されたテンソル
ested_tnsr = cp_d$est

## 誤差(フロベニウスノルムの意味で)
fnorm(tnsr - ested_tnsr)
## [1] 7.802834
## 相対誤差
fnorm(tnsr-ested_tnsr) / fnorm(tnsr)
## [1] 0.06511341

もともとのテンソルには1000個の要素がありました.

CP分解によって, 10,10,10のベクトル5セット, つまり合計150個の要素で表されたことになります.

このとき, フロベニウスノルムの意味で精度が99%.

損失7%で, データが15%に圧縮できた...と解釈できるんですかね.

分解の個数を変えてみる

何個に分解するか...というのは, パラメータとして与えないといけません.

ここを変えると, 相対誤差はどうなるか.

cp_decomp_check = function(num_components){
  ## cp分解する. 
  ## (長さ10のベクトル3つの外積的なもの) * 個数で分解 . 
  cp_d = tnsr %>% cp(num_components = num_components, max_iter = 50)
  ## 最適化の様子. イテレーションを増やすと, 誤差が減少してサチったら終了. 
  ested_tnsr = cp_d$est
  ## 相対誤差
  fnorm(tnsr-ested_tnsr) / fnorm(tnsr) %>% return
}

res = 1:10%>% lapply(FUN = cp_decomp_check) 
## 分解数と相対誤差の様子. 
res %>% unlist %>% plot

plot of chunk unnamed-chunk-8

いくつにすればいいんでしょうね.

次は実データでやってみます.