PyTorch——開源的Python機(jī)器學(xué)習(xí)庫(kù)
一、前言
??首先感謝所有點(diǎn)開本文的朋友們!基于PyTorch的深度學(xué)習(xí)實(shí)戰(zhàn)可能要告一段落了。本想著再寫幾篇關(guān)于PyTorch神經(jīng)網(wǎng)絡(luò)深度學(xué)習(xí)的文章來(lái)著,可無(wú)奈項(xiàng)目時(shí)間緊任務(wù)重,要求短時(shí)間內(nèi)出圖并做好參數(shù)擬合。所以只得轉(zhuǎn)戰(zhàn)Matlab編程,框架舊就舊吧,無(wú)所謂了 (能出結(jié)果就行) 。
??往期回顧:
[深度學(xué)習(xí)實(shí)戰(zhàn)]基于PyTorch的深度學(xué)習(xí)實(shí)戰(zhàn)(上)[變量、求導(dǎo)、損失函數(shù)、優(yōu)化器]
[深度學(xué)習(xí)實(shí)戰(zhàn)]基于PyTorch的深度學(xué)習(xí)實(shí)戰(zhàn)(中)[線性回歸、numpy矩陣的保存、模型的保存和導(dǎo)入、卷積層、池化層]
二、Mnist手寫數(shù)字圖像識(shí)別
??一個(gè)典型的神經(jīng)網(wǎng)絡(luò)的訓(xùn)練過程大致分為以下幾個(gè)步驟:
??(1)首先定義神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu),并且定義各層的權(quán)重參數(shù)的規(guī)模和初始值。
??(2)然后將輸入數(shù)據(jù)分成多個(gè)批次輸入神經(jīng)網(wǎng)絡(luò)。
??(3)將輸入數(shù)據(jù)通過整個(gè)網(wǎng)絡(luò)進(jìn)行計(jì)算。
??(4)每次迭代根據(jù)計(jì)算結(jié)果和真實(shí)結(jié)果的差值計(jì)算損失。
??(5)根據(jù)損失對(duì)權(quán)重參數(shù)進(jìn)行反向求導(dǎo)傳播。
??(6)更新權(quán)重值,更新過程使用下面的公式:
??weight = weight + learning_rate * gradient。其中 weight 是上一次的權(quán)重值,learning_rate 是學(xué)習(xí)步長(zhǎng),gradient 是求導(dǎo)值。
??下面我們還是以經(jīng)典的圖像識(shí)別卷積網(wǎng)絡(luò)作為例子來(lái)學(xué)習(xí) pytorch 的用法。選擇一個(gè)較小的數(shù)據(jù)集進(jìn)行訓(xùn)練,這里我們選擇 mnist 手寫識(shí)別數(shù)據(jù)集,該數(shù)據(jù)集是 0-9 個(gè)數(shù)字的訓(xùn)練數(shù)據(jù)和測(cè)試數(shù)據(jù),訓(xùn)練數(shù)據(jù) 60000 張圖片,測(cè)試數(shù)據(jù) 10000 張圖片。每張圖片是 28×28 像素大小的單通道黑白圖片,這樣讀到內(nèi)存數(shù)據(jù)組就是 28 * 28 * 1 大小。這里的 28 * 28 是寬度和高度的像素?cái)?shù)量,1 是圖像通道數(shù)據(jù)(如果是彩色圖像就是 3 道路,內(nèi)存數(shù)據(jù)為 28 * 28 * 3 大?。?。
??我們先看看樣本圖像的模樣:
??好,下一步我們構(gòu)建一個(gè)簡(jiǎn)單的卷積神經(jīng)網(wǎng)絡(luò)模型,這個(gè)模型的輸入是圖片,輸出是 0-9 的數(shù)字,這是一個(gè)典型的監(jiān)督式分類神經(jīng)網(wǎng)絡(luò)。
2.1 加載數(shù)據(jù)
??這里我們不打算下載原始的圖像文件然后通過 opencv 等圖像庫(kù)讀取到數(shù)組,而是直接下載中間數(shù)據(jù)。當(dāng)然讀者也可以下載原始圖像文件從頭開始裝載數(shù)據(jù),這樣對(duì)整個(gè)模型會(huì)有更深刻的體會(huì)。
??我們用 mnist 數(shù)據(jù)集作例子,下載方法有兩種:
2.1.1 下載地址
??http://yann.lecun.com/exdb/mnist/。一共四個(gè)文件:
??train-images-idx3-ubyte.gz: training set images (9912422 bytes)
??train-labels-idx1-ubyte.gz: training set labels (28881 bytes)
??t10k-images-idx3-ubyte.gz: test set images (1648877 bytes)
??t10k-labels-idx1-ubyte.gz: test set labels (4542 bytes)
??這些文件中是已經(jīng)處理過的數(shù)組數(shù)據(jù),通過numpy的相關(guān)方法讀取到訓(xùn)練數(shù)據(jù)集和測(cè)試數(shù)據(jù)集數(shù)組中。注意調(diào)用 load_mnist 方法之前先解壓上述四個(gè)文件。
from __future__ import print_function
import os
import struct
import numpy as np
def load_mnist(path, kind='train'):
labels_path = os.path.join(path, '%s-labels.idx1-ubyte' % kind)
images_path = os.path.join(path, '%s-images.idx3-ubyte' % kind)
with open(labels_path, 'rb') as lbpath:
magic, n = struct.unpack('>II', lbpath.read(8))
labels = np.fromfile(lbpath, dtype=np.uint8)
with open(images_path, 'rb') as imgpath:
magic, num, rows, cols = struct.unpack(">IIII", imgpath.read(16))
images = np.fromfile(imgpath, dtype=np.uint8).reshape(len(labels), 784)
return images, labels
X_train, y_train = load_mnist('./data', kind='train')
print('Rows: %d, columns: %d' % (X_train.shape[0], X_train.shape[1]))
X_test, y_test = load_mnist('./data', kind='t10k')
print('Rows: %d, columns: %d' % (X_test.shape[0], X_test.shape[1]))
??顯示結(jié)果:
D:\AI>python mnist_cnn.py
Rows: 60000, columns: 784
Rows: 10000, columns: 784
??我們看到訓(xùn)練數(shù)據(jù)的行數(shù)是 60000,表示 60000 張圖片,列是 784,表示 28281=784 個(gè)像素,測(cè)試數(shù)據(jù)是 10000 張圖片,在后面構(gòu)建卷積模型時(shí),要先將 784 個(gè)像素的列 reshape 成 28281 維度。
??例如:
image1 = X_train[1]
image1 = image1.astype('float32')
image1 = image1.reshape(28,28,1)
??我們還可以將這些圖像數(shù)組導(dǎo)出為 jpg 文件,比如下面的代碼:
import cv2
cv2.imwrite('1.jpg',image1)
??當(dāng)前目錄下的 1.jpg 文件都是我們導(dǎo)出的圖像文件了。
??圖像的顯示:
import cv2
import numpy as np
img = cv2.imread("C:\lena.jpg")
cv2.imshow("lena",img)
cv2.waitKey(10000)
2.1.2 用 numpy 讀取 mnist.npz
??可以直接從亞馬遜下載文件:
??https://s3.amazonaws.com/imgdatasets/mnist.npz
??Mnist.npz 是一個(gè) numpy 數(shù)組文件。如果下載的是 mnist.npz.gz 文件,則用 gunzip mnist.npz.gz 先解壓成 mnist.npz,然后再處理。也可以調(diào)用 keras 的方法來(lái)下載:
from keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
??通過 mnist.load_data() 下載的 mnist.npz 會(huì)放在當(dāng)前用戶的 .keras 目錄中。路徑名稱:
??~/.keras/datasets/mnist.npz
??然后調(diào)用 numpy 來(lái)加載 mnist.npz。
??示例代碼:
import numpy as np
class mnist_data(object):
def load_npz(self,path):
f = np.load(path)
for i in f:
print i
x_train = f['trainInps']
y_train = f['trainTargs']
x_test = f['testInps']
y_test = f['testTargs']
f.close()
return (x_train, y_train), (x_test, y_test)
a = mnist_data()
(x_train, y_train), (x_test, y_test) = a.load_npz('D:/AI/torch/data/mnist.npz')
print ("train rows:%d,test rows:%d"% (x_train.shape[0], x_test.shape[0]))
print("x_train shape",x_train.shape)
print("y_train shape",y_train.shape )
??結(jié)果:
??一共 60000 張訓(xùn)練圖片,10000 張測(cè)試圖片。訓(xùn)練圖像的大小 784 個(gè)像素,也就是 1 通道的 28*28 的手寫圖片。標(biāo)簽是長(zhǎng)度為 10 的 (0,1) 向量,表示 0-9 是個(gè)數(shù)字。例如數(shù)字 1 就表示為 [0,1,0,0,0,0,0,0,0,0]。
??讓我們打印出第一張圖片的截圖和標(biāo)簽。代碼如下:
tt=x_train[1]
tt=tt.astype('float32')
image = tt.reshape(28,28,1)
cv2.imwrite("001.png",image)
print("tt label:",y_train[1])
??標(biāo)簽為 [0,0,0,1,0,0,0,0,0,0]。
2.2 定義卷積模型
??這一步我們定義自己的卷積模型,對(duì)于 28 * 28 的數(shù)組,我們定義 (5,5) 的卷積核大小比較合適,卷積核的大小可根據(jù)圖像大小靈活設(shè)置,比如 (3,3),(5,5),(9,9)等。一般卷積核的大小是奇數(shù)。
??輸入數(shù)據(jù)首先連接 1 個(gè)卷積層加 1 個(gè)池化層,conv1 和 pool1,假設(shè)我們定義 conv1 的神經(jīng)元數(shù)目為 10,卷積核大小 (5,5),定義 pool1 的大小 (2,2),意思將 2 * 2區(qū)域共 4 個(gè)像素統(tǒng)計(jì)為 1 個(gè)像素,這樣這層數(shù)據(jù)量減少 4 倍。這時(shí)候輸出圖像大小為 (28-5+1)/2=12。輸出數(shù)據(jù)維度(10,12,12)。
??接著再來(lái)一次卷積池化,連接 1 個(gè)卷積層加 1 個(gè)池化層,conv2 和 pool2,假設(shè)我們定義 conv2 的神經(jīng)元數(shù)目為 20,卷積核大小 (5,5),定義pool2的大小 (2,2),意思將 2*2 區(qū)域共 4 個(gè)像素統(tǒng)計(jì)為 1 個(gè)像素,這樣這層數(shù)據(jù)量減少 4 倍。這時(shí)候輸出圖像大小為 (12-5+1)/2=4。輸出數(shù)據(jù)維度 (20,4,4)。
??然后接一個(gè) dropout 層,設(shè)置 dropout=0.2,隨機(jī)拋棄部分?jǐn)?shù)據(jù)。
??最后連續(xù)接兩個(gè)全連接層,第一個(gè)全連接層 dense1 輸入維度 20 * 4 * 4,輸出 320,第二個(gè)全連接層 dense1 輸入維度 60,輸出 10。
??模型定義的 pytorch 代碼如下:
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
self.conv2_drop = nn.Dropout2d()
self.fc1 = nn.Linear(320, 60)
self.fc2 = nn.Linear(60, 10)
def forward(self, x):
x = F.relu(F.max_pool2d(self.conv1(x), 2))
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
x = x.view(-1, 320)
x = F.relu(self.fc1(x))
x = F.dropout(x, training=self.training)
x = self.fc2(x)
return F.log_softmax(x)
model = Net()
print(model)
??注解:nn.Conv2d(1, 10, kernel_size=5) 是指將 1 通道的圖像數(shù)據(jù)的輸入卷積成 10 個(gè)神經(jīng)元,這 1 個(gè)通道跟 10 個(gè)神經(jīng)元都建立連接,然后神經(jīng)網(wǎng)絡(luò)對(duì)每個(gè)連接計(jì)算出不同的權(quán)重值,某個(gè)神經(jīng)元上的權(quán)重值對(duì)原圖卷積來(lái)提取該神經(jīng)元負(fù)責(zé)的特征。這個(gè)過程是神經(jīng)網(wǎng)絡(luò)自動(dòng)計(jì)算得出的。
??那么 nn.Conv2d(10, 20, kernel_size=5) 是指將 10 通道的圖像數(shù)據(jù)的輸入卷積成 20 個(gè)神經(jīng)元,同樣的,這 10 個(gè)通道會(huì)和 20 個(gè)神經(jīng)元的每一個(gè)建立連接,那么10 個(gè)通道如何卷積到 1 個(gè)通道呢?一般是取 10 個(gè)通道的平均值作為最后的結(jié)果。
??執(zhí)行該腳本,將卷積模型的結(jié)構(gòu)打印出來(lái):
[root@iZ25ix41uc3Z ai]# python mnist_torch.py
Net (
(conv1): Conv2d(1, 10, kernel_size=(5, 5), stride=(1, 1))
(conv2): Conv2d(10, 20, kernel_size=(5, 5), stride=(1, 1))
(conv2_drop): Dropout2d (p=0.5)
(fc1): Linear (320 -> 50)
(fc2): Linear (50 -> 10)
)
2.3 開始訓(xùn)練
??數(shù)據(jù)準(zhǔn)備好,模型建立好,下面根據(jù)神經(jīng)網(wǎng)絡(luò)的三部曲,就是選擇損失函數(shù)和梯度算法對(duì)模型進(jìn)行訓(xùn)練了。
??損失函數(shù)通俗講就是計(jì)算模型的計(jì)算結(jié)果和真實(shí)結(jié)果之間差異性的函數(shù),典型的如距離的平方和再開平方,對(duì)于圖像分類來(lái)說,我們?nèi)〉膿p失函數(shù)是:
F.log_softmax(x)
??在神經(jīng)網(wǎng)絡(luò)的訓(xùn)練過程中,我們使用 loss.backward() 來(lái)反向傳遞修正,反向修正就是根據(jù)計(jì)算和真實(shí)結(jié)果的差值(就是損失)來(lái)反向逆?zhèn)鞑バ拚鲗拥臋?quán)重參數(shù),修正之后的結(jié)果保存在 .grad 中,因此每輪迭代執(zhí)行 loss.backward() 的時(shí)候要先對(duì) .grad 清零。
model.zero_grad()
print('conv1.bias.grad before backward')
print(model.conv1.bias.grad)
loss.backward()
print('conv1.bias.grad after backward')
print(model.conv1.bias.grad)
??優(yōu)化算法一般是指迭代更新權(quán)重參數(shù)的梯度算法,這里我們選擇隨機(jī)梯度算法 SGD。
??SGD 的算法如下:
weight = weight - learning_rate * gradient
??可以用一段簡(jiǎn)單的 python 腳本模擬這個(gè) SGD 的過程:
learning_rate = 0.01
for f in model.parameters():
f.data.sub_(f.grad.data * learning_rate)
??pytorch 中含有其他的優(yōu)化算法,如 Nesterov-SGD, Adam, RMSProp 等。
??它們的用法和 SGD 基本類似,這里就不一一介紹了。
??訓(xùn)練代碼如下:
import torch.optim as optim
input = Variable(torch.randn(1, 1, 32, 32))
out = model(input)
print(out)
# create your optimizer
optimizer = optim.SGD(model.parameters(), lr=0.01)
# in your training loop:
optimizer.zero_grad() # zero the gradient buffers
output = model(input)
loss = criterion(output, target)
loss.backward()
optimizer.step() # Does the update
2.4 完整代碼
??最后的代碼如下所示,這里我們做了一點(diǎn)修改,每次迭代完會(huì)將模型參數(shù)保存到文件,下次再次執(zhí)行腳本會(huì)自動(dòng)加載上次迭代后的數(shù)據(jù)。整個(gè)完整的代碼如下:
'''
Trains a simple convnet on the MNIST dataset.
'''
from __future__ import print_function
import os
import struct
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable
def load_mnist(path, kind='train'):
"""Load MNIST data from `path`"""
labels_path = os.path.join(path, '%s-labels.idx1-ubyte' % kind)
images_path = os.path.join(path, '%s-images.idx3-ubyte' % kind)
with open(labels_path, 'rb') as lbpath:
magic, n = struct.unpack('>II', lbpath.read(8))
labels = np.fromfile(lbpath, dtype=np.uint8)
with open(images_path, 'rb') as imgpath:
magic, num, rows, cols = struct.unpack(">IIII", imgpath.read(16))
images = np.fromfile(imgpath, dtype=np.uint8).reshape(len(labels), 784)
return images, labels
X_train, y_train = load_mnist('./data', kind='train')
print("shape:",X_train.shape)
print('Rows: %d, columns: %d' % (X_train.shape[0], X_train.shape[1]))
X_test, y_test = load_mnist('./data', kind='t10k')
print('Rows: %d, columns: %d' % (X_test.shape[0], X_test.shape[1]))
batch_size = 100
num_classes = 10
epochs = 2
# input image dimensions
img_rows, img_cols = 28, 28
x_train= X_train
x_test=X_test
if 'channels_first' == 'channels_first':
x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
input_shape = (1, img_rows, img_cols)
else:
x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
num_samples=x_train.shape[0]
print("num_samples:",num_samples)
'''
build torch model
'''
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
self.conv2_drop = nn.Dropout2d()
self.fc1 = nn.Linear(320, 50)
self.fc2 = nn.Linear(50, 10)
def forward(self, x):
x = F.relu(F.max_pool2d(self.conv1(x), 2))
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
x = x.view(-1, 320)
x = F.relu(self.fc1(x))
x = F.dropout(x, training=self.training)
x = self.fc2(x)
return F.log_softmax(x)
model = Net()
if os.path.exists('mnist_torch.pkl'):
model = torch.load('mnist_torch.pkl')
print(model)
'''
trainning
'''
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
#loss=torch.nn.CrossEntropyLoss(size_average=True)
def train(epoch,x_train,y_train):
num_batchs = num_samples/ batch_size
model.train()
for k in range(num_batchs):
start,end = k*batch_size,(k+1)*batch_size
data, target = Variable(x_train[start:end],requires_grad=False), Variable(y_train[optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if k % 10 == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, k * len(data), num_samples,
100. * k / num_samples, loss.data[0]))
torch.save(model, 'mnist_torch.pkl')
'''
evaludate
'''
def test(epoch):
model.eval()
test_loss = 0
correct = 0
if 2>1:
data, target = Variable(x_test, volatile=True), Variable(y_test)
output = model(data)
test_loss += F.nll_loss(output, target).data[0]
pred = output.data.max(1)[1] # get the index of the max log-probability
correct += pred.eq(target.data).cpu().sum()
test_loss = test_loss
test_loss /= len(x_test) # loss function already averages over batch size
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(x_test),
100. * correct / len(x_test)))
x_train=torch.from_numpy(x_train).float()
x_test=torch.from_numpy(x_test).float()
y_train=torch.from_numpy(y_train).long()
y_test=torch.from_numpy(y_test).long()
for epoch in range(1,epochs):
train(epoch,x_train,y_train)
test(epoch)
2.5 驗(yàn)證結(jié)果
??2 次迭代后:Test set: Average loss: 0.3419, Accuracy: 9140/10000 (91%)
??3 次迭代后:Test set: Average loss: 0.2362, Accuracy: 9379/10000 (94%)
??4 次迭代后:Test set: Average loss: 0.2210, Accuracy: 9460/10000 (95%)
??5 次迭代后:Test set: Average loss: 0.1789, Accuracy: 9532/10000 (95%)
??順便說一句,pytorch 的速度比 keras 確實(shí)要快很多,每次迭代幾乎 1 分鐘內(nèi)就完成了,確實(shí)很不錯(cuò)!
2.6 修改參數(shù)
??我們把卷積層的神經(jīng)元個(gè)數(shù)重新設(shè)置一下,第一層卷積加到 32 個(gè),第二層卷積神經(jīng)元加到 64 個(gè)。則新的模型為下面的組織:
{
(conv1): Conv2d(1, 32, kernel_size=(5, 5), stride=(1, 1))
(conv2): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1))
(conv2_drop): Dropout2d (p=0.5)
(fc1): Linear (1024 -> 100)
(fc2): Linear (100 -> 10)
)
??然后同樣的步驟我們跑一遍看看結(jié)果如何,這次明顯慢了很多,但看的出來(lái)準(zhǔn)確度也提高了一些:
??第 1 次迭代:Test set: Average loss: 0.3159, Accuracy: 9038/10000 (90%)
??第 2 次迭代:Test set: Average loss: 0.1742, Accuracy: 9456/10000 (95%)
??第 3 次迭代:Test set: Average loss: 0.1234, Accuracy: 9608/10000 (96%)
??第 4 次迭代:Test set: Average loss: 0.1009, Accuracy: 9694/10000 (97%)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-603063.html
三、后記
??至此,我們已經(jīng)完成了機(jī)器學(xué)習(xí)中的深度學(xué)習(xí)的大部分實(shí)用知識(shí)點(diǎn)。其實(shí),我這里都是演示已經(jīng)跑通的程序,而在修改程序配置環(huán)境等等這些“繞彎路”的經(jīng)歷才更能鍛煉人?;赑yTorch的深度學(xué)習(xí)實(shí)戰(zhàn)還會(huì)有個(gè)補(bǔ)充篇放出,補(bǔ)充RNN和LSTM原理相關(guān),作為《基于PyTorch的深度學(xué)習(xí)實(shí)戰(zhàn)》這個(gè)方向的最后一篇。稍后見!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-603063.html
到了這里,關(guān)于[深度學(xué)習(xí)實(shí)戰(zhàn)]基于PyTorch的深度學(xué)習(xí)實(shí)戰(zhàn)(下)[Mnist手寫數(shù)字圖像識(shí)別]的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!