8/5

문제: M1 macbook Pro에서 MPS로 코드 돌리려고 하는데 에러발생

해결: 저번에 device 를 mps로 설정했을때 계속해서 오류가 나오던 이유는 torchsummary 때문이었습니다.

from torchsummary import summary

model = FCL().to(DEVICE)
summary(model, input_size=(1, 28, 28))
>>> 버그 발생 O

from torchsummary import summary

summary(model, input_size=(1, 28, 28))
model = FCL().to(DEVICE)
>>> 버그 발생 x

summary 와 model의 순서를 바꿨을때 버그가 발생 안하는것을 보고 summary에 문제가 있을거라고 예상했고

아래처럼 torchsummary 의 summary에서 device=”mps”로 설정했을 때

summary(model, input_size(1, 28, 28), device='mps')

>>> AssertionError: Input device is not valid, please specify ‘cuda’ of ‘cpu’

에러메세지가 뜨는것을 보고 문제를 확신했습니다

좀 더 찾아보니 torchsummary에서 업데이트된 torchinfo를 발견했고 이걸로 코드를 실행시켜보니 에러 없이 잘 작동하였으나 gpu 전력사용량이 전혀 올라가지 않는게 이상해서 이것저것 확인해본 결과

model.to(DEVICE)
print('model before summary: ', next(model.parameters()).device)
>>> model before summary: mps:0
summary(model, input_size=(1, 28, 28))
print('model after summary: ', next(model.parameters()).device)
>>> model after summary: cpu

가 출력되어서 이런 결론에 도달했습니다.

summary() 호출 시 모종의 이유로 mps로 넘어간 model이 다시 cpu로 돌아온다. 따라서 summary로 모델을 먼저 확인하고 model.to(DEIVCE) 를 호출하자

그래서 summary를 먼저 확인하고 model.to(DEVICE) (여기서 DEVICE=mps) 로 넘기고 코드를 실행하니 문제없이 작동했고 터미널에 아래 코드로 gpu의 전력사용량을 확인해보니 (GPU idle residency %)

sudo powermetrics —samplers gpu_power -i 1000

GPU 전력사용량이 증가하여 GPU를 사용해서 Pytorch코드를 돌리는것에 성공했습니다.

#성공적으로 MNIST MLP를 M1 Pro Macbook에서 훈련시킨 코드

import torch
import torch.optim as optim
import torch.nn as nn
from torchvision import transforms, datasets
from torchinfo import summary
# from torchsummary import summary 이건 mps device를 지원 안함
class FCL(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = torch.nn.Linear(1 * 28 * 28, 500, bias=True)
        self.relu = torch.nn.ReLU()
        self.fc2 = torch.nn.Linear(500, 500, bias=True)
        self.fc3 = torch.nn.Linear(500, 10, bias=True)

    def forward(self, x):
        x = torch.flatten(x, 1)
        x = self.relu(self.fc1(x))
        x = self.relu(self.fc2(x))
        out = self.fc3(x)
        return out

USE_MPS = torch.backends.mps.is_available()

if USE_MPS:
    print("Using MPS as DEVICE")
    DEVICE = torch.device("mps")
else:
    print("MPS isn't available. Using CPU as DEVICE")
    DEVICE = torch.device("cpu")

model = FCL()
print(model)
'''
summary() 호출 시 모종의 이유로 mps로 넘어간 model이 다시 cpu로 돌아온다.
따라서 summary로 모델을 먼저 확인하고 model.to(DEIVCE) 를 호출하자

model.to(DEVICE)
print('model before summary: ', next(model.parameters()).device)
summary(model, input_size=(1, 28, 28))
print('model after summary: ', next(model.parameters()).device)
'''

summary(model, input_size=(1, 28, 28))
model.to(DEVICE)

EPOCHS = 100
BATCH_SIZE = 50

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

optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)

criterion = nn.functional.cross_entropy

print('Batch ', BATCH_SIZE)
print('Total Epoch ', EPOCHS)
print('Total Iterations ', len(train_loader) * EPOCHS)
print()

iteration = 0
min_loss = 10e6

print(min_loss)
for epoch in range(EPOCHS):
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(DEVICE), target.to(DEVICE)
        output = model(data)

        loss = criterion(output, target)

        loss.backward()
        optimizer.step()
        optimizer.zero_grad()

        iteration += 1