changeset 26:fbc4011c7693

Now to try to get the matrix version working!
author Chris Cannam
date Mon, 31 Mar 2014 17:03:51 +0100
parents 80f02ff5d37a
children 9ec18d453889
files yeti/em.yeti yeti/silvet.yeti
diffstat 2 files changed, 119 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/yeti/em.yeti	Mon Mar 31 15:41:15 2014 +0100
+++ b/yeti/em.yeti	Mon Mar 31 17:03:51 2014 +0100
@@ -5,67 +5,125 @@
 vec = load may.vector;
 mat = load may.matrix;
 
-inRange ranges instrument note =
-    note >= ranges[instrument].lowest and note <= ranges[instrument].highest;
+inRange ranges inst note =
+    note >= ranges[inst].lowest and note <= ranges[inst].highest;
+
+normaliseColumn v =
+   (s = vec.sum v;
+    if s > 0 then vec.divideBy s v 
+    else v
+    fi);
+
+normaliseChunk =
+    mat.mapColumns normaliseColumn;
+
+normaliseSources sourceMatrices =
+   (denom = fold do acc source: mat.sum [acc, source] done
+       (mat.zeroMatrix (mat.size sourceMatrices[0])) sourceMatrices;
+    array
+       (map do source: mat.entryWiseDivide source denom done sourceMatrices));
 
 initialise ranges templates notes size =
-   (instruments = keys ranges;
+   (instrumentNames = sort (keys ranges);
+    ranges = array (map (at ranges) instrumentNames);
     {
         pitches = // z in the original. 1xN per note
-            array (map do note:
-                mat.randomMatrix { rows = 1, columns = size.columns }
-            done [0..notes-1]),
-        sources = // u in the original. 1xN per note-instrument
-            mapIntoHash id
-                do instrument:
-                    array (map do note:
-                        mat.constMatrix
-                           (if inRange ranges instrument note then 1 else 0 fi)
-                           (size with { rows = 1 })
-                    done [0..notes-1])
-                done instruments,
-        instruments,
-        templates,
+            normaliseChunk
+               (mat.randomMatrix { rows = notes, columns = size.columns }),
+        sources = // u in the original. a tensor, 1xN per note-instrument
+            normaliseSources
+               (array
+                   (map do inst:
+                        mat.tiledTo { rows = notes, columns = size.columns }
+                           (mat.newColumnVector
+                               (vec.fromList
+                                   (map do note:
+                                        if inRange ranges inst note
+                                        then 1 
+                                        else 0
+                                        fi
+                                    done [0..notes-1])))
+                    done [0..length instrumentNames - 1])),
+        instrumentNames,
+        nInstruments = length instrumentNames,
+        nNotes = notes,
         ranges,
-        lowest = head (sort (map do i: ranges[i].lowest done instruments)),
-        highest = head (reverse (sort (map do i: ranges[i].highest done instruments))),
+        templates = array 
+           (map do iname:
+                normaliseChunk templates[iname];
+            done instrumentNames),
+        lowest = head (sort (map do r: r.lowest done ranges)),
+        highest = head (reverse (sort (map do r: r.highest done ranges))),
     });
 
 epsilon = 1e-16;
 
 select predicate = concatMap do v: if predicate v then [v] else [] fi done;
 
+distributionsFor data inst note =
+    {
+        w = mat.newColumnVector (mat.getColumn note data.templates[inst]),
+        p = mat.newRowVector (mat.getRow note data.pitches),
+        s = mat.newRowVector (mat.getRow note data.sources[inst]),
+    };
+
 performExpectation data chunk =
    (estimate = 
-        fold do acc instrument:
+        fold do acc inst:
             fold do acc note:
-                template = mat.getColumn note data.templates[instrument];
-                resize = mat.tiledTo (mat.size chunk);
-                w = resize (mat.newColumnVector template);
-                p = resize data.pitches[note];
-                s = resize data.sources[instrument][note];
-                mat.sum [acc, mat.entryWiseProduct [w, p, s]];
-            done acc [data.ranges[instrument].lowest .. 
-                      data.ranges[instrument].highest]
-        done (mat.constMatrix epsilon (mat.size chunk)) data.instruments;
-    mat.entryWiseDivide chunk estimate);
+                { w, p, s } = distributionsFor data inst note;
+                mat.sum [acc, mat.product w (mat.entryWiseProduct [p, s]) ];
+            done acc [data.ranges[inst].lowest .. 
+                      data.ranges[inst].highest]
+        done (mat.constMatrix epsilon (mat.size chunk)) [0..data.nInstruments-1];
+    { estimate, q = mat.entryWiseDivide chunk estimate});
 
