目录
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 概率
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])
#对于任意具有相同形状的张量,我们可以进行+-*/和**的元素基本运算
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.每个张量至少一个维度
2.从尾部开始,维度尺寸要么相等,要么其中一个张量维度尺寸为1,要么其中一个张量不存在这个维度
# X的最后一个元素
X[-1]
# 第二个和第三个元素
X[1:3]
# 通过索引将元素写入矩阵
X[1,2] = 9
X[0:2,:] = 12
A = X.numpy()
B = torch.tensor(A)
# 要将大小为1的张量转换为python的标量,可以调用item函数或是python的内置函数
a = torch.tensor([5])
a.item()
float(a)
int(a)
# 操作系统接口模块
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,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')
# 导入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)
标量:
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()
# 使用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)'])
# 对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可以验证梯度是否正确。
%matplotlib inline
import torch
from torch.distributions import multinomial
from d2l import torch as d2l
# 为了抽取一个样本我们只需要传入一个概率向量
fair_probs = torch.ones([6])/6 # tensor([1,1,1,1,1,1])
# 1为取样次数,fair_probs为概率向量
multinomial.Multinomial(1,fair_probs).sample()
# 模拟1000次投掷
counts = multinomial.Multinomial(1000, fair_probs).sample()
counts/1000 # 相对频率作为估计值
# 随着时间推移收敛到真实概率
counts = multinomial.Multinomial(10,fair_probs).sample((500,))
# 层层叠加
cum_count = count.cumsum(dim=0)
estimates = cum_counts / cum_counts.sum(dim=1,keepdims=True)
d2l.set_figsize((6,4.5))
for i in range(6):
d2l.plt.plot(estimate[:,i].numpy(),
label=("P(die=" + str(i+1) + ")"))
d2l.plt.axhline(y=0.167, color='black', linestyle='dashed')
d2l.plt.gca().set_xlabel('试验次数')
d2l.plt.gca().set_ylabel('估算概率')
d2l.plt.legend()