何以贯彻并动用决策树算法必发365bifa0000?

by admin on 2019年1月23日

本文对表决树算法举办简要的下结论和梳理,并对出名的裁定树算法ID3(Iterative
Dichotomiser
迭代二分器)进行落到实处,已毕选择Python语言,一句老梗,“人生苦短,我用Python”,Python确实可以省很多言语方面的事,从而得以让我们注意于问题和缓解问题的逻辑。

前者安全之XSS

转发请注脚出处:unclekeith:
前端安全之XSS

按照分裂的数量,我已毕了几个本子的ID3算法,复杂度逐步升级:

XSS定义

XSS, 即为(Cross Site Scripting), 中文名为跨站脚本,
是发出在对象用户的浏览器规模上的,当渲染DOM树的历程成暴发了不在预期内施行的JS代码时,就发出了XSS攻击。

跨站脚本的显要不在‘跨站’上,而在于‘脚本’上。超过一半XSS攻击的第一方法是置于一段远程或者第三方域上的JS代码。实际上是在对象网站的功效域下执行了那段js代码。

1.纯标称值无缺失数据集

XSS攻击方式

反射型 XSS

反射型XSS,也叫非持久型XSS,是指暴发请求时,XSS代码出现在哀求URL中,作为参数提交到服务器,服务器解析并响应。响应结果中蕴涵XSS代码,最终浏览器解析并履行。

从概念上得以看到,反射型XSS代码是首先出现在URL中的,然后亟待服务端解析,最后内需浏览器解析之后XSS代码才可以攻击。

举一个小栗子。

使用express起一个web服务器,然后设置一下伸手接口。通过ajax的GET请求将参数发往服务器,服务器解析成json后响应。将赶回的数量解析后出示到页面上。(没有对回到的数码进行解码和过滤等操作。)

html
<textarea name="txt" id="txt" cols="80" rows="10">
<button type="button" id="test">测试</button>

js
var test = document.querySelector('#test')
test.addEventListener('click', function () {
  var url = `/test?test=${txt.value}`   // 1. 发送一个GET请求
  var xhr = new XMLHttpRequest()
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
      if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
        // 3. 客户端解析JSON,并执行
        var str = JSON.parse(xhr.responseText).test
        var node = `${str}`
        document.body.insertAdjacentHTML('beforeend', node)
      } else {
        console.log('error', xhr.responseText)
      }
    }
  }
  xhr.open('GET', url, true)
  xhr.send(null)
}, false)

express
var express = require('express');
var router = express.Router();

router.get('/test', function (req, res, next) {
 // 2. 服务端解析成JSON后响应
  res.json({
    test: req.query.test
  })
})

现今大家由此给textarea添加一段有攻击目标的img标签,

<img src="null" onerror='alert(document.cookie)' />

实在的页面时那样的。
必发365bifa0000 1
ok现在,大家点击<测试>按钮,一个XSS攻击就时有爆发了。上面图片中是收获了本地的一些cookie音讯
必发365bifa0000 2
实际,我们只是模仿攻击,通过alert获取到了个人的cookie音信。可是倘假如黑客来说,他们会注入一段第三方的js代码,然后将取得到的cookie信息存到他们的服务器上。这样的话黑客们就有时机得到大家的地位认证做一些作案的业务了。

以上,存在的一部分题目,首要在于没有对用户输入的新闻举办过滤,同时没有去除掉DOM节点中存在的部分有危害的风云和局地有危害的DOM节点。

存储型 XSS
存储型XSS,也叫持久型XSS,首假如将XSS代码发送到服务器(不管是数据库、内存如故文件系统等。),然后在下次恳请页面的时候就绝不带上XSS代码了。

最登峰造极的就是留言板XSS。用户提交了一条包罗XSS代码的留言到数据库。当对象用户查询留言时,那一个留言的始末会从服务器解析之后加载出来。浏览器发现有XSS代码,就作为正常的HTML和JS解析执行。XSS攻击就发出了。
DOM XSS
DOM XSS攻击分化于反射型XSS和存储型XSS,DOM
XSS代码不必要劳务器端的解析响应的直接参加,而是通过浏览器端的DOM解析。那全然是客户端的业务。

DOM
XSS代码的口诛笔伐暴发的也许在于我们编辑JS代码造成的。大家驾驭eval语句有一个效益是将一段字符串转换为真正的JS语句,因而在JS中行使eval是很危险的事务,简单导致XSS攻击。防止接纳eval语句。

如以下代码

