softmax回歸的從零開始實現


一、創建數據集

從Fashion-MNIST數據集中引入創建數據,並設置數據迭代器的批量大小為256

import torch
from IPython import display
from d2l import torch as d2l
#batch_size=256,表明隨機讀取256張圖片
batch_size = 256

# 返回訓練集和測試集的迭代器
# load_data_fashion_mnist函數是在圖像分類數據集中定義的一個函數,可以返回batch_size大小的訓練數據集和測試數據集
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

  

二、初始化模型參數

1、原始數據集中的每個樣本都是28*28的圖像

2、在本書中,我們將展平每個圖像,把它們看作長度為784的向量

3、在softmax回歸中,我們輸出與類別一樣多。(因為我們的數據集有10個類別,所以網絡輸出的維度為10)

4、權重將構成一個 784×10 的矩陣,偏置將構成一個 1×10 的行向量。與線性回歸一樣,我們將使用正態分布初始化我們的權重 W,偏置初始化為0

num_inputs = 784 # 樣本的長和寬都是28,將其展平為空間向量,長度變為784
num_outputs = 10 # 數據集類別,也是輸出數


#size=(num_inputs, num_outputs);行數等於輸入的個數,列數等於輸出的個數
#requires_grad=True表明要計算梯度
#權重
#在這里W被定義為一個784*10的矩陣
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)

#偏離
b = torch.zeros(num_outputs, requires_grad=True)

  

三、定義softmax操作

# 定義softmax()函數

def softmax(X):
    #torch.exp()對每個元素做指數計算
    X_exp = torch.exp(X)
    
    #對矩陣的每一行求和,重新生成新的矩陣
    partition = X_exp.sum(1, keepdim=True)
    
    
    # X_exp / partition:每一行代表一個樣本,行中的每個數據代表在該類別的概率
    return X_exp / partition  # 這里應用了廣播機制

可以發現,每個元素都變成了一個非負數。此外,依據概率原理,每行總和為1。

如下,是個小測試

#創建一個隨機的均值為0,方差為1的兩行五列的矩陣
X = torch.normal(0, 1, (2, 5))

#對矩陣進行softmax計算
X_prob = softmax(X)

X_prob, X_prob.sum(1)
#通過輸出可以發現在softmax之后,每個元素都是正的,且行之和為1

#輸出結果

(tensor([[0.0474, 0.0313, 0.8646, 0.0317, 0.0250],
         [0.1736, 0.4575, 0.2832, 0.0618, 0.0239]]),
 tensor([1.0000, 1.0000]))

  

四、定義模型

1、利用之前實現的softmax操作,可以實現softmax回歸模型

2、在將數據傳遞到模型之前,需要使用reshape函數將每張原始圖像展平為向量

def net(X):
    #matmul是矩陣乘法
    #W.shape[0]=784
    
    # W是一個784*10的矩陣,W.shape就是[784,10]的列表,可以通過W.shape[0]來訪問784
    return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)

  

五、定義損失函數

y = torch.tensor([0, 2])

# y_hat為預測值,此次是對兩個樣本做預測值
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])

# 對第零個樣本,拿出y0的數據=0.1;對第一個樣本,拿出y1的書籍,y1=2,就是拿出第三個數據=0.5
# 第一個參數[0,1]表示樣本號,第二個參數y表示在第一個參數確定的樣本中取數的序號
y_hat[[0, 1], y]

#輸出結果

tensor([0.1000, 0.5000])

上訴可以幫助我們理解“交叉”的含義。接下來,我們只需要添加一行代碼實現損失函數

# 給定我的y_hat預測和真實標號y
def cross_entropy(y_hat, y):
    #對於每一行range(len(y_hat)):生成一個從零開始一直到len(y_hat)的向量
    
    return -torch.log(y_hat[range(len(y_hat)), y])

print(cross_entropy(y_hat, y))
-torch.log(y_hat[[0, 1], y])
#輸出:2.3026樣本零的損失,0.6931樣本1的損失


#輸出結果

tensor([2.3026, 0.6931])
tensor([2.3026, 0.6931])

  

六、分類准確度

1、分類准確率即正確預測數量與總預測數量之比。

2、y_hat 是矩陣,假定第二個維度存儲每個類的預測分數。

3、使用 argmax 獲得每行中最大元素的索引來獲得預測類別

4、將預測類別與真實 y 元素進行比較

# 計算分類准確率,正確預測數量與總預測數量之比
# y_hat是預測值,y是真實值

# 計算預測正確的樣本數
def accuracy(y_hat, y):  #@save
    """計算預測正確的數量。"""
    # len是查看矩陣的行數
    # y_hat.shape[1]就是去列數,y_hat.
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        #每一行中元素最大的那個下標存在y_hat里面
        #最大的元素就可以算為預測分類的類別
        y_hat = y_hat.argmax(axis=1)
        
    #將y_hat轉換為y的數據類型然后作比較
    #使用cmp函數存儲bool類型
    cmp = y_hat.type(y.dtype) == y
    
    #將cmp轉化為y的數據類型再求和——得到找出來預測正確的類別數
    return float(cmp.type(y.dtype).sum())

