【机器学习实战系列】读书笔记之AdaBoost算法公式推导和例子讲解(一)_adaboost公式-程序员宅基地

技术标签: adaboost  机器学习实战  

最近在看集成算法AdaBoost,推荐先看李航的统计学习方法第8章,然后再看机器学习实战第7章,李航的书上的公式推导讲的很详细了,但是很多地方对于初学者来说,还是需要时间去理解和消化的。本文将从以下几个方面来介绍AdaBoost算法。

一、AdaBoost算法公式推导

二、AdaBoost相关例子讲解

三、AdaBoost的toy algorithm的python实现

四、启发和思考“为什么”,“如何做”

一、AdaBoost算法公式推导

AdaBoost,是英文"Adaptive Boosting"(自适应增强)的缩写,由Yoav Freund和Robert Schapire在1995年提出。它的自适应在于:前一个基本分类器分错的样本会得到加强,加权后的全体样本再次被用来训练下一个基本分类器。同时,在每一轮中加入一个新的弱分类器,直到达到某个预定的足够小的错误率或达到预先指定的最大迭代次数。

    具体说来,整个Adaboost 迭代算法就3步:

  1. 初始化训练数据的权值分布。如果有N个样本,则每一个训练样本最开始时都被赋予相同的权值:1/N。
  2. 训练弱分类器。具体训练过程中,如果某个样本点已经被准确地分类,那么在构造下一个训练集中,它的权值就被降低;相反,如果某个样本点没有被准确地分类,那么它的权值就得到提高。然后,权值更新过的样本集被用于训练下一个分类器,整个训练过程如此迭代地进行下去。
  3. 将各个训练得到的弱分类器组合成强分类器。各个弱分类器的训练过程结束后,加大分类误差率小的弱分类器的权重,使其在最终的分类函数中起着较大的决定作用,而降低分类误差率大的弱分类器的权重,使其在最终的分类函数中起着较小的决定作用。换言之,误差率低的弱分类器在最终分类器中占的权重较大,否则较小。

算法流程:

Adaboost的算法流程如下:

  • 步骤1. 首先,初始化训练数据的权值分布。每一个训练样本最开始时都被赋予相同的权值:1/N。

  • 步骤2. 进行多轮迭代,用m = 1,2, ..., M表示迭代的第多少轮

a. 使用具有权值分布Dm的训练数据集学习,得到基本分类器(选取让误差率最低的阈值来设计基本分类器):

b . 计算Gm(x)在训练数据集上的分类误差率
由上述式子可知,Gm(x)在训练数据集上的 误差率em就是被Gm(x)误分类样本的权值之和
c. 计算Gm(x)的系数,am表示Gm(x)在最终分类器中的重要程度(目的:得到基本分类器在最终分类器中所占的权重):
由上述式子可知,em <= 1/2时,am >= 0,且am随着em的减小而增大,意味着分类误差率越小的基本分类器在最终分类器中的作用越大。
d . 更新训练数据集的权值分布(目的:得到样本的新的权值分布),用于下一轮迭代

使得被基本分类器Gm(x)误分类样本的权值增大,而被正确分类样本的权值减小。就这样,通过这样的方式,AdaBoost方法能“重点关注”或“聚焦于”那些较难分的样本上。

    其中,Zm是规范化因子,使得Dm+1成为一个概率分布:

  • 步骤3. 组合各个弱分类器

从而得到最终分类器,如下:


二、AdaBoost相关例子讲解

下面,给定下列训练样本,请用AdaBoost算法学习一个强分类器。

    

    求解过程:初始化训练数据的权值分布,令每个权值W1i = 1/N = 0.1,其中,N = 10,i = 1,2, ..., 10,然后分别对于m = 1,2,3, ...等值进行迭代。

    拿到这10个数据的训练样本后,根据 X 和 Y 的对应关系,要把这10个数据分为两类,一类是“1”,一类是“-1”,根据数据的特点发现:“0 1 2”这3个数据对应的类是“1”,“3 4 5”这3个数据对应的类是“-1”,“6 7 8”这3个数据对应的类是“1”,9是比较孤独的,对应类“-1”。抛开孤独的9不讲,“0 1 2”、“3 4 5”、“6 7 8”这是3类不同的数据,分别对应的类是1、-1、1,直观上推测可知,可以找到对应的数据分界点,比如2.5、5.5、8.5 将那几类数据分成两类。当然,这只是主观臆测,下面实际计算下这个具体过程。

