このページの翻訳は最新ではありません。ここをクリックして、英語の最新版を参照してください。
カスタム tall 配列アルゴリズムの開発
tall 配列は、従来の MATLAB® 構文を使用して大規模なデータセットを扱うための強力で直感的な方法です。ただし、tall 配列は、それぞれが個別にメモリに収まるデータ ブロックを操作するため、ほとんどの関数の従来のアルゴリズムは、tall 配列をサポートするために並列化アプローチを使用するよう更新しなければなりません。このトピックでは、tall 配列を操作するための独自の並列化アルゴリズムの開発方法を説明します。
カスタム関数を tall 配列に適用するために現在使用できるアプローチは、次のとおりです。
単一ステップの変換操作: tall 配列内のデータ ブロックに関数を適用します。
2 ステップのリダクション演算: tall 配列に関数を適用して内容を変換したうえで、関数をもう 1 つ適用して出力を単一のブロックに縮小します。
スライディングウィンドウ操作: tall 配列に移動ウィンドウ関数を適用して内容を変換します。
どの操作を選択するかに関わりなく、すべてのアプローチに適用されるオプション、パフォーマンス上の考慮事項、および一般的な問題があります。
カスタム アルゴリズムを実装する理由
ほとんどの一般的な数学関数と MATLAB 演算では、既に tall 配列がサポートされています。機能が既にサポートされている場合は、独自のアルゴリズムを作成する必要はないかもしれません。
以下に、tall 配列用のカスタム アルゴリズムを実装することが望ましい理由をいくつか示します。
現在サポートされていない関数の実装 — 特定の関数が現在 tall 配列をサポートしていない場合は、ここで概説する API を使用して、tall 配列をサポートするその関数のバージョンを作成できます。
既存のコードの活用 — インメモリ データに対していくつかの操作を実行する既存のコードがある場合は、わずかな変更だけで、tall 配列を扱うように互換性をもたせることができます。このアプローチでは、tall 配列をサポートする MATLAB 言語のサブセットに合わせてコードを変換する必要がありません。
パフォーマンスの向上 — たとえば、MATLAB 関数を C++ MEX 関数として書き換えることができ、ここで概説する API を使用して、データを扱う MEX 関数を呼び出すことができます。
優先外部ライブラリの使用 — 組織内での互換性のために、特定の外部ライブラリを特定の計算に使用するよう求められる場合があります。ここで概説する API を使用して、これらの外部ライブラリの関数を再実装することができます。
サポートされている API
サポートされている API は高度な使用を目的としており、広範な入力チェックは含まれていません。実装した補助関数がすべての要件を満たし、期待どおりの計算を行えることをテストするには、ある程度の時間がかかることが予想されます。tall 配列アルゴリズムを作成するために現在サポートされている API を次に示します。
パッケージ関数名 | 説明 |
---|---|
matlab.tall.transform | 指定された関数を 1 つ以上の tall 配列の各ブロックに適用します。 |
matlab.tall.reduce | 指定された関数を 1 つ以上の tall 配列の各ブロックに適用します。その後、この関数の出力を 2 つ目のリダクション関数に渡します。 |
matlab.tall.movingWindow | 移動ウィンドウ関数をデータ ブロックに適用します。 |
matlab.tall.blockMovingWindow | 移動ウィンドウ関数およびブロック削減をパディングされたデータ ブロックに適用します。 |
背景: tall 配列ブロック
データ ストアから tall 配列を作成すると、基となるデータ ストアによって、計算中のデータの移動が容易になります。データは "ブロック" と呼ばれる別々の要素に分かれて移動します。各ブロックはメモリに収容可能な連続する行のセットです。たとえば、2 次元配列 (table など) の 1 つのブロックは X(n:m,:)
です。各ブロックのサイズは、データ ストアの ReadSize
プロパティの値に基づいていますが、ブロックが必ずしも正確にそのサイズになるわけではありません。tall 配列アルゴリズムを開発する場合、tall 配列はそうした多数のブロックを垂直方向に連結したものと見なされます。
配列のブロックは、使用可能なメモリに基づいて実行時に選択されるため、動的となる場合があります。そのため、ブロックは実行ごとに "厳密には" 同じサイズにならないことがあります。コンピューターで、使用可能なメモリに影響を与える変更がある場合、それがブロックのサイズに影響を与える可能性があります。
このページでは、2 次元の観点から "ブロック" と "行" に言及していますが、この概念は N 次元の tall 配列に拡張されます。ブロック サイズは最初の次元でのみ制約されるため、ブロックには、たとえば X(n:m,:,:,...)
のように他の次元の要素がすべて含まれます。また、N 次元配列には、行ではなく X(p,:,:,...)
のような "スライス" が含まれています。
単一ステップの変換操作
関数 matlab.tall.transform
は、tall 配列の各ブロックに 1 つの関数を適用するため、データの変換、フィルター処理、または削減をブロック単位で適用するために使用できます。たとえば、特定の値をもつ行を削除し、データのセンタリングやスケーリングを行い、あるいは特定の条件を検出して特定データを変換することができます。以下の図は、matlab.tall.transform
によって配列のブロックを操作したときの動作を示しています。
操作 | 説明 | 例 |
---|---|---|
| 変換 — 各ブロックの行数は同じままですが、値が変わります。 |
|
| フィルター処理 — 各ブロックの行数が減少します。そのため、新しい配列内のブロックには前に他のブロックにあった行が含まれる可能性があります。 |
|
変換構文
単一ステップの変換を適用する汎用構文は、次のとおりです。
[tA, tB, tC, ...] = matlab.tall.transform(fcn, tX, tY, tZ, ...)
fcn
の機能要件
fcn
の一般的な関数シグネチャは、次のとおりです。
[a, b, c, ...] = fcn(x, y, z, ...)
fcn
は以下の要件を満たさなければなりません。
入力引数 — 入力
[x, y, z, ...]
は、メモリに収まるデータのブロックです。ブロックは、tall 配列の入力[tX, tY, tZ, ...]
それぞれからデータを抽出することによって生成されます。入力[x, y, z, ...]
は次の特性を満たします。[x, y, z, ...]
はすべて、任意の可能な拡張後の最初の次元が同じサイズをもつ。[x, y, z, ...]
のデータ ブロックは、tall 配列の tall 次元が 1 ではないと仮定した場合、tall 次元の同じインデックスから得られる。たとえば、tX
とtY
の tall 次元が 1 ではない場合、ブロックの最初のセットはx = tX(1:20000,:)
およびy = tY(1:20000,:)
になる可能性があります。[tX, tY, tZ, ...]
のいずれかの最初の次元がサイズ1
である場合、対応するブロック[x, y, z, ...]
は、その tall 配列内のすべてのデータで構成される。
出力引数 — 出力
[a, b, c, ...]
はメモリに収まるブロックで、それぞれの出力[tA, tB, tC, ...]
に送信されます。出力[a, b, c, ...]
は以下の特性を満たします。[a, b, c, ...]
はすべて、最初の次元が同じサイズでなければならない。[a, b, c, ...]
はすべて、fcn
の以前の呼び出しの結果とそれぞれ垂直方向に連結される。[a, b, c, ...]
はすべて、それぞれの出力先で、出力配列の最初の次元にある同じインデックスに送信される。
関数ルール —
fcn
は関数ルールを満たさなければなりません。F([inputs1; inputs2]) == [F(inputs1); F(inputs2)]
:入力の連結に関数を適用することは、入力に関数を個別に適用してから結果を連結する場合と同じでなければならない。
空の入力 —
fcn
が高さ 0 の入力を処理できることを確認します。空の入力は、ファイルが空の場合や、データに対して数多くのフィルター処理を行った場合に発生することがあります。
2 ステップのリダクション演算
matlab.tall.reduce
は 2 つの関数を tall 配列に適用し、最初のステップの結果を最後のリダクション ステップに入力として送ります。リダクション関数は、メモリに収まる単一の最終ブロックが得られるまで、中間結果に繰り返し適用されます。MapReduce のパラダイムでは、この処理は "単一キー" の MapReduce 演算に似ています。そこでは、中間結果がすべて同じキーをもち、リダクション ステップで結合されます。
最初のステップは matlab.tall.transform
に似ており、その要件は同じです。ただし、リダクション ステップでは、中間結果がメモリに収まる単一のブロックへと常に縮小されます。以下の図は、matlab.tall.reduce
によって配列のブロックを操作したときの動作を示しています。
操作 | 説明 | 例 |
---|---|---|
| 変換 + リダクション — 各ブロックの行数は最初のステップの後も同じままで、その後、中間結果が 1 つのブロックに縮小されます。 |
|
| フィルター処理 + リダクション — 最初のステップで、各ブロックの行数が減らされます。その後、中間結果が 1 ブロックへと縮小されます。 |
|
reduce の構文
2 ステップのリダクションを適用する汎用構文を次に示します。
[rA, rB, rC, ...] = matlab.tall.reduce(fcn, reducefcn, tX, tY, tZ, ...)
fcn
の関数シグネチャは、次のとおりです。
[a, b, c, ...] = fcn(x, y, z, ...)
reducefcn
の関数シグネチャは、次のとおりです。
[rA, rB, rC, ...] = reducefcn(a, b, c, ...)
つまり、入力の tall 配列 [tX, tY, tZ, ...]
は、fcn
への入力であるブロック [x, y, z, ...]
に分割されます。その後、fcn
は reducefcn
への入力である出力 [a, b, c, ...]
を返します。最後に、reducefcn
は、matlab.tall.reduce
によって返される最終的な結果 [rA, rB, rC]
を返します。
reducefcn
の機能要件
fcn
の要件は、fcn の機能要件で概説したものと同じです。ただし、reducefcn
の要件は異なります。
reducefcn
の一般的な関数シグネチャは、次のとおりです。
[rA, rB, rC, ...] = reducefcn(a, b, c, ...)
reducefcn
は以下の要件を満たさなければなりません。
入力引数 — 入力
[a, b, c, ...]
はメモリに収まるブロックです。データのブロックは、fcn
によって返される出力か、さらなる縮小のために再度処理される、ある程度縮小されたreducefcn
からの出力のいずれかです。入力[a, b, c, ...]
は次の特性を満たします。入力
[a, b, c, ...]
は、最初の次元が同じサイズとなる。最初の次元の特定インデックスについて、データ
[a, b, c, ...]
のブロックの各行は入力から派生するか、またはreducefcn
に対する以前の同じ呼び出しから派生する。最初の次元の特定インデックスについて、そのインデックスの入力
[a, b, c, ...]
の各行は、最初の次元の同じインデックスから派生する。
出力引数 — すべての出力
[rA, rB, rC, ...]
は、最初の次元が同じサイズでなければなりません。さらに、必要に応じて縮小を繰り返すことができるように、それぞれの入力[a, b, c, ...]
と垂直方向に結合可能でなければなりません。関数ルール —
reducefcn
は、以下の関数ルールを満たさなければなりません (丸め誤差を除く)。F(input) == F(F(input))
:同じ入力に繰り返し関数を適用しても、結果が変わってはならない。F([input1; input2]) == F([input2; input1])
:結果が、連結の順序に依存することがあってはならない。F([input1; input2]) == F([F(input1); F(input2)])
:いくつかの中間結果の連結に関数を 1 回適用することは、それを別々に適用し、連結し、再び適用することと同じでなければならない。
空の入力 —
reducefcn
が高さ 0 の入力を処理できることを確認します。空の入力は、ファイルが空の場合や、データに対して数多くのフィルター処理を行った場合に発生することがあります。この呼び出しでは、すべての入力ブロックは、最初の次元以外で正しい型とサイズをもつ空の配列となります。
スライディングウィンドウ操作
関数 matlab.tall.movingWindow
および matlab.tall.blockMovingWindow
は、tall 配列内のデータのウィンドウに関数を適用します。matlab.tall.transform
と matlab.tall.reduce
は一度にデータ ブロック全体に対して操作を行いますが、移動ウィンドウ関数は、ウィンドウが配列の先頭から末尾まで移動する際にデータのウインドウに対して操作を行います。ウィンドウは、ディスクから読み取られるデータの複数のブロックにまたがることができます。
以下の図は、matlab.tall.movingWindow
または matlab.tall.blockMovingWindow
によって配列のブロックを操作したときの動作を示しています。
操作 | 説明 | 例 |
---|---|---|
| ウィンドウ変換 — 各ブロックの行数は同じままですが、値が変わります。出力は、不完全なデータ ウィンドウと完全なデータ ウインドウの両方に対して実行された操作の結果を含みます。
|
|
| ウィンドウ フィルター処理 — 不完全なデータ ウィンドウが破棄されるため、出力の要素数は入力より少なくなります。出力は、完全なデータ ウィンドウに対して行った操作の結果のみを含みます。
|
|
matlab.tall.movingWindow
と matlab.tall.blockMovingWindow
を使用して、データにウィンドウ変換やフィルターを適用できます。たとえば、末尾平均や移動中央値を計算したり、一度に複数の操作を同じウィンドウに対して適用することができます。2 つの関数は次の点で違いがあります。
matlab.tall.movingWindow
は、ウィンドウが完全かどうかにかかわらず、すべてのデータ ウィンドウにfcn
を適用します。matlab.tall.blockMovingWindow
は、不完全なデータ ウィンドウにwindowfcn
を適用し、完全なデータ ウィンドウにはblockfcn
を適用します。matlab.tall.movingWindow
は一度に 1 つのデータ ウィンドウに対して操作を行います。matlab.tall.blockMovingWindow
は、複数の完全なウィンドウを含んだデータ ブロック全体に対して操作を行うため、計算に必要な関数呼び出しの数が少なくなります。
移動ウィンドウの構文
移動ウィンドウ操作を 1 つのデータ ウィンドウに適用する構文は次のとおりです。
[tA, tB, tC, ...] = matlab.tall.movingWindow(fcn, window, tX, tY, tZ, ...)
fcn
の関数シグネチャは次のようにしなければなりません。
[a, b, c, ...] = fcn(x, y, z, ...)
同様に、移動ウィンドウ操作をデータ ブロック全体に適用する構文は次のとおりです。
[tA, tB, tC, ...] = matlab.tall.blockMovingWindow(windowfcn, blockfcn, window, tX, tY, tZ, ...)
windowfcn
および blockfcn
の関数シグネチャは次のようにしなければなりません。
[a, b, c, ...] = windowfcn(info, x, y, z, ...) [a, b, c, ...] = blockfcn(info, bX, bY, bZ, ...)
入力 info
は、Window
および Stride
フィールドを含む構造体です。関数を作成するときに、これらのフィールドを使用して各ブロックからデータのウィンドウを選択します。
fcn
、windowfcn
、および blockfcn
が従わなければならない一般規則の概要については、fcn の機能要件を参照してください。入力 info
を除けば、fcn
と windowfcn
の要件は同じです。しかし、関数 blockfcn
はデータ ブロック全体に対して操作を行うため、要件が異なります。
windowfcn
の機能要件
windowfcn
の一般的な関数シグネチャは、次のとおりです。
[a, b, c, ...] = windowfcn(info, x, y, ...)
info
は、matlab.tall.blockMovingWindow
によって提供される構造体で、以下のフィールドを含みます。
Stride
— ウィンドウ間の指定ステップ サイズ (既定の設定: 1)。この値は名前と値のペア'Stride'
で設定します。Window
— 指定のウィンドウ サイズ。この値はwindow
入力引数で設定します。
windowfcn
は以下の要件を満たさなければなりません。
入力引数 — 入力
[x, y, z, ...]
は、メモリに収まるデータのブロックです。ブロックは、tall 配列の入力[tX, tY, tZ, ...]
それぞれからデータを抽出することによって生成されます。入力[x, y, z, ...]
は次の特性を満たします。すべての入力
[x, y, z, ...]
は最初の次元が同じサイズとなる。[x, y, z, ...]
のデータ ブロックは、tall 配列の tall 次元が 1 ではないと仮定した場合、tall 次元の同じインデックスから得られる。たとえば、tX
とtY
の tall 次元が 1 ではない場合、ブロックの最初のセットはx = tX(1:20000,:)
およびy = tY(1:20000,:)
になる可能性があります。[tX, tY, tZ, ...]
のいずれかの最初の次元がサイズ1
である場合、対応するブロック[x, y, z, ...]
は、その tall 配列内のすべてのデータで構成される。windowfcn
を適用した結果、入力データはスカラーまたは高さ 1 の配列のスライスに削減されなければならない。入力が行列、N 次元配列、table、または timetable である場合、
windowfcn
を適用することで、その各列または各変数における入力データが削減されなければなりません。
出力引数 — 出力
[a, b, c, ...]
はメモリに収まるブロックで、それぞれの出力[tA, tB, tC, ...]
に送信されます。出力[a, b, c, ...]
は以下の特性を満たします。出力
[a, b, c, ...]
はすべて、最初の次元が同じサイズでなければならない。出力
[a, b, c, ...]
はすべて、windowfcn
の以前の呼び出しの結果とそれぞれ垂直方向に連結される。出力
[a, b, c, ...]
はすべて、それぞれの出力先で、出力配列の最初の次元にある同じインデックスに送信される。
関数ルール —
windowfcn
は以下の関数ルールを満たしていなければなりません。F([inputs1; inputs2]) == [F(inputs1); F(inputs2)]
: 入力の連結に関数を適用することは、入力に関数を個別に適用してから結果を連結する場合と同じでなければならない。
blockfcn
の機能要件
blockfcn
の一般的な関数シグネチャは、次のとおりです。
[a, b, c, ...] = blockfcn(info, bX, bY, bZ, ...)
info
は、matlab.tall.blockMovingWindow
によって提供される構造体で、以下のフィールドを含みます。
Stride
— ウィンドウ間の指定ステップ サイズ (既定の設定: 1)。この値は名前と値のペア'Stride'
で設定します。Window
— 指定のウィンドウ サイズ。この値はwindow
入力引数で設定します。
matlab.tall.blockMovingWindow
が blockfcn
に提供するデータ ブロック bX, bY, bZ, ...
には次の特性があります。
ブロックにはフルサイズのウィンドウのみが含まれる。
blockfcn
は不完全なデータ ウィンドウに対する動作を定義する必要はありません。最初のデータ ウィンドウは、ブロックの最初の要素から開始する。最後のウィンドウの最後の要素は、ブロックの最後の要素です。
blockfcn
は以下の要件を満たさなければなりません。
入力引数 — 入力
[bX, bY, bZ, ...]
は、メモリに収まるデータのブロックです。ブロックは、tall 配列の入力[tX, tY, tZ, ...]
それぞれからデータを抽出することによって生成されます。入力[bX, bY, bZ, ...]
は次の特性を満たします。入力
[bX, bY, bZ, ...]
はすべて、任意の可能な拡張後の最初の次元が同じサイズをもつ。[bX, bY, bZ, ...]
のデータ ブロックは、tall 配列の tall 次元が 1 ではないと仮定した場合、tall 次元の同じインデックスから得られる。たとえば、tX
とtY
の tall 次元が 1 ではない場合、ブロックの最初のセットはbX = tX(1:20000,:)
およびbY = tY(1:20000,:)
になる可能性があります。データ入力
[tX, tY, tZ, ...]
のいずれかの最初の次元がサイズ1
である場合、対応するブロック[bX, bY, bZ, ...]
は、その tall 配列内のすべてのデータで構成される。blockfcn
を適用した結果、入力データがブロック内のウィンドウの数と等しい高さになるよう削減されなければならない。ブロック内のウィンドウの数を特定するにはinfo.Window
とinfo.Stride
を使用できます。入力が行列、N 次元配列、table、または timetable である場合、
blockfcn
を適用した結果、入力データはその各列または各変数において削減されなければなりません。
出力引数 — 出力
[a, b, c, ...]
はメモリに収まるブロックで、それぞれの出力[tA, tB, tC, ...]
に送信されます。出力[a, b, c, ...]
は以下の特性を満たします。出力
[a, b, c, ...]
はすべて、最初の次元が同じサイズでなければならない。出力
[a, b, c, ...]
はすべて、blockfcn
の以前の呼び出しの結果とそれぞれ垂直方向に連結される。出力
[a, b, c, ...]
はすべて、それぞれの出力先で、出力配列の最初の次元にある同じインデックスに送信される。
関数ルール —
blockfcn
は以下の関数ルールを満たしていなければなりません。F([inputs1; inputs2]) == [F(inputs1); F(inputs2)]
: 入力の連結に関数を適用することは、入力に関数を個別に適用してから結果を連結する場合と同じでなければならない。
出力データ型の制御
サポートされている APIのいずれかからの最終的な出力に入力と異なるデータ型がある場合は、名前と値のペア 'OutputsLike'
を指定して、対応する出力と同じデータ型と属性をもつ 1 つ以上のプロトタイプ配列を提示 "しなければなりません"。'OutputsLike'
の値は常に cell 配列であり、各 cell には対応する出力引数のプロトタイプ配列が含まれます。
たとえば、次の matlab.tall.transform
の呼び出しは、1 つの tall 配列 tX
を入力として受け取り、プロトタイプ配列 protoA
および protoB
によって指定された異なる型をもつ 2 つの出力を返します。出力 A
は protoA
と同じデータ型と属性をもち、B
と protoB
の場合も同様です。
C = {protoA protoB}; [A, B] = matlab.tall.transform(fcn, tX, 'OutputsLike', C)
プロトタイプ配列を提供する一般的な方法は、適切なデータ型の自明な入力で fcn
を呼び出すことです。これは、fcn
によって返される出力が正しいデータ型になるためです。この例では、変換関数が tall double を受け取り、tall table を返します。プロトタイプ配列は fcn(0)
の呼び出しによって生成され、プロトタイプは 'OutputsLike'
の値として指定されます。
ds = tabularTextDatastore('airlinesmall.csv','TreatAsMissing','NA'); ds.SelectedVariableNames = {'ArrDelay', 'DepDelay'}; tt = tall(ds); tX = tt.ArrDelay; fcn = @(x) table(x,'VariableNames',{'MyVar'}); proto_A = fcn(0); A = matlab.tall.transform(fcn,tX,'OutputsLike',{proto_A});
コーディングとパフォーマンスのヒント
不必要な入れ子関数を使用するのではなく、1 つの関数にすべての解析を組み込んで呼び出し、データを直接操作します。
データの小さなサブセットを使用して実験します。データセット全体にスケール アップするとボトルネックが強く拡大されて現れる可能性があるため、その前にコードをプロファイリングしてボトルネックを見つけ、修正します。
一部の関数は入力データに応じて異なる形状の出力を返すため、データの方向に注意してください。たとえば、
unique
は入力データの方向に応じて、行ベクトルまたは列ベクトルのいずれかを返すことができます。ブロックは、使用可能なコンピューター メモリに基づいて実行時に動的に生成されます。指定されたすべてのリダクション関数が、関数ルール
F([input1; input2]) == F([F(input1); F(input2)])
に従っていることを確認してください。このルールに従っていない場合、結果は試行間で大きく異なってくることがあります。ブロックの最初の次元は、0 や 1 を含め任意のサイズにできます。サイズ 0 または 1 は、フィルター処理やリダクション演算の結果として中間計算において発生することがあります。関数が、これら両方のケースで正しく動作することを確認してください。関数がこれらのケースを正しく処理しない兆候の 1 つは、"出力のサイズが異なります" のエラー メッセージを受信する場合です。
参考
matlab.tall.reduce
| matlab.tall.transform
| matlab.tall.movingWindow
| matlab.tall.blockMovingWindow