javascript实现朴素贝叶斯分类与决策树ID3分类

今年毕业时的毕设是有关大数据及机器学习的题目。因为那个时间已经步入前端的行业自然选择使用JavaScript来实现其中具体的算法。虽然JavaScript不是做大数据处理的最佳语言,相比还没有优势,但是这提升了自己对与js的理解以及弥补了一点点关于数据结构的弱点。对机器学习感兴趣的朋友还是去用 python,最终还是在学校的死板论文格式要求之外,记录一下实现的过程和我自己对于算法的理解。
源码在github:https://github.com/abzerolee/ID3_Bayes_JS
开始学习机器学习算法是通过 Tom M. Mitchel. Machine Learning[M] 1994 一书。喜欢研究机器学习的朋友入门可以看看这本。接下来叙述的也仅仅是个人对于算法的浅薄理解与实现,只是针对没有接触过机器学习的朋友看个乐呵,自己总结记忆一下。当然能引起大家对机器学习算法的研究热情是最好不过的了。

算法原理

实现过程其实是 对训练集合(已知分类)的数据进行分析解析得到一个分类模型,通过输入一条测试数据(未知分类),分类模型可以推断出该条数据的分类结果。训练数据如下图所示

这个数据集合意思为天气状况决定是否要最终去打网球 一个数组代表一条天气情况与对应结果。前四列代表数据的特征属性(天气,温度,湿度,是否刮风),最后一列代表分类结果。根据这个训练集,运用朴素贝叶斯分类和决策树ID3分类则可以得到一个数据模型,然后通过输入一条测试数据:“sunny cool high TRUE” 来判断是否回去打网球。相似的只要特征属性保持一定且有对应的分类结果,不论训练集为什么样的数据,都可以通过特征属性得到分类结果。所谓分类模型,就是通过一些概率论,统计学的理论基础,用编程语言实现。下面简单介绍一下两种算法原理。

一.朴素贝叶斯分类

大学概率论的贝叶斯定理实现了通过计算概率求出假设推理的结论。贝叶斯定理如下图所示:

E代表训练集合,r表示一个分类结果(即yes或no),P(E)是一个独立于分类结果r的常量,可以发现P(E)越大,P(r|E)受到训练集影响越小。
即可以得到为 P(r) => P(yes)=9/14,或者P(no)=5/14,
再求的条件概率 P(E|r) => P(wind=TRUE|yes)=3/9 P(wind=FALSE|no)=2/5
这样可以得到每个特征属性在分类结果情况下的条件概率。当输入一条测试数据时,通过计算这条数据特质属性值在某种分类假设的钱以下的条件概率,就可以得到对应的分类假设的概率,然后比较出最大值,称为极大似然假设,对应的分类结果就是测试数据的分类结果。
比如测试数据如上:sunny,cool,high,TRUE则对应的计算为:
P(yes)P(sunny|yes)P(high|yes)P(cool|yes)P(TRUE|yes) = P(yes|E)
P(no)P(sunny|no)P(high|no)P(cool|no)P(TRUE|no) = P(no|E)
推断出 no 。
这里推荐介绍贝叶斯文本分类的博客http://www.cnblogs.com/phinecos/archive/2008/10/21/1315948.html

二.决策树ID3分类法

决策树分类法更像是我们思考的过程:

测试数据和上文相同,在天气节点判断 则进入sunny分支 温度节点判断 进入high 分支则直接得出no的结果。
决策树在根据测试数据分类时浅显易懂,关键点在通过训练数据构建决策树,那相应的出现两个问题:
1.选择哪个特征属性作为根节点判断?
2.特征属性值对应的分支上的下一个属性节点如何来判断?
这两个问题可以总结为 如何判断最优测试属性?在信息论中,期望信息越小,那么信息增益就越大,从而纯度就越高。其实就是特征属性能够为最终的分类结果带来多少信息,带来的信息越多,该特征属性越重要。对一个属性而言,分类时它是否存在会导致分类信息量发生变化,而前后信息量的差值就是这个特征属性给分类带来的信息量。而信息量就是信息熵。信息熵表示每个离散的消息提供的平均信息量。
如上文中的例子:可以表示为

