机器学习101-Hello world

在接下来的这篇文章中,我们将借助一个生动而具体的案例,带领读者深入探究机器学习的实际运作过程,清晰直观地让读者明白机器学习究竟是在进行怎样的工作,如何从海量的数据中挖掘出有价值的信息,如何通过不断的学习和优化来实现各种神奇的功能,从而让读者对机器学习有一个更为深刻和准确的认知。

机器学习的Hello world——鸢尾花识别

像大多数编程语言的入门教程一样,都是从输出Hello world开始,我们也从机器学习的Hello world问题开始。

鸢尾花数据集(Iris Dataset)是机器学习领域中一个非常经典且常用的数据集。

它包含了 150 个样本,分别属于三种不同的鸢尾花品种(山鸢尾 setosa、变色鸢尾 versicolor和维吉尼亚鸢尾 virginica)。每个样本有四个特征,分别是花萼长度(sepal length)、花萼宽度(sepal width)、花瓣长度(petal length)和花瓣宽度(petal width)。

现在让我们看一看这个数据集中的数据吧(下表),target列表示所属的品种,分别用0代表山鸢尾,1代表变色鸢尾,2代表维吉尼亚鸢尾,一共三个类别;id是序号,暂时可以不用管。

idsepal length (cm)sepal width (cm)petal length (cm)petal width (cm)target
916.13.04.61.41
776.73.05.01.71
995.72.84.11.31
656.73.14.41.41
145.84.01.20.20
1086.72.55.81.82
1425.82.75.11.92
1276.13.04.91.82
244.83.41.90.20
24.73.21.30.20
我们的问题是:如何根据花萼长度(sepal length)、花萼宽度(sepal width)、花瓣长度(petal length)和花瓣宽度(petal width)这4个属性的值,来预测某个样本是属于哪一种花,这是机器学习中的典型问题——分类。机器学习中的很多问题最后都可以转化为一个分类问题,比如图像识别,人脸识别,推荐系统等等,应用十分广泛。

单变量模型

假设我们完全没有机器学习背景知识,那么我们怎么来解决这个问题呢?最简单的思路,是去找规律。我们先从最简单的规律开始,只看4个属性中的一个——花萼长度。我们猜测,三种花的花萼长度不一样!

于是,我们简单计算了下3种花4个属性的平均值(如上图所示),结果发现每个属性确实都有一些差异,尤其是绿色的柱子(花瓣长度),差异最大。所以,我们根据平均值写下一个识别鸢尾花的算法

$$ target =
\begin{cases}
0, \text{petal length} \lt 2.8 \\
1, \text{petal length} \in [2.8, 4.9) \\
2, \text{petal length} \ge 4.9
\end{cases} $$

这个算法用Python实现非常简单:

def iris_classify(petal_length):
    if petal_length < 2.8:
        return 0
    elif petal_length < 4.9:
        return 1
    return 2

恭喜,我们实现了第一个机器学习模型——单变量的鸢尾花识别模型,是不是很简单。

决策树模型

前面的单变量模型可以用一棵决策树来表示出来,如下图所示。决策树是一棵二叉树,它是一种类似树状的结构,由节点和分支组成。决策树的根节点是整个模型的开始,然后通过对数据特征的不断判断和分支,形成多个子节点和分支,直到到达叶子节点,每个叶子节点(下图中方形结点)代表一个分类结果或预测值。

这个模型只用到一个变量,我们很自然想到,如果用更多变量,是不是可以预测得更好?没错,我们先看下第一个叶子节点(petal length < 2.8左子节点),被分到这个节点的样本有50个,都是setosa这个类别,所以已经100%分类准确了;再看下第二个叶子节点(petal length >= 2.8 && petal length < 4.9),这个节点里有49个样本,46个是versicolor,3个是virginica,但是我们的模型把这些样本都分类到versicolor这个类别,存在一定的分类误差。

那么,我们还能进一步提高分类的准确性吗?显然是可以的,很简单,对这个叶子节点上的49个样本,继续使用前面的分析方法,再选一个属性和阈值,将这49个样本继续划分成两个子集,那么准确率还能继续提升。

