matrix::Activation関数の働き・役割

Activation関数は、ベクトル(数を一列に並べたデータ)や行列(数を縦横に並べた表形式のデータ)の各要素に対して、活性化関数(人工ニューラルネットワークで使う変換用の関数)をまとめて適用するためのメソッドです。呼び出し元となるベクトルや行列に含まれる値を入力として受け取り、指定した活性化関数で計算した結果を、別のベクトルまたは行列に書き込みます。
人工ニューラルネットワーク(入力から出力を学習するための数値モデル)では、ニューロンの出力を決めるときに活性化関数を使います。Activation関数は、この活性化処理をベクトルや行列全体に対して一度に行うためのメソッドであり、学習済みのモデルの順伝播(入力から出力を計算する処理)や、活性化関数のテストなどで利用できます。
Activation関数には、ベクトル用と行列用の二種類のメソッドがあります。ベクトル用はベクトル型に対するメンバメソッド、行列用は行列型に対するメンバメソッドとして用意されています。行列版には、どの軸(行方向か列方向か)で処理するかを指定するためのaxis引数を持つオーバーロード(同名で引数構成が異なる関数)もあります。
活性化関数の種類は、ENUM_ACTIVATION_FUNCTION列挙体(あらかじめ決まった識別子を集めた型)で指定します。たとえば、AF_RELUはReLU(入力が正ならそのまま、負ならゼロにする関数)、AF_LRELUはLeaky ReLU、AF_SWISHはSwishといったように、多くの代表的な活性化関数が選べるようになっています。
Activation関数の引数について
// ベクトルに対するActivationメソッド
bool vector::Activation(
vector& vect_out, // 値を書き込む出力ベクトル
ENUM_ACTIVATION_FUNCTION activation, // 活性化関数の種類
... // 追加パラメータ
);
// 行列に対するActivationメソッド(軸指定なし)
bool matrix::Activation(
matrix& matrix_out, // 値を書き込む出力行列
ENUM_ACTIVATION_FUNCTION activation // 活性化関数の種類
);
// 行列に対するActivationメソッド(軸指定あり)
bool matrix::Activation(
matrix& matrix_out, // 値を書き込む出力行列
ENUM_ACTIVATION_FUNCTION activation, // 活性化関数の種類
ENUM_MATRIX_AXIS axis, // 処理する軸
... // 追加パラメータ
);
ベクトル版Activationメソッドの書式と引数

