💻

線形代数とニューラルネットワークをPythonで実装してみる〜中学生でもわかる機械学習の基礎【第4回】〜

に公開

線形代数とニューラルネットワークをPythonで実装してみる〜中学生でもわかる機械学習の基礎【第4回】〜

こんにちは!前回は、ベクトルと行列について学びました。

今回は、実際にプログラムで計算してみます。あなたも「ニューラルネットワーク」を動かせるようになります!

プログラムは Python を使います。難しく見えるかもしれませんが、実は「掛け算と足し算」の繰り返しです。

始めましょう!


第1章:準備

1-1. 使うもの

このチュートリアルでは、以下を使います:

  1. Python 3.x(プログラミング言語)
  2. テキストエディタ(コードを書く)
  3. ターミナル/コマンドプロンプト(プログラムを実行する)

1-2. Pythonのインストール確認

Pythonがインストールされているか確認しましょう。ターミナルで以下を打ってください:

python --version

または

python3 --version

バージョン番号(3.8以上)が表示されたらOKです!

1-3. はじめてのPythonプログラム

では、簡単なプログラムを書いてみましょう。

# 最初のプログラム
print("こんにちは、機械学習!")

このコードを hello.py というファイルに保存して、ターミナルで実行します:

python hello.py
こんにちは、機械学習!

このように表示されたら成功です!

1-4. Pythonの基本:変数とリスト

プログラムでは、データを「変数」に入れて管理します。

# 1つの数字を入れる
x = 5
y = 3
z = x + y
print(z)  # 8 と表示される

複数の数字を一度に入れるには「リスト」を使います。ベクトルを表すのに使いますよ:

# ベクトルを表すリスト
v = [3, 2]
w = [1, 4]

# リストの中身を取り出す
print(v[0])  # 3 と表示される(最初の要素)
print(v[1])  # 2 と表示される(2番目の要素)

重要:Pythonでは、数え始めが0です!

v = [3, 2]
     ↑   ↑
    v[0] v[1]

第2章:ベクトルの実装

2-1. ベクトルをプログラムで表す

第3回で学んだベクトルを、Pythonで作ってみましょう。

# ベクトル [3, 2] を表す
v = [3, 2]

# ベクトル [1, 4] を表す
w = [1, 4]

非常にシンプルですね。Pythonのリストがそのままベクトルになります!

2-2. ベクトルの大きさ(長さ)を取得

ベクトルの要素がいくつあるか調べるには:

v = [3, 2]
length = len(v)
print(length)  # 2 と表示される

w = [3, 2, 1]
length = len(w)
print(length)  # 3 と表示される

len() という関数を使うと、リストの個数がわかります。

2-3. 複数のベクトルを整理する

複数のベクトルを一つの変数に入れるには、「リストのリスト」を使います:

# 3つのベクトル
v1 = [1, 2]
v2 = [3, 4]
v3 = [5, 6]

# まとめたもの(これが行列に相当)
vectors = [v1, v2, v3]

# 取り出す
print(vectors[0])  # [1, 2] と表示される
print(vectors[1])  # [3, 4] と表示される

第3章:ベクトルの足し算を実装

3-1. 最初のプログラム:ベクトルの足し算

第3回で習ったベクトルの足し算をプログラムで書いてみます:

# 2つのベクトル
v = [3, 2]
w = [1, 3]

# 足し算をする関数を作る
def vector_add(v, w):
    result = []
    for i in range(len(v)):
        result.append(v[i] + w[i])
    return result

# 実行
answer = vector_add(v, w)
print(answer)  # [4, 5] と表示される

コードの説明:

def vector_add(v, w):  # 関数を定義(ベクトル加算)
    result = []        # 空のリストを作る
    for i in range(len(v)):  # v の長さの分、繰り返す
        result.append(v[i] + w[i])  # v[i] と w[i] を足す
    return result      # 結果を返す

for ループ で「各要素を1つずつ足す」という処理を繰り返しています。

