ในการเรียนรู้ Neural Network เราจะพบเจอโค้ดที่ใช้ List, Vector, NumPy Array ไปจนถึง High-Order Tensor หมายถึง Array ที่มีมากกว่า 2 มิติขึ้นไป เช่น 3 มิติ 4 มิติ หรือ 5 มิติ จนเป็นเรื่องธรรมดา ใน ep นี้เราจะมาเรียนรู้การใช้งาน Tensor ทำความเข้าใจ element-wise, broadcasting operations

Vector และ List คืออะไร

Vector (เวกเตอร์) และ List (ลิสต์) คือ ภาชนะบรรจุข้อมูลที่มีขนาดเท่ากัน ชนิดเดียวกัน ที่มี 1 มิติ เช่น [1, 2, 3, 4]

NumPy Array และ Matrix คืออะไร

NumPy Array (นัมไพ อาเรย์) และ Matrix (เมตริกซ์) คือ ภาชนะบรรจุข้อมูลที่มีขนาดเท่ากัน ชนิดเดียวกัน ที่มี 2 มิติ เช่น
[[1, 2, 3],
[4, 5, 6] ,
[7, 8, 9]]

Tensor คืออะไร

Tensor (เทนเซอร์) คือ ภาชนะบรรจุข้อมูลที่มีขนาดเท่ากัน ชนิดเดียวกัน ที่มีกี่มิติก็ได้ เช่น รูปสี ความละเอียด 224 x 224 Pixel จำนวน 10 รูป จะมีข้อมูล 4 มิติ ดังนี้ (10, 3, 224, 224) และสมมติเรามีข้อมูล 10 รูปแบบนี้ จาก 12 เดือน ก็จะเป็น 5 มิติ (12, 10, 3, 224, 224)

เราสามารถออกแบบมิติข้อมูลเพื่อใช้งานได้อย่างอิสระ

Scheme of construction of hypercube up to 4D 0D is point 0D -> 1D : From point to segment 1D -> 2D : From segment to square 2D -> 3D : From square to cube 3D -> 4D : From cube to tesseract. Credit: https://en.wikipedia.org/wiki/Hypercube_internetwork_topology
Scheme of construction of hypercube up to 4D 0D is point 0D -> 1D : From point to segment 1D -> 2D : From segment to square 2D -> 3D : From square to cube 3D -> 4D : From cube to tesseract. Credit: https://en.wikipedia.org/wiki/Hypercube_internetwork_topology

หมายเหตุ

  1. NumPy Array จริง ๆ คือ N-Dimension (ndarray) ก็สามารถใช้บรรจุข้อมูล กี่มิติก็ได้เช่นกัน แต่นิยมใช้ 1-2 มิติ
  2. 3 ในรูปสี คือ 3 Channels RGB
  3. การเรียงมิติไหนก่อนหลัง ขึ้นกับแต่ละ API
  4. Rank Number หมายถึง จำนวนมิติ ของ Tensor เช่น ตัวอย่างด้านบน (10, 3, 224, 224) จะเรียกว่า Rank 4 Tensor

ข้อดีของ Tensor เปรียบเทียบกับ NumPy Array

Tensor นั้นมีข้อดีหลายอย่างกว่า NumPy Array คือ สามารถใช้ GPU และรองรับ Autograd สามารถคำนวน Diff หา Gradient ให้อัตโนมัติ ทำให้เหมาะที่จะนำมาใช้สร้าง Deep Neural Network ในปัจจุบัน ที่เทรนโมเดลด้วยอัลกอริทึม Gradient Descent และ Backpropagation

NVIDIA Tesla K40 GPU Accelerator. Credit https://nvidianews.nvidia.com/news/nvidia-launches-world-s-fastest-accelerator-for-supercomputing-and-big-data-analytics
NVIDIA Tesla K40 GPU Accelerator. Credit https://nvidianews.nvidia.com/news/nvidia-launches-world-s-fastest-accelerator-for-supercomputing-and-big-data-analytics

และเมื่อมี GPU แทนที่เราจะเขียนโปรแกรม วน Loop ทำ Operations ทีละ Element เราจึงต้องปรับ Mindset ออกแบบด้วยแนวคิด Vectorization คือ ทำทุกอย่างให้เป็น Vector, Array หรือ Tensor แล้วทำ Operation นั้น ๆ พร้อม ๆ กันทีเดียวหมด ขนานกันไปเลย ด้วย GPU ซึ่งจะช่วยเพิ่มความเร็วขึ้นกว่าวน Loop ทำทีละอัน หลายร้อย หลายพันเท่า

เรามาเริ่มกันเลย

Open In Colab

0. Import

In [2]:
import torch
from torch import tensor

1. Element-wise operations

Element-wise Operations หมายถึง ทำ Operations แยกคนละตัวเรียงตามตำแหน่ง ตัวแรกทำกับตัวแรก ตัวที่สองทำกับตัวที่สอง ... โดยอาจจะทำไปพร้อม ๆ กัน ทำขนานกันไปก็ได้

1.1 Basic operations

