Python机器学习算法

579 阅读9分钟

「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」。

机器学习

前言

随着深度学习的崛起,机器学习在我们视线中出现的越来越频繁。我们都知道机器学习就是让机器自己从数据中学习知识。但是要更详细说怎么学习,或者学习的原理又说不出太多东西。

本文会不牵扯太对数学知识,从科普的层面来讲解机器学习的一些原理。分析机器学习中常用的几个算法,并使用 Scikit-Learn 来实现几个小例子。

机器学习

机器学习就是根据已有经验,使用一套确定的算法,让机器自动学习出一组可以很好预测未知数据的参数。其中经验就是我们的训练数据,算法就是指机器学习算法,而参数就是指我们的机器学习模型。本文的重点是介绍一些常用算法。

监督学习

机器学习的方式分为监督学习和非监督学习,今天我们主要了解监督学习相关的一些算法。所谓的监督学习就是利用一组已知类别的样本调整分类器的参数,使其达到所要求性能的过程,也称为监督训练或有教师学习。 比如我们想训练一个预测天气的模型,那我们必须要大量的数据,这些数据必须包含特征值和目标值两个部分。其中目标值就是真实的类别信息。

或者我们想要训练一个预测性别的模型,那我们就需要一组包含个人信息和性别的数据。

下面是一个例子:

姓名年龄身高体重性别
Zack2118070male
Alice2216850female
...

我们的目标就是根据上面的数据,来训练出一个满足要求的模型。

KNN 算法

KNN 算法中文名叫 K 紧邻算法,KNN 的思想非常简单,也很符合我们的常识。KNN 算法的思想就是我们在数据中,找到和输入数据最相近的 k 个,然后分别看这 k 个数据的类别。最多出现的类别次数就是我们输入的类别。

比如,我们想猜测一个朋友所在的区域。我们现在知道下面这些信息:

姓名坐标区域
zack0,0东湖区
alice6,6西湖区
atom2,2东湖区
alex2,1东湖区
rudy7,6西湖区
cloud2,3东湖区

现在我们知道 nyx 的坐标是(3,3),我们想猜测 nyx 位置。我们可以分别计算 nyx 和每个人的距离,计算公式如下:

distance=(x1x2)2+(y1y2)2distance = \sqrt{(x_1-x_2)^2+(y_1-y_2)^2}

其中 (x1,y1)(x_1, y_1)(x2,y2)(x_2,y_2) 分别是两个人的坐标。计算结果如下:

姓名距离区域
zacksqrt(18)东湖区
alicesqrt(18)西湖区
atomsqrt(2)东湖区
alexsqrt(5)东湖区
rudy5西湖区
cloud1东湖区

假如 k=1,那 cloud 离 nyx 是最近的,所以我们推测 nyx 在东湖区。假如 k=3,那 nyx 离 cloud、atom、alex 最近。它们三个都在东湖区,所以我们推测 nyx 在东湖区。在 KNN 模型中,k 值是一个超参数,需要人工来选取。下面我们看个具体的案例:

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
# 加载数据集
datasets = load_iris()
# 拆分数据集
X_train, X_test, y_train, y_test = train_test_split(
    datasets.data,
    datasets.target,
    test_size=0.2,
    random_state=1,
)
# 创建模型
knn = KNeighborsClassifier(n_neighbors=5)
# 填充数据
knn.fit(X_train, y_train)
# 预测未知数据
y_predict = knn.predict(X_test)
# 评估模型
score = knn.score(X_test, y_test)
print(score)

上面的代码中,我们使用鸢尾花数据集进行了测试,最终的准确率在 100。在创建 KNN 分类器时有几个参数可以供我们设置,这里就不一样讲解了。其中最重要的就是 n_neighbors 参数,其含义就是 KNN 中的 k 的具体值。其余的参数读者可以自行查阅。

决策树

决策树的思想也与生活非常贴近,就是通过提问来判断类别。这很像微软小冰的读心术,可以通过问 10 个以内的问题来判断你心里想的明星是谁。其实读心术就是一个多分类的问题。下面我们可以看一张图:

在这里插入图片描述

在图中我们通过两个问题来判断一个物体属于上面动物。

我们把上面提问过程形成的树叫做决策树。如果问题序列设置的够好,那么我们就能预测许多内容。下面我们看看在具体代码:

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
# 加载数据集
datasets = load_iris()
# 拆分数据集
X_train, X_test, y_train, y_test = train_test_split(
    datasets.data,
    datasets.target,
    test_size=0.2,
    random_state=1,
)
# 创建决策树模型
tree = DecisionTreeClassifier()
# 训练模型
tree.fit(X_train, y_train)
# 预测
y_predict = tree.predict(X_test)
# 评估
score = tree.score(X_test, y_test)
print(score)

在数据中有一些异常数据时,可能会导致决策树过拟合。这个适合我们可以采取一些措施来降低过拟合的可能性,这里就可以在创建 DecisionTreeClassifier 时使用 max_depth 参数来限制树的深度,这样就可以降低过拟合的可能性。

随机森林

随机森林是在决策树上建立起来的,如果我们直接训练一颗决策树,很容易出现过拟合的现象。但是我们我们训练多颗决策树,我们用多颗树的平均结果来决定最终结果,这样就能达到比较好的效果,同时也不会过拟合。下面我们同时用决策树和随机森林来对一个较大的数据集进行训练:

from sklearn.datasets import load_iris, load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
# 加载数据集
datasets = load_breast_cancer()
# 拆分数据集
X_train, X_test, y_train, y_test = train_test_split(
    datasets.data,
    datasets.target,
    test_size=0.2,
    random_state=1,
)
model1 = DecisionTreeClassifier(max_depth=3)
model2 = RandomForestClassifier(max_depth=3)
model1.fit(X_train, y_train)
model2.fit(X_train, y_train)
score1 = model1.score(X_test, y_test)
score2 = model2.score(X_test, y_test)
print(score1, score2)

