Deep Learning学习day1-数据操作

henry Lv4

2.1 数据操作

这一部分是直接从 jupyter 里面导出来的,可能观感不太好,可以直接在这里下文件

入门

使用 arange 创建一个行向量 x,这个行向量包含以0开始的前12个整数,它们默认创建为整数,也可指定创建类型为浮点数,张量中的每个值都称为张量的元素(element)。基础操作如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import torch               
x = torch.arange(12)
# tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])

x.shape
# torch.Size([12])

#改变一个张量的形状而不改变元素数量和元素值,可以调用reshape函数
x.reshape(3, 4)
#tensor([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11]])


torch.zeros((2, 3, 4)) # 创建一个指定形状的全0张量
# 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张量
# tensor([[[1., 1., 1., 1.],
# [1., 1., 1., 1.],
# [1., 1., 1., 1.]],

# [[1., 1., 1., 1.],
# [1., 1., 1., 1.],
# [1., 1., 1., 1.]]])

torch.randn(3, 4) # 创建一个形状为(3,4)的张量。 其中的每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样
# tensor([[-0.0801, 0.5529, -0.1306, -0.3035],
# [-0.2284, 1.1712, 0.2000, -1.9476],
# [ 0.4408, -0.3938, -2.2961, -0.4860]])

torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]]) # 为张量赋予确定值
# tensor([[2, 1, 4, 3],
# [1, 2, 3, 4],
# [4, 3, 2, 1]])
1
2
3
tensor([[2, 1, 4, 3],
[1, 2, 3, 4],
[4, 3, 2, 1]])

运算符

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import torch
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 # **运算符是求幂运算
# (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.]))

torch.exp(x)
# tensor([2.7183e+00, 7.3891e+00, 5.4598e+01, 2.9810e+03])


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)
# (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.]]))
1
2
3
4
5
6
7
8
9
(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.]]))

广播机制

1
2
3
4
5
6
7
8
9
10
11
12
a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a, b
# (tensor([[0],
# [1],
# [2]]),
# tensor([[0, 1]]))

a+b
# tensor([[0, 1],
# [1, 2],
# [2, 3]])
1
2
3
tensor([[0, 1],
[1, 2],
[2, 3]])

索引&切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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]])
X[-1], X[1:3]

# (tensor([ 8., 9., 10., 11.]),
# tensor([[ 4., 5., 6., 7.],
# [ 8., 9., 10., 11.]]))

X[1, 2] = 9 # 更换单个元素
# tensor([[ 0., 1., 2., 3.],
# [ 4., 5., 9., 7.],
# [ 8., 9., 10., 11.]])

X[0:2, :] = 12 # 按行更新元素
# tensor([[12., 12., 12., 12.],
# [12., 12., 12., 12.],
# [ 8., 9., 10., 11.]])
1
2
3
4
5
tensor([[12., 12., 12., 12.],
[12., 12., 12., 12.],
[ 8., 9., 10., 11.]])


转换其它对象

1
2
3
4
5
6
7
8
9
import torch
A = X.numpy()
B = torch.tensor(A)
type(A), type(B)
# (numpy.ndarray, torch.Tensor)

a = torch.tensor([3.5])
a, a.item(), float(a), int(a)
# (tensor([3.5000]), 3.5, 3.5, 3)
1
2
3
(tensor([3.5000]), 3.5, 3.5, 3)


数据集预处理

读取数据集

创建一个人工数据集,并存储在CSV(逗号分隔值)文件 ../data/house_tiny.csv中。 以其他格式存储的数据也可以通过类似的方式进行处理

1
2
3
4
5
6
7
8
9
10
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')
1
2
3
4
import pandas as pd

data = pd.read_csv(data_file)
print(data)
1
2
3
4
5
6
   NumRooms Alley   Price
0 NaN Pave 127500
1 2.0 NaN 106000
2 4.0 NaN 178100
3 NaN NaN 140000

处理缺失值

“NaN”项代表缺失值。 为了处理缺失的数据,典型的方法包括插值法和删除法, 其中插值法用一个替代值弥补缺失值,而删除法则直接忽略缺失值

1
2
3
4
5
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
numeric_cols = inputs.select_dtypes(include=['number']).columns
# 这里使用均值来替代缺失的数据
inputs[numeric_cols] = inputs[numeric_cols].fillna(inputs[numeric_cols].mean())
print(inputs)
1
2
3
4
5
   NumRooms Alley
