ニューラルネットワークと機械学習の基礎:概念からPyTorchによる実装まで

【概要】ニューラルネットワークは、入力データの重みづけ処理、合計とバイアス調整、活性化関数の適用を行うニューロンがネットワークを形成する機械学習モデルである。PyTorchを活用した実装では、大規模なデータセットを用いた学習により、ニューロン間の結合の重みとバイアスを自動的に最適化し、データの特徴やパターンを獲得する。特に、バックプロパゲーションによる誤差の逆伝播計算と、勾配降下法による誤差の最小化が、学習プロセスにおいて中心的な役割を担う。

【目次】

  1. Pythonのインストールと必要なPythonライブラリのインストール
  2. 機械学習の概要
  3. ニューラルネットワークの基本構造と仕組み
  4. 活性化関数とその役割
  5. ニューラルネットワークの構造と動作原理
  6. バックプロパゲーションと学習メカニズム
  7. 過学習の概念と対策
  8. ディープラーニングの概要
  9. まとめ

1. Pythonのインストールと必要なPythonライブラリのインストール(Windows上)

  1. Pythonのインストール

    注:既にPython(バージョン3.12を推奨)がインストール済みの場合は、この手順は不要である。

    winget(Windowsパッケージマネージャー)を使用してインストールを行う。

    1. Windowsで、管理者権限コマンドプロンプトを起動する(手順:Windowsキーまたはスタートメニュー、cmdと入力、右クリック、「管理者として実行」)。
    2. winget(Windowsパッケージマネージャー)が利用可能か確認する。
      winget --version
      
      wingetバージョン確認の実行結果
    3. Pythonのインストールを行う(下のコマンドによりPython 3.12がインストールされる)。
      REM Python をシステム領域にインストール
      winget install --scope machine --id Python.Python.3.12 -e --silent --accept-source-agreements --accept-package-agreements
      
      REM パス長制限の解除
      reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem" /v LongPathsEnabled /t REG_DWORD /d 1 /f
      reg query "HKLM\SYSTEM\CurrentControlSet\Control\FileSystem" /v LongPathsEnabled
      
      REM Python のパス設定
      set "PYTHON_PATH=C:\Program Files\Python312"
      set "PYTHON_SCRIPTS_PATH=C:\Program Files\Python312\Scripts"
      echo "%PATH%" | find /i "%PYTHON_PATH%" >nul
      if errorlevel 1 setx PATH "%PATH%;%PYTHON_PATH%" /M >nul
      echo "%PATH%" | find /i "%PYTHON_SCRIPTS_PATH%" >nul
      if errorlevel 1 setx PATH "%PATH%;%PYTHON_SCRIPTS_PATH%" /M >nul
      
  2. 必要なPythonライブラリのインストール
    1. Windowsで、管理者権限コマンドプロンプトを起動する(手順:Windowsキーまたはスタートメニュー、cmdと入力、右クリック、「管理者として実行」)。
    2. 以下のコマンドを実行し、必要なライブラリをインストールする。
      pip install -U torch torchvision torchaudio numpy --index-url https://download.pytorch.org/whl/cu121
      pip install -U pandas matplotlib japanize-matplotlib scipy
      

【関連する外部ページ】

【サイト内の関連ページ】

2. 機械学習の概要

機械学習は、訓練データを用いて学習し、分類や予測などの知的能力を獲得する技術である。訓練データの追加により、さらに知的能力が向上する可能性がある。

訓練データは、機械学習の学習に使用されるデータである。学習の手順は以下のとおりである。

  1. データの準備(目的に応じた訓練データを準備する)
  2. 学習の実行(データを用いてパターンを学習し、モデルを構築する)
  3. タスクの実行(新しいデータを処理する)

機械学習の特徴は以下のとおりである。

応用事例には、画像理解、自然言語処理、予測などがある。

MNISTデータセットは、機械学習の入門用データセットとして広く使用されている手書き数字(0〜9)の画像データセットである。

import torch
from torch import nn, optim
from torchvision import datasets, transforms