当选取了某个特征属性attr后的信息熵可以表示为

对应该属性的信息增益可以表示为

选择最适合树节点的特征属性,就是信息增益最大的属性。应该可以得到Gain(天气)=0.246
接下来是对该属性值分支的节点选取的判断,从训练集中找出满足该属性值的子集再次进行对于子集的每个属性的信息增益,比较。重复上述步骤,直到子集为空返回最普遍的分类结果。

上图为《Machine Learning》一书中对于ID3算法的介绍,下图为程序流程图

三.分类模型评估

分类模型的评估指标通过混淆矩阵来进行计算

P为样本数据中yes的数量,N为样本数据中no的数量,TP为正确预测yes的数量,FP为把yes预测为no的数量,FN为把yes预测为no的数量,TN为正确预测yes的数目。评估量度为
1.命中率:正确诊断确实患病的的概率 TP/P
2.虚警率:没有患病却诊断为患病概率。FP/N
分类模型的评估方法为交叉验证法与.632的平均抽样法,比如100条原始数据,对训练集有放回的随机抽样100次,并在每次抽样时标注抽取的次数 将大于63.2的数据作为训练集,小于的数据作为测试集,但是实际程序实现中可以样本偏离的太厉害我选择了44次作为标准。
这样将测试集的每一条数据输入,通过训练集得到的分类模型,得出测试数据的分类结果与真实分类进行比较。就可以得到混淆矩阵,最后根据混淆矩阵可以得到决策树与贝叶斯分类的命中率与虚警率。重复评估40次 则可以得到[命中率,虚警率],以命中率为纵坐标,虚警率为横坐标描点可以得到ROC曲线,描出的点越靠近左上角代表分类模型越正确,直观的表现出来两种分类模型差异。我得到的描点图如下所示

从图中明显可以发现对于小样本的数据,决策树分类模型更为准确。

核心代码

1.朴素贝叶斯分类法

const HashMap = require(‘./HashMap‘);

function Bayes($data){
  this._DATA = $data;
}
Bayes.prototype = {
  /**
   * 将训练数据单条数据按类别分类
   * @return HashMap<类别,对用类别的训练数据>
   */
  dataOfClass: function() {
    var map = new HashMap();
    var t = [], c = ‘‘;
    var datas = this._DATA;
    if(!(datas instanceof Array)) return;
    for(var i = 0; i < datas.length; i++){
      t = datas[i];
      c = t[t.length - 1];
      if(map.hasKey(c)){
        var ot = map.get(c);
        ot.push(t);
        map.put(c, ot);
      }else{
        var nt = [];
        nt.push(t);
        map.put(c, nt);
      }
    }
    return map;
  },
  /**
   * 预测测试数据的类别
   * @param Array testT 测试数据
   * @return String 测试数据对应类别
   */
  predictClass: function(testT){
    var doc = this.dataOfClass();
    var maxP = 0, maxPIndex = -1;
    var classes = doc.keys();
    for(var i = 0; i < classes.length; i++){
      var c = classes[i]
      var d = doc.get(c);
      var pOfC = d.length / this._DATA.length;
      for(var j = 0; j < testT.length; j++){
        var pv = this.pOfV(d, testT[j], j);
        pOfC = pOfC * pv;
      }
      if(pOfC > maxP){
        maxP = pOfC;
        maxPIndex = i;
      }
    }
    if(maxPIndex === -1 || maxPIndex > doc.length){
      return ‘无法分类‘;
    }
    return classes[maxPIndex];
  },
  /**
   * 计算指定属性在训练数据中指定值出现的条件概率
   * @param d     属于某一类的训练元组
   * @param value 指定属性
   * @param index 指定属性所在列
   * @return 特征属性在某类别下的条件概率
   */
  pOfV: function(d, value, index){
    var p = 0, count = 0, total = d.length, t = [];
    for(var i = 0; i < total; i++){
      if(d[i][index] === value)
        count++;
    }
    p = count / total;
    return p;
  }
}

