嘿!您似乎在 United States,您想使用我们的 English 网站吗?
Switch to English site
Skip to main content

Pytorch深度学习框架X NVIDIA JetsonNano应用-实作Pre-Trained Model

  1. 预训练模型 (Pre-Trained Model)、微调 (Fine-Tuning)、转移学习 (Transfer-Learning) 之间的差异
  2. 使用PyTorch进行Pre-Trained Model的Inference

Pre-Trained Model 预训练模型

当你在使用别人的Github的时候是不是常常看到「请下载 Pre-Trained Model」、「使用Pre-Trained Model运行」之类的话,就像下面这张图片,这张图是撷取自Jetson-Inference的Github:

1_jetson_github1_0329ad2d5d611de18d6944ee86b3825cf1ed217b.jpg

Pre-Trained Model顾名思义就是 Pre是事先、Trained是训练完的,所以中文通常都会翻译成「预训练模型」,那这个预训练模型就是只能使用在他当初训练好的数据集上,如果你想要利用他人的网络架构训练自己的模型的话,就叫做「Transfer Learning 」,也就是我们对YOLOv5做的事情。

Transfer Learning 迁移式学习

迁移式学习的部分,我们再来一下语文课,Transfer代表我将这个模型从那个数据集中转移到我这个数据集中去使用,Learning可以想象就是再次训练、再次学习的意思,所以迁移学习 ( 转移学习 ) 也就很好理解了,不过还有很重要的一个点是,Transfer Learning是基于模型在上一个数据集中训练好的权重,再进行训练!

Transfer Learning的方法:

进行迁移学习的方式有两种:

  1. 特征撷取 (Feature Extraction):冻结除了全连接层或者输出层以外的权重,只训练没冻结的即可。
  2. 微调 (Fine-tuning):不采用随机的权重初始,而是使用先前训练好的权重当作初始值,在这边可以全部都训练也可以只训练某一个部分,甚至说针对网络架构去做更动,也都算是Fine-tuning的范畴。

两种方式的本质都是相同的,就是站在巨人的肩膀上看世界,之前训练好的权重已经具有一定的特征撷取能力,那我们面临哪种状况又该使用哪种方式去做呢?做Transfer Learning最主要的原因通常是数据量小,当数据量小就容易发生Overfitting,所以才需要用别人先训练好的权重帮助我们避免特征撷取的时候发生Overfitting的现象;而当数据量大的时候,通常都会建议从头训练或是进行全网络的微调;而当数据量小的时候又有分与原本的数据集是否相似等的状况,这边也整理了一个表格方便大家做整理。

数据集数量

数据集相似

使用方法

备注

相似

特征撷取

由于内容相似,特征撷取的结果也会符合需求,所以直接训练线性分类的部分即可。

不相似

微调

这边就比较特殊了,因为数据集不相似所以CNN的特征撷取不一定适用,所以建议会从CNN的某一段就开始进行训练。

相似

微调

数据量大又相似,建议还是透过对整个网络微调会较佳

不相似

微调

通常遇到不相似又数据量大的,会直接建议从头训练 (learn from scratch),当然也是可以尝试进行全网络的微调,在大部分的状况下微调是能帮助神经网络快速收敛的。

补充:增量学习 (Incremental Learning)

还有另一个要厘清的点,当你的训练数据都是相同类别的而且不停地在更新,这时候用上的技术是叫做增量学习 ( incremental learning ),概念跟Transfer learning相似,但Transfer的定义比较像是你从A任务转成B任务后,模型就会忘记A任务分辨的技巧;Incremental则是当A任务转到B任务时他依旧记得A任务的技巧,又再学习了B任务的技巧,两者还是有差别的,别搞错啰!

PyTorch提供的Pre-Trained Model

PyTorch 等AI框架为了让使用者方便学习,本身的API当中都会包含着著名的几个神经网络模型可供大家使用,所以接下来我们就稍微对Pre-Trained Model进行介绍与实作。

