用c++从头开始实现决策树
Python已經(jīng)成為數(shù)據(jù)科學(xué)的語言之王。大多數(shù)新的數(shù)據(jù)科學(xué)家和程序員繼續(xù)學(xué)習(xí)Python作為他們的第一門語言。這是有充分理由的;Python具有較淺的學(xué)習(xí)曲線、強大的社區(qū)和豐富的數(shù)據(jù)科學(xué)庫生態(tài)系統(tǒng)。
我用Python開始了我的數(shù)據(jù)科學(xué)之旅,它仍然是我解決數(shù)據(jù)科學(xué)問題最常用的工具。我很想更好地理解Python從您那里抽象出了什么,以及用性能更高的語言編寫更快代碼的成本與好處。
為了有代表性地介紹c++,我需要一個代表性的應(yīng)用程序,c++將是一個合適的選擇。從頭實現(xiàn)一個分類決策樹分類器似乎是一個適當?shù)奶魬?zhàn)。這已經(jīng)被證明是一個測試但有益的學(xué)習(xí)旅程,我想分享一些我在這個過程中的主要經(jīng)驗。
關(guān)鍵經(jīng)驗:
c++很少提供代碼提示或保護
在Python中,你可以做很多事情。您可以創(chuàng)建一個變量,隨心所欲地改變它的類型,然后不必擔心如何處理它。這能讓你在執(zhí)行過程中改變想法。非常適合動態(tài)迭代原型設(shè)計。
在c++中,您必須預(yù)先決定您希望您的變量是什么類型。您還必須預(yù)先決定希望函數(shù)返回的類型。如果您聲明錯誤,例如試圖從一個已經(jīng)聲明為返回整數(shù)的函數(shù)返回一個字符串,那么您的進程將會停止。在這種情況下,編譯器將阻止您編譯程序,通常帶有一個令人費解的錯誤消息。令人沮喪的是,編譯器是您的朋友,它會在這個問題導(dǎo)致后續(xù)問題之前預(yù)先提醒您。在Python中,只有在太晚的時候才發(fā)現(xiàn)問題是很常見的,比如在代碼投入生產(chǎn)之后。
在上面的示例中,編譯器捕獲定義為返回試圖返回字符串的整數(shù)的函數(shù)。
也有編譯器不支持您的情況。訪問一個被認為存儲在特定內(nèi)存地址的變量時,可能只收到一個垃圾值,因為該變量已經(jīng)被刪除了。在這里,您通常不會在編譯時收到錯誤,而且很容易在代碼中留下錯誤,而您對此卻渾然不覺。
在上面的示例中,即使我們試圖訪問已被刪除的變量的內(nèi)存地址的值,編譯也不會給出錯誤。
盡早做出好的架構(gòu)決策
在Python中,很容易在嘗試解決問題的早期階段就開始編寫解決方案。由于c++的靈活性和較慢的開發(fā)速度,這種方法在使用c++時不能很好地工作。
在這個項目中,我最初使用的是我的python方法,即只編寫代碼,而不繪制端到端解決方案。最后,我坐下來,想出了一個解決這個問題的總體架構(gòu)。
下面列出了在實現(xiàn)決策樹分類器中開發(fā)的關(guān)鍵對象。它們包括一個Node類和一個Tree類,以及它們相關(guān)的屬性和方法,并且大部分可以在編寫任何代碼之前定義:
Node - Node constructor - Node destuctor - Attributes- children nodes- data- best split feature chosen- best split category chosen - Methods- giniImpurity() - metric for scoring quality of split- bestSplit() - best split feature and categoryTree - Tree constructor - Tree destructor - Attributes- root node of tree - Methods- traverse() - traverse nodes of tree- fit() - fit tree to dataset- predict() - make predictions classes with unseen data- CSVReader() - read a csv決策樹項目的核心文件(不包括測試文件)如下所示,以供參考。
. ├── CMakeLists.txt ├── CSVReader.cpp ├── CSVReader.hpp ├── DecisionTree.cpp ├── DecisionTree.hpp ├── Main.cpp ├── Node.cpp ├── Node.hpp └── README.md一旦該體系結(jié)構(gòu)就位,解決方案自然就會遵循。對類及其成員函數(shù)(類和函數(shù)參數(shù)以及返回的對象)的接口進行前瞻性設(shè)計也可以使事情變得更加容易。
從長遠來看,編寫測試將為您節(jié)省時間
由于c++缺乏安全性,所以測試代碼的每個部分是否都成功地完成了預(yù)期的功能是至關(guān)重要的。用于c++的谷歌Test測試框架很適合這個項目,它使用CMake構(gòu)建。
以可測試的方式編寫代碼可以更容易地識別和隔離bug。方法是為實現(xiàn)的類編寫靜態(tài)定義的成員函數(shù)。靜態(tài)定義的成員函數(shù)可以在沒有父類實例化的情況下獨立執(zhí)行。這使得為完成決策樹業(yè)務(wù)邏輯的一個方面的每一個功能編寫特定的、獨立的測試用例成為可能。
上面顯示了在終端中通過測試的谷歌Test的輸出。
語言的在線社區(qū)非常有價值
Python開發(fā)人員有一個開發(fā)人員社區(qū),使用像Stack Overflow和博客這樣的工具為集體知識做出貢獻。此資源是Python數(shù)據(jù)科學(xué)的命脈。c++沒有等價的社區(qū)。在谷歌上搜索開發(fā)c++代碼時遇到的許多問題和錯誤消息,往往會得到?jīng)]有幫助的結(jié)果。一種語言的社區(qū)價值很大。
從上面我們可以看到,現(xiàn)在每個月被回答的與Python相關(guān)的問題比c++多4倍。在這里查看這些統(tǒng)計數(shù)據(jù)的當前狀態(tài)。
可移植性是一個重要的考慮因素
在Python中,你可以確信任何安裝了Python解釋器的系統(tǒng)都能夠執(zhí)行你的Python程序。而在c++中,你就沒有這種特權(quán)了。由于c++是一種編譯語言,在運行程序之前必須先編譯程序,而且必須針對要運行程序的宿主的體系結(jié)構(gòu)來編譯它。
當嘗試使用Github Actions遠程測試代碼時,這成為一個重要的問題。由于主機是不同的操作系統(tǒng)和架構(gòu),因此需要在虛擬機上測試代碼之前編譯代碼。這是部署代碼時需要管理的額外開銷。
總結(jié)
學(xué)習(xí)像c++這樣的低級語言可以讓你接觸到許多快速程序所需的核心概念,如內(nèi)存管理、數(shù)據(jù)結(jié)構(gòu)和編譯語言。它讓人們意識到Python中預(yù)先實現(xiàn)的數(shù)據(jù)結(jié)構(gòu),比如Pandas DataFrames,將擁有處理內(nèi)存管理的系統(tǒng),這些系統(tǒng)必須做出一系列假設(shè),因此有局限性。
在實踐中,不太可能有很多數(shù)據(jù)科學(xué)家會使用c++來解決實驗性的數(shù)據(jù)科學(xué)問題,但是Python不再是最好的工具,例如編寫快速的數(shù)據(jù)解析器或?qū)崿F(xiàn)昂貴的算法。即使在這種情況下,我也將探索現(xiàn)代低級語言,如Go-lang和Rust,而不是c++。c++的語法讓人感覺很冗長,而且它缺乏許多可以從這些現(xiàn)代語言中獲得的安全特性。
您可以在這里從頭看到c++決策樹分類器的完整源代碼。您還可以在這里找到一個示例jupiter notebook,它直接從Python調(diào)用已實現(xiàn)的決策樹分類器,并在Titanic數(shù)據(jù)集上訓(xùn)練決策樹。https://github.com/hlamotte/decision-tree
本文作者:Hamish Lamotte
總結(jié)
以上是生活随笔為你收集整理的用c++从头开始实现决策树的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信公众平台之模拟登录
- 下一篇: 用一年时间如何能掌握 C++ ?