Hướng dẫn dùng python broadcasting python

Thuật ngữ broadcast đề cập đến khả năng của NumPy để xử lý các mảng có hình dạng khác nhau trong các phép toán số học. Các phép toán số học trên mảng thường được thực hiện trên các phần tử tương ứng. Nếu hai mảng có hình dạng hoàn toàn giống nhau thì các thao tác này được thực hiện trơn tru.

Ví dụ 1 :

import numpy as np 

a = np.array([1,2,3,4]) 
b = np.array([10,20,30,40]) 
c = a * b 
print c

Kết quả :

Nếu kích thước của hai mảng không giống nhau, thì không thể thực hiện các thao tác giữa phần tử với phần tử. Tuy nhiên, các thao tác trên các mảng có hình dạng không giống nhau vẫn có thể thực hiện được trong NumPy, vì khả năng broadcasting. Mảng nhỏ hơn được phát tới kích thước của mảng lớn hơn để chúng có hình dạng tương thích.

Có thể broadcasting nếu các quy tắc sau được thỏa mãn:

  • Mảng có ndim nhỏ hơn mảng kia được thêm chữ '1' trong shape.
  • Kích thước trong mỗi kích thước của shape đầu ra là tối đa của kích thước đầu vào trong kích thước đó.
  • Một đầu vào có thể được sử dụng trong tính toán, nếu kích thước của nó trong một thứ nguyên cụ thể khớp với kích thước đầu ra hoặc giá trị của nó chính xác là 1.
  • Nếu đầu vào có kích thước thứ nguyên là 1, thì mục nhập dữ liệu đầu tiên trong thứ nguyên đó sẽ được sử dụng cho tất cả các phép tính dọc theo thứ nguyên đó

Một tập hợp các mảng được cho là có thể broacast nếu các quy tắc trên tạo ra kết quả hợp lệ và một trong những điều sau là đúng:

  • Các mảng có hình dạng giống hệt nhau.
  • Mảng có cùng số kích thước và độ dài của mỗi thứ nguyên là độ dài chung hoặc 1.
  • Mảng có quá ít kích thước có thể có hình dạng của nó được thêm vào trước với kích thước có độ dài 1, do đó thuộc tính đã nêu ở trên là đúng.

Ví dụ 2 :

import numpy as np 
a = np.array([[0.0,0.0,0.0],[10.0,10.0,10.0],[20.0,20.0,20.0],[30.0,30.0,30.0]]) 
b = np.array([1.0,2.0,3.0])  
   
print 'First array:' 
print a 
print '\n'  
   
print 'Second array:' 
print b 
print '\n'  
   
print 'First Array + Second Array' 
print a + b

Kết quả :

First array:
[[ 0. 0. 0.]
 [ 10. 10. 10.]
 [ 20. 20. 20.]
 [ 30. 30. 30.]]

Second array:
[ 1. 2. 3.]

First Array + Second Array
[[ 1. 2. 3.]
 [ 11. 12. 13.]
 [ 21. 22. 23.]
 [ 31. 32. 33.]]

Hình sau minh họa cách mảng b được broadcast để tương thích với a.

Nội Dung

  • 1. Broadcasting trong Numpy là gì?
  • 2. Broadcasting mảng Numpy cùng kích thước
  • 3. Broadcasting mảng Numpy không cùng kích thước

Thuật ngữ Broadcasting trong Numpy mô tả cách xử lý các mảng trong Numpy có kích thước khác nhau thông qua các phép tính toán học. Theo một số ràng buộc nhất định, mảng nhỏ hơn được sẽ được “Broadcasting ” trên mảng lớn hơn để chúng có hình dạng tương thích với nhau.

Broadcasting còn cung cấp sẵn một phương tiện vectơ hóa các hoạt động của mảng để lặp lại xảy ra trong C thay vì Python. Nó thực hiện điều này mà không cần tạo bản sao dữ liệu không cần thiết và thường dẫn đến việc triển khai thuật toán hiệu quả.

Các quy tắc Broadcasting trong Numpy:

  • Nếu 2 mảng khác kích thước, mảng có ndim nhỏ hơn mảng kia được thêm ‘1’ vào (bên trái) shape của mảng đó.
  • Nếu shape của 2 mảng không tương ứng ở bất cứ chiều nào, mảng có shape bằng 1 tại chiều nào thì chiều đó sẽ giãn ra để tương ứng với shape kia.
  • Nếu trong bất kỳ kích thước nào mà kích thước không bằng nhau và không bằng 1, thì sẽ báo lỗi.

2. Broadcasting mảng Numpy cùng kích thước

Các phép toán cơ bản trong NumPy thường được thực hiện trên các cặp phần tử với nhau. Trong trường hợp đơn giản nhất, hai mảng phải có hình dạng hoàn toàn giống nhau, như trong ví dụ sau thực hiện nhân hai mảng 1 chiều với nhau.

import numpy as np 

# Tao mang a,b co cung kich thuoc
a = np.array([1.0, 2.0, 3.0])
b = np.array([2.0, 2.0, 2.0])

# Thuc hien phep nhan giua mang a va mang b
result = a * b
print(result)

Kết quả:

[2. 4. 6.]

