Python機器學習庫Sklearn系列教程(3)-特徵提取

注意:特徵特徵提取與 特徵選擇 有很大的不同:前者包括將任意數據(如文本或圖像)轉換為可用於機器學習的數值特徵。後者是將這些特徵應用到機器學習中。

從字典類型加載特徵

類 DictVectorizer 可用於將標準的Python字典(dict)對象列表的要素數組轉換為 scikit-learn 估計器使用的 NumPy/SciPy 表示形式。

<code># ===========從字典類型加載特徵。形成係數矩陣結構==========
from sklearn.feature_extraction import DictVectorizer
measurements = [
{'name': 'student1', 'age': 12},
{'boy':True, 'parents': 'baba'},
{'size':16},
]
vec = DictVectorizer().fit(measurements) # 定義一個加載器,後對一個字典對象提取特徵。(值為數值型、布爾型的屬性為單獨的屬性。值為字符串型的屬性,形成"屬性=值"的新屬性)
print('提取的特徵:',vec.get_feature_names()) # 查看提取的新屬性
print('稀疏矩陣形式:\\n',vec.transform(measurements))
print('二維矩陣形式:\\n',vec.transform(measurements).toarray())/<code>

輸出結果為

<code>提取的特徵: ['age', 'boy', 'name=student1', 'parents=baba', 'size']
稀疏矩陣形式:
(0, 0) 12.0
(0, 2) 1.0
(1, 1) 1.0
(1, 3) 1.0
(2, 4) 16.0
二維矩陣形式:
[[ 12. 0. 1. 0. 0.]
[ 0. 1. 0. 1. 0.]
[ 0. 0. 0. 0. 16.]]/<code>


類 DictVectorizer 也是對自然語言處理模型中訓練序列分類器的有用的表示變換,通常通過提取圍繞感興趣的特定的詞的特徵窗口來工作。

如果一個文本語料庫的每一個單詞都提取了這樣一個上下文,那麼所得的矩陣將會非常寬(許多 one-hot-features),其中大部分通常將會是0。 為了使結果數據結構能夠適應內存,該類DictVectorizer 的 scipy.sparse 默認使用一個矩陣而不是一個 numpy.ndarray。

文本特徵提取

文本分詞有個比較好的庫,結巴分詞,可以參考:https://blog.csdn.net/luanpeng825485697/article/details/78757563

類 CountVectorizer 在單個類中實現了 tokenization (詞語切分)和 occurrence counting (出現頻數統計):

<code># =================文本特徵提取==============
from sklearn.feature_extraction.text import CountVectorizer
corpus = ['This is the first document.',
'This is the second second document.',
'And the third one.',
'Is this the first document?',]
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(corpus) # 默認提取至少 包含2個字母的單詞
print('所有特徵:',vectorizer.get_feature_names())
print('樣本特徵向量:\\n',X.toarray()) # X本身為稀疏矩陣存儲形式,toarray轉換為二維矩陣形式
print('document屬性的列索引:',vectorizer.vocabulary_.get('document')) # 從 特徵 名稱到矩陣的(列索引)
# 提取一個單詞或兩個單詞形成的詞組。這樣就能識別“is this”和“this is”這兩種詞彙了

bigram_vectorizer = CountVectorizer(ngram_range=(1, 2),token_pattern=r'\\b\\w+\\b', min_df=1)
analyze = bigram_vectorizer.build_analyzer()
print('所有分詞:',analyze('Bi-grams are cool!'))/<code>

Tf–idf 項加權

在一個大的文本語料庫中,一些單詞將出現很多次(例如 “the”, “a”, “is” 是英文),因此對文檔的實際內容沒有什麼有意義的信息。 如果我們將直接計數數據直接提供給分類器,那麼這些頻繁詞組會掩蓋住那些我們關注但很少出現的詞。

為了為了重新計算特徵權重,並將其轉化為適合分類器使用的浮點值,因此使用 tf-idf 變換是非常常見的。

