第 14 章 演習解答¶
第 14 章「深層学習の基礎と活用」の演習問題の解答例です。問題は 第 14 章 演習問題 を参照してください。
演習 14-1: 活性化関数の理解¶
import numpy as np
x = np.array([-2.0, -1.0, 0.0, 1.0, 2.0])
# ReLU
relu = np.maximum(0, x)
print(f"ReLU: {relu}")
# シグモイド
sigmoid = 1 / (1 + np.exp(-x))
print(f"Sigmoid: {sigmoid}")
# ソフトマックス
softmax = np.exp(x) / np.sum(np.exp(x))
print(f"Softmax: {softmax}")
print(f"Softmax合計: {np.sum(softmax)}")
ReLU: [0. 0. 0. 1. 2.]
Sigmoid: [0.11920292 0.26894142 0.5 0.73105858 0.88079708]
Softmax: [0.01165623 0.03168492 0.08612854 0.23412166 0.63640865]
Softmax合計: 1.0
考察:
- ReLU は負の値を0にし、正の値はそのまま通します(-2, -1, 0 → 0)。計算が単純で勾配消失が起きにくいので、隠れ層でよく使われます。
- シグモイドはどんな値も0〜1の範囲に収めます。入力0で0.5になり、正に大きいほど1、負に大きいほど0に近づきます。2値分類の出力層で、出力を確率として使います。
- ソフトマックスは複数の値をまとめて変換し、合計が1になる確率分布を作ります。入力が大きい要素ほど確率も大きくなります(2.0 → 0.636)。多クラス分類の出力層で、各クラスの確率を出すのに使います。
演習 14-2: Fashion-MNISTでのCNN¶
import numpy as np
from tensorflow.keras.datasets import fashion_mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import (
Input, Conv2D, MaxPooling2D, Flatten, Dense
)
# データの読み込み(問題文のコード)
(X_train, y_train), (X_test, y_test) = fashion_mnist.load_data()
X_train = X_train / 255.0
X_test = X_test / 255.0
X_train = X_train.reshape(-1, 28, 28, 1)
X_test = X_test.reshape(-1, 28, 28, 1)
label_names = ["Tシャツ", "ズボン", "セーター", "ドレス", "コート",
"サンダル", "シャツ", "スニーカー", "バッグ", "ブーツ"]
# 本節のCNNモデルと同じ構造
cnn_model = Sequential([
Input(shape=(28, 28, 1)),
Conv2D(32, (3, 3), activation="relu"),
MaxPooling2D((2, 2)),
Conv2D(64, (3, 3), activation="relu"),
MaxPooling2D((2, 2)),
Flatten(),
Dense(64, activation="relu"),
Dense(10, activation="softmax")
])
cnn_model.compile(
optimizer="adam",
loss="sparse_categorical_crossentropy",
metrics=["accuracy"]
)
cnn_model.fit(X_train, y_train, epochs=5, batch_size=64,
validation_split=0.2)
# テストデータでの評価
test_loss, test_acc = cnn_model.evaluate(X_test, y_test, verbose=0)
print(f"テスト精度: {test_acc:.4f}")
# 先頭5枚の予測結果をラベル名で表示
pred = cnn_model.predict(X_test[:5], verbose=0)
for i, p in enumerate(pred):
print(f"{i+1}枚目: 予測 {label_names[np.argmax(p)]}, "
f"正解 {label_names[y_test[i]]}")
テスト精度: 0.9006
1枚目: 予測 ブーツ, 正解 ブーツ
2枚目: 予測 セーター, 正解 セーター
3枚目: 予測 ズボン, 正解 ズボン
4枚目: 予測 ズボン, 正解 ズボン
5枚目: 予測 シャツ, 正解 シャツ
考察:テスト精度は 0.9006 でした(重みの初期値が乱数なので、実行ごとに少し変わります)。同じ構造の CNN は 14.3.6 項の MNIST で 0.9885 だったので、約9ポイント低くなります。Fashion-MNIST にはセーター、コート、シャツのように形の似たクラスがあり、手書き数字より難しいためです。データの形(28×28 のグレースケール、10クラス)が同じなので、モデルのコードは変えずに使えます。
演習 14-3: Hugging Faceパイプラインの活用¶
from transformers import pipeline
# 1. 日本語テキストの感情分析(本章と同じ多言語モデル)
classifier_ja = pipeline(
"sentiment-analysis",
model="lxyuan/distilbert-base-multilingual-cased-sentiments-student"
)
texts = [
"この本はとても勉強になりました",
"電車が遅延して最悪でした",
"今日は特に何もありませんでした",
]
for text in texts:
scores = classifier_ja(text, top_k=None)
dist = ", ".join(f"{s['label']} {s['score']:.3f}" for s in scores)
print(f"{text} → {dist}")
# 2. 英語のテキスト生成
# モデルを指定しない場合は大きなモデルがダウンロードされるため、
# ここでは軽量な gpt2 を指定しています
generator = pipeline("text-generation", model="gpt2")
result = generator("Machine learning is", max_new_tokens=30,
num_return_sequences=1)
print(result[0]["generated_text"])
この本はとても勉強になりました → negative 0.401, positive 0.328, neutral 0.271
電車が遅延して最悪でした → negative 0.877, positive 0.063, neutral 0.060
今日は特に何もありませんでした → negative 0.421, neutral 0.336, positive 0.243
Machine learning is not just about working with large datasets. It's also about
learning from big data.
When I was starting out, there were a lot of
考察:「電車が遅延して最悪でした」は negative 0.877 で、正しく判定されました。「この本はとても勉強になりました」は negative 0.401、positive 0.328 で、positive のはずが negative になっています。「勉強になりました」のような遠回しなほめ方は、このモデルには難しいようです。「今日は特に何もありませんでした」も negative 0.421、neutral 0.336 と割れました。スコアが低い判定は誤りのことが多いので、ラベルだけでなくスコアも見ます。テキスト生成は単語をサンプリングで選ぶため、実行のたびに文章が変わります。gpt2 は 2019 年の小さなモデルで文章の質は高くありませんが、続きの単語を順に予測する動きは確かめられます。
演習 14-4: ニューラルネットワークの構造変更¶
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Input, Dense
# 本章と同じデータ準備
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = (X_train / 255.0).reshape(-1, 784)
X_test = (X_test / 255.0).reshape(-1, 784)
# 隠れ層の構成を変えた4つのモデルを比較
architectures = {
"本章の構成 (128, 64)": [128, 64],
"1層 (128)": [128],
"3層 (256, 128, 64)": [256, 128, 64],
"小規模 (32, 16)": [32, 16],
}
for name, hidden in architectures.items():
layers = [Input(shape=(784,))]
layers += [Dense(n, activation="relu") for n in hidden]
layers += [Dense(10, activation="softmax")]
model = Sequential(layers)
model.compile(optimizer="adam",
loss="sparse_categorical_crossentropy",
metrics=["accuracy"])
model.fit(X_train, y_train, epochs=10, batch_size=32,
validation_split=0.2, verbose=0)
_, acc = model.evaluate(X_test, y_test, verbose=0)
print(f"{name}: パラメータ数 {model.count_params():,}, "
f"テスト精度 {acc:.4f}")
本章の構成 (128, 64): パラメータ数 109,386, テスト精度 0.9738
1層 (128): パラメータ数 101,770, テスト精度 0.9714
3層 (256, 128, 64): パラメータ数 242,762, テスト精度 0.9762
小規模 (32, 16): パラメータ数 25,818, テスト精度 0.9594
| 構成 | パラメータ数 | テスト精度 |
|---|---|---|
| 本章の構成 (128, 64) | 109,386 | 0.9738 |
| 1層 (128) | 101,770 | 0.9714 |
| 3層 (256, 128, 64) | 242,762 | 0.9762 |
| 小規模 (32, 16) | 25,818 | 0.9594 |
考察(数値は実行のたびに多少変動します):どの構成も 0.96〜0.98 の範囲に収まりました。3層にしてパラメータ数を2倍以上にしても、本章の構成より約0.2ポイントしか上がりません。MNIST は、中くらいの規模の全結合ネットワークで精度が頭打ちになるためです。ニューロン数を減らした (32, 16) は、本章の構成より約1.4ポイント下がりました。大きいほどよいわけではなく、精度が同じくらいなら、学習も予測も速い小さいモデルを選ぶ手もあります。
演習 14-5: プロンプトエンジニアリングの実践¶
解答例として、3つのプロンプトとその出力傾向を比較します(出力はLLMやバージョンによって変わるため、傾向として捉えてください)。
パターン1(そのまま依頼)
機械学習でよく使われる分類アルゴリズムを比較する表を作成してください
出力例の傾向:アルゴリズムの選定も比較項目もLLM任せになり、対象読者が不明のまま一般的な表(アルゴリズム名・特徴・利点・欠点)が返ってきます。内容は正しくても、粒度が細かすぎたり専門用語が多すぎたりと、用途に合うかは運頼みです。
パターン2(対象・項目・数を指定)
機械学習の入門者向けに、分類アルゴリズムを比較する表を作成してください。対象はロジスティック回帰・k近傍法・SVM・ランダムフォレストの4つ、比較項目は「仕組みの直感的な説明」「長所」「短所」「向いているデータ」の4項目としてください。
出力例の傾向:行と列が指定どおりに揃い、内容の粒度も「入門者向け」に調整されます。パターン1と比べて、そのまま資料に使える完成度になります。
パターン3(役割・形式・制約を追加)
あなたは機械学習の講師です。第7〜8章まで学習した学生(ロジスティック回帰・ナイーブベイズ・kNN・SVM・決定木・ランダムフォレストを学習済み)の復習用に、これらを比較する表をMarkdown形式で作成してください。各セルは30字以内で簡潔に。表の後に「アルゴリズム選択の目安」を3行で添えてください。
出力例の傾向:役割(講師)と読者の前提知識を与えたことで、説明が学習済みの用語の範囲に収まり、文字数制約により表が読みやすくなります。表の後の補足指定など、出力の構成まで制御できます。
考察:使いやすい出力が得られやすいのは、パターン3のように役割、読者の前提知識、内容(対象アルゴリズムと比較項目)、出力形式と制約(Markdown、字数)を書いたプロンプトです。LLM は指示があいまいなところを一般的な答えで埋めるので、任せたくない部分ははっきり指定します。ただし制約を増やしすぎると、自分が思いつかなかった比較項目のような提案が出にくくなります。最初はパターン2くらいで試し、出力を見ながら条件を足していく方法もあります。