【模型量化】神经网络量化基础及代码学习总结

news2024/11/26 10:25:16

1 量化的介绍

量化是减少神经网络计算时间和能耗的最有效的方法之一。在神经网络量化中,权重和激活张量存储在比训练时通常使用的16-bit或32-bit更低的比特精度。当从32-bit降低到8-bit,存储张量的内存开销减少了4倍,矩阵乘法的计算成本则二次地减少了16倍。
神经网络已被证明对量化具有鲁棒性,这意味着它们可以被量化到较低的位宽,而对网络精度的影响相对较小。然而,神经网络的量化并不是自由的。低位宽量化会给网络带来噪声,从而导致精度的下降。虽然一些网络对这种噪声具有鲁棒性,但其他网络需要额外的工作来利用量化的好处。

量化实际上是将FLOAT32(32位浮点数)的参数量化到更低精度,精度的变化并不是简单的强制类型转换,而是为不同精度数据之间建立一种数据映射关系,最常见的就是定点与浮点之间的映射关系,使得以较小的精度损失代价得到较好的收益。

2 均匀仿射量化

均匀仿射量化也称为非对称量化,定义如下:
s s s:放缩因子(scale factor)/量化步长(step size),是浮点数
z z z:零点(zero-point),是整数,保证真实的0不会有量化误差,对ReLU和zero-padding很重要
b b b:位宽(bit-width),是整数,比如2, 4, 6, 8
s s s z z z的作用是将浮点数转化为整数,范围由b来定

1)将真实输入的浮点数 x \mathbb x x转化为无符号整数:
x i n t = c l a m p ( ⌊ x s ⌉ + z ; 0 , 2 b − 1 ) \mathbf{x}_{int} = \mathrm{clamp}(\lfloor\frac{\mathbf{x}}{s}\rceil+z; 0, 2^b-1) xint=clamp(⌊sx+z;0,2b1)

截断/四舍五入函数的定义:
c l a m p ( x ; a , c ) = { a , x < a , x , a ≤ x ≤ b , b , x > c . \mathrm{clamp}(x; a, c) = \begin{cases} a, x < a, \\ x, a \leq x\leq b,\\ b, x>c. \end{cases} clamp(x;a,c)= a,x<a,x,axb,b,x>c.

2)反量化(de-quantization)近似真实的输入 x \mathbf x x
x ≈ x ^ = s ( x i n t − z ) \mathbf x\approx \mathbf{\hat x} =s(\mathbf x_{int} -z) xx^=s(xintz)

结合以上1)2)步骤,得到如下量化函数的普遍定义:
x ^ = q ( x ; s , z , b ) = s ( c l a m p ( ⌊ x s ⌉ + z ; 0 , 2 b − 1 ) − z ) \mathbf{\hat x}=q(\mathbf x; s, z, b)=s(\mathrm{clamp}(\lfloor\frac{\mathbf{x}}{s}\rceil+z; 0, 2^b-1)-z) x^=q(x;s,z,b)=s(clamp(⌊sx+z;0,2b1)z)

可以发现,量化函数包含了1)中的“浮点转整数”以及“反量化近似浮点”两个过程,这个过程通常被称为 伪量化(fake quantization)操作。
对伪量化的理解:把输入的浮点数据量化到整数,再反量化回 浮点数,以此来模拟量化误差,同时在反向传播的时候,采用Straight-Through-Estimator (STE)把导数回传到前面的层。

由上面的公式,有两个误差概念:
1) 截断误差(clipping error):浮点数 x x x超过量化范围时,会被截断,产生误差
2)舍入误差(rounding error):在做 ⌊ ⋅ ⌉ \lfloor \cdot\rceil 时,会产生四舍五入的误差,误差范围在 [ − 1 2 , 1 2 ] [-\frac{1}{2}, \frac{1}{2}] [21,21]
为了权衡两种误差,就需要设计合适的s和z,而它们依赖于量化范围和精度。

