-
目标检测中,点特征使用方便,但可能缺乏精确定位的明确边界信息。 论文地址:https://arxiv.org/pdf/2007.11056.pdf源代码地址:https://github.com/Megvii-BaseDetection/BorderDet1简述密集物体检测器依赖于滑动窗口范式,可以在规则的图像网格上预测物体。同时,采用网格点上的特征图来生成边界框预测。点特征使用方便,但可能缺乏精确定位的明确边界信息。 在今天分享中,有研究者提出了一种简单高效的算子,称为 Border-Align,从边界的极值点提取“边界特征”以增强点特征。基于BorderAlign,研究者设计了一种称为BorderDet的新型检测架构,它明确利用边界信息进行更强的分类和更准确的定位。2背景Sliding Window滑窗法作为一种经典的物体检测方法,个人认为不同大小的窗口在图像上进行滑动时候,进行卷积运算后的结果与已经训练好的分类器判别存在物体的概率。选择性搜索(Selective Search)是主要运用图像分割技术来进行物体检测。通过滑窗法流程图可以很清晰理解其主要思路:首先对输入图像进行不同窗口大小的滑窗进行从左往右、从上到下的滑动。每次滑动时候对当前窗口执行分类器(分类器是事先训练好的)。如果当前窗口得到较高的分类概率,则认为检测到了物体。对每个不同窗口大小的滑窗都进行检测后,会得到不同窗口检测到的物体标记,这些窗口大小会存在重复较高的部分,最后采用非极大值抑制(Non-Maximum Suppression, NMS)的方法进行筛选。最终,经过NMS筛选后获得检测到的物体。3新框架分析滑动窗口目标检测器通常在密集的、规则的特征图网格上生成边界框预测。如上图所示,网格每个点上的特征一般用于预测目标的类别和位置。这种基于点的特征表示很难包含有效的边界特征,并且可能会限制目标检测器的定位能力。对于两阶段目标检测器,目标由从整个边界框中提取的区域特征来描述,如上图(b)所示。这种基于区域的特征表示能够为目标分类和定位提供比基于点的特征表示更丰富的特征。在上表中,研究者对边界框的特征表示进行了更深入的分析。首先,采用一个简单的密集目标检测器(FCOS)作为新框架的基线来生成粗边界框预测。Border Align受R-FCN的启发,新框架的BorderAlign以具有(4 + 1)C通道的边界敏感特征图I作为输入。特征图的4C通道对应四个边界(左、上、右、下)。border-sensitive特征图可视化边界上的橙色圆圈表示极值点。'Single Point', 'Left Border', 'Top Border', 'Right Border' 和 'Bottom Border'的特征图是边界敏感特征图的每个 C 通道的最大特征值。新方法也可以作为典型的两级检测器的更好的候选生成器。 研究者将边界对齐模块添加到RPN并将新结构表示为BorderRPN。BorderRPN的架构如上图所示。保留RPN中的回归分支来预测粗边界框位置。RPN中的第一个3 × 3卷积被替换为3 × 3空洞卷积以增加有效感受野。4实验在NVIDIA 2080Ti GPU上的测试结果在某种程度上,可证明BorderAlign确实在提取边界极限的特征,且边界极限点的特征对物体的精准定位确实有一些帮助。转载自:AI算法与图像处理公众号
-
本文分享自华为云社区《[全卷积网络(FCN)实战:使用FCN实现语义分割](https://bbs.huaweicloud.com/blogs/337081?utm_source=csdn&utm_medium=bbs-ex&utm_campaign=other&utm_content=content)》,作者: AI浩。 FCN对图像进行像素级的分类,从而解决了语义级别的图像分割(semantic segmentation)问题。与经典的CNN在卷积层之后使用全连接层得到固定长度的特征向量进行分类(全联接层+softmax输出)不同,FCN可以接受任意尺寸的输入图像,采用反卷积层对最后一个卷积层的feature map进行上采样, 使它恢复到输入图像相同的尺寸,从而可以对每个像素都产生了一个预测, 同时保留了原始输入图像中的空间信息, 最后在上采样的特征图上进行逐像素分类。 下图是语义分割所采用的全卷积网络(FCN)的结构示意图:  # 传统的基于CNN的分割方法缺点? 传统的基于CNN的分割方法:为了对一个像素分类,使用该像素周围的一个图像块作为CNN的输入,用于训练与预测,这种方法主要有几个缺点: 1)存储开销大,例如,对每个像素使用15 * 15的图像块,然后不断滑动窗口,将图像块输入到CNN中进行类别判断,因此,需要的存储空间随滑动窗口的次数和大小急剧上升; 2)效率低下,相邻像素块基本上是重复的,针对每个像素块逐个计算卷积,这种计算有很大程度上的重复; 3)像素块的大小限制了感受区域的大小,通常像素块的大小比整幅图像的大小小很多,只能提取一些局部特征,从而导致分类性能受到限制。 而全卷积网络(FCN)则是从抽象的特征中恢复出每个像素所属的类别。即从图像级别的分类进一步延伸到像素级别的分类。 # FCN改变了什么? 对于一般的分类CNN网络,如VGG和Resnet,都会在网络的最后加入一些全连接层,经过softmax后就可以获得类别概率信息。但是这个概率信息是1维的,即只能标识整个图片的类别,不能标识每个像素点的类别,所以这种全连接方法不适用于图像分割。 而FCN提出可以把后面几个全连接都换成卷积,这样就可以获得一张2维的feature map,后接softmax层获得每个像素点的分类信息,从而解决了分割问题,如图4。  # FCN缺点 (1)得到的结果还是不够精细。进行8倍上采样虽然比32倍的效果好了很多,但是上采样的结果还是比较模糊和平滑,对图像中的细节不敏感。 (2)对各个像素进行分类,没有充分考虑像素与像素之间的关系。忽略了在通常的基于像素分类的分割方法中使用的空间规整(spatial regularization)步骤,缺乏空间一致性。 # 数据集 本例的数据集采用PASCAL VOC 2012 数据集,它有二十个类别: **Person:**person Animal: bird, cat, cow, dog, horse, sheep **Vehicle:**aeroplane, bicycle, boat, bus, car, motorbike, train Indoor: bottle, chair, dining table, potted plant, sofa, tv/monitor  下载地址:The PASCAL Visual Object Classes Challenge 2012 (VOC2012) (ox.ac.uk)。 数据集的结构: VOCdevkit └── VOC2012 ├── Annotations 所有的图像标注信息(XML文件) ├── ImageSets │ ├── Action 人的行为动作图像信息 │ ├── Layout 人的各个部位图像信息 │ │ │ ├── Main 目标检测分类图像信息 │ │ ├── train.txt 训练集(5717) │ │ ├── val.txt 验证集(5823) │ │ └── trainval.txt 训练集+验证集(11540) │ │ │ └── Segmentation 目标分割图像信息 │ ├── train.txt 训练集(1464) │ ├── val.txt 验证集(1449) │ └── trainval.txt 训练集+验证集(2913) │ ├── JPEGImages 所有图像文件 ├── SegmentationClass 语义分割png图(基于类别) └── SegmentationObject 实例分割png图(基于目标) 数据集包含物体检测和语义分割,我们只需要语义分割的数据集,所以可以考虑把多余的图片删除,删除的思路: 1、获取所有图片的name。 2、获取所有语义分割mask的name。 3、求二者的差集,然后将差集的name删除。 代码如下: import glob import os image_all = glob.glob('data/VOCdevkit/VOC2012/JPEGImages/*.jpg') image_all_name = [image_file.replace('\\', '/').split('/')[-1].split('.')[0] for image_file in image_all] image_SegmentationClass = glob.glob('data/VOCdevkit/VOC2012/SegmentationClass/*.png') image_se_name= [image_file.replace('\\', '/').split('/')[-1].split('.')[0] for image_file in image_SegmentationClass] image_other=list(set(image_all_name) - set(image_se_name)) print(image_other) for image_name in image_other: os.remove('data/VOCdevkit/VOC2012/JPEGImages/{}.jpg'.format(image_name)) # 代码链接 本例选用的代码来自deep-learning-for-image-processing/pytorch_segmentation/fcn at master · WZMIAOMIAO/deep-learning-for-image-processing (github.com) 其他的代码也有很多,这篇比较好理解! 其实还有个比较好的图像分割库:GitHub - qubvel/segmentation_models.pytorch: Segmentation models with pretrained backbones. PyTorch. 这个图像分割集合由俄罗斯的程序员小哥Pavel Yakubovskiy一手打造。在后面的文章,我也会使用这个库演示。 # 项目结构 1. ├── src: 模型的backbone以及FCN的搭建 2. ├── train_utils: 训练、验证以及多GPU训练相关模块 3. ├── my_dataset.py: 自定义dataset用于读取VOC数据集 4. ├── train.py: 以fcn_resnet50(这里使用了Dilated/Atrous Convolution)进行训练 5. ├── predict.py: 简易的预测脚本,使用训练好的权重进行预测测试 6. ├── validation.py: 利用训练好的权重验证/测试数据的mIoU等指标,并生成record_mAP.txt文件 7. └── pascal_voc_classes.json: pascal_voc标签文件 由于代码很多不能一一讲解,所以,接下来对重要的代码做剖析。 # 自定义数据集读取 my_dataset.py自定义数据读取的方法,代码如下: import os import torch.utils.data as data from PIL import Image class VOCSegmentation(data.Dataset): def __init__(self, voc_root, year="2012", transforms=None, txt_name: str = "train.txt"): super(VOCSegmentation, self).__init__() assert year in ["2007", "2012"], "year must be in ['2007', '2012']" root = os.path.join(voc_root, "VOCdevkit", f"VOC{year}") root=root.replace('\\','/') assert os.path.exists(root), "path '{}' does not exist.".format(root) image_dir = os.path.join(root, 'JPEGImages') mask_dir = os.path.join(root, 'SegmentationClass') txt_path = os.path.join(root, "ImageSets", "Segmentation", txt_name) txt_path=txt_path.replace('\\','/') assert os.path.exists(txt_path), "file '{}' does not exist.".format(txt_path) with open(os.path.join(txt_path), "r") as f: file_names = [x.strip() for x in f.readlines() if len(x.strip()) > 0] self.images = [os.path.join(image_dir, x + ".jpg") for x in file_names] self.masks = [os.path.join(mask_dir, x + ".png") for x in file_names] assert (len(self.images) == len(self.masks)) self.transforms = transforms 导入需要的包。 定义VOC数据集读取类VOCSegmentation。在init方法中,核心是读取image列表和mask列表。 def __getitem__(self, index): img = Image.open(self.images[index]).convert('RGB') target = Image.open(self.masks[index]) if self.transforms is not None: img, target = self.transforms(img, target) return img, target __getitem__方法是获取单张图片和图片对应的mask,然后对其做数据增强。 def collate_fn(batch): images, targets = list(zip(*batch)) batched_imgs = cat_list(images, fill_value=0) batched_targets = cat_list(targets, fill_value=255) return batched_imgs, batched_targets collate_fn方法是对一个batch中数据调用cat_list做数据对齐。 在train.py中torch.utils.data.DataLoader调用 train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, num_workers=num_workers, shuffle=True, pin_memory=True, collate_fn=train_dataset.collate_fn) val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=1, num_workers=num_workers, pin_memory=True, collate_fn=val_dataset.collate_fn) # 训练 # 重要参数 打开train.py,我们先认识一下重要的参数: def parse_args(): import argparse parser = argparse.ArgumentParser(description="pytorch fcn training") # 数据集的根目录(VOCdevkit)所在的文件夹 parser.add_argument("--data-path", default="data/", help="VOCdevkit root") parser.add_argument("--num-classes", default=20, type=int) parser.add_argument("--aux", default=True, type=bool, help="auxilier loss") parser.add_argument("--device", default="cuda", help="training device") parser.add_argument("-b", "--batch-size", default=32, type=int) parser.add_argument("--epochs", default=30, type=int, metavar="N", help="number of total epochs to train") parser.add_argument('--lr', default=0.0001, type=float, help='initial learning rate') parser.add_argument('--momentum', default=0.9, type=float, metavar='M', help='momentum') parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float, metavar='W', help='weight decay (default: 1e-4)', dest='weight_decay') parser.add_argument('--print-freq', default=10, type=int, help='print frequency') parser.add_argument('--resume', default='', help='resume from checkpoint') parser.add_argument('--start-epoch', default=0, type=int, metavar='N', help='start epoch') # 是否使用混合精度训练 parser.add_argument("--amp", default=False, type=bool, help="Use torch.cuda.amp for mixed precision training") args = parser.parse_args() return args data-path:定义数据集的根目录(VOCdevkit)所在的文件夹 num-classes:检测目标类别数(不包含背景)。 aux:是否使用aux_classifier。 device:使用cpu还是gpu训练,默认是cuda。 batch-size:BatchSize设置。 epochs:epoch的个数。 lr:学习率。 resume:继续训练时候,选择用的模型。 start-epoch:起始的epoch,针对再次训练时,可以不需要从0开始。 amp:是否使用torch的自动混合精度训练。 # 数据增强 增强调用transforms.py中的方法。 训练集的增强如下: class SegmentationPresetTrain: def __init__(self, base_size, crop_size, hflip_prob=0.5, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)): # 随机Resize的最小尺寸 min_size = int(0.5 * base_size) # 随机Resize的最大尺寸 max_size = int(2.0 * base_size) # 随机Resize增强。 trans = [T.RandomResize(min_size, max_size)] if hflip_prob > 0: #随机水平翻转 trans.append(T.RandomHorizontalFlip(hflip_prob)) trans.extend([ #随机裁剪 T.RandomCrop(crop_size), T.ToTensor(), T.Normalize(mean=mean, std=std), ]) self.transforms = T.Compose(trans) def __call__(self, img, target): return self.transforms(img, target) 训练集增强,包括随机Resize、随机水平翻转、随即裁剪。 验证集增强: class SegmentationPresetEval: def __init__(self, base_size, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)): self.transforms = T.Compose([ T.RandomResize(base_size, base_size), T.ToTensor(), T.Normalize(mean=mean, std=std), ]) def __call__(self, img, target): return self.transforms(img, target) 验证集的增强比较简单,只有随机Resize。 # Main方法 对Main方法,我做了一些修改,修改的代码如下: #定义模型,并加载预训练 model = fcn_resnet50(pretrained=True) # 默认classes是21,如果不是21,则要修改类别。 if num_classes != 21: model.classifier[4] = torch.nn.Conv2d(512, num_classes, kernel_size=(1, 1), stride=(1, 1)) model.aux_classifier[4] = torch.nn.Conv2d(256, num_classes, kernel_size=(1, 1), stride=(1, 1)) print(model) model.to(device) # 如果有多张显卡,则使用多张显卡 if torch.cuda.device_count() > 1: print("Let's use", torch.cuda.device_count(), "GPUs!") model = torch.nn.DataParallel(model) 模型,我改为pytorch官方的模型了,如果能使用官方的模型尽量使用官方的模型。 默认类别是21,如果不是21,则要修改类别。 检测系统中是否有多张卡,如果有多张卡则使用多张卡不能浪费资源。 如果不想使用所有的卡,而是指定其中的几张卡,可以使用: `os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'` 也可以在DataParallel方法中设定: `model = torch.nn.DataParallel(model,device_ids=[0,1])` 如果使用了多显卡,再使用模型的参数就需要改为model.module.xxx,例如: params = [p for p in model.module.aux_classifier.parameters() if p.requires_grad] params_to_optimize.append({"params": params, "lr": args.lr * 10}) 上面的都完成了就可以开始训练了,如下图:  # 测试 在开始测试之前,我们还要获取到调色板,新建脚本get_palette.py,代码如下: import json import numpy as np from PIL import Image # 读取mask标签 target = Image.open("./2007_001288.png") # 获取调色板 palette = target.getpalette() palette = np.reshape(palette, (-1, 3)).tolist() print(palette) # 转换成字典子形式 pd = dict((i, color) for i, color in enumerate(palette)) json_str = json.dumps(pd) with open("palette.json", "w") as f: f.write(json_str) 选取一张mask,然后使用getpalette方法获取,然后将其转为字典的格式保存。 接下来,开始预测部分,新建predict.py,插入以下代码: import os import time import json import torch from torchvision import transforms import numpy as np from PIL import Image from torchvision.models.segmentation import fcn_resnet50 导入程序需要的包文件,然在mian方法中: def main(): aux = False # inference time not need aux_classifier classes = 20 weights_path = "./save_weights/model_5.pth" img_path = "./2007_000123.jpg" palette_path = "./palette.json" assert os.path.exists(weights_path), f"weights {weights_path} not found." assert os.path.exists(img_path), f"image {img_path} not found." assert os.path.exists(palette_path), f"palette {palette_path} not found." with open(palette_path, "rb") as f: pallette_dict = json.load(f) pallette = [] for v in pallette_dict.values(): pallette += v - 定义是否需要aux_classifier,预测不需要aux_classifier,所以设置为False。 - 设置类别为20,不包括背景。 - 定义权重的路径。 - 定义调色板的路径。 - 读去调色板。 接下来,是加载模型,单显卡训练出来的模型和多显卡训练出来的模型加载有区别,我们先看单显卡训练出来的模型如何加载。 model = fcn_resnet50(num_classes=classes+1) print(model) # 单显卡训练出来的模型,加载 # delete weights about aux_classifier weights_dict = torch.load(weights_path, map_location='cpu')['model'] for k in list(weights_dict.keys()): if "aux_classifier" in k: del weights_dict[k] # load weights model.load_state_dict(weights_dict) model.to(device) 定义模型fcn_resnet50,num_classes设置为类别+1(背景) 加载训练好的模型,并将aux_classifier删除。 然后加载权重。 再看多显卡的模型如何加载 # create model model = fcn_resnet50(num_classes=classes+1) model = torch.nn.DataParallel(model) # delete weights about aux_classifier weights_dict = torch.load(weights_path, map_location='cpu')['model'] print(weights_dict) for k in list(weights_dict.keys()): if "aux_classifier" in k: del weights_dict[k] # load weights model.load_state_dict(weights_dict) model=model.module model.to(device) 定义模型fcn_resnet50,num_classes设置为类别+1(背景),将模型放入DataParallel类中。 加载训练好的模型,并将aux_classifier删除。 加载权重。 执行torch.nn.DataParallel(model)时,model被放在了model.module,所以model.module才真正需要的模型。所以我们在这里将model.module赋值给model。 接下来是图像数据的处理 # load image original_img = Image.open(img_path) # from pil image to tensor and normalize data_transform = transforms.Compose([transforms.Resize(520), transforms.ToTensor(), transforms.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))]) img = data_transform(original_img) # expand batch dimension img = torch.unsqueeze(img, dim=0) 加载图像。 对图像做Resize、标准化、归一化处理。 使用torch.unsqueeze增加一个维度。 完成图像的处理后,就可以开始预测了。 model.eval() # 进入验证模式 with torch.no_grad(): # init model img_height, img_width = img.shape[-2:] init_img = torch.zeros((1, 3, img_height, img_width), device=device) model(init_img) t_start = time_synchronized() output = model(img.to(device)) t_end = time_synchronized() print("inference+NMS time: {}".format(t_end - t_start)) prediction = output['out'].argmax(1).squeeze(0) prediction = prediction.to("cpu").numpy().astype(np.uint8) np.set_printoptions(threshold=sys.maxsize) print(prediction.shape) mask = Image.fromarray(prediction) mask.putpalette(pallette) mask.save("test_result.png") 将预测后的结果保存到test_result.png中。查看运行结果: 原图:  结果:  打印出来的数据:  类别列表: { "aeroplane": 1, "bicycle": 2, "bird": 3, "boat": 4, "bottle": 5, "bus": 6, "car": 7, "cat": 8, "chair": 9, "cow": 10, "diningtable": 11, "dog": 12, "horse": 13, "motorbike": 14, "person": 15, "pottedplant": 16, "sheep": 17, "sofa": 18, "train": 19, "tvmonitor": 20 } 从结果来看,已经预测出来图像上的类别是“train”。 # 总结 这篇文章的核心内容是讲解如何使用FCN实现图像的语义分割。 在文章的开始,我们讲了一些FCN的结构和优缺点。 然后,讲解了如何读取数据集。 接下来,告诉大家如何实现训练。 最后,是测试以及结果展示。 希望本文能给大家带来帮助。 完整代码: https://download.csdn.net/download/hhhhhhhhhhwwwwwwwwww/83778007
-
来自华为诺亚方舟实验室、北京大学、悉尼大学的研究者提出了一种受量子力学启发的视觉 MLP 新架构。近年来,计算机视觉领域的新型架构层出不穷,包括视觉 Transformer、MLP 等,它们在很多任务上都取得了超越 CNN 的性能,受到广泛关注。其中,视觉 MLP 具有极其简单的架构,它仅由多层感知器(MLP)堆叠而成。与 CNN 和 Transformer 相比,这些简洁的 MLP 架构引入了更少的归纳偏置,具有更强的泛化性能。 然而,现有视觉 MLP 架构的性能依然弱于 CNN 和 Transformer。来自华为诺亚方舟实验室、北京大学、悉尼大学的研究者提出了一种受量子力学启发的视觉 MLP 架构,在 ImageNet 分类、COCO 检测、ADE20K 分割等多个任务上取得了 SOTA 性能。 论文地址:https://arxiv.org/abs/2111.12294 PyTorch 代码:https://github.com/huawei-noah/CV-Backbones/tree/master/wavemlp_pytorch MindSpore 代码:https://gitee.com/mindspore/models/tree/master/research/cv/wave_mlp Wave-MLP 该研究受量子力学中波粒二象性的启发,将 MLP 中每个图像块 (Token) 表示成波函数的形式,从而提出了一个新型的视觉 MLP 架构——Wave-MLP,在性能上大幅超越了现有 MLP 架构以及 Transformer。量子力学是描述微观粒子运动规律的物理学分支,经典力学可被视为量子力学的特例。量子力学的一个基本属性是波粒二象性,即所有的个体(比如电子、光子、原子等)都可以同时使用粒子的术语和波的术语来描述。一个波通常包括幅值和相位两个属性,幅值表示一个波可能达到的最大强度,相位指示着当前处在一个周期的哪个位置。将一个经典意义上的粒子用波(比如,德布罗意波)的形式来表示,可以更完备地描述微观粒子的运动状态。那么,对于视觉 MLP 中的图像块,能不能也把它表示成波的形式呢?该研究用幅值表达每个 Token 所包含的实际信息,用相位来表示这个 Token 当前所处的状态。在**不同 Token 信息的时候,不同 Token 之间的相位差会调制它们之间的聚合过程(如图 3 示)。考虑到来自不同输入图像的 Token 包含不同的语义内容,该研究使用一个简单的全连接模块来动态估计每个 Token 的相位。对于同时带有幅度和相位信息的 Token,作者提出了一个相位感知 Token 混合模块(PATM,如下图 1 所示)来聚合它们的信息。交替堆叠 PATM 模块和 MLP 模块构成了整个 Wave-MLP 架构。相比现有的视觉 Transformer 和 MLP 架构,Wave-MLP 有着明显的性能优势(如下图 2 所示)。在 ImageNet,Wave-MLP-S 模型上以 4.5G FLOPs 实现了 82.6% 的 top-1 准确率,比相似计算代价的 Swin-T 高 1.3 个点。此外,Wave-MLP 也可以推广到目标检测和语义分割等下游任务,展现出强大的泛化性能。 用波表示 Token 在 Wave-MLP 中,Token 被表示为同时具有幅值和相位信息的波其中 i 是满足 i^2 = -1 的虚数单位,|·| 表示绝对值运算,⊙是逐元素乘法。幅值 |z_j| 是实值的特征,表示每个 Token 所包含的内容。θ_j 表示相位,即 Token 在一个波周期内的当前位置。 两个 Token 之间的相位差对它们的聚合过程有很大影响 (如下图 3 所示)。当两个 token 具有相同的相位时,它们会相互增强,得到幅值更大的波(图 3(b));当两个 token 相位相反时,他们合成的波将相互减弱。在其他情况下,它们之间的相互作用更加复杂,但仍取决于相位差(图 3(a))。经典方法中使用实值表示 token 的,这实际上是上式的一个特例。 图 3:两个具有不同相位的波的聚合过程。左侧表示两个波在复数域中的叠加,右侧表示它们在实轴上的投影随着相位的变化。虚线表示两个初始相位不同的波,实线是他们的叠加。 相位感知的 Token 聚合 公式(1)中包含幅值和相位两项,幅值 z_j 类似于实值特征,可以采用标准的 Channel-FC 生成: (5)在视觉 MLP 中,该研究构建了一个相位感知模块(PATM,图 1)来完成 Token 聚合的过程。交替堆叠 PATM 模块和 channel-mixing MLP 组建了整个 WaveMLP 架构。 实验结果 该研究在大规模的分类数据集 ImageNet, 目标检测数据集 COCO 和语义分割数据集 ADE20K 上都进行了大量实验。 ImageNet 上图像分类的结果如表 1,表 2 所示:相比于现有的 Vision MLP 架构和 Transformer 架构,WaveMLP 都取得了明显的性能优势。 在下游目标检测、语义分割等任务中,Wave-MLP 同样表现出更优的性能。
-
YOLOv7非正式版开源,好的框架离不开发起人的参与和技术支持和众多开发者的努力“You Only Look Once”或“YOLO”是一个对象检测算法的名字,这是Redmon等人在2016年的一篇研究论文中命名的。YOLO实现了自动驾驶汽车等前沿技术中使用的实时对象检测。让我们看看是什么使该算法如此受欢迎,并概述其工作原理。当人们看到图像以后,可以立即识别其中的对象、它们的位置和相对位置。这使得我们能够在几乎无意识的情况下完成复杂的任务,比如开车。因此,对汽车进行自动驾驶训练需要类似水平的反应能力和准确性。在其最基本的形式中,这样的系统必须能够分析实时视频中的道路,并能够在继续确定路径之前检测各种类型的对象及其在现实世界中的位置,所有这些都必须是实时的。YOLO将对象检测重新定义为一个回归问题。它将单个卷积神经网络(CNN)应用于整个图像,将图像分成网格,并预测每个网格的类概率和边界框。例如,以一个100x100的图像为例。我们把它分成网格,比如7x7。每个边界框可以使用四个描述符进行描述:界框的中心,高度,宽度,值映射到对象所属的类Github:https://github.com/jinfagang/yolov7
-
CNN原理CNN,又称卷积神经网络,是深度学习中重要的一个分支。CNN在很多领域都表现优异,精度和速度比传统计算学习算法高很多。特别是在计算机视觉领域,CNN是解决图像分类、图像检索、物体检测和语义分割的主流模型。1. 卷积如图1所示,图中的X和O无论怎么旋转或者缩放,人眼其实还是很容易识别出X和O。图1但是计算机不同,它看到的其实是一个个的像素阵列,如图2。如何对像素的阵列进行特征的提取其实就是卷积神经网络要干的事情。图2再看图3,我们发现X即使进行了旋转,但是绿、橙、紫框标记的区域在两张图中还是一致的,某种程度上,这其实就是X的特征。图3因此可以将这三个特征的区间提取出来,就形成了三个卷积核,如图4所示。图4既然有了卷积核,那么卷积核是如何进行卷积操作的呢?其实很简单,可以看一下图5,卷积核其实就是拿着这个矩阵在图片的矩阵上一点点的平移,就像扫地一样。每扫到一处地方就可以进行卷积的运算,计算方法很简单,如图5所示,左上角的卷积核扫到绿色框的位置,则卷积核矩阵的数字就和扫到的位置的矩阵的数字一一对应相乘然后相加,最后取一个均值,该值就是卷积核提取的特征。图5卷积核提取的所有的特征组成了一个长和宽变小的矩阵,这个矩阵又称为feature map,如图6。使用不同的卷积核也就能提取出不同的feature map。所以可以想象的是,如果不断的进行卷积操作,那么图片的矩阵会逐步地长宽减少,厚度增加。图6可以看到卷积操作通过卷积核是可以分别提取到图片的特征的,但是如何提前知道卷积核呢?像上文的例子,很容易可以找到3个卷积核,但是假如是人脸识别这样成千上万个特征的图片,就没办法提前知道什么是合适的卷积核。其实也没必要知道,因为选择什么样的卷积核,完全可以通过训练不断优化。初始时只需要随机设置一些卷积核,通过训练,模型其实自己可以学习到合适的卷积核,这也是卷积神经网络模型强大的地方。
-
KNN算法之训练样本不平衡情况若正负样本处于不平衡状态,运用投票决策的KNN算法判断输入样本的所属类别:结果显示输入样本为绿色类 。原因是红色类的个数远远小于绿色样本,导致出现的分类错误 。(1)若分类决策选择限定半径最近邻法,即以输入样本为圆心,最大半径R的圆内选择出现次数最多的类做为输入样本的类 。如下图,黑色样本的分类结果正确。(2)投票法是默认每个样本的权重相等,我们假定权重与距离成反比,即距离越大,对结果的影响越小,那么该样本的权重也越小,反之,权重则越大,根据权重对输入样本进行分类 。这种思想与adaBoost算法相似,分类性能好的弱分类器给予一个大的权重 。分类过程:(1)、选择与输入样本距离X0最近的K个训练样本Xi(i = 1,2,...,K),d(X0,Xi)表示输入样本和训练样本的距离。 (2)、根据距离与样本成反比的性质将距离转化成权重Wi,Wi表示输入样本X0与训练样本Xi的权重。(3)、我们累加每一类的样本权重,并认为该权重占所有权重和的比例是该类的生成概率,概率最大的类就是输入样本的分类结果。假设目标是二分类{C1,C2},表达式:若,则分类结果为C1类,反之C2类。回归过程:(1)(2)步骤与分类过程一直,第(3)步使用如下表达式得到回归值:其中,y为输出结果,f(xi)为最近邻样本的值。若权重相同的话,则输出结果为K个训练样本的平均值。用权重思想重新对上例进行分类,可得输入样本为红色类。
-
KNN算法之暴力实现方法暴力搜索(brute-force search)是线性扫描输入实例与每一个训练实例的距离并选择前k个最近邻的样本来多数表决,算法简单,但是当训练集或特征维度很大时,计算非常耗时,故这种暴力实现原理是不可行的 。4. KNN算法之kd树实现方法kd树是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构,构造kd树相当于不断用垂直于坐标轴的超平面将k维空间进行划分,构成一系列的K维超矩形区域,kd树省去了对大部分数据的搜索,大大的较少了计算量。kd树的KNN算法实现包括三部分:kd树的构建,kd树的搜索和kd树的分类。1. 构建kd树kd树实质是二叉树,其划分思想与cart树一致,即切分使样本复杂度降低最多的特征。kd树认为特征方差越大,则该特征的复杂度亦越大,优先对该特征进行切分 ,切分点是所有实例在该特征的中位数。重复该切分步骤,直到切分后无样本则终止切分,终止时的样本为叶节点。【例】给定一个二维空间的数据集:构造kd树的步骤:(1)、数据集在维度和的方差分别为6.9和5.3,因此首先从维度进行切分。(2)、 数据集在维度的中位数是7,以平面=7将空间分为左右两个矩形。(3)、分别对左右两个矩形的样本在维度的中位数进行切分。(4)、重复步骤(2)(3),直到无样本,该节点为叶子节点。如下图,绿色为叶子节点 ,红色为节点和根节点。2. KD树搜索(1)、搜索路径从根节点到叶节点,在KD树里面找到包含目标点的叶子节点。(2)、搜索路径从叶节点到根节点,找到距离目标点最近的样本实例点。过程不再复述,具体方法请参考李航博士《统计学习方法》。3. KD树预测每一次搜寻与输入样本最近的样本节点,然后忽略该节点,重复同样步骤K次,找到与输入样本最近邻的K个样本 ,投票法确定输出结果。
-
KNN算法三要素K值的选择、距离度量和分类决策规则是K近邻算法的三个基本要素。当三个要素确定后,对于任何一个新的输入实例,它所属的Y值也确定了,本节介绍了三要素的含义。1. 分类决**策规则**KNN算法一般是用多数表决方法,即由输入实例的K个邻近的多数类决定输入实例的类。这种思想也是经验风险最小化的结果。训练样本为(xi , yi)。当输入实例为 x,标记为c,是输入实例x的k近邻训练样本集。我们定义训练误差率是K近邻训练样本标记与输入标记不一致的比例,误差率表示为:因此,要使误差率最小化即经验风险最小,就要使(2.1)式右端的最大,即K近邻的标记值尽可能的与输入标记一致,所以多数表决规则等价于经验风险最小化。2. K值的选择:K取值较小时,模型复杂度高,训练误差会减小,泛化能力减弱;K取值较大时,模型复杂度低,训练误差会增大,泛化能力有一定的提高。KNN模型的复杂度可以通过对噪声的容忍度来理解,若模型对噪声很敏感,则模型的复杂度高;反之,模型的复杂度低。为了更好理解模型复杂度的含义,我们取一个极端,分析K=1和K="样本数"的模型复杂度。由上图可知,K=1时,模型输出的结果受噪声的影响很大。由上图可知,样本数等于7,当K=7时,不管输入数据的噪声有多大,输出结果都是绿色类,模型对噪声极不敏感,但是模型太过简单,包含的信息太少,也是不可取的。通过上面两种极端的K选取结果可知,K值选择应适中,K值一般小于20,建议采用交叉验证的方法选取合适的K值。3. 距离度量KNN算法用距离来度量两个样本间的相似度,常用的距离表示方法:(1)、欧式距离(2)、曼哈顿距离(3)、闵可夫斯基距离可以看出,欧式距离是闵可夫斯基距离在p=2时的特例,而曼哈顿距离是p=1时的特例 。文章来源于机器学习算法那些事 ,作者石头
-
1. KNN算法原理KNN算法是选择与输入样本在特征空间内最近邻的k个训练样本并根据一定的决策规则,给出输出结果 。决策规则:分类任务:输出结果为k个训练样本中占大多数的类 。回归任务:输出结果为k个训练样本值的平均值 。如下图的分类任务,输出结果为w1类 。KNN算法优缺点优点:1)算法简单,理论成熟,可用于分类和回归。2)对异常值不敏感。3)可用于非线性分类。4)比较适用于容量较大的训练数据,容量较小的训练数据则很容易出现误分类情况。5)KNN算法原理是根据邻域的K个样本来确定输出类别,因此对于不同类的样本集有交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为合适。缺点:1)时间复杂度和空间复杂度高。2)训练样本不平衡,对稀有类别的预测准确率低。3)相比决策树模型,KNN模型可解释性不强。文章来源于机器学习算法那些事 ,作者石头
-
作为基础特征提取器,视觉骨干(vision backbone)是计算机视觉领域的基础研究课题。得益于卓越的特征提取性能,CNN 成为过去十年中不可或缺的研究课题。在 AlexNet 重新开启深度学习十年之后,通过使用更深的网络、更高效的架构、更强的多尺度能力,社区已取得多项突破以获得更强大的视觉骨干和注意力机制。由于平移不变性和共享滑动窗口策略,CNN 对于具有任意大小输入的各种视觉任务是有效的。更先进的视觉骨干网络通常会在各种任务中带来显著性能提升,包括图像分类、对象检测、语义分割和姿势估计。同时,选择性注意力是处理视觉中复杂搜索组合的重要机制。注意力机制可以看作是基于输入特征的自适应选择过程。自从提出完全注意力网络以来,自注意力模型(即 Transformer)迅速成为了 NLP 领域的主导架构。近年来,Dosovitskiy 等人提出 ViT,它将 transformer 骨干引入计算机视觉,并在图像分类任务上优于 CNN。得益于强大的建模能力,基于 transformer 的视觉骨干迅速占领了各种任务的排行榜,包括对象检测和语义分割等。尽管自注意力机制最初是为 NLP 任务而设计的,但近来已经席卷了计算机视觉领域。然而,图像的 2D 特性为在计算机视觉中应用自注意力带来了三个挑战:将图像视为一维序列会忽略它们的二维结构;二次复杂度对于高分辨率图像来说太昂贵了;只捕捉空间适应性而忽略通道适应性。近日,来自清华大学胡事民团队和南开大学程明明团队提出了一种新型大核注意力(large kernel attention,LKA)模块,在避免上述问题的同时实现了自注意力中的自适应和长距离相关性。该研究还进一步提出了一种基于 LKA 的新型神经网络,命名为视觉注意力网络(VAN)。在图像分类、目标检测、语义分割、实例分割等广泛的实验中,VAN 的性能优于 SOTA 视觉 transformer 和卷积神经网络。论文一作为清华大学计算机系博士生国孟昊,主要研究方向为计算机视觉、计算机图形学、深度学习。他也是计图的开发者之一,曾在国际会议 / 期刊 ICLR/IPMI/CVMJ 上发表论文。胡事民,清华大学计算机科学与技术系教授,主要从事计算机图形学、智能信息处理和系统软件等方面的研究。研制并开源了第一个我国高校自主的深度学习框架——计图(Jittor),计图是一个完全动态编译(Just-in-time),基于元算子融合和统一计算图的深度学习框架。计图支持 30 多种的骨干网络,并且开源了多个模型库:对抗生成网络、图像语义分割、检测与实例分割、点云分类、可微渲染等。程明明,南开大学教授,计算机系主任,他的主要研究方向是计算机视觉和计算机图形学,他发表的论文谷歌引用 2 万余次,单篇最高引用 4000 余次。方法大核注意力注意力机制可以看作是一个自适应选择的过程,它可以根据输入特征选择鉴别特征并自动忽略噪声响应。注意力机制的关键步骤是生成注意力图,指出不同点的重要性。因此需要了解各点之间的关系。有两种众所周知的方法可以在不同点之间建立关系。第一种是采用自注意力机制来捕获长距离依赖。第二种是使用大核卷积来建立相关性并产生注意力图,这种方式还是有明显的弊端的,大核卷积带来了大量的计算开销和参数。为了克服上述缺点并利用自注意力和大核卷积的优点,该研究提出分解大核卷积操作来捕获长距离关系。如下图 2 所示,大核卷积可以分为三个部分:空间局部卷积(depth-wise 卷积)、空间长距离卷积(depth-wise 空洞卷积)和通道卷积(1×1 卷积)。视觉注意力网络(VAN) VAN 具有简单的层次结构,包括四个阶段,并逐步降低输出空间分辨率,即 H/4 × W/4 、H/8 × W/8 、H/16 × W/16 和 H /32 × W/32 。其中,H 和 W 代表输入图像的高度和宽度。随着分辨率的降低,输出通道的数量也在增加。输出通道 C_i 的变化如.该研究首先对输入进行下采样,并使用步幅数来控制下采样率。该研究通过定量和定性实验来证明 VAN 的有效性。其中,在 ImageNet-1K 图像分类数据集、COCO 目标检测数据集和 ADE20K 语义分割数据集上进行了定量实验,并在 ImageNet 验证集上使用 Grad-CAM 来可视化类激活映射(CAM)。该研究首先用消融实验证明 LKA 的每个组成部分都是至关重要的。为了快速获得实验结果,该研究选择 VAN-Tiny 作为基线模型通过以上分析,研究者发现 LKA 可以利用局部信息,捕捉长距离依赖,在通道和空间维度上都具有适应性。此外,实验结果证明 LKA 的所有组成部分都有助于完成识别任务。虽然标准卷积可以充分利用局部语境信息,但它忽略了长距离依赖和适应性。而自注意力虽然可以捕获长距离依赖,且在空间维度上具有适应性,但它忽略了局部信息和在通道维度上的适应性。研究者还将 VAN 与现有方法进行了比较,包括 MLP、CNN 和 ViT,结果如下表 5 所示。在相似的参数和计算成本下,VAN 优于常见的 CNN(ResNet、ResNeXt、ConvNeXt 等)、ViT(DeiT、PVT 和 Swin-Transformer 等)和 MLP(MLP -Mixer、ResMLP、gMLP 等)。可视化类激活映射(CAM)是一种可视化鉴别区域(注意力图)的流行工具。该研究采用 Grad-CAM 来可视化 ImageNet 验证集上由 VAN-Base 模型生成的注意力。下图 4 的结果表明,VAN-Base 可以清晰地聚焦在目标对象上,可视化直观地证明了 VAN 的有效性。
-
深度神经网络是由多个单层非线性网络叠加而成的,常见的单层网络按照编码解码情况分为3类:只包含编码器部分、只包含解码器部分、既有编码器部分也有解码器部分。编码器提供从输入到隐含特征空间的自底向上的映射,解码器以重建结果尽可能接近原始输入为目标将隐含特征映射到输入空间。 人的视觉系统对信息的处理是分级的。从低级的提取边缘特征到形状(或者目标等),再到更高层的目标、目标的行为等,即底层特征组合成了高层特征,由低到高的特征表示越来越抽象。深度学习借鉴的这个过程就是建模的过程。 深度神经网络可以分为3类,前馈深度网络(feed-forwarddeep networks, FFDN),由多个编码器层叠加而成,如多层感知机(multi-layer perceptrons, MLP)、卷积神经网络(convolutionalneural networks, CNN)等。反馈深度网络(feed-back deep networks, FBDN),由多个解码器层叠加而成,如反卷积网络(deconvolutionalnetworks, DN)、层次稀疏编码网络(hierarchical sparse coding, HSC)等。双向深度网络(bi-directionaldeep networks, BDDN),通过叠加多个编码器层和解码器层构成(每层可能是单独的编码过程或解码过程,也可能既包含编码过程也包含解码过程),如深度玻尔兹曼机(deep Boltzmann machines, DBM)、深度信念网络(deep beliefnetworks, DBN)、栈式自编码器(stacked auto-encoders, SAE)等。深度神经网络是由多个单层非线性网络叠加而成的,常见的单层网络按照编码解码情况分为3类:只包含编码器部分、只包含解码器部分、既有编码器部分也有解码器部分。编码器提供从输入到隐含特征空间的自底向上的映射,解码器以重建结果尽可能接近原始输入为目标将隐含特征映射到输入空间。
-
Dropout启发其他以随机方法训练指数量级的共享权重的集成。DropConnect是Dropout的一个特殊情况,其中一个标量权重和单个隐藏单元状态之间的每个乘积被认为是可以丢弃的一个单元 (Wan et al., 2013)。随机池化是构造卷积神经网络集成的一种随机池化的形式 (见第 9.3 节),其中每个卷积网络参与每个特征图的不同空间位置。目前为止,Dropout仍然是最广泛使用的隐式集成方法。一个关于Dropout的重要见解是,通过随机行为训练网络并平均多个随机决定进行预测,实现了一种参数共享的Bagging形式。早些时候,我们将Dropout描述为通过包括或排除单元形成模型集成的Bagging。然而,这种参数共享策略不一定要基于包括和排除。原则上,任何一种随机的修改都是可接受的。在实践中,我们必须选择让神经网络能够学习对抗的修改类型。在理想情况下,我们也应该使用可以快速近似推断的模型族。我们可以认为由向量 µ 参数化的任何形式的修改,是对 µ 所有可能的值训练 p(y | x, µ) 的集成。注意,这里不要求 µ 具有有限数量的值。例如, µ 可以是实值。Srivastava et al. (2014) 表明,权重乘以 µ ∼ N (1, I) 比基于二值掩码Dropout表现得更好。由于 E[µ] = 1,标准网络自动实现集成的近似推断,而不需要权重比例推断规则。
-
# -*- coding=UTF-8 -*- import tensorflow as tf # 输入数据 import input_data mnist = input_data.read_data_sets("/tmp/data/", one_hot=True) # 定义网络超参数 learning_rate = 0.001 training_iters = 200000 batch_size = 64 display_step = 20 # 定义网络参数 n_input = 784 # 输入的维度 n_classes = 10 # 标签的维度 dropout = 0.8 # Dropout 的概率 # 占位符输入 x = tf.placeholder(tf.types.float32, [None, n_input]) y = tf.placeholder(tf.types.float32, [None, n_classes]) keep_prob = tf.placeholder(tf.types.float32) # 卷积操作 def conv2d(name, l_input, w, b): return tf.nn.relu(tf.nn.bias_add( tf.nn.conv2d(l_input, w, strides=[1, 1, 1, 1], padding='SAME'),b) , name=name) # 最大下采样操作 def max_pool(name, l_input, k): return tf.nn.max_pool(l_input, ksize=[1, k, k, 1], strides=[1, k, k, 1], padding='SAME', name=name) # 归一化操作 def norm(name, l_input, lsize=4): return tf.nn.lrn(l_input, lsize, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name=name) # 定义整个网络 def alex_net(_X, _weights, _biases, _dropout): _X = tf.reshape(_X, shape=[-1, 28, 28, 1]) # 向量转为矩阵 # 卷积层 conv1 = conv2d('conv1', _X, _weights['wc1'], _biases['bc1']) # 下采样层 pool1 = max_pool('pool1', conv1, k=2) # 归一化层 norm1 = norm('norm1', pool1, lsize=4) # Dropout norm1 = tf.nn.dropout(norm1, _dropout) # 卷积 conv2 = conv2d('conv2', norm1, _weights['wc2'], _biases['bc2']) # 下采样 pool2 = max_pool('pool2', conv2, k=2) # 归一化 norm2 = norm('norm2', pool2, lsize=4) # Dropout norm2 = tf.nn.dropout(norm2, _dropout) # 卷积 conv3 = conv2d('conv3', norm2, _weights['wc3'], _biases['bc3']) # 下采样 pool3 = max_pool('pool3', conv3, k=2) # 归一化 norm3 = norm('norm3', pool3, lsize=4) # Dropout norm3 = tf.nn.dropout(norm3, _dropout) # 全连接层,先把特征图转为向量 dense1 = tf.reshape(norm3, [-1, _weights['wd1'].get_shape().as_list()[0]]) dense1 = tf.nn.relu(tf.matmul(dense1, _weights['wd1']) + _biases['bd1'], name='fc1') # 全连接层 dense2 = tf.nn.relu(tf.matmul(dense1, _weights['wd2']) + _biases['bd2'], name='fc2') # Relu activation # 网络输出层 out = tf.matmul(dense2, _weights['out']) + _biases['out'] return out # 存储所有的网络参数 weights = { 'wc1': tf.Variable(tf.random_normal([3, 3, 1, 64])), 'wc2': tf.Variable(tf.random_normal([3, 3, 64, 128])), 'wc3': tf.Variable(tf.random_normal([3, 3, 128, 256])), 'wd1': tf.Variable(tf.random_normal([4*4*256, 1024])), 'wd2': tf.Variable(tf.random_normal([1024, 1024])), 'out': tf.Variable(tf.random_normal([1024, 10])) } biases = { 'bc1': tf.Variable(tf.random_normal([64])), 'bc2': tf.Variable(tf.random_normal([128])), 'bc3': tf.Variable(tf.random_normal([256])), 'bd1': tf.Variable(tf.random_normal([1024])), 'bd2': tf.Variable(tf.random_normal([1024])), 'out': tf.Variable(tf.random_normal([n_classes])) } # 构建模型 pred = alex_net(x, weights, biases, keep_prob) # 定义损失函数和学习步骤 cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(pred, y)) optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) # 测试网络 correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1)) accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) # 初始化所有的共享变量 init = tf.initialize_all_variables() # 开启一个训练 with tf.Session() as sess: sess.run(init) step = 1 # Keep training until reach max iterations while step * batch_size < training_iters: batch_xs, batch_ys = mnist.train.next_batch(batch_size) # 获取批数据 sess.run(optimizer, feed_dict={x: batch_xs, y: batch_ys, keep_prob: dropout}) if step % display_step == 0: # 计算精度 acc = sess.run(accuracy, feed_dict={x: batch_xs, y: batch_ys, keep_prob: 1.}) # 计算损失值 loss = sess.run(cost, feed_dict={x: batch_xs, y: batch_ys, keep_prob: 1.}) print "Iter " + str(step*batch_size) + ", Minibatch Loss= " + "{:.6f}".format(loss) + ", Training Accuracy= " + "{:.5f}".format(acc) step += 1 print "Optimization Finished!" # 计算测试精度 print "Testing Accuracy:", sess.run(accuracy, feed_dict={x: mnist.test.images[:256], y: mnist.test.labels[:256], keep_prob: 1.})
-
AlexNet模型AlexNet中包含了几个比较新的技术点,也首次在CNN中成功应用了ReLU、Dropout和LRN等Trick。同时AlexNet也使用了GPU进行运算加速。AlexNet将LeNet的思想发扬光大,把CNN的基本原理应用到了很深很宽的网络中。AlexNet主要使用到的新技术点如下:(1)成功使用ReLU作为CNN的激活函数,并验证其效果在较深的网络超过了Sigmoid,成功解决了Sigmoid在网络较深时的梯度弥散问题。虽然ReLU激活函数在很久之前就被提出了,但是直到AlexNet的出现才将其发扬光大。(2)训练时使用Dropout随机忽略一部分神经元,以避免模型过拟合。Dropout虽有单独的论文论述,但是AlexNet将其实用化,通过实践证实了它的效果。在AlexNet中主要是最后几个全连接层使用了Dropout。(3)在CNN中使用重叠的最大池化。此前CNN中普遍使用平均池化,AlexNet全部使用最大池化,避免平均池化的模糊化效果。并且AlexNet中提出让步长比池化核的尺寸小,这样池化层的输出之间会有重叠和覆盖,提升了特征的丰富性。(4)提出了LRN层,对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。(5)使用CUDA加速深度卷积网络的训练,利用GPU强大的并行计算能力,处理神经网络训练时大量的矩阵运算。AlexNet使用了两块GTX 580 GPU进行训练,单个GTX 580只有3GB显存,这限制了可训练的网络的最大规模。因此作者将AlexNet分布在两个GPU上,在每个GPU的显存中储存一半的神经元的参数。因为GPU之间通信方便,可以互相访问显存,而不需要通过主机内存,所以同时使用多块GPU也是非常高效的。同时,AlexNet的设计让GPU之间的通信只在网络的某些层进行,控制了通信的性能损耗。 (6)数据增强,随机地从256*256的原始图像中截取224*224大小的区域(以及水平翻转的镜像),相当于增加了2*(256-224)^2=2048倍的数据量。如果没有数据增强,仅靠原始的数据量,参数众多的CNN会陷入过拟合中,使用了数据增强后可以大大减轻过拟合,提升泛化能力。进行预测时,则是取图片的四个角加中间共5个位置,并进行左右翻转,一共获得10张图片,对他们进行预测并对10次结果求均值。同时,AlexNet论文中提到了会对图像的RGB数据进行PCA处理,并对主成分做一个标准差为0.1的高斯扰动,增加一些噪声,这个Trick可以让错误率再下降1%。
-
这周我们简单介绍一个高效分子优化的方法。该工作由UIUC的Jimeng Sun组合MIT的Connor Coley组合作完成,对应的文章题目是Differentiable Scaffolding Tree for Molecule Optimization[1],被2022年ICLR接受,主要的代码和数据发布在https://github.com/futianfan/DST。思路:基于梯度的分子优化分子的可微分骨架树类梯度上升的优化算法优化效果测试由可微性得到的可解释性思路:基于梯度的分子优化在药物发现中,分子优化,即找到具有理想性质的分子结构,是核心的一步。由于化学结构的复杂性,传统上我们只能依赖于一些启发式的组合优化算法,如遗传算法、树搜索等。最近深度学习的发展确实提供了一些新的思路,但目前大部分依赖图生成算法的分子优化都是通过图神经网络(graph neural network, GNN)来显式地生成一个分子,然后优化目标函数,通过反向传播梯度来更新GNN参数,使得网络生成具有优化性质的分子。例如在增强学习(reinforcement learning, RL)中目标函数是根据反馈(reward)定义的;在深度生成模型(deep generative model, DGM)中基于和目标分子的广义距离定义。但这些算法普遍优化能力不够强,尤其没有考虑oracle的成本,许多算法需要调用数十万甚至百万次oracle才能得到较好的结果,而这在实际分子设计过程中显然是不现实的。我们知道,数值优化的核心就是在一个点估计其指向极值点的方向,而这一方向一般可以通过梯度估计。那我们是否可以估计一个分子的性质相对于结构的梯度,通过这个梯度估计方向进而优化一个分子?在Alan Aspuru-Guzik组的Deep Molecular Dreaming[2]一文中作者利用分子的字符串(SELFIES)表示实现了这一点:将分子看做每个位置字符的分布概率,通过一个一维卷积神经网络(convolutional neural network, CNN)学习其性质,得到可微的性质预测器,进而估计分子的梯度,但是效果并不好。而在本文中,作者通过提出分子的可微分骨架树(Differentiable Scaffolding Tree, DST)这一概念,使得分子直接在结构层面上可微,进而构建了一个高效的优化算法。与其他生成模型不同,作者先预训练(pre-train)了一个以骨架树(ST)为输入的GNN来预测性质(标量)。然后在优化过程中固定GNN参数,每步迭代里首先构造分子相应的DST,通过前向传播用GNN来预测性质,然后优化目标性质并通过反向传播梯度来更新DST里的参数,进而优化分子结构。分子的可微分骨架树首先我们明确本文关注从头分子优化(de novo molecule optimization),即以一个优化算法在一个隐式定义的小分子空间上找到性质较好的分子。而分子性质由一个Oracle给出,即给定一个分子,输出相对应的性质,可以看作一个黑盒函数(black box function of molecules),例如衡量一个分子的类药性的QED。为了使分子在图结构层面上可微,首先需要扩展分子图的概念。另外为了避免生成环的中间步骤可能带来的不必要的麻烦,作者选择在优化过程中用骨架树(scaffolding tree, ST)来表示分子,其节点定义为一个基本单位(substructure),包含了常见的原子和单环(详见原文附录Figure 5)。我们可以用节点的类别矩阵(node indicator matrix)和节点之间的链接矩阵(adjacency matrix)表示一个骨架树。其中类别矩阵每一行是一个one-hot向量,代表了该节点是哪一个基本单位,而链接矩阵中的每一个元素都是binary的数字,指示每一对节点之间是否连接。构造可微分骨架树(DST)的核心是将节点的类别和连接看做可学习的0到1的概率分布,而非0或1的binary code。其中为了实现连接可微性的自洽,作者提出了与连接等价的权重向量(node weight vector),通过一个节点的权重表示其存在与否,并通过权重构造连接矩阵,将连接与否的问题变成了该节点是否存在的问题:从骨架树得到可微分骨架树的方法如下所示:首先将分子结构抽象为骨架树, 然后将骨架树中每个结点连接上一个拓展节点(expansion node)。每一个叶结点和拓展结点的权重和类别是可学习的(learnable)。其中节点类别是一个softmax的输出,保证和为1。结点权重是一个sigmoid的输出,保证在0-1之间。类梯度上升的优化算法为了进行优化,作者首先预先训练(pre-train)了一个GNN来做性质预测,其输入是一个分子的可微分骨架树,输出是目标的性质(标量),即。为了平衡不同权重节点的贡献,作者在GNN中使用了加权平均的read-out方法:其中代表第回迭代之后的节点特征(node embedding)的第行,代表第个节点的权重,代表全连接网络。整个算法是一个迭代式优化。在单步迭代中,给定输入分子的DST,得到了可微的性质预测替代后,可以通过任意梯度优化算法(文中使用了Adam)解来得到优化后的DST。在得到优化后的DST后,根据其优化后的权重和类别,作者设计了如下三种在对应骨架树上的操作:(1)删除(SHRINK)叶结点权重小时,意味着该节点对性质提升没有帮助,或者说该节点的存在对性质提升有负面影响,所以在对应骨架树上删除该节点。(2)拓展(EXPAND)拓展节点权重大时,意味着该节点的存在对性质提升帮助是正向的,所以在对应骨架树上加上一个新的结点。该节点的类别也从对应softmax输出值中选择。(3)替换(REPLACE)若一个节点权重改变不大,但类别改变较大时,不删除也不拓展,但是在优化后的分布中重新采样一个substructure(softmax输出中值比较大的)。根据更新后的DST采样其中一个操作,得到对应的骨架树作为下一轮迭代的输入,如此我们迭代地优化分子。在每一轮迭代内DST的维数是固定的,由输入分子决定,因此一轮迭代只能得到和原分子相差最多一个节点的分子。但在多轮迭代优化过程中,每一轮的输入都是上一轮的输出,因此相应的DST维度也会变化,使得生成分子的大小只受限于优化迭代次数。在得到优化后的骨架树后,作者枚举对应的所有可能的分子图,用oracle测量每一个分子。为了有更好的优化效果,并兼具输出分子的多样性,作者每次优化时同时优化多条轨迹,并在其中使用determinantal point process(DPP)来选择保留的分子。即在每一部枚举出对应的多个可能的分子图之后,不是单纯根据性质好坏,而额外考虑了相似性矩阵的行列式,即选择最大化下式的一批分子:其中是subset 这批分子的性质分数的对角矩阵,而则是这批分子的相似性矩阵。可以注意到单纯依据的行列式挑选就是greedy的top-k选择。而相似矩阵的行列式的最大化则鼓励subset内的diversity的增加(可以考虑一个的例子,对角线为1,非对角为彼此之间的相似度)。如此我们便得到了一个完整的优化算法。优化效果测试作者首先衡量主要的优化效果,包含了单目标优化和多目标优化(同时优化多个性质)。为了能够有比较全面的对比,所有算法选择了分数最高的100个分子衡量其:新颖性(Nov):生成分子不在训练集(如果有)中的比例;多样性(Div):生成分子的多样性,衡量对化学空间的探索能力;目标性质的平均优化结果(APS);Oracle调用的次数(#oracle):我们关注有限的oracle调用的情况下的表现,因为oracle调用的数量是衡量一个算法效率的重要指标;由于DST和一些其他算法的一部分oracle调用可以离线完成(比如利用已有的标注数据),另一部分必须线上完成,所以#oracle是A+B的形式,A为线下调用,B为线上调用。从实验结果上看,直接运用了梯度信息的DST优化效率最高,说明了由DST估计的梯度的可靠性。深度生成模型(LigGPT)由于其本身并不是一个迭代优化算法,表现并不好。增强学习方法(GCPN/MolDQN)虽然在无限oracle调用的情况下能够得到一些较好的结果,但是不出所料在限制oracle调用的情况下表现相对不好。而以传统的组合优化方法为基础的算法(如GA+D,MARS)表现相对还是更好。为了更系统地比较算法效率,作者测试了算法的oracle efficiency,即不同方法在不同oracle调用次数下的优化性能。结果如下:Oracle efficiency测试结果。其中DST-rand为DST的ablation study,即同样设定下每步随机选择骨架树上的操作。横轴为oracle调用数量,纵轴为top-100个分子的平均性质(越高越好)。DST在三个任务上都取得了最好的效果,其他结论也和第一个实验类似。由可微性得到的可解释性作者另外展示了由DST带来的分子性质的可解释性。通过观察当前DST对各个结点权重和梯度,我们可以看到不同的结点对性质提升的影响,进而分析不同基团、亚结构对性质的影响。分子可解释性实例。
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签