しゃけのブログ

某大学院で研究している人の備忘録

GDBでEigenをきれいに表示する

言わずもがな、GDBC++プログラミングなどにおいて必須なデバッガツールである。

デフォルトの設定だと、EigenのMatrixの中身が生データで表示されていて、ColMajarなどになっていると見づらい。

$11 = (const Eigen::Matrix<double, 3, 6, 0, 3, 6> &) @0x7f65a67fab30: {<Eigen::PlainObjectBase<Eigen::Matrix<double, 3, 6, 0, 3, 6> >> = {<Eigen::MatrixBase<Eigen::Matrix<double, 3, 6, 0, 3, 6> >> = {<Eigen::DenseBase<Eigen::Matrix<double, 3, 6, 0, 3, 6> >> = {<Eigen::DenseCoeffsBase<Eigen::Matrix<double, 3, 6, 0, 3, 6>, 3>> = {<Eigen::DenseCoeffsBase<Eigen::Matrix<double, 3, 6, 0, 3, 6>, 1>> = {<Eigen::DenseCoeffsBase<Eigen::Matrix<double, 3, 6, 0, 3, 6>, 0>> = {<Eigen::EigenBase<Eigen::Matrix<double, 3, 6, 0, 3, 6> >> = {<No data fields>}, <No data fields>}, <No data fields>}, <No data fields>}, <No data fields>}, <No data fields>}, m_storage = {m_data = {array = {6.9206107951848877e-310, 4.9406564584124654e-324, 1.6798231958602383e-322, 
          6.9205842353398654e-310, 6.9206028846373822e-310, 6.9206028846381727e-310, -0.012499007761522431, -0.012499007761522431, 
          0.99992188435146145, 0.99992188435146145, 1, 0.99992188435146145, 0, 0, 1.3041326032698003e-18, 8.5038092345563313e-37, 1e-10, 
          0}}}}, <No data fields>}

Eigen公式が出しているフォーマッタをGDBに読み込ませることできれいな表示が可能になるらしいので試してみた。

Setup

次のファイルが本体で、これを読み込ませるようなスクリプトgdbが読み込む設定ファイルに書き込む。

https://gitlab.com/libeigen/eigen/-/blob/master/debug/gdb/printers.py

GDBは~/.gdbinitを読み込むらしいので、上のpythonコードに書いてあるように次のように書き込む。

python
import sys
sys.path.insert(0, '/path/to/eigen/printers')
from printers import register_eigen_printers
register_eigen_printers(None)
end

ここで/path/to/eigen/printerseigen/debug/gdbになる。

※ Eigenをapt-getなどでインストールした人の環境にはeigen/debug/gdbが存在しないので、上のリポジトリをダウンロードして適当な場所において、そのパスを設定する。

結果

次回以降のGDBでEigen Matrixの変数を表示したときは次のようになる。

$1 = Eigen::Matrix<double,3,6,ColMajor> (data ptr: 0x7f32463e5b30) = {[0,0] = 6.9097051814122555e-310, [1,0] = 4.9406564584124654e-324, 
  [2,0] = 1.6798231958602383e-322, [0,1] = 6.9096857975822047e-310, [1,1] = 6.9097009197853916e-310, [2,1] = 6.9097009197861821e-310, 
  [0,2] = -0.012499007761522431, [1,2] = -0.012499007761522431, [2,2] = 0.99992188435146145, [0,3] = 0.99992188435146145, [1,3] = 1, 
  [2,3] = 0.99992188435146145, [0,4] = 0, [1,4] = 0, [2,4] = 1.3041326032698003e-18, [0,5] = 8.5038092345563313e-37, [1,5] = 1e-10, [2,5] = 0}

やや見やすくなったかな?

WSL2上のDockerコンテナで起動したROSノードのトピックをWSL上のrvizから見る

構成

Dockerコンテナ

  • node、rvizで表示するトピックをpublishする

WSL2上

  • roscore
  • rviz

DockerコンテナからWSL上のroscoreへアクセス

host.docker.internalがWSLへのアクセスとなるので、ROS_MASTER_URIを変更する。

export ROS_MASTER_URI=http://host.docker.internal:11311

