[Python] AI 30 学習環境の検討 Apple M1 Pro & Max ベンチスコア

Apple M1 ProとMaxのベンチスコアが判明しましたので、グラフデータを更新します。OpenCLスコアはあくまでもGPU性能の数ある指標の一つです。

前回記事で予測したスコアと比較してProは少し上、Maxは下でした。Proの方が若干のお得感があります。MaxはTesla P100には及ばず、Google Colabは優位性を固守しています。

そろそろIntel MacからAppleシリコン機へ軸足を移すため、Mac mini M1 Proの10コアCPU、16コアGPUを購入するつもりです。発売が11月になるのか来春以降になるのか分かりませんが、とても待ち遠しいです。

なおAI学習をする上で物足りないようであれば、Windows機でGPUを強化します。

参考サイト(ベンチマーク)

[Python] AI 29 学習環境の検討 Apple M1 Pro & Max (付録:横棒グラフの一部を別設定)

先日発表されたApple M1 ProとMaxのAI学習性能をざっくり推定してみました。

Apple M1のベンチスコアをGPUコア数の比率から2倍(Pro)、4倍(Max)にしてNVIDIAの人気機種と比較しました。

この見立てが正しければ、M1 MaxはGoogle Colab Pro(有料)でレンタル可能なTesla P100と同レベルのようです。

私の場合はたまに学習モデルを作成する程度なので、それだけのために新型MacBook Proを購入するよりもGTX 1660Tiを購入して自作PCに装着する方がリーズナブルな感じがします。もっとも価格が発売当初の3万円から倍近くに高騰していて、今すぐ買おうとは思いませんが。

# M1 MaxとM1 Proは枠線の種類を破線にする。
# linestyleのリストは使用不可のためfor文とif文で設定。

import matplotlib.pyplot as plt

x = [1, 2, 3, 4, 5, 6, 7, 8, 9]
y = [4868, 12765, 18260, 36520, 60396, 73040, 73986, 120247, 205239]
label_x = ["Mac mini 2018","GeForce GTX 750Ti","Apple M1","Apple M1 Pro(est.)","GeForce GTX 1660Ti","Apple M1 Max(est.)","Tesla P100","GeForce RTX 3060Ti","GeForce RTX 3090"]

image ='test.png'
fig = plt.figure()
color_list = ['#2e8b57','#8a2be2','#2e8b57','#7fffd4','#8a2be2','#7fffd4','#8a2be2','#8a2be2','#8a2be2']
edgecolor_list = ['#2e8b57','#8a2be2','#2e8b57','#2f4f4f','#8a2be2','#2f4f4f','#8a2be2','#8a2be2','#8a2be2']

for i,j,k,l in zip(x,y,color_list,edgecolor_list):
    if i==4 or i==6:
        plt.barh(i, j,color=k,align="center",edgecolor=l,linestyle='dashed')
    else:
        plt.barh(i, j,color=k,align="center",edgecolor=l,linestyle='solid')

plt.yticks(x, label_x)
plt.title('Geekbench OpenCL', fontsize=18)
plt.tick_params(labelsize=14)
plt.tight_layout()
plt.show()

fig.savefig(image)

参考サイト(ベンチマーク)

[Python] AI 28 Keras学習モデル png画像からの予測

ネットから入手したファッションアイテム画像について予測してみました。

入手した画像を正方形の背景に埋め込み、リサイズ、グレースケールに変換してから学習モデルに供しました。

正解率は67%でした。Tシャツをシャツと予測ミスしていますが、即席の学習モデルではそんなところでしょう。

import tensorflow.keras as keras
from PIL import Image
import numpy as np
import os,glob,re

labels = ['tshirt','trouser','pullover','dress','coat','sandal','shirt','sneaker','bag','boot']

# 画像ファイル名にはラベル(正解)が含まれている(tshirt1.pngなど)
files = glob.glob('/test/*.png')
print(files)

