【动手学深度学习】笔记一
数据操作
torch.Tensor是存储与变换数据的主要工具。Tensor(张量)是一个多维数组,标量可以看作是0维张量,向量可以看作是1维张量,矩阵可以看作是2维张量。
创建一个Tensor
这部分属实很枯燥
注:dtype是自定义数据类型
函数(生成矩阵的) |
实现功能 |
---|---|
torch.empty(m,n) |
创建一个未初始化的m行n列的张量 |
torch.tensor([需要创建的数据]) |
直接创建一个值为 ”需要创建的数据“ 的张量 |
torch.randn(m,n) |
创建一个满足正态分布(0,1)的张量 |
torch.rand(m,n) |
随机生成在(0,1)一个m行n列的张量 |
torch.ones(m,n) |
创建一个全1的m行n列的张量 |
torch.zeros(m,n,dtype=张量类型) |
创建一个符合张量类型的全0m行n列的张量 |
torch.eye(m,n) |
生成一个m行n列的对角线为1,其他为0的张量 |
函数(生成行向量的) |
实现功能 |
---|---|
torch.linspace(s,e,steps) |
生成一个从s到e,均匀切成steps份的向量 |
torch.arange(s,e,steps) |
生成一个从s到e,步长为steps的向量 |
torch.randperm(m) |
生成一个有m个数的随机向量 |
对Tensor进行操作
有超过一百种的Tensor的运算操作。
获取行数与列数
获取Tensor的行数与列数 |
实现功能 |
---|---|
name.size() |
获取张量的行数和列数,返回一个元组(m,n) |
name.shape() |
同上 |
加法运算
加法运算 |
注释 |
---|---|
name1+name2 |
直接将两个张量进行相加 |
torch.add(x,y) |
|
y.add_(x) |
索引使用 :name[n,m]
使用索引访问Tensor:索引出来的结果与元数据共享内存,改变索引的结果也会改变原数据 。
索引使用 |
功能说明 |
---|---|
name[n,m] |
提取出name这个Tensor中的n行m列这个数,注意:索引是从0开始的 |
name[n,:] |
提取出name这个Tensor中的n行的这个向量 |
改变形状
用view()改变Tensor的形状
判断数据类型
函数 |
功能 |
---|---|
torch.is_tensor(obj) |
如果obj是tensor,则返回True |
torch.is_storage(obj) |
如果obj是存储图像,则返回True |
torch.isfloatingpoint(输入) |
如果输入的是浮点数据类型,则返回True |
函数 |
功能 |
---|---|
torch.setdefaultdtype(d) |
设置数据类型 |
torch.getdefaultdtype() |
读取数据类型 |
Tensor变换行、列
这个view()调用出来的数据和源数据共享,改变一个,另一个也会被一起改变。
函数 |
功能 |
---|---|
name.view(-1,m) |
将name这个Tensor转换为m列的张量,行数根据列数自动确定,-1是一种标志 |
name.view(n,-1) |
将name这个Tensor转换为n行的张量,列数根据行数自动确定,-1是一种标志 |
name.view(x,y) |
将name这个m行n列的张量转换为x行y列的张量 |
因为上面的原因,所以可以用clone克隆一个副本,然后对副本进行变换。
将标量张量转换为普通变量
import torch
x = torch.tensor(1.123456)
y = x.item()
print(x) #tensor(2.2469)
print(y) #2.2469120025634766
线性代数
这部分也是对Tensor进行操作。
函数 |
功能 |
---|---|
name1 = torch.trace(name) |
求name这个张量的对角线元素之和,然后存储到name1中 |
name1 = torch.diag(name) |
将name这个张量的对角线元素提取出来,然后存储到name1这个行向量中 |
torch.triu(name,n) |
矩阵上三角,只保留上三角的值,其余为0;n的作用是指定向上偏移量,如n=1,则为0的对角线向上平移1一个对角线 |
torch.tril(name,m) |
矩阵下三角,只保留下三角的值,其余为0;n的作用是指定向下偏移量,如n=1,则为0的对角线向下平移1一个对角线 |
torch.mm(name,name1) |
矩阵乘法 |
name1 = torch.t(name) |
对name转置 |
name1 = torch.inverse(name) |
对name求逆矩阵 |
name1 = torch.svd(name) |
对name求奇异值分解 |
广播( broadcasting )机制
前面的运算都是针对形状相同的Tensor,但当两个形状不同的Tensor进行运算时,会触发广播( broadcasting )机制。
广播机制:先适当复制元素使这两个 Tensor
形状相同后再按元素运算。(前提是两个Tensor要满足可以通过复制某些行或列会变成一样形状的;如:[2,4]和[1,4]可以实现广播机制;但[2,3]和[1,4]无法实现)
运算的内存开销
小注释:
- 索引操作不会新开辟一个内存地址,但y = x+y类的运算会新开辟一个内存地址。
- 了解内存的地址可以通过函数:id(name) 来确定name的内存地址
如果想指定结果到某一特定地址可以通过:
- y[:] = y + x来实现将y存储到原来的地址中(但经我实现,好像不可以在广播机制中实现,只能同形状直接加)
-
torch.add(x,y,out=y)
也可以通过这种方法来实现上述功能 -
y+=x,y.add_(x)
也可以如此实现。
Tensor和NumPy相互转换
通过numpy()和from_numpy()实现将Tensor和NumPy中的数组相互转换。
注意:这两个函数产生的数组共享相同内存,改变其中一个另一个也会转变。
还可以通过 torch.tensor()
实现转换,但这种方法会进行数据复制,所以他们不共享内存,改变一个另一个不会改变。
函数 |
功能 |
---|---|
name1 = name.numpy() |
将name转换为numpy数组并存储到name1中 |
name1 = torch.from_numpy(name) |
将name转换为Tensor数组并存储到name1中 |
自动求梯度(autograd)
动手学习深度学习,2.3
2020.1.28,好久没学习,今天开始学习。
深度学习过程中,常需要对函数进行求梯度,PyTorch提供的autograd包能够根据输入和前向传播过程自动构建计算图,并执行反向传播。
### 使用方法
- 首先需要将Tensor这个包的属性.requires_grad设置为True,作用是追踪在Tensor上所有的操作。
- 调用.backward()来完成所有梯度计算。同时,Tensor的梯度将累计到.grad属性中。
如果被计算对象是标量(只包含一个元素的数据),则不需要为backward()这个函数传入任何参数;否则,需要传入一个与被计算对象同形的Tensor
- 如果被计算对象为想部分追踪的,则可以通过.detach()将其从追踪记录中分离出来,调用该函数后,之后的操作将不再被追踪。
- 除了3中所述的方法,还可以通过with语句 torch.no_grad()来实现将不想被追踪的代码包裹起来。
- 每个Tensor都有一个.grad_fn属性,该属性是Tensor的Function。这个Function是用来记录这个Tensor是不是通过计算得到的,如果是,则存在一个Function;如果不是,则为None。
实例
.grad_fn 的使用。
不进行任何计算的情况
import torch
x = torch.ones(2,2,requires_grad = True) #创建一个张量,将属性 .requires_grad 设置为True
print(x) #打印x
print(x.grad_fn) #打印x的 .grad_fn 属性
输出结果
tensor([[1., 1.], [1., 1.]], requires_grad=True) #可以看到整个属性被设置为True
None #因为x没有进行任何计算,所以grad_fn的值为None
加法计算和叶子节点
叶子节点:如x这种直接创建的变量被称为叶子节点,并可以通过 变量名 .is_leaf 来判断是否为叶子节点
import torchx = torch.ones(2,2,requires_grad = True) #创建一个张量,将属性 .requires_grad 设置为True
y = x + 2
print(y) #打印y
print(y.grad_fn) #打印y的 .grad_fn 属性
print(x.is_leaf,y.is_leaf) #判断变量是否为叶子节点
输出结果
tensor([[3., 3.], [3., 3.]], grad_fn=<AddBackward0>) #可以看到属性.grad_fn的值为 AddBackward0<AddBackward0 object at 0x00000214C9878748> #这是该变量的内存位置
其他种的计算
import torchx = torch.ones(2,2,requires_grad = True)
y = x + 2
z = y*y*3out = z.mean()print(z,out)
输出结果
tensor([[27., 27.], [27., 27.]], grad_fn=<MulBackward0>) #可以看到z的grad_fn的值为MulBackward0tensor(27., grad_fn=<MeanBackward0>) #可以看到z的grad_fn的值为MeanBackward0
自动求梯度的方法
这个他娘的傻逼求梯度的原理就是求导,我没看懂。只知道使用,使锤子吧,不学造轮子了。
传入的结果是标量的情况
不需要传入张量。
import torchx = torch.ones(2,2,requires_grad = True)
y = x + 2
z = y * y * 3
out = z.mean() #求取均值
out.backward() #用来设置下面的被求导对象(即:设置out为被求导对象)
print(x.grad) #返回设置的计算结果对x的梯度向量(即:out对x的梯度向量)
输出结果
tensor([[4.5000, 4.5000], [4.5000, 4.5000]]) #可以见到,求导的结果是与输入张量同形的
传入的结果不是标量的情况
需要传入一个与结果同形的权重张量。
值得注意的是:输出的结果(x.grad)最后是与输入的值(x)相同的。
import torch
x = torch.tensor([1.0,2.0,3.0,4.0],requires_grad = True)y = x * 2z = y.view(2,2) #对x进行变换形状
v = torch.tensor([[1.0,0.1],[0.01,0.001]],dtype = torch.float) #创建一个与结果同形的权重张量。
z.backward(v) #传入这个权重张量。print(x.grad)
输出结果
tensor([2.0000, 0.2000, 0.0200, 0.0020]) #求导的结果也是与输入张量同形的
中断梯度追踪方法
通过 with语句+torch.no_grad()
import torch
x = torch.tensor(1.0,requires_grad = True)
y1 = x**2
with torch.no_grad(): #对下面语句进行中断梯度追踪 y2 = x**3
y3 = y1+y2
print(x.requires_grad) print(y1.requires_grad)print(y2.requires_grad)print(y3.requires_grad) #对四个变量进行判断是否要求梯度
输出
TrueTrueFlaseTrue #可以见到被隔离的y2变量不需要进行求梯度
被隔离的变量,不能调用 .backward(),否则会报错。
隔离更改数据
使用x.data之后,对变量进行任何更改都不会被autograd记录。
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 错误处理
- DiscuzX v3.4 任意文件删除漏洞
- 系列3|走进Node.js之多进程模型
- Java中Arraylist与linkedlist的区别
- 手把手教你撸一个 Webpack Loader
- HashMap与HashTable区别
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 记录日志
- React Native 网络层分析
- 如何实现VM框架中的数据绑定
- Java盲点解析
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 解析JSON
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 处理静态资源
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 视图Nunjucks
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 代码分层
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法