WSL上のGUI起動

export DISPLAY=:0.0

CMakeのチュートリアルをやる(Step2)

CMake Tutorial

CMakeをC++プロジェクトなどで使うことは多いのですが、付け焼刃の知識しかなくて正しい書き方がいまだにわからない... なので初心に戻って、CMakeの公式が作ったチュートリアルをやっていこうと思います。

この記事はその時のメモみたいなものです。

参考はこちら。 チュートリアルの元となるプロジェクトファイルたちもここからダウンロードできます。

CMake Tutorial — CMake 3.25.0-rc3 Documentation

Step2: 自作ライブラリの参照と制御

次の要素が含まれています。

  • 自作ライブラリ(cppファイルとヘッダファイル)の作成
  • ライブラリの参照
  • 使用するライブラリのCMakeLists.txtからの制御

ディレクトリ構成

メインのプロジェクトの下にライブラリディレクトリがある構造です。

Step2
 |-- MathFunctions
 |     |-- CMakeLists.txt
 |     |-- MathFunctions.h
 |     `-- mysqrt.cxx
 |-- CMakeLists.txt
 |-- tutorial.cxx
 `-- TutorialConfig.h.in

ライブラリの作成

Step2/MathFunctions/CMakeLists.txt

add_library(MathFunctions mysqrt.cxx)

add_library

add_library(MathFunctions mysqrt.cxx)

MathFunctionsというライブラリをmysqrt.cxxから作るコマンド。

ここでは、mysqrt.cxxが参照する自作のヘッダファイルはないので、target_include_directoriesなどは不要。

Linuxでビルドしてみると、ビルドディレクトリの下にMathFunctionsというディレクトリができて、libMathFunctions.aという静的ライブラリが作られます。

動的、静的の違いはまた説明するとして、ここではライブラリが作れるんだな~ぐらいの理解でとどめておきます。

ライブラリの参照

メインのCMakeLists.txtから、ライブラリディレクトリのCMakeLists.txtを参照して、実行ファイルへ紐づけます。

Step2/CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

project(Tutorial VERSION 1.0)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

configure_file(TutorialConfig.h.in TutorialConfig.h)

add_subdirectory(MathFunctions)

add_executable(Tutorial tutorial.cxx)

target_link_libraries(Tutorial PUBLIC MathFunctions)

target_include_directories(Tutorial PUBLIC
    "${PROJECT_BINARY_DIR}"
    "${PROJECT_SOURCE_DIR}/MathFunctions"
    )

add_subdirectory

add_subdirectory(MathFunctions)

別のCMakeLists.txtを追加することができます。この時、CMakeLists.txtを含むディレクトリを相対パスで指定しています。

ちなみに、subdirectoryではないディレクトリ(例えば親ディレクトリにあるディレクトリなど、../MathFunctions2とか)を指定すると次のようなエラーが出ます。

  add_subdirectory not given a binary directory but the given source
  directory
  ".../cmake-3.25.0-rc3-tutorial-source/MathFunctions2"
  is not a subdirectory of
  ".../cmake-3.25.0-rc3-tutorial-source/Step2".
  When specifying an out-of-tree source a binary directory must be explicitly
  specified.

階層が異なる時は親ディレクトリにもCMakeLists.txtを置いて、本ディレクトリとMathFunctions2ディレクトリをadd_subdirectoryしてもらう方法をとる方がよい。

MathFunctions2は親ディレクトリの管轄、というイメージ。

target_link_libraries(Tutorial PUBLIC MathFunctions)

このコマンドはかなり重要で今後も頻出する予定です。

Tutorialというターゲットに対して、MathFunctionsというライブラリを紐づけています。今回の場合は、libMathFunctions.aという静的ライブラリを紐づけていることになります。

例によって、PUBLICというキーワードの紹介は保留です。

target_include_directories

target_include_directories(Tutorial PUBLIC
    "${PROJECT_BINARY_DIR}"
    "${PROJECT_SOURCE_DIR}/MathFunctions"
    )

Tutorialがインクルードするヘッダファイルを探索するディレクトリ情報を追加します。

