ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python] numpy 범용함수 (universal functions, ufunc)
    언어/파이썬 & 장고 2022. 8. 14. 22:19

    numpy 배열의 연산은 빠르거나 느릴 수 있는데 연산을 빠르게 만드는 핵심은 벡터화(vectorized) 연산을 사용하는 것인데, 일반적으로 numby의 유니버설 함수 (universal functions, ufunc)를 통해 구현됩니다.

    파이썬 루프는 느리다

    파이썬은 타입이 유연하므로 수많은 작은 연산이 반복되는 상황에서 느립니다. 배열을 반복해서 각 요소를 조작하는 것을 예로 들면 느린 것을 볼 수 있습니다.

    import numpy as np
    
    # 난수 번호 고정
    np.random.seed(0)
    
    def test(values):
        size = len(values)
        output = np.empty(size)
    
        for i, value in enumerate(values):
            output[i] = 1.0 / value
    
        return output
    
    values = np.random.randint(1, 10, size=5)
    print(values)
    
    result = test(values)
    
    print(result)

    위의 코드는 리스트 사이즈가 100만인 경우, 연산하고 저장하는데 수 초가 걸립니다. 병목은 연산 자체가 아니라 루프의 사이클마다 수행해야 하는 타입 확인과 함수 디스패치에서 발생합니다. 역수가 계산될 대마다 파이썬은 먼저 객체의 타입을 확인하고 해당 타입에 맞게 사용할 적절한 함수를 동적으로 검색합니다. 만약 컴파일된 코드로 작업했다면 코드가 실행되기 전 이러한 작업이 선행됐을 것이고 효율적인 연산이 됐을 것입니다.

    Ufincs

    numpy는 여러 종류의 연산에 대해 이러한 종류의 정적 타입 체계를 가진 컴파일된 루틴에 편리한 인터페이스를 제공합니다. 이를 벡터화 연산이라고 합니다. 벡터화 연산은 간단하게 배열에 연산을 수행해 각 요소에 적용함으로써 수행할 수 있습니다. 이 벡터화 방식은 루프를 numpy의 기저를 이루는 컴파일된 계층으로 밀어 넣음으로써 훨씬 빠르게 실행되도록 설계되었습니다.

    values = np.random.randint(1, 10, size=5)
    result2 = 1.0 / values

    위와 같은 numpy의 벡터화 연산은 100만건을 실행해도 밀리세컨드 단위로 연산하고 저장할 수 있습니다.

    보통 이러한 벡터화 연산, 범용함수는 numpy라이브러리를 활용한 대부분의 함수를 얘기하며 더 많은 함수는 공식 홈페이지에서 확인할 수 있습니다.

    댓글