private PcaPredictor TrainCore(IChannel ch, RoleMappedData data, int dimension) { Host.AssertValue(ch); ch.AssertValue(data); if (_rank > dimension) { throw ch.Except("Rank ({0}) cannot be larger than the original dimension ({1})", _rank, dimension); } int oversampledRank = Math.Min(_rank + _oversampling, dimension); //exact: (size of the 2 big matrices + other minor allocations) / (2^30) Double memoryUsageEstimate = 2.0 * dimension * oversampledRank * sizeof(Float) / 1e9; if (memoryUsageEstimate > 2) { ch.Info("Estimate memory usage: {0:G2} GB. If running out of memory, reduce rank and oversampling factor.", memoryUsageEstimate); } var y = Zeros(oversampledRank, dimension); var mean = _center ? VBufferUtils.CreateDense <Float>(dimension) : VBufferUtils.CreateEmpty <Float>(dimension); var omega = GaussianMatrix(oversampledRank, dimension, _seed); var cursorFactory = new FeatureFloatVectorCursor.Factory(data, CursOpt.Features | CursOpt.Weight); long numBad; Project(Host, cursorFactory, ref mean, omega, y, out numBad); if (numBad > 0) { ch.Warning("Skipped {0} instances with missing features/weights during training", numBad); } //Orthonormalize Y in-place using stabilized Gram Schmidt algorithm. //Ref: https://en.wikipedia.org/wiki/Gram-Schmidt#Algorithm for (var i = 0; i < oversampledRank; ++i) { var v = y[i]; VectorUtils.ScaleBy(ref v, 1 / VectorUtils.Norm(y[i])); // Make the next vectors in the queue orthogonal to the orthonormalized vectors. for (var j = i + 1; j < oversampledRank; ++j) //subtract the projection of y[j] on v. { VectorUtils.AddMult(ref v, -VectorUtils.DotProduct(ref v, ref y[j]), ref y[j]); } } var q = y; // q in QR decomposition. var b = omega; // reuse the memory allocated by Omega. Project(Host, cursorFactory, ref mean, q, b, out numBad); //Compute B2 = B' * B var b2 = new Float[oversampledRank * oversampledRank]; for (var i = 0; i < oversampledRank; ++i) { for (var j = i; j < oversampledRank; ++j) { b2[i * oversampledRank + j] = b2[j * oversampledRank + i] = VectorUtils.DotProduct(ref b[i], ref b[j]); } } Float[] smallEigenvalues;// eigenvectors and eigenvalues of the small matrix B2. Float[] smallEigenvectors; EigenUtils.EigenDecomposition(b2, out smallEigenvalues, out smallEigenvectors); PostProcess(b, smallEigenvalues, smallEigenvectors, dimension, oversampledRank); return(new PcaPredictor(Host, _rank, b, ref mean)); }
private void Train(Arguments args, TransformInfo[] transformInfos, IDataView trainingData) { var y = new Float[transformInfos.Length][][]; var omega = new Float[transformInfos.Length][][]; var mean = new Float[transformInfos.Length][]; var oversampledRank = new int[transformInfos.Length]; var rnd = Host.Rand; Double totalMemoryUsageEstimate = 0; for (int iinfo = 0; iinfo < transformInfos.Length; iinfo++) { oversampledRank[iinfo] = Math.Min(transformInfos[iinfo].Rank + _oversampling[iinfo], transformInfos[iinfo].Dimension); //exact: (size of the 2 big matrices + other minor allocations) / (2^30) Double colMemoryUsageEstimate = 2.0 * transformInfos[iinfo].Dimension * oversampledRank[iinfo] * sizeof(Float) / 1e9; totalMemoryUsageEstimate += colMemoryUsageEstimate; if (colMemoryUsageEstimate > 2) { using (var ch = Host.Start("Memory usage")) { ch.Info("Estimate memory usage for transforming column {1}: {0:G2} GB. If running out of memory, reduce rank and oversampling factor.", colMemoryUsageEstimate, Infos[iinfo].Name); ch.Done(); } } y[iinfo] = new Float[oversampledRank[iinfo]][]; omega[iinfo] = new Float[oversampledRank[iinfo]][]; for (int i = 0; i < oversampledRank[iinfo]; i++) { y[iinfo][i] = new Float[transformInfos[iinfo].Dimension]; omega[iinfo][i] = new Float[transformInfos[iinfo].Dimension]; for (int j = 0; j < transformInfos[iinfo].Dimension; j++) { omega[iinfo][i][j] = (Float)Stats.SampleFromGaussian(rnd); } } if (_center[iinfo]) { mean[iinfo] = new Float[transformInfos[iinfo].Dimension]; } } if (totalMemoryUsageEstimate > 2) { using (var ch = Host.Start("Memory usage")) { ch.Info("Estimate memory usage for all PCA transforms: {0:G2} GB. If running out of memory, reduce ranks and oversampling factors.", totalMemoryUsageEstimate); ch.Done(); } } Project(trainingData, mean, omega, y, transformInfos); for (int iinfo = 0; iinfo < transformInfos.Length; iinfo++) { //Orthonormalize Y in-place using stabilized Gram Schmidt algorithm //Ref: http://en.wikipedia.org/wiki/Gram-Schmidt#Algorithm for (var i = 0; i < oversampledRank[iinfo]; ++i) { var v = y[iinfo][i]; VectorUtils.ScaleBy(v, 1 / VectorUtils.Norm(y[iinfo][i])); // normalize // Make the next vectors in the queue orthogonal to the orthonormalized vectors for (var j = i + 1; j < oversampledRank[iinfo]; ++j) { VectorUtils.AddMult(v, y[iinfo][j], -VectorUtils.DotProduct(v, y[iinfo][j])); //subtract the projection of y[j] on v } } } var q = y; // q in QR decomposition var b = omega; // reuse the memory allocated by Omega Project(trainingData, mean, q, b, transformInfos); for (int iinfo = 0; iinfo < transformInfos.Length; iinfo++) { //Compute B2 = B' * B var b2 = new Float[oversampledRank[iinfo] * oversampledRank[iinfo]]; for (var i = 0; i < oversampledRank[iinfo]; ++i) { for (var j = i; j < oversampledRank[iinfo]; ++j) { b2[i * oversampledRank[iinfo] + j] = b2[j * oversampledRank[iinfo] + i] = VectorUtils.DotProduct(b[iinfo][i], b[iinfo][j]); } } Float[] smallEigenvalues; // eigenvectors and eigenvalues of the small matrix B2. Float[] smallEigenvectors; EigenUtils.EigenDecomposition(b2, out smallEigenvalues, out smallEigenvectors); transformInfos[iinfo].Eigenvectors = PostProcess(b[iinfo], smallEigenvalues, smallEigenvectors, transformInfos[iinfo].Dimension, oversampledRank[iinfo]); transformInfos[iinfo].ProjectMean(mean[iinfo]); } }