Pytorch总结十六之优化算法:图像增广训练模型、微调(迁移学习)实现热狗识别

本文主要是介绍Pytorch总结十六之优化算法:图像增广训练模型、微调(迁移学习)实现热狗识别,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Pytorch总结十六之优化算法:图像增广训练模型、微调(迁移学习)实现热狗识别

1.图像增广

在(深度卷积神经⽹络)⾥我们提到过,⼤规模数据集是成功应⽤深度神经⽹络的前提。图像增⼴(image augmentation)技术通过对训练图像做⼀系列随机改变,来产⽣相似但⼜不同的训练样本,从⽽扩⼤训练数据集的规模。图像增⼴的另⼀种解释是,随机改变训练样本可以降低模型对某些属性的依赖,从⽽提⾼模型的泛化能⼒。例如,我们可以对图像进⾏不同⽅式的裁剪,使感兴趣的物体出现在不同位置,从⽽减轻模型对物体出现位置的依赖性。我们也可以调整亮度、⾊彩等因素来降低模型
对⾊彩的敏感度。可以说,在当年AlexNet的成功中,图像增⼴技术功不可没。本节我们将讨论这个在计算机视觉⾥被⼴泛使⽤的技术。
首先,导入实验所需的包和模块:

#图像增广
import time
import torch
from torch import nn, optim
from torch.utils.data import Dataset, DataLoader
import torchvision
from PIL import Image
import sys
sys.path.append("..")
import d2lzh_pytorch as d2l
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

1.1 常用的图像增广方法

我们来读取⼀张形状为 (⾼和宽分别为400像素和500像素)的图像作为实验的样例。

d2l.set_figsize()
img = Image.open('data/cat.jpg')
d2l.plt.imshow(img)

在这里插入图片描述
下⾯定义绘图函数 show_images

#定义绘图函数
def show_images(imgs, num_rows, num_cols, scale=2):figsize = (num_cols * scale, num_rows * scale)_, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)for i in range(num_rows):for j in range(num_cols):axes[i][j].imshow(imgs[i * num_cols + j])axes[i][j].axes.get_xaxis().set_visible(False)axes[i][j].axes.get_yaxis().set_visible(False)plt.show()return axes

⼤部分图像增⼴⽅法都有⼀定的随机性。为了⽅便观察图像增⼴的效果,接下来我们定义⼀个辅助函数 apply 。这个函数对输⼊图像 img 多次运⾏图像增⼴⽅法 aug 并展示所有的结果。

def apply(img, aug, num_rows=2, num_cols=4, scale=1.5):Y = [aug(img) for _ in range(num_rows * num_cols)]show_images(Y, num_rows, num_cols, scale)

具体应用如下:

1.2 翻转和裁剪

左右翻转图像通常不改变物体的类别。它是最早也是最⼴泛使⽤的⼀种图像增⼴⽅法。下⾯我们通过torchvision.transforms 模块创建 RandomHorizontalFlip 实例来实现⼀半概率的图像⽔平(左右)翻转。

apply(img, torchvision.transforms.RandomHorizontalFlip())

在这里插入图片描述
上下翻转不如左右翻转通⽤。但是⾄少对于样例图像,上下翻转不会造成识别障碍。下⾯我们创建RandomVerticalFlip 实例来实现⼀半概率的图像垂直(上下)翻转。

apply(img, torchvision.transforms.RandomVerticalFlip())

在这里插入图片描述

  • 在我们使⽤的样例图像⾥,猫在图像正中间,但⼀般情况下可能不是这样。在池化层⾥我们解释了池化层能降低卷积层对⽬标位置的敏感度。除此之外,我们还可以通过对图像随机裁剪来让物体以不同的⽐例出现在图像的不同位置,这同样能够降低模型对⽬标位置的敏感性。
  • 在下⾯的代码⾥,我们每次随机裁剪出⼀块⾯积为原⾯积10%~100% 的区域,且该区域的宽和⾼之⽐随机取⾃0.5~2 ,然后再将该区域的宽和⾼分别缩放到200像素。若⽆特殊说明,本节中 ab 之间的随机数指的是从区间 [a,b] 中随机均匀采样所得到的连续值。
shape_aug = torchvision.transforms.RandomResizedCrop(200, scale=(0.1, 1), ratio=(0.5, 2))
apply(img, shape_aug)

在这里插入图片描述

1.3 变化颜色

另⼀类增⼴⽅法是变化颜⾊。我们可以从4个⽅⾯改变图像的颜⾊:亮度( brightness )、对⽐度( contrast )、饱和度( saturation )和⾊调( hue )。在下⾯的例⼦⾥,我们将图像的亮度随机变化为原图亮度的 50% * (1-0.5)~150% * (1+0.5)

apply(img, torchvision.transforms.ColorJitter(brightness=0.5))

在这里插入图片描述
我们也可以随机变化图像的⾊调

apply(img, torchvision.transforms.ColorJitter(hue=0.5))

在这里插入图片描述
类似地,我们也可以随机变化图像的对⽐度。

apply(img, torchvision.transforms.ColorJitter(contrast=0.5)) 1

在这里插入图片描述
我们也可以同时设置如何随机变化图像的亮度( brightness )、对⽐度( contrast )、饱和度( saturation )和⾊调( hue )。

color_aug = torchvision.transforms.ColorJitter(brightness=0.5, contrast=0.5, saturation=0.5, hue=0.5)
apply(img, color_aug)

在这里插入图片描述

1.4 叠加多个图像增广方法

实际应⽤中我们会将多个图像增⼴⽅法叠加使⽤。我们可以通过 Compose 实例将上⾯定义的多个图像增⼴⽅法叠加起来,再应⽤到每张图像之上。

color_aug = torchvision.transforms.ColorJitter(brightness=0.5, contrast=0.5, saturation=0.5, hue=0.5)
augs = torchvision.transforms.Compose([torchvision.transforms.RandomHorizontalFlip(), color_aug,shape_aug])
apply(img, augs)

在这里插入图片描述

1.5 使用图像增广训练模型

下⾯我们来看⼀个将图像增⼴应⽤在实际训练中的例⼦。这⾥我们使⽤CIFAR-10数据集,⽽不是之前我们⼀直使⽤的Fashion-MNIST数据集。这是因为Fashion-MNIST数据集中物体的位置和尺⼨都已经经过归⼀化处理,⽽CIFAR-10数据集中物体的颜⾊和⼤⼩区别更加显著。下⾯展示了CIFAR-10数据集中前32张训练图像。
手动下载链接:http://www.cs.toronto.edu/~kriz/cifar.html
在这里插入图片描述

all_imges = torchvision.datasets.CIFAR10(train=True,
root="~/Datasets/CIFAR", download=True)  #download=True 时,自动下载数据集
# all_imges的每⼀个元素都是(image, label)
show_images([all_imges[i][0] for i in range(32)], 4, 8, scale=0.8);

在这里插入图片描述
为了在预测时得到确定的结果,我们通常只将图像增⼴应⽤在训练样本上,⽽不在预测时使⽤含随机操作的图像增⼴。在这⾥我们只使⽤最简单的随机左右翻转。此外,我们使⽤ ToTensor 将⼩批量图像转成PyTorch需要的格式,即形状为(批量⼤⼩, 通道数, ⾼, 宽)、值域在0到1之间且类型为32位浮点数。

flip_aug = torchvision.transforms.Compose([  #组合变换torchvision.transforms.RandomHorizontalFlip(),torchvision.transforms.ToTensor()])
no_aug = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])

接下来我们定义⼀个辅助函数来⽅便读取图像并应⽤图像增⼴。有关 DataLoader 的详细介绍,可参考更早的图像分类数据集(Fashion-MNIST)。

num_workers = 0 if sys.platform.startswith('win32') else 4
def load_cifar10(is_train, augs, batch_size,root="~/Datasets/CIFAR"):dataset = torchvision.datasets.CIFAR10(root=root,train=is_train, transform=augs, download=True)return DataLoader(dataset, batch_size=batch_size,
shuffle=is_train, num_workers=num_workers)

ouput:

Files already downloaded and verified
  • 我们在CIFAR-10数据集上训练残差⽹络中介绍的ResNet-18模型。
  • 我们先定义 train 函数使⽤GPU训练并评价模型。

#定义train使用GPU训练
def train(train_iter, test_iter, net, loss, optimizer, device,num_epochs):net = net.to(device)print("training on ", device)batch_count = 0for epoch in range(num_epochs):train_l_sum, train_acc_sum, n, start = 0.0, 0.0, 0, time.time()for X, y in train_iter:X = X.to(device)y = y.to(device)y_hat = net(X)l = loss(y_hat, y)optimizer.zero_grad()l.backward()optimizer.step()train_l_sum += l.cpu().item()train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()n += y.shape[0]batch_count += 1test_acc = d2l.evaluate_accuracy(test_iter, net)print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f,time % .1fsec'% (epoch + 1, train_l_sum / batch_count,
train_acc_sum / n, test_acc, time.time() - start))

然后就可以定义 train_with_data_aug 函数使⽤图像增⼴来训练模型了。该函数使⽤Adam算法作为训练使⽤的优化算法,然后将图像增⼴应⽤于训练数据集之上,最后调⽤刚才定义的 train 函数训练并评价模型。

def train_with_data_aug(train_augs, test_augs, lr=0.001):batch_size, net = 256, d2l.resnet18(10)optimizer = torch.optim.Adam(net.parameters(), lr=lr)loss = torch.nn.CrossEntropyLoss()train_iter = load_cifar10(True, train_augs, batch_size)test_iter = load_cifar10(False, test_augs, batch_size)train(train_iter, test_iter, net, loss, optimizer, device,num_epochs=10)

下⾯使⽤随机左右翻转的图像增⼴来训练模型。

train_with_data_aug(flip_aug, no_aug)

ouput:
在这里插入图片描述
我的torch是cpu版本,对应的gpu输出如下:
在这里插入图片描述

2.微调

  • 我们介绍了如何在只有6万张图像的Fashion-MNIST训练数据集上训练模型。我们还描述了学术界当下使⽤最⼴泛的⼤规模图像数据集ImageNet,它有超过1,000万的图像和1,000类的物体。然⽽,我们平常接触到数据集的规模通常在这两者之间。

  • 假设我们想从图像中识别出不同种类的椅⼦,然后将购买链接推荐给⽤户。⼀种可能的⽅法是先找出100种常⻅的椅⼦,为每种椅⼦拍摄1,000张不同⻆度的图像,然后在收集到的图像数据集上训练⼀个分类模型。这个椅⼦数据集虽然可能⽐Fashion-MNIST数据集要庞⼤,但样本数仍然不及ImageNet数据集中样本数的⼗分之⼀。这可能会导致适⽤于ImageNet数据集的复杂模型在这个椅⼦数据集上过拟合。同时,因为数据量有限,最终训练得到的模型的精度也可能达不到实⽤的要求。

  • 为了应对上述问题,⼀个显⽽易⻅的解决办法是收集更多的数据。然⽽,收集和标注数据会花费⼤量的时间和资⾦。例如,为了收集ImageNet数据集,研究⼈员花费了数百万美元的研究经费。虽然⽬前的数据采集成本已降低了不少,但其成本仍然不可忽略。

  • 另外⼀种解决办法是应⽤迁移学习(transfer learning),将从源数据集学到的知识迁移到⽬标数据集上。例如,虽然ImageNet数据集的图像⼤多跟椅⼦⽆关,但在该数据集上训练的模型可以抽取较通⽤的图像特征,从⽽能够帮助识别边缘、纹理、形状和物体组成等。这些类似的特征对于识别椅⼦也可能同样有效。

  • 本节我们介绍迁移学习中的⼀种常⽤技术:微调(fine tuning)。如下图所示,微调由以下4步构成。

      1. 在源数据集(如ImageNet数据集)上预训练⼀个神经⽹络模型,即源模型。
      1. 创建⼀个新的神经⽹络模型,即⽬标模型。它复制了源模型上除了输出层外的所有模型设计及其参数。我们假设这些模型参数包含了源数据集上学习到的知识,且这些知识同样适⽤于⽬标数据集。我们还假设源模型的输出层跟源数据集的标签紧密相关,因此在⽬标模型中不予采⽤。
      1. 为⽬标模型添加⼀个输出⼤⼩为⽬标数据集类别个数的输出层,并随机初始化该层的模型参数。
      1. 在⽬标数据集(如椅⼦数据集)上训练⽬标模型。我们将从头训练输出层,⽽其余层的参数都是基于源模型的参数微调得到的。
        在这里插入图片描述
        当⽬标数据集远⼩于源数据集时,微调有助于提升模型的泛化能⼒。

