【動手學——softmax】day02_Fashion-MNIST數據集&softmax兩種方法實現

softmax迴歸

一、獲取Fashion-MNIST訓練集和讀取數據

我這裏我們會使用torchvision包,它是服務於PyTorch深度學習框架的,主要用來構建計算機視覺模型。
-torchvision主要由以下幾部分構成:
torchvision.datasets: 一些加載數據的函數及常用的數據集接口;
torchvision.models: 包含常用的模型結構(含預訓練模型),例如AlexNet、VGG、ResNet等;
torchvision.transforms: 常用的圖片變換,例如裁剪、旋轉等;
torchvision.utils: 其他的一些有用的方法。

1.import package

# import needed package
%matplotlib inline
from IPython import display
import matplotlib.pyplot as plt

import torch
import torchvision
import torchvision.transforms as transforms
import time

import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l

print(torch.__version__)
print(torchvision.__version__)

2. get dataset

mnist_train = torchvision.datasets.FashionMNIST(root='/home/kesci/input/FashionMNIST2065', train=True, download=True, transform=transforms.ToTensor())
mnist_test = torchvision.datasets.FashionMNIST(root='/home/kesci/input/FashionMNIST2065', train=False, download=True, transform=transforms.ToTensor())

class torchvision.datasets.FashionMNIST(root, train=True, transform=None, target_transform=None, download=False)

root(string)– 數據集的根目錄,其中存放processed/training.pt和processed/test.pt文件。
train(bool, 可選)– 如果設置爲True,從training.pt創建數據集,否則從test.pt創建。
download(bool, 可選)– 如果設置爲True,從互聯網下載數據並放到root文件夾下。如果root目錄下已經存在數據,不會再次下載。
transform(可被調用 , 可選)– 一種函數或變換,輸入PIL圖片,返回變換之後的數據。如:transforms.RandomCrop。
target_transform(可被調用 , 可選)– 一種函數或變換,輸入目標,進行變換。

# show result 
print(type(mnist_train))
print(len(mnist_train), len(mnist_test))

<class ‘torchvision.datasets.mnist.FashionMNIST’>
60000 10000

# 我們可以通過下標來訪問任意一個樣本
feature, label = mnist_train[0]
print(feature.shape, label)  # Channel x Height x Width

如果不做變換輸入的數據是圖像,我們可以看一下圖片的類型參數:

mnist_PIL = torchvision.datasets.FashionMNIST(root='/home/kesci/input/FashionMNIST2065', train=True, download=True)
PIL_feature, label = mnist_PIL[0]
print(PIL_feature)

<PIL.Image.Image image mode=L size=28x28 at 0x7F57E8736F28>

# 本函數已保存在d2lzh包中方便以後使用
#作用:將標籤轉化爲文本的形式
#返回的是標籤所對應的文本信息(文本信息是存儲在text_lables這個列表中的)
def get_fashion_mnist_labels(labels):
    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_fashion_mnist(images, labels):
    d2l.use_svg_display()
    # 這裏的_表示我們忽略(不使用)的變量
    _, figs = plt.subplots(1, len(images), figsize=(12, 12))
    for f, img, lbl in zip(figs, images, labels):
        f.imshow(img.view((28, 28)).numpy())
        f.set_title(lbl)
        f.axes.get_xaxis().set_visible(False)
        f.axes.get_yaxis().set_visible(False)
    plt.show()
X, y = [], []
for i in range(10):
    X.append(mnist_train[i][0]) # 將第i個feature加到X中
    y.append(mnist_train[i][1]) # 將第i個label加到y中
show_fashion_mnist(X, get_fashion_mnist_labels(y))
# 讀取數據
batch_size = 256	#批量大小
num_workers = 4		#工作線程
train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)

看看取數據花了多少時間

start = time.time()
for X, y in train_iter:
    continue
print('%.2f sec' % (time.time() - start))

二、softmax從零實現

import packages

import torch
import torchvision
import numpy as np
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l

print(torch.__version__)
print(torchvision.__version__)

獲取訓練數據集和測試數據集

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
#是對dataloader的一個封裝

模型參數初始化

num_inputs = 784	#輸入特徵是784,即X有28*28個元素
print(28*28)
num_outputs = 10	#輸出是十種類型
#接下來定義權重和偏差
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按維度操作

X = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(X.sum(dim=0, keepdim=True))  # dim爲0,按照相同的列求和,並在結果中保留列特徵
print(X.sum(dim=1, keepdim=True))  # dim爲1,按照相同的行求和,並在結果中保留行特徵
print(X.sum(dim=0, keepdim=False)) # dim爲0,按照相同的列求和,不在結果中保留列特徵
print(X.sum(dim=1, keepdim=False)) # dim爲1,按照相同的行求和,不在結果中保留行特徵

dim = 1時,按行求和
在這裏插入圖片描述

定義softmax操作

在這裏插入圖片描述

def softmax(X):
    X_exp = X.exp()				#指數操作,作分子
    partition = X_exp.sum(dim=1, keepdim=True) 	#指數操作後求和,作分母
    # print("X size is ", X_exp.size())
    # print("partition size is ", partition, partition.size())
    return X_exp / partition  # 這裏應用了廣播機制