根据反量化过程,我们设 整数格 上的最大和最小值分别是 Q P = q m a x / s , Q N = q m i n / 2 Q_P=q_{max}/s, Q_N=q_{min}/2 QP=qmax/s,QN=qmin/2量化值(浮点) 范围为 ( q m i n , q m a x ) (q_{min}, q_{max}) (qmin,qmax),其中 q m i n = s Q P = s ( 0 − z ) = − s z , q m a x = s Q N = s ( 2 b − 1 − z ) q_{min}=sQ_P=s(0-z)=-sz, q_{max}=sQ_N=s(2^b-1-z) qmin=sQP=s(0z)=sz,qmax=sQN=s(2b1z) x \mathbf x x超过这个范围会被截断,产生截断误差,如果希望减小截断误差,可以增大s的值,但是增大s会增大舍入误差,因为舍入误差的范围是 [ − 1 2 s , 1 2 s ] [-\frac{1}{2}s, \frac{1}{2}s] [21s,21s]

怎么计算放缩因子 s s s
s = q m a x − q m i n 2 b − 1 . s=\frac{q_{max}-q_{min}}{2^b-1}. s=2b1qmaxqmin.

2.1 对称均匀量化

对称均匀量化是上面非对称量化的简化版,限制了放缩因子 z = 0 z=0 z=0,但是偏移量的缺失限制了整数和浮点域之间的映射。

反量化(de-quantization)近似真实的输入 x \mathbf x x
x ≈ x ^ = s x i n t x\approx \hat x =s\mathbf x_{int} xx^=sxint

将真实输入的浮点数 x \mathbb x x转化为无符号整数:
x i n t = c l a m p ( ⌊ x s ⌉ ; 0 , 2 b − 1 ) \mathbf{x}_{int} = \mathrm{clamp}(\lfloor\frac{\mathbf{x}}{s}\rceil; 0, 2^b-1) xint=clamp(⌊sx;0,2b1)

将真实输入的浮点数 x \mathbb x x转化为有符号整数:
x i n t = c l a m p ( ⌊ x s ⌉ ; − 2 b , 2 b − 1 ) \mathbf{x}_{int} = \mathrm{clamp}(\lfloor\frac{\mathbf{x}}{s}\rceil; -2^b, 2^b-1) xint=clamp(⌊sx;2b,2b1)

在这里插入图片描述

在这里插入图片描述

坐标轴上方(蓝色)表示整数量化格,下方(黑色)表示浮点格。可以很清楚地看到,放缩因子 s s s就是量化的步长(step size), s x i n t s\mathbf x_{int} sxint是反量化近似真实浮点数。

2.2 Power-of-two量化(2的幂)

Power-of-two量化是对称量化的特例,放缩因子被限制到2的幂, s = 2 − k s=2^{-k} s=2k,这对硬件是高效的,因为放缩 s s s相当于简单的比特移位操作(bit-shifting)。

2.3 量化的粒度

1)Per-tensor(张量粒度):神经网络中最常用,硬件实现简单,累加结果都用同样的放缩因子 s w s x s_ws_x swsx
2)Per-channel(通道粒度):更细粒度以提升模型性能,比如对于权重的不同输出通道采用不同的量化
3)Per-group(分组粒度)

3 量化模拟过程/伪量化

量化模拟:为了测试神经网络在量化设备上的运行效果,我们经常在用于训练神经网络的相同通用硬件上模拟量化行为。
我们的目的:使用浮点硬件来近似的定点运算。
优势:与在实际的量化硬件上实验或在使用量化的卷积核上实验相比,这种模拟明显更容易实现

在这里插入图片描述

(a)在设备推理过程中,对硬件的所有输入(偏置、权重和输入激活)都是定点格式
(b)然而,当我们使用通用的深度学习框架和通用硬件来模拟量化时,这些量都是以浮点格式表示的。这就是为什么我们在计算图中引入量化器块来诱导量化效应的原因

值得注意的是:
1)每个量化器都由一组量化参数(放缩因子、零点、位宽)来定义
2)量化器的输入和输出都是浮点格式,但输出都在量化网格上
3)每个量化器都由该公式计算: x ^ = q ( x ; s , z , b ) = s ( c l a m p ( ⌊ x s ⌉ + z ; 0 , 2 b − 1 ) − z ) \mathbf{\hat x}=q(\mathbf x; s, z, b)=s(\mathrm{clamp}(\lfloor\frac{\mathbf{x}}{s}\rceil+z; 0, 2^b-1)-z) x^=q(x;s,z,b)=s(clamp(⌊sx+z;0,2b1)z),也就是包含了反量化过程
4)模拟量化实际上还是在浮点数上计算,模拟的其实是(截断与舍入)误差