迭代过程1

对于m=1,在权值分布为D1(10个数据,每个数据的权值皆初始化为0.1)的训练数据上,经过计算可得:

    1. 阈值v取2.5时误差率为0.3(x < 2.5时取1,x > 2.5时取-1,则6 7 8分错,误差率为0.3),
    2. 阈值v取5.5时误差率最低为0.4(x < 5.5时取1,x > 5.5时取-1,则3 4 5 6 7 8皆分错,误差率0.6大于0.5,不可取。故令x > 5.5时取1,x < 5.5时取-1,则0 1 2 9分错,误差率为0.4),
    3. 阈值v取8.5时误差率为0.3(x < 8.5时取1,x > 8.5时取-1,则3 4 5分错,误差率为0.3)。

可以看到,无论阈值v取2.5,还是8.5,总得分错3个样本,故可任取其中任意一个如2.5,弄成第一个基本分类器为:

上面说阈值v取2.5时则6 7 8分错,所以误差率为0.3,更加详细的解释是:因为样本集中

    1. 0 1 2对应的类(Y)是1,因它们本身都小于2.5,所以被G1(x)分在了相应的类“1”中,分对了。
    2. 3 4 5本身对应的类(Y)是-1,因它们本身都大于2.5,所以被G1(x)分在了相应的类“-1”中,分对了。
    3. 但6 7 8本身对应类(Y)是1,却因它们本身大于2.5而被G1(x)分在了类"-1"中,所以这3个样本被分错了。
    4. 9本身对应的类(Y)是-1,因它本身大于2.5,所以被G1(x)分在了相应的类“-1”中,分对了。

