用 TensorFlow2.0 實(shí)現(xiàn) Softmax 多分類

我們知道線性回歸一般都用來解決回歸類問題秽浇,例如房價預(yù)測酷麦,氣溫預(yù)測等。實(shí)際上否淤,加上 Softmax 這樣的技術(shù)悄但,我們還可以使用線性回歸來解決多分類問題。Softmax 是對網(wǎng)絡(luò)結(jié)構(gòu)中輸出層的改造石抡,其示意圖如下:

Softmax 技術(shù)細(xì)節(jié)

上圖中算墨,x1、x2 和 x3 是輸入層汁雷,它們分別通過兩個線性回歸模型來產(chǎn)生 y1 和 y2:
\begin{aligned} y_1&=w_{11}x_1 + w_{12}x_2 + w_{13}x_3 + b_1\\ y_2&=w_{21}x_1 + w_{22}x_2 + w_{23}x_3 + b_2 \end{aligned}
接著,把 y1 和 y2 輸入到 Softmax 模塊中报咳,輸出 y1' 和 y2'侠讯,經(jīng)過 Softmax 處理后的結(jié)果,是一個離散概率分布暑刃,即 y1' + y2' = 1厢漩。于是,我們可以用 y1' 和 y2' 來表示不同分類的預(yù)測概率岩臣。

Softmax 具體細(xì)節(jié)如下圖所示:

  1. 首先把輸出結(jié)果做 e 的次方處理溜嗜,得到 z_1= e^{y_1}宵膨,z_2=e^{y_2}
  2. 將第 1 步的結(jié)果相加:sum = z1 + z2
  3. 最后,將第 1 步的結(jié)果除以 sum炸宵,得到 y_1' = z_1 / sum辟躏,y_2'=z_2/sum

因?yàn)閷Y(jié)果做了 e 的次方的處理,所以 Softmax 會強(qiáng)化較大的數(shù)——致使較大的結(jié)果的概率更大土全,這也是 Softmax 為什么要叫 Softmax捎琐,而不叫 max 的原因。

Softmax 分類的損失函數(shù)

Softmax 分類采用交叉熵(Cross Entropy)損失函數(shù)裹匙,仔細(xì)觀察瑞凑,交叉熵?fù)p失函數(shù)其實(shí)就是 Log Likelihood(見深入理解邏輯回歸一文),它們的目標(biāo)都是為了讓正確分類的預(yù)測值最大化概页。

上圖中籽御,y1',y2' 和 y3' 為不同分類的預(yù)測概率惰匙,它是 Softmax 的輸出結(jié)果技掏;y1,y2 和 y3 是真實(shí)分類數(shù)據(jù)徽曲,在分類任務(wù)中零截,這三個數(shù)只有一個是 1,另外兩個都是 0秃臣,所以 y1涧衙,y2 和 y3 也是一個概率分布。

那么我們的機(jī)器學(xué)習(xí)任務(wù)就變成了:讓預(yù)測概率分布 y' 不斷接近真實(shí)概率分布 y 的優(yōu)化問題奥此。我們用交叉熵(cross entropy)來衡量兩個概率分布的差異弧哎,交叉熵越小,這兩個分布越接近稚虎,交叉熵的表示如下:
H(y,y') = \sum_{j=1}^{q} -y^{(j)} \log({y'}^{(j)})
上式中撤嫩,q 表示有 q 個分類,y^{(j)} 為第 j 個分類的概率蠢终。把所有樣本的交叉熵加起來序攘,就是我們要的損失函數(shù):
\begin{aligned} Loss &= \sum_{i=1}^n H(y_i,y_i')\\ &=\sum_{i=1}^{n}-y_i\log(y_i') \end{aligned}

剛才說了,交叉熵又等效于 Log Likelihood寻拂,你可以從 Log Likelihood 的角度來理解它程奠。除此之外,你還可以從信息論的角度來理解:

在信息論中祭钉,熵 H=\sum_{j=1}^q -y^{(j)}\log(y^{(j)}) 用來描述系統(tǒng)中所蘊(yùn)含的信息量的期望瞄沙,其中 -\log(y^{(j)}) 是自信息,表示概率為 y^{(j)} 的事件發(fā)生時所產(chǎn)生的信息量,自信息可以理解為距境,小概率事件會產(chǎn)生較大的信息量申尼,相反,大概率事件產(chǎn)生的信息量較小垫桂。

