当前位置: 首页 > news >正文

深度学习-Torch框架-2

八、自动微分

自动微分模块torch.autograd负责自动计算张量操作的梯度,具有自动求导功能。自动微分模块是构成神经网络训练的必要模块,可以实现网络权重参数的更新,使得反向传播算法的实现变得简单而高效。

1. 基础概念

  1. 张量

    Torch中一切皆为张量,属性requires_grad决定是否对其进行梯度计算。默认是 False,如需计算梯度则设置为True。

  2. 计算图

    torch.autograd通过创建一个动态计算图来跟踪张量的操作,每个张量是计算图中的一个节点,节点之间的操作构成图的边。

    在 PyTorch 中,当张量的 requires_grad=True 时,PyTorch 会自动跟踪与该张量相关的所有操作,并构建计算图。每个操作都会生成一个新的张量,并记录其依赖关系。当设置为 True 时,表示该张量在计算图中需要参与梯度计算,即在反向传播(Backpropagation)过程中会自动计算其梯度;当设置为 False 时,不会计算梯度。

    例如:

    z=x\ast y

        loss=z.sum()

在上述代码中,x 和 y 是输入张量,即叶子节点,z 是中间结果,loss 是最终输出。每一步操作都会记录依赖关系:

z = x * y:z 依赖于 x 和 y。

loss = z.sum():loss 依赖于 z。

这些依赖关系形成了一个动态计算图,如下所示:

  x       y
       \     /
        \   /
         \ /
          z
          |
          |
          v
        loss

叶子节点

在 PyTorch 的自动微分机制中,叶子节点(leaf node) 是计算图中:

  • 由用户直接创建的张量,并且它的 requires_grad=True。

  • 这些张量是计算图的起始点,通常作为模型参数或输入变量。

特征:

  • 没有由其他张量通过操作生成。

  • 如果参与了计算,其梯度会存储在 leaf_tensor.grad 中。

  • 默认情况下,叶子节点的梯度不会自动清零,需要显式调用 optimizer.zero_grad() 或 x.grad.zero_() 清除。

如何判断一个张量是否是叶子节点?

通过 tensor.is_leaf 属性,可以判断一个张量是否是叶子节点。

x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)  # 叶子节点
y = x ** 2  # 非叶子节点(通过计算生成)
z = y.sum()print(x.is_leaf)  # True
print(y.is_leaf)  # False
print(z.is_leaf)  # False

叶子节点与非叶子节点的区别

特性叶子节点非叶子节点
创建方式用户直接创建的张量通过其他张量的运算生成
is_leaf 属性TrueFalse
梯度存储梯度存储在 .grad 属性中梯度不会存储在 .grad,只能通过反向传播传递
是否参与计算图是计算图的起点是计算图的中间或终点
删除条件默认不会被删除在反向传播后,默认被释放(除非 retain_graph=True)

detach():张量 x 从计算图中分离出来,返回一个新的张量,与 x 共享数据,但不包含计算图(即不会追踪梯度)。

特点

  • 返回的张量是一个新的张量,与原始张量共享数据。

  • 对 x.detach() 的操作不会影响原始张量的梯度计算。

  • 推荐使用 detach(),因为它更安全,且在未来版本的 PyTorch 中可能会取代 data。

x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
y = x.detach()  # y 是一个新张量,不追踪梯度y += 1  # 修改 y 不会影响 x 的梯度计算
print(x)  # tensor([1., 2., 3.], requires_grad=True)
print(y)  # tensor([2., 3., 4.])
  1. 反向传播

    使用tensor.backward()方法执行反向传播,从而计算张量的梯度。这个过程会自动计算每个张量对损失函数的梯度。例如:调用 loss.backward() 从输出节点 loss 开始,沿着计算图反向传播,计算每个节点的梯度。

  2. 梯度

    计算得到的梯度通过tensor.grad访问,这些梯度用于优化模型参数,以最小化损失函数。

2. 计算梯度

使用tensor.backward()方法执行反向传播,从而计算张量的梯度

2.1 标量梯度计算

参考代码如下:

import torchdef test001():# 1. 创建张量:必须为浮点类型x = torch.tensor(1.0, requires_grad=True)# 2. 操作张量y = x ** 2# 3. 计算梯度,也就是反向传播y.backward()# 4. 读取梯度值print(x.grad)  # 输出: tensor(2.)if __name__ == "__main__":test001()

2.2 向量梯度计算

案例:

def test003():# 1. 创建张量:必须为浮点类型x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)# 2. 操作张量y = x ** 2# 3. 计算梯度,也就是反向传播y.backward()# 4. 读取梯度值print(x.grad)if __name__ == "__main__":test003()

错误预警:RuntimeError: grad can be implicitly created only for scalar outputs

由于 y 是一个向量,我们需要提供一个与 y 形状相同的向量作为 backward() 的参数,这个参数通常被称为 梯度张量(gradient tensor),它表示 y 中每个元素的梯度。

def test003():# 1. 创建张量:必须为浮点类型x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)# 2. 操作张量y = x ** 2# 3. 计算梯度,也就是反向传播y.backward(torch.tensor([1.0, 1.0, 1.0]))# 4. 读取梯度值print(x.grad)# 输出# tensor([2., 4., 6.])if __name__ == "__main__":test003()

我们也可以将向量 y 通过一个标量损失函数(如 y.mean())转换为一个标量,反向传播时就不需要提供额外的梯度向量参数了。这是因为标量的梯度是明确的,直接调用 .backward() 即可。

import torchdef test002():# 1. 创建张量:必须为浮点类型x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)# 2. 操作张量y = x ** 2# 3. 损失函数loss = y.mean()# 4. 计算梯度,也就是反向传播loss.backward()# 5. 读取梯度值print(x.grad)if __name__ == "__main__":test002()

调用 loss.backward() 从输出节点 loss 开始,沿着计算图反向传播,计算每个节点的梯度。

损失函数loss=mean(y)=\frac{1}{n}\sum_{i=1}^{n}y_{i},其中 n=3。

对于每个 y_{i},其梯度为\frac{\partial loss}{\partial y_{i}}=\frac{1}{n}=\frac{1}{3}

对于每个 x_{i},其梯度为:\frac{\partial loss}{\partial x_{i}}=\frac{\partial loss}{\partial y_{i}}\times \frac{\partial y_{i}}{\partial x_{i}}=\frac{1}{3}\times 2x_{i}=\frac{2x_{i}}{3}

所以,x.grad 的值为:

\left [ \frac{2\times 1.0}{3},\frac{2\times 2.0}{3},\frac{2\times 3.0}{3} \right ]=\left [ \frac{2}{3},\frac{4}{3},2 \right ]\approx \left [ 0.6667,1.3333,2.0000 \right ]

2.3 多标量梯度计算

参考代码如下

import torchdef test003():# 1. 创建两个标量x1 = torch.tensor(5.0, requires_grad=True, dtype=torch.float64)x2 = torch.tensor(3.0, requires_grad=True, dtype=torch.float64)# 2. 构建运算公式y = x1**2 + 2 * x2 + 7# 3. 计算梯度,也就是反向传播y.backward()# 4. 读取梯度值print(x1.grad, x2.grad)# 输出:# tensor(10., dtype=torch.float64) tensor(2., dtype=torch.float64)if __name__ == "__main__":test003()

2.4 多向量梯度计算

代码参考如下

import torchdef test004():# 创建两个张量,并设置 requires_grad=Truex = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)y = torch.tensor([4.0, 5.0, 6.0], requires_grad=True)# 前向传播:计算 z = x * yz = x * y# 前向传播:计算 loss = z.sum()loss = z.sum()# 查看前向传播的结果print("z:", z)  # 输出: tensor([ 4., 10., 18.], grad_fn=<MulBackward0>)print("loss:", loss)  # 输出: tensor(32., grad_fn=<SumBackward0>)# 反向传播:计算梯度loss.backward()# 查看梯度print("x.grad:", x.grad)  # 输出: tensor([4., 5., 6.])print("y.grad:", y.grad)  # 输出: tensor([1., 2., 3.])if __name__ == "__main__":test004()

3. 梯度上下文控制

梯度计算的上下文控制和设置对于管理计算图、内存消耗、以及计算效率至关重要。下面我们学习下Torch中与梯度计算相关的一些主要设置方式。

3.1 控制梯度计算

梯度计算是有性能开销的,有些时候我们只是简单的运算,并不需要梯度

import torchdef test001():x = torch.tensor(10.5, requires_grad=True)print(x.requires_grad)  # True# 1. 默认y的requires_grad=Truey = x**2 + 2 * x + 3print(y.requires_grad)  # True# 2. 如果不需要y计算梯度-with进行上下文管理with torch.no_grad():y = x**2 + 2 * x + 3print(y.requires_grad)  # False# 3. 如果不需要y计算梯度-使用装饰器@torch.no_grad()def y_fn(x):return x**2 + 2 * x + 3y = y_fn(x)print(y.requires_grad)  # False# 4. 如果不需要y计算梯度-全局设置,需要谨慎torch.set_grad_enabled(False)y = x**2 + 2 * x + 3print(y.requires_grad)  # Falseif __name__ == "__main__":test001()

3.2 累计梯度

默认情况下,当我们重复对一个自变量进行梯度计算时,梯度是累加的

import torchdef test002():# 1. 创建张量:必须为浮点类型x = torch.tensor([1.0, 2.0, 5.3], requires_grad=True)# 2. 累计梯度:每次计算都会累计梯度for i in range(3):y = x**2 + 2 * x + 7z = y.mean()z.backward()print(x.grad)if __name__ == "__main__":test002()

输出结果:

tensor([1.3333, 2.0000, 4.2000])
tensor([2.6667, 4.0000, 8.4000])
tensor([ 4.0000,  6.0000, 12.6000])

3.3 梯度清零

大多数情况下是不需要梯度累加的,奇葩的事情还是需要解决的~

import torchdef test002():# 1. 创建张量:必须为浮点类型x = torch.tensor([1.0, 2.0, 5.3], requires_grad=True)# 2. 累计梯度:每次计算都会累计梯度for i in range(3):y = x**2 + 2 * x + 7z = y.mean()# 2.1 反向传播之前先对梯度进行清零if x.grad is not None:x.grad.zero_()z.backward()print(x.grad)if __name__ == "__main__":test002()# 输出:
# tensor([1.3333, 2.0000, 4.2000])
# tensor([1.3333, 2.0000, 4.2000])
# tensor([1.3333, 2.0000, 4.2000])

3.4 案例1-求函数最小值

通过梯度下降找到函数最小值

import torch
from matplotlib import pyplot as plt
import numpy as npdef test01():x = np.linspace(-10, 10, 100)y = x ** 2plt.plot(x, y)plt.show()def test02():# 初始化自变量Xx = torch.tensor([3.0], requires_grad=True, dtype=torch.float)# 迭代轮次epochs = 50# 学习率lr = 0.1list = []for i in range(epochs):# 计算函数表达式y = x ** 2# 反向传播y.backward()# 梯度下降,不需要计算梯度,为什么?with torch.no_grad():x -= lr * x.grad# 梯度清零x.grad.zero_()print('epoch:', i, 'x:', x.item(), 'y:', y.item())list.append((x.item(), y.item()))# 散点图,观察收敛效果x_list = [l[0] for l in list]y_list = [l[1] for l in list]plt.scatter(x=x_list, y=y_list)plt.show()if __name__ == "__main__":test01()test02()

代码解释:

# 梯度下降,不需要计算梯度
with torch.no_grad():x -= lr * x.grad

如果去掉梯度控制会有什么结果?

代码中去掉梯度控制会报异常:

RuntimeError: a leaf Variable that requires grad is being used in an in-place operation.

因为代码中x是叶子节点(叶子张量),是计算图的开始节点,并且设置需要梯度。在pytorch中不允许对需要梯度的叶子变量进行原地操作。因为这会破坏计算图,导致梯度计算错误。

在代码中,x 是一个叶子变量(即直接定义的张量,而不是通过其他操作生成的张量),并且设置了 requires_grad=True,因此不能直接通过 -= 进行原地更新。

解决方法

为了避免这个错误,可以使用以下两种方法:

方法 1:使用 torch.no_grad() 上下文管理器

在更新参数时,使用 torch.no_grad() 禁用梯度计算,然后通过非原地操作更新参数。

with torch.no_grad():a -= lr * a.grad

方法 2:使用 data 属性或detach()

通过 x.data 访问张量的数据部分(不涉及梯度计算),然后进行原地操作。

x.data -= lr * x.grad

x.data返回一个与 a 共享数据的张量,但不包含计算图

特点

  • 返回的张量与原始张量共享数据。

  • 对 x.data 的操作是原地操作(in-place),可能会影响原始张量的梯度计算。

  • 不推荐使用 data,因为它可能会导致意外的行为(如梯度计算错误)。

能不能将代码修改为:

x = x - lr * x.grad

答案是不能,以上代码中=左边的x变量是由右边代码计算得出的,就不是叶子节点了,从计算图中被剥离出来后没有了梯度,执行

x.grad.zero_()

报错:AttributeError: 'NoneType' object has no attribute 'zero_'

总结:以上方均不推荐,正确且推荐的做法是使用优化器,优化器后续会讲解。

3.5 案例2-函数参数求解

def test02():# 定义数据x = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float)y = torch.tensor([3, 5, 7, 9, 11], dtype=torch.float)# 定义模型参数 a 和 b,并初始化a = torch.tensor([1], dtype=torch.float, requires_grad=True)b = torch.tensor([1], dtype=torch.float, requires_grad=True)# 学习率lr = 0.1# 迭代轮次epochs = 1000for epoch in range(epochs):# 前向传播:计算预测值 y_predy_pred = a * x + b# 定义损失函数loss = ((y_pred - y) ** 2).mean()# 反向传播:计算梯度loss.backward()# 梯度下降with torch.no_grad():a -= lr * a.gradb -= lr * b.grada.grad.zero_()b.grad.zero_()if (epoch + 1) % 10 == 0:print(f'Epoch [{epoch + 1}/{epochs}], Loss: {loss.item():.4f}')print(f'a: {a.item()}, b: {b.item()}')

代码逻辑:

在 PyTorch 中,所有的张量操作都会被记录在一个计算图中。对于代码:

y_pred = a * x + b
loss = ((y_pred - y) ** 2).mean()

计算图如下:

a → y_pred → loss
x ↗
b ↗

  • a 和 b 是需要计算梯度的叶子张量(requires_grad=True)。

  • y_pred 是中间结果,依赖于 a 和 b。

  • loss 是最终的标量输出,依赖于 y_pred。

当调用 loss.backward() 时,PyTorch 会从 loss 开始,沿着计算图反向传播,计算 loss 对每个需要梯度的张量(如 a 和 b)的梯度。

计算 loss 对 y_pred 的梯度

loss = ((y_pred - y)^ 2).mean()=\frac{1}{n}\Sigma_{i=1}^n(y\_pred_i - y_i)^2

求损失函数关于 y_pred 的梯度(即偏导数组成的向量)。由于 loss 是 y_pred 的函数,我们需要对每个y\_pred_i求偏导数,并将它们组合成一个向量。

应用链式法则和常数求导规则,对于每个 (y\_pred_i-y_i)^2 项,梯度向量的每个分量是:

\frac{\partial loss}{\partial y\_pred_i}=\frac{2}{n}(y\_pred_i-y_i)

将结果组合成一个向量,我们得到:

\frac{\partial loss}{\partial y\_pred} = [\frac{2}{n} (y\_pred_1-y_1), \frac{2}{n} (y\_pred_2-y_2),...,\frac{2}{n}(y\_pred_n-y_n)]\\ =\frac{2}{n} (y\_pred-y)

其中n=5,y_pred和y均为向量。

计算 y_pred 对 a 和 b 的梯度:

对 a 求导:\frac{\partial ypred}{\partial a}=x,x为向量

对 b 求导:\frac{\partial ypred}{\partial b}=1

根据链式法则,loss 对 a 的梯度为:

\frac{\partial loss}{\partial a}=\frac{\partial loss}{\partial y\_pred}\cdot \frac{\partial y\_pred}{\partial a} = \frac{2}{n} (\partial y\_pred-y)x

loss 对 b 的梯度为:

\frac{\partial loss}{\partial b}=\frac{\partial loss}{\partial y\_pred}\cdot \frac{\partial y\_pred}{\partial b} = \frac{2}{n} (\partial y\_pred-y)

第一次迭代:

前向传播:

y_pred = a * x + b = [1*1 + 1, 1*2 + 1, 1*3 + 1, 1*4 + 1, 1*5 + 1] = [2, 3, 4, 5, 6]loss = ((y_pred - y) ** 2).mean() = ((2-3)^2 + (3-5)^2 + (4-7)^2 + (5-9)^2 + (6-11)^2) / 5 = (1 + 4 + 9 + 16 + 25) / 5 = 11.0

反向传播:

∂loss/∂y_pred = 2/5 * (y_pred - y) = 2/5 * [-1, -2, -3, -4, -5] = [-0.4, -0.8, -1.2, -1.6, -2.0]a.grad = ∂loss/∂a = ∂loss/∂y_pred * x = [-0.4*1, -0.8*2, -1.2*3, -1.6*4, -2.0*5] = [-0.4, -1.6, -3.6, -6.4, -10.0]

对 a.grad 求和(因为 a 是标量):a.grad = -0.4 -1.6 -3.6 -6.4 -10.0 = -22.0

b.grad = ∂loss/∂b = ∂loss/∂y_pred * 1 = [-0.4, -0.8, -1.2, -1.6, -2.0]

对 b.grad 求和(因为 b 是标量):b.grad = -0.4 -0.8 -1.2 -1.6 -2.0 = -6.0

梯度更新:

a -= lr * a.grad = 1 - 0.1 * (-22.0) = 1 + 2.2 = 3.2b -= lr * b.grad = 1 - 0.1 * (-6.0) = 1 + 0.6 = 1.6

代码运行结果:

Epoch [10/100], Loss: 3020.7896
Epoch [20/100], Loss: 1550043.3750
Epoch [30/100], Loss: 795369408.0000
Epoch [40/100], Loss: 408125767680.0000
Epoch [50/100], Loss: 209420457869312.0000
Epoch [60/100], Loss: 107459239932329984.0000
Epoch [70/100], Loss: 55140217861896667136.0000
Epoch [80/100], Loss: 28293929961149737992192.0000
Epoch [90/100], Loss: 14518387713533614273593344.0000
Epoch [100/100], Loss: 7449779870375595263567855616.0000
a: -33038608105472.0, b: -9151163924480.0

损失函数在训练过程中越来越大,表明模型的学习过程出现了问题。这是因为学习率(Learning Rate)过大,参数更新可能会“跳过”最优值,导致损失函数在最小值附近震荡甚至发散。

解决方法:调小学习率,将lr=0.01

代码运行结果:

Epoch [10/100], Loss: 0.0965
Epoch [20/100], Loss: 0.0110
Epoch [30/100], Loss: 0.0099
Epoch [40/100], Loss: 0.0092
Epoch [50/100], Loss: 0.0086
Epoch [60/100], Loss: 0.0081
Epoch [70/100], Loss: 0.0075
Epoch [80/100], Loss: 0.0071
Epoch [90/100], Loss: 0.0066
Epoch [100/100], Loss: 0.0062
a: 1.9492162466049194, b: 1.1833451986312866

可以看出loss损失函数值在收敛,a接近2,b接近1

将epochs=500

代码运行结果:

Epoch [440/500], Loss: 0.0006
Epoch [450/500], Loss: 0.0006
Epoch [460/500], Loss: 0.0005
Epoch [470/500], Loss: 0.0005
Epoch [480/500], Loss: 0.0005
Epoch [490/500], Loss: 0.0004
Epoch [500/500], Loss: 0.0004
a: 1.986896276473999, b: 1.0473089218139648

a已经无限接近2,b无限接近1

九、模型定义组件

模型(神经网络,深度神经网络,深度学习)定义组件帮助我们在 PyTorch 中定义、训练和评估模型等。

在进行模型训练时,有三个基础的概念我们需要颗粒度对齐下:

名词定义
Epoch使用训练集的全部数据对模型进行一次完整训练,被称为“一代训练”
Batch使用训练集中的一小部分样本对模型权重进行一次反向传播的参数更新,这一小部分样本被称为“一批数据”
Iteration使用一个Batch数据对模型进行一次参数更新的过程,被称为“一次训练”

1. 基本组件认知

先初步认知,他们用法基本一样的,后续在学习深度神经网络和卷积神经网络的过程中会很自然的学到更多组件!

官方文档:torch.nn — PyTorch 2.6 documentation

1.1 损失函数组件

PyTorch已内置多种损失函数,在构建神经网络时随用随取!

文档:torch.nn — PyTorch 2.6 documentation

常用损失函数举例:

1.均方误差损失(MSE Loss)

  • 函数: torch.nn.MSELoss

  • 公式:

MSE=\frac{1}{N}\sum_{i=1}^{N}(y_i-\hat{y_i})^2

  • 适用场景: 通常用于回归任务,例如预测连续值。

  • 特点: 对异常值敏感,因为误差的平方会放大较大的误差。

2.L1 损失(L1 Loss)

也叫做MAE(Mean Absolute Error,平均绝对误差)

  • 函数: torch.nn.L1Loss

  • 公式:

L1=\frac{1}{N}\sum_{i=1}^{N}|y_i-\hat{y_i}|

  • 适用场景: 用于回归任务,对异常值的敏感性较低。

  • 特点: 比 MSE 更鲁棒,但计算梯度时可能不稳定。

3.交叉熵损失(Cross-Entropy Loss)

  • 函数: torch.nn.CrossEntropyLoss

  • 参数:reduction:mean-平均值,sum-总和

  • 公式:

CE=-\sum _iy_ilog(\hat{y_i})

  • 适用场景: 用于多分类任务,输入是未经 softmax 处理的 logits。

  • 特点: 自带 softmax 操作,适合分类任务,能够有效处理类别不平衡问题。

4.二元交叉熵损失(Binary Cross-Entropy Loss)

  • 函数: torch.nn.BCELoss 或 torch.nn.BCEWithLogitsLoss

  • 参数:reduction:mean-平均值,sum-总和

  • 公式:

BCE=-\frac{1}{N}\sum_i[y_ilog(\hat{y_i})+(1-y_i)log(1-\hat{y_i})]

  • 适用场景: 用于二分类任务。

  • 特点: BCEWithLogitsLoss 更稳定,因为它结合了 Sigmoid 激活函数和 BCE 损失。

1.2 线性层组件

构建一个简单的线性层,后续还有卷积层(Convolution Layers)、池化层(Pooling layers)、激活(Non-linear Activations)、归一化等需要我们去学习和使用...

torch.nn.Linear(in_features, out_features, bias=True)

参数说明:

in_features:

  • 输入特征的数量(即输入数据的维度)。

  • 例如,如果输入是一个长度为 100 的向量,则 in_features=100。

out_features:

  • 输出特征的数量(即输出数据的维度)。

  • 例如,如果希望输出是一个长度为 50 的向量,则 out_features=50。

bias:

  • 是否使用偏置项(默认值为 True)。

  • 如果设置为 False,则不会学习偏置项。

nn.Linear 的作用

nn.Linear 执行以下线性变换:

output=input⋅W^T+b

其中:

  • input是输入数据,形状为 (batch_size, in_features)。

  • W是权重矩阵,形状为 (out_features, in_features)。

  • b是偏置项,形状为 (out_features,)。

  • output是输出数据,形状为 (batch_size, out_features)。

import torch
import torch.nn as nndef test002():model = nn.Linear(20, 60)# input数据形状为:(batch_size,in_features),其中in_features要和Linear中的数量一致input = torch.randn(128, 20)output = model(input)print(output.size())if __name__ == "__main__":test002()

1.3 优化器方法

官方文档:torch.optim — PyTorch 2.6 documentation

这里牵涉到的API有:

  • optim.SGD():优化器方法;是 PyTorch 提供的随机梯度下降(Stochastic Gradient Descent, SGD)优化器。

  • model.parameters():模型参数获取;是一个生成器,用于获取模型中所有可训练的参数(权重和偏置)。

  • optimizer.zero_grad():梯度清零;

  • optimizer.step():参数更新;是优化器的核心方法,用于根据计算得到的梯度更新模型参数。优化器会根据梯度和学习率等参数,调整模型的权重和偏置。

import torch
import torch.nn as nn
import torch.optim as optim# 优化方法SGD的学习
def test003():model = nn.Linear(20, 60)criterion = nn.MSELoss()# 优化器:更新模型参数optimizer = optim.SGD(model.parameters(), lr=0.01)input = torch.randn(128, 20)output = model(input)# 计算损失及反向传播loss = criterion(output, torch.randn(128, 60))# 梯度清零optimizer.zero_grad()# 反向传播loss.backward()# 更新模型参数optimizer.step()print(loss.item())if __name__ == "__main__":test003()

注意:这里只是组件认识和用法演示,没有具体的模型训练功能实现

2. 数据加载器

分数据集和加载器2个步骤~

2.1 构建数据类

2.1.1 Dataset类

Dataset是一个抽象类,是所有自定义数据集应该继承的基类。它定义了数据集必须实现的方法。

必须实现的方法

  1. __len__: 返回数据集的大小

  2. __getitem__: 支持整数索引,返回对应的样本

在 PyTorch 中,构建自定义数据加载类通常需要继承 torch.utils.data.Dataset 并实现以下几个方法:

  1. __init__ 方法 用于初始化数据集对象:通常在这里加载数据,或者定义如何从存储中获取数据的路径和方法。

def __init__(self, data, labels):self.data = dataself.labels = labels

  2.__len__ 方法 返回样本数量:需要实现,以便 Dataloader加载器能够知道数据集的大小。

def __len__(self):return len(self.data)

 3.__getitem__ 方法 根据索引返回样本:将从数据集中提取一个样本,并可能对样本进行预处理或变换。

def __getitem__(self, index):sample = self.data[index]label = self.labels[index]return sample, label

如果你需要进行更多的预处理或数据变换,可以在 __getitem__ 方法中添加额外的逻辑。

  • 整体参考代码如下

import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader# 定义数据加载类
class CustomDataset(Dataset):def __init__(self, data, labels):"""初始化数据集:data: 样本数据(例如,一个 NumPy 数组或 PyTorch 张量):labels: 样本标签"""self.data = dataself.labels = labelsdef __len__(self):return len(self.data)def __getitem__(self, index):index = min(max(index, 0), len(self.data) - 1)sample = self.data[index]label = self.labels[index]return sample, labeldef test001():# 简单的数据集准备data_x = torch.randn(666, 20, requires_grad=True, dtype=torch.float32)data_y = torch.randn(data_x.shape[0], 1, dtype=torch.float32)dataset = CustomDataset(data_x, data_y)# 随便打印个数据看一下print(dataset[0])if __name__ == "__main__":test001()
2.1.2 TensorDataset类

TensorDatasetDataset的一个简单实现,它封装了张量数据,适用于数据已经是张量形式的情况。

特点

  1. 简单快捷:当数据已经是张量形式时,无需自定义Dataset类

  2. 多张量支持:可以接受多个张量作为输入,按顺序返回

  3. 索引一致:所有张量的第一个维度必须相同,表示样本数量

源码:

class TensorDataset(Dataset):def __init__(self, *tensors):# size(0)在python中同shape[0],获取的是样本数量# 用第一个张量中的样本数量和其他张量对比,如果全部相同则通过断言,否则抛异常assert all(tensors[0].size(0) == tensor.size(0) for tensor in tensors)self.tensors = tensorsdef __getitem__(self, index):return tuple(tensor[index] for tensor in self.tensors)def __len__(self):return self.tensors[0].size(0)

示例:

def test03():torch.manual_seed(0)# 创建特征张量和标签张量features = torch.randn(100, 5)  # 100个样本,每个样本5个特征labels = torch.randint(0, 2, (100,))  # 100个二进制标签# 创建TensorDatasetdataset = TensorDataset(features, labels)# 使用方式与自定义Dataset相同print(len(dataset))  # 输出: 100print(dataset[0])  # 输出: (tensor([...]), tensor(0))

2.2 数据加载器

在训练或者验证的时候,需要用到数据加载器批量的加载样本。

DataLoader 是一个迭代器,用于从 Dataset 中批量加载数据。它的主要功能包括:

  • 批量加载:将多个样本组合成一个批次。

  • 打乱数据:在每个 epoch 中随机打乱数据顺序。

  • 多线程加载:使用多线程加速数据加载。

创建DataLoader:

# 创建 DataLoader
dataloader = DataLoader(dataset,          # 数据集batch_size=10,    # 批量大小shuffle=True,     # 是否打乱数据num_workers=2     # 使用 2 个子进程加载数据
)

遍历:

# 遍历 DataLoader
# enumerate返回一个枚举对象(iterator),生成由索引和值组成的元组
for batch_idx, (samples, labels) in enumerate(dataloader):print(f"Batch {batch_idx}:")print("Samples:", samples)print("Labels:", labels)

案例:

import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader# 定义数据加载类
class CustomDataset(Dataset):#略......def test01():# 简单的数据集准备data_x = torch.randn(666, 20, requires_grad=True, dtype=torch.float32)data_y = torch.randn(data_x.size(0), 1, dtype=torch.float32)dataset = CustomDataset(data_x, data_y)# 构建数据加载器data_loader = DataLoader(dataset, batch_size=8, shuffle=True)for i, (batch_x, batch_y) in enumerate(data_loader):print(batch_x, batch_y)breakif __name__ == "__main__":test01()

2.3 重构线性回归

使用pytorch对线性回归项目进行重构,可以看到有多方便!

