iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 22
0

昨天把二元分類的數學原理說明了…最重要的是我們可以二元分類的損失函數如下,這個其實是後邊要講一般分類問題的損失函數的特例:

https://chart.googleapis.com/chart?cht=tx&chl=%5Cell%20(%7By%7D%2C%20%7B%5Chat%7By%7D%7D)%20%3D%20%20-%20%5Csum_%7Bi%3D1%7D%5En%20y_i%20%5Clog%20%5Chat%7By%7D_i%20%2B%20(1-y_i)%20%5Clog%20(1-%5Chat%7By%7D_i)

先取得訓練用的資料集,我們下載到硬碟裡,

#假設是 Fields 登入他的帳號來執行的 “~/” = ”/home/fields”
wget https://www.csie.ntu.edu.tw/%7Ecjlin/libsvmtools/datasets/binary/a1a -O ~/a1a.train
wget https://www.csie.ntu.edu.tw/%7Ecjlin/libsvmtools/datasets/binary/a1a.t -O ~/a1a.test

數據集來自 UCI (UC Irvine Machine Learning Repository),用來預測年收入是否超過五萬美金。 下列節錄資料集的三行,樣式為第一個值 +1 或 -1 是 Label,代表年收入超過五萬美金否『是』或『否』,我們必須把他變成1或是0,後面 N:M 都是特徵值,代表年齡, 種族, 性別等,不過我們只需要N 這個值(亦即 :M 要刪掉),N代表在 UCI 共有 123個特徵值中,非零值的位置。這被稱為 LIBSVM 格式。

-1 2:1 6:1 18:1 20:1 37:1 42:1 48:1 64:1 71:1 73:1 74:1 76:1 81:1 83:1
+1 5:1 11:1 15:1 32:1 39:1 40:1 52:1 63:1 67:1 73:1 74:1 76:1 78:1 83:1
-1 5:1 16:1 30:1 35:1 41:1 64:1 67:1 73:1 74:1 76:1 80:1 83:1

import mxnet as mx
from mxnet import nd, autograd, gluon
data_ctx = mx.cpu()
#我們假設有一塊Nvida 支援 CUDA顯示卡來加速計算,如果沒有則改為mx.cpu()
model_ctx = mx.gpu()

with open("/home/fields/a1a.train") as f:
    train_raw = f.read()

with open("/home/fields/a1a.test") as f:
    test_raw = f.read()

讀進資料後,我們要把資料做一點清理,這在機器學習是經常面臨的數據預處理,我們在此練習一下:

def process_data(raw_data):
    train_lines = raw_data.splitlines()
    num_examples = len(train_lines)
    num_features = 123   #UCI 資料集有123個特徵
    X = nd.zeros((num_examples, num_features), ctx=data_ctx) 
    #預設為0,我們要從資料集找到非零值,把他改為1
    Y = nd.zeros((num_examples, 1), ctx=data_ctx)

    for i, line in enumerate(train_lines):
        tokens = line.split()
        label = (int(tokens[0]) + 1) / 2  # 我們轉換資料集的 Label 『是』『否』 {-1,1} 成我們計算損失函數所需的 {0,1}
        Y[i] = label
        for token in tokens[1:]:
            index = int(token[:-2]) - 1  
            X[i, index] = 1 
    return X, Y

處理後的訓練與測試資料集
Xtrain, Ytrain = process_data(train_raw)
Xtest, Ytest = process_data(test_raw)

有了資料集,我們看看如果用 Gluon 實作:

batch_size = 64
#用 Gluon 提供的 Data Iterator 
train_data = gluon.data.DataLoader(gluon.data.ArrayDataset(Xtrain, Ytrain),
                                      batch_size=batch_size, shuffle=True)

test_data = gluon.data.DataLoader(gluon.data.ArrayDataset(Xtest, Ytest),
                                      batch_size=batch_size, shuffle=True)


def logistic(z):
    return 1. / (1. + nd.exp(-z))

def log_loss(output, y):
    yhat = logistic(output)
    return  - nd.nansum(  y * nd.log(yhat) + (1-y) * nd.log(1-yhat))

net = gluon.nn.Dense(1)
net.collect_params().initialize(mx.init.Normal(sigma=1.), ctx=model_ctx)
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.01})

epochs = 30
loss_sequence = []
num_examples = len(Xtrain)

