乐趣区

关于深度学习:WomanmancameraTV如何做一个完整的深度学习应用

作者:LeanCloud 江宏

前段时间 Trump 的这个采访成为社交媒体焦点的时候,我正好在温习一些 neural network 的资料,于是想到能够用一些新的开源工具做一个辨认 woman、man、camera、TV 的残缺利用试试。这个例子足够小,能够在很短时间实现,很适宜用来阐明如何做一个残缺的深度学习利用。实现的利用部署在 https://trump-sim.jishuq.com(LeanCloud 的一个云引擎实例上)。

做这个利用分为三步:先用一些图片实现模型的训练,而后把模型导出,做一个后端的 API 用来辨认图片,再做一个前端用来上传图片和显示后果。

筹备训练数据

Jupyter notebook 是个很风行的用来做数据分析和机器学习的交互式环境,它能够把 Markdown 文档和 Python 代码放在一个笔记本里,也能够以图表、图片等敌对的形式显示代码的运行后果。这里也会用到 FastAI,它是一个基于 PyTorch,提供了很多网络和文件批量操作便捷接口的开源库。这篇文章就是在 Jupyter notebook 里写的,所以你能够间接 clone 这个 repo、装置依赖、启动 Jupyter notebook。

git clone https://github.com/hjiang/trump-sim-notebook
pip install -r requirements.txt
jupyter notebook

咱们还会用到 Bing image search API 来获取做训练的图片,你须要本人注册并申请一个收费的 API KEY。当然,因为搜寻到的图片是在很多第三方网站上的,所以你须要能无障碍地拜访中国之外的网站。????‍♂️

把你的 Bing image search API key 放在我的项目目录下的 .env 里,免得在代码里泄露进来:

BING_SEARCH_API_KEY=XXXXXXXX....

而后在 Python 里读进来

import os
from dotenv import load_dotenv
load_dotenv()
key = os.getenv('BING_SEARCH_API_KEY')

写一个函数用来搜寻图片:

from azure.cognitiveservices.search.imagesearch import ImageSearchClient
from msrest.authentication import CognitiveServicesCredentials
from fastcore.foundation import L

def search_images_bing(key, term, min_sz=128):
    client = ImageSearchClient('https://api.cognitive.microsoft.com', CognitiveServicesCredentials(key))
    return L(client.images.search(query=term, count=150, min_height=min_sz, min_width=min_sz).value)

理论验证一下, 搜一张 Artemis 的图片:

from torchvision.datasets.utils import download_url
from PIL import Image
import fastai2.vision.widgets

results = search_images_bing(key, 'Artemis')
urls = results.attrgot('content_url')
download_url(urls[0], 'images/', 'artemis.jpg')
image = Image.open('images/artemis.jpg')
image.to_thumb(128, 128)

确认图片下载没问题后,咱们把关怀的四类图片下载到 /objects 上面的四个目录里。

from fastai2.vision.utils import download_images
from pathlib import Path

object_types = 'woman','man','camera', 'TV'
path = Path('objects')

if not path.exists():
    path.mkdir()
    for o in object_types:
        dest = (path/o)
        dest.mkdir(exist_ok=True)
        results = search_images_bing(key, o)
        download_images(dest, urls=results.attrgot('content_url'))

你可能会看到一些图片下载失败的信息,只有不是太多都能够疏忽。网络上有的图片是损坏的,或者是 Python image library 不反对的格局,须要把它们删除。

from fastai2.vision.utils import get_image_files
from fastai2.vision.utils import verify_images

fns = get_image_files(path)
failed = verify_images(fns)
failed.map(Path.unlink);

预处理

在开始训练前,须要通知 FastAI 如何标注图片,并加载到它的数据结构中。上面的代码实现以下几件事:

  • 应用父目录名(parent_label)来标注每个图片。
  • 保留 20% 的图片作为验证集(validation set),其它的作为训练集(training set)。训练集就是用来训练神经网络的数据,验证集用于掂量训练好的模型在遇到新数据时的准确度。这两个汇合不能有重叠。
  • 把图片放大以提高效率

最初一行代码会显示验证集的前三个图片。

from fastai2.data.block import DataBlock, CategoryBlock
from fastai2.vision.data import ImageBlock
from fastai2.data.transforms import RandomSplitter, parent_label
from fastai2.vision.augment import Resize

objects = DataBlock(blocks=(ImageBlock, CategoryBlock),
    get_items=get_image_files,
    splitter=RandomSplitter(valid_pct=0.2, seed=42),
    get_y=parent_label,
    item_tfms=Resize(128))

dls = objects.dataloaders(path)
dls.valid.show_batch(max_n=3, nrows=1)

在做图像识别的时候往往还会对图片做一些随机的缩放、裁剪等变换,以便产生足够多的数据来进步训练成果。能够从上面代码的后果看到对同一个图片做不同变换的后果。

from fastai2.vision.augment import aug_transforms, RandomResizedCrop

objects = objects.new(item_tfms=RandomResizedCrop(224, min_scale=0.5),
    batch_tfms=aug_transforms())
dls = objects.dataloaders(path)
dls.train.show_batch(max_n=6, nrows=2, unique=True)

训练数据

接下来终于能够开始训练了。对于图像识别这样的利用场景来说,往往不会从零开始训练一个新的模型,因为有大量的特色是简直所有利用都须要辨认的,比方物体的边缘、暗影、不同色彩造成的模式等。通常的做法是以一个事后训练好的模型为根底(比方这里的 resnet18),用本人的新数据对最初几层进行训练(术语为 fine tune)。在一个多层的神经网络里,越靠前(凑近输出)的层负责辨认的特色越具体,而越靠后的层辨认的特色越形象、越靠近目标。上面的最初一行代码指训练 4 轮(epoch)。

如果你有 Nvidia 的显卡,在 Linux 下,并且装置了适合的驱动程序的话,上面的代码只须要几秒到十几秒,否则的话就要期待几分钟了。

from fastai2.vision.learner import cnn_learner
from torchvision.models.resnet import resnet18
from fastai2.metrics import error_rate
import fastai2.vision.all as fa_vision

learner = cnn_learner(dls, resnet18, metrics=error_rate)
learner.fine_tune(4)
epoch train_loss valid_loss error_rate time
0 1.928001 0.602853 0.163793 01:16
epoch train_loss valid_loss error_rate time
0 0.550757 0.411835 0.120690 01:42
1 0.463925 0.363945 0.103448 01:46
2 0.372551 0.336122 0.094828 01:44
3 0.314597 0.321349 0.094828 01:44

最初输入的表格里是每一轮里训练集的 loss,验证集的 loss,以及错误率(error rate)。错误率是咱们关怀的指标,而 loss 是管制训练过程的指标(训练的指标就是让 loss 越来越靠近于 0)。须要这两个不同的指标是因为 loss 要满足一些错误率不肯定满足的条件,比方对所有参数可导,而错误率不是一个连续函数。loss 越低错误率也越低,但他们之间没有线性关系。这里错误率有差不多 10%,也就是准确率是 90% 左右。

接下来咱们要看看验证集里到底有哪些图片辨认错了,上面的代码会打印出 confusion matrix。在这个矩阵里,对角线的数字是正确辨认的图片数,其它中央的是辨认谬误的图片数。

from fastai2.interpret import ClassificationInterpretation

interp = ClassificationInterpretation.from_learner(learner)
interp.plot_confusion_matrix()

从输入的矩阵能够看到一共有 11 个谬误,其中男女性别谬误有 4 个,此外电视和其它几类的混同也很多。????

上面咱们把 loss 最高的图片显示进去看看具体有什么问题。

interp.plot_top_losses(12, nrows=4)

输入的后果反映出了从互联网上抓来的数据存在的典型问题:噪声太多。比方电视的搜寻后果里有电视遥控器、电视盒子、电视剧海报,还有一些是齐全无关的后果。

FastAI 提供了一个 cleaner 能够帮忙咱们对比拟小的数据集做手动荡涤。它能够把整个数据集中 loss 最高的图片列出来让用户能够手动批改标签或者删除。

from fastai2.vision.widgets import ImageClassifierCleaner

cleaner = ImageClassifierCleaner(learner)
cleaner

留神 cleaner 只是做标记,你须要用 Python 代码来做理论解决。我通常就间接把有问题的图片标记为 delete 而后删除。

for idx in cleaner.delete(): cleaner.fns[idx].unlink()

清理完之后反复训练的过程。

objects = DataBlock(blocks=(ImageBlock, CategoryBlock),
    get_items=get_image_files,
    splitter=RandomSplitter(valid_pct=0.2, seed=42),
    get_y=parent_label,
    item_tfms=Resize(128))

objects = objects.new(item_tfms=RandomResizedCrop(224, min_scale=0.5),
    batch_tfms=aug_transforms())
dls = objects.dataloaders(path)
learner = cnn_learner(dls, resnet18, metrics=error_rate)
learner.fine_tune(3)
epoch train_loss valid_loss error_rate time
0 1.663555 0.510397 0.201835 01:11
epoch train_loss valid_loss error_rate time
0 0.458212 0.226866 0.091743 01:32
1 0.358364 0.145286 0.036697 01:31
2 0.281517 0.146477 0.036697 01:32

如果你留神到 error_rate 在前面的 epoch 有回升的话,能够升高 fine_tune 的参数以达到最好的成果。因为如果训练轮数过多,模型会对训练集 over fit,在遇到新数据时错误率会变高。从下面的输入能够看到准确率进步到了 96% 以上。

达到称心的准确率后就能够把模型导出用到线上了。上面这行代码会把模型保留到 export.pkl

learner.export()

后端 API

后端 API 是这个我的项目最简略的一部分,只有一个 endpoint。加载后面导出的模型,收到新图片时用模型来预测分类就能够。

trump = load_learner('model.pkl')

@app.route('/api/1.0/classify-image', methods=['POST'])
def classify():
    image = request.files['image']
    res = trump.predict(image.read())
    response = jsonify({'result': res[0]})
    response.status_code = 200
    return response

残缺的代码在 GitHub 上。依照文档部署到 LeanCloud 云引擎就行。

前端网站

前端也比较简单,只须要一个页面让用户上传照片,在浏览器里把照片放大而后发送给后端 API 就能够。残缺的 React 我的项目在 GitHub,次要的代码在 App.js。限于篇幅就不具体阐明了,只附上一张运行的截图:

给读者的作业

你可能曾经留神到下面的后端 API 服务是无状态的,没有存储任何数据,所以其实辨认的过程能够在前端实现。如果你有趣味的话,能够调研一下如何把 PyTorch 模型转化为 JavaScript 可用的模型,尝试在浏览器里间接辨认照片。在实在的利用中,这样的形式因为不须要向服务端传输任何数据,能够完满地爱护用户隐衷,这也是 Apple 在推动的 on-device machine learning 的方向。

图片辨认是机器学习能够解决的最简略的一类问题,因为有很多现成的后果能够重用,新的利用即便只有大量训练数据也能达到比拟好的成果。还有很多其它类型的问题没有那么容易失去让人称心的后果。LeanCloud 目前正在开发机器学习方面的新产品,以帮忙开发者更容易地挖掘数据的价值。你如果对此感兴趣,能够关注咱们的微博、微信公众号、Twitter,或者注册成为 LeanCloud 用户。不久后咱们会颁布更多信息,并邀请一些用户试用新产品。

题图 Charles Deluvio

退出移动版