[python] np.dot이 왜 정확하지 않습니까? (n 차원 배열)

np.dot두 개의 'float32'2D 배열을 사용 한다고 가정 합니다.

res = np.dot(a, b)   # see CASE 1
print(list(res[0]))  # list shows more digits
[-0.90448684, -1.1708503, 0.907136, 3.5594249, 1.1374011, -1.3826287]

번호. 제외하고는 다음을 변경할 수 있습니다.


사례 1 : 슬라이스a

np.random.seed(1)
a = np.random.randn(9, 6).astype('float32')
b = np.random.randn(6, 6).astype('float32')

for i in range(1, len(a)):
    print(list(np.dot(a[:i], b)[0])) # full shape: (i, 6)
[-0.9044868,  -1.1708502, 0.90713596, 3.5594249, 1.1374012, -1.3826287]
[-0.90448684, -1.1708503, 0.9071359,  3.5594249, 1.1374011, -1.3826288]
[-0.90448684, -1.1708503, 0.9071359,  3.5594249, 1.1374011, -1.3826288]
[-0.90448684, -1.1708503, 0.907136,   3.5594249, 1.1374011, -1.3826287]
[-0.90448684, -1.1708503, 0.907136,   3.5594249, 1.1374011, -1.3826287]
[-0.90448684, -1.1708503, 0.907136,   3.5594249, 1.1374011, -1.3826287]
[-0.90448684, -1.1708503, 0.907136,   3.5594249, 1.1374011, -1.3826287]
[-0.90448684, -1.1708503, 0.907136,   3.5594249, 1.1374011, -1.3826287]

인쇄 된 슬라이스에 정확히 같은 수를 곱한 결과도 달라집니다.


CASE 2 : 평평 a의 1D 버전을 가지고 b, 다음 조각 a:

np.random.seed(1)
a = np.random.randn(9, 6).astype('float32')
b = np.random.randn(1, 6).astype('float32')

for i in range(1, len(a)):
    a_flat = np.expand_dims(a[:i].flatten(), -1) # keep 2D
    print(list(np.dot(a_flat, b)[0])) # full shape: (i*6, 6)
[-0.3393164, 0.9528787, 1.3627989, 1.5124314, 0.46389243, 1.437775]
[-0.3393164, 0.9528787, 1.3627989, 1.5124314, 0.46389243, 1.437775]
[-0.3393164, 0.9528787, 1.3627989, 1.5124314, 0.46389243, 1.437775]
[-0.3393164, 0.9528787, 1.3627989, 1.5124314, 0.46389243, 1.437775]
[-0.3393164, 0.9528787, 1.3627989, 1.5124314, 0.46389243, 1.437775]
[-0.3393164, 0.9528787, 1.3627989, 1.5124314, 0.46389243, 1.437775]
[-0.3393164, 0.9528787, 1.3627989, 1.5124314, 0.46389243, 1.437775]
[-0.3393164, 0.9528787, 1.3627989, 1.5124314, 0.46389243, 1.437775]

사례 3 : 더 강력한 통제; 관련되지 않은 전체를 0으로 설정 : a[1:] = 0CASE 1 코드에 추가하십시오 . 결과 : 불일치가 지속됩니다.


사례 4 : 이외의 색인을 확인 [0]; 에서와 같이 [0]결과는 생성 지점에서 고정 된 수의 배열 확대를 안정화하기 시작합니다. 산출

np.random.seed(1)
a = np.random.randn(9, 6).astype('float32')
b = np.random.randn(6, 6).astype('float32')

for j in range(len(a) - 2):
    for i in range(1, len(a)):
        res = np.dot(a[:i], b)
        try:    print(list(res[j]))
        except: pass
    print()

따라서 2D * 2D의 경우 결과가 다르지만 1D * 1D와 일치합니다. 내 독서 중 일부에서 이것은 간단한 덧셈을 사용하여 1D-1D에서 비롯된 것으로 보이지만 2D-2D는 덜 정확한 성능 향상 돋보이는 ‘fancier’를 사용합니다 (예 : pairwise 덧셈은 반대입니다). 그럼에도 불구하고, 나는 1 번 a이 정해진 ‘임계 값’을 지나서 얇아 질 때 불일치가 사라지는 이유를 이해할 수 없습니다 . 큰 a하고 b, 나중에이 임계 값은 거짓말로 보이지만 항상 존재한다.

np.dotND-ND 어레이에 대해 왜 부정확하고 일관성이 없는가? 관련 힘내


추가 정보 :

  • 환경 : Win-10 OS, Python 3.7.4, Spyder 3.3.6 IDE, Anaconda 3.0 2019/10
  • CPU : i7-7700HQ 2.8GHz
  • Numpy v1.16.5

가능한 원인 라이브러리 : NumPy와 MKL – 또한 브래스 라이브러리; 주목 해 주신 Bi Rico 에게 감사합니다


스트레스 테스트 코드 : 언급 한 바와 같이, 더 큰 어레이의 주파수에서 불일치가 악화된다. 위의 내용을 재현 할 수없는 경우 아래 값을 지정해야합니다 (그렇지 않은 경우 더 큰 값을 입력하십시오). 내 결과

