From 58de9b5cc882326fe266d17451ea143896f4eec7 Mon Sep 17 00:00:00 2001 From: Miguel Date: Fri, 22 Mar 2019 02:30:21 +0100 Subject: first simple neuronet, works quite nice for adding nums up to 10 --- mnist/main.hs | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 mnist/main.hs (limited to 'mnist') diff --git a/mnist/main.hs b/mnist/main.hs new file mode 100644 index 0000000..ae39c85 --- /dev/null +++ b/mnist/main.hs @@ -0,0 +1,144 @@ +import Numeric.LinearAlgebra (Matrix,Vector,tr,scale,cmap,(#>),randn,toList,fromList,toLists,fromLists,Container) +import System.Random(randomRIO) +import Data.List + +-- | Our neural network is simply a list of layers each consisting of +-- a weight matrix with input weights and a vector holding the bias at +-- each neuron. +type Layer = (Matrix Double,Vector Double) +type Neuronet = [Layer] + +-- | Initialize a fresh neuronal network given the number of neurons on +-- each layer, as a list. Weights and biases are initialized randomly +-- using gaussian distribution with mean 0 and standard deviation 1. +neuronet :: [Int] -> IO Neuronet +neuronet l = mapM nl $ zip l (tail l) + where nl (i,l) = (,) <$> randn l i <*> (randn 1 l >>= return.fromList.head.toLists) + +-- | Given the input vector calculate the `weighted inputs` and +-- `activations` for all layers of our network. +weightin :: Neuronet -> Vector Double -> [(Vector Double,Vector Double)] +weightin [] _ = [] +weightin ((w,b):lx) x = (z,a):weightin lx a + where z = w #> x + b + a = cmap sigmoid z + +-- | Given the input and outpout vectors calculate the gradient of our +-- cost function, utilizing backpropagation (output list by layer and +-- split in the weight and bias partial derivatives respectively). +-- Keep the required assumptions about the cost function in mind! +backprop :: Neuronet -> Vector Double -> Vector Double -> [(Matrix Double,Vector Double)] +backprop net x y = zipWith (\a e->(wm a e,e)) (x:map snd wa) (go $ zip ws wa) + where ws = (++[fromLists []]) . tail . map fst $ net + wa = weightin net x + wm a e = fromLists $ map (\e->map (*e) (toList a)) (toList e) + go [(w,(z,a))] = [cost_derivative a y * cmap sigmoid' z] + go ((w,(z,a)):lx) =let r@(e:_)=go lx in tr w #> e * cmap sigmoid' z:r + +-- | Sigmoid function +sigmoid :: Double -> Double +sigmoid x = 1/(1+exp(-x)) + +-- | Derivative of sigmoid function +sigmoid' :: Double -> Double +sigmoid' x = sigmoid x * (1-sigmoid x) + +-- | Returs vector of partial derivatives of the cost function +cost_derivative :: Vector Double -> Vector Double -> Vector Double +cost_derivative a y = a-y + +-- | Train on one single sample +train :: Double -> Neuronet -> Vector Double -> Vector Double -> Neuronet +train r net x y = zipWith f net (backprop net x y) + where f :: Layer -> Layer -> Layer + f (a,b) (c,d) = (a-scale r c,b-scale r d) + +-- | Train on a batch of samples +trainBatch :: Double -> Neuronet -> [Vector Double] -> [Vector Double] -> Neuronet +trainBatch = undefined + +main = do + + let numtrain=300000 + + easynet<-neuronet [20,15,10] :: IO Neuronet + + samples<-filter ((<10).uncurry (+)) <$> gensamp numtrain + let testsmp=filter ((<10).uncurry (+))[(a,b)|a<-[0..10],b<-[0..10]] + + let trained=foldl' training easynet samples + let res = map (\(a,b)->test trained (a,b) == a+b) testsmp + let right=length $ filter (id) res + let all = length res + + print $ "trained adding up to 10 with "++ show numtrain ++" random training samples" + print $ "correct answers during testing: " ++ show right ++ "/" ++ show all ++ " (" ++ show (right*100`div`all) ++ "%)" + print "what can I add for you?" + str<-lines<$>getContents + mapM_ (addit trained) str + + where zrs= take 9 $ repeat 0.01 + val x= take x zrs ++ [0.99] ++ drop x zrs + getVal x=snd . last . sort $ zip x [0..9] + + training net (s1,s2) = train 0.25 net (fromList $ val s1++val s2) (fromList $ val $ s1+s2) + test net (s1,s2) = getVal . toList . snd . last $ weightin net (fromList $ val s1++val s2) + + gensamp :: Int -> IO [(Int,Int)] + gensamp 0 = return [] + gensamp n = (\a b r->(a,b):r) <$> randomRIO (0,9) <*> randomRIO (0,9) <*> gensamp (n-1) + + addit n x= do let w=words x + let a=read $ w!!0 :: Int + let b=read $ w!!1 :: Int + putStrLn $ "I try to add " ++ show a ++ " and " ++ show b + putStrLn $ "I think this is = " ++ show (test n (a,b) ) + putStrLn "what two numbers do you want me to add master?" + + +{- + + +train_img="train-images-idx3-ubyte" +train_lbl="train-labels-idx1-ubyte" +sel=3333 + +main1 = do + h <- openFile train_img ReadMode + h2 <- openFile train_lbl ReadMode + BS.hGetContents h>>= showImg . BS.take (28*28) . BS.drop (28*28*sel) . BS.drop 16 + BS.hGetContents h2>>= print. BS.unpack . BS.take 1 . BS.drop (sel) . BS.drop 8 + + + +showImg a | BS.null a = print "--" + | otherwise = do mapM_ putVal $ BS.unpack $ BS.take 28 a + putStr "\n" + showImg $ BS.drop 28 a + +putVal v | v==0 = putStr " " + | v<50 = putStr "." + | v<128 = putStr "o" + | v<160 = putStr "O" + | otherwise = putStr "0" + + +main2 = do + -- 0 1 2 3 4 5 6 7 8 9 + let m=(4><10)[ 0, 100,0, 100,0, 100,0, 100,0, 100 + ,0, 0, 100,100,0, 0, 100,100,0, 0 + ,0, 0, 0, 0, 100,100,100,100,0, 0 + ,0, 0, 0, 0, 0, 0, 0, 0, 100,100] + + let b=vector . take 4 . repeat $ (-50) + + let z= repeat 0.01 + let vv x= vector $ take x z ++ [0.99] ++ take (9-x) z + let v = vv 6 + let z= m #> v + b + let a= cmap sigmoid z + print m + print v + print z + print a +-} -- cgit v1.2.3