나는 NumPy가 싫어요

1 month ago 6

  • 필자는 NumPy에 대한 불만을 주제로 여러 예시와 함께 문제점을 설명함
  • 간단한 배열 연산은 NumPy로 쉽지만, 차원이 늘어나면 복잡성과 혼란이 급격히 증가함
  • 브로드캐스팅과 고급 인덱싱 등 NumPy의 설계는 명확성과 추상화 측면에서 부족함
  • 명시적으로 축을 지정하는 대신 추측과 시행착오에 의존하는 코드 작성이 필수임
  • 개선된 배열 언어에 대한 아이디어를 제시하며 구체적인 대안을 다음 글에서 소개할 예정임

서론: NumPy에 대한 애증

  • 필자는 오랜 기간 NumPy을 사용해왔지만, 그 한계에 많이 실망했음을 밝힘
  • NumPy는 파이썬에서 배열 연산을 위한 필수적이고 영향력 있는 라이브러리임
  • PyTorch 등의 현대 머신러닝 라이브러리에도 NumPy와 유사한 문제들이 존재함

NumPy의 쉬운 점과 어려운 점

  • 기본적인 선형 방정식 풀이와 같은 간단한 연산은 명확하고 우아한 문법으로 가능함
  • 그러나 배열 차원이 높아지거나 연산이 복잡해지면, for 루프 없이 일괄 처리가 필요해짐
  • 루프를 쓰지 못하는 환경(GPU 연산 등)에서는 특이한 벡터화 문법이나 특별한 함수 호출 방식이 필요함
  • 하지만 이러한 함수들의 정확한 사용법이 모호하고 문서만으로도 명확하게 알기 어려움
  • 실제로 numpy의 linalg.solve 함수는 고차원 배열인 경우 어떻게 써야 올바른지 누구도 확신하기 힘듦

NumPy의 문제점

  • NumPy는 다차원 배열의 일부 또는 특정 축에 연산을 적용하는 데 일관된 이론이 부족함
  • 배열 차원이 2 이하일 때는 명확하지만, 3차원 이상에서는 각 배열마다 연산 대상 축의 지정이 불분명함
  • 명시적으로 차원을 맞추기 위해 None 사용, 브로드캐스팅, np.tensordot 등 복잡한 방법을 강제함
  • 이러한 방식은 실수 유발, 코드 가독성 저하, 버그의 가능성 증가를 초래함

반복문과 명확성

  • 실제로 반복문을 허용한다면 더욱 간결하고 명확한 코드 작성이 가능함
  • 반복문 코드가 덜 세련돼 보일 수 있으나, 명확성 측면에서는 큰 장점이 있음
  • 반면, 배열 차원이 바뀌면 transpose나 축 순서를 일일이 고민해야 하고, 복잡성이 증가함

np.einsum: 예외적으로 좋은 함수

  • np.einsum은 축의 이름을 지정할 수 있는 유연한 도메인 특화 언어를 제공하여 강력함
  • einsum은 연산의 의도가 명확하고 일반화도 뛰어나, 복잡한 축 연산을 명시적으로 구현 가능함
  • 하지만 einsum과 유사한 방식의 연산 지원이 일부 연산에 한정되고, 예를 들어 linalg.solve에는 못 씀

브로드캐스팅의 문제점

  • NumPy의 핵심 트릭인 브로드캐스팅은 차원이 안 맞을 때 자동으로 맞춰주는 기능임
  • 간단한 경우에는 편리하지만, 실제로는 차원을 명확하게 알기 어렵게 만들고, 오류 사례가 많음
  • 브로드캐스팅이 암묵적이라 코드를 읽을 때 매번 연산이 어떻게 작동하는지 확인해야 함

인덱싱의 불명확함

  • NumPy의 고급 인덱싱은 배열 shape 예측이 매우 어렵고 불명확함
  • 다양한 인덱싱 조합에 따라 결과 배열의 shape가 달라지므로, 실제로 다뤄본 경험 없이는 예측이 곤란함
  • 인덱싱 규칙 설명 문서도 길고 복잡하여, 익히는 데 큰 시간 소모를 유발함
  • 단순 인덱싱만 쓰려고 해도 특정 연산에서는 어쩔 수 없이 고급 인덱싱을 사용하게 됨

NumPy 함수 설계의 한계

  • 많은 NumPy 함수들은 특정 배열 shape에만 최적화되어 있음
  • 고차원 배열에는 추가적인 axes 인자, 별도 함수명, 관례를 사용해야 하고, 함수마다 일관성이 없음
  • 추상화와 재사용이 기본인 프로그래밍 원칙에 역행하는 구조임
  • 특정 문제를 해결하는 함수를 써도, 다양한 배열과 축에 재적용하려면 아예 다른 코드로 다시 작성해야 함

실제 예시: self-attention 구현

  • self-attention 구현을 NumPy로 작성할 때, 반복문을 쓰면 명확하나, 벡터화를 강제하면 코드가 복잡해짐
  • 다중 헤드 attention과 같이 고차원 연산이 필요할 때, einsum과 축 변환을 복합적으로 써야 하고 코드가 난해해짐

결론 및 대안

  • 필자는 NumPy가 "다른 배열 언어들보다 나쁜 점이 많지만 그만큼 시장에서 중요해진 유일한 선택지"임을 밝힘
  • NumPy의 여러 문제점(브로드캐스팅, 인덱싱 불명확성, 함수의 비일관성 등)을 극복하기 위해 개선된 배열 언어의 프로토타입을 만들었음을 예고함
  • 구체적인 개선안(새로운 배열 언어 API)은 추후 별도의 글에서 소개할 계획임

Read Entire Article