而交叉熵 H(y_q,{y_q}') 描述的是:要消除系統(tǒng)中的信息醋旦,需要付出的努力植酥,且當(dāng)分布 {y_q}'y_q 相等時恕齐,所付出的努力最小——H(y_q,{y_q}') 達(dá)到最小值踩官,機(jī)器學(xué)習(xí)使用交叉熵作為損失函數(shù)也正是因?yàn)檫@一點(diǎn)。

更詳盡的解釋可以參考這篇文章碱呼。

從零實(shí)現(xiàn) Softmax

現(xiàn)在我們用 TF2.0 來從零實(shí)現(xiàn) Softmax蒙挑,步驟如下:

  1. 定義 Softmax 多分類模型
  2. 定義損失函數(shù)
  3. 訓(xùn)練,評估模型

定義 Softmax 多分類模型

線性回歸的 Softmax 模型主要做兩件事情:

一愚臀、將輸入數(shù)據(jù)矩陣與參數(shù)矩陣做矩陣乘法忆蚀,輸入數(shù)據(jù)的行數(shù)為樣本數(shù) n,每行中的內(nèi)容為每條樣本的特征姑裂,設(shè)特征數(shù)為 d馋袜,則輸入數(shù)據(jù)的形狀為 n*d,一次訓(xùn)練多條數(shù)據(jù)舶斧,以此達(dá)到批量計算的目的欣鳖;參數(shù)矩陣的維度為 d*q,d 依然為特征數(shù)茴厉,q 表示分類數(shù)泽台,這樣乘出來的結(jié)果為 n*q,意為這 n 條樣本的每條樣本在不同分類上的輸出矾缓;

二怀酷、將第一步的結(jié)果做 Softmax 處理,得到每條樣本的在不同分類上的預(yù)測結(jié)果嗜闻。

def net(X):
    '''
    一層線性回歸網(wǎng)絡(luò)蜕依,輸出 softmax 后的多分類結(jié)果
    Args:
    - X: n 條樣本,每條樣本有 d 個維度琉雳,即 n*d 維矩陣
    - W: 全局參數(shù)样眠,d*q 維矩陣,q 表示分類數(shù)
    - b: bias翠肘,1*q 維向量
    Return:
    - softmax 后的多分類結(jié)果
    '''
    return softmax(tf.matmul(X, W) + b)
   
def softmax(y):
    '''
    對 n 個樣本吹缔,每個樣本有 q 種分類的數(shù)據(jù)做softmax 
    Args:
    - y: n*q 維的矩陣
    Return:
    - n*q 維的 softmax 后的矩陣
    Example:
    >>> y = np.array([[0.1,0.2,0.8],[0.8,0.2,0.1]])
    >>> softmax(y)
    <tf.Tensor: shape=(2, 3), dtype=float64, numpy=
    array([[0.24278187, 0.26831547, 0.48890266],
       [0.48890266, 0.26831547, 0.24278187]])>
    '''
    return tf.exp(y) / tf.reduce_sum(tf.exp(y), axis=1, keepdims=True)

定義損失函數(shù)

觀察交叉熵?fù)p失函數(shù)的公式,要實(shí)現(xiàn)它锯茄,就要先拿到正確分類對應(yīng)的預(yù)測概率:

這里先用 one-hot 編碼將目標(biāo)向量轉(zhuǎn)化為和預(yù)測結(jié)果一樣的矩陣形式,如預(yù)測結(jié)果為 n*q 的矩陣(n 表示一次預(yù)測 n 條樣本,q 表示分類數(shù))肌幽,那么 one-hot 編碼會將目標(biāo)向量也轉(zhuǎn)化為 n*q 的矩陣晚碾;

接著再對預(yù)測矩陣和目標(biāo)矩陣做一個“與操作” boolean_mask 就可以把正確分類對應(yīng)的預(yù)測值取出來了;

最后對預(yù)測值求 -log 喂急,再求和格嘁,就是這批樣本的 Loss,代碼如下:

def cross_entropy(y, y_hat):
    '''
    交叉熵?fù)p失函數(shù)
    Args:
    - y: n 條樣本的目標(biāo)值廊移,n*1 向量
    - y_hat: n 條樣本的預(yù)測分布(softmax輸出結(jié)果), n*q 矩陣
    Return:
    n 個樣本的 -log(y_hat) 的和
    Examples:
    >>> y = np.array([[2],[1]])
    >>> y_hat = np.array([[0.1,0.2,0.2],[0.3,0.9,0.2]])
    >>> cross_entropy(y, y_hat)
    <tf.Tensor: shape=(), dtype=float64, numpy=1.7147984280919266>
    '''
    y_obs = tf.one_hot(y, depth=y_hat.shape[-1])
    y_obs = tf.reshape(y_obs, y_hat.shape)
    y_hat = tf.boolean_mask(y_hat, y_obs)
    return tf.reduce_sum(-tf.math.log(y_hat))

評估模型

這次我們使用準(zhǔn)確率(accuracy)來評估模型的效果糕簿,準(zhǔn)確率意為預(yù)測正確數(shù)的占比。

評估模型時狡孔,要先對數(shù)據(jù)進(jìn)行預(yù)測懂诗,再拿預(yù)測結(jié)果(概率值最大的分類)和正確分類做對比,以此統(tǒng)計正確的預(yù)測次數(shù)苗膝。這里使用的 tf.argmax 函數(shù)殃恒,表示在多個預(yù)測分類中,取最大的那個值作為預(yù)測結(jié)果:

def accuracy(x, y, num_inputs, batch_size):
    '''
    求數(shù)據(jù)集的準(zhǔn)確率
    Args:
    - x: 數(shù)據(jù)集的特征
    - y: 數(shù)據(jù)集的目標(biāo)值辱揭,n*1 維矩陣
    - num_inputs: 特征維度(輸入層個數(shù))
    - batch_size: 每次預(yù)測的批次
    '''
    test_iter = tf.data.Dataset.from_tensor_slices((x, y)).batch(batch_size)
    acc, n = 0, 0
    for X, y in test_iter:
        X = tf.reshape(X, (-1, num_inputs))
        y = tf.cast(y, dtype=tf.int64)
        acc += np.sum(tf.argmax(net(X), axis=1) == y)
        n += y.shape[0]
    return acc/n

訓(xùn)練

以上為模型訓(xùn)練离唐、預(yù)測和評估所需要的通用方法,接下來我們就可以來訓(xùn)練模型了问窃,本次我們使用的是 fashion_mnist 數(shù)據(jù)集亥鬓,其中每個樣本為一張 28*28 像素的圖片,目標(biāo)值 label 為這張圖片所屬的類別編號域庇,數(shù)據(jù)集中一共有 10 個類別嵌戈,如下所示:

我們的任務(wù)是,讀到一張這樣的圖片较剃,能預(yù)測其所屬的分類咕别。模型的輸入可以是圖片的每個像素值,因?yàn)橛?28*28=784 個像素写穴,每個像素的取值范圍為 0-255惰拱,那么我們模型的輸入層的節(jié)點(diǎn)個數(shù)便是 784;因?yàn)閿?shù)據(jù)集總共只有 10 個分類啊送,則模型的參數(shù) W 的形狀為 784*10 維矩陣偿短,bias 的形狀為 10*1,同時輸出層的節(jié)點(diǎn)數(shù)也為 10 個馋没。

