基於WEKA的C45算法代碼實現及其詳解

1.概述

C45算法在weka已經有具體的實現,即weka中的J48.java。不過J48.java中的具體代碼牽扯到較多的類和其他東西,直接看源代碼比較容易混亂,且需要了解的東西較多,有比較多和C45算法本身沒有太大關係而是爲了方便代碼實現的類、變量和方法等。
本文是基於C45算法思想和對J48源代碼的詳細解讀,自己寫了一個C45算法的代碼(之後均稱爲MyC45)。該代碼只含有兩個類(99%的代碼只在一個類中實現),需要了解的結構相對簡單,算法的實現過程相對清晰,算法的效果和J48.java相差無幾,寫在這裏權作參考。
weka中的每個分類器的類文件都實現了buildClassifier和distributionForInstance兩個算法,前者是構建分類器,後者是根據構建的分類器對每個實例進行類標記預測。所以,MyC45算法中主要分爲建樹(buildClassifier)和預測(distributionForInstance)兩部分,其中建樹(buildClassifier)是主體部分,預測(distributionForInstance)只是簡單的利用前面構建好的樹對每個實例進行預測。
特別說明,本文所使用的實例集均已進行數據預處理,所以在MyC45中沒有J48中對缺失值的處理、判斷J48能否處理該實例集等等對實例集的處理。

2.類及其變量說明

MyC45包括兩個類,一個是MyC45,另一個是MyClassifierTree,它們的具體功能和變量說明如下:
1.MyC45:實現C45算法的類。
變量:
m_Root:C45決策樹的根節點。
2.MyClassifierTree:具體實現C45算法中的建樹和預測實例類標記兩大部分的功能。
變量:
int[] m_AttributeList: 當前節點的可選分裂屬性集合,以整數形式保存.-1表示當前節點不能選擇該屬性作爲分裂屬性。
int m_SplitAttribute:當前節點的分裂屬性,以整數的形式表示。
int m_NumAttributes:訓練實例集中屬性的個數(包括類屬性)。
MyCLassifierTree[] m_Sons:當前節點的兒子節點。
Instances m_Instances:當前節點對應的實例集。
int m_MinInstances:最小實例數,若當前節點對應的實例集的實例數小於該值,則當前節點只能作爲葉節點。

注:以上是關鍵變量說明,類中其他變量並不是很重要就沒有特別說明,在後面的代碼中出現時會有說明。

3.僞代碼

輸入:訓練實例集D,可選屬性列表m_AttributeList,最小實例數m_MinInstances。
1.創建根節點m_root,m_AttributeList初始化爲全0.
2.if D中的所有實例類標記都一樣,則將m_root標記爲葉節點並返回m_root。
3.if m_AttributeList爲空,則將m_root標記爲葉節點並返回m_root。
4.if m_Instances的實例數小於m_MinInstances,則將m_root標記爲葉節點並返回m_root。
5.根據getBestSplitAttribute方法,在m_AttributeList中找出增益率最大的屬性作爲當前節點的分裂屬性m_SplitAttribute。
6.根據m_SplitAttribute的屬性值將當前節點的實例集m_Instances劃分成多個子集,也就是當前節點的兒子節點 m_Sons。
7.對m_Sons中的每個節點遞歸地循環2-6的步驟以構建子樹。
8.決策樹構建好後,需要對其進行collapse處理和prune處理。前者是摺疊子樹過程,後者是後剪枝過程,具體說明在後面。
4.函數調用流程圖

以下是各個部分的主要函數調用流程圖,後面將對這些主要函數進行詳細說明。


圖1. buildClassifier部分的主要函數調用流程圖

在MyC45類中的buildClassifier函數中,先初始化可選屬性列表m_AttributeList和最小實例數m_MinInstances,然後用這兩個參數初始化m_Root,之後再用m_Root調用MyClassifierTree類中的buildeClassifier函數即可。

這裏寫圖片描述
圖2. distributionForInstance部分的主要函數調用流程圖

