PyTorch 基础学习(2)- 张量 Tensors

系列文章:
《PyTorch 基础学习》文章索引

PyTorch张量简介

张量是数学和计算机科学中的一个基本概念,用于表示多维数据,是AI世界中一切事物的表示和抽象。可以将张量视为一个扩展了标量、向量和矩阵的通用数据结构。以下是对张量的详细解释:

张量的定义

  1. 标量(0阶张量):标量是一个单一的数值,例如 3 或 -1.5。

  2. 向量(1阶张量):向量是一维数组,可以表示为一列或一行数值,例如 ([1, 2, 3])。

  3. 矩阵(2阶张量):矩阵是一个二维数组,包含行和列,例如:

    [
\begin{bmatrix}
1 & 2 & 3 \
4 & 5 & 6
\end{bmatrix}
]

  4. 高阶张量:高阶张量是更高维度的数组。例如,三阶张量可以被视为一个矩阵的集合,每个矩阵位于一个"层"上,如下所示:

    [
\begin{bmatrix}
\begin{bmatrix}
1 & 2 \
3 & 4
\end{bmatrix},
\begin{bmatrix}
5 & 6 \
7 & 8
\end{bmatrix}
\end{bmatrix}
]

张量的特点

  • 维度(阶):张量的维度指的是数据的方向数。例如,一个三阶张量可以用一个3D数组表示,其中每个维度称为一个轴。

  • 形状:张量的形状由沿每个轴的元素数量定义。例如,形状为(3, 4, 5)的张量意味着它有3个“层”,每个“层”包含4行5列的数据。

  • 数据类型:张量可以存储不同类型的数据,如整数、浮点数等。

一个四阶多维张量的实例,形状为(2, 2, 2, 3),类型为浮点数:

[
  [
    [
      [0.3542, 0.3711, 0.0223],
      [0.7260, 0.8615, 0.7581]
    ],
    [
      [0.2645, 0.0404, 0.0662],
      [0.2702, 0.9932, 0.4583]
    ],
  ],
  [
    [
      [0.2205, 0.9257, 0.5866],
      [0.6799, 0.2386, 0.8317]
    ],
    [
      [0.5297, 0.9952, 0.6515],
      [0.2069, 0.5454, 0.1466]
    ]
  ]
]

张量在机器学习中的应用

在深度学习和机器学习中,张量是处理和操作数据的基础。以下是一些应用示例:

  1. 图像数据:图像通常表示为3阶张量,其中维度分别表示高度、宽度和颜色通道。

  2. 时间序列数据:时间序列数据可以表示为2阶张量,其中一个维度表示时间步,另一个维度表示特征。

  3. 神经网络权重:在神经网络中,权重和偏置通常存储为张量,以便进行高效的计算。

将图像转换成张量

from PIL import Image
from torchvision import transforms

# 第一步:加载图像
image_path = 'path_to_your_image.jpg'  # 替换为你的图像路径
image = Image.open(image_path)

# 第二步:定义转换
transform = transforms.Compose([
    transforms.ToTensor()  # 将图像转换为张量,并将像素值归一化到[0, 1]之间
])

# 第三步:应用转换
image_tensor = transform(image)

# 查看结果
print(f"张量形状: {image_tensor.shape}")  # 输出张量形状,通常为 (C, H, W)
print(f"数据类型: {image_tensor.dtype}")  # 输出张量的数据类型

输出:

张量形状: torch.Size([3, 1024, 1024])  //一幅三通道(RGB)的 1024x1024 像素图像。
数据类型: torch.float32 //张量的数据类型为32位浮点数

张量计算

PyTorch等深度学习框架利用张量作为基本数据结构,因为它们可以高效地在GPU上进行并行计算。张量支持多种数学运算,如加法、乘法、转置、索引和切片等,这使得它们非常适合处理复杂的数据计算任务。

总结来说,张量是一个灵活且高效的数据结构,广泛用于科学计算、图像处理、自然语言处理等领域。通过使用张量,我们可以在计算机中有效地表示和操作高维数据。