Tf表示術語頻率,而 tf-idf 表示術語頻率乘以轉制文檔頻率: tf-idf(t,d)=tf(t,d)×idf(t).
使用 TfidfTransformer 的默認設置,TfidfTransformer(norm='l2', use_idf=True, smooth_idf=True, sublinear_tf=False) 術語頻率,一個術語在給定文檔中出現的次數乘以 idf 組件, 計算為


Python機器學習庫Sklearn系列教程(3)-特徵提取

其中 n_d 是文檔的總數,df(d,t)是包含術語 t 的文檔數。 然後,所得到的 tf-idf 向量通過歐幾里得範數歸一化:


Python機器學習庫Sklearn系列教程(3)-特徵提取

它源於一個詞權重的信息檢索方式(作為搜索引擎結果的評級函數),同時也在文檔分類和聚類中表現良好。

以下部分包含進一步說明和示例,說明如何精確計算 tf-idfs 以及如何在 scikit-learn 中計算 tf-idfs, TfidfTransformer 並 TfidfVectorizer 與定義 idf 的標準教科書符號略有不同


Python機器學習庫Sklearn系列教程(3)-特徵提取

在 TfidfTransformer 和 TfidfVectorizer 中 smooth_idf=False,將 “1” 計數添加到 idf 而不是 idf 的分母:

Python機器學習庫Sklearn系列教程(3)-特徵提取

<code># ===================Tf–idf 項加權===================
from sklearn.feature_extraction.text import TfidfTransformer
transformer = TfidfTransformer(smooth_idf=False) # smooth_idf=False,將 “1” 計數添加到 idf 而不是 idf 的分母
# transformer = TfidfTransformer()
# 第一個特徵經常出現,所以作用不大,第二個特徵和第三個特徵不經常出現,所以比較重要
counts = [[3, 0, 1],
[2, 0, 0],
[3, 0, 0],
[4, 0, 0],
[3, 2, 0],
[3, 0, 2]]
tfidf = transformer.fit_transform(counts)
print('稀疏矩陣存儲:\\n',tfidf)

print('二維矩陣存儲:\\n',tfidf.toarray())
print('特徵權重:',transformer.idf_)/<code>

圖像特徵提取

參考python中opencv的使用。

OpenCV安裝

<code>pip install --upgrade setuptools
pip install numpy Matplotlib
pip install opencv-python/<code>


OpenCV的結構

和Python一樣,當前的OpenCV也有兩個大版本,OpenCV2和OpenCV3。相比OpenCV2,OpenCV3提供了更強的功能和更多方便的特性。不過考慮到和深度學習框架的兼容性,以及上手安裝的難度,這部分先以2為主進行介紹。

根據功能和需求的不同,OpenCV中的函數接口大體可以分為如下部分:

  • core:核心模塊,主要包含了OpenCV中最基本的結構(矩陣,點線和形狀等),以及相關的基礎運算/操作。
  • imgproc:圖像處理模塊,包含和圖像相關的基礎功能(濾波,梯度,改變大小等),以及一些衍生的高級功能(圖像分割,直方圖,形態分析和邊緣/直線提取等)。
  • highgui:提供了用戶界面和文件讀取的基本函數,比如圖像顯示窗口的生成和控制,圖像/視頻文件的IO等。

如果不考慮視頻應用,以上三個就是最核心和常用的模塊了。針對視頻和一些特別的視覺應用,OpenCV也提供了強勁的支持:

  • video:用於視頻分析的常用功能,比如光流法(Optical Flow)和目標跟蹤等。
  • calib3d:三維重建,立體視覺和相機標定等的相關功能。
  • features2d:二維特徵相關的功能,主要是一些不受專利保護的,商業友好的特徵點檢測和匹配等功能,比如ORB特徵。
  • object:目標檢測模塊,包含級聯分類和Latent SVM
  • ml:機器學習算法模塊,包含一些視覺中最常用的傳統機器學習算法。
  • flann:最近鄰算法庫,Fast Library for Approximate Nearest Neighbors,用於在多維空間進行聚類和檢索,經常和關鍵點匹配搭配使用。
  • gpu:包含了一些gpu加速的接口,底層的加速是CUDA實現。
  • photo:計算攝像學(Computational Photography)相關的接口,當然這只是個名字,其實只有圖像修復和降噪而已。
  • stitching:圖像拼接模塊,有了它可以自己生成全景照片。
  • nonfree:受到專利保護的一些算法,其實就是SIFT和SURF。
  • contrib:一些實驗性質的算法,考慮在未來版本中加入的。
  • legacy:字面是遺產,意思就是廢棄的一些接口,保留是考慮到向下兼容。
  • ocl:利用OpenCL並行加速的一些接口。
  • superres:超分辨率模塊,其實就是BTV-L1(Biliteral Total Variation – L1 regularization)算法
  • viz:基礎的3D渲染模塊,其實底層就是著名的3D工具包VTK(Visualization Toolkit)。

