动手学深度学习Day02

1.数据操作

详细请参考教学文档 https://zh.d2l.ai/chapter_preliminaries/ndarray.html#id2

1.1 N维数组

N维数组是机器学习和神经网络的主要数据结构

0-d(标量) 1.0 一个类别
1-d(向量) [...] 一个特征向量
2-d(矩阵) [[...]] 一个样本-特征矩阵
3-d [[[...]]] RGB图片(宽x高x通道)
4-d[ [[[[....]]]] 一个RGB图片批量(批量大小x宽x高x通道)
5-d [[[[[......]]]]] 一个视频批量(批量大小x时间x宽x高x通道)

1.2 访问元素

一个元素:[1,2]
一行:[1,:]
一列:[:,2]
子区域:[1:3,1:]或者[::3,::2]

1.3 常用的数据操作

基本数学运算、广播、索引、切片、内存节省和转换其他Python对象

操作

说明

x = torch.arange(12)

使用 arange 创建一个行向量 x

x.shape

通过张量的shape属性来访问张量(沿每个轴的长度)的形状

x.numel()

张量中元素的总数

X = x.reshape(3, 4)

要想改变一个张量的形状而不改变元素数量和元素值,可以调用reshape函数

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

使用全0、全1、其他常量,或者从特定分布中随机采样的数字

torch.ones((2, 3, 4))

创建一个形状为(2,3,4)的张量,其中所有元素都设置为1

torch.randn(3, 4)

通过从某个特定的概率分布中随机采样来得到张量中每个元素的值

torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])

通过提供包含数值的Python列表(或嵌套列表),来为所需张量中的每个元素赋予确定值

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

y = torch.tensor([2, 2, 2, 2])

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

常见的标准算术运算符(+、-、*、/和**)都可以被升级为按元素运算

torch.exp(x)

求幂

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

torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)

把多个张量连结(concatenate)在一起,dtype表示数据类型,dim=0表示沿行连接两个矩阵,dim=1表示按列连接两个矩阵

X == Y

通过逻辑运算符构建二元张量

X.sum()

对张量中的所有元素进行求和,会产生一个单元素张量

a = torch.arange(3).reshape((3, 1))

b = torch.arange(2).reshape((1, 2))

a + b

即使形状不同,我们仍然可以通过调用 广播机制(broadcasting mechanism)来执行按元素操作,将两个矩阵广播为一个更大的 3×2 矩阵

X[-1], X[1:3]

可以用[-1]选择最后一个元素,可以用[1:3]选择第二个和第三个元素

X[1, 2] = 9

通过指定索引来将元素写入矩阵

X[0:2, :] = 12

为多个元素赋值相同的值

A = X.numpy()

B = torch.tensor(A)

转换为NumPy张量

a = torch.tensor([3.5])

将大小为1的张量转换为Python标量

节省内存操作中,注意区分以下两个,一个正确一个错误

before = id(Y)
Y = Y + X
id(Y) == before #False
before = id(X)
X += Y
id(X) == before #True

在实际操作过程中,也可以使用如下方式进行节省内存操作

Z = torch.zeros_like(Y)
print('id(Z):', id(Z)) #id(Z): 1576226143744
Z[:] = X + Y
print('id(Z):', id(Z)) # id(Z): 1576226143744

2.数据预处理

详细请参考教学文档 https://zh.d2l.ai/chapter_preliminaries/pandas.html

2.1 读取数据集

创建一个人工数据集,并存储在CSV(逗号分隔值)文件 ../data/house_tiny.csv

import os

os.makedirs(os.path.join('..', 'data'), exist_ok=True)
data_file = os.path.join('..', 'data', 'house_tiny.csv')
with open(data_file, 'w') as f:
    f.write('NumRooms,Alley,Price\n')  # 列名
    f.write('NA,Pave,127500\n')  # 每行表示一个数据样本
    f.write('2,NA,106000\n')
    f.write('4,NA,178100\n')
    f.write('NA,NA,140000\n')
os.makedirs(name, mode=0o777, exist_ok=False)
作用
用来创建 多层目录(单层请用os.mkdir)
参数说明
name:你想创建的目录名
mode:要为目录设置的权限数字模式,默认的模式为 0o777 (八进制)。
exist_ok:是否在目录存在时触发异常。如果exist_ok为 False(默认值),则在目标目录已存在的情况下 触发FileExistsError异常;如果exist_ok为 True,则在目标目录已存在的情况下 不会触发FileExistsError异常。
os.path.join(path1[, path2[, ...]]) 把目录和文件名合成一个路径

