単純パーセプトロンを実装してみた
年が明けていますが、PureScript Advent Calendar 2017 - Qiitaの20日目の記事にしてしまいます。
Python機械学習プログラミングを2章まで読んだ。
読んだ内容を体にいれるために、適当にPureScriptで単純パーセプトロンを実装してみる。
module Main where import Prelude import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) import Control.Monad.State (StateT, execStateT, get, modify, put) import Control.Safely (replicateM_) import Data.Array (snoc, zipWith) import Data.Foldable (sum) import Data.Traversable (for) import Data.Tuple (Tuple(..)) -- 学習によって更新されていく値 type State = { weights :: Array Number , bias :: Number , errors :: Array Number } main :: forall e. Eff (console :: CONSOLE | e) Unit main = do -- 学習率は0.01とし、エポック数は10としてみる state <- flip execStateT initialState $ fit 0.01 10 log $ "expected: 1.0, gotten: " <> (show $ predict $ netInput [1.0, 1.0] state) log $ "expected: -1.0, gotten: " <> (show $ predict $ netInput [0.0, 1.0] state) log $ "expected: -1.0, gotten: " <> (show $ predict $ netInput [1.0, 0.0] state) log $ "expected: -1.0, gotten: " <> (show $ predict $ netInput [0.0, 0.0] state) log $ "weights: " <> show state.weights log $ "bias: " <> show state.bias log $ "errors: " <> show state.errors -- 学習 fit :: forall e. Number -> Int -> StateT State (Eff e) Unit fit eta iter = replicateM_ iter do errors <- for samples \(Tuple xs y) -> do st <- get let d = eta * (y - predict (netInput xs st)) put $ st { weights = zipWith (+) st.weights $ map ((*) d) xs , bias = st.bias + d } pure d -- イテレーションごとに雑に誤差をいれておく modify \s -> s { errors = snoc s.errors $ sum errors } -- ステップ関数 predict :: Number -> Number predict z = if z >= 0.0 then 1.0 else -1.0 -- 総入力 netInput :: Array Number -> State -> Number netInput xs { weights, bias } = bias + (sum $ zipWith (*) xs weights) -- 初期状態 initialState :: State initialState = { weights: [ 0.0, 0.0 ] , bias: 0.0 , errors: [] } -- サンプル -- 特徴量と期待する出力 samples :: Array (Tuple (Array Number) Number) samples = [ Tuple [ 0.0, 0.0 ] (-1.0) , Tuple [ 0.0, 1.0 ] (-1.0) , Tuple [ 1.0, 0.0 ] (-1.0) , Tuple [ 1.0, 1.0 ] 1.0 ]
今回はAND演算を学習させてみた。
実行してみる。
expected: 1.0, gotten: 1.0 expected: -1.0, gotten: -1.0 expected: -1.0, gotten: -1.0 expected: -1.0, gotten: -1.0 weights: [0.04,0.02] bias: -0.06 errors: [0.0,-0.02,-0.02,0.0,-0.02,0.0,0.0,0.0,0.0,0.0]
6回目から誤差が出なくなっているっぽい。
ADALINEも実装しようと思ったが、飽きたので一旦終了。