Introduction 

저의 경우에서는 DDP를 구성하고 코드 작업을 하는 경우가 많다. 그렇다 보면 torch에서 DDP로 구성하는것이 귀찮기도 하고 생각보다 시간이 걸리는데 

이러한 노고의 시간을 줄여주기 위한 유용한 library가 있어서 공유하고자 한다. 

https://huggingface.co/docs/accelerate/index

 

Accelerate

Concepts and fundamentals

huggingface.co

 

 

홈페이지에 들어가보면 보는 것과 같이 Pytorch code의 distributed configuration을 4개의 라인으로 줄여서 효과적으로 사용하게 되는 방법을 제공해준다고 한다. 

홈페이지에서 제공해주는 코드지만 아래의 코드에 보는바와 같이 4줄이면 DDP setting이 끝나는 것을 볼 수 있다.

+ from accelerate import Accelerator
+ accelerator = Accelerator()

+ model, optimizer, training_dataloader, scheduler = accelerator.prepare(
+     model, optimizer, training_dataloader, scheduler
+ )

  for batch in training_dataloader:
      optimizer.zero_grad()
      inputs, targets = batch
      inputs = inputs.to(device)
      targets = targets.to(device)
      outputs = model(inputs)
      loss = loss_function(outputs, targets)
+     accelerator.backward(loss)
      optimizer.step()
      scheduler.step()

위와 같이 코드를 넣어지게 되어지만 torch_xla, torch.distributed가 build가 되어진다. 

tmi) 최근에서는 fully sharded data parallelism도 되어지니 유용하게 사용하고 있다. 

 

코드 실행은 아래와 같이 python파일에 accelerate launch만 넣어주면 된다. 

accelerate launch {my_script.py}

 

그러면 아주 설치과정부터 기본 실행까지 보자.

 Installation 

설치는 Python3.8이상부터는 되어지는 환경이고 pip 으로 설치할 경우 아래와 같이 되어진다. 

pip install accelerate

conda의 경우도 아래와 코드와 같이 되어지며 source github에서 설치할려면 다음과 같다. 

conda install -c conda-forge accelerate
pip install git+https://github.com/huggingface/accelerate

그리고 acclerate에 기본 default로 설정되어지는 값들이 있는데 이 값들의 수정함으로써 DDP나 process의 설정이나 config에 대해서 설정할수 있다. 

accelerate env

- `Accelerate` version: 0.11.0.dev0
- Platform: Linux-5.10.0-15-cloud-amd64-x86_64-with-debian-11.3
- Python version: 3.7.12
- Numpy version: 1.19.5
- PyTorch version (GPU?): 1.12.0+cu102 (True)
- `Accelerate` default config:
        - compute_environment: LOCAL_MACHINE
        - distributed_type: MULTI_GPU
        - mixed_precision: no
        - use_cpu: False
        - num_processes: 2
        - machine_rank: 0
        - num_machines: 1
        - main_process_ip: None
        - main_process_port: None
        - main_training_function: main
        - deepspeed_config: {}
        - fsdp_config: {}

 

Start

설치가 끝났다면 어떻게 사용하는지 다시 한번 살쳐보자. 

1. 첫번째로는 아래와 같이 accelerate를 불러온다. 

 

이때 Accelerator의 내부에서는 많은 변수들을 받을수 있는데 scipts안에서 setting이 가능하기때문에 이 내용에 대해서는 추구에 살펴보기도 하자.

참고로 GPU의 갯수에 따라서 multi GPU인지 single GPU인지 인식이 되어저 사동으로 환경을 setting해주는 많이 건들어질것 없을것이다. 

from accelerate import Accelerator 
accelerator = Accelerator()

 

2. 두번째로 중요한건 우리가 tensor에 선언헀던 to(device), cuda()부분은 지워야 한다. 

왜냐하면 accelerator로 선언되어진 객체로 multi gpu에 따라서 device를 자동으로 할당되어주기 때문에 to(accelerator.device)로 변경해줘야 한다. 

3. training에 사용되어지는 객체들을 prepare() 라는 곳에 넣어준다. (아래코드와 같다. )

아래 코드오 Parellel하게 사용되어지는 경우가 필요하면 prepare에 넣으면 되며 

model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare(
    model, optimizer, train_dataloader, lr_scheduler
)

4. loss.backward()의 경우도 DDP로 되어지는 경우도 있음으로 accelerator.backward(loss)로 되어져야 한다. 

Config 

accelerate를 사용하게 되어질때 이전의 multi GPU나 mixed precision이나 선택해야될때가 있는데  

이것에 따른 config를 설정해주기 위해서 accelerate config라는 명령어가 있다. 