在MyC45類中的distributionForInstance函數中,直接用m_Root調用MyClassifierTree類中的distributionForInstance函數即可。

這裏寫圖片描述
圖3. 建樹(buildTree)的主要函數調用流程圖

建樹首先根據isAllTheSame和canSplit兩個函數和其他條件判斷當前節點是否爲葉節點;若可以分裂則調用getBestSplitAttribute函數求出最佳的分裂屬性(也就是增益率最大的屬性),getBestSplitAttribute函數中對每個可選屬性分別調用beforeSplit、afterSplit和computeSplitInfo函數以求出該可選屬性的增益率;隨後將當前節點對應的實例集根據最佳分裂屬性(即當前節點的分裂屬性)分裂出多個子集,這些子集即爲當前節點的子節點的實例集;然後對每個子集調用getNewTree函數以構建相應的子樹,getNewTree函數中即調用buildTree函數,從而遞歸地構建決策樹。

這裏寫圖片描述
圖4. 摺疊子樹(collapse)的主要函數調用流程圖

collapse是對非葉節點進行操作,對於非葉節點,調用getCurrentTraningErrors求出當前節點的實例集上誤分實例數,再調用getSubTraningErrors求出所有子樹上實例集上誤分實例數;如果後者大於前者,說明這些子樹並不能提高這顆樹的準確度,則把這些子樹刪除。否則在每個子樹上遞歸的collapse。

這裏寫圖片描述
圖5. 後剪枝(prune)的主要函數調用流程圖

prune是後剪枝操作,所以需要先遞歸找到葉節點的上一層的第一個子樹開始剪枝。首先,調用getLargetBranch求出當前節點的最大樹枝;隨後,調用getEstimatedErrorsForBranch求出當前結點實例集在最大樹枝上的誤分實例數,標記爲a;調用getEstimatedErrorsForDistribution求出當前節點的實例集在當前節點上的誤分實例數,標記爲b;調用getEstimatedErrors求出當前節點的所有子樹上的誤分實例數,標記爲c。接着,判斷是否應該把當前結點設置爲葉節點,即第一個if語句。若不成立,則判斷是否用最大樹枝代替當前結點,即第二個if語句。若是,則將最大樹枝上的變量信息覆蓋當前結點的變量信息,並調用restInstances根據更改後的當前節點對當前實例集進行調整樹的結構,並遞歸地對更改後的當前節點進行prune操作。

5.buildTree

isAllTheSame和canSplit函數比較簡單,split就是根據屬性值對實例集進行劃分,getNewTree只是簡單的初始化節點並調用buildTree形成遞歸,這些都比較簡單,略過。

1.buildTree代碼

	public void buildTree(Instances instances) throws Exception
	{
		initializePara(instances);//初始化一些變量

		if (instances.numInstances()  <= m_MinInstances|| isAllTheSame(instances)) 
		//小於m_MinInstances的實例集只能做葉節點,或當前實例集的類標記都一樣也做葉節點,
		{
			m_IsLeaf = true;                   // 是則該節點爲葉節點
			m_Instances = instances;// 葉節點對應的實例集爲instances
			return;
		}
		
		if (!canSplit(m_AttributeList)) // 若可選屬性集爲空,則實例集不能繼續分裂,所以當前節點是葉節點
		{
			m_IsLeaf = true;
			m_Instances = instances;
			return;
		} else
		{
			int[] sonAttributeList = new int[m_NumAttributes];  //子節點的可選屬性列表
			for (int i = 0; i < sonAttributeList.length; i++)
			{
				if (i == m_ClassIndex)
					sonAttributeList[i] = -1;
				else
					sonAttributeList[i] = m_AttributeList[i];
			}
			
			m_SplitAttribute = getBestSplitAttribute(instances, m_AttributeList); // 從當前可選的分裂屬性集合中獲取最佳的分裂屬性
			m_NameOfCurrentNode = instances.attribute(m_SplitAttribute).name(); // 獲取當前分裂屬性的名稱
			if (m_SplitAttribute != -1) // 當前實例集可以劃分,且求得最佳分裂屬性時
			{
				sonAttributeList[m_SplitAttribute] = -1; // 當前分裂屬性在所有子節點上是不可選的,所以這裏進行標記一下
				int numOfSubTree = m_NumAttsValues[m_SplitAttribute];// 該節點對應的子節點數量,等於當前分裂屬性的屬性值個數

				Instances[] localInstances;
				localInstances = split(instances, numOfSubTree, m_SplitAttribute);// 根據分裂屬性的屬性值個數將實例集進行劃分
				m_NameOfLineToSon = new String[numOfSubTree];//每個子節點對應的屬性值
				for (int i = 0; i < numOfSubTree; i++)
					m_NameOfLineToSon[i] = m_Instances.attribute(m_SplitAttribute).value(i);

				m_Sons = new MyCLassifierTree[numOfSubTree];
				for (int i = 0; i < m_Sons.length; i++)
				{// 接着爲每一個localInstances構建子樹
					m_Sons[i] = getNewTree(localInstances[i], sonAttributeList,m_MinInstances);
					localInstances[i] = null;
					if (m_Sons[i].m_IsLeaf)  //統計當前結點葉節點數
						m_NumLeaf ++;
				}
			} else// 當前實例集不可以劃分,說明該節點是葉節點
			{
				m_IsLeaf = true;
				m_Instances = instances;
				return;
			}
		}
	}

2.getBestSplitAttribute

	/**
	 * 從當前可選屬性列表中求出最佳分裂屬性,即增益率最大的屬性
	 * @param instances
	 * @param attributesList
	 * @return
	 */
	public int getBestSplitAttribute(Instances instances, int[] attributesList)
	{
		int bestSplitAttribute = 0; //標記最佳分裂屬性
		boolean canSplit = false; //判斷該實例集是否可以繼續劃分
		double[] gainRatio = new double[m_NumAttributes ]; //增益率,
		double[] infoGain =  new double[m_NumAttributes ]; //信息增益,
		double[] splitInfo =  new double[m_NumAttributes ]; //分裂信息,
		
		for (int i = 0; i < m_NumAttributes ; i++) //遍歷屬性,計算每個屬性增益率
		{
			if (i != m_ClassIndex && attributesList[i] != -1)//對可選的非類屬性進行計算增益率
			{
				infoGain[i] = beforeSplit(instances) - afterSplit(instances,i);
				splitInfo[i] = computeSplitInfo(instances,i);
				gainRatio[i] = infoGain[i] / splitInfo[i];
				canSplit = true; //當進入增益率計算時,說明該實例集可以進行劃分
			}
			else 
			{
				gainRatio[i] = 0;
				infoGain[i] = 0;
				splitInfo[i] = 0;
			}
		}
		
		if (canSplit)
		{  //若可以分裂,則找出gainRatio數組中最大且attributesList數組中不等於-1的下標,即爲最佳分裂屬性
			bestSplitAttribute = getMaxIndex(gainRatio,attributesList);
		}
		else {//若當前實例集無法繼續分裂,則返回-1作爲沒有找到最佳分裂屬性的標記
			bestSplitAttribute = -1;
		}
		
		return bestSplitAttribute;
	}

getBestSplitAttribute是通過計算增益率求出的,以下下是計算增益率的一些公式:
(1)GainRito(A) = Gain(A)/SplitInfo(A)
GainRito(A):屬性A的增益率。
Gain(A):屬性A 的信息增益。
SplitInfo(A):屬性A的分裂信息量。

(2)Gain(A) = Info(Insts) - Info(Insts,A)
Info(Insts):實例集Insts分裂前的信息量。
Info(Insts,A):實例集Insts根據屬性A分裂後的信息量。

(3)這裏寫圖片描述
C:實例集Insts中的類標記個數。
Pi:第i種類標記對應的實例數與實例總數的比值。

(4)這裏寫圖片描述
nA:屬性A的屬性值個數。
n:實例集的實例總數。
ni:屬性A的第i種屬性值對應的實例數。
Instsi:屬性A的第i種屬性值對應的實例集。
Info(Instsi):根據公式(3)計算屬性A的第i種屬性值對應的實例集的信息量。

(5)這裏寫圖片描述

3.beforeSplit

	/**
	 * 計算分裂前實例集的信息值
	 * @param instances
	 * @return
	 */
	public double beforeSplit(Instances instances)
	{
		double infoBeforeSplit = 0;
		
		int numClasses = instances.numClasses();
		int numInstances = instances.numInstances(); //實例總數
		double allWeight = 0;                                                 //實例集instances中的實例數(權重之和)
		double[] numInstancesInClass = new double[numClasses];//每個類標記對應的實例數(權重)
		
		for (int i = 0; i < numInstances; i++) //遍歷每個實例,統計出每個類標記對應的實例數(權重)
		{
			int classLable = (int)instances.instance(i).classValue();
			numInstancesInClass[classLable] += instances.instance(i).weight(); 
			allWeight += instances.instance(i).weight();
		}
		
		if (onlyOneNotZero(numInstancesInClass,allWeight))//1.如果只有一個類標記的實例數(權重)不爲0(其他類標記的實例數(權重)爲0),則信息值爲0
			return 0.0;
		if (eachEqualAve(numInstancesInClass))//2.如果所有類標記對應的實例數(權重)相等,則信息值最大,這裏設置爲1
			return 1.0;
		
		for (int i = 0; i < numClasses; i++) //3.根據公式(3)求出實例集的信息值
		{
			double pi = numInstancesInClass[i] / allWeight;
			if (pi != 0.0) //注意pi爲0時不要納入計算,因爲log0是一個無效值,這會導致整個infoBeforeSplit值無效(NaN)。反正pi等於0時 pi * log2(pi)即爲0,所以不納入計算即可
				infoBeforeSplit = infoBeforeSplit + pi * log2(pi) ;
		}
		return - infoBeforeSplit; //注意加個負號
	}

4.afterSplit

	/**
	 * 計算實例集根據屬性attribute進行分裂後的信息量
	 * @param instances
	 * @param attribute
	 * @return
	 */
	public double afterSplit(Instances instances, int attribute)
	{
		double infoAfterSplit = 0;
		int numAttributeValue = instances.attribute(attribute).numValues(); //屬性attribute的屬性值個數
		int numInstances = instances.numInstances(); // 實例總數
		double allWeight = 0; ////實例集instances中的實例數權重之和
		
		double[] weightInAttValue = new double[numAttributeValue];//每個屬性值對應的實例數(權重之和)
		Instances[] instsOfValue = new Instances[numAttributeValue];//每個屬性值對應的實例子集
		for (int i = 0; i < instsOfValue.length; i++)//初始化
			instsOfValue[i] = new Instances(instances, 0);
		
		for (int i = 0; i < numInstances; i++)//遍歷實例集,將實例集instances根據屬性值劃分實例集,統計每個實例集的實例數(權重)
		{
			int attValue = (int)instances.instance(i).value(attribute); //獲取第i個實例在屬性attribute中的屬性值
			weightInAttValue[attValue] += instances.instance(i).weight(); //計算每個屬性值對應的實例數(權重之和)
			allWeight +=  instances.instance(i).weight();  //計算實例集instances中的實例權重之和
			instsOfValue[attValue].add(instances.instance(i)); //將實例i放入對應的實例子集之中
		}
		
		for (int i = 0; i < numAttributeValue; i++)//根據公式(4)計算根據屬性i分裂後的實例集的信息值
		{
			double value = weightInAttValue[i]/allWeight * beforeSplit(instsOfValue[i]);
			infoAfterSplit = infoAfterSplit + value;
		}
		
		return infoAfterSplit;
	}
}

