Chat GPTへの質問
Pythonにてアニメキャラ画像を入力し、キャラクター名を出力する機械学習を行いたいです
コーディング例を教えてください
(おおざっぱ)
なぜそんな質問をしたか
上記書籍にて、ざっっっくりとPython×機械学習について学び、「機械学習っぽい」ことを実際にやってみたかったから。
回答は
アニメキャラ画像を入力し、キャラクター名を出力する機械学習を行うためには、以下の手順が必要です。
学習用の画像とキャラクター名を用意する
画像から特徴量を抽出する
特徴量を用いて機械学習モデルを学習する
テスト用の画像を用意し、モデルで予測を行いキャラクター名を出力する
以下に、具体的なコード例を示します。
学習用の画像とキャラクター名を用意する
学習用の画像とキャラクター名を用意します。
例えば、「Naruto」というキャラクターの画像を10枚、
「Luffy」というキャラクターの画像を10枚用意するとします。
画像は、任意の場所からダウンロードすることができます。画像から特徴量を抽出する
画像から特徴量を抽出するためには、以下の手順が必要です。
import numpy as np
from PIL import Image
from sklearn.feature_extraction.image import extract_patches_2d
# 学習用データの読み込み
naruto_images = []
for i in range(10):
image = Image.open(f'naruto_{i}.jpg').convert('L')
naruto_images.append(np.array(image))
luffy_images = []
for i in range(10):
image = Image.open(f'luffy_{i}.jpg').convert('L')
luffy_images.append(np.array(image))
# データセットの作成
X = []
y = []
for i, images in enumerate([naruto_images, luffy_images]):
for image in images:
patches = extract_patches_2d(image, (32, 32), max_patches=100)
for patch in patches:
X.append(patch.flatten())
y.append(i)
3.特徴量を用いて機械学習モデルを学習する
特徴量を用いて機械学習モデルを学習するためには、以下の手順が必要です。
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
# 学習データとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# SVMモデルの作成と学習
clf = SVC(kernel='linear')
clf.fit(X_train, y_train)
# テストデータでの正解率を計算
y_pred = clf.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy}")
テスト用の画像を用意し、モデルで予測を行いキャラクター名を出力する
テスト用の画像を用意し、モデルで予測を行いキャラクター名を出力するためには、以下の手順が必要です。
# テスト用画像の読み込み
test_image = Image.open('test.jpg').convert('L')
test_patches = extract_patches_2d(np.array(test_image), (32, 32), max_patches=100)
# 予測
predictions = clf.predict(test_patches.reshape(-1, 1024))
# 出力
print(predictions)
以上の手順で、アニメキャラ画像を入力し、キャラクター名を出力する機械学習が行えます。
とりあえず1スクリプトにまとめてみる
import numpy as np
from PIL import Image
from sklearn.feature_extraction.image import extract_patches_2d
# 学習用データの読み込み
naruto_images = []
for i in range(10):
image = Image.open(f'naruto_faces/naruto_{i}.jpg').convert('L')
naruto_images.append(np.array(image))
luffy_images = []
for i in range(10):
image = Image.open(f'luffy_faces/luffy_{i}.jpg').convert('L')
luffy_images.append(np.array(image))
# データセットの作成
X = []
y = []
for i, images in enumerate([naruto_images, luffy_images]):
for image in images:
patches = extract_patches_2d(image, (32, 32), max_patches=100)
for patch in patches:
X.append(patch.flatten())
y.append(i)
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
# 学習データとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# SVMモデルの作成と学習
clf = SVC(kernel='linear')
print("学習中...")
clf.fit(X_train, y_train)
# テストデータでの正解率を計算
y_pred = clf.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy}")
# テスト用画像の読み込み
test_image = Image.open('test.jpg').convert('L')
test_patches = extract_patches_2d(np.array(test_image), (32, 32), max_patches=100)
# 予測
predictions = clf.predict(test_patches.reshape(-1, 1024))
# 結果の判定
luffy_count = 0
naruto_count = 0
for prediction in predictions:
if prediction == 0:
naruto_count += 1
else:
luffy_count += 1
if naruto_count > luffy_count:
print('結果:ナルト')
else:
print('結果:ルフィー')
実行結果
ipynbファイルと同階層に「naruto_faces」「luffy_faces」というフォルダを作成。
それぞれに、naruto_1.jpg...、luffy_1.jpg...を10ファイル格納。
※これもChat GPTに聞きつつ別プログラム書きました(別記事)
https://qiita.com/horita_toshiki_r0/items/14229168fc0789e2da54
学習中...
Accuracy: 0.5175
結果:ナルト
Accuracy:0.5、ほぼあてにならない?
4回試して3勝1敗
学習量10ずつだとまだ外れることも多そう
ちなみに、実行結果出るまでは5分程度かかりました。
clf.fit(X_train, y_train)(学習)のところで結構かかるらしい。
プログラムをざっくりとひも解く
☆第一ステップ:学習用の画像を配列に
naruto_images = []
for i in range(10):
image = Image.open(f'naruto_faces/naruto_{i}.jpg').convert('L')
naruto_images.append(np.array(image))
ざっくり:
1.画像を読み込む
2.グレースケールに変換
3.NumPy配列に変換
極限までざっくり
⇒画像ファイルを数字に変換して配列に格納する
☆第二ステップ:データセット作成
X = []
y = []
for i, images in enumerate([naruto_images, luffy_images]):
for image in images:
patches = extract_patches_2d(image, (32, 32), max_patches=100)
for patch in patches:
X.append(patch.flatten())
y.append(i)
ざっくり:
0.配列X,yを準備
⇒Xには「パッチ」のデータが格納。
yには各パッチのラベル、つまりナルトは「0」、ルフィーは「1」
1.第一ステップで配列化した各画像について、「パッチ」として100個に分割
※1パッチのサイズは32px*32px
2.それぞれの「パッチ」化した配列について一次元配列に変換
もっとざっくり:
「ナルト」と「ルフィー」のそれぞれ10枚の学習素材をそれぞれさらに100に細分化して、学習用データにする。
疑問:
●なんでわざわざパッチ化するの?画像をそのまま学習すればいいじゃない
⇒画像全体を1枚のデータとして扱うと、画像全体の特徴が把握しにくくなるためです。パッチに分割することで、小さな領域に特徴が集中し、学習が容易になります。
●どうして一次元配列化したの?
⇒SVMなどの機械学習アルゴリズムは、入力として1次元配列を受け取るためです
☆第三ステップ:パッチを「学習用」「テスト用」に分ける
# 学習データとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
ざっくり:
第二ステップで作ったパッチ全部を学習用に使うわけではない
↓
パッチのうち80%だけを学習用に使い、残り20%で学習による予測精度の確認に使う
☆第四ステップ:学習!
clf = SVC(kernel='linear')
print("学習中...")
clf.fit(X_train, y_train)
ざっくり:
SVM(Support Vector Machine)モデルの学習をします
時間がかかります
X_trainが入力データ、y_trainが教師データです。
つまり、「こんな感じ」のやつは、「ルフィー」だよ!を詰め込んでいくイメージでしょうか
☆第五ステップ:精度確認
y_pred = clf.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy}")
ざっくり
1.20%分取っておいたパッチについて、学習成果をもとに、予測
2.y_test(教師データ、つまり正答)と↑の予測を比較
☆第六ステップ:「本番」の予測
test_image = Image.open('test.jpg').convert('L')
test_patches = extract_patches_2d(np.array(test_image), (32, 32), max_patches=100)
# 予測
predictions = clf.predict(test_patches.reshape(-1, 1024))
ざっくり
1.「ナルト」か「ルフィー」か判定したい画像を入力⇒グレースケールに
2.↑をパッチ化(100個に分割)
3.予測
⇒predictionsには、100個に分割されたパッチについて、「ルフィー!」「ルフィー!」「ナルト!」「ルフィー!」「ルフィー!」「ナルト!」と判定結果が格納される
☆最終ステップ:結果出力
luffy_count = 0
naruto_count = 0
for prediction in predictions:
if prediction == 0:
naruto_count += 1
else:
luffy_count += 1
if naruto_count > luffy_count:
print('結果:ナルト')
else:
print('結果:ルフィー')
ざっくり:
配列predictionsの要素のうち、「ルフィー」が多ければ「ルフィー」、「ナルト」が多ければ「ナルト」が答えと出力
最後に
今後機械学習を勉強するうえで頻出しそうなメソッド
●convert('L')
・・・ライブラリpillowのメソッドで、引数が'L'の時はグレースケールに変換してくれる
※引数を'1'にすると、1ビットの白黒画像にするらしい
●extract_patches_2d(image, (32, 32), max_patches=100)
・・・2次元の配列(画像)を小さなブロックに分割
引数は以下の通りです。
●image: パッチを抽出したい画像(2次元のNumPy配列)。
●patch_size: 抽出するパッチのサイズを指定するタプル。(n, m)の形式で指定します。
●max_patches: 抽出するパッチの最大数。デフォルトはNoneで、全パッチを抽出します。
●train_test_split(X, y, test_size=0.2)
・・・、機械学習のデータセットを訓練用データセットとテスト用データセットに分割
引数は以下の通りです。
●特徴量データ配列
●正解データ配列
●テストデータに割り当てる割合
clf.fit(X_train, y_train)
・・・学習
入力データと正解データを引数として、両者の関連性をインプット。
clf.predict(X_test)
・・・予測
一次元配列化したパッチを、学習結果をもとに予測
それぞれのパッチについて、結果を配列に格納する。