Skip to content

Commit

Permalink
Initial validation dataset handling
Browse files Browse the repository at this point in the history
  • Loading branch information
mdymczyk committed May 31, 2018
1 parent d2b97a3 commit 94265db
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 29 deletions.
33 changes: 27 additions & 6 deletions src/base/ffm/ffm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ template<typename T>
FFM<T>::FFM(Params & params, T *weights) : params(params), trainer(weights, params) {}

template<typename T>
void FFM<T>::fit(const Dataset<T> &dataset) {
void FFM<T>::fit(const Dataset<T> &dataset, const Dataset<T> &valid_dataset) {
this->trainer.setDataset(dataset);
this->trainer.setValidationDataset(valid_dataset);

Timer timer;

for (int epoch = 1; epoch <= this->params.nIter; epoch++) {
timer.tic();
trainer.oneEpoch(true);
trainer.oneEpoch();
timer.toc();
log_debug(params.verbose, "Epoch took %f.", timer.pop());
if (trainer.earlyStop()) {
Expand Down Expand Up @@ -71,27 +72,47 @@ T maxElement(const T *data, int size) {
/**
* C API method
*/
void ffm_fit_float(int* features, int* fields, float* values, int *labels, int *rowPositions, float *w, Params &_param) {
log_debug(_param.verbose, "Converting %d float rows into a dataset.", _param.numRows);

void ffm_fit_float(int *features, int* fields, float* values, int *labels, int *rowPositions,
int *features_v, int* fields_v, float* values_v, int *labels_v, int *rowPositions_v,
float *w, Params &_param) {
log_debug(_param.verbose, "Converting %d float rows into a training dataset.", _param.numRows);
float *scales = (float*) malloc(sizeof(float) * _param.numRows);
computeScales(scales, values, rowPositions, _param);

_param.numFields = maxElement(fields, _param.numNodes) + 1;
_param.numFeatures = maxElement(features, _param.numNodes) + 1;

Dataset<float> dataset(_param.numFields, _param.numFeatures, _param.numRows, _param.numNodes, features, fields, values, labels, scales, rowPositions);

Dataset<float> *validationDataset = Dataset<float>();
if(features_v) {
log_debug(_param.verbose, "Converting %d float rows into a validation dataset.", _param.numRowsVal);

float *scales_v = (float*) malloc(sizeof(float) * _param.numRowsVal);
computeScales(scales_v, values_v, rowPositions_v, _param);

validationDataset = new Dataset<float>(_param.numFields, _param.numFeatures, _param.numRowsVal, _param.numNodesVal, features_v, fields_v, values_v, labels_v, scales_v, rowPositions_v);
}

FFM<float> ffm(_param);
_param.printParams();
log_debug(_param.verbose, "Running FFM fit for float.");
Timer timer;
timer.tic();
ffm.fit(dataset);
ffm.fit(dataset, *validationDataset);
ffm.trainer.model->copyTo(w);
timer.toc();
log_debug(_param.verbose, "Float fit took %f.", timer.pop());

if(validationDataset) {
delete validationDataset;
}
}

void ffm_fit_double(int* features, int* fields, double* values, int *labels, int *rowPositions, double *w, Params &_param) {
void ffm_fit_double(int *features, int* fields, double* values, int *labels, int *rowPositions,
int *features_v, int* fields_v, double* values_v, int *labels_v, int *rowPositions_v,
double *w, Params &_param) {
log_debug(_param.verbose, "Converting %d double rows into a dataset.", _param.numRows);
double *scales = (double*) malloc(sizeof(double) * _param.numRows);
computeScales(scales, values, rowPositions, _param);
Expand Down
2 changes: 1 addition & 1 deletion src/base/ffm/ffm.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class FFM {
FFM(Params & params);
FFM(Params & params, T *weights);

void fit(const Dataset<T> &dataset);
void fit(const Dataset<T> &dataset, const Dataset<T> &valid_dataset);

void predict(const Dataset<T> &dataset, T *predictions);

Expand Down
15 changes: 11 additions & 4 deletions src/base/ffm/trainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@ template<typename T>
class Trainer {
public:
Trainer(Params &params);
Trainer(const T* weights, Params &params);
Trainer(const T *weights, Params &params);
~Trainer();

void setDataset(const Dataset<T> &dataset);

T oneEpoch(bool update);
T validationLoss();

T oneEpoch();

T oneEpoch(std::vector<DatasetBatcher<T> *> dataBatcher, bool update);

void predict(T *predictions);

Expand All @@ -32,8 +36,11 @@ class Trainer {
private:
Params &params;

// Vector of datasets split for threads/GPUs
std::vector<DatasetBatcher<T>*> trainDataBatcher;
// Vector of train datasets splits for threads/GPUs
std::vector<DatasetBatcher<T> *> trainDataBatcher;

// Vector of validation datasets split for threads/GPUs
std::vector<DatasetBatcher<T> *> validationDataBatcher;

};

Expand Down
24 changes: 15 additions & 9 deletions src/gpu/ffm/trainer.cu
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ __device__ double atomicAdd(double* address, double val)
namespace ffm {

template<typename T>
Trainer<T>::Trainer(Params &params) : params(params), trainDataBatcher(params.nGpus) {
Trainer<T>::Trainer(Params &params) : params(params), trainDataBatcher(params.nGpus), validationDataBatcher(params.nGpus) {
CUDA_CHECK(cudaSetDeviceFlags(cudaDeviceMapHost));
CUDA_CHECK(cudaSetDeviceFlags(cudaDeviceScheduleSpin));

Expand All @@ -53,7 +53,7 @@ Trainer<T>::Trainer(Params &params) : params(params), trainDataBatcher(params.nG
}

template<typename T>
Trainer<T>::Trainer(const T* weights, Params &params) : params(params), trainDataBatcher(params.nGpus) {
Trainer<T>::Trainer(const T* weights, Params &params) : params(params), trainDataBatcher(params.nGpus), validationDataBatcher(params.nGpus) {
CUDA_CHECK(cudaSetDeviceFlags(cudaDeviceMapHost));
CUDA_CHECK(cudaSetDeviceFlags(cudaDeviceScheduleSpin));

Expand Down Expand Up @@ -225,8 +225,18 @@ void Trainer<T>::predict(T *predictions) {
}
}

template<typename T>
T Trainer<T>::validationLoss() {
return this->oneEpoch(this->validationDataBatcher, false);
}

template<typename T>
T Trainer<T>::oneEpoch(bool update) {
return this->oneEpoch(this->trainDataBatcher, true);
}

template<typename T>
T Trainer<T>::oneEpoch(std::vector<DatasetBatcher<T>*> dataBatcher, bool update) {
Timer timer;
log_debug(this->params.verbose, "Computing an FFM epoch (update = %s)", update ? "true" : "false");

Expand All @@ -236,13 +246,9 @@ T Trainer<T>::oneEpoch(bool update) {
int initialBatchOffset = 0;
CUDA_CHECK(cudaMemcpyToSymbol(cBatchOffset, &initialBatchOffset, sizeof(int)));

while (trainDataBatcher[i]->hasNext()) {
/**
* Get batch
*/

while (dataBatcher[i]->hasNext()) {
timer.tic();
DatasetBatch<T> *batch = trainDataBatcher[i]->nextBatch(this->params.batchSize);
DatasetBatch<T> *batch = dataBatcher[i]->nextBatch(this->params.batchSize);
timer.toc();
log_verbose(params.verbose, "Getting batch took %f.", timer.pop());

Expand Down Expand Up @@ -314,7 +320,7 @@ T Trainer<T>::oneEpoch(bool update) {
cudaFree(losses);
}

trainDataBatcher[i]->reset();
dataBatcher[i]->reset();
}

if (params.nGpus != 1) {
Expand Down
8 changes: 6 additions & 2 deletions src/include/data/ffm/data.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ class Dataset {
rowPositions(rowPositions){}

// Number of rows in the dataset
int numRows;
int numRows = 0;
// Total number of nodes in all the rows
int numNodes;
int numNodes = 0;
// Total number of fields
int numFields = 0;
// Total number of features
Expand All @@ -75,6 +75,10 @@ class Dataset {
int requiredBytes() const {
return numNodes * 2 * sizeof(int) + numRows * sizeof(T) + numRows * sizeof(int) + numRows * sizeof(T);
}

bool empty() {
return numRows == 0;
}
};

}
13 changes: 11 additions & 2 deletions src/include/solver/ffm_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ typedef struct Params {
int nIter = 10;
int batchSize = -1;

// Training dataset params
int numRows = 0;
int numNodes = 0;
int numFeatures = 0;
Expand All @@ -34,14 +35,22 @@ typedef struct Params {
// For GPU number of GPUs to be used
int nGpus = 1;

// Validation dataset params
int numRowsVal = 0;
int numNodesVal = 0;

void printParams() {
log_verbose(verbose, "learningRate = %f \n regLambda = %f \n nIter = %d \n batchSize = %d \n numRows = %d \n numFeatures = %d \n numFields = %d \n k = %d", learningRate, regLambda, nIter, batchSize, numRows, numFeatures, numFields, k);
}

} Params;

void ffm_fit_float(int *features, int* fields, float* values, int *labels, int *positions, float *w, Params &_param);
void ffm_fit_double(int *features, int* fields, double* values, int *labels, int *positions, double *w, Params &_param);
void ffm_fit_float(int *features, int* fields, float* values, int *labels, int *positions,
int *features_v, int* fields_v, float* values_v, int *labels_v, int *positions_v,
float *w, Params &_param);
void ffm_fit_double(int *features, int* fields, double* values, int *labels, int *positions,
int *features_v, int* fields_v, double* values_v, int *labels_v, int *positions_v,
double *w, Params &_param);

void ffm_predict_float(int *features, int* fields, float* values, int* positions, float *predictions, float *w, Params &_param);
void ffm_predict_double(int *features, int* fields, double* values, int* positions, double *predictions, double *w, Params &_param);
Expand Down
22 changes: 17 additions & 5 deletions src/interface_py/h2o4gpu/solvers/ffm.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def set_params(self, **params):
# TODO implement
pass

def fit(self, X, y):
def fit(self, X, y, X_validate=None, y_validate=None):
lib = self._load_lib()

params = lib.params_ffm()
Expand All @@ -117,19 +117,31 @@ def fit(self, X, y):

fields, features, values, positions = self._numpy_to_ffm_rows(params, X)

self.weights = np.zeros(params.k * (np.max(features) + 1) * (np.max(fields) + 1), dtype=self.dtype)

y_np = self._sanatize_labels(y)

fields_validation, features_validation, values_validation, positions_validation = None, None, None, None
if X_validate is not None and y_validate is not None:
fields_validation, features_validation, values_validation, positions_validation = self._numpy_to_ffm_rows(params, X_validate)

y_validation_np = self._sanatize_labels(y_validate)

self.weights = np.zeros(params.k * (np.max(features) + 1) * (np.max(fields) + 1), dtype=self.dtype)

if self.dtype == np.float32:
lib.ffm_fit_float(features, fields, values, y_np, positions, self.weights, params)
lib.ffm_fit_float(features, fields, values, y_np, positions,
features_validation, fields_validation, values_validation, y_validation_np, positions_validation,
self.weights, params)
else:
lib.ffm_fit_double(features, fields, values, y_np, positions, self.weights, params)
lib.ffm_fit_double(features, fields, values, y_np, positions,
features_validation, fields_validation, values_validation, y_validation_np, positions_validation,
self.weights, params)

self.learned_params = params
return self

def _sanatize_labels(self, y):
if y is None:
return None
return np.array(list(map(lambda e: 1 if e > 0 else -1, y)), dtype=np.int32)

def _numpy_to_ffm_rows(self, params, X):
Expand Down

0 comments on commit 94265db

Please sign in to comment.