Предположим, мы берем 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]
Числа. Кроме того, они могут измениться:
CASE 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]
CASE 3 : более сильный контроль;установите все не вовлеченные цели в ноль : добавьте a[1:] = 0
к коду 1. Результат: расхождения сохраняются.
CASE 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, IDE Spyder 3.3.6, Anaconda 3.0 2019/10
- CPU : i7-7700HQ 2,8 ГГц
- Numpy v1.16.5
Возможная библиотека виновников : Numpy MKL - также библиотеки BLASS;спасибо 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 процентов, по этой теме .
Ниже приведен gif массивов, полученных в результате подачи к модели, которая в основном a[0]
, w / len(a)==1
против len(a)==32
:
![image](https://i.stack.imgur.com/nOqIG.gif)
ДРУГИЕ ПЛАТФОРМЫ результаты, в соответствии с результатами Пола :
Воспроизведен случай 1 (частично) :
- Google Colab VM - Intel Xeon 2.3 G-Hz - Jupyter - Python 3.6.8
- Рабочий стол Win-10 Pro Docker - Intel i7-8700K - jupyter / scipy-notebook - Python 3.7.3
- Ubuntu 18.04.2 LTS + Docker - AMD FX-8150 - jupyter / scipy-notebook - Python 3.7.3
Примечание : они дают намного меньшую ошибку, чем показано выше;две записи в первой строке отключены на 1 в младшей значащей цифре из соответствующих записей в других строках.
Случай 1 не воспроизводится :
- Ubuntu18.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 (пока) никогда не было неточности.
- В этом очень ограниченном примере текущая (докеризованная) среда Jupyter более восприимчива, чем среда IPython.
np.show_config()
слишком долго, чтобы публиковать здесь - но очень скучно в средах ipython (на основе blas / lapack) и на основе openblas на Google Colab;В средах ipython Linux библиотеки blas устанавливаются системой, тогда как в средах jupyter и colab они берутся из / opt / conda / lib