PyTorch是一个基于Python的科学计算库,是一个用于深度学习的计算机视觉库。它的底层实现是由C/C++语言编写的,同时也兼容其他语言(如Python、C++等)。PyTorch中的Tensor是其最基本的数据类型之一,本篇博客将主要介绍PyTorch中的Tensor,包括Tensor数据类型的介绍和Tensor的运算。同时也会通过实现Python和PyTorch代码的方式,让读者更加深入地了解PyTorch中的Tensor。
1. Tensor数据类型介绍
在PyTorch中,Tensor是最基本的数据类型之一,可以看作是一个n维的数组。与NumPy中的ndarray类似,Tensor也支持大量的数学运算操作,例如加、减、乘、除、矩阵乘法、转置、索引等等,而在PyTorch中却比较方便实用,也更为高效。
下面我们通过实例的方式来看一下PyTorch中的Tensor数据类型。
import torch
# 创建一个5x3的随机Tensor
x = torch.rand(5, 3)
print(x)
上述代码中,首先引入了PyTorch库,然后使用rand函数创建了一个形状为5x3的随机元素值的Tensor,并将其打印出来:
tensor([[0.3764, 0.0654, 0.4382],
[0.0903, 0.5140, 0.6225],
[0.9246, 0.3911, 0.5736],
[0.7845, 0.4795, 0.2405],
[0.6178, 0.9304, 0.2541]])
在PyTorch中,Tensor可以像标准的Python数组一样进行索引,并使用多种数学运算操作。例如:
import torch
# 创建两个形状为3x3的Tensor
x = torch.rand(3, 3)
y = torch.rand(3, 3)
# Tensor加法
z = x + y
print(z)
# Tensor乘法
z = torch.mm(x, y)
print(z)
# Tensor转置
z = x.t()
print(z)
# Tensor索引
z = x[1, :]
print(z)
上述代码中,我们创建了两个形状为3x3的Tensor,并实现了Tensor的加法、乘法、转置和索引操作。其中,Tensor加法、乘法和转置的语法与NumPy类似。而Tensor的索引操作可以看作是在获取Tensor中某个特定位置上的值,x[1,:]的语法表示获取在第二行所有列上的值。
2. Tensor运算
PyTorch中的Tensor不仅支持基本的数学运算操作,还支持大量的高级运算操作。例如,向Tensor中的每一个元素添加一个常数、矩阵乘法、拼接(Concatenation)以及Tensor形状改变等等。
在本节中,我们将介绍一些Tensor的高级运算操作。
2.1 向Tensor中的每一个元素添加一个常数
import torch
# 创建一个形状为3x3的Tensor,并每一个元素加上常数10
x = torch.ones(3, 3)
y = x + 10
print(y)
上述代码中,我们定义了一个形状为3x3的Tensor x,这个Tensor每个元素的值为1。接着,我们向这个Tensor中的每一个元素加上了一个常数10,并将结果打印出来:
tensor([[11., 11., 11.],
[11., 11., 11.],
[11., 11., 11.]])
2.2 矩阵乘法
import torch
# 创建两个形状为3x3的Tensor,并实现矩阵乘法
x = torch.randn(3, 3)
y = torch.randn(3, 3)
z = torch.mm(x, y)
print(z)
上述代码中,我们创建了两个形状为3x3的Tensor,并对它们进行矩阵乘法运算,结果如下:
tensor([[-0.7018, -0.9357, -1.0921],
[-2.0233, 3.3809, 2.5824],
[ 0.0214, -1.5318, -1.1426]])
2.3 拼接(Concatenation)
import torch
# 创建两个形状为3x3的Tensor,然后对这两个Tensor进行拼接(Concatenation)
x = torch.randn(3, 3)
y = torch.randn(3, 3)
z = torch.cat([x, y], dim=1)
print(z)
上述代码中,我们创建了两个形状为3x3的Tensor x 和 y。然后我们对这两个Tensor进行了拼接(Concatenation),指定拼接方向为列方向(即dim=1),结果如下:
tensor([[ 0.2775, -1.1655, -1.5055, -0.0827, 0.1648, 1.2135],
[ 0.1548, 1.4414, 0.6044, -1.2424, -0.2577, -0.8128],
[-0.1800, -1.0362, 0.5983, 1.0433, -1.3550, -0.7059]])
2.4 Tensor形状改变
import torch
# 创建一个4x4的Tensor,并修改形状为8x2
x = torch.randn(4, 4)
print(x.shape)
y = x.view(8, 2)
print(y.shape)
上述代码中,我们创建了一个形状为4x4的Tensor x,并使用view函数将其形状修改为8x2,结果如下:
torch.Size([4, 4])
torch.Size([8, 2])
3. 实战案例
我们使用PyTorch中的Tensor对手写数字进行分类。首先,我们需要准备一些数据,这里我们使用MNIST数据集。MNIST数据集包含了很多手写数字的图像,每张图像的尺寸为28x28。每一个图像都有一个标签,标记了该图像所代表的数字。首先我们需要将这些数据读取到内存中。
import torch
from torchvision import datasets, transforms
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))])
train_data = datasets.MNIST('data', train=True, download=True, transform=transform)
test_data = datasets.MNIST('data', train=False, download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(train_data, batch_size=64, shuffle=True)
test_loader = torch.utils.data.DataLoader(test_data, batch_size=64, shuffle=True)
上述代码中,我们使用transforms模块对数据集进行了预处理,其中transforms.ToTensor()将图像转换为Tensor格式,transforms.Normalize((0.1307,), (0.3081,))用来对图像进行归一化处理。接着,我们使用datasets模块将数据集下载到了本地,并创建了一个train_loader和test_loader来分别加载训练数据和测试数据。
我们使用PyTorch的神经网络中的两种层来创建这个手写数字分类的模型,分别是全连接层和Dropout层。
import torch
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(28 * 28, 512)
self.fc2 = nn.Linear(512, 256)
self.fc3 = nn.Linear(256, 10)
self.dropout = nn.Dropout(0.2)
def forward(self, x):
x = x.view(-1, 28 * 28)
x = F.relu(self.fc1(x))
x = self.dropout(x)
x = F.relu(self.fc2(x))
x = self.dropout(x)
x = self.fc3(x)
return F.log_softmax(x, dim=1)
上述代码中,我们定义了一个Net类,继承了nn.Module。Net类包含了三个全连接层,其中第一个全连接层的输入维度为28x28,输出维度为512,第二个全连接层的输入维度为512,输出维度为256,第三个全连接层的输入维度为256,输出维度为10。同时,我们还定义了两个Dropout层,用于降低模型的过拟合程度。在Net中,我们首先将输入的Tensor形状转换为(-1, 28 * 28),然后将其经过多个全连接层和Dropout层的处理,最终通过log_softmax进行分类。
我们使用nn.CrossEntropyLoss()来计算loss,并使用torch.optim.Adam()作为优化算法。
import torch.optim as optim
from tqdm import tqdm
net = Net()
loss_fn = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=0.001)
def train(epoch):
net.train()
train_loss = 0
correct = 0
for batch_idx, (data, target) in enumerate(train_loader):
optimizer.zero_grad()
output = net(data)
loss = loss_fn(output, target)
loss.backward()
optimizer.step()
train_loss += loss.item()
pred = output.argmax(dim=1, keepdim=True)
correct += pred.eq(target.view_as(pred)).sum().item()
if batch_idx % 100 == 0:
tqdm.write('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
tqdm.write('Train Epoch: {} [Accuracy]: {}/{} ({:.0f}%)\n'.format(epoch, correct, len(train_loader.dataset),