课程2. 用PyTorch训练神经网络与梯度下降

课程2. 用PyTorch训练神经网络与梯度下降

  • Pytorch
    • torch.Tensor
    • Pytorch 上的神经网络
      • 用于余弦函数逼近的全连接神经网络
    • 训练神经网络
  • 梯度下降
    • 最小化一个变量的函数
    • 最小化多个变量的函数
    • 使用梯度下降训练神经网络
    • 在 Pytorch 中训练神经网络
    • 从 nn.Module 类继承
  • 将计算传输到显卡
  • Pytorch 中的优化器
    • 有哪些类型的优化器?
    • 选择优化器

Pytorch

PyTorch 是当今最流行的深度学习框架之一。 “流行”的意思是,如今大多数与神经网络相关的代码都是用 PyTorch 编写的。这个框架如此受欢迎的原因在于 PyTorch 使用起来非常方便。我们稍后会看到,使用 PyTorch 与使用 Numpy 非常相似。 PyTorch 还具有非常方便且内容丰富的文档和教程。

深度学习框架快速浏览:

  • Caffee、Theano、Lasagne - 几乎不再使用;
  • Tensorflow 是 Google 推出的一个框架,在 PyTorch 出现之前一直流行到 2017-2018 年。当时,与神经网络相关的代码大部分都是用它编写的。但是它非常复杂,因此 PyTorch 很快就占据了领先地位。

Tensorflow 还具有 Keras 包装器,这使得使用神经网络变得更加容易。但是,Keras 的灵活性不如 PyTorch,因此无法完全取代它。

此外,2019 年,Tensorflow 2.0 发布,大大简化了神经网络的工作。但那时市场已经被 PyTorch 占领,因此 Tensorflow 2.0 从未流行起来。

  • PyTorch
  • JAX 是 Google 为取代 Tensorflow 而设计的新框架。

torch.Tensor

框架的基本结构是一个称为“torch.Tensor”的结构。 Tensor 是 numpy.array 的类似物,许多使用 torch.Tensor 的方法完全重复了 numpy array 的方法。

张量是 PyTorch 中所有神经网络处理的数据类型。网络层的权重矩阵是张量。我们提供给网络输入的数据也必须转换为“torch.Tensor”类型。嗯,网络输出当然也是“torch.Tensor”类型。

让我们导入该库并看看创建张量的典型方法。

import torch
import warnings
warnings.filterwarnings('ignore')
  1. 可以从工作表、数组和其他 Python 容器创建张量。
x_list = [1., 2., 3.]
x_tensor = torch.tensor(x_list)
x_tensor

输出:tensor([1., 2., 3.])

  1. 可以使用初始化函数创建张量,如在“numpy”中。
zeros_tensor = torch.zeros(2, 3)
zeros_tensor
ones_tensor = torch.ones(2, 3)
ones_tensor
eye_tensor = torch.eye(6)
eye_tensor

在这里插入图片描述

  1. 一般来说,Numpy数组有的几乎所有方法,torch.Tensor也同样有:
x_tensor = torch.tensor([[1, 2],[3, 4]
])y_tensor = torch.tensor([[-10, 3],[5, -4]
])
x_tensor + y_tensor
x_tensor @ y_tensor
# 函数 np.concatenate([x_tensor, y_tensor], axis=1)
torch.cat([x_tensor, y_tensor], dim=0)

输出:
tensor([[-9, 5],
[ 8, 0]])
tensor([[ 0, -5],
[-10, -7]])
tensor([[ 1, 2],
[ 3, 4],
[-10, 3],
[ 5, -4]])

  1. 张量可以转换回 Numpy 格式
x_tensor = torch.tensor([[1, 2],[3, 4]
])x_numpy = x_tensor.numpy()
x_numpy

输出:
array([[1, 2],
[3, 4]])

Pytorch 上的神经网络

让我们学习如何在 Pytorch 上创建一个完全连接的神经网络。

Pytorch 中的神经网络就像构造函数一样创建:有一组标准块,我们可以从中组装最终的模型。这些块中最简单的是线性层和激活函数。在接下来的课程中,我们将熟悉可以嵌入到网络架构中的新模块。

让我们回忆一下完全连接的神经网络是如何构成的。
在这里插入图片描述

y ^ = σ ( W 3 T σ ( W 2 T σ ( W 1 T X + b 1 ) + b 2 ) + b 3 ) \widehat{y} = \sigma(W_3^T \sigma(W_2^T \sigma(W_1^TX + b_1 ) + b_{2}) + b_{3}) y =σ(W3Tσ(W2Tσ(W1TX+b1)+b2)+b3)
每个全连接层都是线性变换 c ^ = W X + b \widehat{c} = WX + b c =WX+b 和某个激活函数 σ ( c ^ ) \sigma(\widehat{c}) σ(c ) 的组合:

  • 首先,将输入向量 X X X 与矩阵 W W W 相乘,并将偏移向量 b b b 添加到结果中:

c ^ = W X + b \widehat{c} = WX + b c =WX+b

  • 接下来,将获得的结果通过激活函数运行。将得到的结果作为输入向量馈送到网络的下一层。
    σ ( c ^ ) \sigma(\widehat{c}) σ(c )

神经网络被定义为一系列相互衔接的类似转换的列表。

让我们学习如何定义线性变换和激活函数。然后我们将它们结合起来,创建一个完全连接的神经网络。

