Eastsheng's Wiki

机器学习基础-1

2022-09-22 12:18:28

[toc]

Machine Learning with Python

机器学习前期基础

监督/无监督学习算法

  • 从输入/ 输出对中进行学习的机器学习算法叫作监督学习算法(supervised learning algorithm)
  • 无监督学习算法(unsupervised learning algorithm):在无监督学习中,只有输入数据是已知的,没有为算法提供输出数据。虽然这种算法有许多成功的应用,但理解和评估这些算法往往更加困难。
    1. 无论是监督学习任务还是无监督学习任务,将输入数据表征为计算机可以理解的形式都是十分重要的。
    2. 每当想要根据给定输入预测某个结果,并且还有输入/ 输出对的示例时,都应该使用监督学习。

数据

  • 将数据想象成表格:每一行被称为一个样本(sample)或数据点;而每一列(用来描述这些实体的属性)则被称为特征(feature)。

工具

机器学习步骤

构建一个机器学习模型

  • 一部分数据用于构建机器学习模型,叫作训练数据(training data)训练集(training set)
  • 其余的数据用来评估模型性能,叫作测试数据(test data)测试集(test set)留出集(hold-out set)

    一般将75%的行数据及对应标签作为训练集,剩下25%的数据及其标签作为测试集。
    scikit-learn 中的train_test_split 函数

    1
    2
    3
    4
    5
    6
    7
    from sklearn.datasets import load_iris
    iris_dataset = load_iris()
    # 训练集、测试集划分函数
    from sklearn.model_selection import train_test_split
    # train_test_split 函数利用伪随机数生成器将数据集打乱,确保测试集中包含所有类别的数据。
    X_train, X_test, y_train, y_test = train_test_split(
    iris_dataset["data"],iris_dataset["target"],random_state=0)

观测数据

算法

  • 分类算法:如,k近邻算法
    1
    2
    3
    # 引入K近邻分类器,并实例化,给出近邻数目
    from sklearn.neighbors import KNeighborsClassifier
    knn = KNeighborsClassifier(n_neighbors = 1)

训练

1
knn.fit(X_train,y_train)

预测@评估

1
2
3
4
5
6
7
8
9
10
11
12
y_pred = knn.predict(X_test)

print("测试集预测:\n{}".format(y_pred))
print("测试集精度:\n{}".format(np.mean(y_pred == y_test)))

# 预测某一朵花的数据
# sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)'
X_new = np.array([5.6,2.9,1,0.5]).reshape(1,-1)

y_pred_new = knn.predict(X_new)
print("预测:{}".format(y_pred_new))
print("预测花种类:{}".format(iris_dataset["target_names"][y_pred_new]))

监督学习

  • 监督机器学习问题主要有两种,分别叫作分类(classification)回归(regression)

    如果在可能的结果之间具有连续性,那么它就是一个回归问题

graph LR
A[监督学习]==>B(分类)
A==>C(回归)
B==>D(二分类/多分类:目标是预测类别标签)
D==>E1(正类)
D==>E2(反类)
C==>F(回归任务的目标是预测一个连续值)
style A fill:#ffff

过拟合/欠拟合

  • 如果你在拟合模型时过分关注训练集的细节,得到了一个在训练集上表现很好、但不能泛化到新数据上的模型,那么就存在过拟合(overfitting)

  • 选择过于简单的模型被称为欠拟合(underfitting)

    我们的模型越复杂,在训练数据上的预测结果就越好。但是,如果我们的模型过于复杂,我们开始过多关注训练集中每个单独的数据点,模型就不能很好地泛化到新数据上。

  • 正则化方法:在训练数据不够多时,或者overtraining时,常常会导致过拟合(overfitting)。正则化方法即为在此时向原始模型引入额外信息,以便防止过拟合和提高模型泛化性能的一类方法的统称。在实际的深度学习场景中我们几乎总是会发现,最好的拟合模型(从最小化泛化误差的意义上)是一个适当正则化的大型模型。

监督学习算法

KNN:KNeighbors

> KNeighbors 分类器有2 个重要参数:邻居个数与数据点之间距离的度量方法;
> 虽然k 近邻算法很容易理解,但由于预测速度慢且不能处理具有很多特征的数据集,所以

