728x90
딥러닝 모델의 경량화나 속도에 대한 언급을 할때 FLOPs와 MACs에 대한 값을 지표로 많이 사용합니다.
그렇다면 우선 FLOPs 란 무엇일까요?
- FLOPs란 FLoating point OPerations를 의미하며, 사칙연산을 포함한 다양한 연산을 각각 하나의 연산으로 계산합니다.
그럼 MACs 는 무엇일까요?
- MACs는 Multiply–ACcumulates를 의미하며, 곱셈과 덧셈을 결합한 연산을 하나로 계산합니다.
- 딥러닝의 연산은 가중치에 바이어스를 더한 WX+b 와 같은 연산을 많이 사용하기에
FLOPs ≒ 2 X MACs라 볼 수 있습니다.
그렇다면 실제로 FLOPs ≒ 2 X MACs 이 성립할지 궁금했습니다.
그래서 우선 MACs를 계산하는 Library를 찾던 중 thop library를 찾게되었습니다.
테스트를 위해 resnet18을 사용했으며 결과는
MACs: 27336291840.0
가 나왔습니다.
FLOPs를 계산하는 Library는 calflops를 사용했습니다.
해당 Library는 FLOPs와 MACs를 모두 얻을 수 있었기에 위의 테스트와 같이 resnet18에 적용한 결과
FLOPs:54.5188 GFLOPS
MACs:27.2155 GMACs
와 같이 나왔습니다.
MACs와 FLOPs가 거의 비슷하기에 FLOPs ≒ 2 X MACs가 성립한다는 것을 확인할 수 있었습니다!!
주의할 부분은 FLOPS와 FLOPs가 다르다는 것입니다.
FLOPS는 초당 FLOPs를 처리하는 양을 나타냅니다 (flops/sec).
따라서 두 단어가 다르다는 것을 기억하면 좋을 것 같습니다!!!
사용한 코드는 아래와 같습니다
- resnet18
# 테스트를 위한 모델 사용
import torch
import torch.nn as nn
import torch.nn.functional as F
class ResidualBlock(nn.Module):
def __init__(self, inchannel, outchannel, stride=1):
super(ResidualBlock, self).__init__()
self.left = nn.Sequential(
nn.Conv2d(inchannel, outchannel, kernel_size=3, stride=stride, padding=1, bias=False),
nn.BatchNorm2d(outchannel),
nn.ReLU(inplace=True),
nn.Conv2d(outchannel, outchannel, kernel_size=3, stride=1, padding=1, bias=False),
nn.BatchNorm2d(outchannel)
)
self.shortcut = nn.Sequential()
if stride != 1 or inchannel != outchannel:
self.shortcut = nn.Sequential(
nn.Conv2d(inchannel, outchannel, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(outchannel)
)
def forward(self, x):
out = self.left(x)
out = out + self.shortcut(x)
out = F.relu(out)
return out
class ResNet(nn.Module):
def __init__(self, ResidualBlock, num_classes=10):
super(ResNet, self).__init__()
self.inchannel = 64
self.conv1 = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False),
nn.BatchNorm2d(64),
nn.ReLU()
)
self.layer1 = self.make_layer(ResidualBlock, 64, 2, stride=1)
self.layer2 = self.make_layer(ResidualBlock, 128, 2, stride=2)
self.layer3 = self.make_layer(ResidualBlock, 256, 2, stride=2)
self.layer4 = self.make_layer(ResidualBlock, 512, 2, stride=2)
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(512, num_classes)
def make_layer(self, block, channels, num_blocks, stride):
strides = [stride] + [1] * (num_blocks - 1)
layers = []
for stride in strides:
layers.append(block(self.inchannel, channels, stride))
self.inchannel = channels
return nn.Sequential(*layers)
def forward(self, x):
out = self.conv1(x)
out = self.layer1(out)
out = self.layer2(out)
out = self.layer3(out)
out = self.layer4(out)
out = self.avgpool(out)
out = out.view(out.size(0), -1)
out = self.fc(out)
return out
- MACs 계산(thop 사용)
from thop import profile
model = ResNet(ResidualBlock)
input = torch.randn(1, 3, 224, 224)
macs, params = profile(model, inputs=(input, ))
print(f"MACs: {macs}")
print(f"Params: {params}")
- FLOPs & MACs 계산(calflops 사용)
from calflops import calculate_flops
model = ResNet(ResidualBlock)
input_shape = (1, 3, 224, 224)
flops, macs, params = calculate_flops(model=model,
input_shape=input_shape,
output_as_string=True,
output_precision=4)
print("ResNet FLOPs:%s MACs:%s Params:%s \n" %(flops, macs, params))
코드는 제 깃허브에서도 확인하실 수 있습니다!!
'mingsDB' 카테고리의 다른 글
vs code 효과음 끄기 (0) | 2024.08.28 |
---|---|
Unity WebSocket 통신 (0) | 2024.08.14 |
Jetson nano 포트포워딩 (1) | 2024.07.13 |
Denoising-AutoEncoder 결과에 대한 고찰(Model Collapse) (2) | 2024.06.30 |
labelme2yolo에 label_list 지정하는 법 (1) | 2024.06.24 |