机器学习入门基础(一)

机器学习简介

在这里插入图片描述

  • 祖师爷
    • 图灵,人工智能之父,最大成就图灵测试,就是一个机器和一个人跟你聊天,你不知道对方是人还是机器,如果经过聊天后,你分辨不出谁是人谁是机器说明这个机器通过了图灵测试。
  • 人工智能和机器学习的区别?
    • 机器学习就是实现人工智能的一种手段
  • 什么是机器学习?
    • 机器学习就是从【数据】自动分析获得【规律(模型)】,并利用规律对未知数据进行【预测】。
  • 老师的理解
    • 模型:
      • 算法模型,是一个特殊的对象。该算法模型对象中已经集成或者封装好了某种形式的方程、算法。(还没有求出解的方程)
    • 模型的作用:
      • 预测:可以通过方程或者算法产生一个新的未知的数据 / 事务
      • 分类:可以将一个未知归类的事务给其归属到一个已有的类群中
      • 注意:算法模型对应的算法或方程求出的解就是预测或分类的结果。
    • 样本数据
      • 模型的训练:将样本数据带入到模型中,对其进行训练(给方程进行求解操作),模型训练好了之后,则模型的方程就有了唯一的解或者最优解。有解后模型就可以实现分类或者预测的功能。
      • 构成:
        • 特征数据:自变量(楼层、面积、采光率)
        • 标签 / 目标数据:因变量(售价)
        • 例子:
楼层        采光率			面积				售价
 3			55%				100				200w
 5			88%				100				315w
......
  • 模型的分类
    - 有监督学习
    - 如果模型需要的样本数据必须包含特征数据和标签数据,则该模型为有监督学习分类
    - 半监督学习
    - 无监督学习
    - 模型需要的样本数据只需要有特征数据即可,目标数据有或者无都可以。
    • 样本数据(数据集)的载体:
      • 通常情况下历史数据不会存储在数据库中,而是存储在文件中(csv文件)
      • 数据库存储数据存在的问题:
        • 性能瓶颈:数据量级大的数据很难存储和进行高效的读写。
        • 数据存储格式不符合机器学习要求的数据格式
    • 样本数据获取的途径:
      • kaggle:数据竞赛平台
      • UCI数据集:是一个常用的机器学习标准测试数据集,是加州大学欧文分校提出的用于机器学习的数据库
      • sklearn

特征工程

  • 特征提取

  • 数据特征的预处理

  • 特征选择

  • 为什么需要特征工程 ?

    • 样本数据中的特征可能存在缺省值,重复值,异常值等等,那我们是需要对特征中的相关的噪点数据进行处理的,那么处理的目的就是为了营造一个更纯净的样本集,让模型基于这组数据有更好的预测能力。当然特征工程不是单单只处理上述操作。
  • 什么是特征工程?

    • 特征工程是将原始数据转换为更好的代表预测模型的潜在问题的特征的过程,从而提高未知数据预测的准确性
    • 特征工程的意义:
      • 直接影响模型预测的结果
    • 如何实现特征工程
      • 工具:sk-learn
    • sk-learn介绍
      • 是python语言中的机器学习工具,包含了很多机器学习算法的实现,其文档完善,容易上手。
      • 功能:
        • 分类模型
        • 回归模型
        • 聚类模型
        • 特征工程
  • 特征提取

    • 目的:
      • 我们所采集到样本中的特征数据往往很多时候为字符串或者其他类型的数据,我们知道电脑可以识别二进制数值型的数据,机器学习的数据需要的数值型的数据进行学习。
      • 效果演示
        • 将字符串转换成数字
          In [2]:
	from sklearn.feature_extraction.text import CountVectorizer
	vector=CountVectorizer()
	res=vector.fit_transform(['lift is short,i love python','lift is too long,i hate python'])
	print(res.toarray())
	#输出结果 
	[[0 1 1 0 1 1 1 0]
 [1 1 1 1 0 1 0 1]]
  • 演示后的结论
    • 特征抽取对文本数据进行特征值化。特征值为了让机器更好的理解数据。
  • 字典特征提取
    -作用:对字典数据进行特征值化
    -api: from sklearn.feature_extraction.text import DictVectorizer
    -fit_transform(x):x为字典或者包含字典的迭代器,返回值为sparse矩阵
    -inverse_transform(x):x为sparse矩阵或者array数组,返回值为转换之前的数据格式
    -transform(x):按照原先的标准转换
    -get_feature_names():返回类别名称
    In [3]:
from sklearn.feature_extraction import DictVectorizer

alist = [{'city': 'BeiJing', 'temp': 33}, {'city': 'GZ', 'temp': 42}, {'city': 'SH', 'temp': 40}]
# 实例化一个工具类对象
d = DictVectorizer()
# 返回的是一个sparse矩阵(存储的就是特征值化的结果)
feature = d.fit_transform(alist)
print(feature)
# 输出结果: 
  (0, 0)	1.0
  (0, 3)	33.0
  (1, 1)	1.0
  (1, 3)	42.0
  (2, 2)	1.0
  (2, 3)	40.0
  • 什么是sparse矩阵?
    • 在DictVectorizer类的构造方法中设定sparse=False则返回的就不是sparse矩阵,而是一个数组。
    • sparse矩阵就是一个变相的数组或者列表,目的是为了节省内存
      In [5]:
from sklearn.feature_extraction import DictVectorizer

alist = [{'city': 'BeiJing', 'temp': 33}, {'city': 'GZ', 'temp': 42}, {'city': 'SH', 'temp': 40}]
d = DictVectorizer(sparse=False)
# 返回的是一个sparse矩阵(存储的就是特征值化的结果)
feature = d.fit_transform(alist)
print(d.get_feature_names())
print(feature)
# 输出结果:1为是,0为不是
['city=BeiJing', 'city=GZ', 'city=SH', 'temp']
[[ 1.  0.  0. 33.]
 [ 0.  1.  0. 42.]
 [ 0.  0.  1. 40.]]
  • OneHot编码
    • sparse矩阵中的0和1就是OneHot编码
    • 为什么需要OneHot编码呢?
      • 特征抽取主要目的就是对非数值型的数据进行特征值化!
        fig1
        fig2
  • 基于pandas实现OneHot编码
    • pd.get_dummies(df[‘col’])
      In [7]:
import pandas as pd

df = pd.DataFrame([['green', 'M', 20, 'class1'],
                   ['red', 'L', 21, 'class2'],
                   ['blue', 'XL', 22, 'class3']])
df.columns = ['color', 'size', 'weight', 'class label']
print(df)
print(pd.get_dummies(df['color']))
# 输出结果:
   color size  weight class label
0  green    M      20      class1
1    red    L      21      class2
2   blue   XL      22      class3

   blue  green  red
0     0      1    0
1     0      0    1
2     1      0    0

  • 文本特征提取
    • 作用:对文本数据进行特征值化
    • API:from sklearn.feature_extraction.text import CountVectorizer
    • -fit_transform(x):x为文本或者包含文本字符串的可迭代对象,返回sparse矩阵
      -inverse_transform(x):x为sparse矩阵或者array数组,返回值为转换之前的数据格式
      -toarray():将sparse转换成数组
      -get_feature_names():返回类别名称
      In [6]:
from sklearn.feature_extraction.text import CountVectorizer
	vector=CountVectorizer()
	res=vector.fit_transform(['lift is short,i love python','lift is too long,i hate python'])
	print(res)
	print(vector.get_feature_names())
	print(res.toarray())#将sparse矩阵转换成数组
	# 单字母不统计(因为单个字母代表不了实际含义),然后每个数字表示的是单词出现的次数
	# 输出结果:
	(0, 2)	1
  (0, 1)	1
  (0, 6)	1
  (0, 4)	1
  (0, 5)	1
  (1, 2)	1
  (1, 1)	1
  (1, 5)	1
  (1, 7)	1
  (1, 3)	1
  (1, 0)	1
['hate', 'is', 'lift', 'long', 'love', 'python', 'short', 'too']
[[0 1 1 0 1 1 1 0]
 [1 1 1 1 0 1 0 1]]
  • 中文文本特征提取
    • 对有标点符号的中文文本进行特征提取
      In [15]:
from sklearn.feature_extraction.text import CountVectorizer

vector = CountVectorizer()
res = vector.fit_transform(['人生苦短,我用python', '人生漫长,不用python'])
print(res)
print(vector.get_feature_names())
print(res.toarray())
#输出结果:
 (0, 2)	1
  (0, 3)	1
  (1, 1)	1
  (1, 0)	1
['不用python', '人生漫长', '人生苦短', '我用python']
[[0 0 1 1]
 [1 1 0 0]]
  • 对有标点符号且有空格分隔的中文文本进行特征处理
    In[8]:
from sklearn.feature_extraction.text import CountVectorizer

vector = CountVectorizer()
res = vector.fit_transform(['人生 苦短,我 用python', '人生 漫长,不用python'])
print(res)
print(vector.get_feature_names())
print(res.toarray())
# 单个汉字不统计
# 输出结果:
 (0, 1)	1
  (0, 4)	1
  (0, 3)	1
  (1, 1)	1
  (1, 2)	1
  (1, 0)	1
['不用python', '人生', '漫长', '用python', '苦短']
[[0 1 0 1 1]
 [1 1 1 0 0]]
  • 目前CountVectorizer只可以对标点符号和用分隔符对应的文本进行特征提取,显然这是满足不了我们日常需求的:
    • 因为在自然语言处理中,我们是需要将一段中文文本中相关的词语,成语,形容词…都需要提取的
  • **jieba分词 **
    • 对中文文章进行分词处理
    • pip install jieba
    • jieba分词的基本使用

In[17]:

# 基本使用:对文章进行分词
import jieba

jb = jieba.cut("我是一个好人")
content = list(jb)
print(content)
ct = ' '.join(content)
print(ct)  # 返回空格区分的词语
# 输出结果:
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\21641\AppData\Local\Temp\jieba.cache
Loading model cost 0.522 seconds.
Prefix dict has been built successfully.
['我', '是', '一个', '好人']
我 是 一个 好人
  • 特征预处理
    • 无量纲化:
      • 在机器学习算法实践中,我们往往有着将不同规格的数据转换到同一规格,或不同分布的数据转换到某个特定分布的需求这种需求统称为将数据“无量纲化”,譬如梯度和矩阵为核心的算法中,逻辑回归,支持向量机,神经网络,使用无量纲化可以加快求解速度;而在距离类模型,譬如K近邻,K-Means聚类中,无量纲化可以帮我们提升模型精度,避免某一个取值范围特别大的特征对距离计算造成影响。(一个特例是决策树和树的集成算法们,对决策树不需要无量纲化,决策树可以把任意数据处理的很好。)
      • 那么预处理就是用来实现无量纲化的方式
    • 含义:特征提取后我们就获取对应的数值型的样本数据,然后就可以进行数据处理了。
    • 概念:通过特定的统计方法(数学方法),将数据转换算法要求的数据
    • 方式:
      • 归一化
      • 标准化
      • 如果认为每一个特征具有同等大小的权重都同等重要,则必须对其进行归一化处理。
      • 可以使用KNN算法对特征影响进行说明!!
    • 归一化的实现
    • 特点:通过对原始数据进行变换把数据映射到(默认为[0,1])之间
    • 公式:X’= X − m i n m a x − m i n \frac{X-min}{max-min} maxminXmin
    •   X " = X ′ ∗ ( m x − m i ) + m i \ X"=X' * (mx-mi) + mi  X"=X(mxmi)+mi
    • 注:作用于每一列,max为一列的最大值,min为一列的最小值,那么X"为最终结果,mx,mi分别为指定区间值默认mx为1,mi为0
  • 归一化后的数据服从正态分布
  • API:from sklearn.preprocessing import MinMaxScaler
    • 参数:feature_range表示缩放范围,通常使用(0,1)
  • 作用:使得某一个特征对最终结果不会造成很大影响

In[22]:

from sklearn.preprocessing import MinMaxScaler

mm = MinMaxScaler(feature_range=(0, 1))  # 每个特征缩放的范围
data = [[90, 2, 10, 40], [60, 5, 15, 45], [73, 3, 13, 45]]  # 三行四列
data = mm.fit_transform(data)  # x需要归一化的特征
print(data)
# 通过一列进行归一化处理 90,60,73 max=90 min=60 
# 输出结果:
[[1.         0.         0.         0.        ]
 [0.         1.         1.         1.        ]
 [0.43333333 0.33333333 0.6        1.        ]]
  • 问题:如果数据中存在的异常值较多,会对结果造成什么样的影响?
    • 结合着归一化计算的公式可知,异常值对原始数据的最大值和最小值的影响很大,因此也会影响对归一化之后的值。这个也是归一化的一个弊端,无法很好的处理异常值。
  • 归一化总结:在特定场景下最大值和最小值是变化的,另外最大最小值很容易受到异常值的影响,所以这种归一化的方式具有一定的局限性。因此,引出了一种更好的方法叫做:“标准化”。
  • 标准化的处理
    • 当数据均值中心化后,再按标准差缩放,数据就会服从均值为0,方差为1的正态分布(即标准正态分布),而这个过程,就叫做数据标准化(standardization,又称Z-score normalization)公式如下:
    • 公式:X’= x − m e a n σ \frac{x-mean}{σ} σxmean
    • 注:作用于每一列,mean为平均值,σ为标准差 var成为方差,var= ( x 1 − m e a n ) 2 + ( x 2 − m e a n ) 2 + … … n ( 每 个 特 征 的 样 本 数 ) \frac{(x1-mean)^2+(x2-mean)^2+……}{n(每个特征的样本数)} n(x1mean)2+(x2mean)2+ σ = v a r σ=\sqrt{var} σ=var 其中:方差(考量数据的稳定性)
      • 从公式中可以看出,异常值对方差和标准差的影响不大
    • 归一化和标准化总结
      • 对于归一化来说,如果出现了异常值则会影响特征的最大最小值,那么最终结果会受到较大影响
      • 对于标准化来说,如果出现异常点,由于具有一定的数据量,少量 的异常点对于平均值的影响并不大,从而标准差改变比较少
    • StandardScaler和MinMaxScaler选择哪个:看情况,大多数机器学习算法中,会选择StandardScaler来进行特征缩放,因为MinMaxScaler对异常值非常敏感。在PCA,聚类,逻辑回归,支持向量机,神经网络这些算法中,StandardScaler往往是最好的选择。MinMaxScaler在不涉及距离度量,梯度,协方差计算以及数据需要被压缩到特定区间时使用广泛,比如数字图像处理中量化像素强度时,都会使用MinMaxScaler将数据压缩与[0,1]区间之中
    • 建议先看看StandardScaler,如果效果不好换MinMaxScaler
    • API
      • 处理后,每列所有的数据都聚集在均值为0,标准差为1范围附近
      • 标准化API:from sklearn.preprocessing import StandardScaler
      • fit_transform(X):对X进行标准化
      • mean_:均值
      • var_:方差

In[23]:

from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
data = [[90, 2, 10, 40], [60, 5, 15, 45], [73, 3, 13, 45]]
data = ss.fit_transform(data)
print(data)
  • 特征选择:从特征中选择出有意义的对模型有帮助的特征作为最终的机器学习输入的数据!
    • 切记:
      • 在做特征选择之前,有三件非常重要的事情:跟数据提供者联系,跟数据提供者沟通,跟数据提供者开会。
      • 一定要抓住给你提供数据的人,尤其是理解业务和数据含义的人,跟他们进行了解深入。技术能够让模型起飞,前提是你和业务人员一样了解数据。所以特征选择的第一步是根据我们的目标,用业务常识来选择特征。
    • 特征选择的原因:
      • 冗余:部分特征的相关度高,容易消耗计算机的性能
      • 噪点:部分特征对预测结果有偏执影响
    • 特征选择的实现:
      • 人为对不相关的特征进行主观舍弃
      • 当然了,在真正的数据应用领域,比如金融,医疗,电商,我们的数据特征特别多,很明显,那如果遇见极端情况,我们无法依赖对业务的理解来进行选择特征,该怎么办呢?
        • 在已有特征和对应预测结果的基础上,使用相关的工具过滤掉一些无用的或者权重较低的特征
          • 工具:
            • Filter(过滤式)
            • Embedded(嵌入式):决策树模型会选择出对其重要的特征
            • PCA降维
    • Filter过滤式(方差过滤):
      • 原理:这是通过特征本身的方差来筛选特征的类。比如一个特征本身的方差很小,就表示样本在这个特征上基本上没有差异,可能特征中的大多数值都一样,甚至整个特征的取值都相同,那这个特征对于样本区分没有什么作用。所以无论接下来的特征工程要做什么,都要优先消除方差为0或者方差极低的特征。
      • API:from sklearn.feature_selection import VarianceThreshold
      • VarianceThreshold(threshold=x)threshold方差的值,删除所有方差低于x的特征,默认值为0表示保留所有方差非0的特征
      • fit_transform(X):X为特征

