Python 線性分類模型簡介
(點擊
上方藍字
,快速關注我們)
編譯:伯樂在線 - bound
如有好文章投稿,請點擊 → 這裡了解詳情
在過去幾周中,我們開始對機器學習有了更多的了解,也認識到機器學習在機器視覺、圖像分類和深度學習領域的重要作用。
我們已經看到卷積神經網路,如LetNet,可以用於對MNIST數據集的手寫字跡進行分類。我們使用了k-NN演算法來識別一張圖片中是否含有貓或狗,並且我們也已經學習了如何調參來優化模型,進而提高分類精度。
然而,還有一個重要的機器學習的演算法我們尚未涉及:這個演算法非常容易構建,並能很自然地擴展到神經網路和卷積神經網路中。
是什麼演算法呢?
它是一個簡單的線性分類器,並且由於其演算法很直觀,被認為是更多高級的機器學習和深度學習演算法的基石。
繼續閱讀來加深你對線性分類器的認識,以及如何使用它們進行圖像分類。
Python線性分類模型簡介
本教程的前半部分主要關注線性分類有關的基本原理和數學知識。總的來說,線性分類指的是那些真正從訓練數據中「學習」的有參分類演算法。
在這裡,我提供了一個真正的線性分類實現代碼,以及一個用scikit-learn對一張圖片中的內容分類的例子。
4大參數化學習和線性分類的組件
我已經多次使用「參數化」,但它到底是什麼意思?
簡而言之:參數化是確定模型必要參數的過程。
在機器學習的任務中,參數化根據以下幾個方面來確定面對的問題:
數據
:這是我們將要學習的輸入數據。這些數據包括了數據點(例如,特徵向量,顏色矩陣,原始像素特徵等)和它們對應的標籤。
評分函數
:一個函數接收輸入數據,然後將數據匹配到類標籤上。例如,我們輸入特徵向量後,評分函數就開始處理這些數據,調用某個函數f(比如我們的評分函數),最後返回預測的分類標籤。
損失函數
:損失函數可以量化預測的類標籤與真實的類標籤之間的匹配程度。這兩個標籤集合之間的相似度越高,損失就越小(即分類精度越高)。我們的目標是最小化損失函數,相應就能提高分類精度。
權重矩陣
:權重矩陣,通常標記為W,它是分類器實際優化的權重或參數。我們根據評分函數的輸出以及損失函數,調整並優化權重矩陣,提高分類精度。
注意:取決於你使用的模型的種類,參數可能會多的多。但是在最底層,你會經常遇到4個參數化學習的基本模塊。
一旦確定了這4個關鍵組件,我們就可以使用優化方法來找到評分函數的一組參數W,使得損失函數達到最小值(同時提升對數據的分類準確度)
接下來,我們就將看到如何利用這些組件,搭建一個將輸入數據轉化為真實預測值的分類器。
線性分類器:從圖片到標籤
在這部分中,我們將更多的從數學角度研究參數模型到機器學習。
首先,我們需要數據。假設訓練數據集(圖片或者壓縮後的特徵向量)標記為,每個圖或特徵向量都有對應的類標籤
。我們用
和
表示有N個D維(特徵向量的長度)的數據點,劃分到K個唯一的類別中。
為了這些表達式更具體點,參考我們以前的教程:基於提取後的顏色矩陣,使用knn分類器識別圖片中的貓和狗。
這份數據集中,總共有N=25,000張圖片,每張圖片的特徵都是一個3D顏色直方圖,其中每個管道有8個桶。這樣產出的特徵向量有D=8 x 8 x 8 = 512個元素。最後,我們有k=2個類別標籤,一個是「狗」,另一個是「貓」。
有了這些變數後,我們必須定義一個評分函數f,將特徵向量映射到類標籤的打分上。正如本篇博客標題所說,我們將使用一個簡單的線性映射:
我們假設每個都由一個形狀為[D x 1]的單列向量所表示。我們在本例中要再次使用顏色直方圖,不過如果我們使用的是原始像素粒度,那可以直接把圖片中的像素壓扁到一個單列向量中。
我們的權重矩陣W形狀為[K x D](類別標籤數乘以特徵向量的維數)
最後,偏置矩陣b,大小為[K x 1]。實質上,偏置矩陣可以讓我們的評分函數向著一個或另一個方向「提升」,而不會真正影響權重矩陣W,這點往往對學習的成功與否非常關鍵。
回到Kaggle的貓和狗例子中, 每個都表示為512維顏色直方圖,因此的形狀是[512 x 1]。權重矩陣W的形狀為[2 x 512],而偏置矩陣b為[2 x 1]。
下面展示的是線性分類的評分函數f
在上圖的左側是原始輸入圖片,我們將從中提取特徵。在本例中,我們計算的是一個512維的顏色直方圖,也可以用其他一些特徵表示方式(包括原始像素密度),但是對於這個例子,我們就只用顏色分布,即直方圖來表示xi。
然後我們有了權重矩陣W,有2行(每個類標籤一行)和512列(每一列都是特徵向量中的條目)
將W和xi點乘後,再加上大小為[2 x 1]的偏置矩陣bi。
最後就得到了右邊的兩個值:貓和狗標籤各自的分數。
看著上面的公式,你可以確信輸入xi和yi都是固定的,沒法修改。我們當然可以通過不同的特徵提取技術來得到不同的xi,但是一旦特徵抽取後,這些值就不會再改變了。
實際上,我們唯一能控制的參數就是權重矩陣W以及偏置向量b。因此,我們的目標是利用評分函數和損失函數去優化權重和偏置向量,來提升分類的準確度。
如何優化權重矩陣則取決於我們的損失函數,但通常會涉及梯度下降的某種形式。我們會在以後的博客中重溫優化和損失函數的概念,不過現在只要簡單理解為給定了一個評分函數後,我們還需要定義一個損失函數,來告訴我們對於輸入數據的預測有多「好」。
參數學習和線性分類的優點
利用參數學習有兩個主要的優點,正如我上面詳述的方法:
一旦我們訓練完了模型,就可以丟掉輸入數據而只保留權重矩陣W和偏置向量b。這大大減少了模型的大小,因為我們只需要存儲兩個向量集合(而非整個訓練集)。
對新的測試數據分類很快。為了執行分類,我們要做的只是點乘W和xi,然後再加上偏置b。這樣做遠比將每個測試點和整個訓練集比較(比如像knn演算法那樣)快的多。
既然我們理解了線性分類的原理,就一起看下如何在python,opencv和scikit-learn中實現。
使用python,opencv和scikit-learn對圖片線性分類
就像在之前的例子Kaggle 貓vs狗數據集和knn演算法中,我們將從數據集中提取顏色直方圖,不過和前面例子不同的是,我們將用一個線性分類器,而非knn。
準確地說,我們將使用線性支持向量機(SVM),即在n維空間中的數據之間構造一個最大間隔分離超平面。這個分離超平面的目標是將類別為i的所有(或者在給定的容忍度下,儘可能多)的樣本分到超平面的一邊,而所有類別非i的樣本分到另一邊。
關於支持向量機的具體描述已經超出本博客的範圍。(不過在PyImageSearch Gurus course有詳細描述)
同時,只需知道我們的線性SVM使用了和本博客「線性分類器:從圖片到標籤」部分中相似的評分函數,然後使用損失函數,用於確定最大分離超平面來對數據點分類(同樣,我們將在以後的博客中講述損失函數)。
我們從打開一個新文件開始,命名為linear_classifier.py,然後插入以下代碼:
# import the necessary packages
from
sklearn
.
preprocessing
import
LabelEncoder
from
sklearn
.
svm
import
LinearSVC
from
sklearn
.
metrics
import
classification_report
from
sklearn
.
cross_validation
import
train_test_split
from
imutils
import
paths
import
numpy
as
np
import
argparse
import
imutils
import
cv2
import
os
從第2行至11行導入了必須的python包。我們要使用scikit-learn庫,因此如果你還沒安裝的話,跟著這些步驟,確保將其安裝到你機器上。
我們還將使用我的imutils包,用於方便處理圖像的一系列函數。如果你還沒有安裝imutils,那就讓pip安裝。
$ pip install imutils
現在我們定義extract_color_histogram 函數,用於提取和量化輸入圖片的內容:
def
extract_color_histogram
(
image
,
bins
=
(
8
,
8
,
8
))
:
# extract a 3D color histogram from the HSV color space using
# the supplied number of `bins` per channel
hsv
=
cv2
.
cvtColor
(
image
,
cv2
.
COLOR_BGR2HSV
)
hist
=
cv2
.
calcHist
([
hsv
],
[
0
,
1
,
2
],
None
,
bins
,
[
0
,
180
,
0
,
256
,
0
,
256
])
# handle normalizing the histogram if we are using OpenCV 2.4.X
if
imutils
.
is_cv2
()
:
hist
=
cv2
.
normalize
(
hist
)
# otherwise, perform "in place" normalization in OpenCV 3 (I
# personally hate the way this is done
else
:
cv2
.
normalize
(
hist
,
hist
)
# return the flattened histogram as the feature vector
return
hist
.
flatten
()
這個函數接收一個輸入image ,將其轉化為HSV顏色空間,然後利用為每個通道提供的bins,計算3D顏色直方圖。
利用cv2.calcHist函數計算出顏色直方圖後,將其歸一化後返回給調用函數。
有關extract_color_histogram方法更詳細的描述,參閱這篇博客(http://www.pyimagesearch.com/2016/08/08/k-nn-classifier-for-image-classification/)。
接著,我們從命令行解析參數,並初始化幾個變數:
# import the necessary packages
from
sklearn
.
preprocessing
import
LabelEncoder
from
sklearn
.
svm
import
LinearSVC
from
sklearn
.
metrics
import
classification_report
from
sklearn
.
cross_validation
import
train_test_split
from
imutils
import
paths
import
numpy
as
np
import
argparse
import
imutils
import
cv2
import
os
def
extract_color_histogram
(
image
,
bins
=
(
8
,
8
,
8
))
:
# extract a 3D color histogram from the HSV color space using
# the supplied number of `bins` per channel
hsv
=
cv2
.
cvtColor
(
image
,
cv2
.
COLOR_BGR2HSV
)
hist
=
cv2
.
calcHist
([
hsv
],
[
0
,
1
,
2
],
None
,
bins
,
[
0
,
180
,
0
,
256
,
0
,
256
])
# handle normalizing the histogram if we are using OpenCV 2.4.X
if
imutils
.
is_cv2
()
:
hist
=
cv2
.
normalize
(
hist
)
# otherwise, perform "in place" normalization in OpenCV 3 (I
# personally hate the way this is done
else
:
cv2
.
normalize
(
hist
,
hist
)
# return the flattened histogram as the feature vector
return
hist
.
flatten
()
# construct the argument parse and parse the arguments
ap
=
argparse
.
ArgumentParser
()
ap
.
add_argument
(
"-d"
,
"--dataset"
,
required
=
True
,
help
=
"path to input dataset"
)
args
=
vars
(
ap
.
parse_args
())
# grab the list of images that we"ll be describing
(
"[INFO] describing images..."
)
imagePaths
=
list
(
paths
.
list_images
(
args
[
"dataset"
]))
# initialize the data matrix and labels list
data
=
[]
labels
=
[]
33行到36行解析命令行參數。我們這裡只需要一個簡單的開關,—dataset是kaggle 貓vs狗數據集的路徑。
然後我們將25000張圖片保存的磁碟位置賦值給imagePaths,
跟著初始化一個data矩陣,存儲提取後的特徵向量和類別labels。
說到提取特徵,我們接著這樣做:
# loop over the input images
for
(
i
,
imagePath
)
in
enumerate
(
imagePaths
)
:
# load the image and extract the class label (assuming that our
# path as the format: /path/to/dataset/{class}.{image_num}.jpg
image
=
cv2
.
imread
(
imagePath
)
label
=
imagePath
.
split
(
os.path
.
sep
)[
-
1
].
split
(
"."
)[
0
]
# extract a color histogram from the image, then update the
# data matrix and labels list
hist
=
extract_color_histogram
(
image
)
data
.
append
(
hist
)
labels
.
append
(
label
)
# show an update every 1,000 images
if
i
>
0
and
i
%
1000
==
0
:
(
"[INFO] processed {}/{}"
.
format
(
i
,
len
(
imagePaths
)))
在47行,我們開始對輸入的imagePaths進行遍歷,對於每個imagePath,我們從磁碟中載入image,提取類別label,然後通過計算顏色直方圖來量化圖片。然後我們更新data和labels各自的列表。
目前,我們的labels是一個字元串列表,如「狗」或者「貓」。但是,很多scikit-learn中的機器學習演算法傾向於將labels編碼為整數,每個標籤有一個唯一的數字。
使用LabelEncoder類可以很方便的將類別標籤從字元串轉變為整數:
# import the necessary packages
from
sklearn
.
preprocessing
import
LabelEncoder
from
sklearn
.
svm
import
LinearSVC
調用了 .fit_transform方法後,現在我們的labels表示為整數列表。
代碼的最後一部分將數據劃分為訓練和測試兩組、訓練線性SVM、評估模型。
# partition the data into training and testing splits, using 75%
# of the data for training and the remaining 25% for testing
(
"[INFO] constructing training/testing split..."
)
(
trainData
,
testData
,
trainLabels
,
testLabels
)
=
train_test_split
(
np
.
array
(
data
),
labels
,
test_size
=
0.25
,
random_state
=
42
)
# train the linear regression clasifier
(
"[INFO] training Linear SVM classifier..."
)
model
=
LinearSVC
()
model
.
fit
(
trainData
,
trainLabels
)
# evaluate the classifier
(
"[INFO] evaluating classifier..."
)
predictions
=
model
.
predict
(
testData
)
(
classification_report
(
testLabels
,
predictions
,
target_names
=
le
.
classes_
))
70和71行構造了訓練集和測試集。我們將75%的數據用於訓練,剩下的25%用於測試。
我們將使用scikit-learn庫實現的LinearSVC(75和76行)來訓練線性SVM。
最後,80到82行評估我們的模型,顯示一個格式整齊的報告,來說明模型的執行情況。
需要注意的一點是,我故意沒有進行調參,只是為了讓這個例子簡單,容易理解。不過,既然提到了,我就把LinearSVC 分類器的調參作為練習留個讀者。可以參考我以前的k-NN分類器調參博客。
評估線性分類器
為了測試我們的線性分類器,確保你已經下載了:
1. 本博客中的源代碼,可以使用教程底部的「下載」部分。
2. kaggle 貓vs狗數據集
有了代碼和數據集之後,你可以執行如下命令:
$ python linear_classifier.py --dataset kaggle_dogs_vs_cats
特徵提取過程大約要花費1-3分鐘不等,具體時間根據機器的速度。
之後,訓練並評估我們的線性SVM:
正如上圖所示,我們的分類精度有64%,大致接近本教程中調參後的knn演算法精度。
注意:對線性SVM調參可以得到更高的分類精度,為了使教程稍微短一點,而且不至於太複雜,我簡單地省略了這個步驟。
此外,我們不僅得到了和knn相同的分類精度,模型的測試時間也快的多,只需要將權重矩陣和數據集進行點乘(高度優化後),然後是一個簡單的加法。
我們也可以在訓練完成後丟棄訓練集,只保留權重矩陣W和偏置向量b,從而大大精簡了模型表示。
總結
在今天的博客中,我討論了參數學習和線性分類的基礎概念。雖然線性分類器比較簡單, 但它被視為更多高級的機器學習和深度學習演算法的基石,並能很自然地擴展到神經網路和卷積神經網路中。
你看,卷積神經網路可以將原始像素映射到類別標籤,類似於我們在本教程中所作的那些,只是評分函數f更複雜,並且參數更多。
參數學習的一大好處就是可以在訓練完畢之後,丟棄原訓練數據。我們可以只用從數據中學到的參數(比如,權重矩陣和偏置向量)來進行分類。
這使得分類變得非常高效,因為(1)我們不需要像knn那樣在模型中存儲一份訓練數據的拷貝(2)我們不用將測試圖片一個一個的和訓練圖片進行比較(一個O(N)的操作,並且當數據集很大時就變得非常麻煩)
簡而言之,這個方法明顯更快,只需一個簡單的點乘和加法。非常簡潔,不是嗎?
最後,我們在kaggle 狗vs貓的數據集上,利用Python, OpenCV, 和 scikit-learn進行線性分類。從數據集提取出顏色直方圖之後,我們在特徵向量上訓練一個線性支持向量機,並且得到64%的分類精度,這已經很不錯了,因為(1)顏色直方圖並非狗和貓特徵化的最佳選擇(2)我們沒有對線性SVM進行調參。
到這裡,我們開始理解了構建神經網路、卷積神經網路和深度學習模型的基本模塊,但還有很長一段路要走。
首先,我們需要更詳細地了解損失函數,特別是如何使用損失函數來優化權重矩陣以獲得更準確的預測。 未來的博客文章將更詳細地介紹這些概念。
看完本文有收穫?請轉發分享給更多人
關注「大數據與機器學習文摘」,成為Top 1%


※頂會審稿人領你打開深度學習的大門
※Python 標準庫筆記:string模塊
※那些有趣/用的 Python 庫,15篇 Python 技術熱文
※Python 判斷文件是否存在的三種方法
※Greenlet 詳解
TAG:Python開發者 |