慕課:《深度學習應用開發-TensorFlow實踐》
章節:第七講 MNIST手寫數字識別:分類應用入門
TensorFlow版本為2.3
MNIST手寫數字識別數據集介紹及獲取
MNIST 數據集來自美國國家標准與技術研究所, National Institute of Standards and Technology (NIST)。數據集由來自 250 個不同人手寫的數字構成, 其中 50% 是高中學生, 50% 來自人口
普查局 (the Census Bureau) 的工作人員。
這一份數據集共有訓練集60000個,測試集10000個,下面這張圖展示了一小部分
數據集的獲取可以直接去網站下:https://s3.amazonaws.com/img-datasets/mnist.npz
當然也可以通過TensorFlow
的代碼去獲取數據集,不過值得注意的是,TensorFlow1.*
和TensorFlow2.*
的數據集獲取方式並不相同,下面只提供TensorFlow2.*
的獲取代碼
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
tf.__version__
#加載數據集
mnist=tf.keras.datasets.mnist
(train_images,train_labels),(test_images,test_labels)=mnist.load_data()
然后他就會去對應的網站上下載對應的數據集,不過有個問題,他的下載地址是對應谷歌的一個地址,因此有可能會出現下載錯誤或者下載失敗或者下載比較的慢,還是建議去上面提供的那個鏈接下,當然如果有某些方式,那隨意哈
如果你選擇手動下載,那么請把下載的數據集mnist.npz
存放在用戶目錄的“.keras/dataset”
子目錄下(Windows 下用戶目錄為 C:\Users\用戶名
,Linux 下用戶目錄為 /home/用戶名
)。如果是第一次運行(在用戶目錄下沒有找到數據文件),則會自動先從網絡下載后再加載。如果用戶目錄下已經存在數據文件,則直接加載。
可以來看一下這一個數據集的一些信息
print(f"Train image shape:{train_images.shape} Train label shape:{train_labels.shape}");
print(f"Test image shape:{test_images.shape} Test label shape:{test_labels.shape}");
可以看到,他是28*28
的一個黑白圖片,也就是一張圖片會有784
個像素點。
可以用matplotlib.pyplot
來看一下他的一個圖像
def plot_image(image):
plt.imshow(image.reshape(28,28),cmap='binary')
plt.show()
plot_image(train_images[0])
去第一張圖片看一下就長上面這個樣子
邏輯回歸
許多問題的預測結果是一個在連續空間的數值,比如房價預測問題,可以用線性模型來描述: 𝑌 = 𝑥1 ∗ 𝑤1 + 𝑥2 ∗ 𝑤2 + ⋯ + 𝑥𝑛 ∗ 𝑤𝑛 + 𝑏
但也有很多場景需要輸出的是概率估算值,例如:
- 根據郵件內容判斷是垃圾郵件的可能性
- 根據醫學影像判斷腫瘤是惡性的可能性
- 手寫數字分別是 0、1、2、3、4、5、6、7、8、9的可能性(概率)
這時需要將預測輸出值控制在 [0,1]
區間內,二元分類問題的目標是正確預測兩個可能的標簽中的一個,邏輯回歸(Logistic Regression) 可以用於處理這類問題。下圖展示了邏輯回歸的基本模型
在這個過程中,我們要確保輸出值始終落在0和1直接,這里我們就需要用到Sigmoid函數
Sigmoid函數
Sigmoid函數(S型函數)其定義如下:
定義域為全體實數,值域在[0,1]之間,\(z\)值在0點對應的結果為\(0.5\),且sigmoid函數連續可微分。他的圖像如下
損失函數
前面線性回歸的損失函數是平方損失,如果邏輯回歸的損失函數也定義為平方損失,那么:
這其中:𝑖表示第𝑖個樣本點,\(𝑧_𝑖\) = \(𝑥_𝑖\) ∗ 𝑤 + 𝑏 ,𝜑\((𝑧_𝑖)\) 表示對𝑖個樣本的預測值,\(𝑦_𝑖\)表示第𝑖個樣本的標簽值
那么將Sigmoid函數帶入上述函數就會得到下面這張圖像
有個很明顯的問題,這個函數是非凸函數,有多個極小值,如果采用梯度下降法,會容易導致陷入局部最優解中
在二元邏輯回歸的損失函數一般采用對數損失函數,定義如下:
其中:(𝑥, 𝑦) ∈ 𝐷是有標簽樣本 (𝑥, 𝑦) 的數據集,𝑦是有標簽樣本中的標簽,取值必須是 0 或 1,𝑦′是對於特征集𝑥的預測值(介於 0 和 1 之間)
它的圖像就長下面這樣
它就很明顯是個凸函數了
多元分類
多元分類的基本模型如下
而我們要在這個基本模型上給他加一個softmax層
就長這個樣子,這里有一個Softmax思想
邏輯回歸可生成介於 0 和 1.0 之間的小數。例如,某電子郵件分類器的邏輯回歸輸出值為 0.8,表明電子郵件是垃圾郵件的概率為80%,不是垃圾郵件的概率為 20%。很明顯,一封電子郵件是垃圾郵件或非垃圾郵件的概率之和為 1.0。
Softmax 將這一想法延伸到多類別領域。在多類別問題中,Softmax 會為每個類別分配一個用小數表示的概率。這些用小數表示的概率相加之和必須是 1.0。
可以舉個例子
通俗的說就是那個概率大就選哪個,就和買彩票一樣,實質上就是猜。
這里我們使用到的Softmax 方程式如下
這個公式本質上是將邏輯回歸公式延伸到了多類別。
多分類問題中的標簽數據與獨熱編碼
在機器學習中特征之間距離的計算或相似度的常用計算方法都是基於歐式空間的,那么有時候就會產生一些問題,比如舉個例子,對於下面這種編碼方式,能說 1 比 8 更相似於 3 嗎?
很顯然,不可以!那怎么辦呢,我們可以使用獨熱編碼
獨熱編碼 (one hot encoding)
一種稀疏向量,其中:
- 一個元素設為 1
- 所有其他元素均設為 0
獨熱編碼常用於表示擁有有限個可能值的字符串或標識符。
那么為什么要用獨熱編碼?
- 將離散特征的取值擴展到了歐式空間,離散特征的某個取值就對應歐式空間的某個點
- 機器學習算法中,特征之間距離的計算或相似度的常用計算方法都是基於歐式空間的
- 將離散型特征使用one-hot編碼,會讓特征之間的距離計算更加合理
同樣上面這個例子,如果我們使用獨熱編碼
是不是很明顯就合理了許多!
獨熱編碼的一些操作
創建獨熱編碼
import tensorflow as tf
x=[3,4]
tf.one_hot(x,depth=10)
這樣我們就得到了3和4的獨熱編碼
獨熱編碼取值
獨熱編碼的取值其實很簡單,你只要找到值為1的那個下標就可以了,也就是找到最大值的下標,argmax()
方法就可以完成這個操作,同樣舉幾個例子
一維數組
>>>A=tf.constant([1,2,3])
>>>tf.argmax(A).numpy()
2
二維數組
二維數組我們可以通過axis
參數來指定行列,axis=0
為行,axis=1
為列
B=tf.constant([[1,2,3],[4,5,6],[7,8,9]])
tf.math.argmax(B,axis=0).numpy()
Out[5]: array([2, 2, 2], dtype=int64)
tf.math.argmax(B,axis=1).numpy()
Out[6]: array([2, 2, 2], dtype=int64)
此外,萬能的Numpy庫也同樣提供了這個方法
import numpy as np
C=np.array([[1,2,3],[4,5,6],[7,8,9]])
np.argmax(C,axis=1)
Out[10]: array([2, 2, 2], dtype=int64)
np.argmax(C,axis=0)
Out[11]: array([2, 2, 2], dtype=int64)
交叉熵損失
在這個實驗中,我們所使用的是交叉熵(shang)損失
交叉熵是一個信息論中的概念,它原來是用來估算平均編碼長度的。給定兩個概率分布p
和q
,通過q
來表示p
的交叉熵為
交叉熵刻畫的是兩個概率分布之間的距離,p代表正確答案,q代表的是預測值,交叉熵越小,兩個概率的分布約接近
舉個例子
假設有一個3分類問題,某個樣例的正確答案是(1,0,0)
甲模型經過softmax回歸之后的預測答案是(0.5,0.2,0.3)
乙模型經過softmax回歸之后的預測答案是(0.7,0.1,0.2)
那么
𝑯 ((1, 𝟎, 𝟎) , (𝟎. 𝟓, 𝟎. 𝟐, 𝟎. 𝟑)) = -log𝟎.𝟓 ≈ 0.301
𝑯 ((1, 𝟎, 𝟎) , (𝟎. 𝟕, 𝟎. 𝟏, 𝟎. 𝟐))= -log𝟎.𝟕 ≈ 0.155
交叉熵損失函數定義為:
理論篇到此為止,實戰篇請看:
MNIST手寫數字識別:分類應用入門(實踐篇)
學習筆記,僅供參考,如有錯誤,敬請指正!