このコマンドは前回も登場しましたが、今回のように複数を一度に指定することもできます。

今回は、MathFunctionsにあるMathFunctions.hの場所を知りたいので、CMakeLists.txtが存在するディレクトリを指す変数である${PROJECT_SOURCE_DIR}を使って、MathFunctionsディレクトリを指定しています。

これによって、${PROJECT_SOURCE_DIR}/MathFunctionsがインクルードの探索ディレクトリとして追加されているので、C++ファイル側では #include "MathFunctions.h"と、MathFunctionsディレクトリから見た相対パスでヘッダファイルを指定できるようになっています。

ビルドの制御

cmake変数を使って、ソースファイルで使用する関数を制御します。

具体的には、CMakeLists.txtでUSE_MYMATHという変数を用意して、これがONならライブラリをビルドしてTutorialターゲットに紐づけ、#ifdefによってライブラリの関数を呼び出すようにします。

逆にOFFなら、MathFunctionsライブラリはビルドせず紐づけも行わず、#ifdefによって標準関数であるsqrtを呼び出すように変更します。

最終的なCMakeLists.txt(Step2の下の)は次の通りです。

cmake_minimum_required(VERSION 3.10)

project(Tutorial VERSION 1.0)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

option(USE_MYMATH "Use tutorial provided math implementation" ON)

configure_file(TutorialConfig.h.in TutorialConfig.h)

if(USE_MYMATH)
    add_subdirectory(MathFunctions)
    list(APPEND EXTRA_LIBS MathFunctions)
    list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif()

add_executable(Tutorial tutorial.cxx)

target_link_libraries(Tutorial PUBLIC "${EXTRA_LIBS}")

target_include_directories(Tutorial PUBLIC
    "${PROJECT_BINARY_DIR}"
    "${EXTRA_INCLUDES}"
    )

option

option(USE_MYMATH "Use tutorial provided math implementation" ON)

変数を追加することができます。

setと混同しそうで注意したいところですが、optionはbool値を扱う時に利用されます。

また、単純なsetを使うと変数の内容が呼び出された瞬間に決まりますが、optionではcmake .. -DUSE_MYMATH=OFFのように内容をコマンド呼び出し時に変更することができます。

ここで、"単純な" といったのはsetにもCACHEという引数を追加することで、cmakeコマンドから内容を制御することができるようになるからです。

ただ、optionはbool値に特化した関数で、依存関係を扱えるようになるなど多機能なので、bool値でコマンドラインから制御したいときには使うべきということですね。

詳細はキャッシュ変数というキーワードで扱われるようです。詳しくはここでは扱わないことにします。

if - endif

if文です。

elseもあります。

list(APPEND...)

list(APPEND EXTRA_LIBS MathFunctions)

EXTRA_LIBSというリスト変数にMathFunctionsを追加します。

もしEXTRA_LIBSがない場合は新たにリスト変数が作られます。

ここで作られたリスト変数は参照されたとき展開される特性を持ちます。

つまり、

list(APPEND EXTRA_LIBS MathFunctions1)
list(APPEND EXTRA_LIBS MathFunctions2)

と変数を作って、

target_link_libraries(Foo PUBLIC "${EXTRA_LIBS}")

とすると次のように扱われます。

target_link_libraries(Foo PUBLIC MathFunctions1 MathFunctions2)

cmakedefine

#cmakedefine USE_MYMATH

値を持たない変数に対して、#ifdefを使うために変数を用意するためのコマンド。 configure_fileの対象になる、TutorialConfig.h.inに書く。

CMakeのチュートリアルをやる(Step1)

CMake Tutorial

CMakeをC++プロジェクトなどで使うことは多いのですが、付け焼刃の知識しかなくて正しい書き方がいまだにわからない... なので初心に戻って、CMakeの公式が作ったチュートリアルをやっていこうと思います。

この記事はその時のメモみたいなものです。

参考はこちら。 チュートリアルの元となるプロジェクトファイルたちもここからダウンロードできます。

CMake Tutorial — CMake 3.25.0-rc3 Documentation

Step1: 標準的なプロジェクトの作成

