一文读懂RequestBody注解原理

深入理解@RequestBody原理

一、RequestBody注解概述

我们通常使用RequestBody注解来web请求中的body体部分内容,并且通常会将其转换为一个javaBean的对象!

如下面的示例文件:

    @PostMapping("/school")
    public School querySchool(@RequestBody School school){
        return school;
    }
@Data
public class School {
    private String name;
}

示例对象依赖的POM文件为:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>rsa-sign-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>rsa-sign-demo</name>
    <description>rsa-sign-demo</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>



        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.6.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>

            </plugin>
        </plugins>
    </build>
</project>

通过Postman发送请求,得到结果如下:

请求后台

那么springMVC是如何将Body体中的内容绑定到实体对象School中去的呢?

requestBody注解类注释:

Annotation indicating a method parameter should be bound to the body of the web request.
The body of the request is passed through an {@link HttpMessageConverter} to resolve the method argument depending on the content type of the request.

从上面的描述我们可以看到主要是通过HttpMessageConverter来进行转换,

二、执行流程

所有的springmvc请求都会经过org.springframework.web.servlet.DispatcherServlet#doDispatch来进行处理,我们就从该方法开始跟踪。主要流程图如下所示:

在这里插入图片描述

2.1、DispatcherServlet处理

doDispatch方法中的源码如下,只保留我们想进行关注的部分代码:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
                //根据请求来来获取对应的处理器
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
                //根据处理器获取到处理器对应的适配器
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

			    // 省略部分代码 ...

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
                // 这里使用RequestMappingHandlerAdapter来处理请求
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
			// 省略部分代码 ...
			}
            //省略部分代码 ...
			
	}

2.2、AbstractHandlerMethodAdapter的handle方法

RequestMappingHandlerAdapter是AbstractHandlerMethodAdapter的子类,调用的是其父类的handle方法

	@Override
	@Nullable
	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return handleInternal(request, response, (HandlerMethod) handler);
	}

子类RequestMappingHandlerAdapter的handleInternal方法实现,具体代码如下:

@Override
	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
		checkRequest(request);

		// Execute invokeHandlerMethod in synchronized block if required.
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				// No HttpSession available -> no mutex necessary
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No synchronization on session demanded at all...
            //执行方法的调用
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			}
			else {
				prepareResponse(response);
			}
		}

		return mav;
	}

接下来跟踪org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod方法

@Nullable
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	// 省略部分代码...
		try {
		// 省略部分代码...

			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			if (this.argumentResolvers != null) {
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			}
			if (this.returnValueHandlers != null) {
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			}
			// 省略部分代码
            
			// 调用ServletInvocableHandlerMethod的invokeAndHandle
			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}

2.3、ServletInvocableHandlerMethod#invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		
    // 执行请求方法调用
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		setResponseStatus(webRequest);
       
    //省略部分代码 ....

		mavContainer.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");
		try {
            // 处理结果返回
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
	   //省略部分代码 ....
	}

在invokeAndHandle方法中,主要干两个事情

  • invokeForRequest执行方法参数绑定、执行方法调用,获取返回值
  • this.returnValueHandlers.handleReturnValue 对controller层中的方法返回的结果进行处理

接下来我们继续跟踪invokeForRequest方法:

2.4、InvocableHandlerMethod#invokeForRequest

	@Nullable
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Arguments: " + Arrays.toString(args));
		}
		return doInvoke(args);
	}

invokeForRequest中做了两件事情

  • 1、通过getMethodArgumentValues方法获取请求参数
  • 2、通过doInvoke使用反射进行controller层方法的调用

然后我们接下来跟踪getMethodArgumentValues方法:

2.5、getMethodArgumentValues方法源码

// InvocableHandlerMethod.java
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		//获取调用的方法中的对应的参数信息
		MethodParameter[] parameters = getMethodParameters();
        // 如果不接收参数,那么进行直接返回
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}

		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
	     //省略部分代码....
            
            //使用参数解析器来解析对应的参数,这里是HandlerMethodArgumentResolverComposite
				args[i] = 
                    this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
		//省略部分代码....
        }
		return args;
	}

getMethodArgumentValues方法中的主要逻辑是:

  • 1、获取即将调用的controller层方法对应的参数,如果没有参数直接进行返回
  • 2、调用参数解析器(HandlerMethodArgumentResolverComposite)来执行参数的解析

接下来的任务是跟踪HandlerMethodArgumentResolverComposite如果从请求中解析对应的参数出来:

2.6 resolveArgument源码跟踪

// HandlerMethodArgumentResolverComposite.java
@Override
	@Nullable
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
		
        //根据调用方法来获取支持的参数解析器
		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
		if (resolver == null) {
			throw new IllegalArgumentException("Unsupported parameter type [" +
					parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
		}
        // 使用RequestResponseBodyMethodProcessor来解析对应的参数
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}

resolveArgument方法的主要作用是:

  • 1、根据对应方法和参数获取支持的方法及参数解析器,这里获取的是RequestResponseBodyMethodProcessor实例对象
  • 2、使用RequestResponseBodyMethodProcessor解析方法参数

接下来我们继续跟踪RequestResponseBodyMethodProcessor的resolveArgument方法

2.7、RequestResponseBodyMethodProcessor#resolveArgument

	@Override
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		parameter = parameter.nestedIfOptional();
        
        // 从httpInputMessage中来读取请求体信息
		Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
       // 省略部分代码...

		return adaptArgumentIfNecessary(arg, parameter);
	}

resolveArgument方法方法主要部分就是调用了readWithMessageConverters方法

2.8、readWithMessageConverters方法解析

// RequestResponseBodyMethodProcessor.java
@Override
	protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
			Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

		HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
		Assert.state(servletRequest != null, "No HttpServletRequest");
        // 创建一个HttpInputMessage对象
		ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
		//调用另一个重载方法
		Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
		if (arg == null && checkRequired(parameter)) {
			throw new HttpMessageNotReadableException("Required request body is missing: " +
					parameter.getExecutable().toGenericString(), inputMessage);
		}
		return arg;
	}

readWithMessageConverters方法主要做了两个事情

  • 1、基于原始请求的Request构造一个ServletServerHttpRequest对象
  • 2、调用重载的readWithMessageConverters方法来执行真正的参数绑定

2.9、readWithMessageConverters方法解析

// RequestResponseBodyMethodProcessor.java
@Nullable
    protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
                                                   Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
        // 省略部分代码 ...
        Object body = NO_VALUE;

        AbstractMessageConverterMethodArgumentResolver.EmptyBodyCheckingHttpInputMessage message = null;
     
            message = new AbstractMessageConverterMethodArgumentResolver.EmptyBodyCheckingHttpInputMessage(inputMessage);

            for (HttpMessageConverter<?> converter : this.messageConverters) {
                Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
                GenericHttpMessageConverter<?> genericConverter =
                        (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
                if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
                        (targetClass != null && converter.canRead(targetClass, contentType))) {
                    if (message.hasBody()) {
                        //执行所有符合条件的RequestBodyAdvice对应的beforeBodyRead方法
                        HttpInputMessage msgToUse =
                                getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                        //使用MappingJackson2HttpMessageConverter消息转换器来读取对应的消息
                        body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                                ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                        //执行所有符合条件的RequestBodyAdvice对应的afterBodyRead方法
                        body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                    }
                    else {
                        body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
                    }
                    break;
                }
            }

        // 省略部分代码 ...
        return body;
    }

该方法中主要有三件事情进行处理

  • 1、调用RequestResponseBodyAdviceChain的beforeBodyRead来修改原始的HttpInputMessage对象
  • 2、调用MappingJackson2HttpMessageConverte的read方法来进行数据的读取,这里入参中HttpInputMessage对象为上一步中返回的
  • 3、调用RequestResponseBodyAdviceChain的afterBodyRead方法来修改第二步中返回的body

我们继续来回到org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest方法中,来看下获取到参数入参后的处理方法

2.10、invokeForRequest方法执行真正controller层调用

	@Nullable
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Arguments: " + Arrays.toString(args));
		}
		return doInvoke(args);
	}

该方法主要是使用上面所讲通过MappingJackson2HttpMessageConverte从Request中绑定出来的参数对象进行controller层的方法调用

接下来跟踪下获取到结果返回值后,如何对结果进行处理的,org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle

2.11、invokeAndHandle返回值逻辑处理

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
          // 省略部分代码...
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		//省略部分代码...
	}

这里我们核心关注下this.returnValueHandlers.handleReturnValue方法的执行,这个是HandlerMethodReturnValueHandlerComposite对象

2.12、handleReturnValue源码跟踪

//HandlerMethodReturnValueHandlerComposite#handleReturnValue
@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
	// 根据返回的值及返回值类型来选择符合条件的HandlerMethodReturnValueHandler对象
		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
        //使用前面获取到的实例来处理返回的结果值
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}

上述方法逻辑很简单,主要是干两个事情

  • 1、根据返回的结果和MethodParameter对象来到对应的返回值处理器对象
  • 2、使用第一步获取到的对象对返回的值进行真正处理

2.13、selectHandler方法解析

//HandlerMethodReturnValueHandlerComposite.java	
@Nullable
	private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
		boolean isAsyncValue = isAsyncReturnValue(value, returnType);
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
				continue;
			}
			if (handler.supportsReturnType(returnType)) {
				return handler;
			}
		}
		return null;
	}

上述方法的逻辑的是依次遍历HandlerMethodReturnValueHandlerComposite中所有的HandlerMethodReturnValueHandler对象,一旦有支持的那么就会返回该处理器,遍历完后如果还是没有找到那么就返回null对象。我们这里使用的是RequestResponseBodyMethodProcessor对象,org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#supportsReturnType

// RequestResponseBodyMethodProcessor.java
@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
				returnType.hasMethodAnnotation(ResponseBody.class));
	}

从上面的代码可以看出来,当类层级或者方法层级上有@ResponseBody注解的时候,将会使用RequestResponseBodyMethodProcessor来进行处理

我们来继续跟踪handleReturnValue方法

2.14、handleReturnValue方法解析

// RequestResponseBodyMethodProcessor.java	
@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

		mavContainer.setRequestHandled(true);
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		// Try even with null return value. ResponseBodyAdvice could get involved.
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}

该方法的核心的就是调用了writeWithMessageConverters方法,接下来跟踪下该方法

2.15、writeWithMessageConverters方法源码解析

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
			ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

	
		// 省略部分代码 ...
		if (selectedMediaType != null) {
			selectedMediaType = selectedMediaType.removeQualityValue();
			for (HttpMessageConverter<?> converter : this.messageConverters) {
				GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
						(GenericHttpMessageConverter<?>) converter : null);
                //判断当前的HttpMessageConverter是否能够写入该对象
				if (genericConverter != null ?
						((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
						converter.canWrite(valueType, selectedMediaType)) {
                    // 请求体进行写入的前置处理
					body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
							(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
							inputMessage, outputMessage);
					if (body != null) {
						Object theBody = body;
						LogFormatUtils.traceDebug(logger, traceOn ->
								"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
						addContentDispositionHeader(inputMessage, outputMessage);
						if (genericConverter != null) {
                            //使用MappingJackson2HttpMessageConverter对象进行写入
							genericConverter.write(body, targetType, selectedMediaType, outputMessage);
						}
						else {
							((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
						}
					}
					else {
						if (logger.isDebugEnabled()) {
							logger.debug("Nothing to write: null body");
						}
					}
					return;
				}
			}
		}
	// 省略部分代码 ...
	
	}

至此,我们分析完了所有整个流程的代码!

三、源码涉及的到的重点类以及方法总结

3.1 涉及的接口及类

  • 1、HandlerMethodArgumentResolver接口: 用来对一个request进行参数解析的接口,如我们使用@RequestBody注解的时候,会使用RequestResponseBodyMethodProcessor来进行处理参数

  • 2、RequestResponseBodyMethodProcessor:HandlerMethodArgumentResolver接口的实现类,主要是用来处理在方法参数添加了@RequestBody注解的赋值操作以及在方法上添加了@RequestBody的返回值的处理

  • 3、HttpMessageConverter接口: 用来将请求体和响应体进行转换的接口

    将请求转换为接口入参,将http处理方法的返回值转换为http响应。

  • 4、MappingJackson2HttpMessageConverter实现类:

    HttpMessageConverter接口的实现类,用来读取和写入JSON格式类型的请求

  • 5、RequestBodyAdvice: 可以对使用@RequestBody注释的参数进行修改

  • 6、ResponseBodyAdvice:对使用了@ResponseBody注释的方法或类,用来对请求进行修改

}
``

至此,我们分析完了所有整个流程的代码!

三、源码涉及的到的重点类以及方法总结

3.1 涉及的接口及类

  • 1、HandlerMethodArgumentResolver接口: 用来对一个request进行参数解析的接口,如我们使用@RequestBody注解的时候,会使用RequestResponseBodyMethodProcessor来进行处理参数

  • 2、RequestResponseBodyMethodProcessor:HandlerMethodArgumentResolver接口的实现类,主要是用来处理在方法参数添加了@RequestBody注解的赋值操作以及在方法上添加了@RequestBody的返回值的处理

  • 3、HttpMessageConverter接口: 用来将请求体和响应体进行转换的接口

    将请求转换为接口入参,将http处理方法的返回值转换为http响应。

  • 4、MappingJackson2HttpMessageConverter实现类:

    HttpMessageConverter接口的实现类,用来读取和写入JSON格式类型的请求

  • 5、RequestBodyAdvice: 可以对使用@RequestBody注释的参数进行修改

  • 6、ResponseBodyAdvice:对使用了@ResponseBody注释的方法或类,用来对请求进行修改
    在这里插入图片描述

  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Yolov5 是一种广泛应用于目标检测的算法,其 loss 原理相对简单。Yolov5 通过将目标检测问题转化为一个回归问题,通过预测 bounding box 的坐标来实现目标检测。 Yolov5 的 loss 主要包括三个部分:分类损失、定位损失和目标置信度损失。 分类损失是用来衡量预测的类别与真实类别之间的差异。Yolov5 使用交叉熵损失函数来计算分类损失。对于每个边界框(bounding box),它将计算预测类别的 softmax 概率与真实类别的 one-hot 向量之间的交叉熵。 定位损失用于衡量预测的边界框位置与真实边界框位置之间的差异。Yolov5 使用 Smooth L1 损失函数来计算定位损失。它通过对预测边界框的坐标与真实边界框的坐标之间进行平滑处理,减小了异常值的影响。 目标置信度损失用于衡量预测的边界框与真实边界框之间的 IoU(Intersection over Union)之间的差异。Yolov5 使用 Binary Cross-Entropy 损失函数来计算目标置信度损失。它将预测的边界框是否包含目标与真实边界框是否包含目标之间的差异进行衡量。 最终,Yolov5 的总损失是通过将三个部分的损失加权求和得到的。这些权重可以根据具体的任务和数据集进行调整。 通过最小化 Yolov5 的 loss 函数,模型可以学习到更准确的目标检测结果。这样,我们就可以在图像中准确地检测和定位不同类别的目标。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值