반응형

BitNet 배경 설명

  • BitNet은 Microsoft와 칭화대학교에서 23년 10월에 제출한 논문이다. 
  • 커뮤니티에서 해당 논문에 대해 우연히 접하게 되어 리뷰를 하게 되었다. 
  • LLM은 성능 향상을 위해 model의 parameter를 키우는 방향으로 성장하고 있다.
  • Parameter가 늘어나면서, model의 성능은 점점 좋아지지만, 1) model 자체의 용량이 커지면서 필요한 storage 용량의 증가 2) 연산을 위해 필요한 memory의 증가 3) 프로세서의 연산 속도 한계 등의 H/W의 제약이 대두되었다. 
  • 특히, mobile phone과 같은 제한된 resource 내에서 on-device LLM 동작을 위해서는 단순히 H/W의 spec을 늘리는 방향으로 model parameter의 증가세를 따라갈 수 없게 되었다. 
  • 예를 들어, 136B의 parameter 크기를 가지는 GPT-3의 경우 용량을 줄이기 위해, 16bit 연산을 진행함에도 불구하고, 약 272GB의 어마어마한 parameter 용량을 가지게 된다.(심지어, GPT-3는 최신 LLM에 비해 parameter가 많은 것도 아니다.) 이러한 parameter를 on-device 내에서 올리기는 매우 어렵다.
  • AI 분야에서의 weight 연산은 그 자체의 정밀도에 크게 민감하지 않기 때문에, 연산의 정밀도를 내주면서도, parameter의 bit를 줄여가는 방식으로 발전하고 있다. (4bit uint 연산으로 대체 등)
  • 이 논문도 이러한 LLM의 방향성 속에서 극단적으로 Transformer의 연산을 1bit로 진행하고자 하는 시도이다. 

Abstract

  • LLM의 parameter 크기가 커지면서, 에너지 낭비로 인한 환경 문제가 대두되고 있다.
  • 이 논문은 BitNet이라는 확장가능하고, 안정적인 1-bit Transformer 구조를 소개한다. 
  • 특히, 기존 Fully-Connected Layer를 대체할 수 있는 1-bit weight의 BitLeanear를 소개한다.
  • 실험에서 BitNet은 8-bit 방식이나, 16-bit 방식의 Transformer에 필적할만한 성능을 보이면서, 매우 큰 메모리&에너지 절감을 이뤘다. 
  • 또한, BitNet은 일반적 32-bit Transformer와 유사한 성능 Scaling 법칙을 따라서, Language model의 parameter 크기를 늘리는 트렌드에 더 효과적인 방안으로 사용될 가능성이 있다.

 

Introduction

[배경]

  • 최근 LLM은 빠른 성장을 이뤘지만, LLM의 높은 inference cost와 에너지 사용은 감당하기 어렵다. 
  • model의 크기가 커질수록, model parameter를 처리하기 위한 memory bandwidth가 bottleneck이 되어, inference 성능이 제한되기도 한다.
  • 특히, 이런 모델을 분산 시스템이나, 멀티 디바이스 환경에 배포할 때, 디바이스 간 communication overhead(주로 N/W IO)의 영향이 커져, inference latency와 에너지 소비를 유발한다.
  • Model quantization은 이런 문제를 해결하기 위해, 등장했고, 성능을 유지하면서 memory와 연산량을 줄이는 데 성공하였다.

[quantization 연구들 - post training]

  • 현재 존재하는 대부분의 quantization 방법은 post-training 방식이다. (기존 모델로 학습 후, quantization 방식으로 이를 reference로 retraining)
  • 하지만, 정밀도가 더 낮아지면, model은 quantization을 위해 최적화되지 않았기 때문에, 정확도의 큰 손실이 생긴다.

[ quantization 연구들 - quantization aware training ]

  • 또 다른 방법으로는 quantization-aware training 방법이 있는데, post-training과 비교하여 일반적으로 더 높은 성능을 보인다. 
  • 이 방법은 모델을 계속해서 훈련과 fine-tuning이 가능하다. 
  • quantization-aware training의 주요 문제는 최적화에 있다. 즉, 정밀도가 낮아질수록, 최적화가 어렵다는 것이다. 
  • 또한, 이 방법이 LLM의 scaling 법칙(parameter가 커질수록 성능이 좋아진다.)을 따르는지 미지수이다. 

[논문 소개]

  • 이 논문에서는 LLM에 binarization(극단의 quantization) 적용에 초점을 맞춘다. 
  • 앞선 연구들은 주로 CNN에 초점을 맞췄고, 최근 들어 몇 개의 binarized Transformer가 등장하기 시작했다.
  • 하지만, 이런 연구들은 LLM과 꽤 다른 기계번역이나, BERT pretraining에 초점을 맞췄다. (예를 들어, 기계번역은 encoder-decoder 구조를 사용, BERT pretraining은 bidrectional encoder를 사용)
  • 이 논문은 최초의 1-bit large language model에 quantization-aware training을 도입한 최초의 연구이다.
  • 이 논문에서는 BitNet이라는 LLM의 memory와 연산을 효율화하여, LLM에 사용할 수 있는 1-bit Transformer 구조를 제시한다.
  • BitNet은 low-precision binary weights와 quantized activations를 사용하였다.
  • BitNet의 구현은 단순히 Transformer의 linear projection을 대체하면 되기 때문에 매우 간단하다. 

[실험 결과]

  • SOTA quantization 방법들이나, 16-bit Transformer들과 비교해 보았을 때, BitNet은 memory와 에너지 사용을 매우 절감하면서도 preplexity와 downstream task 정확도에서 그들과 비견할만한 좋은 성능을 보였다. 

 

 

BitNet

 

  • 아래 그림과 같이 BiTNet은 Transformer의 self-attention, feed-forward를 쌓는 구조와 비슷한 형태를 가진다.
  • BitNet은 BitLinear라는 연산을 기존 matrix multiplication 대신 사용한다. 이 연산은 1-bit의 model weights를 가진다.
  • BitNet에서는 BitLinear를 제외한 다른 연산들은 8-biit의 값을 사용하는데, 그 이유는 다음과 같다.
    • residual connections과 layer normalization은 LLM 연산에 비해 매우 미미한 cost만 사용한다.
    • 모델의 커질수록 QKV 변환의 연산이 parametic projection보다 훨씬 작다. 
    • samling 수행을 위해, high-precision probability를 사용해야하기 때문에, 입, 출력 임베딩 정밀도 보존한다. 

 

BitLinear

  • 먼저 weight를 +1, -1 양값이 나오는 signum function을 통해, binarize한다. 
  • 표현 가능한 capacity를 늘리기 위해, binarization 전에 weights를 mean 0 값으로 centralize 해준다.
  • scaling factor를 이용하여, binarization 이후에 실제 값과, binarized weight의 l2 error를 줄인다. 
  • weight의 binarization은 아래 식과 같이 표현된다. 

  • 이후, activation을 b-bit precision으로 quantize 한다. 이때, 아래와 같이 absmax quantization 방법을 사용하였다. 

  • non-linear function(activation function) 전에 모든 값에 minimum을 빼줘서, 모두 양수 값으로 만든다. 

  • 위의 quantization function을 이용하여, matrix multiplication은 다음과 같이 구해진다. 

  • W와 x의 mutually independent와 같은 분포를 공유한다는 것을 가정하면, y의 variance는 아래와 같이 구할 수 있다. 

  • quantization 후, 분산 보존을 위해, activation quantization 전에 LayerNorm을 사용한다. 이렇게 하면, output y는 Var(y) ≈ E [LN(x)^2] = 1과 같이 구해진다. 
  • Transformer에서 이것은 SubLN과 정확히 같은 구현을 가진다. 
  • SubLN과 앞선 quantization 방법을 이용하여, BitLinear를 다음과 같이 정의한다. 

 

[Model parallelism with Group Quantization and Normalization]

  • LLM의 핵심요소 중 하나는 여러 device에서 matrix multiplication을 분산처리하는 model parallelism이다. 
  • 이 model parallelism의 전제 조건은 tensor들이 partition dimension으로는 각기 independent 하다는 것이다. 
  • 하지만, 모든 parameter들은 전체 tensor들로부터 계산되기 때문에, 그 전제 자체가 깨진다. 
  • 이를 해결하기 위한 방법으로 각 parameter에 all-reduce operation을 제안한다. 
  • 각 parameter들 간의 communication은 적더라도,  model이 깊어질수록, 전체 synchronization 크기는 커지고, 이로 인해 forward-pass는 느려진다. 
  • SubLN에서도 이 문제는 발생하는데, mean과 variance가 partition 방향으로 구해져야 하기 때문이다. 
  • 이를 해결하기 위해, 이 논문에서는 model parallelism을 효율적으로 구현하는 매우 간단한 방법을 소개한다. 우선, weights와 activations들을 그룹으로 나누고, 각 그룹의 parameter를 독립적으로 추정한다. 이런 방법을 이용하면, 각 parameter들은 별도의 communication 없이 locally 하게 연산이 된다. 
  • 이 방법을 "Group Quantization"이라고 명명하고 다음과 같이 정의한다.
  • weight matrix W(n X m)에 대해, partition 방향으로 G개의 group으로 나눈다. 각 group은 n/G X m의 size를 가진다. 그리고, 각 group은 독립적으로 parameter를 추정한다. 

  • 비슷하게, 각 group에 대한 activation을 구한다. 

  • LN을 위해, group normalization을 적용하는데, 이때 mean과 variance는 각 group에 독립적으로 구하여 사용한다. 

 

 

  • 이 방법으로, 효율적인 Group Qunatization과 Normalization을 이용한 model parallelism이 가능해졌다.

 

 

 

Model Training

 

[Straight-through estimator]

  • 1-bit model 학습을 위해, straight-through estimator(STE)를 이용하여 backpropagation 간 gradient approximatin에 사용했다.
  • 이 방법은 backward pass에서 미분 불가능한 연산(Sign, Clip 등)을 bypass 하는 것이다. 
  • STE는 gradient가 미분 불가능한 연산에 대한 영향 없이 model의 학습을 돕는다. 

 

[Mixed precision training]

  • weights와 activations가 quantized 되어 precision이 떨어졌지만, gradient와 optimizer는 high precision을 유지하고 있어 안정적인 학습과 정확도를 유지한다.
  • 기존 연구를 따라, parameter update를 위한 latent weight를 high precision으로 유지한다. 
  • latent weights는 forward pass 단에서 binarized 될 것이고, inference process에서는 사용되지 않는다. 

 

[Large learning rate]

  • optimization 단에서 1-bit weight 상에서 거의 차이가 나지 않는 small update가 종종 일어난다. 
  • 이 현상이 training 초기 단에서 일어나면, 그 문제는 더 심각해진다. (초기단에서는 빠른 converge가 필요하기 때문에)
  • 이 문제를 해결하기 위해, 여러 방법들을 사용하였는데, 그중 하나가 빠른 optimization을 위해 learning rate를 키우는 것이다. 
  • 저자들은 BitNet이 초반 큰 learning rate를 사용했을 때 convergence 측면에서 큰 이득을 보는 것을 확인했다.

 

Computational Efficiency

  • 가장 중요한, BitNet의 computational 효율성을 energy 측면과 memory 측면에서 확인해 봤다.
  •  특히, LLM의 꽃인 matrix multiplication을 중점적으로 확인했다.  

