吳恩達機器學習課程-作業3-多分類和神經網絡(python實現)

Machine Learning(Andrew) ex3-Multi-class Classification and Neural Networks

椰汁筆記

Multi-class Classification

  • 1.1 Dataset

這裏的數據存儲變成了.mat的格式,這樣的數據選擇藉助scipy.io.loadmat()實現

import scipy.io as sio
data = sio.loadmat("ex3data1.mat")
print(data.keys())#查看其中包含的key-value鍵值對
X = data['X']#通過字典的訪問方式得到數據
y = data['y']

其中X和y都是numpy數組,X(5000,400),y(5000,1),其中X存儲的是5000張20x20pixel的圖片,y是圖片對應的數字

  • 1.2 Visualizing the data

可視化其中一部分圖片,圖片是按照代表的數字大小順序存儲的,爲了更好的呈現數據集,重其中隨機選擇若干張圖片,並拼接成一個大的圖片顯示(這裏是我想多了,可以直接通過matplotlib.pyplotsubplot實現,既然做了就這樣吧)

def randomly_select(images, numbers):
    """
    從images中選擇numbers張圖片
    
    parameters:
    ----------
    images : ndarray
            多幹張圖片
    numbers : int
            隨機選擇的圖片數量
    """
    m = images.shape[0]
    n = images.shape[1]
    flags = np.zeros((m,), bool)
    res = False
    for i in range(numbers):
        index = random.randint(0, m - 1)
        while flags[index]:
            index = random.randint(0, m)
        if type(res) == bool:
            res = images[index].reshape(1, n)
        else:
            res = np.concatenate((res, images[index].reshape(1, n)), axis=0)
    return res

def mapping(images, images_dimension):
    """
    將若干張圖片,組成一張圖片

    parameters:
    ----------
    images : ndarray
            多幹張圖片
    images_dimension : int
            新的正方形大圖片中一邊上有多少張圖片
    """
    image_dimension = int(np.sqrt(images.shape[-1]))
    image = False
    im = False
    for i in images:
        if type(image) == bool:
            image = i.reshape(image_dimension, image_dimension)
        else:
            if image.shape[-1] == image_dimension * images_dimension:
                if type(im) == bool:
                    im = image
                else:
                    im = np.concatenate((im, image), axis=0)
                image = i.reshape(image_dimension, image_dimension)
            else:
                image = np.concatenate((image, i.reshape(image_dimension, image_dimension)), axis=1)
    return np.concatenate((im, image), axis=0)

讓做好的圖片顯示出來,使用matplotlib.pyplot.imshow(),這裏需要注意直接顯示圖片會發現圖片是反的,我們通過轉置將圖片放正。這裏的顏色是默認的設置,可以自己改爲黑白等。

    im = mapping(randomly_select(X, 100), 10)
    plt.imshow(im.T)  # 圖片是鏡像的需要轉置讓它看起來更更正常
    plt.axis('off')
    plt.show()

在這裏插入圖片描述

  • 1.3 Vectorizing Logistic Regression

這一部分在上一次作業實現,這裏直接放代碼

def cost(theta, X, y, l):
    m = X.shape[0]
    part1 = np.mean((-y) * np.log(sigmoid(X.dot(theta))) - (1 - y) * np.log(1 - sigmoid(X.dot(theta))))
    part2 = (l / (2 * m)) * np.sum(theta * theta)
    return part1 + part2
def gradient(theta, X, y, l):
    m = X.shape[0]
    part1 = X.T.dot(sigmoid(X.dot(theta)) - y)
    part2 = (l / m) * theta
    part2[0] = 0
    return part1 + part2
  • 1.4 One-vs-all Classification

實現多分類的方法就是用多個二分類器組合,每個分類器置識別一個數字即可。也就是我們要對0-9這10個數字訓練10組分類器。
訓練對應與某一個數字的分類器,需要重新構造y,當前的y的存儲的是0-9,我們需要把它存儲成向量[0,0,…,1,0],對應下標處的內容爲1則表示是該數字。

def convert(y):
    """
    將y的每個值變化爲向量,來表示數字

    parameters:
    ----------
    y : ndarray
            表示圖片對應額數字
    """
    n = len(np.unique(y))
    res = False
    for i in y:
        temp = np.zeros((1, n))
        temp[0][i[0] % 10] = 1
        if type(res) == bool:
            res = temp
        else:
            res = np.concatenate((res, temp), axis=0)
    return res

這樣對應的y,每一列就代表了是否是某個數字。
對利用循環對每個數字進行分類器訓練,y[…, i]即爲是否爲第i個數字

    y = convert(y)
    X = np.insert(X, 0, 1, axis=1)
    m = X.shape[0]
    n = X.shape[1] - 1
    theta = np.zeros((n + 1,))
    trained_theta = False
    for i in range(y.shape[-1]):
        res = opt.minimize(fun=cost, x0=theta, args=(X, y[..., i], 1), method="TNC", jac=gradient)
        if type(trained_theta) == bool:
            trained_theta = res.x.reshape(1, n + 1)
        else:
            trained_theta = np.concatenate((trained_theta, res.x.reshape(1, n + 1)), axis=0)

