機器學習基石作業01:PLA算法和Pocket算法實現

本文總結了機器學習基石第一次作業,包含20個題目,有6個需要編程實現,涉及線性感知器算法(PLA)和Pocket算法。


在這裏插入圖片描述
判斷一個問題是否適合採用機器學習方法的三個準則:

  • 存在某種可以被學習到的潛在規律;
  • 潛在規律難以被簡單的定義,直接編寫潛在規律的程序是困難的;
  • 存在某些與潛在規律相關的數據(即與問題本身相關的數據);

哪些問題適合採用機器學習的方法?根據上述三條準則,不難選擇②④⑤


在做2-5題時,首先回顧機器學習問題的劃分。

1.根據輸出空間 Y\mathbb{Y} 的不同可以分爲:

  • 分類問題(Classification)Y=1,2,...,K\mathbb{Y}={1,2,...,K}KK 表示類別數;
  • 迴歸問題(Regression)Y=R\mathbb{Y} = \R
  • 結構化學習(Structured Learning):$\mathbb{Y} = $ 結構化數據,例如序列(sequence)、列表(List)或者樹(Tree)等,而不僅限於向量(vector)。

2.根據期望輸出 yny_n 的不同可以分爲:

  • 監督學習(Supervised Learning):每個樣本都包含期望輸出 yny_n (比如分類問題中的標籤,迴歸問題中的相應的實際預測值);
  • 無監督學習(Unsupervised Learning):每個樣本都沒有期望輸出 yny_n
  • 半監督學習(Semi-Supervised Learning):只有部分樣本有期望輸出 yny_n
  • 強化學習(Reinforcement learning):是機器學習中的一個領域,強調如何基於環境而行動,以取得最大化的預期利益。

3.根據學習方式不同可以分爲:

  • 批量學習(Batch Learning):無法進行增量學習,使用全部數據訓練模型。
  • 在線學習(Online Learning):通過mini-batch依次輸入數據逐步訓練模型。
  • 主動學習(Active Learning):“Don’t learn to do, but learn in doing.” –Samuel Butler。在某些情況下,未標記的數據很多,但是手動標記的成本很高。在這種情況下,學習算法可以主動向用戶/教師查詢標籤。這種類型的迭代監督學習稱爲主動學習。

在這裏插入圖片描述

通過不同的決策方法並根據反饋結果來不斷提高下棋水平?強化學習(reinforcement learning)


在這裏插入圖片描述
將沒有任何標籤的書籍進行分類?無監督學習(unsupervised learning)


在這裏插入圖片描述

通過1000張有人臉的照片和10000張無人臉的照片來識別其它照片中是否有人臉?監督學習(supervised learning)


在這裏插入圖片描述
有選擇地進行小白鼠實驗,以快速評估癌症藥物的潛力?主動學習(active learning)


問題6-8主要涉及訓練集之外數據的誤差(Off-Training-Set error ,OTS)的分析。
在這裏插入圖片描述
假設對於全部的xxf(x)=+1f(x)=+1,並且 x=xkx=x_k(其中 kk 爲奇數)時, g(x)=+1g(x)=+1;其他情況,g(x)=1g(x)=-1,求 EOTS(g,f)=?E_{OTS}(g,f)=?ff爲目標函數,gg爲假設函數/似然函數)

對於所有的輸入 xxf(x)f(x) 的輸出都爲 +1+1 ;而 g(x)g(x) 只有輸入爲奇數時才爲 +1+1 ,此時,由誤差公式可知,不產生誤差;只有當輸入 xx 爲偶數時纔會產生誤差,也就是說問題轉化爲求解N+1N+1N+LN+L之間有多少個偶數。先求 11N+LN+L 之間的偶數個數,再減去 11NN 之間的偶數個數,易得到結果:(N+L2N2)(\lfloor\frac{N+L}{2}\rfloor-\lfloor\frac{N}{2}\rfloor),所以最終結果爲1L×(N+L2N2)\frac{1}{L}\times(\lfloor\frac{N+L}{2}\rfloor-\lfloor\frac{N}{2}\rfloor)