很容易想到,只要某个叶子节点上的样本不是同一个类别,都可以使用前面的方法一直划分下去,那么我们最终将会得到一个能几乎100%分类准确的决策树模型了!

# 决策树算法伪代码
def generate_decision_tree(D):
    T = TreeNode()  # 决策树

    # 停止条件
    if stop_critera(D):
        return T

    # 选择一个属性和对应的划分阈值,将数据集D分为D1和D2两个子集
    attr, spliter, D1, D2 = select_attr_and_spliter(D)  
    T.value = (attr, spliter)

    # 左子树
    T1 = generate_decision_tree(D1)
    # 右子树
    T2 = generate_decision_tree(D2)

    T.left = T1
    T.right = T2
      
    return T
    

对于一个样本集合D,先判断是否满足停止条件stop_critera,比如D中只有一个类别了,决策树就没必要继续生长了。然后选择一个数据和对应的划分阈值,将数据集分为两个子集。最后依次对这两个子集继续调用 generate_decision_tree 函数,生成左边的子树和右边的子树。

sklearn中的 DecisionTreeClassifier 实现了上述算法,因此我们可以直接调用这个算法,来实现鸢尾花识别模型。代码及解释如下:

from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier, export_graphviz
import graphviz

# 加载鸢尾花数据集
iris = load_iris()

# 创建决策树分类模型
clf = DecisionTreeClassifier()
# .fit函数执行了决策树分类算法,从数据集中学到了最优的分裂规则
clf.fit(iris.data, iris.target)

# 将决策树模型结构导出为pdf格式的图片
dot_data = export_graphviz(clf, feature_names=iris.feature_names,
                           class_names = iris.target_names,
                           out_file=None, filled=True)
dot = graphviz.Source(dot_data)
dot.render('iris')

load_iris 函数加载鸢尾花数据集,DecisionTreeClassifier()创建了决策树分类模型,但此时模型的决策树还是空的,因为还没有用数据训练它,我们调用决策树的.fit()函数,来执行决策树分类算法,执行结束后,决策树就生成了。后面的代码就是将决策树可视化显示出来,导出为 iris.pdf 文件,打开后可以看到决策树的结构如下:

解释一下决策树上的信息,每个中间节点代表一个分裂规则,每个叶子节点代表一个最终的分类结果。samples是该节点的样本数,value表示三个类别的样本数,class表示这个节点最终判决的类别(即该节点上样本最多的类别)。

可以看到,叶子节点上,value的3个值只有1个非0,这表明叶子节点上只有一种类别的数据了,模型100%分类正确,所以叶子节点就停止继续分裂了,这就是前面的停止准则 stop_critera。

决策树模型和手写规则的区别

决策树模型可以看做一条条规则,前面单变量模型的规则非常简单,后面的多变量模型规则更加负责,但最终,我们发现每个叶子节点对应的规则,都可以用多个条件组合来描述出来。比如多变量模型中最左边的绿色的叶子节点可以写成:

petal length > 2.45 and petal width <= 1.75 and petal length <= 4.95 and petal width <=1.65

那么,决策树模型岂不是跟手写规则没啥区别?

事实上,决策树模型跟手写的if…else规则本质上存在以下区别:

  1. 决策树模型的规则是从数据中拟合出来的,自动生成的;而手写规则是根据经验或数据分析总结出来的规则。
  2. 理论上决策树模型的规则比手写规则准确性会更高,实际问题中,决策树对应的规则也更加复杂,手写规则很难做到同等复杂度。
  3. 决策树的规则更加灵活,当数据发生改变时,不需要人工重新编写和调整规则,可以跟随数据变化,重新调用一下fit即可,灵活性更好。

动手实践

  • 安装sklearn软件包:pip install scikit-learn
  • 使用sklearn软件包,复现决策树分类算法的代码
  • 决策树模型 DecisionTreeClassifier 有个max_depth参数,用于控制决策树的最大深度,设置这个参数的值为不同的值如2,4,6,观察一下生成的决策树的区别

0 0 投票数
文章评分
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x