使用“torch.nn.Linear”类定义表示线性变换的层。让我们创建一个具有 5 个输入神经元和 3 个输出神经元的层。

import torch.nn as nnn = 5
m = 3
linear_layer = nn.Linear(n, m)
linear_layer

输出:
Linear(in_features=5, out_features=3, bias=True)

接下来是激活函数。许多众所周知的激活函数在“nn”模块中实现,并在“nn. functional”模块中重复。我们选择其中之一,例如双曲正切(tanh)。

import torch.nn.functional as F# 你可以使用此选项
# activation = F.tanh
# 或者更正确的选项:
activation = nn.Tanh()
x_tensor = torch.tensor([[1, 2],[3, 4]
])
activation(x_tensor)

输出:
tensor([[0.7616, 0.9640],
[0.9951, 0.9993]])

注意:声明激活函数的第二种选择(使用 nn 模块)是更好的选择,因为直接从“torch.nn.functional”中使用函数有时会导致复杂模型无法正常工作。使用来自“torch.nn”的类更安全、更正确,但是,在专用于“Pytorch”的各种资源上,经常可以找到来自“torch.nn. functional”对象的使用,因此我们认为有必要讲述这种使用激活函数的方法。

现在,将使用一系列命令来指定一些带有数据的张量通过这种完全连接的层:

random_input = torch.rand(5)z = linear_layer(random_input)
output = activation(z)
output

输出:tensor([ 0.7512, -0.1367, 0.6380], grad_fn=< TanhBackward0>)

让我们关注“grad_fn=< TanhBackward0>”。所有作为神经网络中训练参数的张量上都会出现类似的注释。这意味着对于给定的张量,在网络训练过程中,将计算梯度,借助该梯度来更新张量值。我们将在本课后面详细讨论网络训练过程。

现在让我们用线性和 Tanh 块组装一个神经网络。链接多个模块的最简单方法是使用“nn.Sequential”模块。这个类将允许我们将几个模块组合成一个容器,以便当这个容器应用于某个张量时,计算将按照我们传递元素的顺序进行。也就是说,通过这种方式我们可以得到一个具有连续的Linear和Tanh层的神经网络。

FF_layer = nn.Sequential(linear_layer,activation
)
FF_layer(random_input)

输出:
tensor([ 0.7512, -0.1367, 0.6380], grad_fn=< TanhBackward0>)

我们得到了与上面单元格完全相同的结果。现在让我们以同样的方式建立一个完全连接的神经网络来解决一些简单的问题。例如,让我们尝试对余弦函数进行建模。

用于余弦函数逼近的全连接神经网络

首先,我们定义一个一维数据集。

X = torch.normal(mean=torch.zeros((1000, 1)), std= 2)
Y = torch.cos(X)
import matplotlib.pyplot as plt
import seaborn as snssns.set_theme()plt.figure(figsize=(20,7))
plt.scatter(x=X, y=Y);

在这里插入图片描述
让我们定义一个具有三层的神经网络。假设隐藏层中有五个神经元。作为激活函数,我们选择双曲正切,如上例所示。
在这里插入图片描述

import torch.nn as nn
NN = nn.Sequential(nn.Linear(1, 5, bias=True),nn.Tanh(),nn.Linear(5, 5, bias=True),nn.Tanh(),nn.Linear(5, 1, bias=True),nn.Tanh())

让我们看看未经训练的神经网络可以做什么:

X_test = torch.linspace(-6, 6, 1000)
Y_test = torch.cos(X_test)nn_prediction = NN(X_test.view(-1, 1))
nn_prediction = nn_prediction.detach().numpy()plt.figure(figsize=(20,7))
plt.scatter(x=X_test, y=Y_test, label='True Cosine');
plt.scatter(x=X_test, y=nn_prediction, label='NN predictions');
plt.legend()

在这里插入图片描述
由于我们的神经网络尚未经过训练,因此获得的结果是预期的。让我们训练她。但首先,让我们来讨论一下如何才能做到这一点。

注 1:在上一个单元格的第 4 行中我们使用了 .view() 函数。 .view().reshape() 函数的替代,它允许你更改张量的维度而不更改其中的数据。这里我们用它将长度为 n n n 的输入向量转换为大小为 ( n , 1 ) (n, 1) (n,1) 的矩阵。这种形式对于张量正确通过我们的变换是必要的。关键在于神经网络(以及任何其他机器学习模型)以大小为(n,k)的矩阵形式接收数据作为输入,其中n是数据元素的数量,k是每个数据元素的特征数量。在我们的例子中,每个元素都有 1 个特征(沿 OX 轴的坐标)。我们创建了一个由元素-特征组成的矩阵,其中有 n n n 个元素,每个元素都有 1 个特征。

注 2:在第 5 行中,我们执行了张量方法的组合 .detach().numpy()。需要 .detach() 函数从神经网络的计算图中提取张量。粗略地说,神经网络的输出仍然是网络计算图的一部分,为了将这个张量转换成numpy,我们首先需要将其从网络图中分离出来。

训练神经网络

神经网络使用梯度优化算法进行训练。此类算法的思想是基于通过网络权重依次计算损失函数的梯度(偏导数),并更新网络权重。

现在我们来一步一步分析梯度优化算法的思想。

