6.17. 솔버와 난수

연구 대상 함수가 샘플 버퍼가 아니라 Python 코드로 정의될 때는 다른 계열의 도구가 올바른 선택입니다. 함수의 근은 어디인가, 그 최솟값은 무엇인가, 주어진 구간에 대한 적분은 얼마인가? scipy.integratescipy.optimize 서브모듈이 그 작업을 다룹니다. 각 알고리즘은 사용자가 제공한 Python 함수로 콜백하므로 반복당 비용은 버퍼 축약보다 높습니다. 그 편리함은 솔버를 직접 작성하지 않아도 된다는 점에 있습니다.

scipy.special 하위 모듈은 확률 분포의 누적 분포 함수(CDF, 표본이 주어진 값 이하일 확률)나 확률 밀도 함수(PDF, 주어진 값에서의 상대적 가능성)를 계산할 때 등장하는 통계적 특수 함수(오차 함수, 감마 함수)를 다룹니다. numpy.random 은 디더링, 시뮬레이션, 합성 테스트 데이터를 위한 의사 난수 생성기를 다룹니다.

6.17.1. 호출 가능 객체의 수치 적분

피적분 함수가 샘플 버퍼가 아니라 Python 함수일 때, scipy.integrate는 네 가지 구적 알고리즘을 제공합니다:

  • quad() – 적응형 Gauss-Kronrod. 매끄러운 피적분 함수에 적합한 기본값입니다. (value, error)를 반환합니다.

  • romberg() – 고전적 Romberg / Newton-Cotes. 단일 float를 반환합니다. 상류에서 더 이상 사용되지 않습니다(deprecated). 호환성을 위해 포함되었습니다.

  • simpson() – 적응형 Simpson 규칙. 단일 float를 반환합니다.

  • tanhsinh() – 이중 지수 구적. 피적분 함수가 끝점 특이점이나 무한 한계를 가질 때 사용합니다. (value, error)를 반환합니다.

이중 지수 규칙(tanhsinh)으로 평가한 가우스 적분:

from math import exp
from math import pi
from math import sqrt
from ulab import numpy as np
from ulab import scipy as sp

f = lambda x: exp(-x * x)
value, err = sp.integrate.tanhsinh(f, -np.inf, np.inf)
print("approx:", value, "   exact:", sqrt(pi))

출력:

approx: 1.7724538...   exact: 1.7724538...

6.17.2. 근 찾기와 최소화

scipy.optimize는 세 가지 고전적인 단일 변수 솔버를 다룹니다. 각 반복은 사용자가 제공한 Python 함수로 콜백하므로 순수 Python 솔버 대비 속도 향상은 적당한 수준(대략 2배)입니다. 그 편리함은 솔버를 직접 작성하지 않아도 된다는 점에 있습니다.

  • bisect() – 구간을 이등분하여 [a, b] 상에서 f의 근을 찾습니다. f(a)f(b)는 반대 부호를 가져야 합니다:

    def f(x):
        return x * x - 1
    
    sp.optimize.bisect(f, 0, 4)        # ~1.0
    
  • newton() – 할선법 / Newton-Raphson 반복으로 근을 찾습니다:

    def f(x):
        return x * x * x - 2.0
    
    sp.optimize.newton(f, 3., tol=0.001, rtol=0.01)
    # ~1.260
    
  • fmin() – 다운힐 심플렉스(Nelder-Mead) 방법으로 국소 최솟값을 찾습니다:

    def f(x):
        return (x - 1) ** 2 - 1
    
    sp.optimize.fmin(f, 3.0)           # ~1.0
    

단일 변수 범위만으로도 대부분의 카메라 측 최적화 – 센서의 보정 상수, 대비 측정값을 최대화하는 게인, 히스토그램 이봉성이 가장 뚜렷해지는 임계값 – 에는 충분합니다. 다변수 문제의 경우, 올바른 답은 보통 일반적인 비선형 최적화 도구에 손을 대는 대신 문제를 작은 선형대수 풀이로 재구성하는 것입니다.

6.17.3. 특수 함수

scipy.special은 보편 함수처럼 동작하는 몇 가지 통계 및 확률 함수를 제공합니다 – 이들은 스칼라, 이터러블 또는 ndarray를 받아 부동소수점 ndarray를 반환합니다:

x = np.linspace(0, 4, num=8)

sp.special.erf(x)         # error function
sp.special.erfc(x)        # complementary error function
sp.special.gamma(x + 1)   # gamma function
sp.special.gammaln(x + 1) # log-gamma function

오차 함수와 그 여함수는 가우스 분포의 CDF에 등장합니다 – 측정된 z-점수와 확률 사이를 변환하거나 정규 분포의 꼬리 적분을 계산하는 데 선호되는 응용입니다. 감마 및 로그 감마 함수는 베타 / 카이제곱 / 스튜던트 t 계산에 나타납니다. gammalngamma 자체가 오버플로될 큰 인자에 대해 수치적으로 안정적인 형태입니다.

6.17.4. 난수

numpy.random은 일반적인 분포로부터 샘플을 추출하는 Generator 클래스를 제공합니다. 이 생성기는 상태를 가집니다(stateful). 각 호출은 내부 상태를 진행시키므로 연속된 호출은 독립적인 샘플을 반환합니다:

from ulab import numpy as np

rng = np.random.Generator(seed=42)

rng.random(size=5)             # 5 uniform [0.0, 1.0) samples
rng.uniform(low=-1.0, high=1.0, size=10)
rng.normal(loc=0.0, scale=1.0, size=(2, 4))

출력 dtype은 항상 float입니다. size=는 정수(1차원 출력) 또는 튜플(n차원 출력)을 받으며, 생략하면 단일 Python float가 반환됩니다.

이 생성기는 시뮬레이션, 디더링, 합성 테스트 데이터, 그리고 암호학적 강도가 요구되지 않는 다른 모든 응용에 적합합니다. 키나 토큰에는 적합하지 않습니다. 그런 경우에는 os를 통해 시스템 난수 소스를 사용하세요.

6.17.5. 빌드 시점 가용성

각 서브모듈이 실제로 존재하는지는 카메라가 어떻게 빌드되었는지에 달려 있습니다. scipy.optimizescipy.special은 모든 카메라에서 활성화되어 있지 않습니다. 카메라가 포함하지 않는 함수를 호출하면 AttributeError가 발생합니다. dir(sp), dir(sp.optimize), dir(np.random) 및 유사한 호출은 대상 카메라에서 사용 가능한 것을 보고합니다.