From b2e45e0171e1f2a2b733a28102a49cee0e35f9fd Mon Sep 17 00:00:00 2001 From: "Randall C. O'Reilly" Date: Wed, 18 Dec 2024 03:21:20 -0800 Subject: [PATCH] pfcmaint actually working with ndata > 1 -- have to just do flat trials relative to env steps --- examples/pcore-ds/pcore_ds.go | 38 +++++-------------------------- examples/pfcmaint/config.go | 6 ++--- examples/pfcmaint/enumgen.go | 10 ++++---- examples/pfcmaint/params.go | 5 ++++ examples/pfcmaint/pfcmaint.go | 34 +++++++++++++-------------- examples/pfcmaint/pfcmaint_env.go | 26 +++++++++++++++++---- 6 files changed, 58 insertions(+), 61 deletions(-) diff --git a/examples/pcore-ds/pcore_ds.go b/examples/pcore-ds/pcore_ds.go index bfc9f862..2ea038c7 100644 --- a/examples/pcore-ds/pcore_ds.go +++ b/examples/pcore-ds/pcore_ds.go @@ -733,19 +733,19 @@ func (ss *Sim) ConfigStats() { ss.SetRunName() // last arg(s) are levels to exclude - counterFunc := axon.StatLoopCounters(ss.Stats, ss.Current, ss.Loops, net, Trial, Cycle) + counterFunc := axon.StatLoopCounters(ss.Stats, ss.Current, ss.Loops, net, Theta, Cycle) ss.AddStat(func(mode Modes, level Levels, phase StatsPhase) { counterFunc(mode, level, phase == Start) }) - runNameFunc := axon.StatRunName(ss.Stats, ss.Current, ss.Loops, net, Trial, Cycle) + runNameFunc := axon.StatRunName(ss.Stats, ss.Current, ss.Loops, net, Theta, Cycle) ss.AddStat(func(mode Modes, level Levels, phase StatsPhase) { runNameFunc(mode, level, phase == Start) }) - trialNameFunc := axon.StatTrialName(ss.Stats, ss.Current, ss.Loops, net, Trial) + trialNameFunc := axon.StatTrialName(ss.Stats, ss.Current, ss.Loops, net, Theta) ss.AddStat(func(mode Modes, level Levels, phase StatsPhase) { trialNameFunc(mode, level, phase == Start) }) - perTrlFunc := axon.StatPerTrialMSec(ss.Stats, Train, Trial) + perTrlFunc := axon.StatPerTrialMSec(ss.Stats, Train, Theta) ss.AddStat(func(mode Modes, level Levels, phase StatsPhase) { perTrlFunc(mode, level, phase == Start) }) @@ -773,7 +773,7 @@ func (ss *Sim) ConfigStats() { continue } switch level { - case Trial: + case Theta: for di := range ndata { ev := ss.Envs.ByModeDi(mode, di).(*MotorSeqEnv) var stat float32 @@ -796,33 +796,7 @@ func (ss *Sim) ConfigStats() { curModeDir.Float32(name, ndata).SetFloat1D(float64(stat), di) tsr.AppendRowFloat(float64(stat)) } - case Epoch: - stat = stats.StatMean.Call(subDir.Value(name)).Float1D(0) - tsr.AppendRowFloat(stat) - // if mode == Train { - // break - // } - // if si == 0 { - // stats.Groups(curModeDir, subDir.Value("TrialName")) - // break - // } - // stats.GroupStats(curModeDir, stats.StatMean, subDir.Value(name)) - // // note: results go under Group name: TrialName - // gp := curModeDir.RecycleDir("Stats/TrialName/" + name).Value("Mean") - // plot.SetFirstStylerTo(gp, func(s *plot.Style) { - // if si >= 2 && si <= 3 { - // s.On = true - // } - // }) - // if si == len(statNames)-1 { - // nrows := gp.DimSize(0) - // row := curModeDir.RecycleDir("Stats").Int("Row", nrows) - // for i := range nrows { - // row.Set(i, i) - // } - // ss.GUI.Tabs.PlotTensorFS(curModeDir.RecycleDir("Stats")) - // } - case Run: + default: stat = stats.StatMean.Call(subDir.Value(name)).Float1D(0) tsr.AppendRowFloat(stat) } diff --git a/examples/pfcmaint/config.go b/examples/pfcmaint/config.go index b87e8de3..acbd480f 100644 --- a/examples/pfcmaint/config.go +++ b/examples/pfcmaint/config.go @@ -64,7 +64,7 @@ type RunConfig struct { // NData is the number of data-parallel items to process in parallel per trial. // Is significantly faster for both CPU and GPU. Results in an effective // mini-batch of learning. - NData int `default:"16" min:"1"` + NData int `default:"2" min:"1"` // NThreads is the number of parallel threads for CPU computation; // 0 = use default. @@ -79,11 +79,11 @@ type RunConfig struct { Runs int `default:"1" min:"1"` // Epochs is the total number of epochs per run. - Epochs int `default:"30"` + Epochs int `default:"50"` // Trials is the total number of trials per epoch. // Should be an even multiple of NData. - Trials int `default:"64"` + Trials int `default:"128"` // Cycles is the total number of cycles per trial: at least 200. Cycles int `default:"200"` diff --git a/examples/pfcmaint/enumgen.go b/examples/pfcmaint/enumgen.go index 4c7ca529..3a8b329d 100644 --- a/examples/pfcmaint/enumgen.go +++ b/examples/pfcmaint/enumgen.go @@ -45,16 +45,16 @@ func (i Modes) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the [encoding.TextUnmarshaler] interface. func (i *Modes) UnmarshalText(text []byte) error { return enums.UnmarshalText(i, text, "Modes") } -var _LevelsValues = []Levels{0, 1, 2, 3, 4} +var _LevelsValues = []Levels{0, 1, 2, 3} // LevelsN is the highest valid value for type Levels, plus one. -const LevelsN Levels = 5 +const LevelsN Levels = 4 -var _LevelsValueMap = map[string]Levels{`Cycle`: 0, `Theta`: 1, `Trial`: 2, `Epoch`: 3, `Run`: 4} +var _LevelsValueMap = map[string]Levels{`Cycle`: 0, `Trial`: 1, `Epoch`: 2, `Run`: 3} -var _LevelsDescMap = map[Levels]string{0: ``, 1: ``, 2: ``, 3: ``, 4: ``} +var _LevelsDescMap = map[Levels]string{0: ``, 1: ``, 2: ``, 3: ``} -var _LevelsMap = map[Levels]string{0: `Cycle`, 1: `Theta`, 2: `Trial`, 3: `Epoch`, 4: `Run`} +var _LevelsMap = map[Levels]string{0: `Cycle`, 1: `Trial`, 2: `Epoch`, 3: `Run`} // String returns the string representation of this Levels value. func (i Levels) String() string { return enums.String(i, _LevelsMap) } diff --git a/examples/pfcmaint/params.go b/examples/pfcmaint/params.go index a55157f1..d3884bb8 100644 --- a/examples/pfcmaint/params.go +++ b/examples/pfcmaint/params.go @@ -54,9 +54,14 @@ var LayerParams = axon.LayerSheets{ // Base is always applied, and others can be optionally selected to apply on top of that. var PathParams = axon.PathSheets{ "Base": { + {Sel: "Path", Doc: "", + Set: func(pt *axon.PathParams) { + pt.Learn.LRate.Base = 0.01 + }}, {Sel: ".PFCPath", Doc: "", Set: func(pt *axon.PathParams) { pt.PathScale.Abs = 1.0 + pt.Learn.LRate.Base = 0.01 }}, {Sel: "#GPiToPFCThal", Doc: "", Set: func(pt *axon.PathParams) { diff --git a/examples/pfcmaint/pfcmaint.go b/examples/pfcmaint/pfcmaint.go index ace15283..943442bf 100644 --- a/examples/pfcmaint/pfcmaint.go +++ b/examples/pfcmaint/pfcmaint.go @@ -42,7 +42,6 @@ const ( type Levels int32 //enums:enum const ( Cycle Levels = iota - Theta Trial Epoch Run @@ -149,8 +148,8 @@ func (ss *Sim) Run() { func (ss *Sim) ConfigEnv() { // Can be called multiple times -- don't re-create newEnv := (len(ss.Envs) == 0) - - for di := 0; di < ss.Config.Run.NData; di++ { + ndata := ss.Config.Run.NData + for di := 0; di < ndata; di++ { var trn, tst *PFCMaintEnv if newEnv { trn = &PFCMaintEnv{} @@ -166,14 +165,14 @@ func (ss *Sim) ConfigEnv() { if ss.Config.Env.Env != nil { reflectx.SetFieldsFromMap(trn, ss.Config.Env.Env) } - trn.Config(etime.Train, 73+int64(di)*73) + trn.Config(etime.Train, di, ndata, 73) // same seeds so same pats tst.Name = env.ModeDi(etime.Test, di) tst.Defaults() if ss.Config.Env.Env != nil { reflectx.SetFieldsFromMap(tst, ss.Config.Env.Env) } - tst.Config(etime.Test, 181+int64(di)*181) + tst.Config(etime.Test, di, ndata, 181) trn.Init(0) tst.Init(0) @@ -275,7 +274,6 @@ func (ss *Sim) NetViewUpdater(mode enums.Enum) *axon.NetViewUpdate { func (ss *Sim) ConfigLoops() { ls := looper.NewStacks() - ev := ss.Envs.ByModeDi(Train, 0).(*PFCMaintEnv) trials := int(math32.IntMultipleGE(float32(ss.Config.Run.Trials), float32(ss.Config.Run.NData))) cycles := ss.Config.Run.Cycles plusPhase := ss.Config.Run.PlusCycles @@ -284,22 +282,20 @@ func (ss *Sim) ConfigLoops() { AddLevel(Run, ss.Config.Run.Runs). AddLevel(Epoch, ss.Config.Run.Epochs). AddLevelIncr(Trial, trials, ss.Config.Run.NData). - AddLevel(Theta, ev.NTrials). AddLevel(Cycle, cycles) ls.AddStack(Test, Trial). AddLevel(Epoch, 1). AddLevelIncr(Trial, trials, ss.Config.Run.NData). - AddLevel(Theta, ev.NTrials). AddLevel(Cycle, cycles) - axon.LooperStandard(ls, ss.Net, ss.NetViewUpdater, 50, cycles-plusPhase, cycles-1, Cycle, Theta, Train) // note: Theta + axon.LooperStandard(ls, ss.Net, ss.NetViewUpdater, 50, cycles-plusPhase, cycles-1, Cycle, Trial, Train) ls.Stacks[Train].OnInit.Add("Init", func() { ss.Init() }) - ls.AddOnStartToLoop(Theta, "ApplyInputs", func(mode enums.Enum) { + ls.AddOnStartToLoop(Trial, "ApplyInputs", func(mode enums.Enum) { trial := ls.Stacks[mode].Loops[Trial].Counter.Cur - theta := ls.Stacks[mode].Loops[Theta].Counter.Cur + theta := ls.Stacks[mode].Loops[Trial].Counter.Cur ss.ApplyInputs(mode.(Modes), trial, theta) }) @@ -314,7 +310,7 @@ func (ss *Sim) ConfigLoops() { }) if ss.Config.GUI { - axon.LooperUpdateNetView(ls, Cycle, Theta, ss.NetViewUpdater) + axon.LooperUpdateNetView(ls, Cycle, Trial, ss.NetViewUpdater) ls.Stacks[Train].OnInit.Add("GUI-Init", func() { ss.GUI.UpdateWindow() }) ls.Stacks[Test].OnInit.Add("GUI-Init", func() { ss.GUI.UpdateWindow() }) @@ -503,6 +499,7 @@ func (ss *Sim) ConfigStats() { itemly := ss.Net.LayerByName("ItemP") timely := ss.Net.LayerByName("TimeP") for di := range ndata { + ev := ss.Envs.ByModeDi(mode, di).(*PFCMaintEnv) var stat float64 switch name { case "ItemP_CorSim": @@ -510,23 +507,26 @@ func (ss *Sim) ConfigStats() { case "TimeP_CorSim": stat = 1.0 - float64(axon.LayerStates.Value(int(timely.Index), int(di), int(axon.LayerPhaseDiff))) } + if ev.Trial.Prev == 0 { // unpredictable + stat = 1 + } curModeDir.Float64(name, ndata).SetFloat1D(stat, di) tsr.AppendRowFloat(stat) } - case Epoch, Run: + default: stat = stats.StatMean.Call(subDir.Value(name)).Float1D(0) tsr.AppendRowFloat(stat) } } }) - prevCorFunc := axon.StatPrevCorSim(ss.Stats, ss.Current, net, Theta, "ItemP", "TimeP") + prevCorFunc := axon.StatPrevCorSim(ss.Stats, ss.Current, net, Trial, "ItemP", "TimeP") ss.AddStat(func(mode Modes, level Levels, phase StatsPhase) { prevCorFunc(mode, level, phase == Start) }) lays := net.LayersByClass("PFC") - actGeFunc := axon.StatLayerActGe(ss.Stats, net, Train, Theta, lays...) + actGeFunc := axon.StatLayerActGe(ss.Stats, net, Train, Trial, lays...) ss.AddStat(func(mode Modes, level Levels, phase StatsPhase) { actGeFunc(mode, level, phase == Start) }) @@ -547,12 +547,12 @@ func (ss *Sim) StatCounters(mode, level enums.Enum) string { return counters } counters += fmt.Sprintf(" TrialName: %s", curModeDir.StringValue("TrialName").String1D(di)) - statNames := []string{"Gated"} + statNames := []string{"ItemP_CorSim", "TimeP_CorSim"} if level == Cycle || curModeDir.Node(statNames[0]) == nil { return counters } for _, name := range statNames { - counters += fmt.Sprintf(" %s: %.4g", name, curModeDir.Float32(name).Float1D(di)) + counters += fmt.Sprintf(" %s: %.4g", name, curModeDir.Value(name).Float1D(di)) } return counters } diff --git a/examples/pfcmaint/pfcmaint_env.go b/examples/pfcmaint/pfcmaint_env.go index ab23448c..dcd39030 100644 --- a/examples/pfcmaint/pfcmaint_env.go +++ b/examples/pfcmaint/pfcmaint_env.go @@ -6,6 +6,7 @@ package pfcmaint import ( "fmt" + "log/slog" "cogentcore.org/core/base/randx" "cogentcore.org/core/tensor" @@ -30,9 +31,18 @@ type PFCMaintEnv struct { // trial counter is for the maint step within item Trial env.Counter `display:"inline"` - // number of different items to maintain + // Di is the data parallel index. + Di int + + // ndata is number of data parallel total. + NData int + + // number of different items to maintain. NItems int + // StartItem is item we start on, based on Di, NData. + StartItem int + // number of trials to maintain NTrials int @@ -73,15 +83,22 @@ func (ev *PFCMaintEnv) Defaults() { } // Config configures the world -func (ev *PFCMaintEnv) Config(mode etime.Modes, rndseed int64) { +func (ev *PFCMaintEnv) Config(mode etime.Modes, di, ndata int, rndseed int64) { ev.Mode = mode + ev.Di = di + ev.NData = ndata ev.RandSeed = rndseed ev.Rand.NewRand(ev.RandSeed) ev.States = make(map[string]*tensor.Float32) ev.States["Item"] = tensor.NewFloat32(ev.NUnitsY, ev.NUnitsX) ev.States["Time"] = tensor.NewFloat32(ev.NUnitsY, ev.NTrials) ev.States["GPi"] = tensor.NewFloat32(ev.NUnitsY, ev.NUnitsX) - ev.Sequence.Max = ev.NItems + if ev.NItems%ndata != 0 { + slog.Error("PFCMaintEnv: Number of items must be evenly divisible by NData", "NItems:", ev.NItems, "NData:", ndata) + } + nper := ev.NItems / ndata + ev.Sequence.Max = nper + ev.StartItem = ev.Di * nper ev.Trial.Max = ev.NTrials ev.ConfigPats() } @@ -136,7 +153,8 @@ func (ev *PFCMaintEnv) RenderState(item, trial int) { // Step does one step -- must set Trial.Cur first if doing testing func (ev *PFCMaintEnv) Step() bool { - ev.RenderState(ev.Sequence.Cur, ev.Trial.Cur) + item := ev.StartItem + ev.Sequence.Cur + ev.RenderState(item, ev.Trial.Cur) ev.Sequence.Same() if ev.Trial.Incr() { ev.Sequence.Incr()