View on GitHub

第8章 評価値行列の次元削減 | recsys-python

Home

第8章 評価値行列の次元削減

準備

次のコードを書きなさい。

import numpy as np
import numpy.linalg as LA
np.set_printoptions(precision=3)

# 縮約後の次元数
DIM = 2

R = np.array([
              [np.nan, 4,      3,      1,      2,      np.nan],
              [5,      5,      4,      np.nan, 3,      3     ],
              [4,      np.nan, 5,      3,      2,      np.nan],
              [np.nan, 3,      np.nan, 2,      1,      1     ],
              [2,      1,      2,      4,      np.nan, 3     ],
])
U = np.arange(R.shape[0])
I = np.arange(R.shape[1])
Ui = [U[~np.isnan(R)[:,i]] for i in I]
Iu = [I[~np.isnan(R)[u,:]] for u in U]
ru_mean = np.nanmean(R, axis=1)
R2 = R - ru_mean.reshape((ru_mean.size, 1))

分散共分散行列

アイテム\(i\)の分散\(s_{i}^{2}\)は次式で求められる。

\[s_{i}^{2} = \frac{1}{\mid U_{i} \mid} \sum_{u \in U_{i}} (r_{u,i}^{'} - \overline{r}_{i}^{'})^{2}\]

ここで、\(\overline{r}_{i}\)はアイテム\(i\)に対して与えられた平均中心化評価値の平均値であり、次式で求められる。

\[\overline{r}_{i}^{'} = \frac{1}{\mid U_{i} \mid} \sum_{u \in U_{i}} r_{u,i}^{'}\]

アイテム\(i\)とアイテム\(j\)の共分散\(s_{i,k}\)は次式で求められる。

\[s_{i,j} = \begin{cases} \frac{1}{\mid U_{i,j} \mid} \sum_{u \in U_{i,j}} (r_{u,i}^{'} - \overline{r}_{i}^{'}) (r_{u,j}^{'} - \overline{r}_{j}^{'}) & (U_{i,j} \neq \emptyset) \\ 0 & (U_{i,j} = \emptyset) \end{cases}\]

ここで、\(U_{i,j}\)はアイテム\(i\)とアイテム\(j\)の両方のアイテムに評価値を与えているユーザ集合である。

各アイテムについて求めた分散、共分散をまとめると、次式のように分散共分散行列\(\boldsymbol{S}\)が得られる。

\[\boldsymbol{S} = \left[ \begin{array}{rrrrrr} 0.336 & 0.893 & 0.169 & -0.659 & -0.057 & -0.572 \\ 0.893 & 1.348 & 0.505 & -1.466 & 0.166 & -0.817 \\ 0.169 & 0.505 & 0.505 & -0.655 & -0.183 & -0.270 \\ -0.659 & -1.466 & -0.655 & 1.279 & -0.109 & 0.752 \\ -0.057 & 0.166 & -0.183 & -0.109 & 0.137 & -0.015 \\ -0.572 & -0.817 & -0.270 & 0.752 & -0.015 & 0.494 \end{array} \right]\]

このとき、次の問いに答えなさい。

01 各アイテムに対して与えられた平均中心化評価値の平均値

R2において各アイテムに対して与えられた平均中心化評価値の平均値\(\overline{r}_{i}\)をndarrayとしてまとめて求めるコードを書きなさい。得られたndarrayri2_meanとすること。

コード
    問01    
print('ri2_mean = {}'.format(ri2_mean))
結果
ri2_mean = [ 0.367  0.588  0.4   -0.037 -0.938 -0.383]

  1. numpy.nanmean()を使う。

★★★

  1. 二重のリスト内包表記を使う。
  2. numpy.sum()を使う。
  3. numpy.array()を使う。
  4. numpy.nanmean()を使わない。

02 各アイテムの平均中心化評価値の分散

R2において各特徴量の平均中心化評価値の分散\(s_{i}^{2}\)をndarrayとしてまとめて求めるコードを書きなさい。得られたndarrays2とすること。

コード
    問02    
print('s^2 = {}'.format(s2))
結果
s2 = [0.336 1.348 0.505 1.279 0.137 0.494]

  1. numpy.nanvar()を使う。

★★★

  1. 二重のリスト内包表記を使う。
  2. numpy.sum()を使う。
  3. numpy.array()を使う。

★★★

  1. リスト内包表記を使う。
  2. numpy.nansum()を使う。
  3. numpy.array()を使う。
  4. 二重のリスト内包表記を使わない。

03 アイテムiとアイテムjの平均中心化評価値の共分散

アイテム\(i\)とアイテム\(j\)の平均中心化評価値の共分散\(s_{i,j}\)を求めるコードを書きなさい。ただし、\(U_{i,j} = \emptyset\)のとき、\(s_{i,j} = 0\)とする。得られた値をsijとすること。

コード
i = 0
j = 1
Uij = np.intersect1d(Ui[i], Ui[j])
    問03    
print('s{}{} = {:.3f}'.format(i, j, sij))
結果
s01 = 0.892

★★

  1. リスト内包表記を使う。
  2. numpy.sum()を使う。
  3. 条件式を使う。

