circle loss代码实现_TensorFlow2.0 从零实现YoloV3检测网络之TF2.0训练MobileNetV3

3a769935e2cb522db1bc8772720147ba.gif

在正式开始教程之前,需要强调一下,这不仅仅是一篇教你从零实现一个yolov3检测器的教程,同时也是一个最新最详尽比较权威中肯的TensorFlow2.0教程(我们会包含从dataloader到基础keras api网络搭建的所有过程). 同时欢迎大家来我们的论坛探讨AI问题:

奇点AI社区​talk.strangeai.pro
06e0eeff111032d7e2c9cae0dc1b0d29.png

同时也欢迎大家支持一下我们做的AI市场平台,海量AI算法开箱即用:

神力AI(MANA)-国内最大的AI代码平台​manaai.cn
0bc7749e060ee567fb4209a3ab9515fa.png

接着上一讲,继续为您说. 最近关于MobileNetV3的论文可以说是非常的非常的火热,可见大家对于移动端或者高性能的基础网络架构是多么的关注。那么,秉着最快最全最新最有卵用的原则,本篇教程进行上一个节奏,继续为大家更新。上回我们说道要做一个检测器,从0开始,我们已经经过dataloader部分,也就是从coco转tfrecord,再用tfrecord统一作为我们训练的数据导入器,但是还没有涉及到tensorflow2.0的网络api部分,这篇将给大家讲述这些内容:

  • 从零编写MobileNetV3;
  • 不仅要编写,还要用一个小的数据集来训练它;
  • 不仅要训练它,我们还要测试它相对比MobileNetV2在实际场景下速度的提升。

综上,我们这篇将把主线程稍微的偏离一下,来完成这个现任务先,一来让大家tensorflow2.0 api有所熟悉,为后面做准备,二来给大家用实际的例子 展示一下MobilenetV3到底有多牛逼。闲话不多说,先上代码。

TF2.0编写MobilenetV3网络

关于TF2.0的MobileNetV3我也是参考了一些网络的实现,但是有一点是肯定的,那就是网上没有一个真正从实现到训练的实现,能正确把网络实现的都比较少,纸上得来终觉浅绝知此事要躬行呀!既然如此,本教程就来完成这个光荣而艰巨的使命吧。在编写MobileNetV3的网络之前,我得向大家传授一下在TF2.0中,神经网络的标准写法(当然,如果你觉得标准应该属于Google,那可以称之为M式写法),在tf2.0里面,大家千万不要瞎几把乱写,都有套路的。 简单来说,一个标准的Model应该这样写:

import tensorflow as tf

class MobileNetV3Small(tf.keras.Model):
    def __init__(
            self,
            classes: int=1001,
            width_multiplier: float=1.0,
            scope: str="MobileNetV3",
            divisible_by: int=8,
    ):
        super(MobileNetV3Small, self).__init__(name=scope)
    def call(self, input, training=False):
        x = self.yourlayer(input)
        return x

核心在于,集成 tf.keras.Model 这个类,这样你就可以访问所有Model的方法,比如 save_weights(),load_weights()等,而这些都是在tf内部自动完成的。所有从这个角度来说,tensorflow2.0真的简化了很多操作,并且和pytorch的易用性比肩. 关于这个网络详细的代码我不贴了,太占篇幅,可以直接从我的repo:http://github.com/jinfagang/tfboys 里面找到相关代码,同时也包含了pytorch的实现.

使用flowers训练MobileNetV3

本教程的精华部分来了,上一讲大概说了下如何导入数据,但是请相信我,真正麻烦的应该是把数据和网络结合在一起,具体来说你要做 这些事情来训练一个网络:

  • 拿到数据,也就是tf.data.Dataset;
  • 编写你的网络,也就是得到一个 tf.keras.Model;
  • 开始你的optimizer,你的lossfunction,你的accuracymetric;
  • 编写一个循环开始训练。

OK,有了思路就好办了,还记得我在上一篇说的三个网路编写原则吗?

  • 你要处理所有情况,包括断点续训、ctrl+c保存权重等;
  • 你要打印出清晰的loss信息;

看起来比较复杂,不过好消息是,我们有了tf2.0!!所有的东西你可以选择简单也可以选择复杂。我们先来看看简单的:

 model = tf.keras.applications.MobileNetV2(input_shape=(target_size, target_size, 3), weights=None, include_top=True, classes=5)
   
# todo: why keras fit converge faster than tf loop?
model.compile(
    optimizer='adam',
    loss='sparse_categorical_crossentropy',
    metrics=['accuracy'])
try:
    model.fit(
    train_dataset, epochs=50,             
    steps_per_epoch=700,)
except KeyboardInterrupt:
    model.save_weights(ckpt_path.format(epoch=0))
    logging.info('keras model saved.')
model.save_weights(ckpt_path.format(epoch=0))
model.save(os.path.join(os.path.dirname(ckpt_path), 'flowers_mobilenetv2.h5'))

就这几行代码,你就可以实现一个分类网络训练的pipeline了。并且可以在你想停止的时候及时的保存权重。

但是你可能会觉得,我还是想自己写循环,来控制每一个epoch的具体运算和操作。没有问题,同样的简单:

loss_fn = tf.losses.SparseCategoricalCrossentropy()
        optimizer = tf.optimizers.RMSprop()

train_loss = tf.metrics.Mean(name='train_loss')
# the accuracy calculation has some problems, seems not right?
train_accuracy = tf.metrics.SparseCategoricalAccuracy(name='train_accuracy')

for epoch in range(start_epoch, 120):
    try:
        for batch, data in enumerate(train_dataset):
            # images, labels = data['image'], data['label']
            images, labels = data
            with tf.GradientTape() as tape:
                predictions = model(images)
                loss = loss_fn(labels, predictions)
            gradients = tape.gradient(loss, model.trainable_variables)
            optimizer.apply_gradients(zip(gradients, model.trainable_variables))
            train_loss(loss)
            train_accuracy(labels, predictions)
            if batch % 10 == 0:
                logging.info('Epoch: {}, iter: {}, loss: {}, train_acc: {}'.format(
                epoch, batch, train_loss.result(), train_accuracy.result()))
    except KeyboardInterrupt:
        logging.info('interrupted.')
        model.save_weights(ckpt_path.format(epoch=epoch))
        logging.info('model saved into: {}'.format(ckpt_path.format(epoch=epoch)))
        exit(0)

看,这边是TF2.0的核心要义所在。你既可以使用kera的model.fit也可以自己写训练来计算每一个epoch。

下面着重讲解一下tf2.0的训练具体编写不走,别看这几行代码简单,但是实际上跟以往有很大的不同。

  1. 首先上来你得准备你好loss function,optimizer,这一点和pytorch很像啊,几乎就是copy啊:
loss_fn = tf.losses.SparseCategoricalCrossentropy()
        optimizer = tf.optimizers.RMSprop()
train_loss = tf.metrics.Mean(name='train_loss')

这里的loss_fn就是你的loss计算方式,我们这里采用交叉商,因为是多类别分类问题。后面我们对yolov3进行loss计算就得自己定义loss了。 2. 拿到网络的梯度用tf.GradientTape(). 这个操作其实就是把网络前向,然后框架自动求导去更新权重,Tape可以理解为梯度的胶带吧,每一个输出的量都胶了一点梯度:

with tf.GradientTape() as tape:
    predictions = model(images)
    loss = loss_fn(labels, predictions)

上面三行代码基本就是就是核心了。反向传播以前很复杂,现在?就三行核心代码。请注意,这里就是核心,整片教程其实就这三行代码

还记得上一张讲的 tensorflow_datasets这个库吗?现在可以用了,我们刚好用它来load一个花的数据集。下面我们将用100行代码实现一个TF2.0的分类训练代码.

from alfred.dl.tf.common import mute_tf
mute_tf()
import tensorflow as tf
import tensorflow_datasets as tfds
import numpy as np

from alfred.utils.log import logger as logging
import os
import sys

"""
A Simple training pipeline on TensorFlow 2.0 API
with about 60 epochs, model achieves 90% accuracy (training from scratch)
Epoch 1/50
1700/1700 [==============================] - 74s 106ms/step - loss: 0.7693 - accuracy: 0.7104
...
Epoch 44/50
1700/1700 [==============================] - 46s 66ms/step - loss: 0.2280 - accuracy: 0.9182
"""