從使用的角度來看,和OpenCV2相比,OpenCV3的主要變化是更多的功能和更細化的模塊劃分。

opencv基礎操作

存取圖像

主要包含圖像的讀取、存儲、圖片模式的轉換、格式的轉換。

<code>#導入cv模塊
import cv2 as cv
# 讀取一張400x600分辨率的圖像
color_img = cv.imread('img/src_1000x1000.jpg')
print(color_img.shape)
# 直接讀取單通道灰度圖
gray_img = cv.imread('img/src_1000x1000.jpg', cv.IMREAD_GRAYSCALE)
print(gray_img.shape)
# 把單通道圖片保存後,再讀取,仍然是3通道,相當於把單通道值複製到3個通道保存
cv.imwrite('img/test_grayscale.jpg', gray_img)
reload_grayscale = cv.imread('img/test_grayscale.jpg')
print(reload_grayscale.shape)
# cv2.IMWRITE_JPEG_QUALITY指定jpg質量,範圍0到100,默認95,越高畫質越好,文件越大
cv.imwrite('img/test_imwrite.jpg', color_img, (cv.IMWRITE_JPEG_QUALITY, 80))
# cv2.IMWRITE_PNG_COMPRESSION指定png質量,範圍0到9,默認3,越高文件越小,畫質越差
cv.imwrite('img/test_imwrite.png', color_img, (cv.IMWRITE_PNG_COMPRESSION, 5))/<code>


縮放,裁剪和補邊

主要包括圖片大小縮放(比例縮放、按指定值縮放)、局部裁剪、周邊補色

<code>#導入cv模塊
import cv2 as cv
# 讀取一張原始圖片
img = cv.imread('img/src_1000x1000.jpg')
# 縮放成200x200的方形圖像
img_200x200 = cv.resize(img, (200, 200))
# 不直接指定縮放後大小,通過fx和fy指定縮放比例,0.5則長寬都為原來一半
# 等效於img_100x100 = cv2.resize(img, (100, 100)),注意指定大小的格式是(寬度,高度)
# 插值方法默認是cv2.INTER_LINEAR,這裡指定為最近鄰插值
img_100x100 = cv.resize(img_200x200, (0, 0), fx=0.5, fy=0.5,interpolation=cv.INTER_NEAREST)
# 在上張圖片的基礎上,上下各貼50像素的黑邊,生成300x300的圖像
img_200x100 = cv.copyMakeBorder(img_100x100, 50, 50, 0, 0,cv.BORDER_CONSTANT,value=(0, 0, 0))
# 對照片中局部進行剪裁
patch_img = img[220:550, -180:-50]
cv.imwrite('img/cropped_img.jpg', patch_img)
cv.imwrite('img/resized_200x200.jpg', img_200x200)
cv.imwrite('img/resized_100x100.jpg', img_100x100)
cv.imwrite('img/bordered_200x100.jpg', img_200x100)/<code>


色調,明暗,直方圖和Gamma曲線

主要包含色度、飽和度、明暗的條件。

<code>import cv2 as cv
img = cv.imread('img/src_1000x1000.jpg')
# 通過cv2.cvtColor把圖像從BGR轉換到HSV

