↓ ↑ページトップへ
表示調整
閉じる
挿絵表示切替ボタン
▼配色
▼行間
▼文字サイズ
▼メニューバー
×閉じる

ブックマークに追加しました

設定
0/400
設定を保存しました
エラーが発生しました
※文字以内
ブックマークを解除しました。

エラーが発生しました。

エラーの原因がわからない場合はヘルプセンターをご確認ください。

ブックマーク機能を使うにはログインしてください。
バード・フル(H5N1)  作者: 弥一


この作品ページにはなろうチアーズプログラム参加に伴う広告が設置されています。詳細はこちら

12/34

学習の仕組み

本エピソード(ep.12)から ep.30強化学習までは『生成AI』の仕組みについてのお話しになります。

『生成AI』の仕組みについては知っている、あるいは興味が無いという方は 次に ep.31 long-term memory から読んでいただいても、話のつながりに矛盾はないようになっています。


先にすすんで、用語がわからない、というときに読み飛ばした部分に戻って読んでください。





「では、『生成AI』がどのように学習するか、見ていきましょう」システム管理者のアレックス・チャンがポケットから小さな台を出して広げ、カフェテリアのテーブルに置いた。

 自分のタブレットを台の上に斜めに立てかけて、さらに薄くて小さな折り畳みキーボードとマウスを取り出した。


 アレックスが天井のプロジェクタの電源を入れ、タブレット画面を壁に表示させる。


「最初に、最も簡単な学習を行うプログラム・コードを表示しましょう。これはPythonというプログラム言語のコードです」


「ここでは、プログラムが学習できることと、そのプログラムが汎用的であることを示すことが目的です」


「『生成AI』なのだがら、その二つの性能が必要なのね」


「そうです、0から3の整数を2つ発生させて、その加算結果を学習させ、最後にその合計をプログラムに予測させます」


 画面にプログラム・コードが表示された。


import random


# 初期ウェイト

w1 = 0.0

w2 = 0.0


eta = 0.05 # 学習率


epochs = 100


for i in range(epochs):

# 入力(0〜3)

x1 = random.randint(0, 3) # 0から3の整数を乱数として発生→x1

x2 = random.randint(0, 3) # 0から3の整数を乱数として発生→x2


# 正解

t = x1 + x2


# プログラムによる解答

y = w1 * x1 + w2 * x2 # 積和計算でプログラムが予測する解答 y を計算


# 誤差に基づく差分

dw1 = (t - y) * x1 # 正解と回答との差 t-y を計算する

dw2 = (t - y) * x2 # 線形回帰:二乗誤差E = (t-y)^2の最小化

# ∂E/∂w1 = ∂E/∂y・∂y・∂w1= 2*(t-y)*x1



# ウェイト更新

w1 += eta * dw1 # etaは調整量を暴れさせないためのパラメタ

w2 += eta * dw2


# テスト

print("\nテスト:")

for x1 in range(4):

for x2 in range(4):

y = w1 * x1 + w2 * x2

print(f"{x1} + {x2} = {y:.2f}(正解 {x1+x2})")


「このコードを説明する前に、まず実行してみましょう」

 画面が代わって以下のように表示された。


Z:\home\創作\バード・フル\プログラム>python simplep01.py


テスト:

0 + 0 = 0.00(正解 0)

0 + 1 = 1.00(正解 1)

0 + 2 = 1.99(正解 2)

0 + 3 = 2.99(正解 3)

1 + 0 = 1.00(正解 1)

1 + 1 = 2.00(正解 2)

1 + 2 = 3.00(正解 3)

1 + 3 = 3.99(正解 4)

2 + 0 = 2.01(正解 2)

2 + 1 = 3.00(正解 3)

2 + 2 = 4.00(正解 4)

2 + 3 = 5.00(正解 5)

3 + 0 = 3.01(正解 3)

3 + 1 = 4.01(正解 4)

