私はHaskellをプログラミングすることなく長い時間を過ごしました。比較的進んだプロジェクトで取り組むことに決めました。私はthis guideに従うことによってニューラルネットワークをゼロから作り直そうとしています。私は、重みとバイアスのネットワークを構築するような単純な問題への彼の最も難解なアプローチのいくつかのまわりで私の頭に傷しましたが、それはこれに来るとき:なぜ空の関数合成がHaskellで動作するのですか?
feed :: [Float] -> [([Float], [[Float]])] -> [Float]
feed input brain = foldl' (((relu <$>) .) . zLayer) input brain
私は彼が何をするか理解していません。より具体的には、ここで関数の構成に2つの.
を使用する理由がわかりません。彼は(relu <$>) .)
を使用しています。この.
に続けて括弧を付けても意味がありません。私はそれが関数の構成を表していることを理解しています。この場合、関数zLayerは、タイプ([Float], [[Float]])
のニューロンのレイヤーとタイプ[Float]
の前のレイヤーの出力を取り込み、タイプの新しい出力を生成します[Float]
。私は彼がの結果にrelu <$>
関数を適用していることを理解しています。これは意味があります。つまり、脳のレイヤーにzLayer
を適用してrelu <$>
を適用し、最後にそれを次のレイヤーにinput
として渡して、脳(何もレイヤーのリストではない)を折りたたみたいとします。
一見空の構図が私を悩ますものです。
feed :: [Float] -> [([Float], [[Float]])] -> [Float]
feed inp brain = foldl' (((sigmoid <$>) . computeLayer) inp brain
(。私が代わりに整流器(ReLU)のシグモイド関数を使用しています、とcomputeLayerはzLayerのちょうど私の実装である)右:私は上記の何が、私には、このように実装すべきですか?私は(たぶん)、foldl'
の関数として、この提供がありやっている:私は(もちろんの前に、オープン括弧)だけ.)
私.
とcomputeLayer
間を追加すると
(sigmoid <$> (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
| ^^^^^^^^^^^^
なぜ機能のこの一見空の組成物には動作しますか?それは場合に役立ちます
これは、これまでに全体のコードです:
@Bergiノートとして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 'は素敵なトリックですが、それはあまり意味がありません。\ x y - > f(g x y) 'です。後者ははるかに理解できる。そう言われて、私は '(f。)を見始めました。 g 'はそれほど頻繁に使用され、いつかは慣用的になるかもしれない。私はまだ、それを避けるだろう。 Pointfreeコードは、結果がエレガントでクリアな場合にのみ使用する必要があります。 – chi