ニューラルネットワークと機械学習の基礎:概念からPyTorchによる実装まで
【概要】ニューラルネットワークは、入力データの重みづけ処理、合計とバイアス調整、活性化関数の適用を行うニューロンがネットワークを形成する機械学習モデルである。PyTorchを活用した実装では、大規模なデータセットを用いた学習により、ニューロン間の結合の重みとバイアスを自動的に最適化し、データの特徴やパターンを獲得する。特に、バックプロパゲーションによる誤差の逆伝播計算と、勾配降下法による誤差の最小化が、学習プロセスにおいて中心的な役割を担う。
【目次】
1. Pythonのインストールと必要なPythonライブラリのインストール(Windows上)
- Pythonのインストール
注:既にPython(バージョン3.12を推奨)がインストール済みの場合は、この手順は不要である。
winget(Windowsパッケージマネージャー)を使用してインストールを行う。
- Windowsで、管理者権限でコマンドプロンプトを起動する(手順:Windowsキーまたはスタートメニュー、
cmdと入力、右クリック、「管理者として実行」)。 - winget(Windowsパッケージマネージャー)が利用可能か確認する。
winget --version
- 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
- Windowsで、管理者権限でコマンドプロンプトを起動する(手順:Windowsキーまたはスタートメニュー、
- 必要なPythonライブラリのインストール
【関連する外部ページ】
【サイト内の関連ページ】
2. 機械学習の概要
機械学習は、訓練データを用いて学習し、分類や予測などの知的能力を獲得する技術である。訓練データの追加により、さらに知的能力が向上する可能性がある。
訓練データは、機械学習の学習に使用されるデータである。学習の手順は以下のとおりである。
- データの準備(目的に応じた訓練データを準備する)
- 学習の実行(データを用いてパターンを学習し、モデルを構築する)
- タスクの実行(新しいデータを処理する)
機械学習の特徴は以下のとおりである。
- データを用いて知的能力を向上させる
- 自動でデータのパターンを抽出する
- 人手による規則の記述が不要である
応用事例には、画像理解、自然言語処理、予測などがある。
MNISTデータセットは、機械学習の入門用データセットとして広く使用されている手書き数字(0〜9)の画像データセットである。
- 訓練用60,000枚、テスト用10,000枚で構成される
- 各画像は28×28ピクセルのグレースケールである
- 画素値は0〜1に正規化されている
- 各画像に正解ラベル(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}%")
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)は、以下の特徴を持つ活性化関数である。
- 入力が0未満の場合は0を出力する
- 入力が0以上の場合は、その値をそのまま出力する
- 現代のディープラーニングで標準的に使用されている
以下に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}") # シグモイド
5. ニューラルネットワークの構造と動作原理
ニューラルネットワークは、複数の層から構成される。各層は複数のニューロンで構成され、層間はニューロン同士の結合で接続されている。
フォワードプロパゲーションでは、入力データは入力層から出力層へと順方向に伝播する。各層のニューロンは、前層からの入力を処理し、その結果を次層へ伝達する。
分類タスクにおける出力層の構造は以下のとおりである。
- 出力層のニューロン数は、分類対象のクラス数に対応する
- 各出力ニューロンの活性度は、0以上の実数値として表現される
- 最も高い活性度を示すニューロンに対応するクラスが分類結果となる
以下のプログラムは、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))}") # 最大出力を持つニューロンのインデックスを取得
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()}") # 最大値を持つインデックスが分類結果
6. バックプロパゲーションと学習メカニズム
バックプロパゲーションは、ニューラルネットワークの学習アルゴリズムである。出力誤差を基に、出力層から入力層へと逆方向に誤差を伝播させ、各層の重みとバイアスを更新する。
学習の手順は以下のとおりである。
- 訓練データを用いてフォワードプロパゲーションを行い、出力を得る
- 出力と正解(教師信号)を比較し、損失関数により誤差を計算する。損失関数は出力と正解のずれを数値化する関数であり、二乗誤差などが用いられる
- 誤差の勾配を計算し、勾配降下法により重みとバイアスを更新する
- 同一の訓練データを繰り返し使用し、誤差を低減する
以下に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()}")
7. 過学習の概念と対策
過学習は、訓練データへの過剰適合により発生する現象である。訓練データに含まれるノイズや偶然のパターンまで学習してしまい、未知のデータに対する性能が低下する。特に訓練データが少ない場合やモデルのパラメータ数が多い場合に発生しやすい。
対策は以下のとおりである。
- 訓練データの増加
- モデルの複雑さの抑制(層数やニューロン数の適切な設定)
- 検証データによる過学習の検出
- ドロップアウト:学習時にランダムにニューロンを無効化する手法
- 正則化(L1、L2):重みの大きさに制約を加える手法
8. ディープラーニングの概要
ディープラーニングは、多層のニューラルネットワークを使用する機械学習の手法である。「ディープ」という名称は、多層構造に由来する。
ディープラーニングは、画像認識、自然言語処理、音声認識など、多様なタスクで使用されている。これを支える要素には、活性化関数(ReLUなど)、ドロップアウト、大規模データ、高性能コンピュータなどがある。
9. まとめ
ニューラルネットワークは、ニューロンによる重みづけ、バイアス調整、活性化関数の適用という処理を基本とする。フォワードプロパゲーションにより入力から出力へデータが伝播し、バックプロパゲーションにより重みとバイアスが学習される。訓練データを繰り返し使用することで、誤差を低減し、目的のタスクを実行できるようになる。