手寫數字識別
前段時間開始學習pytorch,學習了一點pytorch的小語法,在網上找到了pytorch入門寫CNN的代碼,自己嘗試讀懂加上注釋。更多的了解一下pytorch,代碼注釋寫的還算清楚,在閱讀代碼之前可以看一下我收獲的知識都是在代碼里遇到的不會的語句,我自己通過閱讀別博客獲取的知識,大多數都是torch在讀取數據的操作。先讀一下這個有利於閱讀代碼。
收獲的知識:
1.torch.maual_seed()
在神經網絡中,好比BP神經網絡里面所有的參數都是隨機的,我們使用這樣方式來生成隨機數,這些隨機數是固定的,可以復現的
import torch
torch.manual_seed(1)
print(torch.rand(2))
連續執行兩次:
1.
2.
import torch
#torch.manual_seed(1)
print(torch.rand(2))
2.
2.train_loader = Data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
剛開始不知道什么意思 后來看到是 torch.utils.data的簡稱
在在拜讀了這篇博客了以后
我了解到了 torch.utlis.data.DataLoader是pytorch讀取數據的重要接口,pytorch模型的訓練數據大多會用到這個接口來讀取數據,該接口的源代碼(也來自於我看的那篇博文):
torch.utils.data.DataLoader(
dataset,#數據加載
batch_size = 1,#批處理大小設置
shuffle = False,#是否進項洗牌操作
sampler = None,#指定數據加載中使用的索引/鍵的序列
batch_sampler = None,#和sampler類似
num_workers = 0,#是否進行多進程加載數據設置
collate_fn = None,#是否合並樣本列表以形成一小批Tensor
pin_memory = False,#如果True,數據加載器會在返回之前將Tensors復制到CUDA固定內存
drop_last = False,#True如果數據集大小不能被批處理大小整除,則設置為刪除最后一個不完整的批處理。
timeout = 0,#如果為正,則為從工作人員收集批處理的超時值
worker_init_fn = None )
該接口的目的是將你需要的數據集按照 bitch_size(批處理大小設置),shuffle(是否進行洗牌)把數據封裝成一個Batch Size大小的Tensor,用於后面的訓練。
3.torchvision.datasets.MNIST(root='./mnist/', train=False)
接着我又去百度,拜讀了一篇博客和上面的解讀torch.utils.data.DataLoader的同一個作者,博客地址
閱讀了他的博客我得知了,PyTorch框架中有一個非常重要且好用的包:torchvision,該包主要由3個子包組成,分別是:torchvision.datasets、torchvision.models、torchvision.transforms。torchvision.datasets。torchvision.datasets這個包中包含MNIST、FakeData、COCO、LSUN、ImageFolder、DatasetFolder、ImageNet、CIFAR等一些常用的數據集,並且提供了數據集設置的一些重要參數設置,可以通過簡單數據集設置來進行數據集的調用。
torchvision.datasets.MNIST(root,train = True,transform = None,target_transform = None,download = False )
# 參數介紹:
#root(string) - 數據集的根目錄在哪里MNIST/processed/training.pt 和 MNIST/processed/test.pt存在。
# train(bool,optional) - 如果為True,則創建數據集training.pt,否則創建數據集test.pt。
#download(bool,optional) - 如果為true,則從Internet下載數據集並將其放在根目錄中。如果已下載數據集,則不會再次下載。
# transform(callable ,optional) - 一個函數/轉換,它接收PIL圖像並返回轉換后的版本。例如,transforms.RandomCrop
# target_transform(callable ,optional) - 接收目標並對其進行轉換的函數/轉換。
那么我遇到這個代碼的意思就是,把根目錄下的mnist作為訓練集
4.torch.unsqueeze
根據英文單詞我也判斷出了這個是不壓縮就是 提高維度的操作
有解讀增加維度和壓縮的操作
大概是的意思就是 如果我指定的位置的維度如果為1,我就刪除這個維度,或者在我指定的維度增加一個維度
5.nn.Conv2d(pytorch中的二維卷積)
一般 nn.Conv1d 一維度的卷積用於文本處理,二維 多用於圖像的處理
nn.Conv2d(16, 32, 5, 1, 2),
W2=(28+2X2-5)/1+1=28
H2=(28+2X2-5)/1+1=28
輸出的圖片還是 28X28,
代碼里有些關於卷積的注釋
剛剛開始的我一直理解不了,在這段代碼里面有些地方會返回2個參數、
其實是因為這段代碼里面包含了數據可視化,CNN神經網絡輸出也會輸出兩個數據一個是真實值,一個用於做數據可視化的(現階段我想深入了解pytorch就先不去管數據可視化)
代碼部分():
import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.utils.data as Data
import torchvision
import matplotlib.pyplot as plt
torch.manual_seed(1) # reproducible 可以復現的
# Hyper Parameters
EPOCH = 1 # 為節約時間我們只訓練一輪
BATCH_SIZE = 50
LR = 0.001 # 誰知學習率
DOWNLOAD_MNIST = True # 如果數據集你已經下載好了就設置為False
# Mnist digits dataset
train_data = torchvision.datasets.MNIST(
root='./mnist/',
train=True, # this is training data
transform=torchvision.transforms.ToTensor(), # Converts a PIL.Image or numpy.ndarray to
# torch.FloatTensor of shape (C x H x W) and normalize in the range [0.0, 1.0]
download=DOWNLOAD_MNIST, # download it if you don't have it
)
# plot one example
print(train_data.train_data.size()) # (60000, 28, 28)
print(train_data.train_labels.size()) # (60000)
plt.imshow(train_data.train_data[0].numpy(), cmap='gray')#打印出數據集中第一個手寫數字的灰度圖片
plt.title('%i' % train_data.train_labels[0])
plt.show()
# Data Loader for easy mini-batch return in training, the image batch shape will be (50, 1, 28, 28)
#數據加載器,便於訓練中的小批量返回,圖像批量形狀為(50, 1, 28, 28)28X28是這個灰度圖片的像素,封裝成(50,1,28,28)這樣一個tensor
# shuffle=True 對數據進行洗牌
train_loader = Data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
# convert test data into Variable, pick 2000 samples to speed up testing
#將測試數據轉換為變量,選取2000個樣本加速測試
test_data = torchvision.datasets.MNIST(root='./mnist/', train=False)#--->測試數據
#traindata將原來的[0-255]壓縮到了[0.0, 1.0]這個區間,所以test data也要/255做同樣處理
test_x = Variable(torch.unsqueeze(test_data.test_data, dim=1), volatile=True).type(torch.FloatTensor)[:2000]/255.
# shape from (2000, 28, 28) to (2000, 1, 28, 28), value in range(0,1)
#為了節省時間只取了2000個測試
test_y = test_data.test_labels[:2000]
class CNN(nn.Module):#繼承了nn.Model
def __init__(self):
#繼承了 CNN 父類的的屬性
super(CNN, self).__init__()#用父類的初始化方式來初始化所繼承的來自父類的屬性
#按照網絡的前后順序定義1號網絡
self.conv1 = nn.Sequential( # input shape (1, 28, 28)
nn.Conv2d(#這里的nn.Conv2d使用一個2維度卷積
in_channels=1, #in_channels:在文本應用中,即為詞向量的維度
out_channels=16, # out_channels:卷積產生的通道數,有多少個out_channels,就需要多少個一維卷積(也就是卷積核的數量)
kernel_size=5, #kernel_size:卷積核的尺寸;卷積核的第二個維度由in_channels決定,所以實際上卷積核的大小為kernel_size * in_channels
stride=1, #步長,每次移動的單位格子
padding=2, #padding:對輸入的每一條邊,補充0的層數
), # output shape (16, 28, 28)
nn.ReLU(), #激活函數ReLU # activation
#在2X2的池化層里選出最大值
nn.MaxPool2d(kernel_size=2), # choose max value in 2x2 area, output shape (16, 14, 14)
)
#按照網絡的前后順序定義2號網絡,
self.conv2 = nn.Sequential( # input shape (1, 28, 28)
#使用一個二維卷積
nn.Conv2d(16, 32, 5, 1, 2), # output shape (32, 14, 14)
nn.ReLU(), # activation
nn.MaxPool2d(2), # output shape (32, 7, 7)
)
#全連接層
self.out = nn.Linear(32 * 7 * 7, 10) #因為在pytorch中做全連接的輸入輸出都是二維張量,不同於卷積層要求輸入4維張量
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = x.view(x.size(0), -1) # flatten the output of conv2 to (batch_size, 32 * 7 * 7)
output = self.out(x)
#output是我們的真實值,而x是用於做數據可視化的參數
return output, x # return x for visualization
#把CNN神經網絡類實例化
cnn = CNN()
print(cnn) # net architecture
#設置一個優化器
optimizer = torch.optim.Adam(cnn.parameters(), lr=LR) # optimize all cnn parameters
#設置損失函數
loss_func = nn.CrossEntropyLoss() # the target label is not one-hotted
#這些是做數據可視化的,先不做深入了解
# following function (plot_with_labels) is for visualization, can be ignored if not interested
from matplotlib import cm
try: from sklearn.manifold import TSNE; HAS_SK = True
except: HAS_SK = False; print('Please install sklearn for layer visualization')
def plot_with_labels(lowDWeights, labels):
plt.cla()
X, Y = lowDWeights[:, 0], lowDWeights[:, 1]
for x, y, s in zip(X, Y, labels):
c = cm.rainbow(int(255 * s / 9)); plt.text(x, y, s, backgroundcolor=c, fontsize=9)
plt.xlim(X.min(), X.max()); plt.ylim(Y.min(), Y.max()); plt.title('Visualize last layer'); plt.show(); plt.pause(0.01)
plt.ion()
# training and testing
for epoch in range(EPOCH):#節約時間只循環一次也就是訓練一輪
#當我們迭代我們的數據集的時候批量處理數據
#step 是循環的次數, (x,y) 對應每一個train_loader數據中的target和真實值
for step, (x, y) in enumerate(train_loader): # gives batch data, normalize x when iterate train_loader
#Variable 類型可以對該類型的數據自動求導
b_x = Variable(x) # batch x
b_y = Variable(y) # batch y
#選取從網絡里返回第一個數據作為我們的 真實值
output = cnn(b_x)[0] # cnn output
#在損失函數中 (target(這里也就是這個手寫數字是幾)-真實值)得到誤差
loss = loss_func(output, b_y) # cross entropy loss
#清空這一輪數據的 梯度,不然數據會影響下輪迭代訓練 #在每次循環中清零 grad避免累加
optimizer.zero_grad() # clear gradients for this training step
#反向傳播計算梯度
loss.backward() # backpropagation, compute gradients
#optimizer的執行step更新指令,更新model的每一個parameter
optimizer.step() # apply gradients
#每訓練五十個數據,我們就用當前訓練出的模型來預測
if step % 50 == 0:
#用測試集數據來測試,得到兩個返回值一個是用於做數據可視化的一個是真實值
test_output, last_layer = cnn(test_x)
#在返回真實值選擇最大的一個作為預測值
pred_y = torch.max(test_output, 1)[1].data.squeeze()
#這里 test_y就是灰度圖片本身的標簽 准確度就是所有的 預測值==標簽(測試集預測成功的次數)/測試集所有的數據數量,也就是預測成功率
accuracy = sum(pred_y == test_y) / float(test_y.size(0))
print('Epoch: ', epoch, '| train loss: %.4f' % loss.data.item(), '| test accuracy: %.2f' % accuracy)
#下面是做數據可視化,現階段主要了解pytorch框架
if HAS_SK:
# Visualization of trained flatten layer (T-SNE)
tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000)
plot_only = 500
low_dim_embs = tsne.fit_transform(last_layer.data.numpy()[:plot_only, :])
labels = test_y.numpy()[:plot_only]
plot_with_labels(low_dim_embs, labels)
plt.ioff()
# print 10 predictions from test data
#輸出前十組預測值和真實值, 對比一下預測效果
test_output, _ = cnn(test_x[:10])
pred_y = torch.max(test_output, 1)[1].data.numpy().squeeze()
print(pred_y, 'prediction number')
print(test_y[:10].numpy(), 'real number')
運行截圖(我在jupter上跑的):
這個是拿訓練好的模型來預測前十個 手寫數字,第一行是預測值,第二行是標簽,前十個都識別出來了!還是比較成功的。