當前位置:
首頁 > 知識 > Python 線性分類模型簡介

Python 線性分類模型簡介

(點擊

上方藍字

,快速關注我們)




編譯:伯樂在線 - bound


如有好文章投稿,請點擊 → 這裡了解詳情




在過去幾周中,我們開始對機器學習有了更多的了解,也認識到機器學習在機器視覺、圖像分類和深度學習領域的重要作用。




我們已經看到卷積神經網路,如LetNet,可以用於對MNIST數據集的手寫字跡進行分類。我們使用了k-NN演算法來識別一張圖片中是否含有貓或狗,並且我們也已經學習了如何調參來優化模型,進而提高分類精度。



然而,還有一個重要的機器學習的演算法我們尚未涉及:這個演算法非常容易構建,並能很自然地擴展到神經網路和卷積神經網路中。




是什麼演算法呢?




它是一個簡單的線性分類器,並且由於其演算法很直觀,被認為是更多高級的機器學習和深度學習演算法的基石。




繼續閱讀來加深你對線性分類器的認識,以及如何使用它們進行圖像分類。






Python線性分類模型簡介




本教程的前半部分主要關注線性分類有關的基本原理和數學知識。總的來說,線性分類指的是那些真正從訓練數據中「學習」的有參分類演算法。




在這裡,我提供了一個真正的線性分類實現代碼,以及一個用scikit-learn對一張圖片中的內容分類的例子。




4大參數化學習和線性分類的組件




我已經多次使用「參數化」,但它到底是什麼意思?



簡而言之:參數化是確定模型必要參數的過程。




在機器學習的任務中,參數化根據以下幾個方面來確定面對的問題:






  1. 數據

    :這是我們將要學習的輸入數據。這些數據包括了數據點(例如,特徵向量,顏色矩陣,原始像素特徵等)和它們對應的標籤。



  2. 評分函數

    :一個函數接收輸入數據,然後將數據匹配到類標籤上。例如,我們輸入特徵向量後,評分函數就開始處理這些數據,調用某個函數f(比如我們的評分函數),最後返回預測的分類標籤。



  3. 損失函數

    :損失函數可以量化預測的類標籤與真實的類標籤之間的匹配程度。這兩個標籤集合之間的相似度越高,損失就越小(即分類精度越高)。我們的目標是最小化損失函數,相應就能提高分類精度。



  4. 權重矩陣

    :權重矩陣,通常標記為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。因此,我們的目標是利用評分函數和損失函數去優化權重和偏置向量,來提升分類的準確度。




如何優化權重矩陣則取決於我們的損失函數,但通常會涉及梯度下降的某種形式。我們會在以後的博客中重溫優化和損失函數的概念,不過現在只要簡單理解為給定了一個評分函數後,我們還需要定義一個損失函數,來告訴我們對於輸入數據的預測有多「好」。




參數學習和線性分類的優點




利用參數學習有兩個主要的優點,正如我上面詳述的方法:






  1. 一旦我們訓練完了模型,就可以丟掉輸入數據而只保留權重矩陣W和偏置向量b。這大大減少了模型的大小,因為我們只需要存儲兩個向量集合(而非整個訓練集)。



  2. 對新的測試數據分類很快。為了執行分類,我們要做的只是點乘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


print

(

"[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

:


print

(

"[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


print

(

"[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


print

(

"[INFO] training Linear SVM classifier..."

)


model

=

LinearSVC

()


model

.

fit

(

trainData

,

trainLabels

)



# evaluate the classifier


print

(

"[INFO] evaluating classifier..."

)


predictions

=

model

.

predict

(

testData

)


print

(

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開發者 的精彩文章:

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

TAG:Python開發者 |