【超详解】一文带你搞懂pytorch

安装

PyTorch官网上选择对应的版本,然后下面会出现对应的安装命令 一般安装命令如下:

pip3 install torch torchvision torchaudio

查看版本

import torch
print(torch.__version__)
print("cuda:",torch.cuda.is_available())

2.0.0
cuda: False

import torch
x = torch.rand(5, 3)
print(x)

tensor([[0.5553, 0.0181, 0.7441],
        [0.4689, 0.3908, 0.6449],
        [0.9274, 0.1054, 0.7166],
        [0.3487, 0.2997, 0.3689],
        [0.3598, 0.7262, 0.9564]])

基础知识

张量(Tensors)

声明和定义

  • torch.empty(): 声明一个未初始化的矩阵。创建张量时,它将创建一个具有指定形状的未初始化张量。这意味着张量中的元素可能包含任意值,取决于分配内存时内存中已有的数据。
x = torch.empty(5,3)
print(x)

tensor([[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]])

  • torch.rand():随机初始化一个矩阵
rand_x = torch.rand(5,3)
print(rand_x)

tensor([[0.5710, 0.4795, 0.3452],
        [0.4638, 0.3674, 0.7197],
        [0.7879, 0.7133, 0.5328],
        [0.3456, 0.7724, 0.8772],
        [0.5487, 0.5881, 0.9586]])

  • torch.zeros():创建数值皆为 0 的矩阵,也可以创建都是1的矩阵。torch.ones
# 创建一个数值皆是 0,类型为 long 的矩阵
zero_x = torch.zeros(5, 3, dtype=torch.long)
print(zero_x.shape,zero_x)

torch.Size([5, 3]) tensor([[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]])

  • torch.tensor():直接传递 tensor 数值来创建
# tensor 数值是 [5.5, 3]
tensor1 = torch.tensor([5.5, 3])
print(tensor1.shape,tensor1)

torch.Size([2]) tensor([5.5000, 3.0000])

  • tensor.new_ones():new_*() 方法需要输入尺寸大小
# 显示定义新的尺寸是 5*3,数值类型是 torch.double
tensor2 = tensor1.new_ones(5, 3, dtype=torch.double)  # new_* 方法需要输入 tensor 大小
print(tensor2)

tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]], dtype=torch.float64)

  • torch.randn_like(old_tensor):保留相同的尺寸大小
# 修改数值类型
tensor3 = torch.randn_like(tensor2, dtype=torch.float)
print('tensor3: ', tensor3,"size:",tensor3.shape)

tensor3:  tensor([[-0.0608,  1.5656,  0.4083],
        [ 1.1475, -0.7965,  0.8246],
        [-1.1804, -0.4327, -1.2193],
        [ 1.5783,  0.0615,  0.1998],
        [ 0.9681, -0.1788, -0.2666]]) size: torch.Size([5, 3])

操作

加法操作,示例如下:

  • “+” 运算符
  • torch.add(tensor1, tensor2, [out=tensor3])
  • tensor1.add_(tensor2):直接修改 tensor 变量

可以改变 tensor 变量的操作都带有一个后缀 , 例如 x.copy(y), x.t_() 都可以改变 x 变量

tensor4 = torch.rand(5,3)
print("tensor3 + tensor4 = ",tensor4 + tensor3)
print('tensor3 + tensor4= ', torch.add(tensor3, tensor4))
# 新声明一个 tensor 变量保存加法操作的结果
result = torch.empty(5, 3)
torch.add(tensor3, tensor4, out=result)
print('add result= ', result)
# 直接修改变量
tensor3.add_(tensor4)
print('tensor3= ', tensor3)

tensor3 + tensor4 =  tensor([[ 0.9336,  1.6787,  0.9975],
        [ 1.3947,  0.1874,  0.8959],
        [-0.2482,  0.5251, -0.8883],
        [ 1.9656,  0.2401,  0.3338],
        [ 1.0201, -0.0755,  0.1412]])
tensor3 + tensor4=  tensor([[ 0.9336,  1.6787,  0.9975],
        [ 1.3947,  0.1874,  0.8959],
        [-0.2482,  0.5251, -0.8883],
        [ 1.9656,  0.2401,  0.3338],
        [ 1.0201, -0.0755,  0.1412]])
add result=  tensor([[ 0.9336,  1.6787,  0.9975],
        [ 1.3947,  0.1874,  0.8959],
        [-0.2482,  0.5251, -0.8883],
        [ 1.9656,  0.2401,  0.3338],
        [ 1.0201, -0.0755,  0.1412]])
tensor3=  tensor([[ 0.9336,  1.6787,  0.9975],
        [ 1.3947,  0.1874,  0.8959],
        [-0.2482,  0.5251, -0.8883],
        [ 1.9656,  0.2401,  0.3338],
        [ 1.0201, -0.0755,  0.1412]])

除了加法运算操作,对于 Tensor 的访问,和 Numpy 对数组类似,可以使用索引来访问某一维的数据,如下所示:

# 访问 tensor3 第一列数据
print(tensor3[:, 0])

tensor([ 0.9336,  1.3947, -0.2482,  1.9656,  1.0201])

对 Tensor 的尺寸修改,可以采用 torch.view() ,如下所示:

x = torch.randn(4, 4)
y = x.view(16)
# 第一维的大小由-1自动推断,第二维的大小为8
z = x.view(-1, 8)
print(x.size(), y.size(), z.size())

torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])

如果 tensor 仅有一个元素,可以采用 .item() 来获取类似 Python 中整数类型的数值:

x = torch.randn(1)
print(x)
print(x.item())

tensor([-0.6999])
-0.6999301314353943

Numpy数组转换

Tensor 和 Numpy 的数组可以相互转换,并且两者转换后共享在 CPU 下的内存空间,即改变其中一个的数值,另一个变量也会随之改变。

Tensor转换为Numpy数组

a = torch.ones(5)
print(a)
b = a.numpy()
print(b)

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

两者是共享同个内存空间的,例子如下所示,修改 tensor 变量 a,看看从 a 转换得到的 Numpy 数组变量 b 是否发生变化。

a.add_(1)
print(a)
print(b)

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

Numpy数组转换成Tensor

在 CPU 上,除了 CharTensor 外的所有 Tensor 类型变量,都支持和 Numpy数组的相互转换操作。

import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print(a.shape,a.dtype,a)
print(b.shape,b.dtype,b)

(5,) float64 [2. 2. 2. 2. 2.]
torch.Size([5]) torch.float64 tensor([2., 2., 2., 2., 2.], dtype=torch.float64)

CUDA 张量

Tensors 可以通过 .to 方法转换到不同的设备上,即 CPU 或者 GPU 上。例子如下所示

# 当 CUDA 可用的时候,可用运行下方这段代码,采用 torch.device() 方法来改变 tensors 是否在 GPU 上进行计算操作
if torch.cuda.is_available():
    device = torch.device("cuda")          # 定义一个 CUDA 设备对象
    y = torch.ones_like(x, device=device)  # 显示创建在 GPU 上的一个 tensor
    x = x.to(device)                       # 也可以采用 .to("cuda") 
    z = x + y
    print(z)
    print(z.to("cpu", torch.double))       # .to() 方法也可以改变数值类型

神经网络

对于神经网络来说,一个标准的训练流程是这样的:

  • 定义一个多层的神经网络
  • 对数据集的预处理并准备作为网络的输入
  • 将数据输入到网络
  • 计算网络的损失
  • 反向传播,计算梯度 更新网络的梯度,一个简单的更新规则是 weight = weight - learning_rate * gradient
import torch
import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        # 输入图像是单通道,conv1 kenrnel size=5*5,输出通道 6
        self.conv1 = nn.Conv2d(1, 6, 5)
        # conv2 kernel size=5*5, 输出通道 16
        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):
        # max-pooling 采用一个 (2,2) 的滑动窗口
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # 核(kernel)大小是方形的话,可仅定义一个数字,如 (2,2) 用 2 即可
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, self.num_flat_features(x))
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

    def num_flat_features(self, x):
        # 除了 batch 维度外的所有维度
        size = x.size()[1:]
        num_features = 1
        for s in size:
            num_features *= s
        return num_features

net = Net()
print(net)

params = list(net.parameters())
print("参数量:",len(params))
# conv1.weight
print("第一个参数大小:",params[0].size())


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(in_features=400, out_features=120, bias=True)
  (fc2): Linear(in_features=120, out_features=84, bias=True)
  (fc3): Linear(in_features=84, out_features=10, bias=True)
)
参数量: 10
第一个参数大小: torch.Size([6, 1, 5, 5])

# 验证网络的正确性
input = torch.randn(1,1,32,32)
out = net(input)
print(out)

tensor([[ 0.0498, -0.0137, -0.0628, -0.1135, -0.0288,  0.0982, -0.0134,  0.0994,
          0.0788, -0.1159]], grad_fn=<AddmmBackward0>)

# 清空所有参数的梯度缓存,然后计算随机梯度进行反向传播
net.zero_grad()
out.backward(torch.randn(1,10))

损失函数

损失函数的输入是 (output, target) ,即网络输出和真实标签对的数据,然后返回一个数值表示网络输出和真实标签的差距。

out = net(input)
print("out.shape:",out.shape)
# 定义伪标签,调整大小,需要保持和output一样的size
target = torch.randn(1,10)
print(target.shape)
# 定义均方误差
criterion = nn.MSELoss()

loss = criterion(out,target)
print(loss)

out.shape: torch.Size([1, 10])
torch.Size([1, 10])
tensor(1.2594, grad_fn=<MseLossBackward0>)

反向传播

反向传播的实现只需要调用 loss.backward() 即可,当然首先需要清空当前梯度缓存,即.zero_grad() 方法,否则之前的梯度会累加到当前的梯度,这样会影响权值参数的更新。 下面是一个简单的例子,以 conv1 层的偏置参数 bias 在反向传播前后的结果为例:

