Open In Colab

ステップ3 関数の連結

前ステップまでに実装したコード

[1]:
import numpy as np


class Variable:
    def __init__(self, data):
        self.data = data


class Function:
    def __call__(self, input):
        x = input.data
        y = self.forward(x)
        output = Variable(y)
        return output

    def forward(self, x):
        raise NotImplementedError()


class Square(Function):
    def forward(self, x):
        return x ** 2

ここまで私たちは、DeZeroの「変数」と「関数」を作ってきました。そして、前ステップでは、Squareという2乗の計算を行う関数クラスを実装しました。本ステップでは、別の新しい関数を実装し、複数の関数を組み合わせて計算を行います。

3.1 Exp関数の実装

まずは、DeZeroの新しい関数を1つ実装します。ここでは、\(y = e^x\)という計算を実装します(\(e\)はネイピア数で、具体的には\(e=2.718...\)という値です)。早速そのコードを実装しましょう。

[2]:
class Exp(Function):
    def forward(self, x):
        return np.exp(x)


Squareクラスのときと同様に、Functionクラスを継承してforwardメソッドに目的の計算を実装します。Squareクラスと異なるのは、forwardメソッドの中身がx ** 2からnp.exp(x)に変わった点だけです。

3.2 関数を連結する

Functionクラスの__call__メソッドの入力と出力は、ともにVariableインスタンスです。そのため、DeZeroの関数を連続して使用することは自然にできます。たとえば、\(y = (e^{x^2})^2\)という計算を考えてみましょう。その場合、次のようなコードを書くことができます。

[3]:
A = Square()
B = Exp()
C = Square()

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


ここでは、3つの関数――ABC――を連続して適用するコードが示されています。重要な点は、途中に登場する4つの変数――xaby――はすべてVariableインスタンスだということです。Functionクラスの__call__メソッドの入出力がVariableインスタンスで統一されているため、上記のように複数の関数を連続して適用できます。ちなみに、ここで行った計算は、図3-1のように、関数と変数が交互に並ぶ計算グラフで表すことができます。

img1-4

図3-1 複数の関数による計算グラフ(○は変数、□は関数)

NOTE

図3-1のように、複数の関数を順に適用して作られる変換は、1つの大きな関数と見ることができます。この複数の関数によって構成される関数は、合成関数と呼ばれます。ここで重要な点は、合成関数を構成する各関数が単純な計算であったとしても、それらを連続して適用すれば、より複雑な計算が行えるということです。

ところで、なぜ私たちは一連の計算を「計算グラフ」として表すのでしょうか? その答えは、各変数の微分を効率良く求めることができる(正確には、その準備が整う)からです。そのアルゴリズムがバックプロパゲーション(誤差逆伝播法)です。次のステップからは、バックプロパゲーションが実現できるように、DeZeroを拡張していきます。