Chapter3 :It starts with a Tensor

import torch

3.4 Named Tensors

img_t = torch.randn(3,5,5) #channel, rows, cols
weights = torch.tensor([0.2126,0.7152,0.0722])
batch_t = torch.randn(2,3,5,5) #shape[batich,channels,rows,cols]
img_grey_native = img_t.mean(-3)

这段代码的作用是将一个三维的张量img_t表示的RGB彩色图像转换为一个二维的灰度图像。

具体来说,img_t应该是一个形状为(channels, height, width)的张量,其中channels=3表示有三个通道(R、G、B),height和width分别为图像的高度和宽度。

img_t.mean(-3)表示在img_t的第一个维度(即通道维度)上计算平均值,得到一个形状为(height, width)的张量。这相当于将三个通道的像素值取平均值,得到一个灰度值表示每个像素的亮度。最终得到的img_grey_native张量表示原始彩色图像的灰度版本。

batch_grey_native = batch_t.mean(-3)

这段代码的作用是将一个四维的张量batch_t表示的RGB彩色图像批次转换为一个三维的灰度图像批次。

具体来说,batch_t应该是一个形状为(batch_size, channels, height, width)的张量,其中batch_size表示批次大小,channels=3表示有三个通道(R、G、B),heightwidth分别为图像的高度和宽度。

batch_t.mean(-3)表示在batch_t的第二个维度(即通道维度)上计算平均值,得到一个形状为(batch_size, height, width)的张量。这相当于将三个通道的像素值取平均值,得到一个灰度值表示每个像素的亮度。最终得到的batch_grey_native张量表示原始彩色图像批次的灰度版本。

print(img_grey_native.shape,batch_grey_native.shape)
#torch.Size([5, 5]) torch.Size([2, 5, 5])
torch.Size([5, 5]) torch.Size([2, 5, 5])
unsqueezed_weights = weights.unsqueeze(-1).unsqueeze_(-1)
unsqueezed_1 = weights.unsqueeze(-1)
print('weights.unsqueeze(-1)',unsqueezed_1.shape,'\n','unsqueezed_weights.shape',unsqueezed_weights.shape,'\n','weights.shape',weights.shape)
'''
weights.unsqueeze(-1) torch.Size([3, 1]) 
unsqueezed_weights.shape torch.Size([3, 1, 1]) 
weights.shape torch.Size([3])
'''
weights.unsqueeze(-1) torch.Size([3, 1]) 
 unsqueezed_weights.shape torch.Size([3, 1, 1]) 
 weights.shape torch.Size([3])

在PyTorch中,张量之间的运算需要满足一定的维度规则,这就是广播机制。简单来说,如果两个张量在某个维度上的大小不同,但可以通过扩展其中一个张量的大小来匹配另一个张量的大小,那么它们就可以进行元素级别的运算。在这个过程中,PyTorch会自动复制张量以匹配它们的形状,这就是广播。

例如,如果有一个形状为(3, 4)的张量,和一个形状为(4,)的张量,那么它们可以进行加法运算。在这个过程中,PyTorch会自动将第二个张量扩展为(1, 4)的形状,然后复制它3次,以匹配第一个张量的形状,最终得到一个形状为(3, 4)的结果张量。

这段代码的作用是将一个二维的权重矩阵weights转换为一个四维的张量unsqueezed_weights

具体来说,weights应该是一个形状为(height, width)的二维矩阵,表示卷积操作中的卷积核(kernel)或者池化操作中的池化窗口(pooling window)。

weights.unsqueeze(-1)表示在weights的最后一个维度上(即第二个维度)增加一个维度,得到一个形状为(height, width, 1)的张量。这相当于将权重矩阵中的每个元素都变成一个形状为(1,)的张量。

unsqueeze_(-1)表示在新的最后一个维度上(即第四个维度)再次增加一个维度,得到一个形状为(height, width, 1, 1)的张量。这相当于将heightwidth两个维度上的每个元素都变成一个形状为(1, 1)的张量。

最终得到的unsqueezed_weights张量表示一个四维的权重张量,其中第一个维度表示卷积或池化的输出通道数,第二个和第三个维度表示卷积或池化窗口的大小,第四个维度表示每个权重的形状。在卷积或池化操作中,这个权重张量会和输入张量进行卷积或池化运算,得到输出张量。

img_weights = (img_t * unsqueezed_weights)

这段代码的作用是将一个形状为(channels, height, width)的输入张量img_t和一个形状为(height, width, 1, 1)的权重张量unsqueezed_weights进行逐元素乘法运算,得到一个形状相同的输出张量img_weights

具体来说,img_t表示一个RGB彩色图像的三个通道,每个通道的大小为(height, width)unsqueezed_weights表示一个卷积或池化操作的权重张量,每个权重的形状为(1,1)

在这个过程中,PyTorch会自动对unsqueezed_weights进行广播操作,使其形状与img_t相同。具体来说,PyTorch会将unsqueezed_weights的第三个和第四个维度分别扩展为channels1,以匹配img_t的形状。这样,img_tunsqueezed_weights就可以按通道逐元素相乘得到img_weights

最终得到的img_weights张量表示输入图像和权重张量的逐元素乘积结果,可以用于卷积或池化操作的计算。

batch_weights = (batch_t * unsqueezed_weights)

这段代码的作用是将一个形状为(batch_size, channels, height, width)的输入张量批次batch_t和一个形状为(height, width, 1, 1)的权重张量unsqueezed_weights进行逐元素乘法运算,得到一个形状相同的输出张量批次batch_weights

具体来说,batch_t表示一个RGB彩色图像的三个通道,每个通道的大小为(height, width)unsqueezed_weights表示一个卷积或池化操作的权重张量,每个权重的形状为(1,1)

在这个过程中,PyTorch会自动对unsqueezed_weights进行广播操作,使其形状与batch_t相同。具体来说,PyTorch会将unsqueezed_weights的第三个和第四个维度分别扩展为channels1,以匹配batch_t的形状。这样,batch_tunsqueezed_weights就可以按通道逐元素相乘得到batch_weights

最终得到的batch_weights张量表示输入图像批次和权重张量的逐元素乘积结果,可以用于卷积或池化操作的计算。

img_grey_weighted = img_weights.sum(-3)

这段代码的作用是将一个形状为(channels, height, width)的逐元素乘积结果张量img_weights的第一个维度(即通道维度)上的元素求和,得到一个形状为(height, width)的灰度图像张量img_grey_weighted

具体来说,img_weights表示输入图像和权重张量的逐元素乘积结果,每个通道的大小为(height, width)

img_weights.sum(-3)表示在img_weights的第一个维度上(即通道维度)上进行求和操作,得到一个形状为(height, width)的张量。这相当于将三个通道的逐元素乘积结果相加,得到一个灰度值表示每个像素的加权亮度。最终得到的img_grey_weighted张量表示原始彩色图像的加权灰度版本。

batch_grey_weighted = batch_weights.sum(-3)

这段代码的作用是将一个形状为(batch_size, channels, height, width)的逐元素乘积结果张量批次batch_weights的第二个维度(即通道维度)上的元素求和,得到一个形状为(batch_size, height, width)的灰度图像批次batch_grey_weighted

具体来说,batch_weights表示输入图像批次和权重张量的逐元素乘积结果,每个通道的大小为(height, width)

batch_weights.sum(-3)表示在batch_weights的第二个维度上(即通道维度)上进行求和操作,得到一个形状为(batch_size, height, width)的张量。这相当于将每个图像的三个通道的逐元素乘积结果相加,得到一个灰度值表示每个像素的加权亮度。最终得到的batch_grey_weighted张量表示原始彩色图像批次的加权灰度版本。

