ニューラルネットワーク(NN)と自分が考えるAIの定義

高二

第1章: はじめに - AI時代における「手作り」の意義

現代、AI(人工知能)という言葉を聞かない日はない。VSCodeなどの高機能なエディタで、AIの補完機能に導かれるようにして書き上げたコードは、一見すると完璧に動作します。しかし、その内部で何が起きているのか、なぜそのアルゴリズムが最適なのかを深く理解しているかと問われれば、言葉に詰まる開発者も少なくないでしょう。AIがブラックボックスであるならば、AIが生成したコードもまた、開発者にとってのブラックボックスになりかねないのです。

私が今回、あえて「Scratch(スクラッチ)」というビジュアルプログラミング言語を用いてニューラルネットワークを実装するという、一見遠回りに思える手段を選んだのには明確な理由があります。それは、AIという技術の根源的な面白さとその仕組みを、自らの手で再構築することで、ブラックボックスの内部に光を当てたいと考えたからです。AIでは自動生成することが困難な環境にあえて身を置くことで、アルゴリズムの一行一行まで理解する。これこそが、AIに”使われる”のではなく、AIを”使いこなす”ための本質的な一歩だと思いました

第2章: AIとは何か?

「AIの定義とは何か?」この答えはまだわかっていません。

かつては、人間のように対話できる「強いAI」と、特定のタスクのみをこなす「弱いAI」という分類がなされました。アラン・チューリングが提唱した「チューリング・テスト」のように、機械が人間と見分けがつかないほど知的であるかを試す思考実験も存在します。しかし、現代において実用化されているAIのほとんどは「弱いAI」に分類されるものであり、特定の領域で活躍しています。

この「AI」という広大な分野の中で、現代のAI技術、特に「機械学習」の中核を担っているのが「ニューラルネットワーク」です。ニューラルネットワークは、人間の脳を構成する神経細胞「ニューロン」のネットワーク構造を数理モデルで模倣したものです。個々のニューロンは単純な計算しか行いませんが、それらが何層にもわたって結合し、膨大な数のパラメータ(重みやバイアス)を持つことで、極めて複雑なパターンを学習し、認識・予測・生成といった高度なタスクを実行できるようになります。

つまり、ニューラルネットワークは「AIを実現するための一つの強力なアプローチ(手法)」と位置づけることができます。「知能とは何か」という哲学的な問いの答えそのものではなく、データの中からパターンを学習し、未知のデータに対して適切な出力を返すという「知的な振る舞い」を擬似的に実現するための工学的なモデルなのです。

私が実装した◯×△認識AIも、このニューラルネットワークに基づいています。入力された画像のピクセル情報から、「◯らしさ」「×らしさ」「△らしさ」といった特徴をネットワーク自身が学習し、分類するというタスクをこなします。これはまさに、特定の機能に特化した「弱いAI」の一例と言えるでしょう。

例えば◯であれば真ん中に穴が空いている角ばっている部分がない。バツであれば2ほんの線がある三角であれば角ばっている部分が3つあるなど、一つ一つの図形には特徴があるのです。この特徴というのは図形を画像として扱いそれを数値化しても残るものです、それについては後々語るとしましょう…

第3章: 全体の構成(作るものの設計図)

まず、どのようなニューラルネットワークを作るか決めましょう。一番シンプルで分かりやすい構成は以下の通りです。

ニューロン

ニューラルネットワークの最小単位は「ニューロン」と呼ばれ人間の脳にも存在します。ニューロンは、以下の要素で構成されます。

  • 入力 (\(Input\)): 他の複数のニューロンから信号を受け取ります。
  • 重み (\(Weight\)): 各入力信号には、その重要度を示す「重み」が掛け合わされます。この重みは、学習プロセスを通じて調整される重要なパラメータです。
  • バイアス (\(Bias\)): ニューロンの発火しやすさを調整するための固定値です。重み付けされた入力の総和に加えられます。
  • 活性化関数 (\(Activation Function\)): 重み付けされた入力とバイアスの総和を最終的な出力信号に変換する関数です。この関数によって、ニューラルネットワークは非線形な問題を扱うことが可能になります。代表的な活性化関数に、シグモイド関数や\(ReLU\)関数 (\(Rectified Linear Unit\)) があります。

ネットワーク構造

ニューロンを多数配置し、層状に結合することでニューラルネットワークが構築されます。基本的なネットワークは以下の3つの層から構成されます。

  • 入力層 (\(Input Layer\)): 外部からデータを受け取る層です。
  • 隠れ層 (\(Hidden Layer\)): 中間層とも呼ばれます。入力層から受け取った情報から特徴を抽出し、複雑な計算を行います。 隠れ層が多層になったものをディープニューラルネットワーク (\(DNN\)) と呼び、ディープラーニングの基盤となります。
  • 出力層 (\(Output Layer\)): 最終的な処理結果を出力する層です。 信号は入力層から隠れ層を経て出力層へと一方向に伝播します。これを順伝播 (\(Feedforward\)) と呼びます。

