単純パーセプトロン

Image from Gyazo

単純パーセプトロン

パーセプトロン とは,動物の神経細胞(ニューロン)の発火現象を数理的に表現したモデルのことです. ウォーレン・マカロック氏とウォルター・ピッツ氏が提案した 形式ニューロン を2層のネットワーク状に接続したものは 単純パーセプトロン と呼ばれます. 単純パーセプトロンの1層目のニューロンは,入力データを伝えるだけの役割であるため,実質的には1つの形式ニューロンと考えることができます.

Image from Gyazo

ここでは,2入力,1出力の単純パーセプトロンを考えます. 下記の式に従って,入力された$x_1$と$x_2$から,中間出力$y$を得ます. ここで,$w_1$と$w_2$は入力に対する 重み です. また,$w_0$は バイアス と呼ばれ,出力の閾値として用いられます.

$$ y = w_1 \times x_1 + w_2 \times x_2 + w_0 $$

この中間出力$y$に対して,活性化関数 $f$と呼ばれる特殊な関数を適用して, 得られた値を最終的なニューロンの出力$z$とします.

$$ z = f(y) $$

一般に$n$入力の単純パーセプトロンは,入力${\bf x}$と重み${\bf w}$は下記のようにベクトルで表現されます. ここで,$x_0=1$はバイアス$w_0$を導入するために用いられます. パーセプトロンが多層になると,ベクトル表現が必須となりますので,慣れておきましょう.

$$ {\bf x} = (1, x_1, \cdots, x_n) \\
{\bf w} = (w_0, w_1, \cdots, w_n) \\
y = {\bf x} \cdot {\bf w} \\
z = f(y) $$

活性化関数$f$には下記のような種類があります.

下図はステップ関数のグラフです. ステップ関数は入力$y$が0以上なら1,0未満であれば0を出力する関数です.

Image from Gyazo

下図はシグモイド関数のグラフです. シグモイド関数は,ステップ関数とは異なり滑らかに変化する連続関数であり, 0から1の範囲を出力します.

$$ f(y) = \frac{1}{1 + e^{-y}} $$

Image from Gyazo

下図はReLU関数のグラフです. ReLU関数は入力$y$が0以上なら入力$y$をそのまま出力し, 0未満であれば0を出力する関数です.

Image from Gyazo

単純パーセプトロンの実装

ノートブックを作成し,ノートブックのタイトルをNN-2 に設定します. それでは,単純パーセプトロンを PyTorch を利用して実装してみましょう. まずは,PyTorchとNumpyをインポートします.

import torch
import torch.nn as nn
import numpy as np

単純パーセプトロンのネットワークを生成します. nn.Sequentialは,パーセプトロンの層を追加した順番に並べます. 最初に2入力・1出力の層nn.Linear(2, 1)を作成します. その出力をシグモイド関数nn.Sigmoid()に伝え,最終的な出力を得ています.

# 単純パーセプトロン
network = nn.Sequential(
    nn.Linear(2, 1),
    nn.Sigmoid()
)
print(network)
#出力
Sequential(
  (0): Linear(in_features=2, out_features=1, bias=True)
  (1): Sigmoid()
)

重みやバイアスなどのパラメータ$w$は,ランダムに初期化されています. ここでは,下記のようにパラメータを強制的に変更します.

$$ {\bf w} = (w_0, w_1, w_2) = (-0.5, 0, 1) $$

パーセプトロンの各層には,配列として参照することが可能です. network[0]は先程作成した2入力・1出力の層です. 重みを変更するには,network[0].weight.datafill_メソッドを利用します.

# 重みの変更
network[0].weight.data[0][0].fill_(0)
network[0].weight.data[0][1].fill_(1)
print(network[0].weight)
#出力
Parameter containing:
tensor([[0., 1.]], requires_grad=True)

バイアスを変更するには,network[0].bias.datafill_メソッドを利用します.

# バイアスの変更
network[0].bias.data.fill_(-0.5)
print(network[0].bias)
#出力
Parameter containing:
tensor([-0.5000], requires_grad=True)

単純パーセプトロンの入力${\bf x}=(x_1, x_2)$をテンソルとして作成します(ここではバイアスは除きます).

$$ {\bf x} = (x_1, x_2) = (1, 1) $$

# 入力
x = torch.tensor([1, 1], dtype=torch.float)
print(x)
#出力
tensor([1., 1.])

単純パーセプトロンに${\bf x}$を入力し,出力$z$を得ます.

z = network(x)
print(z)
#出力
tensor([0.6225], grad_fn=<SigmoidBackward>)

出力$z$は 0.6225 になりました. この値が正しいか確認してみましょう. 定義に従って計算すると,PyTorchで作成した単純パーセプトロンと出力が一致していることが分かります.

def sigmoid(x):
    return  1 / (1 + np.exp(-1 * x))
x = np.array([1, 1, 1]) #入力
w = np.array([-0.5, 0, 1]) #重みとバイアス
y = x.dot(w) #中間出力
z = sigmoid(y) #シグモイド関数
print(z)
#出力
0.6224593312018546

参考書籍