这里我们使用乳腺癌的数据集,在测试时限制决策树的深度为 3,随机森林深度也为 3,最后的效果如下:

0.9210526315789473 0.9473684210526315

可以看到,随机森林的准确率要高一些。但是这不是绝对的,不过在大多数情况下随机森林的效果要更加稳定。

朴素贝叶斯

朴素贝叶斯是一个比较高效的算法,它由概率论作为理论支持,在文本分类方面有很广泛的应用。

朴素贝叶斯的关键就是贝叶斯公式:

P(BiA)=P(Bi)P(ABi)j=1nP(Bj)P(ABj)P(B_i|A) = \frac{P(B_i)P(A|B_i)}{\sum_{j=1}^nP(B_j)P(A|B_j)}

根据公式我们可以计算出在事件 A 发生的条件下,BiB_i 发生的概率。我们可以这样理解,事件 A 为“文本中出现了‘云计算’这个词”,事件 B1B_1 为“文章为科技类”,事件 B2B_2 为“文章为教育类”。

然后我们可以分别计算出两者概率,如果 P(B1A)P(B_1|A) 大于 P(B2A)P(B_2|A),那我们就推测文章是科技类。

下面我们来看看具体代码:

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
# 加载数据集
datasets = load_breast_cancer()
# 拆分数据集
X_train, X_test, y_train, y_test = train_test_split(
    datasets.data,
    datasets.target,
    test_size=0.2,
    random_state=1,
)
nb = MultinomialNB()
nb.fit(X_train, y_train)
score = nb.score(X_test, y_test)
print(score)

这里和上面几个都差不多。

支持向量机

支持向量机的数学理论非常复杂,简单来说就是在特征空间中寻找几个特征的向量,我们把这些向量叫做支持向量。然后用支持向量来选择决策边界,从而达到分类的效果。下面是具体代码:

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.svm import LinearSVC
# 加载数据集
datasets = load_breast_cancer()
# 拆分数据集
X_train, X_test, y_train, y_test = train_test_split(
    datasets.data,
    datasets.target,
    test_size=0.2,
    random_state=1,
)
svm = LinearSVC()
svm.fit(X_train, y_train)
score = svm.score(X_test, y_test)
print(score)

在使用支持向量机时,我们需要选取比较适合的核。在创建 SVC 时可以通过 kernel 参数来进行控制。这里可以选择线性核函数(linear)、多项式核函数(poly)、径向基核函数(rbf)、sigmoid 核函数(sigmoid),我们需要根据应用场景来选取。

线性回归

线性回归是我们比较熟悉的一种模型,在中学经常会遇到线性回归的问题。在中学的应用题中,经常会告诉我们两组数据,然后让我们预测其它组数据。对于一个一元函数,我们可以设一个方程:

y=kx+by = kx + b

然后通过两组数据,我们可以分别解出 k 和 b 的值,这样我们就可以预测其它组数据。线性回归的思想就是找到一个这样的方程,只不过我们要找的是多元方程,而且也不是通过解方程组的方式来寻找。

下面是具体代码:

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
# 加载数据集
datasets = load_breast_cancer()
# 拆分数据集
X_train, X_test, y_train, y_test = train_test_split(
    datasets.data,
    datasets.target,
    test_size=0.2,
    random_state=1,
)
lr = LinearRegression()
lr.fit(X_train, y_train)
score = lr.score(X_test, y_test)
print(score)
print(lr.intercept_, lr.coef_)

在训练好模型后,我们可以通过 intercept_ 和 coef_ 查看模型的参数。

逻辑回归

在通常情况下,线性回归更适合线性问题,而逻辑回归则是在线性回归的基础上添加了激活函数。原本我们线性回归得到的结果范围是连续的实数,而分类问题通常结果要是整数。这个时候就可以通过激活函数,将实数映射到整数上。比如我们得到的结果为 2.7,我们就把它映射到 3。如果结果是 1.2,我们就把结果映射到 1。这样就可以用线性模型解决分类问题,具体代码如下:

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
# 加载数据集
datasets = load_iris()
# 拆分数据集
X_train, X_test, y_train, y_test = train_test_split(
    datasets.data,
    datasets.target,
    test_size=0.2,
    random_state=1,
)
lr = LogisticRegression(max_iter=3000)
lr.fit(X_train, y_train)
score = lr.score(X_test, y_test)
print(score)

神经网络

神经网络其实可以看作是一系列的逻辑回归,逻辑回归的图像可以表示如下:

在这里插入图片描述

其中δ就是激活函数。然后我们用多个逻辑回归组合在一起,就成了下面的模样:

在这里插入图片描述

这也就是我们常说的神经网络。具体代码如下:

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
# 加载数据集
datasets = load_iris()
# 拆分数据集
X_train, X_test, y_train, y_test = train_test_split(
    datasets.data,
    datasets.target,
    test_size=0.2,
    random_state=1,
)
mlp = MLPClassifier(solver='lbfgs', hidden_layer_sizes=[4, 2], random_state=0)
mlp.fit(X_train, y_train)
score = mlp.score(X_test, y_test)
print(score)

在创建神经网络时,我们可以通过 hidden_layer_sizes 参数来控制网络的深度和宽度。其中 [4, 2] 表示第一层有 4 个神经元,第二次有 2 个神经元。我们可以按照需要设置深度和层次,不过第一层必须和特征数量相同,最后一层必须和类别数相同。


本文首发于 GitChat,未经授权不得转载,转载需与 GitChat 联系。