目录
1.1 基础知识
1.1.1 入门
1.1.2 运算
1.1.3 广播机制
1.1.4 索引和切片
1.1.5 转换为其他python对象
1.2 数据预处理
1.3 线性代数
1.4 微分
1.5 自动求导
1.6 概率
1.1.1 入门
import torch
# 使用arange创建一个行向量x
x = torch.arange(12)
# 访问张量的形状(沿每个轴的长度)
x.shape
# 张量中元素的总数number of element
x.numel()
# 想要改变张量的形状而不改变元素数量和元素值
# 其中,可以设置其中维度为-1,来自动推断维度
X = x.reshape(3,4)
X = x.reshape(-1,4)
# 使用全0数字初始化矩阵
torch.zeros((2,3,4))
# 使用全1数字初始化矩阵
torch.ones((2,3,4))
# 每个元素均值为0,标准差为1,形状(3,4)的张量
torch.randn(3,4)
# 通过列表初始化
torch.tensor([1,2,3,4])
1.1.2 运算
#对于任意具有相同形状的张量,我们可以进行+-*/和**的元素基本运算
x + y
x - y
x * y
x / y
x ** y
# 按元素的求幂运算(e为底)(得到和x一样大小的e为底,x元素为指数的张量)
torch.exp(x)
# 多个张量的连结
X = torch.arange(12,dtype=torch.float32).reshape((3,4))
Y = torch.tensor([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
# 行连结
torch.cat((X,Y),dim=0)
# 列连结
torch.cat((X,Y),dim=1)
# 当进行逻辑运算时,得到的也是逻辑结果的张量
X == Y
X > 12
# 对张量的所有元素求和会得到只有一个元素的张量
X.sum()
1.1.3 广播机制
#对于任意具有相同形状的张量,我们可以进行+-*/和**的元素基本运算 x + y x - y x * y x / y x ** y # 按元素的求幂运算(e为底)(得到和x一样大小的e为底,x元素为指数的张量) torch.exp(x) # 多个张量的连结 X = torch.arange(12,dtype=torch.float32).reshape((3,4)) Y = torch.tensor([[1,2,3,4],[5,6,7,8],[9,10,11,12]]) # 行连结 torch.cat((X,Y),dim=0) # 列连结 torch.cat((X,Y),dim=1) # 当进行逻辑运算时,得到的也是逻辑结果的张量 X == Y X > 12 # 对张量的所有元素求和会得到只有一个元素的张量 X.sum()
1.1.3 广播机制
在某些情况下,即使形状不同,我们可以调用广播机制执行元素操作。
首先,通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状。
其次,对生成的数组按元素操作。
可广播的张量需满足以下规则:
1.每个张量至少一个维度
2.从尾部开始,维度尺寸要么相等,要么其中一个张量维度尺寸为1,要么其中一个张量不存在这个维度
1.1.4 索引和切片
# X的最后一个元素
X[-1]
# 第二个和第三个元素
X[1:3]
# 通过索引将元素写入矩阵
X[1,2] = 9
X[0:2,:] = 12
1.1.5 转换为其他python对象
A = X.numpy()
B = torch.tensor(A)
# 要将大小为1的张量转换为python的标量,可以调用item函数或是python的内置函数
a = torch.tensor([5])
a.item()
float(a)
int(a)
1.2 数据预处理
# 操作系统接口模块
import os
# os.path.join即简单的路径拼接
# exist_ok如果是False,那么如果目录已经存在则会Error
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,Pricen') # 列名
f.write('NA,Pave,127500n') # 每行表示一个数据样本
f.write('2,NA,106000n')
f.write('4,NA,178100n')
f.write('NA,NA,140000n')
# 导入pandas包调用read_csv函数,数据集四行三列,每行描述了房间数量(“NumRooms”)、巷子类型
# (“Alley”)和房屋价格(“Price”)。
import pandas as pd
data = pd.read_csv(data_file)
print(data)
# 处理缺失值
# loc和iloc都是对pandas Dataframe进行切片索引
# loc是通过字符型标签获得数据
# iloc和list一样通过索引获得数据
inputs,outputs = data.iloc[:,0:2],data.iloc[:,2]
inputs = inputs.fillna(inputs.mean())
print(inputs)
# 对于inputs中的类别值,pandas可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。巷子类型
# 为Pave会将“Alley_Pave”的值设为1,“Alley_nan”值设为0。get_dummies是将拥有不同值的变量转换
# 为0/1数值。
# 如果dummy_na = False表示忽略空缺值
inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)
#转换为张量格式
import torch
X,y = torch.tensor(inputs.values),torch.tensor(outputs.values)
1.3 线性代数
A = X.numpy() B = torch.tensor(A) # 要将大小为1的张量转换为python的标量,可以调用item函数或是python的内置函数 a = torch.tensor([5]) a.item() float(a) int(a)
1.2 数据预处理
# 操作系统接口模块
import os
# os.path.join即简单的路径拼接
# exist_ok如果是False,那么如果目录已经存在则会Error
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,Pricen') # 列名
f.write('NA,Pave,127500n') # 每行表示一个数据样本
f.write('2,NA,106000n')
f.write('4,NA,178100n')
f.write('NA,NA,140000n')
# 导入pandas包调用read_csv函数,数据集四行三列,每行描述了房间数量(“NumRooms”)、巷子类型
# (“Alley”)和房屋价格(“Price”)。
import pandas as pd
data = pd.read_csv(data_file)
print(data)
# 处理缺失值
# loc和iloc都是对pandas Dataframe进行切片索引
# loc是通过字符型标签获得数据
# iloc和list一样通过索引获得数据
inputs,outputs = data.iloc[:,0:2],data.iloc[:,2]
inputs = inputs.fillna(inputs.mean())
print(inputs)
# 对于inputs中的类别值,pandas可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。巷子类型
# 为Pave会将“Alley_Pave”的值设为1,“Alley_nan”值设为0。get_dummies是将拥有不同值的变量转换
# 为0/1数值。
# 如果dummy_na = False表示忽略空缺值
inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)
#转换为张量格式
import torch
X,y = torch.tensor(inputs.values),torch.tensor(outputs.values)
1.3 线性代数
标量:
import torch x = torch.tensor([3.0]) y = torch.tensor([2.0]) x + y x * y x / y x ** y
向量:
x = torch.arange(4) # tensor([0,1,2,3]) x[3] # tensor(3) # 长度,维度和形状 len(x) # 4 x.shape # torch.Size([4])
矩阵:
# 创建矩阵 A = torch.arange(20).reshape(5,4) # 矩阵转置 A.T
张量:
# 创建张量 X = torch.arange(24).reshape(2,3,4) A = torch.arange(20,dtype=torch.float32).reshape(5,4) # 克隆张量A B = A.clone() # 元素按位运算 A * B a = 2 a + X # 降维 x = torch.arange(4,dtype=torch.float32) x.sum() # 默认情况下调用求和函数降低张量维度 # 可以指定某个轴的和 A_sum_axis0 = A.sum(axis=0) A_sum_axis1 = A.sum(axis=1) # 求平均值 A.mean() A.sum()/A.numel() # 非降维求和 sum_A = A.sum(axis=1,keepdims=True) # 计算沿某一轴的累积总和 A.cumsum(axis=0) # 行 # 点积 y = torch.ones(4,dtype = torch.float32) torch.dot(x,y) # 其实就是按元素乘法进而求和来得到向量的卷积 torch.sum(x*y) # 矩阵-向量积 # 代码中使用张量表示矩阵-向量积,我们使用与点积相同的dot函数,当我们为矩阵A和向量x调用 np.dot(A,x)时,即执行矩阵-向量积 torch.mv(A,x) # 矩阵-矩阵积 torch.mm(A,B) # 计算向量的范数 # 计算L2范数(根号下平方和) u = torch.tensor([3.0,-4.0]) torch.norm(u) # tensor(5) # 计算L1范数(绝对值求和) torch.abs(u).sum()
1.4 微分
# 使用svg格式在Jupyter中显示绘图
def use_svg_display():
# 设置显示格式为svg
display.set_matplotlib_formats('svg')
# 设置matplotlib图表大小
def set_figsize(figsize=(3.5,2.5)):
use_svg_display()
d2l.plt.rcParams['figure.figsize'] = figsize
# 设置由matplotlib生成图表的轴的属性
def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend):
axes.set_xlabel(xlabel)
axes.set_ylabel(ylabel)
axes.set_xscale(xscale)
axes.set_yscale(yscale)
axes.set_xlim(xlim)
axes.set_ylim(ylim)
if legend:
axes.legend(legend)
axes.grid()
# 绘制曲线
def plot(X, Y=None,xlabel=None,ylabel=None,legend=None,xlim=None,ylim=None,
xscale='linear',yscale='linear',
fmts=('-','m--','g-.','r:'),figsize=(3.5,2.5),axes=None):
if legend is None:
legend = []
set_figsize(figsize)
axes = axes if axes else d2l.plt.gca()
#如何X有一个轴输出True
def has_one_axis(X):
return (hasattr(X,"ndim") and X.ndim == 1 or isinstance(X,list) and not
hasattr(X[0],"__len__"))
if has_one_axis(X):
X = [X]
if Y is None:
X,Y = [[]]*len(X),X
elif has_one_axis(Y):
Y = [Y]
if len(X) != len(Y):
X = X * len(Y)
axes.cla()
for x,y,fmt in zip(X,Y,fmts):
if len(x):
axes.plot(x,y,fmt)
else:
axes.plot(y,fmt)
set_axes(axes,xlabel,ylabel,xlim,ylim,xscale,yscale,legend)
绘制函数u = f(x)及其在x = 1处的切线y = 2x - 3
x = np.arange(0,3,0.1) plot(x, [f(x), 2*x-3], 'x', 'f(x)', legend=['f(x)','Tangent line (x=1)'])
1.5 自动求导
# 对y=2xTx关于列向量x求导
import torch
x = torch.arange(4.0)
# 在计算y关于x的梯度之前,需要一个地方存储梯度,重要的是,我们不会每次对参数求导都分配新的
内存,因此会成千上万次更新相同的参数,每次分配新的内存会很快将内存耗尽
x.requires_grad_(True) #等价于 x = torch.arange(4.0,requires_grad=True)
x.grad # 默认值为None
# 计算y
y = 2 * torch.dot(x,x)
# 调用反向传播自动计算y关于x每个分量的梯度
y.backward()
# 打印梯度
x.grad
# 这时候如果计算x的另一个函数
# 默认情况下,pytorch会累积梯度,我们需要清楚之前的值
x.grad.zero_()
y = x.sum()
y.backward()
x.grad # tensor([1,1,1,1])
如果y不是标量,那么向量y关于向量x的导数最自然解释是一个矩阵。而对于高阶和高维y和x,求导的结果可以是一个高阶张量。有待深入研究
# 对于非标量调用backward需要传入一个gradient参数,该参数指定微分函数关于self的梯度。在例子中 只是想求偏导数的和,所以传递1的梯度合适的 x.grad.zero_() y = x * x # 等价于y.backward(torch.ones(len(x))) y.sum().backward() x.grad
分离计算:想计算z关于x的梯度,希望视y为常数,并只考虑x在y被计算后的作用。
x.grad.zero_() y = x * x u = y.detach() z = x * u z.sum.backward() x.grad == u #[True,True,True,True] x.grad.zero_() y.sum().backward() x.grad == 2 * x #[True,True,True,True]
python控制流的梯度计算:
即使构建函数的计算图需要通过python控制流,我们仍然可以计算得到变量的梯度。
def f(a):
b = a * 2
while b.norm() < 1000:
b = b * 2
if b.sum() > 0:
c = b
else:
c = 100 * b
return c
# 计算梯度
a = torch.randn(size=(),requires_grad=True)
d = f(a)
d.backward()
a.grad == d/a # True
对于任何a,存在常量k使得f(a) = k*a,其中k的值取决于输入a,因此d/a可以验证梯度是否正确。



