딥러닝 프레임워크 중 하나인 Chainer에 대해 리뷰를 해보려고 합니다.
Chainer 소개
Chainer는 <Preferred Networks>이라는 일본 벤처 기업이 개발한 딥러닝 프레임워크입니다.
특징
▒ "Define-and-run" vs "Define-by-run"
Chainer는 무엇보다도 다른 딥러닝 프레임워크와는 다르게 "Define-by-run" 체계를 기반으로 합니다.
Tensorflow, Caffe, Microsoft의 CNTK 모두 정적 그래프 프레임워크이며 "Define-and-run" 체계 기반입니다.
"Define-and-run"은 네트워크(또는 계산 그래프)가 정의되고 고정된 후 미니 배치가 고정된 네트워크에 공급되는 형식이고,
"Define-by-run"은 미니 배치가 공급되고 네트워크는 순방향 계산을 통해 동적으로 정의됩니다.
따라서 "Define-and-run"은 실행 시점에 동적으로 변경이 불가하며, "Define-by-run"은 가능합니다.
Chainer 말고도 Pytorch, ay/net 등의 프레임워크에서 "Define-by-run" 체계 기반을 사용 중입니다.
▒ 빠른 속도와 쉬운 구현
Chainer는 일반 Python 및 NumPy에서 선언형 API를 제공하기 때문에 표준 디버거 및 프로파일러를 사용하여
Chainer 기반 코드를 디버그하고 리팩터링하기 쉬워 복잡한 신경망을 구현하는 데 있어서 유연성을 제공하고,
빠르게 실행할 수 있다고 합니다.
Paper Translation
ABSTRACT
신경망을 위한 소프트웨어 프레임워크는 딥러닝 체계의 개발과 적용에 핵심적인 역할을 합니다.
이 논문에서는 연구자와 실무자에게 필요한 모든 범위의 딥 러닝 모델을 구현할 수 있는 Chainer 프레임워크를 소개합니다.
Chainer는 NumPy와 유사한 API인 CuPy를 통해 Graphics Processing Units를 사용한 가속화를 제공하고,
"Define-by-Run"체계를 통해 Python의 동적 모델을 지원하며, Computer vision model 및 distributed training을 위한 추가 패키지를 제공합니다.
KEYWORDS
Deep learning frameworks, GPU computing, Distributed training, Computer vision
1. INTRODUCTION
딥러닝은 인공지능 연구의 전성기를 이끌고 있습니다. 최근 조사에 따르면 딥러닝은 패턴 인식에서 초기 성공을 넘어 다양한 영역과 산업에서 새롭게 응용되고 있습니다. 이러한 연구 아이디어를 구현하기 위해서는 딥러닝을 위한 소프트웨어 프레임워크가 필요합니다.
신경망(Neural networks)을 구현하려면 다차원 배열, 활성화 기능, 자동 미분을 포함한 특수 구성 요소가 필요합니다.
이러한 도구들의 중복을 피하기 위해 많은 개발자들이 Caffe나 Torch와 같은 오픈소스 딥러닝 프레임워크를 사용합니다.
딥러닝이 컴퓨터 비전과 음성인식에서 처음으로 성공을 거뒀기 때문에 초기 딥러닝 프레임워크는 주로 합성곱 신경망(Convolutional neural network, CNN)과 같은 순방향 신경망을 위해 디자인됐습니다.
최근에는 딥러닝 모델의 새로운 타입이 주요 연구 과제가 되었습니다. 게임 플레이 결과에 따르면 심층강화학습은 연구 부분에서 유망합니다. 게다가 순환신경망(Recurrent neural networks, RNNs)이 텍스트와 같은 가변 길이 데이터에 대한 유망한 결과를 입증하고, 이 모델의 사용이 증가하였습니다. 장단기메모리(Long Short-Term Memorry, LSTM)를 가진 RNN은 현재 기계 번역 및 대화 모델에 성공적으로 사용되고 있습니다.
하지만 대부분 존재하는 딥러닝 프레임워크들은 이미지 처리를 위해 CNNs을 이용하는데
이러한 프레임워크들은 데이터 구조를 추상화하고, 일반적인 딥러닝 모델을 구현하기 위한 학습 모델 지원이 부족합니다. 게다가 현존하는 많은 프레임워크들은 딥러닝 모델을 표현하기 위해 도메인별 언어를 사용하고, 이를 메모리에 저장된 데이터 구조로 변환하는 인터프리터를 사용합니다. 따라서 이러한 프레임워크들을 사용하는 개발자들은 표준 프로그래밍 언어 디버거를 사용할 수 없습니다. 디버깅은 딥러닝 모델을 개발하고 조정하는 데 중요한 요소입니다.
복잡한 알고리즘 구현, 학습 모델, 모델 파라미터 조정을 위한 간단하고 효율적인 지원을 제공하는 딥러닝을 위한 오픈소스 프레임워크인 Chainer를 소개합니다. 논문의 나머지 부분은 다음과 같이 구성되어있습니다.
section 2: 기존의 딥러닝 프레임워크의 표준 아키텍쳐를 설명합니다.
section 3: Chainer의 아키텍쳐에 대해 설명합니다.
section 4: 메모리 사용 최적화 및 이중 역전파 기술과 같은 성능 퍼포먼스를 설명합니다.
section 5: CuPy와 같은 GPUs를 위한 백엔드 라이브러리를 제시합니다.
section 6: 분산 학습 기능을 설명합니다.
section 7: 컴퓨터 비전을 위한 애드온 패키지인 ChainerCV를 소개합니다.
section 8: 관련 작업을 제시합니다.
section 9: 요약과 향후 계획에 대한 설명을 제시합니다.
2 BACKGROUND AND MOTIVATION
일반적인 NN 프레임워크 모델은 "Define-and-Run"이라는 패러다임에 두 단계로 구축됩니다.
1. 정의 단계에서는 모델의 계산 그래프가 먼저 정의되고 구성됩니다. 이 단계는 계층 간 연결, 초기 가중치, 활성화 함수의 데이터 흐름 그래프를 지정하는 모델 정의에 기반된 신경망 객체의 인스턴스에 해당합니다. 자동 미분은 일반적으로 순방향과 역방향으로 계산되 고, 선택적 그래프 최적화에도 수행됩니다.
2. 실행단계에서는 그래프의 실제 순방향과 역방향 계산이 수행됩니다. 학습할 샘플 데이터를 넣어주면 모델은 확률적 경사 하강법 (Stochastic gradient descent)과 같은 최적화 알고리즘을 사용하여 손실 함수를 최소화함으로써 이 단계에서 학습됩니다.
"Define-and-Run" 패러다임에서는 CNN과 같은 정적 신경망 모델을 쉽게 구현할 수 있습니다. 모델 정의는 Protobuf, YAML과 같은 특정 마크업 언어로 작성될 수 있습니다. 딥러닝 프레임워크는 독립적인 신경망으로 볼 수 있는 모델 정의를 실행하는 인터프리터 같은 역할을 합니다. 신경망은 입력(데이터 예제)을 받고 처리(순방향, 역방향 계산)하고 모델의 내부 상태를 변경(업데이트)하고 결과를 출력(예측) 합니다.
전체 계산 그래프를 이용하여 메모리 효율성과 런타임 성능을 향상시키는 그래프 최적화를 사용할 수 있기 때문에 CNN과 같은 정적 모델에서 "Define-and-Run"이 잘 작동합니다. 하지만 다른 유형의 신경망 모델을 구현할 때 두 가지의 문제점이 발생합니다.
첫 번째 문제점은 흐름 제어가 있는 신경망을 지원하는 것이 다루기 힘들 수 있습니다. 텐서플로우와 같은 프레임워크에서 흐름제어 결정은 호스트 언어의 흐름제어 구문이 아닌 '스위치'와 '병합'과 같은 특수 연산자를 사용하여 데이터 흐름 그래프가 정의됩니다.
두 번째 문제점은 "Define-and-Run" 패러다임에서 신경망의 내부 메커니즘에 사용자가 접근할 수 없다는 것 입니다. 이건 효과적인 모델을 만드는 데 있어 몇 가지 어려움을 제시합니다. 예를 들면, 모델을 효과적으로 디버그하고 고치려면 사용자가 모델 내부에서 일어나는 과정을 볼 수 있어야 합니다. 하지만 단일 클래스의 큰 개체와 같은 계산 그래프에는 전체 모델의 정보, 구조, 가중치, 기울기, 노드 간 작업이 포함돼있어 Black box임을 암시합니다. (블랙박스는 내부 작동에 대한 지식 없이 입력 및 출력 측면에서 볼 수 있는 시스템입니다.)
결과적으로 프로파일러, 디버거 같은 개발 도구는 모델의 결함이나 개선점을 결정할 수 없습니다. 프레임워크에서 그래프 최적화를 수행하면 더 복잡해집니다.
3 DESIGN AND PROGRAMMING MODEL
이 섹션에서는 "Define-by-Run" 패러다임을 기반으로 하는 자동 차별화 APIs의 기본 설계에 대해 설명합니다.
3.1 On Demand Graph Construction
역전파는 입력 배열에 적용된 작업 내용을 기록하고 백트레킹함으로써 실행됩니다. "Define-by-Run" 패러다임에서 작업 내용은 정확한 입력 배열이 적용된 순방향 계산과 함께 동시적으로 기록됩니다. 이것은 각 변수와 연산에 대한 계산 그래프에서 노드를 생성함으로써 해결할 수 있습니다. 계산 그래프는 입력에 적용된 연산을 백트레킹하는 방법만 정의하고 순방향 계산을 정의하진 않습니다.
계산과 관련된 변수를 나타내는 '변수 노드'와 변수에 적용하는 연산을 나타내는 '함수 노드'라는 두 가지 타입의 노드로 계산 그래프를 정의합니다. 함수 f를 입력 변수 x에 대입하여 출력 변수 y를 구한 후, 함수 노드 n_f는 입력 노드 n_x에 대한 참조를 포함하고 출력노드 n_y는 함수 노드 n_f에 대한 참조를 포함합니다. 이러한 참조는 그래프를 백트레킹하는 데에 사용됩니다.
순방향 계산을 정의하는 프로그램은 어떤 기울기도 계산하지 않는 표준 수치 계산과 유사합니다. 유일한 차이점은 각각의 미분 가능한 함수는 출력을 계산하고 계산 내용을 그래프에 저장한다는 것 입니다. 그래프 구성은 실행 추적에만 의존하기 때문에 임의의 사용자 구문 구성(ex:조건부, 루프)과 결합할 수 있습니다. 이러한 프로그램은 정확한 기울기값들을 유지하면서 각각 다른 토폴로지와 크기를 가진 그래프를 만듭니다. 우리가 사용할 수 있는 사용자 언어의 힘은 그러한 원시적인 언어 구조에 국한되지 않습니다. 또한 디버거와 프로파일러와 같은 높은 수준의 도구를 활용할 수도 있습니다.
3.2 Object-Oriented Model Definition
합성성은 딥러닝의 중요한 특징입니다. 네트워크의 프래그먼트는 다양한 조합으로 연결돼있어 풍부한 아키텍처를 형성합니다. 심층 모델을 작성하기 위한 API는 구성요소를 유연하게 재사용하고 결합할 수 있는 합성성을 보여야 합니다.
"Define-by-Run"패러다임에서 모델은 <순방향 계산을 정의하는 코드>와 <동작을 결정하는 파라미터>로 구성됩니다.
코드는 사용자 언어 프로그램으로 작성되며 파라미터에 바인딩되어야 합니다.
이 파라미터 바인딩 문제는 객체지향프로그래밍으로 해결됩니다. 자체 파라미터를 포함하는 각 신경망의 프레그먼트는 클래스에 의해 정의됩니다. 이러한 프레그먼트들은 더 큰 모델 구성 요소를 만들기 위해 다른 클래스로 결합됩니다. 그러므로 신경망의 모듈화와 파라미터 바인딩이 해결됩니다.
다음 그림은 완전 연결 계층과 다층 퍼셉트론을 정의하는 예시입니다. 파라미터는 객체 생성 시 초기화되며 순방향 계산은 메서드로 작성됩니다.
이러한 객체지향 모델의 정의 스타일은 Chainer에 의해 2015년에 처음 소개됐으며, 현재는 Pytorch, Tensorflow Eager과 같은 "Define-by-run" 프레임워크에서 사용되고 있습니다.
4. TECHNICAL FEATURES
이 섹션에서는 "Define-by-Run" 패러다임을 기반으로 하는 프레임워크에 적용할 수 있는 단순성과 효율성을 향상시키는 Chainer의 몇 가지 기술에 대해 설명 합니다.
4.1 Memory-Efficient Backpropagation
모델과 데이터의 사이즈가 물리적 메모리 양에 의해 제한되기 때문에 메모리 효율성은 딥러닝 프레임워크에 있어 중요한 관심사입니다.
최적화는 특히 최대 메모리 사용량을 줄이는 데 있어 프레임워크 레벨에서 추가로 동작될 수 있습니다.
4.1.1 Global Memory Usage Reduction
"Define-by-Run" 패러다임을 기반으로 하는 딥러닝 프레임워크에서는 메모리 관리는 사용자 언어를 채택한다. Chainer는 표준 파이썬 구현(CPython)에서 메모리 관리를 위한 주요 메커니즘인 참조 횟수 계산 방식을 사용하는 쓰레기 수집(Garvage collection)에 의존합니다. 쓰레기 수집은 동적 할당된 메모리 영역 가운데 더 이상 사용할 수 없게 된 영역을 탐지하여 자동으로 해제하는 기법입니다.
"Define-by-Run" 패러다임에 기반한 자동 차별화 API는 참조 횟수 계산 방식의 GC 기법과 잘 작동합니다.
쓰레기 수집 (컴퓨터 과학) - 위키백과, 우리 모두의 백과사전
위키백과, 우리 모두의 백과사전. 쓰레기 수집(garbage collection 가비지 컬렉션[*], GC)은 메모리 관리 기법 중의 하나로, 프로그램이 동적으로 할당했던 메모리 영역 중에서 필요없게 된 영역을 해제
ko.wikipedia.org
참조 횟수 계산 방식 GC에 의해 메모리 영역 할당이 해제가 되면 각 노드가 위상 순서로 해제됩니다. 한편, 역전파 알고리즘은 위상 순서대로 노드를 방문합니다. 이 두 절차를 병합함으로써 역전파의 최대 메모리 소비를 최소화할 수 있습니다. 이는 그래프 백트레킹 중에 함수 노드에 대한 참조를 즉시 처리한 후 수동으로 제거함으로써 달성됩니다.
4.1.2 Function-wise Local Memory Usage Reduction
일반적으로 함수의 기울기(더 정확하게는, 야코비안 행렬)는 입력에 따라 좌우됩니다. 따라서 입력 배열과 출력 오류를 모두 인수로 받아들이도록 역방향 계산의 인터페이스가 설계된 것은 당연합니다. 하지만 이 인터페이스는 입력 배열이 기울기를 계산할 필요가 없는 작업에 메모리 사용 최적화를 적용할 수 없게 만듭니다. 예를 들어, tanh과 같은 일부 작업은 입력 배열 대신 출력 배열을 사용하여 기울기를 계산할 수 있습니다. 출력에 적용되는 다음 작업이 기울기 계산을 위한 입력을 필요로 할 때, 입력 배열을 제거하고 역전파를 위해 메모리에 유지되는 데이터를 최소화하도록 출력 배열을 유지할 수 있습니다. 또한 일부 작업은 입력과 출력 배열을 필요로 하지 않는데 이 경우 두 가지 모두에 대한 참조를 해제할 수 있습니다.
각각의 미분 가능한 연산은 재정의된 순방향과 역방향 메서드와 함께 함수 노드의 하위 클래스로 구현됩니다. 앞으로 역방향에 필요한 입력과 출력들은 retain_inputs과 retain_outputs 메서드 호출을 통해 명시적으로 선언됩니다. 입력과 출력이 이러한 선언에 의해 리스트화 되지 않았다면 해당 입력과 출력은 역전파에 저장되지 않습니다. 대신 역방향의 구현은 필요시 입력값을 가져옵니다.
Chainer는 더 이상 필요하지 않은 메모리를 최대한 빨리 해제하도록 설계된 특정 변수 객체 표현을 사용합니다. "Define-by-Run" 패러다임을 사용하여 자동 미분을 단순하게 구현하려면 각 변수 노드는 다차원 배열을 포함해야 합니다. 이러한 설계를 가진 문제는 배열로부터 사용되는 메모리가 삭제된 변수로의 마지막 참조까지 복구될 수 없습니다. 특히, 사용자 코드가 변수에 대한 어떠한 직접적인 참조도 보유하지 않더라도, 계산 그래프는 여전히 변수에 대한 참조를 보유할 수 있으며, 이 경우 메모리를 회수할 수 없습니다. 이 문제는 사용자 코드 참조를 내부 참조와 구별할 수 없기 때문에 발생합니다. 변수에 대한 사용자 코드 참조가 활성화된 상태라면 변수와 관련된 다차원 배열 데이터를 유지해야 한다는 점은 중요합니다. 한편, 계산 그래프 내부 참조는 항상 데이터가 활성화된 상태일 필요는 없습니다. 만약 어떠한 연산도 변수를 입력과 출력으로 유지하지 않는다면 데이터를 공개해야 합니다. 이러한 관찰을 바탕으로 사용자 코드의 변수와 계산 그래프의 변수를 나타내기 위해 별도의 객체를 사용하여 이 문제를 해결할 수 있습니다. Chainer에서 각 변수 객체는 배열 데이터를 보관하며 그래프의 변수 노드를 나타내는 해당 변수 노드 객체와 구별됩니다. 변수 노드 개체는 변수가 작업에 의해 유지되는 경우에만 배열 데이터에 대한 참조를 가지고있습니다. 이러한 공식을 통해 사용자 코드의 마지막 참조가 제거되면 작업에서 입력과 출력으로 유지하지 않는 한 해당 변수의 메모리를 즉시 해제할 수 있습니다.
4.2 Double Backpropagation
기울기 계산을 포함하는 역전파는 현대 딥러닝 프레임워크의 주요 특징입니다. 이것은 Hessian 벡터 곱에 대한 자동 미분과 같습니다. 이러한 기능을 이중 역전파라고 부르기도 합니다.
이중 역전파는 미분을 지원하는 함수를 사용하여 각 연산의 역방향 계산을 구현함으로써 지원됩니다. 이 아이디어는 간단하게 보일 수 있지만, 단순한 구현은 불필요한 메모리 소비를 유발하는 순환 참조가 발생될 수 있습니다. 순환 참조를 피하려면 두 가지 요인을 고려해보아야 합니다. 기울기 결과에 엑세스하기 위한 인터페이스와 각 미분 가능한 함수에서의 출력 유지 입니다.
4.2.1 Interface to Access the Resulting Gradients
역전파를 발생시키기 위한 두 가지 스타일의 인터페이스가 존재합니다.
첫 번째는 Variable.backward() 메소드 인데요. 각 입력에 대한 기울기를 계산합니다. 기울기 결과들은 가변 객체에 직접 저장이 됩니다.
두 번째는 grad()메소드 입니다. 입력 집합과 출력 집합을 모두 인수로 사용합니다. 이 경우 함수는 지정된 출력에 해당하는 계산된 기울기 집합을 반환합니다. 두 번째 메소드 같은 경우 객체 간의 추가 참조를 도입하지 않는 반면, 첫 번째 메소드는 입력 노드의 참조를 계산된 기울기에 추가할 수 있습니다. 계산된 기울기는 계산 그래프를 통해 간접적으로 입력 노드를 참조하기 때문에 순환 참조가 나타납니다.
4.1절에서 설명한 바와 같이 사용자 코드 참조와 노드 간 참조를 구분하는 것으로부터 이 순환 참조가 제거됩니다. 변수에서 해당 기울기에 대한 참조는 계산 그래프의 일부가 아니기 때문에, 우리는 이 참조를 변수 노드 개체 대신 변수 개체로 배치합니다.
4.2.2 Output Retention for Double Backpropagation
4.1절에서 자세히 설명한 바와 같이, 각 함수노드 구현의 역방향 방법은 순방향 계산에서 유지된다고 선언된 출력 변수를 사용할 수 있습니다. 역전파를 미분하려면 함수 노드가 출력 노드에 대한 참조를 유지할 수 없기 때문에 특별한 단계가 필요합니다. 그렇지 않으면 순환 참조가 발생합니다. 이는 함수 노드의 역방향 계산이 실행되기 전에 출력 노드가 해제될 수 있음을 뜻합니다. 이러한 출력 노드에 대한 그래프 구성을 다시 봄으로써 미분 가능한 역전파의 유효를 유지할 수 있습니다. 다시 말해, 새로운 노드 개체가 출력 노드처럼 역전파 중에 만들어지고 연결됩니다. 게다가 출력 배열 데이터를 함수 노드에 백업으로 저장하고 출력 노드 재생성에 사용 됩니다. 이것은 계산을 유효하게 만듭니다. 꺼내진 출력 노드는 다른 노드나 사용자 코드가 그것에 대해 참조하지 않는다는 것을 의미합니다. 따라서 출력 노드를 다시 생성해도 기존 노드와 충돌하지 않습니다.
5 GPU SUPPORT BY CUPY
심층 신경망의 일반적인 사용은 부동소수점 수치 계산을 위해 상당한 파워가 필요하므로, 딥러닝 프레임워크는 GPU와 같은 외부 가속기의 컴퓨팅 파워를 활용할 필요가 있습니다. 이는 심층 신경망 코드를 작성하는 사람들에게 유연성과 단순성, 구성 요소 확장 용이성을 유지하면서 고성능 GPU 프로그램을 구현하는 것은 쉬운 일이 아닙니다. CuPy는 NumPy와 문법이 호환되는 NVIDIA GPU의 연산 능력을 제공하는 파이썬용 오픈 소스 라이브러리 입니다. NVIDIA가 제공하는 CUDA 플랫폼(cuBLAS, cuDNN, cuRAND, cuSOLVER, cuSPARSE, and NCCL)과 함께 GPU 아키텍쳐를 완전히 활용하여 NumPy와 유사한 문법으로 기술된 모든 계산을 가속화 합니다.
CuPy의 인터페이스는 NumPy의 인터페이스와 잘 호환되며, 대부분의 경우 Drop-in replacement(대체하는 과정에서 코드 변환 없이 성능만 향상되는 것)로 사용될 수 있습니다. 표준 숫자 데이터 유형, 배열 인덱싱, 슬라이스, 전치, 모양 변경 및 브로드캐스트를 지원합니다.
사용자는 C++의 코드 스니펫을 이용해 코드를 더 빨리 실행하여 사용자 커스텀 CUDA 커널을 만들 수 있습니다. CuPy는 자동으로 코드를 랩핑하고 컴파일하여 CUDA 바이너리를 만듭니다. 컴파일된 이진 파일은 캐시되고 이후 실행에서 재사용됩니다.
CuPy는 Chainer의 백엔드로 처음 개발되었습니다. Chainer의 초기 버전은 CUDA GPU 계산을 위해 자주 사용이 되는 파이썬 라이브러리인 PyCUDDA를 사용하여 구현되었습니다. 그러나 PyCUDA는 딥러닝을 위한 NumPy의 충분한 기능을 지원할 수 없었고 CUDA 지원은 불충분 했습니다. CuPy는 2017년 6월 Chainer v2.0과 v1.0이 출시되면서 Chianer로부터 독립되었습니다. 이후 수 많은 비 딥러닝 프로젝트가 CuPy의 강력한 성능과 간단한 인터페이스를 활용했습니다. 예를 들면 파이썬 기반 확률적 모델링 소프트웨어인 Pomegranate와 자연어 처리 라이브러리인 sapCy는 CuPy를 GPU 벡엔드로 사용합니다.
5.1 CuPy Example
CuPy는 NumPy와 유사한 파이썬 패키지이기 때문에 파이썬 프로그램으로 비슷하게 가져올 수 있습니다. CuPy의 줄임말로 Cp를 사용하는데, 이는 위에 있는 Figure 3 코드에서 알 수 있듯이 NumPy과 비슷합니다. cupy.ndarray 클래스는 numpy.ndarray의 GPU 대안으로써 CuPy의 핵심 입니다. 코드를 보면 x는 cupy.ndarry의 매개변수 입니다. 이 Numpy가 CuPy로 대체됐다는 점을 제외하면 NumPy 구문과 동일 합니다. numpy.ndarray와 cupy.ndarray의 주된 차이점은 콘텐츠가 GPU 메모리에 할당된다는 것 입니다. 대부분의 CuPy 배열 조작은 NumPy의 조작과 유사합니다. 예를 들면 NumPy는 유클리드 노름을 계산하기 위해 numpy.linalg.norm을 사용하는 반면, CuPy는 GPU에서 이를 계산하기 위해 cupy.linalg.norm을 사용합니다.
5.2 Supported Functionalities
이전 섹션에서 설명한 것처럼 CuPy는 cupy.ndarray에 많은 기능이 구현됩니다.
5.2.1 Linear Algebra
CuPy는 고유값 분해, 숄레스키 분해, QR 분해, 특이값 분해, 선형 방정식 처리기, 행렬의 역행렬 그리고 무어-펜로즈 유사역행렬과 같은 NumPy에서 대부분의 선형 대수 함수를 지원합니다. 이러한 함수는 cupy.linalg에서 정의되며 numpy.linalg와 호환됩니다. 모두 GPU에서 작동하는 LA-PACK 구현체인 cuSOLVER가 지원 됩니다.
5.2.2 Sparse Matrices
Cupy는 cuSPARSE를 이용한 희소 행렬을 지원합니다. 이러한 행렬은 Scipy 안에 있는 scipy.sparse의 희소 행렬의 동일한 인터페이스가 포함되어 있습니다. 요구사항에 따라 사용자는 좌표 형식의 희소 행렬, 압축 희소 행 행렬, 압축 희소 열 행렬, 대각선 정보가 저장돼있는 희소 행렬 중에서 선택할 수 있습니다.
5.2.3 Sorting
CuPy는 CUDA와 함께 C++ㄹ 작성된 병렬 알고리즘 라이브러리인 Thrust의 지원을 받아 NumPy와 호환되는 sort, argsort, lexsort 기능을 제공합니다. CuPy는 GPU를 위한 정교한 병렬 정렬 알고리즘을 구현하는 Thrust의 장점을 활용합니다.
5.3 Custom CUDA kernels
CuPy는 연산자, 두 가지 유형의 커널, 제네릭 타입을 결합하여 사용자 정의 커널로 쉽게 확장할 수 있습니다. CUDA 코드 조각으로 GPU에서 임의의 커널을 구성하고 실행하는 것은 쉽습니다.
element-wise 커널은 모든 요소에 동일한 작업을 적용합니다. 예를 들면 cupy.add 함수는 각 요소 쌍에 대해 + 연산자를 적용합니다. reduction 커널은 이항 연산자로부터 모든 요소들을 압축시킵니다. element-wise 커널과 reduction 커널은 MapReduce 라는 프레임워크의 Map과 Reduce와 유사합니다. 위에 있는 Figure 4는 사용자 정의 element-wise 커널의 예 입니다. 첫 번째 및 두 번째 인수는 각각 입력 변수 목록과 출력 변수 목록으로 구성 됩니다. 각 변수의 정의는 형식 지정자와 인수 이름으로 구성됩니다. 세 번째 인수는 사용자가 정의하고자 하는 CUDA 코드의 스니펫 입니다. 코드 스니펫에서 임의의 CUDA 코드를 사용할 수 있습니다.
CuPy는 일반적인 유형도 지원합니다. float32와 같은 구체적인 타입 대신 T와 같은 타입 매개변수를 지정하면 커스텀 커널이 템플릿 함수로 생성됩니다. 제네릭 타입의 인수는 임의의 배열 타입을 허용합니다. 예를 들어, 입력 타입이 'T x, T y'로 지정이 되면, 이 함수는 정수 및 소수와 같은 임의 데이터 유형이 동일한 배열 쌍을 취합니다.
6 DISTRIBUTED PARALLEL TRAINING
이 섹션에서는 예전에는 ChainerMN이라고 불렸던 Chainer의 분산된 학습 능력 구성을 소개합니다.
GPU 성능이 지속적으로 향상됨에도 불구하고 최신 GPU로도 학습 과정은 시간이 많이 소요 됩니다. 예를 들어, ImageNet 데이터셋에 대한 ResNet-50 학습은 일반적으로 단일 GPU로 1주일이 걸립니다. Chainer의 분산 기능은 "Define-by-Run" 접근 방식으로 활성화된 Chainer의 유연성을 유지하면서 하드웨어 성능을 완전히 활용하는 여러 GPU의 파워를 통합할 수 있습니다. 이를 통해 동적 신경망, 생산적 적대 신경망, 심층 강화 학습과 같은 복잡한 사용 사례에서도 쉽게 분산 학습이 가능합니다.
6.1 Basics of Distributed Deep Learning
6.1.1 Data and Model Parallelism
분산 처리에 의한 학습을 병렬화하기 위한 두 가지 주요 접근 방식, 즉 데이터 병렬화와 모델 병렬화가 가능합니다. 데이터 병렬 처리에서 각 작업자는 모델 복제본을 가지고 있으며 서로 다른 미니배치의 기울기를 계산합니다. 작업자는 이러한 기울기를 공동으로 사용하여 모델을 업데이트 합니다. 각 작업자가 처리한 배치 크기를 b로 정의하고 작업자 수를 n으로 정의하면 커뮤니케이션을 통해 얻은 기울기는 배치 크기 bn과 같습니다. 한 번의 반복에서 더 많은 학습 데이터로 더 많은 작업자 기울기가 계산되면 기울기 품질이 향상되고 학습 과정도 빨라집니다.
모델 병렬화에서 각 작업자는 모델의 일부를 가지며, 다른 작업자와 협력하여 하나의 미니배치를 계산합니다. 모델 병렬화는 특히 GPU 메모리가 작을 때 적극적으로 사용됐습니다. 현재 데이터 병렬화가 더 효율적인 것으로 나타났지만, 자연어 처리 영역과 같은 모델이 엄청난 수의 매개 변수를 갖는 경우엔 데이터 병렬화와 함께 모델 병렬화가 채택됩니다.
6.1.2 Synchronous vs. Asynchronous
동기식 또는 비동기식 두 가지 옵션에서 커뮤니케이션 모델에 대한 디자인 선택은 전체 병렬 계산을 구성하는 핵심 요소 입니다. 두 모델 모두 데이터 병렬화에 중점을 둔 내용이 아래에 나와있습니다.
분산 학습에서 동기 데이터 병렬화는 순방향 계산, 역방향 계산, 최적화로 구성된 비병렬 학습 시퀀스인 것에 비해 all-reduce라는 추가 단계가 있다. All-reduce는 매개 변수의 합이 계산되고 모든 프로세스에 분산되는 병렬 컴퓨팅 연산 입니다. 이것은 MPI의 표준 기능 입니다. all-reduce 커뮤니케이션 추가 단계에서는 작업자가 서로 커뮤니케이션하여 개별 작업자가 계산한 기울기 합계를 구하여 배포합니다. 각 작업자는 합계를 복제본 수로 나누어 기울기의 평균을 계산하고 최적화 전에 all-reduce 커뮤니케이션을 통해 얻어진 기울기로 모델의 자체 복제본을 업데이트 합니다.
한편, 비동기 모델은 매개 변수 서버라고 불리는 특수 작업자를 가지고 있습니다. 매개 변수 서버는 학습 프로세스 중에 모델 매개 변수를 소유하고 제어 합니다. 일반 작업자는 순방향과 역방향 계산을 통해 기울기를 얻으면 파라미터 서버로 기울기를 보냅니다. 매개 변수 서버는 기울기를 수신하고 사용하여 모델을 업데이트 합니다. 작업자는 새 모델 매개변수를 수신하고 새 기울기 계산을 시작합니다.
6.2 Parallelism Design
Chainer는 데이터 병렬화와 동기 커뮤니케이션 모델을 사용합니다. 다음은 섹션 6.1에서 언급된 옵션 중 선택 사항에 대해 설명합니다.
데이터 병렬화를 위해서는 데이터셋을 분할하고 작업자 간의 기울기 평균을 계산하는 등 기존 구현에 대한 몇 가지 추가 사항이 필요합니다. 데이터 병렬화는 이미지 인식과 같은 많은 케이스 안에 미니배치 크기를 늘리는 것과 마찬가지이기 때문입니다. 따라서 우리는 처음에 데이터 병렬화를 선택했지만 나중에 모델 병렬화에 대한 시험도 추가했습니다. 또한, 우리는 결정론적 행동과 수렴을 위해 동기 커뮤니케이션 모델을 선택했습니다.
동기 데이터 병렬 기울기 교환은 작업자 간의 all-reduce 커뮤니케이션에 의해 실행될 수 있으며, Chainer는 Chainer의 커뮤니케이션 추상화와 all-reduce 연산을 지원하는 모든 커뮤니케이션 라이브러리에서 실행될 수 있습니다. Chainer가 지원하는 첫 번째 라이브러리는 OpenMPI로, 특히 CUDA 인식 빌드에 효율적입니다. 하지만 all-reduce 커뮤니케이션은 모든 반복 학습에서 호출되고 많은 양의 데이터를 처리해야 하기 때문에 효율이 특히나 필요합니다. NVDIA가 개발한 NCCL을 기본 라이브러리를 채택하여 all-reduce를 실행했습니다. NCCL은 노드 내, 노드 간 NVIDIA GPU 사이에서 효율적인 all-reduce 작업을 가능하게 하는 고도로 최적화된 커뮤니케이션 라이브러리 입니다.
6.3 API Design
Chainer의 분산 학습 기능의 디자인 목표에 대해 설명하고, 이어서 분산 학습을 지원하기 위해 Chainer로 작성된 기존 딥러닝 프로그램을 확장하는 최소 단계에 대해 설명합니다.
Chainer의 "Define-by-Run" 디자인의 유연성이 분산 실행을 위해 희생되어서는 안됩니다. "Define-by-Run"는 작업자 간에 all-reduce를 위한 단일 반복에서만 동일하다고 가정할 때 반복 간에 다를 수 있는 모델 구조를 허용합니다. 기울기 교환을 위한 커뮤니케이션은 최적화 단계 직전에 이루어지며, 이는 다른 Chainer 구성과는 상관이 없습니다. 이러한 최소 가정 범위 내에서 최적화 단계 전후에 모든 코드를 넣을 수 있으며, 모델 구조는 모든 반복에서 동적으로 변경될 수 있습니다.
Figure 5는 분산 모드의 세 가지 주요 추가 요인을 포함하여 MNIST 분류 모델을 훈련시키는 프로그램의 핵심 부분을 보여줍니다.
(1) 프로세스 간 모든 커뮤니케이션을 제어하는 커뮤니케이터 구성
(2) 작업자 간에 기울기를 교환하기 위해 optimizer을 multi_node_optimizer로 변환
(3) 데이터셋을 모든 작업자에게 분배
multi_node_optimizer는 Chainer를 분배하는 데 가장 중요한 구성 입니다. 모델을 최적화하기 전에 일반 최적화 도구를 포장하고 all-reduce 연산을 사용하여 프로세스 간에 기울기를 교환합니다. 커뮤니케이션을 제외하고 원래 최적화 도구와 동일하게 동작합니다. 마지막 단계에서 분배된 데이터셋은 작업자가 데이터 병렬화를 위해 어떤 훈련 데이터를 읽어야 하는 지에 대한 합의를 하도록 합니다. 학습 데이터셋은 동일한 조각으로 분할되어 작업자 프로세스에 분배됩니다.
Chainer는 분산 모드로의 포팅에 가능한 최소한의 변경을 요구함으로써 "Define-by-Run" 패러다임이 제공하는 유연성을 유지할 수 있었습니다.
6.4 Evaluation
우리는 ImageNet 데이터셋에 대한 90-epoch ResNet-50 학습을 벤치마크로 사용했습니다. 이 작업은 분산 딥러닝의 성능을 평가하는 데 광범위하게 사용되었습니다.
우리는 각각 8개의 NVDIA Tesla P100 GPU가 장착된 128개 노드의 클러스터를 사용했습니다. 작업자당 미니배치 크기는 32였고 총 미니배치 크기는 32k였으며 작업자는 1024개였다. 실험에 대한 자세한 내용은 부록에 나와 있습니다.
우리는 각각 8개의 NVIDIA Tesla P100 GPU가 장착된 128개 노드의 클러스터를 사용했다. 작업자당 미니 배치 크기는 32였고 총 미니 배치 크기는 32k였으며 작업자는 1024명이었다. 실험 설정에 대한 자세한 내용은 부록에 나와 있습니다.
Figure 6는 커뮤니케이션 시간(i.e., all-reduce 작업)과 평균 100개 이상의 다양한 GPU 수에 대한 전체 반복(i.e., 순방향과 역방향 계산, 커뮤니케이션, 최적화)을 완료한 시간을 보여줍니다. 1024개의 GPU를 사용할 때의 확장 효율은 단일 GPU와 단일 노드(i.e., 8개의 GPU) 기준에 비해 각각 70%, 80% 였습니다. 1024개의 GPU를 사용하여, 각 epoch 이후의 검증을 포함하여 90 epoch 동안 5번의 독립 실행 위에 평균 학습 시간은 897.9±3.3초였습니다.
7 CHAINERCV
이번 섹션에서는 컴퓨터 비전 작업을 위한 add-on 패키지인 ChainerCV를 소개합니다.
Chainer의 강력한 기능이 있음에도 불구하고 Chainer가 지원하는 것과 컴퓨터 비전에서의 딥러닝이 요구하는 것 사이에는 여전히 갭이 존재합니다. 딥러닝 모델은 점점 더 강해지고 복잡해졌습니다. 따라서 연구자와 엔지니어가 이러한 알고리즘들을 맨 처음부터 구현하는 것은 어려울 것 입니다. 게다가 Chainer의 범위를 벗어나는 데이터 로더와 비최대 억제와 같은 전/후처리 기능 같은 많은 컴퓨터 비전 유틸리티가 존재합니다.
ChainerCV는 비전문가가 아이디어의 신속한 시제품화와 필드의 벽을 허무는 것을 가능하게 하도록 목표했습니다. 라이브러리는 이미지 분류, 객체 감지, 영상 분할, 인스턴스 분할과 같은 다양한 컴퓨터 비전 작업을 위한 최신 모델, 사전 학습된 가중치 및 학습 스크립트를 제공합니다. 또한 라이브러리는 통합 API를 통한 데이터 로더와 평가 메트릭과 같은 유틸리티를 제공합니다.
우리의 디자인은 사용하기 쉬운, 통합 API, 재현성이라는 세 가지 원칙을 기반으로 합니다.
7.1 Easy to use
ChainerCV는 이미지 분류, 객체 감지, 영상 분할, 인스턴스 분할라는 네 가지 작업을 지원합니다. 각 작업에 대해 여러 개의 신경망이 구현될 수 있습니다. 예를 들면 객체 감지를 위해 7개의 구현이 포함됩니다.
ChainerCV의 구현을 포함한 인터페이스 동작은 동일한 작업을 위해 준비된 모델 간에 공유된 간단한 인터페이스 덕분에 사용하기 쉽습니다. 동일한 작업을 해결하는 모델의 경우에도 신경망의 출력 타입과 출력이 어떻게 후처리되는 지에 따라 다릅니다. 추론 인터페이스는 다른 모델 간의 근본적인 구현 차이를 숨깁니다. 또한 모델 객체가 인스턴스화 될 때 사전 학습된 가중치를 자동으로 다운로드하여 추론 프로세스를 더욱 단순화 시킵니다. 사전 학습된 가중치를 사용하면 Figure 7과 같이 두 줄의 파이썬 코드로 추론을 구현할 수 있습니다.
게다가 사용하기 쉬운 인터페이스를 지원하는 ChainerCV는 모델의 하위 집합에 대해 쉽게 사용자 지정을 할 수 있는 학습 스크립트를 지원합니다. 학습 스크립트는 Chainer의 학습 추상화를 사용하여 작성되므로 학습 구성을 쉽게 스왑할 수 있습니다. 예를 들어 스크립트는 사용자 커스텀 데이터 셋으로 훈련하도록 사용자에 의해 설계되었습니다.
ChainerCV는 사용하기 쉬운 추론을 지원하는 것 외에도 모델의 하위 집합에 대해 쉽게 사용자 지정할 수 있는 교육 스크립트를 지원한다. 교육 스크립트는 Chainer의 교육 추상화를 사용하여 작성되므로 교육 구성 요소를 쉽게 교환할 수 있습니다. 스크립트는 예를 들어 사용자 지정 사용자 데이터 세트로 훈련하도록 사용자에 의해 확장되도록 설계되었습니다.
7.2 Unified API
ChainerCV는 사용자가 다양한 방법으로 구현할 수 있도록 통합 API로 모듈 디자인을 강조합니다. 구현에는 신경망 모델, 데이터 로더, 평가 메트릭, 시각화 유틸리티가 포함됩니다. API는 라이브러리 전체에서 동일한 데이터 표현을 사용하여 일관되게 만들어집니다. 예를 들어, 이미지, 바운딩 박스, 의미 픽셀 단위 레이블, 인스턴스 마스크, 키 포인트에 대한 데이터 표현을 정의합니다. 게다가 API는 유사한 함수에서 일관됩니다. 예를 들어, 이전에 언급했듯이 추론 방법은 항상 모든 모델에 대한 입력으로 이미지 반복을 합니다.
인터페이스를 직접 렌더링할 수 있으며, 통합 API를 사용하면 인터페이스 안에 유틸리티를 구축할 수 있습니다. 예를 들어, 우리는 평가 루프를 추상화하는 구현을 제공합니다. 자체적으로 이 추상화는 데이터셋을 통해 반복되고, 이미지로부터 예측을 수행하고, 실측 정보와 예측을 사용하여 평가 지표를 계산합니다. 주목할 점은 추상 유틸리티가 데이터 로더, 추론 방법 및 평가 메트릭 간에 데이터를 전달할 수 있도록 데이터 로더 및 추론 방법에 대해 인터페이스가 가정되어있어야 한다는 점입니다. Figure 8에 나와있습니다.
7.3 Reproducibility
기계 학습과 컴퓨터 비전의 재현성은 연구 품질에 영향을 미치는 중요한 요소입니다. ChainerCV는 발표된 결과와 동등한 성능을 보장받는 학습 코드를 제공하여 발표된 결과를 재생산하는 과정을 용이하게 하는 것을 목표로 합니다. 이러한 알고리즘은 개선을 통해 새로운 아이디어를 얻는 기준선과 새로운 접근 방식을 기존 접근 방식과 비교하는 툴의 역할을 할 수 있습니다. 부주의한 구현으로 학습된 모델 성능은 기존 로직과 하이퍼파라미터로부터 벗어나기 때문에 쉽게 바뀔 수 있습니다. 이러한 타입의 실수는 연구자가 경쟁력있는 결과를 얻고 아이디어의 영향을 제대로 평가할 수 없기 때문에 유용한 기준선으로 구현을 무효시킵니다. Table 1는 ChainerCV에서 지원되는 모델과 실험 결과를 보여줍니다. 참조 스코어 또한 나타나며, 이는 원본 논문과 저자의 구현에 의해 보고 됩니다. 보이듯, 우리의 재실행 성과는 원본의 성과에 가깝습니다. 훈련 중 포함된 무작위성이 스코어 차이의 원인이 될 수 있다는 점은 주목할만합니다.
8 RELATED WORK
우리가 알기론, Autograd(미분 자동화)는 Chainer에 의해 제안되기 전에 역방향 그래프를 구성하기 위해 "Define-by-Run" 패러다임을 채택했습니다. Autograd는 NumPy를 기반으로 하는 라이브러리로 사용자가 NumPy를 사용하여 파이썬 코드로 미분 가능한 계산 그래프를 작성할 수 있도록 설계되었습니다. 그러나 딥러닝 프레임워크로 만들어진 것은 아니므로 딥 모델 학습에 필요한 GPU 가속을 지원하지 않습니다. 따라서 Chainer는 "Define-by-Run" 패러다임을 가진 딥러닝 워크로드에 초점을 맞춘 첫 번째 프레임워크입니다. 현재 "Define-by-Run"을 채택하는 다른 몇 가지 딥러닝 프레임워크가 존재합니다. PyTorch는 Chainer2에서 영감을 받은 인기있는 "Define-by-Run" 프레임워크이며, 이어서 Tensorflow는 "Define-by-Run" 모델 정의를 지원하는 "eager mode"라는 기능을 도입했습니다. MXNet은 느긋한 계산법에 의해 선언형 기호 표현과 결합할 수 있는 선언형 텐서 계산을 지원합니다. PaddlePaddle과 CNTK 모두 선언형 스타일을 지원하는 동시에, 선택적 프로그래밍 스타일로 선언형 스타일을 포함합니다. 선택적으로 "Define-by-Run"을 지원하는 이러한 프레임워크의 대부분과 달리 Chainer는 전체 구현과 API에서 "Define-by-Run"에 매우 최적화 돼있습니다. 이로 인해 코드베이스가 단순해져 신규 개발자가 기여하는 비용을 줄일 수 있습니다.
DistBelief는 먼저 심층 신경망, 데이터와 모델의 병렬화, 비동기 SGD와 마스터-작업자의 이질적인 모델이라는 분산 학습에서 이러한 세 가지 중요한 기술을 통합했습니다. 그 후, 이러한 기술은 Tensorflow, MXNet, Paddle에서 사용할 수 있게 되었습니다. 그러나 비동기 SGD는 정확도에 영향을 미치는 오래된 기울기와 파라미터 서버의 병목 현상을 피할 수 없습니다. 이러한 문제를 완화하기 위해선 최신 버전에 동기식 SGD를 실행하는 옵션이 추가되었습니다.
한편, Caffe2와 PyTorch는 동기식 SGD를 사용합니다. 동기화 비용을 보상하기 위해, Caffe2와 PyTorch는 레이어의 역방향 계산이 완료되는 즉시 all-reduce를 시작하여 계산을 통한 all-reduce 커뮤니케이션의 임계 경로를 최소화했습니다.
컴퓨터 비전에서의 오픈 소스 모델은 광범위한 실험이지만 ChainerCV와 유사한 철학을 추구한 몇 가지 연구를 발견했습니다. 주요 경쟁업체에는 논문과 함께 제공되는 재검색 프로그램 코드가 포함됩니다. 그들의 주된 목적은 검증 가능한 방식으로 연구 결과를 공유하는 것이기 때문에 가독성과 모듈성은 종종 무시되는 경우가 있습니다.
ChainerCV와 더 밀접한 관련이 있는 라이브러리는 PyTorch/vision과 GluonCV 입니다. PyTorch/vision은 PyTorch를 백엔드로 사용하는 컴퓨터 비전 라이브러리입니다. 작성 시 사전 훈련된 모델에 대한 지원은 이미지 분류로만 제한됩니다. GluonCV는 또 다른 딥러닝 프레임워크인 Gluon을 백엔드로 사용하는 최근 출시된 컴퓨터 비전 라이브러리입니다. ChainerCV와 유사하게 GluonCV는 객체 감지, 영상 분할 및 인스턴스 분할을 지원합니다. 하지만 그들은 재현성을 핵심 목표로 추구하지 않습니다.
9 CONCLUSION
본 논문에서는 사용자가 새로운 알고리즘과 복잡한 신경망을 쉽게 구현할 수 있는 딥러닝 프레임워크인 Chainer를 소개했습니다. Chainer는 심층 강화 학습, word2vec 분산 표현, 반복 신경망 언어 모델, 인간 포즈 예측, variational auto-encoders를 포함한 다양한 첨단 응용 분야에서 이미 성공적으로 사용되었습니다. 전 세계 헌신적인 개발자와 사용자들이 Chainer를 개선하기 위해 GitHub에서 적극적으로 협업하고 있기 때문에 Chainer가 앞으로 더욱 다재다능하고 유용하게 사용될 것으로 기대합니다. 특히 CuPy를 개선하여 얻은 성능 향상을 통해 사용자는 CPU/GPU에 구애받지 않는 코드로 다양한 유형의 딥러닝 모델을 적용할 수 있습니다. 우리는 딥러닝 커뮤니티의 모든 구성원에게 Chainer를 테스트하고 개발에 기여하도록 초대합니다.
이상 Chainer에 관한 리뷰&번역을 마칩니다.
감사합니다.
사진 출처
https://www.oreilly.com/content/complex-neural-networks-made-easy-by-chainer/
참고 자료
https://www.slideshare.net/pfi/introduction-to-chainer-11-may2018-96768990?from_action=save
https://quintagroup.com/cms/python/chainer
'Paper > Deep Learning' 카테고리의 다른 글
합성곱/풀링 계층 구현하기 (0) | 2023.07.12 |
---|---|
[리뷰]Densecap: Fully Convolutional Localization Networks for Dense Captioning 번역 (1) | 2022.10.11 |