Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ MegFlow 是一个面向视觉应用的流式计算框架, 目标是简单、高
- 基础测试工具,支持插件沙盒,用于单测插件

## HowTo
* [how to build with docker](docs/how-to-build-with-docker.zh.md)
* [how to build from source](docs/how-to-build-from-source.zh.md)
* [how to pack Python .whl](docs/how-to-pack-python-whl.zh.md)
* [how to add my service](docs/how-to-add-graph.zh.md)
* [how to add plugins](docs/how-to-add-plugins.zh.md)
* [how to optimize and debug](docs/how-to-debug.zh.md)
* how to build and run
* [build with docker](docs/how-to-build-and-run/build-with-docker.zh.md)
* [build from source](docs/how-to-build-and-run/build-from-source.zh.md)
* [generate rtsp](docs/how-to-build-and-run/generate-rtsp.zh.md)
* how to use
* [add my first service](docs/how-to-add-my-service/01-single-classification-model.zh.md)
* [how to optimize and debug](docs/how-to-debug.zh.md)
* [how to contribute](docs/how-to-contribute.zh.md)
* [FAQ](docs/FAQ.zh.md)

Expand Down
40 changes: 40 additions & 0 deletions doc_link_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import os
import re

pattern = re.compile(r'\[.*?\]\(.*?\)')
def analyze_doc(home, path):
problem_list = []
with open(path) as f:
lines = f.readlines()
for line in lines:
if '[' in line and ']' in line and '(' in line and ')' in line:
all = pattern.findall(line)
for item in all:
start = item.find('(')
end = item.find(')')
ref = item[start+1: end]
if ref.startswith('http'):
continue
fullpath = os.path.join(home, ref)
if not os.path.exists(fullpath):
problem_list.append(ref)
# print(f' {fullpath} in {path} not exist!')
else:
continue
if len(problem_list) > 0:
print(f'{path}:')
for item in problem_list:
print(f'\t {item}')
print('\n')

def traverse(_dir):
for home, dirs, files in os.walk(_dir):
if "./target" in home or "./.github" in home:
continue
for filename in files:
if filename.endswith('.md'):
path = os.path.join(home, filename)
analyze_doc(home, path)

if __name__ == "__main__":
traverse(".")
149 changes: 149 additions & 0 deletions docs/how-to-add-my-service/01-single-classification-model.zh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# 把分类模型变成服务

尽管 MegFlow 解决的是多个(20+)模型组织成 pipeline 的问题,但凡事总要一步步来。本文介绍如何 step by step 集成 1 个分类模型,最终成为图片/视频 http 服务。

## 准备分类模型

[MegEngine models]() 有现成的 imagenet 预训模型。这里把模型 dump 成 .mge。

新增 [dump.py](../../flow-python/examples/simple_classification/dump.py),按 [1, 3, 224, 224] 尺寸 trace 模型,打开推理优化选项,保存为 `model.mge`。

```bash
$ git clone https://github.com/MegEngine/models
$ cd models
$ export PYHTONPATH=${PWD}:${PYTHONPATH}
$ cd official/vision/classification/resnet
$ python3 dump.py
$ ls -lah model.mge
...
```
`dump.py` 正在 PR 到 MegEngine/models
```bash
$ cat dump.py
...
data = mge.Tensor(np.random.random((1, 3, 224, 224))) # 准备一个样例输入

@jit.trace(capture_as_const=True)
def pred_func(data):
outputs = model(data) # trace 每个 opr 的 shape
return outputs

pred_func(data)
pred_func.dump( # 保存模型
graph_name,
arg_names=["data"],
optimize_for_inference=True, # 打开推理优化选项
enable_fuse_conv_bias_nonlinearity=True, # 打开 fuse conv+bias+ReLU pass 推理更快
)
...
```

## 模型单测
开发模型的推理封装,对外提供[功能内聚](https://baike.baidu.com/item/%E9%AB%98%E5%86%85%E8%81%9A%E4%BD%8E%E8%80%A6%E5%90%88/5227009)的接口。调用方传入一张或多张图片、直接获取结果,尽量避免关心内部实现(如用何种 backbone、预处理是什么、后处理是什么)。

```bash
$ cat flow-python/examples/simple_classification/lite.py
...
def inference(self, mat):
# 设置输入
inp_data =self.net.get_io_tensor("data")
inp_data.set_data_by_share(img)

# 推理
self.net.forward()
self.net.wait()

# 取输出
output_keys = self.net.get_all_output_name()
output = self.net.get_io_tensor(output_keys[0]).to_numpy()
return np.argmax(output[0])
...
$ python3 lite.py --model model.mge --path test.jpg # 测试
2021-09-14 11:45:02.406 | INFO | __main__:<module>:81 - 285
```

`285` 是分类模型最后一层的 argmax,对应含义需要查[ imagenet 数据集分类表](../../flow-python/examples/simple_classification/synset_words.txt) ,这里是 “Egyptian cat”(下标从 0 开始)。

## 配置计算图
`flow-python/examples`增加`simple_classification/image_cpu.toml`

```bash
$ cat flow-python/examples/simple_classification/image_cpu.toml
main = "tutorial_01_image"

[[graphs]]
name = "subgraph"
inputs = [{ name = "inp", cap = 16, ports = ["classify:inp"] }] # 一、输入输出结点
outputs = [{ name = "out", cap = 16, ports = ["classify:out"] }]
connections = [
]

[[graphs.nodes]] # 二、结点参数
name = "classify"
ty = "Classify"
path = "models/simple_classification_models/resnet18.mge"
device = "cpu"
device_id = 0
...
[[graphs.nodes]] # 三、服务类型配置
name = "source"
ty = "ImageServer"
port = 8084 # 端口号 8084
response = "json"
...
```
开发时直接从别处复制一个过来即可,图片单模型服务只需要关心 3 处
* 计算图输入、输出结点的名字。这里是`classify`
* `classify` 结点的参数。最重要的是 `ty="Classify"`指明了类名,MegFlow 将在当前目录搜索`Classify`类。path/device/device_id 分别是模型路径/用 CPU 推理/用哪个核,属于用户自定义配置
* 服务类型。这里想运行图片服务 `ty = "ImageServer"`,如果想运行视频解析服务改 `ty = "VideoServer"`;图片服务默认返回图片,想返回 string 需要配置 `response = "json"`

[完整的计算图 config 定义](appendix-A-graph-definition.md)

## 实现配置中的 node

创建文件`classify.py`,把之前实现的模型推理调起来即可
```bash
$ cat flow-python/examples/simple_classification/classify.py
...
@register(inputs=['inp'], outputs=['out'])
class Classify:
def __init__(self, name, args):
logger.info("loading Resnet18 Classification...")
self.name = name

# load model and warmup
self._model = PredictorLite(path=args['path'], device=args['device'], device_id=args['device_id'])
warmup_data = np.zeros((224, 224, 3), dtype=np.uint8)
self._model.inference(warmup_data)
logger.info("Resnet18 loaded.")

def exec(self):
envelope = self.inp.recv()
if envelope is None:
return

data = envelope.msg['data']
result = self._model.inference(data)
self.out.send(envelope.repack(json.dumps(str(result))))
```
实现只有 2 点:
* `__init__` 里加载模型,做个 warmup 防止首次推理太慢
* 解码成 BGR 的 data 在 `envelope.msg['data']`,推理,send 返回 json string

[更多 node 说明](appendix-B-python-plugin.zh.md)

## 运行测试

运行服务
```bash
$ cd flow-python/examples
$ cargo run --example run_with_plugins -- -c simple_classification/image_cpu.toml -p simple_classification # 源码/docker 编译方式用这条命令
```

浏览器打开 8084 端口服务(例如 http://10.122.101.175:8084/docs ),选择一张图“try it out”即可。

## 其他

一、http 客户端开发

rweb/Swagger 提供了 http RESTful API 描述文件,例如在 http://10.122.101.175:8084/openapi.json 。`swagger_codegen` 可用描述文件生成各种语言的调用代码。更多教程见 [swagger codegen tutorial ](https://swagger.io/tools/swagger-codegen/)。
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ connections = [

对应可视化的计算图:

![](../flow-python/examples/cat_finder/images/image.png)
![](../../flow-python/examples/cat_finder/images/image.png)

## 视频范例

Expand Down Expand Up @@ -137,7 +137,7 @@ connections = [
```

视频解析服务可视化结果和图片接近:
![](../flow-python/examples/cat_finder/images/video.png)
![](../../flow-python/examples/cat_finder/images/video.png)


## 完整定义
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,3 @@ MegFlow也提供了一系列异步工具
3. `join(tasks)`, `tasks`参数是一个函数列表,`join`堵塞直到`tasks`中的函数都执行完毕
4. `create_future(callback)`, `callback`参数是一个函数, 默认值为None,`create_future`返回一个`(Future, Waker)`对象
- `Future::wait`, 堵塞直到`Waker::wake`被调用,返回`Waker::wake(result)`传入的`result`参数

# Rust Plugins

Coming soon
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### 安装 Rust
```bash
$ sudo apt install curl
$ sudo apt install yasm git build-essential ffmpeg curl
$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
```

Expand All @@ -14,11 +14,6 @@ $ cargo --version
cargo 1.53.0 (4369396ce 2021-04-27)
```

如果不成功,提示`Command 'cargo' not found`,可以按照提示加载一下环境变量(重新连接或打开终端也可以):
```
source $HOME/.cargo/env
```

> `cargo` 是 Rust 的包管理器兼编译辅助工具。类似 Java maven/go pkg/C++ CMake 的角色,更易使用。

### 安装 python3.x (推荐 conda)
Expand Down Expand Up @@ -49,7 +44,7 @@ $ conda activate py38
MegFlow 需要编译 ffmpeg。考虑到 ffmpeg 依赖较多、本身又是常用工具,最简单的办法就是直接装 ffmpeg 把编译依赖装上

```bash
$ sudo apt install yasm git build-essential ffmpeg
$ sudo apt install ffmpeg
$ ffmpeg
ffmpeg version 3.4.8...
$ sudo apt install clang
Expand All @@ -68,7 +63,7 @@ $ cd flow-python
$ python3 setup.py install --user
```

**FAQ**:`error while loading shared libraries: libpython3.8.xxx`。如果使用 conda 只需要
**常见问题**:`error while loading shared libraries: libpython3.8.xxx`,意为 libpython.so 找不到。如果使用 conda 就在 miniconda 安装目录下面,只需要设置环境变量

```bash
$ export LD_LIBRARY_PATH=`conda info --base`/pkgs/python-3.8.11-xxx/lib:${LD_LIBRARY_PATH}
Expand All @@ -88,11 +83,11 @@ $ cargo run --example run_with_plugins -- -p logical_test

接下来开始运行好玩的 Python 应用

* [猫猫围栏运行手册](../flow-python/examples/cat_finder/README.md)
* [猫猫围栏运行手册](../../flow-python/examples/cat_finder/README.md)
* 图片注册猫猫
* 部署视频围栏,注册的猫离开围栏时会发通知
* 未注册的不会提示
* [电梯电瓶车告警](../flow-python/examples/electric_bicycle/README.md)
* [电梯电瓶车告警](../../flow-python/examples/electric_bicycle/README.md)
* 电梯里看到电瓶车立即报警
* Comming Soon
* OCR: 通用字符识别
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

## Build Docker Image

MegFlow 提供了 [Dockerfile](../Dockerfile),能够“可复现地”生成运行环境、减少依赖缺失的痛苦
MegFlow 提供了 [Dockerfile](../../Dockerfile),能够“可复现地”生成运行环境、减少依赖缺失的痛苦

```bash
$ cd MegFlow
$ docker build -t megflow .
```
稍等一段时间(取决于网络和 CPU)镜像构建完成并做了基础自测
> 注意:**不要移动 Dockerfile 文件的位置**。受 [EAR](https://www.federalregister.gov/documents/2019/10/09/2019-22210/addition-of-certain-entities-to-the-entity-list) 约束,MegFlow 无法提供现成的 docker 镜像,需要自己 build 出来,这个过程用了相对路径。
```bash
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
Expand All @@ -23,11 +24,11 @@ $ docker run -p 18081:8081 -p 18082:8082 -i -t c65e37e1df6c /bin/bash

接下来开始运行好玩的 Python 应用

* [猫猫围栏运行手册](../flow-python/examples/cat_finder/README.md)
* [猫猫围栏运行手册](../../flow-python/examples/cat_finder/README.md)
* 图片注册猫猫
* 部署视频围栏,注册的猫离开围栏时会发通知
* 未注册的不会提示
* [电梯电瓶车告警](../flow-python/examples/electric_bicycle/README.md)
* [电梯电瓶车告警](../../flow-python/examples/electric_bicycle/README.md)
* 电梯里看到电瓶车立即报警
* Comming Soon
* OCR: 通用字符识别
Loading