傾き(かたむき)とは - 微分係数

傾き(かたむき)について解説します。Perlで学ぶディープラーニング入門では、微分係数と呼ばれる数学の言葉をなるべく使わずに「傾き」という日常の言葉を使って解説していきます。また「勾配(こうばい)」という言葉を使うこともありますが「傾き」と同義の意味として使います。

傾き(かたむき)の定義

「傾き」をソフトウェアエンジニアが理解できる形で、定義します。

ひとつの入力から出力を得る

関数「func」に対して、ひとつの入力「$input」があったとします。そして、その出力は「$output」であったとします。」

funcは「二乗する関数」を例とします。

# 一つの入力に対して出力を得る
my $input = 3;
my $output = func($input);

sub func {
  my ($input) = @_;
  
  my $output = $input ** 2;
  
  return $output;
}

ひとつの入力に微小変化を加算したものから出力を得る

$inputに微小変化「0.00000001」加算したとします。この入力に「$input_plus_delta」という名前をつけます。その時の出力を「$output_plus_delta」と名付けます。

# 一つの入力に微小変化を足したものを作って出力を得る
my $input = 3;
my $delta = 0.00000001;
my $input_plus_delta = $input + $delta;
my $output_plus_delta = func($input_plus_delta);

sub func {
  my ($input) = @_;
  
  my $output = $input ** 2;
  
  return $output;
}

入力がわずかに変化しているため、それに応じて出力もわずかに変化します。

傾きの定義

傾きとは、ある入力の微小な変化に対して、微小な変化を極限まで0に近づけた場合に、出力がどれくらい変化したかの割合を表す値です。分母は、入力の微小な変化です。分子が、それに対応する出力の微小な変化です。

# 傾きの定義 ($input_plus_delta - $inputの値を極限まで0に近づける)
my $grad = ($output_plus_delta - $output) / ($input_plus_delta - $input);

いくつかの入力に対して、傾きを計算する

実際に、いくつかの入力に対して、傾きを計算してみましょう。

use strict;
use warnings;

sub func {
  my ($input) = @_;
  
  my $output = $input ** 2;
  
  return $output;
}

{
  # 一つの入力「1」に対して出力を得る
  my $input = 1;
  my $output = func($input);

  # 一つの入力「1」に微小変化を足したものを作って出力を得る
  my $delta = 0.00000001;
  my $input_plus_delta = $input + $delta;
  my $output_plus_delta = func($input_plus_delta);
  my $grad = ($output_plus_delta - $output) / ($input_plus_delta - $input);
  
  # Grad: 2. Input is 1
  print "Grad: $grad. Input is 1\n";
}
{
  # 一つの入力「2」に対して出力を得る
  my $input = 2;
  my $output = func($input);

  # 一つの入力「2」に微小変化を足したものを作って出力を得る
  my $delta = 0.00000001;
  my $input_plus_delta = $input + $delta;
  my $output_plus_delta = func($input_plus_delta);
  my $grad = ($output_plus_delta - $output) / ($input_plus_delta - $input);

  # Grad: 4. Input is 1
  print "Grad: $grad. Input is 2\n";
}
{
  # 一つの入力「3」に対して出力を得る
  my $input = 3;
  my $output = func($input);

  # 一つの入力「3」に微小変化を足したものを作って出力を得る
  my $delta = 0.00000001;
  my $input_plus_delta = $input + $delta;
  my $output_plus_delta = func($input_plus_delta);
  my $grad = ($output_plus_delta - $output) / ($input_plus_delta - $input);
  
  # Grad: 6. Input is 3
  print "Grad: $grad. Input is 3\n";
}

傾きが計算できました。入力が2の場合は、傾きは2、入力が2の場合は、傾きは4、入力が3の場合は、傾きは6となりました。

傾きが大きいということは、入力の微小変化に対して出力の微小変化が大きいということを表しています。

ディープラーニングでは、逆誤伝播法というアルゴリズムを使って、すべての重みとバイアスに対する損失関数の傾きを求めます。多段の関数になっていますが、考え方は同じです。

求めたすべての重みとバイアスの傾きを、学習率バッチサイズを考慮して、現在の重みとバイアスから減算して更新します。これを勾配降下法といいます。

導関数は、傾きを求めるための関数

ソフトウェアエンジニアが知る必要のあることは、ある関数が与えられたときに、どのようにすれば、傾きを求めることができるかということです。上で解説したアルゴリズムが、まさに傾きを求めるアルゴリズムですが、ソフトゥエアは有限の世界なので、微小変化を0にすることができませんし、求めるのも少し面倒です。

傾きを求めるためには、導関数と呼ばれる関数を使用します。ある関数が与えられた時の導関数というのは、大学研究機関や民間研究における優れた数学者や物理学者の方が、導出してくれています。ありがとうございます。(ソフトウェア実装における偏見を持っていただけないでいただければ、幸いです。公平でWin-Winな関係性でいませんか。)

たとえば、二乗する関数の導関数は以下です。上記の結果と、誤差が小さい範囲で、結果が合っているかを確認してみましょう。

use strict;
use warnings;

sub func {
  my ($input) = @_;
  
  my $output = $input ** 2;
  
  return $output;
}

sub func_derivative {
  my ($input) = @_;
  
  my $output = 2 * $input;
  
  return $output;
}

{
  my $input = 1;
  my $grad = func_derivative($input);
  # Grad: 2. Input is 1
  print "Grad: $grad. Input is 1\n";
}
{
  my $input = 2;
  my $grad = func_derivative($input);
  # Grad: 4. Input is 2
  print "Grad: $grad. Input is 1\n";
}
{
  my $input = 3;
  my $grad = func_derivative($input);
  # Grad: 6. Input is 3
  print "Grad: $grad. Input is 1\n";
}

今回は、誤差がないようです。入力が2の場合は、傾きは2、入力が2の場合は、傾きは4、入力が3の場合は、傾きは6となりました。

関連情報