-performMaximisation data chunk error =
-   (pitches =
-        fold do acc note:
-            fold do acc instrument:
-                // want sum of error * original for all template and instruments
-                // for this pitch, divided by sum of error * original for all
-                // template and instruments for all pitches
+performMaximisation data chunk q =
+   (chunk = normaliseChunk chunk;
+    columns = mat.width chunk;
+    e = mat.constMatrix epsilon { rows = 1, columns };
 
-            template = mat.getColumn note data.templates[instrument];
-            w = mat.repeatedHorizontal (mat.width chunk) (mat.newColumnVector template);
-            p = mat.repeatedVertical (mat.height chunk) data.pitches[note];
-            s = mat.repeatedVertical (mat.height chunk) data.sources[instrument][note];
-            mat.sum [acc, mat.entryWiseProduct [w, p, s, error]]
-        done acc (select do i: inRange data.ranges i note done data.instruments)
-    done (mat.constMatrix epsilon (mat.size chunk)) [data.lowest .. data.highest];
-    pitches);
+    pitches = mat.concatVertical
+       (map do note:
+            if note < data.lowest or note > data.highest then e else
+                fold do acc inst:
+                    { w, p, s } = distributionsFor data inst note;
+                    fold do acc bin:
+                         mat.sum
+                            [acc, 
+                             mat.scaled (mat.at w bin 0) 
+                                (mat.entryWiseProduct
+                                    [p, s, mat.newRowVector (mat.getRow bin chunk)])]
+                    done acc [0..mat.height chunk - 1]
+                done e [0..data.nInstruments-1]
+            fi;
+        done [0..data.nNotes - 1]);
+    pitches = normaliseChunk pitches;
+
+    sources = array
+       (map do inst:
+           (mat.concatVertical
+               (map do note:
+                    if not inRange data.ranges inst note then e else
+                        { w, p, s } = distributionsFor data inst note;
+                        fold do acc bin:
+                            mat.sum
+                               [acc,
+                                mat.scaled (mat.at w bin 0)
+                                   (mat.entryWiseProduct
+                                       [p, s, mat.newRowVector (mat.getRow bin chunk)])]
+                        done e [0..mat.height chunk - 1]
+                    fi
+                done [0..data.nNotes - 1]))
+        done [0..data.nInstruments-1]);
+    sources = normaliseSources sources;
+
+    data with {
+        pitches,
+        sources,
+    });
+
+
 
 {
     initialise,
--- a/yeti/silvet.yeti	Mon Mar 31 15:41:15 2014 +0100
+++ b/yeti/silvet.yeti	Mon Mar 31 17:03:51 2014 +0100
@@ -23,7 +23,6 @@
 
 columns = prepareTimeFrequency "test.wav";
 
-/*
 height = if empty? columns then 0 else vec.length (head columns) fi;
 
 chunkSize = { rows = height, columns = 100 };
@@ -44,24 +43,27 @@
 
 eprintln "we have \(length chunks) chunks of size \(mat.size (head chunks))";
 
-eprintln "attempting one expectation phase...";
+oneIteration emdata chunk n =
+   ({ estimate, q } = em.performExpectation emdata chunk;
+    newdata = em.performMaximisation emdata chunk q;
+    if (n % 6 == 0) then
+        \() (plot.plot [ Grid chunk ]);
+        \() (plot.plot [ Grid estimate ]);
+    fi;
+    newdata);
 
-error = em.performExpectation emdata (head chunks);
+iterations = 6;
 
-eprintln "done, result has dimension \(mat.size error)";
-
-eprintln "attempting one maximisation phase...";
-
-newP = em.performMaximisation emdata (head chunks) error;
+var d = emdata;
+for [1..iterations] do i:
+    eprintln "iteration \(i)...";
+    d := oneIteration d (head chunks) i;
+done;    
 
 eprintln "done";
 
-\() (plot.plot [ Grid (head chunks) ]);
-\() (plot.plot [ Grid error ]);
 
-\() (plot.plot [ Grid newP ]);
-*/
-
+/*
 em1data = em1.initialise ranges templates 88;
 
 col = head (drop 50 columns); // (drop ((length columns) / 2) columns);
@@ -78,7 +80,6 @@
 
 oneIteration em1data col n =
    ({ estimate, q } = em1.performExpectation em1data col;
-//    \() (plot.plot [ Caption "Frequency distribution and output of E-step for iteration \(n)", Vector col, Vector q ]);
     newdata = em1.performMaximisation em1data col q;
     if (n % 6 == 0) then
         \() (plot.plot [ Caption "Pitch distribution before and after M-step update for iteration \(n)", Vector (em1data.pitches), Vector (newdata.pitches) ]);
@@ -137,6 +138,7 @@
    w = mat.getColumn p d.templates[i];
    \() (plot.plot [ Vector w, Vector (normalise col), Caption "Template for instrument \(d.instruments[i]), pitch \(p), against normalised source distribution" ]);
 fi;
+*/
 
 ();