教你用c++从头开始实现决策树

Python已经成为数据科学的语言之王。大多数新的数据科学家和程序员继续学习Python作为他们的第一门语言。这是有充分理由的;Python具有较浅的学习曲线、强大的社区和丰富的数据科学库生态系统。

我用Python开始了我的数据科学之旅,它仍然是我解决数据科学问题最常用的工具。我很想更好地理解Python从您那里抽象出了什么,以及用性能更高的语言编写更快代码的成本与好处。

为了有代表性地介绍c++,我需要一个代表性的应用程序,c++将是一个合适的选择。从头实现一个分类决策树分类器似乎是一个适当的挑战。这已经被证明是一个测试但有益的学习旅程,我想分享一些我在这个过程中的主要经验。

关键经验:

  • c++很少提供代码提示或保护
  • 尽早做出好的架构决策
  • 从长远来看,编写测试将为您节省时间
  • 语言的在线社区非常有价值
  • 可移植性是一个重要的考虑因素

在Python中,你可以做很多事情。您可以创建一个变量,随心所欲地改变它的类型,然后不必担心如何处理它。这能让你在执行过程中改变想法。非常适合动态迭代原型设计。

在c++中,您必须预先决定您希望您的变量是什么类型。您还必须预先决定希望函数返回的类型。如果您声明错误,例如试图从一个已经声明为返回整数的函数返回一个字符串,那么您的进程将会停止。在这种情况下,编译器将阻止您编译程序,通常带有一个令人费解的错误消息。令人沮丧的是,编译器是您的朋友,它会在这个问题导致后续问题之前预先提醒您。在Python中,只有在太晚的时候才发现问题是很常见的,比如在代码投入生产之后。

在上面的示例中,编译器捕获定义为返回试图返回字符串的整数的函数。

也有编译器不支持您的情况。访问一个被认为存储在特定内存地址的变量时,可能只收到一个垃圾值,因为该变量已经被删除了。在这里,您通常不会在编译时收到错误,而且很容易在代码中留下错误,而您对此却浑然不觉。

在上面的示例中,即使我们试图访问已被删除的变量的内存地址的值,编译也不会给出错误。

尽早做出好的架构决策

在Python中,很容易在尝试解决问题的早期阶段就开始编写解决方案。由于c++的灵活性和较慢的开发速度,这种方法在使用c++时不能很好地工作。

在这个项目中,我最初使用的是我的python方法,即只编写代码,而不绘制端到端解决方案。最后,我坐下来,想出了一个解决这个问题的总体架构。

下面列出了在实现决策树分类器中开发的关键对象。它们包括一个Node类和一个Tree类,以及它们相关的属性和方法,并且大部分可以在编写任何代码之前定义:

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 category

Tree
- 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

一旦该体系结构就位,解决方案自然就会遵循。对类及其成员函数(类和函数参数以及返回的对象)的接口进行前瞻性设计也可以使事情变得更加容易。

从长远来看,编写测试将为您节省时间

由于c++缺乏安全性,所以测试代码的每个部分是否都成功地完成了预期的功能是至关重要的。用于c++的谷歌Test测试框架很适合这个项目,它使用CMake构建。

以可测试的方式编写代码可以更容易地识别和隔离bug。方法是为实现的类编写静态定义的成员函数。静态定义的成员函数可以在没有父类实例化的情况下独立执行。这使得为完成决策树业务逻辑的一个方面的每一个功能编写特定的、独立的测试用例成为可能。

上面显示了在终端中通过测试的谷歌Test的输出。

语言的在线社区非常有价值

Python开发人员有一个开发人员社区,使用像Stack Overflow和博客这样的工具为集体知识做出贡献。此资源是Python数据科学的命脉。c++没有等价的社区。在谷歌上搜索开发c++代码时遇到的许多问题和错误消息,往往会得到没有帮助的结果。一种语言的社区价值很大。

从上面我们可以看到,现在每个月被回答的与Python相关的问题比c++多4倍。在这里查看这些统计数据的当前状态。

可移植性是一个重要的考虑因素

在Python中,你可以确信任何安装了Python解释器的系统都能够执行你的Python程序。而在c++中,你就没有这种特权了。由于c++是一种编译语言,在运行程序之前必须先编译程序,而且必须针对要运行程序的宿主的体系结构来编译它。

