深度学习系列笔记01数据操作

本文介绍了深度学习的基础知识,包括张量操作如重塑、运算、广播机制、索引和切片,以及数据预处理。详细探讨了线性代数中的标量、向量、矩阵概念,讲解了降维、点积和矩阵乘法等。同时,文章还涉及微分的概念,包括导数、偏导数和梯度。最后,简要介绍了自动求导的基本应用。
摘要由CSDN通过智能技术生成

1 预备知识

1.1 数据操作

1.1.1 入门

深度学习框架下的数据结构:

n维数组,也称为张量(tensor)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

import torch

x = torch.arange(12)
var = x.shape
a = x.numel() # number of elements
print(var, a)

x = x.reshape(3, 4)
print(x)
x = x.reshape(-1, 3)
print(x)
#########输出:
torch.Size([12]) 12
tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]])
tensor([[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7,  8],
        [ 9, 10, 11]])

可以使用arange创建一个行向量x。这个行向量包含从0开始的前12个整数,它们被默认创建为浮点数。张量中的每个值都称为张量的元素(element)。
我们可以通过在希望张量自动推断的维度放置-1来调用此功能。

import torch

y = torch.tensor([[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]],
                  [[2, 2, 2, 2], [2, 2, 2, 2], [2, 2, 2, 2]]]
                 )
print(y, y.shape)
########
tensor([[[1, 1, 1, 1],
         [1, 1, 1, 1],
         [1, 1, 1, 1]],

        [[2, 2, 2, 2],
         [2, 2, 2, 2],
         [2, 2, 2, 2]]]) torch.Size([2, 3, 4])

1.1.2 运算

对于任意具有相同形状的张量,常见的标准算术运算符(+、-、*、/和**)都可以被升级为按元素运算。

我们也可以把多个张量连结(concatenate)在一起,把它们端对端地叠起来形成一个更大的张量。 我们只需要提供张量列表,并给出沿哪个轴连结。下面的例子分别演示了当我们**沿行(轴-0,形状的第一个元素)按列(轴-1,形状的第二个元素)**连结两个矩阵时会发生什么情况。

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]])

A = torch.cat((X, Y), dim=0)
B = torch.cat((X, Y), dim=1)
print(A)
print(B)
print(X == Y) # 执行逻辑判断
print(X.sum()) # 
###########
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.]])
tensor([[False,  True, False,  True],
        [False, False, False, False],
        [False, False, False, False]])
tensor(66.)

2.1.3 广播机制

在某些情况下,即使形状不同,我们仍然可以通过调用广播机制(broadcasting mechanism)来执行按元素操作。这种机制的工作方式如下:首先,通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状。其次,对生成的数组执行按元素操作。

a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
print(a, b)
print(a + b)
###########
tensor([[0],
        [1],
        [2]]) tensor([[0, 1]])
tensor([[0, 1],
        [1, 2],
        [2, 3]])

1.1.4 索引和切片

像在任何其他Python数组中一样,张量中的元素可以通过索引访问。与任何Python数组一样:第一个元素的索引是0;

import torch

X = torch.arange(12, dtype=torch.float32).reshape(3, 4)
print(X[-1])
print(X[1: 3])
X[1, 2] = 9
X[::2, :] = 12
print(X)
#######
tensor([ 8.,  9., 10., 11.])
tensor([[ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.]])
tensor([[12., 12., 12., 12.],
        [ 4.,  5.,  9.,  7.],
        [12., 12., 12., 12.]])

1.1.5 节省内存

运行一些操作可能会导致为新结果分配内存。例如,如果我们用Y = X + Y,我们将取消引用Y指向的张量,而是指向新分配的内存处的张量。

import torch

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]])

before = id(Y)
print(id(Y))
Y = X + Y
print(id(Y))
print(id(Y) == before)

#######
1559335469952
1559274127680
False

如果在后续计算中没有重复使用X,可以使用X[:] = X + Y或X += Y来减少操作的内存开销。

1.1.6 转换为其他 Python 对象

import torch

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]])

A = X.numpy()
B = torch.tensor(A)
print(type(A), type(B))

a = torch.tensor([3.5])
print(a, a.item(), float(a), int(a))
##########
<class 'numpy.ndarray'> <class 'torch.Tensor'>
tensor([3.5000]) 3.5 3.5 3

1.1.7 练习

import torch

a = torch.arange(24).reshape(3, 4, 2)
print(a)
tensor([[[ 0,  1],
         [ 2,  3],
         [ 4,  5],
         [ 6,  7]],

        [[ 8,  9],
         [10, 11],
         [12, 13],
         [14, 15]],

        [[16, 17],
         [18, 19],
         [20, 21],
         [22, 23]]])

