SigLIP——采用sigmoid损失的图文预训练方式

SigLIP——采用sigmoid损失的图文预训练方式
FesianXu 20240825 at Wechat Search Team

前言

CLIP中的infoNCE损失是一种对比性损失,在SigLIP这个工作中,作者提出采用非对比性的sigmoid损失,能够更高效地进行图文预训练,本文进行介绍。如有谬误请见谅并联系指出,本文遵守CC 4.0 BY-SA版权协议,转载请联系作者并注明出处,谢谢

  • 关键字: 高效图文预训练、sigmoid损失取代softmax损失
  • paper发表信息:CVPR 2023

∇ \nabla 联系方式:

  • e-mail: FesianXu@gmail.com
  • github: https://github.com/FesianXu
  • 知乎专栏: 计算机视觉/计算机图形理论与应用
  • 微信公众号:机器学习杂货铺3号店

本博文可在github page上获得最佳体验:https://fesianxu.github.io/2024/09/08/sigmoid-language-image-pretrain-20240908/


基于对比学习的图文预训练方式,自从CLIP [1] 横空出世后,就成为了图文预训练的主流方式,引申出了一系列的工作,如ALIGN [3]、FLIP [4]、LiT [5]等。这些工作在数据使用、训练效率等上进行了探索,但是其核心损失还是采用了infoNCE,也即是对比型的损失。在SigLIP [2] 中,作者提出了基于sigmoid损失的图文预训练方式,并且指出采用sigmoid损失能带来更高效的图文预训练效率和效果。在此之前,我们有必要再复习下CLIP的基本思想。CLIP是一个双塔结构,分别有图片塔 f ( ⋅ ) f(\cdot) f()和文本塔 g ( ⋅ ) g(\cdot) g(),那么损失可以表达为式子(1),其中的 x i = f ( I i ) ∣ ∣ f ( I i ) ∣ ∣ 2 \mathbf{x}_i = \dfrac{f(I_i)}{||f(I_i)||_2} xi=∣∣f(Ii)2f(Ii) y i = g ( T i ) ∣ ∣ g ( T i ) ∣ ∣ 2 \mathbf{y}_i = \dfrac{g(T_i)}{||g(T_i)||_2} yi=∣∣g(Ti)2g(Ti),是图片特征和文本特征的L2 normalization后的结果, t = exp ⁡ ( t ′ ) t = \exp(t^{\prime}) t=exp(t)为温度系数,其中 t ′ t^{\prime} t为可学习参数, B \mathcal{B} B为一个批次(batch)的数据。
L C L I P = − 1 2 ∣ B ∣ ∑ i = 1 ∣ B ∣ ( L i 2 t + L t 2 i ) L i 2 t = log ⁡ ( exp ⁡ ( t x i ⋅ y i ) ∑ j = 1 ∣ B ∣ exp ⁡ ( t x i ⋅ y j ) ) L t 2 i = log ⁡ ( exp ⁡ ( t x i ⋅ y i ) ∑ j = 1 ∣ B ∣ exp ⁡ ( t x j ⋅ y i ) ) (1) \begin{align} \mathcal{L}_{CLIP} &= -\dfrac{1}{2|\mathcal{B}|} \sum_{i=1}^{|\mathcal{B}|}(\mathcal{L}_{i2t}+\mathcal{L}_{t2i}) \\ \mathcal{L}_{i2t} &= \log(\dfrac{\exp(t\mathbf{x}_i \cdot \mathbf{y}_i)}{\sum_{j=1}^{|\mathcal{B}|}\exp(t\mathbf{x}_i \cdot \mathbf{y}_j)}) \\ \mathcal{L}_{t2i} &= \log(\dfrac{\exp(t\mathbf{x}_i \cdot \mathbf{y}_i)}{\sum_{j=1}^{|\mathcal{B}|}\exp(t\mathbf{x}_j \cdot \mathbf{y}_i)}) \\ \end{align} \tag{1} LCLIPLi2tLt2i=2∣B1i=1B(Li2t+Lt2i)=log(j=1Bexp(txiyj)exp(txiyi))=log(j=1Bexp(txjyi)exp(txiyi))(1)
其基本思想就是从打分矩阵中,从i->tt->i的方向去判断出正样本的位置(也就是对角线的位置),注意到由于采用的是softmax形式去归一化正负样本,将其视为了概率分布,因此正负样本之间的概率关系是耦合在一起的,在提高正样本概率的同时,势必会压低负样本的概率。

