GeMM関数の働き・役割
GeMM関数は、行列やベクトルの乗算を効率的に実行するための関数です。
※GeMMは、General Matrix Multiply(一般行列乗算)の略です。
通常の行列乗算では、2つの行列を掛け算して新しい行列を作成しますが、GeMM関数は既存の行列を再利用しながら計算を行います。これにより、計算中に不要なメモリ割り当てを避けることができます。
GeMM関数の動作は、次のように数式で表されます。
C = α * (A * B) + β * C
ここで、
たとえば、ニューラルネットワークをトレーニングするときに、行列演算が頻繁に使われます。GeMM関数はこのような計算を効率化し、必要なメモリを最小限に抑えます。
GeMM関数は、行列同士の乗算だけでなく、行列とベクトル、ベクトル同士の掛け算もサポートしており、複数の形式で利用できます。
GeMM関数の引数について
行列と行列の乗算(第1書式)
bool matrix::GeMM(
const matrix &A, // 1番目の行列
const matrix &B, // 2番目の行列
double alpha, // 積ABのアルファ乗数
double beta, // 行列Cのベータ乗数
uint flags // 行列A、B、Cが転置されるかどうかを決定するENUM_GEMM値の組み合わせ(ビットOR)
);
この書式では、2つの行列を掛け算し、その結果を別の行列に保存します。数式で表すと次のようになります。
数式
matrix C[M][N] = α * (matrix A[M][K] * matrix B[K][N]) + β * matrix C[M][N]
数式の説明
- matrix C[M][N]
計算結果を保存する行列です。Cの行数はAの行数(M)と一致し、Cの列数はBの列数(N)と一致します。 - α * (matrix A[M][K] * matrix B[K][N])
AとBの行列を掛け算した結果に、スカラー値αを掛けたものです。この部分が計算の主な内容を表します。 - β * matrix C[M][N]
もともとCに保存されている値に、スカラー値βを掛けたものです。この項を追加することで、Cの初期値を計算結果に反映することができます。
引数の説明
A (const matrix &A)
1番目の行列です。計算の左側に位置する行列を指定します。たとえば、行列A[M][K]という形で入力します。
B (const matrix &B)
2番目の行列です。計算の右側に位置する行列を指定します。たとえば、行列B[K][N]という形で入力します。
α (double alpha)
AとBを掛けた結果に掛け算されるスカラー値です。この値によって、計算結果の大きさを調整します。
β (double beta)
もともとの結果行列Cの値に掛けられるスカラー値です。これを使って、行列Cが最終的な結果にどれだけ影響を与えるかを調整します。
flags (uint flags)
行列A、B、Cをそのまま使用するか、転置するかを決定します。このフラグはENUM_GEMMの列挙値を用いて指定します。複数の設定を組み合わせる場合はビットOR演算を使用します。
※転置についてはこの記事の「ENUM_GEMMについて」セクションをご参照ください。
ベクトルと行列の乗算(第2書式)
bool vector::GeMM(
const vector &A, // 水平ベクトル
const matrix &B, // 行列
double alpha, // 積ABのアルファ乗数
double beta, // ベクトルCのベータ乗数
uint flags // 行列Aが転置されるかどうかを決定するENUM_GEMM列挙値
);
この書式では、1つの水平ベクトルと1つの行列を掛け算し、その結果を別の水平ベクトルに保存します。数式で表すと次のようになります。
数式
vector C[N] = α * (vector A[K] * matrix B[K][N]) + β * vector C[N]
数式の説明
- vector C[N]
計算結果を保存する水平ベクトルです。Cの要素数(N)は、行列Bの列数と一致します。 - α * (vector A[K] * matrix B[K][N])
水平ベクトルAと行列Bを掛け算した結果に、スカラー値αを掛けたものです。 - β * vector C[N]
もともとCに保存されている値に、スカラー値βを掛けたものです。この項を追加することで、Cの初期値を計算結果に反映することができます。
この数式は、水平ベクトルと行列の掛け算に加え、スカラー値で結果を調整したり、初期値を反映したりする仕組みを表しています。
引数の説明
A (const vector &A)
水平ベクトルです。計算の左側に位置するベクトルを指定します。要素数はKで、行列Bの行数(K)と一致している必要があります。
B (const matrix &B)
掛け算する行列です。行数はベクトルAの要素数(K)と一致し、列数は結果となるベクトルCの要素数(N)に対応します。
α (double alpha)
AとBを掛けた結果に掛け算されるスカラー値です。この値によって、計算結果の大きさを調整します。
β (double beta)
もともとの結果ベクトルCの値に掛けられるスカラー値です。これを使って、Cが最終的な結果にどれだけ影響を与えるかを調整します。
flags (uint flags)
ベクトルAをそのまま使用するか、転置するかを決定します。このフラグはENUM_GEMMの列挙値を用いて指定します。
行列とベクトルの乗算(第3書式)
bool vector::GeMM(
const matrix &A, // 行列
const vector &B, // 垂直ベクトル
double alpha, // 積ABのアルファ乗数
double beta, // ベクトルCのベータ乗数
uint flags // 行列Bが転置されるかどうかを決定するENUM_GEMM列挙値
);
この書式では、1つの行列と1つの垂直ベクトルを掛け算し、その結果を別の垂直ベクトルに保存します。数式で表すと次のようになります。
数式
vector C[M] = α * (matrix A[M][K] * vector B[K]) + β * vector C[M]
数式の説明
- vector C[M]
計算結果を保存する垂直ベクトルです。Cの要素数(M)は、行列Aの行数と一致します。 - α * (matrix A[M][K] * vector B[K])
行列Aと垂直ベクトルBを掛け算した結果に、スカラー値αを掛けたものです。 - β * vector C[M]
もともとCに保存されている値に、スカラー値βを掛けたものです。この項を追加することで、Cの初期値を計算結果に反映することができます。
この数式は、行列と垂直ベクトルの掛け算に加え、スカラー値で結果を調整したり、初期値を反映したりする仕組みを表しています。
引数の説明
A (const matrix &A)
掛け算する行列です。行数は結果ベクトルCの要素数(M)と一致し、列数はベクトルBの要素数(K)と一致している必要があります。
B (const vector &B)
垂直ベクトルです。計算の右側に位置するベクトルを指定します。要素数は行列Aの列数(K)と一致している必要があります。
α (double alpha)
AとBを掛けた結果に掛け算されるスカラー値です。この値によって、計算結果の大きさを調整します。
β (double beta)
もともとの結果ベクトルCの値に掛けられるスカラー値です。これを使って、Cが最終的な結果にどれだけ影響を与えるかを調整します。
flags (uint flags)
ベクトルBをそのまま使用するか、転置するかを決定します。このフラグはENUM_GEMMの列挙値を用いて指定します。
この書式は、行列計算における標準的な処理であり、特に入力データが行列で構成されるアルゴリズムやシステムで頻繁に使用されます。
ベクトルとベクトルの乗算(第4書式)
この書式では、1つの垂直ベクトルと1つの水平ベクトルを掛け算し、その結果を行列として保存します。数式で表すと次のようになります。
数式
matrix C[M][N] = α * (vector A[M] * vector B[N]) + β * matrix C[M][N]
数式の説明
- matrix C[M][N]
計算結果を保存する行列です。Cの行数(M)はベクトルAの要素数、列数(N)はベクトルBの要素数と一致します。 - α * (vector A[M] * vector B[N])
垂直ベクトルAと水平ベクトルBを掛け算した結果に、スカラー値αを掛けたものです。 - β * matrix C[M][N]
もともとCに保存されている値に、スカラー値βを掛けたものです。この項を追加することで、Cの初期値を計算結果に反映することができます。
この数式は、垂直ベクトルと水平ベクトルの乗算に加え、スカラー値で結果を調整したり、初期値を反映したりする仕組みを表しています。
引数の説明
A (const vector &A)
1つ目のベクトルです。計算の左側に位置する垂直ベクトルを指定します。要素数は行列Cの行数(M)に一致します。
B (const vector &B)
2つ目のベクトルです。計算の右側に位置する水平ベクトルを指定します。要素数は行列Cの列数(N)に一致します。
α (double alpha)
AとBを掛けた結果に掛け算されるスカラー値です。この値によって、計算結果の大きさを調整します。
β (double beta)
もともとの結果行列Cの値に掛けられるスカラー値です。これを使って、Cが最終的な結果にどれだけ影響を与えるかを調整します。
flags (uint flags)
結果行列Cをそのまま使用するか、転置するかを決定します。このフラグはENUM_GEMMの列挙値を用いて指定します。
ENUM_GEMMについて
GeMM関数では、入力する行列やベクトルの形状を変更するために、ENUM_GEMMという列挙値を使用します。この列挙値を指定することで、計算時に行列やベクトルを「そのまま使用するか」「転置して使用するか」を柔軟に制御できます。
転置とは?
転置とは、行列やベクトルの「行」と「列」を入れ替える操作のことを指します。たとえば、次のような行列を考えます。
A = [[1, 2],
[3, 4]]
A^T = [[1, 3],
[2, 4]]
転置後は、元の行列の要素A[i][j]
が新しい位置 A^T[j][i]
に移動します。これにより、行列の形を変えることができます。
※ハット記号(^)は数学においてさまざまな意味を持つことがありますが、行列に関しては通常「転置」を示します。
具体的には、記号 A^T
の意味は、行列Aの転置を表しています。
GeMM関数では、列挙値を使って転置を制御できます。次に、各列挙値とその効果を説明します。
ENUM_GEMMの列挙値とその効果
1. TRANSP_A(行列Aを転置)
このフラグを指定すると、行列Aが転置された形で計算に使用されます。たとえば、行列Aが次のような場合、
A = [[1, 2],
[3, 4]]
フラグ TRANSP_Aを指定すると、次のように転置されて計算に使われます。
A^T = [[1, 3],
[2, 4]]
2. TRANSP_B(行列Bを転置)
このフラグを指定すると、行列Bが転置された形で計算に使用されます。たとえば、行列Bが次のような場合、
B = [[5, 6],
[7, 8]]
フラグ TRANSP_Bを指定すると、次のように転置されて計算に使われます。
B^T = [[5, 7],
[6, 8]]
3. TRANSP_C(結果行列Cを転置)
このフラグを指定すると、計算結果を保存する行列Cが転置された形で保存されます。たとえば、通常の計算結果Cが次のような場合、
C = [[9, 10],
[11, 12]]
フラグ TRANSP_Cを指定すると、次のように転置されて保存されます。
C^T = [[9, 11],
[10, 12]]
フラグの組み合わせ
これらのフラグは、複数を同時に指定して使用することができます。たとえば、行列Aと行列Bを両方転置して計算したい場合は、以下のように組み合わせます。
uint flags = TRANSP_A | TRANSP_B;
この指定により、AとBの両方が転置されて計算に使用されます。
転置を使う目的
転置を指定する理由は、主に次のようなものです。
- 計算可能な形に整える
行列やベクトルの形が一致しておらず、そのままでは掛け算できない場合に使用します。 - 計算結果の形式を調整する
計算結果を特定の形にしたい場合や、結果を別の形で利用したい場合に役立ちます。 - 効率性の向上
計算効率を高めるため、行列の形状を変更することで最適化を図ることがあります。
ENUM_GEMMを使用することで、計算の柔軟性と効率性を向上させることができます。転置操作を適切に活用することで、行列計算の幅が広がり、さまざまな場面で応用が可能になります。
GeMM関数の戻り値について
GeMM関数の戻り値は、計算の成否を表します。以下に詳しく説明します。
成功した場合
関数が正常に動作し、指定した計算が実行された場合、戻り値はtrueになります。この場合、計算結果は行列またはベクトルCに保存されます。
失敗した場合
関数が失敗した場合、戻り値はfalseになります。失敗する原因として、以下のようなケースが考えられます。
GeMM関数を使う際の注意点
GeMM関数を使う際には、対応するデータ型や実装方法について理解する必要があります。この関数は、次のような特徴を持っています。
対応するデータ型
- float型
小数を扱うデータ型です。計算精度が必要な場合に使用しますが、メモリの使用量が少ないため、大量のデータを扱う際にも適しています。 - double型
float型よりも精度が高い小数を扱うデータ型です。科学計算や高精度が求められる場面で使用されます。 - complex型
複素数を扱うデータ型です。複素数とは、実数(普通の数)に加えて、虚数単位「i」を含む数です。たとえば、「3 + 4i」のような形式で表されます。複雑な数式計算や物理シミュレーションで必要になることがあります。
※複素数についての詳細は下記の記事をご参照ください。
テンプレートについて
GeMM関数はテンプレートとして実装されています。テンプレートとは、関数やクラスを特定の型に依存させずに汎用的に使える仕組みのことです。GeMM関数では、float型、double型、complex型など、さまざまなデータ型に対応しています。テンプレートを使用することで、コードを繰り返し書く必要がなくなり、異なるデータ型での計算を柔軟に行うことができます。
※テンプレートについての詳細は下記の記事をご参照ください。
GeMM関数の実装例とコードの見方
公式リファレンスには、GeMM関数がどのように使われるかを示す例がいくつかあります。以下は、その書式の意味について説明します。
行列同士の計算(例)
bool matrix<T>::GeMM(const matrix<T> &A, const matrix<T> &B, T alpha, T beta, ulong flags);
構成要素の意味
- matrix: T(データ型)を使う行列。ここでTにはfloat、double、complexが入ります。
- const matrix &A, const matrix &B: 計算に使う行列AとB。constは「この行列は変更しない」という意味です。
- T alpha, T beta: 計算結果を調整するスカラー値です。T型なので、floatやdoubleなど対応する型になります。
- ulong flags: ENUM_GEMMを使って、行列を転置するかどうかを指定します。
ベクトルと行列の計算(例)
bool vector<T>::GeMM(const vector<T> &A, const matrix<T> &B, T alpha, T beta, ulong flags);
構成要素の意味
このように、コードの書式からどのデータ型を使って計算するかがわかります。また、bool型の戻り値が成功(true)か失敗(false)を示します。
GeMM関数を使ったサンプルコード
void OnStart()
{
// ベクトルを定義 (vector_aは5つの要素を持つベクトル、vector_bは4つの要素を持つベクトル)
vector vector_a = {1, 2, 3, 4, 5}; // ベクトルaの要素は1, 2, 3, 4, 5
vector vector_b = {4, 3, 2, 1}; // ベクトルbの要素は4, 3, 2, 1
// 結果を格納するための行列matrix_cを宣言
matrix matrix_c;
//--- 2つのベクトルを使ってGeMM(行列の一般的な積)を計算
matrix_c.GeMM(vector_a, vector_b, 1, 0);
// 引数の意味:
// vector_a: 行ベクトル、vector_b: 列ベクトルとして使用
// 1: スケーリング係数、0: バイアス値(行列に加算されるオフセット)
// 計算結果はmatrix_cに格納
// 計算結果を出力
Print("matrix_c:\n ", matrix_c, "\n");
/*
出力結果の例:
matrix_c:
[[4, 3, 2, 1]
[8, 6, 4, 2]
[12, 9, 6, 3]
[16, 12, 8, 4]
[20, 15, 10, 5]]
*/
//--- 行列をベクトルから生成
// 5行1列の行列matrix_aを定義
matrix matrix_a(5, 1);
// 1行4列の行列matrix_bを定義
matrix matrix_b(1, 4);
// vector_aの要素をmatrix_aの0列目(唯一の列)に格納
matrix_a.Col(vector_a, 0); // Colメソッドを使用して列として追加
// vector_bの要素をmatrix_bの0行目(唯一の行)に格納
matrix_b.Row(vector_b, 0); // Rowメソッドを使用して行として追加
// 行列matrix_aとmatrix_bを出力
Print("matrix_a:\n ", matrix_a);
Print("matrix_b:\n ", matrix_b);
/*
出力結果の例:
matrix_a:
[[1]
[2]
[3]
[4]
[5]]
matrix_b:
[[4, 3, 2, 1]]
*/
//--- 2つの行列matrix_aとmatrix_bを使ってGeMMを計算
matrix_c.GeMM(matrix_a, matrix_b, 1, 0);
// 引数の意味:
// matrix_a: 行列1、matrix_b: 行列2
// 1: スケーリング係数、0: バイアス値(行列に加算されるオフセット)
// 計算結果は再度matrix_cに格納
// 計算結果を出力
Print("matrix_c:\n ", matrix_c);
/*
出力結果の例:
matrix_c:
[[4, 3, 2, 1]
[8, 6, 4, 2]
[12, 9, 6, 3]
[16, 12, 8, 4]
[20, 15, 10, 5]]
*/
}
コード解説:ベクトルと行列を使った計算の流れ
このコードは、MQL5でベクトルと行列を用いて計算を行い、その結果を出力するものです。具体的には、ベクトル同士の積(外積のような操作)を計算し、それを行列として出力します。また、別の方法でも同じ結果を得られることを確認しています。
1. ベクトル vector_a と vector_b の定義
最初に、2つのベクトルが定義されています。
2. GeMM メソッドによるベクトルの積
次に、GeMM メソッドを用いて vector_a
と vector_b
を使った計算が行われます。
matrix_c.GeMM(vector_a, vector_b, 1, 0);
- GeMM関数の引数の意味:
計算の内容:
matrix_c[i][j] = vector_a[i] * vector_b[j]
ここで、i
はvector_aのインデックス(行番号)、j
はvector_bのインデックス(列番号)です。
スカラー値の適用alpha
が1なので、計算結果はそのまま保持されます。beta
が0なので、既存のmatrix_cの値は無視されます。
計算例
vector_a = [1, 2, 3, 4, 5]
と vector_b = [4, 3, 2, 1]
の要素を計算します。
1行1列目: 1 * 4 = 4
1行2列目: 1 * 3 = 3
1行3列目: 1 * 2 = 2
1行4列目: 1 * 1 = 1
2行1列目: 2 * 4 = 8
2行2列目: 2 * 3 = 6
…
同様に計算を続けると、結果は次のようになります。
matrix_c:
[[4, 3, 2, 1]
[8, 6, 4, 2]
[12, 9, 6, 3]
[16, 12, 8, 4]
[20, 15, 10, 5]]
ベクトルを行列として定義
matrix matrix_a(5, 1);
matrix matrix_b(1, 4);
matrix_a.Col(vector_a, 0);
matrix_b.Row(vector_b, 0);
ここでは、matrix_a
とmatrix_b
という行列を定義し、それぞれvector_a
とvector_b
の要素を使って行列を生成しています。
- matrix_aは5行1列の行列で、vector_aをそのまま縦方向に配置します。
matrix_a:
[[1]
[2]
[3]
[4]
[5]]
matrix_b
は1行4列の行列で、vector_bをそのまま横方向に配置します。
matrix_b:
[[4, 3, 2, 1]]
行列同士のGeMM計算
matrix_c.GeMM(matrix_a, matrix_b, 1, 0);
ここでは、matrix_a
とmatrix_b
を使って再度計算を行います。この場合、行列の掛け算が行われます。
matrix_c[i][j] = sum(matrix_a[i][k] * matrix_b[k][j])
ここで、k
はmatrix_a
の列番号(またはmatrix_b
の行番号)を表します。
計算例
- 1行1列目: (1 * 4) = 4
- 1行2列目: (1 * 3) = 3
- 2行1列目: (2 * 4) = 8
結果として、次の行列が得られます。
matrix_c:
[[4, 3, 2, 1]
[8, 6, 4, 2]
[12, 9, 6, 3]
[16, 12, 8, 4]
[20, 15, 10, 5]]
結果のまとめ
このコードでは、ベクトル同士、そして行列同士の掛け算を実行し、結果をmatrix_c
に格納しています。計算結果はどちらの方法でも同じですが、matrix_aやmatrix_bのように行列形式を利用することで、より汎用的な計算が可能になります。
以下は、算式を1行で表現する形に修正した解説です。
外積とは何か?
外積は、2つのベクトルを使って新しいベクトルや行列を作る計算方法です。以下のように場合によって結果が異なります。
1. ベクトルの外積(行列の作成)
もし2つのベクトルa と b があった場合、外積では次のような行列を作ります。
行列の各要素は以下のように計算されます: matrix[i][j] = a[i] * b[j]
具体例:
- a = [1, 2, 3]
- b = [4, 5]
計算結果: matrix = [ [14, 15], [24, 25], [34, 35] ]
[ [4, 5], [8, 10], [12, 15] ]
ポイント:
2. ベクトルの外積(3次元)
3次元ベクトルの場合、外積は新しいベクトルを作ります。この新しいベクトルは、元の2つのベクトルに対して垂直な方向を示します。
計算式: c[0] = a[1]*b[2] – a[2]*b[1],
c[1] = a[2]*b[0] – a[0]*b[2],
c[2] = a[0]*b[1] – a[1]*b[0]
具体例:
- a = [1, 0, 0]
- b = [0, 1, 0]
計算結果: c = [0, 0, 1]
内積とは何か?
内積は、2つのベクトルを掛け合わせて1つの数値(スカラー)を得る計算方法です。
計算式: dot_product = a[0]*b[0] + a[1]*b[1] + a[2]*b[2]
具体例:
- a = [1, 2, 3]
- b = [4, 5, 6]
計算結果: dot_product = 14 + 25 + 3*6 = 4 + 10 + 18 = 32