5.computeSplitInfo

	/**
	 * 計算分裂信息量
	 * @param instances
	 * @param attribute
	 * @return
	 */
	public double computeSplitInfo(Instances instances, int attribute)
	{
		double splitInfo = 0;
		double allWeight = 0; ////實例集instances中的實例數(權重之和)
		int numAttributeValue = instances.attribute(attribute).numValues(); //屬性attribute的屬性值個數
		
		double[] weightInEachValue = new double[numAttributeValue]; //每個屬性值對應的實例數(權重之和)
		
		for (int i = 0; i < instances.numInstances(); i++)
		{
			int  attValue = (int)instances.instance(i).value(attribute); //獲取第i個實例在屬性attribute中的屬性值
			weightInEachValue[attValue] += instances.instance(i).weight(); //計算每個屬性值對應的實例數(權重之和)
			allWeight += instances.instance(i).weight(); //計算實例集instances中的實例數(權重之和)
		}
		
		for (int i = 0; i < numAttributeValue; i++) //根據公式(5)計算分裂信息值
		{
			double pi = weightInEachValue[i] / allWeight;
			if (pi != 0)//注意pi爲0時不要納入計算,因爲log0是一個無效值,這會導致整個splitInfo值無效。
				splitInfo = splitInfo +pi* log2(pi);
		}
		
		return  - splitInfo; //注意加個負號
	}

6.collapse

1.collapse

	/**
	 * Collapses a tree to a node if training error doesn't increase.
	 * 如果當前節點存在很多子節點,但這些子節點並不能提高這顆分類樹的準確度,則把這些孩子節點刪除。否則在每個孩子上遞歸的collapse。
	 * 通過collapse方法可以在不減少精度的前提下減少決策樹的深度,進而提高效率。
	 */
	public final void collapse( )
	{
		double errorsOfTree;       // 當前節點上訓練實例集誤分的實例數(權重)
		double errorsOfSubtree; // 當前節點的所有子樹上訓練實例集誤分的實例數(權重)
		int i;

		if (!m_IsLeaf)//只有對非葉節點才進行摺疊子樹操作
		{	
			errorsOfTree = getCurrentTrainingErrors();  
			errorsOfSubtree = getSubTrainingErrors();
		
			if (errorsOfSubtree >= errorsOfTree - 1E-3)
				//所有子樹上誤分實例數(權重)大於當前節點誤分實例數(權重)時,說明這些孩子節點不好,將他們刪除。
				//刪除的方式是將當前節點 的子樹變量設置爲空並將該節點設置爲葉節點。1E-3是10的-3次方,即0.001 
			{
				m_Sons = null;
				m_IsLeaf = true;
			}
			else
				for (i = 0; i < m_Sons.length; i++) // 在每個孩子上遞歸地進行摺疊子樹操作
					m_Sons[i].collapse();
		}
	}
	

2.getCurrentTrainingErrors

	/**
	 * 計算當前結點的誤分實例數
	 * @return
	 */
	private double getCurrentTrainingErrors()
	{
		double wrongWeight = 0;
		
		int majorityClassLable = majorityClassLable(m_Instances); //獲取當前實例集中的多數類
		
		for (int i = 0; i < m_Instances.numInstances(); i++)  //遍歷當前實例集,求出總誤分實例數(權重)
		{
			int classValue = (int)m_Instances.instance(i).classValue();
			if (classValue != majorityClassLable)
			{
				m_Predictions[i] = -1;
				wrongWeight += m_Instances.instance(i).weight();
			}
		}
	
		return wrongWeight;
	}

3.getSubTrainingErrors

	/**
	 * 計算當前結點的所有子節點中誤分的實例數(權重)
	 * @return
	 */
	private double getSubTrainingErrors()
	{
		double errors = 0;
	
		if (m_IsLeaf)   //對葉節點,直接調用getCurrentTrainingErrors函數求出該葉節點上的誤分實例數(權重)
			return getCurrentTrainingErrors();
		else //對非葉節點,遞歸調用getSubTrainingErrors以求出所有子節點上的誤分實例數(權重)
		{
			for (int i = 0; i < m_Sons.length; i++)
				errors = errors + m_Sons[i].getSubTrainingErrors();
			return errors;
		}
	}

