Pytorch:torch.optim详解

本篇笔记主要介绍torch.optim模块,记录学习过程

在深度学习中,我们通常会使用优化算法来调整神经网络的权重和偏差,以便模型能够更好地拟合训练数据。torch.optim是PyTorch中的一个模块,它提供了各种优化算法的实现,用于自动化地优化神经网络的参数。换句话说,torch.optim可以帮助我们让模型更好地学习,从而提高性能。

优化器 Optimizer

  • 优化器主要是在模型训练阶段对模型可学习参数进行更新, 常用优化器有 SGD,RMSprop,Adam等
  • 优化器初始化时传入模型的可学习参数,以及其他超参数如 lr,momentum等
  • 在训练过程中先调用 optimizer.zero_grad() 清空梯度,再调用 loss.backward() 反向传播,最后调用optimizer.step()更新模型参数

简单使用示例如下所示:

import torch
import numpy as np
import warnings
warnings.filterwarnings('ignore') #ignore warnings
# 定义数据
x = torch.linspace(-np.pi, np.pi, 2000)
y = torch.sin(x)p = torch.tensor([1, 2, 3])
xx = x.unsqueeze(-1).pow(p)
# 定义模型
model = torch.nn.Sequential(torch.nn.Linear(3, 1),torch.nn.Flatten(0, 1))
# 定义损失函数:损失函数是一个衡量模型预测与实际值之间差距的函数
loss_fn = torch.nn.MSELoss(reduction='sum')
# 学习率:learning_rate参数表示学习率,它控制了每次参数更新的步长。
learning_rate = 1e-3
# 优化器:选择一个优化算法来优化模型的参数
optimizer = torch.optim.RMSprop(model.parameters(), lr=learning_rate)
# 迭代学习:训练模型
for t in range(1, 1001):y_pred = model(xx)loss = loss_fn(y_pred, y)if t % 100 == 0:print('No.{: 5d}, loss: {:.6f}'.format(t, loss.item()))# 梯度清零optimizer.zero_grad() # 反向传播计算梯度loss.backward() # 梯度下降法更新参数optimizer.step()

1.1 PyTorch 中的优化器

所有优化器都是继承父类 Optimizer,如下列表是 PyTorch 提供的优化器:
(具体用法和优缺点,后续更新。。。。。。。。。。。)

  • SGD
  • ASGD
  • Adadelta
  • Adagrad
  • Adam
  • AdamW
  • Adamax
  • SparseAdam
  • RMSprop
  • Rprop
  • LBFGS

1.2 父类Optimizer 基本原理

Optimizer 是所有优化器的父类,它主要有如下公共方法:

add_param_group(param_group): 添加模型可学习参数组
step(closure): 进行一次参数更新
zero_grad(): 清空上次迭代记录的梯度信息
state_dict(): 返回 dict 结构的参数状态
load_state_dict(state_dict): 加载 dict 结构的参数状态

上述方法一一解读:

1.2.1 初始化 Optimizer

初始化优化器只需要将模型的**可学习参数(params)超参数(defaults)**分别传入优化器的构造函数

关键几个点为:

  1. self.defaults = defaults
  2. self.state = defaultdict(dict)
  3. self.param_groups = [] 特别重要,最需要记住的属性,特别是属性的内容和其形式。
  4. self.add_param_group方法,往self.param_groups里面放东西。

下面是Optimizer的初始化函数核心代码:

class Optimizer(object):def __init__(self, params, defaults):# 字典类型,子类传入,用于表示全部参数组的默认超参self.defaults = defaults# 判断参数是否为torch.Tensor格式if isinstance(params, torch.Tensor):raise TypeError("params argument given to the optimizer should be ""an iterable of Tensors or dicts, but got " +torch.typename(params))self.param_groups = []param_groups = list(params)# 确保param_groups为一个字典if not isinstance(param_groups[0], dict):param_groups = [{'params': param_groups}]for param_group in param_groups:# 添加模型可学习参数组self.add_param_group(param_group)