3-2. 実際に動かしてみる

実行結果:

[4, 5]

これは第3回で習った計算ですね:

[3] + [1] = [4]
[2]   [3]   [5]

完璧です!

3-3. もっと長いベクトルで試す

v = [1, 2, 3, 4, 5]
w = [5, 4, 3, 2, 1]

answer = vector_add(v, w)
print(answer)  # [6, 6, 6, 6, 6]

何個の要素があってもOKです。プログラムの力ですね!


第4章:ベクトルのスカラー倍を実装

4-1. スカラー倍とは

第3回で学びました。ベクトルに「普通の数字」を掛けることですね。

# [3, 2] を2倍にしたら [6, 4] になる

4-2. プログラムで実装

def vector_scale(v, scalar):
    result = []
    for i in range(len(v)):
        result.append(v[i] * scalar)
    return result

# 実行
v = [3, 2]
answer = vector_scale(v, 2)
print(answer)  # [6, 4]

# 別の例
w = [1, 2, 3]
answer2 = vector_scale(w, 3)
print(answer2)  # [3, 6, 9]

4-3. ベクトルのスカラー倍を可視化

何をやっているかを print で確認してみましょう:

def vector_scale_verbose(v, scalar):
    print(f"ベクトル {v}{scalar} 倍にします")
    result = []
    for i in range(len(v)):
        value = v[i] * scalar
        result.append(value)
        print(f"  v[{i}] = {v[i]}{value}")
    return result

v = [2, 3, 4]
answer = vector_scale_verbose(v, 2)
print(f"結果:{answer}")

実行結果:

ベクトル [2, 3, 4] を 2 倍にします
  v[0] = 2 → 4
  v[1] = 3 → 6
  v[2] = 4 → 8
結果:[4, 6, 8]

第5章:行列の実装

5-1. 行列をプログラムで表す

第3回で習った行列:

A = [ 1  2 ]
    [ 3  4 ]
    [ 5  6 ]

これをプログラムで表すには、「リストのリスト」を使います:

# 3行2列の行列
A = [
    [1, 2],
    [3, 4],
    [5, 6]
]

# 要素にアクセス
print(A[0])       # [1, 2] - 1行目全体
print(A[0][0])    # 1 - 1行1列
print(A[1][0])    # 3 - 2行1列
print(A[2][1])    # 6 - 3行2列

5-2. 行列のサイズを取得

A = [
    [1, 2],
    [3, 4],
    [5, 6]
]

rows = len(A)           # 3
cols = len(A[0])        # 2

print(f"行列のサイズ:{rows}行 × {cols}列")

5-3. 行列の要素を参照する関数

後で使うので、便利な関数を作っておきましょう:

def get_element(matrix, i, j):
    """行列の(i, j)要素を取得"""
    return matrix[i][j]

def set_element(matrix, i, j, value):
    """行列の(i, j)要素を設定"""
    matrix[i][j] = value

# 使用例
A = [
    [1, 2],
    [3, 4]
]

print(get_element(A, 0, 1))  # 2
set_element(A, 1, 0, 10)
print(A)  # [[1, 2], [10, 4]]

第6章:行列とベクトルの掛け算を実装

6-1. 最重要:行列 × ベクトル

第3回で習った計算をプログラムで実装します。

def matrix_vector_multiply(matrix, vector):
    """行列とベクトルの掛け算"""
    result = []
    rows = len(matrix)
    
    for i in range(rows):
        # i 行目と vector の内積を計算
        dot_product = 0
        for j in range(len(vector)):
            dot_product += matrix[i][j] * vector[j]
        result.append(dot_product)
    
    return result

# 実行
A = [
    [2, 1],
    [3, 4]
]

v = [5, 2]

result = matrix_vector_multiply(A, v)
print(result)  # [12, 23]

計算の説明:

1行目:2×5 + 1×2 = 10 + 2 = 12
2行目:3×5 + 4×2 = 15 + 8 = 23

