机器学习知识点总结
1.监督模型
- 1.1 SVM
- 1.2 感知机
- 1.3 逻辑回归
- 1.4 线性回归
- 1.5 贝叶斯
- 1.6 决策树
- 1.7 随机森林
- 1.8 Adaboosting
- 1.9 GBDT
- 1.10 XGBoost
- 1.11 LightGBM
- 1.12 KNN
- 1.13 LDA
2.无监督模型
3.其它典型模型
4.特征工程
5.实战部分
1.监督模型
1.1 SVM
SVM全名叫支持向量机,是一种十分经典的分类和回归模型,为什么叫支持向量,
是因为该模型的最终形式只和支持向量,即那些位于决策面上的向量有关,根据数据
的线性可分、线性不可分以及非线性关系,可以将对应的SVM分成硬间隔下的SVM,
软间隔下的SVM,以及结合核函数变换下的SVM。
1.硬间隔下的SVM
所谓的硬间隔,是针对数据样本线性可分的情况,这个时候我们希望所有的样本都能够
正确分出来,并且得到的决策面要最优,怎么去定义这个最优?这个就是SVM里面比较
核心的部分,也就是目标函数部分。
- 目标函数和约束条件
假定最终得到的决策面是两个互相平行的直线y=wx+b(两个是因为一边对应一类,中间的
部分就是类间的差异),我们所要得到的当前是这两个平行直线最大间隔的情况,
对于两类的情况,我们可以简化成一个标签为1,另一个为-1,并且我们希望模型实际
预测的结果也和标签尽可能一致(即两类样本下预测的结果准确而且差异尽可能大),结果
准确对应的方程是y * (wx+b) >= 1,即每一个样本都预测正确,而差异尽可能大
则对应两个分类面的间隔尽可能大,即1/(w)**2尽可能的大,也就是对应w的L2范数的平方
尽可能的小,所以目标函数可以表示为最小化w的L2范数平方,同时带有一个约束条件
各个样本的标签和实际预测的结果乘积要大于1。 - 求解过程
此时问题转化为了一个在一定约束条件下求某一个目标函数的最优值,对于这类问题,我们常常
通过拉格朗日算子将约束和待求解目标合并在一起,这样就会引入拉格朗日算子alpha,并且
此时问题变成了最小最大化问题(最大化约束,最小化目标),我们的目标是求最优的参数w和b,如果直接对这个最小最大化
问题去求解比较复杂(因为通常求最小化问题直接求偏导即可,凸优化问题),所以这里利用到了
对偶思想(最大化最小的最优解比最小化最大的最优解要小),转化成了最大化最小的问题,此时最小化
过程可以把拉格朗日乘子alpha当成一个固定的值,然后对w和b求偏导,得到各自的表达式,然后带入进行
相当于就是求alpha的最大化,对式子取负,即转换成求最小化的问题,通过SMO算法去求解即可,
即每一次求两个alpha参数,固定住其它的参数。- KKT条件
什么是kkt条件,即当我们在求解一个带约束问题的待优化问题的时候,通常我们会将其整合到一个方程式
里面,这时候对这个方方程式求最小最大化的最优值,满足几个条件,即整个方程关于待优化的参数求导为0,
等式约束都为0,不等式约束(均为大于等于的)和为0(带上拉格朗日乘子)。 - SMO算法
最终的优化是要对n个alpha进行计算,同时对n个去算比较复杂,smo的想法是每一次取两个alpha出来,固定住
其它的alpha。
- KKT条件
2.软间隔下的SVM
在硬间隔里面我们是假设所有的样本都能够正确的分类,但是实际里面很难做到如此,很可能存在
个别的难分类样本,所谓的难样本即不在最大分类间隔两侧的,即处在分类间隔线内部的样本,对于这样的样本我们所要求 的
间隔没有那么苛刻,也即软间隔的概念。
- 目标函数和约束条件
软间隔本质上体现在约束条件的式子里面,y * (w * x + b) < 1了,即此时的约束条件相比于硬间隔下要放松了,至于放松了多少,这里是给每一个样本设置一个间隔
参数,即y * (w * x + b) >= 1 - gap,gap越大,说明样本越难分,当gap为0时,和硬间隔就没有区别了;同时我们需要对gap也进行一个不小于0的
约束。改变了约束条件当然我们不希望这个gap无限制的变化,而是希望gap尽可能的小,所有目标函数里面多了一个对gap的惩罚项,gap前面的参数
我们称之为惩罚因子C,惩罚因子项可以理解为经验风险(即根据观测数据来得到的模型尽可能使得这些样本的误差尽可能的小,当C越大时,
模型对训练数据的拟合能力得到加强,太大会过拟合),而前一项对w参数的L2范数平方和可以理解为是结构风险(在分类正确即误差一定的情况下,
让模型的泛化性更好,这里其实就可以看到模型参数越复杂,越不利于模型的泛化性提升)。 - 求解过程
求解过程和硬间隔基本一样,对于待优化的目标和约束条件整合到一起,然后最小最大变成最大最小,然后通过对偶问题进行最优值的求解,结合了KKT条件和SMO算法
得到最优的alpha值,然后就可以表示出w和b了。
3.非线性变化
对于非线性即线性不可分的数据样本,我们这时候直接用SVM去求解是不行的,这时候可以尝试对数据样本进行升维处理,即原本的低维已经不足以
来区分样本了,这时候就要考虑结合新的特征属性,可以理解成创造新的特征值,本质上一个维度的变化,但是维度一变,就会导致计算的过程变得
复杂,有没有一种方式可以使得计算比较简便,这时候核函数就出来了,核函数的本质是不需要计算数据样本在高维上的内积计算过程,而是直接通过
低维的映射直接得到高维内积同样的结果,这样做就避免了高维计算,这样做的理论支撑还是SVM最后的最优模型参数由各个数据样本的alpha决定的,
而alpha又是由数据样本之间的内积决定的,所以可以通过这样去得到最终的结果
- 常见的核函数
1. 线性核函数:即低维(x1,x2)→x1 * x2
2. 高斯核函数: 即低维(x1, x2)→代入高斯形式的函数(x1和x2之间的差平方以及高斯的mu和sigma)
3. 拉普拉斯。
4. sigmoid激活核函数。
1.2 感知机算法(二分类算法)
一种比较经典的线性二分类算法,我们知道在SVM中的约束条件为y * (w * x + b) >= 1,在感知机里面的约束变弱了,仅需要y * (w * x + b) >= 0即可,并且感知机里面对于分类正确的样本就不做处理,仅仅对于那些分类错误的进行梯度更新算法,即仅仅对于那些y * (w * x + b) < 0的点进行计算,变成我们的损失函数的话需要变负为正,在前面添加一个负号即可。
- 目标函数
感知机的目标即使得分的错误的样本离分界面尽可能的近,即尽可能使得偏离的间隔要小,所以损失函数也就待优化的目标函数为:
- 求解过程
根据目标函数以及传入的数据样本,对模型的参数进行梯度更新,这里面对参数求导比较容易,dw即是输入的样本和标签的乘积之后的结果,db即是样本标签,当然我们可以自己去设定学习率。
class Perceptron():
def __init__(self, feature_dim, activation):
self.activation = activation
self.weights = [0 for i in range(feature_dim)]
self.bias = 0
def __str__(self):
'''
打印学习到的参数
'''
return 'Perceptron Weights:\t%s\n Bias:\t%s' % (self.weights, self.bias)
def predict(self, input):
return self.activation(
reduce(lambda a, b: a + b, list(map(lambda x, w: x * w, input, self.weights)), 0.0) + self.bias
)
def train(self, input_vectors, labels, iteraions, lr):
for iteration in range(iteraions):
#打包数据和标签
samples = zip(input_vectors, labels)
for (input_vector, label) in samples:
output = self.predict(input_vector)
self.update_weights(input_vector, label, output, lr)
def update_weights(self, input_vector, label, output, lr):
#根据求导结果进行梯度更新,当实际的标签和预测的一样,就不更新
self.weights = list(map(lambda origin, x: origin - (output - label) * x * lr, self.weights, input_vector ))
self.bias -= (output - label) * lr
1.3 逻辑回归算法(二分类算法)
逻辑回归算法是一种典型的概率模型,也是一种判别模型,即直接根据输入的数据样本去预测样本属于其中某个类别的概率大小,该方法能够有效的本质是最大似然估计(即求解出的模型要能够使得当前观测样本发生的概率最大)
- 目标函数
这里我们用theta表示模型的参数,那么在theta下我们会对每一个样本估计出属于特定类别的概率,我们假设模型预测的是属于类别1的概率为p,那么对于那些本身就是类别1的观测样本x1,我们当然希望theta(x1)→p越大越好,而对于非类别1的其它观测样本x2,我们当然希望概率theta(x2)→p越小越好,对应就是1-p越大越好,联合到一起就可以表示成p * (1 - p)这种形式,那么如何去表示这个实际的标签呢,直接将标签的信息代入即可,p ** (y)* (1 - p) ** (1-y),当为类别1的时候就仅仅p起作用,当不是类别1的时候,1-p起作用,都是做大化,为了更方便去表示,这时候取个对数,再加上负数就得到了我们所要的负对数似然函数,也就是逻辑回归所要优化的目标,即最小化的该损失(添加了负号使得最大化似然概率变成了最小化)。
- 求解过程
确定了目标函数之后,只需要对要优化的参数w和b求导即可,这里需要链式求导,因为w经过了线性变换→sigmoid激活→最后的目标函数,所以按照链式求导发现最后w的梯度和均方误差的导数结果基本一致。
1.4 线性回归算法
线性回归即通过对输入样本特征各个属性的线性组合来得到最后的预测值,一般通过预测值和真实值之间的均方误差或者平均绝对误差来去衡量算法性能的优劣,其中平均绝对误差对异常值不敏感,因为不管是偏离多少,求的导数值都为1,而均方误差对于异常值极其敏感,因为优化的速度和预测值与真实值之间的偏差有关。 既然输出是输入和参数的线性组合,那么我们就可以用矩阵的形式来去表示这个过程,即y = w * x,这里的w和x都是矩阵的形式,其中w的维度为样本特征维度,而x的维度有两个,一个是样本数,另一个是特征维度大小,最终的优化目标则是使得(y - yG).T * (y - yG)尽可能的小,也就所以样本的平方和尽可能的小,因此引入了矩阵自然就会有相应数学方面的推导,这也就涉及到我们常说的最小二乘法,使得残差平方和最小的参数估计也就是最小二乘估计(注意如果误差也就是每一个样本的残差满足零均值、同方差并且互不相关的话,那么此时的估计也就是无偏估计了,即无偏性,和实际的参数值没有偏差)
-
最小二乘法的估计
仍然是对目标函数求导,不过这里用矩阵的形式去表示,求导得到最后的参数w表示如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-50BEiyMt-1665970402617)(pics/LinearRegreDerivation1.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O6b2F5zn-1665970402619)(pics/LinearRegreDeviation2.png)]
可以看出这里需要用到样本矩阵X.T * X的逆矩阵,涉及到逆矩阵的运算,就需要考虑一个矩阵是否存在逆矩阵的问题,什么情况下
矩阵不存在逆矩阵呢?我们知道求逆矩阵可以利用行列式的值来算,当X行列式的值为0时,此时分母将为0,所以此时逆矩阵不存在,这时候
矩阵也被称之为奇异矩阵。当该矩阵可逆的时候,我们直接利用上面的公式求解即可。import numpy as np # 最小二乘法实际在求最佳的参数矩阵W,利用求导可以推出 # 最佳的w = (x.T * x).R * x.T * y,其中R代表取逆 def get_best_w(data, target): data, target = np.mat(data), np.mat(target) print(data.shape, target.shape) x_T_x = data.T * data if np.linalg.det(x_T_x) == 0.0: print("x.T * x的结果为奇异矩阵,不可逆") return return x_T_x.I * (data.T * target)
-
局部加权
通过观测最小二乘法最后参数w的估计项,发现无偏估计的结果只和输入的数据和样本标签有关,是一个固定值,通过这种方式得到
的模型很可能欠拟合,这时候我们可以加入一个修正的矩阵W,来使得估计每一个样本时引入一些偏差,从而降低模型估计的偏差,提高
模型在训练数据的拟合能力。
这里的w可以根据需要去选择 ,常用的为高斯核,高斯核的理解为训练集里面离待预测样本越近的样本权重应该更大,反之越小。
def local_weight_LR(sample, data, target, k = 1): data, target = np.mat(data), np.mat(target) weight = np.mat(np.eye((data.shape[0]))) for i in range((data.shape[0])): difference = sample - data[i, :] weight[i, i] = np.exp(difference * difference.T /(-2.0 * k ** 2)) x_T_W_x = data.T * weight * data if np.linalg.det(x_T_W_x) == 0.0: print("x.T * x的结果为奇异矩阵,不可逆") return ws = x_T_W_x.I * (data.T * weight * target) return ws def local_weight_LR_Predict(data, target, k = 1): predict = np.zeros(data.shape[0]) for i in range(data.shape[0]): predict[i] = np.mat(data[i, :]) * local_weight_LR(data[i, :], data, target, k = k) return predict
-
岭回归
我们知道岭回归也即我们所说的L2正则化,在深度网络模型里面常常用来控制模型的复杂度,防止过拟合,而在线性回归里面引入这一项带来的影响其实就是在求最后的参数w时,加入了一个lambda项使得矩阵非奇异,从而可以求逆。
def ridge_Regression(x_mat, y_mat, lambda_value =