admin 管理员组

文章数量: 887021

实例

实例

  • 一、拟合正弦函数(线性回归算法)
  • 二、预测房价
    • 1.输入特征
    • 2.模型训练
    • 3.模型优化
    • 4.学习曲线
  • 三、乳腺癌检测
    • 1.数据采集及特征提取
    • 2.模型训练
    • 3.模型优化
    • 4.学习曲线

一、拟合正弦函数(线性回归算法)

用线性回归算法来模拟正弦函数。首先,生成 200 个在
区间内的正弦函数上的点,并且给这些点加上一些随机的噪声。

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as npn_dots = 200X = np.linspace(-2 * np.pi, 2 * np.pi, n_dots)
Y = np.sin(X) + 0.2 * np.random.rand(n_dots) - 0.1
X = X.reshape(-1, 1)
Y = Y.reshape(-1, 1);

其中 reshape()函数的作用是把Numpy的数组整形成符合scikit-learn 输入格式的数组,否则 scikit-learn 会报错。接着,我们使用 Polynomial Features和Pipeline 创建一个多项式拟合模型:

from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipelinedef polynomial_model(degree=1):polynomial_features = PolynomialFeatures(degree=degree,include_bias=False)linear_regression = LinearRegression(normalize=True)pipeline = Pipeline([("polynomial_features", polynomial_features),("linear_regression", linear_regression)])return pipeline

分别用2、3、5、10 阶多项式来拟合数据集:

from sklearn.metrics import mean_squared_errordegrees = [2, 3, 5, 10]
results = []
for d in degrees:model = polynomial_model(degree=d)model.fit(X, Y)train_score = model.score(X, Y)mse = mean_squared_error(Y, model.predict(X))results.append({"model": model, "degree": d, "score": train_score, "mse": mse})
for r in results:print("degree: {}; train score: {}; mean squared error: {}".format(r["degree"], r["score"], r["mse"]))

算出每个模型拟合的评分, 此外,使用mean_squared_ error算出均方根误差,即实际的点和模型预测的点之间的距离,均方根误差越小说明模型拟合效果越好一一上述代码的输出结果为:

degree: 2; train score: 0.15249059065724024; mean squared error: 0.42756715063037826
degree: 3; train score: 0.2769969018169719; mean squared error: 0.3647539144453675
degree: 5; train score: 0.8947943923032302; mean squared error: 0.05307606194971801
degree: 10; train score: 0.9943515343201111; mean squared error: 0.0028496419621541146

从输出结果可以看出,多项式的阶数越高,拟合评分越高,均方差误差越小,拟合效果越好。最后,我们把不同模型的拟合效果在二维坐标上画出来,可以清楚地看到不同阶数的多项式的拟合效果:

from matplotlib.figure import SubplotParamsplt.figure(figsize=(12, 6), dpi=200, subplotpars=SubplotParams(hspace=0.3))
for i, r in enumerate(results):fig = plt.subplot(2, 2, i+1)plt.xlim(-8, 8)plt.title("LinearRegression degree={}".format(r["degree"]))plt.scatter(X, Y, s=5, c='b', alpha=0.5)plt.plot(X, r["model"].predict(X), 'r-')

我们使用 SubplotParams 调整了子图的竖直间距,并且用 subplot ()函数把4个模型拟合情况都画在同一个图形上。上述代码的输出结果如图示。

二、预测房价

我们使用 scikit-learn 自带的波士顿房价数据集来训练模型,然后用模型来测算房价。

1.输入特征

房价和哪些因素有关?scikit-learn 的波士顿房价数据集里,它总共收集了 13 个特征,具体如下。

•CRIM 城镇人均犯罪率。
•ZN 城镇超过 25,000 平方英尺的住宅区域的占地比例
•INDUS 城镇非零售用地占地比例
•CHAS 是否靠近河边, l 为靠近, 为远离。
•NOX 一氧化氮浓度。
•RM 每套房产的平均房间个数
•AGE 在1940 年之前就盖好,且业主自住的房子的比例。
•DIS 与波士顿市中心的距离
•RAD 周边高速公道的便利性指数。
•TAX 每10 ,000 美元的财产税率。
• PTRATIO 小学老师的比例
• B 城镇黑人的比例
• LSTAT 地位较低的人口比例。

不要小看了这些指标,实际上一个模型的好坏和输入特征的选择关系密切。收集哪些特征数据?这些特征数据的可获得性如何?收集成本多高?我们都需要进行思考。
我们先导入数据:

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as npfrom sklearn.datasets import load_bostonboston = load_boston()
X = boston.data
y = boston.target
X.shape

其输出为”( 506,13 )”, 表明这个数据集有 506个样本,每个样本有13个特征。整个训练样本放在一个506X13的矩阵里。可以通过X [0]来查看一个样本数据:

array([6.320e-03, 1.800e+01, 2.310e+00, 0.000e+00, 5.380e-01, 6.575e+00,6.520e+01, 4.090e+00, 1.000e+00, 2.960e+02, 1.530e+01, 3.969e+02,4.980e+00])

还可以通过 boston.feature_names 来查看这些特征的标签:

array(['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD','TAX', 'PTRATIO', 'B', 'LSTAT'], dtype='<U7')

2.模型训练

在scikit-learn 里, LinearRegression 类实现了线性回归算法。在对模型进行训练之前,我们需要先把数据集分成两份,以便评估算法的准确性。

from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=3)

由于数据量较小,我们只选了 20% 的样本来作为测试数据集。接着,训练模型并测试模型的准确性评分:

import time
from sklearn.linear_model import LinearRegressionmodel = LinearRegression()start = time.clock()
model.fit(X_train, y_train)train_score = model.score(X_train, y_train)
cv_score = model.score(X_test, y_test)
print('elaspe: {0:.6f}; train_score: {1:0.6f}; cv_score: {2:.6f}'.format(time.clock()-start, train_score, cv_score))

我们顺便统计了模型的训练时间,除此之外,统计模型针对训练样本的准确性得分(即对训练样本拟合的好坏程度) train_score ,还统计了模型针对测试样本的得分 cv_score。
运行的结果如下:

elaspe : 0.003699 ; train_score : 0.723941 ; cv_score : 0.794958

从得分情况来看,模型的拟合效果一般,还有没有办法来优化模型的拟合效果呢?

3.模型优化

首先观察一下数据,特征数据的范围相差比较大,最小的在 10^-3 级别,而最大的在10^2级别,看来我们需要先把数据进行归一化处理,归一化处理最简单的方式是,创建线性回归模型时增加 normalize=True 参数:

model = LinearRegress on(normal ze=True)

当然,数据归一化处理只会加快算法收敛速度,优化算法训练的效率,无法提升算法的准确性。
怎么样优化模型准确性呢?我们回到训练分数上来,可以观察到数据针对训练样本的评分比较低(train_score: 0.72394 ),即数据对训练数据的拟合成本比较高,这是个典型的欠拟合现象。而优化欠拟合模型的方法,一是挖掘更多输入特征;二是增加多项式特征。在我们的例子里,通过使用低成本的方案,即增加多项多特征来看
看能否优化模型的性能。增加多项式特征,其实就是增加模型的复杂度。
我们编写创建多项式模型的函数:

from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipelinedef polynomial_model(degree=1):polynomial_features = PolynomialFeatures(degree=degree,include_bias=False)linear_regression = LinearRegression(normalize=True)pipeline = Pipeline([("polynomial_features", polynomial_features),("linear_regression", linear_regression)])return pipeline

接着,我们使用二阶多项式来拟合数据:

model = polynomial_model(degree=2)start = time.clock()
model.fit(X_train, y_train)train_score = model.score(X_train, y_train)
cv_score = model.score(X_test, y_test)
print('elaspe: {0:.6f}; train_score: {1:0.6f}; cv_score: {2:.6f}'.format(time.clock()-start, train_score, cv_score))

输出结果是:

elaspe : 0.013994 ; train score : 0.930547; cv score: 0.860465

训练样本分数和测试分数都提高了,看来模型确实得到了优化,我们可以把多项式改为三阶看一下结果:

