多層パーセプトロン、活性化関数、モデル選択、過学習、学習不足、重み減衰、ドロップアウト、数値安定性、モデル初期化

パーセプトロンから多層パーセプトロンへ:

単純なパーセプトロンは線形分離面しか生成できないため、XORのような非線形問題を解決できません。

この制限を克服するには、ネットワークに1つまたは複数の隠れ層を追加して多層パーセプトロン(MLP)を作成します。しかし、単に隠れ層を追加しただけでは、まだ線形モデルに等しいため、非線形の活性化関数を導入することで、多層アーキテクチャの潜在能力を引き出す必要があります。

理論的には、1つの隠れ層を持つネットワークでも任意の関数を学習できますが、実際には浅い幅広いネットワークは訓練が難しく、過学習しやすいです。一方、深く狭いネットワークは訓練が容易で、各層が少しずつ学習します。

活性化関数:

活性化関数の選択はそれほど重要ではなく、特に考えがない場合はReLUを使用するのが一般的です。他のハイパーパラメータ(例えば、隠れ層の数や各層のサイズ)の方がより重要です。

  • ReLU:
    • \(ReLU(x) = \max(x, 0)\)
    • 非常に単純な非線形変換(指数計算なし、高速)
    • 微分が簡単で、最適化に有利(\(x < 0\) では0、\(x > 0\) では1)
    • Sigmoid関数の勾配消失問題を解決
  • Sigmoid関数:
    • \(sigmoid(x) = \frac{1}{1 + \exp(-x)}\)
    • 形状がソフトバージョンのステップ関数に似ているが、両端での勾配消失により、活性化関数として使用される頻度が減少している(RNNでは時系列情報フローの制御に使用される)
  • tanh関数:
    • \(tanh(x) = \frac{\exp(2x) - 1}{\exp(2x) + 1}\)
    • Sigmoid関数の範囲を(-1, 1)に拡張し、原点を中心に対称

コード実装:


  import torch
  from torch import nn

  net = nn.Sequential(
      nn.Flatten(),
      nn.Linear(784, 256),
      nn.ReLU(),
      nn.Linear(256, 10)
  )

  def init_weights(m):
      if type(m) == nn.Linear:
          nn.init.normal_(m.weight, std=0.01)

  net.apply(init_weights)
  

学習誤差と汎化誤差:

学習誤差は訓練データセット上で計算された誤差であり、汎化誤差はモデルが未知のデータに対してどの程度の性能を発揮するかの期待値です。汎化誤差は正確に計算することは難しいので、独立したテストセットまたは検証セットでの誤差で推定します。

学習不足と過学習:

学習不足はモデルが訓練データのパターンを十分に捉えられない状態で、訓練誤差と検証誤差が共に大きい場合に発生します。一方、過学習はモデルが訓練データの詳細に過剰に適合し、検証データでの性能が悪くなる状態です。

モデル選択:

訓練セットはモデルのパラメータを訓練し、検証セットはハイパーパラメータを選択するために使用されます。データセットが小さい場合は、k-分割交差検証を使用すると良いです。

正則化方法:重み減衰、ドロップアウト

過学習を防ぐために、モデルの容量を制限する必要があります。これには、パラメータの数を減らすか、パラメータの値の範囲を縮小する方法があります。

重み減衰:

重み減衰は、L2正則化項を損失関数に追加することで実現します。


  def train_concise(wd):
      net = nn.Sequential(nn.Linear(num_inputs, 1))
      for param in net.parameters():
          param.data.normal_()
      loss = nn.MSELoss(reduction='none')
      num_epochs, lr = 100, 0.003
      trainer = torch.optim.SGD([
          {"params": net[0].weight, 'weight_decay': wd},
          {"params": net[0].bias}], lr=lr)
      animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',
                              xlim=[5, num_epochs], legend=['train', 'test'])
      for epoch in range(num_epochs):
          for X, y in train_iter:
              trainer.zero_grad()
              l = loss(net(X), y)
              l.mean().backward()
              trainer.step()
          if (epoch + 1) % 5 == 0:
              animator.add(epoch + 1,
                           (d2l.evaluate_loss(net, train_iter, loss),
                            d2l.evaluate_loss(net, test_iter, loss)))
      print('wのL2ノルム:', net[0].weight.norm().item())
  

ドロップアウト:

ドロップアウトは、訓練中に一部のニューロンの出力をランダムに0に設定することで、モデルの複雑さを制限します。


  net = nn.Sequential(
      nn.Flatten(),
      nn.Linear(784, 256),
      nn.ReLU(),
      nn.Dropout(dropout1),
      nn.Linear(256, 256),
      nn.ReLU(),
      nn.Dropout(dropout2),
      nn.Linear(256, 10)
  )

  def init_weights(m):
      if type(m) == nn.Linear:
          nn.init.normal_(m.weight, std=0.01)

  net.apply(init_weights)
  

数値安定性:

深層ネットワークでは、勾配消失や勾配爆発といった数値不安定性の問題が発生します。これらの問題を解決するには、適切な重み初期化、ReLUなどの安定性の高い活性化関数の使用、勾配クリッピングなどが有効です。

タグ: 多層パーセプトロン 活性化関数 モデル選択 過学習 学習不足

6月22日 18:22 投稿