ApacheMXNet-NDArray
この章では、MXNetの多次元配列形式について説明します。 ndarray。
NDArrayを使用したデータの処理
まず、NDArrayを使用してデータを処理する方法を見ていきます。以下は、同じの前提条件です-
前提条件
この多次元配列形式でデータを処理する方法を理解するには、次の前提条件を満たしている必要があります。
Python環境にインストールされたMXNet
Python2.7.xまたはPython3.x
実装例
以下の例を参考にして、基本的な機能を理解しましょう。
まず、MXNetとndarrayをMXNetから次のようにインポートする必要があります-
import mxnet as mx
from mxnet import nd
必要なライブラリをインポートしたら、次の基本機能を使用します。
Pythonリストを含む単純な1次元配列
Example
x = nd.array([1,2,3,4,5,6,7,8,9,10])
print(x)
Output
出力は以下のとおりです-
[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
<NDArray 10 @cpu(0)>
Pythonリストを含む2次元配列
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
出力は以下のとおりです-
[[ 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)>
初期化なしでNDArrayを作成する
ここでは、を使用して3行4列の行列を作成します。 .empty関数。また使用します.full 関数。配列に入力する値に対して追加の演算子を使用します。
Example
x = nd.empty((3, 4))
print(x)
x = nd.full((3,4), 8)
print(x)
Output
出力は以下のとおりです。
[[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)>
.zeros関数を使用したすべてのゼロの行列
Example
x = nd.zeros((3, 8))
print(x)
Output
出力は次のとおりです-
[[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)>
.ones関数を持つすべてのものの行列
Example
x = nd.ones((3, 8))
print(x)
Output
出力は以下に記載されています-
[[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)>
値がランダムにサンプリングされる配列を作成する
Example
y = nd.random_normal(0, 1, shape=(3, 4))
print(y)
Output
出力は以下のとおりです。
[[ 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)>
各NDArrayの次元を見つける
Example
y.shape
Output
出力は次のとおりです-
(3, 4)
各NDArrayのサイズを見つける
Example
y.size
Output
12
各NDArrayのデータ型を見つける
Example
y.dtype
Output
numpy.float32
NDArray操作
このセクションでは、MXNetの配列操作を紹介します。NDArrayは、多数の標準的な数学演算とインプレース演算をサポートしています。
標準的な数学演算
以下は、NDArrayでサポートされている標準の数学演算です。
要素ごとの加算
まず、MXNetとndarrayをMXNetから次のようにインポートする必要があります。
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
出力はここに与えられます-
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)>
要素ごとの乗算
Example
x = nd.array([1, 2, 3, 4])
y = nd.array([2, 2, 2, 1])
x * y
Output
次の出力が表示されます-
[2. 4. 6. 4.]
<NDArray 4 @cpu(0)>
べき乗
Example
nd.exp(x)
Output
コードを実行すると、次の出力が表示されます。
[ 2.7182817 7.389056 20.085537 54.59815 ]
<NDArray 4 @cpu(0)>
行列転置して行列-行列積を計算します
Example
nd.dot(x, y.T)
Output
以下にコードの出力を示します-
[16.]
<NDArray 1 @cpu(0)>
インプレースオペレーション
上記の例では、毎回、操作を実行し、その結果をホストするために新しいメモリを割り当てました。
たとえば、A = A + Bと書くと、Aが指していた行列を逆参照し、代わりに新しく割り当てられたメモリを指します。Pythonのid()関数を使用して、以下の例でそれを理解しましょう-
print('y=', y)
print('id(y):', id(y))
y = y + x
print('after y=y+x, y=', y)
print('id(y):', id(y))
Output
実行すると、次の出力が表示されます-
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
実際、次のように、以前に割り当てられた配列に結果を割り当てることもできます。
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
出力を以下に示します-
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
上記の出力から、x + yは、結果をzにコピーする前に、結果を格納するための一時バッファーを引き続き割り当てることがわかります。これで、メモリをより有効に活用し、一時バッファを回避するために、その場で操作を実行できます。これを行うために、すべての演算子がサポートするoutキーワード引数を次のように指定します-
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
上記のプログラムを実行すると、次の結果が得られます-
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コンテキスト
Apache MXNetでは、各配列にコンテキストがあり、1つのコンテキストがCPUである可能性がありますが、他のコンテキストは複数のGPUである可能性があります。複数のサーバーに作業を展開すると、事態はさらに悪化する可能性があります。そのため、配列をコンテキストにインテリジェントに割り当てる必要があります。これにより、デバイス間でデータを転送するために費やされる時間が最小限に抑えられます。
たとえば、次のように配列を初期化してみてください-
from mxnet import nd
z = nd.ones(shape=(3,3), ctx=mx.cpu(0))
print(z)
Output
上記のコードを実行すると、次の出力が表示されます-
[[1. 1. 1.]
[1. 1. 1.]
[1. 1. 1.]]
<NDArray 3x3 @cpu(0)>
次のようにcopyto()メソッドを使用して、指定されたNDArrayをあるコンテキストから別のコンテキストにコピーできます。
x_gpu = x.copyto(gpu(0))
print(x_gpu)
NumPyアレイとNDArray
私たちは皆NumPy配列に精通していますが、ApacheMXNetはNDArrayという名前の独自の配列実装を提供しています。実際、当初はNumPyと同様に設計されていましたが、重要な違いがあります-
主な違いは、NumPyとNDArrayでの計算の実行方法にあります。MXNetでのすべてのNDArray操作は、非同期で非ブロッキングの方法で実行されます。つまり、c = a * bのようなコードを記述すると、関数はにプッシュされます。Execution Engine、計算を開始します。
ここで、aとbは両方ともNDArrayです。これを使用する利点は、関数がすぐに戻り、前の計算がまだ完了していない場合でも、ユーザースレッドが実行を継続できることです。
実行エンジンの動作
実行エンジンの動作について話すと、計算グラフが作成されます。計算グラフは、一部の計算を並べ替えたり組み合わせたりする場合がありますが、常に依存関係の順序を尊重します。
たとえば、プログラミングコードの後半で「X」を使用して他の操作が行われた場合、「X」の結果が利用可能になると、実行エンジンはそれらの操作を開始します。実行エンジンは、後続のコードの実行を開始するためのコールバックの記述など、ユーザーにとって重要な作業を処理します。
Apache MXNetでは、NDArrayを使用して、計算結果を取得するには、結果の変数にアクセスするだけで済みます。計算結果が結果の変数に割り当てられるまで、コードのフローはブロックされます。このようにして、命令型プログラミングモードをサポートしながら、コードのパフォーマンスを向上させます。
NDArrayをNumPy配列に変換する
MXNetでNDArrayをNumPyArrayに変換する方法を学びましょう。
Combining higher-level operator with the help of few lower-level operators
場合によっては、既存の演算子を使用して、より高いレベルの演算子を組み立てることができます。これの最も良い例の1つは、np.full_like()NDArrayAPIにはありません。次のように、既存の演算子の組み合わせに簡単に置き換えることができます。
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
次のような出力が得られます-
True
Finding similar operator with different name and/or signature
すべての演算子の中で、名前が少し異なるものもありますが、機能的には似ています。この例はnd.ravel_index() と np.ravel()関数。同様に、一部の演算子の名前は似ていますが、署名が異なります。この例はnp.split() そして nd.split() 似ています。
次のプログラミング例でそれを理解しましょう:
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
出力は以下のとおりです-
[1. 2. 3. 0. 0. 0. 0. 0. 0. 0.]
<NDArray 10 @cpu(0)>
通話のブロックによる影響を最小限に抑える
場合によっては、どちらかを使用する必要があります .asnumpy() または .asscalar()ただし、これにより、結果を取得できるようになるまで、MXNetは実行をブロックします。電話をかけることで、通話のブロックの影響を最小限に抑えることができます.asnumpy() または .asscalar() この値の計算がすでに行われていると思われる現在のメソッド。
実装例
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
出力は以下に引用されています:
Loss: 2.3373236656188965
Loss: 2.3656985759735107
Loss: 2.3613128662109375
Loss: 2.3197104930877686
Loss: 2.3054862022399902
Loss: 2.329197406768799
Loss: 2.318927526473999