4 基于STE的反向传播优化过程

严峻的优化问题:量化公式中中的round函数的梯度要么为零,要么到处都不定义,这使得基于梯度的训练不可能进行。一种解决方案就是采用straight-through estimator (STE)方法将round函数的梯度近似为1:
∂ ⌊ y ⌉ ∂ y = 1 \frac{\partial \lfloor y\rceil}{\partial y}=1 yy=1

于是,量化的梯度就可求了,现对输入 x \mathbf x x进行求导:
∂ x ^ ∂ x = ∂ q ( x ) ∂ x        = ∂ c l a m p ( ⌊ x s ⌉ ; Q N , Q P ) s ∂ x        = { s ∂ Q N ∂ x = 0 , x < q m i n , s ∂ ⌊ x / s ⌉ ∂ x = s ∂ ⌊ x / s ⌉ ∂ ( x / s ) ∂ ( x / s ) ∂ x = s ⋅ 1 ⋅ 1 s = 1 , q m i n ≤ x ≤ q m a x , s ∂ Q P ∂ x = 0 , x > q m a x .        = { 0 , x < q m i n , 1 , q m i n ≤ x ≤ q m a x , 0 , x > q m a x . \frac{\partial\mathbf{\hat x}}{\partial\mathbf x}=\frac{\partial q(\mathbf x)}{\partial\mathbf x}\\~~~~~~=\frac{\partial \mathrm{clamp}(\lfloor\frac{\mathbf x}{s}\rceil; Q_N, Q_P)s}{\partial\mathbf x}\\~~~~~~=\begin{cases} s\frac{\partial Q_N}{\partial \mathbf x}=0, \mathbf x < q_{min}, \\ s\frac{\partial \lfloor \mathbf x/s\rceil}{\partial \mathbf x}=s\frac{\partial \lfloor \mathbf x/s\rceil}{\partial (\mathbf x/s)}\frac{\partial (\mathbf x/s)}{\partial \mathbf x}=s\cdot 1\cdot \frac{1}{s}=1, q_{min} \leq x\leq q_{max},\\ s\frac{\partial Q_P}{\partial \mathbf x}=0, x>q_{max}. \end{cases}\\~~~~~~=\begin{cases} 0, \mathbf x < q_{min}, \\ 1, q_{min} \leq \mathbf x\leq q_{max},\\ 0, \mathbf x>q_{max}. \end{cases} xx^=xq(x)      =xclamp(⌊sx;QN,QP)s      = sxQN=0,x<qmin,sxx/s=s(x/s)x/sx(x/s)=s1s1=1,qminxqmax,sxQP=0,x>qmax.      = 0,x<qmin,1,qminxqmax,0,x>qmax.
也就是说,根据STE方法,当输入 x \mathbf x x在量化范围内时,其量化值对真实浮点值的梯度为1,反之为0。
s s s求导的数学推导过程如下文中LSQ工作所示。
下图展示了基于STE的反向传播过程,计算时有效跳过了量化器。
在这里插入图片描述

5 经典量化工作

Learned Step Size Quantization (ICLR 2020)

顾名思义,LSQ这篇文章就是在上述介绍的伪量化中引入可学习/训练的放缩因子 s s s
设clamp的在 整数格 上的最大和最小值分别是 Q P = q m a x / s , Q N = q m i n / 2 Q_P=q_{max}/s, Q_N=q_{min}/2 QP=qmax/s,QN=qmin/2

x ^ = s ( c l a m p ( ⌊ x s ⌉ ; Q N , Q P ) )      = { s Q N , x s < Q N , s ⌊ x s ⌉ , Q N ≤ x s ≤ Q P , s Q P , x s > Q P . \hat x=s(\mathrm{clamp}(\lfloor\frac{\mathbf{x}}{s}\rceil; Q_N, Q_P))\\~~~~=\begin{cases} sQ_N, \frac{\mathbf{x}}{s} < Q_N, \\ s\lfloor\frac{\mathbf{x}}{s}\rceil, Q_N \leq \frac{\mathbf{x}}{s}\leq Q_P,\\ sQ_P, \frac{\mathbf{x}}{s}>Q_P. \end{cases} x^=s(clamp(⌊sx;QN,QP))    = sQN,sx<QN,ssx,QNsxQP,sQP,sx>QP.

x ^ \mathbf{\hat x} x^ s s s求导有:
∂ x ^ ∂ s = { Q N , x s < Q N , ⌊ x s ⌉ + s ∂ ⌊ x s ⌉ ∂ s , Q N ≤ x s ≤ Q P , Q P , x s > Q P . \frac{\partial\mathbf{\hat x}}{\partial s}=\begin{cases} Q_N, \frac{\mathbf{x}}{s} < Q_N, \\ \lfloor\frac{\mathbf{x}}{s}\rceil + s\frac{\partial\lfloor\frac{\mathbf{x}}{s}\rceil}{\partial s}, Q_N \leq \frac{\mathbf{x}}{s}\leq Q_P,\\ Q_P, \frac{\mathbf{x}}{s}>Q_P. \end{cases} sx^= QN,sx<QN,sx+sssx,QNsxQP,QP,sx>QP.
其中, Q N , Q P , ⌊ x s ⌉ Q_N, Q_P, \lfloor\frac{\mathbf{x}}{s}\rceil QN,QP,sx都可以直接得到,但是 s ∂ ⌊ x s ⌉ ∂ s s\frac{\partial\lfloor\frac{\mathbf{x}}{s}\rceil}{\partial s} sssx就不那么好算了。

根据STE,将round函数梯度近似为一个直通操作:
s ∂ ⌊ x s ⌉ ∂ s = s ∂ x s ∂ s = − s x s 2 = − x s s\frac{\partial\lfloor\frac{\mathbf{x}}{s}\rceil}{\partial s}=s\frac{\partial\frac{\mathbf{x}}{s}}{\partial s}=-s\frac{\mathbf x}{s^2}=-\frac{\mathbf x}{s} sssx=sssx=ss2x=sx

于是,得到LSQ原文中的导数值:
∂ x ^ ∂ s = { Q N , x s < Q N , ⌊ x s ⌉ − x s , Q N ≤ x s ≤ Q P , Q P , x s > Q P . \frac{\partial\mathbf{\hat x}}{\partial s}=\begin{cases} Q_N, \frac{\mathbf{x}}{s} < Q_N, \\ \lfloor\frac{\mathbf{x}}{s}\rceil - \frac{\mathbf x}{s}, Q_N \leq \frac{\mathbf{x}}{s}\leq Q_P,\\ Q_P, \frac{\mathbf{x}}{s}>Q_P. \end{cases} sx^= QN,sx<QN,sxsx,QNsxQP,QP,sx>QP.

在LSQ中,每层的权重和激活值都有不同的 s s s,被初始化为 2 ⟨ ∣ x ∣ ⟩ Q P \frac{2\langle| \mathbf x|\rangle}{\sqrt{Q_P}} QP 2x

计算 s s s的梯度时,还需要兼顾模型权重的梯度,二者差异不能过大,LSQ定义了如下比例:
R = ∇ s L s / ∣ ∣ ∇ w L ∣ ∣ ∣ ∣ w ∣ ∣ → 1 R=\frac{\nabla_sL}{s}/\frac{||\nabla_wL||}{||w||}\rightarrow1 R=ssL/∣∣w∣∣∣∣wL∣∣1
为了保持训练的稳定,LSQ在 s s s的梯度上还乘了一个梯度缩放系数 g g g,对于权重, g = 1 / N W Q P g=1/\sqrt{N_WQ_P} g=1/NWQP ,对于激活, g = 1 / N F Q P g=1/\sqrt{N_FQ_P} g=1/NFQP 。其中, N W N_W NW是一层中的权重的大小, N F N_F NF是一层中的特征的大小。

代码实现
参考:LSQuantization复现

import torch
import torch.nn.functional as F
import math
from torch.autograd import Variable

class FunLSQ(torch.autograd.Function):
    @staticmethod
    def forward(ctx, weight, alpha, g, Qn, Qp):
        assert alpha > 0, 'alpha = {}'.format(alpha)
        ctx.save_for_backward(weight, alpha)
        ctx.other = g, Qn, Qp
        q_w = (weight / alpha).round().clamp(Qn, Qp)  # round+clamp将FP转化为int
        w_q = q_w * alpha  # 乘scale重量化回FP
        return w_q

    @staticmethod
    def backward(ctx, grad_weight):
        weight, alpha = ctx.saved_tensors
        g, Qn, Qp = ctx.other
        q_w = weight / alpha
        indicate_small = (q_w < Qn).float()
        indicate_big = (q_w > Qp).float()
        indicate_middle = torch.ones(indicate_small.shape).to(indicate_small.device) - indicate_small - indicate_big
        grad_alpha = ((indicate_small * Qn + indicate_big * Qp + indicate_middle * (
                -q_w + q_w.round())) * grad_weight * g).sum().unsqueeze(dim=0)  # 计算s梯度时的判断语句
        grad_weight = indicate_middle * grad_weight
        return grad_weight, grad_alpha, None, None, None

nbits = 4
Qn = -2 ** (nbits - 1)
Qp = 2 ** (nbits - 1) - 1
g = 1.0 / 2

2 LSQ+: Improving low-bit quantization through learnable offsets and better initialization (CVPR 2020)

LSQ+和LSQ非常相似,就放在一起讲了。LSQ在LSQ+的基础上,引入了可学习的offset,也就是零点 z z z,其定义如下:
x i n t = c l a m p ( ⌊ x − β s ⌉ ; Q N , Q P ) \mathbf x_{int}=\mathrm{clamp}(\lfloor\frac{\mathbf{x-\beta}}{s}\rceil; Q_N, Q_P) xint=clamp(⌊sxβ;QN,QP)
x ^ = s x i n t + β \mathbf{\hat x}=s\mathbf x_{int}+\beta x^=sxint+β
然后按照LSQ的方式对 s , β s,\beta s,β求偏导数进行优化。

3 XNOR-Net: ImageNet Classification Using Binary Convolutional Neural Networks

算是非常早期将二值(1-bit)表示引入神经网络的文章了,本文提出两种近似:

1)Binary-Weight-Network:只有权重是1-bit

对于输入 I \mathbf I I,我们用二值滤波器 B ∈ { + 1 , − 1 } \mathbf B\in \{+1, -1\} B{+1,1}和一个放缩因子 α \alpha α来近似真实浮点滤波器 W \mathbf W W W ≈ α B \mathbf W\approx \alpha \mathbf B WαB,于是卷积的计算可以近似为:
I ∗ W ≈ ( I ⊕ B ) α \mathbf I*\mathbf W\approx (\mathbf I\oplus \mathbf B)\alpha IW(IB)α
如何优化二值权重?我们的目标是找到 W = α B \mathbf W=\alpha \mathbf B W=αB的最优估计,解决如下优化问题:
J ( B , α ) = ∣ ∣ W − α B ∣ ∣ 2      α ∗ , B ∗ = a r g m i n α , B J ( B , α ) J(\mathbf B, \alpha)=||\mathbf W-\alpha \mathbf B||^2~~~~\alpha^*, \mathbf B^*=\mathrm{argmin_{\alpha, \mathbf B}}J(\mathbf B, \alpha) J(B,α)=∣∣WαB2    α,B=argminα,BJ(B,α)
展开后得到:
在这里插入图片描述

其中, B ⊤ B , W ⊤ W \mathbf B^\top \mathbf B, \mathbf W^\top \mathbf W BB,WW都是常数,因此优化目标集中在第二项 W ⊤ B \mathbf W^\top \mathbf B WB上:

在这里插入图片描述

这个优化问题的解可以是使 B = + 1 ( W ≥ 0 ) , B = − 1 ( W < 0 ) \mathbf B=+1(\mathbf W\geq 0), \mathbf B=-1(\mathbf W< 0) B=+1(W0),B=1(W<0),原因是这样可以保持 W ⊤ B \mathbf W^\top \mathbf B WB取最大值+1。因此,可以得到 B ∗ = s i g n ( W ) \mathbf B^*=\mathrm{sign}(\mathbf W) B=sign(W)
然后,求解放缩因子 α \alpha α的最优解,我们用 J J J α \alpha α求偏导数:
∂ J ∂ α = 2 α B ⊤ B − 2 W ⊤ B \frac{\partial J}{\partial \alpha}=2\alpha\mathbf B^\top\mathbf B-2\mathbf W^\top \mathbf B αJ=2αBB2WB

当偏导数等于0时,可求解:
α ∗ = W ⊤ B B ⊤ B = W ⊤ B n \alpha^*=\frac{\mathbf W^\top \mathbf B}{\mathbf B^\top \mathbf B}=\frac{\mathbf W^\top \mathbf B}{n} α=BBWB=nWB

其中,令 n = B ⊤ B n=\mathbf B^\top \mathbf B n=BB,此时的 B \mathbf B B代入 B ∗ \mathbf B^* B,于是:
α ∗ = W ⊤ B n = W ⊤ s i g n ( W ) n = ∑ ∣ W ∣ n = 1 n ∣ ∣ W ∣ ∣ 1 \alpha^*=\frac{\mathbf W^\top \mathbf B}{n}=\frac{\mathbf W^\top \mathrm{sign}(\mathbf W)}{n}=\frac{\sum |\mathbf W|}{n}=\frac{1}{n}||\mathbf W||_1 α=nWB=nWsign(W)=nW=n1∣∣W1

其中, ∣ ∣ ⋅ ∣ ∣ 1 ||\cdot||_1 ∣∣1表示 ℓ 1 \ell_1 1-norm,即对矩阵中的所有元素的绝对值求和。

总结
二值权重/滤波器的最优估计是权重的符号函数值,放缩因子的最优估计是权重的绝对值平均值。

在这里插入图片描述

训练过程
需要注意的是,反向传播计算梯度用的近似的权重 W ~ \tilde W W~,而真正被更新的权重应该是真实的高精度权重 W W W
在这里插入图片描述

2)XNOR-Networks:权重和激活值都是1-bit,乘法全部简化为异或计算

二值dot product计算
X ⊤ W ≈ β H ⊤ α B \mathbf X^\top W\approx \beta \mathbf H^\top \alpha \mathbf B XWβHαB,其中, H , B ∈ { − 1 , + 1 } , β , α ∈ R + \mathbf H, \mathbf B\in \{-1, +1\}, \beta, \alpha\in\mathbb R^+ H,B{1,+1},β,αR+,优化目标如下:
在这里插入图片描述

Y = X W , C ∈ { − 1 , + 1 } , C = H B , γ = α β \mathbf Y=\mathbf X \mathbf W, \mathbf C\in \{-1, +1\}, \mathbf C=\mathbf H \mathbf B, \gamma=\alpha\beta Y=XW,C{1,+1},C=HB,γ=αβ,于是优化目标简化为:
在这里插入图片描述

根据Binary-Weight-Network,通过符号函数可以求解最优的二值激活值和权重:
在这里插入图片描述

同理,根据,通过 ℓ 1 \ell_1 1-norm可以求解最优的放缩因子:
在这里插入图片描述

二值卷积计算
对于输入 I \mathbf I I,首先计算 A = ∑ ∣ I : , : , i ∣ c \mathbf A=\frac{\sum |\mathbf I_{:, :, i}|}{c} A=cI:,:,i,其中 c c c是输入通道数,这个过程计算了跨通道的输入 I \mathbf I I中元素的绝对值的平均值。然后将 I \mathbf I I和一个2D滤波器 k ∈ R w × h \mathbf k\in \mathbb R^{w\times h} kRw×h做卷积, K = A ∗ k , k i j = 1 w h \mathbf K=\mathbf A * \mathbf k, \mathbf k_{ij}=\frac{1}{wh} K=Ak,kij=wh1 K \mathbf K K中包含了 I \mathbf I I中左右子张量的放缩因子 β \beta β
于是,卷积的近似计算如下:
在这里插入图片描述

其中, ⊛ \circledast 表示XNOR+bitcount操作。

在这里插入图片描述

代码参考:XNOR-Net-PyTorch
符号函数直接通过sign函数实现:

input = input.sign()

参考资料

  • 量化训练之可微量化参数—LSQ
  • A White Paper on Neural Network Quantization

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

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

相关文章

Mint Blockchain,一个聚焦在 NFT 领域的 L2 网络

Mint 是什么&#xff1f; Mint 是一个聚焦在 NFT 领域的创新型 L2 网络。Mint Blockchain 致力于促进 NFT 资产协议标准的创新和现实商业场景中 NFT 资产的大规模采用。 不管是过去 3 年在以太坊网络涌现的 NFT&#xff0c;还是当下在比特币网络活跃的“铭文” NFT&#xff0c…

HarmonyOS开发(九):数据管理

1、概述 1.1、功能简介 数据管理为开发者提供数据存储、数据管理能力。 它分为两个部分&#xff1a; 数据存储&#xff1a;提供通用数据持久化能力&#xff0c;根据数据特点&#xff0c;分为用户首选项、键值型数据库和关系型数据库。数据管理&#xff1a;提供高效的数据管…

gitlab注册无中国区电话验证问题

众所周知gitlab对中国区不友好&#xff0c;无法直接注册&#xff0c;页面无法选择86的手机号进行验证码发送。 Google上众多的方案是修改dom&#xff0c;而且时间大约是21年以前。 修改dom&#xff0c;对于现在的VUE、React框架来说是没有用的&#xff0c;所以不用尝试。 直接看…

springboot3远程调用

RPC 两个服务器之间的调用 远程请求 内部服务之间的调用 可以通过 cloud 注册中心 openfeign等 外部服务的调用 http请求 外部协议 api:远程接口 sdk&#xff1a;本地调用 调用阿里云的天气请求

Navicat 技术指引 | 适用于 GaussDB 分布式的日志查询与配置设置

Navicat Premium&#xff08;16.3.3 Windows 版或以上&#xff09;正式支持 GaussDB 分布式数据库。GaussDB 分布式模式更适合对系统可用性和数据处理能力要求较高的场景。Navicat 工具不仅提供可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结…

Python---random库

目录 基本随机数函数(): rand.seed() random() 扩展随机数函数(): random库包含两类函数&#xff1a;基本随机数函数&#xff0c;扩展随机数函数 基本随机数函数:seed(),random() 扩展随机数函数&#xff1a;randint,getrandbits(),uniform(),randrange(),choice(),shuff…

分布式和微服务区别

1.分布式 微服务和分布式的区别 1.将一个大的系统划分为多个业务模块&#xff0c;业务模块分别部署到不同的机器上&#xff0c;各个业务模块之间通过接口进行数据交互。区别分布式的方式是根据不同机器不同业务。 2.分布式是否属于微服务&#xff1f; 答案是肯定的。微服务的意…

微信小程序引入Vant Weapp修改样式不起作用,使用外部样式类进行覆盖

一、引入Vant Weapp后样式问题 在项目中使用第三方组件修改css样式时,总是出现各种各样问题,修改的css样式不起作用,没有效果,效果不符合预期等。 栗子(引入一个搜索框组件)实现效果: 左侧有一个搜索文字背景为蓝色,接着跟一个搜索框 wxml <view class"container&q…

cache 2.单机并发缓存

0.对原教程的一些见解 个人认为原教程中两点知识的引入不够友好。 首先是只读数据结构 ByteView 的引入使用是有点迷茫的&#xff0c;可能不能很好理解为什么需要ByteView。 第二是主体结构 Group的引入也疑惑。其实要是熟悉groupcache&#xff0c;那对结构Group的使用是清晰…

修改pip源

修改pip源 永久修改 PS C:\Users\Dell> pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/Writing to C:\Users\Dell\AppData\Roaming\pip\pip.ini临时修改 pip install -i(即--index-url简写) http://mirrors.aliyun.com/pypi/simple/ selenium…

图像叠加中文字体

目录 1) 前言2) freetype下载3) Demo3.1) 下载3.2) 编译3.3) 运行3.4) 结果3.5) 更详细的使用见目录中说明 4) 积少成多 1) 前言 最近在做图片、视频叠加文字&#xff0c;要求支持中文&#xff0c;基本原理是将图片或视频解码后叠加文字&#xff0c;之后做图片或视频编码即可。…

