"機械学習","信号解析","ディープラーニング"の勉強

読者です 読者をやめる 読者になる 読者になる

HELLO CYBERNETICS

深層学習、機械学習、強化学習、信号処理、制御工学などをテーマに扱っていきます

TensorFlowを始める前に知っておくべきテンソルのこと(追記:より一般的な話題へ)

 

TensorFlowとは

Googleが開発したライブラリ

TensorFlowとはGoogleが開発し、オープンソースとして公開したニューラルネットを記述するライブラリです。社内でも実際の業務に運用されており、研究から開発まで同じツールを使うことで業務を一本化しています。人工知能の最先端を行くGoogleが実際の業務に用いているということで、公開されてから爆発的に利用人口が増加しています。

 

実際にはニューラルネットワークを記述するのに特化しているというわけではなく、データ処理全般に用いることのできる仕様となっており、カバーする範囲はかなり広いです。

 

Tensorとは

Tensorとは多次元配列のこと

TensorFlowではデータをTensorとして扱い、そのFlowを制御して処理を実現させます。処理の流れを記述するのはプログラミングではアタリマエのことで、その文法自体は慣れの問題ですから良いとして、Tensorとはなんでしょうか。

 

簡単に言ってしまえば多次元配列のことです。

 

例えばベクトルデータ\bf xn個あれば、これらを下記のように並べて行列のように見ることができますね。

 

\bf X = (x_1,x_2,x_3,...,x_n)

 

f:id:s0sem0y:20161201210858p:plain

 

これは一次元配列であるベクトルを並べて、二次元配列である行列を作ったということです。

そして同じくして、このような二次元配列データXが複数あるならば、これらを並べて三次元配列を作ることができます。しかしベクトルを並べて行列にするような数式は書くことができましたが、行列を並べる数式というのは書くことができませんね。図でイメージを示すと以下のようになっています。

 

f:id:s0sem0y:20161201211428p:plain

 

当然、三次元配列を並べて四次元配列を作ることもできるわけですが、これは数式は愚か図で示すことももはやできません。イメージはなんとなくつくでしょう。

 

こういう概念をまとめて取り扱えるようにしたのがTensor(テンソル)ということだと理解しておけばいいでしょう。

 

Tensorの名前を与える

まず、二次元配列は一次元配列を並べて作ることができたのを見ました。

これらそれぞれにテンソルの名を与えましょう。

 

ベクトルを今後は1階のテンソルと呼ぶことにします。

そして行列を2階のテンソルと呼ぶことにしましょう。

当然三次元配列は3階のテンソルと呼ぶことができます。

 

ところで元をたどると、ベクトルというのはスカラー値を並べたものですから、こいつもテンソルとして扱ってしまったほうが便利です。

 

そこでスカラーのことは0階のテンソルと呼ぶことにします。

 

これで、○階のテンソルと言えば、それがどのようなデータの形をしているかが簡単に想像できるようになりました。

 

数式に落としこむ

しかし、三次元まではデータの形をそれなりに想像することができますが、それ以上はデータの形がどんなふうかなんてのは議論できません。私達は三次元空間までしか把握できないためです。

 

そこでこれらをもっと扱いやすくするために以下のように記述することにしましょう。

 

●0階テンソル

x

 

0階テンソル(スカラー)は単に何らかの値xに過ぎません。

ですからこれでいいでしょう。

 

●1階テンソル

x_i

 

1階テンソルは元々はベクトルです。何番目の成分かを指定するiを導入すれば、この1階テンソルを一般的に記述できます。

 

●2階テンソル

x_{ij}

 

2階テンソルは元々は行列です。行と列を指定するi,jがあれば2階テンソルのすべてを把握することができるはずです。これは元々は1階テンソルを並べたものですから、j個目の1階テンソルのi番目の成分だと考えることもできます。

 

●3階テンソル

x_{ijk}

 

同様に3階テンソルならば、データの形で言うならば「行と列と高さ」に相当する3つのインデックスを与えればいいでしょう。こちらも、k個目の2階テンソルのi,j成分ということになります。

 

●4階テンソル

x_{ijkl}

 

4階テンソルになると、添字にデータの形としての意味を与えることはできません(行とか列とか高さとかなんて概念は3次元までのお話です)。しかしそれは私達の理解を助けるために便宜的に用いてきた表現であって、ここまでこれば添字がもう1つ増えればl番目の3階テンソルのi,j,k成分を指定できると納得するはずです。

 

 

結局D階のテンソルならばD個の添字を持っておけば表現できるということです。

 

これはプログラマーには大変都合がいいですね。

日頃から

 

a[2][5][6]

 

などと3次元配列の要素にアクセスする文章を書いているはずです。