2.1 热狗识别

  • 接下来我们来实践⼀个具体的例⼦:热狗识别。我们将基于⼀个⼩数据集对在ImageNet数据集上训练好的ResNet模型进⾏微调。该⼩数据集含有数千张包含热狗和不包含热狗的图像。我们将使⽤微调得到的模型来识别⼀张图像中是否包含热狗。
  • ⾸先,导⼊实验所需的包或模块。torchvisionmodels 包提供了常⽤的预训练模型。如果希望获取更多的预训练模型,可以使⽤ pretrained-models.pytorch 仓库。
import torch
from torch import nn, optim
from torch.utils.data import Dataset, DataLoader
import torchvision
from torchvision.datasets import ImageFolder
from torchvision import transforms
from torchvision import models
import os
import sys
sys.path.append("..")
import d2lzh_pytorch as d2l
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

2.2 获取数据集

  • 我们使⽤的热狗数据集(点击下载)是从⽹上抓取的,它含有1400张包含热狗的正类图像,和同样多包含其他⻝品的负类图像。各类的1000张图像被⽤于训练,其余则⽤于测试。
  • 我们⾸先将压缩后的数据集下载到路径 data_dir 之下,然后在该路径将下载好的数据集解压,得到两个⽂件夹 hotdog/trainhotdog/test 。这两个⽂件夹下⾯均有 hotdognot-hotdog 两个类别⽂件夹,每个类别⽂件夹⾥⾯是图像⽂件。
data_dir='data'
_imgdata = os.listdir(os.path.join(data_dir,'hotdog'))
print(_imgdata) #['test', 'train']#创建两个 ImageFolder 实例来分别读取训练数据集和测试数据集中的所有图像⽂件
train_imgs = ImageFolder(os.path.join(data_dir, 'hotdog/train'))
test_imgs = ImageFolder(os.path.join(data_dir, 'hotdog/test'))#画出前8张正类图像和最后8张负类图像。可以看到,它们的⼤⼩和⾼宽⽐各不相同。
hotdogs = [train_imgs[i][0] for i in range(8)]
not_hotdogs = [train_imgs[-i - 1][0] for i in range(8)]
d2l.show_images(hotdogs + not_hotdogs, 2, 8, scale=1.4)

在这里插入图片描述

  • 在训练时,我们先从图像中裁剪出随机⼤⼩和随机⾼宽⽐的⼀块随机区域,然后将该区域缩放为⾼和宽均为224像素的输⼊。测试时,我们将图像的⾼和宽均缩放为256像素,然后从中裁剪出⾼和宽均为224像素的中⼼区域作为输⼊。此外,我们对RGB(红、绿、蓝)三个颜⾊通道的数值做标准化:每个数值减去该通道所有数值的平均值,再除以该通道所有数值的标准差作为输出。
#指定RGB三个通道的均值和方差来将图像通道归一化
normalize=transforms.Normalize(mean=[0.485,0.456,0.406],std=[0.229,0.224,0.225])
train_augs=transforms.Compose([transforms.RandomResizedCrop(size=224),transforms.RandomHorizontalFlip(),transforms.ToTensor(),normalize])
test_augs=transforms.Compose([transforms.Resize(size=256),transforms.CenterCrop(size=224),transforms.ToTensor(),normalize])

2.3 定义和初始化模型

在此使用ImageNet数据集上预训练的ResNet-18作为源模型,这里指定pretrained=True 来自动下载并加载预训练的模型参数。在第一次使用时需要联网下载模型参数。

  • 下⾯打印源模型的成员变量 fc 。作为⼀个全连接层,它将ResNet最终的全局平均池化层输出变换成ImageNet数据集上1000类的输出。
#定义与初始化模型
pretrained_net=models.resnet18(pretrained=True)
print(pretrained_net.fc)

在这里插入图片描述

  • 可⻅此时 pretrained_net 最后的输出个数等于⽬标数据集的类别数1000。所以我们应该将最后的
    fc 成修改我们需要的输出类别数:
#将fc修改成我们需要的类别数
pretrained_net.fc=nn.Linear(512,2)
print(pretrained_net.fc)

output:

Linear(in_features=512, out_features=2, bias=True)
  • 此时, pretrained_netfc 层就被随机初始化了,但是其他层依然保存着预训练得到的参数。由于是在很⼤的ImageNet数据集上预训练的,所以参数已经⾜够好,因此⼀般只需使⽤较⼩的学习率来微调这些参数,⽽ fc 中的随机初始化参数⼀般需要更⼤的学习率从头训练。PyTorch可以⽅便的对模型的不同部分设置不同的学习参数,我们在下⾯代码中将 fc 的学习率设为已经预训练过的部分的10倍。
#调大fc中的学习率
output_params=list(map(id,pretrained_net.fc.parameters()))
feature_params=filter(lambda p:id(p) not in output_params,pretrained_net.parameters())
lr=0.01
optimizer=optim.SGD([{'params':feature_params},{'params':pretrained_net.fc.parameters()},{'lr':lr*10}],lr=lr,weight_decay=0.001)

2.4 微调模型

我们先定义⼀个使⽤微调的训练函数 train_fine_tuning 以便多次调⽤。

#调大fc中的学习率
output_params = list(map(id, pretrained_net.fc.parameters()))
feature_params = filter(lambda p: id(p) not in output_params,pretrained_net.parameters())
lr = 0.01
optimizer = optim.SGD([{'params': feature_params}, {'params': pretrained_net.fc.parameters(),'lr': lr * 10}],lr=lr, weight_decay=0.001)def train_fine_tuning(net, optimizer, batch_size=128, num_epochs=5):train_iter = DataLoader(ImageFolder(os.path.join(data_dir,'hotdog/train'), transform=train_augs),batch_size, shuffle=True)test_iter = DataLoader(ImageFolder(os.path.join(data_dir,'hotdog/test'), transform=test_augs),batch_size)loss = torch.nn.CrossEntropyLoss()d2l.train(train_iter, test_iter, net, loss, optimizer, device,num_epochs)#将10倍的学习率从头训练目标模型的输出层参数
train_fine_tuning(pretrained_net,optimizer)

output:

training on cuda
epoch 1, loss 3.1183, train acc 0.731, test acc 0.932, time 41.4 sec
epoch 2, loss 0.6471, train acc 0.829, test acc 0.869, time 25.6 sec
epoch 3, loss 0.0964, train acc 0.920, test acc 0.910, time 24.9 sec
epoch 4, loss 0.0659, train acc 0.922, test acc 0.936, time 25.2 sec
epoch 5, loss 0.0668, train acc 0.913, test acc 0.929, time 25.0 sec

在这里插入图片描述

作为对⽐,我们定义⼀个相同的模型,但将它的所有模型参数都初始化为随机值。由于整个模型都需要从头训练,我们可以使⽤较⼤的学习率。

scratch_net = models.resnet18(pretrained=False, num_classes=2)
lr = 0.1
optimizer = optim.SGD(scratch_net.parameters(), lr=lr,weight_decay=0.001)
train_fine_tuning(scratch_net, optimizer)

output:

training on cuda
epoch 1, loss 2.6686, train acc 0.582, test acc 0.556, time 25.3 sec
epoch 2, loss 0.2434, train acc 0.797, test acc 0.776, time 25.3 sec
epoch 3, loss 0.1251, train acc 0.845, test acc 0.802, time 24.9 sec
epoch 4, loss 0.0958, train acc 0.833, test acc 0.810, time 25.0 sec
epoch 5, loss 0.0757, train acc 0.836, test acc 0.780, time 24.9 sec

在这里插入图片描述

可以看到,微调的模型因为参数初始值更好,往往在相同迭代周期下取得更⾼的精度。

2.5 小结

  • 迁移学习将从源数据集学到的知识迁移到⽬标数据集上。微调是迁移学习的⼀种常⽤技术。
  • ⽬标模型复制了源模型上除了输出层外的所有模型设计及其参数,并基于⽬标数据集微调这些参
    数。⽽⽬标模型的输出层需要从头训练。
  • ⼀般来说,微调参数会使⽤较⼩的学习率,⽽从头训练输出层可以使⽤较⼤的学习率。

这篇关于Pytorch总结十六之优化算法:图像增广训练模型、微调(迁移学习)实现热狗识别的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/603531

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu