【鼠鼠学AI代码合集#3】简单的数据操作 (pytorch)

简单的数据操作 (pytorch)

张量(Tensor)的基本概念

张量是由数值组成的多维数组,可以看作是一个通用的数据容器。它可以表示从标量(零维)、向量(一维)、矩阵(二维)到更高维度的数组。在深度学习框架中,张量类不仅支持基本的数值计算,还具备以下两个重要特性:

  1. GPU加速: 张量能够在GPU上运行,这使得大量数据的处理变得非常高效。
  2. 自动微分: 张量类支持自动微分,这在训练神经网络时非常重要,因为它可以自动计算梯度,从而简化反向传播过程。

张量基本操作

  1. 创建张量:

    • 使用 torch.arange(12) 可以创建一个包含0到11的行向量:
      x = torch.arange(12)
      print(x)
      
      输出:
      tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
      
  2. 查看形状:

    • 使用 x.shape 可以查看张量的形状,返回值是张量的每个维度的大小:
      print(x.shape)
      
      输出:
      torch.Size([12])
      
  3. 改变形状:

    • 使用 reshape 函数可以改变张量的形状,比如将行向量 x 变为一个3x4的矩阵:
      X = x.reshape(3, 4)
      print(X)
      
      输出:
      tensor([[ 0,  1,  2,  3],
              [ 4,  5,  6,  7],
              [ 8,  9, 10, 11]])
      
  4. 创建全0或全1的张量:

    • 使用 torch.zeros((2, 3, 4)) 可以创建一个形状为2x3x4的全0张量:

      zeros = torch.zeros((2, 3, 4))
      print(zeros)
      

      输出:

      tensor([[[0., 0., 0., 0.],
               [0., 0., 0., 0.],
               [0., 0., 0., 0.]],
      
              [[0., 0., 0., 0.],
               [0., 0., 0., 0.],
               [0., 0., 0., 0.]]])
      
    • 类似地,可以使用 torch.ones((2, 3, 4)) 创建一个全1张量。

  5. 随机初始化:

    • 使用 torch.randn(3, 4) 可以从标准正态分布中随机采样生成一个3x4的张量:
      random_tensor = torch.randn(3, 4)
      print(random_tensor)
      
      输出:
      tensor([[-0.0135,  0.0665,  0.0912,  0.3212],
              [ 1.4653,  0.1843, -1.6995, -0.3036],
              [ 1.7646,  1.0450,  0.2457, -0.7732]])
      
  6. 手动创建张量:

    • 你也可以通过Python的嵌套列表直接创建张量:
      manual_tensor = torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
      print(manual_tensor)
      
      输出:
      tensor([[2, 1, 4, 3],
              [1, 2, 3, 4],
              [4, 3, 2, 1]])
      

通过这些操作,你可以快速上手张量的基本用法,并开始处理各种数据。在实际的深度学习项目中,理解这些张量操作将为你构建和训练模型提供坚实的基础。如果有些概念不太清楚,别担心,后面的章节将通过实际例子进一步巩固这些知识。

张量运算

在深度学习中,我们经常需要对张量执行各种数学运算,其中最常见的操作是按元素运算(elementwise operations)。这些运算将标量运算符应用于张量中的每个元素,并且可以扩展到涉及两个张量的二元运算。

1. 按元素运算

按元素运算可以应用于同一形状的任意两个张量。常见的操作符包括加法、减法、乘法、除法和幂运算。每个操作都逐元素地执行,即两个张量中对应位置的元素进行计算。

import torch

x = torch.tensor([1.0, 2, 4, 8])
y = torch.tensor([2, 2, 2, 2])

# 执行按元素运算
result = (x + y, x - y, x * y, x / y, x ** y)
print(result)

输出:

(tensor([ 3.,  4.,  6., 10.]),
 tensor([-1.,  0.,  2.,  6.]),
 tensor([ 2.,  4.,  8., 16.]),
 tensor([0.5000, 1.0000, 2.0000, 4.0000]),
 tensor([ 1.,  4., 16., 64.]))

2. 一元运算

