model.eval 至关重要!!!!model.eval()是否开启 BN 和 Dropout 的不同

news2024/11/24 20:57:50

之前写过一个项目,eval的时候很正常,但是一infer就有问题,多次排查发现,原来就是只缺一个

model.eval()

哇,重大教训,我debug好久,在小姑娘面前翻车… 🤣🤣🤣

在文档中 nn.Module (torch) 或者 nn.Layer (Paddle) 的 eval 都是说会影响 Dropout 和 BatchNorm

本文来看一下 eval 对 dropout 和 BatchNorm 做了什么

1. Dropout

import paddle
import paddle.nn as nn

model = nn.Dropout(0.9)
model.train()
inp = paddle.rand([3, 4])
out = model(inp)
>>> inp
Tensor(shape=[3, 4], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [[0.62465787, 0.75934911, 0.22212228, 0.46498537],
        [0.52518481, 0.94535673, 0.77799159, 0.50506479],
        [0.26053512, 0.26007101, 0.17834763, 0.70845836]])
        
>>> out 
Tensor(shape=[3, 4], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [[0.        , 0.        , 0.        , 0.        ],
        [0.        , 0.        , 0.        , 0.        ],
        [0.        , 0.        , 0.        , 7.08458233]])

但是如果加上 model.eval

import paddle
import paddle.nn as nn

model = nn.Dropout(0.9)
model.eval()
inp = paddle.rand([3, 4])
out = model(inp)
>>> inp
Tensor(shape=[3, 4], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [[0.85406083, 0.30777919, 0.84194952, 0.31621146],
        [0.19795220, 0.84470266, 0.64288461, 0.28012937],
        [0.76796150, 0.28145868, 0.44665241, 0.64438581]])
        
>>> out 
Tensor(shape=[3, 4], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [[0.85406083, 0.30777919, 0.84194952, 0.31621146],
        [0.19795220, 0.84470266, 0.64288461, 0.28012937],
        [0.76796150, 0.28145868, 0.44665241, 0.64438581]])

2. BatchNorm

如果 model 没有设置 .eval,则在 infer 中大概率有问题,因为 infer 一般 batch_size 为1,导致推理时的偏差较大,来,接下来看看到底是怎么个偏差较大

这里专门用 mnist 来做个例子,用Paddle实现,torch差不多

数据集加载:

import paddle
import paddle.nn as nn
import paddle.vision.transforms as T
from paddle.vision.datasets import MNIST
from paddle.io import DataLoader


transform = T.Compose(
    [
        T.ToTensor(),
        T.Normalize(
            mean=[127.5],
            std=[127.5],
        ),
    ]
)

train_mnist = MNIST(
    mode="train",
    transform=transform,
    backend="cv2",
)
test_mnist  = MNIST(
    mode="test",
    transform=transform,
    backend="cv2",
)

train_loader = DataLoader(
    dataset=train_mnist,
    shuffle=True,
    num_workers=8,
    batch_size=256,
)
test_loader = DataLoader(
    dataset=test_mnist,
    shuffle=False,
    num_workers=8,
    batch_size=256,
)

模型定义:(也可通过本demo学习BN中的 moving_mean 和 moving_variance 是如何计算的)

class Model(nn.Layer):
    def __init__(self, hidden_num=32, out_num=10):
        super().__init__()
        self.fc1 = nn.Linear(28*28, hidden_num)
        self.bn  = nn.BatchNorm(hidden_num)
        self.fc2 = nn.Linear(hidden_num, out_num)
        self.softmax = nn.Softmax()
    def forward(self, inputs, **kwargs):
        x = inputs.flatten(1)
        x = self.fc1(x)
        
        print("========= bn之前存的数据: =========")
        print(self.bn._mean, self.bn._variance)
        print()
        
        x_mean = x.mean(0)
        # x_variance = paddle.var(x, 0)
        x_variance = ((x - x_mean)*(x - x_mean)).mean(0)
        
        print("========= 当前 Batch 的数据: =========")
        print(x_mean, x_variance)
        print()
        
        bn_new_mean = self.bn._mean * self.bn._momentum + x_mean * (1-self.bn._momentum)
        bn_new_var  = self.bn._variance * self.bn._momentum + x_variance * (1-self.bn._momentum)
        print("========= 手动计算的新数据数据: =========")
        print(bn_new_mean, bn_new_var)
        print()
        
        x = self.bn(x)
        print("========= bn计算之后的新数据: =========")
        print(self.bn._mean, self.bn._variance)
        print()
        
        # x = self.dropout(x)
        x = self.fc2(x)
        x = self.softmax(x)
        return x

