张量
引入pytorch,生成一个随机的5x3张量
1>>> from __future__ import print_function 2>>> import torch 3>>> x = torch.rand(5, 3) 4>>> print(x) 5tensor([[0.5555, 0.7301, 0.5655], 6 [0.9998, 0.1754, 0.7808], 7 [0.5512, 0.8162, 0.6148], 8 [0.8618, 0.3293, 0.6236], 9 [0.2787, 0.0943, 0.2074]])
声明一个5x3的张量,张量中所有元素初始化为0
>>> x = torch.zeros(5, 3, dtype=torch.long)
从数据直接构造张量,这里的数据一般是python数组
1>>> x = torch.tensor([5.5, 3]) 2>>> print(x) 3tensor([5.5000, 3.0000])
从一个已有的tensor上类似创建新的张量,新、旧张量的形状和数据类型相同,除非对dtype进行了覆盖声明
1>>> x = x.new_ones(5, 3, dtype=torch.double) 2>>> print(x) 3tensor([[1., 1., 1.], 4 [1., 1., 1.], 5 [1., 1., 1.], 6 [1., 1., 1.], 7 [1., 1., 1.]], dtype=torch.float64) 8>>> y = torch.rand_like(x, dtype=torch.float) 9>>> print(y) 10tensor([[0.6934, 0.9637, 0.0594], 11 [0.0863, 0.6638, 0.4728], 12 [0.3416, 0.0892, 0.1761], 13 [0.6831, 0.6404, 0.8307], 14 [0.6254, 0.4180, 0.2174]])
张量的size,numpy里是shape
1>>> print(x.size()) 2torch.Size([5, 3])
张量的操作
张量相加
1>>> x=torch.rand(5, 3) 2>>> y = torch.zeros(5, 3) 3>>> print(x + y) 4tensor([[0.8991, 0.9222, 0.2050], 5 [0.2478, 0.7688, 0.4156], 6 [0.4055, 0.9526, 0.2559], 7 [0.9481, 0.8576, 0.4816], 8 [0.0767, 0.3346, 0.0922]]) 9 10>>> print(torch.add(x, y)) 11tensor([[0.8991, 0.9222, 0.2050], 12 [0.2478, 0.7688, 0.4156], 13 [0.4055, 0.9526, 0.2559], 14 [0.9481, 0.8576, 0.4816], 15 [0.0767, 0.3346, 0.0922]]) 16 17>>> result = torch.empty(5, 3) 18>>> torch.add(x, y, out=result) 19tensor([[0.8991, 0.9222, 0.2050], 20 [0.2478, 0.7688, 0.4156], 21 [0.4055, 0.9526, 0.2559], 22 [0.9481, 0.8576, 0.4816], 23 [0.0767, 0.3346, 0.0922]]) 24 25>>> y.add_(x) 26tensor([[0.8991, 0.9222, 0.2050], 27 [0.2478, 0.7688, 0.4156], 28 [0.4055, 0.9526, 0.2559], 29 [0.9481, 0.8576, 0.4816], 30 [0.0767, 0.3346, 0.0922]])
张量内元素访问形式和numpy保持一致,如输出张量y的第二维度上下标是1的所有元素
1>>> print(y[:, 1]) 2tensor([0.9222, 0.7688, 0.9526, 0.8576, 0.3346])
iew函数改变tensor的形状,类似numpy的reshape
1>>> x = torch.randn(4, 4) 2>>> y = x.view(16) # 变成1x16的张量 3>>> z = x.view(-1, 8) # 变成第二维度是8,第一维度自动计算的张量,结果是2x8的张量 4>>> print(x.size(), y.size(), z.size()) 5torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
只有一个元素的向量,取这个元素
1>>> x = torch.randn(1) 2>>> print(x) 3tensor([0.8542]) 4>>> print(x.item()) 50.8541867136955261
转换成numpy数组
1>>> x = torch.rand(5, 3) 2>>> x.numpy() 3array([[0.9320856 , 0.473859 , 0.6787642 ], 4 [0.14365482, 0.1112923 , 0.8280207 ], 5 [0.4609589 , 0.51031697, 0.15313298], 6 [0.18854082, 0.4548 , 0.49709243], 7 [0.8351501 , 0.6160053 , 0.61391556]], dtype=float32)
除CharTensor外,所有的cpu张量从numpy转换成tensor
1import numpy as np 2a = np.ones(5) 3b = torch.from_numpy(a) 4np.add(a, 1, out=a) 5print(a) 6print(b)
在cpu和gpu之间移动tensor,
1if torch.cuda.is_available(): 2 device = torch.device("cuda") # a CUDA device object 3 y = torch.ones_like(x, device=device) # 直接在GPU设备上创建 4 x = x.to(device) # or just use strings ``.to("cuda")`` 5 z = x + y 6 print(z) 7 print(z.to("cpu", torch.double)) # ``.to`` can also change dtype together!
构建网络和损失函数
损失函数用来衡量输入和目标之间的距离
1from __future__ import print_function 2import torch 3import torch.nn as nn 4import torch.nn.functional as F 5 6class Net(nn.Module): 7 ## 定义了网络的结构 8 def __init__(self): 9 super(Net, self).__init__() 10 ## input is channel 1, output 6 channels with 3x3 convulutionanl kernel 11 self.conv1 = nn.Conv2d(1, 6, 3) 12 self.conv2 = nn.Conv2d(6, 16, 3) 13 # an affine operation: y = Wx + b, # 6*6 from image dimension 14 self.fc1 = nn.Linear(16*6*6, 120) 15 self.fc2 = nn.Linear(120, 84) 16 self.fc3 = nn.Linear(84, 10) 17 18 ## 前向传播,函数名必须是forward 19 def forward(self, x): 20 # Max pooling over a (2, 2) window 21 x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2)) 22 # If the size is a square you can only specify a single number 23 x = F.max_pool2d(F.relu(self.conv2(x)), 2) 24 x = x.view(-1, self.num_flat_features(x)) 25 x = F.relu(self.fc1(x)) 26 x = F.relu(self.fc2(x)) 27 x = self.fc3(x) 28 return x 29 30 def num_flat_features(self, x): 31 size = x.size()[1:] # all dimensions except the batch dimension 32 num_features = 1 33 for s in size: 34 num_features *= s 35 return num_features 36 37## 新建一个Net对象 38net = Net() 39print(net) 40params = list(net.parameters()) 41print(len(params)) 42print(params[0].size()) # conv1's .weight 43 44# 声明一个1x1x32x32的4维张量作为网络的输入 45input = torch.randn(1, 1, 32, 32) 46# input = torch.randn(1, 1, 32, 32) 47output = net(input) 48 49# net.zero_grad() 50# out.backward(torch.randn(1, 10)) 51target = torch.randn(10) 52target = target.view(1, -1) 53criterion = nn.MSELoss() 54loss = criterion(output, target) 55print(loss) 56 57print(loss.grad_fn) # MSELoss 58print(loss.grad_fn.next_functions[0][0]) # Linear 59print(loss.grad_fn.next_functions[0][0].next_functions[0][0]) # ReLU
网络的反向传播,为了反向传播损失(error)所做的只需要调用loss.backward()函数,如果没有清除已有的梯度,反向传播会累积梯度
调用loss.backward()函数,看以下conv1的bias的梯度在调用前后的差别。
1net.zero_grad() # zeroes the gradient buffers of all parameters 2 3print('conv1.bias.grad before backward') 4print(net.conv1.bias.grad) 5 6loss.backward() 7 8print('conv1.bias.grad after backward') 9print(net.conv1.bias.grad)
使用SGD更新权重
公式:weight = weight - learning_rate * gradient
可以用下面的torch代码实现
1learning_rate = 0.01 2for f in net.parameters(): 3 f.data.sub_(f.grad.data * learning_rate)
但是torch已经实现了各种权重更新方式,比如SGD, Nesterov-SGD, Adam, RMSProp等,可以直接调用
1import torch.optim as optim 2 3# create your optimizer 4optimizer = optim.SGD(net.parameters(), lr=0.01) 5 6# in your training loop: 7optimizer.zero_grad() # zero the gradient buffers 8output = net(input) 9loss = criterion(output, target) 10loss.backward() 11optimizer.step() # Does the update