img_hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)
# H空間中,綠色比黃色的值高一點,所以給每個像素+15,黃色的樹葉就會變綠
turn_green_hsv = img_hsv.copy()
turn_green_hsv[:, :, 0] = (turn_green_hsv[:, :, 0]+15) % 180
turn_green_img = cv.cvtColor(turn_green_hsv, cv.COLOR_HSV2BGR)
cv.imwrite('img/turn_green.jpg', turn_green_img)
# 減小飽和度會讓圖像損失鮮豔,變得更灰
colorless_hsv = img_hsv.copy()
colorless_hsv[:, :, 1] = 0.5 * colorless_hsv[:, :, 1]
colorless_img = cv.cvtColor(colorless_hsv, cv.COLOR_HSV2BGR)
cv.imwrite('img/colorless.jpg', colorless_img)
# 減小明度為原來一半
darker_hsv = img_hsv.copy()
darker_hsv[:, :, 2] = 0.5 * darker_hsv[:, :, 2]
darker_img = cv.cvtColor(darker_hsv, cv.COLOR_HSV2BGR)
cv.imwrite('img/darker.jpg', darker_img)/<code>


圖像的仿射變換

主要包括縮放、旋轉、剪切、翻轉、平移,以及他們之間的組合

<code>#導入cv模塊
import cv2 as cv
import numpy as np
# =====================圖像的仿射變換===============================
# 仿射變換具體到圖像中的應用,主要是對圖像的縮放,旋轉,剪切,翻轉和平移的組合。
# 讀取一張原始照片
img = cv.imread('img/src_400x600.jpg')
# 沿著橫縱軸放大1.6倍,然後平移(-150,-240),最後沿原圖大小截取,等效於裁剪並放大

M_crop_elephant = np.array([
[1.6, 0, -150],
[0, 1.6, -240]
], dtype=np.float32)
img_elephant = cv.warpAffine(img, M_crop_elephant, (400, 600))
cv.imwrite('img/lanka_elephant.jpg', img_elephant)
# x軸的剪切變換,角度15°
theta = 15 * np.pi / 180
M_shear = np.array([
[1, np.tan(theta), 0],
[0, 1, 0]
], dtype=np.float32)
img_sheared = cv.warpAffine(img, M_shear, (400, 600))
cv.imwrite('img/lanka_safari_sheared.jpg', img_sheared)
# 順時針旋轉,角度15°
M_rotate = np.array([
[np.cos(theta), -np.sin(theta), 0],
[np.sin(theta), np.cos(theta), 0]
], dtype=np.float32)
img_rotated = cv.warpAffine(img, M_rotate, (400, 600))
cv.imwrite('img/lanka_safari_rotated.jpg', img_rotated)
# 某種變換,具體旋轉+縮放+旋轉組合可以通過SVD分解理解
M = np.array([
[1, 1.5, -400],
[0.5, 2, -100]
], dtype=np.float32)
img_transformed = cv.warpAffine(img, M, (400, 600))
cv.imwrite('img/lanka_safari_transformed.jpg', img_transformed)/<code>


基本繪圖

可以在畫面上繪製線段,圓,矩形和多邊形等,還可以在圖像上指定位置打印文字

<code>#導入cv模塊
import cv2 as cv
import numpy as np
# 可以在畫面上繪製線段,圓,矩形和多邊形等,還可以在圖像上指定位置打印文字