아래와 같이 입력하면 각종 setting들을 쉽게 만들어준다. 

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------In which compute environment are you running?
This machine                                                                                                                                                                                                                
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Which type of machine are you using?                                                                                                                                                                                        
multi-GPU                                                                                                                                                                                                                   
How many different machines will you use (use more than 1 for multi-node training)? [1]: 2                                                                                                                                  
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------What is the rank of this machine?                                                                                                                                                                                           
0                                                                                                                                                                                                                           
What is the IP address of the machine that will host the main process? ---                                                                                                                                                
What is the port you will use to communicate with the main process? ---                                                                                                                                                   
Are all the machines on the same local network? Answer `no` if nodes are on the cloud and/or on different network hosts [YES/no]: ㅜno
What rendezvous backend will you use? ('static', 'c10d', ...): static
Do you wish to optimize your script with torch dynamo?[yes/NO]:no
Do you want to use DeepSpeed? [yes/NO]: no
Do you want to use FullyShardedDataParallel? [yes/NO]: no
Do you want to use Megatron-LM ? [yes/NO]: no
How many GPU(s) should be used for distributed training? [1]:2
What GPU(s) (by id) should be used for training on this machine as a comma-seperated list? [all]:1,2
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Do you wish to use FP16 or BF16 (mixed precision)?
fp16                                                                                                                                                                                                                        
accelerate configuration saved at ~/.cache/huggingface/accelerate/default_config.yaml

 

위의 같은 명령어로 만들어주면 yaml파일이 만들어지며 

이렇게 설정된 config는 ~/.cache/huggingface/accelerate/default_config.yaml 에 있음으로 확인설정을 할수 있다. 

compute_environment: LOCAL_MACHINE
deepspeed_config: {}
distributed_type: MULTI_GPU
downcast_bf16: 'no'
dynamo_backend: 'NO'
fsdp_config: {}
gpu_ids: 1,2
machine_rank: 0
main_process_ip: 
main_process_port: 
main_training_function: main
megatron_lm_config: {}
mixed_precision: fp16
num_machines: 2
num_processes: 2
rdzv_backend: static
same_network: false
use_cpu: false

 

 

이러한 yaml을 변경해서 사용해도 되며 아래와 같이 config를 test하여 정상적으로 돌아가는지 확인 할 수도 있다.

accelerate test --config_file path_to_config.yaml

최종적인 코드 실행은 아래와 같이 실행이 되어지면 multi-gpu에 대해서 자동으로 할당되어서 parellel하게 실행이 되어진다. 

accelerate launch path_to_script.py --args_for_the_script

 

이렇게 실행된 코드 안에서 유용한 methode들도 많은데 이것에 대해서 간략하게 정리를 해보면 다음과 같다. 

  • if accelerator.is_local_main_process : 서버당 하나의 GPU만 동작 (서버 : GPU장비가 모인 곳)
  • if accelerator.is_main_process : 머신당 하나의 GPU에서만 동작 (머신 : GPU들)
  • accelerator.wait_for_everyone() : Multi GPU에 있는 모든 값을 모으기 위해서 다른 GPU에 있는 동작을 기다리는 명령어. 
  • accelerator.save_model(model, save_directory) : 기다린 GPU들을 model을 저장함(단 저장하기 전에 모델의 크기로 인해서 unwrap을 해야됨)
  • unwrapped_model = accelerator.unwrap_model(model) : 각 GPU에 있는 큰 모델들을 하나의 모델로 변경 (torch 저장을 추천하는 방법으로 unwarp을 해주어서 model을 뱉어낸후에 torch.save로 해서 저장함)

ETC

우리가 흔히 deep learning을 사용하다보면 kaggle이나, notebook에서 사용하는 경우가 많은데 

이와 같은 경우에서는 학습하는 function을 notebook_launcher에 넣고 실행하면된다. 

코드는 아래와 같다. 

from accelerate import notebook_launcher

notebook_launcher(training_function)

 

 

마치며

이번시간에서는 이렇게 accelerator에 유용한 기능들과 실행 방법에 대해서 알아보았다. 

다음번에는 좀더 다양한 기능들에 대해서 알아보겠다.

 

 

반응형

객체 지향 프로그래밍(Object-Oriented Programmin, OOP)라고 부르며 컴퓨터 프로그래밍에서 많이 사용이 되어진다. 

Python을 사용하고 Deep learning에 dataset이나 model을 만들거나 사용할때 중복되거나 코드가 비효율적일때가 많은데 OOP개념을 알고 있으면 좀더 쉽게 코드를 구성할 수도 있다.

