Published on

pytorch入门

Authors
  • avatar
    Name
    yushenw
    Linkedin

pytorch简介

PyTorch 是一个开源的机器学习库,广泛用于计算机视觉和自然语言处理等应用程序。它由 Facebook 的 AI 研究团队首次发布,并得到了许多来自学术界和工业界的贡献。PyTorch 提供了两个主要的功能:

1. 张量计算(类似于 NumPy)的优化

PyTorch 提供了一个强大的多维数组对象称为张量(Tensor),这在功能上类似于 NumPy 的数组,但有一个显著的不同:PyTorch 张量可以在 GPU 上进行计算。这使得数据处理和深度学习模型训练更加快速高效。

2. 深度学习

PyTorch 在自动微分系统中支持动态计算图(称为 autograd)。这允许在网络结构中使用循环和条件控制语句,同时保持梯度计算的准确性。这种动态图的特性使得模型更加灵活,编码时更加直观,尤其是在复杂的结构和递归网络中。

其他关键特点包括:

  • 模块化和可重用性:PyTorch 使用 Module 类为深度学习模型的构建块提供了一个高层次的抽象。这使得模型定义清晰,易于管理,并且可以复用代码。
  • 优化工具箱:PyTorch 提供了多种优化算法,包括常用的 SGD、Adam 等,这些都可以轻松配置并用于训练模型。
  • GPU 加速:PyTorch 支持 CUDA,这使得它可以利用 Nvidia GPU 来加速其数学运算,极大地提高了计算速度和效率,这对于训练复杂的大型神经网络尤其重要。
  • 易于上手:PyTorch 的设计哲学强调简单性和直观性,使得新手和研究人员都能快速上手并实现复杂的模型。
  • 强大的社区和生态系统:作为一个受欢迎的开源项目,PyTorch 拥有一个活跃的社区,提供大量的教程、工具和预训练模型,可以帮助用户解决各种问题。

安装

我这里为了方便管理环境,使用conda安装,你也可以使用pip安装

先创建一个叫ai的环境:

conda create ai --python=3.8
conda activate ai
conda install pytorch torchvision torchaudio -c pytorch
conda install numpy

其中 NumPy(Numerical Python)是 Python 中用于科学计算和数值操作的一个开源库。它提供了高效的多维数组对象(称为 ndarray)、用于数组操作的函数和工具、线性代数运算、傅里叶变换等功能。NumPy 是许多 Python 科学计算库的基础,包括 Pandas、SciPy 和 PyTorch 等。

测试是否安装成功

命令行输入python回车, 然后查看pytorch输出版本:

>>> import torch
>>> print(torch.__version__)
2.3.0

张量

张量(Tensor)是一种多维数组或数据的数学表示,它是许多现代科学计算和机器学习库的核心数据结构,包括 PyTorchTensorFlow。张量可以被视为向量和矩阵的高维推广,它们提供了一种统一的方法来处理从标量到多维数组的各种数据。

张量的基本概念

  • 标量(Scalar):0维张量,仅包含一个数值,没有维度。例如,一个纯数字 7 可以被视为一个标量。
  • 向量(Vector):1维张量,由一系列数字组成,这些数字排列在一个方向上。例如,一个有 3 个元素的向量 [1, 2, 3]。
  • 矩阵(Matrix):2维张量,数字以表格形式排列,有行和列.

张量的基本使用

# 创建
# 创建一个空的张量
x = torch.empty(3, 3)
print(x)

# 创建一个随机初始化的张量
x = torch.rand(3, 3)
print(x)

# 创建一个全零的张量
x = torch.zeros(3, 3)
print(x)

# 加法

# 张量加法
x = torch.rand(3, 3)
y = torch.rand(3, 3)
z = x + y
print(z)

# 张量乘法
z = x * y
print(z)

# 转置张量
x = torch.rand(3, 3)
print(x.T)

# 使用GPU
# 将张量移动到 CUDA 设备上
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
x = torch.rand(3, 3).to(device)

torch.empty(3, 3)创建一个大小为 3x3 的张量,也就是有 9 个元素。 其中的元素值是未初始化的, 也就是说它们包含的是一些随机值, 每个元素都是一个未初始化的浮点数值。类似如下:

tensor([[2.0939e+28, 4.5392e-41, 2.4120e-41],
         [4.5392e-41, 2.4118e-41, 2.4131e-41],
         [2.4122e-41, 2.4126e-41, 2.4130e-41]])