In[10]:

from sklearn.feature_selection import VarianceThreshold

# threshold方差的值,删除所有方差低于x的特征,默认值为0表示保留所有方差非0的特征
v = VarianceThreshold(threshold=3)
v = v.fit_transform([[0, 2, 4, 3], [0, 3, 7, 3], [0, 9, 6, 3]])
print(v)
#输出结果:
[[2]
[3]
[9]]
  • 如果将方差为0或者方差极低的特征去除后,剩余特征还有很多且模型的效果没有显著提升则方差也可以帮我们将特征选择【一步到位】。留下一半的特征,那可以设定一个让特征总数减半的方差阈值,只要找到特征方差的中位数,再将这个中位数作为参数threshold的值输入就好了。
    • VarianceThreshold(np.median(X.var().values)).fit_transform(X)
      • X为样本数据中的特征列

In[28]:

import numpy as np
from sklearn.feature_selection import VarianceThreshold

feature = np.random.randint(0, 100, size=(5, 10))
# print(feature)
med = np.median(feature.var(axis=0)) # axis=0代表每一列 axis=1代表每一行
v = VarianceThreshold(threshold=med)
v = v.fit_transform(feature)
print(v)
  • 方差过滤对模型的影响

    • 我们这样做了以后,对模型效果会有怎么样的影响?在这里,使用KNN算法方差过滤前和方差过滤后运行的效果和运行的时间进行对比。KNN是K近邻算法中的分类算法,其原理非常简单,是利用每个样本到其他样本点的距离来判断每个样本点的相似度,然后对样本进行分类。KNN必须遍历每个样本和每个特征,因而特征越多,KNN计算越缓慢
    • 对于KNN,过滤后的效果十分明显,准确率稍有提升,平均运行时间减少了10分钟,特征选择后算法的效率提升了1/3
    • 注意:方差过滤主要服务的对象是:需要遍历特征的算法模型。而过滤法的主要目的是:在维持算法表现的前提下,帮助算法们降低成本。
  • PCA降维(主成分分析):是一种分析,简化数据集的技术

    • 降维的维度值就是特征的种类
    • 思想:如何最好的对一个立体的物体用二维表示
      在这里插入图片描述
  • 第四张图片可以比较好的标识一个立体三维的水壶。但是也要清楚,用一个低纬度去表示高纬度的物体时,一定会造成一些信息的差异。可以让低纬度也能正确表示高纬度的事务,或信息差异最小。

  • 目的:特征数量达到上百,上千的时候,考虑数据的优化。使数据维度压缩,尽可能降低源数据的维度(复杂度),损失少量信息

  • 作用:可以消减回归分析或者聚类分析中特征的数量

  • PCA大致原理
    在这里插入图片描述

  • 红色为原始的样本特征,为二维的特征,如果降为一维,则可以将5个红色的原始特征,映射一维的线段上就变成了4个特征。

  • PCA语法:

    • from sklearn.decomposition import PCA
    • pca = PCA(n_components=none)
      • n_components可以为小数(保留特征的百分比),整数(减少到的特征数量)
      • ** pca.fit_transform(X)**
from sklearn.decomposition import PCA

# 将数据分解为较低维度的空间
# n_components可以为小数(保留特征的百分比),整数(减少到的特征数量)
pca = PCA(n_components=3)
pca = pca.fit_transform([[0, 2, 4, 3], [0, 3, 7, 3], [0, 9, 6, 3]])
print(pca)

sklearn的数据集

  • 数据集划分
  • 数据集接口介绍
  • 数据集划分
    • 前提:机器学习就是从数据中分析自动分析获得规律,并利用规律对未知数据进行预测。换句话说,我们的模型一定是要经过样本数据对其进行训练,才可以对未知数据进行预测
    • 问题:我们得到数据后,是否将数据全部用来训练模型呢?
      • 不是,因为我们如果模型(数据的规律)都是从数据中得来的,那么模型的性能评估如何进行呢?还是基于对原先数据进行预测吗?当然不是,如果模型对原先数据进行预测,由于模型(数据的规律)本来就是从该数据中获取的,所以预测的精度几乎是百分之百。所以要评估模型的好坏,需要使用一组新数据对模型进行评估。
      • 因此我们需要把原来的样本数据拆分成两部分
        • 训练集:训练模型
        • 测试集:评估模型
          • 不同类型的模型对应的评估方式是不一样的
    • 数据集划分的API
      • from sklearn.model_selection import train_test_split
      • train_test_split(x,y,test_size,random_state)参数介绍:
        • x:特征
        • y:目标
        • test_size:测试集的比例
        • random_state:打乱的随机种子
      • 返回值:训练特征,测试特征,训练目标,测试目标
  • 数据集接口介绍
    • sklearn.datasets.load_*():获取小规模的数据集
    • sklearn.datasets.fetch_*(data_home=None,subset):获取大规模的数据集,data_home表示数据集下载目录,None表示为默认值表示的是主目录/scikit_learn_data(自动创建该文件夹)下。需要从网络下载,subset为需要下载的数据集,可以为train,test,all
      In[30]
import sklearn.datasets as datasets

# # 小规模数据集
# iris = datasets.load_iris()
# print(iris)
# # 样本数据抽取
# feature = iris['data']  # 特征数据
# target = iris['target']  # 标签数据
# print(feature.shape)
# print(target.shape)
# 大规模数据集
news = datasets.fetch_20newsgroups(data_home=None, subset='all')
print(news)
  • 鸢尾花数据集拆分
    In[31]
import sklearn.datasets as datasets

#
# 小规模数据集
iris = datasets.load_iris()
# print(iris)
# 样本数据抽取
feature = iris['data']  # 特征数据
target = iris['target']  # 标签数据
# # print(feature.shape)
# # print(target.shape)
from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.2, random_state=2021)
print(x_test.shape)

机器学习基础

  • 机器学习算法分类
  • 机器学习开发流程
  • 机器学习中的数据类型
    • 离散型数据
      • 离散变量则是通过计数方式获取的,即是要对统计的对象进行计数,增长量非固定的,如:一个地区的企业只有一家,而第二年开了十家;一个企业的职工只有10人,第二年一次招聘招来20人等
    • 连续型数据
      • 连续变量是一直叠加上去的,增长量可以划分为规定的单位,即:1,2,3······,例如:一个人的身高,他首先长到1.51,然后才能到1.52,1.53 ······
    • 注意:连续型是有规律的,离散型是无规律的
  • 机器学习算法分类
    • 分类和回归问题
      • 分类算法基于的是[目标数据]为[离散型]数据
      • 回归算法基于的是[目标数据]为[连续型]数据
      • 结论:在社会中产生的数据必然是离散型数据或者连续型数据,那么企业针对数据的需求无非是分类或者回归问题
    • 机器学习开发流程
      • 1.数据采集
        • 公司内部产生的数据
        • 与其他公式合作产生的数据
        • 购买的数据
      • 2.分析数据所对应要解决的问题或者需求,根据目标数据推断是回归还是分类问题
      • 3.数据的基本处理
        • 数据清洗
        • 合并
        • 级联等
      • 4.特征工程
        • 特征抽取
        • 特征预处理
        • 降维等
      • 5.选择合适的模型,对其进行训练
      • 6.模型的评估
      • 7.上线使用
  • 分类算法
    • KNN分类模型
      • 概念:简单的说,K-近邻算法采用测量不同的特征值之间距离方法进行分类(K-Nearest Neighbor,KNN)
        在这里插入图片描述 在这里插入图片描述
  • 欧几里得距离:欧式距离是常见的距离度量,衡量的多维空间中各个点的绝对距离。公式如下:
    • d i s t ( X , Y ) = ∑ i = 1 n ( x i − y i ) 2 dist(X,Y)=\sqrt{\sum_{i=1}^n(x_i-y_i)^2} dist(X,Y)=i=1n(xiyi)2
  • 案例:电影分类

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 在scitkit-learn库中使用KNN算法
  • 分类问题:
  • 鸢尾花分类实现
    In[25]:
import sklearn.datasets as datasets
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split

# 1.捕获鸢尾花数据
iris = datasets.load_iris()
# 2.提取样本数据
feature = iris['data']  # 特征数据
target = iris['target']  # 标签数据
# 3.数据集进行拆分
x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.2, random_state=2021)
# 4.观察数据集:看是否需要特征工程处理的需要
# print(x_train)
# 5.实例化模型对象
knn = KNeighborsClassifier(n_neighbors=2)  # n_neighbors == k
# 在KNN中k的取值不同会直接导致分类结果的不同。n_neighbors参数表示k值。k值可以叫做模型的超参数
# 模型的超参数:如果模型参数有不同的取值且不同的取值会对模型的分类或者预测产生直接的影响

# 6.使用训练集训练模型
# X:训练集的特征数据,特征数据必须是二维的
# y:训练集的标签数据
knn = knn.fit(x_train, y_train)
print(x_train.shape)
# print(knn)
# 7.测试模型:使用测试数据
# predict表示使用训练好的模型实现分类或者预测
y_pred = knn.predict(x_test)  # 模型基于测试数据返回的分类结果
y_true = y_test  # 测试集真实的分类结果
print("模型的分类结果:", y_pred)
print('真实的分类结果:', y_true)
score = knn.score(x_test, y_test)
print(score)
print(x_test[0])
n = knn.predict([[4.5, 3.3, 1.4, 0.3]])  # 未知数据进行了分类
print(n)
  • 预测年收入是否大于500k美元
    In[36]:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split

# 加载数据
df = pd.read_csv('adult.data')
# print(df)
# 样本数据的提取
target = df['salary']
feature = df[['age', 'education_num', 'occupation', 'hours_peer_week']]
# print(feature.shape)
# print(target.shape)
# 数据集拆分
x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.1, random_state=2021)
# 观察特征数据看是否需要进行特征工程
# print(x_train)
occ_one_hot = pd.get_dummies(x_train['occupation'])
# print(occ_one_hot)
x_train = pd.concat((x_train, occ_one_hot), axis=1).drop(labels='occupation', axis=1)
# print(x_train)
knn = KNeighborsClassifier(n_neighbors=49).fit(x_train, y_train)
# 对测试集的特征进行one-hot编码
occ_one_hot_test = pd.get_dummies(x_test['occupation'])
x_test = pd.concat((x_test, occ_one_hot_test), axis=1).drop(labels='occupation', axis=1)
print(knn.score(x_test, y_test))
scores = []
ks = []
for i in range(5, 50):
    knn = KNeighborsClassifier(n_neighbors=i)
    knn.fit(x_train, y_train)
    score = knn.score(x_test, y_test)
    scores.append(score)
    ks.append(i)
score_arr = np.array(scores)
ks_arr = np.array(ks)
# %matplotlib inline

plt.plot(ks_arr, score_arr)
plt.xlabel('k_value')
plt.ylabel('score')
plt.show()
print(score_arr.max())
# 最大值的下标
print(score_arr.argmax())
# 最优k值
print(ks_arr[score_arr.argmax()])

输出图像
在这里插入图片描述

  • 学习曲线寻找最优的k值
  • 约会网站配对效果判定
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split

df = pd.read_csv('datingTestSet2.txt', header=None, sep='\s+')
# print(df)
feature = df[[0, 1, 2]]
target = df[3]
# print(feature)
# print(target)
x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.2, random_state=2021)
# print(x_train)  # 观察特征数据查看是否需要进行特征工程
# 对训练集特征数据进行预处理
mm = MinMaxScaler()
m_x_train = mm.fit_transform(x_train)  # 训练集的特征数据进行归一化操作
# 对测试集的数据进行归一化处理
m_x_test = mm.transform(x_test)
# 绘制学习曲线寻最优k值
scores = []
ks = []
for i in range(3, 50):
    knn = KNeighborsClassifier(n_neighbors=i)
    knn.fit(m_x_train, y_train)
    score = knn.score(m_x_test, y_test)
    scores.append(score)
    ks.append(i)
score_arr = np.array(scores)
ks_arr = np.array(ks)
plt.plot(ks_arr, score_arr)
plt.xlabel('k_value')
plt.ylabel('score')
plt.show()
# 最大值
print(score_arr.max())
# 最大值的下标
print(score_arr.argmax())
# 最优k值
print(ks_arr[score_arr.argmax()])
knn = KNeighborsClassifier(n_neighbors=7).fit(m_x_train, y_train)
print(knn.score(m_x_test, y_test))
print(knn.predict([[2345, 20, 1.1]]))

  • 问题:约会数据中发现目标数据为非数值型数据,可行吗?
    • 可行,因为在KNN算法原理中,仅仅是计算特征值之间距离,目标数据没有参与运算

K折交叉验证

  • 目的:选出最为合适的模型超参数的取值,然后将超参数的值作用到模型的创建中
  • 思想:将样本的训练数据交叉的拆分出不同的训练集和验证集,使用交叉拆分出不同的训练集和验证集,分别测试模型的精准度,然后求出模型精准度的均值就是此次交叉验证的结果。将交叉验证作用到不同的超参数中,选出精准度最高的超参数作为模型创建的超参数即可
  • 实现思路:
    • 将数据集平均划分成k个等份
    • 使用1份数据作为测试数据,其余作为训练数据
    • 计算测试准确率
    • 使用不同的测试集,重复2,3步骤
    • 对准确率做平均,作为对未知数据预测准确率的估计
    • API
      • from sklearn.model_selection import cross_val_score
      • cross_val_score(estimator,X,y,cv)
        • estimator:模型对象
        • X,y:训练集数据
        • cv:折数
    • 交叉验证在knn中的使用
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
import sklearn.datasets as datasets

knn = KNeighborsClassifier(n_neighbors=12)
iris = datasets.load_iris()
feature = iris['data']
target = iris['target']
x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.2, random_state=2021)
cross_val_score(knn, x_train, y_train, cv=5).mean()

# 使用交叉验证和学习曲线寻找最优超参数

scores = []
ks = []
for k in range(3, 20):
     knn = KNeighborsClassifier(n_neighbors=k)
     score = cross_val_score(knn, x_train, y_train, cv=6).mean()
     scores.append(score)
     ks.append(k)
plt.plot(ks, scores)
plt.xlabel('k_value')
plt.ylabel('score')
plt.show()
ks_arr = np.array(ks)
score_arr = np.array(scores)
print(score_arr.max())
print(score_arr.argmax())
print(ks_arr[score_arr.argmax()])


  • 交叉验证也可以帮我们进行模型选择,以下是一组例子,分别使用KNN,logistic回归模型进行模型的选择和比较
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
import sklearn.datasets as datasets

iris = datasets.load_iris()
feature = iris['data']
target = iris['target']
x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.2, random_state=2021)
knn = KNeighborsClassifier(n_neighbors=12)
print(cross_val_score(knn, x_train, y_train, cv=5).mean())
lr = LogisticRegression()
print(cross_val_score(lr, x_train, y_train, cv=5).mean())

  • K-Fold&cross_val_score
    • Scikit提供了K-Fold的API
      • n-split就是折数
      • shuffle就是对数据是否进行洗牌
      • random_state就是随机种子,固定随机性
from numpy import array
from sklearn.model_selection import KFold

data = array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6])
kFold = KFold(n_splits=3, shuffle=True, random_state=1)
for train, test in kFold.split(data):
    print('train:%s,test:%s' % (data[train], data[test]))
  • Scitkit中提取K-Fold接口的交叉验证接口sklearn.model_selection.cross_validate,但是该接口没有shuffle功能,所以结合K-Fold一起使用。如果train数据在分组前已经经过shuffle处理,比如使用train_test_split分组,那么可以直接使用cross_val_score接口
from sklearn.neighbors import KNeighborsClassifier
import sklearn.datasets as datasets
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score

iris = datasets.load_iris()
X, y = iris.data, iris.target
knn = KNeighborsClassifier(n_neighbors=12)
n_folds = 5
kFold = KFold(n_folds, shuffle=True, random_state=42).get_n_splits(X)
score = cross_val_score(knn, X, y, cv=kFold)
print(score.mean())

线性回归+回归算法的评价指标

  • 回归问题的判定
    • 目标值是连续型的值,而分类问题的目标值是离散型的值
  • 回归处理的问题为预测
    • 预测房价
    • 销售额的预测
    • 设定贷款额度
    • 总结:上述案例,可以根据事物的相关特征预测出对应的结果值
  • 线性回归在生活中的映射:生活案例[预测学生的期末成绩]:
    • 期末成绩的制定:0.7考试成绩+0.3平时成绩,特征值为考试成绩和平时成绩,目标值为总成绩。从此案例中可以感受到
      • 回归算法预测出来的结果就是经过相关算法计算出来的结果值
      • 每个特征需要一个权重的占比,这个权重的占比确定后,则可以得到最终的计算结果,也就是获取了最终预测结果
import numpy as np
import pandas
from pandas import DataFrame
import matplotlib.pylab as plt
from pylab import mpl

