PyTorch深度学习实战(6)——神经网络性能优化技术

news2024/11/23 3:51:01

PyTorch深度学习实战(6)——神经网络性能优化技术

    • 0. 前言
    • 1. 数据准备
      • 1.1 数据集分析
      • 1.2 数据集加载
    • 2. 使用 PyTorch 训练神经网络
      • 2.1 神经网络训练流程
      • 2.2 PyTorch 神经网络训练
    • 3. 缩放数据集
    • 4. 修改优化器
    • 5. 构建深层神经网络
    • 小结
    • 系列链接

0. 前言

我们已经学习了神经网络的基础概念,并了解了如何利用 PyTorch 库构建实用神经网络模型。同时我们还提到了,有多种超参数可以影响神经网络的准确率。在本节中,我们将使用 Fashion MNIST 数据集,用于构建神经网络模型执行图像分类任务,并对比使用不同参数训练模型的性能差异。

1. 数据准备

1.1 数据集分析

Fashion MNIST 数据集是一个用于图像分类任务的经典数据集,它包含了 10 个类别的时尚服饰图像。每个样本都是一张 28x28 像素的灰度图像,总共有 60000 个训练样本和 10000 个测试样本。由于其简单易用的特点,Fashion MNIST 数据集已经成为学术界和研究人员常用的基准数据集之一,可以用于验证图像分类算法的性能。

1.2 数据集加载

(1) 首先下载数据集并导入相关库,torchvision 库包含多个机器学习数据集,其中包括 Fashion MNIST 数据集:

from torchvision import datasets
import torch
data_folder = './data/FMNIST' # This can be any directory you want to download FMNIST to
fmnist = datasets.FashionMNIST(data_folder, download=True, train=True)

在以上代码中,指定了要存储下载数据集的文件夹 (data_folder)。接下来,利用 datasets.FashionMNIST 获取 fmnist 数据并将其存储在 data_folder 中。此外,通过参数 train = True 指定仅下载训练图像。

(2) 接下来,将 fmnist.data 中可用的图像存储为 tr_images,并将对应的图像标签 (fmnist.targets) 存储为 tr_targets

tr_images = fmnist.data
tr_targets = fmnist.targets

(3) 检查加载后的张量数据:

unique_values = tr_targets.unique()
print(f'tr_images & tr_targets:\n\tX - {tr_images.shape}\n\tY - {tr_targets.shape}\n\tY - Unique Values : {unique_values}')
print(f'TASK:\n\t{len(unique_values)} class Classification')
print(f'UNIQUE CLASSES:\n\t{fmnist.classes}') 

代码输出结果如下:

tr_images & tr_targets:
        X - torch.Size([60000, 28, 28])
        Y - torch.Size([60000])
        Y - Unique Values : tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
TASK:
        10 class Classification
UNIQUE CLASSES:
        ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

在以上结果中,可以看出训练数据集有 60,000 张图像,每张图像的大小为 28 x 28,且包含 10 个可能的类别,tr_targets 包含每个图像的类别标签(以数值表示),而 fmnist.classes 表示与 tr_targets 中的每个数值对应的类别名称。

(4) 绘制随机图像样本。

导入相关库用于绘制图像以及处理图像数组:

import matplotlib.pyplot as plt
import numpy as np

创建一个 10 x 10 的图像网格,其中网格的每一行对应一个类别,遍历所有类别 (label_class) 并获取与给定类别对应的行索引 (label_x_rows):

R, C = len(tr_targets.unique()), 10
fig, ax = plt.subplots(R, C, figsize=(10,10))
for label_class, plot_row in enumerate(ax):
    label_x_rows = np.where(tr_targets == label_class)[0]

在以上代码中,获取 np.where 输出的第 0 个索引(因为其输出的长度为 1),它包含目标值 (tr_targets) 等于 label_class 的所有索引。

循环 10 次填充所有图像网格,我们从先前获得的给定类的索引 (label_x_rows) 中选择一个随机值 (ix) 并绘制:

    for plot_cell in plot_row:
        plot_cell.grid(False); plot_cell.axis('off')
        ix = np.random.choice(label_x_rows)
        x, y = tr_images[ix], tr_targets[ix]
        plot_cell.imshow(x, cmap='gray')

示例图像

在上图中,每一行代表属于同一类的 10 个不同图像的样本。

2. 使用 PyTorch 训练神经网络

接下来,将介绍如何使用 PyTorch 训练神经网络,以便根据输入图像预测图像类别。此外,我们还将了解各种超参数对模型预测准确率的影响。

2.1 神经网络训练流程

使用 PyTorch 训练神经网络,通常需要执行以下步骤:

  • 导入相关库
  • 构建数据集,一次获取一个数据点
  • 使用 DataLoader 封装数据集
  • 构建模型,并定义损失函数和优化器
  • 定义两个函数分别用于在一批数据上进行训练和验证
  • 定义函数用于模型预测的准确率
  • 在每批数据训练过程中更新模型权重,通过多个 epoch 的迭代训练模型

2.2 PyTorch 神经网络训练

(1) 导入相关库和 Fashion MNIST 数据集:

from torch.utils.data import Dataset, DataLoader
import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt

device = "cuda" if torch.cuda.is_available() else "cpu"
from torchvision import datasets
data_folder = './data/FMNIST' # This can be any directory you want to download FMNIST to
fmnist = datasets.FashionMNIST(data_folder, download=True, train=True)
tr_images = fmnist.data
tr_targets = fmnist.targets

(2) 构建一个用于获取数据集的类,它继承自 Dataset 类,且需要定义以下三个函数, __init____getitem____len__

class FMNISTDataset(Dataset):
    def __init__(self, x, y):
        x = x.float()
        x = x.view(-1, 28*28)
        self.x, self.y = x, y
    def __getitem__(self, ix):
        x, y = self.x[ix], self.y[ix]
        return x.to(device), y.to(device)
    def __len__(self):
        return len(self.x)

__init__ 方法中,将输入转换为浮点数,并将每个图像展平为 28*28 = 784 个数值(其中每个数值对应一个像素值);在 __len__ 方法中指定数据数量;__getitem__ 方法用于返回第 ix 个索引对应的数据( ix0__len__ 之间的整数)。

(3) 创建函数,从数据集( FMNISTDataset )中生成一个训练数据 DataLoader——trn_dl,每批数据包含随机采样的 32 个数据点:

def get_data():
    train = FMNISTDataset(tr_images, tr_targets)
    trn_dl = DataLoader(train, batch_size=32, shuffle=True)
    return trn_dl

在以上代码中,创建了 FMNISTDataset 类的对象 train,并调用了 DataLoader,使其随机获取 32 个数据点并返回训练 DataLoader

(4) 定义模型,以及损失函数和优化器:

from torch.optim import SGD
def get_model():
    model = nn.Sequential(
        nn.Linear(28*28, 1000),
        nn.ReLU(),
        nn.Linear(1000, 10)
    ).to(device)
    loss_fn = nn.CrossEntropyLoss()
    optimizer = SGD(model.parameters(), lr=1e-2)
    return model, loss_fn, optimizer

模型使用了一个具有 1,000 个神经元的隐藏层,输出层包含 10 个神经元,对应于 10 个可能的类别。由于输出结果表示输入图像属于 10 个类别的概率,因此调用 CrossEntropyLoss 损失函数。最后,将学习率 lr 初始化为 0.01,而不使用默认值 0.001
在神经网络中并未使用 “softmax” 函数(因此模型输出范围不受限制,而交叉熵损失通常期望输出为概率——每一图像的预测结果之和 1),这是因为 nn.CrossEntropyLoss 接受原始 logits (即不受约束的值)并在内部执行 softmax

(5) 定义将在一批图像上训练模型的函数:

def train_batch(x, y, model, optimizer, loss_fn):
    model.train()
    # call your model like any python function on your batch of inputs
    prediction = model(x)
    # compute loss
    batch_loss = loss_fn(prediction, y)
    # based on the forward pass in `model(x)` compute all the gradients of 'model.parameters()'
    batch_loss.backward()
    # apply new-weights = f(old-weights, old-weight-gradients) where "f" is the optimizer
    optimizer.step()
    # Flush gradients memory for next batch of calculations
    optimizer.zero_grad()
    return batch_loss.item()