즉 필요한 class를 만들어서 내부에 행동, 동작, 특ㅈㅇ을 객체로 만들고 서로 서로 동작할수 있도록 하는 프로그래밍 방법이다. 

아마 python을사용하면 객체로 흘러가기 때문에 이해가 될것이다. 

 

이러한 특징들때문에 OOP를 사용하게 되어지면 코드의 재사용이 용이해질뿐만 아니라 유지보수에도 유용하고 제품화 코드처럼 큰 프로젝트에서도 효과적으로 사용할수 있다. 

하지만 장점이 있다면 단점도 존재하기 마련인데 단점으로써는 객체 디자인을 시간과 노력이 많이 들뿐만 아니라 함수로 되어있어서 시간도 많이 걸린다. 

 

여담이지만 코드가 커지다 보니 monorepo로 변경하는 프로젝트도 진행하였지만 시간과 노력이 상당히 많이 들어가고 눈에 띄는 결과가 없으니 힘들었다 ;;; 

 

이번시간에서는 Python에 대해서 아니라 C++관점에서 보고자 한다. 

참고한 자료는 다음과 같다.

reference 1: https://wikidocs.net/book/2380

 

C++ 이야기(A Story of C++)

이 페이지를 보고 있다면 [추천] 클릭! 좌측 메뉴 판넬 상단의 검색 기능을 이용하면 필요한 내용을 빠르게 탐색할 수 있다. 세상은 변한다. 그에 따라 지식도 변한다.…

wikidocs.net

 

reference 2: https://ocw.mit.edu/courses/6-096-introduction-to-c-january-iap-2011/270def7b1f68535b7c3846c606b220eb_MIT6_096IAP11_lec07.pdf

reference 3: https://en.wikipedia.org/wiki/Object-oriented_programming

 

Object-oriented programming - Wikipedia

From Wikipedia, the free encyclopedia Programming paradigm based on the concept of objects "Object-oriented" redirects here. For other meanings of object-oriented, see Object-orientation. Object-oriented programming (OOP) is a programming paradigm based on

en.wikipedia.org

 

OOP를 정리하자면 총 4가지의 중요한 개념이 있으며 Data Abstraction(추상화), Encapsulation(캡슐화), Polymorphism(다형성), Inheritance(상속성) 으로 구성이 되어있다. 

그렇다면 이 각각의 정의는 어떻게 되어지는지 살펴보도록 하자.

 

Data Abstraction(추상화)

* 어떠한 객체를 가질때 공통된 특징이나 기능들을 한곳에 이름을 모아서 작성하는것이다 . 예로들어서 우리가 동물을 추상화를 하고 싶다고 하였을떄 밥을 먹는 행위나 움직이는 것에 대해서는 함수로 기능을 구현할수 있으며 강아지라 하더라도 같은 종의 강아지에 따른 특징들을 변수로 붙여서 저장할수 있는데 이를 추상화라고 부른다. 

 

Encapsulation(캡슐화)

* 공통된 function, 기능들을 하나로 묶어서 캡슐에 부관하는 것이 캡슐화이다. 이를 하게 되어지면 재사용하기가 원할해지며 python으로 따지면 Self변수와 같은 것이다. 

 

Polymorphism(다형성)

* 여러 형태를 받아드릴수 있는 성질로써 동일한 함수명을 다른 의미를 지니도록 하는것 이다. 대표적으로 오버로딩, 오버라이딩이 있으며 

오버로딩의 경우에서는 같은 이름의 함수를 타입이나 갯수를 다르게 하여서 다시 재정의 하는것 

오버라이딩의 경우에서는 class를 상속받는것과 같으며 부모 class에 있는 method를 자식 class 재정의해서 확장하여 다른 기능을 수행할수 있도록 한다. 

 

Inheritance(상속성)

* 다형성보다 이전의 정의된 것으로 부모 class가 자식 class로 나누어서 객체를 좁혀가는 것이다. 그래서 부모 class에 가지고 있는 기능을 그대로 받아서 좀더 확장시킬수 도 있으며 일부 변형도 가능하다. 

우리가 아는 python은 이와같이 상속을 받아서 하는 경우가 많음으로 이 지식은 알아놓자.

 

다음에는 좀더 OOP에 대해서 알아보자

 

 

반응형

이전시간에서는 Dynmaic programming(DP)에 대해서 배워보았으니 문제를 풀어보는 시간에 대해서 가져보겠습니다.

DP문제를 풀기위한 Framework에 대해서 이는 왠만하게 모든 방법에서 풀수 있기때문에 단계별로 이해하면서 문제를 풀어보도록 하겠습니다.

아래그림처럼 처음 풀 문제는 Climbing Staris의 문제입니다. 이 문제는 top-down(recursive)로 풀수 있는데요