从创建的CSV文件中加载原始数据集

# 如果没有安装pandas,只需取消对以下行的注释来安装pandas
# !pip install pandas
import pandas as pd

data = pd.read_csv(data_file)
print(data)

"""
   NumRooms Alley   Price
0       NaN  Pave  127500
1       2.0   NaN  106000
2       4.0   NaN  178100
3       NaN   NaN  140000
"""

2.2 处理缺失值

为了处理缺失的数据,典型的方法包括插值法删除法,其中插值法用一个替代值弥补缺失值,而删除法则直接忽略缺失值。在这里我们选用的是插值法

通过位置索引iloc,我们将data分成inputs和outputs, 其中前者为data的前两列,而后者为data的最后一列。 对于inputs中缺少的数值,我们用同一列的均值替换“NaN”项。

inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = inputs.fillna(inputs.mean()) #mean()函数功能:取平均值
print(inputs)

"""
   NumRooms Alley
0       3.0  Pave
1       2.0   NaN
2       4.0   NaN
3       3.0   NaN
"""

[对于inputs中的类别值或离散值,我们将“NaN”视为一个类别。] 由于“巷子类型”(“Alley”)列只接受两种类型的类别值“Pave”和“NaN”, pandas可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。 巷子类型为“Pave”的行会将“Alley_Pave”的值设置为1,“Alley_nan”的值设置为0。 缺少巷子类型的行会将“Alley_Pave”和“Alley_nan”分别设置为0和1。

inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)

"""
   NumRooms  Alley_Pave  Alley_nan
0       3.0           1          0
1       2.0           0          1
2       4.0           0          1
3       3.0           0          1
"""

pandas.get_dummies参数详解

data : array-like, Series, or DataFrame 输入的数据
prefix : string, get_dummies转换后,列名的前缀,默认为None
columns : 指定需要实现类别转换的列名 否则转换所有类别性的列
dummy_na : bool, default False 增加一列表示空缺值,如果False就忽略空缺值
drop_first : bool, default False 获得k中的k-1个类别值,去除第一个,防止出现多重共线性

2.3 转换为张量格式

现在inputs和outputs中的所有条目都是数值类型,它们可以转换为张量格式。

import torch

X, y = torch.tensor(inputs.values), torch.tensor(outputs.values)
print(X,'\n', y)

"""
tensor([[3., 1., 0.],
        [2., 0., 1.],
        [4., 0., 1.],
        [3., 0., 1.]], dtype=torch.float64) 
 tensor([127500, 106000, 178100, 140000])
"""

3.线性代数

详细请参考教学文档 https://zh.d2l.ai/chapter_preliminaries/linear-algebra.html

3.1 标量

标量由只有一个元素的张量表示,其中标量变量由普通小写字母表示(例如, 、 和 )

下面的代码将实例化两个标量,并执行一些熟悉的算术运算,即加法、乘法、除法和指数。

import torch

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

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

"""
(tensor(5.), tensor(6.), tensor(1.5000), tensor(9.))
"""

3.2 向量

可以被视为标量值组成的列表,向量通常记为粗体、小写的符号 (例如,、和)。
人们通过一维张量表示向量。一般来说,张量可以具有任意长度,取决于机器的内存限制。
可以通过张量的索引来访问任一元素。如x[3]
可以通过调用Python的内置len()函数来 访问张量的长度。如len(x)
当用张量表示一个向量(只有一个轴)时,我们也可以通过.shape属性访问向量的长度
向量或轴的维度被用来表示向量或轴的长度,即向量或轴的元素数量。
然而,张量的维度用来表示张量具有的轴数。

3.3 矩阵

矩阵,我们通常用粗体、大写字母来表示 (例如,、和), 在代码中表示为具有两个轴的张量。

当调用函数来实例化张量时, 我们可以[通过指定两个分量 和 来创建一个形状为 × 的矩阵]。

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

"""
tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15],
        [16, 17, 18, 19]])
"""

在代码中访问矩阵的转置

A.T

"""
tensor([[1, 2, 3],
        [2, 0, 4],
        [3, 4, 5]])
"""

3.4 张量

张量(本小节中的“张量”指代数对象)是描述具有任意数量轴的 维数组的通用方法。
张量用特殊字体的大写字母表示(例如, 、 和 )。

代码实现三阶张量

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

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

3.5 张量算法的基本性质

