Learning Active Learning from Data 主动学习笔记
最近这段时间都在看关于主动学习的文章,现在分享自己看的这篇文章,希望与大家共勉!
这是2017在NIPS上发表的一篇文章:Learning Active Learning from Data
文章目录
- Abstract
- Towards data-driven active learning
- Active learning (AL)
- 主动学习框架
- 不确定性采样(US)
- Motivation
- Monte-Carlo LAL
- Independent LAL
- Iterative LAL
- Experiments
- 实现细节
- Baselines and protocol
- 三个版本
- baseline
- 性能指标
- 实验结果
- 合成数据
- 真实数据
- 时间
- Conclusion
- 代码分析
Abstract
提出一种新的数据驱动的主动学习的方法
关键思想:训练一个回归器,预测候选样本在特定学习状态下的预期误差减少。
将主动学习描述一个回归问题,给定训练好的分类器及其对没有标签的特定样本的输出,我们预测通过将标签添加到该点可以预期的泛化误差的减少。
在实践中,我们证明了我们可以通过使用简单的特征(例如分类器输出的方差或特定数据点在可能标签上的预测概率分布)来训练这个回归函数。
Towards data-driven active learning
Active learning (AL)
主要介绍了主动学习框架和不确定性抽样(AL启发式方法)
主动学习框架
主动学习目的:试图在标注尽可能少的样本的同时最大化模型的性能增益
在实践中:通过预测概率pt(yi=y|xi)[Xi是D维特征向量,yi是0/1标签],我们选择一个概率分类器f,该分类器可以在某个Lt⊂Z上训练,以将特征映射到标签ft(Xi)=ˆyi。
具体步骤:
1、小的带标签数据集Lt 和大的未标记的数据集Ut = Z\Lt
2、用L1来训练分类器ft
3、查询选择过程挑选要在下一迭代中注释的实例x∗∈Ut
4、专家给选择的实例进行标注,更新Lt和Ut
5、T递增,并重复步骤2-5,直到达到期望的精度或迭代次数达到预定义的极限。
不确定性采样(US)
基于数据驱动的US(不确定性采样):其选择集中在当前分类器最不确定的样本上
x ∗ = arg max x i ∈ U t H [ p t ( y i = y ∣ x i ) ] x^{*}=\underset{x_{i} \in \mathcal{U}_{t}}{\arg \max } \mathcal{H}\left[p_{t}\left(y_{i}=y \mid x_{i}\right)\right] x∗=xi∈UtargmaxH[pt(yi=y∣xi)]
熵是随机变量不确定性的度量,熵值越高,表明概率分布的不确定性越大。熵最高的样本是分类器对其类别成员最不确定的样本。
Motivation
通过左图可以看出 p0最接近0.5的数据样本确实是产生最大误差减少的数据样本。右图看出类0包含的数据点是另一个类的两倍,可以看出对应于最大预期误差减少的p0的值不同于0.5,因此US的选择变得次优,对于这0/1类,误差的减少不再是对称的。[横坐标:代表概率 纵坐标:最大误差减小的值]
问题:虽然查询选择过程可以考虑数据集和分类器的统计属性,但没有简单的方法可以预见所有可能因素的影响。
思路:提出学习主动学习(LAL),利用分类器和数据的特性来预测潜在的误差减少。使用回归模型来处理查询选择问题,这种观点使我们能够以灵活的方式构建新的AL策略。
Monte-Carlo LAL
提出两个版本的AL策略 :Independent LAL 和 Iterative LAL
构建第一个分类器Independent LAL时,单独和随机地合并未使用的标签来重新训练分类器,目标是将测试性能的变化与分类器和新添加的数据点的属性相关联。
构建 Iterative LAL时,进一步扩展了我们的方法,通过一个连续的过程来解释AL引起的选择偏差。
Independent LAL
下面将对Algorithm 1 进行描述:
- 首先定义 训练集D、测试集D’、分类器f 、划分函数SPLIT ,SPLIT(D , T )表示将D集合拆分成大小为T的标记集合Lt 和剩余未标记的Ut,初始化阶段通过SPLIT将集合D分为Lt和Ut
- 通过已标注数据集Lt来训练分类器f,生成函数ft,使用ft来预测测试集D’的元素x’的标签,估计测试集分类损失lt
- 用K个参数来刻画分类器的状态 ϕ τ = { ϕ τ 1 , … , ϕ τ K } \phi_{\tau}=\left\{\phi_{\tau}^{1}, \ldots, \phi_{\tau}^{K}\right\} ϕτ={ϕτ1,…,ϕτK}
特定于特定的分类器类型【基于核–>核函数参数 ;随机森林 -->树的平均深度;集成分类器–>预测可变性 】 - 从Uτ中随机选择一个新的数据点x ,形成一个新的标记集合,计算数据点x 的参数,训练分类器fx,导致新的测试集lx
- 计算减损【lt - lx】,学习状态由K和R的表征
- 对Q个不同的初始化L1t,L2t重复过程
- 返回M个不同的数据点x的【误差减少,分类器数据点】的状态对
主要思想是:观测ξ应该位于光滑的流形上,并且分类器的相似状态会导致在注释相似样本时产生类似的行为,因此,回归函数可以预测在给定分类器状态下注释特定样本的潜在错误减少。
对于算法2 而言:
- 通过算法1提供的 Q个误差减少值和状态对,来训练回归器g ,也相当于找查找不特定于数据集D的映射g,可用于检测承诺在其他目标域Z中最大程度地提高分类器性能的样本。
- 贪婪的选择回归器g预测的值中的最大值,在迭代t处具有最大误差减少潜力的数据点 x ∗ = arg max x ∈ U t g ( ϕ t , ψ x ) . x^{*}=\underset{x \in \mathcal{U}_{t}}{\arg \max } g\left(\phi_{t}, \psi_{x}\right) . x∗=x∈Utargmaxg(ϕt,ψx).
Iterative LAL
问题:对于迭代t>0的任何AL策略,标记的集合Lt由在先前迭代中选择的样本组成,这显然不是随机的。在算法1和算法2中,无论有多少标记样本τ可用,数据集D被随机分成Lτ和Uτ。
解决方案:将数据集D随机划分为Lτ和Uτ不同,建议模拟AL过程,该过程根据在先前收集的数据上学习的策略来选择数据点。
通过算法3 的for循环( line 4 )可以知道,基于回归函数g2学习策略A(G2),当有2个随机点可用时,该回归函数g2选择最有希望的3个数据点。在下一次迭代中,它学习策略A(G3),该策略A(G3)在给定2个随机点和由A(G2)等选择的1个数据点的情况下选择4个数据点。这样,每次迭代的样本取决于前一次迭代的样本,并且AL的采样偏差被表示在从中学习最终策略LALITERATIVE的数据Ξ,∆中。
Experiments
实现细节
- 冷启动:我们从两个类别中的每一个类别中各取一个样本开始
- 热启动:大小为N0 << N \mathrm{N} N 用于训练初始分类器
分类器使用: F − \mathrm{F}- F− 随机森林分类器 \quad G-随机森林(RF)回归
学习特征包括: 预测概率 p ( y = 0 ∣ L t , x ) \quad p\left(y=0 \mid \mathcal{L}_{t}, x\right) p(y=0∣Lt,x)
0种类的比例
Lt 交叉验证的准确性Ft
Ft特征重要性的方差
森林方差计算为树木对Ut的预测方差
森林平均树深 Lt的大小
Baselines and protocol
三个版本
- a) LAL-independent-2D --在冷启动合成数据集上训练的LAL - independent策略
- b) LAL-iterative-2D —在冷启动合成数据集上训练的LAL-iterative 策略
- c) LAL-independent-WS --热启动代表性数据训练的LAL - independent策略
baseline
- a)Rs,随机抽样;
- b)Us,不确定性抽样;
- c)Kapoor,一种通过合并GP分类器的均值和方差估计来平衡勘探和开采的算法
- d)ALBE,最近的Meta-AL示例,自适应地使用包括Us和Rs在内的策略组合。
性能指标
对应于特定任务,主要有:
- 准确率
- IOU
- 骰子得分
- AMS得分
- ROC曲线下的面积
实验结果
合成数据
双高斯云实验
用了两个分类器:RF分类器和高斯过程分类器(GPC)
生成了1000个新的不可见的合成数据集,如图2(第一行)的所示。在这两种情况下,所提出的LAL策略都选择了有助于比RS、US、Kapoor和ALBE更快地构建更好分类器的数据点。
XOR实验
XOR的数据集对于许多机器学习方法都是具有挑战性的,AL算法难以处理图2底行中描述的任务,即棋盘2×2、棋盘4×4和Rätsch等人的香蕉数据集。
但LAL-Iteration-2D在棋盘2×2和棋盘2×2上的表现与它在香蕉数据集上的表现相当。
真实数据
主要分为五种数据集:
- a)纹状体,3D电子显微镜大鼠神经组织的堆叠,任务是检测和分割线粒体;
- b)MRI,从BRANTS竞赛中获得的脑部扫描,任务是分割T1,T2,FLAIR和Gdorium T1后MR图像中的脑瘤;
- c)信用卡,这是欧洲持卡人在2013年进行的信用卡交易的数据集;
- d)Splice,一个分子生物学数据集,任务是检测DNA序列中的剪接连接;
- e)Higgs,一个高能物理数据集,包含模拟Tlas实验的测量结果,任务是检测噪声信号中的Higgs玻色子;
都可以清晰的看出两种LAL的策略是相对比较好的。
时间
LAL运行时间:取决于通过交叉验证估计的随机森林回归变量的参数
可以看出在6种不同的数据集中,LAL的时间比ALBE这种方案所消耗的时间会更少一些。
Conclusion
从简单的2D数据中学习主动学习对具有挑战性的新领域具有非常好的泛化能力。从特定于应用程序的数据子集学习进一步扩展了我们方法的适用性。最后,LAL对分类器类型和特征的选择表现出了稳健性。
代码分析
使用随机森林分类器来解决分类问题,在开始时只有几个可用的数据点,AL策略选择下一步添加哪个数据点。然后从ground truth中检索标签,重新训练分类器,并对预定义的迭代次数重复该过程。
首先导入主要的类和库,包括active_learn、lal_model、dataset、experiment、results
import numpy as np
from sklearn.ensemble import RandomForestRegressor
%matplotlib notebook
import matplotlib.pyplot as pltimport sys
sys.path.append('G:\阅读文献\LAL-master\LAL-master/Classes/')
# import various AL strategies
from active_learner import ActiveLearnerRandom
from active_learner import ActiveLearnerUncertainty
from active_learner import ActiveLearnerLAL
# import the dataset class
from dataset import DatasetCheckerboard2x2
from dataset import DatasetCheckerboard4x4
from dataset import DatasetRotatedCheckerboard2x2
from dataset import DatasetStriatumMini
# import the model for LAL strategy
from lal_model import LALmodel
# import Experiment and Result classes that will be responsible for running AL and saving the results
from experiment import Experiment
from results import Results
active_learn模块包含了主动学习的经典选择样本的策略
1、ActiveLearnerRandom:从未标记的样本中随机选择样本加入到标记的样本中,更新未标记的样本
def selectNext(self): self.indicesUnknown = np.random.permutation(self.indicesUnknown)self.indicesKnown = np.concatenate(([self.indicesKnown, np.array([self.indicesUnknown[0]])])); self.indicesUnknown = self.indicesUnknown[1:]
2、ActiveLearnerUncertainty:选择最不确定的样本(预测值越接近0.5,越不确定)
def selectNext(self):# 预测其余的数据点unknownPrediction = self.model.predict_proba(self.dataset.trainData[self.indicesUnknown,:])[:,0]# 计算预测点的值与0.5的差值,并排序,选择最小的值的数据点selectedIndex1toN = np.argsort(np.absolute(unknownPrediction-0.5))[0]#选择样本的索引selectedIndex = self.indicesUnknown[selectedIndex1toN]#将选择样本数据点加入已标记的数据点中self.indicesKnown = np.concatenate(([self.indicesKnown, np.array([selectedIndex])]))#从未标记样本集里面删除已选择数据点self.indicesUnknown = np.delete(self.indicesUnknown, selectedIndex1toN)
3、ActiveLearnerLAL:本文的选择样本的方法,通过随机预先训练好的随机森林的回归器,得到预测的temp,通过计算得到8个特征,将所有特征放在一起进行回归,通过添加点来预测误差的预期减少,选择误差减少最大的数据点。
def selectNext(self):unknown_data = self.dataset.trainData[self.indicesUnknown,:]known_labels = self.dataset.trainLabels[self.indicesKnown,:]n_lablled = np.size(self.indicesKnown)n_dim = np.shape(self.dataset.trainData)[1]# 回归器的预测值temp = np.array([tree.predict_proba(unknown_data)[:,0] for tree in self.model.estimators_])# - 数据点预测值的平均值和标准差f_1 = np.mean(temp, axis=0)f_2 = np.std(temp, axis=0)# - 正分比例f_3 = (sum(known_labels>0)/n_lablled)*np.ones_like(f_1)# 背包估计的得分f_4 = self.model.oob_score_*np.ones_like(f_1)# - 特征重要性的方差系数f_5 = np.std(self.model.feature_importances_/n_dim)*np.ones_like(f_1)# - 通过查看某些预测的方差平均值来估计森林的方差f_6 = np.mean(f_2, axis=0)*np.ones_like(f_1)# -计算森林中树木的平均深度f_7 = np.mean(np.array([tree.tree_.max_depth for tree in self.model.estimators_]))*np.ones_like(f_1)# - 已标记数据点的数量f_8 = np.size(self.indicesKnown)*np.ones_like(f_1)# 所有特征放在一起进行回归LALfeatures = np.concatenate(([f_1], [f_2], [f_3], [f_4], [f_5], [f_6], [f_7], [f_8]), axis=0)LALfeatures = np.transpose(LALfeatures)# 通过添加点来预测误差的预期减少LALprediction = self.lalModel.predict(LALfeatures)# 选择误差减少最大的数据点selectedIndex1toN = np.argmax(LALprediction)# 检索所选数据点的真实索引 selectedIndex = self.indicesUnknown[selectedIndex1toN]self.indicesKnown = np.concatenate(([self.indicesKnown, np.array([selectedIndex])]))self.indicesUnknown = np.delete(self.indicesUnknown, selectedIndex1toN)
lal_model:预测由添加数据点引起的预期误差减少的回归器类
交叉验证模型:crossValidateLALmodel
输入:possible_estimators --随机森林回归中估计器(树)的可能数量列表 、
possible_depth–RF 回归器中树的可能最大深度列表 、 possible_features–RF 回归器中树分裂中可能的最大特征数列表
输出:best_score 最好得分
主要代码model = RandomForestRegressor(n_estimators = est, max_depth=depth, max_features=feat, oob_score=True, n_jobs=8)model.fit(self.all_data_for_lal[:,:], np.ravel(self.all_labels_for_lal))
构建模型:bulitModel 以参数标识符作为输入拟合回归量
self.model = RandomForestRegressor(n_estimators = est, max_depth=depth, max_features=feat, oob_score=True, n_jobs=8)
self.model.fit(self.all_data_for_lal, np.ravel(self.all_labels_for_lal))
print('oob score = ', self.model.oob_score_)
dataset:主要介绍了几个数据集类以及数据集的初始化操作
初始化操作
self.nStart = nStart
#正类
cl1 = np.nonzero(self.trainLabels==1)[0]
#随机排序
indices1 = np.random.permutation(cl1)
self.indicesKnown = np.array([indices1[0]]);
#负类
cl2 = np.nonzero(self.trainLabels==0)[0]
indices2 = np.random.permutation(cl2)
#将正类和负类合并一起
self.indicesKnown = np.concatenate(([self.indicesKnown, np.array([indices2[0]])]));
# combine all the rest of the indices that have not been sampled yetindicesRestAll = np.concatenate(([indices1[1:], indices2[1:]]));
#交换它们
indicesRestAll = np.random.permutation(indicesRestAll)
加载数据集,分为训练集,测试集
def __init__(self):Dataset.__init__(self) filename = './data/checkerboard2x2_train.npz'dt = np.load(filename)self.trainData = dt['x']self.trainLabels = dt['y'] # 在线标准化过程scaler = preprocessing.StandardScaler().fit(self.trainData)self.trainData = scaler.transform(self.trainData)filename = './data/checkerboard2x2_test.npz'dt = np.load(filename)self.testData = dt['x']self.testLabels = dt['y']self.testData = scaler.transform(self.testData)
experiment:运行主动学习实验的类,主要包括初始化、运行、重置操作
def run(self):# 运行实验并返回结果for it in range(self.nIterations):print('.', end="")for alearner in self.alearners:alearner.train()perf = alearner.evaluate(self.performanceMeasures)for key in perf:self.performances[alearner.name][key].append(perf[key])alearner.selectNext()return self.performances
def reset(self):#重置实验,重置数据集的起始数据点,重置学习者和表现self.dataset.setStartState(self.dataset.nStart)for alearner in self.alearners:alearner.reset()self.performances = dict()for alearner in self.alearners:self.performances[alearner.name] = dict()for performanceMeasure in self.performanceMeasures:self.performances[alearner.name][performanceMeasure] = []
result:保存、加载和绘制结果的类
def addPerformance(self, performance):# 添加新结果for alearner in performance:for performanceMeasure in performance[alearner]:if np.size(self.performances[alearner][performanceMeasure])==0:self.performances[alearner][performanceMeasure] = np.array([performance[alearner][performanceMeasure]])else:self.performances[alearner][performanceMeasure] = np.concatenate((self.performances[alearner][performanceMeasure], np.array([performance[alearner][performanceMeasure]])), axis=0
#保存结果def saveResults(self, filename):state = self.__dict__.copy()pkl.dump(state, open( './exp/'+filename+'.p', "wb" ) )
#绘制结果
if performanceMeasure in self.existingMetrics:plt.figure()i = 0for alearner in self.alearners:if performanceMeasure=='accuracy' or performanceMeasure=='auc':avResult = np.mean(self.performances[alearner][performanceMeasure], axis=0)if performanceMeasure=='auc':avResult =np.mean(self.performances[alearner]['auc'],axis=(0))if performanceMeasure=='IoU':avResult =np.mean((self.performances[alearner]['TP']/(self.performances[alearner]['TP']+self.performances[alearner]['FP']+self.performances[alearner]['FN']+small_eps)),axis=(0))elif performanceMeasure=='dice':avResult = np.mean((2*self.performances[alearner]['TP']/(2*self.performances[alearner]['TP']+self.performances[alearner]['FP']+self.performances[alearner]['FN']+small_eps)),axis=(0))elif performanceMeasure=='f-measure':avResult = np.mean((2*self.performances[alearner]['TP']/(2*self.performances[alearner]['TP']+self.performances[alearner]['FP']+self.performances[alearner]['FN']+small_eps)),axis=(0))plt.plot(avResult, color=col(i), label=alearner)
几个类介绍完毕了,现在开始创建LAL回归模型
%%timefn = 'LAL-randomtree-simulatedunbalanced-big.npz'
# 通过交叉验证回归器找这些参数
parameters = {'est': 2000, 'depth': 40, 'feat': 6 }
filename = 'G:\阅读文献\LAL-master\LAL-master/lal datasets/'+fn
# 加载数据集
regression_data = np.load(filename)
#得到数据特征
regression_features = regression_data['arr_0']
#得到数据labels
regression_labels = regression_data['arr_1']print('Building lal regression model..')
#用随机森林回归器来创建
lalModel1 = RandomForestRegressor(n_estimators = parameters['est'], max_depth = parameters['depth'], max_features=parameters['feat'], oob_score=True, n_jobs=8)lalModel1.fit(regression_features, np.ravel(regression_labels)) print('Done!')
print('Oob score = ', lalModel1.oob_score_)
跑实验结果
%%time# number of experiment repeats
nExperiments = 20
# number of estimators (random trees) in the classifier
nEstimators = 50
# number of labeled points at the beginning of the AL experiment
nStart = 2
# number of iterations in AL experiment
nIterations = 100
# the quality metrics computed on the test set to evaluate active learners
quality_metrics = ['accuracy']# load dataset
# 加载数据集
dtst = DatasetCheckerboard2x2()
# set the starting point
dtst.setStartState(nStart)
# Active learning strategies
# 加载active_learner类中的策略
alR = ActiveLearnerRandom(dtst, nEstimators, 'random')
alU = ActiveLearnerUncertainty(dtst, nEstimators, 'uncertainty')
alLALindepend = ActiveLearnerLAL(dtst, nEstimators, 'lal-rand', lalModel1)
alLALiterative = ActiveLearnerLAL(dtst, nEstimators, 'lal-iter', lalModel2)
als = [alR, alU, alLALindepend, alLALiterative]exp = Experiment(nIterations, nEstimators, quality_metrics, dtst, als, 'here we can put a comment about the current experiments')
# the Results class helps to add, save and plot results of the experiments
res = Results(exp, nExperiments)for i in range(nExperiments):print('\n experiment #'+str(i+1))# run an experimentperformance = exp.run()res.addPerformance(performance)# reset the experiment (including sampling a new starting state for the dataset)exp.reset()print()
res.saveResults('checkerboard2x2-exp')
绘制结果
res2plot = Results()
res2plot.readResult('checkerboard2x2-exp')
res2plot.plotResults(metrics = ['accuracy'])
Learning Active Learning from Data 主动学习笔记相关推荐
- Machine Learning(吴恩达) 学习笔记(一)
Machine Learning(吴恩达) 学习笔记(一) 1.什么是机器学习? 2.监督学习 3.无监督学习 4.单变量线性回归 4.1代价函数 4.2 梯度下降 5.代码回顾 最近在听吴恩达老师的 ...
- 【李宏毅机器学习】Semi-supervised Learning 半监督学习(p24) 学习笔记
文章目录 Semi-supervised Learning Introduction Supervised Learning Semi-supervised Learning Why semi-sup ...
- 【Deep Learning】VGG16之feature map学习笔记
最近学习BeautyGAN需要用到VGG16提取的feature map进行训练,简单学习了一些关于VGG16和feature map相关的内容. VGG16网络结构 VGG16总共有16层,13个卷 ...
- Learning the Vi Editor, 6th Edition学习笔记(0)
文本编辑是任何一个计算机系统最普遍的应用之一,而Vi 是最有用的标准文本编辑器之一.我们可以使用Vi创建新的文件或者编辑任意存在的UNIX文本文件. 本书主要分为3部分,由12个章节和5个附录组成,详 ...
- 《machine learning in action》机器学习 算法学习笔记 决策树模型
决策树模型 重要任务:是为了理解数据中所蕴含的知识信息,因此决策树可以使用不熟悉的数据集合,并从中提取出一系列规则,这些机器根据数据集创建规则的过程就是机器学习的过程. 优点:计算复杂度不高,输出结果 ...
- Inside Spring - learning notes - Jerry Wang的Spring学习笔记
Created by Wang, Jerry, last modified on Aug 17, 2016 第一章 Spring:核心,组件,应用 核心:IoC容器和AOP模块.通过IoC容器管理PO ...
- 《Deep Learning from Scratch》鱼书学习笔记 3-6,7 手写数字的识别
3.6 手写数字的识别 3.6.1 MNIST 数据集 import sys, os sys.path.append(os.pardir) # 为了导入父目录中的文件而进行的设定 from datas ...
- 【NDN基础】Named Data Networking 学习笔记
弄懂的问题: (1)Page 4:PIT表的设计考虑: <1>支持多播数据交付. <2>控制流量负载以实现流平衡. <3>PIT条目是路由器负载的指示器,限制PIT ...
- [Spring Data MongoDB]学习笔记--建立数据库的连接
1. 有了上一篇的Mongo后,连接数据库我们还需要更多的信息,比如数据库名字,用户名和密码等. 我们可以继续来配置MongoDbFactory的实例. public interface MongoD ...
最新文章
- Ubuntu解决开机屏幕默认亮度偏低问题
- Linux下显示前10个占用空间最大的文件或目录命令
- python如何使用apriori_python-如何加快基于Apriori框架的速度,以仅生...
- 廖雪峰python学习笔记——函数式编程
- java 字节码分析_手把手带你分析Java中的Class字节码文件
- idea 搜索不到gsonformat_Idea中GsonFormat插件安装
- 近期状态几点简单思考
- arcore 示例_Android增强现实– Android ARCore示例
- CSV 导入SQL Server(bulk insert方式)
- 面试题整理 | 45道CSS面试题
- java snmp walk,snmpwalk命令常用方法总结(转)
- 二元函数可微与可导的关系_二元函数的连续偏导数可微之间的关系
- 3个方法解决百度网盘限速问题
- matlab积分器,MATLAB_SIMULINK__积分器相关操作
- css斜线边框,CSS border斜线效果
- 手机端android app崩溃的常见类型
- reads去污染接头
- 基于Stm32的WiFi多功能LED
- c语言循环结构排序,C语言循环结构
-C语言冒泡排序算法(附带源码)
- 多线程系列学习:ABA问题
热门文章
- vscode装机必备
- 指纹安全再进一步 可区分真假指纹的技术诞生
- WPF DataGrid自定义样式模板 列表头分隔线 滚动条滑块大小设定 动态数据绑定和更新
- 神兵利器系列|nessus8.8安装破解
- 字节跳动面试准备 | 关于字节
- Android sqlite数据库操作通用框架AHibernate(三)-升级为1.1版本
- Qt5.7关于Quazip的编译和使用
- 五十三、免费移动大厅项目(所有类在一个包里面,即复即用)
- CNN 面试向知识点背书
- pika.exceptions.ChannelClosed: (406, “PRECONDITION_FAILED - parameters for queue ‘test‘ in vhost ‘/