Припустимо, ми беремо 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]
Результати відрізняються, навіть якщо надрукований фрагмент походить від точно збіжених чисел.
СЛУЧАЙ 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 : посилений контроль; встановіть усі нульові entires до нуля : додайте a[1:] = 0
до CASE 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 використовує "більш фантазійне", додаток для підвищення продуктивності, який може бути менш точним (наприклад, попарне додавання робить навпаки). Тим не менш, я не можу зрозуміти, чому невідповідності зникають у випадку, коли 1 раз a
прорізається встановлений "поріг"; чим більший a
і b
, тим пізніше цей поріг, схоже, лежить, але він завжди існує.
Всі сказали: чому np.dot
неточні (і непослідовні) для масивів ND-ND? Відповідний Git
Додаткова інформація :
- Навколишнє середовище : Win-10 OS, Python 3.7.4, Spyder 3.3.6 IDE, Anaconda 3.0 2019/10
- Процесор : i7-7700HQ 2,8 ГГц
- Numpy v1.16.5
Можлива бібліотека винуватця : Numpy MKL - також бібліотеки BLASS; дякую Бі-Ріко за те, що помітили
Код стрес-тесту : як зазначалося, розбіжності посилюються в частоті з / більшими масивами; якщо вище не відтворюється, нижче має бути (якщо ні, спробуйте більші дими). Мій вихід
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 ниток на 10 відсотків .
Нижче наведено графік масивів, що виникає в результаті подачі на модель, що в основному a[0]
, з / len(a)==1
в len(a)==32
:
ІНШІ ПЛАТФОРМИ, результати та завдяки подякам Павла :
Справа 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
- Ubuntu 18.04.2 LTS + Docker - AMD FX-8150 - юпітер / scipy-ноутбук - Python 3.7.3
Примітка : вони дають набагато меншу помилку, ніж показано вище; два записи в першому рядку відключаються на 1 в найменш значущій цифрі від відповідних записів в інших рядках.
Випадок 1 не відтворено :
- Ubuntu 18.04.3 LTS - Intel i7-8700K - IPython 5.5.0 - Python 2.7.15+ та 3.6.8 (2 тести)
- Ubuntu 18.04.3 LTS - Intel i5-3320M - IPython 5.5.0 - Python 2.7.15+
- Ubuntu 18.04.2 LTS - AMD FX-8150 - IPython 5.5.0 - Python 2.7.15rc1
Примітки :
- В пов'язаних Colab ноутбуків і jupyter середовищ показують набагато менше розбіжність (і тільки для перших двох рядків) , ніж спостерігається в моїй системі. Також Справа 2 ніколи (ще) не виявляла точності.
- У цьому дуже обмеженому зразку поточне (докерізоване) середовище Юпітера є більш сприйнятливим, ніж середовище IPython.
np.show_config()
занадто довго для публікації, але підсумовуючи: IPython envs засновані на BLAS / LAPACK; Colab заснований на OpenBLAS. У IPython Linux envs бібліотеки BLAS встановлені системою - у Jupyter та Colab вони надходять з / opt / conda / lib
ОНОВЛЕННЯ : прийнята відповідь точна, але широка і неповна. Питання залишається відкритим для всіх, хто може пояснити поведінку на рівні коду, а саме - точний алгоритм, який використовується np.dot
, і як він пояснює "послідовні невідповідності", що спостерігаються у вищезазначених результатах (див. Також коментарі). Ось декілька прямих реалізацій поза моїм розшифровкою: sdot.c - arraytypes.c.src
ndarrays
зазвичай ігнорують числові втрати точності. Оскільки для простоти вони reduce-sum
вздовж кожної осі, порядок операцій може бути не оптимальним ... Зауважте, що якщо ви float64