views:

140

answers:

2

I was reading on perceptrons and trying to implement one in haskell. The algorithm seems to be working as far as I can test. I'm going to rewrite the code entirely at some point, but before doing so I thought of asking a few questions that have arosen while coding this.

The neuron can be trained when returning the complete neuron. let neuron = train set [1,1] works, but if I change the train function to return an incomplete neuron without the inputs, or try to pattern match and create only an incomplete neuron, the code falls into neverending loop.

tl;dr when returning complete neuron everything works, but when returning curryable neuron, the code falls into a loop.

module Main where
import System.Random
type Inputs = [Float]
type Weights = [Float]
type Threshold = Float
type Output = Float
type Trainingset = [(Inputs, Output)]

data Neuron = Neuron Threshold Weights Inputs deriving Show

output :: Neuron -> Output
output (Neuron threshold weights inputs) = 
          if total >= threshold then 1 else 0
          where total = sum $ zipWith (*) weights inputs

rate :: Float -> Float -> Float
rate t o = 0.1 * (t - o)

newweight :: Float -> Float -> Weights -> Inputs -> Weights
newweight t o weight input = zipWith nw weight input
  where nw w x = w + (rate t o) * x

learn :: Neuron -> Float -> Neuron
learn on@(Neuron tr w i) t = 
  let o = output on
  in Neuron tr (newweight t o w i) i

converged :: (Inputs -> Neuron) -> Trainingset -> Bool
converged n set = not $ any (\(i,o) -> output (n i) /= o) set

train :: Weights -> Trainingset -> Neuron
train w s = train' s (Neuron 1 w)

train' :: Trainingset -> (Inputs -> Neuron) -> Neuron
train' s n | not $ converged n set 
              = let (Neuron t w i) = train'' s n
                in train' s (Neuron t w)
          | otherwise = n $ fst $ head s

train'' :: Trainingset -> (Inputs -> Neuron) -> Neuron
train'' ((a,b):[]) n = learn (n a) b
train'' ((a,b):xs) n = let 
                        (Neuron t w i) = learn (n a) b
                      in
                        train'' xs (Neuron t w)

set :: Trainingset
set = [
        ([1,0], 0),
        ([1,1], 1),
        ([0,1], 0),
        ([0,0], 0)
      ]

randomWeights :: Int -> IO [Float]
randomWeights n = 
  do
    g <- newStdGen
    return $ take n $ randomRs (-1, 1) g

main = do
  w <- randomWeights 2
  let (Neuron t w i) = train w set
  print $ output $ (Neuron t w [1,1])
  return ()

Edit: As per comments, specifying a little more.

Running with the code above, I get: perceptron: <<loop>>

But by editing the main method to:

main = do
  w <- randomWeights 2
  let neuron = train w set
  print $ neuron
  return ()

(Notice the let neuron, and print rows), everything works and the output is:

Neuron 1.0 [0.71345896,0.33792675] [1.0,0.0]

+3  A: 

Perhaps I am missing something, but I boiled your test case down to this program:

module Main where
data Foo a = Foo a

main = do
  x ← getLine
  let (Foo x) = Foo x
  putStrLn x

This further simplifies to:

main = do
  x ← getLine
  let x = x
  putStrLn x

The problem is that binding (Foo x) to something that depends on x is a cyclic dependency. To evaluate x, we need to know the value of x. OK, so we just need to calculate x. To calculate x, we need to know the value of x. That's fine, we'll just calculate x. And so on.

This isn't C, remember: it's binding, not assignment, and the binding is evaluated lazily.

Use better variable names, and it all works:

module Main where
data Foo a = Foo a

main = do
  line ← getLine
  let (Foo x) = Foo line
  putStrLn x

(The variable in question, in your case, is w.)

jrockway
+2  A: 

This is a common mistake in Haskell. You cannot say things like:

let x = 0
let x = x + 1

And have it mean what it would in a language with assignment, or even nonrecursive binding. The first line is irrelevant, it gets shadowed by the second line, which defines x as x+1, that is, it defines recursively x = ((((...)+1)+1)+1)+1, which will loop upon evaluation.

luqui