# 清空所有参数的梯度缓存
net.zero_grad()
print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)

loss.backward()

print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)

conv1.bias.grad before backward
None
conv1.bias.grad after backward
tensor([ 0.0010, -0.0127,  0.0054,  0.0048, -0.0075,  0.0059])

更新权重

采用随机梯度下降(Stochastic Gradient Descent, SGD)方法的最简单的更新权重规则如下:

weight = weight - learning_rate * gradient

按照这个规则,代码实现如下所示:

# 简单实现权重的更新例子
learning_rate = 0.01
for f in net.parameters():
    f.data.sub_(f.grad.data*learning_rate)

# 其他优化算法,可以使用torch.optim库

import torch.optim as optim

optimizer = optim.SGD(net.parameters(),lr=0.01)

# 在训练过程中执行下列操作
optimizer.zero_grad() # 清空梯度
output = net(input)
loss = criterion(output,target)
loss.backward()
# 更新权重
optimizer.step()

训练分类器

  • pytorch有一个专门的torchvision库,包含了dataloader和dataset
  • 使用CIFAR10为例

训练图片分类器

一般流程如下:

  1. 通过调用torchvision加载和归一化CIFAR10训练集和测试集
  2. 构建一个卷积神经网络
  3. 定义一个损失函数
  4. 在训练集上训练网络
  5. 在测试集上测试网络性能
# 1.加载和归一化CIFAR10
# 导入必须包
import torch
import torchvision
import torchvision.transforms as transforms

# torchvision 的数据集输出的图片都是 PILImage ,即取值范围是 [0, 1] ,这里需要做一个转换,变成取值范围是 [-1, 1] 
# 定义了一个组合变换
transform = transforms.Compose([
    transforms.ToTensor(), # PIL Image或者Numpy ndarray转换成Pytorch的张量
    transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5)) # 图像数据执行标准化操作,第一个三元组表示每个通道的均值 (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)
# 10个分类
classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

Files already downloaded and verified
Files already downloaded and verified

# 可视化训练图片,也可以使用TensorBoard
import matplotlib.pyplot as plt
import numpy as np

# 展示图片
def imshow(img):
    print(img.shape)
    img = img/2 +0.5 #  对图像进行逆归一化。在进行训练前,图像很可能已经被归一化,其像素值范围在[-1, 1]之间。逆归一化之后,像素值范围将恢复到 [0, 1],从而能够以原始形式正确显示图像。
    npimg = img.numpy() # pytorch张量转换成numpy
    plt.imshow(np.transpose(npimg,(1,2,0))) # np.transpose() 对 NumPy 数组 npimg 进行转置,第1个轴(高度 H)变为新的第 0 个轴 2=>1 0=>2
    plt.show()

# 随机获取训练集图片
dataiter = iter(trainloader)
# 返回的图片数量和设置的batch_size有关
images,labels = next(dataiter)
print(images.shape,labels)

# 展示图片
imshow(torchvision.utils.make_grid(images))
# 打印图片标签
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

torch.Size([4, 3, 32, 32]) tensor([7, 7, 9, 8])
torch.Size([3, 36, 138])

image.png

horse horse truck ship

附录

本文都是由python的notebook编写,包括结果都是运行后生成的结果。最终通过notebook转换成markdown,命令如下:

jupyter nbconvert torch_basic.ipynb --to markdown

,如有侵权,请联系删除。

<center><B>---------------------------END---------------------------



## 题外话



![在这里插入图片描述](https://img-blog.csdnimg.cn/cb9eaa37ce994535bdf60e07ba4cf09e.jpeg#pic_center)

<font face="幼圆" size="4" color="red">感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。
</font>

👉<font color = red>CSDN大礼包:gift::</font>[全网最全《Python学习资料》免费赠送:free:!](https://blog.csdn.net/weixin_68789096/article/details/132275547?spm=1001.2014.3001.5502)<font color=#66cc66>(安全链接,放心点击)</font>



**一、Python所有方向的学习路线**

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

![img](https://img-blog.csdnimg.cn/1d40facda2b84990b8e1743f5487d455.png)
 



**二、Python兼职渠道推荐***

学的同时助你创收,每天花1-2小时兼职,轻松稿定生活费.
![在这里插入图片描述](https://img-blog.csdnimg.cn/23cca249409e4167b89e7b9c7fe546ee.png)


**三、最新Python学习笔记**

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

 ![img](https://img-blog.csdnimg.cn/6d414e9f494742db8bcc3fa312200539.png)





**四、实战案例**

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

![img](https://img-blog.csdnimg.cn/a353983317b14d3c8856824a0d6186c1.png)





👉<font color = red>CSDN大礼包:gift::</font>[全网最全《Python学习资料》免费赠送:free:!](https://blog.csdn.net/weixin_68789096/article/details/132275547?spm=1001.2014.3001.5502)<font color=#66cc66>(安全链接,放心点击)</font>

若有侵权,请联系删除
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值