把參數(shù)弄清楚后昔逗,剩下的就是模型迭代了,這一部分和上一篇線性回歸中的訓(xùn)練代碼大致一樣篷朵,代碼如下:

import tensorflow as tf
from tensorflow import keras
import numpy as np
import time
import sys
from tensorflow.keras.datasets import fashion_mnist

def train(W, b, lr, num_inputs):
    (x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()
    # 數(shù)據(jù)歸一化
    x_train = tf.cast(x_train, tf.float32) / 255
    x_test = tf.cast(x_test, tf.float32) / 255
    
    batch_size = 256
    num_epochs = 5
    for i in range(num_epochs):
        # 小批量迭代
        train_iter = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(batch_size)
        train_acc_sum, loss_sum, n = 0, 0, 0
        for X, y in train_iter:
            X = tf.reshape(X, (-1, num_inputs))
            y = tf.reshape(y, (-1, 1))
            # 計算loss和梯度
            with tf.GradientTape() as tape:
                l = cross_entropy(y, net(X))
            grads = tape.gradient(l, [W, b])
            # 根據(jù)梯度調(diào)整參數(shù)
            W.assign_sub(lr * grads[0])
            b.assign_sub(lr * grads[1])

            loss_sum += l.numpy() # 累加loss
            n += y.shape[0] #累加訓(xùn)練樣本個數(shù)

        print("epoch %s, loss %s, train accuracy %s, test accuracy %s" 
              % (i+1, loss_sum/n, 
                 accuracy(x_train, y_train, num_inputs, batch_size), 
                 accuracy(x_test, y_test, num_inputs, batch_size)))

num_inputs = 784
num_outputs = 10
lr = 0.001
# 初始化模型參數(shù)
W = tf.Variable(tf.random.normal(shape=(num_inputs, num_outputs), 
                                 mean=0, stddev=0.01, dtype=tf.float32))
b = tf.Variable(tf.zeros(num_outputs, dtype=tf.float32))
train(W, b, lr, num_inputs)

下面是該訓(xùn)練的輸出勾怒,可見使用線性回歸這樣簡單的模型婆排,也可以把 fashion_mnist 圖片分類任務(wù)做到 0.85 的準(zhǔn)確率。

epoch 1, loss 0.8956155544281006, train accuracy 0.82518, test accuracy 0.8144
epoch 2, loss 0.6048591234842936, train accuracy 0.83978, test accuracy 0.8272
epoch 3, loss 0.5516327695210774, train accuracy 0.84506, test accuracy 0.8306
epoch 4, loss 0.5295544961929322, train accuracy 0.84906, test accuracy 0.8343
epoch 5, loss 0.5141636388142904, train accuracy 0.85125, test accuracy 0.8348

簡單的實(shí)現(xiàn)

照例笔链,我們還要看下 Softmax 的簡單實(shí)現(xiàn)版本:

# 加載數(shù)據(jù)集
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()
x_train = x_train / 255
x_test = x_test / 255

# 配置模型
model = keras.Sequential([
    keras.layers.Flatten(input_shape=(28, 28)),  # 輸入層
    keras.layers.Dense(10, activation=tf.nn.softmax)  # 輸出層段只,激活函數(shù)使用的是 softmax
])
# 配置交叉熵?fù)p失函數(shù)
loss = 'sparse_categorical_crossentropy'  
# 配置 SGD,學(xué)習(xí)率為 0.1
optimizer = tf.keras.optimizers.SGD(0.1)
model.compile(optimizer=optimizer,
             loss = loss,
             metrics=['accuracy'])  # 使用準(zhǔn)確率來評估模型

model.fit(x_train, y_train, epochs=5, batch_size=256)

依然是只需要配置鉴扫,不用你寫一行邏輯代碼赞枕。

小結(jié)

本文我們一起學(xué)習(xí)了使用線性回歸和 Softmax 來實(shí)現(xiàn)一個多分類模型,并實(shí)際的使用 fashion_mnist 數(shù)據(jù)集做了實(shí)驗(yàn)坪创,得到了 0.85 一個還不賴的準(zhǔn)確率結(jié)果炕婶,在本文中,有兩點(diǎn)細(xì)節(jié)需要掌握

  1. Softmax 實(shí)現(xiàn)細(xì)節(jié)
  2. 交叉熵?fù)p失函數(shù)的原理

