免費(fèi)Python機(jī)器學(xué)習(xí)課程五:多類分類邏輯回歸
Logistic回歸的兩種方法:梯度下降法和優(yōu)化函數(shù)
邏輯回歸是一種非常流行的機(jī)器學(xué)習(xí)技術(shù)。當(dāng)因變量是分類的時,我們使用邏輯回歸。本文將重點(diǎn)介紹針對多類分類問題的邏輯回歸的實(shí)現(xiàn)。我假設(shè)您已經(jīng)知道如何使用Logistic回歸實(shí)現(xiàn)二進(jìn)制分類。
如果您尚未使用Logistic回歸進(jìn)行二進(jìn)制分類,那么建議您先閱讀本文,然后再深入研究本文。
因?yàn)槎囝惙诸愂墙⒃诙M(jìn)制分類之上的。
您將在本文中學(xué)習(xí)二進(jìn)制分類的概念,公式和工作示例
多類別分類
多類分類的實(shí)現(xiàn)遵循與二進(jìn)制分類相同的思想。如您所知,在二進(jìn)制分類中,我們解決了是或否問題。就像上述文章中的示例一樣,輸出回答了一個人是否患有心臟病的問題。我們只有兩類:心臟病和無心臟病。
如果輸出為1,則該人患有心臟病,如果輸出為0,則該人沒有心臟病。
在多類別分類中,我們有兩個以上的類別。這是一個例子。說,我們具有汽車,卡車,自行車和船的不同特征和特性作為輸入特征。我們的工作是預(yù)測標(biāo)簽(汽車,卡車,自行車或船)。
如何解決呢?
我們將以解決心臟病或無心臟病的方式將每個類別視為二元分類問題。
這種方法稱為"一對多"方法。
在one vs all方法中,當(dāng)我們使用一個類時,該類用1表示,其余類變?yōu)?。
例如,如果我們有四個類別:汽車,卡車,自行車和船。當(dāng)我們在汽車上工作時,我們將汽車用作1,將其余類別用作零。同樣,當(dāng)我們在卡車上工作時,卡車的元素將為1,其余類別為零。

當(dāng)您將其實(shí)現(xiàn)時,它將更加易于理解。我建議您在閱讀時繼續(xù)編碼并運(yùn)行代碼。
在這里,我將以兩種不同的方式實(shí)現(xiàn)此算法:
- 梯度下降法。
- 優(yōu)化功能方法。
重要方程式及其運(yùn)作方式:
Logistic回歸使用S形函數(shù)來預(yù)測輸出。S形函數(shù)返回0到1的值。通常,我們采用一個閾值,例如0.5。如果sigmoid函數(shù)返回的值大于或等于0.5,則將其視為1;如果sigmoid函數(shù)返回的值小于0.5,則將其視為0。

z是輸入要素乘以表示為theta的隨機(jī)初始化值的乘積。

X是輸入要素。在大多數(shù)情況下,有幾種輸入功能。因此,此公式變得很大:

X1,X2,X3是輸入要素,并且將為每個輸入要素隨機(jī)初始化一個theta。開頭的Theta0是偏差項(xiàng)。
該算法的目標(biāo)是在每次迭代時更新此theta,以便它可以在輸入要素和輸出標(biāo)簽之間建立關(guān)系。
成本函數(shù)和梯度下降
成本函數(shù)給出的想法是,我們的預(yù)測與原始輸出相差多遠(yuǎn)。這是該公式:

這里:
- m是訓(xùn)練示例數(shù)或訓(xùn)練數(shù)據(jù)數(shù),
- y是原始輸出標(biāo)簽,
- h是假設(shè)或預(yù)測的輸出。
這是梯度下降的方程式。使用此公式,我們將在每次迭代中更新theta值:

梯度下降法的實(shí)現(xiàn)
先決條件:
- 您需要能夠舒適地讀取和編寫python代碼。
- 基本的Numpy和Pandas庫。
在這里,我將逐步展示實(shí)現(xiàn)。
(1) 導(dǎo)入必要的包和數(shù)據(jù)集。我從安德魯·伍(Andrew Ng)在Coursera的機(jī)器學(xué)習(xí)課程中獲取了數(shù)據(jù)集。這是一個手寫識別數(shù)據(jù)集。從1到10的數(shù)字。
從像素?cái)?shù)據(jù)集中,我們需要識別數(shù)字。在此數(shù)據(jù)集中,輸入變量和輸出變量在Excel文件中的不同工作表中組織。請隨時從本頁末尾的鏈接下載數(shù)據(jù)集。
如果您正在閱讀本文,請運(yùn)行每段代碼以學(xué)習(xí)該算法。
讓我們導(dǎo)入必要的包和數(shù)據(jù)集,
- import pandas as pd
- import numpy as np
- xl = pd.ExcelFile('ex3d1.xlsx')
- df = pd.read_excel(xl, 'X', header=None)

(2) 導(dǎo)入y,它是輸出變量
- y = pd.read_excel(xl, 'y', header = None)

(3) 定義采用輸入變量和theta的假設(shè)。它返回計(jì)算出的輸出變量。
- def hypothesis(theta, X):
- return 1 / (1 + np.exp(-(np.dot(theta, X.T)))) - 0.0000001
(4) 構(gòu)建使用輸入變量,輸出變量和theta的成本函數(shù)。它返回假設(shè)的成本。這意味著它給出了關(guān)于預(yù)測距原始輸出有多遠(yuǎn)的想法。
- def cost(X, y, theta):
- y1 = hypothesis(X, theta)
- return -(1/len(X)) * np.sum(y*np.log(y1) + (1-y)*np.log(1-y1))
(5) 現(xiàn)在,該進(jìn)行數(shù)據(jù)預(yù)處理了。
數(shù)據(jù)是干凈的。不需要太多預(yù)處理。我們需要在輸入變量中添加一個偏差列。請檢查df和y的長度。如果長度不同,則該模型將不起作用。
- print(len(df))
- print(len(y))
- X = pd.concat([pd.Series(1, index=df.index, name='00'), df], axis=1)
(6) y列的數(shù)字從1到10。這意味著我們有10個類別。
y是一個不需要的DataFrame。我只會將列保留為包含值的系列。
- yy = y.iloc[:, 0]
我們將為每個類創(chuàng)建與y相同長度的一列。當(dāng)類為5時,請為該行創(chuàng)建一個包含1的列,否則為5和0。
檢查一下,我們有幾個類,
- y.unique()
輸出:
- array([10, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=int64)
因此,我們有10個班級。啟動一個具有10列和df.shape [0]行數(shù)的DataFrame。
- y1 = np.zeros([df.shape[0], len(y.unique())])
- y1 = pd.DataFrame(y1)
我們將使用一些簡單的代碼以編程方式進(jìn)行操作:
- for i in range(0, len(y.unique())):
- for j in range(0, len(y1)):
- if y[j] == y.unique()[i]:
- y1.iloc[j, i] = 1
- else: y1.iloc[j, i] = 0
- y1.head()
(7) 現(xiàn)在定義函數(shù)" gradient_descent"。此函數(shù)將輸入變量,輸出變量,θ,alpha和歷元數(shù)作為參數(shù)。在這里,alpha是學(xué)習(xí)率。
您應(yīng)該根據(jù)需要選擇它。太小或太大的學(xué)習(xí)率可能會使您的算法變慢。我喜歡針對不同的學(xué)習(xí)率運(yùn)行該算法,并獲得正確學(xué)習(xí)率的想法。選擇正確的學(xué)習(xí)率可能需要幾次迭代。
對于y1中的每一列,我們將實(shí)現(xiàn)一個二進(jìn)制分類。
例如,當(dāng)我考慮數(shù)字2時,數(shù)字2應(yīng)該返回1,其余數(shù)字應(yīng)該返回0。因此,由于我們有10個類,所以每個epoch(iteration)運(yùn)行了10次。因此,我們在這里有一個嵌套的for循環(huán)。
- def gradient_descent(X, y, theta, alpha, epochs):
- m = len(X)
- for i in range(0, epochs):
- for j in range(0, 10):
- theta = pd.DataFrame(theta)
- h = hypothesis(theta.iloc[:,j], X)
- for k in range(0, theta.shape[0]):
- theta.iloc[k, j] -= (alpha/m) * np.sum((h-y.iloc[:, j])*X.iloc[:, k])
- theta = pd.DataFrame(theta)
- return theta, cost
(8) 初始化theta。記住,我們將為每個類實(shí)現(xiàn)邏輯回歸。每個課程也會有一系列的theta。
我正在運(yùn)行1500個紀(jì)元。我敢肯定,隨著時間的推移,準(zhǔn)確率會更高。
- theta = np.zeros([df.shape[1]+1, y1.shape[1]])
- theta = gradient_descent(X, y1, theta, 0.02, 1500)
(9) 使用此更新的theta,計(jì)算輸出變量。
- output = []
- for i in range(0, 10):
- theta1 = pd.DataFrame(theta)
- h = hypothesis(theta1.iloc[:,i], X)
- output.append(h)
- output=pd.DataFrame(output)
(10) 比較計(jì)算出的輸出和原始輸出變量,以計(jì)算模型的準(zhǔn)確性。
- accuracy = 0
- for col in range(0, 10):
- for row in range(len(y1)):
- if y1.iloc[row, col] == 1 and output.iloc[col, row] >= 0.5:
- accuracy += 1
- accuracyaccuracy = accuracy/len(X)
準(zhǔn)確度是72%。我相信,準(zhǔn)確度會更高。因?yàn)榛ㄙM(fèi)了很多時間,所以我沒有重新運(yùn)行算法。
如果您正在運(yùn)行此程序,請隨時嘗試更多的紀(jì)元,并在注釋部分中告知我您的準(zhǔn)確度。
除了梯度下降方法外,您還可以使用已經(jīng)為您內(nèi)置的優(yōu)化功能。
在這種方法中,您可以使用優(yōu)化函數(shù)來優(yōu)化算法的theta。這是一種更快的方法。
具有優(yōu)化功能的實(shí)現(xiàn)
(1) 我們將使用與以前相同的數(shù)據(jù)集。如果使用相同的筆記本,請使用其他名稱導(dǎo)入數(shù)據(jù)集:
- xls = pd.ExcelFile('ex3d1.xlsx')
- df = pd.read_excel(xls, 'X', header=None)

