[TOC]
Imports System
Imports System.Text
Imports System.Collections.Generic
Imports CenterSpace.NMath.Core
Imports CenterSpace.NMath.Stats
Namespace CenterSpace.NMath.Stats.Examples.VisualBasic
' Example illustrating Non-negative Matrix Factorization (NMF)
Module NMFExample
Sub Main()
Dim Frame As DataFrame
Dim Filename As String = "..\\..\\NMFExample.dat"
Try
' Load the example data into a DataFrame
Frame = DataFrame.Load(Filename, False, False, ",", True)
Catch E As NMathException
Dim Msg As String = String.Format("Could not find data file {0}.", Filename)
Msg += Environment.NewLine
Msg += E.Message
Msg += Environment.NewLine
Msg += "Data file must have the same name as the example source "
Msg += Environment.NewLine
Msg += "file and be located two directories up from where the "
Msg += Environment.NewLine
Msg += "executable is running."
Console.WriteLine(Msg)
Console.WriteLine()
Return
End Try
' 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 inital 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. Let's 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 - that's 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||. Let's 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. Let's 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, that's more like it. We can find the expected soltion if we
' out right on top of it.
Console.WriteLine()
Console.WriteLine("Press Enter Key")
Console.ReadKey()
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())
Console.WriteLine()
Console.WriteLine("W: ")
Console.WriteLine(NMathFunctions.Round(Fact.W, 4).ToTabDelimited())
Console.WriteLine()
Console.WriteLine("H: ")
Console.WriteLine(NMathFunctions.Round(Fact.H, 4).ToTabDelimited())
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(NMathFunctions.Round(WH, 4).ToTabDelimited())
Console.WriteLine()
Console.WriteLine("V - WH = ")
Console.WriteLine(NMathFunctions.Round(D, 10).ToTabDelimited())
Console.WriteLine()
End Sub
End Module
End Namespace
[TOC]