mlpack: 一個C++機器學習庫
簡介
mlpack是一個C++機器學習庫,側重於可擴充套件性、速度和易用性。它的目的是通過一個簡單的、前後一致的API讓新使用者使用機器學習成為可能,同時利用C++語言特徵為專家使用者提供最好的效能和最大的靈活性。這些通過提供一系列命令列執行程式完成,就像使用一個黑箱,而且專家使用者和研究者可以容易的更改一個模組化的C++ API的內部演算法。
這種方法的結果就是mlpack的效能大幅度超過其他競爭的機器學習庫;在the BigLearning workshop paper 和the benchmarks for details檢視細節。
mlpack由全世界的貢獻者開發。基於伯克利發行軟體許可的第三個條款免費發行。(比1.0.12更老的版本基於GNU通用公共授權規定發行:LGPL,第3版。)
安裝
mlpack儲存在許多Linux的發行版本中,所以在你的系統中使用程式包管理器可能更容易一些。例如:在Ubuntu上,你可以使用下面的命令安裝mlpack。
C++
1 |
$ sudo apt-get install libmlpack-dev |
如果mlpack不能在你的系統的程式包管理器中使用,那麼你可以按照下面的步驟編譯和安裝mlpack原始檔。
Mlpack使用CMake作為生成系統,允許幾個靈活的生成配置選項。你可以查詢大量的CMake教程得到更多的檔案,但是這個教程應該足夠讓你在大多數Linux和類UNIX系統中(包括OS X)成功生成和安裝mlpack。如果你想在Windows作業系統中生成mlpack,請看
首先下載mlpack。
當mlpack的原始檔完成解壓,你可以建立一個生成目錄。
C++
1 2 |
$ cd mlpack-2.2.5 $ mkdir build |
這個目錄可以是任何名字,不僅僅是“build”,但是“build”足夠了。
mlpack依賴項
mlpack依賴下面的庫,它們需要被安裝在系統中並有一些標頭檔案出現。
- Armadillo >=4.200.0(支援LAPACK(線性代數程式包))
- Boost(math_c99, program_options, serialization, unit_test_framework, heap, spirit)>=1.49
在Ununtu和Debian中,你可以通過apt獲得所有這些依賴項:
C++
1 2 |
# apt-get install libboost-math-dev libboost-program-options-dev libboost-test-dev libboost-serialization-dev libarmadillo-dev binutils-dev |
在Fedora、Red Hat或CentOS上,這些相同的依賴項可以通過dnf獲得:
C++
1 2 |
# dnf install boost-devel boost-test boost-program-options boost-math armadillo-devel binutils-devel |
配置CMake
執行CMake相當於使用autotools執行./configure。
如果你工作中使用mlpack的svn trunk版本,且不帶任何選項執行CMake,它將配置這個生成專案帶有除錯符號和分析資訊:如果你工作中使用發行版本的mlpack,不帶任何選項執行CMake,它將配置這個生成專案不帶除錯符號和分析資訊(為了速度)。
C++
1 2 |
$ cd build $ cmake ../ |
你可以手動指定選項去編譯或不編譯除錯資訊和分析資訊(也就是說盡可能快):
C++
1 2 |
$ cd build $ cmake -D DEBUG=OFF -D PROFILE=OFF ../ |
Mlpack允許的全部選項為:
- DEBUG=(ON/OFF): compile with debugging symbols (default ON in svn trunk, OFF in releases)
- PROFILE=(ON/OFF): compile with profiling symbols (default ON in svn trunk, OFF in releases)
- ARMA_EXTRA_DEBUG=(ON/OFF): compile with extra Armadillo debugging symbols (default OFF)
- BUILD_TESTS=(ON/OFF): compile the mlpack_test program (default ON)
- BUILD_CLI_EXECUTABLES=(ON/OFF): compile the mlpack command-line executables (i.e. mlpack_knn, mlpack_kfn, mlpack_logistic_regression, etc.) (default ON)
- TEST_VERBOSE=(ON/OFF): run test cases in mlpack_test with verbose output (default OFF)
每個選項都可以被指定給帶有‘-D’標記的CMake。其他工具也可以用於配置CMake,但是它們沒有被記錄在這裡。
生成mlpack
一旦CMake配置好,生成庫就像打出‘make’一樣簡單。這將生成所有庫元件和‘mlpack_test’。
C++
1 2 3 4 5 |
$ make Scanning dependencies of target mlpack [ 1%] Building CXX object src/mlpack/CMakeFiles/mlpack.dir/core/optimizers/aug_lagrangian/aug_lagrangian_test_functions.cpp.o <...> |
如果你不想生成每一個庫,可以指定你想生成的單個元件。
C++
1 |
$ make mlpack_pca mlpack_knn mlpack_kfn |
一個有趣的特殊元件是mlpack_test,它是執行mlpack的測試元件。你可以使用這個命令生成這個元件:
C++
1 |
$ make mlpack_test |
然後執行所有的測試元件或單個的測試元件:
C++
1 2 |
$ bin/mlpack_test $ bin/mlpack_test -t KNNTest |
如果生成失敗,而你不能找到為什麼失敗,在Github上註冊一個賬戶,提交這個問題,mlpack的開發人員將會盡快幫你解決,
或者在irc.freenode.netm上的mlpack的IRC中也可以找到mlpack的幫助。
安裝mlpack
如果你想將mlpack安裝在/usr/include/mlpack/、/usr/lib/和/usr/bin/中,當它生成後,確保你有root許可權(或向那兩個目錄的寫入許可),然後簡單的打出:
C++
1 |
# make install |
現在你可以通過名字執行可執行程式;你可以使用-lmlpack連結到mlpack,mlpack的標頭檔案可以在目錄/usr/include/mlpack/中找到。
示例
最近鄰搜尋是一個常見的機器學習任務。在這個背景下 ,我們有一個查詢資料集和一個參考資料集。對於在查詢資料集中的每個點,我們希望知道參考資料集中距離給定查詢點最近的k個點。
或者,如果查詢和參考資料集是相同的,問題可以更簡單的說明:對於每個資料集中的點,我們希望知道距離那個點最近的k個點。
Mlpack提供:
- 一個簡單的命令列程式包執行最近鄰搜尋(和最遠鄰搜尋)。
- 一個簡單的C++介面用於完成最近鄰搜尋(和最遠鄰搜尋)。
- 一個通用的、可擴充套件的和強大的C++類(鄰域搜尋)用於複雜演算法。
命令列‘mlpack_knn’
在mlpack中完成最近鄰搜尋最簡單的方式是使用mlpack_knn程式包。這個程式將完成最近鄰搜尋,並將得到的鄰近點放入一個檔案,同時將對應的距離放入另一個檔案。輸出檔案被整理為第一行對應第一個被查詢點的最近鄰點,第一列對應最近的點,以此類推。
下面是幾個簡單用法的例子(和輸出結果)。由於使用了選項‘-v’,因此輸出是給定的。更多關於每個選項的文件可以通過下面這個語句找到。
C++
1 |
$ mlpack_knn –help |
一個數據集,5個最近鄰點
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
$ mlpack_knn -r dataset.csv -n neighbors_out.csv -d distances_out.csv -k 5 -v [INFO ] Loading 'dataset.csv' as CSV data. Size is 3 x 1000. [INFO ] Loaded reference data from 'dataset.csv' (3 x 1000). [INFO ] Building reference tree... [INFO ] Tree built. [INFO ] Searching for 5 nearest neighbors with dual-tree kd-tree search... [INFO ] 18412 node combinations were scored. [INFO ] 54543 base cases were calculated. [INFO ] Search complete. [INFO ] Saving CSV data to 'neighbors_out.csv'. [INFO ] Saving CSV data to 'distances_out.csv'. [INFO ] [INFO ] Execution parameters: [INFO ] distances_file: distances_out.csv [INFO ] help: false [INFO ] info: "" [INFO ] input_model_file: "" [INFO ] k: 5 [INFO ] leaf_size: 20 [INFO ] naive: false [INFO ] neighbors_file: neighbors_out.csv [INFO ] output_model_file: "" [INFO ] query_file: "" [INFO ] random_basis: false [INFO ] reference_file: dataset.csv [INFO ] seed: 0 [INFO ] single_mode: false [INFO ] tree_type: kd [INFO ] verbose: true [INFO ] version: false [INFO ] [INFO ] Program timers: [INFO ] computing_neighbors: 0.108968s [INFO ] loading_data: 0.006495s [INFO ] saving_data: 0.003843s [INFO ] total_time: 0.126036s [INFO ] tree_building: 0.003442s |
在輸出底部為每個不同的計算部分加入方便的程式計時器,同時也加入和模擬一起執行的引數。現在,如果我們看看結果檔案:
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
$ head neighbors_out.csv 862,344,224,43,885 703,499,805,639,450 867,472,972,380,601 397,319,277,443,323 840,827,865,38,438 732,876,751,492,616 563,222,569,985,940 361,97,928,437,79 547,695,419,961,716 982,113,689,843,634
$ head distances_out.csv 5.986076164057e-02,7.664920518084e-02,1.116050961847e-01,1.155595474371e-01,1.169810085522e-01 7.532635022982e-02,1.012564715841e-01,1.127846944644e-01,1.209584396720e-01,1.216543647014e-01 7.659571546879e-02,1.014588981948e-01,1.025114621511e-01,1.128082429187e-01,1.131659758673e-01 2.079405647909e-02,4.710724516732e-02,7.597622408419e-02,9.171977778898e-02,1.037033340864e-01 7.082206779700e-02,9.002355499742e-02,1.044181406406e-01,1.093149568834e-01,1.139700558608e-01 5.688056488896e-02,9.478072514474e-02,1.085637706630e-01,1.114177921451e-01,1.139370265105e-01 7.882260880455e-02,9.454474078041e-02,9.724494179950e-02,1.023829575445e-01,1.066927013814e-01 7.005321598247e-02,9.131417221561e-02,9.498248889074e-02,9.897964162308e-02,1.121202216165e-01 5.295654132754e-02,5.509877761894e-02,8.108227366619e-02,9.785461174861e-02,1.043968140367e-01 3.992859920333e-02,4.471418646159e-02,7.346053904990e-02,9.181982339584e-02,9.843075910782e-02 |
所以對於第0點的最近鄰點是第862點,距離是5.986076164057e-02。第二近鄰點是第344點,距離是7.664920518084e-02。第5點的第三近鄰點是第751點,距離是1.085637706630e-01。
查詢和參考資料集,10個最近鄰點
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
$ mlpack_knn -q query_dataset.csv -r reference_dataset.csv \ > -n neighbors_out.csv -d distances_out.csv -k 10 -v [INFO ] Loading 'reference_dataset.csv' as CSV data. Size is 3 x 1000. [INFO ] Loaded reference data from 'reference_dataset.csv' (3 x 1000). [INFO ] Building reference tree... [INFO ] Tree built. [INFO ] Loading 'query_dataset.csv' as CSV data. Size is 3 x 50. [INFO ] Loaded query data from 'query_dataset.csv' (3x50). [INFO ] Searching for 10 nearest neighbors with dual-tree kd-tree search... [INFO ] Building query tree... [INFO ] Tree built. [INFO ] Search complete. [INFO ] Saving CSV data to 'neighbors_out.csv'. [INFO ] Saving CSV data to 'distances_out.csv'. [INFO ] [INFO ] Execution parameters: [INFO ] distances_file: distances_out.csv [INFO ] help: false [INFO ] info: "" [INFO ] input_model_file: "" [INFO ] k: 10 [INFO ] leaf_size: 20 [INFO ] naive: false [INFO ] neighbors_file: neighbors_out.csv [INFO ] output_model_file: "" [INFO ] query_file: query_dataset.csv [INFO ] random_basis: false [INFO ] reference_file: reference_dataset.csv [INFO ] seed: 0 [INFO ] single_mode: false [INFO ] tree_type: kd [INFO ] verbose: true [INFO ] version: false [INFO ] [INFO ] Program timers: [INFO ] computing_neighbors: 0.022589s [INFO ] loading_data: 0.003572s [INFO ] saving_data: 0.000755s [INFO ] total_time: 0.032197s [INFO ] tree_building: 0.002590s |
一個數據集,3個最近鄰點,15個點的大小
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
$ mlpack_knn -r dataset.csv -n neighbors_out.csv -d distances_out.csv -k 3 -l 15 -v [INFO ] Loading 'dataset.csv' as CSV data. Size is 3 x 1000. [INFO ] Loaded reference data from 'dataset.csv' (3 x 1000). [INFO ] Building reference tree... [INFO ] Tree built. [INFO ] Searching for 3 nearest neighbors with dual-tree kd-tree search... [INFO ] 19692 node combinations were scored. [INFO ] 36263 base cases were calculated. [INFO ] Search complete. [INFO ] Saving CSV data to 'neighbors_out.csv'. [INFO ] Saving CSV data to 'distances_out.csv'. [INFO ] [INFO ] Execution parameters: [INFO ] distances_file: distances_out.csv [INFO ] help: false [INFO ] info: "" [INFO ] input_model_file: "" [INFO ] k: 3 [INFO ] leaf_size: 15 [INFO ] naive: false [INFO ] neighbors_file: neighbors_out.csv [INFO ] output_model_file: "" [INFO ] query_file: "" [INFO ] random_basis: false [INFO ] reference_file: dataset.csv [INFO ] seed: 0 [INFO ] single_mode: false [INFO ] tree_type: kd [INFO ] verbose: true [INFO ] version: false [INFO ] [INFO ] Program timers: [INFO ] computing_neighbors: 0.059020s [INFO ] loading_data: 0.002791s [INFO ] saving_data: 0.002369s [INFO ] total_time: 0.069277s [INFO ] tree_building: 0.002713s |
更多關於選項的文件可以使用‘-help’選項找到。
‘KNN’類
具體來說,‘KNN’類是一個更擴充套件的鄰域搜尋類的型別定義,使用歐氏距離查詢最近鄰點。
C++
1 2 |
typedef NeighborSearch<NearestNeighborSort, metric::EuclideanDistance> KNN; |
使用KNN類相當簡單。首先,目標物件必須被構建並指定一個數據集。然後,執行方法,返回兩個矩陣:一個是最近鄰點的序號,一個是最近鄰點的距離。以相同的結構輸出–neighbors_file和–distances_file到命令列介面。下面給出幾個KNN使用的幾個例子。
單一資料集上的5個最近鄰點
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <mlpack/methods/neighbor_search/neighbor_search.hpp>
using namespace mlpack::neighbor;
// Our dataset matrix, which is column-major. extern arma::mat data;
KNN a(data);
// The matrices we will store output in. arma::Mat<size_t> resultingNeighbors; arma::mat resultingDistances;
a.Search(5, resultingNeighbors, resultingDistances); |
搜尋的輸出儲存在resultingNeighbors和resultingDistances裡面。
查詢和參考資料集上的10個最近鄰點
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <mlpack/methods/neighbor_search/neighbor_search.hpp>
using namespace mlpack::neighbor;
// Our dataset matrices, which are column-major. extern arma::mat queryData, referenceData;
KNN a(referenceData);
// The matrices we will store output in. arma::Mat<size_t> resultingNeighbors; arma::mat resultingDistances;
a.Search(queryData, 10, resultingNeighbors, resultingDistances); |
在資料集上自然(窮舉)搜尋6個最近鄰點
這個例子使用時間度O(n^2)的自然搜尋(不是樹搜尋)。、
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <mlpack/methods/neighbor_search/neighbor_search.hpp>
using namespace mlpack::neighbor;
// Our dataset matrix, which is column-major. extern arma::mat dataset;
KNN a(dataset, true);
// The matrices we will store output in. arma::Mat<size_t> resultingNeighbors; arma::mat resultingDistances;
a.Search(6, resultingNeighbors, resultingDistances); |
不用說,自然搜尋很慢。
擴充套件的‘NeighborSearch’類
NeighborSearch類是可擴充套件的,帶有下列模板引數:
C++
1 2 3 4 5 6 7 8 9 10 11 12 |
template< typename SortPolicy = NearestNeighborSort, typename MetricType = mlpack::metric::EuclideanDistance, typename MatType = arma::mat, template<typename TreeMetricType, typename TreeStatType, typename TreeMatType> class TreeType = tree::KDTree, template<typename RuleType> class TraversalType = TreeType<MetricType, NeighborSearchStat<SortPolicy>, MatType>::template DualTreeTraverser> > class NeighborSearch; |
通過為每個模板類選擇不同的內容,可以建立任意的鄰域搜尋物件。注意,這些模板的每個引數都有預設值,所以不需要為每個引數賦值。
SortPolicy策略類
SortPolicy模板引數允許指定NeighborSearch物件將怎樣確定被搜尋的每個點。mlpack::neighbor::NearestNeighborSort類是一個被清楚記錄的示例。一個定製的SortPolicy類必須完成和NearestNeighborSort類相同的方法。
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
static size_t SortDistance(const arma::vec& list, double newDistance);
static bool IsBetter(const double value, const double ref);
template<typename TreeType> static double BestNodeToNodeDistance(const TreeType* queryNode, const TreeType* referenceNode);
template<typename TreeType> static double BestPointToNodeDistance(const arma::vec& queryPoint, const TreeType* referenceNode);
static const double WorstDistance();
static const double BestDistance(); |
mlpack::neighbor::FurthestNeighborSort類是用於建立‘KFN’型別定義類的另一個方法,和尋找最近鄰點相反,它尋找最遠鄰點。
MetricType策略類
MetricType策略類允許鄰域搜尋發生在任意度量空間。mlpack::metric::LMetric是一個很好的示例實現。一個MetricType類必須提供下列功能:
C++
1 2 3 4 5 6 |
// Empty constructor is required. MetricType();
// Compute the distance between two points. template<typename VecType> double Evaluate(const VecType& a, const VecType& b); |
在內部,NeighborSearch類儲存一個MetricType類的例項(它可以在建構函式中給定)。這對於像馬氏距離這樣必須儲存狀態(協方差矩陣)的度量是有用的(mlpack::metric::MahalanobisDistance)。因此,你可以寫一個非靜態MetricType類,無縫的使用它和NeighborSearch。
MatType策略類
MatType模板引數指定資料型別使用矩陣。這個型別必須實現和Armadillo矩陣相同的操作,標準選擇是arma::mat和arma::sp_mat。
TreeType策略類
鄰域搜尋NeighborSearch類在用於搜尋的樹型別的選擇上有很好的擴充套件。這個型別必須遵循典型的mlpack TreeType策略。
典型的選擇可能包含mlpack::tree::KDTree,mlpack::tree::BallTree,mlpack::tree::StandardCoverTree,mlpack::tree::RTree或mlpack::tree::RStarTree。製作你自己的樹型別用來使用NeighborSearch是很容易的。更多細節請點選這裡。
下面給出一個使用帶有球樹的NeighborSearch類的例子。
C++
1 2 3 4 5 6 7 |
// Construct a NeighborSearch object with ball bounds. NeighborSearch< NearestNeighborSort, metric::EuclideanDistance, arma::mat, tree::BallTree > neighborSearch(dataset); |
TraverserType策略類
鄰域搜尋NeighborSearch類提供的最後一個模板引數是TraverserType類。它具有的策略是在單一樹或者雙樹搜尋模式下遍歷樹。預設情況下,它被設定為使用指定TreeType(成員TreeType::DualTreeTraverser)的預設遍歷器。
這個類必須實現下面兩種方法:
C++
1 2 3 4 5 |
// Instantiate with a given RuleType. TraverserType(RuleType& rule);
// Traverse with two trees. void Traverse(TreeType& queryNode, TreeType& referenceNode); |
RuleType類用在遍歷器中時提供下面的功能:
C++
1 2 3 4 5 6 |
// Evaluate the base case between two points. double BaseCase(const size_t queryIndex, const size_t referenceIndex);
// Score the two nodes to see if they can be pruned, returning DBL_MAX if they // can be pruned. double Score(TreeType& queryNode, TreeType& referenceNode); |
注意任何指定的遍歷器必須滿足修剪雙樹遍歷的定義,其在文章”Tree-independent dual-tree algorithms”中指定。