我正在阅读感知器并尝试在haskell中实现一个。算法似乎正在我可以测试的工作。我将在某个时候完全重写代码,但在此之前,我想在编写代码时提出一些问题。
当返回完整的神经元时,可以训练神经元。 let neuron = train set [1,1]
有效,但是如果我改变训练函数以返回没有输入的不完整神经元,或者尝试模式匹配并仅创建一个不完整的神经元,则代码会陷入无休止的循环。
tl; dr当返回完整的神经元时,一切正常,但当返回可克服的神经元时,代码会陷入循环。
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 ()
编辑:根据评论,再指定一点。
使用上面的代码运行,我得到:
perceptron: <<loop>>
但是通过编辑主要方法:
main = do
w <- randomWeights 2
let neuron = train w set
print $ neuron
return ()
(注意let neuron
和打印行),一切正常,输出为:
Neuron 1.0 [0.71345896,0.33792675] [1.0,0.0]
答案 0 :(得分:4)
也许我错过了一些东西,但我将你的测试案例归结为这个程序:
module Main where
data Foo a = Foo a
main = do
x ← getLine
let (Foo x) = Foo x
putStrLn x
这进一步简化为:
main = do
x ← getLine
let x = x
putStrLn x
问题在于将(Foo x)
绑定到依赖于x的内容
是循环依赖。要评估x,我们需要知道它的价值
X。好的,所以我们只需要计算x。要计算x,我们需要
知道x的价值。没关系,我们只计算x。等等。
这不是C,记住:它是绑定,而不是赋值和绑定 被懒惰地评估。
使用更好的变量名称,一切正常:
module Main where
data Foo a = Foo a
main = do
line ← getLine
let (Foo x) = Foo line
putStrLn x
(在您的情况下,有问题的变量是w
。)
答案 1 :(得分:3)
这是Haskell中的一个常见错误。你不能说:
let x = 0
let x = x + 1
并且它意味着它在具有赋值的语言中的意义,甚至是非递归绑定。第一行是无关紧要的,它被第二行遮蔽,第二行将x
定义为x+1
,也就是说,它定义递归x = ((((...)+1)+1)+1)+1
,它将在评估时循环。