自信息的含义包括两个方面:
1.自信息表示事件发生前,事件发生的不确定性。
2.自信息表示事件发生后,事件所包含的信息量。
互信息指的是两个随机变量之间的关联程度,即给定一个随机变量后,另一个随机变量不确定性的削弱程度,因而互信息取值最小为0,意味着给定一个随机变量对确定一另一个随机变量没有关系,最大取值为随机变量的熵,意味着给定一个随机变量,能完全消除另一个随机变量的不确定性。
- X确定时,Y的不确定性度量
- 在X发生是前提下,Y发生新带来的熵。
- (X,Y)在一起时的不确定性度量
注: 平均互信息:表示信道每传递一个符号所传递的平均信息量。
注 : 热力学第二定律
不可能把热从低温物体传到高温物体而不产生其他影响,或不可能从单一热源取热使之完全转换为有用的功而不产生其他影响,或不可逆热力过程中熵的微增量总是大于零。又称“熵增定律”,表明了在自然过程中,一个孤立系统的总混乱度(即“熵”)不会减小。
用条件概率对应的条件熵来做目标函数。
最大化条件熵,得到 条件概率的分布;
条件概率的分布就是要得到的模型。
(x是特征,y是标签)
回归树
背景
CART是Classification And Regression Tree,这里以Regression Tree为例预习一下回归树。
回归树,或者广义的决策树的工作原理与传统基于梯度下降迭代的Neural Network,或者直接求解析解的线性回归不太一样,它并不是通过某种线性或者非线性的映射,或者变换来进行预测,实现起来也比较反直觉,它的数据结构是一颗二叉树,详细的实现会在下文展示。
但是它的目的和Linear Regression,Neural Network的目的是相同的,本质上都是希望经验风险或者结构风险最小化。
以一个回归问题为例,Regression Tree的目的也是经验风险或者结构风险最小化,此处以最小化Mean Square Loss为例,当时此处的Loss Function是比较自由的,针对具体的学习任务,换成MAE,或者其他Make Sense的Loss Function也完全可以。
数据
首先我们先用几行代码生成一下数据。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
x = np.linspace(-10, 10, 100).reshape((-1, 1))
y = np.linspace(-20, 20, 100) + np.random.normal(loc=0, scale=3.5, size=(100, ))
df = pd.DataFrame()
df['x'] = x.reshape((-1, ))
df['y'] = y
df.plot.scatter(x='x', y='y', figsize=(12, 7))
算法
我们希望最小化:
只是此处与Linear Regression直接求伪逆与Y的乘积,或者与通过梯度下降与反向传播迭代求解的Neural Network不同,Regression Tree通过以下算法最小化上式的Mean Square Error,具体方法会是这样的:
对于一个Shape为 的数据集
,即N行M列的数据集 ,依次逐列遍历逐行,对j列i行,计算下式:
可以看出c1, c2,是对于选定数据集i行j列作为“分割点”后,两个区间内Ground Truth的均值。计算完c1,c2后,将之带入损失函数,计算损失函数的值,并记录之,如果当前c1和c2代表的分割点i, j可以使得损失函数的值最小,那么将这个i, j记为最佳分割,并将数据集切分为 与
,构造一个节点(Node),该节点的阈值是
,树的深度自增,此时,如果树的深度(Max Depth)为1,那么算法就结束了。通常称这种深度非常浅的决策树(此处为回归树)为决策桩。
这个决策桩将会是这样工作的,对于一个给定的输入向量x,如果它的 ,即向量的第j个下标,小于
,那么输出c1,否则输出c2,当然,这样的模型性能是不够的,这个算法还将继续下去。
之后的操作将会是这样的,对切分后的数据集 与
,分别重复以上寻找最佳分割(分裂)点的步骤,并切分数据集,直至达到我们设置的停止条件,如树的深度达到设置的最大深度,或者叶子数达到最大叶子数,或者直至数据集不可再次切分。
这个过程中伴随着一颗二叉树的构造,这个构造通常是以深度优先来进行的,即在数据集,或者数据集的子区间完成寻找最佳分割点后,将构造一个树节点,这个节点通常维护了切分点的下标,以及计算出的c1与c2,以及两个孩子节点。详细的数据结构我们会在下文看到。
实现
这里以我们生成的数据为例,实现一个简单的回归树,首先我们定义树的结点数据结构:
class Node(object):
def __init__(self, i, j, c1, c2, l_node=None, r_node=None):
self.i = i
self.j = j
self.c1 = c1
self.c2 = c2
self.offset = 0
self.l_node = l_node
self.r_node = r_node
可以看出没什么特别的,按照上文描述的,一个节点,维护了最佳切分点的下标,以及给定下标计算出的c1与c2,同时维护了一个下标i相对于原始数据集起始0的偏移,以及左右孩子节点。
接下来我们实现回归树的类,用到了基本的递归生成二叉树与二叉树的先根遍历,这里不再赘述了,一些逻辑写在了注释里:
class RegressionTree(object):
def __init__(self):
self._tree = None
self.x_data = None
self.y_data = None
self.num_nodes = 0
def fit(self, x, y, max_depth=3):
self.x_data = x
self.y_data = y
# Calculate nodes.
self.num_nodes = 2 ** max_depth - 1
# Init root node.
root_node = self.make_node(x, y)
def _fit(_x, _y, _node):
if self.num_nodes <= 0:
return
# Make R.
x_r1, y_r1 = _x[:_node.i], _y[:_node.i]
x_r2, y_r2 = _x[_node.i:], _y[_node.i:]
# Make left node.
l_node = self.make_node(x_r1, y_r1)
_node.l_node = l_node
self.num_nodes -= 1
if _node.l_node:
# Update offset.
l_node.offset = _node.offset
_fit(x_r1, y_r1, _node.l_node)
# Make right node.
r_node = self.make_node(x_r2, y_r2)
_node.r_node = r_node
self.num_nodes -= 1
if _node.r_node:
# Update offset.
r_node.offset = _node.i + _node.offset
_fit(x_r2, y_r2, _node.r_node)
_fit(x, y, root_node)
self._tree = root_node
def predict(self, x):
node = self._tree
def _predict(_x, _node):
val = self.x_data[_node.i + _node.offset, _node.j]
if _x[_node.j] < val:
if _node.l_node:
return _predict(_x, _node.l_node)
else:
return _node.c1
else:
if _node.r_node:
return _predict(_x, _node.r_node)
else:
return _node.c2
return _predict(x, node)
@staticmethod
def make_node(x, y):
# Get shape.
rows, cols = x.shape
if rows <= 1:
return None
# Init params.
best_i, best_j = 1, 1
best_c1, best_c2 = 0, 0
best_loss = np.inf
# Find best split.
for i in range(1, rows):
for j in range(0, cols):
# Calculate c1, c2, loss.
c1 = np.mean(y[:i])
c2 = np.mean(y[i:])
loss = np.mean(y[:i] - c1) + np.mean(y[i:] - c2)
# Update best if need.
if loss < best_loss:
best_loss = loss
best_i = i
best_j = j
best_c1 = c1
best_c2 = c2
node = Node(best_i, best_j, best_c1, best_c2)
return nod
实验
接下来我们对上文生成的数据以不同的最大深度(Max Depth)构造回归树,并检查它的拟合效果,需要注意的是,最大深度其实直接决定了叶子节点的个数,他们的关系会是这样的:
然后,我们构造并拟合数据,设置max_depth
从2到4,然后观察他们拟合的效果:
t = RegressionTree()
df = pd.DataFrame()
df['x'] = x.reshape((-1, ))
df = df.set_index('x')
for max_depth in range(2, 8):
t.fit(x, y, max_depth=max_depth)
y_predict = [t.predict(x[i, :]) for i in range(0, 100)]
df['MAX_DEPTH_{}'.format(max_depth)] = y_predict
plt.figure(figsize=(12, 7))
plt.scatter(x, y, s=10, color='r')
for max_depth in range(2, 8):
col_name = 'MAX_DEPTH_{}'.format(max_depth)
plt.plot(x, df[col_name], label=col_name)
plt.title('Regression Tree')
plt.legend(loc='best')
plt.xlabel('x')
plt.ylabel('y')
可见,当最大深度达到7时,已经出现严重的过拟合了,当然,这与数据量的大小本身也有关系,至此,一个简单的回归树就预习完了。
一、 ROC曲线和AUC值
在逻辑回归、随机森林、GBDT、XGBoost这些模型中,模型训练完成之后,每个样本都会获得对应的两个概率值,一个是样本为正样本的概率,一个是样本为负样本的概率。把每个样本为正样本的概率取出来,进行排序,然后选定一个阈值,将大于这个阈值的样本判定为正样本,小于阈值的样本判定为负样本,然后可以得到两个值,一个是真正率,一个是假正率。
真正率即判定为正样本且实际为正样本的样本数/所有的正样本数,假正率为判定为正样本实际为负样本的样本数/所有的负样本数。每选定一个阈值,就能得到一对真正率和假正率,由于判定为正样本的概率值区间为[0,1],那么阈值必然在这个区间内选择,因此在此区间内不停地选择不同的阈值,重复这个过程,就能得到一系列的真正率和假正率,以这两个序列作为横纵坐标,即可得到ROC曲线了。而ROC曲线下方的面积,即为AUC值。
二 、KS曲线
K-S曲线其实数据来源和本质和ROC曲线是一致的,只是ROC曲线是把真正率和假正率当作横纵轴,而K-S曲线是把真正率和假正率都当作是纵轴,横轴则由选定的阈值来充当。
KS(Kolmogorov-Smirnov):KS用于模型风险区分能力进行评估,指标衡量的是好坏样本累计分部之间的差值。好坏样本累计差异越大,KS指标越大,那么模型的风险区分能力越强。
KS的计算步骤如下:
- 计算每个评分区间的好坏账户数。
- 计算每个评分区间的累计好账户数占总好账户数比率(good%)和累计坏账户数占总坏账户数比率(bad%)。
- 计算每个评分区间累计坏账户占比与累计好账户占比差的绝对值(累计good%-累计bad%),然后对这些绝对值取最大值即得此评分卡的KS值。
三、AUC和KS的关系
要弄明白ks值和auc值的关系首先要弄懂roc曲线和ks曲线是怎么画出来的。其实从某个角度上来讲ROC曲线和KS曲线是一回事,只是横纵坐标的取法不同而已。拿逻辑回归举例,模型训练完成之后每个样本都会得到一个类概率值(注意是类似的类),把样本按这个类概率值排序后分成10等份,每一份单独计算它的真正率和假正率,然后计算累计概率值,用真正率和假正率的累计做为坐标画出来的就是ROC曲线,用10等分做为横坐标,用真正率和假正率的累计值分别做为纵坐标就得到两个曲线,这就是KS曲线。AUC值就是ROC曲线下放的面积值,而ks值就是ks曲线中两条曲线之间的最大间隔距离。
ROC值一般在0.5-1.0之间。值越大表示模型判断准确性越高,即越接近1越好。ROC=0.5表示模型的预测能力与随机结果没有差别。KS值表示了模型将+和-区分开来的能力。值越大,模型的预测准确性越好。一般,KS>0.2即可认为模型有比较好的预测准确性。KS值一般是很难达到0.6的,在0.2~0.6之间都不错。一般如果是如果负样本对业务影响极大,那么区分度肯定就很重要,此时K-S比AUC更合适用作模型评估,如果没什么特别的影响,那么用AUC就很好了。
四、混淆矩阵
查准率(准确率):在被识别为正类别的样本中,确实为正类别的比例是多少?
查全率(召回率):在所有正类别样本中,被正确识别为正类别的比例是多少?
查准率和查全率的计算
查准率和查全率是一对矛盾的度量。一般来说,查准率高时,查全率往往偏低;而查全率高时,查准率往往偏低。例如,若希望将好瓜尽可能多地选出来(查全率高),则可通过增加选瓜的数量来实现,如果将所有西瓜都选上,那么所有的好瓜也必然都被选上了,但这样查准率就会较低;若希望选出的瓜中好瓜比例尽可能高(查准率高),则可只挑选最有把握的瓜, 但这样就难免会漏掉不少好瓜,使得查全率较低。
五、P-R曲线
根据样例是正例的可能性进行排序,排在前面的是学习器认为"最可能 “是正例的样本,排在最后的则是学习器认为"最不可能"是正例的样本。这样,分类过程就相当于在这个排序中以某个"截断点” (cut point)将样本分为两部分,前一部分判作正例,后一部分则判作反例。
这个截断点就相当于阈值(threshold),设置不同的threshold计算出当前的查全率、 查准率。即根据不同threshold来绘制曲线,曲线A上的每个点都对应不同的阈值。A,B,C是三个学习器,也可以说是三个模型。
如何度量学习器性能?
若一个学习器的 P-R 曲线被另一个学习器的曲线完全包住 ,则后者的性能优于前者,A优于C若两个学习器的P-R曲线相交,则可根据平衡点和F1度量。
(1)平衡点(Break-event Point,简称BEP):即查全率=查准率时的取值。平衡点取值A>B,即A优于B (2)F1度量
Fl 是基于查准率与查全率的调和平均 (harinonic mean)定义的:
六、模型稳定度指标PSI
群体稳定性指标PSI(Population Stability Index)是衡量模型的预测值与实际值偏差大小的指标。
举例:
比如训练一个logistic回归模型,预测时候会有个概率输出p。测试集上的输出设定为p1吧,将它从小到大排序后10等分,如0-0.1,0.1-0.2,......。现在用这个模型去对新的样本进行预测,预测结果叫p2,按p1的区间也划分为10等分。实际占比就是p2上在各区间的用户占比,预期占比就是p1上各区间的用户占比。
意义就是如果模型跟稳定,那么p1和p2上各区间的用户应该是相近的,占比不会变动很大,也就是预测出来的概率不会差距很大。一般认为PSI小于0.1时候模型稳定性很高,0.1-0.25一般,大于0.25模型稳定性差,建议重做。
PS:除了按概率值大小等距十等分外,还可以对概率排序后按数量十等分,两种方法计算得到的psi可能有所区别但数值相差不大。
1.Sklearn简介
Scikit-learn(sklearn)是机器学习中常用的第三方模块,对常用的机器学习方法进行了封装,包括回归(Regression)、降维(Dimensionality Reduction)、分类(Classfication)、聚类(Clustering)等方法。当我们面临机器学习问题时,便可根据下图来选择相应的方法。Sklearn具有以下特点:
- 简单高效的数据挖掘和数据分析工具
- 让每个人能够在复杂环境中重复使用
- 建立NumPy、Scipy、MatPlotLib之上
2.Sklearn安装
Sklearn安装要求Python(>=2.7 or >=3.3)
、NumPy (>= 1.8.2)
、SciPy (>= 0.13.3)
。如果已经安装NumPy和SciPy,安装scikit-learn可以使用pip install -U scikit-learn
。
3.Sklearn通用学习模式
Sklearn中包含众多机器学习方法,但各种学习方法大致相同,我们在这里介绍Sklearn通用学习模式。首先引入需要训练的数据,Sklearn自带部分数据集,也可以通过相应方法进行构造,4.Sklearn datasets
中我们会介绍如何构造数据。然后选择相应机器学习方法进行训练,训练过程中可以通过一些技巧调整参数,使得学习准确率更高。模型训练完成之后便可预测新数据,然后我们还可以通过MatPlotLib
等方法来直观的展示数据。另外还可以将我们已训练好的Model进行保存,方便移动到其他平台,不必重新训练。
from sklearn import datasets#引入数据集,sklearn包含众多数据集
from sklearn.model_selection import train_test_split#将数据分为测试集和训练集
from sklearn.neighbors import KNeighborsClassifier#利用邻近点方式训练数据
###引入数据###
iris=datasets.load_iris()#引入iris鸢尾花数据,iris数据包含4个特征变量
iris_X=iris.data#特征变量
iris_y=iris.target#目标值
X_train,X_test,y_train,y_test=train_test_split(iris_X,iris_y,test_size=0.3)#利用train_test_split进行将训练集和测试集进行分开,test_size占30%
print(y_train)#我们看到训练数据的特征值分为3类
'''
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2]
'''
###训练数据###
knn=KNeighborsClassifier()#引入训练方法
knn.fit(X_train,y_train)#进行填充测试数据进行训练
###预测数据###
print(knn.predict(X_test))#预测特征值
'''
[1 1 1 0 2 2 1 1 1 0 0 0 2 2 0 1 2 2 0 1 0 0 0 0 0 0 2 1 0 0 0 1 0 2 0 2 0
1 2 1 0 0 1 0 2]
'''
print(y_test)#真实特征值
'''
[1 1 1 0 1 2 1 1 1 0 0 0 2 2 0 1 2 2 0 1 0 0 0 0 0 0 2 1 0 0 0 1 0 2 0 2 0
1 2 1 0 0 1 0 2]
'''
4.Sklearn datasets
Sklearn提供一些标准数据,我们不必再从其他网站寻找数据进行训练。例如我们上面用来训练的load_iris
数据,可以很方便的返回数据特征变量和目标值。除了引入数据之外,我们还可以通过load_sample_images()
来引入图片。
除了sklearn提供的一些数据之外,还可以自己来构造一些数据帮助我们学习。
from sklearn import datasets#引入数据集
#构造的各种参数可以根据自己需要调整
X,y=datasets.make_regression(n_samples=100,n_features=1,n_targets=1,noise=1)
###绘制构造的数据###
import matplotlib.pyplot as plt
plt.figure()
plt.scatter(X,y)
plt.show()
5.Sklearn Model的属性和功能
数据训练完成之后得到模型,我们可以根据不同模型得到相应的属性和功能,并将其输出得到直观结果。假如通过线性回归训练之后得到线性函数y=0.3x+1
,我们可通过_coef
得到模型的系数为0.3,通过_intercept
得到模型的截距为1。
from sklearn import datasets
from sklearn.linear_model import LinearRegression#引入线性回归模型
###引入数据###
load_data=datasets.load_boston()
data_X=load_data.data
data_y=load_data.target
print(data_X.shape)
#(506, 13)data_X共13个特征变量
###训练数据###
model=LinearRegression()
model.fit(data_X,data_y)
model.predict(data_X[:4,:])#预测前4个数据
###属性和功能###
print(model.coef_)
'''
[ -1.07170557e-01 4.63952195e-02 2.08602395e-02 2.68856140e+00
-1.77957587e+01 3.80475246e+00 7.51061703e-04 -1.47575880e+00
3.05655038e-01 -1.23293463e-02 -9.53463555e-01 9.39251272e-03
-5.25466633e-01]
'''
print(model.intercept_)
#36.4911032804
print(model.get_params())#得到模型的参数
#{'copy_X': True, 'normalize': False, 'n_jobs': 1, 'fit_intercept': True}
print(model.score(data_X,data_y))#对训练情况进行打分
#0.740607742865
6.Sklearn数据预处理
数据集的标准化对于大部分机器学习算法来说都是一种常规要求,如果单个特征没有或多或少地接近于标准正态分布,那么它可能并不能在项目中表现出很好的性能。在实际情况中,我们经常忽略特征的分布形状,直接去均值来对某个特征进行中心化,再通过除以非常量特征(non-constant features)的标准差进行缩放。
例如, 许多学习算法中目标函数的基础都是假设所有的特征都是零均值并且具有同一阶数上的方差(比如径向基函数、支持向量机以及L1L2正则化项等)。如果某个特征的方差比其他特征大几个数量级,那么它就会在学习算法中占据主导位置,导致学习器并不能像我们说期望的那样,从其他特征中学习。例如我们可以通过Scale将数据缩放,达到标准化的目的。
from sklearn import preprocessing
import numpy as np
a=np.array([[10,2.7,3.6],
[-100,5,-2],
[120,20,40]],dtype=np.float64)
print(a)
print(preprocessing.scale(a))#将值的相差度减小
'''
[[ 10. 2.7 3.6]
[-100. 5. -2. ]
[ 120. 20. 40
[[ 0. -0.85170713 -0.55138018]
[-1.22474487 -0.55187146 -0.852133 ]
[ 1.22474487 1.40357859 1.40351318]]
'''
我们来看下预处理前和预处理预处理后的差别,预处理之前模型评分为0.511111111111
,预处理后模型评分为0.933333333333
,可以看到预处理对模型评分有很大程度的提升。
from sklearn.model_selection import train_test_split
from sklearn.datasets.samples_generator import make_classification
from sklearn.svm import SVC
import matplotlib.pyplot as plt
###生成的数据如下图所示###
plt.figure
X,y=make_classification(n_samples=300,n_features=2,n_redundant=0,n_informative=2, random_state=22,n_clusters_per_class=1,scale=100)
plt.scatter(X[:,0],X[:,1],c=y)
plt.show()
###利用minmax方式对数据进行规范化###
X=preprocessing.minmax_scale(X)#feature_range=(-1,1)可设置重置范围
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.3)
clf=SVC()
clf.fit(X_train,y_train)
print(clf.score(X_test,y_test))
#0.933333333333
#没有规范化之前我们的训练分数为0.511111111111,规范化后为0.933333333333,准确度有很大提升
7.交叉验证
交叉验证的基本思想是将原始数据进行分组,一部分做为训练集来训练模型,另一部分做为测试集来评价模型。交叉验证用于评估模型的预测性能,尤其是训练好的模型在新数据上的表现,可以在一定程度上减小过拟合。还可以从有限的数据中获取尽可能多的有效信息。
机器学习任务中,拿到数据后,我们首先会将原始数据集分为三部分:训练集、验证集和测试集。 训练集用于训练模型,验证集用于模型的参数选择配置,测试集对于模型来说是未知数据,用于评估模型的泛化能力。不同的划分会得到不同的最终模型。
以前我们是直接将数据分割成70%的训练数据和测试数据,现在我们利用K折交叉验证分割数据,首先将数据分为5组,然后再从5组数据之中选择不同数据进行训练。
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
###引入数据###
iris=load_iris()
X=iris.data
y=iris.target
###训练数据###
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.3)
#引入交叉验证,数据分为5组进行训练
from sklearn.model_selection import cross_val_score
knn=KNeighborsClassifier(n_neighbors=5)#选择邻近的5个点
scores=cross_val_score(knn,X,y,cv=5,scoring='accuracy')#评分方式为accuracy
print(scores)#每组的评分结果
#[ 0.96666667 1. 0.93333333 0.96666667 1. ]5组数据
print(scores.mean())#平均评分结果
#0.973333333333
那么是否n_neighbor=5便是最好呢,我们来调整参数来看模型最终训练分数。
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import cross_val_score#引入交叉验证
import matplotlib.pyplot as plt
###引入数据###
iris=datasets.load_iris()
X=iris.data
y=iris.target
###设置n_neighbors的值为1到30,通过绘图来看训练分数###
k_range=range(1,31)
k_score=[]
for k in k_range:
knn=KNeighborsClassifier(n_neighbors=k)
scores=cross_val_score(knn,X,y,cv=10,scoring='accuracy')#for classfication
k_score.append(loss.mean())
plt.figure()
plt.plot(k_range,k_score)
plt.xlabel('Value of k for KNN')
plt.ylabel('CrossValidation accuracy')
plt.show()
#K过大会带来过拟合问题,我们可以选择12-18之间的值
我们可以看到n_neighbor在12-18之间评分比较高,实际项目之中我们可以通过这种方式来选择不同参数。另外我们还可以选择2-fold Cross Validation
,Leave-One-Out Cross Validation
等方法来分割数据,比较不同方法和参数得到最优结果。
我们将上述代码中的循环部分改变一下,评分函数改为neg_mean_squared_error
,便得到对于不同参数时的损失函数。
for k in k_range:
knn=KNeighborsClassifier(n_neighbors=k)
loss=-cross_val_score(knn,X,y,cv=10,scoring='neg_mean_squared_error')# for regression
k_score.append(loss.mean())
8.过拟合问题
什么是过拟合问题呢?例如下面这张图片,黑色线已经可以很好的分类出红色点和蓝色点,但是在机器学习过程中,模型过于纠结准确度,便形成了绿色线的结果。然后在预测测试数据集结果的过程中往往会浪费很多时间并且准确率不是太好。
我们先举例如何辨别overfitting问题。Sklearn.learning_curve中的learning curve可以很直观的看出Model学习的进度,对比发现有没有过拟合。
from sklearn.model_selection import learning_curve
from sklearn.datasets import load_digits
from sklearn.svm import SVC
import matplotlib.pyplot as plt
import numpy as np
#引入数据
digits=load_digits()
X=digits.data
y=digits.target
#train_size表示记录学习过程中的某一步,比如在10%,25%...的过程中记录一下
train_size,train_loss,test_loss=learning_curve(
SVC(gamma=0.1),X,y,cv=10,scoring='neg_mean_squared_error',
train_sizes=[0.1,0.25,0.5,0.75,1]
)
train_loss_mean=-np.mean(train_loss,axis=1)
test_loss_mean=-np.mean(test_loss,axis=1)
plt.figure()
#将每一步进行打印出来
plt.plot(train_size,train_loss_mean,'o-',color='r',label='Training')
plt.plot(train_size,test_loss_mean,'o-',color='g',label='Cross-validation')
plt.legend('best')
plt.show()
如果我们改变gamma的值,那么会改变相应的Loss函数。损失函数便在10左右停留,此时便能直观的看出过拟合。
下面我们通过修改gamma参数来修正过拟合问题。
from sklearn.model_selection import validation_curve#将learning_curve改为validation_curve
from sklearn.datasets import load_digits
from sklearn.svm import SVC
import matplotlib.pyplot as plt
import numpy as np
#引入数据
digits=load_digits()
X=digits.data
y=digits.target
#改变param来观察Loss函数情况
param_range=np.logspace(-6,-2.3,5)
train_loss,test_loss=validation_curve(
SVC(),X,y,param_name='gamma',param_range=param_range,cv=10,
scoring='neg_mean_squared_error'
)
train_loss_mean=-np.mean(train_loss,axis=1)
test_loss_mean=-np.mean(test_loss,axis=1)
plt.figure()
plt.plot(param_range,train_loss_mean,'o-',color='r',label='Training')
plt.plot(param_range,test_loss_mean,'o-',color='g',label='Cross-validation')
plt.xlabel('gamma')
plt.ylabel('loss')
plt.legend(loc='best')
plt.show()
通过改变不同的gamma值我们可以看到Loss函数的变化情况。从图中可以看到,如果gamma的值大于0.001便会出现过拟合的问题,那么我们构建模型时gamma参数设置应该小于0.001。
9.保存模型
我们花费很长时间用来训练数据,调整参数,得到最优模型。但如果改变平台,我们还需要重新训练数据和修正参数来得到模型,将会非常的浪费时间。此时我们可以先将model保存起来,然后便可以很方便的将模型迁移。
from sklearn import svm
from sklearn import datasets
#引入和训练数据
iris=datasets.load_iris()
X,y=iris.data,iris.target
clf=svm.SVC()
clf.fit(X,y)
#引入sklearn中自带的保存模块
from sklearn.externals import joblib
#保存model
joblib.dump(clf,'sklearn_save/clf.pkl')
#重新加载model,只有保存一次后才能加载model
clf3=joblib.load('sklearn_save/clf.pkl')
print(clf3.predict(X[0:1]))
#存放model能够更快的获得以前的结果