[Arithmetic operation energy]

  • 기존 연구에 따르면, 기존 산술 연산에서 bit 수에 따른 에너지 소모는 다음과 같이 알려져 있다.

  • 기존 Transformer에서 m X n과 n X p의 matrix multiplication 연산의 energy 소비는 아래와 같이 계산된다. 

  • BitNet에서는 matrix multiplication의 주요 연산이 addition operation이다. (weight가 1bit이므로) BitNet에서 energy 소모는 다음과 같이 계산된다. 

  • 이것은 energy 소비 측면에서 기존 Transformer에 비해 매우 적고, 특히 W1 A8(weight는 1bit, add는 8bit) 구조는 32-32, 16,16 Transformer에 비해 매우 적게 에너지가 소모된다. 

→ multiplication 연산은 add 연산에 비해 cost가 매우 크기 때문에, 많은 compiler들이 multiplication을 add로 바꾸는 방법으로 연산을 optimize 하곤 한다. weight에 1bit를 할당해 줘서, multiplication 연산을 add 연산으로 바꾸는 효과를 볼 수 있는 것이다. 

 

 

FP16 Transformer와의 비교

Setup

  • BitNet 모델을 다양한 size의 language model에서 비교하기 위해, parameter의 size를 125M에서 30B까지 영역에서 비교해 보았다. 
  • model은 English-language corpus(Pile, Common Crawl, RealNews, CC-Stories)를 통해 학습되었고, Sentencpiece tokenizeer로 전처리하였고, vocabulary size는 16K이다. 비교를 위한 Transformer도 동일하게 처리하였다. 

Inference-Optimal Scaling Law

  • 기존 연구에 따르면, Transformer는 연산 cost에 따른 loss는 power law를 따른다. (연산량에 제곱에 비례하게 low가 줄어든다.)
  • 1-Bit BitNet도 이를 따르는지 확인해 보았는데, 결과적으로 BitNet도 어느 정도 power law를 따름을 보인다. 
  • 하지만, 실제 연산 cost와 loss의 관계를 적절히 modeling 하지 못하는데, 기존 연구들은 FLOP을 계산하여 계산량을 추정하였지만, BitNet은 정수 계산이 우세하기 때문에 적용되지 않고, 기존 연구들의 추정은 추론보다는 학습 계산량 추정에 불과하였기 때문이다. 
  • 이 연구에서는 LLM의 효율성을 더 잘 이해하기 위해, inference 단에서 energy 소비와 loss 간의 관계를 모델링하는 Inference-Optimal Scaling Law(아래 왼쪽 그래프)를 소개한다. 
  • Inference-Optimal Scaling Law를 통해, BitNet이 기존 FP16 Transformer에 비해 훨씬 더 높은 scaling 효율성을 가지고 있음을 확인할 수 있다. 또한, FP16 model과 동일한 성능을 얻기 위해 사용되는 energy의 소모량은 매우 적다. 

 

 

 

 

Downstream Task들에서의 비교

  • loss 뿐 아니라, BitNet의 효과성을 확인하기 위해, loss에 비해 더 어려운 capacity를 측정해 본다. 
  • 0-shot과 4-Shot의 downstream task들에서 test를 진행한다. 
  • loss scaling curve와 비슷하게 downstream task는 computation cost가 증가하면 performance가 증가한다. 

 

Stability Test

  • low-bit Transformer의 가장 큰 어려움은 optimization 단에서의 안정성이다. 
  • 따라서, 저자들은 BitNet과 FP16 baseline의 peak learning rate를 바꿔가면서 실험하여, stability test를 진행하였다. 
  • 아래 그래프에서 보듯, BitNet은 FP16에서 불가한 큰 learning rate에서 converge가 가능하였고, 이것은 학습 단에서의 BitNet의 안정성을 증명한다.
  • 이것은 optimization을 큰 learning rate로 진행하여, 빠른 학습이 가능함을 보인다. 

 

 

 

Post-training Quantization과 비교

Setup

  • BitNet과 SOTA quantization 방법들을 비교했다. 
  • 이러한 방법들은 FP16 Transformer model에 대한 post-training을 진행하는 방법들이다. 이러한 방식들은 weight와 activation의 precision을 모두 줄인 경우(Absmax, SmoothQuant)와 weight만 줄인 경우(GPTQ, QuIP) 들로 나뉘는데, 이들과 비교하기 위해 weight-only quantization에는 W4 A16, W2A16을, weight-and-activation quantization에는 W8 A8, W4 A4, W1A8을 이용하였다. 

Result

  • 아래 그래프와 표를 통해, 다른 quantization 방법들과의 성능 비교 결과를 확인할 수 있다. 
  • 공정한 비교를 위해, 모든 model의 parameter size는 6.7B로 통일했다. 
  • 결과를 통해, BitNet이 lower bit를 가지고 있음에도 불구하고, 다른 quantization model에 필적할만한 좋은 성능을 보임을 확인할 수 있다. 
  • zero-shot score에서 BitNet은 inference cost가 훨씬 낮지만, 8bit 연산과 비교될만한  높은 성능을 냄을 확인할 수 있다. 
  • BitNet은 1bit model이지만, weight-and-activation quantization과 weight-only quantization에서 다른 model에 비해 좋은 성능을 보임을 확인할 수 있다. 

 

 

Reference

WANG, Hongyu, et al. Bitnet: Scaling 1-bit transformers for large language models. arXiv preprint arXiv:2310.11453, 2023.

총평

  • 최근, 회사에서 좋은 기회로 computer architecture 교육을 듣고 있어서, 산술 연산의 cost에 대해 관심이 많았는데, 이런 논문을 읽게 되어, 더 재밌게 읽은 것 같다.
  • 실제로 강의를 진행해주시는 교수님께서 matrix multiplication 연산의 비효율성에 대해서 열심히 설명해 줘서 그런지 이 논문의 필요성에 대해 더 공감하게 되었다. 
  • LLM의 용량이 더 줄어서, on-device AI가 더 활발해지는 세상이 왔으면 좋겠다. 
반응형

Airflow를 이용한 oracle batch job을 개발 중, cx_Oracle을 인식하지 못하는 현상이 발생하였다. 결론적으로는 매우 어이없는 실수지만, 동일한 이슈를 겪는 사람들이 분명 있을 것으로 생각하여 해결 과정을 정리하고자 한다. 

문제

  • 기존 postgresql 관련된 dag만 존재하던 airflow에 Oracle 관련 job을 추가하였다. 
  • Airflow 스케줄러에서 cx_Oracle을 호출하여, connection을 시도하니, 아래와 같은 오류가 발생하였다.
cx_oracle.databaseerror: dpi-1047: cannot locate a 64-bit oracle client library: "libclntsh.so: cannot open shared object file: no such file or directory". see https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html for help

 

  • 실제 instantclient에 들어가봤을 때, libclntsh.so 파일이 존재하였다. 

 

개발 환경

  • 문제가 발생한 상황은 centos 7버전에 conda 가상환경을 통해, airflow shcheduler를 수행하고 있었고, 모니터링의 편의를 위해 tmux로 shceduler와 webserber를 각각 다른 터미널로 수행 중이었다.

 

해결 시도

  • 해당 에러를 과거에도 경험해본 적이 있는데, 환경 변수 PATH에 instantclient의 경로가 인지되지 않아, 발생하는 문제이다. 
  • ~/. bashrc에 경로를 잡아주고, source ~/. bashrc를 통해 환경 변수를 적용해 준다. 
vim ~/.bashrc

######## .bashrc 마지막 줄 추가 #######
export ORACLE_HOME=”/home/user/{instatclient 위치}″
export LD_LIBRARY_PATH=”$ORACLE_HOME”
#######################################

source ~/.bashrc
  • python으로 connection을 실험해주니, 정상적으로 연결이 된다. 하지만, airflow scheduler 안에서는 계속 같은 이슈가 발생하였다. 

 

해결 방법

  • 너무나 당연한 내용이지만, source ~/.bashrc를 적용한 터미널과 상관없이, airflow의 scheduler와 webserver가 구동 중인 터미널에는 변경된 bashrc의 내용이 적용되지 않았다. 
  • . bashrc는 터미널이 시작될 때, 적용되는데,. bashrc 수정 전에 tmux로 생성한, airflow의 scheduler와 webserver 관련 터미널들은 변경된. bashrc를 적용하지 않았기 때문에, 당연히 instantclient의 경로를 인지하지 못하고, 경로를 못 찾는 이슈가 계속 발생한 것이다. 
  • scheduler와 webserver를 종료하고, 각 터미널에서 source ~/.bashrc를 적용해 준 후, scheduler와 webserver를 각각 재시작하니, 정상적으로 cx_Oracle을 통한 connection이 가능하였다. 
  • 지금 생각하니, 매우 당연한 내용이지만, 이유를 찾지 못해 한참을 고생했다. 아직 scheduler에 실시간성 스케줄링이 필요한 내용이 없어서 scheduler 종료 후, 시작이 가능하였지만, 운영 중인 환경에서는 어려울 것 같다. 이런 경우에는, cx_Oracle이 필요한 dag 안에서 cx_Oracle을 통한 connection 전에 시스템 환경 변수를 넣어주는 과정으로 해결 가능하다. 
import os

current_path = os.environ.get('PATH', '')
new_path = 'instantclient 경로'

if new_path not in current_path:
    updated_path = f"{current_path}:{new_path}"
    os.environ['PATH'] = updated_path

 

 

'Linux' 카테고리의 다른 글

[이슈 해결] SSH 접속이 갑자기 느려졌을 때 해결 방법  (50) 2023.11.20
반응형

최근 AI 분야가 각광을 받고, 다양한 ML 기반 서비스들이 제공되면서, MLops라는 용어가 널리 알려지게 되었다. 과거에는 AI 연구 영역과 엔지니어링 영역이 너무 멀게만 느껴져서, MLops에 그렇게 주목하지 않았지만, 실제 머신러닝 코드들을 개발하고, 배포하다 보니, MLops가 왜 필요하고, 어떤 기능을 포함해야 하는지에 대해 공감하게 되었다. 오늘 리뷰할 논문은 MLops의 시작이라고 할 수 있는 2015년 Google에서 발표한  "Hidden Technical Debt in Machine Learning Systems"이라는 논문이다. 사실, 머신러닝이 본격적으로 서비스로 제공되기 한참 이전의 논문이라, 현재의 MLops의 모습과 많이 달라져있을 수 있지만, 어떻게 MLops가 논의되기 시작했는지에 대해서는 알아두고 가야 할 것 같아서 논문을 읽어보았다.

Abstract

  • 머신러닝은 복잡한 예측을 빠른 시간 안에 가능하게 하지만, 빠른 성과를 얻는 것이 무조건 좋은 것은 아니다.
  • 소프트웨어 엔지니어링 공학의 "기술부채" 개념에서 실생활에서 머신러닝 도입은 크고, 지속적인 유지보수 비용을 부담하게 될 것이다.
  • 이 논문에서는 시스템 디자인에서 머신러닝에서 발생할 수 있는 특정 리스크 요인(boundry erosion, entanglement, hiddden feedback loop, undeclared consumers, data dependency 등) 들을 살펴본다.

 

Introduction

  • 머신러닝에 대한 개념과 경험이 쌓여가면서, ML 개발과 배포는 빠르고 쉽지만, 유지보수가 어렵다는 인식이 번져가기 시작했다.
  • 이러한 유지보수의 어려움에는 코드 리팩토링, unit test 개발, 코드 정리, 의존성 제거, 문서 정리 등 기술부채를 해결을 위한 비용을 포함한다. 
  • 이 논문에서는 ML에 특화된 기술부채들에 대해서 다룬다.
  • ML에서의 부채들은 코드 레벨이 아닌, 시스템 레벨에 포함되어 찾기 어려울 수 있다. 특히, 데이터가 머신러닝 시스템의 동작에 영향을 미치기 때문에, 전통적인 코드 레벨에서의 해결 방법은 ML 특화적 기술부채들을 다루기에 충분치 않다.
  • 이 논문에서는 ML 기술적 부채가 쉽게 커지는, 시스템 레벨에서의 interaction과 interface에 집중한다. 

 