這裏用循環沒有向量化的計算快,我有個想法就是直接將theta初始化爲(n+1,10),直接用到優化中實現向量化。這裏處理時需要注意,在使用高級優化方法中,theta只能傳入1維的數組,因此需要在內部還原。但是我使用後,出現了報錯linear seach failed。沒有搞懂爲什麼。
這裏的預測函數與之前的不同,需要計算10組,取最大值的就是對應的數字。

def predict(theta, X):
    p = sigmoid(X.dot(theta.T))
    res = False
    for i in p:
        index = np.argmax(i)
        temp = np.zeros((1, 10))
        temp[0][index] = 1
        if type(res) == bool:
            res = temp
        else:
            res = np.concatenate((res, temp), axis=0)
    return res

用這個預測方法到我們的評價中

    print(classification_report(y, predict(trained_theta, X), target_names=[str(i) for i in range(10)], digits=4))

可以看到,效果還是可以
在這裏插入圖片描述


Neural Networks

  • 2.1 Model representation

在這裏插入圖片描述
這個網絡模型除輸入層和輸出層外,只有一層隱藏層,輸入層爲400個單元,隱藏層爲25個單元,輸出層爲10個單元。我對隱藏層的作用的理解就是自動組合生成更好的特徵。
讀入訓練好的參數,特別維度

    data = sio.loadmat("ex3data1.mat")
    theta = sio.loadmat("ex3weights.mat")
    theta1 = theta["Theta1"]  # (25,401)
    theta2 = theta["Theta2"]  # (10,26)
  • 2.2 Feedforward Propagation and Prediction

前向傳播就是按照網絡順序計算出最後的結果,計算的具體方式,核心思想是上一層的每個節點相對於下一層的某個節點都有對應的權重,所有權重組合起來就是參數theta
ai(j)=g(θi0(j1)a0(j1)+θi1(j1)a1(j1)++θin(j1)an(j1)) a_i^{(j)} = g(\theta_{i0}^{(j-1)}a_0^{(j-1)}+\theta_{i1}^{(j-1)}a_1^{(j-1)}+\dots+\theta_{in}^{(j-1)}a_n^{(j-1)})
ai(j)上標表示所在j層,下標表示當前層第i個 a_i^{(j)} \textrm{上標表示所在j層,下標表示當前層第i個}
向量化的表示方法在最上面的模型圖上。
需要注意每層需要添加偏置單元,一般是在該層其他單元結果計算完後再加。

實現之前我們要理解theta是怎麼存儲的,第i行就是對應計算第i個單元的全部參數(參數個數等於前一層的單元數),因此在實現時需要將theta轉置。

實現前向傳播加最後的預測,其中theta接受元組或列表型參數,其中存儲每層對應的參數theta,最後求輸出層值最大的單元號作爲分類結果。

def predict(theta, X):
    labels = theta[-1].shape[0]
    for t in theta:
        X = np.insert(X, 0, 1, axis=1)
        X = sigmoid(X.dot(t.T))
    p = X
    res = np.zeros((1, labels))
    print(p)
    for i in p:
        index = np.argmax(i)
        temp = np.zeros((1, labels))
        temp[0][index] = 1
        res = np.concatenate((res, temp), axis=0)
    return res[1:]

使用之前讀入的已經訓練好的參數,直接預測
這裏需要注意,作業中訓練好的參數中使用的訓練集對y的處理是

數字 1 2 3 4 5 6 7 8 9 0
對應的向量下標 0 1 2 3 4 5 6 7 8 9

這與我自寫的convert方法不同我的處理方式是

數字 0 1 2 3 4 5 6 7 8 9
對應的向量下標 0 1 2 3 4 5 6 7 8 9

因此要轉換一下

    y = convert(data['y'])
    # 這裏用於訓練的訓練集對y的處理是
    # 1 2 3 ... 0
    # [0,0,0,...,0]
    # 而convert處理中時
    # 0 1 2 ... 9
    # [0,0,0,...,0]
    # 因此需要轉換
    y0 = y[..., 0].reshape(y.shape[0], 1)
    y = np.concatenate((y[..., 1:], y0), axis=1)
    X = data['X']  # (5000,400)
    print(classification_report(y, predict((theta1, theta2), X), digits=3))

可以看到擬合得比邏輯迴歸更好,我認爲就是自動訓練出來得特徵起得效果,這是隱藏層做的事,可以通過增加隱藏層來提高準確度。但是神經網絡計算量也是非常的大。
在這裏插入圖片描述


完整的代碼會同步在我的github

歡迎指正錯誤

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