LiteFlow决策系统的策略模式,顺序、最坏、投票、权重

news2024/12/25 3:39:21

个人博客:无奈何杨(wnhyang)

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview


image

想必大家都有听过或做过职业和性格测试吧,尤其是现在的毕业生,在投了简历之后经常会收到一个什么测评,那些测评真的是又臭又长,做的简直让人崩溃,很多时候都是边骂边做,都什么玩意!?

然而,本篇就由此出发,把整个测评作为一个策略的话,其中每一项都是一条规则,通常每一条规则(问答)需要我们输入一个类似1-9的分数,1和9分别代表两个极端,最终这个策略会结合所有的问答结果计算出我们的性格/职业。这是如何做的呢?其实就是一种分类算法,就拿二维平面直角坐标系举例吧!

如下二维平面直角坐标系下分出了4个区域,性格/职业测评的每条问答可以理解为其中一条经过原点的直线,1-9分别指示两个方向,你的答案最终会是一个由原点出发的n条直线,这n条直线可以绘成一个多边形,而这个多边形就构成了最终结果,长得有点类似雷达图。

image

image

image

当然这只是二维平面直角坐标系的例子,实际上现实往往比这个更复杂,高于三维的我也举不出例子啊🙂‍↔️

总之最后结果绝大多数情况下都会是一个不规则的东西(我实在不知道更高维的该怎么描述),这种测评会取出凸点作为我们的倾向性格/职业。

好吧,关于文章开篇就到这里了,下面就可以正式开始了。不过我还是想讲一个题外话,小时候接触的数学函数(方程)可以很轻易的表示在二维直角坐标系下,随着对于数学的深入探索,出现了越来越多的奇奇怪怪的字母和方程,有人也讲“数学的尽头是字母”🤔然而当我们换一个坐标系,这些是不是也会变个模样呢?所以说有时候换个角度看问题就会有不同收获,或者说换个角度问题就会迎刃而解。

策略

策略组件大致实现如下,编排时会使用p_cn.tag("code"),运行FOR(p_fn).DO(r_cn).BREAK(p_bn);FOR(p_fn).parallel(true).DO(r_cn);,前者适用于顺序模式,其他皆适用于后者。

@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = LFUtil.POLICY_COMMON_NODE, nodeType = NodeTypeEnum.COMMON, nodeName = "策略普通组件")
public void policy(NodeComponent bindCmp) {
    PolicyContext policyContext = bindCmp.getContextBean(PolicyContext.class);
    PolicyContext.PolicyCtx policy = PolicyConvert.INSTANCE.convert2Ctx(policyMapper.selectByCode(bindCmp.getTag()));
    policyContext.addPolicy(policy.getCode(), policy);

    log.info("当前策略(code:{}, name:{}, code:{})", policy.getCode(), policy.getName(), policy.getCode());

    if (PolicyMode.ORDER.equals(policy.getMode())) {
        bindCmp.invoke2Resp(LFUtil.P_F, policy.getCode());
    } else {
        bindCmp.invoke2Resp(LFUtil.P_FP, policy.getCode());
    }
}

循环次数组件p_fn如下,查询策略下的所有规则(规则还没做版本控制,后面再改),返回规则列表大小。

@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_FOR, nodeId = LFUtil.POLICY_FOR_NODE, nodeType = NodeTypeEnum.FOR, nodeName = "策略for组件")
public int policyFor(NodeComponent bindCmp) {
    PolicyContext policyContext = bindCmp.getContextBean(PolicyContext.class);
    String policyCode = bindCmp.getSubChainReqData();
    List<PolicyContext.RuleCtx> ruleList = RuleConvert.INSTANCE.convert2Ctx(ruleMapper.selectByPolicyCode(policyCode));
    policyContext.addRuleList(policyCode, ruleList);
    return ruleList.size();
}

循环中断组件p_bn如下,当策略上下文中有命中风险规则时就可以停止循环了。

@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_BOOLEAN, nodeId = LFUtil.POLICY_BREAK_NODE, nodeType = NodeTypeEnum.BOOLEAN, nodeName = "策略break组件")
public boolean policyBreak(NodeComponent bindCmp) {
    PolicyContext policyContext = bindCmp.getContextBean(PolicyContext.class);
    String policyCode = bindCmp.getSubChainReqData();
    return policyContext.isHitRisk(policyCode);
}

另外在使用异步循环编排时需要注意并发操作问题,尤其是对上下文的操作。

剩下的规则组件r_cn和策略上下文PolicyContext请往下看。

顺序:按部就班、循序渐进

顺序模式是最好理解,就是顺序运行策略下的所有规则,默认在第一条设定的风险规则触发后结束,其实更准确的叫法应该是首次。如下表在顺序模式下执行,到规则2就结束了,因为默认pass之外的才是风险规则。

规则是否命中处置方式
1truepass
2truereject
3falsesms
4truereview

最坏:未雨绸缪,防患未然

与顺序模式不同,需要执行所有的规则,综合最坏的作为结果。如下表在最坏模式下,最终结果是reject(因为reject>review>pass,这个是配置的)。

规则是否命中处置方式
1truepass
2truereject
3falsesms
4truereview

投票:集体智慧,共同决策

同上,需要执行完所有规则,以命中规则的结果最多的作为最终结果。如下表在投票模式下,结果是pass

规则是否命中处置方式
1truepass
2truereject
3falsereview
4truepass

可以使用这样的计数器,但是考虑到策略集下有不一样的策略集,想必还要再包一层Map<String,ConcurrentHashMap<String, AtomicInteger>>,以策略code作为键。

private final ConcurrentHashMap<String, AtomicInteger> counters = new ConcurrentHashMap<>();

/**
 * 增加指定 key 的计数值。
 * 如果 key 不存在,则初始化为 1;如果存在,则将当前值加 1。
 */
public void increment(String key) {
    // 使用 computeIfAbsent 方法来确保只在第一次遇到该 key 时创建新的 AtomicInteger
    counters.computeIfAbsent(key, k -> new AtomicInteger(0)).incrementAndGet();
}

/**
 * 获取指定 key 的当前计数值。
 * 如果 key 不存在,则返回 0。
 */
public int get(String key) {
    // 获取指定 key 的 AtomicInteger,并调用 get() 方法获取其值
    AtomicInteger counter = counters.get(key);
    return (counter != null) ? counter.get() : 0;
}

本来考虑的是在规则True组件中根据策略不同做不同的事情,但后来放弃了,还是统一放在上下文中吧,且往下看。

image

权重:量化评估,科学分配

一样,需要运行完所有规则,综合权重模式阈值配置得出最终结论。如下表在权重模式下,结果是23+21+20=64,注意!!!这里只是得到一个数字,在策略设置为权重模式后额外还需要配置一个阈值表,拿这个数字去匹配对应的阈值区间得出最终结论。当然这只是个最简单例子,下面将展开,讨论其丰富的应用场景和更灵活的使用方法。

规则是否命中得分
1true23
2true21
3false30
4true20

阈值配置表

得分结果
(-214,20]pass
(20,45]review
(45,70]sms
(70,900)reject

设计过程

阶段一:规则增加简单的分数属性,命中时累计就好,如:规则1命中时+10,规则2命中时-2,这样最为简单,也因此适用场景最少,最不灵活。

阶段二:固定公式计算,如下规则附加这些属性,在规则命中时计算一下,这种只适合简单的线性相关的规则计算。

/**
 * 计算公式为:base + al aOpType(加/减/乘/除) ${value}(取决于opType类型,是指标还是字段),结果范围[lowerLimit,upperLimit]
 *
 * @author wnhyang
 * @date 2024/12/9
 **/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Weight {

    private Double base; // 基础权重
    private Double al; // 权重调整因子
    private String aOpType; // 操作类型:加/减/乘/除
    private String opType; // 指标还是字段
    private String value; // 指标名称或字段名称
    private Double upperLimit; // 上限
    private Double lowerLimit; // 下限

    /**
     * 计算最终权重
     *
     * @param trueValue 实际值,当 opType 为 "zb" (指标) 时使用
     * @return 最终计算出的权重
     */
    public double compute(double trueValue) {
        if (al == null || trueValue == 0 && ("/".equals(aOpType))) {
            throw new IllegalArgumentException("Invalid parameters for computation.");
        }

        double adjustment = 0;
        switch (aOpType) {
            case "+":
                adjustment = al + trueValue;
                break;
            case "-":
                adjustment = al - trueValue;
                break;
            case "*":
                adjustment = al * trueValue;
                break;
            case "/":
                adjustment = al / trueValue;
                break;
            default:
                throw new UnsupportedOperationException("Unsupported operation type: " + aOpType);
        }

        double result = base + adjustment;
        return Math.min(upperLimit, Math.max(lowerLimit, result));
    }

    public static void main(String[] args) {
        Weight weight = new Weight(10.41, -2.154, "*", "zb", "count", 5000.545, -56.654);
        double compute = weight.compute(25.21);
        System.out.println("Computed weight: " + compute);
    }
}

阶段三:灵活公式,使用QLExpress实现。如下讲计算公式作为规则的一个属性,通过getOutVarNames获取需要用到的外部变量名,在运行表达式之前通过LiteFlow上下文取值塞到QLExpress的上下文中。

当然还有可以优化的地方,1、设计上下文时实现QLExpressIExpressContext接口,也就不用获取后在塞,直接拿LiteFlow上下文作为QLExpress上下文用就行;2、还有就是min(upperLimit, max({}, lowerLimit))是否要放在表达式中,其实也是没必要,可以放在表达式计算完成之后嘛。3、是否要计算平均值,现在是权重之和,是否要做加权平均呢?4、等等

@Test
public void test3() throws Exception {
    ExpressRunner runner = new ExpressRunner();
    DefaultContext<String, Object> context = new DefaultContext<>();
    String fun = "base + al * value";
    String express = StrUtil.format("min(upperLimit, max({}, lowerLimit))", fun);
    log.info(express);
    String[] outVarNames = runner.getOutVarNames(express);
    log.info(Arrays.toString(outVarNames));
    context.put("base", 45.434);
    context.put("al", 3.352);
    context.put("value", 24.3264);
    context.put("lowerLimit", -35.342);
    context.put("upperLimit", 3463.57);
    Object r = runner.execute(express, context, null, true, false);
    log.info("{}", r);
}

注意点

对于像这种可输入的、脚本类的、在系统中运行的,一定要做好安全性校验,避免直接操作系统资源,稍微不注意控制就会有安全漏洞。在保证安全的前提下,再考虑如何优化用户使用体验,如用户需要使用一些系统字段时,在编辑器文本域输入特殊字符(像“@”或“#”),监听到输入后显示候选列表,可以关键词匹配并选择需要的字段,一旦选中,这个将作为一个整体,只能整体操作,就像我们在发邮件,或者聊天时输入“@”一样,另外再做一个内置运算符的提示符,这样编辑公式就更加便捷,且能降低出错率。再进一步就是做一个常用公式库,提示列表中有直接选中就行,剩下的就是填充需要的字段就行。

image

应用场景

比如在做密码登录时,设置了两条规则,一条正向规则y1=f(x1)x1表示最近人脸登录成功次数,其与结果负相关,人脸登录成功次数越多得到的负数越大;一条反向规则y2=f(x2)x2表示最近密码登录失败次数,其与结果正相关,密码登录失败次数越多得到的分数越大,而且保证其“增长率”大于f(x1)

可以大致表示为下面的曲线,最近人脸登录成功次数少时,风险高一些,多时也会存在上限,因为再多也没有意义了;最新密码登录错误次数少时风险低,但密码登录次数越多风险急剧增加,这样的话在整合y=y1+y2=f(x1)+f(x2)后,风险受密码登录错误次数的影响更大。

image

当然将两个公式整合到一块做为一个规则也是可以的,差别就是是否需要独立的规则条件。

条件公式
不合并condition1f(x1)
condition2f(x2)
合并condition1f(x1)+f(x2)

还有就是在信贷计算信用时,需要计算收入稳定性+信用历史+就业情况+债务水平+资产情况的场景时,当然这依赖多方数据,而且一般的信用评估不是简单的规则配置能解决的。

模型:智能学习,进化升级

最终都将到这一步的,虽然现在做的项目中还没有集成模型,但是我之后一定会做。先立flag嘛,实现不是实现另说吧🧐可别连立flag的勇气都没有了!

关于模型我也不是专业的,也是仅有一点点了解。我一直认为学习能力、看待、解决问题的思想是最重要的,特别是我看了几个关于机器学习的视频后,虽然其中很多的公式我都不懂,但是能理解到其看待、解决问题的思路方法,很受益,很有启发。

规则

以下分别是规则组件(包含isAccess实现)、命中规则、未命中规则组件。在规则命中时额外计算规则配置的权重表达式。

@LiteflowMethod(value = LiteFlowMethodEnum.IS_ACCESS, nodeId = LFUtil.RULE_COMMON_NODE, nodeType = NodeTypeEnum.COMMON)
public boolean ruleAccess(NodeComponent bindCmp) {
    String policyCode = bindCmp.getSubChainReqData();
    int index = bindCmp.getLoopIndex();
    PolicyContext policyContext = bindCmp.getContextBean(PolicyContext.class);
    PolicyContext.RuleCtx rule = policyContext.getRule(policyCode, index);
    return !RuleStatus.OFF.equals(rule.getStatus());
}

@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = LFUtil.RULE_COMMON_NODE, nodeType = NodeTypeEnum.COMMON, nodeName = "规则普通组件")
public void rulProcess(NodeComponent bindCmp) {
    String policyCode = bindCmp.getSubChainReqData();
    int index = bindCmp.getLoopIndex();
    PolicyContext policyContext = bindCmp.getContextBean(PolicyContext.class);
    PolicyContext.RuleCtx rule = policyContext.getRule(policyCode, index);
    bindCmp.invoke2Resp(StrUtil.format(LFUtil.RULE_CHAIN, rule.getCode()), rule);
}

@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = LFUtil.RULE_TRUE, nodeType = NodeTypeEnum.COMMON, nodeName = "规则true组件")
public void ruleTrue(NodeComponent bindCmp) {
    PolicyContext policyContext = bindCmp.getContextBean(PolicyContext.class);
    PolicyContext.RuleCtx rule = bindCmp.getSubChainReqData();
    log.info("命中规则(name:{}, code:{})", rule.getName(), rule.getCode());
    if (RuleStatus.MOCK.equals(rule.getStatus())) {
        policyContext.addHitMockRuleVO(rule.getPolicyCode(), rule);
    } else {
        // 权重
        if (PolicyMode.WEIGHT.equals(policyContext.getPolicy(rule.getPolicyCode()).getMode())) {
            try {
                Double value = (Double) QLExpressUtil.execute(rule.getExpress(), bindCmp.getContextBean(FieldContext.class));
                rule.setExpressValue(value);
            } catch (Exception e) {
                log.error("规则表达式执行异常", e);
            }
        }
        policyContext.addHitRuleVO(rule.getPolicyCode(), rule);
    }
}

@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = LFUtil.RULE_FALSE, nodeType = NodeTypeEnum.COMMON, nodeName = "规则false组件")
public void ruleFalse(NodeComponent bindCmp) {
    log.info("规则未命中");
}

策略上下文

直接上代码了。

/**
 * @author wnhyang
 * @date 2024/4/3
 **/
public class PolicyContext {

    /**
     * 处置方式集合
     */
    private final Map<String, DisposalCtx> disposalMap = new ConcurrentHashMap<>();

    /**
     * 策略集
     */
    private PolicySetCtx policySet;

    /**
     * 初始化
     *
     * @param disposalCtxList 处置方式集合
     * @param policySet       策略集
     */
    public void init(List<DisposalCtx> disposalCtxList, PolicySetCtx policySet) {
        for (DisposalCtx disposalCtx : disposalCtxList) {
            disposalMap.put(disposalCtx.getCode(), disposalCtx);
        }
        this.policySet = policySet;
    }

    /**
     * 策略集合
     */
    private final Map<String, PolicyCtx> policyMap = new ConcurrentHashMap<>();

    /**
     * 添加策略
     *
     * @param policyCode 策略code
     * @param policy     策略
     */
    public void addPolicy(String policyCode, PolicyCtx policy) {
        policyMap.put(policyCode, policy);
    }

    /**
     * 获取策略
     *
     * @param policyCode 策略code
     * @return 策略
     */
    public PolicyCtx getPolicy(String policyCode) {
        return policyMap.get(policyCode);
    }

    /**
     * 规则集合
     */
    private final Map<String, List<RuleCtx>> ruleListMap = new ConcurrentHashMap<>();

    /**
     * 添加规则集合
     *
     * @param policyCode 策略code
     * @param ruleList   规则列表
     */
    public void addRuleList(String policyCode, List<RuleCtx> ruleList) {
        ruleListMap.put(policyCode, ruleList);
    }

    /**
     * 获取规则
     *
     * @param policyCode 策略code
     * @param index      规则索引
     * @return 规则
     */
    public RuleCtx getRule(String policyCode, int index) {
        return ruleListMap.get(policyCode).get(index);
    }

    /**
     * 命中规则集合
     */
    private final Map<String, List<RuleCtx>> hitRuleListMap = new ConcurrentHashMap<>();

    /**
     * 添加命中规则
     *
     * @param policyCode 策略code
     * @param rule       规则
     */
    public void addHitRuleVO(String policyCode, RuleCtx rule) {
        if (!hitRuleListMap.containsKey(policyCode)) {
            hitRuleListMap.put(policyCode, CollUtil.newArrayList());
        }
        hitRuleListMap.get(policyCode).add(rule);
    }

    /**
     * 是否命中风险规则
     *
     * @param policyCode 策略code
     * @return true/false
     */
    public boolean isHitRisk(String policyCode) {
        if (CollUtil.isNotEmpty(hitRuleListMap.get(policyCode))) {
            for (RuleCtx ruleCtx : hitRuleListMap.get(policyCode)) {
                if (!DisposalConstant.PASS_CODE.equals(ruleCtx.getDisposalCode())) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 命中模拟规则集合
     */
    private final Map<String, List<RuleCtx>> hitMockRuleListMap = new ConcurrentHashMap<>();

    /**
     * 添加命中模拟规则
     *
     * @param policyCode 策略code
     * @param rule       规则
     */
    public void addHitMockRuleVO(String policyCode, RuleCtx rule) {
        if (!hitMockRuleListMap.containsKey(policyCode)) {
            hitMockRuleListMap.put(policyCode, CollUtil.newArrayList());
        }
        hitMockRuleListMap.get(policyCode).add(rule);
    }

    /**
     * 转策略集结果
     *
     * @return 策略集结果
     */
    public PolicySetResult convert() {
        PolicySetResult policySetResult = new PolicySetResult(policySet.getName(), policySet.getCode(), policySet.getChain(), policySet.getVersion());

        for (Map.Entry<String, PolicyCtx> entry : policyMap.entrySet()) {
            PolicyCtx policy = entry.getValue();
            PolicyResult policyResult = new PolicyResult(policy.getName(), policy.getCode(), policy.getMode());

            // 最坏
            String maxDisposalCode = DisposalConstant.PASS_CODE;
            int maxGrade = Integer.MIN_VALUE;
            // 投票
            Map<String, Integer> votes = new HashMap<>();
            // 权重
            double weight = 0.0;
            List<RuleCtx> ruleList = hitRuleListMap.get(policy.getCode());
            if (CollUtil.isNotEmpty(ruleList)) {
                for (RuleCtx rule : ruleList) {
                    if (PolicyMode.VOTE.equals(policy.getMode())) {
                        // 投票
                        votes.put(rule.getDisposalCode(), votes.getOrDefault(rule.getDisposalCode(), 0) + 1);
                    } else if (PolicyMode.WEIGHT.equals(policy.getMode())) {
                        // 权重
                        weight += rule.getExpressValue();
                    }

                    RuleResult ruleResult = new RuleResult(rule.getName(), rule.getCode(), rule.getExpress());

                    // 最坏和顺序
                    DisposalCtx disposal = disposalMap.get(rule.getDisposalCode());
                    if (null != disposal) {
                        ruleResult.setDisposalName(disposal.getName());
                        ruleResult.setDisposalCode(disposal.getCode());
                        if (disposal.getGrade() > maxGrade) {
                            maxGrade = disposal.getGrade();
                            maxDisposalCode = disposal.getCode();
                        }
                    }
                    // 模拟/正式规则区分开
                    if (RuleStatus.MOCK.equals(rule.getStatus())) {
                        policyResult.addMockRuleResult(ruleResult);
                    } else {
                        policyResult.addRuleResult(ruleResult);
                    }
                }
            }
            if (PolicyMode.VOTE.equals(policy.getMode())) {
                String maxVoteDisposalCode = DisposalConstant.PASS_CODE;
                int maxVoteCount = Integer.MIN_VALUE;
                for (Map.Entry<String, Integer> entry1 : votes.entrySet()) {
                    if (entry1.getValue() > maxVoteCount) {
                        maxVoteCount = entry1.getValue();
                        maxVoteDisposalCode = entry1.getKey();
                    }
                }
                policyResult.setDisposalName(disposalMap.get(maxVoteDisposalCode).getName());
                policyResult.setDisposalCode(maxVoteDisposalCode);
            } else if (PolicyMode.WEIGHT.equals(policy.getMode())) {
                List<Th> thList = policy.getThList();
                // 排序
                thList.sort(Comparator.comparing(Th::getScore));
                for (Th th : thList) {
                    if (weight <= th.getScore()) {
                        policyResult.setDisposalName(disposalMap.get(th.getCode()).getName());
                        policyResult.setDisposalCode(th.getCode());
                        break;
                    }
                }
            } else {
                policyResult.setDisposalName(disposalMap.get(maxDisposalCode).getName());
                policyResult.setDisposalCode(maxDisposalCode);
            }
            policySetResult.addPolicyResult(policyResult);
        }
        // TODO 入度大于1?考虑投票、加权平均等方法:不考虑
        policySetResult.setDisposalName(DisposalConstant.PASS_NAME);
        policySetResult.setDisposalCode(DisposalConstant.PASS_CODE);

        return policySetResult;
    }
}

策略集

策略集是用来编排策略的,即前面的策略组件p_cn.tag("code"),从前面已知策略会有结果的,那么编排他们的策略集如何取这个结果呢?

这就要考虑如何设计策略集的编排了,两种情况,入度为1,入度大于1。

如下,图1并行运行策略1、2,并最终都返回到结束节点,这时就要考虑如何处理策略1、2的结果了,投票?加权平均?随机选一个?还是其他什么方法?图2通过分流结束节点只会接收到一个策略,那么此时就不会有冲突,分流到哪个就返回哪个。

当然这块还没想好怎么做,只是一些想法。

image

小结

本来还想分享一下项目进展的,但转眼一看好像写的已经有点多了,那就下次吧!

写在最后

拙作艰辛,字句心血,望诸君垂青,多予支持,不胜感激。


个人博客:无奈何杨(wnhyang)

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview

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

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

相关文章

ubuntu paddle ocr 部署bug问题解决

ubuntu paddle ocr 部署会出现异常报错。 尝试安装以下版本&#xff1a; pip install paddlepaddle2.5.2 -i https://pypi.tuna.tsinghua.edu.cn/simpl ​​​​​​ 助力快速掌握数据集的信息和使用方式。 数据可以如此美好&#xff01;

华为云计算HCIE笔记02

第二章&#xff1a;华为云Stack规划设计 交付总流程 准备工作&#xff1a;了解客户的基本现场&#xff0c;并且对客户的需求有基本的认知。 HLD方案BOQ报价设备采购和设备上架 2.安装部署流程 硬件架构设计 硬件设备选配 设备上架与初始化配置 准备相关资料&#xff08;自动下载…

WWW23-多行为级联|级联图卷积网络的多行为推荐

论文&#xff1a;https://arxiv.org/abs/2303.15720 代码&#xff1a;https://github.com/SS-00-SS/MBCGCN 这篇论文MB-CGCN和上一篇CRGCN是同一个团队的&#xff0c;都是级联的方式。一个用了残差&#xff0c;一个用了特征转换&#xff0c;文章最后有discussion讨论了两者的不…

【day16】Java开发常用API

模块15回顾 在深入探讨模块16之前&#xff0c;让我们回顾一下【day15】中的重点内容&#xff1a; String类&#xff1a; 构造方法&#xff1a;String(), String(String s), String(char[] chars), String(byte[] bytes), String(char[] chars, int offset, int count), String…

MLU运行Stable Diffusion WebUI Forge【flux】

文章目录 一、平台环境准备二、代码下载三、基础环境准备3.1 支援whl包 四、代码修改4.2 组件下载 六、运行效果 FLUX模型是由Black Forest Labs推出的一款文本生成图像的AI模型&#xff0c;具有120亿参数&#xff0c;显著提升了图像生成的质量和多样性‌。FLUX模型包含三个版本…

PPP - NCP协议

NCP协议是一个很多子协议构成的主要取决于网络层封装的什么协议比如 三层协议NCP协议ipIPCPIPv6IPv6CPMPLSMPLSCP NCP能够协商的内容包括&#xff1a; 1.网络层协议&#xff08;ip&#xff1f;ipv6&#xff1f;MPLS&#xff1f;…&#xff09;  2.协商地址 主要了解使用最多…

< Chrome Extension : TamperMonkey > 去禁用网页的鼠标的事件 (水文)

问题描述 在一个视频网站看视频&#xff0c;在播放视频时&#xff0c; 如果当鼠标移到视频外&#xff0c;只要有点击鼠标的动作&#xff0c;视频就会暂停播放&#xff08;包括 Window 下的 其它 tab&#xff09;。有种被打劫完财物&#xff0c;还被凌辱的感觉。 解决方案 使…

网络安全 | 常见的网络攻击类型及防范技巧全解析

网络安全 | 常见的网络攻击类型及防范技巧全解析 一、前言二、常见网络攻击类型2.1 网络扫描2.2 恶意软件攻击2.3 网络钓鱼2.4 拒绝服务攻击&#xff08;DoS/DDoS&#xff09;2.5 中间人攻击2.6 SQL 注入攻击2.7 跨站脚本攻击&#xff08;XSS&#xff09; 三、防范技巧3.1 网络…

CE第七次作业

1. for创建20用户 用户前缀由用户输入 用户初始密码由用户输入 例如&#xff1a;test01,test10 [rootServer ~]# vim add_user20.sh #!/bin/bash read -p "please input usernames prefix:" name_pre read -p "please input users passwd:" p…

了解反向传播算法

目录 一、说明 二、了解反向传播算法 三、定义神经网络模型 3.1 输入层 3.2 隐藏图层 3.3 输出层 四、前向传播和评估 五、反向传播和计算梯度 六、结束语 一、说明 梯度正向和反向传播&#xff0c;是神经网络的重要概念。其中包含的重要技巧是&#xff1a;1&#xff09;复函数…

实力认可 | 通付盾入选《ISC.AI 2024创新能力全景图谱》五项领域

近日&#xff0c;ISC.AI 2024创新能力百强&#xff08;以下简称“创新百强”&#xff09;正式发布《ISC.AI 2024创新能力全景图谱》。该全景图谱是由政企、资本、高校、行业力量共同完成了领域划分、综合创新等标准的制定&#xff0c;整合梳理了参评的300余家数字安全厂商、120…

Web3.0安全开发实践:9个sCrypt智能合约开发的最佳实践

sCrypt是一种基于TypeScript的嵌入式领域特定语言&#xff08;eDSL&#xff09;&#xff0c;专为在比特币链上编写智能合约而设计。sCrypt智能合约使用比特币支持的操作码&#xff0c;可以编译成Bitcoin Script。由此生成的类似汇编的脚本可用作交易中的锁定脚本。 本文将探讨…

新手SEO指南如何快速入门与提升网站排名

内容概要 搜索引擎优化&#xff08;SEO&#xff09;是提高网站可见度和排名的重要手段&#xff0c;尤其对新手来说&#xff0c;掌握其基本概念和实用技巧至关重要。本文将针对新手提供一系列的指导&#xff0c;帮助你快速入门并逐步提升网站排名。 首先&#xff0c;了解SEO的…

【EthIf-14】EthIfGeneral容器配置-02

1.实际EthIfGeneral的配置实例 关闭DET接口开启发送确认中断开启接收中断主周期接收timeout主周期 2. 代码实例参考 阅读此部分代码,搞清楚代码分为几个section,大概瞄一眼就好,不用深究其含义,只需有一个宏观的层次结构的映像即可。 //Appl/GenData/EthIf_Cfg.h #

‘pnpm’ 不是内部或外部命令,也不是可运行的程序或批处理文件。

‘pnpm’ 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。 1.情况: npm -v 和 node -v的都正常就是 pnpm-v 无效 检查环境变量也没看出问题 2.分析 没有正确添加环境变量 3.解决 找到npm的全局安装目录 npm list -g --depth 0这里出现了npm的全局安装…

Docker 部署 SpringBoot VUE项目

是一套基于若依的wms仓库管理系统 一、后端部署 后端地址&#xff1a;https://gitee.com/zccbbg/wms-ruoyi/tree/v1/ 1、用IDEA拉代码&#xff0c;并修改API统一后缀 2、复制一个配置文件 application-dev.yaml&#xff0c;并修改里面的mysql与redis配置 3、将打包的jar上传…

面试基础篇---迭代器,yield, tcp, 等

1.谈谈python的装饰器&#xff0c;迭代器&#xff0c;yield&#xff1f; 迭代器在内存中通常是一次性遍历的&#xff0c;也就是说&#xff0c;一旦遍历完所有元素&#xff0c;它就会停止工作&#xff0c;不可再用。 惰性计算&#xff1a;生成器按需生成数据&#xff0c;即只有在…

【文档搜索引擎】搜索模块的完整实现

调用索引模块&#xff0c;来完成搜索的核心过程 主要步骤 简化版本的逻辑&#xff1a; 分词&#xff1a;针对用户输入的查询词进行分词&#xff08;用户输入的查询词&#xff0c;可能不是一个词&#xff0c;而是一句话&#xff09;触发&#xff1a;拿着每个分词结果&#xf…

帝国cms灵动标签调用相关文章

帝国cms相关文章调用的方法很多&#xff0c;官方默认调用方法是使用标签 [!--other.link--] 那么内容页调用相关文章&#xff0c;什么时候用到灵动标签呢 答案是调用同类型的文章&#xff0c;比如有相同关键词或者相同tags标签的文章 使用灵动标签是如何调用的呢&#xff0…

低空经济的地理信息支撑:构建安全、高效的飞行管理体系

随着无人机等低空飞行器的广泛应用&#xff0c;低空空域管理的重要性日益凸显。地理信息技术作为低空空域管理的重要支撑&#xff0c;对于保障低空经济的健康发展具有不可替代的作用。 地理信息技术在低空空域管理中的作用 地理信息技术在低空空域管理中扮演着关键角色&#x…