module.exports = Bayes;

2.决策树ID3分类法

const HashMap = require(‘./HashMap‘);
const $data = require(‘./data‘);
const TreeNode = require(‘./TreeNode‘);
const InfoGain = require(‘./InfoGain‘);

function Iterator(arr){
  if(!(arr instanceof Array)){
    throw new Error(‘iterator needs a arguments that type is Array!‘);
  }
  this.arr = arr;
  this.length = arr.length;
  this.index = 0;
}
Iterator.prototype.current = function() {
  return this.arr[this.index-1];
}
Iterator.prototype.next = function(){
  this.index += 1;
  if(this.index > this.length || this.arr[this.index-1] === null)
    return false;
  return true;
}

function DecisionTree(data, attribute) {
  if(!(data instanceof Array) || !(attribute instanceof Array)){
    throw new Error(‘argument needs Array!‘);
  }
  this._data = data;
  this._attr = attribute;
  this._node = this.createDT(this._data,this._attr);
}
DecisionTree.prototype.createDT = function(data, attrList) {
  var node = new TreeNode();
  var resultMap = this.isPure(this.getTarget(data));

  if(resultMap.size() === 1){
    node.setType(‘result‘);
    node.setName(resultMap.keys()[0]);
    node.setVals(resultMap.keys()[0]);
    // console.log(‘单节点树:‘ + node.getVals());
    return node;
  }
  if(attrList.length === 0){
    var max = this.getMaxVal(resultMap);
    node.setType(‘result‘);
    node.setName(max)
    node.setVals(max);
    // console.log(‘最普遍性结果:‘+ max);
    return node;
  }

  var maxGain = this.getMaxGain(data, attrList).maxGain;
  var attrIndex = this.getMaxGain(data, attrList).attrIndex
  // console.log(‘选出的最大增益率属性为:‘+ attrList[attrIndex]);
  // console.log(‘创建节点:‘+attrList[attrIndex])
  node.setName(attrList[attrIndex]);
  node.setType(‘attribute‘);

  var remainAttr = new Array();
  remainAttr = attrList;
  // remainAttr.splice(attrIndex, 1);

  var self = this;
  var gain = new InfoGain(data, attrList)
  var attrValueMap = gain.getAttrValue(attrIndex); //最好分类的属性的值MAP
  var possibleValues = attrValueMap.keys();

  node_vals = possibleValues.map(function(v) {
    // console.log(‘创建分支:‘+v);
    var newData = data.filter(function(x) {
      return x[attrIndex] === v;
    });
    // newData = newData.map(function(v) {
    //   return v.slice(1);
    // })
    var child_node = new TreeNode(v, ‘feature_values‘);
    var leafNode = self.createDT(newData, remainAttr);
    child_node.setVals(leafNode);
    return child_node;
  })
  node.setVals(node_vals);

  this._node = node;
  return node;
}
/**
 * 判断训练数据纯度分类是否为一种分类或没有分类
 */
DecisionTree.prototype.getTarget = function(data){
  var list = new Array();
  var iter = new Iterator(data);
  while(iter.next()){
    var index = iter.current().length - 1;
    var value = iter.current()[index];
    list.push(value);
  }
  return list;
},
/**
 * 获取分类结果数组,判断纯度
 */
DecisionTree.prototype.isPure = function(list) {
  var map = new HashMap(), count = 1;
  list.forEach(function(item) {
    if(map.get(item)){
      count++;
    }
    map.put(item, count);
  });
  return map;
}
/**
 * 获取最大增益量属性
 */
DecisionTree.prototype.getMaxGain = function(data, attrList) {
  var gain = new InfoGain(data, attrList);
  var maxGain = 0;
  var attrIndex = -1;
  for(var i = 0; i < attrList.length; i++){
    var temp = gain.getGainRaito(i);
    if(maxGain < temp){
      maxGain = temp;
      attrIndex = i;
    }
  }
  return {attrIndex: attrIndex, maxGain: maxGain};
}
/**
 * 获取resultMap中值最大的key
 */
