pytorch搭建神經網絡分類Fashion-MNIST數據集

使用Fashion-MNIST數據集訓練神經網絡對數據集中的圖片進行分類

 

pytorch: 1.4.0

 

       Fashion-MNIST 是一個替代原始的MNIST手寫數字數據集的另一個圖像數據集。 它是由Zalando(一家德國的時尚科技公司)旗下的研究部門提供。其涵蓋了來自10種類別的共7萬個不同商品的正面圖片(口紅、包包、鞋子、褲子、襯衫、T恤、襯衣、靴子)。

       Fashion-MNIST的大小、格式和訓練集/測試集劃分與原始的MNIST完全一致。60000/10000的訓練測試數據劃分,28x28的灰度圖片。你可以直接用它來測試你的機器學習和深度學習算法性能,且不需要改動任何的代碼

 

下載Fasion-MNIST數據集,顯示圖片

import torch  # 導入pytorch
from torch import nn, optim  # 導入神經網絡與優化器對應的類
import torch.nn.functional as F 
from torchvision import datasets, transforms ## 導入數據集與數據預處理的方法
import matplotlib.pyplot as plt

# 數據預處理:標準化圖像數據,使得灰度數據在-1到+1之間
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,), (0.5,))])

#1、下載Fasion-MNIST數據集
# 下載Fashion-MNIST訓練集數據,並構建訓練集數據載入器trainloader,每次從訓練集中載入64張圖片,每次載入都打亂順序
trainset = datasets.FashionMNIST('dataset/', download=True, train=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)

# 下載Fashion-MNIST測試集數據,並構建測試集數據載入器trainloader,每次從測試集中載入64張圖片,每次載入都打亂順序
testset = datasets.FashionMNIST('dataset/', download=True, train=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=True)

#2、打開數據集中的圖片
image, label = next(iter(trainloader))

print(image.shape)  #image包含了64張28 * 28的灰度圖片,1代表單通道,也就是灰度

#我們查看索引爲2的圖片
imagedemo = image[3]
imagedemolabel = label[3]

imagedemo = imagedemo.reshape((28,28))



plt.imshow(imagedemo)
plt.show()

labellist = ['T恤','褲子','套衫','裙子','外套','涼鞋','汗衫','運動鞋','包包','靴子']
print(f'這張圖片對應的標籤是 {labellist[imagedemolabel]}')

image包含了64張28 * 28的灰度圖片,1代表單通道,也就是灰度

 

label包含了image裏面64張圖片對應的標籤

 

搭建並訓練四層全連接神經網絡

神經網絡的輸入爲28 * 28 = 784 個像素

      第一個隱含層包含256個神經元

      第二個隱含層包含128個神經元

      第三個隱含層包含64個神經元

輸出層輸出10個結果,對應圖片的10種分類

輸出10個分類結果,就對應着剛纔10種image的標籤

 

相加多少個隱含層,每一層還有多少個神經元,我們都是可以自己調的

通過pytorch的接口,非常簡單

最後通過softmax函數來進行十個種類的分類

import torch  # 導入pytorch
from torch import nn, optim  # 導入神經網絡與優化器對應的類
import torch.nn.functional as F 
from torchvision import datasets, transforms ## 導入數據集與數據預處理的方法
import matplotlib.pyplot as plt

# 數據預處理:標準化圖像數據,使得灰度數據在-1到+1之間
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,), (0.5,))])

#------------------1、下載Fasion-MNIST數據集--------------------------------
# 下載Fashion-MNIST訓練集數據,並構建訓練集數據載入器trainloader,每次從訓練集中載入64張圖片,每次載入都打亂順序
trainset = datasets.FashionMNIST('dataset/', download=True, train=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)

# 下載Fashion-MNIST測試集數據,並構建測試集數據載入器trainloader,每次從測試集中載入64張圖片,每次載入都打亂順序
testset = datasets.FashionMNIST('dataset/', download=True, train=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=True)

#----------------------------2、打開數據集中的圖片---------------------------------
image, label = next(iter(trainloader))

print(image.shape)  #image包含了64張28 * 28的灰度圖片,1代表單通道,也就是灰度

