ChatGPTでニューラルネットのコードを書かせてみたらすごかった

# 0.はじめに
いま流行りのChatGPTではプログラムのコードまで書けるということで、
PythonでMNISTの手書き数字を認識するニューラルネットワークを実装するコードを書かせて、実際に走らせてみました。

ChatGPTを使って遊んでみた程度の話なのでぜひ軽い気持ちで読んでみてください、、

ChatGPT : https://chat.openai.com/chat


```
実行環境
Linux, Python3.9.12
```




# 1.シンプルにMNISTを分類するコードを書かせる
まずは手始めにシンプルにMNISTを分類するコードをお願いしてみると、こんな感じで答えてくれました。なかなか良さげなコードが書けていそうです。
![スクリーンショット 2023-02-04 1.44.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884446/7e3776d2-544e-fe2c-4c83-48aa59bee017.png)

ChatGPTが出力したコード : 

``` python
import tensorflow as tf

# MNISTデータセットをロード
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()

# 画像を正規化
x_train = x_train / 255.0
x_test = x_test / 255.0

# 画像を多次元配列に変換
x_train = x_train.reshape(-1, 28 * 28)
x_test = x_test.reshape(-1, 28 * 28)

# モデルの構築
model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(128, activation='relu', input_shape=(28 * 28,)),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dense(10, activation='softmax')
])

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

# モデルの訓練
model.fit(x_train, y_train, epochs=5)

# モデルの評価
test_loss, test_acc = model.evaluate(x_test, y_test)
print('Test accuracy:', test_acc)
```

まとめると、こんな感じの内容になっています。

・機械学習ライブラリ : TensorFlow
・モデル : 3層の多層パーセプトロン
・最適化の手法 : Adam
・損失関数 : CrossEntoropyLoss
・エポック数 : 5
<br>


これを実際に実行してみると、、
![スクリーンショット 2023-02-04 1.56.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884446/c41c6c81-50fc-836c-dbef-aed35a5b9282.png)

テスト精度は97.47%ほどで正しく学習できています！すごい！

# 2.ライブラリを指定する
次に先ほどChatGPTが書いたコードに対して、PyTorchを使用するようにお願いしてみます。


![スクリーンショット 2023-02-04 2.12.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884446/b35d234a-1db1-a2f9-1007-fa69b57907f6.png)

ChatGPTが出力したコード : 
```python
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

# MNISTデータセットをロード
train_dataset = torch.utils.data.DataLoader(
  torchvision.datasets.MNIST('/files/', train=True, download=True,
                             transform=torchvision.transforms.Compose([
                               torchvision.transforms.ToTensor(),
                               torchvision.transforms.Normalize(
                                 (0.1307,), (0.3081,))
                             ])),
  batch_size=64, shuffle=True)

test_dataset = torch.utils.data.DataLoader(
  torchvision.datasets.MNIST('/files/', train=False, download=True,
                             transform=torchvision.transforms.Compose([
                               torchvision.transforms.ToTensor(),
                               torchvision.transforms.Normalize(
                                 (0.1307,), (0.3081,))
                             ])),
  batch_size=64, shuffle=True)

# モデルの構築
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(28 * 28, 128)
        self.fc2 = nn.Linear(128, 64)
        self.fc3 = nn.Linear(64, 10)

    def forward(self, x):
        x = x.view(-1, 28 * 28)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

model = Net()

# コンパイル
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)

# 訓練
for epoch in range(10):
    for batch_idx, (data, target) in enumerate(train_dataset):
        optimizer.zero_grad()
        output = model(data)
        loss = F.cross_entropy(output, target)
        loss.backward()
        optimizer.step()

# 評価
correct = 0
with torch.no_grad():
    for data, target in test_dataset:
        output = model(data)
        pred = output.argmax(dim=1, keepdim=True)
        correct += pred.eq(target.view_as(pred)).sum().item()

print('Test accuracy:', correct / len(test_dataset.dataset))
```

まとめるとこんな感じの内容になっています。


