本年毕业时的毕设是有关大数据及机械进修的题目。由于谁人时候已步入前端的行业自然挑选应用JavaScript来完成个中详细的算法。虽然JavaScript不是做大数据处理的最好言语,比拟还没有上风,然则这提升了本身对与js的明白以及弥补了一点点关于数据结构的缺点。对机械进修感兴趣的朋侪照样去用 python,终究照样在学校的枯燥论文格式请求以外,纪录一下完成的历程和我本身关于算法的明白。
源码在github:https://github.com/abzerolee/…
最先进修机械进修算法是经由过程 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/phinec…
二.决策树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曲线,描出的点越接近左上角代表分类模子越正确,直观的表现出来两种分类模子差别。我取得的描点图以下所示
从图中显著能够发明关于小样本的数据,决策树分类模子更加正确。
中心代码
质朴贝恭弘=叶 恭弘斯分类法
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.增益率盘算
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;
}
}
详细的顺序完成我会再继承引见的,待续。。。。
第一次在segmentfault发文章 有点慌张 列位有什么看法或许主意能够实时斧正我。