X = list()
y = list()
for file in files:
    image = Image.open(file)
    image = image.resize((28, 28))
    image = image.convert('L')
    data = np.asarray(image)

    label1 = re.search(r'[a-z]+',os.path.split(file)[1].replace('.png',''))
    label2 = label1.group()
    print(label2)
    label3 = labels.index(label2)
    print(label3)

    X.append(data)
    y.append(label3)

X_test = np.array(X)
# 白黒を反転させる
X_test2 = 255 - np.array(X)
print(X_test2)

y_test = np.array(y)
print(y_test)

model = keras.models.load_model('keras-fmnist-model.h5', compile=True)

y_prob = model.predict(X_test2)
print(y_prob)

y_pred = [ i for prob in y_prob for i,p in enumerate(prob) if p == max(prob)]
# predict_classesは廃止予定
# y_pred = model.predict_classes(X_test2)
print(y_pred)

y_pred2 = np.array(labels)[y_pred]
y_answer = np.array(labels)[y_test]

print(f'予測 {y_pred2} 正解 {y_answer}')
--------------------------------------------------

出力の一部
--------------------------------------------------
[[0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00 1.0000000e+00]
 [0.0000000e+00 1.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  0.0000000e+00 1.0539890e-37 0.0000000e+00 0.0000000e+00 0.0000000e+00]
 [3.0448330e-03 0.0000000e+00 0.0000000e+00 0.0000000e+00 0.0000000e+00
  0.0000000e+00 9.9695516e-01 0.0000000e+00 0.0000000e+00 0.0000000e+00]]
[9 1 6]
予測 ['boot' 'trouser' 'shirt'] 正解 ['boot' 'trouser' 'tshirt']

[Python] AI 27 Keras学習モデル 訓練, 検証, テスト

Fashion-MNISTを使ったKeras学習モデルでテストするところまで作成しました。訓練回数は30回です。テスト結果はグラフの右上に表示しています。

テストの結果データはhistory.historyにtestキーとして追加し、JSONファイルに保存しています。

import tensorflow.keras as keras
from tensorflow.keras import models
from keras.layers import Dense, Flatten
import matplotlib.pyplot as plt
import time,datetime,os,json

def plot_loss_accuracy_graph(history,eval):
	<略>
	return dt_now_str # グラフ作成日時

def main():
	epochs=30

	# データセット取得
	fashion_mnist = keras.datasets.fashion_mnist
	(X_train, y_train), (X_test, y_test) = fashion_mnist.load_data()

	print(f'X_train_full.shape {X_train.shape}')
	print(f'X_test.shape {X_test.shape}')

	X_train = X_train / 255.
	X_test = X_test / 255.

	# モデル作成
	model = models.Sequential()
	model.add(Flatten(input_shape=[28, 28]))
	model.add(Dense(300, activation="relu"))
	model.add(Dense(100, activation="relu"))
	model.add(Dense(10, activation="softmax"))

	print(f'model.layers {model.layers}')

	model.summary()

	model.compile(loss="sparse_categorical_crossentropy",
				optimizer="sgd",
				metrics=["accuracy"])

	# 訓練
	history = model.fit(X_train, y_train, epochs=epochs,
						validation_split=0.1)
	print(f'history.history {history.history}')

	# 検証結果
	val_loss = history.history['val_loss'][-1]
	val_accuracy = history.history['val_accuracy'][-1]
	print('val_loss:', val_loss)
	print('val_accuracy:', val_accuracy)

	# テスト
	test = model.evaluate(X_test, y_test)
	print(f'test {test}')

	# グラフ化
	ret = plot_loss_accuracy_graph(history,test)

	test2 = {"test":test}
	history.history.update(**test2)

	json_file = f'{ret}_history_data.json'
	with open(json_file ,'w' ) as f:
		json.dump(history.history ,f ,ensure_ascii=False ,indent=4)

	model.save(f'{ret}_keras-mnist-model.h5')

if __name__ == "__main__":
	start = time.time()
	main()

[Python] AI 26 Keras学習モデル matplotlib詳細設定

