- 导入需要进行训练的数据集。这个数据集一共有5个样本,每个样本都有两个。他们的类别都写在标签矩阵之中。由于这次分类使用的分类函数是符号函数,
- 只能输出-1和1两个结果,因此,和sigmoid函数的分类结果不同。
- def loadSimDat():
- dataMat = matrix([[1, 2.1],
- [2.0, 1.1],
- [1.3, 1.0],
- [1.0, 1.0],
- [2.0, 1.0]])
- classLabels = [1.0, 1.0, -1.0, -1.0, 1.0]
- return dataMat, classLabels
-
- 根据输入的样本,选择样本的第dimen个特征作为分类的特征,选择threshVal作为该特征的阈值,并由threshIneq是否为Lt来确定这个样本是属于-1还是1
- 这个函数直接根据给定特征的阈值来对样本进行分类。
- def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):#just classify the data
- retArray = ones((shape(dataMatrix)[0],1))
- if threshIneq == 'lt':
- retArray[dataMatrix[:,dimen] <= threshVal] = -1.0
- else:
- retArray[dataMatrix[:,dimen] > threshVal] = -1.0
- return retArray //返回一个矩阵,里面是所有样本的分类结果
-
- 这个函数实现:对于一个给定的样本初始权值D,返回错误率最低的分类器极其相应的各个特点,以及这个最佳分类器分类的结果。这个函数只返回一个分类器
- def buildStump(dataArr, classLabels, D):
- dataMatrix = mat(dataArr); labelMat = mat(classLabels).T //样本集合和样本的标签
- m,n = shape(dataMatrix)
- numSteps = 10.0; bestStump = {}; bestClassEst = mat(zeros((m,1)))
- minError = inf //无穷大
- for i in range(n): //对所有 的特征进行遍历
- rangeMin = dataMatrix[:,i].min(); rangeMax = dataMatrix[:,i].max(); //选出某个特征中的最大值和最小值
- stepSize = (rangeMax - rangeMin)/numSteps //设置阈值变化的步长
- for j in range(-1, int(numSteps)+1): //设置阈值增加的次数;对阈值进行等间隔遍历
- for inequal in ['lt','gt']: //设置分界值;对分界值进行遍历
- threshVal = (rangeMin + float(j)*stepSize) //设置阈值
- predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal) //对于给定的矩阵、给定的特征、给定的阈值、给定的分界值,得出一个分类的结果
- errArr = mat(ones((m,1)))
- errArr[predictedVals == labelMat] = 0 //将预测分类的结果和真实的类别结果进行比较,输出分类错误的结果
- weightedError = D.T * errArr //计算误差率,根据定义,误差率是分类错误的样本的权值之和
- #print "split: dim %d, thresh %.2f, thresh inequal: %s, the weighted error is %.3f" %(i, threshVal, inequal, weightedError)
- if weightedError < minError:
- minError = weightedError
- bestClassEst = predictedVals.copy()
- bestStump['dim'] = i
- bestStump['thresh'] = threshVal
- bestStump['ineq'] = inequal
- return bestStump, minError, bestClassEst //对所有的特征、阈值、分界值进行遍历之后,就能够输出误差率最小的那个分类的方法。即相应的特征、阈值和分界值,以及这个最佳分类器的分类结果
-
-
- 训练一个完整的adaboost分类器
- def adaBoostTrainDS(dataArr, classLabels, numIt = 40): //输入样本数据,以及样本真实的分类标签
- weakClassArr = []
- m = shape(dataArr)[0] //样本的个数
- D = mat(ones((m,1))/m) //初始化样本的权值,给每个样本都赋予相同的权值
- aggClassEst = mat(zeros((m,1))) //
- for i in range(numIt): //设置循环的次数,也即预先设定创建40个分类器
- bestStump, error, classEst = buildStump(dataArr, classLabels, D) //对于给定的样本和初始样本权值,返回最佳的分类器特征,分类结果和错误率
- # print "D:", D.T
- alpha = float(0.5 * log((1.0 - error)/max(error, 1e-16))) //由这个分类器的错误率计算这个分类器的权值
- bestStump['alpha'] = alpha
- weakClassArr.append(bestStump) //保存这个分类器的dim/thresh/ineq/alpha等四个特点
- #print "classEst:", classEst.T
- expon = multiply(-1 * alpha * mat(classLabels).T, classEst)
- D = multiply(D, exp(expon))
- D = D/D.sum() //上面三个式子是根据这个分类器的分类结果和分类器权重,来更新用于下一轮迭代的样本权值D。上面三个式子的理论依据如下
- aggClassEst += alpha * classEst #累加变成强分类器 ,将分类器的分类结果同分类器的权值线性叠加,形成强分类器。
- aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T, ones((m,1))) //选出强分类器分类错误的样本
- errorRate = aggErrors.sum()/m //计算强分类器的错分率
- print "total error: ", errorRate, "\n"
- if errorRate == 0.0: break
- return weakClassArr, aggClassEst //返回强分类器每个分类器的特征,以及强分类器的输出结果
样本权值更新函数如下:
强分类器累加函数:
由符号函数确定最终的分类器
- 调用完整的adaboost分类器对测试样本进行测试
- def adaClassify(datToClass, classifierArr): //输入测试样本和上面训练出来的完整的adaboost分类器
- dataMatrix = mat(datToClass)
- m = shape(dataMatrix)[0] //测试样本的个数
- aggClassEst = mat(zeros((m,1))) //初始化强分类器的累加值为0
- for i in range(len(classifierArr)): //按顺序调用adaboost分类器中的每一个分类器,顺序很重要,因为训练器的顺序是由每个样本的权值决定的
- classEst = stumpClassify(dataMatrix, classifierArr[i]['dim'], classifierArr[i]['thresh'], classifierArr[i]['ineq']) //使用这个分类器给样本进行分类,并输出分类结果
- aggClassEst += classifierArr[i]['alpha']*classEst //将每个分类器的分类结果和分类器的权值线性相加,得到逐渐强分类器的分类结果
- print aggClassEst
- return sign(aggClassEst) //使用符号函数,输出真正最终的分类结果
adaboost分类器有个很明显的特点,它可以实现在训练样本中无错误地输出;但是在测试样本中,却只能将错误率稳定在一个值附近。