机器学习一(学习笔记) 数据的特征抽取及预处理

Scikit-learn

安装Scikit-learn

pip3 install Scikit-learn
pip3 install scipy

一、数据的特征抽取

现实世界中多数特征都不是连续变量,比如分类、文字、图像等,为了对非连续变量做特征表述,需要对这些特征做数学化表述,因此就用到了特征提取. sklearn.feature_extraction提供了特征提取的很多方法

1、字典数据抽取

我们将城市和环境作为字典数据,来进行特征的提取。

sklearn.feature_extraction.DictVectorizer(sparse = True)

将映射列表转换为Numpy数组或scipy.sparse矩阵

  • sparse 是否转换为scipy.sparse矩阵表示,默认开启

方法

fit_transform(X,y)

应用并转化映射列表X,y为目标类型

inverse_transform(X[, dict_type])

将Numpy数组或scipy.sparse矩阵转换为映射列表

from sklearn.feature_extraction import DictVectorizer

dict = DictVectorizer(sparse=False)

# 调用fit_transform
data = dict.fit_transform([{'city': '北京','temperature': 100}, {'city': '上海','temperature':60}, {' ': '深圳','temperature': 30}])

print(dict.get_feature_names())

print(dict.inverse_transform(data))

print(data)
'''
['city=上海', 'city=北京', 'city=深圳', 'temperature']
[{'city=北京': 1.0, 'temperature': 100.0}, {'city=上海': 1.0, 'temperature': 60.0}, {'city=深圳': 1.0, 'temperature': 30.0}]
[[  0.   1.   0. 100.]
 [  1.   0.   0.  60.]
 [  0.   0.   1.  30.]]
'''

2、文本特征提取

只限于英文,中文需要先进性分词处理

文本的特征提取应用于很多方面,比如说文档分类、垃圾邮件分类和新闻分类。那么文本分类是通过词是否存在、以及词的概率(重要性)来表示。

(1)文档的中词的出现

数值为1表示词表中的这个词出现,为0表示未出现

sklearn.feature_extraction.text.CountVectorizer()

将文本文档的集合转换为计数矩阵(scipy.sparse matrices)

方法

fit_transform(raw_documents,y)

学习词汇词典并返回词汇文档矩阵

from sklearn.feature_extraction.text import CountVectorizer
#抽取英文文本数据
content = ["life is short,i like python","life is too long,i dislike python"]

cv = CountVectorizer()
#进行抽取
data=  cv.fit_transform(content)
#打印数据,需要toarray()方法转变为numpy的数组形式
print(cv.get_feature_names())
print(data.toarray())

'''
['dislike', 'is', 'life', 'like', 'long', 'python', 'short', 'too']
[[0 1 1 1 0 1 1 0]
 [1 1 1 0 1 1 0 1]]
'''

中文案列

from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer()
data = cv.fit_transform(["人生 苦短,我 喜欢 python", "人生漫长,不用 python"])
#获取特征名称
print(cv.get_feature_names())
#打印特征化之后的数据,需要toarray()方法转变为numpy的数组形式
print(data.toarray())

'''
['python', '不用', '人生', '人生漫长', '喜欢', '苦短']
[[1 0 1 0 1 1]
 [1 1 0 1 0 0]]
'''

温馨提示:每个文档中的词,只是整个语料库中所有词,的很小的一部分,这样造成特征向量的稀疏性(很多值为0)为了解决存储和运算速度的问题,使用Python的scipy.sparse矩阵结构

3、TF-IDF表示词的重要性

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

TfidfVectorizer会根据指定的公式将文档中的词转换为概率表示。(朴素贝叶斯介绍详细的用法)

class sklearn.feature_extraction.text.TfidfVectorizer()

方法

fit_transform(raw_documents,y)

学习词汇和idf,返回术语文档矩阵。

#!/usr/bin/python
# -*- coding: UTF-8 -*-

from sklearn.feature_extraction.text import CountVectorizer
#抽取英文文本数据
cv = CountVectorizer()

from sklearn.feature_extraction.text import TfidfVectorizer
content = ["life is short,i like python","life is too long,i dislike python"]
tf = TfidfVectorizer(stop_words='english')
#处理文本数据
data = tf.fit_transform(content)
#进行抽取
data=  cv.fit_transform(content)
#打印数据
print(cv.get_feature_names())
print(data.toarray())
print(tf.vocabulary_)

'''
['dislike', 'is', 'life', 'like', 'long', 'python', 'short', 'too']
[[0.         0.40993715 0.57615236 0.         0.40993715 0.57615236]
 [0.57615236 0.40993715 0.         0.57615236 0.40993715 0.        ]]
{'life': 1, 'short': 5, 'like': 2, 'python': 4, 'long': 3, 'dislike': 0}
'''

