python-scikit learn学习(2)

0x00 scikit-learn

Scikit-learn(以前称为scikits.learn)是一个用于Python编程语言的免费开源机器学习库。它广泛地支持各种分类、聚类以及回归分析方法比如支持向量机、随机森林、DBSCAN等等,由于其强大的功能、优异的拓展性以及易用性,目前受到了很多数据科学从业者的欢迎,也是业界相当著名的一个开源项目之一。

0x01 模型属性与功能

sklearn库中所有机器学习的模型对象中都有一些属性与功能,假设模型对象名为mod,那么就可以这样表示mod模型的一些属性与功能:

  • mod.coef_ x前的系数
  • mod.intercept_ 截距
  • mod.predict() 预测
  • mod.get_params() 定义的参数
  • mod.score(data_x,data_y) 用data_x做预测,用data_y做比较给模型打分

我们以上一篇中的线性回归模型为例查看一下这些属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from sklearn import datasets
from sklearn.linear_model import LinearRegression
example_x,example_y = datasets.make_regression(n_samples=100,n_features=2,n_targets=1,noise=3)
lr = LinearRegression()
lr.fit(example_x,example_y)
print(lr.coef_)
print(lr.intercept_)
print(lr.predict(example_x[:5,:]))
print(lr.get_params())
print(lr.score(example_x,example_y))

可以从输出中看到:

  • lr.coef_输出一个list代表每一种特征前的系数
  • lr.intercept_输出截距
  • lr.predict()方法可以输入样本进行预测
  • lr.get_params()方法可以输出模型的配置信息
  • lr.score()方法通过对比预测的数据和原始标签对模型打分

0x02 标准化

在训练模型时,某些特征可能会在不同的样本中相差特别大,有一些异常大或者异常小的数据会对模型训练结果造成较大误差,并且数据分布很分散也会影响训练结果,所以一般在训练之前,我们都会对特征数值进行标准化。

基本的标准化流程是去除每个特征的平均值来转换数据使其居中,然后通过将非常数特征除以它们的标准差来对其进行缩放。

在scikit-learn库中,有用于预处理数据的模块sklearn.preprocessing,其中scale方法可以快速简便的实现上述标准化操作。用一段代码来尝试一下:

1
2
3
4
5
6
7
8
9
10
from sklearn import preprocessing
import numpy as np
x_train = np.array([[1, -100, 0.03],
[-1, 500, -0.02],
[0.2, 200, 0.04]], dtype=np.float64)
print(preprocessing.scale(x_train))

可以看到,数据都被标准化到很接近的位置,这样就更利于学习器训练了。

具体能产生多大的影响,我们可以通过datasets产生一组数据,对比直接训练和标准化之后训练的精度:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from sklearn import preprocessing
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
X, y = make_classification(
n_samples=300, n_features=2,
n_redundant=0, n_informative=2,
n_clusters_per_class=1,scale=100)
#可视化数据
plt.scatter(X[:, 0], X[:, 1], c=y)
plt.show()
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))
X = preprocessing.scale(X)
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))

在这个程序中使用datasets中的make_classification方法产生拥有两个特征值的分类数据,然后就可以将两个特征值分别放在横轴和纵轴来观察数据分布

接着对比直接训练和标准化之后训练的精度,发现前后差距非常大。

标准化还有一种常用的方式那就是将特征取值规定到一个范围(默认0-1),只需将最开始代码中标准化函数改为:

1
2
min_max_scaler = preprocessing.MinMaxScaler()
X_MinMax = min_max_scaler.fit_transform(x_train)

0x03 交叉验证

之前在机器学习的模型评估方法里面学过几种模型验证的方法,其中也提到了,学习器训练完成后仍然在训练集种测试实际上是一种错误的方法,因为这个学习器在自身的训练集上很容易得到一个很高的分数,但是对新样本无法预测出任何有用的信息,这种情况被称为过拟合。

