09.softmax回歸+圖像分類數據集+從零實現+簡潔實現(與課程對應)
目錄
?一、softmax回歸
?1、回歸 vs 分類
2、經典分類數據集:?
3、從回歸到分類——均方損失
4、從回歸到多類分類——無校驗比例
?5、從回歸到多類分類——校驗比例
?6、softmax和交叉熵損失
7、總結
二、損失函數
1、均方損失函數 L2?loss
2、絕對值損失函數L1 loss
3、Huber‘s 魯棒損失
三、圖片分類數據集
四、從零實現
五、簡潔實現?
?一、softmax回歸
?1、回歸 vs 分類
回歸估計一個連續值:例如房子賣的價格
- 單連續數值輸出
- 自然區間R
- 跟真實值的區別作為損失
分類預測一個離散類別:例如一個圖片里面是貓還是狗
- 通常多個輸出
- 輸出?i 是預測為第 i 類的置信度。
2、經典分類數據集:?
MNIST 數據集?:一個非常經典的數據集,用于識別手寫數字 0 到 9,是一個十類分類問題。
?
ImageNet 數據集?:深度學習中特別經典的數據集,有 100 萬張圖片,每一張圖片是一個自然物體,屬于 1000 類自然物體之一,其中包含大概 100 種不同的狗 ,該問題為 1000 類的分類問題。
3、從回歸到分類——均方損失
- 類別編碼?:
- 編碼原因?:類別常為字符串,不是數值,需進行編碼處理。
- 編碼方式?:若有 n 個類別,采用一位有效編碼。標號是長為 n 的向量 Y1、Y2 … YN ,若真實類別是第 i 個,則 Yi 等于 1,其他元素全部為 0 。
- 模型訓練與預測?:
- 訓練方法?:編碼后可用回歸問題的均方損失進行訓練,無需改動。
- 預測方式?:訓練出模型后,做預測時選取使置信度值最大化的 i,即通過 argmax o i 得到預測標號 yhat 。
4、從回歸到多類分類——無校驗比例
- Softmax 回歸的問題及目標:
- 關注置信度?:在分類中,更關心對正確類別的置信度要足夠大,而非實際值。
- 目標函數改進?:希望正確類別 y 的置信度 oy 遠大于其他非正確類的 oi,數學表示為 oy - oi 大于某閾值 Delta,以此拉開正確類與其他類的距離(最大值作為預測、需要更置信的識別正確類)。
?5、從回歸到多類分類——校驗比例
- 輸出處理的想法:
- 輸出值區間調整?:將輸出值放到合適區間能讓后續處理更簡單,比如希望輸出是概率。
- 引入 Softmax 操作?:對輸出向量 o 應用 Softmax 操作得到 y hat 向量,其元素非負且和為 1,滿足概率屬性。
- Softmax 具體計算?:y hat 的第 i 個元素等于 o 的第 i 個元素做指數后,除以所有 o 元素做指數的和,這樣 y hat 就可作為概率。
?6、softmax和交叉熵損失
- 交叉熵基本概念:
- 衡量概率區別:使用交叉熵(cross entropy)衡量兩個概率的區別,將其作為損失來比較預測概率和真實概率。
- 離散概率公式:假設有兩個離散概率 p 和 q,有 n 個元素,交叉熵公式為對每個元素 i,負的 pi 乘以 log qi 然后求和。
- 在分類問題中的損失計算:
- 損失公式:對于真實標號 y 和預測標號 y hat,損失 l (y, y hat) 等于對所有 i 類別求和,負的 yi 乘以 log yi hat 。
- 簡化計算:由于 y 中只有一個元素為 1 其余為 0,求和可簡化為負的對真實類別 y 的預測值 y hat 求 log 。
- 與梯度的關系:
- 梯度計算:損失的梯度是真實概率和預測概率的區別,如損失對 DOI 求導等于 Softmax 的第二個元素減去真實類別 y。
- 梯度下降作用:梯度下降時不斷減去真實和預測概率的區別,使預測的 Softmax 值和真實的 y 更相近 。
7、總結
- Softmax回歸是一個多類分類模型
- 使用Softmax操作子得到每個類的預測置信度
- 使用交叉熵來衡量預測和標號的區別
二、損失函數
損失函數用于衡量預測值和真實值間的區別,在機器學習里是重要概念。簡單介紹三個常用的損失函數。
1、均方損失函數 L2?loss
- 定義?:均方損失又叫 L2?loss,定義為真實值 y 減去預測值 y',做平方再除以 2,這樣求導數時 2 和 1/2 抵消變為 1。
- 特性可視化?:用三條曲線可視化其特性,藍色曲線表示 y = 0 時變換預測值 y' 的函數,是二次函數且為偶函數;橙色線表示損失函數的梯度,是穿過原點的一次函數。
- 參數更新?:梯度決定參數更新方式,預測值 y' 與真實值 y 距離遠時梯度大,參數更新多;靠近時梯度絕對值變小,參數更新幅度變小。
?
2、絕對值損失函數L1 loss
- L1 損失函數定義:
- 公式形式:L1 損失函數定義為預測值減去真實值的絕對值,反過來也一樣,即 | 預測值 - 真實值 |。
- 函數曲線情況:
- 曲線呈現:藍色線是損失函數曲線,當 y = 0 時的樣子;綠色線是似然函數曲線,在原點處有一個很尖的點。
- L1 損失函數導數:
- 大于 0 時導數:當 y' 大于 0 時,導數為常數 1。
- 小于 0 時導數:當 y' 小于 0 時,導數為常數 -1。
- 特殊點情況:由于絕對值函數在 0 點處不可導,其 subgradients(次梯度)可以在 -1 和 1 之間 。
- L1 損失函數特性:
- 距離遠時特點:當預測值跟真實值隔得比較遠時,不管距離多遠,梯度永遠是常數,這帶來穩定性好處,權重更新不會特別大。
- 距離近時劣勢:在零點處不可導,且在零點處有從 -1 到 1 之間的劇烈變化,這種不平滑性導致在預測值和真實值靠得比較近,即優化末期時可能變得不穩定 。
3、Huber‘s 魯棒損失
- Harbor 魯棒損失函數定義:
- 差值較大情況:當預測值和真實值差的絕對值大于 1 時,損失函數是絕對值誤差(y 減去 y 一撇的絕對值)并減去 LNG,使曲線能連起來。
- 差值較近情況:當預測值和真實值差的絕對值小于等于 1 時,損失函數是平方誤差。
- Harbor 魯棒損失函數曲線特點:
- 藍色曲線:在正 1 負 1 之間是平滑二次函數曲線,之外是另一條曲線。
- 綠色線(Sech 函數):類似高斯分布,在原點處不像絕對值誤差那樣尖銳。
- Harbor 魯棒損失函數導數特點:
- 差值較大時:當 y 撇大于 1 或者小于 -1 時,導數是常數。
- 差值較小時:當 y 撇在 -1 到 1 之間時,導數是漸變過程。
- 導數好處:預測值和真實值差得遠時,梯度用均勻力度往回拉;靠近時,梯度絕對值變小,保證優化平滑,避免數值問題。
三、圖片分類數據集
MNIST數據集是圖像分類中廣泛使用的數據集之一,但作為基準數據集過于簡單,使用類似但更復雜的Fashion-MNIST數據集。
1、導入庫
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l
2、?通過框架中的內置函數,將Fashion-MNIST數據集下載并讀取到內存中
# 通過ToTensor實例將圖像數據從PIL類型變換成32位浮點數格式;并除以255使得所有像素的數值均在0到1之間
trans = transforms.ToTensor() # 預處理,用于將圖片轉為tensor
mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True) # 在torchvision.datasets找到FashionMNIST數據集,參數:root="../data"下載目錄, train=True下載訓練數據集, transform=trans圖片轉為pytorch的tensor, download=True下載
mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True)
print("len(mnist_train):", len(mnist_train), "\nlen(mnist_test):", len(mnist_test))
print("mnist_train[0][0].shape,訓練集中第一張圖片的形狀:", mnist_train[0][0].shape, "\nmnist_train[0][1],訓練集中第一張圖片的標簽:", mnist_train[0][1]) # mnist_train[0][0]是圖片、mnist_train[0][1]是標簽
3、?兩個可視化數據集的函數,來畫一下數據集
def get_fashion_mnist_labels(labels):"""返回Fashion-MNIST數據集的文本標簽"""text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat','sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']return [text_labels[int(i)] for i in labels]def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):"""繪制圖像列表"""figsize = (num_cols * scale, num_rows * scale)_, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize) # 使用subplots創建子圖axes = axes.flatten()for i, (ax, img) in enumerate(zip(axes, imgs)):if torch.is_tensor(img):ax.imshow(img.numpy()) # 圖片張量else:ax.imshow(img) # PIL圖片ax.axes.get_xaxis().set_visible(False)ax.axes.get_yaxis().set_visible(False)if titles:ax.set_title(titles[i])return axesX, y = next(iter(data.DataLoader(mnist_train, batch_size=18))) # 加載批量圖片
show_images(X.reshape(18, 28, 28), 2, 9, titles=get_fashion_mnist_labels(y)) # 顯示圖片并添加標簽
d2l.plt.show() # 圖片在線展示
4、?讀取一小批量數據,大小batch_size
batch_size = 256
def get_dataloader_workers():"""使用4個進程來讀取的數據"""return 0 # 寫幾就是幾個進程train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_workers())
timer = d2l.Timer()
for X, y in train_iter:continue
print(f'{timer.stop():.2f} sec')
5、 整合所有組件
# 4、整合所有組件
def load_data_fashion_mnist(batch_size, resize=None):"""下載Fashion-MNIST數據集,然后將其加載到內存中"""trans = [transforms.ToTensor()]if resize:trans.insert(0, transforms.Resize(resize))trans = transforms.Compose(trans)mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True)mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True)return (data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_workers()),data.DataLoader(mnist_test, batch_size, shuffle=False, num_workers=get_dataloader_workers()))
# 測試整和組件功能
train_iter, test_iter = load_data_fashion_mnist(32, resize=64)
for X, y in train_iter:print(X.shape, X.dtype, y.shape, y.dtype)break
完整代碼:
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l# 1、通過框架中的內置函數,將Fashion-MNIST數據集下載并讀取到內存中
# 通過ToTensor實例將圖像數據從PIL類型變換成32位浮點數格式;并除以255使得所有像素的數值均在0到1之間
trans = transforms.ToTensor() # 預處理,用于將圖片轉為tensor
mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True) # 在torchvision.datasets找到FashionMNIST數據集,參數:root="../data"下載目錄, train=True下載訓練數據集, transform=trans圖片轉為pytorch的tensor, download=True下載
mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True)
print("len(mnist_train):", len(mnist_train), "\nlen(mnist_test):", len(mnist_test))
print("mnist_train[0][0].shape,訓練集中第一張圖片的形狀:", mnist_train[0][0].shape, "\nmnist_train[0][1],訓練集中第一張圖片的標簽:", mnist_train[0][1]) # mnist_train[0][0]是圖片、mnist_train[0][1]是標簽# 2、兩個可視化數據集的函數,來畫一下數據集
def get_fashion_mnist_labels(labels):"""返回Fashion-MNIST數據集的文本標簽"""text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat','sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']return [text_labels[int(i)] for i in labels]def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):"""繪制圖像列表"""figsize = (num_cols * scale, num_rows * scale)_, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize) # 使用subplots創建子圖axes = axes.flatten()for i, (ax, img) in enumerate(zip(axes, imgs)):if torch.is_tensor(img):ax.imshow(img.numpy()) # 圖片張量else:ax.imshow(img) # PIL圖片ax.axes.get_xaxis().set_visible(False)ax.axes.get_yaxis().set_visible(False)if titles:ax.set_title(titles[i])return axesX, y = next(iter(data.DataLoader(mnist_train, batch_size=18))) # 加載批量圖片
show_images(X.reshape(18, 28, 28), 2, 9, titles=get_fashion_mnist_labels(y)) # 顯示圖片并添加標簽
# d2l.plt.show() # 圖片在線展示# 3、讀取一小批量數據,大小batch_size
batch_size = 256
def get_dataloader_workers():"""使用4個進程來讀取的數據"""return 0 # 寫幾就是幾個進程train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_workers())
timer = d2l.Timer()
for X, y in train_iter:continue
print(f'{timer.stop():.2f} sec')'''
# 4、整合所有組件
def load_data_fashion_mnist(batch_size, resize=None):"""下載Fashion-MNIST數據集,然后將其加載到內存中"""trans = [transforms.ToTensor()]if resize:trans.insert(0, transforms.Resize(resize))trans = transforms.Compose(trans)mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True)mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True)return (data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_workers()),data.DataLoader(mnist_test, batch_size, shuffle=False, num_workers=get_dataloader_workers()))
# 測試整和組件功能
train_iter, test_iter = load_data_fashion_mnist(32, resize=64)
for X, y in train_iter:print(X.shape, X.dtype, y.shape, y.dtype)break
'''
四、從零實現
1、導入庫
import torch
from IPython import display
from d2l import torch as d2l
2、數據集
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) # 每次讀取256張圖片,返回訓練集、測試集的迭代器:train_iter, test_iter# 將展平每個圖像1x28x28=784,將它們視為長度為784的向量(因為對于softmax回歸來講,輸入需要是一個向量)。因為我們的數據集有10個類別,所以網絡輸出維度為10
num_inputs = 784
num_outputs = 10
3、參數初始化
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True) # 均值為0,方差為0.01初始化W權重
b = torch.zeros(num_outputs, requires_grad=True) # 全0初始化
4、實現softmax
# 實現softmax
def softmax(X):X_exp = torch.exp(X) # 對每一個元素做指數運算partition = X_exp.sum(1, keepdim=True) # 按照維度為1來求和,對每一行求和;按行求和并保持維度不變,還是2維矩陣return X_exp / partition # 這里應用了廣播機制,# 實現softmax回歸模型
def net(X):return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b) # 對X reshape使其能夠與W做內積
?5、實現交叉熵損失函數
# 實現交叉熵損失函數
def cross_entropy(y_hat, y):return - torch.log(y_hat[range(len(y_hat)), y])
6、?將預測類別與真實y元素進行比較
def accuracy(y_hat, y):"""計算預測正確的數量"""if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:y_hat = y_hat.argmax(axis=1) # argmax返回每行最大值的索引cmp = y_hat.type(y.dtype) == y # cmp為01向量,表示y_hat和y相同索引的個數return float(cmp.type(y.dtype).sum()) # 返回cmp中所有相同索引的和
7、?評估任意模型net的精度
# 我們可以評估任意模型net的精度
def evaluate_accuracy(net, data_iter):"""計算在指定數據集上模型的精度"""if isinstance(net, torch.nn.Module):net.eval() # eval()停用dropout和batchnormmetric = Accumulator(2) # 創建兩個元素的列表with torch.no_grad():for X, y in data_iter:metric.add(accuracy(net(X), y), y.numel()) # 累加獲得預測正確的個數和總個數return metric[0] / metric[1]# Accumulator實例中創建了2個變量, 分別用于存儲正確預測的數量和預測的總數量
class Accumulator:"""在n個變量上累加"""def __init__(self, n):self.data = [0.0] * ndef add(self, *args):self.data = [a + float(b) for a, b in zip(self.data, args)] # 對數據累加def reset(self):self.data = [0.0] * len(self.data)def __getitem__(self, idx):return self.data[idx]
8、Softmax回歸的訓練
# Softmax回歸的訓練
def train_epoch_ch3(net, train_iter, loss, updater):"""訓練模型一個迭代周期(定義見第3章)"""if isinstance(net, torch.nn.Module): # 判斷是否自己實現函數net.train()metric = Accumulator(3)for X, y in train_iter:y_hat = net(X)l = loss(y_hat, y)if isinstance(updater, torch.optim.Optimizer):updater.zero_grad()l.sum().backward()updater.step()else:l.sum().backward()updater(X.shape[0])metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())return metric[0] / metric[2], metric[1] / metric[2]# 定義一個在動畫中繪制數據的實用程序類
class Animator:"""在動畫中繪制數據"""def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,ylim=None, xscale='linear', yscale='linear',fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,figsize=(3.5, 2.5)):if legend is None:legend = []d2l.use_svg_display()self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize)if nrows * ncols == 1:self.axes = [self.axes, ]self.config_axes = lambda: d2l.set_axes(self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)self.X, self.Y, self.fmts = None, None, fmtsdef add(self, x, y):if not hasattr(y, "__len__"):y = [y]n = len(y)if not hasattr(x, "__len__"):x = [x] * nif not self.X:self.X = [[] for _ in range(n)]if not self.Y:self.Y = [[] for _ in range(n)]for i, (a, b) in enumerate(zip(x, y)):if a is not None and b is not None:self.X[i].append(a)self.Y[i].append(b)self.axes[0].cla()for x, y, fmt in zip(self.X, self.Y, self.fmts):self.axes[0].plot(x, y, fmt)self.config_axes()display.display(self.fig)display.clear_output(wait=True)# 訓練函數
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):"""訓練模型(定義見第3章)"""animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],legend=['train loss', 'train acc', 'test acc'])for epoch in range(num_epochs):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_metricsassert train_loss < 0.5, train_lossassert train_acc <= 1 and train_acc > 0.7, train_accassert test_acc <= 1 and test_acc > 0.7, test_acc# 小批量隨機梯度下降來優化模型的損失函數
lr = 0.1
def updater(batch_size):return d2l.sgd([W, b], lr, batch_size)# 訓練模型10個迭代周期
num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater) # 調用前面的函數
d2l.plt.show()
?9、對圖像進行分類預測
# 對圖像進行分類預測
def predict_ch3(net, test_iter, n=6):"""預測標簽(定義見第3章)"""for X, y in test_iter:breaktrues = 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)
d2l.plt.show()
完整代碼:
import torch
from IPython import display
from d2l import torch as d2l# softmax回歸的從零開始實現
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) # 每次讀取256張圖片,返回訓練集、測試集的迭代器:train_iter, test_iter# 將展平每個圖像1x28x28=784,將它們視為長度為784的向量(因為對于softmax回歸來講,輸入需要是一個向量)。因為我們的數據集有10個類別,所以網絡輸出維度為10
num_inputs = 784
num_outputs = 10
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True) # 均值為0,方差為0.01初始化W權重
b = torch.zeros(num_outputs, requires_grad=True) # 全0初始化# 實現softmax
def softmax(X):X_exp = torch.exp(X) # 對每一個元素做指數運算partition = X_exp.sum(1, keepdim=True) # 按照維度為1來求和,對每一行求和;按行求和并保持維度不變,還是2維矩陣return X_exp / partition # 這里應用了廣播機制,# 實現softmax回歸模型
def net(X):return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b) # 對X reshape使其能夠與W做內積# 實現交叉熵損失函數
def cross_entropy(y_hat, y):return - torch.log(y_hat[range(len(y_hat)), y])# 將預測類別與真實y元素進行比較
def accuracy(y_hat, y):"""計算預測正確的數量"""if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:y_hat = y_hat.argmax(axis=1) # argmax返回每行最大值的索引cmp = y_hat.type(y.dtype) == y # cmp為01向量,表示y_hat和y相同索引的個數return float(cmp.type(y.dtype).sum()) # 返回cmp中所有相同索引的和# 我們可以評估在任意模型net的精度
def evaluate_accuracy(net, data_iter):"""計算在指定數據集上模型的精度"""if isinstance(net, torch.nn.Module):net.eval() # eval()停用dropout和batchnormmetric = Accumulator(2) # 創建兩個元素的列表with torch.no_grad():for X, y in data_iter:metric.add(accuracy(net(X), y), y.numel()) # 累加獲得預測正確的個數和總個數return metric[0] / metric[1]# Accumulator實例中創建了2個變量, 分別用于存儲正確預測的數量和預測的總數量
class Accumulator:"""在n個變量上累加"""def __init__(self, n):self.data = [0.0] * ndef add(self, *args):self.data = [a + float(b) for a, b in zip(self.data, args)] # 對數據累加def reset(self):self.data = [0.0] * len(self.data)def __getitem__(self, idx):return self.data[idx]# Softmax回歸的訓練
def train_epoch_ch3(net, train_iter, loss, updater):"""訓練模型一個迭代周期(定義見第3章)"""if isinstance(net, torch.nn.Module): # 判斷是否自己實現函數net.train()metric = Accumulator(3)for X, y in train_iter:y_hat = net(X)l = loss(y_hat, y)if isinstance(updater, torch.optim.Optimizer):updater.zero_grad()l.sum().backward()updater.step()else:l.sum().backward()updater(X.shape[0])metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())return metric[0] / metric[2], metric[1] / metric[2]# 定義一個在動畫中繪制數據的實用程序類
class Animator:"""在動畫中繪制數據"""def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,ylim=None, xscale='linear', yscale='linear',fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,figsize=(3.5, 2.5)):if legend is None:legend = []d2l.use_svg_display()self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize)if nrows * ncols == 1:self.axes = [self.axes, ]self.config_axes = lambda: d2l.set_axes(self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)self.X, self.Y, self.fmts = None, None, fmtsdef add(self, x, y):if not hasattr(y, "__len__"):y = [y]n = len(y)if not hasattr(x, "__len__"):x = [x] * nif not self.X:self.X = [[] for _ in range(n)]if not self.Y:self.Y = [[] for _ in range(n)]for i, (a, b) in enumerate(zip(x, y)):if a is not None and b is not None:self.X[i].append(a)self.Y[i].append(b)self.axes[0].cla()for x, y, fmt in zip(self.X, self.Y, self.fmts):self.axes[0].plot(x, y, fmt)self.config_axes()display.display(self.fig)display.clear_output(wait=True)# 訓練函數
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):"""訓練模型(定義見第3章)"""animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],legend=['train loss', 'train acc', 'test acc'])for epoch in range(num_epochs):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_metricsassert train_loss < 0.5, train_lossassert train_acc <= 1 and train_acc > 0.7, train_accassert test_acc <= 1 and test_acc > 0.7, test_acc# 小批量隨機梯度下降來優化模型的損失函數
lr = 0.1
def updater(batch_size):return d2l.sgd([W, b], lr, batch_size)# 訓練模型10個迭代周期
num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater) # 調用前面的函數
d2l.plt.show()# 對圖像進行分類預測
def predict_ch3(net, test_iter, n=6):"""預測標簽(定義見第3章)"""for X, y in test_iter:breaktrues = 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)
d2l.plt.show()
五、簡潔實現?
1、導入庫
import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
from torch import nn
2、?人造數據集,使用線性模型參數 w = [2, -3.4]T、b = 4.2;得到features, labels
# 1、人造數據集,使用線性模型參數 w = [2, -3.4]T、b = 4.2;得到features, labels
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)def load_array(data_arrays, batch_size, is_train=True):"""構造一個Pytorch數據迭代"""dataset = data.TensorDataset(*data_arrays) # 得到數據集,*表示接受任意多個參數并將其放在一個元組中,拆包return data.DataLoader(dataset, batch_size, shuffle=is_train) # 加載數據集,shuffle表示是否隨機打亂batch_size = 10
data_iter = load_array(data_arrays=(features, labels), batch_size=batch_size) # 把features, labels做成一個list傳入到data.TensorDataset,得到數據集datasetprint(next(iter(data_iter)))
3、?模型定義;'nn'是神經網絡的縮寫
# 2、模型定義;'nn'是神經網絡的縮寫
# (1)使用框架的預定義好的層
net = nn.Sequential(nn.Linear(2, 1)) # 指定輸入維度為2,輸出維度為1# (2)初始化模型參數
net[0].weight.data.normal_(0, 0.01) # 就是對w初始化化為均值為0,方差為0.01的正態分布
net[0].bias.data.fill_(0) # 就是對b初始化為0
4、?計算均方誤差使用的是MSELoss類,也稱為 平方范數
# 3、計算均方誤差使用的是MSELoss類,也稱為 平方范數
loss = nn.MSELoss()
5、?實例化SGD實例,優化器
# 4、實例化SGD實例,優化器
trainer = torch.optim.SGD(net.parameters(), lr=0.03) # 傳入參數、學習率
6、?訓練過程
# 5、訓練過程
# (1)超參數設置
num_epochs = 3 # 整個數據掃三遍
# (2)訓練的實現大同小異,一般就是兩層for循環:第一層是每一次對數據掃一遍;第二層是對于每一次拿出一個批量大小的X、y
for epoch in range(num_epochs):for X, y in data_iter:l = loss(net(X), y) # 把 X 放到模型里面做預測(net本身自己帶了模型參數,所以不需要w、b再傳入了);把 預測的y 與 真實的y 做損失;得到的損失就是 一個批量大小的向量trainer.zero_grad() # 優化器梯度清0l.backward() # 求梯度,此處不用求sum,因為已經自動求完sum了trainer.step() # 調用step()函數,進行一次模型參數的更新# 對數據掃完一邊之后,評價一下進度,此時是不需要梯度的,l = loss(net(features), labels)print(f"epoch {epoch + 1}, loss {l:f}") # 打印評估的結果
完整代碼:
import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
from torch import nn# 線性回歸的簡潔實現(使用深度學習框架提供的計算);包括數據流水線、模型、損失函數和小批量隨機梯度下降優化器
# 1、人造數據集,使用線性模型參數 w = [2, -3.4]T、b = 4.2;得到features, labels
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)def load_array(data_arrays, batch_size, is_train=True):"""構造一個Pytorch數據迭代"""dataset = data.TensorDataset(*data_arrays) # 得到數據集,*表示接受任意多個參數并將其放在一個元組中,拆包return data.DataLoader(dataset, batch_size, shuffle=is_train) # 加載數據集,shuffle表示是否隨機打亂batch_size = 10
data_iter = load_array(data_arrays=(features, labels), batch_size=batch_size) # 把features, labels做成一個list傳入到data.TensorDataset,得到數據集datasetprint(next(iter(data_iter)))# 2、模型定義;'nn'是神經網絡的縮寫
# (1)使用框架的預定義好的層
net = nn.Sequential(nn.Linear(2, 1)) # 指定輸入維度為2,輸出維度為1# (2)初始化模型參數
net[0].weight.data.normal_(0, 0.01) # 就是對w初始化化為均值為0,方差為0.01的正態分布
net[0].bias.data.fill_(0) # 就是對b初始化為0# 3、計算均方誤差使用的是MSELoss類,也稱為 平方范數
loss = nn.MSELoss()# 4、實例化SGD實例,優化器
trainer = torch.optim.SGD(net.parameters(), lr=0.03) # 傳入參數、學習率# 5、訓練過程
# (1)超參數設置
num_epochs = 3 # 整個數據掃三遍
# (2)訓練的實現大同小異,一般就是兩層for循環:第一層是每一次對數據掃一遍;第二層是對于每一次拿出一個批量大小的X、y
for epoch in range(num_epochs):for X, y in data_iter:l = loss(net(X), y) # 把 X 放到模型里面做預測(net本身自己帶了模型參數,所以不需要w、b再傳入了);把 預測的y 與 真實的y 做損失;得到的損失就是 一個批量大小的向量trainer.zero_grad() # 優化器梯度清0l.backward() # 求梯度,此處不用求sum,因為已經自動求完sum了trainer.step() # 調用step()函數,進行一次模型參數的更新# 對數據掃完一邊之后,評價一下進度,此時是不需要梯度的,l = loss(net(features), labels)print(f"epoch {epoch + 1}, loss {l:f}") # 打印評估的結果
如果此文章對您有所幫助,那就請點個贊吧,收藏+關注 那就更棒啦,十分感謝!!!?