鸢尾花数据集是机器学习入门的一个十分经典的数据集,这里依然简单的介绍一下这个项目是做什么的,通过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性预测鸢尾花卉属于(Setosa,Versicolour,Virginica)三个种类中的哪一类。在这篇博客中,我们将在此数据集上同时构建多个机器学习模型,并对比它们分类效果的好坏,从实际项目上对机器学习的各个基础算法的应用背景能有一个初步的掌握,将这些基础算法从理论中实际运用起来,文章中还是有很多经典算法没有涉及到,也欢迎读者进行补充。
2.1 数据读取
鸢尾花的数据集网络上有很多资源,并且机器学习库sk-learn中也内置了此数据集。
1 from sklearn.datasets import load_iris
2 iris = load_iris()
我们也可以用pandas读取数据
import pandas as pd
iris_data = pd.read_csv('iris.data')
#由于这个数据没有列名, 所以先给每个列取个名字。
iris_data.columns = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'class']
print(iris_data.head(5))
输出结果
sepal_length | sepal_width | petal_length | petal_width | class | |
---|---|---|---|---|---|
0 | 4.9 | 3.0 | 1.4 | 0.2 | Iris-setosa |
1 | 4.7 | 3.2 | 1.3 | 0.2 | Iris-setosa |
2 | 4.6 | 3.1 | 1.5 | 0.2 | Iris-setosa |
3 | 5.0 | 3.6 | 1.4 | 0.2 | Iris-setosa |
4 | 5.4 | 3.9 | 1.7 | 0.4 | Iris-setosa |
2.2 数据观察
一般来说,拿到数据之后都要看下数据的分布规则,有没有缺失值(当然这个数据是很干净的,但是流程还是要走一下的)
iris_data.describe()
输出结果
sepal_length | sepal_width | petal_length | petal_width | |
---|---|---|---|---|
count | 149.000000 | 149.000000 | 149.000000 | 149.000000 |
mean | 5.848322 | 3.051007 | 3.774497 | 1.205369 |
std | 0.828594 | 0.433499 | 1.759651 | 0.761292 |
min | 4.300000 | 2.000000 | 1.000000 | 0.100000 |
25% | 5.100000 | 2.800000 | 1.600000 | 0.300000 |
50% | 5.800000 | 3.000000 | 4.400000 | 1.300000 |
75% | 6.400000 | 3.300000 | 5.100000 | 1.800000 |
max | 7.900000 | 4.400000 | 6.900000 | 2.500000 |
为了更好地观察数据的分布情况,借助图形是必不可少的,这里引入了matplotlib和seaborn库进行可视化展示。
import matplotlib.pyplot as plt
import seaborn as sb
sb.pairplot(iris_data.dropna(), hue='class')
输出结果
下图也是对数据观察的另一种方式,通过绘制这类图可以看出当前数据分布在哪一个区间内,同时在这个区间的分布密度,分布密度最大的在那个位置。
plt.figure(figsize=(10, 10))
for column_index, column in enumerate(iris_data.columns):
if column == 'class':
continue
plt.subplot(2, 2, column_index + 1)
sb.violinplot(x='class', y=column, data=iris_data)
从上两幅图中可以看出’petal_length’, 'petal_width’特征的重叠的值比较少,说明它们的区分能力比较强。
因为鸢尾花数据集很简单,特征已经全部提取好了,而且也很纯,所以就直接放到机器学习算法里面训练了。本博客中详细介绍的是决策树算法,其余算法只给出训练过程及准确率,其余一笔带过。
3.1 用决策树进行划分
由于而我们此次的鸢尾花数据集的纯度很高,在构建决策时可以考虑采用默认参数。
from sklearn.model_selection import train_test_split
all_inputs = iris_data[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']]
all_classes = iris_data['class'].values
from sklearn.tree import DecisionTreeClassifier
decision_tree_classifier = DecisionTreeClassifier()
# 划分训练系与测试集
(training_inputs,testing_inputs,training_classes,testing_classes)=train_test_split(all_inputs,all_classes,test_size=0.25,random_state=1)
# 在训练集上训练分类器
decision_tree_classifier.fit(training_inputs,training_classes)
# 使用分类准确性在测试集上验证分类器
decision_tree_classifier.score(testing_inputs,testing_classes)
输出结果
0.9736842105263158
一般我们在最后给出结论时,要考虑到一次结果的不稳定性,所以我们在这里选择的交叉验证,并取平均值作为最终的划分结果,代码如下:
from sklearn.model_selection import cross_val_score
import numpy as np
cv_scores = cross_val_score(decision_tree_classifier,all_inputs,all_classes,cv=10)
#cross_val_score返回我们可以想象的分数列表
#对分类器的性能进行合理估
print(cv_scores)
sb.distplot(cv_scores)
plt.title('Average score:{}'.format(np.mean(cv_scores)))
输出结果
[ 1. 0.93333333 1. 0.93333333 0.93333333 0.86666667 0.93333333 0.93333333 1. 1. ]
从上面的输出结果可以看出,默认参数也取得了很好的划分效果,但是,在实际中,绝大部分数据都不会一次就成功,往往要经历漫长的调参过程,所以,我们还是要经历下调参,主要为了了解整个建模过程,下面给出在实际使用决策树的过程中,常用到的10个参数。
1.criterion gini or entropy
2.splitter best or random 前者是在所有特征中找最好的切分点 后者是在部分特征中(数据量大的时候)
3.max_features None(所有),log2,sqrt,N 特征小于50的时候一般使用所有的
4.max_depth 数据少或者特征少的时候可以不管这个值,如果模型样本量多,特征也多的情况下,可以尝试限制下
5.min_samples_split 如果某节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
6.min_samples_leaf 这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝,如果样本量不大,不需要管这个值,大些如10W可是尝试下5
7.min_weight_fraction_leaf 这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。
8.max_leaf_nodes 通过限制最大叶子节点数,可以防止过拟合,默认是"None”,即不限制最大的叶子节点数。 如果加了限制,算法会建立在最大叶子节点数内最优的决策树。 如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制具体的值可以通过交叉验证得到。
9.class_weight 指定样本各类别的的权重,主要是为了防止训练集某些类别的样本过多导致训练的决策树过于偏向这些类别。这里可以自己指定各个样本的权重如果使用“balanced”,则算法会自己计算权重,样本量少的类别所对应的样本权重会高。
10.min_impurity_split 这个值限制了决策树的增长,如果某节点的不纯度 (基尼系数,信息增益,均方差,绝对差)小于这个阈值 则该节点不再生成子节点。即为叶子节点 。
import numpy as np
#GridSearchCV,它存在的意义就是自动调参,只要把参数输进去,就能给出最优化的结果和参数
from sklearn.model_selection import StratifiedKFold,GridSearchCV
decision_tree_classifier = DecisionTreeClassifier()
parameter_grid = {'max_depth':[1,2,3,4,5],'max_features':[1,2,3,4]}
cross_validation = StratifiedKFold(all_classes,n_splits=10)
grid_search = GridSearchCV(decision_tree_classifier,param_grid=parameter_grid,cv=cross_validation)
#print(grid_search)
grid_search.fit(all_inputs, all_classes)
print('Best score: {}'.format(grid_search.best_score_))
print('Best parameters: {}'.format(grid_search.best_params_))
输出结果
Best score: 0.9664429530201343
Best parameters: {'max_features': 3, 'max_depth': 5}
当我们跑机器学习程序时,尤其是调节网络参数时,通常待调节的参数有很多,参数之间的组合更是繁复。依照注意力>时间>金钱的原则,人力手动调节注意力成本太高,非常不值得。这里我们介绍sklearn模块的GridSearchCV模块,能够在指定的范围内自动搜索具有不同超参数的不同模型组合,有效的节约时间和经历。
decision_tree_classifier = grid_search.best_estimator_
print(decision_tree_classifier)
输出结果
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=5,
max_features=3, max_leaf_nodes=None, min_impurity_split=1e-07,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, presort=False, random_state=None,
splitter='best')
我们在构造决策树的时候,好像只是脑海中构建了决策树,可是实际的决策树到底长什么样子呢,下面我们就来画出实际决策树的形状。
import sklearn.tree as tree
from sklearn.externals.six import StringIO
with open('iris_dtc.dot', 'w') as out_file:
out_file = tree.export_graphviz(decision_tree_classifier, out_file=out_file)
3.2 用SVM进行划分
def show_accuracy(y_hat,y_train,str):
pass
def SVM():
from sklearn import svm
# kernel='linear'时,为线性核函数,C越大分类效果越好,但有可能会过拟合(defaul C=1)。
classifier=svm.SVC(kernel='rbf',gamma=0.1,decision_function_shape='ovo',C=0.8)
#调用ravel()函数将矩阵转变成一维数组
classifier.fit(training_inputs,training_classes.ravel())
# (4)计算svm分类器的准确率
print("SVM-输出训练集的准确率为:", classifier.score(training_inputs, training_classes))
y_hat = classifier.predict(training_inputs)
show_accuracy(y_hat, training_classes, '训练集')
print("SVM-输出测试集的准确率为:", classifier.score(testing_inputs, testing_classes))
y_hat = classifier.predict(testing_inputs)
show_accuracy(y_hat, testing_classes, '测试集')
SVM()
关于SVM参数的选择,有以下参考值。
# kernel=‘rbf’(default)时,为高斯核函数,gamma值越小,分类界面越连续;gamma值越大,分类界面越“散”,分类效果越好,但有可能会过拟合。
# decision_function_shape='ovo’时,为one v one分类问题,即将类别两两之间进行划分,用二分类的方法模拟多分类的结果。
# decision_function_shape='ovr’时,为one v rest分类问题,即一个类别与其他类别进行划分。
# kernel=‘rbf’(default)时,为高斯核函数,gamma值越小,分类界面越连续;gamma值越大,分类界面越“散”,分类效果越好,但有可能会过拟合。
# decision_function_shape='ovo’时,为one v one分类问题,即将类别两两之间进行划分,用二分类的方法模拟多分类的结果。
# decision_function_shape='ovr’时,为one v rest分类问题,即一个类别与其他类别进行划分。
输出结果
SVM-输出训练集的准确率为: 0.972972972972973
SVM-输出测试集的准确率为: 0.9736842105263158
3.3 用k近邻进行划分
def knn():
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=1)
knn.fit(training_inputs, training_classes)
y_pred = knn.predict(testing_inputs)
print(np.mean(y_pred == testing_classes))
knn()
输出结果
0.9736842105263158
通过决策树、支持向量机及k近邻算法的精确度可以看出,三种算法都有着不同的划分效果,这是由于鸢尾花数据本身就有着良好的划分特性,本博客只作为熟悉机器学习建模的流程,以及基础算法的对比,在以后更复杂的数据集上,想要有良好的准确率,还有很长的一段调参之路要走。