张量操作

  1. 张量检测

    • torch.is_tensor(obj):判断对象obj是否是一个PyTorch张量,返回TrueFalse
  2. 元素个数

    • torch.numel(input):返回张量input中的元素个数。
    import torch
    
    a = torch.randn(1, 2, 3, 4, 5)
    print(torch.numel(a))  # 输出: 120
    
  3. 设置默认张量类型

    • torch.set_default_tensor_type(t):设置默认的张量类型。
  4. 打印选项

    • torch.set_printoptions(precision=None, threshold=None, edgeitems=None, linewidth=None, profile=None):设置张量打印的选项,如精度、行宽等。

张量创建

  1. 单位矩阵

    • torch.eye(n, m=None, out=None):返回一个2D单位矩阵。
    print(torch.eye(3))
    
  2. 从Numpy数组创建张量

    • torch.from_numpy(ndarray):将Numpy数组转换为PyTorch张量。
    import numpy as np
    
    a = np.array([1, 2, 3])
    t = torch.from_numpy(a)
    
  3. 线性间隔张量

    • torch.linspace(start, end, steps=100, out=None):创建一个1D张量,包含从startend的等间隔数值。
    print(torch.linspace(3, 10, steps=5))
    
  4. 对数间隔张量

    • torch.logspace(start, end, steps=100, out=None):在对数尺度上创建等间隔数值的1D张量。
    print(torch.logspace(start=0.1, end=1.0, steps=5))
    
  5. 全1张量

    • torch.ones(*sizes, out=None):创建一个形状为sizes的全1张量。
    print(torch.ones(2, 3))
    
  6. 随机张量

    • torch.rand(*sizes, out=None):创建一个形状为sizes的张量,包含均匀分布的随机数。
    • torch.randn(*sizes, out=None):创建一个形状为sizes的张量,包含标准正态分布的随机数。
  7. 排列张量

    • torch.randperm(n, out=None):生成从0到n-1的随机排列。
    print(torch.randperm(4))
    

索引与切片

  1. 张量连接

    • torch.cat(inputs, dimension=0):在给定维度上连接张量序列。
    x = torch.randn(2, 3)
    print(torch.cat((x, x, x), 0))
    
  2. 张量分块

    • torch.chunk(tensor, chunks, dim=0):将张量分成指定数量的块。
  3. 选择性索引

    • torch.index_select(input, dim, index, out=None):在指定维度上选择特定的索引项。
    x = torch.randn(3, 4)
    indices = torch.LongTensor([0, 2])
    print(torch.index_select(x, 0, indices))
    
  4. 非零元素索引

    • torch.nonzero(input, out=None):返回一个张量,包含非零元素的索引。
    print(torch.nonzero(torch.Tensor([1, 1, 0, 0, 1])))
    
  5. 挤压与扩展

    • torch.squeeze(input, dim=None, out=None):移除形状中的1。
    • torch.unsqueeze(input, dim, out=None):在指定位置插入维度1。
    x = torch.Tensor([1, 2, 3, 4])
    print(torch.unsqueeze(x, 0))
    

随机抽样

  1. 设定随机数种子

    • torch.manual_seed(seed):设定随机数生成的种子。
  2. 伯努利分布

    • torch.bernoulli(input, out=None):从伯努利分布中抽取二元随机数。
    a = torch.Tensor(3, 3).uniform_(0, 1)
    print(torch.bernoulli(a))
    
  3. 多项分布抽样

    • torch.multinomial(input, num_samples, replacement=False, out=None):从多项分布中抽取样本。
  4. 正态分布

    • torch.normal(means, std, out=None):从正态分布中抽取样本。
    print(torch.normal(means=torch.arange(1, 6)))
    

序列化

  1. 保存对象

    • torch.save(obj, f):保存对象到文件。
  2. 加载对象

    • torch.load(f, map_location=None):从文件加载对象。

并行化

  1. 获取和设置线程数
    • torch.get_num_threads():获取用于并行化CPU操作的线程数。
    • torch.set_num_threads(int):设置用于并行化CPU操作的线程数。

应用实例:线性回归模型

