зеркало из https://github.com/microsoft/EdgeML.git
Merge branch 'ProtoNN-dev' of github.com:Microsoft/EdgeML into ProtoNN-dev
This commit is contained in:
Коммит
0eea0cf96c
|
@ -21,7 +21,7 @@ int main(int argc, char **argv) {
|
|||
assert(sizeof(MKL_INT) == sizeof(Eigen::Index) && "MKL BLAS routines are called directly on data of an Eigen matrix. Hence, the index sizes should match.");
|
||||
|
||||
ProtoNN::ProtoNNPredictor predictor(argc, (const char**)argv);
|
||||
ProtoNN::ProtoNNPredictor::ResultStruct res;
|
||||
EdgeML::ResultStruct res;
|
||||
|
||||
res = predictor.test();
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ COMMON_INCLUDE_DIR=../common
|
|||
|
||||
IFLAGS= -I ../../eigen/ -I $(COMMON_INCLUDE_DIR) -I$(MKL_ROOT)/include
|
||||
|
||||
PARAMETER_SPARSITY_FLAGS = -DSPARSE_LABEL #-DSPARSE_Z #-DSPARSE_B #-DSPARSE_W
|
||||
PARAMETER_SPARSITY_FLAGS = -DSPARSE_LABEL_PROTONN #-DSPARSE_Z_PROTONN #-DSPARSE_B_PROTONN #-DSPARSE_W_PROTONN
|
||||
PROTONN_FLAGS = -DL2 $(PARAMETER_SPARSITY_FLAGS)
|
||||
|
||||
CFLAGS += $(PROTONN_FLAGS)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#define __PROTONN_H__
|
||||
|
||||
#include "Data.h"
|
||||
#include "metrics.h"
|
||||
|
||||
namespace EdgeML
|
||||
{
|
||||
|
@ -14,25 +15,25 @@ namespace EdgeML
|
|||
// Should the model parameters and labels be represented as sparse or dense?
|
||||
//
|
||||
|
||||
#ifdef SPARSE_Z
|
||||
#ifdef SPARSE_Z_PROTONN
|
||||
#define ZMatType SparseMatrixuf
|
||||
#else
|
||||
#define ZMatType MatrixXuf
|
||||
#endif
|
||||
|
||||
#ifdef SPARSE_W
|
||||
#ifdef SPARSE_W_PROTONN
|
||||
#define WMatType SparseMatrixuf
|
||||
#else
|
||||
#define WMatType MatrixXuf
|
||||
#endif
|
||||
|
||||
#ifdef SPARSE_B
|
||||
#ifdef SPARSE_B_PROTONN
|
||||
#define BMatType SparseMatrixuf
|
||||
#else
|
||||
#define BMatType MatrixXuf
|
||||
#endif
|
||||
|
||||
#ifdef SPARSE_LABEL
|
||||
#ifdef SPARSE_LABEL_PROTONN
|
||||
#define LabelMatType SparseMatrixuf
|
||||
#else
|
||||
#define LabelMatType MatrixXuf
|
||||
|
@ -223,7 +224,7 @@ namespace EdgeML
|
|||
Data testData;
|
||||
FP_TYPE* dataPoint; // for scoreSparseDataPoint
|
||||
|
||||
#ifdef SPARSE_Z
|
||||
#ifdef SPARSE_Z_PROTONN
|
||||
// for mkl csc_mv call
|
||||
char matdescra[6] = { 'G', 'X', 'X', 'C', 'X', 'X' }; // 'X' means unused
|
||||
char transa = 'n';
|
||||
|
@ -239,18 +240,6 @@ namespace EdgeML
|
|||
void createOutputDirs();
|
||||
|
||||
public:
|
||||
struct ResultStruct {
|
||||
ProblemFormat problemType;
|
||||
FP_TYPE accuracy;
|
||||
FP_TYPE precision1;
|
||||
FP_TYPE precision3;
|
||||
FP_TYPE precision5;
|
||||
|
||||
ResultStruct();
|
||||
inline void scaleAndAdd(ResultStruct& a, FP_TYPE scale);
|
||||
inline void scale(FP_TYPE scale);
|
||||
};
|
||||
|
||||
// Use this consutrctor when loading model from binary stream.
|
||||
// For this you need to have exported a binary stream model from the trainer.
|
||||
ProtoNNPredictor(
|
||||
|
@ -291,20 +280,10 @@ namespace EdgeML
|
|||
|
||||
ResultStruct testPointWise();
|
||||
|
||||
ResultStruct test();
|
||||
ResultStruct test();
|
||||
|
||||
void saveTopKScores(std::string filename="", int topk=5);
|
||||
|
||||
ResultStruct evaluate(
|
||||
const MatrixXuf& Yscores,
|
||||
const LabelMatType& Y);
|
||||
|
||||
void getTopKScoresBatch(
|
||||
const MatrixXuf& Yscores,
|
||||
MatrixXuf& topKIndices,
|
||||
MatrixXuf& topKScores,
|
||||
int k = 5);
|
||||
|
||||
void saveTopKScores(std::string filename = "", int k = 5);
|
||||
|
||||
void normalize();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace EdgeML
|
|||
const MatrixXuf& WX,
|
||||
const FP_TYPE& gamma,
|
||||
const EdgeML::ProblemFormat& problemType,
|
||||
EdgeML::ProtoNN::ProtoNNPredictor::ResultStruct& res,
|
||||
EdgeML::ResultStruct& res,
|
||||
FP_TYPE* const stats);
|
||||
|
||||
//
|
||||
|
@ -51,7 +51,7 @@ namespace EdgeML
|
|||
void accuracy(
|
||||
const ZMatType& Z, const LabelMatType& Y, const MatrixXuf& D,
|
||||
const EdgeML::ProblemFormat& problemType,
|
||||
EdgeML::ProtoNN::ProtoNNPredictor::ResultStruct& res);
|
||||
EdgeML::ResultStruct& res);
|
||||
|
||||
|
||||
//
|
||||
|
|
|
@ -59,7 +59,7 @@ size_t ProtoNNModel::modelStat()
|
|||
{
|
||||
size_t offset = 0;
|
||||
offset += sizeof(hyperParams);
|
||||
#ifdef SPARSE_Z
|
||||
#ifdef SPARSE_Z_PROTONN
|
||||
offset += sizeof(bool);
|
||||
offset += sizeof(Eigen::Index);
|
||||
Eigen::Index nnz = params.Z.outerIndexPtr()[params.Z.cols()]
|
||||
|
@ -92,7 +92,7 @@ void ProtoNNModel::exportModel(
|
|||
memcpy(toModel + offset, (void *)&hyperParams, sizeof(hyperParams));
|
||||
offset += sizeof(hyperParams);
|
||||
|
||||
#ifdef SPARSE_Z
|
||||
#ifdef SPARSE_Z_PROTONN
|
||||
memcpy(toModel + offset, (void *)&isZSparse, sizeof(bool));
|
||||
offset += sizeof(bool);
|
||||
offset += sizeof(Eigen::Index);
|
||||
|
@ -125,7 +125,7 @@ void ProtoNNModel::importModel(const size_t numBytes, const char *const fromMode
|
|||
params.resizeParamsFromHyperParams(hyperParams, false); // No need to set to zero.
|
||||
|
||||
bool isZSparse(true);
|
||||
#ifdef SPARSE_Z
|
||||
#ifdef SPARSE_Z_PROTONN
|
||||
#else
|
||||
memcpy((void *)&isZSparse, fromModel + offset, sizeof(bool));
|
||||
offset += sizeof(bool);
|
||||
|
|
|
@ -9,37 +9,6 @@
|
|||
using namespace EdgeML;
|
||||
using namespace EdgeML::ProtoNN;
|
||||
|
||||
|
||||
ProtoNNPredictor::ResultStruct::ResultStruct()
|
||||
:
|
||||
problemType(undefinedProblem),
|
||||
accuracy(0),
|
||||
precision1(0),
|
||||
precision3(0),
|
||||
precision5(0)
|
||||
{}
|
||||
|
||||
|
||||
inline void ProtoNNPredictor::ResultStruct::scaleAndAdd(ProtoNNPredictor::ResultStruct& a, FP_TYPE scale)
|
||||
{
|
||||
if ((problemType == undefinedProblem) && (a.problemType != undefinedProblem))
|
||||
problemType = a.problemType;
|
||||
assert(problemType == a.problemType);
|
||||
|
||||
accuracy += scale * a.accuracy;
|
||||
precision1 += scale * a.precision1;
|
||||
precision3 += scale * a.precision3;
|
||||
precision5 += scale * a.precision5;
|
||||
}
|
||||
|
||||
inline void ProtoNNPredictor::ResultStruct::scale(FP_TYPE scale)
|
||||
{
|
||||
accuracy *= scale;
|
||||
precision1 *= scale;
|
||||
precision3 *= scale;
|
||||
precision5 *= scale;
|
||||
}
|
||||
|
||||
ProtoNNPredictor::ProtoNNPredictor(
|
||||
const int& argc,
|
||||
const char ** argv)
|
||||
|
@ -111,7 +80,7 @@ ProtoNNPredictor::ProtoNNPredictor(
|
|||
dataPoint = new FP_TYPE[model.hyperParams.D];
|
||||
}
|
||||
|
||||
#ifdef SPARSE_Z
|
||||
#ifdef SPARSE_Z_PROTONN
|
||||
ZRows = model.params.Z.rows();
|
||||
ZCols = model.params.Z.cols();
|
||||
alpha = 1.0;
|
||||
|
@ -140,7 +109,7 @@ ProtoNNPredictor::ProtoNNPredictor(
|
|||
|
||||
dataPoint = new FP_TYPE[model.hyperParams.D];
|
||||
|
||||
#ifdef SPARSE_Z
|
||||
#ifdef SPARSE_Z_PROTONN
|
||||
ZRows = model.params.Z.rows();
|
||||
ZCols = model.params.Z.cols();
|
||||
alpha = 1.0;
|
||||
|
@ -329,7 +298,7 @@ void ProtoNNPredictor::scoreDenseDataPoint(
|
|||
RBF();
|
||||
|
||||
// mm(scoresMat, model.params.Z, CblasNoTrans, D, CblasTrans, 1.0, 0.0L);
|
||||
#ifdef SPARSE_Z
|
||||
#ifdef SPARSE_Z_PROTONN
|
||||
cscmv(&transa,
|
||||
&ZRows, &ZCols,
|
||||
&alpha, matdescra, model.params.Z.valuePtr(),
|
||||
|
@ -368,7 +337,7 @@ void ProtoNNPredictor::scoreSparseDataPoint(
|
|||
RBF();
|
||||
|
||||
// mm(scoresMat, model.params.Z, CblasNoTrans, D, CblasTrans, 1.0, 0.0L);
|
||||
#ifdef SPARSE_Z
|
||||
#ifdef SPARSE_Z_PROTONN
|
||||
cscmv(&transa,
|
||||
&ZRows, &ZCols,
|
||||
&alpha, matdescra, model.params.Z.valuePtr(),
|
||||
|
@ -430,13 +399,13 @@ void ProtoNNPredictor::normalize()
|
|||
}
|
||||
}
|
||||
|
||||
ProtoNNPredictor::ResultStruct ProtoNNPredictor::test()
|
||||
EdgeML::ResultStruct ProtoNNPredictor::test()
|
||||
{
|
||||
if(batchSize == 0) return testPointWise();
|
||||
else return testBatchWise();
|
||||
}
|
||||
|
||||
ProtoNNPredictor::ResultStruct ProtoNNPredictor::testBatchWise()
|
||||
EdgeML::ResultStruct ProtoNNPredictor::testBatchWise()
|
||||
{
|
||||
dataCount_t n;
|
||||
|
||||
|
@ -446,14 +415,14 @@ ProtoNNPredictor::ResultStruct ProtoNNPredictor::testBatchWise()
|
|||
|
||||
dataCount_t nBatches = (n + batchSize - 1)/ batchSize;
|
||||
|
||||
ProtoNNPredictor::ResultStruct res, tempRes;
|
||||
EdgeML::ResultStruct res, tempRes;
|
||||
for (dataCount_t i = 0; i < nBatches; ++i) {
|
||||
Eigen::Index startIdx = i * batchSize;
|
||||
dataCount_t curBatchSize = (batchSize < n - startIdx)? batchSize : n - startIdx;
|
||||
MatrixXuf Yscores = MatrixXuf::Zero(model.hyperParams.l, curBatchSize);
|
||||
scoreBatch(Yscores, startIdx, curBatchSize);
|
||||
|
||||
tempRes = evaluate(Yscores, testData.Ytest.middleCols(startIdx, curBatchSize));
|
||||
tempRes = evaluate(Yscores, testData.Ytest.middleCols(startIdx, curBatchSize), model.hyperParams.problemType);
|
||||
|
||||
res.scaleAndAdd(tempRes, curBatchSize);
|
||||
}
|
||||
|
@ -461,7 +430,7 @@ ProtoNNPredictor::ResultStruct ProtoNNPredictor::testBatchWise()
|
|||
return res;
|
||||
}
|
||||
|
||||
ProtoNNPredictor::ResultStruct ProtoNNPredictor::testPointWise()
|
||||
EdgeML::ResultStruct ProtoNNPredictor::testPointWise()
|
||||
{
|
||||
dataCount_t n;
|
||||
FP_TYPE *scores, *featureValues;
|
||||
|
@ -473,14 +442,14 @@ ProtoNNPredictor::ResultStruct ProtoNNPredictor::testPointWise()
|
|||
scores = new FP_TYPE[model.hyperParams.l];
|
||||
Map<MatrixXuf> Yscores(scores, model.hyperParams.l, 1);
|
||||
|
||||
ProtoNNPredictor::ResultStruct res, tempRes;
|
||||
EdgeML::ResultStruct res, tempRes;
|
||||
for (dataCount_t i = 0; i < n; ++i) {
|
||||
scoreSparseDataPoint(scores,
|
||||
(const FP_TYPE*) testData.Xtest.valuePtr() + testData.Xtest.outerIndexPtr()[i],
|
||||
(const featureCount_t*) testData.Xtest.innerIndexPtr() + testData.Xtest.outerIndexPtr()[i],
|
||||
(featureCount_t) testData.Xtest.outerIndexPtr()[i + 1] - testData.Xtest.outerIndexPtr()[i]);
|
||||
|
||||
tempRes = evaluate(Yscores, testData.Ytest.middleCols(i, 1));
|
||||
tempRes = evaluate(Yscores, testData.Ytest.middleCols(i, 1), model.hyperParams.problemType);
|
||||
res.scaleAndAdd(tempRes, 1);
|
||||
}
|
||||
res.scale(1/(FP_TYPE)n);
|
||||
|
@ -490,139 +459,20 @@ ProtoNNPredictor::ResultStruct ProtoNNPredictor::testPointWise()
|
|||
return res;
|
||||
}
|
||||
|
||||
|
||||
// computes accuracy for binary/multiclass datasets, and prec1, prec3, prec5 for multilabel datasets
|
||||
ProtoNNPredictor::ResultStruct ProtoNNPredictor::evaluate(
|
||||
const MatrixXuf& Yscores,
|
||||
const LabelMatType& Y)
|
||||
{
|
||||
assert(Yscores.cols() == Y.cols());
|
||||
assert(Yscores.rows() == Y.rows());
|
||||
MatrixXuf Ytrue(Y);
|
||||
MatrixXuf Ypred = Yscores;
|
||||
|
||||
FP_TYPE acc = 0;
|
||||
FP_TYPE prec1 = 0;
|
||||
FP_TYPE prec3 = 0;
|
||||
FP_TYPE prec5 = 0;
|
||||
|
||||
ProblemFormat problemType = model.hyperParams.problemType;
|
||||
|
||||
ProtoNNPredictor::ResultStruct res;
|
||||
res.problemType = problemType;
|
||||
|
||||
if (problemType == EdgeML::ProblemFormat::binary || problemType == EdgeML::ProblemFormat::multiclass) {
|
||||
dataCount_t Ytrue_, Ypred_;
|
||||
|
||||
for (Eigen::Index i = 0; i < Ytrue.cols(); ++i) {
|
||||
Ytrue.col(i).maxCoeff(&Ytrue_);
|
||||
Ypred.col(i).maxCoeff(&Ypred_);
|
||||
|
||||
if (Ytrue_ == Ypred_)
|
||||
acc += 1;
|
||||
}
|
||||
|
||||
assert(Y.cols() != 0);
|
||||
res.accuracy = acc = safeDiv(acc, (FP_TYPE)Y.cols());
|
||||
}
|
||||
else if (problemType == EdgeML::ProblemFormat::multilabel) {
|
||||
const labelCount_t k = 5;
|
||||
|
||||
assert(k * Ypred.cols() < 3e9);
|
||||
std::vector<labelCount_t> topInd(k * Ypred.cols());
|
||||
pfor(Eigen::Index i = 0; i < Ytrue.cols(); ++i) {
|
||||
for (Eigen::Index j = 0; j < Ytrue.rows(); ++j) {
|
||||
FP_TYPE val = Ypred(j, i);
|
||||
if (j >= k && (val < Ypred(topInd[i*k + (k - 1)], i)))
|
||||
continue;
|
||||
size_t top = std::min(j, (Eigen::Index)k - 1);
|
||||
while (top > 0 && (Ypred(topInd[i*k + (top - 1)], i) < val)) {
|
||||
topInd[i*k + (top)] = topInd[i*k + (top - 1)];
|
||||
top--;
|
||||
}
|
||||
topInd[i*k + top] = j;
|
||||
}
|
||||
}
|
||||
|
||||
assert(k >= 5);
|
||||
for (Eigen::Index i = 0; i < Ytrue.cols(); ++i) {
|
||||
for (labelCount_t j = 0; j < 1; ++j) {
|
||||
if (Ytrue(topInd[i*k + j], i) == 1)
|
||||
prec1++;
|
||||
}
|
||||
for (labelCount_t j = 0; j < 3; ++j) {
|
||||
if (Ytrue(topInd[i*k + j], i) == 1)
|
||||
prec3++;
|
||||
}
|
||||
for (labelCount_t j = 0; j < 5; ++j) {
|
||||
if (Ytrue(topInd[i*k + j], i) == 1)
|
||||
prec5++;
|
||||
}
|
||||
}
|
||||
|
||||
dataCount_t totalCount = Y.cols();
|
||||
assert(totalCount != 0);
|
||||
res.precision1 = (prec1 /= (FP_TYPE)totalCount);
|
||||
res.precision3 = (prec3 /= ((FP_TYPE)totalCount)*3);
|
||||
res.precision5 = (prec5 /= ((FP_TYPE)totalCount)*5);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void ProtoNNPredictor::getTopKScoresBatch(
|
||||
const MatrixXuf& Yscores,
|
||||
MatrixXuf& topKindices,
|
||||
MatrixXuf& topKscores,
|
||||
int k)
|
||||
{
|
||||
dataCount_t totalCount = Yscores.cols();
|
||||
dataCount_t totalLabels = Yscores.rows();
|
||||
|
||||
if (k < 1)
|
||||
k = 5;
|
||||
|
||||
if (totalLabels < k)
|
||||
k = totalLabels;
|
||||
|
||||
topKindices = MatrixXuf::Zero(k, totalCount);
|
||||
topKscores = MatrixXuf::Zero(k, totalCount);
|
||||
|
||||
pfor(Eigen::Index i = 0; i < Yscores.cols(); ++i) {
|
||||
for (Eigen::Index j = 0; j < Yscores.rows(); ++j) {
|
||||
FP_TYPE val = Yscores(j, i);
|
||||
if (j >= k && (val < Yscores(topKindices(k-1, i), i)))
|
||||
continue;
|
||||
size_t top = std::min(j, (Eigen::Index)k - 1);
|
||||
while (top > 0 && (Yscores(topKindices(top-1, i), i) < val)) {
|
||||
topKindices(top, i) = topKindices(top - 1, i);
|
||||
top--;
|
||||
}
|
||||
topKindices(top, i) = j;
|
||||
}
|
||||
}
|
||||
|
||||
pfor(Eigen::Index i = 0; i < topKindices.cols(); ++i)
|
||||
for (Eigen::Index j = 0; j < topKindices.rows(); ++j)
|
||||
topKscores(j, i) = Yscores(topKindices(j, i), i);
|
||||
}
|
||||
|
||||
|
||||
void ProtoNNPredictor::saveTopKScores(std::string filename, int topk)
|
||||
{
|
||||
dataCount_t tempBatchSize = batchSize;
|
||||
if(tempBatchSize == 0) tempBatchSize = 1;
|
||||
|
||||
dataCount_t n;
|
||||
n = testData.Xtest.cols();
|
||||
|
||||
const dataCount_t n = testData.Ytest.cols();
|
||||
assert(n > 0);
|
||||
|
||||
if (topk < 1)
|
||||
topk = 5;
|
||||
|
||||
if (filename.empty())
|
||||
filename = outDir + "/detailedPrediction";
|
||||
filename = outDir + "/detailedPrediction";
|
||||
LOG_INFO("Attempting to open the following file for detailed prediction output: " + filename);
|
||||
std::ofstream outfile(filename);
|
||||
assert(outfile.is_open());
|
||||
|
||||
|
@ -636,7 +486,7 @@ void ProtoNNPredictor::saveTopKScores(std::string filename, int topk)
|
|||
getTopKScoresBatch(Yscores, topKindices, topKscores, topk);
|
||||
|
||||
for (Eigen::Index j = 0; j < topKindices.cols(); j++) {
|
||||
for (LabelMatType::InnerIterator it(testData.Ytest, i*tempBatchSize+j); it; ++it)
|
||||
for (SparseMatrixuf::InnerIterator it(testData.Ytest, i*tempBatchSize+j); it; ++it)
|
||||
outfile << it.row() << ", ";
|
||||
for (Eigen::Index k = 0; k < topKindices.rows(); k++) {
|
||||
outfile << topKindices(k, j) << ":" << topKscores(k, j) << " ";
|
||||
|
@ -647,5 +497,3 @@ void ProtoNNPredictor::saveTopKScores(std::string filename, int topk)
|
|||
|
||||
outfile.close();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -339,7 +339,7 @@ void ProtoNNTrainer::initializeModel()
|
|||
for (labelCount_t i = 0; i < model.hyperParams.m; ++i) {
|
||||
dataCount_t prot = rand() % data.Xtrain.cols();
|
||||
model.params.B.col(i) = model.params.W * data.Xtrain.col(prot);
|
||||
#ifdef SPARSE_Z
|
||||
#ifdef SPARSE_Z_PROTONN
|
||||
model.params.Z.col(i) = data.trainLabel.col(prot).sparseView();
|
||||
#else
|
||||
model.params.Z.col(i) = data.trainLabel.col(prot);
|
||||
|
@ -353,7 +353,7 @@ void ProtoNNTrainer::initializeModel()
|
|||
MatrixXuf WX = MatrixXuf::Zero(model.params.W.rows(), data.Xtrain.cols());
|
||||
mm(WX, model.params.W, CblasNoTrans, data.Xtrain, CblasNoTrans, 1.0, 0.0L);
|
||||
|
||||
#ifdef SPARSE_Z
|
||||
#ifdef SPARSE_Z_PROTONN
|
||||
MatrixXuf Z = model.params.Z;
|
||||
assert(model.params.B.cols() % data.Ytrain.rows() == 0);
|
||||
kmeansLabelwise(data.Ytrain, WX, model.params.B, Z,
|
||||
|
@ -379,7 +379,7 @@ void ProtoNNTrainer::initializeModel()
|
|||
SparseMatrixuf YTrainSub(data.Ytrain.rows(), numRand);
|
||||
randPick(WX, WXSub);
|
||||
randPick(data.Ytrain, YTrainSub);
|
||||
#ifdef SPARSE_Z
|
||||
#ifdef SPARSE_Z_PROTONN
|
||||
MatrixXuf Z = model.params.Z;
|
||||
kmeansOverall(YTrainSub, WXSub, model.params.B, Z);
|
||||
model.params.Z = Z.sparseView();
|
||||
|
@ -388,7 +388,7 @@ void ProtoNNTrainer::initializeModel()
|
|||
#endif
|
||||
|
||||
#else
|
||||
#ifdef SPARSE_Z
|
||||
#ifdef SPARSE_Z_PROTONN
|
||||
MatrixXuf Z = model.params.Z;
|
||||
kmeansOverall(data.Ytrain, WX, model.params.B, Z);
|
||||
model.params.Z = Z.sparseView();
|
||||
|
|
|
@ -9,8 +9,10 @@ IFLAGS= -I ../../eigen/ -I$(MKL_ROOT)/include
|
|||
COMMON_INCLUDES = logger.h timer.h \
|
||||
blas_routines.h par_utils.h \
|
||||
mmaped.h utils.h \
|
||||
goldfoil.h Data.h
|
||||
COMMON_OBJS = logger.o timer.o blas_routines.o par_utils.o mmaped.o utils.o goldfoil.o Data.o
|
||||
goldfoil.h Data.h \
|
||||
metrics.h
|
||||
|
||||
COMMON_OBJS = logger.o timer.o blas_routines.o par_utils.o mmaped.o utils.o goldfoil.o Data.o metrics.o
|
||||
|
||||
COMMON_LIB = ../../libcommon.so
|
||||
|
||||
|
@ -44,6 +46,9 @@ utils.o: utils.cpp $(COMMON_INCLUDES)
|
|||
goldfoil.o: goldfoil.cpp $(COMMON_INCLUDES)
|
||||
$(CC) -c -o $@ $(IFLAGS) $(CFLAGS) $<
|
||||
|
||||
metrics.o: metrics.cpp $(COMMON_INCLUDES)
|
||||
$(CC) -c -o $@ $(IFLAGS) $(CFLAGS) $<
|
||||
|
||||
|
||||
.PHONY: clean cleanest
|
||||
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "metrics.h"
|
||||
|
||||
using namespace EdgeML;
|
||||
|
||||
EdgeML::ResultStruct::ResultStruct()
|
||||
:
|
||||
problemType(undefinedProblem),
|
||||
accuracy(0),
|
||||
precision1(0),
|
||||
precision3(0),
|
||||
precision5(0)
|
||||
{}
|
||||
|
||||
void EdgeML::ResultStruct::scaleAndAdd(ResultStruct& results, FP_TYPE scale)
|
||||
{
|
||||
if ((problemType == undefinedProblem) && (results.problemType != undefinedProblem))
|
||||
problemType = results.problemType;
|
||||
assert(problemType == results.problemType);
|
||||
|
||||
accuracy += scale * results.accuracy;
|
||||
precision1 += scale * results.precision1;
|
||||
precision3 += scale * results.precision3;
|
||||
precision5 += scale * results.precision5;
|
||||
}
|
||||
|
||||
void EdgeML::ResultStruct::scale(FP_TYPE scale)
|
||||
{
|
||||
accuracy *= scale;
|
||||
precision1 *= scale;
|
||||
precision3 *= scale;
|
||||
precision5 *= scale;
|
||||
}
|
||||
|
||||
|
||||
// computes accuracy for binary/multiclass datasets, and prec1, prec3, prec5 for multilabel datasets
|
||||
EdgeML::ResultStruct EdgeML::evaluate(
|
||||
const MatrixXuf& Yscores,
|
||||
const SparseMatrixuf& Y,
|
||||
const ProblemFormat problemType)
|
||||
{
|
||||
assert(Yscores.cols() == Y.cols());
|
||||
assert(Yscores.rows() == Y.rows());
|
||||
MatrixXuf Ytrue(Y);
|
||||
MatrixXuf Ypred = Yscores;
|
||||
|
||||
FP_TYPE acc = 0;
|
||||
FP_TYPE prec1 = 0;
|
||||
FP_TYPE prec3 = 0;
|
||||
FP_TYPE prec5 = 0;
|
||||
|
||||
EdgeML::ResultStruct res;
|
||||
res.problemType = problemType;
|
||||
|
||||
if (problemType == EdgeML::ProblemFormat::binary || problemType == EdgeML::ProblemFormat::multiclass) {
|
||||
dataCount_t Ytrue_, Ypred_;
|
||||
|
||||
for (Eigen::Index i = 0; i < Ytrue.cols(); ++i) {
|
||||
Ytrue.col(i).maxCoeff(&Ytrue_);
|
||||
Ypred.col(i).maxCoeff(&Ypred_);
|
||||
|
||||
if (Ytrue_ == Ypred_)
|
||||
acc += 1;
|
||||
}
|
||||
|
||||
assert(Y.cols() != 0);
|
||||
res.accuracy = acc = safeDiv(acc, (FP_TYPE)Y.cols());
|
||||
}
|
||||
else if (problemType == EdgeML::ProblemFormat::multilabel) {
|
||||
const labelCount_t k = 5;
|
||||
|
||||
assert(k * Ypred.cols() < 3e9);
|
||||
std::vector<labelCount_t> topInd(k * Ypred.cols());
|
||||
pfor(Eigen::Index i = 0; i < Ytrue.cols(); ++i) {
|
||||
for (Eigen::Index j = 0; j < Ytrue.rows(); ++j) {
|
||||
FP_TYPE val = Ypred(j, i);
|
||||
if (j >= k && (val < Ypred(topInd[i*k + (k - 1)], i)))
|
||||
continue;
|
||||
size_t top = std::min(j, (Eigen::Index)k - 1);
|
||||
while (top > 0 && (Ypred(topInd[i*k + (top - 1)], i) < val)) {
|
||||
topInd[i*k + (top)] = topInd[i*k + (top - 1)];
|
||||
top--;
|
||||
}
|
||||
topInd[i*k + top] = j;
|
||||
}
|
||||
}
|
||||
|
||||
assert(k >= 5);
|
||||
for (Eigen::Index i = 0; i < Ytrue.cols(); ++i) {
|
||||
for (labelCount_t j = 0; j < 1; ++j) {
|
||||
if (Ytrue(topInd[i*k + j], i) == 1)
|
||||
prec1++;
|
||||
}
|
||||
for (labelCount_t j = 0; j < 3; ++j) {
|
||||
if (Ytrue(topInd[i*k + j], i) == 1)
|
||||
prec3++;
|
||||
}
|
||||
for (labelCount_t j = 0; j < 5; ++j) {
|
||||
if (Ytrue(topInd[i*k + j], i) == 1)
|
||||
prec5++;
|
||||
}
|
||||
}
|
||||
|
||||
dataCount_t totalCount = Y.cols();
|
||||
assert(totalCount != 0);
|
||||
res.precision1 = (prec1 /= (FP_TYPE)totalCount);
|
||||
res.precision3 = (prec3 /= ((FP_TYPE)totalCount)*3);
|
||||
res.precision5 = (prec5 /= ((FP_TYPE)totalCount)*5);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void EdgeML::getTopKScoresBatch(
|
||||
const MatrixXuf& Yscores,
|
||||
MatrixXuf& topKindices,
|
||||
MatrixXuf& topKscores,
|
||||
int k)
|
||||
{
|
||||
dataCount_t totalCount = Yscores.cols();
|
||||
dataCount_t totalLabels = Yscores.rows();
|
||||
|
||||
if (k < 1)
|
||||
k = 5;
|
||||
|
||||
if (totalLabels < k)
|
||||
k = totalLabels;
|
||||
|
||||
topKindices = MatrixXuf::Zero(k, totalCount);
|
||||
topKscores = MatrixXuf::Zero(k, totalCount);
|
||||
|
||||
pfor(Eigen::Index i = 0; i < Yscores.cols(); ++i) {
|
||||
for (Eigen::Index j = 0; j < Yscores.rows(); ++j) {
|
||||
FP_TYPE val = Yscores(j, i);
|
||||
if (j >= k && (val < Yscores(topKindices(k-1, i), i)))
|
||||
continue;
|
||||
size_t top = std::min(j, (Eigen::Index)k - 1);
|
||||
while (top > 0 && (Yscores(topKindices(top-1, i), i) < val)) {
|
||||
topKindices(top, i) = topKindices(top - 1, i);
|
||||
top--;
|
||||
}
|
||||
topKindices(top, i) = j;
|
||||
}
|
||||
}
|
||||
|
||||
pfor(Eigen::Index i = 0; i < topKindices.cols(); ++i)
|
||||
for (Eigen::Index j = 0; j < topKindices.rows(); ++j)
|
||||
topKscores(j, i) = Yscores(topKindices(j, i), i);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#ifndef __METRICS_H__
|
||||
#define __METRICS_H__
|
||||
|
||||
#include "Data.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace EdgeML
|
||||
{
|
||||
struct ResultStruct {
|
||||
ProblemFormat problemType;
|
||||
FP_TYPE accuracy;
|
||||
FP_TYPE precision1;
|
||||
FP_TYPE precision3;
|
||||
FP_TYPE precision5;
|
||||
|
||||
ResultStruct();
|
||||
void scaleAndAdd(ResultStruct& a, FP_TYPE scale);
|
||||
void scale(FP_TYPE scale);
|
||||
};
|
||||
|
||||
|
||||
ResultStruct evaluate(
|
||||
const MatrixXuf& Yscores,
|
||||
const SparseMatrixuf& Y,
|
||||
const ProblemFormat problemType);
|
||||
|
||||
|
||||
void getTopKScoresBatch(
|
||||
const MatrixXuf& Yscores,
|
||||
MatrixXuf& topKIndices,
|
||||
MatrixXuf& topKScores,
|
||||
int k = 5);
|
||||
};
|
||||
|
||||
#endif
|
Загрузка…
Ссылка в новой задаче