当尝试使用Github Actions远程测试代码时,这成为一个重要的问题。由于主机是不同的操作系统和架构,因此需要在虚拟机上测试代码之前编译代码。这是部署代码时需要管理的额外开销。

总结

学习像c++这样的低级语言可以让你接触到许多快速程序所需的核心概念,如内存管理、数据结构和编译语言。它让人们意识到Python中预先实现的数据结构,比如Pandas DataFrames,将拥有处理内存管理的系统,这些系统必须做出一系列假设,因此有局限性。

在实践中,不太可能有很多数据科学家会使用c++来解决实验性的数据科学问题,但是Python不再是最好的工具,例如编写快速的数据解析器或实现昂贵的算法。即使在这种情况下,我也将探索现代低级语言,如Go-lang和Rust,而不是c++。c++的语法让人感觉很冗长,而且它缺乏许多可以从这些现代语言中获得的安全特性。

您可以在这里从头看到c++决策树分类器的完整源代码。您还可以在这里找到一个示例jupiter notebook,它直接从Python调用已实现的决策树分类器,并在Titanic数据集上训练决策树。https://github.com/hlamotte/decision-tree

到此这篇关于教你用c++从头开始实现决策树的文章就介绍到这了,更多相关c++实现决策树内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java实现的决策树算法完整实例

    本文实例讲述了Java实现的决策树算法.分享给大家供大家参考,具体如下: 决策树算法是一种逼近离散函数值的方法.它是一种典型的分类方法,首先对数据进行处理,利用归纳算法生成可读的规则和决策树,然后使用决策对新数据进行分析.本质上决策树是通过一系列规则对数据进行分类的过程. 决策树构造可以分两步进行.第一步,决策树的生成:由训练样本集生成决策树的过程.一般情况下,训练样本数据集是根据实际需要有历史的.有一定综合程度的,用于数据分析处理的数据集.第二步,决策树的剪枝:决策树的剪枝是对上一阶段生成的决

  • python机器学习之决策树分类详解

    决策树分类与上一篇博客k近邻分类的最大的区别就在于,k近邻是没有训练过程的,而决策树是通过对训练数据进行分析,从而构造决策树,通过决策树来对测试数据进行分类,同样是属于监督学习的范畴.决策树的结果类似如下图: 图中方形方框代表叶节点,带圆边的方框代表决策节点,决策节点与叶节点的不同之处就是决策节点还需要通过判断该节点的状态来进一步分类. 那么如何通过训练数据来得到这样的决策树呢? 这里涉及要信息论中一个很重要的信息度量方式,香农熵.通过香农熵可以计算信息增益. 香农熵的计算公式如下: p(xi)

  • 教你用c++从头开始实现决策树

    Python已经成为数据科学的语言之王.大多数新的数据科学家和程序员继续学习Python作为他们的第一门语言.这是有充分理由的;Python具有较浅的学习曲线.强大的社区和丰富的数据科学库生态系统. 我用Python开始了我的数据科学之旅,它仍然是我解决数据科学问题最常用的工具.我很想更好地理解Python从您那里抽象出了什么,以及用性能更高的语言编写更快代码的成本与好处. 为了有代表性地介绍c++,我需要一个代表性的应用程序,c++将是一个合适的选择.从头实现一个分类决策树分类器似乎是一个适当

  • 手把手教你用Java实现一套简单的鉴权服务

    前言 时遇JavaEE作业,题目要求写个简单web登录程序,按照老师的意思是用servlet.jsp和jdbc完成.本着要么不做,要做就要做好的原则,我开始着手完成此次作业(其实也是写实训作业的用户鉴权部分),而之前写项目的时候也有相关经验,这次正好能派上用场. 一.何为鉴权服务 引用百度百科的话说 鉴权(authentication)是指验证用户是否拥有访问系统的权利. 鉴权包括两个方面: 用户鉴权,网络对用户进行鉴权,防止非法用户占用网络资源. 网络鉴权,用户对网络进行鉴权,防止用户接入了非

  • 教你用Python matplotlib库制作简单的动画

    matplotlib制作简单的动画 动画即是在一段时间内快速连续的重新绘制图像的过程. matplotlib提供了方法用于处理简单动画的绘制: import matplotlib.animation as ma def update(number): pass # 每隔30毫秒,执行一次update ma.FuncAnimation( mp.gcf(), # 作用域当前窗体 update, # 更新函数的函数名 interval=30 # 每隔30毫秒,执行一次update ) 案例1: 随机生

  • AngularJS入门教程之AngularJS表达式

    表达式用于应用程序数据绑定到HTML.表达式都写在双括号就像{{表达式}}.表达式中的行为跟ng-bind指令方式相同. AngularJS应用表达式是纯javascript表达式,并输出它们被使用的数据在那里. AngularJS表达式格式 : {{expression }} AngularJS表达式可以是字符串.数字.运算符和变量 数字运算{{1 + 5}} 字符串连接{{ 'abc' + 'bcd' }} 变量运算 {{ firstName + " " + lastName }}

  • 程序员 代码是从头编还是使用框架好呢?

    在编码的世界里,程序员永远不要期待东西保持静止太久.技术已经决定了我们如何互动.创造.学习.生活等,并且不断发展.对于程序员来说,只有靠近和依赖最新技术才能完成任务.而且最深刻的改变之一就是框架.编程语言以及两者之间的范式转变. 编程语言本质上是与计算机通信的方式,并通过使用语法和语义告诉计算机要做什么.框架是汇集了一起完成任务的程序的集合,使编码更有效率,并且通常使程序员的生活更容易. 当然,二者绝不是对立的关系,只是编程社区中仍然存在一些争议:到底是应该自己从头开始编写代码还是使用各种框架简

  • 手把手教你用“按键精灵”图文教程

    如果你还为一些枯燥.繁琐的电脑操作而烦恼,按键精灵绝对会是你最好的帮手.    那么,按键精灵具体能帮我们干什么呢?我们来列举几个例子来说明下.    * 网络游戏中可作脚本实现自动打怪,自动补血,自动说话等:    * 办公族可用它自动处理表格.文档,自动收发邮件等:    * 任何你觉得"有点烦"的电脑操作都可以替你完成.     按键精灵第一个实现了"动动鼠标就可以制作出脚本"的功能.我们不希望为了使用一个小软件而去学习编程知识,考虑到这些,所以按键精灵完全界

  • AngularJS入门教程之Helloworld示例

    本文实例讲述了AngularJS入门教程之Helloworld示例.分享给大家供大家参考,具体如下: 什么是AngularJs? angularjs是一个为动态WEB应用设计的结构框架.它能让你使用HTML作为模板语言,通过扩展HTML的语法,让你能更清楚.简洁地构建你的应用组件.它的创新点在于,利用数据绑定和依赖注入,它使你不用再写大量的代码了.这些全都通过浏览器端的javascript实现,这也使得它能够完美地和任何服务器技术结合. AngularJS简单的Helloworld例子: <!D

  • ES6教程之for循环和Map,Set用法分析

    本文实例讲述了ES6教程之for循环和Map,Set用法.分享给大家供大家参考,具体如下: 现在大家先想一想,如果要你遍历一个数组的元素,你会选择如何去做呢?一般都会想起for循环: for (var index = 0; index < myArray.length; index++) { console.log(myArray[index]); } 可惜我得告诉你,这个方法是二十年的人才应该使用的方法,在ES5中已经提出了更为简便的forEach方法,代码如下: myArray.forEac

  • Zend Framework教程之Zend_Registry对象用法分析

    本文实例讲述了Zend Framework教程之Zend_Registry对象用法.分享给大家供大家参考,具体如下: 使用对象注册表(Registry) 对象注册表(或称对象仓库)是一个用于在整个应用空间(application space)内存储对象和值的容器.通过把对象存储在其中,我们可以在整个项目的任何地方使用同一个对象.这种机制相当于一种全局存储. 我们可以通过Zend_Registry类的静态方法来使用对象注册表,另外,由于该类是一个数组对象,你可以使用数组形式来访问其中的类方法. 1

  • MYSQL实现连续签到功能断签一天从头开始(sql语句)

    1,创建测试表 CREATE TABLE `testsign` ( `userid` int(5) DEFAULT NULL, `username` varchar(20) DEFAULT NULL, `signtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `type` int(1) DEFAULT '0' COMMENT '为0表示签到数据,1表示签到日期字典数据' ) ENGIN

随机推荐