이 문제의 경우에서는 한글말로 "계단오르기"문제로써 간편하게 풀수 있는 문제입니다.

문제 : n step을 통해서 정상에 올라갈수 있는데 정상에 오르기 위해서는 몇가지의 방법을 통해서 올라갈수 있는지??
조건 : 한번 올라갈때마다 반듯이 1,2칸만 갈 수 있음.
예시 : n=3이라면 (1,1,1),(1,2),(2,1)로 총 3가지의 방법으로 올라갈수 있음.

https://leetcode.com/problems/climbing-stairs/description/

이 문제를 풀기 위해서는 우리가 이전에 공부하였던 DP를 활용하여 문제를 풀어 봅시다.

DP를 적용하기 위해서 state라는 개념이 있는데요.

state의 경우는 지금의 현제 상태에 대해서 설명할수 있는 변수의 집합이라고 됩니다. 위의 그림처럼 계단 오르기를 할때 step이 3이라고 하면 3가지의 방법들로 풀수 있는데요 (ex) n=3이라면 (1,1,1),(1,2),(2,1))

이때 집합들의 값들에 있는 변수들을 state variables라고 부릅니다.

이때 state variable에서 연관(relevant)이 있는 요소는 하나만 있는데 이 relevant 에 대해서는 다음에 설명하도록 하겠습니다 .

이러한 문제를 DP의 문제는 3가지의 조건을 반듯이 가지는데요 이러한 Framework에 대해서 알아보도록 하겠습니다.

Framework

  1. function이나 data structure들은 모든 state 마다 계산이 되어져야 합니다.
    위의 문제로 예시로 들어보았을떄 dp(i)를 function으로 잡았을때 이 dp(i)는 $i^{th}$번째에 방법들에 대해서 나와야 합니다.
  2.  점화식/재귀식 (recurrence relation)은 다른state에 이행되어진다.
    마찬가지로 위의 문제처럼 dp(30)으로 간다고 하였을떄 수많은 state variables이 집합으로 되어있을겁니다. 이떄 dp(30)은 이전의 29th과 28th에 도착되어졌을때 1,2의 계단만 올라가면 되는 문제로 풀수 있습니다.
    따라서 이론적으로 recurrence relation로 풀수 있으므로 dp(i) = dp(i-1) + dp(i-2)로 풀어집니다
  3. 초기 값들은 recurrence relation은 무한하게 되어지지 않도록 방지하는 역할을 합니다.
    dp(i) = dp(i-1) + dp(i-2)라는 수식 자체는 반복하다보면 negative값까지 가게 되어서 무한하게 수식이 되어집니다.
    그렇기 때문에 이를 방지할 수 있는 base가 되는 값을 가져야 하며 위의 문제에서는 dp(1)=1, dp(2)=2라는 초기 값을 설정이 되어짐을 볼수 있습니다.
    이를 통해서 negative로 가는 것을 막을수 있으며 위의값들을 풀어낼 수 있습니다.

Example Implementations

그렇다면 2가지의 문제로 접근해서 풀어보도로 하겠습니다. 

Top-down

class Solution: 
	def climbStaris(slef, n: int) -> int: 
    	def dp(i): 
        	if <= 2: 
            	return i 
                
            return dp(i-1) + dp(i-2)
    	return dp(n)

위의 방법은 $O(2^n)$의 방법으로 구성이 되어지지만 top-down 방식의 기본인 memoization이 없는 것을 볼 수 있습니다. 이 방법의 time complexity를 줄이기 위해서 memoization를 사용하게 되면 아래와 같습니다. 

class Solution:
    def climbStairs(self, n: int) -> int:
        def dp(i):
            if i <= 2: 
                return i
            if i not in memo:
                memo[i] = dp(i - 1) + dp(i - 2)
            
            return memo[i]
        
        memo = {}
        return dp(n)

이렇게 되면 $O(n)$로 줄어드는 것을 볼 수 있다. 

 

 

bottom-up 

이 방법을 적용해보자 

class Solution:
    def climbStairs(self, n: int) -> int:
        if n == 1:
            return 1
            
        # An array that represents the answer to the problem for a given state
        dp = [0] * (n + 1)
        dp[1] = 1 # Base cases
        dp[2] = 2 # Base cases
        
        for i in range(3, n + 1):
            dp[i] = dp[i - 1] + dp[i - 2] # Recurrence relation

        return dp[n]

 

 

위와 같은 방법으로 보았을떄 각자의 장단점이 있음으로 top-down, bootom-up의 방법을 적당히 사용하면된다. 

반응형

+ Recent posts