・機械学習ライブラリ : PyTorch
・モデル : 3層の多層パーセプトロン
・最適化の手法 : SGD
・損失関数 : CrossEntoropyLoss
・エポック数 : 10
<br>


ちゃんと使用ライブラリがTensorFlowからPyTorchをなりました！優秀すぎる！
ただ、モデルの構造と損失関数は先ほどと同じですが、最適化の手法がAdamからSGDに変わっています。あと、なぜかエポック数も10に増えています。
気になるところは少しありますが、ここらへんの変化はまあ大丈夫です。

しかし、このコードには1つ問題があってTorchvisionをインポートしていないのに使用しています、、
このまま実行するとエラーが起こってしまうので、Torchvisionのインポートを追加してから実行すると、、
![スクリーンショット 2023-02-04 3.06.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884446/cd8f415e-bacf-43ef-3f99-a3450ca49e95.png)

先ほどと同じくらいの精度になりました。

# 3. 学習の工夫をする
続いて、学習の工夫としてEarlyStoppingをするようにお願いしてみます。
![スクリーンショット 2023-02-04 5.17.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884446/a47d82ee-3a98-3af5-ade5-d24a7c23c695.png)

ChatGPTが出力したコード :

```python
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms

# Define the neural network
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(28 * 28, 128)
        self.fc2 = nn.Linear(128, 64)
        self.fc3 = nn.Linear(64, 10)

    def forward(self, x):
        x = x.view(-1, 28 * 28)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# Load MNIST dataset
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])
train_dataset = datasets.MNIST(root='.', train=True, transform=transform, download=True)
test_dataset = datasets.MNIST(root='.', train=False, transform=transform, download=True)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=64, shuffle=False)

# Train the network
model = Net()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)

best_acc = 0.0
early_stop_count = 0
for epoch in range(100):
    for batch_idx, (data, target) in enumerate(train_loader):
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()

    # Test the network
    correct = 0
    total = 0
    with torch.no_grad():
        for data, target in test_loader:
            output = model(data)
            prediction = output.argmax(dim=1, keepdim=True)
            correct += prediction.eq(target.view_as(prediction)).sum().item()
            total += target.size(0)

    acc = 100. * correct / total
    if acc > best_acc:
        best_acc = acc
        early_stop_count = 0
    else:
        early_stop_count += 1
    print('Epoch: {} Accuracy: {}/{} ({:.2f}%)'.format(epoch, correct, total, acc))

    if early_stop_count >= 10:
        print('Early stopping')
        break

print('Best Accuracy: {:.2f}%'.format(best_acc))
```

モデルについては前回と同じでした。
EarlyStoppingについては、検証データ(コード上ではtest_loaderと表記)に対するaccuracyを参照して、10エポックにわたって精度に向上が見られない場合は学習を早期停止するようになっています。
これを実行すると、、
![スクリーンショット 2023-02-04 5.36.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884446/4a50c601-c7ab-dff1-42e5-a9c774036d5c.png)

今度は学習中の検証データに対する精度もエポックごとに出力するようになっていて、正しく学習できているのがわかります。


(本当は最後に検証データとは異なるテストデータに対する精度を計算して出力してくれたら最高ですが、それはChatGPTに追加で指示を出していけばその部分のコードを書いてくれます)

# 4.最後に


正直siriの多少すごいバージョンくらいだと思っていたので、ここまでできることに驚きました。というか少し怖さすら感じました、、(本当に仕事がなくなりそう、、)
けれどChatGPTに細かく指示を出していけばもっといろんなモデルが書けるだろうし、今後プログラムを書く際にかなり参考になりそうでした。


あと、長いコードになると途中でChatGPTの出力が終わってしまって有料版じゃないと見れないのかなと思っていたのですが、ChatGPTに'続きは？'などと聞いてみると続きの出力も出してくれます。

ここまで読んでくださり、ありがとうございました。


# 追記
今回は、Torchvisionをインポートしないという間違えがありましたが、やり直すとちゃんと間違えのないコードも出力されました。
![スクリーンショット 2023-02-04 4.55.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884446/088dcc40-231d-9b75-705a-6aaee9e935af.png)