dic = {
    '面积': [55, 76, 80, 100, 120, 150],
    '售价': [110, 152, 160, 200, 240, 300]
}
df = DataFrame(data=dic)
print(df)
mpl.rcParams['font.sans-serif'] = ['FangSong']  # 指定默认字体
mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号‘-’显示为方块的问题
plt.scatter(df['面积'], df['售价'])
plt.xlabel('面积')
plt.ylabel('售价')
plt.title('面积和价钱的分布图')
plt.scatter(np.linspace(0, 180, num=100), np.linspace(0, 180, num=100)*2, alpha=0.3)
plt.show()

  • 思考:上述的线性方程y=wx+b其中x为特征y为目标,这种方程作为线性关系模型的预测数据的话是否可以满足所有的预测场景呢?

    • 如果现在房价受影响的不光是面积了,加入了采光率和楼层,那意味着特征变成了三种。在原始的线性方程y=wx+b中只可以有一个特征,则该方程不具有通用性。
    • 标准线性关系模型为:
      • 售价=(w1面积+w2采光率+w3楼层)+b==》 y=(w1x1+w2x2+···+wn*xn)+b
        • w又叫做权重。
        • b可以变换成w0*x0,x0=1
          • y=w0x0+w1x1+w2x2+···+wnxn
        • 权重向量(行向量):w0,w1,w2···wn
          • 行向量的转置就是列向量。行向量是一个n*1的矩阵,即矩阵由一个含n个元素的行所组成
  • 线性回归:

    • 找出特征和特征权重之间的一种组合,从而来预测对应的结果!!!
      • 线性方程式:
        • y=w1x1+w2x2+···+wn*xn+b
        • 为了方便后续写出矩阵的形式,我们这边可以稍作修改,令w0=b,x0=1,就可以写出下边的形式:
        • y=w0x0+w1x1+w2x2+···+wnxn
        • 假设现在有m个样本,写出矩阵的形式就是:
        • X = [ 1 x 1 1 x 1 2 ⋅ ⋅ ⋅ x 1 n 1 x 2 1 x 2 2 ⋅ ⋅ ⋅ x 2 n ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1 x m 1 x m 2 ⋅ ⋅ ⋅ x m n ] X=\left[ \begin{array}{lcr} 1& x_1^1 & x_1^2&···&x_1^n \\ 1& x_2^1 & x_2^2&···&x_2^n \\···& ··· & ···&···&···\\1& x_m^1 & x_m^2&···&x_m^n\end{array} \right] X=111x11x21xm1x12x22xm2x1nx2nxmn Y = [ y 1 y 2 y 3 ⋅ ⋅ ⋅ y m ] Y=\left[ \begin{array}{lcr} y_1\\y_2\\y_3\\···\\y_m \end{array} \right] Y=y1y2y3ym
        • 权重w也可以写成矩阵的形式:
          • W = [ w 0 w 1 w 2 ⋅ ⋅ ⋅ w n ] W=\left[ \begin{array}{lcr} w_0&w_1&w_2&···&w_n\end{array} \right] W=[w0w1w2wn]
          • 那么可以写成一种简单明了的方式:
            • Y = X W T Y=XW^T Y=XWT
            • PS:n代表特征数目,m代表样本数目
  • 问题:真实结果和预测结果是否存在误差?

    • 如果房价预测案例中,特征与目标之间的分布规律不是线性的,那么还可以使用一条直线表示特征与目标之间的趋势呢?
      • 可以,只要保证直线距离所有的散点距离最近,则该直线还是可以在一定程度上表示非线性分布散点之间的分布规律,但是该规律存在误差
    • 误差存在,那么我们应该如何处理误差呢?在处理误差之前,我们必须先要知道一个回归算法的特性
      • 回归算法是一个迭代算法。
        • 当开始训练线性回归模型的时候,是逐步将样本数据带入模型对其进行训练的
        • 训练开始前先用部分样本数据训练模型生成一组w和b,对应的直线和数据对应散点的误差较大,通过不断的带入样本数据会逐步迭代更好的w和b从而使w和b的值更加的精准
        • 如何不断迭代的减少误差呢?
          • 通过损失函数来表示误差
            • 总损失定义:
              • J ( θ ) = ( h w ( x 1 ) − y 1 ) 2 + ( h w ( x 2 ) − y 2 ) 2 + ⋅ ⋅ ⋅ + ( h w ( x m ) − y m ) 2 = ∑ i = 1 m ( h w ( x i ) − y i ) 2 J(θ)=(h_w(x_1)-y_1)^2+(h_w(x_2)-y_2)^2+···+(h_w(x_m)-y_m)^2=\sum_{i=1}^m(h_w(x_i)-y_i)^2 J(θ)=(hw(x1)y1)2+(hw(x2)y2)2++(hw(xm)ym)2=i=1m(hw(xi)yi)2
              • y i y_i yi:第i个训练样本的真实值
              • h w ( x i ) h_w(x_i) hw(xi):预测值
          • 损失函数也可以表示为:
            • ∑ i = 1 m ( y i − y i ^ ) 2 = ∑ i = 1 m ( y i − x i w T ) 2 \sum\limits_{i=1}^m(y_i-\hat{y_i})^2=\sum\limits_{i=1}^m(y_i-x_iw^T)^2 i=1m(yiyi^)2=i=1m(yixiwT)2
        • 因此得知误差的大小线性回归方程中的系数w是有直接的关系
          • w(权重)的不同会导致误差大小的不同
          • 那么最终的问题就转换成了,【如何去求解方程中的w使得误差最小】
  • L2范式

    • 这个损失函数代表了向量yi-y^i的L2范式的平方结果,L2范式的本质就是欧氏距离,即是两个向量上的每个点对应相减后的平方和再开平方,我们现在只实现向量上每个点对应相减后的平方和,并没有开方,所以我们的损失函数是L2范式,即欧氏距离的平方结果。
    • d i s t = ∑ i = 1 n ( x i − y i ) 2 dist=\sqrt{\sum\limits_{i=1}^n}(x_i-y_i)^2 dist=i=1n (xiyi)2 = m i n w ∣ ∣ y − x w T ∣ ∣ 2 min_w||y-xw^T||_2 minwyxwT2
    • 在这个平方结果下,我们的y和y^分别是我们的真实值和预测值,也就是说,这个损失函数实际计算我们的真实标签和预测值之间的距离。因此,我们认为这个损失函数衡量了我们构造的模型的预测结果和真实标签的差异,因此我们固然希望我们的预测结果和真实值差异越小越好,所以我们的求解目标就可以转换为:
    • m i n w ∣ ∣ y − x w T ∣ ∣ 2 2 min_w||y-xw^T||_2 {^2} minwyxwT22
    • SSE&RSS:
      • 其中右下角的2表示向量y-xw的L2范式,也就是我们损失函数所代表的意义。在L2范式上开平方就是我们的损失函数。我们往往称呼这个式子为SSE(Sun of Sqaured Error,误差平方和)或者RSS(Residual Sum of Squares,残差平方和)
    • 最小二乘法
      • 现在问题转换成了求解让RSS的最小化参数向量w,这种通过最小化真实值和预测值之间的RSS来求解参数的方法叫做最小二乘法
      • 求解极值(最小值)的第一步求导一阶导数并让一阶导数等于0
      • 首先w表示的是一个列向量(矩阵),我们现在对列向量求导
      • 首先将L2范式拆开:
      • ∂ R S S ∂ w = ∂ ∣ ∣ y − x w ∣ ∣ 2 2 ∂ w = ∂ ( y − x w ) T ( y − x w ) ∂ w \frac{\partial RSS}{\partial w}=\frac{\partial||y-xw||_2 {^2}}{\partial w}=\frac{\partial(y-xw)^T(y-xw)}{\partial w} wRSS=wyxw22=w(yxw)T(yxw)
      • 两个向量(y&xw)的平方就等于两个向量的转置乘以两个向量本身
      • 处理转置乘法和除法:
      • ( A − B ) T = A T − B T 并 且 ( A B ) T = B T ∗ A T (A-B)^T=A^T-B^T并且(AB)^T=B^T*A^T (AB)T=ATBT(AB)T=BTAT
      • = ∂ ( y T − w T x T ) ( y − x w ) ∂ w =\frac{\partial(y^T-w^Tx^T)(y-xw)}{\partial w} =w(yTwTxT)(yxw)
      • 然后将上面的分子进行多项式相乘
      • = ∂ ( y T y − w T x T y − y T x w + w T x T x w ) ∂ w = ∂ y T y − ∂ w T x T y − ∂ y T x w + ∂ w T x T x w ∂ w =\frac{\partial (y^Ty-w^Tx^Ty-y^Txw+w^Tx^Txw)}{\partial w}= \frac{\partial y^Ty-\partial w^Tx^Ty-\partial y^Txw+\partial w^Tx^Txw}{\partial w} =w(yTywTxTyyTxw+wTxTxw)=wyTywTxTyyTxw+wTxTxw
      • 在矩阵求导中如果小a为常数项,A为矩阵则:
      • ∂ a ∂ A = 0 , ∂ A T B T C ∂ A = B T C , ∂ C T B A ∂ A = B T C , ∂ A T B A ∂ A = ( B + B T ) A \frac{\partial a}{\partial A}=0 , \frac{\partial A^TB^TC}{\partial A}=B^TC,\frac{\partial C^TBA}{\partial A}=B^TC,\frac{\partial A^TBA}{\partial A}=(B+B^T)A Aa=0,AATBTC=BTC,ACTBA=BTC,AATBA=(B+BT)A
      • 分子上的每一项对w进行求导的结果是:
      • = 0 − x T y − x T y + 2 x T x w = x T x w − x T y =0-x^Ty-x^Ty+2x^Txw=x^Txw-x^Ty =0xTyxTy+2xTxw=xTxwxTy
      • 至此我们就求解出对w求导的一阶导数,接下来让一阶导数,接下来让一阶导数为0则就求出了最小误差下的w的值了。
      • x T x w − x T y = 0 x^Txw-x^Ty=0 xTxwxTy=0
      • x T x w = x T y x^Txw=x^Ty xTxw=xTy
      • 左乘一个 ( x T x ) − 1 (x^Tx)^{-1} (xTx)1则有:
      • w = ( x T x ) − 1 x T y w=(x^Tx)^{-1}x^Ty w=(xTx)1xTy
      • A − 1 ∗ A = 1 A^{-1}*A=1 A1A=1
    • API
      • 最小二乘(正规方程):from sklearn.linear_model import LinearRegression
      • fit_intercept:布尔值,可不填,默认为True,是否计算模型的截距。如果设置为False,则不会计算截距。
      • normalize:布尔值,可不填,默认为False,当fit_intercept为False,此参数可忽略。如果为True,则特征矩阵X进入回归之前将会被减去均值(中心化)并除以L2范式(缩放)。如果你希望进行标准化,请在fit数据之前使用preprocessing模块中的标准化专用类StandardScaler
      • copy_X:布尔值,可不填,默认为True。如果为真,将在X.copy()上进行操作否则的话,原本的特征矩阵X可能被线性回归影响并覆盖
      • n_jobs:整数或者None,可不填,默认为None。用于计算的作业数。只在多标签的回归和数据量足够大的时候才生效。除非None在joblib.parallel_backend上下文,否则None统一表示为1。如果输入-1,则表示使用全部的cpu来进行计算
      • 这些参数并没有一个是必填的,更没有对我们的模型有不可替代的参数,这说明,线性回归的性能,往往取决于数据本身,而并非是我们的调参能力,线性回归也因此对数据有着很高的要求。幸运的是,大部分连续型变量之间,存在着或多或少的线性联系。所以线性回归固然简单,却很强大。sklearn的线性回归可以处理多标签问题,只需在fit的时候输入多维度标签就可以了。
      • normalize参数:如果为True,则会对特征数据进行归一化处理,如果想对特征数据进行标准化处理,则需要训练模型前调用相关工具类对其进行标准化处理。
      • 使用最小二乘法对房屋进行预测
      • 特征介绍:
        • AveBedrms:该街区平均的卧室数目
        • Population:街区人口
        • AveOccup:平均入住率
        • Latitude:街区的纬度
        • Longitude:街区的经度
        • MedInc:街区住户收入的中位数
        • HouseAge:房屋使用年数中位数
        • AveRooms:街区平均房屋的数量
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.datasets import fetch_california_housing as fch

# print(fch())
feature = fch().data
target = fch().target
x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.1, random_state=2021)
liner = LinearRegression()
liner.fit(x_train, y_train)
liner.coef_  # 返回的是w系数
# print(liner.coef_)
liner.intercept_  # 返回的是截距
# print(liner.intercept_)
# 将系数和特征名称结合在一起查看
[*zip(fch().feature_names, liner.coef_)]

回归模型的评价指标

  • 回归类算法的模型评估一直都是回归算法中的一个难点,回归类与分类型算法的模型评估其实是相似的法则- -找真实标签和预测值的差异。只不过在分类型算法,这个差异只有一种角度来评判,那就是是否预测到了正确的分类,而在我们的回归类算法,我们有两种不同的角度来看待回归的效果:
    • 第一:我们是否预测到了正确或者接近正确的数值(因为误差的存在)
    • 第二:我们是否拟合到了足够的信息。(是否模型预测的结果线性和样本真实的结果的线性更加吻合)
    • 这两种角度,分别对应着不同的模型评估指标
  • 是否预测到了正确的数值
    • 回忆下我们的残差平方和RSS,它的本质是我们的预测值和真实值之间的差异,也就是从一种角度来评估我们回归的效力,所以RSS既是我们的损失函数,也是我们回归类模型的模型评估指标之一。但是,RSS有着致命的缺点: 它是一个无界的和,可以无限地大或者无限的小。我们只知道,我们想要求解最小的RSS,从RSS的公式来看,它不能为负,所以 RSS越接近0越好,但我们没有一个概念,究竟多小才算好,多接近0才算好?为了应对这种状况,sklearn中使用RSS 的变体,均方误差MSE(mean squared error)来衡量我们的预测值和真实值的差异:
    • M S E = 1 m ∑ i = 1 m ( y i − y i ^ ) 2 MSE=\frac{1}{m}\sum\limits_{i=1}^m (y_i-\hat{y_i})^2 MSE=m1i=1m(yiyi^)2
    • 均方误差,本质是在RSS的基础上除以了样本总量,得到了每个样本量上的平均误差。有了平均误差,我们就可以将平均误差和我们的标签的取值范围(最大值和最小值)在一起比较,以此获得一个较为可靠的评估依据。(查看这个错误有多严重)
      • 因为标签的最大值和最小值可以表示标签的一个分部情况,那么将其最大值和最小值和平均误差比较就可以大概看出在每个样本上的误差或者错误有多严重。
      • 在sklearn当中,我们有两种方式调用这个评估指标:
        • 一种是使用sklearn专用的模型评估模块metrics里的类mean_squared_error
        • 另一种是调用交叉验证的类cross_val_score并使用里面的scoring参数来设置为:neg_mean_squared_error使用均方误差。
        • 交叉验证求得的均方误差为负值
          • 均方误差的计算公式中求得的均方误差的值不可能为负。但是sklearn中的参数scoring下,均方误差作为评判 标准时,却是计算负均方误差(neg_mean_squared_error)。这是因为sklearn在计算模型评估指标的时候,会考虑指标本身的性质,均方误差本身是一种误差,所以被sklearn划分为模型的一种损失(loss)。在sklearn当中,所有的损失都使用负数表示,因此均方误差也被显示为负数了。真正的均方误差MSE的数值,其实就是neg_mean_squared_error去掉负号的数字。
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.datasets import fetch_california_housing as fch

# print(fch())
feature = fch().data
target = fch().target
x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.1, random_state=2021)
liner = LinearRegression()
liner.fit(x_train, y_train)
liner.coef_  # 返回的是w系数
# print(liner.coef_)
liner.intercept_  # 返回的是截距
# print(liner.intercept_)
# 将系数和特征名称结合在一起查看
[*zip(fch().feature_names, liner.coef_)]
# print(liner.score(x_test, y_test))
from sklearn.metrics import mean_squared_error

y_true = y_test
y_pred = liner.predict(x_test)
mean_squared_error(y_true, y_pred)
# print(y_true.max())
# print(y_true.min())

from sklearn.model_selection import cross_val_score