print(img_grey_weighted.shape,batch_grey_weighted.shape,unsqueezed_weights.shape,batch_t.shape)
#torch.Size([5, 5]) torch.Size([2, 5, 5]) torch.Size([3, 1, 1])
torch.Size([5, 5]) torch.Size([2, 5, 5]) torch.Size([3, 1, 1]) torch.Size([2, 3, 5, 5])
img_gray_weighted_fancy = torch.einsum('...chw,c->...hw', img_t, weights)
batch_gray_weighted_fancy = torch.einsum('...chw,c->...hw', batch_t, weights)
batch_gray_weighted_fancy.shape
torch.Size([2, 5, 5])

在这行代码中,torch.einsum()函数用于计算加权平均后的灰度图。具体来说,它将输入张量img_t与权重张量weights进行逐元素乘法操作,并对乘积进行求和,得到一个形状为(B, H, W)的输出张量img_gray_weighted_fancy,其中B是批次大小,HW是图像的高度和宽度。

下面是对该行代码的详细解释:

img_gray_weighted_fancy = torch.einsum('...chw,c->...hw', img_t, weights)
  • img_t是一个形状为(B, C, H, W)的4D张量,其中B是批次大小,C是输入通道数,HW是图像的高度和宽度。
  • weights是一个形状为(C,)的1D张量,其中C是输入通道数。这个张量中的每个元素都是一个权重值,用于计算加权平均。
  • '...chw,c->...hw'是一个描述张量运算的字符串,它告诉torch.einsum()函数如何计算加权平均。具体来说,这个字符串表示将输入张量img_t的最后两个维度(hw)与权重张量weights的唯一维度(c)相乘,并对乘积进行求和。...表示在这个字符串中可能存在其他维度,但它们不会参与运算。通过使用...可以使此代码具有通用性,即使输入张量具有不同的维数,也可以正确计算加权平均。
  • img_gray_weighted_fancy是一个形状为(B, H, W)的输出张量,其中每个元素都是输入张量中对应位置的加权平均值。
weights_named = torch.tensor([0.2126, 0.7152, 0.0722], names=['channels'])
weights_named
C:\Users\yeqian.xu\AppData\Local\Temp\ipykernel_3036\2371314847.py:1: UserWarning: Named tensors and all their associated APIs are an experimental feature and subject to change. Please do not use them for anything important until they are released as stable. (Triggered internally at  C:\b\abs_bao0hdcrdh\croot\pytorch_1675190257512\work\c10/core/TensorImpl.h:1408.)
  weights_named = torch.tensor([0.2126, 0.7152, 0.0722], names=['channels'])





tensor([0.2126, 0.7152, 0.0722], names=('channels',))

在PyTorch中,named tensors是一种具有命名维度的张量表示方式。在传统的张量表示中,张量的维度只是一个无名的整数序列,不具有任何语义信息。而在named tensors中,每个维度都可以被赋予一个名字,用于标识该维度所表示的含义。例如,在图像处理中,可以使用named tensors来表示图像张量,其中每个维度可以被赋予一个名字,例如“批次大小”、“通道数”、“高度”、“宽度”等,使得张量的含义更加清晰和直观。

使用named tensors可以使得张量操作更加易于理解和维护,尤其是在处理具有多个维度、多个通道或多个特征的数据时更为常见。它可以让代码更加简洁,使得代码的含义更加明确,也可以提高代码的可读性和可维护性。

在PyTorch中,named tensors是通过在张量上添加names属性来实现的。这个属性是一个元组,其中包含了每个维度的名称。例如,以下代码创建了一个形状为(2, 3)的named tensor,并为它的两个维度分别赋予了名称’batch’和’channels’:

import torch

Create a named tensor

x = torch.randn(2, 3, names=(‘batch’, ‘channels’))

Print the tensor and its names

print(x)
print(x.names)

这行代码创建了一个形状为(3,)的1D张量weights_named,其中包含了三个通道的权重值。与普通张量不同的是,这个张量还包含了一个名为'channels'的维度名称,用于标识权重张量中的维度。