In [3]:
a = tensor([1., 2., 3., 4.])

บวก ลบ คูณ หาร ยกกำลัง กับ ตัวเลขธรรมดา ใส่ . จุด หลังตัวเลข หมายถึง ให้เป็นเลขแบบ Float จุดทศนิยม รองรับการคำนวนคณิตศาสตร์ เช่น 1. เหมือนกับ 1.0

In [4]:
a + 1
Out[4]:
tensor([2., 3., 4., 5.])
In [5]:
2**a
Out[5]:
tensor([ 2.,  4.,  8., 16.])

บวก ลบ คูณ หาร ยกกำลัง กับ tensor ด้วยกัน

In [6]:
b = torch.ones(4) + 1
b
Out[6]:
tensor([2., 2., 2., 2.])
In [7]:
a - b
Out[7]:
tensor([-1.,  0.,  1.,  2.])
In [8]:
a * b
Out[8]:
tensor([2., 4., 6., 8.])
In [9]:
j = torch.arange(5)
j
Out[9]:
tensor([0, 1, 2, 3, 4])
In [10]:
2**(j + 1) - j
Out[10]:
tensor([ 2,  3,  6, 13, 28])

1.2 เปรียบเทียบความเร็ว

Tensor

In [11]:
a = torch.arange(10000)
%timeit a + 1  
14.7 µs ± 174 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

วน loop array

In [12]:
l = range(10000)
%timeit [i+1 for i in l] 
614 µs ± 2.28 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

tensor เร็วกว่าประมาณเกือบ 40 เท่า เนื่องจากเป็นการประมวลผลแบบขนาน ไม่ได้ทำทีละ item เรียงไปเรื่อย ๆ

1.3 Multiplication

In [13]:
c = torch.ones((3, 3))
c
Out[13]:
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]])

คูณแบบปกติ จะเป็น คูณแบบ element-wise ไม่ได้คูณแบบ matrix

In [14]:
c * c     
Out[14]:
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]])

คูณแบบ matrix dot product

In [15]:
c.matmul(c)
Out[15]:
tensor([[3., 3., 3.],
        [3., 3., 3.],
        [3., 3., 3.]])

1.4 Comparison Operations

Operation เปรียบเทียบ ก็ element-wise

In [16]:
a = tensor([1, 2, 3, 4])
b = tensor([4, 2, 2, 4])

1 คือ True, 0 คือ False

In [17]:
a == b
Out[17]:
tensor([0, 1, 0, 1], dtype=torch.uint8)
In [18]:
a > b
Out[18]:
tensor([0, 0, 1, 0], dtype=torch.uint8)

1.5 Transcendental functions

ฟังก์ชันคณิตศาสตร์ ก็ element-wise

In [19]:
a = torch.arange(5.)
In [20]:
torch.sin(a)
Out[20]:
tensor([ 0.0000,  0.8415,  0.9093,  0.1411, -0.7568])
In [21]:
torch.log(a)
Out[21]:
tensor([  -inf, 0.0000, 0.6931, 1.0986, 1.3863])
In [22]:
torch.exp(a)
Out[22]:
tensor([ 1.0000,  2.7183,  7.3891, 20.0855, 54.5981])

2. Broadcasting

ในกรณี 2 ฝั่งมีสมาชิกไม่เท่ากัน จะเกิดการ Broadcasting กระจายสมาชิกจาก 1 ให้กลายเป็นเท่ากัน ก่อนทำ Element-wise Operation

ใน Numpy, TensorFlow และ Pytorch มิติแรก 0 จะเป็น Row มิติที่สอง 1 จะเป็น Column

In [23]:
a = torch.arange(0, 40, 10).repeat(1, 1, 3).view(3, 4).t()
a
Out[23]:
tensor([[ 0,  0,  0],
        [10, 10, 10],
        [20, 20, 20],
        [30, 30, 30]])

2.1 Row Broadcasting

กระจาย 1 Row เป็น 4 Row ก่อนบวก

In [24]:
b = tensor([0, 1, 2])
b
Out[24]:
tensor([0, 1, 2])
In [25]:
a + b
Out[25]:
tensor([[ 0,  1,  2],
        [10, 11, 12],
        [20, 21, 22],
        [30, 31, 32]])

2.2 Column Broadcasting

กระจาย 1 Column เป็น 3 Column ก่อนบวก

In [26]:
c = tensor([[0], [1], [2], [3]])
c
Out[26]:
tensor([[0],
        [1],
        [2],
        [3]])
In [27]:
a + c
Out[27]:
tensor([[ 0,  0,  0],
        [11, 11, 11],
        [22, 22, 22],
        [33, 33, 33]])

2.3 Row and Column Broadcasting

In [28]:
d = tensor([2])
d
Out[28]:
tensor([2])

กระจายเป็น 4 Row 3 Column ก่อนบวก

In [29]:
a+d
Out[29]:
tensor([[ 2,  2,  2],
        [12, 12, 12],
        [22, 22, 22],
        [32, 32, 32]])

