论文解读:(MoCo)Momentum Contrast for Unsupervised Visual Representation Learning

文章汇总

 

参数的更新,\theta_q指encoder q的参数,\theta_k为encoder k,sampling,monentum encoder 的参数。

值得注意的是对于(b)、(c)这里反向传播只更新\theta_q\theta_k的更新只依赖于\theta_q

对比学习如同查字典 

考虑一个编码查询q和一组编码样本\{k_0,k_1,k_2,...\}是字典的键。假设字典中只有一个键(记为k_+)与q匹配。对比损失[29]是指当q与它的正键k_+相似,且与其他所有键不相似时(认为是q的负键),其值较低的函数。

其中qk_+ 可以有多种构造方式。本文使用了一种较为简单的方式:

(1)一种图片进行 random resize;

(2)进行两次224*224的随机Crop得到两个图像分别作为q,k_+

(3)进行增强操作,包括 random color jittering, random horizontal flip, random grayscale conversion 等。

一位网友的总结(来源见参考文章)

MoCo的特点是:(1)用于 negative 采样的队列是动态的(2)用于 negative 样本特征提取的编码器与用于query提取的编码器不一致,是一种Momentum更新的关系。而在memory-bank中两个编码器是一致的。(3)与Memory Bank类似,NCE 产生的损失并不影响f_k ,只影响f_q。原因是
x^k样本一般是x^q样本的几倍,更新f_k的代价很大(例如在 end-to-end中),因此 Memory-Bank 和 MoCo 都不更新f_k

摘要

我们提出动量对比(MoCo)用于无监督视觉表征学习。从对比学习[29]作为字典查找的角度出发,我们构建了一个带有队列和移动平均编码器的动态字典。这使得建立一个大的和一致的字典,方便对比无监督学习。MoCo在ImageNet分类的通用线性协议下提供了具有竞争力的结果。更重要的是,MoCo学习到的表征可以很好地转移到下游任务中。在PASCAL VOC、COCO和其他数据集上,MoCo在7个检测/分割任务上的表现优于有监督的预训练对手,有时甚至远远超过它。这表明在许多视觉任务中,无监督和有监督表示学习之间的差距已经很大程度上缩小了。

1. 介绍

无监督表示学习在自然语言处理中非常成功,如GPT[50,51]和BERT[12]所示。但在计算机视觉领域,监督预训练仍然占主导地位,而非监督方法通常落后于此。其原因可能源于它们各自信号空间的差异。语言任务具有离散的信号空间(词,子词单位等),用于构建标记化字典,无监督学习可以基于此。相比之下,计算机视觉则进一步关注字典构建[54,9,5],因为原始信号处于连续的高维空间中,并且不适合人类交流(例如,不像单词)。

最近的几项研究[61,46,36,66,35,56,2]在使用与对比损失相关的方法进行无监督视觉表征学习方面取得了令人鼓舞的结果[29]。尽管受到各种动机的驱动,但这些方法可以被认为是构建动态字典。字典中的“键”(令牌)从数据(例如,图像或补丁)中采样,并由编码器网络表示。无监督学习训练编码器执行字典查找:编码的“查询”应该与其匹配的键相似,而与其他键不同。学习被表述为最小化对比损失[29]。

从这个角度来看,我们假设建立字典是可取的:(i)大,(ii)在训练过程中保持一致。直观地说,更大的字典可以更好地采样底层连续的高维视觉空间,而字典中的键应该由相同或类似的编码器表示,以便它们与查询的比较是一致的。然而,使用对比损耗的现有方法可能在这两个方面中的一个方面受到限制(稍后将在上下文中讨论)。

我们提出了动量对比(MoCo)作为一种构建具有对比损失的无监督学习的大型一致字典的方法(图1)。我们将字典维护为数据样本队列:当前小批的编码表示被排队,最老的被排队。队列将字典大小与mini批处理大小解耦,从而允许它很大。此外,由于字典键来自于前面的几个小批量,因此提出了一种缓慢推进的键编码器,作为查询编码器的基于动量的移动平均来实现,以保持一致性。

图1所示。动量对比(MoCo)通过使用对比损失将编码查询q与编码键的字典进行匹配来训练视觉表示编码器。字典键\{k_0,k_1,k_2,...\}是由一组数据样本动态定义的。字典被构建为一个队列,当前的小批量进入队列,最老的小批量退出队列,将其与小批量大小解耦。键由一个缓慢推进的编码器编码,由查询编码器的动量更新驱动。这种方法为学习视觉表示提供了一个大而一致的字典。