7.prune

1.prune

	/**
	 * 後剪枝操作
	 * @throws Exception
	 */
	public final void prune( ) throws Exception
	{
		double errorsLargestBranch; //當前節點實例集在最大樹枝上的誤分實例數
		double errorsLeaf;                     //假設當前節點是葉節點時,該節點對應的實例集在該節點上的誤分實例數
		double errorsTree;                     //計算當前節點的所有子樹上的誤分實例數
		int indexOfLargestBranch;     //最大樹枝的下標
		MyCLassifierTree largestBranch;  //臨時保存最大樹枝
		int i;
		
		if (!m_IsLeaf) //對非葉節點均進行剪枝
		{
			for (i = 0; i < m_Sons.length; i++)// 對當前節點的子節點遞歸地進行剪枝,由於是後剪枝,所以從樹的最底層開始往上
				m_Sons[i].prune();
			
			// 求出當前樹上的最大樹枝,即當前節點的所有子集中實例數最大的子集下標,
			indexOfLargestBranch = getLargetBranch();
			
			// 計算當前節點實例集在最大樹枝上的誤分實例數
			errorsLargestBranch = m_Sons[indexOfLargestBranch].getEstimatedErrorsForBranch(m_Instances);
			
			//計算當前節點對應的實例集在當前節點上的誤分實例數
			errorsLeaf = getEstimatedErrorsForDistribution(m_Instances);
			
			// 計算當前節點的所有子樹上的誤分實例數
			errorsTree = getEstimatedErrors();
			
			// 判斷將該節點設置爲葉節點是不是最好的選擇,
			if (Utils.smOrEq(errorsLeaf, errorsTree + 0.1) && Utils.smOrEq(errorsLeaf, errorsLargestBranch + 0.1))
			{
				m_Sons = null;
				m_IsLeaf = true;
				return;
			}

			// 判斷用最大樹枝代替當前節點是不是最好的選擇
			if (Utils.smOrEq(errorsLargestBranch, errorsTree + 0.1))
			{
				largestBranch = m_Sons[indexOfLargestBranch];  //獲取最大樹枝
				m_Sons = largestBranch.m_Sons;   
				m_AttributeList = largestBranch.m_AttributeList;   //將最大樹枝的可選分裂屬性列表覆蓋當前節點的可選分裂屬性列表
				m_AttributeList[m_SplitAttribute] = 0;                        //由於會用最大樹枝的分裂屬性代替了原先節點的分裂屬性,所以原先節點的分裂屬性處於可選狀態
				m_SplitAttribute = largestBranch.m_SplitAttribute;  //用最大樹枝的分裂屬性代替了原先節點的分裂屬性
				m_IsLeaf = largestBranch.m_IsLeaf;
				
				resetInstances(m_Instances);  //將當前實例集根據修改後的分裂屬性進行劃分
				prune(); //遞歸地對修改後的節點進行剪枝
			}
		}
	}
	

2.getEstimatedErrorsForDistribution

	/**
	 * 求出testInstances實例集在以m_Instances爲根據的分類器中的誤分實例數(權重)
	 * @param theDistribution
	 *            the distribution to use
	 * @return the estimated errors
	 */
	private double getEstimatedErrorsForDistribution(Instances testInstances)
	{
	
		if (Utils.eq(testInstances.numInstances(), 0)) //若testInstances實例數爲0,則誤分實例數只能爲0
			return 0;
		else
		{
			double inCorrectWeight = 0;
			double allWeight = 0.0;
			int majorityClassLable ;
			
			majorityClassLable = majorityClassLable(m_Instances);//求出當前實例集m_Instances中的多數類
			for (int i = 0; i < testInstances.numInstances(); i++)
			{
				allWeight += testInstances.instance(i).weight(); //統計測試實例集testInstances中的實例總數(權重)
				int classVlaue = (int)testInstances.instance(i).classValue();
				if (classVlaue != majorityClassLable)
					inCorrectWeight += testInstances.instance(i).weight(); //統計測試實例集testInstances中誤分實例的實例總數(權重)
			}
			
			return inCorrectWeight +  Stats.addErrs(allWeight,inCorrectWeight, 0.25f); 
		}
	}

