当前位置: 首页 > news >正文

PyTorch深度学习实战(12)——数据增强

PyTorch深度学习实战(12)——数据增强

    • 0. 前言
    • 1. 图像增强
      • 1.1 仿射变换
      • 1.2 亮度修改
      • 1.3 添加噪音
      • 1.4 联合使用多个增强方法
    • 2. 对批图像执行图像增强
    • 3. 利用数据增强训练模型
    • 小结
    • 系列链接

0. 前言

数据增强是指通过对原始数据进行一系列变换和处理,生成更多、更丰富的训练样本的技术方法。数据增强在机器学习和深度学习领域中被广泛应用,它可以有效地解决数据不足的问题,提高模型的泛化能力和鲁棒性。我们已经了解了卷积神经网络 (Convolutional Neural Network, CNN) 有助于解决图像平移问题,但如果平移的范围过大同样可能影响模型的性能。在本节中,我们将学习如何使用数据增强确保模型能够得到正确的预测结果,即使图像移动较大范围。

1. 图像增强

数据增强的目的是通过对原始数据进行合理的变换,生成新的样本,使得这些样本在保持原始类别标签不变的情况下,尽可能涵盖更多的数据特征和变化情况。在计算机视觉领域,对于给定的图像,即使我们平移,旋转或缩放图像,图像的标签也将保持不变。
基于上述原理,数据增强是从给定的图像集中创建更多图像的一种方法,即通过旋转,平移或缩放它们并将它们映射到原始图像的标签,以扩充数据集。通过随机平移输入图像并将它们传递给网络来训练神经网络,相同的图像将在不同批次中作为不同的图像处理,因为在每个批次中具有不同的平移量。我们已经了解了图像平移对模型预测准确性的影响。但是,在现实世界中,我们可能会遇到其他多种图像变换的情况:

  • 图像旋转
  • 图像缩放
  • 图片翻转
  • 图像剪切
  • 图像中存在噪点
  • 图像亮度较低/高

如果不考虑以上情况,训练出的神经网络并不会得到准确的预测结果。图像增强根据给定图像创建更多图像,通过旋转、平移、缩放、添加噪声和亮度等修改原始图像,此外,可以使用不同的参数指定图像的变化程度(例如,某个图像的平移可以是 +10 像素,也可以是 -5 像素)。imgaug 库中的 augmenters 类可以用于实现数据增强,常用的数据增强技术如下:

  • 仿射变换
  • 亮度修改
  • 添加噪音

PyTorch 中同样包含图像增强管道 torchvision.transforms。但是,imgaug 包含更多选项,且易于解释数据增强功能,可以使用 pip 命令安装 imgaug 库:

pip install imgaug

1.1 仿射变换

仿射变换包括平移、旋转、缩放和剪切图像,可以使用 augmenters 类中的 Affine 方法执行仿射变换。Affine 方法中包含以下重要参数:

  • scale:要对图像进行的缩放量
  • translate_percent: 将平移量指定为图像高度和宽度的百分比
  • translate_px:将平移量指定为绝对像素数
  • rotate:要在图像上完成的旋转量
  • shear:要在图像的一部分上完成的旋转量

(1)Fashion-MNIST 数据集中获取随机图像:

import imgaug
import imgaug.augmenters as iaa
print(imgaug.__version__)
# 0.4.0from torchvision import datasets
import torch
data_folder = './data/FMNIST'
fmnist = datasets.FashionMNIST(data_folder, download=True, train=True)tr_images = fmnist.data
tr_targets = fmnist.targetsimport matplotlib.pyplot as plt
import numpy as np
from torch.utils.data import Dataset, DataLoader
import torch.nn as nn
device = 'cuda' if torch.cuda.is_available() else 'cpu'def to_numpy(tensor):return tensor.cpu().detach().numpy()plt.imshow(tr_images[0], cmap='gray')
plt.title('Original image')
plt.show()

原始图像
(2) 定义用于执行缩放的对象 aug

aug = iaa.Affine(scale=2)

(3) 指定使用 aug 对象中的 augment_image 方法执行图像增强,并进行绘制:

plt.imshow(aug.augment_image(to_numpy(tr_images[0])))
plt.title('Scaled image')
plt.show()

缩放图像
在以上输出中,图像已被放大,但由于图像的输出形状没有改变,因此会从原始图像中删除一部分像素。

(4) 使用 translate_px 参数执行图像平移:

aug = iaa.Affine(translate_px=10)
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Translated image by 10 pixels (right and bottom)')
plt.show()

图像平移
在以上输出中,x 轴和 y 轴都平移了 10 个像素。如果两个轴上平移不同的像素量,则必须指定在每个轴上的平移量:

aug = iaa.Affine(translate_px={'x':10,'y':2})
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Translation of 10 pixels \nacross columns and 2 pixels over rows')
plt.show()

图像平移
在以上输出结果中,在 translate_px 参数中使用字典指定 xy 轴的平移量,图像在 x 轴上平移了更多像素。

(5) 观察旋转和剪切对图像增强的影响:

plt.figure(figsize=(20,20))
plt.subplot(151)
plt.imshow(tr_images[0], cmap='gray')
plt.title('Original image')
plt.subplot(152)
aug = iaa.Affine(scale=2)
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Scaled image')
plt.subplot(153)
aug = iaa.Affine(translate_px={'x':10,'y':2})
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Translation of 10 pixels across \ncolumns and 2 pixels over rows')
plt.subplot(154)
aug = iaa.Affine(rotate=30)
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Rotation of image \nby 30 degrees')
plt.subplot(155)
aug = iaa.Affine(shear=30)
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Shear of image \nby 30 degrees')
plt.show()

旋转和剪切
在以上输出中,可以看到某些像素在转换后从图像中被裁剪掉。接下来,我们利用 Affine 方法中的 fit_output 参数确保图像不因裁剪丢失信息。默认情况下,fit_output 设置为 False,将 fit_output 指定为 True 时,观察在缩放、平移、旋转和剪切图像时,输出图像的变化:

plt.figure(figsize=(20,20))
plt.subplot(151)
plt.imshow(tr_images[0], cmap='gray')
plt.title('Original image')
plt.subplot(152)
aug = iaa.Affine(scale=2, fit_output=True)
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Scaled image')
plt.subplot(153)
aug = iaa.Affine(translate_px={'x':10,'y':2}, fit_output=True)
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Translation of 10 pixels across \ncolumns and 2 pixels over rows')
plt.subplot(154)
aug = iaa.Affine(rotate=30, fit_output=True)
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Rotation of image \nby 30 degrees')
plt.subplot(155)
aug = iaa.Affine(shear=30, fit_output=True)
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Shear of image \nby 30 degrees')
plt.show()

旋转和剪切
可以看到原始图像没有被裁剪,并且会增加图像的大小以进行完整显示。当增强图像的大小增加时,我们需要清楚如何填充不属于原始图像的新像素。

fit_outputTrue 时,使用 cval 参数指定创建的新像素的像素值。在以上代码中,cval 填充了默认值 0,即黑色像素,接下来,将 cval 参数改为 255,即白色像素,观察输出结果:

aug = iaa.Affine(rotate=30, fit_output=True, cval=255)
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Rotation of image by 30 degrees')
plt.show()

图像填充
此外,可以使用不同的模式来填充新创建的像素的值,mode 参数的可选值如下:

  • constant:使用恒定值填充
  • edge:用输入的边缘值填充
  • symmetric:用沿输入边缘的反射填充
  • reflect:用反射向量填充
  • wrap:用沿轴的向量填充

cval 设置为 0 并使用不同 mode 参数:

plt.figure(figsize=(20,20))
plt.subplot(151)
aug = iaa.Affine(rotate=30, fit_output=True, cval=0, mode='constant')
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Rotation of image by \n30 degrees with constant mode')
plt.subplot(152)
aug = iaa.Affine(rotate=30, fit_output=True, cval=0, mode='edge')
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Rotation of image by 30 degrees \n with edge mode')
plt.subplot(153)
aug = iaa.Affine(rotate=30, fit_output=True, cval=0, mode='symmetric')
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Rotation of image by \n30 degrees with symmetric mode')
plt.subplot(154)
aug = iaa.Affine(rotate=30, fit_output=True, cval=0, mode='reflect')
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Rotation of image by 30 degrees \n with reflect mode')
plt.subplot(155)
aug = iaa.Affine(rotate=30, fit_output=True, cval=0, mode='wrap')
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.title('Rotation of image by \n30 degrees with wrap mode')
plt.show()

图像填充示例
在执行数据增强时,很难指定图像需要旋转的确切角度,通常提供图像将旋转的范围:

plt.figure(figsize=(20,20))
plt.subplot(141)
aug = iaa.Affine(rotate=(-45,45), fit_output=True, cval=0, mode='constant')
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.subplot(142)
aug = iaa.Affine(rotate=(-45,45), fit_output=True, cval=0, mode='constant')
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.subplot(143)
aug = iaa.Affine(rotate=(-45,45), fit_output=True, cval=0, mode='constant')
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.subplot(144)
aug = iaa.Affine(rotate=(-45,45), fit_output=True, cval=0, mode='constant')
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray')
plt.show()

不同旋转角度
在以上输出中,由于根据旋转的上限和下限指定了可能的旋转角度范围,相同的图像在不同的迭代中旋转角度不同。同样,我们可以在平移或缩放图像时引入随机化增强。

1.2 亮度修改

由于图像中的照明条件不同,背景和前景之间的差异有时并不明显。如果在训练模型时背景的像素值始终为 0,前景的像素值始终为 255,而预测图像的背景像素值为 20,前景像素值为 220,则预测很可能并不正确。乘法( Multiply )和线性对比度( Linearcontrast) 是两种不同的增强技术,可以解决照明条件不同的问题。

Multiply 方法将每个像素值乘以指定的值,例如将图像中的每个像素值乘以 0.5 后输出:

aug = iaa.Multiply(0.5)
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray',vmin = 0, vmax = 255)
plt.title('Pixels multiplied by 0.5')
plt.show()

Multiply 方法
Linearcontrast 根据以下公式调整每个像素值:

127 + α × ( x i − 127 ) 127+\alpha \times(x_i-127) 127+α×(xi127)

其中, x i x_i xi 表示像素值,当 α α α 等于 1 时,像素值保持不变,当 α α α 小于 1 时,高像素值减少,低像素值增加。观察对输出图像的影响:

aug = iaa.LinearContrast(0.5)
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray',vmin = 0, vmax = 255)
plt.title('Pixel contrast by 0.5')
plt.show()

Linearcontrast
可以看到,使用 Linearcontrast 方法,图像中的背景变得更加明亮,而前景像素的强度降低了。

使用 GaussianBlur 方法模糊图像以模拟真实场景(图像可能由于运动而模糊):

aug = iaa.GaussianBlur(sigma=1)
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray',vmin = 0, vmax = 255)
plt.title('Gaussian blurring of image\n with a sigma of 1')
plt.show()

GaussianBlur
可以看到图像非常模糊,并且随着 sigma 值的增加,图像也会变得更加模糊。

1.3 添加噪音

在现实世界的场景中,可能会在图像中包含噪点,DropoutSaltAndPepper 是用于模拟图像噪声的两种主要方法:

aug = iaa.Dropout(p=0.2)
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray',vmin = 0, vmax = 255)
plt.title('Random 20% pixel dropout')
plt.show()aug = iaa.SaltAndPepper(0.2)
plt.imshow(aug.augment_image(to_numpy(tr_images[0])), cmap='gray',vmin = 0, vmax = 255)
plt.title('Random 20% salt and pepper noise')
plt.show()

添加噪声
可以看到 Dropout 方法随机丢弃了一定数量的像素,即将它们的像素值转换为 0,而 SaltAndPepper 方法则在图像中随机添加白色和黑色的像素。

1.4 联合使用多个增强方法

在现实世界的场景中,我们必须综合使用尽可能多的增强方法。在本节中,我们将了解图像增强的 Sequential 方式,
Sequential 方法中可以使用需要执行的增强方法来构造图像增强。如果只考虑旋转和 Dropout 来增强图像,Sequential 对象如下所示:

seq = iaa.Sequential([iaa.Dropout(p=0.2,),iaa.Affine(rotate=(-30,30))], random_order= True)

在以上代码中,指定两种增强方法,并且使用 random_order 参数,指示采用随机顺序执行两种增强方法:

plt.imshow(seq.augment_image(to_numpy(tr_images[0])), cmap='gray',vmin = 0, vmax = 255)
plt.title('Image augmented using a \nrandom orderof the two augmentations')
plt.show()

联合使用多个增强方法

2. 对批图像执行图像增强

为了最大限度的提高模型性能,需要同一图像在不同迭代中执行不同的增强。如果我们在 __init__ 方法中定义了一个增强管道,则只需要对输入图像集执行一次增强,这意味着在不同的迭代中不会有不同的增强;如果增强是在 __getitem__ 方法中,会对每个图像执行一组不同的增强,对每个图像执行一次增强。如果每次对一批图像而不是一次对一个图像执行增强,可以提高算法执行效率,接下来,我们对比以下两种方案:

  • 一次对一张图像执行增强,得到 32 张图像
  • 一次对一批图像执行增强,得到 32 张图像

为了了解在这两种情况下执行图像增强所需的时间,利用 Fashion-MNIST 数据集的训练图像中的前 32 张图像。

(1) 获取训练数据集中的前 32 张图像:

from torchvision import datasets
import torch
device = 'cuda' if torch.cuda.is_available() else 'cpu'
import timedata_folder = './data/FMNIST'
fmnist = datasets.FashionMNIST(data_folder, download=True, train=True)tr_images = fmnist.data
tr_targets = fmnist.targetsdef to_numpy(tensor):return tensor.cpu().detach().numpy()

(2) 指定要对图像执行的增强:

from imgaug import augmenters as iaa
aug = iaa.Sequential([iaa.Affine(translate_px={'x':(-10,10)}, mode='constant'),])

接下来,介绍如何在 Dataset 类中扩充数据,有两种方法来扩充数据:

  • 每次增强一批数据中的一张图像
  • 一次性增强一批数据中的所有图像

增强批数据中的 32 张图像,一次一张
使用 augment_image 方法,计算增强批数据中的 32 张图像(一次增强一张)所需的时间:

start = time.time()
for i in range(32):aug.augment_image(to_numpy(tr_images[i]))
print('total times: ', time.time()-start)

增强 32 张图像大约需要 33 毫秒。

一次性增强批数据中的 32 张图像
使用 augment_images 方法,计算一次性增强 32 张图像所需的时间:

start = time.time()
x = aug.augment_images(to_numpy(tr_images[:32]))
print('total times: ', time.time()-start)

对一批图像执行增强大约需要 12 毫秒。

因此,最佳实践是在一批图像之上执行增强,而不是一次增强一个图像,augment_images 方法的输出是一个 numpy 数组。但是,之前使用的 Dataset 类在 __getitem__ 方法中一次提供一张图像的索引。因此,需要创建一个新函数 collate_fn,使我们能够对一批图像执行操作。

(3) 定义 Dataset 类,将输入图像、类别和增强对象作为初始化器:

class FMNISTDataset(Dataset):def __init__(self, x, y, aug=None):self.x, self.y = x, yself.aug = augdef __getitem__(self, ix):x, y = self.x[ix], self.y[ix]return x, ydef __len__(self):return len(self.x)

(4) 定义 collate_fn 函数,将批数据作为输入:

    def collate_fn(self, batch):

将批图像及其类别分成两个不同的变量:

        ims, classes = list(zip(*batch))

如果提供了增强对象,则执行数据增强,因为我们只需要对训练数据执行增强,而验证数据无需执行增强:

        # transform a batch of images at onceif self.aug:ims=self.aug.augment_images(images=list(map(to_numpy,list(ims))))

在以上代码中,使用 augment_images 方法,以便可以一次性处理一批图像。

创建图像张量,并通过将数据除以 255 来缩放数据:

        ims = torch.tensor(ims)[:,None,:,:].to(device)/255.classes = torch.tensor(classes).to(device)return ims, classes

一般来说,当我们需要执行复杂计算时,会利用 collate_fn 方法,这是因为一次性对一批图像执行计算比一次执行一个图像要快得多。

(5) 为了利用 collate_fn 方法,在创建 DataLoader 时使用一个新参数。

首先,创建 train 对象:

train = FMNISTDataset(tr_images, tr_targets, aug=aug)

接下来,定义 DataLoader 以及对象的 collate_fn 方法:

trn_dl = DataLoader(train, batch_size=64,collate_fn=train.collate_fn, shuffle=True)

最后,训练模型,通过利用 collate_fn 方法,可以更快地训练模型。

3. 利用数据增强训练模型

接下来,我们使用增强数据训练模型,观察数据增强对模型训练的影响。

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

from torchvision import datasets
import torch
import matplotlib.pyplot as plt
import numpy as np
from torch.utils.data import Dataset, DataLoader
import torch
import torch.nn as nn
device = 'cuda' if torch.cuda.is_available() else 'cpu'data_folder = './data/FMNIST' 
fmnist = datasets.FashionMNIST(data_folder, download=True, train=True)tr_images = fmnist.data
tr_targets = fmnist.targetsval_fmnist = datasets.FashionMNIST(data_folder, download=True, train=False)
val_images = val_fmnist.data
val_targets = val_fmnist.targets

(2) 创建数据集类,用于随机平移图像执行数据增强:

定义数据增强管道:

from imgaug import augmenters as iaa
aug = iaa.Sequential([iaa.Affine(translate_px={'x':(-10,10)},mode='constant'),])

定义数据集类:

def to_numpy(tensor):return tensor.numpy()class FMNISTDataset(Dataset):def __init__(self, x, y, aug=None):self.x, self.y = x, yself.aug = augdef __getitem__(self, ix):x, y = self.x[ix], self.y[ix]return x, ydef __len__(self):return len(self.x)def collate_fn(self, batch):'logic to modify a batch of images'ims, classes = list(zip(*batch))# transform a batch of images at onceif self.aug:ims=self.aug.augment_images(images=list(map(to_numpy,list(ims))))ims = torch.tensor(ims)[:,None,:,:].to(device)/255.classes = torch.tensor(classes).to(device)return ims, classes

在以上代码中,利用 collate_fn 方法来指定要对批图像执行增强。

(3) 定义模型架构:

from torch.optim import SGD, Adam
def get_model():model = nn.Sequential(nn.Conv2d(1, 64, kernel_size=3),nn.MaxPool2d(2),nn.ReLU(),nn.Conv2d(64, 128, kernel_size=3),nn.MaxPool2d(2),nn.ReLU(),nn.Flatten(),nn.Linear(3200, 256),nn.ReLU(),nn.Linear(256, 10)).to(device)loss_fn = nn.CrossEntropyLoss()optimizer = Adam(model.parameters(), lr=1e-3)return model, loss_fn, optimizer

(4) 定义 train_batch 函数以在批数据上训练模型:

def train_batch(x, y, model, optimizer, loss_fn):prediction = model(x)batch_loss = loss_fn(prediction, y)batch_loss.backward()optimizer.step()optimizer.zero_grad()return batch_loss.item()

(5) 定义 get_data 函数获取训练和验证 DataLoaders

def get_data():train = FMNISTDataset(tr_images, tr_targets, aug=aug)'notice the collate_fn argument'trn_dl = DataLoader(train, batch_size=64,collate_fn=train.collate_fn, shuffle=True)val = FMNISTDataset(val_images, val_targets)val_dl = DataLoader(val, batch_size=len(val_images),collate_fn=val.collate_fn, shuffle=True)return trn_dl, val_dl

(6) 指定训练和验证 DataLoaders 并获取模型对象、损失函数和优化器:

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

(7) 训练模型:

for epoch in range(10):print(epoch)for ix, batch in enumerate(iter(trn_dl)):x, y = batchbatch_loss = train_batch(x, y, model, optimizer, loss_fn)

(8) 在平移图像上测试模型:

preds = []
ix = 24150
for px in range(-5,6):img = tr_images[ix]/255.img = img.view(28, 28)plt.subplot(1, 11, px+6)img2 = np.roll(img, px, axis=1)img3 = torch.Tensor(img2).view(-1,1,28,28).to(device)np_output = model(img3).cpu().detach().numpy()pred = np.exp(np_output)/np.sum(np.exp(np_output))preds.append(pred)plt.imshow(img2)plt.title(fmnist.classes[pred[0].argmax()])plt.show()

图像平移
绘制模型关于平移图形的预测类别变化:

import seaborn as sns
fig, ax = plt.subplots(1,1, figsize=(12,10))
plt.title('Probability of each class for various translations')
sns.heatmap(np.array(preds).reshape(11,10), annot=True, ax=ax, fmt='.2f', xticklabels=fmnist.classes, yticklabels=[str(i)+str(' pixels') for i in range(-5,6)], cmap='gray')
plt.show()

类别预测
可以看到,当我们预测各种平移图像时,模型能够以极高的置信度预测图像的正确类别。

小结

数据增强是一种有效的提升模型性能的方法,通过扩充训练数据集和增加数据的多样性,可以提高模型的泛化能力和鲁棒性。在实际应用中,可以根据需求选择适当的数据增强方法,并进行合理的参数设置,以获得更好的训练效果。imgaug 是一个用于机器学习中图像增强的 Python 库,它支持多种增强技术,能够轻松组合这些技术,且有丰富的文档支持,能满足大多数的数据增强的需求。本节中,介绍了图像增强的基本概念,并使用 imgaug 介绍了常见的图像增强技术,通过实验表明使用图像增强能够显著提高神经网络模型性能。

系列链接

PyTorch深度学习实战(1)——神经网络与模型训练过程详解
PyTorch深度学习实战(2)——PyTorch基础
PyTorch深度学习实战(3)——使用PyTorch构建神经网络
PyTorch深度学习实战(4)——常用激活函数和损失函数详解
PyTorch深度学习实战(5)——计算机视觉基础
PyTorch深度学习实战(6)——神经网络性能优化技术
PyTorch深度学习实战(7)——批大小对神经网络训练的影响
PyTorch深度学习实战(8)——批归一化
PyTorch深度学习实战(9)——学习率优化
PyTorch深度学习实战(10)——过拟合及其解决方法
PyTorch深度学习实战(11)——卷积神经网络

相关文章:

PyTorch深度学习实战(12)——数据增强

PyTorch深度学习实战(12)——数据增强 0. 前言1. 图像增强1.1 仿射变换1.2 亮度修改1.3 添加噪音1.4 联合使用多个增强方法 2. 对批图像执行图像增强3. 利用数据增强训练模型小结系列链接 0. 前言 数据增强是指通过对原始数据进行一系列变换和处理&…...

SpringCloud Ribbon中的7种负载均衡策略

SpringCloud Ribbon中的7种负载均衡策略 Ribbon 介绍负载均衡设置7种负载均衡策略1.轮询策略2.权重策略3.随机策略4.最小连接数策略5.重试策略6.可用性敏感策略7.区域敏感策略 总结 负载均衡通器常有两种实现手段,一种是服务端负载均衡器,另一种是客户端…...

04 qt功能类、对话框类和文件操作

一 QT中时间和日期 时间 ---- QTime日期 ---- QDate对于Qt而言,在实际的开发过程中, 1)开发者可能知道所要使用的类 ---- >帮助手册 —>索引 -->直接输入类名进行查找 2)开发者可能不知道所要使用的类,只知道开发需求文档 ----> 帮助 手册,按下图操作: 1 …...

安装软件包

安装软件包 创建一个名为 /home/curtis/ansible/packages.yml 的 playbook : 将 php 和 mariadb 软件包安装到 dev、test 和 prod 主机组中的主机上 将 RPM Development Tools 软件包组安装到 dev 主机组中的主机上 将 dev 主机组中主机上的所有软件包更新为最新版本 vim packa…...

玩转单元测试之gmock

引言 前文我们学习了gtest相关的使用,单靠gtest,有些场景仍然无法进行测试,因此就诞生了gmock。 gmock快速入门 在引入gtest时,gmock也同样引入了,因此只需要在编译时加上合适的编译选项即可,注意不同版…...

POI与EasyExcel--写Excel

简单写入 03和07版的简单写入注意事项: 1. 对象不同:03对应HSSFWorkbook,07对应XSSFWorkbook 2. 文件后缀不同:03对应xls,07对应xlsx package com.zrf;import org.apache.poi.hssf.usermodel.HSSFWorkbook; import …...

7. CSS(四)

目录 一、浮动 (一)传统网页布局的三种方式 (二)标准流(普通流/文档流) (三)为什么需要浮动? (四)什么是浮动 (五)浮…...

uni-app 集成推送

研究了几天,终于是打通了uni-app的推送,本文主要针对的是App端的推送开发过程,分为在线推送和离线推送。我们使用uni-app官方推荐的uni-push2.0。官方文档 准备工作:开通uni-push功能 勾选uniPush2.0点击"配置"填写表单…...

Spring Boot+Redis 实现消息队列实践示例

Spring BootRedis 实现一个轻量级的消息队列 文章目录 Spring BootRedis 实现一个轻量级的消息队列0.前言1.基础介绍2.步骤2.1. 引入依赖2.2. 配置文件2.3. 核心源码 4.总结答疑 5.参考文档6. Redis从入门到精通系列文章 0.前言 本文将介绍如何利用Spring Boot与Redis结合实现…...

11. 实现业务功能--获取用户信息

目录 1. 实现 Controller 2. 单体测试 3. 修复返回值存在的缺陷 3.1 用户的隐私数据:密码的密文和盐不能显示 3.2 将值为 null 的字段可以进行过滤 3.3 时间的格式需要进行处理,如 yyyy-mmmm-ddd HH:mm:ss 3.4 data 属性没有返回 4. 实现前端页…...

HTTPS

HTTPS是什么 HTTPS 属于应用层协议,其原理是通过SSL/TLS协议在HTTP和TCP之间插入一层安全机制。通过SSL/TLS握手过程,客户端和服务器协商出一个对称密钥,用于后续的数据加密和解密,从而保证数据的机密性和完整性。 为什么会需要…...

spring详解

spring是于2003年兴起的一款轻量级的,非侵入式的IOC和AOP的一站式的java开发框架,为简化企业级应用开发而生。 轻量级的:指的是spring核心功能的jar包不大。 非侵入式的:业务代码不需要继承或实现spring中任何的类或接口 IOC&…...

香港服务器备案会通过吗?

​  对于企业或个人来说,合规备案是网络运营的基本要求,也是保护自身权益的重要举措。以下内容围绕备案展开话题,希望为您解开疑惑。 香港服务器备案会通过吗? 目前,香港服务器无法备案,这是由于国内管理规定的限制…...

乐鑫推出 ESP ZeroCode 控制台

乐鑫科技 ESP ZeroCode 控制台是一个网页应用,用户只需点击鼠标,描述想要创建的产品类型、功能及其硬件配置,即可按照自身需求,快速生成符合 Matter 认证的固件,并在硬件上进行试用。试用过程中,如有任何不…...

从NLP到聊天机器人

一、说明 今天,当打电话给银行或其他公司时,听到电话另一端的机器人向你打招呼是很常见的:“你好,我是你的数字助理。请问你的问题。是的,机器人现在不仅可以说人类语言,还可以用人类语言与用户互动。这是由…...

相关搜索引擎常用搜索语法(Google hacking语法和FOFA语法)

一:Google Hack语法 Google Hacking原指利用Google搜索引擎搜索信息来进行入侵的技术和行为,现指利用各种搜索引擎并使用一些高级的搜索语法来搜索信息。既利用搜索引擎强大的搜索功能,在在浩瀚的互联网中搜索到我们需要的信息。 &#xff0…...

Mysql查询

第三章:select 语句 SELECT employees.employee_id,employees.department_id FROM employees WHERE employees.employee_id176; DESC departments;SELECT * FROM departments;第四章:运算符使用 SELECT employees.last_name,employees.salary FROM em…...

解决http下navigator.clipboard为undefined问题

开发环境下使用navigator.clipboard进行复制操作,打包部署到服务器上后,发现该功能显示为undefined;查相关资料后,发现clipboard只有在安全域名下才可以访问(https、localhost),在http域名下只能得到undefined&#xf…...

mysql之host is blocked问题

程序上线一段时间之后,更新程序总是遇到这个问题 每次都是重启几次程序,或者执行 flush hosts; 毕竟指标不治本,抽出时间决定分析一下问题,查阅了几篇博客。(感谢这几位大佬) https://blog.51cto.com/u_…...

每日一题:2337 移动片段得到字符串

给你两个字符串 start 和 target ,长度均为 n 。每个字符串 仅 由字符 L、R 和 _ 组成,其中: 字符 L 和 R 表示片段,其中片段 L 只有在其左侧直接存在一个 空位 时才能向 左 移动,而片段 R 只有在其右侧直接存在一个 …...

嵌入式设备的 Json 库基本使用

大家好,今天给介绍一款基于 C 语言的轻量级的 Json 库 – cJson。可用于资源受限的嵌入式设备中。 cJSON 是一个超轻巧,携带方便,单文件,简单的可以作为 ANSI-C 标准的 JSON 解析器。 cJSON 是一个开源项目,github 下…...

GEEMAP 中如何拉伸图像

图像拉伸是最基础的图像增强显示处理方法,主要用来改善图像显示的对比度,地物提取流程中往往首先要对图像进行拉伸处理。图像拉伸主要有三种方式:线性拉伸、直方图均衡化拉伸和直方图归一化拉伸。 GEE 中使用 .sldStyle() 的方法来进行图像的…...

软件测试学术顶会——ISSTA 2023 论文(网络安全方向)清单、摘要与总结

总结 本次会议涵盖的安全研究主题广泛,包括源代码分析、二进制代码分析、恶意软件检测、漏洞检测、模糊测试、程序验证等。一些热门的研究方向包括:基于机器学习的漏洞检测、大型语言模型在软件安全中的应用、区块链智能合约安全分析。这些方向都在最近几年持续发展。一些较冷门…...

基于YOLOv8模型和PCB电子线路板缺陷目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要:基于YOLOv8模型PCB电子线路板缺陷目标检测系统可用于日常生活中检测与定位PCB线路板瑕疵,利用深度学习算法可实现图片、视频、摄像头等方式的目标检测,另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检…...

centos安装mysql8

检查是否有mariadb rpm -qa|grep mariadb rpm -e --nodeps mariadb-server安装mysql-8.0.31-el7-x86_64.tar.gz包 安装mysql-8.0.31-el7-x86_64.tar.gz包 cd datatar -xvf mysql-8.0.31-el7-x86_64.tar.gzmv mysql-8.0.31-el7-x86_64 mysql 依赖安装 报错解决,安…...

【Apollo】阿波罗自动驾驶技术:引领汽车行业革新

前言 Apollo (阿波罗)是一个开放的、完整的、安全的平台,将帮助汽车行业及自动驾驶领域的合作伙伴结合车辆和硬件系统,快速搭建一套属于自己的自动驾驶系统。 开放能力、共享资源、加速创新、持续共赢是 Apollo 开放平台的口号。百度把自己所拥有的强大、…...

一文看懂!数据管道和数据流在数据分析中的作用

当我们谈论数据分析时,我们通常会想到一系列的步骤,包括数据收集、数据清洗、数据分析和数据可视化等。然而,在这些步骤中,有两个非常重要的概念:数据管道和数据流。这两个概念在数据分析过程中起着至关重要的作用。本…...

Linux系统下检验Tensorflow 2.xx版本和1.xx版本是否安装成功

目录 版本问题Tensorflow 1.xx的测试代码:Tensorflow 2.xx的测试代码:Tensorflow 2.6版本实际的测验结果 总结 版本问题 查询资料发现,多数检验Tensorflow是否安装成功的方法,多数方法都是1.xx版本的,直接使用1.xx版本…...

暑期高铁站大量遗失物品,FindMy帮助寻找

近日,一女子在上海坐高铁时,将户口本、房产证遗落安检处的新闻引起网友的关注。然后业内人士表示:常事,车站什么都能捡到。 据中国铁路透露,暑运期间,上海虹桥站客流增加,日均发送旅客20多万人…...

通过安全日志读取WFP防火墙放行日志

前言 之前的文档中,描写了如何对WFP防火墙进行操作以及如何在防火墙日志中读取被防火墙拦截网络通讯的日志。这边文档,着重描述如何读取操作系统中所有被放行的网络通信行为。 读取系统中放行的网络通信行为日志,在win10之后的操作系统上&am…...

JDK、JRE、Java SE、Java EE和Java ME有什么区别?

Java现在已不仅仅是一种语言,从广义上说,它代表了一个技术体系。该体系根据应用方向的不同主要分为Java SE、Java EE和Java ME的3个部分。 1.SE(JavaSE),standard edition,标准版,是我们通常用的一个版本,从…...

Neo4j之unwind基础

UNWIND 语句在 Cypher 查询中用于将列表(数组)中的元素逐个“展开”,从而将每个元素视为单独的行进行处理。它通常与 MATCH、CREATE、MERGE 等语句结合使用,用于对列表中的每个元素执行相应的操作。以下是一些常用的示例和解释&am…...

回归预测 | MATLAB实现SSA-SVM麻雀搜索算法优化支持向量机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现SSA-SVM麻雀搜索算法优化支持向量机多输入单输出回归预测(多指标,多图) 目录 回归预测 | MATLAB实现SSA-SVM麻雀搜索算法优化支持向量机多输入单输出回归预测(多指标,多图)效果一览基…...

深入探索代理技术:Socks5、IP代理与网络安全

在当今高度互联的世界中,代理技术在网络安全和爬虫等领域发挥着重要作用。本文将着重介绍Socks5代理、IP代理以及它们在网络安全与爬虫开发中的应用,旨在帮助读者深入理解这些技术,从而更好地应用于实际情境。 1. Socks5代理的特点与用途 S…...

Matlab 频谱图中如何设置频率刻度

Matlab 频谱图中如何设置频率刻度(横坐标) 1、概述 时域信号经FFT 变换后得到了频谱,在作图时还必须设置正确的频率刻度,这样才能从图中得到正确的结果。下面来介绍如何设置正确的频率刻度。 2、案例分析 有一个余弦信号&#…...

在线转换器有哪些优势?在线Word转PDF操作分享

我们如果想要将两者不同格式文件进行格式转换,就需要下载安装转换器。如果出门带的设备没有安装转换软件客户端,就无法使用,会比较麻烦。现在有了在线转换工具,只需要打开相应的网页就可使用,那么在线Word转PDF的操作是…...

2023国赛数学建模A题思路模型代码汇总 高教社杯

本次比赛我们将会全程更新思路模型及代码,大家查看文末名片获取 之前国赛相关的资料和助攻可以查看 2022数学建模国赛C题思路分析_2022国赛c题matlab_UST数模社_的博客-CSDN博客 2022国赛数学建模A题B题C题D题资料思路汇总 高教社杯_2022国赛c题matlab_UST数模社…...

vue3如何批量设置动态ref

示例如下&#xff1a; <template v-for"item in selectList"><el-select v-model"item.value" :ref"el > setRef(el, item)"><el-optionv-for"v in item.options":key"v.value":label"v.label"…...

Android Studio run app 设置 release 模式

背景 为验证我们的 SDK 集成在客户应用上的质量&#xff0c;需要我们的测试环境尽量的与客户应用保持一致。客户普遍都会打 release 包并混淆&#xff0c;然后进行上线应用&#xff0c;因此我们在测试过程中也需要使用 release 包进行验证。对于 Android Studio 运行项目&…...

【SA8295P 源码分析】41 - SA8295所有镜像位置、拷贝脚本、生成QFIL包

【SA8295P 源码分析】41 - SA8295所有镜像位置、拷贝脚本、生成QFIL包 一、SA8295 各镜像位置二、SA8295 QNX 侧镜像拷贝脚本三、SA8295 Android 侧镜像拷贝脚本四、使用QFIL 下载整包五、Fastboot 下载命令整理系列文章汇总见:《【SA8295P 源码分析】00 - 系列文章链接汇总》…...

【Redis】Redisson分布式锁原理与使用

【Redis】Redisson分布式锁原理与使用 什么是Redisson&#xff1f; Redisson - 是一个高级的分布式协调Redis客服端&#xff0c;能帮助用户在分布式环境中轻松实现一些Java的对象&#xff0c;Redisson、Jedis、Lettuce 是三个不同的操作 Redis 的客户端&#xff0c;Jedis、Le…...

Segment Anything论文阅读笔记

Segment Anything论文阅读笔记 1. Segment Anything论文基本信息2. Segment Anything论文阅读2.1 第一遍阅读 Segment Anything2.2. 第二遍阅读 Segment Anything2.2.1. Segment Anything中相关的图表 1. Segment Anything论文基本信息 论文地址https://arxiv.org/abs/2304.02…...

Python入门教程 | Python 基础语法

标识符 第一个字符必须是字母表中字母或下划线 _ 。标识符的其他的部分由字母、数字和下划线组成。标识符对大小写敏感。 在 Python 3中&#xff0c;可以用中文作为变量名&#xff0c;非 ASCII 标识符也是允许的了。默认情况下&#xff0c;Python 3 源码文件以 UTF-8 编码&am…...

JAMstack架构:快速构建安全、高性能的现代应用

随着Web应用的快速发展&#xff0c;开发者们在寻找更加高效、安全和可维护的应用架构。JAMstack架构应运而生&#xff0c;它通过将前端、后端和部署过程分离&#xff0c;提供了一种现代化的方式来构建Web应用。在本文中&#xff0c;我们将深入探讨JAMstack架构的特点、优势以及…...

Web会话技术

会话:用户打开浏览器&#xff0c;访问web服务器的资源&#xff0c;会话建立&#xff0c;直到有一方断开连接&#xff0c;会话结束。在一次会话中可以包含多次请求和响应 会话跟踪:一种维护浏览器状态的方法&#xff0c;服务器需要识别多次请求是否来自于同一浏览器&#xff0c;…...

hbuilderx打包苹果证书获取步骤

简介&#xff1a; 目前app开发&#xff0c;很多企业都用H5框架来开发&#xff0c;而uniapp又是这些h5框架里面最成熟的&#xff0c;因此hbuilderx就成为了开发者的首选。然而,打包APP是需要证书的&#xff0c;那么这个证书又是如何获得呢&#xff1f; 生成苹果证书相对复杂一些…...

JAVA下载Excel文件之后无法打开,提示损坏

resources 目录下放模板 excel 文件&#xff0c;通过接口下载后&#xff0c;可以正常下载&#xff0c;但打不开。 问题&#xff1a; springboot 项目简单的下载excel 模板功能&#xff0c;模板放在resources/template/目录中 public void downloadItemBatch(HttpServletRespo…...

复合 类型

字符串和切片 切片 切片的作用是允许你引用集合中部分连续的元素序列&#xff0c;而不是引用整个集合。 例如&#xff1a; let s String::from("hello world");let hello &s[0..5]; // 切片 [0,5) 等效于&s[..5] let world &s[6..11]; // 切片…...

Practices11|41. 缺失的第一个正数(数组)、73. 矩阵置零(矩阵)

41. 缺失的第一个正数(数组) 1.题目&#xff1a; 给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,0] 输出&#xf…...

深入完整的带你了解java对象的比较

目录 元素的比较 1.基本类型的比较 2.对象比较的问题 1.运行结果 2.疑问 3.原因 对象的比较 1.覆写基类的equals 2.基于Comparble接口类的比较 3.基于比较器比较 4.三种方式对比 元素的比较 1.基本类型的比较 在Java 中&#xff0c;基本类型的对象可以直接比较大…...