หมายเหตุ การ Broadcasting ไม่ได้จำกัดอยู่แค่ 2 มิติ เราสามารถ Broadcast ได้ในทุก ๆ มิติ เช่น broardcast มิติที่ 4 ใน tensor 5 มิติ เป็นต้น

3. Indexing and Slicing

In [30]:
a = torch.ones((4, 5))
a
Out[30]:
tensor([[1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1.]])

3.1 Indexing

Indexing คือ การเลือกเฉพาะ Row, Column ที่ต้องการขึ้นมา เช่น ให้ Row ที่ 0 เป็น ค่า 2

ไม่ได้ระบุมิติที่ 2 ให้ถือว่าเป็น : คือเอาทุก Column

In [31]:
a[0] = 2
a
Out[31]:
tensor([[2., 2., 2., 2., 2.],
        [1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1.]])

ให้ Column ที่ 1 (เริ่มต้นที่ 0) เป็น 3

หมายเหตุ : (เครื่องหมาย colon) แปลว่าทุกอัน ถ้าอยู่ตำแหน่งแรก แปลว่าทุก Row

In [32]:
a[:, 1] = 3
a
Out[32]:
tensor([[2., 3., 2., 2., 2.],
        [1., 3., 1., 1., 1.],
        [1., 3., 1., 1., 1.],
        [1., 3., 1., 1., 1.]])

ให้ Column ที่ 1 จากสุดท้าย เป็น 4

Index ติดลบ แปลว่า นับจากท้ายสุด

In [33]:
a[:, -1] = 4
a
Out[33]:
tensor([[2., 3., 2., 2., 4.],
        [1., 3., 1., 1., 4.],
        [1., 3., 1., 1., 4.],
        [1., 3., 1., 1., 4.]])

3.2 Slicing

Slicing เลือกเอาช่วง Index ในลำดับที่ต้องการ

In [34]:
x = tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
x
Out[34]:
tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

เริ่มที่ 3 ถึง 6 (ไม่รวม 6)

In [35]:
x[3:6]
Out[35]:
tensor([3, 4, 5])

เริ่มที่ 1 ถึง 7 (ไม่รวม 7) ข้ามทีละ 2

In [36]:
x[1:7:2]
Out[36]:
tensor([1, 3, 5])

เลือกอันดับ 2 จากสุดท้าย ถึง อันดับ 10 (ไม่รวม 10)

In [37]:
x[-2:10]
Out[37]:
tensor([8, 9])

เลือกตั้งแต่ 5 เป็นต้นไป

In [38]:
x[5:]
Out[38]:
tensor([5, 6, 7, 8, 9])

เลือกตั้งแต่ต้นจนถึง 5 (ไม่รวม 5)

In [39]:
x[:5]
Out[39]:
tensor([0, 1, 2, 3, 4])

4. Summation

การหาผลรวมของ Tensor เนื่องจาก Tensor มีหลายมิติ เราจึงต้องกำหนดว่า จะคิดตามมิติไหน ดังตัวอย่าง

In [82]:
a = tensor([[1, 1, 1], [2, 2, 2], [3, 3, 3]])
a
Out[82]:
tensor([[1, 1, 1],
        [2, 2, 2],
        [3, 3, 3]])

สมมติ a คือ Tensor 2 มิติ ขนาด 3 x 3

In [83]:
a.shape
Out[83]:
torch.Size([3, 3])

Sum ตามแนวมิติที่ 0 คือ Row

In [90]:
s = a.sum(0)
s
Out[90]:
tensor([6, 6, 6])
In [91]:
s.shape
Out[91]:
torch.Size([3])

Sum ตามแนวมิติที่ 1 คือ Column

In [92]:
s = a.sum(1)
s
Out[92]:
tensor([3, 6, 9])
In [93]:
s.shape
Out[93]:
torch.Size([3])

เมื่อมีการ Sum มิตินั้นจะหายไป ทำให้ Tensor 2 มิติ กลายเป็น Tensor 1 มิติ หรือ Vector ถ้าเราต้องการให้มิติคงเดิม ให้เราใส่ keepdim=True

In [86]:
s = a.sum(0, keepdim=True)
s
Out[86]:
tensor([[6, 6, 6]])
In [87]:
s.shape
Out[87]:
torch.Size([1, 3])
In [88]:
s = a.sum(1, keepdim=True)
s
Out[88]:
tensor([[3],
        [6],
        [9]])
In [89]:
s.shape
Out[89]:
torch.Size([3, 1])

Sum มิติท้ายสุด

In [79]:
a.sum(-1)
Out[79]:
tensor([3, 6, 9])

มิติ ติดลบ แปลว่า นับจากท้ายสุด

Credit

In [ ]:
 

แชร์ให้เพื่อน:

Keng Surapong on FacebookKeng Surapong on GithubKeng Surapong on Linkedin
Keng Surapong
Project Manager at Bua Labs
The ultimate test of your knowledge is your capacity to convey it to another.

Published by Keng Surapong

The ultimate test of your knowledge is your capacity to convey it to another.

Enable Notifications    Ok No thanks