3.getEstimatedErrorsForBranch

	/**
	 *  求出testInstances實例集在以m_Instances爲根據的分類器中的誤分實例數(權重)
	 * @param data
	 *            the data to work with
	 * @return the estimated errors
	 * @throws Exception
	 *             if something goes wrong
	 */
	private double getEstimatedErrorsForBranch(Instances testInstances) throws Exception
	{
		double errors = 0;
		int i;
	
		if (m_IsLeaf) //若當前節點是葉節點,則調用getEstimatedErrorsForDistribution求出當前節點上的誤分實例數
			return getEstimatedErrorsForDistribution(testInstances);
		else //若當前節點不是葉節點,則計算testInstances在其所有子節點上的誤分實例數之和
		{
			//將testInstances根據當前節點的分裂屬性的屬性值,劃分成不同的測試實例集
			int numSubset = testInstances.attribute(m_SplitAttribute).numValues();
			Instances[] localInstances = split(testInstances, numSubset, m_SplitAttribute);//測試實例子集
			
			for (i = 0; i < m_Sons.length; i++) //計算每個測試實例子集在對應子節點上的誤分實例數(權重)
				errors = errors + m_Sons[i].getEstimatedErrorsForBranch(localInstances[i]);
			return errors;
		}
	}

4.getEstimatedErrors

	/**
	 *計算當前結點的所有子樹上的誤分實例數(權重)
	 * @return the estimated errors
	 */
	private double getEstimatedErrors()
	{
	
		double errors = 0;
		int i;
	
		if (m_IsLeaf)  //若當前結點是葉節點,則直接計算誤分實例樹(權重)
			return getEstimatedErrorsForDistribution(m_Instances);
		else
		{
			for (i = 0; i < m_Sons.length; i++)  //若當前節點不是葉節點,則遞歸地計算其所有子樹上的誤分實數(權重)
				errors = errors + m_Sons[i].getEstimatedErrors();
			return errors;
		}
	}

5.resetInstances

    /**
	 * 將當前實例集根據修改後的分裂屬性進行劃分,並修改預測結果數組m_Prediction[]
	 * @param instances
	 * @throws Exception 
	 */
	private void resetInstances(Instances instances) throws Exception
	{
		
		m_Instances = instances;
		
		if (!m_IsLeaf) //若當前節點不是葉子節點,則遞歸地對其及其子樹進行重新劃分實例集
		{
			int numSubset = (int)instances.attribute(m_SplitAttribute).numValues();
			Instances[] localInstances = split(instances, numSubset, m_SplitAttribute);
			
			for (int i = 0; i < m_Sons.length; i++)//遞歸地對其子樹進行重新劃分實例集
			{
				m_Sons[i].m_Instances = localInstances[i];   
				m_Sons[i].resetInstances(localInstances[i]);
			}
		}
		else
		{//由於實例集發生了變化,所以需要根據新實例集和新的可選分裂屬性列表構建樹
			m_AttributeList[m_SplitAttribute] = -1;  //在當前節點上建樹,則當前分裂屬性在其子樹的構建過程中不可選
			m_IsLeaf = false;
			
			int numSubset = (int)instances.attribute(m_SplitAttribute).numValues();   //此時是在各個子節點上建樹
			Instances[] localInstances = split(instances, numSubset, m_SplitAttribute);
			m_Sons = new MyCLassifierTree[numSubset];
			for (int i = 0; i < localInstances.length; i++)
			{
				m_Sons[i] = getNewTree(localInstances[i], m_AttributeList, m_MinInstances);
			}
		}
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章