#我們查看索引爲2的圖片
imagedemo = image[3]
imagedemolabel = label[3]

imagedemo = imagedemo.reshape((28,28))



plt.imshow(imagedemo)
plt.show()

labellist = ['T恤','褲子','套衫','裙子','外套','涼鞋','汗衫','運動鞋','包包','靴子']
print(f'這張圖片對應的標籤是 {labellist[imagedemolabel]}')

#------------------3、搭建並訓練四層全連接神經網絡---------------------------------

class Classifier(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(784, 256)
        self.fc2 = nn.Linear(256, 128)
        self.fc3 = nn.Linear(128, 64)
        self.fc4 = nn.Linear(64, 10)
        
    def forward(self, x):
        # make sure input tensor is flattened
        x = x.view(x.shape[0], -1)
        
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = F.relu(self.fc3(x))
        x = F.log_softmax(self.fc4(x), dim=1)
        
        return x


# 對上面定義的Classifier類進行實例化
model = Classifier()

# 定義損失函數爲負對數損失函數
criterion = nn.NLLLoss()

# 優化方法爲Adam梯度下降方法,學習率爲0.003
optimizer = optim.Adam(model.parameters(), lr=0.003)

# 對訓練集的全部數據學習15遍,這個數字越大,訓練時間越長
epochs = 15

# 將每次訓練的訓練誤差和測試誤差存儲在這兩個列表裏,後面繪製誤差變化折線圖用
train_losses, test_losses = [], []

print('開始訓練')
for e in range(epochs):
    running_loss = 0
    
    # 對訓練集中的所有圖片都過一遍
    for images, labels in trainloader:
        # 將優化器中的求導結果都設爲0,否則會在每次反向傳播之後疊加之前的
        optimizer.zero_grad()
        
        # 對64張圖片進行推斷,計算損失函數,反向傳播優化權重,將損失求和
        log_ps = model(images)
        loss = criterion(log_ps, labels)
        loss.backward()
        optimizer.step()
        running_loss += loss.item()
    
    # 每次學完一遍數據集,都進行以下測試操作
    else:
        test_loss = 0
        accuracy = 0
        # 測試的時候不需要開自動求導和反向傳播
        with torch.no_grad():
            # 關閉Dropout
            model.eval()
            
            # 對測試集中的所有圖片都過一遍
            for images, labels in testloader:
                # 對傳入的測試集圖片進行正向推斷、計算損失,accuracy爲測試集一萬張圖片中模型預測正確率
                log_ps = model(images)
                test_loss += criterion(log_ps, labels)
                ps = torch.exp(log_ps)
                top_p, top_class = ps.topk(1, dim=1)
                equals = top_class == labels.view(*top_class.shape)
                
                # 等號右邊爲每一批64張測試圖片中預測正確的佔比
                accuracy += torch.mean(equals.type(torch.FloatTensor))
        # 恢復Dropout
        model.train()
        # 將訓練誤差和測試誤差存在兩個列表裏,後面繪製誤差變化折線圖用
        train_losses.append(running_loss/len(trainloader))
        test_losses.append(test_loss/len(testloader))

        print("訓練集學習次數: {}/{}.. ".format(e+1, epochs),
              "訓練誤差: {:.3f}.. ".format(running_loss/len(trainloader)),
              "測試誤差: {:.3f}.. ".format(test_loss/len(testloader)),
              "模型分類準確率: {:.3f}".format(accuracy/len(testloader)))

 

驗證模型效果

import torch  # 導入pytorch
from torch import nn, optim  # 導入神經網絡與優化器對應的類
import torch.nn.functional as F 
from torchvision import datasets, transforms ## 導入數據集與數據預處理的方法
import matplotlib.pyplot as plt

# 數據預處理:標準化圖像數據,使得灰度數據在-1到+1之間
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,), (0.5,))])

#------------------1、下載Fasion-MNIST數據集--------------------------------
# 下載Fashion-MNIST訓練集數據,並構建訓練集數據載入器trainloader,每次從訓練集中載入64張圖片,每次載入都打亂順序
trainset = datasets.FashionMNIST('dataset/', download=True, train=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)

