Skip to content

Add MF fixes to release branch #3292

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 11 additions & 13 deletions src/Microsoft.ML.Recommender/MatrixFactorizationTrainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,19 @@ namespace Microsoft.ML.Trainers
/// and the value at the location specified by the two indexes. For an example data structure of a tuple, one can use:
/// </para>
/// <code language="csharp">
/// // The following variables defines the shape of a m-by-n matrix. The variable firstRowIndex indicates the integer that
/// // would be mapped to the first row index. If user data uses 0-based indices for rows, firstRowIndex can be set to 0.
/// // Similarly, for 1-based indices, firstRowIndex could be 1.
/// const int firstRowIndex = 1;
/// const int firstColumnIndex = 1;
/// // The following variables defines the shape of a m-by-n matrix. Indexes start with 0; that is, our indexing system
/// // is 0-based.
/// const int m = 60;
/// const int n = 100;
///
/// // A tuple of row index, column index, and rating. It specifies a value in the rating matrix.
/// class MatrixElement
/// {
/// // Matrix column index starts from firstColumnIndex and is at most firstColumnIndex+n-1.
/// // Contieuous=true means that all values from firstColumnIndex to firstColumnIndex+n-1 are allowed keys.
/// // [KeyType(Contiguous = true, Count = n, Min = firstColumnIndex)]
/// // public uint MatrixColumnIndex;
/// // Matrix row index starts from firstRowIndex and is at most firstRowIndex+m-1.
/// // Contieuous=true means that all values from firstRowIndex to firstRowIndex+m-1 are allowed keys.
/// [KeyType(Contiguous = true, Count = m, Min = firstRowIndex)]
/// // Matrix column index starts from 0 and is at most n-1.
/// [KeyType(n)]
/// public uint MatrixColumnIndex;
/// // Matrix row index starts from 0 and is at most m-1.
/// [KeyType(m)]
/// public uint MatrixRowIndex;
/// // The rating at the MatrixColumnIndex-th column and the MatrixRowIndex-th row.
/// public float Value;
Expand All @@ -65,7 +60,7 @@ namespace Microsoft.ML.Trainers
/// <i>R</i> is approximated by the product of <i>P</i>'s transpose and <i>Q</i>. This trainer implements
/// <a href='https://www.csie.ntu.edu.tw/~cjlin/papers/libmf/mf_adaptive_pakdd.pdf'>a stochastic gradient method</a> for finding <i>P</i>
/// and <i>Q</i> via minimizing the distance between<i> R</i> and the product of <i>P</i>'s transpose and Q.</para>.
/// <para>For users interested in the mathematical details, please see the references below.</para>
/// <para>The underlying library used in ML.NET matrix factorization can be found on <a href='https://github.com/cjlin1/libmf'>a Github repository</a>. For users interested in the mathematical details, please see the references below.</para>
/// <list type = 'bullet'>
/// <item>
/// <description><a href='https://www.csie.ntu.edu.tw/~cjlin/papers/libmf/libmf_journal.pdf' > A Fast Parallel Stochastic Gradient Method for Matrix Factorization in Shared Memory Systems</a></description>
Expand All @@ -76,6 +71,9 @@ namespace Microsoft.ML.Trainers
/// <item>
/// <description><a href='https://www.csie.ntu.edu.tw/~cjlin/papers/libmf/libmf_open_source.pdf' > LIBMF: A Library for Parallel Matrix Factorization in Shared-memory Systems</a></description>
/// </item>
/// <item>
/// <description><a href='https://www.csie.ntu.edu.tw/~cjlin/papers/one-class-mf/biased-mf-sdm-with-supp.pdf' > Selection of Negative Samples for One-class Matrix Factorization</a></description>
/// </item>
/// </list>
/// </remarks>
/// <example>
Expand Down
84 changes: 45 additions & 39 deletions src/Microsoft.ML.Recommender/SafeTrainingAndModelBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,50 @@ namespace Microsoft.ML.Recommender.Internal
/// </summary>
internal sealed class SafeTrainingAndModelBuffer : IDisposable
{
[StructLayout(LayoutKind.Explicit)]
[StructLayout(LayoutKind.Sequential)]
private struct MFNode
{
[FieldOffset(0)]
/// <summary>
/// Row index.
/// </summary>
public int U;
[FieldOffset(4)]

/// <summary>
/// Column index;
/// </summary>
public int V;
[FieldOffset(8)]

/// <summary>
/// Matrix element's value at <see cref="U"/>-th row and <see cref="V"/>-th column.
/// </summary>
public float R;
}

[StructLayout(LayoutKind.Explicit)]
[StructLayout(LayoutKind.Sequential)]
private unsafe struct MFProblem
{
[FieldOffset(0)]
/// <summary>
/// Number of rows.
/// </summary>
public int M;
[FieldOffset(4)]

/// <summary>
/// Number of columns.
/// </summary>
public int N;
[FieldOffset(8)]

/// <summary>
/// Number of specified matrix elements in <see cref="R"/>.
/// </summary>
public long Nnz;
[FieldOffset(16)]

/// <summary>
/// Specified matrix elements.
/// </summary>
public MFNode* R;
}

[StructLayout(LayoutKind.Explicit)]
[StructLayout(LayoutKind.Sequential)]
private struct MFParameter
{
/// <summary>
Expand All @@ -58,130 +77,117 @@ private struct MFParameter
/// Fun 12 is solved by a coordinate descent method while other functions invoke
/// a stochastic gradient method.
/// </summary>
[FieldOffset(0)]
public int Fun;

/// <summary>
/// Rank of factor matrices.
/// </summary>
[FieldOffset(4)]
public int K;

/// <summary>
/// Number of threads which can be used for training.
/// </summary>
[FieldOffset(8)]
public int NrThreads;

/// <summary>
/// Number of blocks that the training matrix is divided into. The parallel stochastic gradient
/// method in LIBMF processes assigns each thread a block at one time. The ratings in one block
/// would be sequentially accessed (not randomaly accessed like standard stochastic gradient methods).
/// </summary>
[FieldOffset(12)]
public int NrBins;

/// <summary>
/// Number of training iteration. At one iteration, all values in the training matrix are roughly accessed once.
/// </summary>
[FieldOffset(16)]
public int NrIters;

/// <summary>
/// L1-norm regularization coefficient of left factor matrix.
/// </summary>
[FieldOffset(20)]
public float LambdaP1;

/// <summary>
/// L2-norm regularization coefficient of left factor matrix.
/// </summary>
[FieldOffset(24)]
public float LambdaP2;

/// <summary>
/// L1-norm regularization coefficient of right factor matrix.
/// </summary>
[FieldOffset(28)]
public float LambdaQ1;

/// <summary>
/// L2-norm regularization coefficient of right factor matrix.
/// </summary>
[FieldOffset(32)]
public float LambdaQ2;

/// <summary>
/// Learning rate of LIBMF's stochastic gradient method.
/// </summary>
[FieldOffset(36)]
public float Eta;

/// <summary>
/// Coefficient of loss function on unobserved entries in the training matrix. It's used only with fun=12.
/// </summary>
[FieldOffset(40)]
public float Alpha;

/// <summary>
/// Desired value of unobserved entries in the training matrix. It's used only with fun=12.
/// </summary>
[FieldOffset(44)]
public float C;

/// <summary>
/// Specify if the factor matrices should be non-negative.
/// </summary>
[FieldOffset(48)]
public int DoNmf;
public byte DoNmf;

/// <summary>
/// Set to true so that LIBMF may produce less information to STDOUT.
/// </summary>
[FieldOffset(52)]
public int Quiet;
public byte Quiet;

/// <summary>
/// Set to false so that LIBMF may reuse and modifiy the data passed in.
/// </summary>
[FieldOffset(56)]
public int CopyData;
public byte CopyData;
}

[StructLayout(LayoutKind.Explicit)]
[StructLayout(LayoutKind.Sequential)]
private unsafe struct MFModel
{
[FieldOffset(0)]
/// <summary>
/// See <see cref="MFParameter.Fun"/>.
/// </summary>
public int Fun;

/// <summary>
/// Number of rows in the training matrix.
/// </summary>
[FieldOffset(4)]
public int M;

/// <summary>
/// Number of columns in the training matrix.
/// </summary>
[FieldOffset(8)]
public int N;

/// <summary>
/// Rank of factor matrices.
/// </summary>
[FieldOffset(12)]
public int K;

/// <summary>
/// Average value in the training matrix.
/// </summary>
[FieldOffset(16)]
public float B;

/// <summary>
/// Left factor matrix. Its shape is M-by-K stored in row-major format.
/// </summary>
[FieldOffset(24)] // pointer is 8-byte on 64-bit machine.
public float* P;

/// <summary>
/// Right factor matrix. Its shape is N-by-K stored in row-major format.
/// </summary>
[FieldOffset(32)] // pointer is 8-byte on 64-bit machine.
public float* Q;
}

Expand Down Expand Up @@ -223,9 +229,9 @@ public SafeTrainingAndModelBuffer(IHostEnvironment env, int fun, int k, int nrTh
_mfParam.Eta = (float)eta;
_mfParam.Alpha = (float)alpha;
_mfParam.C = (float)c;
_mfParam.DoNmf = doNmf ? 1 : 0;
_mfParam.Quiet = quiet ? 1 : 0;
_mfParam.CopyData = copyData ? 1 : 0;
_mfParam.DoNmf = doNmf ? (byte)1 : (byte)0;
_mfParam.Quiet = quiet ? (byte)1 : (byte)0;
_mfParam.CopyData = copyData ? (byte)1 : (byte)0;
}

~SafeTrainingAndModelBuffer()
Expand Down
21 changes: 17 additions & 4 deletions src/Native/MatrixFactorizationNative/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
project (MatrixFactorizationNative)
add_definitions(-D_SCL_SECURE_NO_WARNINGS)
add_definitions(-DUSEOMP)
add_definitions(-DUSESSE)

include_directories(libmf)

set(SOURCES
UnmanagedMemory.cpp
libmf/mf.cpp
)
if(UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O3 -pthread -std=c++0x -march=native")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fopenmp")
if (APPLE)
include_directories("/usr/local/opt/libomp/include")
link_directories("/usr/local/opt/libomp/lib")
endif()
endif()

if(WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /nologo /O2 /EHsc /D \"_CRT_SECURE_NO_DEPRECATE\" /openmp")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()

set(SOURCES UnmanagedMemory.cpp libmf/mf.cpp)

if(NOT WIN32)
list(APPEND SOURCES ${VERSION_FILE_PATH})
Expand Down
Loading