「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」。
机器学习
前言
随着深度学习的崛起,机器学习在我们视线中出现的越来越频繁。我们都知道机器学习就是让机器自己从数据中学习知识。但是要更详细说怎么学习,或者学习的原理又说不出太多东西。
本文会不牵扯太对数学知识,从科普的层面来讲解机器学习的一些原理。分析机器学习中常用的几个算法,并使用 Scikit-Learn 来实现几个小例子。
机器学习
机器学习就是根据已有经验,使用一套确定的算法,让机器自动学习出一组可以很好预测未知数据的参数。其中经验就是我们的训练数据,算法就是指机器学习算法,而参数就是指我们的机器学习模型。本文的重点是介绍一些常用算法。
监督学习
机器学习的方式分为监督学习和非监督学习,今天我们主要了解监督学习相关的一些算法。所谓的监督学习就是利用一组已知类别的样本调整分类器的参数,使其达到所要求性能的过程,也称为监督训练或有教师学习。 比如我们想训练一个预测天气的模型,那我们必须要大量的数据,这些数据必须包含特征值和目标值两个部分。其中目标值就是真实的类别信息。
或者我们想要训练一个预测性别的模型,那我们就需要一组包含个人信息和性别的数据。
下面是一个例子:
姓名 | 年龄 | 身高 | 体重 | 性别 |
---|---|---|---|---|
Zack | 21 | 180 | 70 | male |
Alice | 22 | 168 | 50 | female |
... |
我们的目标就是根据上面的数据,来训练出一个满足要求的模型。
KNN 算法
KNN 算法中文名叫 K 紧邻算法,KNN 的思想非常简单,也很符合我们的常识。KNN 算法的思想就是我们在数据中,找到和输入数据最相近的 k 个,然后分别看这 k 个数据的类别。最多出现的类别次数就是我们输入的类别。
比如,我们想猜测一个朋友所在的区域。我们现在知道下面这些信息:
姓名 | 坐标 | 区域 |
---|---|---|
zack | 0,0 | 东湖区 |
alice | 6,6 | 西湖区 |
atom | 2,2 | 东湖区 |
alex | 2,1 | 东湖区 |
rudy | 7,6 | 西湖区 |
cloud | 2,3 | 东湖区 |
现在我们知道 nyx 的坐标是(3,3),我们想猜测 nyx 位置。我们可以分别计算 nyx 和每个人的距离,计算公式如下:
其中 和 分别是两个人的坐标。计算结果如下:
姓名 | 距离 | 区域 |
---|---|---|
zack | sqrt(18) | 东湖区 |
alice | sqrt(18) | 西湖区 |
atom | sqrt(2) | 东湖区 |
alex | sqrt(5) | 东湖区 |
rudy | 5 | 西湖区 |
cloud | 1 | 东湖区 |
假如 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
可以看到,随机森林的准确率要高一些。但是这不是绝对的,不过在大多数情况下随机森林的效果要更加稳定。
朴素贝叶斯
朴素贝叶斯是一个比较高效的算法,它由概率论作为理论支持,在文本分类方面有很广泛的应用。
朴素贝叶斯的关键就是贝叶斯公式:
根据公式我们可以计算出在事件 A 发生的条件下, 发生的概率。我们可以这样理解,事件 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),我们需要根据应用场景来选取。
线性回归
线性回归是我们比较熟悉的一种模型,在中学经常会遇到线性回归的问题。在中学的应用题中,经常会告诉我们两组数据,然后让我们预测其它组数据。对于一个一元函数,我们可以设一个方程:
然后通过两组数据,我们可以分别解出 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 联系。