在PyTorch的官方文件中可以找到 torchvision.model(如下图),而这是在介绍PyTorch供用户使用的网络模型,点击名称都会直接链接到该模型的论文,那接下来我将稍微针对几个比较有名的模型做介绍。

2_documentation1_4bbb56c74e75e30fac87d5c6a6f61b114bbd8d6b.jpg

AlexNet (2012)、VGG (2014)、GoogleNet (2014)

这三个都是相当相当有名的神经网络模型,都是在ImageNet这个影像辨识的比赛中获得名次,最明显的差异就是他们的隐藏层一个比一个还要多,AlexNet是2012年的第一名,而这是开启大AI时代的一个关键点,因为他们使用了GPU去做平行运算也使用了更深 (8层) 的卷积神经网络;接下来2014年ImageNet比赛中的第一名跟第二名分别是GoogleNet、VGG,那VGG的部分是探讨了深度对于神经网络影响,深度来到了19层,其实跟AlexNet很像但调整了卷积层的数量跟Kernel大小;而GoogleNet又采用更深的神经网络来到了22层,并且导入了1x1的卷积层 (降维)、Global Average Pooling (全局平均池化)、Auxiliary Classifiers (辅助分类器),这些技术一直到现在都十分受用。

ResNet ( 2015 )

随着网络层的深度增加,越来越长发生梯度消失的问题,所以ResNet就被研发出来了,他用的核心技术是残缺块 ( Residual Block ),经过实验证明确实能改善梯度消失的问题,透过跳接的概念,如果某一层发生梯度消失也不会连带影响其他的权重。

3_ResNet1_9499af9e2f4fc49ece20be6ae09681e4641933df.jpg

DenseNet ( 2016 )

DenseNet则是ResNet的进化版本,在DenseNet当中,每一层都会跟其余层做连接,看似会让权重变多,但实质则相反反而比传统的一对一连接还要少。

4_DenseNet1_7f9ea5013f866ceaca7564dad4ac728623e3f1f0.jpg

Inception v3 (2015)

这个模型的第一代就是GoogleNet,一直到今年已经有了第四代了,中间变化的过程有兴趣再自行研究啰~

Mobile Net v2 (2018)

Mobile Net是著名的轻量化网络,一直到现在各种边缘装置上都能见到他的身影,那第二代跟第一代的差异在于导入了Linear Bottleneck 和 Inverted Residual Blocks,我们今天的Pre-Trained Model也会利用Mobile Net来做实作。

使用Pre-Trained Model进行Inference

这里需要先导入 torchvision.models,刚刚上面的图片有提到有哪些可以模型可以导入,除此之外,在导入模型的同时你可以定义是否要用预训练好的模型还是只是单纯的模型架构:

import torchvision.models as models
target_model = models.mobilenet_v2( )   # 没导入预训练的权重
target_model = models.mobilenet_v2(pretrained=True)     # # 导入预训练的权重

在这边官方的文件中有说建议是以下列的参数作为正规化的参数:

normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])

那接下来就可以来写程序了,在主要执行的部分我写成透过命令行改变参数的方式,所以导入了argparse这个套件,并且将主要执行的程序写在run_pretrained_model的副函式中,其中我提供了7个常见的模型选择,然后自定义义要辨识的图片、还有需要导入模型对应的卷标文件:

if __name__=="__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('--model', default='mobilenet', help="resnet18, alexnet, vgg16, densenet, inception, googlenet")
    parser.add_argument('--image', default='image.jpg', help="Image Path")
    parser.add_argument('--label', default='imagenet_classes.txt', help="Label Path")
    args = parser.parse_args()
    run_pretrained_model(args)

