【基于 PyTorch 的 Python深度学习】5 机器学习基础(2)

前言

文章性质:学习笔记 📖

学习资料:吴茂贵《 Python 深度学习基于 PyTorch ( 第 2 版 ) 》【ISBN】978-7-111-71880-2

主要内容:根据学习资料撰写的学习笔记,该篇主要介绍了如何选择合适的激活函数、损失函数和优化器。

一、选择合适的激活函数

激活函数 在神经网络中的作用有很多,主要作用是给神经网络提供 非线性建模能力 。如果没有激活函数,那么再多层的神经网络也只能处理线性可分问题。作为神经网络的激活函数,通常需要满足如下 3 个条件:

  1.  非线性:为提高模型的学习能力,如果是线性,那么再多层都相当于只有两层效果。
  2.  可微性:有时可以弱化,在一些点存在偏导即可。
  3.  单调性:保证模型简单。

常用的激活函数有 sigmoid 、tanh 、ReLU 、softmax 等。它们的图形、表达式、导数等信息如表 5-2 所示。

在搭建神经网络时,如何选择激活函数?如果搭建的神经网络层数不多,选择 sigmoid 、tanh 、ReLU 、softmax 都可以;如果搭建的网络层次比较多,选择不当就可能导致梯度消失问题。此时一般不宜选择 sigmoid 、tanh 激活函数,因为其导数都小于 1 ,尤其是 sigmoid 的导数在 [ 0, 1/4 ] 之间,多层叠加后,根据微积分链式法则,随着层数增多,导数或偏导将会指数级变小。所以层数较多时,激活函数需要考虑其导数不宜小于 1 也不能大于 1 ,大于 1 将导致梯度爆炸,导数为 1 最好,激活函数 ReLU 正好满足这个条件。也就是说,搭建比较深的神经网络时,一般使用 ReLU 激活函数,当然一般神经网络也可使用。除此之外,由于激活函数 softmax 的 \sum_{i}\sigma _{i}(z)=1 ,softmax 常用于多分类神经网络输出层。激活函数在 PyTorch 中的使用示例:

m = nn.Sigmoid()
input = torch.randn(2)
output = m(input)

激活函数的输入维度与输出维度是一样的,其输入维度一般包括批量数 N ,即输入数据的维度一般是 4 维,如 ( N, C, W, H )

二、选择合适的损失函数

损失函数(Loss Function)在机器学习中非常重要,因为 训练模型的过程实际就是优化损失函数的过程 。损失函数对每个参数的偏导数就是梯度下降中提到的梯度,防止过拟合时添加的正则化项也是加在损失函数后面。损失函数用于衡量模型的好坏,损失函数值越小说明模型和参数越符合训练样本。任何能够衡量模型预测值与真实值之间的差异的函数都可以叫做损失函数。

在机器学习中常用的损失函数有两种,即 交叉熵 Cross Entropy 均方误差 Mean Squared Error ,分别对应机器学习中的 分类问题回归问题 。对分类问题的损失函数一般采用交叉熵,交叉熵反映了两个概率分布的距离(不是欧氏距离)。分类问题进一步又可分为多目标分类,如一次判断 100 张图是否包含 10 种动物,或单目标分类。回归问题预测的不是类别,而是一个任意实数。在神经网络中一般只有一个输出节点,该输出值就是预测值。其反映的预测值与实际值之间的距离可以用欧氏距离来表示,所以对这类问题我们通常使用均方差作为损失函数,均方差的定义如下:

MSE=\frac{\sum_{i=1}^{n}(y_i-y'_i)^{2}}{n}

PyTorch 中已经集成了多种损失函数,这里介绍两个经典的损失函数,其他损失函数基本上是在它们的基础上的变种或延伸。

1、torch.nn.MSELoss

具体格式:torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')

计算公式:

L(x,y)=[l_{1},l_{2},...,l_{N}]^{T},l_{n}=(x_{n}-y_{n})^{2} 

其中,N 是批量大小。如果参数 reduction 为非 None(默认值为‘mean’),则:

L(x,y)=\left\{\begin{matrix} mean(L), \: \, if \: \, reducation=\,'mean'\\ sum(L), \: \, if \: \, reducation=\,'sum' \end{matrix}\right.

公式说明:x 和 y 是任意形状的张量,每个张量都有 n 个元素,若 reduction 取 none 则 L(x, y) 将不是标量;若取 sum 则 L(x, y) 只是差平方的和,但不会除以 n 。

参数说明:size_average,reduce 在 PyTorch 官方的后续版本中将移除,主要看参数 reduction,可以取 'none', 'mean', 'sum',其默认值为 'mean' 。如果 size_average,reduce 都取了值,则将覆盖 reduction 的值。

import torch
import torch.nn as nn
import torch.nn.functional as F
 
torch.manual_seed(10)
 
loss = nn.MSELoss(reduction='mean')
input = torch.randn(1, 2, requires_grad=True)
print(input)
target = torch.randn(1, 2)
print(target)
output = loss(input, target)
print(output)
output.backward()

2、torch.nn.CrossEntropyLoss

交叉熵损失(Cross-Entropy Loss)又称为对数似然损失、对数损失,二分类时还可称为逻辑回归损失。在 PyTroch 里,它不是严格意义上的交叉熵损失函数,而是先将输入 input 经过 softmax 激活函数,将向量 “ 归一化 ” 为概率形式,然后再与实际值 target 计算严格意义上的交叉熵损失。在多分类任务中,经常采用 softmax 激活函数 + 交叉熵损失函数,因为交叉熵描述了两个概率分布的差异,然而神经网络输出的是向量,并不是概率分布的形式。所以需要 softmax 激活函数将一个向量进行 “ 归一化 ” 成概率分布的形式,再采用交叉熵损失函数计算损失值。

具体格式:torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')

计算公式:

loss(x,class)=-log(\frac{exp(x[class])}{\sum_{j}exp(x[j])})=-x[class]+log(\sum_{j}exp(x[j]))

如果带上权重参数 weight ,则:

loss(x,class)=weight[class](-x[class]+log(\sum_{j}exp(x[j])))

其中,weight 表示每个类别的损失设置权值,常用于类别不均衡问题。weight 必须是 float 类型的张量,其长度要与类别 C 一致,即每一个类别都要设置 weight 。代码示例:

import torch
import torch.nn as nn
 
torch.manual_seed(10)
 
loss = nn.CrossEntropyLoss()
# 假设类别数为 5
input = torch.randn(3, 5, requires_grad=True)
# 每个样本对应的类别索引,其值范围为 [0,4]
target = torch.empty(3, dtype=torch.long).random_(5)
output = loss(input, target)
output.backward()

三、选择合适的优化器

优化器 在机器学习、深度学习中往往起着举足轻重的作用,同一个模型,因选择的优化器不同,性能有可能相差很大,甚至导致模型无法训练。因此了解各种优化器的基本原理非常必要。本节重点介绍各种优化器或算法的主要原理,及其各自的优点或不足。

1、传统梯度优化算法

传统梯度优化算法 为最常见、最简单的一种参数更新策略。其基本思想是:先设定一个学习率 𝜆 ,参数沿梯度的反方向移动。假设基于损失函数 𝐿 (𝑓(𝑥, 𝜃), 𝑦) ,其中 𝜃 表示需要更新的参数,梯度为 g ,则其更新策略的伪代码如下所示:

这种梯度优化算法简洁,当学习率取值恰当时,可以收敛到全面最优点(凸函数)或局部最优点(非凸函数)。其不足也很明显:对超参数学习率比较敏感,过小导致收敛速度过慢,过大又越过极值点,如图 5-12 的 c 所示。在比较平坦的区域,因梯度接近 0 ,易导致提前终止训练,如图 5-12 的 a 所示。要选中一个恰当的学习速率往往要花费不少时间。

学习率不仅敏感,有时还会因其在迭代过程中保持不变,很容易造成算法被卡在鞍点的位置,如图 5-13 所示。

除此之外,在较平坦的区域,因梯度接近于 0 ,优化算法往往因误判还未到达极值点就提前结束迭代,如图 5-14 所示。

传统梯度优化算法的这些不足,在深度学习中会更加明显。为此,人们自然想到要克服这些不足。从前文更新策略的伪代码可知,影响优化的主要因素有:训练数据集的大小、梯度方向、学习率。很多优化方法从这些方面入手:

  • 从数据集优化方面入手,采用批量随机梯度下降方法;
  • 从梯度方向优化方面入手,采用动量更新策略;
  • 从学习率入手,涉及自适应问题;
  • 还有从两方面同时入手等方法。

接下来我们将具体介绍这些方法。

2、批量随机梯度下降法

梯度下降法是非常经典的算法,训练时如果使用全训练集,虽然可获得较稳定的值,但比较耗费资源,尤其当训练数据比较大时;另一个极端时,每次训练时用一个样本(又称为随机梯度下降法),这种训练方法振幅较大,也比较耗时,如图 5-15 所示。

这种方法虽然资源消耗较少,但非常消耗时间,因此无法充分发挥深度学习程序库中高度优化的矩阵运算的优势。为了更有效地训练模型,我们采用一种折中方法,即 批量随机梯度下降法 。这种梯度下降方法有两个特点:批量随机性

如何实现批量随机梯度下降呢? 其伪代码如下:

其中 x^{(i)} 和小批量数据集的所有元素都是从训练集中随机抽出的,这样梯度的预期将保持不变。相对于随机梯度下降,批量随机梯度下降降低了收敛波动性,即降低了参数更新的方差,使得更新更加稳定,有利于提升其收敛效果,如图 5-16 所示。

3、动量算法

梯度下降法在遇到平坦或高曲率区域时,学习过程有时很慢。利用 动量算法 能比较好地解决这个问题。

我们以求解函数 f(x_{1},x_{2})=0.05x_{1}^{2}+2x_{2}^{2}  极值为例,使用梯度下降法和动量算法分别进行迭代求解。

由图 5-17 可知,不使用动量算法的梯度下降法的学习速度比较慢,振幅比较大。由图 5-18 可知,使用动量算法的振幅较小,而且较快到达极值点。动量算法是如何做到这点的呢?动量(Momentum)是模拟物理里动量的概念,具有物理上惯性的含义。一个物体在运动时具有惯性,把这个思想运用到梯度下降法中,可以增加算法的收敛速度和稳定性,具体实现如图 5-19 所示。

由图 5-19 所示,动量算法每下降一步都是由前面下降方向的一个累积和当前点的梯度方向组合而成。

含动量的随机梯度下降法的算法伪代码如下:

动量算法的 PyTorch 代码实现如下:

def sgd_momentum(parameters, vs, lr, gamma):
    for param, v in zip(parameters, vs):
        v[:] = gamma * v + lr * param.grad
        param.data -= v
        param.grad.data.zero_()

其中 parameters 是模型参数,假设模型为 model ,则 parameters 为 model. parameters() 。

具体使用动量算法时,动量项的计算公式如下:

\nu _{k}\leftarrow \alpha \nu _{k-1}+(-\lambda \hat{g}(\theta _{k}))

如果按时间展开,则第 k 次迭代使用了从 1 到 k 次迭代的所有负梯度值,且负梯度按动量系数 α 指数级衰减,相当于使用了移动指数加权平均。假设每个时刻的梯度 \hat{g} 相似,则得到:

\nu_{k} \approx \frac{\lambda \, \hat{g}}{1-\alpha }

由此可知,当在比较平缓处,但 α = 0.5 , 0.9 时,梯度值将分别是梯度下降法的 2 倍、10 倍。使用动量算法,不但可以加快迭代速度,还可以跨过局部最优找到全局最优,如图 5-20 所示。

4、Nesterov 动量算法

既然每一步都要将两个梯度方向(历史梯度、当前梯度)做一个合并再下降,那为什么不先按照历史梯度往前走那么一小步,按照前面一小步位置的 “ 超前梯度 ” 来做梯度合并呢?可以先往前走一步,在靠前一点的位置(图 5-21 中的 C 点)看到梯度,然后按照那个位置来修正这一步的梯度方向,如图 5-21 所示。 

这就得到动量算法的一种改进算法,即 NAG(Nesterov Accelerated Gradient)算法,也称 Nesterov 动量算法。这种预更新方法能防止大幅振荡,不会错过最小值,并对参数更新更加敏感。如图 5-22 所示。

NAG 动量算法的伪代码如下: 

NAG 动量算法的 PyTorch 代码实现如下:

def sgd_nag(parameters, vs, lr, gamma):
    for param, v in zip(parameters, vs):
        v[:]*= gamma
        v[:]-= lr * param.grad
        param.data += gamma * gamma * v
        param.data -= (1 + gamma) * lr * param.grad
        param.grad.data.zero_()

NAG 动量算法和经典动量算法的差别就在 B 点和 C 点梯度的不同。动量算法更关注梯度下降方法的优化,如果能从方向和学习率同时优化,效果或许更理想。事实也确实如此,而且这些优化在深度学习中显得尤为重要。

接下来我们介绍几种自适应优化算法,这些算法可以同时从梯度方向及学习率进行优化,效果非常好。

5、AdaGrad 算法

传统梯度下降算法对学习率这个超参数非常敏感,难以驾驭;对参数空间的某些方向也没有很好的方法。这些不足在深度学习中,因高维空间、多层神经网络等因素,常会出现平坦、鞍点、悬崖等问题,因此,传统梯度下降法在深度学习中显得力不从心。还好现在已有很多解决这些问题的有效方法。上节介绍的动量算法在一定程度上缓解了参数空间某些方向的问题,但是需要新增一个参数,而且对学习率的控制还不是很理想。为了更好地驾驭这个超参数,人们想出来多种自适应优化算法,使用自适应优化算法,学习率不再是一个固定不变值,它会根据不同情况自动调整学习率以适用实际情况。这些算法使深度学习向前迈出一大步!下面我们将介绍几种自适应优化算法。先来看 AdaGrad 算法。
AdaGrad 算法 通过参数来调整合适的学习率 𝜆 ,能独立地自动调整模型参数的学习率,对稀疏参数进行大幅更新,对频繁参数进行小幅更新,如图 5-23 所示。因此,Adagrad 方法非常适合处理稀疏数据。AdaGrad 算法在某些深度学习模型上效果不错。但还有些不足,可能因其累积梯度平方导致学习率过早或过量地减少所致。

AdaGrad 算法的伪代码如下: 

由上面算法的伪代码可知:

1. 随着迭代时间越长,累积梯度参数 𝑟 越大,学习率 𝜆 / ( 𝛿+√𝑟) 会越小,在接近目标值时,不会因为学习速率过大而越过极值点。

2. 不同参数之间学习速率不同,因此,与前面的固定学习率相比,不容易在鞍点卡住。

3. 如果梯度累加参数 𝑟 比较小,则学习率会比较大,所以参数迭代的步长就会比较大。反之亦然。

AdaGrad 算法的 PyTorch 代码实现如下:

def sgd_adagrad(parameters, s, lr):
    eps = 1e-10
    for param, s in zip(parameters, s):
        s[:] = s + (param.grad) ** 2
        div = lr / torch.sqrt(s + eps) * param.grad
        param.data = param.data - div
        param.grad.data.zero_()

6、RMSProp 算法

RMSProp 算法 修改自 AdaGrad 算法,其在非凸背景下的效果更好,在凸函数中振幅可能较大,如图 5-24 所示。对梯度平方和累计越来越大的问题,RMSProp 算法用指数加权的移动平均代替梯度平方和。为了使用移动平均,RMSProp 算法引入了一个新的超参数 ρ ,用来控制移动平均的长度范围。

RMSProp 算法的伪代码如下: 

RMSProp 算法的 PyTorch 代码实现如下:

def rmsprop(parameters, s, lr, alpha):
    eps = 1e-10
    for param, sqr in zip(parameters, s):
        sqr[:] = alpha * sqr + (1 - alpha) * param.grad ** 2
        div = lr / torch.sqrt(sqr + eps) * param.grad
        param.data = param.data - div
        param.grad.data.zero_()

7、Adam 算法

Adam(Adaptive Moment Estimation,自适应矩估计)算法 本质上是带有动量项的 RMSprop 算法,它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。Adam 的优点主要在于经过偏置校正后,每一次迭代学习率都有 1 个确定范围,使得参数比较平稳,如图 5-25 所示。

Adam 算法的伪代码如下: 

Adam 算法的 PyTorch 代码实现如下:

def adam(parameters, vs, s, lr, t, beta1=0.9, beta2=0.999):
    eps = 1e-8
    for param, v, sqr in zip(parameters, vs, s):
        v[:] = beta1 * v + (1 - beta1) * param.grad
        sqr[:] = beta2 * sqr + (1 - beta2) * param.grad ** 2
        v_hat = v / (1 - beta1 ** t)
        s_hat = sqr / (1 - beta2 ** t)
        param.data = param.data - lr * v_hat / (torch.sqrt(s_hat) + eps)
        param.grad.data.zero_()

8、Yogi 算法

Adam 算法综合了动量算法及自适应算法的优点,是深度学习常用的算法,但也存在一些问题:即使在凸环境下,当 𝑟 的第二阶矩估计值爆炸时,它可能无法收敛。为此可通过改进 𝑟 和优化参数初始化等方法来解决。 其中通过改进 𝑟 是一种有效方法:

Yogi 算法的 PyTorch 代码实现如下:

def yogi(parameters, vs, s, lr, t, beta1=0.9, beta2=0.999):
    eps = 1e-8
    for param, v, sqr in zip(parameters, vs, s):
        v[:] = beta1 * v + (1 - beta1) * param.grad
        #sqr[:] = beta2 * sqr + (1 - beta2) * param.grad ** 2
        sqr[:] = sqr + (1 - beta2) * torch.sign(torch.square(param.grad) - sqr) * torch.square(param.grad)
        v_hat = v / (1 - beta1 ** t)
        s_hat = sqr / (1 - beta2 ** t)
        param.data = param.data - lr * v_hat / (torch.sqrt(s_hat) + eps)
        param.grad.data.zero_()

9、使用优化算法实例

前面介绍了深度学习的正则化方法,它是深度学习的核心;优化算法也是深度学习的核心。优化算法很多,如随机梯度下降法、自适应优化算法等,那么具体使用时该如何选择呢?RMSprop 、Nesterov 、Adadelta 和 Adam 被认为是自适应优化算法,因为它们会自动更新学习率。而使用 SGD 时,必须手动选择学习率和动量参数,因此通常会随着时间的推移而降低学习率。有时可以考虑综合使用这些优化算法,如先用 Adam 算法,再用 SGD 优化方法。实际上,由于在训练的早期阶段,SGD 对参数调整和初始化非常敏感,我们可以通过先使用 Adam 优化算法进行训练(这将大大节省训练时间,且不必担心初始化和参数调整),待用 Adam 训练获得较好的参数后,再切换到 SGD + 动量优化,以达到最佳性能。采用这种方法有时能达到很好效果,如图 5-26 所示,迭代次数超过 150 后,SGD 的效果好于 Adam 的效果。

实例:基于 MNIST 数据集,使用自定义的优化算法实现图像的分类任务。为便于比较,使用梯度下降法及动量算法两种优化算法。

参考代码:feiguyunai/Python-DL-PyTorch2/pytorch-05/pytorch-05-04.ipynb at main · Wumg3000/feiguyunai · GitHub

1)算法定义。

# 梯度下降法
def sgd(parameters, lr):
    for param in parameters:
        param.data -= lr * param.grad
        param.grad.data.zero_()
# 动量梯度下降法
def sgd_momentum(parameters, vs, lr, gamma):
    for param, v in zip(parameters, vs):
        v[:] = gamma * v + lr * param.grad
        param.data -= v
        param.grad.data.zero_()

  2)定义模型。

net = nn.Sequential(
    nn.Linear(784, 200),
    nn.ReLU(),
    nn.Linear(200, 10),
)

3)定义损失函数。

loss_sgd = nn.CrossEntropyLoss()

loss_sgd_mom = nn.CrossEntropyLoss()

4)加载数据。这里使用批量大小为 128 的训练数据集。

# 定义预处理函数
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize([0.5], [0.5])])

# 下载数据,并对数据进行预处理
train_dataset = mnist.MNIST('./data', train=True, transform=transform, download=False)
test_dataset = mnist.MNIST('./data', train=False, transform=transform)

# 得到数据生成器
train_loader = DataLoader(train_dataset, batch_size=128, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=128, shuffle=False)

5)训练模型。

# 初始化梯度平方项
s = []
for param in net.parameters():
    s.append(torch.zeros_like(param.data))

# 开始训练
losses0 = []
# losses1 = []
idx = 0

start = time.time()
for e in range(5):
    train_loss = 0
    for img, label in train_loader:
        # 展平 img
        img=img.view(img.size(0), -1)
        # 正向传播
        out = net(img)
        loss = loss_sgd(out, label)
        # loss = loss_sgd_mom(out, label)
        # 反向传播
        net.zero_grad()
        loss.backward()
        sgd(net.parameters(), 1e-2)
        # sgd_momentum(net.parameters(), vs, 1e-2, 0.9)
        # 记录误差
        train_loss += loss.item()
        if idx % 30 == 0:
            losses0.append(loss.item())
            # losses1.append(loss.item())
        idx += 1
    print('epoch: {}, Train Loss: {:.6f}'.format(e, train_loss / len(train_loader)))
end = time.time()
print('使用时间: {:.5f} s'.format(end - start))

如果使用动量算法,只要把 sgd(net.parameters(),1e-2) 改为 sgd_momentum(net.parameters(), vs, 1e-2, 0.9) 即可。

6)可视化两种优化算法的运行结果

x_axis = np.linspace(0, 5, len(losses0), endpoint=True)
plt.semilogy(x_axis, losses0, label='sgd')
plt.semilogy(x_axis, losses1, label='momentum')
plt.legend(loc='best')

根据运行结果图可知,动量算法的优势还是非常明显的。

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

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

相关文章

学习大数据,所需更要的shell基础(2)

文章目录 read读取控制台输入函数系统函数bashnamedirname 自定义函数Shell工具(重点)cutawk 正则表达式入门常规匹配常用特殊字符 read读取控制台输入 1)基本语法 read (选项) (参数) ①选项: -p:指定读取值时的提示…

【福利】思科CCNP考试介绍(附CCNP题库下载)

网络行业有两个大神级别的证书:思科认证和华为认证,目前相比思科认证,华为认证在国内更加吃香哦,如果你在国内就业或发展考虑建议考华为的。不过还是有少部分在外企或有出国计划的IT人员考思科的。 那今天小微就来给大家介绍下思科…

SpringBoot启动流程源码解析

目录 一、SpringApplication构造方法解析 1. web应用类型 2. BootstrapRegistryInitializer 3. ApplicationContextInitializer 4. ApplicationListener 5. 推断Main方法所在类 二、SpringApplication.run(String... args)方法解析 1.创建DefaultBootstrapContext 2.获…

回顾5款我非常喜欢的软件,希望大家也能喜欢

​ 我喜欢分享好软件,这就像与老友聊天一样让我感到快乐。在这个过程中,我可以回顾这些实用的小工具,也希望它们可以帮助到更多人。 1.备份工具——Cobian Backup ​ Cobian Backup是一款功能强大的备份软件,支持自动定时备份、增量备份、差异备份等多种备份方式。…

Java八股文系列之四(JVM)

什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? Java虚拟机是一个可以执行Java字节码的虚拟机进程。 Java 实现跨平台的主要原因在于它的编译和执行方式。Java 程序首先被编译成中间代码(bytecode),然…

九泰智库 | 医械周刊- Vol.26

⚖️ 法规动态 国家卫健委等八部门印发《关于加强重症医学医疗服务能力建设的意见》 5月6日,国家卫健委发布《关于加强重症医学医疗服务能力建设的意见》,明确我国重症医学领域的重要部署。数据显示,我国在重症救治设备方面依然存在巨大缺口…

vue中使用element的i18n语言转换(保姆式教程-保证能用)

1、项目中需要使用的插件,vue2或vue3、element、vue-i18n、js-cookie、vuex我是在vue2中使用 npm i element-ui -S npm i js-cookie -S npm i vue-i18n8.28.2 //因为我项目使用的vue2,直接安装报错了,就下载了固定的版本2、在main.js中引入i18n impor…

芸众商城电商专业版400+插件源码+搭建教程

介绍: 芸众商城社交电商系统SAAS平台前端基于vue开发,后端基于研发积分商城系统源码 php,本文安装芸众商城全插件(400多个)商业版平台源码,可同时支持多端口部署运行;使用宝塔面板一键部署的形…

出货300万片后,智舱界「小高通」浮出水面

‍作者 |张祥威 编辑 |德新 2024年北京车展,本土芯片公司开始截击外企供应商。 很长一段时间内,汽车行业智驾芯片看英伟达,座舱芯片看高通。英伟达Orin系列广受欢迎,高通8155席卷主流智能汽车,8295更是被视为最强配置…

倍思|西圣开放式耳机哪个好用?热门机型深度测评!

在数字化生活的浪潮中,耳机已成为我们不可或缺的伴侣。然而,长时间佩戴传统的耳机容易导致的耳道疼痛等问题,严重的话将影响听力。许多人开始寻找更为舒适的佩戴体验。开放式耳机因为不需要需直接插入耳道的设计,逐渐受到大众的青…

不会pdf修改编辑文字怎么办?看完秒懂

不会pdf修改编辑文字怎么办?在日常生活中,PDF文件已成为我们工作、学习不可或缺的一部分。然而,很多人对PDF文件的编辑操作感到困惑,尤其是修改其中的文字。今天,我们就来详细解析一下,不会PDF修改编辑文字…

全局变量在 Python 中的应用场景

在Python中,全局变量是在程序的全局范围内定义的变量,可以在整个程序中访问。虽然在Python中使用全局变量并不像在其他编程语言中那样被推荐,因为它可能导致代码不易理解和维护,但在一些特定的情况下,全局变量仍然是有…

企业微信hook接口协议,ipad协议http,设置是否自动同意

设置是否自动同意 参数名必选类型说明uuid是String每个实例的唯一标识,根据uuid操作具体企业微信 请求示例 {"uuid":"bc4800492083fdec4c1a7e5c94","state":1 //1 是需要验证同意(需要手动点击同意) 0关闭验证…

gtest的编译与使用

文章目录 gtest的编译与使用概述笔记CMake参数官方文档测试程序测试效果END gtest的编译与使用 概述 gTest是 googletest的缩写,如果直接找gTest项目,是找不到的。 库地址 https://github.com/google/googletest.git 迁出到本地后,切到最新…

中电金信:看 “咨询+技术”如何引领数字化变革新风向

当前,新一轮创新技术和产业变革正在重塑全球的经济格局。日本政府及社会各界也从各个领域着手推进数字化。2021年,日本政府成立了“数字厅”,通过一系列举措推动数字化升级,希望将日本加速转型为数字经济的区域领导者,…

继续SQL

主知识点六:having 聚合前的筛选用where,聚合后的筛选用having Having和where的区别是:运行顺序和对象不用 Having是在group by聚合后的基础上进行筛选。 ● 【例题27*】(运行原理)查询总人口数至少为3亿的大洲和…

git的标签管理

理解标签 在Git中,标签tag用于标记特定的一个重要点,比如版本发布。标签允许捕捉某一次提交的状态,当我们需要退回到某次提叫的版本时,通过标签我们快速定位到。标签具有两种类型: 轻量标签:最简单的标签形式&#x…

QGIS编译

一,安装:OSGeo4W 二,安装:Cygwin64 https://www.cygwin.com/setup-x86_64.exe 三,安装: 安装bison和flex 四)QGIS_3.28 下载QGIS_3.28的源码包 五 环境变量设置: echo off set VS19…

那些可免费使用的在线大语言模型服务

2022年底以ChatGPT[1]为代表的大语言模型的出现掀起了人工智能应用的新浪潮。这些庞大的语言模型经过对海量文本数据的训练,能够理解和生成逼近人类水平的自然语言,在对话、问答、文本生成、代码编写等领域展现出了惊人的能力。 最初这种能力“垄断”在O…

用手势掌控PPT,玩转演示新姿势

推荐运行环境 使用anaconda创建环境,以免污染原来的python开发环境conda install python3.9pip install -q mediapipe0.10.0pip install pyautoguiPython: version 3.8 - 3.11PIP: version 20.3 请注意以下的坑 以下为我测试过程中的大坑,请及时避开&am…