学习机器学习六 数据表示与特征工程3

 

目录

类别变量表示

文本特征表示

图像表示

1、使用色彩空间

2、图像角点检测

3、使用尺度不变特征变换

4、使用加速健壮特征


上两篇学习了特征工程的一些知识,现在接着学习,其实特征工程看起来简单,其实很大很杂,最多的还是要靠自己的智慧去做这件事,也许至少心里要有个底要怎么做会比较好。

回归正题,构建机器学习系统时最常见的数据类型之一是“类别特征”(也叫作离散特征),比如水果的颜色和公司的名字,就是我们现实中的离散特征怎么用数字表示。比如我们要做名字特征时,如果用1表示张三,2表示李四,3表示王五,机器算法会觉得1<2<3,但是他们是离散的不存在任何关系啊。这个时候我们需要找一个二值编码,也叫作独热编码(one-hot)

※one-hot编码在openCV中不支持

在scikit-learn中one-hot编码在类DictVectorizer中,在feature_extraction模块中能找到,它的工作方法就是简单的输入一个包含数据的字典到fit_transfrom函数中,这个函数会自动确定编码哪个特征。

参考了很多网上的牛人,结合书本中的知识简单总结了下:

类别变量表示

如编码一份名单:

# 一份人工智能先驱的名单,姓名,出生年份,死亡年份
data = [
    {'name': 'Alan Turing', 'born': 1912, 'died': 1954},
    {'name': 'Herbert A. Simon', 'born': 1916, 'died': 2001},
    {'name': 'Jacek Karpinski', 'born': 1927, 'died': 2010},
    {'name': 'J.C.R. Licklider', 'born': 1915, 'died': 1990},
    {'name': 'Marvin Minsky', 'born': 1927, 'died': 2016},
]

# one-hot编码
from sklearn.feature_extraction import DictVectorizer
vec = DictVectorizer(sparse=False, dtype=int)
vec.fit_transform(data)   #输出array([[1912, 1954,    1,    0,    0,    0,    0],
                                      [1916, 2001,    0,    1,    0,    0,    0],
                                      [1927, 2010,    0,    0,    0,    1,    0],
                                      [1915, 1990,    0,    0,    1,    0,    0],
                                      [1927, 2016,    0,    0,    0,    0,    1]], 
                                    dtype=int64)

现在来解释下,实际的列名应该是:

【born   died   “Alan Turing”   “Herbert A. Simon”  “Jacek Karpinski”  “J.C.R. Licklider”  “Marvin Minsky”】

  拿第一行来说,1,0,0,0,0,其中1表示属于,0表示不属于,很明显第一行就是说生于1912,卒于1954的Alan Turing。

我们验证下这些列是不是我这么说的列名:

vec.get_feature_names()  #输出['born',
                               'died',
                               'name=Alan Turing',
                               'name=Herbert A. Simon',
                               'name=J.C.R. Licklider',
                               'name=Jacek Karpinski',
                               'name=Marvin Minsky']

对吧,一切就是跟我说的一样。同时你是不是在想如果要有一百万人,那么是不是有一百万列表示,没错,想的一点也没错。我们也能看出如果数据大那么这是个稀疏矩阵!所以说one-hot编码并不是很完美,以后我们会学习用别的代替下这种情况。

 

文本特征表示

“文本特征”是另一种很常见的特征类型,跟上述类别很相似,还是scikit-learn提供了一种编码方式来表示——文本特征。当我们处理文本特征时,通常都可以比较方便地将某个单词或者短语编码为数值。一般情况下我们是计算这个短语中每个单词出现的次数在scikit-learn中使用CountVectorizer就可以轻松完成,功能和类别变量特征的DictVectorizer相似。

如,表示包含一小部分文本短语的数据集

sample = [
    'feature engineering',
    'feature selection',
    'feature extraction'
]

# 这个操作会把特征矩阵X保存为一个稀疏矩阵
from sklearn.feature_extraction.text import CountVectorizer
vec = CountVectorizer()
X = vec.fit_transform(sample)