第3回で習った計算ですね!

6-2. 詳細に見てみる

何が起きているか確認してみましょう:

def matrix_vector_multiply_verbose(matrix, vector):
    """行列とベクトルの掛け算(詳細表示版)"""
    result = []
    rows = len(matrix)
    
    for i in range(rows):
        dot_product = 0
        print(f"行 {i}:", end="")
        for j in range(len(vector)):
            product = matrix[i][j] * vector[j]
            dot_product += product
            print(f"{matrix[i][j]}×{vector[j]} + ", end="")
        print(f"\b\b = {dot_product}")
        result.append(dot_product)
    
    return result

# 実行
A = [
    [2, 1],
    [3, 4]
]
v = [5, 2]

result = matrix_vector_multiply_verbose(A, v)
print(f"\n結果:{result}")

実行結果:

行 0:2×5 + 1×2 = 12
行 1:3×5 + 4×2 = 23

結果:[12, 23]

第7章:簡単なニューラルネットワークの実装

7-1. ニューラルネットワークの構造を思い出そう

入力層      隠れ層      出力層
  x1 ──w11──→ h1
       \──w12─┐
  x2 ──w21──→ h2  ──v1──→ y1
       \──w22─┐
  x3 ──w31──→ h3
       \──w32─┘

w:重み
h:隠れ層の出力
y:最終出力

7-2. 活性化関数:シグモイド

ニューラルネットワークでは、「活性化関数」という関数を使って、計算結果を0~1の範囲に変換します。

今回は シグモイド関数 を使います:

import math

def sigmoid(x):
    """シグモイド関数"""
    return 1 / (1 + math.exp(-x))

# テスト
print(sigmoid(0))      # 0.5
print(sigmoid(2))      # 0.88
print(sigmoid(-2))     # 0.12

グラフで見ると:

    1 ├────────────────
      │        /
  0.5 ├──────/
      │    /
    0 └──────────────
     -3    0    3

入力が大きいほど1に近づき、小さいほど0に近づきます。

7-3. シンプルなニューラルネットワークを実装

import math

def sigmoid(x):
    return 1 / (1 + math.exp(-x))

def relu(x):
    """ReLU関数(もう1つの活性化関数)"""
    return max(0, x)

class SimpleNeuralNetwork:
    def __init__(self, input_size, hidden_size, output_size):
        """
        ネットワークを初期化
        input_size: 入力の個数
        hidden_size: 隠れ層のニューロン数
        output_size: 出力の個数
        """
        # 重みを初期化(ランダム値)
        self.W1 = [
            [0.5, 0.3, 0.2],      # 最初のニューロンへの重み
            [0.2, 0.8, 0.1]       # 2番目のニューロンへの重み
        ]
        
        self.W2 = [
            [0.4, 0.9],           # 出力層への重み
            [0.6, 0.3]
        ]
        
        # バイアス(つまみ)
        self.b1 = [0.1, 0.2]
        self.b2 = [0.3]
    
    def forward(self, x):
        """
        順伝播(入力から出力を計算)
        x: 入力ベクトル
        """
        # 隠れ層の計算
        h = []
        for i in range(len(self.W1)):
            z = self.b1[i]  # バイアスから開始
            for j in range(len(x)):
                z += self.W1[i][j] * x[j]
            h.append(sigmoid(z))  # 活性化関数を通す
        
        # 出力層の計算
        y = []
        for i in range(len(self.W2)):
            z = self.b2[i]  # バイアスから開始
            for j in range(len(h)):
                z += self.W2[i][j] * h[j]
            y.append(sigmoid(z))  # 活性化関数を通す
        
        return y, h

# 使ってみる
nn = SimpleNeuralNetwork(3, 2, 1)

# 入力
x = [0.5, 0.3, 0.8]

# 実行
output, hidden = nn.forward(x)

print(f"入力: {x}")
print(f"隠れ層: {hidden}")
print(f"出力: {output}")