# 下載Fashion-MNIST測試集數據,並構建測試集數據載入器trainloader,每次從測試集中載入64張圖片,每次載入都打亂順序
testset = datasets.FashionMNIST('dataset/', download=True, train=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=True)

#----------------------------2、打開數據集中的圖片---------------------------------
image, label = next(iter(trainloader))

print(image.shape)  #image包含了64張28 * 28的灰度圖片,1代表單通道,也就是灰度

#我們查看索引爲2的圖片
imagedemo = image[3]
imagedemolabel = label[3]

imagedemo = imagedemo.reshape((28,28))



plt.imshow(imagedemo)
plt.show()

labellist = ['T恤','褲子','套衫','裙子','外套','涼鞋','汗衫','運動鞋','包包','靴子']
print(f'這張圖片對應的標籤是 {labellist[imagedemolabel]}')

#------------------3、搭建並訓練四層全連接神經網絡---------------------------------

class Classifier(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(784, 256)
        self.fc2 = nn.Linear(256, 128)
        self.fc3 = nn.Linear(128, 64)
        self.fc4 = nn.Linear(64, 10)
        
    def forward(self, x):
        # make sure input tensor is flattened
        x = x.view(x.shape[0], -1)
        
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = F.relu(self.fc3(x))
        x = F.log_softmax(self.fc4(x), dim=1)
        
        return x


# 對上面定義的Classifier類進行實例化
model = Classifier()

# 定義損失函數爲負對數損失函數
criterion = nn.NLLLoss()

# 優化方法爲Adam梯度下降方法,學習率爲0.003
optimizer = optim.Adam(model.parameters(), lr=0.003)

# 對訓練集的全部數據學習15遍,這個數字越大,訓練時間越長
epochs = 15

# 將每次訓練的訓練誤差和測試誤差存儲在這兩個列表裏,後面繪製誤差變化折線圖用
train_losses, test_losses = [], []

print('開始訓練')
for e in range(epochs):
    running_loss = 0
    
    # 對訓練集中的所有圖片都過一遍
    for images, labels in trainloader:
        # 將優化器中的求導結果都設爲0,否則會在每次反向傳播之後疊加之前的
        optimizer.zero_grad()
        
        # 對64張圖片進行推斷,計算損失函數,反向傳播優化權重,將損失求和
        log_ps = model(images)
        loss = criterion(log_ps, labels)
        loss.backward()
        optimizer.step()
        running_loss += loss.item()
    
    # 每次學完一遍數據集,都進行以下測試操作
    else:
        test_loss = 0
        accuracy = 0
        # 測試的時候不需要開自動求導和反向傳播
        with torch.no_grad():
            # 關閉Dropout
            model.eval()
            
            # 對測試集中的所有圖片都過一遍
            for images, labels in testloader:
                # 對傳入的測試集圖片進行正向推斷、計算損失,accuracy爲測試集一萬張圖片中模型預測正確率
                log_ps = model(images)
                test_loss += criterion(log_ps, labels)
                ps = torch.exp(log_ps)
                top_p, top_class = ps.topk(1, dim=1)
                equals = top_class == labels.view(*top_class.shape)
                
                # 等號右邊爲每一批64張測試圖片中預測正確的佔比
                accuracy += torch.mean(equals.type(torch.FloatTensor))
        # 恢復Dropout
        model.train()
        # 將訓練誤差和測試誤差存在兩個列表裏,後面繪製誤差變化折線圖用
        train_losses.append(running_loss/len(trainloader))
        test_losses.append(test_loss/len(testloader))

        print("訓練集學習次數: {}/{}.. ".format(e+1, epochs),
              "訓練誤差: {:.3f}.. ".format(running_loss/len(trainloader)),
              "測試誤差: {:.3f}.. ".format(test_loss/len(testloader)),
              "模型分類準確率: {:.3f}".format(accuracy/len(testloader)))

#繪製訓練誤差和測試誤差隨學習次數增加的變化圖
plt.plot(train_losses, label='Training loss')
plt.plot(test_losses, label='Validation loss')
plt.legend()
plt.show()


dataiter = iter(testloader)
images, labels = dataiter.next()
img = images[0]
img = img.reshape((28,28)).numpy()
plt.imshow(img)

# 將測試圖片轉爲一維的列向量
img = torch.from_numpy(img)
img = img.view(1, 784)

# 進行正向推斷,預測圖片所在的類別
with torch.no_grad():
    output = model.forward(img)
ps = torch.exp(output)

top_p, top_class = ps.topk(1, dim=1)
labellist = ['T恤','褲子','套衫','裙子','外套','涼鞋','汗衫','運動鞋','包包','靴子']
prediction = labellist[top_class]
probability = float(top_p)
print(f'神經網絡猜測圖片裏是 {prediction},概率爲{probability*100}%')

      可以看到,雖然訓練誤差一直在下降,但測試誤差居高不下,這就出現了過擬合現象,我們的神經網絡彷彿一個高分低能的同學,平時把所有課後題答案都死記硬背下來,一到考試見到新題的時候就不會做了。

     雖然高分低能,但大部分時候依舊能做出正確判斷,但是有時候預測概率只有百分之三四十的把握,不能做到十有八九的確定

 

採用Dropout方法防止過擬合

       我們可以採用Dropout的方法,也就是在每次正向推斷訓練神經元的時候隨機“掐死”一部分神經元,阻斷其輸入輸出,這樣可以起到正則化的作用。

      可以理解爲,皇上雨露均沾,今天受寵,明天可能就被打入冷宮,這樣就防止了楊貴妃那樣的“三千寵愛在一身”,從而防止了某些神經元一家獨大,成爲話題領袖,隻手遮天。

      所有神經元處於平等地位,防止過擬合

 

修改代碼

import torch  # 導入pytorch
from torch import nn, optim  # 導入神經網絡與優化器對應的類
import torch.nn.functional as F 
from torchvision import datasets, transforms ## 導入數據集與數據預處理的方法
import matplotlib.pyplot as plt

# 數據預處理:標準化圖像數據,使得灰度數據在-1到+1之間
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,), (0.5,))])