学習プロセス

ニューラルネットワークの学習とは、与えられたデータ(学習データ)に対して望ましい出力を得られるように、各ニューロンの「重み」と「バイアス」を自動で調整するプロセスです

  • 損失関数 (\(Loss Function\)): まず、ネットワークの出力と、正解データとの誤差を計算します。この誤差を定量化する関数を損失関数(またはコスト関数)と呼びます。
  • 誤差逆伝播法 (\(Backpropagation\)): 損失関数によって計算された誤差を最小化するために、出力層から入力層に向かって、各層の重みとバイアスをどの程度修正すればよいかを効率的に計算していくアルゴリズムです。
  • 勾配降下法 (\(Gradient Descent\)): 誤差逆伝播法によって求められた勾配(誤差の傾き)に基づき、誤差が最も減少する方向に重みとバイアスを少しずつ更新していきます。この最適化のプロセスを繰り返すことで、ネットワークの精度が向上していきます。 この一連の学習プロセスを大量のデータを用いて繰り返し行い、ニューラルネットワークができます。

第4章: スクラッチで実装した画像認識AI

ここまでの理論を元に、私がスクラッチで実装した「◯×△認識AI」の具体的な構成を見ていきましょう。

  • 入力データ: ユーザーがスクラッチのステージ上に描いた図形を、8x8ピクセルのモノクロ画像に変換します。各ピクセルの明るさを0(白)から9(黒)の10段階の数値で表現します。この64個の数値が、入力層の64個のニューロンに入力されます。
  • ネットワーク構造:
    • 入力層: 64ニューロン。各ピクセルの輝度値がそのまま入力値となります。
    • 隠れ層: 16ニューロン。64次元のピクセル情報から、図形を特徴づけるためのより抽象的な16次元のデータに変換します。例えば、特定のニューロンは「縦の線」に強く反応し、別のニューロンは「右上のカーブ」に強く反応する、といった役割を学習の過程で獲得していきます。
    • 出力層: 3ニューロン。それぞれが「◯である確率」「×である確率」「△である確率」を出力するように設計されています。ソフトマックス関数を用いることで、3つの出力の合計は1(100%)となり、最も確率の高いものを予測結果として採用します。
  • 学習: 予め手作業で用意した「◯の画像データ100個」「×の画像データ100個」「△の画像データ100個」を教師データとして使用します。これらのデータを使って前章で説明した誤差逆伝播法を繰り返し実行することで、64x16個の入力層-隠れ層間の重みと、16x3個の隠れ層-出力層間の重み、そして各ニューロンのバイアスが最適な値に調整されていきます。

具体的な計算を以下に上げていきます。 今回は4つの \(input\), 3つの \(hidden\), 2つの \(output\) で構成します。

  • 入力層 (\(input\)): 4ニューロン
  • 隠れ層 (\(hidden\)): 3ニューロン
  • 出力層 (\(output\)): 2ニューロン
  • 学習率: \(0.5\)

初期設定(AIの脳の初期状態)

学習を始める前に、重みとバイアスをランダムな値で初期化します。

重み (\(input \rightarrow hidden\)):

\[i_1 \rightarrow h-1: 0.2, i_1 \rightarrow h_2: 0.3, i_1 \rightarrow h_3: -0.1\] \[i_2 \rightarrow h-1: 0.1, i_2 \rightarrow h_2: -0.2, i_2 \rightarrow h_3: 0.4\] \[i_3 \rightarrow h-1: -0.3, i_3 \rightarrow h_2: 0.5, i_3 \rightarrow h_3: 0.1\] \[i_4 \rightarrow h-1: 0.4, i_4 \rightarrow h_2: 0.1, i_4 \rightarrow h_3: -0.2\]

バイアス (\(hidden\)): \[h_1: 0.1, h_2: 0.2, h_3: -0.1\]

重み (\(hidden \rightarrow output\)): \[h_1 \rightarrow o_1: 0.5, h_1 \rightarrow o_2: -0.4\] \[h_2 \rightarrow o_1: -0.2, h_2 \rightarrow o_2: 0.6\] \[h_3 \rightarrow o_1: 0.3, h_3 \rightarrow o_2: 0.1\]

バイアス (\(output\)): \[o_1: 0.3, o_2: -0.2\]

ステップ1:順伝播

ある1つのデータでAIを動かします。

  • 入力データ: \([8, 5, 1, 0]\)
  • 正解ラベル: \([1, 0]\) (例えば「物体A」)

隠れ層の計算

編注:\(S(x)\): \(S(x)\):シグモイド関数とは \[S(x) = \frac{1}{1+e^{-x}}\] この上記の画像のように結果が必ず0~1に収まります , \(sum\): \(sum\): \(h_1\)の合計値, \(weight\): \(weight\): 重み

h1の計算:

\[ \begin{eqnarray*} sum &=& (i_1*weight) + (i_2*weight) + (i_3*weight) + (i_4*weight) + bias \\ &=& (8*0.2) + (5*0.1) + (1*-0.3) + (0*0.4) + 0.1 \\ &=& 1.6 + 0.5 - 0.3 + 0 + 0.1 \\ &=& 1.9 \end{eqnarray*} \]

\[\therefore output = S(1.9) ≈ 0.87\]

o1の計算: \[ \begin{eqnarray*} sum &=& (h_1*weight) + (h_2*weight) + (h_3*weight) + bias \\ &=& (0.87*0.5) + (0.89*-0.2) + (0.77*0.3) + 0.3 \\ &=& 0.435 - 0.178 + 0.231 + 0.3 = 0.788 \\ \end{eqnarray*} \]

\[\therefore output = S(0.788) ≈ 0.687\]

同様にすべての\(h_1,h_2,h_3 \cdots,o_1,o_2\)に対して同じ処理を行います

ステップ2:逆伝播

AIは \([0.687, 0.516]\) と予測しましたが、正解は \([1, 0]\) です。この間違いを元に修正します。

編注:\(first error\):\(first error\): 一次誤差, \(last error\): \(last error\): 最終誤差, \(answer\): \(answer\): 正解, \(expectation\): \(expectation\): 予測

\(o_1\)の誤差計算:

\[ \begin{eqnarray*} first error &=& answer - expectation \\ &=& 1 - 0.687 = 0.313 \\ last error &=& 0.313 * (0.687 * (1 - 0.687)) \\ &=& 0.313 * 0.215 ≈ 0.067\\ \end{eqnarray*}\]

\(o_2\)の誤差計算:

\[ \begin{eqnarray*} first error &=& answer - expectation \\ &=& 0 - 0.516 = 0.313 \\ last error &=& -0.516 * (0.516 * (1 - 0.516)) \\ &=& -0.516 * 0.25 ≈ -0.129 \\ \end{eqnarray*}\]


編注:\(modification\):\(modification\):修正量

重み( \(h_1 \rightarrow o_1\) )の修正: \[ \begin{eqnarray*} modification &=& learning rate * error(o_1) * output(h_1) \\ &=& 0.5 * 0.067 * 0.87 \\ &≈& 0.029 \\ new weight &=& former weight + 0.029 \\ &=& 0.5 + 0.029 = 0.529 \end{eqnarray*} \]

…これを\(hidden \rightarrow output\)の全ての重み(6本)と、\(output\)のバイアス(2個)で行います。

編注:\(forcederror\):\(forcederror\):押し付けられた誤差

\(h_1\)の誤差計算:

\[ \begin{eqnarray*} forced error &=& (error(o_1) * weight(h_1 \rightarrow o_1)) + (error(o_2) * weight(h_1 \rightarrow o_2)) \\ &=& (0.067 * 0.5) + (-0.129 * -0.4) = 0.0335 + 0.0516 = 0.0851 \\ last error &=& 0.0851 * (output(h_1) * (1 - output(h_1))) \\ &=& 0.0851 * (0.87 * 0.13) ≈ 0.0096 \end{eqnarray*} \]

重み(\(i_1 \rightarrow h_1\))の修正: \[ \begin{eqnarray*} modification &=& learning rate * error(h_1) * input(i_1) \\ &=& 0.5 * 0.0096 * 8 ≈ 0.0384 \\ newweight &=& formerweight(0.2) + 0.0384 = 0.2384 \end{eqnarray*} \]

…これを\(input \rightarrow hidden\)の全ての重み(12本)と、\(hidden\)のバイアス(3個)で行います。

ステップ3:繰り返し

この「順伝播→逆伝播」の1サイクルが終わりました。 これにより、AIの脳(重みとバイアス)は、ほんの少しだけ「入力\([8, 5, 1, 0]\)が来たら、出力が\([1, 0]\)に近づく」ように変化しました。

第5章: 結論 - ブラックボックスを自分の手で理解する

AI支援コーディングが当たり前になった今、ニューラルネットワークをスクラッチで実装するという行為は、時代に逆行しているように見えるかもしれません。しかし、私はこの経験を通じて、計り知れない価値を得られたと確信しています。

それは、AIという巨大なブラックボックスを、自らの手で開けるという体験です。もちろん全てはわかりませんが、順伝播における重み付き和、活性化関数による非線形変換、そして誤差逆伝播法による地道なパラメータ更新。これら全てを自分のコードとして実装することで、AIの仕組みが理解できました。

しかし未だにAIとはなんなのかは不明です、 だって全部数式でかけるんですよ! 先程も書いた通りAIの定義というのは明確には存在しませんが、どこまで行っても全ては数式上に成り立っていて不思議さは依然として残り続けるのかなとか思いました。

次へThe unsuccessful self-treatment of a case of "writer's block">
前へトロッコ問題から理解する人間の考え方>