1. 程式人生 > >動手學習深度學習1-4 softmax

動手學習深度學習1-4 softmax

Softmax 迴歸的從零開始實現

%matplotlib inline
import gluonbook as gb  #要把這個包放在同一個根目錄下
from mxnet import autograd, nd
#============獲取和讀取資料使用 Fashion-MNIST 資料集,並設定批量大小為 256
batch_size = 256
train_iter, test_iter = gb.load_data_fashion_mnist(batch_size)#完成了將資料封裝成資料迭代器
#這函式中有.vision.FashionMNIST()會自動下載資料還有.DataLoader()這函式是將資料弄成一個數據迭代器

#===========初始化模型引數
#跟線性迴歸中的例子一樣,我們將使用向量表示每個樣本。已知每個樣本輸入是高和寬均為 28 畫素的影象。
#模型的輸入向量的長度是  28×28=784 :該向量的每個元素對應影象中每個畫素。由於影象有 10 個類別,單層神經網路輸出層的輸出個數為 10。
#所以 Softmax 迴歸的權重和偏差引數分別為 784×10  和  1×10 的矩陣。
num_inputs=784
num_outputs=10
W=nd.random.normal(scale=0.01,shape=(num_inputs,num_outputs))
b=nd.zeros(num_outputs)
W.attach_grad()  #就是為引數梯度的存放開闢了空間,以助於引數更新的時候進行訪問
b.attach_grad()

#===========實現 Softmax 運算
#給定一個 NDArray 矩陣X。我們可以只對其中同一列(axis=0)或同一行(axis=1)的元素求和,
# #並在結果中保留行和列這兩個維度(keepdims=True)。
# X = nd.array([[1, 2, 3], [4, 5, 6]])
# X.sum(axis=0, keepdims=True), X.sum(axis=1, keepdims=True)
# 在下面的函式中,矩陣X的行數是樣本數,列數是輸出個數。
# 為了表達樣本預測各個輸出的概率,softmax 運算會先通過exp函式對每個元素做指數運算,
# 再對exp矩陣同行元素求和,最後令矩陣每行各元素與該行元素之和相除。這樣一來,最終得到的矩陣每行元素和為 1 且非負。
# 因此,該矩陣每行都是合法的概率分佈。Softmax 運算的輸出矩陣中的任意一行元素代表了一個樣本在各個輸出類別上的預測概率。
def softmax(X):
    X_exp=X.exp()
    partition=X_exp.sum(axis=1,keepdims=True)#對行求和,保留行和列
    return X_exp/partition  #這裡運用了廣播機制

#==================定義模型
# 有了 softmax 運算,我們可以定義上節描述的 softmax 迴歸模型了。
# 這裡通過reshape函式將每張原始影象改成長度為num_inputs的向量
def net(X):
    return softmax(nd.dot(X.reshape((-1,num_inputs)),W)+b)
# 因為X是3d的東西要把它resize成2d的,-1是讓系統自己去判斷要多少行其實這就是batch_size


#======================定義損失函式
# softmax 迴歸使用的交叉熵損失函式。為了得到標籤的預測概率,我們可以使用pick函式。
# 在下面例子中,變數y_hat是 2 個樣本在 3 個類別的預測概率,變數y是這 2 個樣本的標籤類別。
# 通過使用pick函式,我們得到了 2 個樣本的標籤的預測概率。
# 與“Softmax 迴歸”一節數學表述中標籤類別離散值從 1 開始逐一遞增不同,在程式碼中,標籤類別的離散值是從 0 開始逐一遞增的。
# y_hat = nd.array([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
# y = nd.array([0, 2])
# nd.pick(y_hat, y)
#定義交叉熵損失函式
def cross_entropy(y_hat,y):
    return -nd.pick(y_hat,y).log()


#===================計算分類準確率
# 給定一個類別的預測概率分佈y_hat,我們把預測概率最大的類別作為輸出類別。
# 如果它與真實類別y一致,說明這次預測是正確的。分類準確率即正確預測數量與總預測數量之比。
# 下面定義準確率accuracy函式。其中y_hat.argmax(axis=1)返回矩陣y_hat每行中最大元素的索引,
# 且返回結果與變數y形狀相同。我們在“資料操作”一節介紹過,相等條件判斷式(y_hat.argmax(axis=1) == y)
# 是一個值為 0(相等為假)或 1(相等為真)的 NDArray。由於標籤型別為整數,我們先將變數y變換為浮點數再進行相等條件判斷。
def accuracy(y_hat,y):
    return (y_hat.argmax(axis=1)==y.astype('float32')).mean().asscalar()  #asscalar()將向量變成標量

# 讓我們繼續使用在演示pick函式時定義的變數y_hat和y,並將它們分別作為預測概率分佈和標籤。
# 可以看到,第一個樣本預測類別為 2(該行最大元素 0.6 在本行的索引為 2),與真實標籤 0 不一致;
# 第二個樣本預測類別為 2(該行最大元素 0.5 在本行的索引為 2),與真實標籤 2 一致。因此,這兩個樣本上的分類準確率為 0.5。
# accuracy(y_hat, y)

# 類似地,我們可以評價模型net在資料集data_iter上的準確率。
def evaluate_accuracy(data_iter, net):
    acc = 0
    for X, y in data_iter:
        acc += accuracy(net(X), y)
    return acc / len(data_iter)
# 因為我們隨機初始化了模型net,所以這個隨機模型的準確率應該接近於類別個數 10 的倒數 0.1。
# evaluate_accuracy(test_iter, net)


#=============訓練模型
# 訓練 softmax 迴歸的實現跟前面介紹的線性迴歸中的實現非常相似。我們同樣使用小批量隨機梯度下降來優化模型的損失函式。
# 在訓練模型時,迭代週期數num_epochs和學習率lr都是可以調的超引數。改變它們的值可能會得到分類更準確的模型。
num_epochs,lr=5,0.1
def train_ch3(net,train_iter,test_iter,loss,num_epochs,batch_size,params=None,lr=None,trainer=None):
    for epoch in range(num_epochs):
        train_l_sum=0
        train_acc_sum=0
        for X,y in train_iter:          #X是一個被分成batch_size=256的樣本 ,也就是說這一坨對應256個標籤
            with autograd.record():
                y_hat=net(X)                                 
                l=loss(y_hat,y)

            l.backward()
            if trainer is None:
                gb.sgd(params,lr,batch_size)
            else:
                trainer.setp(batch_size)#x下節會用到
            train_l_sum+=l.mean().asscalar()
            train_acc_sum += accuracy(y_hat,y)
        test_acc=evaluate_accuracy(test_iter,net)
        print('epoch %d,loss %.4f,train acc %.3f,test acc %.3f'
              %(epoch+1,train_l_sum/len(train_iter),
              train_acc_sum/len(train_iter),test_acc))
# train_ch3(net,train_iter,test_iter, cross_entropy,num_epochs,
#          batch_size,[W,b],lr)
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs,
          batch_size, [W, b], lr)