Source-linked AI summary
PyTorch: An Imperative Style, High-Performance Deep Learning Library
Adam Paszke, Sam Gross, Francisco Massa, Adam Lerer, James Bradbury, Gregory Chanan, Trevor Killeen, Zeming Lin, Natalia Gimelshein, Luca Antiga, Alban Desmaison, Andreas Köpf, Edward Yang, Zach DeVito, Martin Raison, Alykhan Tejani, Sasank Chilamkurthy, Benoit Steiner, Lu Fang, Junjie Bai, Soumith Chintala
TL;DR
Deep learning framework은 성능을 위해 사용성과 유연성을 희생하는 경우가 많았다. PyTorch는 automatic differentiation과 GPU acceleration을 지원하는 imperative·Pythonic 실행을 사용해, 신중한 성능 고려와 사용성을 결합하면서 가장 빠른 framework 대비 17% 이내의 benchmark 성능을 달성한다.
문제
Static dataflow graph는 계산을 미리 파악할 수 있게 하지만, 사용 편의성, debugging, 계산 유연성을 제한한다.
방법
PyTorch는 유연한 실험을 위해 설계된 즉시 실행 dynamic tensor, automatic differentiation, GPU acceleration, Pythonic interface를 결합한다.
결과
17%: PyTorch의 성능은 평가한 모든 benchmark에서 가장 빠른 framework 대비 17% 이내였다.
시사점 및 한계
PyTorch는 사용성과 성능을 결합했으며 deep learning 연구 커뮤니티에서 널리 사용되는 도구가 되었다.
시사점 및 한계
stream당 one-pool-per-stream allocator 설계는 특정 corner case에 취약하지만, 실제 code에서 원치 않는 동작은 드물다.
Abstract
from arXiv · showhide
Deep learning frameworks have often focused on either usability or speed, but not both. PyTorch is a machine learning library that shows that these two goals are in fact compatible: it provides an imperative and Pythonic programming style that supports code as a model, makes debugging easy and is consistent with other popular scientific computing libraries, while remaining efficient and supporting hardware accelerators such as GPUs. In this paper, we detail the principles that drove the implementation of PyTorch and how they are reflected in its architecture. We emphasize that every aspect of PyTorch is a regular Python program under the full control of its user. We also explain how the careful and pragmatic implementation of the key components of its runtime enables them to work together to achieve compelling performance. We demonstrate the efficiency of individual subsystems, as well as the overall speed of PyTorch on several common benchmarks.
1 서론
PyTorch는 Pythonic 동적 eager execution을 automatic differentiation, GPU acceleration, 그리고 주요 library에 필적하는 성능과 결합해 deep learning의 사용성과 성능 사이의 긴장을 해소한다. 속도를 희생하지 않고 즉각적인 동적 tensor computation을 실용화하도록 설계된 Python library로 소개된다.
- 동기: Caffe [1], CNTK, TensorFlow, Theano 와 같은 framework는 반복적인 batch execution을 위해 computation을 나타내는 static dataflow graphs를 구성한다.Static graphs는 전체 computation을 사전에 파악할 수 있게 하며, 이론적으로 성능과 확장성을 개선할 수 있다.
- 관련 연구: 기존 dynamic eager-execution framework는 Chainer 처럼 성능 비용을 초래하거나, Torch 와 DyNet [7]처럼 표현력은 낮지만 더 빠른 language를 사용했다.이러한 절충으로 인해 기존 define-by-run 접근법의 적용 범위가 제한되었다.
- 기여: PyTorch는 automatic differentiation과 GPU acceleration을 활용해 dynamic tensor computation을 즉시 실행하면서도, 현재 가장 빠른 deep-learning library에 필적하는 성능을 유지한다.이 논문은 이러한 결합이 세심한 구현과 설계 선택에서 비롯된다고 설명한다.
2 배경
Deep learning은 네 가지 scientific-computing 흐름, 즉 array-based programming, automatic differentiation, 개방형 Python 생태계, massively parallel hardware를 기반으로 발전했다. PyTorch는 GPU-가속 arrays, automatic differentiation, Python 통합을 통해 이 흐름들을 결합한다.
- APL, MATLAB, R [10], Julia, NumPy [12], Torch [6], Eigen [13], Lush [14]를 비롯한 domain-specific languages와 libraries는 tensors와 array-based programming을 핵심 도구로 정립했다.
- Automatic differentiation은 derivative computation을 자동화해 machine-learning approaches를 더 쉽게 실험하면서도 효율적인 gradient-based optimization을 유지하게 했으며, autograd 는 이를 NumPy arrays에 널리 확산시켰다.
- NumPy, SciPy, Pandas 를 포함한 open-source Python ecosystem은 연구자들의 numerical-analysis 요구를 충족하는 동시에, 더 폭넓은 scientific workflows를 위한 interoperable libraries를 제공했다.
- Commodity GPUs와 cuDNN [22] 같은 재사용 가능한 high-performance kernels는 Caffe [1], Torch7, TensorFlow 를 비롯한 frameworks가 accelerators를 사용할 수 있게 한 computing power를 제공했다.
- PyTorch는 GPUs로 가속되는 array-based programming model을 제공하며, Python에 통합된 automatic differentiation을 통해 이를 differentiable하게 만든다.
3 설계 원칙
PyTorch의 설계는 Pythonic 인터페이스, 연구자 중심의 사용성, 실용적 성능, 단순한 내부 구현이라는 네 가지 원칙을 통해 속도와 사용 편의성의 균형을 이룬다.
- 설계 원칙: 이 원칙들은 함께 기존 아이디어를 하나의 설계로 엮어 속도와 사용 편의성의 균형을 이룬다.이 원칙들은 PyTorch의 전반적인 구현 선택을 이끈다.
- Pythonic하게 설계하기: PyTorch는 단순하고 일관된 인터페이스를 사용하며 plotting, debugging, data-processing 도구와 자연스럽게 통합되는 Pythonic 라이브러리다.목표는 PyTorch를 Python 생태계의 일급 구성원으로 만들고, 작업을 수행하는 관용적인 방식을 하나로 정립하는 것이다.
- 연구자를 우선하기: PyTorch는 직관적이고 side effect가 없는 API 뒤에 machine-learning의 복잡성을 감춰 모델, data loader, optimizer를 쉽고 생산적으로 작성할 수 있게 한다.이 연구자 우선 설계는 일반적인 작업 흐름을 단순하게 유지하면서 예기치 않은 성능 저하를 피하는 것을 목표로 한다.
- 실용적 성능 제공하기: PyTorch는 단순성을 희생하지 않으면서 탁월한 성능을 제공하기 위해 구현 복잡성을 감수하고, 연구자에게 실행을 제어하고 성능을 개선할 수 있는 도구를 제공한다.이 설계에서는 훨씬 단순한 사용을 위해 10%의 속도 절충은 허용되지만, 100%의 절충은 허용되지 않는다고 본다.
- 더 나쁜 것이 더 낫다: PyTorch는 엔지니어링 역량과 유지보수성을 보존할 수 있다면 포괄적이고 복잡한 설계보다 단순하지만 약간 불완전한 해결책을 선호한다.절약한 노력은 추가 기능, 새로운 상황에 대한 적응, AI의 빠른 발전에 대한 대응력 을 뒷받침할 수 있다.
4 사용성 중심 설계
PyTorch는 모델, optimizer, data loader가 일반적인 프로그램으로 작동하는 imperative, Pythonic 설계를 우선시해 복잡한 architecture와 training technique를 쉽게 구현하고 debug할 수 있게 한다. 서로 교체 가능하고 확장 가능한 component는 Python library와의 interoperability도 지원하며, 동적으로 실행되고 tensor를 변경하는 프로그램을 통해 automatic differentiation을 제공한다.
- Imperative programming: PyTorch는 imperative programming을 유지하므로 stateful layer와 model class를 포함한 임의의 neural-network architecture를 일반적인 Python program으로 작성할 수 있다.이 설계는 loop와 recursive function을 포함하는 빠르게 변화하는 network를 지원하면서 새로운 architecture를 쉽게 구현할 수 있게 한다.
- Imperative programming: 동일한 programmatic design은 optimizer와 data loader에도 적용되어, 서로 상호작용하는 두 model, optimizer, loss를 사용하는 GANs와 같은 training setup을 가능하게 한다.loss가 generator와 discriminator에 공동으로 의존하므로 rigid API는 GAN training을 처리하기 어렵다.
- Imperative programming: Eager execution은 개발 전반에서 Python debugging 및 inspection 도구를 작동하게 하며, compilation을 기다리지 않고 중간 computation을 노출한다.print statement, standard debugger, matplotlib과 같은 visualization tool을 사용해 model의 동작을 이해하고 결과를 검증할 수 있다.
- Interoperability and extensibility: PyTorch는 external library와의 bidirectional data exchange를 지원하고, 사용자가 custom subclass를 통해 automatic differentiation과 dataset을 확장할 수 있게 한다.예로 NumPy array와 tensor 간 변환, custom differentiable function, 새로운 dataset 구현이 있다.
- Automatic differentiation: Automatic differentiation은 operator overloading을 사용해 실행된 각 function의 representation을 구축하고, 안전한 gradient를 보장하기 위해 versioning으로 tensor mutation을 추적한다.이 접근법은 Python의 dynamic behavior에도 불구하고 임의의 Python program을 처리하며, tensor를 변경하는 code를 통한 differentiation을 지원한다.
5 성능 중심 구현
PyTorch는 C++ 핵심부, 비동기 실행, 메모리 관리, multiprocessing을 최적화하면서 제어 흐름을 사용자가 관리하도록 해 imperative Python 환경에서 높은 성능을 달성한다. 이러한 메커니즘은 Python의 실행 및 메모리 제약에도 GPU 활용, 효율적인 tensor 공유, 즉각적인 메모리 해제를 지원한다.
- 실행 전략: PyTorch는 실행의 모든 측면을 최적화하면서 사용자가 추가 최적화 전략을 적용할 수 있도록 하며, Python interpreter의 한계를 극복하기 위해 static data-flow graph에 의존하지 않는다.이 접근법은 계산을 custom static-graph interpreter로 미루는 대신 실행을 직접 최적화해 global interpreter lock 문제를 해결한다.
- 핵심 구현: PyTorch의 대부분은 C++로 구현되며, libtorch는 tensor, CPU 및 GPU operator, parallel primitive, automatic differentiation을 제공한다.생성된 Python binding이 이 고성능 핵심부와 Python ecosystem을 연결한다.
- 비동기 실행: PyTorch는 Python이 제어하는 control flow와 tensor data flow를 분리한 뒤, CUDA stream을 통해 GPU operator를 비동기적으로 queue에 넣어 CPU 실행과 GPU 작업을 중첩한다.이 중첩은 Python interpreter overhead에도 GPU를 포화 상태로 유지하며, operator는 CPU 또는 GPU에서 실행될 수 있다.
- 메모리 할당: PyTorch의 CUDA allocator는 메모리를 cache하고 allocation을 512-byte 배수로 반올림하며, deallocation 병목과 fragmentation을 줄이기 위해 CUDA stream마다 별도의 pool을 유지한다.stream serialization에서는 CPU free가 해당 GPU 재사용보다 먼저 일어나므로 동일 stream에서 즉시 재사용할 수 있지만, 이 설계에는 corner case가 있고 여러 stream에는 적합성이 낮다.
- Multiprocessing: torch.multiprocessing은 비효율적인 tensor serialization을 shared memory로 대체해 성능을 높이고, 이후 gradient synchronization을 수행하는 독립 GPU 간 parallel program을 가능하게 한다.또한 CUDA tensor를 투명하게 공유해 Hogwild 와 같은 기법을 지원한다.
- Tensor 메모리 관리: PyTorch는 더 이상 reference가 없을 때 reference counting을 사용해 tensor memory를 즉시 해제하지만, 메모리 보장은 reference counting 또는 사용자가 정의한 copy 및 move 동작을 지원하는 language runtime에 의존한다.이는 지연된 garbage collection으로 인한 추가 메모리 사용을 방지하며, 지원되지 않는 language binding에는 특수한 메모리 관리가 필요하다.
6 평가
PyTorch는 다양한 deep-learning task에서 경쟁력 있는 단일 머신 성능을 달성하며, 모든 benchmark에서 가장 빠른 framework 대비 처리량이 17% 이내다. 비동기 실행을 통해 runtime은 GPU utilization을 거의 완벽하게 유지하는 한편, profiling을 통해 첫 번째 iteration에서 발생하는 상당한 CUDA allocation overhead를 드러낸다.
- GPU execution: GPU execution은 CPU scheduling보다 약 세 배 더 오래 걸리므로, PyTorch는 비동기 dataflow execution을 통해 device utilization을 거의 완벽하게 달성한다.이 결과는 내장 profiler로 측정한 대표적인 ResNet-50 training-step timeline에서 얻어졌다.
- CUDA startup overhead: 첫 번째 ResNet-50 iteration 동안 cudaMalloc과 cudaFree가 CPU thread를 장시간 block하여 execution을 크게 늦추고, subsequent iteration에 비해 utilization을 낮춘다.NVIDIA profiling을 통해 CUDA runtime activity와 실행된 CUDA kernel을 모두 추적했다.
- Overall performance: 모든 benchmark에서 가장 빠른 framework 대비 17% 이내인 PyTorch는 CNTK, MXNet, TensorFlow, Chainer, PaddlePaddle과 비교해 여섯 개 model에서 경쟁력 있는 training performance를 보인다.이 유사성의 일부는 동일한 cuDNN 및 cuBLAS version을 함께 사용하기 때문으로 설명된다.
- 커뮤니티 수용: 커뮤니티 수용의 대리 지표로, 일월 2017년 출시 이후 arXiv에서 PyTorch가 언급된 월별 횟수를 일반적인 딥러닝 프레임워크가 언급된 횟수 대비 백분율로 집계한다.대리 지표에는 Caffe, Chainer, CNTK, Keras, MXNet, TensorFlow, Theano가 포함되며, 결과는 Figure 3에 보고한다.
7 결론 및 향후 과제
PyTorch는 신중한 성능 고려와 사용 편의성을 결합해 deep learning 연구에서 널리 사용된다. 향후 과제는 PyTorch JIT를 통해 Python 외부에서 프로그램을 실행하고 추가 최적화를 수행하는 등 속도와 확장성을 계속 개선하는 데 있다.
- 결론: deep learning 연구에서 PyTorch가 널리 사용되는 이유는 사용 편의성과 신중한 성능 고려를 결합했기 때문이다.
- 향후 과제: 향후 과제는 PyTorch JIT suite를 통해 Python 외부에서 실행하고 추가 최적화를 수행하는 등 PyTorch의 속도와 확장성을 계속 개선하는 것이다.