2019/08/25

機器學習_分類

如果要判斷矩形是橫向或是縱向,通常可以直接看出結果。

可以將所有矩形左下角跟原點對齊,以右上角的座標位置,來判斷橫或縱向。分類就是要找出一條分類的線,將兩種矩形分類。

這條線,是將權重向量 (weight) 視為法線的直線,因為互相垂直,所以內積為 0。

\(w \cdot x = \sum_{i=1}^{n} w_ix_i = 0\)

如果有兩個維度,且 \(weight = (1,1)\)

\(w \cdot x = w_1x_1 + w_2x_2 = 1 \cdot x_1 + 1 \cdot x_2 = x_1 + x_2 = 0\)

內積也可以用另一個式子

\(w \cdot x = |w| \cdot |x| \cdot cos𝜃​\)

因為內積為 0,表示 \(cos𝜃 =0\),也就是 \(𝜃=90^o \) 或 \(𝜃=270^o\)

一般來說是要用機器學習,找出權重向量,得到跟該向量垂直的直線,再透過該直線進行分類。

感知器

感知器是一個可以接受多個輸入,並對每一個值,乘上權重再加總,輸出得到的結果的模型。

  • 準備機器學習的資料
矩形大小 形狀 \(x_1\) \(x_2\) \(y\)
80 x 150 縱向 80 150 -1
60 x 110 縱向 60 110 -1
160 x 50 橫向 160 50 1
125 x 30 橫向 125 30 1

識別函數 \(f_w(x)​\) 就是給定向量 \(x​\) 後,回傳 1 或 -1 的函數,用來判斷橫向或縱向。