使用命名维度可以使得张量操作更加直观和易于理解。在这个例子中,我们可以使用weights_named张量的命名维度来描述张量的含义,例如:

  • weights_named.channels表示权重张量中的“通道”维度,即权重张量中每个元素的含义是对应通道的权重值。
  • weights_named.sum('channels')表示对权重张量中的“通道”维度进行求和,得到一个标量值,表示所有通道的权重之和。
  • weights_named.unsqueeze('batch')表示在权重张量的“批次”维度上添加一个大小为1的维度,得到一个形状为(1, 3)的张量,表示批次大小为1,通道数为3。
img_named =  img_t.refine_names(..., 'channels', 'rows', 'columns')
batch_named = batch_t.refine_names(..., 'channels', 'rows', 'columns')
print("img named:", img_named.shape, img_named.names)
print("batch named:", batch_named.shape, batch_named.names)

'''
img named: torch.Size([3, 5, 5]) ('channels', 'rows', 'columns')
batch named: torch.Size([2, 3, 5, 5]) (None, 'channels', 'rows', 'columns')
'''
img named: torch.Size([3, 5, 5]) ('channels', 'rows', 'columns')
batch named: torch.Size([2, 3, 5, 5]) (None, 'channels', 'rows', 'columns')





"\nimg named: torch.Size([3, 5, 5]) ('channels', 'rows', 'columns')\nbatch named: torch.Size([2, 3, 5, 5]) (None, 'channels', 'rows', 'columns')\n"
weights_aligned = weights_named.align_as(img_named)
print(weights_aligned.shape, weights_aligned.names)
torch.Size([3, 1, 1]) ('channels', 'rows', 'columns')

3.6 The Tensor API

a = torch.ones(3,2)
a_t = torch.transpose(a,0,1)
print('a',a,'\n','a_t',a_t)
a tensor([[1., 1.],
        [1., 1.],
        [1., 1.]]) 
 a_t tensor([[1., 1., 1.],
        [1., 1., 1.]])

这行代码使用了PyTorch中的transpose()函数,用于对输入张量进行转置操作。具体来说,它将指定的两个维度进行交换,从而改变张量的维度顺序。

在这个例子中,a是一个张量,我们需要对它进行转置操作。具体来说,我们将a的第0维和第1维进行交换,从而改变张量的维度顺序。

3.8.2 Transposing without copying

points = torch.tensor([[4.0,1.0],[5.0,3.0],[2.0,1.0]])
points_t = points.t() #transpose
print(points)
print(points_t)
tensor([[4., 1.],
        [5., 3.],
        [2., 1.]])
tensor([[4., 5., 2.],
        [1., 3., 1.]])
points.stride()
print(points.stride())
(2, 1)
print(points_t.stride())
(1, 2)

这行代码使用了PyTorch张量的stride()方法,用于获取张量在内存中的跨度信息。具体来说,它返回一个元组,其中包含了每个维度上相邻元素在内存中的偏移量,以字节为单位。

在这个例子中,points是一个形状为(3, 2)的2D张量,其中包含了三个二维坐标点的坐标值。
需要注意的是,stride()方法返回的是一个元组,而不是一个张量。在PyTorch中,一个张量的跨度信息通常用于计算张量的索引和切片操作,以及张量的转置、重塑等操作。

3.9 Moving Tensors to the GPU

points_gpu = torch.tensor([[4.0,1.0],[5.0,3.0],[2.0,1.0]])
import torch
print(torch.cuda.is_available())
False
points_gpu = points.to(device='cuda')
---------------------------------------------------------------------------

AssertionError                            Traceback (most recent call last)

Cell In[68], line 1
----> 1 points_gpu = points.to(device='cuda')


File ~\AppData\Local\anaconda3\lib\site-packages\torch\cuda\__init__.py:211, in _lazy_init()
    207     raise RuntimeError(
    208         "Cannot re-initialize CUDA in forked subprocess. To use CUDA with "
    209         "multiprocessing, you must use the 'spawn' start method")
    210 if not hasattr(torch._C, '_cuda_getDeviceCount'):
