马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
一、任务配景
本文是python实战系列专栏的第一篇文章,我们将从分类开始由浅入深渐渐学习如何利用python完成常规的机器学习/深度学习任务。iris数据集是经典的机器学习入门数据集,许多分类任务教程都会以这个数据集作为示例,它的数据量是150条,由三种不同品种的鸢尾花数据构成,每种鸢尾花各50条数据。显然,这是一个多分类(三分类)任务,我们将从头开始进行探索性数据分析及机器学习建模。
二、机器学习建模流程
1、探索性数据分析
探索性数据分析是建模的第一步,尤其是机器学习任务。只管这是一个很标准、很干净的数据集,我们照旧按照惯例对它进行底子的分析(特性列、空值、各列取值范围等),以对该数据集有个开端的了解和认知。
(1)数据集读取
由于iris数据集实在太经典,所以很多python库都内置了该数据集的调用代码,比方在scikit-learn中可以这样调用:
- from sklearn import datasets
- # 加载iris数据集
- iris = datasets.load_iris()
- # 获取特征数据
- iris_X = iris.data
- # 获取目标(类别)数据
- iris_y = iris.target
复制代码 但是实际应用中,我们更多的是读取本地文件,所以这里笔者从Kaggle下载了iris数据文件并本地读取:
- # 读取csv文件数据
- path = './data/iris.csv'
- df = pd.read_csv(path)
- print('数据量:', len(df))
- print(df.head())
复制代码 结果如下,可见该数据集总共有150行,6列,其中一列是Id,在我们这次任务中作用不大,背面会去掉:
根据官方的介绍,各列的含义如下:
- SepalLengthCm:花萼长度,即花萼的最长部分,单位是厘米。
- SepalWidthCm:花萼宽度,即花萼的最宽部分,单位是厘米。
- PetalLengthCm:花瓣长度,即花瓣的最长部分,单位是厘米。
- PetalWidth Cm:花瓣宽度,即花瓣的最宽部分,单位是厘米。
而标签列Species中的类别有:
- Iris setosa:山鸢尾
- Iris virginica:变色鸢尾
- Iris versicolor:维吉尼亚鸢尾
(2)查看各列中是否有空值
去空值是数据处理惩罚阶段的重要步骤,存在空值的数据会导致背面的特性工程或者建模报错。下面的代码逐列统计是否有空值:
- print('空值数量统计:')
- print(df.isnull().sum())
复制代码 结果如下,每一列都很干净:
(3)简要统计各列的数据数值分布
统计各列的数据分布重要目的是看看每一列各自以及相互之间的数据差异。如果某一列的数据取值之间非常接近(比方一个极端情况是整一列的取值都是1),那么可能这一列对于建模来说是没故意义的,可以去掉。同时,分位数也能帮我们判定当前列数据中是否存在离群点,可以思量去掉离群点。别的,如果列和列之间取值范围差得较大(比如一列的取值范围是0-1,另一列是100-200)就需要进行归一化/标准化操作,量纲差异太大会导致模子学习过程中对特性重要性的判别出现误差。这里我们选择不对原始数据进行归一化或者标准化,一是各列之间的量纲接近,二是我们背面用的是决定树模子建模,决定树不需要归一化/标准化操作。
(4)查抄标签列是否符合建模要求
从上面的图中我们可以看到,标签列是文本类型,无法进行建模。因此需要将标签转换成模子可理解的数值类型(转为数值类型才可以盘算损失)。
- # 定义各类别对应的索引号
- label_index = {'Iris-setosa':0, 'Iris-versicolor':1, 'Iris-virginica':2}
- # apply方法应用到标签列中的每一个标签
- df['Species'] = df['Species'].apply(lambda x: label_index[x])
复制代码 2、数据可视化
我们可以利用sklearn提供的PCA降维方法将原始数据降维成2维(也可以进行3维可视化),而且可视化出来。
- # 分出特征列和标签列
- X = df[['SepalLengthCm', 'SepalWidthCm', 'PetalLengthCm', 'PetalWidthCm']].values.tolist()
- y = df['Species'].tolist()
- # 降维
- pca = PCA(n_components=2)
- pca_data = pca.fit_transform(X)
- # 可视化
- plt.scatter(pca_data[:, 0], pca_data[:, 1], c=y)
- plt.show()
复制代码 可视化结果如下图,固然可视化的过程没有标出来哪个颜色对应哪一类的鸢尾花,但我们可以看到某一类鸢尾花很轻易就可以与另外两类区分开来,而间隔较近的两类似乎数据也是线性可分的,那么我们可以思量用一个简单的模子完成这个任务。
3、数据划分
了解了数据分布之后,我们可以开始进行数据的划分了,即划分训练集和测试集。其中,训练集用于训练模子,测试集用于测试模子的效果(测试集数据应当是在模子的训练阶段是未出现过的,否则就存在模子作弊的题目了)。
- X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, stratify=y, random_state=2024)
- print('训练集数据量:', len(X_train))
- print('测试集数据量:', len(X_test))
复制代码 这里讲一下几个比力重要的参数:
- stratify:控制训练集和测试集的标签类别比例,默认是无,但是我们一般会根据y进行划分。假如我们有100个数据,70个属于A类,30个属于B类,如果不指定该参数且test_size恰恰是0.3,那么就有可能出现我们训练会合的70个都是A类数据,这样子模子只学了A的数据规律,利用测试集的30个数据进行测试的时间效果就很糟糕了。而如果指定了该参数,那么算法会确保训练会合各类别的比例和测试会合的各类别比例同等(如果算出来的数目不是整数会主动取整),假设这个例子我们指定的stratify是y,那么训练会合A和B类别的比例和测试集同等,都是7:3,则训练会合A类有49个,B类有21个,测试会合A类有21个,B类有9个,保持了数据集划分的公平性。
- test_size:指定训练集和测试集划分过程中的比例,用0-1开区间之间的小数就可以了。
- random_state:指定随机数,以保证模子结果可复现。
打印数据划分结果如下:
4、模子训练
这里我们利用一个决定树分类器作为模子,利用训练数据迭代训练决定树模子并输出模子的各项性能指标。
- dt = DecisionTreeClassifier(criterion='gini', splitter='best', max_depth=5, class_weight='balanced', random_state=2024)
- dt.fit(X_train, y_train)
- y_pred = dt.predict(X_test)
- # 多分类问题需要指定average,其中:
- # micro计算全局精确度:通过将所有类别的真正例和假正例汇总,然后计算整体的精确度。
- # macro计算未加权平均精确度:简单地计算每个类别的精确度,然后求平均值,不考虑每个类别的样本数量。
- # weighted计算加权平均精确度:计算每个类别的精确度,然后根据每个类别的样本数量进行加权平均。
- print('Precision:', precision_score(y_test, y_pred, average='macro'))
- print('Recall:', recall_score(y_test, y_pred, average='macro'))
- print('F1:', f1_score(y_test, y_pred, average='macro'))
复制代码 这里有几个模子参数需要注意:
- criterion:可选的有“gini”、“entropy”、“log_loss”,用于指定构建立时用于分裂节点的准则,默认是”gini”,即基尼不纯度。当利用gini作为参数的值时,决定树在每个节点的分裂都旨在最小化基尼不纯度;而利用entropy时,决定树在每个节点的分裂都旨在最大化信息增益,这意味着最小化熵。
- splitter:可选的有“best”、“random”,用于指定如何选择分裂节点的方法,默认是”best”。当设置为best时,决定树会思量全部可能的特性和全部可能的分割点,并选择能够最好地减少criterion取值的最佳分裂点。
- max_depth:指定树的深度,默认是不限定,也就是树可以无穷生长直到满足损失要求,但是对于一些模子规模有限定的场景建议限定树的深度在一定范围,否则模子可能非常大。
- class_weight:默认为无,可以输入字典(键为类别,值为当前类别的样本数)也可以直接写“balanced”。建议直接指定“balanced”,这样模子会主动为不同类别的样本赋予权重。这个参数在样本类别不平衡的时间非常重要,假设一个二分类任务中样本A有98个,B有2个,不设置权重模子可能只学A的特性,从而忽略B类(由于即便B类猜测错误,总体数据上仍然能够有一个很好的指标结果),但设置class_weight后,模子会认为A和B同样重要,从而制止样本类别不平衡造成的模子题目。
- random_state:指定随机数以便复现模子结果。
以下是训练后的模子在测试集上的表现,由于数据集较为简单,模子的指标效果不错。当然,如果进行更细致的参数调优,模子可以取得更好的效果,这里就不演示了。
三、完整代码
- import pandas as pdfrom sklearn.decomposition import PCAimport matplotlib.pyplot as pltfrom sklearn.model_selection import train_test_splitfrom sklearn.tree import DecisionTreeClassifierfrom sklearn.metrics import precision_score, recall_score, f1_scorepath = './data/iris.csv'df = pd.read_csv(path)print('数据量:', len(df))print(df.head())print('空值数量统计:')
- print(df.isnull().sum())print(df.describe())df.drop(['Id'], axis=1)label_index = {'Iris-setosa':0, 'Iris-versicolor':1, 'Iris-virginica':2}df['Species'] = df['Species'].apply(lambda x: label_index[x])X = df[['SepalLengthCm', 'SepalWidthCm', 'PetalLengthCm', 'PetalWidthCm']].values.tolist()y = df['Species'].tolist()pca = PCA(n_components=2)pca_data = pca.fit_transform(X)plt.scatter(pca_data[:, 0], pca_data[:, 1], c=y)plt.show()X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, stratify=y, random_state=2024)
- print('训练集数据量:', len(X_train))
- print('测试集数据量:', len(X_test))dt = DecisionTreeClassifier(criterion='gini', splitter='best', max_depth=5, class_weight='balanced', random_state=2024)dt.fit(X_train, y_train)y_pred = dt.predict(X_test)print('Precision:', precision_score(y_test, y_pred, average='macro'))print('Recall:', recall_score(y_test, y_pred, average='macro'))print('F1:', f1_score(y_test, y_pred, average='macro'))
复制代码 四、总结
本文利用决定树模子对iris鸢尾花数据集进行了分类建模,由于数据量小、数据集简单且各特性含义明确,本文直接利用全部特性进行建模。实际的建模任务中,特性选择也是重要的一步,我们需要选择对建模真正有用的特性(特性选择),而不是全部特性都用于建模(比如一份数据可能有100多个特性列,但实际对建模有用的可能只有5、6个特性)。背面的博文将会陆续介绍这些技巧,并展示如何在更复杂的生产数据中利用这些技巧进行建模。
|