実行結果例:

入力: [0.5, 0.3, 0.8]
隠れ層: [0.731..., 0.821...]
出力: [0.823...]

7-4. コードの説明

計算の流れ:

1. 隠れ層の計算
   z1 = b1[0] + W1[0][0]×x[0] + W1[0][1]×x[1] + W1[0][2]×x[2]
   h[0] = sigmoid(z1)
   
   z2 = b1[1] + W1[1][0]×x[0] + W1[1][1]×x[1] + W1[1][2]×x[2]
   h[1] = sigmoid(z2)

2. 出力層の計算
   z_out = b2[0] + W2[0][0]×h[0] + W2[0][1]×h[1]
   y = sigmoid(z_out)

これが第3回で習った「行列とベクトルの掛け算」です!


第8章:実践:犬の分類ネットワーク

8-1. 問題設定

第3回で習った「犬の体型分析」を、ニューラルネットワークでやってみましょう。

入力:犬の特徴 [体重, 身長, 年齢]
出力:「小型犬か中型犬か」の確率

8-2. 実装

import math

def sigmoid(x):
    return 1 / (1 + math.exp(-x))

class DogClassifier:
    def __init__(self):
        """犬の分類ネットワーク"""
        # 入力3個 → 隠れ層2個 → 出力1個
        self.W1 = [
            [0.1, 0.2, -0.05],    # 隠れニューロン1の重み
            [0.3, -0.1, 0.15]     # 隠れニューロン2の重み
        ]
        self.b1 = [0.05, -0.02]
        
        self.W2 = [
            [0.4, 0.6]            # 出力ニューロンの重み
        ]
        self.b2 = [0.1]
    
    def predict(self, dog_features):
        """
        犬の特徴から「小型犬の確率」を予測
        dog_features: [体重kg, 身長cm, 年齢才]
        戻り値: 0~1の確率(1に近いほど「小型犬」)
        """
        x = dog_features
        
        # 隠れ層
        h = []
        for i in range(len(self.W1)):
            z = self.b1[i]
            for j in range(len(x)):
                z += self.W1[i][j] * x[j]
            h.append(sigmoid(z))
        
        # 出力層
        z_out = self.b2[0]
        for j in range(len(h)):
            z_out += self.W2[0][j] * h[j]
        
        output = sigmoid(z_out)
        return output

# 分類器を作成
classifier = DogClassifier()

# テストデータ
print("=== 犬の分類テスト ===")

# 小型犬
small_dog = [2.0, 25, 1]  # 体重2kg, 身長25cm, 年齢1才
prob = classifier.predict(small_dog)
print(f"小型犬のようなデータ: 確率 {prob:.3f}")

# 中型犬
medium_dog = [15, 50, 3]  # 体重15kg, 身長50cm, 年齢3才
prob = classifier.predict(medium_dog)
print(f"中型犬のようなデータ: 確率 {prob:.3f}")

# 大型犬
large_dog = [30, 70, 5]  # 体重30kg, 身長70cm, 年齢5才
prob = classifier.predict(large_dog)
print(f"大型犬のようなデータ: 確率 {prob:.3f}")

実行結果例:

=== 犬の分類テスト ===
小型犬のようなデータ: 確率 0.823
中型犬のようなデータ: 確率 0.512
大型犬のようなデータ: 確率 0.234

わーい!ネットワークが学習してなくても、ちゃんと異なる出力を返しています!

8-3. 何が起きているか図で見る

入力 [2.0, 25, 1] (小型犬)
  ↓
隠れ層で計算
  ↓
活性化関数(sigmoid)
  ↓
出力層で計算
  ↓
活性化関数(sigmoid)
  ↓
確率 0.823 (小型犬の可能性が高い!)

第9章:NumPyを使った効率的な実装

9-1. NumPyとは

プログラムが大きくなると、「for ループ」での計算は遅くなります。

NumPy は、行列計算に特化したPythonのライブラリです。