首先让我们记住,训练神经网络时的目标是选择这样的网络超参数,使得所选损失函数的平均值在训练数据集上平均最小。
例如,我们取上一课的数据集:
在这里插入图片描述
让我们选择一个我们想要在数据上最小化的损失函数 L L L

那么网络训练的任务就是找到这样的网络参数 W 1 , b 1 , W 2 , b 2 , … W_1, b_1, W_2, b_2, \dots W1,b1,W2,b2,,使得损失函数在训练数据集元素上的平均值最小: l o s s = ∑ i = 1 n L ( y i , y ^ i ) n → m i n loss = \frac{\sum_{i=1}^n L(y_i, \widehat{y}_i)}{n} \to min loss=ni=1nL(yi,y i)min

,其中 n n n是数据中的元素数量, y i y_i yi是第 i i i个数据元素的目标变量的正确值, y ^ i \widehat{y}_i y i是第 i i i个数据元素的模型响应。
让我们再次看一下神经网络,并了解它的公式可以被视为其参数( W i W_i Wi b i b_i bi)的函数
在这里插入图片描述
y ^ = σ ( W 3 T σ ( W 2 T σ ( W 1 T X + b 1 ) + b 2 ) + b 3 ) \widehat{y} = \sigma(W_3^T \sigma(W_2^T \sigma(W_1^TX + b_1 ) + b_{2}) + b_{3}) y =σ(W3Tσ(W2Tσ(W1TX+b1)+b2)+b3)
将此公式中的 y ^ \widehat{y} y 代入公式 l o s s loss loss 可得出:

l o s s = ∑ i = 1 n L ( y i , σ ( W 3 σ ( W 3 T σ ( W 1 T X i + b 1 ) + b 2 ) + b 3 ) ) n → m i n loss = \frac{\sum_{i=1}^n L(y_i, \sigma(W_3 \sigma(W_3^T \sigma(W_1^TX_i + b_1 ) + b_{2}) + b_{3}))}{n} \to min loss=ni=1nL(yi,σ(W3σ(W3Tσ(W1TXi+b1)+b2)+b3))min

事实证明, l o s s loss loss是网络参数 W i W_i Wi b i b_i bi的函数。在训练网络时,任务是最小化许多变量的函数,即寻找 W 1 , b 1 , W 2 , b 2 , W 3 , b 3 W_1, b_1, W_2, b_2, W_3, b_3 W1,b1,W2,b2,W3,b3 的这样的值使得它们的 l o s s loss loss 值最小的问题。

我们如何解决最小化函数的问题?在 ML 课程中我们讨论了梯度下降的思想。我们先简单回顾一下。

首先,我们将研究解决函数最小化问题的方法和一元函数的梯度下降算法。然后我们将其推广到多个变量的函数。


梯度下降

最小化一个变量的函数

在这里我们将讨论如何解决最小化一个变量函数的问题

第一种方法是分析性的。对于许多函数来说,可以通过使导数等于零来找到最小点。

y = x 3 − 3 x − 4 y = x^3 - 3x - 4 y=x33x4
d y d x = 3 x 2 − 3 \frac{dy}{dx} = 3x^2-3 dxdy=3x23
x m i n = { − 1 , 1 } x_{min} = \{-1, 1\} xmin={1,1}

但是,这种方法并不适用于所有函数,即使只适用于一个变量。关于多变量函数我们能说些什么呢?

y = x 10 − 3 x 7 − 4 x 3 + 4 x y = x^{10} - 3x^7 - 4x^3 + 4x y=x103x74x3+4x
d y d x = 10 x 9 − 21 x 6 − 12 x 2 + 4 \frac{dy}{dx} = 10x^9-21x^6 -12x^2 + 4 dxdy=10x921x612x2+4
x m i n = ? x_{min} = \ ? xmin= ?

这里偏导数的两个性质对我们有帮助。对于一个变量的函数,这些属性可以表述如下:

  1. 某一点的导数的符号表示函数在该点是增还是减;
  2. 导数值的模表示函数在该点的增长/减少的速率。 f’(x) 的模值越高,函数 f 在点 x 处的减少/增加率就越高。

让我们说明一下这些属性。让我们考虑一个单变量函数 f ( x ) = x 4 + 5 x 3 − 10 x f(x) = x^4 + 5x^3 - 10x f(x)=x4+5x310x

其导数:
d f d x = 4 x 3 + 15 x 2 − 10 \frac{df}{dx} = 4x^3 + 15x^2 - 10 dxdf=4x3+15x210

让我们计算一下点 -5、-3.55、-2 处的导数的值:

d f d x ( − 5 ) = − 135 \frac{df}{dx}(-5) = -135 dxdf(5)=135
d f d x ( − 3.55 ) = 0 \frac{df}{dx}(-3.55) = ~0 dxdf(3.55)= 0
d f d x ( − 2 ) = 18 \frac{df}{dx}(-2) = 18 dxdf(2)=18

在这里插入图片描述

事实证明,计算出点 x x x 处的函数导数后,我们就知道需要从点 x x x 向哪个方向移动才能使函数值减小

我们可以利用这个性质来寻找函数的最小值。想法是这样的:让我们选择一个随机点,例如 x = 5 x=5 x=5。我们来计算一下此时函数导数的值。根据导数的符号,我们将了解需要移动到哪里才能到达最小点。我们将搬到那里。

然而,存在一个问题:我们知道最小点位于哪个位置,但不知道它有多远。也就是说,我们不知道需要从当前点移动多少才能到达最小点。

