Imports System Imports System.Text Imports System.Collections.Generic Imports CenterSpace.NMath.Core Imports System.IO Namespace CenterSpace.NMath.Examples.VisualBasic A .NET example in Visual Basic illustrating Non-negative Matrix Factorization (NMF) Module NMFExample Sub Main() Load the example data into a DataFrame Dim Frame As DataFrame = DataFrame.Load("NMFExample.dat", False, False, ",", True) Construct a Nonnegative Matrix Factorization object that will perform 200 iterations when computing the factors. Dim Fact As NMFact = New NMFact(200) Now, perform the factorization data = WH Imports the the default initial initial values for WH (which are uniform random in (0, 1) ), the default update algorithm (NMFMultiplicativeUpdate) and k = 2. Fact.Factor(Frame, 2) Look at the results. Note that since the initial values for W and H are random, the results will not be exactly the same on each run of the program. Console.WriteLine() Console.WriteLine("Output 0, Default initial W, H, and algorithm -----------------") PrintResults(Fact) Note that the values of W and H are not close to the expected values Set up some initial values for W and H so we can play with some of the factorizations parameters and observe the effects without the random variations induced by random initial values. Dim RNG As RandGenUniform = New RandGenUniform(124) Dim initialW As DoubleMatrix = New DoubleMatrix(3, 2, RNG) Dim initialH As New DoubleMatrix(2, 4, RNG) Factor and print the results Fact.Factor(Frame, 2, initialW, initialH) Console.WriteLine() Console.WriteLine() Console.WriteLine("Output 1 -------------------------------------------------------") PrintResults(Fact) Note that the factorization is pretty darn good - the cost function is on the order of 10e-18 and the difference of the elements of WH and V are all on the order of 10e-9 or 10e-10. But we are not close to the expected results. This shows the non-uniqueness of NMF solutions. Lets up the number of iterations to, say, 1000 and see if our factorization improves: Fact.NumIterations = 1000 initialW = New DoubleMatrix(3, 2, RNG) initialH = New DoubleMatrix(2, 4, RNG) Fact.Factor(Frame, 2, initialW, initialH) Console.WriteLine() Console.WriteLine() Console.WriteLine("Output 2, 1000 iterations ----------------------------------") PrintResults(Fact) Cost goes way down, all the elements of V - WH are 0 or on the order of 10e-15 - thats basically 0 within the limits of a double precision number (15 significant digits). Within machine precision, the factorization is exact and the solution is definitely a local minimum of the function ||V - WH||. Lets try and make it converge to the expected solution by setting the initial values very close to the expected values. initialW = New DoubleMatrix("3x2[1 5 2 1 3 4]") initialW = initialW + 0.01 initialH = New DoubleMatrix("2x4[1 4 3 2 8 1 2 7]") initialH = initialH - 0.01 Fact.Factor(Frame, 2, initialW, initialH) Console.WriteLine() Console.WriteLine() Console.WriteLine("Output 3, initial W, H close to expected values --------------------") PrintResults(Fact) Wow. We still converge to the same solution as before. That solution must be a very strong attractor for this algorithm. Lets try a different algorithm. The GD-CLS algorithm uses the multiplicative method, which is basically a version of the gradient descent (GD) optimization scheme, to approximate the basis vector matrix W. H is calculated Imports a constrained least squares (CLS). Fact.UpdateAlgorithm = New NMFGdClsUpdate() Dim delta As Double = 0.01 initialW = New DoubleMatrix("3x2[1 5 2 1 3 4]") initialW = initialW + delta initialH = New DoubleMatrix("2x4[1 4 3 2 8 1 2 7]") initialH = initialH + delta Fact.Factor(Frame, 2, initialW, initialH) Console.WriteLine() Console.WriteLine() Console.WriteLine("Output 4, GD-CLS algorithm initial W, H close to expected values -----") PrintResults(Fact) Now, thats more like it. We can find the expected solution if we out right on top of it. Console.WriteLine() Console.WriteLine("Press Enter Key") Console.Read() End Sub Sub PrintResults(ByVal Fact As NMFact) Matrices rounded for better legibility Look at the results: Console.WriteLine() Console.WriteLine("Factored Matrix: ") Console.WriteLine(Fact.V.ToTabDelimited("G5")) Console.WriteLine() Console.WriteLine("W: ") Console.WriteLine(Fact.W.ToTabDelimited("G5")) Console.WriteLine() Console.WriteLine("H: ") Console.WriteLine(Fact.H.ToTabDelimited("G5")) Console.WriteLine() Console.WriteLine("Cost (error) = " & Fact.Cost) Look at the product of the factors and the difference from the factored matrix V Dim WH As DoubleMatrix = NMathFunctions.Product(Fact.W, Fact.H) Dim D As DoubleMatrix = Fact.V - WH Console.WriteLine() Console.WriteLine("WH = ") Console.WriteLine(WH.ToTabDelimited("G5")) Console.WriteLine() Console.WriteLine("V - WH = ") Console.WriteLine(D.ToTabDelimited("G5")) Console.WriteLine() End Sub End Module End Namespace← All NMath Code Examples