ニューラルネットワークの構築と稼働
下図のように、入力層、中間層、出力層がそれぞれ 1 層ずつからなるニューラルネットワークを構築します。
中間層の活性化関数は ReLU を採用します。
すでに部品は揃っているので、ネットワークの構築は簡単な作業です。
以下のように記述するだけで、ニューロン 5 個の中間層とニューロン 2 個の出力層が用意されます
mid = Middle_layer(2, 5, ReLU)
out = Output_layer_c(5, 2)
ネットワークの稼働も簡単です。入力データ x が用意されているとして、
mid.forward(x)
と記述すれば、入力層から中間層にデータが入力され、中間層から活性化関数によって調整された信号が出力されます。その出力値は
mid.y_out
で取得できるので、この信号と正解値データ t を出力層に渡します:
out.activate(mid.y_out, t)
すると出力層は逆信号を送ってくるので、これを再び中間層が受け取ります:
mid.backward(out.y_back)
逆伝播によって各層で重みの更新も行なわれるので、上の手順を繰り返すことによって学習を進めることができます。
確率的勾配降下法
以下のコードでは、前回記事で作成した data_in (入力値) と data_c (正解値) のセットから無作為に 1 組ずつ取り出してネットワークに入力し、重みの更新を繰り返します。
このように、更新ごとにサンプルからランダムにデータを選んで学習に当てる手法を 確率的勾配降下法 (SGD:stochastic gradient descent) とよびます。確率的勾配降下法を使うと、データセット全体を使って計算される勾配とは若干異なる方向に進むので、局所的最適解 に囚われにくいというメリットがあります(極小値のあたりをジグザグに進むので、浅い窪みであれば脱出できる可能性があります)。
# In[9]
# 確率的勾配降下法
# 乱数を初期化
np.random.seed(11)
# 学習率を設定
alpha = 0.01
# epochを設定
epoch = 18
# データの個数
nd = data_in.shape[0]
# インデックス配列
idx = np.arange(nd)
# 中間層と出力層を作成
mid = Middle_layer(2, 5, ReLU)
out = Output_layer_c(5, 2)
# FigureとAxesを用意
fig, axs = plt.subplots(9, 2, figsize = (12, 60))
# エポック数だけ学習を繰り返す
for ax, j in zip(axs.ravel(), range(epoch)):
# インデックスをシャッフル
np.random.shuffle(idx)
# オンライン学習
for k in idx:
mid.forward(data_in[k : k + 1])
out.activate(mid.y_out, data_c[k : k + 1])
mid.backward(out.y_back)
# 中間層にすべてのデータを入れる
mid.forward(data_in)
# ネットワークからの出力値を取得
out.forward(mid.y_out)
# 出力値をクラスデータに変換
y_class = np.argmax(out.y_out, axis = 1)
# クラスデータを1 of K(one-hot)表記に変換
y_class_01 = np.identity(2, dtype = "int8")[y_class]
# 放物線の上側にある座標
# クラスデータの1列目が1となっているデータを抽出
x1 = data_in[y_class_01[:,0] == 1][:, 0]
y1 = data_in[y_class_01[:,0] == 1][:, 1]
# 放物線の下側にある座標
# クラスデータの1列目が0となっているデータを抽出
x2 = data_in[y_class_01[:,0] == 0][:, 0]
y2 = data_in[y_class_01[:,0] == 0][:, 1]
# 境界線のデータ
x3 = np.linspace(-6, 6, 33)
y3 = x3**2 - 4
# クラス分布を表示
ax.set_title("epoch = {}".format(j + 1), size = 15)
ax.set_xlim([-6, 6])
ax.set_ylim([-6, 6])
ax.scatter(x1, y1,marker = "D", color = "darkorange")
ax.scatter(x2, y2, marker = "+", color = "darkblue")
ax.plot(x3, y3, color = "black")
plt.show()
コードを実行すると、学習過程を追ってエポックごとのクラス分布が表示されます。
完全に分類に成功しているわけではありませんが、15 エポックあたりでほぼ最適化を完了しています。ニューラルネットワークでは、学習率や中間層の数、各層のニューロン数などのパラメータを少し変えるだけで、学習速度や分類精度が変化するので、色々と試してみてください。
人工知能が確率的勾配降下法(SGD)を解説します
[ChatGPT] 確率的勾配降下法(Stochastic Gradient Descent, SGD)は、機械学習や最適化のアルゴリズムの一つです。SGDは、特に大規模なデータセットや複雑なモデルのトレーニングにおいて効果的です。SGDは、最小化したい目的関数(損失関数)を最適化するために使用されます。この目的関数は、モデルのパラメータ(重みやバイアスなど)に対して定義され、その値を最小化することでモデルの性能を最適化します。SGDの基本的なアイデアは、データセット内のランダムなサンプル(ミニバッチ)を使用して、各ステップで目的関数の勾配(導関数)を推定することです。具体的には、データセット全体を一度に処理するのではなく、ミニバッチと呼ばれる一部のデータサンプルを使用して目的関数の勾配を計算します。この勾配は、目的関数の現在のパラメータにおける傾きを表します。
SGDの手順は以下のようになります:
1. ランダムに初期化されたパラメータでモデルを初期化します。
2. データセットからランダムにミニバッチを選択します。
3. 選択したミニバッチを使用して、目的関数の勾配を計算します。
4. 勾配を使用して、パラメータを更新します。具体的には、現在のパラメータから一定のステップサイズ(学習率)を引いたり足したりします。
5. 上記のステップを繰り返し行います。通常、データセット内の全てのミニバッチを使用する「エポック」と呼ばれる単位で繰り返します。
このアルゴリズムでは、各ステップでの勾配の推定はランダムに選択されたミニバッチに基づいています。そのため、勾配の推定が完全ではなく、近似値です。これが確率的勾配降下法と呼ばれる理由です。SGDの利点は、データセットが非常に大きい場合でも、各ステップでランダムなミニバッチを使用することで計算効率を向上させることができる点です。また、SGDは局所的な最小値には陥りやすいですが、全体の最小値には十分に収束する可能性が高いとされています。
ただし、SGDにはいくつかの課題もあります。学習率の選択が重要であり、小さすぎると収束が遅くなり、大きすぎると発散してしまう可能性があります。また、SGDは目的関数が非凸である場合には、グローバルな最小値に収束する保証はありません。そのため、SGDの改良版や派生アルゴリズムがいくつか提案されています。例えば、モーメンタムや学習率の減衰などのテクニックを組み合わせたり、バッチサイズや学習率のスケジューリングなどを調整することで、SGDの性能を向上させることができます。
以上が、確率的勾配降下法(SGD)の基本的な解説です。SGDは機械学習の中でも重要なアルゴリズムであり、広く使用されています。