anchor.diff:          let l = model.loss(x)
           l.reverse()
           optimizer.step()
  -        printfn "epoch: %A/%A minibatch: %A/%A loss: %A" epoch epochs i trainLoader.length (float(l))
  +        printfn "Epoch: %A/%A minibatch: %A/%A loss: %A" epoch epochs i trainLoader.length (float(l))
   
  -        if i % 250 = 0 then
  -            printfn "Saving samples"
  -            let samples = model.sample(64).view([-1; 1; 28; 28])
  -            samples.saveImage(sprintf "samples_%A_%A.png" epoch i)
  +        if i % validInterval = 0 then
  +            let mutable validLoss = dsharp.zero()
  +            for _, x, _ in validLoader.epoch() do
  +                validLoss <- validLoss + model.loss(x, normalize=false)
  +            validLoss <- validLoss / validSet.length
  +            printfn "Validation loss: %A" (float validLoss)
  +            let fileName = sprintf "samples_epoch_%A_minibatch_%A.png" epoch i
  +            printfn "Saving %A samples to %A" numSamples fileName
  +            let samples = model.sample(numSamples).view([-1; 1; 28; 28])
  +            samples.saveImage(fileName)
   
  
positive.code:         let logVar = enc.[enc.Length-1].forward(x)
          mu, logVar
  
      let latent mu (logVar:Tensor) =
          let std = dsharp.exp(0.5*logVar)
          let eps = dsharp.randnLike(std)
          eps.mul(std).add(mu)
  
negative.code:         let kl = -0.5 * dsharp.sum(1. + logVar - mu.pow(2.) - logVar.exp())
          bce + kl
  
      member m.loss(x, ?normalize:bool) =
          let normalize = defaultArg normalize true
          let xRecon, mu, logVar = m.encodeDecode x
          let loss = VAE.loss(xRecon, x, mu, logVar)
          if normalize then loss / x.shape.[0] else loss
  
      member _.sample(?numSamples:int) = 
          let numSamples = defaultArg numSamples 1
  