训练开始:

model = Model()
model.train()
for img, label in train_loader:
    label = nn.functional.one_hot(label.flatten(), 10)
    out = model(img)
    break

来看下最后的输出:

========= bn之前存的数据: =========
Parameter containing:
Tensor(shape=[32], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) Parameter containing:
Tensor(shape=[32], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
        1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])

========= 当前 Batch 的数据: =========
Tensor(shape=[32], dtype=float32, place=Place(gpu:0), stop_gradient=False,
       [-0.38882941,  0.03430356,  0.72941995,  0.88257772,  0.59287441,
         1.43031311,  1.16276920, -0.10491234, -0.93039852,  0.42681891,
         1.14838040, -0.21730696, -0.54693860, -0.14556082, -1.01294327,
        -0.71115893,  1.10771430, -0.77535808,  0.39560708, -0.68615019,
         1.51775694, -0.47886604, -0.14928204,  0.50894970,  1.76031160,
         0.50432783,  0.19052365, -2.01953459,  1.84687483, -0.70989704,
        -1.44391048,  1.64701092]) Tensor(shape=[32], dtype=float32, place=Place(gpu:0), stop_gradient=False,
       [0.58479834, 0.99659586, 0.33979574, 0.48517889, 0.65143955, 0.56042129,
        0.48198137, 0.41009885, 0.56537294, 0.50146306, 0.37284675, 0.24294424,
        0.71886718, 0.40560260, 0.49942270, 0.51505977, 0.71436852, 0.46243513,
        0.44024459, 0.76668155, 0.48029205, 0.57720137, 0.33403489, 0.41284043,
        0.37509984, 0.62300909, 0.53987831, 0.60129762, 0.55340236, 0.34260243,
        0.53284788, 0.49520931])

========= 手动计算的新数据数据: =========
Tensor(shape=[32], dtype=float32, place=Place(gpu:0), stop_gradient=False,
       [-0.03888294,  0.00343036,  0.07294200,  0.08825777,  0.05928744,
         0.14303131,  0.11627692, -0.01049123, -0.09303986,  0.04268189,
         0.11483804, -0.02173070, -0.05469386, -0.01455608, -0.10129433,
        -0.07111590,  0.11077143, -0.07753581,  0.03956071, -0.06861502,
         0.15177570, -0.04788661, -0.01492820,  0.05089497,  0.17603116,
         0.05043278,  0.01905237, -0.20195346,  0.18468748, -0.07098971,
        -0.14439104,  0.16470109]) Tensor(shape=[32], dtype=float32, place=Place(gpu:0), stop_gradient=False,
       [0.95847982, 0.99965954, 0.93397957, 0.94851786, 0.96514392, 0.95604211,
        0.94819814, 0.94100988, 0.95653725, 0.95014626, 0.93728465, 0.92429441,
        0.97188669, 0.94056022, 0.94994223, 0.95150596, 0.97143686, 0.94624346,
        0.94402444, 0.97666812, 0.94802916, 0.95772010, 0.93340349, 0.94128400,
        0.93750995, 0.96230090, 0.95398784, 0.96012974, 0.95534021, 0.93426025,
        0.95328474, 0.94952089])

========= bn计算之后的新数据: =========
Parameter containing:
Tensor(shape=[32], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [-0.03888295,  0.00343036,  0.07294201,  0.08825779,  0.05928745,
         0.14303134,  0.11627695, -0.01049124, -0.09303988,  0.04268190,
         0.11483806, -0.02173070, -0.05469387, -0.01455608, -0.10129435,
        -0.07111591,  0.11077145, -0.07753582,  0.03956072, -0.06861503,
         0.15177573, -0.04788661, -0.01492821,  0.05089498,  0.17603120,
         0.05043279,  0.01905237, -0.20195350,  0.18468753, -0.07098972,
        -0.14439109,  0.16470113]) Parameter containing:
Tensor(shape=[32], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [0.95870918, 1.00005043, 0.93411279, 0.94870818, 0.96539938, 0.95626187,
        0.94838715, 0.94117069, 0.95675898, 0.95034295, 0.93743086, 0.92438966,
        0.97216862, 0.94071931, 0.95013809, 0.95170796, 0.97171700, 0.94642484,
        0.94419706, 0.97696882, 0.94821757, 0.95794648, 0.93353444, 0.94144595,
        0.93765706, 0.96254522, 0.95419955, 0.96036553, 0.95555723, 0.93439460,
        0.95349371, 0.94971514])

可以看到咱手动计算的 moving_mean 和 moving_variance 和 BN 内部计算的大差不差

OK, 接下里看看BN .train 和 .eval 有啥区别

paddle.seed(1107)
model = nn.BatchNorm(4)
model.eval()
inp = paddle.rand([2, 4])
out = model(inp)
>>> out
Tensor(shape=[2, 4], dtype=float32, place=Place(gpu:0), stop_gradient=False,
       [[0.33660540, 0.05333305, 0.47220078, 0.21046987],
        [0.03869555, 0.67438763, 0.12688087, 0.62906295]])
>>> inp
Tensor(shape=[2, 4], dtype=float32, place=Place(gpu:0), stop_gradient=False,
       [[0.33660540, 0.05333305, 0.47220078, 0.21046987],
        [0.03869555, 0.67438763, 0.12688087, 0.62906295]])

eval 情况下,BN模型直接用已经记录的 mean 和 var 来进行BN操作,而不是用那个滑动平均值去计算
而默认情况下,BN模型职工的 mean 和 var 是多少呢?

来看下源代码:
在这里插入图片描述
默认情况下mean都是0,var都是1,

而在.train()训练情况下,会计算原有值和数据分布值的一个加权平均

m o v i n g _ m e a n = m o v i n g _ m e a n ∗ m o m e n t u m + μ β ∗ ( 1. − m o m e n t u m ) / / g l o b a l m e a n m o v i n g _ v a r i a n c e = m o v i n g _ v a r i a n c e ∗ m o m e n t u m + σ β 2 ∗ ( 1. − m o m e n t u m ) / / g l o b a l v a r i a n c e moving\_mean = moving\_mean * momentum + \mu_{\beta} * (1. - momentum) \quad // global mean \\ moving\_variance = moving\_variance * momentum + \sigma_{\beta}^{2} * (1. - momentum) \quad // global variance moving_mean=moving_meanmomentum+μβ(1.momentum)//globalmeanmoving_variance=moving_variancemomentum+σβ2(1.momentum)//globalvariance

paddle.seed(1107)
model = nn.BatchNorm(4)
model.train()
inp = paddle.rand([2, 4])
out = model(inp)
>>> out
Tensor(shape=[2, 4], dtype=float32, place=Place(gpu:0), stop_gradient=False,
       [[ 0.99977475, -0.99994820,  0.99983233, -0.99988586],
        [-0.99977475,  0.99994814, -0.99983233,  0.99988598]])
>>> inp
Tensor(shape=[2, 4], dtype=float32, place=Place(gpu:0), stop_gradient=False,
       [[0.33660540, 0.05333305, 0.47220078, 0.21046987],
        [0.03869555, 0.67438763, 0.12688087, 0.62906295]])

接下来手动计算一下看看,这是BN中原来的 mean 和 var:

>>> model._mean, model._variance
(Parameter containing:
 Tensor(shape=[4], dtype=float32, place=Place(gpu:0), stop_gradient=True,
        [0., 0., 0., 0.]),
        
 Parameter containing:
 Tensor(shape=[4], dtype=float32, place=Place(gpu:0), stop_gradient=True,
        [1., 1., 1., 1.]))

接下来计算一下数据的 mean 和 var:

>>> inp_mean = inp.mean(0)
>>> inp_mean
Tensor(shape=[4], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [0.18765143, 0.36386219, 0.29954234, 0.41976851])
       
>>> inp_var = ((inp - inp_mean) * (inp - inp_mean)).mean(0)
>>> inp_var
Tensor(shape=[4], dtype=float32, place=Place(gpu:0), stop_gradient=True,
       [0.02218779, 0.09642817, 0.02981176, 0.04380548])

手动计算一下以上二者的加权平均,这里用BN的默认值 momentum = 0.9 来计算

