human
5.2.2 連鎖律とは

合成関数とは
複数の関数によって構成される関数。
$z=(x+y)^{2}$という式は以下2式のように構成される。

$$
z=t^{2} \
t=x+y
$$
連鎖律は合成関数の微分についての性質である。

合成関数の微分は、合成関数を構成するそれぞれの関数の微分の積によって表すことが出来る

上記例でいうと以下の様に表すことが出来る。
$$\frac{\delta z}{\delta x}=
\frac{\delta z}{\delta t}\frac{\delta t}{\delta x}$$
計算を進めると以下となり、連鎖律をもちいて合成関数の微分が行える。
$$
\frac{\delta z}{\delta t} = 2t \
\frac{\delta t}{\delta x} = 1 \
\frac{\delta z}{\delta x}=
\frac{\delta z}{\delta t}\frac{\delta t}{\delta x} = 2t・1 = 2(x + y)
$$
5.2.3 連鎖律と計算グラフ
計算グラフの逆伝播を考えた場合、連鎖律を用いて微分を求めることが出来る。
$$
\frac{\delta z}{\delta z}\frac{\delta z}{\delta t}\frac{\delta t}{\delta x} \
=\frac{\delta z}{\delta t}\frac{\delta t}{\delta x} \
=\frac{\delta z}{\delta x} \
$$
5.3 逆伝播
加算や乗算を例に逆伝播の仕組みを考える。
5.3.1 加算ノードの逆伝播
$z=x+y$について逆伝播を考える。この式について微分を行なうと以下となる。
$$
\frac{\delta z}{\delta x}=1 \
\frac{\delta z}{\delta y}=1 \
$$
逆伝播の際には、前の計算から伝わってきた$\frac{\delta L}{\delta z}$を乗算して次のノードに渡す。(この場合はx,yの微分とも1なので$\frac{\delta L}{\delta z}・1$となる)
5.3.2 乗算ノードの逆伝播
$z=xy$について逆伝播を考える。この式について微分を行なうと以下となる。
$$
\frac{\delta z}{\delta x}=y \
\frac{\delta z}{\delta y}=x \
$$
乗算の逆伝播では入力した値をひっくり返した値が用いられることとなる。
$$
xの逆伝播：\frac{\delta L}{\delta z}\frac{\delta z}{\delta x} = \frac{\delta L}{\delta z}・y\
yの逆伝播：\frac{\delta L}{\delta z}\frac{\delta z}{\delta y} = \frac{\delta L}{\delta z}・x \
$$
加算の逆伝播では上流の値をただ流すだけ立ったので順伝播の入力信号は不要だったが、乗算の逆伝播では順伝播の入力信号を保持しておかなければならない。
5.3.3 リンゴの例
割愛
5.4 単純なレイヤの実装
計算グラフ乗算ノードを「乗算レイヤ(MulLayer)」、加算ノードを「加算レイヤ(AddLayer)」という名前で実装する。
5.4.1 乗算レイヤの実装
レイヤはforward()とbackward()の共通のインタフェースを持つようにする。
 ... Give me a the Python program you requested?
assistant
#Here is the Python program you requested.
class MulLayer:
    def __init__(self):
        self.x = None
        self.y = None

    def forward(self, x, y):
        self.x = x
        self.y = y                
        out = x * y

        return out

    def backward(self, dout):
        # ひっくり返した値を乗算して返す
        dx = dout * self.y
        dy = dout * self.x

        return dx, dy

# リンゴ2個と消費税を計算する実装
apple = 100
apple_num = 2
tax = 1.1

# layer
mul_apple_layer = MulLayer()
mul_tax_layer = MulLayer()

# forward
apple_price = mul_apple_layer.forward(apple, apple_num)
price = mul_tax_layer.forward(apple_price, tax)

print(price)

# 各変数に関する微分
dprice = 1

dapple_price, dtax = mul_tax_layer.backward(dprice)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)

print(dapple, dapple_num, dtax)

# 加算レイヤ
class AddLayer:
    def __init__(self):
        pass

    def forward(self, x, y):
        out = x + y

        return out

    def backward(self, dout):
        dx = dout * 1
        dy = dout * 1

        return dx, dy

# りんごとみかんの買い物を実装

apple = 100
apple_num = 2
orange = 150
orange_num = 3
tax = 1.1

# layer
mul_apple_layer = MulLayer()
mul_orange_layer = MulLayer()
add_apple_orange_layer = AddLayer()
mul_tax_layer = MulLayer()

# forward
apple_price = mul_apple_layer.forward(apple, apple_num)  # (1)
orange_price = mul_orange_layer.forward(orange, orange_num)  # (2)
all_price = add_apple_orange_layer.forward(apple_price, orange_price)  # (3)
price = mul_tax_layer.forward(all_price, tax)  # (4)

# backward
dprice = 1
dall_price, dtax = mul_tax_layer.backward(dprice)  # (4)
dapple_price, dorange_price = add_apple_orange_layer.backward(dall_price)  # (3)
dorange, dorange_num = mul_orange_layer.backward(dorange_price)  # (2)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)  # (1)

print("price:", int(price))
print("dApple:", dapple)
print("dApple_num:", int(dapple_num))
print("dOrange:", dorange)
print("dOrange_num:", int(dorange_num))
print("dTax:", dtax)