9-2. NumPyのインストール

ターミナルで以下を実行:

pip install numpy

9-3. NumPyでベクトルと行列を表す

import numpy as np

# ベクトル
v = np.array([3, 2])
w = np.array([1, 3])

# ベクトルの足し算(1行で!)
result = v + w
print(result)  # [4 5]

# 行列
A = np.array([
    [2, 1],
    [3, 4]
])

# ベクトルのスカラー倍(1行で!)
v_scaled = v * 2
print(v_scaled)  # [6 4]

# 行列とベクトルの掛け算(1行で!)
result = A @ v
print(result)  # [12 23]

@ 記号は「行列の掛け算」を意味します。

9-4. NumPyでニューラルネットワークを書き直す

import numpy as np

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

class NeuralNetworkNumPy:
    def __init__(self):
        # 重みをNumPy配列で定義
        self.W1 = np.array([
            [0.5, 0.3, 0.2],
            [0.2, 0.8, 0.1]
        ])
        self.b1 = np.array([0.1, 0.2])
        
        self.W2 = np.array([
            [0.4, 0.9],
            [0.6, 0.3]
        ])
        self.b2 = np.array([0.3])
    
    def forward(self, x):
        """順伝播(NumPy版)"""
        x = np.array(x)
        
        # 隠れ層
        z1 = self.W1 @ x + self.b1
        h = sigmoid(z1)
        
        # 出力層
        z2 = self.W2 @ h + self.b2
        y = sigmoid(z2)
        
        return y

# テスト
nn = NeuralNetworkNumPy()
x = np.array([0.5, 0.3, 0.8])
output = nn.forward(x)
print(f"出力: {output}")

何が違うか:

# for ループ版(遅い)
z = self.b1[i]
for j in range(len(x)):
    z += self.W1[i][j] * x[j]

# NumPy版(速い)
z1 = self.W1 @ x + self.b1

NumPy版は1行です!そして100倍以上速いです!


第10章:複数データの同時処理

10-1. 複数の犬を一度に処理

NumPyの本当の力は、複数のデータを一度に処理するときです。

import numpy as np

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

class DogClassifierNumPy:
    def __init__(self):
        self.W1 = np.array([
            [0.1, 0.2, -0.05],
            [0.3, -0.1, 0.15]
        ])
        self.b1 = np.array([0.05, -0.02])
        
        self.W2 = np.array([[0.4, 0.6]])
        self.b2 = np.array([0.1])
    
    def predict_batch(self, dogs):
        """複数の犬を一度に処理"""
        # dogs: (N, 3) の形状 N個の犬、各3つの特徴
        
        # 隠れ層
        z1 = dogs @ self.W1.T + self.b1
        h = sigmoid(z1)
        
        # 出力層
        z2 = h @ self.W2.T + self.b2
        y = sigmoid(z2)
        
        return y

# 分類器
classifier = DogClassifierNumPy()

# 5匹の犬のデータ
dogs_data = np.array([
    [2.0, 25, 1],      # 小型犬
    [5.0, 30, 2],      # 小型犬
    [15, 50, 3],       # 中型犬
    [20, 55, 4],       # 中型犬
    [30, 70, 5]        # 大型犬
])

# 一度に処理
predictions = classifier.predict_batch(dogs_data)

print("犬の分類結果(5匹一度に):")
for i, pred in enumerate(predictions):
    print(f"犬{i+1}: {pred[0]:.3f}")

実行結果例:

犬の分類結果(5匹一度に):
犬1: 0.823
犬2: 0.792
犬3: 0.512
犬4: 0.487
犬5: 0.234

これが機械学習の力です!


第11章:学習の基本

11-1. なぜ重みを変えるのか

現在のネットワークの「重み」は、適当に決めたものです。

本来は、データから「最適な重み」を学習する必要があります。

11-2. 損失関数(ロス関数)

ネットワークが「どのくらい間違ったか」を測る関数が「損失関数」です。

