はじめに
こんにちは!watnowアドベントカレンダー22日目を担当しますB3の尾﨑です!
記事を書くのはこれが初めてなのでお手柔らかにお願いします😣
今回は私が以前プロダクト開発で使用したPythonのライブラリであるTensorFlowを使って、初心者の方にも簡単にモデル構築ってこういう感じなんだと体験してもらうことを目的とした記事を書いてみました。
機械学習やに触れたことがない方でも、実際にコードを書きながらモデルを作っていけるようになっています!!!
今回やること
この記事では、手書き数字の画像を「0」か「1」かに分類するモデルを作ります。
- 入力:28×28ピクセルの手書き数字画像
- 出力:0 または 1
MNISTという有名なデータセットを使い、画像分類モデルを一から構築していきます。
0. そもそも TensorFlow ってなんなの?
TensorFlow は、機械学習や深層学習のモデルを作るための Python ライブラリです。
もう少し噛み砕くと、
- データを入力すると
- 何らかの計算をして
- 結果を出す
というモデルを、比較的簡単に作れるようにしてくれる道具です。
0.1 機械学習のモデルって何をしているのか
機械学習のモデルは、ざっくり言うと「入力」と「正解」をたくさん見て、その関係をうまく再現できるように調整された関数です。
今回の記事で言うと、
- 入力:手書き数字の画像
- 正解:その画像が 0 か 1 か
という組み合わせを大量に見せて、
「この画像なら 0 っぽい」「この画像なら 1 っぽい」という判断ができるように、中身のパラメータを少しずつ調整しています。TensorFlow は、このパラメータを調整する仕組みや学習のための計算処理を全部まとめて用意してくれています。めっちゃ便利!
0.2 TensorFlow を使うメリット
TensorFlow を使うメリットは、主に次の3つです。
- モデルの形をコードで直感的に書ける
- 学習や予測の処理をほぼ自動でやってくれる
- 実務や研究でも使われている定番ライブラリなので、情報が多い
特に初心者にとって大きいのは、「モデルの中身をゼロから自分で実装しなくていい」 という点です。
もし TensorFlow がなかったら、
- 行列計算
- 微分
- 重みの更新
といった処理を全部自分で書く必要があります。
TensorFlow を使えば、「どんなモデルを作りたいか」を考えることに集中できます。
0.3 今回の記事で TensorFlow をどう使うのか
この記事では TensorFlow を使って、
- 画像を入力として受け取る
- その画像が 0 か 1 かを判断する
- 判断が間違っていたら、少しずつ修正する
という一連の流れを体験します。
このあと出てくる
model = ...model.compile()model.fit()
といったコードはすべて、「モデルを作る → 学習させる → 使ってみる」ための TensorFlow の機能です。
ここでは細かい理論はあまり気にせず、「TensorFlow を使うと、こういう流れでモデルが作れるんだ」という感覚をつかんでもらえれば十分です。
それでは、実際に TensorFlow を使ってモデルを作っていきましょう!!
1. 環境構築
まずは TensorFlow をインストールします。
ここでは
- Intel Mac / Windows
- Apple Silicon Mac
の両方について説明します。
1.1 Intel Mac / Windows の場合
インストール
Intel Mac や Windows の場合は、
基本的に TensorFlow をそのままインストールすれば動きます。
pip install --upgrade pip
pip install tensorflow
インストール確認
インストールが終わったら、
pip show を使って TensorFlow が入っているか確認します。
pip show tensorflow
次のような情報が表示されればOKです。
Name: tensorflow
Version: 2.16.2
Summary: TensorFlow is an open source machine learning framework for everyone.
Location: /Users/xxxx/venv/lib/python3.10/site-packages
1.2 Apple Silicon Macの場合
インストール
Apple Silicon Mac では、
通常の TensorFlow をそのままインストールすると
エラーが出ることがあります。
そのため、Apple Silicon 専用のパッケージを使用します。
pip install --upgrade pip
pip install tensorflow-macos tensorflow-metal
インストール確認
こちらも同様に pip show で確認します。
pip show tensorflow
Name: tensorflow
Version: 2.16.2
と表示されていれば成功です。
※ 余談に自分が環境構築でバージョン管理が杜撰で詰まった話を載せてるので気になる人は見てみてください
2. データを読み込む
次に、学習に使うデータを読み込みます。
今回は MNIST(エムニスト) という、機械学習の入門でよく使われるデータセットを使います。
2.1 MNIST データセットとは?
MNIST は、手書き数字(0〜9)の画像を集めたデータセットです。
- 1 枚の画像は 28×28 ピクセル
- 白黒画像
- 各画像には「これは 0」「これは 1」などの正解ラベルが付いている
そのため、
- 画像認識
- 画像分類
- 機械学習・深層学習の入門
によく使われています。
「画像を入力して、何の数字かを当てる」という問題設定がとても分かりやすいので、
モデル構築の流れを学ぶのにちょうどいいデータセットです。
2.2 TensorFlow には最初から用意されている
MNIST はとても有名なデータセットなので、
TensorFlow には 最初から読み込むための機能が用意されています。
そのため、
- Web から自分でダウンロードする
- ファイルを読み込む処理を書く
といったことをする必要はありません。
TensorFlow の関数を呼び出すだけで、
すぐに MNIST を使うことができます。
2.3 実際にデータを読み込む
それでは、MNIST データセットを読み込みます。
import tensorflow as tf
import numpy as np
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
この1行で、次の4つのデータが読み込まれます。
- x_train:学習用の画像データ
- y_train:学習用の正解ラベル
- x_test:評価用の画像データ
- y_test:評価用の正解ラベル
2.4 データの形を確認する
読み込んだデータがどんな形をしているか、確認してみます。
print(x_train.shape)
print(y_train.shape)
出力例:
(60000, 28, 28)
(60000,)
これは次のことを意味しています。
- 学習用の画像が 60000 枚ある
- 1 枚の画像は 28×28 ピクセル
- 各画像に対応するラベルが 1 つずつある
ラベルの中身は、次のような 数字(0〜9) です。
print(np.unique(y_train))
この時点では、
- 画像データ
- 正解ラベル
がセットで用意できた状態です。
次のステップでは、この中から「0」と「1」だけを取り出して、2クラス分類の問題にしていきます。
3. 「0」と「1」だけに絞る
ここまでで、MNIST の画像データとラベルを読み込むことができました。
ただし、このままだと 0〜9 の 10 クラス分類になります。
今回は TensorFlow を使ったモデル構築の流れを体験することが目的なので、
問題を少し簡単にして、数字の「0」と「1」だけを使った2 クラス分類にします。
3.1 なぜ 0 と 1 だけにするのか?
理由は大きく 2 つあります。
- クラス数が少ない方が、モデルの構造をシンプルにできる
- 「ちゃんと学習できているか」を確認しやすい
いきなり 10 クラスを扱うと、
- 出力の形が複雑になる
- エラーの原因が分かりにくくなる
といった問題が出やすくなります。
まずは 0 / 1 の判別ができるモデルを一度作り切ることを優先します。
3.2 ラベルが 0 または 1 のデータだけを取り出す
それでは、実際にデータを絞っていきます。
train_mask = (y_train == 0) | (y_train == 1)
test_mask = (y_test == 0) | (y_test == 1)
このコードでは、
y_train == 0y_train == 1
のどちらかを満たすデータだけを True とする マスク(True / False の配列)を作っています。
3.3 マスクを使ってデータを抽出する
作ったマスクを使って、対応する画像とラベルだけを取り出します。
x_train = x_train[train_mask]
y_train = y_train[train_mask]
x_test = x_test[test_mask]
y_test = y_test[test_mask]
これにより、
- 画像
- ラベル
の両方が、「0 と 1 だけ」のデータになります。
3.4 本当に 0 と 1 だけになっているか確認する
最後に、ラベルの中身を確認します。
print(np.unique(y_train))
出力:
[0 1]
この結果から、データが正しく 2 クラスに絞られていることが分かります。
この時点で、
- 入力:手書き数字の画像
- 出力:0 または 1
という シンプルな分類問題が準備できました。
次のステップでは、この画像データをモデルに入れられる形に整える前処理を行っていきます。
4. 前処理
ここまでで、画像データとラベルを用意できました。
ただし、このままでは モデルにそのまま入力することはできません。
そこで、モデルが扱いやすい形に整える前処理(preprocessing)を行います。
今回は次の2つを行います。
- 画像の形を整える
- 画素値の範囲を整える
4.1 チャンネル次元を追加する
現在の画像データの形は (28, 28) ですが、
CNN(畳み込みニューラルネットワーク)では、
(高さ, 幅, チャンネル)
という形で画像を扱います。
MNIST は白黒画像なので、チャンネル数は 1 です。
x_train = x_train[..., np.newaxis]
x_test = x_test[..., np.newaxis]
この処理によって、画像の形は
(枚数, 28, 28, 1)
になります。
正しく変換できているか確認します。
print(x_train.shape)
4.2 画素値を正規化する
MNIST の画素値は 0〜255 の整数です。
このまま学習させるよりも、0〜1 の範囲にそろえた方が学習が安定しやすくなります。
そこで、画素値を 255 で割って正規化します。
x_train = x_train.astype("float32") / 255.0
x_test = x_test.astype("float32") / 255.0
正しく変換できているか、最小値と最大値を確認します。
print(x_train.min(), x_train.max())
ここが
0.0 1.0
付近になっていればOKです。
これで、画像データは
- CNN に入力できる形になり
- 学習しやすい値の範囲に整えられた
状態になりました。
次のステップでは、いよいよ TensorFlow を使ってモデルを定義していきます。
5. モデルを作る
ここからがモデル構築です。
TensorFlow では、レイヤー(層)を順番に積み重ねてモデルを作ることが多いです。
今回は 最小構成の CNN を作ります。流れとしては、
- 画像から特徴を取り出す(Conv2D)
- 特徴を少しまとめて重要な部分を残す(MaxPooling2D)
- 画像っぽい形を1列のデータに変換する(Flatten)
- 最後に 0 / 1 を判断する(Dense + sigmoid)
というイメージです。
5.1 レイヤー構成のイメージ
-
Conv2D(16, 3, ...)
画像の中から線や形などの特徴を取り出します。
16は「特徴の種類の数」、3は「見る範囲(3×3)」くらいの感覚でOKです。 -
MaxPooling2D()
特徴を少し圧縮して、重要な部分を残します。計算量も減ります。 -
Flatten()
(28, 28, 何か)のような画像の形を、1列のベクトルに変換します。 -
Dense(1, activation="sigmoid")
最後に 0〜1 の値を1つ出します。
これを「1 っぽさ(確率っぽい値)」として扱い、0.5 を境に 0 / 1 を決めます。
5.2 実際にモデルを書く
それでは、先ほど説明した構成をそのままコードで表現していきます。
ここでは「画像を入力として受け取り、0 か 1 かを出力する」モデルを
Sequential を使って上から順に定義しています。
以下のように書いていきます。
model = tf.keras.Sequential([
tf.keras.layers.Input(shape=(28, 28, 1)),
tf.keras.layers.Conv2D(16, 3, activation="relu"),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(1, activation="sigmoid")
])
5.3 構造を確認する(summary)
モデルが想定通りの形になっているかは summary() で確認できます。
初心者ほど、ここで一度チェックしておくのがおすすめです。
model.summary()
6. 学習の設定
モデルの構造を定義しただけでは、まだ学習はできません。
次に行うのが学習の設定(compile)です。
compile では、
- どうやって学習するか
- 何を「間違い」とみなすか
- 学習の結果をどう評価するか
といった 学習のルールを決めます。
model.compile(
optimizer="adam",
loss="binary_crossentropy",
metrics=["accuracy"]
)
ここで指定しているそれぞれの項目について、簡単に説明します。
6.1 optimizer(最適化手法)
optimizer は、 モデルのパラメータをどうやって更新するかを決めるものです。
今回は adam を使っています。
- 初心者でも扱いやすい
- 多くの場合、特に調整しなくても安定して学習する
という理由から、まずは adam を選んでおけば問題ありません。
6.2 loss(損失関数)
loss は、モデルの予測がどれくらい間違っているかを数値で表すものです。
今回は 2 クラス分類(0 / 1)なので、
binary_crossentropy
を使います。
これは、
- 出力が 0 か 1 の問題
- 出力が 0〜1 の値(sigmoid)
という条件に合った損失関数です。
6.3 metrics(評価指標)
metrics は、学習の途中経過をどう表示するかを指定します。
ここでは
accuracy
を指定しているので、
- 全体のうち、どれくらい正解しているか
が表示されます。
学習が進むにつれて、
この値が上がっていけば「ちゃんと学習できている」ことが分かります。
これで、モデルを学習させるための準備が整いました。
次のステップでは、実際にデータを使ってモデルを学習させていきます。
7. 学習させる
学習の設定が終わったので、いよいよ 実際にモデルを学習させます。
TensorFlow では、fit を使って学習を行います。
7.1 学習を実行する
model.fit(
x_train,
y_train,
epochs=5,
batch_size=32,
validation_split=0.2
)
この処理を実行すると、
- 画像を入力として渡す
- モデルが予測を行う
- 正解と比べてどれくらい間違っているかを計算する
- その結果を使って、モデルを少しずつ修正する
という流れが自動で繰り返されます。
7.2 各引数の意味(epochs / batch_size / validation_split)
ここで指定している引数を、簡単に確認しておきます。
epochs
epochs=5
学習を何回繰り返すかを指定します。
- 1 epoch = 学習データを一通り使って学習する
- 今回は 5 回分学習する
まずは小さめの値で動かしてみるのがおすすめです。
batch_size
batch_size=32
学習データを 何枚ずつまとめて処理するかを指定します。
- 32 枚ずつ画像をまとめて
- 予測・修正を行う
という意味です。
validation_split
validation_split=0.2
学習データのうち、
20% を検証用データとして使います。
- 学習には使わない
- 途中経過の性能チェック用
のデータを用意することで、
- 学習しすぎていないか
- ちゃんと汎用的に学習できているか
を確認できます。
7.3 学習中に表示されるもの
fit を実行すると、次のような情報が表示されます。
- loss:どれくらい間違っているか
- accuracy:どれくらい正解しているか
- val_loss / val_accuracy:検証データでの結果
学習が進むにつれて、
- loss が下がる
- accuracy が上がる
という傾向が見られれば、
モデルがうまく学習できていると判断できます。
これで、モデルは実際にデータを使って学習された状態になりました。
次のステップでは、学習に使っていないデータを使ってモデルの性能を確認し、予測を試してみます。
8. 評価・予測
モデルの学習が終わったら、
本当にうまく動いているかを確認します。
ここでは、
- 学習に使っていないデータで性能を評価する
- 実際に予測結果を見てみる
という2つを行います。
8.1 テストデータで評価する(evaluate)
まずは、テストデータを使ってモデルの性能を数値で確認します。
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)
print(test_acc)
ここで使っている x_test / y_test は、学習には一切使っていないデータです。
そのため、この結果を見ることで、
- 見たことのないデータに対して
- どれくらい正しく予測できるか
を確認できます。
test_acc は 正解率(accuracy)を表しており、0.9 なら「約90%当たっている」という意味です。
8.2 予測してみる(predict)
次に、モデルがどんな予測をしているのかを実際に見てみます。
proba = model.predict(x_test[:10])
この proba には、
- 各画像が「1 である確率っぽい値」
- 0〜1 の小数
が入っています。
8.3 0 / 1 に変換する(閾値 0.5)
今回は 2 クラス分類なので、この値を 0 / 1 に変換して結果を見やすくします。
pred = (proba >= 0.5).astype(int).reshape(-1)
ここでは、
- 0.5 以上 → 1
- 0.5 未満 → 0
というルールで分類しています。
これは、最後の層で sigmoid を使っているため、出力が 0〜1 の範囲になることを利用しています。
8.4 予測結果と正解を比べる
最後に、予測結果と正解ラベルを並べて表示します。
print("pred :", pred)
print("true :", y_test[:10])
ここで、
- pred:モデルの予測
- true:本当の正解
を見比べて、
- どれくらい当たっているか
- 間違えている場合はどんな間違いか
を確認できます。
これで、
- モデルを作る
- 学習させる
- 評価する
- 予測する
というTensorFlow を使ったモデル構築の一連の流れをすべて体験できました。
おわりに
この記事では、TensorFlow を使って
- データの読み込み
- 前処理
- モデルの定義
- 学習
- 評価・予測
という モデル構築の一連の流れを、実際にコードを書きながら体験しました。
MNIST データセットを用いたシンプルな画像分類モデルを一から作ることで、
TensorFlow を使ったモデル構築がどのような手順で行われるのかを、全体として把握できたと思います。
最初は分からないことが多くても、一度「動くモデル」を作れた経験は、今後 TensorFlow や機械学習を学んでいく上で大きな土台になります。
この記事が、TensorFlow や機械学習に触れるきっかけになれば嬉しいです!
最後まで読んでいただき、ありがとうございました!
余談
ここからは余談です。
TensorFlowを初めて使う際に、自分はTensorFlow の環境構築でかなりハマりました。
起きた問題
最初は特に何も考えず、次のようにインストールしました。
pip install tensorflow
インストール自体は成功しましたが、
コードを実行すると次のようなエラーが出ました。
This version of jaxlib was built using AVX instructions
または、
The TensorFlow library was compiled to use AVX instructions
といったエラーです。
原因
原因は、
- Mac は Apple Silicon(ARM)
- しかし Python / TensorFlow が Intel(x86_64)向けだった
という アーキテクチャの不一致でした。
実際に確認してみると、
python -c "import platform; print(platform.machine())"
の結果が
x86_64
となっており、Rosetta 経由の Python が使われていました。
解決方法
解決のために行ったことは、次の3つです。
- Apple Silicon 用の Python(arm64)を使う
- 仮想環境を作り直す
-
tensorflow-macosを使う
最終的には、次の構成で安定しました。
- Python 3.10(arm64)
- venv
- tensorflow-macos + tensorflow-metal
インストールコマンドは以下です。
pip install tensorflow-macos tensorflow-metal
その後、
python -c "import platform; print(platform.machine())"
が
arm64
となり、 TensorFlow のコードもエラーなく実行できるようになりました。