在這裏插入圖片描述
由題設可知,存在目標函數 f(x)f(x) ,若對訓練集中的樣本{(xn,yn)}n=1ND\{(x_n,y_n)\}_{n=1}^N\in D 均有 f(xn)=ynf(x_n)=y_n,則爲無噪聲設置。對於一切可能的 f:XYf: \mathbb{X}\to\mathbb{Y},存在多少種可能的 ff,使得數據集 DD 滿足無噪聲設置?(注:如果對於所有的 xXx \in \mathbb{X} ,有 f1=f2f_1=f_2,則認爲兩個函數相同)

數據集 X={x1,x2,...,xN,xN+1,...,xN+L}\mathbb{X}=\{x_{1},x_2,...,x_N,x_{N+1},...,x_{N+L}\}DD 中都是無噪聲設置,所以不同的 f(x)f(x) 在樣本 {xN+1,xN+2,...,xN+L}\{x_{N+1},x_{N+2},...,x_{N+L} \}中產生,Y={1,+1}\mathbb{Y}=\{-1,+1\},所以可以產生 2L2^L 個不同的 f(x)f(x)


在這裏插入圖片描述
一個確定的算法A\mathbb{A}定義爲以訓練集DD爲輸入,輸出一個假設函數gg。如果對於全部滿足問題7條件的 ff 產生的可能性是相等的,則對於兩個算法 A1\mathbb{A}_1A2\mathbb{A}_2,哪個等式成立?

由於算法 A\mathbb{A} 產生的假設函數 gg 對訓練集之外的數據 (xN+1,...,xN+L)(x_{N+1},...,x_{N+L}) 產生的結果 (yN+1,...,yN+L)(y_{N+1},...,y_{N+L}) 只可能是 2L2^L(±1\pm1的全部可能)中的一種,由於EOTS(g,f)E_{OTS}(g,f) 僅和訓練集DD之外的數據有關,因此完全可以將 gg 視爲全部可能的 ff (總共2L2^L種)中的一種而不影響最終結果。因此,問題就轉換爲全部ff中的任意兩個f1f_1f2f_2關於EOTSE_{OTS}的問題了。則顯然有Ef{EOTS(f1,f)}=Ef{EOTS(f2,f)}E_f\{E_{OTS}(f_1,f)\}=E_f\{E_{OTS}(f_2,f)\} 成立。


問題9-12主要考察對容器模型(本系列課程中爲一個罐子)的理解。

題設:考慮一個含有無限彈珠的容器(僅有橙色和綠色兩種類型彈珠),容器中橙色彈珠的比例爲 μ\mu;從容器中抽取10顆彈珠,其中橙色彈珠的比例爲 ν\nu
在這裏插入圖片描述
如果 μ=0.5\mu=0.5,則 ν=μ\nu=\mu 的概率爲多少?

學過概率論應該不難,簡單的組合問題,C1050.550.55=0.246C_{10}^5 0.5^50.5^5=0.246


在這裏插入圖片描述

如果 μ=0.9\mu=0.9,則 ν=μ\nu=\mu 的概率爲多少?

C1090.990.11=0.387C_{10}^9 0.9^90.1^1=0.387


在這裏插入圖片描述
如果 μ=0.9\mu=0.9,則 ν0.1\nu\leq 0.1 的概率爲多少?

兩種情況:沒有橙色彈珠和只有一顆橙色彈珠。C1010.910.19+C1000.900.110=9.1×109C_{10}^1 0.9^{1}0.1^9+C_{10}^0 0.9^{0} 0.1^{10}=9.1\times 10^{-9}


在這裏插入圖片描述
如果 μ=0.9\mu=0.9,則由霍夫丁不等式(Hoeffding’s Inequality)計算 ν0.1\nu\leq 0.1 的上界爲多少?

Hoeffding 不等式:

P[νμ>ϵ]2exp(2ϵ2N)P[|\nu-\mu|>\epsilon]\leq2exp(-2\epsilon^2N)

由題意得 ϵ=0.8\epsilon=0.8,代入霍夫丁不等式得P[νμ>ϵ]5.52×106P[|\nu-\mu|>\epsilon]\leq5.52\times10^{-6},與問題11中的結果對比可知,該概率明顯大於上述值。從而說明霍夫丁不等式獲得的上界要遠大於真實情況,從側面反映了後面幾章中對於樣本估計和真實情況近似可接受度的說明。

再回顧一下課程所學:

一個罐子裏放着很多彈珠,有兩種顏色(橙色,綠色),設罐中橘色彈珠的比例爲 μ\mu (未知)。從罐中隨機取出 NN 個,其中橘色彈珠的比例爲 ν\nu(已知)。根據霍夫丁不等式:若N足夠大,ν\nu 就很可能接近 μ\mu

同理,在機器學習中:NN 足夠大時(樣本數量足夠多),可以用訓練集 DD 上的 [h(x)f(x)][h(x)≠f(x)] 來推測數據集 X{\mathbb{X}} 上的 [h(x)f(x)][h(x)≠f(x)]。即備選假設函數 hhDD 上犯錯誤的比例接近其在數據集 X{\mathbb{X}} 上犯錯誤的比例。設某一備選假設函數 hh 在訓練集 DD 上的犯錯比例爲 Ein(h)E_{in}(h),在整個數據集上的犯錯比例爲 Eout(h)E_{out}(h),則有:

P[Ein(h)Eout(h)>ϵ]2exp(2ϵ2N)P[|E_{in}(h)-E_{out}(h)|>\epsilon]\leq2exp(-2\epsilon^2N)

通過上式,可以根據備選假設函數 hh 在訓練集 DD 上的表現來衡量它的正確性,並最終從備選假設函數集 HH 中選出最優的 hh 作爲 gg,且 gfg≈f


問題13-14題設:

一個袋子裏含有無數個骰子,這些骰子共有四種不同類型,每種類型出現的概率相等:

  • a. 所有偶數面爲橙色,奇數面爲綠色;
  • b. 所有偶數面爲綠色,奇數面爲橙色;
  • c. 數字1-3面爲橙色,數字4-6面爲綠色;
  • d. 數字1-3面爲綠色,數字4-6面爲橙色。
    在這裏插入圖片描述
    如果從袋子中取出5顆骰子,則獲得的5顆骰子數字1面全爲橙色的概率?
    數字1爲橙色的有(b)©兩種類型,因此取出一個的概率爲0.5,所以可得:P=0.55=8256P=0.5^5=\frac{8}{256}

在這裏插入圖片描述
如果從袋子中取出5顆骰子,則獲得的5顆骰子某個面均爲橙色的概率?

單次取,將滿足條件的兩兩組合,全部可能的組合爲{(a)©, (a)(d), (b)©, (b)(d)},由於組合中,(1)和(3)中有C重複,(1)和(4)中有B重複,(2)和(3)中有A重複,(2)和(4)中有D重複,所以需要去掉重複的情況,最終的計算公式爲:P=0.55×40.255×4=31256P=0.5^5\times 4-0.25^5\times 4=\frac{31}{256}


問題15-20,主要考察PLA和Pocket算法。

PLA算法(Percetron Learning Algorithm):

初始化w=0w=0
For t=0,1,…
① 尋找滿足條件 sign(wtTxn(t))yn(t)sign(w_t^Tx_{n(t)})\neq y_{n(t)} 的樣本 (xn(t),yn(t))(x_{n(t)}, y_{n(t)})
② 按照 wt+1wt+yn(t)xn(t)w_{t+1}\leftarrow w_t+y_{n(t)}x_{n(t)} 修正 ww
直到沒有錯誤,終止循環。

在步驟 ① 中可以採用不同的策略:

  • a) 每次從前往後(1,...,N)(1,...,N)尋找“錯誤數據” ;
  • b) 每次從前一次“錯誤數據”開始往後尋找“錯誤數據”;
  • c) 每次隨機打亂數據,按打亂後的順序從前往後尋找“錯誤數據”;

後兩種策略的計算速度相對更快。

PLA算法只能處理線性可分的數據,Pocket算法則不然。


Pocket算法:

初始化w=0w=0
For t=0,1,…
① 尋找滿足條件 sign(wtTxn(t))yn(t)sign(w_t^Tx_{n(t)})\neq y_{n(t)} 的數據 (xn(t),yn(t))(x_{n(t)}, y_{n(t)})
② 按照 wt+1wt+yn(t)xn(t)w_{t+1}\leftarrow w_t+y_{n(t)}x_{n(t)} ,修正錯誤;
③ 如果 wt+1w_{t+1} 的錯誤率小於 wtw_t,則令 w=wt+1w=w_{t+1}
達到預先設定的循環次數,終止循環;
返回ww

