func TestHMMChain(t *testing.T) { r := rand.New(rand.NewSource(444)) numModels := 20 dim := 4 //8 maxNumStates := 6 iter0 := 1 // from alignments iter1 := 2 // FB ntrain0 := 1000 ntrain1 := 20000 maxChainLen := 8 // max number of nets in chain. numTestItems := 1000 // num test sequences. maxTestLen := 10 // Create reference HMM to generate random sequences. ms0, _ := NewSet() for q := 0; q < numModels; q++ { ns := int(r.Intn(maxNumStates-2) + 3) id := "m" + strconv.FormatInt(int64(q), 10) net, e := addRandomNet(r, ms0, id, ns, dim) if e != nil { t.Fatal(e) } _ = net } hmm0 := NewModel(OSet(ms0), OAssign(DirectAssigner{}), UpdateTP(true), UpdateOP(true)) t.Log("hmm0: ", hmm0) // Create random HMM and estimate params using the randomly generated sequences. ms, e := initRandomSet(r, ms0) if e != nil { t.Fatal(e) } // hmm := NewModel(OSet(ms), OAssign(DirectAssigner{}), UpdateTP(true), UpdateOP(true)) hmm := NewModel(OSet(ms), OAssign(DirectAssigner{}), UseAlignments(true)) t.Log("initial hmm: ", hmm) numFrames := 0 t0 := time.Now() // Start timer. t.Log("start training from alignments") for i := 0; i < iter0; i++ { t.Logf("iter [%d]", i) // Make sure we generate the same data in each iteration. r := rand.New(rand.NewSource(33)) gen := newChainGen(r, true, maxChainLen, ms0.Nets...) // Reset all counters. hmm.Clear() // fix the seed to get the same sequence for j := 0; j < ntrain0; j++ { obs, states := gen.next("oid-" + fi(j)) numFrames += len(states) - 2 hmm.UpdateOne(obs, 1.0) } hmm.Estimate() } t.Log("start training using forward-backward algo") hmm.SetFlags(false, true, true) for i := 0; i < iter1; i++ { t.Logf("iter [%d]", i) // Make sure we generate the same data in each iteration. r := rand.New(rand.NewSource(55)) gen := newChainGen(r, true, maxChainLen, ms0.Nets...) // Reset all counters. hmm.Clear() // fix the seed to get the same sequence for j := 0; j < ntrain1; j++ { obs, states := gen.next("oid-" + fi(j)) numFrames += len(states) - 2 hmm.UpdateOne(obs, 1.0) } hmm.Estimate() } dur := time.Now().Sub(t0) t.Log("dur: ", dur) for name, net0 := range ms0.byName { net := ms.byName[name] h0 := net0.A h := net.A tp0 := narray.Exp(nil, h0.Copy()) tp := narray.Exp(nil, h.Copy()) ns := tp.Shape[0] for i := 0; i < ns; i++ { for j := 0; j < ns; j++ { p0 := tp0.At(i, j) logp0 := h0.At(i, j) p := tp.At(i, j) logp := h.At(i, j) if p > smallNumber || p0 > smallNumber { t.Logf("name: %s, %d=>%d, p0:%5.2f, p:%5.2f, logp0:%8.5f, logp:%8.5f", name, i, j, p0, p, logp0, logp) } } } t.Log("") for i := 1; i < ns-1; i++ { t.Logf("hmm0 state:%d, %s", i, net0.B[i]) t.Logf("hmm state:%d, %s", i, net.B[i]) t.Log("") } } t.Log("final hmm: ", hmm) // Print time stats. t.Log("") t.Logf("Total time: %v", dur) t.Logf("Time per iteration: %v", dur/time.Duration(iter1)) t.Logf("Time per frame: %v", dur/time.Duration(iter1*numFrames*ntrain1)) // Recognize. g := ms.SearchGraph() dec, e := graph.NewDecoder(g) if e != nil { t.Fatal(e) } r = rand.New(rand.NewSource(5151)) gen := newChainGen(r, true, maxTestLen, ms0.Nets...) testDecoder(t, gen, dec, numTestItems) }
// should be equivalent to training a single gaussian, great for debugging. func TestSingleState(t *testing.T) { // HMM to generate data. g01 := gm.NewModel(1, gm.Name("g01"), gm.Mean([]float64{0}), gm.StdDev([]float64{1})) h0 := narray.New(3, 3) h0.Set(1, 0, 1) h0.Set(.8, 1, 1) h0.Set(.2, 1, 2) h0 = narray.Log(nil, h0.Copy()) ms0, _ := NewSet() net0, e0 := ms0.NewNet("hmm", h0, []model.Modeler{nil, g01, nil}) fatalIf(t, e0) hmm0 := NewModel(OSet(ms0)) _ = hmm0 // Create gaussian to estimate without using the HMM code. g := gm.NewModel(1, gm.Name("g1"), gm.Mean([]float64{-1}), gm.StdDev([]float64{2})) // Create initial HMM and estimate params from generated data. g1 := gm.NewModel(1, gm.Name("g1"), gm.Mean([]float64{-1}), gm.StdDev([]float64{2})) h := narray.New(3, 3) h.Set(1, 0, 1) h.Set(.5, 1, 1) h.Set(.5, 1, 2) h = narray.Log(nil, h.Copy()) ms, _ = NewSet() net, e := ms.NewNet("hmm", h, []model.Modeler{nil, g1, nil}) fatalIf(t, e) hmm := NewModel(OSet(ms), UpdateTP(true), UpdateOP(true)) iter := 5 // number of sequences m := 1000 numFrames := 0 t0 := time.Now() // Start timer. for i := 0; i < iter; i++ { t.Logf("iter [%d]", i) // Make sure we generate the same data in each iteration. r := rand.New(rand.NewSource(33)) gen := newGenerator(r, false, net0) // Reset all counters. hmm.Clear() g.Clear() // fix the seed to get the same sequence for j := 0; j < m; j++ { obs, states := gen.next("oid-" + fi(j)) numFrames += len(states) - 2 hmm.UpdateOne(obs, 1.0) // Update Gaussian for _, o := range obs.ValueAsSlice() { vec := o.([]float64) gobs := model.NewFloatObs(vec, model.SimpleLabel("")) g.UpdateOne(gobs, 1.0) } } hmm.Estimate() g.Estimate() t.Logf("iter:%d, hmm g1: %+v", i, net.B[1]) t.Logf("iter:%d, direct g1:%+v", i, g) } dur := time.Now().Sub(t0) tp0 := narray.Exp(nil, h0.Copy()) tp := narray.Exp(nil, net.A.Copy()) ns := tp.Shape[0] for i := 0; i < ns; i++ { for j := 0; j < ns; j++ { p0 := tp0.At(i, j) logp0 := h0.At(i, j) p := tp.At(i, j) logp := h.At(i, j) if p > smallNumber || p0 > smallNumber { t.Logf("TP: %d=>%d, p0:%5.2f, p:%5.2f, logp0:%8.5f, logp:%8.5f", i, j, p0, p, logp0, logp) } } } t.Log("") t.Logf("hmm0 g1:%+v", net0.B[1]) t.Logf("hmm g1: %+v", net.B[1]) t.Log("") t.Logf("direct g1:%+v", g) // Print time stats. t.Log("") t.Logf("Total time: %v", dur) t.Logf("Time per iteration: %v", dur/time.Duration(iter)) t.Logf("Time per frame: %v", dur/time.Duration(iter*numFrames*m)) gjoa.CompareSliceFloat(t, tp0.Data, tp.Data, "error in Trans Probs [0]", .03) CompareGaussians(t, net0.B[1].(*gm.Model), net.B[1].(*gm.Model), 0.03) if t.Failed() { t.FailNow() } // Recognize. sg := ms.SearchGraph() dec, e := graph.NewDecoder(sg) if e != nil { t.Fatal(e) } r := rand.New(rand.NewSource(5151)) gen := newGenerator(r, true, net0) // testDecoder(t, gen, dec, 1000) testDecoder(t, gen, dec, 10) }
func TestHMMGauss(t *testing.T) { // Create reference HMM to generate observations. g01 := gm.NewModel(1, gm.Name("g01"), gm.Mean([]float64{0}), gm.StdDev([]float64{1})) g02 := gm.NewModel(1, gm.Name("g02"), gm.Mean([]float64{16}), gm.StdDev([]float64{2})) h0 := narray.New(4, 4) h0.Set(.6, 0, 1) h0.Set(.4, 0, 2) h0.Set(.9, 1, 1) h0.Set(.1, 1, 2) h0.Set(.7, 2, 2) h0.Set(.3, 2, 3) h0 = narray.Log(nil, h0.Copy()) ms0, _ := NewSet() net0, e0 := ms0.NewNet("hmm", h0, []model.Modeler{nil, g01, g02, nil}) fatalIf(t, e0) hmm0 := NewModel(OSet(ms0), UpdateTP(true), UpdateOP(true)) _ = hmm0 // Create random HMM and estimate params from obs. g1 := gm.NewModel(1, gm.Name("g1"), gm.Mean([]float64{-1}), gm.StdDev([]float64{2})) g2 := gm.NewModel(1, gm.Name("g2"), gm.Mean([]float64{18}), gm.StdDev([]float64{4})) h := narray.New(4, 4) h.Set(.5, 0, 1) h.Set(.5, 0, 2) h.Set(.5, 1, 1) h.Set(.5, 1, 2) h.Set(.5, 2, 2) h.Set(.5, 2, 3) h = narray.Log(nil, h.Copy()) ms, _ = NewSet() net, e := ms.NewNet("hmm", h, []model.Modeler{nil, g1, g2, nil}) fatalIf(t, e) hmm := NewModel(OSet(ms), UpdateTP(true), UpdateOP(true)) iter := 10 // number of sequences m := 500 numFrames := 0 t0 := time.Now() // Start timer. for i := 0; i < iter; i++ { t.Logf("iter [%d]", i) // Make sure we generate the same data in each iteration. r := rand.New(rand.NewSource(33)) gen := newGenerator(r, false, net0) // Reset all counters. hmm.Clear() // fix the seed to get the same sequence for j := 0; j < m; j++ { obs, states := gen.next("oid-" + fi(j)) numFrames += len(states) - 2 hmm.UpdateOne(obs, 1.0) } hmm.Estimate() } dur := time.Now().Sub(t0) tp0 := narray.Exp(nil, h0.Copy()) tp := narray.Exp(nil, net.A.Copy()) ns := tp.Shape[0] for i := 0; i < ns; i++ { for j := 0; j < ns; j++ { p0 := tp0.At(i, j) logp0 := h0.At(i, j) p := tp.At(i, j) logp := h.At(i, j) if p > smallNumber || p0 > smallNumber { t.Logf("TP: %d=>%d, p0:%5.2f, p:%5.2f, logp0:%8.5f, logp:%8.5f", i, j, p0, p, logp0, logp) } } } t.Log("") t.Logf("hmm0 g1:%+v, g2:%+v", net0.B[1], net0.B[2]) t.Logf("hmm g1: %+v, g2:%+v", net.B[1], net.B[2]) // Print time stats. t.Log("") t.Logf("Total time: %v", dur) t.Logf("Time per iteration: %v", dur/time.Duration(iter)) t.Logf("Time per frame: %v", dur/time.Duration(iter*numFrames*m)) gjoa.CompareSliceFloat(t, tp0.Data, tp.Data, "error in Trans Probs [0]", .03) CompareGaussians(t, net0.B[1].(*gm.Model), net.B[1].(*gm.Model), 0.03) CompareGaussians(t, net0.B[2].(*gm.Model), net.B[2].(*gm.Model), 0.03) if t.Failed() { t.FailNow() } // Recognize. g := ms.SearchGraph() dec, e := graph.NewDecoder(g) if e != nil { t.Fatal(e) } r := rand.New(rand.NewSource(5151)) gen := newGenerator(r, true, net0) testDecoder(t, gen, dec, 1000) }