import torch
import torch.optim as optim
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
import random
from sklearn.datasets import make_regression# 定义特征数
n_features = 5"""使用 sklearn 的 make_regression 方法来构建一个模拟的回归数据集。make_regression 方法的参数解释:- n_samples: 生成的样本数量,决定了数据集的规模。- n_features: 生成的特征数量,决定了数据维度。- n_informative: 对目标变量有影响的特征数量(默认 10)。- n_targets: 目标变量的数量(默认 1,单输出回归)。- bias:	目标变量的偏置(截距),默认 0.0。- noise: 添加到目标变量的噪声标准差,用于模拟真实世界数据的不完美。- coef: 如果为 True, 会返回生成数据的真实系数(权重),用于了解特征与目标变量间的真实关系。- random_state: 随机数生成的种子,确保在多次运行中能够复现相同的结果。返回:- X: 生成的特征矩阵。X 的维度是 (n_samples, n_features)- y: 生成的目标变量。y 的维度是(n_samples,) 或 (n_samples, n_targets)- coef: 如果在调用时 coef 参数为 True,则还会返回真实系数(权重)。coef 的维度是 (n_features,)
"""
def build_dataset():noise = random.randint(1, 3)bias = 14.5X, y, coef = make_regression(n_samples=1000,n_features=n_features,bias=bias,noise=noise,coef=True,random_state=0)# 数据转换为张量X = torch.tensor(X, dtype=torch.float32)y = torch.tensor(y, dtype=torch.float32)coef = torch.tensor(coef, dtype=torch.float32)bias = torch.tensor(bias, dtype=torch.float32)return X, y, coef, bias# 训练函数
def train():# 0. 构建模型model = nn.Linear(n_features, 1)# 1. 构建数据集X, y, coef, bias = build_dataset()dataset = TensorDataset(X, y)# 2. 定义训练参数learning_rate = 0.1epochs = 50batch_size = 16# 定义损失函数criterion = nn.MSELoss()# 定义优化器optimizer = optim.SGD(model.parameters(), lr=learning_rate)# 3. 开始训练for epoch in range(epochs):# 4. 构建数据集加载器data_loader = DataLoader(dataset, batch_size=batch_size, shuffle=True)epoch_loss = 0num_batches = 0for train_X, train_y in data_loader:num_batches += 1# 5. 前向传播y_pred = model(train_X)# 6. 计算损失,注意y_pred, train_y的形状保持一致loss = criterion(y_pred, train_y.reshape(-1, 1))# 7. 梯度清零optimizer.zero_grad()# 8. 反向传播:会自动计算梯度loss.backward()# 9. 更新参数optimizer.step()# 10. 训练批次及损失率epoch_loss += loss.item()print(f"Epoch: {epoch}, Loss: {epoch_loss / num_batches}")# 获取训练好的权重和偏置w = model.weight.detach().flatten()  # 将 weight 转换为一维张量b = model.bias.detach().item()return coef, bias, w, bif __name__ == "__main__":coef, bias, w, b = train()print(f"真实系数: {coef}")print(f"预测系数: {w}")print(f"真实偏置: {bias}")print(f"预测偏置: {b}")

训练结果:

Epoch: 0, Loss: 515.9365651872423
Epoch: 1, Loss: 17.0213944949801
......
Epoch: 99, Loss: 16.81899456750779
真实系数: tensor([41.2059, 66.4995, 10.7145, 60.1951, 25.9615])
预测系数: tensor([41.2794, 67.1859, 11.3169, 59.5126, 25.6431])
真实偏置: 14.5
预测偏置: 14.348488807678223

3. 数据集加载案例

通过一些数据集的加载案例,真正了解数据类及数据加载器。

3.1 加载csv数据集

代码参考如下

import torch
from torch.utils.data import Dataset, DataLoader
import pandas as pdclass MyCsvDataset(Dataset):def __init__(self, filename):df = pd.read_csv(filename)# 删除文字列df = df.drop(["学号", "姓名"], axis=1)# 转换为tensordata = torch.tensor(df.values)# 最后一列以前的为data,最后一列为labelself.data = data[:, :-1]self.label = data[:, -1]self.len = len(self.data)def __len__(self):return self.lendef __getitem__(self, index):idx = min(max(index, 0), self.len - 1)return self.data[idx], self.label[idx]def test001():excel_path = r"./大数据答辩成绩表.csv"dataset = MyCsvDataset(excel_path)dataloader = DataLoader(dataset, batch_size=4, shuffle=True)for i, (data, label) in enumerate(dataloader):print(i, data, label)if __name__ == "__main__":test001()

3.2 加载图片数据集

参考代码如下:只是用于文件读取测试

import torch
from torch.utils.data import Dataset, DataLoader
import os# 导入opencv
import cv2class MyImageDataset(Dataset):def __init__(self, folder):# 文件存储路径列表self.filepaths = []# 文件对应的目录序号列表self.labels = []# 指定图片大小self.imgsize = (112, 112)# 临时存储文件所在目录名dirnames = []# 递归遍历目录,root:根目录路径,dirs:子目录名称,files:子文件名称for root, dirs, files in os.walk(folder):# 如果dirs和files不同时有值,先遍历dirs,然后再以dirs的目录为路径遍历该dirs下的files# 这里需要在dirs不为空时保存目录名称列表if len(dirs) > 0:dirnames = dirsfor file in files:# 文件路径filepath = os.path.join(root, file)self.filepaths.append(filepath)# 分割root中的dir目录名classname = os.path.split(root)[-1]# 根据目录名到临时目录列表中获取下标self.labels.append(dirnames.index(classname))self.len = len(self.filepaths)def __len__(self):return self.lendef __getitem__(self, index):# 获取下标idx = min(max(index, 0), self.len - 1)# 根据下标获取文件路径filepath = self.filepaths[idx]# opencv读取图片img = cv2.imread(filepath)# 图片缩放,图片加载器要求同一批次的图片大小一致img = cv2.resize(img, self.imgsize)# 转换为tensorimg_tensor = torch.tensor(img)# 将图片HWC调整为CHWimg_tensor = torch.permute(img_tensor, (2, 0, 1))# 获取目录标签label = self.labels[idx]return img_tensor, labeldef test02():path = os.path.join(os.path.dirname(__file__), 'dataset')# 转换为相对路径path = os.path.relpath(path)dataset = MyImageDataset(path)dataloader = DataLoader(dataset, batch_size=8, shuffle=True)for img, label in dataloader:print(img.shape)print(label)if __name__ == "__main__":test02()

优化:使用ImageFolder加载图片集

ImageFolder 会根据文件夹的结构来加载图像数据。它假设每个子文件夹对应一个类别,文件夹名称即为类别名称。例如,一个典型的文件夹结构如下:

root/
    class1/
        img1.jpg
        img2.jpg
        ...
    class2/
        img1.jpg
        img2.jpg
        ...
    ...

在这个结构中:

  • root 是根目录。

  • class1class2 等是类别名称。

  • 每个类别文件夹中的图像文件会被加载为一个样本。

ImageFolder构造函数如下:

torchvision.datasets.ImageFolder(root, transform=None, target_transform=None, is_valid_file=None)

参数解释

  • root:字符串,指定图像数据集的根目录。

  • transform:可选参数,用于对图像进行预处理。通常是一个 torchvision.transforms 的组合。

  • target_transform:可选参数,用于对目标(标签)进行转换。

  • is_valid_file:可选参数,用于过滤无效文件。如果提供,只有返回 True 的文件才会被加载。

import torch
from torchvision import datasets, transforms
import os
from torch.utils.data import DataLoader
from matplotlib import pyplot as plttorch.manual_seed(42)def load():path = os.path.join(os.path.dirname(__file__), 'dataset')print(path)transform = transforms.Compose([transforms.Resize((112, 112)),transforms.ToTensor()])dataset = datasets.ImageFolder(path, transform=transform)dataloader = DataLoader(dataset, batch_size=1, shuffle=True)for x,y in dataloader:x = x.squeeze(0).permute(1, 2, 0).numpy()plt.imshow(x)plt.show()print(y[0])breakif __name__ == '__main__':load()

3.3 加载官方数据集

在 PyTorch 中官方提供了一些经典的数据集,如 CIFAR-10、MNIST、ImageNet 等,可以直接使用这些数据集进行训练和测试。

数据集:Datasets — Torchvision 0.21 documentation

常见数据集:

  • MNIST: 手写数字数据集,包含 60,000 张训练图像和 10,000 张测试图像。

  • CIFAR10: 包含 10 个类别的 60,000 张 32x32 彩色图像,每个类别 6,000 张图像。

  • CIFAR100: 包含 100 个类别的 60,000 张 32x32 彩色图像,每个类别 600 张图像。

  • COCO: 通用对象识别数据集,包含超过 330,000 张图像,涵盖 80 个对象类别。

torchvision.transforms 和 torchvision.datasets 是 PyTorch 中处理计算机视觉任务的两个核心模块,它们为图像数据的预处理和标准数据集的加载提供了强大支持。

transforms 模块提供了一系列用于图像预处理的工具,可以将多个变换组合成处理流水线。

datasets 模块提供了多种常用计算机视觉数据集的接口,可以方便地下载和加载。

参考如下:

import torch
from torch.utils.data import Dataset, DataLoader
import torchvision
from torchvision import transforms, datasetsdef test():transform = transforms.Compose([transforms.ToTensor(),])# 训练数据集data_train = datasets.MNIST(root="./data",train=True,download=True,transform=transform,)trainloader = DataLoader(data_train, batch_size=8, shuffle=True)for x, y in trainloader:print(x.shape)print(y)break# 测试数据集data_test = datasets.MNIST(root="./data",train=False,download=True,transform=transform,)testloader = DataLoader(data_test, batch_size=8, shuffle=True)for x, y in testloader:print(x.shape)print(y)breakdef test006():transform = transforms.Compose([transforms.ToTensor(),])# 训练数据集data_train = datasets.CIFAR10(root="./data",train=True,download=True,transform=transform,)trainloader = DataLoader(data_train, batch_size=4, shuffle=True, num_workers=2)for x, y in trainloader:print(x.shape)print(y)break# 测试数据集data_test = datasets.CIFAR10(root="./data",train=False,download=True,transform=transform,)testloader = DataLoader(data_test, batch_size=4, shuffle=False, num_workers=2)for x, y in testloader:print(x.shape)print(y)breakif __name__ == "__main__":test()test006()

相关文章:

深度学习-Torch框架-2

八、自动微分 自动微分模块torch.autograd负责自动计算张量操作的梯度&#xff0c;具有自动求导功能。自动微分模块是构成神经网络训练的必要模块&#xff0c;可以实现网络权重参数的更新&#xff0c;使得反向传播算法的实现变得简单而高效。 1. 基础概念 张量 Torch中一切皆…...

FlinkCDC初始化时报错“IllegalArgumentException: Unexpected input: “异常定位与原理分析

本篇是纯技术文章,是排查线上问题的真实记录。这个异常我在网上没搜到相同案例,所以特此记录下,方便后期回顾。 一、背景 利用FlinkCDC3.0动态监听数据库Schema变更的能力开发了一个生产数据库DDL语句变更审计告警的服务,这两天突然发现服务一直报错,经过4个小时的排查,…...

从代码学习深度学习 - Transformer PyTorch 版

文章目录 前言1. 位置编码(Positional Encoding)2. 多头注意力机制(Multi-Head Attention)3. 前馈网络与残差连接(Position-Wise FFN & AddNorm)3.1 基于位置的前馈网络(PositionWiseFFN)3.2 残差连接和层规范化(AddNorm)4. 编码器(Encoder)4.1 编码器块(Enco…...

多模态大语言模型arxiv论文略读(二十五)

ManipLLM: Embodied Multimodal Large Language Model for Object-Centric Robotic Manipulation ➡️ 论文标题&#xff1a;ManipLLM: Embodied Multimodal Large Language Model for Object-Centric Robotic Manipulation ➡️ 论文作者&#xff1a;Xiaoqi Li, Mingxu Zhang…...

LVS+Keepalived+dns高可用项目架构

一、搭建DNS服务 配置主服务器 1.修改核心配置文件 [rootDNS-master ~]# vim /etc/named.conf options { listen-on port 53 { 192.168.111.107;192.168.111.100; }; directory "/var/named"; }; zone "haha.com" IN { ty…...

C#日志辅助类(Log4Net)实现

一、Log4Net类库安装 在解决方案中项目上右键单击,选择“管理NuGet程序包”,在浏览窗口的搜索框输入log4net进行搜索,安装搜索出的第一项,如下图。 二、辅助类实现(Log4NetHelper) using log4net.Appender; using log4net.Config; using log4net.Layout; using log4net…...

【FFmpeg从入门到精通】第二章-FFmpeg工具使用基础

1 ffmpeg常用命令 ffmpeg在做音视频编解码时非常方便&#xff0c;所以在很多场景下转码使用的是ffmpeg&#xff0c;通过 ffmpeg --help可以看到 ffmpeg 常见的命令大概分为6个部分&#xff0c;具体如下。 ffmpeg信息查询部分公共操作参数部分文件主要操作参数部分视频操作参数…...

论文阅读:2022 ACL TruthfulQA: Measuring How Models Mimic Human Falsehoods

总目录 大模型安全相关研究&#xff1a;https://blog.csdn.net/WhiffeYF/article/details/142132328 TruthfulQA: Measuring How Models Mimic Human Falsehoods https://arxiv.org/pdf/2109.07958 https://www.doubao.com/chat/3130551217163266 https://github.com/sylin…...

基于C++(MFC)实现的文件管理系统

基于 MFC 的文件管理系统 第一章 题目解读与要求分析 1 实习题目 实现一个文件系统。 2 功能要求 界面上显示树形目录结构 a&#xff09;根节点是“我的电脑” b&#xff09;“我的电脑”下有几个盘符&#xff08;C、D、E 等&#xff09;就有几个子节点&#xff0c;递归…...

selenium 实现模拟登录中的滑块验证功能

用python在做数据采集过程中&#xff0c;经常需要用到模拟登录&#xff0c;经常遇到各种图片、文字甚至短信等验证&#xff0c;如果能通过脚本的方便实现验证&#xff0c;就可以自动帮我更高效地收集数据。Selenium 是一个开源的 Web 自动化测试工具&#xff0c;最初是为网站自…...

Oracle 19c部署之数据库软件安装(二)

在完成了Oracle Linux 9的初始化配置之后&#xff0c;我们准备安装Oracle 19c数据库软件。 Oracle数据库支持两种主要的安装方式&#xff1a;图形化安装和静默安装。这两种方法各有优缺点&#xff0c;选择哪种取决于你的具体需求、环境配置以及个人偏好。 图形化安装 图形化安…...

Paramiko 使用教程

目录 简介安装 Paramiko连接到远程服务器执行远程命令文件传输示例 简介 Paramiko 是一个基于 Python 的 SSH 客户端库&#xff0c;它提供了在网络上安全传输文件和执行远程命令的功能。本教程将介绍 Paramiko 的基本用法&#xff0c;包括连接到远程服务器、执行命令、文件传输…...

从EOF到REOF:如何用旋转经验正交函数提升时空数据分析精度?

目录 1. 基本概念与原理2. 应用场景3. 与传统EOF的区别4. 技术实现5. 其他领域中的“REOF”参考资料 REOF 的输入是多个地区在不同时间的气候数据&#xff08;如温度或降雨量&#xff09;&#xff0c;它的作用是通过旋转计算找出这些数据中最主要的变化规律&#xff0c;输出则是…...

VS-Code创建Vue3项目

1 创建工程文件 创建一个做工程项目的文件夹 如&#xff1a;h5vue 2 cmd 进入文件 h5vue 3 输入如下命令 npm create vuelatest 也可以输入 npm create vitelatest 4 输入项目名称 项目名称&#xff1a;自已输入 回车 可以按键盘 a (全选) 回车&#xff1a; Playwright…...

JESD204B接收器核心实现和系统级关键细节

目录 1.通道偏移 2.弹性缓冲器的实现 3.接受延迟 4.RX端到端延迟 5.计算端到端延迟 6.实现可重复的延迟 1.通道偏移 JESD204B接收器核心已经过验证,其功能具有高达8个字节的通道到通道偏斜。 2.弹性缓冲器的实现 在JESD204B设备中,接收通道对齐弹性缓冲区是在分布式…...

NLP高频面试题(四十七)——探讨Transformer中的注意力机制:MHA、MQA与GQA

MHA、MQA和GQA基本概念与区别 1. 多头注意力(MHA) 多头注意力(Multi-Head Attention,MHA)通过多个独立的注意力头同时处理信息,每个头有各自的键(Key)、查询(Query)和值(Value)。这种机制允许模型并行关注不同的子空间上下文信息,捕捉复杂的交互关系。然而,MHA…...

k230学习笔记-疑难点(1)

1.出现boot failed with exit code 19: 需要将k230开发板的btoot0拨到ON 2.出现boot failed with exit code 13: 说明k230开发板的固件烧录已经丢失&#xff0c;需要重新烧录 *** 注意重新烧录时需要将btoot0重新拨到OFF&#xff0c;才会弹出加载固件需要的通用串行总线&…...

JavaScript性能优化实战:让你的Web应用飞起来

JavaScript性能优化实战&#xff1a;让你的Web应用飞起来 在前端开发中&#xff0c;JavaScript性能优化是提升用户体验的关键。一个性能良好的应用不仅能吸引用户&#xff0c;还能提高转化率和用户留存率。今天&#xff0c;我们就来深入探讨JavaScript性能优化的实战技巧&…...

金融数据库转型实战读后感

荣幸收到老友太保科技有限公司数智研究院首席专家林春的签名赠书。 这是国内第一本关于OceanBase数据库实际替换过程总结的的实战书。打个比方可以说是从战场上下来分享战斗经验。读后感受颇深。我在这里讲讲我的感受。 第三章中提到的应用改造如何降本。应用改造是国产化替换…...

血脂代谢通路(医学-计算机系统对照方式)

血脂代谢通路(医学-计算机系统对照方式) 整合所有类比&#xff0c;用医学-计算机系统对照的方式完整描述血脂代谢通路&#xff0c;采用分步骤的对照结构&#xff1a; 1. 食物摄入&#xff08;数据输入层&#xff09; # 医学术语: 膳食脂肪摄入 → 计算机类比: 原始数据输入 …...

git更新的bug

文章目录 1. 问题2. 分析 1. 问题 拉取了一个项目后遇到了这个问题&#xff0c; nvocation failed Server returned invalid Response. java.lang.RuntimeException: Invocation failed Server returned invalid Response. at git4idea.GitAppUtil.sendXmlRequest(GitAppUtil…...

直流电源基本原理

整流电路 在构建整流电路时&#xff0c;要选择合适参数的二极管 If是二极管能够通过电流的能力&#xff0c;也是最大整流的平均电流。 还要考虑二极管的反向截至电压。 脉动系数电压交流幅值/直流平均电压&#xff08;越小越好&#xff09; 三相整流电路优点&#xff1a; …...

Git -> git merge --no-ff 和 git merge的区别

git merge --no-ff <branch> 与 git merge <branch> 的区别 核心区别 git merge <branch>: 默认使用Fast-forward模式&#xff08;若可行&#xff09;不创建额外的合并提交记录合并后看不出曾经存在过分支 git merge --no-ff <branch>:强制创建一个…...

名胜古迹传承与保护系统(springboot+ssm+vue+mysql)含运行文档

名胜古迹传承与保护系统(springbootssmvuemysql)含运行文档 名胜古迹传承与保护系统是一个专注于文化遗产保护和管理的综合性平台。系统提供了一系列功能模块&#xff0c;包括名胜古迹管理、古迹预约管理、古迹故事管理、举报信息管理、保护措施管理、古迹讨论、管理员管理、版…...

windows资源管理器左边导航窗格增加2个项,windows10/11有效

下面文档存为.reg文件&#xff0c; Windows Registry Editor Version 5.00; 根 CLSID —— 名称、图标、固定到导航窗格 [HKEY_CURRENT_USER\Software\Classes\CLSID\{C1A3F2D2-BD2D-4D60-82C5-394F01753A5F}] "手机系统" "System.IsPinnedToNamespaceTree&quo…...

【八股文】基于源码聊聊ConcurrentHashmap的设计

版本演进 jdk 1.7中是分段锁的设计&#xff0c;将哈希表划分为多个segment&#xff0c;每个段独立加锁&#xff0c;锁粒度为段级别。 操作需两次哈希&#xff0c;第一次定位段&#xff0c;第二次定位桶内链表。这种实现方式的缺点就是段数量固定&#xff0c;扩容复杂&#xf…...

Mysql--基础知识点--93--两阶段提交

1 两阶段提交 以update语句的具体执行过程为例&#xff1a; 具体更新一条记录 UPDATE t_user SET name ‘xiaolin’ WHERE id 1;的流程如下&#xff1a; 1.执行器负责具体执行&#xff0c;会调用存储引擎的接口&#xff0c;通过主键索引树搜索获取 id 1 这一行记录&#…...

数字化招标采购系统怎么让招采协同更高效?

招标采购领域的数智化转型正在引发行业革命性变革。从传统线下模式到全流程电子化&#xff0c;再到当前数智化阶段的超时空协同&#xff0c;行业的演进路径清晰展现了技术与管理的深度融合。郑州信源信息数智化招采系统作为行业标杆&#xff0c;其创新实践为未来协同工作方式的…...

池塘计数(BFS)

题目描述 由于最近的降雨&#xff0c;光头强的田地里的各个地方都积水了&#xff0c;用 NM(1≤N≤100&#xff1b;1≤M≤100)NM(1≤N≤100&#xff1b;1≤M≤100) 的正方形的矩形表示。每个广场都有水 W 或旱地 .。光头强想知道他的田地里形成了多少池塘。池塘是指一组相邻的有…...

《Science》观点解读:AI无法创造真正的智能体(AI Agent)

无论是想要学习人工智能当做主业营收&#xff0c;还是像我一样作为开发工程师但依然要运用这个颠覆开发的时代宠儿&#xff0c;都有必要了解、学习一下人工智能。 近期发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;入行门槛低&#x…...

从零开始学A2A四:A2A 协议的安全性与多模态支持

文章目录 A2A 协议的安全性与多模态支持一、A2A 协议安全机制1. 认证机制2. 授权机制3. 数据加密 二、多模态交互支持1. 文本交互2. 音频支持3. 视频与图像处理4. 复合数据格式 三、安全与多模态最佳实践1. 安全性实践2. 多模态实践 四、与 MCP 的对比1. 安全机制对比2. 多模态…...

一种大位宽加减法器的时序优化

平台&#xff1a;vivado2018.3 芯片&#xff1a;xc7a100tfgg484-2 (active) 在FPGA中实现超高位宽加减法器&#xff08;如256&#xff09;时&#xff0c;时序收敛常成为瓶颈。由于进位链&#xff08;Carry Chain&#xff09;跨越多级逻辑单元&#xff0c;关键路径延迟会随位宽…...

【专业解读:Semantic Kernel(SK)】大语言模型与传统编程的桥梁

目录 Start:什么是Semantic Kernel&#xff1f; 一、Semantic Kernel的本质&#xff1a;AI时代的操作系统内核 1.1 重新定义LLM的应用边界 1.2 技术定位对比 二、SK框架的六大核心组件与技术实现 2.1 内核&#xff08;Kernel&#xff09;&#xff1a;智能任务调度中心 2…...

InfiniBand与RoCEv2负载均衡机制的技术梳理与优化实践

AI技术的高速迭代正驱动全球算力格局进入全新纪元。据IDC预测&#xff0c;未来五年中国智能算力规模将以超50%的年复合增长率爆发式扩张&#xff0c;数据中心全面迈入“智能算力时代”。然而&#xff0c;海量AI训练、实时推理等高并发场景对底层网络提出了更严苛的挑战——超大…...

Vue与React组件化设计对比

组件化是现代前端开发的核心思想之一&#xff0c;而Vue和React作为两大主流框架&#xff0c;在组件化设计上既有相似之处&#xff0c;也存在显著差异。本文将从语法设计、数据管理、组件通信、性能优化、生态系统等多个方向&#xff0c;结合实例详细对比两者的特点。 一、模板…...

UE中通过AAIController::MoveTo函数巡逻至目标点后没法正常更新巡逻目标点

敌人巡逻的逻辑如下&#xff1a; 敌人在游戏一开始的时候就通过moveto函数先前往首先设定的patroltarget目标&#xff0c;在距离patroltarget距离为patroradius&#xff08;200unit)之内时就可以通过checkpatroltarget函数更新新的patroltarget目标&#xff0c;随后前往新的pat…...

Python-细节知识点range函数的详解

在 Python 中&#xff0c;range 是一个内置函数&#xff0c;用于生成一个不可变的整数序列&#xff0c;通常用于控制循环次数或生成数值范围。以下是详细说明&#xff1a; 基本语法 range(stop) # 生成 [0, stop) 的整数&#xff0c;步长为1 range(start, stop) …...

git rebase的使用

我的使用 git checkout feature # 本地分支 git pull origin main --rebase # 目标分支 git pull origin feature --rebase git push origin featuregit rebase 是 Git 中用于重写提交历史的强大工具&#xff0c;可将分支的提交移动到新的基点上&#xff0c;使历史更线性。以…...

CMake Error at build/_deps/glog-src/CMakeLists.txt:1 (cmake_minimum_required):

这个错误提示意思是你当前系统上安装的 CMake 版本过低&#xff0c;不满足项目的要求。根据错误信息&#xff1a; CMake Error at build/_deps/glog-src/CMakeLists.txt:1 (cmake_minimum_required): CMake 3.22 or higher is required. You are running version 3.16.3 项目…...

MATLAB 控制系统设计与仿真 - 34

多变量系统知识回顾 - MIMO system 这一章对深入理解多变量系统以及鲁棒分析至关重要 首先&#xff0c;对于如下系统&#xff1a; 当G(s)为单输入&#xff0c;单输出系统时&#xff1a; 如果&#xff1a; 则&#xff1a; 所以 当G(s)为MIMO时&#xff0c;例如2X2时&#xff…...

【Unity】JSON数据的存取

这段代码的结构是为了实现 数据的封装和管理&#xff0c;特别是在 Unity 中保存和加载玩家数据时。以下是对代码设计的逐步解释&#xff1a; 1. PlayerCoin 类 PlayerCoin 是一个简单的数据类&#xff0c;用于表示单个玩家的硬币信息。它包含以下字段&#xff1a; count&…...

利用 Java 爬虫按关键字搜索淘宝商品

在电商领域&#xff0c;通过关键字搜索商品是常见的需求。淘宝作为国内知名的电商平台&#xff0c;提供了丰富的商品搜索功能。本文将详细介绍如何使用 Java 爬虫技术按关键字搜索淘宝商品&#xff0c;并获取搜索结果的详细信息。 一、准备工作 1. 注册淘宝开放平台账号 要使…...

【C】初阶数据结构11 -- 选择排序

本篇文章主要讲解经典排序算法 -- 选择排序 目录 1 算法思想 2 代码 3 时间复杂度与空间复杂度分析 1&#xff09; 时间复杂度 2&#xff09; 空间复杂度 1 算法思想 选择排序是一种在一段区间里面选择最小的元素和最大的元素的一种排序算法。假设这里排升序&#…...

【Semantic Kernel核心组件】Plugin:连接AI与业务逻辑的桥梁

目录 一、Plugin是什么&#xff1f;为什么它是SK的核心&#xff1f; 一、Plugin的核心机制与Python实现 1. 插件类型&#xff1a;语义函数与本地函数 语义函数&#xff08;Semantic Function&#xff09; 本地函数&#xff08;Native Function&#xff09; 2. Plugin的注…...

《基于神经网络实现手写数字分类》

《基于神经网络实现手写数字分类》 一、主要内容&#xff1a; 1、通过B站陈云霁老师的网课&#xff0c;配合书本资料&#xff0c;了解神经网络的基本组成和数学原理。 2、申请云平台搭建实验环境 3、基于5个不同的实验模块逐步理解实验操作步骤&#xff0c;并实现不同模块代码…...

车载诊断架构 --- 车载诊断概念的深度解读

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧! 旧人不知我近况,新人不知我过…...

四、探索LangChain:连接语言模型与外部世界的桥梁

一、什么是 LangChain LangChain 是一个开源的软件框架,旨在帮助开发者高效构建和部署基于**大型语言模型(LLM)**的应用程序。它通过提供一系列工具、组件和接口,简化了从模型调用、提示工程到复杂应用开发的全流程,使得开发者能够快速将 LLM 集成到实际场景中。 1. 核心…...

LangChain4j中的Chat与语言模型API详解:构建高效对话系统的利器

LangChain4j中的Chat与语言模型API详解&#xff1a;构建高效对话系统的利器 引言&#xff1a;大模型时代的开发利器 在人工智能快速发展的今天&#xff0c;大型语言模型&#xff08;LLM&#xff09;已成为构建智能应用的核心组件。LangChain4j作为Java生态中领先的LLM集成框架…...

C++中const与constexpr的区别

在C中&#xff0c;const和constexpr都用于定义常量&#xff0c;但它们的用途和行为有显著区别&#xff1a; ### 1. **初始化时机** - **const**&#xff1a;表示变量是只读的&#xff0c;但其值可以在**编译时或运行时**初始化。 cpp const int a 5; // 编译…...

长亭2月公开赛Web-ssrfme

环境部署 拉取环境报错&#xff1a; 可以尝试拉取一下ubuntu:16.04&#xff0c;看是否能拉取成功 将wersion&#xff1a;"3"删掉 我拉去成功之后&#xff0c;再去拉取环境&#xff0c;成功&#xff01; 访问环境 测试ssrf 源码 <?php highlight_file(__file__…...