

斎藤康毅著「ゼロから作るDeep Learning」(オライリー・ジャパン)でP.162の【5.7.4
誤差逆伝搬法を使った学習】項、以下のプログラムコードのパラメーター更新部分に関してです。

[code]

    for key in ('W1', 'b1', 'W2', 'b2'): 
    
        network.params[key] -= learning_rate * grads[key]
    
[/code]

の部分について

[code]

    network.params[key] -= learning_rate * grads[key]
    
[/code]

を

[code]

    network.params[key] = network.params[key] - learning_rate * grads[key]
    
[/code]

としてしまうとうまく学習できずに正解率も一定となってしまいます。

自分なりに調べて考えた結果、-=とした時とは異なり元々生成したTwoLayerNetオブジェクトが持つparamsプロパティとは全く違うアドレスに保存されているオブジェクトを繰り返し作り出し、それを更新する事になる為、このコードの後で算出する正解率がおかしくなってしまうのではないかとざっくり考えております。

しかし、これが見当違いなのかどうかさえ分からずスタックしています。  
もしこちらの書籍を学習済みでお分かりになる方がいらっしゃいましたら教えて頂けると助かります。

追記

ご回答頂いた内容を考慮しながら考え,

TwoLayerNetを生成した時に生成したnet.params[パラメータ]のアドレスをいつも参照する為、代入演算子を使用する事でアドレスを変更しないようにしている、

事が判明したのですが何故net.params[パラメータ]のアドレスをいつも参照するのか理由がわかりません。

再現できるか簡単なプログラムで検証しましたが、この場合予想通り累積演算子でアドレスが変わってもtestクラスのインスタンスプロパティself.aも同じアドレスを参照していました。

[code]

    import numpy as np
    
    class test:
        
        def __init__(self, b):
            
            self.a = b
            
        def in_instance(self):
            print(id(self.a))
    
            
    input_b = np.array([1, 2, 3])
    added_c = np.array([2, 3, 4])
    
    
    test = test(input_b)
    
    print(id(test.a))
    test.a = test.a + added_c
    # test.a += added_c
    print(id(test.a))
    
    test.in_instance()
    
[/code]

という事はこのtestクラスの中で別クラスのインスタンスを生成する事をこの本のコードではしているので、  
a(TwoLayerNet)クラスの中でaクラスがもつプロパティを引数としてb(AffineやRelu)クラスのインスタンスを生成すると、その引数はいつも元々aクラスがもつプロパティのアドレスを参照するように保持されるという事になりそうです。  
だからTwoLayerNetを生成した時に生成したnet.params[パラメータ]のアドレスが変わってしまうとパラメータが更新されずに勾配が一定になってしまう。  
これはクロージャのクラス版みたいなものなのでしょうか。


CC BY-SA 4.0
2022-01-13T11:41:04.243
85668
2022-01-28T07:55:34.207
2022-01-28T07:55:34.207
50671
50671
question
python
ゼロから作るDeep Learning パラメーター更新コードについて


>
> 自分なりに調べて考えた結果、-=とした時とは異なり元々生成したTwoLayerNetオブジェクトが持つparamsプロパティとは全く違うアドレスに保存されているオブジェクトを繰り返し作り出し、それを更新する事になる為、このコードの後で算出する正解率がおかしくなってしまうのではないかとざっくり考えております。

間違っていないと思います。

【累算代入版】

[code]

    network.params[key] -= learning_rate * grad[key]
    
    
[/code]

と  
【減算後、代入版】

[code]

    network.params[key] = network.params[key] - learning_rate * grad[key]
    
    
[/code]

の違いを調べてみました。  
この計算結果はどちらも同じでした。

しかし、keyが'b1'のとき、network.layers["Affine1"].bの値が異なっていました。  
network.layers["Affine1"].bの算出にnetwork.params['W1']とnetwork.params['b1']を参照していますが、これは【減算後、代入版】の代入前のものです。

代入前の値は更新されませんので、何回計算を繰り返しても、network.layers["Affine1"].bも更新されません。他のnetwork.params[key]も同様です。

意図どおりの結果とならないのは以上が理由だと思います。

* * *

調査にしたコードは以下です。  
<https://github.com/oreilly-japan/deep-learning-from-
scratch/blob/master/ch05/>

* * *

【追記】

> これはクロージャのクラス版みたいなものなのでしょうか。

クロージャで説明するようなことではないと思います。もっと単純な話だと思います。

次のコードでは`a += 1`で`a`自体が変化していますので、aそのものであるbも変化しています。※bが変化しているというよりも、bは変化したaそのもの

[code]

    import numpy as np
    a = np.array([1, 2, 3])
    print(a)    #[1 2 3]
    b = a
    a += 1
    print(b)    #[2 3 4]
    
[/code]

次のコードでは`a = a +
1`で代入前のa自身は変化していません。代入前のaの参照を保持しているbも変化していません、bは演算前のaそのものだからです。

[code]

    import numpy as np
    a = np.array([1, 2, 3])
    print(a)    #[1 2 3]
    b = a
    a = a + 1
    print(b)    #[1 2 3]
    
[/code]

`a = a + 1`の`a + 1`でaとば別の新しいオブジェクトが生成され、代入で左辺のaは演算前とは別のオブジェクトを示すことになりました。  
演算前のaのオブジェクトを参照している変数bは`a + 1`とは無関係です。


CC BY-SA 4.0
2022-01-16T11:39:09.220
85737
2022-01-23T01:13:54.020
2022-01-23T01:13:54.020
35558
35558
85668
answer