Data Science

데이터 사이언스를 위한 GPU 가속 시작하기

데이터 사이언스를 위한 GPU 가속 시작하기
Reading Time: 5 minutes

데이터 사이언스 분야에서 운영 효율성은 점점 더 복잡하고, 거대해지고 있는 데이터 세트를 처리하는 데 있어 핵심적인 요소입니다. GPU 가속화는 최신 워크플로우에 필수적인 요소가 되었으며, 놀라운 성능 향상을 제공하고 있습니다.

RAPIDS는 최소한의 코드 변경으로 GPU를 사용하여 데이터 사이언스 파이프라인을 가속화하도록 설계된 NVIDIA에서 개발한 오픈 소스 라이브러리이자 프레임워크 제품군입니다. 데이터 조작을 위한 cuDF, 머신 러닝을 위한 cuML, 그래프 분석을 위한 cuGraph와 같은 도구를 제공하는 RAPIDS는 기존 Python 라이브러리와 원활하게 통합할 수 있어 데이터 사이언티스트가 더 빠르고 효율적으로 처리할 수 있도록 지원해줍니다.

본 포스팅에서는 특히 숙련된 데이터 사이언티스트들을 위해 CPU 데이터 사이언스 라이브러리에서 GPU 가속 워크플로우로 전환하기 위한 팁을 공유하고자 합니다.

데스크톱 또는 클라우드 인프라에서 RAPIDS 설정하기

RAPIDS를 시작하는 방법은 간단하지만 몇 가지 종속성들이 있습니다. 로컬 설치에 대한 자세한 가이드를 제공하는 공식 RAPIDS 설치 가이드를 따르는 것이 좋은데요, pip 설치, Docker 이미지 또는 Conda와 같은 환경을 통해 프레임워크를 설치할 수 있는 여러 경로가 있습니다. 특히 클라우드 환경에서 RAPIDS를 설정하려면 RAPIDS 클라우드 배포 가이드를 참조하시기 바랍니다. 설치하기 전에 설치 페이지에서 사용 중인 CUDA 버전과 지원되는 RAPIDS 버전을 확인하여 호환성을 확인해야만 합니다.

pandas를 위한 cuDF 및 GPU 가속

RAPIDS의 장점은 사용자가 GPU 가속 워크플로우를 위해 설계된 특정 라이브러리를 채택할 수 있는 모듈식 아키텍처에 있습니다. 이 중 cuDF는 기존 pandas 기반 워크플로우에서 GPU에 최적화된 데이터 처리로 원활하게 전환할 수 있는 강력한 도구로, 코드 변경이 전혀 필요가 없습니다.

시작하려면 데이터 가져오기 및 나머지 작업을 GPU에서 실행하기 위해 pandas를 가져오기 전에 cuDF 확장을 활성화해야 하는데요, load_ext cudf.pandas로 RAPIDS 확장 프로그램을 로드하면, 익숙한 구문과 구조를 유지하면서 기존 워크플로우 내에 cuDF DataFrame을 손쉽게 통합하여 pandas의 익숙한 구문을 그대로 유지할 수 있습니다.

또한 pandas와 마찬가지로, cuDF pandas는 .csv, .json, .pickle, .paraquet 등 다양한 파일 형식을 지원하므로 GPU 가속 데이터 조작을 가능하게 해줍니다.

아래의 코드는 cudf.pandas 확장자를 활성화하고 두 개의 .csv 파일을 연결하는 방법의 예시입니다:

%load_ext cudf.pandas
import pandas as pd 
import cupy as cp 
  
train = pd.read_csv('./Titanic/train.csv') 
test = pd.read_csv('./Titanic/test.csv') 
concat = pd.concat([train, test], axis = 0) 

cudf.pandas 확장자를 로드하면 코드 변경이나 재작성 없이도 필터링, 그룹화, 병합과 같은 익숙한 pandas 연산을 GPU에서 실행할 수 있습니다. cuDF 가속기는 pandas API와 호환되어 CPU에서 GPU로 원활하게 전환하는 동시에 상당한 연산 속도 향상을 제공한답니다.

