海康
1.电话面试:
介绍你做的项目?
特征选择方法?
解释logostic回归?
说一下Xgboost?
Xgboost和GBDT的区别?发布
2.杭州面
问项目问的很细,重点看项目?
你觉得你项目中哪部分做的好?
如果再给你一次机会,你会从哪些方面考虑?
Xgboost特点(我用的这个比较多)?
特征选择的方法?
其它的就说我自己的项目了,问的很细,有的你没有做过,不要写上去
多益
(参考这个基本一样;面试官很懒几天的面试都用这个题目)
机器学习
有监督学习和无监督学习的区别
有监督学习:对具有标记的训练样本进行学习,以尽可能对训练样本集外的数据进行分类预测。(LR,SVM,BP,RF,GBDT)
无监督学习:对未标记的样本进行训练学习,比发现这些样本中的结构知识。(KMeans,DL)
正则化
正则化是针对过拟合而提出的,以为在求解模型最优的是一般优化最小的经验风险,现在在该经验风险上加入模型复杂度这一项(正则化项是模型参数向量的范数),并使用一个rate比率来权衡模型复杂度与以往经验风险的权重,如果模型复杂度越高,结构化的经验风险会越大,现在的目标就变为了结构经验风险的最优化,可以防止模型训练过度复杂,有效的降低过拟合的风险。奥卡姆剃刀原理,能够很好的解释已知数据并且十分简单才是最好的模型。
过拟合
如果一味的去提高训练数据的预测能力,所选模型的复杂度往往会很高,这种现象称为过拟合。所表现的就是模型训练时候的误差很小,但在测试的时候误差很大。
产生的原因
过拟合原因
1. 样本数据的问题。
样本数量太少
抽样方法错误,抽出的样本数据不能有效足够代表业务逻辑或业务场景。比如样本符合正态分布,却按均分分布抽样,或者样本数据不能代表整体数据的分布
样本里的噪音数据干扰过大
2. 模型问题
模型复杂度高 、参数太多
决策树模型没有剪枝
权值学习迭代次数足够多(Overtraining),拟合了训练数据中的噪声和训练样例中没有代表性的特征.
解决方法
1. 样本数据方面。
增加样本数量,对样本进行降维,添加验证数据
抽样方法要符合业务场景
清洗噪声数据
2. 模型或训练问题
控制模型复杂度,优先选择简单的模型,或者用模型融合技术。
利用先验知识,添加正则项。L1正则更加容易产生稀疏解、L2正则倾向于让参数w趋向于0.
交叉验证
不要过度训练,最优化求解时,收敛之前停止迭代。
决策树模型没有剪枝
权值衰
线性分类器与非线性分类器的区别以及优劣
如果模型是参数的线性函数,并且存在线性分类面,那么就是线性分类器,否则不是。
常见的线性分类器有:LR,贝叶斯分类,单层感知机、线性回归
常见的非线性分类器:决策树、RF、GBDT、多层感知机SVM两种都有(看线性核还是高斯核)
线性分类器速度快、编程方便,但是可能拟合效果不会很好
非线性分类器编程复杂,但是效果拟合能力强
为什么LR要使用Sigmod函数?
说到底源于sigmoid,或者说exponentialfamily所具有的最佳性质,即maximum entropy的性质。虽然不清楚历史上孰先孰后,但这并不妨碍maximum entropy给了logistic regression一个很好的数学解释。为什么maximum entropy好呢?entropy翻译过来就是熵,所以maximum entropy也就是最大熵。熵原本是information theory中的概念,用在概率分布上可以表示这个分布中所包含的不确定度,熵越大不确定度越大。所以大家可以想象到,均匀分布熵最大,因为基本新数据是任何值的概率都均等。而我们现在关心的是,给定某些假设之后,熵最大的分布。也就是说这个分布应该在满足我假设的前提下越均匀越好。比如大家熟知的正态分布,正是假设已知mean和variance后熵最大的分布。回过来看logistic regression,这里假设了什么呢?首先,我们在建模预测 Y|X,并认为 Y|X 服从bernoulli distribution,所以我们只需要知道 P(Y|X);其次我们需要一个线性模型,所以 P(Y|X) = f(wx)。接下来我们就只需要知道 f 是什么就行了。而我们可以通过最大熵原则推出的这个 f,就是sigmoid。其实前面也有人剧透了bernoulli的exponential family形式,也即是 1/ (1 + e^-z)
LR与Liner SVM区别
n Linear SVM和LR都是线性分类器
n Linear SVM不直接依赖数据分布,分类平面不受一类点影响;LR则受所有数据点的影响,如果数据不同类别strongly unbalance一般需要先对数据做balancing。
n Linear SVM依赖数据表达的距离测度,所以需要对数据先做normalization(归一化);LR不受其影响Linear SVM依赖penalty的系数,实验中需要做validation
n Linear SVM和LR的performance都会收到outlier的影响,其敏感程度而言,谁更好很难下明确结论。
常见的分类算法有哪些?
SVM、神经网络、随机森林、逻辑回归、KNN、贝叶斯
请描述极大似然估计MLE和最大后验估计MAP之间的区别。请解释为什么MLE比MAP更容易过拟合。
最大似然估计提供了一种给定观察数据来评估模型参数的方法,即:“模型已定,参数未知”。简单而言,假设我们要统计全国人口的身高,首先假设这个身高服从服从正态分布,但是该分布的均值与方差未知。我们没有人力与物力去统计全国每个人的身高,但是可以通过采样,获取部分人的身高,然后通过最大似然估计来获取上述假设中的正态分布的均值与方差。最大后验估计是根据经验数据获得对难以观察的量的点估计。与最大似然估计类似,但是最大的不同时,最大后验估计的融入了要估计量的先验分布在其中。故最大后验估计可以看做规则化的最大似然估计。
SVM为什么引入对偶问题?什么是对偶?
l 对偶问题往往更加容易求解(结合拉格朗日和kkt条件)
可以很自然的引用核函数(拉格朗日表达式里面有内积,而核函数也是通过内积进行映射的)
l 在优化理论中,目标函数 f(x) 会有多种形式:如果目标函数和约束条件都为变量 x 的线性函数, 称该问题为线性规划; 如果目标函数为二次函数, 约束条件为线性函数, 称该最优化问题为二次规划; 如果目标函数或者约束条件均为非线性函数, 称该最优化问题为非线性规划。每个线性规划问题都有一个与之对应的对偶问题,对偶问题有非常良好的性质,以下列举几个:
对偶问题的对偶是原问题;
无论原始问题是否是凸的,对偶问题都是凸优化问题;
对偶问题可以给出原始问题一个下界;
当满足一定条件时,原始问题与对偶问题的解是完全等价的
判别式模型和生成式模型
判别方法:由数据直接学习决策函数 Y = f(X),或者由条件分布概率 P(Y|X)作为预测模型,即判别模型。
生成方法:由数据学习联合概率密度分布函数 P(X,Y),然后求出条件概率分布P(Y|X)作为预测的模型,即生成模型。
由生成模型可以得到判别模型,但由判别模型得不到生成模型。
常见的判别模型有:K近邻、SVM、决策树、感知机、线性判别分析(LDA)、线性回归、传统的神经网络、逻辑斯蒂回归、boosting、条件随机场
常见的生成模型有:朴素贝叶斯、隐马尔可夫模型、高斯混合模型、文档主题生成模型(LDA)、限制玻尔兹曼机
SVM如何实现,SMO算法如何实现?
SMO是用于快速求解SVM的。
它选择凸二次规划的两个变量,其他的变量保持不变,然后根据这两个变量构建一个二次规划问题,这个二次规划关于这两个变量解会更加的接近原始二次规划的解,通过这样的子问题划分可以大大增加整个算法的计算速度,关于这两个变量:
其中一个是严重违反KKT条件的一个变量
另一个变量是根据自由约束确定,好像是求剩余变量的最大化来确定的。
如果训练样本数量少于特征数量,怎么办?
如果训练集很小,那么高偏差/低方差分类器(如朴素贝叶斯分类器)要优于低偏差/高方差分类器(如k近邻分类器),因为后者容易过拟合。然而,随着训练集的增大,低偏差/高方差分类器将开始胜出(它们具有较低的渐近误差),因为高偏差分类器不足以提供准确的模型。你也可以认为这是生成模型与判别模型的区别。维度高用非线性分类器,维度低用线性分类器。
Xgboost是如何调参的?
XGBoost的参数
●General Parameters:
○ booster:所使用的模型,gbtree或gblinear
○ silent:1则不打印提示信息,0则打印,默认为0
○ nthread:所使用的线程数量,默认为最大可用数量
●Booster Parameters(gbtree):
○ eta:学习率,默认初始化为0.3,经多轮迭代后一般衰减到0.01至0.2
○ min_child_weight:每个子节点所需的最小权重和,默认为1
○ max_depth:树的最大深度,默认为6,一般为3至10
○ max_leaf_nodes:叶结点最大数量,默认为2^6
○ gamma:拆分节点时所需的最小损失衰减,默认为0
○ max_delta_step:默认为0
○ subsample:每棵树采样的样本数量比例,默认为1,一般取0.5至1
○ colsample_bytree:每棵树采样的特征数量比例,默认为1,一般取0.5至1
○ colsample_bylevel:默认为1
○ lambda:L2正则化项,默认为1
○ alpha:L1正则化项,默认为1
○ scale_pos_weight:加快收敛速度,默认为1
●Learning Task Parameters:
○ objective:目标函数,默认为reg:linear,还可取binary:logistic、multi:softmax、multi:softprob
○ eval_metric:误差函数,回归默认为rmse,分类默认为error,其他可取值包括rmse、mae、logloss、merror、mlogloss、auc
○ seed:随机数种子,默认为0
解释一下LDA?
主题模型是一个比较广的领域。Spark 1.3加入了隐含狄利克雷分布(LDA),差不多是现今最成功的主题模型。最初被开发用于文本分析和群体遗传学,LDA之后被不断拓展,应用到从时间序列分析到图片分析等问题。首先,我们从文本分析的角度描述LDA。
什么是主题?主题不是LDA的输入,所以LDA必须要从纯文本中推断主题。LDA将主题定义为词的分布。例如,当我们在一个20个新闻组的文章数据集上运行MLlib的LDA,开始的几个主题是:
Adaboost、GBDT和 Xgboost的区别?
1. 传统GBDT以CART作为基分类器,xgboost还支持线性分类器,这个时候xgboost相当于带L1和L2正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题)。
2. 2传统GBDT在优化时只用到一阶导数信息,xgboost则对代价函数进行了二阶泰勒展开,同时用到了一阶和二阶导数。顺便提一下,xgboost工具支持自定义代价函数,只要函数可一阶和二阶求导。
3. xgboost在代价函数里加入了正则项,用于控制模型的复杂度。正则项里包含了树的叶子节点个数、每个叶子节点上输出的score的L2模的平方和。从Bias-variancetradeoff角度来讲,正则项降低了模型的variance,使学习出来的模型更加简单,防止过拟合,这也是xgboost优于传统GBDT的一个特性。
4. Shrinkage(缩减),相当于学习速率(xgboost中的eta)。xgboost在进行完一次迭代后,会将叶子节点的权重乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间。实际应用中,一般把eta设置得小一点,然后迭代次数设置得大一点。(补充:传统GBDT的实现也有学习速率)
5. 列抽样(column subsampling)。xgboost借鉴了随机森林的做法,支持列抽样,不仅能降低过拟合,还能减少计算,这也是xgboost异于传统gbdt的一个特性。
6. 对缺失值的处理。对于特征的值有缺失的样本,xgboost可以自动学习出它的分裂方向。
7. xgboost工具支持并行。boosting不是一种串行的结构吗?怎么并行的?注意xgboost的并行不是tree粒度的并行,xgboost也是一次迭代完才能进行下一次迭代的(第t次迭代的代价函数里包含了前面t-1次迭代的预测值)。xgboost的并行是在特征粒度上的。我们知道,决策树的学习最耗时的一个步骤就是对特征的值进行排序(因为要确定最佳分割点),xgboost在训练之前,预先对数据进行了排序,然后保存为block结构,后面的迭代中重复地使用这个结构,大大减小计算量。这个block结构也使得并行成为了可能,在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行。
8. 可并行的近似直方图算法。树节点在进行分裂时,我们需要计算每个特征的每个分割点对应的增益,即用贪心法枚举所有可能的分割点。当数据无法一次载入内存或者在分布式情况下,贪心算法效率就会变得很低,所以xgboost还提出了一种可并行的近似直方图算法,用于高效地生成候选的分割点。
Adaboost和gbdt的区别是adaboost对于每个样本有一个权重,样本预估误差越大,权重越大。gradientboosting则是直接用梯度拟合残差,没有样本权重的概念。
SVM 的推导,特性?多分类怎么处理?
SVM是最大间隔分类器,几何间隔和样本的误分次数之间存在关系。
从线性可分情况下,原问题,特征转换后的dual问题,引入kernel(线性kernel,多项式,高斯),最后是soft margin。
线性:简单,速度快,但是需要线性可分
多项式:比线性核拟合程度更强,知道具体的维度,但是高次容易出现数值不稳定,参数选择比较多。
高斯:拟合能力最强,但是要注意过拟合问题。不过只有一个参数需要调整。
多分类问题,一般将二分类推广到多分类的方式有三种,一对一,一对多,多对多。
一对一:将N个类别两两配对,产生N(N-1)/2个二分类任务,测试阶段新样本同时交给所有的分类器,最终结果通过投票产生。
一对多:每一次将一个例作为正例,其他的作为反例,训练N个分类器,测试时如果只有一个分类器预测为正类,则对应类别为最终结果,如果有多个,则一般选择置信度最大的。从分类器角度一对一更多,但是每一次都只用了2个类别,因此当类别数很多的时候一对一开销通常更小(只要训练复杂度高于O(N)即可得到此结果)。
多对多:若干各类作为正类,若干个类作为反类。注意正反类必须特殊的设计
决策树的特性?
决策树基于树结构进行决策,与人类在面临问题的时候处理机制十分类似。其特点在于需要选择一个属性进行分支,在分支的过程中选择信息增益最大的属性,在划分中我们希望决策树的分支节点所包含的样本属于同一类别,即节点的纯度越来越高。决策树计算量简单,可解释性强,比较适合处理有缺失属性值的样本,能够处理不相关的特征,但是容易过拟合,需要使用剪枝或者随机森林。信息增益是熵减去条件熵,代表信息不确定性较少的程度,信息增益越大,说明不确定性降低的越大,因此说明该特征对分类来说很重要。由于信息增益准则会对数目较多的属性有所偏好,因此一般用信息增益率(c4.5) 其中分母可以看作为属性自身的熵。取值可能性越多,属性的熵越大。Cart决策树使用基尼指数来选择划分属性,直观的来说,Gini(D)反映了从数据集D中随机抽取两个样本,其类别标记不一致的概率,因此基尼指数越小数据集D的纯度越高,一般为了防止过拟合要进行剪枝,有预剪枝和后剪枝,一般用crossvalidation集进行剪枝。连续值和缺失值的处理,对于连续属性a,将a在D上出现的不同的取值进行排序,基于划分点t将D分为两个子集。一般对每一个连续的两个取值的中点作为划分点,然后根据信息增益选择最大的。与离散属性不同,若当前节点划分属性为连续属性,该属性还可以作为其后代的划分属性。
SVM、LR、决策树的对比?
SVM既可以用于分类问题,也可以用于回归问题,并且可以通过核函数快速的计算,LR实现简单,训练速度非常快,但是模型较为简单,决策树容易过拟合,需要进行剪枝等。从优化函数上看,soft margin的SVM用的是hingeloss,而带L2正则化的LR对应的是cross entropy loss,另外adaboost对应的是exponential loss。所以LR对远点敏感,但是SVM对outlier不太敏感,因为只关心support vector,SVM可以将特征映射到无穷维空间,但是LR不可以,一般小数据中SVM比LR更优一点,但是LR可以预测概率,而SVM不可以,SVM依赖于数据测度,需要先做归一化,LR一般不需要,对于大量的数据LR使用更加广泛,LR向多分类的扩展更加直接,对于类别不平衡SVM一般用权重解决,即目标函数中对正负样本代价函数不同,LR可以用一般的方法,也可以直接对最后结果调整(通过阈值),一般小数据下样本维度比较高的时候SVM效果要更优一些。SVM通过映射到高维在做回归使用的。
GBDT 和 决策森林的区别?
随机森林采用的是bagging的思想,bagging又称为bootstrap aggreagation,通过在训练样本集中进行有放回的采样得到多个采样集,基于每个采样集训练出一个基学习器,再将基学习器结合。随机森林在对决策树进行bagging的基础上,在决策树的训练过程中引入了随机属性选择。传统决策树在选择划分属性的时候是在当前节点属性集合中选择最优属性,而随机森林则是对结点先随机选择包含k个属性的子集,再选择最有属性,k作为一个参数控制了随机性的引入程度。
另外,GBDT训练是基于Boosting思想,每一迭代中根据错误更新样本权重,因此是串行生成的序列化方法,而随机森林是bagging的思想,因此是并行化方法。
如何判断函数凸或非凸?
首先定义凸集,如果x,y属于某个集合C,并且所有的 也属于c,那么c为一个凸集,进一步,如果一个函数其定义域是凸集,并且
则该函数为凸函数。上述条件还能推出更一般的结果,
如果函数有二阶导数,那么如果函数二阶导数为正,或者对于多元函数,Hessian矩阵半正定则为凸函数。
(也可能引到SVM,或者凸函数局部最优也是全局最优的证明,或者上述公式期望情况下的Jessen不等式)
解释对偶的概念
一个优化问题可以从两个角度进行考察,一个是primal 问题,一个是dual 问题,就是对偶问题,一般情况下对偶问题给出主问题最优值的下界,在强对偶性成立的情况下由对偶问题可以得到主问题的最优下界,对偶问题是凸优化问题,可以进行较好的求解,SVM中就是将primal问题转换为dual问题进行求解,从而进一步引入核函数的思想。
如何进行特征选择?
特征选择是一个重要的数据预处理过程,主要有两个原因,首先在现实任务中我们会遇到维数灾难的问题(样本密度非常稀疏),若能从中选择一部分特征,那么这个问题能大大缓解,另外就是去除不相关特征会降低学习任务的难度,增加模型的泛化能力。冗余特征指该特征包含的信息可以从其他特征中推演出来,但是这并不代表该冗余特征一定没有作用,例如在欠拟合的情况下也可以用过加入冗余特征,增加简单模型的复杂度。
在理论上如果没有任何领域知识作为先验假设那么只能遍历所有可能的子集。但是这显然是不可能的,因为需要遍历的数量是组合爆炸的。一般我们分为子集搜索和子集评价两个过程,子集搜索一般采用贪心算法,每一轮从候选特征中添加或者删除,分别成为前向和后先搜索。或者两者结合的双向搜索。子集评价一般采用信息增益,对于连续数据往往排序之后选择中点作为分割点。
常见的特征选择方式有过滤式,包裹式和嵌入式,filter,wrapper和embedding。Filter类型先对数据集进行特征选择,再训练学习器。Wrapper直接把最终学习器的性能作为特征子集的评价准则,一般通过不断候选子集,然后利用cross-validation过程更新候选特征,通常计算量比较大。嵌入式特征选择将特征选择过程和训练过程融为了一体,在训练过程中自动进行了特征选择,例如L1正则化更易于获得稀疏解,而L2正则化更不容易过拟合。L1正则化可以通过PGD, 近端梯度下降进行求解。
机器学习模型的评价指标?
分类:召回率、准确率、F值、ROC-AUC和PRC
回归:R值
小例子:
假设我们手上有60个正样本,40个负样本,我们要找出所有的正样本,系统查找出50个,其中只有40个是真正的正样本,计算上述各指标。
*TP: 将正类预测为正类数 40
*FN: 将正类预测为负类数 20
*FP: 将负类预测为正类数 10
*TN: 将负类预测为负类数 30
准确率(accuracy) = 预测对的/所有 = (TP+TN)/(TP+FN+FP+TN) = 70%
精确率(precision) = TP/(TP+FP) = 80%
召回率(recall) = TP/(TP+FN) = 2/3
为什么会产生过拟合,有哪些方法可以预防或克服过拟合?
一般在机器学习中,将学习器在训练集上的误差称为训练误差或者经验误差,在新样本上的误差称为泛化误差。显然我们希望得到泛化误差小的学习器,但是我们事先并不知道新样本,因此实际上往往努力使经验误差最小化。然而,当学习器将训练样本学的太好的时候,往往可能把训练样本自身的特点当做了潜在样本具有的一般性质。这样就会导致泛化性能下降,称之为过拟合,相反,欠拟合一般指对训练样本的一般性质尚未学习好,在训练集上仍然有较大的误差。
欠拟合:一般来说欠拟合更容易解决一些,例如增加模型的复杂度,增加决策树中的分支,增加神经网络中的训练次数等等。
过拟合:一般认为过拟合是无法彻底避免的,因为机器学习面临的问题一般是np-hard,但是一个有效的解一定要在多项式内可以工作,所以会牺牲一些泛化能力。过拟合的解决方案一般有增加样本数量,对样本进行降维,降低模型复杂度,利用先验知识(L1,L2正则化),利用cross-validation,early stopping等等。
采用 EM 算法求解的模型有哪些,为什么不用牛顿法或梯度下降法?
用EM算法求解的模型一般有GMM或者协同过滤,k-means其实也属于EM。EM算法一定会收敛,但是可能收敛到局部最优。由于求和的项数将随着隐变量的数目指数上升,会给梯度计算带来麻烦。
EM算法的基本概念和应用场景?
最大期望(EM)算法是在概率(probabilistic)模型中寻找参数最大似然估计或者最大后验估计的算法,其中概率模型依赖于无法观测的隐藏变量(Latent Variable)。假设我们估计知道A和B两个参数,在开始状态下二者都是未知的,并且知道了A的信息就可以得到B的信息,反过来知道了B也就得到了A。可以考虑首先赋予A某种初值,以此得到B的估计值,然后从B的当前值出发,重新估计A的取值,这个过程一直持续到收敛为止。
参考链接:http://www.tuicool.com/articles/Av6NVzy
最大期望经常用在机器学习和计算机视觉的数据聚类领域。
用 EM 算法推导解释 Kmeans
k-means算法是高斯混合聚类在混合成分方差相等,且每个样本仅指派一个混合成分时候的特例。注意k-means在运行之前需要进行归一化处理,不然可能会因为样本在某些维度上过大导致距离计算失效。k-means中每个样本所属的类就可以看成是一个隐变量,在E步中,我们固定每个类的中心,通过对每一个样本选择最近的类优化目标函数,在M步,重新更新每个类的中心点,该步骤可以通过对目标函数求导实现,最终可得新的类中心就是类中样本的均值。
机器学习中如何避免局部最优?
首先改变学习迭代算法,采用adam之类动态更新的迭代算法,或者采用启发式算法,加入规避局部最小值的措施。再或者就是多做几次。
常见聚类算法比较
(1) k-means
优点:简单,易于理解和实现;时间复杂度低,每轮迭代负载度为O(n*k)
缺点:需要对均值给出定义;需要指定聚类的数目;一些过大的异常值会带来很大影响;需要指定初始聚类中心,算法对初始值敏感;适合球形类簇。
(2) 层次聚类(试图在不同层次对数据集进行划分,从而形成树形的聚类结构。AGNES是一种采用自底向上聚合策略的层次聚类算法)
优点:距离和规则的相似度容易定义,限制少;不需要预先指定聚类数目;可以发现类的层次关系;可以聚类成其他形状
缺点:计算复杂度高;奇异值也能产生很大影响;算法很可能聚类成链状
(3) 基于密度的聚类
(4) 基于网格的聚类
(5) 基于平方误差的迭代重分配聚类
(6) 基于约束的聚类
用过哪些聚类算法,解释密度聚类算法
k-means算法,聚类性能的度量一般分为两类,一类是聚类结果与某个参考模型比较(外部指标),另外是直接考察聚类结果(内部指标)。后者通常有DB指数和DI,DB指数是对每个类,找出类内平均距离/类间中心距离最大的类,然后计算上述值,并对所有的类求和,越小越好。类似k-means的算法仅在类中数据构成簇的情况下表现较好,密度聚类算法从样本密度的角度考察样本之间的可连接性,并基于可连接样本不断扩展聚类蔟得到最终结果。DBSCAN(density-based spatial clustering of applications with noise)是一种著名的密度聚类算法,基于一组邻域参数 进行刻画,包括 邻域,核心对象(邻域内至少包含 个对象),密度直达(j由i密度直达,表示j在i的邻域内,且i是一个核心对象),密度可达(j由i密度可达,存在样本序列使得每一对都密度直达),密度相连(xi,xj存在k,i,j均有k可达),先找出样本中所有的核心对象,然后以任一核心对象作为出发点,找出由其密度可达的样本生成聚类蔟,直到所有核心对象被访问过为止。
聚类算法中的距离度量有哪些
聚类算法中的距离度量一般用闽科夫斯基距离,在p取不同的值下对应不同的距离,例如p=1的时候对应曼哈顿距离,p=2的情况下对应欧式距离,p=inf的情况下变为切比雪夫距离,还有jaccard距离,幂距离(闽科夫斯基的更一般形式),余弦相似度,加权的距离,马氏距离(类似加权)作为距离度量需要满足非负性,同一性,对称性和直递性,闽科夫斯基在p>=1的时候满足读来那个性质,对于一些离散属性例如{飞机,火车,轮船}则不能直接在属性值上计算距离,这些称为无序属性,可以用VDM(ValueDiffrence Metrix),属性u上两个离散值a,b之间的VDM距离定义为
其中表示在第i个簇中属性u上a的样本数,样本空间中不同属性的重要性不同的时候可以采用加权距离,一般如果认为所有属性重要性相同则要对特征进行归一化。一般来说距离需要的是相似性度量,距离越大,相似度越小,用于相似性度量的距离未必一定要满足距离度量的所有性质,例如直递性。比如人马和人,人马和马的距离较近,然后人和马的距离可能就很远。
解释贝叶斯公式和朴素贝叶斯分类求解方法
贝叶斯公式
最小化分类错误的贝叶斯最优分类器等价于最大化后验概率
基于贝叶斯公式来估计后验概率的主要困难在于,条件概率 是所有属性上的联合概率,难以从有限的训练样本直接估计得到。朴素贝叶斯分类器采用了属性条件独立性假设,对于已知的类别,假设所有属性相互独立。这样,朴素贝叶斯分类则定义为
如果有足够多的独立同分布样本,那么 可以根据每个类中的样本数量直接估计出来。在离散情况下先验概率可以利用样本数量估计或者离散情况下根据假设的概率密度函数进行最大似然估计。朴素贝叶斯可以用于同时包含连续变量和离散变量的情况。如果直接基于出现的次数进行估计,会出现一项为0而乘积为0的情况,所以一般会用一些平滑的方法,例如拉普拉斯修正
频率学派和贝叶斯派的本质区别
使用随机事件的发生的频率描述概率的方法,就是通常说的古典概型,或者称为频率学派。另外有一个更加综合的观点就是贝叶斯学派,在贝叶斯学派的观点下概率表示的是事件的不确定性大小。
使用概率表示不确定性,虽然不是唯一的选择,但是是必然的,因为如果想使用比较自然的感觉进行合理的综合的推断的话。在模式识别领域,对概率有一个更综合的了解将会非常有帮助。例如在多项式曲线拟合的过程中,对观察的目标变量使用频率学派的观点来理解看起来比较合适。但是我们希望确定最佳模型的参数w的不确定性情况,于是我们可以看见在贝叶斯理论中不仅可以描述参数的不确定性,实际上选择模型本身也是不确定的
优化方法(随机梯度下降、拟牛顿法等优化算法)
两种算法都是通过对数据进行参数评估,然后进行调整,找到一组最小化损失函数的参数的方法。
在标准梯度下降中,您将评估每组参数的所有训练样本。这类似于为解决这个问题而采取了大而缓慢的步骤。
在随机梯度下降中,在更新参数集之前,您只需评估1个训练样本。这类似于向解决方案迈出的小步骤。
特征比数据量还大时,选择什么样的分类器
如果训练集很小,那么高偏差/低方差分类器(如朴素贝叶斯分类器)要优于低偏差/高方差分类器(如k近邻分类器),因为后者容易过拟合。然而,随着训练集的增大,低偏差/高方差分类器将开始胜出(它们具有较低的渐近误差),因为高偏差分类器不足以提供准确的模型。你也可以认为这是生成模型与判别模型的区别。
L1和L2正则的区别,如何选择L1和L2正则?L1在0处不可导,怎么处理
他们都是可以防止过拟合,降低模型复杂度
L1是在loss function后面加上模型参数的1范数(也就是|xi|)L0范数的最小化问题在实际应用中是NP难问题,无法实际应用
L2是在loss function后面加上模型参数的2范数(也就是sigma(xi^2)),注意L2范数的定义是sqrt(sigma(xi^2)),在正则项上没有添加sqrt根号是为了更加容易优化
L1 会产生稀疏的特征
L2 会产生更多地特征但是都会接近于0
L1会趋向于产生少量的特征,而其他的特征都是0,而L2会选择更多的特征,这些特征都会接近于0。L1在特征选择时候非常有用,而L2就只是一种规则化而已。
L1对应拉普拉斯分布,L2对应高斯分布,L1偏向于参数稀疏性,L1不可导可以使用ProximalAlgorithms或者ADMM来解决
随机森林的学习过程;随机森林中的每一棵树是如何学习的;随机森林学习算法中CART树的基尼指数是什么?
随机森林由LeoBreiman(2001)提出,它通过自助法(bootstrap)重采样技术,从原始训练样本集N中有放回地重复随机抽取k个样本生成新的训练样本集合,然后根据自助样本集生成k个分类树组成随机森林,新数据的分类结果按分类树投票多少形成的分数而定。其实质是对决策树算法的一种改进,将多个决策树合并在一起,每棵树的建立依赖于一个独立抽取的样品,森林中的每棵树具有相同的分布,分类误差取决于每一棵树的分类能力和它们之间的相关性。特征选择采用随机的方法去分裂每一个节点,然后比较不同情况下产生的误差。能够检测到的内在估计误差、分类能力和相关性决定选择特征的数目。单棵树的分类能力可能很小,但在随机产生大量的决策树后,一个测试样品可以通过每一棵树的分类结果经统计后选择最可能的分类。
为什么一些机器学习模型需要对数据进行归一化?
http://blog.csdn.net/xbmatrix/article/details/56695825
归一化化就是要把你需要处理的数据经过处理后(通过某种算法)限制在你需要的一定范围内。
1)归一化后加快了梯度下降求最优解的速度。等高线变得显得圆滑,在梯度下降进行求解时能较快的收敛。如果不做归一化,梯度下降过程容易走之字,很难收敛甚至不能收敛
2)把有量纲表达式变为无量纲表达式, 有可能提高精度。一些分类器需要计算样本之间的距离(如欧氏距离),例如KNN。如果一个特征值域范围非常大,那么距离计算就主要取决于这个特征,从而与实际情况相悖(比如这时实际情况是值域范围小的特征更重要)
3) 逻辑回归等模型先验假设数据服从正态分布。
归一化的类型有线性归一化、标准差归一化、非线性归一化
归一化和标准化的区别?
归一化:
1)把数据变成(0,1)之间的小数
2)把有量纲表达式变成无量纲表达
常见的有线性转换、对数函数转换、反余切函数转换等
标准化:
数据的标准化(normalization)是将数据按比例缩放,使之落入一个小的特定区间。在某些比较和评价的指标处理中经常会用到,去除数据的单位限制,将其转化为无量纲的纯数值,便于不同单位或量级的指标能够进行比较和加权。
1 ) 最小-最大规范化(线性变换)
y=((x-MinValue) / (MaxValue-MinValue))(new_MaxValue-new_MinValue)+new_minValue
2)z-score规范化(或零-均值规范化)
y=(x-X的平均值)/X的标准差
3)小数定标规范化:通过移动X的小数位置来进行规范化
y= x/10的j次方 (其中,j使得Max(|y|) <1的最小整数
4).对数Logistic模式:
新数据=1/(1+e^(-原数据))
5)模糊量化模式
新数据=1/2+1/2sin[派3.1415/(极大值-极小值)
特征向量的缺失值处理
1. 缺失值较多.直接将该特征舍弃掉,否则可能反倒会带入较大的noise,对结果造成不良影响。
2. 缺失值较少,其余的特征缺失值都在10%以内,我们可以采取很多的方式来处理:
1) 把NaN直接作为一个特征,假设用0表示;
2) 用均值填充;
3) 用随机森林等算法预测填充
决策树的停止条件
直到每个叶子节点都只有一种类型的记录时停止,(这种方式很容易过拟合)
另一种时当叶子节点的记录树小于一定的阈值或者节点的信息增益小于一定的阈值时停止
SVM、LR、决策树的对比?
模型复杂度:SVM支持核函数,可处理线性非线性问题;LR模型简单,训练速度快,适合处理线性问题;决策树容易过拟合,需要进行剪枝
损失函数:SVM hinge loss; LR L2正则化; adaboost 指数损失
数据敏感度:SVM添加容忍度对outlier不敏感,只关心支持向量,且需要先做归一化; LR对远点敏感
数据量:数据量大就用LR,数据量小且特征少就用SVM非线性核
GBDT 和随机森林的区别?
随机森林采用的是bagging的思想,bagging又称为bootstrap aggreagation,通过在训练样本集中进行有放回的采样得到多个采样集,基于每个采样集训练出一个基学习器,再将基学习器结合。随机森林在对决策树进行bagging的基础上,在决策树的训练过程中引入了随机属性选择。传统决策树在选择划分属性的时候是在当前节点属性集合中选择最优属性,而随机森林则是对结点先随机选择包含k个属性的子集,再选择最有属性,k作为一个参数控制了随机性的引入程度。
另外,GBDT训练是基于Boosting思想,每一迭代中根据错误更新样本权重,因此是串行生成的序列化方法,而随机森林是bagging的思想,因此是并行化方法。
监督学习一般使用两种类型的目标变量
标称型和数值型
标称型:标称型目标变量的结果只在有限目标集中取值,如真与假(标称型目标变量主要用于分类)
数值型:数值型目标变量则可以从无限的数值集合中取值,如0.100,42.001等 (数值型目标变量主要用于回归分析)
为什么说朴素贝叶斯是高偏差低方差?
它简单的假设了各个特征之间是无关的,是一个被严重简化了的模型。所以,对于这样一个简单模型,大部分场合都会bias部分大于variance部分,也就是高偏差,低方差
决策树的父节点和子节点的熵的大小?请解释原因。
父节点的熵>子节点的熵
最好是在项目/实习的大数据场景里用过,比如推荐里用过 CF、LR,分类里用过 SVM、GBDT;
一般用法是什么,是不是自己实现的,有什么比较知名的实现,使用过程中踩过哪些坑;
KMeans怎么选择聚类中心?如果存在空块怎么办?
1) 选择批次距离尽可能远的K个点
首先随机选择一个点作为第一个初始类簇中心点,然后选择距离该点最远的那个点作为第二个初始类簇中心点,然后再选择距离前两个点的最近距离最大的点作为第三个初始类簇的中心点,以此类推,直至选出K个初始类簇中心点。
2) 选用层次聚类或者Canopy算法进行初始聚类,然后利用这些类簇的中心点作为KMeans算法初始类簇中心点。 常用的层次聚类算法有BIRCH和ROCK,在此不作介绍
如何确定K-mean中的值?
给定一个合适的类簇指标,比如平均半径或直径,只要我们假设的类簇的数目等于或者高于真实的类簇的数目时,该指标上升会很缓慢,而一旦试图得到少于真实数目的类簇时,该指标会急剧上升。类簇的直径是指类簇内任意两点之间的最大距离。类簇的半径是指类簇内所有点到类簇中心距离的最大值
机器学习常见优化算法比较
概率论:均匀分布如何转变为高斯分布?
由均匀分布生成标准正态分布主要有3种方法:Box–Muller算法 ,中心极限定理和Kinderman and Monahanmethod。
给你公司内部群组的聊天记录,怎样区分出主管和员工?
如何评估网站内容的真实性(针对代刷、作弊类)?
深度学习在推荐系统上可能有怎样的发挥?
路段平均车速反映了路况,在道路上布控采集车辆速度,如何对路况做出合理估计?采集数据中的异常值如何处理?
如何根据语料计算两个词词义的相似度?
在百度贴吧里发布 APP 广告,问推荐策略?
如何判断自己实现的 LR、Kmeans 算法是否正确?
100亿数字,怎么统计前100大的?
常见的聚类算法?
校正R2或者F值是用来评估线性回归模型的。那用什么来评估逻辑回归模型?
1.由于逻辑回归是用来预测概率的,我们可以用AUC-ROC曲线以及混淆矩阵来确定其性能。
2.此外,在逻辑回归中类似于校正R2的指标是AIC。AIC是对模型系数数量惩罚模型的拟合度量。因此,我们更偏爱有最小AIC的模型。
3.空偏差指的是只有截距项的模型预测的响应。数值越低,模型越好。残余偏差表示由添加自变量的模型预测的响应。数值越低,模型越好。
推荐系统:
推荐系统的实现主要分为两个方面:基于内容的实现和协同滤波的实现。
基于内容的实现:
不同人对不同电影的评分这个例子,可以看做是一个普通的回归问题,因此每部电影都需要提前提取出一个特征向量(即x值),然后针对每个用户建模,即每个用户打的分值作为y值,利用这些已有的分值y和电影特征值x就可以训练回归模型了(最常见的就是线性回归)。这样就可以预测那些用户没有评分的电影的分数。(值得注意的是需对每个用户都建立他自己的回归模型)
从另一个角度来看,也可以是先给定每个用户对某种电影的喜好程度(即权值),然后学出每部电影的特征,最后采用回归来预测那些没有被评分的电影。
当然还可以是同时优化得到每个用户对不同类型电影的热爱程度以及每部电影的特征。具体可以参考Ng在coursera上的ml教程:
基于协同滤波的实现:
协同滤波(CF)可以看做是一个分类问题,也可以看做是矩阵分解问题。协同滤波主要是基于每个人自己的喜好都类似这一特征,它不依赖于个人的基本信息。比如刚刚那个电影评分的例子中,预测那些没有被评分的电影的分数只依赖于已经打分的那些分数,并不需要去学习那些电影的特征。
SVD将矩阵分解为三个矩阵的乘积,公式如下所示:
中间的矩阵sigma为对角矩阵,对角元素的值为Data矩阵的奇异值(注意奇异值和特征值是不同的),且已经从大到小排列好了。即使去掉特征值小的那些特征,依然可以很好的重构出原始矩阵。如下图所示:
其中更深的颜色代表去掉小特征值重构时的三个矩阵。
果m代表商品的个数,n代表用户的个数,则U矩阵的每一行代表商品的属性,现在通过降维U矩阵(取深色部分)后,每一个商品的属性可以用更低的维度表示(假设为k维)。这样当新来一个用户的商品推荐向量X,则可以根据公式X’*U1*inv(S1)得到一个k维的向量,然后在V’中寻找最相似的那一个用户(相似度测量可用余弦公式等),根据这个用户的评分来推荐(主要是推荐新用户未打分的那些商品)。具体例子可以参考网页:SVD在推荐系统中的应用。另外关于SVD分解后每个矩阵的实际含义可以参考google吴军的《数学之美》一书(不过个人感觉吴军解释UV两个矩阵时好像弄反了,不知道大家怎样认为)。或者参考machine learning in action其中的svd章节。
TF-IDF是什么?
TF指Term frequecy,代表词频,IDF代表inverse document frequency,叫做逆文档频率,这个算法可以用来提取文档的关键词,首先一般认为在文章中出现次数较多的词是关键词,词频就代表了这一项,然而有些词是停用词,例如的,是,有这种大量出现的词,首先需要进行过滤,比如过滤之后再统计词频出现了中国,蜜蜂,养殖且三个词的词频几乎一致,但是中国这个词出现在其他文章的概率比其他两个词要高不少,因此我们应该认为后两个词更能表现文章的主题,IDF就代表了这样的信息,计算该值需要一个语料库,如果一个词在语料库中出现的概率越小,那么该词的IDF应该越大,一般来说TF计算公式为(某个词在文章中出现次数/文章的总词数),这样消除长文章中词出现次数多的影响,IDF计算公式为log(语料库文章总数/(包含该词的文章数)+1)。将两者乘乘起来就得到了词的TF-IDF。传统的TF-IDF对词出现的位置没有进行考虑,可以针对不同位置赋予不同的权重进行修正,注意这些修正之所以是有效的,正是因为人观测过了大量的信息,因此建议了一个先验估计,人将这个先验估计融合到了算法里面,所以使算法更加的有效。
文本中的余弦距离是什么,有哪些作用?
余弦距离是两个向量的距离的一种度量方式,其值在-1~1之间,如果为1表示两个向量同相,0表示两个向量正交,-1表示两个向量反向。使用TF-IDF和余弦距离可以寻找内容相似的文章,例如首先用TF-IDF找出两篇文章的关键词,然后每个文章分别取出k个关键词(10-20个),统计这些关键词的词频,生成两篇文章的词频向量,然后用余弦距离计算其相似度。
如何解决类别不平衡问题?
2. 处理不平衡数据集的方法
2.1.1 随机欠采样(RandomUnder-Sampling)
2.1.2 随机过采样(RandomOver-Sampling)
2.1.3 基于聚类的过采样(Cluster-BasedOver Sampling)
在这种情况下,K-均值聚类算法独立地被用于少数和多数类实例。这是为了识别数据集中的聚类。随后,每一个聚类都被过采样以至于相同类的所有聚类有着同样的实例数量,且所有的类有着相同的大小。
2.1.4 信息性过采样:合成少数类过采样技术(SMOTE)
这一技术可用来避免过拟合——当直接复制少数类实例并将其添加到主数据集时。从少数类中把一个数据子集作为一个实例取走,接着创建相似的新合成的实例。这些合成的实例接着被添加进原来的数据集。新数据集被用作样本以训练分类模型。
2.15 改进的合成少数类过采样技术(MSMOTE)
2.2 算法集成技术(AlgorithmicEnsemble Techniques)
Bagging boosting
什么是贝叶斯估计
new 和 malloc 的区别
hash冲突是指什么?怎么解决?给两种方法,写出过程和优缺点。
是否了解线性加权、bagging、boosting、cascade等模型融合方式
LDA的原理和推导
做广告点击率预测,用哪些数据什么算法
推荐系统的算法中最近邻和矩阵分解各自适用场景
用户流失率预测怎么做(游戏公司的数据挖掘都喜欢问这个)
一个游戏的设计过程中该收集什么数据
如何从登陆日志中挖掘尽可能多的信息
推荐系统的冷启动问题如何解决
是否了解A/B Test以及A/B Test结果的置信度
给你一个有1000列和1百万行的训练数据集。这个数据集是基于分类问题的。经理要求你来降低该数据集的维度以减少模型计算时间。你的机器内存有限。你会怎么做?(你可以自由做各种实际操作假设。)
1.由于我们的RAM很小,首先要关闭机器上正在运行的其他程序,包括网页浏览器,以确保大部分内存可以使用。
2.我们可以随机采样数据集。这意味着,我们可以创建一个较小的数据集,比如有1000个变量和30万行,然后做计算。
3.为了降低维度,我们可以把数值变量和分类变量分开,同时删掉相关联的变量。对于数值变量,我们将使用相关性分析。对于分类变量,我们可以用卡方检验。
4.另外,我们还可以使用PCA(主成分分析),并挑选可以解释在数据集中有最大偏差的成分。
5.利用在线学习算法,如VowpalWabbit(在Python中可用)是一个可能的选择。
6.利用StochasticGradientDescent(随机梯度下降)法建立线性模型也很有帮助。
7.我们也可以用我们对业务的理解来估计各预测变量对响应变量的影响大小。但是,这是一个主观的方法,如果没有找出有用的预测变量可能会导致信息的显著丢失。
在PCA中有必要做旋转变换吗?如果有必要,为什么?如果你没有旋转变换那些成分,会发生什么情况?
旋转(正交)是必要的,因为它把由主成分捕获的方差之间的差异最大化。这使得主成分更容易解释。
但是不要忘记我们做PCA的目的是选择更少的主成分(与特征变量个数相较而言),那些选上的主成分能够解释数据集中最大方差。通过做旋转,各主成分的相对位置不发生变化,它只能改变点的实际坐标。
如果我们没有旋转主成分,PCA的效果会减弱,那样我们会不得不选择更多个主成分来解释数据集里的方差。
给你一个数据集。这个数据集有缺失值,且这些缺失值分布在离中值有1个标准偏差的范围内。百分之多少的数据不会受到影响?为什么?
这个问题给了你足够的提示来开始思考!由于数据分布在中位数附近,让我们先假设这是一个正态分布。我们知道,在一个正态分布中,约有68%的数据位于跟平均数(或众数、中位数)1个标准差范围内的,那样剩下的约32%的数据是不受影响的。因此,约有32%的数据将不受到缺失值的影响。
给你一个癌症检测的数据集。你已经建好了分类模型,取得了96%的精度。为什么你还是不满意你的模型性能?你可以做些什么呢?
如果你分析过足够多的数据集,你应该可以判断出来癌症检测结果是不平衡数据。在不平衡数据集中,精度不应该被用来作为衡量模型的标准,因为96%(按给定的)可能只有正确预测多数分类,但我们感兴趣是那些少数分类(4%),是那些被诊断出癌症的人。
因此,为了评价模型的性能,应该用灵敏度(真阳性率),特异性(真阴性率),F值用来确定这个分类器的“聪明”程度。如果在那4%的数据上表现不好,我们可以采取以下步骤:
1.我们可以使用欠采样、过采样或SMOTE让数据平衡。
2.我们可以通过概率验证和利用AUC-ROC曲线找到最佳阀值来调整预测阀值。
3.我们可以给分类分配权重,那样较少的分类获得较大的权重。
4.我们还可以使用异常检测。
解释朴素贝叶斯算法里面的先验概率、似然估计和边际似然估计?
先验概率就是因变量(二分法)在数据集中的比例。这是在你没有任何进一步的信息的时候,是对分类能做出的最接近的猜测。例如,在一个数据集中,因变量是二进制的(1和0)。例如,1(垃圾邮件)的比例为70%和0(非垃圾邮件)的为30%。
因此,我们可以估算出任何新的电子邮件有70%的概率被归类为垃圾邮件。似然估计是在其他一些变量的给定的情况下,一个观测值被分类为1的概率。例如,“FREE”这个词在以前的垃圾邮件使用的概率就是似然估计。边际似然估计就是,“FREE”这个词在任何消息中使用的概率。
花了几个小时后,现在你急于建一个高精度的模型。结果,你建了5 个GBM (Gradient Boosted Models),想着boosting算法会显示魔力。不幸的是,没有一个模型比基准模型表现得更好。最后,你决定将这些模型结合到一起。尽管众所周知,结合模型通常精度高,但你就很不幸运。你到底错在哪里?
据我们所知,组合的学习模型是基于合并弱的学习模型来创造一个强大的学习模型的想法。但是,只有当各模型之间没有相关性的时候组合起来后才比较强大。由于我们已经试了5个 GBM,但没有提高精度,表明这些模型是相关的。具有相关性的模型的问题是,所有的模型提供相同的信息。例如:如果模型1把User1122归类为 1,模型2和模型3很有可能会做有同样分类,即使它的实际值应该是0,因此,只有弱相关的模型结合起来才会表现更好。
我知道校正R?或者F值来是用来评估线性回归模型的。那用什么来评估逻辑回归模型?
1.由于逻辑回归是用来预测概率的,我们可以用AUC-ROC曲线以及混淆矩阵来确定其性能。
2.此外,在逻辑回归中类似于校正R?的指标是AIC。AIC是对模型系数数量惩罚模型的拟合度量。因此,我们更偏爱有最小AIC的模型。
3.空偏差指的是只有截距项的模型预测的响应。数值越低,模型越好。残余偏差表示由添加自变量的模型预测的响应。数值越低,模型越好。
偏差和方差的平衡?
偏差误差在量化平均水平之上预测值跟实际值相差多远时有用。高偏差误差意味着我们的模型表现不太好,因为没有抓到重要的趋势。
而另一方面,方差量化了在同一个观察上进行的预测是如何彼此不同的。高方差模型会过度拟合你的训练集,而在训练集以外的数据上表现很差。
介绍卷积神经网络,和 DBN 有什么区别?
卷积神经网络的特点是卷积核,CNN中使用了权共享,通过不断的上采用和卷积得到不同的特征表示,采样层又称为pooling层,基于局部相关性原理进行亚采样,在减少数据量的同时保持有用的信息。DBN是深度信念网络,每一层是一个RBM,整个网络可以视为RBM堆叠得到,通常使用无监督逐层训练,从第一层开始,每一层利用上一层的输入进行训练,等各层训练结束之后再利用BP算法对整个网络进行训练。
LSTM结构推导,为什么比RNN好?
推导forget gate,input gate,cell state, hidden information等的变化;因为LSTM有进有出且当前的cell informaton是通过input gate控制之后叠加的,RNN是叠乘,因此LSTM可以防止梯度消失或者爆炸;
为什么很多做人脸的Paper会最后加入一个LocalConnected Conv?
人脸在不同的区域存在不同的特征(眼睛/鼻子/嘴的分布位置相对固定),当不存在全局的局部特征分布时,Local-Conv更适合特征的提取。
什么样的资料集不适合用深度学习?
1. 数据集太小,数据样本不足时,深度学习相对其它机器学习算法,没有明显优势。
2. 数据集没有局部相关特性,目前深度学习表现比较好的领域主要是图像/语音/自然语言处理等领域,这些领域的一个共性是局部相关性。图像中像素组成物体,语音信号中音位组合成单词,文本数据中单词组合成句子,这些特征元素的组合一旦被打乱,表示的含义同时也被改变。对于没有这样的局部相关性的数据集,不适于使用深度学习算法进行处理。举个例子:预测一个人的健康状况,相关的参数会有年龄、职业、收入、家庭状况等各种元素,将这些元素打乱,并不会影响相关的结果。
对所有优化问题来说, 有没有可能找到比現在已知算法更好的算法
没有免费的午餐定理
对于训练样本(黑点),不同的算法A/B在不同的测试样本(白点)中有不同的表现,这表示:对于一个学习算法A,若它在某些问题上比学习算法 B更好,则必然存在一些问题,在那里B比A好。
也就是说:对于所有问题,无论学习算法A多聪明,学习算法 B多笨拙,它们的期望性能相同。
但是:没有免费午餐定力假设所有问题出现几率相同,实际应用中,不同的场景,会有不同的问题分布,所以,在优化算法时,针对具体问题进行分析,是算法优化的核心所在。
用贝叶斯机率说明Dropout的原理
Dropout 是一种模型选择技术,其旨在避免在训练过程中出现过拟合现象,Dropout 的基本做法是在给定概率 p 的情况下随机移除输入数据 X 的维度。因此,探讨一下其如何影响潜在损失函数和最优化问题是有启发性的。
何为共线性, 跟过拟合有啥关联?
共线性:多变量线性回归中,变量之间由于存在高度相关关系而使回归估计不准确。
共线性会造成冗余,导致过拟合。
解决方法:排除变量的相关性/加入权重正则
说明如何用支持向量机实现深度学习
支持向量机的原理大致是由感知器得到的,可以认为是神经网络的一个特例。
广义线性模型是怎被应用在深度学习中
1. 深度学习从统计学角度,可以看做递归的广义线性模型。
2. 广义线性模型相对于经典的线性模型(y=wx+b),核心在于引入了连接函数g(.),形式变为:y=g−1(wx+b)。
3. 深度学习时递归的广义线性模型,神经元的激活函数,即为广义线性模型的链接函数。逻辑回归(广义线性模型的一种)的Logistic函数即为神经元激活函数中的Sigmoid函数,很多类似的方法在统计学和神经网络中的名称不一样,容易引起初学者(这里主要指我)的困惑。下图是一个对照表:
深度学习
什么造成梯度消失问题? 推导一下
1. 神经网络的训练中,通过改变神经元的权重,使网络的输出值尽可能逼近标签以降低误差值,训练普遍使用BP算法,核心思想是,计算出输出与标签间的损失函数值,然后计算其相对于每个神经元的梯度,进行权值的迭代。
2. 梯度消失会造成权值更新缓慢,模型训练难度增加。造成梯度消失的一个原因是,许多激活函数将输出值挤压在很小的区间内,在激活函数两端较大范围的定义域内梯度为0。造成学习停止
Weights Initialization. 不同的方式,造成的后果。为什么会造成这样的结果。
权重初始化为0,造成无法学习
lecun_uniform / glorot_normal / he_normal /batch_normal
神经网络过度拟合、规范化?
正则化、DroupOut
神经网络有哪些优化算法?
解决优化问题,有很多算法(最常见的就是梯度下降),这些算法也可以用于优化神经网络。每个深度学习库中,都包含了大量的优化算法,用于优化学习速率,让网络用最快的训练次数达到最优,还能防止过拟合。
keras中就提供了这样一些优化器[1]:
SGD:随机梯度下降
SGD+Momentum: 基于动量的SGD(在SGD基础上做过优化)
SGD+Nesterov+Momentum:基于动量,两步更新的SGD(在SGD+Momentum基础上做过优化)
Adagrad:自适应地为各个参数分配不同学习速率
Adadelta: 针对Adagrad问题,优化过的算法(在Adagrad基础上做过优化)
RMSprop:对于循环神经网络(RNNs)是最好的优化器(在Adadelta基础上做过优化)
Adam:对每个权值都计算自适应的学习速率(在RMSprop基础上做过优化)
Adamax:针对Adam做过优化的算法(在Adam基础上做过优化)
梯度爆炸
神经网络后向传播的时候导致求导的值很大,累乘之后很大,导致最后的梯度很大。为防止梯度爆炸,一种方式是设置梯度剪切阈值 gradient_clipping_threshold, 一旦梯度超过改值,直接置为该值。
如何防止梯度爆炸和梯度弥散
1. BatchNorm
BN就是通过一定的规范化手段,把每层神经网络任意神经元这个输入值的分布强行拉回到均值为0方差为1的标准正太分布而不是萝莉分布(哦,是正态分布),其实就是把越来越偏的分布强制拉回比较标准的分布,这样使得激活输入值落在非线性函数对输入比较敏感的区域,这样输入的小变化就会导致损失函数较大的变化,意思是这样让梯度变大,避免梯度消失问题产生,而且梯度变大意味着学习收敛速度快,能大大加快训练速度
2. 换一种激活函数
3. 对于不同的网络结构
LSTM把原本RNN的单元改造成一个叫做CEC的部件,这个部件保证了误差将以常数的形式在网络中流动 ,并在此基础上添加输入门和输出门使得模型变成非线性的,并可以调整不同时序的输出对模型后续动作的影响。
传统的神经网络层数一多,就会有梯度消逝和爆炸的现象,究其原因上一个答案已经说的很清楚了,导数的链式法则导致了连乘的形式。造成梯度指数级的消失,lstm使用cec机制,使得远处的梯度传到近处没有改变、但这样又会造成input weight /output weight conflict。所以又使用了gateunit来解决。
神经网络损失函数?
输出具体的类别标签时:
使用sigmod激活函数时
1. 平方误差
2. 交叉熵
使用softmax激活函数时
用log-likehood
在激活函数使用sigmoid的前提之下,相比于quadratic cost function, cross entropy costfunction具有收敛速度快和更容易获得全局最优(至于为什么更容易获得全局最优,个人感觉有点类似于动量的物理意义,增加收敛的步长,加快收敛的速度,更容易跳过局部最优)的特点。
因为我们一般使用随机值来初始化权重,这就可能导致一部分期望值和预测值相差甚远。所以选择sigmoid作为激活函数的时候,推荐使用cross entropy。如果激活函数不是sigmoid,quadratic cost function就不会存在收敛速度慢的问题。
对于分类问题,如果希望输出是类别的概率,那么激活函数选择使用softmax,同时使用log-likelihood作为损失函数。
Dropout 怎么做,有什么用处,解释
可以通过阻止某些特征的协同作用来缓解。在每次训练的时候,每个神经元有百分之50的几率被移除,这样可以让一个神经元的出现不应该依赖于另外一个神经元。另外,我们可以把dropout理解为 模型平均。假设我们要实现一个图片分类任务,我们设计出了100000个网络,这100000个网络,我们可以设计得各不相同,然后我们对这100000个网络进行训练,训练完后我们采用平均的方法,进行预测,这样肯定可以提高网络的泛化能力,或者说可以防止过拟合,因为这100000个网络,它们各不相同,可以提高网络的稳定性。而所谓的dropout我们可以这么理解,这n个网络,它们权值共享,并且具有相同的网络层数(这样可以大大减小计算量)。我们每次dropout后,网络模型都可以看成是整个网络的子网络。(需要注意的是如果采用dropout,训练时间大大延长,但是对测试阶段没影响)。
Dropout说的简单一点就是我们让在前向传导的时候,让某个神经元的激活值以一定的概率p,让其停止工作
Dropout 单元的数理?
Bathsize的影响?
神经网络交叉验证?
神经网络归一化
数值问题
不归一化容易引起数值问题
求解需要
在训练前我们将数据归一化,说明数据归是为了更方便的求解。
CNN参数调节
学习率
学习率是指在优化算法中更新网络权重的幅度大小。学习率可以是恒定的、逐渐降低的、基于动量的或者是自适应的,采用哪种学习率取决于所选择优化算法的类型,如SGD、Adam、Adagrad、AdaDelta或RMSProp等算法。优化策略这方面的内容可参阅量子位之前编译过的“一文看懂各种神经网络优化算法:从梯度下降到Adam方法”。
迭代次数
迭代次数是指整个训练集输入到神经网络进行训练的次数。当测试错误率和训练错误率相差较小时,可认为当前的迭代次数是合适的,否则需继续增大迭代次数,或调整网络结构。
批次大小
在卷积神经网络的学习过程中,小批次会表现得更好,选取范围一般位于区间[16,128]内。
还需要注意的是,CNN网络对批次大小的调整十分敏感。
激活函数
激活函数具有非线性,理论上可以使模型拟合出任何函数。通常情况下,rectifier函数在CNN网络中的效果较好。当然,可以根据实际任务,选择其他类型的激活函数,如Sigmoid和Tanh等等。
隐含层的数目和单元数
增加隐含层数目以加深网络深度,会在一定程度上改善网络性能,但是当测试错误率不再下降时,就需要寻求其他的改良方法。增加隐含层数目也带来一个问题,即提高了训练该网络的计算成本。
当网络的单元数设置过少时,可能会导致欠拟合,而单元数设置过多时,只要采取合适的正则化方式,就不会产生不良影响。
权重初始化
在网络中,通常会使用小随机数来初始化各网络层的权重,以防止产生不活跃的神经元,但是设置过小的随机数可能生成零梯度网络。一般来说,均匀分布方法效果较好。
Dropout方法
作为一种常用的正则化方式,加入Dropout层可以减弱深层神经网络的过拟合效应。该方法会按照所设定的概率参数,在每次训练中随机地不激活一定比例的神经单元。该参数的默认值为0.5。
手动调整超参数是十分费时也不切实际。接下来介绍两种搜索最优超参数的常用方法。
网格搜索和随机搜索
网格搜索是通过穷举法列出不同的参数组合,确定性能最优的结构。随机搜索是从具有特定分布的参数空间中抽取出一定数量的候选组合。
网格搜索方法也需要制定策略,在初始阶段最好先确定各超参数值的大概范围。可以先尝试在较小迭代次数或较小规模的训练集上进行大步幅的网格搜索。然后在下个阶段中,设置更大的迭代次数,或是使用整个训练集,实现小幅精确定位。
虽然在许多机器学习算法中,通常会使用网格搜索来确定超参数组合,但是随着参数量的增大,训练网络所需的计算量呈指数型增长,这种方法在深层神经网络的超参数调整时效果并不是很好。
有研究指出,在深度神经网络的超参数调整中,随机搜索方法比网格搜索的效率更高,具体可参考文末中的“随机搜索在超参数优化中的应用”。
当然,可根据神经网络的理论经验,进行超参数的手动调整在一些场景下也是可行的。
可视化
我们可以通过可视化各个卷积层,来更好地了解CNN网络是如何学习输入图像的特征。
可视化有两种直接方式,分别是可视化激活程度和可视化相关权重。在网络训练过程中,卷积层的激活情况通常会变得更为稀疏和具有局部特性。当不同输入图像的激活图都存在大片未激活的区域,那么可能是设置了过高的学习率使得卷积核不起作用,导致产生零激活图像。
性能优良的神经网络通常含有多个明显而平滑的卷积器,且没有任何干扰特征。若在权重中观察到相关干扰特征,可能原因是网络未被充分训练,或是正则化强度较低导致了过拟合效
CNN池化层的作用?
1. 不变性,旋转位移不变性,更关注是否存在某些特征而不是特征具体的位置。可以看作加了一个很强的先验,让学到的特征要能容忍一些的变化。
2. 减小下一层输入大小,减小计算量和参数个数。
3. 获得定长输出。(文本分类的时候输入是不定长的,可以通过池化获得定长输出)
4. 防止过拟合或有可能会带来欠拟合。保留主要的特征同时减少参数(降维,效果类似PCA)和计算量,防止过拟合,提高模型泛化能力
神经网络层数的影响
有文章研究过层数的影响,He在2015年的研究表明,如果神经网络的层数很深,那么可以减少卷积核心得数量,也是可以的。更深的层,抽象能力会更好,但是按照普通的来说,五个卷积层已经足够使用了,已经可以学习到很好的图像表达了,在卷积神经网络中,也分析了每一个卷积层表达的具体抽象。说明了Alexnet当中其实已经学习到了蛮不错的表达。
神经网络局部最优
局部最优其实不是神经网络的问题,在一个非常高维的空间中做梯度下降,这时的localminimum是很难形成的,因为局部最小值要求函数在所有维度上都是局部最小的。实际情况是,函数会落在一个saddle-point上。
在saddle-point上会有一大片很平坦的平原,让梯度几乎为0,导致无法继续下降。
但是saddle-point并不是一个局部极小值点,因为它还是有可以下降的方向,只不过现在这些优化算法都很难去找到这个方向罢了。
神经网络为什么要激活
卷积神将网络中,反向传播在池化层和卷积乘如何实现?
要套用DNN的反向传播算法到CNN,有几个问题需要解决:
1)池化层没有激活函数,这个问题倒比较好解决,我们可以令池化层的激活函数为σ(z)=z,即激活后就是自己本身。这样池化层激活函数的导数为1.
2)池化层在前向传播的时候,对输入进行了压缩,那么我们现在需要向前反向推导δl−1,这个推导方法和DNN完全不同。
3) 卷积层是通过张量卷积,或者说若干个矩阵卷积求和而得的当前层的输出,这和DNN很不相同,DNN的全连接层是直接进行矩阵乘法得到当前层的输出。这样在卷积层反向传播的时候,上一层的δl−1递推计算方法肯定有所不同。
4)对于卷积层,由于W使用的运算是卷积,那么从δl推导出该层的所有卷积核的W,b的方式也不同。
从上面可以看出,问题1比较好解决,但是问题2,3,4就需要好好的动一番脑筋了,而问题2,3,4也是解决CNN反向传播算法的关键所在。另外大家要注意到的是,DNN中的al,zl都只是一个向量,而我们CNN中的al,zl都是一个张量,这个张量是三维的,即由若干个输入的子矩阵组成。
下面我们就针对问题2,3,4来一步步研究CNN的反向传播算法。
在研究过程中,需要注意的是,由于卷积层可以有多个卷积核,各个卷积核的处理方法是完全相同且独立的,为了简化算法公式的复杂度,我们下面提到卷积核都是卷积层中若干卷积核中的一个。
已知池化层的δl,推导上一隐藏层的δl−1
已知卷积层的δl,推导上一隐藏层的δl−1
已知卷积层的δl,推导该层的W,b的梯度
Tensorflow如何看模型评估
可以使用tensorboard来看一系列的过程,模型的评估主要有几个指标:平均准确率、识别的时间、loss下降变化等
直方图均衡化
采用累积分布函数对像素进行变换
数据结构
谈谈内存泄露的避免方法?
在类的构造函数与析构函数中没有匹配地调用 new/delete!
没有正确地清除嵌套的对象指针
在释放对象数组时,没有使用delete [];
指向由指向对象的指针构成的数组不等同于与对象数组。
缺少拷贝构造函数
缺少重载赋值运算符
关于nonmodifying运算符重载的常见错误
没用将基类的析构函数定义成虚函数
类的大小计算。C语言中变量存储为什么要内存对齐?
为了有助于加快计算机的取数速度,编译器默认会对结构体进行处理(实际上其它地方的数据变量也是如此),让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,让宽度为4的基数据类型(int等)都位于能被4整除的地址上,以此类推。这样,两个数中间就可能需要加入填充字节,所以整个结构体的sizeof值就增长了。
C++中的字节
32位:
char :1个字节
char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节
unsigned long: 4个字节
64位:
char :1个字节
char*(即指针变量): 8个字节
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 8个字节
long long: 8个字节
unsigned long: 8个字节
STL相关的问题:vector增长;map是如何实现的?(红黑树)红黑树是什么?有什么性质?红黑树与AVL树的区别?
红黑树(Red Black Tree) 是一种自平衡二叉查找树。红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(logn)时间内做查找,插入和删除,这里的n 是树中元素的数目。红黑树的统计性能比较好,红黑树是牺牲了严格的高度平衡的优越条件为代价红黑树能够以O(log2 n)的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不平衡都会在三次旋转之内解决。
在函数传参时候正常对象,指针和引用分别如何使用,并简述他们之间的区别。
传递指针时,在函数内只能改变指针指向的区域内的值,而传递指针引用时,还可以改变指针所指的区域,即原指针的值。例如指针q, 传递指针时,只能改变(*q)的值,而不能改变q的值,传引用时,可以将q的值改变!
main函数当中变量,全局变量还有宏,他们的执行顺序是什么样子的?
第一步是到全局变量的申明上,然后到类的构造函数,最后到main函数中
C++的三个特性,如何理解多态?虚函数是如何实现的?
多态性可以简单概括为“一个接口,多种行为”。
多态分为两种:
(1)编译时多态:主要通过函数的重载和模板来实现。
(2)运行时多态:主要通过虚函数来实现。
二叉镜像树
C++的多继承的缺点
优点:对象可以调用多个基类中的接口。
缺点:易产生二义性和钻石型继承问题。钻石型继承树(DOD:DiamondOfDeath)
操作系统
线程的基本概念、线程的基本状态及状态之间的关系
线程,即轻量级进程(LWP:Light WeightProcess),是程序执行流的最小单元。一个标准的线程由线程ID、当前指令指针(PC),寄存器集合和堆栈组成。线程是进程中的一个实体,是被系统独立调度和分派的基本单位。线程不拥有系统资源,近拥有少量运行必须的资源
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
基本状态:就绪、阻塞和运行三种基本状态。
就绪状态,指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;
运行状态,指线程占有处理机正在运行;
阻塞状态,指线程在等待一个事件(如信号量),逻辑上不可执行
并发原理
多个线程或进程”同时”运行只是我们感官上的一种表现。事实上进程和线程是并发运行的,OS的线程调度机制将时间划分为很多时间片段(时间片),尽可能均匀分配给正在运行的程序,获取CPU时间片的线程或进程得以被执行,其他则等待。而CPU则在这些进程或线程上来回切换运行。微观上所有进程和线程是走走停停的,宏观上都在运行,这种都运行的现象叫并发,但是不是绝对意义上的“同时发生。
进程有哪几种状态?
就绪状态:进程已获得除处理机以外的所需资源,等待分配处理机资源
运行状态:占用处理机资源运行,处于此状态的进程数小于等于CPU数
阻塞状态: 进程等待某种条件,在条件满足之前无法执行
操作系统中进程调度策略有哪几种
FCFS(先来先服务),优先级,时间片轮转,多级反馈
进程和线程的区别有哪些?
定义方面:进程是程序在某个数据集合上的一次运行活动;线程是进程中的一个执行路径。角色方面:在支持线程机制的系统中,进程是系统资源分配的单位,线程是系统调度的单位。资源共享方面:进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。同时线程还有自己的栈和栈指针,程序计数器等寄存器。
独立性方面:进程有自己独立的地址空间,而线程没有,线程必须依赖于进程而存在。
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程
进程间通信
管道:它传递数据是单向性的,只能从一方流向另一方,也就是一种半双工的通信方式;只用于有亲缘关系的进程间的通信,亲缘关系也就是父子进程或兄弟进程;没有名字并且大小受限,传输的是无格式的流,所以两进程通 信时必须约定好数据通信的格式。管道它就像一个特殊的文件,但这个文件之存在于内存中,在创建管道时,系统为管道分配了一个页面作为数据缓冲区,进程对这个数据缓冲区进行读写,以此来完成通信。其中一个进
程只能读一个只能写,所以叫半双工通信,为什么一个只能读一个只能写呢?因为写进程是在缓冲区的末尾写入,读进程是在缓冲区的头部读取,他们各自 的数据结构不同,所以功能不同。
有名管道:看见这个名字就能知道个大概了,它于管道的不同的是它有名字了。这就不同与管道只能在具有亲缘关系的进程间通信了。它提供了一个路径名与之关联,有了自己的传输格式。有名管道和管道的不同之处还有一点是,有名管道是个设备文件,存储在文件系统中,没有亲缘关系的进程也可以访问,但是它要按照先进先出的原则读取数据。同样也是单双工的。
共享内存:就是分配一块能被其他进程访问的内存。共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。首先说下在使用共享内存区前,必须通过系统函数将其附加到进程的地址空间或说为映射到进程空间。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到
进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就
解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。
信号:信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。信号事件的发生有两个来源:硬件来源(比如我们按下了键盘或者其它硬件故障);软件来源。信号分为可靠信号和不可靠信号,实时信号和非实时信号。进程有三种方式响应信号1.忽略信号2.捕捉信号3.执行缺省操作。
信号量:也可以说是一个计数器,常用来处理进程或线程同步的问题,特别是对临界资源的访问同步问题。临界资源:为某一时刻只能由一个进程或线程操作的资源,当信号量的值大于或等于0时,表示可以供并发进程访问的临界资源数,当小于0时,表示正在等待使用临界资源的进程数。更重要的是,信号量的值仅能由PV操作来改变。
套接字:就是socket通信,upd或tcp。
线程间通信
互斥锁和读写锁:提供对临界资源的保护,当多线程试图访问临界资源时,都必须通过获取锁的方式来访问临界资源。(临界资源:是被多线程共享的资源)当读写线程获取锁的频率差别不大时,一般采用互斥锁,如果读线程访问临界资源的频率大于写线程,这个时候采用读写锁较为合适,读写锁允许多个读线程同时访问临界资源,读写线程必须互斥访问临界资源。读写锁的实现采用了互斥锁,所以在读写次数差不多的情况下采用读写锁性能没有直接采用互斥锁来的高。
信号量:提供对临界资源的安全分配。如果存在多份临界资源,在多个线程争抢临界资源的情况下,向线程提供安全分配临界资源的方法。如果临界资源的数量为1,将退化为锁。
条件变量:提供线程之间的一种通知机制,当某一条件满足时,线程A可以通知阻塞在条件变量上的线程B,B所期望的条件已经满足,可以解除在条件变量上的阻塞操作,继续做其他事情。
信号机制(Signal):类似进程间的信号处理。线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。
令牌:一种高级的线程同步的方法。它既提供锁的安全访问临界资源的功能,又利用了条件变量使得线程争夺临界资源时是有序的。
进程同步的方式
原子操作、信号量、管程、会合
原子操作:谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch 换到另一个线程
信号量:信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关
管程:信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难。因此后来又提出了一种集中式同步进程——管程。其基本思想是将共享变量和对它们的操作集中在一个模块中,操作系统或并发程序就由这样的模块构成。这样模块之间联系清晰,便于维护和修改,易于保证正确性
会合:适合分布式系统的同步机制有通信顺序进程,会合,分布式进程,远程过程调用等,这里由于篇幅则只介绍会合。当一个任务调用另一个任务的入口,而且被调用者已经准备好接收这个调用时,便发生会合
线程同步的方式
临界区 互斥量 信号量 事件
临界区(Critical Section)(同一个进程内,实现互斥)
保证在某一时刻只有一个线程能访问数据的简便办法。在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。
互斥量:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。
信号量:它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。
事件(信号):通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作。
什么是死锁?死锁产生的条件?
在两个或者多个并发进程中,如果每个进程持有某种资源而又等待其它进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。通俗的讲就是两个或多个进程无限期的阻塞、相互等待的一种状态。
死锁产生的四个条件(有一个条件不成立,则不会产生死锁)
互斥条件:一个资源一次只能被一个进程使用
请求与保持条件:一个进程因请求资源而阻塞时,对已获得资源保持不放
不剥夺条件:进程获得的资源,在未完全使用完之前,不能强行剥夺
循环等待条件:若干进程之间形成一种头尾相接的环形等待资源关系
如何防止线程死锁?
所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
加锁顺序(线程按照一定的顺序加锁)
加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
死锁检测
死锁的处理基本策略和常用方法
解决死锁的基本方法如下:
预防死锁、避免死锁、检测死锁、解除死锁
解决四多的常用策略如下:
鸵鸟策略、预防策略、避免策略、检测与解除死锁
多线程有几种实现方法
Java多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的
多线程同步和互斥有何异同,在什么情况下分别使用他们?举例说明
线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。
线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步(下文统称为同步)。
线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。
当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团
进程的特性
并发:进程执行时间断性的,执行速度是不可预测的;
共享:进程/线程之间的制约性;
不确定性:进程执行的结果和执行的相对速度有关,所以是不确定的;
什么是缓冲区溢出?有什么危害?其原因是什么?
缓冲区溢出是指当计算机向缓冲区填充数据时超出了缓冲区本身的容量,溢出的数据覆盖在合法数据上。
危害有以下两点:
程序崩溃,导致拒绝额服务
跳转并且执行一段恶意代码
造成缓冲区溢出的主要原因是程序中没有仔细检查用户输入。
进程有哪几种状态?
就绪状态:进程已获得除处理机以外的所需资源,等待分配处理机资源
运行状态:占用处理机资源运行,处于此状态的进程数小于等于CPU数
阻塞状态: 进程等待某种条件,在条件满足之前无法执行
分页和分段有什么区别?
段是信息的逻辑单位,它是根据用户的需要划分的,能够更好满足用户的需要,因此段对用户是可见的;页是信息的物理单位,是为了管理主存的方便而划分的,对用户是透明的。
段的大小不固定,有它所完成的功能决定;页大大小固定,由系统决定
分页的作业地址空间是一维的,即单一的线性地址空间。分段的作业地址空间是二维的 在标识一个地址时,即需给出段名,又需给出段内地址
段是信息的逻辑单位,便于存储保护和信息的共享,页的保护和共享受到限制。
操作系统中进程调度策略有哪几种?
FCFS(先来先服务),优先级,时间片轮转,多级反馈
计算机网络
TCP和UDP哪些不同?
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
编程语言
Python垃圾回收机制
Python GC主要使用引用计数(referencecounting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generationcollection)以空间换时间的方法提高垃圾回收效率。
Python 内存管理机制
第0层是操作系统提供的内存管理接口,如malloc、free
第1层是Python基于第0层操作系统的内存管理接口包装而成的,主要是为了处理与平台相关的内存分配行为。
第2层 以PyObje_为前缀的函数族,主要提供创建Python对象的接口。包括了gc内存管理机制
第3层 对象缓冲池机制
Python引入了内存池机制, 用于管理对小块内存的申请和释放
这两个参数是什么意思:*args,**kwargs?我们为什么要使用它们?
答:如果我们不确定往一个函数中传入多少参数,或者我们希望以元组(tuple)或者列表(list)的形式传参数的时候,我们可以使用*args(单星号)。如果我们不知道往函数中传递多少个关键词参数或者想传入字典的值作为关键词参数的时候我们可以使用**kwargs(双星号),args、kwargs两个标识符是约定俗成的用法。
谈一谈Python的装饰器(decorator)
装饰器本质上是一个Python函数,它可以让其它函数在不作任何变动的情况下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景。比如:插入日志、性能测试、事务处理、缓存、权限校验等。有了装饰器我们就可以抽离出大量的与函数功能无关的雷同代码进行重用。
Python多线程(multi-threading)。这是个好主意吗?
Python并不支持真正意义上的多线程,Python提供了多线程包。Python中有一个叫Global Interpreter Lock(GIL)的东西,它能确保你的代码中永远只有一个线程在执行。经过GIL的处理,会增加执行的开销。这就意味着如果你先要提高代码执行效率,使用threading不是一个明智的选择,当然如果你的代码是IO密集型,多线程可以明显提高效率,相反如果你的代码是CPU密集型的这种情况下多线程大部分是鸡肋。
说明os sys 模块不同,并列举常用的模块方法?
os模板提供了一种方便的使用操作系统函数的方法
sys模板可供访问由解释器使用或维护的变量和与解释器交互的函数
或者:
os模块负责程序与操作系统的交互,提供了访问操作系统底层的接口。sys模块负责程序与Python解释器的交互,提供了一系列的函数和变量用户操作Python运行时的环境。
什么是lambda表达式?它有什么好处?
简单来说,lambda表达式通常是当你需要使用一个函数,但是又不想费脑袋去命名一个函数的时候使用,也就是通常所说的匿名函数。
Python里面如何拷贝一个对象?
Python中对象之间的赋值是按引用传递的,如果要拷贝对象需要使用标准模板中的copy
copy.copy:浅拷贝,只拷贝父对象,不拷贝父对象的子对象。
copy.deepcopy:深拷贝,拷贝父对象和子对象。
__new__和__init__的区别
__init__为初始化方法,__new__方法是真正的构造函数。
__new__是实例创建之前被调用,它的任务是创建并返回该实例,是静态方法
__init__是实例创建之后被调用的,然后设置对象属性的一些初始值。
Python中单下划线和双下划线分别是什么?
__name__:一种约定,Python内部的名字,用来与用户自定义的名字区分开,防止冲突
_name:一种约定,用来指定变量私有
__name:解释器用_classname__name来代替这个名字用以区别和其他类相同的命名
说一说Python自省
自省就是面向对象的语言所写的程序在运行时,所能知道对象的类型。简单一句话就是运行时能够获得对象的类型。比如:
处理海量数据问题之六把密匙
- 分而治之/hash映射 + hash统计 + 堆/快速/归并排序;
- 双层桶划分 —第k大,中位数,不重复或重复的数字
- Bloom filter/Bitmap –实现数据字典,进行数据的判重,或者集合求交集
- Trie树/数据库/倒排索引
- 外排序
- 分布式处理之Hadoop/Mapreduce
密匙一、分而治之/Hash映射 +Hash_map统计 + 堆/快速/归并排序
1、海量日志数据,提取出某日访问百度次数最多的那个IP。
既然是海量数据处理,那么可想而知,给我们的数据那就一定是海量的。针对这个数据的海量,我们如何着手呢?对的,无非就是分而治之/hash映射 + hash统计 + 堆/快速/归并排序,说白了,就是先映射,而后统计,最后排序:
- 分而治之/hash映射:针对数据太大,内存受限,只能是:把大文件化成(取模映射)小文件,即16字方针:大而化小,各个击破,缩小规模,逐个解决
- hash_map统计:当大文件转化了小文件,那么我们便可以采用常规的hash_map(ip,value)来进行频率统计。
- 堆/快速排序:统计完了之后,便进行排序(可采取堆排序),得到次数最多的IP。
具体而论,则是: “首先是这一天,并且是访问百度的日志中的IP取出来,逐个写入到一个大文件中。注意到IP是32位的,最多有个2^32个IP。同样可以采用映射的方法,比如%1000,把整个大文件映射为1000个小文件,再找出每个小文中出现频率最大的IP(可以采用hash_map对那1000个文件中的所有IP进行频率统计,然后依次找出各个文件中频率最大的那个IP)及相应的频率。然后再在这1000个最大的IP中,找出那个频率最大的IP,即为所求。”–十道海量数据处理面试题与十个方法大总结。
关于本题,还有几个问题,如下:
1、Hash取模是一种等价映射,不会存在同一个元素分散到不同小文件中的情况,即这里采用的是mod1000算法,那么相同的IP在hash取模后,只可能落在同一个文件中,不可能被分散的。因为如果两个IP相等,那么经过Hash(IP)之后的哈希值是相同的,将此哈希值取模(如模1000),必定仍然相等。
2、那到底什么是hash映射呢?简单来说,就是为了便于计算机在有限的内存中处理big数据,从而通过一种映射散列的方式让数据均匀分布在对应的内存位置(如大数据通过取余的方式映射成小树存放在内存中,或大文件映射成多个小文件),而这个映射散列方式便是我们通常所说的hash函数,设计的好的hash函数能让数据均匀分布而减少冲突。尽管数据映射到了另外一些不同的位置,但数据还是原来的数据,只是代替和表示这些原始数据的形式发生了变化而已。
OK,有兴趣的,还可以再了解下一致性hash算法,见blog内此文第五部分:http://blog.csdn.net/v_july_v/article/details/6879101。
2、寻找热门查询,300万个查询字符串中统计最热门的10个查询
原题:搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录(这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门),请你统计最热门的10个查询串,要求使用的内存不能超过1G。
解答:由上面第1题,我们知道,数据大则划为小的,如如一亿个Ip求Top 10,可先%1000将ip分到1000个小文件中去,并保证一种ip只出现在一个文件中,再对每个小文件中的ip进行hashmap计数统计并按数量排序,最后归并或者最小堆依次处理每个小文件的top10以得到最后的结。
但如果数据规模比较小,能一次性装入内存呢?比如这第2题,虽然有一千万个Query,但是由于重复度比较高,因此事实上只有300万的Query,每个Query255Byte,因此我们可以考虑把他们都放进内存中去(300万个字符串假设没有重复,都是最大长度,那么最多占用内存3M*1K/4=0.75G。所以可以将所有字符串都存放在内存中进行处理),而现在只是需要一个合适的数据结构,在这里,HashTable绝对是我们优先的选择。
所以我们放弃分而治之/hash映射的步骤,直接上hash统计,然后排序。So,针对此类典型的TOP K问题,采取的对策往往是:hashmap + 堆。如下所示:
- hash_map统计:先对这批海量数据预处理。具体方法是:维护一个Key为Query字串,Value为该Query出现次数的HashTable,即hash_map(Query,Value),每次读取一个Query,如果该字串不在Table中,那么加入该字串,并且将Value值设为1;如果该字串在Table中,那么将该字串的计数加一即可。最终我们在O(N)的时间复杂度内用Hash表完成了统计;
- 堆排序:第二步、借助堆这个数据结构,找出Top K,时间复杂度为N‘logK。即借助堆结构,我们可以在log量级的时间内查找和调整/移动。因此,维护一个K(该题目中是10)大小的小根堆,然后遍历300万的Query,分别和根元素进行对比。所以,我们最终的时间复杂度是:O(N) + N’ * O(logK),(N为1000万,N’为300万)。
别忘了这篇文章中所述的堆排序思路:“维护k个元素的最小堆,即用容量为k的最小堆存储最先遍历到的k个数,并假设它们即是最大的k个数,建堆费时O(k),并调整堆(费时O(logk))后,有k1>k2>…kmin(kmin设为小顶堆中最小元素)。继续遍历数列,每次遍历一个元素x,与堆顶元素比较,若x>kmin,则更新堆(x入堆,用时logk),否则不更新堆。这样下来,总费时O(k*logk+(n-k)*logk)=O(n*logk)。此方法得益于在堆中,查找等各项操作时间复杂度均为logk。”–第三章续、Top K算法问题的实现。
当然,你也可以采用trie树,关键字域存该查询串出现的次数,没有出现为0。最后用10个元素的最小推来对出现频率进行排序。
3、有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。
由上面那两个例题,分而治之+ hash统计+ 堆/快速排序这个套路,我们已经开始有了屡试不爽的感觉。下面,再拿几道再多多验证下。请看此第3题:又是文件很大,又是内存受限,咋办?还能怎么办呢?无非还是:
- 分而治之/hash映射:顺序读文件中,对于每个词x,取hash(x)%5000,然后按照该值存到5000个小文件(记为x0,x1,…x4999)中。这样每个文件大概是200k左右。如果其中的有的文件超过了1M大小,还可以按照类似的方法继续往下分,直到分解得到的小文件的大小都不超过1M。
- hash_map统计:对每个小文件,采用trie树/hash_map等统计每个文件中出现的词以及相应的频率。
- 堆/归并排序:取出出现频率最大的100个词(可以用含100个结点的最小堆)后,再把100个词及相应的频率存入文件,这样又得到了5000个文件。最后就是把这5000个文件进行归并(类似于归并排序)的过程了。
4、海量数据分布在100台电脑中,想个办法高效统计出这批数据的TOP10。
如果每个数据元素只出现一次,而且只出现在某一台机器中,那么可以采取以下步骤统计出现次数TOP10的数据元素:
- 堆排序:在每台电脑上求出TOP10,可以采用包含10个元素的堆完成(TOP10小,用最大堆,TOP10大,用最小堆,比如求TOP10大,我们首先取前10个元素调整成最小堆,如果发现,然后扫描后面的数据,并与堆顶元素比较,如果比堆顶元素大,那么用该元素替换堆顶,然后再调整为最小堆。最后堆中的元素就是TOP10大)。
- 求出每台电脑上的TOP10后,然后把这100台电脑上的TOP10组合起来,共1000个数据,再利用上面类似的方法求出TOP10就可以了。
但如果同一个元素重复出现在不同的电脑中呢,如下例子所述: 这个时候,你可以有两种方法:
- 遍历一遍所有数据,重新hash取摸,如此使得同一个元素只出现在单独的一台电脑中,然后采用上面所说的方法,统计每台电脑中各个元素的出现次数找出TOP10,继而组合100台电脑上的TOP10,找出最终的TOP10。
- 或者,暴力求解:直接统计统计每台电脑中各个元素的出现次数,然后把同一个元素在不同机器中的出现次数相加,最终从所有数据中找出TOP10。
5、有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。要求你按照query的频度排序。
方案1:直接上:
- hash映射:顺序读取10个文件,按照hash(query)%10的结果将query写入到另外10个文件(记为a0,a1,..a9)中。这样新生成的文件每个的大小大约也1G(假设hash函数是随机的)。
- hash_map统计:找一台内存在2G左右的机器,依次对用hash_map(query, query_count)来统计每个query出现的次数。注:hash_map(query,query_count)是用来统计每个query的出现次数,不是存储他们的值,出现一次,则count+1。
- 堆/快速/归并排序:利用快速/堆/归并排序按照出现次数进行排序,将排序好的query和对应的query_cout输出到文件中,这样得到了10个排好序的文件(记为 )。最后,对这10个文件进行归并排序(内排序与外排序相结合)。根据此方案1,这里有一份实现:https://github.com/ooooola/sortquery/blob/master/querysort.py。
除此之外,此题还有以下两个方法:
方案2:一般query的总量是有限的,只是重复的次数比较多而已,可能对于所有的query,一次性就可以加入到内存了。这样,我们就可以采用trie树/hash_map等直接来统计每个query出现的次数,然后按出现次数做快速/堆/归并排序就可以了。
方案3:与方案1类似,但在做完hash,分成多个文件后,可以交给多个文件来处理,采用分布式的架构来处理(比如MapReduce),最后再进行合并。
6、给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?
可以估计每个文件安的大小为5G×64=320G,远远大于内存限制的4G。所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法。
- 分而治之/hash映射:遍历文件a,对每个url求取 ,然后根据所取得的值将url分别存储到1000个小文件(记为 ,这里漏写个了a1)中。这样每个小文件的大约为300M。遍历文件b,采取和a相同的方式将url分别存储到1000小文件中(记为 )。这样处理后,所有可能相同的url都在对应的小文件( )中,不对应的小文件不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。
- hash_set统计:求每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。
OK,此第一种方法:分而治之/hash映射 + hash统计 + 堆/快速/归并排序,再看最后4道题,如下:
方案:先做hash,然后求模映射为小文件,求出每个小文件中重复次数最多的一个,并记录重复次数。然后找出上一步求出的数据中重复次数最多的一个就是所求(具体参考前面的题)。
8、上千万或上亿数据(有重复),统计其中出现次数最多的前N个数据。
方案:上千万或上亿的数据,现在的机器的内存应该能存下。所以考虑采用hash_map/搜索二叉树/红黑树等来进行统计次数。然后利用堆取出前N个出现次数最多的数据。
9、一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析。
方案1:如果文件比较大,无法一次性读入内存,可以采用hash取模的方法,将大文件分解为多个小文件,对于单个小文件利用hash_map统计出每个小文件中10个最常出现的词,然后再进行归并处理,找出最终的10个最常出现的词。
方案2:通过hash取模将大文件分解为多个小文件后,除了可以用hash_map统计出每个小文件中10个最常出现的词,也可以用trie树统计每个词出现的次数,时间复杂度是O(n*le)(le表示单词的平准长度),最终同样找出出现最频繁的前10个词(可用堆来实现),时间复杂度是O(n*lg10)。
10. 1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串。请怎么设计和实现?
- 方案1:这题用trie树比较合适,hash_map也行。
- 方案2:from xjbzju:,1000w的数据规模插入操作完全不现实,以前试过在stl下100w元素插入set中已经慢得不能忍受,觉得基于hash的实现不会比红黑树好太多,使用vector+sort+unique都要可行许多,建议还是先hash成小文件分开处理再综合。
上述方案2中读者xbzju的方法让我想到了一些问题,即是set/map,与hash_set/hash_map的性能比较?共计3个问题,如下:
- 1、hash_set在千万级数据下,insert操作优于set? 这位blog:http://t.cn/zOibP7t 给的实践数据可靠不?
- 2、那map和hash_map的性能比较呢? 谁做过相关实验?
- 3、那查询操作呢,如下段文字所述?
或者小数据量时用map,构造快,大数据量时用hash_map?
rbtree PKhashtable
据朋友№邦卡猫№的做的红黑树和hashtable的性能测试中发现:当数据量基本上int型key时,hash table是rbtree的3-4倍,但hash table一般会浪费大概一半内存。
因为hash table所做的运算就是个%,而rbtree要比较很多,比如rbtree要看value的数据,每个节点要多出3个指针(或者偏移量)如果需要其他功能,比如,统计某个范围内的key的数量,就需要加一个计数成员。
且1s rbtree能进行大概50w+次插入,hash table大概是差不多200w次。不过很多的时候,其速度可以忍了,例如倒排索引差不多也是这个速度,而且单线程,且倒排表的拉链长度不会太大。正因为基于树的实现其实不比hashtable慢到哪里去,所以数据库的索引一般都是用的B/B+树,而且B+树还对磁盘友好(B树能有效降低它的高度,所以减少磁盘交互次数)。比如现在非常流行的NoSQL数据库,像MongoDB也是采用的B树索引。关于B树系列,请参考本blog内此篇文章:从B树、B+树、B*树谈到R 树。更多请待后续实验论证。
11.一个文本文件,找出前10个经常出现的词,但这次文件比较长,说是上亿行或十亿行,总之无法一次读入内存,问最优解。
方案1:首先根据用hash并求模,将文件分解为多个小文件,对于单个文件利用上题的方法求出每个文件件中10个最常出现的词。然后再进行归并处理,找出最终的10个最常出现的词。
12.100w个数中找出最大的100个数。
方案1:采用局部淘汰法。选取前100个元素,并排序,记为序列L。然后一次扫描剩余的元素x,与排好序的100个元素中最小的元素比,如果比这个最小的要大,那么把这个最小的元素删除,并把x利用插入排序的思想,插入到序列L中。依次循环,知道扫描了所有的元素。复杂度为O(100w*100)。
方案2:采用快速排序的思想,每次分割之后只考虑比轴大的一部分,知道比轴大的一部分在比100多的时候,采用传统排序算法排序,取前100个。复杂度为O(100w*100)。
方案3:在前面的题中,我们已经提到了,用一个含100个元素的最小堆完成。复杂度为O(100w*lg100)。
接下来,咱们来看第二种方法,双层捅划分。
密匙二、多层划分
多层划分—-其实本质上还是分而治之的思想,重在“分”的技巧上!
适用范围:第k大,中位数,不重复或重复的数字
基本原理及要点:因为元素范围很大,不能利用直接寻址表,所以通过多次划分,逐步确定范围,然后最后在一个可以接受的范围内进行。
问题实例:
13、2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。
有点像鸽巢原理,整数个数为2^32,也就是,我们可以将这2^32个数,划分为2^8个区域(比如用单个文件代表一个区域),然后将数据分离到不同的区域,然后不同的区域在利用bitmap就可以直接解决了。也就是说只要有足够的磁盘空间,就可以很方便的解决。
14、5亿个int找它们的中位数。
- 思路一:这个例子比上面那个更明显。首先我们将int划分为2^16个区域,然后读取数据统计落到各个区域里的数的个数,之后我们根据统计结果就可以判断中位数落到那个区域,同时知道这个区域中的第几大数刚好是中位数。然后第二次扫描我们只统计落在这个区域中的那些数就可以了。
实际上,如果不是int是int64,我们可以经过3次这样的划分即可降低到可以接受的程度。即可以先将int64分成2^24个区域,然后确定区域的第几大数,在将该区域分成2^20个子区域,然后确定是子区域的第几大数,然后子区域里的数的个数只有2^20,就可以直接利用direct addr table进行统计了。 - 思路二@绿色夹克衫:同样需要做两遍统计,如果数据存在硬盘上,就需要读取2次。
方法同基数排序有些像,开一个大小为65536的Int数组,第一遍读取,统计Int32的高16位的情况,也就是0-65535,都算作0,65536 - 131071都算作1。就相当于用该数除以65536。Int32 除以 65536的结果不会超过65536种情况,因此开一个长度为65536的数组计数就可以。每读取一个数,数组中对应的计数+1,考虑有负数的情况,需要将结果加32768后,记录在相应的数组内。
第一遍统计之后,遍历数组,逐个累加统计,看中位数处于哪个区间,比如处于区间k,那么0- k-1的区间里数字的数量sum应该<n/2(2.5亿)。而k+1 - 65535的计数和也<n/2,第二遍统计同上面的方法类似,但这次只统计处于区间k的情况,也就是说(x / 65536) + 32768 = k。统计只统计低16位的情况。并且利用刚才统计的sum,比如sum = 2.49亿,那么现在就是要在低16位里面找100万个数(2.5亿-2.49亿)。这次计数之后,再统计一下,看中位数所处的区间,最后将高位和低位组合一下就是结果了。
3).现在有一个0-30000的随机数生成器。请根据这个随机数生成器,设计一个抽奖范围是0-350000彩票中奖号码列表,其中要包含20000个中奖号码。
这个题刚好和上面两个思想相反,一个0到3万的随机数生成器要生成一个0到35万的随机数。那么我们完全可以将0-35万的区间分成35/3=12个区间,然后每个区间的长度都小于等于3万,这样我们就可以用题目给的随机数生成器来生成了,然后再加上该区间的基数。那么要每个区间生成多少个随机数呢?计算公式就是:区间长度*随机数密度,在本题目中就是30000*(20000/350000)。最后要注意一点,该题目是有隐含条件的:彩票,这意味着你生成的随机数里面不能有重复,这也是我为什么用双层桶划分思想的另外一个原因
密匙三:Bloomfilter/Bitmap
Bloom filter
关于什么是Bloomfilter,请参看blog内此文:
- 海量数据处理之Bloom Filter详解
适用范围:可以用来实现数据字典,进行数据的判重,或者集合求交集
基本原理及要点:
对于原理来说很简单,位数组+k个独立hash函数。将hash函数对应的值的位数组置1,查找时如果发现所有hash函数对应位都是1说明存在,很明显这个过程并不保证查找的结果是100%正确的。同时也不支持删除一个已经插入的关键字,因为该关键字对应的位会牵动到其他的关键字。所以一个简单的改进就是 counting Bloom filter,用一个counter数组代替位数组,就可以支持删除了。
还有一个比较重要的问题,如何根据输入元素个数n,确定位数组m的大小及hash函数个数。当hash函数个数k=(ln2)*(m/n)时错误率最小。在错误率不大于E的情况下,m至少要等于n*lg(1/E)才能表示任意n个元素的集合。但m还应该更大些,因为还要保证bit数组里至少一半为0,则m应该>=nlg(1/E)*lge大概就是nlg(1/E)1.44倍(lg表示以2为底的对数)。
举个例子我们假设错误率为0.01,则此时m应大概是n的13倍。这样k大概是8个。
注意这里m与n的单位不同,m是bit为单位,而n则是以元素个数为单位(准确的说是不同元素的个数)。通常单个元素的长度都是有很多bit的。所以使用bloom filter内存上通常都是节省的。
扩展:
Bloomfilter将集合中的元素映射到位数组中,用k(k为哈希函数个数)个映射位是否全1表示元素在不在这个集合中。Countingbloom filter(CBF)将位数组中的每一位扩展为一个counter,从而支持了元素的删除操作。SpectralBloom Filter(SBF)将其与集合元素的出现次数关联。SBF采用counter中的最小值来近似表示元素的出现频率。
可以看下上文中的第6题:
“6、给你A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出A,B文件共同的URL。如果是三个乃至n个文件呢?
根据这个问题我们来计算下内存的占用,4G=2^32大概是40亿*8大概是340亿,n=50亿,如果按出错率0.01算需要的大概是650亿个bit。现在可用的是340亿,相差并不多,这样可能会使出错率上升些。另外如果这些urlip是一一对应的,就可以转换成ip,则大大简单了。
同时,上文的第5题:给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?如果允许有一定的错误率,可以使用Bloomfilter,4G内存大概可以表示340亿bit。将其中一个文件中的url使用Bloomfilter映射为这340亿bit,然后挨个读取另外一个文件的url,检查是否与Bloomfilter,如果是,那么该url应该是共同的url(注意会有一定的错误率)。”
Bitmap
- 关于什么是Bitmap,请看blog内此文第二部分:http://blog.csdn.net/v_july_v/article/details/6685962。
下面关于Bitmap的应用,可以看下上文中的第13题,以及另外一道新题:
“13、在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数。
方案1:采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)进行,共需内存2^32 * 2bit=1 GB内存,还可以接受。然后扫描这2.5亿个整数,查看Bitmap中相对应位,如果是00变01,01变10,10保持不变。所描完事后,查看bitmap,把对应位是01的整数输出即可。
方案2:也可采用与第1题类似的方法,进行划分小文件的方法。然后在小文件中找出不重复的整数,并排序。然后再进行归并,注意去除重复的元素。”
15、给40亿个不重复的unsignedint的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?
方案1:frome oo,用位图/Bitmap的方法,申请512M的内存,一个bit位代表一个unsignedint值。读入40亿个数,设置相应的bit位,读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在。
密匙四、Trie树/数据库/倒排索引
Trie树
适用范围:数据量大,重复多,但是数据种类小可以放入内存
基本原理及要点:实现方式,节点孩子的表示方式
扩展:压缩实现。
问题实例:
- 上面的第2题:寻找热门查询:查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个,每个不超过255字节。
- 上面的第5题:有10个文件,每个文件1G,每个文件的每一行都存放的是用户的query,每个文件的query都可能重复。要你按照query的频度排序。
- 1000万字符串,其中有些是相同的(重复),需要把重复的全部去掉,保留没有重复的字符串。请问怎么设计和实现?
- 上面的第8题:一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词。其解决方法是:用trie树统计每个词出现的次数,时间复杂度是O(n*le)(le表示单词的平准长度),然后是找出出现最频繁的前10个词。
更多有关Trie树的介绍,请参见此文:从Trie树(字典树)谈到后缀树。
数据库索引
适用范围:大数据量的增删改查
基本原理及要点:利用数据的设计实现方法,对海量数据的增删改查进行处理。
- 关于数据库索引及其优化,更多可参见此文:http://www.cnblogs.com/pkuoliver/archive/2011/08/17/mass-data-topic-7-index-and-optimize.html;
- 关于MySQL索引背后的数据结构及算法原理,这里还有一篇很好的文章:http://blog.codinglabs.org/articles/theory-of-mysql-index.html;
- 关于B 树、B+ 树、B* 树及R 树,本blog内有篇绝佳文章:http://blog.csdn.net/v_JULY_v/article/details/6530142。
倒排索引(Invertedindex)
适用范围:搜索引擎,关键字查询
基本原理及要点:为何叫倒排索引?一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。
以英文为例,下面是要被索引的文本:
T0 = “it is what it is”
T1 = “what is it”
T2 = “it is a banana”
我们就能得到下面的反向文件索引:
“a”: {2}
“banana”: {2}
“is”: {0, 1,2}
“it”: {0, 1, 2}
“what”: {0, 1}
检索的条件“what”,”is”和“it”将对应集合的交集。
正向索引开发出来用来存储每个文档的单词的列表。正向索引的查询往往满足每个文档有序频繁的全文查询和每个单词在校验文档中的验证这样的查询。在正向索引中,文档占据了中心的位置,每个文档指向了一个它所包含的索引项的序列。也就是说文档指向了它包含的那些单词,而反向索引则是单词指向了包含它的文档,很容易看到这个反向的关系。
扩展:
问题实例:文档检索系统,查询那些文件包含了某单词,比如常见的学术论文的关键字搜索。
关于倒排索引的应用,更多请参见:
密匙五、外排序
适用范围:大数据的排序,去重
基本原理及要点:外排序的归并方法,置换选择败者树原理,最优归并树
问题实例:
1).有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16个字节,内存限制大小是1M。返回频数最高的100个词。
这个数据具有很明显的特点,词的大小为16个字节,但是内存只有1M做hash明显不够,所以可以用来排序。内存可以当输入缓冲区使用。
关于多路归并算法及外排序的具体应用场景,请参见blog内此文:
- 第十章、如何给10^7个数据量的磁盘文件排序
密匙六、分布式处理之Mapreduce
MapReduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。这样做的好处是可以在任务被分解后,可以通过大量机器进行并行计算,减少整个操作的时间。但如果你要我再通俗点介绍,那么,说白了,Mapreduce的原理就是一个归并排序。
适用范围:数据量大,但是数据种类小可以放入内存
基本原理及要点:将数据交给不同的机器去处理,数据划分,结果归约。
问题实例:
- The canonical example application of MapReduce is a process to count the appearances of each different word in a set of documents:
- 海量数据分布在100台电脑中,想个办法高效统计出这批数据的TOP10。
- 一共有N个机器,每个机器上有N个数。每个机器最多存O(N)个数并对它们操作。如何找到N^2个数的中数(median)?
更多具体阐述请参见blog内:
- 从Hadhoop框架与MapReduce模式中谈海量数据处理,
- 及MapReduce技术的初步了解与学习。
其它模式/方法论,结合操作系统知识
至此,六种处理海量数据问题的模式/方法已经阐述完毕。据观察,这方面的面试题无外乎以上一种或其变形,然题目为何取为是:秒杀99%的海量数据处理面试题,而不是100%呢。OK,给读者看最后一道题,如下:
非常大的文件,装不进内存。每行一个int类型数据,现在要你随机取100个数。
我们发现上述这道题,无论是以上任何一种模式/方法都不好做,那有什么好的别的方法呢?我们可以看看:操作系统内存分页系统设计(说白了,就是映射+建索引)。
Windows 2000使用基于分页机制的虚拟内存。每个进程有4GB的虚拟地址空间。基于分页机制,这4GB地址空间的一些部分被映射了物理内存,一些部分映射硬盘上的交换文件,一些部分什么也没有映射。程序中使用的都是4GB地址空间中的虚拟地址。而访问物理内存,需要使用物理地址。关于什么是物理地址和虚拟地址,请看:
- 物理地址 (physical address): 放在寻址总线上的地址。放在寻址总线上,如果是读,电路根据这个地址每位的值就将相应地址的物理内存中的数据放到数据总线中传输。如果是写,电路根据这个 地址每位的值就将相应地址的物理内存中放入数据总线上的内容。物理内存是以字节(8位)为单位编址的。
- 虚拟地址 (virtual address): 4G虚拟地址空间中的地址,程序中使用的都是虚拟地址。 使用了分页机制之后,4G的地址空间被分成了固定大小的页,每一页或者被映射到物理内存,或者被映射到硬盘上的交换文件中,或者没有映射任何东西。对于一 般程序来说,4G的地址空间,只有一小部分映射了物理内存,大片大片的部分是没有映射任何东西。物理内存也被分页,来映射地址空间。对于32bit的 Win2k,页的大小是4K字节。CPU用来把虚拟地址转换成物理地址的信息存放在叫做页目录和页表的结构里。
物理内存分页,一个物理页的大小为4K字节,第0个物理页从物理地址 0x00000000 处开始。由于页的大小为4KB,就是0x1000字节,所以第1页从物理地址0x00001000 处开始。第2页从物理地址 0x00002000 处开始。可以看到由于页的大小是4KB,所以只需要32bit的地址中高20bit来寻址物理页。
返回上面我们的题目:非常大的文件,装不进内存。每行一个int类型数据,现在要你随机取100个数。针对此题,我们可以借鉴上述操作系统中内存分页的设计方法,做出如下解决方案:
操作系统中的方法,先生成4G的地址表,在把这个表划分为小的4M的小文件做个索引,二级索引。30位前十位表示第几个4M文件,后20位表示在这个4M文件的第几个,等等,基于key value来设计存储,用key来建索引。
但如果现在只有10000个数,然后怎么去随机从这一万个数里面随机取100个数?请读者思考。更多海里数据处理面试题,请参见此文第一部分:http://blog.csdn.net/v_july_v/article/details/6685962。
参考文献
- 十道海量数据处理面试题与十个方法大总结;
- 海量数据处理面试题集锦与Bit-map详解;
- 十一、从头到尾彻底解析Hash表算法;
- 海量数据处理之Bloom Filter详解;
- 从Trie树(字典树)谈到后缀树;
- 第三章续、Top K算法问题的实现;
- 第十章、如何给10^7个数据量的磁盘文件排序;
- 从B树、B+树、B*树谈到R 树;
- 第二十三、四章:杨氏矩阵查找,倒排索引关键词Hash不重复编码实践;
- 第二十六章:基于给定的文档生成倒排索引的编码与实践;
- 从Hadhoop框架与MapReduce模式中谈海量数据处理;
- 第十六~第二十章:全排列,跳台阶,奇偶排序,第一个只出现一次等问题;
- http://blog.csdn.net/v_JULY_v/article/category/774945;
- STL源码剖析第五章,侯捷著;
- 2012百度实习生招聘笔试题:http://blog.csdn.net/hackbuteer1/article/details/7542774。
后记
经过上面这么多海量数据处理面试题的轰炸,我们依然可以看出这类问题是有一定的解决方案/模式的,所以,不必将其神化。然这类面试题所包含的问题还是比较简单的,若您在这方面有更多实践经验,欢迎随时来信与我不吝分享:zhoulei0907@yahoo.cn。当然,自会注明分享者及来源。
不过,相信你也早就意识到,若单纯论海量数据处理面试题,本blog内的有关海量数据处理面试题的文章已涵盖了你能在网上所找到的70~80%。但有点,必须负责任的敬告大家:无论是这些海量数据处理面试题也好,还是算法也好,面试时,70~80%的人不是倒在这两方面,而是倒在基础之上(诸如语言,数据库,操作系统,网络协议等等),所以,无论任何时候,基础最重要,没了基础,便什么都不是。
模型融合的方法
· 通过验证(validation)的方式,从第一步中训练出的多个模型中挑选最佳的模型,作为最终的模型。这种方式必须要验证,否则很容易过拟合。
· 统一融合(Uniform blending),分类时使用一人一票的投票方式,回归时使用多个模型的平均值。这种方式的优点是一般泛化能力会得到加强,但是只能保证比那些模型中最差的模型要好,不能保证能得到比那些不同模型中的最好的模型要好
· 线性融合(Linear blending),二次学习,使用线性模型将第一步中学习到的学习器组合起来,用得好可以提高模型性能,但是要注意有过拟合的风险。
· 堆融合(Any blending、stacking),任何其它非线性模型将那些学习器组合起来,有过拟合的风险,注意验证
项目中遇到的最大的困难时什么?
项目如何做的:
首先官网是会给数据的一些说明的,有的是直接给一些特征有的,没有特征的话就要自己做了,这也是一个难点。
1.1 分析题目
拿到赛题以后,第一步就是要破题,我们需要将问题转化为相应的机器学习问题。其中,Kaggle最常见的机器学习问题类型有:
回归问题
分类问题(二分类、多分类、多标签) 多分类只需从多个类别中预测一个类别,而多标签则需要预测出多个类别。
比如Quora的比赛就是二分类问题,因为只需要判断两个问句的语义是否相似。
1.2 数据分析(Data Exploration)
所谓数据挖掘,当然是要从数据中去挖掘我们想要的东西,我们需要通过人为地去分析数据,才可以发现数据中存在的问题和特征。我们需要在观察数据的过程中思考以下几个问题:
数据应该怎么清洗和处理才是合理的?
根据数据的类型可以挖掘怎样的特征?
数据中的哪些特征会对标签的预测有帮助?
1.2.1 统计分析
对于数值类变量(Numerical Variable),我们可以得到min,max,mean,meduim,std等统计量,用pandas可以方便地完成,
通过分析可以可以观察Label是否均衡,如果不均衡则需要进行over sample少数类,或者down sample多数类。我们还可以统计Numerical Variable之间的相关系数,用pandas就可以轻松获得相关系数矩阵:
1.2.2 可视化
人是视觉动物,更容易接受图形化的表示,因此可以将一些统计信息通过图表的形式展示出来,方便我们观察和发现。比如用直方图展示问句的频数:
常用的可视化工具有matplotlib和seaborn。当然,你也可以跳过这一步,因为可视化不是解决问题的重点。
1.3 数据预处理(Data Preprocessing)
刚拿到手的数据会出现噪声,缺失,脏乱等现象,我们需要对数据进行清洗与加工,从而方便进行后续的工作。针对不同类型的变量,会有不同的清洗和处理方法:
对于数值型变量(Numerical Variable),需要处理离群点,缺失值,异常值等情况。
对于类别型变量(Categorical Variable),可以转化为one-hot编码。
文本数据是较难处理的数据类型,文本中会有垃圾字符,错别字(词),数学公式,不统一单位和日期格式等。我们还需要处理标点符号,分词,去停用词,对于英文文本可能还要词性还原(lemmatize),抽取词干(stem)等等。
1.4 特征工程(Feature Engineering)
都说特征为王,特征是决定效果最关键的一环。我们需要通过探索数据,利用人为先验知识,从数据中总结出特征。
1.4.1 特征抽取(FeatureExtraction)
我们应该尽可能多地抽取特征,只要你认为某个特征对解决问题有帮助,它就可以成为一个特征。特征抽取需要不断迭代,是最为烧脑的环节,它会在整个比赛周期折磨你,但这是比赛取胜的关键,它值得你耗费大量的时间。
那问题来了,怎么去发现特征呢?光盯着数据集肯定是不行的。如果你是新手,可以先耗费一些时间在Forum上,看看别人是怎么做Feature Extraction的,并且多思考。虽然Feature Extraction特别讲究经验,但其实还是有章可循的:
对于Numerical Variable,可以通过线性组合、多项式组合来发现新的Feature。
对于文本数据,有一些常规的Feature。比如,文本长度,Embeddings,TF-IDF,LDA,LSI等,你甚至可以用深度学习提取文本特征(隐藏层)。
如果你想对数据有更深入的了解,可以通过思考数据集的构造过程来发现一些magicfeature,这些特征有可能会大大提升效果。在Quora这次比赛中,就有人公布了一些magic feature。
通过错误分析也可以发现新的特征(见1.5.2小节)。
1.4.2 特征选择(FeatureSelection)
在做特征抽取的时候,我们是尽可能地抽取更多的Feature,但过多的Feature会造成冗余,噪声,容易过拟合等问题,因此我们需要进行特征筛选。特征选择可以加快模型的训练速度,甚至还可以提升效果。
特征选择的方法多种多样,最简单的是相关度系数(Correlationcoefficient),它主要是衡量两个变量之间的线性关系,数值在[-1.0, 1.0]区间中。数值越是接近0,两个变量越是线性不相关。但是数值为0,并不能说明两个变量不相关,只是线性不相关而已。
我们通过一个例子来学习一下怎么分析相关系数矩阵:
相关系数矩阵是一个对称矩阵,所以只需要关注矩阵的左下角或者右上角。我们可以拆成两点来看:
Feature和Label的相关度可以看作是该Feature的重要度,越接近1或-1就越好。
Feature和Feature之间的相关度要低,如果两个Feature的相关度很高,就有可能存在冗余。
除此之外,还可以训练模型来筛选特征,比如带L1或L2惩罚项的Linear Model、RandomForest、GDBT等,它们都可以输出特征的重要度。在这次比赛中,我们对上述方法都进行了尝试,将不同方法的平均重要度作为最终参考指标,筛选掉得分低的特征。
1.5 建模(Modeling)
终于来到机器学习了,在这一章,我们需要开始炼丹了。
1.5.1 模型
机器学习模型有很多,建议均作尝试,不仅可以测试效果,还可以学习各种模型的使用技巧。其实,几乎每一种模型都有回归和分类两种版本,常用模型有:
KNN
SVM
Linear Model(带惩罚项)
ExtraTree
RandomForest
Gradient Boost Tree
Neural Network
幸运的是,这些模型都已经有现成的工具(如scikit-learn、XGBoost、LightGBM等)可以使用,不用自己重复造轮子。但是我们应该要知道各个模型的原理,这样在调参的时候才会游刃有余。当然,你也使用PyTorch/Tensorflow/Keras等深度学习工具来定制自己的Deep Learning模型,玩出自己的花样。
1.5.2 错误分析
人无完人,每个模型不可能都是完美的,它总会犯一些错误。为了解某个模型在犯什么错误,我们可以观察被模型误判的样本,总结它们的共同特征,我们就可以再训练一个效果更好的模型。这种做法有点像后面Ensemble时提到的Boosting,但是我们是人为地观察错误样本,而Boosting是交给了机器。通过错误分析->发现新特征->训练新模型->错误分析,可以不断地迭代出更好的效果,并且这种方式还可以培养我们对数据的嗅觉。
举个例子,这次比赛中,我们在错误分析时发现,某些样本的两个问句表面上很相似,但是句子最后提到的地点不一样,所以其实它们是语义不相似的,但我们的模型却把它误判为相似的。比如这个样本:
Question1: Which is the best digitalmarketing institution in banglore?
Question2: Which is the best digitalmarketing institute in Pune?
为了让模型可以处理这种样本,我们将两个问句的最长公共子串(Longest CommonSequence)去掉,用剩余部分训练一个新的深度学习模型,相当于告诉模型看到这种情况的时候就不要判断为相似的了。因此,在加入这个特征后,我们的效果得到了一些提升。
1.5.3 调参
在训练模型前,我们需要预设一些参数来确定模型结构(比如树的深度)和优化过程(比如学习率),这种参数被称为超参(Hyper-parameter),不同的参数会得到的模型效果也会不同。总是说调参就像是在“炼丹”,像一门“玄学”,但是根据经验,还是可以找到一些章法的:
根据经验,选出对模型效果影响较大的超参。
按照经验设置超参的搜索空间,比如学习率的搜索空间为[0.001,0.1]。
选择搜索算法,比如Random Search、Grid Search和一些启发式搜索的方法。
验证模型的泛化能力(详见下一小节)。
1.5.4 模型验证(Validation)
在Test Data的标签未知的情况下,我们需要自己构造测试数据来验证模型的泛化能力,因此把Train Data分割成Train Set和Valid Set两部分,Train Set用于训练,Valid Set用于验证。
简单分割
将Train Data按一定方法分成两份,比如随机取其中70%的数据作为Train Set,剩下30%作为Valid Set,每次都固定地用这两份数据分别训练模型和验证模型。这种做法的缺点很明显,它没有用到整个训练数据,所以验证效果会有偏差。通常只会在训练数据很多,模型训练速度较慢的时候使用。
交叉验证
交叉验证是将整个训练数据随机分成K份,训练K个模型,每次取其中的K-1份作为TrainSet,留出1份作为Valid Set,因此也叫做K-fold。至于这个K,你想取多少都可以,但一般选在3~10之间。我们可以用K个模型得分的mean和std,来评判模型得好坏(mean体现模型的能力,std体现模型是否容易过拟合),并且用K-fold的验证结果通常会比较可靠。
如果数据出现Label不均衡情况,可以使用Stratified K-fold,这样得到的Train Set和Test Set的Label比例是大致相同。
1.6 模型集成(Ensemble)
曾经听过一句话,”Feature为主,Ensemble为后”。Feature决定了模型效果的上限,而Ensemble就是让你更接近这个上限。Ensemble讲究“好而不同”,不同是指模型的学习到的侧重面不一样。举个直观的例子,比如数学考试,A的函数题做的比B好,B的几何题做的比A好,那么他们合作完成的分数通常比他们各自单独完成的要高。
常见的Ensemble方法有Bagging、Boosting、Stacking、Blending。这里用的是uniform blending。
其他的就不说了说一下这个
在实现Stacking时,要注意的一点是,避免标签泄漏(Label Leak)。在训练次学习器时,需要上一层学习器对Train Data的测试结果作为特征。如果我们在Train Data上训练,然后在Train Data上预测,就会造成Label Leak。为了避免Label Leak,需要对每个学习器使用K-fold,将K个模型对ValidSet的预测结果拼起来,作为下一层学习器的输入。
Stage1: 将两个问句与MagicFeature输入Deep Learning中,将其输出作为下一层的特征(这里的Deep Learning相当于特征抽取器)。我们一共训练了几十个DeepLearning Model。
Stage2: 将DeepLearning特征与手工抽取的几百个传统特征拼在一起,作为输入。在这一层,我们训练各种模型,有成百上千个。
Stage3: 上一层的输出进行EnsembleSelection
比赛中发现的一些深度学习的局限:
通过对深度学习产生的结果进行错误分析,并且参考论坛上别人的想法,我们发现深度学习没办法学到的特征大概可以分为两类:
对于一些数据的Pattern,在TrainData中出现的频数不足以让深度学习学到对应的特征,所以我们需要通过手工提取这些特征。
由于Deep Learning对样本做了独立同分布假设(iid),一般只能学习到每个样本的特征,而学习到数据的全局特征,比如TF-IDF这一类需要统计全局词频才能获取的特征,因此也需要手工提取这些特征。
传统的机器学习模型和深度学习模型之间也存在表达形式上的不同。虽然传统模型的表现未必比深度学习好,但它们学到的Pattern可能不同,通过Ensemble来取长补短,也能带来性能上的提升。因此,同时使用传统模型也是很有必要的。
2.2 第一名的解决方案
比赛结束不久,第一名也放出了他们的解决方案,我们来看看他们的做法。他们的特征总结为三个类别:
Embedding Feature
Text Mining Feature
Structural Feature(他们自己挖掘的Magic Feature)
并且他们也使用了Stacking的框架,并且使用固定的k-fold:
Stage1: 使用了DeepLearning,XGBoost,LightGBM,ExtraTree,Random Forest,KNN等300个模型。
Stage2: 用了手工特征和第一层的预测和深度学习模型的隐藏层,并且训练了150个模型。
Stage3: 使用了分别是带有L1和L2的两种线性模型。
Stage4: 将第三层的结果加权平均。
对比以后发现我们没有做LDA、LSI等特征,并且N-gram的粒度没有那么细(他们用了8-gram),还有他们对Magic Feature的挖掘更加深入。还有一点是他们的Deep Learning模型设计更加合理,他们将筛选出来的手工特征也输入到深度学习模型当中,我觉得这也是他们取得好效果的关键。因为显式地将手工特征输入给深度学习模型,相当于告诉“它你不用再学这些特征了,你去学其他的特征吧”,这样模型就能学到更多的语义信息。所以,我们跟他们的差距还是存在的。
我们在做LSTM的时候使用的是词向量的嵌入:
embedding_matrix = np.zeros((len(word_index) + 1,EMBEDDING_DIM))
for word, i in word_index.items():
embedding_vector = embeddings_index.get(word)
if embedding_vector is not None:
# words not found in embeddingindex will be all-zeros.
embedding_matrix[i]= embedding_vector
现在我们将这个词向量矩阵加载到Embedding层中,注意,我们设置trainable=False使得这个编码层不可再训练。
from keras.layers import Embedding
embedding_layer = Embedding(len(word_index) + 1,
EMBEDDING_DIM,
weights=[embedding_matrix],
input_length=MAX_SEQUENCE_LENGTH,
trainable=False)
一个Embedding层的输入应该是一系列的整数序列,比如一个2D的输入,它的shape值为(samples,indices),也就是一个samples行, indeces列的矩阵。每一次的batch训练的输入应该被padded成相同大小(尽管Embedding层有能力处理不定长序列,如果你不指定数列长度这一参数) dim). 所有的序列中的整数都将被对应的词向量矩阵中对应的列(也就是它的词向量)代替,比如序列[1,2]将被序列[词向
量[1],词向量[2]]代替。这样,输入一个2D张量后,我们可以得到一个3D张量。具体的架构
如下:
使用特征:
word_match
tfidf_word_match 根据tf-idf做的权重,然后和匹配上的单词,得到匹配值
common_unigrams_len 根据句子做分词,可以得到多分词,然后计算相同的概率
common_unigrams_ratio
common_bigrams_len
common_bigrams_ratio
common_trigrams_len
common_trigrams_ratio
q1_freq
q2_freq
q1_q2_intersect
#—————–fromhttps://github.com/abhishekkrthakur/is_that_a_duplicate_quora_question
len_q1 句子1的长度包含空格
len_q2 句子2的长度包含空格
diff_len 长度差
len_char_q1 句子1变成集合后的长度
len_char_q2 句子2变成集合后的长度
len_word_q1 句子1长度不包含空格
len_word_q2 句子2长度不包含空格
common_words 相同的单词的个数
fuzz_qratio 模糊匹配得到的匹配距离
fuzz_WRatio
fuzz_partial_ratio
fuzz_partial_token_set_ratio
fuzz_partial_token_sort_ratio
fuzz_token_set_ratio
fuzz_token_sort_ratio
wmd 计算词移动距离
norm_wmd 正则化词向量后的词移动的距离
cosine_distance 余弦相似度
cityblock_distance 曼哈顿距离
jaccard_distance 雅卡尔距离
canberra_distance 堪培拉距离
euclidean_distance 欧几里得距离
minkowski_distance 闵可夫斯基距离
braycurtis_distance 布雷柯蒂斯
skew_q1vec
skew_q2vec
kur_q1vec
kur_q2vec
#——————from https://www.kaggle.com/act444/lb-0-158-xgb-handcrafted-leaky
word_match 共同享有的词,也就是共同出现的词,首先要去掉stopword
tfidf_wm_stops 去除停止词,根据tf-idf做的权重,然后和匹配上的单词,得到匹配值
wc_diff
wc_ratio
wc_diff_unique 集合句子后长度差
wc_ratio_unique 集合句子后长度差比
wc_diff_unq_stop 去除停止词句子长苏差比
wc_ratio_unique_stop
same_start
char_diff
char_diff_unq_stop
total_unique_words
total_unq_words_stop
char_ratio
#————————————
加上特征集合4中的特征
Xgboost如何用于多分类?
Softmax函数
Xgboost为什么树的高度很低已经可以学习的很好?
误差公式:
模型复杂度和偏差方差的图:
当我们训练一个模型时,偏差和方差都得照顾到,漏掉一个都不行。对于Bagging算法来说,由于我们会并行地训练很多不同的分类器的目的就是降低这个方差(variance) ,因为采用了相互独立的基分类器多了以后,h的值自然就会靠近E(h).所以对于每个基分类器来说,目标就是如何降低这个偏差(bias),所以我们会采用深度很深甚至不剪枝的决策树。对于Boosting来说,每一步我们都会在上一轮的基础上更加拟合原数据,所以可以保证偏差(bias),所以对于每个基分类器来说,问题就在于如何选择variance更小的分类器,即更简单的分类器,所以我们选择了深度很浅的决策树。