大部分上传的截图都挂了,大家见谅吧,也不影响学习的
什么是特征工程
书接上文,前文我们看到鸢尾花数据集张什么样子:
{
'data': array([[5.1, 3.5, 1.4, 0.2],
[4.9, 3. , 1.4, 0.2],
[4.7, 3.2, 1.3, 0.2],
[6.7, 3.3, 5.7, 2.5],
[6.7, 3. , 5.2, 2.3],
[6.3, 2.5, 5. , 1.9],
[6.5, 3. , 5.2, 2. ],
[6.2, 3.4, 5.4, 2.3],
[5.9, 3. , 5.1, 1.8]]),
'target': array([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, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]),
'target_names': array(['setosa', 'versicolor', 'virginica'], dtype='<U10'),
'DESCR': '.. ,
'feature_names': ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)'],
}
但是大家想到没有,数据我们统计到了就可以直接拿来做分析了吗?错,大错特错,任何东西,包括数据都不能不经过处理就直接使用。我们杀完猪采集到的猪肉可以直接吃吗?快递收上来就能直接发吗?自来水可以直接喝吗?
我们采集到的数据一样属于原生质,要经过加工处理的。这个加工处理基于什么?基于能磨平数据之间的差异,相互之间的影响,能更好的适应那些典型的知名的有效的机器学习算法,所以诞生了特征工程这个工序
特征工程是使用专业背景知识和机能处理数据,可以让特征在机器学习算法上得到更好发挥更好的过程,会影响机器学习的效果
机器学习有句著名的话:数据和特征决定了机器学习的上限,模型和算法则让我们逼近这个上限
,特征工程做的好可以让你的算法尽可能得到发挥
特征工程分3步:
特征抽取(提取)
特征预处理
特征降维
特征工程里面会使用到2个工具:
-
pandas
- 加载数据,对数据进行清洗,处理缺省值等初步操作 -
sklearn
- sklearn 是专门做特征工程的库,sklearn 提供了大量专业并且强大的 API
为什么要有特征提取
我想大家都会问问的,我们采集来的数据难倒不是特征吗?错,还是错,打错特错,我们采集来的数据叫做:原始数据
,原始数据一般都不能直接拿来参与后面的计算
为啥呢?前文我们说了机器学习是在统计学的基础上发展出来的,那么统计学是什么呢,统计学其实是一个个数学公式,大家想想,数学公式可以处理文本吗,图片吗,只能是数字啊,所以特征工程由此产生
特征工程公认的解释如下:
将任意的原始数据(如文本、图像)转换为可用于机器学习的数字特征
那么在我们看来,特征工程就是特征值化,把数据转换成特征的过程。我们把采集来的数据叫:原始数据
,而经过特征抽取处理过后的数据叫做:特征
,这下大家知道了吗
特征抽取中我们面对不同的数据时,有不同的思路和算法:
- 遇到字典 字典特征提取
- 遇到文本 文本特征提取
- 遇到图像 图像特征提取(深度学习范畴)
其中文本又分为英文文本和中文文本,思路和算法又不一样,我们会使用sklearn.feature_extraction
包下的 API
-
feature
- 特征 -
extraction
- 提取
One-Hot 编码
数据处理中 One-Hot Encoding 我们不得不说,我也不贴地址了,直接跟大家说
One-Hot 编码是处理那些特征值是各种类型的数据,比如下面这样的
- 性别:["male","female"]
- 地区:["Europe","US","Asia"]
- 浏览器:["Firefox","Chrome","Safari","Internet Explorer"]
机器学习的算法那可是数学公式,没这么NB能处理这种文字,随意啊 One-Hot Encoding 就是把类型变成数字的大宝剑了。有人会问,一般这种情况在代码里我们都是写枚举或是常量的啊,male=1,female=2 啥的,给类型编个号不就ok啦。专家告诉我们不行,这样是不行的,代码里型不代表机器学习这里行啊
male=1,female=2 这样操作的话,最大的问题是 2和1 之间是有大小的,这样会干扰后面的机器学习算法,让后结果不再准确,所以这里我们必须把他们编程同级别的数字,所以[0,1]诞生了,这样才能公平的去表示类别
One-Hot 编码会统计你这个特性有多少种类型,用[0......0]表示,有多少种类型,[]里面就有多少位,0表示没有,1表示有,下面我们看看转换之后的例子:
-
male
= [1,0],性别有2类,按照顺序排列类型,所以是[1,0] -
US
= [0,1,0] -
Chrome
= [0,1,0,0] -
[male,US,Chrome]
= [1,0,0,1,0,0,1,0,0]
好了,就这样,一个数据就变成了一维数组了
特征抽取 - 字典特征抽取
啥是字典特征抽取呢,就是把数据集中那些类型是特征的变成算法能处理的 One-Hot 编码,sklearn 提供了响应的 API:sklearn.feature_extraction.dict_vectorizer
-
dict
- 字典,[0,1,0] 这就是字典 -
vectorizer
- 矢量 - 合起来就是字典矢量化
字典为啥还要矢量化呢,若是某个特征类型有很多,比如上千个的话,你这字典里表示一个类型要用多少个0呀,你想过没有,这会无形中大大增加存储空间,占用非常大的内存使用量合磁盘使用量。优化的思路就是用方位表示值在哪里,把剩下的0都省了,大家看个图:
这是一组数据,原始数据转换成字典是右边的样子,是不是有很多没有的0啊,字典矢量化之后就是左边的样子了,(0,1)=第一行,第二列,数值是1。就是这个意思,这样一搞0就没用了不是
字典矢量化之后叫做:sparse 稀疏矩阵
DictVectorizer
API 讲解:
-
DictVectorizer
是转换器,一上来我们得 new 一个 DictVectorizer 转换器出来用 -
DictVectorizer.fit_transform
填充数据转换,得到 sparse 类型得稀疏矩阵 -
DictVectorizer.inverse_transform
把稀疏矩阵还原成原始数据 -
DictVectorizer.get_feature_names()
获取特征值类型
代码走起:
from sklearn.feature_extraction import DictVectorizer
data = [{"city":"北京","温度":20},{"city":"天津","温度":30},{"city":"上海","温度":40},]
tranfrom = DictVectorizer(sparse=False)
data_new = tranfrom.fit_transform(data)
features = tranfrom.get_feature_names()
print(data_new)
print(features)
(0, 1) 1.0
(0, 3) 20.0
(1, 2) 1.0
(1, 3) 30.0
(2, 0) 1.0
(2, 3) 40.0
['city=上海', 'city=北京', 'city=天津', '温度']
拿到得最终结果就是稀疏矩阵了,然后大家看看特征值类型
稀疏矩阵转换回字典类型:
data_new.toarray()
[[ 0. 1. 0. 20.]
[ 0. 0. 1. 30.]
[ 1. 0. 0. 40.]]
特征抽取 - 英文文本特征抽取
数据千变万化,前面我们处理了那种特征值都是类型的那种数据,那么我们遇到英文文本怎么处理呢?我的朋友可能会问:我们为什么要处理英文文本,这个机器学习有什么关系
?问的很好,知道能干什么,具体应用在哪里才能彻底理解这个技术。文本就是文章就是蚊子,在我们用机器学习分析文章的情感、分类、是否有不当词语、是不是不当言论方面时,我们就需要对文本进行处理了,英文和中文有不同的处理思路,这里我们说英文的
英文是由什么组成的:句子、短语、单词、字母。教授们经过研究认为单词
是破局的关键:
把单词看做文章的特征
把单词在文章中出现的频率作为特征值
这样的话,我们就能有效的抓住这篇文章的特点了,然后我们把数据加工成 sparse 稀疏矩阵用来分析
英文文本分析 API 在:sklearn.feature_extraction.text.CountVectorizer
里面
-
CountVectorizer
- new 一个 CountVectorizer 转换器出来 -
CountVectorizer.fit_transform
填充数据转换,得到 sparse 类型得稀疏矩阵 -
CountVectorizer.inverse_transform
把稀疏矩阵还原成原始数据 -
CountVectorizer.get_feature_names()
获取特征值类型
和DictVectorizer
用法一样,就是名字不一样,大家记住这个思路,要不说 sklearn API 写的好呢,多规矩
代码走起:
from sklearn.feature_extraction.text import CountVectorizer
data = ["life i short,i like you","life is too long,i want to python day to day"]
transform = CountVectorizer()
data_new = transform.fit_transform(data)
features = transform.get_feature_names()
print("单词特征:\n",features)
print("one_hot 矩阵:\n",data_new.toarray())
print("sparse 稀疏矩阵:\n",data_new)
单词特征:
['day', 'is', 'life', 'like', 'long', 'python', 'short', 'to', 'too', 'want', 'you']
one_hot 矩阵:
[[0 0 1 1 0 0 1 0 0 0 1]
[2 1 1 0 1 1 0 2 1 1 0]]
sparse 稀疏矩阵:
(0, 2) 1
(0, 6) 1
(0, 3) 1
(0, 10) 1
(1, 2) 1
(1, 1) 1
(1, 8) 1
(1, 4) 1
(1, 9) 1
(1, 7) 2
(1, 5) 1
(1, 0) 2
例子里,我们用机器学习文本特征抽取的方式处理了2段英文,在 one_hot 矩阵里显示的是每段英文中每个单词出现的频率,你看 day 单词统计的数据就是2,大家仔细体会下,不再过多展开了
最后还有一点,CountVectorizer(stop_words)
,构造函数里还有参数可以设置,这个stop_words
是个集合,表示我们要忽略的单词,相当一个黑名单,我们不想要的就不统计了
stop_words
不用我们自己去统计,这东西有个学名叫:停用词表
,有很多做自然语言处理的结构会去统计这个停用词表没,每个行业都有不同的停用词表,有需要的网上找找就可以了
特征抽取 - 中文文本特征抽取
有的小伙伴又要问了,为啥英文文本合中文文本处理不一样呀~ 我能说啥,很明显的呀,英文有单词,中文都是字没单词啊...要是用CountVectorizer
处理中文,那么每个字都是一个特征,你说这还有啥用
但是啊又是这个万变不离其宗
,中文文本特征提取的思路其实还是英文那套,虽然在中文中我们没有单词可以处理了,但是大家想啊,英文中单词之间是要加空格
的,这里我们给中文文本中词语之间加上空格
那就好使啦
一般人马上想到,我们可以手动啊,那么我们去试试:
from sklearn.feature_extraction.text import CountVectorizer
data = ["毛主义 万岁","新中国 万岁"]
transform = CountVectorizer()
data_new = transform.fit_transform(data)
features = transform.get_feature_names()
print("单词特征:\n",features)
print("one_hot 矩阵:\n",data_new.toarray())
print("sparse 稀疏矩阵:\n",data_new)
单词特征:
['万岁', '新中国', '毛主义']
one_hot 矩阵:
[[1 0 1]
[1 1 0]]
sparse 稀疏矩阵:
(0, 2) 1
(0, 0) 1
(1, 0) 1
(1, 1) 1
OK 妥妥的没问题,但是手动累啊,给你来个几万字,那不得累死你...所以啊,这还是得自动的才靠谱啊
这里中文分词自动化靠jieba
这个库,这是个免费的通用库,一般都用这个,但是一些行业有自己更加 专业的分词库,了解更多的就去看看自然语言处理部分
jieba
使用 pip 安装即可:
pip3 install jieba
API 使用
import jieba
def cut_text(text):
return list(jieba.cut(text))
print(cut_text("毛主义万岁,新中国万岁"))
['毛', '主义', '万岁', ',', '新', '中国', '万岁']
OK 这样中文有了空格
之后就能使用CountVectorizer
统计词频了。jieba 的方法大家注意下,cut 方法返回的是迭代器,需要我们强转成集合类型,然后用"".join()
转成字符串输出
代码:
from sklearn.feature_extraction.text import CountVectorizer
import jieba
def cut_text(text):
return " ".join(list(jieba.cut(text)))
data = ["今天很残酷,明天很残酷没,后天很美好,但是大家基本都死在明天晚上了",
"我们看到的光是星系从很远的距离发射过来的,所以我们在观测宇宙时,是在观测他的过去",
"如果只能一种方法了解他,那么你永远不会了解他",]
data_jieba = []
for text in data:
data_jieba.append(cut_text(text))
transform = CountVectorizer()
data_new = transform.fit_transform(data_jieba)
features = transform.get_feature_names()
print("单词特征:\n",features)
print("one_hot 矩阵:\n",data_new.toarray())
print("sparse 稀疏矩阵:\n",data_new)
单词特征:
['一种', '不会', '了解', '今天', '但是', '光是', '发射', '只能', '后天', '基本', '大家', '如果', '宇宙', '我们', '所以', '方法', '明天', '星系', '晚上', '残酷', '永远', '看到', '美好', '观测', '距离', '过去', '过来', '那么']
one_hot 矩阵:
[[0 0 0 1 1 0 0 0 1 1 1 0 0 0 0 0 2 0 1 2 0 0 1 0 0 0 0 0]
[0 0 0 0 0 1 1 0 0 0 0 0 1 2 1 0 0 1 0 0 0 1 0 2 1 1 1 0]
[1 1 2 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1]]
sparse 稀疏矩阵:
(0, 3) 1
(0, 19) 2
(0, 16) 2
(0, 8) 1
(0, 22) 1
(0, 4) 1
(0, 10) 1
(0, 9) 1
(0, 18) 1
(1, 13) 2
(1, 21) 1
(1, 5) 1
(1, 17) 1
(1, 24) 1
(1, 6) 1
(1, 26) 1
(1, 14) 1
(1, 23) 2
(1, 12) 1
(1, 25) 1
(2, 11) 1
(2, 7) 1
(2, 0) 1
(2, 15) 1
(2, 2) 2
(2, 27) 1
(2, 20) 1
(2, 1) 1
好了,中文文本特征抽取就说这么多了,相比英文就是中间多了分词添空格
的步奏
特征抽取 - 关键词筛选
研究认为,词频越大的词语对文本的响应更大,比如界定文章的类属。但是像:只有、因为、所以、这样、那样
这样的语气词、介词,其实对我们来说是没有实际意义的,我们应该过滤这种没用的词
这种有意义的词有个学名:关键词
在某一个类别的文章中,某个词词频很大,但是在其他类别的文章中这个词词频则很小,这样的词才是有意义的,这样的词叫做关键词
举个例子:
- 某个文章中大量出现
共享
、车
这样的词,那这篇文章应该就是科技类或是互联网的文章
- 某个文章中大量出现
经济
、证券
、银行
这样的词,那这篇文章应该就是经济或是金融类的文章
像上面这样的词才是我们想要找出来的,那么有方法吗,自然有的:TF-IDF
,这个指标是是专家们研究出来的,TF-IDF
越大这个次越有意思,越重要
TF-IDF 公式:TF-IDF = TF * IDF:
-
TF
- 词频,这个词在你文章当中出现的频率 -
IDF
- 逆向文档频率,总文件数目/包含该词语的文件的数目,再取一个以10为底的对数
先说下什么是对数
,比如以10为底求对数
,就是说这个数是10的几次方,这个几次方就是对数
看解释:
- 语料库 1000 篇文章
- 100 篇文章含有 "今天"
- 10 篇文章还有 "单车"
- 文章A 100个词,出现 "今天" 10次,出现 "单车" 10次
- TF:"今天" = 10/100 = 0.1 ; "单车" = 10/100 = 0.1
- IDF: "今天" = log 10 1000/100 = 1; "单车" = log 10 1000/10 = 2
- TF-IDF:"今天" = 0.1 * 1=0.1;"单车" = 0.1 * 2=0.2
因为单车
的TF-IDF值更大,这就说明单车
比今天
更有代表性,单车
才是这篇文章的关键字
代码:
from sklearn.feature_extraction.text import TfidfVectorizer
import jieba
def cut_text(text):
return " ".join(list(jieba.cut(text)))
data = ["今天很残酷,明天很残酷没,后天很美好,但是大家基本都死在明天晚上了",
"我们看到的光是星系从很远的距离发射过来的,所以我们在观测宇宙时,是在观测他的过去",
"如果只能一种方法了解他,那么你永远不会了解他",]
data_jieba = []
for text in data:
data_jieba.append(cut_text(text))
transform = TfidfVectorizer()
data_new = transform.fit_transform(data_jieba)
features = transform.get_feature_names()
print("单词特征:\n",features)
print("one_hot 矩阵:\n",data_new.toarray())
print("sparse 稀疏矩阵:\n",data_new)
单词特征:
['一种', '不会', '了解', '今天', '但是', '光是', '发射', '只能', '后天', '基本', '大家',
'如果', '宇宙', '我们', '所以', '方法', '明天', '星系', '晚上', '残酷', '永远', '看到',
'美好', '观测', '距离', '过去', '过来', '那么']
one_hot 矩阵:
[[0. 0. 0. 0.25819889 0.25819889 0.
0. 0. 0.25819889 0.25819889 0.25819889 0.
0. 0. 0. 0. 0.51639778 0.
0.25819889 0.51639778 0. 0. 0.25819889 0.
0. 0. 0. 0. ]
[0. 0. 0. 0. 0. 0.24253563
0.24253563 0. 0. 0. 0. 0.
0.24253563 0.48507125 0.24253563 0. 0. 0.24253563
0. 0. 0. 0.24253563 0. 0.48507125
0.24253563 0.24253563 0.24253563 0. ]
[0.30151134 0.30151134 0.60302269 0. 0. 0.
0. 0.30151134 0. 0. 0. 0.30151134
0. 0. 0. 0.30151134 0. 0.
0. 0. 0.30151134 0. 0. 0.
0. 0. 0. 0.30151134]]
sparse 稀疏矩阵:
(0, 18) 0.25819888974716115
(0, 9) 0.25819888974716115
(0, 10) 0.25819888974716115
(0, 4) 0.25819888974716115
(0, 22) 0.25819888974716115
(0, 8) 0.25819888974716115
(0, 16) 0.5163977794943223
(0, 19) 0.5163977794943223
(0, 3) 0.25819888974716115
(1, 25) 0.24253562503633297
(1, 12) 0.24253562503633297
(1, 23) 0.48507125007266594
(1, 14) 0.24253562503633297
(1, 26) 0.24253562503633297
(1, 6) 0.24253562503633297
(1, 24) 0.24253562503633297
(1, 17) 0.24253562503633297
(1, 5) 0.24253562503633297
(1, 21) 0.24253562503633297
(1, 13) 0.48507125007266594
(2, 1) 0.30151134457776363
(2, 20) 0.30151134457776363
(2, 27) 0.30151134457776363
(2, 2) 0.6030226891555273
(2, 15) 0.30151134457776363
(2, 0) 0.30151134457776363
(2, 7) 0.30151134457776363
(2, 11) 0.30151134457776363
特征抽取总结
看过上文的小伙伴们可能有点懵,反正我是在看了一遍才略顺的
特特征抽取我们要面对:类型值特征、中英文文本
这种非数组类型的数据,对于这么数据 sklearn 有响应的处理方法:
-
类型值特征:
使用DictVectorizer
把数据特征转换成 One Hot 编码,再进一步省略0转成 sparse 稀疏矩阵 -
英文文本:
使用CountVectorizer
统计英文单词出现的词频,再转成 sparse 稀疏矩阵 -
中文文本:
使用jieba
分词库先给中文词组之间加空格,在按照英文类型处理 -
关键词筛选:
使用TfidfVectorizer
把文章中的关键词找出来
sklearn API 非常标注,特征抽取方面 API 使用顺序:
- 先创建
XXXVectorizer
转换器对象 -
XXXVectorizer.fit_transform
填充数据转换,得到 sparse 类型得稀疏矩阵 -
XXXVectorizer.inverse_transform
把稀疏矩阵还原成原始数据 -
XXXVectorizer.get_feature_names()
获取特征值类型 -
sparse.toarray()
把稀疏矩阵转成 one hot 编码矩阵
特征预处理 - 归一化
经过上面的努力,我们终于拿到数值化的特征值了,但是这些特征就能直接进行机器学习的计算吗?错,大错特错!这只是万里长征的第一步罢了,再特征抽取之后,我们还要经历一个叫做:特征预处理
的阶段
啥叫特征预处理
把特征数值转换成算法能接受的过程
看不懂正常,我也没指望大家看这一行字就都 OK 了,我们举个例子:
这是别人统计的老美女性眼中的好男人的特征,有3个特征,大家先观察下数值,里程数相比其他数都特别大对吧。假设机器学习的算法是3个特征值相加或者相乘,那么大家想想,是不是里程数因为自身数字大而基本决定了计算结果,而其他2个特征对结果影响非常小啊。如果是这样的话,机器学习算法能学习到的特征其实只有公里数一个特征罢了,剩下的2个特征白统计了
这不是我们想要的,我们想要的是能让所有特征都公平的参与进计算,而不是一家独大,所以这就引出了一个概念:无量纲化
归一化
是无量纲化
的一种,也是本节要讲的内容。干的事其实很简单,就是统一量纲
,通过算法把原始数据变换映射到同一个数值范围区间,比如默认的就是到[0,1]之间,给所有的特征公平的相互地位
归一化算法如下:
- 思路是使用一列特征的最大最小值计算
- X` 是量纲在[0,1]之间的算法
- X`` 是其他量纲的算法
举个例子:
- 特征1 的 90 X` = (90-60)/(90-60) = 1
- 特征1 的 90 X`` = 1 * (3-2) + 2 (若量纲是[2,3])
- 特征1 的 75 X` = (75-60)/(90-60) = 0.5
- 特征3 的 13 X` = (13-10)/(15-10) = 0.6
最后我们转换成这个样子:
这样的话,特征之间就是平等的了,站的权重都是一样的了,也不会有谁比谁厉害的问题了
归一化
API:sklearn.preprocessing.minmax_scale
,使用方式和之前的一样
代码:
from sklearn.preprocessing import MinMaxScaler
import pandas as pd
data = pandas.read_csv("dataing.txt")
transform = MinMaxScaler(feature_range=[0,1])
data_new = transform.fit_transform(data)
print(data_new)
我这里没有数据,自己搞了一个,结果不太对,就不贴了,但是代码是没问题的
特征预处理 - 标准化
上文书归一化
,使用的是最大值最小值来计算的,但是吧归一化
这算法很蛋疼,没法适用异常点。何谓异常点,就是那些忽大忽小的值,比方说大家都在 100-300 之间,而你就非的是 100W,那这样按照归一化
来算的话,你这个一个异常点对结果影响是非常大的,大家看一张图:
红色的数据就是异常值,对结果很不利,应该踢出掉
既然归一化
不好使,那么必然会有新的东东啦标准化
把数据转换到均值为0,标准差为1的范围内
算法是:当前数值减去平均值再除以标准差
说实话我对标准差不是很理解,大家就这么看着吧,以后再多多加强这方面的学习
API sklearn.preprocessing.StandardScaler
代码
from sklearn.preprocessing import StandardScaler
import pandas as pd
data = pandas.read_csv("dataing.txt")
print(data)
transform = StandardScaler()
data_new = transform.fit_transform(data)
print("data_new:\n",data_new)
Milage Liters Consuniome
0 61364 7.5160 1.268880
1 36789 12.4500 0.645550
2 5741 8.0570 1.656400
3 28567 939865.0000 0.731231
4 6080 1.3655 0.648780
data_new:
[[ 1.61692847 -0.49999955 0.68659842]
[ 0.43626965 -0.49998643 -0.84895692]
[-1.05537206 -0.49999811 1.64124268]
[ 0.0412594 2. -0.63788427]
[-1.03908546 -0.50001591 -0.84099991]]
标准化
就理解这么多,显然这是不够的,其中的数学概念和思路以后我有经验之后再加进来
特征预处理 - 总结
特征预处理做的是无量钢化
的事,是把数据统一到统一数值范围内,已实现特征之间平等性,算法有2个
归一化:
-
算法
:以最大值,最小值为依托 -
缺点
:非常容易受异常值影响 -
适用范围
:只适合传统精确小数据场景 -
API
: MinMaxScaler
标准化:
-
算法
:以平均数,标准差为依托 -
有点
:可以屏蔽异常值不是非常多的时候对结果的影响 -
适用范围
:在样本足够多的情况下比较稳定,适合现代嘈杂的大数据场景 -
API
: StandardScaler
-
总体来说:
- 对于归一化来说:如果出现异常点,影响了最大值和最小值,那些结果显然会发生改变
- 对于标准化来说:如果出现异常点,由于具有一定数据量,少量的异常点对于平均值的影响不大,从而方差改变较小
什么是特征选择
前文我们解决了特征数值不适用于算法需要变换的问题,那么现在问题又来了:特征都是我们需要的吗?
显然不是,假如说我们在纷乱的数据中提取出了150个特征,那这150个特征我们都要,都参与计算,那显然是不可能的。特征当中必然有没用的,有涌余的,有垃圾的,我们要从中找到重要作用,起决定性作用的,那些能代表事物规律的特征,拿着这些特征我们再去跑算法才能得到接近事实的规律结果
特征选择这里有个专用概念:降维
降维就是减少特征数量
降维在机器学习里和数学不一样,数学的维度就是坐标系的X/Y,机器学习里的维度就是特征,降维
就是减少特征个数
那么怎样的特征可以删除呢:
方差小的特征
关联程度高的特征
什么是方差:
在统计描述中,方差用来计算每一个变量(观察值)与总体均数之间的差异
简单来说就是纵向比较一个特征中所有的数据,查看数据之间的差异
-
方差越小:
说明该特征大多数样本的值越接近 -
方差越大:
说明该特征大多数样本的值越不一样
什么是关联性:
就是字面意思,2个特征之间是由关系的,比如湿度与降雨量,降雨量越高自然湿度越大
总结一些,特征选择做的是:主成分分析
,找出特征中最有代表性的那些关键特征
特征选择在技术上有如下的思路:
-
filter 过滤式
-
方差选择法:
过滤低方差的特征,方差越低,特征越说明样本中该特征值越趋同 -
相关系数法:
特征与特征之间关系密切程度系数- 皮尔逊相关系数,斯皮尔曼相关系数
-
-
embedded 嵌入式
- 决策树
- 正则化
- 深度学习 卷积等
特征选择 - 方差选择法
上面说过方差了,这里我举个例子:
统计了鸟的一组特征,其中有有一个特征是:鸟有没有腿
- 显然所有的鸟都是有腿的,所以腿在我们看来就是方差小的特征,是没啥用的
- 但是换个角度看,比较鸟了其他动物的时候,有没有腿这个特征就很重要了,有点动物有腿,有的动物没腿,但是此时有没有腿这个特征的方差就不是0了,而是有意思的了
- 所以结论就是:方差小的特征,自然就是没用的,不管我们如何考虑问题,都应该过滤这个特征
最简单的涌余数据就是样本中都共同的特征,这个特征就是涌余的,比如鸟有个特征:是不是有脚有翅膀。单个安要看这个特征是不是涌余的还要看我们像分析什么,要是把鸟和鸟之间比较的话,这个特征就是涌余的,要是鸟和其他动物比较的话,这个特征就是有意义的
API:sklearn.feature_selection.VarianceThreshold
代码:
from sklearn.feature_selection import VarianceThreshold
from sklearn.datasets import load_iris
data = load_iris()
print(data.data.shape)
transform = VarianceThreshold(threshold=0.3)
data_new = transform.fit_transform(data.data)
print("data_new:\n",data_new.shape)
(150, 4)
data_new:
(150, 3)
threshold
这个参数是方差,默认是0,具体的方差要多大得仔细研究,但是只要传入的 threshold > 数据之间最大方差数,会报错的,要注意
这里我们还是用那个花的数据,在方差=0.3 时,是过滤掉了一组特征值的
OK,就这么多,大家理解为啥要考虑方差就行了
特征选择 - 相关系数
相关系数是个从 -1 到 1
之间的数,绝对值越大、越接近1,越说明这2个特征之间关联性越强,正负表示方向,正相关,反向相关
反向相关: 一个特征的越大,则会让另一个特征越小
-
绝对值 < 0:
底相关 -
绝对值 0.4 - 0.7
显著相关 -
绝对值 0.7 - 1
高度线性相关
算法有:
皮尔逊相关系数
斯皮尔曼相关系数
- 这里常用的是:
皮尔逊相关系数
公式如下:很复杂...
注意 皮尔逊相关系数 就不在 sklearn 里了,而是在 scipy 里
API: scipy.stats.pearsonr
代码:
from sklearn.datasets import load_iris
from scipy.stats import pearsonr
data = load_iris()
print(data["feature_names"])
data_new = data.data
data_new = pearsonr(data_new[0],data_new[1])
print("data_new:\n",data_new)
['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
data_new:
(0.99599866124026, 0.0040013387597399586)
这里就是简单的试了下,大家看结果,花的特征里面,相关特征关系都非常紧密,其实可以当成一个特征来看
特征选择 - 主成分分析
-
定义:
高维数据转换成底维数据的过程,在此过程中可能会舍弃愿有特征,创造新的特征 -
作用:
对数据维数进行压缩,尽可能降低元数据的维度(复杂度),损失少量信息 -
适用范围:
回归分析、聚类分析
当特征之间关联性很高时:
- 选取其中一额个作为代表
- 特征之间进行加权计算成一个新的特征
除此之外还有一个专业的算法:主成分分析(PCA)
PCA 算法很复杂,在有5个数据时,是这么算的:
很蛋疼,我也没看懂
API:from sklearn.decomposition.PCA
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
data = load_iris()
print(data["feature_names"])
transform = PCA(n_components=0.99)
data_new = transform.fit_transform(data.data)
print("data_new:\n",data_new)
['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
data_new:
[[-2.68412563 0.31939725 -0.02791483]
[-2.71414169 -0.17700123 -0.21046427]
[-2.88899057 -0.14494943 0.01790026]
[-2.74534286 -0.31829898 0.03155937]
[-2.72871654 0.32675451 0.09007924]
[-2.28085963 0.74133045 0.16867766]
.....................................
[ 1.90094161 0.11662796 0.72325156]
[ 1.39018886 -0.28266094 0.36290965]]
PCA
方法中的参数 n_components
传整数和小数时有不同的意思:
-
小数
保留百分之几的信息 -
整数
减少到多少特征
好了,写完了,PCA 看完我也只是知道 PCA 是干嘛的,但是对于算法和结果我是真不知道啊,我也是懵逼的,这个就有待以后学习了,估计相关知识点都在后面了,大家先知道有这么个算法,后面的学习自然会碰到