[ultralytics] Ultralytics 코드베이스 경량화: SciPy 의존성 감소 및 NumPy 기반 최적화
PR 링크: ultralytics/ultralytics#24572 상태: Merged | 변경: +31 / -14
들어가며
최신 딥러닝 모델을 개발하고 배포하는 과정에서 코드베이스의 효율성과 이식성은 매우 중요합니다. 특히, 외부 라이브러리에 대한 의존성을 최소화하는 것은 배포 환경의 복잡성을 줄이고 잠재적인 성능 병목 현상을 완화하는 데 도움이 됩니다. Ultralytics의 최근 PR은 이러한 목표를 달성하기 위해 SciPy 라이브러리에 대한 의존성을 줄이고, NumPy를 활용하여 핵심 연산의 성능을 최적화하는 데 중점을 두었습니다.
이 PR은 주로 다음과 같은 문제들을 해결합니다:
- 불필요한 SciPy 의존성 제거: 트래킹 및 플로팅 유틸리티에서 SciPy의 특정 함수들이 NumPy로 대체될 수 있음을 확인하고, 이를 통해 라이브러리 의존성을 줄였습니다.
- 성능 최적화: 특히 임베딩 거리 계산과 같은 반복적인 연산에서 NumPy 기반 구현이 SciPy보다 더 나은 성능을 보임을 확인하고 적용했습니다.
- 코드 가독성 및 유지보수성 향상: SciPy 임포트를 로컬 스코프로 제한하고, 새로운 내부 함수를 문서화하여 코드베이스의 명확성을 높였습니다.
- 사용자 경험 개선: 검증 시각화 옵션을 추가하여 사용자가 더 깔끔하고 제어된 출력을 얻을 수 있도록 했습니다.
이 글에서는 해당 PR의 주요 코드 변경 사항을 분석하고, 각 변경이 왜 좋은 최적화인지, 그리고 어떤 기술적 교훈을 얻을 수 있는지 자세히 살펴보겠습니다.
코드 분석
1. 트래킹 유틸리티: SciPy에서 NumPy로의 전환
이 PR의 핵심적인 변경 중 하나는 ultralytics/trackers/utils/kalman_filter.py와 ultralytics/trackers/utils/matching.py 파일에서 SciPy의 선형 대수 및 거리 계산 함수를 NumPy의 동등 함수로 대체한 것입니다.
1.1. kalman_filter.py: scipy.linalg.cho_factor/cho_solve 대체
Kalman 필터의 update 메서드에서 칼만 게인(Kalman gain)을 계산하는 부분은 scipy.linalg.cho_factor와 scipy.linalg.cho_solve를 사용하여 투영된 공분산 행렬(projected_cov)을 분해하고 해를 구했습니다. 이 PR에서는 이를 NumPy의 np.linalg.solve로 대체했습니다.
Before:
chol_factor, lower = scipy.linalg.cho_factor(projected_cov, lower=True, check_finite=False)
kalman_gain = scipy.linalg.cho_solve(
(chol_factor, lower), np.dot(covariance, self._update_mat.T).T, check_finite=False
).T
After:
kalman_gain = np.linalg.solve(projected_cov, np.dot(covariance, self._update_mat.T).T).T
또한, gating_distance 함수에서 Mahalanobis 거리를 계산할 때 사용되던 scipy.linalg.solve_triangular도 np.linalg.solve로 대체되었습니다.
Before:
cholesky_factor = np.linalg.cholesky(covariance)
z = scipy.linalg.solve_triangular(cholesky_factor, d.T, lower=True, check_finite=False, overwrite_b=True)
After:
cholesky_factor = np.linalg.cholesky(covariance)
z = np.linalg.solve(cholesky_factor, d.T)
왜 이게 좋은가?
리뷰 댓글에서 제공된 프로파일링 결과에 따르면, np.linalg.solve는 scipy.linalg.cho_factor/cho_solve 조합이나 scipy.linalg.solve_triangular보다 훨씬 빠릅니다. 특히 solve_triangular의 경우 NumPy 구현이 약 19배 이상 빠르다는 결과도 있습니다. 이는 NumPy가 최적화된 BLAS/LAPACK 라이브러리를 활용하여 이러한 선형 대수 연산을 매우 효율적으로 처리하기 때문입니다. SciPy의 cho_factor/cho_solve는 Cholesky 분해를 사용하지만, np.linalg.solve는 일반적인 선형 시스템 해결 알고리즘을 사용하며, 특정 조건에서는 더 효율적일 수 있습니다. 또한, SciPy의 특정 함수들은 내부적으로 추가적인 오버헤드를 가질 수 있습니다. 이러한 변경은 트래킹 모듈의 성능을 직접적으로 향상시킵니다.
1.2. matching.py: scipy.spatial.distance.cdist 대체 (코사인 거리)
객체 추적에서 트랙과 탐지 간의 유사성을 측정하는 데 사용되는 embedding_distance 함수는 특징 벡터(feature vector) 간의 거리를 계산합니다. 기존 코드에서는 scipy.spatial.distance.cdist를 사용하여 코사인 거리를 계산했습니다. 이 PR에서는 코사인 거리 계산을 NumPy 연산으로 직접 구현하여 scipy.spatial.distance.cdist 사용을 제거했습니다.
Before (주석 처리된 부분):
# cost_matrix[i, :] = np.maximum(0.0, cdist(track.smooth_feat.reshape(1,-1), det_features, metric))
After (코사인 거리 계산 부분):
if metric ==
## 참고 자료
- https://numpy.org/doc/stable/reference/generated/numpy.linalg.solve.html
- https://numpy.org/doc/stable/reference/generated/numpy.linalg.norm.html
- https://numpy.org/doc/stable/reference/generated/numpy.dot.html
- https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.cdist.html
- https://numpy.org/doc/stable/reference/generated/numpy.convolve.html
- https://pytorch.org/docs/stable/generated/torch.compile.html
> ⚠️ **알림:** 이 분석은 AI가 실제 코드 diff를 기반으로 작성했습니다.
관련 포스트
- [transformers] Hugging Face Transformers: SequenceFeatureExtractor.pad() 최적화로 불필요한 NumPy 배열 재변환 제거
- [cpython] tarfile 스트리밍 모드(r|*) 성능 개선: 파이썬 압축 파일 처리의 숨겨진 병목 제거
- [feast] Feast Feature Server의 직렬화 성능 4배 향상: MessageToDict 최적화
- [cpython] Python의 os.fork 후 발생하던 성능 프로파일링 충돌 문제 해결 및 최적화 분석
- [cpython] CPython의 PySequence_GetSlice 성능 개선: 불필요한 참조 카운트 연산 제거
PR Analysis 의 다른글
- 이전글 [onnxruntime] ONNX Runtime CPU ScatterElements 커널의 멀티스레딩 최적화 분석
- 현재글 : [ultralytics] Ultralytics 코드베이스 경량화: SciPy 의존성 감소 및 NumPy 기반 최적화
- 다음글 [vllm] vLLM 기술 딥다이브: CUTLASS를 활용한 NVFP4 Linear 커널의 Batch Invariance 최적화
댓글