【作业三】林轩田机器学习技法

这次关注的作业题目是Q13~Q20,主要是实现basic C&RT分类树,以及由其构成的Random Forest。

其中basic C&RT分类树的实现思路如下:

(一)先抽象出来几个功能:

1)从local file读数据并转化成numpy.array的形式(考虑空行容错)(def read_input_data(path)

2)如何根据某个维度的feature,计算这个feature产生的branch criteria(此题中为decision stump)(def learn_decisionStump(x,y)

3)比较多个feature产生的branch criteria,挑选最好的作为当前节点的b(x),并把数据分成两份送到左右子树 (def splited_by_decisionStump(x, y)

4)给定一组y,计算其gini index ( def calculate_GiniIndex(y) )

(二)根据上述几个抽象的功能,用dfs递归方法实现C&RT分类树:

1)终止条件:空(返回空)、输入数据都属于一类(这里是一个坑,不能直接返回空,需要构造一个叶子节点再返回;这里叶子节点的index设为-1,用于标示走到叶子节点)

2)利用抽象出来功能,计算branch criteria,生成新节点

3)将数据分为两组并送到left和right两个分支分别获得分支的sub branch criteria

4)返回这一层新生成的节点

(三)如何保存模型学习结果?

随着树的生长不断进行,记录每个节点的branch criteria,并以binary search tree的形式存放model。

(四)如何预测?

推来一个新的样本,只要根据每一层branch criteria中的index和val,来判断往哪走(注意,这个过程是根sign没关系的);直到走到叶子节点(index==-1判断叶子节点),看叶子节点的sign是啥,输入数据的标签就是啥。

按照(一)~(四),再细心一些,就可以把模型写做出来了。至于后面的Random Forest的过程,就是把先sampling再不断生成C&RT树。

这里没有对高效的sampling方法探究,感觉自己的sampling方式很shit。但是稍微等等,也可以出来正确的结果。

全部代码如下:

#encoding=utf8
import sys
import numpy as np
import math
from random import *

##
# tree node for storing C&RT model‘s decision node
# i: feature index
# v: decision-stump threshold value
# s: decision-stump sign ( direction )
# left: left branch TreeNode
# right: right branch TreeNode
class TreeNode:
    def __init__(self, i, v):
        self.index = i
        self.val = v
        self.sign = 0
        self.left = None
        self.right = None

##
# read data from local file
# return with numpy array
def read_input_data(path):
    x = []
    y = []
    for line in open(path).readlines():
        if line.strip()==‘‘: continue
        items = line.strip().split(‘ ‘)
        tmp_x = []
        for i in range(0,len(items)-1): tmp_x.append(float(items[i]))
        x.append(tmp_x)
        y.append(float(items[-1]))
    return np.array(x),np.array(y)

##
# input All data ( binary categories in this context )
# learning decision-stump from the data
# splited subdata via learned decision-stump
# return two splited data, index, val, sign
def splited_by_decisionStump(x, y):
    # storeing sorted index via all x‘s certain feature
    sorted_index = []
    for i in range(0, x.shape[1]):
        sorted_index.append(np.argsort(x[:,i]))
    # learn the best feature for this node‘s decision stump
    n1 = x.shape[0]/2
    n2 = x.shape[0]-n1
    Branch = float("inf")
    index = -1
    val = 0
    for i in range(0, x.shape[1]):
        # learn decision stump via x[i]
        xi = x[sorted_index[i], i]
        yi = y[sorted_index[i]]
        # minimize cost function of feature i
        b, v = learn_decisionStump(xi, yi)
        # update least impuirty parameter (val,sign)
        if Branch>b:
            Branch = b
            index = i
            val = v
    # spliting data with best feature and it‘s val & sign
    leftX = x[np.where(x[:,index]<val)]
    leftY = y[np.where(x[:,index]<val)]
    rightX = x[np.where(x[:,index]>=val)]
    rightY = y[np.where(x[:,index]>=val)]
    return leftX, leftY, rightX, rightY, index, val

# learn decision-stump threshold from one feature dimension
def learn_decisionStump(x,y):
    # calculate median of interval
    thetas = np.array([ (x[i]+x[i+1])/2 for i in range(0, x.shape[0]-1) ] )
    B = float("inf")
    target_theta = 0.0
    # traversal each median value
    for theta in thetas:
        ly = y[np.where(x<theta)]
        ry = y[np.where(x>=theta)]
        b = ly.shape[0]*calculate_GiniIndex(ly) + ry.shape[0]*calculate_GiniIndex(ry)
        if B>b:
            B = b
            target_theta = theta
    return B, target_theta

##
# input data ( binary catergories in this context )
# return with Gini Index
def calculate_GiniIndex(y):
    if y.shape[0]==0: return 0
    n1 = sum(y==1)
    n2 = sum(y==-1)
    if (n1+n2)==0: return 0
    return 1.0 - math.pow(1.0*n1/(n1+n2),2) - math.pow(1.0*n2/(n1+n2),2)