test.addEventListener('click', function () {
  var node = window.eval(txt.value)
  window.alert(node)
}, false)

txt中的代码如下
<img src='null' onerror='alert(123)' />

上述通过eval语句就造成了XSS攻击。

2.连连值和标称值混合且无缺失数据集

XSS危害

  1. 通过document.cookie盗取cookie
  2. 动用js或css破坏页面正常的布局与体制
  3. 流量威胁(通过访问某段具有window.location.href定位到其它页面)
  4. Dos攻击:利用合理的客户端请求来据为己有过多的服务器资源,从而使合法用户不可能赢得服务器响应。
  5. 应用iframe、frame、XMLHttpRequest或上述Flash等措施,以(被口诛笔伐)用户的身份实施一些管制动作,或举行一些相似的如发博客园、加好友、发私信等操作。
  6. 使用可被攻击的域受到其他域信任的特色,以受看重来源的身价呼吁一些平日不一样意的操作,如进行不当的投票活动。

3.一连值和标称值混合,有缺失数据集

XSS防御

从上述的反射型和DOM
XSS攻击能够看看,大家无法长相的将用户输入的数据直接存到服务器,需要对数码举行部分甩卖。以上的代码出现的一部分问题如下

  1. 没有过滤危险的DOM节点。如所有履行脚本能力的script,
    具有彰显广告和色情图片的img, 具有改变样式的link, style,
    具有内嵌页面的iframe, frame等因素节点。
  2. 尚未过滤危险的习性节点。如事件, style, src, href等
  3. 没有对cookie设置httpOnly。

假定将上述三点都在渲染进度中过滤,那么出现的XSS攻击的票房价值也就小很多。

缓解方式如下

对cookie的保护

  1. 对首要的cookie设置httpOnly,
    防止客户端通过document.cookie读取cookie。服务端可以安装此字段。

对用户输入数据的拍卖

  1. 编码:无法对用户输入的始末都维持原样,对用户输入的数码举办字符实体编码。对于字符实体的概念可以参考小说底部给出的参考链接。
  2. 解码:原样呈现内容的时候必须解码,不然突显不到内容了。
  3. 过滤:把输入的局地非法的事物都过滤掉,从而保险安全性。如移除用户上传的DOM属性,如onerror,移除用户上传的Style节点,iframe,
    script节点等。

通过一个例证讲解一下什么处理用户输入的数额。

贯彻原理如下:

  1. 留存一个parse函数,对输入的数量举行拍卖,重临处理将来的数目
  2. 对输入的多少(如DOM节点)举行解码(使用第三方库 he.js)
  3. 过滤掉一部分元素有损害的因素节点与性能节点。如script标签,onerror事件等。(使用第三方库HTMLParser.js)

<script src='/javascripts/htmlparse.js'></script>
<script src='/javascripts/he.js'></script>
// 第三方库资源在文章底部给出

// parse函数实现如下

function parse (str) {
      // str假如为某个DOM字符串
      // 1. result为处理之后的DOM节点
      let result = ''
      // 2. 解码
      let decode = he.unescape(str, {
          strict: true
      })
      HTMLParser(decode, {
          start (tag, attrs, unary) {
              // 3. 过滤常见危险的标签
              if (tag === 'script' || tag === 'img' || tag === 'link' || tag === 'style' || tag === 'iframe' || tag === 'frame') return
              result += `<${tag}`
              for (let i = 0; i < attrs.length; i++) {
                  let name = (attrs[i].name).toLowerCase()
                  let value = attrs[i].escaped
                  // 3. 过滤掉危险的style属性和js事件
                  if (name === 'style' || name === 'href' || name === 'src' || ~name.indexOf('on')) continue
                  result += ` ${name}=${value}`
              }
              result += `${unary ? ' /' : ''} >`
          },
          chars (text) {
              result += text
          },
          comment (text) {
              result += `<!-- ${text} -->`
          },
          end (tag) {
              result += `</${tag}>`
          }
      })
      return result
  }

所以,有了以上的parse函数之后,就足以幸免超过一半的xss攻击了。

test.addEventListener('click', function () {
  // ... 省略部分代码
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
      if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
        // 3. 客户端解析JSON,并执行
        // test按钮的点击事件中唯一的变化就是使用parse对服务端返回的数据进行了解码和过滤的处理。
        var str = parse(JSON.parse(xhr.responseText).test)
        // 通过parse解析之后返回的数据就是安全的DOM字符串
        var node = `${str}`
        document.body.insertAdjacentHTML('beforeend', node)
      }
    }
  }
  // ... 省略部分代码
}, false)

