機械学習の開発環境としてのMacBook Air（M1）

# 機械学習の開発環境としてのMacBook Air（M1）はアリなのか
仕事でデータ分析に少し関わることがある、プライベートのゲームでAIによる予測をしたいことがある、両者より少しだけ機械学習の勉強をしています。今までは所有マシンのパワーが足りずGoogleのColaboratoryを利用していました。そんな中、16コアのNeural Engineを備えたMacBook Air（M1）が発売されたため、機械学習の開発環境としてアリか買って検証してみました。

# 機械学習の開発環境としてのMacBook Air（M1）は将来性も考えるとアリ
現時点では各種ライブラリが最適化されておらずコードによっては性能が出ませんが、Colaboratoryの約3倍の性能があり、機械学習の開発環境としてアリだとわかりました。ライブラリの最適化は時間が解決してくれるでしょう。

# 検証方法
TensorFlowの公式ページに公開されているコードで測定した結果、MacBook Airの性能が著しく悪かったため、2つのコードで性能を測定しました。

## 検証1：TensorFlowの公式ページに公開されているコードでの実行時間の計測
 [「エキスパートのための TensorFlow 2.0 入門」](https://www.tensorflow.org/tutorials/quickstart/advanced)のコードに実行時間計測を入れ、各環境で実行時間を測定しました。

``` benchmark.py
import tensorflow as tf

from tensorflow.keras.layers import Dense, Flatten, Conv2D
from tensorflow.keras import Model

mnist = tf.keras.datasets.mnist

#ダウンロード終了後からの実行時間計測のために追加
import time
start_time = time.time()

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

# Add a channels dimension
x_train = x_train[..., tf.newaxis]
x_test = x_test[..., tf.newaxis]

train_ds = tf.data.Dataset.from_tensor_slices(
    (x_train, y_train)).shuffle(10000).batch(32)

test_ds = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)

class MyModel(Model):
  def __init__(self):
    super(MyModel, self).__init__()
    self.conv1 = Conv2D(32, 3, activation='relu')
    self.flatten = Flatten()
    self.d1 = Dense(128, activation='relu')
    self.d2 = Dense(10, activation='softmax')

  def call(self, x):
    x = self.conv1(x)
    x = self.flatten(x)
    x = self.d1(x)
    return self.d2(x)

# モデルのインスタンスを作成
model = MyModel()

loss_object = tf.keras.losses.SparseCategoricalCrossentropy()

optimizer = tf.keras.optimizers.Adam()

train_loss = tf.keras.metrics.Mean(name='train_loss')
train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')

test_loss = tf.keras.metrics.Mean(name='test_loss')
test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_accuracy')

@tf.function
def train_step(images, labels):
  with tf.GradientTape() as tape:
    predictions = model(images)
    loss = loss_object(labels, predictions)
  gradients = tape.gradient(loss, model.trainable_variables)
  optimizer.apply_gradients(zip(gradients, model.trainable_variables))

  train_loss(loss)
  train_accuracy(labels, predictions)

@tf.function
def test_step(images, labels):
  predictions = model(images)
  t_loss = loss_object(labels, predictions)

  test_loss(t_loss)
  test_accuracy(labels, predictions)

EPOCHS = 5

for epoch in range(EPOCHS):
  for images, labels in train_ds:
    train_step(images, labels)

  for test_images, test_labels in test_ds:
    test_step(test_images, test_labels)

  template = 'Epoch {}, Loss: {}, Accuracy: {}, Test Loss: {}, Test Accuracy: {}'
  print (template.format(epoch+1,
                         train_loss.result(),
                         train_accuracy.result()*100,
                         test_loss.result(),
                         test_accuracy.result()*100))
  
  # 次のエポック用にメトリクスをリセット
  train_loss.reset_states()
  train_accuracy.reset_states()
  test_loss.reset_states()
  test_accuracy.reset_states()

# 計測した結果を出力
tat_time = time.time() - start_time
print ("実行時間:{0}".format(tat_time) + "[秒]")
```

## 検証2：M1チップセットでの性能測定をした人のコードでの実行時間の計測
[「Apple Silicon M1 でtensorflow-macosを実行したらめちゃくちゃ速かった。」](https://qiita.com/tomoyaeibu/items/46f2f3384a370df71d5e)のページにあるサンプルコードを一部修正し、各環境で実行時間を測定しました。

```
import tensorflow as tf
mnist = tf.keras.datasets.mnist
import time

(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(),
  tf.keras.layers.Dense(512, activation=tf.nn.relu),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

start = time.perf_counter()
model.fit(x_train, y_train, epochs=5)
model.evaluate(x_test, y_test)

end = time.perf_counter()
print(f"Elapsed time : {end - start:.3f} s.")
```

# 検証結果
検証1（TensorFlowの公式ページに公開されているコード）では、Colaboratory約30秒、MacBook Air約69秒と、MacBook Airの性能が悪い結果がでました。TensorFlowの公式ページに公開されているコードではM1チップセットに最適化されていないライブラリを多く使っているようです。

検証2（M1チップセットでの性能測定をした人のコード）では、Colaboratory約17秒、MacBook Air約6秒と、MacBook Airの性能が良い結果がでました。こちらも検証1と同様のワーニングが出ているのですが速く、M1チップセットに最適化されていないライブラリの使用量が少ない？

## 検証1の結果
Colaboratoryの方がMacBook Air（M1）より性能が良いという結果になりました。

### Colaboratoryの結果
「Tesla T4」が割り当てられて約30秒でした。

```
Epoch 1, Loss: 0.14365525543689728, Accuracy: 95.72000122070312, Test Loss: 0.06952153891324997, Test Accuracy: 97.88999938964844
Epoch 2, Loss: 0.04413086175918579, Accuracy: 98.62999725341797, Test Loss: 0.05124557763338089, Test Accuracy: 98.29000091552734
Epoch 3, Loss: 0.025515230372548103, Accuracy: 99.19666290283203, Test Loss: 0.0544661320745945, Test Accuracy: 98.45999908447266
Epoch 4, Loss: 0.013434577733278275, Accuracy: 99.56666564941406, Test Loss: 0.05688665434718132, Test Accuracy: 98.4000015258789
Epoch 5, Loss: 0.00990342814475298, Accuracy: 99.65666198730469, Test Loss: 0.061585117131471634, Test Accuracy: 98.47000122070312
実行時間:29.77555251121521[秒]
```

### MacBook Air（M1）の結果
測定結果は約69秒でした。ワーニングが出ており、M1チップセットに最適化されていない部分があるようです。

```
2020-12-19 18:42:50.161239: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:116] None of the MLIR optimization passes are enabled (registered 2)
2020-12-19 18:42:50.161937: W tensorflow/core/platform/profile_utils/cpu_utils.cc:126] Failed to get CPU frequency: 0 Hz
Epoch 1, Loss: 0.13489553332328796, Accuracy: 95.90166473388672, Test Loss: 0.06636839359998703, Test Accuracy: 97.5999984741211
Epoch 2, Loss: 0.042307861149311066, Accuracy: 98.68000030517578, Test Loss: 0.052898358553647995, Test Accuracy: 98.22000122070312
Epoch 3, Loss: 0.023042812943458557, Accuracy: 99.28500366210938, Test Loss: 0.05993646755814552, Test Accuracy: 98.07999420166016
Epoch 4, Loss: 0.013483826071023941, Accuracy: 99.54666137695312, Test Loss: 0.07232650369405746, Test Accuracy: 97.87999725341797
Epoch 5, Loss: 0.008992733433842659, Accuracy: 99.69166564941406, Test Loss: 0.056803181767463684, Test Accuracy: 98.33999633789062
実行時間:68.73311996459961[秒]
```

## 検証2の結果
MacBook Air（M1）の方がColaboratoryより性能が良いという結果になりました。

### Colaboratoryの結果
「Tesla T4」が割り当てられて約17秒でした。

```
Epoch 1/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.3601 - accuracy: 0.8915
Epoch 2/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.0979 - accuracy: 0.9698
Epoch 3/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.0670 - accuracy: 0.9786
Epoch 4/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.0494 - accuracy: 0.9844
Epoch 5/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.0410 - accuracy: 0.9868
313/313 [==============================] - 1s 2ms/step - loss: 0.0732 - accuracy: 0.9777
Elapsed time : 17.478 s.
```

### MacBook Air（M1）の結果
測定結果は約6秒でした。こちらも検証1と同様のワーニングが出ているのですが、速い…

```
2020-12-19 22:19:18.559533: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:116] None of the MLIR optimization passes are enabled (registered 2)
2020-12-19 22:19:18.559677: W tensorflow/core/platform/profile_utils/cpu_utils.cc:126] Failed to get CPU frequency: 0 Hz
Epoch 1/5
1875/1875 [==============================] - 1s 646us/step - loss: 0.3808 - accuracy: 0.8857
Epoch 2/5
1875/1875 [==============================] - 1s 641us/step - loss: 0.1122 - accuracy: 0.9661
Epoch 3/5
1875/1875 [==============================] - 1s 641us/step - loss: 0.0802 - accuracy: 0.9746
Epoch 4/5
1875/1875 [==============================] - 1s 638us/step - loss: 0.0643 - accuracy: 0.9795
Epoch 5/5
1875/1875 [==============================] - 1s 638us/step - loss: 0.0537 - accuracy: 0.9825
313/313 [==============================] - 0s 286us/step - loss: 0.0773 - accuracy: 0.9776
Elapsed time : 6.447 s.
```

# 参考情報
## 各環境のスペック
ColaboratoryとMacBook Air（M1）のスペックです。

### Colaboratory
Colaboratoryの紹介ページではスペックが明確に記載されていません。[Cloud GPU](https://cloud.google.com/gpu) に記載されている`NVIDIA K80、P100、P4、T4、V100`のどれかが割り当てられるようです。

### MacBook Air（M1）
MacBook AirはGPU8コアのものを購入しました。
* 8コアCPU
* 8コアGPU
* 16コアNeural Engine

## 検証環境の構築方法
ColaboratoryとMacBook Air（M1）の検証環境の構築方法です。

### Colaboratory
ColaboratoryはデフォルトでGPUを利用してくれません。GPUを利用してコードを実行するよう設定します。
1. 上部にあるメニューで「ランタイム」をクリックする。
2. ランタイムのメニューが表示されます。「ランタイムのタイプを変更」をクリックする。
3. ハードウェアアクセラレータの選択メニューが表示されます。ドロップダウンで「GPU」を選択します。
4. ハードウェアアクセラレータの選択メニューの右下にある「保存」をクリックします。

### MacBook Air（M1）
環境は[「Apple Silicon M1 でtensorflow-macosを実行したらめちゃくちゃ速かった。」](https://qiita.com/tomoyaeibu/items/46f2f3384a370df71d5e)のページに従って構築しました。
（注）上記ページで作った環境は、起動方法によってはコードが動きません。Macのターミナルからだと問題なく実行できるのですが、VSCodeのターミナルからだとエラーになります。

# 最後に
コードによって実行時間の差が大きい、VSCodeのターミナルからは実行できない、と謎の残る結果になりました。謎は引き続き調査し、進展が有り次第ページを更新します。（機械学習の勉強をこの環境でやっていくため、解決するしかない）

# 参考文献
* [エキスパートのための TensorFlow 2.0 入門](https://www.tensorflow.org/tutorials/quickstart/advanced)
* [Apple Silicon M1 でtensorflow-macosを実行したらめちゃくちゃ速かった。](https://qiita.com/tomoyaeibu/items/46f2f3384a370df71d5e)
* [Mac-optimized TensorFlow and TensorFlow Addons](https://github.com/apple/tensorflow_macos)