tensorflowについて調査してみた。(初心者がChatGPTを使ってAIを作って研究してみた)

ChatGTP-AI研究開発

はじめに

前回はAIの手法について軽く調査し、tensorflowという手法で研究開発していくことを決めました。今回は前回ChatGPTで聞いたコードを一つずつ紐解いていきたいと思います。

コード解説

コード全体

まずは前回のtensorflowのコードです。

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical

# MNISTデータセットの読み込み
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# データの前処理
X_train = X_train.reshape((X_train.shape[0], -1)).astype('float32') / 255
X_test = X_test.reshape((X_test.shape[0], -1)).astype('float32') / 255
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

# モデルの構築
model = Sequential([
    Dense(128, activation='relu', input_shape=(784,)),
    Dense(64, activation='relu'),
    Dense(10, activation='softmax')
])

# モデルのコンパイル
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# モデルのトレーニング
model.fit(X_train, y_train, epochs=10, batch_size=32)

# テストデータでモデルを評価
loss, accuracy = model.evaluate(X_test, y_test)
print(f"Loss: {loss}, Accuracy: {accuracy}")

今回はライブラリとMNISTの読み込み、データの前処理について調べていきたいと思います。

ライブラリ

まずは、ライブラリです。pythonのライブラリについてはこちらの記事でも書きました。

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical

tensorflowについて

基本的なライブラリはtensorflowで構成されているようです。tensorflowについて簡単に調査してみました。

tensorflowについて

私にとって興味深そうなことがたくさん書かれていますが、後で再調査します。

kerasについて

ライブラリを見ていきます。

tensorflowの一つ下の階層がkerasというライブラリのようです。kerasを簡単に調べるとAIを短いコードで書けるようにしたコードの集まりのようです。

kerasの下にmodels、layers、datasets、utilsとあるようです。

modelについて

modelsはAIモデルともいわれているようで、AIがどういったプロセス(例えば、本で学習するとか動画で学習するとか)で学習する方法のようです。中でもライブラリに書かれているSequentialが一般的な方法らしく(参考)入力に対して前から順々に学習しているようです。学生時の教科書を前から順々に学んでいくプロセスみたいです。

layersについて

次にlayersです。ニューラルネットワークの要なようです。キーワードとしてニューロンや隠れ層などなようです。ニューロン数や隠れ層の数が増えればより複雑なAIが作ることができるようですが、しばらくはデフォルトのものを使っていきます。

datasetsについて

次にdatasetsです。AIの開発や初学者に向けたデータの集まりを提供しているようです。英語のサイトですが以下にたくさんのデータの集まりの解説をしています。

datasetsのデータリスト

中でもよく使われているのはmnistと呼ばれる数字の判別に使うデータの集まりのようです。

mnistについて

utilsについて

最後にutilsです。これはAIが判別しやすいように変換したりする操作のようです。今回はto_categoricalというOne-Hotという0と1だけに変換する操作のようで、ラベルを分かりやすく示しているようです。例えば、以下に示すような0のラベルを示すのに0~9までの配列があって0番目が1で1番目から9番目が0にしたりできるようです。