您可以尝试这样做:固定步长,比如 δ x = 1 \delta x = 1 δx=1。选择一个随机点,例如 x = 5 x=5 x=5。计算此点处函数导数的值。根据导数的符号,您可以了解需要移动到哪里才能到达最小点。朝此方向移动步长值 δ x \delta x δx。在新的点处,再次计算函数导数的值。再次,了解你现在需要移动到哪里才能达到最低点。移动 δ x \delta x δx 到那里等。

但这个想法是相当无效的。至少有两个原因:

  1. 有时起点距离最小点非常远,为了到达最小点,你必须采取非常多的步骤;
  2. 有时起点(或我们在算法过程中到达的点)非常接近最小点,以至于我们以 δ x = 1 \delta x = 1 δx=1 的步长“跳过”最小点。

在这里插入图片描述
这里提到的导数的第二个性质将会对我们有所帮助:导数值的模数说明了该点处函数的增长/减少率。这里的想法是这样的:我们每次移动的不是 δ x = 1 \delta x = 1 δx=1,而是 δ x = a l p h a ∗ d f d x \delta x = alpha*\frac{df}{dx} δx=alphadxdf。那么当我们远离最小点时,我们将移动更大的步长,如果我们接近最小点,我们将移动较小的步长。

然后,单变量函数的梯度下降算法将如下所示:

  1. 选择一个随机的起点 x x x。我们选择梯度下降步长 α \alpha α 的值
  2. 执行以下操作,直到满足停止标准:
    • 计算当前点 x x x f ( x ) f(x) f(x) 值; ——我们计算导数 d f d x \frac{d f}{d x} dxdf的值;
    • 我们转向一个新的观点:
      x = x − α d f d x x = x - \alpha \frac{df}{dx} x=xαdxdf

停止标准可能会有所不同。例如,如果当前点的导数值(以及相应的梯度下降步骤)已经变得非常小。

最小化多个变量的函数

对于具有两个或多个变量的函数,关于每个变量的偏导数具有相同的性质:

  1. 在某一点处关于变量 x 1 x_1 x1的偏导数的符号表示该点处函数相对于变量 x 1 x_1 x1是增加还是减少;
  2. 偏导数值关于变量 x 1 x_1 x1的模表示函数在该点关于变量 x 1 x_1 x1的增长/减少率。值 f ’ x 1 ( x ) f’_{x_1}(x) fx1(x) 的模越高,函数 f 相对于点 x 处的变量 x 1 x_1 x1 的减少/增加率就越高。

例如,考虑两个变量的函数 f ( x 1 , x 2 ) = 3 x 1 2 + 2 x 1 x 2 f(x_1, x_2) = 3x_1^2 + 2x_1x_2 f(x1,x2)=3x12+2x1x2

在这里插入图片描述
它关于 x 1 x_1 x1 的偏导数是:
d f d x 1 = 6 x 1 + 2 x 2 \frac{df}{dx_1} = 6x_1 + 2x_2 dx1df=6x1+2x2
其在点 x = (-20, -20) 处的值:
d f d x 1 ( − 20 , − 20 ) = 6 ⋅ ( − 20 ) + 2 ⋅ ( − 20 ) = − 160 \frac{df}{dx_1}(-20, -20) = 6 \cdot (-20) + 2\cdot (-20) = -160 dx1df(20,20)=6(20)+2(20)=160

事实证明,通过计算点 x x x 处函数关于变量 x 1 x_1 x1 的偏导数,我们知道需要从点 x x x 相对于变量 x 1 x_1 x1 向哪个方向移动才能减少函数的值。

然后,多变量函数的梯度下降算法将如下所示:

  1. 选择一个随机起点 x = ( x 1 , x 2 , . . . , x n ) x = (x_1, x_2, ..., x_n) x=(x1,x2,...,xn)。我们选择梯度下降步长 α \alpha α 的值
  2. 执行以下操作,直到满足停止标准:
  • 计算当前点 f ( x ) f(x) f(x) 处的函数值
  • 计算导数 d f d x i ( x ) \frac{d f}{d x_i}(x) dxidf(x)的值;
  • 我们转向一个新的观点:
    x i = x i − α d f d x i ( x ) x_i = x_i - \alpha \frac{df}{dx_i}(x) xi=xiαdxidf(x)

该算法被称为梯度下降,因为函数 ∇ f = ( d f d x 1 , d f d x 2 , . . . , d f d x n ) \nabla f = (\frac{df}{dx_1}, \frac{df}{dx_2}, ..., \frac{df}{dx_n}) f=(dx1df,dx2df,...,dxndf) 的偏导数向量被称为函数 f 的梯度。第二步,梯度被更新:
x = x − α ∇ f x = x - \alpha \nabla f x=xαf


使用梯度下降训练神经网络

我们再看一下神经网络公式。我们已经了解到,神经网络的损失公式可以被视为来自网络权重的许多变量的函数。
在这里插入图片描述
y ^ = σ ( W 3 T σ ( W 2 T σ ( W 1 T X + b 1 ) + b 2 ) + b 3 ) \widehat{y} = \sigma(W_3^T \sigma(W_2^T \sigma(W_1^TX + b_1 ) + b_{2}) + b_{3}) y =σ(W3Tσ(W2Tσ(W1TX+b1)+b2)+b3)
l o s s = ∑ i = 1 n L ( y i , σ ( W 3 σ ( W 3 T σ ( W 1 T X i + b 1 ) + b 2 ) + b 3 ) ) n → m i n loss = \frac{\sum_{i=1}^n L(y_i, \sigma(W_3 \sigma(W_3^T \sigma(W_1^TX_i + b_1 ) + b_{2}) + b_{3}))}{n} \to min loss=ni=1nL(yi,σ(W3σ(W3Tσ(W1TXi+b1)+b2)+b3))min
然后,神经网络的梯度优化算法如下所示:

  1. 用随机值初始化所有网络权重 W i W_i Wi b i b_i bi。我们选择梯度下降步长 α \alpha α 的值
  2. 执行以下操作,直到满足停止标准:
  • 利用参数 W i W_i Wi b i b_i bi的当前值计算 l o s s loss loss的值;
  • 我们计算偏导数 ∂ l o s s ∂ W i \frac{\partial loss}{\partial W_i} Wiloss ∂ l o s s ∂ b i \frac{\partial loss}{\partial b_i} biloss的值;
  • 更新网络参数值:
    W i = W i − α ∂ l o s s ∂ W i , b i = b i − α ∂ l o s s ∂ b i W_i = W_i - \alpha \frac{\partial loss}{\partial W_i}, \ \ b_i = b_i - \alpha \frac{\partial loss}{\partial b_i} Wi=WiαWiloss,  bi=biαbiloss

该算法是训练我们在本课程中学习的所有神经网络的基础。它有许多变体,可以在许多情况下改善算法的行为,但其思想保持不变。

我们不会讨论导数 ∂ L ∂ W i \frac{\partial L}{\partial W_i} WiL ∂ L ∂ b i \frac{\partial L}{\partial b_i} biL的具体计算方式。让我们停留在可以计算它们这一事实上。您可以在该模块的附加材料中了解有关如何计算它们的更多信息。值得一说的是,计算 ∂ L ∂ W i \frac{\partial L}{\partial W_i} WiL ∂ L ∂ b i \frac{\partial L}{\partial b_i} biL偏导数的算法被称为反向传播算法,或反向传播

我们将继续训练我们的神经网络来近似 PyTorch 上的余弦函数。

在 Pytorch 中训练神经网络

PyTorch 内部实现了计算损失函数关于网络权重的偏导数的算法。我们不需要编写它的代码。

让我们编写一个“训练”函数来训练神经网络。

import tqdm
from tqdm.auto import tqdmdef train(model, X, y, criterion, optimizer, num_epoch):'''args:model - 神经网络模型X 和 y - 训练样本criterion - 从 `torch.nn` 模块中获取的损失函数optimizer - 从 `torch.optim` 模块中获取的优化器num_epoch - 训练周期数。 这是对样本中每个对象执行的梯度步骤数。'''# 按训练周期数循环for t in tqdm(range(num_epoch)):# 让我们计算一下模型的预测y_pred = model(X)# 让我们计算一下所得预测的损失函数值loss = criterion(y_pred, y)# 让我们重置之前计算的梯度值optimizer.zero_grad()# 让我们计算新的梯度loss.backward()# optimizer.step()return model

让我们再次声明我们的网络:

NN = nn.Sequential(nn.Linear(1, 5, bias=True),nn.Tanh(),nn.Linear(5, 5, bias=True),nn.Tanh(),nn.Linear(5, 1, bias=True))

让我们声明一个损失函数和一个优化器,并对网络进行 30 个 epoch 的训练:

# 损失函数
criterion = torch.nn.MSELoss()
# 优化器
optimizer = torch.optim.Adam(NN.parameters(), lr=1e-2)NN = train(NN, X, Y, criterion,optimizer, 30)

现在让我们看看结果会怎样。

nn_prediction = NN(X_test.view(-1, 1))
nn_prediction = nn_prediction.detach().numpy()plt.figure(figsize=(20,7))
plt.scatter(x=X_test, y=Y_test, label='True Cosine');
plt.scatter(x=X_test, y=nn_prediction, label='NN predictions');
plt.legend()

在这里插入图片描述
它明显好多了,但显然需要更多的训练迭代。

NN = train(NN, X, Y, criterion, optimizer, 15)
nn_prediction = NN(X_test.view(-1, 1))
nn_prediction = nn_prediction.detach().numpy()plt.figure(figsize=(20,7))
plt.scatter(x=X_test, y=Y_test, label='True Cosine');
plt.scatter(x=X_test, y=nn_prediction, label='NN predictions');
plt.legend()

在这里插入图片描述
提高收敛性的一个可能选择是减少梯度步骤。让我们将其降低到 0.001 并运行更多训练迭代。

optimizer = torch.optim.Adam(NN.parameters(), lr=1e-5)NN = train(NN, X, Y, criterion,optimizer, 50)
nn_prediction = NN(X_test.view(-1, 1))
nn_prediction = nn_prediction.detach().numpy()plt.figure(figsize=(20,7))
plt.scatter(x=X_test, y=Y_test, label='True Cosine');
plt.scatter(x=X_test, y=nn_prediction, label='NN predictions');
plt.legend()

在这里插入图片描述
结果明显变得更好。

我们看到图形边缘的近似值存在缺陷,这主要是因为在我们的原始样本中,坐标取自正态分布,因此很少有物体位于 -3 和 +3 的边界之外,这意味着我们的神经网络训练的先例很少。

