- Published on
pytorch入门
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)是一种多维数组或数据的数学表示,它是许多现代科学计算和机器学习库的核心数据结构,包括 PyTorch 和 TensorFlow。张量可以被视为向量和矩阵的高维推广,它们提供了一种统一的方法来处理从标量到多维数组的各种数据。
张量的基本概念
- 标量(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的区别
torch.tensor和torch.zeros的区别:torch.tensor(data): 这个函数用于从 Python 的列表、元组等数据结构创建张量。创建的张量会保留原始数据的数据类型。torch.zeros(shape): 这个函数用于创建一个指定大小的张量,所有元素的初始值都为 0。创建的张量数据类型默认为 float32。
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()
线性回归介绍
线性回归的目标是拟合一个线性函数,来预测一个连续目标变量的值。具体来说:
数学模型:
- 给定一组输入特征 X 和目标变量 Y,线性回归模型假设 Y 是 X 的线性函数。
- 模型可以表示为: Y = a * X + b,其中 a 是斜率(权重), b 是截距。
目标函数:
- 线性回归的目标是找到最佳的 a 和 b 值,使得预测值和实际值之间的误差最小。
- 常用的损失函数是均方误差(Mean Squared Error, MSE)。
训练过程:
- 通过优化算法(如梯度下降)来调整 a 和 b 的值,使损失函数最小化。
- 这个过程就是模型的训练,得到最终的线性回归模型。
预测和评估:
- 将训练好的模型应用于新的输入数据,预测目标变量的值。
- 可以使用 R^2 等指标来评估模型的拟合效果。
均方误差
在机器学习中,均方误差(Mean Squared Error, MSE)是一种常用的损失函数或评估指标,它反映了模型预测值和真实值之间的平均差距。
具体来说:
定义:
- 假设有 n 个样本,预测值为 y_pred,真实值为 y_true。
- 均方误差的计算公式为: MSE = (1/n) * Σ(y_pred - y_true)^2
意义:
- 均方误差量化了预测值和真实值之间的差距。
- 值越小,说明模型的预测越准确,拟合效果越好。
- 均方误差体现了模型在整体上的预测能力。
使用场景:
- 均方误差常用于回归任务,如线性回归、神经网络回归等。
- 它可以帮助我们评估模型在连续型目标变量预测上的性能。
优化目标:
- 在训练模型时,我们通常会将均方误差作为损失函数,通过优化算法(如梯度下降)来最小化它。
- 这样可以使得模型的预测值尽可能接近真实值。
举个例子,假设我们有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的作用
模型训练的目标:
- 最终目标是使模型的预测输出
y_pred尽可能接近真实的目标输出y。
- 最终目标是使模型的预测输出
损失函数
criterion:- 损失函数
criterion用于评估y_pred和y之间的差异,量化了模型的预测性能。 - 常见的损失函数有MSE、交叉熵等,根据问题的不同而选择合适的损失函数。
- 损失函数
优化器
optimizer:- 优化器
optimizer负责根据损失函数的梯度,自动调整模型的参数(权重和偏置)。 - 常见的优化算法有SGD、Adam、RMSProp等,每种算法都有自己的特点。
- 优化器
训练过程:
- 在训练过程中,模型会不断地:
- 使用当前的参数计算预测输出
y_pred - 计算
y_pred和真实标签y之间的损失loss - 根据损失的梯度,使用优化器更新模型参数
- 使用当前的参数计算预测输出
- 通过多次迭代,模型会学习到输入和输出之间的最优映射关系。
- 在训练过程中,模型会不断地:
优化器负责调整参数,损失函数负责评估性能,最终目标就是使预测输出尽可能接近真实输出。
使用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)) 这个例子:
NumPy 简介:
- NumPy 是 Python 中事实上的标准科学计算库。
- 它提供了高性能的多维数组对象
ndarray(n-dimensional array),以及大量的函数用于对这些数组进行操作。
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 在实际应用中的强大功能。
- 图像处理
假设我们有一张 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 的数组结构和高效计算能力使得这种图像处理操作变得非常简单。
- 线性回归
假设我们有一些样本数据,想要训练一个线性回归模型。使用 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
nn.Linear(1, 1):- 这个线性层有 1 个输入特征和 1 个输出目标。
- 对应的线性函数就是
y = wx + b,其中:w是 1x1 的权重矩阵b是 1 维的偏置向量
- 如果我们有一个输入
x(1维),经过这个线性层计算得到的输出y也是 1 维的。
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 这个线性函数上:
使用
nn.Linear(1, 1):- 权重
w = 2 - 偏置
b = 3 - 所以这个线性层能学习到
y = 2x + 3的线性关系。
- 权重
使用
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 来向你解释一下梯度算法的工作原理。 假设我们要找到这个线性函数的最优参数 w 和 b(即 y = wx + b),使得预测输出 y_pred 尽可能接近真实输出 y。
我们可以定义一个损失函数 L(w, b) 来量化预测值和真实值之间的差异,比如使用均方误差(MSE):
L(w, b) = (y_pred - y)^2 = (wx + b - y)^2
现在,要找到使 L(w, b) 最小的 w 和 b 值,就需要用到梯度下降算法:
计算梯度:
- 对
w和b分别计算损失函数L的偏导数,即梯度:dL/dw = 2(wx + b - y)xdL/db = 2(wx + b - y)
- 对
更新参数:
沿着负梯度方向更新
w和b的值:w = w - lr * dL/dwb = b - lr * dL/db
其中
lr是学习率,控制每次更新的步长。
迭代更新:
- 重复上述步骤,直到损失函数
L收敛到最小值。
- 重复上述步骤,直到损失函数
通过不断迭代更新参数 w 和 b,使损失函数 L 最小化,我们就可以找到线性函数 y = 2x + 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()