主要执行的函式如下,第一步是导入模型、卷标,接着设定PyTorch提供的数据处理torchvistion.transforms,要注意的是它吃的是PIL格式,一开始我用OpenCV读图档,但当然可以一开始就用PIL,不过我提供给大家一个转换的范例,在Inference前记得要将模型的模式调成eval (验证模式),透过np.argmax找到最大的数值的位置,再将对应的Label显示出来:

def run_pretrained_model(args):
    
    ### 打印参数
    print_args(args)
    ### 导入预训练模型、卷标
    target_model = choose_model(args.model)
    ls_label = read_txt(args.label) 
    
### 先定义数据处理的部分
    transform = T.Compose([ T.Resize(256),
                            T.CenterCrop(224), 
                            T.ToTensor(),
                            T.Normalize(mean=[0.485, 0.456, 0.406], 
                                        std=[0.229, 0.224, 0.225])])
    ### 透过 OpenCV 导入图片
    path = args.image
    img = cv2.imread(f"{path}")
    
  ### PyTorch 吃PIL图
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img_pil = Image.fromarray(img)
    img_tensor = transform(img_pil).unsqueeze(0)    # 将数据加一维,仿真batch_size

    ### 推论 Infernece
    target_model.eval()
    predict = target_model(img_tensor).squeeze(0)
    
    ### 找到最大的值 find max value
    idx = np.argmax(predict.detach().numpy())
    print("\n\n辨识结果: {}".format(ls_label[idx]))

其余的程序代码分别是选择模型 ( choose_model )、读取txt檔 ( read_txt )、打印argparser的内容 ( print_args ):

def choose_model(n):

    trg_model = None
    if n=='resnet18':
        trg_model = models.resnet18(pretrained=True)
    elif n=='alexnet':
        trg_model = models.alexnet(pretrained=True)
    elif n=='vgg16':
        trg_model = models.vgg16(pretrained=True)
    elif n=='densenet':
        trg_model = models.vgg16(pretrained=True)
    elif n=='inception':
        trg_model = models.inception_v3(pretrained=True)
    elif n=='googlenet':
        trg_model = models.googlenet(pretrained=True)
    else:
        trg_model = models.mobilenet_v2(pretrained=True)

    return trg_model

def read_txt(path):

    f = open(path, 'r')
    total = f.readlines()
    print(f'classes number:{len(total)}')
    f.close()
    return total

def print_args(args):
    for arg in vars(args):
        print (arg, getattr(args, arg))

这次我使用我们家的猫咪来当测试照片:

5_mycat1_d560d5295205f5e4a7f8129a81628c941526dc13.jpg

执行成果如下,如果是第一次使用pre-trained model它会自动帮你下载下来,这个部分是GoogleNet的成果:

6_run_result_googlenet1_e392464307cc33a672e81d4c0436d0a7d6dc6a1f.jpg

可以看到结果为tabby cat,非常正确的是虎斑猫,那接下来来看一下还有什么模型可以选择:

7_help1_ab5b73568ec32d477bed15a2ec96520f3726f0fa.jpg

接着我们使用densenet来看看有没有辨识出不同的内容:

8_dense_net1_6ee9ecaca86bf0572007eb9aa3d8accf84f7440e.jpg

结果是都是相同的,其实这几个都是各个年度的佼佼者,准确度都是吓吓叫,丢一些简单的猫狗要难倒他们真的是不太可能~

结语

这次介绍Pre-Trained Model、Transfer Learning的差别,大家有没有更加了解呢?如果有相关的问题欢迎留言提问,文章的结果也带大家实作PyTorch提供的Pre-Trained Model,应用上也是非常的简单,大家可以多方尝试看看。

相关文章

CS231n: Convolutional Neural Networks for Visual Recognition

CAVEDU Education is devoted into robotics education and maker movement since 2008, and is intensively active in teaching fundamental knowledge and skills. We had published many books for readers in all ages, topics including Deep Learning, edge computing, App Inventor, IoT and robotics. Please check CAVEDU's website for more information: http://www.cavedu.com, http://www.appinventor.tw