在这里插入图片描述

x = torch.arange(24).reshape((3, 4, 2))
y = torch.arange(8).reshape((4, 2))
print(x, y)
c = x + y
print(c)

1.2 数据预处理

1.2.1 读取数据集

举一个例子,我们首先创建一个人工数据集,并存储在csv(逗号分隔值)文件…/data/house_tiny.csv中。

1.3 线性代数

1.3.1 标量、向量、矩阵

标量由只有一个元素的张量表示。

import torch

x = torch.tensor([3.0])
y = torch.tensor([2.0])

x + y, x * y, x / y, x**y

将向量视为标量值组成的列表。将这些标量值称为向量的元素(element)或分量(component)。

import torch

x = torch.arange(4)
print(x, len(x), x.shape)
#######
tensor([0, 1, 2, 3]) 4 torch.Size([4])

正如向量将标量从零阶推广到一阶,矩阵将向量从一阶推广到二阶。在代码中表示为具有两个轴的张量。

作为方矩阵的一种特殊类型,对称矩阵(symmetric matrix)A等于其转置:A=A⊤。

B = torch.tensor([[1, 2, 3], [2, 0, 4], [3, 4, 5]])
print(B == B.T)
#######
tensor([[True, True, True],
        [True, True, True],
        [True, True, True]])

1.3.2 张量和算法的基本性质

向量是一阶张量,矩阵是二阶张量。
当我们开始处理图像时,张量将变得更加重要,图像以n维数组形式出现,其中3个轴对应于高度、宽度,以及一个通道(channel)轴,用于堆叠颜色通道(红色、绿色和蓝色)。

import torch

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
B = A.clone()  # 通过分配新内存,将A的一个副本分配给B
print(A, A + B, A*B)

张量×张量:
在这里插入图片描述

将张量乘以或加上一个标量不会改变张量的形状,其中张量的每个元素都将与标量相加或相乘。

a = 2
X = torch.arange(24).reshape(2, 3, 4)
print(a * X)

1.3.3 降维

import torch

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
print(A, A.shape, A.sum())

A_sum_axis0 = A.sum(axis=0) # 列求和
print(A_sum_axis0, A_sum_axis0.shape)

A_sum_axis1 = A.sum(axis=1) # 行求和
print(A_sum_axis1, A_sum_axis1.shape)

print(A.sum(axis=[0, 1]))  # Same as `A.sum()`

print(A.mean(), A.sum() / A.numel())

print(A.mean(axis=0), A.sum(axis=0) / A.shape[0])

默认情况下,调用求和函数会沿所有的轴降低张量的维度,使它变为一个标量。 我们还可以指定张量沿哪一个轴来通过求和降低维度

在调用函数sum()时指定axis=0,则是对每一列的所有元素求和。这样nm的矩阵就变为,1m的行向量。同理,axis=1时,对每一行的元素求和,这样nm的矩阵就变为,n1的列向量。

沿着行和列对矩阵求和,等价于对矩阵的所有元素进行求和。(A.sum(axis=[0, 1]) )

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
print(A)
print(A.mean(), A.sum() / A.numel())
print(A.mean(axis=0), A.sum(axis=0) / A.shape[0])

一个与求和相关的量是平均值(mean或average)。我们通过将总和除以元素总数来计算平均值。在代码中,我们可以调用函数来计算任意形状张量的平均值。同样,计算平均值的函数也可以沿指定轴降低张量的维度。

1.3.4 非降维求和

有时候,我们求和不是为了降维,如何防止呢?

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)

print(A.sum(axis=1))

sum_A = A.sum(axis=1, keepdims=True)
print(sum_A)
print(A / sum_A)

print(A)
print(A.cumsum(axis=0))

sum_A在对每行进行求和后仍保持两个轴。

如果我们想沿某个轴计算A元素的累积总和,比如axis=0(按行计算),我们可以调用cumsum函数。此函数不会沿任何轴降低输入张量的维度。

1.3.5 点积、矩阵-向量积、矩阵-矩阵乘法

点积:相同位置的按元素乘积的和。

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
x = torch.arange(4, dtype=torch.float32)
y = torch.tensor([1., 1, 1, 1])

print(x, y, torch.dot(x, y))
#########
tensor([0., 1., 2., 3.]) tensor([1., 1., 1., 1.]) tensor(6.)

注意,我们可以通过执行按元素乘法,然后进行求和来表示两个向量的点积:(torch.sum(x * y))

