第一次作业:深度学习基础
一、数据操作
- 基本操作
张量(Tensor):实际为n维数组,具有一个轴的对应数学上的向量,具有两个轴的对应数学上的矩阵,三个及以上的无特殊称谓。
- arrange可创建一个行向量,即一个轴的张量,其中每个值称为张量的元素(element)
- shape可以访问张量的形状(沿每个轴的长度)
- numel可以知道张量中所有元素的总数
- reshape可以改变张量的形状,比如将向量改变为矩阵,只需规定一个及以上的轴的长度,其他轴的长度可以用-1代替
- 可以使用全0、全1、其他常量或者从特定分布中随机采样的数字来初始化矩阵
- 可以通过提供包含数值的Python列表(或嵌套列表)来为所需张量中的每个元素赋予确定值。最外层的列表对应于轴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]])
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.]]))
cat操作可连结两个张量,dim=0是指在矩阵的行上操作,如上所示,是把X和Y两个张量在行上面“堆”起来了,dim=1是指在矩阵的列上面操作,如上所示,是将原来张量的列拓宽了。
3.可以通过逻辑运算符创建二元张量,比如用==、>和<
4.sum对张量中的所有元素进行求和会产生一个只有一个元素的张量。
- 广播机制
当在形状不同的张量上进行计算时,需要使用广播机制,首先,通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状。其次,对生成的数组执行按元素操作。
a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a, b
- 索引和切片
- 与Python中对于索引的使用一样,有正索引和负索引(负值)两种索引,使用方括号选定区间访问元素时是左闭右开的区间
- 可以通过指定索引来将元素写入矩阵
- 如果我们想为多个元素赋值相同的值,我们只需要索引所有元素,然后为它们赋值。 例如,
[0:2, :]
访问第1行和第2行,其中“:”代表沿轴1(列)的所有元素。虽然我们讨论的是矩阵的索引,但这也适用于向量和超过2个维度的张量
X[0:2, :] = 12
X
- 节省内存
可使用切片操作进行原地更新操作,使得更新后的指向不变(更新后的结果的ID与原来的相同),切片的结果是原列表片段或者全部的拷贝(与原来ID不同)
Z = torch.zeros_like(Y)
print('id(Z):', id(Z))
Z[:] = X + Y
print('id(Z):', id(Z))
id(Z): 140272150341696 id(Z): 140272150341696
- 转换为其他Python对象
A = X.numpy()
B = torch.tensor(A)
type(A), type(B)
a = torch.tensor([3.5])
a, a.item(), float(a), int(a)
二、数据预处理
- 读取数据集
首先创建一个人工数据集,并存储在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')
要从创建的CSV文件中加载原始数据集,我们导入pandas
包并调用read_csv
函数
# 如果没有安装pandas,只需取消对以下行的注释:
# !pip install pandas
import pandas as pd
data = pd.read_csv(data_file)
print(data)
2. 处理缺失值
例子中,“NaN”项代表缺失值。为了处理缺失的数据,典型的方法包括插值和删除,其中插值用替代值代替缺失值。而删除则忽略缺失值。
通过位置索引iloc
,我们将data
分成inputs
和outputs
,其中前者为data
的前两列,而后者为data
的最后一列。对于inputs
中缺少的数值,我们用同一列的均值替换“NaN”项
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = inputs.fillna(inputs.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
3. 转换为张量格式
import torch
X, y = torch.tensor(inputs.values), torch.tensor(outputs.values)
X, y
(tensor([[3., 1., 0.], [2., 0., 1.], [4., 0., 1.], [3., 0., 1.]], dtype=torch.float64), tensor([127500, 106000, 178100, 140000]))
三、线性代数
1、标量:仅包含一个数值的叫标量(scalar)
2、向量:可以将向量视为标量值组成的列表(列向量为向量的默认方向)。我们将这些标量值称为向量的元素(element)或分量(component)。我们通过一维张量处理向量。一般来说,张量可以具有任意长度,取决于机器的内存限制。
在代码中,我们通过张量的索引来访问任一元素。
x[3]
向量的长度和维度相等,指包含标量的个数,当用张量表示一个向量(只有一个轴)时,我们也可以通过.shape
属性访问向量的长度。形状(shape)是一个元组,列出了张量沿每个轴的长度(维数)。对于只有一个轴的张量,形状只有一个元素。当张量的维度用来表示张量具有的轴数。在这个意义上,张量的某个轴的维数就是这个轴的长度。
3、矩阵:用.reshape讲一元张量转换为二元张量(矩阵)
A = torch.arange(20).reshape(5, 4)
A
矩阵的转置
A.T
对称矩阵可手动定义,再将其与转置矩阵进行张量的逻辑运算,判断是否对称。
4、张量
当我们开始处理图像时,张量将变得更加重要,图像以nn维数组形式出现,其中3个轴对应于高度、宽度,以及一个通道(channel)轴,用于堆叠颜色通道(红色、绿色和蓝色)。其他张量的操作前面已经学习过。
5、张量算法的基本性质
两个矩阵的按元素乘法称为哈达玛积(Hadamard product)(数学符号⊙)区别于矩阵乘法
将张量乘以或加上一个标量不会改变张量的形状(方向不变,只是改变了大小),其中张量的每个元素都将与标量相加或相乘。
6、降维
默认情况下,调用求和函数会沿所有的轴降低张量的维度,使它变为一个标量。 我们还可以指定张量沿哪一个轴来通过求和降低维度。以矩阵为例,为了通过求和所有行的元素来降维(轴0),我们可以在调用函数时指定axis=0
。 由于输入矩阵沿0轴降维以生成输出向量,因此输入的轴0的维数在输出形状中丢失。
A_sum_axis0 = A.sum(axis=0)
A_sum_axis0, A_sum_axis0.shape
(tensor([ 6., 22., 38., 54., 70.]), torch.Size([5]))
7、矩阵-向量积
A.shape, x.shape, torch.mv(A, x)
8、矩阵-矩阵乘法
B = torch.ones(4, 3)
torch.mm(A, B)
9、范数:L1范数为向量元素的平方和的平方根,L2范数为向量元素的绝对值之和,与L2范数相比,L1范数受异常值的影响较小
u = torch.tensor([3.0, -4.0])
torch.norm(u)
矩阵的弗罗贝尼乌斯范数(Frobenius norm)是矩阵元素平方和的平方根
torch.norm(torch.ones((4, 9)))
在深度学习中,我们经常试图解决优化问题: 最大化分配给观测数据的概率; 最小化预测和真实观测之间的距离。 用向量表示物品(如单词、产品或新闻文章),以便最小化相似项目之间的距离,最大化不同项目之间的距离。 通常,目标,或许是深度学习算法最重要的组成部分(除了数据),被表达为范数。
四、微分
%matplotlib inline
import numpy as np
from IPython import display
from d2l import torch as d2l
def f(x):
return 3 * x ** 2 - 4 * x
绘制函数u=f(x)及其在x=1处的切线y=2x−3,其中系数2是切线的斜率
x = np.arange(0, 3, 0.1)
plot(x, [f(x), 2 * x - 3], 'x', 'f(x)', legend=['f(x)', 'Tangent line (x=1)'])
偏导数,梯度,链式法则
五、自动求导
深度学习框架通过自动计算导数,即自动求导(automatic differentiation),来加快这项工作。实际中,根据我们设计的模型,系统会构建一个计算图(computational graph),来跟踪计算是哪些数据通过哪些操作组合起来产生输出。自动求导使系统能够随后反向传播梯度。 这里,反向传播(backpropagate)只是意味着跟踪整个计算图,填充关于每个参数的偏导数。
标量链式法则拓展到向量链式法则
https://my.oschina.net/amyhome/blog/691317向量求导
import torch
x = torch.arange(4.0)
x
tensor([0., 1., 2., 3.])
x.requires_grad_(True) # 等价于 `x = torch.arange(4.0, requires_grad=True)`
x.grad # 默认值是None
y = 2 * torch.dot(x, x)
y
tensor(28., grad_fn=<MulBackward0>)
y.backward()
x.grad
tensor([ 0., 4., 8., 12.])
x.grad == 4 * x
tensor([True, True, True, True])
https://zhuanlan.zhihu.com/p/109755675