下面是一个简单的线性回归模型示例,展示了如何使用上述PyTorch操作实现基本的机器学习任务。

import torch
import torch.nn as nn
import torch.optim as optim

# 生成随机数据
x = torch.rand(100, 1) * 10  # 100个样本,每个样本有1个特征
y = 2 * x + 3 + torch.randn(100, 1)  # y = 2*x + 3 + 噪声

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

# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = 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()

    if (epoch+1) % 100 == 0:
        print(f'Epoch [{epoch+1}/1000], Loss: {loss.item():.4f}')

# 打印模型参数
[w, b] = model.parameters()
print(f'Weight: {w.item()}, Bias: {b.item()}')

输出:

......
Epoch [800/1000], Loss: 1.1024
Epoch [900/1000], Loss: 1.1014
Epoch [1000/1000], Loss: 1.1011
Weight: 1.9926187992095947, Bias: 3.0762107372283936

总结

本教程介绍了PyTorch中的基本张量操作,包括创建、索引、随机采样、序列化和并行化等。通过实例演示了如何使用这些操作构建一个简单的线性回归模型。掌握这些基本操作能够帮助开发者更高效地进行深度学习模型的构建与优化。

附录 1:Numpy数组,及与张量的区别

Numpy数组

Numpy(Numeric Python)是一个开源的Python科学计算库,它提供了一个高性能的多维数组对象ndarray和用于操作这些数组的工具和函数。Numpy数组是Numpy库中的核心组件,具有以下特点:

  1. 多维数组:Numpy数组可以是一维(向量)、二维(矩阵)或更高维度的数组。
  2. 同质数据:数组中的所有元素必须是相同类型的数据。
  3. 固定大小:一旦创建,Numpy数组的大小是固定的,不能动态改变。
  4. 广播(Broadcasting):Numpy支持广播,这是一种强大的机制,允许不同形状的数组在数学运算中协同工作。
  5. 矢量化操作:Numpy数组支持矢量化操作,这意味着可以一次性对数组的多个元素执行操作,而不需要编写循环。
  6. 内存连续:Numpy数组在内存中是连续存储的,这使得它们可以利用现代CPU的向量化指令进行快速计算。
张量(以PyTorch为例)

张量是多维数组的一般化概念,用于表示和存储数据。在深度学习框架如PyTorch中,张量是核心数据结构,具有以下特点:

  1. 多维数组:与Numpy数组类似,张量也可以是一维、二维或更高维度的数组。
  2. 数据类型多样性:张量可以存储不同类型的数据,如整数、浮点数等。
  3. 动态大小:与Numpy数组不同,PyTorch张量的大小是动态的,可以在运行时改变。
  4. GPU支持:PyTorch张量可以存储在GPU上,这使得它们可以利用GPU的并行计算能力进行高效的计算。
  5. 自动求导:PyTorch张量支持自动求导,这是深度学习中非常重要的特性,允许框架自动计算梯度。
  6. 更多的操作和函数:PyTorch提供了大量的操作和函数来操作张量,包括但不限于数学运算、线性代数、随机数生成等。
Numpy数组与张量的区别
  1. 库和语言:Numpy是Python的一个库,而张量是深度学习框架(如PyTorch和TensorFlow)中的概念。
  2. GPU计算:Numpy数组通常只能在CPU上计算,而张量可以利用GPU进行计算,这对于大规模数据和复杂模型的训练非常重要。
  3. 自动求导:张量支持自动求导,这对于构建和训练神经网络至关重要,而Numpy数组不支持这一功能。
  4. 内存布局:Numpy数组在内存中是连续存储的,而张量可以有不同的内存布局,包括非连续存储,这提供了更多的灵活性。
  5. 动态性:张量的大小可以在运行时改变,而Numpy数组的大小是固定的。

总的来说,Numpy数组和张量都是多维数组的实现,但它们在用途、功能和性能上有所不同,适用于不同的计算场景。

Numpy数组实例
import numpy as np

# 广告支出(自变量),单位:千美元
X = np.array([10, 20, 30, 40, 50])