for e in range(epochs):
    cumulative_loss = 0
    for i, (data, label) in enumerate(train_data):
        data = data.as_in_context(model_ctx) #如果用 GPU加速,需要把資料引入 GPU
        label = label.as_in_context(model_ctx)
        with autograd.record():
            output = net(data)
            loss = log_loss(output, label)
        loss.backward()
        trainer.step(batch_size)
        cumulative_loss += nd.sum(loss).asscalar()
    print("Epoch %s, loss: %s" % (e, cumulative_loss ))
    loss_sequence.append(cumulative_loss)

這裡的程式碼如果全部都採用 Gluon,會更簡潔,但我們局部從無到有,逐步了解如果我們自己發明更好的損失函數,我們可以就自己改寫上面的 loss = log_loss(output, label)


一般分類 Softmax Regression:

二元分類在許多應用上很有用處,例如:分辨是否為垃圾信件;某疾病判定陽性或陰性。但是我們也常遇到多類別的分類,例如:辨識數字,產品分類,電影分級等等。

我們假設要進行 k 類別(以下圖示意,有三個輸出單元,那 k = 3)的分類 ,假設特徵值有 4 個,以下圖表達:

我們沿用線性迴歸輸出為
https://chart.googleapis.com/chart?cht=tx&chl=o_1%20%3D%20x_1%20w_%7B11%7D%20%2B%20x_2%20w_%7B21%7D%20%2B%20x_3%20w_%7B31%7D%20%2B%20x_4%20w_%7B41%7D%20%2B%20b_1%2C%5C%5C%20o_2%20%3D%20x_1%20w_%7B12%7D%20%2B%20x_2%20w_%7B22%7D%20%2B%20x_3%20w_%7B32%7D%20%2B%20x_4%20w_%7B42%7D%20%2B%20b_2%2C%5C%5C%20o_3%20%3D%20x_1%20w_%7B13%7D%20%2B%20x_2%20w_%7B23%7D%20%2B%20x_3%20w_%7B33%7D%20%2B%20x_4%20w_%7B43%7D%20%2B%20b_3.

但是現在是分類問題,那我們要做的是再加一個轉換f,當 x 這樣本是第i個分類時,我們希望其機率最大

https://chart.googleapis.com/chart?cht=tx&chl=%24%24%5Cmax_i%20%7Bf_i(%7Bx%7D)%7D%24%24

我們巧妙的這樣設計:讓輸出值成 1 到 k 這幾個值是呈現離散分佈的,符合

  1. 每個 值都不小於0
  2. 1 到 k 這幾個輸出值 加總等於 1

所以我們嘗試用這個softmax函數來當作這個離散分佈的表達,的確符合上述兩個條件:

https://chart.googleapis.com/chart?cht=tx&chl=%24%24%5Ctext%7Bsoftmax%7D(%7Bz%7D)%20%3D%20%5Cfrac%7Be%5E%7B%7Bz%7D%7D%20%7D%7B%5Csum_%7Bi%3D1%7D%5Ek%20e%5E%7Bz_i%7D%7D%24%24

那我們就可以改寫輸出為

https://chart.googleapis.com/chart?cht=tx&chl=%5Chat%7By%7D_1%2C%20%5Chat%7By%7D_2%2C%20%5Chat%7By%7D_3%20%3D%20%5Ctext%7Bsoftmax%7D(o_1%2C%20o_2%2C%20o_3)

其中類別 j 是:

https://chart.googleapis.com/chart?cht=tx&chl=%5Chat%7By%7D_j%20%3D%20%5Cfrac%7B%20%5Cexp(o_j)%7D%7B%5Csum_%7Bi%3D1%7D%5E3%20%5Cexp(o_i)%7D

mini Batch訓練,一個 Batch 多個樣本的表示:

上述都是一個樣本來說明,當我們採 mini Batch 我們抽樣 s 個樣本,而輸入有f個特徵,我們要預測k種可能的分類時,我們就必須泛化改寫維度來表達。

  • https://chart.googleapis.com/chart?cht=tx&chl=%7BX%7D%20%5Cin%20%5Cmathbb%7BR%7D%5E%7Bs%20%5Ctimes%20f%7D : 表示輸入為s個樣本,每個樣本有f個特徵
  • https://chart.googleapis.com/chart?cht=tx&chl=%7BW%7D%20%5Cin%20%5Cmathbb%7BR%7D%5E%7Bf%20%5Ctimes%20k%7D :參數
  • https://chart.googleapis.com/chart?cht=tx&chl=%7Bb%7D%20%5Cin%20%5Cmathbb%7BR%7D%5E%7B1%20%5Ctimes%20k%7D :偏差 Bias