在这里插入图片描述

Fig 1. CLIP的基本结构由图片塔和文本塔组成,打分矩阵的对角线为正样本,从i2t和t2i的方向分别计算infoNCE损失。

而在SigLIP中,损失函数为式子(2)所示,其中的 z i j z_{ij} zij为给定图片文本对的标签,当为成对的正样本时候 z i j = 1 z_{ij}=1 zij=1,当不是成对的负样本时候 z i j = − 1 z_{ij}=-1 zij=1。此时对于正负样本来说是解耦的,增加正样本的概率并不意味着压低负样本的概率。负样本数量的绝对占优,会导致在训练初期负样本的损失主导了整个损失,因此引入了一个可学习的偏置项 b b b去缓解初始阶段的训练困难问题,此处的 b b b在原文中被初始化为-10,这也容易理解,初始的logit减去一个较大的值(如-10),使得正负样本logit的差别相对不会很大,在正负样本数量极大不均匀的情况下,可以让初始状态更加均匀,从而不会带来过度调整。
L S i g L I P = − 1 ∣ ∣ B ∣ ∣ ∑ i = 1 ∣ B ∣ ∑ j = 1 ∣ B ∣ log ⁡ ( 1 1 + exp ⁡ ( z i j ( − t x i ⋅ y j + b ) ) ) ⏟ L i j (2) \mathcal{L}_{SigLIP} = -\dfrac{1}{||\mathcal{B}||} \sum_{i=1}^{|\mathcal{B}|} \sum_{j=1}^{|\mathcal{B}|} \underbrace{\log(\dfrac{1}{1+\exp(z_{ij}(-t\mathbf{x}_i \cdot \mathbf{y}_j+b))})}_{\mathcal{L}_{ij}} \tag{2} LSigLIP=∣∣B∣∣1i=1Bj=1BLij log(1+exp(zij(txiyj+b))1)(2)

整个损失的建模,如以下代码所示:

# img_emb : image model embedding [n, dim]
# txt_emb : text model embedding [n, dim]
# t_prime, b : learnable temperature and bias
# n : mini-batch size
t = exp(t_prime)
zimg = l2_normalize(img_emb)
ztxt = l2_normalize(txt_emb)
logits = dot(zimg, ztxt.T) * t + b
labels = 2 * eye(n) - ones(n) # -1 with diagonal 1
l = -sum(log_sigmoid(labels * logits)) / n

这个就是SigLIP的核心优化点,我们先不考虑这个建模的模型效果,先看到这种建模方式带来的模型训练的优势。

  • 在CLIP中,正负样本是pairwise建模的:由于采用的softmax函数去建模正负样本之间的关系,而CLIP训练的global batch size一般都很大(如32k),这意味着GPU #1上的正样本需要见到其他所有GPU上的样本,并以此作为负样本。因此通常都需要汇聚多节点多卡的特征向量,这个时候需要频繁地调用all_gather,带来了沉重的通信代价,也会拖慢整个训练过程的速度。
  • 在SigLIP中,正负样本是pointwise建模的:采用的sigmoid loss是独立对每个正负样本进行计算的,最后再进行loss的累加,这意味着可以在本地完成大部分的计算,在涉及到本地的正样本和其他设备的负样本进行交互计算的时候,仅需要很少的gather操作就能完成设备间向量的交换就可以(对于图文预训练来说,交换文本特征向量即可,通信代价很低),而不需要all_gather操作。

