| @@ -13,13 +13,15 @@ | |||
| # limitations under the License. | |||
| # ============================================================================ | |||
| """FTRL""" | |||
| # 本文件为FTRL优化器的构建 | |||
| from mindspore.ops import functional as F, composite as C, operations as P | |||
| from mindspore.common import Tensor | |||
| import mindspore.common.dtype as mstype | |||
| from mindspore._checkparam import Validator as validator | |||
| from mindspore._checkparam import Rel | |||
| from .optimizer import Optimizer, _apply_decay, _grad_scale | |||
| from.optimizer import Optimizer, _apply_decay, _grad_scale | |||
| # 定义_ftrl_opt函数,用于接收四个参数:opt(优化器),spars_opt(模式优化器),push(推送),pull(拉取),l1(L1正则化),l2(L2正则化),lr_power(学习率指数),learning_rate(学习率),linear(线性),gradient(梯度),weight(权重),moment(动量),ps_parameter(参数),cache_enable(是否使用缓存) | |||
| _ftrl_opt = C.MultitypeFuncGraph("ftrl_opt") | |||
| @@ -28,14 +30,23 @@ _ftrl_opt = C.MultitypeFuncGraph("ftrl_opt") | |||
| def _tensor_run_opt_with_sparse(opt, spars_opt, push, pull, l1, l2, lr_power, learning_rate, linear, | |||
| gradient, weight, moment, ps_parameter, cache_enable): | |||
| """Apply sparse ftrl optimizer to the weight parameter when the gradient is sparse.""" | |||
| # 对为稀疏矩阵的参数权重使用FTRL优化器 | |||
| # 判断是否满足条件 | |||
| success = True | |||
| # 获取梯度的索引和值 | |||
| indices = gradient.indices | |||
| values = gradient.values | |||
| # 如果没有指定ps参数,且不满足cache_enable条件 | |||
| if ps_parameter and not cache_enable: | |||
| # 获取操作的形状 | |||
| op_shape = P.Shape() | |||
| # 获取形状 | |||
| shapes = (op_shape(weight), op_shape(moment), op_shape(linear), op_shape(values), op_shape(indices)) | |||
| # 执行pull操作 | |||
| success = F.depend(success, pull(push((values, indices), shapes), weight)) | |||
| # 如果指定了ps参数,且满足cache_enable条件 | |||
| else: | |||
| # 执行spars_opt操作 | |||
| success = F.depend(success, spars_opt(weight, moment, linear, values, indices)) | |||
| return success | |||
| @@ -44,35 +55,52 @@ def _tensor_run_opt_with_sparse(opt, spars_opt, push, pull, l1, l2, lr_power, le | |||
| "Tensor", "Tensor", "Tensor", "Bool", "Bool") | |||
| def _tensor_run_opt(opt, spars_opt, push, pull, l1, l2, lr_power, learning_rate, linear, | |||
| gradient, weight, moment, ps_parameter, cache_enable): | |||
| # 对权重参数应用FTRL优化器 | |||
| """Apply ftrl optimizer to the weight parameter.""" | |||
| success = True | |||
| # 如果ps_parameter为True,且不支持缓存,则使用pull函数拉取push函数的输出 | |||
| if ps_parameter and not cache_enable: | |||
| op_shape = P.Shape() | |||
| success = F.depend(success, pull(push((gradient, learning_rate, l1, l2, lr_power), | |||
| (op_shape(weight), op_shape(moment), op_shape(linear))), weight)) | |||
| # 否则,使用opt函数计算weight的梯度,并使用push函数推送梯度 | |||
| else: | |||
| success = F.depend(success, opt(weight, moment, linear, gradient, learning_rate, l1, l2, lr_power)) | |||
| # 返回更新成功标志 | |||
| return success | |||
| def _check_param(initial_accum, lr_power, l1, l2, use_locking, prim_name=None): | |||
| """Check param.""" | |||
| """ | |||
| 检查参数 | |||
| """ | |||
| # 检查initial_accum的类型是否为float | |||
| validator.check_value_type("initial_accum", initial_accum, [float], prim_name) | |||
| # 检查initial_accum的值是否在0.0以上 | |||
| validator.check_number("initial_accum", initial_accum, 0.0, Rel.GE, prim_name) | |||
| # 检查lr_power的类型是否为float | |||
| validator.check_value_type("lr_power", lr_power, [float], prim_name) | |||
| # 检查lr_power的值是否在0.0和Rel.LE之间 | |||
| validator.check_number("lr_power", lr_power, 0.0, Rel.LE, prim_name) | |||
| # 检查l1的类型是否为float | |||
| validator.check_value_type("l1", l1, [float], prim_name) | |||
| # 检查l1的值是否在0.0和Rel.GE之间 | |||
| validator.check_number("l1", l1, 0.0, Rel.GE, prim_name) | |||
| # 检查l2的类型是否为float | |||
| validator.check_value_type("l2", l2, [float], prim_name) | |||
| # 检查l2的值是否在0.0和Rel.GE之间 | |||
| validator.check_number("l2", l2, 0.0, Rel.GE, prim_name) | |||
| # 检查use_locking的类型是否为bool | |||
| validator.check_value_type("use_locking", use_locking, [bool], prim_name) | |||
| class FTRL(Optimizer): | |||
| # FTRL优化器 | |||
| r""" | |||
| Implements the FTRL algorithm with ApplyFtrl Operator. | |||
| @@ -193,59 +221,89 @@ class FTRL(Optimizer): | |||
| """ | |||
| def __init__(self, params, initial_accum=0.1, learning_rate=0.001, lr_power=-0.5, l1=0.0, l2=0.0, | |||
| use_locking=False, loss_scale=1.0, weight_decay=0.0): | |||
| # 初始化FTRL类 | |||
| super(FTRL, self).__init__(learning_rate, params, weight_decay, loss_scale=loss_scale) | |||
| # 检查参数 | |||
| if self.dynamic_lr or self.is_group_lr: | |||
| raise ValueError('Dynamic learning rate or group learning rate is currently not supported.') | |||
| _check_param(initial_accum, lr_power, l1, l2, use_locking, self.cls_name) | |||
| # 初始化参数 | |||
| self.moments = self.parameters.clone(prefix="moments", init=initial_accum) | |||
| self.linear = self.parameters.clone(prefix="linear", init='zeros') | |||
| self.l1 = l1 | |||
| self.l2 = l2 | |||
| self.lr = learning_rate | |||
| self.lr_power = lr_power | |||
| # 判断是否是分组 | |||
| if not self.is_group: | |||
| # 如果不是组,则将decay_flags设置为True | |||
| self.decay_flags = tuple((lambda: True)() for x in self.parameters) | |||
| # 初始化优化器 | |||
| self.hyper_map = C.HyperMap() | |||
| self.opt = P.ApplyFtrl(use_locking=use_locking) | |||
| self.use_locking = use_locking | |||
| # 初始化sparse优化器 | |||
| self.sparse_opt = P.SparseApplyFtrl(learning_rate, l1, l2, lr_power, use_locking=use_locking) | |||
| # 初始化sparse_opt | |||
| self._ps_pull = P.Pull() | |||
| # 初始化_ps_pull | |||
| self._ps_push = P.Push("Ftrl", [0, 1, 2]) | |||
| # 初始化_ps_push | |||
| self._ps_push.add_prim_attr("init_accum", initial_accum) | |||
| # 向_ps_push中添加init_accum属性 | |||
| self._ps_push.add_prim_attr("lr", learning_rate) | |||
| # 向_ps_push中添加lr属性 | |||
| self._ps_push.add_prim_attr("l1", l1) | |||
| # 向_ps_push中添加l1属性 | |||
| self._ps_push.add_prim_attr("l2", l2) | |||
| # 向_ps_push中添加l2属性 | |||
| self._ps_push.add_prim_attr("lr_power", lr_power) | |||
| def construct(self, grads): | |||
| ''' | |||
| 构建FTRL优化器 | |||
| :param grads: 梯度 | |||
| :return: 更新后的参数 | |||
| ''' | |||
| params = self.parameters | |||
| moments = self.moments | |||
| linear = self.linear | |||
| # 对梯度权重衰减 | |||
| grads = self.decay_weight(grads) | |||
| # 对梯度中心化 | |||
| grads = self.gradients_centralization(grads) | |||
| # 对梯度进行缩放 | |||
| grads = self.scale_grad(grads) | |||
| # 对梯度进行去重 | |||
| grads = self._grad_sparse_indices_deduplicate(grads) | |||
| # 更新参数 | |||
| lr = self.get_lr() | |||
| # 将_ftrl_opt函数放入参数 | |||
| success = self.map_(F.partial(_ftrl_opt, self.opt, self.sparse_opt, self._ps_push, self._ps_pull, | |||
| self.l1, self.l2, self.lr_power, lr), | |||
| linear, grads, params, moments, self.ps_parameters, self.cache_enable) | |||
| self.l1, self.l2, self.lr_power, lr), | |||
| linear, grads, params, moments, self.ps_parameters, self.cache_enable) | |||
| return success | |||
| @Optimizer.target.setter | |||
| # 优化器target设置 | |||
| def target(self, value): | |||
| """If the input value is set to "CPU", the parameters will be updated on the host using the Fused | |||
| optimizer operation.""" | |||
| # 如果value不为str类,则抛出TypeError异常 | |||
| if not isinstance(value, str): | |||
| raise TypeError("The value must be str type, but got value type is {}".format(type(value))) | |||
| # 如果value值不在三者之中,则抛出ValueError异常 | |||
| if value not in ('CPU', 'Ascend', 'GPU'): | |||
| raise ValueError("The value must be 'CPU', 'Ascend' or 'GPU', but got value {}".format(value)) | |||
| if value == 'CPU': | |||
| # 如果输入值为CPU,则使用FusedSparseFtrl优化器更新参数 | |||
| self.sparse_opt = P.FusedSparseFtrl(self.lr, self.l1, self.l2, self.lr_power, self.use_locking) | |||
| self.sparse_opt.add_prim_attr("primitive_target", "CPU") | |||
| else: | |||
| # 如果输入值为GPU,则使用SparseApplyFtrl优化器更新参数 | |||
| self.sparse_opt = P.SparseApplyFtrl(self.lr, self.l1, self.l2, self.lr_power, self.use_locking) | |||
| self._target = value | |||
| # 设置目标值 | |||
| self._target = value | |||