3 + 2 = 5.00(正解 5)

3 + 3 = 6.00(正解 6)


「左の二つの数字が乱数で発生させた数字です。 = の右の数字がプログラムが予想した数字です。どうです、正解とほとんど一致しているでしょう」


「たしかに、そうだな」




「ではプログラムを順に見ていきましょう、まず最初は初期ウェイトの部分です」


# 初期ウェイト

w1 = 0.0

w2 = 0.0


「w1、w2をウェイト、あるいはパラメータといいます。現代の『生成AI』は数百兆ものパラメーターがありますが、このプログラムは最も簡単なものなので2つしかありません」


「学習する、ということは、このウェイトの数字を調整するということと同じです」




「etaやepochs はハイパーパラメーターといいます。 epochs は学習を繰り返す回数です。ここではふたつの数字を問題としてプログラムに与え、二つを足したものを正解として与える、これを100回繰り返します」


「for のところが繰り返しの命令だな」マルコが言う。


「そうです、そのとおりです。桁が下がっているところが、繰り返されます」




「ここではまず 問題の組、二つの整数を発生させます」


# 入力(0〜3)

x1 = random.randint(0, 3) # 0から3の整数を乱数として発生→x1

x2 = random.randint(0, 3) # 0から3の整数を乱数として発生→x2


そして、その正解 t も発生させます。


# 正解

t = x1 + x2


「これで、問題と正解のセットがひとつできます。そしてプログラムに正解を予測させましょう。予測する部分はここです」


# プログラムによる解答

y = w1 * x1 + w2 * x2 # 積和計算でプログラムが予測する解答 y を計算


「yがプログラムが出した解答です。右辺はウェイトと問題をかけて、足し算をしています。このような計算を積和(せきわ)計算といいます。『生成AI』では積和計算を多用します」

「また、学習の結果はw1, w2 に格納されるので、ここではこのようなものを『記憶セル』と呼ぶことにしましょう」




「最初は w1 も w2 もゼロですから、最初のプログラムの解答 y はゼロです。ほとんどの場合間違っています」


「そうね」


「どれくらい間違っているのかを見るために、正解と解答との差 t-y を計算しています」


# 誤差に基づく差分

dw1 = (t - y) * x1 # 正解と回答との差 t-y を計算する

dw2 = (t - y) * x2 # 線形回帰:二乗誤差E = (t-y)^2の最小化

# ∂E/∂w1 = ∂E/∂y・∂y・∂w1= 2*(t-y)*x1


「dw1 の d はデルタ、つまり微小な変化量という意味です」


「なぜ、x1 をかけているの」エレナが尋ねる。


「それは線形(せんけい)回帰(かいき)の最小二乗法を使っているからです。誤差の二乗を最小にする、というやつです」


「なるほど、合成関数の微分ね。dy/dw1 はたしかに x1 ね」これもエレナが言った。


「そうです。別に最小二乗法を使わなくともいいのですが、ここでは最もわかりやすい方法として使っています。。ただ、このプログラムは ウェイトの更新式から、x1, x2 の部分を省いても収束します」




「最後にウェイト w1, w2 を修正します」


# ウェイト更新

w1 += eta * dw1 # ウェイトを微調整する

w2 += eta * dw2




「プログラムはこれだけです。簡単ですね。最後にテストの部分で学習された w1, w2 で足し算のすべてのパターンを計算しています」


# テスト

print("\nテスト:")

for x1 in range(4):

for x2 in range(4):

y = w1 * x1 + w2 * x2

print(f"{x1} + {x2} = {y:.2f}(正解 {x1+x2})")




「重要なのは y を計算する時 x1+x2 という情報を使っていないということです。使っているのは w1 * x1 + w2 * x2 です」


「たしかに」




「では、学習の過程を見てみましょう。途中経過を見るために2行ほど追加しましょう」


print(f'{i+1}回目:解答は {y} かな。違うかな')

