XIII · NVIDIA acceleration (welvet.cuda) / CHAPTER 87
NVIDIA CUDA: weight return and ENTITY save
github.com/openfluke/welvet/nativecudalinux/windows amd64
Why it exists
Return trained weights to Go before executing or saving the Go model.
What it is
Train natively, explicitly SyncToGo, save an ENTITY, reload it and compare the native parameter snapshots.
Weight handoff
session, err := nativecuda.Mount(layer)
// Check err; defer session.Close().
// Repeat session.TrainMSE(...) as needed.
err = session.SyncToGo()
// Check err, then save through Go ENTITY IO.Mount copies model state into a native session. The original Go parameters stay unchanged while that explicit session trains. SyncToGo() returns parameter bytes, scales and Float64 biases for Go saving. This saves model parameters, not a portable checkpoint of live Line, proxy, KV or rule state.
Use -out /path/model.entity to choose the output location. The default uses a temporary directory and prints it.
Requirements: chapter 89.
Go example
examples/92-nvidia-save-weights/main.go
cd welvet/examples/92-nvidia-save-weights && source ../env.sh && go run .// Explicit mount ownership, weight return to Go, ENTITY save and reload.
package main
import (
"flag"
"fmt"
"log"
"os"
"path/filepath"
"reflect"
"runtime"
"github.com/openfluke/welvet/core"
"github.com/openfluke/welvet/layers/dense"
"github.com/openfluke/welvet/layers/parallel"
"github.com/openfluke/welvet/model/entity"
"github.com/openfluke/welvet/nativecuda"
"github.com/openfluke/welvet/quant"
)
func main() {
if e := run(); e != nil {
log.Fatal(e)
}
}
func run() error {
out := flag.String("out", "", "ENTITY destination; default is a new temporary directory")
flag.Parse()
if !cudaPlatformOK() {
fmt.Println("SKIP: NVIDIA CUDA needs linux/amd64 or windows/amd64 and cgo")
return nil
}
l, e := dense.NewConfigured(2, 2, core.ActivationLinear, core.DTypeFloat32, quant.FormatNone, []float32{.3, -.2, .1, .4})
if e != nil {
return e
}
model, e := parallel.NewStack(l)
if e != nil {
return e
}
before, e := l.Weights.FlattenF32()
if e != nil {
return e
}
s, e := nativecuda.Mount(model)
if e != nil {
fmt.Printf("SKIP: CUDA unavailable (%v)\n", e)
return nil
}
defer s.Close()
x := &core.Tensor[float32]{Shape: []int{1, 2}, Data: []float32{.5, .25}}
target := core.NewTensor[float32](1, 2)
for i := 0; i < 5; i++ {
if _, e = s.TrainMSE(x, target, parallel.ModeNormalBP, .1); e != nil {
return e
}
}
unchanged, e := l.Weights.FlattenF32()
if e != nil {
return e
}
if !reflect.DeepEqual(before, unchanged) {
return fmt.Errorf("explicit mount unexpectedly changed Go parameters before SyncToGo")
}
fmt.Println("Go parameters unchanged before SyncToGo")
if e = s.SyncToGo(); e != nil {
return e
}
after, e := l.Weights.FlattenF32()
if e != nil {
return e
}
if reflect.DeepEqual(before, after) {
return fmt.Errorf("expected trained parameters after SyncToGo")
}
if *out == "" {
dir, e := os.MkdirTemp("", "welvet-cuda-entity-")
if e != nil {
return e
}
*out = filepath.Join(dir, "trained.entity")
}
if e = entity.WriteCameralFile(*out, model, parallel.ModeNormalBP); e != nil {
return e
}
loaded, mode, e := entity.LoadCameral(*out)
if e != nil {
return e
}
if mode != parallel.ModeNormalBP {
return fmt.Errorf("training mode changed during save")
}
restored, e := nativecuda.Mount(loaded)
if e != nil {
return e
}
defer restored.Close()
want, e := s.Weights()
if e != nil {
return e
}
got, e := restored.Weights()
if e != nil {
return e
}
if !reflect.DeepEqual(want, got) {
return fmt.Errorf("saved parameters changed on reload")
}
fmt.Printf("weights returned to Go; saved=%s; reload=exact\n", *out)
return nil
}
func cudaPlatformOK() bool {
return runtime.GOARCH == "amd64" && (runtime.GOOS == "linux" || runtime.GOOS == "windows")
}
Output
Go parameters unchanged before SyncToGo
weights returned to Go; saved=/srv/gs/openfluke.github.io/welvet/examples/.cache/tmp/welvet-cuda-entity-3626499342/trained.entity; reload=exact
Validate (harness)
cd welvet/examples/92-nvidia-save-weights && source ../env.sh && go run .