一、张量的操作
1、cat 张量拼接
t=torch.ones((2,3))
t_0=torch.cat([t,t],dim=0) #2+2
t_1=torch.cat([t,t],dim=1) #3+3
print('t_0:{} shape:{}\n t_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])
2、stack 创建新维度,扩展维度
t=torch.ones((2,3))
t_stack=torch.stack([t,t],dim=0) #在第0维度再创建一个维度并进行拼接
print('\nt:{} shape:{}'.format(t,t.shape))
print('\nt_stack:{} shape:{}'.format(t_stack,t_stack.shape))
结果:(是在第一维度位置拼接)
t:tensor([[1., 1., 1.],
[1., 1., 1.]]) shape:torch.Size([2, 3])
t_stack:tensor([[[1., 1., 1.],
[1., 1., 1.]],
[[1., 1., 1.],
[1., 1., 1.]]]) shape:torch.Size([2, 2, 3])
如果在第3维度插入,会改变比较大的形状
t=torch.ones((2,3))
t_stack=torch.stack([t,t],dim=0) #在第0维度再创建一个维度并进行拼接
print('\nt:{} shape:{}'.format(t,t.shape))
print('\nt_stack:{} shape:{}'.format(t_stack,t_stack.shape))
结果:(是在第一维度位置拼接)
t:tensor([[1., 1., 1.],
[1., 1., 1.]]) shape:torch.Size([2, 3])
t_stack:tensor([[[1., 1.],
[1., 1.],
[1., 1.]],
[[1., 1.],
[1., 1.],
[1., 1.]]]) shape:torch.Size([2, 3, 2])
3、chunk 将张量按维度dim进行平均切分
a=torch.ones((2,7))
list_of_tensors=torch.chunk(a,dim=1,chunks=3)
#切分两份张量
for idx,t in enumerate(list_of_tensors):
print('第{}个张量:{}, shape is {}'.format(idx+1,t,t.shape))
结果:7/3=2.5,所以 分的话是3, 3,1
第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])
4、split 将张量按维度dim进行切分
t=torch.ones((2,5))
list_of_tensors=torch.split(t,2,dim=1)
list_of_tensors=torch.split(t,[2,1,2],dim=1) #按照list上数值来切分,注意list各元素之和=dim维上元素之和
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])
5、index_select() 按index索引数据
t=torch.ones((2,5))
list_of_tensors=torch.split(t,2,dim=1)
list_of_tensors=torch.split(t,[2,1,2],dim=1) #按照list上数值来切分,注意list各元素之和=dim维上元素之和
for idx,t in enumerate(list_of_tensors):
print('第{}个张量:{}. shape is {}'.format(idx+1,t,t.shape))
结果:
t:
tensor([[5, 6, 2],
[7, 1, 7],
[1, 4, 8]])
t_select:
tensor([[5, 6, 2],
[1, 4, 8]])
6、masked_select() 筛选数据,按照mask中的True进行索引
t=torch.randint(0,9,size=(3,3))
mask=t.ge(5) #ge大于等于 greater than or equal gt大于 le小于等于
t_select=torch.masked_select(t,mask)
print('t:\n{}\nmask:{}\nt_select:\n{}'.format(t,mask,t_select))
结果:
t:
tensor([[0, 3, 0],
[1, 7, 5],
[3, 5, 5]])
mask:tensor([[False, False, False],
[False, True, True],
[False, True, True]])
t_select:
tensor([7, 5, 5, 5])
7、reshape 变换张量形状
t=torch.randperm(8)
# t_reshape=torch.reshape(t,(2,4))
# t_reshape=torch.reshape(t,(-1,4)) #采用8/4来得到新维度
t_reshape=torch.reshape(t,(-1,2,2)) #采用8/2/2来得到新维度,新维度也就是赋值为-1的维度
print('t:{}\nt_reshape:\n{}'.format(t,t_reshape.shape))
结果:
t:tensor([1, 2, 5, 4, 0, 3, 7, 6])
t_reshape:
torch.Size([2, 2, 2])
此处也体现出内存共享
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, 2, 5, 4, 0, 3, 7, 6])
t_reshape:
tensor([[[1024, 2],
[ 5, 4]],
[[ 0, 3],
[ 7, 6]]])
t.data 内存地址:1691486721592
t_reshape.data 内存地址:1691486721592
8、torch.transpose() 交换维度
t=torch.rand((2,3,4))
t_transpose=torch.transpose(t,dim0=0,dim1=2) #c*h*w--->h*w*c
print('t shape:{}\n t_transpose shape:{}'.format(t.shape,t_transpose.shape))
结果:
t shape:torch.Size([2, 3, 4])
t_transpose shape:torch.Size([4, 3, 2])
9、二维张量转置torch.rand,对矩阵而言,等价torch.transpose(input,0,1)
t=torch.rand((2,3))
t_t=torch.t(t)
print('t shape:{}\n t_t shape:{}'.format(t.shape,t_t.shape))
结果:
t shape:torch.Size([2, 3])
t_t shape:torch.Size([3, 2])
10、张量变换 squeeze 压缩长度为1的维度
dim 若为None,移除所有长度为1的轴;若指定维度,当且仅当该轴长度为1时,可以移除。
t=torch.rand((1,2,3,1))
t_sq=torch.squeeze(t) #长度为1的轴被移除
t_0=torch.squeeze(t,dim=0) #指定
t_1=torch.squeeze(t,dim=1) #虽然指定,但是由于长度不为1,所以也不会被压缩
print(t.shape)
print(t_sq.shape)
print(t_0.shape)
print(t_1.shape)
结果:
torch.Size([1, 2, 3, 1])
torch.Size([2, 3])
torch.Size([2, 3, 1])
torch.Size([1, 2, 3, 1])
11、unsqueeze() 依据dim扩展维度
dim 若为None,移除所有长度为1的轴;若指定维度,当且仅当该轴长度为1时,可以移除。
t=torch.rand((1,2,3,1))
t_2=torch.unsqueeze(t,dim=1)
print('t的shape',t.shape)
print('t_2的shape',t_2.shape)
结果:
t的shape torch.Size([1, 2, 3, 1])
t_2的shape torch.Size([1, 1, 2, 3, 1])
二、张量的数学运算
1、torch.add()
这里有个trick,torch.add(a,10,b)===>a+10*b
t_0=torch.randn((3,3))
t_1=torch.ones_like(t_0)
t_add=torch.add(t_0,10,t_1)
print('t_0:\n{}\nt_1:\n{}\nt_add_10:\n{}'.format(t_0,t_1,t_add))
结果:
t_0:
tensor([[-0.0172, -0.5480, 0.9224],
[-0.1373, 1.4477, -0.9650],
[-0.5231, 0.3081, -1.0270]])
t_1:
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
t_add_10:
tensor([[ 9.9828, 9.4520, 10.9224],
[ 9.8627, 11.4477, 9.0350],
[ 9.4769, 10.3081, 8.9730]])
三、求导篇
1、计算图concept
2、计算图之动态图与静态图
3、autograd
autograd.backward
自动求取梯度
tensors:求导的张量
retain_Graph 保存计算图
create_graph 创建导数计算图
grad_tensors多梯度权重
(1)retain_Graph
import torch
w=torch.tensor([1.],requires_grad=True)
x=torch.tensor([2.],requires_grad=True)
a=torch.add(w,x)
b=torch.add(w,1)
y=torch.mul(a,b)
y.backward()
print(w.grad)
结果:
tensor([5.])
如果需要求某个非叶子节点的导数,需要retain_Graph=True
y.backward(retain_graph=True)
y.backward()
print(w.grad)
结果
tensor([10.])
(2)grad_tensors
w=torch.tensor([1.],requires_grad=True)
x=torch.tensor([2.],requires_grad=True)
a=torch.add(w,x)
b=torch.add(w,1)
y0=torch.mul(a,b) #y0=(x+w)*(w+1) dy0/dw=5
y1=torch.add(a,b) #y1=(x+w)+(w+1) dy1/dw=2
loss=torch.cat([y0,y1],dim=0) #[y0.y1]
grad_tensors=torch.tensor([1.,2.])
loss.backward(gradient=grad_tensors)
print(w.grad)
gradient 传入 torch.autograd.backward()的grad_tensors,用于多个梯度之间权重的设置
y0导数grad_tensors[0] +y1导数grad_tensors[1]
结果:
tensor([9.])
(3)create_graph
x=torch.tensor([3.],requires_grad=True)
y=torch.pow(x,2) #y=x**2
grad_1=torch.autograd.grad(y,x,create_graph=True)
print(grad_1)
grad_1=dy/dx=2x=2*3=6 一阶导数,需要在create_graph,创建导数计算图,从而得出高阶求导
结果:
(tensor([6.], grad_fn=<MulBackward0>),)
再次求导
grad_2=torch.autograd.grad(grad_1[0],x) #grad_2=d(dy/dx)/dx=d(2x)/dx=2
print(grad_2)
结果:
(tensor([2.]),)
(4)梯度不会自动清零
w=torch.tensor([1.],requires_grad=True)
x=torch.tensor([2.],requires_grad=True)
for i in range(2):
a=torch.add(w,x)
b=torch.add(w,1)
y=torch.mul(a,b)
y.backward()
print(w.grad)
结果:
tensor([5.])
tensor([10.])
所以在求完梯度后,要清零
w.grad.zero_()
这里的zero_的下划线表示in-place原地操作。
(5)依赖于叶子节点的结点,require_grad默认为True
w=torch.tensor([1.],requires_grad=True)
x=torch.tensor([2.],requires_grad=True)
a=torch.add(w,x)
b=torch.add(w,1)
y=torch.mul(a,b)
print(a.requires_grad,b.requires_grad,y.requires_grad)
结果:
True True True
(6)叶子节点不可执行in-place操作,原地操作:在原始内存中改变这个数据
w=torch.tensor([1.],requires_grad=True)
x=torch.tensor([2.],requires_grad=True)
a=torch.add(w,x)
b=torch.add(w,1)
y=torch.mul(a,b)
w.add_(1)
结果:
RuntimeError: a leaf Variable that requires grad has been used in an in-place operation.
原理解释:
这是原始空间数据:
a=torch.ones((1,))
print(id(a),a)
结果:
1691445516952 tensor([1.])
如果不进行原地操作:
a=a+torch.ones((1,)) #开辟新空间
print(id(a),a)
a=a+torch.ones((1,)) #开辟新空间
print(id(a),a)
结果:
1691486723752 tensor([1.])
1691486722232 tensor([2.])
如果进行原地操作:
a=torch.ones((1,))
print(id(a),a)
a+=torch.ones((1,)) #原地址直接改变
print(id(a),a)
结果:
1691486723832 tensor([1.])
1691486723832 tensor([2.])
叶子节点不能原地操作,因为如果原地操作后,该数据已经改变了,那样会破坏前向传播与反向传播的数据错误,从而导致梯度求解错误
四、应用篇
1、线性回归
w=w-LRw.grad b=b-LRw.grad
LR是学习率
Step1: 导包+设置随机数种子
这个随机数种子作用是以种子为随机数的基本,使得首次随机生成的数,在后面运行程序时则固定下来,即首次随机生成A、b、c,那么后面再次运行的时候,还是随机生成这些数
import torch
import matplotlib.pyplot as plt
torch.manual_seed(10)
lr=0.1 #学习率
Step2: 建立模型
x=torch.rand(20,1)*10 #x data(tensor), shape=(20,1)
y=2*x+(5+torch.randn(20,1)) #y data(tensor), shape=(20,1)
(5+torch.randn(20,1)) 此处相当于给方程偏置加入噪声
Step3: 初始化线性回归参数
w=torch.randn((1),requires_grad=True)
b=torch.zeros((1),requires_grad=True)
Step4: 开始训练
for iteration in range(1000):
#前向传播
wx=torch.mul(w,x)
y_pred=torch.add(wx,b) #这里的alpha=1,所以直接就是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)
Step5: 可视化
if loss.data.numpy()<0.5:
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.savefig('result.jpg')
break
结果如图:
2、逻辑回归
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import numpy as np
torch.manual_seed(10)
#导包
Step1: 生成数据
sample_nums=100
mean_value=1.7
bias=1
n_data=torch.ones(sample_nums,2)
x0=torch.normal(mean_value*n_data,1)+bias #类0 数据 shape=100,2
y0=torch.zeros(sample_nums) #类0 标签 shape=100,1
x1=torch.normal(-mean_value*n_data,1)+bias #类1 数据 shape=100,2
y1=torch.ones(sample_nums) #类1 标签shape=100,1
#拼接
train_x=torch.cat((x0,x1),0)
train_y=torch.cat((y0,y1),0)
mean_value是超参,控制自变量生成
Step2: 选择模型
class LR(nn.Module):
def __init__(self):
super(LR, self).__init__()
self.features=nn.Linear(2,1)
self.sigmoid=nn.Sigmoid()
def forward(self,x):
x=self.features(x)
x=self.sigmoid(x)
return x
这里的Linear(2,1)是输入x0,x1,输出1个概率值进行分类
.并将其实例化
lr_net=LR()
Step3: 选择损失函数
loss_fn=nn.BCELoss()
Step4: 选择优化器
lr=0.01 #学习率
optimizer=torch.optim.SGD(lr_net.parameters(),lr=lr,momentum=0.9)
Step5: 模型训练
for iteration in range(1000):
#前行传播
y_pred=lr_net(train_x)
#计算loss
loss=loss_fn(y_pred.squeeze(),train_y)
#反向传播
loss.backward()
#更新参数
optimizer.step()
Step6: 可视化(这里是接着上面循环体内)
计算参数(ge是大于等于)
mask = y_pred.ge(0.5).float().squeeze() # 0.5为阈值进行分类
correct = (mask == train_y).sum() # 计算正确预测的样本个数
acc = correct.item() / train_y.size(0) # 计算分类准确率
设置阈值停止训练
if acc > 0.99:
# 绘制训练数据
plt.scatter(x0.data.numpy()[:, 0], x0.data.numpy()[:, 1], c='r', label='class 0')
plt.scatter(x1.data.numpy()[:, 0], x1.data.numpy()[:, 1], c='b', label='class 1')
# 绘制逻辑回归模型
w0, w1 = lr_net.features.weight[0]
w0, w1 = float(w0.item()), float(w1.item())
plot_b = float(lr_net.features.bias[0].item())
plot_x = np.arange(-6, 6, 0.1)
plot_y = (-w0 * plot_x - plot_b) / w1
plt.xlim(-5, 7)
plt.ylim(-7, 7)
plt.plot(plot_x, plot_y)
plt.text(-5, 5, 'Loss=%.4f' % loss.data.numpy(), fontdict={'size': 20, 'color': 'red'})
plt.title(
'Iteration: {}\nw0:{:.2f} w1:{:.2f} b: {:.2f} accuracy:{:.2%}'.format(iteration, w0, w1, plot_b, acc))
plt.legend()
plt.show()
plt.pause(0.5)
break
结果为:
终于总结完了。没想到就因为偷懒了一下,积压了两次课的内容,搞了一晚上才搬迁完成,知识量激增。加油加油。