最も簡単な損失関数は「二乗誤差」:

def mse_loss(y_pred, y_true):
    """二乗誤差(Mean Squared Error)"""
    return np.mean((y_pred - y_true) ** 2)

# テスト
y_pred = np.array([0.8, 0.3])
y_true = np.array([1.0, 0.0])

loss = mse_loss(y_pred, y_true)
print(f"損失: {loss:.4f}")  # 0.0850

予測と正解の差を二乗して、平均を取ります。

11-3. 勾配降下法の概念

重みを「良い方向」に少しずつ変えていく方法が「勾配降下法」です。

# 現在の重みの損失
loss_before = mse_loss(prediction_before, ground_truth)

# 重みをちょっと変える
weight += 0.01  # ほんのちょっと増やす

# 新しい損失を計算
loss_after = mse_loss(prediction_after, ground_truth)

# 損失が減ったか?
if loss_after < loss_before:
    print("良い方向だ!もっと変えよう")
else:
    print("悪い方向だ。戻そう")

11-4. 学習ループの簡単な例

import numpy as np

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def mse_loss(y_pred, y_true):
    return np.mean((y_pred - y_true) ** 2)

# シンプルなネットワーク
W = np.array([[0.5, 0.3]])  # 2個の入力
b = np.array([0.2])
learning_rate = 0.1

# ダミーデータ
X = np.array([[1.0, 0.0], [0.0, 1.0], [1.0, 1.0]])
Y = np.array([[1.0], [0.0], [1.0]])

print("=== 学習プロセス ===")

for epoch in range(5):
    # 予測
    z = X @ W.T + b
    y_pred = sigmoid(z)
    
    # 損失
    loss = mse_loss(y_pred, Y)
    
    # 勾配を計算(簡略版)
    error = y_pred - Y
    dW = np.mean(error * X, axis=0, keepdims=True)
    db = np.mean(error)
    
    # 重みを更新
    W -= learning_rate * dW
    b -= learning_rate * db
    
    print(f"エポック{epoch+1}: 損失 = {loss:.4f}")

実行結果例:

=== 学習プロセス ===
エポック1: 損失 = 0.2340
エポック2: 損失 = 0.2150
エポック3: 損失 = 0.1980
エポック4: 損失 = 0.1850
エポック5: 損失 = 0.1720

損失が減っていく!ネットワークが学習しています!

11-5. この学習はどこへ

次回(第5回以降)では:

  • バックプロパゲーション(逆伝播)の計算
  • 複数層での学習
  • 実際のデータセット(例:手書き数字認識)
  • より高度な最適化手法

などを学びます。


第12章:まとめと実装練習問題

12-1. 今回学んだこと

  1. Pythonの基本:変数、リスト、ループ、関数
  2. 行列計算の実装:from ループを使った実装
  3. ニューラルネットワーク:実装と実行
  4. NumPy:効率的な行列計算
  5. 学習の基本:損失関数と勾配降下法

12-2. 実装練習問題

問題1:ベクトルのスカラー倍を実装して、結果を確認せよ

# これを実装してください
def my_vector_scale(v, scalar):
    # ここにコードを書く
    pass

# テスト
result = my_vector_scale([2, 3, 4], 3)
print(result)  # [6, 9, 12] になるはず

問題2:行列の足し算を実装せよ

# これを実装してください
def matrix_add(A, B):
    # ここにコードを書く
    pass

# テスト
A = [[1, 2], [3, 4]]
B = [[5, 6], [7, 8]]
result = matrix_add(A, B)
print(result)  # [[6, 8], [10, 12]] になるはず

問題3:シグモイド関数が本当に0~1の値を返すか確認せよ

import math

def sigmoid(x):
    return 1 / (1 + math.exp(-x))

# -10 から 10 まで、1単位で出力を表示してみてください
# すべて 0 から 1 の間の値になっているはず

問題4:自分で重みを変えて、犬の分類の結果がどう変わるか試せ

