haskell-ml-0.4.0: example/iris/iris.hs
-- Example use of `Haskell_ML.FCN` to categorize the Iris dataset.
--
-- Original author: David Banas <capn.freako@gmail.com>
-- Original date: January 22, 2018
--
-- Copyright (c) 2018 David Banas; all rights reserved World wide.
{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Main where
import Control.Arrow
import Control.Monad
import Data.List
import System.Random.Shuffle
import Haskell_ML.FCN
import Haskell_ML.Util
dataFileName :: String
dataFileName = "data/iris.csv"
main :: IO ()
main = do
-- Read in the Iris data set. It contains an equal number of samples
-- for all 3 classes of iris.
putStrLn "Reading in data..."
samps <- readIrisData dataFileName
-- Make field values uniform over [0,1].
let samps' = mkSmplsUniform samps
-- Split according to class, so we can keep equal representation
-- throughout.
let samps1 = filter ((== Setosa) . snd) samps'
samps2 = filter ((== Versicolor) . snd) samps'
samps3 = filter ((== Virginica) . snd) samps'
-- Replace attributes record w/ feature vector.
samps1' = map (first attributeToVector) samps1
samps2' = map (first attributeToVector) samps2
samps3' = map (first attributeToVector) samps3
-- Replace iris type w/ one-hot vector.
samps1'' = map (second irisTypeToVector) samps1'
samps2'' = map (second irisTypeToVector) samps2'
samps3'' = map (second irisTypeToVector) samps3'
-- Shuffle samples.
shuffled1 <- shuffleM samps1''
shuffled2 <- shuffleM samps2''
shuffled3 <- shuffleM samps3''
-- Split into training/testing groups.
-- We calculate separate lengths, even though we expect all 3 to be
-- the same, just for safety's sake.
let len1 = length shuffled1
len2 = length shuffled2
len3 = length shuffled3
numTrn1 = len1 * 80 `div` 100
numTrn2 = len2 * 80 `div` 100
numTrn3 = len3 * 80 `div` 100
-- training data
trn1 = take numTrn1 shuffled1
trn2 = take numTrn2 shuffled2
trn3 = take numTrn3 shuffled3
-- test data
tst1 = drop numTrn1 shuffled1
tst2 = drop numTrn2 shuffled2
tst3 = drop numTrn3 shuffled3
-- Reassemble into single training/testing sets.
trn = trn1 ++ trn2 ++ trn3
tst = tst1 ++ tst2 ++ tst3
-- Reshuffle.
trnShuffled <- shuffleM trn
tstShuffled <- shuffleM tst
putStrLn "Done."
-- Ask user for internal network structure.
putStrLn "Please, enter a list of integers specifying the width"
putStrLn "of each hidden layer you want in your network."
putStrLn "For instance, entering '[2, 4]' will give you a network"
putStrLn "with 2 hidden layers:"
putStrLn " - one (closest to the input layer) with 2 output nodes, and"
putStrLn " - one with 4 output nodes."
hs <- readLn
n <- randNet hs
putStrLn "Great! Now, enter your desired learning rate."
putStrLn "(Should be a decimal floating point value in (0,1)."
rate <- readLn
let (n', TrainEvo{..}) = trainNTimes 60 rate n trnShuffled
res = runNet n' $ map fst tstShuffled
ref = map snd tstShuffled
putStrLn $ "Test accuracy: " ++ show (classificationAccuracy res ref)
-- Plot the evolution of the training accuracy.
putStrLn "Training accuracy:"
putStrLn $ asciiPlot accs
-- Plot the evolution of the weights and biases.
let weights = zip [1::Int,2..] $ (transpose . map fst) diffs
biases = zip [1::Int,2..] $ (transpose . map snd) diffs
forM_ weights $ \ (i, ws) -> do
putStrLn $ "Average variance in layer " ++ show i ++ " weights:"
putStrLn $ asciiPlot $ map (calcMeanList . map (\x -> x*x)) ws
forM_ biases $ \ (i, bs) -> do
putStrLn $ "Average variance in layer " ++ show i ++ " biases:"
putStrLn $ asciiPlot $ map (calcMeanList . map (\x -> x*x)) bs