target_rows = 1_000_000
repeats = -(-target_rows // len(train))  # Ceiling division
train_df = pd.concat([train] * repeats, ignore_index=True).head(target_rows)
print(train_df.shape)  # (1000000, 2)
 
repeats = -(-target_rows // len(test))  # Ceiling division
test_df = pd.concat([test] * repeats, ignore_index=True).head(target_rows)
print(test_df.shape)  # (1000000, 2)
 
combine = [train_df, test_df]
 
 
(1000000, 12)
(1000000, 11)
filtered_df = train_df[(train_df['Age'] > 30) & (train_df['Fare'] > 50)] 
grouped_df = train_df.groupby('Embarked')[['Fare', 'Age']].mean() 
additional_info = pd.DataFrame({ 
    'PassengerId': [1, 2, 3], 
    'VIP_Status': ['No', 'Yes', 'No'] 
  }) 
merged_df = train_df.merge(additional_info, on='PassengerId', 
how='left')

디코딩 성능: CPU 및 GPU 런타임 지표의 실제 실행

데이터 사이언스 분야에서 성능 최적화는 비단 속도 뿐만 아니라 컴퓨팅 리소스가 어떻게 활용되는지 이해하는 것까지 포함됩니다. 여기에는 작업이 CPU와 GPU 아키텍처를 활용하는 방식을 분석하고, 비효율적인 부분을 파악하며, 워크플로우의 효율성을 향상시키기 위한 전략을 구현하는 것도 포함이 되는데요.

cudf.pandas.profile과 같은 성능 프로파일링 도구는 코드 실행에 대한 자세한 검사를 제공하여 핵심적인 역할을 합니다. 아래 실행 결과는 각 기능을 세분화하고 CPU에서 처리된 작업과 GPU에서 가속화된 작업을 구분합니다:

%%cudf.pandas.profile
train_df[['Pclass', 'Survived']].groupby(['Pclass'], 
as_index=False).mean().sort_values(by='Survived', ascending=False)
        Pclass    Survived
0         1        0.629592
1         2        0.472810
2         3        0.242378
 
                         Total time elapsed: 5.131 seconds
                         5 GPU function calls in 5.020 seconds
                         0 CPU function calls in 0.000 seconds
 
                                       Stats
 
+------------------------+------------+-------------+------------+------------+-------------+------------+
| Function           | GPU ncalls  | GPU cumtime | GPU percall | CPU ncalls | CPU cumtime | CPU percall |
+------------------------+------------+-------------+------------+------------+-------------+------------+
| DataFrame.__getitem__ | 1          | 5.000       | 5.000      | 0          | 0.000       | 0.000      |
| DataFrame.groupby     | 1          | 0.000       | 0.000      | 0          | 0.000       | 0.000      |
| GroupBy.mean          | 1          | 0.007       | 0.007      | 0          | 0.000       | 0.000      |
| DataFrame.sort_values | 1          | 0.002       | 0.002      | 0          | 0.000       | 0.000      |
| DataFrame.__repr__    | 1          | 0.011       | 0.011      | 0          | 0.000       | 0.000      |
+------------------------+------------+-------------+------------+------------+-------------+------------+

이러한 세분성은 지원되지 않는 cuDF 함수, 호환되지 않는 데이터 유형 또는 최적이 아닌 메모리 처리로 인해 실수로 CPU 실행으로 되돌아가는 연산을 정확히 찾아내는 데 도움이 됩니다. 이러한 폴백은 전체 성능에 큰 영향을 미칠 수 있으므로 이러한 문제를 식별하는 것이 중요합니다. 이 로더(Loader)에 대해 자세히 알아보려면 GPU 가속을 위한 cudf.pandas 프로파일러 마스터하기를 참고하세요.

또한 %%time 및 %%timeit과 같은 Python 매직 커맨드(Magic commands)을 사용하여 특정 코드 블록의 벤치마킹을 활성화하면 pandas (CPU)와 pandas용 cuDF 가속기(GPU) 간의 런타임을 직접 비교할 수 있습니다. 이러한 도구는 GPU 가속을 통해 얻을 수 있는 효율성 향상에 대한 인사이트를 제공합니다. 특히 %%time을 사용한 벤치마킹은 CPU와 GPU 환경 간의 실행 시간을 명확하게 비교하여 병렬 처리를 통해 달성할 수 있는 효율성 향상을 보여줍니다.

%%time 
  
print("Before", train_df.shape, test_df.shape, combine[0].shape, combine[1].shape) 
  
train_df = train_df.drop(['Ticket', 'Cabin'], axis=1) 
test_df = test_df.drop(['Ticket', 'Cabin'], axis=1) 
combine = [train_df, test_df] 
  
print("After", train_df.shape, test_df.shape, combine[0].shape, combine[1].shape) 
CPU output:
Before (999702, 12) (999856, 11) (999702, 12) (999856, 11)
After  (999702, 10) (999856, 9)  (999702, 10) (999856, 9)
 
CPU times: user 56.6 ms, sys: 8.08 ms, total: 64.7 ms
 
Wall time: 63.3 ms
GPU output:
Before (999702, 12) (999856, 11) (999702, 12) (999856, 11)
After  (999702, 10) (999856, 9)  (999702, 10) (999856, 9)
 
CPU times: user 6.65 ms, sys: 0 ns, total: 6.65 ms
 
Wall time: 5.46 ms

%%time 예제는 실행 시간이 10배 빨라져 CPU에서 63.3밀리초(ms)였던 월 타임이 GPU에서 5.46ms로 단축되었습니다. 이는 대규모 데이터 작업에서 cuDF pandas를 통한 GPU 가속의 효율성을 보여주는데요, 성능 메트릭의 일관성과 안정성을 측정하기 위해 반복 실행을 수행하는 %%timeit을 사용하면 더 많은 인사이트를 얻을 수 있습니다.

%%timeit  
  
for dataset in combine: 
    dataset['Title'] = dataset.Name.str.extract(' ([A-Za-z]+)\\.', expand=False) 
  
pd.crosstab(train_df['Title'], train_df['Sex']) 
CPU output:
1.11 s ± 7.49 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
GPU output:
89.6 ms ± 959 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit 예제는 GPU 가속을 통해 10배의 성능 향상을 보여 주며, CPU에서 루프당 1.11초였던 실행 시간을 GPU에서 루프당 89.6ms로 단축시키는데요, 이는 집중적인 데이터 작업에 대한 cuDF pandas의 효율성을 보여줍니다.

GPU 사용률 확인

서로 다른 데이터 유형으로 작업할 때는 시스템이 GPU를 효과적으로 활용하고 있는지 확인하는 것이 중요합니다. 익숙한 타입 커맨트(Type command)을 사용하여 배열이 CPU에서 처리되고 있는지 GPU에서 처리되고 있는지 확인하여 NumPy 배열과 CuPy 배열을 구분할 수 있습니다.

type(guess_ages)
<module 'pandas' (ModuleAccelerator(fast=cudf, slow=pandas))>

출력이 np.array이면 데이터가 CPU에서 처리되고 있는 것입니다. 출력이 cupy.ndarray이면 데이터가 GPU에서 처리되고 있는 것입니다. 이 빠른 확인을 통해 워크플로우가 의도한 곳에서 GPU 리소스를 활용하고 있는지 확인할 수 있습니다.

둘째, print 명령을 사용하여 간단히 GPU가 활용되고 있는지 확인하고 cuDF 데이터프레임이 처리되고 있는지 확인할 수 있습니다. 출력에는 빠른 경로(cuDF) 또는 느린 경로(pandas)가 사용 중인지 여부가 명시되어 있습니다. 이 간단한 검사를 통해 GPU가 데이터 작업 가속화를 위해 활성화되어 있는지 쉽게 확인할 수 있습니다.

print(pd)
<module 'pandas' (ModuleAccelerator(fast=cudf, slow=pandas))>

마지막으로, df.info와 같은 명령을 사용하여 cuDF 데이터프레임의 구조를 검사하고 계산이 GPU 가속으로 이루어지는지 확인할 수 있습니다. 이를 통해 작업이 GPU에서 실행되는지 아니면 CPU로 되돌아가는지 확인할 수 있습니다.

train_df.info()
<class 'cudf.core.dataframe.DataFrame'>
 
RangeIndex: 1000000 entries, 0 to 999999
Data columns (total 9 columns):
 #   Column   Non-Null Count   Dtype   
---  ------   --------------   -----  
 0   Survived 1000000 non-null  int64   
 1   Pclass   1000000 non-null  int64   
 2   Sex      1000000 non-null  int64   
 3   Age      1000000 non-null  float64 
 4   SibSp    1000000 non-null  int64   
 5   Parch    1000000 non-null  int64   
 6   Fare     1000000 non-null  float64 
 7   Embarked 997755 non-null   object 
 8   Title    1000000 non-null  int64   
dtypes: float64(2), int64(6), object(1)
memory usage: 65.9+ MB

결론

RAPIDS는 cuDF pandas와 같은 도구를 통해 기존 CPU 기반 데이터 워크플로우에서 GPU 가속 처리로 원활하게 전환하여 상당한 성능 향상을 제공하는데요, %%time, %%timeit, %%cudf.pandas.profile과 같은 프로파일링 도구와 같은 기능을 활용하여 런타임 효율성을 측정하고 최적화할 수 있습니다. 또한 type, print(pd), df.info와 같은 간단한 명령을 통해 GPU 사용률을 검사할 수 있어 워크플로우가 GPU 리소스를 효과적으로 활용하고 있는지 확인할 수 있습니다.

게시물에 자세히 설명된 데이터 작업을 직접 해보려면 함께 제공되는 Jupyter 노트북을 확인하세요.

GPU 가속 데이터 사이언에 대해 자세히 알아보려면 데이터 사이언스로 입문하는 10분: RAPIDS cuDF와 CuPy 라이브러리 간의 전환 Google Colab에서 팬더를 최대 50배까지 가속화하는 RAPIDS cuDF 게시물을 참고하세요.

관련 리소스

Discuss (0)

Tags