所以为了避免这种情况,我们一般在验证的时候会将样本分为训练集和测试集,在之前的博客( http://next.uuzdaisuki.com/2018/07/24/机器学习-5-——模型评估方法/ )介绍了留数法、交叉验证法、自助法这几种方法来分割训练集与测试集。

前面的程序我们大多使用train_test_split方法将样本分为两部分,也就是留数法,那么这里就使用sklearn实现交叉验证法。

交叉验证法需要用到sklearn的cross_val_score模块,我们就在刚才svm分类算法的基础上写出交叉验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from sklearn import preprocessing
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
from sklearn.model_selection import cross_val_score
#生成具有2种属性的300笔数据
X, y = make_classification(
n_samples=300, n_features=2,
n_redundant=0, n_informative=2,
n_clusters_per_class=1,scale=100)
#可视化数据
plt.scatter(X[:, 0], X[:, 1], c=y)
plt.show()
X = preprocessing.scale(X)
clf = SVC()
scores=cross_val_score(clf,X,y,cv=10,scoring='accuracy')
print(scores)
print(scores.mean())

数据分布如图:

输出十组分别的分数和平均分数:

交叉验证因为抽取的测试集更随机化且全部抽到,所以平均后的评估得分更令人信服。

其中cross_val_score种的clf是模型,X样本特征,y是样本标签,cv是交叉验证的分组数量,scoring参数是计分指标,可以根据实际情况在官方文档选取合适的计分指标:

0x04 模型保存

在实际的机器学习运用中,训练用的样本量是十分大的,也就是说训练一个模型需要的时间开销非常大,我们要使用一个训练好的模型,每次都去重新训练一遍是不现实的,所以在训练好之后我们需要保存模型。

我们常用的存取模型方法有如下两种

使用pickle保存模型

第一种方法是使用pickle库保存和读取模型,以上一篇博客中的k近邻算法为例:

保存模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
from sklearn import datasets
from sklearn.neighbors import KNeighborsClassifier
import pickle
iris = datasets.load_iris()
iris_x = iris.data
iris_y = iris.target
knn = KNeighborsClassifier()
knn.fit(iris_x,iris_y)
file = open("model/knn.pickle","wb")
pickle.dump(knn,file)

然后当前程序目录中model子目录下就会产生一个knn.pickle的文件。

读取模型:

1
2
3
4
5
6
7
8
9
10
11
from sklearn import datasets
import pickle
iris = datasets.load_iris()
iris_x = iris.data
file = open("model/knn.pickle","rb")
knn=pickle.load(file)
pred = knn.predict(iris_x[0:5])
print(pred)

读取模型时直接读取这个文件,就可以使用现有模型,在以上代码中使用这个模型预测了前五个数据,我们都知道鸢尾花数据集前五个样本分类都是0,将它们放在这个模型中预测输出,发现也都是0。

使用joblib保存模型

第二种方法是用sklearn的joblib模块保存,joblib库会自动多线程运行,所以在面对大数据的时候性能要优于pickle模块,但是joblib模块只能将文件保存在磁盘中,而且会产生多个文件。

保存模型:

1
2
3
4
5
6
7
8
9
10
11
12
from sklearn import datasets
from sklearn.neighbors import KNeighborsClassifier
from sklearn.externals import joblib
iris = datasets.load_iris()
iris_x = iris.data
iris_y = iris.target
knn = KNeighborsClassifier()
knn.fit(iris_x,iris_y)
joblib.dump(knn,'model/knn.pkl')

然后当前程序目录中model子目录下就会产生一个knn.pkl的文件,有时会产生多个文件,但是读取时只用读取第一个。

读取模型:

1
2
3
4
5
6
7
8
9
10
11
from sklearn import datasets
from sklearn.externals import joblib
iris = datasets.load_iris()
iris_x = iris.data
knn=joblib.load('model/knn.pkl')
pred = knn.predict(iris_x[0:5])
print(pred)

测试结果仍然成立。