0 3.0 Pave
1 2.0 NaN
2 4.0 NaN
3 3.0 NaN
1
2
3
# dummy_na=True: 指定是否将缺失值(NaN)作为一个单独的类别进行编码。如果设置为 True,那么缺失值会被转换为一个额外的哑变量列,哑变量是指用 0 和 1 表示的二进制变量
inputs = pd.get_dummies(inputs, dummy_na = True)
print(inputs)
1
2
3
4
5
6
   NumRooms  Alley_Pave  Alley_nan
0 3.0 True False
1 2.0 False True
2 4.0 False True
3 3.0 False True

转换为张量格式

1
2
3
4
5
import torch

x = torch.tensor(inputs.to_numpy(dtype=float))
y = torch.tensor(outputs.to_numpy(dtype=float))
x, y
1
2
3
4
5
6
7
(tensor([[3., 1., 0.],
[2., 0., 1.],
[4., 0., 1.],
[3., 0., 1.]], dtype=torch.float64),
tensor([127500., 106000., 178100., 140000.], dtype=torch.float64))


练习

删除缺失值最多的列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np
data = pd.DataFrame({
'A': [1, 2, np.nan, 4],
'B': [np.nan, 2, np.nan, 4],
'C': [1, 2, 3, 4]
})
print("before delete")
print(data)

# 计算每一列缺失值数量
missing_counts = data.isnull().sum()

# 找到缺失值最多的列
col_to_drop = missing_counts.idxmax()

# 删除缺失值最多的列
data = data.drop(columns=col_to_drop)
print("after delete")
print(data)
1
2
3
4
5
6
7
8
9
10
11
12
13
before delete
A B C
0 1.0 NaN 1
1 2.0 2.0 2
2 NaN NaN 3
3 4.0 4.0 4
after delete
A C
0 1.0 1
1 2.0 2
2 NaN 3
3 4.0 4

线性代数

两个矩阵的按元素乘法称为Hadamard积(Hadamard product)。

1
2
3
4
5
import torch

A = torch.arange(20, dtype=torch.float32).reshape(5,4)
B = A.clone()
A, A*B # Hadamard积
1
2
3
4
5
6
7
8
9
10
(tensor([[ 0.,  1.,  2.,  3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.],
[16., 17., 18., 19.]]),
tensor([[ 0., 1., 4., 9.],
[ 16., 25., 36., 49.],
[ 64., 81., 100., 121.],
[144., 169., 196., 225.],
[256., 289., 324., 361.]]))

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

1
2
a = 2
A+a
1
2
3
4
5
6
7
tensor([[ 2.,  3.,  4.,  5.],
[ 6., 7., 8., 9.],
[10., 11., 12., 13.],
[14., 15., 16., 17.],
[18., 19., 20., 21.]])


张量降维

1
2
x = torch.arange(4, dtype = torch.float32)
x, x.sum()
1
2
(tensor([0., 1., 2., 3.]), tensor(6.))

1
2
A_sum_axis0 = A.sum(axis=0)   #按列相加
A_sum_axis0, A_sum_axis0.shape
1
(tensor([40., 45., 50., 55.]), torch.Size([4]))
1
2
A_sum_axis1 = A.sum(axis=1)  #按行相加
A_sum_axis1, A_sum_axis1.shape
1
2
(tensor([ 6., 22., 38., 54., 70.]), torch.Size([5]))

通过将总和除以元素总数来计算平均值。 在代码中,我们可以调用函数来计算任意形状张量的平均值。

1
A.sum() / A.numel()
1
tensor(9.5000)
1
A.mean(axis=0), A.sum(axis=0)/A.shape[0]
1
(tensor([ 8.,  9., 10., 11.]), tensor([ 8.,  9., 10., 11.]))

非降维求和

1
2
sum_A = A.sum(axis=1, keepdims=True)
sum_A
1
2
3
4
5
tensor([[ 6.],
[22.],
[38.],
[54.],
[70.]])

通过广播让 A 除以 sum_A

1
A/sum_A
1
2
3
4
5
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]])

求累加总和

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

点积

两个向量相同位置元素乘积之和

1
2
y = torch.ones(4, dtype = torch.float32)
x,y,torch.dot(x, y)
1
(tensor([0., 1., 2., 3.]), tensor([1., 1., 1., 1.]), tensor(6.))
1
2
# 矩阵向量积
A.shape, x.shape, torch.mv(A, x)
1
2
(torch.Size([5, 4]), torch.Size([4]), tensor([ 14.,  38.,  62.,  86., 110.]))

1
2
3
# 矩阵乘法积
B = torch.ones(4, 3)
torch.mm(A, B)
1
2
3
4
5
tensor([[ 6.,  6.,  6.],
[22., 22., 22.],
[38., 38., 38.],
[54., 54., 54.],
[70., 70., 70.]])