在前向传播中通过模型处理输入图像,计算输入批数据损失,然后通过反向传播计算梯度并更新权重,最后刷新梯度的内存,以免对下一次传递中计算梯度时产生影响。可以通过在 batch_loss 之上获取 batch_loss.item() 提取标量损失值。

(6) 编写函数计算模型在给定数据集上准确率:

@torch.no_grad()
def accuracy(x, y, model):
    model.eval()
    # get the prediction matrix for a tensor of `x` images
    prediction = model(x)
    # compute if the location of maximum in each row coincides with ground truth
    max_values, argmaxes = prediction.max(-1)
    is_correct = argmaxes == y
    return is_correct.cpu().numpy().tolist()

在以上代码中,通过使用 @torch.no_grad() 显式的声明无需进行梯度计算。调用 prediction.max(-1) 来识别每行对应的 argmax 索引;此外,通过 argmaxes == y 将预测结果 argmaxes 与真实值( ground true )进行比较,以便检查是否得到正确预测。最后,将 is_correct 对象列表移动到 CPU 中并将其转换为 numpy 数组后返回。

(7) 训练神经网络。

首先初始化模型、损失、优化器和数据加载器:

trn_dl = get_data()
model, loss_fn, optimizer = get_model()

在每个 epoch 结束时记录准确率和损失值:

losses, accuracies = [], []

定义模型训练的 epoch 数:

for epoch in range(10):
    print(epoch)

初始化列表用于记录一个 epoch 内每批数据对应的准确率和损失值:

    epoch_losses, epoch_accuracies = [], []

通过迭代 DataLoader 创建批训练数据:

    for ix, batch in enumerate(iter(trn_dl)):
        x, y = batch

使用 train_batch 函数利用批数据训练模型,并将批训练结束时的损失值 batch_loss 存储在 epoch_losses 列表中:

        batch_loss = train_batch(x, y, model, optimizer, loss_fn)
        epoch_losses.append(batch_loss)

存储一个 epoch 内所有批训练的平均损失值:

    epoch_loss = np.array(epoch_losses).mean()

在所有批训练结束时计算预测的准确率:

    for ix, batch in enumerate(iter(trn_dl)):
        x, y = batch
        is_correct = accuracy(x, y, model)
        epoch_accuracies.extend(is_correct)
    epoch_accuracy = np.mean(epoch_accuracies)

将每个 epoch 结束时的损失和准确率值存储在列表中:

    losses.append(epoch_loss)
    accuracies.append(epoch_accuracy)

(8) 绘制训练损失和准确率随时间的变化情况:

epochs = np.arange(10)+1
plt.figure(figsize=(20,5))
plt.subplot(121)
plt.title('Loss value over increasing epochs')
plt.plot(epochs, losses, label='Training Loss')
plt.legend()
plt.subplot(122)
plt.title('Accuracy value over increasing epochs')
plt.plot(epochs, accuracies, label='Training Accuracy')
plt.gca().set_yticklabels(['{:.0f}%'.format(x*100) for x in plt.gca().get_yticks()]) 
plt.legend()
plt.show()

损失和准确率随时间的变化情况

在训练 5epoch 时,模型的训练准确率为 15%,并且随着 epoch 的增加,损失值并没有显着降低。换句话说,无论再训练多长时间,该模型的准确率都不太可能显著增长。
我们已经对训练神经网络的完整流程有了完整了解,接下来,我们通过微调超参数来获得更好的模型性能。

3. 缩放数据集

缩放数据集是确保变量被限制在给定范围内的过程,以确保数据不会分布在较大的区间。在本节中,我们通过将每个输入值除以数据集中的最大可能值,将自变量的值限制在 01 之间。通常,缩放输入数据集能够提高神经网络的性能表现。

(1) 获取数据集,包括训练图像及其标签:

from torch.utils.data import Dataset, DataLoader
import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt

device = "cuda" if torch.cuda.is_available() else "cpu"
from torchvision import datasets
data_folder = './data/FMNIST' 
fmnist = datasets.FashionMNIST(data_folder, download=True, train=True)
tr_images = fmnist.data
tr_targets = fmnist.targets

(2) 修改获取数据的 FMNISTDataset 类,将输入图像除以 255 (最大像素强度值):

class FMNISTDataset(Dataset):
    def __init__(self, x, y):
        x = x.float() / 255.
        x = x.view(-1, 28*28)
        self.x, self.y = x, y
    def __getitem__(self, ix):
        x, y = self.x[ix], self.y[ix]
        return x.to(device), y.to(device)
    def __len__(self):
        return len(self.x)

与上一小节相比,唯一需要修改的是将输入数据除以最大可能的像素值 (255),将它们除以 255 将得到介于 01 之间的值。

(3) 训练模型,首先获取数据、定义模型和用于训练和验证数据的数据,然后训练模型,最后绘制训练期间损失和准确率的变化:


def get_data():
    train = FMNISTDataset(tr_images, tr_targets)
    trn_dl = DataLoader(train, batch_size=32, shuffle=True)
    return trn_dl

trn_dl = get_data()
model, loss_fn, optimizer = get_model()

losses, accuracies = [], []
for epoch in range(10):
    print(epoch)
    epoch_losses, epoch_accuracies = [], []
    for ix, batch in enumerate(iter(trn_dl)):
        x, y = batch
        batch_loss = train_batch(x, y, model, optimizer, loss_fn)
        epoch_losses.append(batch_loss)
    epoch_loss = np.array(epoch_losses).mean()
    for ix, batch in enumerate(iter(trn_dl)):
        x, y = batch
        is_correct = accuracy(x, y, model)
        epoch_accuracies.extend(is_correct)
    epoch_accuracy = np.mean(epoch_accuracies)
    losses.append(epoch_loss)
    accuracies.append(epoch_accuracy)

epochs = np.arange(10)+1
import matplotlib.pyplot as plt
plt.figure(figsize=(20,5))
plt.subplot(121)
plt.title('Loss value over increasing epochs')
plt.plot(epochs, losses, label='Training Loss')
plt.legend()
plt.subplot(122)
plt.title('Accuracy value over increasing epochs')
plt.plot(epochs, accuracies, label='Training Accuracy')
plt.gca().set_yticklabels(['{:.0f}%'.format(x*100) for x in plt.gca().get_yticks()]) 
plt.legend()
plt.show()

损失和准确率随时间的变化情况

如上图所示,训练损失不断减少,训练准确率不断提高,可以将准确率提高到约 85%

接下来,我们将了解缩放数据集能使神经网络性能更好的原因。假设输入数据未缩放,以计算 sigmoid 值为例:

输入权重偏置sigmoid 值
2550.0100.93
2550.101.00
2550.201.00
2550.401.00
2550.801.00
2551.601.00
2553.201.00
2556.401.00

在上表中,即使权重值在 0.016.4 之间变化,在经过函数 Sigmoid 后输出变化也不大。Sigmoid 函数的计算公式如下:
o u t p u t = 1 1 + e − ( w ∗ x + b ) output = \frac1 {1+e^{-(w*x + b)}} output=1+e(wx+b)1
其中 w w w 是权重, x x x 是输入, b b b 是偏置值。Sigmoid 输出不变的原因是由于 w ∗ x w*x wx 的乘积很大(因为 x x x 较大),导致 Sigmoid 值始终落在 Sigmoid 曲线的饱和部分中( Sigmoid 曲线的右上角或左下角的值称为饱和部分)。
如果我们将不同的权重值乘以一个较小的输入数字,如下所示:

输入权重偏置sigmoid 值
10.0100.50
10.100.52
10.200.55
10.400.60
10.800.69
11.600.83
13.200.96
16.401.00

由于输入值较小,因此上表中的 Sigmoid 输出的变化幅度较大。通过此示例,我们了解了缩放输入对数据集的影响,当权重(假设权重不具有较大范围)乘以输入值时,产生的值范围空间并不会突变,从而使输入数据能够对输出产生足够重要的影响。
当权重值也很大时,输入值对输出的影响也将变得不太重要。因此,我们一般将权重值初始化为更接近零的较小数值。同时,为了获得最佳的权重值,通常设置初始权重的范围变化不大,比如权重初始化为介于 -1+1 之间的随机值。

4. 修改优化器

不同优化器同样可能会影响模型学习拟合输入和输出的速度,在本节中,将了解修改优化器对模型准确性的影响。为了便于比较随机梯度下降( Stochastic Gradient Descent, SGD )和 Adam 在更多 epoch 上的性能,将 epoch 修改为 20

(1) 修改优化器,在 get_model() 函数中使用 SGD 优化器,同时确保其他设定保持不变:

from torch.optim import SGD, Adam
def get_model():
    model = nn.Sequential(
        nn.Linear(28 * 28, 1000),
        nn.ReLU(),
        nn.Linear(1000, 10)
    ).to(device)

    loss_fn = nn.CrossEntropyLoss()
    optimizer = SGD(model.parameters(), lr=1e-2)
    return model, loss_fn, optimizer

(2) 增加训练模型的 epoch 数:

trn_dl, val_dl = get_data()
model, loss_fn, optimizer = get_model()

train_losses, train_accuracies = [], []
val_losses, val_accuracies = [], []
for epoch in range(20):
    print(epoch)
    train_epoch_losses, train_epoch_accuracies = [], []
    for ix, batch in enumerate(iter(trn_dl)):
        x, y = batch
        batch_loss = train_batch(x, y, model, optimizer, loss_fn)
        train_epoch_losses.append(batch_loss) 
    train_epoch_loss = np.array(train_epoch_losses).mean()

    for ix, batch in enumerate(iter(trn_dl)):
        x, y = batch
        is_correct = accuracy(x, y, model)
        train_epoch_accuracies.extend(is_correct)
    train_epoch_accuracy = np.mean(train_epoch_accuracies)
    for ix, batch in enumerate(iter(val_dl)):
        x, y = batch
        val_is_correct = accuracy(x, y, model)
        validation_loss = val_loss(x, y, model, loss_fn)
    val_epoch_accuracy = np.mean(val_is_correct)
    train_losses.append(train_epoch_loss)
    train_accuracies.append(train_epoch_accuracy)
    val_losses.append(validation_loss)
    val_accuracies.append(val_epoch_accuracy)

epochs = np.arange(20)+1
import matplotlib.ticker as mtick
import matplotlib.ticker as mticker
plt.subplot(121)
plt.plot(epochs, train_losses, 'bo', label='Training loss')
plt.plot(epochs, val_losses, 'r', label='Validation loss')
plt.gca().xaxis.set_major_locator(mticker.MultipleLocator(1))
plt.title('Training and validation loss with SGD optimizer')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.grid('off')
plt.subplot(122)
plt.plot(epochs, train_accuracies, 'bo', label='Training accuracy')
plt.plot(epochs, val_accuracies, 'r', label='Validation accuracy')
plt.gca().xaxis.set_major_locator(mticker.MultipleLocator(1))
plt.title('Training and validation accuracy with SGD optimizer')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.gca().set_yticklabels(['{:.0f}%'.format(x*100) for x in plt.gca().get_yticks()]) 
plt.legend()
plt.grid('off')
plt.show()

进行这些更改后,优化器为 SGD 时,训练和验证数据集上的准确率和损失的变化如下所示:

准确率和损失的变化

在优化器为 Adam 时,训练和验证数据集上的准确率和损失值的变化如下:

准确率和损失的变化

与其他优化器相比,Adam 通常优化器可以更快地实现最佳准确率,其他一些可用的优化器包括 AdagradAdadeltaAdamWLBFGSRMSprop

5. 构建深层神经网络

到目前为止,我们构建的神经网络架构只有一个隐藏层。在本节中,我们将对比具有两个隐藏层和没有隐藏层的神经网络模型的性能。

(1) 构建包含两层隐藏层的神经网络模型:

def get_model():
    model = nn.Sequential(
        nn.Linear(28 * 28, 1000),
        nn.ReLU(),
        nn.Linear(1000, 512),
        nn.ReLU(),
        nn.Linear(512, 10)
    ).to(device)

    loss_fn = nn.CrossEntropyLoss()
    optimizer = Adam(model.parameters(), lr=1e-3)
    return model, loss_fn, optimizer

(2) 类似地,修改 get_model() 函数构建不含隐藏层的神经网络,将输入直接连接到输出层:

from torch.optim import SGD, Adam
def get_model():
    model = nn.Sequential(
        nn.Linear(28 * 28, 10)
    ).to(device)

    loss_fn = nn.CrossEntropyLoss()
    optimizer = Adam(model.parameters(), lr=1e-3)
    return model, loss_fn, optimizer

训练和验证数据集的准确率和损失变化如下所示:

训练和验证数据集的准确率和损失变化

从以上结果可以看出:

  • 当没有隐藏层时,模型无法学习
  • 与一个隐藏层相比,当有两个隐藏层时,模型的过拟合会更严重

深度神经网络意味着在输入层和输出层间存在多个隐藏层。多个隐藏层确保神经网络可以学习输入和输出之间的复杂非线性关系,而简单的神经网络则无法完成这样的需求(由于隐藏层数量有限)。

小结

神经网络性能优化技术是指通过改进神经网络的结构、参数初始化、正则化和训练过程等方面来提高其性能和泛化能力的方法。本节首先训练了一个简单的全连接网络,然后在此基础上介绍了简单有效的神经网络性能提升技巧,在之后的学习中,还将进一步介绍包括批归一化、动态学习率等常见技术。

系列链接

PyTorch深度学习实战(1)——神经网络与模型训练过程详解
PyTorch深度学习实战(2)——PyTorch基础
PyTorch深度学习实战(3)——使用PyTorch构建神经网络
PyTorch深度学习实战(4)——常用激活函数和损失函数详解
PyTorch深度学习实战(5)——计算机视觉基础

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

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

相关文章

AcWing167. 木棒(DFS+剪枝)

输入样例: 9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0输出样例: 6 5 解析: DFS 搜索顺序:根据木棒的长度从小到大枚举每根木棒,对于每根木棒,枚举可以由哪些木棍拼成,如果所有的木棍拼成了长度相等的多…

Cisco学习笔记(CCNA)——Equipment Infrastructure Management

Equipment infrastructure management 路由器组件 路由器的组成及功能 CPU:执行操作系统的指令 随机访问存储器(RAM内存):RAM中内容断电丢失 只读存储器(ROM):开机自检软件,路由…

速锐得智能汽车车身域CANFD控制芯片MCU接口电路原理图

CAN总线技术不仅涉及汽车电子和轨道交通,还涉及医疗器械、工业控制、智能家居和机器人网络互连,这些行业对CAN产品的稳定性和抗干扰能力都有很高的要求。 上篇我们讲了在汽车CAN FD上,数据出错可能导致数据位被错误地解析为填充位&#xff0c…

MB5B在HDB上的性能调优

背景 MB5B是用于查询物料的收发以及现有库存。日常业务查询,通常会按照月份查看某片地区物料的库存以及收发状态。 调优思路 按照客户日常操作的习惯,得到日常操作的数据范围,选出数据量最为突出最有代表性的地区和物料;利用SE30分别运行不同数量级的数据,比如20个门店、…

系统程序的编译与处理

目录: 一,程序的编译与执行 二,预处理详解 三,#define的运用 四,条件编译 一,程序的编译与执行 1,编译环境 首先,要说明的是,计算机只能识别二进制指令&#xff0c…

文件共享服务器(五)sicis

目录 前言 一、概述 1.iscsi概念 2.iscsi介绍 3.相关名词 二、实验 1.构建iscsi服务 2.实现步骤 服务器端 客户端 3.注意事项 总结 前言 iSCSI是由IBM发明的基于以太网的存储协议,该协议与SUN的NFS协议都是为了解决存储资源共享问题的解决方案。两者意图…

传统商超苦战即时零售,或沦为炮灰

眼下,在美团闪购、京东到家、饿了么、淘宝买菜/淘鲜达、盒马,还有朴朴超市、叮咚买菜等一众类超市App或者平台的绝情裹挟下,包含沃尔玛、家乐福、永辉、大润发、联华、华润万家、步步高、中百等等这些传统商超企业巨头,正过得越来…

Unity噪声图生成(编辑器扩展)

最近发现项目里很多shader都需要噪声图,(shadergraph中有自己的噪声图生成)当遇到需要噪声图时去寻找很麻烦,所以从网上查阅资料编写了一个Unity扩展的噪声图生成。 Perlin噪声 Perlin噪声是一种渐变噪声算法,由Ken …

【面试】 redis击穿现象?如何防止击穿?

文章目录 背景击穿案例解决方案:通过synchronized双重检查机制:某个key只让一个线程查询,阻塞其它线程设置value永不过期(设置热点数据永不过期)使用互斥锁(mutex key) 背景 大家都知道,计算机的瓶颈之一就是IO,为了解决内存与磁…

组件化开发复习

1.vue的根组件使用 // 1.创建appconst app Vue.createApp({// data: option apidata() {return {message: "Hello Vue",counter: 0,counter2: 0,content: ""}},watch: {content(newValue) {console.log("content:", newValue)}}}) createApp 函…

C#之事件

目录 一、发布者和订阅者 (一)概述 (二)有关事件的重要事项 (三)有关事件的私有委托需要了解的重要事项 二、源代码组件概览 三、声明事件 事件是成员 四、订阅事件 五、触发事件 六、标准事件的…

分析-WinHttpReceiveResponse失败问题追踪

Windows中的WinHttp库提供了比较完善的访问HTTP资源的接口API,一次在使用WinHTTP爬取QQ邮箱过程中,WinHttpReceiveResponse的调用总是失败,于是对此问题进行跟踪。 开始分析QQ邮箱的HTTP交互协议时,用到了代理工具Fiddler&#xf…

t.einsum(‘ijk,jkl->ijl‘, [a,b])

这个东西虽然计算起来真的方便的很多,但是对于人的理解难度是真的加大的,特别是高纬度的时候,例如:t.einsum(‘ijk,jkl->ijl’, [a,b])三维计算的时候。因此,最好的方法就是举个例子并且换一种方式来实现相同的功能…

安卓开发--4步实现Menu菜单动态显示隐藏

MenuInflater用法_韦_恩的博客-CSDN博客MenuInflater是用来加载menu布局文件的.应用程序运行时会预先加载资源中的布局文件,如果Menu布局中的资源比较多,会影响性能,所以可以选择MenuInflater方式用的时候加载,这样减轻了应用程序…

C语言通讯录

在本博客中,我们将介绍如何使用C语言构建一个基本的通讯录。主要涉及C语言的指针、结构体、动态内存管理、文件操作等方面的知识。我们还将学习如何使用C语言的各种功能和技巧来实现通讯录的各种操作,如添加联系人、编辑联系人、删除联系人和搜索联系人等…

并发与并行的区别(详细介绍)

并发和并行的区别为:意思不同、侧重不同、处理不同。 一、意思不同 1、并发:并发是指两个或多个事件在同一时间间隔发生,把任务在不同的时间点交给处理器进行处理。在同一时间点,任务并不会同时运行。 2、并行:并行…

【uniapp】更改富文本编辑器图片大小

代码块 //<view v-html"productDetails"></view><rich-text :nodes"productDetails"></rich-text>// 假设htmlContent字段是后台返回的富文本字段var htmlContent res.result.productDetailsconst regex new RegExp(<img, gi…

macOS Big Sur 11.7.9 (20G1426) 正式版 ISO、PKG、DMG、IPSW 下载

macOS Big Sur 11.7.9 (20G1426) 正式版 ISO、PKG、DMG、IPSW 下载 本站下载的 macOS 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U 盘安装&#xff0c;或者在虚拟机中启动安装。另外也支持在 Window…

nginx怎么做负载均衡

Nginx怎么做负载均衡 Nginx 是一个高性能的开源反向代理服务器&#xff0c;可以用于实现负载均衡。负载均衡指的是将用户请求平均分配给多个服务器&#xff0c;以提高整体系统性能和可靠性。下面是一个详细介绍如何使用 Nginx 实现负载均衡的步骤&#xff1a; 步骤 1&#xf…

vue项目打包成App

地址一 地址二 一、将项目开发完成后&#xff0c;在vue.config.js 文件中添加路径 publicPath:‘./’ 在router/index.js关闭路由的history模式&#xff08;默认哈希&#xff09; 二、npm run build&#xff0c;生成的dist文件目录 三、打开 HBuilder X 开发工具 新建 >…