次の要素が含まれている。

  • 自作のヘッダーファイルをインクルードするための設定
  • 実行ファイルの作り方
  • バージョン情報を付加したヘッダーファイルの自動生成

ファイルとしては、上のURLからダウンロードしたCMAKE-x.xx.x-RC3-TUTORIAL_SOURCEのStep1を参照しています。

目的

tutorial.cxxをCMakeLists.txtを使ってコンパイルする。

最小構成:実行ファイルを作る

CMakeLists.txtの最小構成だろうと思われるものを作って、そこから付け加えていく流れでまとめます。

tutorial.cxxをコンパイルしてTutorialという名前の実行ファイルを作るCMakeLists.txtは次の通り:

cmake_minimum_required(VERSION 3.10)

project(Tutorial VERSION 1.0)

add_executable(Tutorial tutorial.cxx)

cmake_minimum_required

cmake_minimum_required(VERSION 3.10) のように必要とされる最小のcmakeバージョンを指定できます。

私のcmakeはバージョン3.16.3だったので、cmake_minimum_required(VERSION 3.17)としてみたら、ちゃんと次のエラーが出た。

CMake Error at CMakeLists.txt:2 (cmake_minimum_required):
  CMake 3.17 or higher is required.  You are running version 3.16.3

project

project(Tutorial VERSION 1.0)のようにプロジェクト名と、バージョン情報を付加できます。

CMakeが自動生成する変数の名前が プロジェクトでつけた名前_...のようになる。また${PROJECT_NAME}でプロジェクト名を参照できます。

バージョン情報はあってもなくてもいいみたいですが、つけておくとTutorial_VERSION_MAJORTutorial_VERSION_MINORという変数が自動生成されて、にそれぞれ1と0が入るみたい。

add_executable

add_executable(Tutorial tutorial.cxx)

コンパイルの対象となるソースファイル(tutorial.cxx)を指定して実行ファイル(Tutorial)を作ります。 ターゲット(生成される実行ファイル名)はプロジェクト名と同じじゃなくてもいい。

機能追加1:C++バージョンの指定

CMake

cmake_minimum_required(VERSION 3.10)

project(Tutorial VERSION 1.0)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

add_executable(Tutorial tutorial.cxx)

CMAKE_CXX_STANDARD CMAKE_CXX_STANDARD_REQUIRED

set(XXX YYY)というコマンドを使うことで、YYYという値を持つXXXという変数を定義することができます。

そして、set(CMAKE_CXX_STANDARD 11)のようにすることで、ビルドに使うC++バージョンを設定できます。

また、set(CMAKE_CXX_STANDARD_REQUIRED True)としておくとC++11が使えない環境でのビルドにエラーを出すことができるようです。

※ 実行ファイルを作るadd_executableより前に指定する必要がある

チュートリアルでは、C++11にしかない機能がコンパイルできるようになることを確認しています。

補足:target_compile_features

上の変数を直接いじる方法以外に、最近のCMakeではtarget_compile_features(Tutorial PUBLIC cxx_std_11)C++バージョンを指定するやり方が主流のようです。

この機能によって、プロジェクトごとではなくターゲットごと(実行ファイルとかライブラリごと)にC++のバージョンを設定できるので使いやすい。

(もしかしてこのチュートリアル古い...???)

このPUBLICについては一旦考えずに次に進めましょう。

機能追加2: プロジェクトのバージョン情報をヘッダファイルからソースファイルへ教示

CMakeLists.txtで定義した変数の情報をソースファイルへ流す機能として、ソースファイルがインクルードするヘッダファイルへバージョン情報を自動で書き込む機能を追加します。

cmake_minimum_required(VERSION 3.10)

project(Tutorial VERSION 1.0)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

configure_file(TutorialConfig.h.in TutorialConfig.h)

add_executable(Tutorial tutorial.cxx)

target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")

configure_file

configure_file(TutorialConfig.h.in TutorialConfig.h)

TutorialConfig.h.inは事前に用意する次のようなファイル。

#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

ここで、@Tutorial_VERSION_MAJOR@など見慣れない書き方がされているが、ここはCMakeによって後で書き直される部分を明示しています。

configure_fileはこのようなヘッダファイルのひな型に対して、現在までに定義された変数を使ってC++のためのヘッダファイルを生成します。

この例では、Tutorial_VERSION_MAJORTutorial_VERSION_MAJORproject(Tutorial VERSION 1.0)コマンドによって1と0になっているので、 次のようなヘッダファイルが生成されると考えることができます。

#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 0

実際、ビルドを走らせるとビルドフォルダ内に、この内容のTutorialConfig.hというファイルが生成されます。

target_include_directories

target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")

一旦、PUBLICという単語は忘れる(後できっと説明される)。

このコマンドによって、add_executableを実行したときに、ソースファイルがヘッダファイルをインクルードするときに探索するディレクトリを指定することができます。

${PROJECT_BINARY_DIR}変数はCMakeが自動生成してくれるもので、ビルドファイルを指し示します。

今回、ヘッダファイルはソースファイルと同じディレクトリではなく、ビルドフォルダ内に自動生成されるので、このような探索するディレクトリを明示する必要があります。

そして、このヘッダファイルをソースファイルからインクルードすれば、バージョン情報をmain関数から参照してprintすることができますね。

#include "TutorialConfig.h"

...
    std::cout << "Version: " << 
        Tutorial_VERSION_MAJOR << "." << Tutorial_VERSION_MINOR << std::endl;
...

【論文雑紹介】Combined Scaling for Zero-shot Transfer Learning

(備忘録です)

概要

[2111.10050] Combined Scaling for Zero-shot Transfer Learning

Googleから出されたこの論文,zero-shot learningの精度を大幅に改善したことで話題になっています.

Zero-shot learningとは,モデルがいままで見たことのないものに対して予測を行うタスクを指します. 言い換えると,推論を行うドメイン(targetドメイン)のデータを一切使わないタスクです.

例えば,猫以外の画像で構成されるデータセットで学習を行い,猫の画像に対して猫であると推定することを目的とする...という感じでしょうか.

人間は,今までに見てきた画像とそのラベルの知識から未知の物体(猫)に対しても,これは生物であり,哺乳類っぽい,とある程度の予測をすることができます.

しかし,機械学習のモデルにしてみればまだまだ難しい分野です. Targetドメインの画像を少ない枚数だけ使う few-shot learning と比較しても,このタスク設定はかなり難しいものであるといえます.

まずはこの論文の結果を,

ImageNetにおけるimage classificationの精度を今までのzero-shot learning手法と比較していますが, 改良の度合いが凄まじいですね...

f:id:shuckle-rbtech:20211203152704p:plain

モデルのパラメータ数などが公平でないですが,zero-shotであるのに見たことのないラベルの80%程度を推定できるという結果には,目を見張るものがあります.

論文では,zero-shot image classificationに有効な表現学習を提案しています.

要約

  • Image-to-textタスクが,image classificationにとって良い表現学習を実現することを示した
  • 3つのスケーリングパラメータ(モデルサイズ,バッチサイズ,データ数)を膨大な数にすることで精度が大幅に改善されることを示した
    • モデルサイズ=3B,バッチサイズ=65536,データセット=6.6Bのnoisy image-textペア(めっちゃ大きい...)
  • 大規模な学習のために,GPU消費を抑えるGradAccumをContrastive Learning用に拡張した

所感

  • スケーリング則に従って,zero-shotのimage classificationでも80%以上の精度が達成されたことは素直にすごい
  • GradAccumを使っているがいまだ,数千Cores×Days(コア数×学習時間)かかっている(つまり1コアGPUで1000日とか) これはGoogleじゃなきゃできないので...もう少し現実的な学習方法を開発するか,主要なタスクについてはPretrainingのパラメータを公開してほしい
  • image-to-textによる表現学習が有用であることは実用上有効かもしれない
    • 学習データセットがnoisyであるのにもかかわらず,image classificationに有用な特徴学習ができているので,SNSの投稿などの大規模データを活用できるかもしれない

文献情報

  • Pham, Hieu, et al. "Combined Scaling for Zero-shot Transfer Learning." arXiv preprint arXiv:2111.10050 (2021).

情報幾何学のすゝめ(参考書おすすめ)

情報幾何学という分野に興味が出てきて最近勉強しているので 使っている参考書の軽い紹介でもしようと思います.

情報幾何学の新展開

こちらは,情報幾何学の創設者であり第一線の研究者の甘利俊一先生著の参考書であります.

まず導入として,情報幾何が対象とする「多様体」とは何であるか, ニューラルネットワークを含む様々な例を通して説明しています.

続く章では確率分布の空間を主に考え,確率分布のようなよくわからないものでも,情報幾何学という武器を使えば 直観的な幾何学として最適化できます,といったことを丁寧に説明しています.


感想

この本は全体を通して,情報幾何学直観的に要約して説明しています. 多様体を含むいろいろな厳密な定義を取り払って説明しているので,一見すると抽象的で難しい説明なのですが,じっくり読むと情報幾何学とは本質的に何かというイメージを理解する足掛かりとなってくれます.

一般的に言えることですが,このような要約というか直観的な理解はある程度基礎を学んでからやっとわかってくるものです. しかし,この本ではそのような説明をある種天下り的に説明してくれているので,大変有用な参考書だと思いました.

おすすめです.

情報幾何学の基礎

こちらは,情報幾何を多様体の定義から丁寧に説明している参考書です. 対象範囲は確率分布の幾何学について詳細な説明まで及んでいます.

リンク先は2021年発売で新しく見えますが,2015年に発行された本を別の出版社が発行したもののようです.

↓古い方

感想

こちらの本は先ほどの「情報幾何学の新展開」に比べると,多様体の定義から丁寧に説明しています.

多様体を一から学習しようとするとかなりの時間を取られますが,この本では多様体などの説明を必要最低限にとどめていてとても読みやすいです. 定義などもある種天下り的にまとめて定義されているので,必要な知識をぱっぱっぱと得ることができるのでおすすめです.


結論

最初の「情報幾何学の新展開」を先に読んでなんとなく情報幾何学の雰囲気を理解して,「情報幾何学の基礎」を読んで理解を深める読み方が入門としていいのかなと思いました.

状態推定のためのLie群上の最適化4

前回の投稿からだいぶ時間が空いてしまいました.

今回は、Lie群上の最適化でとても重要な要素となる、導関数(Jacobian)についてまとめます.

実数空間におけるJacobian

復習として実数空間で定義される関数のJacobian(ヤコビ行列)についてみていきましょう.

ベクトル入力ベクトル出力の関数 f: \mathbb{R}^m \rightarrow \mathbb{R}^nについて,Jacobianは次のように定義されます.

 \begin{align}
\displaystyle \textbf{J} = \displaystyle \frac{\partial f({\boldsymbol x})}{\partial {\boldsymbol x}} \triangleq \begin{bmatrix}
\frac{\partial f_1}{\partial x_1} & \cdots & \frac{\partial f_1}{\partial x_m} \\
\vdots & & \vdots \\
\frac{\partial f_n}{\partial x_1} & \cdots & \frac{\partial f_n}{\partial x_m}
\end{bmatrix} \in \mathbb{R}^{n \times m}
\end{align}

ここで,各列  \begin{array}\displaystyle \textbf{j}_i = \left[\frac{\partial f_1}{\partial x_i} \cdots \frac{\partial f_n}{\partial x_i}\right]^\top \end{array} は次のように定義されます( {\boldsymbol e}_i \mathbb{R}^nにおける i番目の自然な基底.)

 \begin{align}  \label{2}
\displaystyle \textbf{j}_i = \displaystyle \frac{\partial f(\boldsymbol x)}{\partial x_i} \triangleq \lim_{h \rightarrow 0} \frac{f({\boldsymbol x} + h {\boldsymbol e}_i) - f({\boldsymbol x})}{h} \in \mathbb{R}^n 
\end{align}

つまりJacobianは,定義域の変数の各基底方向への微小な移動に対する変化率(ベクトル)を横方向に並べたもの,と捉えることができます.

元解説論文では,次のような演算を定義してJacobianの定義を簡潔にしています.

 \begin{align}