范数

L2 范数
对于一个向量来说,它的 L2 范数就是它的各元素平方和的平方根

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

L1 范数对于一个向量来说是各向量元素的绝对值之和

1
torch.abs(u).sum()
1
tensor(7.)

Frobenius范数(Frobenius norm)是矩阵元素平方和的平方根:

1
torch.norm(torch.ones((4, 9)))
1
tensor(6.)
1
A,A.sum(axis=0),A/A.sum(axis=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.],
[12., 13., 14., 15.],
[16., 17., 18., 19.]]),
tensor([40., 45., 50., 55.]),
tensor([[0.0000, 0.0222, 0.0400, 0.0545],
[0.1000, 0.1111, 0.1200, 0.1273],
[0.2000, 0.2000, 0.2000, 0.2000],
[0.3000, 0.2889, 0.2800, 0.2727],
[0.4000, 0.3778, 0.3600, 0.3455]]))

自动微分

1
2
3
import torch
x = torch.arange(4.0)
x
1
tensor([0., 1., 2., 3.])
1
2
3
# 下面代码需要一个地方来存储梯度,不会在每次对一个参数求导时都分配新的内存
x.requires_grad_(True) # 等价于x=torch.arange(4.0,requires_grad=True)
print(x.grad) # 默认值是None
1
2
None

1
2
3
4
y = 2 * torch.dot(x,x)
y
#x是一个长度为4的向量,计算x和x的点积,得到了我们赋值给y的标量输出。
#接下来,通过调用反向传播函数来自动计算y关于x每个分量的梯度,并打印这些梯度。
1
2
tensor(28., grad_fn=<MulBackward0>)

注意事项:前面这里 y 实际上是一个标量,标量关于向量的梯度是向量,并且与x拥有相同形状

1
2
y.backward()
x.grad
1
tensor([ 0.,  4.,  8., 12.])
1
x.grad == 4*x
1
tensor([True, True, True, True])
1
2
3
4
5
# 在默认情况下,PyTorch会累积梯度,我们需要清除之前的值
x.grad.zero_()
y = x.sum()
y.backward()
x.grad
1
2
tensor([1., 1., 1., 1.])

非标量变量的反向传播

当y不是标量时,向量y关于向量x的导数的最自然解释是一个矩阵。 对于高阶和高维的y和x,求导的结果可以是一个高阶张量。

1
2
3
4
5
6
7
# 对非标量调用backward需要传入一个gradient参数,该参数指定微分函数关于self的梯度。
# 本例只想求偏导数的和,所以传递一个1的梯度是合适的
x.grad.zero_()
y = x * x
# 等价于y.backward(torch.ones(len(x)))
y.sum().backward()
x.grad
1
2
3
tensor([0., 2., 4., 6.])


然而,这种情况会出现在高级机器学习中(包括深度学习中), 但当调用向量的反向计算时,通常会试图计算一批训练样本中每个组成部分的损失函数的导数

分离计算

假设y是作为x的函数计算的,而z则是作为y和x的函数计算的。 如果想计算z关于x的梯度,但由于某种原因,希望将y视为一个常数,并且只考虑到x在y被计算后发挥的作用,这里就需要用分离计算

1
2
3
4
5
6
7
x.grad.zero_()
y = x * x
u = y.detach() # u 与 y 有相同的值,但是 u 会被作为常数处理,不在看作是 x 的函数
z = u * x

z.sum().backward()
x.grad == u
1
2
tensor([True, True, True, True])

python 控制流梯度计算

1
2
3
4
5
6
7
8
9
10
11
12
def f(a):
b = a * 2
while b.norm() < 1000:
b = b * 2
print(b)
if b.sum() > 0:
c = b
print(c)
else:
c = 100 * b
print(c)
return c

然后计算梯度

1
2
3
4
5
a = torch.randn(size=(), requires_grad=True)
d = f(a)
d.backward()

a, d/a, a.grad
1
2
3
4
5
6
tensor(-1794.2429, grad_fn=<MulBackward0>)
tensor(-179424.2969, grad_fn=<MulBackward0>)

(tensor(-0.2190, requires_grad=True),
tensor(819200., grad_fn=<DivBackward0>),
tensor(819200.))
  • Title: Deep Learning学习day1-数据操作
  • Author: henry
  • Created at : 2024-06-16 21:11:19
  • Updated at : 2024-06-16 21:32:58
  • Link: https://henrymartin262.github.io/2024/06/16/data_process/
  • License: This work is licensed under CC BY-NC-SA 4.0.
 Comments