在实践中往往不会用到

  • 以n_neighbors 为自变量,对比训练集精度和测试集精度
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    from sklearn.datasets import load_breast_cancer
    from sklearn.model_selection import train_test_split
    from sklearn.neighbors import KNeighborsClassifier
    import matplotlib.pyplot as plt
    cancer = load_breast_cancer()
    X_train, X_test, y_train, y_test = train_test_split(
    cancer.data, cancer.target, stratify=cancer.target, random_state=66)
    training_accuracy = []
    test_accuracy = []
    # n_neighbors取值从1到10
    neighbors_settings = range(1, 11)
    for n_neighbors in neighbors_settings:
    # 构建模型
    clf = KNeighborsClassifier(n_neighbors=n_neighbors)
    clf.fit(X_train, y_train)
    # 记录训练集精度
    training_accuracy.append(clf.score(X_train, y_train))
    # 记录泛化精度
    test_accuracy.append(clf.score(X_test, y_test))
    plt.plot(neighbors_settings, training_accuracy, label="training accuracy")
    plt.plot(neighbors_settings, test_accuracy, label="test accuracy")
    plt.ylabel("Accuracy")
    plt.xlabel("n_neighbors")
    plt.legend()
    plt.show()

线性模型

  • 线性模型利用输入特征的线性函数(linear function)进行预测