這樣我們就可以用較簡潔的方式來表達,注意 偏差 https://chart.googleapis.com/chart?cht=tx&chl=%7Bb%7D%20%5Cin%20%5Cmathbb%7BR%7D%5E%7B1%20%5Ctimes%20k%7D 會自動廣播成 https://chart.googleapis.com/chart?cht=tx&chl=%5Cmathbb%7BR%7D%5E%7Bs%20%5Ctimes%20k%7D,這樣才能進行下列的矩陣加法

https://chart.googleapis.com/chart?cht=tx&chl=%7BO%7D%20%3D%20%7BX%7D%20%7BW%7D%20%2B%20%7Bb%7D%2C
https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Chat%7BY%7D%7D%20%3D%20%5Ctext%7Bsoftmax%7D(%7BO%7D)

Cross Entropy 交叉熵與損失函數

要計算兩個分佈有多大的距離,有 Cross Entropy 函數。
https://chart.googleapis.com/chart?cht=tx&chl=H%5Cleft(%5C%20y%5E%7B(i)%7D%2C%20%20%7B%5Chat%20y%7D%5E%7B(i)%7D%5Cright%20)%20%3D%20-%5Csum_%7Bj%3D1%7D%5Eq%20y_j%5E%7B(i)%7D%20%5Clog%20%5Chat%20y_j%5E%7B(i)%7D (公式一)

這個函數解決在k個分類中,只有一個正確的分類值是1,其他為零這種情境下,我們要量測其誤差的方法。

定義 Cross Entropy Loss Function:

https://chart.googleapis.com/chart?cht=tx&chl=%5Cell(%7B%5CTheta%7D)%20%3D%20%5Cfrac%7B1%7D%7Bn%7D%20%5Csum_%7Bi%3D1%7D%5En%20H%5Cleft(%20y%5E%7B(i)%7D%2C%20%20%7B%5Chat%20y%7D%5E%7B(i)%7D%5Cright%20) (公式二)

觀察上面(公式一)式子,有加註j 下標的符號代表 https://chart.googleapis.com/chart?cht=tx&chl=%24y_j%5E%7B(i)%7D%24 向量 https://chart.googleapis.com/chart?cht=tx&chl=y%5E%7B(i)%7D 裡的第j個元素,只有0或1的可能。我們規定每個樣本只能有一個分類,亦即向量 https://chart.googleapis.com/chart?cht=tx&chl=y%5E%7B(i)%7D 中只有第 https://chart.googleapis.com/chart?cht=tx&chl=y%5E%7B(i)%7D_%7By%5E%7B(i)%7D%7D 個元素為1;其他都是0。結合(公式一)變成:

https://chart.googleapis.com/chart?cht=tx&chl=H(%20y%5E%7B(i)%7D%2C%20%20%7B%5Chat%20y%7D%5E%7B(i)%7D)%20%3D%20-%5Clog%20%5Chat%20y_%7By%5E%7B(i)%7D%7D%5E%7B(i)%7D (公式三)

結合(公式三)與 (公式二)那損失函數的公式就變成:

https://chart.googleapis.com/chart?cht=tx&chl=%5Cell(%5CTheta%7D)%20%3D%20-(1%2Fn)%20%20%5Csum_%7Bi%3D1%7D%5En%20%5Clog%20%5Chat%20y_%7By%5E%7B(i)%7D%7D%5E%7B(i)%7D

評估模型的優劣方法

再進一步我們可以把最小化損失函數取 exp 運算後變成 最大化

https://chart.googleapis.com/chart?cht=tx&chl=%5Cexp(-n%5Cell(%7B%5CTheta%7D))%3D%5Cprod_%7Bi%3D1%7D%5En%20%5Chat%20y_%7By%5E%7B(i)%7D%7D%5E%7B(i)%7D

這代表聯合機率。有這些後我們就可以往下進行用準確率做模型的評估了。

備註:

專案緣起記錄在 【UP, Scrum 與 AI專案】


上一篇
Data Iteration;二元分類
下一篇
深度學習的 Hello World - 多層感知機 MLP
系列文
深度學習所需入門知識--一位初學者的認知31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言