这是一篇根据我个人需要的复习笔记 本来想一篇写下的结果发现太多了自己找起来都好累 干脆分成两半 上半部分包括:Decision Tree/Random Forest/Preprocessing and Feature Engineering/PCA/Logistic Regression/Kmeans. 下半部分从SVM开始 还在整理中。
七、SVM
非常强(强学习器,线性分类非线性分类都能做、二分类多分类都能做、连续型变量的回归也能做、无监督的聚类也能做)
它在手写识别数字和人脸识别中应用广泛,在文本和超 文本的分类中举足轻重,因为SVM可以大量减少标准归纳(standard inductive)和转换设置(transductive settings)中对标记训练实例的需求。除此之外,生物学和许多其他科学都是SVM的青睐者,SVM现在已经广泛被用于蛋白质分类,现 在化合物分类的业界平均水平可以达到90%以上的准确率。从学术的角度来看,SVM是最接近深度学习的机器学习算法。线性SVM可以看成是神经网络的单个神经元(虽然损失函数与神经网络不同),非线性的SVM则与两层的神经网络相当,非线性的SVM中如果添加多个核函数,则可以模仿多层的神经网络。
SVM在干嘛?---选择一条margin最大的decision boundary。(拥有更大边际的决策边界在分类中的泛化误差更小,这边际很小的情况,是一种模型在训练集上表现很好,却在测试集上表现糟糕的情况,所以会“过拟合”。)过程分为:1)损失函数;2)用拉格朗日对偶函数求解系数
除了特别表明是线性的两个类LinearSVC和LinearSVR之外,其他的所有类都是同时支持线性和非线性的。 NuSVC和NuSVC可以手动调节支持向量的数目,其他参数都与最常用的SVC和SVR一致。注意OneClassSVM是无监督的类。
class sklearn.svm.SVC (C=1.0, kernel=’rbf’, degree=3, gamma=’auto_deprecated’, coef0=0.0, shrinking=True, probability=False, tol=0.001, cache_size=200, class_weight=None, verbose=False, max_iter=-1, decision_function_shape=’ovr’, random_state=None)
- linear SVC损失函数
先看decision boundary: 。在一组数据下,给定固定的w和b,
以下黑不拉几的图来自我们鸭绒教授的notebook(我觉得很清楚)从MMC讲起
- from sklearn import svm
- clf = svm.SVC(kernel='linear',C=10**10)
- clf.fit(df[['X1','X2']], df['class'])
- clf.predict([[3., 3.] , [-3., -3.]])
- beta1,beta2 = clf.coef_[0]
- beta0 = clf.intercept_[0]
- M = 1/np.sqrt(beta1**2 + beta2**2) #Margin
- clf.support_ #support vectors indexes
- P = clf.decision_function(xy).reshape(axisx.shape) #重要接口decision_function,返回每个输入的样本所对应的到决策边界的距离
- 用plt里面contour画 decision boundary
*PS. 画decision boundary的函数: matplotlib.axes.Axes.contour([X, Y,] Z, [levels]) Contour是我们专门用来绘制等高线的函数。等高线,本质上是在二维图像上表现三维图像的一种形式,其中两维X和Y是两条坐标轴上的取值,而Z表示高度。Contour就是将由X和Y构成平面上的所有点中,高度一致的点连接成线段的函数. 要注意的是,我们画线需要重新创建网格,用网格上的点,并不是用样本点。制作网格用到函数meshgrid()
SVC的clf的接口
- Clf.predict(X)#根据决策边界,对X中的样本进行分类,返回的结构为n_samples
- clf.score(X,y)#返回给定测试数据和标签的平均准确度
- clf.support_vectors_#返回支持向量
- clf.n_support_#返回每个类中支持向量的数目
下一个问题?非线性可分数据怎么办?
升维-Extended feature space-映射至多维空间再找decision boundary--核变换--kernel (比如rbf:高斯径向基核函数,本质就是根据原有 feature创建出新的feature来升维)
Kernel: a measure of the similarity of two observations(也就是两个vector)
2.将损失函数的形态转换为拉格朗日乘数形态
我们求解的参数和以及求解的超平面的存在,只与支持向量相关,与其他样本都无关。
3.非线性SVM与核函数
核技巧”(Kernel Trick),是一种能够使用数据原始空间中的向量计算来表示升维后的空间中的点积结果的数学方式。具体表现为K(u,v) =
4. 什么时候选择哪一个核函数?核函数在不同的数据集上的表现怎样?
线性核函数和多项式核函数在非线性数据上表现会浮动,如果数据相对线性可分,则表现不错,如果是像环形数据那样彻底不可分的,则表现糟糕。在线性数据集上,线性核函数和多项式核函数即便有扰动项也可以表现不错,可见多项式核函数是虽然也可以处理非线性情况,但更偏向于线性的功能。Sigmoid核函数就比较尴尬了,它在非线性数据上强于两个线性核函数,但效果明显不如rbf,它在线性数据上完全比不上线性的核函数们,对扰动项的抵抗也比较弱,所以它功能比较弱小,很少被用到。rbf,高斯径向基核函数基本在任何数据集上都表现不错,属于比较万能的核函数。根据经验,无论如何先试试看高斯径向基核函数,它适用于核转换到很高的空间的情况,在各种情况下往往效果都很不错,如果rbf效果不好,那我们再试试看其他的核函数。另外,多项式核函数多被用于图像处理之中。
值得提到的是量纲问题:虽然SVM不能说是完全的距离类模型,但是它严重受到数据量纲的影响。尤其是poly和rbf。所以要先进行数据的无量纲化。
5.调参
参数gamma就是表达式中的γ:γ越大,每个点的影响随distance下降越快(影响范围越小),“everyone becomes support vector”,会导致overfitting。(鸭老师的话我记得超清楚啊)
degree就是多项式核函数的次数,参数coef0就是常数项。其中,高斯径向基核函数受到gamma的影响,而多项式核函数受到全部三个参数的影响。
我们往往直接使用学习曲线或者网格搜索来帮助我们查找最佳的参数组合。
soft margins: 在不完全linear separable的数据上 allow points to be on the wrong side of the margin/separating hyperplane,并且惩罚分错的程度(点到它应该所属那一方的margin的距离),C就是惩罚系数,用于权衡”训练样本的正确分类“与”决策函数的边际最大化“两个不可同时完成的目标,希望找出一个平衡点来让模型的效果最佳。
这时候谁是support vector呢?在两条margin上以及中间,还有那些training error的点,都是support vector。
multi-classification:
- clf = svm.SVC(kernel='linear',C=1,decision_function_shape='ovo')#one versus one
- clf = svm.SVC(kernel='linear',C=1,decision_function_shape='ovr') # one versus rest
八、SVM(下)
unbalanced是个挺大的问题,其一多数标签的学习效果更好,但往往我们关注的少数类别;其二模型的accuracy会失去意义。对于SVM来说,增加样本总量并不合适(第一影响速度,第二其实你增加了很多对无意义的点)
使用SVC的参数:class_weight(本质上这个权重加在了C身上,对两类的惩罚就不一样了,对少数类的惩罚小,对多数类的惩罚大,因此决策边界就会被拉向少数类那一边。用dictionary规定权重或者“balanced”模式)。或者使用fit的参数sample_weight(每个样本在fit时的权重)。这两个用一个就好。通常给少数样本赋更大的权重,就是少数类被分错的惩罚更大,为避免惩罚,decision boundary就会移动。
例如: wclf = svm.SVC(kernel='linear', class_weight={1: 10}) 少数类是1多数类是10.
但是加了不同权重以后,accuracy会下降。因为为了更准确低捕捉到少数类,会误伤多数类。如果我们的目的是模型整体的准确率,那我们就要拒绝样本平衡,使用class_weight被设置之前的模型。然而在现实中,将少数类判错(没有捕捉到)这个代价是巨大的,我们希望不惜一切代价来捕获少数类,或者希望捕捉出尽量多的少数类,那我们就必须使用class_weight设置后的模型。
下一个问题是评估指标,单纯地追求捕捉出少数类,就会成本太高,而不顾及少数类,又会无法达成模型的效果。所以在现实中,我们往往在寻找捕获少数类的能力和将多数类判错后需要付出的成本的平衡。如果一个模型在能够尽量捕获少数类的情况下,还能够尽量对多数类判断正确才是完美。We need Confusion Matrix.(略了 太熟悉了)
- Precision:”将多数类判错后所需付出成本“的衡量。我们抓少数抓的有多准确(实际为真/预测为真)
- Recall/Sensitivity/TPR:在所有的少数类里面,我们抓住了多少
- 追求Precision还是Recall取决于,究竟是误伤多数类的成本更高,还是无法捕捉少数类的代价更高。
- F-score:为了兼顾Precision和Recall。
- TNR:Specificity
- FPR:1-Specificity
Recall增加的时候,Precision的下降,表示更多的多数类被误判了。我们希望理解,我每判断正确一个少数类,就有多少个多数类会被判断错误。FPR(因为它分母是总共的多数类个数)正好可以帮助我们衡量这个能力的变化。我们可以使用Recall和FPR之间的平衡,来替代Recall和Precision之间的平衡,让我们衡量模型在不同阈值下,尽量捕捉少数类的时候,误伤多数类的情况如何变化,这就是我们的ROC曲线。
clf_proba = svm.SVC(kernel="linear",C=1.0,probability=True).fit(X,y) 这样设置以后,我们就可以返回predict_proba,也就是概率预测,我们就可以改变阈值来改变预测的分类了。
ROC曲线代表随着Recall的不断增加,FPR如何增加。我们希望随着Recall的不断提升,FPR增加得越慢越好,这说明我们可以尽量高效地捕捉出少数类,而不会将很多地多数类判断错误。所以,我们希望看到的图像是,纵坐标急速上升,横坐标缓慢增长,也就是在整个图像左上方的一条弧线。这代表模型的效果很不错,拥有较好的捕获少数类的能力。ROC曲线通常是凸型的,所以越靠近左上角越好,也就是线下面积AUC越大。那么最好的阈值在哪呢?其实是Recall和FPR差距最大的点,就是ROC曲线上面离左上角最近的点。这个点,又叫做约登指数。
- from sklearn.metrics import roc_curve
- FPR, recall, thresholds = roc_curve(y,clf_proba.decision_function(X), pos_label=1)
- from sklearn.metrics import roc_auc_score as AUC
- area = AUC(y,clf_proba.decision_function(X))
- 然后画图就好 比如:plt.plot(FPR, recall, color='red',label='ROC curve (area = %0.2f)' % area)
class sklearn.svm.LinearSVC (penalty=’l2’, loss=’squared_hinge’, dual=True, tol=0.0001, C=1.0, multi_class=’ovr’,fit_intercept=True, intercept_scaling=1, class_weight=None, verbose=0, random_state=None, max_iter=1000) 线性支持向量机其实与SVC类中选择"linear"作为核函数的功能类似,但是其背后的实现库是liblinear而不是libsvm,这使得在线性数据上,linearSVC的运行速度比SVC中的“linear”核函数要快,不过两者的运行结果相似。
九、Linear Regression
首先,线性回归的核心是预测连续型变量,源于统计分析。以下理解基于矩阵/线代角度。
- 多元线性回归
- 损失函数:残差平方和 RSS(Residual Sum of Squares)
- 求解方法:最小二乘法(RSS对参数矩阵求导一阶导=0)(无偏估计需要y服从正态分布)
- 线性回归的性能,往往取决于数据本身,而并非是我们的调参能力,线性回归也因此对数据有着很高的要求。
- from sklearn.linear_model import LinearRegression as LR
- reg = LR().fit(Xtrain, Ytrain)
- yhat = reg.predict(Xtest)
2. 模型评估指标
(1)预测数值
- 均方误差MSE(Mean Squared error)用MSE类或者cross_val_score
- from sklearn.metrics import mean_squared_error as MSE
- MSE(yhat,Ytest)
- cross_val_score(reg,X,y,cv=10,scoring="neg_mean_squared_error")
- 绝对均值误差MAE(Mean Absolute error)用法和MSE基本一致
(2)拟合信息
- R方:我们捕获到的信息量/y所含有的信息总量
- from sklearn.metrics import r2_score
- r2_score(yhat,Ytest) ####要注意输入的顺序
- 或者r2 = reg.score(Xtest,Ytest)
- 或者cross_val_score(reg,X,y,cv=10,scoring="r2").mean()
当MSE不高,但是我们的R方也不高的时候,说明我们的模型比较好地拟合了一部分数据的数值,却没有能正确拟合数据的分布。如果有更多的数据进入我们的模型,那数据标签被预测错误的可能性是非常大的。
3. 解决多重共线性问题:Ridge与Lasso
我们都知道多重共线性毁掉回归。原因非满秩矩阵无法求逆,会毁掉最小二乘法。精确相关关系和高度相关关系并称为"多重共线性"。在多重共线性下,模型无法建立,或者模型不可用。一个矩阵如果要满秩,则要求矩阵中每个向量之间不能存在多重共线性,这也构成了线性回归算法对于特征矩阵的要求。
那么想要解决多重共线性的问题,最常用的办法就是在原有的线性回归的基础上进行改进,就有了岭回归,Lasso和弹性网。
1) Ridge: 岭回归在多元线性回归的损失函数上加上了正则项,表达为系数的L2范式(即系数的平方项)乘以正则化系数α。这时只要α取值ok,那么逆矩阵永远存在,也就是有解。如此,正则化系数就非常爽快地避免了”精确相关关系“带来的影响,至少最小二乘法在存在的情况下是一定可以使用了。对于存在”高度相关关系“的矩阵,我们也可以通过调α,从而让逆矩阵变小,以此控制参数向量的偏移。当α越大,模型越不容易受到共线性的影响。
用于判断有无多重共线性---我们在看到较低的拟合程度时,在统计学中,我们会通过VIF或者各种检验来判断数据是否存在共线性,然而在机器学习中,我们可以使用模型来判断——如果一个数据集在岭回归中使用各种正则化参数取值下模型表现没有明显上升(比如出现持平或者下降),则说明数据没有多重共线性,顶多是特征之间有一些相关性。反之,如果一个数据集在岭回归的各种正则化参数取值下表现出明显的上升趋势,则说明数据存在多重共线性。
- from sklearn.linear_model import Ridge, LinearRegression, Lasso
- reg = Ridge(alpha=1).fit(Xtrain,Ytrain)
- reg.score(Xtest,Ytest)
- eg加利福尼亚房价数据集上的学习曲线:岭回归的结果(比线性回归)轻微上升,随后骤降。可以说,加利佛尼亚房屋价值数据集带有很轻微的一部分共线性,这种共线性被正则化参数消除后,模型的效果提升了一点点,但是对于整个模型而言是杯水车薪。在过了控制多重共线性的点后,模型的效果飞速下降,显然是正则化的程度太重,挤占了参数本来的估计空间。从这个结果可以看出,加利佛尼亚数据集的核心问题不在于多重共线性,岭回归不能够提升模型表现。
- 如何选取合适的α?第一种方法是岭迹图。每一条线是一个系数w,岭迹图认为,线条交叉越多,则说明特征之间的多重共线性越高。我们应该选择系数较为平稳的喇叭口所对应的取值作为最佳取值。
- 但是现实中使用更多的是使用交叉验证来选择最佳的正则化系数:class sklearn.linear_model.RidgeCV (alphas=(0.1, 1.0, 10.0), fit_intercept=True, normalize=False, scoring=None,cv=None, gcv_mode=None, store_cv_values=False)
- Ridge_ = RidgeCV(alphas=np.arange(1,1001,100),store_cv_values=True).fit(X, y)
- Ridge_.score(X,y) #无关交叉验证的岭回归结果
- Ridge_.cv_values_.shape #调用所有交叉验证的结果
- Ridge_.cv_values_.mean(axis=0) #进行平均后可以查看每个正则化系数取值下的交叉验证结果
- Ridge_.alpha_ #查看被选择出来的最佳正则化系数
2)Lasso:Lasso无法解决特征之间”精确相关“的问题。当我们使用最小二乘法求解线性回归时,如果线性回归无解或者报除零错误,换Lasso不能解决任何问题。Lasso核心作用还是特征选择。
- from sklearn.linear_model import Ridge, LinearRegression, Lasso
- lasso_ = Lasso(alpha=0.01).fit(Xtrain,Ytrain)
- (lasso_.coef_*100).tolist()
- 比起岭回归,Lasso所带的L1正则项对于系数的惩罚要重得多(敏感很多!),并且它会将系数压缩至0,因此可以被用来做特征选择。也因此,我们往往让Lasso的正则化系数α在很小的空间中变动,以此来寻找最佳的正则化系数。
- from sklearn.linear_model import LassoCV
- alpharange = np.logspace(-10, -2, 200,base=10) #自己建立Lasso进行alpha选择的范围,其实是形成10为底的指数函数,#10**(-10)到10**(-2)次方
- lasso_ = LassoCV(alphas=alpharange #自行输入的alpha的取值范围,cv=5 #交叉验证的折数).fit(Xtrain, Ytrain)
- lasso_.alpha_ #查看被选择出来的最佳正则化系数
- lasso_.coef_ #最佳正则化系数下获得的模型的系数结果
- lasso_.score(Xtest,Ytest) #MSE
4. 非线性问题:多项式回归
概括一下,对于回归问题,数据若能分布为一条直线,则是线性的,否则是非线性。对于分类问题,数据分布若能使用一条直线来划分类别,则是线性可分的,否则数据则是线性不可分的
对于分类模型来说,这是我们判断模型是线性还是非线性的重要评判因素:线性模型的决策边界是平行的直线,非线性模型的决策边界是曲线或者交叉的直线。对分类问题而言,如果一个分类模型的决策边界上自变量的最高次方为1,则我们称这个模型是线性模型。
当我们获取数据时,我们往往希望使用线性模型来对数据进行最初的拟合(线性回归用于回归,逻辑回归用于分类),如果线性模型表现良好,则说明数据本身很可能是线性的或者线性可分的,如果线性模型表现糟糕,那毫无疑问我们会投入决策树,随机森林这些模型的怀抱,就不必浪费时间在线性模型上了。不过,我们有多种手段来处理线性回归无法拟合非线性问题的问题:比如使用分箱或者多项式
分箱:
- from sklearn.preprocessing import KBinsDiscretizer
- enc = KBinsDiscretizer(n_bins=10 #分几类?,encode="onehot") #ordinal
- X_binned = enc.fit_transform(X) #encode模式"onehot":使用做哑变量方式做离散化,之后返回一个稀疏矩阵(m,n_bins),每一列是一个分好的类别,对每一个样本而言,它包含的分类(箱子)中它表示为1,其余分类中它表示为0
- 使用分箱数据进行建模
- LinearR_ = LinearRegression().fit(X_binned, y)
- TreeR_ = DecisionTreeRegressor(random_state=0).fit(X_binned, y)
- 学习曲线找最优的箱子数目
多项式变化:这是一种通过增加自变量上的次数,而将数据映射到高维空间的方法,只要我们设定一个自变量上的次数(大于1),就可以相应地获得数据投影在高次方的空间中的结果。
- from sklearn.preprocessing import PolynomialFeatures
- poly = PolynomialFeatures(degreee=2) #include_bias最好设成0?
- X = poly.fit_transform(X)
summary:都是围绕着原始的线性回归进行的拓展和改进。其中岭回归和Lasso是为了解决多元线性回归中使用最小二乘法的各种限制,主要用途是消除多重共线性带来的影响并且做特征选择,而多项式回归解决了线性回归无法拟合非线性数据的明显缺点,核心作用是提升模型的表现。
十、Naive Bayes
- 真正的概率算法/专注分类/基于概率论和贝叶斯理论
- 先验概率,后验概率,类的条件概率,机器学习中,X表示特征,Y表示label的分类。
- 贝叶斯假设特征之间是有条件独立的,即
因此在特征之间有较多相关性的数据集上表现不佳。
- 分母P(X)可以用全概率公式求得。
- Naive Bayes是一个不建模的算法。
2. 如何对连续型特征变量求解概率呢
当一个特征下有无数种可能发生的事件时,这个特征的取值就是连续型的。当特征为连续型时,随机取到某一个事件发生的概率就为0. 但是我们可以画概率密度曲线(PDF)。我们就将求解连续型变量下某个点取值的概率问题,转化成了求解一个函数f(x)在点xi上的取值的问题.
在现实中,我们往往假设我们的是满足某种统计学中的分布的,最常见的就是高斯分布,常用的还有伯努利分布,多项式分布。这些分布对应着不同的贝叶斯算法,每个都对应着一系列需要我们去估计的参数,因此在贝叶斯中,我们的fit过程其实是在估计对应分布的参数,predict过程是在该参数下的分布中去进行概率预测。
3. sklearn中实现
1)GaussianNB
假设P(xi|Y)服从正态分布,来估计每个特征下每个类别上的条件概率(以给样本分类别)。对于任意一个Y的取值,贝叶斯都以求解最大化的P(xi|Y)为目标,这样我们才能够比较在不同标签下我们的样本究竟更靠近哪一个取值。以最大化P(xi|Y)为目标,高斯朴素贝叶斯会为我们求解公式中的参数σ和μ。求解出参数后,带入一个xi的值,就能够得到一个P(xi|Y)的概率取值。
- from sklearn.naive_bayes import GaussianNB
- gnb = GaussianNB().fit(Xtrain,Ytrain)
- acc_score = gnb.score(Xtest,Ytest)
- Y_pred = gnb.predict(Xtest)
- prob = gnb.predict_proba(Xtest)
- 适合什么样的数据集?高斯朴素贝叶斯的决策边界是曲线,可以是环形也可以是弧线,所以尽管贝叶斯本身更加擅长线性可分的二分数据,但朴素贝叶斯在环形数据和月亮型数据上也可以有远远胜过其他线性模型的表现。但是朴素贝叶斯有一个很严格的假设:特征之间相互独立,所以现实中的效果不会这样好。
2)贝叶斯的拟合特点总结:贝叶斯是速度很快,但分类效果一般,并且初次训练之后的结果就很接近算法极限的算法,几乎没有调参的余地。也就是说,如果我们追求对概率的预测,并且希望越准确越好,那我们应该先选择逻辑回归。如果数据十分复杂,或者是稀疏矩阵,那我们坚定地使用贝叶斯。如果我们分类的目标不是要追求对概率的预测,那我们完全可以先试试看高斯朴素贝叶斯的效果(反正它运算很快速,还不需要太多的样本),如果效果很不错,我们就很幸运地得到了一个表现优秀又快速的模型。如果我们没有得到比较好的结果,那我们完全可以选择再更换成更加复杂的模型。
首先从运行速度来看,贝叶斯与决策树差不多(3s),而且领先其他很多,下一个等级的是SVM和随机森林(20s),最慢的是逻辑回归(50s)。决策树快的原因是因为每一层选择特征的时候不会遍历全部特征,而是抽几个进行遍历,但是随着样本量和特征量增加,决策树的速度必然会降低,而且你敢把少部分样本量用贝叶斯去train(因为效果够好),但是你不敢用决策树在一小部分的样本上面train。逻辑回归按理来说应该挺快的,这里面是因为特征太多了,收到稀疏矩阵的影响而很慢。
然后是学习能力,贝叶斯是一个天生学习很弱的算法,而且没有什么参数可以调,样本量一变大人家学习的成果都突飞猛进接近100%,只有可怜贝叶斯本来就不咋好,还一直下降。所以少量样本量考虑考虑贝叶斯还行。
再来看过拟合问题,决策树的过拟合一直都很严重,在这张图里面,1400样本量对它还不够。第二是对于过拟合的解决办法,只有贝叶斯是通过降低训练集的效果来减轻过拟合的。
3)使用sklearn中的learning_curve类来画学习曲线(样本数量与模型效果的关系)
- train_sizes,train_scores,test_scores=learning_curve(clf, X, y, cv=cv, n_jobs = n_jobs)
- cv可以是一个数,也可是一种模式:
- cv = ShuffleSplit(n_splits = 50, test_size = 0.2, random_state = 0)
4)概率类模型评估指标
布里尔分数Brier Score:
- 类似于均方误差,可以用于任何可以使用predict_proba接口调用概率的模型
- from sklearn.metrics import brier_score_loss
- brier_score_loss(Ytest, prob[:,1], pos_label=1)
- 它是对于多分类的每一个类别下的标签取值进行计算的
- 例子中svc最差,逻辑回归最好。(Svc.decision.function(Xtest)返回置信度:点到决策边界的距离,越远说明越自信)其实svc是强行使用sigmoid函数来压缩概率,所以svc产出的概率结果不怎么可靠。
对数似然log_loss:
里面的第二个参数y_pred只接受概率不接受标签,如下,log_loss在svc和逻辑回归上面会天生表现较好,现实中用做概率模型的评价指标更加常用。适合于:多分类标签/多个模型的比较/
Reliability Curve可靠性曲线/调节概率的校准程度: 一条以预测概率为横坐标,真实标签为纵坐标的曲线。越靠近对角线越好。calibration_curve可用,与ROC类似,用了分箱的做法,给我们横纵坐标。
分箱:
参数:
n_bins取不同的值:10比较合适,3没什么用,100波动太大
依旧在不同的分类器上面尝试:LR/SVC/Bayes,注意LR和Bayes都可以直接返回概率,但是SVC返回的是decision_function接口的置信度,因此要归一化一下。Hasattr(obj,name)这个函数查看类obj中是否存在名为name的接口。下图看出:逻辑回归真的是天生对于概率预测很强。而贝叶斯表现不够好的原因还是特征之间没有相互独立。SVC在于它本身是天生置信度不足的分类器,概率上不是很自信,大部分样本的预测概率都是0.5左右。
预测概率的直方图:
高斯朴素贝叶斯与SVC相反,它最自信,但是过度自信,不准。
概率校准:如何校准可靠性曲线?(如何让它接近对角线)等近似回归。Sklearn中的CalibratedClassifierCV(model, cv, method) 返回校准之后的预测结果。下图看出bayes+isotonic等渗回归校正的效果最好。那么准确率呢?准确率稍稍降低,与布里尔的值相悖了。(太复杂了…但还是以准确率为准)对于SVC来说,两种概率校准办法都很有效。
多项式朴素贝叶斯MultinomialNB
源于多项式实验,它假设特征变量服从一个简单的多项式分布,意义一般为:次数,频数,是否出现等等(离散正整数)。多项式分布擅长的是分类型变量,P(xi|Y)的概率是离散的,并且不同xi之下的概率互相独立。所以它的特征矩阵一般是稀疏矩阵,很多会和TF-IDF相结合。如果要处理连续型变量,我们用KbinsDiscretizer类进行分箱,生成一系列哑变量(是否在第i个箱子里的一堆dummy)。
MultinomialNB的参数:
1)α是一个平滑系数,避免θ=0出现。把矩阵想象成term frequency matrix,每一个样本是大Corpus里面的一个document,每个特征都是一个词。参数θ就代表一个概率。
2)fit_prior: 是否学习先验概率,false的话就用统一先验概率,比如掷色子。
3)class_prior: 给它每个class的先验概率(有点像提前赋上class weight),如果不给它就自己学习。
如果特征矩阵中有负值 考虑归一化。
返回接口:
_mnb.class_log_prior_返回先验概率,查看是否有unbalanced情况
_mnb.feature_log_prob_返回log(P(xi|Y))
伯努利朴素贝叶斯BernoulliNB
假设特征们服从多元二项分布(即这个特征所代表的事件发生与否/存在与否,比较适合文本分类数据,尤其是较短的文档)
参数:α;binarize(二值化,填阈值或None,但是一次性二值化所有的特征);fit_prior; class_prior
贝叶斯的Unbalanced问题
分箱+onehot可以同时解决二项式和多项式分布,二项式贝叶斯在unbalanced的数据集上面recall的表现最好,但是现实中不会强行将所有特征二值化,需要一个一个判断阈值,但这样就很低效率了。
改进后的多项式NB:complementNB
计算权重,避免某一些特征取值较多的样本占更大的权重,会对参数估计起到支配作用。预测规则如下:
Y不等于c的概率越小,=c的概率就越大,样本就属于标签c。
参数:α;norm(是否使用L2范式来规定权重的大小);fit_prior; class_prior
complementNB在unbalanced上面的效果:CNB牺牲了部分整体的精确度和布里尔指数,但是得到了非常高的recall,说明捕捉少数类很优秀,同时AUC也保持很高,因此如果目标是尽可能地捕捉少数类,那么CNB是第一选择。
一个小例子:Bayes Classifier做文本分类。
- 单词向量计数编码:
- sample = ["Machine learning is fascinating, it is wonderful","Machine learning is a sensational technology","Elsa is a popular character"]
- from sklearn.feature_extraction.text import CountVectorizer
- vec = CountVectorizer()
- X = vec.fit_transform(sample)
- X:特征矩阵(稀疏矩阵)
- Vec. get_feature_names()查看都有什么单词向量
- #注意稀疏矩阵是无法输入pandas的,要先toarray()
- CVresult = pd.DataFrame(X.toarray(),columns = vec.get_feature_names())
问题1:越长的句子(样本),对于θ的估计影响越大。因此补集贝叶斯让每个特征的权重除以自己的L2范式。
问题2:没什么实际意义的词,比如is。因此有了TF-IDF来平衡权重。
- from sklearn.feature_extraction.text import TfidfVectorizer as TFIDF
- vec = TFIDF()
- X = vec.fit_transform(sample)
from sklearn.datasets import fetch_20newsgroups 包含约20000篇新闻,20分类的语料库
十一、Xgboost
Xgboost由GBDT梯度提升树发展而来。所以首先要搞懂GBDT在干嘛。
原始的boosting是在每一步训练的模型中 为每一个样本赋权重. 最后将N个分类器组合。Gradient Boosting与Boosting区别在于,每一计算的是为了减少上一次的残差,下一个模型主要在残差减少的梯度方上建立模型,使得残差往梯度方向上减少。
k棵树的集成结果y是所有树的叶子权重的累加,持续迭代找到让损失函数最小的y。迭代的单位是新的那棵树,(原理类比逻辑回归的梯度下降)yi(k+1) = yi(k)+ηf(k+1)(xi) 。η类比α步长。
GBDT一些重要参数:
- 寻找最佳的n_estimator: 学习曲线画谁(偏差r方,方差,和泛化误差)找的是泛化误差最小的点
- subsample:随机抽样时的抽样比例(0,1] 这里要注意如果本身训练集就很小,而且因训练集小而处于过拟合,就不要再抽样了直接用全部trainset
- eta(η):迭代步长(shrinkage),又叫学习率(learning rate),类比逻辑回归里面的α,用于控制迭代速度,越大就越快。太大太小都不好,xgb库中eta默认0.3,sklearn中learning_rate默认0.1。要和n_estimator放在一起用GridSearch调参。如果说经验之谈,η在[0.01,0.2]区间内,而且我们不依赖调整η去提升模型,而还是对树进行操作比如剪枝。
以上参数都是xgboost中与GBDT过程相关的函数。GBDT可以理解为由三个重要部分组成:(1)一个能够衡量集成算法效果的损失函数Obj;(2)每次迭代往上加的弱评估器fk(x);(3)集成手段(迭代方法/抽样方法/样本加权方法/…)
而xgboost就是从以上三方面进行改进,拓展,重新定义,来实现更强大的功能。
xgboost参数:
- booster(xgb库中叫xgb_model): 选择弱评估器,gbtree/gblinear/dart
- objective(xgb库中叫obj,两个库里面默认值不同):目标函数/迭代过程中想要min的东西=传统损失函数(mse/log_loss)+模型复杂度(树的结构),可以看作是对模型方差与偏差的平衡,也就是在min泛化误差。
Xgb库的调用:
- 1. 用DMatrix同时传入X matrix和y label
- 2. 写Param,dictionary格式,注意num_round要单独写 不在param里面
- 3. Train
- 4. Predict
如何求解xgboost的目标函数:
1.转化目标函数:偏差和方差都用fk(x)表示,也就是树的结构。因为我们求解的是树的结构。这里有泰勒展开和刨除已知常数。
2.树结构参数化
fk(xi)表示样本xi在第k棵树的叶子权重/得分,f就代表这
使用叶子节点数量T来代表模型复杂度:Ω(f) = γT+正则项 (α和λ(类比lasso和ridge)正则化强度
3.区别:GBDT的目标函数不含正则项的
4.求解w(每片叶子的叶子得分)与T(叶子数量):最小化结构分数来求解树结构:贪婪算法(控制局部最优来达到全局最优)类比Decision Tree的信息增益,
γ参数:复杂性控制,让树停止生长,防止过拟合。定义:在树 的叶节点上进行进一步分枝所需的最小的目标函数减少量(如果比γ还要小,那gain就小于0了)。γ通过后剪枝的方式防止过拟合。
xgboost.cv类:
Xgboost应用(要记住它也是天生过拟合):剪枝参数与回归模型调参
1.Max_depth(与γ用处差不多 一般调一个就可,调好一个以后,再调另外一个怎么调都看不到到大的效果的)
2.随机抽样:colsample_bytree/bysample/bynode这里是指抽取特征(注意区分这里和subsample的区别:subsample是每次建树时抽取部分样本来防止过度学习)会缩短时间,对于大的数据集,调这三个可能都会有反应。
3.调参思路:通过gridsearch找出最优的n_estimators和eta的组合,然后使用gamma和max_depth观察模型处于什么样的状态(over/under fitting,应该往左还是往右推),最后调整正则化参数(λ和α)和抽样参数。通常来说xgboost大多数时候时需要剪枝的,使用xgb.cv这个类(通过看train和test效果的曲线/就是上面的红黄或者蓝绿曲线)来进行剪枝调参,以调整出一组泛化能力很强的参数。(ps不建议直接把全部参数放进网格搜索,起码要先确定大致范围)(pps 分类的问题一般使用方差和偏差曲线,回归问题使用MSE曲线)
默认参数:
Python中保存和调用模型的方法:
1)pickle:
- import pickle
- Pickle.dump(model,open(‘wenjianming.dat’,’wb’))
- Import sys
- Sys.path现有目录
- Loaded_model=Pickle.load(model,open(‘wenjianming.dat’,’rb’))
- 然后就可以就可以直接做预测了:ypreds = Loaded_model.predict(test)
2)joblib:
- joblib.dump(model, ‘wenjianming.dat’)
- loaded_model = joblib.load(model, ‘wenjianming.dat’)
- ypreds = Loaded_model.predict(test)
Xgboost做classification的例子:
1)unbalanced问题解决
之前的random forest/svm/LR里面都有class_weight参数,xgboost中类似参数为scale_pos_weight,输入负样本量和正样本量之比。
2)sklearn的predict返回分类,xgboost的predict返回概率
十二、nnet
‘黑箱算法’,是深度学习的基础,多用于人工智能而不太适用于商业环境,因为无法解释,只告诉你结果,你的调参也是结果导向
基本原理:基于感知机提出:
感知机三个组成部分:
- 每个输入的特征被匹配的参数w
- 线性关系Z
- 激活函数sign
Nnet就是感知机的集成。隐藏层之间是嵌套关系,是激活函数σ的嵌套。
首先理解参数w,每一条神经键有一个w,他们之间没有关系相互独立。