在步驟 ① 中,通常採用每次隨機打亂數據,按打亂後的順序從前往後尋找“錯誤數據”的策略,編程實現則直接找出所有錯誤,再從中隨機選一個。

數據集下載:https://www.csie.ntu.edu.tw/~htlin/mooc/datasets/mlfound_math/hw1_15_train.dat 。
在這裏插入圖片描述
通過在naive cycle(發生錯誤時不中止,而是繼續運行)中按照數據集中樣本的順序讀取來實現PLA算法。在算法停止之前,更新的次數是多少?

39

在這裏插入圖片描述
在數據集上運行算法,重複2000次實驗,每個實驗使用不同的隨機種子。 在算法停止之前,平均更新次數是多少?

39.789

在這裏插入圖片描述
不同η\eta情況下的感知機平均更新次數:

40.1915

問題17和問題16的結果基本一致的原因是:
參數同時縮放對sign(wTx)sign(w^Tx)來說是一樣的,但當初始w0w\neq 0時,兩問的結果還是有一定差別的。


Pocket算法

數據集下載:

https://www.csie.ntu.edu.tw/~htlin/mooc/datasets/mlfound_math/hw1_18_train.dat
https://www.csie.ntu.edu.tw/~htlin/mooc/datasets/mlfound_math/hw1_18_test.dat
在這裏插入圖片描述

50次更新情況下的測試集錯誤率:

0.1327739999999998

在這裏插入圖片描述
50次更新情況下,最後一次theta作爲參數時測試集的錯誤率:

0.35281599999999985

在這裏插入圖片描述
100次更新情況下的測試集錯誤率:

 0.11573000000000029

完整代碼:

import numpy as np
import pandas as pd
def load_data(filename):
    '''
    讀取數據
    '''
    data = pd.read_csv(filename, sep='\s+', header=None)
    row, col = data.shape[0], data.shape[1] # 獲取行數和列數;shape=(400,5)
    
    # np.c_:按【列】連接兩個矩陣,要求行數相等。np.r_:按【行】連接兩個矩陣,要求列數相等;
    # 由題設可知,前4行爲數據x;並要求添加偏差1,通過np.ones((col, 1))實現;
    X = np.c_[np.ones((row, 1)), data.iloc[:, 0:col-1]] 
    # 最後一行爲期望輸出y;
    y = data.iloc[:, col-1:col].values
   
    return X, y
X, y = load_data('hw1_15_train.dat')
row, col = X.shape[0], X.shape[1] # row=400,col=5;
theta = np.zeros((col, 1)) # 初始化權重w
# 爲保證每次都先從當前數據的後面數據中尋找錯誤項,加入prevpos變量;
#(這樣的方式相比每次均從第一個數據開始尋找要更快速)
def perceptron(X, y, theta, eta=1):
    '''
    感知器算法
    
    theta:權重;
    eta:更新步長;
    '''
    num = 0 # 保存循環次數
    prevpos = 0
    
    while(True):
        yhat = np.sign(X.dot(theta)) # 預測輸出值;
        yhat[np.where(yhat == 0)] = -1 # 按題目要求,如果預測輸出值爲0,強制置爲1;
        index = np.where(yhat != y)[0] # 找到預測輸出與期望輸出不相等的索引;
        
        # any()函數用於判斷給定的可迭代參數 iterable 是否全部爲 False,
        # 若全爲False,則返回 False;若存在 True,則返回 True。
        if not index.any(): # index.any()的作用是,如果還存在yhat != y的情況,則返回True;
            break
        if not index[index >= prevpos].any(): # 如果yhat != y的樣本索引超出數據個數,則從頭開始尋找
            prevpos = 0
        
        pos = index[index >= prevpos][0] # index[index >= prevpos] 返回從之前yhat != y的樣本索引開始之後的index列表
        prevpos = pos
        
        theta += eta*y[pos, 0] * X[pos:pos+1, :].T # 更新權重
        num += 1
        
    return theta, num
# Q15
theta, num = perceptron(X, y, theta)
print('權重更新次數: ', num)
# Q16
total = 0
for i in range(2000):
    theta = np.zeros((col, 1))
    randpos = np.random.permutation(row) # 對行數進行隨機排序
    Xrnd = X[randpos, :]
    yrnd = y[randpos, 0:1]
    _, num = perceptron(Xrnd, yrnd, theta)
    total += num
print('2000次平均每次更新theta的次數:',total/2000)
# Q17
total = 0
for i in range(2000):
    theta = np.zeros((col, 1))
    randpos = np.random.permutation(row)
    Xrnd = X[randpos, :]
    yrnd = y[randpos, 0:1]
    _, num = perceptron(Xrnd, yrnd, theta, 0.5) # 使用0.5的步長
    total += num
print('2000次平均每次更新theta的次數:',total/2000)

# Q18-20導入數據
X, y = load_data('hw1_18_train.dat')
testX, testy = load_data('hw1_18_test.dat')
row, col = X.shape[0], X.shape[1]
theta = np.zeros((col, 1))
# 在定義Pocket算法前,先引入錯誤率函數
def mistake(yhat, y):
    row, col = y.shape[0], y.shape[1]
    return np.sum(yhat != y)/row
# 通常採用每次隨機打亂數據,按打亂後的順序從前往後尋找“錯誤數據”的策略;
# 編程實現則直接找出所有錯誤,再從中隨機選一個。
def pocket(X, y, theta, iternum, eta = 1):
    '''
    Pocket算法
    '''
    yhat = np.sign(X.dot(theta))
    yhat[np.where(yhat == 0)] = -1
    
    errold = mistake(yhat, y) # 上一次運算的錯誤率
    thetabest = np.zeros(theta.shape) # 保存最佳權重
    
    for t in range(iternum):
        index = np.where(yhat != y)[0]
        if not index.any():
            break
            
        pos = index[np.random.permutation(len(index))[0]] # 隨機打亂數據之後,隨機取一個作爲錯誤更新的起點
        theta += eta * y[pos, 0] * X[pos:pos + 1, :].T # 更新權重
        
        yhat = np.sign(X.dot(theta))
        yhat[np.where(yhat == 0)] = -1
        errnow = mistake(yhat, y) # 本次循環的錯誤率
        
        if errnow < errold:
            thetabest = theta.copy() # 切記如果直接thetabest=theta,則會使兩者指向同一空間;
            errold = errnow
            
    return thetabest, theta
# Q18
total = 0
for i in range(2000):
    theta = np.zeros((col, 1))
    randpos = np.random.permutation(row)
    Xrnd = X[randpos, :]
    yrnd = y[randpos, 0:1]
    theta, thetabad = pocket(Xrnd, yrnd, theta, 50)
    
    yhat = np.sign(testX.dot(theta))
    yhat[np.where(yhat == 0)] = -1
    err = mistake(yhat, testy)
    total += err
    
print('迭代次數爲50時,theta_pocket情況下的測試集錯誤率:',total/2000)
# Q19
total = 0
for i in range(2000):
    theta = np.zeros((col, 1))
    randpos = np.random.permutation(row)
    Xrnd = X[randpos, :]
    yrnd = y[randpos, 0:1]
    theta, thetabad = pocket(Xrnd, yrnd, theta, 50)
    
    yhat = np.sign(testX.dot(thetabad))
    yhat[np.where(yhat == 0)] = -1
    err = mistake(yhat, testy)
    total += err
    
print('迭代次數爲50時,theta_50情況下的測試集錯誤率:',total/2000)
# Q20
total = 0
for i in range(2000):
    theta = np.zeros((col, 1))
    randpos = np.random.permutation(row)
    Xrnd = X[randpos, :]
    yrnd = y[randpos, 0:1]
    theta, thetabad = pocket(Xrnd, yrnd, theta, 100)
    
    yhat = np.sign(testX.dot(theta))
    yhat[np.where(yhat == 0)] = -1
    err = mistake(yhat, testy)
    total += err
    
print('迭代次數爲100時,theta_pocket情況下的測試集錯誤率:',total/2000)

強化學習:https://en.wikipedia.org/wiki/Reinforcement_learning
批處理和在線學習:https://www.c-sharpcorner.com/article/batch-and-online-machine-learning/
主動學習:https://en.wikipedia.org/wiki/Active_learning_(machine_learning)
霍夫丁不等式:https://en.wikipedia.org/wiki/Hoeffding%27s_inequality
霍夫丁不等式與真實的機器學習:https://www.cnblogs.com/nolonely/p/6155145.html
機器學習基石作業:https://github.com/AceCoooool/MLF-MLT
機器學習基石作業1:https://www.cnblogs.com/wanderingzj/p/4950578.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章