UP | HOME

Table of Contents

1 Model Selection, Underfitting and Overfitting

1.1 Polynomial Regression

(ns clj-d2l.model-selection
  (:require [clj-djl.ndarray :as nd]
            [clj-djl.training :as t]
            [clj-djl.training.loss :as loss]
            [clj-djl.training.optimizer :as o]
            [clj-djl.training.tracker :as tracker]
            [clj-djl.device :as dev]
            [clj-djl.training.listener :as listener]
            [clj-djl.model :as m]
            [clj-djl.nn :as nn]
            [clj-djl.training.dataset :as ds]
            [clj-djl.metric :as metric]
            [clj-d2l.core :as d2l]))
(defn facti [x]
  (if (< x 0) 0
      (loop [fact 1
             x x]
        (if (> x 1)
          (recur (* x fact) (dec x))
          fact))))

1.1.1 Generating the dataset

(def ndm (nd/new-base-manager))
(def ntrain 100)
(def ntest 100)
(def max-degree 20)
(def true-w (nd/zeros ndm 20))
(nd/set true-w "0:4" [5. 1.2 -3.4 5.6])

(def features (nd/random-normal ndm [(+ ntrain ntest) 1]))
(def poly-features (nd/** features (-> (nd/arange ndm max-degree) (nd/reshape 1 -1))))
(doseq [i (range max-degree)]
  (.divi (nd/get poly-features ":,{}" i) (facti (+ i 1))))

(def labels (nd/dot poly-features true-w))
(nd/+ labels (nd/random-normal ndm 0 0.1 (nd/get-shape labels)))

(map #(d2l/ps %) [(nd/get features "0:2") (nd/get poly-features ":2,:") (nd/get labels ":2")])
ND: (2, 1) cpu() float32
[[1.0701],
 [1.3585],
]
ND: (2, 20) cpu() float32
[[  1.    ,   1.0701,   1.1451,   1.2254,   1.3113,   1.4032,   1.5016,   1.6068,   1.7195,   1.84  ,   1.969 ,   2.107 ,   2.2547,   2.4128,   2.5819,   2.7629,   2.9566,   3.1638,   3.3856,   3.6229],
 [  1.    ,   1.3585,   1.8454,   2.507 ,   3.4057,   4.6265,   6.285 ,   8.538 ,  11.5987,  15.7565,  21.4047,  29.0778,  39.5013,  53.6615,  72.8977,  99.0296, 134.529 , 182.754 , 248.2664, 337.2631],
]
ND: (2) cpu() float32
[ 9.2529, 14.3948]
clojure.lang.LazySeq@745f

1.1.2 Training and Testing the Model

(def metrics (atom {:epoch []
                    :train-loss []
                    :validate-loss []}))

(defn train [train-features test-features train-labels test-labels ndegree]
  (let [l2loss (loss/l2-loss)
        ndm (nd/new-base-manager)
        sgd (o/sgd {:tracker (tracker/fixed 0.01)})
        config (t/default-training-config {:loss l2loss
                                           :devices (dev/get-devices 1)
                                           :optimizer sgd
                                           :listeners (listener/logging)})
        batchsize (min 10 (nd/get (nd/get-shape train-labels) 0))
        train-iter (ds/array-dataset {:data [train-features]
                                      :labels [train-labels]
                                      :batchsize batchsize
                                      :shuffle true})
        test-iter (ds/array-dataset {:data [test-features]
                                     :labels [test-labels]
                                     :batchsize batchsize
                                     :shuffle true})]
    (with-open [model (m/new-model {:name "mlp"
                                    :block (-> (nn/sequential-block)
                                               (nn/add (nn/linear-block {:bias false :units 1})))})
                trainer (t/new-trainer model config)]
      (t/initialize trainer (nd/shape 1 ndegree))
      (doseq [epoch (range 1 1001)]
        (doseq [batch (t/iterate-dataset trainer train-iter)]
          (t/train-batch trainer batch)
          (t/step trainer)
          (ds/close-batch batch))
        (doseq [batch (t/iterate-dataset trainer test-iter)]
          (t/validate-batch trainer batch)
          (ds/close-batch batch))
        (t/notify-listeners trainer #(.onEpoch % trainer))
        (when (zero? (mod epoch 50))
          (let [train-result (t/get-training-result trainer)
                index (- (quot epoch 50) 1)]
            (swap! metrics assoc-in [:epoch index] (train-result :epoch))
            (swap! metrics assoc-in [:train-loss index] (train-result :train-loss))
            (swap! metrics assoc-in [:validate-loss index] (train-result :validate-loss)))))
      (swap! metrics assoc :weight (-> model
                                       (m/get-block)
                                       (nn/get-parameters)
                                       (.get "01Linear_weight")
                                       (.getArray)
                                       (nd/to-vec))))))
(def ndegree 4)
(train (nd/get poly-features (str "0:" ntrain ", 0:" ndegree))
       (nd/get poly-features (str ntrain ":, 0:" ndegree))
       (nd/get labels (str ":" ntrain))
       (nd/get labels (str ntrain ":"))
       ndegree)
(@metrics :weight)
[4.999991 1.2000033 -3.3999977 5.599999]
(d2l/plot-lines "figure/normalfitting.svg"
                ["train loss" "validate loss"]
                (map #(/ % 50) (@metrics :epoch))
                [(map #(Math/log10 %) (@metrics :train-loss))
                 (map #(Math/log10 %) (@metrics :validate-loss))])

Sorry, your browser does not support SVG.

(reset! metrics {:epoch []
                 :train-loss []
                 :validate-loss []})

(def ndegree 2)

(train (nd/get poly-features (str "0:" ntrain ", 0:" ndegree))
       (nd/get poly-features (str ntrain ":, 0:" ndegree))
       (nd/get labels (str ":" ntrain))
       (nd/get labels (str ntrain ":"))
       ndegree)

(@metrics :weight)
[-1.6962206 27.78034]
(d2l/plot-lines "figure/underfitting.svg"
                ["train loss" "validate loss"]
                (map #(/ % 50) (@metrics :epoch))
                [(map #(Math/log10 %) (@metrics :train-loss))
                 (map #(Math/log10 %) (@metrics :validate-loss))])

Sorry, your browser does not support SVG.

Created: 2021-04-11 Sun 20:59