matplotlibの設定箇所が間延びした見た目だったのでスッキリさせました。

グラフ内と枠内にテキスト表示させています。val_accuracyの最後の値をグラフ内表示しています。枠内表示は実行時間とファイル名です。

start = time.time()

def plot_loss_accuracy_graph(history):
	process_time = time.time() - start
	td = datetime.timedelta(seconds = process_time)

	image ='accuracy_loss.png'

	fig = plt.figure(facecolor='#e0ffff')
	fig.subplots_adjust(bottom=0.15,wspace=0.3)

	ax = fig.add_subplot(121, title = 'ACCURACY',xlabel = 'Epochs',ylabel = 'Accuracy')
	ax.plot(history.history['accuracy'],"-o", color="green", label="train_accuracy", linewidth=2)
	ax.plot(history.history['val_accuracy'],"-o",color="black", label="val_accuracy", linewidth=2)
	ax.legend(loc="lower right")
	
	# グラフ内テキスト表示
	ax.text(len(history.history['val_accuracy']) -1, history.history['val_accuracy'][-1]-0.002, '{:.3f}'.format(history.history['val_accuracy'][-1]),verticalalignment='top',horizontalalignment='center')

	ax2 = fig.add_subplot(122, title = 'LOSS',xlabel = 'Epochs',ylabel = 'Loss')
	ax2.plot(history.history['loss'], "-D", color="blue", label="train_loss", linewidth=2)
	ax2.plot(history.history['val_loss'], "-D", color="black", label="val_loss", linewidth=2)
	ax2.legend(loc='upper right')

	# 枠内テキスト表示
	fig.text(0.68, 0.01, os.path.basename(__file__))
	fig.text(0.85, 0.97, str(td)[:11])

	fig.savefig(image)

[Python] AI 25 Keras学習モデル Fashion-MNIST

今回から教本はオライリー本 “scikit-learn, Keras, TensorFlowによる実践機械学習 第2版”になりました。10.2 KerasによるMLPの実装 から読み進めています。

ファッションアイテム画像のデータセットであるFashion-MNISTを使って識別学習させました。

訓練セットの画像データをX_trainと大文字で表記していますが、数学のルールで2次元配列は大文字、1次元配列は小文字になるからだそうです。

新しい教本ではデータセットを訓練セット、テストセット、検証セットの3種類に分けていました。私が目にしたネット情報では検証セットがないケースがほとんどでした。どちらがより適切なのかは不明ですが、今回は教本に従います。

import tensorflow.keras as keras
from tensorflow.keras import models
from keras.layers import Dense, Flatten
import matplotlib.pyplot as plt
import time,datetime,os,json

def plot_loss_accuracy_graph(history,val_accuracy):
	<略>
	return dt_now_str # 戻り値はグラフ作成日時

def main():
	epochs=10

	fashion_mnist = keras.datasets.fashion_mnist
	(X_train_full, y_train_full), (X_test, y_test) = fashion_mnist.load_data()

	print(f'X_train_full.shape {X_train_full.shape}')

	print(f'X_train_full.dtype {X_train_full.dtype}')

	X_valid, X_train = X_train_full[:5000] / 255., X_train_full[5000:] / 255.
	y_valid, y_train = y_train_full[:5000], y_train_full[5000:]
	X_test = X_test / 255.

	model = models.Sequential()
	model.add(Flatten(input_shape=[28, 28])) # 入力層
	model.add(Dense(300, activation="relu")) # Dense隠れ層
	model.add(Dense(100, activation="relu")) # Dense隠れ層
	model.add(Dense(10, activation="softmax")) # Dense出力層

	print(f'model.layers {model.layers}')

	model.summary()

	model.compile(loss="sparse_categorical_crossentropy",
				optimizer="sgd",
				metrics=["accuracy"])

	history = model.fit(X_train, y_train, epochs=epochs,
						validation_data=(X_valid, y_valid))
	print(f'history.history {history.history}')

	# 検証結果
	val_loss = history.history['val_loss'][-1]
	val_accuracy = history.history['val_accuracy'][-1]
	print('val_loss:', val_loss)
	print('val_accuracy:', val_accuracy)

	# 学習データ・グラフ化
	ret = plot_loss_accuracy_graph(history,val_accuracy)

	json_file = f'{ret}_history_data.json'
	with open(json_file ,'w' ) as f:
		json.dump(history.history ,f ,ensure_ascii=False ,indent=4)

	model.save(f'{ret}_keras-mnist-model.h5')

