Tensor的内容还是挺多的,不过还是要坚持下去继续学习,下面我们首先回顾一下上一篇文章的知识点:
创建Tensor有哪些方法?
如何获取Tensor的形状?
Tensor和Numpy的互相转化?
增加维度和降低维度用什么命令?
Tensor的默认类型是什么?
mul和nn的区别?
ceil、round、floor的定义不要弄混了,还记得吗?
clamp(input,min,max)还知道啥意思吗?
归并操作,如求某一维度的均值或和,输出的结果的形状又是啥?
提取对角线元素和求矩阵的迹又是用什么命令呢?
作者在写上面这些内容的时候脑海里也在不断回想上一篇的知识点呢,如果不记得的小伙伴记得要回头看哦。所以嘛,人都会忘记的,记忆力有限,只要坚持勤加复习,就可以了!
1. Tensor和Numpy的转换
今天首先我们先讲一下Tensor和Numpy,相信大家也看出来了,Tensor和Numpy的相似性非常大,彼此之间的互操作也非常简单高效。并且还要记得的是Tensor和Numpy是共享内存的。由于Numpy历史悠久,支持丰富的操作,所以如果遇到Tensor不支持的操作时,可先转为numpy数组,处理结束后再转为Tensor也可以哦。
>>> import torch as t>>> import numpy as np>>> a=np.array([1,2,3],dtype=np.float32)>>> aarray([1., 2., 3.], dtype=float32)>>> b=t.from_numpy(a)>>> btensor([1., 2., 3.])>>> c=b.numpy()>>> carray([1., 2., 3.], dtype=float32)
2. 广播法则
广播法则也是计算中经常用到的一种法则,目前pytorch已经实现了自动广播法则,但是了解其中的细节可以帮助我们更好的理解广播法则,大家也可以通过下面两个函数手动实现广播法则,下面让我们一起来试试吧:
- unsqueeze或者view:为数据的某一维的形状补1;
- expand或者expand_as:重复数组;
>>> a=t.ones(3,2)>>> b=t.zeros(2,3,1)"""第一步:a是二维,b是三维,所以先在较小的a前面补1;如:a.unsqueeze(0),a的形状变为(1,3,2);第二步:a和b的第一维和第三维形状不一样,开始重复数组如:a.unsqueeze(0).expand(2,3,2),a变成了(2,3,2)的形状;""">>> a+b # 自动广播法则tensor([[[1., 1.], [1., 1.], [1., 1.]], [[1., 1.], [1., 1.], [1., 1.]]])>>> a=t.ones(3,2)>>> b=t.zeros(2,3,1)>>> a+btensor([[[1., 1.], [1., 1.], [1., 1.]], [[1., 1.], [1., 1.], [1., 1.]]])# 两种方式手动实现广播法则>>> a.unsqueeze(0)tensor([[[1., 1.], [1., 1.], [1., 1.]]])>>> a.unsqueeze(0).expand(2,3,2)+b.expand(2,3,2)tensor([[[1., 1.], [1., 1.], [1., 1.]], [[1., 1.], [1., 1.], [1., 1.]]])>>> a.view(1,3,2).expand(2,3,2)+b.expand(2,3,2)tensor([[[1., 1.], [1., 1.], [1., 1.]], [[1., 1.], [1., 1.], [1., 1.]]])
3. 内部结构
Tensor其实是由来两部分组成:头信息区(Tensor)和存储区(Storage),头信息区主要保存着tensor的形状(size)、步长(stride)、数据类型(dtype)等信息。而真正的数据则保存成连续数组。
一般来说,不同Tensor的头信息可能不同,但是却可能使用相同的storage。下面来看两个例子。
>>> a=t.arange(0,6)>>> atensor([0, 1, 2, 3, 4, 5])>>> a.storage() 0 1 2 3 4 5[torch.LongStorage of size 6]>>> b=a.view(2,3)>>> btensor([[0, 1, 2], [3, 4, 5]])>>> b.storage() # a和b共享内存地址 0 1 2 3 4 5[torch.LongStorage of size 6]>>> id(a.storage()) == id(b.storage()) # 一个对象的id可以当作内存地址True>>> a[1]=110 # a改变,则b也跟着改变,因为共享内存>>> btensor([[ 0, 110, 2], [ 3, 4, 5]])>>> c = a[2:]>>> ctensor([2, 3, 4, 5])>>> c.storage() # 这个还不太懂,为啥c也是6个,不应该是4个嘛 0 110 2 3 4 5[torch.LongStorage of size 6]>>> c[0]= 100 # c[0]对应着a[2],所以a也改变了>>> atensor([ 0, 110, 100, 3, 4, 5])
4. 持久化
将Tensor进行保存也是非常简单的,使用t.save和t.load即可完成相应的功能。在load时还可以将GPU Tensor映射到CPU或者其他GPU上。
if t.cuda.is_available(): a = a.cuda(1) # 把a转为GPU1上的tensor t.save(a, "a.pth") #加载为b,保存在GPU1上 #因为存储时就在GPU1上 b = t.load("a.pth") #加载为c,存储为CPU c = t.load("a.pth", map_location = lambda storage, loc: storage) #加载为d,存储为GPU0上 d = t.load("a.pth",map_location = {"cuda:1":"cuda:0"})
5. 小试牛刀:线性回归
import torch as tfrom matplotlib.pyplot as pltt.manual_seed(20)def get_data(batch_size = 8): """ y = 2 * x + 3 + noise """ x=t.rand(batch_size,1) * 20 y = x * 2 + (1 + t.randn(batch_size, 1)) * 1 return x, yx, y = get_data()#看看产生的x和y分布plt.scatter(x.squeeze().numpy(), y.squeeze().numpy())plt.savefig("x_y.png")plt.show()#现在我们开始根据结果学习w和b#首先随即初始化参数w = t.rand(1, 1)b = t.rand(1, 1)lr = 0.01 #学习率for epoch in range(20000): x, y = get_data() #foward:计算loss (其中b.expand_as(y)意思是将b重复到和y形状相同) y_pred = x.mm(w) + b.expand_as(y) loss = 0.5 * (y_pred - y) ** 2 #均方差 loss = loss.sum() #backward:手动计算梯度 dloss = 1 dy_pred = dloss * (y_pred- y) dw = x.t().mm(dy_pred) db = dy_pred.sum() #更新参数 w.sub_(lr * dw) b.sub_(lr * db) if epoch % 100 == 0: x = t.arange(0, 20).view(-1, 1) y = x.mm(w) + b.expand_as(x) plt.plot(x.numpy(),y.numpy()) #predict x2, y2 = get_data(batch_size) plt.scatter(x2.numpy(), y2.numpy()) plt.xlim(0,20) plt.ylim(0,41) plt.show()print(w.squeeze()[0],b.squeeze()[0]) #2.0185 3.0357
上面提到的tensor的许多操作,不要求全部掌握,日后遇到了可以再来查阅,有个印象即可。