動手深度學習7-從零開始完成softmax分類

import torch
import torchvision
import numpy as np
import sys
import random
import torchvision.transforms as transforms
sys.path.append('..')
import d2lzh_pytorch as d2l
獲取和讀取數據

咱們將使用Fahsion_MNIST數據集,並設置批量大小爲256python

batch_size= 256
mnist_train= torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST',download=True,train=True,transform=transforms.ToTensor())
mnist_test = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST',download=True,train=False,transform=transforms.ToTensor())

if sys.platform.startswith('win'):
    num_worker=0   # 表示不用額外的進程來加速讀取數據
    
else:
    num_worker=4
train_iter = torch.utils.data.DataLoader(mnist_train,batch_size=batch_size,shuffle=True,num_workers=num_worker)
test_iter = torch.utils.data.DataLoader(mnist_test,batch_size=batch_size,shuffle=False,num_workers=num_worker)
初始化模型參數

與線性迴歸中的例子同樣,咱們將使用向量表示每一個樣本。已知每一個樣本輸入是高和寬均爲像素28的圖像,模型輸入向量的長度爲28*28=784;該向量的每一個元素對應圖中每一個元素。因爲圖像有10個類別,單層神經網絡輸出層的輸出個數爲10,所以softmax迴歸的權重和誤差參數分別是784x10 和1x10的矩陣。算法

num_inputs= 784
num_outputs = 10

def set_seed(seed=9699): # seed的數值能夠隨意設置,本人不清楚有沒有推薦數值
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    #根據文檔,torch.manual_seed(seed)應該已經爲全部設備設置seed
    #可是torch.cuda.manual_seed(seed)在沒有gpu時也可調用,這樣寫沒什麼壞處
    torch.cuda.manual_seed(seed)
    #cuDNN在使用deterministic模式時(下面兩行),可能會形成性能降低(取決於model)
    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False

set_seed(9699)  # 保證同一個隨機種子產生的訓練結果一致





W = torch.tensor(np.random.normal(0,0.01,(num_inputs,num_outputs)),dtype=torch.float)
b = torch.zeros(num_outputs,dtype=torch.float)
# 同以前同樣,咱們須要模型參數梯度
W.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True)
tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], requires_grad=True)
實現softmax運算
'''
在實現softmax迴歸以前,咱們先描述回下對多維Tensor按照維度進行操做。
跟以前pandas的裏面對於DataFrame的操做同樣,對於axis或者維度的進行操做
下面給定一個Tensor矩陣X,能夠對於其中同一列dim=0或者同一行的元素進行操做,
並在結果中保留行和列兩個維度(keepdim=True)
'''
X= torch.tensor([[1,2,3],[4,5,6]])
print(X)
print('沿着列進行操做\n',X.sum(dim=0,keepdim=True))
print('沿着行進行操做\n',X.sum(dim=1,keepdim=True))
tensor([[1, 2, 3],
        [4, 5, 6]])
沿着列進行操做
 tensor([[5, 7, 9]])
沿着行進行操做
 tensor([[ 6],
        [15]])
'''
1. 先規定X矩陣的中行數爲樣本數,列數爲輸出個數。爲了表示樣本預測各個輸出的機率,
2. softmax運算會先經過exp函數對每一個元素作指數運算,在對exp矩陣同行元素求和
3. 而後令矩陣每行元素與該行元素之和相除。
4. 這樣,最終獲得的矩陣每行元素和爲1且非負,符合機率分佈。
5. softmax運算的輸出矩陣中的任一行元素表明了一個樣本在各個輸出類別上個預測機率
'''
def softmax(X):
    X_exp = X.exp()
    partition = X_exp.sum(dim=1,keepdim=True)
    return X_exp/partition
X = torch.rand((2,5))
X_prob = softmax(X)
print(X_prob)
print(X_prob.sum(dim=1))
tensor([[0.2524, 0.1500, 0.2120, 0.1873, 0.1982],
        [0.2157, 0.1602, 0.3160, 0.1392, 0.1689]])
tensor([1., 1.])
定義模型