\displaystyle \textbf{J} = \displaystyle \frac{\partial f(\boldsymbol x)}{\partial {\boldsymbol x}} \triangleq \lim_{{\boldsymbol h} \rightarrow {\boldsymbol 0}} \frac{f({\boldsymbol x} + {\boldsymbol h}) - f({\boldsymbol x})}{{\boldsymbol h}} \in \mathbb{R}^{n \times m} \label{1}
\end{align}

ベクトルによる割り算なんて定義していないのですが,「分母の要素ごとに分子の要素を割って並べる」という操作とします.


Jacobianは直観的に,「変数 \boldsymbol{x}の微小移動を,関数 fの微小移動に変換する線形写像」,と考えることができます. 次の一次近似はその線形写像を表しています.

 \begin{align}
\ f({\boldsymbol x} + {\boldsymbol h}) -  f({\boldsymbol x}) \rightarrow \textbf{J}{\boldsymbol h},\ \ as\ {\boldsymbol h} \rightarrow 0,
\end{align}

Lie群上のRight Jacobian

上のJacobianは,実数空間上の関数値の微小変化率という形で定義されています.

前の話で,定義したLie群の要素間の和と差  \oplus,  \ominusを定義しました. このLie群上の演算を使うと,Lie群からLie群への写像 f: \mathcal{M} \rightarrow \mathcal{N}の,Jacobianを考えることができます. つまり,あるLie群上の変数(例えば回転)を別のLie群上(これはユークリッド空間( \in Lie群)でもいい)に移す関数のJacobianを考えることができるということです.

Right Jacobianの定義

まずは,right-  \{\oplus, \ominus\}を用いて,点 \mathcal{X} \in \mathcal{M}における Jacobianを次のように定義します.

 \begin{eqnarray}
\ \displaystyle \frac{^{\mathcal{X}} D f(\mathcal{X})}{D\mathcal{X}} &\triangleq& \displaystyle \lim_{\boldsymbol{\tau} \rightarrow 0} \displaystyle \frac{f(\mathcal{X} \oplus \boldsymbol{\tau}) \ominus f(\mathcal{X})}{\boldsymbol{\tau}} \in \mathbb{R}^{n\times m} \\
&=& \displaystyle \lim_{\boldsymbol{\tau} \rightarrow 0} \frac{\mathrm{Log}\left(f(\mathcal{X})^{-1} \circ f(\mathcal{X} \circ \mathrm{Exp}(\boldsymbol{\tau}))\right)}{\boldsymbol \tau} \\
&=& \left.\frac{\partial \mathrm{Log} \left(f(\mathcal{X})^{-1} \circ f(\mathcal{X} \circ \mathrm{Exp}(\boldsymbol \tau))\right)}{\partial {\boldsymbol \tau}}\right|_{\boldsymbol \tau = 0}
\end{eqnarray}

これを「fの点 \mathcal{X} \in \mathcal{M}におけるright Jacobian」と呼ぶことにします.

さて,これを式\eqref{2}のように説明変数の次元ごとのベクトルとして表現すると,次のようになります.  \boldsymbol{\sigma}_i(h)が,式\eqref{2}における \mathbf{j}_iの分子に対応しています.

 \begin{align} 
\boldsymbol{\sigma}_i (h) = f({\mathcal{X}} \oplus h {\boldsymbol e}_i) \ominus f(\mathcal{X}) \in \mathbb{R}^n
\end{align}
Right Jacobianの直観的な理解

この導関数の直観的な理解は 「点 \mathcal{X}の微小移動に対する,関数 fの変化度合い」 です.


ここで「接空間」について思い出してみましょう.接空間はある点における速度ベクトルがなす空間を指していました.

ならば,点 \mathcal{X}における微小移動も,関数 fの変化の度合い(言い換えれば f(\mathcal{X})における微小移動)も それぞれ \mathcal{X} f(\mathcal{X})で定義された接空間に属する要素であることが分かります.

つまり,(Right) Jacobianとは点 \mathcal{X} \in \mathcal{M}における接空間  T_{\mathcal X} \mathcal{M}から, f(\mathcal{X}) \in \mathcal{N}における接空間  T_{f(\mathcal{X})} \mathcal{N}への写像であるといえます.


