Softmax Regression識別手寫數字

Softmax Regression識別手寫數字

來自專欄對象檢測1 人贊了文章

一、問題描述

MNIST簡介:MNIST(Mixed National Institute of Standards and Technology database)是一個非常簡單的機器視覺數據集,如圖一所示。MNIST數據集是一個手寫體數據集,數據集中每一個樣本都是0-9中的某一個數字。該數據集由四部分組成,訓練圖片集,訓練標籤集,測試圖片集和測試標籤集。其中,訓練集中有60000個樣本,測試集中有10000個樣本。每張照片均為28*28的灰度圖像。為方便存儲,官方已對圖片集進行處理,將每一張圖片變成了維度為(1,784)的向量。

任務:對這些手寫數字的圖片進行分類,轉成0~9共10類。

圖一:MNIST數據集

二、設計演算法:Softmax Regression

Softmax Regression原理:將可以判定為某類的特徵相加(這裡的特徵相加指的是對所有像素做加權和,相當於 Z_{1*10} = X_{1*784} ast W_{784*10} + b_{1*10} ),然後將這些特徵轉化為判定為是這一類的概率。

  • 將可以判定為某類的特徵相加:

Z_{1*10} = X_{1*784} ast W_{784*10} + b_{1*10}

前面我們提到MNIST數據集是28*28的灰度圖像,如圖二所示。這裡的 X_{1*784} 是將一張圖片展開成一維向量,需要注意的是將圖片展開成一維向量時,順序並不重要,只要每一張圖片都是用同樣的順序進行展開的就可以。其中的W,b是模型根據數據自動學習、訓練出來的。輸出的 Z_{1*10} 矩陣,代表對當前輸入圖片的預測結果,每一列表示這個灰度圖像屬於0~9某一類的特徵。為方便表述,下面將 Z_{1*10} 記為 z_{i} ,其中 iin[0,9], 其中z_{1} 表示這個灰度圖像屬於數字1這一類的特徵,其他記號的含義以此類推

圖二:手寫數字灰度圖像

  • 將這些特徵轉化為判定為是這一類的概率:

y_{i} = frac{e^{z_{i}}}{sum_{j}^{}{e^{z_{j}}}}

上式是softmax函數,更形象的表示見圖三。softmax函數可以將任意輸入映射成[0,1]之間的輸出,且結果之和為1,常被用於多分類(如這裡的0-9十分類任務)。通過softmax 函數將得到的 特徵z_{i} 轉換為判定是第i類的概率 y_{i} ,其中 iin[0,9] ,最後取概率最大的 y_{i} 作為模型的輸出結果。

圖三:softmax函數圖解

三、損失函數

為了訓練模型,我們需要定義一個loss function來描述模型對問題的分類精度。loss越小,代表模型的分類結果與真實值的偏差越小。在多分類問題中常使用交差熵作為損失函數,交叉熵定義如下,其中y代表真實值, 	ilde{y} 代表預測值,n代表需要區分的類別數,如這裡就是n=10。

H_{y}(	ilde{y}) =- sum_{i}^{n}{y}log	ilde{y}

那麼,為什麼交叉熵可以用來判斷模型對真實概率分布 估計的準確程度呢?首先補充幾個知識點

  1. 信息量

直覺上來說:一件事情發生的可能性越小(概率小),當這件事發生時,人們所獲得信息量越大;相反,一件事極有可能發生(概率大),當這件事發生時,人們會感覺沒有什麼信息量,因為早知道會發生。因此信息量跟事件發生的概率有關。定義如下,其中 p(x_{o}) 為事件 x_{o} 發生的概率,log為自然對數。

I(x_{0}) = -log(p(x_{o}))

由於是概率所以 p(x_{0}) 的取值範圍是[0,1],繪製為圖四,可見該函數符合我們對信息量的直覺

圖四:信息量

2.熵

對於某個事件,有n種可能性,每種可能性有個概率值。這裡假設你拿出了自己的電腦,會出現n=3種可能,每種可能性都給出了其發生的概率值,同時計算了信息量。

熵用來表示所有信息量的期望,其中n表示事件的n種可能性

H(X)=?sum_{i}^{n}{}p(x_{i})log(p(x_{i}))

所以上面問題的結果就是:

H(X)=?[p(A)log(p(A))+p(B)log(p(B))+p(C))log(p(C))] =0.7×0.36+0.2×1.61+0.1×2.30=0.804

3.相對熵(KL散度)

對同一個問題,用P、Q同時描述,用KL散度來衡量兩個分布的差異。

D_{KL}(p||q)=sum_{i}^{n}{}p(x_{i})log(frac{p(x_{i})}{q(x_{i})})

在機器學習中,P往往用來表示真實的樣本分布,比如[1,0,0]表示當前樣本屬於第一類。Q用來表示模型所預測的分布,比如[0.7,0.2,0.1]。當然Q越接近P越好,這樣就說明預測值十分逼近真實值。也就是上式的 D_{KL}的值越小越好。

4.交差熵

D_{KL}(p||q)公式進行變形:

D_{KL}(p||q)=sum_{i}^{n}{p(x_{i})log(p(x_{i}))} - sum_{i}^{n}{p(x_{i})log(q(x_{i}))}

