基本概念
Tensor
tensor是的含義是張量稼跳,簡(jiǎn)單的理解可以將其當(dāng)成三維矩陣妄帘,pytorch中的張量是對(duì)數(shù)據(jù)的一種封裝懂傀,也是數(shù)據(jù)結(jié)構(gòu)中最核心的部分之一慎玖。對(duì)于pytorch中的張量,數(shù)組可能是更好的理解方法呻待。
Tensor的定義
- 直接定義矩陣打月,使用
torch.Tensor(shape)
方法定義未初始化的張量,使用torch.rand(shape)
或torch.randn(shape)
定義隨機(jī)張量
import torch as pt
x = pt.Tensor(2,4)
print(x)
# 1.00000e-23 *
# 0.0000 0.0000 1.2028 0.0000
# 0.0000 0.0000 1.1517 0.0000
# [torch.FloatTensor of size 2x4]
x = pt.rand(5,3)
# 0.7609 0.5925 0.5840
# 0.1949 0.6366 0.3763
# 0.1802 0.8529 0.9373
# 0.6013 0.9685 0.9945
# 0.6555 0.1740 0.9884
# [torch.FloatTensor of size 5x3]
print(x,x.size()[0])
# 5
y = pt.rand(5,3)
- 從numpy 中定義tensor蚕捉,使用
torch.from_numpy(ndarray)
的方法奏篙,需要注意的是,這種情況下numpy矩陣和tensor會(huì)“綁定”,即修改任何一個(gè)的值秘通,另一個(gè)的值也會(huì)發(fā)生變化
a = np.ones(5)
b = pt.from_numpy(a)
print(a,b)
#[ 1. 1. 1. 1. 1.]
#1
#1
#1
#1
#1
#[torch.DoubleTensor of size 5]
Tensor的基本操作
Tensor和numpy中的ndarray相似为严,可以完成加減乘除等運(yùn)算,常見(jiàn)的操作方法通常為Tensor.操作(參數(shù))
或torch.操作(參數(shù),out=輸出tensor)
肺稀,以加法為例
result = pt.Tensor(5,3)
test = pt.add(x,y,out=result)
# print(result,test)
y.add_(x)
兩種方法test = pt.add(x,y,out=result)
和y.add_(x)
都是相加第股,前者是相加后將結(jié)果交給一個(gè)新的Tensorresult
,而后者可以理解為y
自加x
Tensor還可以轉(zhuǎn)換為numpy的對(duì)象ndarray话原,可以使用Tensor.numpy()
獲得與Tensor
綁定的ndarray對(duì)象夕吻,修改Tensor時(shí),ndarray對(duì)象也發(fā)生變化
a = pt.ones(5)
b = a.numpy()
# print(a,b)
a.add_(1)
# print(a,b)
使用GPU加速
使用Tensor = Tensor.cuda()
的方法可以講Tensor
放到GPU上稿静,通常的運(yùn)算不支持從CPU到GPU的變換梭冠,因此若要在GPU上進(jìn)行網(wǎng)絡(luò)運(yùn)算,網(wǎng)絡(luò)聲明完成后也要調(diào)用網(wǎng)絡(luò)和輸入的.cuda()
方法將網(wǎng)絡(luò)和輸入放在GPU上
a,b = pt.Tensor(2,2),pt.Tensor(2,2)
a = a.cuda()
b = b.cuda()
Variable
Variable正向傳播
Variable與TensorFlow中的Variable一樣改备,是構(gòu)建神經(jīng)網(wǎng)絡(luò)和訓(xùn)練的核心類(lèi)控漠,使用Variable可以構(gòu)建計(jì)算圖,并在圖中計(jì)算結(jié)果(正向傳播)和微分(反向傳播)悬钳,Variable的一些運(yùn)算符重載過(guò)盐捷,因此可以直接使用+-*/
運(yùn)算符
x = Variable(pt.ones(2,2),requires_grad=True)
y = x + 2
z = y * y * 3
out = z.mean()
# Variable containing:
#27
#[torch.FloatTensor of size 1]
Variable反向傳播
以上構(gòu)建了一個(gè)計(jì)算圖并計(jì)算了out
的值,完成前向傳播默勾,使用out.backward()
可以執(zhí)行反向傳播碉渡,就是計(jì)算微分。
out.backward()
print(x.grad)
#Variable containing:
# 4.5000 4.5000
# 4.5000 4.5000
#[torch.FloatTensor of size 2x2]
網(wǎng)絡(luò)構(gòu)建
網(wǎng)絡(luò)結(jié)構(gòu)構(gòu)建
class Net(nn.Module):
"""docstring for Net"""
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1,6,5)# input channel,6 output channel,5x5
self.conv2 = nn.Conv2d(6,16,5)
self.fc1 = nn.Linear(16*5*5,120)#input 16*5*5,output120
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self,x):
x = F.max_pool2d(F.relu(self.conv1(x)),(2,2)) #input,poolcore shape
x = F.max_pool2d(F.relu(self.conv2(x)), 2) #(2,2) => 2 because 2=2
x = x.view(-1, self.num_flat_features(x)) #reshape
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
def num_flat_features(self,x):
size = x.size()[1:] #remove batch size
num_f = 1
for s in size:
num_f *= s
return num_f
以上為構(gòu)建一個(gè)簡(jiǎn)單的CNN的例子母剥,其中
-
nn
來(lái)自import torch.nn as nn
這其中封裝各種各樣的網(wǎng)絡(luò)層 -
F
來(lái)自import torch.nn.functional as F
滞诺,這其中封裝了各種各樣的神經(jīng)網(wǎng)絡(luò)需要使用的函數(shù)
在網(wǎng)絡(luò)結(jié)構(gòu)中
-
nn.Linear(input_size,output_size)
為線(xiàn)性連接層,為MLP的線(xiàn)性部分
-nn.Conv2d(input_channel,output_channel,shape)
表示卷積核
函數(shù)中
-
F.max_pool2d(input,core_shape)
為池化層 -
F.relu(input)
為ReLu激活函數(shù)
另外环疼,Variable.view()
為變形函數(shù)习霹,其中的-1
表示不關(guān)心batch,而函數(shù)self.num_flat_features(x)
是為了獲得x
的元素?cái)?shù)量炫隶,這一步直接將x
拍扁成向量
網(wǎng)絡(luò)的前向傳播
定義網(wǎng)絡(luò)后(以上的類(lèi))后淋叶,聲明后可以直接調(diào)用
net = Net().cuda()
print(net)
#Net (
# (conv1): Conv2d(1, 6, kernel_size=(5, 5), #stride=(1, 1))
# (conv2): Conv2d(6, 16, kernel_size=(5, 5), #stride=(1, 1))
# (fc1): Linear (400 -> 120)
# (fc2): Linear (120 -> 84)
# (fc3): Linear (84 -> 10)
#)
其中.cuda()
是將整個(gè)網(wǎng)絡(luò)放到GPU上,如果直接使用net = Net()
網(wǎng)絡(luò)位置在CPU上伪阶,將無(wú)法使用GPU加速煞檩。
定義網(wǎng)絡(luò)后,直接傳入輸入即可完成前向傳播
net.zero_grad() # Zero the gradient buffers of all parameters
inputdata = Variable(pt.randn(1,1,32,32)) #?(1,1,32,32) nSamples x nChannels x Height x Width
inputdata = inputdata.cuda()
# 1-batch 1-input channel 32,32
# print(inputdata)
# print(inputdata.unsqueeze(0)) #[torch.FloatTensor of size 1x1x1x32x32]
out = net(inputdata)
print(out)
其中net.zero_grad()
是為了清除梯度栅贴,起類(lèi)似于初始化的作用斟湃。pytorch要求數(shù)據(jù)與網(wǎng)絡(luò)的位置相同,因此若是網(wǎng)絡(luò)聲明在GPU上檐薯,數(shù)據(jù)也必須要GPU上加速桐早。
網(wǎng)絡(luò)的反向傳播(權(quán)值更新)
網(wǎng)絡(luò)的反向傳播可以直接使用預(yù)先定義的代價(jià)函數(shù)的.backward()
方法實(shí)現(xiàn)
net.zero_grad()
print(net.conv1.bias.grad)
loss.backward()
print(net.conv1.bias.grad)
在更新權(quán)值的時(shí)候,可以手動(dòng)指定更新的方法
for f in net.parameters():
f.data.sub_(f.grad.data * 0.01)
其中:
- net.parameters()是個(gè)生成器,可以遍歷net中的所有參數(shù)
- f.grad.data為輸出(代價(jià)函數(shù))到這一參數(shù)的梯度
除了手動(dòng)制定哄酝,也可以從import torch.optim as optim
中調(diào)用優(yōu)化器
optimizer = optim.SGD(net.parameters(),lr=0.01)
optimizer.zero_grad()
out = net(inputdata)
loss = criterion(out,target)
loss.backward()
optimizer.step()
其中criterion()
為代價(jià)函數(shù),loss
為代價(jià)函數(shù)的輸出值祷膳,optimizer.step()
為調(diào)用一次優(yōu)化
代價(jià)函數(shù)
代價(jià)函數(shù)表示當(dāng)前結(jié)果距離期望輸出的“距離”陶衅,torch.nn
封裝了一些代價(jià)函數(shù),可以在訓(xùn)練的時(shí)候直接調(diào)用
target = Variable(pt.arange(1,11)).cuda()
# print(target)
criterion = nn.MSELoss().cuda()
loss = criterion(out,target) #out shape = 1*10 target shape = 10
這里調(diào)用的代價(jià)函數(shù)是MSELoss()
平方平均函數(shù)
分類(lèi)網(wǎng)絡(luò)搭建直晨,訓(xùn)練與測(cè)試
分類(lèi)網(wǎng)絡(luò)數(shù)據(jù)準(zhǔn)備
教程提供的范例的訓(xùn)練集是CIFAR10數(shù)據(jù)集搀军,該數(shù)據(jù)集提供了10種不同類(lèi)型的圖片,引入代碼如下圖
import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
shuffle=True, num_workers=2)
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=2)
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
這部分僅僅是下載并提供數(shù)據(jù)集勇皇,不必深究罩句,需要注意的是從testloader
中獲得數(shù)據(jù)即可
分類(lèi)網(wǎng)絡(luò)搭建
分類(lèi)網(wǎng)絡(luò)搭建使用兩層conv+pool后接3層mlp層的結(jié)構(gòu),是個(gè)基本的卷積神經(jīng)網(wǎng)絡(luò)敛摘,構(gòu)建類(lèi)如下
class Net(nn.Module):
"""docstring for Net"""
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3,6,5)
self.pool = nn.MaxPool2d(2,2)
self.conv2 = nn.Conv2d(6,16,5)
self.fc1 = nn.Linear(16*5*5,120)
self.fc2 = nn.Linear(120,84)
self.fc3 = nn.Linear(84, 10)
def forward(self,x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = x.view(-1,16*5*5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = Net().cuda()
這里將組件的定義放在了構(gòu)造函數(shù)中门烂,而將網(wǎng)絡(luò)前饋部分放在了單獨(dú)的forward()
函數(shù)中。另外兄淫,使用net = Net().cuda()
將網(wǎng)絡(luò)放在了GPU上
分類(lèi)網(wǎng)絡(luò)的訓(xùn)練
分類(lèi)網(wǎng)絡(luò)的訓(xùn)練需要定義優(yōu)化器和代價(jià)函數(shù)屯远,剩下的就是將數(shù)據(jù)丟進(jìn)神經(jīng)網(wǎng)絡(luò)中了,代碼如下
criterion = nn.CrossEntropyLoss()
#聲明使用交叉熵函數(shù)作為代價(jià)函數(shù)
optimizer = optim.SGD(net.parameters(),lr=0.001,momentum=0.9)
#聲明使用學(xué)習(xí)率0.001的SGD優(yōu)化器
for epoch in range(2):
running_loss = 0
for i,data in enumerate(trainloader,0):
inputs,labels = data
inputs,labels = Variable(inputs).cuda(),Variable(labels).cuda()
#獲得數(shù)據(jù)并將其放在GPU上
optimizer.zero_grad()
#初始化梯度
outputs = net(inputs)
#前饋
loss = criterion(outputs,labels)
loss.backward()
optimizer.step()
#反饋計(jì)算梯度并更新權(quán)值
running_loss += loss.data[0]
if i % 200 == 0:
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0
#打印平均代價(jià)函數(shù)值
print('Finished Training')
總結(jié)一下捕虽,該部分代碼總共做了以下幾件事
- 定義優(yōu)化器與代價(jià)函數(shù)
- 執(zhí)行網(wǎng)絡(luò)訓(xùn)練
執(zhí)行網(wǎng)絡(luò)訓(xùn)練部分慨丐,每次迭代包括以下操作
- 獲取batch數(shù)據(jù)并將其放在GPU上
2.初始化梯度
3.執(zhí)行前饋計(jì)算代價(jià)函數(shù)
4.執(zhí)行反饋計(jì)算梯度并更新權(quán)值
分類(lèi)網(wǎng)絡(luò)的測(cè)試
網(wǎng)絡(luò)測(cè)試部分就是將所有的訓(xùn)練數(shù)據(jù)再投入網(wǎng)絡(luò)中訓(xùn)練一次,看真實(shí)結(jié)果與預(yù)測(cè)結(jié)果是否相同泄私,代碼如下
corret,total = 0,0
for images,labels in testloader:
images = images.cuda()
labels = labels.cuda()
outputs = net(Variable(images))
_,predicted = torch.max(outputs.data,1)
total += labels.size(0)
corret += (predicted == labels).sum()
print('Accuracy of the network on the 10000 test images: %d %%' % (
100 * corret / total))
前饋得到預(yù)測(cè)結(jié)果后房揭,使用_,predicted = torch.max(outputs.data,1)
在第一維看取出最大的數(shù)(丟棄)和最大數(shù)的位置(保留)后再與label相比即可進(jìn)行測(cè)試