実践型AIプログラミング特講 ニューラルネットワークについて その4 #39

実践型AIプログラミング特講 ニューラルネットワークについて その4 #39

今回は第四回にわたってライティングしてきたテーマの最終回ということで、少し話が小難しくなるかもしれませんがここまで記事を読んでくださった皆様なら問題ありません。では、本題について触れていきたいと思います。

前回の記事について

ニューラルネットワーククラスの作成

重みとバイアスの両方を更新する式の書き方がわかりました。次は、ニューラルネットワークのクラスを作成しましょう。クラスは、オブジェクト指向プログラミング(OOP)の主要な構成要素です。NeuralNetworkクラスは、重みとバイアスの変数にランダムな開始値を生成します。

NeuralNetworkオブジェクトをインスタンス化する際には、learning_rateパラメータを渡す必要があります。predict()を使って予測を行います。メソッド_compute_derivatives()と_update_parameters()には、このセクションで学んだ計算機能があります。これが最終的なNeuralNetworkクラスです。

class NeuralNetwork:

def __init__(self, learning_rate):

self.weights = np.array([np.random.randn(), np.random.randn()])

self.bias = np.random.randn()

self.learning_rate = learning_rate

def _sigmoid(self, x):

return 1 / (1 + np.exp(-x))

def _sigmoid_deriv(self, x):

return self._sigmoid(x) * (1 – self._sigmoid(x))

def predict(self, input_vector):

layer_1 = np.dot(input_vector, self.weights) + self.bias

layer_2 = self._sigmoid(layer_1)

prediction = layer_2

return prediction

def _compute_gradients(self, input_vector, target):

layer_1 = np.dot(input_vector, self.weights) + self.bias

layer_2 = self._sigmoid(layer_1)

prediction = layer_2

derror_dprediction = 2 * (prediction – target)

dprediction_dlayer1 = self._sigmoid_deriv(layer_1)

dlayer1_dbias = 1

dlayer1_dweights = (0 * self.weights) + (1 * input_vector)

derror_dbias = (

derror_dprediction * dprediction_dlayer1 * dlayer1_dbias

)

derror_dweights = (

derror_dprediction * dprediction_dlayer1 * dlayer1_dweights

)

return derror_dbias, derror_dweights

def _update_parameters(self, derror_dbias, derror_dweights):

self.bias = self.bias – (derror_dbias * self.learning_rate)

self.weights = self.weights – (

derror_dweights * self.learning_rate

)

このコードは、これまでに見てきたすべての要素をまとめたものです。予測を行いたい場合は、まずNeuralNetwork()のインスタンスを作成し、.predict()を呼び出します。

In [42]: learning_rate = 0.1

In [43]: neural_network = NeuralNetwork(learning_rate)

In [44]: neural_network.predict(input_vector)

上のコードで予測ができましたが、今度はネットワークの学習方法を学ぶ必要があります。目標は、ネットワークが学習データセットに対して一般化することです。つまり、訓練データセットと同じ確率分布に従う新しい未見のデータに適応するようにするのです。これが次のセクションで行うことです。

より多くのデータでネットワークをトレーニングする

1つのデータインスタンスに対して重みとバイアスを調整しましたが、目標はネットワークをデータセット全体に汎化させることです。確率的勾配降下法とは、反復するたびに、ランダムに選択された学習データに基づいてモデルが予測を行い、その誤差を計算して、パラメータを更新する手法です。

さて、いよいよNeuralNetworkクラスのtrain()メソッドを作成します。このメソッドでは、100回ごとにすべてのデータポイントの誤差を保存します。これは、反復回数の増加に伴ってこの指標がどのように変化するかを示すグラフを作成するためです。これがニューラルネットワークの最終的なtrain()メソッドです。

 1class NeuralNetwork:

2 # …

3

4 def train(self, input_vectors, targets, iterations):

5 cumulative_errors = []

6 for current_iteration in range(iterations):

7 # ランダムにデータインスタンスを1つ選ぶ

8 random_data_index = np.random.randint(len(input_vectors))

9

10 input_vector = input_vectors[random_data_index] 9.

11 target = targets[random_data_index].

12

13 # グラデーションを計算し、重みを更新する

14 derror_dbias, derror_dweights = self._compute_gradients(

15 input_vector, target

16 )

17

18 self._update_parameters(derror_dbias, derror_dweights)

19

20 # 全インスタンスの累積誤差を計測する

21 if current_iteration % 100 == 0:

22 cumulative_error = 0

23 # 誤差を測定するために全てのインスタンスをループする

24 for data_instance_index in range(len(input_vectors)):

25 data_point = input_vectors[data_instance_index].

26 target = targets[data_instance_index].

27

28 prediction = self.predict(data_point)

29 error = np.square(prediction – target)

30

31 cumulative_error = cumulative_error + error

32 cumulative_errors.append(cumulative_error)

33

34 return cumulative_errors

上記のコードブロックには多くのことが行われていますので、行ごとに説明します。8行目はデータセットからランダムにインスタンスを選びます。

14行目から16行目は、偏微分を計算し、バイアスと重みの微分を返します。これらは、先ほど定義した_compute_gradients()を使用しています。

