我花了很長的時間,無需編程哈斯克爾,並決定採取一個相對先進的項目重新進入它。我試圖通過遵循this guide從頭開始編程一個神經網絡。我劃傷了我的身邊了他的一些最深奧的方法,如創建重量和偏見的網絡的簡單問題的頭,但是當涉及到這一點:爲什麼空函數組合在Haskell中工作?
feed :: [Float] -> [([Float], [[Float]])] -> [Float]
feed input brain = foldl' (((relu <$>) .) . zLayer) input brain
我不明白他做什麼。更具體地說,我不明白爲什麼在這裏使用兩個.
的函數組合。他使用(relu <$>) .)
。這個.
跟着括號對我來說沒有意義。我明白它代表功能組合物,並且在這種情況下,函數zLayer發生在神經元的層,其是([Float], [[Float]])
類型和先前層,這是[Float]
類型的輸出,併產生一個新的輸出,也類型的[Float]
。我明白他的應用relu <$>
功能的zLayer
的結果,這是有道理的。也就是說,要通過大腦的層上應用zLayer
,然後對這個結果將relu <$>
摺疊大腦(這是什麼,但圖層的列表),並最終通過,作爲input
到下一層。
看似空洞的成分是什麼錯誤我。
feed :: [Float] -> [([Float], [[Float]])] -> [Float]
feed inp brain = foldl' (((sigmoid <$>) . computeLayer) inp brain
(我使用的雙曲線函數,而不是整流器(RELU)和computeLayer只是我實現zLayer的)右:我上面描述應該是什麼,對我來說,是這樣實現的?我在做什麼有(據說)提供,作爲功能foldl'
,這樣的:
(sigmoid <$> (computeLayer))
當我(當然之前和開放的括號中)僅增加.)
我.
和computeLayer
之間,它的工作原理。沒有他們,這是錯誤的:
net.hs:42:42: error:
• Couldn't match type ‘[Float]’ with ‘Float’
Expected type: [Float] -> ([Float], [[Float]]) -> Float
Actual type: [Float] -> ([Float], [[Float]]) -> [Float]
• In the second argument of ‘(.)’, namely ‘computeLayer’
In the first argument of ‘foldl'’, namely
‘((sigmoid <$>) . computeLayer)’
In the expression: foldl' ((sigmoid <$>) . computeLayer) inp brain
|
42 | feed inp brain = foldl' ((sigmoid <$>) . computeLayer) inp brain
| ^^^^^^^^^^^^
爲什麼這個看似空的函數組合工作?
這是整個代碼到目前爲止,如果它可以幫助:
import System.Random
import Control.Monad
import Data.Functor
foldl' f z [] = z
foldl' f z (x:xs) = let z' = z `f` x
in seq z' $ foldl' f z' xs
sigmoid :: Float -> Float
sigmoid x = 1/(1 + (exp 1) ** (-x))
-- Given a list, gives out a list of lists of length *each element of the list*
makeBiases :: [Int] -> Float -> [[Float]]
makeBiases x b = flip replicate b <$> x
-- Given a list, gives out, for each element X in the list, a list of length x + 1, of
-- x elements in any normal distribution
makeWeights :: [Int] -> Float -> [[[Float]]]
makeWeights [email protected](_:xs) el = zipWith (\m n -> replicate n (replicate m el)) xl xs
-- Make initial biases and weights to give a list of tuples that corresponds to biases
-- and weights associated with each node in each layer
makeBrain :: [Int] -> Float -> Float -> [([Float], [[Float]])]
makeBrain (x:xs) b el = zip (makeBiases xs b) (makeWeights (x:xs) el)
-- Given output of a layer, apply weights and sum for all nodes in a layer. For each list
-- of weights (each node has multiple inputs), there will be one output
sumWeightsL l wvs = sum . zipWith (*) l <$> wvs
-- Given output of a layer, apply weights to get tentative output of each node. Then
-- sum biases of each node to its output
computeLayer :: [Float] -> ([Float], [[Float]]) -> [Float]
computeLayer l (bs, wvs) = zipWith (+) bs (sumWeightsL l wvs)
feed :: [Float] -> [([Float], [[Float]])] -> [Float]
feed inp brain = foldl' ((sigmoid <$>) . computeLayer) inp brain
main = do
putStrLn "3 inputs, a hidden layer of 4 neurons, and 2 output neurons:"
print $ feed [0.1, 0.2, 0.3] (makeBrain [3,4,2] 0 0.22)
這是一個[段](https://wiki.haskell.org/Section_of_an_infix_operator) – Bergi
「他用'(RELU <$>))'。這個「。」後面跟着一個括號對我來說沒有意義。「如果你問這個表達式的類型(回想起'relu'將被解釋爲一個類型化的洞,如果它沒有綁定)那麼你會得到一個解析錯誤。這應該是一個暗示,您沒有正確地將該程序的子字符串標識爲子表達式。如果這樣做沒有意義,可以嘗試用前綴函數替換所有中綴運算符(一旦你完成了這個操作,同時保留了語義,你可能已經理解了中綴運算符的實際意思)。 – user2407038
在我看來,這是一個毫無意義風格的典範。寫作(f。)。 g'是一個很好的技巧,但它並不比那個有意義的'\ xy - > f(g x y)'短得多。後者更容易理解。這就是說,我已經開始看到'(f。)。 g'用得這麼頻繁,甚至有一天甚至會變得習慣用語。不過,我仍然會避免它。只有當結果是優雅和清晰時,IMO才能使用Pointfree代碼。 – chi