.. raw:: html
.. raw:: html
.. raw:: html
.. _sec_ndarray:
Thao tác với Dữ liệu
====================
.. raw:: html
Muốn thực hiện bất cứ điều gì, chúng ta đều cần một cách nào đó để lưu
trữ và thao tác với dữ liệu. Thường sẽ có hai điều quan trọng chúng ta
cần làm với dữ liệu: (i) thu thập; và (ii) xử lý sau khi đã có dữ liệu
trên máy tính. Sẽ thật vô nghĩa khi thu thập dữ liệu mà không có cách để
lưu trữ nó, vậy nên trước tiên hãy cùng làm quen với dữ liệu tổng hợp.
Để bắt đầu, chúng tôi giới thiệu mảng :math:`n` chiều (``ndarray``) –
công cụ chính trong MXNET để lưu trữ và biến đổi dữ liệu. Trong MXNet,
``ndarray`` là một lớp và mỗi thực thể của lớp đó là “một ``ndarray``”.
.. raw:: html
Nếu bạn từng làm việc với NumPy, gói tính toán phổ biến nhất trong
Python, bạn sẽ thấy mục này quen thuộc. Việc này là có chủ đích. Chúng
tôi thiết kế ``ndarray`` trong MXNet là một dạng mở rộng của ``ndarray``
trong NumPy với một vài tính năng đặc biệt. Thứ nhất, ``ndarray`` trong
MXNet hỗ trợ tính toán phi đồng bộ trên CPU, GPU, và các kiến trúc phân
tán đám mây, trong khi NumPy chỉ hỗ trợ tính toán trên CPU. Thứ hai,
``ndaray`` trong MXNet hỗ trợ tính vi phân tự động. Những tính chất này
khiến ``ndarray`` của MXNet phù hợp với học sâu. Thông qua cuốn sách,
nếu không nói gì thêm, chúng ta ngầm hiểu ``ndarray`` là ``ndarray`` của
MXNet.
.. raw:: html
.. raw:: html
.. raw:: html
Bắt đầu
-------
.. raw:: html
Trong mục này, mục tiêu của chúng tôi là trang bị cho bạn các kiến thức
toán cơ bản và cài đặt các công cụ tính toán mà bạn sẽ xây dựng dựa trên
nó xuyên suốt cuốn sách này. Đừng lo nếu bạn gặp khó khăn với các khái
niệm toán khó hiểu hoặc các hàm trong thư viện tính toán. Các mục tiếp
theo sẽ nhắc lại những khái niệm này trong từng ngữ cảnh kèm theo ví dụ
thực tiễn. Mặt khác, nếu bạn đã có kiến thức nền tảng và muốn đi sâu hơn
vào các nội dung toán, bạn có thể bỏ qua mục này.
.. raw:: html
Để bắt đầu, ta cần khai báo mô-đun ``np`` (``numpy``) và ``npx``
(``numpy_extension``) từ MXNet. Ở đây, mô-đun ``np`` bao gồm các hàm hỗ
trợ bởi NumPy, trong khi mô-đun ``npx`` chứa một tập các hàm mở rộng
được phát triển để hỗ trợ học sâu trong một môi trường giống với NumPy.
Khi sử dụng ``ndarray``, ta luôn cần gọi hàm ``set_np``: điều này nhằm
đảm bảo sự tương thích của việc xử lý ``ndarray`` bằng các thành phần
khác của MXNet.
.. code:: python
from mxnet import np, npx
npx.set_np()
.. raw:: html
Một ``ndarray`` biểu diễn một mảng (có thể đa chiều) các giá trị số. Với
một trục, một ``ndarray`` tương ứng (trong toán) với một *vector*. Với
hai trục, một ``ndarray`` tương ứng với một *ma trận*. Các mảng với
nhiều hơn hai trục không có tên toán học cụ thể–chúng được gọi chung là
*tensor*.
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
Để bắt đầu, chúng ta sử dụng ``arange`` để tạo một vector hàng ``x``
chứa :math:`12` số nguyên đầu tiên bắt đầu từ :math:`0`, nhưng được khởi
tạo mặc định dưới dạng số thực. Mỗi giá trị trong một ``ndarray`` được
gọi là một *phần tử* của ``ndarray`` đó. Như vậy, có :math:`12` phần tử
trong ``ndarray`` ``x``. Nếu không nói gì thêm, một ``ndarray`` mới sẽ
được lưu trong bộ nhớ chính và được tính toán trên CPU.
.. code:: python
x = np.arange(12)
x
.. parsed-literal::
:class: output
array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11.])
.. raw:: html
Chúng ta có thể lấy *kích thước* (độ dài theo mỗi trục) của ``ndarray``
bằng thuộc tính ``shape``.
.. code:: python
x.shape
.. parsed-literal::
:class: output
(12,)
.. raw:: html
Nếu chỉ muốn biết tổng số phần tử của một ``ndarray``, nghĩa là tích của
tất cả các thành phần trong ``shape``, ta có thể sử dụng thuộc tính
``size``. Vì ta đang làm việc với một vector, cả ``shape`` và ``size``
của nó đều chứa cùng một phần tử duy nhất.
.. code:: python
x.size
.. parsed-literal::
:class: output
12
.. raw:: html
Để thay đổi kích thước của một ``ndarray`` mà không làm thay đổi số
lượng phần tử cũng như giá trị của chúng, ta có thể gọi hàm ``reshape``.
Ví dụ, ta có thể biến đổi ``ndarray`` ``x`` trong ví dụ trên, từ một
vector hàng với kích thước (:math:`12`,) sang một ma trận với kích thước
(:math:`3`, :math:`4`). ``ndarray`` mới này chứa :math:`12` phần tử y
hệt, nhưng được xem như một ma trận với :math:`3` hàng và :math:`4` cột.
Mặc dù kích thước thay đổi, các phần tử của ``x`` vẫn giữ nguyên. Chú ý
rằng ``size`` giữ nguyên khi thay đổi kích thước.
.. code:: python
x = x.reshape(3, 4)
x
.. parsed-literal::
:class: output
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
.. raw:: html
.. raw:: html
.. raw:: html
Việc chỉ định cụ thể mọi chiều khi thay đổi kích thước là không cần
thiết. Nếu kích thước mong muốn là một ma trận với kích thước
(chiều_cao, chiều_rộng), thì sau khi biết chiều_rộng, chiều_cao có thể
được ngầm suy ra. Tại sao ta lại cần phải tự làm phép tính chia? Trong
ví dụ trên, để có được một ma trận với :math:`3` hàng, chúng ta phải chỉ
định rõ rằng nó có :math:`3` hàng và :math:`4` cột. May mắn thay,
``ndarray`` có thể tự động tính một chiều từ các chiều còn lại. Ta có
thể dùng chức năng này bằng cách đặt ``-1`` cho chiều mà ta muốn
``ndarray`` tự suy ra. Trong trường hợp vừa rồi, thay vì gọi
``x.reshape(3, 4)``, ta có thể gọi ``x.reshape(-1, 4)`` hoặc
``x.reshape(3, -1)``.
.. raw:: html
Phương thức ``empty`` lấy một đoạn bộ nhớ và trả về một ma trận mà không
thay đổi các giá trị sẵn có tại đoạn bộ nhớ đó. Việc này có hiệu quả
tính toán đáng kể nhưng ta phải cẩn trọng bởi các phần tử đó có thể chứa
bất kỳ giá trị nào, kể cả các số rất lớn!
.. code:: python
np.empty((3, 4))
.. parsed-literal::
:class: output
array([[1.1001436e-26, 4.5648699e-41, 5.7875015e-19, 3.0751495e-41],
[0.0000000e+00, 0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
[0.0000000e+00, 0.0000000e+00, 0.0000000e+00, 0.0000000e+00]])
.. raw:: html
Thông thường ta muốn khởi tạo các ma trận với các giá trị bằng không,
bằng một, bằng hằng số nào đó hoặc bằng các mẫu ngẫu nhiên lấy từ một
phân phối cụ thể. Ta có thể tạo một ``ndarray`` biểu diễn một tensor với
tất cả các phần tử bằng :math:`0` và có kích thước (:math:`2`,
:math:`3`, :math:`4`) như sau:
.. raw:: html
.. raw:: html
.. code:: python
np.zeros((2, 3, 4))
.. parsed-literal::
:class: output
array([[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]],
[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]]])
.. raw:: html
Tương tự, ta có thể tạo các tensor với các phần tử bằng 1 như sau:
.. code:: python
np.ones((2, 3, 4))
.. parsed-literal::
:class: output
array([[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]],
[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]]])
.. raw:: html
Ta thường muốn lấy mẫu ngẫu nhiên cho mỗi phần tử trong một ``ndarray``
từ một phân phối xác suất. Ví dụ, khi xây dựng các mảng để chứa các tham
số của một mạng nơ-ron, ta thường khởi tạo chúng với các giá trị ngẫu
nhiên. Đoạn mã dưới đây tạo một ``ndarray`` có kích thước (:math:`3`,
:math:`4`) với các phần tử được lấy mẫu ngẫu nhiên từ một phân phối
Gauss (phân phối chuẩn) với trung bình bằng :math:`0` và độ lệch chuẩn
:math:`1`.
.. code:: python
np.random.normal(0, 1, size=(3, 4))
.. parsed-literal::
:class: output
array([[ 2.2122064 , 1.1630787 , 0.7740038 , 0.4838046 ],
[ 1.0434405 , 0.29956347, 1.1839255 , 0.15302546],
[ 1.8917114 , -1.1688148 , -1.2347414 , 1.5580711 ]])
.. raw:: html
Ta cũng có thể khởi tạo giá trị cụ thể cho mỗi phần tử trong ``ndarray``
mong muốn bằng cách đưa vào một mảng Python (hoặc mảng của mảng) chứa
các giá trị số. Ở đây, mảng ngoài cùng tương ứng với trục :math:`0`, và
mảng bên trong tương ứng với trục :math:`1`.
.. code:: python
np.array([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
.. parsed-literal::
:class: output
array([[2., 1., 4., 3.],
[1., 2., 3., 4.],
[4., 3., 2., 1.]])
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
Phép toán
---------
.. raw:: html
Cuốn sách này không nói về kỹ thuật phần mềm. Chúng tôi không chỉ hứng
thú với việc đơn giản đọc và ghi dữ liệu vào/từ các mảng mà còn muốn
thực hiện các phép toán trên các mảng này. Một vài phép toán đơn giản và
hữu ích nhất là các phép toán tác động lên *từng phần tử*
(*elementwise*). Các phép toán này hoạt động như những phép toán chuẩn
trên số vô hướng áp dụng lên từng phần tử của mảng. Với những hàm nhận
hai mảng đầu vào, phép toán theo từng thành phần được áp dụng trên từng
cặp phần tử tương ứng của hai mảng. Ta có thể tạo một hàm theo từng phần
tử từ một hàm bất kỳ ánh xạ từ một số vô hướng tới một số vô hướng.
.. raw:: html
Trong toán học, ta ký hiệu một toán tử *đơn ngôi* vô hướng (lấy một đầu
vào) bởi :math:`f: \mathbb{R} \rightarrow \mathbb{R}`. Điều này nghĩa là
hàm số ánh xạ từ một số thực bất kỳ (:math:`\mathbb{R}`) sang một số
thực khác. Tương tự, ta ký hiệu một toán tử *hai ngôi* vô hướng (lấy hai
đầu vào, trả về một đầu ra) bởi
:math:`f: \mathbb{R}, \mathbb{R} \rightarrow \mathbb{R}`. Cho trước hai
vector bất kỳ :math:`\mathbf{u}` và :math:`\mathbf{v}` *với cùng kích
thước*, và một toán tử hai ngôi :math:`f`, ta có thể tính được một
vector :math:`\mathbf{c} = F(\mathbf{u},\mathbf{v})` bằng cách tính
:math:`c_i \gets f(u_i, v_i)` cho mọi :math:`i` với :math:`c_i, u_i`, và
:math:`v_i` là các phần tử thứ :math:`i` của vector
:math:`\mathbf{c}, \mathbf{u}`, và :math:`\mathbf{v}`. Ở đây, chúng ta
tạo một hàm trả về vector
:math:`F: \mathbb{R}^d, \mathbb{R}^d \rightarrow \mathbb{R}^d` bằng cách
áp dụng hàm :math:`f` lên từng phần tử.
.. raw:: html
.. raw:: html
.. raw:: html
Trong MXNet, các phép toán tiêu chuẩn (``+``, ``-``, ``*``, ``/``, và
``**``) là các phép toán theo từng phần tử trên các tensor đồng kích
thước bất kỳ. Ta có thể gọi những phép toán theo từng phần tử lên hai
tensor đồng kích thước. Trong ví dụ dưới đây, các dấu phẩy được sử dụng
để tạo một tuple :math:`5` phần tử với mỗi phần tử là kết quả của một
phép toán theo từng phần tử.
.. code:: python
x = np.array([1, 2, 4, 8])
y = np.array([2, 2, 2, 2])
x + y, x - y, x * y, x / y, x ** y # The ** operator is exponentiation
.. parsed-literal::
:class: output
(array([ 3., 4., 6., 10.]),
array([-1., 0., 2., 6.]),
array([ 2., 4., 8., 16.]),
array([0.5, 1. , 2. , 4. ]),
array([ 1., 4., 16., 64.]))
.. raw:: html
Rất nhiều các phép toán khác có thể được áp dụng theo từng phần tử, bao
gồm các phép toán đơn ngôi như hàm mũ cơ số :math:`e`.
.. code:: python
np.exp(x)
.. parsed-literal::
:class: output
array([2.7182817e+00, 7.3890562e+00, 5.4598148e+01, 2.9809580e+03])
.. raw:: html
Ngoài các phép tính theo từng phần tử, ta cũng có thể thực hiện các phép
toán đại số tuyến tính, bao gồm tích vô hướng của hai vector và phép
nhân ma trận. Chúng ta sẽ giải thích những điểm quan trọng của đại số
tuyến tính (mà không cần kiến thức nền tảng) trong
:numref:`sec_linear-algebra`.
.. raw:: html
.. raw:: html
.. raw:: html
Ta cũng có thể *nối* nhiều ``ndarray`` với nhau, xếp chồng chúng lên
nhau để tạo ra một ``ndarray`` lớn hơn. Ta chỉ cần cung cấp một danh
sách các ``ndarray`` và khai báo chúng được nối theo trục nào. Ví dụ
dưới đây thể hiện cách nối hai ma trận theo hàng (trục :math:`0`, phần
tử đầu tiên của kích thước) và theo cột (trục :math:`1`, phần tử thứ hai
của kích thước). Ta có thể thấy rằng, cách thứ nhất tạo một ``ndarray``
với độ dài trục :math:`0` (:math:`6`) bằng tổng các độ dài trục
:math:`0` của hai ``ndarray`` đầu vào (:math:`3 + 3`); trong khi cách
thứ hai tạo một ``ndarray`` với độ dài trục :math:`1` (:math:`8`) bằng
tổng các độ dài trục :math:`1` của hai ``ndarray`` đầu vào
(:math:`4 + 4`).
.. code:: python
x = np.arange(12).reshape(3, 4)
y = np.array([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
np.concatenate([x, y], axis=0), np.concatenate([x, y], axis=1)
.. parsed-literal::
:class: output
(array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[ 2., 1., 4., 3.],
[ 1., 2., 3., 4.],
[ 4., 3., 2., 1.]]),
array([[ 0., 1., 2., 3., 2., 1., 4., 3.],
[ 4., 5., 6., 7., 1., 2., 3., 4.],
[ 8., 9., 10., 11., 4., 3., 2., 1.]]))
.. raw:: html
Đôi khi, ta muốn tạo một ``ndarray`` nhị phân thông qua các *mệnh đề
logic*. Lấy ``x == y`` làm ví dụ. Với mỗi vị trí, nếu giá trị của\ ``x``
và ``y`` tại vị trí đó bằng nhau thì phần tử tương ứng trong ``ndarray``
mới lấy giá trị :math:`1`, nghĩa là mệnh đề logic ``x == y`` là đúng tại
vị trí đó; ngược lại vị trí đó lấy giá trị :math:`0`.
.. code:: python
x == y
.. parsed-literal::
:class: output
array([[False, True, False, True],
[False, False, False, False],
[False, False, False, False]])
.. raw:: html
Lấy tổng mọi phần tử trong một ``ndarray`` tạo ra một ``ndarray`` với
chỉ một phần tử.
.. code:: python
x.sum()
.. parsed-literal::
:class: output
array(66.)
.. raw:: html
Ta cũng có thể thay ``x.sum()`` bởi ``np.sum(x)``.
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
Cơ chế Lan truyền
-----------------
.. raw:: html
.. raw:: html
Trong mục trên, ta đã thấy cách thực hiện các phép toán theo từng phần
tử với hai ``ndarray`` đồng kích thước. Trong những điều kiện nhất định,
thậm chí khi kích thước khác nhau, ta vẫn có thể thực hiện các phép toán
theo từng phần tử bằng cách sử dụng *cơ chế lan truyền* (*broadcasting
mechanism*). Cơ chế này hoạt động như sau: Thứ nhất, mở rộng một hoặc cả
hai mảng bằng cách lặp lại các phần tử một cách hợp lý sao cho sau phép
biến đổi này, hai ``ndarray`` có cùng kích thước. Thứ hai, thực hiện các
phép toán theo từng phần tử với hai mảng mới này.
.. raw:: html
Trong hầu hết các trường hợp, chúng ta lan truyền một mảng theo trục có
độ dài ban đầu là :math:`1`, như ví dụ dưới đây:
.. code:: python
a = np.arange(3).reshape(3, 1)
b = np.arange(2).reshape(1, 2)
a, b
.. parsed-literal::
:class: output
(array([[0.],
[1.],
[2.]]),
array([[0., 1.]]))
.. raw:: html
Vì ``a`` và ``b`` là các ma trận có kích thước lần lượt là
:math:`3\times1` và :math:`1\times2`, kích thước của chúng không khớp
nếu ta muốn thực hiện phép cộng. Ta *lan truyền* các phần tử của cả hai
ma trận thành các ma trận :math:`3\times2` như sau: lặp lại các cột
trong ma trận ``a`` và các hàng trong ma trận ``b`` trước khi cộng chúng
theo từng phần tử.
.. code:: python
a + b
.. parsed-literal::
:class: output
array([[0., 1.],
[1., 2.],
[2., 3.]])
.. raw:: html
.. raw:: html
.. raw:: html
Chỉ số và Cắt chọn mảng
-----------------------
.. raw:: html
Cũng giống như trong bất kỳ mảng Python khác, các phần tử trong một
``ndarray`` có thể được truy cập theo chỉ số. Tương tự, phần tử đầu tiên
có chỉ số :math:`0` và khoảng được cắt chọn bao gồm phần tử đầu tiên
nhưng *không tính* phần tử cuối cùng. Và trong các danh sách Python tiêu
chuẩn, chúng ta có thể truy cập các phần tử theo vị trí đếm ngược từ
cuối danh sách bằng cách sử dụng các chỉ số âm.
.. raw:: html
Vì vậy, ``[-1]`` chọn phần tử cuối cùng và ``[1:3]`` chọn phần tử thứ
hai và phần tử thứ ba như sau:
.. code:: python
x[-1], x[1:3]
.. parsed-literal::
:class: output
(array([ 8., 9., 10., 11.]),
array([[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]]))
.. raw:: html
Ngoài việc đọc, chúng ta cũng có thể viết các phần tử của ma trận bằng
cách chỉ định các chỉ số.
.. code:: python
x[1, 2] = 9
x
.. parsed-literal::
:class: output
array([[ 0., 1., 2., 3.],
[ 4., 5., 9., 7.],
[ 8., 9., 10., 11.]])
.. raw:: html
Nếu chúng ta muốn gán cùng một giá trị cho nhiều phần tử, chúng ta chỉ
cần trỏ đến tất cả các phần tử đó và gán giá trị cho chúng. Chẳng hạn,
``[0:2 ,:]`` truy cập vào hàng thứ nhất và thứ hai, trong đó ``:`` lấy
tất cả các phần tử dọc theo trục :math:`1` (cột). Ở đây chúng ta đã thảo
luận về cách truy cập vào ma trận, nhưng tất nhiên phương thức này cũng
áp dụng cho các vector và tensor với nhiều hơn :math:`2` chiều.
.. code:: python
x[0:2, :] = 12
x
.. parsed-literal::
:class: output
array([[12., 12., 12., 12.],
[12., 12., 12., 12.],
[ 8., 9., 10., 11.]])
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
Tiết kiệm Bộ nhớ
----------------
.. raw:: html
Ở ví dụ trước, mỗi khi chạy một phép tính, chúng ta sẽ cấp phát bộ nhớ
mới để lưu trữ kết quả của lượt chạy đó. Cụ thể hơn, nếu viết
``y = x + y``, ta sẽ ngừng tham chiếu đến ``ndarray`` mà ``y`` đã chỉ
đến trước đó và thay vào đó gán ``y`` vào bộ nhớ được cấp phát mới.
Trong ví dụ tiếp theo, chúng ta sẽ minh họa việc này với hàm ``id()``
của Python - hàm cung cấp địa chỉ chính xác của một đối tượng được tham
chiếu trong bộ nhớ. Sau khi chạy ``y = y + x``, chúng ta nhận ra rằng
``id(y)`` chỉ đến một địa chỉ khác. Đó là bởi vì Python trước hết sẽ
tính ``y + x``, cấp phát bộ nhớ mới cho kết quả trả về và gán ``y`` vào
địa chỉ mới này trong bộ nhớ.
.. code:: python
before = id(y)
y = y + x
id(y) == before
.. parsed-literal::
:class: output
False
.. raw:: html
Đây có thể là điều không mong muốn vì hai lý do. Thứ nhất, không phải
lúc nào chúng ta cũng muốn cấp phát bộ nhớ không cần thiết. Trong học
máy, ta có thể có đến hàng trăm megabytes tham số và cập nhật tất cả
chúng nhiều lần mỗi giây, và thường thì ta muốn thực thi các cập nhật
này *tại chỗ*. Thứ hai, chúng ta có thể trỏ đến cùng tham số từ nhiều
biến khác nhau. Nếu không cập nhật tại chỗ, các bộ nhớ đã bị loại bỏ sẽ
không được giải phóng, dẫn đến khả năng một số chỗ trong mã nguồn sẽ vô
tình tham chiếu lại các tham số cũ.
.. raw:: html
May mắn thay, ta có thể dễ dàng thực hiện các phép tính tại chỗ với
MXNet. Chúng ta có thể gán kết quả của một phép tính cho một mảng đã
được cấp phát trước đó bằng ký hiệu cắt chọn (*slice notation*), ví dụ,
``y[:] = ``. Để minh họa khái niệm này, đầu tiên chúng ta
tạo một ma trận mới ``z`` có cùng kích thước với ma trận ``y``, sử dụng
``zeros_like`` để gán giá trị khởi tạo bằng :math:`0`.
.. code:: python
z = np.zeros_like(y)
print('id(z):', id(z))
z[:] = x + y
print('id(z):', id(z))
.. parsed-literal::
:class: output
id(z): 139910709656624
id(z): 139910709656624
.. raw:: html
Nếu các tính toán tiếp theo không tái sử dụng giá trị của ``x``, chúng
ta có thể viết ``x[:] = x + y`` hoặc ``x += y`` để giảm thiểu việc sử
dụng bộ nhớ không cần thiết trong quá trình tính toán.
.. code:: python
before = id(x)
x += y
id(x) == before
.. parsed-literal::
:class: output
True
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
Chuyển đổi sang các Đối Tượng Python Khác
-----------------------------------------
.. raw:: html
Chuyển đổi một MXNet ``ndarray`` sang NumPy ``ndarray`` hoặc ngược lại
là khá đơn giản. Tuy nhiên, kết quả của phép chuyển đổi này không chia
sẻ bộ nhớ với đối tượng cũ. Điểm bất tiện này tuy nhỏ nhưng lại khá quan
trọng: khi bạn thực hiện các phép tính trên CPU hoặc GPUs, bạn không
muốn MXNet dừng việc tính toán để chờ xem liệu gói Numpy của Python có
sử dụng cùng bộ nhớ đó để làm việc khác không. Hàm ``array`` và
``asnumpy`` sẽ giúp bạn giải quyết vấn đề này.
.. code:: python
a = x.asnumpy()
b = np.array(a)
type(a), type(b)
.. parsed-literal::
:class: output
(numpy.ndarray, mxnet.numpy.ndarray)
.. raw:: html
Để chuyển đổi một mảng ``ndarray`` chứa một phần tử sang số vô hướng
Python, ta có thể gọi hàm ``item`` hoặc các hàm có sẵn trong Python.
.. code:: python
a = np.array([3.5])
a, a.item(), float(a), int(a)
.. parsed-literal::
:class: output
(array([3.5]), 3.5, 3.5, 3)
.. raw:: html
.. raw:: html
.. raw:: html
Tổng kết
--------
.. raw:: html
- MXNet ``ndarray`` là phần mở rộng của NumPy ``ndarray`` với một số ưu
thế vượt trội giúp cho nó phù hợp với học sâu.
- MXNet ``ndarray`` cung cấp nhiều chức năng bao gồm các phép toán cơ
bản, cơ chế lan truyền (*broadcasting*), chỉ số (*indexing*), cắt
chọn (*slicing*), tiết kiệm bộ nhớ và khả năng chuyển đổi sang các
đối tượng Python khác.
.. raw:: html
Bài tập
-------
.. raw:: html
1. Chạy đoạn mã nguồn trong mục này. Thay đổi điều kiện mệnh đề
``x == y`` sang ``x < y`` hoặc ``x > y``, sau đó kiểm tra dạng của
``ndarray`` nhận được.
2. Thay hai ``ndarray`` trong phép tính theo từng phần tử ở phần cơ chế
lan truyền (*broadcasting mechanism*) với các ``ndarray`` có kích
thước khác, ví dụ như tensor ba chiều. Kết quả có giống như bạn mong
đợi hay không?
.. raw:: html
.. raw:: html
.. raw:: html
Thảo luận
---------
- `Tiếng Anh `__
- `Tiếng Việt `__
.. raw:: html
Những người thực hiện
---------------------
Bản dịch trong trang này được thực hiện bởi:
- Đoàn Võ Duy Thanh
- Vũ Hữu Tiệp
- Lê Khắc Hồng Phúc
- Phạm Hồng Vinh
- Trần Thị Hồng Hạnh
- Phạm Minh Đức
- Lê Đàm Hồng Lộc
- Nguyễn Lê Quang Nhật