參考:

相關(guān)文章:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末莱预,一起剝皮案震驚了整個濱河市柠掂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锁施,老刑警劉巖陪踩,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異悉抵,居然都是意外死亡肩狂,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門姥饰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來傻谁,“玉大人,你說我怎么就攤上這事列粪∩蟠牛” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵岂座,是天一觀的道長态蒂。 經(jīng)常有香客問我,道長费什,這世上最難降的妖魔是什么钾恢? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮鸳址,結(jié)果婚禮上瘩蚪,老公的妹妹穿的比我還像新娘。我一直安慰自己稿黍,他們只是感情好疹瘦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著巡球,像睡著了一般言沐。 火紅的嫁衣襯著肌膚如雪邓嘹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天险胰,我揣著相機(jī)與錄音吴超,去河邊找鬼。 笑死鸯乃,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的跋涣。 我是一名探鬼主播缨睡,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼陈辱!你這毒婦竟也來了奖年?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤沛贪,失蹤者是張志新(化名)和其女友劉穎陋守,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體利赋,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡水评,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了媚送。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片中燥。...
    茶點(diǎn)故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖塘偎,靈堂內(nèi)的尸體忽然破棺而出疗涉,到底是詐尸還是另有隱情,我是刑警寧澤吟秩,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布咱扣,位于F島的核電站,受9級特大地震影響涵防,放射性物質(zhì)發(fā)生泄漏闹伪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一武学、第九天 我趴在偏房一處隱蔽的房頂上張望祭往。 院中可真熱鬧,春花似錦火窒、人聲如沸硼补。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽已骇。三九已至离钝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間褪储,已是汗流浹背卵渴。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鲤竹,地道東北人浪读。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像辛藻,于是被迫代替她去往敵國和親碘橘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容