该初始化方法接收两个参数,一个是params,一个是defaults

这两个分开说,先说params,最常见的就是model.parameters(),当然net.parameters()也是一样的,就是模型类的对象的变量名不同,如下所示。

optimizer = optim.SGD( net.parameters(),               # params的一种形式lr=LR, momentum=0.9)

params是个生成器,只返回各模型层的参数,没有参数名。

注意__init__方法中param_groups = list(params),得到一个新的变量 param_groups,(这里self.param_groups是干什么的呢??)。

param_groups = list(params),list可以把生成器的元素都取出来,所以,很明显,param_groups就是一个Parameter类对象的列表,里面的元素是每个网络层的参数weight和bias(如果有)

if not isinstance(param_groups[0], dict):param_groups = [{'params': param_groups}]

param_groups[0]是Parameter类,不是dict,这种形式的param_groups会被改造,将整个param_groups作为值,"params"作为键,形成一个键值对,放在字典里,然后重新赋值给param_groups。

现在我们要记得 param_groups的形式,一个列表,里面是一个字典,字典的键是"params",值为所有网络层的参数。

for param_group in param_groups:self.add_param_group(param_group)

将param_groups中的每个元素送进self.add_param_group这个列表中。现在的param_groups里只有一个元素{“param”: [参数]}。

1.2.2 add_param_group

该方法在初始化函数中用到,主要用来向 self.param_groups添加不同分组的模型参数

def add_param_group(self, param_group):r"""Add a param group to the :class:`Optimizer` s `param_groups`.This can be useful when fine tuning a pre-trained network as frozen layers can be madetrainable and added to the :class:`Optimizer` as training progresses.Arguments:param_group (dict): Specifies what Tensors should be optimized along with groupspecific optimization options."""# 步骤1:判断传进来的参数是否是一个字典,必然是一个字典,不是字典报错。assert isinstance(param_group, dict), "param group must be a dict"# 步骤2:取出字典里的"params"的值,就是参数的列表,这是个列表,然后一系列判断,走到第3步params = param_group['params']if isinstance(params, torch.Tensor):param_group['params'] = [params]elif isinstance(params, set):raise TypeError('optimizer parameters need to be organized in ordered collections, but ''the ordering of tensors in sets will change between runs. Please use a list instead.')else:# 步骤3:重新以列表的形式赋值回去param_group['params'] = list(params)# 步骤4:判断参数的列表里边的元素类型,必然是Parameter类型,也就是Tensor类型的,并且是叶子结点。for param in param_group['params']:if not isinstance(param, torch.Tensor):raise TypeError("optimizer can only optimize Tensors, ""but one of the params is " + torch.typename(param))if not param.is_leaf:raise ValueError("can't optimize a non-leaf Tensor")# 利用默认参数给所有组设置统一的超参# 步骤5:将defaults这个字典里的键值对拿出来,放到现在的param_group这个字典里,这样该字典构成一个具有完整参数的字典,# 其所有键为:dict_keys(['params', 'lr', 'momentum', 'dampening', 'weight_decay', 'nesterov']),方便step()方法调用。for name, default in self.defaults.items():if default is required and name not in param_group:raise ValueError("parameter group didn't specify a value of required optimization parameter "+name)else:param_group.setdefault(name, default)params = param_group['params']if len(params) != len(set(params)):warnings.warn("optimizer contains a parameter group with duplicate parameters; ""in future, this will cause an error; ""see github.com/pytorch/pytorch/issues/40967 for more information", stacklevel=3)# 步骤6: 判定当前字典中的参数组和之前的参数组是不是一样的。对于当前来说,self.param_groups是空的,所以直接到第7步param_set = set()for group in self.param_groups:param_set.update(set(group['params']))# 步骤7:判断param_set集合是否和param_group["params"]这个集合中具有相同元素,没有返回True,反之False。显然没有,所以7不执行。if not param_set.isdisjoint(set(param_group['params'])):raise ValueError("some parameters appear in more than one parameter group")# 步骤8:将构造完整的param_group这个字典,加到self.param_groups中去。self.param_groups.append(param_group)

将上述代码分为8个步骤: 请认真看一遍代码以及注释

现在我们知道self.param_groups这个列表中具有字典,每个字典的keys为dict_keys([‘params’, ‘lr’, ‘momentum’, ‘dampening’, ‘weight_decay’, ‘nesterov’]),当然,每个键都有其对应的值。这些键值对是构建SGD实例时,传进来的参数。

params还有一种常见形式如下。

fcParamsId = list(map(id, resnet18_ft.fc.parameters()))     # 返回的是parameters的 内存地址
features_params = filter(lambda p: id(p) not in fcParamsId, resnet18_ft.parameters())optimizer = optim.SGD([{'params': features_params, 'lr': LR * 0.1},                         #  这个列表是params的另一种形式{'params': resnet18_ft.fc.parameters()}], 'lr': LR, momentum=0.9
)

我们可以对比一下 这种形式(前面讲的)上述model.parameters() 这种方式的不同。

  • 前者是包含着字典的列表,后者是生成器
  • 进入到Optimizer类的初始化函数中,前者不变,还是包含2个字典的列表,后者先变成参数的列表,再变成只有一个字典的列表,该字典中只有一个键值对,"params"和所有参数值
  • 在执行self.add_param_group()方法的循环里,前者需要遍历两个字典,后者只有一个字典,也就是说,最终在self.param_groups这个列表中,前者最终具有两个具有全部键值对的字典,后者只有一个
  • 在如上self.add_param_group()方法中,步骤2处,取出前者字典中的"params"对应的值为生成器,在步骤3处,将其变成了参数的列表,后者在步骤2和步骤3处没变化,因为在初始化一开始就使用list()将model.parameters()的所有参数取出来了。
  • 在如上self.add_param_group()方法中,标记为步骤4处没区别。在步骤5处有区别,并且该区别非常关键。这是对不同参数设置不同学习率和动量的地方。
    param_group是循环中取出的字典,对于如上有两个字典的来说,就是将self.defaults里面取出来的键值对,分别放到两个字典中去。param_group.setdefault(name,
    default)这句代码。如果param_group这个字典中比如第一个字典具有"lr"这个键的,保持不变,对于第二个字典里面没有"lr"这个键的,将default和name设置为新的键值对。不知道的同学可以看一下dict中setdefault这个方法的功能。
  • 前者的步骤6和步骤7,由于存在两个字典,所以第二次需要和第一次进行对比,看看两个字典里面的"params"这个键对应的参数是否相同,如果相同,引用的就是同一块地址,设置不同的学习率等参数就没意义了,就会报错。
  • 最后,将两个具有所有键值对,但是参数不同的两个字典,加入到self.param_groups这个列表中。
1.2.3 step

此方法主要完成一次模型参数的更新

基类 Optimizer 定义了 step 方法接口,如下所示

def step(self, closure):r"""Performs a single optimization step (parameter update).Arguments:closure (callable): A closure that reevaluates the model andreturns the loss. Optional for most optimizers... note::Unless otherwise specified, this function should not modify the``.grad`` field of the parameters."""raise NotImplementedError

子类如 SGD 需要实现 step 方法,如下所示:

	@torch.no_grad()def step(self, closure=None):"""Performs a single optimization step.Arguments:closure (callable, optional): A closure that reevaluates the modeland returns the loss."""loss = Noneif closure is not None:with torch.enable_grad():loss = closure()#对参数进行遍历for group in self.param_groups: params_with_grad = []  #有梯度的网路参数收集列表d_p_list = [] #收集网络参数的梯度列表momentum_buffer_list = []# #以下为一些超参数的收集weight_decay = group['weight_decay']momentum = group['momentum']dampening = group['dampening']nesterov = group['nesterov']lr = group['lr']# 对而网络参数进行逐个遍历更新for p in group['params']: if p.grad is None:continued_p = p.gradif weight_decay != 0:d_p = d_p.add(p, alpha=weight_decay)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_(d_p, alpha=1 - dampening)if nesterov:d_p = d_p.add(buf, alpha=momentum)else:d_p = bufp.add_(d_p, alpha=-group['lr'])return loss
  • step 方法可传入闭包函数 closure,主要目的是为了实现如Conjugate Gradient和LBFGS等优化算法,这些算法需要对模型进行多次评估
  • Python 中闭包概念:在一个内部函数中,对外部作用域的变量进行引用(并且一般外部函数的返回值为内部函数),那么内部函数就被认为是闭包

下面是 closure 的简单示例:

from torch.nn import CrossEntropyLossdummy_model = DummyModel().cuda()optimizer = SGD(dummy_model.parameters(), lr=1e-2, momentum=0.9, weight_decay=1e-4)
# 定义loss
loss_fn = CrossEntropyLoss()
# 定义数据
batch_size = 2
data = torch.randn(64, 3, 64, 128).cuda()  # 制造假数据shape=64 * 3 * 64 * 128
data_label = torch.randint(0, 10, size=(64,), dtype=torch.long).cuda()  # 制造假的labelfor batch_index in range(10):batch_data = data[batch_index*batch_size: batch_index*batch_size + batch_size]batch_label = data_label[batch_index*batch_size: batch_index*batch_size + batch_size]def closure():optimizer.zero_grad()  # 清空梯度output = dummy_model(batch_data)  # forwardloss = loss_fn(output, batch_label)  # 计算lossloss.backward()  # backwardprint('No.{: 2d} loss: {:.6f}'.format(batch_index, loss.item()))return lossoptimizer.step(closure=closure)  # 更新参数
1.2.4 zero_grad

在反向传播计算梯度之前对上一次迭代时记录的梯度清零,参数set_to_none 设置为 True 时会直接将参数梯度设置为 None,从而减小内存使用

但通常情况下不建议设置这个参数,因为梯度设置为 None 和 0 在 PyTorch 中处理逻辑会不一样。

def zero_grad(self, set_to_none: bool = False):r"""Sets the gradients of all optimized :class:`torch.Tensor` s to zero.Arguments:set_to_none (bool): instead of setting to zero, set the grads to None.This is will in general have lower memory footprint, and can modestly improve performance.However, it changes certain behaviors. For example:1. When the user tries to access a gradient and perform manual ops on it,a None attribute or a Tensor full of 0s will behave differently.2. If the user requests ``zero_grad(set_to_none=True)`` followed by a backward pass, ``.grad``sare guaranteed to be None for params that did not receive a gradient.3. ``torch.optim`` optimizers have a different behavior if the gradient is 0 or None(in one case it does the step with a gradient of 0 and in the other it skipsthe step altogether)."""for group in self.param_groups:for p in group['params']:if p.grad is not None:if set_to_none:p.grad = Noneelse:if p.grad.grad_fn is not None:p.grad.detach_()else:p.grad.requires_grad_(False)p.grad.zero_()
1.2.5 state_dict() 和 load_state_dict

这两个方法实现序列化反序列化功能。

  • state_dict(): 将优化器管理的参数和其状态信息以 dict 形式返回
  • load_state_dict(state_dict): 加载之前返回的 dict,更新参数和其状态

两个方法可用来实现模型训练中断后继续训练功能

def state_dict(self):r"""Returns the state of the optimizer as a :class:`dict`.It contains two entries:* state - a dict holding current optimization state. Its contentdiffers between optimizer classes.* param_groups - a dict containing all parameter groups"""# Save order indices instead of Tensorsparam_mappings = {}start_index = 0def pack_group(group):nonlocal start_indexpacked = {k: v for k, v in group.items() if k != 'params'}param_mappings.update({id(p): i for i, p in enumerate(group['params'], start_index)if id(p) not in param_mappings})packed['params'] = [param_mappings[id(p)] for p in group['params']]start_index += len(packed['params'])return packedparam_groups = [pack_group(g) for g in self.param_groups]# Remap state to use order indices as keyspacked_state = {(param_mappings[id(k)] if isinstance(k, torch.Tensor) else k): vfor k, v in self.state.items()}return {'state': packed_state,'param_groups': param_groups,}

2.1 基类: _LRScheduler

学习率调整类主要的逻辑功能就是每个 epoch 计算参数组的学习率,更新 optimizer对应参数组中的lr值,从而应用在optimizer里可学习参数的梯度更新。

所有的学习率调整策略类的父类是torch.optim.lr_scheduler._LRScheduler,基类 _LRScheduler 定义了如下方法:

  • step(epoch=None): 子类公用
  • get_lr(): 子类需要实现
  • get_last_lr(): 子类公用
  • print_lr(is_verbose, group, lr, epoch=None): 显示 lr 调整信息
  • state_dict(): 子类可能会重写
  • load_state_dict(state_dict): 子类可能会重写
2.1.1 初始化

LR_scheduler是用于调节学习率lr的,在代码中,我们经常看到这样的一行代码

scheduler.step()

在pytorch代码中,各种类型scheduler大多基于 _LRScheduler

基类的初始化函数可传入两个参数,

  • 第一是optimizer就是之前我们讲过的优化器的实例
  • 第二个参数last_epoch是最后一次 epoch 的 index,默认值是 -1,代表初次训练模型

此时会对optimizer里的各参数组设置初始学习率 initial_lr。若last_epoch传入值大于 -1,则代表从某个 epoch 开始继续上次训练,此时要求optimizer的参数组中有initial_lr初始学习率信息。

初始化函数内部的 with_counter 函数主要是为了确保lr_scheduler.step()是在optimizer.step()之后调用的 (PyTorch=1.1 发生变化). 注意在__init__函数最后一步调用了self.step(),即_LRScheduler在初始化时已经调用过一次step()方法。

class _LRScheduler(object):def __init__(self, optimizer, last_epoch=-1, verbose=False):# Attach optimizerif not isinstance(optimizer, Optimizer):raise TypeError('{} is not an Optimizer'.format(type(optimizer).__name__))self.optimizer = optimizer# Initialize epoch and base learning ratesif last_epoch == -1:for group in optimizer.param_groups:group.setdefault('initial_lr', group['lr'])else:for i, group in enumerate(optimizer.param_groups):if 'initial_lr' not in group:raise KeyError("param 'initial_lr' is not specified ""in param_groups[{}] when resuming an optimizer".format(i))self.base_lrs = list(map(lambda group: group['initial_lr'], optimizer.param_groups))self.last_epoch = last_epoch# Following https://github.com/pytorch/pytorch/issues/20124# We would like to ensure that `lr_scheduler.step()` is called after# `optimizer.step()`def with_counter(method):if getattr(method, '_with_counter', False):# `optimizer.step()` has already been replaced, return.return method# Keep a weak reference to the optimizer instance to prevent# cyclic references.instance_ref = weakref.ref(method.__self__)# Get the unbound method for the same purpose.func = method.__func__cls = instance_ref().__class__del method@wraps(func)def wrapper(*args, **kwargs):instance = instance_ref()instance._step_count += 1wrapped = func.__get__(instance, cls)return wrapped(*args, **kwargs)# Note that the returned function here is no longer a bound method,# so attributes like `__func__` and `__self__` no longer exist.wrapper._with_counter = Truereturn wrapperself.optimizer.step = with_counter(self.optimizer.step)self.optimizer._step_count = 0self._step_count = 0self.verbose = verboseself.step()
2.1.2 step

当模型完成一个 epoch 训练时,需要调用step()方法,该方法里对last_epoch自增之后,在内部上下文管理器类里调用子类实现的get_lr()方法获得各参数组在此次 epoch 时的学习率,并更新到 optimizer的param_groups属性之中,最后记录下最后一次调整的学习率到self._last_lr,此属性将在get_last_lr()方法中返回。在这个方法中用到了上下文管理功能的内部类 _enable_get_lr_call,实例对象添加了_get_lr_called_within_step属性,这个属性可在子类中使用。此外,需要注意的是,step方法中的参数epoch已经废弃了,在使用时可以直接忽略这个参数。

def step(self, epoch=None):# Raise a warning if old pattern is detected# https://github.com/pytorch/pytorch/issues/20124if self._step_count == 1:if not hasattr(self.optimizer.step, "_with_counter"):warnings.warn("...") # 移除了警告信息# Just check if there were two first lr_scheduler.step() calls before optimizer.step()elif self.optimizer._step_count < 1:warnings.warn("...") # 移除了警告信息self._step_count += 1class _enable_get_lr_call:def __init__(self, o):self.o = odef __enter__(self):self.o._get_lr_called_within_step = Truereturn selfdef __exit__(self, type, value, traceback):self.o._get_lr_called_within_step = Falsewith _enable_get_lr_call(self):if epoch is None:self.last_epoch += 1values = self.get_lr()else:warnings.warn(EPOCH_DEPRECATION_WARNING, UserWarning)self.last_epoch = epochif hasattr(self, "_get_closed_form_lr"):values = self._get_closed_form_lr()else:values = self.get_lr()for i, data in enumerate(zip(self.optimizer.param_groups, values)):param_group, lr = dataparam_group['lr'] = lrself.print_lr(self.verbose, i, lr, epoch)self._last_lr = [group['lr'] for group in self.optimizer.param_groups]
2.1.3 get_last_lr、get_lr和print_lr

get_last_lr()方法比较简单,就是step()方法调用后,记录的最后一次 optimizer各参数组里更新后的学习率信息
get_lr() 方法是抽象方法,定义了更新学习率策略的接口,不同子类继承后会有不同的实现.其返回值是[lr1, lr2, …]结构
print_lr(is_verbose, group, lr, epoch=None)): 该方法提供了显示 lr 调整信息的功能

def get_last_lr(self):""" Return last computed learning rate by current scheduler."""return self._last_lrdef get_lr(self):# Compute learning rate using chainable form of the schedulerraise NotImplementedErrordef print_lr(self, is_verbose, group, lr, epoch=None):"""Display the current learning rate."""if is_verbose:if epoch is None:print('Adjusting learning rate'' of group {} to {:.4e}.'.format(group, lr))else:print('Epoch {:5d}: adjusting learning rate'' of group {} to {:.4e}.'.format(epoch, group, lr))
2.1.4 state_dict 和 load_state_dict

这两个方法和Optimizer里的方法功能是一样的,就是为了保存和重新加载状态信息,需要注意的是,这里不会重复记录self.optimizer属性的状态信息,因为 Optimizer 有自己实现的对应方法。

state_dict(): 以字典 dict 形式返回当前实例除 self.optimizer 之外的其他所有属性信息
load_state_dict(state_dict): 重新载入之前保存的状态信息

def state_dict(self):"""Returns the state of the scheduler as a :class:`dict`.It contains an entry for every variable in self.__dict__ whichis not the optimizer."""return {key: value for key, value in self.__dict__.items() if key != 'optimizer'}def load_state_dict(self, state_dict):"""Loads the schedulers state.Arguments:state_dict (dict): scheduler state. Should be an object returnedfrom a call to :meth:`state_dict`."""self.__dict__.update(state_dict)

参考:
https://zhuanlan.zhihu.com/p/346205754?utm_medium=social&utm_oi=73844937195520&utm_id=0
https://zhuanlan.zhihu.com/p/539642125

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/181944.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Seata简介与常用模式解决方案概述

Seata 是什么? Seata 是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。 Seata事务管理中有三个重要的角色&#xff1a; TC (Transaction Coordinator) - 事务协调者&#xff1a;维护全局和分支事务的状态&#xff0c;协调全局事务提…

挑战传统IT:RPA以更低的成本和更高的效率领跑数字化转型

在企业数字化进程中&#xff0c;传统的IT解决方案往往带来高成本和低效率的问题。因此&#xff0c;如何顺利地、平稳地进行数字化转型对企业来说是核心考虑。 为此&#xff0c;本文将深入探讨RPA&#xff08;Robotic Process Automation&#xff09;如何以其独特的优势&#xf…

记一次mysql 3306端口映射到外网 frp

通过下面命令启动被访问机器的frp nohup ./frps -c ./frps.toml & 记一次mysql 3306端口映射到外网 坑 mysql本身没有配置远程访问 frp配置错误&#xff0c;没注意中文单引号和英文单引号的区别 mysql本身没有配置远程访问 问题 navacat 远程链接mysql 出现 Lost c…

Camunda 7.x 系列【58】自定义表单设计器

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 2.7.9 本系列Camunda 版本 7.19.0 源码地址:https://gitee.com/pearl-organization/camunda-study-demo 文章目录 1. 前言2.表单设计器3. 案例演示3.1 引入流程设计器3.2 表单数据存储3.3 测试1. 前言 Camu…

牛客 算法题 golang语言实现

题目 HJ101 输入整型数组和排序标识&#xff0c;对其元素按照升序或降序进行排序 描述 输入整型数组和排序标识&#xff0c;对其元素按照升序或降序进行排序数据范围&#xff1a; 1 ≤ &#xfffd; ≤ 10001≤n≤1000 &#xff0c;元素大小满足 0 ≤ &#xfffd; &#…

CONTROLLING VISION-LANGUAGE MODELS FOR MULTI-TASK IMAGE RESTORATION

CONTROLLING VISION-LANGUAGE MODELS FOR MULTI-TASK IMAGE RESTORATION (Paper reading) Ziwei Luo, Uppsala University, ICLR under review(6663), Cited:None, Stars: 350, Code, Paper. 1. 前言 像CLIP这样的视觉语言模型已经显示出对零样本或无标签预测的各种下游任务…

AutoDIR: Automatic All-in-One Image Restoration with Latent Diffusion

AutoDIR: Automatic All-in-One Image Restoration with Latent Diffusion (Paper reading) Yitong Jiang, The Chinese University of Hong Kong, arXiv23, Code, Paper 1. 前言 我们提出了一种具有潜在扩散的一体化图像恢复系统&#xff0c;名为AutoDIR&#xff0c;它可以…

sql23(Leetcode2356每位教师所教授的科目种类的数量)

代码&#xff1a; # Write your MySQL query statement below select teacher_id,count(distinct(subject_id)) as cnt from Teacher group by teacher_id

YoloV7改进策略:RefConv打造轻量化YoloV7利器

文章目录 摘要论文:《RefConv: 重参数化的重聚焦卷积》1、简介2、相关研究2.1、用于更好性能的架构设计2.2、结构重参数化2.3、权重重参数化方法3、重参数化的重聚焦卷积3.1、深度RefConv3.2、普通的RefConv3.3、重聚焦学习4、实验4.1、在ImageNet上的性能评估4.2、与其他重参…

西南科技大学信号与系统A实验三(线性连续时间系统的分析)

一、实验目的 1.掌握用 matlab 分析系统时间响应的方法 2.掌握用 matlab 分析系统频率响应的方法 3.掌握系统零、极点分布与系统稳定性关系 二、实验原理 1. 系统函数 H(s) 系统函数:系统零状态响应的拉氏变换与激励的拉氏变换之比. H(s)=R(s)/E(s) 在 matlab 中可采用…

GZ031 应用软件系统开发赛题第10套

2023年全国职业院校技能大赛 应用软件系统开发赛项&#xff08;高职组&#xff09; 赛题第10套 工位号&#xff1a; 2023年4月 竞赛说明 一、项目背景 党的二十大报告指出&#xff0c;要加快建设制造强国、数字中国&#xff0c;推动制造业高端化、智能化、…

图片伪装,将RAR文件隐藏到图片里

下载链接 效果图&#xff1a; 代码&#xff1a; ECHO OFF TITLE PtoR MODE con COLS55 LINES25 color 0A:main cls echo.当前时间&#xff1a;%date% %time% echo.欢迎使用图片伪装&#xff0c;本脚本可以将RAR文件隐藏到图片里. echo.set /p "imagefile①请拖入图像文件…

数据仓库数据管理模型

数据仓库分为贴源层、数据仓库层、数据服务层&#xff0c;有人叫做数仓数据模型&#xff0c;或者叫"数据管理模型”。 我们为什么要进行数据分层管理&#xff0c;下图的优点介绍已经说得比较明确&#xff0c;再补充几点&#xff1a; 保障数据一致性&#xff1a;上层的数…

最简单的Python程序员编辑器——学习Python的第二篇

第二章 基础 安装python 按照上一期的教程安装&#xff0c;如果有什么问题可以提出阿莱 安装完之后&#xff0c;在电脑的程序中查找SHELL 这里是Win10&#xff0c;win11你也是到程序菜单查找。 你可以把把它点击右键&#xff0c;保存到桌面上&#xff0c;或者固定到开始菜单…

数组元素积的符号

数组元素积的符号 描述 : 已知函数 signFunc(x) 将会根据 x 的正负返回特定值&#xff1a; 如果 x 是正数&#xff0c;返回 1 。如果 x 是负数&#xff0c;返回 -1 。如果 x 是等于 0 &#xff0c;返回 0 。 给你一个整数数组 nums 。令 product 为数组 nums 中所有元素值的…

leetcode 283. 移动零

代码&#xff1a; class Solution {public void swap(int[] nums,int m,int n){int tmpnums[m];nums[m]nums[n];nums[n]tmp;}public void moveZeroes(int[] nums) {int cur0;int dest-1;int nnums.length;for(;cur<n;cur){if(nums[cur]!0){dest;swap(nums,cur,dest);}}} } …

数字系列——数字经济(2)​

上次呢&#xff0c;已经为大家捋了什么是数字经济&#xff1f;、数字经济的特点有哪些&#xff1f;和数字经济的构成&#xff0c;对于数字经济有了基础性的了解&#xff0c;今天继续为大家捋一捋。 数字经济的发展 1.互联网的普及 互联网作为数字经济的坚实基础&#xff0c;其…

C++中的unique函数

一、头文件 包括在#include <algorithm>下 二、用法与作用 1.函数作用&#xff1a;“去除”容器或数组中相邻元素之间重复出现的元素&#xff08;所以一般使用前需要排序&#xff09;。 2.函数参数&#xff1a;第一个参数是集合的起始地址&#xff0c;第二个参数是集…

香港身份、香港永居身份、香港护照区别,三种证件之间是什么关系?

香港身份、香港永居身份、香港护照区别&#xff0c;三种证件之间是什么关系&#xff1f; 在港“通常性”住满7年之后&#xff0c;可以申请永居身份&#xff01; 香港身份&#xff1a;也可以称之为临时身份&#xff0c;无论通过香港优才计划、高才通计划、专才计划或者留学拿身份…

Keras 早停、获取预测值和真实值数据

诸神缄默不语-个人CSDN博文目录 1. 早停 early_stopping_callback EarlyStopping(monitorval_loss, patienceepochs_to_wait_for_improve) 这个是叠callback的写法&#xff1a; early_stopping_callback EarlyStopping(monitorval_loss, patienceepochs_to_wait_for_impr…