從零開始用Python構建神經網絡
創新互聯是專業的敘州網站建設公司,敘州接單;提供成都網站設計、網站制作,網頁設計,網站設計,建網站,PHP網站建設等專業做網站服務;采用PHP框架,可快速的進行敘州網站開發網頁制作和功能擴展;專業做搜索引擎喜愛的網站,專業的做網站團隊,希望更多企業前來合作!
動機:為了更加深入的理解深度學習,我們將使用 python 語言從頭搭建一個神經網絡,而不是使用像 Tensorflow 那樣的封裝好的框架。我認為理解神經網絡的內部工作原理,對數據科學家來說至關重要。
這篇文章的內容是我的所學,希望也能對你有所幫助。
神經網絡是什么?
介紹神經網絡的文章大多數都會將它和大腦進行類比。如果你沒有深入研究過大腦與神經網絡的類比,那么將神經網絡解釋為一種將給定輸入映射為期望輸出的數學關系會更容易理解。
神經網絡包括以下組成部分
? 一個輸入層,x
? 任意數量的隱藏層
? 一個輸出層,?
? 每層之間有一組權值和偏置,W and b
? 為隱藏層選擇一種激活函數,σ。在教程中我們使用 Sigmoid 激活函數
下圖展示了 2 層神經網絡的結構(注意:我們在計算網絡層數時通常排除輸入層)
2 層神經網絡的結構
用 Python 可以很容易的構建神經網絡類
訓練神經網絡
這個網絡的輸出 ? 為:
你可能會注意到,在上面的等式中,輸出 ? 是 W 和 b 函數。
因此 W 和 b 的值影響預測的準確率. 所以根據輸入數據對 W 和 b 調優的過程就被成為訓練神經網絡。
每步訓練迭代包含以下兩個部分:
? 計算預測結果 ?,這一步稱為前向傳播
? 更新 W 和 b,,這一步成為反向傳播
下面的順序圖展示了這個過程:
前向傳播
正如我們在上圖中看到的,前向傳播只是簡單的計算。對于一個基本的 2 層網絡來說,它的輸出是這樣的:
我們在 NeuralNetwork 類中增加一個計算前向傳播的函數。為了簡單起見我們假設偏置 b 為0:
但是我們還需要一個方法來評估預測結果的好壞(即預測值和真實值的誤差)。這就要用到損失函數。
損失函數
常用的損失函數有很多種,根據模型的需求來選擇。在本教程中,我們使用誤差平方和作為損失函數。
誤差平方和是求每個預測值和真實值之間的誤差再求和,這個誤差是他們的差值求平方以便我們觀察誤差的絕對值。
訓練的目標是找到一組 W 和 b,使得損失函數最好小,也即預測值和真實值之間的距離最小。
反向傳播
我們已經度量出了預測的誤差(損失),現在需要找到一種方法來傳播誤差,并以此更新權值和偏置。
為了知道如何適當的調整權值和偏置,我們需要知道損失函數對權值 W 和偏置 b 的導數。
回想微積分中的概念,函數的導數就是函數的斜率。
梯度下降法
如果我們已經求出了導數,我們就可以通過增加或減少導數值來更新權值 W 和偏置 b(參考上圖)。這種方式被稱為梯度下降法。
但是我們不能直接計算損失函數對權值和偏置的導數,因為在損失函數的等式中并沒有顯式的包含他們。因此,我們需要運用鏈式求導發在來幫助計算導數。
鏈式法則用于計算損失函數對 W 和 b 的導數。注意,為了簡單起見。我們只展示了假設網絡只有 1 層的偏導數。
這雖然很簡陋,但是我們依然能得到想要的結果—損失函數對權值 W 的導數(斜率),因此我們可以相應的調整權值。
現在我們將反向傳播算法的函數添加到 Python 代碼中
為了更深入的理解微積分原理和反向傳播中的鏈式求導法則,我強烈推薦 3Blue1Brown 的如下教程:
Youtube:
整合并完成一個實例
既然我們已經有了包括前向傳播和反向傳播的完整 Python 代碼,那么就將其應用到一個例子上看看它是如何工作的吧。
神經網絡可以通過學習得到函數的權重。而我們僅靠觀察是不太可能得到函數的權重的。
讓我們訓練神經網絡進行 1500 次迭代,看看會發生什么。 注意觀察下面每次迭代的損失函數,我們可以清楚地看到損失函數單調遞減到最小值。這與我們之前介紹的梯度下降法一致。
讓我們看看經過 1500 次迭代后的神經網絡的最終預測結果:
經過 1500 次迭代訓練后的預測結果
我們成功了!我們應用前向和方向傳播算法成功的訓練了神經網絡并且預測結果收斂于真實值。
注意預測值和真實值之間存在細微的誤差是允許的。這樣可以防止模型過擬合并且使得神經網絡對于未知數據有著更強的泛化能力。
下一步是什么?
幸運的是我們的學習之旅還沒有結束,仍然有很多關于神經網絡和深度學習的內容需要學習。例如:
? 除了 Sigmoid 以外,還可以用哪些激活函數
? 在訓練網絡的時候應用學習率
? 在面對圖像分類任務的時候使用卷積神經網絡
我很快會寫更多關于這個主題的內容,敬請期待!
最后的想法
我自己也從零開始寫了很多神經網絡的代碼
雖然可以使用諸如 Tensorflow 和 Keras 這樣的深度學習框架方便的搭建深層網絡而不需要完全理解其內部工作原理。但是我覺得對于有追求的數據科學家來說,理解內部原理是非常有益的。
這種練習對我自己來說已成成為重要的時間投入,希望也能對你有所幫助
正則化(Regularization)
機器學習中幾乎都可以看到損失函數后面會添加一個額外項,常用的額外項一般有兩種,一般英文稱作 ?1-norm 和 ?2-norm ,中文稱作 L1正則化 和 L2正則化 ,或者 L1范數 和 L2范數 。
L1正則化和L2正則化可以看做是損失函數的懲罰項。所謂『懲罰』是指對損失函數中的某些參數做一些限制。對于線性回歸模型,使用L1正則化的模型建叫做Lasso回歸,使用L2正則化的模型叫做Ridge回歸(嶺回歸)。下圖是Python中Lasso回歸的損失函數,式中加號后面一項α||w||1即為L1正則化項。
下圖是Python中Ridge回歸的損失函數,式中加號后面一項α||w||22即為L2正則化項。
一般回歸分析中回歸w表示特征的系數,從上式可以看到正則化項是對系數做了處理(限制)。 L1正則化和L2正則化的說明如下:
L1正則化是指權值向量w中各個元素的 絕對值之和 ,通常表示為||w||1
L2正則化是指權值向量w中各個元素的 平方和然后再求平方根 (可以看到Ridge回歸的L2正則化項有平方符號),通常表示為||w||2
一般都會在正則化項之前添加一個系數,Python中用α表示,一些文章也用λ表示。這個系數需要用戶指定。
那添加L1和L2正則化有什么用? 下面是L1正則化和L2正則化的作用 ,這些表述可以在很多文章中找到。
L1正則化可以產生稀疏權值矩陣,即產生一個稀疏模型,可以用于特征選擇
L2正則化可以防止模型過擬合(overfitting);一定程度上,L1也可以防止過擬合
稀疏模型與特征選擇
上面提到L1正則化有助于生成一個稀疏權值矩陣,進而可以用于特征選擇。為什么要生成一個稀疏矩陣?
稀疏矩陣指的是很多元素為0,只有少數元素是非零值的矩陣,即得到的線性回歸模型的大部分系數都是0.
通常機器學習中特征數量很多,例如文本處理時,如果將一個詞組(term)作為一個特征,那么特征數量會達到上萬個(bigram)。在預測或分類時,那么多特征顯然難以選擇,但是如果代入這些特征得到的模型是一個稀疏模型,表示只有少數特征對這個模型有貢獻,絕大部分特征是沒有貢獻的,或者貢獻微小(因為它們前面的系數是0或者是很小的值,即使去掉對模型也沒有什么影響),此時我們就可以只關注系數是非零值的特征。這就是稀疏模型與特征選擇的關系。
L1和L2正則化的直觀理解
這部分內容將解釋 為什么L1正則化可以產生稀疏模型(L1是怎么讓系數等于零的) ,以及 為什么L2正則化可以防止過擬合 。
L1正則化和特征選擇
假設有如下帶L1正則化的損失函數:
J=J0+α∑w|w|(1)
其中J0是原始的損失函數,加號后面的一項是L1正則化項,α是正則化系數。注意到L1正則化是權值的 絕對值之和 ,J是帶有絕對值符號的函數,因此J是不完全可微的。機器學習的任務就是要通過一些方法(比如梯度下降)求出損失函數的最小值。當我們在原始損失函數J0后添加L1正則化項時,相當于對J0做了一個約束。令L=α∑w|w|,則J=J0+L,此時我們的任務變成 在L約束下求出J0取最小值的解 。考慮二維的情況,即只有兩個權值w1和w2,此時L=|w1|+|w2|對于梯度下降法,求解J0的過程可以畫出等值線,同時L1正則化的函數L也可以在w1w2的二維平面上畫出來。如下圖:
圖1? L1正則化
圖中等值線是J0的等值線,黑色方形是L函數的圖形。在圖中,當J0等值線與L圖形首次相交的地方就是最優解。上圖中J0與L在L的一個頂點處相交,這個頂點就是最優解。注意到這個頂點的值是(w1,w2)=(0,w)。可以直觀想象,因為L函數有很多『突出的角』(二維情況下四個,多維情況下更多),J0與這些角接觸的機率會遠大于與L其它部位接觸的機率,而在這些角上,會有很多權值等于0,這就是為什么L1正則化可以產生稀疏模型,進而可以用于特征選擇。
而正則化前面的系數α,可以控制L圖形的大小。α越小,L的圖形越大(上圖中的黑色方框);α越大,L的圖形就越小,可以小到黑色方框只超出原點范圍一點點,這是最優點的值(w1,w2)=(0,w)中的w可以取到很小的值。
類似,假設有如下帶L2正則化的損失函數:
J=J0+α∑ww2(2)
同樣可以畫出他們在二維平面上的圖形,如下:
圖2? L2正則化
二維平面下L2正則化的函數圖形是個圓,與方形相比,被磨去了棱角。因此J0與L相交時使得w1或w2等于零的機率小了許多,這就是為什么L2正則化不具有稀疏性的原因。
L2正則化和過擬合
擬合過程中通常都傾向于讓權值盡可能小,最后構造一個所有參數都比較小的模型。因為一般認為參數值小的模型比較簡單,能適應不同的數據集,也在一定程度上避免了過擬合現象。可以設想一下對于一個線性回歸方程,若參數很大,那么只要數據偏移一點點,就會對結果造成很大的影響;但如果參數足夠小,數據偏移得多一點也不會對結果造成什么影響,專業一點的說法是『抗擾動能力強』。
那為什么L2正則化可以獲得值很小的參數?
以線性回歸中的梯度下降法為例。假設要求的參數為θ,hθ(x)是我們的假設函數,那么線性回歸的代價函數如下:
J(θ)=12m∑i=1m(hθ(x(i))?y(i))(3)
那么在梯度下降法中,最終用于迭代計算參數θ的迭代式為:
θj:=θj?α1m∑i=1m(hθ(x(i))?y(i))x(i)j(4)
其中α是learning rate. 上式是沒有添加L2正則化項的迭代公式,如果在原始代價函數之后添加L2正則化,則迭代公式會變成下面的樣子:
θj:=θj(1?αλm)?α1m∑i=1m(hθ(x(i))?y(i))x(i)j(5)
其中 λ就是正則化參數 。從上式可以看到,與未添加L2正則化的迭代公式相比,每一次迭代,θj都要先乘以一個小于1的因子,從而使得θj不斷減小,因此總得來看,θ是不斷減小的。
最開始也提到L1正則化一定程度上也可以防止過擬合。之前做了解釋,當L1的正則化系數很小時,得到的最優解會很小,可以達到和L2正則化類似的效果。
正則化參數的選擇
L1正則化參數
通常越大的λ可以讓代價函數在參數為0時取到最小值。下面是一個簡單的例子,這個例子來自 Quora上的問答 。為了方便敘述,一些符號跟這篇帖子的符號保持一致。
假設有如下帶L1正則化項的代價函數:
F(x)=f(x)+λ||x||1
其中x是要估計的參數,相當于上文中提到的w以及θ. 注意到L1正則化在某些位置是不可導的,當λ足夠大時可以使得F(x)在x=0時取到最小值。如下圖:
圖3 L1正則化參數的選擇
分別取λ=0.5和λ=2,可以看到越大的λ越容易使F(x)在x=0時取到最小值。
L2正則化參數
從公式5可以看到,λ越大,θj衰減得越快。另一個理解可以參考圖2,λ越大,L2圓的半徑越小,最后求得代價函數最值時各參數也會變得很小。
Reference
過擬合的解釋:
正則化的解釋:
正則化的解釋:
正則化的數學解釋(一些圖來源于這里):
原文參考:blog.csdn.net/jinping_shi/article/details/52433975
平滑函數。
交叉熵損失函數,也稱為對數損失或者logistic損失。當模型產生了預測值之后,將對類別的預測概率與真實值(由0或1組成)進行不比較,計算所產生的損失,然后基于此損失設置對數形式的懲罰項。
在神經網絡中,所使用的Softmax函數是連續可導函數,這使得可以計算出損失函數相對于神經網絡中每個權重的導數(在《機器學習數學基礎》中有對此的完整推導過程和案例,這樣就可以相應地調整模型的權重以最小化損失函數。
擴展資料:
注意事項:
當預測類別為二分類時,交叉熵損失函數的計算公式如下圖,其中y是真實類別(值為0或1),p是預測類別的概率(值為0~1之間的小數)。
計算二分類的交叉熵損失函數的python代碼如下圖,其中esp是一個極小值,第五行代碼clip的目的是保證預測概率的值在0~1之間,輸出的損失值數組求和后,就是損失函數最后的返回值。
參考資料來源:百度百科-交叉熵
參考資料來源:百度百科-損失函數
不看numpy一維數組的話,就是len相同的一個列表相同索引值相加吧。
x1=[1,2,3]
x2=[4,5,6]
x3=[]
def?add():
for?i?in?range(0,len(x1)):
x3.append(x1[i]+x2[i])
return?x3
print(add())
ID3算法介紹
ID3算法全稱為迭代二叉樹3代算法(Iterative Dichotomiser 3)
該算法要先進行特征選擇,再生成決策樹,其中特征選擇是基于“信息增益”最大的原則進行的。
但由于決策樹完全基于訓練集生成的,有可能對訓練集過于“依賴”,即產生過擬合現象。因此在生成決策樹后,需要對決策樹進行剪枝。剪枝有兩種形式,分別為前剪枝(Pre-Pruning)和后剪枝(Post-Pruning),一般采用后剪枝。
信息熵、條件熵和信息增益
信息熵:來自于香農定理,表示信息集合所含信息的平均不確定性。信息熵越大,表示不確定性越大,所含的信息量也就越大。
設x 1 , x 2 , x 3 , . . . x n {x_1, x_2, x_3, ...x_n}x
1
,x
2
,x
3
,...x
n
為信息集合X的n個取值,則x i x_ix
i
的概率:
P ( X = i ) = p i , i = 1 , 2 , 3 , . . . , n P(X=i) = p_i, i=1,2,3,...,n
P(X=i)=p
i
,i=1,2,3,...,n
信息集合X的信息熵為:
H ( X ) = ? ∑ i = 1 n p i log ? p i H(X) =- \sum_{i=1}^{n}{p_i}\log{p_i}
H(X)=?
i=1
∑
n
p
i
logp
i
條件熵:指已知某個隨機變量的情況下,信息集合的信息熵。
設信息集合X中有y 1 , y 2 , y 3 , . . . y m {y_1, y_2, y_3, ...y_m}y
1
,y
2
,y
3
,...y
m
組成的隨機變量集合Y,則隨機變量(X,Y)的聯合概率分布為
P ( x = i , y = j ) = p i j P(x=i,y=j) = p_{ij}
P(x=i,y=j)=p
ij
條件熵:
H ( X ∣ Y ) = ∑ j = 1 m p ( y j ) H ( X ∣ y j ) H(X|Y) = \sum_{j=1}^m{p(y_j)H(X|y_j)}
H(X∣Y)=
j=1
∑
m
p(y
j
)H(X∣y
j
)
由
H ( X ∣ y j ) = ? ∑ j = 1 m p ( y j ) ∑ i = 1 n p ( x i ∣ y j ) log ? p ( x i ∣ y j ) H(X|y_j) = - \sum_{j=1}^m{p(y_j)}\sum_{i=1}^n{p(x_i|y_j)}\log{p(x_i|y_j)}
H(X∣y
j
)=?
j=1
∑
m
p(y
j
)
i=1
∑
n
p(x
i
∣y
j
)logp(x
i
∣y
j
)
和貝葉斯公式:
p ( x i y j ) = p ( x i ∣ y j ) p ( y j ) p(x_iy_j) = p(x_i|y_j)p(y_j)
p(x
i
y
j
)=p(x
i
∣y
j
)p(y
j
)
可以化簡條件熵的計算公式為:
H ( X ∣ Y ) = ∑ j = 1 m ∑ i = 1 n p ( x i , y j ) log ? p ( x i ) p ( x i , y j ) H(X|Y) = \sum_{j=1}^m \sum_{i=1}^n{p(x_i, y_j)\log\frac{p(x_i)}{p(x_i, y_j)}}
H(X∣Y)=
j=1
∑
m
i=1
∑
n
p(x
i
,y
j
)log
p(x
i
,y
j
)
p(x
i
)
信息增益:信息熵-條件熵,用于衡量在知道已知隨機變量后,信息不確定性減小越大。
d ( X , Y ) = H ( X ) ? H ( X ∣ Y ) d(X,Y) = H(X) - H(X|Y)
d(X,Y)=H(X)?H(X∣Y)
python代碼實現
import numpy as np
import math
def calShannonEnt(dataSet):
""" 計算信息熵 """
labelCountDict = {}
for d in dataSet:
label = d[-1]
if label not in labelCountDict.keys():
labelCountDict[label] = 1
else:
labelCountDict[label] += 1
entropy = 0.0
for l, c in labelCountDict.items():
p = 1.0 * c / len(dataSet)
entropy -= p * math.log(p, 2)
return entropy
def filterSubDataSet(dataSet, colIndex, value):
"""返回colIndex特征列label等于value,并且過濾掉改特征列的數據集"""
subDataSetList = []
for r in dataSet:
if r[colIndex] == value:
newR = r[:colIndex]
newR = np.append(newR, (r[colIndex + 1:]))
subDataSetList.append(newR)
return np.array(subDataSetList)
def chooseFeature(dataSet):
""" 通過計算信息增益選擇最合適的特征"""
featureNum = dataSet.shape[1] - 1
entropy = calShannonEnt(dataSet)
bestInfoGain = 0.0
bestFeatureIndex = -1
for i in range(featureNum):
uniqueValues = np.unique(dataSet[:, i])
condition_entropy = 0.0
for v in uniqueValues: #計算條件熵
subDataSet = filterSubDataSet(dataSet, i, v)
p = 1.0 * len(subDataSet) / len(dataSet)
condition_entropy += p * calShannonEnt(subDataSet)
infoGain = entropy - condition_entropy #計算信息增益
if infoGain = bestInfoGain: #選擇最大信息增益
bestInfoGain = infoGain
bestFeatureIndex = i
return bestFeatureIndex
def creatDecisionTree(dataSet, featNames):
""" 通過訓練集生成決策樹 """
featureName = featNames[:] # 拷貝featNames,此處不能直接用賦值操作,否則新變量會指向舊變量的地址
classList = list(dataSet[:, -1])
if len(set(classList)) == 1: # 只有一個類別
return classList[0]
if dataSet.shape[1] == 1: #當所有特征屬性都利用完仍然無法判斷樣本屬于哪一類,此時歸為該數據集中數量最多的那一類
return max(set(classList), key=classList.count)
bestFeatureIndex = chooseFeature(dataSet) #選擇特征
bestFeatureName = featNames[bestFeatureIndex]
del featureName[bestFeatureIndex] #移除已選特征列
decisionTree = {bestFeatureName: {}}
featureValueUnique = sorted(set(dataSet[:, bestFeatureIndex])) #已選特征列所包含的類別, 通過遞歸生成決策樹
for v in featureValueUnique:
copyFeatureName = featureName[:]
subDataSet = filterSubDataSet(dataSet, bestFeatureIndex, v)
decisionTree[bestFeatureName][v] = creatDecisionTree(subDataSet, copyFeatureName)
return decisionTree
def classify(decisionTree, featnames, featList):
""" 使用訓練所得的決策樹進行分類 """
classLabel = None
root = decisionTree.keys()[0]
firstGenDict = decisionTree[root]
featIndex = featnames.index(root)
for k in firstGenDict.keys():
if featList[featIndex] == k:
if isinstance(firstGenDict[k], dict): #若子節點仍是樹,則遞歸查找
classLabel = classify(firstGenDict[k], featnames, featList)
else:
classLabel = firstGenDict[k]
return classLabel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
下面用鳶尾花數據集對該算法進行測試。由于ID3算法只能用于標稱型數據,因此用在對連續型的數值數據上時,還需要對數據進行離散化,離散化的方法稍后說明,此處為了簡化,先使用每一種特征所有連續性數值的中值作為分界點,小于中值的標記為1,大于中值的標記為0。訓練1000次,統計準確率均值。
from sklearn import datasets
from sklearn.model_selection import train_test_split
iris = datasets.load_iris()
data = np.c_[iris.data, iris.target]
scoreL = []
for i in range(1000): #對該過程進行10000次
trainData, testData = train_test_split(data) #區分測試集和訓練集
featNames = iris.feature_names[:]
for i in range(trainData.shape[1] - 1): #對訓練集每個特征,以中值為分界點進行離散化
splitPoint = np.mean(trainData[:, i])
featNames[i] = featNames[i]+'='+'{:.3f}'.format(splitPoint)
trainData[:, i] = [1 if x = splitPoint else 0 for x in trainData[:, i]]
testData[:, i] = [1 if x = splitPoint else 0 for x in testData[:, i]]
decisionTree = creatDecisionTree(trainData, featNames)
classifyLable = [classify(decisionTree, featNames, td) for td in testData]
scoreL.append(1.0 * sum(classifyLable == testData[:, -1]) / len(classifyLable))
print 'score: ', np.mean(scoreL)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
輸出結果為:score: 0.7335,即準確率有73%。每次訓練和預測的準確率分布如下:
數據離散化
然而,在上例中對特征值離散化的劃分點實際上過于“野蠻”,此處介紹一種通過信息增益最大的標準來對數據進行離散化。原理很簡單,當信息增益最大時,說明用該點劃分能最大程度降低數據集的不確定性。
具體步驟如下:
對每個特征所包含的數值型特征值排序
對相鄰兩個特征值取均值,這些均值就是待選的劃分點
用每一個待選點把該特征的特征值劃分成兩類,小于該特征點置為1, 大于該特征點置為0,計算此時的條件熵,并計算出信息增益
選擇信息使信息增益最大的劃分點進行特征離散化
實現代碼如下:
def filterRawData(dataSet, colIndex, value, tag):
""" 用于把每個特征的連續值按照區分點分成兩類,加入tag參數,可用于標記篩選的是哪一部分數據"""
filterDataList = []
for r in dataSet:
if (tag and r[colIndex] = value) or ((not tag) and r[colIndex] value):
newR = r[:colIndex]
newR = np.append(newR, (r[colIndex + 1:]))
filterDataList.append(newR)
return np.array(filterDataList)
def dataDiscretization(dataSet, featName):
""" 對數據每個特征的數值型特征值進行離散化 """
featureNum = dataSet.shape[1] - 1
entropy = calShannonEnt(dataSet)
for featIndex in range(featureNum): #對于每一個特征
uniqueValues = sorted(np.unique(dataSet[:, featIndex]))
meanPoint = []
for i in range(len(uniqueValues) - 1): # 求出相鄰兩個值的平均值
meanPoint.append(float(uniqueValues[i+1] + uniqueValues[i]) / 2.0)
bestInfoGain = 0.0
bestMeanPoint = -1
for mp in meanPoint: #對于每個劃分點
subEntropy = 0.0 #計算該劃分點的信息熵
for tag in range(2): #分別劃分為兩類
subDataSet = filterRawData(dataSet, featIndex, mp, tag)
p = 1.0 * len(subDataSet) / len(dataSet)
subEntropy += p * calShannonEnt(subDataSet)
## 計算信息增益
infoGain = entropy - subEntropy
## 選擇最大信息增益
if infoGain = bestInfoGain:
bestInfoGain = infoGain
bestMeanPoint = mp
featName[featIndex] = featName[featIndex] + "=" + "{:.3f}".format(bestMeanPoint)
dataSet[:, featIndex] = [1 if x = bestMeanPoint else 0 for x in dataSet[:, featIndex]]
return dataSet, featName
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
重新對數據進行離散化,并重復該步驟1000次,同時用sklearn中的DecisionTreeClassifier對相同數據進行分類,分別統計平均準確率。運行代碼如下:
from sklearn.tree import DecisionTreeClassifier
import matplotlib.pyplot as plt
scoreL = []
scoreL_sk = []
for i in range(1000): #對該過程進行1000次
featNames = iris.feature_names[:]
trainData, testData = train_test_split(data) #區分測試集和訓練集
trainData_tmp = copy.copy(trainData)
testData_tmp = copy.copy(testData)
discritizationData, discritizationFeatName= dataDiscretization(trainData, featNames) #根據信息增益離散化
for i in range(testData.shape[1]-1): #根據測試集的區分點離散化訓練集
splitPoint = float(discritizationFeatName[i].split('=')[-1])
testData[:, i] = [1 if x=splitPoint else 0 for x in testData[:, i]]
decisionTree = creatDecisionTree(trainData, featNames)
classifyLable = [classify(decisionTree, featNames, td) for td in testData]
scoreL.append(1.0 * sum(classifyLable == testData[:, -1]) / len(classifyLable))
clf = DecisionTreeClassifier('entropy')
clf.fit(trainData[:, :-1], trainData[:, -1])
clf.predict(testData[:, :-1])
scoreL_sk.append(clf.score(testData[:, :-1], testData[:, -1]))
print 'score: ', np.mean(scoreL)
print 'score-sk: ', np.mean(scoreL_sk)
fig = plt.figure(figsize=(10, 4))
plt.subplot(1,2,1)
pd.Series(scoreL).hist(grid=False, bins=10)
plt.subplot(1,2,2)
pd.Series(scoreL_sk).hist(grid=False, bins=10)
plt.show()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
兩者準確率分別為:
score: 0.7037894736842105
score-sk: 0.7044736842105263
準確率分布如下:
兩者的結果非常一樣。
(但是。。為什么根據信息熵離散化得到的準確率比直接用均值離散化的準確率還要低啊??哇的哭出聲。。)
最后一次決策樹圖形如下:
決策樹剪枝
由于決策樹是完全依照訓練集生成的,有可能會有過擬合現象,因此一般會對生成的決策樹進行剪枝。常用的是通過決策樹損失函數剪枝,決策樹損失函數表示為:
C a ( T ) = ∑ t = 1 T N t H t ( T ) + α ∣ T ∣ C_a(T) = \sum_{t=1}^TN_tH_t(T) +\alpha|T|
C
a
(T)=
t=1
∑
T
N
t
H
t
(T)+α∣T∣
其中,H t ( T ) H_t(T)H
t
(T)表示葉子節點t的熵值,T表示決策樹的深度。前項∑ t = 1 T N t H t ( T ) \sum_{t=1}^TN_tH_t(T)∑
t=1
T
N
t
H
t
(T)是決策樹的經驗損失函數當隨著T的增加,該節點被不停的劃分的時候,熵值可以達到最小,然而T的增加會使后項的值增大。決策樹損失函數要做的就是在兩者之間進行平衡,使得該值最小。
對于決策樹損失函數的理解,如何理解決策樹的損失函數? - 陶輕松的回答 - 知乎這個回答寫得挺好,可以按照答主的思路理解一下
C4.5算法
ID3算法通過信息增益來進行特征選擇會有一個比較明顯的缺點:即在選擇的過程中該算法會優先選擇類別較多的屬性(這些屬性的不確定性小,條件熵小,因此信息增益會大),另外,ID3算法無法解決當每個特征屬性中每個分類都只有一個樣本的情況(此時每個屬性的條件熵都為0)。
C4.5算法ID3算法的改進,它不是依據信息增益進行特征選擇,而是依據信息增益率,它添加了特征分裂信息作為懲罰項。定義分裂信息:
S p l i t I n f o ( X , Y ) = ? ∑ i n ∣ X i ∣ ∣ X ∣ log ? ∣ X i ∣ ∣ X ∣ SplitInfo(X, Y) =-\sum_i^n\frac{|X_i|}{|X|}\log\frac{|X_i|}{|X|}
SplitInfo(X,Y)=?
i
∑
n
∣X∣
∣X
i
∣
log
∣X∣
∣X
i
∣
則信息增益率為:
G a i n R a t i o ( X , Y ) = d ( X , Y ) S p l i t I n f o ( X , Y ) GainRatio(X,Y)=\frac{d(X,Y)}{SplitInfo(X, Y)}
GainRatio(X,Y)=
SplitInfo(X,Y)
d(X,Y)
關于ID3和C4.5算法
在學習分類回歸決策樹算法時,看了不少的資料和博客。關于這兩個算法,ID3算法是最早的分類算法,這個算法剛出生的時候其實帶有很多缺陷:
無法處理連續性特征數據
特征選取會傾向于分類較多的特征
沒有解決過擬合的問題
沒有解決缺失值的問題
即該算法出生時是沒有帶有連續特征離散化、剪枝等步驟的。C4.5作為ID3的改進版本彌補列ID3算法不少的缺陷:
通過信息最大增益的標準離散化連續的特征數據
在選擇特征是標準從“最大信息增益”改為“最大信息增益率”
通過加入正則項系數對決策樹進行剪枝
對缺失值的處理體現在兩個方面:特征選擇和生成決策樹。初始條件下對每個樣本的權重置為1。
特征選擇:在選取最優特征時,計算出每個特征的信息增益后,需要乘以一個**“非缺失值樣本權重占總樣本權重的比例”**作為系數來對比每個特征信息增益的大小
生成決策樹:在生成決策樹時,對于缺失的樣本我們按照一定比例把它歸屬到每個特征值中,比例為該特征每一個特征值占非缺失數據的比重
關于C4.5和CART回歸樹
作為ID3的改進版本,C4.5克服了許多缺陷,但是它自身還是存在不少問題:
C4.5的熵運算中涉及了對數運算,在數據量大的時候效率非常低。
C4.5的剪枝過于簡單
C4.5只能用于分類運算不能用于回歸
當特征有多個特征值是C4.5生成多叉樹會使樹的深度加深
————————————————
版權聲明:本文為CSDN博主「Sarah Huang」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:
當前名稱:關于Python損失函數例題的信息
文章來源:http://vcdvsql.cn/article38/hsoesp.html
成都網站建設公司_創新互聯,為您提供響應式網站、動態網站、定制網站、企業建站、標簽優化、虛擬主機
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