diff --git a/README.md b/README.md index b1ce43b..b0ba123 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,69 @@ The tensorflow-haskell package provides Haskell bindings to This is not an official Google product. -# Instructions +# Documentation + +https://tensorflow.github.io/haskell/haddock/ + +[TensorFlow.Core](https://tensorflow.github.io/haskell/haddock/tensorflow-0.1.0.0/TensorFlow-Core.html) +is a good place to start. + +# Examples + +Neural network model for the MNIST dataset: [code](tensorflow-mnist/app/Main.hs) + +Toy example of a linear regression model +([full code](tensorflow-ops/tests/RegressionTest.hs)): + +```haskell +import Control.Monad (replicateM, replicateM_, zipWithM) +import System.Random (randomIO) +import Test.HUnit (assertBool) + +import qualified TensorFlow.Core as TF +import qualified TensorFlow.GenOps.Core as TF +import qualified TensorFlow.Gradient as TF +import qualified TensorFlow.Ops as TF + +main :: IO () +main = do + -- Generate data where `y = x*3 + 8`. + xData <- replicateM 100 randomIO + let yData = [x*3 + 8 | x <- xData] + -- Fit linear regression model. + (w, b) <- fit xData yData + assertBool "w == 3" (abs (3 - w) < 0.001) + assertBool "b == 8" (abs (8 - b) < 0.001) + +fit :: [Float] -> [Float] -> IO (Float, Float) +fit xData yData = TF.runSession $ do + -- Create tensorflow constants for x and y. + let x = TF.vector xData + y = TF.vector yData + -- Create scalar variables for slope and intercept. + w <- TF.build (TF.initializedVariable 0) + b <- TF.build (TF.initializedVariable 0) + -- Define the loss function. + let yHat = (x `TF.mul` w) `TF.add` b + loss = TF.square (yHat `TF.sub` y) + -- Optimize with gradient descent. + trainStep <- TF.build (gradientDescent 0.001 loss [w, b]) + replicateM_ 1000 (TF.run trainStep) + -- Return the learned parameters. + (TF.Scalar w', TF.Scalar b') <- TF.run (w, b) + return (w', b') + +gradientDescent :: Float + -> TF.Tensor TF.Value Float + -> [TF.Tensor TF.Ref Float] + -> TF.Build TF.ControlNode +gradientDescent alpha loss params = do + let applyGrad param grad = + TF.assign param (param `TF.sub` (TF.scalar alpha `TF.mul` grad)) + TF.group =<< zipWithM applyGrad params =<< TF.gradients loss params +``` + +# Installation Instructions ## Build with Docker on Linux diff --git a/tensorflow-ops/tensorflow-ops.cabal b/tensorflow-ops/tensorflow-ops.cabal index 94c7478..d5a37bd 100644 --- a/tensorflow-ops/tensorflow-ops.cabal +++ b/tensorflow-ops/tensorflow-ops.cabal @@ -30,6 +30,18 @@ library , text default-language: Haskell2010 +Test-Suite RegressionTest + default-language: Haskell2010 + type: exitcode-stdio-1.0 + main-is: RegressionTest.hs + hs-source-dirs: tests + build-depends: base + , HUnit + , random + , tensorflow + , tensorflow-core-ops + , tensorflow-ops + Test-Suite BuildTest default-language: Haskell2010 type: exitcode-stdio-1.0 diff --git a/tensorflow-ops/tests/RegressionTest.hs b/tensorflow-ops/tests/RegressionTest.hs new file mode 100644 index 0000000..67e087b --- /dev/null +++ b/tensorflow-ops/tests/RegressionTest.hs @@ -0,0 +1,47 @@ +-- | Simple linear regression example for the README. + +import Control.Monad (replicateM, replicateM_, zipWithM) +import System.Random (randomIO) +import Test.HUnit (assertBool) + +import qualified TensorFlow.Core as TF +import qualified TensorFlow.GenOps.Core as TF +import qualified TensorFlow.Gradient as TF +import qualified TensorFlow.Ops as TF + +main :: IO () +main = do + -- Generate data where `y = x*3 + 8`. + xData <- replicateM 100 randomIO + let yData = [x*3 + 8 | x <- xData] + -- Fit linear regression model. + (w, b) <- fit xData yData + assertBool "w == 3" (abs (3 - w) < 0.001) + assertBool "b == 8" (abs (8 - b) < 0.001) + +fit :: [Float] -> [Float] -> IO (Float, Float) +fit xData yData = TF.runSession $ do + -- Create tensorflow constants for x and y. + let x = TF.vector xData + y = TF.vector yData + -- Create scalar variables for slope and intercept. + w <- TF.build (TF.initializedVariable 0) + b <- TF.build (TF.initializedVariable 0) + -- Define the loss function. + let yHat = (x `TF.mul` w) `TF.add` b + loss = TF.square (yHat `TF.sub` y) + -- Optimize with gradient descent. + trainStep <- TF.build (gradientDescent 0.001 loss [w, b]) + replicateM_ 1000 (TF.run trainStep) + -- Return the learned parameters. + (TF.Scalar w', TF.Scalar b') <- TF.run (w, b) + return (w', b') + +gradientDescent :: Float + -> TF.Tensor TF.Value Float + -> [TF.Tensor TF.Ref Float] + -> TF.Build TF.ControlNode +gradientDescent alpha loss params = do + let applyGrad param grad = + TF.assign param (param `TF.sub` (TF.scalar alpha `TF.mul` grad)) + TF.group =<< zipWithM applyGrad params =<< TF.gradients loss params