标量、向量、矩阵和任意数量轴的张量(本小节中的“张量”指代数对象)有一些实用的属性。 例如,从按元素操作的定义中可以注意到,任何按元素的一元运算都不会改变其操作数的形状。 同样,[给定具有相同形状的任意两个张量,任何按元素二元运算的结果都将是相同形状的张量]。 例如,将两个相同形状的矩阵相加,会在这两个矩阵上执行元素加法。
A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
B = A.clone()  # 通过分配新内存,将A的一个副本分配给B
A, A + B

"""
(tensor([[ 0.,  1.,  2.,  3.],
         [ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.],
         [12., 13., 14., 15.],
         [16., 17., 18., 19.]]),
 tensor([[ 0.,  2.,  4.,  6.],
         [ 8., 10., 12., 14.],
         [16., 18., 20., 22.],
         [24., 26., 28., 30.],
         [32., 34., 36., 38.]]))
"""
两个矩阵的按元素乘法称为Hadamard积(Hadamard product)(数学符号 ⊙ )
A * B

"""
tensor([[  0.,   1.,   4.,   9.],
        [ 16.,  25.,  36.,  49.],
        [ 64.,  81., 100., 121.],
        [144., 169., 196., 225.],
        [256., 289., 324., 361.]])
"""
将张量乘以或加上一个标量不会改变张量的形状,其中张量的每个元素都将与标量相加或相乘。
a = 2
X = torch.arange(24).reshape(2, 3, 4)
a + X, (a * X).shape

"""
(tensor([[[ 2,  3,  4,  5],
          [ 6,  7,  8,  9],
          [10, 11, 12, 13]],
 
         [[14, 15, 16, 17],
          [18, 19, 20, 21],
          [22, 23, 24, 25]]]),
 torch.Size([2, 3, 4]))
"""

3.6 降维

计算任意张量元素的和

x = torch.arange(4, dtype=torch.float32)
x, x.sum()

"""
(tensor([0., 1., 2., 3.]), tensor(6.))
"""

A.shape, A.sum()
"""
(torch.Size([5, 4]), tensor(190.))
"""
默认情况下,调用求和函数会沿所有的轴降低张量的维度,使它变为一个标量
我们还可以[ 指定张量沿哪一个轴来通过求和降低维度]

以矩阵为例,为了通过求和所有行的元素来降维(轴0),可以在调用函数时指定axis=0。 由于输入矩阵沿0轴降维以生成输出向量,因此输入轴0的维数在输出形状中消失。

A_sum_axis0 = A.sum(axis=0)
A_sum_axis0, A_sum_axis0.shape

"""
(tensor([40., 45., 50., 55.]), torch.Size([4]))
"""

指定axis=1将通过汇总所有列的元素降维(轴1)。因此,输入轴1的维数在输出形状中消失。

A_sum_axis1 = A.sum(axis=1)
A_sum_axis1, A_sum_axis1.shape

"""
(tensor([ 6., 22., 38., 54., 70.]), torch.Size([5]))
"""

沿着行和列对矩阵求和,等价于对矩阵的所有元素进行求和。

A.sum(axis=[0, 1])  # 结果和A.sum()相同

"""
tensor(190.)
"""

[一个与求和相关的量是平均值(mean或average)]。 我们通过将总和除以元素总数来计算平均值。 在代码中,我们可以调用函数来计算任意形状张量的平均值。

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

"""
(tensor(9.5000), tensor(9.5000))
"""

同样,计算平均值的函数也可以沿指定轴降低张量的维度。

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

"""
(tensor([ 8.,  9., 10., 11.]), tensor([ 8.,  9., 10., 11.]))
"""

3.7 非降维求和

计算总和或均值时保持轴数不变
torch.sum(input, list: dim, bool: keepdim=False, dtype=None) → Tensor
input:输入一个tensor
dim:要求和的维度,可以是一个列表
keepdim:求和之后这个dim的元素个数为1,所以要被去掉,如果要保留这个维度,则应当keepdim=True
sum_A = A.sum(axis=1, keepdims=True)
sum_A

"""
tensor([[ 6.],
        [22.],
        [38.],
        [54.],
        [70.]])
"""

例如,由于sum_A在对每行进行求和后仍保持两个轴,我们可以(通过广播将A除以sum_A)。

A / sum_A

"""
tensor([[0.0000, 0.1667, 0.3333, 0.5000],
        [0.1818, 0.2273, 0.2727, 0.3182],
        [0.2105, 0.2368, 0.2632, 0.2895],
        [0.2222, 0.2407, 0.2593, 0.2778],
        [0.2286, 0.2429, 0.2571, 0.2714]])
"""

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

A.cumsum(axis=0)