註釋掉的兩句顯示X和partition的形狀區別,利用了廣播機制。
結果如下圖

在這裏插入圖片描述

X = torch.rand((2, 5))
X_prob = softmax(X)
print(X_prob, '\n', X_prob.sum(dim=1))

softmax迴歸模型

在這裏插入圖片描述
※下面的參數x是輸入特徵,所以是一個行向量,通過view()函數將其變形成列向量,方便與權重w相乘,再與b相加,傳入softmax函數中,得到輸出y_hat

def net(X):
    return softmax(torch.mm(X.view((-1, num_inputs)), W) + b)

定義損失函數

在這裏插入圖片描述

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))
def cross_entropy(y_hat, y):
    return - torch.log(y_hat.gather(1, y.view(-1, 1)))

定義準確率

我們模型訓練完了進行模型預測的時候,會用到我們這裏定義的準確率。

def accuracy(y_hat, y):
    return (y_hat.argmax(dim=1) == y).float().mean().item()
  	#"y_hat.argmax(dim=1)"按行取y_hat中的最大值與真實標籤y的值作比較,如果相同的話爲1,不同爲0;再相加起來求平均值
print(accuracy(y_hat, y))
# 本函數已保存在d2lzh_pytorch包中方便以後使用。該函數將被逐步改進:它的完整實現將在“圖像增廣”一節中描述
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))

訓練模型

num_epochs, lr = 5, 0.1

# 本函數已保存在d2lzh_pytorch包中方便以後使用
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:
                d2l.sgd(params, lr, batch_size)
            else:
                optimizer.step() 
            
            
            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)

模型預測

現在我們的模型訓練完了,可以進行一下預測,我們的這個模型訓練的到底準確不準確。 現在就可以演示如何對圖像進行分類了。給定一系列圖像(第三行圖像輸出),我們比較一下它們的真實標籤(第一行文本輸出)和模型預測結果(第二行文本輸出)。

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])

softmax的簡潔實現

# 加載各種包或者模塊
import torch
from torch import nn
from torch.nn import init
import numpy as np
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l

print(torch.__version__)

初始化參數和獲取數據

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

定義網絡模型

num_inputs = 784
num_outputs = 10

class LinearNet(nn.Module):
    def __init__(self, num_inputs, num_outputs):
        super(LinearNet, self).__init__()
        #初始化28*28輸入,10個輸出的線性網絡 
        self.linear = nn.Linear(num_inputs, num_outputs)
        
    def forward(self, x): # x 的形狀: (batch, 1, 28, 28)
    	#給y一個輸出的"格式"
        y = self.linear(x.view(x.shape[0], -1))
        return y
    
# net = LinearNet(num_inputs, num_outputs)

class FlattenLayer(nn.Module):
    def __init__(self):
        super(FlattenLayer, self).__init__()
    def forward(self, x): # x 的形狀: (batch, *, *, ...)
    	#將28*28的輸出轉換成784的形式
        return x.view(x.shape[0], -1)

#用nn.Sequential()來初始化網絡
from collections import OrderedDict
net = nn.Sequential(
        # FlattenLayer(),
        # LinearNet(num_inputs, num_outputs) 
        OrderedDict([
           ('flatten', FlattenLayer()),    #變換層
           ('linear', nn.Linear(num_inputs, num_outputs))]) 
#線性層,即softmax層
# 或者寫成我們自己定義的 LinearNet(num_inputs, num_outputs) 也可以
        )

初始化模型參數

用torch的init模塊進行權重w,偏差b的初始化

init.normal_(net.linear.weight, mean=0, std=0.01)
init.constant_(net.linear.bias, val=0)
Parameter containing:
tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], requires_grad=True)

定義損失函數

交叉熵損失函數原型

loss = nn.CrossEntropyLoss() # 下面是他的函數原型
# class torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')

定義優化函數

一樣的隨機梯度下降函數,傳入的是該網絡的參數和超參學習率

optimizer = torch.optim.SGD(net.parameters(), lr=0.1) # 下面是函數原型
# class torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False)

訓練

num_epochs = 5
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, optimizer)

代碼註解

X = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(X.sum(dim=0, keepdim=True))  # dim爲0,按照相同的列求和,並在結果中保留列特徵
print(X.sum(dim=1, keepdim=True))  # dim爲1,按照相同的行求和,並在結果中保留行特徵
print(X.sum(dim=0, keepdim=False)) # dim爲0,按照相同的列求和,不在結果中保留列特徵
print(X.sum(dim=1, keepdim=False)) # dim爲1,按照相同的行求和,不在結果中保留行特徵

輸出:

tensor([[5, 7, 9]])
tensor([[ 6],
[15]])
tensor([5, 7, 9])
tensor([ 6, 15])


錯題:Softmax與分類模型
在這裏插入圖片描述
答:softmax化簡過程中,會減去最大項,避免因運算過大導致上溢出或下溢出,解決辦法可參考筆記https://www.cnblogs.com/guohaoblog/p/12306118.html

拓展:試着比較SVM和softmax的區別和聯繫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章