AIに欠かせない数学を、プログラミング言語Pythonを使って高校生の学習範囲から学び直す連載。今回は行列の計算分解について、図版とPythonコードを交えて解説します。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
AIに欠かせない数学を、プログラミング言語Pythonを使って高校生の学習範囲から学び直す本連載『「AI」エンジニアになるための「基礎数学」再入門』。前回は「行列計算」について学びました。今回のテーマは、「行列の計算分解」です。
前回、データを分析する際には、基本的に1次元のベクトルデータではなく多次元のデータを用いることが多いので、行列計算が重要になると解説しました。行列の計算は、AIが行う計算でもよく使用されており、さまざまな分析に関わる教科書などは行列表記での説明がほとんどです。今回紹介する「行列分解」は、そんな行列計算の中で重要なテクニックの一つです。
行列分解をする理由や行列分解後に得られる結果の意味などについて、数式の内容よりも意味の解釈に注力して解説するので、そこに注目して学習してください。
初めに「行列分解とは何か」を説明します。行列分解とは、ある行列を行列の積に分解することを意味します。中学生の時に学習する「因数分解」の行列版です。昔を思い出すと因数分解は足し算や引き算で表されている数式をカッコ付きの掛け算の形にすることでした。下記のようなものです。
行列分解は、これを行列で行うものです。行列に対する行列分解の例は次のようになります。
このように、行列分解はある行列を別の行列の積に分解することです。また上記の行列分解は一例であり、一般に行列分解は次数が同じ「行列の積」ではない場合でも分解できます。行列の積については、前回詳しく説明しているので、知りたい方は参照してください。
例で用いた行列分解は特に意味のあるものではありませんが、業務で使う行列分解は、「ある特定の形に分解すること」を目的とします。行列を分解する目的は大きく分けて2つあります。
1つ目は計算上の実用的な理由で、計算量(計算時間)を減らすためです。そもそも実はPCで計算する場合でも行列計算をするには多くの計算量を必要とします。そこで、この計算量が少なくなり、より効率的に計算できる形に行列分解してから行列計算することがあります。
2つ目はAIがデータを要約してデータが持つ系統的な性質を取り出すためです。ただ眺めるだけでは分からなかった行列データを分解することで見えてくることが多々あります。このような行列分解は画像処理や推薦システムなど幅広い分野で用いられています。
前回の解説で「行列は連立1次方程式を解きやすくするために行列が作られた」と説明しました。連立1次方程式を行列とベクトルの積で表し、行列の逆行列を求めてその積を計算することで連立1次方程式を解くことができます。行列をA、ベクトル「→x」「→b」とすると、連立1次方程式は下記のよう表せます。
Aと→bが既知の場合、(1)式の→xは、Aの逆行列「A-1」を求めると下記のように式を変形して求めることができます。
しかし、実は逆行列を直接Aから求めて連立1次方程式を解くやり方は推奨されていません。次数が大きい行列(行数、列数が大きい行列)の場合は必要とされる計算時間が膨大になるという実用面での問題が存在するからです。この計算量を減少させて連立1次方程式を解く数学のテクニックとして行列分解が用いられます。
次数が大きい行列では、どの程度の計算量になるのかを解説します。
まずは、スカラーと行列の積を考えてみます。2行2列の行列の行列Aの場合で考えると、スカラー値「5」との積は次のようになります。
2行2列の場合は行列の要素が4個存在するので、その計算回数は4回(つまり次数の2乗個)になります。よってN次の正方行列(N行N列の行列)ではN2回の乗算をする必要があります。単純な掛け算ですが、3次の正方行列では計算回数が9回、10次になると100回と次数が大きくなるにつれて計算量が飛躍的に上がっていきます。
次に、行列同士の積での計算回数を確認します。2行2列のある行列A、Bの積を考えると次のようになります。
この場合は、4要素(次数の2乗個)について計算されていますが、各要素を計算するためにAの行要素とBの列要素の内積が必要になるので、各要素で2回の乗算が行われています。この2回の乗算が要素数分の4個必要になるので、2×4の計8回の乗算をすることになります。さらに、行列の積では各要素内で和も計算しており、こちらは1回(次数-1)の和が要素数分必要なので、1×4の計4回存在します。これをN次の正方行列同士の積で考えると、乗算がN3回、加算がN2*(N−1)回必要になります。つまり、10次で1000回の乗算と900回の加算を計算することになり、行列同士の積はスカラーと行列の積よりもさらに計算量が増えています。
Pythonのコードでもこの計算を確認してみましょう。前回では「Numpy」の「dot」関数で行列の積を計算していましたが、行列の積による計算量理解のために、行列の積をfor文で記述します。また、積を行う行列は簡単のため、単位行列にしてあります(当然、積の結果は単位行列になります)。
import numpy as np # 行列の次数を定義 N = 10 # N行N列の単位行列生成 A = np.eye(N) B = np.eye(N) # 行列の積の結果を格納する行列定義 C = np.zeros((N,N)) # 行列の積計算 for i in range(N): for j in range(N): for k in range(N): C[i,j] += A[i,k]*B[k,j]
このコードを見ると、for文を3回使用しているのが分かります。各for文でN回実行されるのが、独立して3つあるので、N3回の計算が必要になっています。Nを大きくしていくと計算時間が飛躍的に長くなるので試してみてください。
また、一般的な「掃き出し法」を用いて逆行列を求めた場合、必要となる計算の回数は行列の積と同様に乗算がN3回に、減算がN2*(N−1)回必要になります。つまり、行列の積同様N3回に比例する計算量が必要です。よって、逆行列を求めてその逆行列の積を求める場合でも次数が大きい場合に計算量が膨大になります。
ここまでで、行列の積や逆行列の計算には計算量が膨大になり、実用面で難があることが分かったと思います。ここからは行列分解することで計算量がどう変化するかを解説します。計算量削減の行列分解方法は多々ありますが、その中でも基本的な「LU分解」について説明します。
Copyright © ITmedia, Inc. All Rights Reserved.