天天看點

Pytorch入門深度學習(3)——Pytorch入門及MNIST實作(OOP)

一、Pytorch介紹

1)Pytorch介紹

PyTorch 是 Torch 在 Python 上的衍生. 因為 Torch 是一個使用 Lua 語言的神經網絡庫, Torch 很好用, 但是 Lua 又不是特别流行, 所有開發團隊将 Lua 的 Torch 移植到了更流行的語言 Python 上.

2)Pytorch和Tensorflow

Pyrtoch最大的優點就是建立的神經網絡是動态的,對比靜态的TensorFlow,它能更加有效地去處理一些問題,Tensorflow 的靜态計算圖使得他在 RNN 上有一點點被動 (雖然它用其他途徑解決了), 不過用 PyTorch 的時候, 你會對這種動态的 RNN 有更好的了解.而且 Tensorflow 的高度工業化, 它的底層代碼… 你是看不懂的. PyTorch 好那麼一點點, 如果你深入 API, 你至少能比看 Tensorflow 多看懂一點點 PyTorch 的底層在幹嘛.

動态圖:建構BP算法,首先要建構一個計算圖,即網絡的結構拓撲圖。如建構一個三層結構,前項計算的時候把網絡加到計算圖裡去,前項結束的時候,結構圖就有了,反向傳播的時候,從圖的最後一層倒推,一層一層計算每一層的梯度,然後更新參數。

Tensorflow是先建構一個結構圖,再對參數進行更新,不夠靈活。但能分布式訓練。

3)Numpy 還是Pytorch

Torch自稱為神經網絡界的Numpy,它能将torch産生的tensor放在GPU中加速運算,就想Numpy會把array放在CPU中加速運算。是以在神經網絡中,用Torch的tensor形式更優。

import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np

# 聲明一個tensor張量
a = torch.tensor(3)# 标量
print(a)
a = torch.tensor([3]) #向量
print(a)
print(type(a))
b = torch.tensor([1,2,3], dtype=torch.float32)
print(b)

a = torch.Tensor([1,2])#浮點型
print(a)
a = torch.tensor([1,2])
print(a)

a = torch.tensor([[1,2,3],[4,5,6]])
b = torch.tensor([[1,2,3],[4,5,6]])
print(a * b)
print(a + b)
print(a - b)
print(a / b)
print(torch.mm(a,b.t()))#矩陣乘法
print(torch.dot(a[0],b[0]))#求内積(torch.dot求向量的内積)
           

out:

tensor(3)

tensor([3])

<class ‘torch.Tensor’>

tensor([1., 2., 3.])

tensor([1., 2.])

tensor([1, 2])

tensor([[ 1, 4, 9],

[16, 25, 36]])

tensor([[ 2, 4, 6],

[ 8, 10, 12]])

tensor([[0, 0, 0],

[0, 0, 0]])

tensor([[1, 1, 1],

[1, 1, 1]])

tensor([[14, 32],

[32, 77]])

tensor(14)

4)Pytorch是什麼?

Pytorch是一個基于Python的科學計算工具包,它主要面向兩種場景:

用于替代Numpy,可以使用GPU的計算力

一種深度學習研究平台,可以提供最大的靈活性和速度

x = torch.tensor([1,2])
#x.to(device="cuda")
y = torch.tensor([3,4])
if torch.cuda.is_available():
    x = x.cuda() # 把x放在GPU裡面計算
    y = y.cuda()   # cpu convert cuda
    y = y.cpu() # cuda convert cpu
else:
    print(x, y)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(type(device))
x = x.to(device)
print(x)
x.cpu()
           

out:

tensor([1, 2]) tensor([3, 4])

<class ‘torch.device’>

tensor([1, 2])

# numpy轉torch
a = np.array([1,2,3])
b = torch.tensor([1,2,3])
c = (torch.from_numpy(a))
print(type(c))
print(type(b.numpy()))
a[0] = 5
print(c)#用的是同一套記憶體空間
print(torch.tensor(a))#直接轉Tensor
           

out:

<class ‘torch.Tensor’>

<class ‘numpy.ndarray’>

tensor([5, 2, 3], dtype=torch.int32)

tensor([5, 2, 3], dtype=torch.int32)

二、Pytorch的使用方法

1)Autograd:自動求導(automatic differentiation)

Pytorch中所有神經網絡的核心是autograd包。

autograd包為張量上的所有操作提供了自動求導,它是一個運作時定義的架構,這意味着反向傳播是根據你的代碼如何運作來定義,并且每次疊代可以不同。

2)變量(Variable)