DecisionTree.prototype.getMaxVal = function(map){
  var obj = map.obj, temp = 0, okey = ‘‘;
  for(var key in obj){
    if(temp < obj[key] && typeof obj[key] === ‘number‘){
      temp = obj[key];
      okey = key;
    };
  }
  return okey;
}
/**
 * 预测属性
 */
DecisionTree.prototype.predictClass = function(sample){
  var root = this._node;
  var map = new HashMap();
  var attrList = this._attr;
  for(var i = 0; i < attrList.length; i++){
    map.put(attrList[i], sample[i]);
  }

  while(root.type !== ‘result‘){
    if(root.name === undefined){
      return root = ‘无法分类‘;
    }
    var attr = root.name;
    var sample = map.get(attr);
    var childNode = root.vals.filter(function(node) {
      return node.name === sample;
    });
    if(childNode.length === 0){
      return root = ‘无法分类‘;
    }
    root = childNode[0].vals; // 只遍历attribute节点
  }
  return root.vals;
}

module.exports = DecisionTree;

3.增益率计算

const HashMap = require(‘./HashMap‘);

function Iterator(arr){
  if(!(arr instanceof Array)){
    throw new Error(‘iterator needs a arguments that type is Array!‘);
  }
  this.arr = arr;
  this.length = arr.length;
  this.index = 0;
}
Iterator.prototype.current = function() {
  return this.arr[this.index-1];
}
Iterator.prototype.next = function(){
  this.index += 1;
  if(this.index > this.length || this.arr[this.index-1] === null)
    return false;
  return true;
}

/**
 * 计算信息增益类
 * @param Array data 训练数据集
 * @param Array data 作用的特征属性
 */
function InfoGain(data, attr) {
  if(!(data instanceof Array) || !(attr instanceof Array)){
    throw new Error(‘arguments needs Array!‘);
  }
  this._data = data;
  this._attr = attr;
}
InfoGain.prototype = {
  /**
   * 获取训练数据分类个数
   * @return hashMap<类别, 该类别数量>
   */
  getTargetValue: function() {
    var map = new HashMap();
    var iter = new Iterator(this._data);
    while(iter.next()){
      var t = iter.current();
      var key = t[t.length-1];
      var value = map.get(key);
      map.put(key, value !== undefined ? ++value : 1);
    }
    return map;
  },
  /**
   * 获取训练数据信息熵
   * @return 训练数据信息熵
   */
  getEntroy: function(){
    var targetValueMap = this.getTargetValue();
    var targetKey = targetValueMap.keys(), entroy = 0;
    var self = this;
    var iter = new Iterator(targetKey);
    while(iter.next()){
      var p = targetValueMap.get(iter.current()) / self._data.length;
      entroy += (-1) * p * (Math.log(p) / Math.LN2);
    }
    return entroy;
  },
  /**
   * 获取属性值在训练数据集中的数量
   * @param number index 属性名数组索引
   */
  getAttrValue: function(index){
    var map = new HashMap();
    var iter = new Iterator(this._data);
    while(iter.next()){
      var t = iter.current();
      var key = t[index];
      var value = map.get(key);
      map.put(key, value !== undefined ? ++value : 1);
    }
    return map;
  },
  /**
   * 得到属性值在决策空间的比例
   * @param string name 属性值
   * @param number index 属性所在第几列
   */
  getAttrValueTargetValue: function(name, index){
    var map = new HashMap();
    var iter = new Iterator(this._data);
    while(iter.next()){
      var t = iter.current();
      if(name === t[index]){
        var size = t.length;
        var key = t[t.length-1];
        var value = map.get(key);
        map.put(key, value !== undefined ? ++value : 1);
      }
    }
    return map;
  },
  /**
   * 获取特征属性作用于训练数据集后分类出的数据集的熵
   * @param number index 属性名数组索引
   */
  getInfoAttr: function(index){
    var attrValueMap = this.getAttrValue(index);
    var infoA = 0;
    var c = attrValueMap.keys();
    for(var i = 0; i < attrValueMap.size(); i++){
      var size = this._data.length;
      var attrP = attrValueMap.get(c[i]) / size;
      var targetValueMap = this.getAttrValueTargetValue(c[i], index);
      var totalCount = 0 ,valueSum = 0;
      for(var j = 0; j < targetValueMap.size(); j++){
        totalCount += targetValueMap.get(targetValueMap.keys()[j]);
      }
      for(var k = 0; k < targetValueMap.size(); k++){
        var p = targetValueMap.get(targetValueMap.keys()[k]) / totalCount;
        valueSum += (Math.log(p) / Math.LN2) * p;
      }
      infoA += (-1) * attrP * valueSum;
    }
    return infoA;
  },
  /**
   * 获得信息增益量
   */
  getGain: function(index) {
    return this.getEntroy() - this.getInfoAttr(index);
  },
  getSplitInfo: function(index){
    var map = this.getAttrValue(index);
    var splitA = 0;
    for(var i = 0; i < map.size(); i++){
      var size = this._data.length;
      var attrP = map.get(map.keys()[i]) / size;
      splitA += (-1) * attrP * (Math.log(attrP) / Math.LN2);
    }
    return splitA;
  },
  /**
   * 获得增益率
   */
  getGainRaito: function(index){
    return this.getGain(index) / this.getSplitInfo(index);
  },
  getData4Value: function(attrValue, attrIndex){
    var resultData = new Array();
    var iter = new Iterator(this._data);
    while(iter.next()){
      var temp = iter.current();
      if(temp[attrIndex] === attrValue){
        resultData.push(temp);
      }
    }
    return resultData;
  }
}
// var gain = new InfoGain($data, [‘sunny‘]);