我们来看下它 的结果是啥:

X.toarray()   #输出array([[1, 0, 1, 0],
                          [0, 0, 1, 1],
                          [0, 1, 1, 0]], dtype=int64)

# 我们来看下这些列都是什么
vec.get_feature_names()   #输出['engineering', 'extraction', 'feature', 'selection']

我们上面介绍了类别变量特征的理解,看这个也能理解了吧。1代表出现1次,0代表无。是的,它文本出现的单词多的时候也是一个稀疏矩阵。所以对于文本特征而言他有一个缺点,就是某些单词出现频率太高,造成它的权重过大。你想想看比如我这文章“的”字目前出现32次,权重就是32。“短”字可能就出现4次,权重就是4,是不是差别太大。

对于这个缺点我们有个方法修复,这个方法叫做“词频——逆文档频率”(term frequency-inverse document frequency,TF-IDF)。TF-IDF主要就是通过衡量单词在整个数据集中出现的频率来计算它们的权重

from sklearn.feature_extraction.text import TfidfVectorizer

vec = TfidfVectorizer()

X = vec.fit_transform(sample)

X.toarray()      #输出array([[ 0.861037  ,  0.        ,  0.50854232,  0.        ],
                             [ 0.        ,  0.        ,  0.50854232,  0.861037  ],
                             [ 0.        ,  0.861037  ,  0.50854232,  0.        ]])

发现什么问题了没,通过频率去设置权重就避免了很大与很小的缺点,毕竟1和0比好过32和4比吧。

 

图像表示

图像表示在机器学习中是非常重要且非常常见的特征之一。表示图像最直接的方法就是灰度值,就像上一篇文章一样。但是灰度值却不是描述图像的最有效的特征,就像我告诉你红的你会知道它是什么吗?显然不能,那么我们要去解决这个事。

1、使用色彩空间

色彩能包含一些灰度值无法捕获的信息,比如你看黑白图和看彩色图哪个能让你更快明白那是什么呢?!我们现在使用的色彩一般是由RGB组成的。还记得颜料三颜色吗?就是说世界的色彩都是由3种颜色调出来的,这三种颜色就是红色(R)、绿色(G)和蓝色(B)。我们图片上的每个像素点都是RGB的强度值,和成这个点的颜色。

openCV还提供了其它更丰富的色彩空间,比如色相、饱和度、亮度,以及Lab色彩空间等去描述图像。

①以RGB色彩空间编码

在openCV中彩色图像实际上是按照BGR图像进行保存的,颜色通道的顺序是蓝-绿-

import cv2
import matplotlib.pyplot as plt
%matplotlib inline

# 我们载入BGR格式的样本图像
img_bgr = cv2.imread('data/lena.jpg')

如果我们用Matplotlib或者类似的库来显示BGR图像,会有点偏蓝色,那是因为Matplotlib希望输入的是RGB图像(就是顺序不同),所以我们需要转换下:

# 将BGR转换成RGB
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)

# 画出来RGB和BGR两个图
plt.figure(figsize=(12, 6))
plt.subplot(121)
plt.imshow(img_bgr)
plt.subplot(122)
plt.imshow(img_rgb);

②以HSV和HLS色彩空间编码图像

在后来的发展中,前辈们发明了两个比RGB更好的色彩空间,一种叫做HSV,表示的是色相、饱和度和明度;一种叫做HLS,表示的是色相、亮度和饱和度。在这些色彩空间中,色彩的色相有一个单独的色相通道表示,色彩的鲜艳程度有一个饱和度通道表示,而亮度或者明度由一个亮度通道或者名度通道表示。

openCV中使用cv2.cvtColor就可以轻松地把RGB图像转换到HSV色彩空间:

img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)

对于其它的有:

※HLS(色相、亮度、饱和度)使用cv2.COLOR_BGR2HLS;

※LAB(亮度、绿-红和蓝-黄)使用cv2.COLOR_BGR2LAB;

※YUV(整体亮度、蓝色度、红色度)使用cv2.COLOR_BGR2YUV;

 

2、图像角点检测