一文讲解关于MCU启动原理的几个关键问题

MCU最开始一启动后去哪里读代码&#xff1f; CPU上电启动后被设计为去地址0x00000000位置处读取代码&#xff1b;首先会连续读取两个字&#xff0c;分别是栈指针初始值和复位异常处理函数的地址&#xff1b;然后跳去执行复位异常处理函数。 当然在一些早期的ARM处理器设计中&a…

【计算机网络学习之路】HTTP请求

目录 前言 HTTP请求报文格式 一. 请求行 HTTP请求方法 GET和POST的区别 URL 二. 请求头 常见的Header 常见的额请求体数据类型 三. 请求体 结束语 前言 HTTP是应用层的一个协议。实际我们访问一个网页&#xff0c;都会像该网页的服务器发送HTTP请求&#xff0c;服务…

nodejs+vue+微信小程序+python+PHP的黄山旅游景点购票系统设计与实现-计算机毕业设计推荐

本文首先对该系统进行了详细地描述&#xff0c;然后对该系统进行了详细的描述。管理人员增加了系统首页、个人中心、用户管理、景点分类管理、景点简介管理、旅游路线管理、文章分类管理、公告文章管理、系统管理理等功能。黄山旅游景点购票系统是根据当前的现实需要&#xff0…

ELK(五)—集群搭建

写目录 ip规划ElasticSearch集群集群节点搭建集群es切片和副本切片&#xff08;Shard&#xff09;&#xff1a;副本&#xff08;Replica&#xff09;&#xff1a; 故障转移postman创建索引的情况直接在面板中创建索引总结 ip规划 ip名称服务192.168.150.190elk_masterelastics…

web漏洞原理与防御策略,web漏洞怎么挖掘

目录 Web安全的重要性 ​编辑常见的Web漏洞类型及其原理&#xff1a; 1、跨站脚本攻击&#xff08;XSS&#xff09;: 2、SQL注入: 3、跨站请求伪造&#xff08;CSRF&#xff09;: 4、远程文件包含&#xff08;RFI&#xff09;和本地文件包含&#xff08;LFI&#xff09;:…

深入浅出:HTTPS单向与双向认证及证书解析20231208

介绍: 网络安全的核心之一是了解和实施HTTPS认证。本文将探讨HTTPS单向认证和双向认证的区别&#xff0c;以及SSL证书和CA证书在这些过程中的作用&#xff0c;并通过Nginx配置实例具体说明。 第一部分&#xff1a;HTTPS单向认证 定义及工作原理&#xff1a;HTTPS单向认证是一…

数据分析基础之《matplotlib(6)—饼图》

一、饼图介绍 1、什么是饼图 饼图广泛的应用在各个领域&#xff0c;用于表示不同分类的占比情况&#xff0c;通过弧度大小来对比各种分类。饼图通过将一个圆饼按照分类的占比划分成多个区块&#xff0c;整个圆饼代表数据的总量&#xff0c;每个区块&#xff08;圆弧&#xff0…

Python网络爬虫的基础理解-对应的自我理解误区

##通过一个中国大学大学排名爬虫的示例进行基础性理解 以软科中国最好大学排名为分析对象&#xff0c;基于requests库和bs4库编写爬虫程序&#xff0c;对2015年至2019年间的中国大学排名数据进行爬取&#xff1a;&#xff08;1&#xff09;按照排名先后顺序输出不同年份的前10…

生产上线需要注意的安全漏洞

一、关闭swagger 1、关闭swagger v3 # 需同时设置auto-startupfalse&#xff0c;否则/v3/api-docs等接口仍能继续访问 springfox:documentation:enabled: falseauto-startup: falseswagger-ui:enabled: false 2、关闭swagger v2 # 只要不是true就不启用 swagger:enable: fa…