"""
tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  6.,  8., 10.],
        [12., 15., 18., 21.],
        [24., 28., 32., 36.],
        [40., 45., 50., 55.]])
"""

3.8 点积(Dot Product)

给定两个向量 , 它们的点积(dot product) (或 ⟨,⟩ ) 是相同位置的按元素乘积的和:

y = torch.ones(4, dtype = torch.float32)
x, y, torch.dot(x, y)

"""
(tensor([0., 1., 2., 3.]), tensor([1., 1., 1., 1.]), tensor(6.))
"""

我们可以通过执行按元素乘法,然后进行求和来表示两个向量的点积

torch.sum(x * y)

"""
tensor(6.))
"""

3.9 矩阵-向量积

在代码中使用张量表示矩阵-向量积,我们使用mv函数。 当我们为矩阵A和向量x调用torch.mv(A, x)时,会执行矩阵-向量积。 注意,A的列维数(沿轴1的长度)必须与x的维数(其长度)相同。

A, x, A.shape, x.shape, torch.mv(A, x)

"""
(tensor([[ 0.,  1.,  2.,  3.],
         [ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.],
         [12., 13., 14., 15.],
         [16., 17., 18., 19.]]),
 tensor([0., 1., 2., 3.]),
 torch.Size([5, 4]),
 torch.Size([4]),
 tensor([ 14.,  38.,  62.,  86., 110.]))
"""

3.10 矩阵-矩阵乘法

我们可以将矩阵-矩阵乘法 看作简单地执行 次矩阵-向量积,并将结果拼接在一起,形成一个 × 矩阵

在下面的代码中,我们在A和B上执行矩阵乘法。 这里的A是一个5行4列的矩阵,B是一个4行3列的矩阵。 两者相乘后,我们得到了一个5行3列的矩阵。

B = torch.ones(4, 3)
A, B, torch.mm(A, B)

"""
(tensor([[ 0.,  1.,  2.,  3.],
         [ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.],
         [12., 13., 14., 15.],
         [16., 17., 18., 19.]]),
 tensor([[1., 1., 1.],
         [1., 1., 1.],
         [1., 1., 1.],
         [1., 1., 1.]]),
 tensor([[ 6.,  6.,  6.],
         [22., 22., 22.],
         [38., 38., 38.],
         [54., 54., 54.],
         [70., 70., 70.]]))
"""

3.11 范数

范数是向量元素平方和的平方根
范数中常常省略下标 2,也就是说 等同于

在代码中,我们可以按如下方式计算向量的 范数。

u = torch.tensor([3.0, -4.0])
torch.norm(u)

"""
tensor(5.)
"""
深度学习中更经常地使用 范数的平方,也会经常遇到 范数,它 表示为向量元素的绝对值之和:

范数相比, 范数受异常值的影响较小。 为了计算 范数,我们将绝对值函数和按元素求和组合起来。
torch.abs(u).sum()

"""
tensor(7.)
"""
范数和 范数都是更一般的 范数的特例:

类似于向量的 范数, [矩阵] (的Frobenius范数(Frobenius norm)是矩阵元素平方和的平方根:)

Frobenius范数满足向量范数的所有性质,它就像是矩阵形向量的 范数。 调用以下函数将计算矩阵的Frobenius范数。
torch.norm(torch.ones((4, 9)))

"""
tensor(6.)
"""

4.微积分

详细请参考教学文档 https://zh.d2l.ai/chapter_preliminaries/calculus.html

4.1 导数和微分

如果 的导数存在,这个极限被定义为

如果 ′() 存在,则称 在 处是可微(differentiable)的。 如果 在一个区间内的每个数上都是可微的,则此函数在此区间中是可微的

为了更好地解释导数,让我们做一个实验。 (定义 )如下:

%matplotlib inline
import numpy as np
from matplotlib_inline import backend_inline
from d2l import torch as d2l


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

通过令 =1 并让 ℎ 接近 0

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

"""
h=0.10000, numerical limit=2.30000
h=0.01000, numerical limit=2.03000
h=0.00100, numerical limit=2.00300
h=0.00010, numerical limit=2.00030
h=0.00001, numerical limit=2.00003
"""

4.2 偏导数

是一个具有 个变量的函数,偏导数可以表示为

4.3 梯度

函数()相对于的梯度是一个包含个偏导数的向量:
,
其中 通常在没有歧义时被 取代。

4.4 链式法则

假设可微分函数 有变量 ,其中每个可微分函数 都有变量 。 注意, 是 的函数。 对于任意 =1,2,…, ,链式法则给出:

你可能感兴趣的:(动手学深度学习,深度学习,人工智能,算法)