MoCo是一种为对比学习构建动态字典的机制,可用于各种文本预训练任务。在本文中,我们遵循一个简单的实例识别任务[61,63,2]:如果它们是同一图像的编码视图(例如,不同的作物),则查询匹配一个键。使用此借口任务,MoCo在ImageNet数据集的常见线性分类协议下显示了竞争结果[11]。

无监督学习的一个主要目的是预训练表征(即特征),这些表征可以通过微调转移到下游任务。我们表明,在与检测或分割相关的7个下游任务中,MoCo无监督预训练可以超过其ImageNet有监督的对应项,在某些情况下,这一差距非常大。在这些实验中,我们探索了在ImageNet或10亿张Instagram图像集上预训练的MoCo,证明MoCo可以在更真实的、10亿张图像规模和相对未经策划的场景中很好地工作。这些结果表明,MoCo在很大程度上缩小了许多计算机视觉任务中无监督和有监督表示学习之间的差距,并且可以在一些应用中作为ImageNet监督预训练的替代方案。

MoCo是一种为对比学习构建动态字典的机制,可用于各种预序列任务。在本文中,我们遵循一个简单的实例识别任务[61,63,2]:如果它们是同一图像的编码视图(例如,不同的作物),则查询匹配一个键。使用此预训练任务,MoCo在ImageNet数据集的常见线性分类协议下显示了竞争结果[11]。

无监督学习的一个主要目的是预训练表征(即特征),这些表征可以通过微调转移到下游任务。我们表明,在与检测或分割相关的7个下游任务中,MoCo无监督预训练可以超过其ImageNet有监督的对应项,在某些情况下,这一差距非常大。在这些实验中,我们探索了在ImageNet或10亿张Instagram图像集上预训练的MoCo,证明MoCo可以在更真实的、10亿张图像规模和相对未经策划的场景中很好地工作。这些结果表明,MoCo在很大程度上缩小了许多计算机视觉任务中无监督和有监督表示学习之间的差距,并且可以在一些应用中作为ImageNet监督预训练的替代方案。

2. 相关工作

无监督/自监督学习方法一般包括两个方面:预训练任务和损失函数。术语“pretext”意味着要解决的任务不是真正感兴趣的,而是为了学习良好的数据表示而解决的。损失函数通常可以独立于借口任务进行研究。MoCo侧重于损失函数方面。下面我们就这两个方面的相关研究进行讨论。

损失函数

定义损失函数的一种常见方法是测量模型预测与固定目标之间的差异,例如通过L1或L2损失重建输入像素(例如,自编码器),或者通过交叉熵或基于边缘的损失将输入分类为预定义的类别(例如,八个位置[13],颜色箱[64])。其他选择,如下面所述,也是可能的。

对比损失[29]衡量一个表示空间中样本对的相似性。在对比损失公式中,目标可以在训练过程中动态变化,并且可以根据网络计算的数据表示来定义,而不是将输入与固定目标匹配[29]。对比学习是最近几项关于无监督学习的研究的核心[61,46,36,66,35,56,2],我们将在后面的上下文中详细阐述(第3.1节)。

对抗性损失[24]衡量概率分布之间的差异。这是一项广泛成功的技术用于无监督数据生成。表征学习的对抗性方法在[15,16]中进行了探讨。生成对抗网络和噪声对比估计(NCE)之间存在关系(参见[24])[28]。

Pretext tasks

已经提出了各种各样的预训练任务。示例包括在某些损坏情况下恢复输入,例如去噪自编码器[58],上下文自编码器[48]或跨通道自编码器(着色)[64,65]。一些预训练任务形成伪标签,例如,单个(“范例”)图像的变换[17],补丁排序[13,45],跟踪[59]或分割视频中的对象[47],或聚类特征[3,4]。

Contrastive learning vs. pretext tasks

各种pretext任务可以基于某种形式的对比损失函数。实例辨别方法[61]与基于范例的任务[17]和NCE[28]有关。对比预测编码(CPC)[46]中的托辞任务是一种语境自动编码[48],对比多视图编码(CMC)[56]中的托辞任务与着色有关[64]。

3. 方法

3.1. 对比学习如查字典

对比学习[29]及其最近的发展,可以被认为是为字典查找任务训练编码器,如下所述。

考虑一个编码查询q和一组编码样本\{k_0,k_1,k_2,...\}是字典的键。假设字典中有一个键(记为k_+)与q匹配。对比损失[29]是指当q与它的正键k_+相似,且与其他所有键不相似时(认为是q的负键),其值较低的函数。通过点积度量相似性,本文考虑了一种对比损失函数InfoNCE [46]:

其中τ为温度超参数per[61]。和是1个正样本和K个负样本。直观地说,这个损失是试图将q分类为
k_+的(K+1)-way基于softmax的分类器的log损失。对比损失函数也可以基于其他形式[29,59,61,36],如基于边际的损失和NCE损失的变体。

对比损失作为无监督目标函数,用于训练表示查询和键的编码器网络[29]。通常,查询表示为
q=f_q(x^q),其中f_q是编码器网络,x^q是查询样本(同样,k=f_k(x^k))。它们的实例化取决于特定的预训练任务。输入的x^qx^k可以是图像[29,61,63],补丁[46],或由一组补丁组成的上下文[46]。网络f_qf_k可以是相同的[29,59,63],部分共享的[46,36,2],或者不同的[56]

3.2. 动量的对比

从上面的角度来看,对比学习是一种在高维连续输入(如图像)上构建离散字典的方法。字典是动态的,因为键是随机采样的,并且键编码器在训练过程中不断发展。我们的假设是,好的特征可以通过一个包含丰富负样本集的大字典来学习,而字典键的编码器尽管在进化中仍尽可能保持一致。基于这一动机,我们提出动量对比,如下所述。

作为队列的字典

我们方法的核心是将字典维护为数据样本队列。这允许我们重用前面小批量的编码密钥。队列的引入将字典大小与小批处理大小解耦。我们的字典大小可以比典型的迷你批处理大小大得多,并且可以灵活和独立地设置为超参数。

字典中的样本逐渐被替换。当前的小批被加入字典队列,队列中最老的小批被删除。字典总是表示所有数据的一个抽样子集,而维护这个字典的额外计算是可管理的。此外,删除最旧的迷你批可能是有益的,因为它的编码密钥是最过时的,因此与最新的密钥最不一致。

动力更新

使用队列可以使字典变大,但它也使得通过反向传播(梯度应该传播到队列中的所有样本)更新密钥编码器变得难以处理。一个比较简单的解决方案是从查询编码器f_q复制键编码器f_k,忽略这个梯度。但是这个解决方案在实验中产生了很差的结果(第4.1节)。我们假设这种失败是由于快速变化的编码器降低了密钥表示的一致性造成的。我们建议对这一问题进行动量更新。

形式上,将f_k的参数记为\theta_k,f_q的参数记为\theta_q,我们将\theta_k更新为:

这里m\in [0,1)为动量系数。只有参数\theta_q通过反向传播更新。Eqn.(2)中的动量更新使得\theta_k\theta_q更平滑地演化。因此,尽管队列中的键由不同的编码器编码(在不同的mini-batch中),但这些编码器之间的差异可以很小。在实验中,相对较大的动量(例如m = 0.999,我们的默认值)比较小的动量(例如m = 0.9)要好得多,这表明缓慢发展的密钥编码器是利用队列的核心

与先前机制的关系

MoCo是使用对比损耗的一般机制。我们将其与图2中现有的两种通用机制进行比较。它们在字典大小和一致性上表现出不同的属性。

图2。三种对比损失机制的概念比较(经验比较见图3和表3)。这里我们举例说明一对查询和键。这三种机制的不同之处在于如何维护密钥以及如何更新密钥编码器。

(a):计算查询和键表示的编码器通过反向传播端到端更新(两个编码器可以不同)。

(b):密钥表示从存储库中采样[61]。

(c): MoCo通过动量更新编码器对新键进行动态编码,并维护一个键队列(未在此图中说明)。

通过反向传播进行的端到端更新是一种自然机制(例如[29,46,36,63,2,35],图2a)。它使用当前迷你批处理中的样本作为字典,因此键被一致地编码(由相同的编码器参数集)。但是字典大小与mini-batch大小相结合,受到GPU内存大小的限制。它也受到大型小批量优化的挑战[25]。最近的一些方法[46,36,2]是基于由局部位置驱动的借口任务,其中多个位置可以使字典大小变大。但这些预训练任务可能需要特殊的网络设计,如修补输入[46]或定制接受域大小[2],这可能会使这些网络向下游任务的转移复杂化。

另一种机制是由[61]提出的记忆库方法(图2b)。内存库由数据集中所有样本的表示组成。每个mini-batch的字典都是从内存库中随机采样的,没有反向传播,因此它可以支持较大的字典大小。然而,一个样本的表示记忆库是在最后一次看到时更新的,所以采样的密钥本质上是关于编码器在过去一个纪元的多个不同步骤,因此不太一致。文献[61]对内存库采用动量更新。它的动量更新是在相同样本的表示上,而不是在编码器上。这个动量更新与我们的方法无关,因为MoCo并没有跟踪每个样本。此外,我们的方法具有更高的内存效率,可以在十亿规模的数据上进行训练,这对于内存库来说是难以处理的。

第4节对这三种机制进行了实证比较。

3.3. Pretext Task

对比学习可以驱动各种各样的pretext任务。由于本文的重点不是设计一个新的pretext任务,我们使用了一个简单的pretext任务,主要是遵循[61]中的实例区分任务,最近的一些研究[63,2]与实例区分任务有关。

根据[61],如果查询和键来自同一图像,我们将其视为正对,否则将其视为负样本对。按照[63,2],我们在随机数据增强的情况下,对同一张图像随机取两个“视图”,形成正对。查询和键分别由它们的编码器f_qf_k编码。编码器可以是任何卷积神经网络[39]。

算法1为此提供了MoCo的pretext任务的伪代码。对于当前的小批量,我们对查询及其对应的键进行编码,它们形成正样本对。负样本来自队列。

# f_q, f_k: encoder networks for query and key
# queue: dictionary as a queue of K keys (CxK)
# m: momentum
# t: temperature
f_k.params = f_q.params # initialize
for x in loader: # load a minibatch x with N samples
    x_q = aug(x) # a randomly augmented version
    x_k = aug(x) # another randomly augmented version
    q = f_q.forward(x_q) # queries: NxC
    k = f_k.forward(x_k) # keys: NxC
    k = k.detach() # no gradient to keys
    # positive logits: Nx1
    l_pos = bmm(q.view(N,1,C), k.view(N,C,1))
    # negative logits: NxK
    l_neg = mm(q.view(N,C), queue.view(C,K))
    # logits: Nx(1+K)
    logits = cat([l_pos, l_neg], dim=1)
    # contrastive loss, Eqn.(1)
    labels = zeros(N) # positives are the 0-th
    loss = CrossEntropyLoss(logits/t, labels)
    # SGD update: query network
    loss.backward()
    update(f_q.params)
    # momentum update: key network
    f_k.params = m*f_k.params+(1-m)*f_q.params
    # update dictionary
    enqueue(queue, k) # enqueue the current minibatch
    dequeue(queue) # dequeue the earliest minibatch

# bmm: batch matrix multiplication; mm: matrix multiplication; cat: concatenation. 

技术细节

我们采用ResNet[33]作为编码器,其最后一个全连接层(经过全局平均池化)具有固定维输出(128-D[61])。该输出向量由L2范数归一化[61]。这是查询或键的表示形式。设Eqn.(1)中的温度τ为0.07[61]。数据增强设置如下[61]:从随机调整大小的图像中截取224×224-pixel裁剪,然后经历随机颜色抖动,随机水平翻转和随机灰度转换,这些都可以在PyTorch的torchvision包中使用。

洗牌BN

我们的编码器f_qf_k都与标准ResNet[33]一样具有批处理归一化(BN)[37]。在实验中,我们发现使用BN会阻止模型学习良好的表示,正如[35]中类似的报道(避免使用BN)。该模型似乎“欺骗”了pretext任务,并很容易找到低损失的解决方案。这可能是因为样品之间的批内通信(由BN引起)泄露了信息

我们通过变换BN来解决这个问题。我们使用多个GPU进行训练,并对每个GPU的样本独立执行BN(如常见实践所做的那样)。对于密钥编码器f_k,我们在当前的小批量中打乱采样顺序,然后将其分配给gpu(并在编码后打乱);查询编码器f_q的小批量示例顺序没有改变。这确保了用于计算查询及其正键的批统计信息来自两个不同的子集。这有效地解决了作弊问题,并使培训受益于BN。

我们在我们的方法和其端到端消融对应的方法中都使用了洗牌BN(图2a)。它与对应的内存库无关(图2b),后者不受此问题的影响,因为正键过去来自不同的小批量。

4. 实验

使用 MoCo 进行无监督训练后,固定网络不变,在其后增加一个线性层+SoftMax进行分类。后面的线性层和 softmax 是需要进行训练的。下图的结果中,随着负样本采样个数的增大准确率不断提升,且MoCo明显优于 End-to-End 和 Memory Bank 的方法。

表1。ImageNet上线性分类协议下的比较。这个图形使这张表形象化了。所有这些都在ImageNet-1M训练集上进行无监督预训练,然后在冻结特征上进行有监督线性分类,在验证集上进行评估。参数计数是特征提取器的参数计数。如果可用,我们将与改进的重新实现进行比较(在数字后面引用)。

注:R101∗/R170∗为去掉最后残差级的ResNet-101/170 [14,46,35], R170变宽[35];Rv50为可逆网[23],RX50为ResNeXt-50-32×8d[62]。

5. 讨论与结论

我们的方法在各种计算机视觉任务和数据集中显示了无监督学习的积极结果。有几个悬而未决的问题值得讨论。MoCo从IN-1M到IG-1B的改进一直很明显,但相对较小,这表明更大规模的数据可能没有得到充分利用。我们希望一个先进的pretext任务将改善这一点。除了简单的实例识别任务[61]外,还可以将MoCo用于伪装自编码等借口任务,例如语言[12]和视觉[46]。我们希望MoCo对其他涉及对比学习的pretext任务有用。

参考资料

论文下载(CVPR 2020)

https://arxiv.org/abs/1911.05722

代码地址

GitHub - facebookresearch/moco: PyTorch implementation of MoCo: https://arxiv.org/abs/1911.05722

参考文章

MoCo: Momentum Contrast 无监督学习 - 知乎

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

负载均衡集群——LVS

目录 1.LVS简介 2.LVS体系结构 3.LVS相关术语 4. LVS工作模式 5. LVS调度算法 6.LVS集群介绍 6.1 LVS-DR模式 6.2 LVS – NAT 模式 6.3 LVS – TUN 模式 7.LVS 集群构建 7.1 LVS/NAT 模式配置 实验操作步骤 步骤 1 Nginx1 和 Nginx2 配置 步骤 2 安装和配置 LVS …

Visual Studio 2019 社区版下载

一、网址 https://learn.microsoft.com/zh-cn/visualstudio/releases/2019/release-notes#start-window 二、选择这个即可

ISP图像处理pipeline简介1

ISP 是什么? ISP (Image Signal Processor),图像信号处理器,是用于摄影和视频处理的一种专用芯片。它是用来干什么的呢?简单说就是用来将图像传感器(CCD, CMOS)信号转化成可视的信号的功能,这里…

回归损失函数

目录 1 MAE 2 MSE 3 MAPE 4 Quantile Loss分位数损失 回归损失函数也可以做为评价指标使用,但是有没有想过数据分布与损失函数之间的关系呢! 使用特定损失函数的前提是我们对标签的分布进行了某种假设,在这种假设的前提下通过极大似然法推…

社交媒体数据恢复:YY语音

YY语音数据恢复指南 在我们的日常生活中,数据丢失是一种常见的现象。有时候,我们可能会不小心删除了重要的文件,或者因为硬件故障而导致数据丢失。在这种情况下,数据恢复软件可以帮助我们找回丢失的数据。本文将重点介绍如何使用Y…

一招将vscode自动补全的双引号改为单引号

打开设置,搜索quote,在结果的HTML选项下找到自动完成,设置默认引号类型即可。 vscode版本:1.88.1, vscode更新日期:2024-4-10

STM32-ADC(独立模式、双重模式)

ADC简介 18个通道:外部信号源就是16个GPIO回。在引脚上直接接模拟信号就行了,不需要侄何额外的电路。引脚就直接能测电压。2个内部信号源是内部温度传感器和内部参考电压。 逐次逼近型ADC: 它是一个独立的8位逐次逼近型ADC芯片,这个ADC0809是…

net core 程序运行报错,需要kb2533623补丁

报错大概如下: Failed to load the dll from xxxx 0x80070057 The library hostfxr.dll was found, but loading it from .xxxx\hostfxr.dll failed 目前微软官方已经停止这个补丁下载了,找个了多个网址不是带病毒就是带推广了,下面这个目前…

I2C通信的详细讲解

物理接口: SCL SDA (1)SCL(serial clock):时钟线,传输CLK信号,一般是I2C主设备向从设备提供时钟的通道。 (2)SDA(serial data):数据…

【从零开始手搓12306项目】第一阶段遇到的问题及解决方案

IDEA中datebase连接mysql失败 读取外包函数报错 注意区分private和public 找不到数据库? 一定要注意数据库的url链接,在datebase的url复制过来 xml和java对应不上? 最好复制一遍到xml文件 git忽略条件文件目录 定义Git全局的 .gitigno…

还有同学开题报告没写吗?

引言 作为一名在软件技术领域深耕多年的专业人士,我不仅在软件开发和项目部署方面积累了丰富的实践经验,更以卓越的技术实力获得了🏅30项软件著作权证书的殊荣。这些成就不仅是对我的技术专长的肯定,也是对我的创新精神和专业承诺…

Golang面试题四(GMP)

目录 1.Goroutine 定义 2.GMP 指的是什么 3.GMP模型的简介 全局队列(Global Queue) P的本地队列 P列表 M列表 4.有关P和M的个数问题 P的数量问题 M的数量问题 P和M何时会被创建 5.调度器P的设计策略 复⽤线程 work stealing机制 hand off…

Adobe将Sora、Runway、Pika,集成在PR中

4月15日晚,全球多媒体巨头Adobe在官网宣布,将OpenAI的Sora、Pika 、Runway等著名第三方文生视频模型,集成在视频剪辑软件Premiere Pro中(简称“PR”)。 同时,Adob也会将自身研发的Firefly系列模型包括视频…

Java工程师常见面试题:Java基础(一)

1、JDK 和 JRE 有什么区别? JDK是Java开发工具包,它包含了JRE和开发工具(如javac编译器和java程序运行工具等),主要用于Java程序的开发。而JRE是Java运行环境,它只包含了运行Java程序所必须的环境&#xf…

社交媒体数据恢复:钉钉

在数字化办公日益普及的今天,钉钉作为一款综合性的企业级通讯工具,已经深入到众多企业和个人的工作与生活中。然而,在日常使用过程中,我们难免会遇到一些意外情况导致数据丢失的问题。本文将针对钉钉数据恢复这一主题,…

Cisco ACI使用Postman配置交换机-未完待续

先看下不使用脚本的情况下是怎么配置交换机端口的? 例: 有10个交换机接口要开trunk,透传50个vlan, 使用GUI的操作方式为 1 进入EPG -->Static port 2 右键,绑定接口 3 选中node -->指定接口—>指定vlan —>…

意大利侍酒师Galvan Maurizia分享意大利葡萄酒与美食文化魅力

在酒水行业日益繁荣的今天,消费者对酒类产品的品质、文化和品味的追求不断提升。为了满足这一市场需求,云仓酒庄近日宣布开启首届《综合品酒师》培训,旨在培养更多具备专业素养和品鉴能力的品酒师,为酒水行业的专业化和形象提升注…

【行为型模式】观察者模式

一、观察者模式概述​ 软件系统其实有点类似观察者模式,目的:一个对象的状态或行为的变化将导致其他对象的状态或行为也发生改变,他们之间将产生联动。 观察者模式属于对象行为型: 1.定义了对象之间一种一对多的依赖关系&#xff…

翱途O2OA新手上路-服务器下载及私有云部署

本篇主要简要描述从官网下载服务器,进行部署,启动的过程,并且描述在部署过程中常见的问题与报错以及云服务器安全策略配置和O2OA服务器端口修改的方式。 O2OA部署的服务器要求不高,一般使用4C8G以上的服务器均可正常运行。 一、检…

锂电池充放电管理-单片机通用

锂电池充放电管理-单片机通用 一、锂电池充放电检测的原理二、power.c的实现三、power.h的实现四、锂电池检测和充电电路 一、锂电池充放电检测的原理 ①两节锂电池通过电阻分压检测ADC,再根据电压划分电量等级;②充电使用的是锂电池充电IC方案&#xf…
最新文章