Complex Models Erode Boundaries

  • 전통적 소프트웨어 엔지니어링에서는 캡슐화와 모듈화 디자인을 통해, 추상화 경계를 명확하게 나누어, 다른 모듈이나 캡슐의 변화에 영향을 받지 않도록 설계한다.
  • 이러한 분리 속에서는 각 모듈이 input과 output이 명확하기 때문에, 기능적 일관성을 유지할 수 있었다. (예를 들어, Interface 모듈은 Interface 기능만 담당함)
  • 하지만, ML에서는 목적 단위로 명확한 추상화 경계를 나누는 것이 어렵다. 실제로 ML의 서비스에서 외부 데이터에 의존하지 않는 깔끔한 캡슐화는 어렵다. 

[Entanglement]

  • 머신러닝은 여러 signal들을 혼합하여 활용하는데, 각 signal들의 효과를 분리하면서 성능 향상을 하는 것은 어렵다. 
  • 예를 들어, N개의 feature가 있을 때, 한 개의 feature가 변화하게 되면, importance나 weights, 다른 feature들의 필요성등 모든 것들이 변화한다. 이런 경우는 Batch 단위의 학습이든, 온라인 학습이든 모두 적용된다.
  • 새로운 feature를 추가하는 것이나, 특정 feature를 지우는 것도 비슷하다. 
  • 즉, 모든 Input들이 독립적이지 않다는 것이고, 이 논문에서는 이 현상을 CACE(Changing Anything Changes Everything pipeline으로 명명한다.
  • CACE는 input 뿐 아니라, hyper-parameter에도 적용된다. 
  • 이러한 현상을 완화하기 위한 방법으로는 model을 분리하고, 앙상블 형태로 serving 하는 것이다.
  • 이러한 접근 방법은 전체 문제를 sub problem으로 쪼갤 수 있는 경우에는 효과적일 수 있다. 하지만, 많은 경우에서 앙상블이 효과적인 이유는 각 모델에 속한 오류가 상관관계가 없기 때문인데, 결합에 의존하게 되면 오히려 각 모델의 성능을 향상하는 것이, 전체 앙상블 모델 성능을 떨어뜨릴 수 있다.
  • 두 번째 전략으로는 prediction behavior의 변화를 감지하는 것에 초점을 맞추는 것이다. 시각화 도구등을 활용하거나, 각 단계 별로 작동하는 metrics 등이 효과적일 수 있다.

 
[Correction Cascades]

  • 문제 A에 대한 model이 존재할 때, 살짝 다른 문제 A'를 풀기 위해, 두 문제 간의 차이를 빨리 학습하는 것이 필요하다. (예를 들어, pre-weight에 대한 Fine tuning)
  • 하지만, 이런 문제가 계속 생긴다고 하였을 때, 시스템 종속성이 계속 연결되어 발생하고, 큰 비용이 발생한다. 
  • 완화전략으로는 동일 모델에서 casecade가 아닌, 직접 correction을 학습하던지, 별도의 모델을 만드는 것이다.

 
[Undeclared Consumers]

  • 머신러닝 시스템의 예측은 runtime에 광범위하게 접근 가능하게 되거나, 다른 시스템에 소비될 파일이나 로그 형태로 생성된다.
  • access control이 없으면, 이러한 결과는 다른 시스템의 입력으로 충분한 공유 없이 사용할 수 있다. 이러한 문제를 전통 소프트웨어 엔지니어링에서는 visibility debt라고 한다. (의존성의 부채)
  • 이러한 문제는 구성 요소간의 상화 작용과 의존성을 파악하지 못해서, 예측 불가능한 문제를 발생시킬 수 있다.
  • Undeclared Consumer를 고려하기 위해서, 방화벽이나 access 제한 등의 접근을 제한할 수 있는 방법을 검토해야 한다.

 

Data Dependencies Cost More than Code Dependencies

  • 전통 소프트웨어에서는 의존성에 대한 cost가 기술 부채의 주요 요인으로 꼽힌다. 
  • ML에서는 data dependency가 주요 기술 부채 요인이 되지만, 오히려 감지는 더 어렵다.
  • Code 의존성등은 컴파일러 등을 통한 정적 분석으로 어느 정도 확인이 가능하다. data dependency도 비슷한 tool이 없다면, 데이터 의존성을 풀기 어렵다. 

[Unstable Data Dependecies]

  • 빠른 학습을 위해, 다른 시스템에서 생성된 Feature 등을 활용하는 것이 일반적이다. (pre-weight 등) 
  • 하지만, 몇몇의 input signal들은 질적 또는 양적으로 행동이 변화하는 불안정한 특성을 가질 수 있다. 
  • input signal을 제공하는 머신러닝 시스템 자체가 학습된 경우나, TF/IDF 같은 데이터 종속적 데이터들은 데이터 변화에 따라 변화한다.
  • 특히, 데이터와 모델의 ownership이 분리된 경우 많이 발생한다. 이 경우 input signal의 개선조차도, 사용하는 시스템에서는 업데이트에 대한 비용이 많이 발생한다. 
  • 완화를 위한 방법으로는 주어진 signal에 대한 버전 관리(DVC 같은)이다. 예를 들어, 시간에 따라 변화하는 input을 사용하는 것이 아니라, input을 특정 버전으로 freeze 하여 사용한다. 
  • 이러한 버전관리는 그 자체의 cost를 발생하지만, 아무도 모르게 모델 성능에 영향을 미치는 일은 일어나지 않을 것이다.

→ 이 부분은 Database에서의 Data Dependency와 비슷하다. 일반적으로 ML은 앞선 매우 많은 종류의 원천 데이터들을 ETL 하여 활용하기 때문에, Data Dependency에 대한 cost를 계속 안고 가야 한다. 사실 이 부분이 ML serving을 쉽게 하는 tool이 많이 만들어져도, ML engineer의 비중이 점점 더 커지고 있는 요인 아닐까 싶다. 
 
 
[Underutilized Data Dependencies]

  • Code에서의 불필요한 의존성은 보통 필요하지 않은 패키지들로 발생한다.
  • 비슷하게, underutilized data dependencies는 model 성능에 더 이상 이점을 제공하지 못하는 data들을 의미한다. 
  • 이러한 요소들은 ML 시스템을 불필요하게 변화에 취약하게 할 수 있다. 
    • Legacy Features: 시간에 변화에 따라 Feature 특징이 바뀌었는데, 필요 없지만 그것을 감지하지 못하는 경우
    • Bundled Features: bundle 형태로 제공되는 Feature 들 중, 필요성이 떨어지거나, 없는 Feature들이 포함된 경우
    • Epsilon-Features: 연구자들이 성능 향상을 위해, 성능에 미치는 영향이 미미하지만, 복잡한 데이터들을 포함해 놓은 경우
    • Correlated Features: 종종 두 개의 Feature들이 큰 상관관계를 가지고, 하나의 Feature가 다른 것의 원인인 경우. 많은 ML 방법들은 두 Feature가 의미적으로 동일하다는 것을 감지하지 못하거나, 원인이 아닌 결과를 선택하기도 한다. 
  • 적게 활용되는 종속성은 철저한 leave-one-feature-out 평가를 통해 감지될 수 있다. 이러한 평가를 정기적으로 실행하여, 불필요한 특징을 식별하고 제거하는 데 사용되어야 한다. 

→ 과거, 8개의 view를 활용하여 Object Detection을 진행하는 과제를 진행한 적이 있다. 중간에 2개 view를 더이상 evaluation 단에서 활용할 수 없는 경우가 발생하였고, 이에 따라, 데이터를 필터링하고 재학습하는 과정을 거쳤다. 당시 단일 모델을 많은 researcher들이 다루고 있어, 금방 해결했지만, 대규모 시스템에서는 데이터를 제외하고 재학습해야 하는 매우 큰 cost가 발생할 수 있을 것이다.
 
 
[Static Analysis of Data Dependencies]

  • 전통적인(머신러닝이 아닌) 코드 들은 컴파일러와 빌드 시스템이 종속성에 대한 정적 분석을 실행해 준다.
  • Data dependencies들에 대한 정적 분석 툴은 흔하지는 않지만, error checking이나, 마이그레이션, 업데이트 등에 중요한 요소이다. 
  • Ad Click Prediction 논문에서 소개한 자동 기능 관리 시스템은 이러한 작업을 가능하게 해준다. 자동 체크는 데이터 소스와 기능에 annotation을 달게 하여, 이를 자동으로 체크해 준다. 

→ 데이터 의존성을 정적 분석하기에는 모든 데이터를 cover하기 어려울 것이다. 그나마, 중간 단 feature의 visualization이나 통계값들을을 쉽게 뽑을 수 있는 tool을 활용하는 것이 최선이 아닐까 싶다.
 

Feedback Loop

  • ML 시스템의 특징 중 하나는 시간에 따라 업데이트가 진행되면서, 모델 자체 행동에 영향을 미칠 수 있다는 것이다. 이로 인해, 특정 모델의 행동을 예측하기 어려워지는 "anaylisis debt"가 발생한다.
  • 이러한 Feedback loop는 다양한 형태로 발생하지만, 시간에 따라 점진적으로 발생할 경우, 감지와 해결이 더 어렵다.

[Direct Feedback Loops]

  • 모델이 직접적으로 미래 training data 선택에 영향을 미치는 현상이다. 
  • 이는 bandit 알고리즘을 사용하여 해결가능하다.(무작위성 등을 이용하여, 공간을 줄임)

[Hidden Feedback Loops]

  • Direct feedback loops는 분석에 cost가 많이 들지만, ML 연구자들이 통계적으로 풀어낼 수 있을 것이다.
  • 더 어려운 케이스는 두 시스템이 서로에게 영향을 미치는 hidden feedback loops이다. 
  • 예를 들어, 두 다른 투자 회사에서 나온 주식 시장 예측 모델의 경우, 하나의 모델을 개선하면, 다른 모델의 입찰과 매수에 영향을 미칠 수 있다.

 

ML-System Anti-Patterns

  • 실제 ML 코드를 보면, 학습과 분석에 해당하는 코드는 전체 코드의 아주 일부분 만을 차지한다. 이러한 ML을 포함한 시스템에서 high-debt 디자인 패턴을 가지는 것이 흔한 현상이다. 
  • ML 시스템에서 발생할 수 있는 시스템 디자인 안티패턴들은 다음과 같다. 

[Glue Code]

  • 일반적으로 ML 코드 개발을 위해, 미리 만들어진 package를 사용하는 경우가 많다. 
  • 이러한 package들은 다양한 범위를 커버하기 위해 만들어졌기 때문에, 실제 package의 일부만 사용하더라도 대량의 코드를 import 하는 glue code가 발생한다. 
  • Glue code는 특정 버전의 패키지로 시스템을 고정하여, 개선을 어렵게 하는 등 장기적으로 비용이 많이 발생한다.
  • 이를 해결하기 위해, 블랙박스 패키지를 공통 API로 wrapping 하는 방법들이 있다. 이렇게 하면, 재사용성 향상과, package 변경에 따른 비용으로 절감할 수 있다.

 
[Pipeline Jungles]

  • glue code의 특별한 케이스로 발생하는 pipeline jungles는 data를 전처리할 때 자주 발생한다.
  • 이러한 경향은 새로운 signal과 source가 점진적으로 추가됨에 따라 발생하는데, 주의를 기울이지 않으면 ML 전처리 단에서 스크랩, 조인, 샘플링 단계등이 남발하는 정글이 발생할 수 있다.
  • 이런 pipleline을 관리하고, 에러를 발견하여 해결하는 것은 어렵고, 많은 비용이 든다. 예를 들어, pipeline 테스트를 위해서는 비용이 많이 드는 end-to-end 통합 테스트가 필요한데, 이런 테스트를 관리하는 것들이 기술적 부채를 발생시킨다.
  • Pipeline jungle은 데이터 수집 및 Feature 추출에 대해 종합적으로 생각해야만 피할 수 있다. 
  • Pipeline jungle을 제거하고, 처음부터 다시 디자인하는 것들은 공수가 많이 발생하지만, 비용 감소와 발전의 속도 측면에서 효과적일 수 있다. 
  • Glue Code와 Pipeline Jungle은 Research 영역과 Engineering 역할을 지나치게 분리되어 있어 발생하는 현상이기도 하다. 
  • Engineer와 Researcher를 동일 팀에서 편입시키는(혹은 동일한 사람이) 하이브리드 연구 방식은 이런 갈등의 근본 원인을 제거할 수 있다. 

 
[Dead Experimental Codepaths]

  • Glue code나 Pipeline jungle의 흔한 결과 중 하나는, 대체 방법 실험을 진행하는 것이다. 빠른 실험을 위해,  Production code 내에 조건부 브랜치를 만들어, 실험비용을 줄이기도 한다.
  • 하지만, 시간이 갈수록, 이런 codepath들은 쌓이게 되고, 역호환성을 유지하기 어려워지며, 순환 복잡성이 매우 증가하게 된다. 
  • 전통적 소프트웨어에서의 dead flag와 마찬가지로, 주기적으로 각 실험용 브랜치를 재조사하여, 제거하는 것이 해결 방법이 될 수 있다.

 
[Abstraction Debt]

  • 최근에는 머신러닝의 추상화에 대한 올바른 Reference가 없음에 대한 문제가 지적되고 있다.
  • 표준 추상화의 부재로 구성요소 간의 경계가 흐릿해지기 쉽게 된다.

[Common Smells]

  • Plain-Old Data Type Smell : ML 시스템에서 사용되는 많은 정보(hyper parameter 등)가 코드에 그대로 들어가 있는 경우가 있다. 
  • Multiple-Language Smell : 특정 부분을 특정 언어로 작성하는 것이 해당 작업에 적합한 경우가 있다. 하지만, 여러 언어를 사용하면, 효과적인 테스트 비용이 증가하게 된다.
  • Prototype Smell : 적은 양의 데이터로 프로토타입을 테스트하는 것이 유용하지만, 유지보수 측면에서는 별도의 cost가 발생한다는 점, 시스템의 복잡성이 증가한다는 점 등의 문제가 발생할 수 있다.

 
 

Cofiguration Debt

  • ML에서는 configuration으로 인한 부채가 발생할 수 있다. 
  • 대규모 시스템은 다양한 범위의 configuration 옵션 등을 포함하는데, researcher와 engineer 모두 이런 configuration을 을 고려하고 개발하지 않는다. 
  • 실제로, configuration을 확인이나 테스트도 중요하게 여겨지지 않는다. 
  • ML 시스템에서는 configuration을 위한 ocde line이 traditional code의 line 수를 훨씬 초과할 수 있다. 이런 configuration은 실수를 발생할 수 있는 여지가 있다.
  • 이러 configuration에서의 실수들은 시간, 컴퓨팅 리소스 등의 낭비나 production 레벨에서의 문제로 이어질 수 있다. 
  • 이를 고려한 좋은 configutaiton 시스템의 원칙은 다음과 같다.
    • 이전 configuration에서 작은 변경을 하는 것이 쉬워야 한다.
    • 수동 오류, 누락 또는 간과를 어렵게 해야 한다.
    • 두 모델 간의 configuration의 차이를 시각적으로 쉽게 파악할 수 있어야 한다.
    • configuration에 대해 자동 검증이 쉬워야 한다.
    • 사용하지 않거나, 중복된 설정을 감지하는 것이 가능해야 한다.
    • configuration을 전체 코드 검토를 거치고, 저장소에 올라가야 한다. 

 

Dealing with Changes in the External World

  • ML의 특징 중하나는, External World와 직접 상호 작용한다는 것이다. 하지만, External World는 일반적으로 안정되어 있지 않다. 계속된 변화들은 추가적인 유지보수 비용을 발생시킨다.

[Fixed Thresholds in Dynamic Systems]

  • 모델의 prediction을 위해, decision threshold를 선택하는 경우가 있다. ML에서 전형적인 접근 방법으로는 precision과 recall 등의 trade-off인 지표 들 사이에서 균형점을 찾는 것이다. 
  • 하지만, 이러한 Threshold는 종종 manual 하게 지정되기도 한다.
  • 이런 경우, 모델이 새로운 데이터로 업데이트되면, 예전에 manual로 지정한 threshold는 무효화된다. 
  • 여러 모델에 걸쳐 많은 threshold는 manual로 업데이트하는 것은 매우 번거롭고, 관리가 어렵다.
  • 이를 완화할 수 있는 방법으로는 threshold를 validation data에서 평가를 통해 뽑는 것이다.

[Monitoring and Testing]

  • 각 요소들의 unit test와 end-to-end test 모두 가치 있지만, 변화하는 환경에 대응하기 에는 충분치 않다.
  • 실시간으로 시스템의 동작을 모니터링하고, 이를 이용한 대응을 하는 것이 장기적 시스템 신뢰성 관점에서 중요하다.
  • 이때, 어떤 것을 모니터링해야 하는지에 대해 명확하지 않다. 모니터링을 위한 몇 가지 부분을 소개한다.
    • Prediction Bias : 의도된 대로 작동하는 시스템에서는 예측의 분포가 실제 분포와 일치해야 한다. 이는 종합적인 테스트는 아니지만, 입력과 관계없이 레이블 발생의 평균값을 예측하는 단순한 모델에 의해 확인될 수 있다. 이 방법은 종종 현재 현실을 반영하지 않는 과거 데이터로부터 추출된 훈련 분포가 갑자기 변경된 경우와 같은 문제를 신속하게 감지할 수 있다.
    • Action Limits : 실제 예측에 대한 Action을 진행하는 시스템들의 경우, action에 대해 합리적인 limit을 설정하고, 이를 강제하는 것이 유용하다. 
    • Up-Stream Producers : 데이터를 생성하는 Producer에 대해 정기적인 모니터링이 필요하다. 

 

그 밖에 ML 관련 부채

  • Data Testing Debt : 입력 데이터에 대한 어느 정도의 테스트가 필수 적이다. 입력 분포의 변화 등을 모니터링하는 테스트도 필요하다.
  • Reproducibility Debt: 실험을 재현하는 것이 난수 알고리즘, 병렬 학습에서의 결정 불가능 성, 초기 조건 등 매우 어렵다.
  • Process Management Debt : 실제 시스템에서는 단일 모델이 아닌 많은 모델을 유지보수 할 텐데, 비즈니스 우선순위가 다른 모델 간에 리소스를 어떻게 관리하고 할당할 것인지 등에 대한 고려도 필요하다. 또한, 프로더션 사고로부터 복구하기 위한 도구를 개발하는 것도 중요하다.
  • Cultural Debt: 때로 ML reseach와 Engineering 단에 경계가 존재하는데, 이런 경계는 시스템 건강에 역효과를 낼 수 있다. 

 

총평

  • 사실 MLops를 처음 접하고, 사용해보았을때에는 기술적 부채와 소프트웨어 공학적 이해가 부족하여, Bash Shell로 가능한 기능들을 이쁘게 만들어놓았구나라는 생각에 그쳤다. 
  • 이 논문은 실제 ML이 본격적으로 service 단에 오르기 전에 작성되었지만, 이 논문에서 말하는 MLops가 필요한 이유와 발생할 수 있는 문제들이 실제 service 단에서 많이 발생하고 있는 것들을 보면서 매우 신기하고, 대단하게 느껴졌다. 
  • 현재 존재하는 MLops 관련 tool들이 이 논문에서 제기한 문제들 중 매우 일부만 해결할 수 있다는 점 때문에 아직 MLops가 가야 할 길이 멀구나 하는 생각을 갖게 되었다. 

 

Reference

Sculley, David, et al. "Hidden technical debt in machine learning systems." Advances in neural information processing systems 28 (2015).

'MLops' 카테고리의 다른 글

Kubeflow (2) - Katlib  (1) 2023.04.11
Kubeflow (1) - 설치 (Windows 11 - WSL로 설치)  (3) 2023.03.28
Kubeflow (0) - 소개  (1) 2023.03.26
반응형

Scikit-learn 이란?

  • Python 기반의 머신러닝 라이브러리로, 머신러닝 관련 다양한 알고리즘과 함수들을 포함하고 있어, 머신러닝 프로젝트에서는 필수 라이브러리이다.

설치 방법

pip install scikit-learn

 

 

Scikit-learn 주요 함수 

  • 사실 Scikit-learn은 계속 새로운 버전이 등장하는 라이브러리이기 때문에, 라이브러리 내의 모든 함수를 보기 위해서는 공식 홈페이지에 방문하는 것이 좋다.
  • 아래 정리된 내용은 지금껏 Scikit-learn을 사용해 오면서, 순전히 주관적인 기준으로 Scikit-learn의 주요 함수를 정리한 것이다.
  • Scikit-learn은 크게 분류하면, Classification, Regression , Clustering 등의 특정 알고리즘을 구현해 놓은 알고리즘 함수와 전처리, 특성추출, 평가등을 통해, 머신러닝 데이터를 쉽게 처리할 수 있게 하는 비알고리즘 함수로 나눌 수 있다. 

 

[머신러닝 데이터처리 함수]

분류 함수명 설명
preprocessing StandardScaler z-score normalization
MinMaxScaler min-max normalization
OrdinalEncoder 범주형 변수 숫자로 encoding
model_selection train_test_split trainset, testset split
KFold K-fold 교차 검증
cross_validate 교차 검증
GridSearchCV 하이퍼파라미터 튜닝(Grid Search로)
RandomizedSearchCV 하이퍼파라미터 튜닝(Random Search로)
metrics accuracy_score accuracy 계산
top_k_accuracy_score top-k accuracy 계산
auc auc 계산
roc_curve roc curve(fpr, tpr)
roc_auc_score roc curve에서 auc 계산
confusion_matrix confusion matrix
recall_score reacall 계산
classification_report 분류 모델 성능 요약 report 생성
mean_squared_error MSE 계산
r2_score 결정계수 계산

 

 

<preprocessing>

preprocessing은 숫자형 데이터를 normalize하거나, encoding 하는 데 사용된다. 

  • StandardScaler : 평균 0, 표준편차 1로 값을 normalize한다. 
  • MinMaxScaler : min-max normalization을 진행한다. (0~1사이의 범위를 가짐)

→ fit을 이용하여, scaler를 조정(최대,최소 나 평균 표준편차)하고, transform을 통해, 데이터를 변환한다. fit_transform을 사용하면, 해당 데이터셋 내에서 scale을 조정할 수 있다. 

from sklearn import preprocessing
import numpy as np

if __name__ == '__main__':
    # x = [1,2,3,...,9]
    x = np.arange(10).reshape(10,1)
    
    # z-score normalize
    std_normal = preprocessing.StandardScaler()
    normalized_x =  std_normal.fit_transform(x)
    
    
    # min-max normalize
    minmax_normal = preprocessing.MinMaxScaler()
    normalized_x = minmax_normal.fit_transform(x)
  • OrdinalEncoder : 범주형 데이터들에 숫자를 mapping 해줄 때 사용한다.

→ Scaler와 마찬가지로, fit을 이용하여, 문자를 숫자로 만드는 dictionary 구조를 만들 수 있고, transform을 이용하여 적용할 수 있다. fit_transform으로 dictionary 생성 및 변환이 가능하고, inverse_transform을 통해, 거꾸로 숫자에서 문자로 바꾸는 변환도 가능하다.

from sklearn import preprocessing
import numpy as np

if __name__ == '__main__':
    category = np.array(["사과", "딸기", "배", "두리안"])
    category = np.expand_dims(category,axis=-1)
    encoder.fit_transform(category)
    
    # mapping 정보 확인 (해당 list의 index에 번호 대응)
    print(encoder.categories_)

 

 

<model_selection>

model_selection은 데이터를 나누거나, 교차검증, 하이퍼 파라미터등을 통해, model 실험에 도움이 되는 함수들이 존재한다.

 

  • train_test_split : data를 train과 test로 분할해준다. 비율을 지정할 수 있는 등, 굉장히 유용하여, 많이 사용한다.

→ array를 넣어주고, train size나 test size 중 1개를 설정하여(비율), 정한다. random_state를 명시적으로 지정하면, 복원 가능하다. 

from sklearn import datasets
from sklearn import model_selection

if __name__ == '__main__':
	# iris data load
	iris_data = datasets.load_iris()
    
    X = iris_data["data"]
	Y = iris_data["target"]
    
    # train과 test를 8:2로 분리
    x_train, x_test, y_train, y_test = model_selection.train_test_split(X,Y, train_size = 0.8)
    
    print(x_train.size) #480
    print(x_test.size)  #120

 

  • KFold : K-fold 교차 검증을 위한, 객체 생성에 사용된다. 

→ n_split를 통해, K값을 정해주고, shuffle을 통해, 각 반복에서 데이터를 섞을지 여부를 선택가능하다. 

from sklearn.datasets import load_iris
from sklearn.model_selection import KFold
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
import numpy as np


if __name__ == '__main__':
    iris = load_iris()
    X, Y = iris.data, iris.target

    # K-Fold 교차 검증
    kfold = KFold(n_splits=5, shuffle=True, random_state=0)

    model = LogisticRegression()

    fold_accuracies = []

    for train_index, test_index in kfold.split(X):
        x_train, x_test = X[train_index], X[test_index]
        y_train, y_test = Y[train_index], Y[test_index]

        model.fit(x_train, y_train)

        y_pred = model.predict(x_test)

        accuracy = accuracy_score(y_test, y_pred)
        fold_accuracies.append(accuracy)
        
    for i, accuracy in enumerate(fold_accuracies, 1):
        print(f"Fold {i} Accuracy: {accuracy}")

    mean_accuracy = np.mean(fold_accuracies)
    print(f"\nMean Cross-Validation Accuracy: {mean_accuracy}")
  • GridSearchCV : GridSearch로 최적의 하이퍼파라미터를 찾는다. 
  • RandomizedSearchCV : Random Search로 최적의 하이퍼파라미터를 찾는다. GridSearch보다 빠르다. 

→ 최적화하려는 모델을 인자로 넣어준다. pram_grid 인자에 탐색할 하이퍼파라미터의 리스트들을 딕셔너리 형태로 넣어준다. scoring 인자를 통해, 모델의 성능 평가를 위한 지표 설정이 가능하다. cv를 통해, Fold 수를 지정 가능하다.

from sklearn.datasets import load_iris
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV, train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from scipy.stats import uniform, randint

if __name__ == '__main__':
    iris = load_iris()
    X, y = iris.data, iris.target

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

    model = LogisticRegression()

    param_grid = {
        'C': [0.001, 0.01, 0.1, 1, 10, 100],
        'penalty': ['l1', 'l2']
    }

    param_dist = {
        'C': uniform(0.001, 100),
        'penalty': ['l1', 'l2']
    }

    grid_search = GridSearchCV(model, param_grid, cv=5, scoring='accuracy')
    grid_search.fit(X_train, y_train)

    print("GridSearchCV - Best Parameters:", grid_search.best_params_)
    print("GridSearchCV - Best Accuracy:", grid_search.best_score_)

    random_search = RandomizedSearchCV(model, param_dist, n_iter=10, cv=5, scoring='accuracy', random_state=0)
    random_search.fit(X_train, y_train)

    print("\nRandomizedSearchCV - Best Parameters:", random_search.best_params_)
    print("RandomizedSearchCV - Best Accuracy:", random_search.best_score_)

 

 

<metrics> 

metrics는 모델의 성능을 쉽게 측정할 수 있도록 한다. torch 등의 딥러닝에서 얻은 데이터도 numpy나 list로 변환하여, 사이킷런 의 metrics를 이용하면, 매우 쉽게 성능을 구할 수 있다.

  • accuracy_score : accuracy 값을 구할 수 있다.
  • top_k_accuracy_score : top-k accuracy(상위 k개 중 정답 존재하는지) score를 구할 수 있다.
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score, top_k_accuracy_score, confusion_matrix, recall_score, r2_score, classification_report

if __name__ == '__main__':
    iris = load_iris()
    X,y = iris["data"], iris["target"]
    
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

    model = DecisionTreeClassifier()
    model.fit(X_train, y_train)

    y_pred = model.predict(X_test)

    accuracy = accuracy_score(y_test, y_pred)
    print(f"Accuracy Score: {accuracy}")
    # Accuracy Score: 1.0
    
    top_k_acc = top_k_accuracy_score(y_test, model.predict_proba(X_test), k=2)
    print(f"Top-k Accuracy Score: {top_k_acc}")
    # Top-k Accuracy Score: 1.0
  • roc_curve : 분류 모델의 roc curve의 x축과 y축 (각각 fpr, tpr) 값을 구할 수 있다.
  • auc : AUC(area under the curve)를 쉽게 구할 수 있다. auc를 사용하기 전에는 roc_curve함수를 먼저 사용하여, fpr과 tpr을 구해야 한다. 
  • roc_auc_score : auc 값을 roc_curve 선행 없이 구할 수 있다.
  • confusion_matrix : confusion matrix를 쉽게 구할 수 있다.
  • recall_score : recall 값을 구할 수 있다.
  • classification_report : recall, precision , f1 score등의 결과를 쉽게 구할 수 있다. 
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import auc, roc_curve, roc_auc_score, confusion_matrix, recall_score, classification_report
import numpy as np

if __name__ == '__main__':
    iris = load_iris()
    X,y = iris["data"], iris["target"]
    
    y = np.array([1 if i==0 else 0 for i in y])

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

    model = DecisionTreeClassifier()
    model.fit(X_train, y_train)

    y_pred = model.predict(X_test)
    y_prob = model.predict_proba(X_test)[:, 1]

    fpr, tpr, thresholds = roc_curve(y_test, y_prob)
    area_under_curve = auc(fpr, tpr)
    print(f"AUC Score: {area_under_curve}")
    # AUC Score: 1.0

    plt.plot(fpr, tpr, label='ROC Curve')
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.title('Receiver Operating Characteristic (ROC) Curve')
    plt.legend()
    plt.show()

    roc_auc = roc_auc_score(y_test, y_prob)
    print(f"ROC AUC Score: {roc_auc}")
    # ROC AUC Score: 1.0

    conf_matrix = confusion_matrix(y_test, y_pred)
    print(f"Confusion Matrix:\n{conf_matrix}")
    # Confusion Matrix:
    # [[20  0]
    # [ 0 10]]

    recall = recall_score(y_test, y_pred)
    print(f"Recall Score: {recall}")
    # Recall Score: 1.0

    class_report = classification_report(y_test, y_pred)
    print(f"Classification Report:\n{class_report}")
    # Classification Report:
    #               precision    recall  f1-score   support
    # 
    #            0       1.00      1.00      1.00        20
    #            1       1.00      1.00      1.00        10
    # 
    #     accuracy                           1.00        30
    #    macro avg       1.00      1.00      1.00        30
    # weighted avg       1.00      1.00      1.00        30

 

  • mean_squared_error : 평균 제곱 오차(Mean Squared Error, MSE)를 구할 수 있다.
  • r2_score : 회귀 모델의 결정 계수를 구할 수 있다.
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score, mean_squared_error
import numpy as np

if __name__ == '__main__':
    iris = load_iris()
    X,y = iris["data"][:,0].reshape(-1,1), iris["data"][:,-1]

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

    model = LinearRegression()
    model.fit(X_train, y_train)
    
    y_pred = model.predict(X_test)

    mse = mean_squared_error(y_test, y_pred)
    print(f"Mean Sqared Error:{mse}")
    # Mean Sqared Error:0.1541458433507937
    
    r2 = r2_score(y_test, y_pred) 
    print(f"R2 Score:{r2}")
    # R2 Score:0.7575009893273535

 

반응형

GPU 없는 환경에서 Image Classification을 해야 할 상황이 되었다. MobileNet으로 실험할까 하다, 성능도 어느 정도 챙기고 싶어, MobileViT 논문을 읽게 되었다. 

MobileViT v2 배경 설명

  • MobileVIT v2는 2022년 Apple에서 발표한 "Separable Self-attention for Mobile Vision Transformers" 논문에서 소개된 모델이다.
  • 기존 MobieViT가 model의 parameter 경량화에 성공하면서, CNN 기반의 mobilenet보다 좋은 성능을 보였지만, inference 속도가 느리다는 점을, separable self-attention 개념으로 해결한 논문이다.

 

Abstract

  • MobileVit가 적은 parameter로 좋은 성능을 보였지만, CNN 기반의 모델에 비해 수행시간이 오래 걸리는 문제가 있다.
  • 수행 시간 감소에 가장 큰 bottleneck은 MobileViT의 multi-headed self-attention(MHA)이다. 이 연산은 patch의 개수 k에 대해 O(k^2)의 연산 복잡도를 갖기 때문이다. 또한, MHA는 batch 단위의 matrix multiplication 같은 cost가 큰 연산을 포함하여, 수행시간이 길어지게 된다.
  • 이 논문에서는 선형복잡도를 가지는 "separable self-attention"을 제안한다. separable self-attention을 위해, element-wise 연산을 수행하여, 제한된 device 환경에서도 효율적으로 사용가능하다.
  • separable self-attention을 적용한 모델인 MobiileViTv2는 mobile vision task의 다양한 분야에서 SOTA 성능을 보인다. (ImageNet classification, MS-COCO OD)
  • 3 million parameter만으로 ImageNet top-1 accuracy 75.6%를 달성하였다. 이것은 MobileViT v1보다, 3.2배 빠르면서, 1% 정도의 높은 성능을 보여준 것이다.

 

Introduction

[배경]

  • MobileViT의 등장으로, ViT를 제한된 device 환경에서도 활용할 수 있게 되었지만, 이러한 모델은 속도 향상을 막는 주요 원인은 global representation을 학습하기 위해 꼭 필요한, multi-headed self-attention(MHA)이다. 
  • MHA 계산을 위해, token(ViT에서는 patch)의 제곱에 해당하는 연산 복잡도(O(k^2))를 가진 연산이 필요하다. 
  • 또한, MHA 계산에는 복잡한 matrix 연산이 필요한데, 이러한 cost가 큰 연산들은 제한된 메모리 환경과 power 환경에서 제한된 성능을 가지게 된다.
  • 따라서, 이 논문은 제한된 device 환경에서 사용할 수 있도록, transformer의 self-attention을 효율화할 수 있을 것인가에 대한 결과이다. 

[관련 연구]

  • ViT에 한정하지 않더라도, 몇 가지 연구들이 self-attention 연산을 최적화하기 위한 시도를 하였다.
  • 그들 중, 대다수는 self-attention layer의 sparsity를 도입하여, 각 toekn이 다른 token 전체를 계산하는 것이 아니라, 일부를 참조하도록 바꾸는 방향이다. 이러한 시도들은 연산복잡도를, O(k*sqrt(k))나 O(k*logk)까지 낮췄다. 하지만, 그에 따라 성능도 감소했다.
  • 다른 방법으로는 low-rank approximation을 이용한 연산이다. Linformer(아직 안 읽어봄)는 linear projection을 통해, self-attention 연산을 여러 개의 작은 self-attention 연산으로 decompose 하였다. 이로 인해, 연산복잡도는 O(k)까지 떨어졌다. 그러나, Linformer는 여전히 cost가 큰 batch 단위의 matrix multiplication 등을 유지하고 있다.

[소개]

  • 이 논문에서는 separable self-attention 이라는 O(k)의 연산 복잡도를 가진 새로운 self-attention 방법을 소개한다.
  • 효율적인 inference를 위해, 복잡한 연산들을 element-wise operation(덧셈, 곱셈)으로 대체했다. 

 

Model : MobileViT V2

  • MobileViT는 CNN과 ViT를 결합한 네트워크이다. MobileViT는 transformer를 convolution으로 보는 방식으로, convolution과 transformer의 장범을 모두 활용했다. MobileViT는 적은 parameter만으로도, CNN 기반의 MobileNet보다 좋은 성능을 보였으나, 속도가 느리다는 단점이 있고, 그 bottleneck은 multi-headed attention(MHA)이다.
  • MHA는 각 patch들간의 context 관계 연산을 위해, scaled dot-product attention을 사용한다. 하지만, MHA는 O(k^2)의 복잡 연산도를 가지기에, k가 큰 경우(큰 이미지나, patch를 작은 단위로 나눈 경우)에는 매우 연산량이 많아진다. 
  • 또한, MHA는 연산과 memory의 소모가 큰 batch 단위의 matrix multiplication 등의 연산을 사용하는데, 이것은 제한된 device 환경에서 성능 저하의 요인이 될 수 있다. 
  • 이를 해결하기 위해, 논문에서는 선형 복잡도를 가지는 separable self-attention을 제시한다.
  • separable self-attention의 핵심 아이디어는 latent token L에 대한 context score를 계산하는 것이다. 이 socre를 이용하여 input token을 re-weight 하고, global information을 담은 context vector도 만들어낸다. 

Overview of MHA

  • MHA는 transformer가 token 간의 관계를 encoding 할 수 있도록 하는 모듈이다.
  • Transformer의 MHA에 대한 자세한 내용은 아래 글 참조 바란다. 

2023.05.08 - [NLP 논문] - Transformer (Attention Is All You Need) - (1) 리뷰\

 

Transformer (Attention Is All You Need) - (1) 리뷰

Transformer 배경 설명 Transformer는 Google Brain이 2017년 "Attention is All You Need"라는 논문에서 제안된 딥러닝 모델이다. Transformer는 기존 자연어 처리 분야에서 주로 사용되던 RNN, LSTM 같은 순환 신경망 모

devhwi.tistory.com

 

Separable self-attention

  • separable self-attention은 MHA의 구조를 따랐다. MHA와 비슷하게, input 값은 Query, Key , Value의 3개의 branch를 통해 처리된다.

[Query]

  • ViT에서 Query는 이미지의 patch를 linear layer에 통과하여 얻은 d dimension의 feature이다. 이를 d X 1 linear layer에 통과시키고, softmax를 취해주어, context score를 생성한다. (기존 Transformer에서 연산량을 크게 줄인 부분, 과거에는 feature 단위로 attention을 계산하였지만, latent vector 단위로 attention을 계산하겠다.)

[Key]

  • 한편, Key에서는 input이 d X d linear layer를 거쳐서, k X d의 key feature를 만들고, 앞서 구한, context score와 key feature의 weighted sum으로 context vector를 구한다. 이렇게 구해진 context vector는 가볍지만, input x에 대한 모든 token 정보를 지니고 있다.

[Value]

  • 마지막으로, value에서는 input이 d X d linear layer와 ReLU를 거쳐서, k X d의 feature를 만들고, 앞서 구한 context vector를 Value feature에 적용해 준다. 이렇게 구해진 feature를 최종 d X d linear layer에 거쳐서, self-attention 값을 생성한다. 


MobileViTv2 아키텍처

  • separable self-attention의 효과를 입증하기 위해, 기본 MobileViT 구조에서 attention만 바꿨다. 
  • 추가적으로 기본 MobileViT에서 skip connection과 fusion block을 제거하여, 성능을 약간 향상했다. 

 

실험 결과

[실험 셋팅]

ImageNet-1k 학습

  • batch size 1024로 300 epoch을 scratch부터 학습시켰다.
  • optimizer로는 AdamW를 사용하였고, ImageNet-1k를 1.28 million과 5000개로 나누어, train set과 validation set을 구성했다. 

ImageNet-21k-P로 Pretraining & ImageNet-1k finetuning

  • ImageNet-21k(13 mullion, 19000 classes)를 이용하여 pretraining을 진행했다. 
  • ImageNet-21k의 validation set과 ImageNet-1k validationset은 중복되지 않는다. 

 

[기존 모델들과 비교]

  • 성능과 속도 측면, parameter 양에서 종합적으로 고려해 보았을 때, MobileViT는 좋은 성능을 보인다. 

 

[다른 Task]

  • 각각 semantic segmentation과 OD에서도 종합적으로 좋은 성능을 보인다. 

 

 

총평 

  • 간단한 아이디어인데, 논문에서 비슷한 내용이 계속 반복되는 느낌이 든다. 
  • 해결한 방법보다, 현재 transformer의 구조에서 속도가 느린 점을 명확히 파악한 것이 더 흥미롭다. 
  • 가벼운 모델 하면, EfficientNet과 MobileNet 정도 생각했었는데, 사용 가능한 선택지가 하나 추가된 것 같다. 

 

Reference

MEHTA, Sachin; RASTEGARI, Mohammad. Separable self-attention for mobile vision transformers. arXiv preprint arXiv:2206.02680, 2022.

 

반응형

 
 
원래, 일기도 거의 쓰지 않는 나지만, 올해보다 발전된 2024년을 바라면서, 2023년도를 회고해보고자 한다.
 

2023년에 있던 일들

2023년을 돌이켜보면 아쉬움은 많이 남지만, 나에게 많은 변화가 있던 해이다. 개발 블로그를 시작했으며, NLP 분야를 본격적으로 공부하기 시작했다. 평소 잘 알지 못했던 Clean Code에 대한 개념도 새로 배우게 되었고, 그 과정에서 수많은 코드들을 리팩터링 하였다. Spark를 공부하여, Plsql 기반 코드를 변환해보기도 하였고, 병렬 구조에 대한 이해가 더 생기기도 하였다. 개인적으로는 도전과 실패에 대한 무서움이 적어져, 예전보다 더 많은 도전을 하였지만, 그만큼 많은 실패를 하였다. 가끔 한숨이 나오기도 했지만, 다 내 삶에 자양분이 될 것이라고 생각한다. 
 

회사 업무

회사 업무의 큰 변화는 없었다. 요청 온 사항을 분석하고, 이를 해결하는 업무를 진행하였다. 다만, 변한점이 있다면, 1000줄 이상의 복잡한 SQL문으로 구성되는 코드들을 싫어했지만, 이제는 성능 관점에서의 적당한 양보(?)는 허용할 수 있어졌다. 보안상 자세한 이야기들은 할 수 없지만, 운영 중인 시스템에 성능 개선에 대한 니즈가 있어, 새로운 아키텍처들을 많이 찾아보게 되었다. 이 과정에서 코드 프로파일링이나, 자원 모니터링, SQL Tuning 등 많은 부분을 공부하게 되었다. 아쉬운 점으로는 진행하고자 하였던 많은 과제들이 다양한 사정을 통해 꺾이는 것을 보면서, 내 스스로 신입 때의 도전정신과 과격함이 점점 줄어드는 것 같아 걱정이 된다. 아쉬움에 잠 못 이루던 날들도 많았지만, 그래도 도전과 성장 욕구(?)를 채우기 위해, 회사 밖에서 더 노력할 수 있는 원동력이 되지 않았을까 하면서 스스로를 위로해본다.

 

조직 생활

파트에 유능한 신입 인재들이 많이 합류하였다. 모두들 실력도 뛰어나고, 의지도 넘쳐서, 같이 이야기 나눌 수 있는 사람들이 많아져서 기분이 좋아졌다. 새로 들어온 분들을 보면서, 재작년엔 스스로의 성장이 가장 중요한 오브젝티브였는데, 이젠 그들에게 도움이 될 수 있는 사람이 되고 싶다는 생각을 한다. 특히, 나에게 많은 질문을 해주는 동료가 있는데, 이 분께 최대한 정확한 답변과 도움을 주기 위해, 스스로 많이 찾아보게 된 것 같다. (감사합니다.)
한편으로는 그분들이 주장하는 다소 과격해 보이는 변화에 대해, 방어적인 태도를 취하는 나를 보며, 걱정하는 시기도 있었다. (지금은 기술적으로 이유 없는 반대하지 않기에서 타협점을 찾았다.)  이런 고민들을 할 수 있도록, 항상 의문을 갖고 최선을 다해주는 동료들이 있어서 조직생활에서는 좋은 일만 있었던 한 해였다.

 

코드 리뷰어

우리 회사에는 클린코드에 대해, 교육과 활동 등을 진행하고, 이를 자격으로 인증해 주는 제도가 있다. 사실 그동안 머신러닝이나 딥러닝 코드들을 혼자나 소수로 개발하고 공유하는 것에 익숙해져서, 좋은 코드를 짜는 것을 교육으로 운영한다는 것 자체에 거부감을 느꼈다. 하지만, 교육 기간 동안 굉장히 많은 것들을 배웠고, 뛰어난 타 부서 분들과 협업의 기회가 있어서, 매우 좋은 시간이었다. 교육 후에 거의 모든 코드를 리팩터링 하고, 부서원들로부터 코드리뷰를 받았는데, 그 과정에서 몰랐던 개념들을 많이 배웠고, 좋은 코드를 위해 코드리뷰가 얼마나 중요한지 다시 한번 느끼게 되었다. 
 

 

블로그 시작

지금 글을 쓰는 이 블로그를 올해 시작하게 되었다. 블로그를 시작하게 된 이유는 어차피 공부하는 내용이 휘발될 것이 아깝기도 하였고, 꾸준히 공부하기 위한 원동력이 필요하였다. 특히, 누군가 질문했을 때, 정돈된 답을 하고 싶다는 욕구가 존재하여, 공부한 내용을 글로 정리하게 되었다. 블로그에 글을 써가면서, 점점 이미 아는 내용의 소재가 떨어지고, 새로운 내용을 탐색하게 되면서, 나아가고 있다는 느낌을 받아, 매우 기분이 좋다. 블로그의 또 큰 장점은 회사 업무와 관련된 내용을 공부하고, 올렸을 때, 빨리 회사에 가서 새로 공부한 내용을 바로 적용해보고 싶다는 기분으로 회사 출근에 거부감을 없애준다는 것이다. 대학원 시절, 갑자기 생각난 아이디어를 빨리 적용해보고 싶어, 연구실로 향하던 기분을 조금이나마 재현해 냈다. 블로그 활동 중, 그나마 아쉬운 점을 뽑으라면, 퇴근 후에 글을 작성하다 보니, 간단하게 작성할 수 있는 이론 설명이나, 논문 리뷰성 글을 많이 올리게 되는 것 같다. 24년에는 개발한 내용을 정리하는 형식으로 발전해야겠다. 

 

NLP 공부 시작

컴퓨터 비전 베이스라, NLP도 비슷하여 쉽게 공부할 수 있을 것이라고 생각했다. 근데 NLP는 컴퓨터 비전과 차원이 다르게 빨리 발전하였고, 결과도 매우 명확하여 공부할 내용이 많았다. 흐름을 따라가기 위해, NLP 쪽 유명한 논문들은 최대한 다 읽으려고 노력했고, 직접 코드도 많이 짰다. 점점 데이터엔지니어링 영역으로 발전하는 LLM 논문들을 보면서, 모델링 중심의 AI보다는 데이터엔지니어링이 중요하겠구나라는 생각을 다시 하였고, 공부의 방향도 다시 설정하게 되었다. 모델링의 영역이 과거보다 희미해지니, 컴퓨터 비전쪽 논문도 처음보다는 덜 어렵게 읽히는 것 같아, 최근에는 컴퓨터 비전쪽 논문을 많이 읽으려 노력 중이다. 논문을 다수 읽으면서 느끼는 점은 단순 이해도 중요하지만, 가능하면 코드를 직접 짜보는 것이 좀 더 전문가로 성장할 수 있는 방향이 아닐까 싶다. 

 

도전과 실패

개인적으로는 23년에 정말 많은 도전을 하였고, 그에 따른 많은 실패도 하였다. 인생에서 그동안 실패의 기회들이 적었기 때문에, 작은 실패에도 굉장히 큰 타격을 받았고, 그것을 인정하는 것이 매우 어려웠다. 올해를 긍정적으로 생각하면, 실패에 대해 조금은 무뎌져, 도전의 어려움이 조금은 줄어들었다는 것이고, 아쉬운 점은 결국 아무것도 성공하지 못했다는 것이다. 그래도 내 인생의 좌우명처럼 "아무것도 하지 않으면 아무것도 이뤄지지 않는다"는 것을 알기에 나는 계속 시도를 할 예정이다. 이렇게 하다 보면 언젠가는 성공할 날도 있지 않을까?
 

 

Refresh

나의 스트레스를 푸는 유이한 방법은 운동과 먹는 것이다. 사실 먹는 것은 효과가 명확한데, 운동은 그 행위 자체의 행복보다는 나를 변화시키기 위해 무언가를 했다는 안도에 가까웠다. 23년에는 아직 완전히는 아니지만, 운동 자체에서 가끔 재미를 찾기도 하고, 다른 사람들이 가끔 칭찬을 해주면 기분이 매우 좋았다. 최근에는 거의 3~4년 만에 해외여행을 갔는데, 몇 년 간 묵었던 스트레스가 풀리는 기분도 나고, 세상이 넓음에 감탄하기도 했다. 가끔은 여행으로 스트레스를 푸는 것도 좋을 것 같다. 또 이번 여행에서 갑자기 오르골을 하나 사게 되었는데, 오르골 소리를 듣고 있으면 여행 때 좋았던 생각이 나기도 하고, 마음이 평온해진다. 매일 자기 전에 한 번씩 돌리고 자야겠다. 

 

2024년에는...

  • 24년 가장 이루고 싶은 것은 "증명"이다. 단순히 이 사람은 이걸 잘해가 아닌, 가시적인 결과를 내고 싶다. 
  • 24년 가장 바라는 점은 "많은 기회"다. 아직 나는 노력으로 뭐든 이룰 수 있다고 믿는다. 이를 위해 24년에는 많은 기회들이 주어졌으면 좋겠다.
  • 이를 위해, 올해는 공부할 내용을 조금 더 체계적으로 계획해야겠다. 
  • 내 주변 사람들 모두 행복했으면 좋겠다. 
반응형

논문 배경 설명

  • Fine-tuning Image Transformers using Learnable Memory은 2022년 CVPR에 제출된 Google 논문이다.
  • memory token 개념을 사용하여, ViT에서 과거 task에 대한 정보를 저장하여, 성능을 유지하고, 새로운 task에 대한 학습을 진행할 수 있는 방법을 소개했다. 
  • 저자들은 지속 & 확장 가능한 memory 개념으로 소개하는데, 만약 진짜라면, external memory 개념으로 탈부착 가능한 memory가 될 수도 있지 않을까? 하는 생각이 든다. 

 

Abstract

  • 이 논문에서는 Vision Transformer model에 학습 가능한 memory token을 넣은 새로운 ViT 모델을 소개한다.
  • 이 모델의 방법에서는 하나의 task에서 학습한 parameter들을 새로운 task 학습에서 활용 가능하다.
  • 이를 위해, 각 layer들에 특정 dataset에 대한 contextual 정보를 제공하는 "memory token"이라는  learnable embedding vector들을 도입하였다. 
  • 이러한 간단한 변형은 transfer learning에서 단순 head만을 fine-tuning 하는 것보다 큰 성능 향상을 보이고, 연산 비용이 매우 큰 full fine-tuning에 비교해서도 약간의 성능 차이만 보인다. (약간 낮다.)
  • 또한, 이 논문에서는 computation 재사용으로 새로운 downstream task들에 사용가능한 attention-masking 방법을 소개한다.

 

Introduction

[배경]

  • ViT 모델은 일반적으로 대량의 데이터들을 통해 학습되고, 다양한 downstream task에서 성능 향상을 위해 fine-tuning 하는 방법을 사용한다. 
  • 높은 정확도를 위해, 전체 모델을 목적에 맞는 task에 fine-tuning하는 것이 가장 좋은 방법이다.

[문제]

  • 하지만, 일반적으로 Transformer 기반 모델들은 많은 수의 parameter로 구성되어 있기 때문에, fine-tuning 과정에 연산 cost가 크고, 특히, 전체 모델 fine-tuning 방법은 learning rate에 민감하다는 문제가 있다. 

[소개]

  • 이 논문에서는 transformer의 각 layer에 learnable token을 새로 추가하여, pre-trained model을 구성하는 새로운 방법을 소개한다. 
  • 새로운 token들은 최종 prediction 성능 향상에 사용될 수 있는 contextual 정보들을 포함한 영구적 memory처럼 동작한다.
  • 또한, 이러한 token은 pixel 기반 patch부터 최종 concept 수준까지 다양한 수준의 추상화 개념을 나타낼 수 있다. (pixel 정보부터 다양한 문맥 정보까지 모두 포함할 수 있다.)

[결과]

  • 새로운 방법은 downstream task에서 기존 head를 fine-tuning한 모델보다, 큰 성능 향상을 보였다. 
  • 또한, 이러한 아키텍처 디자인은 약간의 computing cost 증가만으로, 새로운 taksk를 학습하면서도, 기존 task에 대한 성능을 유지할 수 있게 한다. (기존 모델들은 새로운 task를 학습하면, 이전 task에 대한 성능이 떨어짐)
  • 이를 위해, 적절한 attention masking이라는 새로운 방법을 소개하여, 과거 task에 대한 정보를 memory에 저장하는 방식을 사용한다. 이러한 방식은 continual learning의 개념처럼, 계속하여 새로운 기능을 더하여 다양한 task에 모두 활용 가능한 하나의 모델로 활용될 수 있다.

 

Memory Model

  • Base mode로는 original ViT 논문의 구조를 그대로 사용한다. (다만, 이 논문에서는 classification model들만을 고려하기 때문에, decoder는 사용하지 않는다. )
  • 일반적인 image transformer의 input은 다음과 같다. (Epos : positional embedding, Xcls : class token)

  • 논문에서는 새로운 memory 정보를 더하기 위해, 이 input에 m개의 learning embedding(Emem)을 concatenate해준다.

  • 즉, N+1+m token이 Transformer encoder layer들의 input으로 사용된다.
  • 각 layer들은 일반 ViT의 layer들과 거의 비슷하다. 유일하게 다른 점은 self attention module의 처음 N+1 token들만 output으로 보낸다는 점이다. (m개의 token들은 다음 layer로 보내지 않음)
  • 따라서, l번 layer의 output은 N+1개의 token을 가진 y_l로 명명하면, 연속적인 layer들은 모두 아래와 같은 pattern의 input을 받는다. (전 layer에서 token N+1개와 memory token m개)

  • 전체 network 구조는 아래와 같다. 

 

[Fine-tuning with full attention]

  • 이 모델에서 memory의 주된 용도는 fine-tuning에서의 활용이다.
  • 이를 위해, randomly-initialized memory token을 제시하고, gradient descent를 활용하여 memory token을 학습한다. 
  • 이 방법은 좋은 결과를 보여주지만, 이렇게 학습된 model은 hidden activation들의 변화가 일어나, 과거 task에 더 이상 활용 불가능하다.

[Attention masking for computation reuse]

  • 새로운 task만을 위한 fine-tuning에서는 문제가 되지 않지만, 앞선 task의 성능을 유지하면서, 새로운 task를 학습하고 싶은 경우에는 활용 불가능하다.
  • 이 논문에서는 기존 dataset class token을 유지하면서, 새로운 dataset class token에 대한 memory 구조를 사용한다. 
  • 결과적으로, parameter들과 computation을 reuse 하여 동일 input에 대한 multiple task들을 처리할 수 있다. 
  • 이를 위해, original class token 및 input patch들이 새로운 dataset이 사용하는 memory token 및 new class token에 활용되지 않도록 막는 attention mask를 사용한다.  (attention mask의 동작은 아래와 같다. 아래 그림처럼, 해당하는 학습에 대한 class token만 유지하고, 다른 class들은 masking 한다. 별표가 mask)
  • 각 task의 head는 각 dataset token에 연결된다. 

[model concatenation]

  • Attention masking은 새로운 task 확장을 위한 memory 추가에 이용될 수 있다.
  • 새로운 dataset에 대한 model 학습 시에는 attention masking을 이용하여 이전 dataset들에 대한 참조를 막고, 새로운 dataset에 대해 학습하는 방법을 사용한다. 
  • 아래 그림처럼, 각기 독립적으로 학습된 memory를 연결하여, 모델을 구성한다. 

Experiments

[실험 setup]

  • ViT-B/32 base transformer model을 base 모델로 사용하였고, ViT original paper에서 제공된 Imagenet-21K pretrained model을 사용했다. (80M parameters)
  • orginal ViT의 fine-tuning 방법을 따라, cosine learning reate schedule 등의 fine-tuning setup을 그대로 사용했다. 
  • batch size는 512이고, 20000 step의 fine-tuning을 진행했다. 
  • SGD를 사용했고, gradient clipping을 사용했다. 5-step의 linear rate warmup도 했다.
  • Memory는 N(0,0.02)로 initialization 시켰다. 
  • 성능 측정을 위한 dataset으로는 CIFAR-100, i-Naturalist, Places-365, SUN-397을 사용했다. 

 

[실험 모델: Baseline fine-tuning]

 

Full fine-tuning

  • 전체 model을 fine-tuning 함. 가장 expensive 하고, 각 task가 전체 model이 필요하기 때문에, 실용성이 가장 떨어짐. overfitting 가능성이나, learning rate에 민감함. 

Head-only fine-tuning

  • classifier의 head만 fine-tuning 함. 이 방식은 pre-trained의 mmbedding을 재활용함. 가장 큰 장점은 parameter들과 compute를 재사용할 수 있다는 것.

Head + Class token

  • head와 class token을 fine-tuning함. 이 방식에서는 input token attention이 새로운 class token에 따라 바뀌면서, 연산 재사용이 불가능함. 

[실험 모델: memory fine-tuning]

 

Memory + head + class token

  • memory fine-tuning 모델로 부르는, 논문에서 소개된 모델임. 1,2,5,10,20 cell에 대한 실험을 진행함. 각 layer에서 20개 이상에서는 더 이상 성능 향상이 일어나지 않음. cell 5개가 알맞았음. 

Memory with attention mask

  • head-only fine-tuning과 Head + Class token의 장점을 결합한 방법. Head + Class token보다 높은 성능을 보이고, full-attention fine-tuning보다는 약간 낮은 성능을 보임. 이 방법의 가장 큰 장점은 독립적으로 다양한 task를 fine-tuning 할 수 있다는 것이고, 여러 연산을 하나의 모델로 모을 수 있다는 것.

 

[Transfer Learning 성능 비교]

 

  • full fine-tuning은 learning rate에 민감하다. large learning tate에서 큰 성능 저하를 일으켜, 이 부분에서는 memory와 head-only fine-tuning이 오히려 더 좋은 성능을 보인다. 
  • 아래 그림에서 확인할 수 있듯, memory를 이용한 방법들이 다른 방식의 fine-tuning보다 좋은 성능을 보인다. 

  • 각 fine-tuning의 가장 optimal learning rate에서의 성능을 비교해 보았을 때, 결과는 아래 표와 같다. 
  • 많은 데이터셋에서 5~20 cell 사이의 memory fine-tuning이 좋은 성능을 보인다. 

 

Conclusion & Limitation

  • 이 논문에서는 memory 구조를 transformer model에 결합한 새로운 방법을 제안했다.
  • memory를 사용한 새로운 모델은 fine-tuning에서 좋은 성능을 보였고, 다양한 task 학습을 가능하게 했다.
  • memory yoken은 attention model의 중요한 부분으로, lifetime and continuous learning에 중요한 요소가 될 것으로 생각된다. 
  • 몇 가지 한계와 future work를 소개한다.

[scaling memory]

  • 저자들은 memory token이 특정 숫자를 넘어가면, 성능 향상이 줄어드는 것을 발견했다. 
  • 한 가지 가능한 방법은 Top-K memory token만 참조하도록 제한을 거는 것이다. 이러한 방법은 불필요한 token들의 참조를 막아, background noise 등을 제거할 수 있을 것이다.

[combining episodic memory with learnable memory]

  • Episodic memory와 함께 사용하는 방법도 생각해 볼 수 있다.

[incremetal model extension]

  • 논문에서는 multiple attention masking model이 독립적으로 학습되어 하나의 모델로 합쳐지는 방법을 사용하였다. 하지만, 순차적인 학습으로 인한 중간 메모리 축적으로 생기는 이점(아마, 각기 다른 학습 간의 시너지를 의미하는 것 같음)은 커리큘럼 학습에 중요하고, 이것이 향후 연구의 주제가 될 것이다. 

 

논문 총평

  • 매우 아이디어가 간단하고, 아이디어에 대한 실험 결과가 매우 명확한 논문이다. 
  • 과거에 Knowledge distilliation을 잠시 파본 기억이 있는데, CNN 기반에서 distillation이 매우 어렵다고 생각했었는데, ViT 기반에서 memory token 개념을 사용하여 해결한 것이 매우 인상적이다. (그 후론 CNN 기반에서 방법을 찾아본 적이 없어서, 비슷한 아이디어가 있는지는 모르겠다.)
  • 다만, 실제 데이터에서 잘 working 하는지는 직접 실험을 해봐야 판단이 가능할 것 같다.

 

Reference

Sandler, M., Zhmoginov, A., Vladymyrov, M., & Jackson, A. (2022). Fine-tuning image transformers using learnable memory. In Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (pp. 12155-12164).

반응형

MobileViT 배경 설명

  • MobileViT은 2022년 Apple에서 ICLR 2022에 제출한 논문이다. (Apple이여서, mobile에 대한 CNN이 더욱 필요했을 것이다.)
  • CNN에서 mobilenet이 나왔듯, ViT에서도 light cost에 초점을 맞춘 논문이 등장하였다.

 

Abstract

  • mobile 환경에서 구동 가능할 정도의 가벼운 vision task CNN 모델이 등장하였었다. (mobilenet) 
  • 하지만, CNN과 달리 ViT는 최근 많은 vision task에 사용됨에도 불구하고, global representation들을 활용하기 위한 self-attention 구조를 사용하기 때문에, CNN에 비해 모델이 무겁다.
  • 이 논문에서는 CNN과 ViT를 결합하여 mobile vision task에서 구동 가능한 가벼운 ViT 모델을 만들 수 있는가? 에 대한 답을 제시한다. 
  • 이를 위해, 가볍고, 좋은 성능을 내고, 다양한 용도로 사용할 수 있는 MobileViT를 소개한다. 
  • 실험에서는 MobileViT가 다양한 task와 dataset들에서 CNN과 ViT 기반의 모델보다 압도적인 성능을 보여준다.

 

Introduction

[배경]

  • ViT의 소개 이후로, ViT의 연구 트랜드는 model의 parameter의 양을 늘리면서, 좋은 성능의 모델을 만들어내는 것이었다. (LLM처럼...)
  • 하지만, 이런 모델 크기의 증가는 model의 용량 증가와 속도 저하등을 일으킨다. 
  • 실생활에서는 mobile 기기 같은 제한된 H/W에서 구동되어야 하는 task들이 많이 존재하기 때문에, 가볍고 빠른 모델에 대한 요구사항이 있다. 
  • 이를 위해, ViT의 parameter 개수를 줄이면, 오히려, 가벼운 CNN들보다도 좋지 않은 성능을 보여준다. 따라서, 어떻게좋은 성능을 유지하면서 가벼운 ViT를 만들 것인지에 대한 심도 깊은 고민이 필요하다.

[CNN + ViT]

  • 일반적으로 ViT는 CNN에 많은 wieght를 필요로 하고, optimize가 어렵다. 예를 들어, ViT 기반의 segmentation network는 345 million의 parameter가 필요한데, 비슷한 성능의 CNN은 59 million으로, ViT 기반이 비효율적이다. 이것은 ViT가 CNN의 특성인 image specific inductive bias가 부족하기 때문이다.
  • 좋은 성능의 ViT 모델을 위해서라면, convolution과 transformer의 하이브리드 방법을 채택하는 것이 효과적일 수 있다.
  •  하지만, 기존 시도된 하이브리드 방법들은 아직 매우 큰 model size를 가지고 있고, data augmentation에 민감하다는 단점이 있다. 

[MobileViT]

  • 이 논문에서는 제한된 H/W 리소스에서도 효과적으로 좋은 성능을 보이는 ViT 모델을 만들고자 한다. 
  • 모델의 효율성 측정을 위해 보통 사용되는 FLops는 mobile 장비 등에서 성능 측정에는 충분하지 않다. 이는 FLOPs는 memory access나 parallelism 정도, platform 특성등을 고려하지 않기 때문이다. 
  • 따라서, 논문에서는 FLOPs을 optimize 하기보다는, light weight, general-purpose, low latency에 초점을 맞췄다.  
  • 이 논문에서는 CNN의 spatial inductive bias와 ViT의 global processing에 장점들을 결합가능한 MobileViT model을 소개한다.
  • 특히, local 정보와 global 정보를 모두 encoding 할 수 있는 MobileViT block은 효과적인 representation을 포함한 feature를 만들 수 있게 해 준다.

 

MobileViT : A light-weight Transformer

  • 일반적인 ViT model에서는 (H X W X C) 크기의 이미지를 P 사이즈의 patch들로 자르고, flatten 하고, projection 하여, (N X d) 크기의 inter patch representation으로 만든다.  이때, comnputational cost는 O(N*N*d)이다. (N: number of patch)
  • 이러한 ViT 구조의 모델들은 CNN에 내재된 spatial inductive bias를 무시하기 때문에, CNN에 비해 더 많은 parameter를 필요로 한다. 
  • 또한, 이런 구조들의 모델은 L2 regularization이나, overfitting을 피하기 위해 많은 data augmentation이 필요한 등 optimization이 어렵다. 

 
[MobileViT 아키텍처]
 
MiobileViT의 핵심 아이디어는 Transformer를 convolution으로 사용하여 global representation들을 학습하는 것이다. 
 

  • MobileViT block 
    • MobileViT block은 model이 input의 local과 global 정보를 적은 paramter 안에 모두 담을 수 있도록 하는 것이 목적이다. 
    • 우선 MobileViT는 (n X n) 개의 convolutional layer와 (1 X 1) convolution을 수행해 준다. 이로 인해, local representation을 포함하게 된다. 
    • global representation을 학습하기 위해, 앞서 covolutional layer의 output을 flatten 하고 겹치지 않도록, patch로 나눠준다. 그러고 나서, 이 patch들을 input으로 Transformer에 넣어준다. 
    • 이로 인해, 아래 그림처럼, local 정보를 포함하면서, global 정보까지 포함할 수 있는 feature가 완성되게 된다.

 

  • Light Weight
    • MobileViT는 image specific inductive bias를 사용할 수 있기 때문에, 일반 ViT에 비해 훨씬 더 가벼운 모델로 학습이 가능하다. (dimension 등을 줄일 수 있음)
  • Computational Cost
    • MobileViT와 ViT의 computational cost를 비교하면, 각각 O(N*N*P*d)와 O(N*N*d)로 이론적으로는 MobileViT가 더 비효율적으로 보인다. 하지만, 실제로는 MobileViT가 DeIT ImageNet-1K image classification에서 2배 적은 FLOPs으로 1.8% 좋은 성능을 낼 정도로 더 효과적이다.
    • 이것은 더 가벼운 모델로 학습이 가능하기 때문이다. 

 
[Multi-scale sampler for training efficiency]

  • 일반적으로 ViT 기반의 모델들은 fine-tuning 시에 다양한 스케일의 representation들을 학습한다. 이 과정에서 Positional embedding을 사용하는데, 이는 input size에 기반하여 interpolation이 필요하고, interpolation 방법에 따라 성능에 영향을 미치게 된다. 
  • CNN과 마찬가지로, MobileViT에서도 이러한 positional embedding이 필요가 없다. 
  • MobileViT에서 이러한 다양한 스케일의 representation들을 학습하기 위해, batch size를 변경시키는 방법을 사용했다. 다양한 spatial resolutions set을 만들어 놓고, 각 bach 시, resoluton을 무작위로 뽑고, 해당 resolution에 따라 batch size를 유동적으로 변경하여 학습했다. (small spatial resolution에는 많은 batch) 
  • 이러한 학습 방법은 더 빠른 optimize가 가능하도록 하였다. 

 

실험 결과

  • MobileViT 모델을 scratch부터 ImageNet-1K classification dataset으로 학습했다.
  • multi-scale sampler로 각각 (160,160), (192,192), (256,256), (288,288), (320,320) 해상도를 사용했다. 
  • 성능 측정은 top-1 accuracy로 진행했다. 

[CNN과 비교]

  • MobileViT가 CNN 기반의 MobileNet 등의 light-weight CNN이나, ResNet 등의 일반적인 CNN의 성능을 압도했다.

[ViT 비교]

  • ViT와 비교해서도 좋은 성능을 보인다. 특히, DataAugmentation에 따라 성능이 크게 바뀌는 다른 모델들과 달리, Basic Augmentation만으로도 다른 ViT보다 좋은 성능을 보인다. 

 
 

총평

  • 매우 아이디어가 간단하고, 효과적인 것 같다.
  • 논문 내용만 보면, 장점만 많아서, 진짜인지 직접 돌려보고 싶다. 

 

Reference

Mehta, S., & Rastegari, M. (2021). Mobilevit: light-weight, general-purpose, and mobile-friendly vision transformer. arXiv preprint arXiv:2110.02178.

+ Recent posts