machine-learning-in-haskell: try to implement network functions

This commit is contained in:
2025-02-05 10:09:23 +01:00
parent fc2ead847a
commit cff9c70187

View File

@@ -17,6 +17,18 @@ randomList n gen = go n gen []
-- definition of a neuron -- definition of a neuron
data NeuralNetwork = NeuralNetwork
{ layers :: [[Neuron]],
finalLayer :: Neuron
}
runNetwork :: NeuralNetwork -> Input -> Output
runNetwork NeuralNetwork {layers, finalLayer} input =
run finalLayer $ runLayers layers input
where
runLayers [] input = input
runLayers (l : ls) input = runLayers ls $ map (`run` input) l
data Neuron = Neuron data Neuron = Neuron
{ bias :: Float, { bias :: Float,
activate :: Float -> Output, activate :: Float -> Output,
@@ -43,12 +55,17 @@ modifyWeights dw neuron = neuron {weights = zipWith (+) dw (weights neuron)}
modifyBias :: Weight -> Neuron -> Neuron modifyBias :: Weight -> Neuron -> Neuron
modifyBias db neuron = neuron {bias = db + bias neuron} modifyBias db neuron = neuron {bias = db + bias neuron}
initializeNeuron :: Int -> Int -> Neuron initializeNeuron :: StdGen -> Int -> (Neuron, StdGen)
initializeNeuron seed nWeights = initializeNeuron gen nWeights =
let gen = mkStdGen seed let (weights, gen') = randomList nWeights gen
(weights, gen') = randomList nWeights gen
(bias, gen'') = random gen' (bias, gen'') = random gen'
in Neuron {bias = bias, activate = id, weights = weights} in ( Neuron
{ bias = bias,
activate = id,
weights = weights
},
gen''
)
-- prerequisites for gradient descent -- prerequisites for gradient descent
@@ -66,63 +83,104 @@ cost neuron trainingData =
expected = map snd trainingData expected = map snd trainingData
in meanSquaredError actual expected in meanSquaredError actual expected
costNetwork :: NeuralNetwork -> TrainingData -> Float
costNetwork network trainingData =
let actual = map (runNetwork network . fst) trainingData
expected = map snd trainingData
in meanSquaredError actual expected
oneHotVectors :: Int -> [[Weight]] oneHotVectors :: Int -> [[Weight]]
oneHotVectors n = [oneHot i | i <- [0 .. n - 1]] oneHotVectors n = [oneHot i | i <- [0 .. n - 1]]
where where
oneHot index = [if j == index then epsilon else 0 | j <- [0 .. n - 1]] oneHot index = [if j == index then epsilon else 0 | j <- [0 .. n - 1]]
differential :: Neuron -> TrainingData -> Neuron differential :: (Neuron -> TrainingData -> Float) -> Neuron -> TrainingData -> Neuron
differential neuron trainingData = differential costFunc neuron trainingData =
let c = cost neuron trainingData let c = costFunc neuron trainingData
weightUpdates = oneHotVectors $ length $ weights neuron weightUpdates = oneHotVectors $ length $ weights neuron
dws = dws =
map map
( \weightUpdate -> ( \weightUpdate ->
(cost (modifyWeights weightUpdate neuron) trainingData - c) / epsilon (costFunc (modifyWeights weightUpdate neuron) trainingData - c) / epsilon
) )
weightUpdates weightUpdates
db = (cost (modifyBias epsilon neuron) trainingData - c) / epsilon db = (costFunc (modifyBias epsilon neuron) trainingData - c) / epsilon
in trace (show neuron ++ ", cost = " ++ show c) $ in neuron {weights = dws, bias = db}
neuron
{ weights = dws, differentialNetwork :: NeuralNetwork -> TrainingData -> NeuralNetwork
bias = db differentialNetwork network trainingData =
} let costFunc = costNetwork
updatedLayers = map (map (\neuron -> differential costFunc neuron trainingData)) (layers network)
updatedFinalLayer = differential costFunc (finalLayer network) trainingData
in NeuralNetwork {layers = updatedLayers, finalLayer = updatedFinalLayer}
learn :: Float -> Neuron -> (Neuron -> Neuron) learn :: Float -> Neuron -> (Neuron -> Neuron)
learn learningRate differential = learn learningRate differential =
modifyBias (-learningRate * bias differential) modifyBias (-learningRate * bias differential)
. modifyWeights (map (\dw -> -learningRate * dw) (weights differential)) . modifyWeights (map (\dw -> -learningRate * dw) (weights differential))
learnNetwork :: Float -> NeuralNetwork -> (NeuralNetwork -> NeuralNetwork)
learnNetwork learningRate differential network =
NeuralNetwork
{ finalLayer = learn learningRate (finalLayer differential) (finalLayer network),
layers = zipWith (zipWith (learn learningRate)) (layers differential) (layers network)
}
epoch :: TrainingData -> Neuron -> Neuron epoch :: TrainingData -> Neuron -> Neuron
epoch trainingData neuron = epoch trainingData neuron =
let neuron' = learn learningRate (differential neuron trainingData) neuron let neuron' = learn learningRate (differential cost neuron trainingData) neuron
in neuron' in neuron'
where where
learningRate = 1e-3 learningRate = 1e-3
-- concrete example -- concrete example
trainingDataAdd, trainingDataDouble, trainingDataOr, trainingDataAnd :: TrainingData trainingDataAdd, trainingDataDouble, trainingDataOr, trainingDataAnd, trainingDataNand, trainingDataNot, trainingDataXor :: TrainingData
trainingDataDouble = [([x], x * 2) | x <- [0 .. 4]] trainingDataDouble = [([x], x * 2) | x <- [0 .. 4]]
trainingDataAdd = [([x, y], x + y) | x <- [1 .. 10], y <- [1 .. 10]] trainingDataAdd = [([x, y], x + y) | x <- [1 .. 10], y <- [1 .. 10]]
trainingDataOr = [([0, 0], 0), ([1, 0], 1), ([0, 1], 1), ([1, 1], 1)] trainingDataOr = [([0, 0], 0), ([1, 0], 1), ([0, 1], 1), ([1, 1], 1)]
trainingDataAnd = [([0, 0], 0), ([1, 0], 0), ([0, 1], 0), ([1, 1], 1)] trainingDataAnd = [([0, 0], 0), ([1, 0], 0), ([0, 1], 0), ([1, 1], 1)]
trainingDataNand = [([0, 0], 1), ([1, 0], 1), ([0, 1], 1), ([1, 1], 0)]
trainingDataXor = [([0, 0], 0), ([1, 0], 1), ([0, 1], 1), ([1, 1], 0)]
trainingDataNot = [([0], 1), ([1], 0)]
gen = mkStdGen 69
trainDouble n = trainDouble n =
let neuron = initializeNeuron 69 1 let (neuron, _) = initializeNeuron gen 1
in iterate (epoch trainingDataDouble) neuron !! n in iterate (epoch trainingDataDouble) neuron !! n
trainAdd n = trainAdd n =
let neuron = initializeNeuron 69 2 let (neuron, _) = initializeNeuron gen 2
in iterate (epoch trainingDataAdd) neuron !! n in iterate (epoch trainingDataAdd) neuron !! n
trainOr n = trainOr n =
let neuron = (initializeNeuron 69 2) {activate = sigmoid} let (neuron, _) = initializeNeuron gen 2
in iterate (epoch trainingDataOr) neuron !! n neuron' = neuron {activate = sigmoid}
in iterate (epoch trainingDataOr) neuron' !! n
trainAnd n = trainAnd n =
let neuron = (initializeNeuron 69 2) {activate = sigmoid} let (neuron, _) = initializeNeuron gen 2
in iterate (epoch trainingDataAnd) neuron !! n neuron' = neuron {activate = sigmoid}
in iterate (epoch trainingDataAnd) neuron' !! n
trainXor n =
let (neuron, _) = initializeNeuron gen 2
neuron' = neuron {activate = sigmoid}
in iterate (epoch trainingDataXor) neuron' !! n
trainNand n =
let (neuron, _) = initializeNeuron gen 2
neuron' = neuron {activate = sigmoid}
in iterate (epoch trainingDataNand) neuron' !! n
trainNot n =
let (neuron, _) = initializeNeuron gen 2
neuron' = neuron {activate = sigmoid}
in iterate (epoch trainingDataNot) neuron' !! n
evaluateNeuron :: Neuron -> TrainingData -> [(Input, Output)] evaluateNeuron :: Neuron -> TrainingData -> [(Input, Output)]
evaluateNeuron neuron = map (\(x, _) -> (x, run neuron x)) evaluateNeuron neuron = map (\(x, _) -> (x, run neuron x))
evaluateNetwork :: NeuralNetwork -> TrainingData -> [(Input, Output)]
evaluateNetwork network = map (\(x, _) -> (x, runNetwork network x))