二、数据的特征预处理

1、单个特征数据

1、特征归一化

归一化首先在特征(维度)非常多的时候,可以防止某一维或某几维对数据影响过大,也是为了把不同来源的数据统一到一个参考区间下,这样比较起来才有意义,其次可以程序可以运行更快。 例如:一个人的身高和体重两个特征,假如体重50kg,身高175cm,由于两个单位不一样,数值大小不一样。如果比较两个人的体型差距时,那么身高的影响结果会比较大,k-临近算法会有这个距离公式。

min-max方法

常用的方法是通过对原始数据进行线性变换把数据映射到[0,1]之间,变换的函数为:

Min-Max Scaling又称为Min-Max normalization, 特征量化的公式为:
 特征量化的公式

量化后的特征将分布在区间[0,1]之间。

其中min是样本中最小值,max是样本中最大值,注意在数据流场景下最大值最小值是变化的,另外,最大值与最小值非常容易受异常点影响,所以这种方法鲁棒性较差,只适合传统精确小数据场景。(鲁棒是Robust的音译,也就是健壮和强壮的意思。它也是在异常和危险情况下系统生存的能力

  • min-max自定义处理

这里我们使用相亲约会对象数据在MatchData.txt,这个样本时男士的数据,三个特征:1、玩游戏所消耗时间的百分比;2、每年获得的飞行常客里程数;3、每周消费的冰淇淋公升数。然后有一个所属类别,被女士评价的三个类别,不喜欢、魅力一般、极具魅力。 首先导入数据进行矩阵转换处理

import numpy as np
from sklearn.preprocessing import MinMaxScaler, StandardScaler, Imputer

mm = MinMaxScaler(feature_range=(2, 3))

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

print(data)

输出结果为

[[3.         2.         2.         2.        ]
 [2.         3.         3.         2.83333333]
 [2.5        2.5        2.6        3.        ]]

我们查看数据集会发现,有的数值大到几万,有的才个位数,同样如果计算两个样本之间的距离时,其中一个影响会特别大。也就是说飞行里程数对于结算结果或者说相亲结果影响较大,但是统计的人觉得这三个特征同等重要,所以需要将数据进行这样的处理。

这样每个特征任意的范围将变成[0,1]的区间内的值,或者也可以根据需求处理到[-1,1]之间,我们再定义一个函数,进行这样的转换。

2、标准化

标准化数据通过减去均值然后除以方差(或标准差),这种数据标准化方法经过处理后数据符合标准正态分布,即均值为0,标准差为1,转化函数为:

x =(x - 𝜇)/𝜎

适用于:如果数据的分布本身就服从正态分布,就可以用这个方法。
通常这种方法基本可用于有outlier(异常值;极端值;离群值)的情况,但是,在计算方差和均值的时候outliers仍然会影响计算。所以,在出现outliers的情况下可能会出现转换后的数的不同feature分布完全不同的情况。
其中μ是样本的均值,σ是样本的标准差,它们可以通过现有的样本进行估计,在已有的样本足够多的情况下比较稳定,适合嘈杂的数据场景
参考文章:归一化和标准化的一些理解

sklearn中提供了StandardScalar类实现列标准化:

from sklearn.preprocessing import MinMaxScaler, StandardScaler, Imputer
std = StandardScaler()
data = std.fit_transform([[ 1., -1., 3.],[ 2., 4., 2.],[ 4., 6., -1.]])
print(data)
‘’‘
[[-1.06904497 -1.35873244  0.98058068]
 [-0.26726124  0.33968311  0.39223227]
 [ 1.33630621  1.01904933 -1.37281295]]
‘’’

3、缺失值处理

由于各种原因,许多现实世界的数据集包含缺少的值,通常编码为空白,NaN或其他占位符。然而,这样的数据集与scikit的分类器不兼容,它们假设数组中的所有值都是数字,并且都具有和保持含义。使用不完整数据集的基本策略是丢弃包含缺失值的整个行和/或列。然而,这是以丢失可能是有价值的数据(即使不完整)的代价。更好的策略是估算缺失值,即从已知部分的数据中推断它们。

(1)填充缺失值 使用sklearn.preprocessing中的Imputer类进行数据的填充

#!/usr/bin/python
# -*- coding: UTF-8 -*-
from sklearn.preprocessing import Imputer
import numpy as np

im = Imputer(missing_values='NaN', strategy='mean', axis=0)
data = im.fit_transform([[1, 5], [np.nan, 3], [7, 6]])
print(data)
'''
[[1. 5.]
 [4. 3.]
 [7. 6.]]
'''
'''
class Imputer(sklearn.base.BaseEstimator, sklearn.base.TransformerMixin)
    """
    用于完成缺失值的补充

    :param param missing_values: integer or "NaN", optional (default="NaN")
        丢失值的占位符,对于编码为np.nan的缺失值,使用字符串值“NaN”

    :param strategy: string, optional (default="mean")
        插补策略
        如果是“平均值”,则使用沿轴的平均值替换缺失值
        如果为“中位数”,则使用沿轴的中位数替换缺失值
        如果“most_frequent”,则使用沿轴最频繁的值替换缺失

    :param axis: integer, optional (default=0)
        插补的轴
        如果axis = 0,则沿列排列
        如果axis = 1,则沿行排列
    """

3、异常值处理

异常值,即在数据集中存在不合理的值,又称离群点。
其他:待补充

2、多个特征

1、线性降维

1、删除低方差的特征

#!/usr/bin/python
# -*- coding: UTF-8 -*-
from sklearn.feature_selection import VarianceThreshold
var = VarianceThreshold(threshold=1.0)
data = var.fit_transform([
    [0, 2, 0, 3, 2],
    [0, 1, 4, 3, 2],
    [0, 1, 1, 3, 6]])
print(data)
'''
[[0 2]
 [4 2]
 [1 6]]
'''

2、主成分分析算法(PCA)

参考:如何通俗易懂地讲解什么是 PCA 主成分分析?
PCA算法理解及代码实现
Principal Component Analysis(PCA)是最常用的线性降维方法,它的目标是通过某种线性投影,将高维的数据映射到低维的空间中表示,并期望在所投影的维度上数据的方差最大,以此使用较少的数据维度,同时保留住较多的原数据点的特性。

通俗的理解,如果把所有的点都映射到一起,那么几乎所有的信息(如点和点之间的距离关系)都丢失了,而如果映射后方差尽可能的大,那么数据点则会 分散开来,以此来保留更多的信息。可以证明,PCA是丢失原始数据信息最少的一种线性降维方式。(实际上就是最接近原始数据,但是PCA并不试图去探索数 据内在结构)

设 n 维向量w为目标子空间的一个座标轴方向(称为映射向量),最大化数据映射后的方差,

四大机器学习降维算法:PCA、LDA、LLE、Laplacian Eigenmaps

其中 m 是数据实例的个数, xi是数据实例 i 的向量表达, x拔是所有数据实例的平均向量。定义W为包含所有映射向量为列向量的矩阵,经过线性代数变换,可以得到如下优化目标函数:

四大机器学习降维算法:PCA、LDA、LLE、Laplacian Eigenmaps

其中tr表示矩阵的迹, 四大机器学习降维算法:PCA、LDA、LLE、Laplacian Eigenmaps A是数据协方差矩阵。

容易得到最优的W是由数据协方差矩阵前 k 个最大的特征值对应的特征向量作为列向量构成的。这些特征向量形成一组正交基并且最好地保留了数据中的信息。

PCA的输出就是Y = W‘X,由X的原始维度降低到了k维。

PCA追求的是在降维之后能够最大化保持数据的内在信息,并通过衡量在投影方向上的数据方差的大小来衡量该方向的重要性。但是这样投影以后对数据 的区分作用并不大,反而可能使得数据点揉杂在一起无法区分。这也是PCA存在的最大一个问题,这导致使用PCA在很多情况下的分类效果并不好。具体可以看 下图所示,若使用PCA将数据点投影至一维空间上时,PCA会选择2轴,这使得原本很容易区分的两簇点被揉杂在一起变得无法区分;而这时若选择1轴将会得 到很好的区分结果。

四大机器学习降维算法:PCA、LDA、LLE、Laplacian Eigenmaps

PCA(Principal component analysis),主成分分析。特点是保存数据集中对方差影响最大的那些特征,PCA极其容易受到数据中特征范围影响,所以在运用PCA前一定要做特征标准化,这样才能保证每维度特征的重要性等同。

sklearn.decomposition.PCA

.PCA常用方法

  • fit(X): 用数据X来训练PCA模型。
  • fit_transform(X):用X来训练PCA模型,同时返回降维后的数据。
#参数:n_components:  
#意义:PCA算法中所要保留的主成分个数n,也即保留下来的特征个数n
#类型:int 或者 string,缺省时默认为None,所有成分被保留。
          赋值为int,比如n_components=1,将把原始数据降到一个维度。
          赋值为string,比如n_components='mle',将自动选取特征个数n,使得满足所要求的方差百分比。

通过一个例子来看

    #主成分分析进行特征降维
    :return: None
    """
    pca = PCA(n_components=0.9)

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

    print(data)

    return None

2、LDA

Linear Discriminant Analysis(也有叫做Fisher Linear Discriminant)是一种有监督的(supervised)线性降维算法。与PCA保持数据信息不同,LDA是为了使得降维后的数据点尽可能地容易被区分!

假设原始数据表示为X,(m*n矩阵,m是维度,n是sample的数量)

既然是线性的,那么就是希望找到映射向量a, 使得 a‘X后的数据点能够保持以下两种性质:

1、同类的数据点尽可能的接近(within class)

2、不同类的数据点尽可能的分开(between class)

所以呢还是上次PCA用的这张图,如果图中两堆点是两类的话,那么我们就希望他们能够投影到轴1去(PCA结果为轴2),这样在一维空间中也是很容易区分的。

四大机器学习降维算法:PCA、LDA、LLE、Laplacian Eigenmaps

接下来是推导,因为这里写公式很不方便,我就引用Deng Cai老师的一个ppt中的一小段图片了:

四大机器学习降维算法:PCA、LDA、LLE、Laplacian Eigenmaps

思路还是非常清楚的,目标函数就是最后一行J(a),μ(一飘)就是映射后的中心用来评估类间距,s(一瓢)就是映射后的点与中心的距离之和用来评估类内距。J(a)正好就是从上述两个性质演化出来的。

因此两类情况下:

加上a’a=1的条件(类似于PCA)

四大机器学习降维算法:PCA、LDA、LLE、Laplacian Eigenmaps

可以拓展成多类:

四大机器学习降维算法:PCA、LDA、LLE、Laplacian Eigenmaps

以上公式推导可以具体参考pattern classification书中的相应章节,讲fisher discirminant的

OK,计算映射向量a就是求最大特征向量,也可以是前几个最大特征向量组成矩阵A=[a1,a2,….ak]之后,就可以对新来的点进行降维了: y = A’X (线性的一个好处就是计算方便!)

可以发现,LDA最后也是转化成为一个求矩阵特征向量的问题,和PCA很像,事实上很多其他的算法也是归结于这一类,一般称之为谱(spectral)方法。

LinearDiscriminantAnalysis

#接口
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

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


三、jieba分词(附录)

jieba分词是python写成的一个算是工业界的分词开源库,其github地址为:https://github.com/fxsjy/jieba,在Python里的安装方式:

pip install jieba

简单示例:

import jieba as jb

seg_list = jb.cut("我来到北京清华大学", cut_all=True)
print("全模式: " + "/ ".join(seg_list))  # 全模式

seg_list = jb.cut("我来到北京清华大学", cut_all=False)
print("精确模式: " + "/ ".join(seg_list))  # 精确模式

seg_list = jb.cut("他来到了网易杭研大厦")  
print("默认模式: " + "/ ".join(seg_list)) # 默认是精确模式

seg_list = jb.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造")  
print("搜索引擎模式: " + "/ ".join(seg_list)) # 搜索引擎模式

执行结果:

全模式:/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学
精确模式:/ 来到/ 北京/ 清华大学
默认模式:/ 来到// 网易/ 杭研/ 大厦
搜索引擎模式: 小明/ 硕士/ 毕业// 中国/ 科学/ 学院/ 科学院/ 中国科学院/ 计算/ 计算所//// 日本/ 京都/ 大学/ 日本京都大学/ 深造

jieba分词的基本思路

jieba分词对已收录词和未收录词都有相应的算法进行处理,其处理的思路很简单,主要的处理思路如下:

  • 加载词典dict.txt
  • 从内存的词典中构建该句子的DAG(有向无环图)
  • 对于词典中未收录词,使用HMM模型的viterbi算法尝试分词处理
  • 已收录词和未收录词全部分词完毕后,使用dp寻找DAG的最大概率路径 输出分词结果

import jieba 
con1 = jieba.cut("今天很残酷,明天更残酷,后天很美好,但绝对大部分是死在明天晚上,所以每个人不要放弃今天。")

con2 = jieba.cut("我们看到的从很远星系来的光是在几百万年之前发出的,这样当我们看到宇宙时,我们是在看它的过去。")

con3 = jieba.cut("如果只用一种方式了解某样事物,你就不会真正了解它。了解事物真正含义的秘密取决于如何将其与我们所了解的事物相联系。")

# 转换成列表
content1 = list(con1)
content2 = list(con2)
content3 = list(con3)

# 吧列表转换成字符串
c1 = ' '.join(content1)
c2 = ' '.join(content2)
c3 = ' '.join(content3)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章