Torch学习
一 、数据操作
1.基础操作
为了能够完成各种数据操作,我们需要某种方法来存储和操作数据。 通常,我们需要做两件重要的事:(1)获取数据;(2)将数据读入计算机后对其进行处理。 如果没有某种方法来存储数据,那么获取数据是没有意义的。
首先,我们导入torch
1 | import torch |
张量表示一个由数值组成的数组,这个数组可能有多个维度。 具有一个轴的张量对应数学上的向量(vector); 具有两个轴的张量对应数学上的矩阵(matrix); 具有两个轴以上的张量没有特殊的数学名称。
我们可以使用 arange
创建一个行向量 x
。这个行向量包含以0开始的前12个整数,它们默认创建为整数。也可指定创建类型为浮点数。张量中的每个值都称为张量的 元素(element)。例如,张量 x
中有 12 个元素。除非额外指定,新的张量将存储在内存中,并采用基于CPU的计算。
1 | x = torch.arange(12) |
可以通过shape属性访问张量
1 | x.shape |
要想改变一个张量的形状而不改变元素数量和元素值,可以调用reshape
函数。 例如,可以把张量x
从形状为(12,)的行向量转换为形状为(3,4)的矩阵。 这个新的张量包含与转换前相同的值,但是它被看成一个3行4列的矩阵。 要重点说明一下,虽然张量的形状发生了改变,但其元素值并没有变。 注意,通过改变张量的形状,张量的大小不会改变。
1 | X = x.reshape(3, 4) |
2.广播
在上面的部分中,我们看到了如何在相同形状的两个张量上执行按元素操作。 在某些情况下,即使形状不同,我们仍然可以通过调用 广播机制(broadcasting mechanism)来执行按元素操作。 这种机制的工作方式如下:
通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
对生成的数组执行按元素操作。
1
2
3
4
5
6
7a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a, b
a+b
tensor([[0, 1],
[1, 2],
[2, 3]])二、数据预处理
为了能用深度学习来解决现实世界的问题,我们经常从预处理原始数据开始, 而不是从那些准备好的张量格式数据开始。 在Python中常用的数据分析工具中,我们通常使用
pandas
软件包。1.读取数据集
1
2
3
4
5
6
7
8
9
10import 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
函数。该数据集有四行三列。其中每行描述了房间数量(“NumRooms”)、巷子类型(“Alley”)和房屋价格(“Price”)。
1 | import pandas as pd |
2.处理缺失值
Na N ”项代表缺失值。 为了处理缺失的数据,典型的方法包括插值法和删除法, 其中插值法用一个替代值弥补缺失值,而删除法则直接忽略缺失值。
通过位置索引iloc
,我们将data
分成inputs
和outputs
, 其中前者为data
的前两列,而后者为data
的最后一列。 对于inputs
中缺少的数值,我们用同一列的均值替换“Na N”项。
1 | inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2] |
对于inputs
中的类别值或离散值,我们将“NaN”视为一个类别。 由于“巷子类型”(“Alley”)列只接受两种类型的类别值“Pave”和“NaN”, pandas
可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。 巷子类型为“Pave”的行会将“Alley_Pave”的值设置为1,“Alley_nan”的值设置为0。 缺少巷子类型的行会将“Alley_Pave”和“Alley_nan”分别设置为0和1。
1 | NumRooms Alley_Pave Alley_nan |
1.3转换为张量格式
现在inputs
和outputs
中的所有条目都是数值类型,它们可以转换为张量格式。 当数据采用张量格式后,可以通过引入的那些张量函数来进一步操作。
三、线性代数
从按元素操作的定义中可以注意到,任何按元素的一元运算都不会改变其操作数的形状。 同样,给定具有相同形状的任意两个张量,任何按元素二元运算的结果都将是相同形状的张量。 例如,将两个相同形状的矩阵相加,会在这两个矩阵上执行元素加法。
矩阵转置
1
A.T
求和原理:对哪个维度求和,哪个维度就会消失
1
2
3A.sum(axis=0) #对于第一个轴求和
#如果想保持轴数不变
A.sum(axis=0, keepdims=True) #用于广播机制,要求必须维度相同按轴计算元素的累积总和:维度不会消失
1
A.cumsum(axis=0)
L 2范数:向量元素的平方和的平方根 ||F范数与之类似(矩阵元素平方和的平方根)
1
torch.noem(x) #||x||2
L 1范数:每个元素的绝对值求和
1
torch.abs(x).sum() #||x||1
矩阵乘法
1 | torch.dot() # 向量·向量 |
四、微积分:
微积分,导数和微分,偏导数,梯度,链式法则
小结:
1.微分和积分是微积分的两个分支,前者可以应用于深度学习中的优化问题。
2.导数可以被解释为函数相对于其变量的瞬时变化率,它也是函数曲线的切线的斜率。
3.梯度是一个向量,其分量是多变量函数相对于其所有变量的偏导数。
4.链式法则可以用来微分复合函数。
五、自动微分
求导是几乎所有深度学习优化算法的关键步骤。 虽然求导的计算很简单,只需要一些基本的微积分。 但对于复杂的模型,手工进行更新是一件很痛苦的事情(而且经常容易出错)。
深度学习框架通过自动计算导数,即自动微分(automatic differentiation)来加快求导。 实际中,根据设计好的模型,系统会构建一个计算图(computational graph), 来跟踪计算是哪些数据通过哪些操作组合起来产生输出。 自动微分使系统能够随后反向传播梯度。 这里,反向传播(backpropagate)意味着跟踪整个计算图,填充关于每个参数的偏导数。
实际中,根据设计好的模型,系统会构建一个计算图(computational graph), 来跟踪计算是哪些数据通过哪些操作组合起来产生输出。 自动微分使系统能够随后反向传播梯度
计算图
优点:局部计算,从而简化问题;将中间的计算结果全部保存起来;可以通过反向传播高效计算导数
链式法则
原理:如果某个函数由复合函数表示,则该复合函数的导数可以用构成复合函数的各个函数的导数的乘积表示
反向传播
加法节点
加法节点存在于某个最后输出的计算的一部分中。反向传播时,从最右边的输出出发,局部导数从节点向节点反方向传播
乘法节点
乘法的反向传播会将上游的值乘以正向传播时的输入信号的“翻转值”后传递给下游
实现乘法节点的反向传播时,要保存正向传播的输入信号