print(cross_val_score(liner, x_train, y_train, cv=5, scoring='neg_mean_squared_error').mean())
  • 绝对误差
    • 除了MSE,我们还有与MSE类似的MAE(Mean absolute error,绝对均值误差):
    • M A E = 1 m ∑ i = 0 m − 1 ∣ y i − y i ^ ∣ MAE=\frac{1}{m}\sum\limits_{i=0}^{m-1}|y_i-\hat{y_i}| MAE=m1i=0m1yiyi^
    • 其表达的概念与均方误差完全一致,不过在真实标签和预测值之间的差异我们使用的是L1范式(绝对值)。现实使用中,MSE和MAE选一个使用就可以了
    • 在sklearn当中,我们使用命令
      • from sklearn.metrics import mean_absolute_error
      • 同时,我们也可以用交叉验证中的scoring="neg_mean_absolute_error"
  • 是否拟合了足够的信息
    • 对于回归类算法,只探索数据预测是否准确是不足够的。除了数据本身的数值大小之外,我们还希望我们的模型能够捕捉到数据的”规律“,比如数据的分布规律(抛物线),单调性等等。而是否捕获到这些信息是无法使用MSE来衡量的。
      在这里插入图片描述
  • 看这张图,其中红色是我们的真实标签,而蓝色线是我们模型预测的值。这是一种比较极端的情况,但极有可能发生。这张图像上前半部分的拟合非常成功,看上去我们的真实标签和我们的预测结果几乎重合,但后半部分的拟合非常糟糕,模型向着与真实标签完全相反的方向去了。对于这样的一个拟合模型,如果我们使用MSE来对它进行判断,它的MSE会很小,因为大部分样本其实都被完美拟合了,少数样本的真实值与预测值的巨大差异在被均分到每个样本上之后,MSE就会很小。但这样的拟合结果不是一个好的结果,因为一旦我的新样本是处于拟合曲线的后半段的,我的预测结果必然会有巨大的偏差。所以,我们希望找到新的指标,除了判断预测的数值是否正确之外,还能够判断我们的模型是否拟合了足够多的,数值之外的信息。
  • 在我们学习降维选择,我们提到我们使用方差来衡量数据上的信息量。如果方差越大,代表数据上的信息量越多,而这个信息量(数据潜在的规律)不仅包括了数值的大小,还包括了我们希望模型捕捉的那些规律。为了衡量模型对数据上的信息量的捕捉,我们定义了 R 2 R^2 R2来帮助我们:
    R 2 = 1 − ∑ i = 0 m ( y i − y i ^ ) 2 ∑ i = 0 m ( y i − y ˉ ) 2 = 1 − R S S ∑ i = 0 m ( y i − y ˉ ) 2 R^2=1-\frac{\sum_{i=0}^m(y_i-\hat{y_i})^2}{\sum_{i=0}^m(y_i-\bar{y})^2}=1-\frac{RSS}{\sum_{i=0}^m(y_i-\bar{y})^2} R2=1i=0m(yiyˉ)2i=0m(yiyi^)2=1i=0m(yiyˉ)2RSS
  • 其中,y是我们的真实标签, y ^ \hat{y} y^是我们的预测结果, y ˉ \bar{y} yˉ是我们的均值, y i − y ˉ y_i-\bar{y} yiyˉ如果除以样本量m就是我们的方差。方差的本质是任意一个y值和样本均值的差异,差异越大,这些值所带的信息越多。在 R 2 R^2 R2中,分子是真实值和预测值之间的差值,也就是我们的模型没有捕获到的信息总量,分母是真实标签所带的信息量,所以其衡量的是1-我们的模型没有捕获到的信息量占真实标签中所带的信息量的比例,所以, R 2 R^2 R2越接近1越好。
  • 分母其实可以表示称为样本的潜在规律,分子为模型的误差(损失),那么样本数据潜在的规律是不变的,则误差越小则分子分母表达式返回的结果越小,则 R 2 越 接 近 1 R^2越接近1 R21
  • 可以用三方式来调用
    • 一种是直接从metrics中导入r2_score,输入预测值和真实值打分
    • 第二种是直接从线性回归LinerRegression的接口score来调用
    • 第三种是在交叉验证中,输入“r2”来调用
from sklearn.metrics import r2_score
from sklearn.model_selection import cross_val_score

print(r2_score(y_test, liner.predict(x_test)))
print(liner.score(x_test, y_test))
print(cross_val_score(liner, x_train, y_train, cv=5, scoring='r2').mean())
  • 绘制拟合图
import matplotlib.pyplot as plt

plt.plot(range(len(y_test)), sorted(y_test), c='black', label='y_true')
plt.plot(range(len(y_pred)), sorted(y_pred), c='red', label='y_pred')
plt.legend()
plt.show()


在这里插入图片描述

  • 可见,虽然我们的大部分数据被拟合得比较好,但是图像的开头和结尾处却又着较大的拟合误差。如果我们在图像右侧分布着更多的数据,我们的模型就会越来越偏离我们真正的标签。这种结果类似于我们前面提到的,虽然在有限的数据集上将数值预测正确了,但却没有正确拟合数据的分布,如果有更多的数据进入我们的模型,那数据标签被预测错误的可能性是非常大的。
  • 实战
    • 房地产估价数据集
      • 数据集信息
        • 房地产估值的市场历史数据集来自台湾新北市新店区。“房地产估价” 是一个回归问题。
      • 属性信息
      • 输入
        • X1 =交易日期(例如,2013.250 = 2013年3月,2013.500 = 2013年6月,等等)
        • X2 =房屋年龄(单位:年)
        • X3 =到最近的捷运站的距离(单位:米) )
        • X4 =步行生活圈中的便利店的数量(整数)
        • X5 =地理坐标,纬度。(单位:度)
        • X6 =地理坐标,经度。(单位:度)
      • 输出结果
        • Y =单位面积的房价(10000新台币/ Ping,其中Ping是本地单位,1 Ping = 3.3米平方)
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error as MSE
from sklearn.metrics import r2_score
from sklearn.linear_model import LinearRegression
import sklearn.datasets as datasets

df = pd.read_excel('Real estate valuation data set.xlsx')
df.drop(labels='No', axis=1, inplace=True)
# print(df.head())
# print(df)
feature = df.loc[:, df.columns != 'Y house price of unit area']
target = df['Y house price of unit area']
x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.2, random_state=2021)
linner = LinearRegression().fit(x_train, y_train)
# 模型在测试集的表现
y_true = y_test
y_pred = linner.predict(x_test)
print(MSE(y_true, y_pred))
print(y_true.max())
print(y_true.min())
print(r2_score(y_true, y_pred))
# 模型在训练集的表现
print(MSE(y_train, linner.predict(x_train)))
print(r2_score(y_train, linner.predict(x_train)))

  • 总结
    • 欠拟合:(对训练集的数据和测试集的数据拟合的都不是很好)
      原因:模型学习到样本的特征太少
      解决:增加样本的特征数量(多项式回归)
      多项式回归:from sklearn.preprocessing import PolynomialFeatures
      在原有特征的基础上增加高次方特征
    • 过拟合:(对训练集的数据高度拟合,对测试集的数据拟合的很离谱)
      原因:原始特征过多,存在一些嘈杂特征。
      解决:1.进行特征选择,消除关联性大的特征
      2.正则化之岭回归:from sklearn.linear_model import Ridge
      将对模型影响较大的特征(高次方)的权重系数变小,将模型弯曲程度大的地方捋直一定,降低关联性大的特征对预测模型的影响
  • 模型的保存和加载
    • 方式一 (推荐):from sklearn.externals import joblib
      joblib.dump(model,‘xxx.m’):保存
      joblib.load(‘xxx.m’):加载
    • 方式二:import pickle
      with open(’./123.pkl’,‘wb’) as fp:
      pickle.dump(linner,fp)
      with open(’./123.pkl’,‘rb’) as fp:
      linner = pickle.load(fp)
  • 欠拟合&&过拟合
    • 欠拟合:一个假设在训练数据上不能获得很好的拟合,但是在训练数据以外的数据集上也不能很好的拟合数据,此时认为这个假设出现了欠拟合的现象。(模型过于简单)
    • 过拟合:一个假设在训练数据上能够获得比其他假设更好的拟合,但是在训练数据以外的数据集上却不能很好的拟合数据,此时认为这个假设出现了过拟合现象。(模型过于复杂)
      在这里插入图片描述
  • 欠拟合和过拟合问题的解决
    • 欠拟合:
      • 原因:模型学习到样本的特征太少
      • 解决:增加样本的特征数量(多项式回归)
    • 过拟合:
      • 原因:原始特征过多,存在一些嘈杂特征。
      • 解决:
        • 进行特征选择,消除关联大的特征(很难做)
        • 正则化之岭回归
    • 模型的复杂度:回归出直线or曲线
      • 我们的回归模型最终回归出的一定是直线吗(y=wx+b)?有没有可能是曲线(非线性)呢(y=wx^2+b)?
        • 我们都知道回归模型算法就是在寻找特征值和目标值之间存在的某种关系,那么这种关系越复杂则表示训练出的模型的复杂度越高,反之越低。
        • 模型的复杂度是由特征和目标之间的关系导致的!特征和目标之间的关系不仅仅是线性关系!
    • 欠拟合的处理:多项式回归
      • 为了解决欠拟合的情况,经常需要提高线性的次数(高次多项式)建立模型拟合曲线,次数过多会导致过拟合,次数不够会欠拟合
        • y=w*x+b一次多项式函数
        • y=w1 * x^2+w2 * x+b 二次多项式函数
        • y=w1 * x^3 +w2 * x^2+w3 * x+b 三次多项式函数
        • ……
        • 高次多项式函数表示为曲线
    • 相对于线性回归模型y=wx+b只能解决线性(回归出的为直线)问题,多项式回归能够解决非线性回归(回归出的为曲线)问题。
    • 拿最简单的线性模型来说,其数学表达式可以表示为:y=wx+b,它表示的是一条直线,而多项式回归则可以表示成:y=w1 * x^2+w2 * x+b,它表示的是二次曲线,实际上,多项式回归可以看成特殊的线性模型,即把x∧2看成一个特征,把x看成另一个特征,这样就可以表示成y=w1z+w2x+b,其中z=x∧2,这样多项式回归实际上就变成线性回归了。
    • 当然还可以将y=wx+b转为更高次的多项式。是否需要转成更高次的多项式取决于我们想要拟合样本的程度了,更高次的多项式可以更好的拟合我们的样本数据,但是也不是一定的,很可能会造成过拟合。
    • 示例:根据蛋糕大小预测价格
from sklearn.linear_model import LinearRegression
import numpy as np
import matplotlib.pyplot as plt

# 样本训练数据,特征,目标
x_train = [[6], [8], [10], [16], [18]]  # 大小
y_train = [[7], [9], [15], [17.5], [18]]  # 价格
# 一次线性回归的学习和预测y=wx+b
re = LinearRegression()
re.fit(x_train, y_train)
# 画出一次线性回归的拟合曲线
xx = np.linspace(0, 25, 100)
xx = xx.reshape(-1, 1)
yy = re.predict(xx)
plt.scatter(x_train, y_train)  # 原始样本数据的分布规律
plt.plot(xx, yy)
plt.show()

  • 建立二次多项式线性回归模型进行预测
    • 根据二次多项式可知,需要给原始特征添加更高次特征数据x^2
    • 如何给样本添加高次的特征数据?
      • 使用sklearn.preprocessing import PolynomialFeatures来进行更高次特征的构造
        • 它是使用多项式的方法来构造的。如果有a,b两个特征,那么它的2次多项式为(1,a,b,a^2 ,ab,b^2)
        • PolynomialFeatures有三个参数:
          • degree:控制多项式的度
          • interaction_only:默认为False,如果指定为True,那么就不会有特征和特征自己结合的项,上面的二次项没有a^2, b^2
          • include_bias:默认为True。如果为False的话,那么就不会有上面的1那一项
from sklearn.linear_model import LinearRegression
import numpy as np
import matplotlib.pyplot as plt

# 样本训练数据,特征,目标
x_train = [[6], [8], [10], [16], [18]]  # 大小
y_train = [[7], [9], [15], [17.5], [18]]  # 价格
# 一次线性回归的学习和预测y=wx+b
re = LinearRegression()
re.fit(x_train, y_train)
# 画出一次线性回归的拟合曲线
xx = np.linspace(0, 25, 100)
xx = xx.reshape(-1, 1)
yy = re.predict(xx)
plt.scatter(x_train, y_train)  # 原始样本数据的分布规律
plt.plot(xx, yy)
plt.show()
from sklearn.preprocessing import PolynomialFeatures

# 参数degree控制增加特征的次数
# 参数interaction_only默认为False是有二次项,如果变成True就会去掉二次项
# 参数include_bias默认为True,如果是False就是不要增加出来的1那一项
c = [[5, 10]]
pl = PolynomialFeatures()
b = pl.fit_transform(c)
print(b)
# 建立二次多项式回归模型进行预测
poly2 = PolynomialFeatures(degree=2)  # 2次多项式特征生成器
x_train_poly2 = poly2.fit_transform(x_train)
# 建立模型预测
liner_poly2 = LinearRegression()
liner_poly2.fit(x_train_poly2, y_train)
# 画二次多项式回归的图
xx_poly2 = poly2.transform(xx)
yy_poly2 = liner_poly2.predict(xx_poly2)
plt.scatter(x_train, y_train)
plt.plot(xx, yy, label='Degree1')
plt.plot(xx, yy_poly2, label='Degree2')
plt.legend()
plt.show()
poly3 = PolynomialFeatures(degree=4)
x_train_poly3 = poly3.fit_transform(x_train)
liner_poly3 = LinearRegression()
liner_poly3.fit(x_train_poly3, y_train)
xx_poly3 = poly3.transform(xx)
yy_poly3 = liner_poly3.predict(xx_poly3)
plt.scatter(x_train, y_train)
plt.plot(xx, yy, label='Degree1')
plt.plot(xx, yy_poly3, label='Degree2')
plt.legend()
plt.show()
  • 过拟合处理:正则化
    • 将过拟合的曲线的凹凸幅度减少就可以将过拟合曲线趋近于拟合曲线了。那么过拟合曲线的凹凸肯定是由y=wx2+x3+x4中的高次项导致的**
    • 那么L2正则化就是通过将高次项的特征的权重w调小到趋近于0,则高次项的特征就几乎没有了,那么凹凸幅度就会减少,就越趋近于拟合曲线了!
    • LinnerRegression是没有办法进行正则化的,所以该算法模型容易出现过拟合,并且无法解决。
    • L2正则化:使用带有正则化算法的回归模型(Ridge岭回归)处理过拟合的问题。
  • Ridge岭回归:具备L2正则化的线性回归模型
    • API:from sklearn.linear_model import Ridge
    • Ridge(alpha=1.0):
      • alpha:正则化的力度,力度越大,则表示高次项的权重w越接近于0,导致过拟合曲线的凹凸幅度越小。
        取值:0-1小数或者1-10整数
      • coef_:回归系数
    • 使用岭回归可以通过控制正则化力度参数alpha降低高次项特征的权重
from sklearn.linear_model import Ridge
from sklearn.linear_model import LinearRegression
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures

# 样本的训练数据,特征和目标值
x_train = [[6], [8], [10], [14], [18]] #大小
y_train = [[7], [9], [13], [17.5], [18]]#价格

# 在原有特征上增加3次方特征
p = PolynomialFeatures(degree=3)
d_3_train = p.fit_transform(x_train)
linear = LinearRegression().fit(d_3_train,y_train)
# 原本线性回归权重系数
linear.coef_

array([[ 0.        , -1.42626096,  0.31320489, -0.01103344]])

# 使用L2正则化力度调整权重系数
# 参数alpha正则化力度,可以通过学习曲线寻找最优alpha值
ridge = Ridge(alpha=0.5)
ridge.fit(d_3_train,y_train)
ridge.coef_
# 调整后权重系数,明显将高权重的变低了
array([[ 0.        , -0.14579637,  0.19991159, -0.00792083]])
  • 岭回归的优点:
    • 获取的回归系数更符合实际更可靠
    • 在病态数据(异常值多的数据)偏多的研究中有更大的存在意义
  • 模型的保存和加载
    • 方式一 (推荐使用方式一,更加便捷)
      • from sklearn.externals import joblib
        joblib.dump(model,‘xxx.m’):保存
        joblib.load(‘xxx.m’):加载
    • 方式二
      • import pickle
        with open(’./123.pkl’,‘wb’) as fp:
        pickle.dump(linner,fp)
        with open(’./123.pkl’,‘rb’) as fp:
        linner = pickle.load(fp)

