Pytorch の tensor を理解してみる 前編 テンソルを理解する

昨今話題の PyTorch ですが、
どうせやるからにはテンソルの数学的な意味を
ちゃんと押さえたいところです。


実践においても、
背景にある数学を理解しておけば、
初歩的なエラーを避けやすいでしょう。


そこで今回は、
お気に入りの本の1つである
幾何学から物理学へ』に沿って、
テンソルの理論的な理解をしたうえで
改めて実装の仕方を整理する。
これを行っていきます。

事前準備:ベクトルと数ベクトル


まずは (3.33)、
「枠」を使ったベクトルの表現に
着目します。

 \begin{eqnarray}
\boldsymbol{v} &=& v^{1} \boldsymbol{e} _1 + \cdots + v^{n} \boldsymbol{e} _n \\
 &=& \tilde{v} ^{1} \tilde{\boldsymbol{e}} _1 + \cdots + \tilde{v}^{n} \tilde{\boldsymbol{e}} _n
\end{eqnarray}


このように、ベクトルは 枠の要素  \boldsymbol{e} _{\mu}
対応する成分  v_{\mu} を掛け、線形結合したものとして
表現できるようです。

ここで興味深いのは、枠の取り方しだいで
その成分の値も変わるという点です。


数学慣れしていない私なんかは、これのことを
思わず「基底」と呼びたくなります。
しかし基底は同書 21 P で別途定義
与えられているため、慎重な区別が必要です。


具体的には、枠  ( \boldsymbol{e} _1, \cdots \boldsymbol{e} _n) という tuple に対し
基底は、  \{ \boldsymbol{e} _1, \cdots \boldsymbol{e} _n \} という set、
つまり枠の並び順を無視した集合というものです。


ここで、線形写像  T というものがあります。

(3.36) の式のスポンジボブのような表記は
一瞬目がくらむので少し簡略化して
書いてみましょう。

正味のところ  T は、

 \displaystyle \tilde{v} ^{\mu} = \sum _{\nu = 1} ^{n} T ^{\mu} _{\nu} v^{\nu}

と、成分から成分への変換だと分かります。


言い方を変えると、 T
枠を枠に変換するものではないわけですね。

枠から枠への変換はやはりというか、
 \displaystyle \boldsymbol{e} _{\nu} = \sum _{\mu = 1} ^{n} \tilde{\boldsymbol{e}} _{\mu} T ^{\mu} _{\nu}
と、
チルダとそうでないものの関係が逆転します。


恐らくですが、(6.13) と合わせると、
 \displaystyle \tilde{\boldsymbol{e}} _{\mu} = \sum _{\nu = 1} ^{n} \boldsymbol{e} _{\nu} (T ^{-1}) ^{\nu} _{\mu}
と、枠の変換が成分の変換の丁度逆行列
なっているものと推論できます。


いずれにしても、
正規直交基底変換を考えるときに
よくつまずいた苦い思い出があるので、
このあたりの記述が振り返れるのは
嬉しいところです。

なお、あえて一度スルーしましたが、
射影とおぼしき  \theta ^{\mu} (\boldsymbol{v}) = v^{\mu} の束
 \Theta = ^T ( \theta _1, \cdots ,\theta _n) は、
ベクトルを「ベクトル」に変換する
写像であるとされています。


数ベクトル  ^T ( v _1, \cdots , v _n)
ベクトルと区別されているのには
一瞬ギョッとします。

が、確かに、
数ベクトルだと「何の」枠の成分か分からず
ベクトルの意味が一意に定まらない意味で、
やはり区別すべきでしょう。


例えば多項式同士の基底変換を考えるときに、
何の多項式の成分なのかを忘れるとマズいわけですよね。


なお、上では  \Theta: V \rightarrow \mathbb{R} ^{n}
28P では射と見做されますが、
ベクトル空間の圏を考える 34P では
今度は(共変)関手とみなされます。


考える圏次第で扱われ方が変わる、
いい例になっていますね。

テンソルの意味を確認する

目的である torch.tensor は、
numpy の類推で考えれば単に、
多重配列*1に過ぎないという感があります。


つまり1重配列をベクトル、
2重配列を行列とし*2
これを多重に一般化したものが
torch.tensor だと言えるでしょう。


ただし、このとき、うえで見てきた「枠」は
固定されているとみなすのが妥当です。
(つまり「数」ベクトルであること忘れ、
 数ベクトルとベクトルを同一視し、
 基底変換は起こらないものとします。)