elaspe: 0.343404; train score : 1.000000; cv score : -106.31 3412

改为三阶多项式后,针对训练样本分数达到了 1,而针对测试样本的分数却是负数,说明这个模型过拟合了。

4.学习曲线

更好的方法是画出学习曲线 ,这样对模型的状态以及优化的方向就一目了然了。

from common.utils import plot_learning_curve
from sklearn.model_selection import ShuffleSplitcv = ShuffleSplit(n_splits=10, test_size=0.2, random_state=0)
plt.figure(figsize=(18, 4))
title = 'Learning Curves (degree={0})'
degrees = [1, 2, 3]start = time.clock()
plt.figure(figsize=(18, 4), dpi=200)
for i in range(len(degrees)):plt.subplot(1, 3, i + 1)plot_learning_curve(plt, polynomial_model(degrees[i]), title.format(degrees[i]), X, y, ylim=(0.01, 1.01), cv=cv)print('elaspe: {0:.6f}'.format(time.clock()-start))

其中common.utils 包里 plot_learning_curve()函数是笔者对此sklearn.model_selection. learning_curve()函数的封装。

一阶多项式欠拟合,因为针对训练样本的分数比较低;而三阶多项式过拟合,因为针对训练样本的分数达到1,却看不到针对交叉验证数据集的分数。针对二阶多项式拟合的情况, 虽然比一阶多项式效果好,但从图中可以明显看出,针对训练数据集的分数和针对交叉验证数据集的分数之间的间隙比较大,这一 特征说明训练样本数量不够,我们应该去采集更多的数据,以便提高模型的准确性。

三、乳腺癌检测

使用逻辑回归算法解决乳腺癌检测问题。我们需要先采集肿瘤病灶造影图片,然后对图片进行分析,从图片中提取特征,再根据特征来训练模型。最终使用模型来检测新采集到的肿瘤病灶造影,以便判断肿瘤是良性的还是恶性的。这是个典型的二元分类问题。

1.数据采集及特征提取

我们决定要提取的特征集合,还需要编写图片处理程序,以便从病灶造影图片中提出我们需要的特征。
为了简单起见, 我们直接加载 scikit- learn 自带的乳腺癌数据集。 这个数据集是已经采集后的数据:

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np# 载入数据
from sklearn.datasets import load_breast_cancercancer = load_breast_cancer()
X = cancer.data
y = cancer.target
print('data shape: {0}; no. positive: {1}; no. negative: {2}'.format(X.shape, y[y==1].shape[0], y[y==0].shape[0]))
print(cancer.data[0])

上述代码输出结果如下:

data shape: (569, 30); no. positive: 357; no. negative: 212
[1.799e+01 1.038e+01 1.228e+02 1.001e+03 1.184e-01 2.776e-01 3.001e-011.471e-01 2.419e-01 7.871e-02 1.095e+00 9.053e-01 8.589e+00 1.534e+026.399e-03 4.904e-02 5.373e-02 1.587e-02 3.003e-02 6.193e-03 2.538e+011.733e+01 1.846e+02 2.019e+03 1.622e-01 6.656e-01 7.119e-01 2.654e-014.601e-01 1.189e-01]

我们可以看到, 数据集中总共有 569 个样本,每个样本有 30 个特征,其中357 个阳性(y=1)样本, 212 个阴性 (y=0)样本。同时,我们还打印出一个样本数据,以便直观地进行观察。
为了强调特征提取工作的重要性,这里介绍一下这些特征值的 物理含义, 读者可以也思考一下,如果提取特征,你会怎么做?

这个数据集总共从病灶造影图片中提取了以下 10 个关键属性。
• radius 半径,即病灶中心点离边界的平均距离
• texture 纹理,灰度值的标准偏差。
• perimeter 周长,即病灶的大小
• area 面积,也是反映病灶大小的一个指标。
• smoothness 平滑度 ,即半径的变化幅度。
• compactness 密实度,周长的平方除以面积的商,再减1,即
• concavity 凹度,凹陷部分轮廓的严重程度
• concave points 凹点 凹陷轮廓的数量。
• symmetry 对称性。
• fractal dimension 分形维度

