shigeyukioba / matchernet

Apache License 2.0
1 stars 1 forks source link

fn のテストを動かす #18

Closed uchihashikenshi closed 4 years ago

uchihashikenshi commented 5 years ago

目的

fn.py 内のテストを分離し、動かせる状態にする

提案内容

/tests 以下に test_fn.py を作り、

if __name__ == "__main__":
    unittest.main()

のような形でテストできるようにする。 IO は以下。

$ python tests/test_fn.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

タスク

関連

uchihashikenshi commented 5 years ago

@shigeyukioba 微分のところの設計がよくわかっていなくて、方針誤りないか確認お願いしたいです🙏

https://github.com/shigeyukioba/matchernet/blob/3deb2f2cef77f73977c344ebba7902490dfff86f/matchernet_py_001/fn.py#L51-L52

ここは微分の計算をしていなくて、なにか関数を与えたら微分できるクラスを用意して関数は実行時に与えるのが良いと思っています。どうでしょう?

miyosuda commented 5 years ago

@uchihashikenshi 自分は、MPCEnvのdynamics部分を実装した時に

https://github.com/shigeyukioba/matchernet/blob/d707ce84aa8a045c7813bfed1b3be98e0c98686a/demo/car_control/car.py#L26-L28

こんな感じでautogradのjacobian()を使ってみています。 (import numpy as npimport 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)ではないのでちょっと修正が必要ですが、割とすっきりかけるかな?と。

uchihashikenshi commented 4 years ago

params という形になっているのは、

uchihashikenshi commented 4 years ago

https://github.com/shigeyukioba/matchernet/blob/3deb2f2cef77f73977c344ebba7902490dfff86f/matchernet_py_001/fn.py#L48-L49

ここは

return np.dot(self.params["A"], x)

しないといけないと思ったけどそうではない・・・?

shigeyukioba commented 4 years ago

解説ノート上で x は縦ベクトルなのですが、昨年までの実装上では x を横ベクトルにしていました。

uchihashikenshi commented 4 years ago

@shigeyukioba そうなると微分をとった時 A と同じものが返ってこないと思ったんですが、この挙動って正しいんですかね・・・?

uchihashikenshi commented 4 years ago

現在観測モデルを定義する際、

https://github.com/shigeyukioba/matchernet/blob/3deb2f2cef77f73977c344ebba7902490dfff86f/matchernet_py_001/ekf.py#L237-L239

のようにしているが、params が他に使われているということもないので単に行列を入力としたほうがわかりやすそう。

shigeyukioba commented 4 years ago

@uchihashikenshi ちょっと質問の意味が正確にわからないので、

微分をとった時 A と同じものが返ってくる

の箇所を test にしてもらえませんか。

コード全般を、 x が縦ベクトルとなるように書き換える必要があるのかもしれない。 局所的に A を転置することで済むのかもしれない です。

それから、 params を使わずに行列を入力とする点は分かりやすさのためにそのほうがよいと思います。ありがとうございます。

uchihashikenshi commented 4 years ago

@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 側の挙動が正しくないように感じたのですが、自信がなかったので確認とった次第です。

shigeyukioba commented 4 years ago

@uchihashikenshi 仕様上でどちらを正しいことにするか、迷いがあったのですが、 今後

x1 = np.array([3, 1, 1], dtype=np.float32)

を縦ベクトル扱いにすることにします。 ドキュメント上の数式ではそのようにしていました。

そこで、この箇所は

np.dot(self.A, x)

が正しいことになりますが、他の箇所に影響が出るかもしれません。

uchihashikenshi commented 4 years ago

わかりました! 影響箇所含めて修正しておきます。

uchihashikenshi commented 4 years ago
$ python tests/test_fn.py
.....
----------------------------------------------------------------------
Ran 5 tests in 0.035s

OK
uchihashikenshi commented 4 years ago

test_fn.py を実装する過程で、従来の np.dot(x, A) ではなく np.dot(A, x) を正しいものとする仕様変更を行った。 ekf.py はじめ複数箇所に影響が出ると思われ、それらへの対応もこちらの issue に紐付ける。

uchihashikenshi commented 4 years ago
$ python -m unittest discover tests
..................
----------------------------------------------------------------------
Ran 18 tests in 0.125s

OK