np.random.seed(1)
a = (0.01*np.random.randn(9, 9999)).astype('float32') # first multiply then type-cast
b = (0.01*np.random.randn(9999, 6)).astype('float32') # *0.01 to bound mults to < 1

for i in range(1, len(a)):
    print(list(np.dot(a[:i], b)[0]))

문제 심각도 : 불일치가 ‘소규모’이지만 수십억 개의 숫자가 몇 초 동안 곱해지고 전체 런타임에서 수조에 이르는 신경망에서 작동하는 경우 더 이상 그렇지 않습니다. 보고 된 모델 정확도는 이 스레드 당 전체 10 %에 따라 다릅니다 .

아래는 기본적으로 a[0]w / len(a)==1vs. 모델에 공급하여 얻은 배열의 gif입니다 len(a)==32.


Paul 의 테스트 덕분에 다른 플랫폼 결과 :

사례 1 재생산 (일부) :

  • Google Colab VM-Intel Xeon 2.3 G-Hz-Jupyter-Python 3.6.8
  • Win-10 Pro Docker Desktop-Intel i7-8700K-Jupyter / Scipy-notebook-Python 3.7.3
  • 우분투 18.04.2 LTS + 도커-AMD FX-8150-jupyter / scipy-notebook-Python 3.7.3

참고 : 위의 것보다 훨씬 낮은 오류가 발생합니다. 첫 번째 행의 두 항목은 다른 행의 해당 항목에서 최소 유효 숫자가 1만큼 줄어 듭니다.

사례 1이 재현되지 않음 :

  • 우분투 18.04.3 LTS-인텔 i7-8700K-IPython 5.5.0-파이썬 2.7.15+ 및 3.6.8 (2 테스트)
  • 우분투 18.04.3 LTS-인텔 i5-3320M-IPython 5.5.0-파이썬 2.7.15+
  • 우분투 18.04.2 LTS-AMD FX-8150-IPython 5.5.0-Python 2.7.15rc1

참고 사항 :

  • 연결 Colab 노트북 및 jupyter 환경 내 시스템에서 관찰되는 것보다 (단지 처음 두 행에) 훨씬 적은 차이를 보여줍니다. 또한 사례 2는 (아직도) 부정확성을 나타내지 않았습니다.
  • 이 매우 제한된 샘플 내에서 현재 (Dockerized) Jupyter 환경은 IPython 환경보다 더 취약합니다.
  • np.show_config()게시하기에는 너무 길지만 요약하면 IPython 환경은 BLAS / LAPACK 기반입니다. Colab은 OpenBLAS 기반입니다. IPython Linux 환경에서 BLAS 라이브러리는 시스템에 설치됩니다. Jupyter 및 Colab에서는 / opt / conda / lib에서 제공됩니다.

업데이트 : 허용 된 답변은 정확하지만 광범위하고 불완전합니다. 코드 수준 에서 동작을 설명 할 수있는 사람 , 즉에 의해 사용되는 정확한 알고리즘 np.dot과 위의 결과에서 관찰 된 ‘일관된 불일치’를 설명하는 방법에 대한 질문은 여전히 ​​열려 있습니다 (의견 참조). 내 해독을 넘어서는 직접 구현이 있습니다 : sdot.carraytypes.c.src



답변

피할 수없는 숫자 부정확 한 것처럼 보입니다. 설명한 바와 같이, 여기서 , NumPy와 행렬 승산을위한 고도로 최적화 신중하게 조정 BLAS 방법을 사용 . 이것은 아마도 행렬의 크기가 변할 때 2 개의 행렬을 곱한 일련의 연산 (합계 및 곱)이 변경됨을 의미합니다.

더 명확하게하기 위해, 수학적으로 결과 행렬의 각 요소는 두 벡터의 등가 길이 (등 길이의 숫자 시퀀스) 로 계산 될 수 있다는 것을 알고 있습니다 . 그러나 이것은 NumPy가 결과 행렬의 요소를 계산하는 방법 이 아닙니다 . 사실 Strassen 알고리즘 과 같이 행 열 내적을 직접 계산하지 않고 동일한 결과를 얻는 더 효율적이지만 복잡한 알고리즘이 있습니다.

소자하더라도 같은 알고리즘을 사용하는 경우 C의 IJ 결과적인 행렬에서의 C가 = AB는 수학적의 내적으로 정의되는 i 번째 행 과 j 번째 열에 B 에는 승산 매트릭스 경우 A2는 데 동일한 제 i 같은 행 행렬과 B2가 동일 갖는 j 번째 같은 열 B는 , 소자 C2는 IJ는 실제로 전체의 동작에 의존하는 다른 서열 (아래 계산한다 A2B2를 다른 숫자 오류로 이어질 수 있습니다.

그 이유의,해도 수학적 C IJ = C2 IJ (케이스 1에서와 같이), 동작의 다른 시퀀스는 상이한 수치의 오차 리드 (인해 행렬의 크기 변화) 계산의 알고리즘 하였다. 수치 오류는 또한 환경에 따라 약간 다른 결과를 설명하며 경우에 따라 일부 환경에서는 수치 오류가 없을 수도 있습니다.


답변