数学的に厳密話しはともかくとして、TensorFlowを使おうと思った時理解しておくのは、上述の通り、テンソルは多次元配列であるということで十分でしょう。

 

データとテンソル

グレースケール画像は2階テンソル

グレースケールの画像は2階のテンソルとして表現することができます。

28×28のピクセルならば一枚の画像が

 

x_{ij}   (i,j=1,...,28)

 

などとなるわけです。(言語によっては0〜27でしょう)。各i,jに何らかの値が入っており、その値がそのピクセルの黒さを指定していることになります。

 

RGB画像は3階テンソル

RGBの画像は3階のテンソルとして表現することができます。

28×28のカラー画像は

 

x_{ijk} (i,j=1,...,28) (k=1,2,3)

 

となっており、kの値が赤と緑と青の指定の役割を担います。

 

x_{12,18,1}

 

は12行18列のピクセルの赤色の濃さを表しているという形です。

 

インデックスは人間が決めていい

もちろん同じソフトウェアで解析をする限り、添字の順番は統一されている方が便利ですが、本人が認識していばどうでもいいはずです。

 

x_{ijk}

 

iが色を指定していても何も問題ありません。

順番は自由に付けてもいいのと同じように、添字に対する意味も何でもいいはずです。

 

例えばグレースケール画像x_{ij}に対して空間フィルタfを30個準備しておいて、空間フィルタに対して添字kを与え、f_kなどと表しておきましょう。

 

グレースケール画像x_{ij}に対して各フィルタf_kを通ったあとの画像は

 

y_{ijk}=f_k(x_{ij})

 

と表すことができ、二階のテンソル(画像)をフィルタの個数分だけ並べた三階テンソルy_{ijk}が獲得されます。もちろん個人が認識していれば添字の順番はy_{kij}でもいいでしょう。(普通は最後に付け足したほうが分かりやすいですが)

結局ここではkが何個目のフィルタかを指定するインデックスになっているというわけです。

 

これは畳み込みニューラルネットを扱う上では標準的な考え方になっています。

 

他にも、複数のセンサーcから信号が入ってくるとしたならば添字tでサンプリングタイムを指定し、s_{ct}などと二階のテンソルでデータを扱うことができます。

 

あるセンサーの信号を時間周波数解析したならば、各周波数fの情報が得られるので1つのセンサーの信号はs_{tf}などと二階のテンソルで表されることになり、更に複数のセンサーがあるならば、センサーのインデックスcを使ってs_{ctf}と3階のテンソルとしてまとめておくことができるでしょう。(この際の添字の順番は、どのような順序で考えたかだけの話で本質的には意味はありません。)

 

このような形で複数の電極からの信号を時間周波数解析で3階テンソルにしておき、適切な情報を抽出する研究が脳波解析では盛んです。

 

 

注意点

n階テンソルをn次元配列であると考えると良いというお話ですが、これは世の中に一般的に認知されているように思います。その結果1つ注意すべき点が出てきています。

 

普通データの次元を考えた時には次元とは、要するにデータの要素数のことを指します。

 

例えば(床面積、築年数、階数)が格納された物件のデータの次元は3次元だと言えます。しかし、これはベクトルデータ(すなわち1階のテンソル)ですから、時折データが1次元配列だと表現されるわけです。

 

画像の場合28×28のピクセルで表現されたグレースケールのデータというのは784次元であって、この各ピクセルの値が画像を特徴付けていると言えますが、これは2次元配列だと表現されることになります。

 

次元という言葉が出てきた時に、それがテンソルの階数(配列の次元)を言っているのか、データの要素数のことを言っているのかは注意が必要です。

 

そんなことはわかってるよ!ということであれば心配はありません。

 

 

余談

テンソルをネットで調べれば、もっと難しい話がいっぱいあるはずです。

こんな簡単な概念を数学では何故に小難しく話す必要があるのか。

一応このことを説明しておきます。

 

以下のことは機械学習を学ぶ上では全く関係ないです。

 

しかし概念に対する理解を深めておくと思わぬところで発見があるかもしれません。

興味があれば読んでみてください。

 

1階のテンソルを例に

ある物体に生ずる重力というのはいつでも下方向に掛かります。なぜそのように考えることができるのでしょうか。それは座標をそうなるように取っているからです。

床にx,y座標を立てて、上下方向にz軸を立てた場合、重力はいつでもz軸だけ考えておけばいいです。z軸に対してしか値が現れないのです。

しかし、もしも座標のとり方を変えて、座標を斜めに取った場合は、重力というのは複数の軸に分けられることになります。

 

f:id:s0sem0y:20161202212055p:plain

 

 

高校物理御用達の三角形の謎の台の上に物体が乗っている問題を見ましょう。