>>> my_bn_mean = inp_mean * 0.1 + 0 * 0.9
>>> my_bn_var = inp_var * 0.1 + 1 * 0.9
>>> my_bn_mean, my_bn_var

(Tensor(shape=[4], dtype=float32, place=Place(gpu:0), stop_gradient=True,
        [0.01876514, 0.03638622, 0.02995423, 0.04197685]),
        
 Tensor(shape=[4], dtype=float32, place=Place(gpu:0), stop_gradient=True,
        [0.90221876, 0.90964282, 0.90298116, 0.90438050]))

接下来打印一下bn模型计算计算之后的结果:

>>> model._mean, model._variance
(Parameter containing:
 Tensor(shape=[4], dtype=float32, place=Place(gpu:0), stop_gradient=True,
        [0.01876515, 0.03638623, 0.02995424, 0.04197686]),
        
 Parameter containing:
 Tensor(shape=[4], dtype=float32, place=Place(gpu:0), stop_gradient=True,
        [0.90443754, 0.91928560, 0.90596235, 0.90876108]))

以上结果大差不差,主要是在计算时还有一个 ϵ = 1 0 − 5 \epsilon = 10^{-5} ϵ=105 来防止除0错误:
x i ^ ← x i − μ β σ β 2 + ϵ / /   n o r m a l i z e y i ← γ x i ^ + β / /   s c a l e   a n d   s h i f t \hat{x_i} \gets \frac{x_i - \mu_\beta} {\sqrt{\sigma_{\beta}^{2} + \epsilon}} \qquad //\ normalize \\ y_i \gets \gamma \hat{x_i} + \beta \qquad //\ scale\ and\ shift xi^σβ2+ϵ xiμβ// normalizeyiγxi^+β// scale and shift

eval开启后,BN不再计算和数据分布有关的加权平均值,而是直接用已有的值mean 和 var 进行计算

所以如果在infer过程中,bs=1,会极大的扰动BN中的_mean 和 _var值 导致模型预测出错

3. 总结

综上,infer 时,一定要开启 model.eval()

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

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

相关文章

样本与抽样分布(2)-基本分布

本节介绍在数理统计中常用的几个基本分布。为此,先引进分位数定义。 定义1. 2. 1 设X为随机变量,则称满足 的为X的上侧分位数,简称为(上侧)分位数. 1 标准正态分布 标准正态分布N (0,1)是构造其他分布的基础,其密度函数为 它的图形关于y轴…

【毕业设计】深度学习卫星遥感图像检测与识别系统(目标检测)

文章目录0 前言1 课题背景2 实现效果3 Yolov5算法4 数据处理和训练5 最后0 前言 🔥 Hi,大家好,这里是丹成学长的毕设系列文章! 🔥 对毕设有任何疑问都可以问学长哦! 这两年开始,各个学校对毕设的要求越来…

LeetCode-44-通配符匹配