import numpy as np
import cv2
# 定義一塊寬600,高400的畫布,初始化為白色
canvas = np.zeros((400, 600, 3), dtype=np.uint8) + 255
# 畫一條縱向的正中央的黑色分界線
cv2.line(canvas, (300, 0), (300, 399), (0, 0, 0), 2)
# 畫一條右半部份畫面以150為界的橫向分界線
cv2.line(canvas, (300, 149), (599, 149), (0, 0, 0), 2)
# 左半部分的右下角畫個紅色的圓
cv2.circle(canvas, (200, 300), 75, (0, 0, 255), 5)
# 左半部分的左下角畫個藍色的矩形
cv2.rectangle(canvas, (20, 240), (100, 360), (255, 0, 0), thickness=3)
# 定義兩個三角形,並執行內部綠色填充
triangles = np.array([
[(200, 240), (145, 333), (255, 333)],
[(60, 180), (20, 237), (100, 237)]])
cv2.fillPoly(canvas, triangles, (0, 255, 0))
# 畫一個黃色五角星
# 第一步通過旋轉角度的辦法求出五個頂點
phi = 4 * np.pi / 5
rotations = [[[np.cos(i * phi), -np.sin(i * phi)], [i * np.sin(phi), np.cos(i * phi)]] for i in range(1, 5)]
pentagram = np.array([[[[0, -1]] + [np.dot(m, (0, -1)) for m in rotations]]], dtype=np.float)
# 定義縮放倍數和平移向量把五角星畫在左半部分畫面的上方
pentagram = np.round(pentagram * 80 + np.array([160, 120])).astype(np.int)
# 將5個頂點作為多邊形頂點連線,得到五角星
cv2.polylines(canvas, pentagram, True, (0, 255, 255), 9)
# 按像素為間隔從左至右在畫面右半部份的上方畫出HSV空間的色調連續變化
for x in range(302, 600):
color_pixel = np.array([[[round(180*float(x-302)/298), 255, 255]]], dtype=np.uint8)
line_color = [int(c) for c in cv2.cvtColor(color_pixel, cv2.COLOR_HSV2BGR)[0][0]]
cv2.line(canvas, (x, 0), (x, 147), line_color)

# 如果定義圓的線寬大於半斤,則等效於畫圓點,隨機在畫面右下角的框內生成座標
np.random.seed(42)
n_pts = 30
pts_x = np.random.randint(310, 590, n_pts)
pts_y = np.random.randint(160, 390, n_pts)
pts = zip(pts_x, pts_y)
# 畫出每個點,顏色隨機
for pt in pts:
pt_color = [int(c) for c in np.random.randint(0, 255, 3)]
cv2.circle(canvas, pt, 3, pt_color, 5)
# 在左半部分最上方打印文字
cv2.putText(canvas,
'打印的文字just english',
(5, 15),
cv2.FONT_HERSHEY_SIMPLEX,
0.5,
(0, 0, 0),
1)
cv2.imshow('窗口名稱', canvas)
cv2.waitKey()/<code>


相機功能

一個是VideoCapture,用於獲取相機設備並捕獲圖像和視頻,或是從文件中捕獲。還有一個VideoWriter,用於生成視頻。

下面的代碼會根據電腦攝像頭捕捉到的信息,在img文件夾下生成一個save.avi的視頻文件。

<code>#導入cv模塊
import time
import cv2
import os
import sys

interval = 1 # 捕獲圖像的間隔,單位:秒
num_frames = 50 # 捕獲圖像的總幀數
out_fps = 24 # 輸出文件的幀率
# VideoCapture(0)表示打開默認的相機
cap = cv2.VideoCapture(0)
# 獲取捕獲的分辨率
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
# 設置要保存視頻的編碼,分辨率和幀率
video = cv2.VideoWriter(
"img/save.avi",
cv2.VideoWriter_fourcc('M', 'P', '4', '2'), # 視頻編碼格式參考 http://www.fourcc.org/codecs.php
out_fps,
size
)
# 對於一些低畫質的攝像頭,前面的幀可能不穩定,略過
for i in range(42):
cap.read()
# 開始捕獲,通過read()函數獲取捕獲的幀
try:
for i in range(num_frames):
_, frame = cap.read()
video.write(frame)
# 如果希望把每一幀也存成文件,比如製作GIF,則允許下面的代碼運行
# filename = '{:0>6d}.png'.format(i)
# cv2.imwrite(filename, frame)
print('Frame {} is captured.'.format(i))
time.sleep(interval)
except KeyboardInterrupt:
# 捕獲提前停止。方便後面使已經捕獲好的部分視頻可以順利生成
print('Stopped! {}/{} frames captured!'.format(i, num_frames))
# 釋放資源並寫入視頻文件
video.release()

cap.release()/<code>


視頻文件功能