if __name__ == "__main__":
	main()

[Python] AI 24 Keras学習モデル Custom train loop

Kerasのfit(), evaluate()を使わずforループで訓練する Custom train loopという方法で学習させてみました。

TensorFlowでは計算の高速化のためデータ型はfloat32を採用しています。そのためfloat64しか受け付けないjson.dumpを使う前には、float32からfloat64に変換しておく必要があります。

プログラマの習性でコードとしての整合性ばかり見がちなので、深層学習の理論も並行して身に付けるよう心掛けたいです。

import tensorflow as tf
from tensorflow.keras import models
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
import matplotlib.pyplot as plt
import time,datetime,os
from stegano import lsb
import numpy as np
import json

def plot_loss_accuracy_graph(data):
	<略>
	return dt_now_str # 戻り値はグラフ作成日時

def create_model():
	model = models.Sequential()

	model.add(Conv2D(32, kernel_size=(3, 3),activation='relu'))
	model.add(Conv2D(64, (3, 3), activation='relu'))
	model.add(MaxPooling2D(pool_size=(2, 2)))
	model.add(Dropout(0.25))
	model.add(Flatten())
	model.add(Dense(128, activation='relu'))
	model.add(Dropout(0.5))
	model.add(Dense(10, activation='softmax'))

	return model

