深度学习图像分类(十四): EifficientNet系列(V1, V2)
深度学习图像分类(十四): EifficientNet系列
文章目录
- 深度学习图像分类(十四): EifficientNet系列
- 前言
- 一、Movtivation
- 二、EifficentNetV1
- 1. Three scale method for network
- 2. scaling factor
- 3. Model Architecture
- 二、EifficientV2
- 1. Motivation
- 2.EfficeientNet存在的三个问题
- 大图像尺寸会导致显著的内存占用
- DepthWise卷积不能完全利用现有的加速器
- EfficientNet的缩放策略不是最优解
- 3.EfficeientNetV2的改进
- 优化NAS神经网络结构搜索
- 详解Fused-MBConv算子单元
- 每阶段的网络缩放因子应该不同
- 引入改进的渐进式学习的策略
- 三、 代码
- 四、 总结
前言
EfficientNet源自Google Brain的论文EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks. 从标题也可以看出,这篇论文最主要的创新点是Model Scaling(感觉是填了GoogLeNetV3中网络设计准则留下了的坑,好奇的同学可以看我之前的博文). 论文提出了compound scaling(混合缩放)把网络缩放的三种方式:深度、宽度、分辨率,组合起来按照一定规则缩放,从而提高网络的效果。EfficientNet在网络变大时效果提升明显,把精度上限进一步提升,成为了当前最强网络。EfficientNet-B7在ImageNet上获得了最先进的 84.4%的top-1精度 和 97.1%的top-5精度,比之前最好的卷积网络(也是谷歌自己家的GPipe, Top-1: 84.3%, Top-5: 97.0%)大小缩小8.4倍、速度提升6.1倍。
一、Movtivation
EfficientNet的主要创新点并不是结构,不像ResNet、SENet发明了shortcut或通道attention机制,EfficientNet的base结构像MobileNetV3一样, 也是利用NAS网络搜索技术搜出来的(好奇的同学可以看完之前MobileNet的讲解博文),然后使用compound scaling规则放缩,得到一系列表现优异的网络:B0~B7. 下面两幅图分别是ImageNet的Top-1 Accuracy随参数量和flops变化关系图,可以看到EfficientNet饱和值高,并且到达速度快。
增加网络参数可以获得更好的精度(有足够的数据,不过拟合的条件下),例如ResNet可以加深从ResNet-18到ResNet-200,GPipe将baseline模型放大四倍在ImageNet数据集上获得了84.3%的top-1精度。增加网络参数的方式有三种:深度、宽度和分辨率。探究这三种方式对网络性能的影响,以及如何同时缩放这三种因素是EifficentNet的主要贡献。
二、EifficentNetV1
1. Three scale method for network
下图很形象的描述了三种放大网络的策略,(a)是参照网络;(b)是放大网络的宽度; (c)是放大网络的深度; (d)是放大网络的输入尺寸; (e)是EifficentNet的策略,同时缩放。
这里对三种网络策略做详细解释:
深度:缩放网络深度在许多ConvNets都有使用,例如Vggnet,Resnet等,直觉上更深的网络可以捕获到更丰富和更复杂的特征,在新任务上也可以泛化的更好。然而,更深的网络由于梯度消失问题(这里我更倾向于说成是网络退化问题)也更难训练。尽管有一些技术,例如跨层连接、批量归一化等可以有效减缓训练问题,但是深层网络的精度回报减弱了:举个例子,ResNet-1000和ResNet-101具有类似的精度,即使它的层数更多。
宽度:缩放网络宽度也是一种常用的手段,例如GoogLeNet系列网络,正如之前讨论过的,更宽的网络可以捕捉到更细粒度的特征从而易于训练。然而,非常宽而又很浅的网络在捕捉高层次特征时有困难。
分辨率:使用更高分辨率的输入图像,ConvNets可能捕捉到更细粒度的模式。从最早的 224x224,现在有些ConvNets为了获得更高的精度选择使用 229x229 或者 331x331。目前,GPipe使用 480x480 的分辨率获得了最先进的ImageNet精度,更好的精度比如 600x600 也被广泛使用在目标检测网络中。
但是,这里单独加深某一种策略,所得到的精度回报都不是成正比的,如下图所示:
三种策略显示着同一规律:刚开始时有效,后面逐渐趋于饱和,而且所得的的精度回报越来越少。
2. scaling factor
我们经验上可以观察到不同缩放维度之间是不独立的,直观上来讲,对于分辨率更高的图像,我们应该增加网络深度,因为需要更大的感受野来帮助捕获更多像素点的类似特征,同时也应该增加网络宽度来获得更细粒度的特征。这些直觉指导着我们去协调平衡不同缩放维度而不是传统的单个缩放维度。
为了追去更好的精度和效率,在缩放时平衡网络所有维度至关重要。事实上,之前的一些工作已经开始在追去任意缩放网络深度和宽度,但是他们仍然需要复杂的人工微调。在本篇论文中,我们提出了一个新的复合缩放方法——使用一个复合系数ϕ统一缩放网络宽度、深度和分辨率:
这里的α,β和γ是通过NAS网络搜索技术得到的,下图给出了具体缩放因子在MobileNetV1V2和ResNet50网络上的提升效果:
3. Model Architecture
EfficientNet在模型结构设计上借鉴了MobileNet系列网络,结构同样是使用NAS技术搜索得到,不同的是优化目标,这里优化的是FLOPS而不是延迟。再次感叹一下大力出奇迹… 同时,使用了MobileNet V3中的MBConv作为模型的主干网络,同时也是用了SENet中的squeeze and excitation方法进一步提升性能,可能是因为使用的NAS搜索空间相似,所以得到的网络结构也很相似。
这里放一张图回顾一下MBConv,详见我之前MobileNet系列的博文
其完整的网络模型架构参数如下表所示:
如上表所示,EiffienctNetB0其实就是由简单的MBconv操作堆叠而成的。对于EfficientNet-B0这样的一个基线网络,如何使用复合扩展发对该网络进行扩展呢?原文中给出答案----主要就是分两步走:
首先,给出了最佳缩放因子α,β和γ的具体数值,分别是1.2, 1.1, 1.15。 然后我们将 α、β、γ 固定为常数并按比例放大得到B0~7一个七个网络模型。具体缩放因子如下:
最后,作者便由此扩展出了一系列的网络结构,如下所示:
二、EifficientV2
1. Motivation
首先,大家都知道,对于深度学习模型而言,模型的训练效率是非常重要的一个问题,这里的效率主要包括两个方面,一是训练时间、二是训练后的模型参数。
举个例子,GPT3,大家公认的在少样本学习上表现出来的性能很好,但是训练效率就不高,需要好几周的训练、几千块GPU集群,所以训练成本极高,对于一般用户来说,不可能有机会参与到训练或者优化的工作中,这是目前深度学习发展的一个很大的限制。
第二个是,EfficientNet模型,这个模型是19年谷歌发布的,这个模型已经开源了,系统地研究了模型缩放并且仔细验证了网络深度、宽度和分辨率之间的平衡进而获得更好的性能表现。
另外,现有的渐进式学习都是采用渐进式的更改输入图片的尺寸来实现,但是保持同样的正则化配置,作者认为这是导致模型性能下降的一大因素。可能大家还不太知道什么是渐进式学习,待会儿我会介绍一下。
因此基于上述三个动机,本文EfficientNetV2提出了相应的改进方案。总的来说,本文的贡献主要包括三点
提出了EfficientNetV2模型,谷歌在EfficientNet的基础上,引入了Fused-MBConv到搜索空间中。
修改了渐进式学习策略,提出了自适应调整正则化参数的机制,实现了加速训练、同时还能够保证准确率不降,甚至还有细微上升的效果。
最后作者在多个benchmark上进行了实验,我们可以发现模型在多个基准数据集上取得了SOTA性能,且训练效率更高。
2.EfficeientNet存在的三个问题
大图像尺寸会导致显著的内存占用
已有研究表明:EfficientNet的大图像尺寸会导致显著的内存占用。由于计算单元的总内存是固定的,我们不得不采用更小的batch训练这些模型,这无疑会降低训练速度。大家可以看下面这个图,如果训练图片尺寸设置为512,就会出现OOM的问题,如果小尺寸,则batch size的数量又会显著增加。这个很好理解,对吧。所以,需要渐进式的输入图片。
DepthWise卷积不能完全利用现有的加速器
DepthWise卷积虽然理论上减少了很多的计算量,但是,不能完全利用现有的加速器。DepthWise卷积已经多次提及,在之前的博文MobileNe中有详细介绍。因此Fused-MBConv被论证能够充分利用移动端和服务器端的加速器加速计算,因此引入了这个算子到搜索空间中,进一步提速。
EfficientNet的缩放策略不是最优解
值得讨论的是EfficientNet的缩放策略,提出我们需要同时考虑网络宽度、网络深度和分辨率来设计网络结构,也验证了这种方式提升了模型性能。但是采用的策略是对模型结构均匀的缩放和扩张,什么意思呢?就是每个stage的扩张倍数是一样的,举个例子,比如我把depth系数配置为2,那么每个stage的网络深度都乘2。作者认为,这种策略不是最优解,不是所有的阶段对模型的训练效率、模型性能影响都是相同的。因此,本文采用了非均匀的模型缩放策略,越往后的stage模型层数越深。
3.EfficeientNetV2的改进
有了上面的讨论,那么对于这篇论文网络架构的设计就很好理解了。
优化NAS神经网络结构搜索
作者的主要改进包括:把Fused-MBConv算子加入到搜索空间中,移除一些不重要的搜索选项。
搜索的结果是将模型前几个stage的MBconv替换成Fused-MBConv,如下表所示:
详解Fused-MBConv算子单元
如下图所示,与MBconv相比,Fuesd-MBconv将减少卷积操作参数量的DWconv又换成了正常的3X3卷积(大无语事件:搞半天又回去了…),这也对应着ShuffleNetV2中提出的观点:分组卷积看似减少了参数量,却也增加了运算时间(DWconv是分组卷积的极端情况);除了这点,其他的毫无一点不同。
每阶段的网络缩放因子应该不同
在EifficentNetV1版本中,网络在每个阶段的缩放倍率是相同的。但是,在V2中作者提出新观点:针对不同的stage应该有不同的缩放倍率。在论文中,作者也确实给出了网络的具体参数。作为一种启发式方法,作者在网络的后几个阶段stage5和6阶段添加更多的层结构但是没有解释为什么这么设置,估计也是NAS网络搜索技术得出来的。
并且,值得注意的是,作者将最大推理图像大小限制为 480,因为非常大的图像通常会导致昂贵的内存和训练速度开销。
引入改进的渐进式学习的策略
什么是渐进式学习呢?渐进式学习的话,就是在训练过程中,随时间动态的调整输入图片的尺寸,来尽可能多的使用数据集。
但是现有的渐进式学习研究,只改了输入,并没有修改模型的正则化参数,也就是没有改变模型反向调参的过程,那是不是认为不同分辨率的图片对模型的影响是同等的?
作者认为,这是导致模型Accuracy下降的一个原因。我们的经验其实可以看到,更大的模型应该需要更严格的正则化来避免过拟合,例如EfficientNet-B7就需要设置更大的dropout值以及更强的数据增强方式。
所以这篇论文,认为即使对于同一个网络,更小的图片尺寸训练出的模型性能更差,所以正则化要求更弱。反之,大尺寸的图片,依赖于更严格的正则化配置。
上图是原论文中的大熊猫例子,随着训练的迭代(1…100…300),输入模型的图像尺寸也在逐渐变大。同时实验配置的正则化参数也随之增大,包括三个参数,dropout的概率、随机擦除样本的行数、两种图片混合的比例,也就是数据增强的幅度在逐渐增大,来提升模型正则的要求。
完整的网络结构在‘优化NAS神经网络结构搜索’章节已经给出,对比了EfficientNetV2和EfficientNet的性能。EfficientNetV2 相较于EfficientNet, 模型参数、FLOPs提升效果有限,但是推断速度提升了3倍,如下图所示。这个结果的获得,就是刚才提到的,引入了Fuded_MBConv这个结构,还有搜索空间的调整。
三、 代码
这里给出模型搭建的python代码(基于pytorch实现)。完整的代码是基于图像分类问题的(包括训练和推理脚本,自定义层等)详见我的GitHub: 完整代码链接
from collections import OrderedDict
from functools import partial
from typing import Callable
from unittest import resultimport torch.nn as nn
import torch
from torch import Tensor
from custom_layers.CustomLayers import ConvBNActivation, SqueezeExcitation
from custom_layers.CustomMethod import DropPath
class MBconv(nn.Module):def __init__(self, input_channels, output_channels, kernel_size, expand_ratio, stride, se_ratio, drop_ratio, norm_layer):super().__init__()if stride not in [1,2]:raise ValueError('illegal stride value')activate_layer = nn.SiLUexpanded_channels = input_channels * expand_ratio# 在EfficientNetV2中,MBConv中不存在expansion=1的情况(V1存在)assert expand_ratio != 1# point-wise expansionself.expand_conv = ConvBNActivation(input_channels, expanded_channels, kernel_size=1, stride=1, padding=0, norm_layer=norm_layer, activation_layer=activate_layer)# depth-wise convself.dw_conv = ConvBNActivation(expanded_channels, expanded_channels, kernel_size, stride, groups=expanded_channels, norm_layer=norm_layer, activation_layer=activate_layer)# SE-blockself.se_block = SqueezeExcitation(expanded_channels, input_channels, scale_ratio=4) if se_ratio > 0 else nn.Identity()# point-wise convself.pw_conv = ConvBNActivation(expanded_channels, output_channels, kernel_size=1, stride=1, padding=0, norm_layer=norm_layer, activation_layer=nn.Identity)self.output_channels = output_channelsself.drop_ratio = drop_ratioself.has_shortcut = (stride == 1 and input_channels == output_channels)# dropPathif self.drop_ratio > 0 and self.has_shortcut:self.dropout = DropPath(drop_ratio)def forward(self, x):x = self.expand_conv(x)x = self.dw_conv(x)x = self.se_block(x)x = self.pw_conv(x)if self.has_shortcut:if self.drop_ratio > 0:result = self.dropout(x)result += xelse:result = xreturn resultclass FusedMBConv(nn.Module):def __init__(self, input_channels, output_channels, kernel_size, expand_ratio, stride, se_ratio, drop_ratio, norm_layer):super().__init__()assert stride in [1, 2]assert se_ratio == 0self.has_shortcut = ( stride==1 and input_channels == output_channels)self.drop_rate = drop_ratioself.has_expansion = expand_ratio != 1 activate_layer = nn.SiLUexpanded_c = input_channels * expand_ratio# 只有当expand ratio不等于1时才有expand convif self.has_expansion:# Expansion convolutionself.expand_conv = ConvBNActivation(input_channels, expanded_c, kernel_size, stride, norm_layer=norm_layer, activation_layer=activate_layer)self.project_conv = ConvBNActivation(expanded_c, output_channels, kernel_size=1, norm_layer=norm_layer, activation_layer=nn.Identity)else:self.project_conv = ConvBNActivation(input_channels, output_channels, kernel_size, stride, norm_layer=norm_layer, activation_layer=activate_layer)self.out_channels = output_channels# 只有在使用shortcut连接时才使用dropout层self.drop_rate = drop_ratioif self.has_shortcut and drop_ratio > 0:self.dropout = DropPath(drop_ratio)def forward(self, x:Tensor):if self.has_expansion:reslut = self.expand_conv(x)result = self.project_conv(reslut)else:result = self.project_conv(x)if self.has_shortcut:if self.drop_rate>0:reslut = self.dropout(result)result += xreturn resultclass EfficientNetV2(nn.Module):def __init__(self,model_cnf: list,num_classes: int = 1000,num_features: int = 1280,dropout_rate: float = 0.2,drop_connect_rate: float = 0.2):super(EfficientNetV2, self).__init__()for cnf in model_cnf:assert len(cnf) == 8norm_layer = partial(nn.BatchNorm2d, eps=1e-3, momentum=0.1)stem_filter_num = model_cnf[0][4]self.stem = ConvBNActivation(3,stem_filter_num,kernel_size=3,stride=2,norm_layer=norm_layer) # 激活函数默认是SiLUtotal_blocks = sum([i[0] for i in model_cnf])block_id = 0blocks = []for cnf in model_cnf:repeats = cnf[0]op = FusedMBConv if cnf[-2] == 0 else MBconvfor i in range(repeats):blocks.append(op(kernel_size=cnf[1],input_channels=cnf[4] if i == 0 else cnf[5],output_channels=cnf[5],expand_ratio=cnf[3],stride=cnf[2] if i == 0 else 1,se_ratio=cnf[-1],drop_ratio=drop_connect_rate * block_id / total_blocks,norm_layer=norm_layer))block_id += 1self.blocks = nn.Sequential(*blocks)head_input_c = model_cnf[-1][-3]head = OrderedDict()head.update({"project_conv": ConvBNActivation(head_input_c,num_features,kernel_size=1,norm_layer=norm_layer)}) # 激活函数默认是SiLUhead.update({"avgpool": nn.AdaptiveAvgPool2d(1)})head.update({"flatten": nn.Flatten()})if dropout_rate > 0:head.update({"dropout": nn.Dropout(p=dropout_rate, inplace=True)})head.update({"classifier": nn.Linear(num_features, num_classes)})self.head = nn.Sequential(head)# initial weightsfor m in self.modules():if isinstance(m, nn.Conv2d):nn.init.kaiming_normal_(m.weight, mode="fan_out")if m.bias is not None:nn.init.zeros_(m.bias)elif isinstance(m, nn.BatchNorm2d):nn.init.ones_(m.weight)nn.init.zeros_(m.bias)elif isinstance(m, nn.Linear):nn.init.normal_(m.weight, 0, 0.01)nn.init.zeros_(m.bias)def forward(self, x: Tensor) :x = self.stem(x)x = self.blocks(x)x = self.head(x)return xdef efficientnetv2_s(num_classes: int = 1000):"""EfficientNetV2https://arxiv.org/abs/2104.00298"""# train_size: 300, eval_size: 384# repeat, kernel, stride, expansion, in_c, out_c, operator, se_ratiomodel_config = [[2, 3, 1, 1, 24, 24, 0, 0],[4, 3, 2, 4, 24, 48, 0, 0],[4, 3, 2, 4, 48, 64, 0, 0],[6, 3, 2, 4, 64, 128, 1, 4],[9, 3, 1, 6, 128, 160, 1, 4],[15, 3, 2, 6, 160, 256, 1, 4]]model = EfficientNetV2(model_cnf=model_config,num_classes=num_classes,dropout_rate=0.2)return modeldef efficientnetv2_m(num_classes: int = 1000):"""EfficientNetV2https://arxiv.org/abs/2104.00298"""# train_size: 384, eval_size: 480# repeat, kernel, stride, expansion, in_c, out_c, operator, se_ratiomodel_config = [[3, 3, 1, 1, 24, 24, 0, 0],[5, 3, 2, 4, 24, 48, 0, 0],[5, 3, 2, 4, 48, 80, 0, 0],[7, 3, 2, 4, 80, 160, 1, 4],[14, 3, 1, 6, 160, 176, 1, 4],[18, 3, 2, 6, 176, 304, 1, 4],[5, 3, 1, 6, 304, 512, 1, 4]]model = EfficientNetV2(model_cnf=model_config,num_classes=num_classes,dropout_rate=0.3)return modeldef efficientnetv2_l(num_classes: int = 1000):"""EfficientNetV2https://arxiv.org/abs/2104.00298"""# train_size: 384, eval_size: 480# repeat, kernel, stride, expansion, in_c, out_c, operator, se_ratiomodel_config = [[4, 3, 1, 1, 32, 32, 0, 0],[7, 3, 2, 4, 32, 64, 0, 0],[7, 3, 2, 4, 64, 96, 0, 0],[10, 3, 2, 4, 96, 192, 1, 4],[19, 3, 1, 6, 192, 224, 1, 4],[25, 3, 2, 6, 224, 384, 1, 4],[7, 3, 1, 6, 384, 640, 1, 4]]model = EfficientNetV2(model_cnf=model_config,num_classes=num_classes,dropout_rate=0.4)return model
四、 总结
EifficentNetV1:
- 对比分析了网络深度,网络宽度和输入图像尺寸对模型性能的影响。
- 通过NAS网络搜索技术给出了综合缩放网络的方法和参数
EifficentNetV2:
- 提出DepthWise卷积不能完全利用现有的加速器的观点,并将V1版本中网络的前几个stage的MBconv换成Fuesd-MBconv,以提升推理速度。
- 提出了网络不同阶段的不同缩放策略,并且限制了网络的最大输入尺寸。
- 提出了新的渐进式学习策略:正则化应该随着输入图像尺寸的改变而改变。
深度学习图像分类(十四): EifficientNet系列(V1, V2)相关推荐
- 花书+吴恩达深度学习(十四)卷积神经网络 CNN 之经典案例(LetNet-5, AlexNet, VGG-16, ResNet, Inception Network)
目录 0. 前言 1. LeNet-5 2. AlexNet 3. VGG-16 4. ResNet 残差网络 5. Inception Network 如果这篇文章对你有一点小小的帮助,请给个关注, ...
- 深度学习(十四)基于CNN的性别、年龄识别
CNN应用之性别.年龄识别 原文地址:http://blog.csdn.net/hjimce/article/details/49255013 作者:hjimce 一.相关理论 本篇博文主要讲解201 ...
- 深度学习(十四)——Softmax详解, 目标检测, RCNN
https://antkillerfarm.github.io/ Softmax详解 首先给出Softmax function的定义: yc=ζ(z)c=ezc∑Cd=1ezd for c=1,-,C ...
- 非监督特征学习与深度学习(十四)--------循环神经网络
注:本文转载自https://github.com/exacity/simplified-deeplearning/blob/master/%E5%BE%AA%E7%8E%AF%E9%80%92%E5 ...
- 动手学深度学习(十四)——权重衰退
文章目录 1. 如何缓解过拟合? 2. 如何衡量模型的复杂度? 3. 通过限制参数的选择范围来控制模型容量(复杂度) 4. 正则化如何让权重衰退? 5. 可视化地看看正则化是如何利用权重衰退来达到缓解 ...
- 推荐系统与深度学习(十四)——GBDT+LR模型原理
公众号后台回复"图书",了解更多号主新书内容 作者:livan 来源:数据python与算法 模型原理 与GBDT+LR模型结缘是刚开始学习推荐系统的过程中,FaceBook一推出 ...
- 系统学习深度学习(十四)--权重初始化Xavier
"Xavier"初始化方法是一种很有效的神经网络初始化方法,方法来源于2010年的一篇论文<Understanding the difficulty of training ...
- 推荐系统遇上深度学习(三十九)-推荐系统中召回策略演进!
推荐系统中的核心是从海量的商品库挑选合适商品最终展示给用户.由于商品库数量巨大,因此常见的推荐系统一般分为两个阶段,即召回阶段和排序阶段.召回阶段主要是从全量的商品库中得到用户可能感兴趣的一小部分候选 ...
- 花书+吴恩达深度学习(十二)卷积神经网络 CNN 之全连接层
目录 0. 前言 1. 全连接层(fully connected layer) 如果这篇文章对你有一点小小的帮助,请给个关注,点个赞喔~我会非常开心的~ 花书+吴恩达深度学习(十)卷积神经网络 CNN ...
- 花书+吴恩达深度学习(十)卷积神经网络 CNN 之卷积层
目录 0. 前言 1. 2D 图像卷积 2. 3D 图像卷积 3. 过滤器(核函数) 4. 过滤器应用于边缘检测 5. padding 填充 6. stride 步长 7. 使用卷积的动机 8. 1乘 ...
最新文章
- MSSQL2005 手工盲注详解
- Streams那些事之概述与原理
- Windows下的.NET+ Memcached安装
- ​来,一起搞AV,LiveVideoStack Meet再启动
- AQS源码阅读笔记(一)
- Andrew Ng 如何重拾梦想
- Spark-Mllib(二)基本统计
- 科普│串联质谱分析仪(ESI离子源)之离子阱质谱仪
- 图像文字识别:Python批量识别图片中的文字并自动改名
- 计算机无法识别机械硬盘,机械硬盘无法识别【面对办法】
- 修改linux主机名称
- 快讯分类_如何从Google快讯创建RSS Feed
- 基金公司以及各大平台发布的那些亮眼的收益率数据,是你个人真实的投资收益率吗
- LeetCode 第35题 搜索插入位置 做题记录
- 十二存单法 和 阶梯存款法
- java 调用 yed 绘制 流程图_用 yEd Graph Editor 绘制流程图(2)
- 童年往事,好看的电影,好听的音乐!
- Java 性能优化实战工具实践:如何获取代码性能数据?
- 【MySQL】压缩包方式 - Windows下载安装及简单配置
- 微信订阅号发送模板消息
热门文章
- 解决在 WP8/ WP8.1 项目中 引用 C++ 组件时出现的 System.TypeLoadException 错误
- Myeclipse字符集设置
- mysql 求平方_如何用MySQL求一个范围内的完全平方数
- [2018.10.15 T2] 字符串
- adobe字体_乱用字体赔2860万?
- python输出个数、给定一个n*n的矩阵m_简述Numpy
- 产品id 关联 分类id mysql_MySQL的多表联查
- 外部表改为内部表_2、从外部导入数据创建表(ACCESS图解操作系列)
- python画素描画_画画了,画画了,几行Python就成一幅素描画
- java cucumber_Cucumber框架入门篇