もっと言えば,この写像は行列で表されるので線形写像となります. 実際,次のような一次近似が成り立ち,これはまさにそれぞれの空間における接空間同士の写像を表しています.

 \begin{align}
\ f(\mathcal{X} \oplus\ ^{\mathcal{X}} {\boldsymbol \tau}) \ominus  f({\mathcal x}) \rightarrow \frac{^{\mathcal{X}} D f(\mathcal{X})}{D\mathcal{X}}\ ^{\mathcal{X}} \boldsymbol{\tau},\ \ as\ ^{\mathcal{X}} \boldsymbol{\tau} \rightarrow 0,
\end{align}

ここで \ ^\mathcal{X}\boldsymbol{\tau}は点 \mathcal{X}における微小移動です. 点 \mathcal{X}によってこの接空間は変わってしまうので,一般的に T_{\mathcal{X}}\mathcal{M}のような接空間は,localな接空間と呼ばれます.

Lie群上のLeft Jacobian

Right Jacobianではright- \{\oplus,\ominus\}でJacobianを定義しました. 同様にleft- \{\oplus,\ominus\}を使ってleft Jacobianも定義することができます.

Left Jacobianの定義
 \begin{eqnarray}
\ \displaystyle \frac{^{\epsilon} D f(\mathcal{X})}{D\mathcal{X}} &\triangleq& \displaystyle \lim_{\boldsymbol{\tau} \rightarrow 0} \displaystyle \frac{f(\boldsymbol{\tau} \oplus \mathcal{X}) \ominus f(\mathcal{X})}{\boldsymbol{\tau}} \in \mathbb{R}^{n\times m} \\
&=& \displaystyle \lim_{\boldsymbol{\tau} \rightarrow 0} \frac{\mathrm{Log}\left(f( \mathrm{Exp}(\boldsymbol{\tau}) \circ  \mathcal{X})  \circ f(\mathcal{X})^{-1}\right)}{\boldsymbol \tau} \\
&=& \left.\frac{\partial \mathrm{Log} \left(f(\mathrm{Exp}(\boldsymbol{\tau}) \circ  \mathcal{X})  \circ f(\mathcal{X})^{-1}\right)}{\partial {\boldsymbol \tau}}\right|_{\boldsymbol \tau = 0}
\end{eqnarray}
Left Jacobianの直観的な理解

ここで, \boldsymbol{\tau} \in T_{\epsilon} \mathcal{M}であり,分子が T_{\epsilon} \mathcal{N}に属することに注意します. このことから,left Jacobianはglobalな接空間を写像する線形写像(行列)となります. 言い換えると, \mathcal{M} \mathcal{N}のLie代数を写像する行列を示すことになります.

Right Jacobianの時と同様に,一次近似が次のように計算できます.

 \begin{align}
\ f(\ ^{\epsilon} {\boldsymbol \tau}  \oplus \mathcal{X})  \ominus f({\mathcal x}) \rightarrow \frac{^{\epsilon} D f(\mathcal{X})}{D\mathcal{X}}\ ^{\epsilon} \boldsymbol{\tau},\ \ as\ ^{\epsilon} \boldsymbol{\tau} \rightarrow 0,
\end{align}

Crossed right-left Jacobian

上のようなright, leftそれぞれの \oplus,  \ominusで定義された{Right, Left} Jacobian.それぞれ写像する接空間の定義がlocal であるか globalであるかの違いがありました.

ところで,前の章でlocalな接空間とglobalな接空間を変換する演算としてAdjoint(随伴行列)を導入しました. この演算を使うと,それぞれのJacobianを相互に次のように変換することができるようになります.

 \begin{align}
\frac{^{\epsilon} D f(\mathcal{X})}{D\mathcal{X}} \mathbf{Ad}_{\mathcal X} = \mathbf{Ad}_{f(\mathcal X)} \frac{^{\mathcal{X}} D f(\mathcal{X})}{D\mathcal{X}}
\end{align}

このような変換は,いずれ状態変数の最適化を考える上で問題を簡単にするのに大変役立っていくので,覚えておいて損はないと思います.