你可以应用像求幂、指数等一元运算符,这些操作只涉及一个张量,并逐元素执行。

# 计算x中每个元素的指数
exp_result = torch.exp(x)
print(exp_result)

输出:

tensor([2.7183e+00, 7.3891e+00, 5.4598e+01, 2.9810e+03])

3. 张量连结

我们还可以将多个张量连结(concatenate)在一起。你可以指定沿哪个轴进行连结。

X = torch.arange(12, dtype=torch.float32).reshape((3, 4))
Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])

# 沿着第0轴(行)连结
concat_0 = torch.cat((X, Y), dim=0)

# 沿着第1轴(列)连结
concat_1 = torch.cat((X, Y), dim=1)

print(concat_0)
print(concat_1)

输出:

tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.],
        [ 2.,  1.,  4.,  3.],
        [ 1.,  2.,  3.,  4.],
        [ 4.,  3.,  2.,  1.]])
tensor([[ 0.,  1.,  2.,  3.,  2.,  1.,  4.,  3.],
        [ 4.,  5.,  6.,  7.,  1.,  2.,  3.,  4.],
        [ 8.,  9., 10., 11.,  4.,  3.,  2.,  1.]])

4. 逻辑运算

张量的逻辑运算符允许你比较两个张量。输出是一个布尔张量,其中每个元素的值取决于两个输入张量相应位置的比较结果。

# 逐元素比较 X 和 Y
comparison_result = X == Y
print(comparison_result)

输出:

tensor([[False,  True, False,  True],
        [False, False, False, False],
        [False, False, False, False]])

5. 张量求和

对张量中的所有元素进行求和,得到的是一个标量(单元素张量)。

# 对 X 的所有元素求和
sum_result = X.sum()
print(sum_result)

输出:

tensor(66.)

在这一部分中,我们将讨论如何在不同形状的张量之间执行按元素操作,以及如何使用索引和切片来访问和修改张量中的元素。

广播机制(Broadcasting Mechanism)

广播机制允许在形状不同的张量之间进行按元素运算。它通过以下方式工作:

  1. 扩展张量: 通过适当复制某些张量的元素,将它们扩展到相同的形状。
  2. 按元素操作: 在扩展后的张量上执行按元素操作。
示例代码
import torch

# 创建两个不同形状的张量
a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))

print("Tensor a:", a)
print("Tensor b:", b)

# 通过广播机制执行按元素加法操作
c = a + b
print("Result of a + b with broadcasting:", c)

输出

Tensor a:
tensor([[0],
        [1],
        [2]])
Tensor b:
tensor([[0, 1]])
Result of a + b with broadcasting:
tensor([[0, 1],
        [1, 2],
        [2, 3]])

在这个例子中,张量 a 的形状是 (3, 1),而张量 b 的形状是 (1, 2)。通过广播机制,a 的列被复制以匹配 b 的列数,b 的行被复制以匹配 a 的行数,然后执行加法操作,得到形状为 (3, 2) 的结果张量 c

索引和切片(Indexing and Slicing)

就像在Python列表中一样,你可以通过索引来访问张量中的元素,也可以使用切片来访问特定范围内的元素。

访问单个元素
X = torch.arange(12, dtype=torch.float32).reshape((3, 4))

# 访问最后一个元素
print("Last element:", X[-1])

# 访问第2行和第3行
print("Second and third rows:\n", X[1:3])

输出

Last element: tensor([ 8.,  9., 10., 11.])
Second and third rows:
 tensor([[ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.]])
修改元素

你还可以通过索引来修改张量中的元素:

# 修改特定位置的元素
X[1, 2] = 9
print("Modified X:\n", X)

输出

Modified X:
 tensor([[ 0.,  1.,  2.,  3.],
         [ 4.,  5.,  9.,  7.],
         [ 8.,  9., 10., 11.]])
修改多个元素

使用切片,你可以一次性修改多个元素的值:

# 修改第1行和第2行的所有列
X[0:2, :] = 12
print("X after modification:\n", X)

输出

X after modification:
 tensor([[12., 12., 12., 12.],
         [12., 12., 12., 12.],
         [ 8.,  9., 10., 11.]])

在深度学习中,有时需要优化内存使用,特别是在处理大型模型或数据时。以下是一些节省内存的方法和示例代码。

原地操作(In-place Operations)

当你执行张量操作时,如果不需要保留操作之前的张量,可以选择原地操作,这样可以避免新内存的分配,从而节省内存。原地操作会直接修改原始张量,而不分配新的内存。

例子 1: 非原地操作
import torch

# 创建一个张量 Y
Y = torch.tensor([2.0, 3.0, 4.0, 5.0])
X = torch.tensor([1.0, 2.0, 3.0, 4.0])

# 查看操作前 Y 的内存地址
before = id(Y)

# 进行非原地操作
Y = Y + X

# 查看操作后 Y 的内存地址
print("Is memory address of Y the same?:", id(Y) == before)

输出

Is memory address of Y the same?: False

在这个例子中,Y = Y + X 会生成一个新的张量,并将 Y 指向这个新张量,这会导致 Y 的内存地址发生变化。

例子 2: 原地操作
# 使用原地操作
Z = torch.zeros_like(Y)
print('id(Z) before:', id(Z))

# 通过切片原地操作
Z[:] = X + Y
print('id(Z) after:', id(Z))

输出

id(Z) before: 140327634811696
id(Z) after: 140327634811696

在这个例子中,Z[:] = X + Y 是一个原地操作,因此 Z 的内存地址没有改变。

使用 += 操作符

如果你不需要保留操作前的 X 张量,可以直接使用 += 操作符来原地修改 X,从而节省内存。

before = id(X)

# 原地操作:X += Y
X += Y

# 检查内存地址是否改变
print("Is memory address of X the same?:", id(X) == before)

输出

Is memory address of X the same?: True

通过使用 +=X 的内存地址保持不变,因为操作是在原地进行的。

总结

通过使用原地操作和切片赋值,你可以在深度学习中有效节省内存。这对于需要频繁更新大型参数矩阵的模型来说尤为重要,避免了不必要的内存分配和潜在的内存泄漏。这些技巧可以帮助优化你的代码性能,特别是在处理大规模模型时。

在深度学习中,有时需要将深度学习框架定义的张量(如PyTorch的torch.Tensor)转换为其他Python对象,例如NumPy数组(numpy.ndarray)或Python标量。以下是如何进行这些转换的步骤:

1. 将张量转换为NumPy数组

你可以轻松地将一个PyTorch张量转换为NumPy数组,反之亦然。这两个对象将共享底层内存,因此对其中一个对象的修改也会影响另一个对象。

示例代码:
import torch

# 创建一个PyTorch张量
X = torch.tensor([[1, 2, 3], [4, 5, 6]])

# 将张量转换为NumPy数组
A = X.numpy()

# 将NumPy数组转换回PyTorch张量
B = torch.tensor(A)

# 查看转换后对象的类型
print(type(A))  # 输出: <class 'numpy.ndarray'>
print(type(B))  # 输出: <class 'torch.Tensor'>

在这个示例中,A 是一个 numpy.ndarray,而 B 是一个 torch.Tensor。它们共享同一块内存,因此对 AB 进行就地操作会同时改变这两个对象。

2. 将张量转换为Python标量

如果张量的大小为1,你可以使用 .item() 方法或直接转换为 Python 标量(如 floatint)。

示例代码:
# 创建一个只有一个元素的张量
a = torch.tensor([3.5])

# 将张量转换为Python标量
scalar_value_item = a.item()
scalar_value_float = float(a)
scalar_value_int = int(a)

# 输出
print(a)                 # 输出: tensor([3.5000])
print(scalar_value_item) # 输出: 3.5 (Python标量)
print(scalar_value_float)# 输出: 3.5 (Python float)
print(scalar_value_int)  # 输出: 3 (Python int)

总结

这些转换操作在深度学习中非常常见,尤其是当你需要在PyTorch和NumPy之间切换,或提取单个标量值进行进一步处理时。共享底层内存的机制使得这些操作高效而直观。

  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值