autograd.Variable是autograd包的核心類,它包裝了張量,支援幾乎所有的張量上的操作,一旦你完成前向計算,可以通過.backward()方法來自動計算所有的梯度,可以通過.data屬性來通路變量中的原始張量,關于這個變量的梯度被計算放入.grad屬性中

import torch
from torch.autograd import Variable

x = torch.tensor([1.,2,3], requires_grad=True)
# x = Variable(x, requires_grad=True) # 需要計算梯度
y = x.mean() # y = 1/3*(x1+x2+x3)# 隻能對标量進行求導
z = y**2
w = 3*z
w.backward()

print(w)
print(z)
print(y)
print(x.grad)# 求的x的導數
print(x.data)
           

out:

tensor(12., grad_fn=<MulBackward0>)

tensor(4., grad_fn=<PowBackward0>)

tensor(2., grad_fn=<MeanBackward0>)

tensor([4., 4., 4.])

tensor([1., 2., 3.])

#自動求導機制
_x = [i/100 for i in range(100)]
_y = [5*i + 3 for i in _x]

w = torch.rand(1,requires_grad=True)
b = torch.rand(1,requires_grad=True)
optimizer= torch.optim.SGD([w,b],lr=0.1)

for i in range(100):
    for x,y in zip(_x, _y):
        z = w*x + b
        loss = torch.pow((z-y),2)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        print(w,b)
           

out:

40次以上得到

tensor([5.0000], requires_grad=True) tensor([3.0000], requires_grad=True)

四、Pytorch實作CNN

計算MNIST圖檔的均值和方差

import torch
from torchvision import datasets, transforms

if __name__ == '__main__':
	train_dataset = datasets.MNIST("datasets/", train=True, download=True, transform=transforms.ToTensor())
	testdataset = datasets.MNIST("datasets/", train=False, download=True, transform=transforms.ToTensor())
	dataset = train_dataset+testdataset
	dataloader = torch.utils.data.DataLoader(dataset, batch_size=70000, shuffle=True)
	data = next(iter(dataloader))[0]#通過疊代器疊代,取出來隻有一個值,索引用0取出來
	mean = torch.mean(data,dim=(0,2,3))
	std = torch.std(data,dim=(0,2,3))
	print(mean)
	print(std)
           

out:

tensor([0.1309])

tensor([0.3084])

from torchvision import datasets, transforms
import numpy as np
if __name__ == '__main__':

	train_dataset = datasets.MNIST("datasets/", train=True, download=True, transform=transforms.ToTensor())
	testdataset = datasets.MNIST("datasets/", train=False, download=True, transform=transforms.ToTensor())
	mnist_data = train_dataset + testdataset
	_data = [d[0].data.cpu().numpy() for d in mnist_data]
	print(np.mean(_data))
	print(np.std(_data))
           

out:

tensor([0.1309])

tensor([0.3084])

五、Pytorch實作MNIST(OOP實作)

MyNet.py

import torch

class MLPNet(torch.nn.Module):

    def __init__(self):
        super().__init__()
        self.layer1 = torch.nn.Linear(784,128)
        self.layer2 = torch.nn.Linear(128,256)
        self.layer3 = torch.nn.Linear(256,512)
        self.layer4 = torch.nn.Linear(512,256)
        self.layer5 = torch.nn.Linear(256,128)
        self.layer6 = torch.nn.Linear(128,10)

    def forward(self, x):
        x = torch.reshape(x, (-1,784))
        x = torch.relu(self.layer1(x))
        x = torch.relu(self.layer2(x))
        x = torch.relu(self.layer3(x))
        x = torch.relu(self.layer4(x))
        x = torch.relu(self.layer5(x))
        x = self.layer6(x)#用均方差就不要用softmax
        return x

           

Train.py

術語:

輪次 10

疊代次數 120

batch_size

mini_batch:一個樣本的泛型不廣

from MyNet import MLPNet
import torch
import torch.nn.functional as F
from torchvision.datasets import MNIST
import torchvision.transforms as trans
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
from PIL import Image
import numpy as np

class Trainer:
    def __init__(self):
        self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        self.net = MLPNet().to(self.device)
        self.loss_func = torch.nn.MSELoss()
        self.opt = torch.optim.Adam(self.net.parameters())
        self.trainset, self.testset = self.get_dataset()
    def get_dataset(self):
        transformer = trans.Compose([
            trans.ToTensor(),
            trans.Normalize((0.1309,), (0.3084,))
        ])

        trianData = MNIST(root="datasets/", train=True, download=False, transform=transformer)
        testData = MNIST(root="datasets/", train=False, download=False, transform=transformer)
        return trianData, testData

    def loader_data(self):
        trainloader = DataLoader(dataset=self.trainset, batch_size=512, shuffle=True)
        testloader = DataLoader(dataset=self.testset, batch_size=512, shuffle=True)
        return trainloader, testloader

    def train(self):
        trainloader, testloader = self.loader_data()
        losses = []
        for i in range(10):
            print("epochs:{}".format(i))
            for j, (x, y) in enumerate(trainloader):
                x = x.to(self.device)
                y = F.one_hot(y.long()).float().to(self.device)#(500,10)
                #y = torch.zeros(y.size(0),10).scatter_(1, y.view(-1, 1), 1).to(self.device)
                out = self.net(x)
                loss = self.loss_func(out, y)
                if j % 10 == 0:
                    print("{}/{},loss{}".format(j, len(trainloader), loss.float()))
                    losses.append(loss.float())
                    plt.clf()
                    plt.plot(losses)
                    plt.pause(0.01)

                self.opt.zero_grad()
                loss.backward()
                self.opt.step()

            torch.save(self.net, "models/net.pth")
            self.test(testloader)#每輪結束都測試一下

    def test(self, testloader):
        total = 0
        for x ,y in testloader:
            x = x.to(self.device)
            y = y.to(self.device)
            out = self.net(x)
            predict = torch.argmax(out, dim=1)
            total += (predict == y).sum()

        print("精确度:{}".format(total.item() / len(self.testset) * 100))

    def testPic(self):
        net = torch.load("models/net.pth")
        img = Image.open("8.jpg")
        img = trans.Resize(28)(img)
        img = trans.Grayscale()(img)
        img = trans.ToTensor()(img)
        img = 1. - img
        img = trans.Normalize((0.1309,),(0.3084,))(img)
        img = img.unsqueeze(0)
        # print(img)
        """
        但均值标準差不是0.5,歸一化後不再是[-1,1],是以
        測試圖檔255 - 0 歸一化後是 1 - 0,訓練的圖檔是 0 - 1
        我們要測試的圖檔變成0 - 1,用1-img即可
        
        """
        out = net(img.to(self.device))
        print("AI的預測是:{}".format(out.argmax(dim=1)))
        # img.show()

    def testPic2(self):
        net = torch.load("models/net.pth")
        transformer = trans.Compose([
            trans.Resize(28),
            trans.CenterCrop(28),
            trans.Grayscale(),
            trans.ToTensor(),
            trans.Normalize((0.5,),(0.5,))
        ])
        """
        訓練:-1黑 1 白
        測試:也要這麼變,測試圖檔是白底黑字,訓練圖檔是黑底白字,
        即測試圖檔255 - 0 歸一化後是 1,-1
        訓練圖檔0 - 255,歸一化後是-1,1,是以給img.unsqueeze(0)*-1可以轉化
        """
        img = transformer(Image.open("8.jpg")).unsqueeze(0)*-1
        output = net(img.to(self.device))
        print("AI的預測是:{}".format(output.argmax(dim=1)))

    def test2(self, testloader):#檢視資料集
        net = torch.load("models/net.pth")
        for x ,y in testloader:
            x = x.to(self.device)
            datas = (x * 0.3084 + 0.1309) * 255
            datas = datas.cpu().numpy().astype(np.uint8)
            for i in range(x.shape[0]):
                data = datas[i].squeeze()
                out = net(x[i])
                predict = out.argmax(1)
                plt.clf()
                plt.subplot(1,2,1)
                plt.imshow(data)
                plt.subplot(1,2,2)
                plt.ylim(20)
                plt.xlim(20)
                plt.text(15,10, s="Result is :{}".format(predict.item()), color="r", fontsize=20)
                plt.pause(1)
if __name__ == '__main__':
    t = Trainer()
    train,test = t.loader_data()
    t.test2(test)
           
"""
self, dim, index ,value
torch.scatter()把value按照index根據dim的方向填入self中
"""
index = torch.tensor([0,2,4,1])
src = torch.zeros(4,5)
src = torch.scatter(src, dim=1, index=index.view(-1,1), value=1)
print(src)

a = F.one_hot(index)#要轉long()
print(a)
           

out:

tensor([[1., 0., 0., 0., 0.],

[0., 0., 1., 0., 0.],

[0., 0., 0., 0., 1.],

[0., 1., 0., 0., 0.]])

tensor([[1, 0, 0, 0, 0],

[0, 0, 1, 0, 0],

[0, 0, 0, 0, 1],

[0, 1, 0, 0, 0]])

繼續閱讀