あとで読み返したらめっちゃ間違っていたので修正しましたm(_ _)m
(2022年5月23日)
はじめに
この記事によると,PyTorch(pytorch==1.12.0)でM1 MacでGPUが使えるようになったらしい.
ということで環境構築して使ってみた記事です.
※2022年5月19日現在の内容です.
誰かが同じ轍を踏んでなければいいな,と思い書くことにしました.
環境構築をするたびに無力感を感じるので,タイトルに『雑魚のための』とつけました.
0から構築する人はこちらの手順でおkです
- Miniforge3-MacOSX-arm64をクリックしてminiforgeのインストーラーをダウンロード
- 下のコマンドでインストールから環境構築まで一気に実行
bash Miniforge3-MacOSX-arm64.sh
conda create -n pytorch python=3.8
conda activate pytorch
pip3 install --pre torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/nightly/cpu
気をつけること
このあたりに気をつけて進めていきましょう.
- miniforgeでpython環境を構築←超大事
- python3.8の環境構築する
- pip3でtorchとtorchvisionのNightlyを入れる←まぁ大事(というかこれができればおk)
- 動かすときはtorch.device("mps")を使う
やったこと
最終的に環境構築がうまくいったのはこの手順でした.
- anacondaを消してminiforgeをいれる
- python3.8の環境構築
- torchインストール
- 動かす
順番に見ていきましょう
anacondaを消してminiforgeを入れる
情弱雑魚な自分はなんの考えもなしにanacondaを入れていました.
PyTorch公式ブログにも書いてありますが,pythonのアーキテクチャがarm64じゃないと動きません.(私は当初x86運用でちゃんと動きませんでした.)
そしてanacondaで入れるとx86アーキテクチャのpythonがインストールされるようです.
To get started, just install the latest Preview (Nightly) build on your Apple silicon Mac running macOS 12.3 or later with a native version (arm64) of Python.
なんなら...M1Macはanacondaではなくminifogeをいれるべき,といたるとこでみかけます(これだから情弱は←).
いたるとこの一例.
自分のpythonのアーキテクチャがなんなのかはpythonでこのコードを実行するとわかります.
import platform
print(platform.platform())
面倒ならコマンドでこうです.
python
import platform
platform.platform()
armならarm64
と出ますが,x86ならx86が含まれたそれなりの文字列
が出ます.
私のようにx86が含まれたそれなりの文字列
が出てしまった残念な方は,まずはanacondaを消しましょう(きっとanacondaをインストールしているはずです).
この記事とか参考になります.
とりあえず,conda install anaconda-clean
してanaconda-clean
してフォルダ消せば大丈夫でした.
いったんここでPCごと再起動しておきましょう.
さて,続いてminiforgeのgithubにあるMiniforge3-MacOSX-arm64を落としましょう(一応ハイパーリンクにしておきます).
シェルスクリプトがダウンロードされるので,迷わす実行しましょう.(雑魚は迷わないのです.)
bash Miniforge3-MacOSX-arm64.sh
ここで,pythonのアーキテクチャを確認しておきましょう.
python
import platform
platform.platform()
arm64
と出ればオッケーです!
python3.8の環境構築
miniforgeもanacondaと同じように環境を構築したりアクティベートしたりできます.
conda create -n hoge python=3.8
conda activate hoge
3.9と3.7は試してないのでわかりません.
ただし,3.10.0は失敗しました.
どこぞの雑魚ライブラリ(雑魚は私だ)ライブラリで3.10.0を勘違いして3.1を呼び出してエラーを吐いてました.
公式githubでは3.7.6か3.8.1以上が推奨されていますね.
torchインストール
これも公式に従ってインストールしましょう.
Preview(Nightly)->Mac->Pip->Python->CPUの組み合わせで出るコードを使いましょう.
pip3でインストールしましょう.condaでのインストールもソースからビルドするのもうまくいきませんでした.おそらく雑魚には向いていなかったのです.
※condaに関しては,現時点でconda-forge内でtorchvisionに対応するpytorchのバージョンが1.11.0だった模様.
pip3 install --pre torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/nightly/cpu
一度ここでバージョンを確認しておきましょう.
目指すはpytorch==1.12.0
です.
python
import torch
torch.__version__
'1.12.0.dev20220519'
的な感じで1.12.0
以上であることを示すものが出ればオッケーです.
ここでいったん打ち上げをしましょう.
動かす
ここまで大人しくついてきていれば動くはずです.
こんな感じで,torch.device()
に引数としてmps
を与えてやります.
※Metal Performance Shaders (MPS)だそうです.
a = torch.tensor([3])
device = torch.device("mps")
a.to(device)
ちなみに私が試したコードはこちらです.FashionMNISTを解いています.
公式のチュートリアルQuickStartのコードです.
自分では書きません雑魚だからです.
サンプルコード
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor
import time
# Download training data from open datasets.
training_data = datasets.FashionMNIST(
root="data",
train=True,
download=True,
transform=ToTensor(),
)
# Download test data from open datasets.
test_data = datasets.FashionMNIST(
root="data",
train=False,
download=True,
transform=ToTensor(),
)
batch_size = 64
# Create data loaders.
train_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)
# Get cpu or gpu device for training.
# device = "cpu"
device = "mps"
# device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Using {device} device")
# Define model
class NeuralNetwork(nn.Module):
def __init__(self):
super(NeuralNetwork, self).__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)
print(model)
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)
def train(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset)
model.train()
for batch, (X, y) in enumerate(dataloader):
X, y = X.to(device), y.to(device)
# Compute prediction error
pred = model(X)
loss = loss_fn(pred, y)
# Backpropagation
optimizer.zero_grad()
loss.backward()
optimizer.step()
if batch % 100 == 0:
loss, current = loss.item(), batch * len(X)
# print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]")
def test(dataloader, model, loss_fn):
size = len(dataloader.dataset)
num_batches = len(dataloader)
model.eval()
test_loss, correct = 0, 0
with torch.no_grad():
for X, y in dataloader:
X, y = X.to(device), y.to(device)
pred = model(X)
test_loss += loss_fn(pred, y).item()
correct += (pred.argmax(1) == y).type(torch.float).sum().item()
test_loss /= num_batches
correct /= size
# print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
start = time.time()
epochs = 50
for t in range(epochs):
# print(f"Epoch {t+1}\n-------------------------------")
train(train_dataloader, model, loss_fn, optimizer)
test(test_dataloader, model, loss_fn)
print("Done!")
end = time.time()
print(end-start)
結論から申しますと,転送速度がボトルネックになったようでcpuのまま走らせた方が速かったです.
50エポック学習させた結果こんな感じでした.
デバイス | 学習時間 |
---|---|
cpu | 133.8357961177826 |
mps | 208.2523648738861 |
まぁ,MNISTですからね...
おわりに
なにはともあれ無事に環境構築できてよかったです.ありがとうPyTorch.
終わりよければ全てよし.
(というか,M1 Macのpytorchのインストール記事になってしまった.)
もっと重いモデルとデータセットで試してきちんと効能を得たいところ.
何か変な部分があればご指摘いただければと思います(何せ雑魚なので...)