PerlとSPVMで学ぶディープラーニング入門 - 国産AI 日本の期待の星プロジェクト

中間層における計算 - m個の入力をn個の出力に変換する

ディープラーニングにおける中間層における計算として、m個の入力をn個の出力に変換する方法を解説します。

ディープラーニングでは、複数の入力と複数の出力を、多段階に連続的につなぎ、最終的な出力を得ることをします。

このとき、m個の入力から、n個の出力を得る必要があります。

たとえば、4つの入力に対して、5つの出力を得る。5つの入力から、3つの出力を得るということを行います。

(x0, x1, x2, x3)
↓
(y0, y1, y2, y3, y4)
↓
(z0, z1, z2)

二つの入力から三つの出力を得る

では、簡単な例として、二つの入力から三つの出力を得る方法を記述してみましょう。まずPerlのコードで書きます。

use strict;
use warnings;

my $x0 = 0.5;
my $x1 = 0.8;

my $w00 = 0.1;
my $w01 = -0.4;
my $w10 = 0.2;
my $w11 = -0.5;
my $w20 = 0.3;
my $w21 = 1.3;

my $b0 = 0.3;
my $b1 = -0.9;
my $b2 = 0.5;

my $y0 = ($w00 * $x0) + ($w01 * $x1) + $b0;
my $y1 = ($w10 * $x0) + ($w11 * $x1) + $b1;
my $y2 = ($w20 * $x0) + ($w21 * $x1) + $b2;

print "($y0, $y1, $y2)\n";

出力結果は以下のようになります。

(0.0299999999999999, -1.2, 1.69)

このような計算を行うことで、($x1, $x2)という二つの入力値から($y0, $y1, $y2)という三つの出力を得ることができました。

活性化関数で出力を変換する

ディープラーニングでは、次の入力に渡す前に、出力を、ある規則によって変換する必要があります。この変換が必要な理由は、この変換を行わないと学習が進まないためです。

このような関数は活性化関数と呼ばれ、活性化関数の一つとして、ReLUと呼ばれる関数があります。

# ReLU関数
sub relu {
  my ($num) = @_;
  
  return $num > 0 : $num : 0;
}

この関数は、入力が0より大きければ、その値を返し、0以下の場合は、0を返します。

先のサンプルに活性化関数を適用してみましょう。関数で書く必要はないので、ソースコードにべた書きします。

use strict;
use warnings;

my $x0 = 0.5;
my $x1 = 0.8;

my $w00 = 0.1;
my $w01 = -0.4;
my $w10 = 0.2;
my $w11 = -0.5;
my $w20 = 0.3;
my $w21 = 1.3;

my $b0 = 0.3;
my $b1 = -0.9;
my $b2 = 0.5;

my $y0 = ($w00 * $x0) + ($w01 * $x1) + $b0;
my $y1 = ($w10 * $x0) + ($w11 * $x1) + $b1;
my $y2 = ($w20 * $x0) + ($w21 * $x1) + $b2;

$y0 = $y0 > 0 ? $y0 : 0;
$y1 = $y1 > 0 ? $y1 : 0;
$y2 = $y2 > 0 ? $y2 : 0;

print "($y0, $y1, $y2)\n";

出力結果は以下のようになります。値がマイナスの場合は、0に変わっています。

(0.0299999999999999, 0, 1.69)

m個の入力をn個の出力に変換する処理の一般化

m個の入力をn個の出力に変換する処理を一般化してみましょう。もう一度、下の処理をじっくりみてください。

use strict;
use warnings;

my $x0 = 0.5;
my $x1 = 0.8;

my $w00 = 0.1;
my $w01 = -0.4;
my $w10 = 0.2;
my $w11 = -0.5;
my $w20 = 0.3;
my $w21 = 1.3;

my $b0 = 0.3;
my $b1 = -0.9;
my $b2 = 0.5;

my $y0 = ($w00 * $x0) + ($w01 * $x1) + $b0;
my $y1 = ($w10 * $x0) + ($w11 * $x1) + $b1;
my $y2 = ($w20 * $x0) + ($w21 * $x1) + $b2;

$y0 = $y0 > 0 ? $y0 : 0;
$y1 = $y1 > 0 ? $y1 : 0;
$y2 = $y2 > 0 ? $y2 : 0;

print "($y0, $y1, $y2)\n";

必要な情報を、漏れなく書き出してみましょう。

入力の個数は、2個。

出力の個数は、3個。

必要な$wパラメーターの個数はいくつでしょうか。これは「入力個数「2」 * 出力個数「3」 = 6」です。

必要な$bパラメーターの個数はいくつでしょうか? 出力個数「3」と同じです。

入力は

[$x0, $x1]

パラメーター$wは、

[
  $w00, $w01,
  $w10, $w11,
  $w20, $w21,
]

パラメーター$bは、

[
  $b0,
  $b1,
  $b2,
]

受け取る出力は

[$y0, $y1, $y2]

です。

配列化

では、コーディングしてみます。最初は、配列化です。最初の例と、と同じ出力を得ることができれば、正解ですね。

目を凝らして、法則性を眺めてください。

use strict;
use warnings;

my $x = [0.5, 0.8];
my $y = [0, 0, 0];
my $x_len = @$x;
my $y_len = @$y;

my $w = [
  0.1, -0.4,
  0.2, -0.5,
  0.3, 1.3,
];

my $b = [
  0.3,
  -0.9,
  0.5,
];

$y->[0] = ($w->[$x_len * 0 + 0] * $x->[0]) + ($w->[$x_len * 0 + 1] * $x->[1]) + $b->[0];
$y->[1] = ($w->[$x_len * 1 + 0] * $x->[0]) + ($w->[$x_len * 1 + 1] * $x->[1]) + $b->[1];
$y->[2] = ($w->[$x_len * 2 + 0] * $x->[0]) + ($w->[$x_len * 2 + 1] * $x->[1]) + $b->[2];

$y->[0] = $y->[0] > 0 ? $y->[0] : 0;
$y->[1] = $y->[1] > 0 ? $y->[1] : 0;
$y->[2] = $y->[2] > 0 ? $y->[2] : 0;

print "($y->[0], $y->[1], $y->[2])\n";

出力結果は以下で、同じになりました。

(0.661503159202952, 0.802183888558582, 0.755838899094797)

ループ化

配列化が終わったら、次はループ化です。配列というデータ構造にすることで、ループのアルゴリズムを適用できます。二重ループで処理を書き直してみます。最初の例と、と同じ出力を得ることができれば、正解ですね。

use strict;
use warnings;

my $x = [0.5, 0.8];
my $y = [0, 0, 0];
my $x_len = @$x;
my $y_len = @$y;

my $w = [
  0.1, -0.4,
  0.2, -0.5,
  0.3, 1.3,
];

my $b = [
  0.3,
  -0.9,
  0.5,
];

for (my $y_index = 0; $y_index < $y_len; $y_index++) {
  my $total = 0;
  for (my $x_index = 0; $x_index < $x_len; $x_index++) {
    $total += ($w->[$x_len * $y_index + $x_index] * $x->[$x_index]);
  }
  $total +=  $b->[$y_index];
  $y->[$y_index] = $total > 0 ? $total : 0;
}

print "($y->[0], $y->[1], $y->[2])\n";

出力結果は以下で、同じになりました。

(0.0299999999999999, 0, 1.69)

これで、mが2以外の数になったり、nが3以外の数になっても、対応できるようになりました。

ディープラーニングに必要な技術の中の、m個の入力をn個の出力に変換する方法、活性化関数の適用の学習を終えました。

Side Bar