33 机器学习(一):特征工程

文章目录

  • 机器学习需要用到的包的介绍
    • 机器学习做什么
    • 深度学习做什么
    • 常见的概念
  • 数据介绍
    • 数据类型
    • 数据的来源
    • 数据的构成
  • 特征工程
    • one-hot编码
    • 接口集
    • 特征提取
      • DictVectorizer
      • CountVectorizer
      • TfidfVectorizer
    • 特征处理
      • 归一化 MinMaxScaler
      • 标准化 StandardScaler
      • 标准化和归一化的对比
      • 空值处理 SimpleImputer
    • 特征选择
      • 过滤(方差阈值) VarianceThreshold
    • 降维
  • 凑字数

机器学习需要用到的包的介绍

pip3 install scikit-learn

下载之后会下载下面的两个包:
在这里插入图片描述

  1. 第一个包scikit-learn是用来做机器学习的包。
  2. 第二个包是用来表示稀疏矩阵的。

相对应的官网:链接

我们按照sklearn这个包大概介绍一下机器学习的流程步骤,不太完整,但胜在可以稍微理解一下流程,如果有想法了解全流程的读者,可以在看完之后去啃一下西瓜书

机器学习做什么

我们在sklearn的官网上可以看到:
33 机器学习(一):特征工程_第1张图片
他做的大概就是上面的这些东西。

深度学习做什么

分类
回归
先略讲,到时候自然明白

常见的概念

  1. 10-折交叉验证是一种常用的交叉验证方法,用于评估机器学习模型的性能 它将数据集分成10份,轮流将其中9份作为训练数据,1份作为测试数据,进行试验1. 每次试验都会得出相应的正确率 10次的结果的正确率的平均值作为对算法精度的估计,一般还需要进行多次10折交叉验证(例如10次10折交叉验证),再求其均值,作为对算法准确性的估计。
  2. 监督式学习和非监督式学习是机器学习中的两种技术. 监督式学习是一种目的明确的训练方式,需要给数据打标签,以便训练模型。监督式学习可以分为回归分析和分类. 非监督式学习则是没有明确目的的训练方式,不需要给数据打标签. 非监督式学习可以用于聚类和关联问题.
  3. 参数和超参数:参数是模型可以根据数据自动学习出的变量,例如神经网络的权重和偏置。而超参数是模型外部的配置,需要手动设置,例如学习率、迭代次数、层数、每层神经元的个数等等,超参数和参数的区别在于,超参数是模型外部的配置,需要手动设置,而参数是模型内部的配置,需要用数据估计超参数不需要数据来驱动调整,而参数通常是有数据来驱动调整,在深度学习中,超参数的选择对模型的性能有很大影响。因此,通常需要通过试验和交叉验证来选择最佳的超参数。

数据介绍

数据类型

  1. 离散型数据:由记录不同个体得到的数据,比如点评数,点赞数等等,又称计数数据,这些数据一般都是整数,但是不能再细分,也没办法提升他们的精确度。
  2. 连续型数据:变量可以在某个范围内取任一数,即变量的取值可以是连续的,如:长度、时间、质量值等,这类整数通常是非整数,含有小数部分。

注:只要记住一点,离散型是区间内不可分,连续型是区间内可分

数据的来源

这边列三个吧:

Kaggle网址: https://www.kaggle.com/datasets
uci数据集网址: http://archive.ics.uci.edu/ml/
scikit-learn网址: https://scikit-learn.org/stable/datasets

数据的构成

这就先涉及到监督学习 和 无监督学习,二者的概念的差别就是数据的构成,对于监督学习就是传入的数据是包含着label的,也就是包含着输入x我们希望他得到y,而无监督学习的数据构成就全是数据。

所以数据的构成:特征值+目标值(当然如果目标值不存在那么也就是无监督学习)

特征工程

特征工程是将原始数据转换为更好地代表预测模型的潜在问题的特征的过程,从而提高了模型对未知数据预测的准确性。

讲人话就是特征工程干的就是数据预处理的过程。

one-hot编码

经过机器学习的长期实验,我们在很早之前就采用one-hot编码进行代替标签,当然(没有本文写的那么简单,之后讲word2vec的时候会在进行讲解,或者读者可以自行去看看transformer的序列的东西)

我们先理解one-hot的思想:

例如,假设我们有一个分类变量“颜色”,它有三个可能的值:红、绿和蓝。我们可以使用one-hot编码将这些值转换为三个二进制变量:红色=(1,0,0),绿色=(0,1,0),蓝色=(0,0,1)这样,我们就可以在机器学习算法中使用这些值,比如说小明此时输入是(0,0,1),代表的就是他选择的是蓝色。

接口集

特征提取的部分:

sklearn.feature_extraction

特征提取

DictVectorizer

from sklearn.feature_extraction import DictVectorizer

# 准备数据
dict_data = [{'city': '北京', 'temperature': 100},
             {'city': '上海', 'temperature': 80}]

# 创建字典的向量器 默认sparse = True 也就是按照稀疏矩阵进行存储
Dic_vector1 = DictVectorizer(sparse=False)
Dic_vector2 = DictVectorizer()

# 得到数据
Dic_data1 = Dic_vector1.fit_transform(dict_data)
Dic_data2 = Dic_vector2.fit_transform(dict_data)
print(Dic_data1,end='\n\n')
print(Dic_data2)
print('-'*20)

# 这边看看其他通用的接口 
# 查看矩阵每一列储存的是什么特征  这边需要理解one-hot
print(Dic_vector1.get_feature_names_out(),end='\n\n')

# 将矩阵反转成特征 用什么向量器得到的数据 就用什么向量器反转
print(Dic_vector1.inverse_transform(Dic_data1))

输出:

[[  0.   1. 100.]
 [  1.   0.  80.]]

  (0, 1)	1.0
  (0, 2)	100.0
  (1, 0)	1.0
  (1, 2)	80.0
--------------------
['city=上海' 'city=北京' 'temperature']

[{'city=北京': 1.0, 'temperature': 100.0}, {'city=上海': 1.0, 'temperature': 80.0}]

注意:如果sparse=true的话存储按照的是稀疏矩阵的方式进行存储。

CountVectorizer

英文的分词:

from sklearn.feature_extraction.text import CountVectorizer

# max_df, min_df整数:指每个词的所有文档词频数不小于最小值,出现该词的文档数目小于等于max_df
# max_df, min_df小数:每个词的次数/所有文档数量
input_data = ["life is  short,i like python life",
              "life is too long,i dislike python",
              "life is short"]
vector1 = CountVectorizer()
vector2 = CountVectorizer(min_df=2) #这个词的词频需要到达2个即以上才会被记录
vector3 = CountVectorizer(min_df=0.2) #这个词的词频占每个词的0.2以上才会被记录

# 调用fit_transform输入并转换数据
data1 = vector1.fit_transform(input_data)
data2 = vector2.fit_transform(input_data)
data3 = vector3.fit_transform(input_data)

# 一组一组来看吧 get_feature_names_out和toarray进行查看可以看出来我们对于i是没有进行统计的,原因就是在我们看来单个字符,属于干扰词频,比如我们中文中的得,就对于文本分析一点没用,故会去掉。
print(data1)
print(vector1.get_feature_names_out())
print(data1.toarray())
print(vector1.inverse_transform(data1))
print('-'*20)

# 可以看出统计的词频是根据所有出现的词语进行统计的
print(data2)
print(vector2.get_feature_names_out())
print(data2.toarray())
print(vector2.inverse_transform(data2))
print('-'*20)

print(data3)
print(vector3.get_feature_names_out())
print(data3.toarray())
print(vector3.inverse_transform(data3))

输出:

  (0, 2)	2
  (0, 1)	1
  (0, 6)	1
  (0, 3)	1
  (0, 5)	1
  (1, 2)	1
  (1, 1)	1
  (1, 5)	1
  (1, 7)	1
  (1, 4)	1
  (1, 0)	1
  (2, 2)	1
  (2, 1)	1
  (2, 6)	1
['dislike' 'is' 'life' 'like' 'long' 'python' 'short' 'too']
[[0 1 2 1 0 1 1 0]
 [1 1 1 0 1 1 0 1]
 [0 1 1 0 0 0 1 0]]