// console.log(gain.getGainRaito(0), gain.getGainRaito(1),gain.getGainRaito(2),gain.getGainRaito(3))
// console.log(gain.getGain(0),gain.getGain(1),gain.getGain(2),gain.getGain(3))
module.exports = InfoGain;

具体的程序实现我会再继续介绍的,待续。。。。

时间: 2024-08-03 05:47:00

javascript实现朴素贝叶斯分类与决策树ID3分类的相关文章

机器学习经典算法详解及Python实现---朴素贝叶斯分类及其在文本分类、垃圾邮件检测中的应用

摘要: 朴素贝叶斯分类是贝叶斯分类器的一种,贝叶斯分类算法是统计学的一种分类方法,利用概率统计知识进行分类,其分类原理就是利用贝叶斯公式根据某对象的先验概率计算出其后验概率(即该对象属于某一类的概率),然后选择具有最大后验概率的类作为该对象所属的类.总的来说:当样本特征个数较多或者特征之间相关性较大时,朴素贝叶斯分类效率比不上决策树模型:当各特征相关性较小时,朴素贝叶斯分类性能最为良好.另外朴素贝叶斯的计算过程类条件概率等计算彼此是独立的,因此特别适于分布式计算.本文详述了朴素贝叶斯分类的统计学

基于朴素贝叶斯分类器的文本分类算法

源代码下载:NaviveBayesClassify.rar Preface 文本的分类和聚类是一个比较有意思的话题,我以前也写过一篇blog<基于K-Means的文本聚类算法>,加上最近读了几本数据挖掘和机器学习的书籍,因此很想写点东西来记录下学习的所得. 在本文的上半部分<基于朴素贝叶斯分类器的文本分类算法(上)>一文中简单介绍了贝叶斯学习的基本理论,这一篇将展示如何将该理论运用到中文文本分类中来,具体的文本分类原理就不再介绍了,在上半部分有,也可以参见代码的注释. 文本特征向量

基于朴素贝叶斯分类器的文本分类

实验要求 题目要求 1.用MapReduce算法实现贝叶斯分类器的训练过程,并输出训练模型: 2.用输出的模型对测试集文档进行分类测试.测试过程可基于单机Java程序,也可以是MapReduce程序.输出每个测试文档的分类结果: 3.利用测试文档的真实类别,计算分类模型的Precision,Recall和F1值. 2.实验环境 实验平台:VMware Workstation10 虚拟机系统:Suse11 集群环境:主机名master  ip:192.168.226.129 从机名slave1 

