From b19b9fe5904177db4810a81092b2938b469e633f Mon Sep 17 00:00:00 2001 From: Jakhes <dean.schmitz@schmitzbauer.de> Date: Fri, 21 Oct 2022 16:30:04 +0200 Subject: [PATCH] Adding softmax_regression tests --- .../softmax_regression/softmax_regression.cpp | 28 +- .../softmax_regression/softmax_regression.pl | 11 +- .../softmax_regression_test.pl | 333 +++++++++++++++++- test_all.pl | 5 +- 4 files changed, 350 insertions(+), 27 deletions(-) diff --git a/src/methods/softmax_regression/softmax_regression.cpp b/src/methods/softmax_regression/softmax_regression.cpp index ddf2c2c..a4505f2 100644 --- a/src/methods/softmax_regression/softmax_regression.cpp +++ b/src/methods/softmax_regression/softmax_regression.cpp @@ -19,6 +19,7 @@ using namespace mlpack::regression; SoftmaxRegression softmaxRegression; + // input: const size_t inputSize = 0, // const size_t numClasses = 0, // const bool fitIntercept = false// @@ -74,8 +75,7 @@ void initModelWithTrain(float *dataMatArr, SP_integer dataMatSize, SP_integer da SP_integer classifyPoint(float *pointArr, SP_integer pointArrSize) { // convert the Prolog arrays to arma::rowvec - rowvec pointVector = convertArrayToRowvec(pointArr, pointArrSize); - + vec pointVector = conv_to<vec>::from(convertArrayToRowvec(pointArr, pointArrSize)); try { @@ -118,6 +118,17 @@ void classifyMatrix(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMa } + // check for nan elements + if (labelsReturnVector.has_nan()) + { + raisePrologSystemExeption("Labels return Vector contains nan!"); + return; + } + if (probsReturnMat.has_nan()) + { + raisePrologSystemExeption("Probabilities return Matrix contains nan!"); + return; + } // return the Vector returnVectorInformation(labelsReturnVector, labelsArr, labelsArrSize); // return the Matrix @@ -132,18 +143,24 @@ void classifyMatrix(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMa // description: // Computes accuracy of the learned model given the feature data and the labels associated with each data point. // -double computeAccuracy(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMatRowNum, float *labelsArr, SP_integer labelsArrSize) +double computeAccuracy(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMatRowNum, + float *labelsArr, SP_integer labelsArrSize) { // convert the Prolog array to arma::mat mat data = convertArrayToMat(dataMatArr, dataMatSize, dataMatRowNum); // convert the Prolog array to arma::rowvec Row< size_t > labelsVector = convertArrayToVec(labelsArr, labelsArrSize); - + try { return softmaxRegression.ComputeAccuracy(data, labelsVector); } + catch(const std::out_of_range& e) + { + raisePrologSystemExeption("The Labels Vector has the wrong Dimension!"); + return 0.0; + } catch(const std::exception& e) { raisePrologSystemExeption(e.what()); @@ -188,7 +205,8 @@ void parameters(float **parametersMatArr, SP_integer *parametersMatColNum, SP_in // description: // Trains the softmax regression model with the given training data. // -double train(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMatRowNum, float *labelsArr, SP_integer labelsArrSize, SP_integer numClasses) +double train(float *dataMatArr, SP_integer dataMatSize, SP_integer dataMatRowNum, + float *labelsArr, SP_integer labelsArrSize, SP_integer numClasses) { // convert the Prolog array to arma::mat mat data = convertArrayToMat(dataMatArr, dataMatSize, dataMatRowNum); diff --git a/src/methods/softmax_regression/softmax_regression.pl b/src/methods/softmax_regression/softmax_regression.pl index 4a34dd6..17e1162 100644 --- a/src/methods/softmax_regression/softmax_regression.pl +++ b/src/methods/softmax_regression/softmax_regression.pl @@ -25,7 +25,7 @@ %% --Input-- -%% int inputSize => 0, +%% int inputSize => 1, %% int numClasses => 0, %% bool fitIntercept => (1)true / (0)false => false %% @@ -36,6 +36,8 @@ %% Be sure to use Train before calling Classif or ComputeAccuracy, otherwise the results may be meaningless. %% initModelNoTrain(InputSize, NumClasses, FitIntercept) :- + InputSize > 0, + NumClasses >= 0, initModelNoTrainI(InputSize, NumClasses, FitIntercept). foreign(initModelNoTrain, c, initModelNoTrainI( +integer, +integer, @@ -55,6 +57,8 @@ foreign(initModelNoTrain, c, initModelNoTrainI( +integer, +integer, %% Initializes the softmax_regression model and trains it. %% initModelWithTrain(DataList, DataRows, LabelsList, NumClasses, Lambda, FitIntercept) :- + NumClasses >= 0, + Lambda >= 0, convert_list_to_float_array(DataList, DataRows, array(Xsize, Xrownum, X)), convert_list_to_float_array(LabelsList, array(Ysize, Y)), initModelWithTrainI(X, Xsize, Xrownum, Y, Ysize, NumClasses, Lambda, FitIntercept). @@ -144,9 +148,9 @@ foreign(featureSize, c, featureSizeI([-integer])). %% --Description-- %% Get the model parameters. %% -parameters(PraametersList, XCols) :- +parameters(PrametersList, XCols) :- parametersI(X, XCols, XRows), - convert_float_array_to_2d_list(X, XCols, XRows, PraametersList). + convert_float_array_to_2d_list(X, XCols, XRows, PrametersList). foreign(parameters, c, parametersI(-pointer(float_array), -integer, -integer)). @@ -163,6 +167,7 @@ foreign(parameters, c, parametersI(-pointer(float_array), -integer, -integer)). %% Trains the softmax regression model with the given training data. %% train(DataList, DataRows, LabelsList, NumClasses, FinalValue) :- + NumClasses >= 0, convert_list_to_float_array(DataList, DataRows, array(Xsize, Xrownum, X)), convert_list_to_float_array(LabelsList, array(Ysize, Y)), trainI(X, Xsize, Xrownum, Y, Ysize, NumClasses, FinalValue). diff --git a/src/methods/softmax_regression/softmax_regression_test.pl b/src/methods/softmax_regression/softmax_regression_test.pl index 2ed582c..6612ce1 100644 --- a/src/methods/softmax_regression/softmax_regression_test.pl +++ b/src/methods/softmax_regression/softmax_regression_test.pl @@ -6,38 +6,335 @@ :- use_module(softmax_regression). :- use_module('../../helper_files/helper.pl'). -reset_Model :- - initModel(1,0,50,0.0001). +reset_Model_NoTrain :- + initModelNoTrain(3, 2, 0). + +reset_Model_WithTrain :- + initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,1,0,1], 2, 0.0001, 0). + +%% +%% TESTING predicate initModelNoTrain/3 +%% +:- begin_tests(initModelNoTrain). + +%% Failure Tests + +test(softmax_InitNoTrain_Negative_InputSize, fail) :- + initModelNoTrain(-1, 0, 0). + +test(softmax_InitNoTrain_Negative_InputSize, fail) :- + initModelNoTrain(3, -1, 0). + + +%% Successful Tests + +test(softmax_InitNoTrain_FitIntercept_False) :- + initModelNoTrain(3, 2, 0). + +test(softmax_InitNoTrain_FitIntercept_True) :- + initModelNoTrain(2, 3, 1). + +:- end_tests(initModelNoTrain). + %% -%% TESTING predicate predicate/10 +%% TESTING predicate initModelWithTrain/6 %% -:- begin_tests(predicate). +:- begin_tests(initModelWithTrain). %% Failure Tests -test(testDescription, [error(domain_error('expectation' , culprit), _)]) :- - reset_Model_No_Train(perceptron), - train([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,0,0,0], 2, culprit, 50, 0.0001, _). -test(testDescription2, [error(_,system_error('The values of the Label have to start at 0 and be >= 0 and < the given numClass!'))]) :- - reset_Model_No_Train(perceptron), - train([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,1,0,2], 2, perceptron, 50, 0.0001, _). +test(softmax_InitWithTrain_Negative_NumClass, fail) :- + initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,1,0,1], -1, 0.0001, 0). + +test(softmax_InitWithTrain_Negative_Lambda, fail) :- + initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,1,0,1], 2, -0.0001, 0). + +test(softmax_InitWithTrain_Wrong_Label_Dims1, [error(_,system_error('element-wise multiplication: incompatible matrix dimensions: 2x4 and 2x2'))]) :- + initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,1], 2, 0.0001, 0). + + +test(softmax_InitWithTrain_Wrong_Label_Dims2, [error(_,system_error('element-wise multiplication: incompatible matrix dimensions: 2x4 and 2x7'))]) :- + initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,1,0,1,0,0,1], 2, 0.0001, 0). + +%% Doesnt cause exception +test(softmax_InitWithTrain_Wrong_Label_Value) :- + initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,-1,0,-1], 2, 0.0001, 0). + +%% Doesnt cause exception +test(softmax_InitWithTrain_Too_Many_Label_Value) :- + initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [1,1,0,2], 2, 0.0001, 0). %% Successful Tests -test(testDescription3, [true(Error =:= 1)]) :- - reset_Model_No_Train(perceptron), - train([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,0,0,0], 2, perceptron, 50, 0.0001, Error). +test(softmax_InitWithTrain_Direct_Input) :- + initModelWithTrain([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,1,0,1], 2, 0.0001, 0). -test(testDescription4, [true(Error =:= 0.9797958971132711)]) :- - reset_Model_No_Train(perceptron), - open('/home/afkjakhes/eclipse-workspace/prolog-mlpack-libary/src/data_csv/iris2.csv', read, File), +test(softmax_InitWithTrain_CSV_Input) :- + open('src/data_csv/iris2.csv', read, File), take_csv_row(File, skipFirstRow,10, Data), - train(Data, 4, [0,1,0,1,1,0,1,1,1,0], 2, perceptron, 50, 0.0001, Error). + initModelWithTrain(Data, 4, [0,1,0,1,1,0,1,1,1,0], 2, 0.003, 1). + +:- end_tests(initModelWithTrain). + + +%% +%% TESTING predicate classifyPoint/2 +%% +:- begin_tests(classifyPoint). + +%% Failure Tests + +%% Doesnt cause an exception +test(softmax_ClassifyPoint_On_Untrained_Model) :- + reset_Model_NoTrain, + classifyPoint([5.1,3.5,1.4], _). + +test(softmax_ClassifyPoint_With_Too_Big_Dims, [error(_,system_error('SoftmaxRegression::Classify(): dataset has 5 dimensions, but model has 3 dimensions!'))]) :- + reset_Model_WithTrain, + classifyPoint([5.1,3.5,1.4,5.2,3.2], _). + +test(softmax_ClassifyPoint_With_Too_Small_Dims, [error(_,system_error('SoftmaxRegression::Classify(): dataset has 2 dimensions, but model has 3 dimensions!'))]) :- + reset_Model_WithTrain, + classifyPoint([5.1,3.5], _). + + +%% Successful Tests + +test(softmax_ClassifyPoint) :- + reset_Model_WithTrain, + classifyPoint([4.1,2.5,1.4], Prediction), + print('\nPrediction: '), + print(Prediction). + +:- end_tests(classifyPoint). + + +%% +%% TESTING predicate classifyMatrix/5 +%% +:- begin_tests(classifyMatrix). + +%% Failure Tests + +%% Doesnt cause an exception +test(softmax_ClassifyMatrix_On_Untrained_Model) :- + reset_Model_NoTrain, + classifyMatrix([3, 2, 0, 5, 1, 4, 1, 0, 4, 3, 3, 5, 0, 5, 5], 3, _, _, _). + +test(softmax_ClassifyMatrix_With_Too_Big_Dims, [error(_,system_error('SoftmaxRegression::Classify(): dataset has 5 dimensions, but model has 3 dimensions!'))]) :- + reset_Model_WithTrain, + classifyMatrix([3, 2, 0, 5, 1, 4, 1, 0, 4, 3, 3, 5, 0, 5, 5], 5, _, _, _). + +test(softmax_ClassifyMatrix_With_Too_Small_Dims, [error(_,system_error('SoftmaxRegression::Classify(): dataset has 2 dimensions, but model has 3 dimensions!'))]) :- + reset_Model_WithTrain, + classifyMatrix([3, 2, 0, 5, 1, 4, 1, 0, 4, 3, 3, 5, 0, 5], 2, _, _, _). + + +%% Successful Tests + +test(softmax_ClassifyMatrix_Direct_Input) :- + reset_Model_WithTrain, + classifyMatrix([3, 2, 0, 5, 1, 4, 1, 0, 4, 3, 3, 5, 0, 5, 5], 3, PredicList, ProbsList, _), + print('\nPredicted Labels: '), + print(PredicList), + print('\nProbabilities: '), + print(ProbsList). + +test(softmax_ClassifyMatrix_CSV_Input) :- + open('src/data_csv/iris2.csv', read, File), + take_csv_row(File, skipFirstRow,10, Data), + initModelWithTrain(Data, 4, [0,1,0,1,1,0,1,1,1,0], 2, 0.003, 1), + classifyMatrix(Data, 4, PredicList, ProbsList, _), + print('\nPredicted Labels: '), + print(PredicList), + print('\nProbabilities: '), + print(ProbsList). + +:- end_tests(classifyMatrix). + + +%% +%% TESTING predicate computeAccuracy/4 +%% +:- begin_tests(computeAccuracy). + +%% Failure Tests + +%% Doesnt cause an exception +test(softmax_ComputeAccuracy_On_Untrained_Model) :- + reset_Model_NoTrain, + computeAccuracy([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,1,0,1], _). + +test(softmax_ComputeAccuracy_Wrong_Label_Dims1, [error(_,system_error('The Labels Vector has the wrong Dimension!'))]) :- + reset_Model_WithTrain, + computeAccuracy([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,1], _). + +%% Doesnt cause exception +test(softmax_ComputeAccuracy_Wrong_Label_Dims2) :- + reset_Model_WithTrain, + computeAccuracy([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,1,0,1,0,0,1], _). + +%% The same when the label values are out of range +test(softmax_ComputeAccuracy_Wrong_Label_Value) :- + reset_Model_WithTrain, + computeAccuracy([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,-1,0,-1], _). + +%% Doesnt cause an exception +test(softmax_ComputeAccuracy_Too_Many_Label_Value) :- + reset_Model_WithTrain, + computeAccuracy([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [1,1,0,2], _). + +test(softmax_ComputeAccuracy_Wrong_Data_Dims, [error(_,system_error('SoftmaxRegression::Classify(): dataset has 4 dimensions, but model has 3 dimensions!'))]) :- + reset_Model_WithTrain, + computeAccuracy([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 4, [0,1,0], Accuracy), + print('\nAccuracy: '), + print(Accuracy). + + +%% Successful Tests + +test(softmax_ComputeAccuracy_Direct_Input) :- + reset_Model_WithTrain, + computeAccuracy([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,1,0,1], Accuracy), + print('\nAccuracy: '), + print(Accuracy). + +test(softmax_ComputeAccuracy_CSV_Input) :- + open('src/data_csv/iris2.csv', read, File), + take_csv_row(File, skipFirstRow,10, Data), + initModelWithTrain(Data, 4, [0,1,0,1,1,0,1,1,1,0], 2, 0.003, 1), + computeAccuracy([3, 2, 0, 5, 1, 4, 1, 0, 4, 3, 3, 5, 0, 5, 5, 2], 4, [0,1,0,1], Accuracy), + print('\nAccuracy: '), + print(Accuracy). + +:- end_tests(computeAccuracy). + + +%% +%% TESTING predicate featureSize/1 +%% +:- begin_tests(featureSize). + +%% Failure Tests + + + + +%% Successful Tests + +test(softmax_FeatureSize_No_Train) :- + reset_Model_NoTrain, + featureSize(FeatureSize), + print('\nFeatureSize: '), + print(FeatureSize). + +test(softmax_FeatureSize_Direct_Input) :- + reset_Model_WithTrain, + featureSize(FeatureSize), + print('\nFeatureSize: '), + print(FeatureSize). + +test(softmax_FeatureSize_CSV_Input) :- + open('src/data_csv/iris2.csv', read, File), + take_csv_row(File, skipFirstRow,10, Data), + initModelWithTrain(Data, 4, [0,1,0,1,1,0,1,1,1,0], 2, 0.003, 1), + featureSize(FeatureSize), + print('\nFeatureSize: '), + print(FeatureSize). + +:- end_tests(featureSize). + + +%% +%% TESTING predicate parameters/2 +%% +:- begin_tests(parameters). + +%% Failure Tests + + +%% Successful Tests + +test(softmax_Parameters_No_Train) :- + reset_Model_NoTrain, + parameters(PrametersList, _), + print('\nParameters: '), + print(PrametersList). + +test(softmax_Parameters_Direct_Input) :- + reset_Model_WithTrain, + parameters(PrametersList, _), + print('\nParameters: '), + print(PrametersList). + +test(softmax_Parameters_CSV_Input) :- + open('src/data_csv/iris2.csv', read, File), + take_csv_row(File, skipFirstRow,10, Data), + initModelWithTrain(Data, 4, [0,1,0,1,1,0,1,1,1,0], 2, 0.003, 1), + parameters(PrametersList, _), + print('\nParameters: '), + print(PrametersList). + +:- end_tests(parameters). + + +%% +%% TESTING predicate train/5 +%% +:- begin_tests(train). + +%% Failure Tests + +test(softmax_Train_Negative_NumClass, fail) :- + reset_Model_NoTrain, + train([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,1,0,1], -1, _). + +test(softmax_Train_Wrong_Label_Dims1, [error(_,system_error('element-wise multiplication: incompatible matrix dimensions: 2x4 and 2x2'))]) :- + reset_Model_NoTrain, + train([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,1], 2, _). + +%% If the label vector is to long it seems to cause no problems +test(softmax_Train_Wrong_Label_Dims2, [error(_,system_error('element-wise multiplication: incompatible matrix dimensions: 2x4 and 2x7'))]) :- + reset_Model_NoTrain, + train([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,1,0,1,0,0,1], 2, _). + +%% The same when the label values are out of range +test(softmax_Train_Wrong_Label_Value) :- + reset_Model_NoTrain, + train([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,-1,0,-1], 2, _). + +%% doesnt cause a exeption +test(softmax_Train_Too_Many_Label_Value) :- + reset_Model_NoTrain, + train([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [1,1,0,2], 2, _). + +%% doesnt cause a exeption +test(softmax_Train_Wrong_Data_Dims) :- + reset_Model_NoTrain, + train([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 4, [0,1,0], 2, _). + + +%% Successful Tests + +test(softmax_Train_Direct_Input) :- + reset_Model_NoTrain, + train([5.1,3.5,1.4,4.9,3.0,1.4,4.7,3.2,1.3,4.6,3.1,1.5], 3, [0,1,0,1], 2, FinalValue), + print('\nFinalValue: '), + print(FinalValue). + +test(softmax_Train_CSV_Input) :- + initModelNoTrain(4, 2, 0), + open('src/data_csv/iris2.csv', read, File), + take_csv_row(File, skipFirstRow,10, Data), + train(Data, 4, [0,1,0,1,1,0,1,1,1,0], 2, FinalValue), + print('\nFinalValue: '), + print(FinalValue). + +:- end_tests(train). -:- end_tests(predicate). run_softmax_regression_tests :- run_tests. diff --git a/test_all.pl b/test_all.pl index 570fd71..3ef6d61 100644 --- a/test_all.pl +++ b/test_all.pl @@ -6,6 +6,9 @@ %% The commented Methods either arent finished or still have some unfixed problems +%% Make sure you run the tests in an sicstus Toplevel with prolog-mlpack-libary as its last path +%% If not then the path to the iris2.csv will not be correct + :- use_module('src/methods/adaboost/adaboost_test.pl'). @@ -63,7 +66,7 @@ %%:- use_module('src/methods/random_forest/random_forest_test.pl'). -%%:- use_module('src/methods/softmax_regression/softmax_regression_test.pl'). +:- use_module('src/methods/softmax_regression/softmax_regression_test.pl'). %% better to run the sparse_coding tests alone because the c++ Method writes out alot of Debug messages that make the tests hard to read. %%:- use_module('src/methods/sparse_coding/sparse_coding_test.pl'). -- GitLab