Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Machine Learning Fundamentals in Clojure

Paul English
September 07, 2013

Machine Learning Fundamentals in Clojure

A basic tour of fundamental algorithms and libraries for working on machine learning problems in Clojure.

Paul English

September 07, 2013
Tweet

Other Decks in Programming

Transcript

  1. • Small Team: A few computer scientists; a couple of

    bald mathematicians; and an analytics guy • Clojure (Ruby, Python, Node.js, R, C#, whatever works best or is needed) • Predictive Analytics (Automated Decisions, Machine Learning, Visualizations & Intelligence) • Math Mornings (Stochastic Calculus For Finance; Friends of Red Brain Labs)
  2. • Why Lisp? Why Clojure? • Managing Data & IO

    • Analysis • Features & Data Manipulation • Machine Learning, Training, & Evaluation • Distributed Computing Overview
  3. Why Lisp? • 1958: List Processing • Second Oldest High

    Order Language, Second to Fortran • Practical Mathematical Notation (Lambda Calculus) • A favorite for early AI research
  4. Why Clojure? • A modern Lisp • Laziness, concurrency, sequence

    manipulation, and more. • Everything great about lisp abstractions • Portability and integration of the JVM • Still a favorite for AI applications
  5. Managing Data & IO • Loading Data (CSV, Database) •

    Working Lazily • Development & Production Differences
  6. Loading Data • clojure.data.csv • clojure.java.io • SQL Korma (DSL

    for SQL using JDBC) • Anything Java can do we can do better and with less lines of code.
  7. Laziness (use  'clojure.data.csv) (use  'clojure.java.io)   (defn  lazy-­‐read-­‐csv  [csv-­‐file]  

     (let  [in-­‐file  (reader  csv-­‐file)                csv-­‐seq  (read-­‐csv  in-­‐file)                lazy  (fn  lazy  [wrapped]                              (lazy-­‐seq  (if-­‐let  [s  (seq  wrapped)]                                                      (cons  (first  s)                                                                  (lazy  (rest  s)))                                                      (.close  in-­‐file))))]        (lazy  csv-­‐seq)))
  8. Data In Development (def  big-­‐list-­‐of-­‐superheros          

     (read-­‐csv  "marvel-­‐superheros.csv"))   (def  development-­‐sample            (take  100  big-­‐list-­‐of-­‐superheros))
  9. Map/Reduce • Who needs Hadoop? • JK, distributed processing is

    cool. • We’re already thinking in functionally!
  10. Functional Data Manipulation (defn  column    "Returns  the  ith  column

     from  a  matrix  as   a  vector"    [M  i]    (map  (fn  [m]  (nth  m  i))  M))
  11. Functional Data Manipulation (defn  argmax    "Returns  the  argument  in

     collection   that  produces  the maximum  result  of  a  function  f"    [f  coll]    (reduce  (fn  [a  b]  (if  (>  (f  a)  (f  b))                                                  a  b))                    coll))
  12. Elementary Row Operations (defn  interchange-­‐rows    "Produce  a  new  matrix

     `M`  with  rows  `a`  &   `b`  swapped"    [M  a  b]    (assoc  M  b  (M  a)  a  (M  b)))
  13. Elementary Row Operations (defn  scaler-­‐multiply    "Return  the  scalar  multiplication

     `s`   of  a  row  `a`  from  the  matrix  `M1"    [M  s  a]    (map  #(*  %  s)  (M  a)))
  14. Elementary Row Operations (defn  add-­‐row-­‐to-­‐row    "Adds  the  values  of

     a  row  `a`  from  the  matrix  `M`   to  the  row  `b`  returning  the  updated  matrix.  Assumes   that  `a`  is  the  values  of  the  row,  and  `b`  is  the   index  of  the  row  to  be  operated  on."    [M  a  b]    (assoc  M  b  (vec  (map  (fn  [a_i  b_i]  (+  a_i  b_i))                                              a  (M  b)))))
  15. The Start Of Our Own Matrix Math Library (defn  eliminate

     [M  pivot]    (let  [m  (count  M)]        (reduce  (fn  [M  i]                            (let  [scaler-­‐factor  (scale-­‐factor  M  i  pivot)                                        scaled-­‐row  (scaler-­‐multiply  M  scaler-­‐factor  pivot)]                                (add-­‐row-­‐to-­‐row  M  scaled-­‐row  i)))                        M  (range  (inc  pivot)  m)))) (defn  row-­‐echelon-­‐form    "Computes  the  row-­‐echelon  form  of  a  matrix,  `A`, using  gaussian  elimination."    [A]    (let  [m  (count  A)]        (reduce  (fn  [A  pivot]                            (let  [B  (partial-­‐pivot  A  pivot)]                                (eliminate  B  pivot)))                        A  (range  0  m))))
  16. Transforming Data: One Hot Encoding (defn  one-­‐hot-­‐encode    "Will  fit

     and  encode  an  array  of  categorical  values  using  one-­‐hot  encoding.   Assumes  that  empty  lists,  and  nil  values  aren't  supposed  to  be  encoded."    [&  values]    (let  [categories  (remove  vacant?  (distinct  (apply  concat  values)))                empty  (vec  (zeros  (count  categories)))                encoded  (map  (fn  [row]                                              (if  (>  (count  row)  1)                                                  (map  (fn  [category]                                                                (let  [index  (first  (indexes-­‐of  category                                                                                                                                categories))]                                                                    (if  index                                                                        (assoc  empty  index  1)                                                                        empty)))                                                            row)                                                  empty))                                          values)]        [categories  encoded]))
  17. Multiple Data Files (defn  read-­‐lf1  [dir]    (let  [path  (.getPath

     dir)]        (map  concat                      (read-­‐csv  path  "TimeTicks1")                      (read-­‐csv  path  "LF1I")                      (read-­‐csv  path  "LF1V"))))
  18. Let’s speed it up! • Parallel: pmap, preduce • Reducers

    (fork/join) • Native Arrays: areduce, amap • Hip Hip Array! • core.matrix (BLAS support!)
  19. Hip Hip Array! ;;  1ms  for  10k  doubles:  20  MFlops

    (defn  dot-­‐product  [^doubles  ws  ^doubles  xs]    (reduce  +  (map  *  ws  xs)) ;;  8.5  us  for  10k  doubles:  2.3  GFlops ;;  (11  us  with  *unchecked-­‐math*  false) (defn  dot-­‐product  [^doubles  ws  ^doubles  xs]    (areduce  xs  i  ret  0.0        (+  ret  (*  (aget  xs  i)                            (aget  ws  i))))) ;;  8.5  us  for  10k  doubles:  2.3  GFlops (require  '[hiphip.double  :as  dbl]) (defn  dot-­‐product  [ws  xs]    (dbl/asum  [x  xs  w  ws]  (*  x  w))) //  8.5  us  for  10k  doubles:  2.3  GFlops public  double  dotProduct(double  []  ws,  double[]  xs)  {      double  result  =  0.0;        for  (int  i=0;  i  <  ws.length;  ++i)  {            result  +=  ws[i]  *  xs[i];        }      return  result; } http://blog.getprismatic.com/blog/2013/7/10/introducing-hiphip-array-fast-and-flexible-numerical-computation-in-clojure
  20. Core.Matrix (use  'core.matrix) (+  [[1  2]        [3

     4]]      (*  (identity-­‐matrix  2)  3.0)) =>  [[4.0  2.0]        [3.0  7.0]]
  21. Summary Statistics (require          '[incanter.core  :as  i]

             'incanter.io          '[incanter.stats  :as  s]) (def  data-­‐file  "data/all_160.P3.csv") (def  census  (incanter.io/read-­‐dataset  data-­‐file  :header  true)) (i/$rollup  :mean  :POP100  :STATE  census) (i/$rollup  s/sd  :POP100  :STATE  census)
  22. Time Series (require  '[incanter.core  :as  i]  '[incanter.zoo  :as  zoo]  '[clj-­‐time.format

     :as  tf]) (def  ^:dynamic  *formatter*  (tf/formatter  "dd-­‐MMM-­‐yy")) (defn  parse-­‐date  [date]  (tf/parse  *formatter*  date)) (def  data    (i/with-­‐data        (i/col-­‐names            (incanter.io/read-­‐dataset  data-­‐file)            [:date-­‐str  :open  :high  :low  :close  :volume])        (-­‐>>            (i/$map  parse-­‐date  :date-­‐str)            (i/dataset  [:date])            (i/conj-­‐cols  i/$data)))) (def  data-­‐zoo  (zoo/zoo  data  :date)) (def  data-­‐roll5    (-­‐>>        (i/sel  data-­‐zoo  :cols  :close)        (zoo/roll-­‐mean  5)        (i/dataset  [:five-­‐day])        (i/conj-­‐cols  data-­‐zoo)))
  23. Graphing/Plotting (def  iris-­‐petal-­‐scatter    (c/scatter-­‐plot  (i/sel  iris  :cols  :Petal.Width)  

                                     (i/sel  iris  :cols  :Petal.Length)                                    :title  "Irises:  Petal  Width  by  Petal  Length"                                    :x-­‐label  "Width  (cm)"                                    :y-­‐label  "Length  (cm)")) (i/view  iris-­‐petal-­‐scatter)
  24. Logistic Regression (defn  sigmoid  [z]   (/  1  (+  1

     (Math/exp  (-­‐  z))))) (defn  weights  [init  c  mat]   (let  [start-­‐values  init        alpha  0.001        error  (minus  labels  (map  sigmoid                                                                      (mmult  mat  start-­‐values)))        stop-­‐values  (plus  start-­‐values                                                        (mult  alpha  (mmult  (trans  mat)  error)))]     (if  (>  (+  c  1)  500)       start-­‐values       (weights  stop-­‐values  (+  c  1)  mat)))) (println  (weights  (matrix  1  3  1)  1  data-­‐matrix)) https://github.com/jandot/machine-learning-in-action/blob/master/logistic-regression.clj
  25. K-Nearest Neighbors (defn  calculate-­‐distance  [v1  v2]   "Calculates  distance  between

     2  vectors.  The  higher  the  result,  the  more  different  the  vectors."   (/  (count  (filter  #(=  false  %)  (map  =  v1  v2)))  (count  v1))) (defn  count-­‐occurences  [v]   "Counts  occurences  in  a  vector"   (partition  2  (interleave    (set  v)  (map  #(count  (filter  #{%}  v))  (set  v))))) (defn  majority-­‐vote  [v]   "Calculates  which  item  appears  most  often  in  a  vector.  CAUTION:  in  case  two  items  appear  equally  often,  will  pick  at   random"   (first  (last  (sort-­‐by  second  (count-­‐occurences  v))))) (defn  classify  [sample  training-­‐set  k]   (let  [distances  (pmap  #(calculate-­‐distance  (:pattern  %)  sample)  training-­‐set)        labels  (map  :label  training-­‐set)        distances-­‐with-­‐labels  (map  first  (take  k  (sort-­‐by  second  (partition  2  (interleave  labels  distances)))))]     (majority-­‐vote  distances-­‐with-­‐labels))) https://github.com/jandot/machine-learning-in-action/blob/master/knn.clj
  26. Perceptron https://github.com/fffej/ClojureProjects/blob/master/neural-networks/src/uk/co/fatvat/perceptron.clj (defn  create-­‐network      [in]    (repeat  in

     0)) (defn  run-­‐network    [input  weights]    (if  (pos?  (reduce  +  (map  *  input  weights)))  1  0)) (def  learning-­‐rate  0.05) (defn-­‐  update-­‐weights    [weights  inputs  error]    (map        (fn  [weight  input]  (+  weight  (*  learning-­‐rate  error  input)))      weights  inputs)) (defn  train    ([samples  expecteds]  (train  samples  expecteds  (create-­‐network  (count  (first  samples)))))    ([samples  expecteds  weights]          (if  (empty?  samples)              weights              (let  [sample  (first  samples)            expected  (first  expecteds)            actual  (run-­‐network  sample  weights)            error  (-­‐  expected  actual)]    (recur  (rest  samples)  (rest  expecteds)  (update-­‐weights  weights  sample  error))))))
  27. WEKA • Data mining & machine learning libraries for Java,

    built by the University of Waikato • clj-ml: Clojure interface for using weka
  28. Loading Data (use  'clj-­‐ml-­‐dev.io)   (def  ds  (load-­‐instances  :arff  "marvel-­‐universe.arff"))

    (def  ds  (load-­‐instances  :csv  "marvel-­‐universe.csv"))   (save-­‐instances  :csv  "marvel-­‐universe-­‐transformed.csv"    ds)   (use  'clj-­‐ml-­‐dev.data) (def  ds  (make-­‐dataset  "super-­‐powers"                                              [:name  :first-­‐issue  {:superpower  [:flying                                                                                                                  :melts-­‐things                                                                                                                  :regenerates]}]                                              [["Wolverine"  "Incredible  Hulk  #180"  :regenerates]                                              ["Bruno  Horgan"  "Tales  of  Suspense  #47"  :melts-­‐things]                                              ...]))
  29. Filtering & Attributes (use  '(clj-­‐ml-­‐dev  filters  io)) (def  filtered-­‐ds  (-­‐>

     ds                                          (unsupervised-­‐string-­‐to-­‐nominal                                              {:attributes  [:name  :first-­‐issue]})                                          (unsupervised-­‐nominal-­‐to-­‐binary                                              {:attributes  [:name  :first-­‐issue]}))  
  30. Classification & Training (use  'clj-­‐ml-­‐dev.classifiers) (def  classifier  (make-­‐classifier  :decission-­‐tree  :c45))

    (dataset-­‐set-­‐class  filtered-­‐ds  :superpower) (classifier-­‐train  classifier  filtered-­‐ds) (def  evaluation  (classifier-­‐evaluate  classifier  :cross-­‐validation  filtered-­‐ds  10))   (def  to-­‐classify  (make-­‐instance  ds                                                                {:superpower  :metal-­‐suit                                                                  :name  "Iron  Man"                                                                  :first-­‐issue  "Tales  of  Suspense  #39"})) (classifier-­‐classify  classifier  to-­‐classify)
  31. Saving Your Progress (use  'clj-­‐ml-­‐dev.utils) (serialize-­‐to-­‐file        

     classifier          "superhero-­‐classifier-­‐svm.bin")
  32. Parallel Processing • pmap, preduce, and other parallel operations. •

    Reducers: map, reduce, filter, fold. Moves all the transformation operations into reduce. Can make use of Java’s Fork & Join for parallel processing.
  33. Reducers (require  '[clojure.core.reducers  :as  r]) (defn  old-­‐reduce  [nums]    

       (reduce  +  (map  inc  (map  inc  (map  inc  nums)))))     (defn  new-­‐reduce  [nums]    (reduce  +  (r/map  inc  (r/map  inc  (r/map  inc  nums)))))     (defn  new-­‐fold  [nums]    (r/fold  +  (r/map  inc  (r/map  inc  (r/map  inc  nums)))))     (println  "Old  reduce:  "  (benchmark  old-­‐reduce  N  times)  "ms") (println  "New  reduce:  "  (benchmark  new-­‐reduce  N  times)  "ms") (println  "New  fold:      "  (benchmark  new-­‐fold  N  times)  "ms")     ;;  Old  reduce:              1450  ms ;;  Reducers  reduce:    1256  ms ;;  Reducers  fold:        306  ms http://adambard.com/blog/clojure-reducers-for-mortals/
  34. Cascalog (?-­‐  (stdout)        (<-­‐  [?word  ?count]  

                 (sentence  :>  ?line)                (tokenise  :<  ?line  :>  ?word)                (c/count  :>  ?count)))