.. 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