# DogClassifier クラスの W1 や W2 を変えて、
# 予測がどう変わるか観察してください

12-3. よくあるエラー

エラー1:インデックスエラー

v = [1, 2, 3]
print(v[5])  # エラー!v には3個の要素しかない

対策: len(v) で長さを確認

エラー2:行列のサイズが合わない

A = [[1, 2], [3, 4]]  # 2×2
v = [1, 2, 3]         # 3個

# A と v は掛け算できない!

対策: 行列の列数とベクトルの要素数が同じか確認

エラー3:NumPyがインストールされていない

ModuleNotFoundError: No module named 'numpy'

対策: pip install numpy を実行

12-4. 次への準備

次回(第5回以降)では:

  • 実データ(手書き数字など)での学習
  • バックプロパゲーション(学習の仕組み)
  • より複雑なネットワーク
  • 実装のコツとハック

付録:全コードの統合版

参考のため、1つのファイルにまとめたコードを以下に示します:

import numpy as np
import math

def sigmoid(x):
    """シグモイド活性化関数"""
    if isinstance(x, np.ndarray):
        return 1 / (1 + np.exp(-x))
    else:
        return 1 / (1 + math.exp(-x))

class SimpleNeuralNetwork:
    """シンプルなニューラルネットワーク"""
    def __init__(self):
        # 重みの初期化
        self.W1 = np.array([
            [0.5, 0.3, 0.2],
            [0.2, 0.8, 0.1]
        ])
        self.b1 = np.array([0.1, 0.2])
        
        self.W2 = np.array([[0.4, 0.9]])
        self.b2 = np.array([0.3])
    
    def forward(self, x):
        """順伝播"""
        x = np.array(x)
        z1 = self.W1 @ x + self.b1
        h = sigmoid(z1)
        z2 = self.W2 @ h + self.b2
        y = sigmoid(z2)
        return y

class DogClassifier:
    """犬の分類ネットワーク"""
    def __init__(self):
        self.W1 = np.array([
            [0.1, 0.2, -0.05],
            [0.3, -0.1, 0.15]
        ])
        self.b1 = np.array([0.05, -0.02])
        
        self.W2 = np.array([[0.4, 0.6]])
        self.b2 = np.array([0.1])
    
    def predict_batch(self, dogs):
        """複数の犬を予測"""
        z1 = dogs @ self.W1.T + self.b1
        h = sigmoid(z1)
        z2 = h @ self.W2.T + self.b2
        y = sigmoid(z2)
        return y

# 実行例
if __name__ == "__main__":
    print("=== シンプルなニューラルネットワーク ===")
    nn = SimpleNeuralNetwork()
    x = np.array([0.5, 0.3, 0.8])
    output = nn.forward(x)
    print(f"入力: {x}")
    print(f"出力: {output}\n")
    
    print("=== 犬の分類 ===")
    classifier = DogClassifier()
    
    dogs_data = np.array([
        [2.0, 25, 1],      # 小型犬
        [5.0, 30, 2],      # 小型犬
        [15, 50, 3],       # 中型犬
        [20, 55, 4],       # 中型犬
        [30, 70, 5]        # 大型犬
    ])
    
    predictions = classifier.predict_batch(dogs_data)
    
    for i, pred in enumerate(predictions):
        print(f"犬{i+1}: {pred[0]:.3f}")

保存して実行できます!


最後に

おめでとう!あなたは以下のことができるようになりました:

✅ Pythonでベクトルと行列を扱える
✅ ニューラルネットワークを実装できる
✅ 複数のデータを一度に処理できる
✅ 学習の基本概念を理解できる

これは本物の機械学習エンジニアの第一歩です!

重要: 次回(第5回以降)は、別のスレッドでお待ちしています。

バックプロパゲーション、より複雑なネットワーク、実データでの学習など、さらに深い内容をカバーします。

それでは、コードを書いて、ニューラルネットワークの力を感じてみてくださいね!

Discussion