# デバイスの設定
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# データセットの準備(MNIST)
transform = transforms.Compose([transforms.ToTensor()])
train_data = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_data = datasets.MNIST(root='./data', train=False, download=True, transform=transform)

# データローダーの設定
train_loader = torch.utils.data.DataLoader(train_data, batch_size=64, shuffle=True)
test_loader = torch.utils.data.DataLoader(test_data, batch_size=64, shuffle=False)

class SimpleNN(nn.Module):
    def __init__(self):
        super(SimpleNN, self).__init__()
        self.fc1 = nn.Linear(28*28, 128)
        self.fc2 = nn.Linear(128, 64)
        self.fc3 = nn.Linear(64, 10)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# モデルをデバイスに移動
model = SimpleNN().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.1)

# 学習の実行
epochs = 5
for epoch in range(epochs):
    model.train()  # 訓練モードの設定
    running_loss = 0.0
    for images, labels in train_loader:
        # データをデバイスに移動
        images = images.view(images.shape[0], -1).to(device)
        labels = labels.to(device)

        optimizer.zero_grad()
        output = model(images)
        loss = criterion(output, labels)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()

        # メモリ解放
        del loss
        if torch.cuda.is_available():
            torch.cuda.empty_cache()

    print(f"エポック {epoch+1}/{epochs}, 訓練損失: {running_loss/len(train_loader):.4f}")

# モデルの評価
model.eval()  # 評価モードの設定
correct = 0
total = 0
with torch.no_grad():
    for images, labels in test_loader:
        images = images.view(images.shape[0], -1).to(device)
        labels = labels.to(device)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print(f"テストデータにおける正解率: {100 * correct / total:.2f}%")
MNISTデータセットを用いた機械学習の実行結果

3. ニューラルネットワークの基本構造と仕組み

ニューラルネットワークは、機械学習の手法の一つであり、ニューロンと呼ばれる処理単位から構成される学習システムである。各ニューロンは複数の入力信号を受け取り、以下の処理を行う。

この出力は、次層のニューロンへの入力として伝播される。

以下にPythonスクリプト例(ニューロンの処理)を示す。

# ニューラルネットワークの単一ニューロンの順伝播計算
# 入力、重み、バイアスから活性化値を計算

# 入力値
inputs = [0.3, -0.5, 0.2]
# 重み
weights = [0.1, 0.8, -0.5]
# バイアス
bias = 0.2

# 重み付き和の計算
weighted_sum = 0
# 各入力に対応する重みを掛けて合計
for i in range(len(inputs)):
    weighted_sum += inputs[i] * weights[i]

# バイアスを加える
weighted_sum += bias

# ReLU活性化関数による出力計算
# 0以下の場合は0、そうでなければweighted_sumをそのまま出力
if weighted_sum > 0:
    output = weighted_sum
else:
    output = 0

# 結果を表示
print(f"{inputs} -> {output}")
単一ニューロンの順伝播計算の実行結果

4. 活性化関数とその役割

活性化関数は、ニューロンの出力値を決定する関数である。重みづけと合計、バイアス調整の結果に適用される。代表的な活性化関数として、ReLUやシグモイド関数がある。

ReLU(Rectified Linear Unit)は、以下の特徴を持つ活性化関数である。

以下にPythonスクリプト例(ReLUとシグモイド)を示す。

import math

def relu(x):
   """
   ReLU活性化関数
   入力が0より大きい場合はそのまま返し、0以下の場合は0を返す
   """
   return max(0, x)

def sigmoid(x):
   """
   シグモイド活性化関数
   入力を0-1の範囲に変換する
   """
   return 1 / (1 + math.exp(-x))

# 入力値の設定
x1 = 1
x2 = -1
x3 = 0

# ReLU関数による出力計算
relu_out1 = relu(x1)
relu_out2 = relu(x2)

# シグモイド関数による出力計算
sigmoid_out = sigmoid(x3)

# 結果の表示
print(f"{x1} -> {relu_out1}")  # 正の入力
print(f"{x2} -> {relu_out2}")  # 負の入力
print(f"{x3} -> {sigmoid_out}")  # シグモイド
ReLUとシグモイド関数の実行結果

