机器学习入门

机器学习教程

机器学习概述

随着大数据的发展和计算机运算能力的不断提升,人工智能在最近几年取得了令人瞩目的成就。目前在很多行业中,都有企业开始应用机器学习技术,从而获取更深刻的洞察,为企业经营或日常生活提供帮助,提升产品服务水平。机器学习已经广泛应用于数据挖掘、搜索引擎、电子商务、自动驾驶、图像识别、量化投资、自然语言处理、计算机视觉、医学诊断、信用卡欺诈检测、证券金融市场分析、游戏和机器人等领域,机器学习相关技术的进步促进了人工智能在各个领域的发展。

机器学习简介

机器学习(Machine Learning)是计算机科学的子领域,也是人工智能的一个分支和实现方式。汤姆·米切尔(TomMitchell)在1997年出版的Machine Learning一书中指出,机器学习这门学科所关注的是计算机程序如何随着经验积累,自动提高性能。他同时给出了形式化的描述:对于某类任务T和性能度量P,如果一个计算机程序在T上以P衡量的性能随着经验E而自我完善,那么就称这个计算机程序在从经验E学习。

机器学习理论基础

机器学习主要的理论基础涉及概率论、数理统计、线性代数、数学分析、数值逼近、最优化理论和计算复杂理论等,其核心要素是数据、算法和模型。

机器学习、人工智能和数据挖掘

目前人工智能很热门,但是很多人容易将人工智能与机器学习混淆。此外,数据挖掘、人工智能和机器学习之间的关系也容易被混淆。从本质上看,数据科学的目标是通过处理各种数据促进人们的决策,机器学习的主要任务是使机器模仿人类的学习,从而获得知识。而人工智能借助机器学习和推理最终是形成具体的智能行为。

什么是人工智能

人工智能是让机器的行为看起来像人所表现出的智能行为一样,这是由麻省理工学院的约翰·麦卡锡在1956年的达特茅斯会议上提出的,字面上的意思是为机器赋予人的智能。人工智能的先驱们希望机器具有与人类似的能力:感知、语言、思考、学习、行动等。最近几年人工智能风靡全球的主要原因就是,随着机器学习的发展,人们发现机器具有了一定的感知(图像识别)和学习等方面的能力,很容易认为目前已经达到了人工智能发展过程中的奇点。实际上,人工智能包括计算智能、感知智能和认知智能等层次,目前人工智能还介于前两者之间。

由于目前人工智能与人类智能相比较,二者实现的原理并不相同,特别是人脑对于信息的存储和加工过程尚未被研究清楚,与目前主流的深度学习理论存在较大的基础差异。因此,目前人工智能所处的阶段还在“弱人工智能”(Narrow AI)阶段,距离“强人工智能”(General AI)阶段还有较长的路要走。例如,目前人类对于知识的获取和推理并不需要大量的数据进行反复迭代学习,只需要看一眼自行车的照片就能大致区分出各式各样的自行车。因此,要达到强人工智能的阶段可能要在计算机基础理论方面进行创新,实现类人脑的结构设计。

通常来说,人工智能是使机器具备类似人类的智能性,人工智能的典型系统包括以下几个方面。

(1)博弈游戏(如深蓝、Alpha Go、Alpha Zero等)

(2)机器人相关控制理论(运动规划、控制机器人行走等)

(3)机器翻译

(4)语音识别

(5)计算机视觉系统

(6)自然语言处理(自动程序)

什么是数据挖掘

数据挖掘使用机器学习、统计学和数据库等方法在相对大量的数据集中发现模式和知识,它涉及数据预处理、模型与推断、可视化等。数据挖掘包括以下几类常见任务。

  1. 异常检测

    异常检测(anomaly detection)是对不符合预期模式的样本、事件进行识别。异常也被称为离群值、偏差和例外等。异常检测常用于入侵检测、银行欺诈、疾病检测、故障检测等。

  2. 关联分析

    关联规则学习(Association rule learning)是在数据库中发现变量之间的关系(强规则)。例如,在购物篮分析中,发现规则{面包,牛奶}→{酸奶},表明如果顾客同时购买了面包和牛奶,很有可能也会买酸奶,利用这些规则可以进行营销。

  3. 聚类

    聚类是一种探索性分析,在未知数据结构的情况下,根据相似性把样本分为不同的簇或子集,不同簇的样本具有很大的差异性,从而发现数据的类别与结构。

  4. 分类

    分类是根据已知样本的某些特征,判断一个新样本属于哪种类别。通过特征选择和学习,建立判别函数以对样本进行分类。

  5. 回归

    回归是一种统计分析方法,用于了解两个或多个变量之间的相关关系,回归的目标是找出误差最小的拟合函数作为模型,用特定的自变量来预测因变量的值。

数据挖掘在大数据相关技术的支持下,随着数据存储(非关系型NoSQL数据库)、分布式数据计算(Hadoop/Spark等)、数据可视化等技术的发展,数据挖掘对事务的理解能力越来越强,如此多的数据堆积在一起,增加了对算法的要求,所以数据挖掘一方面要尽可能获取更多、更有价值、更全面的数据,并从这些数据中提取价值。

数据挖掘在商务智能方面的应用较多,特别是在决策辅助、流程优化、精准营销等方面。广告公司可以使用用户的浏览历史、访问记录、点击记录和购买信息等数据,对广告进行精准推广。利用舆情分析,特别是情感分析可以提取公众意见来驱动市场决策。例如,在电影推广时对社交评论进行监控,寻找与目标观众产生共鸣的元素,然后调整媒体宣传策略迎合观众口味,吸引更多人群。

机器学习、人工智能与数据挖掘的关系

机器学习是人工智能的一个分支,作为人工智能的核心技术和实现手段,通过机器学习的方法解决人工智能面对的问题。机器学习是通过一些让计算机可以自动“学习”的算法,从数据中分析获得规律,然后利用规律对新样本进行预测。

机器学习是人工智能的重要支撑技术,其中深度学习就是一个典型例子。深度学习的典型应用是选择数据训练模型,然后用模型做出预测。例如,博弈游戏系统(Deep Blue)重于探索和优化未来的解空间(Solution Space),而深度学习则是在博弈游戏算法(例如Alpha Go)的开发上付诸努力,取得了世人瞩目的成就。

机器学习算法

机器学习算法是一类从数据中自动分析获得规律,并利用规律对未知数据进行预测的方法,可以分成下面几种类别:监督学习、无监督学习、强化学习。

监督学习

是从有标记的训练数据中学习一个模型,然后根据这个模型对未知样本进行预测。其中,模型的输入是某一样本的特征,函数的输出是这一样本对应的标签。常见的监督学习算法包括回归分析统计分类。监督学习包括分类和数字预测两大类别,前者包括逻辑回归、决策树、KNN、随机森林、支持向量机、朴素贝叶斯等,后者包括线性回归、KNN、GradientBoosting和AdaBoost等。

无监督学习

又称为非监督式学习,它的输入样本并不需要标记,而是自动从样本中学习特征实现预测。常见的无监督学习算法有聚类关联分析等。

强化学习

是通过观察来学习做成什么样的动作。每个动作都会对环境有所影响,学习对象根据观察到的周围环境的反馈来做出判断。强化学习强调如何基于环境而行动,以取得最大化的预期利益。其灵感来源于心理学中的行为主义理论,即有机体如何在环境给予的奖励或惩罚的刺激下,逐步形成对刺激的预期,产生能获得最大利益的习惯性行为。

根据机器学习的任务分类,可以分为回归、分类、聚类三大常见机器学习任务。某些机器学习算法可能同时属于不同的分类,如深度学习算法可能存在于监督学习,也可能用于强化学习,在实践过程中可依据实际需要进行选择。

机器学习的一般流程

机器学习的一般流程包括确定分析目标、收集数据、整理数据、预处理数据、训练模型、评估模型、优化模型、上线部署等步骤。首先要从业务的角度分析,然后提取相关的数据进行探查,发现其中的问题,再依据各算法的特点选择合适的模型进行实验验证,评估各模型的结果,最终选择合适的模型进行应用。

  1. 分析目标

    应用机器学习解决实际问题,首先要明确目标任务,这是机器学习算法选择的关键。明确要解决的问题和业务需求,才可能基于现有数据设计或选择算法。例如,在监督式学习中对定性问题可用分类算法,对定量分析可用回归方法。在无监督式学习中,如果有样本细分则可应用聚类算法,如需找出各数据项之间的内在联系,可应用关联分析。

  2. 收集数据

    数据要有代表性并尽量覆盖领域,否则容易出现过拟合欠拟合。对于分类问题,如果样本数据不平衡,不同类别的样本数量比例过大,都会影响模型的准确性。还要对数据的量级进行评估,包括样本量和特征数,可以估算出数据以及分析对内存的消耗,判断训练过程中内存是否过大,否则需要改进算法或使用一些降维技术,或者使用分布式机器学习技术。

  3. 整理预处理

    获得数据以后,不必急于创建模型,可先对数据进行一些探索,了解数据的大致结构、数据的统计信息、数据噪声以及数据分布等。在此过程中,为了更好地查看数据情况,可使用数据可视化方法或数据质量评价对数据质量进行评估。

    通过数据探索后,可能发现不少问题,如缺失数据、数据不规范、数据分布不均衡、数据异常、数据冗余等。这些问题都会影响数据质量。为此,需要对数据进行预处理,这部分工作在机器学习中非常重要,特别是在生产环境中的机器学习,数据往往是原始、未加工和处理过的,数据预处理常常占据整个机器学习过程的大部分时间。归一化、离散化、缺失值处理、去除共线性等,是机器学习的常用预处理方法。

  4. 数据建模

应用特征选择方法,可以从数据中提取出合适的特征,并将其应用于模型中得到较好的结果。筛选出显著特征需要理解业务,并对数据进行分析。特征选择是否合适,往往会直接影响模型的结果,对于好的特征,使用简单的算法也能得出良好、稳定的结果。特征选择时可应用特征有效性分析技术,如相关系数、卡方检验、平均互信息、条件熵、后验概率和逻辑回归权重等方法。

训练模型前,一般会把数据集分为训练集和测试集,或对训练集再细分为训练集和验证集,从而对模型的泛化能力进行评估。

模型本身并没有优劣。在模型选择时,一般不存在对任何情况都表现很好的算法,这又称为“没有免费的午餐”原则。因此在实际选择时,一般会用几种不同方法来进行模型训练,然后比较它们的性能,从中选择最优的一个。不同的模型使用不同的性能衡量指标。

  1. 模型训练

在模型训练过程中,需要对模型超参进行调优,如果对算法原理理解不够透彻,往往无法快速定位能决定模型优劣的模型参数,所以在训练过程中,对机器学习算法原理的要求较高,理解越深入,就越容易发现问题的原因,从而确定合理的调优方案。

  1. 模型评估

    使用训练数据构建模型后,需使用测试数据对模型进行测试和评估,测试模型对新数据的泛化能力。如果测试结果不理想,则分析原因并进行模型优化,如采用手工调节参数等方法。如果出现过拟合,特别是在回归类问题中,则可以考虑正则化方法来降低模型的泛化误差。可以对模型进行诊断以确定模型调优的方向与思路,过拟合、欠拟合判断是模型诊断中重要的一步。常见的方法有交叉验证、绘制学习曲线等。过拟合的基本调优思路是增加数据量,降低模型复杂度。欠拟合的基本调优思路是提高特征数量和质量,增加模型复杂度。

    误差分析是通过观察产生误差的样本,分析误差产生的原因,一般的分析流程是依次验证数据质量、算法选择、特征选择、参数设置等,其中对数据质量的检查最容易忽视,常常在反复调参很久后才发现数据预处理没有做好。一般情况下,模型调整后,需要重新训练和评估,所以机器学习的模型建立过程就是不断地尝试,并最终达到最优状态,从这一点看,机器学习具有一定的艺术性。

    在工程实现上,提升算法准确度可以通过特征清洗和预处理等方式,也可以通过模型集成的方式。一般情况下,直接调参的工作不会很多。毕竟大量数据训练起来很慢,而且效果难以保证。

  2. 模型应用

模型应用主要与工程实现的相关性比较大。工程上是结果导向,模型在线上运行的效果直接决定模型的好坏,不单纯包括其准确程度、误差等情况,还包括其运行的速度(时间复杂度)、资源消耗程度(空间复杂度)、稳定性是否可接受等方面。

机器学习的典型应用

股价预测、推荐引擎、自然语言识别、语音识别、图像识别、人脸识别

机器学习的基本问题

1)回归(Regression)问题:根据已知的输入和输出寻找某种性能最佳的模型,将未知输出的输入代入模型,得到连续的输出。

2)分类(Classification)问题:根据已知的输入和输出寻找某种性能最佳的模型,将未知输出的输入代入模型,得到离散的输出。

3)聚类问题:根据已知输入的相似程度,将其划分为不同的群落。

4)降维问题:在性能损失尽可能小的前提下,降低数据的复杂度。

特征工程

​ 在实际工作中获取到的数据往往不那么理想,可能会存在非数值类型的文本数据、重复值、缺失值、异常值及数据分布不均衡等问题,因此,在进行数学建模前还需要对这些问题进行处理,这项工作称为特征工程。特征工程通常分为特征使用方案特征获取方案特征处理特征监控几大部分,其中特征处理是特征工程的核心内容,有时称为数据预处理。

数据预处理

一行一样本,一列一特征

数据预处理的过程: 输入数据 -> 模型 -> 输出数据

使用sklearn转换器进行数据预处理与降维

sklearn转换器三个方法:

​ sklearn把相关的功能封装为转换器(transformer)。使用sklearn转换器能够实现对传入的NumPy数组进行标准化处理,归一化处理,二值化处理,PCA降维等操作。转换器主要包括三个方法:

方法名称 说明
fit fit方法主要通过分析特征和目标值,提取有价值的信息,这些信息可以是统计量,也可以是权值系数等。
transform transform方法主要用来对特征进行转换。
fit_transform fit_transform方法就是先调用fit方法,然后调用transform方法。

数据预处理相关库

# 解决机器学习问题的科学计算工具包
import sklearn.preprocessing as sp

sklearn部分预处理函数与其作用

函数名称 说明
StandardScaler 对特征进行标准差标准化。
MinMaxScaler 对特征进行离差标准化。
Normalizer 对特征进行归一化。
Binarizer 对定量特征进行二值化处理。
OneHotEncoder 对定性特征进行独热编码处理。
LabelEncoder 对特征进行标签编码。
FunctionTransformer 对特征进行自定义函数变换。

均值移除(标准差标准化)

由于一个样本的不同特征值差异较大,不利于使用现有机器学习算法进行样本处理。

标准差标准化也叫零均值标准化或分数标准化,是当前使用最广泛的数据标准化方法。经过该方法处理的数据均值移除可以让样本矩阵中的每一列的平均值为0,标准差为1。

如何使样本矩阵中的每一列的平均值为0呢?

公式:X* = (X-mean)/ std

标准差标准化不会改变数据的分布情况。

例如有一列特征值表示年龄: 17, 20, 23
mean = (17 + 20 + 23)/3 = 20
a' = -3
b' =  0
c' =  3
完成!

如何使样本矩阵中的每一列的标准差为1呢?

a' = -3
b' =  0
c' =  3
s' = std(a', b', c') 
[a'/s',  b'/s',  c'/s']

均值移除API:

import sklearn.preprocessing as sp
# scale函数用于对函数进行预处理,实现均值移除。
# array为原数组,返回A为均值移除后的结果。
A = sp.StandardScaler()
B = sp.scale(array)

案例:

import numpy as np
import sklearn.preprocessing as sp
raw_samples = np.array([
    [17., 100., 4000],
    [20., 80., 5000],
    [23., 75., 5500]])

scaler = sp.StandardScaler()
std_samples = scaler.fit_transform(raw_samples)

# std_samples = sp.scale(raw_samples)
print(std_samples)
print(std_samples.mean(axis=0))
print(std_samples.std(axis=0))

范围缩放(离差标准化)

将样本矩阵中的每一列的最小值和最大值设定为相同的区间,统一各列特征值的范围。一般情况下会把特征值缩放至[0, 1]区间。

离差标准化是对原始数据的一种线性变换,结果是将原始数据的数值映射到[0,1]区间之间。

公式:X* = ( X - min ) / ( max - min )

其中max为样本数据的最大值,min 为样本数据的最小值,max-min为极差。离差标准化保留了原始数据值之间的联系,是消除量纲和数据取值范围影响最简单的方法。

离差标准化的特点:

​ 数据的整体分布情况并不会随离差标准化而发生改变,原先取值较大的数据,在做完离差标准化后的值依旧较大。

​ 当数据和最小值相等的时候,通过离差标准化可以发现数据变为0。

​ 同时,还可以看出离差标准化的缺点:若数据集中某个数值很大,则离差标准化的值就会接近于0,并且相互之间差别不大。若将来遇到超过目前属性[min,max]取值范围的时候,会引起系统出错,这时便需要重新确定min和max。

示例:

例如有一列特征值表示年龄: [17, 20, 23]
每个元素减去特征值数组所有元素的最小值即可:[0, 3, 6]

如何使一组特征值的最大值为1呢?

[0, 3, 6]
把特征值数组的每个元素除以最大值即可:[0, 1/2, 1]

范围缩放API:

# 创建MinMax缩放器
mms = sp.MinMaxScaler(feature_range=(0, 1))
# 调用mms对象的方法执行缩放操作, 返回缩放过后的结果
result = mms.fit_transform(原始样本矩阵)

案例:

import numpy as np
import sklearn.preprocessing as sp
raw_samples = np.array([
    [17., 100., 4000],
    [20., 80., 5000],
    [23., 75., 5500]])
print(raw_samples)
mms_samples = raw_samples.copy()
for col in mms_samples.T:
    col_min = col.min()
    col_max = col.max()
    a = np.array([
        [col_min, 1],
        [col_max, 1]])
    b = np.array([0, 1])
    x = np.linalg.solve(a, b)
    col *= x[0]
    col += x[1]
print(mms_samples)
# 根据给定范围创建一个范围缩放器
mms = sp.MinMaxScaler(feature_range=(0, 1))
# 用范围缩放器实现特征值的范围缩放
mms_samples = mms.fit_transform(raw_samples)
print(mms_samples)

归一化

​ 归一化/标准化可以去除数据单位对计算带来的影响,也就是所谓的去量纲行为,归一化/标准化实质是一种线性变换,线性变换有很多良好的性质,这些性质决定了对数据改变后不会造成“失效”,反而能提高数据的表现,这些性质是归一化/标准化的前提。

归一化/标准化的去量纲作用能够带来以下两个好处:

​ 1)提升模型的精度。一些分类器需要计算样本之间的距离(如欧氏距离),例如KNN。如果一个特征值域范围非常大,那么距离计算就主要取决于这个特征,从而与实际情况相悖(比如这时实际情况是值域范围小的特征更重要)。

​ 2) 提高收敛速度。对于线性模型来说,数据归一化/标准化后,最优解的寻优过程明显会变得平缓,更容易正确的收敛到最优解。

有些情况每个样本的每个特征值具体的值并不重要,但是每个样本特征值的占比更加重要。

动作 爱情 科幻
小明 20 10 5
小王 4 2 1
小李 15 11 13

所以归一化即是用每个样本的每个特征值除以该样本各个特征值绝对值的总和。变换后的样本矩阵,每个样本的特征值绝对值之和为1。

归一化相关API:

# array 原始样本矩阵
# norm  范数
#    l1 - l1范数,向量中各元素绝对值之和
#    l2 - l2范数,向量中各元素平方之和
# 返回归一化预处理后的样本矩阵
sp.Normalize(array, norm='l2')

案例:

import numpy as np
import sklearn.preprocessing as sp
raw_samples = np.array([
    [17., 100., 4000],
    [20., 80., 5000],
    [23., 75., 5500]])
print(raw_samples)
nor_samples = raw_samples.copy()
for row in nor_samples:
    row = abs(row).sum()
print(nor_samples)
print(abs(nor_samples).sum(axis=1))
# 归一化预处理
nor_samples = sp.normalize(raw_samples, norm='l1')
print(nor_samples)
print(abs(nor_samples).sum(axis=1))

二值化

有些业务并不需要分析矩阵的详细完整数据(比如图像边缘识别只需要分析出图像边缘即可),可以根据一个事先给定的阈值,用0和1表示特征值不高于或高于阈值。二值化后的数组中每个元素非0即1,达到简化数学模型的目的。

二值化相关API:

# 给出阈值, 获取二值化器
bin = sp.Binarizer(threshold=阈值)
# 调用transform方法对原始样本矩阵进行二值化预处理操作
result = bin.transform(原始样本矩阵)

案例:

import numpy as np
import sklearn.preprocessing as sp
raw_samples = np.array([
    [17., 100., 4000],
    [20., 80., 5000],
    [23., 75., 5500]])
print(raw_samples)
bin_samples = raw_samples.copy()
bin_samples[bin_samples <= 80] = 0
bin_samples[bin_samples > 80] = 1
print(bin_samples)
# 根据给定的阈值创建一个二值化器
bin = sp.Binarizer(threshold=80)
# 通过二值化器进行二值化预处理
bin_samples = bin.transform(raw_samples)
print(bin_samples)

独热编码(onehot)

为样本特征的每个值建立一个由一个1和若干个0组成的序列,用该序列对所有的特征值进行编码。

两个数   三个数	四个数
1		3		2
7		5		4
1		8		6  
7		3		9
为每一个数字进行独热编码:
1-10    3-100	2-1000
7-01    5-010   4-0100
8-001   6-0010  9-0001
编码完毕后得到最终经过独热编码后的样本矩阵:
101001000
010100100
100010010
011000001

		    导演		   演员		上映地点	    类型
战狼1		   吴京         余男         内地         战争
钢铁侠1      钢哥	     铁蛋         美国	       科幻
战狼2        吴京         吴京         内地		    战争
             10		     100		10		      10
             01		     010		01		      01
		     10		     001		10		      10

        101001010
        010100101
		100011010 
		

		 导演		 演员		   上映地点	      类型
战狼1		吴京    吴京,余男      内地         战争 主旋律
钢铁侠1   钢哥	  铁蛋          美国	     科幻 特效
战狼2     吴京    吴京,姗姗      内地		    战争 主旋律
         10		 1100		  10			1100	
         01		 0010		  01			0011
		 10		 1001		  10			1100

独热编码相关API:

# 创建一个独热编码器
# sparse: 是否使用紧缩格式(稀疏矩阵)
# dtyle:  数据类型
ohe = sp.OneHotEncoder(sparse=是否采用紧缩格式, dtype=数据类型)
# 对原始样本矩阵进行处理,返回独热编码后的样本矩阵。
result = ohe.fit_transform(原始样本矩阵)
ohe = sp.OneHotEncoder(sparse=是否采用紧缩格式, dtype=数据类型)
# 对原始样本矩阵进行训练,得到编码字典
encode_dict = ohe.fit(原始样本矩阵)
# 调用encode_dict字典的transform方法 对数据样本矩阵进行独热编码
result = encode_dict.transform(原始样本矩阵)

案例:

import numpy as np
import sklearn.preprocessing as sp
raw_samples = np.array([
    [17., 100., 4000],
    [20., 80., 5000],
    [23., 75., 5500]])
# 创建独热编码器
ohe = sp.OneHotEncoder(sparse=False, dtype=int)
# 用独特编码器对原始样本矩阵做独热编码
ohe_dict = ohe.fit(raw_samples)
ohe_samples = ohe_dict.transform(raw_samples)

ohe_samples = ohe.fit_transform(raw_samples)
print(ohe_samples)

标签编码

根据字符串形式的特征值在特征序列中的位置,为其指定一个数字标签,用于提供给基于数值算法的学习模型。

标签编码相关API:

# 获取标签编码器
lbe = sp.LabelEncoder()
# 调用标签编码器的fit_transform方法训练并且为原始样本矩阵进行标签编码
result = lbe.fit_transform(原始样本数组)
# 根据标签编码的结果矩阵反查字典 得到原始数据矩阵
samples = lbe.inverse_transform(result)

案例:

import numpy as np
import sklearn.preprocessing as sp
raw_samples = np.array([
    'audi', 'ford', 'audi', 'toyota',
    'ford', 'bmw', 'toyota', 'ford',
    'audi'])
print(raw_samples)
lbe = sp.LabelEncoder()
lbe_samples = lbe.fit_transform(raw_samples)
print(lbe_samples)
inv_samples = lbe.inverse_transform(lbe_samples)
print(inv_samples)

Sklearn库

sklearn提供了model_selection模型选择模块,preprocessing数据预处理模块和 decompisition特征分解模块, 通过这三个模块能够实现数据的预处理与模型构建前的数据标准化,二值化,数据集的分割,交叉验证,和PCA降维等工作。

datasets模块

sklearn库的datasets模块集成了部分数据分析的经典数据集,可以使用这些数据集进行数据预处理, 建模等操作,熟悉sklearn的数据处理流程和建模流程。

加载datasets模块中数据集

数据集大小 数据集名称 调用方式 适用算法 数据规模
小数据集 波士顿房价 load_boston() 回归 5066*13
小数据集 鸢尾花 load_iris() 分类 150*4
小数据集 糖尿病数据 load_diabetes() 分类 442*10
小数据集 手写数字 load_digits() 分类 5620*64
大数据集 Olivetti脸部图像 fetch_olivetti_faces() 降维 400 * 64 * 64
大数据集 新闻分类 fetch-20newsgroups() 分类 -
大数据集 带标签的人脸 fetch_lfw_people() 分类;降维 -
大数据集 路透社新闻语料 fetch_revl() 分类 804414*47236

各种模型

回归模型

回归分析:回归分析法指利用数据统计原理,对大量统计数据进行数学处理,并确定因变量Y与某些自变量X的相关关系,建立一个相关性较好的回归方程(函数表达式),并加以外推,用于预测今后的因变量的变化分析方法。

线性回归模型,包括一元线性回归模型、多元线性回归和多项式线性回归模型等。

  • 依据定义的因变量与单个自变量可以构建如下模型:Y = w0 + w1 x (简单线性回归)
  • 依据定义的因变量与多个自变量可以构建如下模型:Y = w0 + w1x1 + w2x2 +...+wnxn(多元线性回归)
  • 依据定义的因变量与多个自变量可以构建如下模型:Y = w0 + w1x + w2x^2 + ...+ wnx^n(多项式线性回归)

核心思想:从连续型统计数据中得到数学模型,然后将该数学模型用于预测。

回归是用来估计数据元素之间的数值关系

用来处理回归问题的,主要对数值型数据进行预测

应用:如股票预测,网站点击量预测等等

一元线性回归

线性回归模型是利用线性拟合的方式探寻数据背后的规律。先通过搭建线性回归模型寻找这些散点(也称样本点)背后的趋势线(也称回归曲线),再利用回归曲线进行一些简单的预测分析或因果关系分析。

在线性回归中,我们根据特征变量(也称自变量)来预测反应变量(也称因变量)。根据特征变量的个数可将线性回归模型分为一元线性回归和多元线性回归。

一元线性回归模型又称为简单线性回归模型,其形式可以表示为:y=ax+b,其中,y为因变量,x为自变量,a为回归系数,b为截距。

示例:

输入		输出
0.5      5.0
0.6      5.5
0.8      6.0
1.1      6.8
1.4      7.0
...
y = f(x)

预测(目标)函数:y = w0+w1x
x: 输入
y: 输出
w0和w1: 模型参数

所谓模型训练,就是根据已知的x和y,找到最佳的模型参数w0 和 w1,尽可能精确地描述出输入和输出的关系。

5.0 = w0 + w1 × 0.5
5.5 = w0 + w1 × 0.6

单样本误差:

根据预测函数求出输入为x时的预测值:y' = w0 + w1x,单样本误差为1/2(y' - y)2

总样本误差:

把所有单样本误差相加即是总样本误差:1/2 Σ(y' - y)2

损失函数:

loss = 1/2 Σ(w0 + w1x - y)2

所以损失函数就是总样本误差关于模型参数的函数,该函数属于三维数学模型,即需要找到一组w0 w1使得loss取极小值。

http://bais.com.cn/static/数学/直线方程.html

核心:找到w0和w1的值,使得预测值和真实值之间的平均差异最小。

损失函数

损失:机器学习模型关于单个样本的预测值与真实值的差,损失越小,模型越好;如果预测值与真实值相等,就是没有损失。

损失函数:用于计算损失的函数模型每一次预测的好坏用损失函数来度量。

常见的损失函数:

  • 平方损失函数(Square Loss):主要用于最小二乘法(OLS),如:线性回归

  • 对数损失函数( Cross Entropy Loss ):主要用于Logistic回归

梯度下降

梯度下降 — Gradient Descent
  • 定义:梯度下降法不是一个机器学习算法,是一种基于搜索的最优化方法;
  • 功能:最优化一个损失函数;
  • 梯度上升法:最大化一个效用函数;
  • 机器学习中,熟练的使用梯度法(下降法、上升法)求取目标函数的最优解,非常重要;
  • 线性回归算法模型的本质就是最小化一个损失函数,求出损失函数的参数的数学解;
  • 很多机器学习的模型是无法求出目标函数的参数的最优解;
  • 梯度下降法是在机器学习领域中最小化损失函数的最为常用的方法;
学习速率 — learning_rate
  • η 的取值影响获得最优解的速度;
  • η 取值不合适,甚至得不到最优解;
  • η 是梯度下降法的一个超参数;
  • 一般需要调参找到最适合的 η;
  • η 太小,减慢收敛学习速度;
  • η 太大,导致不收敛;
  • 如果出现 J 的变化有减有曾,可能是 η 的取值太大;

单变量函数的梯度下降

定义J(theta1),表示整体的损失,它更加学术范的名称叫做均方误差。然后我们的目标是找到使得J最小化的theta1。这里使用了均方误差来作为我们衡量预测值和真实值之间差异的指标,但它并非唯一的。事实上,我们可以定义任何一种能够衡量预测值和真实值之间差异的指标作为损失函数,而均方误差是最简单的一种,而且对于大多数回归场景而言都能表现出不错的性能。

左边是假设函数,右边是损失函数。当参数为一个的时候,求极值的数学思想,对公式求导=0即可得到极值。

多变量函数的梯度下降

左边是假设函数,右边是损失函数 因为我们两个参数θ0和θ1,这使得我们的损失函数在三维图形上类似一个碗型。根据不同的训练集,我 们会得到不同的碗型,底部平面的任何一个点表示了一个theta0和theta1,而我们的这个三维图形在该点上 的垂直高度即代表了相应的损失函数值J。

梯度下降算法

如何以从右上角的山(高成本)移动到左下角的深蓝色海(低成本)。 需要找到一个合适的测量方向的频率,来确保下山的方向不错误,同时又不至于耗时太多!

梯度下降算法逻辑:

 1. 初始时,猜一下theta0和theta1的取值,其实就是随机生成一个theta0和theta1。
 2.  接着,要做的是,不断地小幅改变theta0和theta1的值,试着降低J(theta0, theta1), 直到收敛到最小值点,或者是一个局部最小值点。

梯度下降算法情景假设:

​ 如果我们从第一次的点出发,我们最终会到达这个局部最小点,但是如果我们稍微从一个不同位置出发, 我们又会到达一个完全不同的局部最小点。这其实就是梯度下降的一个性质。

​ 梯度下降的基本过程就和下山的场景很类似。

  1. 计算梯度——找到当前位置最陡峭的方向,即下山最快的方向。
  2. 目标下降——朝着梯度相反的方向,就能让函数值下降的最快。 重复利用以上方法,反复求取梯度,最后就能到达局部的最小值, 这就类似于我们下山的过程。而求取梯度就确定了最陡峭的方向, 也就是场景中测量方向的手段。

梯度下降的问题
  • 问题:并不是所有的函数都有唯一的极值点,优化的目标是找到最小值点;
  • 方案:多次运行,随机化初始点,比较后取最优解;
  • 方案弊端:也不一定能找到全局最优解;
  • 直线方程中导数代表斜率;
  • 曲线方程中导数代表在这一点的切线的斜率;
  • 为什么叫梯度:在多维函数中,要对各个方向的分量分别求导,最终得到的方向就是梯度;
  • 多维函数中,梯度代表函数变化的方向,对应 就 J 增大/减小的方向;
  • 梯度下降法的初始点也是一个超参数,起始点对于一个算法是非常重要的;

案例:画图模拟梯度下降的过程

​ 整理训练集数据,自定义梯度下降算法规则,求出w0 , w1 ,绘制回归线。

# 导入模块
import numpy as np
import matplotlib.pyplot as mp

# 创建训练集和测试集
train_x = np.array([0.5, 0.6, 0.8, 1.1, 1.4])
train_y = np.array([5.0, 5.5, 6.0, 6.8, 7.0])
test_x = np.array([0.45, 0.55, 1.0, 1.3, 1.5])
test_y = np.array([4.8, 5.3, 6.4, 6.9, 7.3])

# 梯度下降算法
times = 1000	# 定义梯度下降次数
lrate = 0.01	# 记录每次梯度下降参数变化率
epoches = []	# 记录每次梯度下降的索引
w0, w1, losses = [1], [1], []
for i in range(1, times + 1):
    epoches.append(i)
    loss = (((w0[-1] + w1[-1] * train_x) - train_y) ** 2).sum() / 2
    losses.append(loss)
    d0 = ((w0[-1] + w1[-1] * train_x) - train_y).sum()
    d1 = (((w0[-1] + w1[-1] * train_x) - train_y) * train_x).sum()
    print('{:4}> w0={:.8f}, w1={:.8f}, loss={:.8f}'.format(epoches[-1], w0[-1], w1[-1], losses[-1]))
    w0.append(w0[-1] - lrate * d0)
    w1.append(w1[-1] - lrate * d1)

pred_test_y = w0[-1] + w1[-1] * test_x

# 可视化
mp.figure('Linear Regression', facecolor='lightgray')
mp.title('Linear Regression', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.scatter(train_x, train_y, marker='s', c='dodgerblue', alpha=0.5, s=80, label='Training')
mp.scatter(test_x, test_y, marker='D', c='orangered', alpha=0.5, s=60, label='Testing')
mp.scatter(test_x, pred_test_y, c='orangered', alpha=0.5, s=80, label='Predicted')
mp.plot(test_x, pred_test_y, '--', c='limegreen', label='Regression', linewidth=1)
mp.legend()
mp.show()

绘制随着每次梯度下降,w0,w1,loss的变化曲线。

w0 = w0[:-1]
w1 = w1[:-1]

mp.figure('Training Progress', facecolor='lightgray')
mp.subplot(311)
mp.title('Training Progress', fontsize=20)
mp.ylabel('w0', fontsize=14)
mp.gca().xaxis.set_major_locator(mp.MultipleLocator(100))
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(epoches, w0, c='dodgerblue', label='w0')
mp.legend()

mp.subplot(312)
mp.ylabel('w1', fontsize=14)
mp.gca().xaxis.set_major_locator(mp.MultipleLocator(100))
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(epoches, w1, c='limegreen', label='w1')
mp.legend()

mp.subplot(313)
mp.xlabel('epoch', fontsize=14)
mp.ylabel('loss', fontsize=14)
mp.gca().xaxis.set_major_locator(mp.MultipleLocator(100))
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(epoches, losses, c='orangered', label='loss')
mp.legend()

基于三维曲面绘制梯度下降过程中的每一个点。

import mpl_toolkits.mplot3d as axes3d

grid_w0, grid_w1 = np.meshgrid(
    np.linspace(0, 9, 500),
    np.linspace(0, 3.5, 500))

grid_loss = np.zeros_like(grid_w0)
for x, y in zip(train_x, train_y):
    grid_loss += ((grid_w0 + x*grid_w1 - y) ** 2) / 2

mp.figure('Loss Function')
ax = mp.gca(projection='3d')
mp.title('Loss Function', fontsize=20)
ax.set_xlabel('w0', fontsize=14)
ax.set_ylabel('w1', fontsize=14)
ax.set_zlabel('loss', fontsize=14)
ax.plot_surface(grid_w0, grid_w1, grid_loss, rstride=10, cstride=10, cmap='jet')
ax.plot(w0, w1, losses, 'o-', c='orangered', label='BGD')
mp.legend()

以等高线的方式绘制梯度下降的过程。

mp.figure('Batch Gradient Descent', facecolor='lightgray')
mp.title('Batch Gradient Descent', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.contourf(grid_w0, grid_w1, grid_loss, 10, cmap='jet')
cntr = mp.contour(grid_w0, grid_w1, grid_loss, 10,
                  colors='black', linewidths=0.5)
mp.clabel(cntr, inline_spacing=0.1, fmt='%.2f',
          fontsize=8)
mp.plot(w0, w1, 'o-', c='orangered', label='BGD')
mp.legend()
mp.show()

线性回归相关API

sklearn.linear_model.LinearRegression(*, fit_intercept=True, normalize=False, copy_X=True, n_jobs=None)
参数名称 描述
fit_intercept 是否计算此模型的截距。如果设置为False,则在计算中将不使用截距。默认为True
normalize 默认False。如果为True,则将在回归之前通过减去均值并除以l2-范数来对回归变量X进行归一化。
copy_X 如果为True,将复制X,否则,X可能会被覆盖。
n_jobs 用于计算的作业数

属性

属性 描述
coef_ 系数。形状为(n_features,)或(n_targets,n_features)的数组
rank_ 矩阵的等级X
intercept_ 截距

示例代码:

import sklearn.linear_model as lm
# 创建模型
model = lm.LinearRegression()
# 训练模型
# 输入为一个二维数组表示的样本矩阵
# 输出为每个样本最终的结果
model.fit(输入, 输出) # 通过梯度下降法计算模型参数
# 预测输出  
# 输入array是一个二维数组,每一行是一个样本,每一列是一个特征。
result = model.predict(array)

案例:基于线性回归训练single.txt中的训练样本,使用模型预测测试样本。

# 导入模块
import numpy as np
import sklearn.linear_model as lm
import matplotlib.pyplot as mp
import sklearn.metrics as sm

# 采集数据
x, y = np.loadtxt('../data/single.txt', delimiter=',', usecols=(0,1), unpack=True)
x = x.reshape(-1, 1) # 自变量x要写成2维数组方便计算,因变量y不用。

# 创建模型
model = lm.LinearRegression()  # 线性回归
# 训练模型
model.fit(x, y)
# 根据输入预测输出
pred_y = model.predict(x)

# 线性回归方程构造
# 通过coef_和intercept_属性可以得到此时趋势线的系数和截距
print('系数a:' + str(model.coef_[0]))
print('截距b:' + str(model.intercept_))
# 或者
print(model.coef_) # 返回列表,不是数字
print(model.intercept_)

# 可视化
mp.figure('Linear Regression', facecolor='lightgray')
mp.title('Linear Regression', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.scatter(x, y, c='dodgerblue', alpha=0.75, s=60, label='Sample')
mp.plot(x, pred_y, c='orangered', label='Regression')
mp.legend()
mp.show()

# 模型评估
print(sm.mean_absolute_error(y, pred_y))
print(sm.mean_squared_error(y, pred_y))
print(sm.median_absolute_error(y, pred_y))
print(sm.r2_score(y, pred_y))

# 保存模型
with open('./data/linear.pkl', 'wb') as f:
    pickle.dump(model, f)
    
# 加载模型
with open('./../data/linear.pkl', 'rb') as f:
    model = pickle.load(f)

# 使用模型
pred_y = model.predict(x)
评估训练结果误差

线性回归模型训练完毕后,可以利用测试集评估训练结果误差。sklearn.metrics提供了计算模型误差的几个常用算法:

import sklearn.metrics as sm

# 平均绝对值误差:1/m∑|实际输出-预测输出|
sm.mean_absolute_error(y, pred_y)
# 平均平方误差:SQRT(1/mΣ(实际输出-预测输出)^2)
sm.mean_squared_error(y, pred_y)
# 中位绝对值误差:MEDIAN(|实际输出-预测输出|)
sm.median_absolute_error(y, pred_y)
# R2得分,(0,1]区间的分值。分数越高,误差越小。
sm.r2_score(y, pred_y)
模型的保存和加载

模型训练是一个耗时的过程,一个优秀的机器学习是非常宝贵的。可以模型保存到磁盘中,也可以在需要使用的时候从磁盘中重新加载模型即可。不需要重新训练。

模型保存和加载相关API:

import pickle
pickle.dump(内存对象, 磁盘文件) # 保存模型
model = pickle.load(磁盘文件) # 加载模型
相对路径、绝对路径
# 相对路径
# 表示从当前位置出发的一个相对位置
./../data/linear.pkl
"."表示当前目录
".."表示上一级目录
"/folder_name/"文件夹名

# 绝对路径
E:/folder_name/

sklearn库常用回归算法函数

模块名称 函数名称 算法名称
linear_model LinearRegression 线性回归
svm SVR 支持向量回归
neighbors KNeighborsRegressor 最近邻回归
tree DecisionTreeRegressor 回归决策树
ensemble RandomForestRegressor 随机森林回归
ensemble GradientBoostingRegressor 梯度提升回归树

回归模型评价指标

回归模型拥有一套独立的评价指标:平均绝对误差均方误差中值绝对误差的值越靠近0,模型性能越好。可解释方差值R方值则越靠近1 ,模型性能越好。

方法名称 最优值 sklearn函数
平均绝对误差 0.0 metrics. mean_absolute_error
平均均方误差 0.0 metrics. mean_squared_error
中位绝对误差 0.0 metrics. median_absolute_error
可解释方差值 1.0 metrics. explained_variance_score
R方值 1.0 metrics. r2_score

多元线性回归模型

岭回归

​ 普通线性回归模型使用基于梯度下降的最小二乘法,在最小化损失函数的前提下,寻找最优模型参数,于此过程中,包括少数异常样本在内的全部训练数据都会对最终模型参数造成程度相等的影响,异常值对模型所带来影响无法在训练过程中被识别出来。为此,岭回归在模型迭代过程所依据的损失函数中增加了正则项,以限制模型参数对异常样本的匹配程度,进而提高模型面对多数正常样本的拟合精度。

线性回归的正则化

  • 线性回归+L1正则项:Lasso 回归(套索回归)

  • 线性回归+L2正则项:Ridge 回归(岭回归)

岭回归API:

import sklearn.linear_model as lm

# 创建模型
model = lm.Ridge(正则强度,fit_intercept=是否训练截距, max_iter=最大迭代次数)
# 训练模型
# 输入为一个二维数组表示的样本矩阵
# 输出为每个样本最终的结果
model.fit(输入, 输出)
# 预测输出  
# 输入array是一个二维数组,每一行是一个样本,每一列是一个特征。
result = model.predict(array)

案例:加载abnormal.txt文件中的数据,基于岭回归算法训练回归模型。

import numpy as np
import sklearn.linear_model as lm
import matplotlib.pyplot as mp

# 采集数据
x, y = np.loadtxt('../data/abnormal.txt', delimiter=',', usecols=(0,1), unpack=True)
x = x.reshape(-1, 1)

# 创建线性回归模型
model = lm.LinearRegression() 
# 训练模型
model.fit(x, y)
# 根据输入预测输出
pred_y1 = model.predict(x)

# 创建岭回归模型
model = lm.Ridge(150, fit_intercept=True, max_iter=10000) 
# 训练模型
model.fit(x, y)
# 根据输入预测输出
pred_y2 = model.predict(x)

# 可视化
mp.figure('Linear & Ridge', facecolor='lightgray')
mp.title('Linear & Ridge', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.scatter(x, y, c='dodgerblue', alpha=0.75,
           s=60, label='Sample')
sorted_indices = x.T[0].argsort()
mp.plot(x[sorted_indices], pred_y1[sorted_indices],
        c='orangered', label='Linear')
mp.plot(x[sorted_indices], pred_y2[sorted_indices],
        c='limegreen', label='Ridge')
mp.legend()
mp.show()

多项式回归

若希望回归模型更好的拟合训练样本数据,可以使用多项式回归器。

示例数据

浓度		深度		温度			腐蚀速率
0.001	 100	  -1			0.0002
0.001	 100	  -1			0.0002
0.001	 100	  -1			0.0002
0.001	 100	  -1			0.0002

0.002	 200      -2              ?
0.003	 300      -4              ?

一元多项式回归

y=w0 + w1 x + w2 x2 + w3 x3 + ... + wd xd

将高次项看做对一次项特征的扩展得到:

y=w0 + w1 x1 + w2 x2 + w3 x3 + ... + wd xd

那么一元多项式回归即可以看做为多元线性回归,可以使用LinearRegression模型对样本数据进行模型训练。

所以一元多项式回归的实现需要两个步骤:

  1. 将一元多项式回归问题转换为多元线性回归问题(只需给出多项式最高次数即可)。
  2. 将1步骤得到多项式的结果中 w1 w2 .. 当做样本特征,交给线性回归器训练多元线性模型。

使用sklearn提供的数据管线实现两个步骤的顺序执行:

import sklearn.pipeline as pl
import sklearn.preprocessing as sp
import sklearn.linear_model as lm

model = pl.make_pipeline(
    sp.PolynomialFeatures(10),  # 多项式特征扩展器
    lm.LinearRegression())      # 线性回归器

案例:

import numpy as np
import sklearn.pipeline as pl
import sklearn.preprocessing as sp
import sklearn.linear_model as lm
import sklearn.metrics as sm
import matplotlib.pyplot as mp
# 采集数据
x, y = np.loadtxt('../data/single.txt', delimiter=',', usecols=(0,1), unpack=True)
x = x.reshape(-1, 1)
# 创建模型(管线)
model = pl.make_pipeline(
    sp.PolynomialFeatures(10),  # 多项式特征扩展器
    lm.LinearRegression())      # 线性回归器
# 训练模型
model.fit(x, y)
# 根据输入预测输出
pred_y = model.predict(x)
test_x = np.linspace(x.min(), x.max(), 1000).reshape(-1, 1)
pred_test_y = model.predict(test_x)
mp.figure('Polynomial Regression', facecolor='lightgray')
mp.title('Polynomial Regression', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.scatter(x, y, c='dodgerblue', alpha=0.75, s=60, label='Sample')
mp.plot(test_x, pred_test_y, c='orangered', label='Regression')
mp.legend()
mp.show()

过拟合和欠拟合

过拟合和欠拟合

过于简单的模型,无论对于训练数据还是测试数据都无法给出足够高的预测精度,这种现象叫做欠拟合

过于复杂的模型,对于训练数据可以得到较高的预测精度,但对于测试数据通常精度较低,这种现象叫做过拟合

一个性能可以接受的学习模型应该对训练数据和测试数据都有接近的预测精度,而且精度不能太低。

训练集R2   测试集R2
0.3        0.4        欠拟合:过于简单,无法反映数据的规则
0.9        0.2        过拟合:过于复杂,太特殊,缺乏一般性
0.7        0.6        可接受:复杂度适中,既反映数据的规则,同时又不失一般性

防止过拟合

  1. 减少特征
  2. 增加数据量
  3. 正则化(Regularized)

正则化代价函数:L2正则化,L1正则化

防止欠拟合

  1. 增加模型复杂度

使用Sklearn估计器构建回归模型

常用的回归模型

回归模型名称 使用条件 算法描述
线性回归 因变量与自变量是线性关系 对一个或多个自变量和因变量之间的线性关系进行建模,可 用最小二乘法求解模型系数。
非线性回归 因变量与自变量之间不都是线性关系 对一个或多个自变量和因变量之间的非线性关系进行建模。 如果非线性关系可以通过简单的函数变换转化成线性关系,用线性回归的思想求解;如果不能转化,用非线性最小二乘方法求解。
Logistic回归 因变量一般有1和0(是与否)两种取 值 是广义线性回归模型的特例,利用Logistic函数将因变量的取值范围控制在0和1之间,表示取值为1的概率。
岭回归 参与建模的自变量之间具有多重共线 性 是一种改进最小二乘估计的方法。
主成分回归 参与建模的自变量之间具有多重共线 性 主成分回归是根据主成分分析的思想提出来的,是对最小二 乘法的一种改进,它是参数估计的一种有偏估计。可以消除 自变量之间的多重共线性。

Sklearn库常用回归算法函数

  • sklearn内部提供了不少回归算法,常用的函数如下表所示。

  • 可以利用预测结果和真实结果画出折线图作对比,以便更直观看出线性回归模型效果。

    模块名称 函数名称 算法名称
    linear_model LinearRegression 线性回归
    svm SVR 支持向量回归
    neighbors KNeighborsRegressor 最近邻回归
    tree DecisionTreeRegressor 回归决策树
    ensemble RandomForestRegressor 随机森林回归
    ensemble GradientBoostingRegressor 梯度提升回归树

补充知识点:

​ 有时对模型使用默认参数,运行程序时会出现FutureWarning警告信息,它只是在说明模型的官方默认参数在未来会有所调整,并非报错。通常可以不用理会这个警告信息,程序的运行不会受到影响。如果不想看到这个警告信息,可以在完整代码的最上方添加如下代码来忽略警告信息。

import warnings
warnings.filterwarnings('ignore')

分类模型

逻辑回归

逻辑回归分类模型是一种基于回归思想实现分类业务的分类模型。

逻辑回归做二元分类时的核心思想为:

针对输出为{0, 1}的已知训练样本训练一个回归模型,使得训练样本的预测输出限制在(0, 1)的数值区间。该模型使原类别为0的样本的输出更接近于0,原类别为1的样本的输出更接近于1。这样就可以使用相同的回归模型来完成分类预测。

逻辑回归原理

逻辑回归目标函数:

\[逻辑函数(sigmoid):y = \frac{1}{1+e^{-z}}; \quad z=w^Tx+b \]

该逻辑函数值域被限制在(0, 1)区间,当x>0,y>0.5;当x<0, y<0.5。可以把训练样本数据通过线性预测模型 \(z\) 代入逻辑函数,找到一组最优秀的模型参数使得原本属于1类别的样本输出趋近于1;原本属于0类别的样本输出趋近于0。即将预测函数的输出看做被划分为1类的概率,择概率大的类别作为预测结果。

逻辑回归模型的本质是预测概率,而不是直接预测具体类别(如属于0还是1)。通过如下代码可以获取概率值。

y_pred_proba= model.predict_proba(X)

可以直接将y_pred_proba打印出来,它是一个NumPy格式的二维数组,通过引入pandas库将y_pred_proba转换成DataFrame的格式,代码如下:

import pandas as pd
a = pd.DataFrame(y_pred_proba,columns=['分类为0的概率','分类为1的概率'])

模型搭建

模型的搭建相对比较容易,逻辑回归相关API:

import sklearn.linear_model as lm
# 构建逻辑回归器 
# solver:逻辑函数中指数的函数关系(liblinear为线型函数关系)
# C:参数代表正则强度,为了防止过拟合。正则越大拟合效果越小。
model = lm.LogisticRegression(solver='liblinear', C=正则强度)
model.fit(X_train,y_train)

模型使用

搭建模型是为了利用它来预测数据,把测试集中的数据导入模型中进行预测,其中model就是上面搭建的逻辑回归模型。

y_pred = model.predict(x_test)

将模型的预测值y_pred和测试集的实际值y_test进行对比

import sklearn.metrics as lm

# 引入可以计算准确度的accuracy_score()函数,即预测准确度
scort = lm.accuracy_score(y_pred,y_test)

预测概率:

y_pred_proba = model.predict_proba(X_test)

案例:基于逻辑回归器绘制网格化坐标颜色矩阵。

import numpy as np
import sklearn.linear_model as lm
import matplotlib.pyplot as mp
x = np.array([
    [3, 1],
    [2, 5],
    [1, 8],
    [6, 4],
    [5, 2],
    [3, 5],
    [4, 7],
    [4, -1]])
y = np.array([0, 1, 1, 0, 0, 1, 1, 0])`
# 逻辑分类器
model = lm.LogisticRegression(solver='liblinear', C=1)
model.fit(x, y)
l, r = x[:, 0].min() - 1, x[:, 0].max() + 1
b, t = x[:, 1].min() - 1, x[:, 1].max() + 1
n = 500
grid_x, grid_y = np.meshgrid(np.linspace(l, r, n), np.linspace(b, t, n))
samples = np.column_stack((grid_x.ravel(), grid_y.ravel()))

grid_z = model.predict(samples)
grid_z = grid_z.reshape(grid_x.shape)
mp.figure('Logistic Classification', facecolor='lightgray')
mp.title('Logistic Classification', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.pcolormesh(grid_x, grid_y, grid_z, cmap='gray')
mp.scatter(x[:, 0], x[:, 1], c=y, cmap='brg', s=80)
mp.show()

多元分类

通过多个二元分类器解决多元分类问题。

特征1 特征2 ==> 所属类别
4 7 ==> A
3.5 8 ==> A
1.2 1.9 ==> B
5.4 2.2 ==> C

若拿到一组新的样本,可以基于二元逻辑分类训练出一个模型判断属于A类别的概率。再使用同样的方法训练出两个模型分别判断属于B、C类型的概率,最终选择概率最高的类别作为新样本的分类结果。

案例:基于逻辑分类模型的多元分类。

import numpy as np
import sklearn.linear_model as lm
import matplotlib.pyplot as mp
x = np.array([
    [4, 7],
    [3.5, 8],
    [3.1, 6.2],
    [0.5, 1],
    [1, 2],
    [1.2, 1.9],
    [6, 2],
    [5.7, 1.5],
    [5.4, 2.2]])
y = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2])
# 逻辑分类器
model = lm.LogisticRegression(solver='liblinear', C=1000)
model.fit(x, y)
l, r = x[:, 0].min() - 1, x[:, 0].max() + 1
b, t = x[:, 1].min() - 1, x[:, 1].max() + 1
n = 500
grid_x, grid_y = np.meshgrid(np.linspace(l, r, n), np.linspace(b, t, n))
samples = np.column_stack((grid_x.ravel(), grid_y.ravel()))
grid_z = model.predict(samples)
print(grid_z)
grid_z = grid_z.reshape(grid_x.shape)

mp.figure('Logistic Classification', facecolor='lightgray')
mp.title('Logistic Classification', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.pcolormesh(grid_x, grid_y, grid_z, cmap='gray')
mp.scatter(x[:, 0], x[:, 1], c=y, cmap='brg', s=80)
mp.show()

数据集划分

和之前创建的线性回归模型稍有不同的是,进行模型搭建和使用前都会将数据分成训练集数据(简称训练集)和测试集数据(简称测试集)。顾名思义,训练集用于训练数据和搭建模型,测试集则用于检验训练后所搭建模型的效果。划分训练集和测试集的目的一是为了对模型进行评估,二是可以通过测试集对模型进行调优。

对于分类问题训练集和测试集的划分不应该用整个样本空间的特定百分比作为训练数据,而应该在其每一个类别的样本中抽取特定百分比作为训练数据。sklearn模块提供了数据集划分相关方法,可以方便的划分训练集与测试集数据,使用不同数据集训练或测试模型,达到提高分类可信度。

数据集划分相关API:

import sklearn.model_selection as ms

X_train, X_test, y_train, y_test = \
	ms.train_test_split(X, y, test_size=测试集占比, random_state=随机种子)   
参数名称 说明
*arrays 接收一个或多个数据集。代表需要划分的数据集,若为分类回归则分别传入数据和标签,若为聚类则传入数 据。无默认。
test_size 接收float,int,None类型的数据。代表测试集的大小。如果传入的为float类型的数据则需要限定在0-1之 间,代表测试集在总数中的占比;如果传入为int类型的数据,则表示测试集记录的绝对数目。该参数与 train_size可以只传入一个。在0.21版本前,若test_size和train_size均为默认则testsize为25%。
train_size 接收float,int,None类型的数据。代表训练集的大小。该参数与test_size可以只传入一个。
random_state 接收int。代表随机种子编号,相同随机种子编号产生相同的随机结果,不同的随机种子编号产生不同的随机结果。默认为None。random_state就是为了保证程序每次运行都分割一样的训练集合测试集。
shuffle 接收boolean。代表是否进行有放回抽样。若该参数取值为True则stratify参数必须不能为空。
stratify 接收array或者None。如果不为None,则使用传入的标签进行分层抽样。

train_test_split是最常用的数据划分方法,在model_selection模块中还提供了其他数据集划分的函数,如PredefinedSplit,ShuffleSplit等。

从Scikit-Learn库中引入train_test_split()函数。

​ 用train_test_split()函数划分训练集和测试集,X_train、y_train为训练集的特征变量和目标变量数据,X_test、y_test则为测试集的特征变量和目标变量数据。train_test_split()函数的参数中,X和y便是之前划分的特征变量和目标变量。

​ test_size则是测试集数据所占的比例,通常会根据样本量的大小来划分训练集和测试集,当样本量较大时,可以划分多一点数据给训练集。

​ 每次运行程序时,train_test_split()函数都会随机划分数据,如果想要让每次划分数据的结果保持一致,可以设置random_state参数。random_state参数赋值为一个整数,该数字没有特殊含义,可以是任意数字,它相当于一个种子参数,使得每次划分数据的结果一致。

​ 划分训练集和测试集在某种程度上也是为了检查模型是否出现过拟合。过拟合指模型在训练样本中拟合程度过高,虽然它很好地契合了训练集数据,但是却丧失了泛化能力,因而不具有推广性,导致在测试集数据中的预测表现不佳。就好比每次模考都做同一份卷子,训练时得分很高,但是期末考试换了一套卷子就得分很低。而划分训练集和测试集可以用来对模型进行更好的检验。

案例:

import numpy as np
import sklearn.model_selection as ms
import sklearn.naive_bayes as nb
import matplotlib.pyplot as mp

# 采集数据
data = np.loadtxt('./data/multiple1.txt', unpack=False, dtype='U20', delimiter=',')
print(data.shape)
x = np.array(data[:, :-1], dtype=float)
y = np.array(data[:, -1], dtype=float)

# 划分训练集和测试集
train_x, test_x, train_y, test_y = \
    ms.train_test_split( x, y, test_size=0.25, random_state=7)
    
# 朴素贝叶斯分类器
model = nb.GaussianNB()
# 用训练集训练模型
model.fit(train_x, train_y)

l, r = x[:, 0].min() - 1, x[:, 0].max() + 1
b, t = x[:, 1].min() - 1, x[:, 1].max() + 1
n = 500
grid_x, grid_y = np.meshgrid(np.linspace(l, r, n), np.linspace(b, t, n))
samples = np.column_stack((grid_x.ravel(), grid_y.ravel()))
grid_z = model.predict(samples)
grid_z = grid_z.reshape(grid_x.shape)

pred_test_y = model.predict(test_x)
# 计算并打印预测输出的精确度
print((test_y == pred_test_y).sum() / pred_test_y.size)

# 可视化
mp.figure('Naive Bayes Classification', facecolor='lightgray')
mp.title('Naive Bayes Classification', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.pcolormesh(grid_x, grid_y, grid_z, cmap='gray')
mp.scatter(test_x[:,0], test_x[:,1], c=test_y, cmap='brg', s=80)
mp.show()

评估分类模型

一级指标

分类模型的评价指标:真假与正类负类

​ a. 真正例(TP) 是指模型将正类别样本正确地预测为正类别。

​ b. 真负例(TN) 是指模型将负类别样本正确地预测为负类别。

​ c. 假正例(FP) 是指模型将负类别样本错误地预测为正类别。

​ d. 假负例(FN) 是指将正类别样本错误地预测为负类别。

混淆矩阵

混淆矩阵(也称误差矩阵,Confusion Matrix)就是分别统计分类模型归错类,归对类的观测值个数,然后把结果放在一个表里展示出来。这个表就是混淆矩阵。

真实值 真实值
Positive Negative
预测值 Positive 真正例(TP) 假正例(FP)
预测值 Negative 假负例(FN) 真负例(TN)

获取模型分类结果的混淆矩阵的相关API:

import sklearn.metrics as sm
混淆矩阵 = sm.confusion_matrix(实际输出, 预测输出)

案例:输出分类结果的混淆矩阵。

# 输出混淆矩阵并绘制混淆矩阵图谱
cm = sm.confusion_matrix(test_y, pred_test_y)
print(cm)
mp.figure('Confusion Matrix', facecolor='lightgray')
mp.title('Confusion Matrix', fontsize=20)
mp.xlabel('Predicted Class', fontsize=14)
mp.ylabel('True Class', fontsize=14)
mp.xticks(np.unique(pred_test_y))
mp.yticks(np.unique(test_y))
mp.tick_params(labelsize=10)
mp.imshow(cm, interpolation='nearest', cmap='jet')
mp.show()

二级指标

  • 准确率(accuracy):是指模型预测正确的样本数比上总样本数的比重。

    ​ 计算公式:accuracy = (TP + TN) / (TP + TN + FP + FN)

  • 精确率(precision_weighted):针对每一个类别,预测正确的样本数比上预测出来的样本数。

    ​ 计算公式:precision_weighted = TP / TP + FP

  • 召回率(recall_weighted):针对每一个类别,预测正确的样本数比上实际存在的样本数。

    ​ 计算公式:recall_weighted = TP / TP + FN

    某池塘有1400条鲤鱼,300只虾,300只鳖。现在以捕鲤鱼为目的。 撒一大网,逮着了700条鲤鱼,200只虾,100只鳖。那么,这些指标 分别如下:
    准确率 = 700 / (700 + 200 + 100) = 70% 
    召回率 = 700 / 1400 = 50% 
    F值 = 70% * 50% * 2 / (70% + 50%) = 58.3%
    

三级指标

  • F1_Score(f1_weighted):2x查准率x召回率/(查准率+召回率)

分类模型对测试集进行预测而得出的准确率(accuracy)并不能很好地反映模型的性能,为了有效判断一个预测模型的性能表现,需要结合真实值,计算出精确率(precision_weighted)、召回率(recall_weighted)、F1_Score和Cohen’s Kappa系数等指标来衡量。分值越高越好。

要全面评估模型的有效性,必须同时检查精确率(precision_weighted)和召回率(recall_weighted)。遗憾的是,精确率和召回率往往是此消彼长的情况。也就是说,提高精确率通常会降低召回率值,反之亦然。提高分类阈值,精确率可能会提高(因为FP可能会减小);召回率会下降或保持不变(因为TP会减少或不变,且FN会增加或不变)。降低分类阈值,精确率可能会下降(因为FP可能会增加),而召回率(FN可能会减少)可能会有所提高。

问题:那么阈值是如何取值的呢?

ROC曲线

除了使用数值,表格形式评估分类模型的性能,还可通过绘制ROC曲线的方式来评估分类模型。

ROC 曲线(接收者操作特征曲线)是一种显示分类模型在所有分类阈值下的效果的图表。

该曲线绘制了以下两个参数:

  • 真正例率(TPR):TPR = TP / TP + FN
  • 假正例率(FPR):FPR = FP / FP + TN

ROC 曲线用于绘制采用不同分类阈值时的 TPR 与 FPR。降低分类阈值会导致将更多样本归为正类别,从而增加假正例和真正例的个数。下图显示了一个典型的 ROC 曲线。

为了计算 ROC 曲线上的点,我们可以使用不同的分类阈值多次评估逻辑回归模型,但这样做效率非常低。幸运的是,有一种基于排序的高效算法可以为我们提供此类信息,这种算法称为曲线下面积。

AUC值

AUC(Area Under Curve)被定义为ROC曲线下与坐标轴围成的面积。曲线下面积测量的是从 (0,0) 到 (1,1) 之间整个 ROC 曲线以下的整个二维面积,显然这个面积的数值不会大于1。由于ROC曲线一般都处于y=x这条直线的上方,所以AUC的取值范围在0.5和1之间。AUC越接近1.0,检测方法真实性越高,表示模型性能越好;等于0.5时,则真实性最低,无应用价值。

曲线下面积对所有可能的分类阈值的效果进行综合衡量。曲线下面积的一种解读方式是看作模型将某个随机正类别样本排列在某个随机负类别样本之上的概率。

交叉验证

​ 在机器学习中,因为训练集和测试集的数据划分是随机的,所以有时会重复地使用数据,以便更好地评估模型的有效性,并选出最好的模型,该做法称为交叉验证。具体而言就是对原始样本数据进行切分,然后组合成为多组不同的训练集和测试集,用训练集训练模型,用测试集评估模型。某次的训练集可能是下次的测试集,故而称为交叉验证。

​ 交叉验证的方法有简单交叉验证、K折交叉验证和留一交叉验证3种。其中K折交叉验证应用较为广泛,它是指将数据集随机等分为K份,每次选取K-1份作为训练集,用剩下的1份作为测试集,得到K个模型后将这K个模型的平均测试效果作为最终的模型效果。

​ 通常来说,如果训练集相对较小,则增大K值,这样在每次迭代过程中将会有更多数据用于模型训练,同时算法时间延长;如果训练集相对较大,则减小K值,这样可以降低模型在不同的数据块上进行重复拟合性能评估的计算成本,在平均性能的基础上获得模型的准确评估。

​ 除了更精确地评估模型,交叉验证的另一个重要作用就是利用更精确的评估结果对模型进行参数调优,它经常与GridSearch网格搜索配合使用。

sklearn提供了交叉验证相关API:

import sklearn.model_selection as ms
指标值数组 = ms.cross_val_score(模型, 输入集, 输出集, cv=折叠数, scoring=指标名)

案例:使用交叉验证,输出分类器的精确度:

# 划分训练集和测试集
train_x, test_x, train_y, test_y = \
    ms.train_test_split(x, y, test_size=0.25, random_state=7)

# 朴素贝叶斯分类器
model = nb.GaussianNB()
# 交叉验证
# 精确度
ac = ms.cross_val_score( model, train_x, train_y, cv=5, scoring='accuracy')
print(ac.mean())
#用训练集训练模型
model.fit(train_x, train_y)

在交叉验证过程中,针对每一次交叉验证,计算所有类别的查准率、召回率或者f1得分,然后取各类别相应指标值的平均数,作为这一次交叉验证的评估指标,然后再将所有交叉验证的评估指标以数组的形式返回调用者。

# 交叉验证
# 准确度
ac = ms.cross_val_score( model, train_x, train_y, cv=5, scoring='accuracy')
print(ac)
print(ac.mean())

# 精准率
pw = ms.cross_val_score( model, train_x, train_y, cv=5, scoring='precision_weighted')
print(pw)
print(pw.mean())

# 召回率
rw = ms.cross_val_score( model, train_x, train_y, cv=5, scoring='recall_weighted')
print(rw)
print(rw.mean())

# F1_score
fw = ms.cross_val_score( model, train_x, train_y, cv=5, scoring='f1_weighted')
print(fw)
print(fw.mean())

# 以ROC曲线的AUC值作为评估标准
acc= ms.cross_val_score( model, train_x, train_y, cv=5, scoring='roc_auc')
print(acc)
print(acc.mean())

分类报告

sklearn.metrics提供了分类报告相关API,不仅可以得到混淆矩阵,还可以得到交叉验证查准率、召回率、f1得分的结果,可以方便的分析出哪些样本是异常样本。

# 获取分类报告
cr = sm.classification_report(实际输出, 预测输出)

案例:输出分类报告:

# 获取分类报告
cr = sm.classification_report(test_y, pred_test_y)
print(cr)

sklearn库常用分类评估API

方法名称 最佳值 sklearn函数
Accuracy(准确率) 1.0 metrics.accuracy_score
Precision(精确率) 1.0 metrics.precision_score
Recall(召回率) 1.0 metrics.recall_score
F1_score(F1值) 1.0 metrics.f1_score
ROC曲线 最靠近y轴 metrics. roc_curve
Cohen’s Kappa系数 1.0 metrics.cohen_kappa_score
# metrics模块中常使用的函数
# 二分类(binary classification)使用的:
	matthews_corrcoef(y_true, y_pred)
	precision_recall_curve(y_true, probas_pred)
	roc_curve(y_true, y_score[, pos_label, …])

# 多分类(multiclass)使用的:
	confusion_matrix(y_true, y_pred[, labels])
	hinge_loss(y_true, pred_decision[, labels, …])
	
# 多标签(multilabel)使用的:
	accuracy_score(y_true, y_pred[, normalize, …])
	classification_report(y_true, y_pred[, …])
	f1_score(y_true, y_pred[, labels, …])
	fbeta_score(y_true, y_pred, beta[, labels, …])
	hamming_loss(y_true, y_pred[, classes])
	jaccard_similarity_score(y_true, y_pred[, …])
	log_loss(y_true, y_pred[, eps, normalize, …])
	precision_recall_fscore_support(y_true, y_pred)
	precision_score(y_true, y_pred[, labels, …])
	recall_score(y_true, y_pred[, labels, …])
	zero_one_loss(y_true, y_pred[, normalize, …])

# 同时用于二标签和多标签(不是多分类)问题:
	average_precision_score(y_true, y_score[, …])
	roc_auc_score(y_true, y_score[, average, …])

决策树

基础概念

决策树概念:决策树模型是机器学习的各种算法模型中比较好理解的一种模型,它的基本原理是通过对一系列问题进行if/else的推导,最终实现相关决策。

决策树模型的几个重要概念:父节点和子节点、根节点和叶子节点。

父节点和子节点是相对的,子节点由父节点根据某一规则分裂而来,然后子节点作为新的父节点继续分裂,直至不能分裂为止。

根节点则和叶子节点是相对的,根节点是没有父节点的节点,即初始节点,叶子节点则是没有子节点的节点,即最终节点。

决策树模型的关键:就是如何选择合适的节点进行分裂。

基本算法原理

决策树利用树结构进行决策,每一个非叶子节点是一个判断条件,每一个叶子节点是结论。从根节点开始,经过多次判断得出结论。

决策树的构建是数据逐步分裂的过程,构建的步骤如下:

  1. 将所有的数据看成是一个节点点,进入步骤2;
  2. 从所有的数据特征中挑选一个数据特征对节点进行分割,进入步骤3;
  3. 生成若干子节点,对每一个子节点进行判断,如果满足停止分裂的条件,进入步骤 4;否则,进入步骤2;
  4. 设置该节点是叶子节点,其输出的结果为该节点数量占比最大的类别。

决策生成过程中有三个重要的问题:

  1. 数据如何分割?
  2. 如何选择分裂的属性?
  3. 什么时候停止分裂?

决策树采用贪婪思想进行分裂,即选择可以得到最优分裂结果的属性进行分裂。那么怎样才算是最优的分裂结果?最理想的情况当然是能找到一个属性刚好能够将不同类别分开,但是大多数情况下分裂很难一步到位,我们希望每一次分裂之后子节点的数据尽量“纯”。

停止分裂的条件

  • 最小节点数: 当节点的数据量小于一个指定的数量时,不继续分裂。
  • 数据的纯度:当数据的纯度达到一定量时,不继续分裂。
  • 决策树的深度:节点的深度可以理解为节点与决策树根节点的距离,如根节点的子节点的深度为1,因为这些节点与根节点的距离为1,子节点的深度要比父节点的深度大1。决策树的深度是所有叶子节点的最大深度,当深度到达指定的上限大小时,停止分裂。
  • 所有特征已经使用完毕:被动式停止分裂的条件。

决策树是一种常见的算法,其思想和“人类逐步分析比较然后作出结论”的过程十分相似。 特征选择核心:特征选择在于选取对训练数据具有分类能力的特征,这样可以提高决策树学习的效率,如果利用一个特征进行分类的结果与随机分类的结果没有很大差别,则称这个特征是没有分类能力的,经验上,扔掉这样的特征对决策树的精度影响不大, 比如:我们希望构建一颗决策树来根据不同人的各种属性来预测性别,那么属性“头发的长度” 这个属性可能就要比属性“头发的颜色”所能包含的信息更多,因为一般来说,男生的头发要比女 生的头发短,所以,我们希望“头发的长度”这个属性处于决策树的上部,随着划分不断进行,我 们希望决策树的分支节点所包含的样本尽可能属于同一个类别,即结点的“纯度”越来越高。

决策树的构建

构建决策树的过程就是做决策的过程,因其形状为树状结构,故称之为决策树。决策树是一种可以帮助我们做决策的算法;可以解决回归和分类问题,但经常用来做分类。

决策论:

信息量就是衡量一个信息包含内容的多少。信息的大小跟随机事件的概率有关。越小概率的事情发生了产生的信息量越大,越大概率的事情发生了产生的信息量越小。

事件:
小明学习不好,爱玩,上课也不认真听讲,每次考试都考倒数几名,然而这一次考试考了班级前几名,大家都非常震惊。
小张同学,每次考试都是第一名,这次也不意外,也是第一。

结论:
小明同学考试好的概率非常之低,但是这次考的非常好,概率小的事情发生了,大家心里非常震惊,觉得这里面包含了很多事情:奋发图像,挑灯夜读,大神相助,压中考点,投机取巧,考试作弊。概率小的事情具有的信息量大
小张同学每次都考的很好,这次也不意外,理所当然:概率大的事情具有的信息量小

信息量度量的是一个具体事件发生了所带来的信息,而则是在结果出来之前对可能产生的信息量的期望——考虑该随机变量的所有可能取值,即所有可能发生事件所带来的信息量的期望。

熵:度量随机变量的不确定性 (纯度)

熵:熵可以表示样本集合的不确定性,熵越大,样本的不确定性就越大,纯度就低。

算法:

  • ID3

  • C4.5

  • CART

    决策树模型的建树依据主要用到的是基尼系数的概念。基尼系数(gini)用于计算一个系统中的失序现象,即系统的混乱程度。基尼系数越高,系统的混乱程度就越高,建立决策树模型的目的就是降低系统的混乱程度,从而得到合适的数据分类效果。

算法总结

核心思想:相似的输入必会产生相似的输出。例如预测某人薪资:

年龄:1-青年,2-中年,3-老年
学历:1-本科,2-硕士,3-博士
经历:1-出道,2-一般,3-老手,4-骨灰
性别:1-男性,2-女性

年龄 学历 经历 性别 ==> 薪资
1 1 1 1 ==> 6000(低)
2 1 3 1 ==> 10000(中)
3 3 4 1 ==> 50000(高)
... ... ... ... ==> ...
1 3 2 2 ==> ?

为了提高搜索效率,使用树形数据结构处理样本数据:

\[年龄=1\left\{ \begin{aligned} 学历1 \\ 学历2 \\ 学历3 \\ \end{aligned} \right. \quad\quad 年龄=2\left\{ \begin{aligned} 学历1 \\ 学历2 \\ 学历3 \\ \end{aligned} \right. \quad\quad 年龄=3\left\{ \begin{aligned} 学历1 \\ 学历2 \\ 学历3 \\ \end{aligned} \right. \]

首先从训练样本矩阵中选择一个特征进行子表划分,使每个子表中该特征的值全部相同,然后再在每个子表中选择下一个特征按照同样的规则继续划分更小的子表,不断重复直到所有的特征全部使用完为止,此时便得到叶级子表,其中所有样本的特征值全部相同。对于待预测样本,根据其每一个特征的值,选择对应的子表,逐一匹配,直到找到与之完全匹配的叶级子表,用该子表中样本的输出,通过平均(回归)或者投票(分类)为待预测样本提供输出。

首先选择哪一个特征进行子表划分决定了决策树的性能。这么多特征,使用哪个特征先进行子表划分?

sklearn提供的决策树底层为cart树(Classification and Regression Tree),cart回归树在解决回归问题时的步骤如下:

  1. 原始数据集S,此时树的深度depth=0;
  2. 针对集合S,遍历每一个特征的每一个value,用该value将原数据集S分裂成2个集合:左集合left(<=value的样本)、右集合right(>value的样本),分别计算这2个集合的mse,找到使(left_mse+right_mse)最小的那个value,记录下此时的特征名称和value,这个就是最佳分割特征以及最佳分割值;
  3. 找到最佳分割特征以及最佳分割value之后,用该value将集合S分裂成2个集合,depth+=1;
  4. 针对集合left、right分别重复步骤2,3,直到达到终止条件。

决策树回归器模型相关API

import sklearn.tree as st

# 决策树分类模型接口
sklearn.tree.DecisionTreeClassifier(criterion='entropy', splitter='best', max_depth=None, min_samples_split=2,min_samples_leaf =1, min_weight_fraction_leaf=0.0, max_features=None, random_state=None, max_leaf_nodes=None,class_weight=None, presort=False)

# 决策树回归模型接口
sklearn.tree.DecisionTreeRegressor(criterion='mse', splitter='best', max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=None, random_state=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, presort='deprecated', ccp_alpha=0.0)
参数 含义
criterion='entropy' 衡量分类的质量“entropy”代表信息增益
max_depth 树的最大深度
min_samples_split 一个内部节点需要的最少的样本数
min_samples_leaf 一个叶节点所需要的最小样本数
max_leaf_nodes 最大叶子节点的值
class_weight 类的关联权值
import sklearn.tree as st

# 创建决策树回归器模型  决策树的最大深度为4
model = st.DecisionTreeRegressor(max_depth=4)
# 训练模型  
# train_x: 二维数组样本数据
# train_y: 训练集中对应每行样本的结果
model.fit(train_x, train_y)
# 测试模型
pred_test_y = model.predict(test_x)

案例:预测波士顿地区房屋价格。

  1. 读取数据,打断原始数据集。 划分训练集和测试集。
import sklearn.datasets as sd
import sklearn.utils as su
# 加载波士顿地区房价数据集
boston = sd.load_boston()
print(boston.feature_names)
# |CRIM|ZN|INDUS|CHAS|NOX|RM|AGE|DIS|RAD|TAX|PTRATIO|B|LSTAT|
# 犯罪率|住宅用地比例|商业用地比例|是否靠河|空气质量|房间数|年限|距中心区距离|路网密度|房产税|师生比|黑人比例|低地位人口比例|
# 打乱原始数据集的输入和输出
x, y = su.shuffle(boston.data, boston.target, random_state=7)
# 划分训练集和测试集
train_size = int(len(x) * 0.8)
train_x, test_x, train_y, test_y = \
    x[:train_size], x[train_size:], \
    y[:train_size], y[train_size:]
  1. 创建决策树回归器模型,使用训练集训练模型。使用测试集测试模型。
import sklearn.tree as st
import sklearn.metrics as sm

# 创建决策树回归模型
model = st.DecisionTreeRegressor(max_depth=4)
# 训练模型
model.fit(train_x, train_y)
# 测试模型
pred_test_y = model.predict(test_x)
print(sm.r2_score(test_y, pred_test_y))

决策树优化

一棵过于复杂的决策树很可能出现过拟合的情况,如果完全按照决策树构建生成一个完整的决策树可能会出现预测不准确的情况, 因此需要对决策树进行优化,优化的方法主要是剪枝。剪枝是指将一颗子树的子节点全部删掉,根节点作为叶子节点。

首先剪枝(pruning)的目的是为了避免决策树模型的过拟合。因为决策树算法在学习的过程中为了尽可能的正确的分类训练样本,不停地对结点进行划分,因此这会导致整棵树的分支过多,也就导致了过拟合。决策树的剪枝策略最基本的有两种:预剪枝(pre-pruning)和后剪枝(post-pruning):

  • 预剪枝(pre-pruning):预剪枝就是在构造决策树的过程中,先对每个结点在划分前进行估计,如果当前结点的划分不能带来决策树模型泛化性能的提升,则不对当前结点进行划分并且将当前结点标记为叶结点。从上往下剪枝,通常利用超参数进行剪枝。例如,通过限制树的最大深度(max_depth)便能剪去该最大深度下面的节点。

  • 后剪枝(post-pruning):后剪枝就是先把整颗决策树构造完毕,然后自底向上的对非叶结点进行考察,若将该结点对应的子树换为叶结点能够带来泛华性能的提升,则把该子树替换为叶结点。

    从下往上剪枝,大多是根据业务需求剪枝。在商业实战中,前剪枝应用得更广泛,参数调优其实也起到了一定的前剪枝作用。

导出决策树

如果想将决策树模型以可视化的方式展示出来,可以使用Python的Graphviz插件。下载地址:https://graphviz.gitlab.io/download/。

# 安装Graphviz库
pip install graphviz

以DOT格式导出决策树。此函数生成决策树的GraphViz表示形式,然后将其写入out_file。导出后,可以使用以下方式生成图形渲染:

sklearn.tree.export_graphviz(decision_tree,out_file = None,*,max_depth = None,feature_names = None,class_names = None,label ='all',filled = False,leaves_parallel = False,杂质= ​​True,node_ids = False,比例= False,rotate = False,rounded = False,special_characters = False,precision = 3 )

模型预测效果评估

roc_auc_score()函数:

import sklearn.metrics as sm

score = roc_auc_score(y_tese,y_pred_proba)

特征重要性评估:

​ 作为决策树模型训练过程的副产品,根据划分子表时选择特征的顺序标志了该特征的重要程度,此即为该特征重要性指标。训练得到的模型对象提供了属性:feature_importances_来存储每个特征的重要性。

model.fit(train_x, train_y)
fi = model.feature_importances_

参数调优

​ 机器学习的各个模型其实都有一些内置的参数,如前面讲决策树模型时就用到了一个很重要的参数max_depth(树的最大深度),这种参数又称为超参数。除了max_depth,决策树模型还有一些常用参数,如criterion(特征选择标准)、min_samples_leaf(叶子节点的最少样本数)等。

​ 大多数情况下,使用模型的默认参数也能获得较好的结果及预测准确度,然而如果想要获得更精确的结果,就需要对模型的超参数进行调优。例如,max_depth取3还是取默认值None(即不限制最大深度,分裂到所有叶子节点的基尼系数都为0)是有讲究的,如果取值过小,可能会导致模型欠拟合,如果取值过大,则容易导致模型过拟合,因此需要一个手段来合理地调节模型参数。

分类决策树模型DecisionTreeClassifier()的常用超参数:

参数名 含义
criterion 特征选择标准,取值为'entropy'(信息熵)和'gini'(基尼系数),默认值为'gini'。
splitter 取值为'best'和'random'。'best'指在特征的所有划分点中找出最优的划分点,适合样本量不大的情况;'random'指随机地在部分划分点中寻找局部最优的划分点,适合样本量非常大的情况;默认值为'best'。
max_depth 决策树最大深度,取值为int型数据或None,默认值为None。一般数据或特征较少时可以不设置,如果数据或特征较多,可以设置最大深度进行限制。
min_samples_split 子节点往下分裂所需的最小样本数,默认值为2。如果子节点中的样本数小于该值则停止分裂。
min_samples_leaf 叶子节点的最小样本数,默认值为1。如果叶子节点中的样本数小于该值,该叶子节点会和兄弟节点一起被剪枝,即剔除该叶子节点和其兄弟节点,并停止分裂。
min_weight_fraction_leaf 叶子节点最小的样本权重和,默认值为0,即不考虑权重问题。如果小于该值,该叶子节点会和兄弟节点一起被剪枝。如果较多样本有缺失值或者样本的分布类别偏差很大,则需考虑样本权重问题。
max_features 在划分节点时所考虑的特征值数量的最大值,默认值为None,可以传入int型或float型数据,如果传入的是float型数据,则表示百分数。
max_leaf_nodes 最大叶子节点数,默认值为None,可以传入int型数据。
class_weight 指定类别权重,默认值为None,可以取'balanced',代表样本量少的类别所对应的样本权重更高,也可以传入字典来指定权重。
random_state 设置random_state参数(如设置为123)可以保证每次运行程序后各节点的分裂结果都是一致的,这在特征变量较多、树的深度较深时较为重要。

K折交叉验证

import sklearn.model_selection as ms
指标值数组 = ms.cross_val_score(模型, 输入集, 输出集, cv=折叠数, scoring=指标名)

GridSearch网格搜索

​ GridSearch网格搜索是一种穷举搜索的参数调优手段:遍历所有的候选参数,循环建立模型并评估模型的有效性和准确性,选取表现最好的参数作为最终结果。以决策树模型最大深度参数max_depth为例,我们可以在[1,3,5,7,9]这些值中遍历,以准确度或ROC曲线的AUC值作为评估标准来搜索最合适的max_depth值。如果要同时调节多个模型参数,例如,模型有2个参数,第1个参数有4种可能,第2个参数有5种可能,所有的可能性可以表示成4×5的网格,那么遍历的过程就像是在网格(Grid)里搜索(Search),这就是该方法名称的由来。

sklearn提供了网格搜索相关API:

import sklearn.model_selection as ms
parameters = {'max_depth':[1,3,5,7,9]} # 指定决策树模型中待调优参数max_depth的候选值范围。
model = DecisionTreeClassifier() # 构建决策树模型并将其赋给变量model。
grid_search = GridSearchCV(model,parameters,scoring='roc_auc',cv=5) 
# 将决策树模型和待调优参数的候选值范围传入GridSearchCV()函数,并设置scoring参数为'roc_auc',表示以ROC曲线的AUC值作为评估标准,如果不设置则以默认值'accuracy'(准确度)作为评估标准,设置cv参数为5,表示进行5折交叉验证。

grid_search.fit(X_train,y_train) # 传入测试集数据并开始进行参数调优
grid_search.best_params_ # 输出参数的最优值

也可以进行多参数同时调优:

import sklearn.model_selection as ms

parameters = {'max_depth':[1,3,5,7,9],'criterion':['gini','entropy'],'min_sample_split':[5,7,9,11,13,15]} # 三个参数的候选值范围。
model = DecisionTreeClassifier() # 构建决策树模型
grid_search = GridSearchCV(model,parameters,scoring='roc_auc',cv=5)
grid_search.fit(X_train,y_train)
# 输出参数的最优值
grid_search.best_params_

集合算法

集合算法也叫集成学习模型,它是使用一系列弱学习器(也称为基础模型或基模型)进行学习,并将各个弱学习器的结果进行整合,从而获得比单个学习器更好的学习效果。集成学习模型的常见算法有Bagging算法和Boosting算法两种。Bagging算法的典型机器学习模型为随机森林模型,而Boosting算法的典型机器学习模型为:AdaBoost、GBDT、XGBoost和LightGBM模型。

Bagging算法

​ Bagging算法的原理类似投票,每个弱学习器都有一票,最终根据所有弱学习器的投票,按照“少数服从多数”的原则产生最终的预测结果。

​ 假设原始数据共有10000条,从中随机有放回地抽取10000次数据构成一个新的训练集(因为是随机有放回抽样,所以可能出现某一条数据多次被抽中,也有可能某一条数据一次也没有被抽中),每次使用一个训练集训练一个弱学习器。这样有放回地随机抽取n次后,训练结束时就能获得由不同的训练集训练出的n个弱学习器,根据这n个弱学习器的预测结果,按照“少数服从多数”的原则,获得一个更加准确、合理的最终预测结果。具体来说,在分类问题中是用n个弱学习器投票的方式获取最终结果,在回归问题中则是取n个弱学习器的平均值作为最终结果。

Boosting算法

​ Boosting算法的本质是将弱学习器提升为强学习器,它和Bagging算法的区别在于:Bagging算法对待所有的弱学习器一视同仁;而Boosting算法则会对弱学习器“区别对待”,通俗来讲就是注重“培养精英”和“重视错误”。

​ 培养精英”就是每一轮训练后对预测结果较准确的弱学习器给予较大的权重,对表现不好的弱学习器则降低其权重。这样在最终预测时,“优秀模型”的权重是大的,相当于它可以投出多票,而“一般模型”只能投出一票或不能投票。

​ 重视错误”就是在每一轮训练后改变训练集的权值或概率分布,通过提高在前一轮被弱学习器预测错误的样例的权值,降低前一轮被弱学习器预测正确的样例的权值,来提高弱学习器对预测错误的数据的重视程度,从而提升模型的整体预测效果。

随机森林

​ 随机森林(Random Forest)是一种经典的Bagging模型,其弱学习器为决策树模型。随机森林模型会在原始数据集中随机抽样,构成n个不同的样本数据集,然后根据这些数据集搭建n个不同的决策树模型,最后根据这些决策树模型的平均值(针对回归模型)或者投票情况(针对分类模型)来获取最终结果。为了保证模型的泛化能力(或者说通用能力),随机森林模型在建立每棵树时,往往会遵循“数据随机”“特征随机”这两个基本原则。

​ 与单独的决策树模型相比,随机森林模型由于集成了多个决策树,其预测结果会更准确,且不容易造成过拟合现象,泛化能力更强。

​ 和决策树模型一样,随机森林模型既能进行分类分析,又能进行回归分析,对应的模型分别为随机森林分类模型(RandomForestClassifier)和随机森林回归模型(RandomForestRegressor)。随机森林分类模型的弱学习器是分类决策树模型,随机森林回归模型的弱学习器则是回归决策树模型。

随机森林相关API:

import sklearn.ensemble as se
# 随机森林回归模型	(属于集合算法的一种)
# max_depth:决策树最大深度10
# n_estimators:构建1000棵决策树,训练模型
# min_samples_split: 子表中最小样本数 若小于这个数字,则不再继续向下拆分
model = se.RandomForestRegressor(max_depth=10, n_estimators=1000,min_samples_split=2,random_state=7)
model.fit(X,y)

# 随集森林分类模型
model=RandomForestClassifier(max_depth=10, n_estimators=1000,min_samples_split=2,random_state=7)
model.fit(X,y)

案例:分析共享单车的需求,从而判断如何进行共享单车的投放。

import numpy as np
import sklearn.utils as su
import sklearn.ensemble as se
import sklearn.metrics as sm
import matplotlib.pyplot as mp

data = np.loadtxt('../data/bike_day.csv', unpack=False, dtype='U20', delimiter=',')
day_headers = data[0, 2:13]
x = np.array(data[1:, 2:13], dtype=float)
y = np.array(data[1:, -1], dtype=float)

x, y = su.shuffle(x, y, random_state=7)
print(x.shape, y.shape)
train_size = int(len(x) * 0.9)
train_x, test_x, train_y, test_y = \
    x[:train_size], x[train_size:], y[:train_size], y[train_size:]
# 随机森林回归器
model = se.RandomForestRegressor( max_depth=10, n_estimators=1000, min_samples_split=2)
model.fit(train_x, train_y)
# 基于“天”数据集的特征重要性
fi_dy = model.feature_importances_
pred_test_y = model.predict(test_x)
print(sm.r2_score(test_y, pred_test_y))

data = np.loadtxt('../data/bike_hour.csv', unpack=False, dtype='U20', delimiter=',')
hour_headers = data[0, 2:13]
x = np.array(data[1:, 2:13], dtype=float)
y = np.array(data[1:, -1], dtype=float)
x, y = su.shuffle(x, y, random_state=7)
train_size = int(len(x) * 0.9)
train_x, test_x, train_y, test_y = \
    x[:train_size], x[train_size:], \
    y[:train_size], y[train_size:]
# 随机森林回归器
model = se.RandomForestRegressor(
    max_depth=10, n_estimators=1000,
    min_samples_split=2)
model.fit(train_x, train_y)
# 基于“小时”数据集的特征重要性
fi_hr = model.feature_importances_
pred_test_y = model.predict(test_x)
print(sm.r2_score(test_y, pred_test_y))

画图显示两组样本数据的特征重要性:

mp.figure('Bike', facecolor='lightgray')
mp.subplot(211)
mp.title('Day', fontsize=16)
mp.ylabel('Importance', fontsize=12)
mp.tick_params(labelsize=10)
mp.grid(axis='y', linestyle=':')
sorted_indices = fi_dy.argsort()[::-1]
pos = np.arange(sorted_indices.size)
mp.bar(pos, fi_dy[sorted_indices], facecolor='deepskyblue', edgecolor='steelblue')
mp.xticks(pos, day_headers[sorted_indices], rotation=30)

mp.subplot(212)
mp.title('Hour', fontsize=16)
mp.ylabel('Importance', fontsize=12)
mp.tick_params(labelsize=10)
mp.grid(axis='y', linestyle=':')
sorted_indices = fi_hr.argsort()[::-1]
pos = np.arange(sorted_indices.size)
mp.bar(pos, fi_hr[sorted_indices], facecolor='lightcoral', edgecolor='indianred')
mp.xticks(pos, hour_headers[sorted_indices], rotation=30)
mp.tight_layout()
mp.show()

AdaBoost

​ AdaBoost算法(Adaptive Boosting)是一种有效而实用的Boosting算法,它以一种高度自适应的方式按顺序训练弱学习器。针对分类问题,AdaBoost算法根据前一次的分类效果调整数据的权重,在上一个弱学习器中分类错误的样本的权重会在下一个弱学习器中增加,分类正确的样本的权重则相应减少,并且在每一轮迭代时会向模型加入一个新的弱学习器。不断重复调整权重和训练弱学习器,直到误分类数低于预设值或迭代次数达到指定最大值,最终得到一个强学习器。简单来说,AdaBoost算法的核心思想就是调整错误样本的权重,进而迭代升级。

​ AdaBoost算法既能做分类分析,也能做回归分析,对应的模型分别为AdaBoost分类模型(AdaBoostClassifier)和AdaBoost回归模型(AdaBoostRegressor)。AdaBoost分类模型的弱学习器是分类决策树模型,AdaBoost回归模型的弱学习器则是回归决策树模型。

sklearn.ensemble.AdaBoostRegressor(base_estimator = None,*,n_estimators = 50,learning_rate = 1.0,loss ='linear',random_state = None )
参数 含义
base_estimator 基本估计器。默认=无,如果是None,则基本估计量为 DecisionTreeRegressor(max_depth=3)。
n_estimators 估计器最大个数。int,默认=50
learning_rate 学习率,float,默认=1
loss 损失函数,取值范围{'linear','square','exponential'},默认:‘linear'
random_state 随机种子,int ,默认=无

AdaBoost相关API:

import sklearn.tree as st
import sklearn.ensemble as se

# AdaBoost分类模型代码,参数详情见官网
# model: 决策树模型(一棵)
model = st.DecisionTreeRegressor(max_depth=4)
# 自适应增强决策树回归模型	
# n_estimators:构建400棵不同权重的决策树,训练模型
model = se.AdaBoostRegressor(model, n_estimators=400, random_state=7)
# 训练模型
model.fit(train_x, train_y)
# 测试模型
pred_test_y = model.predict(test_x)

# AdaBoost分类模型代码
model = AdaBoostClassifier(base_estimator = None,n_estimators = 50,learning_rate = 1.0,algorithm ='SAMME.R',random_state = None)
# AdaBoost分类模型的常用参数和AdaBoost回归模型基本一致,唯一的不同是少了一个loss参数,多了一个# algorithm(算法)取值范围:{'SAMME','SAMME.R'},默认='SAMME.R'

GBDT

​ GBDT是Gradient Boosting Decision Tree(梯度提升树)的缩写。GBDT算法也是一种非常实用的Boosting算法,它与AdaBoost算法的区别在于:AdaBoost算法根据分类效果调整权重并不断迭代,最终生成强学习器;GBDT算法则将损失函数的负梯度作为残差的近似值,不断使用残差迭代和拟合回归树,最终生成强学习器。简单来说,AdaBoost算法是调整权重,而GBDT算法则是拟合残差。

​ GBDT算法既能做分类分析,又能做回归分析,对应的模型分别为GBDT分类模型(GradientBoostingClassifier)和GBDT回归模型(GradientBoostingRegressor)。GBDT分类模型的弱学习器是分类决策树模型,GBDT回归模型的弱学习器则是回归决策树模型。

# GBDT分类模型(GradientBoostingClassifier)

sklearn.ensemble.GradientBoostingClassifier(*, loss='deviance', learning_rate=0.1, n_estimators=100, subsample=1.0, criterion='friedman_mse', min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_depth=3, min_impurity_decrease=0.0, min_impurity_split=None, init=None, random_state=None, max_features=None, verbose=0, max_leaf_nodes=None, warm_start=False, presort='deprecated', validation_fraction=0.1, n_iter_no_change=None, tol=0.0001, ccp_alpha=0.0)
参数名 含义
loss 损失函数,取值范围:{'deviance','exponential'},default ='deviance'
learning_rate 学习率,float,default=0.1
n_estimators 估计器最大个数。int,default=100
subsample 子样本,float,default=1.0
criterion 条件,取值范围:{'friedman_mse','mse','mae'},default='friedman_mse'
min_samples_split 拆分内部节点所需的最少样本数。int/float,default=2
min_samples_leaf 叶节点处所需的最小样本数。int/float,default=1
min_weight_fraction_leaf 在所有叶节点处(所有输入样本)的权重总和中的最小加权分数。float,default=0.0
max_depth 各个估计器的最大深度。int,default= 3
min_impurity_decrease 如果节点分裂会导致杂质的减少大于或等于该值,则该节点将被分裂。float,default=0.0
min_impurity_split 最小不纯度分裂停止阈值。如果节点的杂质高于阈值,则该节点将分裂,否则为叶。float,default=0.0
init 初始值,estimator or ‘zero’, default=None
random_state 随机种子,int,default=None
max_features 寻找最佳分割时要考虑的功能数量。{'auto','sqrt','log2'},int/float,default=None
verbose 启用详细输出。int,default=0
max_leaf_nodes 最大叶子节点。int,default=None,如果为None,则叶节点数不受限制。
# GBDT回归模型(GradientBoostingRegressor)

sklearn.ensemble.GradientBoostingRegressor(*, loss='ls', learning_rate=0.1, n_estimators=100, subsample=1.0, criterion='friedman_mse', min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_depth=3, min_impurity_decrease=0.0, min_impurity_split=None, init=None, random_state=None, max_features=None, alpha=0.9, verbose=0, max_leaf_nodes=None, warm_start=False, presort='deprecated', validation_fraction=0.1, n_iter_no_change=None, tol=0.0001, ccp_alpha=0.0)

GBDT分类模型相关API:

import sklearn.tree as st
import sklearn.ensemble as se

# GBDT分类模型代码
model = GradientBoostingClassifier(random_state=7)
model.fit(X,y)

# GBDT回归模型代码
# 自适应增强决策树回归模型	
# n_estimators:构建400棵不同权重的决策树,训练模型
model = se.GridientBoostingRegressor(max_depth=10, n_estimators=1000, min_samples_split=2)
# 训练模型
model.fit(train_x, train_y)
# 测试模型
pred_test_y = model.predict(test_x)

XGBoost

​ XGBoost算法是由华盛顿大学的陈天奇博士开发并开源给大家使用的,其官方说明文档地址为https://xgboost.readthedocs.io,其中有很多算法细节和使用方法。XGBoost和LightGBM算法可以说是目前机器学习竞赛中的明星算法,在商业实战中也有非常广泛的应用场景。这两种算法运行速度快,预测准确度高,且支持并行操作,极大地提升了机器学习的效率和效果,无论是分类模型还是回归模型都有很大的作用。

​ XGBoost算法可以说是集成学习方法中的王牌算法。在著名的数据挖掘比赛平台Kaggle上,众多获胜者都使用了XGBoost算法,它在绝大多数回归问题和分类问题上的表现都十分不错。

XGBoost算法的核心思想

​ XGBoost算法在某种程度上可以说是GBDT算法的改良版,两者在本质上都是利用了Boosting算法中拟合残差的思想。

作为对GBDT算法的高效实现,XGBoost算法在以下两方面进行了优化:

算法本身的优化:XGBoost算法的损失函数,除了本身的损失,还加上了正则化部分,可以防止过拟合,泛化能力更强。XGBoost算法的损失函数是对误差部分做二阶泰勒展开,相较于GBDT算法的损失函数只对误差部分做负梯度(一阶泰勒)展开,更加准确。

算法运行效率的优化:对每个弱学习器,如决策树建立的过程做并行选择,找到合适的子节点分裂特征和特征值,从而提升运行效率。

XGBoost算法既能做分类分析,又能做回归分析,对应的模型分别为XGBoost分类模型(XGBClassifier)和XGBoost回归模型(XGBRegressor)。

安装XGBoost:

pip install xgboost

XGBoost分类模型:

import xgboost as xgb

model = xgb.XGBClassifier()
model.fit(X,y)

XGBoost回归模型:

import xgboost as xgb

model = xgb.XGBRegressor()
model.fit(X,y)

XGBoost参数:

参数 含义
max_depth 弱学习器决策树的最大深度,默认取3。
n_estimators 弱学习器的个数,或者说弱学习器的最大迭代次数,默认取100。
learning_rate 学习率,又称为每个弱学习器的权重缩减系数,取值范围为(0,1],取值较小意味着要达到一定的学习效果,需要更多迭代次数和更多弱学习器,默认取0.1。

Light_GBM

​ LigthGBM算法是Boosting算法的新成员,由微软公司开发。它和XGBoost算法一样是对GBDT算法的高效实现,在原理上与GBDT算法和XGBoost算法类似,都采用损失函数的负梯度作为当前决策树的残差近似值,去拟合新的决策树。

​ 与传统的机器学习算法相比,LightGBM算法具有这些优势:训练效率更高;低内存使用;准确率更高;支持并行化学习;可以处理大规模数据。

​ LightGBM算法的官方说明文档网址为https://lightgbm.readthedocs.io,其中有很多对该算法的原理及使用方法的讲解。

​ 大部分决策树算法使用的生长策略是level-wise生长策略,即同一层的叶子节点每次都一起分裂,如下图所示。但实际上一些叶子节点的分裂增益较低,这样分裂会增加不小的开销。

LightGBM算法数学原理的核心算法:

1.基于leaf-wise的决策树生长策略

​ 大部分决策树算法使用的生长策略是level-wise生长策略,即同一层的叶子节点每次都一起分裂,如下图所示。但实际上一些叶子节点的分裂增益较低,这样分裂会增加不小的开销。LightGBM算法使用的则是leaf-wise生长策略,每次在当前叶子节点中找出分裂增益最大的叶子节点进行分裂,而不是所有节点都进行分裂,如下图所示,这样可以提高精度。但是,leaf-wise策略在样本量较小时容易造成过拟合,LightGBM算法可以通过参数max_depth限制树的深度来防止过拟合。

2.直方图算法

​ 直方图分为频数直方图和频率直方图,横坐标为相关数据,纵坐标为该数据出现的频数或频率。直方图算法又称为histogram算法,简单来说,就是先对特征值进行装箱处理,将连续的浮点特征值离散化成k个整数,形成一个个箱体(bins),同时构造一个宽度为k的直方图,在遍历数据时,以离散化后的值作为索引在直方图中累积统计量(因此这里是频数直方图)。遍历一次数据后,直方图累积了需要的统计量,再根据直方图的离散值遍历寻找最优分割点。对于连续特征来说,装箱处理就是特征工程中的离散化。例如,[0,10)区间的值都赋值为0,[10,20)区间的值都赋值为1等,这样就可以把众多的数值划分到有限的分箱中。LightGBM算法中默认的分箱数(bins)为256。

​ 举例来说,现在有10000个客户,也就有10000个身高数据,将身高分箱为256份后(例如,身高180~180.2cm的所有客户都分箱为数字200),就变为256个数字,这时再统计每个数值对应的频数(例如,身高180~180.2cm的客户为100人,那么数字200对应的频数就是100)。这样在节点分裂时,就不需要按照预排序算法对每个特征都计算10000遍(样本总数),而只需要计算256遍(分箱数),大大加快了训练速度。对于分类特征来说,则是将每一种取值放入一个分箱(bin),且当取值的个数大于最大分箱数时,会忽略那些很少出现的分类值。例如,10000个客户的国籍数据,便可以按国家名称进行分箱,如果超过最大分箱数(如256),那么很少出现的国家就会被忽略。

3.并行学习

​ LightGBM算法支持特征并行和数据并行两种学习方式。传统的特征并行的主要思想是在并行化决策树中寻找最佳切分点,在数据量大时难以加速,同时需要对切分结果进行通信整合。而LightGBM算法在本地保存全部数据,这样就没有了机器间通信所需的开销。此外,传统的数据并行是构建本地直方图,然后进行整合,在全局直方图中寻找最佳切分点。LightGBM算法则使用分散规约(reduce scatter),将直方图合并的任务分给不同的机器,降低通信和计算的开销,并利用直方图做加速训练,进一步减少开销。

​ 除了上述原理,LightGBM算法还包含一些重要的算法思想,如单边梯度采样GOSS算法(Gradient-based One-SideSampling)和互斥特征绑定EFB算法(Exclusive FeatureBundling)。在GOSS算法中,梯度更大的样本点在计算信息增益时会发挥更重要的作用,当对样本进行下采样时保留这些梯度较大的样本点,并随机去掉梯度小的样本点。EFB算法则将互斥特征绑在一起以减少特征维度。感兴趣的读者可以查阅相关文档做进一步了解。

​ LightGBM算法既能做分类分析,又能做回归分析,对应的模型分别为LightGBM分类模型(LGBMClassifier)和LightGBM回归模型(LGBMRegressor)。

LightGBM相关API:

import LightGBM as lgb

# 分类
model = LGBMClassifier()
model.fit(X,y)

# 回归
model = LGBMRegressor()
model.fit(X,y)

模型参数:

参数 含义
num_leaves 决策树的最大叶子节点数,即决策树最多有多少个叶子节点,默认取31。因为LightGBM模型使用的是leaf-wise生长策略,所以在调节树的复杂度时常用的参数是num_leaves,而不是树的最大深度参数max_depth。
n_estimators 弱学习器的个数,或者说是弱学习器的最大迭代次数,默认取100。
learning_rate 学习率,又称为每个弱学习器的权重缩减系数,取值范围为(0,1],默认取0.1。取值较小意味着要达到一定的误分类数或学习效果,需要更多迭代次数和更多弱学习器。

朴素贝叶斯

贝叶斯分类是机器学习中应用极为广泛的分类算法之一,其产生自英国数学家贝叶斯对于逆概问题的思考。朴素贝叶斯是贝叶斯模型当中最简单的一种。

贝叶斯公式 : P(A|B) = P(B|A)P(A) / P(B)

其中P(A)为事件A发生的概率,P(B)为事件B发生的概率,P(A|B)表示在事件B发生的条件下事件A发生的概率,同理P(B|A)则表示在事件A发生的条件下事件B发生的概率。

朴素贝叶斯分类是贝叶斯分类中最简单,也是常见的一种分类方法

P( Y|X1,X2,...,Xn ) = p( X1,X2,...Xn|Y) P(Y) / P(X1,X2,...Xn)

朴素贝叶斯模型假设给定目标值后各个特征之间相互独立,分子的计算公式可以写成如下形式:

P( X1,X2,....Xn|Y)P(Y) = P(X1|Y)P(X2|Y)P(X3|Y)...P(Xn|Y)P(Y)。其中P(X1|Y)、P(X2|Y)、P(Y)等数据都是已知的,由此可以计算在n个特征变量取不同的值的条件下,目标变量取某个值的概率,并且选择概率更高者对样本进行分类。

朴素贝叶斯模型相关API:

import sklearn.naive_bayes as nb

# 创建高斯分布朴素贝叶斯分类器,它适用于任何连续数值型的数据集。
model = nb.GaussianNB()
model = nb.MultinomialNB()
model.fit(x, y)
result = model.predict(samples)

朴素贝叶斯模型属于分类模型,是一种非常经典的机器学习模型,它主要基于贝叶斯公式,在应用过程中会把数据集中的特征看成是相互独立的,而不需考虑特征间的关联关系,因此运算速度较快。相比于其他经典的机器学习模型,朴素贝叶斯模型的泛化能力稍弱,不过当样本及特征的数量增加时,其预测效果也是不错的。

K近邻算法

​ 当有多种类别数据时,我们常常面临着对新加入的数据进行分类的问题,例如,根据口味和色泽划分新的葡萄酒的类别,根据内容形式划分新上映电影的类别,根据过往人脸特征进行人脸识别等。这些问题都可以采用机器学习中非常经典的K近邻算法来解决。

K近邻算法的原理:

​ K近邻算法(英文为K-Nearest Neighbor,因而又简称KNN算法)是非常经典的机器学习算法。K近邻算法的原理非常简单:对于一个新样本,K近邻算法的目的就是在已有数据中寻找与它最相似的K个数据,或者说“离它最近”的K个数据,如果这K个数据大多数属于某个类别,则该样本也属于这个类别。

这里采用最为常见的欧氏距离来定义向量空间内2个点的距离,2维空间公式:|AB| =SQRT( (X1- X2)^2 + (Y1 -Y2)^2)这个其实就是常见的两点间距离公式,其适用于只有2个特征变量的情况。实际应用中,数据的特征通常有n个,此时可将该距离公式推广到n维空间,如n维向量空间内A点坐标为(X1,X2,X3,…,Xn),B点坐标为(Y1,Y2,Y3,…,Yn),那么A、B两点间的欧氏距离计算公式如下:|AB| =SQRT( (X1- X2)^2 + (Y1 -Y2)2+...+(Xn-Yn)2)

相关API:

from sklearn.neighbors import KNeighborsClassifier

sklearn.neighbors.KNeighborsClassifier(n_neighbors = 5,*,weights ='uniform',algorithm ='auto',leaf_size = 30,p = 2,metric ='minkowski',metric_params = None,n_jobs = None,** kwargs )
参数名 含义
n_neighbors int, default=5。邻近数
weights {‘uniform’, ‘distance’} or callable, default=’uniform'。预测中使用的权重函数
algorithm {‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’}, default=’auto’。用于计算最近邻居的算法
leaf_size int, default=30。传递的叶大小
P int, default=2。Minkowski指标的功率参数。当p = 1时,这等效于对p = 2使用manhattan_distance(l1)和euclidean_distance(l2)。对于任意p,使用minkowski_distance(l_p)。
metric str or callable, default=’minkowski’。树使用的距离度量
metric_params dict, default=None。度量功能的其他关键字参数
n_jobs int, default=None。为邻居搜索运行的并行作业数

​ K近邻算法分类模型(KNeighborsClassifier)进行分类分析,K近邻算法还可以做回归分析,对应的模型为K近邻算法回归模型(KNeighborsRegressor)。K近邻算法分类模型将离待预测样本点最近的K个训练样本点中出现次数最多的分类作为待预测样本点的分类,K近邻算法回归模型则将离待预测样本点最近的K个训练样本点的平均值作为待预测样本点的分类。

sklearn.neighbors.KNeighborsRegressor(n_neighbors = 5,*,weights ='uniform',algorithm ='auto',leaf_size = 30,p = 2,metric ='minkowski',metric_params = None,n_jobs = None,** kwargs )

支持向量机(SVM)

支持向量机原理

  1. 寻求最优分类边界

    正确:对大部分样本可以正确地划分类别。

    泛化:最大化支持向量间距。

    公平:与支持向量等距。

    简单:线性,直线或平面,分割超平面。

  2. 基于核函数的升维变换

    通过名为核函数的特征变换,增加新的特征,使得低维度空间中的线性不可分问题变为高维度空间中的线性可分问题。

    线性核函数:linear,不通过核函数进行维度提升,仅在原始维度空间中寻求线性分类边界。

    基于线性核函数的SVM分类相关API:

    model = svm.SVC(kernel='linear')
    model.fit(train_x, train_y)
    

    案例:对simple2.txt中的数据进行分类。

    import numpy as np
    import sklearn.model_selection as ms
    import sklearn.svm as svm
    import sklearn.metrics as sm
    import matplotlib.pyplot as mp
    x, y = [], []
    data = np.loadtxt('../data/multiple2.txt', delimiter=',', dtype='f8')
    x = data[:, :-1]
    y = data[:, -1]
    train_x, test_x, train_y, test_y = \
        ms.train_test_split(x, y, test_size=0.25, random_state=5)
    # 基于线性核函数的支持向量机分类器
    model = svm.SVC(kernel='linear')
    model.fit(train_x, train_y)
    n = 500
    l, r = x[:, 0].min() - 1, x[:, 0].max() + 1
    b, t = x[:, 1].min() - 1, x[:, 1].max() + 1
    grid_x = np.meshgrid(np.linspace(l, r, n),
                         np.linspace(b, t, n))
    flat_x = np.column_stack((grid_x[0].ravel(), grid_x[1].ravel()))    
    flat_y = model.predict(flat_x)
    grid_y = flat_y.reshape(grid_x[0].shape)
    pred_test_y = model.predict(test_x)
    cr = sm.classification_report(test_y, pred_test_y)
    print(cr)
    mp.figure('SVM Linear Classification', facecolor='lightgray')
    mp.title('SVM Linear Classification', fontsize=20)
    mp.xlabel('x', fontsize=14)
    mp.ylabel('y', fontsize=14)
    mp.tick_params(labelsize=10)
    mp.pcolormesh(grid_x[0], grid_x[1], grid_y, cmap='gray')
    mp.scatter(test_x[:, 0], test_x[:, 1], c=test_y, cmap='brg', s=80)
    mp.show()
    

    多项式核函数:poly,通过多项式函数增加原始样本特征的高次方幂

    \[y = x_1+x_2 \\ y = x_1^2 + 2x_1x_2 + x_2^2 \\ y = x_1^3 + 3x_1^2x_2 + 3x_1x_2^2 + x_2^3 \]

    案例,基于多项式核函数训练sample2.txt中的样本数据。

    # 基于线性核函数的支持向量机分类器
    model = svm.SVC(kernel='poly', degree=3)
    model.fit(train_x, train_y)
    

    径向基核函数:rbf,通过高斯分布函数增加原始样本特征的分布概率

    案例,基于径向基核函数训练sample2.txt中的样本数据。

    # 基于径向基核函数的支持向量机分类器
    # C:正则强度
    # gamma:正态分布曲线的标准差
    model = svm.SVC(kernel='rbf', C=600, gamma=0.01)
    model.fit(train_x, train_y)
    

聚类模型

分类(class)与聚类(cluster)不同,分类是有监督学习模型,聚类属于无监督学习模型。聚类讲究使用一些算法把样本划分为n个群落。一般情况下,这种算法都需要计算欧氏距离。

欧氏距离即欧几里得距离。

\[P(x_1) - Q(x_2): |x_1-x_2| = \sqrt{(x_1-x_2)^2} \\ P(x_1,y_1) - Q(x_2,y_2): \sqrt{(x_1-x_2)^2+(y_1-y_2)^2} \\ P(x_1,y_1,z_1) - Q(x_2,y_2,z_2): \sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2} \\ \]

用两个样本对应特征值之差的平方和之平方根,即欧氏距离,来表示这两个样本的相似性。

KMeans算法

​ KMeans算法名称中的K代表类别数量,Means代表每个类别内样本的均值,所以KMeans算法又称为K-均值算法。KMeans算法以距离作为样本间相似度的度量标准,将距离相近的样本分配至同一个类别。样本间距离的计算方式可以是欧氏距离、曼哈顿距离、余弦相似度等,KMeans算法通常采用欧氏距离来度量各样本间的距离。KMeans算法的核心思想是对每个样本点计算到各个中心点的距离,并将该样本点分配给距离最近的中心点代表的类别,一次迭代完成后,根据聚类结果更新每个类别的中心点,然后重复之前操作再次迭代,直到前后两次分类结果没有差别。

第一步:随机选择k个样本作为k个聚类的中心,计算每个样本到各个聚类中心的欧氏距离,将该样本分配到与之距离最近的聚类中心所在的类别中。

第二步:根据第一步所得到的聚类划分,分别计算每个聚类的几何中心,将几何中心作为新的聚类中心,重复第一步,直到计算所得几何中心与聚类中心重合或接近重合为止。

注意:

  1. 聚类数k必须事先已知。借助某些评估指标,优选最好的聚类数。
  2. 聚类中心的初始选择会影响到最终聚类划分的结果。初始中心尽量选择距离较远的样本。

K均值算法相关API:

import sklearn.cluster as sc
# n_clusters: 聚类数
model = sc.KMeans(n_clusters=4)
# 不断调整聚类中心,知道最终聚类中心稳定则聚类完成
model.fit(x)
# 获取训练结果的聚类中心
labels = model.labels_
centers = model.cluster_centers_
pred_y = model.predict(x)

案例:加载multiple3.txt,基于K均值算法完成样本的聚类。

import numpy as np
import sklearn.cluster as sc
import matplotlib.pyplot as mp
x = np.loadtxt('../data/multiple3.txt', delimiter=',')
# K均值聚类器
model = sc.KMeans(n_clusters=4)
model.fit(x)
centers = model.cluster_centers_
n = 500
l, r = x[:, 0].min() - 1, x[:, 0].max() + 1
b, t = x[:, 1].min() - 1, x[:, 1].max() + 1
grid_x = np.meshgrid(np.linspace(l, r, n),
                     np.linspace(b, t, n))
flat_x = np.column_stack((grid_x[0].ravel(), grid_x[1].ravel()))    
flat_y = model.predict(flat_x)
grid_y = flat_y.reshape(grid_x[0].shape)
pred_y = model.predict(x)
mp.figure('K-Means Cluster', facecolor='lightgray')
mp.title('K-Means Cluster', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.pcolormesh(grid_x[0], grid_x[1], grid_y, cmap='gray')
mp.scatter(x[:, 0], x[:, 1], c=pred_y, cmap='brg', s=80)
mp.scatter(centers[:, 0], centers[:, 1], marker='+', c='gold', s=1000, linewidth=1)
mp.show()

均值漂移算法

首先假定样本空间中的每个聚类均服从某种已知的概率分布规则,然后用不同的概率密度函数拟合样本中的统计直方图,不断移动密度函数的中心(均值)的位置,直到获得最佳拟合效果为止。这些概率密度函数的峰值点就是聚类的中心,再根据每个样本距离各个中心的距离,选择最近聚类中心所属的类别作为该样本的类别。

均值漂移算法的特点:

  1. 聚类数不必事先已知,算法会自动识别出统计直方图的中心数量。
  2. 聚类中心不依据于最初假定,聚类划分的结果相对稳定。
  3. 样本空间应该服从某种概率分布规则,否则算法的准确性会大打折扣。

均值漂移算法相关API:

# 量化带宽,决定每次调整概率密度函数的步进量
# n_samples:样本数量
# quantile:量化宽度(直方图一条的宽度)
bw = sc.estimate_bandwidth(x, n_samples=len(x), quantile=0.1)
# 均值漂移聚类器
model = sc.MeanShift(bandwidth=bw, bin_seeding=True)
model.fit(x)

案例:加载multiple3.txt,使用均值漂移算法对样本完成聚类划分。

import numpy as np
import sklearn.cluster as sc
import matplotlib.pyplot as mp

x = np.loadtxt('../data/multiple3.txt', delimiter=',')
# 量化带宽,决定每次调整概率密度函数的步进量
bw = sc.estimate_bandwidth(x, n_samples=len(x), quantile=0.2)
# 均值漂移聚类器
model = sc.MeanShift(bandwidth=bw, bin_seeding=True)
model.fit(x)
centers = model.cluster_centers_
n = 500
l,  r = x[:, 0].min() - 1, x[:, 0].max() + 1
b,  t = x[:, 1].min() - 1, x[:, 1].max() + 1
grid_x = np.meshgrid(np.linspace(l, r, n),
                     np.linspace(b, t, n))
flat_x = np.column_stack((grid_x[0].ravel(), grid_x[1].ravel()))
flat_y = model.predict(flat_x)
grid_y = flat_y.reshape(grid_x[0].shape)
pred_y = model.predict(x)
mp.figure('Mean Shift Cluster', facecolor='lightgray')
mp.title('Mean Shift Cluster', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.pcolormesh(grid_x[0], grid_x[1], grid_y, cmap='gray')
mp.scatter(x[:, 0], x[:, 1], c=pred_y, cmap='brg', s=80)
mp.scatter(centers[:, 0], centers[:, 1], marker='+', c='gold', s=1000, linewidth=1)
mp.show()

轮廓系数

好的聚类:内密外疏,同一个聚类内部的样本要足够密集,不同聚类之间样本要足够疏远。

轮廓系数计算规则:针对样本空间中的一个特定样本,计算它与所在聚类其它样本的平均距离a,以及该样本与距离最近的另一个聚类中所有样本的平均距离b,该样本的轮廓系数为(b-a)/max(a, b),将整个样本空间中所有样本的轮廓系数取算数平均值,作为聚类划分的性能指标s。

轮廓系数的区间为:[-1, 1]。 -1代表分类效果差,1代表分类效果好。0代表聚类重叠,没有很好的划分聚类。

轮廓系数相关API:

import sklearn.metrics as sm
# v:平均轮廓系数
# metric:距离算法:使用欧几里得距离(euclidean)
v = sm.silhouette_score(输入集, 输出集, sample_size=样本数, metric=距离算法)

案例:输出KMeans算法聚类划分后的轮廓系数。

# 打印平均轮廓系数
print(sm.silhouette_score( x, pred_y, sample_size=len(x), metric='euclidean'))

DBSCAN算法

从样本空间中任意选择一个样本,以事先给定的半径做圆,凡被该圆圈中的样本都视为与该样本处于相同的聚类,以这些被圈中的样本为圆心继续做圆,重复以上过程,不断扩大被圈中样本的规模,直到再也没有新的样本加入为止,至此即得到一个聚类。于剩余样本中,重复以上过程,直到耗尽样本空间中的所有样本为止。

DBSCAN算法的特点:

  1. 事先给定的半径会影响最后的聚类效果,可以借助轮廓系数选择较优的方案。

  2. 根据聚类的形成过程,把样本细分为以下三类:

    外周样本:被其它样本聚集到某个聚类中,但无法再引入新样本的样本。

    孤立样本:聚类中的样本数低于所设定的下限,则不称其为聚类,反之称其为孤立样本。

    核心样本:除了外周样本和孤立样本以外的样本。

DBSCAN聚类算法相关API:

# DBSCAN聚类器
# eps:半径
# min_samples:聚类样本数的下限,若低于该数值,则称为孤立样本
model = sc.DBSCAN(eps=epsilon, min_samples=5)
model.fit(x)

案例:修改凝聚层次聚类案例,基于DBSCAN聚类算法进行聚类划分,选择最优半径。

import numpy as np
import sklearn.cluster as sc
import sklearn.metrics as sm
import matplotlib.pyplot as mp

x = np.loadtxt('../data/perf.txt', delimiter=',')
epsilons, scores, models = np.linspace(0.3, 1.2, 10), [], []
for epsilon in epsilons:
    # DBSCAN聚类器
    model = sc.DBSCAN(eps=epsilon, min_samples=5)
    model.fit(x)
    score = sm.silhouette_score(
        x, model.labels_, sample_size=len(x), metric='euclidean')
    scores.append(score)
    models.append(model)
scores = np.array(scores)
best_index = scores.argmax()
best_epsilon = epsilons[best_index]
print(best_epsilon)
best_score = scores[best_index]
print(best_score)
best_model = models[best_index]

案例:获取核心样本、外周样本、孤立样本。并且使用不同的点型绘图。

best_model = models[best_index]
pred_y = best_model.fit_predict(x)
core_mask = np.zeros(len(x), dtype=bool)
core_mask[best_model.core_sample_indices_] = True
offset_mask = best_model.labels_ == -1
periphery_mask = ~(core_mask | offset_mask)
mp.figure('DBSCAN Cluster', facecolor='lightgray')
mp.title('DBSCAN Cluster', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
labels = best_model.labels_
mp.scatter(x[core_mask][:, 0], x[core_mask][:, 1], c=labels[core_mask], 
           cmap='brg', s=80, label='Core')
mp.scatter(x[periphery_mask][:, 0], x[periphery_mask][:, 1], alpha=0.5,
           c=labels[periphery_mask], cmap='brg', marker='s', s=80, label='Periphery')
mp.scatter(x[offset_mask][:, 0], x[offset_mask][:, 1],
           c=labels[offset_mask], cmap='brg', marker='x', s=80, label='Offset')
mp.legend()
mp.show()
posted @ 2021-07-16 16:07  小石小石摩西摩西  阅读(275)  评论(0编辑  收藏  举报