有了softmax運算,咱們就能夠定義上節描述的迴歸模型了。這個經過view函數將每張原始圖像改爲長度爲num_inputs的向量網絡

def net(X):
    return softmax(torch.mm(X.view(-1,num_inputs),W)+b)
定義損失函數

上節中,咱們介紹了softmax迴歸使用的交叉熵損失函數。爲了獲得標籤的預測機率,咱們可使用father函數。在下面的例子中,變量y_hat是2個樣本在3個類別的預測機率,變量y是兩個樣本的標籤類別。使用gather函數,咱們獲得了2個樣本的標籤預測機率app

y_hat = torch.tensor([[0.1,0.3,0.6],[0.3,0.2,0.5]])
y = torch.LongTensor([0,2])
y_hat.gather(1,y.view(-1,1))
# 這裏須要單獨講一下,y.view(-1,1)是將y變造成2行1列的tensor
# 而後從y_hat中去取第一行中的第一個元素和第二行中的第三個元素
tensor([[0.1000],
        [0.5000]])
def cross_entropy(y_hat,y):
    return - torch.log(y_hat.gather(1,y.view(-1,1)))
cross_entropy(y_hat,y)
tensor([[2.3026],
        [0.6931]])
計算分類準確率
  • 給定一個類別的預測機率分佈y_hat,咱們預測機率最大的類別做爲輸出類別。若是它與真實的類別y一致,說明預測正確,分類準確率=正確預測數量/總預測量只比dom

  • 爲了演示準確率的計算,下面定義準確率定義函數函數

def accuracy(y_hat,y):
    return (y_hat.argmax(dim=1)==y).float().mean().item()
print(accuracy(y_hat,y))
0.5
def evaluate_accuracy(data_iter, net):
    acc_sum, n = 0.0, 0
    for X, y in data_iter:
        acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
        n += y.shape[0]
    return acc_sum / n
# print(evaluate_accuracy(test_iter,net))
# 初始化模型後對測試集的準確率
訓練模型

softmax迴歸於實現線性迴歸相似,一樣適用小批量隨機梯度降低來優化模型的損失函數。在訓練模型時,迭代週期數num_epochs和學習率lr都是能夠調節的超參數。性能

def sgd(params,lr,batch_size):
    for param in params:
        param.data -=lr* param.grad/batch_size
num_epochs, lr = 5, 0.1

def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size,
              params=None, lr=None, optimizer=None):
    for epoch in range(num_epochs):
        train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
        for X, y in train_iter:
            y_hat = net(X)
            l = loss(y_hat, y).sum()

            # 梯度清零
            if optimizer is not None:
                optimizer.zero_grad()
            elif params is not None and params[0].grad is not None:
                for param in params:
                    param.grad.data.zero_()

            l.backward()
            if optimizer is None:
                sgd(params, lr, batch_size)
            else:
                optimizer.step()  # 「softmax迴歸的簡潔實現」一節將用到


            train_l_sum += l.item()
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
            n += y.shape[0]
        test_acc = evaluate_accuracy(test_iter, net)
        print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'
              % (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc))
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, batch_size, [W, b], lr)
epoch 1, loss 0.7850, train acc 0.750, test acc 0.786
epoch 2, loss 0.5705, train acc 0.814, test acc 0.810
epoch 3, loss 0.5254, train acc 0.825, test acc 0.814
epoch 4, loss 0.5017, train acc 0.832, test acc 0.822
epoch 5, loss 0.4854, train acc 0.836, test acc 0.826
X, y = iter(test_iter).next()

true_labels = d2l.get_fashion_mnist_labels(y.numpy())
pred_labels = d2l.get_fashion_mnist_labels(net(X).argmax(dim=1).numpy())
titles = [true + '\n' + pred for true, pred in zip(true_labels, pred_labels)]

d2l.show_fashion_mnist(X[0:9], titles[0:9])

小結
  1. 獲取並讀取數據,拆分數據集
  2. 定義模型和損失函數並使用優化算法訓練模型
  3. 評估模型
相關文章
相關標籤/搜索