朴素贝叶斯算法

  • 在许多分类算法应用中,特征和标签之间的关系并非是决定性的。比如说,我们想预测一个人究竟是否会在泰坦尼克号海难中生存下来,那我们可以建立某个分类模型来学习我们的训练集。在训练中,其中一个人的特征为:30岁,男,普 通舱,他最后在泰坦尼克号海难中去世了。当我们测试的时候,我们发现有另一个人的特征也为:30岁,男,普通 舱。基于在训练集中的学习,我们的模型必然会给这个人打上标签:去世。然而这个人的真实情况一定是去世了吗?并非如此。也许这个人是心脏病患者,得到了上救生艇的优先权。又有可能,这个人就是挤上了救生艇,活了下来。对分类算法 来说,基于训练的经验,这个人“很有可能”是没有活下来,但算法永远也无法确定”这个人一定没有活下来“。即便这 个人最后真的没有活下来,算法也无法确定基于训练数据给出的判断,是否真的解释了这个人没有存活下来的真实情况。
  • 这就是说,算法得出的结论,永远不是100%确定的,更多的是判断出了一种“样本的标签更可能是某类的可能性”,而非一种“确定”。我们通过模型算法的某些规定,来强行让算法为我们返回一个固定的分类结果。但许多时候,我们也希望能够理解算法判断出结果的可能性概率。
  • 无论如何,我们都希望使用真正的概率来衡量可能性,因此就有了真正的概率算法:朴素贝叶斯。
  • 朴素贝叶斯是一种直接衡量标签和特征之间的概率关系的有监督学习算法,是一种专分类的算法。朴素贝叶斯的算法根源是基于概率论与数理统计的贝叶斯理论。
  • 概率计算准则:联合概率和条件概率
    • 联合概率:包含多个条件,且所有条件同时成立的概率
      • 记作P(A,B)
      • P(A,B)=P(A)P(B)
    • 条件概率:就是事件A在另外一个事件B已经发生条件下的概率
      • 记作P(A|B)
      • 特性:P(A1,A2|B)=P(A1|B)P(A2|B)
      • 注意:此条件概率的成立,是由于A1,A2相互独立的结果

在这里插入图片描述

  • -女神喜欢一个人的概率:4/7
    -职业是程序员并且体型匀称的概率:3/74/7=12/49
    -在女神喜欢的条件下,职业是程序员的概率:2/4=1/2
    -在女神喜欢的条件下,职业是产品,体重超重的概率:2/4
    1/4=1/8
  • 注意:
    • 上述的求概率公式只适用于各个特征之间是条件独立(每个特征之间没有必然关系)的。条件不独立指的是特征之间有关联的比如,体重和是否喜欢吃零食这两个条件之间就有关联。
    • 朴素贝叶斯只适用于特征之间是条件独立的情况下。否则分类效果不好。这里的朴素指的就是条件独立
    • 朴素贝叶斯主要被广泛的适用于文档分类中!
  • 朴素贝叶斯的分类
    • 在sk-learn中提供了三种不同类型的贝叶斯模型算法
      • 高斯模型
      • 多项式模型
      • 伯努利模型
    • 高斯模型
      • 介绍
        • **大家在学习高等数学时,应该学过高斯分布,也就是正态分布,是一种连续型变量的概率分布。简单来说,高斯分布就是当频率直方图的区间变得特别小时的拟合曲线,像座小山峰,其中两端的特别小,越往中间越高。
          **
        • 所谓正态分布,就是正常形态的分布,它是自然界的一种规律。现实生活中有很多现象均服从高斯分布,比如收入,身高,体重等,大部分都处于中等水平,特别少和特别多的比例都会比较低。
      • 高斯分布(正态分布)
        • 在数学中往往正态分布被称为高斯分布
        • 通过假设P(xi|Y)是服从高斯分布(也就是正态分布),来估计训练集数据的每个特征分到每个类别Y的条件概率P是多少(估计每个特征下对应每个类别的条件概率)。对于每个特征下的对应每个分类结果概率的取值,高斯朴素贝叶斯有如下公式:
        • exp函数为高等数学里以自然常数e为底的指数函数
        • P ( x i ∣ Y ) = f ( x i ; μ y , σ y ) ∗ ϵ = 1 2 π σ y 2 e x p ( − ( x i − μ y ) 2 2 σ y 2 ) P(x_i|Y)=f(x_i;\mu_y,σ_y)*\epsilon=\frac{1}{\sqrt{2\piσ_y^2}}exp(-\frac{(x_i-\mu_y)^2}{2σ_y^2}) P(xiY)=f(xi;μy,σy)ϵ=2πσy2 1exp(2σy2(xiμy)2)
          ∘ \circ Y:分类的类别
          ∘ \circ x:分类的特征
          ∘ \circ 特征所属标签的均值(𝜇)和标准差(𝛔)
          ∘ \circ 每个特征x分到每个类别Y的条件概率P,比如一个人的身高,体重,三维三个特征,其中一个特征占这个丑美类别的概率
        • 什么是连续性变量和离散型变量
          • 离散变量:是指其数值只能用自然数或整数单位计算的则为离散变量.例如,企业个数,职工人数,设备台数等,只能按计量单位数计数,这种变量的数值一般用计数方法取得
          • 连续性变量:在一定区间内可以任意取值的变量叫连续变量,其数值是连续不断的,相邻两个数值可作无限分割,即可取无限个数值.例如,生产零件的规格尺寸,人体测量的身高,体重,胸围等为连续变量,其数值只能用测量或计量的方法取得
        • 高斯分布模型的作用:
          • 在贝叶斯分类中,高斯模型就是用来处理连续型特征变量的,当使用此模型时,我们会假定特征属于高斯分布,然后基于训练样本计算特征所属标签的均值(𝜇)和标准差(𝛔),这样就可以估计某个特征属于某个类别的概率。
            • 比如:判断一个人帅还是丑,则帅&丑就是分类的标签,一个人的特征假设有身高、体重,三围,则高斯分布函数会计算身高的特征分到帅的条件概率P和丑的条件概率P,在计算体重的特征分到帅和丑的条件概率,以此类推。
          • 对于任意一个Y的取值,高斯函数都以求解最大化的P为目标,这样我们才能够比较在不同标签下我们的样本究竟更靠近哪一个取值。以最大化P为目标,高斯函数会为我们求解公式中的参数𝜇y和𝛔y。求解出参数后,带入一个xi的值,就能够得到一个P的概率取值。
            • 比如基于上述案例,将高斯函数将身高这个特征分到帅的概率为70%,分到丑的概率为30%,则基于最大化P的准则,高斯函数返回的结果为70%,就表示特征身高偏向于让带有身高特征的样本数据分到帅的类别。剩下的体重和三围以此类推。然后将每一个特诊分到每一个类别的最大概率进行标签(类别)的均值计算和方差计算返回类似每一个特征的系数w。
            • 最后就相当于于基于训练数据就可以求解出每一个特征对应的高斯函数的结果,该结果就表示该特征更偏向于将该条样本分到哪一个类别,也可以将每一个特征的高斯函数返回值作为类似线性回归中的系数w。
        • 高斯模型API:
          • from sklearn.naive_bayes import GuassianNB
          • 实例化模型对象的时候,我们不需要对高斯朴素贝叶斯输入任何的参数,可以说是非常轻量级的类,操作非常容易。但过于简单也意味着贝叶斯没有太多的参数可以调整,因此贝叶斯算法的成长空间并不是太大,如果
        • 高斯模型作用在手写数字识别案例中
        • predict_proba():给出每一个测试集样本属于每个类别的概率,最大的就是分类结果
        • predict_log_proba():predict_proba的对数化,最大的就是分类结果
from sklearn.naive_bayes import GaussianNB
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split

data = load_digits()
feature = data['data']
target = data['target']
x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.2, random_state=2021)
nb = GaussianNB()
nb.fit(x_train, y_train)
print(nb.score(x_test, y_test))
print(nb.predict(x_test[4].reshape(1, -1)))
print(y_test[4])
print(y_test[:10])  # 真实分类结果
print(nb.predict(x_test)[:10])  # 模型分类结果
print(nb.predict_log_proba(x_test[5].reshape(1, -1)))
  • 将高斯模型作用到鸢尾花分类中
from sklearn.naive_bayes import GaussianNB
from sklearn.datasets import load_digits
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

data1 = load_iris()
f = data1.data
t = data1.target
x1_train, x1_test, y1_train, y1_test = train_test_split(f, t, test_size=0.2, random_state=2021)
nb = GaussianNB()
nb.fit(x_train, y_train)
nb.fit(x1_train, y1_train)
print(nb.score(x1_test, y1_test))
print(nb.predict(x1_test[0].reshape(1, -1)))
print(y1_test[0])
print(nb.predict_proba(x1_test[0].reshape(1, -1)))
print(nb.predict_log_proba(x1_test[0].reshape(1, -1)))
  • 多项式模型 (离散特征的概率计算)
    • 与高斯分布相反,多项式主要适用于离散特征的概率计算,且sklearn的多项式模型不接受负值。虽然sklearn中的多项式模型也可以被用作在连续性特征概率计算中,但是我们如果想要处理连续性变量则最好选择高斯模型
      • 注意:因为多项式不接受负值的输入,所以样本数据的特征为数值型数据的话,务必要进行归一化处理保证特征数据中无负值出现!!!
      • 原理:计算出一篇文章为某些类别的概率(文章是固定的,也就是说在该文章为前提下求出所属类别的概率,因此文章就是概率论中条件概率的条件),最大概率的类型就是该文章的类别。
      • P(类别|文章):类别可以为军事,财经,体育等等,文章其实就是一个又一个的词语
      • P(体育|词1,词2,词3…)==1/6
      • P(财经|词1,词2,词3…)==1/3
        则该文章属于财经类别,那么P(财经|词1,词2,词3…)如何计算值呢?如何计算多个条件下一个结果的概率呢?
        记作:P(A|B)
        特性:P(A1,A2|B)=P(A1|B)P(A2|B)
  • 朴素贝叶斯算法公式
    • P ( C ∣ W ) = P ( W ∣ C ) P ( C ) P ( W ) P(C|W)=\frac{P(W|C)P(C)}{P(W)} P(CW)=P(W)P(WC)P(C)
    • 公式可以理解为:
      • P ( C ∣ F 1 , F 2... ) = P ( F 1 , F 2... ∣ C ) P ( C ) P ( F 1 , F 2... ) P(C|F1,F2...)=\frac{P(F1,F2...|C)P(C)}{P(F1,F2...)} P(CF1,F2...)=P(F1,F2...)P(F1,F2...C)P(C)
    • 细节解释:
      • w为给定文档的特征,也就是文章中拆分出来的不同词语
      • c为文档的类别(财经,体育,军事…)
    • 那么:一篇文档为财经和军事的概率计算如下
      • *P(财经|词1,词2,词3) ==》P(词1,词2,词3|财经)P(财经)/P(W)
      • *P(军事|词1,词2,词3) ==》P(词1,词2,词3|军事)P(军事)/P(W)
      • 上述两个公式中都有想用的P(W),可以抵消,则公式简化为:
        • *P(词1,词2,词3|财经)P(财经)==》P(W|C)P©
        • *P(词1,词2,词3|军事)P(军事) ==》P(W|C)P©
        • 这样的公式我们是可以进行计算的,这就是条件概率
      • P©:每个文档类别的概率(某个文档类别文章的数量/总文档数量)
      • P(W|C):给定类别下特征的概率,此处的特征就是预测文档中出现的词语
        • P(W|C)的计算方法:
          • P(F1|C)=Ni/N:F1为预测文档中的某一个词,C为指定的类别
            • Ni:F1这个词在C类别所有文档中出现的次数
            • N:所属类别C下的文档所有词出现的次数和
      • 思考:属于某个类别的概率为0,合适吗?
        • 肯定不合适,虽然被预测文章中没有出现云计算这个词语,但是出现娱乐类别中的其他词,所以概率为0不合适!那么如何处理呢?
        • 解决方法:拉普拉斯平滑系数
        • P ( F 1 ∣ C ) = N i + α N + α m P(F1|C)=\frac{Ni+\alpha}{N+\alpha m} P(F1C)=N+αmNi+α
        • α \alpha α为指定的系数一般为1,m为训练文档中统计出的特征词个数
        • 多项式朴素贝叶斯API
          • from sklearn.naive_bayes import MultinomialNB
          • MultinomialNB(alpha=1.0, fit_prior=True, class_prior=None)
            • alpha:拉普拉斯平滑系数
        • sklearn文本提取——TfidfVectorizer(求出Ni/N的值)
          • 什么是TF-IDF?
            • 在信息检索中,tf-idf(词频-逆文档频率)是一种统计方法,用以评估一个单词在一个文档集合或语料库中的重要程度。
          • 原理
            • TF-IDF实际上是:TF*IDF。主要思想:如果某个词或某个短语在一篇文章出现的频率高(即TF高),则认为此词具有很好的类别区分能力,适合用来分类。
          • TF:表示一个给定词语t在一篇给定文档d中出现的频率。TF越高,则词语t对文档d来说越重要,TF越低,则词语t对文档d来说越不重要。那是否可以以TF作为文本相似度评价标准呢?答案是不行的,举个例子,常用的中文词语如“我”,“了”,“是”等,在给定的一篇中文文档中出现的频率是很高的,但这些中文词几乎在每篇文档中都具有非常高的词频,如果以TF作为文本相似度评价标准,那么几乎每篇文档都能被命中。
            • TF(w)=(词w在文档中出现的次数)/(文档的总词数) == P(F1|C)=Ni/N
          • IDF:逆向文章频率。有些词可能在文本中频繁出现,但并不重要,也即信息量小,如is,of,that这些单词或者“我”,“了”,“是”等,这些单词在语料库中出现的频率也非常大,我们就可以利用这点,降低其权重。IDF(W)=log_e(语料库的总文档书)/(语料库词w《is,of……》出现的文档数。)
      • 实战
        • 数据使用- 实战:数据使用fetch_20newsgroups中的数据,包含了20个主题的18000个新闻组的帖子
          • 流程
            • 加载20类新闻数据,并进行样本分割
              生成文章特征词
              使用模型进行文章分类
from sklearn.naive_bayes import MultinomialNB
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
import sklearn.datasets as datasets

news = datasets.fetch_20newsgroups(subset='all')
feature = news.data
target = news.target
# 特征提取,对文章进行特征抽取,特征值化(TF_IDF)文档提取
# 参数,input可以手动写内容
t = TfidfVectorizer()
feature_t = t.fit_transform(feature)  # 返回的是比例Ni/N
x_train, x_test, y_train, y_test = train_test_split(feature_t, target, test_size=0.01, random_state=2021)
m = MultinomialNB()
m.fit(x_train, y_train)
print(m.score(x_test, y_test))
print(m.predict(x_test[10]))
print(y_test[10])
print(m.predict_log_proba(x_test[10]).max())
  • 注意:

    • fit_transform()干了两件事,fit找到数据转换规则,并将数据标准化
    • transform():是将数据进行转换,比如数据的归一化和标准化,将测试数据按照训练数据同样的模型进行转换,得到特征向量。可以直接把转换规则拿来用,所以并不需要fit_transform(),否则,两次标准化的数据格式(或说数据参数)就不一样了。
  • 伯努利模型BernoulliNB - (只能做二分类) - 特征数据为二项分布

    • 介绍:
      • 多项式朴素贝叶斯可同时处理二项分布(抛硬币)和多项分布(掷骰子),其中二项分布又叫做伯努利分布,它是一种现实中常见,并且拥有很多优越数学性质的分布。因此,既然有着多项式朴素贝叶斯,我们自然也就又专门用来处理二项分布的朴素贝叶斯:伯努利朴素贝叶斯。
      • 与多项式模型一样,伯努利模型适用于离散特征的情况,所不同的是,数据集中可以存在多个特征,但每个特征都是二分类的.伯努利模型中每个特征的取值只能是1和0(以文本分类为例,某个单词在文档中出现过,则其特征值为1,否则为0).伯努利模型需要比MultinomialNB多定义一个二值化的方法,该方法会接受一个阈值并将输入的特征二值化(1,0).当然也可以直接采用MultinomialNB,但需要预先将输入的特征二值化.
    • 作用:
      • 伯努利朴素贝叶斯与多项式朴素贝叶斯非常相似,都常用于处理文本分类数据。但由于伯努利朴素贝叶斯是处理二项 分布,所以它更加在意的是“是与否”。判定一篇文章是否属于体育资讯,而不是说属于体育类还是娱乐类。
    • API:
      • class sklearn.naive_bayes.BernoulliNB (alpha=1.0, binarize=0.0, fit_prior=True, class_prior=None)
      • 参数计算
        • alpha:拉普拉斯平滑系数
          binarize:可以是数值或者不输入。如果不输入,则BernoulliNB认为每个数据特征都已经是二元(二值化)的。否则的话,小于binarize的会归为一类,大于binarize的会归为另外一类
from sklearn import preprocessing
import numpy as np

X = np.array([[1, -2, 2, 3, 1, 10],
              [1, 2, 3, 33, 4, -90],
              [11, 29, 90, -80, 0, 4]])
b = preprocessing.Binarizer(threshold=5)
X_b = b.transform(X)
print('二值化(闸值:5)', X_b)
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
import sklearn.datasets as datasets
from sklearn.naive_bayes import BernoulliNB