朴素贝叶斯分类--python实现

1.概述 朴素贝叶斯分类是贝叶斯分类器的一种,贝叶斯分类算法是统计学的一种分类方法,利用概率统计知识进行分类,其分类原理就是利用贝叶斯公式根据某对象的先验 概率计算出其后验概率(即该对象属于某一类的概率),然后选择具有最大后验概率的类作为该对象所属的类.总的来说:当样本特征个数较多或者特征之间相关性较大时,朴素贝叶斯分类效率比不上决策树模型:当各特征相关性较小时,朴素贝叶斯分类性能最为良好.另外朴素贝叶斯的计算过程类条件概率等计算彼此是独立的,因此特别适于分布式计算.本文详述了朴素贝叶斯分类的统

数据挖掘系列(8)朴素贝叶斯分类算法原理与实践

隔了很久没有写数据挖掘系列的文章了,今天介绍一下朴素贝叶斯分类算法,讲一下基本原理,再以文本分类实践. 一个简单的例子 朴素贝叶斯算法是一个典型的统计学习方法,主要理论基础就是一个贝叶斯公式,贝叶斯公式的基本定义如下: 这个公式虽然看上去简单,但它却能总结历史,预知未来.公式的右边是总结历史,公式的左边是预知未来,如果把Y看出类别,X看出特征,P(Yk|X)就是在已知特征X的情况下求Yk类别的概率,而对P(Yk|X)的计算又全部转化到类别Yk的特征分布上来. 举个例子,大学的时候,某男生经常去图

理解朴素贝叶斯分类器的三层境界

1.背景 首先,在文章的开头,先提出几个问题,如果这些问题你都答得上来,那么本文你就无需阅读了,或者你阅读的动机纯粹是给本文挑毛病,当然我也无比欢迎,请发送邮件"毛病の朴素贝叶斯"发送至[email protected],我会认真阅读你的来信. By the way,如果阅读完本文,你还是无法回答以下问题,那么也请你邮件通知我,我会尽量解答你的疑惑. 朴素贝叶斯分类器中的"朴素"特指此分类器的什么特性 朴素贝叶斯分类器与极大似然估计(MLE).最大后验概率(MAP)

SparkMLib分类算法之朴素贝叶斯分类

SparkMLib分类算法之朴素贝叶斯分类 (一)朴素贝叶斯分类理解 朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法.简单来说,朴素贝叶斯分类器假设样本每个特征与其他特征都不相关.举个例子,如果一种水果具有红,圆,直径大概4英寸等特征,该水果可以被判定为是苹果.尽管这些特征相互依赖或者有些特征由其他特征决定,然而朴素贝叶斯分类器认为这些属性在判定该水果是否为苹果的概率分布上独立的.尽管是带着这些朴素思想和过于简单化的假设,但朴素贝叶斯分类器在很多复杂的现实情形中仍能够取得相当好的效果.

分类算法——朴素贝叶斯分类

贝叶斯分类是利用概率统计知识进行分类的算法,其分类原理是贝叶斯定理.贝叶斯定理的公式如下: 贝叶斯公式表明,我们可以从先验概率P(A).条件概率P(B|A)和证据P(B)来计算出后验概率. 朴素贝叶斯分类器就是假设证据之间各个条件相互独立的基础上,根据计算的后验概率选择各类别后验概率最大的类别作为目标证据的类别. 构建朴素贝叶斯分类器的步骤如下: 1.根据训练样例分别计算每个类别出现的概率P(Ai), 2.对每个特征属性计算所有划分的条件概率P(Bi|Ai), 3.对每个类别计算P(B|Ai)*

决策树ID3算法[分类算法]

ID3分类算法的编码实现 1 <?php 2 /* 3 *决策树ID3算法(分类算法的实现) 4 */ 5 6 /* 7 *把.txt中的内容读到数组中保存 8 *$filename:文件名称 9 */ 10 11 //-------------------------------------------------------------------- 12 function gerFileContent($filename) 13 { 14 $array = array(NULL); 15