那就是说,栗子说完了。

有些总括一下

  1. 假诺在DOM解析进度成现身不在预期内的改变(JS代码执行或样式多量变化时),就可能发生XSS攻击
  2. XSS分为反射型XSS,存储型XSS和DOM XSS
  3. 反射型XSS是在将XSS代码放在URL中,将参数提交到服务器。服务器解析后响应,在响应结果中留存XSS代码,最终通过浏览器解析执行。
  4. 存储型XSS是将XSS代码存储到服务端(数据库、内存、文件系统等),在下次乞求同一个页面时就不必要带上XSS代码了,而是从服务器读取。
  5. DOM XSS的发生重如果在JS中应用eval造成的,所以理应防止拔取eval语句。
  6. XSS危害有偷窃用户cookie,通过JS或CSS改变样式,DDos造成健康用户无法取得服务器响应。
  7. XSS代码的防备重大透过对数据解码,再过滤掉危险标签、属性和事件等。

参考资源

  1. 《WEB前端黑客技术揭秘》
  2. 浅谈XSS攻击的这几个事(附常用绕过姿势)
  3. XSS实战:我是什么样砍下你的百度账号
  4. HTMLParser
  5. he
  6. Web安全-XSS

首先个算法参考了《机器学习实战》的多数代码,第二、多少个算法基于前边的贯彻进行模块的加码。

决策树简介

决定树算法不用说大家应该都掌握,是机械学习的一个有名算法,由澳大得梅因赫赫盛名计算机物理学家罗丝Quinlan公布。

决策树是一种监督学习的分类算法,目标是上学出一颗决策树,该树中间节点是数量特征,叶子节点是种类,实际分类时根据树的构造,一步一步按照当下多少特征取值选取进入哪一颗子树,直到走到叶子节点,叶子节点的门类就是此决策树对此数额的求学结果。下图就是一颗不难的决策树:

必发365bifa0000 3此决定树用来判断一个装有纹理,触感,密度的西瓜是还是不是是“好瓜”。

当有那般一个西瓜,纹理清晰,密度为0.333,触感硬滑,那么要你判定是或不是是一个“好瓜”,那时如果由此决策树来判定,显然可以直接本着纹理->清晰->密度<=0.382->否,即此瓜不是“好瓜”,一次决定就好像此成功了。正因为决策树决策很方便,并且准确率也较高,所以平常被用来做分类器,也是“机器学习十大算法”之一C4.5的骨干考虑。

学学出一颗决策树主要考虑一个题目,即 按照数量集构建当前树应该拔取哪类属性作为树根,即划分标准? 

设想最好的图景,一开始接纳某个特征,就把多少集划分成功,即在该特征上取某个值的全是一类。

考虑最坏的情况,不断接纳特征,划分后的数据集总是杂乱无章,就二分拣任务以来,总是有正类有负类,向来到特征全部用完了,划分的数码集合照旧有正有负,这时只好用投票法,正类多就选正类作为叶子,否则选负类。

为此得出了貌似结论:
随着划分的拓展,我们期望选拔一个特性,使得子节点包蕴的样本尽可能属于同一档次,即“纯度”越高越好。

基于“纯度”的标准不一,有两种算法:

1.ID3算法(Iterative
Dichotomiser
迭代二分器),也是本文要促成的算法,基于音讯增益即新闻熵来度量纯度

2.C4.5算法(Classifier
4.5),ID3 的后继算法,也是昆兰提出

3.CART算法(Classification
And Regression Tree),基于基尼指数度量纯度。

ID3算法简介

音信熵是音讯论中的一个重中之重概念,也叫“香农熵”,香农先生的事迹相比较很多人都听过,一个人创制了一门理论,牛的非常。香农理论中一个很重点的特性就是”熵“,即”信息内容的不确定性“,香农在拓展新闻的定量测算的时候,明确地把音讯量定义为随意不定性程度的削减。那就标志了她对新闻的明白:音讯是用来压缩自由不定性的东西。或者公布为香农逆定义:新闻是醒目标增添。那也印证了决策树以熵作为划分选拔的胸襟标准的正确性,即大家想更敏捷地从数据中拿走越多音讯,大家就应该连忙下落不确定性,即缩减”熵“。

音信熵定义为:

必发365bifa0000 4

D表示数据集,连串总数为|Y|,pk代表D中第k类样本所占的比重。依据其定义,Ent的值越小,音信纯度越高。Ent的限量是[0,log|Y|]