5. ニューラルネットワークの構造と動作原理

ニューラルネットワークは、複数の層から構成される。各層は複数のニューロンで構成され、層間はニューロン同士の結合で接続されている。

フォワードプロパゲーションでは、入力データは入力層から出力層へと順方向に伝播する。各層のニューロンは、前層からの入力を処理し、その結果を次層へ伝達する。

分類タスクにおける出力層の構造は以下のとおりである。

以下のプログラムは、2入力3出力のニューラルネットワークによる3クラス分類を実装している。

# ニューラルネットワークによる3クラス分類の実装
# 入力層(2ニューロン)から出力層(3ニューロン)への順伝播計算を行う

inputs = [0.2, 0.8]  # 入力値

# 入力層から出力層への重み係数(2×3行列)
layer_weights = [
   [0.1, 0.4, 0.7],  # 入力1からの重み
   [0.2, 0.5, 0.8]   # 入力2からの重み
]

layer_bias = [0.1, 0.1, 0.1]  # 出力層の各ニューロンのバイアス

# 出力層の各ニューロンの出力を計算
output = [0] * 3  # 出力用配列を初期化
for j in range(3):  # 出力層の各ニューロンについて
   sum = 0
   for i in range(2):  # 入力層の各ニューロンからの入力を合計
       sum += inputs[i] * layer_weights[i][j]
   sum += layer_bias[j]  # バイアスを加算
   output[j] = max(0, sum)  # ReLU関数による活性化

print(f"入力: {inputs}")
print(f"出力: {output}")
print(f"分類結果: {output.index(max(output))}")  # 最大出力を持つニューロンのインデックスを取得
3クラス分類の実行結果

PyTorchによる実装

PyTorchを用いると、上記と同等の計算を簡潔に記述できる。

# ニューラルネットワークの3クラス分類プログラム
import torch

# 入力データ(2個のニューロン)
inputs = torch.tensor([0.2, 0.8])

# 重み行列(入力2×出力3の行列、転置して使用)
layer_weights = torch.tensor([
   [0.1, 0.4, 0.7],  # 1番目の入力からの重み
   [0.2, 0.5, 0.8]   # 2番目の入力からの重み
]).T  # 行列を転置して各出力ニューロンへの重みを行方向に配置

# バイアス(出力層の3ニューロン分)
layer_bias = torch.tensor([0.1, 0.1, 0.1])

# フォワードプロパゲーション
# 1. 入力と重みの行列積で重み付き和を計算
# 2. バイアスを加算
# 3. ReLU関数で活性化(max(0,x))
output = torch.relu(torch.matmul(inputs, layer_weights.T) + layer_bias)

# 結果出力
print(f"入力: {inputs.tolist()}")
print(f"出力: {output.tolist()}")
print(f"分類結果: {torch.argmax(output).item()}")  # 最大値を持つインデックスが分類結果
PyTorchによる3クラス分類の実行結果

6. バックプロパゲーションと学習メカニズム

バックプロパゲーションは、ニューラルネットワークの学習アルゴリズムである。出力誤差を基に、出力層から入力層へと逆方向に誤差を伝播させ、各層の重みとバイアスを更新する。

学習の手順は以下のとおりである。

  1. 訓練データを用いてフォワードプロパゲーションを行い、出力を得る
  2. 出力と正解(教師信号)を比較し、損失関数により誤差を計算する。損失関数は出力と正解のずれを数値化する関数であり、二乗誤差などが用いられる
  3. 誤差の勾配を計算し、勾配降下法により重みとバイアスを更新する
  4. 同一の訓練データを繰り返し使用し、誤差を低減する

以下に2つの実装を示す。プログラム1は基本的な実装、プログラム2はPyTorchの自動微分機能を活用した実装である。

プログラム1:基本的な実装

"""
ニューラルネットワークの学習実装
入力層(2)→出力層(3)の順伝播型ネットワーク
ReLU活性化関数を使用し、二乗誤差による学習を行う
"""

