ใส่ง่ายๆtorch.Tensor.view()
ซึ่งได้แรงบันดาลใจจากnumpy.ndarray.reshape()
หรือnumpy.reshape()
สร้างมุมมองใหม่ของเทนเซอร์ตราบใดที่รูปร่างใหม่เข้ากันได้กับรูปร่างของเทนเซอร์เดิม
มาทำความเข้าใจในรายละเอียดโดยใช้ตัวอย่างที่เป็นรูปธรรม
In [43]: t = torch.arange(18)
In [44]: t
Out[44]:
tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17])
ด้วยเมตริกซ์นี้t
ของรูปร่าง(18,)
ใหม่มุมมองที่สามารถเพียงถูกสร้างขึ้นสำหรับรูปร่างต่อไปนี้:
(1, 18)
หรือเท่ากัน (1, -1)
หรือ หรือเท่ากัน หรือ หรือเท่ากัน หรือ หรือเท่ากัน หรือ หรือเท่ากัน หรือหรือเทียบเท่า หรือ(-1, 18)
(2, 9)
(2, -1)
(-1, 9)
(3, 6)
(3, -1)
(-1, 6)
(6, 3)
(6, -1)
(-1, 3)
(9, 2)
(9, -1)
(-1, 2)
(18, 1)
(18, -1)
(-1, 1)
ในฐานะที่เรามีอยู่แล้วสามารถสังเกตจากรูปร่าง tuples ข้างต้นคูณขององค์ประกอบของ tuple รูปร่าง (เช่น2*9
, 3*6
ฯลฯ )มักจะต้องเท่ากับจำนวนขององค์ประกอบในเมตริกซ์เดิม (18
ในตัวอย่างของเรา)
สิ่งที่ต้องสังเกตอีกอย่างก็คือเราใช้-1
หนึ่งในสถานที่ในแต่ละสิ่งอันดับ tuples โดยใช้-1
เราจะขี้เกียจในการทำคำนวณตัวเองและค่อนข้างมอบหมายงานให้ PyTorch จะทำคำนวณมูลค่าที่สำหรับรูปร่างเมื่อมันสร้างใหม่มุมมอง สิ่งหนึ่งที่สำคัญที่จะต้องทราบคือการที่เราสามารถเพียงใช้ครั้งเดียว-1
ใน tuple รูปร่าง ค่าที่เหลือควรให้เราอย่างชัดเจน PyTorch อื่นจะบ่นโดยการขว้างปาRuntimeError
:
RuntimeError: สามารถอนุมานได้หนึ่งมิติเท่านั้น
ดังนั้นด้วยรูปร่างที่กล่าวมาทั้งหมด PyTorch จะกลับมามีมุมมองใหม่ของเมตริกซ์เดิมt
เสมอ โดยทั่วไปหมายความว่าเพียงเปลี่ยนข้อมูลกางเกงของเทนเซอร์สำหรับแต่ละมุมมองใหม่ที่ร้องขอ
ด้านล่างนี้คือตัวอย่างบางส่วนแสดงวิธีก้าวหน้าของเทนเซอร์ที่มีการเปลี่ยนแปลงกับใหม่แต่ละมุมมอง
# stride of our original tensor `t`
In [53]: t.stride()
Out[53]: (1,)
ตอนนี้เราจะเห็นความก้าวหน้าสำหรับมุมมองใหม่:
# shape (1, 18)
In [54]: t1 = t.view(1, -1)
# stride tensor `t1` with shape (1, 18)
In [55]: t1.stride()
Out[55]: (18, 1)
# shape (2, 9)
In [56]: t2 = t.view(2, -1)
# stride of tensor `t2` with shape (2, 9)
In [57]: t2.stride()
Out[57]: (9, 1)
# shape (3, 6)
In [59]: t3 = t.view(3, -1)
# stride of tensor `t3` with shape (3, 6)
In [60]: t3.stride()
Out[60]: (6, 1)
# shape (6, 3)
In [62]: t4 = t.view(6,-1)
# stride of tensor `t4` with shape (6, 3)
In [63]: t4.stride()
Out[63]: (3, 1)
# shape (9, 2)
In [65]: t5 = t.view(9, -1)
# stride of tensor `t5` with shape (9, 2)
In [66]: t5.stride()
Out[66]: (2, 1)
# shape (18, 1)
In [68]: t6 = t.view(18, -1)
# stride of tensor `t6` with shape (18, 1)
In [69]: t6.stride()
Out[69]: (1, 1)
นั่นคือความมหัศจรรย์ของview()
ฟังก์ชั่น เพียงแค่เปลี่ยนความก้าวหน้าของเทนเซอร์ (ต้นฉบับ) สำหรับแต่ละมุมมองใหม่ตราบใดที่รูปร่างของมุมมองใหม่เข้ากันได้กับรูปร่างเดิม
อีกสิ่งหนึ่งที่น่าสนใจอาจสังเกตจากความก้าวหน้าอย่างเป็นอันดับที่มูลค่าขององค์ประกอบใน 0 ที่ณตำแหน่งเท่ากับมูลค่าขององค์ประกอบใน 1 เซนต์ตำแหน่งของ tuple รูปร่าง
In [74]: t3.shape
Out[74]: torch.Size([3, 6])
|
In [75]: t3.stride() |
Out[75]: (6, 1) |
|_____________|
นี้เป็นเพราะ:
In [76]: t3
Out[76]:
tensor([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17]])
กางเกง(6, 1)
บอกว่าจะไปจากองค์ประกอบหนึ่งไปยังองค์ประกอบถัดไป 0 THมิติเราจะต้องกระโดดหรือใช้ขั้นตอนที่ 6 (คือจะไปจาก0
ที่จะ6
มีใครที่จะใช้ขั้นตอนที่ 6.) แต่จะไปจากองค์ประกอบหนึ่งไปยังองค์ประกอบถัดไปใน 1 เซนต์มิติเราเพียงแค่ต้องการเพียงขั้นตอนเดียว (สำหรับเช่นจะไปจาก2
การ3
)
ดังนั้นข้อมูลความก้าวหน้าจึงเป็นหัวใจสำคัญของการเข้าถึงองค์ประกอบจากหน่วยความจำสำหรับการคำนวณ
ฟังก์ชั่นนี้จะคืนค่ามุมมองและเหมือนกับการใช้torch.Tensor.view()
งานตราบใดที่รูปร่างใหม่เข้ากันได้กับรูปร่างของเทนเซอร์ดั้งเดิม มิฉะนั้นจะส่งคืนสำเนา
อย่างไรก็ตามการบันทึกของ torch.reshape()
เตือนว่า:
อินพุตที่ต่อเนื่องและอินพุตที่มี strides ที่รองรับสามารถถูก reshaped โดยไม่ต้องคัดลอก แต่ไม่ควรขึ้นอยู่กับพฤติกรรมการคัดลอกและการดู
reshape
ใน PyTorch!