在图像中可以最直接找到的特点之一大概就是角点。openCV提供了至少两个不同的算法来检测图像中的角点。

①Harris角点检测:在知道边缘是所有方向上变化强度较高的区域后,Harris和Stephens提出了一个快速找到这个区域的方法。在openCV中这个算法叫做cv2.cornerHarris;

Harris角点检测仅可以处理灰度图像,因此首先需要把BGR图像转换成灰度图像:

img_gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)

接下来输入一张图像,设置角点检测的像素领域大小(blockSize),边缘检测的孔径参数(Ksize),以及所谓的Harris检测器的自有参数(k):

corners = cv2.cornerHarris(img_gray, 2, 3, 0.04)

plt.figure(figsize=(12, 6))
plt.imshow(corners, cmap='gray');

生成如下图:

②Shi-Tomasi角点检测:Shi和Tomasi对于什么是要追踪的好的特征有自己的想法,他们的方法是通过找到N个最强的角点,往往结果比Harris角点检测好。在openCV中这个算法叫做cv2.goodFeaturesToTrack;

 

3、使用尺度不变特征变换

当图像尺寸变化时,角点检测的手法就行不通了。为了解决这个问题,有个叫David Lowe提出一个描述图像中兴趣点的方法,该方法不受方向和尺寸的变化影响。因为才叫做“尺度不变特征变换”(scale-invariant feature transform,SIFT)

在openCV3里面,是函数xfeatures2d模块的一部分:

sift = cv2.xfeatures2d.SIFT_create()

这个算法一般分为两个步骤:

①检测:这一步识别图像中的兴趣点(也叫作关键点);

②计算:这一步计算每个关键点真实的特征值;

关键点监测可以这么写:

kp = sift.detect(img_bgr)

除此之外,有个函数叫做drawKeypoints函数提供了一个对检测到的关键点进行可视化非常好的方式。通过传入一个选项标记,cv2.DRAW_MATCHES_FLAGS_DRAW_KEYPOINTS函数就会在每个关键点上画一个圆圈,圆圈的大小表示关键点的重要性,以及一条径向线表示关键点的方向。

import numpy as np

img_kp = np.zeros_like(img_bgr)

img_kp=cv2.drawKeypoints(img_rgb,kp,img_kp,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

plt.figure(figsize=(12, 6))
plt.imshow(img_kp)

接下来特征描述符就可以用compute函数来计算:

kp, des = sift.compute(img_bgr, kp)

得到的特征描述符(des)应该有128个特征值,用来找到每一个关键点。在我们的例子中一共有238个特征,每个特征有128个特征值:

des.shape   #输出(238, 128)

或者可以进一步就检测关键点并计算特征描述符:

kp2, des2 = sift.detectAndCompute(img_bgr, None)

使用Numpy,通过保证des中每个值近似等于des2中的值,可以确信这两个方法的结果是完全一样的。

np.allclose(des, des2)  #输出True

 

4、使用加速健壮特征

SIFT很不错,但缺点是速度不够快,有一个算法可以解决这个问题,那就是“加速健壮特征”(Speeded Up Robust Features,SURF)算法。它使用一个箱式滤波器替换了SIFT中大量的计算过程。

surf = cv2.xfeatures2d.SURF_create()

kp = surf.detect(img_bgr)

img_kp = np.zeros_like(img_bgr)

img_kp=cv2.drawKeypoints(img_rgb,kp,img_kp,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

plt.figure(figsize=(12, 6))

plt.imshow(img_kp)

SURF比SIFT检测到更多的特征,结果图像如下所示:

在计算了特征描述符之后,我们可以检测des(特征描述符)对象,发现一共找到了351个特征,每个特征有64个特征值。

kp, des = surf.compute(img_bgr, kp)

des.shape  #输出(351, 64)

※有一点要注意下,SURF比SIFT如果商用是要付费的,我们可以用开源的cv2.ORB去替代,它使用所谓的FAST关键点检测子和BRIEF描述符,无论是性能还是计算开销都与SURF比SIFT相匹配。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值