= -H(p(x)) + [-sum_{i}^{n}{p(x_{i})log(q(x_{i}))}]

等式的前一部分恰巧就是p的熵,等式的後一部分,就是交叉熵:

H(p,q) = - sum_{i}^{n}{p(x_{i})log(q(x_{i}))}

在機器學習中,我們需要評估label和predicts之間的差距,使用KL散度剛剛好,即 D_{KL}(y||	ilde{y}) ,由於KL散度中的前一部分?H(y)不變,故在優化過程中,只需要關注交叉熵就可以了。所以一般在機器學習中直接用用交叉熵做loss,評估模型。

四、Tensorflow實現

深刻理解入門級別的項目對後續實現更深更大的項目有非常大的幫助,麻雀雖小,五臟俱全,所以不要小瞧這些被講爛了的入門項目,自己試著動手去實現一下吧!代碼可以在我的github上面下載hiJulie/Softmax-Regression。當然要學習Tensorflow入門的項目可以看文末的鏈接一,寫的通俗易懂。本文的代碼撰寫大部分也參考了該文的格式。

其實使用Tensorflow實現一個演算法,大多由以下幾個核心步驟組成:

  • 準備訓練數據:包括導入必須的包及庫
  • forward:包括前向計算中的所有公式及變數的定義
  • loss及loss優化器的選擇
  • 使用tensorboard
  • 訓練
  • 在測試集或者驗證集上對準確率進行測評

#準備訓練數據import tensorflow as tfimport numpy as npfrom tensorflow.examples.tutorials.mnist import input_datamnist = input_data.read_data_sets(MNIST_data/, one_hot=True)print(mnist.train.images.shape, mnist.train.labels.shape)print(mnist.test.images.shape, mnist.test.labels.shape)print(mnist.validation.images.shape, mnist.validation.labels.shape)learning_rate = 0.5training_epochs =1000display_step = 50save_step = 500#forwardX = tf.placeholder(dtype=tf.float32, shape=[None, 784], name=x)Y = tf.placeholder(dtype=tf.float32, shape=[None, 10], name=y)with tf.variable_scope(softmax_regression): W = tf.get_variable(initializer=tf.zeros([784, 10]),name="W") b = tf.get_variable(initializer=tf.zeros([10]), name="b") y_hat = tf.nn.softmax(tf.matmul(X, W) + b)#loss及loss優化with tf.variable_scope(loss): loss = tf.reduce_mean(-tf.reduce_sum(Y * tf.log(y_hat), reduction_indices=[1])) train_op = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)#使用tensorboardckpt_path = ./ckpt/softmax-regression-model.ckptsaver = tf.train.Saver()summary_path = ./ckpt/graphtf.summary.histogram(weights, W)tf.summary.histogram(bias, b)tf.summary.scalar(loss, loss)merge_all = tf.summary.merge_all()#訓練with tf.Session() as sess: init_op = tf.global_variables_initializer() sess.run(init_op) summary_writer = tf.summary.FileWriter(summary_path, sess.graph) x, y = mnist.train.next_batch(100) for epoch in range(training_epochs): _, summary = sess.run([train_op, merge_all], feed_dict={X:x, Y:y}) if (epoch+1) % display_step == 0: c = sess.run(loss, feed_dict={X:x, Y:y}) print(Epoch:, %04d % (epoch+1), loss=, {:.9f}.format(c), w=, W.eval(), b=, b.eval()) summary_writer.add_summary(summary, global_step=epoch) if (epoch+1) % save_step == 0: save_path = saver.save(sess, ckpt_path, global_step=epoch) print(model saved in file: %s % save_path) print(Optimization Finished!) save_path = saver.save(sess, ckpt_path, global_step=epoch) print(Final model saved in file: %s % save_path) summary_writer.close() training_loss = sess.run(loss, feed_dict={X: x, Y: y}) print(Training loss=, training_loss, w=, sess.run(W), b=, sess.run(b),
) #測評準確率 prediction = tf.equal(tf.argmax(Y, 1), tf.argmax(y_hat, 1)) accuracy = tf.reduce_mean(tf.cast(prediction, tf.float32)) print(sess.run(accuracy, feed_dict={X:mnist.test.images, Y:mnist.test.labels}))

五、題外話

這裡多說一句,這裡雖然是多分類,但針對某一張圖片,該圖片上只可能是這十種類別中的某一類。對於需要區分單張圖片既有貓也有狗的分類任務,不能使用softmax函數,而應該使用sigmoid函數。因為這張圖片有貓跟有狗是獨立的,因此最後的預測概率值不再滿足概率和為1.只需要使用sigmoid函數將輸出進行歸一化到[0,1],此時把每一種類別當做0-1分布分別計算交叉熵。實在不想打公式了,有興趣的可以看下我的筆記~雖然字丑表達的還有點凌亂

參考博客:

從線性回歸入門Tensorflow

一文搞懂交叉熵在機器學習中的使用,透徹理解交叉熵背後的直覺 - CSDN博客

推薦閱讀:

鴨綠江大橋邊長大,自己隨手寫寫
Photoshop製作精美手寫情書
這字好靚,硬筆高手寫行書比字帖還漂亮,看了叫好不絕!
回不去的手寫時光…
新華字典手寫查字

TAG:手寫 | 深度學習DeepLearning | 神經網路 |