我们着重介绍下SigLIP是如何进行分布式训练的,假设全局的batch size为 B \mathcal{B} B,一共有 D D D个GPU,那么每个GPU上的batch size为 b = B / D b=\mathcal {B}/D b=B/D,可以将公式(2)的损失拆解为公式(3)所示,在Fig 2. 展示了整个过程的示意图,在初始化阶段,我们以第一个GPU为例子,其所包含的样本为:
GPU 1 Image ∈ { I 1 , I 2 , I 3 , I 4 } GPU 1 Text ∈ { T 1 , T 2 , T 3 , T 4 } \begin{aligned} \text{GPU 1}_{\text{Image}} & \in \{I_1,I_2,I_3,I_4\} \\ \text{GPU 1}_{\text{Text}} & \in \{T_1,T_2,T_3,T_4\} \\ \end{aligned} GPU 1ImageGPU 1Text{I1,I2,I3,I4}{T1,T2,T3,T4}
此时GPU 1可以完成一次公式(3)中的C计算,然后,交换GPU 1和GPU 2的文本编码器特征向量,既是:
GPU 1 Text ∈ { T 1 , T 2 , T 3 , T 4 } → GPU 1 Text ∈ { T 5 , T 6 , T 7 , T 8 } \text{GPU 1}_{\text{Text}} \in \{T_1,T_2,T_3,T_4\} \rightarrow \text{GPU 1}_{\text{Text}} \in \{T_5,T_6,T_7,T_8\} GPU 1Text{T1,T2,T3,T4}GPU 1Text{T5,T6,T7,T8}
此时GPU 2完成一次公式(3)中的B计算,以此类推,直到GPU 1遍历完所有样本为止,其他GPU也是如此操作的,最终把所有卡上的损失汇聚即可,也就是A计算。这个轮流交换不同GPU之间数据的操作,可以称之为permutation。不难发现,整个过程的通信成本来自于permutation,一共需要 D − 1 D-1 D1gather操作即可完成一次permutation,而在CLIP中需要对图文的编码器特征都进行汇聚,因此需要2次all-gather操作。如果all-gather采用ring的话,那么一个all-gather就是 D − 1 D-1 D1gather操作。由此我们得出一个SigLIP和CLIP的性能复杂度对比:

通信复杂度单卡储存复杂度单卡计算复杂度(计算 exp ⁡ ( ⋯   ) \exp(\cdots) exp()的数量)单卡计算复杂度(计算 x i ⋅ y j \mathbf{x}_i \cdot \mathbf{y_j} xiyj的数量)
CLIP 2 ( D − 1 ) 2(D-1) 2(D1) B 2 \mathcal{B}^2 B2 B 2 \mathcal{B}^2 B2 B 2 \mathcal{B}^2 B2
SigLIP D − 1 D-1 D1 ( B / D ) 2 (\mathcal{B}/D)^2 (B/D)2 ( B / D ) 2 × D = B 2 / D (\mathcal{B}/D)^2 \times D = \mathcal{B}^2/D (B/D)2×D=B2/D ( B / D ) 2 × D = B 2 / D (\mathcal{B}/D)^2 \times D = \mathcal{B}^2/D (B/D)2×D=B2/D

容易发现,SigLIP无论从通信复杂度,储存复杂度还是计算复杂度上,都远比CLIP更为优越。
L = − 1 ∣ B ∣ ∑ d 1 = 1 D ⏟ A : ∀  GPU  d i ∑ d j = 1 D ⏞ B : 在GPU之间交换负样本 ∑ i = b d i b ( d i + 1 ) ⏟ 所有本地的正样本 ∑ j = b d j b ( d j + 1 ) ⏟ 负样本 L i j ⏞ C : 每个GPU上的损失 (3) \mathcal{L} = -\dfrac{1}{|\mathcal{B}|} \underbrace{\sum_{d_1=1}^D}_{A: \forall \text{ GPU } d_i} \overbrace{\sum_{d_j=1}^D}^{B: \text{在GPU之间交换负样本}} \overbrace{\underbrace{\sum_{i=bd_i}^{b(d_i+1)}}_{所有本地的正样本} \underbrace{\sum_{j=bd_j}^{b(d_j+1)}}_{负样本} \mathcal{L}_{ij}}^{C: \text{每个GPU上的损失}} \tag{3} L=B1A: GPU di d1=1Ddj=1D B:GPU之间交换负样本所有本地的正样本 i=bdib(di+1)负样本 j=bdjb(dj+1)Lij C:每个GPU上的损失(3)

