hirohirohirohirosのブログ

地方国立大学に通う情報系学部4年

ゼロから作るDeep Learning 3 ステップ5~ステップ10 まとめ

hirohirohirohiros.hatenablog.com

ステップ7

assert

 assert文は,条件を書いて条件がFalseの時例外を出します.プログラムの条件をテストする目的で使われます.if文で書くよりスマートに書けます.実際にテストしてみます.

A = Square()
B = Exp()
C = Square()

x = Variable(np.array(0.5))
a = A(x)
b = B(a)
y = C(b)

assert y.creator == C
assert y.creator.input == b
assert y.creator.input.creator == A

>> Traceback (most recent call last):
>>  File "step1.py", line 64, in <module>
>>    assert y.creator.input.creator == A
>> AssertionError

 わざと間違っているassert文を用意しましたが,ちゃんとAssertionErrorと表示されました.ただし,以下のコードでは,

assert y.creator == C
assert y.creator.input == b
assert y.creator.input.creator == A
assert y.creator.input.creator.input == b

>>Traceback (most recent call last):
>> File "step1.py", line 64, in <module>
>>   assert y.creator.input.creator == A
>>AssertionError

assertの3行目と4行目が間違っていますが,エラーが出るところは3行目のみです(当たり前ですが).assertで例外が発生したとき,それ以降のassertはまだ確認出来ていないことに注意する必要があります.

ステップ9

0次元のndarrayの挙動
x = np.array(1)
y = x*2
print(type(x), x)
print(type(y), y)
>> <class 'numpy.ndarray'> 1
>> <class 'numpy.int32'> 2

 0次元のndarrayに演算を行うと型がndarrayから変わってしまいます.(そもそもndarrayに0次元を取れるということを初めて知った気がします)

np.isscalar

 引数がスカラーの時Trueを返す関数がnp.isscalarです.今回のようにndarrayしか取りたくない場合の判定に使えそうです.

print(np.isscalar(1))
print(np.isscalar(np.float(2.0)))
print(np.isscalar(np.array(1)))
print(np.isscalar(np.array([1])))
print(np.isscalar("a"))
>>True
>>True
>>False
>>False
>>True

 文字列のような関係ない型でもTrueとなるのでそこは注意が必要そうです.

ステップ10

np.allclose

 二つの値が一致しているかを判別するには==が用いられますが,floatの時は計算誤差がつきまとうので注意が要ります.

0.3*3 == 0.9
>> False

 完全に一致しているのでは無く,ある程度の誤差は許容して一致しているかを判定するのがnp.iscloseです.
 np.isclose(a, b)とすると,|a-b| <= atol + rtol*|b| が成り立つときTrueを返します.デフォルトではatol=10^-8, rtol=10^-5です.
 これを使うと

np.isclose(0.3*3, 0.9)
>> True

となり,誤差を許容して一致している事が確認出来ます.許容する誤差の程度はatol, rtolで調整するということです.

テスト

 unittestという標準ライブラリを使う事でソフトウェアのテストが簡単にできます.

import unittest

class SquareTest(unittest.TestCase):
    def test_forward(self):
        x = Variable(np.array(2.0))
        y = square(x)
        expected = np.array(4.0)
        self.assertEqual(y.data, expected)

 unittest.TestCaseを継承したクラスを作成し,テストしたい内容を書きます.その時メゾットの名前はtestで始まらないとテスト内容と認識しないので注意が必要です.
 実行時は-m unittestという引数を取ることでテストモードになります.成功すると

----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

となり,失敗すると

self.assertEqual(y.data, expected)
AssertionError: array(4.) != array(5.)

----------------------------------------------------------------------
Ran 1 test in 0.005s

FAILED (failures=1)

となります.分かりやすいですね.
 複数個テストするメゾットを用意しても問題ありません.全て成功すると

...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

2つ失敗すると

FF.
======================================================================
FAIL: test_backward (test.SquareTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\***\Desktop\self-dev\dezero\test.py", line 16, in test_backward
    self.assertEqual(y.data, expected)
AssertionError: array(4.) != array(6.)

======================================================================
FAIL: test_forward (test.SquareTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\***\Desktop\self-dev\dezero\test.py", line 9, in test_forward
    self.assertEqual(y.data, expected)
AssertionError: array(4.) != array(5.)

----------------------------------------------------------------------
Ran 3 tests in 0.002s

FAILED (failures=2)

となります.どこのテストケースで失敗したのかも分かります.
 また,引数に-vを入れ,python -m unittest -v test.py とすることで,どのテストケースで失敗したのか冗長的に表示されます.

test_backward (test.SquareTest) ... FAIL
test_forward (test.SquareTest) ... FAIL
test_gradient_check (test.SquareTest) ... ok

...