この問題ではきっと以下のように座標を取り直すことで、スッキリと問題が解けることを知っているはずです。物体が三角形の台にのめり込んだりしない限りは、台の形に沿って移動することを知っているから、以下のように座標を取ろうと思えるのです。

 

f:id:s0sem0y:20161202212422p:plain

 

あとは取った座標方向に重力を分解すれば、台に沿った方向の力だけを考えれば問題を解くことができます。

 

 

ここで重要なことは、座標を人間がどのように取ろうと、物体の動作(物理現象)の結果は本質的に全く同等であるということです。

 

座標のとり方を変えれば、当然計算される座標の値は異なってきます。例えば左の絵では、ひとつの成分にしか重力が現れませんが、右の絵では2つの成分に重力が分解されます。しかし、物理現象は全く持って同じ結果でなければなりません。

座標の取り方で物理現象が変わるならば、人間は神様であるのと同じになってしまいます。

 

ここで重力を分解した際のベクトルというのは1階のテンソルになっています。

例えば左の座標では重力ベクトル\bf g

 

{\bf g}=(0,g_y)^T

 

などとなっているはずです。一方で右の座標では

 

{\bf g}=(g_{x'},g_{y'})^T

 

と2つの成分で値を持っているはずです。

 

基底を変換する前とあとでは、このテンソルの要素の値は異なってくるのですが、本質的に表していることは同じでなければなりません。

このテンソルの要素の変化の原因は基底のとり方によるものですから、基底に生じた変換が重力ベクトルの変換と関連づいていなければなりません。左の図と右の図では基底は単に少しだけ回転されただけですから、重力を表現しているテンソルも、基底を回転させたのと同じように回転させれば値が得られるはずであるということです。

 

一般にこのような基底の変換に対して生じる変換に対する性質をきちんと満たしているようなものをテンソルと呼びます。

 

応力解析での応用

上記の例はつまらない例題です。

テンソルの概念がおそらく最も応用されているのは応力解析でしょう。

例えば機械の強度を測るときに、荷重を加えて、機械のどの位置にどのような方向でどれほどの力が生じているかを調べたりします。応力というのは、ある単位面積に対する力です(圧力のようなもの)。

 

このとき座標(基底)というのはどのように取るべきでしょうか。

例えばサイコロ型の物体に荷重を加えた場合には、なんとなく座標の取り方というのは想像がつきます。きっと以下のように座標を取るでしょう。

 

f:id:s0sem0y:20161202214411p:plain

 

では今調べようと思っている物体が、正八面体ならどうでしょう、正十二面体ならば?もっと一般的に曲面を含む複雑な形状ならどうでしょう。

応力というのは、単位面積あたりの力ですから、できれば座標は面に沿って取りたいのです。サイコロならば上記の座標ですべての面をカバーできます。でも一般的な機械はそう上手くは行きません。

 

かと言って、各々好きに座標を取った場合には解析者がどのような座標で解析をしたかによって応力の計算結果が異なってしまうのです(基底の取り方で成分の値が異なるのを見ましたね)。

でもそれを統一するのも無理です。応力を測定する装置に機械をどのようにセットするのかをこの世に存在するすべての形状に対して決めるのは無理だからです。

 

ですから、応力をテンソルとして表現できたならば大変便利なのです。

勝手な基底を取ったとしても、それがテンソルである限り、基底を共通のものにあとから変換すれば同じ基準を使えるからです。

 

 

通常、応力を適当な基底で測定した場合には2階のテンソルが得られます。対角成分は垂直応力で、非対角成分がせん断応力です。基底の取り方によっては、せん断応力が0になる(つまり応力テンソルが対角行列になる)ような場合があり、きっとこれが最も便利な基底のとり方です。

 

そのような基底の変換を得るのは実は特異値分解に他なりません。

 

数学ではテンソルがなぜ複雑に見えるか

特異値分解は二階のテンソルを分解する一般的な手法です。

応力解析では、本質的に空間が3次元であるがためにこれ以上に高階のテンソルを扱う必要はあまりありませんが、更に高階のテンソルを分解する手法はテンソル分解として研究されています。

これが何に応用されるかはともかくとして、数学ではあることで成り立った法則や便利な定理、公式たちが、実はもっと複雑な(より一般的な)場面で成り立たないのかに強く興味を持ちます。

 

そうなると、今まで扱ってきた具体的な事例から、本質的な部分のみを取り出して抽象化しようと試みるのです。その結果、一般人には到底近づけない難解な数学が構築されるのです。

このことの是非はいろいろ問われていますが、言及はしないでおきましょう(数学を批判しているように見えますが、相対論など、数学が整備されていた背景があって成り立った応用もあります)。

 

 

ただ私達は、テンソルと聞いて変に怖気づく必要はありません。

 

必要に応じて必要な数学のレベルを吸収しておけばいいのです。