使用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%
那應該是碰巧了,剛纔碰巧遇見一個好識別的,這次是一個不好識別的
參考: