📌 빠르게
Further Quetion
1) 이번 강의는 lightweight modeling과 어떤 관계가 있을까? (이 강의를 만든 목적은 무엇일까?)
저번 시간에 압축을 통해 공간(메모리)를 줄였다면 , 'acceleration'을 통해 시간을 줄일 수 있다.
- bandwidth는 기간 내 네트워크를 통해 전송되는 데이터의 양
- latency는 어떤 일을 처리(source ->target)할 때 걸리는 시간
- throughput는 일종의 처리율로 기간 내 "성공적으로" 네트워크 전송을 통해 처리된 데이터의 양
cpu에서는 병목현상이 일어나고, gpu는 '병렬'로 처리해 throughput을 증가시켜준다.
2) 두 계산에 차이가 발생하는 이유는 뭘까?(p 4)
numpy는 c언어로 구현돼 파이썬보다 더 빠르게 작동한다.
그렇기 때문에 ndarray는 list와 다르게 무조건 같은 타입의 elem들로만 구성된다.
import numpy as np
import time
size =1000000
# list excution time
lst = range(size)
initial_time = time.time()
result_list = [(a*b) for a,b in zip(lst,lst)]
print(round(time.time() - initial_time , 4) ,'seconds')
#numpy
array = np.arange(size)
initial_time = time.time()
result_array = array*array
print(round(time.time() - initial_time , 4) ,'seconds')
3) Compression과 acceleration의 관계는? (p 22)
Hardware의 성능은 주로 Acceleration(속도)에 초점을 둔다.
software의 highlevel alnguage를 받아 중간 complier 과정을 통해 중간단계의 assembly language로 바꿔준다.
assembler를 기계가 이해할 수 있는 machine language로 받아 hardware에서 연산을 수행한다.
Toy Code
remote fuction은 ray의 프로세스에 의해 비동기적으로 실행된다.
직렬보다는 병렬로 계산할 때 속도가 훨씬 빨리진다.
아래 식의 시간 복잡도는 왼쪽은 N , 오른쪽은 $logN$이 된다.
import time
@ray.remote
def add(x, y):
time.sleep(1)
return x + y
start = time.time()
# Aggregate the values slowly. This approach takes O(n) where n is the
# number of values being aggregated. In this case, 7 seconds.
id1 = add.remote(1, 2)
id2 = add.remote(id1, 3)
id3 = add.remote(id2, 4)
id4 = add.remote(id3, 5)
id5 = add.remote(id4, 6)
id6 = add.remote(id5, 7)
id7 = add.remote(id6, 8)
result = ray.get(id7)
print("Vanilla version : {}".format(time.time() - start))
# Vanilla version : 7.029922008514404
start = time.time()
# Aggregate the values in a tree-structured pattern. This approach
# takes O(log(n)). In this case, 3 seconds.
id1 = add.remote(1, 2)
id2 = add.remote(3, 4)
id3 = add.remote(5, 6)
id4 = add.remote(7, 8)
id5 = add.remote(id1, id2)
id6 = add.remote(id3, id4)
id7 = add.remote(id5, id6)
result = ray.get(id7)
print("Advanced version : {}".format(time.time() - start))
#Advanced version : 4.019379138946533
📌 가지치기 : pruning
미니코드 : Masking
import numpy as np
from numpy.ma import array, masked_array,masked_values , masked_outside
vec1 = np.round(np.linspace(0.1,1,5) ,3)
print('vec1: ' ,vec1)
print('array : ' ,array(vec1 , mask = [0,0,0,1,0]))
print('masked array : ' , masked_array(np.array(vec1) , mask = [0,0,0,1,0]))
print('masked_outside : ' , masked_outside(vec1 , 0.2,0.9) , '\n')
# vec1: [0.1 0.325 0.55 0.775 1. ]
# array : [0.1 0.325 0.55 -- 1.0]
# masked array : [0.1 0.325 0.55 -- 1.0]
# masked_outside : [-- 0.325 0.55 0.775 --]
vec2 = [0,1,2,3,4,-9999]
print('vec2 : ' , vec2)
print('masked_values : ' , masked_values(vec2 , -9999))
print('masked_values & filled : ', masked_values(vec2, -9999).filled(5555))
# vec2 : [0, 1, 2, 3, 4, -9999]
# masked_values : [0 1 2 3 4 --]
# masked_values & filled : [ 0 1 2 3 4 5555]
Further Questoin
1) 이번 강의는 lightweight modeling과 어떤 관계가 있을까? (이 강의를 만든 목적은 무엇일까?)
pruning은 딥러닝 경량화 대표 기술 중 하나이다.
일정 비율만큼 중요하다고 생각 되는 것은 살리고, 중요하지 않다고 생각 되는 것은 죽여버린다.
장점 : inference 속도도 빨라지고, generalization 효과도 얻을 수 있다.
단점 : information loss가 증가하고, 너무 sparse해지면 hardware에서의 최적화가 힘들어진다.
그림을 보면 dropout과 비슷하게생겼는데 이 둘의 차이점은
pruning의 경우 한 번 잘라내면 고정되어 inference 시 까지도 계속 없다.
하지만 dropout같은 경우 한 타임동안만 일시적으로 잘라내는 것이고 inference 시에는 잘라내는 것 없이 모두 사용한다.
알고리즘은 아래와 같다.
1. 초기화
2. 수렴할 때 까지 훈련
3. 1로 fill된 마스크 준비
4. W의 score에 mask를 씌어줘 pruning 해준 후 finetunning 을 n번 반복
pruning을 하면 size와 speed 사이의 tradeoff 이 존재한다.
size가 작아지면 (number of parameter) 속도는 빨라진다. (number of FLOPs)
그렇다고 성능까지 무조건 떨어지지는 않는다. VGG의 경우 pruned model이 acc도 높은 걸 확인할 수 있다.
아래 논문에서 EfficientNet에 대해서 Pruned model이 표시되어있지 않은데 그 이유는,,,, 그냥 최근에 나와서 pruned model이 없어서라고 한다;
- pruning의 종류
1) 무엇을 prune 할지 2) 어떻게 prune 할 지 3) 언제 prune할 지 4) 얼마나 자주 prune 할 지 4가지의 척도로 나눠서 생각해볼 수 있다.
- unstructured pruning과 structured pruning
unstructured pruning은 특정 가중치에 대해 하는 게 아니라 그냥 neuron이나 layer 등 아무거나 연결성이 낮아보이면 삭제하는 것이다. 이러면 sparse해지기 쉽고 hardware에서의 optimize가 어렵다.
반대로 structured pruning은 특정 filter를 삭제한다. filter전체, 혹은 채널 , shape의 일부를 삭제할 수 있다.
- Iterative pruning
너무 많은 파라미터가 한번에 삭제되면 모델의 성능도 급감하기 때문에, one-shot이 아닌 Iterative pruning을 주로 사용한다고 한다. pruning과 retrain을 반복해서 loss가 줄어드는 것을 방지할 수 있다. 반복을 통해 조금조금씩 파라미터를 삭제해간다.
training iteration j, pruning할 비율 p , 최대 loss 손실 비율 k 를 하이퍼파라미터로 설정한다
1. initial state 로 네트워크를 초기화한다.
2. mask를 node/filter에 적용해준다.
3. j번만큼 네트워크를 train시킨다.
4. p만큼 node/filter를 pruning해준 후, 마스크를 update해준다.
위 과정을 val acc가 원레 acc의 k배가 될 때까지 반복한다.
2) Mask는 언제 사용하는게 유용할까? (p 4)
pruning시, node/ filter를 제거할 때 mask를 씌어준다.
3) Lottery Ticket Hypothesis의 key idea는 무엇인가? (p 28)
network가 주어졌을 때, 더 적은 training 횟수 , 더 높은 accuracy, 더 적은 parameters를 가지는 subsnetwork가 존재한다는 가정이다.
그럴 듯하지만 증명이 안 된 가정에다가, 저런 subnetwork를 찾기위해서는 결국 network부터 시작을 해야한다.
그래서 그 이후 나온 논문들은 이를 보완하는 형식인데 "Weight rewinding"을 제안한다.
이전 iterative magnitude pruning 방식은 retrain 시 node/filter들을 inital state로 초기화해줬다.
Rewinding은 좀 더 학습된 네트워크로 초기화해주자는 아이디어이다.
하이퍼파라미터로 정한 k번까지만 train 시킨 후, 이때의 네트워크를 저장해준다.
이후 수렴할 때 까지 훈련하고 pruning한 후 마스크를 적용해준다.
Retrain 시, 마스크가 적용된 네트워크를 k번 학습하고 저장해놓은 네트워크를 이용하여 초기화해준다.
Toy Code
pruning을 하면 model의 사이즈가 더 커진다! 왜일까?
pytorch에서는 직접 0으로 바꾸는 대신 mask 텐서를 추가로 생성해 곱해줌으로써 pruning이 진행된다.
import torch.nn.utils.prune as prune
prune.random_unstructured(module, name="weight", amount=0.3)
그렇기때문에 원래 모델에 마스크 텐서가 추가 되기 때문에 pruning을 할 수록 모델의 사이즈가 커지게 된다.
마스크는 named_buffers()를 이용하여 확인할 수 있다.
print(list(module.named_buffers()))
원본 모델의 parameter는 "weight_orig", "bias_orig"과 같은 형태로 보존해준다.
print(list(module.named_parameters()))
# [('bias', Parameter containing:
# tensor([ 0.1874, 0.0468, 0.0102, -0.1306, 0.2191, 0.4025],
# requires_grad=True)), ('weight_orig', Parameter containing:
# tensor([[[[ 0.1672, -0.1517, -0.1711],
# [ 0.1280, -0.1407, 0.3215],
# [-0.1074, 0.2512, 0.2103]]],
# [[[ 0.0602, 0.2026, 0.0181],
# [ 0.1411, 0.0426, 0.1177],
# [-0.0267, 0.1855, 0.0767]]],
# [[[ 0.3355, 0.0562, -0.5521],
# [-0.1873, 0.0618, -0.2184],
# [-0.0807, 0.2019, 0.4228]]],
# [[[ 0.3663, -0.0216, 0.2986],
# [ 0.1475, 0.4550, 0.3350],
# [-0.6378, -0.6662, -0.2658]]],
# [[[ 0.2871, 0.1041, 0.2666],
# [ 0.0158, 0.4709, -0.2112],
# [ 0.4000, 0.0856, -0.3989]]],
# [[[ 0.4908, 0.0509, 0.2348],
# [-0.3349, -0.2446, -0.7014],
# [-0.6735, -0.3921, -0.2518]]]], requires_grad=True))]
마지막에 mask를 제거하고 pruned weight 를 영구히 적용시켜주면 모델의 사이즈와 소요시간 또한 원래 사이즈대로 돌아오는 것을 확인할 수 있다. 하지만 acc는 낮아진 상태를 유지한다.
'Naver Ai Boostcamp' 카테고리의 다른 글
[03/30] Data Feeding (0) | 2021.03.31 |
---|---|
[03/29] P Stage Start ! (0) | 2021.03.30 |
[Day 37] 모델의 시공간 & 알뜰히 (0) | 2021.03.27 |
[DAY 36] OMG 왜 안했누 (0) | 2021.03.26 |
[DAY 35] Multi-modal & 3D Understanding (0) | 2021.03.25 |