04 分散共分散行列

分散共分散行列\(\boldsymbol{S}\)をndarrayとして求めるコードを書きなさい。得られたndarraySとすること。

コード
    問04    
print('S = \n{}'.format(S))
結果
S = 
[[ 0.336  0.892  0.169 -0.659 -0.057 -0.572]
 [ 0.892  1.348  0.505 -1.466  0.166 -0.817]
 [ 0.169  0.505  0.505 -0.655 -0.183 -0.27 ]
 [-0.659 -1.466 -0.655  1.279 -0.109  0.752]
 [-0.057  0.166 -0.183 -0.109  0.137 -0.015]
 [-0.572 -0.817 -0.27   0.752 -0.015  0.494]]

★★★

  1. numpy.zeros()を使う。
  2. 二重のforループを使う。
  3. numpy.intersect1d()を使う。
  4. リスト内包表記を使う。
  5. numpy.sum()を使う。

固有値・固有ベクトル

分散共分散行列\(\boldsymbol{S}\)に対して、

\[\boldsymbol{S} \boldsymbol{v} = \lambda \boldsymbol{v} \;\;\;\; (\boldsymbol{x} \neq \boldsymbol{0})\]

を満たす$d$次元ベクトル\(\boldsymbol{v}\)と実数\(\lambda\)が存在するとき、\(\lambda\)を行列$\boldsymbol{S}$の固有値,\(\boldsymbol{v}\)を\(\lambda\)に関する行列\(\boldsymbol{S}\)の固有ベクトルという。このとき、次の問いに答えなさい。

05 固有値・固有ベクトル

分散共分散行列\(\boldsymbol{S}\)の固有値\(\lambda\)、固有ベクトル\(\boldsymbol{v}\)を求めるコードを書きなさい。ndarrayとして得られた固有値、固有ベクトルを、それぞれlmdvとすること。

コード
    問05    
print('λ = {}'.format(lmd))
print('v = \n{}'.format(v))
結果
λ = [ 3.909  0.48   0.233 -0.315 -0.049 -0.16 ]
v = 
[[ 0.327  0.228  0.484 -0.685  0.279 -0.245]
 [ 0.609  0.211 -0.099  0.565  0.371 -0.344]
 [ 0.245 -0.806 -0.097 -0.134 -0.202 -0.472]
 [-0.583  0.126  0.374  0.258 -0.019 -0.661]
 [ 0.028  0.462 -0.624 -0.294 -0.394 -0.393]
 [-0.348 -0.157 -0.465 -0.204  0.767 -0.087]]

  1. numpy.linalg.eig()を使う。

06 第d主成分までの固有ベクトル

DIM主成分までの対応する固有ベクトルを列ベクトルとして並べた行列\(\boldsymbol{V}\)をndarrayとして生成するコードを書きなさい。得られたndarrayVとすること。

コード
    問06    
print('V = \n{}'.format(V))
結果
V = 
[[ 0.327  0.228]
 [ 0.609  0.211]
 [ 0.245 -0.806]
 [-0.583  0.126]
 [ 0.028  0.462]
 [-0.348 -0.157]]

★★★

  1. numpy.argsort()を使う。
  2. 整数配列インデキシングを使う。
  3. スライシングを使う。

主成分得点

ユーザ\(u\)の第\(k\)主成分得点\(p_{u,k}\)は次式で求められる。

\[p_{u,k} = \frac{\sum_{i \in I_{u}} r_{u,i}^{'} v_{k,i}}{\mid I_{u} \mid}\]

すべてのユーザについて、第\(d\)主成分までの主成分得点を計算すると、次式の潜在因子行列\(\boldsymbol{P}\)が得られる。

\[\boldsymbol{P} = \left[ \begin{array}{rr} -0.474 & 0.127 \\ -0.251 & -0.027 \\ -0.195 & 0.463 \\ -0.214 & -0.017 \\ 0.445 & -0.009 \end{array} \right]\]

このとき、次の問いに答えなさい。

07 ユーザuの第k主成分得点

ユーザ\(u\)の第\(k\)主成分得点\(p_{u,k}\)を求めるコードを書きなさい。得られた値をpukとすること。

コード
u = 0
k = 0
    問07    
print('p{}{} = {:.3f}'.format(u, k, puk))
結果
p00 = 0.474

★★

  1. リスト内包表記を使う。
  2. numpy.sum()を使う。

08 潜在因子行列

潜在因子行列\(\boldsymbol{P}\)をndarrayとしてまとめて求めるコードを書きなさい。ただし、潜在因子行列\(\boldsymbol{P}\)の次元数はDIMとする。得られたndarrayPとすること。

コード
    問08    
print('P = \n{}'.format(P))
結果
P = 
[[ 0.474 -0.127]
 [ 0.251  0.027]
 [ 0.195 -0.463]
 [ 0.214  0.017]
 [-0.445  0.009]]

★★★

  1. numpy.zeros()を使う。
  2. 二重のforループを使う。
  3. リスト内包表記を使う。
  4. numpy.sum()を使う。

★★★

  1. 三重のリスト内包表記を使う。
  2. numpy.sum()を使う。