《机器学习-回归问题》知道了回归问题的处理方式,分类问题才是机器学习的重点.从数据角度讲,回归问题可以转换为分类问题的微分
逻辑回归(Logistics Regression),逻辑回归虽然带有回归字样,但是逻辑回归属于分类算法。但只可以解决二分类问题
为什么先用0.5这值? σ ( 0 ) = 0.5 即 θ T X = 0 \sigma(0) = 0.5 即 \theta^TX = 0 σ(0)=0.5即θTX=0
对于给定的样本数据集x,y如何找到参数 θ \theta θ,使得使用这样的方式,可以最大程度地获得样本数据集x,对应的分类输出y?
到此,就可以总结出Logistic Regression的损失函数:
这个函数是一个凸函数,他没有公式解,只能使用梯度下降法求解。
如图所示,直线就是所谓的决策边界,左边为红点,右边为蓝点. 样例中选用了两个特征值,即边界公式
具体代码如下
import numpy as np
from sklearn import datasets
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
# 加载数据集, 只选取2个特征,2种花
iris = datasets.load_iris()
x = iris.data
y = iris.target
x = x[y<2, :2]
y = y[y<2]
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=666)
log_reg = LogisticRegression()
log_reg.fit(x_train, y_train)
## 分界函数
def x2(x1):
return (-log_reg.coef_[0][0] * x1 - log_reg.intercept_) / log_reg.coef_[0][1]
## 虚拟axis范围的点,传入训练的model
def plot_decision_boundary(model, axis):
x0, x1 = np.meshgrid(np.linspace(axis[0], axis[1], int((axis[1] - axis[0])*100)).reshape(1, -1),
np.linspace(axis[2], axis[3], int((axis[3] - axis[2])*100)).reshape(1, -1),)
x_new = np.c_[x0.ravel(), x1.ravel()]
y_predict = model.predict(x_new)
zz = y_predict.reshape(x0.shape)
from matplotlib.colors import ListedColormap
custom_cmap = ListedColormap(['#EF9A9A', '#FFF59D', '#90CAF9'])
plt.contourf(x0, x1, zz, cmap=custom_cmap)
# 画出数据点和决策边界
x1_plot = np.linspace(4, 8, 1000)
x2_plot= x2(x1_plot)
plot_decision_boundary(log_reg, axis=[4, 7.5, 1.5, 4.5])
plt.scatter(x[y==0, 0], x[y==0, 1], color='red')
plt.scatter(x[y==1, 0], x[y==1, 1], color='blue')
plt.plot(x1_plot, x2_plot)
plt.show()
KNN算法的决策边界也可以同理得出
如果像下图中不可能使用一根直线完成分割,但是很显然可以使用圆形或者椭圆形完整这个分类任务。
其实在线性回归到多项式回归我们思想就是给训练数据集添加多项式项。同理我们把这个东西用到逻辑回归中。
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
def PolynomiaLogisticRegression(degree):
return Pipeline([
('poly', PolynomialFeatures(degree=degree)),
('std_scale', StandardScaler()),
('log_reg', LogisticRegression())
])
poly_log_reg = PolynomiaLogisticRegression(degree=2)
poly_log_reg.fit(x, y)
## 决策边界
plot_decision_boundary(poly_log_reg, axis=[-4, 4, -4, 4])
plt.scatter(x[y==0, 0], x[y==0, 1])
plt.scatter(x[y==1, 0], x[y==1, 1])
plt.show()
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
# 传入一个新的参数C
def PolynomiaLogisticRegression(degree, C):
return Pipeline([
('poly', PolynomialFeatures(degree=degree)),
('std_scale', StandardScaler()),
('log_reg', LogisticRegression(C=C))
])
poly_log_reg3 = PolynomiaLogisticRegression(degree=20, C=0.1)
poly_log_reg3.fit(x, y)
plot_decision_boundary(poly_log_reg3, axis=[-4, 4, -4, 4])
plt.scatter(x[y==0, 0], x[y==0, 1])
plt.scatter(x[y==1, 0], x[y==1, 1])
plt.show()
在开始之初,说逻辑回归只可以解决二分类问题, 其实可以稍加改造使其能够解决多分类问题。当然这个改造方式并不是只针对逻辑回归这一种算法,这是一种通用的近乎于可以改造所有的二分类。
通常我们把One定义成正样本即p>0.5的. 我们可以求得每个分类的p,然后找到最高的
相当于每一个分类有n-1次PK. 选PK赢次数最多的
假如现在有一个癌症预测系统,输入检测信息,可以判断是否有癌症。如果仅仅使用分类准确度来评价模型的好坏是否合理呢?假如此时模型的预测准确度是99.9%,那么是否能认为模型是好的呢?如果癌症产生的概率只有0.01%,那就意味着这个癌症预测系统只有预测所有人都是健康,即可达到99.99%的准确率。到这里,就能大概理解分类准确度评价模型存在的问题。什么时候才会出现这样的问题呢?这就是对于极度偏斜的数据(Skewed Data),也就样本数据极度不平衡的情况下,只使用分类准确度是远远不够的。因此需要引进更多的指标。
混淆矩阵也称误差矩阵,是表示精度评价的一种标准格式,用n行n列的矩阵形式来表示。以二分类为例子
假设还是癌症预测,先测试了10000个人,预测结果如下
TN:9978个人真实没有癌症,预测没有癌症;FP:12个人真实没有癌症,预测有癌症;FN:2个人真实有癌症,预测没有癌症;TP:8个人真实有癌症,预测也有癌症。其实,就是希望右斜对角线越多越好就,即TN和TP的数量越多越好,也由此会延伸出更多的二级指标。
精准率,召回率就是对更加关注的事件进行一个评判,比如例子中我们把预测有癌症作为关注的重点.在现实的使用过程中,这两个评价指标可能会出现一些矛盾,比如有些时候使用这种方法精准率高但召回率低,使用另外一种方法精准率低召回率高,那么如何权衡两种指标呢?
有时候比较注重精准率,比如股票预测,有时候更加注重召回率,比如病人诊断。对于不同的应用场景,偏好不同的指标。而往往有些时候可能并不是这么的极端,既需要保证精准率又需要保证召回率?由此引出一个新的指标:F1-score。
F1分数(F1 Score),是统计学中用来衡量二分类模型精确度的一种指标。它同时兼顾了分类模型的精确率和召回率。F1分数可以看作是模型精确率和召回率的一种调和平均,它的最大值是1,最小值是0。
决策边界: θ T X = 0 \theta^TX = 0 θTX=0 ,在解析几何中,其实这就是一条直线,这条直线就是分类中的决策边界,在直线的一侧为0,另一侧为1,那么为什么要取0呢? 因为要对半开? 那为什么要对半开呢?
如果让决策边界: θ T X = t h r e s h o l d \theta^TX = threshold θTX=threshold ,此时就是相当于平移决策边界,从而影响分类结果。
这样子threshold就相当一个超参数,可以根据F1的分值去调整
另外也可以通过以下两种曲线,反应超参数调解的效果
支持向量机(support vector machines)是寻找一个超平面来对样本进行分割,分割的原则是间隔最大化,最终转化为一个凸二次规划问题来求解。
逻辑回归通过最小化损失函数来确定决策边界而svm尝试寻找一个最优的决策边界,距离两个类别最近的样本最远,图中3个点到决策边界距离相同。这三个点就叫做支持向量(support vector)。而平行于决策边界的两条直线之间的距离就是 m a r g i n margin margin ,svm就是要最大化 m a r g i n margin margin保证模型的泛化能力,这样就把这个问题转化称为最优化问题。
要找到具有最大间隔的划分超平面,即是找到能满足条件约束的参数 w w w和 b b b,使得 m i n 1 2 ∣ ∣ w ∣ ∣ 2 min {1 \over 2}||w||^2 min21∣∣w∣∣2,这就是支持向量机的基本型。
解决有约束问题的最优化问题需要使用拉格朗日乘子法得到对偶问题,最终得到函数:
其中 α i \alpha_i αi就是拉格朗日乘子。这就是Hard Margin SVM。
如果有一个蓝色的点混进了红色的点当中,这就导致数据集根本就是线性不可分的情况,根本找不出一条直线能够将这两类分开,在这种情况下Hard margin就不再是泛化能力强不强的问题,而是根本找不出一条直线将其分开。由此引出Soft Margin SVM(正则化)。
如图,将直线往里面移点,来点误差 ζ i \zeta_i ζi,最终函数将变成
L1正则还引入一个新的超参数C去权衡容错能力和目标函数,因为已经限制了 ζ i > 0 \zeta_i > 0 ζi>0,所以不加绝对值。C越大越趋近于一个Hard Margin SVM,C越小,就意味着有更大的容错空间。
其实这也可以理解为我们为其加入了L1正则项,避免模型向一个极端方向发展,使得对于极端的数据集不那么敏感,对于未知的数据有更好的泛华能力。
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
iris = datasets.load_iris()
x = iris.data
y = iris.target
# 只做一个简单的二分类
x = x[y<2, :2]
y = y[y<2]
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
## 数据标准化
standardscaler = StandardScaler()
standardscaler.fit(x)
x_standard = standardscaler.transform(x)
svc = LinearSVC(C=0.1)
svc.fit(x_standard, y)
## 画图
def plot_svc_decision_boundary(model, axis):
x0, x1 = np.meshgrid(np.linspace(axis[0], axis[1], int((axis[1] - axis[0])*100)).reshape(1, -1),
np.linspace(axis[2], axis[3], int((axis[3] - axis[2])*100)).reshape(1, -1),)
x_new = np.c_[x0.ravel(), x1.ravel()]
y_predict = model.predict(x_new)
zz = y_predict.reshape(x0.shape)
from matplotlib.colors import ListedColormap
custom_cmap = ListedColormap(['#EF9A9A', '#FFF59D', '#90CAF9'])
plt.contourf(x0, x1, zz, cmap=custom_cmap)
w = model.coef_[0]
b = model.intercept_[0]
# w0*x0 + w1*x1 + b = 0
# x1 = -w0/w1 * x0 - b/w1
plot_x = np.linspace(axis[0], axis[1], 200)
up_y = -w[0]/w[1] * plot_x - b/w[1] + 1/w[1]
down_y = -w[0]/w[1] * plot_x - b/w[1] - 1/w[1]
up_index = (up_y >= axis[2]) & (up_y <= axis[3])
down_index = (down_y >= axis[2]) & (down_y <= axis[3])
plt.plot(plot_x[up_index], up_y[up_index], color='black')
plt.plot(plot_x[down_index], down_y[down_index], color='black')
plot_svc_decision_boundary(svc, axis=[-3, 3, -3, 3])
plt.scatter(x_standard[y==0, 0], x_standard[y==0, 1], color='red')
plt.scatter(x_standard[y==1, 0], x_standard[y==1, 1], color='blue')
plt.show()
前面一直都在讲的是线性的svm,对于svm来说也可以解决非线性问题,类比线性回归到非线性回归的思想,首先使用多项式特征PolynomialFeatures。另外也可以使用核函数
与多项式的方法相同,核函数的目的帮忙从原始空间映射到一个更高维的特征空间. 但不同的是多项的方式要自己造数据,占据大量空间
以高斯核函数为例: 实质上就是把一个mn维的数据映射成了mm的数据。由于理论上数据量可以是无穷维,所以说是映射到一个无穷维空间中。
高斯分布和正态分布可以互相等价
gamma( γ \gamma γ) 就是正态中常数的汇总
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
x, y = datasets.make_moons(noise=0.15, random_state=666)
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.pipeline import Pipeline
def RBFKernelSVC(gamma=1.0):
return Pipeline([
('std_scale', StandardScaler()),
('svc', SVC(kernel='rbf', gamma=gamma))
])
svc = RBFKernelSVC(gamma=1.0)
svc.fit(x, y)
plot_decision_boundary(svc, axis=[-1.5, 2.5, -1.0, 1.5])
plt.scatter(x[y==0, 0], x[y==0, 1])
plt.scatter(x[y==1, 0], x[y==1, 1])
plt.show()
svm解决回归问题的思路:在margin区域内的点越多越好
决策树是基于树结构进行决策的。决策树(Decision Tree)是在已知各种情况发生概率的基础上,通过构成决策树来求取净现值的期望值大于等于零的概率.以一现实中招聘为例子
发表过论文|是否研究生等,对应的就是我们的特征值
如何找出这个2.4,1.8的特征判断值呢?
熵在信息论中代表随机变量不确定度的度量。熵越大,数据不确定性越高,熵越低,数据的不确定性越低。假定当前样本集合D中k类样本所占比例为 p i p_i pi,则信息熵的定义:
- 节点中,计算把A类别 和B类别能到达此处的概率 p i p_i pi,调节判断值,让H尽量小
- 叶子节点中, A类别的 P a P_a Pa > B类别的 P b P_b Pb, 则程序走到这就是A类型
以二分类的例公式为: H = − x l o g ( x ) − ( 1 − x ) l o g ( 1 − x ) H = -xlog(x)-(1-x)log(1-x) H=−xlog(x)−(1−x)log(1−x),其图为下,H值在0.5的时最大
基尼指数CART和信息熵的作用是一样的,也是来帮助决策树选择划分属性.其公式
当决策树建立完成后,平均来说模型预测时候的复杂度: O ( l o g ( m ) ) O(log(m)) O(log(m))其中m表示数据集中的样本个数,但是模型训练的复杂度: m ∗ n O ( l o g ( m ) ) m*nO(log(m)) m∗nO(log(m))。决策树(非参数)的缺点就是非常容易产生过拟合.而决策树中解决过拟合问题的主要手段就是剪枝(pruning)。主要手段
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
x, y = datasets.make_moons(noise=0.25, random_state=666)
from sklearn.tree import DecisionTreeClassifier
# 在不传入任何参数时候,默认就是使用gini指数,树的结构分不到不能再分为止。很容易过拟合
# 传入max_depth=2,限制树的最大层数
dt_clf = DecisionTreeClassifier(max_depth=2)
dt_clf.fit(x, y)
plot_decision_boundary(dt_clf, axis=[-1.5, 2.5, -1.0, 1.5])
plt.scatter(x[y==0, 0], x[y==0, 1])
plt.scatter(x[y==1, 0], x[y==1, 1])
plt.show()
集成学习(ensemble learning)通过构建并结合多个学习器来完成学习任务。假如逻辑回归、决策树、svm三种算法集成.
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split
x, y =datasets.make_moons(n_samples=10000, noise=0.3, random_state=42)
x_train, x_test, y_train, y_test = train_test_split(x, y,test_size=0.4, random_state=42)
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
## voting='soft'
voting_clf = VotingClassifier(estimators=[
('log_reg', LogisticRegression()),
('svm_clf', SVC()),
('dt_clf', DecisionTreeClassifier()),
], voting='hard')
voting_clf.fit(x_train, y_train)
voting_clf.score(x_test, y_test)
根据输出结果显然比Hard Voting效果要好很多,但是仍然有个问题就是虽然有很多机器学习算法,但是从投票的角度看,仍然不够多。因此需要创建更多的子模型,集成更多的子模型的意见。而且子模型之间不能一致,必须要有差异性。
更多的子模型且模型之间不能一致会产生什么效果呢?
这就是集成学习的威力。但是在实际的使用过程很难保证500个子模型的准确率都高于平均水平,甚至有些子模型可能预测错误的概率比预测正确的概率还要高,但是这样并不影响总体准确率的提升。
如何创建差异性?每个子模型只能看数据样本的一部分,就涉及取样.
取样:放回取样(Bagging)和不放回取样(Pasting)。在统计学,放回取样:bootstrap。
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
x, y =datasets.make_moons(n_samples=5000, noise=0.3, random_state=42)
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=42)
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import BaggingClassifier
bagging_clf = BaggingClassifier(DecisionTreeClassifier(),
n_estimators=50, # 模型个数
max_samples=100, # 最大样本数
bootstrap=True) # 有放回随机
bagging_clf.fit(x_train, y_train)
# n_estimators=500 0.912
# n_estimators=50 0.9096
bagging_clf.score(x_test, y_test)
dt_clf = DecisionTreeClassifier()
dt_clf.fit(x_train, y_train)
# 0.872
dt_clf.score(x_test, y_test)
根据对比,当不适用集成学习只是用DecisionTree时,准确率只有0.872,使用50个子模型,每次有放回抽样100个样本,准确率为0.9096,使用500个样本,每次有放回抽样100个样本,准确率为0.912。
更多关于Bagging的特性:
OOB(Out-of-Bag):放回取样会有一定的概率导致一部分样本很有可能没有被取到,严格地进行数据计算平均大约有37%的样本是取不到的,这部分样本就成为Out-of-Bag。因此为了解决这个问题,就不需要将数据集划分为训练集和测试集了,就使用这部分没有被取到的样本做测试或验证。
bagging_clf = BaggingClassifier(DecisionTreeClassifier(),
n_estimators=500,
max_samples=100,
bootstrap=True,
oob_score=True)
# 此时使用的数据集就是全部数据。
# oob_score=True的意思就使用剩下未取到的数据作为测试集。
bagging_clf.fit(x, y)
bagging_clf.oob_score_
Bagging的思路极易并行化处理,在sklearn中对于并行处理传入的参数:n_jobs,-1表示使用全部的核。
bagging_clf = BaggingClassifier(DecisionTreeClassifier(),
n_estimators=5000,
max_samples=100,
bootstrap=True,
oob_score=True,
n_jobs=-1)
使得子模型具有差异化。除了使用有放回的随机取样使得训练子模型的数据集有差异之外
clf = BaggingClassifier(DecisionTreeClassifier(),
n_estimators=5000,
max_samples=100,
bootstrap=True,
oob_score=True,
n_jobs=-1,
max_features=1,
bootstrap_features=True,)
# max_samples=100, bootstrap=True,表示针对样本进行随机抽样
# max_features=1,bootstrap_features=True表示针对样本特征进行随机抽样
clf.fit(x, y)
clf.oob_score_
随机森林(Random Forest,简称RF)是Bagging的一个扩展变体,RF在以决策树为基学习器构建Bagging集成的基础是,进一步在决策树的训练过程中引入随机属性选择。
决策树在节点划分上,在随机的特征子集上寻找最优的划分特征。具体来说在传统决策树在选择划分属性时是当前结点的属性结合(假设有d个属性)中随机选择一个最优属性;而在RF中,对基决策树的每个结点,先从该结点的属性集合中随机选择一个包含k个属性的子集,然后再从这个子集中选择一个属性用于划分。一般情况下,推荐 k = l o g 2 d k=log_2d k=log2d。随机森林中基学习器的多样性不仅来自样本扰动,还来自属性扰动。这就使得最终集成的泛华性能可通过个体学习器之间差异度的增加而进一步提升。
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
x, y =datasets.make_moons(n_samples=500, noise=0.3, random_state=666)
from sklearn.ensemble import RandomForestClassifier
rf_clf = RandomForestClassifier(n_estimators=500,
random_state=666,
oob_score=True,
n_jobs=-1,
max_leaf_nodes=16)
rf_clf.fit(x, y)
# 0.906
rf_clf.oob_score_
除了随机样本的方法RF,还可以随机特性值,极端随机树ET(Extremely randomized trees).即根据特征数去拆出模型.ET与RF的区别:
目前集成学习的方法大致分为两类:一类就是个体学习器之间不存在强依赖关系、可同时生成的并行化方法;另一类是个体学习器之间存在强依赖关系,必须串行生成的序列化方法。前者的代表是Bagging和随机森林,后者的代表是Boosting。
具体来说:就是先从初始训练集训练出一个基学习器,再根据基学习器的表现对训练样本分布进行调整,使得先前学习器做的训练样本在后续受到更多的关注,然后基于调整后的样本分布来训练下一个学习器,如此重读进行,直至学习器数目达到预先指定的值。
根据上面这张图来看,经过第一个学习器的学习后给预测错误样本呈现为更深的蓝色的点,训练正确的为浅色的蓝色的点,赋予错误的训练样本以更高的权重,对于经过调整的训练样本再次训练一个学习器,同理一直迭代下去。最终函数是
h ( x ) = h 1 ( x ) + h 2 ( x ) + h 3 ( x ) h(x)=h_1(x)+h_2(x)+h_3(x) h(x)=h1(x)+h2(x)+h3(x)
from sklearn.ensemble import GradientBoostingClassifier
gb_clf = GradientBoostingClassifier(max_depth=2, n_estimators=30)
gb_clf.fit(x_train, y_train)
# 0.9048
gb_clf.score(x_test, y_test)
在sklearn中梯度提升回归树(Gradient Boosting Decision Tree),简称GBDT
除了采用加法模型的方式之外,还有一类学习型Stacking.
将训练样本分为三部分,
第一部分确定layer1的模型,第二部分+第一部分确定layer2的模型,第三部分+第二部分+第一部分确定layer3的模型
这样得到最终的学习结果。此时这样的网络跟人工神经网络就有点类似了。
《机器学习理论(八)逻辑回归》
《机器学习理论(九)分类算法的评价》
《机器学习理论(十)支持向量机》
《机器学习理论(十一)决策树》
《机器学习理论(十二)集成学习》
《分类模型评判指标–混淆矩阵》