間隔讀取視頻文件中的每幀的圖片。

<code>frame_path="img/frames"  # 存放視頻截圖的文件夾路徑
# 第二個輸入參數是設定每隔多少幀截取一幀
frame_interval = 1
if not os.path.exists(frame_path):
os.mkdir(frame_path)
# 初始化一個VideoCapture對象
cap = cv2.VideoCapture()
filepath = "img/save.avi"
# VideoCapture::open函數可以從文件獲取視頻
cap.open(filepath)
# 獲取視頻幀數
n_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
print(n_frames)
# 同樣為了避免視頻頭幾幀質量低下,黑屏或者無關等
for i in range(2):
cap.read()
for i in range(2,n_frames): # 讀取後面的幀數據
ret, frame = cap.read()
# 每隔frame_interval幀進行一次截屏操作
if i % frame_interval == 0:
imagepath = frame_path+'/video_test'+ str(i)+".jpg"
print('導出 {}!'.format(imagepath))
cv2.imwrite(imagepath, frame)
# 執行結束釋放資源
cap.release()/<code>


交互——播放圖片

窗口播放圖片、循環播放圖片

<code>#導入cv模塊
import time
import cv2
import os
import sys
from itertools import cycle
# =====================OpenCV窗口顯示===============================
img = cv2.imread('img/src_1000x1000.jpg')
cv2.imshow('窗口標題', img)
cv2.waitKey()
# =====================OpenCV窗口循環===============================
frame_path="img/frames" # 圖片的文件夾路徑
# 列出frames文件夾下的所有圖片
filenames = os.listdir(frame_path)
# 通過itertools.cycle生成一個無限循環的迭代器,每次迭代都輸出下一張圖像對象
img_iter = cycle([cv2.imread(os.sep.join([frame_path, x])) for x in filenames])
key = 0
while key & 0xFF != 27:
cv2.imshow('window title', next(img_iter))
key = cv2.waitKey(500) # cv2.waitKey()參數不為零的時候則可以和循環結合產生動態畫面/<code>


交互——鼠標事件

主要包括為窗口綁定鼠標事件的處理。

<code>#導入cv模塊
import time
import cv2
import os
import sys

from itertools import cycle
# 定義鼠標事件回調函數
def on_mouse(event, x, y, flags, param):
# 鼠標左鍵按下,抬起,雙擊
if event == cv2.EVENT_LBUTTONDOWN:
print('左鍵按下 ({}, {})'.format(x, y))
elif event == cv2.EVENT_LBUTTONUP:
print('左鍵彈起 ({}, {})'.format(x, y))
elif event == cv2.EVENT_LBUTTONDBLCLK:
print('左鍵雙擊 ({}, {})'.format(x, y))
# 鼠標右鍵按下,抬起,雙擊
elif event == cv2.EVENT_RBUTTONDOWN:
print('右鍵按下 ({}, {})'.format(x, y))
elif event == cv2.EVENT_RBUTTONUP:
print('右鍵彈起 ({}, {})'.format(x, y))
elif event == cv2.EVENT_RBUTTONDBLCLK:
print('右鍵雙擊 ({}, {})'.format(x, y))
# 鼠標中/滾輪鍵(如果有的話)按下,抬起,雙擊
elif event == cv2.EVENT_MBUTTONDOWN:
print('中間鍵按下 ({}, {})'.format(x, y))
elif event == cv2.EVENT_MBUTTONUP:
print('中間鍵彈起 ({}, {})'.format(x, y))
elif event == cv2.EVENT_MBUTTONDBLCLK:
print('中間鍵雙擊 ({}, {})'.format(x, y))
# 鼠標移動
elif event == cv2.EVENT_MOUSEMOVE:
print('移動到 ({}, {})'.format(x, y))
# 為指定的窗口綁定自定義的回調函數
cv2.namedWindow('window title')
cv2.setMouseCallback('window title', on_mouse) # 第一個參數為要綁定的窗口名稱,第二個參數為要綁定的鼠標事件
cv2.waitKey()

/<code>


分享到:


相關文章: