查看原文
其他

【paddlepaddle速成】paddlepaddle图像分类从模型自定义到测试

言有三 有三AI 2019-12-26

这是给大家准备的paddlepaddle与visualdl速成例子

言有三


毕业于中国科学院,计算机视觉方向从业者,有三工作室等创始人

作者 | 言有三(微信号Longlongtogo)

编辑 | 言有三


这一次我们讲讲paddlepadle这个百度开源的机器学习框架,一个图像分类任务从训练到测试出结果的全流程。


将涉及到paddlepaddle和visualdl,git如下:https://github.com/PaddlePaddle


相关的代码、数据都在我们 Git 上,希望大家 Follow 一下这个 Git 项目,后面会持续更新不同框架下的任务。


https://github.com/longpeng2008/LongPeng_ML_Course

01


paddlepaddle是什么

正所谓google有tensorflow,facebook有pytorch,amazon有mxnet,作为国内机器学习的先驱,百度也有PaddlePaddle,其中Paddle即Parallel Distributed Deep Learning(并行分布式深度学习),整体使用起来与tensorflow非常类似。

sudo pip install paddlepaddle

安装就是一条命令,话不多说上代码。

02


paddlepaddle训练

训练包括三部分,数据的定义,网络的定义,以及可视化和模型的存储。

2.1 数据定义

定义一个图像分类任务的dataset如下:

from multiprocessing import cpu_count

import paddle.v2 as paddle


class Dataset:

    def __init__(self,cropsize,resizesize):

        self.cropsize = cropsize

        self.resizesize = resizesize


    def train_mapper(self,sample):

        img, label = sample

        img = paddle.image.load_image(img)

        img = paddle.image.simple_transform(img, self.resizesize, self.cropsize, True)

        #print "train_mapper",img.shape,label

        return img.flatten().astype('float32'), label


    def test_mapper(self,sample):

        img, label = sample

        img = paddle.image.load_image(img)

        img = paddle.image.simple_transform(img, self.resizesize, self.cropsize, False)

        #print "test_mapper",img.shape,label

        return img.flatten().astype('float32'), label


    def train_reader(self,train_list, buffered_size=1024):

        def reader():

            with open(train_list, 'r') as f:

                lines = [line.strip() for line in f.readlines()]

                print "len of train dataset=",len(lines)

                for line in lines:

                    img_path, lab = line.strip().split(' ')

                    yield img_path, int(lab)


        return paddle.reader.xmap_readers(self.train_mapper, reader,

                                          cpu_count(), buffered_size)


    def test_reader(self,test_list, buffered_size=1024):

        def reader():

            with open(test_list, 'r') as f:

                lines = [line.strip() for line in f.readlines()]

                print "len of val dataset=",len(lines)

                for line in lines:

                    img_path, lab = line.strip().split(' ')

                    yield img_path, int(lab)


        return paddle.reader.xmap_readers(self.test_mapper, reader,

                                          cpu_count(), buffered_size)


从上面代码可以看出:

(1) 使用了paddle.image.load_image进行图片的读取,
paddle.image.simple_transform进行了简单的图像变换,这里只有图像crop操作,更多的使用可以参考API。

(2)  使用了paddle.reader.xmap_readers进行数据的映射。

2.2 网络定义

# coding=utf-8
import paddle.fluid as fluid
def simplenet(input):
   # 定义卷积块
   conv1 = fluid.layers.conv2d(input=input, num_filters=12,stride=2,padding=1,filter_size=3,act="relu")
   bn1 = fluid.layers.batch_norm(input=conv1)
   conv2 = fluid.layers.conv2d(input=bn1, num_filters=12,stride=2,padding=1,filter_size=3,act="relu")
   bn2 = fluid.layers.batch_norm(input=conv2)
   conv3 = fluid.layers.conv2d(input=bn2, num_filters=12,stride=2,padding=1,filter_size=3,act="relu")
   bn3 = fluid.layers.batch_norm(input=conv3)
   fc1 = fluid.layers.fc(input=bn3, size=128, act=None)
   return fc1,conv1

与之前的caffe,pytorch,tensorflow框架一样,定义了一个3层卷积与2层全连接的网络。为了能够更好的进行可视化,我们使用了PaddlePaddle Fluid,Fluid的设计也是用来让用户像Pytorch和Tensorflow Eager Execution一样可以执行动态计算而不需要创建图。

2.3可视化

paddlepaddle有与之配套使用的可视化框架,即visualdl。

visualdl是百度数据可视化实验室发布的深度学习可视化平台,它的定位与tensorboard很像,可视化内容包含了向量,参数直方图分布,模型结构,图像等功能,以后我们会详细给大家讲述,这次直接在代码中展示如何使用。


安装使用pip install --upgrade visualdl,使用下面的命令可以查看官方例子:

vdl_create_scratch_log

visualDL --logdir ./scratch_log --port 8080

http://127.0.0.1:8080


下面是loss和直方图的查看

在咱们项目中,具体使用方法如下

# 首先定义相关变量
# 创建VisualDL,并指定log存储路径
logdir = "./logs"
logwriter = LogWriter(logdir, sync_cycle=10)

# 创建loss的趋势图
with logwriter.mode("train") as writer:
   loss_scalar = writer.scalar("loss")

# 创建acc的趋势图
with logwriter.mode("train") as writer:
   acc_scalar = writer.scalar("acc")

# 定义输出频率
num_samples = 4
# 创建卷积层和输出图像的图形化展示
with logwriter.mode("train") as writer:
   conv_image = writer.image("conv_image", num_samples, 1)
   input_image = writer.image("input_image", num_samples, 1)

# 创建可视化的训练模型结构
with logwriter.mode("train") as writer:
   param1_histgram = writer.histogram("param1", 100)

然后在训练过程中进行记录,这是完整的训练代码,红色部分就是记录结果。


# coding=utf-8
import numpy as np
import os
import paddle.fluid as fluid
import paddle.fluid.framework as framework
import paddle.v2 as paddle
from paddle.fluid.initializer import NormalInitializer
from paddle.fluid.param_attr import ParamAttr
from visualdl import LogWriter
from dataset import Dataset
from net_fluid import simplenet

# 创建VisualDL,并指定当前该项目的VisualDL的路径
logdir = "./logs"
logwriter = LogWriter(logdir, sync_cycle=10)

# 创建loss的趋势图
with logwriter.mode("train") as writer:
   loss_scalar = writer.scalar("loss")

# 创建acc的趋势图
with logwriter.mode("train") as writer:
   acc_scalar = writer.scalar("acc")

# 定义输出频率
num_samples = 4
# 创建卷积层和输出图像的图形化展示
with logwriter.mode("train") as writer:
   conv_image = writer.image("conv_image", num_samples, 1)
   input_image = writer.image("input_image", num_samples, 1)

# 创建可视化的训练模型结构
with logwriter.mode("train") as writer:
   param1_histgram = writer.histogram("param1", 100)

def train(use_cuda, learning_rate, num_passes, BATCH_SIZE=128):
   class_dim = 2
   image_shape = [3, 48, 48]
   image = fluid.layers.data(name='image', shape=image_shape, dtype='float32')
   label = fluid.layers.data(name='label', shape=[1], dtype='int64')

   net, conv1 = simplenet(image)
   # 获取全连接输出
   predict = fluid.layers.fc(
       input=net,
       size=class_dim,
       act='softmax',
       param_attr=ParamAttr(name="param1", initializer=NormalInitializer()))

   # 获取损失
   cost = fluid.layers.cross_entropy(input=predict, label=label)
   avg_cost = fluid.layers.mean(x=cost)

   # 计算batch,从而来求平均的准确率
   batch_size = fluid.layers.create_tensor(dtype='int64')
   print "batchsize=",batch_size
   batch_acc = fluid.layers.accuracy(input=predict, label=label, total=batch_size)

   # 定义优化方法
   optimizer = fluid.optimizer.Momentum(
       learning_rate=learning_rate,
       momentum=0.9,
       regularization=fluid.regularizer.L2Decay(5 * 1e-5))

   opts = optimizer.minimize(avg_cost)

   # 是否使用GPU
   place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()
   # 创建调试器
   exe = fluid.Executor(place)
   # 初始化调试器
   exe.run(fluid.default_startup_program())
   # 保存结果
   model_save_dir = "./models"

   # 获取训练数据
   resizesize = 60
   cropsize = 48
   mydata = Dataset(cropsize=cropsize,resizesize=resizesize)
   mydatareader = mydata.train_reader(train_list='./all_shuffle_train.txt')
   train_reader = paddle.batch(reader=paddle.reader.shuffle(reader=mydatareader,buf_size=50000),batch_size=128)
   
   # 指定数据和label的对应关系
   feeder = fluid.DataFeeder(place=place, feed_list=[image, label])

   step = 0
   sample_num = 0
   start_up_program = framework.default_startup_program()
   param1_var = start_up_program.global_block().var("param1")

   accuracy = fluid.average.WeightedAverage()
   # 开始训练,使用循环的方式来指定训多少个Pass
   for pass_id in range(num_passes):
       # 从训练数据中按照一个个batch来读取数据
       accuracy.reset()
       for batch_id, data in enumerate(train_reader()):
           loss, conv1_out, param1, acc, weight = exe.run(fluid.default_main_program(),
                                                          feed=feeder.feed(data),
                                                          fetch_list=[avg_cost, conv1, param1_var, batch_acc,batch_size])
           accuracy.add(value=acc, weight=weight)
           pass_acc = accuracy.eval()

           # 重新启动图形化展示组件
           if sample_num == 0:
               input_image.start_sampling()
               conv_image.start_sampling()
           # 获取taken
           idx1 = input_image.is_sample_taken()
           idx2 = conv_image.is_sample_taken()
           # 保证它们的taken是一样的
           assert idx1 == idx2
           idx = idx1
           if idx != -1:
               # 加载输入图像的数据数据
               image_data = data[0][0]
               input_image_data = np.transpose(
                   image_data.reshape(image_shape), axes=[1, 2, 0])
               input_image.set_sample(idx, input_image_data.shape,
                                      input_image_data.flatten())
               # 加载卷积数据
               conv_image_data = conv1_out[0][0]
               conv_image.set_sample(idx, conv_image_data.shape,
                                     conv_image_data.flatten())
               # 完成输出一次
               sample_num += 1
               if sample_num % num_samples == 0:
                   input_image.finish_sampling()
                   conv_image.finish_sampling()
                   sample_num = 0

           # 加载趋势图的数据
           loss_scalar.add_record(step, loss)
           acc_scalar.add_record(step, acc)
           # 添加模型结构数据
           param1_histgram.add_record(step, param1.flatten())

          

           # 输出训练日志
           print("loss:" + str(loss) + " acc:" + str(acc) + " pass_acc:" + str(pass_acc))
           step += 1
           model_path = os.path.join(model_save_dir,str(pass_id))
           if not os.path.exists(model_save_dir):
               os.mkdir(model_save_dir)
           fluid.io.save_inference_model(model_path,['image'],[predict],exe)


if __name__ == '__main__':
   # 开始训练
   train(use_cuda=False, learning_rate=0.005, num_passes=300)

2.4训练结果

看看acc和loss的曲线,可见已经收敛

03


paddlepaddle测试

训练的时候使用了fluid,测试的时候也需要定义调试器,加载训练好的模型,完整的代码如下

# encoding:utf-8
import sys
import numpy as np
import paddle.v2 as paddle
from PIL import Image
import os
import cv2
# coding=utf-8
import numpy as np
import paddle.fluid as fluid
import paddle.fluid.framework as framework
import paddle.v2 as paddle
from paddle.fluid.initializer import NormalInitializer
from paddle.fluid.param_attr import ParamAttr
from visualdl import LogWriter
from net_fluid import simplenet

if __name__ == "__main__":
   # 开始预测
   type_size = 2
   testsize = 48

   imagedir = sys.argv[1]
   images = os.listdir(imagedir)
       

   # 定义调试器
   save_dirname = "./models/299"
   exe = fluid.Executor(fluid.CPUPlace())
   inference_scope = fluid.core.Scope()
   with fluid.scope_guard(inference_scope):
   # 加载模型

 [inference_program,feed_target_names,fetch_targets] = fluid.io.load_inference_model(save_dirname,exe)

       predicts = np.zeros((type_size,1))
       for image in images:
           imagepath = os.path.join(imagedir,image)
           img = paddle.image.load_image(imagepath)
           img = paddle.image.simple_transform(img,testsize,testsize,False)
           img = img[np.newaxis,:]

           #print img.shape

           results = np.argsort(-exe.run(inference_program,feed={feed_target_names[0]:img},
                   fetch_list=fetch_targets)[0])
           label = results[0][0]
           predicts[label] += 1
   
   print predicts

由于所有框架的测试流程都差不多,所以就不对每一部分进行解释了,大家可以自行去看代码。


更多,欢迎到知乎专栏去投稿与交流,配套资料将放出在github,可扫描二维码进入。


打一个小广告,我的计算机视觉公开课《AI 图像识别项目从入门到上线》上线了,将讲述从零基础到完成一个实际的项目到微信小程序上线的整个流程,欢迎交流捧场。





如果想加入我们,后台留言吧

微信

Longlongtogo

公众号内容

1 图像基础|2 深度学习|3 行业信息

往期精选

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存