Closed uchihashikenshi closed 4 years ago
@shigeyukioba 微分のところの設計がよくわかっていなくて、方針誤りないか確認お願いしたいです🙏
ここは微分の計算をしていなくて、なにか関数を与えたら微分できるクラスを用意して関数は実行時に与えるのが良いと思っています。どうでしょう?
@uchihashikenshi 自分は、MPCEnvのdynamics部分を実装した時に
こんな感じでautogradのjacobian()を使ってみています。
(import numpy as np
を import autograd.numpy as np
に置き換えて利用)
例えば、
import autograd.numpy as np
from autograd import jacobian
class SampleFn(object):
def __init__(self):
self.A = np.array([[1,2,1],
[1,1,1],
[0,0,1]], dtype=np.float32)
# value()の0番目の引数であるxに関するjacobianの計算をする関数
self.x = jacobian(self.value, 0)
def value(self, x):
return np.dot(self.A, x)
fn = SampleFn()
x = np.array([0,1,2], dtype=np.float32)
v0 = fn.value(x)
# (4,3,1)
print(v0)
j0 = fn.x(x)
print(j0)
# Aと同じものになる
こんな感じでvalue()
を定義するだけでいいようになります。
この例だとx
のshapeが(3,)
で、Fnの仕様の(3,1)
ではないのでちょっと修正が必要ですが、割とすっきりかけるかな?と。
params という形になっているのは、
ここは
return np.dot(self.params["A"], x)
しないといけないと思ったけどそうではない・・・?
解説ノート上で x は縦ベクトルなのですが、昨年までの実装上では x を横ベクトルにしていました。
@shigeyukioba
そうなると微分をとった時 A
と同じものが返ってこないと思ったんですが、この挙動って正しいんですかね・・・?
現在観測モデルを定義する際、
のようにしているが、params
が他に使われているということもないので単に行列を入力としたほうがわかりやすそう。
@uchihashikenshi ちょっと質問の意味が正確にわからないので、
微分をとった時
A
と同じものが返ってくる
の箇所を test にしてもらえませんか。
コード全般を、 x が縦ベクトルとなるように書き換える必要があるのかもしれない。 局所的に A を転置することで済むのかもしれない です。
それから、 params を使わずに行列を入力とする点は分かりやすさのためにそのほうがよいと思います。ありがとうございます。
@shigeyukioba 僕が言いたかったのは↓のような話ですね。
import autograd.numpy as np
from autograd import jacobian
class LinearFn(object):
"""Linear function y = np.dot( x, A ) and its derivatives.
"""
def __init__(self):
self.A = np.array([[5, 1, 1],[1, 2, 3],[4, 5, 6]], dtype=np.float32)
self.dxA = jacobian(self.value_xA, 0)
self.dAx = jacobian(self.value_Ax, 0)
def value_xA(self, x):
return np.dot(x, self.A)
def value_Ax(self, x):
return np.dot(self.A, x)
if __name__ == "__main__":
lfn = LinearFn()
x1 = np.array([3, 1, 1], dtype=np.float32)
x2 = np.array([3, 1, 1], dtype=np.float32)
xA = lfn.value_xA(x1)
Ax = lfn.value_Ax(x2)
print(xA)
# [20. 10. 12.]
print(Ax)
# [17. 8. 23.]
dxA = lfn.dxA(xA)
dAx = lfn.dAx(Ax)
print(dAx)
# [[5. 1. 1.]
# [1. 2. 3.]
# [4. 5. 6.]]
print(dxA)
# [[5. 1. 4.]
# [1. 2. 5.]
# [1. 3. 6.]]
print(dxA.T)
# [[5. 1. 1.]
# [1. 2. 3.]
# [4. 5. 6.]]
この xA
側の挙動が正しくないように感じたのですが、自信がなかったので確認とった次第です。
@uchihashikenshi 仕様上でどちらを正しいことにするか、迷いがあったのですが、 今後
x1 = np.array([3, 1, 1], dtype=np.float32)
を縦ベクトル扱いにすることにします。 ドキュメント上の数式ではそのようにしていました。
そこで、この箇所は
np.dot(self.A, x)
が正しいことになりますが、他の箇所に影響が出るかもしれません。
わかりました! 影響箇所含めて修正しておきます。
$ python tests/test_fn.py
.....
----------------------------------------------------------------------
Ran 5 tests in 0.035s
OK
test_fn.py を実装する過程で、従来の np.dot(x, A)
ではなく np.dot(A, x)
を正しいものとする仕様変更を行った。
ekf.py
はじめ複数箇所に影響が出ると思われ、それらへの対応もこちらの issue に紐付ける。
$ python -m unittest discover tests
..................
----------------------------------------------------------------------
Ran 18 tests in 0.125s
OK
目的
fn.py
内のテストを分離し、動かせる状態にする提案内容
/tests
以下にtest_fn.py
を作り、のような形でテストできるようにする。 IO は以下。
タスク
関連