\(f_w(x) = \left\{\begin{matrix} 1 \quad (w \cdot x \geq 0) \\ -1 \quad (w \cdot x < 0) \end{matrix}\right.​\)

回到 \(w \cdot x = |w| \cdot |x| \cdot cos𝜃\) 這個式子,如果內積為負數,那麼表示 \( 90^o < 𝜃 < 270^o\)

內積是用來表示向量之間相似程度,如果是正數,就是相似,0 是直角,負數代表不相似。

  • 權重更新式

\(w := \left\{\begin{matrix} w + y^{(i)}x^{(i)} \quad (f_w(x) \neq y^{(i)}) \\ w \quad \quad \quad \quad (f_w(x) = y^{(i)}) \end{matrix}\right.​\)

i 是學習資料的索引,這個權重更新是針對所有學習資料重複處理,用來更新權重向量。上面的部分,意思是藉由識別函數進行分類失敗時,才要去更新權重向量。

權重向量是用隨機的值初始化的

因為一開始識別函數的結果為 -1,而學習資料 \(y^{(1)} =1\),所以要更新權重向量

\( w + y^{(1)}x^{(1)} = w + x^{(1)}​\)

運用向量加法得到新的 w,而新的 w 的法線,會讓 (125, 30) 跟 w 向量在同一側。

線性可分離

感知器有個缺點,只能用來解決線性可分離的問題,以下這樣的問題,無法用一條線去分類。

所以如果要處理圖片分類,就沒辦法以線性的方式處理。

上面例子是單層的感知器,多層感知器,就是神經網路。

另外有一種方法,可用在線性不可分離的問題上:邏輯迴歸 Logistic Regression

邏輯迴歸 Logistic Regression

這種方法是將分類用機率來思考。以一開始矩形橫向或縱向的例子,這邊假設橫向為 1,縱向為 0。

S 型函數

前面的回歸有提到 \(f_𝜃(x) = 𝜃^T x​\) 這個函數,可用最速下降法或隨機梯度下降法學習 𝜃,然後用 𝜃 求得未知資料 x 的輸出值。

這邊需要的函數為,其中 \(exp(-𝜃^Tx) = e^{-𝜃^T x}\) , e 為自然對數的底數 Euler's number (2.71828)

\(f_𝜃(x) = \frac{1}{1 + exp(-𝜃^Tx)}​\)

會稱為 S 函數是因為如果將 \(𝜃^Tx\) 設為橫軸,\(f_𝜃(x)\) 設定為縱軸,會出現這樣的圖形

S函數的特徵是 當 \(𝜃^Tx = 0​\) 會得到 \(f_𝜃(x) = 0.5​\),且 \(0< f_𝜃(x) <1 ​\)

當作機率處理的原因是 \(0< f_𝜃(x) <1 \)

決策邊界

將未知資料 x 屬於橫向的機率設為 \(f_𝜃(x)\) ,用條件機率的方式描述為

\(P( y = 1 | x) = f_𝜃(x)​\)

當給予資料 x 時,y=1的機率為 \(f_𝜃(x)\) 。如果計算後的結果,機率為 0.7,就表示矩形為橫向的機率為 0.7。以 0.5 為閥值,判斷是不是橫向。

\(y = \left\{\begin{matrix} 1 \quad (f_𝜃(x) \geq 0.5) \\ 0 \quad (f_𝜃(x) < 0.5) \end{matrix}\right.​\)

回頭看 S 函數,當 \(f_𝜃(x) \geq 0.5​\),也就是 \(𝜃^T x >0​\) ,就判定為橫向。可將判斷式改為

\(y = \left\{\begin{matrix} 1 \quad (𝜃^T x \geq 0) \\ 0 \quad (𝜃^T x < 0) \end{matrix}\right.\)


任意選擇一個 𝜃 為例子,\(x_1\) 是橫長, \(x_2\) 是高

\(𝜃= \left[ \begin{matrix} 𝜃_0 \\ 𝜃_1 \\𝜃_2 \end{matrix} \right] = \left[ \begin{matrix} -100 \\ 2 \\ 1 \end{matrix} \right] , x= \left[ \begin{matrix} 1 \\ x_1 \\x_2 \end{matrix} \right] ​\)

\(𝜃^T x = -100 \cdot 1 +2x_1 +x_2 \geq 0 \)

\( x_2 \geq -2x_1 +100 \) 就表示分類為橫向

以圖形表示

以 \(𝜃^Tx =0\) 這條直線為邊界線,就能區分橫向或縱向,這條線就是決策邊界。但實際上,這個任意選擇的 𝜃 並不能正確的進行分類,因此為了求得正確的 𝜃 ,就要定義目標函數,進行微分,以求得正確的參數 𝜃 ,這個方法就稱為邏輯迴歸。

似然函數

現在要找到 𝜃 的更新式

一開始將 x 為橫向的機率 \(P( y = 1 | x) ​\) 定義為 \(f_𝜃(x)​\) ,根據這個定義,學習資料 \(y​\) 跟 \(f_𝜃(x)​\) 的關係,最佳的狀況是 \(y=1, f_𝜃(x)=1​\) , \(y=0, f_𝜃(x)=0​\) ,但還要改寫為

  • \( y=1 ​\) 時,要讓機率 \(P( y = 1 | x) ​\) 是最大,判定為橫向
  • \( y=0 ​\) 時,要讓機率 \(P( y = 0| x) ​\) 是最大,判定為縱向
矩形大小 形狀 \(y\) 機率
80 x 150 縱向 0 要讓機率 $P( y = 0
60 x 110 縱向 0 要讓機率 $P( y = 0
160 x 50 橫向 1 要讓機率 $P( y = 1
125 x 30 橫向 1 要讓機率 $P( y = 1

因為所有學習資料互相獨立沒有關聯,整體的機率就是全部的機率相乘

\(L(𝜃) = P( y^{(1)} = 0|x^{(1)} ) P( y^{(2)} = 0|x^{(2)} ) P( y^{(3)} = 1|x^{(3)} ) P( y^{(4)} = 1|x^{(4)} ) \)

這個式子可改寫為

\(L(𝜃) = \prod _{i=1}^{n} P( y^{(i)} = 1|x^{(i)} )^{y^{(i)}} P( y^{(i)} = 0|x^{(i)} )^{1-y^{(i)}}​\)


如果假設 \(y^{(i)} = 1​\)

\(P( y^{(i)} = 1|x^{(i)} )^1 P( y^{(i)} = 0|x^{(i)} )^0 = P( y^{(i)} = 1|x^{(i)} )​\)

如果假設 \(y^{(i)} =0 \)

\(P( y^{(i)} = 1|x^{(i)} )^0 P( y^{(i)} = 0|x^{(i)} )^1 = P( y^{(i)} = 0|x^{(i)} )​\)


目標函數 \(L(𝜃)\) 就稱為似然函數 Likelihood,就是要找到讓 \(L(𝜃)\) 最大的參數 𝜃

對數似然函數

因為機率都小於 1,機率的乘積會不斷變小,在程式設計會產生精確度的問題。所以加上 log

\(\log L(𝜃) = \log \prod _{i=1}^{n} P( y^{(i)} = 1|x^{(i)} )^{y^{(i)}} P( y^{(i)} = 0|x^{(i)} )^{1-y^{(i)}}​\)

因為 log 是單調遞增函數,因此不會影響到結果。換句話說,要讓 \(L(𝜃)​\) 最大化,跟要讓 \(logL(𝜃)​\) 最大化是一樣的。

\(\begin{equation} \begin{split} \log L(𝜃) &= \log \prod _{i=1}^{n} P( y^{(i)} = 1|x^{(i)} )^{y^{(i)}} P( y^{(i)} = 0|x^{(i)} )^{1-y^{(i)}}\\ &=\sum_{i=1}^{n}( \log P( y^{(i)} = 1|x^{(i)} )^{y^{(i)}} + log P( y^{(i)} = 0|x^{(i)} )^{1-y^{(i)}} ) \\ &=\sum_{i=1}^{n}( {y^{(i)}} \log P( y^{(i)} = 1|x^{(i)} ) + ({1-y^{(i)}}) \log P( y^{(i)} = 0|x^{(i)} ) ) \\ &=\sum_{i=1}^{n}( {y^{(i)}} \log P( y^{(i)} = 1|x^{(i)} ) + ({1-y^{(i)}}) \log (1-P( y^{(i)} = 1|x^{(i)} )) ) \\ &=\sum_{i=1}^{n}( {y^{(i)}} \log f_𝜃( x^{(i)} ) + ({1-y^{(i)}}) \log (1- f_𝜃(x^{(i)} )) ) \\ \end{split} \end{equation}​\)

  • \(\log(ab) = \log a + \log b\)
  • \(\log a^b = b \log a​\)
  • 因為只考慮 \(y=1\) 或 \(y=0\),所以 \(P( y^{(i)} = 0|x^{(i)} ) = 1 - P( y^{(i)} = 1|x^{(i)} )\)

似然函數的微分

邏輯迴歸,就是將這個對數似然函數當作目標函數使用

\( \log L(𝜃) =\sum_{i=1}^{n}( {y^{(i)}} \log f_𝜃( x^{(i)} ) + ({1-y^{(i)}}) \log (1- f_𝜃(x^{(i)} )) ) ​\)

要將這個函數,個別針對參數 \(𝜃_j\) 進行偏微分

同樣利用合成函數的微分方法

\( u = \log L(𝜃)​\)

\(v = f_𝜃 (x) = \frac{1}{1 + exp(-𝜃^Tx)}​\)

然後

\(\frac{𝜕E}{𝜕𝜃_j} = \frac{𝜕u}{𝜕v} \frac{𝜕v}{𝜕𝜃_j}​\)


先計算第一項

因為 \(\log (v)\) 的微分是 \(\frac{1}{v}\)

而 \( \log (1-v)\) 的微分為

\( s =1-v ​\)

\( t = \log (s) \)

\( \frac{dt}{dv} = \frac{dt}{ds} \cdot \frac{ds}{dv} = \frac{1}{s} \cdot -1 = - \frac{1}{1-v} \)

所以

\( \frac{𝜕u}{𝜕v} = \frac{𝜕}{𝜕v} \sum_{i=1}^{n}( {y^{(i)}} \log (v) + ({1-y^{(i)}}) \log (1- v ) ) = \sum_{i=1}^{n} ( \frac{y^{(i)}}{v} - \frac{1- y^{(ii)} }{1-v} )\)


然後將 \(v\) 以 \(𝜃_j\) 微分

\(\frac{𝜕v}{𝜕𝜃_j} = \frac{𝜕}{𝜕𝜃_j} \frac{1}{ 1+ exp(-𝜃^Tx)} ​\)

因為 \(f_𝜃 (x)\) 是 S 型函數,且已知 S 型函數的微分為

\( \frac{d𝜎(x)}{dx} = 𝜎(x) (1-𝜎(x)) \)

利用合成函數的微分方法

\( z = 𝜃^T x\)

\(v = f_𝜃 (x) = \frac{1}{1 + exp(-z)}\)

\(\frac{𝜕v}{𝜕𝜃_j} = \frac{𝜕v}{𝜕z} \frac{𝜕z}{𝜕𝜃_j} ​\)

前面的部分

\( \frac{𝜕v}{𝜕z} = v(1-v) ​\)

後面的部分

\( \frac{𝜕z}{𝜕𝜃_j} = \frac{𝜕}{𝜕𝜃_j} 𝜃^Tx = \frac{𝜕}{𝜕𝜃_j} (𝜃_0x_0 +𝜃_1x_1 +\cdots + 𝜃_nx_n ) = x_j ​\)

所以

\(\frac{𝜕v}{𝜕𝜃_j} = \frac{𝜕v}{𝜕z} \frac{𝜕z}{𝜕𝜃_j} = v(1-v) x_j ​\)


\(\begin{equation} \begin{split} \frac{𝜕u}{𝜕𝜃_j} &= \frac{𝜕u}{𝜕v} \frac{𝜕v}{𝜕𝜃_j} \\ & = \sum_{i=1}^{n}( \frac{y^{(i)}}{v} - \frac {1 - y^{(i)}}{1-v} ) \cdot v(1-v) \cdot x_j^{(i)} \\ & = \sum_{i=1}^{n}( y^{(i)}(1-v) - (1-y^{(i)})v )x_j^{(i)} \\ & = \sum_{i=1}^{n}( y^{(i)} -v )x_j^{(i)} \\ & = \sum_{i=1}^{n}( y^{(i)} - f_𝜃(x^{(i)}) )x_j^{(i)} \\ \end{split} \end{equation}​\)

先前最小化,是要往微分後的結果的正負符號相反方向移動。但現在要最大化,所以要往微分後的結果的正負符號相同方向移動。

\( 𝜃_j := 𝜃_j + 𝜂 \sum_{i=1}^{n}( y^{(i)} - f_𝜃(x^{(i)}) )x_j^{(i)} ​\)

也能配合多元迴歸,改寫成這樣

\(𝜃_j := 𝜃_j - 𝜂 \sum_{i=1}^{n}( f_𝜃(x^{(i)}) - y^{(i)} )x_j^{(i)}\)

線性不可分離

線性不可分離的問題不能直線,但可嘗試用曲線。

例如,將 \(x_1^2\) 加入學習資料

\(𝜃= \left[ \begin{matrix} 𝜃_0 \\ 𝜃_1 \\𝜃_2 \\𝜃_3 \end{matrix} \right] , x= \left[ \begin{matrix} 1 \\ x_1 \\x_2 \\x_1^2\end{matrix} \right] ​\)

然後

\( 𝜃^Tx = 𝜃_0+𝜃_1x_1+𝜃_2x_2+𝜃_3x_1^2 ​\)

假設

\(𝜃= \left[ \begin{matrix} 𝜃_0 \\ 𝜃_1 \\𝜃_2 \\𝜃_3 \end{matrix} \right] = \left[ \begin{matrix} 0 \\ 0 \\1 \\-1 \end{matrix} \right] \)

因為 \(𝜃^Tx \geq 0​\)

\( 𝜃^Tx = 𝜃_0+𝜃_1x_1+𝜃_2x_2+𝜃_3x_1^2 = x_2 - x_1^2 \geq 0 ​\)

得到方程式 \( x_2 \geq x_1^2 ​\)

現在的決策邊界變成曲線,因為參數 𝜃 是任意選擇的,所以資料沒有被正確地分類。

可以增加次方數,得到複雜的形狀的決策邊界。

另外還有 SVM (支援向量機) 的分類演算法,多元分類處理方法。

References

練好機器學習的基本功:用Python進行基礎數學理論的實作

2019/08/18

機器學習_線性迴歸

機器學習擅長

  1. 回歸 regression

    將連續性的資料進行觀察,用以預測未來的結果。例如股價、身高、體重

  2. 分類 classification

    收集既有的資料,進行訓練,根據訓練結果預測新資料的分類。例如:垃圾郵件判斷、手寫數字辨識

  3. 分群 clustering

    根據資料進行分群,但跟分類不同,分類的訓練資料已經有標記結果,要用來分群的資料並沒有群組的標記。例如:根據學測成績,進行文理組分群

使用具有標記的資料進行機器學習,稱為監督式學習。

使用不具有標記的資料進行機器學習,稱為非監督式學習。

線性迴歸(Linear regression)

當我們取得原始量測資料時,如果在平面座標上標記這些量測點,會感覺到這些點之間,可以畫出最接近這些點的一條直線方程式,線性回歸方法,可以找到這樣的方程式,未來就可以根據這個方程式,預測數值。

從圖形看起來,我們可找到一條「最接近」所有紅色觀測值的直線,以直線方程式 \({f(x)=ax+b}\) 表示這條直線,我們要做的就是找到一個方法,確定 a 與 b 的值,未來就可以利用這個方程式預測數據。在統計中,為了因應未來可能有很多未知數的問題,改以這樣的寫法:

\({f(x)=𝜃_0+𝜃_1x}\)

最小平方法

假設目前有這些數據,當我們任意找 \({𝜃_0 =1, 𝜃_1=2}​\) 時,f(x) 跟實際上的 y 之間有誤差。所以要找到適當的參數,讓 f(x) 與 y 之間的誤差最小,當然如果誤差為 0 是最好的。

x y f(x)
58 374 117
70 385 141
81 375 163
84 401 169

定義誤差函數為

\({E(𝜃)= \frac{1}{2} \sum_{i=1}^{n}( y^{(i)} - f_𝜃(x^{(i)})^2 }\)

  • \(x^{(i)}, y^{(i)}\) 分別是第 i 項的 x 與 y,例如 \(x^{(1)} = 58, y^{(1)}=374\)
  • \({(y^{(i)} - f_𝜃(x^{(i)}) }\) 是誤差值,但因為誤差有可能是負數,所以就用平方,轉成正數
  • 將所有誤差的平方加總後,為了微分計算方便,就在前面再乘上 1/2。因為乘上任意的正數,只會讓圖形橫向壓扁,但不會改變最小值的位置。任意的正數都可以,選擇 1/2 是因為後面的例子,f(x) 是二次函數的關係。

在讓 E(𝜃) 最小的狀況下,找到的 \(𝜃_0, 𝜃_1​\) 就是最小平方法

最速下降法

剛剛要讓 E(𝜃) 最小的狀況下,必須不斷地找到不同的 \(𝜃_0, 𝜃_1​\),這個計算很麻煩,可用微分來解決,因為微分就是在找函數切線斜率的變化。

例如 \(f(x) = (x-1)^2\) ,微分後 \(f'(x) = 2x-2\)

x f'(x) 的正負 f(x) 遞增或遞減
\(x < 1\) \(-\) 遞減
\(x=0\) 0
\(x>1\) \(+\) 遞增

f(x) 的圖形如下,當 x 由 3 往 1 逼近,f(x) 就越來越小,另外當 x 由 -1 往 1 逼近,f(x) 也會越來越小

意思就是說,只要 x 往導函數 (微分) 的反方向移動,就函數值會往最小值移動。

最速下降法(梯度下降法) 就是定義為

\(x := x - 𝜂 \frac{d}{dx}f(x)​\)

以實際的數字為例,當 \(𝜂 = 1, x = 3​\),x 會在 3 與 -1 之間往返

\(x := 3-1(2*3-2) = -1​\)

\(x := -1-1(2*(-1)-2) = 3​\)

當 \(𝜂 = 0.1, x = 3\),x 會往最小值逼近

\(x := 3-0.1(2*3-2) = 2.6​\)

\(x := 2.6-0.1(2*2.6-2) = 2.3​\)

\(x := 2.3-0.1(2*2.3-2) = 2.1​\)

當 𝜂 越大,x 就會往返,當 𝜂 越小,x 會往最小值逼近


回到剛剛的 誤差函數

\({E(𝜃)= \frac{1}{2} \sum_{i=1}^{n}(y^{(i)} - f_𝜃(x^{(i)})^2 }\)

因為 \(f_𝜃(x^{(i)})\) 是 \({f(x)=𝜃_0+𝜃_1x}\) ,有兩個未知的參數 \(𝜃_0, 𝜃_1\) ,要改用偏微分找最小值。

\(𝜃_0 := 𝜃_0 - 𝜂 \frac{𝜕E}{𝜕𝜃_0}​\)

\(𝜃_1 := 𝜃_1 - 𝜂 \frac{𝜕E}{𝜕𝜃_1}​\)

因 E(𝜃) 裡面有 \(f_𝜃(x)​\),而 \(f_𝜃(x)​\) 裡面有 𝜃

\(u = E(𝜃)​\)

\(v = f_𝜃(x)​\)

然後用合成函數的方式,計算微分

\(\frac{𝜕E}{𝜕𝜃_0} = \frac{𝜕u}{𝜕v} \frac{𝜕v}{𝜕𝜃_0}​\)

其中,前面的部分

\( \frac{𝜕u}{𝜕v} = \frac{𝜕}{𝜕v}( \frac{1}{2} \sum_{i=1}^{n}(y^{(i)} - v)^2 ) = \frac{1}{2} \sum_{i=1}^{n}\frac{𝜕}{𝜕v}(y^{(i)} - v)^2 = \frac{1}{2}\sum_{i=1}^{n}( -2y^{(i)} +2v ) = \sum_{i=1}^{n}( v-y^{(i)} )\)

後面的部分

\(\frac{𝜕v}{𝜕𝜃_0} = \frac{𝜕}{𝜕𝜃_0}( 𝜃_0 + 𝜃_1x ) = 1​\)

所以

\(\frac{𝜕E}{𝜕𝜃_0} = \sum_{i=1}^{n}( f_𝜃(x^{(i)})-y^{(i)} )\)

另外對 \(𝜃_1​\) 微分,可得到

\(\frac{𝜕v}{𝜕𝜃_1} = \frac{𝜕}{𝜕𝜃_1}( 𝜃_0 + 𝜃_1x ) = x\)

\(\frac{𝜕E}{𝜕𝜃_1} = \sum_{i=1}^{n}( f_𝜃(x^{(i)} )-y^{(i)} )x^{(i)} \)


最後

\(𝜃_0 := 𝜃_0 - 𝜂 \sum_{i=1}^{n}( f_𝜃(x^{(i)} )-y^{(i)} )\)

\(𝜃_1 := 𝜃_1 - 𝜂 \sum_{i=1}^{n}( f_𝜃(x^{(i)} )-y^{(i)} )x^{(i)}​\)

用這個方法,就可以找出正確的 \(𝜃_0, 𝜃_1​\)

多項式回歸

一開始,我們假設數據的模型是線性的,所以使用一次函數,但也可能用二次或更高次的函數來定義 \(f_𝜃(x)​\),會更貼近原本的數據模型

\(f_𝜃(x) = 𝜃_0 + 𝜃_1x + 𝜃_2x^2​\)

\(f_𝜃(x) = 𝜃_0 + 𝜃_1x + 𝜃_2x^2 + \dots +𝜃_nx^n​\)

回到剛剛的問題,要對 \(𝜃_2​\) 進行偏微分

對 \(𝜃_1​\) 微分,可得到

\(\frac{𝜕v}{𝜕𝜃_2} = \frac{𝜕}{𝜕𝜃_2}( 𝜃_0 + 𝜃_1x +𝜃_2x^2 ) = x^2\)

\(\frac{𝜕E}{𝜕𝜃_2} = \sum_{i=1}^{n}( f_𝜃(x^{(i)} )- y^{(i)} )(x^{(i)} )^2\)

多元回歸

目前解決的問題,都只有一個變數 x,但大多數的問題,都是有兩個以上的變數。例如廣告的點擊率,可能會受廣告費、顯示位置、顯示大小 等原因影響。

\(f_𝜃(x_1, x_2, x_3)=𝜃_0+𝜃_1x_1+𝜃_2x_2+𝜃_3x_3\)

當變數有 n 個,可改用向量的方式表示

\(𝜃= \left[ \begin{matrix} 𝜃_0 \\ 𝜃_1 \\𝜃_2 \\. \\. \\𝜃_n \end{matrix} \right] x= \left[ \begin{matrix} x_1 \\ x_2 \\x_3 \\. \\. \\x_n \end{matrix} \right] ​\)

但因為 𝜃 跟 x 個數不同,不容易計算,就再加上一項 \(x_0 =1​\)

\(𝜃= \left[ \begin{matrix} 𝜃_0 \\ 𝜃_1 \\𝜃_2 \\. \\. \\𝜃_n \end{matrix} \right] x= \left[ \begin{matrix} x_0 \\ x_1 \\x_2 \\. \\. \\x_n \end{matrix} \right] \)

將 𝜃 變成轉置矩陣後,再跟 x 相乘,就會是剛剛的 \(f_𝜃(x)\)

\(𝜃^Tx = 𝜃_0x_0+𝜃_1x_1+ \dots + 𝜃_nx_n = f_𝜃(x) \)

變成向量後,再用剛剛合成函數偏微分的方法

\(u = E(𝜃)​\)

\(v = f_𝜃(x)​\)

\(\frac{𝜕u}{𝜕𝜃_j} =\frac{𝜕E}{𝜕𝜃_j} = \frac{𝜕u}{𝜕v} \frac{𝜕v}{𝜕𝜃_j}\)

前面的部分一樣,後面的部分

\(\frac{𝜕v}{𝜕𝜃_j} = \frac{𝜕}{𝜕𝜃_j}( 𝜃^Tx ) = \frac{𝜕}{𝜕𝜃_j}( 𝜃_0x_0+𝜃_1x_1+\dots+𝜃_nx_n )= x_j\)

第 j 項參數的定義為

\(𝜃_j := 𝜃_j - 𝜂 \sum_{i=1}^{n}( f_𝜃(x^{(i)} )-y^{(i)} )x_j^{(i)}\)

當變數增加,計算量變大,用最速下降法會導致計算速度變慢,可用隨機梯度下降法改進。

最速下降法除了有計算速度慢的問題,還有可能陷入局部解的問題,像以下的函數圖形中,不同的起點,可能會找到局部最小值。

隨機梯度下降法

在多元迴歸中,第 j 項參數的定義為

\(𝜃_j := 𝜃_j - 𝜂 \sum_{i=1}^{n}( f_𝜃(x^{(i)} )-y^{(i)} )x_j^{(i)}​\)

但因為用到所有的資料的誤差,計算量太大,隨機梯度下降法式隨機選擇一項學習資料,套用在參數的更新上,例如選擇第 k 項。

\(𝜃_j := 𝜃_j - 𝜂 ( f_𝜃(x^{(k)} )-y^{(k)} )x_j^{(k)}​\)

原本最速下降法用來更新一次參數的時間,隨機梯度下降法可更新 n 次參數。因為是隨機選擇學習資料,不會陷入局部解的問題。


另外也有隨機選擇 m 筆學習資料的方法,也稱為小量批次資料法,假設 m 筆資料的集合為 K

\(𝜃_j := 𝜃_j - 𝜂 \sum_{k𝜖K} ( f_𝜃(x^{(k)} )-y^{(k)} )x_j^{(k)}​\)

References

練好機器學習的基本功:用Python進行基礎數學理論的實作

2019/08/12

erlang lager with date in log filename

erlang lager 預設是以設定中的 filename 加上 .1 .2 的 postfix 作為 logfile rotate 的依據,但通常在使用 logfile,會希望直接在 logfile 看到產生該 log 的日期,這時需要使用 Custom Log Rotation 的功能,自己撰寫 log_rotator。

首先我們先找到 lager 原始程式碼中預設的 lagerrotatordefault.erl,先複製成 mylagerlog_rotator,然後修改裡面的程式碼。

-module(my_lager_log_rotator).

-include_lib("kernel/include/file.hrl").

-behaviour(lager_rotator_behaviour).

-export([
  create_logfile/2, open_logfile/2, ensure_logfile/4, rotate_logfile/2
]).

create_logfile(Name, Buffer) ->
  {{Y, M, D}, {H, _, _}} = calendar:now_to_local_time(os:timestamp()),
  DateHour =  {Y, M, D, H},
  FileName = filename(Name, DateHour, 1),
  file:delete(Name),
  file:make_symlink(filename:absname(FileName), Name),
  open_logfile(Name, Buffer).

open_logfile(Name, Buffer) ->
  case filelib:ensure_dir(Name) of
    ok ->
      Options = [append, raw] ++
        case  Buffer of
          {Size, Interval} when is_integer(Interval), Interval >= 0, is_integer(Size), Size >= 0 ->
            [{delayed_write, Size, Interval}];
          _ -> []
        end,
      case file:open(Name, Options) of
        {ok, FD} ->
          case file:read_file_info(Name) of
            {ok, FInfo} ->
              Inode = FInfo#file_info.inode,
              {ok, {FD, Inode, FInfo#file_info.size}};
            X -> X
          end;
        Y -> Y
      end;
    Z -> Z
  end.


ensure_logfile(Name, FD, Inode, Buffer) ->
  case file:read_link(Name) of
    {ok, _} ->
      lager_ensure_logfile(Name, FD, Inode, Buffer);
    _ ->
      create_logfile(Name, Buffer)
  end.


lager_ensure_logfile(Name, undefined, _Inode, Buffer) ->
  open_logfile(Name, Buffer);
lager_ensure_logfile(Name, FD, Inode, Buffer) ->
  case file:read_file_info(Name) of
    {ok, FInfo} ->
      Inode2 = FInfo#file_info.inode,
      case Inode == Inode2 of
        true ->
          {ok, {FD, Inode, FInfo#file_info.size}};
        false ->
          %% delayed write can cause file:close not to do a close
          _ = file:close(FD),
          _ = file:close(FD),
          case open_logfile(Name, Buffer) of
            {ok, {FD2, Inode3, Size}} ->
              %% inode changed, file was probably moved and
              %% recreated
              {ok, {FD2, Inode3, Size}};
            Error ->
              Error
          end
      end;
    _ ->
      %% delayed write can cause file:close not to do a close
      _ = file:close(FD),
      _ = file:close(FD),
      case open_logfile(Name, Buffer) of
        {ok, {FD2, Inode3, Size}} ->
          %% file was removed
          {ok, {FD2, Inode3, Size}};
        Error ->
          Error
      end
  end.
%%
%%%% renames failing are OK
%%rotate_logfile(File, 0) ->
%%  %% open the file in write-only mode to truncate/create it
%%  case file:open(File, [write]) of
%%    {ok, FD} ->
%%      file:close(FD),
%%      ok;
%%    Error ->
%%      Error
%%  end;
%%rotate_logfile(File0, 1) ->
%%  File1 = File0 ++ ".0",
%%  _ = file:rename(File0, File1),
%%  rotate_logfile(File0, 0);
%%rotate_logfile(File0, Count) ->
%%  File1 = File0 ++ "." ++ integer_to_list(Count - 2),
%%  File2 = File0 ++ "." ++ integer_to_list(Count - 1),
%%  _ = file:rename(File1, File2),
%%  rotate_logfile(File0, Count - 1).
%%

rotate_logfile(Name, _Count) ->
  case file:read_link(Name) of
    {ok, LinkedName} ->
      case filelib:file_size(LinkedName) of
        0 ->
          %% if the files size is zero, it is removed
          catch file:delete(LinkedName);
        _ ->
          void
      end;
    _ ->
      void
  end,
  {ok, {FD, _, _}} = create_logfile(Name, []),
  file:close(FD).

%% @doc Create name of a new file
%% @private
filename(BaseFileName, DateHour, Branch) ->
  FileName = lists:append([BaseFileName,
    suffix(DateHour, false), ".", integer_to_list(Branch)
  ]),
  case filelib:is_file(FileName) of
    true ->
      filename(BaseFileName, DateHour, Branch + 1);
    _ ->
      FileName
  end.

%% @doc Zero-padding number
%% @private
zeropad(Num, MinLength) ->
  NumStr = integer_to_list(Num),
  zeropad_str(NumStr, MinLength - length(NumStr)).
zeropad_str(NumStr, Zeros) when Zeros > 0 ->
  zeropad_str([$0 | NumStr], Zeros - 1);
zeropad_str(NumStr, _) ->
  NumStr.

%% @doc Create a suffix
%% @private
suffix({Y, M, D, H}, WithHour) ->
  YS = zeropad(Y, 4),
  MS = zeropad(M, 2),
  DS = zeropad(D, 2),
  HS = zeropad(H, 2),
  case WithHour of
    true ->
      lists:flatten([$., YS, MS, DS, $., HS]);
    _ ->
      lists:flatten([$., YS, MS, DS])
  end.

將 mylagerlogrotator 套用在 lager 的設定檔的 lagerfile_backend 中,{rotator, my_lager_log_rotator}

[
  {lager, [
    {log_root, "./log"},
    {crash_log, "crash.log"},
    {error_logger_redirect, false},
    {colored, true},
    {colors, [
      {debug,     "\e[0;36m" },
      {info,      "\e[1;37m" },
      {notice,    "\e[1;36m" },
      {warning,   "\e[1;33m" },
      {error,     "\e[1;31m" },
      {critical,  "\e[1;35m" },
      {alert,     "\e[1;44m" },
      {emergency, "\e[1;41m" }
    ]},
    {handlers, [
      {lager_console_backend, [{level, debug}, {formatter, lager_default_formatter},
        {formatter_config, [date, " ", time, color, " ", pid, " ", module, ":", line, " [", severity, "] ", message, "\e[0m\n"]}]},
      {lager_file_backend, [{file, "debug.log"}, {level, debug}, {size, 3000}, {date, "$H00"}, {count, 2},
        {formatter_config, [date, " ", time, " ", pid, " ", module, ":", line, " [", severity, "] ", message, "\n"]}, {rotator, my_lager_log_rotator}]}
    ]}
  ]}
].

現在就會產生像這樣的 logfile

debug.log (symbolic link to debug.log.20190426.2)
debug.log.20190426.1
debug.log.20190426.2

目前還會需要調整的是設定檔中的 count,如果在 logfile 加上日期,count 應該是要代表保留幾天的資料,但目前還是依照 lager 原本的定義,為保留幾個同樣 prefix 檔名的 logfile。

References

erlang lager

leologgerrotator.erl

2019/08/05

奧卡姆剃刀 Occam's Razor, Ockham's Razor

奧卡姆剃刀,是由14世紀邏輯學家、聖方濟各會修士奧卡姆的威廉(William of Occam,約1285年至1349年)提出。奧卡姆(Ockham)在英格蘭的薩裡郡,那是他出生的地方。他在《箴言書註》2捲15題說「切勿浪費較多東西,去做『用較少的東西,同樣可以做好的事情』。」後來這個原理也簡化為「如無必要,勿增實體。」(Do not multiply entities beyond necessity.)

另外一些更簡要的說法是:避重趨輕、避繁逐簡、以簡御繁、避虛就實,更白話的說法是「夠用就好」

奧卡姆剃刀並不是一個數學定理,他是一個思考的原則,這個原則告訴我們應該追求簡化,不追求完全正確的答案,選擇一個最簡單的方式,來解釋與面對問題。

剃刀原則並不是說簡單的理論就是正確的理論,應該了解為「當兩個假說具有完全相同的解釋力和預測力時,我們以那個較為簡單的假說作為討論依據。」

這個原則套用在不同知識領域時:

  1. 科學領域:當你有兩個處於競爭地位的理論能得出同樣的結論,那麼簡單的那個更好。
  2. 企業管理領域:在管理企業制定決策時,應該儘量把複雜的事情簡單化,剔除干擾,解決最根本的問題,才能讓企業保持正確的方向。
  3. 投資領域:面對複雜當投資市場,應把複雜事情簡單化,簡化自己的投資策略,擺脫那些消耗了大量金錢、時間、精力的事情。
  4. 組織結構:組織結構扁平化與組織結構非層級化已經成為企業組織變革的趨勢。員工之間的關係是平等的分工合作關係,基層員工被賦予更多的權力。由於員工的積極參與,組織目標與個人目標之間的矛盾得到最大程度地消除。
  5. 簡化文書流程:簡單的信息遠比複雜的信息更有利於人們的思考與決策。
  6. 日心說/地心說:五百年前,還不知道世界的中心是太陽還是地球,但利用望遠鏡得到的許多數據,可證明地球跟太陽都可能是中心,不過日心說只需要 7 個假設,而地心說需要更多假設,於是哥白尼在天體運行論中利用奧卡姆剃刀原則,判斷太陽才是中心。

跟人溝通時,如果遇到一些喜歡長篇大論的人,就會覺得講半天講不到重點,但講話太精簡,簡單到讓人聽不懂也是問題,精簡程度怎麼拿捏就是個人修養的問題。

References

奧卡姆剃刀定律

奧卡姆剃刀定律 wiki

奧卡姆剃刀決策原則

設計法則:奧卡姆剃刀原理