DL-常用函数及其作用
DL-常用函数及其作用
1、optimizer.zero_grad(),loss.backward()和optimizer.step()三个函数
在用pytorch训练模型时,通常会在遍历epochs的过程中依次用到optimizer.zero_grad(),loss.backward()和optimizer.step()三个函数,如下所示:
model = MyModel()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4)
for epoch in range(1, epochs):
for i, (inputs, labels) in enumerate(train_loader):
output= model(inputs)
loss = criterion(output, labels)
# compute gradient and do SGD step
optimizer.zero_grad()
loss.backward()
optimizer.step()
总得来说,这三个函数的作用是先将梯度归零(optimizer.zero_grad()),然后反向传播计算得到每个参数的梯度值(loss.backward()),*后通过梯度下降执行一步参数更新(optimizer.step())
optimizer.zero_grad():
def zero_grad(self):
r”””Clears the gradients of all optimized :class:`torch.Tensor` s.”””
for group in self.param_groups:
for p in group[‘params’]:
if p.grad is not None:
p.grad.detach_()
p.grad.zero_()
optimizer.zero_grad()函数会遍历模型的所有参数,通过p.grad.detach_()方法截断反向传播的梯度流,再通过p.grad.zero_()函数将每个参数的梯度值设为0,即上一次的梯度记录被清空。
清零原因:训练的过程通常使用mini-batch方法,如果不将梯度清零的话,梯度会与上一个batch的数据相关,因此该函数要写在反向传播和梯度下降之前。
loss.backward():
PyTorch的反向传播(即tensor.backward())是通过autograd包来实现的,autograd包会根据tensor进行过的数学运算来自动计算其对应的梯度。
具体来说,torch.tensor是autograd包的基础类,
如果设置tensor的requires_grads为True,就会开始跟踪这个tensor上面的所有运算,
如果做完运算后使用tensor.backward(),所有的梯度就会自动运算,tensor的梯度将会累加到它的.grad属性里面去。
更具体地说,损失函数loss是由模型的所有权重w经过一系列运算得到的,
若某个w的requires_grads为True,则w的所有上层参数(后面层的权重w)的.grad_fn属性中就保存了对应的运算,
然后在使用loss.backward()后,会一层层的反向传播计算每个w的梯度值,并保存到该w的.grad属性中。
如果没有进行tensor.backward()的话,梯度值将会是None,因此loss.backward()要写在optimizer.step()之前。
optimizer.step():
以SGD为例,torch.optim.SGD().step()源码如下:
def step(self, closure=None):
“””Performs a single optimization step.
Arguments:
closure (callable, optional): A closure that reevaluates the model
and returns the loss.
“””
loss = None
if closure is not None:
loss = closure()
for group in self.param_groups:
weight_decay = group[‘weight_decay’]
momentum = group[‘momentum’]
dampening = group[‘dampening’]
nesterov = group[‘nesterov’]
for p in group[‘params’]:
if p.grad is None:
continue
d_p = p.grad.data
if weight_decay != 0:
d_p.add_(weight_decay, p.data)
if momentum != 0:
param_state = self.state[p]
if ‘momentum_buffer’ not in param_state:
buf = param_state[‘momentum_buffer’] = torch.clone(d_p).detach()
else:
buf = param_state[‘momentum_buffer’]
buf.mul_(momentum).add_(1 – dampening, d_p)
if nesterov:
d_p = d_p.add(momentum, buf)
else:
d_p = buf
p.data.add_(-group[‘lr’], d_p)
return loss
step()函数的作用是执行一次优化步骤,通过梯度下降法来更新参数的值。因为梯度下降是基于梯度的,所以在执行optimizer.step()函数前应先执行loss.backward()函数来计算梯度。
注意:optimizer只负责通过梯度下降进行优化,而不负责产生梯度,梯度是tensor.backward()方法产生的。
参考https://blog.csdn.net/PanYHHH/article/details/107361827
2、assert(断言)
用于判断一个表达式,在表达式条件为 false 的时候触发异常。
断言可以在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃的情况,例如我们的代码只能在 Linux 系统下运行,可以先判断当前系统是否符合条件。
使用实例
>>> assert True # 条件为 true 正常执行
>>> assert False # 条件为 false 触发异常
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
AssertionError
>>> assert 1==1 # 条件为 true 正常执行
>>> assert 1==2 # 条件为 false 触发异常
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
AssertionError
>>> assert 1==2, ‘1 不等于 2’
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
AssertionError: 1 不等于 2
>>>
3、torch.cat()
函数目的: 在给定维度上对输入的张量序列seq 进行连接操作。
outputs = torch.cat(inputs, dim=0) → Tensor
参数
inputs : 待连接的张量序列,可以是任意相同Tensor类型的python 序列
dim : 选择的扩维, 必须在0到len(inputs[0])之间,沿着此维连接张量序列。
C = torch.cat( (A,B),0 ) #按维数0拼接(竖着拼)
C = torch.cat( (A,B),1 ) #按维数1拼接(横着拼)
示例
>>> import torch
>>> A=torch.ones(2,3) #2×3的张量(矩阵)
>>> A
tensor([[ 1., 1., 1.],
[ 1., 1., 1.]])
>>> B=2*torch.ones(4,3) #4×3的张量(矩阵)
>>> B
tensor([[ 2., 2., 2.],
[ 2., 2., 2.],
[ 2., 2., 2.],
[ 2., 2., 2.]])
>>> C=torch.cat((A,B),0) #按维数0(行)拼接
>>> C
tensor([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 2., 2., 2.],
[ 2., 2., 2.],
[ 2., 2., 2.],
[ 2., 2., 2.]])
>>> C.size()
torch.Size([6, 3])
>>> D=2*torch.ones(2,4) #2×4的张量(矩阵)
>>> C=torch.cat((A,D),1)#按维数1(列)拼接
>>> C
tensor([[ 1., 1., 1., 2., 2., 2., 2.],
[ 1., 1., 1., 2., 2., 2., 2.]])
>>> C.size()
torch.Size([2, 7])
4、slice()
slice() 函数返回 slice 对象(切片)。
slice 对象用于指定如何对序列进行裁切。您可以指定在哪里开始裁切以及在哪里结束裁切。您还可以指定步进,例如只切每隔一个项目。
语法:
slice(start, end, step)
参数值
start 可选。整数,指定在哪个位置开始裁切。默认为 0。
end 可选。整数,指定在哪个位置结束裁切。
step 可选。整数,指定裁切的步进值。默认为 1。
示例:
a = (“a”, “b”, “c”, “d”, “e”, “f”, “g”, “h”)
x = slice(2)
print(a[x])
运行结果:
(‘a’, ‘b’)
——————————————–
a = (“a”, “b”, “c”, “d”, “e”, “f”, “g”, “h”)
x = slice(3, 5)
print(a[x])
运行结果:
(‘d’, ‘e’)
———————————————-
a = (“a”, “b”, “c”, “d”, “e”, “f”, “g”, “h”)
x = slice(0, 8, 3)
print(a[x])
运行结果:
(‘a’, ‘d’, ‘g’)
5、 *args:表示接受任意长度的参数,然后存放入一个元组中;如def fun(*args) print(args),‘fruit’,’animal’,’human’作为参数传进去,输出(‘fruit’,’animal’,’human’)
6、**kwargs:表示接受任意长的参数,然后存放入一个字典中;如
def fun(**kwargs):
for key, value in kwargs.items():
print(“%s:%s” % (key,value)
输出:
fun(a=1,b=2,c=3)会输出 a=1 b=2 c=3
7、semilogy()绘图
semilogy 将使用 y 轴的对数刻度绘制数据。
d2l.semilogy(range(1, num_epochs + 1), train_ls, ‘epochs’, ‘rmse’,
range(1, num_epochs + 1), valid_ls,
[‘train’, ‘valid’])
8、plot()绘图
a、plot(y)
当y为向量时,是以y的分量为纵坐标,以元素序号为横坐标,用直线依次连接数据点,绘制曲线。若y为实矩阵,则按列绘制每列对应的曲线。
b、plot(x,y)
若y和x为同维向量,则以x为横坐标,y为纵坐标绘制连线图。若x是向量,y是行数或列数与x长度相等的矩阵,则绘制多条不同色彩的连线图,x被作为这些曲线的共同横坐标。若x和y为同型矩阵,则以x,y对应元素分别绘制曲线,曲线条数等于矩阵列数。
c、plot(x1,y1,x2,y2,……)
在此格式中,每对x,y必须符合plot(x,y)中的要求,不同对之间没有影响,命令将对每一对x,y绘制曲线。
以上三种格式中的x,y都可以是表达式。plot是绘制一维曲线的基本函数,但在使用此函数之前,须先定义曲线上每一点的x以及y坐标。
9、pytorch detach() item() cpu() numpy()
item()返回的是tensor中的值,且只能返回单个值(标量),不能返回向量,使用返回loss等。
detach阻断反向传播,返回值仍为tensor
cpu()将变量放在cpu上,仍为tensor:
numpy()将tensor转换为numpy:
注意cuda上面的变量类型只能是tensor,不能是其他
loss.item() # 获得loss的值
——————————–
gpu_info.detach() #返回tensor,仍在gpu上
tensor([[ 0.9621, -1.0931, -0.8481],
[-0.1668, -1.3945, 0.6562],
[ 0.6152, 0.4177, -0.3538]], device=’cuda:0′)
——————————————-
gpu_info.cpu()
tensor([[ 0.9621, -1.0931, -0.8481],
[-0.1668, -1.3945, 0.6562],
[ 0.6152, 0.4177, -0.3538]])
———————————–
gpu_info.cpu().numpy()
array([[ 0.9621306 , -1.0930926 , -0.8481391 ],
[-0.1667992 , -1.3945109 , 0.656157 ],
[ 0.6151904 , 0.41773367, -0.35378388]], dtype=float32)
10、concat方法相当于数据库中的全连接(UNION ALL),可以指定按某个轴进行连接,也可以指定连接的方式join(outer,inner 只有这两种)。
与数据库不同的是concat不会去重,要达到去重的效果可以使用drop_duplicates方法
concat(objs, axis=0, join=’outer’, join_axes=None, ignore_index=False,
keys=None, levels=None, names=None, verify_integrity=False, copy=True):
运行结果:
from pandas import Series,DataFrame,concat
df1 = DataFrame({‘city’: [‘Chicago’, ‘San Francisco’, ‘New York City’], ‘rank’: range(1, 4)})
df2 = DataFrame({‘city’: [‘Chicago’, ‘Boston’, ‘Los Angeles’], ‘rank’: [1, 4, 5]})
print ‘按轴进行内连接\r\n’,concat([df1,df2],join=”inner”,axis=1)
print ‘进行外连接并指定keys(行索引)\r\n’,concat([df1,df2],keys=[‘a’,’b’]) #这里有重复的数据
print ‘去重后\r\n’,concat([df1,df2],ignore_index=True).drop_duplicates()
————————————————————————-
运行结果:
按轴进行内连接
city rank city rank
0 Chicago 1 Chicago 1
1 San Francisco 2 Boston 4
2 New York City 3 Los Angeles 5
进行外连接并指定keys(行索引)
city rank
a 0 Chicago 1
1 San Francisco 2
2 New York City 3
b 0 Chicago 1
1 Boston 4
2 Los Angeles 5
去重后
city rank
0 Chicago 1
1 San Francisco 2
2 New York City 3
4 Boston 4
5 Los Angeles 5