『集合知プログラミング』の非負値行列因子分解とか

自分用メモ。

オライリーの『集合知プログラミング』で、
特徴抽出の為に非負値行列因子分解というアルゴリズムが使われているんだけど、
それをPythonからPHPに書き換えてみたコード。
本当はMath_Matrixを使いこなせればよいのだろうけど、
なんか行列の掛け算のロジックがちょっとあやしい気がする。

非負値行列因子分解ってのは1つの行列を2つの行列にバラすってことらしくて、
それぞれを「重みの行列」「特徴の行列」と見なすことにより特徴抽出に役立てるってことらしいのだけど、
詳しいことは本を読んでみたらいいと思うし、
まぁ個人的なメモなんで。

[php] <?php function a() { return func_get_args(); }

function shape($ar) { if (empty($ar)) { return a(0,0); } $r = count($ar); $c = count($ar[0]); return a($r, $c); }

function transpose($ar) { $rc = shape($ar); $r = a(); for ($i=0;$i<$rc[0];$i++) { for ($j=0;$j<$rc[1];$j++) { if (!isset($r[$j])) { $r[$j] = a(); } $r[$j][$i] = $ar[$i][$j]; } } return $r; }

function multiply($ar1, $ar2) { $rc = shape($ar1); $r = a(); for ($i=0;$i<$rc[0];$i++) { $r[$i] = a(); for ($j=0;$j<$rc[1];$j++) { $r[$i][$j] = $ar1[$i][$j] * $ar2[$i][$j]; } } return $r; }

function division($ar1, $ar2) { $rc = shape($ar1); $r = a(); for ($i=0;$i<$rc[0];$i++) { $r[$i] = a(); for ($j=0;$j<$rc[1];$j++) { $r[$i][$j] = $ar1[$i][$j] / $ar2[$i][$j]; } } return $r; }

function multiplyMatrix($ar1, $ar2) { $rc1 = shape($ar1); $rc2 = shape($ar2); if ($rc1[1] != $rc2[0]) { return FALSE; }

$r = a();
for ($i=0;$i<$rc1[0];$i++) {
    $r[$i] = a();
    for ($j=0;$j<$rc2[1];$j++) {
        $r[$i][$j] = 0;
        for ($k=0;$k<$rc1[1];$k++) {
             $r[$i][$j] +=
                $ar1[$i][$k] * $ar2[$k][$j];
        }
    }
}
return $r;

}

function difcost($ar1, $ar2) { $rc = shape($ar1); $r = 0; for ($i=0;$i<$rc[0];$i++) { for ($j=0;$j<$rc[1];$j++) { $r += pow($ar1[$i][$j] - $ar2[$i][$j], 2); } } return $r; }

function factorize($v, $pc=10, $iter=50) { list($ic, $fc) = shape($v); $w = a(); for ($i=0;$i<$ic;$i++) { $w[$i] = a(); for ($j=0;$j<$pc;$j++) { $w[$i][$j] = mt_rand(); } }

    $h = a();
    for ($i=0;$i<$pc;$i++) {
            $h[$i] = a();
            for ($j=0;$j<$fc;$j++) {
                    $h[$i][$j] = mt_rand();
            }
    }


for ($i=0;$i<$iter;$i++) {
    $wh = multiplyMatrix($w,$h);

    $cost = difcost($v, $wh);

    if ($i % 10 == 0) {
        echo $cost . "¥n";
    }

    if ($cost == 0) {
        break;
    }
    $hn = multiplyMatrix(transpose($w), $v); 
    $hd = multiplyMatrix(multiplyMatrix(transpose($w), $w), $h); 

    $h = division(multiply($h, $hn), $hd);
    $wn = multiplyMatrix($v, transpose($h));
    $wd = multiplyMatrix(multiplyMatrix($w, $h),transpose($h));

    $w = division(multiply($w, $wn), $wd);
}

return array($w, $h);

}

$a = a(a(1,2,3), a(4,5,6)); $b = a(a(1,2), a(3,4), a(5,6));

$m = multiplyMatrix($a, $b);

list($w, $h) = factorize($m, 3, 100);

print_r($m);

print_r(multiplyMatrix($w, $h));[/php]