18行目では、前のコードブロックで定義した_update_parameters()を使って、バイアスとウェイトを更新しています。

21行目では、現在の反復インデックスが100の倍数であるかどうかをチェックしています。これは、100回の反復ごとに誤差がどのように変化するかを観察するために行います。

24行目は、すべてのデータ・インスタンスを処理するループを開始します。

28行目は、予測結果を計算します。

29行目は、各インスタンスの誤差を計算します。

31行目では、cumulative_error変数を使って、誤差の合計を累積します。これは、すべてのデータインスタンスの誤差を示す点をプロットするためです。そして、32行目では、エラーを格納する配列であるcumulative_errorsにエラーを追加します。この配列を使ってグラフを描きます。要するに、データセットからランダムにインスタンスを選び、勾配を計算し、重みとバイアスを更新します。また、100回の繰り返しごとに累積誤差を計算し、その結果を配列に保存します。この配列をプロットすることで,学習過程における誤差の変化を視覚的に確認することができます.

注意:Jupyter Notebookでコードを実行している場合は、NeuralNetworkクラスにtrain()を追加した後、カーネルを再起動する必要があります。

複雑にならないように、8つのインスタンスを持つデータセット(input_vectors配列)を使用します。train()を呼び出し,Matplotlibを使って,各反復の累積誤差をプロットすることができます.

In [45]: # NeuralNetwork クラスのコードをここに貼り付けます。

…: # (クラスに train メソッドを追加するのを忘れないでください)

In [46]: import matplotlib.pyplot as plt

In [47]: input_vectors = np.array(

…: [

…:         [3, 1.5],

…:         [2, 1],

…:         [4, 1.5],

…:         [3, 4],

…:         [3.5, 0.5],

…:         [2, 0.5],

…:         [5.5, 1],

…:         [1, 1],

…:     ]

…: )

In [48]: targets = np.array([0, 1, 0, 1, 0, 1, 1, 0])

In [49]: learning_rate = 0.1

In [50]: neural_network = NeuralNetwork(learning_rate)

In [51]: training_error = neural_network.train(input_vectors, targets, 10000)

In [52]: plt.plot(training_error)

In [53]: plt.xlabel(“Iterations”)

In [54]: plt.ylabel(“Error for all training instances”)

In [54]: plt.savefig(“cumulative_error.png”)

再びNeuralNetworkクラスをインスタンス化し、input_vectorsとtargetの値を使ってtrain()を呼び出します。このとき、10000回実行するように指定します。これは、ニューラルネットワークのインスタンスのエラーを示すグラフです。

全体的なエラーは減少しており、これはあなたが望むことです。画像はIPythonを実行しているのと同じディレクトリに生成されています。最大の減少の後、誤差は1つのインタラクションから別のインタラクションへと素早く上下し続けます。それは、データセットがランダムで非常に小さいため、ニューラルネットワークが特徴を抽出するのが難しいからです。

しかし、この指標を使って性能を評価するのは、ネットワークがすでに見たデータインスタンスを使って評価することになるので、あまり良いことではありません。これは、モデルがトレーニングデータセットにうまく適合しすぎて、新しいデータに一般化しないオーバーフィッティングを引き起こす可能性があります。

ニューラルネットワークにレイヤーを追加する

このチュートリアルでは、学習のためにデータセットを小さくしました。通常、深層学習モデルは、データセットがより複雑で、多くのニュアンスを持っているため、大量のデータを必要とします。

データセットはより複雑な情報を持っているので、1つまたは2つのレイヤーだけでは十分ではありません。これが深層学習モデルが “ディープ “と呼ばれる理由です。深層学習モデルは、通常、多数の層を持っています。

層を増やしたり、活性化関数を使ったりすることで、ネットワークの表現力を高め、非常に高度な予測を行うことができます。例えば、携帯電話で自分の顔を撮影し、それを自分と認識した場合に携帯電話のロックが解除されるような顔認識などがその例です。

深層学習とは何か、機械学習との違いは何か

・NumPyでベクトルを表現する方法

・活性化関数とは何か、なぜニューラルネットワークで使われるのか

・バックプロパゲーション・アルゴリズムとは何か、またその仕組みについて

・ニューラルネットワークを学習し、予測を行う方法

ニューラルネットワークの学習は、主にベクトルに演算を施すことで行われます。今日は、依存関係にあるNumPyだけを使って、ゼロからそれを行いました。これは生産的な環境ではお勧めできません。なぜなら、このプロセス全体が非生産的でエラーを起こしやすいからです。これが、Keras、PyTorch、TensorFlowなどの深層学習フレームワークが人気を博している理由のひとつです。

以上が、最終回でしたが本シリーズはいかがでしたでしょうか。

プログラミング分野とは少し違いアカデミックな用語定義などが幾度も登場したので困惑する人も少なくはなかったと思います。しかし、AIソフトウェアを今後専攻する方や生業として現場で使用する機会がある方はこういった言葉の定義は共同開発で円滑にチームで成果を出す際に必要不可欠になってきます。なので心に留めておく程度のでいいので言葉の意味のあらましを理解しておくと今後の飛躍につながると思います。

プログラミングカテゴリの最新記事