--> 211     raise AssertionError("Torch not compiled with CUDA enabled")
    212 if _cudart is None:
    213     raise AssertionError(
    214         "libcudart functions unavailable. It looks like you have a broken build?")


AssertionError: Torch not compiled with CUDA enabled

points_gpu = points.to(device=‘cuda’)

这行代码使用了PyTorch中的to()方法,用于将一个张量移动到指定的设备上。具体来说,它将张量的数据和计算图都移动到指定的设备上,以便在该设备上进行计算。

3.14 Exercise

  1. Creat a tensor a from list(range(9)).Predict and then check the size, offset, and stride
a = torch.tensor(range(9))
print(a)
#tensor([0, 1, 2, 3, 4, 5, 6, 7, 8])

print(a.shape)
#torch.Size([9])

print(a.storage_offset())
#offset = 0

print(a.stride())
#stride = 1
tensor([0, 1, 2, 3, 4, 5, 6, 7, 8])
torch.Size([9])
0
(1,)

a. Create a new tensor b=a.view(3,3), what does view do?Check that a and b share the same storage

b = a.view(3,3)
print(b)
tensor([[0, 1, 2],
        [3, 4, 5],
        [6, 7, 8]])

view rezise the tensor a, make it into 33 size, instead of 19

# 检查共享存储
print(a.storage().data_ptr() == b.storage().data_ptr())
#True
True

b. Create a tensor c = b[1:1]. Predict and then check the size, offset and stride.

c = b[1:1]
print(c)
tensor([], size=(0, 3), dtype=torch.int64)
print(c.shape)
#torch.Size([0, 3])

print(c.storage_offset())
#offset=3

print(c.stride())
#stride(3, 1)
torch.Size([0, 3])
3
(3, 1)

2.Pick a mathematical operation like cosine or square root. Can you find a corresponding function in the torch library?

import torch

x = torch.tensor([1,1,1])
y = torch.cos(x)
z = torch.sqrt(x)
print(y) 
#tensor([0.5403, 0.5403, 0.5403])

print(z)
#tensor([1., 1., 1.])
tensor([0.5403, 0.5403, 0.5403])
tensor([1., 1., 1.])

a. Apply the function element-wise to a. Why does it return an error?

cos_a = torch.cos(a)
print(cos_a)
sqrt_a = torch.sqrt(a)
print(sqrt_a) 

#NO ERROR, DK WHY
tensor([ 1.0000,  0.5403, -0.4161, -0.9900, -0.6536,  0.2837,  0.9602,  0.7539,
        -0.1455])
tensor([0.0000, 1.0000, 1.4142, 1.7321, 2.0000, 2.2361, 2.4495, 2.6458, 2.8284])

b. What operation is required to make the function work?
NO ERROR

c. Is there a version of your function that operates in place?

import torch

# Create a tensor a and convert it to torch.float
a = torch.tensor(range(9)).float()
print("Original tensor:", a)

# Apply cosine function element-wise in-place
a.cos_()
print("Tensor after applying cosine in-place:", a)

# Apply square root function element-wise in-place
a.sqrt_()
print("Tensor after applying square root in-place:", a)
Original tensor: tensor([0., 1., 2., 3., 4., 5., 6., 7., 8.])
Tensor after applying cosine in-place: tensor([ 1.0000,  0.5403, -0.4161, -0.9900, -0.6536,  0.2837,  0.9602,  0.7539,
        -0.1455])
Tensor after applying square root in-place: tensor([1.0000, 0.7351,    nan,    nan,    nan, 0.5326, 0.9799, 0.8683,    nan])

Original tensor: tensor([0., 1., 2., 3., 4., 5., 6., 7., 8.])
Tensor after applying cosine in-place: tensor([ 1.0000, 0.5403, -0.4161, -0.9900, -0.6536, 0.2837, 0.9602, 0.7539,
-0.1455])
Tensor after applying square root in-place: tensor([1.0000, 0.7351, nan, nan, nan, 0.5326, 0.9799, 0.8683, nan])


你可能感兴趣的:(python,开发语言,人工智能,机器学习,深度学习)