torch.tensor 和 torch.zeros的区别

  1. torch.tensortorch.zeros 的区别:

    • torch.tensor(data): 这个函数用于从 Python 的列表、元组等数据结构创建张量。创建的张量会保留原始数据的数据类型。
    • torch.zeros(shape): 这个函数用于创建一个指定大小的张量,所有元素的初始值都为 0。创建的张量数据类型默认为 float32。
  2. torch.zeros(2, 3, 4) 创建的是一个三维张量:

    • 这个函数创建了一个 2x3x4 大小的三维张量。
    • 三维张量有3个维度(axis):
      • 第一个维度大小是 2
      • 第二个维度大小是 3
      • 第三个维度大小是 4
    • 所以这个张量总共有 2 * 3 * 4 = 24 个元素。
    • 每个元素的初始值都是 0。
  • torch.tensor(data) 根据输入的数据创建张量,保留原始数据类型。
  • torch.zeros(shape) 创建指定大小的张量,所有元素初始值为 0。
  • torch.zeros(2, 3, 4) 创建了一个 2x3x4 的三维张量,有 2、3 和 4 三个维度。

张量形状

比如torch.empty(2, 3, 4, dtype=torch.float64),这是一个三维张量,它的形状(shape)是 (2, 3, 4)。用文本表示如下:

[
  [
    [0.0, 0.0, 0.0, 0.0],
    [0.0, 0.0, 0.0, 0.0], 
    [0.0, 0.0, 0.0, 0.0]
  ],
  [
    [0.0, 0.0, 0.0, 0.0],
    [0.0, 0.0, 0.0, 0.0],
    [0.0, 0.0, 0.0, 0.0]
  ]
]

pytorch示例

张量运算

import torch

# 创建张量
tensor1 = torch.tensor([1, 2, 3])
tensor2 = torch.zeros(2, 3)

# 张量运算
result = tensor1 + tensor2
print(result)

输出:

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

矩阵加法介绍

有两个矩阵A和B:

A = [[1, 2, 3],
     [4, 5, 6]]

B = [[7, 8, 9],
     [10, 11, 12]]

相加过程:

C = A + B
  = [[1 + 7, 2 + 8, 3 + 9],
     [4 + 10, 5 + 11, 6 + 12]]
  = [[8, 10, 12],
     [14, 16, 18]]

import torch

A = torch.tensor([[1, 2, 3], 
                   [4, 5, 6]])
B = torch.tensor([[7, 8, 9],
                   [10, 11, 12]])

C = A + B
print(C)

输出:

tensor([[ 8, 10, 12],
        [14, 16, 18]])

矩阵和列表相加

如果要将一个矩阵和一个列表相加,PyTorch 会首先尝试将列表转换成一个与矩阵维度匹配的张量。 例如, torch.zeros(2, 3) + torch.tensor([1, 2, 3]) 会自动将列表 [1, 2, 3] 扩展成 [[1, 2, 3], [1, 2, 3]] 的 2x3 矩阵,然后再与原矩阵相加。

import torch

matrix = torch.zeros(2, 3)
vector = torch.tensor([1, 2, 3])

result = matrix + vector
print(result)

输出:

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

实现线性回归

import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt

# 生成随机数据
X = torch.randn(100, 1)
y = 2 * X + 3 + torch.randn(100, 1) # 线性关系 y = 2x + 3 + noise

# 定义模型
model = nn.Linear(1, 1)

# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

# 训练模型
for epoch in range(1000):
    # 前向传播
    outputs = model(X)
    loss = criterion(outputs, y)
    
    # 反向传播和优化
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

print(f"Slope: {model.weight.item()}, Intercept: {model.bias.item()}")

# 可视化结果
plt.scatter(X.squeeze(), y.squeeze())
plt.plot(X.squeeze(), outputs.squeeze(), 'r-')
plt.show()

线性回归介绍

线性回归的目标是拟合一个线性函数,来预测一个连续目标变量的值。具体来说:

  1. 数学模型:

    • 给定一组输入特征 X 和目标变量 Y,线性回归模型假设 Y 是 X 的线性函数。
    • 模型可以表示为: Y = a * X + b,其中 a 是斜率(权重), b 是截距。
  2. 目标函数:

    • 线性回归的目标是找到最佳的 a 和 b 值,使得预测值和实际值之间的误差最小。
    • 常用的损失函数是均方误差(Mean Squared Error, MSE)。
  3. 训练过程:

    • 通过优化算法(如梯度下降)来调整 a 和 b 的值,使损失函数最小化。
    • 这个过程就是模型的训练,得到最终的线性回归模型。
  4. 预测和评估:

    • 将训练好的模型应用于新的输入数据,预测目标变量的值。
    • 可以使用 R^2 等指标来评估模型的拟合效果。

均方误差

在机器学习中,均方误差(Mean Squared Error, MSE)是一种常用的损失函数或评估指标,它反映了模型预测值和真实值之间的平均差距。

具体来说:

  1. 定义:

    • 假设有 n 个样本,预测值为 y_pred,真实值为 y_true。
    • 均方误差的计算公式为: MSE = (1/n) * Σ(y_pred - y_true)^2
  2. 意义:

    • 均方误差量化了预测值和真实值之间的差距。
    • 值越小,说明模型的预测越准确,拟合效果越好。
    • 均方误差体现了模型在整体上的预测能力。
  3. 使用场景:

    • 均方误差常用于回归任务,如线性回归、神经网络回归等。
    • 它可以帮助我们评估模型在连续型目标变量预测上的性能。
  4. 优化目标:

    • 在训练模型时,我们通常会将均方误差作为损失函数,通过优化算法(如梯度下降)来最小化它。
    • 这样可以使得模型的预测值尽可能接近真实值。

举个例子,假设我们有3个样本,预测值分别为2.5、3.2、1.8,真实值为3.0、3.5、2.0。那么均方误差计算如下:

MSE = (1/3) * [(2.5 - 3.0)^2 + (3.2 - 3.5)^2 + (1.8 - 2.0)^2]
    = 0.1667

可以看到,MSE 越小,说明模型的预测效果越好。在训练过程中,我们会不断优化模型参数,使得 MSE 越来越小。F

optimizer和criterion的作用

  1. 模型训练的目标:

    • 最终目标是使模型的预测输出 y_pred 尽可能接近真实的目标输出 y
  2. 损失函数 criterion:

    • 损失函数 criterion 用于评估 y_predy 之间的差异,量化了模型的预测性能。
    • 常见的损失函数有MSE、交叉熵等,根据问题的不同而选择合适的损失函数。
  3. 优化器 optimizer:

    • 优化器 optimizer 负责根据损失函数的梯度,自动调整模型的参数(权重和偏置)。
    • 常见的优化算法有SGD、Adam、RMSProp等,每种算法都有自己的特点。
  4. 训练过程:

    • 在训练过程中,模型会不断地:
      1. 使用当前的参数计算预测输出 y_pred
      2. 计算 y_pred 和真实标签 y 之间的损失 loss
      3. 根据损失的梯度,使用优化器更新模型参数
    • 通过多次迭代,模型会学习到输入和输出之间的最优映射关系。

优化器负责调整参数,损失函数负责评估性能,最终目标就是使预测输出尽可能接近真实输出。

使用CNN进行图像分类


import torch
import torch.nn as nn
import torchvision.datasets as datasets
import torchvision.transforms as transforms

# 加载 MNIST 数据集
train_dataset = datasets.MNIST(root='./data', train=True, transform=transforms.ToTensor(), download=True)
test_dataset = datasets.MNIST(root='./data', train=False, transform=transforms.ToTensor())

# 定义 CNN 模型
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 4 * 4, 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 * 4 * 4)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# 训练模型
model = Net()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

从numpy转成tensor(张量)

NumPy(Numerical Python)是 Python 中一个非常重要的数值计算库,它提供了强大的数据结构和数值计算功能。 比如 np.ones((3,3)) 这个例子:

  1. NumPy 简介:

    • NumPy 是 Python 中事实上的标准科学计算库。
    • 它提供了高性能的多维数组对象 ndarray(n-dimensional array),以及大量的函数用于对这些数组进行操作。
  2. np.ones((3,3)):

    • 这个表达式创建了一个 3x3 的 NumPy 数组,所有元素的值都是 1。
    • np.ones() 是 NumPy 提供的一个函数,用于创建指定形状的数组,并将所有元素初始化为 1。
    • (3,3) 是这个数组的形状,表示 3 行 3 列。

具体来说:

import numpy as np

arr = np.ones((3,3))
print(arr)

输出:

[[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]

可以看到,np.ones((3,3)) 创建了一个 3x3 的二维 NumPy 数组,所有元素的值都是 1。

NumPy 的数组相比 Python 内置的列表有许多优势,比如:

  • 更高的计算效率和内存利用率
  • 丰富的数组操作函数,如切片、索引、数学运算等
  • 与其他科学计算库(如 SciPy、Matplotlib)的良好集成

NumPy 是 Python 中进行科学计算、数值运算的重要基础,对于机器学习、数据分析等领域都有广泛应用。

NumPy的应用

举两个例子,展示 NumPy 在实际应用中的强大功能。

  1. 图像处理

假设我们有一张 RGB 图像,想要对其进行灰度化处理。使用 NumPy 可以非常简单地实现:

import numpy as np
from PIL import Image

# 读取图像
img = Image.open('example.jpg')

# 将图像转换为 NumPy 数组
img_array = np.array(img)

# 计算灰度值
gray_array = np.dot(img_array[...,:3], [0.2989, 0.5870, 0.1140])

# 创建灰度图像
gray_img = Image.fromarray(gray_array.astype(np.uint8))
gray_img.save('gray_example.jpg')

在这个例子中,我们使用 NumPy 的矩阵运算(np.dot())来计算每个像素的灰度值,然后创建一个新的灰度图像。NumPy 的数组结构和高效计算能力使得这种图像处理操作变得非常简单。

  1. 线性回归

假设我们有一些样本数据,想要训练一个线性回归模型。使用 NumPy 可以很方便地实现:

import numpy as np

# 样本数据
X = np.array([[1, 2], [1, 4], [1, 6], [1, 8]])
y = np.array([5, 11, 17, 23])

# 计算模型参数
theta = np.linalg.inv(X.T @ X) @ X.T @ y

print(f"Slope: {theta[1]}, Intercept: {theta[0]}")

在这个例子中,我们使用 NumPy 的矩阵运算来计算线性回归模型的参数(斜率和截距)。NumPy 提供的高性能的矩阵乘法、逆运算等函数,使得这种数值计算变得非常高效。

这两个例子展示了 NumPy 在图像处理和机器学习领域的强大功能。NumPy 的数组结构和数值计算能力,为这些应用场景带来了高效和便利。

线性层介绍

为什么 y = 2 * X + 3 + torch.randn(100, 1) 要加上 noise?

在线性回归中,我们假设目标变量 y 与特征变量 x 存在线性关系,即 y = wx + b + ε。 其中 w 是斜率, b 是截距, ε 是随机误差项,也就是 noise。 加入 noise 是为了模拟现实世界中数据的不确定性,因为实际情况下很少会有完美的线性关系。 这样生成的数据更加贴近真实场景,有利于训练出更健壮的线性回归模型。

nn.Linear的解释

nn.Linear

  1. nn.Linear(1, 1):

    • 这个线性层有 1 个输入特征和 1 个输出目标。
    • 对应的线性函数就是 y = wx + b,其中:
      • w 是 1x1 的权重矩阵
      • b 是 1 维的偏置向量
    • 如果我们有一个输入 x(1维),经过这个线性层计算得到的输出 y 也是 1 维的。
  2. nn.Linear(2, 1):

    • 这个线性层有 2 个输入特征和 1 个输出目标。
    • 对应的线性函数就是 y = w1*x1 + w2*x2 + b,其中:
      • w 是 1x2 的权重矩阵
      • b 是 1 维的偏置向量
    • 如果我们有一个 2 维的输入 x = [x1, x2],经过这个线性层计算得到的输出 y 就是 1 维的。

现在让我们将这两种线性层应用到 y = 2x + 3 这个线性函数上:

  1. 使用 nn.Linear(1, 1):

    • 权重 w = 2
    • 偏置 b = 3
    • 所以这个线性层能学习到 y = 2x + 3 的线性关系。
  2. 使用 nn.Linear(2, 1):

    • 权重 w = [2, 0]
    • 偏置 b = 3
    • 如果我们的输入是 2 维的 x = [x, 0],那么这个线性层也能学习到 y = 2x + 3 的线性关系。

nn.Linear(1, 1) 适合于单输入单输出的线性关系,而 nn.Linear(2, 1) 则更加灵活,可以处理多输入单输出的情况。关键区别在于输入特征的维度。

nn.Linear(2, 2)

nn.Linear(2, 2) 定义了一个 2 输入 2 输出的线性层

输入特征维度为 2 输出目标维度也为 2

y1 = w11 * x1 + w12 * x2 + b1
y2 = w21 * x1 + w22 * x2 + b2

其中: w11, w12, w21, w22 是 2x2 的权重矩阵 b1, b2 是 2 维的偏置向量 也就是说,这个线性层可以同时学习两个线性函数,其中:

第一个线性函数的输出是 y1 第二个线性函数的输出是 y2

这种多输出的线性层在某些机器学习任务中非常有用,比如:

  • 同时预测多个目标变量
  • 执行多个线性回归任务
  • 作为神经网络中更复杂模型的基础组件

同理:nn.Linear(3, 2) 和nn.Linear(2, 3)

y1 = w11 * x1 + w12 * x2 + w13 * x3 + b1
y2 = w21 * x1 + w22 * x2 + w23 * x3 + b2

w11, w12, w13, w21, w22, w23 组成了 2x3 的权重矩阵 W b1, b2 是 2 维的偏置向量

输入特征是 3 维的 x = [x1, x2, x3] 输出目标是 2 维的 y = [y1, y2] 每个输出 y1 和 y2 都是 3 个输入特征的线性组合,加上各自的偏置项 这样的线性层可以学习到输入 3 个特征到输出 2 个目标之间的映射关系。

y1 = w11 * x1 + w12 * x2 + b1
y2 = w21 * x1 + w22 * x2 + b2 
y3 = w31 * x1 + w32 * x2 + b3

其中:

w11, w12, w21, w22, w31, w32 组成了 3x2 的权重矩阵 W b1, b2, b3 是 3 维的偏置向量 具体来说:

输入特征是 2 维的 x = [x1, x2] 输出目标是 3 维的 y = [y1, y2, y3] 每个输出 y1、y2、y3 都是 2 个输入特征的线性组合,加上各自的偏置项 这种 2 输入 3 输出的线性层,可以学习到从 2 个输入特征到 3 个目标输出之间的映射关系。

梯度算法

用一个简单的线性函数 y = 2x + 3 来向你解释一下梯度算法的工作原理。 假设我们要找到这个线性函数的最优参数 wb(即 y = wx + b),使得预测输出 y_pred 尽可能接近真实输出 y

我们可以定义一个损失函数 L(w, b) 来量化预测值和真实值之间的差异,比如使用均方误差(MSE):

L(w, b) = (y_pred - y)^2 = (wx + b - y)^2

现在,要找到使 L(w, b) 最小的 wb 值,就需要用到梯度下降算法:

  1. 计算梯度:

    • wb 分别计算损失函数 L 的偏导数,即梯度:
      • dL/dw = 2(wx + b - y)x
      • dL/db = 2(wx + b - y)
  2. 更新参数:

    • 沿着负梯度方向更新 wb的值:

      • w = w - lr * dL/dw
      • b = b - lr * dL/db
    • 其中 lr 是学习率,控制每次更新的步长。

  3. 迭代更新:

    • 重复上述步骤,直到损失函数 L 收敛到最小值。

通过不断迭代更新参数 wb,使损失函数 L 最小化,我们就可以找到线性函数 y = 2x + 3 的最优参数。

这就是梯度下降算法的基本工作原理:

  1. 计算损失函数对参数的梯度
  2. 沿负梯度方向更新参数
  3. 通过迭代,最终收敛到最优参数

运行报错

ModuleNotFoundError: No module named 'matplotlib'

conda install matplotlib

RuntimeError: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.

尝试将一个需要计算梯度的 PyTorch Tensor 直接转换为 NumPy 数组,这是不被允许的。 出现这个错误的原因是,在进行模型训练的过程中,你的模型参数(权重和偏置)是需要计算梯度的 Tensor 类型。而 PyTorch 中的 Tensor 是与计算图相关联的,不能直接转换为 NumPy 数组。

# 获取模型输出
outputs = model(X)

# 将输出从 Tensor 分离为 NumPy 数组
outputs_np = outputs.detach().squeeze().numpy()

# 使用 NumPy 数组绘制
plt.plot(X.squeeze().numpy(), outputs_np, "r-")

我们使用了 outputs.detach().squeeze().numpy() 来将模型输出 outputs 从计算图中分离出来,然后转换为 NumPy 数组。这样就能够顺利地将预测结果可视化了。 获取模型权重和偏置也最好分离

# 获取模型参数
slope = model.weight.detach().item()
intercept = model.bias.detach().item()