Forest详细介绍

前言

这段时间我们公司在开发一个商城,我负责的是产品中心,需求有以下3点:

  1. 创建时需要同步到erp
  2. 同步时需要异步
  3. 同步失败时重试,3次
  4. 3次都失败时推送钉钉给对应的开发人员

其实1、2点都很容易实现,使用异步线程池,或者消息中间件等等都可以实现,第3点的话可以有很多实现方式, 可以在表中加个同步次数的字段,用定时任务去扫描,达到三次就发送,所以说实现的方式有很多种,就看你的选择,而我的选择是Forest,理由就是能提高我的效率,不需要关系内部实现,而且它有很好的集成,还有一点是没用过,想学习新的框架,还有就是它是中文的,学习成本相对来说不高

学习一个新的东西之前问自己三个问题,是什么?为什么?怎么用?下面将引用官方文档来说明

Forest介绍

Forest 是一个开源的 Java HTTP 客户端框架,它能够将 HTTP 的所有请求信息(包括 URL、Header 以及 Body 等信息)绑定到您自定义的 Interface 方法上,能够通过调用本地接口方法的方式发送 HTTP 请求。

为什么使用 Forest?

使用 Forest 就像使用类似 Dubbo 那样的 RPC 框架一样,只需要定义接口,调用接口即可,不必关心具体发送 HTTP 请求的细节。同时将 HTTP 请求信息与业务代码解耦,方便您统一管理大量 HTTP 的 URL、Header 等信息。而请求的调用方完全不必在意 HTTP 的具体内容,即使该 HTTP 请求信息发生变更,大多数情况也不需要修改调用发送请求的代码。

Forest 如何使用?

Forest 不需要您编写具体的 HTTP 调用过程,只需要您定义一个接口,然后通过 Forest 注解将 HTTP 请求的信息添加到接口的方法上即可。请求发送方通过调用您定义的接口便能自动发送请求和接受请求的响应。

Forest 的工作原理

Forest 会将您定义好的接口通过动态代理的方式生成一个具体的实现类,然后组织、验证 HTTP 请求信息,绑定动态数据,转换数据形式,SSL 验证签名,调用后端 HTTP API(httpclient 等 API)执行实际请求,等待响应,失败重试,转换响应数据到 Java 类型等脏活累活都由这动态代理的实现类给包了。 请求发送方调用这个接口时,实际上就是在调用这个干脏活累活的实现类。

Forest 的架构

在这里插入图片描述

Forest的使用

forest有两种使用方式,一种是声明式接口,也就是我们熟悉的注解方式,另一种是编程式接口,本篇文章只介绍声明式使用方式;

依赖导入

<dependency>
    <groupId>com.dtflys.forest</groupId>
    <artifactId>forest-spring-boot-starter</artifactId>
    <version>1.5.26</version>
</dependency>

配置yml

forest:
  backend: okhttp3             # 目前 Forest 支持okhttp3和httpclient两种后端 HTTP API,若不配								置该属性,默认为okhttp3 当然,您也可以改为httpclient
  max-connections: 1000        # 连接池最大连接数(默认为 500)
  max-route-connections: 500   # 每个路由的最大连接数(默认为 500)
  max-request-queue-size: 100  # [自v1.5.22版本起可用] 最大请求等待队列大小
  max-async-thread-size: 300   # [自v1.5.21版本起可用] 最大异步线程数
  max-async-queue-size: 16     # [自v1.5.22版本起可用] 最大异步线程池队列大小
  timeout: 3000                # [已不推荐使用] 请求超时时间,单位为毫秒(默认为 3000)
  connect-timeout: 3000        # 连接超时时间,单位为毫秒(默认为 timeout)
  read-timeout: 3000           # 数据读取超时时间,单位为毫秒(默认为 timeout)
  max-retry-count: 0           # 请求失败后重试次数(默认为 0 次不重试)
  ssl-protocol: TLS            # 单向验证的HTTPS的默认TLS协议(默认为 TLS)
  log-enabled: true            # 打开或关闭日志(默认为 true)
  log-request: true            # 打开/关闭Forest请求日志(默认为 true)
  log-response-status: true    # 打开/关闭Forest响应状态日志(默认为 true)
  log-response-content: true   # 打开/关闭Forest响应内容日志(默认为 false)

请求

请求一、

创建一个interface,并用@Request注解修饰接口方法。

public interface MyClient {

    @Request("http://localhost:8080/hello")
    String simpleRequest();

}

通过@Request注解,将上面的MyClient接口中的simpleRequest()方法绑定了一个 HTTP 请求, 其 URL 为http://localhost:8080/hello ,并默认使用GET方式,且将请求响应的数据以String的方式返回给调用者。

请求二、

public interface MyClient {

    @Request(
            url = "http://localhost:8080/hello/user",
            headers = "Accept: text/plain"
    )
    String sendRequest(@Query("uname") String username);
}

上面的sendRequest方法绑定的 HTTP 请求,定义了 URL 信息,以及把Accept:text/plain加到了请求头中,多个用逗号隔开, 方法的参数String username绑定了注解@Query("uname"),它的作用是将调用者传入入参 username 时,自动将username的值加入到 HTTP 的请求参数uname中。

请求方法

Forest 使用不同的请求注解来标识某个接口方法来进行发送不同类型的请求,其支持的HTTP方法如下表所示:

HTTP 请求方法请求注解描述
GET@Get@GetRequest获取资源
POST@Post@PostRequest传输实体文本
PUT@Put@PutRequest上传资源
HEAD@HeadRequest获取报文首部
DELETE@Delete@DeleteRequest删除资源
OPTIONS@Options@OptionsRequest询问支持的方法
TRACE@Trace@TraceRequest追踪路径
PATCH@Patch@PatchRequest更新资源的某一部分
不定方法@Request可动态传入HTTP方法

URL 参数

可通过{参数序号}来动态 获取参数,也可以在参数中使用@Var注解来标明变量名配合{变量名来获取}

/**
 * 整个完整的URL都通过参数传入
 * {0}代表引用第一个参数
 */
@Get("{0}")
String send1(String myURL);

/**
 * 整个完整的URL都通过 @Var 注解修饰的参数动态传入
 */
@Get("{myURL}")
String send2(@Var("myURL") String myURL);

/**
 * 通过参数转入的值作为URL的一部分
 */
@Get("http://{myURL}/abc")
String send3(@Var("myURL") String myURL);

/**
 * 参数转入的值可以作为URL的任意一部分
 */
@Get("http://localhost:8080/test/{myURL}?a=1&b=2")
String send4(@Var("myURL") String myURL);

数据转换

几乎所有数据格式的转换都包含序列化和反序列化,Forest的数据转换同样如此

序列化是指,将原始的 Java 类型数据对象转化为 HTTP 请求想要发送的数据格式(如:JSONXMLProtobuf

Content-Type 请求头

Forest中对数据进行序列化可以通过指定contentType属性或Content-Type头指定内容格式

@Post(
        url = "http://localhost:8080/hello/user",
        contentType = "application/json"    // 指定contentType为application/json
)
String postJson(@Body MyUser user);   // 自动将user对象序列化为JSON格式

同理,指定为application/xml会将参数序列化为XML格式,text/plain则为文本,默认的application/x-www-form-urlencoded则为表格格式。

请求体类型

或者,也可以通过@BodyType注解指定type属性

// 自动将user对象序列化为JSON格式
// 但此方式不会在请求中带有 Content-Type 请求头
@Post("http://localhost:8080/hello/user")
@BodyType("json")
String postJson(@Body MyUser user);
Encoder
// 指定仅仅使用 Jackson 转换器来序列化数据
@Post("http://localhost:8080/hello/user")
@BodyType(type = "json", encoder = ForestJacksonConverter.class)
String postJson(@Body MyUser user);

提示

在方法不指定 Encoder 的默认情况下,会去找接口上有没有设置 Encoder,如接口上也没有则使用全局的转换器为改方法请求的 Encoder

拦截器(常用)

用过Spring MVC的朋友一定对Spring的拦截器并不陌生,Forest也同样支持针对Forest请求的拦截器。

一、构建拦截器

定义一个拦截器需要实现com.dtflys.forest.interceptor.Interceptor接口

public class SimpleInterceptor<T> implements Interceptor<T> {

    private final static Logger log = LoggerFactory.getLogger(SimpleInterceptor.class);

    /**
     * 该方法在被调用时,并在beforeExecute前被调用 
     * @Param request Forest请求对象
     * @Param args 方法被调用时传入的参数数组 
     */
    @Override
    public void onInvokeMethod(ForestRequest req, ForestMethod method, Object[] args) {
        log.info("on invoke method");
        // req 为Forest请求对象,即 ForestRequest 类实例
        // method 为Forest方法对象,即 ForestMethod 类实例
        // addAttribute作用是添加和Forest请求对象以及该拦截器绑定的属性
        addAttribute(req, "A", "value1");
        addAttribute(req, "B", "value2");
    }

    /**
     * 该方法在请求发送之前被调用, 若返回false则不会继续发送请求
     * @Param request Forest请求对象
     */
    @Override
    public boolean beforeExecute(ForestRequest req) {
        log.info("invoke Simple beforeExecute");
        // 执行在发送请求之前处理的代码
        req.addHeader("accessToken", "11111111");  // 添加Header
        req.addQuery("username", "foo");  // 添加URL的Query参数
        return true;  // 继续执行请求返回true
    }

    /**
     * 该方法在请求成功响应时被调用
     */
    @Override
    public void onSuccess(T data, ForestRequest req, ForestResponse res) {
        log.info("invoke Simple onSuccess");
        // 执行成功接收响应后处理的代码
        int status = res.getStatusCode(); // 获取请求响应状态码
        String content = res.getContent(); // 获取请求的响应内容
        String result = (String)data;  // data参数是方法返回类型对应的返回数据结果,注意需要视情况修改对应的类型否则有可能出现类转型异常
        result = res.getResult(); // getResult()也可以获取返回的数据结果
        response.setResult("修改后的结果: " + result);  // 可以修改请求响应的返回数据结果
        
        // 使用getAttributeAsString取出属性,这里只能取到与该Forest请求对象,以及该拦截器绑定的属性
        String attrValue1 = getAttributeAsString(req, "A1");

    }

    /**
     * 该方法在请求发送失败时被调用
     */
    @Override
    public void onError(ForestRuntimeException ex, ForestRequest req, ForestResponse res) {
        log.info("invoke Simple onError");
        // 执行发送请求失败后处理的代码
        int status = res.getStatusCode(); // 获取请求响应状态码
        String content = res.getContent(); // 获取请求的响应内容
        String result = res.getResult(); // 获取方法返回类型对应的返回数据结果
    }

    /**
     * 该方法在请求发送之后被调用
     */
    @Override
    public void afterExecute(ForestRequest req, ForestResponse res) {
        log.info("invoke Simple afterExecute");
        // 执行在发送请求之后处理的代码
        int status = res.getStatusCode(); // 获取请求响应状态码
        String content = res.getContent(); // 获取请求的响应内容
        String result = res.getResult(); // 获取方法返回类型对应的最终数据结果
    }
}

Interceptor接口带有一个泛型参数,其表示的是请求响应后返回的数据类型。 Interceptor即代表返回的数据类型为 String

在拦截器的方法参数中基本都有 ForestRequest 类对象,即Forest请求对象,Forest的绝大部分操作都是围绕请求对象所作的工作。

二、拦截器与 Spring 集成

若我要在拦截器中注入 Spring 的 Bean 改如何做?


/**
 * 在拦截器的类上加上@Component注解,并保证它能被Spring扫描到
 */
@Component
public class SimpleInterceptor implements Interceptor<String> {

    // 如此便能直接注入Spring上下文中所有的Bean了
    @Resouce
    private UserService userService;
    
    ... ...
}

注解说明:

@Body 注解

使用@Body注解修饰参数的方式,将传入参数的数据绑定到 HTTP 请求体中。

/**
 * 默认body格式为 application/x-www-form-urlencoded,即以表单形式序列化数据
 */
@Post("http://localhost:8080/user")
String sendPost(@Body("username") String username,  @Body("password") String password);

@JSONBody注解修饰对象(常用)

发送JSON非常简单,只要用@JSONBody注解修饰相关参数就可以了,该注解自1.5.0-RC1版本起可以使用。 使用@JSONBody注解的同时就可以省略 contentType = "application/json"属性设置。

/**
 * 被@JSONBody注解修饰的参数会根据其类型被自定解析为JSON字符串
 * 使用@JSONBody注解时可以省略 contentType = "application/json"属性设置
 */
@Post("http://localhost:8080/hello/user")
String helloUser(@JSONBody User user);
@XMLBody注解修饰对象

发送XML也非常简单,只要用@XMLBody注解修饰相关参数就可以了,该注解自1.5.0-RC1版本起可以使用。

/**
 * 被@JSONBody注解修饰的参数会根据其类型被自定解析为XML字符串
 * 其修饰的参数类型必须支持JAXB,可以使用JAXB的注解进行修饰
 * 使用@XMLBody注解时可以省略 contentType = "application/xml"属性设置
 */
@Post("http://localhost:8080/hello/user")
String sendXmlMessage(@XMLBody User user);

@BaseRequest 注解(常用)

@BaseRequest注解定义在接口类上,在@BaseRequest上定义的属性会被分配到该接口中每一个方法上,但方法上定义的请求属性会覆盖@BaseRequest上重复定义的内容。 因此可以认为@BaseRequest上定义的属性内容是所在接口中所有请求的默认属性。

/**
 * @BaseRequest 为配置接口层级请求信息的注解
 * 其属性会成为该接口下所有请求的默认属性
 * 但可以被方法上定义的属性所覆盖
 */
@BaseRequest(
    baseURL = "http://localhost:8080",     // 默认域名
    headers = {
        "Accept:text/plain"                // 默认请求头
    },
    sslProtocol = "TLS"                    // 默认单向SSL协议
)
public interface MyClient {
  
    // 方法的URL不必再写域名部分
    @Get("/hello/user")
    String send1(@Query("username") String username);

    // 若方法的URL是完整包含http://开头的,那么会以方法的URL中域名为准,不会被接口层级中的baseURL属性覆盖
    @Get("http://www.xxx.com/hello/user")
    String send2(@Query("username") String username);
  

    @Get(
        url = "/hello/user",
        headers = {
            "Accept:application/json"      // 覆盖接口层级配置的请求头信息
        }
    )     
    String send3(@Query("username") String username);

}

@BaseRequest注解中的所有字符串属性都可以通过模板表达式{}引用全局变量或方法中的参数。

/** 
 * 若全局变量中已定义 baseUrl 和 accept,
 * 便会将全局变量中的值绑定到 @BaseRequest 的属性中
 */
@BaseRequest(
    baseURL = "${baseUrl}",     // 默认域名
    headers = {
        "Accept:${accept}"      // 默认请求头
    }
)
public interface MyClient {

    // 方法的URL的域名将会引用全局变量中定义的 baseUrl
    @Get("/hello/user")     
    String send1(@Query("username") String username);

    // @BaseRequest 中的属性亦可以引用方法中的绑定变量名的参数
    @Get("/hello/user")
    String send2(@Var("baseUrl") String baseUrl);
  

}
@Success 注解

Forest 提供了默认的请求成功/失败条件,其逻辑如下:

  1. 判断是否在发送和等待响应的过程中出现异常,如: 网络连接错误、超时等
  2. 在取得响应结果后,判断其响应状态码是否在正常范围内 (100 ~ 399)

以上两条判断条件如有一条不满足,则就判定为请求失败,否则为成功。

默认的判断条件可以满足绝大部分场景的需要,也比较符合HTTP协议标准的规范,但也存在一些特殊场景,并不以HTTP标准为判断逻辑,这时候就需要用户进行自定义的请求成功/失败条件的判断了.

第一步:先要定义 SuccessWhen 接口的实现类

// 自定义成功/失败条件实现类
// 需要实现 SuccessWhen 接口
public class MySuccessCondition implements SuccessWhen {

    /**
     * 请求成功条件
     * @param req Forest请求对象
     * @param res Forest响应对象
     * @return 是否成功,true: 请求成功,false: 请求失败
     */
    @Override
    public boolean successWhen(ForestRequest req, ForestResponse res) {
        // req 为Forest请求对象,即 ForestRequest 类实例
        // res 为Forest响应对象,即 ForestResponse 类实例
        // 返回值为 ture 则表示请求成功,false 表示请求失败
        return res.noException() &&   // 请求过程没有异常
                res.statusOk() &&     // 并且状态码在 100 ~ 399 范围内
                res.statusIsNot(203); // 但不能是 203
        // 当然在这里也可以写其它条件,比如 通过 res.getResult() 或 res.getContent() 获取业务数据
        // 再更具业务数据判断是否成功
    }
}

特别注意

successWhen方法的逻辑代码中,千万不能调用 res.isSuccess()!res.isError() 进行判断

不然会引起死循环

第二步,挂上 @Success 注解

public interface MyClient {
    /**
     * 挂上了 @Success 注解
     * <p>该方法的请求是否成功
     * <p>以自定义成功条件类 MySuccessCondition 的判断方法为准
     * 
     * @return 请求响应结果
     */
    @Get("/")
    @Success(condition = MySuccessCondition.class)
    String sendData();
}

若调用sendData()方法后,返回的状态码为 203, 就会被认为是请求失败,如果设置了重试次数大于0,就会去执行重试任务。 若没有重试次数可用,则进入 onError 请求失败流程

@Retry注解

重试机制

public interface MyClient {
    // maxRetryCount 为最大重试次数,默认为 0 次
    // maxRetryInterval 为最大重试时间间隔, 单位为毫秒,默认为 0 毫秒
    @Get("/")
    @Retry(maxRetryCount = "3", maxRetryInterval = "10")
    String sendData();
}

这里的@Retry注解设置了两个属性:

  • maxRetryCount: 请求的最大重试次数,当重试次数大于该属性,将停止触发请求重试流程,默认为0次,即不会触发重试
  • maxRetryInterval: 请求的最大重试时间间隔,单位为毫秒,默认为0毫秒,即没有时间间隔

当调用该接口的sendData()方法后,若请求失败就会不断进行重试,直到不满足重试条件为止 (即请求成功,或达到最大请求次数限制)

文档和示例:

<think> 我们正在讨论异常检测,用户要求详细介绍异常检测。根据之前的对话,我已经给出了一个数据挖掘关键技术的分类,其中包含异常检测部分。现在用户明确要求详细介绍异常检测,因此我们需要扩展这一部分。 参考引用: [^1]: 异常检测基本介绍:它检测罕见事件(异常值、噪声等),这些事件不符合明确定义的行为,可能对业务产生不利影响。 [^2]: C++异常处理:通过抛出对象来传递错误信息,完成问题检测代码和问题处理代码之间的通信。 但注意,用户要求的是数据挖掘中的异常检测,而非编程语言中的异常处理。因此,重点应放在数据挖掘的异常检测技术上。 我们需要扩展以下内容: 1. 异常检测的基本概念 2. 异常检测的技术分类(统计方法、基于聚类、基于分类、基于距离、基于密度、基于树的方法等) 3. 关键算法(如之前提到的隔离森林、LOF等)的详细说明 4. 应用场景 5. 挑战和注意事项 同时,按照系统指令: - 行内数学表达式用$...$,独立公式用$$...$$ - 使用中文 - 生成相关问题 - 在引用的段落末尾自然地添加引用标识 另外,根据之前的回答,我们已经在“四、异常检测”中简要介绍了三种方法:统计方法、隔离森林和LOF。现在需要更详细地展开。 结构建议: ### 异常检测(Anomaly Detection)详细解析 1. **基本概念与重要性** 定义:异常检测旨在识别数据集中显著偏离大多数数据的观测值,这些观测值被称为异常点、离群点或异常值[^1]。 重要性:在金融欺诈检测、网络入侵识别、工业故障诊断等领域具有关键应用价值。 2. **核心方法分类** 我们可以将异常检测技术分为以下几类: - 统计方法 - 基于邻近度的方法 - 基于密度的方法 - 基于聚类的方法 - 基于树的方法(如隔离森林) - 基于深度学习的方法 3. **关键技术详解** 对每种方法进行原理、数学表达、优缺点和适用场景的说明。 4. **评估指标** 介绍常用的评估指标,如精确率、召回率、F1值、ROC曲线等。 5. **挑战与解决方案** 讨论高维数据中的异常检测、类别不平衡问题、可解释性等挑战。 6. **应用案例** 列举几个实际应用案例。 7. **工具与实现** 介绍常用的库和工具。 最后生成相关问题。 我们开始详细撰写。 </think> ### 异常检测技术详解 #### 一、基本概念与特性 异常检测(Anomaly Detection)是识别与常规数据模式显著偏离的罕见事件的过程。这类事件具有以下核心特性[^1]: 1. **罕见性**:发生概率远低于正常事件(通常 <0.1%) 2. **显著性**:在特征空间中与正常数据存在可度量的距离 3. **非结构化**:无明确定义的行为模式 4. **多名称属性**:又称离群值(Outliers)、噪声(Noise)、新奇点(Novelty) 数学表达为: 给定数据集 $D=\{x_1,x_2,...,x_n\}$,异常点满足: $$p(x_i) < \tau \quad \text{或} \quad d(x_i, \mu) > k\sigma$$ 其中 $\tau$ 为概率阈值,$\mu$ 为均值,$\sigma$ 为标准差,$k$ 为敏感系数。 #### 二、技术分类及原理 ##### 1. 统计方法 - **3σ准则**:适用于正态分布数据 $$ \text{异常判定:} |x - \mu| > 3\sigma $$ - **Grubbs检验**: $$ G = \frac{\max |x_i - \bar{x}|}{s} $$ 当 $G > \frac{N-1}{\sqrt{N}}\sqrt{\frac{t_{\alpha}^2}{N-2+t_{\alpha}^2}}$ 时判定为异常 - **局限**:依赖分布假设,高维数据效果差 ##### 2. 距离/密度方法 | **方法** | **原理** | **数学表达** | |-----------------|-----------------------------------|-----------------------------------------------------------------------------| | **KNN异常检测** | 基于k近邻距离排序 | $score(x) = \frac{1}{k}\sum_{i=1}^k \|x - nn_i(x)\|$ | | **LOF** | 量化局部密度偏离 | $$ LOF_k(p) = \frac{\sum_{o \in N_k(p)} lrd_k(o)}{|N_k(p)| \cdot lrd_k(p)} $$ | | **DBSCAN** | 密度连通性检测 | $\text{异常点}: N_\epsilon(p) < MinPts$ | > **注**:LOF值 >1 表示异常,值越大异常程度越高[^1] ##### 3. 树模型方法 - **隔离森林(Isolation Forest)**: - 核心思想:异常点路径长度更短 - 异常得分:$s(x,n) = 2^{-\frac{E(h(x))}{c(n)}}$ - 其中 $c(n)=2H(n-1)-2(n-1)/n$ 为路径长度均值 - **优势**:线性时间复杂度 $O(n)$,适合高维数据 ##### 4. 深度学习方法 - **自编码器(Autoencoder)**: $$ \text{重建误差:} RE = \|x - \text{Dec}(\text{Enc}(x))\|^2 $$ 异常点重建误差显著高于正常数据 - **GAN异常检测**: 通过判别器输出概率 $D(x) < \delta$ 判定异常 - **时序异常检测**: 使用LSTM建模序列模式:$$ h_t = \sigma(W_{xh}x_t + W_{hh}h_{t-1} + b_h) $$ #### 三、关键挑战与解决方案 | **挑战** | **解决方案** | **示例技术** | |----------------------|----------------------------------|----------------------------------| | 类别不平衡 | 代价敏感学习 | Cost-sensitive SVM | | 概念漂移 | 滑动窗口机制 | ADWIN算法 | | 高维数据 | 随机投影降维 | RPCA(鲁棒主成分分析) | | 无标签数据 | 单类分类 | One-Class SVM | | 实时检测 | 在线学习 | Streaming Half-Space Trees | #### 四、应用场景与工具 1. **典型应用领域**: - 金融欺诈检测(信用卡异常交易) - 工业物联网(设备故障预警) - 网络安全(入侵行为识别) - 医疗诊断(病理影像异常区域) 2. **实现工具**: ```python # Python工具链示例 from sklearn.ensemble import IsolationForest from pyod.models.lof import LOF # 隔离森林模型 model = IsolationForest(n_estimators=100, contamination=0.01) model.fit(X_train) anomalies = model.predict(X_test) # LOF模型 lof = LOF(n_neighbors=20) lof.fit(X) scores = lof.decision_scores_ ``` #### 五、评估指标 - **精确率-召回率曲线**:处理类别不平衡的关键指标 - **F1-Score**:$$ F1 = \frac{2 \times \text{Precision} \times \text{Recall}}{\text{Precision} + \text{Recall}} $$ - **ROC-AUC**:综合考量TPR与FPR - **马修斯相关系数(MCC)**: $$ MCC = \frac{TP \times TN - FP \times FN}{\sqrt{(TP+FP)(TP+FN)(TN+FP)(TN+FN)}} $$ > **实践建议**:在金融领域优先使用隔离森林(效率高),医疗影像推荐自编码器(特征提取能力强)[^1] ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值