上边要拔取某个属性进行剪切,要逐项考虑每个属性,即使当前设想属性a,a的取值有|V|种,那么大家盼望取a作为划分属性,划分到|V|个子节点后,所有子节点的新闻熵之和即划分后的信息熵可以有很大的削减,减小的最多的老大属性就是我们拔取的属性。

分开后的音信熵定义为:

必发365bifa0000 5 

因而用属性a对样本集D进行剪切的信息增益就是本来的音信熵减去划分后的音信熵:

必发365bifa0000 6

ID3算法就是那般每一次拔取一个性能对样本集举办划分,知道三种情景使那些历程甘休:

(1)某个子节点样本全体属于一类

(2)属性都用完了,那时候假使实节点样本依然不平等,那么只可以少数遵从多数了

必发365bifa0000 7(图片来源于网络)

ID3算法已毕(纯标称值)

比方样本全体是标称值即离散值的话,会比较不难。

代码:

必发365bifa0000 8必发365bifa0000 9

from math import log
from operator import itemgetter
def createDataSet():            #创建数据集
    dataSet = [[1,1,'yes'],
               [1,1,'yes'],
               [1,0,'no'],
               [0,1,'no'],
               [0,1,'no']]
    featname = ['no surfacing', 'flippers']
    return dataSet,featname
def filetoDataSet(filename):
    fr = open(filename,'r')
    all_lines = fr.readlines()
    featname = all_lines[0].strip().split(',')[1:-1]
    print(featname)
    dataSet = []
    for line in all_lines[1:]:
        line = line.strip()
        lis = line.split(',')[1:]
        dataSet.append(lis)
    fr.close()
    return dataSet,featname
def calcEnt(dataSet):           #计算香农熵
    numEntries = len(dataSet)
    labelCounts = {}
    for featVec in dataSet:
        label = featVec[-1]
        if label not in labelCounts.keys():
            labelCounts[label] = 0
        labelCounts[label] += 1
    Ent = 0.0
    for key in labelCounts.keys():
        p_i = float(labelCounts[key]/numEntries)
        Ent -= p_i * log(p_i,2)
    return Ent
def splitDataSet(dataSet, axis, value):   #划分数据集,找出第axis个属性为value的数据
    returnSet = []
    for featVec in dataSet:
        if featVec[axis] == value:
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
    return returnSet
def chooseBestFeat(dataSet):
    numFeat = len(dataSet[0])-1
    Entropy = calcEnt(dataSet)
    DataSetlen = float(len(dataSet))
    bestGain = 0.0
    bestFeat = -1
    for i in range(numFeat):
        allvalue = [featVec[i] for featVec in dataSet]
        specvalue = set(allvalue)
        nowEntropy = 0.0
        for v in specvalue:
            Dv = splitDataSet(dataSet,i,v)
            p = len(Dv)/DataSetlen
            nowEntropy += p * calcEnt(Dv)
        if Entropy - nowEntropy > bestGain:
            bestGain = Entropy - nowEntropy
            bestFeat = i
    return bestFeat
def Vote(classList):
    classdic = {}
    for vote in classList:
        if vote not in classdic.keys():
            classdic[vote] = 0
        classdic[vote] += 1
    sortedclassDic = sorted(classdic.items(),key=itemgetter(1),reverse=True)
    return sortedclassDic[0][0]
def createDecisionTree(dataSet,featnames):
    featname = featnames[:]              ################
    classlist = [featvec[-1] for featvec in dataSet]  #此节点的分类情况
    if classlist.count(classlist[0]) == len(classlist):  #全部属于一类
        return classlist[0]
    if len(dataSet[0]) == 1:         #分完了,没有属性了
        return Vote(classlist)       #少数服从多数
    # 选择一个最优特征进行划分
    bestFeat = chooseBestFeat(dataSet)
    bestFeatname = featname[bestFeat]
    del(featname[bestFeat])     #防止下标不准
    DecisionTree = {bestFeatname:{}}
    # 创建分支,先找出所有属性值,即分支数
    allvalue = [vec[bestFeat] for vec in dataSet]
    specvalue = sorted(list(set(allvalue)))  #使有一定顺序
    for v in specvalue:
        copyfeatname = featname[:]
        DecisionTree[bestFeatname][v] = createDecisionTree(splitDataSet(dataSet,bestFeat,v),copyfeatname)
    return DecisionTree