以上を踏まえると、torch.tensor は単に、
テンソル」において枠を固定した
特殊な場合だと思えばよいでしょう。


めでたしめでたし・・・と
言いたい所です。


しかし・・・、
まだ気を付けるべき点が残っていると気づきます。



幾何学から物理学へ』を読んでいくと、
ベクトルとコベクトルの区別なども必要で、
それほど事が単純ではないようなのです。


理論上、当然行列はテンソル(の表現)に
包含ざれるはずですが*3

パッと見た感じでは、
同書からそのことが確信しにくいです。


今回はこれを乗り越えてみましょう。
一度これを通じることで、
対象と射の区別をしたり、
対象を型の制約に活用したりといった、
豊饒な見方が得られるはずです。



私が上記のように行き詰ったのは、
テンソルが初出となる同書 40P です。


テンソル積 ⊗ で言わば多重配列にされた、
 \boldsymbol{v} _1 \otimes \cdots \otimes \boldsymbol{v} _r ( \in V_1 \otimes \cdots \otimes V_r)
これが(テンソル積空間の元である)
テンソルだと言います。


明記はされていませんが、その自由度は
 \prod _{i=1} ^{r} {\rm dim} V_i であるようです。


しかし、ここで挙げられている用例は
ずいぶんと限局的なものに見えます。
上で登場した  \boldsymbol{v} _1 \otimes \cdots \otimes \boldsymbol{v} _r は、
 V^{*} _1 \otimes \cdots \otimes V^{*}_r \rightarrow \mathbb{R} という
実数への射だとされています。


しかし、
多重配列を単にスカラーにするのがテンソルだ、
と言われてしまっては、
じゃあ(ベクトルをベクトルにする)行列は
テンソルではないの?と、
頭を悩ませてしまいます。


その直前に登場する関数の積も同様で、
 f _1 \otimes \cdots \otimes f _r ( \in V^{*} _1 \otimes \cdots \otimes V^{*}_r)
 V _1 \otimes \cdots \otimes V_r \rightarrow \mathbb{R} という射だそうです。
これまた実数に圧縮してしまうばかりです。


ベクトルをベクトルにする行列は
一体テンソルのどこに現れるんでしょうか。


これにはしばらく頭を悩ませましたが、
実はのちに分かるように、テンソル空間は
ベクトル空間とコベクトル空間の両方を
同時に含んで良く、
これを考えることで行列(で表現されるもの)を
包含することができそうです。


そもそも、
ベクトルかコベクトルの一方のみからなる
テンソルというのは、
それ自体特殊な例だったようです。


※ ↓↓ 2020/06/19 追記 ↓↓
以下では、ベクトルを V とコメなしで、
コベクトルを V^* とコメありで
それぞれ表記するものとします。
※ ↑↑ 追記ここまで ↑↑


後続の (6.7) 式にテンソル空間  \mathcal{T} が定義されるので、
これを落ち着いて見てみます。すると、
 \mathcal{T} _q ^p (V) =  (\otimes ^p V) \otimes ( \otimes ^q V^*)
と、ベクトル空間、コベクトル空間がそれぞれ
 p 重、 q 重になっています。


ここで、 46P に戻ってみると、
 A \in V^{*} _1 \otimes \cdots \otimes V^{*}_r \otimes W なる射が
 A:  V _1 \otimes \cdots \otimes V_r \rightarrow W
であると言っています。


始域ベクトルに作用させる射が
コベクトルだけではなくベクトルを含むとき
終域がベクトルになるようです。


さらには恐らく、ベクトル空間を多重に含むとき、
その多重性の分だけ終域が、
多重のベクトル空間のテンソル積を持つでしょう。


ということは端的に、行列とは、
 A \in V^* \otimes W
という射を要素表示したものではないでしょうか。
1重ベクトルを1重ベクトルに移しますからね。


さて、ここで、
何を対象とみなし、何を射とみなすか
一歩ずつ確認しながら振り返りましょう。


