Apache MXNet - NDArray
Trong chương này, chúng ta sẽ thảo luận về định dạng mảng đa chiều của MXNet được gọi là ndarray.
Xử lý dữ liệu với NDArray
Đầu tiên, chúng ta sẽ xem cách chúng ta có thể xử lý dữ liệu với NDArray. Sau đây là các điều kiện tiên quyết cho cùng một -
Điều kiện tiên quyết
Để hiểu cách chúng tôi có thể xử lý dữ liệu với định dạng mảng đa chiều này, chúng tôi cần đáp ứng các điều kiện tiên quyết sau:
MXNet được cài đặt trong môi trường Python
Python 2.7.x hoặc Python 3.x
Ví dụ triển khai
Hãy để chúng tôi hiểu chức năng cơ bản với sự trợ giúp của một ví dụ dưới đây:
Đầu tiên, chúng ta cần nhập MXNet và ndarray từ MXNet như sau:
import mxnet as mx
from mxnet import nd
Khi chúng tôi nhập các thư viện cần thiết, chúng tôi sẽ thực hiện các chức năng cơ bản sau:
Mảng 1-D đơn giản với danh sách python
Example
x = nd.array([1,2,3,4,5,6,7,8,9,10])
print(x)
Output
Đầu ra như được đề cập bên dưới -
[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
<NDArray 10 @cpu(0)>
Mảng 2-D với danh sách python
Example
y = nd.array([[1,2,3,4,5,6,7,8,9,10], [1,2,3,4,5,6,7,8,9,10], [1,2,3,4,5,6,7,8,9,10]])
print(y)
Output
Đầu ra như được nêu dưới đây -
[[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]]
<NDArray 3x10 @cpu(0)>
Tạo NDArray mà không cần bất kỳ khởi tạo nào
Ở đây, chúng ta sẽ tạo một ma trận với 3 hàng và 4 cột bằng cách sử dụng .emptychức năng. Chúng tôi cũng sẽ sử dụng.full hàm này sẽ nhận thêm một toán tử cho giá trị bạn muốn điền vào mảng.
Example
x = nd.empty((3, 4))
print(x)
x = nd.full((3,4), 8)
print(x)
Output
Đầu ra được đưa ra dưới đây -
[[0.000e+00 0.000e+00 0.000e+00 0.000e+00]
[0.000e+00 0.000e+00 2.887e-42 0.000e+00]
[0.000e+00 0.000e+00 0.000e+00 0.000e+00]]
<NDArray 3x4 @cpu(0)>
[[8. 8. 8. 8.]
[8. 8. 8. 8.]
[8. 8. 8. 8.]]
<NDArray 3x4 @cpu(0)>
Ma trận của tất cả các số không với hàm .zeros
Example
x = nd.zeros((3, 8))
print(x)
Output
Kết quả như sau:
[[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]]
<NDArray 3x8 @cpu(0)>
Ma trận của tất cả những cái có chức năng .ones
Example
x = nd.ones((3, 8))
print(x)
Output
Đầu ra được đề cập bên dưới -
[[1. 1. 1. 1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1. 1. 1. 1.]]
<NDArray 3x8 @cpu(0)>
Tạo mảng có các giá trị được lấy mẫu ngẫu nhiên
Example
y = nd.random_normal(0, 1, shape=(3, 4))
print(y)
Output
Đầu ra được đưa ra dưới đây -
[[ 1.2673576 -2.0345826 -0.32537818 -1.4583491 ]
[-0.11176403 1.3606371 -0.7889914 -0.17639421]
[-0.2532185 -0.42614475 -0.12548696 1.4022992 ]]
<NDArray 3x4 @cpu(0)>
Tìm thứ nguyên của mỗi NDArray
Example
y.shape
Output
Kết quả như sau:
(3, 4)
Tìm kích thước của từng NDArray
Example
y.size
Output
12
Tìm kiểu dữ liệu của mỗi NDArray
Example
y.dtype
Output
numpy.float32
Hoạt động NDArray
Trong phần này, chúng tôi sẽ giới thiệu cho bạn các thao tác với mảng của MXNet. NDArray hỗ trợ số lượng lớn các phép toán tiêu chuẩn cũng như tại chỗ.
Các phép toán chuẩn
Sau đây là các phép toán tiêu chuẩn được hỗ trợ bởi NDArray:
Bổ sung nguyên tố khôn ngoan
Đầu tiên, chúng ta cần nhập MXNet và ndarray từ MXNet như sau:
import mxnet as mx
from mxnet import nd
x = nd.ones((3, 5))
y = nd.random_normal(0, 1, shape=(3, 5))
print('x=', x)
print('y=', y)
x = x + y
print('x = x + y, x=', x)
Output
Đầu ra được cung cấp kèm theo:
x=
[[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]]
<NDArray 3x5 @cpu(0)>
y=
[[-1.0554522 -1.3118273 -0.14674698 0.641493 -0.73820823]
[ 2.031364 0.5932667 0.10228804 1.179526 -0.5444829 ]
[-0.34249446 1.1086396 1.2756858 -1.8332436 -0.5289873 ]]
<NDArray 3x5 @cpu(0)>
x = x + y, x=
[[-0.05545223 -0.3118273 0.853253 1.6414931 0.26179177]
[ 3.031364 1.5932667 1.102288 2.1795259 0.4555171 ]
[ 0.6575055 2.1086397 2.2756858 -0.8332436 0.4710127 ]]
<NDArray 3x5 @cpu(0)>
Phép nhân khôn ngoan nguyên tố
Example
x = nd.array([1, 2, 3, 4])
y = nd.array([2, 2, 2, 1])
x * y
Output
Bạn sẽ thấy kết quả sau
[2. 4. 6. 4.]
<NDArray 4 @cpu(0)>
Luỹ thừa
Example
nd.exp(x)
Output
Khi bạn chạy mã, bạn sẽ thấy kết quả sau:
[ 2.7182817 7.389056 20.085537 54.59815 ]
<NDArray 4 @cpu(0)>
Chuyển vị ma trận để tính tích ma trận-ma trận
Example
nd.dot(x, y.T)
Output
Dưới đây là đầu ra của mã:
[16.]
<NDArray 1 @cpu(0)>
Hoạt động tại chỗ
Mỗi lần, trong ví dụ trên, chúng tôi chạy một hoạt động, chúng tôi cấp phát một bộ nhớ mới để lưu trữ kết quả của nó.
Ví dụ, nếu chúng ta viết A = A + B, chúng ta sẽ tham khảo ma trận mà A đã dùng để trỏ tới và thay vào đó trỏ nó vào bộ nhớ mới được cấp phát. Hãy để chúng tôi hiểu nó với ví dụ được đưa ra bên dưới, sử dụng hàm id () của Python -
print('y=', y)
print('id(y):', id(y))
y = y + x
print('after y=y+x, y=', y)
print('id(y):', id(y))
Output
Khi thực hiện, bạn sẽ nhận được kết quả sau:
y=
[2. 2. 2. 1.]
<NDArray 4 @cpu(0)>
id(y): 2438905634376
after y=y+x, y=
[3. 4. 5. 5.]
<NDArray 4 @cpu(0)>
id(y): 2438905685664
Trên thực tế, chúng ta cũng có thể gán kết quả cho một mảng đã được phân bổ trước đó như sau:
print('x=', x)
z = nd.zeros_like(x)
print('z is zeros_like x, z=', z)
print('id(z):', id(z))
print('y=', y)
z[:] = x + y
print('z[:] = x + y, z=', z)
print('id(z) is the same as before:', id(z))
Output
Đầu ra được hiển thị bên dưới -
x=
[1. 2. 3. 4.]
<NDArray 4 @cpu(0)>
z is zeros_like x, z=
[0. 0. 0. 0.]
<NDArray 4 @cpu(0)>
id(z): 2438905790760
y=
[3. 4. 5. 5.]
<NDArray 4 @cpu(0)>
z[:] = x + y, z=
[4. 6. 8. 9.]
<NDArray 4 @cpu(0)>
id(z) is the same as before: 2438905790760
Từ kết quả trên, chúng ta có thể thấy rằng x + y vẫn sẽ cấp phát một bộ đệm tạm thời để lưu trữ kết quả trước khi sao chép nó vào z. Vì vậy, bây giờ, chúng ta có thể thực hiện các hoạt động tại chỗ để sử dụng bộ nhớ tốt hơn và tránh bộ đệm tạm thời. Để làm điều này, chúng tôi sẽ chỉ định đối số từ khóa out mà mọi toán tử hỗ trợ như sau:
print('x=', x, 'is in id(x):', id(x))
print('y=', y, 'is in id(y):', id(y))
print('z=', z, 'is in id(z):', id(z))
nd.elemwise_add(x, y, out=z)
print('after nd.elemwise_add(x, y, out=z), x=', x, 'is in id(x):', id(x))
print('after nd.elemwise_add(x, y, out=z), y=', y, 'is in id(y):', id(y))
print('after nd.elemwise_add(x, y, out=z), z=', z, 'is in id(z):', id(z))
Output
Khi thực hiện chương trình trên, bạn sẽ nhận được kết quả sau:
x=
[1. 2. 3. 4.]
<NDArray 4 @cpu(0)> is in id(x): 2438905791152
y=
[3. 4. 5. 5.]
<NDArray 4 @cpu(0)> is in id(y): 2438905685664
z=
[4. 6. 8. 9.]
<NDArray 4 @cpu(0)> is in id(z): 2438905790760
after nd.elemwise_add(x, y, out=z), x=
[1. 2. 3. 4.]
<NDArray 4 @cpu(0)> is in id(x): 2438905791152
after nd.elemwise_add(x, y, out=z), y=
[3. 4. 5. 5.]
<NDArray 4 @cpu(0)> is in id(y): 2438905685664
after nd.elemwise_add(x, y, out=z), z=
[4. 6. 8. 9.]
<NDArray 4 @cpu(0)> is in id(z): 2438905790760
NDArray Contexts
Trong Apache MXNet, mỗi mảng có một ngữ cảnh và một ngữ cảnh có thể là CPU, trong khi các ngữ cảnh khác có thể là một số GPU. Mọi thứ thậm chí có thể trở nên tồi tệ nhất, khi chúng tôi triển khai công việc trên nhiều máy chủ. Đó là lý do tại sao, chúng ta cần gán các mảng cho các ngữ cảnh một cách thông minh. Nó sẽ giảm thiểu thời gian truyền dữ liệu giữa các thiết bị.
Ví dụ: hãy thử khởi tạo một mảng như sau:
from mxnet import nd
z = nd.ones(shape=(3,3), ctx=mx.cpu(0))
print(z)
Output
Khi bạn thực thi đoạn mã trên, bạn sẽ thấy kết quả sau:
[[1. 1. 1.]
[1. 1. 1.]
[1. 1. 1.]]
<NDArray 3x3 @cpu(0)>
Chúng ta có thể sao chép NDArray đã cho từ ngữ cảnh này sang ngữ cảnh khác bằng cách sử dụng phương thức copyto () như sau:
x_gpu = x.copyto(gpu(0))
print(x_gpu)
Mảng NumPy so với NDArray
Tất cả chúng ta đều quen thuộc với mảng NumPy nhưng Apache MXNet cung cấp triển khai mảng riêng có tên là NDArray. Trên thực tế, ban đầu nó được thiết kế tương tự như NumPy nhưng có một điểm khác biệt chính:
Sự khác biệt chính là cách tính toán được thực hiện trong NumPy và NDArray. Mọi thao tác NDArray trong MXNet đều được thực hiện theo cách không đồng bộ và không chặn, có nghĩa là khi chúng ta viết mã như c = a * b, hàm được đẩy đếnExecution Engine, sẽ bắt đầu tính toán.
Ở đây, a và b đều là NDArrays. Lợi ích của việc sử dụng nó là, hàm ngay lập tức trả về trở lại và luồng người dùng có thể tiếp tục thực thi mặc dù thực tế là phép tính trước đó có thể chưa được hoàn thành.
Hoạt động của công cụ thực thi
Nếu chúng ta nói về hoạt động của công cụ thực thi, nó sẽ xây dựng đồ thị tính toán. Biểu đồ tính toán có thể sắp xếp lại hoặc kết hợp một số phép tính, nhưng nó luôn tuân theo thứ tự phụ thuộc.
Ví dụ: nếu có các thao tác khác với 'X' được thực hiện sau này trong mã lập trình, thì Công cụ thực thi sẽ bắt đầu thực hiện chúng khi có kết quả là 'X'. Công cụ thực thi sẽ xử lý một số công việc quan trọng đối với người dùng, chẳng hạn như viết các lệnh gọi lại để bắt đầu thực thi mã tiếp theo.
Trong Apache MXNet, với sự trợ giúp của NDArray, để có được kết quả tính toán, chúng ta chỉ cần truy cập vào biến kết quả. Dòng mã sẽ bị chặn cho đến khi kết quả tính toán được gán cho biến kết quả. Bằng cách này, nó làm tăng hiệu suất mã trong khi vẫn hỗ trợ chế độ lập trình mệnh lệnh.
Chuyển đổi NDArray thành NumPy Array
Hãy để chúng tôi tìm hiểu cách chuyển NDArray thành NumPy Array trong MXNet.
Combining higher-level operator with the help of few lower-level operators
Đôi khi, chúng ta có thể tập hợp toán tử cấp cao hơn bằng cách sử dụng các toán tử hiện có. Một trong những ví dụ tốt nhất về điều này là,np.full_like()toán tử này không có trong API NDArray. Nó có thể dễ dàng được thay thế bằng sự kết hợp của các toán tử hiện có như sau:
from mxnet import nd
import numpy as np
np_x = np.full_like(a=np.arange(7, dtype=int), fill_value=15)
nd_x = nd.ones(shape=(7,)) * 15
np.array_equal(np_x, nd_x.asnumpy())
Output
Chúng ta sẽ nhận được kết quả tương tự như sau:
True
Finding similar operator with different name and/or signature
Trong số tất cả các toán tử, một số trong số chúng có tên hơi khác nhau, nhưng chúng giống nhau về chức năng. Một ví dụ về điều này lànd.ravel_index() với np.ravel()chức năng. Theo cách tương tự, một số toán tử có thể có tên tương tự, nhưng chúng có chữ ký khác nhau. Một ví dụ về điều này lànp.split() và nd.split() tương tự nhau.
Hãy hiểu nó với ví dụ lập trình sau:
def pad_array123(data, max_length):
data_expanded = data.reshape(1, 1, 1, data.shape[0])
data_padded = nd.pad(data_expanded,
mode='constant',
pad_width=[0, 0, 0, 0, 0, 0, 0, max_length - data.shape[0]],
constant_value=0)
data_reshaped_back = data_padded.reshape(max_length)
return data_reshaped_back
pad_array123(nd.array([1, 2, 3]), max_length=10)
Output
Đầu ra được nêu dưới đây -
[1. 2. 3. 0. 0. 0. 0. 0. 0. 0.]
<NDArray 10 @cpu(0)>
Giảm thiểu tác động của việc chặn cuộc gọi
Trong một số trường hợp, chúng ta phải sử dụng .asnumpy() hoặc là .asscalar()nhưng điều này sẽ buộc MXNet phải chặn việc thực thi, cho đến khi kết quả có thể được truy xuất. Chúng tôi có thể giảm thiểu tác động của việc chặn cuộc gọi bằng cách gọi.asnumpy() hoặc là .asscalar() tại thời điểm này, khi chúng tôi nghĩ rằng việc tính toán giá trị này đã được thực hiện.
Ví dụ triển khai
Example
from __future__ import print_function
import mxnet as mx
from mxnet import gluon, nd, autograd
from mxnet.ndarray import NDArray
from mxnet.gluon import HybridBlock
import numpy as np
class LossBuffer(object):
"""
Simple buffer for storing loss value
"""
def __init__(self):
self._loss = None
def new_loss(self, loss):
ret = self._loss
self._loss = loss
return ret
@property
def loss(self):
return self._loss
net = gluon.nn.Dense(10)
ce = gluon.loss.SoftmaxCELoss()
net.initialize()
data = nd.random.uniform(shape=(1024, 100))
label = nd.array(np.random.randint(0, 10, (1024,)), dtype='int32')
train_dataset = gluon.data.ArrayDataset(data, label)
train_data = gluon.data.DataLoader(train_dataset, batch_size=128, shuffle=True, num_workers=2)
trainer = gluon.Trainer(net.collect_params(), optimizer='sgd')
loss_buffer = LossBuffer()
for data, label in train_data:
with autograd.record():
out = net(data)
# This call saves new loss and returns previous loss
prev_loss = loss_buffer.new_loss(ce(out, label))
loss_buffer.loss.backward()
trainer.step(data.shape[0])
if prev_loss is not None:
print("Loss: {}".format(np.mean(prev_loss.asnumpy())))
Output
Kết quả được trích dẫn dưới đây:
Loss: 2.3373236656188965
Loss: 2.3656985759735107
Loss: 2.3613128662109375
Loss: 2.3197104930877686
Loss: 2.3054862022399902
Loss: 2.329197406768799
Loss: 2.318927526473999