从这些指标可以看出, 有些指标属于“复合”指标,即由其他的指标经过运算得到的。比如密实度,是由周长和面积计算出来的。不要小看这种运算构建出来的新特征,这是事物内在逻辑关系的体现。
所以, **提取特征时,不妨从事物的内在逻辑关系入手,分析己有特征之间的关系,从而构造出新的特征。**这一方法在实际工程应用中是常用的特征提取手段。
回到我们讨论的乳腺癌数据集的特征问题中,实际上它只关注 10个特征,然后又构造出了每个特征的标准差及最大值,这样每个特征就又衍生出了两个特征,所以总共就有了30个特征。可以通过 cancer.feature_names 来查看这些特征的名称。

2.模型训练

首先,我们把数据集分成训练数据集和测试数据集。

from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

然后,我们使用 LogisticRegression 模型来训练,并计算训练数据集的评分数据和测试数据集的评分数据:

# 模型训练
from sklearn.linear_model import LogisticRegressionmodel = LogisticRegression(solver='liblinear')
model.fit(X_train, y_train)train_score = model.score(X_train, y_train)
test_score = model.score(X_test, y_test)
print('train score: {train_score:.6f}; test score: {test_score:.6f}'.format(train_score=train_score, test_score=test_score))

输出为:

train score: 0.960440; test score: 0.956140

我们还可以看一下测试样本中,有几个是预测正确的:

# 样本预测
y_pred = model.predict(X_test)
print('matchs: {0}/{1}'.format(np.equal(y_pred, y_test).sum(), y_test.shape[0]))

matchs: 109/114

# 预测概率:找出低于 90% 概率的样本个数
y_pred_proba = model.predict_proba(X_test)
# 打印出第一个样本数据,以便读者了解数据形式
print('sample of predict probability: {0}'.format(y_pred_proba[0]))
# 找出第一列,即预测为阴性的概率大于0.1的样本,保存在result里
y_pred_proba_0 = y_pred_proba[:, 0] > 0.1 
result = y_pred_proba[y_pred_proba_0]
# 在result结果集里,找出第二列,即预测阳性的概率大于0.1的样本
y_pred_proba_1 = result[:, 1] > 0.1
print(result[y_pred_proba_1])

输出为:

sample of predict probability: [0.02637883 0.97362117]
[[0.74992875 0.25007125][0.53915951 0.46084049][0.72678466 0.27321534][0.20946605 0.79053395][0.8979358  0.1020642 ][0.1067563  0.8932437 ][0.77851956 0.22148044][0.27019454 0.72980546][0.88897938 0.11102062][0.66036005 0.33963995][0.86510516 0.13489484][0.11937478 0.88062522][0.65284064 0.34715936][0.16253096 0.83746904][0.33117312 0.66882688][0.80773179 0.19226821]]

我们使用 model. predict_proba() 来计算概率,同时找出那些预测“ 自信度 ”低于90%的样本。

3.模型优化

我们使用 LogisticRegression 模型的默认参数训练出来的模型,准确性看起来还是挺高的。问题是有没有优化空间呢?如果有,往哪个方向优化呢?
我们先尝试增加多项式特征,实际上,多项式特征和上文介绍的人为添加的复合特征类似,都是从已有特征经过数学运算得来的。只是这里的逻辑关系没那么明显。所幸,虽然我们不能直观地理解多项式特征的逻辑关系,但是有一些方法和工具可 以用来过滤出那些对模型准确性有帮助的特征。
首先,我们使用 Pipeline 来增加多项式特征:

from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline# 增加多项式预处理
def polynomial_model(degree=1, **kwarg):polynomial_features = PolynomialFeatures(degree=degree,include_bias=False)logistic_regression = LogisticRegression(**kwarg)pipeline = Pipeline([("polynomial_features", polynomial_features),("logistic_regression", logistic_regression)])return pipeline

接着,增加二阶多项式特征,创建并训练模型:

import timemodel = polynomial_model(degree=2, penalty='l1', solver='liblinear')start = time.clock()
model.fit(X_train, y_train)train_score = model.score(X_train, y_train)
cv_score = model.score(X_test, y_test)
print('elaspe: {0:.6f}; train_score: {1:0.6f}; cv_score: {2:.6f}'.format(time.clock()-start, train_score, cv_score))

其中,我们使用 L1 范数作为正则项(参数 penalty=‘11’ ),输出如下:

elaspe: 0.490695; train_score: 1.000000; cv_score: 0.956140

可以看到,训练数据集评分和测试数据集评分都增加了。 为什么使用L1 范数作为正则化呢?L1 范数作为正则项,可以实现参数的稀疏化,即自动帮助我们选择那些对模型有关联的特征。我们可以观察一下有多少个特征没有被丢弃,即其对应的模型参数

logistic_regression = model.named_steps['logistic_regression']
print('model parameters shape: {0}; count of non-zero element: {1}'.format(logistic_regression.coef_.shape, np.count_nonzero(logistic_regression.coef_)))

model parameters shape: (1, 495); count of non-zero element: 90

逻辑回归模型的 coef_ 属性里保存的就是模型参数。从输出结果可以看到,增加二阶多项式特征后,输入特征由原来的30个增加到了 495 个,最终大多数特征都被丢弃,只保留了90个有效特征。

4.学习曲线

怎么知道使用 Ll 范数作为正则项能提高算法的准确性?答案
是:画出学习曲线。学习曲线是模型最有效的诊断工具之一,这也是之前章节一直强调的内容。
首先画出使用 Ll 范数作为正则项所对应的一阶和二阶多项式的学习曲线:

from common.utils import plot_learning_curve
from sklearn.model_selection import ShuffleSplitcv = ShuffleSplit(n_splits=10, test_size=0.2, random_state=0)
title = 'Learning Curves (degree={0}, penalty={1})'
degrees = [1, 2]
penalty = 'l1'start = time.clock()
plt.figure(figsize=(12, 4), dpi=144)
for i in range(len(degrees)):plt.subplot(1, len(degrees), i + 1)plot_learning_curve(plt, polynomial_model(degree=degrees[i], penalty=penalty, solver='liblinear', max_iter=300), title.format(degrees[i], penalty), X, y, ylim=(0.8, 1.01), cv=cv)print('elaspe: {0:.6f}'.format(time.clock()-start))```接着画出使用 L2 范数作为正则项所对应的一阶和二阶多项式的学习曲线:```bash
import warnings
warnings.filterwarnings("ignore")penalty = 'l2'start = time.clock()
plt.figure(figsize=(12, 4), dpi=144)
for i in range(len(degrees)):plt.subplot(1, len(degrees), i + 1)plot_learning_curve(plt, polynomial_model(degree=degrees[i], penalty=penalty, solver='lbfgs'), title.format(degrees[i], penalty), X, y, ylim=(0.8, 1.01), cv=cv)print('elaspe: {0:.6f}'.format(time.clock()-start))


L1范数学习曲线

L2范数学习曲线

从图中可以明显地看出,使用二阶多项式并使用 Ll 范数作为正则项的模型最优,因为它的训练样本评分最高,交叉验证样本评分也最高。从图中还可以看出,训练样本评分和交叉验证样本评分之间的问隙还比较大 ,我们可以来集更多的数据来训练模型,以便进一步优化模型。
sklearn的learning_curve()函数在画学习曲线的过程中,要对模型进行多次训练,并计算交叉验证样本评分。同时,为了使曲 线更平滑,针对每个点还会进行多次计算求平均值。这个就是 ShuffleSplit 类的作用。在我们这个实例里,只有569个训练样本,这是个很小的数据集。如果数据集增加 100 倍,甚至 1000 拿出来画学习曲线将是场灾难。
那么针对大数据集,怎样高效地画学习曲线?
答案很简单, 我们可以从大数据集里选择一小部分数据来画学习曲线,待选择好最优的模型之后,再使用全部的数据集来训练模型。有个地方需要注意,我们要尽量保持选择出来的这部分数据的标签分布与大数据集的标签分布相同,如针对二元分 类,阳性和阴性比例要一致。

本文标签: 实例