Merge branch 'master' of https://github.com/mlpack/mlpack into access_models

This commit is contained in:
NippunSharma
2021-04-14 16:42:18 +05:30
70 changed files with 2089 additions and 1296 deletions
-219
View File
@@ -1,219 +0,0 @@
clone_depth: 10
environment:
BOOST_MATH : "C:/projects/mlpack/\
boost_math_c99-vc140.1.60.0.0/lib/native/address-model-64/lib/*.*"
BOOST_RANDOM : "C:/projects/mlpack/\
boost_random-vc140.1.60.0.0/lib/native/address-model-64/lib/*.*"
ARMADILLO_DOWNLOAD : "https://data.kurg.org/armadillo-8.400.0.tar.xz"
ARMADILLO_LIBRARY : "C:/projects/mlpack/armadillo-8.400.0/\
build/Debug/armadillo.lib"
BLAS_LIBRARY : "%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/\
libopenblas.dll.a"
BOOST_INCLUDE : "C:/projects/mlpack/boost.1.60.0.0/lib/native/include"
JENKINS_DOC_DOWNLOAD : "http://ci.mlpack.org/job/mlpack%20-%20doxygen%20\
build/lastSuccessfulBuild/artifact/build/doc/html/*zip*/html.zip"
JENKINS_DOC : "C:/projects/mlpack/dist/win-installer/jenkinsdoc.zip"
GIT_VERSION_FILE : "C:/projects/mlpack/src/mlpack/core/util/gitversion.hpp"
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
VSVER: Visual Studio 16 2019
MSBUILD: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe
# We have removed the VS studio 15 2017 build since it is not possible to complete
# or finish the build due to the `compiler out of heap space issues`.
# Therefore, in the meanwhile, we are only doing the installation for VS 16 2019.
configuration: Release
os: Visual Studio 2019
install:
- ps: nuget install boost -o "${env:APPVEYOR_BUILD_FOLDER}" -Version 1.60.0
- ps: >
nuget install boost_random-vc140
-o "${env:APPVEYOR_BUILD_FOLDER}" -Version 1.60.0
- ps: >
nuget install boost_math_c99-vc140
-o "${env:APPVEYOR_BUILD_FOLDER}" -Version 1.60.0
- ps: >
nuget install unofficial-flayan-cereal
-o "${env:APPVEYOR_BUILD_FOLDER}"
- ps: nuget install OpenBLAS -o "${env:APPVEYOR_BUILD_FOLDER}"
- set path=C:\Program Files (x86)\WiX Toolset v3.11\bin;%path%
build_script:
- mkdir boost_libs
- ps: cp ${env:BOOST_MATH} C:\projects\mlpack\boost_libs\
- ps: cp ${env:BOOST_RANDOM} C:\projects\mlpack\boost_libs\
- echo TEST_ARMA is %ARMADILLO_DOWNLOAD%
- >
appveyor DownloadFile %ARMADILLO_DOWNLOAD%
-FileName armadillo.tar.xz
- 7z x armadillo.tar.xz -so -txz | 7z x -si -ttar > nul
- cd armadillo-8.400.0 && mkdir build && cd build
- >
cmake -G "%VSVER%"
-DBLAS_LIBRARY:FILEPATH=%BLAS_LIBRARY%
-DLAPACK_LIBRARY:FILEPATH=%BLAS_LIBRARY%
-DCMAKE_PREFIX:FILEPATH="%APPVEYOR_BUILD_FOLDER%/armadillo"
-DBUILD_SHARED_LIBS=OFF
-DCMAKE_BUILD_TYPE=Release ..
- >
"%MSBUILD%" "C:\projects\mlpack\armadillo-8.400.0\build\armadillo.sln"
/m /verbosity:quiet /p:Configuration=Release;Platform=x64
- cd C:\projects\mlpack && mkdir build && cd build
- >
cmake -G "%VSVER%"
-DBLAS_LIBRARIES:FILEPATH=%BLAS_LIBRARY%
-DLAPACK_LIBRARIES:FILEPATH=%BLAS_LIBRARY%
-DARMADILLO_INCLUDE_DIR="C:/projects/mlpack/armadillo-8.400.0/include"
-DARMADILLO_LIBRARY:FILEPATH=%ARMADILLO_LIBRARY%
-DCEREAL_INCLUDE_DIR="C:/projects/mlpack/unofficial-flayan-cereal.1.2.2/build/native/include"
-DBOOST_INCLUDEDIR:PATH=%BOOST_INCLUDE%
-DDEBUG=OFF
-DPROFILE=OFF
-DBUILD_PYTHON_BINDINGS=OFF
-DBUILD_GO_BINDINGS=OFF
-DBUILD_R_BINDINGS=OFF
-DBUILD_TESTS=OFF
-DCMAKE_BUILD_TYPE=Release ..
- >
"%MSBUILD%" "C:\projects\mlpack\build\mlpack.sln"
/m /verbosity:minimal /nologo /p:BuildInParallel=true
/p:Configuration=Release;Platform=x64
# Zip Artifacts.
- >
7z a mlpack-windows-no-libs.zip
"%APPVEYOR_BUILD_FOLDER%\build\Release\*.exe"
- >
7z a mlpack-windows.zip
"%APPVEYOR_BUILD_FOLDER%\build\Release\*.*"
"%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/*.*"
# Pulling documentation for the installer.
- ps: >
try{(new-object net.webclient).DownloadFile(${env:JENKINS_DOC_DOWNLOAD},
'C:\projects\mlpack\dist\win-installer\jenkinsdoc.zip')}
catch{Write-Output "Unable to pull jenkins doc, skipping!"}
- ps: >
try{(Add-Type -AssemblyName System.IO.Compression.FileSystem);
[System.IO.Compression.ZipFile]::ExtractToDirectory(${env:JENKINS_DOC},
'C:\projects\mlpack\dist\win-installer\staging\doc')}
catch{Write-Output "Unable to add doc to installer, skipping!"}
# Preparing installer staging.
- cd C:\projects\mlpack\dist\win-installer\staging && mkdir lib
- ps: >
cp C:\projects\mlpack\build\Release\*.lib
C:\projects\mlpack\dist\win-installer\staging\lib\
- ps: >
cp C:\projects\mlpack\build\Release\*.exp
C:\projects\mlpack\dist\win-installer\staging\lib\
- ps: >
cp C:\projects\mlpack\build\Release\*.dll
C:\projects\mlpack\dist\win-installer\staging\
- ps: >
cp C:\projects\mlpack\build\Release\*.exe
C:\projects\mlpack\dist\win-installer\staging\
- ps: >
cp C:\projects\mlpack\OpenBLAS.0.2.14.1\lib\native\bin\x64\*.dll
C:\projects\mlpack\dist\win-installer\staging\
- ps: >
cp C:\projects\mlpack\build\include\mlpack
C:\projects\mlpack\dist\win-installer\staging -recurse
- ps: >
cp C:\projects\mlpack\doc\examples
C:\projects\mlpack\dist\win-installer\staging -recurse
- ps: >
cp C:\projects\mlpack\src\mlpack\tests\data\german.csv
C:\projects\mlpack\dist\win-installer\staging\examples\sample-ml-app\sample-ml-app\data\
# Checking current gitversion or mlpack version.
- ps: >
$ver = (Get-Content
"${env:APPVEYOR_BUILD_FOLDER}\src\mlpack\core\util\version.hpp" |
where {$_ -like "*MLPACK_VERSION*"});
$env:MLPACK_VERSION += $ver[0].substring($ver[0].length - 1, 1) + '.';
$env:MLPACK_VERSION += $ver[1].substring($ver[1].length - 1, 1) + '.';
$env:MLPACK_VERSION += $ver[2].substring($ver[2].length - 1, 1);
if (Test-Path ${env:GIT_VERSION_FILE})
{
$ver = (Get-Content ${env:GIT_VERSION_FILE});
$env:INSTALL_VERSION = $ver.Split('"')[1].Split(' ')[1];
}
else
{
$env:INSTALL_VERSION = $env:MLPACK_VERSION;
}
- echo INSTALL_VERSION is %INSTALL_VERSION%
# Building MSI installer.
- cd C:\projects\mlpack\dist\win-installer\mlpack-win-installer
- >
heat dir ..\staging
-cg HeatGenerated
-dr INSTALLFOLDER
-sreg
-srd
-var var.HarvestPath
-ag
-sfrag
-out HeatGeneratedFileList.wxs
- >
candle -dHarvestPath=..\staging
-dConfiguration=Release
-dOutDir=bin\x64\Release\
-dPlatform=x64
-dProjectDir=.
-dProjectExt=.wixproj
-dProjectFileName=mlpack-win-installer.wixproj
-dProjectName=mlpack-win-installer
-dProjectPath=mlpack-win-installer.wixproj
-dTargetDir=.\bin\x64\Release\
-dTargetExt=.msi
-dTargetFileName=mlpack-windows.msi
-dTargetName=mlpack-windows
-dTargetPath=.\bin\x64\Release\mlpack-windows.msi
-out obj\x64\Release\
-arch x64
-ext "C:\Program Files (x86)\WiX Toolset v3.11\bin\\WixUIExtension.dll"
Product.wxs HeatGeneratedFileList.wxs
- >
light -out .\bin\x64\Release\mlpack-%INSTALL_VERSION%.msi
-pdbout .\bin\x64\Release\mlpack-windows.wixpdb
-cultures:null
-loc mlpack-localization.wxl
-ext "C:\Program Files (x86)\WiX Toolset v3.11\bin\\WixUIExtension.dll"
-contentsfile
obj\x64\Release\mlpack-win-installer.wixproj.BindContentsFileListnull.txt
-outputsfile
obj\x64\Release\mlpack-win-installer.wixproj.BindOutputsFileListnull.txt
-builtoutputsfile
obj\x64\Release\mlpack-win-installer.wixproj.BindBuiltOutputsFileListnull.txt
-wixprojectfile
mlpack-win-installer.wixproj
obj\x64\Release\Product.wixobj
obj\x64\Release\HeatGeneratedFileList.wixobj
artifacts:
- path: 'build\*.zip'
name: mlpack-windows-zip
- path: 'dist\win-installer\mlpack-win-installer\bin\x64\Release\*.msi'
name: mlpack-windows-installer
notifications:
- provider: Email
to:
- mlpack-git@lists.mlpack.org
on_build_success: true
on_build_failure: true
on_build_status_changed: true
cache:
- packages -> **\packages.config
- armadillo.tar.xz -> appveyor.yaml
-110
View File
@@ -1,110 +0,0 @@
# This module looks for mex, the MATLAB compiler.
# The following variables are defined when the script completes:
# MATLAB_MEX: location of mex compiler
# MATLAB_ROOT: root of MATLAB installation
# MATLABMEX_FOUND: 0 if not found, 1 if found
set(MATLABMEX_FOUND 0)
if(WIN32)
# This is untested but taken from the older FindMatlab.cmake script as well as
# the modifications by Ramon Casero and Tom Doel for Gerardus.
# Search for a version of Matlab available, starting from the most modern one
# to older versions.
foreach(MATVER "7.20" "7.19" "7.18" "7.17" "7.16" "7.15" "7.14" "7.13" "7.12"
"7.11" "7.10" "7.9" "7.8" "7.7" "7.6" "7.5" "7.4")
if((NOT DEFINED MATLAB_ROOT)
OR ("${MATLAB_ROOT}" STREQUAL "")
OR ("${MATLAB_ROOT}" STREQUAL "/registry"))
get_filename_component(MATLAB_ROOT
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\${MATVER};MATLABROOT]"
ABSOLUTE)
set(MATLAB_VERSION ${MATVER})
endif()
OR ("${MATLAB_ROOT}" STREQUAL "")
OR ("${MATLAB_ROOT}" STREQUAL "/registry"))
endforeach()
find_program(MATLAB_MEX
mex
${MATLAB_ROOT}/bin
)
else()
# Check if this is a Mac.
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
# This code is untested but taken from the older FindMatlab.cmake script as
# well as the modifications by Ramon Casero and Tom Doel for Gerardus.
set(LIBRARY_EXTENSION .dylib)
# If this is a Mac and the attempts to find MATLAB_ROOT have so far failed,~
# we look in the applications folder
if((NOT DEFINED MATLAB_ROOT) OR ("${MATLAB_ROOT}" STREQUAL ""))
# Search for a version of Matlab available, starting from the most modern
# one to older versions
foreach(MATVER "R2013b" "R2013a" "R2012b" "R2012a" "R2011b" "R2011a"
"R2010b" "R2010a" "R2009b" "R2009a" "R2008b")
if((NOT DEFINED MATLAB_ROOT) OR ("${MATLAB_ROOT}" STREQUAL ""))
if(EXISTS /Applications/MATLAB_${MATVER}.app)
set(MATLAB_ROOT /Applications/MATLAB_${MATVER}.app)
endif()
endif()
endforeach()
endif()
find_program(MATLAB_MEX
mex
PATHS
${MATLAB_ROOT}/bin
)
else()
# On a Linux system. The goal is to find MATLAB_ROOT.
set(LIBRARY_EXTENSION .so)
find_program(MATLAB_MEX_POSSIBLE_LINK
mex
PATHS
${MATLAB_ROOT}/bin
/opt/matlab/bin
/usr/local/matlab/bin
$ENV{HOME}/matlab/bin
# Now all the versions
/opt/matlab/[rR]20[0-9][0-9][abAB]/bin
/usr/local/matlab/[rR]20[0-9][0-9][abAB]/bin
/opt/matlab-[rR]20[0-9][0-9][abAB]/bin
/opt/matlab_[rR]20[0-9][0-9][abAB]/bin
/usr/local/matlab-[rR]20[0-9][0-9][abAB]/bin
/usr/local/matlab_[rR]20[0-9][0-9][abAB]/bin
$ENV{HOME}/matlab/[rR]20[0-9][0-9][abAB]/bin
$ENV{HOME}/matlab-[rR]20[0-9][0-9][abAB]/bin
$ENV{HOME}/matlab_[rR]20[0-9][0-9][abAB]/bin
)
get_filename_component(MATLAB_MEX "${MATLAB_MEX_POSSIBLE_LINK}" REALPATH)
get_filename_component(MATLAB_BIN_ROOT "${MATLAB_MEX}" PATH)
# Strip ./bin/.
get_filename_component(MATLAB_ROOT "${MATLAB_BIN_ROOT}" PATH)
endif()
endif()
if(NOT EXISTS "${MATLAB_MEX}" AND "${MatlabMex_FIND_REQUIRED}")
message(FATAL_ERROR "Could not find MATLAB mex compiler; try specifying MATLAB_ROOT.")
else()
if(EXISTS "${MATLAB_MEX}")
message(STATUS "Found MATLAB mex compiler: ${MATLAB_MEX}")
message(STATUS "MATLAB root: ${MATLAB_ROOT}")
set(MATLABMEX_FOUND 1)
endif()
endif()
mark_as_advanced(
MATLAB_MEX
MATLABMEX_FOUND
MATLAB_ROOT
)
+3 -3
View File
@@ -44,8 +44,8 @@ function(append_model SERIALIZATION_FILE PROGRAM_MAIN_FILE)
else ()
string(APPEND GOMODEL_SAFE_TYPE ${MODEL_CHAR})
endif()
endif()
endforeach()
endforeach()
endif()
# See if the model type already exists.
file(READ "${SERIALIZATION_FILE}" SERIALIZATION_FILE_CONTENTS)
@@ -77,7 +77,7 @@ function(append_model SERIALIZATION_FILE PROGRAM_MAIN_FILE)
" C.mlpackSet${MODEL_SAFE_TYPE}"
"Ptr(C.CString(identifier), (unsafe.Pointer)(ptr.mem))\n"
"}\n\n")
endif ()
endif()
endforeach ()
endif()
endfunction()
-135
View File
@@ -1,135 +0,0 @@
#!/bin/bash
# This script gets the test coverage for mlpack_test.
test_case="ALL"
gcov_loc=""
token=""
clean=true
current_log_file=`date +'%Y.%h.%d:%H:%M:%S-coverage.log'`
current_coverage_file=`date +'%Y.%h.%d:%H:%M:%S-coverage.info'`
max_cov_count=50000
# default directories
root_dir="../"
# Extract arguments.
for i in "$@"
do
case $i in
-h|--help)
echo "Usage: mlpack_coverage --help|-h"
echo " mlpack_coverage [-r=test_suite] [-g=gcov_tool_location]"
echo " [--token=coveralls_token]"
echo "Optional parameters:"
echo " -n|--no_test Do not run test before coverage computation"
echo " -r|--run_test Run tests with specific test suite"
echo " --no_clean Do not remove existing gcda file"
echo " -g|--gcov_tool_location Gcov location if not default"
echo " -t|--token Upload to coveralls with given token"
echo " --max_cov_count Max line coverage count (default 50000)"
echo " --root_dir Set the root directory from which gcov will be called. (default ../)"
exit 0
shift
;;
-n|--no_test)
test_case=""
shift
;;
-r=*|--run_test=*)
test_case="${i#*=}"
shift # past argument=value
;;
--no_clean)
clean=false
shift
;;
-g=*|--gcov_tool_location=*)
gcov_loc="${i#*=}"
shift # past argument=value
;;
-t=*|--token=*)
token="${i#*=}"
shift # past argument=value
;;
--max_cov_count)
max_cov_count="${i#*=}"
shift
;;
--root_dir=*)
root_dir="${i#*=}"
shift
;;
*)
# unknown option
;;
esac
done
if [ "$clean" = true ]; then
echo "Deleting existing coverage data..."
find ./ -name "*.gcda" -type f -delete
fi
# Initial pass.
echo "Generating primary coverage report."
[[ -d ./coveragehistory/ ]] || mkdir coveragehistory
lcov -b . -c -i -d ./ -o .coverage.wtest.base > ./coveragehistory/$current_log_file
# Run the tests.
if [ "$test_case" = "ALL" ]; then
echo "Running all the tests..."
"@CMAKE_BINARY_DIR@"/bin/mlpack_test
elif ! [ "$test_case" = "" ]; then
echo "Running test suite: $test_case"
"@CMAKE_BINARY_DIR@"/bin/mlpack_test --run_test=$test_case
fi
# Generate coverage based on executed tests.
echo "Computing coverage..."
if [ "$gcov_loc" = "" ];
then lcov -b . -c -d ./ -o .coverage.wtest.run >> ./coveragehistory/$current_log_file
else
lcov -b . -c -d ./ -o .coverage.wtest.run --gcov-tool=$gcov_loc >> ./coveragehistory/$current_log_file
fi
echo "Filtering coverage files..."
# Clear negative entries in coverage file
sed -E 's/-([0-9]+)/$max_cov_count/g' -i .coverage.wtest.run
# Merge coverage tracefiles.
lcov -a .coverage.wtest.base -a .coverage.wtest.run -o .coverage.total >> ./coveragehistory/$current_log_file
# Filtering, extracting project files.
lcov -e .coverage.total "@CMAKE_CURRENT_SOURCE_DIR@/src/mlpack/*" -o .coverage.total.filtered >> ./coveragehistory/$current_log_file
# Filtering, removing test-files and main.cpp.
lcov -r .coverage.total.filtered "@CMAKE_CURRENT_SOURCE_DIR@/src/mlpack/*/*_main.cpp" -o .coverage.total.filtered >> ./coveragehistory/$current_log_file
lcov -r .coverage.total.filtered "@CMAKE_CURRENT_SOURCE_DIR@/src/mlpack/tests/*" -o .coverage.total.filtered >> ./coveragehistory/$current_log_file
# Remove untestable files.
lcov -r .coverage.total.filtered "@CMAKE_CURRENT_SOURCE_DIR@/src/mlpack/core/util/gitversion.hpp" -o .coverage.total.filtered >> ./coveragehistory/$current_log_file
lcov -r .coverage.total.filtered "@CMAKE_CURRENT_SOURCE_DIR@/src/mlpack/core/util/arma_config.hpp" -o .coverage.total.filtered >> ./coveragehistory/$current_log_file
# Extra: Replace /build/ with /src/ to unify directories.
cat .coverage.total.filtered > .coverage.total
# Extra: Clear up previous data, create html folder.
if [[ -d ./coverage/ ]] ; then
rm -rf ./coverage/*
else
mkdir coverage
fi
# Step 9: Generate webpage.
genhtml -o ./coverage/ .coverage.total
# Extra: Preserve coverage file in coveragehistory folder.
coverage_file=$current_coverage_file
cp .coverage.total ./coveragehistory/$current_coverage_file
# Clean temporary coverage files.
#rm .coverage.*
# Upload the result to coveralls if token is provided.
if ! [ "$token" = "" ]; then
cpp-coveralls -n -r $root_dir -b $root_dir -l ./coveragehistory/$current_coverage_file -t "$token" --max-cov-count $max_cov_count
fi
-35
View File
@@ -9,7 +9,6 @@ include(CMake/CheckHash.cmake)
option(DEBUG "Compile with debugging information." OFF)
option(PROFILE "Compile with profiling information." OFF)
option(ARMA_EXTRA_DEBUG "Compile with extra Armadillo debugging symbols." OFF)
option(MATLAB_BINDINGS "Compile MATLAB bindings if MATLAB is found." OFF)
option(TEST_VERBOSE "Run test cases with verbose output." OFF)
option(BUILD_TESTS "Build tests." ON)
option(BUILD_CLI_EXECUTABLES "Build command-line executables." ON)
@@ -80,8 +79,6 @@ option(BUILD_R_BINDINGS "Build R bindings." OFF)
# generation.
option(BUILD_MARKDOWN_BINDINGS "Build Markdown bindings for website documentation." OFF)
option(BUILD_WITH_COVERAGE
"Build with support for code coverage tools (gcc only)." OFF)
option(MATHJAX
"Use MathJax for HTML Doxygen output (disabled by default)." OFF)
option(FORCE_CXX11
@@ -196,38 +193,6 @@ if(CMAKE_COMPILER_IS_GNUCC)
${CMAKE_THREAD_LIBS_INIT})
endif()
# Setup build for test coverage
if(BUILD_WITH_COVERAGE)
# Currently coverage only works with GNU g++.
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# Find gcov and lcov
find_program(GCOV gcov)
find_program(LCOV lcov)
if(NOT GCOV)
message(FATAL_ERROR
"gcov not found! gcov is required when BUILD_WITH_COVERAGE=ON.")
endif()
set(MLPACK_LIBRARIES ${MLPACK_LIBRARIES} "supc++")
set(MLPACK_LIBRARIES ${MLPACK_LIBRARIES} "quadmath")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage -fno-inline -fno-inline-small-functions -fno-default-inline -fprofile-arcs -fkeep-inline-functions")
message(STATUS "Adding debug compile options for code coverage.")
# Remove optimizations for better line coverage
set(DEBUG ON)
if(LCOV)
configure_file(CMake/mlpack_coverage.in mlpack_coverage @ONLY)
add_custom_target(mlpack_coverage DEPENDS mlpack_test COMMAND ${PROJECT_BINARY_DIR}/mlpack_coverage)
else()
message(WARNING "'lcov' not found; local coverage report is disabled. "
"Install 'lcov' and rerun cmake to generate local coverage report.")
endif()
else()
message(FATAL_ERROR "BUILD_WITH_COVERAGE can only work with GNU environment.")
endif()
endif()
# Debugging CFLAGS. Turn optimizations off; turn debugging symbols on.
if(DEBUG)
if (NOT MSVC)
+9
View File
@@ -1,5 +1,8 @@
### mlpack ?.?.?
###### ????-??-??
* Added warm start feature to Random Forest (#2881); this feature is
accessible from mlpack's bindings to different languages.
* Added Pixel Shuffle layer (#2563).
* Add "check_input_matrices" option to python bindings that checks
@@ -37,6 +40,12 @@
* Add `Lambda1()`, `Lambda2()`, `UseCholesky()`, and `Tolerance()` members to
`LARS` so parameters for training can be modified (#2861).
* Remove unused `ElemType` template parameter from `DecisionTree` and
`RandomForest` (#2874).
* Fix Python binding build when the CMake variable `USE_OPENMP` is set to
`OFF` (#2884).
### mlpack 3.4.2
###### 2020-10-26
* Added Mean Absolute Percentage Error.
+7 -8
View File
@@ -14,8 +14,7 @@ src="https://cdn.rawgit.com/mlpack/mlpack.org/e7d36ed8/mlpack-black.svg" style="
</h5>
<p align="center">
<a href="http://ci.mlpack.org/job/mlpack%20-%20git%20commit%20test/"><img src="https://img.shields.io/jenkins/build.svg?jobUrl=http%3A%2F%2Fci.mlpack.org%2Fjob%2Fmlpack%2520-%2520git%2520commit%2520test%2F&label=Linux%20build&style=flat-square" alt="Jenkins"></a>
<a href="https://coveralls.io/github/mlpack/mlpack?branch=master"><img src="https://img.shields.io/coveralls/mlpack/mlpack/master.svg?style=flat-square" alt="Coveralls"></a>
<a href="https://dev.azure.com/mlpack/mlpack/_build?definitionId=1"><img alt="Azure DevOps builds (job)" src="https://img.shields.io/azure-devops/build/mlpack/84320e87-76e3-4b6e-8b6e-3adaf6b36eed/1/master?job=Linux&label=Linux%20Build&style=flat-square"></a>
<a href="https://opensource.org/licenses/BSD-3-Clause"><img src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg?style=flat-square" alt="License"></a>
<a href="http://numfocus.org/donate-to-mlpack"><img src="https://img.shields.io/badge/sponsored%20by-NumFOCUS-orange.svg?style=flat-square&colorA=E1523D&colorB=007D8A" alt="NumFOCUS"></a>
</p>
@@ -142,10 +141,10 @@ If you are compiling Armadillo by hand, ensure that LAPACK and BLAS are enabled.
### 4. Building mlpack from source
This document discusses how to build mlpack from source. These build directions
This document discusses how to build mlpack from source. These build directions
will work for any Linux-like shell environment (for example Ubuntu, macOS,
FreeBSD etc). However, mlpack is in the repositories of many Linux distributions
and so it may be easier to use the package manager for your system. For example,
FreeBSD etc). However, mlpack is in the repositories of many Linux distributions
and so it may be easier to use the package manager for your system. For example,
on Ubuntu, you can install the mlpack library and command-line executables (e.g.
mlpack_pca, mlpack_kmeans etc.) with the following command:
@@ -183,7 +182,7 @@ sufficient.
The next step is to run CMake to configure the project. Running CMake is the
equivalent to running `./configure` with autotools. If you run CMake with no
options, it will configure the project to build with no debugging symbols and
options, it will configure the project to build with no debugging symbols and
no profiling information:
$ cmake ../
@@ -239,7 +238,7 @@ This will build all library components as well as 'mlpack_test'.
$ make
If you do not want to build everything in the library, individual components
If you do not want to build everything in the library, individual components
of the build can be specified:
$ make mlpack_pca mlpack_knn mlpack_kfn
@@ -252,7 +251,7 @@ and submit an issue. The mlpack developers will quickly help you figure it out:
Alternately, mlpack help can be found in IRC at `#mlpack` on chat.freenode.net.
If you wish to install mlpack to `/usr/local/include/mlpack/`, `/usr/local/lib/`,
and `/usr/local/bin/`, make sure you have root privileges (or write permissions
and `/usr/local/bin/`, make sure you have root privileges (or write permissions
to those three directories), and simply type
$ make install
-2
View File
@@ -191,8 +191,6 @@ The full list of options mlpack allows:
- DOWNLOAD_ENSMALLEN=(ON/OFF): If ensmallen is not found, download it
(default ON)
- DOWNLOAD_STB_IMAGE=(ON/OFF): If STB is not found, download it (default ON)
- BUILD_WITH_COVERAGE=(ON/OFF): Build with support for code coverage tools
(gcc only) (default OFF)
- PYTHON_EXECUTABLE=(/path/to/python_version): Path to specific Python executable
- PYTHON_INSTALL_PREFIX=(/path/to/python/): Path to root of Python installation
- JULIA_EXECUTABLE=(/path/to/julia): Path to specific Julia executable
@@ -46,6 +46,7 @@ A list of all the sections this tutorial contains.
- \ref cli_ex2_lrtut
- \ref cli_ex3_lrtut
- \ref cli_ex4_lrtut
- \ref cli_ex5_lrtut
- \ref linreg_lrtut
- \ref linreg_ex1_lrtut
- \ref linreg_ex2_lrtut
@@ -113,22 +114,23 @@ $ cat dataset.csv
4,4
$ cat lr.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="12">
<linearRegressionModel class_id="0" tracking_level="0" version="0">
<parameters class_id="1" tracking_level="0" version="0">
<n_rows>2</n_rows>
<n_cols>1</n_cols>
<n_elem>2</n_elem>
<vec_state>1</vec_state>
<item>-3.97205464519563669e-16</item>
<item>1.00000000000000022e+00</item>
</parameters>
<lambda>0.00000000000000000e+00</lambda>
<intercept>1</intercept>
</linearRegressionModel>
</boost_serialization>
<?xml version="1.0" encoding="utf-8"?>
<cereal>
<model>
<cereal_class_version>0</cereal_class_version>
<parameters>
<n_rows>2</n_rows>
<n_cols>1</n_cols>
<vec_state>1</vec_state>
<elem>0</elem>
<elem>1</elem>
</parameters>
<lambda>0</lambda>
<intercept>true</intercept>
</model>
</cereal>
@endcode
As you can see, the function for this input is \f$f(y)=0+1x_1\f$. We can see
@@ -141,36 +143,102 @@ dataset is one dimensional, and the last column has the \f$y\f$ values, or
responses, for each row. You can specify these responses in a separate file if
you want, using the \c --input_responses, or \c -r, option.
@subsection cli_ex2_lrtut Compute model and predict at the same time
@subsection cli_ex2_lrtut Train a multivariate linear regression model
Multivariate linear regression means that the response variable is predicted by
more than just one input variable. In this example we will try to fit a
multivariate linear regression model to data that contains four variables, stored in
\c dataset_2.csv.
@code
$ mlpack_linear_regression --training_file dataset.csv --test_file predict.csv \
$ cat dataset_2.csv
0,0,0,0,14
1,1,1,1,24
2,1,0,2,27
1,2,2,2,32
-1,-3,0,2,17
@endcode
Now let's run \c mlpack_linear_regression as usual:
@code
$ mlpack_linear_regression --training_file dataset_2.csv -v -M lr.xml
[INFO ] Loading 'dataset_2.csv' as CSV data. Size is 5 x 5.
[INFO ]
[INFO ] Execution parameters:
[INFO ] help: 0
[INFO ] info:
[INFO ] input_model_file:
[INFO ] lambda: 0
[INFO ] output_model_file: lr.xml
[INFO ] output_predictions_file:
[INFO ] test_file:
[INFO ] training_file: dataset_2.csv
[INFO ] training_responses_file:
[INFO ] verbose: 1
[INFO ] version: 0
[INFO ] Program timers:
[INFO ] load_regressors: 0.000060s
[INFO ] loading_data: 0.000050s
[INFO ] regression: 0.000049s
[INFO ] total_time: 0.000118s
$ cat lr.xml
<?xml version="1.0" encoding="utf-8"?>
<cereal>
<model>
<cereal_class_version>0</cereal_class_version>
<parameters>
<n_rows>5</n_rows>
<n_cols>1</n_cols>
<vec_state>1</vec_state>
<elem>14.00000000000002</elem>
<elem>1.9999999999999447</elem>
<elem>1.0000000000000431</elem>
<elem>2.9999999999999516</elem>
<elem>4.0000000000000249</elem>
</parameters>
<lambda>0</lambda>
<intercept>true</intercept>
</model>
</cereal>
@endcode
If we take a look at the \c lr.xml output we can see the \c \<parameters\> part has five elements which
the first corresponds to \f$\beta_0\f$ , the second corresponds to \f$\beta_1\f$ , and so on. This is equivalent
to \f$f(y) = \beta_0 + \beta_1x_1 + \beta_2x_2 + \beta_3x_3 + \beta_4x_4\f$ or \f$f(y)=14+2x_1+1x_2+3x_3+4x_4\f$.
@subsection cli_ex3_lrtut Compute model and predict at the same time
@code
$ mlpack_linear_regression --training_file dataset.csv --test_file predict.csv --output_predictions_file predictions.csv \
> -v
[WARN ] '--output_predictions_file (-o)' ignored because '--test_file (-T)' is specified!
[INFO ] Loading 'dataset.csv' as CSV data. Size is 2 x 5.
[INFO ] Loading 'predict.csv' as raw ASCII formatted data. Size is 1 x 3.
[INFO ] Saving CSV data to 'predictions.csv'.
[INFO ]
[INFO ]
[INFO ] Execution parameters:
[INFO ] help: false
[INFO ] info: ""
[INFO ] input_model_file: ""
[INFO ] help: 0
[INFO ] info:
[INFO ] input_model_file:
[INFO ] lambda: 0
[INFO ] output_model_file: ""
[INFO ] output_predictions: predictions.csv
[INFO ] test_file: predict.csv
[INFO ] training_file: dataset.csv
[INFO ] training_responses: ""
[INFO ] verbose: true
[INFO ] version: false
[INFO ]
[INFO ] output_model_file:
[INFO ] output_predictions_file: 'predictions.csv' (1x3 matrix)
[INFO ] test_file: 'predict.csv' (0x0 matrix)
[INFO ] training_file: 'dataset.csv' (0x0 matrix)
[INFO ] training_responses_file: ''
[INFO ] verbose: 1
[INFO ] version: 0
[INFO ] Program timers:
[INFO ] load_regressors: 0.000371s
[INFO ] load_test_points: 0.000229s
[INFO ] loading_data: 0.000491s
[INFO ] prediction: 0.000075s
[INFO ] regression: 0.000449s
[INFO ] saving_data: 0.000186s
[INFO ] total_time: 0.002731s
[INFO ] load_regressors: 0.000069s
[INFO ] load_test_points: 0.000031s
[INFO ] loading_data: 0.000079s
[INFO ] prediction: 0.000001s
[INFO ] regression: 0.000054s
[INFO ] saving_data: 0.000055s
[INFO ] total_time: 0.000203s
$ cat dataset.csv
0,0
@@ -195,51 +263,52 @@ about the \c predict.csv dataset is that it has the same dimensionality as the
dataset used to create the model, one. If the model generating dataset has
\f$d\f$ dimensions, so must the dataset we want to predict for.
@subsection cli_ex3_lrtut Prediction using a precomputed model
@subsection cli_ex4_lrtut Prediction using a precomputed model
@code
$ mlpack_linear_regression --input_model_file lr.xml --test_file predict.csv -v
$ mlpack_linear_regression --input_model_file lr.xml --test_file predict.csv --output_predictions_file predictions.csv -v
[WARN ] '--output_predictions_file (-o)' ignored because '--test_file (-T)' is specified!
[INFO ] Loading 'predict.csv' as raw ASCII formatted data. Size is 1 x 3.
[INFO ] Saving CSV data to 'predictions.csv'.
[INFO ]
[INFO ]
[INFO ] Execution parameters:
[INFO ] help: false
[INFO ] info: ""
[INFO ] help: 0
[INFO ] info:
[INFO ] input_model_file: lr.xml
[INFO ] lambda: 0
[INFO ] output_model_file: ""
[INFO ] output_predictions: predictions.csv
[INFO ] test_file: predict.csv
[INFO ] training_file: ""
[INFO ] training_responses: ""
[INFO ] verbose: true
[INFO ] version: false
[INFO ]
[INFO ] output_model_file:
[INFO ] output_predictions_file: 'predictions.csv' (1x3 matrix)
[INFO ] test_file: 'predict.csv' (0x0 matrix)
[INFO ] training_file: ''
[INFO ] training_responses_file: ''
[INFO ] verbose: 1
[INFO ] version: 0
[INFO ] Program timers:
[INFO ] load_model: 0.000264s
[INFO ] load_test_points: 0.000186s
[INFO ] loading_data: 0.000157s
[INFO ] prediction: 0.000098s
[INFO ] saving_data: 0.000157s
[INFO ] total_time: 0.001688s
[INFO ] load_model: 0.000051s
[INFO ] load_test_points: 0.000052s
[INFO ] loading_data: 0.000044s
[INFO ] prediction: 0.000010s
[INFO ] saving_data: 0.000079s
[INFO ] total_time: 0.000160s
$ cat lr.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="12">
<linearRegressionModel class_id="0" tracking_level="0" version="0">
<parameters class_id="1" tracking_level="0" version="0">
<n_rows>2</n_rows>
<n_cols>1</n_cols>
<n_elem>2</n_elem>
<vec_state>1</vec_state>
<item>-3.97205464519563669e-16</item>
<item>1.00000000000000022e+00</item>
</parameters>
<lambda>0.00000000000000000e+00</lambda>
<intercept>1</intercept>
</linearRegressionModel>
</boost_serialization>
<?xml version="1.0" encoding="utf-8"?>
<cereal>
<model>
<cereal_class_version>0</cereal_class_version>
<parameters>
<n_rows>2</n_rows>
<n_cols>1</n_cols>
<vec_state>1</vec_state>
<elem>0</elem>
<elem>1</elem>
</parameters>
<lambda>0</lambda>
<intercept>true</intercept>
</model>
</cereal>
$ cat predict.csv
2
@@ -252,7 +321,7 @@ $ cat predictions.csv
4.0000000000e+00
@endcode
@subsection cli_ex4_lrtut Using ridge regression
@subsection cli_ex5_lrtut Using ridge regression
Sometimes, the input matrix of predictors has a covariance matrix that is not
invertible, or the system is overdetermined. In this case, ridge regression is
@@ -153,19 +153,22 @@ the output shape is represented by the number of possible actions, which in this
(`foward` and `backward`).
We can also use mlpack's ann module to setup a custom FFN network. For example, here we use a single
hidden layer.
hidden layer. However, the Q-Learning agent expects the object to have a `ResetNoise` method which `SimpleDQN` has.
We can't pass mlpack's FFN network directly. Instead, we have to wrap it into `SimpleDQN` object.
@code
int main()
{
// Set up the network.
FFN<MeanSquaredError<>, GaussianInitialization> model(MeanSquaredError<>(),
FFN<MeanSquaredError<>, GaussianInitialization> network(MeanSquaredError<>(),
GaussianInitialization(0, 0.001));
model.Add<Linear<>>(4, 128);
model.Add<ReLULayer<>>();
model.Add<Linear<>>(128, 128);
model.Add<ReLULayer<>>();
model.Add<Linear<>>(128, 2);
network.Add<Linear<>>(4, 128);
network.Add<ReLULayer<>>();
network.Add<Linear<>>(128, 128);
network.Add<ReLULayer<>>();
network.Add<Linear<>>(128, 2);
SimpleDQN<> model(network);
@endcode
@@ -323,7 +326,7 @@ auto measure = [&returns, &position, &episode](double episodeReturn)
std::cout << "Episode No.: " << episode
<< "; Episode Return: " << episodeReturn
<< "; Average Return: " << arma::mean(returns) << endl;
<< "; Average Return: " << arma::mean(returns) << std::endl;
};
@endcode
@@ -389,7 +392,7 @@ int main()
std::cout << "Episode No.: " << episode
<< "; Episode Return: " << episodeReturn
<< "; Average Return: " << arma::mean(returns) << endl;
<< "; Average Return: " << arma::mean(returns) << std::endl;
};
for (int i = 0; i < 100; i++)
+5
View File
@@ -284,6 +284,11 @@ if (BUILD_R_BINDINGS)
DESTINATION
"${CMAKE_CURRENT_BINARY_DIR}/mlpack/")
file(COPY
"${CMAKE_CURRENT_SOURCE_DIR}/mlpack/configure"
DESTINATION
"${CMAKE_CURRENT_BINARY_DIR}/mlpack/")
# Do the actual build.
add_custom_target(r_build ALL)
@@ -20,6 +20,5 @@ Suggests: testthat (>= 2.1.0)
URL: https://www.mlpack.org/doc/mlpack-@PACKAGE_VERSION@/r_documentation.html,
https://github.com/mlpack/mlpack
BugReports: https://github.com/mlpack/mlpack/issues
LazyData: true
RoxygenNote: 7.1.0
Encoding: UTF-8
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
if test `uname` = "SunOS" ;
then
sed '1 s/$/ -ftrack-macro-expansion=0 -pipe --param ggc-min-expand=10 --param ggc-min-heapsize=8192/' ./src/Makevars > ./src/Makevars.tmp && cat ./src/Makevars.tmp > ./src/Makevars && rm ./src/Makevars.tmp
fi
exit 0
@@ -1,3 +1,3 @@
PKG_CXXFLAGS = -DBOOST_MATH_PROMOTE_DOUBLE_POLICY=false -I. $(SHLIB_OPENMP_CXXFLAGS)
PKG_CXXFLAGS = -DBOOST_MATH_PROMOTE_DOUBLE_POLICY=false -I. $(SHLIB_OPENMP_CXXFLAGS) -ftrack-macro-expansion=0 -pipe --param ggc-min-expand=10 --param ggc-min-heapsize=8192
PKG_LIBS = $(SHLIB_OPENMP_CXXFLAGS) $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)
CXX_STD = CXX11
@@ -15,7 +15,7 @@
#include <Rcpp.h>
// To suppress Found __assert_fail, possibly from assert (C).
// To suppress Found '__assert_fail', possibly from 'assert' (C).
#define BOOST_DISABLE_ASSERTS
// Rcpp has its own stream object which cooperates more nicely with R's i/o
@@ -98,8 +98,9 @@ std::string PrintTypeDoc(
"of the data is detected by the extension of the filename. The storage"
" should be such that one row corresponds to one point, and one column "
"corresponds to one dimension (this is the typical storage format for "
"on-disk data). All values of the matrix will be loaded as double-"
"precision floating point data.";
"on-disk data). CSV files will be checked for a header; if no header "
"is found, the first row will be loaded as a data point. All values of"
" the matrix will be loaded as double-precision floating point data.";
}
else if (std::is_same<T, arma::Mat<size_t>>::value)
{
@@ -111,8 +112,10 @@ std::string PrintTypeDoc(
"compiled with HDF5 support. The type of the data is detected by the "
"extension of the filename. The storage should be such that one row "
"corresponds to one point, and one column corresponds to one dimension "
"(this is the typical storage format for on-disk data). All values of "
"the matrix will be loaded as unsigned integers.";
"(this is the typical storage format for on-disk data). CSV files will"
" be checked for a header; if no header is found, the first row will be"
" loaded as a data point. All values of the matrix will be loaded as "
"unsigned integers.";
}
else if (std::is_same<T, arma::rowvec>::value ||
std::is_same<T, arma::vec>::value)
+5 -8
View File
@@ -52,18 +52,15 @@ if os.getenv('NO_BUILD') == '1':
else:
cxx_flags = '${CMAKE_CXX_FLAGS}'.strip()
cxx_flags = re.sub(' +', ' ', cxx_flags)
extra_args = ['-DBINDING_TYPE=BINDING_TYPE_PYX', '-std=c++11']
if '${OpenMP_CXX_FLAGS}' != '':
extra_args.append('${OpenMP_CXX_FLAGS}')
if cxx_flags:
extra_args = ['-DBINDING_TYPE=BINDING_TYPE_PYX',
'-std=c++11',
'${OpenMP_CXX_FLAGS}'] + cxx_flags.split(' ')
else:
extra_args = ['-DBINDING_TYPE=BINDING_TYPE_PYX',
'-std=c++11',
'${OpenMP_CXX_FLAGS}']
extra_args.extend(cxx_flags.split(' '))
# Extra options for MSVC compiler.
if platform.system() == 'Windows':
extra_args = extra_args + ['/MD', '/O2', '/Ob2', '/DNDEBUG']
extra_args.extend(['/MD', '/O2', '/Ob2', '/DNDEBUG'])
# This is used for parallel builds; CMake will set PYX_TO_BUILD accordingly.
if module is not None:
+1 -1
View File
@@ -1,4 +1,4 @@
/**
/**
* @file core.hpp
*
* Include all of the base components required to write mlpack methods, and the
+55 -2
View File
@@ -47,6 +47,10 @@ std::string GetStringType(const arma::file_type& type)
* from Armadillo's function guess_file_type_internal(), but we avoid using
* internal Armadillo functionality.
*
* If the file is detected as a CSV, and the CSV is detected to have a header
* row, the stream `f` will be fast-forwarded to point at the second line of the
* file.
*
* @param f Opened istream to look into to guess the file type.
*/
arma::file_type GuessFileType(std::istream& f)
@@ -114,6 +118,53 @@ arma::file_type GuessFileType(std::istream& f)
}
}
if (hasComma && (hasBracket == false))
{
// If we believe we have a CSV file, then we want to try to skip any header
// row. We'll detect a header row by simply seeing if anything in the first
// line doesn't parse as a number.
//
// TODO: this is not a foolproof algorithm, so there should eventually be a
// way added for the user to explicitly indicate that there is or isn't a
// header.
std::string firstLine;
std::getline(f, firstLine);
std::stringstream str(firstLine);
std::string token;
bool allNumeric = true;
// We'll abuse 'getline()' to split on commas.
while (std::getline(str, token, ','))
{
// Let's see if we can parse the token into a number.
double num;
std::string rest;
// Try to parse into a number.
std::stringstream s(token);
s >> num;
if (s.fail())
{
allNumeric = false;
break;
}
// Now check to see there isn't anything else. (This catches cases like,
// e.g., "1a".)
s >> rest;
if (rest.length() > 0)
{
allNumeric = false;
break;
}
}
// If we could parse everything into a number, then let's rewind `f` so that
// it's at the start of the file.
if (allNumeric)
f.seekg(pos1);
}
delete[] dataMem;
if (hasBinary)
@@ -131,12 +182,14 @@ arma::file_type GuessFileType(std::istream& f)
* necessary. (For instance, a .csv file could be delimited by spaces, commas,
* or tabs.) This is meant to be used during loading.
*
* If the file is detected as a CSV, and the CSV is detected to have a header
* row, `stream` will be fast-forwarded to point at the second line of the file.
*
* @param stream Opened file stream to look into for autodetection.
* @param filename Name of the file.
* @return The detected file type.
*/
arma::file_type AutoDetect(std::fstream& stream,
const std::string& filename)
arma::file_type AutoDetect(std::fstream& stream, const std::string& filename)
{
// Get the extension.
std::string extension = Extension(filename);
@@ -30,6 +30,10 @@ std::string GetStringType(const arma::file_type& type);
* from Armadillo's function guess_file_type_internal(), but we avoid using
* internal Armadillo functionality.
*
* If the file is detected as a CSV, and the CSV is detected to have a header
* row, the stream `f` will be fast-forwarded to point at the second line of the
* file.
*
* @param f Opened istream to look into to guess the file type.
*/
arma::file_type GuessFileType(std::istream& f);
@@ -40,6 +44,9 @@ arma::file_type GuessFileType(std::istream& f);
* necessary. (For instance, a .csv file could be delimited by spaces, commas,
* or tabs.) This is meant to be used during loading.
*
* If the file is detected as a CSV, and the CSV is detected to have a header
* row, `stream` will be fast-forwarded to point at the second line of the file.
*
* @param stream Opened file stream to look into for autodetection.
* @param filename Name of the file.
* @return The detected file type. arma::file_type_unknown if unknown.
+4
View File
@@ -48,6 +48,10 @@ namespace data /** Functions to load and save matrices and models. */ {
* `inputLoadType` parameter with the correct type above (e.g.
* `arma::csv_ascii`.)
*
* If the detected file type is CSV (`arma::csv_ascii`), the first row will be
* checked for a CSV header. If a CSV header is not detected, the first row
* will be treated as data; otherwise, the first row will be skipped.
*
* If the parameter 'fatal' is set to true, a std::runtime_error exception will
* be thrown if the matrix does not load successfully. The parameter
* 'transpose' controls whether or not the matrix is transposed after loading.
+328 -86
View File
@@ -18,11 +18,57 @@
namespace mlpack {
namespace data {
/**
* This helper function splits any `input` data into training and testing parts.
* In order to shuffle the input data before spliting, an array of shuffled
* indices of the input data is passed in the form of argument `order`.
*/
template<typename InputType>
void SplitHelper(const InputType& input,
InputType& train,
InputType& test,
const double testRatio,
const arma::uvec& order = arma::uvec())
{
const size_t testSize = static_cast<size_t>(input.n_cols * testRatio);
const size_t trainSize = input.n_cols - testSize;
// Initialising the sizes of outputs if not already initialized.
train.set_size(input.n_rows, trainSize);
test.set_size(input.n_rows, testSize);
// Shuffling and spliting simultaneously.
if (!order.is_empty())
{
if (trainSize > 0)
{
for (size_t i = 0; i < trainSize; ++i)
train.col(i) = input.col(order(i));
}
if (trainSize < input.n_cols)
{
for (size_t i = trainSize; i < input.n_cols; ++i)
test.col(i - trainSize) = input.col(order(i));
}
}
// Spliting only.
else
{
if (trainSize > 0)
train = input.cols(0, trainSize - 1);
if (trainSize < input.n_cols)
test = input.cols(trainSize, input.n_cols - 1);
}
}
/**
* Given an input dataset and labels, stratify into a training set and test set.
* It is recommended to have the input labels between the range [0, n) where n
* is the number of different labels. The NormalizeLabels() function in
* mlpack::data can be used for this.
* Expects labels to be of type arma::Row<> or arma::Col<>.
* Throws a runtime error if this is not the case.
* Example usage below. This overload places the stratified dataset into the
* four output parameters given (trainData, testData, trainLabel,
* and testLabel).
@@ -52,13 +98,14 @@ namespace data {
* @param shuffleData If true, the sample order is shuffled; otherwise, each
* sample is visited in linear order. (Default true.)
*/
template<typename T, typename U>
template<typename T, typename LabelsType,
typename = std::enable_if_t<arma::is_arma_type<LabelsType>::value> >
void StratifiedSplit(const arma::Mat<T>& input,
const arma::Row<U>& inputLabel,
const LabelsType& inputLabel,
arma::Mat<T>& trainData,
arma::Mat<T>& testData,
arma::Row<U>& trainLabel,
arma::Row<U>& testLabel,
LabelsType& trainLabel,
LabelsType& testLabel,
const double testRatio,
const bool shuffleData = true)
{
@@ -95,29 +142,24 @@ void StratifiedSplit(const arma::Mat<T>& input,
* 0
* 1 1
*/
const bool typeCheck = (arma::is_Row<LabelsType>::value)
|| (arma::is_Col<LabelsType>::value);
if (!typeCheck)
throw std::runtime_error("data::Split(): when stratified sampling is done, "
"labels must have type `arma::Row<>`!");
size_t trainIdx = 0;
size_t testIdx = 0;
size_t trainSize = 0;
size_t testSize = 0;
arma::uvec labelCounts;
arma::uvec testLabelCounts;
U maxLabel = inputLabel.max();
typename LabelsType::elem_type maxLabel = inputLabel.max();
labelCounts.zeros(maxLabel+1);
testLabelCounts.zeros(maxLabel+1);
arma::uvec order =
arma::linspace<arma::uvec>(0, input.n_cols - 1, input.n_cols);
if (shuffleData)
{
order = arma::shuffle(order);
}
for (U label : inputLabel)
{
for (typename LabelsType::elem_type label : inputLabel)
++labelCounts[label];
}
for (arma::uword labelCount : labelCounts)
{
@@ -127,24 +169,50 @@ void StratifiedSplit(const arma::Mat<T>& input,
trainData.set_size(input.n_rows, trainSize);
testData.set_size(input.n_rows, testSize);
trainLabel.set_size(trainSize);
testLabel.set_size(testSize);
trainLabel.set_size(inputLabel.n_rows, trainSize);
testLabel.set_size(inputLabel.n_rows, testSize);
for (arma::uword i : order)
if (shuffleData)
{
U label = inputLabel[i];
if (testLabelCounts[label] < floor(labelCounts[label] * testRatio))
arma::uvec order = arma::shuffle(
arma::linspace<arma::uvec>(0, input.n_cols - 1, input.n_cols));
for (arma::uword i : order)
{
testLabelCounts[label] += 1;
testData.col(testIdx) = input.col(i);
testLabel[testIdx] = inputLabel[i];
testIdx += 1;
typename LabelsType::elem_type label = inputLabel[i];
if (testLabelCounts[label] < floor(labelCounts[label] * testRatio))
{
testLabelCounts[label] += 1;
testData.col(testIdx) = input.col(i);
testLabel[testIdx] = inputLabel[i];
testIdx += 1;
}
else
{
trainData.col(trainIdx) = input.col(i);
trainLabel[trainIdx] = inputLabel[i];
trainIdx += 1;
}
}
else
}
else
{
for (arma::uword i = 0; i < input.n_cols; i++)
{
trainData.col(trainIdx) = input.col(i);
trainLabel[trainIdx] = inputLabel[i];
trainIdx += 1;
typename LabelsType::elem_type label = inputLabel[i];
if (testLabelCounts[label] < floor(labelCounts[label] * testRatio))
{
testLabelCounts[label] += 1;
testData.col(testIdx) = input.col(i);
testLabel[testIdx] = inputLabel[i];
testIdx += 1;
}
else
{
trainData.col(trainIdx) = input.col(i);
trainLabel[trainIdx] = inputLabel[i];
trainIdx += 1;
}
}
}
}
@@ -169,6 +237,9 @@ void StratifiedSplit(const arma::Mat<T>& input,
* testData, trainLabel, testLabel, 0.3);
* @endcode
*
* @tparam T Type of the elements of the input matrix.
* @tparam LabelsType Type of input labels. It can be arma::Mat, arma::Row,
* arma::Cube or arma::SpMat.
* @param input Input dataset to split.
* @param inputLabel Input labels to split.
* @param trainData Matrix to store training data into.
@@ -179,50 +250,28 @@ void StratifiedSplit(const arma::Mat<T>& input,
* @param shuffleData If true, the sample order is shuffled; otherwise, each
* sample is visited in linear order. (Default true.)
*/
template<typename T, typename U>
template<typename T, typename LabelsType,
typename = std::enable_if_t<arma::is_arma_type<LabelsType>::value> >
void Split(const arma::Mat<T>& input,
const arma::Row<U>& inputLabel,
const LabelsType& inputLabel,
arma::Mat<T>& trainData,
arma::Mat<T>& testData,
arma::Row<U>& trainLabel,
arma::Row<U>& testLabel,
LabelsType& trainLabel,
LabelsType& testLabel,
const double testRatio,
const bool shuffleData = true)
{
const size_t testSize = static_cast<size_t>(input.n_cols * testRatio);
const size_t trainSize = input.n_cols - testSize;
trainData.set_size(input.n_rows, trainSize);
testData.set_size(input.n_rows, testSize);
trainLabel.set_size(trainSize);
testLabel.set_size(testSize);
if (shuffleData)
{
arma::uvec order = arma::shuffle(arma::linspace<arma::uvec>(
0, input.n_cols - 1, input.n_cols));
if (trainSize > 0)
{
trainData = input.cols(order.subvec(0, trainSize - 1));
trainLabel = inputLabel.cols(order.subvec(0, trainSize - 1));
}
if (trainSize < input.n_cols)
{
testData = input.cols(order.subvec(trainSize, input.n_cols - 1));
testLabel = inputLabel.cols(order.subvec(trainSize, input.n_cols - 1));
}
arma::uvec order = arma::shuffle(arma::linspace<arma::uvec>(0,
input.n_cols - 1, input.n_cols));
SplitHelper(input, trainData, testData, testRatio, order);
SplitHelper(inputLabel, trainLabel, testLabel, testRatio, order);
}
else
{
if (trainSize > 0)
{
trainData = input.cols(0, trainSize - 1);
trainLabel = inputLabel.subvec(0, trainSize - 1);
}
if (trainSize < input.n_cols)
{
testData = input.cols(trainSize , input.n_cols - 1);
testLabel = inputLabel.subvec(trainSize , input.n_cols - 1);
}
SplitHelper(input, trainData, testData, testRatio);
SplitHelper(inputLabel, trainLabel, testLabel, testRatio);
}
}
@@ -256,29 +305,15 @@ void Split(const arma::Mat<T>& input,
const double testRatio,
const bool shuffleData = true)
{
const size_t testSize = static_cast<size_t>(input.n_cols * testRatio);
const size_t trainSize = input.n_cols - testSize;
trainData.set_size(input.n_rows, trainSize);
testData.set_size(input.n_rows, testSize);
if (shuffleData)
{
arma::uvec order = arma::shuffle(arma::linspace<arma::uvec>(
0, input.n_cols - 1, input.n_cols));
if (trainSize > 0)
trainData = input.cols(order.subvec(0, trainSize - 1));
if (trainSize < input.n_cols)
testData = input.cols(order.subvec(trainSize, input.n_cols - 1));
arma::uvec order = arma::shuffle(arma::linspace<arma::uvec>(0,
input.n_cols - 1, input.n_cols));
SplitHelper(input, trainData, testData, testRatio, order);
}
else
{
if (trainSize > 0)
trainData = input.cols(0, trainSize - 1);
if (trainSize < input.n_cols)
testData = input.cols(trainSize , input.n_cols - 1);
SplitHelper(input, trainData, testData, testRatio);
}
}
@@ -295,6 +330,9 @@ void Split(const arma::Mat<T>& input,
* auto splitResult = Split(input, label, 0.2);
* @endcode
*
* @tparam T Type of the elements of the input matrix.
* @tparam LabelsType Type of input labels. It can be arma::Mat, arma::Row,
* arma::Cube or arma::SpMat.
* @param input Input dataset to split.
* @param inputLabel Input labels to split.
* @param testRatio Percentage of dataset to use for test set (between 0 and 1).
@@ -302,22 +340,24 @@ void Split(const arma::Mat<T>& input,
* sample is visited in linear order. (Default true).
* @param stratifyData If true, the train and test splits are stratified
* so that the ratio of each class in the training and test sets is the same
* as in the original dataset.
* as in the original dataset. Expects labels to be of type arma::Row<> or
* arma::Col<>.
* @return std::tuple containing trainData (arma::Mat<T>), testData
* (arma::Mat<T>), trainLabel (arma::Row<U>), and testLabel (arma::Row<U>).
*/
template<typename T, typename U>
std::tuple<arma::Mat<T>, arma::Mat<T>, arma::Row<U>, arma::Row<U>>
template<typename T, typename LabelsType,
typename = std::enable_if_t<arma::is_arma_type<LabelsType>::value> >
std::tuple<arma::Mat<T>, arma::Mat<T>, LabelsType, LabelsType>
Split(const arma::Mat<T>& input,
const arma::Row<U>& inputLabel,
const LabelsType& inputLabel,
const double testRatio,
const bool shuffleData = true,
const bool stratifyData = false)
{
arma::Mat<T> trainData;
arma::Mat<T> testData;
arma::Row<U> trainLabel;
arma::Row<U> testLabel;
LabelsType trainLabel;
LabelsType testLabel;
if (stratifyData)
{
@@ -368,6 +408,208 @@ Split(const arma::Mat<T>& input,
std::move(testData));
}
/**
* Given an input dataset and labels, split into a training set and test set.
* Example usage below. This overload places the split dataset into the four
* output parameters given (trainData, testData, trainLabel, and testLabel).
*
* The input dataset must be of type arma::field. It should have the shape -
* (n_rows = 1, n_cols = Number of samples, n_slices = 1).
*
* NOTE: Here FieldType could be arma::field<arma::mat> or arma::field<arma::vec>.
*
* @code
* arma::field<arma::mat> input = loadData();
* arma::field<arma::vec> label = loadLabel();
* arma::field<arma::mat> trainData;
* arma::field<arma::mat> testData;
* arma::field<arma::vec> trainLabel;
* arma::field<arma::vec> testLabel;
* math::RandomSeed(100); // Set the seed if you like.
*
* // Split the dataset into a training and test set, with 30% of the data being
* // held out for the test set.
* Split(input, label, trainData, testData, trainLabel, testLabel, 0.3);
* @endcode
*
* @param input Input dataset to split.
* @param inputLabel Input labels to split.
* @param trainData FieldType to store training data into.
* @param testData FieldType test data into.
* @param trainLabel Field vector to store training labels into.
* @param testLabel Field vector to store test labels into.
* @param testRatio Percentage of dataset to use for test set (between 0 and 1).
* @param shuffleData If true, the sample order is shuffled; otherwise, each
* sample is visited in linear order. (Default true.)
*/
template <typename FieldType, typename T,
typename = std::enable_if_t<
arma::is_Col<typename FieldType::object_type>::value ||
arma::is_Mat_only<typename FieldType::object_type>::value>>
void Split(const FieldType& input,
const arma::field<T>& inputLabel,
FieldType& trainData,
arma::field<T>& trainLabel,
FieldType& testData,
arma::field<T>& testLabel,
const double testRatio,
const bool shuffleData = true)
{
if (shuffleData)
{
arma::uvec order = arma::shuffle(arma::linspace<arma::uvec>(0,
input.n_cols - 1, input.n_cols));
SplitHelper(input, trainData, testData, testRatio, order);
SplitHelper(inputLabel, trainLabel, testLabel, testRatio, order);
}
else
{
SplitHelper(input, trainData, testData, testRatio);
SplitHelper(inputLabel, trainLabel, testLabel, testRatio);
}
}
/**
* Given an input dataset, split into a training set and test set.
* Example usage below. This overload places the split dataset into the two
* output parameters given (trainData, testData).
*
* The input dataset must be of type arma::field. It should have the shape -
* (n_rows = 1, n_cols = Number of samples, n_slices = 1)
*
* NOTE: Here FieldType could be arma::field<arma::mat> or arma::field<arma::vec>
*
* @code
* arma::field<arma::mat> input = loadData();
* arma::field<arma::mat> trainData;
* arma::field<arma::mat> testData;
* math::RandomSeed(100); // Set the seed if you like.
*
* // Split the dataset into a training and test set, with 30% of the data being
* // held out for the test set.
* Split(input, trainData, testData, 0.3);
* @endcode
*
* @param input Input dataset to split.
* @param trainData FieldType to store training data into.
* @param testData FieldType test data into.
* @param testRatio Percentage of dataset to use for test set (between 0 and 1).
* @param shuffleData If true, the sample order is shuffled; otherwise, each
* sample is visited in linear order. (Default true).
*/
template <class FieldType,
class = std::enable_if_t<
arma::is_Col<typename FieldType::object_type>::value ||
arma::is_Mat_only<typename FieldType::object_type>::value>>
void Split(const FieldType& input,
FieldType& trainData,
FieldType& testData,
const double testRatio,
const bool shuffleData = true)
{
if (shuffleData)
{
arma::uvec order = arma::shuffle(arma::linspace<arma::uvec>(0,
input.n_cols - 1, input.n_cols));
SplitHelper(input, trainData, testData, testRatio, order);
}
else
{
SplitHelper(input, trainData, testData, testRatio);
}
}
/**
* Given an input dataset and labels, split into a training set and test set.
* Example usage below. This overload returns the split dataset as a std::tuple
* with four elements: an FieldType containing the training data, an
* FieldType containing the test data, an arma::field<arma::vec> containing the
* training labels, and an arma::field<arma::vec> containing the test labels.
*
* The input dataset must be of type arma::field. It should have the shape -
* (n_rows = 1, n_cols = Number of samples, n_slices = 1)
*
* NOTE: Here FieldType could be arma::field<arma::mat> or arma::field<arma::vec>
*
* @code
* arma::field<arma::mat> input = loadData();
* arma::field<arma::vec> label = loadLabel();
* auto splitResult = Split(input, label, 0.2);
* @endcode
*
* @param input Input dataset to split.
* @param inputLabel Input labels to split.
* @param testRatio Percentage of dataset to use for test set (between 0 and 1).
* @param shuffleData If true, the sample order is shuffled; otherwise, each
* sample is visited in linear order. (Default true).
* @return std::tuple containing trainData (FieldType), testData
* (FieldType), trainLabel (arma::field<arma::vec>), and
* testLabel (arma::field<arma::vec>).
*/
template <class FieldType, typename T,
class = std::enable_if_t<
arma::is_Col<typename FieldType::object_type>::value ||
arma::is_Mat_only<typename FieldType::object_type>::value>>
std::tuple<FieldType, FieldType, arma::field<T>, arma::field<T>>
Split(const FieldType& input,
const arma::field<T>& inputLabel,
const double testRatio,
const bool shuffleData = true)
{
FieldType trainData;
FieldType testData;
arma::field<T> trainLabel;
arma::field<T> testLabel;
Split(input, inputLabel, trainData, trainLabel, testData, testLabel,
testRatio, shuffleData);
return std::make_tuple(std::move(trainData),
std::move(testData),
std::move(trainLabel),
std::move(testLabel));
}
/**
* Given an input dataset, split into a training set and test set.
* Example usage below. This overload returns the split dataset as a std::tuple
* with two elements: an FieldType containing the training data and an
* FieldType containing the test data.
*
* The input dataset must be of type arma::field. It should have the shape -
* (n_rows = 1, n_cols = Number of samples, n_slices = 1)
*
* NOTE: Here FieldType could be arma::field<arma::mat> or arma::field<arma::vec>
*
* @code
* arma::field<arma::mat> input = loadData();
* auto splitResult = Split(input, 0.2);
* @endcode
*
* @param input Input dataset to split.
* @param testRatio Percentage of dataset to use for test set (between 0 and 1).
* @param shuffleData If true, the sample order is shuffled; otherwise, each
* sample is visited in linear order. (Default true).
* @return std::tuple containing trainData (FieldType)
* and testData (FieldType).
*/
template <class FieldType,
class = std::enable_if_t<
arma::is_Col<typename FieldType::object_type>::value ||
arma::is_Mat_only<typename FieldType::object_type>::value>>
std::tuple<FieldType, FieldType>
Split(const FieldType& input,
const double testRatio,
const bool shuffleData = true)
{
FieldType trainData;
FieldType testData;
Split(input, trainData, testData, testRatio, shuffleData);
return std::make_tuple(std::move(trainData),
std::move(testData));
}
} // namespace data
} // namespace mlpack
@@ -90,11 +90,11 @@ class GaussianDistribution
*/
void Probability(const arma::mat& x, arma::vec& probabilities) const
{
probabilities.set_size(x.n_cols);
for (size_t i = 0; i < x.n_cols; ++i)
{
probabilities(i) = Probability(x.unsafe_col(i));
}
// Use LogProbability(), then transform the log-probabilities out of
// logspace.
arma::vec logProbs;
LogProbability(x, logProbs);
probabilities = arma::exp(logProbs);
}
/**
@@ -110,17 +110,11 @@ class GaussianDistribution
// Column i of 'diffs' is the difference between x.col(i) and the mean.
arma::mat diffs = x;
diffs.each_col() -= mean;
// Now, we only want to calculate the diagonal elements of (diffs' * cov^-1
// * diffs). We just don't need any of the other elements. We can
// calculate the right hand part of the equation (instead of the left side)
// so that later we are referencing columns, not rows -- that is faster.
const arma::mat rhs = -0.5 * invCov * diffs;
arma::vec logExponents(diffs.n_cols); // We will now fill this.
for (size_t i = 0; i < diffs.n_cols; ++i)
logExponents(i) = accu(diffs.unsafe_col(i) % rhs.unsafe_col(i));
// Now, we only want to calculate the diagonal elements of (diffs' * cov^-1
// * diffs). We just don't need any of the other elements.
logProbabilities = -0.5 * x.n_rows * log2pi - 0.5 * logDetCov +
logExponents;
sum(diffs % (-0.5 * invCov * diffs), 0).t();
}
/**
+33 -1
View File
@@ -28,7 +28,7 @@ template<typename T>
T LogAdd(T x, T y);
/**
* Sum a vector of log values. (T should be an Armadillo type.)
* Log-sum a vector of log values. (T should be an Armadillo type.)
*
* @param x vector of log values
* @return log(e^x0 + e^x1 + ...)
@@ -36,6 +36,38 @@ T LogAdd(T x, T y);
template<typename T>
typename T::elem_type AccuLog(const T& x);
/**
* Compute the sum of exponentials of each element in each column, then compute
* the log of that. If InPlace is true, then the values of `y` will also be
* added to the sum.
*
* That is, if InPlace is false, then this method will set `y` such that:
*
* `y_i = log(sum(exp(x.col(i))))`
*
* and if InPlace is true, then `y` will be set such that:
*
* `y_i = log(sum(exp(x.col(i))) + exp(y_i))`.
*/
template<typename T, bool InPlace = false>
void LogSumExp(const T& x, arma::Col<typename T::elem_type>& y);
/**
* Compute the sum of exponentials of each element in each row, then compute the
* log of that. If InPlace is true, then the values of `y` will also be added
* to the sum.
*
* That is, if InPlace is false, then this method will set `y` such that:
*
* `y_i = log(sum(exp(x.row(i))))`
*
* and if InPlace is true, then `y` will be set such that:
*
* `y_i = log(sum(exp(x.row(i))) + exp(y_i))`.
*/
template<typename T, bool InPlace = false>
void LogSumExpT(const T& x, arma::Col<typename T::elem_type>& y);
} // namespace math
} // namespace mlpack
+71 -7
View File
@@ -47,8 +47,10 @@ T LogAdd(T x, T y)
r = y;
}
return (r == -std::numeric_limits<T>::infinity() ||
d == -std::numeric_limits<T>::infinity()) ? r : r + log(1 + exp(d));
if (std::isinf(d) || std::isinf(r))
return r;
return r + log(1 + exp(d));
}
/**
@@ -60,15 +62,77 @@ T LogAdd(T x, T y)
template<typename T>
typename T::elem_type AccuLog(const T& x)
{
typename T::elem_type sum =
-std::numeric_limits<typename T::elem_type>::infinity();
typename T::elem_type maxVal = max(x);
if (maxVal == -std::numeric_limits<typename T::elem_type>::infinity())
return maxVal;
for (size_t i = 0; i < x.n_elem; ++i)
return maxVal + log(sum(exp(x - maxVal)));;
}
/**
* Compute the sum of exponentials of each element in each column, then compute
* the log of that. If InPlace is true, then the values of `y` will also be
* added to the sum.
*/
template<typename T, bool InPlace>
void LogSumExp(const T& x, arma::Col<typename T::elem_type>& y)
{
arma::Col<typename T::elem_type> maxs;
if (InPlace)
{
sum = LogAdd(sum, x[i]);
// Compute the maximum in each column (treating y as a column too).
maxs = max(max(x, 1), y);
y = maxs + log(sum(exp(x - repmat(maxs, 1, x.n_cols)), 1) +
exp(y - maxs));
}
else
{
// Compute the maximum element in each column.
maxs = max(x, 1);
y = maxs + log(sum(exp(x - repmat(maxs, 1, x.n_cols)), 1));
}
return sum;
if (maxs.has_inf())
{
y.replace(-std::numeric_limits<typename T::elem_type>::quiet_NaN(),
-std::numeric_limits<typename T::elem_type>::infinity());
}
}
/**
* Compute the sum of exponentials of each element in each row, then compute the
* log of that. If InPlace is true, then the values of `y` will also be added
* to the sum.
*/
template<typename T, bool InPlace>
void LogSumExpT(const T& x, arma::Col<typename T::elem_type>& y)
{
arma::Row<typename T::elem_type> maxs;
if (InPlace)
{
// Compute the maximum element in each column.
maxs = max(max(x, 0), y.t());
y = maxs.t() + log(sum(exp(x - repmat(maxs, x.n_rows, 1)), 0) +
exp(y.t() - maxs)).t();
}
else
{
// Compute the maximum element in each column.
arma::Row<typename T::elem_type> maxs = max(x, 0);
y = (maxs + log(sum(exp(x - repmat(maxs, x.n_rows, 1)), 0))).t();
}
if (maxs.has_inf())
{
y.replace(-std::numeric_limits<typename T::elem_type>::quiet_NaN(),
-std::numeric_limits<typename T::elem_type>::infinity());
}
}
} // namespace math
+1 -1
View File
@@ -42,7 +42,7 @@ inline void RandomSeed(const size_t seed)
#if (!defined(BINDING_TYPE) || BINDING_TYPE != BINDING_TYPE_TEST)
randGen.seed((uint32_t) seed);
#if (BINDING_TYPE == BINDING_TYPE_R)
// To suppress Found srand, possibly from srand (C).
// To suppress Found 'srand', possibly from 'srand' (C).
(void) seed;
#else
srand((unsigned int) seed);
+9 -9
View File
@@ -129,23 +129,23 @@ class Timers
void PrintTimer(const std::string& timerName);
/**
 * Initializes a timer, available like a normal value specified on
 * the command line.  Timers are of type timeval. If a timer is started, then
* Initializes a timer, available like a normal value specified on
* the command line. Timers are of type timeval. If a timer is started, then
* stopped, then re-started, then stopped, the final timer value will be the
* length of both runs of the timer.
 *
 * @param timerName The name of the timer in question.
*
* @param timerName The name of the timer in question.
* @param threadId Id of the thread accessing the timer.
 */
*/
void StartTimer(const std::string& timerName,
const std::thread::id& threadId = std::thread::id());
/**
 * Halts the timer, and replaces its value with the delta time from its start.
  *
  * @param timerName The name of the timer in question.
* Halts the timer, and replaces its value with the delta time from its start.
*
* @param timerName The name of the timer in question.
* @param threadId Id of the thread accessing the timer.
 */
*/
void StopTimer(const std::string& timerName,
const std::thread::id& threadId = std::thread::id());
+4 -4
View File
@@ -16,10 +16,10 @@ namespace mlpack {
namespace util {
/**
 * Convert a string to lowercase letters.
 *
 * @param input The string to convert.
 */
* Convert a string to lowercase letters.
*
* @param input The string to convert.
*/
inline std::string ToLower(const std::string& input)
{
std::string output;
@@ -31,7 +31,7 @@ namespace amf {
* }
* @endcode
*
* This is a multiplicative rule that ensures that the KullbackLeibler
* This is a multiplicative rule that ensures that the Kullback-Leibler
* divergence
*
* \f[
@@ -20,6 +20,7 @@ set(SOURCES
poisson1_function.hpp
gaussian_function.hpp
hard_swish_function.hpp
tanh_exponential_function.hpp
)
# Add directory name to sources.
@@ -0,0 +1,96 @@
/**
* @file methods/ann/activation_functions/tanh_exponential_function.hpp
* @author Mayank Raj
*
* Definition and implementation of the Tanh exponential function.
*
* For more information see the following paper
*
* @code
* @misc{The Institution of Engineering and Technology 2015 ,
* title = {TanhExp: A Smooth Activation Function with High Convergence Speed for Lightweight Neural Networks},
* author = {Xinyu Liu and Xiaoguang Di},
* year = {2020},
* url = {https://arxiv.org/pdf/2003.09855v2.pdf},
* eprint = {2003.09855v2},
* archivePrefix = {arXiv},
* primaryClass = {cs.LG} }
* @endcode
*
* mlpack is free software; you may redistribute it and/or modify it under the
* terms of the 3-clause BSD license. You should have received a copy of the
* 3-clause BSD license along with mlpack. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef MLPACK_METHODS_ANN_ACTIVATION_FUNCTIONS_TANH_EXPONENTIAL_FUNCTION_HPP
#define MLPACK_METHODS_ANN_ACTIVATION_FUNCTIONS_TANH_EXPONENTIAL_FUNCTION_HPP
#include <mlpack/prereqs.hpp>
namespace mlpack {
namespace ann /** Artificial Neural Network. */ {
/**
* The TanhExp function, defined by
*
* @f{eqnarray*}{
* f(x) = x * tanh(e^x)\\
* f'(x) = tanh(e^x) - x*e^x*(tanh(e^x)^2 - 1)\\
* @f}
*/
class TanhExpFunction
{
public:
/**
* Computes the TanhExp function.
*
* @param x Input data.
* @return f(x).
*/
static double Fn(const double x)
{
return x * std::tanh(std::exp(x));
}
/**
* Computes the TanhExp function.
*
* @param x Input data.
* @param y The resulting output activation.
*/
template<typename InputVecType, typename OutputVecType>
static void Fn(const InputVecType& x, OutputVecType& y)
{
y = x % arma::tanh(arma::exp(x));
}
/**
* Computes the first derivative of the TanhExp function.
*
* @param y Input activation.
* @return f'(x)
*/
static double Deriv(const double y)
{
return std::tanh(std::exp(y)) - y * std::exp(y) *
(std::pow(std::tanh(std::exp(y)), 2) - 1);
}
/**
* Computes the first derivatives of the tanh function.
*
* @param y Input activations.
* @param x The resulting derivatives.
*/
template<typename InputVecType, typename OutputVecType>
static void Deriv(const InputVecType& y, OutputVecType& x)
{
x = arma::tanh(arma::exp(y)) - y % arma::exp(y) %
(arma::pow(arma::tanh(arma::exp(y)), 2) - 1);
}
}; // class TanhExpFunction
} // namespace ann
} // namespace mlpack
#endif
@@ -28,6 +28,7 @@
#include <mlpack/methods/ann/activation_functions/elish_function.hpp>
#include <mlpack/methods/ann/activation_functions/gaussian_function.hpp>
#include <mlpack/methods/ann/activation_functions/hard_swish_function.hpp>
#include <mlpack/methods/ann/activation_functions/tanh_exponential_function.hpp>
namespace mlpack {
namespace ann /** Artificial Neural Network. */ {
@@ -52,6 +53,7 @@ namespace ann /** Artificial Neural Network. */ {
* - ElliotLayer
* - GaussianLayer
* - HardSwishLayer
* - TanhExpLayer
*
* @tparam ActivationFunction Activation function used for the embedding layer.
* @tparam InputDataType Type of the input data (arma::colvec, arma::mat,
@@ -290,6 +292,17 @@ template <
using HardSwishFunctionLayer = BaseLayer<
ActivationFunction, InputDataType, OutputDataType>;
/**
* Standard TanhExp-Layer using the TanhExp activation function.
*/
template <
class ActivationFunction = TanhExpFunction,
typename InputDataType = arma::mat,
typename OutputDataType = arma::mat
>
using TanhExpFunctionLayer = BaseLayer<
ActivationFunction, InputDataType, OutputDataType>;
} // namespace ann
} // namespace mlpack
@@ -79,6 +79,12 @@ class Constant
//! Get the output size.
size_t OutSize() const { return outSize; }
//! Get the size of the weights.
size_t WeightSize() const
{
return 0;
}
/**
* Serialize the layer.
*/
@@ -123,6 +123,12 @@ class LinearNoBias
//! Modify the gradient.
OutputDataType& Gradient() { return gradient; }
//! Get the size of the weights.
size_t WeightSize() const
{
return inSize * outSize;
}
//! Get the shape of the input.
size_t InputShape() const
{
@@ -38,7 +38,7 @@ LinearNoBias<InputDataType, OutputDataType, RegularizerType>::LinearNoBias(
outSize(outSize),
regularizer(regularizer)
{
weights.set_size(outSize * inSize, 1);
weights.set_size(WeightSize(), 1);
}
template<typename InputDataType, typename OutputDataType,
+35 -16
View File
@@ -166,9 +166,17 @@ class MeanPooling
for (size_t i = 0, rowidx = 0; i < output.n_rows;
++i, rowidx += strideWidth)
{
size_t rowEnd = rowidx + kernelWidth - 1;
size_t colEnd = colidx + kernelHeight - 1;
if (rowEnd > input.n_rows - 1)
rowEnd = input.n_rows - 1;
if (colEnd > input.n_cols - 1)
colEnd = input.n_cols - 1;
arma::mat subInput = input(
arma::span(rowidx, rowidx + kernelWidth - 1 - offset),
arma::span(colidx, colidx + kernelHeight - 1 - offset));
arma::span(rowidx, rowEnd),
arma::span(colidx, colEnd));
output(i, j) = arma::mean(arma::mean(subInput));
}
@@ -186,22 +194,36 @@ class MeanPooling
const arma::Mat<eT>& error,
arma::Mat<eT>& output)
{
const size_t rStep = input.n_rows / error.n_rows - offset;
const size_t cStep = input.n_cols / error.n_cols - offset;
arma::Mat<eT> unpooledError;
for (size_t j = 0; j < input.n_cols - cStep; j += cStep)
for (size_t j = 0, colidx = 0; j < input.n_cols; j += strideHeight, colidx++)
{
for (size_t i = 0; i < input.n_rows - rStep; i += rStep)
{
const arma::Mat<eT>& inputArea = input(arma::span(i, i + rStep - 1),
arma::span(j, j + cStep - 1));
for (size_t i = 0, rowidx = 0; i < input.n_rows; i += strideWidth, rowidx++)
{
size_t rowEnd = i + kernelWidth - 1;
size_t colEnd = j + kernelHeight - 1;
unpooledError = arma::Mat<eT>(inputArea.n_rows, inputArea.n_cols);
unpooledError.fill(error(i / rStep, j / cStep) / inputArea.n_elem);
if (rowEnd > input.n_rows - 1)
{
if (floor)
continue;
rowEnd = input.n_rows - 1;
}
output(arma::span(i, i + rStep - 1 - offset),
arma::span(j, j + cStep - 1 - offset)) += unpooledError;
if (colEnd > input.n_cols - 1)
{
if (floor)
continue;
colEnd = input.n_cols - 1;
}
arma::mat InputArea = input(arma::span(i, rowEnd), arma::span(j, colEnd));
unpooledError = arma::Mat<eT>(InputArea.n_rows, InputArea.n_cols);
unpooledError.fill(error(rowidx, colidx) / InputArea.n_elem);
output(arma::span(i, i + InputArea.n_rows - 1),
arma::span(j, j + InputArea.n_cols - 1)) += unpooledError;
}
}
}
@@ -245,9 +267,6 @@ class MeanPooling
//! If true use maximum a posteriori during the forward pass.
bool deterministic;
//! Locally-stored stored rounding offset.
size_t offset;
//! Locally-stored number of input units.
size_t batchSize;
@@ -45,7 +45,6 @@ MeanPooling<InputDataType, OutputDataType>::MeanPooling(
outputHeight(0),
reset(false),
deterministic(false),
offset(0),
batchSize(0)
{
// Nothing to do here.
@@ -67,8 +66,6 @@ void MeanPooling<InputDataType, OutputDataType>::Forward(
(double) kernelWidth) / (double) strideWidth + 1);
outputHeight = std::floor((inputHeight -
(double) kernelHeight) / (double) strideHeight + 1);
offset = 0;
}
else
{
@@ -76,8 +73,6 @@ void MeanPooling<InputDataType, OutputDataType>::Forward(
(double) kernelWidth) / (double) strideWidth + 1);
outputHeight = std::ceil((inputHeight -
(double) kernelHeight) / (double) strideHeight + 1);
offset = 1;
}
outputTemp = arma::zeros<arma::Cube<eT> >(outputWidth, outputHeight,
@@ -110,6 +110,12 @@ class RBF
//! Modify the delta.
OutputDataType& Delta() { return delta; }
//! Get the size of the weights.
size_t WeightSize() const
{
return 0;
}
//! Get the shape of the input.
size_t InputShape() const
{
@@ -18,12 +18,12 @@ using namespace mlpack::regression;
BayesianLinearRegression::BayesianLinearRegression(const bool centerData,
const bool scaleData,
const size_t nIterMax,
const double tol) :
const size_t maxIterations,
const double tolerance) :
centerData(centerData),
scaleData(scaleData),
nIterMax(nIterMax),
tol(tol),
maxIterations(maxIterations),
tolerance(tolerance),
responsesOffset(0.0),
alpha(0.0),
beta(0.0),
@@ -60,7 +60,7 @@ double BayesianLinearRegression::Train(const arma::mat& data,
unsigned short i = 0;
double deltaAlpha = 1.0, deltaBeta = 1.0, crit = 1.0;
while ((crit > tol) && (i < nIterMax))
while ((crit > tolerance) && (i < maxIterations))
{
deltaAlpha = -alpha;
deltaBeta = -beta;
@@ -21,30 +21,30 @@ namespace mlpack {
namespace regression {
/**
* A Bayesian approach to the maximum likelihood estimation of the parameters
* \f$ \omega \f$ of the linear regression model. The Complexity is governed by
* the addition of a gaussian isotropic prior of precision \f$ \alpha \f$ over
* \f$ \omega \f$:
* A Bayesian approach to the maximum likelihood estimation of the parameters
* \f$ \omega \f$ of the linear regression model. The Complexity is governed by
* the addition of a gaussian isotropic prior of precision \f$ \alpha \f$ over
* \f$ \omega \f$:
*
* \f[
* p(\omega|\alpha) = \mathcal{N}(\omega|0, \alpha^{-1}I)
* \f]
*
* The optimization procedure calculates the posterior distribution of
* \f$ \omega \f$ knowing the data by maximizing an approximation of the log
* marginal likelihood derived from a type II maximum likelihood approximation.
*
* The optimization procedure calculates the posterior distribution of
* \f$ \omega \f$ knowing the data by maximizing an approximation of the log
* marginal likelihood derived from a type II maximum likelihood approximation.
* The determination of \f$ alpha \f$ and of the noise precision \f$ beta \f$
* is part of the optimization process, leading to an automatic determination of
* w. The model being entirely based on probabilty distributions, uncertainties
* is part of the optimization process, leading to an automatic determination of
* w. The model being entirely based on probabilty distributions, uncertainties
* are available and easly computed for both the parameters and the predictions.
*
* The advantage over linear regression and ridge regression is that the
* The advantage over linear regression and ridge regression is that the
* regularization is determined from all the training data alone without any
* require to an hold out method.
* require to an hold out method.
*
* The code below is an implementation of the maximization of the evidence
* The code below is an implementation of the maximization of the evidence
* function described in the section 3.5.2 of the C.Bishop book, Pattern
* Recognition and Machine Learning.
* Recognition and Machine Learning.
*
* @code
* @article{MacKay91bayesianinterpolation,
@@ -60,36 +60,37 @@ namespace regression {
* @code
* @book{Bishop:2006:PRM:1162264,
* author = {Bishop, Christopher M.},
* title = {Pattern Recognition and Machine Learning (Information Science
* title = {Pattern Recognition and Machine Learning (Information Science
* and Statistics)},
* chapter = {3}
* year = {2006},
* isbn = {0387310738},
* publisher = {Springer-Verlag},
* address = {Berlin, Heidelberg},
* }
* }
* @endcode
*
*
* Example of use:
*
* @code
* arma::mat xTrain; // Train data matrix. Column-major.
* arma::rowvec yTrain; // Train target values.
*
* // Train the model. Regularization strength is optimally tunned with the
* // training data alone by applying the Train method.
* BayesianLinearRegression estimator(); // Instanciate the estimator with default option.
* // Instantiate the estimator with default option.
* BayesianLinearRegression estimator;
* estimator.Train(xTrain, yTrain);
*
* // Prediction on test points.
* arma::mat xTest; // Test data matrix. Column-major.
* arma::rowvec predictions;
*
* estimator.Predict(xTest, prediction);
*
* arma::rowvec yTest; // Test target values.
* estimator.RMSE(xTest, yTest); // Evaluate using the RMSE score.
*
* // Compute the standard deviations of the predictions.
* arma::rowvec stds;
* estimator.Predict(xTest, responses, stds)
@@ -107,19 +108,20 @@ class BayesianLinearRegression
* examples.
* @param scaleData Whether or not scale the data according to the
* standard deviation of each feature.
* @param nIterMax Maximum number of iterations for convergency.
* @param tol Level from which the solution is considered sufficientlly
* stable.
* @param maxIterations Maximum number of iterations for convergency.
* @param tolerance Level from which the solution is considered sufficientlly
* stable.
*/
BayesianLinearRegression(const bool centerData = true,
const bool scaleData = false,
const size_t nIterMax = 50,
const double tol = 1e-4);
const size_t maxIterations = 50,
const double tolerance = 1e-4);
/**
* Run BayesianLinearRegression. The input matrix (like all mlpack matrices) should be
* column-major -- each column is an observation and each row is a dimension.
*
* Run BayesianLinearRegression. The input matrix (like all mlpack matrices)
* should be column-major -- each column is an observation and each row is a
* dimension.
*
* @param data Column-major input data, dim(P, N).
* @param responses A vector of targets, dim(N).
* @return Root mean squared error.
@@ -139,12 +141,13 @@ class BayesianLinearRegression
arma::rowvec& predictions) const;
/**
* Predict \f$y_{i}\f$ and the standard deviation of the predictive posterior
* Predict \f$y_{i}\f$ and the standard deviation of the predictive posterior
* distribution for each data point in the given data matrix, using the
* currently-trained Bayesian Ridge estimator.
*
* @param points The data point to apply the model.
* @param predictions Vector which will contain calculated values on completion.
* @param predictions Vector which will contain calculated values on
* completion.
* @param std Standard deviations of the predictions.
*/
void Predict(const arma::mat& points,
@@ -163,7 +166,7 @@ class BayesianLinearRegression
const arma::rowvec& responses) const;
/**
* Get the solution vector.
* Get the solution vector.
*
* @return omega Solution vector.
*/
@@ -187,7 +190,7 @@ class BayesianLinearRegression
/**
* Get the estimated variance. Train() must be called before.
*
*
* @return 1.0 / \f$ \beta \f$
*/
double Variance() const { return 1.0 / Beta(); }
@@ -200,7 +203,7 @@ class BayesianLinearRegression
const arma::colvec& DataOffset() const { return dataOffset; }
/**
* Get the vector of standard deviations computed on the features over the
* Get the vector of standard deviations computed on the features over the
* training points.
*
* @return dataOffset
@@ -214,9 +217,31 @@ class BayesianLinearRegression
*/
double ResponsesOffset() const { return responsesOffset; }
//! Get whether the data will be centered during training.
bool CenterData() const { return centerData; }
//! Modify whether the data will be centered during training.
bool& CenterData() { return centerData; }
//! Get whether the data will be scaled by standard deviations during
//! training.
bool ScaleData() const { return scaleData; }
//! Modify whether the data will be scaled by standard deviations during
//! training.
bool& ScaleData() { return scaleData; }
//! Get the maximum number of iterations for training.
size_t MaxIterations() const { return maxIterations; }
//! Modify the maximum number of iterations for training.
size_t& MaxIterations() { return maxIterations; }
//! Get the tolerance for training to converge.
double Tolerance() const { return tolerance; }
//! Modify the tolerance for training to converge.
double& Tolerance() { return tolerance; }
/**
* Serialize the BayesianLinearRegression model.
**/
*/
template<typename Archive>
void serialize(Archive& ar, const uint32_t version);
@@ -227,11 +252,11 @@ class BayesianLinearRegression
//! Scale the data by standard deviations if true.
bool scaleData;
//! Maximum number of iterations for convergency.
size_t nIterMax;
//! Maximum number of iterations for convergence.
size_t maxIterations;
//! Level from which the solution is considered sufficientlly stable.
double tol;
double tolerance;
//! Mean vector computed over the points.
arma::colvec dataOffset;
@@ -251,7 +276,7 @@ class BayesianLinearRegression
//! Effective number of parameters.
double gamma;
//! Solution vector
//! Solution vector.
arma::colvec omega;
//! Covariance matrix of the solution vector omega.
@@ -26,8 +26,8 @@ void BayesianLinearRegression::serialize(Archive& ar,
{
ar(CEREAL_NVP(centerData));
ar(CEREAL_NVP(scaleData));
ar(CEREAL_NVP(nIterMax));
ar(CEREAL_NVP(tol));
ar(CEREAL_NVP(maxIterations));
ar(CEREAL_NVP(tolerance));
ar(CEREAL_NVP(dataOffset));
ar(CEREAL_NVP(dataScale));
ar(CEREAL_NVP(responsesOffset));
@@ -83,7 +83,7 @@ void RandomizedBlockKrylovSVD::Apply(const arma::mat& data,
arma::qr_econ(Q, R, K);
// Approximate eigenvalues and eigenvectors using RayleighRitz method.
// Approximate eigenvalues and eigenvectors using Rayleigh-Ritz method.
arma::svd_econ(u, s, v, Q.t() * data);
// Do economical singular value decomposition and compute only the
@@ -29,7 +29,6 @@ class AllCategoricalSplit
{
public:
// No extra info needed for split.
template<typename ElemType>
class AuxiliarySplitInfo { };
/**
@@ -64,8 +63,8 @@ class AllCategoricalSplit
const WeightVecType& weights,
const size_t minimumLeafSize,
const double minimumGainSplit,
arma::Col<typename VecType::elem_type>& classProbabilities,
AuxiliarySplitInfo<typename VecType::elem_type>& aux);
arma::vec& classProbabilities,
AuxiliarySplitInfo& aux);
/**
* Return the number of children in the split.
@@ -73,9 +72,8 @@ class AllCategoricalSplit
* @param classProbabilities Auxiliary information for the split.
* @param * (aux) Auxiliary information for the split (Unused).
*/
template<typename ElemType>
static size_t NumChildren(const arma::Col<ElemType>& classProbabilities,
const AuxiliarySplitInfo<ElemType>& /* aux */);
static size_t NumChildren(const arma::vec& classProbabilities,
const AuxiliarySplitInfo& /* aux */);
/**
* Calculate the direction a point should percolate to.
@@ -87,8 +85,8 @@ class AllCategoricalSplit
template<typename ElemType>
static size_t CalculateDirection(
const ElemType& point,
const arma::Col<ElemType>& classProbabilities,
const AuxiliarySplitInfo<ElemType>& /* aux */);
const arma::vec& classProbabilities,
const AuxiliarySplitInfo& /* aux */);
};
} // namespace tree
@@ -26,8 +26,8 @@ double AllCategoricalSplit<FitnessFunction>::SplitIfBetter(
const WeightVecType& weights,
const size_t minimumLeafSize,
const double minimumGainSplit,
arma::Col<typename VecType::elem_type>& classProbabilities,
AuxiliarySplitInfo<typename VecType::elem_type>& /* aux */)
arma::vec& classProbabilities,
AuxiliarySplitInfo& /* aux */)
{
// Count the number of elements in each potential child.
const double epsilon = 1e-7; // Tolerance for floating-point errors.
@@ -110,20 +110,19 @@ double AllCategoricalSplit<FitnessFunction>::SplitIfBetter(
}
template<typename FitnessFunction>
template<typename ElemType>
size_t AllCategoricalSplit<FitnessFunction>::NumChildren(
const arma::Col<ElemType>& classProbabilities,
const AuxiliarySplitInfo<ElemType>& /* aux */)
const arma::vec& classProbabilities,
const AuxiliarySplitInfo& /* aux */)
{
return classProbabilities[0];
return size_t(classProbabilities[0]);
}
template<typename FitnessFunction>
template<typename ElemType>
size_t AllCategoricalSplit<FitnessFunction>::CalculateDirection(
const ElemType& point,
const arma::Col<ElemType>& /* classProbabilities */,
const AuxiliarySplitInfo<ElemType>& /* aux */)
const arma::vec& /* classProbabilities */,
const AuxiliarySplitInfo& /* aux */)
{
return (size_t) point;
}
@@ -28,7 +28,6 @@ class BestBinaryNumericSplit
{
public:
// No extra info needed for split.
template<typename ElemType>
class AuxiliarySplitInfo { };
/**
@@ -37,6 +36,10 @@ class BestBinaryNumericSplit
* return the value 'bestGain'. If a split is made, then classProbabilities
* and aux may be modified.
*
* It's not necessary that `ElemType` is the same as the type of the data in
* `VecType`---if they are different, casting will be done to store the
* auxiliary information.
*
* @param bestGain Best gain seen so far (we'll only split if we find gain
* better than this).
* @param data The dimension of data points to check for a split in.
@@ -60,15 +63,14 @@ class BestBinaryNumericSplit
const WeightVecType& weights,
const size_t minimumLeafSize,
const double minimumGainSplit,
arma::Col<typename VecType::elem_type>& classProbabilities,
AuxiliarySplitInfo<typename VecType::elem_type>& aux);
arma::vec& classProbabilities,
AuxiliarySplitInfo& aux);
/**
* Returns 2, since the binary split always has two children.
*/
template<typename ElemType>
static size_t NumChildren(const arma::Col<ElemType>& /* classProbabilities */,
const AuxiliarySplitInfo<ElemType>& /* aux */)
static size_t NumChildren(const arma::vec& /* classProbabilities */,
const AuxiliarySplitInfo& /* aux */)
{
return 2;
}
@@ -83,8 +85,8 @@ class BestBinaryNumericSplit
template<typename ElemType>
static size_t CalculateDirection(
const ElemType& point,
const arma::Col<ElemType>& classProbabilities,
const AuxiliarySplitInfo<ElemType>& /* aux */);
const arma::vec& classProbabilities,
const AuxiliarySplitInfo& /* aux */);
};
} // namespace tree
@@ -25,8 +25,8 @@ double BestBinaryNumericSplit<FitnessFunction>::SplitIfBetter(
const WeightVecType& weights,
const size_t minimumLeafSize,
const double minimumGainSplit,
arma::Col<typename VecType::elem_type>& classProbabilities,
AuxiliarySplitInfo<typename VecType::elem_type>& /* aux */)
arma::vec& classProbabilities,
AuxiliarySplitInfo& /* aux */)
{
// First sanity check: if we don't have enough points, we can't split.
if (data.n_elem < (minimumLeafSize * 2))
@@ -189,8 +189,8 @@ template<typename FitnessFunction>
template<typename ElemType>
size_t BestBinaryNumericSplit<FitnessFunction>::CalculateDirection(
const ElemType& point,
const arma::Col<ElemType>& classProbabilities,
const AuxiliarySplitInfo<ElemType>& /* aux */)
const arma::vec& classProbabilities,
const AuxiliarySplitInfo& /* aux */)
{
if (point <= classProbabilities[0])
return 0; // Go left.
@@ -30,18 +30,20 @@ namespace tree {
*
* The class inherits from the auxiliary split information in order to prevent
* an empty auxiliary split information struct from taking any extra size.
*
* Note that `ElemType` is a template parameter controlling the type that is
* used to store split information. In general, you would want to set this to
* be the same as the type of the data that you will be using, but it's not
* required to do that.
*/
template<typename FitnessFunction = GiniGain,
template<typename> class NumericSplitType = BestBinaryNumericSplit,
template<typename> class CategoricalSplitType = AllCategoricalSplit,
typename DimensionSelectionType = AllDimensionSelect,
typename ElemType = double,
bool NoRecursion = false>
class DecisionTree :
public NumericSplitType<FitnessFunction>::template
AuxiliarySplitInfo<ElemType>,
public CategoricalSplitType<FitnessFunction>::template
AuxiliarySplitInfo<ElemType>
public NumericSplitType<FitnessFunction>::AuxiliarySplitInfo,
public CategoricalSplitType<FitnessFunction>::AuxiliarySplitInfo
{
public:
//! Allow access to the numeric split type.
@@ -500,9 +502,9 @@ class DecisionTree :
//! Note that this class will also hold the members of the NumericSplit and
//! CategoricalSplit AuxiliarySplitInfo classes, since it inherits from them.
//! We'll define some convenience typedefs here.
typedef typename NumericSplit::template AuxiliarySplitInfo<ElemType>
typedef typename NumericSplit::AuxiliarySplitInfo
NumericAuxiliarySplitInfo;
typedef typename CategoricalSplit::template AuxiliarySplitInfo<ElemType>
typedef typename CategoricalSplit::AuxiliarySplitInfo
CategoricalAuxiliarySplitInfo;
/**
@@ -578,13 +580,11 @@ class DecisionTree :
template<typename FitnessFunction = GiniGain,
template<typename> class NumericSplitType = BestBinaryNumericSplit,
template<typename> class CategoricalSplitType = AllCategoricalSplit,
typename DimensionSelectType = AllDimensionSelect,
typename ElemType = double>
typename DimensionSelectType = AllDimensionSelect>
using DecisionStump = DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectType,
ElemType,
false>;
/**
@@ -595,7 +595,6 @@ typedef DecisionTree<InformationGain,
BestBinaryNumericSplit,
AllCategoricalSplit,
AllDimensionSelect,
double,
true> ID3DecisionStump;
} // namespace tree
} // namespace mlpack
@@ -22,14 +22,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename MatType, typename LabelsType>
DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::DecisionTree(
MatType data,
const data::DatasetInfo& datasetInfo,
@@ -62,14 +60,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename MatType, typename LabelsType>
DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::DecisionTree(
MatType data,
LabelsType labels,
@@ -100,14 +96,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename MatType, typename LabelsType, typename WeightsType>
DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::DecisionTree(
MatType data,
const data::DatasetInfo& datasetInfo,
@@ -144,14 +138,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename MatType, typename LabelsType, typename WeightsType>
DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::DecisionTree(
const DecisionTree& other,
MatType data,
@@ -185,14 +177,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename MatType, typename LabelsType, typename WeightsType>
DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::DecisionTree(
MatType data,
LabelsType labels,
@@ -229,14 +219,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename MatType, typename LabelsType, typename WeightsType>
DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::DecisionTree(
const DecisionTree& other,
MatType data,
@@ -275,13 +263,11 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::DecisionTree(const size_t numClasses) :
splitDimension(0),
dimensionTypeOrMajorityClass(0),
@@ -296,13 +282,11 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::DecisionTree(const DecisionTree& other) :
NumericAuxiliarySplitInfo(other),
CategoricalAuxiliarySplitInfo(other),
@@ -320,13 +304,11 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::DecisionTree(DecisionTree&& other) :
NumericAuxiliarySplitInfo(std::move(other)),
CategoricalAuxiliarySplitInfo(std::move(other)),
@@ -344,19 +326,16 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>&
DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::operator=(const DecisionTree& other)
{
if (this == &other)
@@ -388,19 +367,16 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>&
DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::operator=(DecisionTree&& other)
{
if (this == &other)
@@ -432,13 +408,11 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::~DecisionTree()
{
for (size_t i = 0; i < children.size(); ++i)
@@ -450,14 +424,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename MatType, typename LabelsType>
double DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::Train(
MatType data,
const data::DatasetInfo& datasetInfo,
@@ -493,14 +465,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename MatType, typename LabelsType>
double DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::Train(
MatType data,
LabelsType labels,
@@ -535,14 +505,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename MatType, typename LabelsType, typename WeightsType>
double DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::Train(
MatType data,
const data::DatasetInfo& datasetInfo,
@@ -584,14 +552,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename MatType, typename LabelsType, typename WeightsType>
double DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::Train(
MatType data,
LabelsType labels,
@@ -632,14 +598,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<bool UseWeights, typename MatType>
double DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::Train(
MatType& data,
const size_t begin,
@@ -818,14 +782,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<bool UseWeights, typename MatType>
double DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::Train(
MatType& data,
const size_t begin,
@@ -976,14 +938,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename VecType>
size_t DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::Classify(const VecType& point) const
{
if (children.size() == 0)
@@ -1000,14 +960,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename VecType>
void DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::Classify(const VecType& point,
size_t& prediction,
arma::vec& probabilities) const
@@ -1028,14 +986,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename MatType>
void DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::Classify(const MatType& data,
arma::Row<size_t>& predictions) const
{
@@ -1056,14 +1012,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename MatType>
void DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::Classify(const MatType& data,
arma::Row<size_t>& predictions,
arma::mat& probabilities) const
@@ -1095,14 +1049,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename Archive>
void DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::serialize(Archive& ar,
const uint32_t /* version */)
{
@@ -1126,14 +1078,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<typename VecType>
size_t DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::CalculateDirection(const VecType& point) const
{
if ((data::Datatype) dimensionTypeOrMajorityClass ==
@@ -1150,13 +1100,11 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
size_t DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::NumClasses() const
{
// Recurse to the nearest child and return the number of elements in the
@@ -1171,14 +1119,12 @@ template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
typename ElemType,
bool NoRecursion>
template<bool UseWeights, typename RowType, typename WeightsRowType>
void DecisionTree<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
ElemType,
NoRecursion>::CalculateClassProbabilities(
const RowType& labels,
const size_t numClasses,
@@ -26,7 +26,7 @@ class InformationGain
{
public:
/**
* Evaluate the Gini impurity given a vector of class weight counts.
* Evaluate the information gain given a vector of class weight counts.
*/
template<bool UseWeights, typename CountType>
static double EvaluatePtr(const CountType* counts,
+45
View File
@@ -67,6 +67,37 @@ double DiagonalGMM::LogProbability(const arma::vec& observation) const
return sum;
}
/**
* Return the log probability of the given observation GMM matrix.
*
* @param observation Observation matrix to compute log-probabilty.
* @param logProbs Stores the value of log-probability for input.
*/
void DiagonalGMM::LogProbability(const arma::mat& observation,
arma::vec& logProbs) const
{
// Sum the probability for each Gaussian in our mixture (and we have to
// multiply by the prior for each Gaussian too).
logProbs.set_size(observation.n_cols);
// Store log-probability value in a matrix.
arma::mat logProb(observation.n_cols, gaussians);
// Assign value to the matrix.
for (size_t i = 0; i < gaussians; i++)
{
arma::vec temp(logProb.colptr(i), observation.n_cols, false, true);
dists[i].LogProbability(observation, temp);
}
// Save log(weights) as a vector.
arma::vec logWeights = arma::log(weights);
// Compute log-probability.
logProb += repmat(logWeights.t(), logProb.n_rows, 1);
math::LogSumExp(logProb, logProbs);
}
/**
* Return the probability of the given observation being from this GMM.
*/
@@ -75,6 +106,20 @@ double DiagonalGMM::Probability(const arma::vec& observation) const
return exp(LogProbability(observation));
}
/**
* Return the probability of the given observation GMM matrix.
*
* @param observation Observation matrix to compute probabilty.
* @param probs Stores the value of probability for observation.
*/
void DiagonalGMM::Probability(const arma::mat& observation,
arma::vec& probs) const
{
LogProbability(observation, probs);
probs = exp(probs);
}
/**
* Return the log probability of the given observation being from the given
* component in the mixture.
+17 -1
View File
@@ -166,14 +166,30 @@ class DiagonalGMM
*/
double Probability(const arma::vec& observation) const;
/**
* Return the probability that the given observation matrix.
*
* @param observation Observation to evaluate the probability of.
* @param probs Stores the value of probability for observation.
*/
void Probability(const arma::mat& observation, arma::vec& probs) const;
/**
* Return the log probability that the given observation came from this
* distribution.
*
* @param observation Observation to evaluate the probability of.
* @param observation Observation to evaluate the log-probability of.
*/
double LogProbability(const arma::vec& observation) const;
/**
* Return the log probability that the given observation matrix.
*
* @param observation Observation to evaluate the log-probability of.
* @param logProbs Stores the value of log-probability for observation.
*/
void LogProbability(const arma::mat& observation, arma::vec& logProbs) const;
/**
* Return the probability that the given observation came from the given
* Gaussian component in this distribution.
+62
View File
@@ -53,6 +53,8 @@ GMM& GMM::operator=(const GMM& other)
/**
* Return the log probability of the given observation being from this GMM.
*
* @param observation Observation vector to compute log-probabilty.
*/
double GMM::LogProbability(const arma::vec& observation) const
{
@@ -66,17 +68,67 @@ double GMM::LogProbability(const arma::vec& observation) const
return sum;
}
/**
* Return the log probability of the given observation GMM matrix.
*
* @param observation Observation matrix to compute log-probabilty.
* @param logProbs Stores the value of log-probability for Observation.
*/
void GMM::LogProbability(const arma::mat& observation,
arma::vec& logProbs) const
{
// Sum the probability for each Gaussian in our mixture (and we have to
// multiply by the prior for each Gaussian too).
logProbs.set_size(observation.n_cols);
// Store log-probability value in a matrix.
arma::mat logProb(observation.n_cols, gaussians);
// Assign value to the matrix.
for (size_t i = 0; i < gaussians; i++)
{
arma::vec temp(logProb.colptr(i), observation.n_cols, false, true);
dists[i].LogProbability(observation, temp);
}
// Save log(weights) as a vector.
arma::vec logWeights = arma::log(weights);
// Compute log-probability.
logProb += repmat(logWeights.t(), logProb.n_rows, 1);
math::LogSumExp(logProb, logProbs);
}
/**
* Return the probability of the given observation being from this GMM.
*
* @param observation Observation vector to compute probabilty.
*/
double GMM::Probability(const arma::vec& observation) const
{
return exp(LogProbability(observation));
}
/**
* Return the probability of the given observation GMM matrix.
*
* @param observation Observation matrix to compute probabilty.
* @param probs Stores the value of probability for x.
*/
void GMM::Probability(const arma::mat& observation,
arma::vec& probs) const
{
LogProbability(observation, probs);
probs = exp(probs);
}
/**
* Return the log probability of the given observation being from the given
* component in the mixture.
*
* @param observation Observation vector to compute log-probabilty.
* @param component Calculate the log-probability for given observation vector.
*/
double GMM::LogProbability(const arma::vec& observation,
const size_t component) const
@@ -89,6 +141,9 @@ double GMM::LogProbability(const arma::vec& observation,
/**
* Return the probability of the given observation being from the given
* component in the mixture.
*
* @param observation Observation matrix to compute probabilty.
* @param component Calculate the probability for given component.
*/
double GMM::Probability(const arma::vec& observation,
const size_t component) const
@@ -129,6 +184,9 @@ arma::vec GMM::Random() const
/**
* Classify the given observations as being from an individual component in this
* GMM.
*
* @param observation Observation matrix for classification.
* @param labels Save the labels for the given observation matrix.
*/
void GMM::Classify(const arma::mat& observations,
arma::Row<size_t>& labels) const
@@ -158,6 +216,10 @@ void GMM::Classify(const arma::mat& observations,
/**
* Get the log-likelihood of this data's fit to the model.
*
* @param data Data matrix to compute log-likelihood.
* @parma distsL Vector of Gaussian distribution.
* @param weightsL Vector of weights for computing likelihoods.
*/
double GMM::LogLikelihood(
const arma::mat& data,
+18 -2
View File
@@ -160,18 +160,34 @@ class GMM
* Return the probability that the given observation came from this
* distribution.
*
* @param observation Observation to evaluate the probability of.
* @param observation Observation vector to evaluate the probability of.
*/
double Probability(const arma::vec& observation) const;
/**
* Return the probability of the given observation matrix.
*
* @param observation Observation matrix.
* @param probs Vector to store probability value of observation x.
*/
void Probability(const arma::mat& observation, arma::vec& probs) const;
/**
* Return the log probability that the given observation came from this
* distribution.
*
* @param observation Observation to evaluate the probability of.
* @param observation Observation vector to evaluate the probability of.
*/
double LogProbability(const arma::vec& observation) const;
/**
* Return the log-probability of the given observation (x) matrix.
*
* @param observation Observation matrix.
* @param logProbs Vector to store log-probability value of observation.
*/
void LogProbability(const arma::mat& observation, arma::vec& logProbs) const;
/**
* Return the probability that the given observation came from the given
* Gaussian component in this distribution.
+11 -11
View File
@@ -316,8 +316,8 @@ class HMM
*
* @param emissionLogProb emission probability at time t.
* @param logLikelihood Log-likelihood of the given sequence of emission
* probability up to time t-1. This will be overwritten with the log-likelihood
* of the given emission probability up to time t.
* probability up to time t-1. This will be overwritten with the
* log-likelihood of the given emission probability up to time t.
* @param forwardLogProb Vector in which forward probabilities will be saved.
* Passing forwardLogProb as an empty vector indicates the start of the
* sequence (i.e. time t=0).
@@ -446,9 +446,8 @@ class HMM
* @param logScales Vector in which the log of scaling factors will be saved.
* @return Forward probabilities
*/
arma::vec ForwardAtT0(
const arma::vec& emissionLogProb,
double& logScales) const;
arma::vec ForwardAtT0(const arma::vec& emissionLogProb,
double& logScales) const;
/**
* Given emission probabilities, computes forward probabilities for time t>0.
@@ -458,10 +457,9 @@ class HMM
* @param prevForwardLogProb Previous forward probabilities.
* @return Forward probabilities
*/
arma::vec ForwardAtTn(
const arma::vec& emissionLogProb,
double& logScales,
const arma::vec& prevForwardLogProb) const;
arma::vec ForwardAtTn(const arma::vec& emissionLogProb,
double& logScales,
const arma::vec& prevForwardLogProb) const;
// Helper functions.
/**
@@ -476,7 +474,8 @@ class HMM
*/
void Forward(const arma::mat& dataSeq,
arma::vec& logScales,
arma::mat& forwardLogProb) const;
arma::mat& forwardLogProb,
arma::mat& logProbs) const;
/**
* The Backward algorithm (part of the Forward-Backward algorithm). Computes
@@ -491,7 +490,8 @@ class HMM
*/
void Backward(const arma::mat& dataSeq,
const arma::vec& logScales,
arma::mat& backwardLogProb) const;
arma::mat& backwardLogProb,
arma::mat& logProbs) const;
//! Set of emission probability distributions; one for each state.
std::vector<Distribution> emission;
+123 -75
View File
@@ -149,8 +149,19 @@ double HMM<Distribution>::Train(const std::vector<arma::mat>& dataSeq)
backwardLog, logScales);
// Add to estimate of initial probability for state j.
for (size_t j = 0; j < logTransition.n_cols; ++j)
newLogInitial[j] = math::LogAdd(newLogInitial[j], stateLogProb(j, 0));
math::LogSumExp<arma::vec, true>(stateLogProb.unsafe_col(0),
newLogInitial);
// Define a variable to store the value of log-probability for data.
arma::mat logProbs(dataSeq[seq].n_cols, logTransition.n_rows);
// Save the values of log-probability to logProbs.
for (size_t i = 0; i < logTransition.n_rows; i++)
{
// Define alias of desired column.
arma::vec alias(logProbs.colptr(i), logProbs.n_rows, false, true);
// Use advanced constructor for using logProbs directly.
emission[i].LogProbability(dataSeq[seq], alias);
}
// Now re-estimate the parameters. This is the M-step.
// pi_i = sum_d ((1 / P(seq[d])) sum_t (f(i, 0) b(i, 0))
@@ -160,25 +171,31 @@ double HMM<Distribution>::Train(const std::vector<arma::mat>& dataSeq)
// We store the new estimates in a different matrix.
for (size_t t = 0; t < dataSeq[seq].n_cols; ++t)
{
for (size_t j = 0; j < logTransition.n_cols; ++j)
// Assemble temporary vector that's used in log-sum computation.
if (t < dataSeq[seq].n_cols - 1)
{
if (t < dataSeq[seq].n_cols - 1)
{
// Estimate of T_ij (probability of transition from state j to state
// i). We postpone multiplication of the old T_ij until later.
for (size_t i = 0; i < logTransition.n_rows; i++)
{
newLogTransition(i, j) = math::LogAdd(newLogTransition(i, j),
forwardLog(j, t) + backwardLog(i, t + 1) +
emission[i].LogProbability(dataSeq[seq].unsafe_col(t + 1)) -
logScales[t + 1]);
}
}
// This term is the same across all states, so compute it once and
// cache it.
const arma::vec tmp = backwardLog.col(t + 1) +
logProbs.row(t + 1).t() - logScales[t + 1];
arma::vec output;
math::LogSumExp(tmp, output);
// Add to list of emission observations, for Distribution::Train().
emissionList.col(sumTime) = dataSeq[seq].col(t);
emissionProb[j][sumTime] = exp(stateLogProb(j, t));
for (size_t j = 0; j < logTransition.n_cols; ++j)
{
// Compute the estimate of T_ij (probability of transition from
// state j to state i). We postpone multiplication of the old T_ij
// until later.
arma::vec tmp2 = output + forwardLog(j, t);
arma::vec alias = newLogTransition.unsafe_col(j);
math::LogSumExp<arma::vec, true>(tmp2, alias);
}
}
// Add to list of emission observations, for Distribution::Train().
for (size_t j = 0; j < logTransition.n_cols; ++j)
emissionProb[j][sumTime] = exp(stateLogProb(j, t));
emissionList.col(sumTime) = dataSeq[seq].col(t);
sumTime++;
}
}
@@ -337,9 +354,20 @@ double HMM<Distribution>::LogEstimate(const arma::mat& dataSeq,
arma::mat& backwardLogProb,
arma::vec& logScales) const
{
arma::mat logProbs(dataSeq.n_cols, logTransition.n_rows);
// Save the values of log-probability to logProbs.
for (size_t i = 0; i < logTransition.n_rows; i++)
{
// Define alias of desired column.
arma::vec alias(logProbs.colptr(i), logProbs.n_rows, false, true);
// Use advanced constructor for using logProbs directly.
emission[i].LogProbability(dataSeq, alias);
}
// First run the forward-backward algorithm.
Forward(dataSeq, logScales, forwardLogProb);
Backward(dataSeq, logScales, backwardLogProb);
Forward(dataSeq, logScales, forwardLogProb, logProbs);
Backward(dataSeq, logScales, backwardLogProb, logProbs);
// Now assemble the state probability matrix based on the forward and backward
// probabilities.
@@ -481,6 +509,19 @@ double HMM<Distribution>::Predict(const arma::mat& dataSeq,
// Store the best first state.
arma::uword index;
// Define a variable to store the value of log-probability for dataSeq.
arma::mat logProbs(dataSeq.n_cols, logTransition.n_rows);
// Save the values of log-probability to logProbs.
for (size_t i = 0; i < logTransition.n_rows; i++)
{
// Define alias of desired column.
arma::vec alias(logProbs.colptr(i), logProbs.n_rows, false, true);
// Use advanced constructor for using logProbs directly.
emission[i].LogProbability(dataSeq, alias);
}
for (size_t t = 1; t < dataSeq.n_cols; t++)
{
// Assemble the state probability for this element.
@@ -489,8 +530,7 @@ double HMM<Distribution>::Predict(const arma::mat& dataSeq,
for (size_t j = 0; j < logTransition.n_rows; j++)
{
arma::vec prob = logStateProb.col(t - 1) + logTransition.row(j).t();
logStateProb(j, t) = prob.max(index) +
emission[j].LogProbability(dataSeq.unsafe_col(t));
logStateProb(j, t) = prob.max(index) + logProbs(t, j);
stateSeqBack(j, t) = index;
}
}
@@ -516,7 +556,19 @@ double HMM<Distribution>::LogLikelihood(const arma::mat& dataSeq) const
arma::mat forwardLog;
arma::vec logScales;
Forward(dataSeq, logScales, forwardLog);
// This is needed here.
arma::mat logProbs(dataSeq.n_cols, logTransition.n_rows);
// Save the values of log-probability to logProbs.
for (size_t i = 0; i < logTransition.n_rows; i++)
{
// Define alias of desired column.
arma::vec alias(logProbs.colptr(i), logProbs.n_rows, false, true);
// Use advanced constructor for using logProbs directly.
emission[i].LogProbability(dataSeq, alias);
}
Forward(dataSeq, logScales, forwardLog, logProbs);
// The log-likelihood is the log of the scales for each time step.
return accu(logScales);
@@ -557,8 +609,7 @@ double HMM<Distribution>::EmissionLogLikelihood(
arma::vec& forwardLogProb) const
{
bool isStartOfSeq = forwardLogProb.empty();
double curLogScale = EmissionLogScaleFactor(emissionLogProb,
forwardLogProb);
double curLogScale = EmissionLogScaleFactor(emissionLogProb, forwardLogProb);
logLikelihood = isStartOfSeq ? curLogScale : curLogScale + logLikelihood;
return logLikelihood;
}
@@ -607,7 +658,19 @@ void HMM<Distribution>::Filter(const arma::mat& dataSeq,
// First run the forward algorithm.
arma::mat forwardLogProb;
arma::vec logScales;
Forward(dataSeq, logScales, forwardLogProb);
// This is needed here.
arma::mat logProbs(dataSeq.n_cols, logTransition.n_rows);
// Save the values of log-probability to logProbs.
for (size_t i = 0; i < logTransition.n_rows; i++)
{
// Define alias of desired column.
arma::vec alias(logProbs.colptr(i), logProbs.n_rows, false, true);
// Use advanced constructor for using logProbs directly.
emission[i].LogProbability(dataSeq, alias);
}
Forward(dataSeq, logScales, forwardLogProb, logProbs);
// Propagate state ahead.
if (ahead != 0)
@@ -655,14 +718,12 @@ arma::vec HMM<Distribution>::ForwardAtT0(const arma::vec& emissionLogProb,
// P(X_k | o_{1:k}) for all possible states X_k, for each time point k.
ConvertToLogSpace();
arma::vec forwardLogProb(logTransition.n_rows);
forwardLogProb.fill(-std::numeric_limits<double>::infinity());
// The first entry in the forward algorithm uses the initial state
// probabilities. Note that MATLAB assumes that the starting state (at
// t = -1) is state 0; this is not our assumption here. To force that
// behavior, you could append a single starting state to every single data
// sequence and that should produce results in line with MATLAB.
forwardLogProb = logInitial + emissionLogProb;
arma::vec forwardLogProb = logInitial + emissionLogProb;
// Normalize probability.
logScales = math::AccuLog(forwardLogProb);
@@ -677,22 +738,23 @@ arma::vec HMM<Distribution>::ForwardAtT0(const arma::vec& emissionLogProb,
*/
template<typename Distribution>
arma::vec HMM<Distribution>::ForwardAtTn(const arma::vec& emissionLogProb,
double& logScales,
const arma::vec& prevForwardLogProb) const
double& logScales,
const arma::vec& prevForwardLogProb)
const
{
// Our goal is to calculate the forward probabilities:
// P(X_k | o_{1:k}) for all possible states X_k, for each time point k.
arma::vec forwardLogProb(logTransition.n_rows);
forwardLogProb.fill(-std::numeric_limits<double>::infinity());
// Now compute the probabilities for each successive observation.
for (size_t state = 0; state < logTransition.n_rows; state++) {
// The forward probability of state j at time t is the sum over all
// states of the probability of the previous state transitioning to
// the current state and emitting the given observation.
arma::vec tmp = prevForwardLogProb + logTransition.row(state).t();
forwardLogProb(state) = math::AccuLog(tmp) + emissionLogProb(state);
}
// The forward probability of state j at time t is the sum over all states of
// the probability of the previous state transitioning to the current state
// and emitting the given observation. To do this computation in log-space,
// we can use LogSumExp().
arma::vec forwardLogProb;
arma::mat tmp = logTransition + repmat(prevForwardLogProb.t(),
logTransition.n_rows, 1);
math::LogSumExp(tmp, forwardLogProb);
forwardLogProb += emissionLogProb;
// Normalize probability.
logScales = math::AccuLog(forwardLogProb);
if (std::isfinite(logScales))
@@ -707,7 +769,8 @@ arma::vec HMM<Distribution>::ForwardAtTn(const arma::vec& emissionLogProb,
template<typename Distribution>
void HMM<Distribution>::Forward(const arma::mat& dataSeq,
arma::vec& logScales,
arma::mat& forwardLogProb) const
arma::mat& forwardLogProb,
arma::mat& logProbs) const
{
// Our goal is to calculate the forward probabilities:
// P(X_k | o_{1:k}) for all possible states X_k, for each time point k.
@@ -722,33 +785,21 @@ void HMM<Distribution>::Forward(const arma::mat& dataSeq,
// behavior, you could append a single starting state to every single data
// sequence and that should produce results in line with MATLAB.
arma::vec emissionLogProb(logTransition.n_rows);
for (size_t state = 0; state < logTransition.n_rows; state++)
{
emissionLogProb(state) =
emission[state].LogProbability(dataSeq.unsafe_col(0));
}
forwardLogProb.col(0) = ForwardAtT0(emissionLogProb, logScales(0));
forwardLogProb.col(0) = ForwardAtT0(logProbs.row(0).t(), logScales(0));
// Now compute the probabilities for each successive observation.
for (size_t t = 1; t < dataSeq.n_cols; t++)
{
for (size_t state = 0; state < logTransition.n_rows; state++)
{
emissionLogProb(state) =
emission[state].LogProbability(dataSeq.unsafe_col(t));
}
forwardLogProb.col(t) =
ForwardAtTn(emissionLogProb, logScales(t), forwardLogProb.col(t-1));
forwardLogProb.col(t) = ForwardAtTn(logProbs.row(t).t(), logScales(t),
forwardLogProb.col(t - 1));
}
}
template<typename Distribution>
void HMM<Distribution>::Backward(const arma::mat& dataSeq,
const arma::vec& logScales,
arma::mat& backwardLogProb) const
arma::mat& backwardLogProb,
arma::mat& logProbs) const
{
// Our goal is to calculate the backward probabilities:
// P(X_k | o_{k + 1:T}) for all possible states X_k, for each time point k.
@@ -761,23 +812,20 @@ void HMM<Distribution>::Backward(const arma::mat& dataSeq,
// Now step backwards through all other observations.
for (size_t t = dataSeq.n_cols - 2; t + 1 > 0; t--)
{
for (size_t j = 0; j < logTransition.n_rows; j++)
{
// The backward probability of state j at time t is the sum over all state
// of the probability of the next state having been a transition from the
// current state multiplied by the probability of each of those states
// emitting the given observation.
for (size_t state = 0; state < logTransition.n_rows; state++)
{
backwardLogProb(j, t) = math::LogAdd(backwardLogProb(j, t),
logTransition(state, j) + backwardLogProb(state, t + 1)
+ emission[state].LogProbability(dataSeq.unsafe_col(t + 1)));
}
// The backward probability of state j at time t is the sum over all
// states of the probability of the next state having been a transition
// from the current state multiplied by the probability of each of those
// states emitting the given observation. To compute this in log-space, we
// can use LogSumExpT().
const arma::mat tmp = logTransition +
repmat(backwardLogProb.col(t + 1), 1, logTransition.n_cols) +
repmat(logProbs.row(t + 1).t(), 1, logTransition.n_cols);
arma::vec alias = backwardLogProb.unsafe_col(t);
math::LogSumExpT<arma::mat, true>(tmp, alias);
// Normalize by the weights from the forward algorithm.
if (std::isfinite(logScales[t + 1]))
backwardLogProb(j, t) -= logScales[t + 1];
}
// Normalize by the weights from the forward algorithm.
if (std::isfinite(logScales[t + 1]))
backwardLogProb.col(t) -= logScales[t + 1];
}
}
@@ -19,23 +19,39 @@
namespace mlpack {
namespace tree {
/**
* The RandomForest class provides an implementation of random forests,
* described in Breiman's seminal paper:
*
* @code
* @article{breiman2001random,
* title={Random forests},
* author={Breiman, Leo},
* journal={Machine Learning},
* volume={45},
* number={1},
* pages={5--32},
* year={2001},
* publisher={Springer}
* }
* @endcode
*/
template<typename FitnessFunction = GiniGain,
typename DimensionSelectionType = MultipleRandomDimensionSelect,
template<typename> class NumericSplitType = BestBinaryNumericSplit,
template<typename> class CategoricalSplitType = AllCategoricalSplit,
typename ElemType = double>
template<typename> class CategoricalSplitType = AllCategoricalSplit>
class RandomForest
{
public:
//! Allow access to the underlying decision tree type.
typedef DecisionTree<FitnessFunction, NumericSplitType, CategoricalSplitType,
DimensionSelectionType, ElemType> DecisionTreeType;
DimensionSelectionType> DecisionTreeType;
/**
* Construct the random forest without any training or specifying the number
* of trees. Predict() will throw an exception until Train() is called.
*/
RandomForest() { }
RandomForest();
/**
* Create a random forest, training on the given labeled training data with
@@ -167,6 +183,8 @@ class RandomForest
* @param minimumLeafSize Minimum number of points in each tree's leaf nodes.
* @param minimumGainSplit Minimum gain for splitting a decision tree node.
* @param maximumDepth Maximum depth for the tree.
* @param warmStart When set to `true`, it adds `numTrees` new trees to the
* existing random forest otherwise a new forest is trained from scratch.
* @param dimensionSelector Instantiated dimension selection policy.
* @return The average entropy of all the decision trees trained under forest.
*/
@@ -178,6 +196,7 @@ class RandomForest
const size_t minimumLeafSize = 1,
const double minimumGainSplit = 1e-7,
const size_t maximumDepth = 0,
const bool warmStart = false,
DimensionSelectionType dimensionSelector =
DimensionSelectionType());
@@ -198,6 +217,8 @@ class RandomForest
* @param minimumLeafSize Minimum number of points in each tree's leaf nodes.
* @param minimumGainSplit Minimum gain for splitting a decision tree node.
* @param maximumDepth Maximum depth for the tree.
* @param warmStart When set to `true`, it adds `numTrees` new trees to the
* existing random forest else a new forest is trained from scratch.
* @param dimensionSelector Instantiated dimension selection policy.
* @return The average entropy of all the decision trees trained under forest.
*/
@@ -210,6 +231,7 @@ class RandomForest
const size_t minimumLeafSize = 1,
const double minimumGainSplit = 1e-7,
const size_t maximumDepth = 0,
const bool warmStart = false,
DimensionSelectionType dimensionSelector =
DimensionSelectionType());
@@ -228,6 +250,8 @@ class RandomForest
* @param minimumLeafSize Minimum number of points in each tree's leaf nodes.
* @param minimumGainSplit Minimum gain for splitting a decision tree node.
* @param maximumDepth Maximum depth for the tree.
* @param warmStart When set to `true`, it adds `numTrees` new trees to the
* existing random forest else a new forest is trained from scratch.
* @param dimensionSelector Instantiated dimension selection policy.
* @return The average entropy of all the decision trees trained under forest.
*/
@@ -240,6 +264,7 @@ class RandomForest
const size_t minimumLeafSize = 1,
const double minimumGainSplit = 1e-7,
const size_t maximumDepth = 0,
const bool warmStart = false,
DimensionSelectionType dimensionSelector =
DimensionSelectionType());
@@ -260,6 +285,8 @@ class RandomForest
* @param minimumLeafSize Minimum number of points in each tree's leaf nodes.
* @param minimumGainSplit Minimum gain for splitting a decision tree node.
* @param maximumDepth Maximum depth for the tree.
* @param warmStart When set to `true`, it adds `numTrees` new trees to the
* existing random forest else a new forest is trained from scratch.
* @param dimensionSelector Instantiated dimension selection policy.
* @return The average entropy of all the decision trees trained under forest.
*/
@@ -273,6 +300,7 @@ class RandomForest
const size_t minimumLeafSize = 1,
const double minimumGainSplit = 1e-7,
const size_t maximumDepth = 0,
const bool warmStart = false,
DimensionSelectionType dimensionSelector =
DimensionSelectionType());
@@ -354,6 +382,8 @@ class RandomForest
* @param minimumGainSplit Minimum gain for splitting a decision tree node.
* @param maximumDepth Maximum depth for the tree.
* @param dimensionSelector Instantiated dimension selection policy.
* @param warmStart When set to `true`, it fits new trees and add them to the
* previous forest else a new forest is trained from scratch.
* @tparam UseWeights Whether or not to use the weights parameter.
* @tparam UseDatasetInfo Whether or not to use the datasetInfo parameter.
* @tparam MatType The type of data matrix (i.e. arma::mat).
@@ -369,10 +399,14 @@ class RandomForest
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType& dimensionSelector);
DimensionSelectionType& dimensionSelector,
const bool warmStart = false);
//! The trees in the forest.
std::vector<DecisionTreeType> trees;
//! The average gain of the forest.
double avgGain;
};
} // namespace tree
@@ -22,16 +22,31 @@ template<
typename FitnessFunction,
typename DimensionSelectionType,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename ElemType
template<typename> class CategoricalSplitType
>
RandomForest<
FitnessFunction,
DimensionSelectionType,
NumericSplitType,
CategoricalSplitType
>::RandomForest() :
avgGain(0.0)
{
// Nothing to do here.
}
template<
typename FitnessFunction,
typename DimensionSelectionType,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType
>
template<typename MatType>
RandomForest<
FitnessFunction,
DimensionSelectionType,
NumericSplitType,
CategoricalSplitType,
ElemType
CategoricalSplitType
>::RandomForest(const MatType& dataset,
const arma::Row<size_t>& labels,
const size_t numClasses,
@@ -39,29 +54,29 @@ RandomForest<
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType dimensionSelector)
DimensionSelectionType dimensionSelector) :
avgGain(0.0)
{
// Pass off work to the Train() method.
data::DatasetInfo info; // Ignored.
arma::rowvec weights; // Fake weights, not used.
Train<false, false>(dataset, info, labels, numClasses, weights, numTrees,
minimumLeafSize, minimumGainSplit, maximumDepth, dimensionSelector);
minimumLeafSize, minimumGainSplit, maximumDepth, dimensionSelector,
false);
}
template<
typename FitnessFunction,
typename DimensionSelectionType,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename ElemType
template<typename> class CategoricalSplitType
>
template<typename MatType>
RandomForest<
FitnessFunction,
DimensionSelectionType,
NumericSplitType,
CategoricalSplitType,
ElemType
CategoricalSplitType
>::RandomForest(const MatType& dataset,
const data::DatasetInfo& datasetInfo,
const arma::Row<size_t>& labels,
@@ -70,29 +85,28 @@ RandomForest<
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType dimensionSelector)
DimensionSelectionType dimensionSelector):
avgGain(0.0)
{
// Pass off work to the Train() method.
arma::rowvec weights; // Fake weights, not used.
Train<false, true>(dataset, datasetInfo, labels, numClasses, weights,
numTrees, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
dimensionSelector, false);
}
template<
typename FitnessFunction,
typename DimensionSelectionType,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename ElemType
template<typename> class CategoricalSplitType
>
template<typename MatType>
RandomForest<
FitnessFunction,
DimensionSelectionType,
NumericSplitType,
CategoricalSplitType,
ElemType
CategoricalSplitType
>::RandomForest(const MatType& dataset,
const arma::Row<size_t>& labels,
const size_t numClasses,
@@ -101,28 +115,28 @@ RandomForest<
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType dimensionSelector)
DimensionSelectionType dimensionSelector) :
avgGain(0.0)
{
// Pass off work to the Train() method.
data::DatasetInfo info; // Ignored by Train().
Train<true, false>(dataset, info, labels, numClasses, weights, numTrees,
minimumLeafSize, minimumGainSplit, maximumDepth, dimensionSelector);
minimumLeafSize, minimumGainSplit, maximumDepth, dimensionSelector,
false);
}
template<
typename FitnessFunction,
typename DimensionSelectionType,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename ElemType
template<typename> class CategoricalSplitType
>
template<typename MatType>
RandomForest<
FitnessFunction,
DimensionSelectionType,
NumericSplitType,
CategoricalSplitType,
ElemType
CategoricalSplitType
>::RandomForest(const MatType& dataset,
const data::DatasetInfo& datasetInfo,
const arma::Row<size_t>& labels,
@@ -132,27 +146,27 @@ RandomForest<
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType dimensionSelector)
DimensionSelectionType dimensionSelector) :
avgGain(0.0)
{
// Pass off work to the Train() method.
Train<true, true>(dataset, datasetInfo, labels, numClasses, weights, numTrees,
minimumLeafSize, minimumGainSplit, maximumDepth, dimensionSelector);
Train<true, true>(dataset, datasetInfo, labels, numClasses, weights,
numTrees, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector, false);
}
template<
typename FitnessFunction,
typename DimensionSelectionType,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename ElemType
template<typename> class CategoricalSplitType
>
template<typename MatType>
double RandomForest<
FitnessFunction,
DimensionSelectionType,
NumericSplitType,
CategoricalSplitType,
ElemType
CategoricalSplitType
>::Train(const MatType& dataset,
const arma::Row<size_t>& labels,
const size_t numClasses,
@@ -160,30 +174,29 @@ double RandomForest<
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
const bool warmStart,
DimensionSelectionType dimensionSelector)
{
// Pass off to Train().
data::DatasetInfo info; // Ignored by Train().
data::DatasetInfo datasetInfo; // Ignored by Train().
arma::rowvec weights; // Ignored by Train().
return Train<false, false>(dataset, info, labels, numClasses, weights,
return Train<false, false>(dataset, datasetInfo, labels, numClasses, weights,
numTrees, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
dimensionSelector, warmStart);
}
template<
typename FitnessFunction,
typename DimensionSelectionType,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename ElemType
template<typename> class CategoricalSplitType
>
template<typename MatType>
double RandomForest<
FitnessFunction,
DimensionSelectionType,
NumericSplitType,
CategoricalSplitType,
ElemType
CategoricalSplitType
>::Train(const MatType& dataset,
const data::DatasetInfo& datasetInfo,
const arma::Row<size_t>& labels,
@@ -192,29 +205,28 @@ double RandomForest<
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
const bool warmStart,
DimensionSelectionType dimensionSelector)
{
// Pass off to Train().
arma::rowvec weights; // Ignored by Train().
return Train<false, true>(dataset, datasetInfo, labels, numClasses, weights,
numTrees, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
dimensionSelector, warmStart);
}
template<
typename FitnessFunction,
typename DimensionSelectionType,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename ElemType
template<typename> class CategoricalSplitType
>
template<typename MatType>
double RandomForest<
FitnessFunction,
DimensionSelectionType,
NumericSplitType,
CategoricalSplitType,
ElemType
CategoricalSplitType
>::Train(const MatType& dataset,
const arma::Row<size_t>& labels,
const size_t numClasses,
@@ -223,29 +235,28 @@ double RandomForest<
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
const bool warmStart,
DimensionSelectionType dimensionSelector)
{
// Pass off to Train().
data::DatasetInfo info; // Ignored by Train().
return Train<false, false>(dataset, info, labels, numClasses, weights,
data::DatasetInfo datasetInfo; // Ignored by Train().
return Train<false, false>(dataset, datasetInfo, labels, numClasses, weights,
numTrees, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
dimensionSelector, warmStart);
}
template<
typename FitnessFunction,
typename DimensionSelectionType,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename ElemType
template<typename> class CategoricalSplitType
>
template<typename MatType>
double RandomForest<
FitnessFunction,
DimensionSelectionType,
NumericSplitType,
CategoricalSplitType,
ElemType
CategoricalSplitType
>::Train(const MatType& dataset,
const data::DatasetInfo& datasetInfo,
const arma::Row<size_t>& labels,
@@ -255,28 +266,27 @@ double RandomForest<
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
const bool warmStart,
DimensionSelectionType dimensionSelector)
{
// Pass off to Train().
return Train<true, true>(dataset, datasetInfo, labels, numClasses, weights,
numTrees, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
dimensionSelector, warmStart);
}
template<
typename FitnessFunction,
typename DimensionSelectionType,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename ElemType
template<typename> class CategoricalSplitType
>
template<typename VecType>
size_t RandomForest<
FitnessFunction,
DimensionSelectionType,
NumericSplitType,
CategoricalSplitType,
ElemType
CategoricalSplitType
>::Classify(const VecType& point) const
{
// Pass off to another Classify() overload.
@@ -291,16 +301,14 @@ template<
typename FitnessFunction,
typename DimensionSelectionType,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename ElemType
template<typename> class CategoricalSplitType
>
template<typename VecType>
void RandomForest<
FitnessFunction,
DimensionSelectionType,
NumericSplitType,
CategoricalSplitType,
ElemType
CategoricalSplitType
>::Classify(const VecType& point,
size_t& prediction,
arma::vec& probabilities) const
@@ -338,16 +346,14 @@ template<
typename FitnessFunction,
typename DimensionSelectionType,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename ElemType
template<typename> class CategoricalSplitType
>
template<typename MatType>
void RandomForest<
FitnessFunction,
DimensionSelectionType,
NumericSplitType,
CategoricalSplitType,
ElemType
CategoricalSplitType
>::Classify(const MatType& data,
arma::Row<size_t>& predictions) const
{
@@ -373,16 +379,14 @@ template<
typename FitnessFunction,
typename DimensionSelectionType,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename ElemType
template<typename> class CategoricalSplitType
>
template<typename MatType>
void RandomForest<
FitnessFunction,
DimensionSelectionType,
NumericSplitType,
CategoricalSplitType,
ElemType
CategoricalSplitType
>::Classify(const MatType& data,
arma::Row<size_t>& predictions,
arma::mat& probabilities) const
@@ -411,16 +415,14 @@ template<
typename FitnessFunction,
typename DimensionSelectionType,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename ElemType
template<typename> class CategoricalSplitType
>
template<typename Archive>
void RandomForest<
FitnessFunction,
DimensionSelectionType,
NumericSplitType,
CategoricalSplitType,
ElemType
CategoricalSplitType
>::serialize(Archive& ar, const uint32_t /* version */)
{
size_t numTrees;
@@ -436,22 +438,21 @@ void RandomForest<
trees.resize(numTrees);
ar(CEREAL_NVP(trees));
ar(CEREAL_NVP(avgGain));
}
template<
typename FitnessFunction,
typename DimensionSelectionType,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename ElemType
template<typename> class CategoricalSplitType
>
template<bool UseWeights, bool UseDatasetInfo, typename MatType>
double RandomForest<
FitnessFunction,
DimensionSelectionType,
NumericSplitType,
CategoricalSplitType,
ElemType
CategoricalSplitType
>::Train(const MatType& dataset,
const data::DatasetInfo& datasetInfo,
const arma::Row<size_t>& labels,
@@ -461,13 +462,20 @@ double RandomForest<
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType& dimensionSelector)
DimensionSelectionType& dimensionSelector,
const bool warmStart)
{
// Train each tree individually.
trees.resize(numTrees); // This will fill the vector with untrained trees.
double avgGain = 0.0;
// Reset the forest if we are not doing a warm-start.
if (!warmStart)
trees.clear();
const size_t oldNumTrees = trees.size();
trees.resize(trees.size() + numTrees);
#pragma omp parallel for reduction( + : avgGain)
// Convert avgGain to total gain.
double totalGain = avgGain * oldNumTrees;
// Train each tree individually.
#pragma omp parallel for reduction( + : totalGain)
for (omp_size_t i = 0; i < numTrees; ++i)
{
Timer::Start("bootstrap");
@@ -478,40 +486,44 @@ double RandomForest<
bootstrapLabels, bootstrapWeights);
Timer::Stop("bootstrap");
// Now build the decision tree.
Timer::Start("train_tree");
if (UseWeights)
{
if (UseDatasetInfo)
{
avgGain += trees[i].Train(bootstrapDataset, datasetInfo,
bootstrapLabels, numClasses, bootstrapWeights, minimumLeafSize,
minimumGainSplit, maximumDepth, dimensionSelector);
totalGain += trees[oldNumTrees + i].Train(bootstrapDataset,
datasetInfo, bootstrapLabels, numClasses, bootstrapWeights,
minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
}
else
{
avgGain += trees[i].Train(bootstrapDataset, bootstrapLabels, numClasses,
bootstrapWeights, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
totalGain += trees[oldNumTrees + i].Train(bootstrapDataset,
bootstrapLabels, numClasses, bootstrapWeights, minimumLeafSize,
minimumGainSplit, maximumDepth, dimensionSelector);
}
}
else
{
if (UseDatasetInfo)
{
avgGain += trees[i].Train(bootstrapDataset, datasetInfo,
bootstrapLabels, numClasses, minimumLeafSize, minimumGainSplit,
maximumDepth, dimensionSelector);
totalGain += trees[oldNumTrees + i].Train(bootstrapDataset,
datasetInfo, bootstrapLabels, numClasses, minimumLeafSize,
minimumGainSplit, maximumDepth, dimensionSelector);
}
else
{
avgGain += trees[i].Train(bootstrapDataset, bootstrapLabels, numClasses,
minimumLeafSize, minimumGainSplit, maximumDepth, dimensionSelector);
totalGain += trees[oldNumTrees + i].Train(bootstrapDataset,
bootstrapLabels, numClasses, minimumLeafSize, minimumGainSplit,
maximumDepth, dimensionSelector);
}
}
Timer::Stop("train_tree");
}
return avgGain / numTrees;
avgGain = totalGain / trees.size();
return avgGain;
}
} // namespace tree
@@ -130,6 +130,8 @@ PARAM_INT_IN("subspace_dim", "Dimensionality of random subspace to use for "
"d", 0);
PARAM_INT_IN("seed", "Random seed. If 0, 'std::time(NULL)' is used.", "s", 0);
PARAM_FLAG("warm_start", "If true and passed along with `training` and "
"`input_model` then trains more trees on top of existing model.", "w");
/**
* This is the class that we will serialize. It is a pretty simple wrapper
@@ -167,7 +169,11 @@ static void mlpackMain()
math::RandomSeed((size_t) std::time(NULL));
// Check for incompatible input parameters.
RequireOnlyOnePassed({ "training", "input_model" }, true);
if (!IO::HasParam("warm_start"))
RequireOnlyOnePassed({ "training", "input_model" }, true);
else
// When warm_start is passed, training and input_model must also be passed.
RequireNoneOrAllPassed({"warm_start", "training", "input_model"}, true);
ReportIgnoredParam({{ "training", false }}, "print_training_accuracy");
ReportIgnoredParam({{ "test", false }}, "test_labels");
@@ -201,10 +207,17 @@ static void mlpackMain()
ReportIgnoredParam({{ "training", false }}, "minimum_leaf_size");
RandomForestModel* rfModel;
// Input model is loaded when we are either doing warm-started training or
// else we are making predictions only or both.
if (IO::HasParam("input_model"))
rfModel = IO::GetParam<RandomForestModel*>("input_model");
// Handles the case when we are training new forest from scratch.
else
rfModel = new RandomForestModel();
if (IO::HasParam("training"))
{
Timer::Start("rf_training");
rfModel = new RandomForestModel();
// Train the model on the given input data.
arma::mat data = std::move(IO::GetParam<arma::mat>("training"));
@@ -233,7 +246,8 @@ static void mlpackMain()
// Train the model.
rfModel->rf.Train(data, labels, numClasses, numTrees, minimumLeafSize,
minimumGainSplit, maxDepth, mrds);
minimumGainSplit, maxDepth, IO::HasParam("warm_start"), mrds);
Timer::Stop("rf_training");
// Did we want training accuracy?
@@ -251,11 +265,6 @@ static void mlpackMain()
Timer::Stop("rf_prediction");
}
}
else
{
// Then we must be loading a model.
rfModel = IO::GetParam<RandomForestModel*>("input_model");
}
if (IO::HasParam("test"))
{
@@ -33,6 +33,7 @@
#include <mlpack/methods/ann/activation_functions/poisson1_function.hpp>
#include <mlpack/methods/ann/activation_functions/gaussian_function.hpp>
#include <mlpack/methods/ann/activation_functions/hard_swish_function.hpp>
#include <mlpack/methods/ann/activation_functions/tanh_exponential_function.hpp>
#include "catch.hpp"
@@ -1220,3 +1221,23 @@ TEST_CASE("HardSwishFunctionTest", "[ActivationFunctionsTest]")
CheckDerivativeCorrect<HardSwishFunction>
(desiredActivations, desiredDerivatives);
}
/**
* Basic test of the TanhExp function.
*/
TEST_CASE("TanhExpFunctionTest", "[ActivationFunctionsTest]")
{
const arma::colvec activationData("-2 3.2 4.5 1 -1 2 0");
// Hand-calculated values.
const arma::colvec desiredActivations("-0.26903 3.20000 4.50000 \
0.991329 -0.352135 2.0 0.0000");
// Hand-calculated values.
const arma::colvec desiredDerivatives("0.523051 1.0000 1.0000 \
1.03924 0.449818 1.00002 0.761594");
CheckActivationCorrect<TanhExpFunction>(activationData, desiredActivations);
CheckDerivativeCorrect<TanhExpFunction>(desiredActivations, desiredDerivatives);
}
+54 -3
View File
@@ -3883,6 +3883,57 @@ TEST_CASE("LpMaxPoolingTestCase", "[ANNLayerTest]")
REQUIRE(output.n_elem == 4);
}
/**
* Simple test for Mean Pooling layer.
*/
TEST_CASE("MeanPoolingTestCase", "[ANNLayerTest]")
{
// For rectangular input to pooling layers.
arma::mat input = arma::mat(28, 1);
input.zeros();
input(0) = input(16) = 1;
input(1) = input(17) = 2;
input(2) = input(18) = 3;
input(3) = input(19) = 4;
input(4) = input(20) = 5;
input(5) = input(23) = 6;
input(6) = input(24) = 7;
input(14) = input(25) = 8;
input(15) = input(26) = 9;
MeanPooling<> module1(2, 2, 2, 2, false);
MeanPooling<> module2(2, 2, 2, 2, true);
module1.InputWidth() = 7;
module1.InputHeight() = 4;
module2.InputWidth() = 7;
module2.InputHeight() = 4;
// Calculated using torch.nn.MeanPool2d().
arma::mat result1, result2;
result1 << 0.7500 << 4.2500 << arma::endr
<< 1.7500 << 4.0000 << arma::endr
<< 2.7500 << 6.0000 << arma::endr
<< 3.5000 << 2.5000 << arma::endr;
result2 << 0.7500 << 4.2500 << arma::endr
<< 1.7500 << 4.0000 << arma::endr
<< 2.7500 << 6.0000 << arma::endr;
arma::mat output1, output2;
module1.Forward(input, output1);
module2.Forward(input, output2);
output1.reshape(4, 2);
output2.reshape(3, 2);
CheckMatrices(output1, result1, 1e-1);
CheckMatrices(output2, result2, 1e-1);
arma::mat delta1, delta2;
module1.Backward(input, output1, delta1);
REQUIRE(arma::accu(delta1) == 25.5);
module2.Backward(input, output2, delta2);
REQUIRE(arma::accu(delta2) == 19.5);
}
/**
* Simple test for Max Pooling layer.
*/
@@ -4155,7 +4206,7 @@ TEST_CASE("AdaptiveMeanPoolingTestCase", "[ANNLayerTest]")
REQUIRE(output.n_cols == 1);
// Test the Backward Function.
module1.Backward(input, output, delta);
REQUIRE(arma::accu(delta) == 7.0);
REQUIRE(arma::accu(delta) == 19.75);
// For Square input.
input = arma::mat(9, 1);
@@ -4177,7 +4228,7 @@ TEST_CASE("AdaptiveMeanPoolingTestCase", "[ANNLayerTest]")
REQUIRE(output.n_cols == 1);
// Test the Backward Function.
module2.Backward(input, output, delta);
REQUIRE(arma::accu(delta) == 0.0);
REQUIRE(arma::accu(delta) == 4.50);
// For Square input.
input = arma::mat(16, 1);
@@ -4219,7 +4270,7 @@ TEST_CASE("AdaptiveMeanPoolingTestCase", "[ANNLayerTest]")
REQUIRE(output.n_cols == 1);
// Test the Backward Function.
module4.Backward(input, output, delta);
REQUIRE(arma::accu(delta) == 1.5);
REQUIRE(arma::accu(delta) == 2.25);
}
TEST_CASE("TransposedConvolutionalLayerOptionalParameterTest", "[ANNLayerTest]")
+222 -166
View File
@@ -1,9 +1,9 @@
/*
* Catch v2.13.4
* Generated: 2020-12-29 14:48:00.116107
* Catch v2.13.5
* Generated: 2021-04-10 23:43:17.560525
* ----------------------------------------------------------
* This file has been merged from multiple headers. Please don't edit it directly
* Copyright (c) 2020 Two Blue Cubes Ltd. All rights reserved.
* Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,7 +15,7 @@
#define CATCH_VERSION_MAJOR 2
#define CATCH_VERSION_MINOR 13
#define CATCH_VERSION_PATCH 4
#define CATCH_VERSION_PATCH 5
#ifdef __clang__
# pragma clang system_header
@@ -66,13 +66,16 @@
#if !defined(CATCH_CONFIG_IMPL_ONLY)
// start catch_platform.h
// See e.g.:
// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
#ifdef __APPLE__
# include <TargetConditionals.h>
# if TARGET_OS_OSX == 1
# define CATCH_PLATFORM_MAC
# elif TARGET_OS_IPHONE == 1
# define CATCH_PLATFORM_IPHONE
# endif
# include <TargetConditionals.h>
# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
(defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
# define CATCH_PLATFORM_MAC
# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
# define CATCH_PLATFORM_IPHONE
# endif
#elif defined(linux) || defined(__linux) || defined(__linux__)
# define CATCH_PLATFORM_LINUX
@@ -132,9 +135,9 @@ namespace Catch {
#endif
// We have to avoid both ICC and Clang, because they try to mask themselves
// as gcc, and we want only GCC in this block
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__)
// Only GCC compiler should be used in this block, so other compilers trying to
// mask themselves as GCC should be ignored.
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
@@ -7054,8 +7057,8 @@ namespace Catch {
double b2 = bias - z1;
double a1 = a(b1);
double a2 = a(b2);
auto lo = std::max(cumn(a1), 0);
auto hi = std::min(cumn(a2), n - 1);
auto lo = (std::max)(cumn(a1), 0);
auto hi = (std::min)(cumn(a2), n - 1);
return { point, resample[lo], resample[hi], confidence_level };
}
@@ -7124,7 +7127,9 @@ namespace Catch {
}
template <typename Clock>
EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) {
auto time_limit = std::min(resolution * clock_cost_estimation_tick_limit, FloatDuration<Clock>(clock_cost_estimation_time_limit));
auto time_limit = (std::min)(
resolution * clock_cost_estimation_tick_limit,
FloatDuration<Clock>(clock_cost_estimation_time_limit));
auto time_clock = [](int k) {
return Detail::measure<Clock>([k] {
for (int i = 0; i < k; ++i) {
@@ -7771,7 +7776,7 @@ namespace Catch {
double sb = stddev.point;
double mn = mean.point / n;
double mg_min = mn / 2.;
double sg = std::min(mg_min / 4., sb / std::sqrt(n));
double sg = (std::min)(mg_min / 4., sb / std::sqrt(n));
double sg2 = sg * sg;
double sb2 = sb * sb;
@@ -7790,7 +7795,7 @@ namespace Catch {
return (nc / n) * (sb2 - nc * sg2);
};
return std::min(var_out(1), var_out(std::min(c_max(0.), c_max(mg_min)))) / sb2;
return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2;
}
bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
@@ -7980,86 +7985,58 @@ namespace Catch {
// start catch_fatal_condition.h
// start catch_windows_h_proxy.h
#if defined(CATCH_PLATFORM_WINDOWS)
#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
# define CATCH_DEFINED_NOMINMAX
# define NOMINMAX
#endif
#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
#ifdef __AFXDLL
#include <AfxWin.h>
#else
#include <windows.h>
#endif
#ifdef CATCH_DEFINED_NOMINMAX
# undef NOMINMAX
#endif
#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
# undef WIN32_LEAN_AND_MEAN
#endif
#endif // defined(CATCH_PLATFORM_WINDOWS)
// end catch_windows_h_proxy.h
#if defined( CATCH_CONFIG_WINDOWS_SEH )
#include <cassert>
namespace Catch {
struct FatalConditionHandler {
static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo);
FatalConditionHandler();
static void reset();
~FatalConditionHandler();
private:
static bool isSet;
static ULONG guaranteeSize;
static PVOID exceptionHandlerHandle;
};
} // namespace Catch
#elif defined ( CATCH_CONFIG_POSIX_SIGNALS )
#include <signal.h>
namespace Catch {
struct FatalConditionHandler {
static bool isSet;
static struct sigaction oldSigActions[];
static stack_t oldSigStack;
static char altStackMem[];
static void handleSignal( int sig );
// Wrapper for platform-specific fatal error (signals/SEH) handlers
//
// Tries to be cooperative with other handlers, and not step over
// other handlers. This means that unknown structured exceptions
// are passed on, previous signal handlers are called, and so on.
//
// Can only be instantiated once, and assumes that once a signal
// is caught, the binary will end up terminating. Thus, there
class FatalConditionHandler {
bool m_started = false;
// Install/disengage implementation for specific platform.
// Should be if-defed to work on current platform, can assume
// engage-disengage 1:1 pairing.
void engage_platform();
void disengage_platform();
public:
// Should also have platform-specific implementations as needed
FatalConditionHandler();
~FatalConditionHandler();
static void reset();
void engage() {
assert(!m_started && "Handler cannot be installed twice.");
m_started = true;
engage_platform();
}
void disengage() {
assert(m_started && "Handler cannot be uninstalled without being installed first");
m_started = false;
disengage_platform();
}
};
} // namespace Catch
#else
namespace Catch {
struct FatalConditionHandler {
void reset();
//! Simple RAII guard for (dis)engaging the FatalConditionHandler
class FatalConditionHandlerGuard {
FatalConditionHandler* m_handler;
public:
FatalConditionHandlerGuard(FatalConditionHandler* handler):
m_handler(handler) {
m_handler->engage();
}
~FatalConditionHandlerGuard() {
m_handler->disengage();
}
};
}
#endif
} // end namespace Catch
// end catch_fatal_condition.h
#include <string>
@@ -8185,6 +8162,7 @@ namespace Catch {
std::vector<SectionEndInfo> m_unfinishedSections;
std::vector<ITracker*> m_activeSections;
TrackerContext m_trackerContext;
FatalConditionHandler m_fatalConditionhandler;
bool m_lastAssertionPassed = false;
bool m_shouldReportUnexpected = true;
bool m_includeSuccessfulResults;
@@ -10057,6 +10035,36 @@ namespace Catch {
}
// end catch_errno_guard.h
// start catch_windows_h_proxy.h
#if defined(CATCH_PLATFORM_WINDOWS)
#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
# define CATCH_DEFINED_NOMINMAX
# define NOMINMAX
#endif
#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
#ifdef __AFXDLL
#include <AfxWin.h>
#else
#include <windows.h>
#endif
#ifdef CATCH_DEFINED_NOMINMAX
# undef NOMINMAX
#endif
#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
# undef WIN32_LEAN_AND_MEAN
#endif
#endif // defined(CATCH_PLATFORM_WINDOWS)
// end catch_windows_h_proxy.h
#include <sstream>
namespace Catch {
@@ -10573,7 +10581,7 @@ namespace Catch {
// Extracts the actual name part of an enum instance
// In other words, it returns the Blue part of Bikeshed::Colour::Blue
StringRef extractInstanceName(StringRef enumInstance) {
// Find last occurence of ":"
// Find last occurrence of ":"
size_t name_start = enumInstance.size();
while (name_start > 0 && enumInstance[name_start - 1] != ':') {
--name_start;
@@ -10735,25 +10743,47 @@ namespace Catch {
// end catch_exception_translator_registry.cpp
// start catch_fatal_condition.cpp
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
#include <algorithm>
#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS )
namespace Catch {
// If neither SEH nor signal handling is required, the handler impls
// do not have to do anything, and can be empty.
FatalConditionHandler::engage_platform() {}
FatalConditionHandler::disengage_platform() {}
FatalConditionHandler::FatalConditionHandler() = default;
FatalConditionHandler::~FatalConditionHandler() = default;
} // end namespace Catch
#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS
#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS )
#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time"
#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS
#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
namespace {
// Report the error condition
//! Signals fatal error message to the run context
void reportFatal( char const * const message ) {
Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message );
}
}
#endif // signals/SEH handling
//! Minimal size Catch2 needs for its own fatal error handling.
//! Picked anecdotally, so it might not be sufficient on all
//! platforms, and for all configurations.
constexpr std::size_t minStackSizeForErrors = 32 * 1024;
} // end unnamed namespace
#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS
#if defined( CATCH_CONFIG_WINDOWS_SEH )
namespace Catch {
struct SignalDefs { DWORD id; const char* name; };
// There is no 1-1 mapping between signals and windows exceptions.
@@ -10766,7 +10796,7 @@ namespace Catch {
{ static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" },
};
LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
for (auto const& def : signalDefs) {
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
reportFatal(def.name);
@@ -10777,38 +10807,50 @@ namespace Catch {
return EXCEPTION_CONTINUE_SEARCH;
}
FatalConditionHandler::FatalConditionHandler() {
isSet = true;
// 32k seems enough for Catch to handle stack overflow,
// but the value was found experimentally, so there is no strong guarantee
guaranteeSize = 32 * 1024;
exceptionHandlerHandle = nullptr;
// Register as first handler in current chain
exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
// Pass in guarantee size to be filled
SetThreadStackGuarantee(&guaranteeSize);
}
// Since we do not support multiple instantiations, we put these
// into global variables and rely on cleaning them up in outlined
// constructors/destructors
static PVOID exceptionHandlerHandle = nullptr;
void FatalConditionHandler::reset() {
if (isSet) {
RemoveVectoredExceptionHandler(exceptionHandlerHandle);
SetThreadStackGuarantee(&guaranteeSize);
exceptionHandlerHandle = nullptr;
isSet = false;
// For MSVC, we reserve part of the stack memory for handling
// memory overflow structured exception.
FatalConditionHandler::FatalConditionHandler() {
ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors);
if (!SetThreadStackGuarantee(&guaranteeSize)) {
// We do not want to fully error out, because needing
// the stack reserve should be rare enough anyway.
Catch::cerr()
<< "Failed to reserve piece of stack."
<< " Stack overflows will not be reported successfully.";
}
}
FatalConditionHandler::~FatalConditionHandler() {
reset();
// We do not attempt to unset the stack guarantee, because
// Windows does not support lowering the stack size guarantee.
FatalConditionHandler::~FatalConditionHandler() = default;
void FatalConditionHandler::engage_platform() {
// Register as first handler in current chain
exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
if (!exceptionHandlerHandle) {
CATCH_RUNTIME_ERROR("Could not register vectored exception handler");
}
}
bool FatalConditionHandler::isSet = false;
ULONG FatalConditionHandler::guaranteeSize = 0;
PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;
void FatalConditionHandler::disengage_platform() {
if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) {
CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler");
}
exceptionHandlerHandle = nullptr;
}
} // namespace Catch
} // end namespace Catch
#elif defined( CATCH_CONFIG_POSIX_SIGNALS )
#endif // CATCH_CONFIG_WINDOWS_SEH
#if defined( CATCH_CONFIG_POSIX_SIGNALS )
#include <signal.h>
namespace Catch {
@@ -10817,10 +10859,6 @@ namespace Catch {
const char* name;
};
// 32kb for the alternate stack seems to be sufficient. However, this value
// is experimentally determined, so that's not guaranteed.
static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
static SignalDefs signalDefs[] = {
{ SIGINT, "SIGINT - Terminal interrupt signal" },
{ SIGILL, "SIGILL - Illegal instruction signal" },
@@ -10830,7 +10868,32 @@ namespace Catch {
{ SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
};
void FatalConditionHandler::handleSignal( int sig ) {
// Older GCCs trigger -Wmissing-field-initializers for T foo = {}
// which is zero initialization, but not explicit. We want to avoid
// that.
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
static char* altStackMem = nullptr;
static std::size_t altStackSize = 0;
static stack_t oldSigStack{};
static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{};
static void restorePreviousSignalHandlers() {
// We set signal handlers back to the previous ones. Hopefully
// nobody overwrote them in the meantime, and doesn't expect
// their signal handlers to live past ours given that they
// installed them after ours..
for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
}
// Return the old stack
sigaltstack(&oldSigStack, nullptr);
}
static void handleSignal( int sig ) {
char const * name = "<unknown signal>";
for (auto const& def : signalDefs) {
if (sig == def.id) {
@@ -10838,16 +10901,33 @@ namespace Catch {
break;
}
}
reset();
reportFatal(name);
// We need to restore previous signal handlers and let them do
// their thing, so that the users can have the debugger break
// when a signal is raised, and so on.
restorePreviousSignalHandlers();
reportFatal( name );
raise( sig );
}
FatalConditionHandler::FatalConditionHandler() {
isSet = true;
assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists");
if (altStackSize == 0) {
altStackSize = std::max(static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors);
}
altStackMem = new char[altStackSize]();
}
FatalConditionHandler::~FatalConditionHandler() {
delete[] altStackMem;
// We signal that another instance can be constructed by zeroing
// out the pointer.
altStackMem = nullptr;
}
void FatalConditionHandler::engage_platform() {
stack_t sigStack;
sigStack.ss_sp = altStackMem;
sigStack.ss_size = sigStackSize;
sigStack.ss_size = altStackSize;
sigStack.ss_flags = 0;
sigaltstack(&sigStack, &oldSigStack);
struct sigaction sa = { };
@@ -10859,40 +10939,17 @@ namespace Catch {
}
}
FatalConditionHandler::~FatalConditionHandler() {
reset();
}
void FatalConditionHandler::reset() {
if( isSet ) {
// Set signals back to previous values -- hopefully nobody overwrote them in the meantime
for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
}
// Return the old stack
sigaltstack(&oldSigStack, nullptr);
isSet = false;
}
}
bool FatalConditionHandler::isSet = false;
struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
stack_t FatalConditionHandler::oldSigStack = {};
char FatalConditionHandler::altStackMem[sigStackSize] = {};
} // namespace Catch
#else
namespace Catch {
void FatalConditionHandler::reset() {}
}
#endif // signals/SEH handling
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
void FatalConditionHandler::disengage_platform() {
restorePreviousSignalHandlers();
}
} // end namespace Catch
#endif // CATCH_CONFIG_POSIX_SIGNALS
// end catch_fatal_condition.cpp
// start catch_generators.cpp
@@ -11447,7 +11504,8 @@ namespace {
return lhs == rhs;
}
auto ulpDiff = std::abs(lc - rc);
// static cast as a workaround for IBM XLC
auto ulpDiff = std::abs(static_cast<FP>(lc - rc));
return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
}
@@ -11621,7 +11679,6 @@ Floating::WithinRelMatcher WithinRel(float target) {
} // namespace Matchers
} // namespace Catch
// end catch_matchers_floating.cpp
// start catch_matchers_generic.cpp
@@ -12955,9 +13012,8 @@ namespace Catch {
}
void RunContext::invokeActiveTestCase() {
FatalConditionHandler fatalConditionHandler; // Handle signals
FatalConditionHandlerGuard _(&m_fatalConditionhandler);
m_activeTestCase->invoke();
fatalConditionHandler.reset();
}
void RunContext::handleUnfinishedSections() {
@@ -15320,7 +15376,7 @@ namespace Catch {
}
Version const& libraryVersion() {
static Version version( 2, 13, 4, "", 0 );
static Version version( 2, 13, 5, "", 0 );
return version;
}
+66 -6
View File
@@ -289,7 +289,7 @@ TEST_CASE("BestBinaryNumericSplitSimpleSplitTest", "[DecisionTreeTest]")
weights.ones();
arma::vec classProbabilities;
BestBinaryNumericSplit<GiniGain>::template AuxiliarySplitInfo<double> aux;
BestBinaryNumericSplit<GiniGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = GiniGain::Evaluate<false>(labels, 2, weights);
@@ -327,7 +327,7 @@ TEST_CASE("BestBinaryNumericSplitMinSamplesTest", "[DecisionTreeTest]")
arma::rowvec weights(labels.n_elem);
arma::vec classProbabilities;
BestBinaryNumericSplit<GiniGain>::template AuxiliarySplitInfo<double> aux;
BestBinaryNumericSplit<GiniGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = GiniGain::Evaluate<false>(labels, 2, weights);
@@ -363,7 +363,7 @@ TEST_CASE("BestBinaryNumericSplitNoGainTest", "[DecisionTreeTest]")
}
arma::vec classProbabilities;
BestBinaryNumericSplit<GiniGain>::template AuxiliarySplitInfo<double> aux;
BestBinaryNumericSplit<GiniGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = GiniGain::Evaluate<false>(labels, 2, weights);
@@ -388,7 +388,7 @@ TEST_CASE("AllCategoricalSplitSimpleSplitTest", "[DecisionTreeTest]")
weights.ones();
arma::vec classProbabilities;
AllCategoricalSplit<GiniGain>::template AuxiliarySplitInfo<double> aux;
AllCategoricalSplit<GiniGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = GiniGain::Evaluate<false>(labels, 3, weights);
@@ -424,7 +424,7 @@ TEST_CASE("AllCategoricalSplitMinSamplesTest", "[DecisionTreeTest]")
weights.ones();
arma::vec classProbabilities;
AllCategoricalSplit<GiniGain>::template AuxiliarySplitInfo<double> aux;
AllCategoricalSplit<GiniGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = GiniGain::Evaluate<false>(labels, 3, weights);
@@ -457,7 +457,7 @@ TEST_CASE("AllCategoricalSplitNoGainTest", "[DecisionTreeTest]")
}
arma::vec classProbabilities;
AllCategoricalSplit<GiniGain>::template AuxiliarySplitInfo<double> aux;
AllCategoricalSplit<GiniGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = GiniGain::Evaluate<false>(labels, 3, weights);
@@ -701,6 +701,66 @@ TEST_CASE("SimpleGeneralizationTest", "[DecisionTreeTest]")
REQUIRE(wdcorrect > 0.75);
}
/**
* Test that the decision tree generalizes reasonably when built on float data.
*/
TEST_CASE("SimpleGeneralizationFMatTest", "[DecisionTreeTest]")
{
arma::fmat inputData;
if (!data::Load("vc2.csv", inputData))
FAIL("Cannot load test dataset vc2.csv!");
arma::Row<size_t> labels;
if (!data::Load("vc2_labels.txt", labels))
FAIL("Cannot load labels for vc2_labels.txt");
// Initialize an all-ones weight matrix.
arma::rowvec weights(labels.n_cols, arma::fill::ones);
// Build decision tree.
DecisionTree<> d(inputData, labels, 3, 10 /* Leaf size of 10. */);
DecisionTree<> wd(inputData, labels, 3, weights, 10 /* Leaf size of 10. */);
// Load testing data.
arma::mat testData;
if (!data::Load("vc2_test.csv", testData))
FAIL("Cannot load test dataset vc2_test.csv!");
arma::Mat<size_t> trueTestLabels;
if (!data::Load("vc2_test_labels.txt", trueTestLabels))
FAIL("Cannot load labels for vc2_test_labels.txt");
// Get the predicted test labels.
arma::Row<size_t> predictions;
d.Classify(testData, predictions);
REQUIRE(predictions.n_elem == testData.n_cols);
// Figure out the accuracy.
double correct = 0.0;
for (size_t i = 0; i < predictions.n_elem; ++i)
if (predictions[i] == trueTestLabels[i])
++correct;
correct /= predictions.n_elem;
REQUIRE(correct > 0.75);
// Reset the prediction.
predictions.zeros();
wd.Classify(testData, predictions);
REQUIRE(predictions.n_elem == testData.n_cols);
// Figure out the accuracy.
double wdcorrect = 0.0;
for (size_t i = 0; i < predictions.n_elem; ++i)
if (predictions[i] == trueTestLabels[i])
++wdcorrect;
wdcorrect /= predictions.n_elem;
REQUIRE(wdcorrect > 0.75);
}
/**
* Test that we can build a decision tree on a simple categorical dataset.
*/
+1 -1
View File
@@ -27,7 +27,7 @@ using namespace mlpack;
using namespace mlpack::gmm;
/**
* Test GMM::Probability() for a single observation for a few cases.
* Test GMM::Probability() with a single observation at a time for a few cases.
*/
TEST_CASE("GMMProbabilityTest", "[GMMTest]")
+3 -2
View File
@@ -1079,10 +1079,11 @@ TEST_CASE("GaussianHMMPredictTest", "[HMMTest]")
{
double loglikelihood = 0;
arma::vec forwardLogProb;
for (size_t t = 0; t<emissionProb.size(); ++t)
for (size_t t = 0; t < emissionProb.size(); ++t)
{
loglikelihood = hmm.EmissionLogLikelihood(emissionProb.at(t),
loglikelihood, forwardLogProb);
loglikelihood,
forwardLogProb);
}
REQUIRE(loglikelihood == Approx(loglikelihoodRef).epsilon(1e-1));
}
+19
View File
@@ -2485,3 +2485,22 @@ TEST_CASE("DatasetMapperNonUniqueTest", "[LoadSaveTest]")
REQUIRE(dm.UnmapString(nan, 0, 1) == "goodbye");
REQUIRE(dm.UnmapString(nan, 0, 2) == "cheese");
}
/**
* Make sure if we load a CSV with a header, that that header doesn't get loaded
* as a point.
*/
TEST_CASE("LoadCSVHeaderTest", "[LoadSaveTest]")
{
fstream f;
f.open("test.csv", fstream::out);
f << "a, b, c, d" << endl;
f << "1, 2, 3, 4" << endl;
f << "5, 6, 7, 8" << endl;
arma::mat dataset;
data::Load("test.csv", dataset);
REQUIRE(dataset.n_rows == 4);
REQUIRE(dataset.n_cols == 2);
}
@@ -204,7 +204,8 @@ TEST_CASE_METHOD(RandomForestTestFixture, "RandomForestMaximumDepthTest",
}
/**
* Make sure only one of training data or pre-trained model is passed.
* Make sure only one of training data or pre-trained model is passed, when
* warm_start is not passed.
*/
TEST_CASE_METHOD(RandomForestTestFixture, "RandomForestTrainingVerTest",
"[RandomForestMainTest][BindingTests]")
@@ -453,3 +454,74 @@ TEST_CASE_METHOD(RandomForestTestFixture, "RandomForestDiffMaxDepthTest",
delete rf2;
delete rf3;
}
/**
* Make sure that training and input_model are both passed when warm_start is
* false.
*/
TEST_CASE_METHOD(RandomForestTestFixture, "RandomForestTrainingModelWarmStart",
"[RandomForestMainTest][BindingTests]")
{
arma::mat inputData;
if (!data::Load("vc2.csv", inputData))
FAIL("Cannot load train dataset vc2.csv!");
arma::Row<size_t> labels;
if (!data::Load("vc2_labels.txt", labels))
FAIL("Cannot load labels for vc2_labels.txt");
// Input training data.
SetInputParam("training", std::move(inputData));
SetInputParam("labels", std::move(labels));
mlpackMain();
// Setting warm_start flag.
SetInputParam("warm_start", false);
Log::Fatal.ignoreInput = true;
REQUIRE_THROWS_AS(mlpackMain(), std::runtime_error);
Log::Fatal.ignoreInput = false;
}
/**
* Ensuring that model does gets trained on top of existing one when warm_start
* and input_model are both passed.
*/
TEST_CASE_METHOD(RandomForestTestFixture, "RandomForestWarmStart",
"[RandomForestMainTest][BindingTests]")
{
arma::mat inputData;
if (!data::Load("vc2.csv", inputData))
FAIL("Cannot load train dataset vc2.csv!");
arma::Row<size_t> labels;
if (!data::Load("vc2_labels.txt", labels))
FAIL("Cannot load labels for vc2_labels.txt");
// Input training data.
SetInputParam("training", inputData);
SetInputParam("labels", labels);
mlpackMain();
// Old number of trees in the model.
size_t oldNumTrees =
IO::GetParam<RandomForestModel*>("output_model")->rf.NumTrees();
// Input training data.
SetInputParam("training", std::move(inputData));
SetInputParam("labels", std::move(labels));
SetInputParam("warm_start", true);
// Input pre-trained model.
SetInputParam("input_model",
IO::GetParam<RandomForestModel*>("output_model"));
mlpackMain();
size_t newNumTrees =
IO::GetParam<RandomForestModel*>("output_model")->rf.NumTrees();
REQUIRE(oldNumTrees + 10 == newNumTrees);
}
+62 -2
View File
@@ -457,14 +457,14 @@ TEST_CASE("RandomForestCategoricalTrainReturnEntropy", "[RandomForestTest]")
// Test random forest on unweighted categorical dataset.
RandomForest<> rf;
double entropy = rf.Train(fullData, di, fullLabels, 5, 15 /* 15 trees */, 1,
1e-7, 0, MultipleRandomDimensionSelect(3));
1e-7, 0, false, MultipleRandomDimensionSelect(3));
REQUIRE(std::isfinite(entropy) == true);
// Test random forest on weighted categorical dataset.
RandomForest<> wrf;
entropy = wrf.Train(fullData, di, fullLabels, 5, weights, 15 /* 15 trees */,
1, 1e-7, 0, MultipleRandomDimensionSelect(3));
1, 1e-7, 0, false, MultipleRandomDimensionSelect(3));
REQUIRE(std::isfinite(entropy) == true);
}
@@ -498,3 +498,63 @@ TEST_CASE("DifferentTreesTest", "[RandomForestTest]")
REQUIRE(success == true);
}
/**
* Test that RandomForest::Train() when passed warmStart = True trains on top
* of exixting forest and adds the newly trained trees to the previously
* exixting forest.
*/
TEST_CASE("WarmStartTreesTest", "[RandomForestTest]")
{
arma::mat trainingData;
arma::Row<size_t> trainingLabels;
data::DatasetInfo di;
MockCategoricalData(trainingData, trainingLabels, di);
// Train a random forest.
RandomForest<> rf(trainingData, di, trainingLabels, 5, 25 /* 25 trees */, 1,
1e-7, 0, MultipleRandomDimensionSelect(4));
REQUIRE(rf.NumTrees() == 25);
rf.Train(trainingData, di, trainingLabels, 5, 20 /* 20 trees */, 1, 1e-7, 0,
true /* warmStart */, MultipleRandomDimensionSelect(4));
REQUIRE(rf.NumTrees() == 25 + 20);
}
/**
* Test that RandomForest::Train() when passed warmStart = True does not drop
* prediction quality on train data. Note that prediction quality may drop due
* to overfitting in some cases.
*/
TEST_CASE("WarmStartTreesPredictionsQualityTest", "[RandomForestTest]")
{
arma::mat trainingData;
arma::Row<size_t> trainingLabels;
data::DatasetInfo di;
MockCategoricalData(trainingData, trainingLabels, di);
// Train a random forest.
RandomForest<> rf(trainingData, di, trainingLabels, 5, 3 /* 3 trees */, 1,
1e-7, 0, MultipleRandomDimensionSelect(4));
// Get performance statistics on train data.
arma::Row<size_t> oldPredictions;
rf.Classify(trainingData, oldPredictions);
// Calculate the number of correct points.
size_t oldCorrect = arma::accu(oldPredictions == trainingLabels);
rf.Train(trainingData, di, trainingLabels, 5, 20 /* 20 trees */, 1, 1e-7, 0,
true /* warmStart */, MultipleRandomDimensionSelect(4));
// Get performance statistics on train data.
arma::Row<size_t> newPredictions;
rf.Classify(trainingData, newPredictions);
// Calculate the number of correct points.
size_t newCorrect = arma::accu(newPredictions == trainingLabels);
REQUIRE(newCorrect - oldCorrect >= 0);
}
+118 -19
View File
@@ -223,9 +223,9 @@ TEST_CASE("ZeroRatioStratifiedSplitData", "[SplitDataTest]")
// Set the labels to 5 0s and 10 1s.
const Row<size_t> labels = { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
const double test_ratio = 0;
const double testRatio = 0;
const auto value = Split(input, labels, test_ratio, false, true);
const auto value = Split(input, labels, testRatio, false, true);
REQUIRE(std::get<0>(value).n_cols == 15);
REQUIRE(std::get<1>(value).n_cols == 0);
REQUIRE(std::get<2>(value).n_cols == 15);
@@ -242,9 +242,9 @@ TEST_CASE("TotalRatioStratifiedSplitData", "[SplitDataTest]")
// Set the labels to 5 0s and 10 1s.
const Row<size_t> labels = { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
const double test_ratio = 1;
const double testRatio = 1;
const auto value = Split(input, labels, test_ratio, false, true);
const auto value = Split(input, labels, testRatio, false, true);
REQUIRE(std::get<0>(value).n_cols == 0);
REQUIRE(std::get<1>(value).n_cols == 15);
REQUIRE(std::get<2>(value).n_cols == 0);
@@ -263,9 +263,9 @@ TEST_CASE("StratifiedSplitDataResultTest", "[SplitDataTest]")
const Row<size_t> labels = { 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 };
const double test_ratio = 0.25;
const double testRatio = 0.25;
const auto value = Split(input, labels, test_ratio, true, true);
const auto value = Split(input, labels, testRatio, true, true);
REQUIRE(static_cast<uvec>(find(std::get<2>(value) == 0)).n_rows == 3);
REQUIRE(static_cast<uvec>(find(std::get<2>(value) == 1)).n_rows == 6);
REQUIRE(static_cast<uvec>(find(std::get<2>(value) == 2)).n_rows == 9);
@@ -293,22 +293,22 @@ TEST_CASE("StratifiedSplitLargerDataResultTest", "[SplitDataTest]")
input.randu();
// 256 0s, 128 1s, 64 2s and 32 3s.
Row<size_t> zero_label(256);
Row<size_t> one_label(128);
Row<size_t> two_label(64);
Row<size_t> three_label(32);
Row<size_t> zeroLabel(256);
Row<size_t> oneLabel(128);
Row<size_t> twoLabel(64);
Row<size_t> threeLabel(32);
zero_label.fill(0);
one_label.fill(1);
two_label.fill(2);
three_label.fill(3);
zeroLabel.fill(0);
oneLabel.fill(1);
twoLabel.fill(2);
threeLabel.fill(3);
Row<size_t> labels = arma::join_rows(zero_label, one_label);
labels = arma::join_rows(labels, two_label);
labels = arma::join_rows(labels, three_label);
const double test_ratio = 0.3;
Row<size_t> labels = arma::join_rows(zeroLabel, oneLabel);
labels = arma::join_rows(labels, twoLabel);
labels = arma::join_rows(labels, threeLabel);
const double testRatio = 0.3;
const auto value = Split(input, labels, test_ratio, false, true);
const auto value = Split(input, labels, testRatio, false, true);
REQUIRE(static_cast<uvec>(find(std::get<2>(value) == 0)).n_rows == 180);
REQUIRE(static_cast<uvec>(find(std::get<2>(value) == 1)).n_rows == 90);
REQUIRE(static_cast<uvec>(find(std::get<2>(value) == 2)).n_rows == 45);
@@ -322,3 +322,102 @@ TEST_CASE("StratifiedSplitLargerDataResultTest", "[SplitDataTest]")
mat concat = arma::join_rows(std::get<0>(value), std::get<1>(value));
CheckMatEqual(input, concat);
}
/**
* Check that Split() with stratifyData true throws a runtime error if labels
* are not of type arma::Row<>.
*/
TEST_CASE("StratifiedSplitRunTimeErrorTest", "[SplitDataTest]")
{
mat input(3, 480);
mat labels(2, 480);
input.randu();
labels.randu();
const double testRatio = 0.3;
REQUIRE_THROWS_AS(Split(input, labels, testRatio, false, true),
std::runtime_error);
}
/*
* Split with input of type field<mat>.
*/
TEST_CASE("SplitDataResultField", "[SplitDataTest]")
{
field<mat> input(1, 2);
mat matA(2, 10);
mat matB(2, 10);
size_t count = 0; // Counter for unique sequential values.
matA.imbue([&count]() { return ++count; });
matB.imbue([&count]() { return ++count; });
input(0, 0) = matA;
input(0, 1) = matB;
const auto value = Split(input, 0.5, false);
REQUIRE(std::get<0>(value).n_cols == 1); // Train data.
REQUIRE(std::get<1>(value).n_cols == 1); // Test data.
field<mat> concat = {std::get<0>(value)(0), std::get<1>(value)(0)};
// Order matters here.
CheckFields(input, concat);
}
/**
* Test for Split() with labels of type arma::Mat with shuffleData = False.
*/
TEST_CASE("SplitMatrixLabeledData", "[SplitDataTest]")
{
const mat input(2, 10, fill::randu);
const mat labels(2, 10, fill::randu);
const auto value = Split(input, labels, 0.2, false);
REQUIRE(std::get<0>(value).n_cols == 8);
REQUIRE(std::get<1>(value).n_cols == 2);
REQUIRE(std::get<2>(value).n_cols == 8);
REQUIRE(std::get<3>(value).n_cols == 2);
mat inputConcat = arma::join_rows(std::get<0>(value), std::get<1>(value));
mat labelsConcat = arma::join_rows(std::get<2>(value), std::get<3>(value));
// Order matters here.
CheckMatrices(input, inputConcat);
CheckMatrices(labels, labelsConcat);
}
/**
* Split with input of type field<mat> and label of type field<vec>.
*/
TEST_CASE("SplitLabeledDataResultField", "[SplitDataTest]")
{
field<mat> input(1, 2);
field<vec> label(1, 2);
mat matA(2, 10, fill::randu);
mat matB(2, 10, fill::randu);
vec vecA(10, fill::randu);
vec vecB(10, fill::randu);
input(0, 0) = matA;
input(0, 1) = matB;
label(0, 0) = vecA;
label(0, 1) = vecB;
const auto value = Split(input, label, 0.5, false);
REQUIRE(std::get<0>(value).n_cols == 1); // Train data.
REQUIRE(std::get<1>(value).n_cols == 1); // Test data.
REQUIRE(std::get<2>(value).n_cols == 1); // Train label.
REQUIRE(std::get<3>(value).n_cols == 1); // Test label.
field<mat> inputConcat = {std::get<0>(value)(0), std::get<1>(value)(0)};
field<vec> labelConcat = {std::get<2>(value)(0), std::get<3>(value)(0)};
// Order matters here.
CheckFields(input, inputConcat);
CheckFields(label, labelConcat);
}
+14
View File
@@ -50,6 +50,20 @@ inline void CheckMatrices(const arma::Mat<size_t>& a,
REQUIRE(a[i] == b[i]);
}
template <typename FieldType,
typename = std::enable_if_t<
arma::is_arma_type<typename FieldType::object_type>::value>>
// Check the values of two field types.
inline void CheckFields(const FieldType& a,
const FieldType& b)
{
REQUIRE(a.n_rows == b.n_rows);
REQUIRE(a.n_cols == b.n_cols);
for (size_t i = 0; i < a.n_slices; ++i)
CheckMatrices(a(i), b(i));
}
// Check the values of two cubes.
inline void CheckMatrices(const arma::cube& a,
const arma::cube& b,
+3 -3
View File
@@ -31,7 +31,7 @@ using namespace mlpack::metric;
// weird things and will cause bizarre problems.
// Test the defaults.
TEST_CASE("DefaultsTraitsTest", "[TreeTraitsTestt]")
TEST_CASE("DefaultsTraitsTest", "[TreeTraitsTest]")
{
// An irrelevant non-tree type class is used here so that the default
// implementation of TreeTraits is chosen.
@@ -48,7 +48,7 @@ TEST_CASE("DefaultsTraitsTest", "[TreeTraitsTestt]")
}
// Test the binary space tree traits.
TEST_CASE("BinarySpaceTreeTraitsTest", "[TreeTraitsTestt]")
TEST_CASE("BinarySpaceTreeTraitsTest", "[TreeTraitsTest]")
{
typedef BinarySpaceTree<LMetric<2, false>> TreeType;
@@ -74,7 +74,7 @@ TEST_CASE("BinarySpaceTreeTraitsTest", "[TreeTraitsTestt]")
}
// Test the cover tree traits.
TEST_CASE("CoverTreeTraitsTest", "[TreeTraitsTestt]")
TEST_CASE("CoverTreeTraitsTest", "[TreeTraitsTest]")
{
// Children may be overlapping.
bool b = TreeTraits<CoverTree<>>::HasOverlappingChildren;