点积表示加权平均(weighted average)。将两个向量归一化得到单位长度后,点积表示它们夹角的余弦。

矩阵-向量积:比如一个矩阵是mn,一个向量是n1,两者相乘就是m*1的列向量,矩阵的基本运算嘛。

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
x = torch.arange(4, dtype=torch.float32)
a = torch.mv(A, x)
print(a)
##########
tensor([ 14.,  38.,  62.,  86., 110.])

矩阵-矩阵乘法可以简单地称为矩阵乘法,不应与“哈达玛积”混淆。(符合线性代数计算规则。)

1.3.6 范数和目标

线性代数中最有用的一些运算符是范数(norm)。非正式地说,一个向量的范数告诉我们一个向量有多大。

欧几里得距离是一个范数:具体而言,它是L2范数。假设n维向量x中的元素是x1,…,xn,其L2范数是向量元素平方和的平方根。(可以理解为张量的模长)
在这里插入图片描述

u = torch([3.0, -4.0])
torch.nomr(u)
######
a = 5.

在深度学习中,我们更经常地使用L2范数的平方。你还会经常遇到L1范数,它表示为向量元素的绝对值之和

在这里插入图片描述
与L2范数相比,L1范数受异常值的影响较小。为了计算L1范数,我们将绝对值函数和按元素求和组合起来。

u = torch([3.0, -4.0])
a = torch.abs(u).sum()
#####
a = 7.

L2范数和L1范数都是更一般的Lp范数的特例:
(P = 1, P = 2)
在这里插入图片描述

类似于向量的L2范数,矩阵X∈Rm×n的弗罗贝尼乌斯范数(Frobenius norm)是矩阵元素平方和的平方根:

在这里插入图片描述
简单的理解就是,把张量拉成向量,然后和向量一样求和取根号。(类似距离公式)

C = torch.norm(torch.ones((4, 9)))
print(torch.norm(C))
########
tensor(6.)

1.3.7 练习

import torch

## 1
A = torch.arange(12, dtype=torch.float32).reshape(3, 4)
C = A.T
print(A == C.T)

## 2
B = torch.rand(12, dtype=torch.float32).reshape(3, 4)
print(A.T + B.T == (A + B).T)

## 3.
A = torch.rand(9, dtype=torch.float32).reshape(3, 3)
C = A + A.T
print(C == C.T)

X = torch.arange(24).reshape(2, 3, 4)
print(len(X))  # 针对0轴求长度
print(X.sum(axis=0))
print(X.sum(axis=1))
print(X.sum(axis=2))

1.4 微分

在这里插入图片描述

1.4.1 导数和微分

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

import numpy as np
from IPython import display
from d2l import torch as d2l

def f(x):
    return 3 * x ** 2 - 4 * x

def numerical_lim(f, x, h):
    return (f(x + h) - f(x)) / h

h = 0.1
for i in range(5):
    print(f'h={h:.5f}, numerical limit={numerical_lim(f, 1, h):.5f}')
    h *= 0.1

1.4.2 偏导数

在这里插入图片描述

1.4.3 梯度

我们可以连结一个多元函数对其所有变量的偏导数,以得到该函数的梯度(gradient)向量。
在这里插入图片描述

import numpy as np

def numerical_lim(f, x1, x2, h):
    return (f(x1+h, x2) - f(x1, x2)+(f(x1, x2+h) - f(x1, x2)))/h

def f(x1, x2):
    return 3*x1*x1 + 5*np.exp(x2)

h = 0.1
for i in range(5):
    print(f'h = {h:.7f}, piandaoshu = {numerical_lim(f,1,1,h)}')
    h *= 0.1

1.4.4 链式法则

在这里插入图片描述

1.5 自动求导

1.5.1 简单的例子

import torch

x = torch.arange(4.0, requires_grad=True)
print(x.grad)  # 默认值是None

y = 2 * torch.dot(x, x)  # 函数 y=2x⊤x 关于 x 的梯度应为 4x
print(y)
y.backward()
print(x.grad)
print(x.grad == 4 * x)
#########
None
tensor(28., grad_fn=<MulBackward0>)
tensor([ 0.,  4.,  8., 12.])
tensor([True, True, True, True])
print(x.grad)  # 在默认情况下,PyTorch会累积梯度,我们需要清除之前的值
x.grad.zero_()
print(x.grad)

y = x.sum()
y.backward()
print(x.grad)
##############
tensor([ 0.,  4.,  8., 12.])
tensor([0., 0., 0., 0.])
tensor([1., 1., 1., 1.])

这后面的内容,我还不太会,日后慢慢学习吧!┭┮﹏┭┮

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值