print(f'違うよ、正解は {t} だよ、正解との差は {t-y } だね。\n')


 実行すると、画面に以下のように表示された。



1回目:解答は 0.0 かな。違うかな

違うよ、正解は 2 だよ、正解との差は 2.0 だね。


2回目:解答は 0.6000000000000001 かな。違うかな

違うよ、正解は 5 だよ、正解との差は 4.4 だね。


3回目:解答は 3.9000000000000004 かな。違うかな

違うよ、正解は 6 だよ、正解との差は 2.0999999999999996 だね。


4回目:解答は 1.51 かな。違うかな

違うよ、正解は 2 だよ、正解との差は 0.49 だね。


……


99回目:解答は 3.000645541777643 かな。違うかな

違うよ、正解は 3 だよ、正解との差は -0.0006455417776431993 だね。


100回目:解答は 1.000556656272755 かな。違うかな

違うよ、正解は 1 だよ、正解との差は -0.0005566562727550295 だね。


「このように、学習によって徐々に正解に近づいています。この様子をグラフで見てみましょう。横軸が学習の回数で、縦軸が誤りの量です」


挿絵(By みてみん)


「このように徐々に w1, w2 を調整していき、それにつれて緑の線、つまり誤りの度合いがゼロに近づいていきます。学習しているのです」


「ほんとうに学習しているみたいね」

アレックスが最後にグラフを表示したPythonプログラム全文は、以下の通りです。


import random

import matplotlib.pyplot as plt


# 初期ウェイト

w1 = 0.0

w2 = 0.0


eta = 0.05 # 学習率


epochs = 100


# グラフ用の記録リスト

w1_list = []

w2_list = []

err_list = []


for i in range(epochs):

# 入力(0〜3)

x1 = random.randint(0, 3) # 0から3の整数を乱数として発生→x1

x2 = random.randint(0, 3) # 0から3の整数を乱数として発生→x2


# 正解

t = x1 + x2


# プログラムによる解答

y = w1 * x1 + w2 * x2 # 積和計算でプログラムが予測する解答 y を計算


print(f'{i+1}回目:解答は {y} かな。違うかな')

print(f'違うよ、正解は {t} だよ、正解との差は {t-y } だね。\n')


# 記録

w1_list.append(w1)

w2_list.append(w2)

err_list.append(t-y)


# 誤差に基づく差分

dw1 = (t - y) * x1 # 正解と回答との差 t-y を計算する

dw2 = (t - y) * x2 # 線形回帰:二乗誤差E = (t-y)^2の最小化

# ∂E/∂w1 = ∂E/∂y・∂y・∂w1= 2*(t-y)*x1


# ウェイト更新

w1 += eta * dw1 # ウェイトを微調整する

w2 += eta * dw2


# テスト

print("\nテスト:")

for x1 in range(4):

for x2 in range(4):

y = w1 * x1 + w2 * x2

print(f"{x1} + {x2} = {y:.2f}(正解 {x1+x2})")


# グラフ描画

plt.figure(figsize=(10, 6))

plt.plot(w1_list, label="w1")

plt.plot(w2_list, label="w2")

plt.plot(err_list, label="t - y (error)")

plt.xlabel("epochs")

plt.ylabel("value")

plt.title("Learning curves of w1, w2, and error")

plt.legend()

plt.grid(True)

plt.show()


評価をするにはログインしてください。
ブックマークに追加
ブックマーク機能を使うにはログインしてください。
― 新着の感想 ―
このエピソードに感想はまだ書かれていません。
感想一覧
+注意+

特に記載なき場合、掲載されている作品はすべてフィクションであり実在の人物・団体等とは一切関係ありません。
特に記載なき場合、掲載されている作品の著作権は作者にあります(一部作品除く)。
作者以外の方による作品の引用を超える無断転載は禁止しており、行った場合、著作権法の違反となります。