1、递归 具体思路同LeetCode-剑指19-正则表达式匹配,但在本题中由于字符串长度过长会导致超时。 在这里插入代码片class Solution { public:bool isMatch(string s, string p) {if (p.empty()) return s.empty();bool first_match !s.empty() && (s[0] …

Gradle修改镜像库 ,初始启动配置 init.gradle

目录 ■前言 ■代码放置位置 ■具体代码 代码建议: ■Gradle 的 更多知识(私密) ■前言 默认镜像库太慢了,在【初始启动配置(init.d)】中,添加xxx.gradle (init.gradle) 文件,指…

网络自动化运维(NetDevOps)创作者推荐

前言: 随着NetDevOps技术登上了历史舞台,越来越多的从业者开始利用NetDevOps简化网络的运维,并进行了技术分享,将蛋糕越做越大。在这里,仅代表个人对这些无私奉献的网络、运维工程师们表达由衷的敬意。 此外&#xff…

用户身份验证的令牌—Token教程

一、什么是Token? 1、Token的引入:Token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,Token便应…

学习二十大奋进新征程线上知识答题小程序登录技术点分析与实现

学习二十大奋进新征程线上知识答题小程序登录技术点分析与实现 在最新搭建的知识答题小程序,遇到了微信授权登录上的技术难点,所以对于以往的那套登录框架不能使用了,需要搭建一套新的注册登录流程框架。 不得不做出调整,为此&a…

json交叉编译并移植到嵌入式开发板

1、解压:tar -xvf json-c-0.9.tar.gz 默认解压在当前目录 2、进入解压后的目录:$ cd cd json-c-0.9/ 3、执行: sudo ./configure CCaarch64-linux-gnu-gcc --hostarm-linux --prefix/opt/json-c-0.9/ 说明:CC赋值为嵌入式开发环…

Java之线程详解(二)——线程安全概述、synchronized锁

一、线程安全概述 什么是线程安全问题? 当多个线程共享同一个全局变量,做写的操作时(即修改该全局变量),可能会受到其他的线程干扰,发生线程安全问题。 eg: public class Thread01 implemen…

丝裂原活化蛋白激酶TaMPK3抑制植物对ABA的反应

文章信息 题目:Mitogen-activated protein kinase TaMPK3 suppresses ABA response by destabilising TaPYL4 receptor in wheat 刊名:New Phytologist 作者:Ying Liu,You-Zhi Ma, Zhao-Shi Xu et al. 单位:Instit…

【Linux】如何在Linux下提交代码到gittee

文章目录使用 git 命令行创建项目三板斧第一招: git add三板斧第二招: git commit三板斧第三招: git push其他几个重要的命令git pull(将远端同步到本地)git rm(删除)git log(查看提交日志)使用 git 命令行…

GPT语言模型

GPT:GPT采用了大量的无监督的数据进行预训练(Pre-training),并在有监督的数据上进行微调(Fine-tuning)来做具体的NLP的任务。结构:GPT使用了12层12头,单向transformer的decoder部分&…

2001-2020年全国31省城镇居民人均可支配收入/居民实际收入水平

1、时间区间为:2001-2020年 2、范围包括:全国31省 3、基期:以2001年为基期 4、来源:国家统计J 5、指标包括: 城市居民消费价格指数(上年100)、城镇居民人均可支配收入(元)、实际可支配收入(2001年为基…

LabVIEW开发LabVIEW类

LabVIEW开发LabVIEW类 从概念上来说,LabVIEW面向对象编程和其它面向对象编程语言相似。但由于LabVIEW是数据流图形化编程环境,LabVIEW对类数据的操作和交互,以及LabVIEW类代码的调试方法和其它语言有所不同。 LabVIEW中的对象由值来传递&a…

02【SpringMVC的工作流程】

文章目录二、SpringMVC的工作流程2.1 SpringMVC的三大组件2.1.1 HandlerMapping:2.1.2 HandlerAdapter:2.1.3 ViewResolver:2.2 SpringMVC源码分析2.2.1 准备工作2.2.2 查看DispatcherServlet的继承体系:2.2.3 通过映射器获取执行…

工业机械设备设计与艺术设计

众所周知,外观设计是目前工业设计的一种,也是展示产品外观性能的最佳途径。如果外观设计做得好,产品可以在第一时间吸引顾客。说到外观设计,我们不得不说,工业机械的外观设计,因为机械工业是工业设计的主要…

Java客户关系系统(含源码+论文+答辩PPT等)

该项目采用技术Struts2代理注解反射MyBatisjqueryjscss等技术,项目含有源码、文档、配套开发软件、软件安装教程、项目发布教程等 项目功能介绍: 系统管理:用户登录、用户退出 员工管理:主要实现对员工的增、删、查、改功能 客户信…

ZMQ之面向服务的可靠队列(管家模式)

管家模式协议(MDP)在扩展PPP协议时引入了一个有趣的特性:client发送的每一个请求都有一个“服务名称”,而worker在像队列装置注册时需要告知自己的服务类型。MDP的优势在于它来源于现实编程,协议简单,且容易…

孩子到底是食物过敏?还是食物不耐受?

虾蟹过敏,牛奶过敏,鸡蛋过敏,甚至花生过敏……近年来,儿童食物过敏的发病率逐年上升。食物过敏对儿童危害很大,其临床症状会使儿童患病,影响身心健康;长期禁食,影响均衡营养摄入&…

Segger RTT深度使用说明-移植-Jlink rtt viewer显示-输出到Secure CRT

简介 RTT( Real Time Terminal)是SEGGER公司新出的可以在嵌入式应用中与用户进行交互的实时终端。J-Link驱动4.90之后的版本都支持RTT。RTT既可以从MCU上输出信息、也可以向应用程序发送信息,由于其高速的特性,所以不影响MCU的实…