Phép toán trên thực hiện nhân các cặp phần tử trong cả ma trận a và ma trận b. Hình ảnh dưới đây minh họa việc nhân hai ma trận 1 chiều có cùng kích thước:

Chúng ta cũng có thể nhân mảng với một số vô hướng bên ngoài của mảng, và cách thức thực hiện phép nhân này cũng sẽ là nhân từng phần tử trong mảng với số vô hướng đó. Cùng xem xét ví dụ dưới đây, chúng ta sẽ nhân một mảng cho một số sẽ cho ra kết quả tương tự với ví dụ trên:

import numpy as np 

# Tao mang a
a = np.array([1.0, 2.0, 3.0])
# Khai bao bien b = 2
b = 2

# Thuc hien phep nhan mang a * 2
result = a * b
print(result)

Kết quả:

[2. 4. 6.]

Việc nhân mảng 1 chiều với một số bên ngoài cũng thực hiện nhân từng phần tử có trong mảng với số đó. Hình ảnh dưới đây minh họa việc nhân mảng với một số:

Tương tự, ta cũng có thể thực hiện phép nhân có cùng kích thước giứa hai mảng hai chiều như sau:

import numpy as np 

# Tao mang a, b cung kich thuoc
a = np.array([
    [1.0, 2.0],
    [2.0, 3.0]
])

b = np.array([
    [2.0, 2.0],
    [2.0, 2.0]
])

# Thuc hien phep nhan giua mang a va mang b
result = a * b
print(result)

Kết quả:

[[2. 4.]
 [4. 6.]]

Nhân mảng hai chiều với một số bên ngoài mảng cũng cho ra kết quả tương tự kết quả trên!

import numpy as np 

# Tao mang a
a = np.array([
    [1.0, 2.0],
    [2.0, 3.0]
])

# Khai bao bien b = 2
b = 2

# Thuc hien phep nhan giua mang a va mang b
result = a * b
print(result)

Kết quả:

[[2. 4.]
 [4. 6.]]

3. Broadcasting mảng Numpy không cùng kích thước

Mảng không cùng kích thước khi thực hiện phép tính với nhau có thể là các giữa các mảng 1 chiều thực hiện phép toán với mảng 2 chiều hoặc các mảng 2 chiều thực hiện phép toán với mảng 3 chiều….. Nghĩa là, shape của hai mảng sẽ là không giống nhau để thực hiện phép tính trên từng cặp phần tử có trong hai mảng.

Ví dụ đơn giản nhất, ta có một mảng 2 chiều (ma trận) có kích thước 2 x 3 mảng này thực hiện phép toán cộng với mảng 1 chiều (vectơ) có kích thước 1 x 3 như sau:

import numpy as np 

# Tao mang 2 chieu 2 x 3 
a = np.array([
    [0, 0, 0],
    [10, 10, 10]
])

# Khai bao mang 1 chieu kich thuoc 3
b = np.array([1, 2, 3])


# Thuc hien phep cong giua mang a va mang b
result = a + b
print(result)

Kết quả:

[[ 1  2  3]
 [11 12 13]]

Mặc dù hai mảng trên không có chung shape thế nhưng khi thực hiện phép toán vẫn cho ra được kết quả là một ma trận có kích thước 2 x 3. Lý do là vì: Mảng a là mảng 2 chiều có kích thước 2 x 3, mảng b là mảng 1 chiều có kích thước 1 x 3 Numpy sẽ dựa theo các quy tắc để chuyển đổi shape của mảng như sau:

  • Theo quy tắc 1, sẽ thêm “1” vào phía bên trái shape của mảng – khi đó mảng b sẽ có kích thước mới là 2 x 3 và hoàn toàn có thể thực hiện phép toán cộng với mảng a giống như 2 mảng có cùng kích thước
  • Theo quy tắc 2, chiều có giá trị = 1 sẽ giãn ra để tương ứng với shape còn lại và khi đó mảng b từ kích thước 1 x 3 sẽ được giãn ra thành kích thước 2 x 3 để tương ứng với mảng a và thực hiện phép toán cộng.
  • Kết quả sau khi cộng hai mảng trên sẽ là một mảng 2 chiều có kích thước là 2 x 3 là do mảng nhỏ hơn được sẽ được “Broadcasting ” trên mảng lớn hơn để chúng có hình dạng tương thích với nhau.

Hình ảnh dưới đây miêu tả rõ hơn về việc thực hiện phép toán cộng giữa mảng 2 chiều (ma trận) có kích thước 2 x 3 với mảng 1 chiều (vectơ) có kích thước 1 x 3 và cho ra kết quả là 1 mảng có kích thước 2 x 3

Tương tự với phép toán cộng, chúng ta cũng có thể sử dụng phép toán nhân giữa hai mảng không cùng shape. Mảng a sẽ là mảng 2 chiều có kích thước x 3 và mảng b sẽ là mảng 1 chiều có kích thước x 3 – khi đó a * b sẽ được thực hiện như sau:

import numpy as np 

# Tao mang 2 chieu 2 x 3 
a = np.array([
    [0, 0, 0],
    [10, 10, 10]
])

# Khai bao mang 1 chieu kich thuoc 3
b = np.array([1, 2, 3])

# Thuc hien phep nhan giua mang a va mang b
result = a * b
print(result)

Kết quả:

[[ 0  0  0]
 [10 20 30]]