(2) 我們?nèi)匀恍枰獮閐f中的偏差項(xiàng)添加一列全為1的列。
- X = np.c_[np.ones((df.shape[0], 1)), df]

(3) 導(dǎo)入" y"的數(shù)據(jù)。
- y = pd.read_excel(xls, 'y', header=None)

由于這是一個DataFrame,因此只需將列零作為一個序列并將其設(shè)為二維以將維與X的維匹配。
- yy = y[0]
- yy = y[:, np.newaxis]

在這里," y"只有一列。將其設(shè)為10列,以供10個班級使用。每列將處理一個類。例如,當(dāng)我們處理類10時,我們將保留10的位置,并將其余值替換為零。這是函數(shù)y_change,它將使用y本身和一個類(例如3)。然后它將用其他所有類將1替換為3,將其替換為0。此功能將在以后的步驟中很快使用。
- def y_change(y, cl):
- y_pr=[]
- for i in range(0, len(y)):
- if y[i] == cl:
- y_pr.append(1)
- else:
- y_pr.append(0)
- return y_pr
數(shù)據(jù)準(zhǔn)備完成?,F(xiàn)在開發(fā)模型:
(4) 定義假設(shè)函數(shù)。這與以前的方法相同。
- def hypothesis(X, theta):
- z = np.dot(X, theta)
- return 1/(1+np.exp(-(z)))
(5) 開發(fā)成本函數(shù)。此方法也與以前的方法相同:
- def cost_function(theta, X, y):
- m = X.shape[0]
- y1 = hypothesis(X, theta)
- return -(1/len(X)) * np.sum(y*np.log(y1) + (1-y)*np.log(1-y1))
(6) 定義漸變。這是不同的。此函數(shù)定義如何更新theta。
- def gradient(theta, X, y):
- m = X.shape[0]
- y1 = hypothesis(X, theta)
- return (1/m) * np.dot(X.T, y1 - y)
(7) 現(xiàn)在,導(dǎo)入優(yōu)化函數(shù)并初始化theta。我將零作為初始theta值。任何其他值也應(yīng)該起作用。
- from scipy.optimize import minimize, fmin_tnc
- theta = np.zeros((X.shape[1], 1))
8.讓我們做一個擬合函數(shù),將X,y和theta作為輸入。它將使用優(yōu)化函數(shù)并為我們輸出優(yōu)化的theta。
它采用以下三個參數(shù):
- 需要最小化的功能
- 要優(yōu)化的參數(shù),
- 用于優(yōu)化的參數(shù)。
在此示例中,應(yīng)將成本函數(shù)最小化,并且為此需要優(yōu)化theta。輸入和輸出變量X和y是要使用的參數(shù)。
該優(yōu)化函數(shù)采用另一個參數(shù),即漸變。但這是可選的。在這里,我們有一個用于漸變的公式或函數(shù)。因此,我們正在通過它。
- def fit(X, y, theta):
- opt_weigths = fmin_tnc(func=cost_function,
- x0=theta, fprime=gradient,
- args=(X, y.flatten()))
- return opt_weigths[0]
(9) 使用這種擬合方法來找到優(yōu)化的theta。我們必須分別為每個類優(yōu)化theta。讓我們開發(fā)一個函數(shù),其中對于每個類,將在步驟3中使用y_change方法相應(yīng)地修改" y"。
- def find_param(X, y, theta):
- y_uniq = list(set(y.flatten()))
- theta_list = []
- for i in y_uniq:
- y_tr = pd.Series(y_change(y, i))
- y_try_tr = y_tr[:, np.newaxis]
- theta1 = fit(X, y, theta)
- theta_list.append(theta1)
- return theta_list
使用此方法找到最終theta
- theta_list = find_param(X, y, theta)
(10) 現(xiàn)在是時候預(yù)測輸出了。我們還必須單獨(dú)預(yù)測類別。
- def predict(theta_list, x, y):
- y_uniq = list(set(y.flatten()))
- y_hat = [0]*len(y)
- for i in range(0, len(y_uniq)):
- y_tr = y_change(y, y_uniq[i])
- y1 = hypothesis(X, theta_list[i])
- for k in range(0, len(y)):
- if y_tr[k] == 1 and y1[k] >= 0.5:
- y_hat[k] = y_uniq[i]
- return y_hat
使用上面的預(yù)測方法并計(jì)算預(yù)測輸出y_hat:
- y_hat = predict(theta_list, X, y)
(11) 計(jì)算精度
- accuracy=0
- for i in range(0, len(y)):
- if y_hat[i] == y.flatten()[i]:
- accuracy += 1print(accuracy/len(df)*100)
此過程可提供100%的準(zhǔn)確性?,F(xiàn)在。您可以自己決定要在項(xiàng)目中使用哪種邏輯回歸方法。
本文還使用神經(jīng)網(wǎng)絡(luò)解決了相同的問題。
檢查此GitHub頁面以獲取數(shù)據(jù)集:
https://github.com/rashida048/Machine-Learning-With-Python/blob/master/ex3d1.xlsx