if __name__ == '__main__':
    filename = "D:\\MLinAction\\Data\\西瓜2.0.txt"
    DataSet,featname = filetoDataSet(filename)
    #print(DataSet)
    #print(featname)
    Tree = createDecisionTree(DataSet,featname)
    print(Tree)

View Code

解释一下多少个函数:

filetoDataSet(filename)
 将文件中的数据整理成数据集

calcEnt(dataSet)    
计算香农熵

splitDataSet(dataSet, axis, value)    
划分数据集,选取出第axis个特性的取值为value的富有数据集,即D^v,并去掉第axis个特性,因为不必要了

chooseBestFeat(dataSet)    
 依据新闻增益,选取一个最好的性能

Vote(classList)      
 若是属性用完,连串仍分化,投票决定

createDecisionTree(dataSet,featnames)    
递归成立决策树


用西瓜数据集2.0对算法举办测试,西瓜数据集见 西瓜数据集2.0,输出如下:

['色泽', '根蒂', '敲声', '纹理', '脐部', '触感']
{'纹理': {'清晰': {'根蒂': {'蜷缩': '是', '硬挺': '否', '稍蜷': {'色泽': {'青绿': '是', '乌黑': {'触感': {'硬滑': '是', '软粘': '否'}}}}}}, '稍糊': {'触感': {'硬滑': '否', '软粘': '是'}}, '模糊': '否'}}

为了可以反映决策树的优越性即决定方便,那里依照matplotlib模块编写可视化函数treePlot,对转移的决策树进行可视化,可视化结果如下:

必发365bifa0000 10

 

是因为数量太少,没有设置测试数据以表明其准确度,不过我背后会依据宫颈癌的事例进行准确度的测试的,上边进入下局地:

有一而再值的意况

有一而再值的情景如 西瓜数据集3.0 

一个属性有很多种取值,大家终将不可能每个取值都做一个分段,这时候须要对连接属性举行离散化,有二种办法供接纳,其中三种是:

1.对每一类其他数据集的连年值取平均值,再取各样的平均值的平均值作为划分点,将接连属性化为两类成为离散属性

2.C4.5利用的二分法,排序离散属性,取每五个的中段作为划分点的候选点,总结以每个划分点划分数据集的音讯增益,取最大的分外划分点将接连属性化为两类成为离散属性,用该属性举办分割的信息增益就是刚刚总括的最大新闻增益。公式如下:

必发365bifa0000 11

那里运用第三种,并在念书前对连年属性举办离散化。扩充处理的代码如下:

def splitDataSet_for_dec(dataSet, axis, value, small):
    returnSet = []
    for featVec in dataSet:
        if (small and featVec[axis] <= value) or ((not small) and featVec[axis] > value):
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
    return returnSet
def DataSetPredo(filename,decreteindex):
    dataSet,featname = filetoDataSet(filename)
    Entropy = calcEnt(dataSet)
    DataSetlen = len(dataSet)
    for index in decreteindex:     #对每一个是连续值的属性下标
        for i in range(DataSetlen):
            dataSet[i][index] = float(dataSet[i][index])
        allvalue = [vec[index] for vec in dataSet]
        sortedallvalue = sorted(allvalue)
        T = []
        for i in range(len(allvalue)-1):        #划分点集合
            T.append(float(sortedallvalue[i]+sortedallvalue[i+1])/2.0)
        bestGain = 0.0
        bestpt = -1.0
        for pt in T:          #对每个划分点
            nowent = 0.0
            for small in range(2):   #化为正类负类
                Dt = splitDataSet_for_dec(dataSet, index, pt, small)
                p = len(Dt) / float(DataSetlen)
                nowent += p * calcEnt(Dt)
            if Entropy - nowent > bestGain:
                bestGain = Entropy-nowent
                bestpt = pt
        featname[index] = str(featname[index]+"<="+"%.3f"%bestpt)
        for i in range(DataSetlen):
            dataSet[i][index] = "是" if dataSet[i][index] <= bestpt else "否"
    return dataSet,featname

重中之重是预处理函数DataSetPredo,对数据集提前离散化,然后再举办学习,学习代码类似。输出的决策树如下:

必发365bifa0000 12

有缺失值的事态

数据有缺失值是普遍的情况,大家糟糕间接甩掉这几个数据,因为那样会损失大量数额,不划算,但是缺失值大家也心慌意乱判定它的取值。怎么办吧,办法仍旧有些。

设想三个问题: 

1.有缺失值时怎么进展分割选用

2.已摘取划分属性,有缺失值的样本划不分开,怎样划分?

问题1:有缺失值时怎么进展私分选拔**

基本思想是进行最优属性选取时,先只考虑无缺失值样本,然后再乘以相应比例,得到在全体样本集上的大体意况。连带考虑到首个问题的话,考虑给每一个样书一个权重,此时每个样本不再总是被看作一个单独样本,那样便于第四个问题的缓解:即若样本在属性a上的值缺失,那么将其用作是所有值都取,只但是取每个值的权重不均等,每个值的权重参考该值在无缺失值样本中的比例,简单地说,比如在无缺失值样本集中,属性a取去八个值1和2,并且取1的权重和占整个权重和1/3,而取2的权重和占2/3,那么按照该属性对样本集进行分割时,遭遇该属性上有缺失值的样书,那么我们觉得该样本取值2的可能更大,于是将该样本的权重乘以2/3归到取值为2的范本集中继续举行分割构造决策树,而乘1/3划到取值为1的样本集中继续协会。不知道自家说领会没有。

公式如下:

必发365bifa0000 13

其中,D~表示数据集D在属性a上无缺失值的样本,按照它来判断a属性的上下,rho(即‘lou’)表示属性a的无缺失值样本占所有样本的比重,p~_k表示无缺失值样本中第k类所占的比重,r~_v表示无缺失值样本在属性a上取值为v的样本所占的比例。

在划分样本时,假若有缺失值,则将样本划分到所有子节点,在属性a取值v的子节点上的权重为r~_v
* 原来的权重。

更详细的解读参考《机器学习》P86-87。

根据权重法修改后的ID3算法完毕如下:

必发365bifa0000 14必发365bifa0000 15

from math import log
from operator import itemgetter

def filetoDataSet(filename):
    fr = open(filename,'r')
    all_lines = fr.readlines()
    featname = all_lines[0].strip().split(',')[1:-1]
    dataSet = []
    for line in all_lines[1:]:
        line = line.strip()
        lis = line.split(',')[1:]
        if lis[-1] == '2':
            lis[-1] = '良'
        else:
            lis[-1] = '恶'
        dataSet.append(lis)
    fr.close()
    return dataSet,featname

def calcEnt(dataSet, weight):           #计算权重香农熵
    labelCounts = {}
    i = 0
    for featVec in dataSet:
        label = featVec[-1]
        if label not in labelCounts.keys():
            labelCounts[label] = 0
        labelCounts[label] += weight[i]
        i += 1
    Ent = 0.0
    for key in labelCounts.keys():
        p_i = float(labelCounts[key]/sum(weight))
        Ent -= p_i * log(p_i,2)
    return Ent

def splitDataSet(dataSet, weight, axis, value, countmissvalue):   #划分数据集,找出第axis个属性为value的数据
    returnSet = []
    returnweight = []
    i = 0
    for featVec in dataSet:
        if featVec[axis] == '?' and (not countmissvalue):
            continue
        if countmissvalue and featVec[axis] == '?':
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
        if featVec[axis] == value:
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
            returnweight.append(weight[i])
        i += 1
    return returnSet,returnweight

def splitDataSet_for_dec(dataSet, axis, value, small, countmissvalue):
    returnSet = []
    for featVec in dataSet:
        if featVec[axis] == '?' and (not countmissvalue):
            continue
        if countmissvalue and featVec[axis] == '?':
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
        if (small and featVec[axis] <= value) or ((not small) and featVec[axis] > value):
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
    return returnSet

def DataSetPredo(filename,decreteindex):     #首先运行,权重不变为1
    dataSet,featname = filetoDataSet(filename)
    DataSetlen = len(dataSet)
    Entropy = calcEnt(dataSet,[1 for i in range(DataSetlen)])
    for index in decreteindex:     #对每一个是连续值的属性下标
        UnmissDatalen = 0
        for i in range(DataSetlen):      #字符串转浮点数
            if dataSet[i][index] != '?':
                UnmissDatalen += 1
                dataSet[i][index] = int(dataSet[i][index])
        allvalue = [vec[index] for vec in dataSet if vec[index] != '?']
        sortedallvalue = sorted(allvalue)
        T = []
        for i in range(len(allvalue)-1):        #划分点集合
            T.append(int(sortedallvalue[i]+sortedallvalue[i+1])/2.0)
        bestGain = 0.0
        bestpt = -1.0
        for pt in T:          #对每个划分点
            nowent = 0.0
            for small in range(2):   #化为正类(1)负类(0)
                Dt = splitDataSet_for_dec(dataSet, index, pt, small, False)
                p = len(Dt) / float(UnmissDatalen)
                nowent += p * calcEnt(Dt,[1.0 for i in range(len(Dt))])
            if Entropy - nowent > bestGain:
                bestGain = Entropy-nowent
                bestpt = pt
        featname[index] = str(featname[index]+"<="+"%d"%bestpt)
        for i in range(DataSetlen):
            if dataSet[i][index] != '?':
                dataSet[i][index] = "是" if dataSet[i][index] <= bestpt else "否"
    return dataSet,featname

def getUnmissDataSet(dataSet, weight, axis):
    returnSet = []
    returnweight = []
    tag = []
    i = 0
    for featVec in dataSet:
        if featVec[axis] == '?':
            tag.append(i)
        else:
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
        i += 1
    for i in range(len(weight)):
        if i not in tag:
            returnweight.append(weight[i])
    return returnSet,returnweight

def printlis(lis):
    for li in lis:
        print(li)

def chooseBestFeat(dataSet,weight,featname):
    numFeat = len(dataSet[0])-1
    DataSetWeight = sum(weight)
    bestGain = 0.0
    bestFeat = -1
    for i in range(numFeat):
        UnmissDataSet,Unmissweight = getUnmissDataSet(dataSet, weight, i)   #无缺失值数据集及其权重
        Entropy = calcEnt(UnmissDataSet,Unmissweight)      #Ent(D~)
        allvalue = [featVec[i] for featVec in dataSet if featVec[i] != '?']
        UnmissSumWeight = sum(Unmissweight)
        lou = UnmissSumWeight / DataSetWeight        #lou
        specvalue = set(allvalue)
        nowEntropy = 0.0
        for v in specvalue:      #该属性的几种取值
            Dv,weightVec_v = splitDataSet(dataSet,Unmissweight,i,v,False)   #返回 此属性为v的所有样本 以及 每个样本的权重
            p = sum(weightVec_v) / UnmissSumWeight          #r~_v = D~_v / D~
            nowEntropy += p * calcEnt(Dv,weightVec_v)
        if lou*(Entropy - nowEntropy) > bestGain:
            bestGain = Entropy - nowEntropy
            bestFeat = i
    return bestFeat

def Vote(classList,weight):
    classdic = {}
    i = 0
    for vote in classList:
        if vote not in classdic.keys():
            classdic[vote] = 0
        classdic[vote] += weight[i]
        i += 1
    sortedclassDic = sorted(classdic.items(),key=itemgetter(1),reverse=True)
    return sortedclassDic[0][0]

def splitDataSet_adjustWeight(dataSet,weight,axis,value,r_v):
    returnSet = []
    returnweight = []
    i = 0
    for featVec in dataSet:
        if featVec[axis] == '?':
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
            returnweight.append(weight[i] * r_v)
        elif featVec[axis] == value:
            retVec = featVec[:axis]
            retVec.extend(featVec[axis+1:])
            returnSet.append(retVec)
            returnweight.append(weight[i])
        i += 1
    return returnSet,returnweight

def createDecisionTree(dataSet,weight,featnames):
    featname = featnames[:]              ################
    classlist = [featvec[-1] for featvec in dataSet]  #此节点的分类情况
    if classlist.count(classlist[0]) == len(classlist):  #全部属于一类
        return classlist[0]
    if len(dataSet[0]) == 1:         #分完了,没有属性了
        return Vote(classlist,weight)       #少数服从多数
    # 选择一个最优特征进行划分
    bestFeat = chooseBestFeat(dataSet,weight,featname)
    bestFeatname = featname[bestFeat]
    del(featname[bestFeat])     #防止下标不准
    DecisionTree = {bestFeatname:{}}
    # 创建分支,先找出所有属性值,即分支数
    allvalue = [vec[bestFeat] for vec in dataSet if vec[bestFeat] != '?']
    specvalue = sorted(list(set(allvalue)))  #使有一定顺序
    UnmissDataSet,Unmissweight = getUnmissDataSet(dataSet, weight, bestFeat)   #无缺失值数据集及其权重
    UnmissSumWeight = sum(Unmissweight)      # D~
    for v in specvalue:
        copyfeatname = featname[:]
        Dv,weightVec_v = splitDataSet(dataSet,Unmissweight,bestFeat,v,False)   #返回 此属性为v的所有样本 以及 每个样本的权重
        r_v = sum(weightVec_v) / UnmissSumWeight          #r~_v = D~_v / D~
        sondataSet,sonweight = splitDataSet_adjustWeight(dataSet,weight,bestFeat,v,r_v)
        DecisionTree[bestFeatname][v] = createDecisionTree(sondataSet,sonweight,copyfeatname)
    return DecisionTree

if __name__ == '__main__':
    filename = "D:\\MLinAction\\Data\\breastcancer.txt"
    DataSet,featname = DataSetPredo(filename,[0,1,2,3,4,5,6,7,8])
    Tree = createDecisionTree(DataSet,[1.0 for i in range(len(DataSet))],featname)
    print(Tree)

View Code

有缺失值的处境如 西瓜数据集2.0alpha

尝试结果:

必发365bifa0000 16

在子宫肌瘤数据集上的测试与表现

有了算法,大家本来想做一定的测试看一看算法的表现。那里我选用了威斯康辛女性子宫肌瘤的数量。

数量总共有9列,每一列分别代表,以逗号分割

1 Sample
code number (病人ID)
2 Clump
Thickness 肿块厚度
3
Uniformity of Cell Size 细胞大小的均匀性
4
Uniformity of Cell Shape 细胞形状的均匀性
5
Marginal Adhesion 边缘粘
6 Single
Epithelial Cell Size 单上皮细胞的大大小小
7 Bare
Nuclei 裸核
8 Bland
Chromatin 乏味染色体
9 Normal
Nucleoli 正常核
10
Mitoses 有丝分裂
11 Class:
2 for benign, 4 formalignant(恶性或良性分类)

[from
Toby]

总结700条左右的数额,选拔最后80条作为测试集,前边作为训练集,举办学习。

利用分类器的代码如下:

import treesID3 as id3
import treePlot as tpl
import pickle

def classify(Tree, featnames, X):
    classLabel = "未知"
    root = list(Tree.keys())[0]
    firstGen = Tree[root]
    featindex = featnames.index(root)  #根节点的属性下标
    for key in firstGen.keys():   #根属性的取值,取哪个就走往哪颗子树
        if X[featindex] == key:
            if type(firstGen[key]) == type({}):
                classLabel = classify(firstGen[key],featnames,X)
            else:
                classLabel = firstGen[key]
    return classLabel

def StoreTree(Tree,filename):
    fw = open(filename,'wb')
    pickle.dump(Tree,fw)
    fw.close()

def ReadTree(filename):
    fr = open(filename,'rb')
    return pickle.load(fr)

if __name__ == '__main__':
    filename = "D:\\MLinAction\\Data\\breastcancer.txt"
    dataSet,featnames = id3.DataSetPredo(filename,[0,1,2,3,4,5,6,7,8])
    Tree = id3.createDecisionTree(dataSet[:620],[1.0 for i in range(len(dataSet))],featnames)
    tpl.createPlot(Tree)
    storetree = "D:\\MLinAction\\Data\\decTree.dect"
    StoreTree(Tree,storetree)
    #Tree = ReadTree(storetree)
    i = 1
    cnt = 0
    for lis in dataSet[620:]:
        judge = classify(Tree,featnames,lis[:-1])
        shouldbe = lis[-1]
        if judge == shouldbe:
            cnt += 1
        print("Test %d was classified %s, it's class is %s %s" %(i,judge,shouldbe,"=====" if judge==shouldbe else ""))
        i += 1
    print("The Tree's Accuracy is %.3f" % (cnt / float(i)))

教练出的决策树如下:

必发365bifa0000 17

最后的正确率可以看出:

必发365bifa0000 18

正确率约为96%左右,算是不差的分类器了。

我的急性化脓性乳腺炎数据见:http://7xt9qk.com2.z0.glb.clouddn.com/breastcancer.txt

迄今截至,决策树算法ID3的达成竣工,上边考虑基于基尼指数和音讯增益率进行分割采用,以及考虑完成剪枝过程,因为大家可以见到地方磨炼出的决策树还存在着许多冗余分支,是因为已毕进程中,由于数据量太大,每个分支都不完全纯净,所以会创制往下的分层,可是分支投票的结果又是平等的,而且数据量再大,特征数再多的话,决策树会卓殊大卓殊复杂,所以剪枝一般是必做的一步。剪枝分为先剪枝和后剪枝,假如细说的话可以写很多了。

此文亦可知:这里
参考资料:《机器学习》《机器学习实战》通过本次实战也意识了那两本书中的一些张冠李戴之处。

lz初学机器学习不久,如有错漏之处请多原谅提议照旧各位有何样想法或意见欢迎评论去告诉自己:)

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图