在这里插入图片描述

Fig 2. SigLIP高效的损失计算示意图,假设有3个设备,每个设备上的batch size为4,global batch size为12。

让我们再关注到SigLIP的模型能力表现,作者主要对比的是SigLIP,以及将图片表征固定的SigLiT(从而可以将batch size设置到非常大,比如100万)以及CLIP的表现。我们都知道在CLIP中采用对比损失,意味着越大的batch size能极大地提高对比效率,从而提升效果,受限于softmax的内存占用情况和GPU卡数等原因,无法将batch size设置得很大,在SigLiT中则可以将batch size设置到百万以上,从而探索极大batch size情况下的收益。如Fig 3.所示,作者对比了三种模型在batch size进行尺度放大后的0-shot能力,训练量都是18B的数据量,容易发现几点结论:

  1. 在batch size小于32k的时候,采用sigmoid的SigLIP的性能都会优于采用softmax的CLIP。
  2. 在batch size足够大的时候,CLIP能够追上,甚至超越SigLIP的表现,但是最佳性能仍然是SigLIP@32k情况下得到,从实际应用来看,采用SigLIP能用更少的资源更快的训练速度得到更好的性能。
  3. 从SigLiT的实验来看,随着batch size的尺度放大性能将会在32k batch size的情况下达到饱和,同时能观察到SigLiT在不同batch size下持续优于LiT。继续提高batch size将不能带来更好的收益,甚至会有细微的性能下降。

在这里插入图片描述

Fig 3. SigLiT、SigLIP和CLIP在batch size进行尺度放大情况下的0-shot性能对比。

超大的batch size是否需要更长的训练量支持?作者在SigLiT的设置下,训练了更长时间(也即是见了更多数据量),如Fig 4.所示,在超大batch size,如262k的情况下,对比较小batch size(如8k)提供更长的训练时间的确能观察到性能的较大提升。并且也能观察到在超大batch size下,采用sigmoid和采用softmax的区别很小,但是在较小batch size(如8k)的情况下,则差距明显。因此,在资源受限的情况下,采用SigLIP是很划算的,所需资源少而且性能更强。同时,这个试验也说明了,超大的batch size并不意味着训练得更快,反而还需要更长的训练时间。

在这里插入图片描述

Fig 4. 扩大了见过的数据量后,越大的batch size能带来较为明显的性能提升,同时,可以观察到在超大batch size下,采用sigmoid和采用softmax的区别很小,但是在较小batch size(如8k)的情况下,则差距明显。

除了batch size的影响外,作者还探索了很多有趣的点,包括SigLIP在多语言数据集上的表现、大尺度batch size下的训练稳定性问题、训练中负样本比例的影响、sigmoid训练的鲁棒性探索等问题。在多语言数据集上,作者发现性能同样在32k batch size上达到了饱和,其他细节就不累述了,感兴趣的读者自行翻阅。

笔者比较感兴趣的是其他问题,比如在大尺度batch size下,训练容易出现不稳定的情况,这个原因在于在训练过程中,gradient norm会出现大幅度的尖峰,如Fig 5. 所示,这导致了参数和训练损失的不稳定(也即是尖峰),作者观察到,如果将Adam优化器的 β 2 \beta_2 β2值从0.999下降到0.95,那么训练过程就会稳定下来。

在这里插入图片描述

Fig 5. 大尺度batch size下训练容易出现不稳定的情况。

从公式(2)中,注意到SigLIP是对所有正负样本的pair进行计算损失然后累加求和的,这意味着可以从中剔除掉负样本以控制负样本的比例。对于batch size为 ∣ B ∣ |\mathcal{B}| B的一次损失计算而言,其中有 ∣ B ∣ |\mathcal{B}| B个正样本,有 ∣ B ∣ 2 − ∣ B ∣ |\mathcal{B}|^2-|\mathcal{B}| B2B个负样本,负样本其实在后期很多都是简单负样本,是否可以剔除简单负样本是一个值得探究的问题。作者提出了几种消融试验,去挑选负样本,从而控制正负样本比例:

  • 随机:随机挑选负样本对,并且对其进行剔除。
  • 难负样本:把难负样本保留下来,即是通过将最高打分的topk负样本保留下来。
  • 简单负样本:把简单负样本保留下来,即是将打分最低的lowk负样本保留下来。
  • 难负样本+对齐训练量:保留难负样本的同时,提高训练step数量以对齐训练数据量。

实验结果如Fig 6.所示,其中的横坐标为一个batch内的正样本数量:负样本数量,其中的1:16k则是不进行任何负样本剔除的基线,从实验中可以得出几个结论:

  • 只保留简单负样本,会使得模型性能完全崩溃。
  • 随机剔除负样本,也会损失模型性能。
  • 只保留难负样本,对模型性能的损失是最小的,在对齐了训练数据量后(因为剔除了负样本,同个step下模型讲过的数据对数量就少了,因此需要训练更多step去对齐训练数据量),性能甚至还能比基线更好,这说明了难负样本才是最有价值的,怎么去合理地挑选难负样本是提高模型性能的关键因素
  • 再看到随着负样本数量的减少,可学习偏置 b b b的值和正负样本的平均logit值都在递增,这也是符合预期的。有趣的一点是,当采用难负例保留的策略中,随着负样本数量逐渐减少,正负例的logit区分度在加速减少,并且正例的logit变化基本上是平坦的,这个现象和随机丢弃的策略是不同的。对此的解释是,本来难负样本和正样本就比较接近,在减少了负样本数量,只保留最难的负样本后,负样本的logit值就加速地上涨,从而导致了区分度减低的情况,这也是符合预期的。

在这里插入图片描述

Fig 6. 采用不同策略控制损失中的正负样本比例的效果对比。

前文已经提到了sigmoid和softmax的区别在于,前者解耦了正负样本的概率关系,这使得即便负样本中出现假阴性样本,也只会影响自己的损失,而不会影响到其他样本,因此这带来了数据的健壮性。作者也进行了对应的试验,如Fig 7.所示,作者对数据中的图片、文本进行随机加噪、对batch内的图文对进行随机打乱、或者将上面的加噪方式都进行组合,发现基于sigmoid的训练过程,总是比基于softmax的训练过程更加鲁棒。

在这里插入图片描述

Fig 7. 基于sigmoid的训练能够提高训练的健壮性,对数据中的噪声更为鲁棒。

总的来说,SigLIP是一个很棒的工作,作者采用sigmoid损失去取代对比学习中的softmax函数,以更小的资源开销带来了更好的模型表现,目前也被很多多模态大模型所采用,作为视觉端的编码器。

Reference

[1]. Radford, A., Kim, J. W., Hallacy, C., Ramesh, A., Goh, G., Agarwal, S., … & Sutskever, I. (2021, July). Learning transferable visual models from natural language supervision. In International Conference on Machine Learning (pp. 8748-8763). PMLR. aka CLIP

[2]. Zhai, Xiaohua, Basil Mustafa, Alexander Kolesnikov, and Lucas Beyer. “Sigmoid loss for language image pre-training.” In Proceedings of the IEEE/CVF International Conference on Computer Vision, pp. 11975-11986. 2023. aka SigLIP

[3]. Jia, C., Yang, Y., Xia, Y., Chen, Y. T., Parekh, Z., Pham, H., … & Duerig, T. (2021, July). Scaling up visual and vision-language representation learning with noisy text supervision. In International Conference on Machine Learning (pp. 4904-4916). PMLR. Short for ALIGN

[4]. Li, Y., Fan, H., Hu, R., Feichtenhofer, C., & He, K. (2022). Scaling Language-Image Pre-training via Masking. arXiv preprint arXiv:2212.00794. aka FLIP

[5]. Zhai, X., Wang, X., Mustafa, B., Steiner, A., Keysers, D., Kolesnikov, A., & Beyer, L. (2022). Lit: Zero-shot transfer with locked-image text tuning. In Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (pp. 18123-18133). aka LiT

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

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

相关文章

93. UE5 GAS RPG 应用负面效果表现

在上一篇文章里,我们实现了添加负面效果GE,并且在添加GE时,也会给角色应用一个负面效果标签作为标识。在这一篇里,我们将通过负面效果标签标识,应用角色身上展现对应的负面效果的表现。 我们将在这篇文章里添加一个自定…

【c++进阶[五]】list相关接口介绍及list和vector的对比

💓博主CSDN主页::Am心若依旧💓 ⏩专栏分类c从入门到精通⏪ 🚚代码仓库:青酒余成🚚 🌹关注我🫵带你学习更多c   🔝🔝 1.前言 本章重点 本章重点讲解list的接口函数的熟悉&#xf…

Linux-RPM与YUM

目录 前言: rpm包的管理 rpm包的简单查询指令 ​编辑 rpm包名的基本格式 rpm包名基本格式 ​编辑 卸载rpm包 细节问题 安装rpm包 yum yum的基本指令 安装指定的yum包 yum报错 问题描述: 解决方法: 前言: Linux操…

电脑硬盘数据丢失了怎么恢复?简单实用的硬盘数据找回的方法

我们的电脑使用硬盘作为存储设备来保存数据,硬盘里的数据是存储在扇区上,这些存储数据的单元则位于表面有磁性材料的旋转的盘片上。硬盘内部的磁头悬浮于高速旋转的盘片上,用于读写和检索数据。 假如我们使用电脑时不小心删除了某个文件&…

Vue3使用Uni-ui的popup弹出层组件

由于uni-ui中有些组件文档的基于vue2编写的,比如popup组件 下面是vue3的写法 除了文档中要求的aleterDialog外,还得利用v-if设置一个isDialog判断 // template // script 解决

Linux基础2-权限2(操作权限,粘滞位,umask,目录文件的rwx权限)

上篇内容:Linux基础2-权限1(用户,权限是什么?)-CSDN博客 目录 一. 权限的操作(命令) 1.1 chmod 1.2 chown 1.3 chgrp 二. 粘滞位 三. umask(遮掩码) 四. 目录文件的 r w x 权限 一. 权限…

Ubuntu22.04版本左右,开机自动启动脚本

Ubuntu22.04版本左右,开机自动启动脚本 1. 新增/lib/systemd/system/rc-local.service中[Install]内容 vim /lib/systemd/system/rc-local.service 按 i 进入插入模式后,新增内容如下: [Install] WantedBymulti-user.target Aliasrc-local.…

如何读.Net Framework 的源码?

.Net Framework的源码可以从这里下载 Download 也可以在线直接浏览 https://referencesource.microsoft.com 这里我们以System.IO.Directory.CreateDirectory函数为例,来说明如何去读.Net Framework的源码。 在ReferenceSource在线界面的搜索框里输入Directory.Cr…

分享基于PDF.JS的移动端PDF阅读器代码

一、前言 在之前的文章《分享基于PDF.js的pdf阅读器代码》里提到了PC端基于PDF.js的阅读器,本文将提供针对移动端的版本。 二、pdfViewer 为了能够直接使用,这里分享一下经过简单修改后能直接使用的pdfViewer代码: pdfViewer代码目录&…

