สมมติว่าเราใช้np.dot
สอง'float32'
อาร์เรย์สองมิติ:
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 : การควบคุมที่แข็งแกร่ง ตั้งค่าการไม่เกี่ยวข้องทั้งหมดให้เป็นศูนย์ : เพิ่ม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, Python 3.7.4, Spyder 3.3.6 IDE, Anaconda 3.0 2019/10
- CPU : i7-7700HQ 2.8 GHz
- 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
vs. 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
- Ubuntu 18.04.2 LTS + Docker - AMD FX-8150 - jupyter / scipy-notebook - 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 ยังไม่เคยมีความไม่แน่นอน
- ภายในตัวอย่างที่ จำกัด มากนี้สภาพแวดล้อม Jupyter ปัจจุบัน (เชื่อมต่อ) มีความไวมากกว่าสภาพแวดล้อม IPython
np.show_config()
ยาวเกินไปที่จะโพสต์ แต่โดยสรุป: IPython envs นั้นใช้ BLAS / LAPACK Colab ใช้ OpenBLAS ใน IPython Linux envs ไลบรารี BLAS ถูกติดตั้งระบบ - ใน Jupyter และ Colab พวกเขามาจาก / opt / conda / lib
UPDATE : คำตอบที่ยอมรับนั้นถูกต้อง แต่กว้างและไม่สมบูรณ์ คำถามยังคงเปิดอยู่สำหรับทุกคนที่สามารถอธิบายพฤติกรรมในระดับรหัสได้นั่นคืออัลกอริธึมที่แน่นอนที่ใช้โดยnp.dot
และอธิบายถึง 'ความไม่สอดคล้องที่สอดคล้องกัน' ที่สังเกตได้ในผลลัพธ์ข้างต้น (ดูความคิดเห็นด้วย) นี่คือการใช้งานโดยตรงนอกเหนือจากการถอดรหัสของฉัน: sdot.c - arraytypes.c.src
ndarrays
มักจะไม่สนใจการสูญเสียความแม่นยำของตัวเลข เพราะเพื่อความเรียบง่ายพวกมันreduce-sum
ไปตามแต่ละแกนลำดับของการดำเนินการอาจไม่ใช่สิ่งที่ดีที่สุด ... โปรดทราบว่าหากคุณคำนึงถึงข้อผิดพลาดที่แม่นยำคุณอาจใช้เช่นกันfloat64