target_size = 224
use_keras_fit = False
# use_keras_fit = True
ckpt_path = './checkpoints/no_finetune/flowers_mbv2_scratch-{epoch}.ckpt'


def preprocess(x):
    """
    minus mean pixel or normalize?
    """
    x['image'] = tf.image.resize(x['image'], (target_size, target_size))
    x['image'] /= 255.
    x['image'] = 2*x['image'] - 1
    return x['image'], x['label']

def train():
    # using mobilenetv2 classify tf_flowers dataset
    dataset, _ = tfds.load('tf_flowers', with_info=True)
    train_dataset = dataset['train']
    train_dataset = train_dataset.shuffle(100).map(preprocess).batch(4).repeat()

    # init model
    model = tf.keras.applications.MobileNetV2(input_shape=(target_size, target_size, 3), weights=None, include_top=True, classes=5)
    # model.summary()
    # model = tf.keras.models.load_model('flowers_mobilenetv2.h5')
    logging.info('model loaded.')
    
    start_epoch = 0
    latest_ckpt = tf.train.latest_checkpoint(os.path.dirname(ckpt_path))
    if latest_ckpt:
        start_epoch = int(latest_ckpt.split('-')[1].split('.')[0])
        model.load_weights(latest_ckpt)
        logging.info('model resumed from: {}, start at epoch: {}'.format(latest_ckpt, start_epoch))
    else:
        logging.info('passing resume since weights not there. training from scratch')

    if use_keras_fit:
        # todo: why keras fit converge faster than tf loop?
        model.compile(
            optimizer='adam',
            loss='sparse_categorical_crossentropy',
            metrics=['accuracy'])
        try:
            model.fit(
            train_dataset, epochs=50,             
            steps_per_epoch=700,)
        except KeyboardInterrupt:
            model.save_weights(ckpt_path.format(epoch=0))
            logging.info('keras model saved.')
        model.save_weights(ckpt_path.format(epoch=0))
        model.save(os.path.join(os.path.dirname(ckpt_path), 'flowers_mobilenetv2.h5'))
    else:
        loss_fn = tf.losses.SparseCategoricalCrossentropy()
        optimizer = tf.optimizers.RMSprop()

        train_loss = tf.metrics.Mean(name='train_loss')
        # the accuracy calculation has some problems, seems not right?
        train_accuracy = tf.metrics.SparseCategoricalAccuracy(name='train_accuracy')

        for epoch in range(start_epoch, 120):
            try:
                for batch, data in enumerate(train_dataset):
                    # images, labels = data['image'], data['label']
                    images, labels = data
                    with tf.GradientTape() as tape:
                        predictions = model(images)
                        loss = loss_fn(labels, predictions)
                    gradients = tape.gradient(loss, model.trainable_variables)
                    optimizer.apply_gradients(zip(gradients, model.trainable_variables))
                    train_loss(loss)
                    train_accuracy(labels, predictions)
                    if batch % 10 == 0:
                        logging.info('Epoch: {}, iter: {}, loss: {}, train_acc: {}'.format(
                        epoch, batch, train_loss.result(), train_accuracy.result()))
            except KeyboardInterrupt:
                logging.info('interrupted.')
                model.save_weights(ckpt_path.format(epoch=epoch))
                logging.info('model saved into: {}'.format(ckpt_path.format(epoch=epoch)))
                exit(0)



if __name__ == "__main__":
    train()

如果你找不到alfred, 通过 sudo pip3 install alfred-py进行安装。这个库得安利一下:

from alfred.dl.tf.common import mute_tf
mute_tf()

两行代码,自动屏蔽tensorflow烦人的log。其实很简单,但是很方便。这个库还有很多一些功能,可以去github star一下:

http://github.com/jinfagang/alfred​github.com

小结

在我的repo里面,有训练MobileNetV3的脚本,跟MobileNetV2差不多,我们在这篇博客里面学习了如何从零编写网络,使用tf2.0 api来训练一个图片分类器。 下一篇,我们将继续从0实现YoloV3 in TensorFlow2.0。 并且我会更新一些MobileNetV3和MobileNetV2的实际使用速度对比。(仓库模型我会随时更新,大家可以pull)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值