用于回归的线性模型:
> ![](https://gitee.com/eastsheng/VnoteFigures/raw/master/worknotes/notes/coding/python/machinelearning/mlpython-1.md/331633410220872.png)
> 用于回归的线性模型可以表示为这样的回归模型:对单一特征的预测结果是一条直线,两

个特征时是一个平面,或者在更高维度(即更多特征)时是一个超平面。
> 有许多不同的线性回归模型。这些模型之间的区别在于如何从训练数据中学习参数w 和
b,以及如何控制模型复杂度

线性回归(普通最小二乘法)
> 线性回归,或者普通最小二乘法(ordinary least squares,OLS),是回归问题最简单也最经

典的线性方法。
> 线性回归没有参数,这是一个优点,但也因此无法控制模型的复杂度。
> 均方误差(mean squared error)是预测值与真实值之差的平方和除
以样本数
> 线性回归寻找参数w 和b,使得对训练集的预测值与真实的回归目标值y
之间的均方误差最小
> 训练集和测试集之间的性能差异是过拟合的明显标志

  • 线性回归使用例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
import mglearn

X, y = mglearn.datasets.make_wave(n_samples=60)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
lr = LinearRegression().fit(X_train, y_train)

"""scikit-learn
总是将从训练数据中得出的值保存在以下划线结尾的属性中。这是为了将其
与用户设置的参数区分开。"""

print("lr.coef_: {}".format(lr.coef_)) # 斜率
print("lr.intercept_: {}".format(lr.intercept_)) # 截距
print("Training set score: {:.2f}".format(lr.score(X_train, y_train)))
print("Test set score: {:.2f}".format(lr.score(X_test, y_test)))
岭回归(ridge regression)
  • 岭回归也是一种用于回归的线性模型,因此它的预测公式与普通最小二乘法相同

    但在岭回归中,对系数(w)的选择不仅要在训练数据上得到好的预测结果,而且还要拟合附加约束。我们还希望系数尽量小。换句话说,w 的所有元素都应接近于0。
    这种约束是所谓正则化(regularization)的一个例子
    岭回归用到的这种被称为L2 正则化
    Ridge 是一种约束更强的模型,所以更不容易过拟合。

  • 岭回归使用例子:

    1
    2
    3
    from sklearn.linear_model import Ridge
    ridge = Ridge(alpha=1.0).fit(X_train, y_train)
    print("Training set score: {:.2f}".format(ridge.score(X_train, y_trai

    Ridge 模型在模型的简单性(系数都接近于0)与训练集性能之间做出权衡。简单性和训练
    集性能二者对于模型的重要程度可以由用户通过设置alpha 参数来指定
    默认alpha=1.0
    增大alpha 会使得系数更加趋向于0,从而降低训练集性能,但可能会提高泛化性能。

  • 如果数据点少,线性回归可能学不到任何内容。随着模型可用的数据越来越多,两个模型的性能都在提升,最终线性回归的性能追上了岭回归

  • 如果有足够多的训练数据,正则化变得不那么重要,并且岭回归和线性回归将具有相同的性能.

lasso
  • 除了Ridge,还有一种正则化的线性回归是Lasso。与岭回归相同,使用lasso 也是约束系数使其接近于0,但用到的方法不同,叫作L1 正则化。

    L1 正则化的结果是,使用lasso 时某些系数刚好为0。这说明某些特征被模型完全忽略。

  • Lasso使用例子:

    1
    2
    3
    4
    5
    from sklearn.linear_model import Lasso
    lasso = Lasso().fit(X_train, y_train)
    print("Training set score: {:.2f}".format(lasso.score(X_train, y_train)))
    print("Test set score: {:.2f}".format(lasso.score(X_test, y_test)))
    print("Number of features used: {}".format(np.sum(lasso.coef_ != 0)))
  • 在两个模型中一般首选岭回归。但如果特征很多,你认为只有其中几个是重要的,那么选择Lasso 可能更好。

  • 如果你想要一个容易解释的模型,Lasso 可以给出更容易理解的模型,因为它只选择了一部分输入特征

用于分类的线性模型
  • 对于用于回归的线性模型,输出ŷ 是特征的线性函数,是直线、平面或超平面(对于更高维的数据集)。对于用于分类的线性模型,决策边界是输入的线性函数。换句话说,(二元)线性分类器是利用直线、平面或超平面来分开两个类别的分类器

  • 学习线性模型有很多种算法。这些算法的区别在于以下两点:

    系数和截距的特定组合对训练数据拟合好坏的度量方法;
    是否使用正则化,以及使用哪种正则化方法。

  • 最常见的两种线性分类算法:

    Logistic 回归(logistic regression):linear_model.LogisticRegression 中实现,虽然LogisticRegression的名字中含有回归(regression), 但它是一种分类算法, 并不是回归算法, 不应与LinearRegression 混淆。
    线性支持向量机(linear support vector machine, 线性SVM)

用于多分类的线性模型
  • 许多线性分类模型只适用于二分类问题,不能轻易推广到多类别问题(除了Logistic 回归)。将二分类算法推广到多分类算法的一种常见方法是“一对其余”(one-vs.-rest)方法。
优点、缺点和参数
  • 线性模型的主要参数是正则化参数

    回归模型中叫作alpha
    alpha,在LinearSVC 和Logistic-Regression 中叫作C。
    alpha 值较大或C 值较小,说明模型比较简单
    通常在对数尺度上对C 和alpha 进行搜索

  • 正则化选择:

    如果你假定只有几个特征是真正重要的,那么你应该用L1 正则化,否则应默认使用L2 正则化

  • 优点和缺点:

    线性模型的训练速度非常快,预测速度也很快
    这种模型可以推广到非常大的数据集,对稀疏数据也很有效。
    如果你的数据包含数十万甚至上百万个样本,你可能需要研究如何使用LogisticRegression 和Ridge 模型的solver=’sag’ 选项,在处理大型数据时,这一选项比默认值要更快。
    线性模型的另一个优点在于,利用我们之间见过的用于回归和分类的公式,理解如何进行预测是相对比较容易的。不幸的是,往往并不完全清楚系数为什么是这样的。
    如果特征数量大于样本数量,线性模型的表现通常都很好

朴素贝叶斯分类器

  • sklearn中一共三种朴素贝叶斯分类器:GaussianNBBernoulliNBMultinomialNB
  • GaussianNB 可应用于任意连续数据, 而BernoulliNB 假定输入数据为二分类数据,MultinomialNB 假定输入数据为计数数据(即每个特征代表某个对象的整数计数,比如一个单词在句子里出现的次数)。BernoulliNBMultinomialNB 主要用于文本数据分类。
优缺点
  • 训练速度快;但是泛化能力差点儿

决策树

决策树是广泛用于分类和回归任务的模型,本质上,它从一层层的if/else 问题中进行学习,并得出结论。

  • 使用乳腺癌数据集进行决策树学习
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    from sklearn.tree import DecisionTreeClassifier
    from sklearn.datasets import load_breast_cancer
    from sklearn.model_selection import train_test_split
    cancer = load_breast_cancer()
    X_train, X_test, y_train, y_test = train_test_split(
    cancer.data, cancer.target, stratify=cancer.target, random_state=40)
    tree = DecisionTreeClassifier(random_state=0)
    tree.fit(X_train, y_train)
    print("Accuracy on training set: {:.3f}".format(tree.score(X_train, y_train)))
    print("Accuracy on test set: {:.3f}".format(tree.score(X_test, y_test)))

    未剪枝的树容易过拟合,对新数据的泛化性能不佳。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from sklearn.tree import DecisionTreeClassifier
    from sklearn.datasets import load_breast_cancer
    from sklearn.model_selection import train_test_split
    cancer = load_breast_cancer()
    X_train, X_test, y_train, y_test = train_test_split(
    cancer.data, cancer.target, stratify=cancer.target, random_state=40)
    # tree = DecisionTreeClassifier(random_state=0)
    # tree.fit(X_train, y_train)
    tree = DecisionTreeClassifier(max_depth=4,random_state=0)
    tree.fit(X_train, y_train)
    print("Accuracy on training set: {:.3f}".format(tree.score(X_train, y_train)))
    print("Accuracy on test set: {:.3f}".format(tree.score(X_test, y_test)))

    设置max_depth=4,这意味着只可以连续问4 个问题;
    限制树的深度可以减少过拟合;
    会降低训练集的精度,但可以提高测试集的精度;
    防止过拟合有两种常见的策略:一种是及早停止树的生长,也叫预剪枝(pre-pruning);另一种是先构造树,但随后删除或折叠信息量很少的结点,也叫后剪枝(post-pruning)或剪枝(pruning)。预剪枝的限制条件可能包括限制树的最大深度、限制叶结点的最大数目,或者规定一个结点中数据点的最小数目来防止继续划分。

    • scikit-learn 的决策树在DecisionTreeRegressor 类和DecisionTreeClassifier 类中实现。scikit-learn 只实现了预剪枝,没有实现后剪枝。
生成决策树png
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.tree import export_graphviz
# import graphviz
cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
cancer.data, cancer.target, stratify=cancer.target, random_state=42)
# tree = DecisionTreeClassifier(random_state=0)
# tree.fit(X_train, y_train)
tree = DecisionTreeClassifier(max_depth=4,random_state=0)
tree.fit(X_train, y_train)
print("Accuracy on training set: {:.3f}".format(tree.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(tree.score(X_test, y_test)))
export_graphviz(tree, out_file="tree.dot", class_names=["malignant","benign"],
feature_names=cancer.feature_names, impurity=False, filled=True)
# with open("tree.dot") as f:
# dot_graph = f.read()
# graphviz.Source(dot_graph)
  • 安装graphviz
  • 转换dot to png

    tree

    1
    dot -Tpng tree.dot -o tree.png
    ~p53

决策树集成

决策树的主要缺点在于,即使做了预剪枝,它也经常会过拟合,泛化性能很差。因此,在大多数应用中,往往使用集成方法来替代单棵决策树。
集成(ensemble)是合并多个机器学习模型来构建更强大模型的方法
有两种常用集成模型:分别是随机森林(random forest)和梯度提升决策树(gradient boosted decision tree)。

1
2
3
4
5
from sklearn.ensemble import RandomForestClassifier
forest = RandomForestClassifier(n_estimators=100, random_state=0) # n_estimators为随机森林树的数目
from sklearn.ensemble import GradientBoostingClassifier
gbrt = GradientBoostingClassifier(random_state=0, max_depth=1) # 可以通过max_depth学习深度调控过拟合
gbrt = GradientBoostingClassifier(random_state=0, learning_rate=0.01) # learning_rate学习率,为纠正上一棵树的错误的强度
  • 梯度提升决策树是监督学习中最强大也最常用的模型之一,主要缺点是需要仔细调参,而且训练时间可能会比较长
  • 梯度提升树模型的主要参数包括树的数量n_estimators 和学习率learning_rate,后者用于控制每棵树对前一棵树的错误的纠正强度。这两个参数高度相关,因为learning_rate 越低,就需要更多的树来构建具有相似复杂度的模型
  • 随机森林的n_estimators 值总是越大越好,但梯度提升不同,增大n_estimators 会导致模型更加复杂,进而可能导致过拟合。通常的做法是根据时间和内存的预算选择合适的n_estimators,然后对不同的learning_rate 进行遍历。
  • 另一个重要参数是max_depth(或max_leaf_nodes),用于降低每棵树的复杂度。梯度提升模型的max_depth 通常都设置得很小,一般不超过5。

核支持向量机(kernelized support vector machine)

  • 这里需要记住的是,向数据表示中添加非线性特征,可以让线性模型变得更强大
  • 核技巧(kernel trick),它的原理是直接计算扩展特征表示中数据点之间的距离(更准确地说是内积),而不用实际对扩展进行计算。
  • 一种是多项式核,在一定阶数内计算原始特征所有可能的多项式(比如feature1 ** 2 * feature2 ** 5);另一种是径向基函数(radial basis function,RBF)核,也叫高斯核。高斯核有点难以解释,因为它对应无限维的特征空间。一种对高斯核的解释是它考虑所有阶数的所有可能的多项式,但阶数越高,特征的重要性越小。

神经网络(深度学习)

  • 虽然深度学习在许多机器学习应用中都有巨大的潜力,但深度学习算法往往经过精确调整,只适用于特定的使用场景。

    多层感知机(multilayer perceptron,MLP),它可以作为研究更复杂的深度学习方法的起点。MLP 也被称为(普通)前馈神经网络,有时也简称为神经网络。
    MLP 可以被视为广义的线性模型,执行多层处理后得到结论。
    $ŷ = w[0] * x[0] + w[1] * x[1] + … + w[p] * x[p] + b$

    图中,左边的每个结点代表一个输入特征,连线代表学到的系数,右边的结点代表输出,是输入的加权求和。
    在MLP 中,多次重复这个计算加权求和的过程,首先计算代表中间过程的隐单元(hiddenunit),然后再计算这些隐单元的加权求和并得到最终结果

    这个模型需要学习更多的系数(也叫作权重):在每个输入与每个隐单元(隐单元组成了隐层)之间有一个系数,在每个隐单元与输出之间也有一个系数。

    校正非线性(rectifying nonlinearity,也叫校正线性单元或relu)或正切双曲线(tangens hyperbolicus,tanh)有了这两种非线性函数,神经网络可以学习比线性模型复杂得多的函数。

  • 这些由许多计算层组成的大型神经网络,正是术语“深度学习”的灵感来源。

  • 包含100 个隐单元的神经网络在two_moons 数据集上学到的决策边界

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    from sklearn.neural_network import MLPClassifier
    from sklearn.datasets import make_moons
    import matplotlib.pyplot as plt
    import mglearn
    from sklearn.model_selection import train_test_split
    X, y = make_moons(n_samples=100, noise=0.25, random_state=3)
    X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y,
    random_state=42)
    mlp = MLPClassifier(solver='lbfgs', hidden_layer_sizes=[100],random_state=0).fit(X_train, y_train)
    mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3)
    mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train)
    plt.xlabel("Feature 0")
    plt.ylabel("Feature 1")
    plt.show()
  • Figure_1

  • 控制神经网络复杂度的方法有很多种:隐层的个数每个隐层中的单元个数正则化(alpha)等

总结

  • 最近邻

    适用于小型数据集,是很好的基准模型,很容易解释。

  • 线性模型

    非常可靠的首选算法,适用于非常大的数据集,也适用于高维数据。

  • 朴素贝叶斯

    只适用于分类问题。比线性模型速度还快,适用于非常大的数据集和高维数据。精度通常要低于线性模型。

  • 决策树

    速度很快,不需要数据缩放,可以可视化,很容易解释。

  • 随机森林

    几乎总是比单棵决策树的表现要好,鲁棒性很好,非常强大。不需要数据缩放。不适用于高维稀疏数据。

  • 梯度提升决策树

    精度通常比随机森林略高。与随机森林相比,训练速度更慢,但预测速度更快,需要的内存也更少。比随机森林需要更多的参数调节。

  • 支持向量机

    对于特征含义相似的中等大小的数据集很强大。需要数据缩放,对参数敏感。

  • 神经网络

    可以构建非常复杂的模型,特别是对于大型数据集而言。对数据缩放敏感,对参数选取敏感。大型网络需要很长的训练时间。

  • 面对新数据集,通常最好先从简单模型开始,比如线性模型、朴素贝叶斯或最近邻分类器,看能得到什么样的结果。对数据有了进一步了解之后,你可以考虑用于构建更复杂模型的算法,比如随机森林、梯度提升决策树、SVM 或神经网络。

无监督学习

  • 无监督学习的一个主要挑战就是评估算法是否学到了有用的东西
  • 无监督算法的另一个常见应用是作为监督算法的预处理步骤

无监督学习类型

无监督变换(降维)
聚类算法