0 : [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
1 : [0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
2 : [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
3 : [0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
4 : [0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
5 : [0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
6 : [0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
7 : [0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
8 : [0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
9 : [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]

今回の「to_categorical(y_train, 10)」はy_trainというデータを0~9までのラベルに分けるという意味になるようです。

以上がライブラリの調査結果です!長かったですね(笑)

データの読み込み

次にデータの読み込みです。

(X_train, y_train), (X_test, y_test) = mnist.load_data()

ライブラリで説明したmnistを呼び出して、load_data()という関数でデータを呼び出しているようです。呼び出した結果を4つに分類してx_train、y_train、x_test、y_testに格納しています。

これらの違いについて簡単に調査しました。

インプットデータとアウトプットデータ

まずはxとyについてですが、
xはインプットデータでyがアウトプットデータのようです。
ここではxが目で見た数字でyが脳でその数字を判断した結果になると思います。

次にtrainとtestについてです。
trainはtrainingの略で「学習」という意味のようです。
testはそのままテストのようです。

trainで学習した結果をtestできちんと学習されているかテストするためにあるようです。
他にも検証用データがあるよう(参考)ですが、いつか勉強しましょう。

つまり、
x_train : trainのインプットデータ
y_train : trainのアウトプットデータ
x_test : testするためのインプットデータ
y_test : testsるためのアウトプットデータ
になるようです。

読みこんだそれぞれのデータをprint文で確認してみます。

x_trainとx_testの中身

まずはx_trainです。

[[[0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  ...
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]]

 [[0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  ...
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]]

 [[0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  ...
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]]

 ...

 [[0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  ...
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]]

 [[0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  ...
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]]

 [[0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  ...
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]]]

少し分かりづらいですね…。どんな中身なのかしるために

df = pd.DataFrame(X_train[0])
df.to_csv("X_train.csv")

を追加してx_trainの最初をcsvで確認します。

	0	1	2	3	4	5	6	7	8	9	10	11	12	13	14	15	16	17	18	19	20	21	22	23	24	25	26	27
0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0
1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0
2	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0
3	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0
4	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0
5	0	0	0	0	0	0	0	0	0	0	0	0	3	18	18	18	126	136	175	26	166	255	247	127	0	0	0	0
6	0	0	0	0	0	0	0	0	30	36	94	154	170	253	253	253	253	253	225	172	253	242	195	64	0	0	0	0
7	0	0	0	0	0	0	0	49	238	253	253	253	253	253	253	253	253	251	93	82	82	56	39	0	0	0	0	0
8	0	0	0	0	0	0	0	18	219	253	253	253	253	253	198	182	247	241	0	0	0	0	0	0	0	0	0	0
9	0	0	0	0	0	0	0	0	80	156	107	253	253	205	11	0	43	154	0	0	0	0	0	0	0	0	0	0
10	0	0	0	0	0	0	0	0	0	14	1	154	253	90	0	0	0	0	0	0	0	0	0	0	0	0	0	0
11	0	0	0	0	0	0	0	0	0	0	0	139	253	190	2	0	0	0	0	0	0	0	0	0	0	0	0	0
12	0	0	0	0	0	0	0	0	0	0	0	11	190	253	70	0	0	0	0	0	0	0	0	0	0	0	0	0
13	0	0	0	0	0	0	0	0	0	0	0	0	35	241	225	160	108	1	0	0	0	0	0	0	0	0	0	0
14	0	0	0	0	0	0	0	0	0	0	0	0	0	81	240	253	253	119	25	0	0	0	0	0	0	0	0	0
15	0	0	0	0	0	0	0	0	0	0	0	0	0	0	45	186	253	253	150	27	0	0	0	0	0	0	0	0
16	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	16	93	252	253	187	0	0	0	0	0	0	0	0
17	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	249	253	249	64	0	0	0	0	0	0	0
18	0	0	0	0	0	0	0	0	0	0	0	0	0	0	46	130	183	253	253	207	2	0	0	0	0	0	0	0
19	0	0	0	0	0	0	0	0	0	0	0	0	39	148	229	253	253	253	250	182	0	0	0	0	0	0	0	0
20	0	0	0	0	0	0	0	0	0	0	24	114	221	253	253	253	253	201	78	0	0	0	0	0	0	0	0	0
21	0	0	0	0	0	0	0	0	23	66	213	253	253	253	253	198	81	2	0	0	0	0	0	0	0	0	0	0
22	0	0	0	0	0	0	18	171	219	253	253	253	253	195	80	9	0	0	0	0	0	0	0	0	0	0	0	0
23	0	0	0	0	55	172	226	253	253	253	253	244	133	11	0	0	0	0	0	0	0	0	0	0	0	0	0	0
24	0	0	0	0	136	253	253	253	212	135	132	16	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0
25	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0
26	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0
27	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0

おーなんか数字が見える!なるほどx_trainとx_testは上記のような見た目が数字のデータが入っているようです。

y_trainとy_testの中身

同様にy_trainとy_testを確認すると

[5 0 4 ... 5 6 8]

こちらは見た目の数字ではなく、見た目の数字を数字と判断するための数字が格納されているようです。

とりあえず、各データの中身が分かりました。次にデータの前処理について調べていきます。

データの前処理

前処理のコードは

X_train = X_train.reshape((X_train.shape[0], -1)).astype('float32') / 255
X_test = X_test.reshape((X_test.shape[0], -1)).astype('float32') / 255
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

x_trainとx_testの前処理

でした。まずはX_train.reshape((X_train.shape[0], -1)).astype(‘float32’) / 255についてです。

x_trainとx_testのshapeとreshape

X_trainの形状をreshapeして形状を作り変えています。作り変える前の形状をprint(X_train.shape)で確認してみます。

(60000, 28, 28)

となりました。3次元形状なので想像は難しそうですが、28×28というのはX_trainのcsvで確認したときの縦×横がちょうど28行28列なので、先ほどのcsvが1/60000のデータなのでしょう。他の59999が見た目の数字データなのでしょう。イメージとしては

これをreshapeして、(X_train.shape[0], -1))という形状に変更しています。

X_train.shape[0]は上のprintした結果から60000と分かります。-1は逆から並べているように見えますが、一度print(X_train.reshape((X_train.shape[0], -1)))で確認してみましょう。

[[0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 ...
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]]

となりました。0ばかりでよくわかりません。試しにshapeも確認してみましょう。

(60000, 784)

となってます。ということは上図の一枚の数字データをさらに1行1行に分けて横に並べているようですね。

試しに

df = pd.DataFrame(X_train.reshape((X_train.shape[0], -1)))
df.to_csv("X_train_reshape.csv")

で確認してみます。csvを乗せようと思ったのですが、重くて載せられませんでしたので、スクリーンショットで

これでreshapeができました。とんでもないデータ量ですね…。

x_trainとx_testのastypeと正規化(ChatGPTにも聞いてみた)

また、前処理に戻って、コードの確認。

X_train = X_train.reshape((X_train.shape[0], -1)).astype('float32') / 255
X_test = X_test.reshape((X_test.shape[0], -1)).astype('float32') / 255
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

astype(‘float32’)はreshapeした数字の型をfloat32に変換してますね。データを見る限りおそらくint型のをfloat型に変えて計算させやすくしているのでしょう。
(一応print(type(X_train.reshape((X_train.shape[0], -1))))で型を調べましたが、ndarrayでした。個々の数字も調べれば多分int型になっているのでしょう)

最後に255で割っています。これは何でしょうか?GTPに聞いてみましょう。(笑)

J.A.
J.A.

X_train.reshape((X_train.shape[0], -1)).astype(‘float32’) / 255は何をしているの

X_train.reshape((X_train.shape[0], -1)).astype('float32') / 255 というコードは、ニューラルネットワークの入力用に画像データを前処理するために使用されます。このコードは主に3つのステップで構成されています。

  1. reshape((X_train.shape[0], -1)): この部分は、データセット内の画像を一次元の配列に変換します。X_train は、通常、形状 [数, 高さ, 幅](グレースケール画像の場合)または [数, 高さ, 幅, チャンネル](カラー画像の場合)の3次元または4次元の配列です。reshape((X_train.shape[0], -1)) は、最初の次元(画像の数)を維持しながら、残りの次元を一つの次元に平坦化します。これにより、画像データがニューラルネットワークの入力層に適した形状(2次元配列)になります。
  2. astype('float32'): この部分は、データの型を float32 に変換します。多くの画像データセットでは、ピクセルの値が整数(0から255の範囲)で格納されていますが、多くのニューラルネットワークは浮動小数点数を入力として期待しています。astype('float32') は、データ型を浮動小数点数に変換し、計算効率と精度を向上させます。
  3. / 255: この部分は、ピクセル値を0から1の範囲に正規化します。画像データのピクセル値は通常、0から255の範囲で表されます。ニューラルネットワークは入力データが正規化されている場合に効率的に学習するため、ピクセル値を255で割ることで、すべての値が0から1の範囲に収まるようにします。

これらのステップは、ニューラルネットワークの性能を向上させ、より効率的なトレーニングを可能にするための標準的なデータ前処理手順です。

調べた内容も確認できますね。ただ少し抽象的と感じますね。今回は3の255で割って理由です。正規化しているようです。ピクセル値の最大値の255で割ることで255を1としたときの他のピクセル値の割合を求めているみたいです。理由は効率的に学習するためと書かれています。今回はそんな操作があるとしておきましょう。

y_trainとy_testの前処理

長かったですが、最後の前処理です。

y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

これは前述したようにy_trainとy_testのデータを10個のラベリングしているようですね。

変換前後をprintで見てみます。

変換前

[5 0 4 ... 5 6 8]

変換後

[[0. 0. 0. ... 0. 0. 0.]
 [1. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 ...
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 1. 0.]]

変換前の2つ目の0は変換後の2行目の最初(0)の位置に1とラベリングされています。
変換前の最後の8は変換後の最終行の最後から2番目(8)の位置に1とラベリングされています。

最後に

これでようやく前処理をすべて終わりました。
次回から本格的にAIを動かしてみたいと思います。

コメント

タイトルとURLをコピーしました