当前位置:   article > 正文

机器学习入门教程_机器学习 教程

机器学习 教程

从这篇博客开始,我将比较系统的讲解机器学习的相关知识,博客内容来自本人看过的书籍,视频教程(占大多数),其它博客,以及自己在实际项目过程中的思考和总结。写博客的初衷是与大家一起交流技术,共同进步。因为本人水平有限,可能部分理解会有错误,还请读者发现之后可以及时告知,另外机器学习是一个比较大的领域,因此本系列博客会不断更新,欢迎参考阅读。

机器学习入门系列博客

涉及内容

  • KNN算法
  • 线性回归算法
  • 多项式回归算法
  • 逻辑回归算法
  • 模型正则化
  • PCA 算法
  • 支撑向量机(SVM)
  • 决策树
  • 随机森林
  • 集成学习
  • 模型选择方法
  • 模型调整方法

机器学习相关概念

1 机器学习过程

输入大量的学习资料(数据) 到机器学习算法中,产生一个模型,模型可以对输入的样例进行预测,得到预测的结果。图示如下:

输入大量学习资料
机器学习算法
产生的模型
输出结果
输入样例

2 数据相关

  • 数据集:数据的整体叫做数据集 (dataset)
  • 样本: 每一行数据称为一个样本(sample)
  • 特征:样本中每一项代表样本的一个特征 (feature)
  • 示例:
    数学语文英语级别
    10095100学神
    909090学霸
    858585优秀
    807570普通
    606060学渣
    上面是一个成绩评定标准表格,表格整体就是一个数据集,每一行代表一个样本,除了最后的级别列,每一列代表一个特征。最后一列通常称为标记(label) 。
    通常将除了 最后的级别列的数据集用一个矩阵 X 表示 ,而标记列(本例中就是级别列)用y 表示。(通常用大写字母表示矩阵,用小写字母表示向量)第i 个样本写作 X ( i ) X^{(i)} X(i) ,第i 个样本的第j 个特征值为 X j ( i ) X^{(i)}_j Xj(i)。第i个样本的标记为 y i y^i yi
  • 特征空间:每一个样本 X ( i ) X^{(i)} X(i) 都是由特征组成的空间的一个点,这个空间就是特征空间。
  • 注意事项:
    在上面的例子中,每一个样本的特征都有具体的语义(代表着各个科目的成绩信息),其实特征可以很抽象,例如图像数据,每一个像素点都可以作为一个特征,比如 28 × 28 大小的手写数字黑白图片,一共有 784 个特征(彩色图像特征会更多)。直观上,图片上某一位置的像素值大小与最终图像绘制的是数字几好像没有什么关系,但是机器学习的神奇之处就是可以通过一些方法来学到某种关系,这在人类看来是不好解释的。

3 机器学习的基本任务

(1)分类任务

例如判断一张图像是猫还是狗,手写数字图像上的数字是几,一封邮件是否是垃圾邮件等

  • 二分类任务
    就是二选一的分类,最终的类别只有两个,例如垃圾邮件识别,猫狗识别,肿瘤良性还是恶性,预测股票涨还是跌等。
  • 多分类任务
    最终的列别不是简单的两个,而是有多个,最典型的就是手写数字识别,最终的类别有 10 个,0-9 。复杂图像识别,成绩评级(A-D)等。
  • 多标签分类
    简单的分类任务只能将一张图像分类到一个类别中。目前比较前沿的研究方向是多标签分类,可以将一张图像分类到多个类别中,例如一张有猫和狗的图像,既可以分类到猫类,也可以分类到狗类;一张人打网球的图片可以提取出 网球,人,网球衣,网球场地等类别,这种多标签分类可以提取出图片的语义信息。
(2)回归任务
  • 结果是一个连续数字的值,并非一个类别。例如房屋价格预测,市场分析预测,学生成绩预测,股票价格预测。
  • 一些回归任务可以简化为分类任务,例如预测学生成绩,简化为预测学生成绩的级别,是A,B,还是C等。

4 机器学习算法的分类

在机器学习算法方面,可以将来机器学习分为四类:监督学习,非监督学习,半监督学习,增强学习

  • 监督学习:

    给机器的训练数据拥有标记或者答案。例如手写数字图片,猫狗识别等。

    • 例子:图像拥有标记信息,银行积累了一定的客户信息和他们的信用卡信用情况,医院已经积累了一定的病人信息和他们最终是否患病的情况,市场积累了房屋的基本信息和最终成交的金额信息。
    • 监督主要用于分类任务和回归任务
  • 非监督学习

    给机器的训练数据没有标记信息。

    • 通常用于对没有标记的数据进行分类-聚类分析。例如电商网站在初次访问的时候用户是没有标记的,但是随着购买记录的增加,后台可以使用非监督聚类分析来将用户分类到某一群体,这一群体具有相似的购物需求或者习惯,方便后续的推荐促销等。
    • 另外一个非常重要的工作是对数据进行降维。主要包含两方面内容,首先是特征提取(输入数据可能有很多的特征,但是并不是所有特征都对最终预测结果有用,比如一个预测学生学习成绩的模型, 人的高矮胖瘦显然与最终成绩无关,舍弃掉这些无用特征,保留有用特征的过程就是特征提取。) 然后是特征压缩,并不扔掉特征,利用特征之前的相关性,在尽可能不损失的情况下将高维特征用低维特征表示。典型方法是 PCA。
    • 除此之外,非监督方法还可以进行异常检测,检测出训练数据中对于提取一般特征无用的异常点,予以去除,利于训练(但是会有一些由于特殊点存在造成的准确度损失)
  • 半监督学习

    一部分数据有标记或者答案,另一部分数据没有。这种情况更加常见

    • 通常使用无监督学习手段对数据做处理,之后使用监督学习手段做模型的训练和预测。
  • 增强学习

    根据周围环境的情况,采取行动,根据采取行动的结果,学习行动方式。
    在这里插入图片描述
    增强学习非常适合于机器人,无人驾驶等领域,是比较前沿的研究方向。

5 机器学习算法的其它分类

(1)批量学习和在线学习
  • 批量学习 Batch Learning
    又称为离线学习,事先收集一批训练数据,训练出一个模型,然后应用模型,虽然在应用的过程中又会有新的数据加入,但是模型不使用新数据来优化模型。
    • 优点: 简单。
    • 问题:无法适应环境的变化。 解决方案:如果数据变化不算太快,可以使用定时重新批量学习,过一段时间就使用新增加的数据和原来的数据重新进行批量学习。但是这样运算量巨大,同时,如果环境变化非常快, 这种也变得不可能(例如股市的预测)。
  • 在线学习 Online Learning
    输入样例得到准确结果之后反馈到模型的训练过程中,从而可以使用输入样例来继续优化模型。
    • 优点:及时反应新的环境变化
    • 缺点:新的数据带来不好的变化,解决方案通常是对数据进行监控(例如使用非监督学习来检测异常点等等)
(2)参数学习与非参数学习
  • 参数学习

    预先确定好最终模型的形式,然后对参数进行不断的学习。例如预测房价,房屋面积为 x 1 x_1 x1,房间数量为 x 2 x_2 x2,房间距离地铁站为 x 3 x_3 x3,我们假设最终房价为 y = a x 1 + b x 2 + c x 3 y=ax_1+bx_2+cx_3 y=ax1+bx2+cx3。后期的任务就是根据训练任务确定参数 a,b,c。

    特点:一旦学到了参数,就不在需要原有的数据集。

  • 非参数学习

    不对模型进行过多假设。
    非参数不是没有参数,只是不把问题理解为找到预先确定模型的最佳参数的过程。

6 机器学习数据集的拆分

以一个股票预测的任务为例,早期的方法示意如下:

股票交易数据集
机器学习算法
产生的模型
产生的模型
输出结果
真实环境下的数据

这种做法将数据集中的全部数据进行训练,训练得到的模型直接在真实环境中使用。
这会有一些问题:
(1) 首先,模型可能很差,导致预测的结果错误率比较高,在股市预测的例子中可能造成严重经济损失
(2)真实的label 有时候很难获得(例如银行评估贷款给某一用户的风险级别,要想评估风险,可能需要对用户在一定时间内的信用情况统计,需要很多时间)

改进之后的模式如下所示:

股票交易数据集
训练用的部分 x_train
测试用的部分 x_test
产生的模型
输出结果
真实环境下的数据
产生的模型
输出结果

与之前不同的是,在训练的时候, 不再是将全部的数据都用于训练,而是采用将数据集分为训练用的部分和测试用的部分。这样做的好处是可以使用测试数据对模型的好坏进行判断,在模型进入真是的环境之前可以改进模型。这也是现在主流的方法。

训练用的部分叫做训练集,测试用的部分叫做测试集,一般而言,70-80%的数据用于训练,剩下的用于测试。 需要特别注意的是,在从数据集中分离训练集和测试集的时候,不能简单的前 70% 是训练集,其它是测试集。训练集和测试集中数据的类别应该包括所有类别,并且类别的分布具有随机性。我们还以猫狗识别为例,假设有1000张猫和狗的图像,测试集和训练集中都应该包括猫和狗的图片,不能全部都是猫或者狗,否则模型训练出来效果会不好;也不能出现下面的情况:训练集或者测试集前半部分都是猫的图像,后半部分都是狗。这样的后果是模型可能会学习到隐含的数据集中类别的顺序特征,而我们不希望模型的预测结果与样本在数据集中的位置有关,所以,类别的分布要具有随机性。

基于上面的问题,下面是将来数据集分为训练集和测试集的代码实现:

def train_test_split(x,y,test_size=0.2,random_seed=None):
    """
    进行训练集和数据集的分离
    :param x: 要分离的x数据 np.array
    :param y: 要分离的y数据 np.array
    :param test_size: 测试集占用数据集的比重,范围:[0.0,1.0]
    :param random_seed:  【可选项】 随机数的种子。用于实现实验的可重复性
    :return:  返回拆分之后的数据集。
    """
    assert x.shape[0]==y.shape[0],\
    "the size of x must be equal to the y"
    assert 0.0<=test_size<=1.0,\
    "the test_size must be in the range of [0.0,1.0]"
    if random_seed:
        np.random.seed(random_seed)
    # 洗牌功能,让样本的 label类别分布具有随机性
    shuffer_indexs=np.random.permutation(len(x))
    test_num=int(test_size*len(x))
    # 取前test_num 的数据作为测试集
    x_test=x[shuffer_indexs[:test_num]]
    x_train=x[shuffer_indexs[test_num:]]
    y_test=y[shuffer_indexs[:test_num]]
    y_train=y[shuffer_indexs[test_num:]]

    return x_train,x_test,y_train,y_test
  • 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

7 机器学习算法性能评估

  • 对于一个分类任务,使用分类准确度评估即可。

    计算方式如下:
    a c c u r a c y = 分 类 正 确 的 样 本 数 量 样 本 的 总 数 accuracy=\frac{分类正确的样本数量}{样本的总数} accuracy=
    代码实现如下(python):

    import numpy as np
    def accuracy_score(y_true,y_predict):
        """
        检查最终的准确度
        :param y_true:  正确的标签数组
        :param y_predict:  预测出来的标签结果数组
        :return:  返回预测成功的准确度
        """
        assert y_true.shape[0]==y_predict.shape[0],\
        "the size of the y_true must be equal to the y_predict"
        return np.sum(y_true==y_predict)/len(y_predict)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 回归任务可以使用多种方式预测

    对于简单的线性回归任务,主要有 MAE,MSE,RMSE, R 2 R^2 R2 等评估方法

    • MAE:平均绝对误差,计算公式如下:
      M A E = 1 m ∑ i = 1 m ∣ y t e s t ( i ) − y ^ t e s t ( i ) ∣ MAE=\frac{1}{m} \sum_{i=1}^{m}
      |ytest(i)y^test(i)|
      MAE=m1i=1mytest(i)y^test(i)
    • MSE:均方误差,计算公式如下:
      M S E = 1 m ∑ i = 1 m ( y t e s t ( i ) − y ^ t e s t ( i ) ) 2 MSE=\frac{1}{m} \sum_{i=1}^m (y_{test}^{(i)} - \hat{y}_{test}^{(i)})^2 MSE=m1i=1m(ytest(i)y^test(i))2
    • RMSE:均方根误差,计算公式如下:
      R M S E = 1 m ∑ i = 1 m ( y t e s t ( i ) − y ^ t e s t ( i ) ) 2 RMSE=\sqrt{\frac{1}{m} \sum_{i=1}^m (y_{test}^{(i)} - \hat{y}_{test}^{(i)})^2} RMSE=m1i=1m(ytest(i)y^test(i))2
    • R 2 R^2 R2 误差(推荐),计算方式如下:
      R 2 = 1 − ∑ i ( y ^ ( i ) − y ( i ) ) 2 ∑ i ( y ‾ − y ( i ) ) 2 R^2=1-\frac{\sum_{i}(\hat{y}^{(i)}-y^{(i)})^2} {\sum_{i}(\overline{y}-y^{(i)})^2} R2=1i(yy(i))2i(y^(i)y(i))2
      做一下变换,其实就是:
      R 2 = 1 − M S E 方 差 R^2=1-\frac{MSE} {方差} R2=1MSE

    以上四种评估方式的实现代码如下:

    def mean_squared_error(y_true,y_predict):
        """
        根据准确值和预测值,计算均方误差并返回
        :param y_true:  准确值
        :param y_predict:  模型的预测结果
        :return:  返回的均方误差
        """
        assert y_true.shape[0]==y_predict.shape[0],\
        "the size of the y_true and the y_predict must be equal"
        return np.sum((y_true-y_predict)**2) / len(y_true)
    
    def root_mean_sqiared_error(y_true,y_predict):
        """
        根据准确值和预测值,计算均方根误差并返回
        :param y_true: 准确值
        :param y_predict: 模型的预测值
        :return: 返回均方根误差
        """
        assert y_true.shape[0] == y_predict.shape[0],\
        "the size of the y_true and the y_predict must be equal"
        return sqrt(np.sum((y_true-y_predict)**2) / len(y_true))
    
    def mean_absolute_error(y_true,y_predict):
        """
        根据准确值和预测值返回平均绝对误差
        :param y_true:  准确值
        :param y_predict:  预测值
        :return: 返回平均绝对误差
        """
        assert y_true.shape[0] == y_predict.shape[0], \
            "the size of the y_true and the y_predict must be equal"
        return np.sum(np.absolute(y_true-y_predict)) /len(y_true)
    
    def r2_score(y_true,y_predict):
        """
        计算 预测值和准确值 的 R2值。
        :param y_true:  准确值
        :param y_predict:  预测值
        :return:  返回预测值和准确值之间的 R2 值。
        """
        assert y_true.shape[0]==y_predict.shape[0],\
        "the size of the y_true and the y_predict must be equal"
        return 1-mean_squared_error(y_true,y_predict) / np.var(y_true)
    
    • 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

8 机器学习中的参数

分为模型参数和超参数

  • 模型参数:算法运行过程中需要学习的参数。

  • 超参数:在算法运行前需要人为指定的参数。

    平时听到的机器学习算法工程师所说的调参,实际上就是调整的超参数。
    需要注意的是,超参数很重要,选取不当可能会给最终的模型造成很大的影响!

    例如梯度下降法(本机器学习系列课程会详细介绍,请到相应专栏学习参考),学习率和训练次数就是超参数,假如学习率太大,无法下降到最低点,可能造成模型无法达到最优,学习率太小可能陷入局部最优解。对于训练次数,如果太少,可能还没有得到最优解程序就运行结束了,训练次数太多会使得训练耗时增加。

机器学习领域中,寻找好的超参数,也是非常重要的内容,常用的方法有:领域知识,经验数值,实验搜索等。

对于领域知识,比较专业了,这里不做介绍。对于经验数值,常见的例如卷积神经网络 的卷积核大小设置为 3, KNN算法中的K设置为 3 等。对于实验搜索,也是一种比较普遍的方法,使用循环遍历超参数取值范围的方式,找到最好的超参数。

sklearn 机器学习库中有一个很好的用于超参数搜索的类:GridSearchCV ,之后的系列教程中会演示使用方法。

9 数据归一化

在运行机器学习算法之前,一个很重要的步骤就是数据归一化过程。数据归一化可以将所有的数据映射到统一尺度,避免造成某一特征主导最终预测结果的现象。

举例说明如下:

肿瘤大小(厘米)肿瘤发现时间(天)
样本11200
样本25100

对于以上表格,在计算样本特征空间距离的时候,发现时间起到了主导作用,肿瘤大小即使变化了五倍,对于最终的距离影响也很小,这样肿瘤大小特征相当于没有起到什么作用。

常见的特征归一化是最值归一化,这种归一化可以把所有数据映射到 0 - 1 之间,计算方法如下:
x s c a l e = x − x m i n x m a x − x m i n x_{scale}=\frac{x-x_{min}}{x_{max}-x_{min}} xscale=xmaxxminxxmin
这种方法适用于分布有明显边界的情况,例如学生的成绩(0-100),图像的像素点(0-255)。显著的缺点是受 outlier 的影响交大。例如 收入的分布,绝大部分人月工资在 8000左右,但是马云爸爸可能是好几亿,马云爸爸就是一个outlier,再使用最值归一化,导致的结果是分布不均匀,绝大部分人都在 0.000008这样的范围内。

另外一种归一化方法是均值方差归一化,适用于数据分布没有明显边界,有可能存在极端数据值(例如收入分布中的马云爸爸) 的情况。这种方法把所有的数据归一化到均值为0 ,方差为 1 的分布中。推荐这种方法,因为它同样也适用于分布有明显边界的情况。计算方法如下:
x s c a l e = x − x m e a n s t d x_{scale}=\frac{x-x_{mean}}{std} xscale=stdxxmean
即样本与均值的差比上标准差。

使用Python numpy 模块实现两种归一化代码如下:

最值归一化:

import numpy as np
import matplotlib.pyplot as plt

# 模拟成绩数据,范围0-100,两个科目。
X=np.random.randint(0,100,(20,2))
X1=np.asarray(X,dtype=float)
X2=np.asarray(X,dtype=float)

# 最值归一化
X1[:,0]=(X1[:,0]-np.min(X1[:,0]) ) / ( np.max(X1[:,0])-np.min(X1[:,0]) )
X1[:,1]=(X1[:,1]-np.min(X1[:,1]) ) / ( np.max(X1[:,1])-np.min(X1[:,1]) )

# 绘制结果图像
plt.scatter(X1[:,0],X1[:,1])
plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

绘制的散点图如下:
在这里插入图片描述
均值方差归一化

import numpy as np
import matplotlib.pyplot as plt

# 模拟成绩数据,范围0-100,两个科目。
X=np.random.randint(0,100,(20,2))
X1=np.asarray(X,dtype=float)

# 均值方差归一化
X1[:,0]=(X1[:,0]-np.mean(X1[:,0]) ) / np.std(X1[:,0])
X1[:,1]=(X1[:,1]-np.mean(X1[:,1]) ) / np.std(X1[:,1])

print(np.mean(X1[:,0]))
print(np.mean(X1[:,1]))
print(np.std(X1[:,0]))
print(np.std(X1[:,1]))
# 绘制结果图像
plt.scatter(X1[:,0],X1[:,1])
plt.show()

输出结果为:
-4.718447854656915e-17
-6.938893903907228e-17
1.0
1.0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

绘制的散点图如下:
在这里插入图片描述

特别注意:以上进行归一化的过程是在训练集上进行的,那对于测试集,要怎么进行归一化呢?
有人说只需要对测试集做同样的操作就可以了,实际上这是错误的做法,产生这种想法的根本原因是没有理解测试集的概念:测试集模拟的是真实的环境, 根本无从得知有多少样本,进而不可能得到均值和方差。所以正确的做法是均值和方差都使用训练集得到的值。即对于测试集,均值方差归一化的公式为:
x s c a l e = x t e s t − m e a n _ x t r a i n s t d ( t r a i n ) x_{scale}=\frac{x_{test}-mean\_x_{train}} {std_{(train)}} xscale=std(train)xtestmean_xtrain

封装一个均值方差归一化的工具类:

import numpy as np

class StandardScaler:
    """
    均值方差归一化方法简单实现
    """
    def __init__(self):
        """
        初始化函数,创建一个 Scaler
        """
        self.mean_=None
        self.scale_=None

    def fit(self,x):
        """
        对数据的每一列进行方差和标准差的运算。
        :param x: 训练数据 np.array (注意,这里只能是训练数据)
        :return:  返回scaler本身
        """
        assert x.ndim==2,"the dimension of x must be 2"
        self.mean_=np.array([np.mean(x[:,i]) for i in range(x.shape[1])])
        self.scale_=np.array([np.std(x[:,i]) for i in range(x.shape[1])])
        return self

    def transform(self,x):
        """
        对数据进行归一化过程,并且返回归一化之后的数据集
        :param x: 可能是测试数据,也可能是训练数据,训练数据和测试数据必须使用同一种归一化的方法。
        :return:
        """
        assert x.ndim==2 ,"the dimension of x must be 2"
        assert self.mean_ is not None and self.scale_ is not None,\
        "you must fit before transform"
        assert x.shape[1]==len(self.mean_),\
        "the feature number of the x must be equal to mean_ and std_"

        res=np.empty(shape=x.shape,dtype=float)
        for col in range(x.shape[1]):
            res[:,col]=(x[:,col]-self.mean_[col]) / self.scale_[col]
        return res
  • 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
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

使用工具类对数据进行归一化操作,数据集还是选择 sklearn 中的 鸢尾花数据集

from sklearn import datasets
import matplotlib.pyplot as plt
import numpy as np
iris=datasets.load_iris()
x=iris.data
y=iris.target
# 训练集和测试集分离
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.2,random_state=666)

stand=StandardScaler()
stand.fit(x_train)
# 查看均值:
print(stand.mean_)
# 查看方差:
print(stand.scale_)  
# 对原数据进行归一化操作
x_train=stand.transform(x_train)
x_test=stand.transform(x_test)

输出结果:
[5.83416667 3.08666667 3.70833333 1.17      ]
[0.81019502 0.44327067 1.76401924 0.75317107]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

实际上,sklearn 中封装了StandardScaler工具类,直接导入即可使用,方法和上面自己实现的一致。

from sklearn.preprocessing import StandardScaler
  • 1
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/340644
推荐阅读
相关标签
  

闽ICP备14008679号