##
# C&RT tree‘s dfs learning algorithm
# return with learned model within a binary tree
def CART(x, y):
    if x.shape[0]==0: return None # none case
    if calculate_GiniIndex(y)==0: # terminal case ( only one category )
        node = TreeNode(-1, -1)
        node.sign = 1 if y[0]==1 else -1
        return node
    leftX, leftY, rightX, rightY, index, val = splited_by_decisionStump(x,y)
    node = TreeNode(index,val)
    node.left = CART(leftX, leftY)
    node.right = CART(rightX, rightY)
    return node

## Q13
# count internal nodes
def count_internal_nodes(root):
    if root==None: return 0
    if root.left==None and root.right==None: return 0
    print root.index, root.val
    l = 0
    r = 0
    if root.left!=None:
        l = count_internal_nodes(root.left)
    if root.right!=None:
        r = count_internal_nodes(root.right)
    return 1 + l + r

## Q15
# predict
def predict(root, x):
    if root.index==-1: return root.sign
    if x[root.index]<root.val:
        return predict(root.left, x)
    else:
        return predict(root.right, x)
# calculate Eout
def calculate_E(model, path):
    x,y = read_input_data(path)
    error_count = 0
    for i in range(0, x.shape[0]):
        error_count = error_count + (1 if predict(model, x[i])!=y[i] else 0)
    return 1.0*error_count/x.shape[0]

## Q16
# Random Forest via Bagging and average Ein(gt)
def randomForest(x, y, T):
    error_rate = 0
    trees = []
    for i in range(0,T):
        xi,yi = naive_sampling(x, y)
        model = CART(xi,yi)
        error_rate += calculate_E(model,"train.dat")
        trees.append(model)
    return error_rate/T, trees
# holy shit naive sampling
def naive_sampling(x, y):
    sampleX = np.zeros(x.shape)
    sampleY = np.zeros(y.shape)
    for i in range(0, x.shape[0]):
        index = randint(0, x.shape[0]-1)
        sampleX[i] = x[index]
        sampleY[i] = y[index]
    return sampleX, sampleY

## Q17 Q18
# Ein(G)
def calculate_RF_E(trees, path):
    x,y = read_input_data(path)
    error_count = 0
    for i in range(0, x.shape[0]):
        yp = rf_predict(trees, x[i])
        error_count += 1 if yp!=y[i] else 0
    return 1.0*error_count/x.shape[0]
# random forest predict process
def rf_predict(trees, x):
    positives = 0
    negatives = 0
    for tree in trees:
        yp = predict(tree, x)
        if yp==1:
            positives += 1
        else:
            negatives += 1
    return 1 if positives>negatives else -1

## Q19
# prune to only one branch
def one_branch_CART(x, y):
    if x.shape[0]==0: return None # none case
    if calculate_GiniIndex(y)==0: # terminal case ( only one category )
        node = TreeNode(-1, -1)
        node.sign = 1 if y[0]==1 else -1
        return node
    leftX, leftY, rightX, rightY, index, val = splited_by_decisionStump(x,y)
    node = TreeNode(index, val)
    node.left = TreeNode(-1, -1)
    node.right = TreeNode(-1, -1)
    ly = y[np.where(x[:,index]<val)]
    node.left.sign = 1 if sum(ly==1)>sum(ly==-1) else -1
    node.right.sign = -node.left.sign
    return node

def one_branch_randomForest(x, y, T):
    trees = []
    for i in range(0,T):
        xi,yi = naive_sampling(x, y)
        model = one_branch_CART(xi, yi)
        trees.append(model)
    return trees

def main():
    # x,y = read_input_data("unitTestSplitedByDecisionStump.dat")
    x,y = read_input_data("train.dat")
    root = CART(x,y)
    print count_internal_nodes(root)
    print calculate_E(root, "test.dat")
    error_rate,trees = randomForest(x, y, 301)
    print error_rate
    print calculate_RF_E(trees, "train.dat")
    print calculate_RF_E(trees, "test.dat")
    trees = one_branch_randomForest(x, y, 301)
    print calculate_RF_E(trees, "train.dat")
    print calculate_RF_E(trees, "test.dat")

if __name__ == ‘__main__‘:
    main()

通过这几道题目,体会了Random Forest的好处:

1)Random Forest的每棵树都不是最强的,但是整合在一起可以很强(如Ein做到0)

2)虽然单棵不剪枝tree的Ein也可以做到0,但是泛化性能(Eout)就比较弱了

3)Random Forest巧用了Bagging的方法,整合了多棵tree:不用剪枝,还增加了泛化性能

4)如果削弱Random Forest中的每棵树的功能,对整体效果是有影响的

时间: 2024-12-18 03:40:06

【作业三】林轩田机器学习技法的相关文章

【作业四】林轩田机器学习技法 + 机器学习公开新课学习个人体会