最初に、ベクトル(数値を一列に並べたデータ)に対して活性化関数(ニューラルネットワークの出力を決めるための変換関数)を適用する書式です。
bool vector::Activation(
vector& vect_out, // ベクトルに計算結果を書き込む
ENUM_ACTIVATION_FUNCTION activation, // 活性化関数の種類
... // 追加パラメータ
);
第1引数 vect_out について
第1引数vect_outは、計算結果を書き込むための出力ベクトルです。呼び出し元となるベクトルに入っている値を入力として、指定した活性化関数で変換した結果が、このvect_outに格納されます。入力側のベクトルはメソッド呼び出し元のオブジェクトであり、vect_outはあくまで結果を受け取る入れ物という関係になります。サイズ(要素数)は、呼び出し元ベクトルのサイズに合わせて処理されるため、通常は特別な初期化を意識せずにそのまま渡して問題ありません。
第2引数 activation について
第2引数activationは、ENUM_ACTIVATION_FUNCTION列挙体(あらかじめ登録された活性化関数の種類を表す定数群)から選ぶ値です。この引数で、どの活性化関数を使って各要素を変換するかを指定します。例えばAF_ELUはELU(指数関数を用いた活性化)、AF_LINEARは線形関数、AF_LRELUはLeaky ReLU、AF_RELUはReLU、AF_SWISHはSwish、AF_TRELUはThresholded ReLU、AF_PRELUはParametric ReLUといった意味を持ちます。どの識別子を選ぶかによって、入力値xに対して適用される数式が変わり、その結果として出力ベクトルの値の分布も変化します。
追加パラメータ … について
最後の可変長引数「…」は、一部の活性化関数に対して与える追加パラメータ(挙動を細かく調整するための係数や配列)を受け取るための部分です。例えばAF_ELUではalphaという係数、AF_LINEARではalphaとbeta、AF_LRELUではalpha、AF_RELUではalphaとmax_valueとtreshold、AF_SWISHではbeta、AF_TRELUではtheta、AF_PRELUではalpha配列といった具合に、関数ごとに必要な値が異なります。これらの追加パラメータを省略した場合は、ドキュメントに記載されている既定値が自動的に使われるため、まずはパラメータ無しで試し、必要に応じて数値を変えて挙動を調整する使い方もできます
行列版Activationメソッド(axisなし)の書式と引数
次に、行列(数値を縦横に並べた表のようなデータ)全体に対して、要素ごとに活性化関数を適用する書式です。こちらはaxis(軸)を指定しない、より単純な形です
bool matrix::Activation(
matrix& matrix_out, // 行列に計算結果を書き込む
ENUM_ACTIVATION_FUNCTION activation // 活性化関数の種類
);
第1引数 matrix_out について
第1引数matrix_outは、活性化関数を適用した結果を書き込むための出力行列です。呼び出し元となる行列に含まれている全ての要素について、指定した活性化関数を計算し、その値をmatrix_out側に順に格納します。行と列のサイズは、入力側の行列に合わせて処理されるため、基本的には「入力と同じ形の行列に結果が入る」と理解しておくとイメージしやすくなります。複数のサンプル(入力データの組)を行方向や列方向に並べた行列を、一度にまとめて変換したい場合に利用できます。
第2引数 activation について
第2引数activationは、ベクトル版と同様にENUM_ACTIVATION_FUNCTION列挙体から選ぶ活性化関数の種類です。行列版であっても、変換は各要素に対して行われるため、基本的な考え方はベクトル版と変わりません。同じAF_RELUを指定すれば、各要素に対してReLUの数式が適用され、AF_SWISHを指定すればSwishの数式が適用されるといった形で、行列全体の各要素が変換されます。
行列版Activationメソッド(axisあり)の書式と引数
三つ目の書式は、行列に対して活性化関数を適用する点は同じですが、axis引数を使ってどの軸(行方向か列方向か)に沿って処理するかを指定できる形です。軸という考え方は、行列のどちらの方向をひとまとまりとして扱うかを決めるためのもので、ニューラルネットワークでサンプルの並び方を意識するときに役立ちます。
bool matrix::Activation(
matrix& matrix_out, // 行列に計算結果を書き込む
ENUM_ACTIVATION_FUNCTION activation, // 活性化関数の種類
ENUM_MATRIX_AXIS axis, // 処理する軸
... // 追加パラメータ
);
第1引数 matrix_out について
第1引数matrix_outは、axisを指定しない行列版と同じく、計算結果を書き込む出力行列です。活性化関数の種類や軸の指定に応じて計算された値が、このmatrix_outに格納されます。行列の形そのものは入力側と揃えた状態で扱われるため、「どの方向を基準に計算するか」という情報はaxis側に任せ、matrix_outは結果の受け皿としてシンプルに考えておくと整理しやすくなります。
第2引数 activation について
第2引数activationは、こちらもENUM_ACTIVATION_FUNCTION列挙体から選ぶ活性化関数の種類を表す引数です。ベクトル版やaxis無しの行列版と同じ考え方で、どの数式に従って行列の値を変換するかを決める役割を持ちます。選べる識別子の種類や意味は、他の書式と共通です。
第3引数 axis について
第3引数axisは、ENUM_MATRIX_AXIS列挙体(行列のどの方向を基準にするかを表す定数群)から選ぶ値です。AXIS_HORZは水平方向の軸、つまり行方向を意味し、AXIS_VERTは垂直方向の軸、つまり列方向を意味します。この引数によって、「行ごとに計算するのか」「列ごとに計算するのか」といった処理の向きが決まります。例えば、行ごとに一つのサンプルが並んでいる行列であれば、どちらをサンプル方向と見なすかによって、axisにAXIS_HORZとAXIS_VERTのどちらを指定するかが変わってきます。
追加パラメータ … について
最後の可変長引数「…」は、ベクトル版と同じく、一部の活性化関数で必要となる追加パラメータを渡すための部分です。ELUのalpha、線形関数のalphaやbeta、Leaky ReLUのalpha、ReLUのalphaやmax_valueやtreshold、Swishのbeta、Thresholded ReLUのtheta、Parametric ReLUのalpha配列といった値を、必要に応じてここで指定します。何も渡さなかった場合には、ドキュメントで定義されている既定値が自動的に利用されます。行列を対象にするときも、この追加パラメータの考え方はベクトル版と同じであり、「活性化関数ごとの細かな挙動を数字で調整する場所」と考えると理解しやすくなります。
Activation関数の戻り値について
Activation関数の戻り値は真偽値(処理が正しく行えたかどうかを示す値)です。計算が正常に完了した場合はtrueが返され、内部で何か問題が起きて処理に失敗した場合はfalseが返されます。trueが返っているときには、第1引数で指定したベクトルや行列に、活性化関数(入力を別の値に変換するための関数)を適用した結果が格納されています。falseが返っているときは、出力先の内容が期待どおりではない可能性があるため、そのまま後続の計算に使わず、前提条件やエラーメッセージを確認した方が安全です。
ドキュメントでは、失敗してfalseが返る具体的な条件が細かく列挙されているわけではありませんが、一般的には、行列やベクトルのサイズが内部の期待と合わない場合や、軸を指定する引数に不適切な値が渡された場合、あるいは内部的なメモリ確保に問題があった場合などが考えられます。
Activation関数のサンプルコード
```mql5
//+------------------------------------------------------------------+
//| ActivationSample.mq5 |
//| Activationメソッドの基本的な使い方を確認するためのサンプル |
//| ・ベクトルに価格データを入れる |
//| ・別のベクトルに活性化関数の結果を書き込む |
//| ・ログに出力して値の変化を確認する |
//+------------------------------------------------------------------+
#property script_show_inputs // このファイルは「スクリプト」として動作することを示す指定
//+------------------------------------------------------------------+
//| 入力パラメータ |
//| ユーザーがスクリプトを実行するときに、ダイアログから変更できる値 |
//+------------------------------------------------------------------+
input int InpBars = 10; // 入力として使うバー本数(ローソク足の本数)。
// たとえば10なら、最新から10本分の終値を使う。
//+------------------------------------------------------------------+
//| スクリプトのエントリポイント |
//| OnStartは、スクリプト実行時に最初に呼び出される特別な関数 |
//+------------------------------------------------------------------+
void OnStart()
{
//--- 十分なバーがあるか確認する
// Bars関数は、指定した銘柄と時間足で、どれだけのバーがあるかを返す。
// _Symbol は現在のチャートの銘柄、_Period は現在のチャートの時間足を表す。
// 必要な本数(InpBars)より少なければ、そもそも計算に使うデータが足りない。
if(Bars(_Symbol,_Period) < InpBars)
{
// 条件を満たさない場合は、ユーザーに状況を知らせて処理を中止する。
// Print関数は、ターミナルのログにメッセージを表示する。
Print("バー本数が不足しています。InpBarsを小さくしてください。");
return; // ここでOnStart関数を終了し、スクリプトの実行を止める。
}
//--- 終値を格納するベクトルを用意する
// vector型は、double型の値を縦に一列に並べて持つための専用型。
// Initメソッドで、要素数(ここではInpBars)を指定して領域を確保する。
vector closes;
closes.Init((ulong)InpBars); // InpBarsはint型なので、ulongにキャストして渡している。
//--- 最新バーから順番に終値を格納する
// for文で0からInpBars-1まで繰り返し、各バーの終値を取得してベクトルに入れる。
// i = 0 が最新のバー、i が大きくなるほど過去のバーになる。
for(int i = 0; i < InpBars; i++)
{
// iClose関数は、指定したシンボル・時間足・バー番号の終値を返す。
// 第3引数のiがバーインデックスであり、0が最新、1が一つ前、と数えていく。
double close_price = iClose(_Symbol,_Period,i);
// ベクトルの各要素には、添え字として0,1,2,...の番号でアクセスする。
// ここでは、「i番目のバーの終値」を「closesのi番目の要素」に代入している。
// 添え字はulong型なので、(ulong)i に変換して使っている。
closes[(ulong)i] = close_price;
}
//--- 「終値の変化量」を格納するベクトルを用意する
// ニューラルネットワークに渡す入力として、
// 「絶対的な価格」ではなく「最新終値との差」(変化量)を例として使う。
vector inputs;
inputs.Init((ulong)InpBars); // 要素数は終値ベクトルと同じInpBarsとする。
// 最新バー(バーインデックス0)の終値を基準値として取得する。
// closes[0] には最新バーの終値が入っている。
double last_close = closes[0];
//--- 変化量ベクトルを作成する
// 各バーの終値から最新終値を引き、その差をinputsベクトルに入れる。
// これにより、「どれくらい上がったか・下がったか」が数値として表現される。
for(ulong i = 0; i < inputs.Size(); i++)
{
// closes[i] は i番目のバーの終値を表す。
// last_close は最新バーの終値。
// closes[i] - last_close は、「最新終値に対してどれくらい差があるか」を意味する。
inputs[i] = closes[i] - last_close;
}
//--- 活性化関数の出力を受け取るためのベクトルを用意する
// ここでは、同じ入力データに対して2種類の活性化関数を試す。
// 1つ目はAF_RELU(ReLU、Rectified Linear Unit)、2つ目はAF_SWISH(Swish)。
vector relu_out; // ReLU適用後の結果を入れるベクトル
vector swish_out; // Swish適用後の結果を入れるベクトル
//--- ReLUで活性化(AF_RELU)
// Activationメソッドは、呼び出し元のベクトル(ここではinputs)を入力として、
// 各要素に活性化関数を適用し、その結果を第1引数のベクトルに書き込む。
// 第2引数で、どの活性化関数を使うかを指定する。
//
// ここで指定しているAF_RELUは、ReLUという活性化関数を表す識別子。
// ReLUの基本形では、xが0以上ならf(x)=x、0未満ならf(x)=0というルールで変換する。
//(ドキュメントでは追加パラメータを指定することで、もう少し柔らかいルールも表現できる)
if(!inputs.Activation(relu_out,AF_RELU))
{
// Activationメソッドの戻り値がfalseのときは、計算に失敗したことを意味する。
// ここでは、単純にエラーメッセージを出して処理を終了している。
Print("AF_RELUでのActivationに失敗しました。");
return;
}
//--- Swishで活性化(AF_SWISH)
// 同じ入力ベクトルinputsに対して、今度はSwishという活性化関数を適用する。
// AF_SWISHはSwishを表す識別子で、ドキュメント上は
// f(x) = x / (1 + exp(-x * beta))
// という形の関数で説明されている。
// ここではbetaを追加パラメータとして渡していないため、既定値(通常は1.0)が使われる。
if(!inputs.Activation(swish_out,AF_SWISH))
{
// Swishでの計算が失敗した場合も、メッセージを出して処理を終了する。
Print("AF_SWISHでのActivationに失敗しました。");
return;
}
//--- 結果をログに出力する
// Print関数にベクトルを渡すと、その中身(各要素の値)をまとめて表示してくれる。
// ここでは、元の入力データと、それにReLU・Swishを適用した結果を並べて表示する。
// これにより、活性化関数の違いによって値がどう変わるかを確認できる。
Print("元の入力ベクトル(終値の変化量) = ", inputs);
Print("ReLU適用後のベクトル = ", relu_out);
Print("Swish適用後のベクトル = ", swish_out);
// ここまででスクリプトの処理は完了。
// OnStart関数の終わりに到達すると、自動的にスクリプトの実行も終了する。
}
```
Activation関数を使ってEAを作る際のアイディア
Activation関数は、ニューラルネットワーク(入力データから出力を学習する数値モデル)の中で、各層の出力を変換する役割を持つメソッドです。EA(自動売買プログラム)での使い方を考えるときは、行列やベクトルによる計算と組み合わせて、売買判断のロジック全体の一部として活用する形がイメージしやすくなります。
一つの考え方として、複数のテクニカル指標(チャート上で相場の状態を線や数値で表す道具)や価格データをベクトルや行列にまとめ、そのデータに対して重み行列を掛ける処理を作り、その直後にActivation関数を適用する構成があります。これにより、「入力データを重み付きの足し算で一次変換する層」と「Activation関数で非線形な変換を行う層」を交互に重ねた、シンプルな多層パーセプトロン型(何層かの線形変換と活性化関数を重ねたモデル)のEAを構築できます。中間層ではAF_RELUやAF_ELU、AF_SWISHなどの活性化関数を使うことで、価格と指標の関係が直線では表しきれないような局面も表現の対象に含めることができます。
売買方向をカテゴリとして判断したい場合、例えば上昇傾向、下降傾向、様子見の三つに分類したいようなEAでは、行列版のActivation関数を出力層の直前に使い、AF_SOFTMAXのような分類向けの活性化関数を適用する構成が考えられます。ソフトマックス関数(各クラスに属する確率のような値を出力する活性化関数)を使うと、出力ベクトルの各要素を「それぞれのパターンに属する度合い」として解釈しやすくなります。EA側では、この出力の中で最も大きい要素を採用して売買方向を決めたり、あるしきい値(条件を切り替える境目となる値)を超えたときだけエントリーするようなルールを組み合わせたりすることができます。
将来のリターンや価格変化量など、連続値を予測する回帰問題(答えが数直線上の連続的な値になる問題)としてEAのロジックを設計する場合には、隠れ層(入力と出力の間に挟む中間の層)でAF_RELUやAF_LRELU、AF_SWISHなどを用い、最終出力層にAF_LINEARを使うパターンが考えられます。中間層のActivation関数で入力と出力の関係を柔軟に表しつつ、最後に線形出力を使うことで、上方向か下方向かだけでなく、「どの程度の大きさの値動きを見込んでいるか」という情報を連続量として扱うことができます。EAでは、この予測値が一定以上のプラスなら買い、一定以下のマイナスなら売り、その中間なら様子見といったルールに結び付けることができます。
また、損失関数(予測と実際の差を数値で評価する関数)や導関数(ある関数がどの方向にどれくらい変化しやすいかを表す値)と組み合わせて、EA内部でパラメータを更新していくオンライン学習(稼働中に少しずつ重みや係数を調整していく学習)を行う構成も考えられます。この場合、Activation関数は各層の出力を計算するだけでなく、その導関数を通じて誤差の逆伝播(出力の誤差を入力側にさかのぼって伝える計算)の中でも重要な役割を果たします。相場の状態が時間とともに変わることを前提に、最新のデータを取り込みながら重み行列やバイアス(出力に足し込む定数項)を更新していくEAでは、Activation関数とその周辺の数値計算を安定させることが、ロジック全体の挙動を保つうえで重要になります。
最後に、Activation関数は単体では売買条件そのものを決めるものではなく、あくまで行列やベクトルを使った数値モデルの中の一つの変換処理という位置付けになります。EAの設計では、どの指標や価格情報を入力とするか、どのような時間枠でデータをまとめるか、どのような損切りや利確のルールと組み合わせるかといった周辺部分と合わせて、Activation関数をどの層にどの種類で使うかを考えていくことで、ニューラルネットワーク的なロジックを含んだ自動売買システムを組み立てていくことができます。


