I am researching with evolutionary neural networks, and am using HNN. My first question is if there exist any evolutionary algorithm frameworks in Haskell already, as I was not able to find any?
I am currently struggling to find a way to mutate the weights of the neural network in a general way. At the moment, I am trying to map a random function (of of the form (RandomGen g) => g -> a -> (b,g)
) over the HMatrix
of weights.
I would like a generalizable way to modify an existing fmap (or fold?) to make use of a random function if it can be done. For example, I might have a function that may or may not add some Gaussian noise to its input, and would like that to apply to the entire network. The problem I'm having is how to work with the random number generators.
For map, I am currently doing the following:
rmap :: (StdGen -> a -> (b,StdGen)) -> StdGen -> [a] -> ([b],StdGen)
rmap _ g [] = ([],g)
rmap f g (x:xs) = let (mapped, g') = rmap f g xs
(rVal, g'') = f g' x
in (rVal:mapped, g'')
This seems like a hack to me, and I was hoping some of the better haskellers might have some advice on how to deal with this randomness more effectively?
I cannot say anything about the neural networks side of this problem (I hope someone posts something about that). From a general Haskeller's perspective, it looks like you should be doing your computation inside some sort of random monad. For example, MonadRandom
.
Then, your rmap
's signature turns into something like:
rmap :: MonadRandom m => (a -> m b) -> [a] -> m [b]
Just looking at that signature, you may realize that rmap
is traverse
in disguise.
The key pattern to recognize here is that things that look like StdGen -> a -> (b,StdGen)
can be transformed into a -> m b
for a random monad (that may even be IO
). Once you see that, you can start using the full power of Haskell's monad utilities.
This is what the Traversable
class is for. mapAccumL
(along with its back-to-front twin mapAccumR
) is a higher-order function which captures a sort of generalised fold-and-map operation, statefully working across a traversable structure and transforming the elements. Your randomised mapping function is an example of this pattern - as you can see, mapAccumL
's type matches the type of your function quite closely:
mapAccumL :: Traversable t => (a -> b -> (a, c)) -> a -> t b -> (a, t c)
rmap :: (StdGen -> a -> (b, StdGen)) -> StdGen -> [a] -> ([b], StdGen)
We can set t ~ []
and a ~ StdGen
, so that rmap
is basically another name for mapAccumL
, after some tuple-flipping.
rmap f z = swap . mapAccumL (\x y -> swap (f x y)) z
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With