はじめに
この記事では、Apple Silicon M5 Max を搭載したMacで、MLflow公式の Deep Learning Quickstart を実行します。
公式チュートリアルはこちらです。
第一回目:
第二回目:
公式チュートリアルの順番とコードをできるだけ維持し、
- Python環境を
uvで管理 - NVIDIA CUDAではなくApple Siliconの MPS
- ローカルMLflow用にSQLiteを使用
という環境差分だけを変更します。
公式チュートリアルでは、PyTorchを使用してFashionMNISTの画像分類モデルを学習し、MLflowで以下を記録します。
学習パラメータ
↓
学習Loss / Accuracy
↓
Validation Loss / Accuracy
↓
EpochごとのCheckpoint
↓
最終Model
↓
System Metrics
↓
モデルを再ロード
↓
推論
今回のゴール
今回構築する処理を図にすると次のようになります。
今回は前回までとは異なり、M5 MaxのGPUを実際にDeep Learningの学習に使用します。
公式チュートリアルとの対応
公式Deep Learning Quickstartは以下の順番です。
| この記事 | MLflow公式 |
|---|---|
| 事前準備 | Prerequisites: Set up MLflow and Pytorch |
| STEP 1 | Create a new experiment |
| STEP 2 | Prepare the dataset |
| STEP 3 | Define the model and optimizer |
| STEP 4 | Train the model |
| STEP 5 | View the training results in the MLflow UI |
| STEP 6 | Load back the model and run inference |
この記事でも、この順番を変更せずに進めます。
前提環境
前回までに作成した環境を引き続き使用します。
| 項目 | 環境 |
|---|---|
| Mac | Apple Silicon M5 Max |
| Architecture | arm64 |
| Python | 3.14.6 |
| Python管理 | uv |
| Deep Learning | PyTorch |
| GPU Backend | Apple MPS |
| Dataset | FashionMNIST |
| Model | MLP |
| Experiment Tracking | MLflow |
| Backend Store | SQLite |
| Notebook | JupyterLab |
NVIDIA GPU版との違い
MLflow公式チュートリアルでは、デバイスを次のように選択しています。
device = torch.device(
"cuda" if torch.cuda.is_available() else "cpu"
)
しかしM5 MaxにはNVIDIA CUDA GPUはありません。
そこでこの記事では、この部分だけをApple Silicon用に変更します。
device = torch.device(
"mps" if torch.backends.mps.is_available() else "cpu"
)
それ以外のPyTorchモデルや学習処理は、原則として公式コードをそのまま使用します。
事前準備
前回作成したプロジェクトへ移動します。
cd mlflow-quickstart
#Pythonを確認します。
uv run python --version
想定:
Python 3.14.6
PyTorchをインストールする
uv add torch torchvision
とします。
またMLflowのSystem Metricsを取得するため、
uv add psutil
も実行します。
まとめて追加しても構いません。
uv add torch torchvision psutil
uv sync
#PyTorchのバージョンを確認します。
uv run python -c "import torch; print(torch.__version__)"
#torchvisionも確認します。
uv run python -c "import torchvision; print(torchvision.__version__)"
% uv add torch torchvision
Resolved 200 packages in 220ms
Prepared 2 packages in 2.03s
Installed 8 packages in 169ms
+ filelock==3.32.2
+ fsspec==2026.7.0
+ mpmath==1.3.0
+ networkx==3.6.1
+ setuptools==84.0.0
+ sympy==1.14.0
+ torch==2.13.0
+ torchvision==0.28.0
% uv sync
Resolved 200 packages in 3ms
Checked 175 packages in 10ms
uv run python -c "import torch; print(torch.__version__)"
2.13.0
uv run python -c "import torchvision; print(torchvision.__version__)"
0.28.0
MPSが利用できるか確認する
ここはM5 Maxでは重要です。
uv run python -c "import torch; print('MPS built:', torch.backends.mps.is_built()); print('MPS available:', torch.backends.mps.is_available())"
以下のようになればM5 Max GPUを使用できます。
MPS built: True
MPS available: True
さらに確認します。
uv run python -c "import torch; print(torch.device('mps') if torch.backends.mps.is_available() else torch.device('cpu'))"
想定結果:
mps
JupyterLabを起動する
uv run jupyter lab
新しいNotebookを作成します。
ファイル名:
mlflow_deep_learning.ipynb
前回のNotebookとは分けて作成します。
STEP 1: Create a new experiment
ここからMLflow公式チュートリアルのSTEP 1です。
まずMLflowを読み込みます。
今回は前回まで使用してきたローカルSQLiteを引き続き使用するため、Tracking URIだけ追加します。
import mlflow
mlflow.set_tracking_uri(
"sqlite:///mlflow.db"
)
ここはローカルM5 Max環境のための追加設定です。
続いて、公式コードを実行します。
# The set_experiment API creates a new experiment if it doesn't exist.
mlflow.set_experiment("Deep Learning Experiment")
# IMPORTANT: Enable system metrics monitoring
mlflow.config.enable_system_metrics_logging()
mlflow.config.set_system_metrics_sampling_interval(1)
これで、
Deep Learning Experiment
というExperimentが作成されます。
またSystem Metricsも有効になります。
現在の状態
STEP 2: Prepare the dataset
次に学習データを準備します。
公式チュートリアルでは、
FashionMNIST
を使用します。
必要なライブラリを読み込みます。
ここでは公式コードのうち、deviceの部分だけMPS用に変更します。
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
# Define device
# device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device = torch.device(
"mps" if torch.backends.mps.is_available() else "cpu"
)
# Load and prepare data
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
train_dataset = datasets.FashionMNIST("data", train=True, download=True, transform=transform)
test_dataset = datasets.FashionMNIST("data", train=False, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=1000)
デバイスを確認します。
print(device)
M5 MaxでMPSが利用できれば、
mps
と表示されます。
FashionMNISTのデータ数を確認する
確認用として次のコードを実行します。
print(
"Training images:",
len(train_dataset),
)
print(
"Test images:",
len(test_dataset),
)
さらに1つのデータを確認します。
image, label = train_dataset[0]
print(
"Image shape:",
image.shape,
)
print(
"Label:",
label,
)
画像サイズは、
torch.Size([1, 28, 28])
となります。
STEP 3: Define the model and optimizer
次はNeural Networkを作ります。
ここは公式チュートリアルのコードをそのまま使用します。
import torch.nn as nn
class NeuralNetwork(nn.Module):
def __init__(self):
super().__init__()
self.flatten = nn.Flatten()
self.linear_relu_stack = nn.Sequential(
nn.Linear(28 * 28, 512),
nn.ReLU(),
nn.Linear(512, 512),
nn.ReLU(),
nn.Linear(512, 10),
)
def forward(self, x):
x = self.flatten(x)
logits = self.linear_relu_stack(x)
return logits
model = NeuralNetwork().to(device)
最後の、
.to(device)
によって、モデルがM5 Max GPUへ送られます。
確認します。
print(model)
さらにモデルのパラメータが存在するデバイスを確認します。
print(
next(model.parameters()).device
)
Neural NetworkがM5 MaxのMPSデバイスへ配置されたことを確認
Training Parameterを設定する
ここも公式コードをそのまま使用します。
# Training parameters
params = {
"epochs": 5,
"learning_rate": 1e-3,
"batch_size": 64,
"optimizer": "SGD",
"model_type": "MLP",
"hidden_units": [512, 512],
}
# Define optimizer and loss function
loss_fn = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=params["learning_rate"])
STEP 4: Train the model
いよいよM5 Max GPUで学習します。
ここは公式コードを変更します。
どうやら、2026/08/13段階のAPI仕様とサンプルがミスマッチしており、このままでは動きませんでした。
公式チュートリアルでは input_example なしで log_model() を呼んでいますが、現在のAPIでは serialization_format="pt2" がデフォルトで、pt2 には input_example が必須です。
以下の2カ所を変更します
mlflow.pytorch.log_model(
model,
name=f"checkpoint_{epoch}"
)
mlflow.pytorch.log_model(
model,
name=f"checkpoint_{epoch}",
serialization_format="pickle",
)
model_info = mlflow.pytorch.log_model(
model,
name="final_model",
serialization_format="pickle",
)
またこの後表示するMlflowのSytem MetricsにこのままだとGPU系が表示できません。
そこで以下のメトリクスを取得できるように修正しました。
CPU utilization ← MLflow標準
System Memory ← MLflow標準
MPS Tensor Memory ← 今回追加
MPS Driver Memory ← 今回追加
一方、
GPU utilization 72%
のようなM5 Max GPU演算使用率そのものは、このコードでは取得していません。
今回は大変そうなので除外します。
PyTorchのMPS APIにはメモリ取得APIはありますが、NVIDIA/NVMLのような単純なGPU utilization取得APIは用意されておらず、MPSのプロファイリングはOS Signpostを生成してXcode Instrumentsで見る仕組みが公式に提供されています。
それでは修正したコードを実行します。
with mlflow.start_run() as run:
# Log training parameters
mlflow.log_params(params)
for epoch in range(params["epochs"]):
model.train()
train_loss, correct, total = 0, 0, 0
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
# Forward pass
optimizer.zero_grad()
output = model(data)
loss = loss_fn(output, target)
# Backward pass
loss.backward()
optimizer.step()
# Calculate metrics
train_loss += loss.item()
_, predicted = output.max(1)
total += target.size(0)
correct += predicted.eq(target).sum().item()
# Log batch metrics (every 100 batches)
if batch_idx % 100 == 0:
batch_loss = train_loss / (batch_idx + 1)
batch_acc = 100.0 * correct / total
step = epoch * len(train_loader) + batch_idx
mlflow.log_metrics(
{
"batch_loss": batch_loss,
"batch_accuracy": batch_acc,
},
step=step,
)
# Apple Silicon / MPS GPU metrics
if device.type == "mps":
mps_tensor_memory_mb = (
torch.mps.current_allocated_memory() / 1024**2
)
mps_driver_memory_mb = (
torch.mps.driver_allocated_memory() / 1024**2
)
mps_recommended_memory_mb = (
torch.mps.recommended_max_memory() / 1024**2
)
mlflow.log_metrics(
{
"system/mps_tensor_memory_megabytes":
mps_tensor_memory_mb,
"system/mps_driver_memory_megabytes":
mps_driver_memory_mb,
"system/mps_recommended_memory_megabytes":
mps_recommended_memory_mb,
},
step=step,
)
# Calculate epoch metrics
epoch_loss = train_loss / len(train_loader)
epoch_acc = 100.0 * correct / total
# Validation
model.eval()
val_loss, val_correct, val_total = 0, 0, 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
loss = loss_fn(output, target)
val_loss += loss.item()
_, predicted = output.max(1)
val_total += target.size(0)
val_correct += predicted.eq(target).sum().item()
# Calculate and log epoch validation metrics
val_loss = val_loss / len(test_loader)
val_acc = 100.0 * val_correct / val_total
# Log epoch metrics
mlflow.log_metrics(
{
"train_loss": epoch_loss,
"train_accuracy": epoch_acc,
"val_loss": val_loss,
"val_accuracy": val_acc,
},
step=epoch,
)
# Log checkpoint at the end of each epoch
mlflow.pytorch.log_model(
model,
name=f"checkpoint_{epoch}",
serialization_format="pickle",
)
print(
f"Epoch {epoch + 1}/{params['epochs']}, "
f"Train Loss: {epoch_loss:.4f}, "
f"Train Acc: {epoch_acc:.2f}%, "
f"Val Loss: {val_loss:.4f}, "
f"Val Acc: {val_acc:.2f}%"
)
# Log the final trained model
model_info = mlflow.pytorch.log_model(
model,
name="final_model",
serialization_format="pickle",
)
学習結果
実行すると、次のような結果が5 Epoch分表示されます。
Epoch 1/5, Train Loss: ..., Train Acc: ...%, Val Loss: ..., Val Acc: ...%
Epoch 2/5, Train Loss: ..., Train Acc: ...%, Val Loss: ..., Val Acc: ...%
Epoch 3/5, Train Loss: ..., Train Acc: ...%, Val Loss: ..., Val Acc: ...%
Epoch 4/5, Train Loss: ..., Train Acc: ...%, Val Loss: ..., Val Acc: ...%
Epoch 5/5, Train Loss: ..., Train Acc: ...%, Val Loss: ..., Val Acc: ...%
実際の数値は実行環境によって異なります。
M5 Max GPUを使用してFashionMNISTを5 Epoch学習
学習中に何が起きていたのか
処理だけを図にすると次のようになります。
MLflowに記録された内容
1回のRunの中に、
Deep Learning Experiment
│
└── Run
│
├── Parameters
│ ├── epochs
│ ├── learning_rate
│ ├── batch_size
│ ├── optimizer
│ ├── model_type
│ └── hidden_units
│
├── Metrics
│ ├── batch_loss
│ ├── batch_accuracy
│ ├── train_loss
│ ├── train_accuracy
│ ├── val_loss
│ └── val_accuracy
│
├── checkpoint_0
├── checkpoint_1
├── checkpoint_2
├── checkpoint_3
├── checkpoint_4
│
└── final_model
が保存されています。
STEP 5: View the training results in the MLflow UI
次にMLflow UIから学習結果を確認します。
JupyterLabとは別のTerminalを開きます。
プロジェクトディレクトリへ移動します。
cd mlflow-quickstart
MLflow Serverを起動します。
uv run mlflow server \
--backend-store-uri sqlite:///mlflow.db \
--port 5000
ブラウザから、
http://localhost:5000
へアクセスします。
Deep Learning Experimentを開く
以下を選択します。
Deep Learning Experiment
今回作成したRunが表示されます。
RunのOverviewを確認する
Runをクリックします。
Overviewでは、
Duration
Start Time
Parameters
Models
Tags
などを確認できます。
Model Metricsを確認する
次に、
Model Metrics
を開きます。
今回記録した、
batch_loss
batch_accuracy
train_loss
train_accuracy
val_loss
val_accuracy
を見ることができます。
MLflowから学習LossとValidation Lossの推移を確認
見てほしいグラフ
特に重要なのが、
横軸:Epoch(STEP)
縦軸:Loss
です。
理想的には、
Loss
│\
│ \
│ \ Validation Loss
│ \ ______
│
│\
│ \
│ \________ Training Loss
│
└──────────────── Epoch
のようにLossが低下していきます。
System Metricsを確認する
次に、
System Metrics
タブを開きます。
今回、
mlflow.config.enable_system_metrics_logging()
を設定したため、学習中のシステム情報が記録されています。
M5 Maxの場合、例えば、
CPU
System Memory
Disk
Network
などを確認できます。
今回apple用に追加したmetricsはこれです。
#現在PyTorchのTensorがMPS GPU上で占有しているメモリ量
system/mps_tensor_memory_megabytes
#Metal driverがこのプロセス向けに確保しているメモリ全体
system/mps_driver_memory_megabytes
#Metalデバイスが示す推奨最大Working Setサイズです。
#PyTorch公式APIでもMetalのrecommendedMaxWorkingSetSizeを返すものとされています
system/mps_recommended_memory_megabytes
Checkpointを確認する
RunのModel / Artifacts周辺を確認します。
これは公式コードの、
mlflow.pytorch.log_model(
model,
name=f"checkpoint_{epoch}",
)
によって作られています。
STEP 6: Load back the model and run inference
最後に、MLflowへ保存したモデルを再ロードします。
まず今回のRun IDを確認します。
Notebookで、
print(
run.info.run_id
)
を実行します。
例えば、
a1b2c3d4...
というRun IDが表示されます。
モデルをロードする
公式チュートリアルでは、
model = mlflow.pytorch.load_model(
"runs:/<run_id>/final_model"
)
となっています。
<run_id> を先ほど取得したRun IDへ置き換えます。
例えば、
model = mlflow.pytorch.load_model(
"runs:/a1b2c3d4xxxxxxxx/final_model"
)
です。
以下は公式チュートリアルのコードです。
# Load the final model
model = mlflow.pytorch.load_model(
"runs:/<run_id>/final_model"
)
# or load a checkpoint
# model = mlflow.pytorch.load_model(
# "runs:/<run_id>/checkpoint_<epoch>"
# )
model.to(device)
model.eval()
# Resume the previous run to log test metrics
with mlflow.start_run(
run_id=run.info.run_id
) as run:
# Evaluate the model on the test set
test_loss, test_correct, test_total = (
0,
0,
0,
)
with torch.no_grad():
for data, target in test_loader:
data, target = (
data.to(device),
target.to(device),
)
output = model(data)
loss = loss_fn(
output,
target,
)
test_loss += loss.item()
_, predicted = output.max(1)
test_total += target.size(0)
test_correct += (
predicted.eq(target)
.sum()
.item()
)
# Calculate and log final test metrics
test_loss = (
test_loss
/ len(test_loader)
)
test_acc = (
100.0
* test_correct
/ test_total
)
mlflow.log_metrics(
{
"test_loss": test_loss,
"test_accuracy": test_acc,
}
)
print(
f"Final Test Accuracy: {test_acc:.2f}%"
)
推論結果
最後に、
Final Test Accuracy: xx.xx%
と表示されます。
実際の値は学習結果によって変わります。
最終的な処理
ここまでを全部まとめると、
ここから初学者向け解説
ここから、今回登場した技術を整理します。
Deep Learningとは
前回まではRandom ForestやLogistic Regressionを使いました。
今回は、
Neural Network
を使います。
Neural Networkでは、多数の数値を持つ層を組み合わせ、
入力
↓
計算
↓
特徴を抽出
↓
さらに計算
↓
分類
を行います。
今回の場合、
となっています。
FashionMNISTとは
FashionMNISTは、
28 × 28 Pixel
Grayscale
10 Class
の衣類画像Datasetです。
例えば、
T-shirt
Trouser
Pullover
Dress
Coat
Sandal
Shirt
Sneaker
Bag
Ankle boot
などを分類します。
今回のNeural Networkは、
画像
↓
どの服なのか
を予測します。
MPSとは
MPSは、
Metal Performance Shaders
の略です。
NVIDIA GPUでは、
PyTorch
↓
CUDA
↓
NVIDIA GPU
となります。
Apple Siliconでは、
PyTorch
↓
MPS
↓
Metal
↓
Apple GPU
となります。
今回、
device = torch.device(
"mps"
)
としたことでM5 Max GPUを利用しています。
なぜ .to(device) が必要なのか
PyTorchでは、
Model
と、
Data
の両方を同じデバイスに置く必要があります。
今回、
model.to(device)
でモデルをMPSへ移動しました。
さらに、
data.to(device)
target.to(device)
でデータもMPSへ移しています。
Apple SiliconはUnified Memory Architectureですが、PyTorch上ではTensorがどのdeviceで計算されるかを明示する必要があります。
Epochとは
Epochとは、
学習データ全部を1回学習すること
です。
今回、
"epochs": 5
なので、
FashionMNIST全部
↓
Epoch 1
FashionMNIST全部
↓
Epoch 2
...
FashionMNIST全部
↓
Epoch 5
となります。
Batchとは
FashionMNISTの全データを一度にGPUへ渡すわけではありません。
今回、
batch_size=64
なので、
64 Images
↓
Training
次の64 Images
↓
Training
次の64 Images
↓
Training
と処理します。
これをBatchと呼びます。
Forward Passとは
output = model(data)
がForward Passです。
画像
↓
Neural Network
↓
予測結果
を計算します。
Lossとは
モデルの、
予測
と、
正解
のズレを数値化したものです。
今回は、
nn.CrossEntropyLoss()
を使用しています。
基本的には、
Lossが大きい
↓
まだ予測が悪い
Lossが小さい
↓
予測が良くなった
と考えます。
Backward Passとは
Forward PassでLossを計算した後、
loss.backward()
を実行します。
これにより、
どのParameterを
どの方向へ
どれだけ修正すれば
Lossが減るのか
を計算します。
Optimizerとは
計算したGradientを使って実際にモデルのParameterを変更するのが、
optimizer.step()
です。
今回は、
SGD
を使用しています。
全体として、
を繰り返しています。
これがDeep Learningの「学習」です。
TrainingとValidationの違い
Training Datasetは、
モデルを学習するため
に使用します。
Validation Datasetは、
学習していないデータでも
正しく予測できるか
を確認するために使用します。
今回、
train_loss
train_accuracy
val_loss
val_accuracy
の両方をMLflowへ記録している理由です。
なぜValidation Lossを見るのか
例えば、
Epoch 1
Train Loss ↓
Val Loss ↓
Epoch 2
Train Loss ↓
Val Loss ↓
Epoch 3
Train Loss ↓
Val Loss ↓
Epoch 4
Train Loss ↓
Val Loss ↑
Epoch 5
Train Loss ↓
Val Loss ↑
となった場合、
Training Datasetにはどんどん適応しているのに、
未知データへの性能が悪化
しています。
これは、
Overfitting
つまり過学習の兆候です。
だからMLflowで、
train_loss
vs
val_loss
をグラフ化する意味があります。
Checkpointとは
今回、
mlflow.pytorch.log_model(
model,
name=f"checkpoint_{epoch}",
)
を実行しています。
その結果、
Epoch 0 → checkpoint_0
Epoch 1 → checkpoint_1
Epoch 2 → checkpoint_2
Epoch 3 → checkpoint_3
Epoch 4 → checkpoint_4
となります。
例えばEpoch 5で性能が悪化した場合、
checkpoint_2
まで戻すことも可能になります。
final_modelとは
最後に、
mlflow.pytorch.log_model(
model,
name="final_model",
)
として、最終状態のモデルも保存しています。
つまり、
Training
│
├── checkpoint_0
├── checkpoint_1
├── checkpoint_2
├── checkpoint_3
├── checkpoint_4
│
└── final_model
です。
System Metricsとは
MLflowではモデル性能だけでなく、
CPU
Memory
Disk
Network
GPU
などの計算資源も記録できます。
これが重要なのは、
モデルA
Accuracy 90%
Training 10秒
モデルB
Accuracy 90.1%
Training 10時間
だった場合、
AccuracyだけではモデルBが良く見えてしまうからです。
MLflowでは、
Model Performance
+
System Performance
の両方を観察できます。
Apple GPUがSystem Metricsに出ない理由
MLflowの標準GPU Metrics収集は主にNVIDIA Management Libraryを利用します。
そのため、
NVIDIA GPU
↓
nvidia-ml-py
↓
MLflow GPU Metrics
は可能ですが、
Apple GPU
↓
MPS
↓
MLflow標準GPU Metrics
には現時点で直接対応していません。
したがってM5 Maxでは、
PyTorch
MPS available = True
device = mps
model device = mps:0
を確認することが重要です。
Quickstartからここまで何が進化したのか
最初のQuickstartでは、
scikit-learn
↓
1回Model学習
↓
MLflow
でした。
Hyperparameter Tuningでは、
Optuna
↓
大量のModel学習
↓
MLflow
↓
Best Model
になりました。
そして今回は、
PyTorch
↓
M5 Max GPU
↓
EpochごとのTraining
↓
Metrics
↓
Checkpoint
↓
System Metrics
↓
MLflow
まで進みました。
3つのチュートリアルを比較
Deep Learningでは最終Accuracyだけではなく、
Epoch
Loss
Accuracy
Checkpoint
System Resource
まで時系列で管理することが重要になります。
今回特に重要なのは、
NVIDIA CUDAがなくても、Apple SiliconのMPSを使ってPyTorchによるDeep LearningをGPU実行できる
という点です。
MLflowと組み合わせることで、
何を学習したか
どの設定だったか
Lossはどう変化したか
Accuracyはどう変化したか
どのCheckpointだったか
どのモデルが生成されたか
まで記録できます。
これによってM5 Max単体でも、
モデル開発 → 実験管理 → モデル保存 → 再現
という一連のML開発フローを構築できます。