[array(['life', 'is', 'short', 'like', 'python'], dtype='), array(['life', 'is', 'python', 'too', 'long', 'dislike'], dtype='), array(['life', 'is', 'short'], dtype=')]
--------------------
  (0, 1)	2
  (0, 0)	1
  (0, 3)	1
  (0, 2)	1
  (1, 1)	1
  (1, 0)	1
  (1, 2)	1
  (2, 1)	1
  (2, 0)	1
  (2, 3)	1
['is' 'life' 'python' 'short']
[[1 2 1 1]
 [1 1 1 0]
 [1 1 0 1]]
[array(['life', 'is', 'short', 'python'], dtype='), array(['life', 'is', 'python'], dtype='), array(['life', 'is', 'short'], dtype=')]
--------------------
  (0, 2)	2
  (0, 1)	1
  (0, 6)	1
  (0, 3)	1
  (0, 5)	1
  (1, 2)	1
  (1, 1)	1
  (1, 5)	1
  (1, 7)	1
  (1, 4)	1
  (1, 0)	1
  (2, 2)	1
  (2, 1)	1
  (2, 6)	1
['dislike' 'is' 'life' 'like' 'long' 'python' 'short' 'too']
[[0 1 2 1 0 1 1 0]
 [1 1 1 0 1 1 0 1]
 [0 1 1 0 0 0 1 0]]
[array(['life', 'is', 'short', 'like', 'python'], dtype='), array(['life', 'is', 'python', 'too', 'long', 'dislike'], dtype='), array(['life', 'is', 'short'], dtype=')]

中文的分词:

# 但是此时我们会发现我们统计的词频实际上是跟着空格来的,也就是他并不存在一个很好的机制来分词中文
# 相同的对于一个汉字并不统计

# 使用jieba进行分词
input_data1 = jieba.cut("今天很残酷,明天更残酷,后天很美好,但绝对大部分是死在明天晚上,所以每个人不要放弃今天。")
input_data2 = jieba.cut("我们看到的从很远星系来的光是在几百万年之前发出的,这样当我们看到宇宙时,我们是在看它的过去。")
input_data3 = jieba.cut("如果只用一种方式了解某样事物,你就不会真正了解它。了解事物真正含义的秘密取决于如何将其与我们所了解的事物相联系。")

# 对于jieba分词后的数据不能直接print 需要对其list化后才是一系列的文字数据
print(list(input_data1))
print('-'*20)

# 赋值加上带空格的中文
data1 = ' '.join(list(input_data1))
data2 = ' '.join(list(input_data2))
data3 = ' '.join(list(input_data3))

vector = CountVectorizer()
result = vector.fit_transform([data1, data2, data3])
print(vector.get_feature_names_out())
print(result.toarray())

# 这时候你会发现为什么我们输入的第一句的词语全都不见了,而且我们切开的词语不是也有非1个字的吗?原因就是这个函数内部自定义了一个停用表。

输出:

['今天', '很', '残酷', ',', '明天', '更', '残酷', ',', '后天', '很', '美好', ',', '但', '绝对', '大部分', '是', '死', '在', '明天', '晚上', ',', '所以', '每个', '人', '不要', '放弃', '今天', '。']
--------------------
['一种' '不会' '之前' '了解' '事物' '光是在' '几百万年' '发出' '取决于' '只用' '含义' '如何' '如果' '宇宙'
 '我们' '方式' '星系' '某样' '看到' '真正' '秘密' '联系' '过去' '这样']
[[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 1 0 0 1 1 1 0 0 0 0 0 1 3 0 1 0 2 0 0 0 1 1]
 [1 1 0 4 3 0 0 0 1 1 1 1 1 0 1 1 0 1 0 2 1 1 0 0]]

TfidfVectorizer

TF-IDF的主要思想是:如果某个词或短语在一篇文章中出现的概率高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。

计算的方式是log10(这个词在此篇文章中出现的次数/在所有文章中出现的次数)

from sklearn.feature_extraction.text import TfidfVectorizer

# tfidf 算的是比率,我们可以看到上面出现的分词的出现的是词频,而我们使用tdidf来计算词频
input_data1 = jieba.cut("今天很残酷,明天更残酷,后天很美好,但绝对大部分是死在明天晚上,所以每个人不要放弃今天。")
input_data2 = jieba.cut("我们看到的从很远星系来的光是在几百万年之前发出的,这样当我们看到宇宙时,我们是在看它的过去。")
input_data3 = jieba.cut("如果只用一种方式了解某样事物,你就不会真正了解它。了解事物真正含义的秘密取决于如何将其与我们所了解的事物相联系。")
data1 = ' '.join(list(input_data1))
data2 = ' '.join(list(input_data2))
data3 = ' '.join(list(input_data3))

vector = TfidfVectorizer()
result = vector.fit_transform([data1,data2,data3])

print(vector.get_feature_names_out())
print(result.toarray())

输出:

['一种' '不会' '不要' '之前' '了解' '事物' '今天' '光是在' '几百万年' '发出' '取决于' '只用' '后天' '含义'
 '大部分' '如何' '如果' '宇宙' '我们' '所以' '放弃' '方式' '明天' '星系' '晚上' '某样' '残酷' '每个'
 '看到' '真正' '秘密' '绝对' '美好' '联系' '过去' '这样']
[[0.         0.         0.21821789 0.         0.         0.
  0.43643578 0.         0.         0.         0.         0.
  0.21821789 0.         0.21821789 0.         0.         0.
  0.         0.21821789 0.21821789 0.         0.43643578 0.
  0.21821789 0.         0.43643578 0.21821789 0.         0.
  0.         0.21821789 0.21821789 0.         0.         0.        ]
 [0.         0.         0.         0.2410822  0.         0.
  0.         0.2410822  0.2410822  0.2410822  0.         0.
  0.         0.         0.         0.         0.         0.2410822
  0.55004769 0.         0.         0.         0.         0.2410822
  0.         0.         0.         0.         0.48216441 0.
  0.         0.         0.         0.         0.2410822  0.2410822 ]
 [0.15698297 0.15698297 0.         0.         0.62793188 0.47094891
  0.         0.         0.         0.         0.15698297 0.15698297
  0.         0.15698297 0.         0.15698297 0.15698297 0.
  0.1193896  0.         0.         0.15698297 0.         0.
  0.         0.15698297 0.         0.         0.         0.31396594
  0.15698297 0.         0.         0.15698297 0.         0.        ]]

特征处理

归一化 MinMaxScaler

在这里插入图片描述
归一化后就是经过左边的式子之后会让原本的数落到【0,1】之间,但如果想要让这个数落到(-3,-1)之间呢?实际上也是遵守放缩的原则,就直接在归一化后得到的数据 ×(-2) -1,但这个接口代码都帮我们实现了。

# 归一化容易受极值的影响 举个例子:比如我们要和马云进行财产归一化然后给到机器进行训练,那我们自己的财产相对于马云不值一提,就全是0,那也就是说输入的全是0给机器训练,只有马云自己有值是1,那效果肯定很不好
from sklearn.preprocessing import MinMaxScaler

# 默认归一化到0-1之间,这个需要根据合适的任务进行适当的调整
max_min = MinMaxScaler(feature_range=(0, 2))

data = max_min.fit_transform([[90, 2, 10, 40], 
                              [60, 4, 15, 45], 
                              [75, 3, 13, 46]])
print(data)

输出:

[[1.         0.         0.         0.        ]
 [0.         1.         1.         0.83333333]
 [0.5        0.5        0.6        1.        ]]

标准化 StandardScaler

通过对原始数据进行变换把数据变换到均值为0,标准差为1范围内
33 机器学习(一):特征工程_第2张图片

# 标准化缩放,不是标准正太分布,只均值为0,方差为1的分布
from sklearn.preprocessing import StandardScaler

std = StandardScaler()
data = std.fit_transform([[1., -1., 3.], [2., 4., 2.], [4., 6., -1.]])
print(data)
print('-'*20)

# mean 和 var 输出不是0 和 1 原因就是这个计算的是原本数组的均值和方差var是方差 std是标准差
print(std.mean_,end='\n\n')
print(std.var_,end='\n\n')
print(std.n_samples_seen_)  # 样本数

输出:

[[-1.06904497 -1.35873244  0.98058068]
 [-0.26726124  0.33968311  0.39223227]
 [ 1.33630621  1.01904933 -1.37281295]]
--------------------
[2.33333333 3.         1.33333333]

[1.55555556 8.66666667 2.88888889]

3
3

标准化和归一化的对比

对于归一化来说:如果出现异常点,影响了最大值和最小值,那么结果显然会发生改变,对于标准化来说:如果出现异常点,由于具有一定数据量,少量的异常点对于平均值的影响并不大,从而方差改变较小,但是对于归一化来说他天然的支持快速运算梯度下降。

空值处理 SimpleImputer

替换下可以填写的参数:

  1. mean 平均值
  2. median 中值
  3. most_frequent 出现最多的值
  4. constant 搭配着fill_value参数进行一起使用,即采用默认值
# 空值处理一般分为两种 替换 或 删除 删除的化直接使用np的dropna 或者 pd的dropna即可
from sklearn.impute import SimpleImputer

# NaN, nan,缺失值必须是这种形式
im = SimpleImputer(missing_values=np.nan, strategy='median')
data = im.fit_transform([[1, 2], [np.nan, 3], [7, 6], [3, 2]])
print(data)

输出:

[[1. 2.]
 [3. 3.]
 [7. 6.]
 [3. 2.]]

特征选择

首先比较差的特征分为冗余特征和噪声特征,前者举个例子就是我们需要机器分辨出是猫还是狗这两个类别,然后我们取了两个特征进行分析,第一个特征是否有爪子,第二个特征爪子长度,那么是否有爪子和爪子长度二者之间已经互相成为了冗余特征,他们有点包含关系,而噪声特征就是分辨他们俩,我举的是是否有羽毛,这种类型的造成负面影响的特征,为了避免有这种数据,我们除了手动删除特征,还有几个需要知道的删除:

主要方法(三大武器):

  1. Filter(过滤式):VarianceThreshold
  2. Embedded(嵌入式):正则化、决策树(之后再进行介绍)
  3. Wrapper(包裹式):多次训练,不断取子集,最后得到最好的特征,效果虽好,但是耗时耗力(有时候可以作为优点)。

过滤(方差阈值) VarianceThreshold

# 方差过滤
from sklearn.feature_selection import VarianceThreshold

#默认只删除方差为0,threshold是方差阈值,删除方差这个值小的那些特征,默认是0
var = VarianceThreshold(threshold=1)

data = var.fit_transform([[0, 2, 0, 3],
                          [0, 1, 4, 3],
                          [0, 1, 1, 3]])

print(data)

# 获得剩余的特征的列编号
print('The surport is %s' % var.get_support(True))

输出:

[[0]
 [4]
 [1]]
The surport is [2]

降维

本质:是一种分析、简化数据集的技术
目的:是数据维数压缩,尽可能降低原数据的维数(复杂度),损失少量信息,让计算机运算的快一些,将原本的多维的特征降成比较低维度的特征。
作用:可以削减回归分析或者聚类分析中特征的数量

实际上的算法的理解,这边只给出大概的说法,不进行详细探究,以后再说,我们可以将多维的特征,当成多维空间中的一个点,然后对其做到对应低一维度的垂直距离,让所有的这些垂直距离最短的一个低一维度的空间就是我们降一维的空间,然后多次运行即可得到任意维度的降维。

# 特征降维

from sklearn.decomposition import PCA
# n_components:小数 0~1 代表就是降成原本的多少维度一般都是取0.9-0.95 然后计算机会计算出到底要降成多少维度 如果是整数,即直接降成对应的维度

pca = PCA(n_components=0.9)
data = pca.fit_transform([[2, 8, 4, 5], [6, 3, 0, 8], [5, 4, 9, 1]])
print(data)

输出:

[[ 1.28620952e-15  3.82970843e+00]
 [ 5.74456265e+00 -1.91485422e+00]
 [-5.74456265e+00 -1.91485422e+00]]

相同的降维还有LDA,这边就不进行介绍,此系列主要是一个流程向的介绍,而不是多的介绍。

凑字数

最后凑到1w字吧,这边讲解一下机器学习或者深度学习到底在做什么,拿回归为例子:比如说我们先有10个的特征值(学历 工作 是否有房有车)+目标值(女生是否喜欢)的一串数据,我们把这些数据拿给计算机,让他进行训练,并把目标值也告诉他,最后这个模型训练好了,我们再拿一组特征值输入进去,让机器帮忙判断女生是否喜欢,得到这样子一个数。

而具体什么是监督学习什么是非监督学习呢?上面说了,但下面为了氵,就再讲一点,就是一个是否存在已经提前定好的目标值,比如对于一些点进行分类,监督学习就是我们已经给出了具体的类,然后让机器判断这些别的东西怎么分类进去,而无监督学习则是,我们没说,机器自己去分类,最后分成多少类,分成什么样子都没有办法提前知道。

然后关于分类和回归,很多人也不理解,这边也给出一个相关的区别,对于分类来说,他的值大多数都是离散的,而对于回归来说他的值大多都是连续的。

举一些例子:
监督学习

  1. 分类: k-近邻算法、贝叶斯分类、决策树与随机森林、逻辑回归、神经网络
  2. 回归:线性回归、岭回归
  3. 标注:隐马尔可夫模型·

无监督学习

  1. 聚类: k-means

你可能感兴趣的:(个人python流程学习,机器学习,人工智能)