从而得到G1(x)在训练数据集上的误差率(被G1(x)误分类样本“6 7 8”的权值之和e1=P(G1(xi)≠yi) = 3*0.1 = 0.3

然后根据误差率e1计算G1的系数:

这个a1代表G1(x)在最终的分类函数中所占的权重,为0.4236。
接着 更新训练数据的权值分布,用于下一轮迭代:

值得一提的是,由权值更新的公式可知,每个样本的新权值是变大还是变小,取决于它是被分错还是被分正确。

即如果某个样本被分错了,则yi * Gm(xi)为负,负负得正,结果使得整个式子变大(样本权值变大),否则变小。

第一轮迭代后,最后得到各个数据的权值分布D2 = (0.0715, 0.0715, 0.0715, 0.0715, 0.0715,  0.0715, 0.1666, 0.1666, 0.1666, 0.0715)。由此可以看出,因为样本中是数据“6 7 8”被G1(x)分错了,所以它们的权值由之前的0.1增大到0.1666,反之,其它数据皆被分正确,所以它们的权值皆由之前的0.1减小到0.0715。

分类函数f1(x)= a1*G1(x) = 0.4236G1(x)。

此时,得到的第一个基本分类器sign(f1(x))在训练数据集上有3个误分类点(即6 7 8)。

    从上述第一轮的整个迭代过程可以看出:被误分类样本的权值之和影响误差率,误差率影响基本分类器在最终分类器中所占的权重

  迭代过程2

对于m=2,在权值分布为D2 = (0.0715, 0.0715, 0.0715, 0.0715, 0.0715,  0.0715, 0.1666, 0.1666, 0.1666, 0.0715)的训练数据上,经过计算可得:

    1. 阈值v取2.5时误差率为0.1666*3(x < 2.5时取1,x > 2.5时取-1,则6 7 8分错,误差率为0.1666*3),
    2. 阈值v取5.5时误差率最低为0.0715*4(x > 5.5时取1,x < 5.5时取-1,则0 1 2 9分错,误差率为0.0715*3 + 0.0715),
    3. 阈值v取8.5时误差率为0.0715*3(x < 8.5时取1,x > 8.5时取-1,则3 4 5分错,误差率为0.0715*3)。

所以,阈值v取8.5时误差率最低,故第二个基本分类器为:

面对的还是下述样本:

很明显,G2(x)把样本“3 4 5”分错了,根据D2可知它们的权值为0.0715, 0.0715,  0.0715,所以G2(x)在训练数据集上的误差率e2=P(G2(xi)≠yi) = 0.0715 * 3 = 0.2143。

计算G2的系数:

更新训练数据的权值分布:
D3  = (0.0455, 0.0455, 0.0455,  0.1667, 0.1667,  0.01667 , 0.1060, 0.1060, 0.1060, 0.0455)。被分错的样本“ 3 4 5 ”的权值变大,其它被分对的样本的权值变小。
f2(x)=0.4236G1(x) + 0.6496G2(x)

此时,得到的第二个基本分类器sign(f2(x))在训练数据集上有3个误分类点(即3 4 5)。

  迭代过程3

对于m=3,在权值分布为D3 = (0.0455, 0.0455, 0.0455, 0.1667, 0.1667,  0.01667, 0.1060, 0.1060, 0.1060, 0.0455)的训练数据上,经过计算可得:

    1. 阈值v取2.5时误差率为0.1060*3(x < 2.5时取1,x > 2.5时取-1,则6 7 8分错,误差率为0.1060*3),
    2. 阈值v取5.5时误差率最低为0.0455*4(x > 5.5时取1,x < 5.5时取-1,则0 1 2 9分错,误差率为0.0455*3 + 0.0715),
    3. 阈值v取8.5时误差率为0.1667*3(x < 8.5时取1,x > 8.5时取-1,则3 4 5分错,误差率为0.1667*3)。

所以阈值v取5.5时误差率最低,故第三个基本分类器为:

依然还是原样本:

此时,被误分类的样本是:0 1 2 9,这4个样本所对应的权值皆为0.0455,

所以G3(x)在训练数据集上的误差率e3 = P(G3(xi)≠yi) = 0.0455*4 = 0.1820。

计算G3的系数:

更新训练数据的权值分布:

D4 = (0.125, 0.125, 0.125, 0.102, 0.102,  0.102, 0.065, 0.065, 0.065, 0.125)。被分错的样本“0 1 2 9”的权值变大,其它被分对的样本的权值变小。

f3(x)=0.4236G1(x) + 0.6496G2(x)+0.7514G3(x)

此时,得到的第三个基本分类器sign(f3(x))在训练数据集上有0个误分类点。至此,整个训练过程结束。

    现在,咱们来总结下3轮迭代下来,各个样本权值和误差率的变化,如下所示(其中,样本权值D中加了下划线的表示在上一轮中被分错的样本的新权值):

  1. 训练之前,各个样本的权值被初始化为D1 = (0.1, 0.1,0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1);
  2. 第一轮迭代中,样本“6 7 8”被分错,对应的误差率为e1=P(G1(xi)≠yi) = 3*0.1 = 0.3,此第一个基本分类器在最终的分类器中所占的权重为a1 = 0.4236。第一轮迭代过后,样本新的权值为D2 = (0.0715, 0.0715, 0.0715, 0.0715, 0.0715,  0.0715, 0.1666, 0.1666, 0.1666, 0.0715);
  3. 第二轮迭代中,样本“3 4 5”被分错,对应的误差率为e2=P(G2(xi)≠yi) = 0.0715 * 3 = 0.2143,此第二个基本分类器在最终的分类器中所占的权重为a2 = 0.6496。第二轮迭代过后,样本新的权值为D3 = (0.0455, 0.0455, 0.0455, 0.1667, 0.1667,  0.01667, 0.1060, 0.1060, 0.1060, 0.0455);
  4. 第三轮迭代中,样本“0 1 2 9”被分错,对应的误差率为e3 = P(G3(xi)≠yi) = 0.0455*4 = 0.1820,此第三个基本分类器在最终的分类器中所占的权重为a3 = 0.7514。第三轮迭代过后,样本新的权值为D4 = (0.125, 0.125, 0.125, 0.102, 0.102,  0.102, 0.065, 0.065, 0.065, 0.125)。

    从上述过程中可以发现,如果某些个样本被分错,它们在下一轮迭代中的权值将被增大,同时,其它被分对的样本在下一轮迭代中的权值将被减小。就这样,分错样本权值增大,分对样本权值变小,而在下一轮迭代中,总是选取让误差率最低的阈值来设计基本分类器,所以误差率e(所有被Gm(x)误分类样本的权值之和)不断降低。

    综上,将上面计算得到的a1、a2、a3各值代入G(x)中,G(x) = sign[f3(x)] = sign[ a1 * G1(x) + a2 * G2(x) + a3 * G3(x) ],得到最终的分类器为:

G(x) = sign[f3(x)] = sign[ 0.4236G1(x) + 0.6496G2(x)+0.7514G3(x) ]。


三、AdaBoost的toy algorithm的python实现

"""
  6     AdaBoost提升算法:(自适应boosting)
  7         优点:泛化错误率低,易编码,可以应用在大部分分类器上,无参数调整
  8         缺点:对离群点敏感
  9 
 10     bagging:自举汇聚法(bootstrap aggregating)
 11         基于数据随机重抽样的分类器构建方法
 12         原始数据集中重新选择S次得到S个新数据集,将磨沟算法分别作用于这个数据集,
 13         最后进行投票,选择投票最多的类别作为分类类别
 14 
 15     boosting:类似于bagging,多个分类器类型都是相同的
 16 
 17         boosting是关注那些已有分类器错分的数据来获得新的分类器,
 18         bagging则是根据已训练的分类器的性能来训练的。
 19 
 20         boosting分类器权重不相等,权重对应与上一轮迭代成功度
 21         bagging分类器权重相等
 22 """
 23 from numpy import*
 24 
 25 
 26 class Adaboosting(object):
 27 
 28     def loadSimpData(self):
 29         datMat = matrix(
 30             [[1., 2.1],
 31              [2., 1.1],
 32              [1.3, 1.],
 33              [1., 1.],
 34              [2., 1.]])
 35         classLabels = [1.0, 1.0, -1.0, -1.0, 1.0]
 36         return datMat, classLabels
 37 
 38     def stumpClassify(self, datMat, dimen, threshVal, threshIneq):
 39         """
 40         通过阈值比较进行分类
 41             dataMat:数据矩阵
 42             dimen:表示列下标
 43             threshVal:阈值
 44             threshIneq:不等号  lt, gt
 45         只是简单的将数据分为两类-1,1,初始化了一个全1的矩阵,我们判断一下阈值第i列小于/大于阈值的就为-1,(因为我们并不清楚这个划分标准,所以要大于小于都试一次)
 46 
 47         每一个维度的所有数据跟阈值比较,就相当于找到一个点划分所有数据。
 48 
 49         """
 50         # print "-----data-----"
 51         # print datMat
 52         retArr = ones((shape(datMat)[0], 1))  # m(数据量)行,1列,列向量
 53         if threshIneq == 'lt':
 54             retArr[datMat[:, dimen] <= threshVal] = -1.0  # 小于阈值的列都为-1
 55         else:
 56             retArr[datMat[:, dimen] > threshVal] = -1.0  # 大于阈值的列都为-1
 57         # print "---------retArr------------"
 58         # print retArr
 59         return retArr
 60 
 61     def buildStump(self, dataArr, classLables, D):
 62         """
 63         单层决策树生成函数
 64         """
 65         dataMatrix = mat(dataArr)
 66         lableMat = mat(classLables).T
 67         m, n = shape(dataMatrix)
 68         numSteps = 10.0  # 步数,影响的是迭代次数,步长
 69         bestStump = {}  # 存储分类器的信息
 70         bestClassEst = mat(zeros((m, 1)))  # 最好的分类器
 71         minError = inf  # 迭代寻找最小错误率
 72         for i in range(n):
 73             # 求出每一列数据的最大最小值计算步长
 74             rangeMin = dataMatrix[:, i].min()
 75             rangeMax = dataMatrix[:, i].max()
 76             stepSize = (rangeMax - rangeMin) / numSteps
 77             # j唯一的作用用步数去生成阈值,从最小值大最大值都与数据比较一边了一遍
 78             for j in range(-1, int(numSteps) + 1):
 79                 threshVal = rangeMin + float(j) * stepSize  # 阈值
 80                 for inequal in ['lt', 'gt']:
 81                     predictedVals = self.stumpClassify(
 82                         dataMatrix, i, threshVal, inequal)
 83                     errArr = mat(ones((m, 1)))
 84                     errArr[predictedVals == lableMat] = 0  # 为1的 表示i分错的
 85                     weightedError = D.T * errArr  # 分错的个数*权重(开始权重=1/M行)
 86                     # print "split: dim %d, thresh %.2f, thresh ineqal:\
 87 #%s,the weighted error is %.3f" % (i, threshVal, inequal, weightedError)
 88                     if weightedError < minError:  # 寻找最小的加权错误率然后保存当前的信息
 89                         minError = weightedError
 90                         bestClassEst = predictedVals.copy()  # 分类结果
 91                         bestStump['dim'] = i
 92                         bestStump['thresh'] = threshVal
 93                         bestStump['ineq'] = inequal
 94         # print bestStump
 95         # print minError
 96         # print bestClassEst  # 类别估计
 97         return bestStump, minError, bestClassEst
 98 
 99     def adaBoostingDs(self, dataArr, classLables, numIt=40):
100         """
101         基于单层决策树的AdaBoosting训练过程:
102         """
103         weakClassArr = []  # 最佳决策树数组
104         m = shape(dataArr)[0]
105         D = mat(ones((m, 1)) / m)
106         aggClassEst = mat(zeros((m, 1)))
107         for i in range(numIt):
108             bestStump, minError, bestClassEst = self.buildStump(
109                 dataArr, classLables, D)
110             print "bestStump:", bestStump
111             print "D:", D.T
112             alpha = float(
113                 0.5 * log((1.0 - minError) / max(minError, 1e-16)))
114             bestStump['alpha'] = alpha
115             weakClassArr.append(bestStump)
116             print "alpha:", alpha
117             print "classEst:", bestClassEst.T  # 类别估计
118 
119             expon = multiply(-1 * alpha * mat(classLables).T, bestClassEst)
120             D = multiply(D, exp(expon))
121             D = D / D.sum()
122 
123             aggClassEst += alpha * bestClassEst
124             print "aggClassEst ;", aggClassEst.T
125             # 累加错误率
126             aggErrors = multiply(sign(aggClassEst) !=
127                                  mat(classLables).T, ones((m, 1)))
128             # 错误率平均值
129             errorsRate = aggErrors.sum() / m
130             print "total error:", errorsRate, "\n"
131             if errorsRate == 0.0:
132                 break
133         print "weakClassArr:", weakClassArr
134         return weakClassArr
135 
136     def adClassify(self, datToClass, classifierArr):
137         """
138         预测分类:
139         datToClass:待分类数据
140         classifierArr: 训练好的分类器数组
141         """
142         dataMatrix = mat(datToClass)
143         m = shape(dataMatrix)[0]
144         aggClassEst = mat(zeros((m, 1)))
145         print
146         for i in range(len(classifierArr)):  # 有多少个分类器迭代多少次
147             # 调用第一个分类器进行分类
148             classEst = self.stumpClassify(dataMatrix, classifierArr[i]['dim'],
149                                           classifierArr[i]['thresh'],
150                                           classifierArr[i]['ineq']
151                                           )
152             # alpha 表示每个分类器的权重,
153             print classEst
154             aggClassEst += classifierArr[i]['alpha'] * classEst
155             print aggClassEst
156         return sign(aggClassEst)
157 
158 
159 if __name__ == "__main__":
160     adaboosting = Adaboosting()
161     D = mat(ones((5, 1)) / 5)
162     dataMat, lableMat = adaboosting.loadSimpData()
163     # 训练分类器
164     classifierArr = adaboosting.adaBoostingDs(dataMat, lableMat, 40)
165     # 预测数据
166     result = adaboosting.adClassify([0, 0], classifierArr)
167     print result

运行结果:可以看到迭代三次加权错误率为0


最后有一个对数据[0,0]的预测:weakClassArr表示保存的三个分类器的信息,我们用这个分类器对数据进行预测

三个小数对应的是三个分类器前N个分类加权分类结果累加。对应的-1,-1,-1表示三个分类器对这个数据分类是-1,最后一个表示增强分类器对这个数据的加权求和分类结果为-1


四、启发和思考“为什么”,“如何做”

问题一:为什么给错分的数据的权重越高,也就是对错分数据越关注,在下一次分类,误差率就会降低?这个过程是怎么体现的?

解答:如果错分数据的权重变高了,那么在下一次对数据分类时,分类器假如对上一次的错分数据再一次分错的话,根据公式,分类误差率=错分数据权重之和,我们就会发现这个时候的分类误差率变高了,而此时分类器的这个‘阈值’肯定不是我们想要的,其他的阈值(分类误差率最小)才是我们想要的,这样就会使得分类器更加关注前一次的错分数据,使这部分数据在本次分类后不会被错分,从而也达到了我们的目的,这就是算法逻辑的整个过程。

问题二:阈值如何确定?

解答:考虑到数值型的特征,我们可以通过计算最小值和最大值来了解需要多大的步长,比如说上面代码里面的numsteps = 10.0,stepsize = (rangemax-rangemin)/numsteps,threshVal = (rangemin+float(j)*stepsize)


Reference:

统计学习方法

机器学习实战

https://blog.csdn.net/v_july_v/article/details/40718799

https://www.cnblogs.com/NextNight/p/6227526.html


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_38150441/article/details/80476242

智能推荐

如何在P4中管理Unreal Engine 代码-程序员宅基地

文章浏览阅读2.1k次。准备引擎开发环境下载代码Unreal Engine代码库在Github上,要下载Github上的源代码,需要具有Github账号和虚幻引擎的订阅账号,并且将二者相关联。步骤如下。登陆www.github.com使用虚幻引擎订阅账号登陆unrealengine.com点击CONNECTIONS菜单-> ACCOUNTS->GITHUB一路按照提示操作,最终会收到一个Github的通知邮件,邀请参加EpicGames Organization。点击链接,就可以看到源

SpringBoot基于数据库的定时任务实现_spring定时任务本地日志只有一条插入语句,但是数据库有多条-程序员宅基地

文章浏览阅读410次。在我们平时开发的项目中,定时任务基本属于必不可少的功能,那大家都是怎么做的呢?但我知道的大多都是静态定时任务实现。基于注解来创建定时任务非常简单,只需几行代码便可完成。实现如下:[Java]纯文本查看复制代码? 01 02 03 04 05 06 07 08 09 10 11 ..._spring定时任务本地日志只有一条插入语句,但是数据库有多条

常见的Java错误以及规避方法-程序员宅基地

文章浏览阅读1.7k次,点赞3次,收藏4次。1. “Cannot Find Symbol”这是一个非常常见的问题,因为Java中的所有标识符都需要在使用之前进行声明。出现这个错误是因为,在编译代码时,编译器不明白该标识符的含义。![在这里插入图片描述](https://img-blog.csdnimg.cn/20201107220714255.png#pic_center2. “Public Class XXX Should Be in File”当XXX类和Java程序文件名不匹配时,就会产生“public class XXX should

LOJ2540 「PKUWC2018」随机算法(状态压缩)_pkuwc2018 状态压缩-程序员宅基地

文章浏览阅读138次。传送门:https://loj.ac/problem/2540solutionsolutionsolution:直接状压dpdpdp做fs,if_{s,i}fs,i​表示独立集的状态为sss,考虑到排列的第iii位枚举下一位转移考虑下一位可以填什么考虑现在可以加入独立集的点,之前肯定没考虑过,直接转移到fs∪x,i+1f_{s∪{x},i+1}fs∪x,i+1​不可以加入独立集的点..._pkuwc2018 状态压缩

快速搭建一个网关服务,动态路由、鉴权的流程,看完秒会(含流程图)-程序员宅基地

文章浏览阅读150次。点击上方蓝色字体,选择“标星公众号”优质文章,第一时间送达前言本文记录一下我是如何使用Gateway搭建网关服务及实现动态路由的,帮助大家学习如何快速搭建一个网关服务,了解路由相关配置,鉴权的流程及业务处理,有兴趣的一定看到最后,非常适合没接触过网关服务的同学当作入门教程。搭建服务框架SpringBoot 2.1<parent><groupId&...

看懂这篇文章就够了!什么是Redis缓存雪崩、缓存穿透和缓存击穿?五分钟统统搞定-程序员宅基地

文章浏览阅读462次。前言今天的分享主要是讲下这个 redis,什么是缓存雪崩、穿透和击穿。这三个技术问题是我们平时开发工作中和面试过程中,必须要会的知识点,因为目前的互联网系统没有几个不需要用到缓存的,只要用到缓存的话,就需要掌握这三个技术问题。基本上无论哪个老哥去大厂面试,都会被问题这几个问题,所以作为一个互联网开发程序员来说,这个几个技术问题大家是需要搞懂的。而解决这几个问题的方案,通常有布隆过滤器,还有分布式锁。布隆过滤器是1970年的一项技术,距今也有50年了,之所以能够应用至今,说明这项技术还是挺优秀的,

随便推点

生信分析--入门实践一条龙的CWL中文教程_生信分析入门教程csdn-程序员宅基地

文章浏览阅读2.3k次。生信分析--入门实践一条龙的CWL中文教程。关于下载安装所有中文教程,开始学习吧_生信分析入门教程csdn

JavaScript的学习4——面向对象编程_编写一个函数来创建xiaoming:-程序员宅基地

文章浏览阅读177次。前言JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。Object.create()方法可以传入一个原型对象,并创建一个基于该原型的新对象,但是新对象什么属性都没有,因此,我们可以编写一个函数来创建xiaoming:// 原型对象:var Student = { name: 'Robot', height: 1.2, run: function () { console.log(this.name + ' is r_编写一个函数来创建xiaoming:

前端:webpack-程序员宅基地

文章浏览阅读39次。前端相关基础知识webpackwebpack1.1 什么是webpack1.2 webpack安装安装webpackvue脚手架webpack1.1 什么是webpackwebpack 是一个现代 JavaScript 应用程序的模块打包器(module bundler),分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Sass,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。1.2 webpack安装注意:请先安装node环境we

Kryo序列化实现源码分析_python kryo 实现-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏2次。在使用Kryo序列化之前需要将被序列化的类通过register()方法注册到其中去。在register的过程中,实则是要根据要序列化的类生成对应的Registration,Registration中记录了类的唯一id与对应的序列化类,在Kryo中,默认的序列化对象是FieldSerializer,没有特别指明的,都将以FieldSerializer来进行序列化。public Regist..._python kryo 实现

最全面的AndroidStudio配置指南总结-包括护眼模式-程序员宅基地

文章浏览阅读3.7k次。使用AndroidStudio开发APP已有半年多的时间了,从刚开始的不习惯到慢慢适应再到逐渐喜欢上AndroidStudio,中间的过程颇有一番曲折,现在把自己对AndroidStudio的配置心得总结下来,分享给大家,希望给后来人带来方便。强迫症童鞋的护眼模式设置方法传统模式的编辑域护眼模式的编辑域设置保护视力颜色 #C7EDCC(护眼绿)

【批处理DOS-CMD命令-汇总和小结】-跳转、循环、条件命令(goto、errorlevel、if、for[读取、切分、提取字符串]、)cmd命令错误汇总,cmd错误_cmd errorlevel-程序员宅基地

文章浏览阅读4k次,点赞5次,收藏28次。此文主要研究对代码分支化执行和重复利用的实现。分支化执行指根据中途的实际执行结果决定下一步执行的代码,跳转的代码行号;分支化执行大概分为跳转执行、条件判断执行;因此,分支化执行基本是只执行部分代码,部分代码不执行。代码重复利用的实现,一方面依赖程序调用(详见本人写的CMD命令实现程序调用一文),另一方面基于循环命令。打印goto命令的帮助信息。我们可以看到该命令的参数只有一个label。具体应用方法——在goto命令的下方放一行,开头是英文冒号后边紧跟“分支标识符”,然后再goto所在行后面加上“分支标识符_cmd errorlevel