news = datasets.fetch_20newsgroups(subset='all')
feature = news.data
target = news.target
print(len(feature))
print(len(target))
# 特征提取,对文章进行特征抽取,特征值化(TF_IDF)文档提取
# 参数,input可以手动写内容
x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.01, random_state=2021)
t = TfidfVectorizer()
x_train = t.fit_transform(x_train)
x_test = t.transform(x_test)
mlt = BernoulliNB()
mlt.fit(x_train, y_train)
y_predict = mlt.predict(x_test)
print('预测文章类别为', y_predict)
print('真实文章类别', y_test)
print('准确率为:', mlt.score(x_test, y_test))
  • 朴素贝叶斯模型的分类优缺点
    • 优点:
      • 朴素贝叶斯发源于古典数学理论,有稳定的分类效率
      • 对缺少数据不敏感,算法也比较简单,常用于文本分类
      • 分类准确度高,速度快
    • 缺点:
      • 由于使用了样本属性独立性的假设,所以如果样本有关联时,其效果不好
  • 逻辑回归
    • 在之前的课程中我们已经学习接触过相关的回归模型了,我们知道回归模型是用来处理和预测连续型标签的算法。然而逻辑回归,是一种名为“回归”的线性分类器,其本质是由线性回归变化而来的,一种广泛使用于分类问题中的广义回归算法。要理解逻辑回归从何而来,得要先理解线性回归。线性回归是机器学习中最简单的的回归算法,它写作一个几乎人人熟悉的方程(为了更好理解本节后面的讲解到的sigmod函数,下面的回归函数用z来表示):
    • z = θ 0 + θ 1 x 1 + θ 2 x 2 + … … + θ n x n z=\theta_0+\theta_1x_1+\theta_2x_2+……+\theta_nx_n z=θ0+θ1x1+θ2x2++θnxn
    • 其中θ0被称为截距,θ1~θn被称为系数,这个表达式我们可以用矩阵来表示这个方程,其中x和θ都可以看做是一个列矩阵,则有:
    • z = [ θ 0 θ 1 θ 2 ⋅ ⋅ ⋅ θ n ] ∗ [ x 0 x 1 x 2 ⋅ ⋅ ⋅ x n ] = θ T x ( x 0 = 1 ) z=\left[ \begin{array}{lcr} θ_0&θ_1&θ_2&···&θ_n\end{array} \right]*\left[ \begin{array}{lcr} x_0\\x_1\\x_2\\···\\x_n\end{array} \right]=θ^Tx(x_0=1) z=[θ0θ1θ2θn]x0x1x2xn=θTx(x0=1)
    • 通过函数z,线性回归使用输入的特殊矩阵X来输出一组连续型的标签值y_pred,以完成各种预测连续型变量的任务(比如预测产品销量,预测股价等等)。
    • 那如果我们的标签是离散型变量,尤其是,如果是满足0-1分布的离散型变量,使用逻辑回归这个回归模型
    • 问题:回归模型可以实现分类效果吗?
    • 可以,使用sigmod函数
  • sigmod函数
    • 我们可以通过引入sigmod函数,将线性回归方程z变换为g(z),并且将g(z)的值分布在(0,1)之间,且当g(z)接近0时样本的标签为类别0,当g(z)接近1时样本的标签为类别1,这样就得到了一个分类模型。
    • g ( z ) = 1 1 + e − z g(z)=\frac{1}{1+e^{-z}} g(z)=1+ez1
      在这里插入图片描述
  • sigmod函数解释
    • sigmod函数是一个s型的函数,当自变量z趋近于正无穷时,因变量g(z)趋近于1,而自变量趋近于负无穷时,因变量趋近于0,它能够将任何实数(非0和1的标签数据)映射到(0,1)区间,使其可用于将任意值函数转换为更适合二分类的函数。因为这个性质,sigmod函数也被当做是归一化的一种方法,与我们之前学过的MinMaxSclaer同理,是属于数据预处理中的“缩放”功能,可以将数据压缩到[0,1]之内。区别在于,MinMaxScaler归一化之后,是可以取到0和1的(最大值归一化后就是1,最小值归一化后就是0),但Sigmoid函数只是无限趋近于0和1。
import numpy as np

num = 34.5
print(1 / (1 + (np.e ** (-num))))

num = -12
print(1 / (1 + (np.e ** (-num))))
  • 逻辑回归与线性回归的联系
    • 线性回归中 z=𝛉Tx,于是我们将z带入,就得到了二元逻辑回归模型的一般形式:
    • g ( z ) = y ( x ) = 1 1 + e − θ T x g(z)=y(x)=\frac{1}{1+e^{-\theta^T x}} g(z)=y(x)=1+eθTx1
    • y(x)就是我们逻辑回归返回的标签值。此时y(x)的取值都在[0,1]之间,因此y(x)和1-y(x)相加必然为1。如果我们令y(x)除以1-y(x)可以得到形似几率的y(x)/1-y(x),在此基础上取对数,可以很容易就得到:
    • l n y ( x ) 1 − y ( x ) = l n 1 1 + e − θ T x 1 − 1 1 + e − θ T x = l n 1 e − θ T x = l n ( e θ x ) = θ x ln\frac{y(x)}{1-y(x)}=ln\frac{\frac{1}{1+e^{-\theta^T x}}}{1-\frac{1}{1+e^{-\theta^T x}}}=ln\frac{1}{e^{-\theta^T x}}=ln(e^{\theta x})=\theta x ln1y(x)y(x)=ln11+eθTx11+eθTx1=lneθTx1=ln(eθx)=θx
    • 不难发现,y(x)逻辑回归的形似几率取对数的本质其实就是我们的线性回归z,我们实际上是在对线性回归模型的预测结果取对数几率来让其的结果无限逼近0和1。因此,其对应的模型被称为”对数几率回归“(logistic Regression),也就 是我们的逻辑回归,这个名为“回归”却是用来做分类工作的分类器。
    • 逻辑回归的形似几率取对数就是线性回归
      线性回归解的对数几率就是逻辑回归
  • 因此逻辑回归是由线性回归变化而来的
    • 线性回归的核心任务是通过求解θ构建z这个预测函数,并希望预测函数z能够尽量拟合数据,因此逻辑回归的核心任务也是类似的:求解θ来构建一个能够尽量拟合数据的预测函数z,并通过向预测函数中输入特征矩阵来获取相应的标签值y。
      在这里插入图片描述
  • 逻辑回归的优点
    • 首先必须要声明的一点就是逻辑回归是一个受工业商业热爱,使用广泛的模型
      • 1.逻辑回归对线性关系(特征与标签之间的线性关系极强的数据)的拟合效果好到丧心病狂,比如金融领域中的信用卡欺诈,评分卡制作,电商中的营销预测等等相关的数据,都是逻辑回归的强项。相对,逻辑回归在非线性数据中的效果有时候比瞎猜还不如,如果你事先知道你的数据之间的联系是非线性的,千万一定不要使用逻辑回归!!!
        • 其实最简单判别一个模型是否为线性的,只需要判别决策边界是否是直线,也就是是否能用一条直线来划分
      • 2.逻辑回归计算快:对于线性数据,逻辑回归的拟合和计算都非常快,计算效率优于SVM和随机森林,亲测表示在大型数据上尤其能看出区别。
      • 3.逻辑回归返回的分类结果不是固定的0,1,而是以小数形式呈现的类概率数字:我们因此可以把逻辑回归返回的结果当成连续型数据来利用。比如在评分卡制作时,我们不仅需要判断客户是否会违约,还需要给出确定的”信用分“,而这个信用分的计算就需要使用类概率计算出的对数几率(概率)。
    • 总结:我们已经了解了逻辑回归的本质,它是一个返回对数几率的在线性数据上表现优异的分类器,它主要被应用在金融领域。注意,虽然我们熟悉的逻辑回归通常被用于处理二分类问题,但逻辑回归也可以做多分类。
  • 逻辑回归的损失函数
    • 在逻辑回归分类的时候,不管原始样本中的类别使用怎样的值或者文字表示,逻辑回归统一将其视为0类别和1类别。因为逻辑回归也采用了寻找特征和目标之间的某种关系,则每个特征也是有权重的就是w,那么也会存在真实值和预测值之间的误差(损失函数),那么逻辑回归的损失函数和线性回归的损失函数是否一样呢?由于逻辑回归是用于分类的,因此该损失函数和线性回归的损失函数是不一样的!逻辑回归采用的损失函数是:对数似然损失函数:
    • 注意:没有求解参数需求的模型是没有损失函数的,比如KNN,决策树,一般有求解系数需求就有损失函数。
    • 损失函数被写作如下:
      • 为什么使用-log函数为损失函数,损失函数的本质就是,如果我们预测对了,则没有损失,反之则损失需要变的很大,而-log函数在【0,1】之间正好符合这一点。(-log可以放大损失)
      • -log(h)表示分类到正例1的损失
      • -log(1-h)表示分类到反例0的损失
      • c o s t ( h θ ( x ) , y ) = { − l o g ( h θ ( x ) ) if y=1 − l o g ( 1 − h θ ( x ) ) if y=0 cost(h_\theta(x),y)=\left\{ \begin{aligned} -log(h_{\theta}(x)) & &\text{if y=1}\\ -log(1-h_{\theta}(x))& &\text{if y=0} \\ \end{aligned} \right. cost(hθ(x),y)={log(hθ(x))log(1hθ(x))if y=1if y=0
  • 怎么理解单个的式子呢?这个要根据-log的函数图像来理解
    在这里插入图片描述
    • 综合完整损失函数
    • $ c o s t ( h θ ( x ) , y ) = ∑ i = 1 m − y i l o g ( h θ ( x ) ) − ( 1 − y i ) l o g ( 1 − h θ ( x ) ) cost(h_\theta(x),y)=\sum\limits_{i=1}^m-y_ilog(h_\theta(x))-(1-y_i)log(1-h_\theta(x)) cost(hθ(x),y)=i=1myilog(hθ(x))(1yi)log(1hθ(x))
    • 损失函数表征预测值与真实值之间的差异程度,如果预测值与真正值越接近则损失函数应该越小
    • 损失函数解释
      • -yilog(h)表示分类到真实标签正例的损失,根据-log函数得知如果分类正确则损失值小,反之损失大。
      • -(1-yi)log(1-h)表示分类到真实标签反例的损失,根据-log函数得知如果分类正确则损失值小,反之损失大。
      • 那么两者相加就获得了逻辑回归模型总分类结果的损失!
    • 梯度下降
      • 逻辑回归的数学目的是求解能够让模型最优化,拟合程度最好的参数𝛉的值,即求解能够让损失函数J(𝛉)最小化的𝛉值。
      • 梯度下降原理介绍:
        • 假设现在有一个带两个特征并且没有截距的逻辑回归y(x1,x2),两个特征所对应的参数分别为[𝛉1,𝛉2]。下面这个华丽的平面就是我们的损失函数 J(𝛉1,𝛉2)在以𝛉1,𝛉2和J为坐标轴的三维立体坐标系上的图像。现在,我们寻求的是损失函数的最小值,也就是图像的最低点。
          在这里插入图片描述
  • 那我们怎么做呢?我在这个图像上随机放一个小球,当我松手,这个小球就会顺着这个华丽的平面滚落,直到滚到 深蓝色的区域——损失函数的最低点。为了严格监控这个小球的行为,我让小球每次滚动的距离有限,不让他一次性滚到最低点,并且最多只允许它滚动100步,还要记下它每次滚动的方向,直到它滚到图像上的最低点。
  • 可以看见,小球从高处滑落,在深蓝色的区域中来回震荡,最终停留在了图像凹陷处的某个点上。非常明显,我们可以观察到几个现象:
    • 首先,小球并不是一开始就直向着最低点去的,它先一口气冲到了蓝色区域边缘,后来又折回来,我们已经规定了小球是多次滚动,所以可见,小球每次滚动的方向都是不同的。
    • 另外,小球在进入深蓝色区域后,并没有直接找到某个点,而是在深蓝色区域中来回震荡了数次才停下。这有两种可能:
      • 1) 小球已经滚到了图像的最低点,所以停下了,
      • 2) 由于我设定的步数限制,小球还没有找到最低点,但也只 好在100步的时候停下了。也就是说,小球不一定滚到了图像的最低处。
    • 但无论如何,小球停下的就是我们在现有状况下可以获得的唯一点了。如果我们够幸运,这个点就是图像的最低点,那我们只要找到这个点的对应坐标(𝛉1,𝛉2,J),就可以获取能够让损失函数最小的参数取值[𝛉1,𝛉2]了。如此,梯度下降的过程就已经完成。
  • 在这个过程中,小球其实就是一组组的坐标点(𝛉1,𝛉2,J);小球每次滚动的方向就是那一个坐标点的梯度向量的方 向,因为每滚动一步,小球所在的位置都发生变化,坐标点和坐标点对应的梯度向量都发生了变化,所以每次滚动 的方向也都不一样;人为设置的100次滚动限制,就是sklearn中逻辑回归的参数max_iter,代表着能走的最大步数.
    • 所以梯度下降,其实就是在众多[𝛉1,𝛉2]可能的值中遍历,一次次求解坐标点的梯度向量,不断让损失函数的取值J逐渐逼近最小值,再返回这个最小值对应的参数取值[𝛉1,𝛉2]的过程。
  • 正则化(过拟合的处理)
    • 注意:
      • 由于我们追求损失函数的最小值,让模型在训练集上表现最优,可能会引发另一个问题:如果模型在训练集上表现优秀,在测试集表现糟糕,模型就会过拟合。所以我们还是需要使用控制过拟合的技术来帮助我们调整模型,对逻辑回归中过拟合的控制,使用正则化来实现。
    • 正则化是用来防止模型过拟合的过程,常用的有L1正则化和L2正则化两种选项,分别通过在损失函数后加上参数向量𝛉的L1和L2范式的倍数来实现。这个增加的范式,被称为“正则项”,也被称为“惩罚项”
    • L1范式
      • L1范式表现为参数向量𝛉中每个参数的绝对值之和
      • J ( θ ) L 1 = J ( θ ) + 1 C ∑ j = 1 n ∣ θ j ∣ ( j ≥ 1 ) J(\theta)_{L1}=J(\theta)+\frac{1}{C}\sum\limits_{j=1}^{n}|\theta_j|(j\ge1) J(θ)L1=J(θ)+C1j=1nθj(j1)
    • L2范式
      • L2范式表现为参数向量𝛉中的每个参数的平方和的开方值
      • J ( θ ) L 2 = J ( θ ) + 1 C ∑ j = 1 n ( θ j ) 2 ( j ≥ 1 ) J(\theta)_{L2}=J(\theta)+\frac{1}{C}\sqrt{\sum\limits_{j=1}^{n}(\theta_j)^2}(j\ge1) J(θ)L2=J(θ)+C1j=1n(θj)2 (j1)
      • 其中J(𝛉)是我们之前提过的损失函数,C是用来控制正则化程度的超参数(参数C越大返回正则化后的值越小,C越小返回正则化后的值越大),n是方程中特征的总数,也是方程中参数的总数,j代表每个𝛉参数(w系数)。在这里,j要大于等于1,是因为我们的参数向量中,第一个参数是𝛉0,是我们的截距它通常是不参与正则化的。
      • 总结:我们知道损失函数的损失值越小(在训练集中预测值和真实值越接近)则逻辑回归模型就越有可能发生过拟合(模型只在训练集中表现的好,在测试集表现的不好)的现象。通过正则化的L1和L2范式可以加入惩罚项C来矫正模型的拟合度。因为C越小则损失函数会越大表示正则化的效力越强,参数𝛉会被逐渐压缩的越来越小。
      • 注意:L1正则化会将参数w压缩为0,L2正则化只会让参数尽量小,不会取到0。
      • L1和L2范式的区别
        • 在L1正则化在逐渐加强的过程中,携带信息量小的、对模型贡献不大的特征的参数w,会比携带大量信息的、对模型有巨大贡献的特征的参数更快地变成0,所以L1正则化本质是一个特征选择的过程。L1正则化越强,参数向量中就越多的参数为0,选出来的特征就越少,以此来防止过拟合。因此,如果特征量很大,数据维度很高,我们会倾向于使用L1正则化。
        • L2正则化在加强的过程中,会尽量让每个特征对模型都有一些小的贡献,但携带信息少,对模型贡献不大的特征的参数w会非常接近于0。通常来说,如果我们的主要目的只是为了防止过拟合,选择L2正则化就足够了。但是如果选择L2正则化后还是过拟合,模型在未知数据集上的效果表现很差,就可以考虑L1正则化
        • 建立两个逻辑回归,L1正则化和L2正则化的差别就一目了然了:
from sklearn.linear_model import LogisticRegression as LR
from sklearn.datasets import load_breast_cancer
import numpy as np
from sklearn.model_selection import train_test_split

# 加载样本数据
data = load_breast_cancer()
x = data.data
y = data.target
# 建立两种模型
lrl1 = LR(penalty='l1', C=0.1, solver='liblinear', max_iter=100)
lrl2 = LR(penalty='l2', C=0.1, solver='liblinear', max_iter=100)
# 逻辑回归的重要属性coef_,查看每个特征对应的系数
lrl1.fit(x, y)
print('L1范式:', lrl1.coef_)
lrl2.fit(x, y)
print('L2范式:', lrl2.coef_)
  • 逻辑回归API
    • **from sklearn.linear_model import LogisticRegression **
    • 超参数介绍
      • penalty:可以输入l1或l2来指定使用哪一种正则化方式。不填写默认“l2。注意若选择“l1”正则化,参数solver仅能够使用求解方式“liblinear”和“saga”。若使用“l2”正则化,参数solver中所有的求解方式都可以使用。
      • C:惩罚项。必须是一个大于0的浮点数,不填写默认1.0,则默认正则项与损失函数比值是1:1。C越小,损失函数会越大,模型对损失函数的惩罚越严重,正则化的效力越强,参数会被逐渐压缩的越小。
      • max_iter:梯度下降中能走的最大步数,默认为100.步数的不同取值可以帮我们获取不同的损失函数的损失值。目前没有好的办法可以计算出最优的max_iter值,一般是绘制学习曲线对其进行取值。
      • solver:我们之前提到的梯度下降法,只是求逻辑回归参数θ的一种方法。sklearn为我们提供了多种选择,让我们可以使用不同的求解器来计算逻辑回归。求解器的选择,由参数solver控制,共有5种选择。
        • liblinear:是二分类专用(梯度下降),也是选择默认的求解器。
        • lbfgs,newton-cg,sag,saga:是多分类专用,几乎不用
          在这里插入图片描述
      • multi_class(告知模型处理的是二分类还是多分类)
        • 输入"ovr", “multinomial”, “auto"来告知模型,我们要处理的分类问题的类型。默认是"auto”。
          • ’ovr’:表示分类问题是二分类,或让模型使用"一对多"的形式来处理多分类问题。
          • ’multinomial’:表示处理多分类问题,这种输入在参数solver是’liblinear’时不可用。
          • "auto":表示会根据数据的分类情况和其他参数来确定模型要处理的分类问题的类型。比如说,如果数据是二分 类,或者solver的取值为"liblinear",“auto"会默认选择"ovr”。反之,则会选择"multinomial"。
        • class_weight (处理标签类别分类不均衡)
          • 表示样本不平衡处理的参数。样本不平衡指的是在一组数据中,某一类标签天生占有很大的比例,或误分类的代价很高,即我们想要捕捉出某种特定的分类的时候的状况。什么情况下误分类的代价很高?
            • 例如,我们现在要对潜在犯罪者和普通人进行分类,如果没有能够识别出潜在犯罪者,那么这些人就可能去危害社会,造成犯罪,识别失败的代价会非常高,但如果,我们将普通人错误地识别成了潜在犯罪者,代价却相对较小。所以我们宁愿将普通人分类为潜在犯罪者后再人工甄别,但是却不愿将潜在犯罪者 分类为普通人,有种"宁愿错杀不能放过"的感觉。
            • 再比如说,在银行要判断“一个新客户是否会违约”,通常不违约的人vs违约的人会是99:1的比例,真正违约的人其实是非常少的。这种分类状况下,即便模型什么也不做,全把所有人都当成不会违约的人,正确率也能有99%, 这使得模型评估指标变得毫无意义,根本无法达到我们的“要识别出会违约的人”的建模目的。
        • None:
          • 因此我们要使用参数class_weight对样本标签进行一定的均衡,给少量的标签更多的权重,让模型更偏向少数类, 向捕获少数类的方向建模。该参数默认None,此模式表示自动给与数据集中的所有标签相同的权重,即自动1: 1。
        • balanced
          • 当误分类的代价很高的时候,我们使用”balanced“模式,可以解决样本不均衡问题。
from sklearn.linear_model import LogisticRegression as LR
from sklearn.datasets import load_breast_cancer
import numpy as np
from sklearn.model_selection import train_test_split

# 加载样本数据
data = load_breast_cancer()
x = data.data
y = data.target
from sklearn.metrics import accuracy_score
import matplotlib.pyplot as plt

l2 = []
l2_test = []
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=420)
for i in np.arange(1, 201, 10):
    lrl2 = LR(penalty='l2', solver='liblinear', C=0.9, max_iter=i)
    lrl2.fit(x_train, y_train)
    l2.append(accuracy_score(lrl2.predict(x_train), y_train))
    l2_test.append(accuracy_score(lrl2.predict(x_test), y_test))