这次作业的coding任务量比较大,总的来说需要实现neural network, knn, kmeans三种模型. Q11~Q14为Neural Network的题目,我用单线程实现的,运行的时间比较长,因此把这几道题的正确答案记录如下: Q11: 6 Q12: 0.001 Q13: 0.01 Q14: 0.02 ≤ Eout ≤ 0.04 其中Q11和Q14的答案比较明显,Q12和Q13有两个答案比较接近(参考了讨论区的内容,最终也调出来了) neural network的代码实现思路如下:

【Matrix Factorization】林轩田机器学习技法

在NNet这个系列中讲了Matrix Factorization感觉上怪怪的,但是听完第一小节课程就明白了. 林首先介绍了机器学习里面比较困难的一种问题:categorical features 这种问题的特征就是一些ID编号这类的,不是numerical的. 如果要处理这种情况,需要encoding from categorical to numerical 最常用的一种encoding方法就是binary vector encoding(也是实习工作中用过的路子),将binary vecto

【Deep Learning】林轩田机器学习技法

这节课的题目是Deep learning,个人以为说的跟Deep learning比较浅,跟autoencoder和PCA这块内容比较紧密. 林介绍了deep learning近年来受到了很大的关注:deep NNet概念很早就有,只是受限于硬件的计算能力和参数学习方法. 近年来深度学习长足进步的原因有两个: 1)pre-training技术获得了发展 2)regularization的技术获得了发展 接下来,林开始介绍autoencoder的motivation. 每过一个隐层,可以看做是做了

【Linear Support Vector Machine】林轩田机器学习技法

首先从介绍了Large_margin Separating Hyperplane的概念. (在linear separable的前提下)找到largest-margin的分界面,即最胖的那条分界线.下面开始一步步说怎么找到largest-margin separating hyperplane. 接下来,林特意强调了变量表示符号的变化,原来的W0换成了b(这样的表示利于推导:觉得这种强调非常负责任,利于学生听懂,要不然符号换来换去的,谁知道你说的是啥) 既然目标是找larger-margin s

【Random Forest】林轩田机器学习技法

总体来说,林对于random forest的讲解主要是算法概况上的:某种程度上说,更注重insights. 林分别列举了Bagging和Decision Tree的各自特点: Random Forest就是这二者的结合体. 1)便于并行化 2)保留了C&RT的优势 3)通过bagging的方法削弱了fully-grown tree的缺点 这里提到一个insights:如果各个分类器的diversity越大,aggregation之后的效果可能就越好. 因此,Random Forest不仅样本是b

【Neural Network】林轩田机器学习技法

首先从单层神经网络开始介绍 最简单的单层神经网络可以看成是多个Perception的线性组合,这种简单的组合可以达到一些复杂的boundary. 比如,最简单的逻辑运算AND  OR NOT都可以由多个perception构成的单层神经网络模拟. 但是,单层感知器神经网络能力再强也是有限的,有些逻辑也无法完成.比如,XOR异或运算. 这个时候,就很自然地引出了多层神经网络. 通过这个例子,可以看到多层的神经网络的表达能力要比单层的要强. 上面给出了看待神经网络的一种方式: 1)从原始输入开始一直

【Adaptive Boosting】林轩田机器学习技法

首先用一个形象的例子来说明AdaBoost的过程: 1. 每次产生一个弱的分类器,把本轮错的样本增加权重丢入下一轮 2. 下一轮对上一轮分错的样本再加重学习,获得另一个弱分类器 经过T轮之后,学得了T个弱分类器,再将这T个弱分类器组合在一起,形成了一个强分类器. 由于每一轮样本的权重都在变化,因此分类器学习的目标函数也产生了变化: 无论是SVM还是Logistic Regression都可以用这种方式学习,给每个样本增加不同的权重. 接下来问题就变成了,如何调整样本的权重?目的是什么? 林介绍了

【Dual Support Vector Machine】林轩田机器学习技法

这节课内容介绍了SVM的核心. 首先,既然SVM都可以转化为二次规划问题了,为啥还有有Dual啥的呢?原因如下: 如果x进行non-linear transform后,二次规划算法需要面对的是d`+1维度的N个变量,以及N个约束 如果d`的维度超大,那么二次规划解起来的代价就太大了.因此,SVM的精髓就在于做了如下的问题转化: 不需要问太深奥的数学,知道为啥要dual的motivation就可以了. 这里再次搬出前人的智慧:Lagrange Multipliers 但是这里跟ridge regr

【Support Vector Regression】林轩田机器学习技法

上节课讲了Kernel的技巧如何应用到Logistic Regression中.核心是L2 regularized的error形式的linear model是可以应用Kernel技巧的. 这一节,继续沿用representer theorem,延伸到一般的regression问题. 首先想到的就是ridge regression,它的cost函数本身就是符合representer theorem的形式. 由于optimal solution一定可以表示成输入数据的线性组合,再配合Kernel T