0을 아무리 많이 만들어도 빨라지지 않는다
3편에서 가중치를 int8 로 뭉갰다. 값을 거칠게 만드는 대신 개수는 그대로 뒀다. 반대 방향도 있다. 정밀도는 그대로 두고 개수를 줄이는 것 - 일부를 아예 0으로 만든다.
무엇을 0으로 만드느냐
같은 비율을 잘라도 무엇을 자르느냐로 결과가 갈린다. 절댓값이 작은 것부터 자르는 방법과 무작위로 자르는 방법을 비교한다.
0 으로 만든 비율 크기순(전역) 크기순(층별) 무작위
10% 2.0358 2.0397 3.7873
30% 2.2097 2.3893 4.4976
50% 3.1009 3.3498 4.4543
70% 4.0908 4.4716 4.5714
90% 5.0523 5.0089 4.6292
기준이 2.0272 다. 크기순으로 자르면 30% 까지 2.2097 로 버티는데, 무작위로
자르면 10% 에서 이미 3.7873 이다.
무작위 쪽을 잘게 보면 얼마나 급한지 보인다.
1% 2.2862
2% 2.4815
5% 3.1419
10% 3.7873
20% 4.2955
5% 에서 이미 3.1419 로, 1편의 바이그램 2.6501 보다 나쁘다. 20% 면
4.2955 로 학습 전 4.6052 에 붙는다. 가중치 스무 개 중 하나를 무작위로
지우면 앞 글자만 세는 것보다 못한 모델이 된다.
크기순은 왜 다른가. 잘리는 것이 이미 거의 0인 값들이라 그렇다. 무작위로 자르면 큰 값도 같은 확률로 지워지고, 그 하나가 다음 층 전체를 흔든다.
층별로 자르는 것보다 전역으로 자르는 쪽이 낫다는 것도 표에 있다. 30% 에서
2.2097 대 2.3893 이다. 층마다 값의 크기 분포가 다른데 층별로 같은 비율을
강제하면, 큰 값만 있는 층에서 큰 값을 잘라내게 된다.
3편과 같은 손실을 내려면
양자화와 직접 견줘 본다. 3편의 각 비트 수가 낸 손실을 프루닝으로 맞추려면 몇 퍼센트를 잘라야 하나.
8비트 (2.0280) 희소도 1.8%
6비트 (2.0387) 희소도 10.3%
4비트 (2.3571) 희소도 35.5%
int8 은 4 배 압축인데 프루닝으로 그 손실을 내려면 1.8% 만 자를 수 있다.
1.8% 를 지워서는 아무것도 아낀 게 없다.
같은 공간이면 양자화가 이긴다
여기서 프루닝의 함정이 나온다. 절반을 0으로 만들었다고 저장 공간이 절반이 되지 않는다. 어디가 0이 아닌지도 저장해야 하기 때문이다.
원소당 바이트
float32 촘촘 4.00
int8 촘촘 1.00 (3편)
float32 희소 50% COO 4.00 / 비트마스크 2.12
float32 희소 75% COO 2.00 / 비트마스크 1.12
float32 희소 90% COO 0.80 / 비트마스크 0.52
값과 인덱스를 나란히 두는 흔한 방식(COO)이면 50% 를 잘라도 아낀 게 없다.
원소마다 1비트짜리 마스크를 두는 방식이 낫지만 그래도 50% 에서 2.12 바이트다.
같은 압축률에서 손실을 견주면 결론이 분명하다.
int8 압축 4.00배 손실 2.0280
희소 75% 압축 3.56배 손실 4.3935 (비트마스크)
희소 50% 압축 1.88배 손실 3.1009
int8 이 더 많이 압축하면서 손실은 2.0280 이고, 비슷한 압축률의 프루닝은
4.3935 로 학습 전보다 나쁘다. 이 모델에서는 양자화가 프루닝을 모든
압축률에서 이긴다.
그리고 빨라지지도 않는다
프루닝을 하는 다른 이유는 속도다. 곱셈의 절반이 0이면 절반을 건너뛸 수 있을 것 같다. 재 보면
주장이 곱셈에 관한 것이니 곱셈 하나로 좁힌다. 피드포워드 첫 층 모양
(128 x 128) @ (128 x 512) 을 라운드로빈 400회 돌리고, 매 라운드 안에서 촘촘한
원본과 나눈 값이다.
시간(us) 원본 대비 사분위
촘촘한 원본 73.4 1.000
희소도 50% 73.6 1.010 0.957 ~ 1.105
희소도 90% 73.2 1.013 0.924 ~ 1.147
희소도 99% 74.0 1.005 0.850 ~ 1.142
99%를 0으로 만들어도 시간이 그대로다. 세 사분위가 모두 1.0 을 품고 있으니
차이가 있다고 말할 근거가 없다. 행렬곱은 여전히 촘촘한 행렬곱이고, 0을 곱하는
것도 곱하는 것이다. 건너뛰려면 어디가 0인지 확인해야 하는데, 그 확인이 곱셈보다
비싸다.
3편에서 int8 이 메모리만 아끼고 시간은 안 아꼈던 것과 같은 이야기다. 4편에서
c = 1.059 로 8비트 초안이 목표보다 비쌌던 것도 여기서 다시 나온다. 가중치를
건드리는 압축은 기본적으로 메모리 이야기이지 시간 이야기가 아니다.
시간까지 얻으려면 0의 위치가 규칙적이어야 한다. 네 개 중 두 개씩 0으로 만드는 식으로 모양을 맞추면 전용 명령으로 건너뛸 수 있고, 그것을 구조적 희소성이라고 부른다. 이 실험은 그런 제약 없이 잘랐으므로 그 이득을 못 받는다.
그래서
- 무엇을 0으로 만드느냐가 전부다. 크기순은
30%에서2.2097, 무작위는10%에서3.7873 - 무작위로
5%만 지워도3.1419로 바이그램2.6501보다 나쁘다 - 층별보다 전역이 낫다.
30%에서2.2097대2.3893 - 3편의 int8 손실을 프루닝으로 내려면
1.8%밖에 못 자른다 - 0의 위치도 저장해야 해서
50%를 잘라도 COO 로는 아낀 게 없다. 같은 압축률 에서 양자화가 이긴다 - 빨라지지도 않는다.
99%를 0으로 만들어도1.01배다. 압축은 메모리 이야기이지 시간 이야기가 아니다
다음 편은 5편이 명시적으로 남겨 둔 자리로 간다. 거기서 패딩 낭비를 재고 해법은 이름만 댔는데, 그 해법을 재 본다.
Comments