5、按照之前定義的變量y_hat和y,我們預測准確率(第一個樣本的預測類別是2(該行的最大元素為0.6,索引為2),這與實際標簽0不一致。第二個樣本的預測類別是2(該行的最大元素為0.5,索引為 2),這與實際標簽2一致)

#除以整個y的長度(樣本數),就是預測正確的概率
accuracy(y_hat, y) / len(y)

#輸出結果

0.5

 6、泛化,對於任意數據迭代器data_iter 可訪問的數據集,我們可以評估在任意模型 net 的准確率。

# 評估任意模型的准確率

def evaluate_accuracy(net, data_iter):  #@save
    """計算在指定數據集上模型的精度。"""
    
    
    # isinstance():判斷一個對象是否是一個已知的類型
    # 判斷輸入的net模型是否是torch.nn.Module類型
    if isinstance(net, torch.nn.Module):
        net.eval()  # 將模型設置為評估模式(不用計算梯度)
        
    metric = Accumulator(2)  # 存儲正確預測數、預測總數
    
    # 每次從迭代器中拿出一個x和y
    for X, y in data_iter:
        
        # 1、net(X):X放在net模型中進行softmax操作
        # 2、accuracy(net(X), y):再計算所有預算正確的樣本數
        # numel()函數:返回數組中元素的個數,在此可以求得樣本數
        metric.add(accuracy(net(X), y), y.numel())
        
    #metric[0]:分類正確的樣本數,metric[1]:總的樣本數
    return metric[0] / metric[1]

evaluate_accuracy(net, test_iter)

#輸出結果
0.1001

  

七、訓練

1、首先,我們定義一個函數來訓練一個迭代周期

2、注意,updater是更新模型參數的常用函數,接受批量大小作為參數

def train_epoch_ch3(net, train_iter, loss, updater):  #@save
"""訓練模型一個迭代周期(定義見第3章)。"""
# 將模型設置為訓練模式 if isinstance(net, torch.nn.Module): net.train()#告訴pytorch我要計算梯度 # 訓練損失總和、訓練准確度總和、樣本數 # Accumulator 是一個實用程序類,用於對多個變量進行累加 #在此創建了一個長度為三的迭代器,用於累加信息 metric = Accumulator(3) for X, y in train_iter: # 計算梯度並更新參數 y_hat = net(X) l = loss(y_hat, y) if isinstance(updater, torch.optim.Optimizer): # 使用PyTorch內置的優化器和損失函數 updater.zero_grad()#先把梯度設置為零 l.backward() #計算梯度 updater.step()#自更新 metric.add( float(l) * len(y), accuracy(y_hat, y), y.size().numel()) else: # 使用定制的優化器和損失函數 # 如果是自我實現的話,l出來就是向量,我們先做求和,再求梯度 l.sum().backward() updater(X.shape[0]) metric.add(float(l.sum()), accuracy(y_hat, y), y.numel()) # 返回訓練損失和訓練准確率 # metric[0]就是損失樣本數目;metric[1]是訓練正確的樣本數;metric[2]是總的樣本數 return metric[0] / metric[2], metric[1] / metric[2]

3、實現一個訓練函數,它會在train_iter 訪問到的訓練數據集上訓練一個模型net

4、該訓練函數將會運行多個迭代周期(由num_epochs指定)

def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):  #@save
    """訓練模型(定義見第3章)。"""
    animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],
                        legend=['train loss', 'train acc', 'test acc'])
    
    
    # num_epochs:訓練次數
    for epoch in range(num_epochs):
        #train_epoch_ch3:訓練模型,返回准確度和錯誤度
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
        #在測試數據集上評估精度
        test_acc = evaluate_accuracy(net, test_iter)
        
        animator.add(epoch + 1, train_metrics + (test_acc,))
    train_loss, train_acc = train_metrics
    assert train_loss < 0.5, train_loss
    assert train_acc <= 1 and train_acc > 0.7, train_acc
    assert test_acc <= 1 and test_acc > 0.7, test_acc

5、作為一個從零開始的實現,我們使用 小批量隨機梯度下降來優化模型的損失函數,設置學習率為0.1

lr = 0.1

def updater(batch_size):
    return d2l.sgd([W, b], lr, batch_size)

6、現在,訓練模型10個迭代周期。請注意,迭代周期(num_epochs)和學習率(lr)都是可調節的超參數。通過更改它們的值,我們可以提高模型的分類准確率。

#開始訓練
num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)

  

八、預測

給定一系列圖像,比較它們的實際標簽(文本輸出的第一行)和模型預測(文本輸出的第二行)

def predict_ch3(net, test_iter, n=6):  #@save
    """預測標簽(定義見第3章)。"""
    for X, y in test_iter:
        break
    #真實標號
    trues = d2l.get_fashion_mnist_labels(y)
    #預測標號
    preds = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1))
    titles = [true + '\n' + pred for true, pred in zip(trues, preds)]
    d2l.show_images(X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n])

predict_ch3(net, test_iter)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM