システムトレードと機械学習のための活性化関数入門

Python
スポンサーリンク
スポンサーリンク
  1. はじめに
  2. 活性化関数の役割
    1. ニューラルネットワークとは?
    2. ニューロンとは?
    3. 「発火」するとは?
  3. 主な活性化関数の種類
    1. ステップ関数
    2. シグモイド関数
      1. 勾配消失問題とは?
      2. なぜ勾配消失が起きるのか?
      3. 具体例を用いた計算
        1. 1. 入力が0の場合
        2. 2. 入力が5の場合
        3. 3. 入力が-5の場合
      4. 勾配消失問題の影響
      5. 勾配消失問題への対策
        1. 1. ReLU関数の活用
        2. 2. バッチ正規化
        3. 3. 適切な初期値設定
    3. ReLU関数(Rectified Linear Unit)
      1. ReLU関数の計算過程(具体例)
        1. 1. 入力が正の値の場合
        2. 2. 入力が0の場合
        3. 3. 入力が負の値の場合
      2. ReLU関数の勾配(微分値)について
        1. 勾配の具体例
      3. ReLU関数が勾配消失問題を防ぐ理由
        1. 例: 深い層での勾配の伝播
      4. ReLU関数の注意点
    4. タンジェント双曲線関数
    5. Tanh関数の計算過程(具体例)
      1. 1. 入力が0の場合
      2. 2. 入力が正の値(例: 2)の場合
      3. 3. 入力が負の値(例: -2)の場合
    6. Tanh関数の微分(勾配)について
      1. 1. 入力が0の場合
      2. 2. 入力が2の場合
      3. 3. 入力が-2の場合
    7. Tanh関数が勾配消失問題を受けにくい理由
  4. 活性化関数の注意点
  5. 活性化関数を使ったPythonコード
  6. サンプルコードの解説
    1. 1. ライブラリのインポート
    2. 2. ReLU関数の定義
    3. 3. シグモイド関数の定義
    4. 4. 入力データの作成
    5. 5. ReLU関数の適用
    6. 6. ReLU関数の結果を表示
    7. 7. シグモイド関数の適用
    8. 8. シグモイド関数の結果を表示
    9. 全体の流れ
  7. システムトレードでの活性化関数の応用
  8. まとめ

はじめに

システムトレード機械学習の分野では、数学的なモデルを活用して予測や意思決定を行います。その中で重要な役割を果たすのが「活性化関数」です。
本記事では、活性化関数の基本的な役割や種類、活性化関数がどのようにシステムトレード機械学習に影響を与えるかについて解説します。

活性化関数の役割

活性化関数は、ニューラルネットワークにおけるニューロンを活性化するために使用されます。ニューロンが数値を加工して次の層に送る際、この加工を行う仕組みが活性化関数です。

活性化関数の主な役割は次の通りです。

  1. ニューロンがどの程度「発火」するかを決める。
  2. データに非線形性を加え、複雑な問題に対応できるようにする。
  3. 出力を特定の範囲に収めることで、計算の安定性を保つ。

※ここで「層」とは、ニューラルネットワークが情報を処理するためのステップのことです。例えば、料理を作るときに材料を準備する段階、調理する段階、そして盛り付ける段階があるように、ニューラルネットワークでも情報を処理する段階が分かれています。この層は「入力層」「中間層(隠れ層)」「出力層」の3つに分けられ、情報が順番に処理されていきます。

例えば、システムトレードで株価データを使った予測モデルを作る場合、活性化関数を使わないと単純な足し算や掛け算しかできません。しかし、活性化関数を導入することで、より複雑なパターンを学習することが可能になります。

ニューラルネットワークとは?

ニューラルネットワークは、人間の脳神経(ニューロン)の働きをモデル化した計算手法です。複数の計算ユニット(人工ニューロン)が層状に配置されており、入力を処理して出力を生成します。これにより、画像認識や株価予測などの複雑な問題を解決する能力を持っています。


ニューロンとは?

ニューロンは、ニューラルネットワークの基本単位で、数値を受け取り、それを加工して次の層に送る役割を果たします。実際のニューロンと同様に、「入力を受けて反応(発火)する」という仕組みを模倣しています。


「発火」するとは?

「発火」とは、ニューロンが一定の条件を満たしたときに信号を送る動作を指します。人工ニューロンでは、この条件を活性化関数が決定します。例えば、後程解説するReLU関数では入力値が0より大きいときに「発火」と見なし、その値を出力します。これは、生物学的なニューロンが興奮して信号を伝達する仕組みを模倣しています。

主な活性化関数の種類

活性化関数にはさまざまな種類があります。それぞれの特徴と用途について解説します。

ステップ関数

ステップ関数は入力が一定の値を超えると「1」を出力し、それ以外の場合は「0」を出力します。この関数は昔の単純なモデルでよく使われていましたが、現在の複雑なモデルではほとんど使われません。

: 入力が0より大きければ1、そうでなければ0を返します。

シグモイド関数

シグモイド関数は、入力値を0から1の間に変換する関数です。この特徴により、確率のような値を扱う場合に適しています。

式: f(x) = 1 / (1 + exp(-x))

例えば、x = 2 の場合を考えてみましょう。

  1. まず、exp(-x) を計算します。ここで exp(-x) とは、自然対数e(おおよそ 2.718)を -x 乗したものです。
    • x = 2 の場合、exp(-2) はおよそ 0.135 になります。
  2. 次に、1 + exp(-x) を計算します。
    • 1 + 0.135 = 1.135
  3. 最後に、1 を 1.135 で割ります。
    • 1 / 1.135 ≈ 0.881

したがって、f(2) ≈ 0.881 となり、出力が 0 から 1 の間に収まることがわかります。このようにシグモイド関数数は入力を特定の範囲に収めることで、次の層に渡す情報を調整します。

シグモイド関数の特徴として勾配消失という現象が起こり得る、という点が挙げられます。

勾配消失問題とは?

勾配消失問題は、ニューラルネットワークを訓練する際に起こる問題の一つです。この問題は、深い層を持つネットワークで学習が進むにつれ、各層の重みを更新する「勾配」がどんどん小さくなり、最終的にほぼゼロになってしまう現象を指します。

なぜ勾配消失が起きるのか?

シグモイド関数のような活性化関数を考えます。この関数は入力値を0から1の範囲に圧縮しますが、入力値が大きすぎるか小さすぎると、その傾き(微分値)が非常に小さくなります。。

具体例を用いた計算

1. 入力が0の場合
  • 計算過程
    f(0) = 1 / (1 + exp(-0)) = 1 / 2 = 0.5
    f'(0) = f(0) * (1 – f(0)) = 0.5 * (1 – 0.5) = 0.5 * 0.5 = 0.25
  • 結果
    傾き(勾配)は0.25です。この場合、まだ十分に大きな値なので、学習に問題はありません。

2. 入力が5の場合
  • 計算過程
    f(5) = 1 / (1 + exp(-5)) ≈ 1 / (1 + 0.0067) ≈ 0.9933
    f'(5) = f(5) * (1 – f(5)) = 0.9933 * (1 – 0.9933) ≈ 0.9933 * 0.0067 ≈ 0.0066
  • 結果
    傾き(勾配)は約0.0066です。勾配がかなり小さいため、この層の学習が遅くなります。

3. 入力が-5の場合
  • 計算過程
    f(-5) = 1 / (1 + exp(5)) ≈ 1 / (1 + 148.413) ≈ 1 / 149.413 ≈ 0.0067
    f'(-5) = f(-5) * (1 – f(-5)) = 0.0067 * (1 – 0.0067) ≈ 0.0067 * 0.9933 ≈ 0.0066
  • 結果
    傾き(勾配)は約0.0066です。この場合も勾配が非常に小さく、学習にほとんど寄与しなくなります。

勾配消失問題の影響

ニューラルネットワークでは、各層の勾配は次の層から伝播されて計算されます(誤差逆伝播法)。勾配が小さい層が何層も重なると、勾配がほとんどゼロになってしまい、最初の層では重みが更新されません。

  • 例:勾配が連続的に小さくなる場合
    初期の勾配が0.5だとします。それが各層で微分され、次のように小さくなるとします。
    層1: 0.5, 層2: 0.5 * 0.1 = 0.05, 層3: 0.05 * 0.01 = 0.0005層が深くなるにつれて、勾配がゼロに近づいているのが分かります。この状態になると学習が完全に止まります。

勾配消失問題への対策

1. ReLU関数の活用

ReLU関数(f(x) = max(0, x))では、入力値が0を超える限り勾配が1で一定です。そのため、勾勾配消失が起きにくい特性があります。

2. バッチ正規化

各層の出力を正規化することで、勾配消失を緩和します。

3. 適切な初期値設定

重みの初期値を工夫することで、勾配が急激に小さくなることを防ぎます。

ReLU関数(Rectified Linear Unit)

ReLU関数は、ニューラルネットワークで広く使われる活性化関数の1つです。この関数は入力値が0以下の場合に「0」を返し、それ以外の値はそのまま出力します。このシンプルな性質により、計算が効率的であり、さらに勾配消失問題が起きにくいという特徴があります。

:
f(x) = max(0, x)


ReLU関数の計算過程(具体例)

1. 入力が正の値の場合

例えば、入力が3の場合を考えます。

  • 計算過程
    f(3) = max(0, 3)
    → 0と3を比較して、大きい値である「3」を出力します。
  • 結果
    f(3) = 3

2. 入力が0の場合

次に、入力が0の場合を考えます。

  • 計算過程
    f(0) = max(0, 0)
    → 0と0を比較して、同じ値である「0」を出力します。
  • 結果
    f(0) = 0

3. 入力が負の値の場合

最後に、入力が-2の場合を考えます。

  • 計算過程
    f(-2) = max(0, -2)
    → 0と-2を比較して、大きい値である「0」を出力します。
  • 結果
    f(-2) = 0

ReLU関数の勾配(微分値)について

ReLU関数の微分値は、入力値が0以下の場合に「0」、入力値が0を超える場合に「1」となります。この性質が、勾配消失問題を防ぐ理由の1つです。

ReLU関数の微分値の式:
f'(x) = 1 (x > 0 の場合)
f'(x) = 0 (x <= 0 の場合)

勾配の具体例
  • 入力が3の場合
    f'(3) = 1
    → 勾配は1であり、十分大きい値が得られるため、学習がスムーズに進みます。
  • 入力が0の場合
    f'(0) = 0
    → 勾配が0になるため、学習は停止しますが、これはニューロンが「発火しない」状態を意味します。
  • 入力が-2の場合
    f'(-2) = 0
    → 勾配が0になるため、このニューロンは学習に寄与しません。

ReLU関数が勾配消失問題を防ぐ理由

シグモイド関数のような活性化関数では、入力が大きすぎる場合や小さすぎる場合に勾配が非常に小さくなります。しかし、ReLU関数では入力が0を超える限り、勾配が常に「1」で一定です。そのため、層が深くなっても勾配がゼロになりにくく、学習が進みます。

例: 深い層での勾配の伝播
  • 各層でReLU関数を使い、勾配が1のまま伝わる場合を考えます。
    層1: 1、層2: 1、層3: 1 …
    勾配が減衰することなく伝播します。
  • 一方、シグモイド関数を使う場合、勾配が0.1のような小さい値になると、
    層1: 0.1、層2: 0.01、層3: 0.001 …
    というように減衰し、学習が停止してしまいます。

ReLU関数の注意点

ReLU関数勾配消失問題を防ぎますが、以下のような点に注意が必要です。

死んだReLU関数(Dead ReLU)問題についての詳細は下記の記事をご参照ください。


ReLU関数は、シンプルな性質で計算が効率的であり、勾配が一定のため、勾配消失問題を防ぎます。具体例を通じて見た通り、ReLU関数は入力値が0を超えれば勾配が「1」を保ち、学習が進む仕組みを持っています。システムトレード機械学習モデルで広く使われている理由がここにあります。

タンジェント双曲線関数

Tanh関数シグモイド関数に似た活性化関数ですが、出力範囲が-1から1となる点が異なります。この特性により、出力値が正と負の両方を持つことができ、データの中心を0にする必要がある場合に特に有用です。

:
f(x) = (exp(x) – exp(-x)) / (exp(x) + exp(-x))

ここで、exp(x)指数関数で、自然対数eを基数として計算します。


Tanh関数の計算過程(具体例)

1. 入力が0の場合

  • 計算過程
    f(0) = (exp(0) – exp(-0)) / (exp(0) + exp(-0))
    = (1 – 1) / (1 + 1)
    = 0 / 2
    = 0
  • 結果
    入力が0のとき、出力は0です。

2. 入力が正の値(例: 2)の場合

  • 計算過程
    f(2) = (exp(2) – exp(-2)) / (exp(2) + exp(-2))
    exp(2) ≈ 7.389, exp(-2) ≈ 0.135
    f(2) = (7.389 – 0.135) / (7.389 + 0.135)
    = 7.254 / 7.524
    ≈ 0.964
  • 結果
    入力が2のとき、出力は約0.964です。Tanh関数の出力が1に近づくのが分かります。

3. 入力が負の値(例: -2)の場合

  • 計算過程
    f(-2) = (exp(-2) – exp(2)) / (exp(-2) + exp(2))
    exp(2) ≈ 7.389, exp(-2) ≈ 0.135
    f(-2) = (0.135 – 7.389) / (0.135 + 7.389)
    = -7.254 / 7.524
    ≈ -0.964
  • 結果
    入力が-2のとき、出力は約-0.964です。Tanh関数の出力が-1に近づくのが分かります。

Tanh関数の微分(勾配)について

Tanh関数の微分値は次の式で計算されます。

f'(x) = 1 – (f(x))^2

1. 入力が0の場合

  • 計算過程
    f(0) = 0(先ほどの計算結果)
    f'(0) = 1 – (0)^2
    = 1 – 0
    = 1
  • 結果
    勾配は1であり、学習が進みやすい状態です。

2. 入力が2の場合

  • 計算過程
    f(2) ≈ 0.964(先ほどの計算結果)
    f'(2) = 1 – (0.964)^2
    = 1 – 0.929
    = 0.071
  • 結果
    勾配は約0.071です。入力が大きいほど勾配が小さくなりますが、勾配が完全にゼロになるわけではありません。

3. 入力が-2の場合

  • 計算過程
    f(-2) ≈ -0.964(先ほどの計算結果)
    f'(-2) = 1 – (-0.964)^2
    = 1 – 0.929
    = 0.071
  • 結果
    勾配は約0.071です。Tanh関数は負の入力に対しても勾配を保ちます。

Tanh関数が勾配消失問題を受けにくい理由

シグモイド関数と比較すると、Tanh関数の出力範囲が-1から1であり、中心が0に近い値を取るため、勾配がシグモイド関数よりも大きく保たれる傾向があります。

  • シグモイド関数の微分値の範囲: 最大値は0.25(中心付近のみ大きい)
  • Tanh関数の微分値の範囲: 最大値は1(広い範囲で勾配が保たれる)

特に、入力値が小さい場合、Tanh関数の勾配は1に近い値を持ち続けるため、シグモイド関数に比べて勾配が消失しにくくなります。


Tanh関数は、出力範囲が-1から1であるため、データの中心を0に整える場合に適しています。また、シグモイド関数に比べて勾配が広い範囲で大きく保たれるため、勾配消失問題の影響を受けにくい特徴があります。具体例を通じて計算したように、Tanh関数は深いネットワークにおける学習をより安定させる役割を果たします。

活性化関数の注意点

活性化関数を選ぶ際には、以下の点を考慮する必要があります。

活性化関数を使ったPythonコード

次に、活性化関数を使った簡単なニューラルネットワークの例を示します。

import numpy as np  # numpyライブラリをインポートします。このライブラリは数値計算を効率的に行うために使用されます。

# ReLU関数(Rectified Linear Unit)の定義
# ReLU関数は、入力が0以下の場合は「0」を返し、入力が0より大きい場合はその値をそのまま返します。
def relu(x):
    # np.maximum関数を使用して、xと0の最大値を計算します。
    # これにより、負の値が「0」に変換され、0より大きい値はそのまま保持されます。
    return np.maximum(0, x)

# シグモイド関数の定義
# シグモイド関数は、入力値を0から1の範囲に変換する活性化関数です。
# この関数は確率のような値を扱う場合や、データを0から1に圧縮する場合に使われます。
def sigmoid(x):
    # 数式は 1 / (1 + np.exp(-x)) です。
    # np.exp(-x) は「自然対数の底eを基数として、-xを指数として計算した値」です。
    # 1 + np.exp(-x) の逆数をとることで、シグモイド関数の出力を計算します。
    return 1 / (1 + np.exp(-x))

# 入力データをnumpy配列として定義します
# numpy配列(array)は、リストのような構造で、数値データを効率的に扱えるようにしたものです。
inputs = np.array([-1, 0, 2, 3])  # 入力データとして4つの数値 [-1, 0, 2, 3] を指定します。

# ReLU関数を入力データに適用します
# relu関数にinputs配列を渡して、各値にReLU関数を適用した結果を取得します。
relu_outputs = relu(inputs)  # 計算結果は配列で返されます。
# ReLU関数の適用結果を画面に出力します
print("ReLUの出力:", relu_outputs)  # ReLUの結果を確認するために出力します。

# シグモイド関数を入力データに適用します
# sigmoid関数にinputs配列を渡して、各値にシグモイド関数を適用した結果を取得します。
sigmoid_outputs = sigmoid(inputs)  # 計算結果は配列で返されます。
# シグモイド関数の適用結果を画面に出力します
print("シグモイドの出力:", sigmoid_outputs)  # シグモイドの結果を確認するために出力します。

サンプルコードの解説

以下に示したサンプルコードについて、各部分を丁寧に解説します。使われている関数メソッド引数の役割、処理の流れについても詳しく説明します。


1. ライブラリのインポート

import numpy as np

このコードは、Pythonの数値計算ライブラリであるnumpyをインポートしています。このライブラリを使うと、大量のデータを効率よく扱えるようになり、特に配列や行列の操作が簡単になります。

  • import numpynumpyライブラリをインポートするための記述です。
  • as npは、numpyという名前を短くしてnpというエイリアス(別名)をつけるための記述です。これにより、以降のコードではnumpyをnpとして簡略化して呼び出せます。

2. ReLU関数の定義

def relu(x):
    return np.maximum(0, x)

この部分では、ReLU関数を自分で定義しています。ReLU関数(Rectified Linear Unit)は、入力値が0以下の場合は「0」を返し、0より大きい場合はその値をそのまま返す活性化関数です。

  • def relu(x):
  • Python関数を定義するための構文です。関数名はreluで、xという引数を取ります。ここでのxは、ReLU関数に入力されるデータを指します。
  • np.maximum(0, x)
  • numpymaximumメソッドを使っています。このメソッドは、指定された2つの値(または配列)を比較し、大きい方を返します。
  • 引数0は比較対象の1つ目で、常に固定値「0」です。
  • 引数xは、ReLU関数に入力される値(または配列)で、これと「0」を比較します。
  • 結果として、入力された値が「0以下」であれば「0」を、0より大きければその値を返します。

3. シグモイド関数の定義

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

この部分では、シグモイド関数を定義しています。シグモイド関数は、入力値を0から1の範囲に変換する活性化関数で、確率のような値を出力するのに適しています。

  • def sigmoid(x):
  • Python関数を定義する構文です。関数名はsigmoidで、引数xを受け取ります。ここでのxはシシグモイド関数に入力される値を指します。
  • np.exp(-x)
  • numpyのexpメソッドを使っています。このメソッドは、自然対数e(おおよそ2.718)を基数とした指数計算を行います。
  • 引数-xは、入力値xにマイナスをかけたものです。指数関数の計算に使用されます。
  • 1 / (1 + np.exp(-x))
  • これはシグモイド関数の公式そのものです。
  • 1 + np.exp(-x)は分母部分で、指数計算の結果を加算しています。
  • 最後に1 /でその結果を逆数にして、0から1の範囲の値を出力します。

4. 入力データの作成

inputs = np.array([-1, 0, 2, 3])

ここでは、入力データを作成しています。このデータは、ReLU関数シグモイド関数に渡される値です。

  • np.array([-1, 0, 2, 3])
  • numpyarrayメソッドを使い、リスト形式のデータを配列に変換しています。
  • 引数[-1, 0, 2, 3]はリスト形式で、ここでは4つの数値を含むデータです。
  • 配列を使用することで、関数を一度に複数の値に適用できます。

5. ReLU関数の適用

relu_outputs = relu(inputs)

この部分では、reLU関数をinputsデータに適用し、その結果をrelu_outputsに格納しています。

  • relu(inputs)
  • 定義したReLU関数を呼び出します。
  • 引数inputsは、ReLU関数に渡される配列データです。このデータに対してReLU関数が適用され、各要素が「0以下なら0、それ以外はその値」を返します。
  • relu_outputs
  • ReLU関数の計算結果を格納する変数です。ここにはinputsの各値にReLU関数を適用した結果が配列として保存されます。

6. ReLU関数の結果を表示

print("ReLUの出力:", relu_outputs)

この部分では、ReLU関数の出力結果を画面に表示しています。


7. シグモイド関数の適用

sigmoid_outputs = sigmoid(inputs)

この部分では、sigmoid関数をinputsデータに適用し、その結果をsigmoid_outputsに格納しています。


8. シグモイド関数の結果を表示

print("シグモイドの出力:", sigmoid_outputs)

この部分では、シグモイド関数の出力結果を画面に表示しています。


全体の流れ

  1. numpyライブラリをインポートして、数値計算の準備をします。
  2. ReLU関数シグモイド関数をそれぞれ定義します。
  3. 配列形式の入力データを準備します。
  4. 入力データに対してReLU関数シグモイド関数を適用し、それぞれの結果を変数に格納します。
  5. 計算結果をprint関数で画面に出力して確認します。

このコードは、ReLU関数シグモイド関数がどのように入力データを変換するかを理解するのに役立ちます。

システムトレードでの活性化関数の応用

システムトレードでは、活性化関数を用いることで次のようなメリットがあります。

  1. 非線形性の追加: 株価や為替レートの複雑な動きをモデル化できます。
  2. 確率出力の活用: シグモイド関数を用いることで、取引の成功確率を計算することが可能です。
  3. 高速な学習: ReLUを使用することで、トレーニング時間を短縮できます。

例えば、ReLUを使ったニューラルネットワークを構築し、過去の取引データを学習させることで、未来の市場動向を予測するモデルを作成できます。


まとめ

活性化関数は、ニューラルネットワークにおいて重要な役割を果たします。システムトレードや機機械学習モデル構築を行う際には、タスクに適した活性化関数数を選ぶことが成功の鍵です。本記事で紹介した基本的な関数や特徴を理解し、実際にコードを書いて試してみることで、より深く理解できるでしょう。

タイトルとURLをコピーしました