いま、対象のベクトル空間  V が、
射の行列(で表現されるテンソル A によって
対象のベクトル空間  W に移されるとしましょう。


すると当然、 A \in V^* \otimes W
横が  {\rm dim} V 、縦が  {\rm dim} W
行列で表現できるでしょう。


対象がベクトル空間のみしか含まない場合を
考えましょう。


射のテンソルに含まれるコベクトルによって
始域のベクトル空間(の1~多重性)が打ち消され、
その代わり、
射のテンソルに含まれるベクトルによって
終域のベクトル空間(の1~多重性)が
復元されるわけです。


これは多重配列から多重配列の射に
容易に拡張できるでしょう。


もし、1重のテンソル V から
2重のテンソル W_1 \otimes W_2への射を作りたけれれば、
端的にテンソル  H \in V^* \otimes W_1 \otimes W_2
を考えればよいのです。


ここでうれしいのは、射のテンソル空間が、
何階のテンソル積を何階のテンソル積に移すのか
内包して持っている点です。


上のテンソル  H \in V^* \otimes W_1 \otimes W_2 でいえば、
ただ単に3階テンソルであるだけでなく、
1階を2階へ移すテンソルであることが
明確化されているわけですね*4


加えて言えば、 V V^*
 W_1 W^*_1 らの双対性が、
次元が合わない射は使えないという制約を
うまく内包できているわけですね。


これは合成射を考える上でも都合がいいです。


分かりやすさのために、再び、
行列で表現されるテンソルを射としましょう。


 {\rm dim} U, {\rm dim} V, {\rm dim} W がそれぞれ
2、4、3であるとき、
 A: U \rightarrow V は4行2列、
 B: V \rightarrow W は3行4列で表現されることが
期待されるわけですが、ここで、
合成射  B \circ A: U \rightarrow W が3行2列であると
始域と終域からも導かれるわけです。


このように、圏の性質によって
射が始域と終域に束縛されることから、
型の合わない射が使えないことが
自然に示唆されてきます。


なお、テンソルテンソル積は双関手として
理解されるようですが*5
同書では双関手としての理解がされていないため
これについてはまた後日考えます。


さて、ではこのテンソルの知見を使うと、
torch.tensor の演算をするさいの見通しが
どのようにクリアになるでしょうか。
次回、これを考えてみましょう。

まとめ

  • ベクトルは単に要素を並べたものではなく、「枠」を持つ。
  • テンソルの多階性は、ベクトルのベクトル積(直積のようなもの)と、コベクトルのベクトル積の両方に由来する。
  • 対象がベクトル空間、射がテンソルであるとする。このとき、射のベクトルと始域の要素のベクトルは内積をなして、多階性を対消滅させる。同時に、射のベクトルが終域の多階性を作る。
  • torch.tensor は、枠を考慮しないまたは固定している点と、ベクトルとコベクトルを区別していない点で、テンソルよりも緩いものであると言える。

このように見返すと、torch.tensor はその緩さの分、
対象としても射としても柔軟に使えるが、
運用を気をつける必要があるとも言えるでしょう。


逆に言えば、どの階をベクトルとして使い
どの階をコベクトルとして使うかを意識すれば、
力強いツールとなるでしょう。


それでは、ここまでありがとうございました。





参考文献


god101037.hatenablog.com


※出版社サイト


はじめて学ぶリー群 ―線型代数から始めよう

はじめて学ぶリー群 ―線型代数から始めよう

※出版社サイト
双対空間の説明がわかりやすいです。
ベクトル空間に対する線形関数空間の要素がコベクトルだ、と、
ベクトルを対象とする条件下での両者の関係が明確にされます。


山上滋. "作用素環とテンソル圏." 数学 59.1 (2007): 56-74.
https://www.jstage.jst.go.jp/article/sugaku/59/1/59_1_56/_pdf/-char/ja

*1:以下では階数が複数あることを「多重」と記載します。テンソル積の話題の登場後、適宜、多階のテンソル積、という言葉に置き換えていきます。

*2:一般のテンソルを考えると、2重配列が必ずしも行列であるとは限りません。のちの脚注で、2重配列だが行列でない例を考えます。

*3:行列は枠を考慮しないという意味でテンソルそのものではありません。行列は、ある特殊な2階テンソルを、要素表示したものだというべきでしょう。以降では、要素表示、あるいは表現という風に書き添えます。

*4:ということは、1階のベクトルに対して、2階のベクトルのテンソル積からなるテンソルを作用させると、3階のベクトルのテンソル積が得られることになります。プログラム上では使用の仕方に依存しますが、これを思うと2重配列が必ずしも行列であるといえないわけです。

*5:例えば「作用素環とテンソル圏