def main():
	(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
	x_train = x_train.astype(np.float32).reshape(60000,28,28,1) / 255.0
	x_test = x_test.astype(np.float32).reshape(10000,28,28,1) / 255.0

	trainset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
	trainset = trainset.shuffle(buffer_size=1024).batch(128)
	print(f'len(trainset) {len(trainset)}')

	testset = tf.data.Dataset.from_tensor_slices((x_test, y_test))
	testset = testset.batch(128)
	print(f'len(testset) {len(testset)}')

	model = create_model()
	loss = tf.keras.losses.SparseCategoricalCrossentropy()
	acc = tf.keras.metrics.SparseCategoricalAccuracy()
	optim = tf.keras.optimizers.Adam()

	@tf.function
	def train_on_batch(x, y):
		with tf.GradientTape() as tape:
			pred = model(x, training=True)
			loss_val = loss(y, pred)
		
		graidents = tape.gradient(loss_val, model.trainable_weights)
		optim.apply_gradients(zip(graidents, model.trainable_weights))
		acc.update_state(y, pred)

		return loss_val

	# 学習
	epochs = 10

	loss_list = list()
	accuracy_list = list()
	val_loss_list = list()
	val_accuracy_list = list()

	data = dict()
	for i in range(epochs):
		acc.reset_states()
		print("Epoch =", i + 1)

		for step, (x, y) in enumerate(trainset):
			loss1 = train_on_batch(x, y)

			if step % 100 == 0:
				print(f"step = {step}, loss = {loss1}, accuracy = {acc.result()}")
			elif step == 468:
				print(f"step = {step}, loss = {loss1}, accuracy = {acc.result()}")
				loss_list.append(loss1.numpy())
				accuracy_list.append(acc.result().numpy())

		acc.reset_states()
		for step, (x, y) in enumerate(testset):
			pred = model(x, training=False)
			loss2 = loss(y, pred)
			acc.update_state(y, pred)

			if step == 78:
				print(f"test step = {step}, loss = {loss2}, test accuracy = {acc.result()}")
				val_loss_list.append(loss2.numpy())
				val_accuracy_list.append(acc.result().numpy())

	# float32からfloat64に変換
	loss_list2 = [float(a) for a in loss_list]
	accuracy_list2 = [float(a) for a in accuracy_list]
	val_loss_list2 = [float(a) for a in val_loss_list]
	val_accuracy_list2 = [float(a) for a in val_accuracy_list]

	# 各リストをdict型にまとめてhistory.historyと同じデータ構成にする
	data1 = {"loss":loss_list2}
	data2 = {"accuracy":accuracy_list2}
	data3 = {"val_loss":val_loss_list2}
	data4 = {"val_accuracy":val_accuracy_list2}

	data.update(**data1,**data2,**data3,**data4)
	print(f'data {data}')

	# 学習データ・グラフ化
	ret = plot_loss_accuracy_graph(data)

	json_file = '{}_history_data.json'.format(ret)
	with open(json_file ,'w' ) as f:
		json.dump(data ,f ,ensure_ascii=False ,indent=4)

	model.save(f'{ret}_keras-mnist-model.h5')

if __name__ == "__main__":
	main()

参考サイト

[Python] AI 23 Keras学習モデルのTensorFlow2への移植

[macOS Catalina 10.15.7, Python 3.9.7]

TensorFlow1で使っていた学習モデル作成コードをTensorFlow2にうまく移植できず困っていたのですが、ほんの些細なところを修正するだけで解決しました。

TensorFlow2ではConv2DとMaxPooling2Dが求めるinput_shapeの次元が異なるため、あえてinput_shapeの指定を外してライブラリ任せにするとうまくいきました。

学習時間は約1分30秒の短縮です。

import tensorflow as tf
from tensorflow.keras import models
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
import matplotlib.pyplot as plt
import time,datetime,os
from stegano import lsb
import numpy as np
import json

def plot_loss_accuracy_graph(history):
	<略>
	return dt_now_str # 戻り値はグラフ作成日時

def create_model():
	model = models.Sequential()

	model.add(Conv2D(32, kernel_size=(3, 3),activation='relu')) # input_shape = (28,28,1)を削除
	model.add(Conv2D(64, (3, 3), activation='relu'))
	model.add(MaxPooling2D(pool_size=(2, 2)))
	model.add(Dropout(0.25))
	model.add(Flatten())
	model.add(Dense(128, activation='relu'))
	model.add(Dropout(0.5))
	model.add(Dense(10, activation='softmax'))

	return model

def main():
	# 前処理
	(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
	x_train = x_train.astype(np.float32).reshape(60000,28,28,1) / 255.0
	x_test = x_test.astype(np.float32).reshape(10000,28,28,1) / 255.0

	# 学習モデル作成
	model = create_model()
	loss = tf.keras.losses.SparseCategoricalCrossentropy()
	acc = tf.keras.metrics.SparseCategoricalAccuracy()
	optim = tf.keras.optimizers.Adam()
	model.compile(optimizer=optim, loss=loss, metrics=[acc])

	# 学習
	epochs = 10
	history = model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=epochs, batch_size=128)
	print(f'history.history {history.history}')

	# 学習データ・グラフ化
	ret = plot_loss_accuracy_graph(history)
	json_file = '{}_history_data.json'.format(ret)

	with open(json_file ,'w' ) as f:
		json.dump(history.history ,f ,ensure_ascii=False ,indent=4)

	# 検証結果
	test_loss = history.history['val_loss'][-1]
	test_accuracy = history.history['val_sparse_categorical_accuracy'][-1]
	print('Test loss:', test_loss)
	print('Test accuracy:', test_accuracy)

	model.save('keras-mnist-model.h5')

if __name__ == "__main__":

	main()

[Python] AI 22 学習モデル作成時の精度等データ保存

[macOS catalina 10.15.7, Python 3.9.7]

学習モデル作成時の精度等データをJSONファイルに保存できるようにしました。

これで学習モデル間の比較が容易になります。前回記事でソースコードのパスや学習時間をグラフ画像に不可視的に埋め込みましたが、同時にこのファイルの要素として入れておくといいでしょう。

学習モデル作成部分はネットから拝借しました。過学習傾向ですが、精度は高めです。

import tensorflow as tf
import json

def plot_loss_accuracy_graph(history):
	<略>
	return dt_now_str # グラフ作成日時の文字列

def create_model():
    model = models.Sequential()
    model.add(layers.Input((784,)))
    model.add(layers.Dense(128, activation="relu"))
    model.add(layers.Dense(64, activation="relu"))
    model.add(layers.Dense(10, activation="softmax"))
    return model

def main():
	# 前処理
	(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
	x_train = x_train.astype(np.float32).reshape(-1, 784) / 255.0
	x_test = x_test.astype(np.float32).reshape(-1, 784) / 255.0
	
	# 学習モデル作成
	model = create_model()
	loss = tf.keras.losses.SparseCategoricalCrossentropy()
	acc = tf.keras.metrics.SparseCategoricalAccuracy()
	optim = tf.keras.optimizers.Adam()
	model.compile(optimizer=optim, loss=loss, metrics=[acc])

	# 学習
	epochs = 10
	
	history = model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=epochs, batch_size=128)
	print(f'history.history {history.history}')

	# 学習データグラフ化
	ret = plot_loss_accuracy_graph(history)
	print(f'history.history {history.history}')
  
	# データ保存
	json_file = '{}_history_data.json'.format(ret)
	with open(json_file ,'w' ) as f:
		json.dump(history.history ,f ,ensure_ascii=False ,indent=4)

	# 検証結果
	test_loss = history.history['val_loss'][-1]
	test_accuracy = history.history['val_sparse_categorical_accuracy'][-1]
	print('Test loss:', test_loss)
	print('Test accuracy:', test_accuracy)

	model.save('keras-mnist-model.h5')

if __name__ == "__main__":
	start = time.time()

	dt_now = datetime.datetime.now()
	print('処理開始 '+ str(dt_now))

	main()

	# 処理時間算出
	process_time = time.time() - start
	td = datetime.timedelta(seconds = process_time)
	dt_now = datetime.datetime.now()

	print('処理終了 ' + str(td) + ' ' + str(dt_now))

参考サイト

[Python] AI 21 学習モデル・グラフ画像への不可視的テキスト埋め込み

前回の続きです。tips的内容でAI学習とは直接関係ないのですが、一応書いておきます。

steganoライブラリを使って、画像ファイルにソースファイルの絶対パスと学習時間を不可視的に埋め込みました。画像の質については特に変化はありませんでした。

これでCSVへの記録が不要になり、データの散逸を防止できます。

from stegano import lsb

start = time.time()

def plot_loss_accuracy_graph(history):
	process_time = time.time() - start
	td = datetime.timedelta(seconds = process_time)
	dt_now = datetime.datetime.now()
	dt_now_str = dt_now.strftime('%y%m%d%H%M')

	image_loss ='{}_loss.png'.format(dt_now_str)

	fig = plt.figure()
	plt.plot(history.history['loss'], "-D", color="blue", label="train_loss", linewidth=2)
	plt.plot(history.history['val_loss'], "-D", color="black", label="val_loss", linewidth=2)
	plt.title('LOSS')
	fig.text(0.7, 0.95, os.path.basename(__file__))
	fig.text(0.8, 0.03, str(td))
	plt.xlabel('Epochs')
	plt.ylabel('Loss')
	plt.legend(loc='upper right')
	plt.tight_layout()
	fig.savefig(image_loss)

	# ソースファイルの絶対パスと学習時間をリストにして画像に埋め込む
	embed_text = '[' + os.path.abspath(__file__) + ',' + str(td) + ']'
	image_loss2 = image_loss.split('.')[0] + '2.png'
	secret = lsb.hide(image_loss, embed_text)
	secret.save(image_loss2)
  
	# 埋め込んだテキストを確認する
	embed_text_loss = lsb.reveal(image_loss2)
	print('embed_text_loss '+ embed_text_loss)