从 nn.Module 类继承

让我们再看一下如何定义我们的神经网络:

NN = nn.Sequential(nn.Linear(1, 5, bias=True),nn.Tanh(),nn.Linear(5, 5, bias=True),nn.Tanh(),nn.Linear(5, 1, bias=True),nn.Tanh())

有时像“顺序”这样的简单结构不足以创建相当复杂的模型。有时这是由于需要在计算中创建几个独立的分支,有时是因为需要记录计算的历史记录。可能还有其他原因。无论如何,如果能够干扰中间计算的结果就更好了。为此,在 Pytorch 中使用神经网络有一种更灵活的方法——那就是编写从 nn.Module 类继承的自己的类。

通过这种块的前向通道的描述以“前向”函数的规范形式出现。

class Net(nn.Module):def __init__(self, dim=1):super(Net, self).__init__()self.fc1 = nn.Linear(dim, 5)self.tanh1 = nn.Tanh()self.fc2 = nn.Linear(5, 5)self.tanh2 = nn.Tanh()self.fc3 = nn.Linear(5, 1)self.tanh3 = nn.Tanh()def forward(self, x):x = self.fc1(x)x = self.tanh1(x)x = self.fc2(x)x = self.tanh2(x)x = self.fc3(x)x = self.tanh3(x)return x
NN = Net(1)

您可以用完全相同的方式训练这样的模型——使用我们已经拥有的“训练”函数。

criterion = torch.nn.MSELoss()
optimizer = torch.optim.Adam(NN.parameters(), lr=1e-2)NN = train(NN, X.view(-1, 1), Y, criterion,optimizer, 100)
nn_prediction = NN(X_test.view(-1, 1))
nn_prediction = nn_prediction.detach().numpy()plt.figure(figsize=(20,7))
plt.scatter(x=X_test, y=Y_test, label='True Cosine');
plt.scatter(x=X_test, y=nn_prediction, label='NN predictions');
plt.legend()

在这里插入图片描述
从“nn.Module”类继承使我们能够实现模型与“Pytorch”接口的兼容性,以及使用一些附加功能。例如,我们可以以矩阵形式访问模型参数:

for param in NN.parameters():print('parameter shape: ', param.shape)

输出:
parameter shape: torch.Size([5, 1])
parameter shape: torch.Size([5])
parameter shape: torch.Size([5, 5])
parameter shape: torch.Size([5])
parameter shape: torch.Size([1, 5])
parameter shape: torch.Size([1])

将计算传输到显卡

使用图形核心是使用深度神经网络的强制性属性。图形核心可实现快速、高效的并行计算。 Pytorch 实现了将计算传输到显卡的接口。为了执行此传输,必须将模型参数和输入数据都传输到显卡。

import torch
# 此命令检查 GPU 是否可用
torch.cuda.is_available()
device = 'cuda' if torch.cuda.is_available() else 'cpu'

现在我们可以在 GPU 上执行计算了。使用 .to(device).cuda() 函数将张量和模块传输到 GPU。
注意:我们必须重复定义主要函数,因为连接硬件加速器后运行时会重新启动

import torch
import torch.nn as nnclass Net(nn.Module):def __init__(self, dim):super(Net, self).__init__()self.fc1 = nn.Linear(dim, 5)self.tanh1 = nn.Tanh()self.fc2 = nn.Linear(5, 5)self.tanh2 = nn.Tanh()self.fc3 = nn.Linear(5, 1)self.tanh3 = nn.Tanh()def forward(self, x):x = self.fc1(x)x = self.tanh1(x)x = self.fc2(x)x = self.tanh2(x)x = self.fc3(x)x = self.tanh3(x)return x
NN = Net(1)
NN = NN.to(device) # 传输至设备
NN

输出:
Net(
(fc1): Linear(in_features=1, out_features=5, bias=True)
(tanh1): Tanh()
(fc2): Linear(in_features=5, out_features=5, bias=True)
(tanh2): Tanh()
(fc3): Linear(in_features=5, out_features=1, bias=True)
(tanh3): Tanh()
)

import tqdm
from tqdm.auto import tqdmdef train(model, X, y, criterion, optimizer, num_epoch):'''args:model - 神经网络模型X 和 y - 训练样本criterion - 从 `torch.nn` 模块中获取的损失函数optimizer - 从 `torch.optim` 模块中获取的优化器num_epoch - 训练周期数。 这是对样本中每个对象执行的梯度步骤数。'''# 按训练周期数循环for t in tqdm(range(num_epoch)):# 让我们计算一下模型的预测y_pred = model(X)# 让我们计算一下所得预测的损失函数值loss = criterion(y_pred, y)# 让我们重置之前计算的梯度值optimizer.zero_grad()# 让我们计算新的梯度loss.backward()# 让我们执行梯度下降步骤optimizer.step()return model

我们再次输入余弦的数据:

X = torch.normal(mean=torch.zeros((1000, 1)), std= 2)
Y = torch.cos(X)

让我们在 GPU 上训练神经网络

criterion = torch.nn.MSELoss()
optimizer = torch.optim.Adam(NN.parameters(), lr=1e-2)NN = train(NN, X, Y, criterion,optimizer, 50)

现在要将张量转换为 tgzn 格式,我们必须在 .detach().numpy() 命令集中添加一个命令 - .cpu()

import seaborn as sns
import matplotlib.pyplot as plt
sns.set_theme()X_test = torch.linspace(-6, 6, 1000).to(device)
Y_test = torch.cos(X_test)nn_prediction = NN(X_test.view(-1, 1))
nn_prediction = nn_prediction.cpu().detach().numpy()plt.figure(figsize=(20,7))
plt.scatter(x=X_test.cpu(), y=Y_test.cpu(), label='True Cosine');
plt.scatter(x=X_test.cpu(), y=nn_prediction, label='NN predictions');
plt.legend()

在这里插入图片描述

Pytorch 中的优化器

有哪些类型的优化器?

事实证明,时至今日,许多先进的梯度优化算法都是基于梯度下降的基本思想发明的。其中许多都是在‘pytorch’中实现的。以下是此类算法的简短列表,它们在 torch.optim 模块中作为类实现:

  • ADAM(带动量的自适应梯度)是默认使用的事实上的优化方法。实践证明,在其他条件相同的情况下,它在大多数任务中都是最好的优化器。 pytorch 中的类:torch.optim.Adam
  • Adagrad(自适应子梯度)——一种独立选择梯度下降“步长”的方法。 pytorch 中的类:torch.optim.Adagrad
  • RMSProp 是另一种根据训练历史调整梯度前的乘数的方法。 pytorch 中的类:torch.optim.RMSprop
  • Adadelta 是一种类似于 adagrad 的算法。 pytorch 中的类:torch.optim.Adadelta

Pytorch 还有许多其他优化器。 在每个类别的文档中,您可以找到相关文章的链接。

选择优化器

让我们再次重复一遍,“Adam”在绝大多数实际应用中都表现出了最好的效果。但有时尝试几种不同的优化器是有意义的。以下是使用其他优化器的一些建议:

  • SGD 适用于简单问题,并且适用于大型数据集
  • RMSProp 适用于解决非平稳目标函数问题,并且可以自适应地改变每个参数的学习率,这使其适用于循环神经网络
  • Adagrad 对于稀疏数据非常有效,因为它会根据参数更新率调整学习率

最后,优化器的选择应基于问题的具体情况和模型架构。建议尝试多种优化器及其设置以获得最佳结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/25050.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IP-----动态路由OSPF(2)

这只是IP的其中一块内容&#xff0c;IP还有更多内容可以查看IP专栏&#xff0c;前一章内容为动态路由OSPF &#xff0c;可通过以下路径查看IP-----动态路由OSPF-CSDN博客,欢迎指正 注意&#xff01;&#xff01;&#xff01;本部分内容较多所以分成了两部分在上一章 5.动态路…

手机打电话时如何识别对方按下的DTMF按键的字符-安卓AI电话机器人

手机打电话时如何识别对方按下的DTMF按键的字符 --安卓AI电话机器人 一、前言 前面的篇章中&#xff0c;使用蓝牙电话拦截手机通话的声音&#xff0c;并对数据加工&#xff0c;这个功能出来也有一段时间了。前段时间有试用的用户咨询说&#xff1a;有没有办法在手机上&#xff…

深入剖析:自定义实现C语言中的atoi函数

在C语言的标准库中&#xff0c; atoi 函数是一个非常实用的工具&#xff0c;它能够将字符串形式的数字转换为对应的整数。然而&#xff0c;当我们深入探究其实现原理时&#xff0c;会发现其中蕴含着许多有趣的编程技巧和细节。本文将详细讲解如何自定义实现一个类似 atoi 功能的…

PyTorch 源码学习:GPU 内存管理之它山之石——TensorFlow BFC 算法

TensorFlow 和 PyTorch 都是常用的深度学习框架&#xff0c;各自有一套独特但又相似的 GPU 内存管理机制&#xff08;BFC 算法&#xff09;。它山之石可以攻玉。了解 TensorFlow 的 BFC 算法有助于学习 PyTorch 管理 GPU 内存的精妙之处。本文重点关注 TensorFlow BFC 算法的核…

流式输出方案:sse与websocket的使用

1、sse(Server-Sent Events) SSE是一种允许服务器向浏览器推送实时更新的技术。它基于HTTP协议&#xff0c;是一种单向的通信方式 单向通信基于HTTP自动重连&#xff08;内置了自动重连机制&#xff0c;当连接断开时&#xff0c;浏览器会自动尝试重新连接&#xff09; 1.1 …

基于定制开发开源AI大模型S2B2C商城小程序的商品选品策略研究

摘要&#xff1a;随着电子商务的蓬勃发展和技术的不断进步&#xff0c;商品选品在电商领域中的重要性日益凸显。特别是在定制开发开源AI大模型S2B2C商城小程序的环境下&#xff0c;如何精准、高效地选择推广商品&#xff0c;成为商家面临的一大挑战。本文首先分析了商品选品的基…

时序论文41 | Medformer:基于多粒度patch的时序分类模型

论文标题&#xff1a;Medformer: A Multi-Granularity Patching Transformer for Medical Time-Series Classification 论文链接&#xff1a;https://arxiv.org/abs/2405.19363 代码链接&#xff1a;https://github.com/DL4mHealth/Medformer. &#xff08;后台回复“交流”…

建筑兔零基础自学python记录32|学过的函数代码记录19-25

这是之前matplotlib用过的代码记录&#xff0c;以防忘记记录一下: 19.price_data 是一个 NumPy 记录股票数组。每一列可以有不同的数据类型&#xff0c;并且每列都有一个对应的字段名。&#xff08;类似excel的表中的列&#xff09; date&#xff1a;存储交易日期&#xff0c…

面试八股文--数据库基础知识总结(2) MySQL

本文介绍关于MySQL的相关面试知识 一、关系型数据库 1、定义 关系型数据库&#xff08;Relational Database&#xff09;是一种基于关系模型的数据库管理系统&#xff08;DBMS&#xff09;&#xff0c;它将数据存储在表格&#xff08;表&#xff09;中&#xff0c;并通过表格…

Linux:目录创建命令mkdir功能及用法详解

mkdir是Make Directory的缩写&#xff0c;该命令在 Linux 中用于创建目录&#xff08;单层或多层&#xff09;&#xff0c;在Linux中很常用&#xff0c;可以说是基础性命令。该命令支持&#xff1a;1&#xff09;创建单层或多层目录2&#xff09;直接指定目录权限。本文详细介绍…

2024年国赛高教杯数学建模D题反潜航空深弹命中概率问题解题全过程文档及程序

2024年国赛高教杯数学建模 D题 反潜航空深弹命中概率问题 原题再现 应用深水炸弹&#xff08;简称深弹&#xff09;反潜&#xff0c;曾是二战时期反潜的重要手段&#xff0c;而随着现代军事技术的发展&#xff0c;鱼雷已成为现代反潜作战的主要武器。但是&#xff0c;在海峡或…

Visual Studio Code 远程开发方法

方法1 共享屏幕远程控制&#xff0c;如 to desk, 向日葵 &#xff0c;像素太差&#xff0c;放弃 方法2 内网穿透 ssh 第二个方法又很麻烦&#xff0c;尤其是对于 windows 电脑&#xff0c;要使用 ssh 还需要额外安装杂七杂八的东西&#xff1b;并且内网穿透服务提供商提供的…

SQLite 安装教程以及可视化工具介绍

目录 简述 1. Windows 系统安装 1.1 下载预编译的二进制文件 1.2 解压文件 1.3 配置环境变量 1.4 验证安装 2. GUI 可视化工具 2.1 免费工具 2.1.1 DB Browser for SQLite 2.1.2 SQLiteStudio 2.1.3 SQLite Expert 2.1.4 SQLiteGUI 2.1.5 Antares SQL 2.1.6 DbGa…

smolagents学习笔记系列(五)Tools-in-depth-guide

这篇文章锁定官网教程中的 Tools-in-depth-guide 章节&#xff0c;主要介绍了如何详细构造自己的Tools&#xff0c;在之前的博文 smolagents学习笔记系列&#xff08;二&#xff09;Agents - Guided tour 中我初步介绍了下如何将一个函数或一个类声明成 smolagents 的工具&…

LLM2CLIP论文学习笔记:强大的语言模型解锁更丰富的视觉表征

1. 写在前面 今天分享的一篇论文《LLM2CLIP: P OWERFUL L ANGUAGE M ODEL U NLOCKS R ICHER V ISUAL R EPRESENTATION》&#xff0c; 2024年9月微软和同济大学的一篇paper&#xff0c; 是多模态领域的一篇工作&#xff0c;主要探索了如何将大模型融合到Clip模型里面来进一步提…

一键部署DeepSeek

腾讯Cloud Studio提供DeepSeek一键部署功能&#xff0c;0行代码&#xff0c;秒级部署使用&#xff01; 重点是每月免费提供10000分钟&#xff01; 不用等待模型下载&#xff0c;创建即可使用。 内置 Ollama、DeepSeek-R1 1.5B、7B、8B、14B 及 32B 模型。 热门模板 AI模板 前…

【计算机网络】IP协议

目录 1. 协议头格式 2. 网段划分 3. 特殊的IP 4. 公网IP && 内网IP 总结 网络层的IP协议主要解决的是什么问题&#xff1f;——将数据包从B主机发送给C主机&#xff1b;传输层协议tcp提供可靠的策略&#xff1b;网络层IP协议提供数据数据传输的能力&#xff1b; 发…

YOLOv12 ——基于卷积神经网络的快速推理速度与注意力机制带来的增强性能结合

概述 实时目标检测对于许多实际应用来说已经变得至关重要&#xff0c;而Ultralytics公司开发的YOLO&#xff08;You Only Look Once&#xff0c;只看一次&#xff09;系列一直是最先进的模型系列&#xff0c;在速度和准确性之间提供了稳健的平衡。注意力机制的低效阻碍了它们在…

2022年全国职业院校技能大赛网络系统管理赛项模块A:网络构建(样题6)-网络部分解析-附详细代码

目录 附录1:拓扑图 附录2:地址规划表 1.SW1 2.SW2 3.SW3 4.SW4 5.VSU 6.SW7 7.R1 8.R2 9.R3 10.AC1 11.AC2 12.EG1 13.EG2 附录1:拓扑图 附录2:地址规划表

C#实现本地Deepseek模型及其他模型的对话

前言 1、C#实现本地AI聊天功能 WPFOllamaSharpe实现本地聊天功能,可以选择使用Deepseek 及其他模型。 2、此程序默认你已经安装好了Ollama。 在运行前需要线安装好Ollama,如何安装请自行搜索 Ollama下载地址&#xff1a; https://ollama.org.cn Ollama模型下载地址&#xf…