# 销售额(因变量),单位:百万美元
y = np.array([12, 21, 35, 40, 56])

# 为自变量添加一列1,用于计算线性回归的截距项:
#   np.ones(X.shape[0]):这部分创建了一个与 X 数组行数相同、所有元素都是1的数组。
#   X.shape[0] 表示 X 数组的行数,即样本数量。
#   这个全1数组将作为 X_b 的第一列,对应于线性方程中的截距项。
#   X:这是原始的特征数组,即广告支出数据。
#   np.c_:这是一个 Numpy 函数,用于按列(第二个轴)连接数组。c_ 前缀代表 "column"(列)。
X_b = np.c_[np.ones(X.shape[0]), X]

print(X_b)

# 计算系数(权重)和截距
# 使用正规方程求解:(X^T * X) * w = X^T * y
# 这里的目的是找到参数向量w,使得预测值与实际值之间的误差平方和最小。逐步代码解释:
# 1.转置操作 X_b.T
# 2.矩阵乘法 X_b.T.dot(X_b)
# 3.求逆操作 np.linalg.inv(X_b.T.dot(X_b)):
# 4.再次矩阵乘法 np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T):
# 5.最终矩阵乘法 ...dot(X_b.T).dot(y):
w = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y)

# 打印得到的线性回归模型参数
print("线性回归模型参数(权重和截距):")
print(w)

# 使用模型进行预测
y_pred = X_b.dot(w)

# 打印预测结果
print("预测的销售额:")
print(y_pred)

# 计算预测的准确度
rmse = np.sqrt((y_pred - y) ** 2).mean()
print("均方根误差(RMSE):", rmse)
### 蓝桥杯 C语言 实现 天干地支 纪年 题目 解法 #### 1. 干支纪年原理 在中国传统的干支纪年体系中,天干共有十个字符:“乙丙丁戊己庚辛壬癸”。地支则有十二个字符:“子丑寅卯辰巳午未申酉戌亥”。两者组合形成六十个不同的周期单位,称为“六十年花子”。 对于给定的一年,可以通过计算该年距离某个已知的参照年份(如2004年为申年),并利用模运算得出对应的天干和地支。 #### 2. 计算方法 假设要找到某一年`year`对应的天干地支: - **天干**:`(year - 2004) % 10`的结果加上起始位置即可得到对应索引[^1]。 - **地支**:由于每过两年才会重复一次相同的地支,因此需要除以二再取余数 `(year - 2004) / 2 % 12` 来获取地支配对表中的下标。 下面是一个简单的C语言函数用于返回指定公历年份对应的中文干支字符串表示形式: ```c #include <stdio.h> #include <string.h> void getGanZhi(int year, char *result){ const char* tian_gan[]={"","乙","丙","丁","戊","己","庚","辛","壬","癸"}; const char* di_zhi[]={"子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"}; int ganIndex=(year-2004)%10; if(ganIndex<0){ganIndex+=10;} //处理负数情况 int zhiIndex=((year-2004)/2)%12; if(zhiIndex<0){zhiIndex+=12;} strcpy(result,tian_gan[ganIndex]); strcat(result,di_zhi[zhiIndex]); } ``` 此代码片段定义了一个名为 `getGanZhi()` 的函数接收两个参数——目标年份以及用来存储结果的字符数组指针。通过上述提到的方法分别求得天干与地支的位置,并将其拼接成最终输出的形式保存至传入的目标缓冲区中。 #### 3. 测试实例 为了验证这段逻辑是否正确工作,在主程序里调用这个辅助功能来进行一些测试案例: ```c int main(){ char result[8]; memset(result,'\0',sizeof(result)); getGanZhi(2009,result); printf("2009年的干支是:%s\n",result); memset(result,'\0',sizeof(result)); getGanZhi(2023,result); printf("2023年的干支是:%s\n",result); return 0; } ``` 执行以上代码将会打印出如下信息: ``` 2009年的干支是:己丑 2023年的干支是:癸卯 ``` 这表明我们的算法能够准确无误地转换现代公元历法下的具体日期到古代中国特有的干支计时方式之上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值