#------------------1、下載Fasion-MNIST數據集--------------------------------
# 下載Fashion-MNIST訓練集數據,並構建訓練集數據載入器trainloader,每次從訓練集中載入64張圖片,每次載入都打亂順序
trainset = datasets.FashionMNIST('dataset/', download=True, train=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)

# 下載Fashion-MNIST測試集數據,並構建測試集數據載入器trainloader,每次從測試集中載入64張圖片,每次載入都打亂順序
testset = datasets.FashionMNIST('dataset/', download=True, train=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=True)

#----------------------------2、打開數據集中的圖片---------------------------------
image, label = next(iter(trainloader))

print(image.shape)  #image包含了64張28 * 28的灰度圖片,1代表單通道,也就是灰度

#我們查看索引爲2的圖片
imagedemo = image[3]
imagedemolabel = label[3]

imagedemo = imagedemo.reshape((28,28))



plt.imshow(imagedemo)
plt.show()

labellist = ['T恤','褲子','套衫','裙子','外套','涼鞋','汗衫','運動鞋','包包','靴子']
print(f'這張圖片對應的標籤是 {labellist[imagedemolabel]}')

#------------------3、搭建並訓練四層全連接神經網絡---------------------------------

class Classifier(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(784, 256)
        self.fc2 = nn.Linear(256, 128)
        self.fc3 = nn.Linear(128, 64)
        self.fc4 = nn.Linear(64, 10)
        
        # 構造Dropout方法,在每次訓練過程中都隨機“掐死”百分之二十的神經元,防止過擬合。
        self.dropout = nn.Dropout(p=0.2)
        
    def forward(self, x):
        # 確保輸入的tensor是展開的單列數據,把每張圖片的通道、長度、寬度三個維度都壓縮爲一列
        x = x.view(x.shape[0], -1)
        
        # 在訓練過程中對隱含層神經元的正向推斷使用Dropout方法
        x = self.dropout(F.relu(self.fc1(x)))
        x = self.dropout(F.relu(self.fc2(x)))
        x = self.dropout(F.relu(self.fc3(x)))
        
        # 在輸出單元不需要使用Dropout方法
        x = F.log_softmax(self.fc4(x), dim=1)
        
        return x


# 對上面定義的Classifier類進行實例化
model = Classifier()

# 定義損失函數爲負對數損失函數
criterion = nn.NLLLoss()

# 優化方法爲Adam梯度下降方法,學習率爲0.003
optimizer = optim.Adam(model.parameters(), lr=0.003)

# 對訓練集的全部數據學習15遍,這個數字越大,訓練時間越長
epochs = 15

# 將每次訓練的訓練誤差和測試誤差存儲在這兩個列表裏,後面繪製誤差變化折線圖用
train_losses, test_losses = [], []

print('開始訓練')
for e in range(epochs):
    running_loss = 0
    
    # 對訓練集中的所有圖片都過一遍
    for images, labels in trainloader:
        # 將優化器中的求導結果都設爲0,否則會在每次反向傳播之後疊加之前的
        optimizer.zero_grad()
        
        # 對64張圖片進行推斷,計算損失函數,反向傳播優化權重,將損失求和
        log_ps = model(images)
        loss = criterion(log_ps, labels)
        loss.backward()
        optimizer.step()
        running_loss += loss.item()
    
    # 每次學完一遍數據集,都進行以下測試操作
    else:
        test_loss = 0
        accuracy = 0
        # 測試的時候不需要開自動求導和反向傳播
        with torch.no_grad():
            # 關閉Dropout
            model.eval()
            
            # 對測試集中的所有圖片都過一遍
            for images, labels in testloader:
                # 對傳入的測試集圖片進行正向推斷、計算損失,accuracy爲測試集一萬張圖片中模型預測正確率
                log_ps = model(images)
                test_loss += criterion(log_ps, labels)
                ps = torch.exp(log_ps)
                top_p, top_class = ps.topk(1, dim=1)
                equals = top_class == labels.view(*top_class.shape)
                
                # 等號右邊爲每一批64張測試圖片中預測正確的佔比
                accuracy += torch.mean(equals.type(torch.FloatTensor))
        # 恢復Dropout
        model.train()
        # 將訓練誤差和測試誤差存在兩個列表裏,後面繪製誤差變化折線圖用
        train_losses.append(running_loss/len(trainloader))
        test_losses.append(test_loss/len(testloader))

        print("訓練集學習次數: {}/{}.. ".format(e+1, epochs),
              "訓練誤差: {:.3f}.. ".format(running_loss/len(trainloader)),
              "測試誤差: {:.3f}.. ".format(test_loss/len(testloader)),
              "模型分類準確率: {:.3f}".format(accuracy/len(testloader)))


#繪製訓練誤差和測試誤差隨學習次數增加的變化圖
plt.plot(train_losses, label='Training loss')
plt.plot(test_losses, label='Validation loss')
plt.legend()
plt.show()


model.eval()  #不啓用 Dropout
dataiter = iter(testloader)
images, labels = dataiter.next()
img = images[0]
img = img.reshape((28,28)).numpy()
plt.imshow(img)
plt.show()

# 將測試圖片轉爲一維的列向量
img = torch.from_numpy(img)
img = img.view(1, 784)

# 進行正向推斷,預測圖片所在的類別
with torch.no_grad():
    output = model.forward(img)
ps = torch.exp(output)

top_p, top_class = ps.topk(1, dim=1)
labellist = ['T恤','褲子','套衫','裙子','外套','涼鞋','汗衫','運動鞋','包包','靴子']
prediction = labellist[top_class]
probability = float(top_p)
print(f'神經網絡猜測圖片裏是 {prediction},概率爲{probability*100}%')

可以看到,訓練誤差和測試誤差都隨學習次數增加逐漸降低,沒有出現“高分低能”和“死記硬背”的過擬合現象,這其實是Dropout正則化的功勞。

至於爲什麼剛纔沒加dropout優化過擬合是90%多,現在纔是60%

那應該是碰巧了,剛纔碰巧遇見一個好識別的,這次是一個不好識別的

 

參考:

https://www.bilibili.com/video/BV1w4411u7ay

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