CAN总线的位同步详细讲解

接收方数据采样 (1)CAN总线没有时钟线,总线上的所有设备通过约定波特率的方式确定每一个数据位的时长 (2)发送方以约定的位时长每隔固定时间输出一个数据位 (3)接收方以约定的位时长每隔固定…

Kafka 分布式消息系统详细介绍

Kafka 分布式消息系统 一、Kafka 概述1.1 Kafka 定义1.2 Kafka 设计目标1.3 Kafka 特点 二、Kafka 架构设计2.1 基本架构2.2 Topic 和 Partition2.3 消费者和消费者组2.4 Replica 副本 三、Kafka 分布式集群搭建3.1 下载解压3.1.1 上传解压 3.2 修改 Kafka 配置文件3.2.1 修改z…

[网络原理]关于网络的基本概念 及 协议

文章目录 一. 关于网络的概念介绍1. 局域⽹LAN2. ⼴域⽹WAN3. 主机4. 路由器5. 交换机IP地址端口号 二. 协议协议分层TCP/IP五层模型(或四层)OSI七层模型封装分用 一. 关于网络的概念介绍 1. 局域⽹LAN 局域⽹,即 Local Area Network,简称LAN。 Local …

NGINX开启HTTP3,给web应用提个速

环境说明 linuxdockernginx版本:1.27 HTTP3/QUIC介绍 HTTP3是由IETF于2022年发布的一个标准,文档地址为:https://datatracker.ietf.org/doc/html/rfc9114 如rfc9114所述,http3主要基于QUIC协议实现,在具备高性能的同时又兼备了…

TCP远程命令执行

目录 一. 命令集 二. 命令执行模块实现 三. 服务端模块实现 四. 服务端调用模块实现 五. 客户端模块实现 六. 效果展示 此篇教大家如何利用TCP进行远程命令执行。 一. 命令集 将值得信任的命令放进一个txt文件中,执行命令时,就去这…

elementUI根据列表id进行列合并@莫成尘

本文章提供了elementUI根据列表id进行列合并的demo&#xff0c;效果如图&#xff08;可直接复制代码粘贴&#xff09; <template><div id"app"><el-table border :data"tableList" style"width: 100%" :span-method"objectS…

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍&#xff11;、W25Q64简介&am…

小怡分享之数据结构LinkedList与链表

前言&#xff1a; &#x1f308;✨前面小怡给大家介绍了ArrayList&#xff0c;今天小怡给大家介绍一下链表。 1.ArrayList的缺陷 当在ArrayList任意位置插入或者删除元素时&#xff0c;就需要后续元素整体往前或者往后搬移&#xff0c;时间复杂度为O&#xff08;n&#xff09;…

网恋照妖镜源码搭建教程

文章目录 前言原理创建网站1.打开网站设置 配置ssl2.要打开强制HTTPS&#xff0c;用宝塔免费的ssl证书即可&#xff0c;也可以使用其他证书&#xff0c;必须是与域名匹配的3.上传文件至根目录进行解压4.解压后&#xff0c;修改文件 sc.php 里面的内容5.其余探索 结语 前言 前俩…

Excel和Word日常使用记录:

Excel使用总结 表格颜色填充&#xff1a; 合并单元格&#xff1a; 选中你要合并的单元格区域。 按下快捷键 Alt H&#xff0c;然后松开这些键。 再按下 M&#xff0c;接着按 C。 这个组合键执行的操作是&#xff1a;Alt H&#xff1a;打开“主页”选项卡。 M&#xff1a;选…

“阡陌云旅”黄河九省文化旅游平台

“阡陌云旅”黄河九省文化旅游平台 GitHub地址&#xff1a;https://github.com/guoJiaQi-123/Yellow-River-Cloud-Journey 项目背景 “阡陌云旅”黄河九省文化旅游平台 “阡陌云旅” 黄河九省文化旅游平台是一个专注于黄河流域九省文化旅游资源整合与推广的项目。 黄河是中…