本篇完成于 2022-04-12, 对应的 doccano 版本为 1.6.2.

doccano 是一个标注平台, 支持图片分类\文本分类\文本标注等多种标注功能.

有关它的一些基础的使用方法, 可以参考 如何使用文本标注工具——doccano? 这篇文章.

本篇主要介绍的是 doccano 在部署时的一些问题和 自动标注功能 .

Docker Compose 构建

doccano 支持多种形式的部署方式, 包括:

  • 源码部署(单体应用)
  • Docker部署(单体应用)
  • Docker Compose部署(集群应用)
  • 以及一些国外平台的一键部署

这里笔者使用的是 Docker Compose 部署方式, 因为涉及到一些源码的改动, 使用这种方式是部署起来更加灵活.

doccano 的 docker 构建文件对国内不太友好, 它的后端使用的是 python - django, 而后端项目的管理使用的是 poetry. 整个镜像使用的 Linux 发行版是 Debian. 所以在国内的服务器上构建时会陷入 backend 和 nginx 两个组件速度极其缓慢的问题.

这里需要修改一些配置文件, 来适应国内的网络环境, 达到加速构建的目的.

Debian 加速

在根目录的 docker 文件夹下, 有 Dockerfile.prodDockerfile.nginx 两个文件, 分别对应了 doccano 的后端和 nginx 的镜像.

可以在文件中找到 RUN apt-get update, 在前面加上一行 sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list , 也就是我们俗称的 “换源” . 这里由于笔者是使用的 阿里云 的服务器, 所以换的是阿里的源.

Poetry

Poetry的安装需要用到他们 Github仓库 的 get-poetry.py 文件, 在国内网络环境下, 下载这个文件会很玄学, 所以推荐先将文件放入构建目录 backend, 只有这个文件是不够的, 文件中会触发下载 poetry 的最新依赖包, 这个也是托管在 Github上的, 所以也需要提前下载下来.

这样, 就可以把安装 poetry 的 curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - 这个命令替换为 python get-poetry.py --file poetry-1.1.13-linux.tar.gz 这个命令.

补充: 记得更新 CPOY 命令, poetry 的安装先于 python 依赖包的安装.

由于 poetry 的安装包体积较大, 所以建议清理一下, 否则镜像的体积可能会超过 100M .

yarn

yarn 的安装同样会受到一些来自国内网络环境 “微小的阻力”, 所以需要进行换源.

具体的操作就是在 yarn install 这个命令之前使用 yarn config set registry https://registry.npm.taobao.org.

gnutls_handshake() 问题

在使用yarn进行安装时, 还有可能会出现 gnutls_handshake()的报错.这个错误的具体成因和解决方案可以见于others-How to solve ‘gnutls_handshake() failed’ error when doing ‘git clone’ from github.com ?

这里我使用的是第一种方案, 将https更改为http.

git config --global url."http://github.com/".insteadOf git://github.com/

自动标注

doccano 在文本标注项目中支持以 RESTFul 的形式调用我们的预测接口. 一些详细的使用方法可以参考 How to connect to a local REST API for auto annotation?.

这里列出一些 Tips.

  1. 一般我们会以 POST 的形式来调用接口, 因为 GET 可能会出现 URL 超长的情况.
  2. 我们需要将待预测文本放在 Request Body 中, 这需要使用它的一个内置语法, 即 代表待预测文本. 注意, 双括号中的空格不能省略.
  3. 接口返回的的数据需要遵循统一的规范, 这个规范是我们自己决定的.

接口的返回如果使用如下的格式.

[{
    'start_offset': "",
    'end_offset': "",
    'label': ""
}]

那么在渲染时就需要使用如下的格式进行匹配.

[
    {% for entity in input %}
        {
            "start_offset": {{ entity.start_offset }},
            "end_offset": {{ entity.end_offset}},
            "label": "{{ entity.label }}"
        }{% if not loop.last %},{% endif %}
    {% endfor %}
]