寫在前面:我將從一個入門者的視角(水平)將機器學習中的經常使用算法娓娓道來。自身水平確實有限,若是其中有什麼錯誤的話但願你們指出,避免誤導你們。而後這是這個系列的第二篇了,對於初學者來講,若是你沒看過第一篇,推薦看看機器學習基本算法之線性迴歸,裏面說起到了不少基礎的數學知識和一些的機器學習思惟,對於理解這篇文章頗有幫助,不少機器學習流程化的東西這裏就不在具體介紹爲何這樣作了,而是直接解釋爲何在邏輯迴歸中須要使用這樣的方式來實現,更加聚焦於邏輯迴歸實現自己,而非機器學習的流程理解。html
先上效果圖 ^_^git
Python: 3.5github
TensorFlow: Anaconda3 envs算法
IDE: PyCharm 2017.3.3編程
若是你看過前面的線性迴歸介紹,能夠知道,線性迴歸是經過對一類呈線性分佈的數據進行擬合,而後訓練一個線性模型對數據進行預測。那麼在邏輯迴歸中,能夠暫時理解爲處理分類問題。好比給出一我的的經緯度,要判斷這我的在哪一個國家,若是僅僅以線性迴歸的思惟,咱們很難根據經緯度去構造出一個很好的線性模型去預測地區,咱們就須要知道的是國家分界線,而這些分界線的得到就是經過邏輯迴歸。app
Sigmoid
若是你尚未太理解邏輯迴歸處理的問題和線性迴歸比的獨特性,咱們再來舉個小例子(注意:爲了簡單,這裏的數據都是特殊取值的),來引出今天的主角:機器學習
對於上面這張圖,很明顯,怎麼畫直線也很差,總會產生較大的偏差。而若是有一個函數可以表示:函數
那麼就完美符合當前的數據分佈,直覺告訴咱們有很大可能其對數據的預測能力也比較強。接下來,就是找一個 形函數來做爲擬合函數。這裏很明顯咱們的函數不是線性的。這裏提出一個叫 的函數:學習
從圖像中能夠看到,這個函數的完美符合咱們心中的預期。
# coding: utf-8
import numpy as np
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 6, 7, 8, 9, 10]
y = [0, 0, 0, 0, 1, 1, 1, 1, 1]
train_X = np.asarray(x)
train_Y = np.asarray(y)
fig = plt.figure()
plt.xlim(-1, 12)
plt.ylim(-0.5, 1.5)
plt.scatter(train_X, train_Y)
s_X = np.linspace(-2, 12, 100)
s_Y = 1/(1 + np.power(np.e, -6*(s_X - 5)))
plt.plot(s_X, s_Y)
plt.savefig("linear2LogisticreGressionGraphAddSigmoid.png")
# plt.savefig("linear2LogisticreGressionGraph.png")
plt.show()
複製代碼
前面已經說了,爲了更好體會邏輯迴歸,因此數據有點特殊和單一,下面咱們再來看一組數據:
像這種有明顯的聚攏效果的散點圖,能夠看作是一個分類問題,因此對於這裏咱們怎麼利用前面提到的邏輯迴歸來處理相似於這種分類問題?
這裏思惟要稍微轉換一下,之前咱們以 的值做爲輸出,這裏不是了,這裏須要預測的結果是給定 求出該點是紅色的圓形仍是藍色的三角形?因此要找一個形如 的函數來處理,前面的 函數能夠幫咱們分類,只要假設 和 各表明一種狀況,那麼得出 時就能夠推斷出 如今的狀況是紅色的圓形仍是藍色的三角形,如今的狀況是,如何將 預處理的結果與 函數聯繫起來。可能你已經想到了,將 的值域當作 的定義域。那麼給定一個 咱們就能得出一個在區間 內的值。這下,函數就可以知足咱們的要求了。
模仿前面機器學習的思惟,咱們對於給定的輸入統一用一個輸入矩陣 表示 而後加入權重 ,則得出函數 ,接下來咱們要定義一個模型去區分這種分佈?若是你看過代碼就知道,在作數據的時候默認就是利用 來分的類,因此天然取的是線性模型做爲首選。至於怎麼選模型我認爲最簡潔的表示每每就是最好的,下面看看吳恩達老師的例子體會一下這個過程。
既然是線性,那麼直接得出 這裏提醒下加 是由於咱們默認的向量都是列向量。 理解了這裏那就好辦了,綜合咱們的 能夠得出擬合函數:
既然咱們都已經把數據作了處理,因此對於輸入是二元仍是一元沒差了,就是一個 而已。下面咱們根據以前線性函數的例子,找一個適合描述偏差的函數。看圖:
下面咱們先只分析爲 的點如何經過 的關係找到一個函數來表達損失。首先能夠確定的是,要對 的懲罰力度是最大的。由於從圖中看出對它的預測最離譜,因此損失函數要是一個遞減函數,而後繼續分析損失函數是否對於 的變化呈均勻變化,也就是這個遞減函數的遞減幅度是設麼樣的?很明顯, 越靠近 ,它的懲罰力度應該和指數增加有點相似。越小一點點,懲罰力度馬上激增,這樣對於最後損失函數收斂時的參數擬合結果比較符合大多數數據的狀況,不會出現它極端的分類,由於對極端的數據處罰力度實在是太大了。好了,這個函數符合三個特色,遞減
、導數絕對值遞減
(遞減幅度要減少)和定義域在 內的跨度必須大,最好是從正無窮到 由於若是函數值爲 咱們就不要對這個點進行懲罰了,因此確定是要有 這個函數值的。這時你可能想到了,對數函數正好知足狀況。因此有以下結果:
這裏就不貼函數圖了,下面你們本身分析一下 的狀況。本身實在想不出來再往下閱讀吧,若是直接看缺乏思考過程對本身的理解可能有點欠缺。就像不少書不少博客都是直接給公式,雖然勉強也能看懂,可是更多時候是本身記住,對於其中的思考過程沒有一點本身的體會和心得。給個圖你們本身猜猜:
下面直接給出最後的結果:
還記得前面說的邏輯迴歸能夠看作是分類問題嗎?而對於分類問題的預測咱們能夠看作機率來計算。因此函數 能夠看作,在 下,輸入 取值爲 的機率。而咱們要作的就是求出一個 是的對於給定的 的機率儘量和 符合。這時候你會發現,結果 服從伯努利分佈,也就是隨機變量 的取值只能是 ,在 看作是取 的機率的狀況下,當這個值大於 咱們認爲分類爲 。
好了,把上面的東西都「忘掉」,接下來進入一個機率論的思惟模式。假設這一個平面內有無數個點,而咱們給出的數據只是一個抽樣數據。可是咱們不可能把全部的狀況都列舉出來,因此咱們要作的就是經過給出的樣原本估測總體分佈。其實這也和機器學習須要大量樣本訓練有關,很明顯數據量越大,偶然偏差越小,樣本的分佈越接近總體分佈。收回到機率論,我發現容易寫着寫着就跑偏了😂😂😂。如今咱們統計數據的狀況是:符合伯努利分佈。而這種知道分佈的求可以和分佈匹配的參數的方法機率論中有一個參數估計方法叫:極大似然估計
。若是不懂看一下簡短介紹:
極大似然估計,通俗理解來講,就是利用已知的樣本結果信息,反推最具備可能(最大機率)致使這些樣本結果出現的模型參數值! 換句話說,極大似然估計提供了一種給定觀察數據來評估模型參數的方法,即:「模型已定,參數未知」。
依照上面的意思那很明顯,咱們是把 那天然就有:
這是單個 的機率。若是把整個樣本看作 次的獨立重複實驗呢?那發生的機率就是這個的乘積了。這裏使用 向量表示整個數據。即:
能夠看出這是一個關於 的函數,如今比較玄學的部分來了,咱們認爲,這個事件既然發生了,那麼它發生的機率就應該是全部事件中機率最大的,若是不是那它憑什麼發生?哈哈,就是這麼講道(xuan)理(xue)。最大值?那就好辦了,直接求導?不行不行,太難了。。。高中最經常使用的把戲:乘變加,用對數。而後乘以 就編程求最小值了。
前面已經給出了損失函數,咱們再次利用梯度降低算法更新權重。若是本身手寫代碼來實現,咱們仍是要求偏導的😂😂😂
高數的東西,這裏偷懶了,字有點難打就直接截圖了,畢竟這不是什麼難點:
那麼更新函數就是:
注意這裏的 , 表明的是一個列向量, 表明第 列第 個數字。其實仔細想一想就能夠發現,更新的 就是一個數,對它求偏導的時候餘出來就是和它相乘的那個 。因爲前面都是從單個點開始研究,爲了好理解因此直接使用下標表示,可是在使用線性邊界函數的時候要有一個矩陣的思想。這裏就不細講了,後面多元的就必須使用向量形式了。
好了,基本邏輯已經搞清楚了,就是和線性迴歸相似,只不過這裏處理的是一個線性二分類問題,須要對原始數據處理成線性值而後把線性值映射到 函數的定義域上,根據 函數特徵咱們能夠得出數據分類爲正 的機率大小,而後進行評估。
若是對公式還有什麼不懂的話,參考這篇 機器學習--Logistic迴歸計算過程的推導 裏面講解了如何向量化,若是你對線性代數不熟悉能夠本身去看看這篇文章的講解。下面我只講解代碼,就不對原理重複介紹了。
須要的庫和數據,這裏爲了獲取更好的精度最好使用 float64
:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
x = [1, 2, 3, 4, 6, 7, 8, 9, 10]
y = [0, 0, 0, 0, 1, 1, 1, 1, 1]
train_X = np.asarray(np.row_stack((np.ones(shape=(1, len(x))), x)), dtype=np.float64)
train_Y = np.asarray(y, dtype=np.float64)
train_W = np.asarray([-1, -1], dtype=np.float64).reshape(1, 2)
複製代碼
定義 和 函數
def sigmoid(X):
return 1 / (1 + np.power(np.e, -(X)))
def lossfunc(X, Y, W):
n = len(Y)
return (-1 / n) * np.sum(Y * np.log(sigmoid(np.matmul(W, X))) + (1 - Y) * np.log((1 - sigmoid(np.matmul(W, X)))))
複製代碼
實現參數更新(梯度降低算法)
def gradientDescent(X, Y, W, learningrate=0.001, trainingtimes=500):
n = len(Y)
for i in range(trainingtimes):
W = W - (learningrate / n) * np.sum((sigmoid(np.matmul(W, X)) - Y) * X, axis=1)
複製代碼
這裏的一個重點是把以前的分析向量化了,而後注意維度變化就很簡單了。其中 是一個值得注意的點,它表明求某個維度的和。
其實到這裏整個算法就算是完成了。下面咱們進行可視化,不會的能夠參考我以前的文章 Matplotlib 保存 GIF 動圖,下面給出效果和源代碼:
# coding: utf-8
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
x = [1, 2, 3, 4, 6, 7, 8, 9, 10]
y = [0, 0, 0, 0, 1, 1, 1, 1, 1]
train_X = np.asarray(np.row_stack((np.ones(shape=(1, len(x))), x)), dtype=np.float64)
train_Y = np.asarray(y, dtype=np.float64)
train_W = np.asarray([-1, -1], dtype=np.float64).reshape(1, 2)
def sigmoid(X):
return 1 / (1 + np.power(np.e, -(X)))
def lossfunc(X, Y, W):
n = len(Y)
return (-1 / n) * np.sum(Y * np.log(sigmoid(np.matmul(W, X))) + (1 - Y) * np.log((1 - sigmoid(np.matmul(W, X)))))
Training_Times = 100000
Learning_Rate = 0.3
loss_Trace = []
w_Trace = []
b_Trace = []
def gradientDescent(X, Y, W, learningrate=0.001, trainingtimes=500):
n = len(Y)
for i in range(trainingtimes):
W = W - (learningrate / n) * np.sum((sigmoid(np.matmul(W, X)) - Y) * X, axis=1)
# for GIF
if 0 == i % 1000 or (100 > i and 0 == i % 2):
b_Trace.append(W[0, 0])
w_Trace.append(W[0, 1])
loss_Trace.append(lossfunc(X, Y, W))
return W
final_W = gradientDescent(train_X, train_Y, train_W, learningrate=Learning_Rate, trainingtimes=Training_Times)
print("Final Weight:", final_W)
print("Weight details trace: ", np.asarray([b_Trace, w_Trace]))
print("Loss details trace: ", loss_Trace)
fig, ax = plt.subplots()
ax.scatter(np.asarray(x), np.asarray(y))
ax.set_title(r'$Fitting\ line$')
def update(i):
try:
ax.lines.pop(0)
except Exception:
pass
plot_X = np.linspace(-1, 12, 100)
W = np.asarray([b_Trace[i], w_Trace[i]]).reshape(1, 2)
X = np.row_stack((np.ones(shape=(1, len(plot_X))), plot_X))
plot_Y = sigmoid(np.matmul(W, X))
line = ax.plot(plot_X, plot_Y[0], 'r-', lw=1)
ax.set_xlabel(r"$Cost\ %.6s$" % loss_Trace[i])
return line
ani = animation.FuncAnimation(fig, update, frames=len(w_Trace), interval=100)
ani.save('logisticregression.gif', writer='imagemagick')
plt.show()
複製代碼
先從三元分類開始瞭解,加入給瞭如下分佈,要你求邊界:
比起前面的來就難多了,首先前面是線性分類的而這裏很明顯不是,其次這裏又多出了一個變量。。。別急,對比一下三幅圖你就清楚了。
這裏咱們就解決了多一個變量的問題,經過選取一個主變量爲 ,其它的值就爲 ,而後就能夠進行二分。還有一個問題是如何選取參數?咱們既然能夠轉換成二分問題了,那麼對於每一次二分都只要選取 了。 很明顯有兩個基本的 可是爲了能過選取更加合適的邊界,若是隻是這兩個特徵的話是不夠的,因此其它的 等等,看你要擬合成什麼樣的邊界就選取什麼樣的特徵,同理三維就選取一個能表達彎彎曲曲的平面的特徵量。下面選取權重,也就是特徵前前面的係數,若是特徵選取好了,那麼只要在此基礎上加一個 就行了。
下面咱們來舉個小例子如何操做:
很明顯這裏有一個包圍趨勢,因此理所固然想到圓。也就是說選取的 那麼函數就能夠定義爲:
至於解法就和上面相似了,把這些都抽象成向量,那麼至於什麼維度,有幾個變量都沒差了。須要記得的是,這裏要跑的次數是分類的種數的大小,也就是把每一種都當作主角處理一次獲得屬於它把其它元素區分的分界線,預測是也要把全部模型跑一遍,哪一個模型的機率高就取哪一個做爲分類預測的結果。
這裏就不詳細介紹應用了,之後有機會補充代碼實踐,下面給出幾篇參考博客:
求解最優化問題不僅是隻有梯度降低算法,還有其它可以收斂更快的方式。好比:共軛梯度法 BFGS
(變尺度法) 和 L-BFGS
(限制變尺度法) 等,不過我如今還沒看過這些算法的具體實現,這裏就立下 FLAG 了。不過通常咱們使用這些算法都是調用現成的庫,畢竟本身寫的算法多多少少仍是有缺陷的,
在以前的例子中,你會發現個選取好特徵量以後,怎麼選取迭代次數很重要。舉個小例子:咱們擬合一個符合二次函數分佈的散點圖,若是你選取的模型是 那怎麼跑都跑不出理想結果,必須把 這個特徵項加上,這樣就不會欠擬合。一樣,若是你選取好了一個二次模型,可是迭代次數不夠,也會致使欠擬合,講道理若是模型預測和分佈一致,那麼迭代次數天然是越多越好。但可是,若是你不能肯定,又加一個 的特徵量進去,原本是數據是符合二次函數的特徵,若是訓練次數控制得比較好,也能夠訓練出好模型,無非就是把 的權重置零,若是訓練次數控制很差,那麼必將形成欠擬合(訓練次數過少)或過擬合(訓練次數過多),這裏不是說損失函數值越小越少,而是說那擬合函數的趨勢符合總體數據的趨勢,從機率論的角度講,就是咱們抽取的是整體中的一個樣本,因爲得到全體數據不現實,因此只能使用樣本估計總體,因此這裏的估計的就應該是一個趨勢。
那麼如何避免過擬合或欠擬合呢?我以爲能夠從一下角度考慮:
總之這裏最最關鍵的是模型的準確度,若是一開始模型選錯那怎麼迭代也不會跑出好的效果。
下面給出吳恩達老師的課件圖,你們體會一下:
能夠看到,咱們還有一種方式處理:正則化
我根據我本身的理解來大膽解釋一下吧,若是你有本身的理解就能夠不看了。。避免誤導。正則化通常就是解決過擬合,也就是咱們根本不能肯定趨勢,因此添加了不少多餘的特徵量,那麼對於這個模型什麼樣的結果是咱們能夠接受的呢?若是那些多餘特徵量的權重接近 是否是就意味着咱們也能獲得較好的擬合效果,甚至和沒有這些干擾特徵是達到同樣的效果。這裏就是對那些特徵量的係數進行處罰達到這個目的的。
從上面的解釋能夠看出,咱們不須要對 進行懲罰,因此結果就是在損失函數後面加上 。固然這只是其中一種方式,你能夠選取其可行的方式,主要目的是避免權重過大,擬合的曲線過於彎曲(由於求導以後,斜率和係數是正相關的)。這裏的哲學思想就是在擬合結果可以接受的狀況下,曲線越平滑,容錯能力越高,樣本越可以表明整體。
這裏就直接截圖舉例子了:
正則化線性迴歸
正則化邏輯迴歸
通過一些細節的閱讀,對整個過程的瞭解又加深了很多。動手實踐纔是最好的方式,以前覺得本身公式看懂了就好了,而後此次寫代碼是把一個變量寫在了括號裏面,死活擬合不出效果。而後花了幾個小時以後決心本身再推一遍公式而且從新敲一下公式代碼(由於打印數據發現很詭異),後面就一遍過了。因此若是你也是剛剛開始學習,哪怕照着敲一遍,以後也要本身將思路整理成文字/代碼形式,最好是能整理成博客,方便之後本身複習。
仍是很喜歡那就話:花時間弄懂細節就是節省時間
最後:Happy Year Of Dog ^_^ !