graph = [l2, l2_test]
color = ["black", "red"]
label = ["L2train", "L2test"]
plt.figure(figsize=(10, 10))
for i in range(len(graph)):
    plt.plot(np.arange(1, 201, 10), graph[i], color[i], label=label[i])
plt.legend(loc=4)
plt.xticks(np.arange(1, 201, 10))
plt.show()

WOE&IV编码&分箱

  • 概述
    • IV和WOE通常是用在对模型的特征筛选的场景中,在模型刚建立时,选择的变量往往比较多,这个时候就需要一种方法来帮助我们衡量什么变量应该进入模型什么变量应该舍弃。IV和WOE就可以帮助我们进行衡量。用IV和WOE值来进行判断,值越大就表示该特征的预测能力越强,则该特征应该加入到模型的训练中。
  • 应用
    • 1.变量筛选。我们选取比较重要的变量加入模型,预测强度可以作为我们判断变量是否重要的一个依据。
    • 2.指导变量离散化。在建模过程中,时常需要对连续变量进行离散化处理,如将年龄进行分段。但是变量不同的离散化结果(如年龄分为[0-15还是[0-20]])会对模型产生不同的影响。因此,可以根据指标所反应的预测强度,调整变量离散化结果。(对一些取值很多的分类变量,在需要时也可以对其进行再分组,实现降维)
  • 计算方法
    • WOE
      • WOE的全称是“Weight of Evidence”,即证据权重。WOE是对原始特征的一种编码形式。要对一个特征进行WOE编码,需要首先把这个变量进行分组处理(也叫离散化、分箱等等,将一个连续型变量离散化)。分组后,对于第i组,WOE的计算公式如下:
      • W O E i = l n p y i p n i = l n η y i η y T η n i η n T WOE_i=ln\frac{py_i}{pn_i}=ln\frac{\frac{\eta y_i}{\eta y_T}}{\frac{\eta n_i}{\eta n_T}} WOEi=lnpnipyi=lnηnTηniηyTηyi
      • 其中,pyi是这个组中正例样本占整个样本中正例样本的比例,pni是这个组中负例样本占整个样本中负例样本的比例,ηyi是这个组中正例样本的数量,ηni是这个组中负例样本的数量,ηyT是整个样本中所有正例样本的数量,ηnT是整个样本中所有负例样本的数量。
      • 从这个公式中我们可以体会到,WOE表示的实际上是“当前分组中正例样本占所有样本中所有正例样本的比例”和“当前分组中负例样本占所有样本中所有负例样本比例”的差异。
    • IV
      • 对于一个分组后的变量对应的IV值,计算公式如下:
      • I V i = ( p y i − p n i ) ∗ W O E i = ( p y i − p n i ) ∗ l n p y i p n i = ( η y i η y T − η n i η n T ) ∗ l n η y i η y T η n i η n T IV_i=(py_i-pn_i)*WOE_i=(py_i-pn_i)*ln\frac{py_i}{pn_i}=(\frac{\eta y_i}{\eta y_T}-\frac{\eta n_i}{\eta n_T})*ln\frac{\frac{\eta y_i}{\eta y_T}}{\frac{\eta n_i}{\eta n_T}} IVi=(pyipni)WOEi=(pyipni)lnpnipyi=(ηyTηyiηnTηni)lnηnTηniηyTηyi
      • 有了一个特征各分组的IV值,我们就可以计算整个特征的IV值,方法很简单,就是把各分组的IV相加:其中n就是分组的个数
      • I V = ∑ i n I V i IV=\sum\limits_{i}^nIV_i IV=inIVi
    • 用实例介绍IV的计算和使用
      • 例子:假设现在某个公司举行一个活动,在举行这个活动之前,先在小范围的客户中进行了一次试点,收集了一些用户对这次活动的一些响应,然后希望通过这些数据,去构造一个模型,预测如果举行这次的活动,是否能够得到很好的响应或者得到客户的响应概率之类。
        假设我们已经从公司客户列表中随机抽取了100000个客户进行了营销活动测试,收集了这些客户的响应结果,作为我们的建模数据集,其中响应的客户有10000个。另外假设我们也已经提取到了这些客户的一些变量,作为我们模型的候选变量集,这些变量包括以下这些:
        最近一个月是否有购买;
        最近一次购买金额;
        最近一笔购买的商品类别;
        是否是公司VIP客户;
        假设,我们已经对这些变量进行了离散化,统计的结果如下面几张表所示。

(1) 最近一个月是否有过购买:

最近一个月是否有过购买响应未响应合计响应比例
4000160002000020%
600074000800007.5%
合计100009000010000010%

(2) 最近一次购买金额:

最近一次购买金额响应未响应合计响应比例
<100250047500500005%
[100,200)3000270003000010%
[200,500)3000120001500020%
>=50015003500500030%
合计100009000010000010%

(3) 最近一笔购买的商品类别:
在这里插入图片描述
(4) 是否是公司VIP客户:
在这里插入图片描述

  • 计算WOE和IV
    • 我们以其中的一个变量“最近一次购买金额”变量为例:
最近一次购买金额响应未响应合计响应比例
<100250047500500005%
[100,200)3000270003000010%
[200,500)3000120001500020%
>=50015003500500030%
合计100009000010000010%

<100元: W O E 1 = l n 2500 10000 47500 90000 = − 0.74721 WOE_1=ln\frac{\frac{2500}{10000}}{\frac{47500}{90000}}=-0.74721 WOE1=ln9000047500100002500=0.74721
[100,200): W O E 2 = l n 3000 10000 27000 90000 = 0 WOE_2=ln\frac{\frac{3000}{10000}}{\frac{27000}{90000}}=0 WOE2=ln9000027000100003000=0
[200,500): W O E 3 = l n 3000 12000 10000 90000 = 0.81093 WOE_3=ln\frac{\frac{3000}{12000}}{\frac{10000}{90000}}=0.81093 WOE3=ln9000010000120003000=0.81093
> = 500 : W O E 4 = l n 1500 3500 10000 90000 = 1.349927 >=500:WOE_4=ln\frac{\frac{1500}{3500}}{\frac{10000}{90000}}=1.349927 >=500:WOE4=ln900001000035001500=1.349927

  • 我们把这个变量离散化为了4个分段:<100元,[100,200),[200,500),>=500元。首先,根据WOE计算公式,这四个分段的WOE分别为:
    在这里插入图片描述
  • 从上面的计算结果中我们可以看一下WOE的基本特点:
    • 当前分组中,响应的比例越大,WOE值越大;
    • 当前分组WOE的正负,由当前分组响应和未响应的比例,与样本整体响应和未响应的比例的大小关系决定,当前分组的比例小于样本整体比例时(分子小于分母),WOE为负,当前分组的比例大于整体比例时(分子大于分母),WOE为正,当前分组的比例和整体比例相等时(分子等于分母),WOE为0。
    • 我们进一步理解一下WOE,会发现,WOE其实描述了某一特征的当前这个分组,对判断该特征是否会响应(或者说属于哪个类)所起到影响方向和大小,当WOE为正时,特征当前取值对判断个体是否会响应起到的正向的影响,当WOE为负时,起到了负向影响。而WOE值的大小,则是这个影响的大小的体现。
    • 分别计算四个分组的IV值:
      <100元: W O E 1 = ( 2500 10000 − 47500 90000 ) ∗ l n 2500 10000 47500 90000 = 0.20756 WOE_1=(\frac{2500}{10000}-\frac{47500}{90000})*ln\frac{\frac{2500}{10000}}{\frac{47500}{90000}}=0.20756 WOE1=(1000025009000047500)ln9000047500100002500=0.20756
      [100,200): W O E 2 = ( 3000 10000 − 27000 90000 ) ∗ l n 3000 10000 27000 90000 = 0 WOE_2=(\frac{3000}{10000}-\frac{27000}{90000})*ln\frac{\frac{3000}{10000}}{\frac{27000}{90000}}=0 WOE2=(1000030009000027000)ln9000027000100003000=0
      [200,500): W O E 3 = ( 3000 10000 − 12000 90000 ) ∗ l n 3000 12000 10000 90000 = 0.135155 WOE_3=(\frac{3000}{10000}-\frac{12000}{90000})*ln\frac{\frac{3000}{12000}}{\frac{10000}{90000}}=0.135155 WOE3=(1000030009000012000)ln9000010000120003000=0.135155
      > = 500 : W O E 4 = ( 1500 10000 − 3500 90000 ) ∗ l n 1500 3500 10000 90000 = 0.149992 >=500:WOE_4=(\frac{1500}{10000}-\frac{3500}{90000})*ln\frac{\frac{1500}{3500}}{\frac{10000}{90000}}=0.149992 >=500:WOE4=(100001500900003500)ln900001000035001500=0.149992
    • 从上面IV的计算结果我们可以看出IV的以下特点:
      • 对于特征的一个分组,这个分组的响应和未响应的比例与样本整体响应和未响应的比例(WOE)相差越大,IV值越大,否则,IV值越小;
      • 极端情况下,当前分组的响应和未响应的比例和样本整体的响应和未响应的比例相等时,IV值为0;
      • 我们计算变量总IV值
        I V = I V 1 + I V 2 + I V 3 + I V 4 = 0.492706 IV=IV_1+IV_2+IV_3+IV_4=0.492706 IV=IV1+IV2+IV3+IV4=0.492706
  • IV值的比较和变量预测能力的排序
    • 四个特征的IV值结果如下:
      • 最近一次购买金额的IV值:0.49270645
      • 最近一个月是否有购买的IV值:0.250224725
      • 最近一笔购买的商品类别的IV值:0.615275563
      • 是否是公司VIP客户的IV值:1.56550367
    • 这四个特征IV排序结果是这样的:是否是公司VIP客户 > 最近一笔购买的商品类别 > 最近一次购买金额 > 最近一个月是否有过购买。我们发现“是否是公司VIP客户”是预测能力最高的特征,“最近一个月是否有过购买”是预测能力最低的特征。如果我们需要在这四个变量中去挑选特征,就可以根据IV从高到低去挑选了。
    • 关于IV和WOE的进一步思考:为什么用IV而不是直接用WOE?
      • 从上面的内容来看,特征各分组的WOE和IV都隐含着这个分组对目标变量的预测能力这样的意义。那我们为什么不直接用WOE相加或者绝对值相加作为衡量一个变量整体预测能力的指标呢?并且,从计算公式来看,对于特征的一个分组,IV是WOE乘以这个分组响应占比和未响应占比的差。而一个特征的IV等于各分组IV的和。如果愿意,我们同样也能用WOE构造出一个这样的一个和出来,我们只需要把特征各个分组的WOE和取绝对值再相加,(取绝对值是因为WOE可正可负,如果不取绝对值,则会把特征的区分度通过正负抵消的方式抵消掉):
      • W O E = ∑ i n ∣ W O E i ∣ WOE=\sum\limits_{i}^{n}|WOE_i| WOE=inWOEi,其中,n为变量分组个数
      • 那么我们为什么不直接用这个WOE绝对值的加和来衡量一个变量整体预测能力的好坏,而是要用WOE处理后的IV呢。
      • 我们这里给出两个原因。IV和WOE的差别在于IV在WOE基础上乘以的那个(py-pn),我们暂且用pyn来代表这个值。
        • 第一个原因,当我们衡量一个特征的预测能力时,我们所使用的指标值不应该是负数,否则,说一个特征的预测能力的指标是-2.3,听起来很别扭。从这个角度讲,乘以pyn这个系数,保证了特征每个分组的结果都是非负数,你可以验证一下,当一个分组的WOE是正数时,pyn也是正数,当一个分组的WOE是负数时,pyn也是负数,而当一个分组的WOE=0时,pyn也是0。
        • 更主要的原因,也就是第二个原因是,乘以pyn后,体现出了特征当前分组中个体的数量占整体个体数量的比例,对变量预测能力的影响。怎么理解这句话呢?我们还是举个例子。
          • 假设我们上面所说的营销响应模型中,还有一个特征A,其取值只有两个:0,1,将特征A分成两组0和1。数据如下:
A响应未响应合计响应比例
1901010090%
09910899909990010%
合计100009000010000010%
  • 我们从上表可以看出,当特征A取值1时,其响应比例达到了90%,非常的高,但是我们能否说特征A的预测能力非常强呢?不能。为什么呢?原因就在于,A取1时,响应比例虽然很高,但这个分组的客户数太少了,占的比例太低了。虽然,如果一个客户在A这个特征上取1,那他有90%的响应可能性,但是一个客户特征A取1的可能性本身就非常的低。所以,对于样本整体来说,变量的预测能力并没有那么强。我们分别看一下变量各分组和整体的WOE,IV。
    在这里插入图片描述
    • 从这个表我们可以看到,特征取1时,响应比达到90%,对应的WOE很高,但对应的IV却很低,原因就在于IV在WOE的前面乘以了一个系数(py-pn),而这个系数很好的考虑了这个分组中样本占整体样本的比例,比例越低,这个分组对特征整体预测能力的贡献越低。相反,如果直接用WOE的绝对值加和,会得到一个很高的指标,这是不合理的。
  • 分箱
    • 数据分箱(也称为离散分箱或分段)是一种数据预处理技术,用于减少次要观察误差的影响,是一种将多个连续值分组为较少数量的“分箱”的方法。说白了就是将连续型特征进行离散化。
    • 注意
      • 分箱的数据不一定必须是数字,它们可以是任何类型的值,如“狗”,“猫”,“仓鼠”等。 分箱也用于图像处理,通过将相邻像素组合成单个像素,它可用于减少数据量。
    • 分箱的作用&意义
      • 离散特征的增加和减少都很容易,易于模型的快速迭代,提升计算速度
      • 特征离散化后,模型会更稳定,比如如果对用户年龄离散化,20-30作为一个区间,不会因为一个用户年龄长了一岁就变成一个完全不同的人。
      • 特征离散化以后,起到了简化了模型的作用,可以适当降低了模型过拟合的风险。
      • pandas实现数据的分箱:pd.cut()
import pandas as pd
import numpy as np
from pandas import Series, DataFrame

score_list = np.random.randint(30, 100, size=20)
print(score_list)
# 设置分箱:方式1
bins = [0, 59, 70, 80, 100]  # 指定分箱数据的范围
score_cat = pd.cut(score_list, bins)
print(score_cat)
print(pd.value_counts(score_cat))
# 设置分箱:方式2
bins1 = 3  # 指定分箱数据的范围的个数
score_cat1 = pd.cut(score_list, bins1)
print(score_cat1)
print(pd.value_counts(score_cat1))
# label参数的作用:设置分箱每一个范围的标识
bins2 = 3  # 指定分箱数据的范围的个数
score_cat2 = pd.cut(score_list, bins2, labels=range(3))
print(score_cat2.tolist())
print(pd.value_counts(score_cat2))
  • 注意:我们对特征分箱后,需要对分箱的每组进行woe编码,然后才能进行模型训练
  • pandas实现数据的分箱:pd.qcut() 等频分箱
import pandas as pd
import numpy as np
from pandas import Series, DataFrame

# qcut是根据这些值的频率来选择箱子的均匀间隔,即每个箱子中含有的数量是相同的
factors = np.random.randn(9)
print(pd.qcut(factors, 3))  # 返回每个数对应的分组
print(pd.qcut(factors, 3).value_counts())  # 计算每个分组中含有的数的数量
  • cut与qcut的区别:
    • cut:按连续数据的大小分到各个桶里,每个桶里样本量可能不同,但是,每个桶相当于一个等长的区间,即:以数据的最大和最小为边界,等份成n个桶。
    • qcut:与cut主要的区别就是每个桶里的样本数量是一定的
    • 经验:在使用中,如果特征是连续的倾向于使用qcut,离散型的倾向于用cut
  • 分箱后求解IV和WOE的值
    • 注意:分箱后就可以求解出IV和WOE的值了。如果想对连续性特征进行离散化,需要对分箱后的每组(箱)数据进行woe或者iv编码,然后才能放进模型训练。
import sklearn.datasets as ds

bc = ds.load_breast_cancer()
feature = bc.data
target = bc.target
# 查看第1列数据范围,判定其为连续性数据
print(feature[:, 1])
print(feature[:, 1].min())
print(feature[:, 1].max())
# 对其进行分箱处理,将其离散化
fea_bins = pd.cut(feature[:, 1], bins=5, labels=range(5))
print(fea_bins.value_counts())
# 对分箱后的特征列进行woe编码
# print(target)
gi = pd.crosstab(fea_bins, target)  # 使用交叉表进行woe编码
print(gi)
gb = pd.Series(data=target).value_counts()
print(gb)
gbi = (gi[1] / gi[0]) / (gb[1] / gb[0])
print(gbi)
woe = np.log(gbi)
print(woe)
# 进行映射操作
dict = woe.to_dict()
print(dict)
woe_bins = fea_bins.map(dict)
print(woe_bins.tolist())
  • 样本类别分布不均衡处理
    • 什么是样本类别分布不均衡?
      • 举例说明,在一组样本中不同类别的样本量差异非常大,比如拥有1000条数据样本的数据集中,有一类样本的分类只占有10条,此时属于严重的数据样本分布不均衡。
    • 样本类别分布不均衡导致的危害
      • 样本类别不均衡将导致样本量少的分类所包含的特征过少,并很难从中提取规律;即使得到分类模型,也容易产生过度依赖与有限的样本数据而导致过拟合问题,当模型应用到新数据,模型的准确性会很差。
    • 解决办法:
      • 通过过抽样和欠抽样解决样本不均衡
        • 也可以叫做上采样,和下采样
    • 过抽样(over-sampling)
      • from imblearn.over_sampling import SMOTE
      • 通过增加分类中少数类样本的数量来实现样本均衡,比较好的方法有SMOTE算法。
      • SMOTE算法原理介绍
        • 简单来说smote算法的思想是合成新的少数类样本,合成的策略是对每个少数类样本a,从它的最近邻中随机选一个样本b,然后在a、b之间的连线上随机选一点作为新合成的少数类样本。
        • 参数:k_neighbors,找出类别少的样本点周围最近的k个邻居
from imblearn.over_sampling import SMOTE
import pandas as pd
import numpy as np

# 数据源产生
x = np.random.randint(0, 100, size=(100, 3))
print(x)
y = pd.Series(data=np.random.randint(0, 1, size=(95,)))
print(y)
y = y.append(pd.Series(data=[1, 1, 1, 1, 1]), ignore_index=False).values
print(y)
y = y.reshape(-1, 1)
all_data_np = np.concatenate((x, y), axis=1)
np.random.shuffle(all_data_np)
df = pd.DataFrame(all_data_np)
print(df)
# 样本数据提取
feature = df[[0, 1, 2]]
target = df[3]
print(target.value_counts())
s = SMOTE(k_neighbors=3)  # 实例化算法对象
s_feature, s_target = s.fit_resample(feature, target)  # 合成新的样本数据
print(s_target.value_counts())
  • 欠抽样(under-sampling)
    • 通过减少分类中多数类样本的数量来实现样本均衡(可能造成样本数据大量丢失)
    • from imblearn.under_sampling import RandomUnderSampler
from imblearn.under_sampling import RandomUnderSampler

r = RandomUnderSampler()
r_feature, r_target = r.fit_resample(feature, target)
print(r_target.value_counts())
print(r_feature.shape)

分类模型的评价指标

  • 问题:如何评判两部手机的好坏?
    1.根据性能评价
    2.根据外观评价
    3.根据价格评价
  • 分析:如果对一个事物进行好坏的评价,首先我们一定是在指定场景下,使用符合该场景相关的评价标准对其进行好坏的评价!那么归于分类模型的评价有如下几种方式:
    准确率
    精准率
    召回率
    f1-Score
    auc
  • 在介绍每种评价指标之前,首先我们来看一个叫做混淆矩阵的东西:

预测结果

正例反例
正例真正例TP伪反例FN
反例伪正例FP真反例TN
  • 混淆矩阵
    • 概念:在分类任务下,预测结果和真实结果之间存在四种不同的组合。适用于二分类和多分类。
    • 真正例率TPR = TP / (TP + FN):预测对的比例,TPR越大越好
      • 预测为正例且实际为正例样本占所有训练集中正例样本的比例
      • 将正例预测对的占正样本的比例(预测对的比例),这个比例越大越好
    • 伪反例率FPR = FP / (FP + TN):预测错的比例,FPR越小越好
      • 预测为正例但实际为负例的样本占训练集中所有负例样本的比例
      • 将负例预测错的占负样本的比例(预测错的比例),这个比例越小越好
      • 注意:如果有其他的类别,其他的每一个类别也有其对应的混淆矩阵表示真伪正例和真伪反例的比例
  • 准确率
    • Accuracy = (TP+TN)/(TP+FN+FP+TN)
    • 解释:(预测正确)/(预测对的和不对的所有结果),简而言之就是预测正确的比例。
    • 模型.score()方法返回的就是模型的准确率
  • 召唤率(较多被使用)
    • Recal = TP/(TP+FN)
    • 解释:真正为正例的样本中预测结果为正例的比例。正样本有多少被找出来了(召回了多少)
    • API:recall_score
  • 精确率
    • Precision = TP/(TP+FP)
    • 解释:预测结果为正例样本(TP+FP)中真实值为正例(TP)的比例。
    • API:accuracy_score
  • f1-score:精确率和召回率的调和平均数
    在这里插入图片描述
    • 有时候我们需要综合精确率和召回率的指标,则需要使用f1-score
    • 模型的精确率和召回率是有矛盾的,而F1分数(F1-score)是分类问题的一个衡量指标。一些多分类问题的机器学习竞赛,常常将F1-score作为最终测评的方法。它是精确率和召回率的调和平均数,最大为1,最小为0。
    • f1_score= 1 1 r e c a l l + 1 p r e = 2 ∗ p r e ∗ r e c a l l p r e + r e c a l l \frac{1}{\frac{1}{recall}+\frac{1}{pre}}=\frac{2*pre*recall}{pre+recall} recall1+pre11=pre+recall2prerecall
    • 反应了模型的稳健性
    • 它是精确率和召回率的调和平均数
    • 是一个综合的评判标准
    • API:f1_score
  • AUC曲线
    • AUC是一个模型评价指标,只能用于二分类模型的评价。该评价指标通常应用的比较多!
    • 应用的比较多是原因是因为很多的机器学习的分类模型计算结果都是概率的形式(比如逻辑回归),那么对于概率而言,我们就需要去设定一个阈值来判定分类,那么这个阈值的设定就会对我们的正确率和准确率造成一定程度的影响。
      • 逻辑回归的默认阈值为0.5
    • AUC(Area under Curve),表面上意思是曲线下边的面积,这么这条曲线是什么?
      • ROC曲线(receiver operating characteristic curve,接收者操作特征曲线)
        在这里插入图片描述
    • 在理想情况下,最佳的分类器应该尽可能地处于左上角,这就意味着分类器在伪反例率(预测错的概率)很低的同时获得了很高的真正例率(预测对的概率)。也就是说ROC曲线围起来的面积越大越好,因为ROC曲线面积越大,则曲线上面的面积越小,则分类器越能停留在ROC曲线的左上角。
    • AUC的的取值是固定在0-1之间。AUC的值越大越好。
    • AUC的API
    • from sklearn.metrics import roc_auc_score
      y_pre = predict_proba(x_test)返回预测的概率
      auc=roc_auc_score(y_test,y_pre[:,1])
from sklearn.linear_model import LogisticRegression
import sklearn.datasets as datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, f1_score, recall_score, roc_auc_score

iris = datasets.load_iris()
feature = iris.data
target = iris.target
x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.2, random_state=2020)
lr = LogisticRegression()
lr.fit(x_train, y_train)
# 准确率
print(lr.score(x_test, y_test))
y_pred = lr.predict(x_test)
# 精准率
print(accuracy_score(y_test, y_pred))
# 召回率
# 参数average='binary'为默认值,只针对二分类,需要作修改
print(recall_score(y_test, y_pred, average='macro'))
# f1-Score
# 参数average='binary'为默认值,只针对二分类,需要作修改
print(f1_score(y_test, y_pred, average='macro'))
# AUC 只能用于二分类
# 参数y_score是分到某一类别的概率
print(lr.predict_proba(x_test)[:, 1])
# print(roc_auc_score(y_test, lr.predict_proba(x_test)[:1])) 这里报错是因为我们把AUC作用于了多分类
  • 决策树
    • 认识决策树
      • 决策树(Decision Tree)是一种有监督学习方法,它能够从一系列有特征和标签的数据中总结出决策(基于分类或者回归)规则,并用树状图的结构来呈现这些规则,以解决分类和回归问题。决策树算法容易理解,适用各种数据,在解决各种问题时都有良好表现,尤其是以树模型为核心的各种集成算法(集成学习的核心操作),在各个行业领域都有广泛的应用。
      • 我们来简单了解一下决策树是如何工作的。决策树算法的本质是一种树结构,我们只需要问一系列问题就可以对数据进行分类了。比如说,来看看下面的一组数据集,这是一些列已知物种以及所属类别的数据:
        在这里插入图片描述
    • 在这个决策过程中,我们一直在对记录的特征进行提问。最初的问题所在的地方叫做根节点,在得到结论前的每一个问题都是中间节点,而得到的每一个结论(动物的类别)都叫做叶子节点。
  • 节点
    • 根节点:没有进边,有出边。包含最初的,针对特征的提问。
    • 中间节点:既有进边也有出边,进边只有一条,出边可以有很多条。都是针对特征的提问。
    • 叶子节点:有进边,没有出边,每个叶子节点都是一个类别标签。
    • 子节点和父节点:在两个相连的节点中,更接近根节点的是父节点,另一个是子节点。
    • 相亲案例
      在这里插入图片描述

在这里插入图片描述

  • 上述案例中就可以将被相亲对象预测为见或者不见。上述分叉的结构就是决策树,如果一个决策树建立好之后,就可以基于该树的相关分支得到不同分类的预测结果。
  • 问题:为什么上述决策树中不把收入(特征)作为判断的第一步(作为树的根节点)呢?
    • 树的根节点和中间节点对应的是某一种类型特征,那么这些节点(特征)自上而下的重要性应该是由大到小进行排列的。
    • 因为我们普遍的认为年龄的重要性要大于收入!当然树中的这些节点具有怎样的重要性都是我们根据以往的经验来设定的,其实这样是不精准的,因为经验会有误差,不严谨。那么决策树模型肯定是根据有理有据的科学的方法进行重要性的设定的。那么如何科学设定呢?首先我们得先知道决策树算法主要解决的问题是什么!
  • 决策树算法的核心是要解决两个问题:
    • 1.如何从数据表中找出最佳节点或最佳分枝?
    • 2.如何让决策树停止生长,防止过拟合?
      • 决策树是基于训练集数据构建出来的,如果树长的越大分支越细致,则对训练数据的描述越清楚,但是不一定会很好的作用到测试数据中。
    • 几乎所有决策树有关的模型调整方法,都围绕这两个问题展开。接下来,我们就来了解一下决策树背后的原理。
  • 构建决策树
    • 接下来讨论如何根据已有的数据集来建立有效的决策树。原则上讲,任意一个数据集上的所有特征都可以被拿来分枝,特征上的任意节点又可以自由组合,所以一个数据集上可以发展出非常非常多棵决策树,其数量可达指数级。在这些树中,总有那么一棵树比其他的树分类效力都好,那样的树叫做”全局最优树“。
      • 全局最优:经过某种组合形成的决策树,整体来说分类效果为最好的模型
      • 局部最优:每一次分枝的时候都向着更好的分类效果分枝,但无法确认如此生成的树在全局上是否是最优的
    • 要在这么多棵决策树中去一次性找到分类效果最佳的那一棵是不可能的,如果通过排列组合来进行筛选,计算量过于大而且低效,因此我们不会这样做。相对的,机器学习研究者们开发了一些有效的算法,能够在合理的时间内构造出具有一定准确率的次最优决策树。
    • 这些算法基本都执行”贪心策略“,即通过局部的最优来达到我们相信是最接近全局最优(次最优决策树)的结果。
    • 贪心算法
      • 通过实现局部最优来达到接近全局最优结果的算法,所有的树模型都是这样的算法。
  • ID3算法构建决策树
    • 最典型的决策树算法是Hunt算法,该算法是由Hunt等人提出的最早的决策树算法。现代,Hunt算法是许多决策树算法的基础,包括ID3、C4.5和CART等。此处以应用较广、理论基础较为完善的ID3算法的基本原理开始,讨论如何利用局部最优化方法来创建决策模型。
    • ID3算法原型见于J.R Quinlan的博士论文,是基础理论较为完善,使用较为广泛的决策树模型算法,在此基础上J.R Quinlan进行优化后,陆续推出了C4.5决策树算法,后者现已称为当前最流行的决策树算法,我们先从ID3 开始讲起,再讨论如何从ID3逐渐优化至C4.5。
    • 为了要将数据集转化为一棵树,决策树需要找出最佳节点和最佳的分枝方法,而衡量这个“最佳”的指标叫做“不纯度”。 不纯度基于叶子节点来计算的,所以树中的每个节点都会有一个不纯度,并且子节点的不纯度一定是低于父节点的, 也就是说,在同一棵决策树上,叶子节点的不纯度一定是最低的。
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值