[PyTorch 学习笔记] 1.3 张量操作与线性回归
本章代码:https://github.com/zhangxiann/PyTorch_Practice/blob/master/lesson1/linear_regression.py
张量的操作
拼接
torch.cat()
torch.cat(tensors, dim=0, out=None)
功能:将张量按照 dim 维度进行拼接
- tensors: 张量序列
- dim: 要拼接的维度
代码示例:
t = torch.ones((2, 3))
t_0 = torch.cat([t, t], dim=0)
t_1 = torch.cat([t, t], dim=1)
print("t_0:{} shape:{}\nt_1:{} shape:{}".format(t_0, t_0.shape, t_1, t_1.shape))
输出是:
t_0:tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]]) shape:torch.Size([4, 3])
t_1:tensor([[1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1.]]) shape:torch.Size([2, 6])
torch.stack()
torch.stack(tensors, dim=0, out=None)
功能:将张量在新创建的 dim 维度上进行拼接
- tensors: 张量序列
- dim: 要拼接的维度
代码示例:
t = torch.ones((2, 3))
# dim =2
t_stack = torch.stack([t, t, t], dim=2)
print("\nt_stack.shape:{}".format(t_stack.shape))
# dim =0
t_stack = torch.stack([t, t, t], dim=0)
print("\nt_stack.shape:{}".format(t_stack.shape))
输出为:
t_stack.shape:torch.Size([2, 3, 3])
t_stack.shape:torch.Size([3, 2, 3])
第一次指定拼接的维度 dim =2,结果的维度是 [2, 3, 3]。后面指定拼接的维度 dim =0,由于原来的 tensor 已经有了维度 0,因此会把tensor 往后移动一个维度变为 [1,2,3],再拼接变为 [3,2,3]。
切分
torch.chunk()
torch.chunk(input, chunks, dim=0)
功能:将张量按照维度 dim 进行平均切分。若不能整除,则最后一份张量小于其他张量。
- input: 要切分的张量
- chunks: 要切分的份数
- dim: 要切分的维度
代码示例:
a = torch.ones((2, 7)) # 7
list_of_tensors = torch.chunk(a, dim=1, chunks=3) # 3
for idx, t in enumerate(list_of_tensors):
print("第{}个张量:{}, shape is {}".format(idx+1, t, t.shape))
输出为:
第1个张量:tensor([[1., 1., 1.],
[1., 1., 1.]]), shape is torch.Size([2, 3])
第2个张量:tensor([[1., 1., 1.],
[1., 1., 1.]]), shape is torch.Size([2, 3])
第3个张量:tensor([[1.],
[1.]]), shape is torch.Size([2, 1])
由于 7 不能整除 3,7/3 再向上取整是 3,因此前两个维度是 [2, 3],所以最后一个切分的张量维度是 [2,1]。
torch.split()
torch.split(tensor, split_size_or_sections, dim=0)
功能:将张量按照维度 dim 进行平均切分。可以指定每一个分量的切分长度。
- tensor: 要切分的张量
- split_size_or_sections: 为 int 时,表示每一份的长度,如果不能被整除,则最后一份张量小于其他张量;为 list 时,按照 list 元素作为每一个分量的长度切分。如果 list 元素之和不等于切分维度 (dim) 的值,就会报错。
- dim: 要切分的维度
代码示例:
t = torch.ones((2, 5))
list_of_tensors = torch.split(t, [2, 1, 2], dim=1)
for idx, t in enumerate(list_of_tensors):
print("第{}个张量:{}, shape is {}".format(idx+1, t, t.shape))
结果为:
第1个张量:tensor([[1., 1.],
[1., 1.]]), shape is torch.Size([2, 2])
第2个张量:tensor([[1.],
[1.]]), shape is torch.Size([2, 1])
第3个张量:tensor([[1., 1.],
[1., 1.]]), shape is torch.Size([2, 2])
索引
torch.index_select()
torch.index_select(input, dim, index, out=None)
功能:在维度 dim 上,按照 index 索引取出数据拼接为张量返回。
- input: 要索引的张量
- dim: 要索引的维度
- index: 要索引数据的序号
代码示例:
# 创建均匀分布
t = torch.randint(0, 9, size=(3, 3))
# 注意 idx 的 dtype 不能指定为 torch.float
idx = torch.tensor([0, 2], dtype=torch.long)
# 取出第 0 行和第 2 行
t_select = torch.index_select(t, dim=0, index=idx)
print("t:\n{}\nt_select:\n{}".format(t, t_select))
输出为:
t:
tensor([[4, 5, 0],
[5, 7, 1],
[2, 5, 8]])
t_select:
tensor([[4, 5, 0],
[2, 5, 8]])
torch.mask_select()
torch.masked_select(input, mask, out=None)
功能:按照 mask 中的 True 进行索引拼接得到一维张量返回。
- 要索引的张量
- mask: 与 input 同形状的布尔类型张量
代码示例:
t = torch.randint(0, 9, size=(3, 3))
mask = t.le(5) # ge is mean greater than or equal/ gt: greater than le lt
# 取出大于 5 的数
t_select = torch.masked_select(t, mask)
print("t:\n{}\nmask:\n{}\nt_select:\n{} ".format(t, mask, t_select))
结果为:
t:
tensor([[4, 5, 0],
[5, 7, 1],
[2, 5, 8]])
mask:
tensor([[ True, True, True],
[ True, False, True],
[ True, True, False]])
t_select:
tensor([4, 5, 0, 5, 1, 2, 5])
最后返回的是一维张量。
变换
torch.reshape()
torch.reshape(input, shape)
功能:变换张量的形状。当张量在内存中是连续时,返回的张量和原来的张量共享数据内存,改变一个变量时,另一个变量也会被改变。
- input: 要变换的张量
- shape: 新张量的形状
代码示例:
# 生成 0 到 8 的随机排列
t = torch.randperm(8)
# -1 表示这个维度是根据其他维度计算得出的
t_reshape = torch.reshape(t, (-1, 2, 2))
print("t:{}\nt_reshape:\n{}".format(t, t_reshape))
结果为:
t:tensor([5, 4, 2, 6, 7, 3, 1, 0])
t_reshape:
tensor([[[5, 4],
[2, 6]],
[[7, 3],
[1, 0]]])
在上面代码的基础上,修改原来的张量的一个元素,新张量也会被改变。
代码示例:
# 修改张量 t 的第 0 个元素,张量 t_reshape 也会被改变
t[0] = 1024
print("t:{}\nt_reshape:\n{}".format(t, t_reshape))
print("t.data 内存地址:{}".format(id(t.data)))
print("t_reshape.data 内存地址:{}".format(id(t_reshape.data)))
结果为:
t:tensor([1024, 4, 2, 6, 7, 3, 1, 0])
t_reshape:
tensor([[[1024, 4],
[ 2, 6]],
[[ 7, 3],
[ 1, 0]]])
t.data 内存地址:2636803119936
t_reshape.data 内存地址:2636803119792
torch.transpose()
torch.transpose(input, dim0, dim1)
功能:交换张量的两个维度。常用于图像的变换,比如把c*h*w
变换为h*w*c
。
- input: 要交换的变量
- dim0: 要交换的第一个维度
- dim1: 要交换的第二个维度
代码示例:
#把 c * h * w 变换为 h * w * c
t = torch.rand((2, 3, 4))
t_transpose = torch.transpose(t, dim0=1, dim1=2) # c*h*w h*w*c
print("t shape:{}\nt_transpose shape: {}".format(t.shape, t_transpose.shape))
结果为:
t shape:torch.Size([2, 3, 4])
t_transpose shape: torch.Size([2, 4, 3])
torch.t()
功能:2 维张量转置,对于 2 维矩阵而言,等价于torch.transpose(input, 0, 1)
。
torch.squeeze()
torch.squeeze(input, dim=None, out=None)
功能:压缩长度为 1 的维度。
- dim: 若为 None,则移除所有长度为 1 的维度;若指定维度,则当且仅当该维度长度为 1 时可以移除。
代码示例:
# 维度 0 和 3 的长度是 1
t = torch.rand((1, 2, 3, 1))
# 可以移除维度 0 和 3
t_sq = torch.squeeze(t)
# 可以移除维度 0
t_0 = torch.squeeze(t, dim=0)
# 不能移除 1
t_1 = torch.squeeze(t, dim=1)
print("t.shape: {}".format(t.shape))
print("t_sq.shape: {}".format(t_sq.shape))
print("t_0.shape: {}".format(t_0.shape))
print("t_1.shape: {}".format(t_1.shape))
结果为:
t.shape: torch.Size([1, 2, 3, 1])
t_sq.shape: torch.Size([2, 3])
t_0.shape: torch.Size([2, 3, 1])
t_1.shape: torch.Size([1, 2, 3, 1])
torch.unsqueeze()
torch.unsqueeze(input, dim)
功能:根据 dim 扩展维度,长度为 1。
张量的数学运算
主要分为 3 类:加减乘除,对数,指数,幂函数 和三角函数。
这里介绍一下常用的几种方法。
torch.add()
torch.add(input, other, out=None)
torch.add(input, other, *, alpha=1, out=None)
功能:逐元素计算 input + alpha * other。因为在深度学习中经常用到先乘后加的操作。
- input: 第一个张量
- alpha: 乘项因子
- other: 第二个张量
torch.addcdiv()
torch.addcdiv(input, tensor1, tensor2, *, value=1, out=None)
计算公式为:out ${i}=\operatorname{input}{i}+$ value $\times \frac{\text { tensor } 1_{i}}{\text { tensor } 2_{i}}$
torch.addcmul()
torch.addcmul(input, tensor1, tensor2, *, value=1, out=None)
计算公式为:out ${i}=$ input ${i}+$ value $\times$ tensor $1_{i} \times$ tensor $2_{i}$
线性回归
线性回归是分析一个变量 ($y$) 与另外一 (多) 个变量 ($x$) 之间的关系的方法。一般可以写成 $y=wx+b$。线性回归的目的就是求解参数$w, b$。
线性回归的求解可以分为 3 步:
- 确定模型:$y=wx+b$
- 选择损失函数,一般使用均方误差 MSE:$\frac{1}{m} \sum_{i=1}{m}\left(y_{i}-\hat{y}_{i}\right){2}$。其中 $ \hat{y}_{i} $ 是预测值,$y$ 是真实值。
- 使用梯度下降法求解梯度 (其中 $lr$ 是学习率),并更新参数:
- $w = w - lr * w.grad$
- $b = b - lr * b.grad$
代码如下:
import torch
import matplotlib.pyplot as plt
torch.manual_seed(10)
lr = 0.05 # 学习率
# 创建训练数据
x = torch.rand(20, 1) * 10 # x data (tensor), shape=(20, 1)
# torch.randn(20, 1) 用于添加噪声
y = 2*x + (5 + torch.randn(20, 1)) # y data (tensor), shape=(20, 1)
# 构建线性回归参数
w = torch.randn((1), requires_grad=True) # 设置梯度求解为 true
b = torch.zeros((1), requires_grad=True) # 设置梯度求解为 true
# 迭代训练 1000 次
for iteration in range(1000):
# 前向传播,计算预测值
wx = torch.mul(w, x)
y_pred = torch.add(wx, b)
# 计算 MSE loss
loss = (0.5 * (y - y_pred) ** 2).mean()
# 反向传播
loss.backward()
# 更新参数
b.data.sub_(lr * b.grad)
w.data.sub_(lr * w.grad)
# 每次更新参数之后,都要清零张量的梯度
w.grad.zero_()
b.grad.zero_()
# 绘图,每隔 20 次重新绘制直线
if iteration % 20 == 0:
plt.scatter(x.data.numpy(), y.data.numpy())
plt.plot(x.data.numpy(), y_pred.data.numpy(), 'r-', lw=5)
plt.text(2, 20, 'Loss=%.4f' % loss.data.numpy(), fontdict={'size': 20, 'color': 'red'})
plt.xlim(1.5, 10)
plt.ylim(8, 28)
plt.title("Iteration: {}\nw: {} b: {}".format(iteration, w.data.numpy(), b.data.numpy()))
plt.pause(0.5)
# 如果 MSE 小于 1,则停止训练
if loss.data.numpy() < 1:
break
训练的直线的可视化如下:
在 80 次的时候,Loss 已经小于 1 了,因此停止了训练。
参考资料
如果你觉得这篇文章对你有帮助,不妨点个赞,让我有更多动力写出好文章。
我的文章会首发在公众号上,欢迎扫码关注我的公众号张贤同学。
[PyTorch 学习笔记] 1.3 张量操作与线性回归的更多相关文章
- RX学习笔记:JavaScript数组操作
RX学习笔记:JavaScript数组操作 2016-07-03 增删元素 unshift() 在数组开关添加元素 array.unshift("value"); array.un ...
- 树莓派学习笔记——使用文件IO操作GPIO SysFs方式
0 前言 本文描写叙述假设通过文件IO sysfs方式控制树莓派 GPIO端口.通过sysfs方式控制GPIO,先訪问/sys/class/gpio文件夹,向export文件写入GPIO编号, ...
- Windows phone 8 学习笔记(2) 数据文件操作
原文:Windows phone 8 学习笔记(2) 数据文件操作 Windows phone 8 应用用于数据文件存储访问的位置仅仅限于安装文件夹.本地文件夹(独立存储空间).媒体库和SD卡四个地方 ...
- ES6学习笔记<五> Module的操作——import、export、as
import export 这两个家伙对应的就是es6自己的 module功能. 我们之前写的Javascript一直都没有模块化的体系,无法将一个庞大的js工程拆分成一个个功能相对独立但相互依赖的小 ...
- python3.4学习笔记(十五) 字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、分割等)
python3.4学习笔记(十五) 字符串操作(string替换.删除.截取.复制.连接.比较.查找.包含.大小写转换.分割等) python print 不换行(在后面加上,end=''),prin ...
- Android:日常学习笔记(10)———使用LitePal操作数据库
Android:日常学习笔记(10)———使用LitePal操作数据库 引入LitePal 什么是LitePal LitePal是一款开源的Android数据库框架,采用了对象关系映射(ORM)的模式 ...
- Python3+Selenium3+webdriver学习笔记13(js操作应用:弹出框无效如何处理)
#!/usr/bin/env python# -*- coding:utf-8 -*-'''Selenium3+webdriver学习笔记13(js操作应用:弹出框无效如何处理)'''from sel ...
- Python3+Selenium3+webdriver学习笔记12(js操作应用:滚动条 日历 内嵌div)
#!/usr/bin/env python# -*- coding:utf-8 -*-'''Selenium3+webdriver学习笔记12(js操作应用:滚动条 日历 内嵌div)'''from ...
- Windows phone 8 学习笔记(2) 数据文件操作(转)
Windows phone 8 应用用于数据文件存储访问的位置仅仅限于安装文件夹.本地文件夹(独立存储空间).媒体库和SD卡四个地方.本节主要讲解它们的用法以及相关限制性.另外包括本地数据库的使用方式 ...
随机推荐
- linux上安装mysql 5.7.22
主要步骤可以参照该网址: https://www.cnblogs.com/jxrichar/p/9248480.html 这里记录一下自己遇到的问题 1.在配置 vim /etc/my.cnf 文件的 ...
- 毫无基础的人入门Python,Python入门教程
随着人工智能的发展,Python近两年也是大火,越来越多的人加入到Python学习大军,对于毫无基础的人该如何入门Python呢?这里整理了一些个人经验和Python入门教程供大家参考. 如果你是零基 ...
- DVWA SQL 注入关卡初探
1. 判断回显 给id参数赋不同的值,发现有不同的返回信息 2. 判断参数类型 在参数后加 ' ,查看报错信息 数字型参数左右无引号,字符型参数左右有引号 4. 引号闭合与布尔类型判断 由于是字符型参 ...
- PHP E-mail 注入
PHP Secure E-mails 在上一节中的 PHP e-mail 脚本中,存在着一个漏洞. PHP E-mail 注入 首先,请看上一章中的 PHP 代码: <html> < ...
- PHP stat() 函数
定义和用法 stat() 函数返回关于文件的信息. 该函数将返回一个包含下列元素的数组: [0] 或 [dev] - 设备编号 [1] 或 [ino] - inode 编号 [2] 或 [mode] ...
- PHP uniqid() 函数
实例 生成一个唯一的 ID: <?phpecho uniqid();?>高佣联盟 www.cgewang.com 定义和用法 uniqid() 函数基于以微秒计的当前时间,生成一个唯一的 ...
- PHP ord() 函数
实例 返回 "h" 的 ASCII值: <?php高佣联盟 www.cgewang.comecho ord("h")."<br>&q ...
- 实战:docker搭建FastDFS文件系统并集成SpringBoot
实战:docker搭建FastDFS文件系统并集成SpringBoot 前言 15年的时候,那时候云存储还远远没有现在使用的这么广泛,归根结底就是成本和安全问题,记得那时候我待的公司是做建站开发的,前 ...
- 为什么需要将网站封装为app?
网站封装为app是一种宝贵的资源,为客户提供稳定的平台,一个网站也是一个有效的工具,用于企业与其客户之间的通信.企业网站用户可以通过他们的笔记本电脑,台式机,平板电脑,智能手机以及带有浏览器设备的 ...
- Qt 应用程序打包成安装文件
欢迎关注公众号: fensnote 文章目录 编译Release版本,拷贝依赖库文件 选择Release模式 使用windeployqt.exe命令提取用到的dll库 使用Inno Setup打包 下 ...