using System; using System.Text; using System.Collections.Generic; using CenterSpace.NMath.Core; using System.IO; namespace CenterSpace.NMath.Examples.CSharp { /// <summary> /// A .NET example in C# showing use of Nonnegative Matrix Factorization (NMF). /// </summary> class NMFExample { static void Main( string[] args ) { DataFrame data = DataFrame.Load( "NMFExample.dat", false, false, ", ", true ); // Construct a Nonnegative Matrix Factorization object that will perform 200 // iterations when computing the factors. var fact = new NMFact( 200 ); // Now, perform the factorization data = WH using 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( data, 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( "\nOutput 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. var rng = new RandGenUniform( 0x124 ); var initialW = new DoubleMatrix( 3, 2, rng ); var initialH = new DoubleMatrix( 2, 4, rng ); // Factor and print the results fact.Factor( data, 2, initialW, initialH ); Console.WriteLine( "\n\nOutput 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( data, 2, initialW, initialH ); Console.WriteLine( "\n\nOutput 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 + .01; initialH = new DoubleMatrix( "2x4[1 4 3 2 8 1 2 7]" ); initialH = initialH - .01; fact.Factor( data, 2, initialW, initialH ); Console.WriteLine( "\n\nOutput 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 using a constrained least squares (CLS). fact.UpdateAlgorithm = new NMFGdClsUpdate(); double delta = 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( data, 2, initialW, initialH ); Console.WriteLine( "\n\nOutput 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(); } static void PrintResults( NMFact nmf ) { // Matrices rounded for better legibility // Look at the results: Console.WriteLine( "\nFactored Matrix: " ); Console.WriteLine( nmf.V.ToTabDelimited( "G5" ) ); Console.WriteLine( "\nW: " ); Console.WriteLine( nmf.W.ToTabDelimited( "G5" ) ); Console.WriteLine( "\nH: " ); Console.WriteLine( nmf.H.ToTabDelimited( "G5" ) ); Console.WriteLine( "\nCost (error) = " + nmf.Cost ); // Look at the product of the factors and the difference from // the factored matrix V DoubleMatrix WH = NMathFunctions.Product( nmf.W, nmf.H ); DoubleMatrix D = nmf.V - WH; Console.WriteLine( "\nWH = " ); Console.WriteLine( WH.ToTabDelimited( "G5" ) ); Console.WriteLine( "\nV - WH = " ); Console.WriteLine( D.ToTabDelimited( "G5" ) ); Console.WriteLine(); } } }← All NMath Code Examples