H. 为了进行数据集的测试,需要知道每个测试集分类过后的标签,因此使用find()方法,递归的找到每个枝杈上的叶子节点,叶子节点的Value值就是当前的分类情况。因为在创建树的时候已经将每个叶子节点的dim设置为了-1,因此只需要判断tree.getdim是否为-1就可以知道是否为叶子节点,如果不是则分别在左右子树上找,直到找到叶子节点:
def find(node, data):
if node.getdim() == -1:
return node.getValue()
else:
reducedFeatVec1 = []
reducedFeatVec1 = data[:node.getdim()]
reducedFeatVec1.extend(data[node.getdim() + 1:])
if data[node.getdim()] > node.getValue():
return find(node.getLeft(), reducedFeatVec1)
elif data[node.getdim()] <= node.getValue():
return find(node.getRight(), reducedFeatVec1)
I. 找到了每个测试集的分类之后就可以进行正确率的判断,如果预测的类和真实标签相同则+1:
def classify(dataset, tree):
cout = 0
for example in dataset:
a = find(tree, example)
b = example[-1]
if a == b:
cout += 1
return cout
J. 通过预剪枝的方法建造一棵树,首先判断停止的条件与上面的相同,传入的参数多了一个测试集。用count1和count2分别表示在这个特征上不分开和分开的正确的个数。首先获得当前节点的value值,就是通过投票而获得的当前节点的类标签,通过这个值和真实标签作比较。之后对训练集在这个节点处进行划分,有得到一个划分后的正确率,如果前者大,则停止划分,将其的dim设置为-1,如果前者小则继续分叉,在其左子树和右子树上进行同样的操作:
# 预剪枝
def precutting(dataset, tree, testdata):
count1 = 0
count2 = 0
classList = [example[-1] for example in dataset]
if (classList.count(classList[0]) == len(classList)):
tree.setValue(classList[0])
return tree.setdim(-1)
if (len(dataset[0]) == 1):
tree.setValue(majorityCnt(classList))
return tree.setdim(-1)
bestvalue1 = tree.getValue()
for dt in testdata:
if (dt[-1] == bestvalue1):
count1 += 1
bestDim, bestValue = ChooseBestBranch(dataset)
tree.setdim(bestDim)
tree.setValue(bestValue)
leftTree = TreeNode()
rightTree = TreeNode()
LeftData, RightData = splitContinuousDataSet(dataset, bestDim, bestValue)
LeftTData, RightTData = splitContinuousDataSet(testdata, bestDim, bestValue)
# left
if (len(LeftTData) > 0 and len(LeftTData[0]) > 0):
classListleft = [example[-1] for example in LeftTData]
leftValue = majorityCnt(classListleft)
for dt in LeftTData:
if (dt[-1] == leftValue):
count2 += 1
# right
if (len(RightTData) > 0 and len(RightTData[0]) > 0):
classListright = [example[-1] for example in RightTData]
rightValue = majorityCnt(classListright)
for dt in RightTData:
if (dt[-1] == rightValue):
count2 += 1
if (count1 < count2):
if (len(LeftData) > 0):
precutting(LeftData, leftTree, LeftTData)
if (len(RightData) > 0):
precutting(RightData, rightTree, RightTData)
tree.setLeft(leftTree)
tree.setRight(rightTree)
K. 后剪枝操作,首先是用create方法生成一个树,然后进行后剪枝。从底层节点向上开始考虑,如果该节点的dim值为-1 则返回,之后对上一层进行正确率的判断。在找叶子节点的同时,也将测试集分成了两个部分。然后计算这个节点分支后的正确率和不让这个节点分支的正确率,如果前者的正确率大,则将该节点的dim设置为-1,也就是直接把这个节点变成了叶子:
def posttree(tree, testdata):
# 找到叶子节点
if (tree.getdim() == -1):
return
else:
lefttest, righttest = splitContinuousDataSet(testdata, tree.getdim(), tree.getValue())
if len(lefttest) > 0:
posttree(tree.getRight(), lefttest)
if len(righttest) > 0:
posttree(tree.getRight(), righttest)
cout1 = 0
cout2 = 0
classlist1 = [example[-1] for example in testdata]
Value = majorityCnt(classlist1)
for ex in testdata:
if (ex[-1] == Value):
cout1 += 1
# left
if (len(lefttest) > 0 and len(lefttest[0]) > 0):
classListleft = [example[-1] for example in lefttest]
leftValue = majorityCnt(classListleft)
for dt in lefttest:
if (dt[-1] == leftValue):
cout2 += 1
# right
if (len(righttest) > 0 and len(righttest[0]) > 0):
classListright = [example[-1] for example in righttest]
leftValue = majorityCnt(classListright)
for dt in righttest:
if (dt[-1] == righttest):
cout2 += 1
if (cout1 < cout2):
return tree.setdim(-1)
L. 进行5折交叉验证。首先使用了random.shuffle方法,将数据集打乱顺序。之后从0-30,30-60,60-90,90-120,120-150,分别作为测试集,用remove方法的到训练集。分别生成树,比较其中的正确率:
random.shuffle(dataset)
train1 =dataset[:121]
test1 =dataset[121:]
train2 =dataset[31:]
test2 =dataset[:31]
test3=[]
train3 =dataset[:]
test4=[]
train4=dataset[:]
test5=[]
train5=dataset[:]
for i in range(30,60):
test3.append(dataset[i])
train3.remove(dataset[i])
print(len(dataset))
for i in range(60,90):
test4.append(dataset[i])
train4.remove(dataset[i])
for i in range(90,120):
test5.append(dataset[i])
train5.remove(dataset[i])、
实验结果截图:
一共测试了四次次,其中第一行为正常构造决策树,第二行为使用后剪枝,第三行为使用预剪枝分别得到的正确的样本个数。总的测试集大小为30。倒数第二行为在测试集和训练集上测试的结果,发现训练集的结果高于测试集。(第一行为训练集本身,第二三行为测试集)
实验结果分析:
在原来树的准确率和后剪枝后和预剪枝的准确率上来看(第一行为正常构建的树,第二行为后剪枝后,第三行为预剪枝构造树):这棵树存在过拟合的情况。通过5折交叉验证可以使这棵树的准确率为基本为100%