inputs = [0.2, 0.8]
target = [1, 0, 0]  # 正解ラベル(3クラス分類の1番目)
learning_rate = 0.1

# 初期重みとバイアス
layer_weights = [
   [0.1, 0.4, 0.7],  # 入力1から各出力への重み
   [0.2, 0.5, 0.8]   # 入力2から各出力への重み
]
layer_bias = [0.1, 0.1, 0.1]  # 各出力ノードのバイアス

# フォワード計算:入力値と重みの積和にバイアスを加え、ReLU関数で活性化
output = [0, 0, 0]
for j in range(3):  # 各出力ノードについて
   sum = 0
   for i in range(2):  # 各入力との重み付き和を計算
       sum += inputs[i] * layer_weights[i][j]
   sum += layer_bias[j]
   output[j] = max(0, sum)  # ReLU関数による活性化

# 誤差計算:教師信号と出力の差
errors = [target[j] - output[j] for j in range(3)]

# 重みとバイアスの更新:勾配降下法による調整
for j in range(3):
   for i in range(2):
       if output[j] > 0:  # ReLUの勾配は出力が正の時のみ1
           layer_weights[i][j] += learning_rate * errors[j] * inputs[i]
   if output[j] > 0:  # バイアスも同様に更新
       layer_bias[j] += learning_rate * errors[j]

print(f"誤差: {errors}")
print(f"更新後の重み: {layer_weights}")
基本的な学習実装の実行結果

プログラム2:PyTorchによる実装

import torch

# 入力データと教師信号の設定
inputs = torch.tensor([0.2, 0.8], requires_grad=True)
target = torch.tensor([1.0, 0.0, 0.0])  # 3クラス分類の正解ラベル
learning_rate = 0.1  # 学習率

# 重みとバイアスの初期化(requires_gradを設定して勾配計算を有効化)
layer_weights = torch.tensor([
   [0.1, 0.4, 0.7],
   [0.2, 0.5, 0.8]
], requires_grad=True)
layer_bias = torch.tensor([0.1, 0.1, 0.1], requires_grad=True)

# フォワード計算:入力値と重みの行列積にバイアスを加算し、ReLU関数で活性化
output = torch.relu(torch.matmul(inputs, layer_weights) + layer_bias)

# 二乗誤差による損失計算
loss = torch.sum((target - output) ** 2)

# 誤差逆伝播による勾配計算
loss.backward()

# 勾配降下法による重みとバイアスの更新
with torch.no_grad():  # 更新時は勾配計算不要
   layer_weights -= learning_rate * layer_weights.grad
   layer_bias -= learning_rate * layer_bias.grad

   # 次回の学習のために勾配をクリア
   layer_weights.grad.zero_()
   layer_bias.grad.zero_()

# 学習結果の出力
print(f"誤差: {(target - output).tolist()}")
print(f"更新後の重み: {layer_weights.tolist()}")
PyTorchによる学習実装の実行結果

7. 過学習の概念と対策

過学習は、訓練データへの過剰適合により発生する現象である。訓練データに含まれるノイズや偶然のパターンまで学習してしまい、未知のデータに対する性能が低下する。特に訓練データが少ない場合やモデルのパラメータ数が多い場合に発生しやすい。

対策は以下のとおりである。

8. ディープラーニングの概要

ディープラーニングは、多層のニューラルネットワークを使用する機械学習の手法である。「ディープ」という名称は、多層構造に由来する。

ディープラーニングは、画像認識、自然言語処理、音声認識など、多様なタスクで使用されている。これを支える要素には、活性化関数(ReLUなど)、ドロップアウト、大規模データ、高性能コンピュータなどがある。

9. まとめ

ニューラルネットワークは、ニューロンによる重みづけ、バイアス調整、活性化関数の適用という処理を基本とする。フォワードプロパゲーションにより入力から出力へデータが伝播し、バックプロパゲーションにより重みとバイアスが学習される。訓練データを繰り返し使用することで、誤差を低減し、目的のタスクを実行できるようになる。