ChatGPTで、Pythonプログラム生成を試してみました。CNNによるMNIST画像分類のPythonプログラミングを生成してみたので、結果をまとめてみたいと思います。
Contents
ChatGPTによるPythonプログラム生成
ChatGPTとは、OpenAIが開発したAIチャットサービスです。ChatGPTの概要やアカウント登録等の使い方は「【ChatGPT】概要と始め方・使い方」でまとめていますので興味があれば参考にしてください。
ChatGPTは、様々な利用場面での活用が期待されています。その中の一つがプログラミング領域です。ChatGPTでどのぐらいのことができるのかを試してみました。
はじめに感想を言ってみると正直驚きました。一部の優秀なプログラマ以外は仕事がなくなっていくかもなと感じたところです。また、私が書いている本サイトの記事みたいなものもいらなくなってくるなと思いました。
本記事では、CNNによるMNIST画像分類のPythonプログラム例を使いつつ、どんな感じであったかを紹介します。
ChatGPTでCNNによるMNIST画像分類
題材として私が過去に本サイトでまとめている「【TensorFlow/Keras】CNN(畳み込みニューラルネットワーク)による画像分類の基本」を使おうかと思います。上記は、TensorFlow/Kerasを用いてCNN(畳み込みニューラルネットワーク)によるMNIST画像の分類をする例を紹介しているページです。
CNNによるPythonプログラムをChagGPTだとどこまで作れるのかを試してみます。なお、以下結果の環境としてはChatGPT Plusで最新のGPT-4を使って試してみています。なお、無料版のGPT-3.5でも同様の結果は返ってきます。
ChatGPTの回答は毎回変わるので、以下例と全く同じ結果は返ってこないと思ってください。私も何回か試しましたが、回答表現やプログラム内容は色々と変わりました。ただし、本質的にプログラムの部分は実際に実行可能なものを提供してくれました。
では、順番に試したことを見ていきましょう。
簡単に聞いてみる
まずは、簡単に以下の質問を投げかけてみます。すごく簡単な聞き方ですがどこまでのプログラムが生成されるかを見てみます。
あなたは優秀なPythonプログラマです。 CNNでMNIST画像分類をするプログラムを作成してください。
上記の質問を投げかけると以下のようにプログラムがどんどん書き始められます。以降の結果例では、以下のような回答内容を転記しつつ紹介していきます。
「あなたは〇〇です」というのは、ChatGPTに役割を与える方法の一つです。ChatGPTに良い回答をもらうには何として振舞うかを指定するとよいです。
このように適切な回答を得る技術はプロンプトエンジニアリングと言います。日本語ではまだあまり情報がなく、私も勉強中です。
以下がChatGPTの回答内容を転記したものです。
MNIST画像分類のために、Kerasを使ってシンプルなCNNを構築します。このプログラムは、KerasとTensorFlowがインストールされていることを前提としています。以下のコードを実行してください。
import numpy as np import tensorflow as tf from tensorflow.keras import layers, models from tensorflow.keras.datasets import mnist from tensorflow.keras.utils import to_categorical # データをロードして前処理 (train_images, train_labels), (test_images, test_labels) = mnist.load_data() train_images = train_images.reshape((60000, 28, 28, 1)) train_images = train_images.astype('float32') / 255 test_images = test_images.reshape((10000, 28, 28, 1)) test_images = test_images.astype('float32') / 255 train_labels = to_categorical(train_labels) test_labels = to_categorical(test_labels) # CNNモデルを作成 model = models.Sequential() model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1))) model.add(layers.MaxPooling2D((2, 2))) model.add(layers.Conv2D(64, (3, 3), activation='relu')) model.add(layers.MaxPooling2D((2, 2))) model.add(layers.Conv2D(64, (3, 3), activation='relu')) # 全結合層を追加 model.add(layers.Flatten()) model.add(layers.Dense(64, activation='relu')) model.add(layers.Dense(10, activation='softmax')) # モデルをコンパイル model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) # モデルを学習 model.fit(train_images, train_labels, epochs=5, batch_size=64) # テストデータで評価 test_loss, test_acc = model.evaluate(test_images, test_labels) print(f"Test accuracy: {test_acc:.4f}")
このコードは、MNISTデータセットをロードし、前処理を行い、CNNモデルを構築・学習し、最後にテストデータで評価を行います。この例では、2つの畳み込み層と2つのプーリング層が含まれており、最後に全結合層が追加されています。モデルは、5エポックで学習され、テストデータの精度が出力されます。
上記のようにプログラムの記載の前後に説明文が加えられます。十分な説明がされている印象でした。実際にプログラムを実行してみた結果は以下の通りです。
【実行結果】 Epoch 1/5 938/938 [==============================] - 13s 13ms/step - loss: 0.1847 - accuracy: 0.9432 Epoch 2/5 938/938 [==============================] - 13s 14ms/step - loss: 0.0515 - accuracy: 0.9844 Epoch 3/5 938/938 [==============================] - 12s 13ms/step - loss: 0.0365 - accuracy: 0.9886 Epoch 4/5 938/938 [==============================] - 12s 13ms/step - loss: 0.0279 - accuracy: 0.9915 Epoch 5/5 938/938 [==============================] - 13s 13ms/step - loss: 0.0227 - accuracy: 0.9930 313/313 [==============================] - 1s 2ms/step - loss: 0.0341 - accuracy: 0.9899 Test accuracy: 0.9899
普通に実行できました。正直驚きでした。
なお、ChatGPTは会話サービスなので、最初で完璧な回答をしてもらうというよりは、続けて指示するというのを繰り返す使い方をするのが基本です。以降では、引き続きで色々と指示をしてみた結果を紹介します。
条件を詳細に指定してみる
今回題材にしているCNNは、私が以前記載したこちらのページでもプログラム例を紹介しています。その時のプログラムは上記ページから参照してください。
正直、ほぼ最初の簡単な質問で同じようなプログラムが生成されています。もう少し具体的な条件を追記して修正をしてもらいましょう。
作成されたプログラムを以下の前提条件で修正してください。 ・ネットワークのフィルタ数は32, 64, 128と適用してください。 ・全結合層は128としてください。 ・過学習防止のため、全結合層の前にドロップアウト(0.5)を追加してください。 ・評価データは訓練データの20%を使用してください。 ・モデルの構成をsummaryで取得して、printで表示してください。 ・モデルの構成情報をplot_modelでshow_shape=Trueとして、"mnist_cnn_classifier.png"というファイル名で保存してください。 ・history情報で損失関数(loss)と正解率(accuracy)の履歴を取得し、matplotlibで履歴を表示してください。 ・predictを使ってMNISTの0番目のデータに対する予測と正解をprintで表示してください。
以下がChatGPTの回答内容を転記したものです。
上記の要件に従って、プログラムを修正しました。必要なライブラリもインポートしています。以下のコードを実行してください。
import numpy as np import tensorflow as tf from tensorflow.keras import layers, models from tensorflow.keras.datasets import mnist from tensorflow.keras.utils import to_categorical, plot_model import matplotlib.pyplot as plt # データをロードして前処理 (train_images, train_labels), (test_images, test_labels) = mnist.load_data() train_images = train_images.reshape((60000, 28, 28, 1)) train_images = train_images.astype('float32') / 255 test_images = test_images.reshape((10000, 28, 28, 1)) test_images = test_images.astype('float32') / 255 train_labels = to_categorical(train_labels) test_labels = to_categorical(test_labels) # CNNモデルを作成 model = models.Sequential() model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1))) model.add(layers.MaxPooling2D((2, 2))) model.add(layers.Conv2D(64, (3, 3), activation='relu')) model.add(layers.MaxPooling2D((2, 2))) model.add(layers.Conv2D(128, (3, 3), activation='relu')) # 全結合層を追加 model.add(layers.Flatten()) model.add(layers.Dropout(0.5)) model.add(layers.Dense(128, activation='relu')) model.add(layers.Dense(10, activation='softmax')) # モデルをコンパイル model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) # モデルの構成を表示 model.summary() # モデルの構成を画像として保存 plot_model(model, to_file='mnist_cnn_classifier.png', show_shapes=True) # モデルを学習 history = model.fit(train_images, train_labels, epochs=5, batch_size=64, validation_split=0.2) # 損失関数と正解率の履歴を表示 plt.plot(history.history['loss'], label='Training loss') plt.plot(history.history['val_loss'], label='Validation loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.show() plt.plot(history.history['accuracy'], label='Training accuracy') plt.plot(history.history['val_accuracy'], label='Validation accuracy') plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.legend() plt.show() # 0番目のデータに対する予測と正解を表示 prediction = model.predict(test_images[0:1]) print("Predicted label:", np.argmax(prediction)) print("True label:", np.argmax(test_labels[0]))
このコードは、前提条件に従って修正されたCNNモデルを構築・学習し、モデルの構成を表示・保存し、損失関数と正解率の履歴を表示し、最後に0番目のデータに対する予測と正解を表示します。
実際に実行してみると以下のようになります。
【実行結果】 Model: "sequential_1" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d_3 (Conv2D) (None, 26, 26, 32) 320 max_pooling2d_2 (MaxPooling (None, 13, 13, 32) 0 2D) conv2d_4 (Conv2D) (None, 11, 11, 64) 18496 max_pooling2d_3 (MaxPooling (None, 5, 5, 64) 0 2D) conv2d_5 (Conv2D) (None, 3, 3, 128) 73856 flatten_1 (Flatten) (None, 1152) 0 dropout (Dropout) (None, 1152) 0 dense_2 (Dense) (None, 128) 147584 dense_3 (Dense) (None, 10) 1290 ================================================================= Total params: 241,546 Trainable params: 241,546 Non-trainable params: 0 _________________________________________________________________ Epoch 1/5 750/750 [==============================] - 12s 15ms/step - loss: 0.2060 - accuracy: 0.9353 - val_loss: 0.0586 - val_accuracy: 0.9832 Epoch 2/5 750/750 [==============================] - 11s 15ms/step - loss: 0.0621 - accuracy: 0.9802 - val_loss: 0.0396 - val_accuracy: 0.9887 Epoch 3/5 750/750 [==============================] - 11s 15ms/step - loss: 0.0468 - accuracy: 0.9852 - val_loss: 0.0454 - val_accuracy: 0.9863 Epoch 4/5 750/750 [==============================] - 11s 15ms/step - loss: 0.0384 - accuracy: 0.9876 - val_loss: 0.0329 - val_accuracy: 0.9902 Epoch 5/5 750/750 [==============================] - 11s 15ms/step - loss: 0.0325 - accuracy: 0.9896 - val_loss: 0.0338 - val_accuracy: 0.9908 1/1 [==============================] - 0s 73ms/step Predicted label: 7 True label: 7
表示画像
mnist_cnn_classifier.png
上記のように指定した条件の通りのモデルの作成や、損失関数や正解率の表示、モデルの保存といった一連の動作を動作可能なプログラムを生成できました。
- 説明文の途中で表示が止まってしまう場合は「続き」「つづき」などとすると回答が再開されます。
- プログラムの表示エリアの途中で止まってしまう場合は、「つづき」等とすると、プログラムを普通の説明文として回答を再開してしまうなど表示が崩れることがあります。「プログラムの続きをプログラム表示エリアで再開してください。」などのように明確にプログラムの続きを書くように促すと崩れにくくなります。
作成プログラムの修正・書き換え
関数化してもらう
次は、これまでのChatGPT回答プログラムの実行機能を関数化してみてもらいましょう。
可能な限り機能を関数化し、main関数を作成してください。
以下がChatGPTの回答内容を転記したものです。
以下のコードは、機能を関数化し、main関数を作成しています。各機能は独立した関数に分割され、main関数内で呼び出されます。
import numpy as np import tensorflow as tf from tensorflow.keras import layers, models from tensorflow.keras.datasets import mnist from tensorflow.keras.utils import to_categorical, plot_model import matplotlib.pyplot as plt def load_and_preprocess_data(): (train_images, train_labels), (test_images, test_labels) = mnist.load_data() train_images = train_images.reshape((60000, 28, 28, 1)) train_images = train_images.astype('float32') / 255 test_images = test_images.reshape((10000, 28, 28, 1)) test_images = test_images.astype('float32') / 255 train_labels = to_categorical(train_labels) test_labels = to_categorical(test_labels) return (train_images, train_labels), (test_images, test_labels) def build_model(): model = models.Sequential() model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1))) model.add(layers.MaxPooling2D((2, 2))) model.add(layers.Conv2D(64, (3, 3), activation='relu')) model.add(layers.MaxPooling2D((2, 2))) model.add(layers.Conv2D(128, (3, 3), activation='relu')) model.add(layers.Flatten()) model.add(layers.Dropout(0.5)) model.add(layers.Dense(128, activation='relu')) model.add(layers.Dense(10, activation='softmax')) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) return model def display_and_save_model(model): model.summary() plot_model(model, to_file='mnist_cnn_classifier.png', show_shapes=True) def plot_history(history): plt.plot(history.history['loss'], label='Training loss') plt.plot(history.history['val_loss'], label='Validation loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.show() plt.plot(history.history['accuracy'], label='Training accuracy') plt.plot(history.history['val_accuracy'], label='Validation accuracy') plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.legend() plt.show() def predict_and_display(model, test_images, test_labels): prediction = model.predict(test_images[0:1]) print("Predicted label:", np.argmax(prediction)) print("True label:", np.argmax(test_labels[0])) def main(): (train_images, train_labels), (test_images, test_labels) = load_and_preprocess_data() model = build_model() display_and_save_model(model) history = model.fit(train_images, train_labels, epochs=5, batch_size=64, validation_split=0.2) plot_history(history) predict_and_display(model, test_images, test_labels) if __name__ == '__main__': main()
このコードでは、load_and_preprocess_data、build_model、display_and_save_model、plot_history、predict_and_displayという5つの関数が定義されています。最後にmain関数が定義され、上記の関数を適切な順序で呼び出しています。
上記のように、機能ごとの関数化ができています。また、if __name__ == ‘__main__’でこのスクリプトが直接呼び出されたときにのみmain関数が動くように適切にプログラミングがされました。
モデル構築の実装方法を変更してもらう
TensorFlowの実装では、「Keras APIでモデルを構築する色々な方法と違い(Sequential API, Functional API, Subclassing API)」で紹介しているように、3つのモデル構築方法が使用できます。上記までの回答では最もシンプルなSequential APIを使った実装でした。
以降では、より高度なFunctional APIやSubclassing APIの実装方法で書き換えてもらってみました。
Functional APIに書き換える
作成したプログラムを以下のような指示でFunctionalAPIに書き換えてもらいます。
作成されたプログラムをFunctional APIを使って書き換えてください。
以下がChatGPTの回答内容を転記したものです。
以下のコードは、Functional APIを使用して書き換えられたプログラムです。build_model関数がFunctional APIを使用してモデルを構築するように変更されています。
import numpy as np import tensorflow as tf from tensorflow.keras import layers, models, Input from tensorflow.keras.datasets import mnist from tensorflow.keras.utils import to_categorical, plot_model import matplotlib.pyplot as plt def load_and_preprocess_data(): (train_images, train_labels), (test_images, test_labels) = mnist.load_data() train_images = train_images.reshape((60000, 28, 28, 1)) train_images = train_images.astype('float32') / 255 test_images = test_images.reshape((10000, 28, 28, 1)) test_images = test_images.astype('float32') / 255 train_labels = to_categorical(train_labels) test_labels = to_categorical(test_labels) return (train_images, train_labels), (test_images, test_labels) def build_model(): inputs = Input(shape=(28, 28, 1)) x = layers.Conv2D(32, (3, 3), activation='relu')(inputs) x = layers.MaxPooling2D((2, 2))(x) x = layers.Conv2D(64, (3, 3), activation='relu')(x) x = layers.MaxPooling2D((2, 2))(x) x = layers.Conv2D(128, (3, 3), activation='relu')(x) x = layers.Flatten()(x) x = layers.Dropout(0.5)(x) x = layers.Dense(128, activation='relu')(x) outputs = layers.Dense(10, activation='softmax')(x) model = models.Model(inputs=inputs, outputs=outputs) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) return model def display_and_save_model(model): model.summary() plot_model(model, to_file='mnist_cnn_classifier.png', show_shapes=True) def plot_history(history): plt.plot(history.history['loss'], label='Training loss') plt.plot(history.history['val_loss'], label='Validation loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.show() plt.plot(history.history['accuracy'], label='Training accuracy') plt.plot(history.history['val_accuracy'], label='Validation accuracy') plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.legend() plt.show() def predict_and_display(model, test_images, test_labels): prediction = model.predict(test_images[0:1]) print("Predicted label:", np.argmax(prediction)) print("True label:", np.argmax(test_labels[0])) def main(): (train_images, train_labels), (test_images, test_labels) = load_and_preprocess_data() model = build_model() display_and_save_model(model) history = model.fit(train_images, train_labels, epochs=5, batch_size=64, validation_split=0.2) plot_history(history) predict_and_display(model, test_images, test_labels) if __name__ == '__main__': main()
このコードでは、build_model関数でFunctional APIを使用してモデルを構築しています。モデルは、入力層をInput関数で定義し、次の層を順に接続していきます。最後に、models.Modelを使用して入力と出力を指定し、モデルを作成します。その後、以前のコードと同様に、モデルをコンパイル、学習、評価しています。 Functional APIを使用することで、より複雑なモデル構造を簡単に構築できるようになります。この例では、シーケンシャルなモデルをFunctional APIを使用して書き換えただけですが、より柔軟なモデル構造が可能になります。たとえば、マルチ入力、マルチ出力、共有層、残差接続などのモデルを構築する際に有用です。
実行結果はSequential APIの場合と同じのため省略します。上記の通り、適切にFunctional APIに書き換えるとともに、Functional APIでの実装方法の特徴についても説明がされています。
Subclassing APIに書き換える
次に、作成したプログラムを以下のような指示でSubclassing APIに書き換えてもらいます。
作成されたプログラムをSubclassing APIを使って書き換えてください。
以下がChatGPTの回答内容を転記したものです。
以下のコードは、Subclassing APIを使用して書き換えられたプログラムです。MNISTClassifierという新しいクラスが作成され、tf.keras.Modelを継承しています。このクラス内でモデルの構造が定義されており、build_model関数でインスタンス化されています。
import numpy as np import tensorflow as tf from tensorflow.keras import layers, models, Input from tensorflow.keras.datasets import mnist from tensorflow.keras.utils import to_categorical, plot_model import matplotlib.pyplot as plt def load_and_preprocess_data(): (train_images, train_labels), (test_images, test_labels) = mnist.load_data() train_images = train_images.reshape((60000, 28, 28, 1)) train_images = train_images.astype('float32') / 255 test_images = test_images.reshape((10000, 28, 28, 1)) test_images = test_images.astype('float32') / 255 train_labels = to_categorical(train_labels) test_labels = to_categorical(test_labels) return (train_images, train_labels), (test_images, test_labels) class MNISTClassifier(models.Model): def __init__(self): super(MNISTClassifier, self).__init__() self.conv1 = layers.Conv2D(32, (3, 3), activation='relu') self.pool1 = layers.MaxPooling2D((2, 2)) self.conv2 = layers.Conv2D(64, (3, 3), activation='relu') self.pool2 = layers.MaxPooling2D((2, 2)) self.conv3 = layers.Conv2D(128, (3, 3), activation='relu') self.flatten = layers.Flatten() self.dropout = layers.Dropout(0.5) self.dense1 = layers.Dense(128, activation='relu') self.dense2 = layers.Dense(10, activation='softmax') def call(self, inputs): x = self.conv1(inputs) x = self.pool1(x) x = self.conv2(x) x = self.pool2(x) x = self.conv3(x) x = self.flatten(x) x = self.dropout(x) x = self.dense1(x) x = self.dense2(x) return x def build_model(): model = MNISTClassifier() model.build(input_shape=(None, 28, 28, 1)) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) return model def display_and_save_model(model): model.summary() plot_model(model, to_file='mnist_cnn_classifier.png', show_shapes=True, expand_nested=True) def plot_history(history): plt.plot(history.history['loss'], label='Training loss') plt.plot(history.history['val_loss'], label='Validation loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.show() plt.plot(history.history['accuracy'], label='Training accuracy') plt.plot(history.history['val_accuracy'], label='Validation accuracy') plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.legend() plt.show() def predict_and_display(model, test_images, test_labels): prediction = model.predict(test_images[0:1]) print("Predicted label:", np.argmax(prediction)) print("True label:", np.argmax(test_labels[0])) def main(): (train_images, train_labels), (test_images, test_labels) = load_and_preprocess_data() model = build_model() display_and_save_model(model) history = model.fit(train_images, train_labels, epochs=5, batch_size=64, validation_split=0.2) plot_history(history) predict_and_display(model, test_images, test_labels) if __name__ == '__main__': main()
このコードでは、MNISTClassifierというサブクラス化されたモデルが使用されています。このモデルは、build_model関数でインスタンス化され、以前のコードと同様にコンパイル、学習、評価が行われています。 Subclassing APIは、カスタムモデルを作成する際に非常に柔軟性があります。特に、独自の層や構造を持つモデルを構築する際に有用です。ただし、この方法では、モデルの構造がPythonコードで定義されているため、その構造を直接把握することが難しい場合があります。
実行結果はSequential APIの場合と同じのため省略します。tf.keras.Modelを継承したカスタムモデルが作成されていることが分かります。また、Subclassing APIは柔軟である一方でモデル構造が把握しずらいという特徴をしっかりと説明できています。
まとめ
ChatGPTで、Pythonプログラム生成を試してみました。具体的には、CNNによるMNIST画像分類のPythonプログラミングを生成してみた結果例を紹介しています。いかがだったでしょうか。
非常に簡単な質問でも十分なプログラムの回答をしてくれましたが、細かな条件指定や、機能の関数化、実装方法の変更など幅広く指示に沿ったプログラムを生成してくれていることが分かるかと思います。
ただし、実際に皆さんも上記のように実行してみていただくと分かりますが、回答のされ方やプログラム構成等が微妙に変わりますので、あまりよい回答が得られなかったり、誤った回答が返ってくる可能性もあります。
また、細かな部分は、プログラマが自分で修正するのが現状は早いかなと感じます。全体として動くプログラムを提供してくれますし、未経験で少し勉強した程度のPythonプログラマよりはよっぽどよいプログラムを生成してくれる印象です。最初にベースとなるプログラムをChatGPTで生成し、細部チューニングをするというのが効率的にプログラム開発に活用できる方法かなと感じました。
ChatGPTがより性能が良くなってきたら、本当に一部の優秀なプログラマ以外はいらなくなってくるかもしれませんね。また、本サイトのようなプログラムに関する説明サイトなども不要になってくる気がしました。
皆さんもChatGPTで色々と指示をしてプログラムを作ってみてもらえると面白いかと思います。