label-studio
label-studio社区版和企业版比较
基于UIE的命名实体识别下的label-studio-ml教程
Specific-examples-for-pre-annotations
Label Studio
是一个开源的,可配置的数据注释工具,其目的是使您能够使用标准化输出格式的最方便的界面标记不同类型的数据
特色 :支持多人协作,支持主动学习、支持多种的标注的任务
缺点 :在目标检测标注时,好像没有十字辅助线
支持的标注任务,包括了如下所示,具体的见官方git介绍
label studio 结合 MMDetection 实现数据集自动标记、模型迭代训练的闭环
Label Studio使用技巧
关键的两个git仓库,其中label-studio的官方文档在这里
label-studio , 进行普通的图片标记工作,如果要使用其提供的辅助预标记功能,则需要进行后续配置
label-studio-ml-backend ,主要提供深度学习服务,包括预标记
和模型训练
,结合前端形成模型迭代训练的主动学习
效果
版本
1 2 3 4 5 6 7 8 9 10 11 -- 前端 label -studio==1 .10 .0 .post0-- 后端 label -studio-ml==1 .0 .9 gunicorn==20 .1 .0 rq==1 .10 .1 -- 下面这两个自己会安装 label -studio-converter==0 .0 .57 label -studio-tools==0 .0 .3
前后端结合可以达到模型自动标记数据集、数据集更新迭代训练模型的闭环
前端 安装 1 2 3 pip install label -studio # 如果安装psycopg2==2 .9 .5 失败, 可以先装下下面的库 sudo yum install python-devel postgresql-devel postgresql-libs gcc
环境安装完成后在任意位置打开命令行,使用以下命令启动 label studio
1 2 # 不指定路径时, 默认放在了 /root/.local/share/label -studio label -studio --data-dir Label -Studio -p 80
其中 --data-dir
用于指定工作目录, -p
用来指定运行端口,运行成功后会当前目录会生成 Label-Studio 目录
浏览器打开label studio工作界面,创建用户后即可登录使用(这里的创建很简单,符合规范就可以)
文件夹中主要包括了export和media文件夹,以及一个label_studio.sqlite3数据库文件
数据库配置 数据库默认使用的是sqlite3,还可以配置其他的关系库,如PostgreSQL 等,通常如果你想标注数百万个任务,或者预计会有很多并发用户,或者你计划在现实生活中的项目中工作,那么使用PostgreSQL数据库
官方提供的设置pg的环境变量好像不起效果,不知道把数据库建哪里去了,这里直接在命令行带上pg库的参数
1 DJANGO_DB=default POSTGRE_NAME=label_studio_db POSTGRE_USER=postgres POSTGRE_PASSWORD=xxx POSTGRE_PORT=5432 POSTGRE_HOST=192 .168 .123 .xx LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT=/home/huangyc/label_ws label -studio start --data-dir /home/huangyc/label_ws -p 8080
LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT
用于指定本地文件的位置 ,项目设置里面配置本地路径为/home/huangyc/label_ws/media
(这只是个例子好像配成/home/huangyc/label_ws/media/upload
也是一样的),此时可以访问
1 2 http://192.168 .123 .xx:8080 /data/local-files?d=media/upload/3 /demo.jpg
当前目录结构如下
1 2 3 4 5 6 7 8 9 10 11 12 13 (label38) [root@uslave02 label_ws]# tree -d . ├── export └── media ├── avatars ├── export └── upload ├── 1 ├── 3 ├── demo.jpg └── 4 8 directories
注意这里需要提前创建好数据库label_studio_db
,启动完成控制台输出如下的日志
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 label -studio start --data-dir /home/huangyc/label_ws -p 8080 => Database and media directory: /root/.local/share/label -studio => Static URL is set to: /static/ => Database and media directory: /home/huangyc/label_ws => Static URL is set to: /static/ Starting new HTTPS connection (1 ): pypi.org:443 https://pypi.org :443 "GET /pypi /label -studio /json HTTP /1.1" 200 56156 Initializing database ..Performing system checks ...[2023-03-29 05:01:46,560] [django ::register_actions_from_dir ::97] [INFO ] No module named 'data_manager.actions.__pycache_ ' [2023-03-29 05:01:46,560] [django ::register_actions_from_dir ::97] [INFO ] No module named 'data_manager.actions.__pycache_ ' System check identified no issues (1 silenced ).March 29, 2023 - 05:01:46Django version 3.2.16, using settings 'label_studio.core.settings.label_studio 'Starting development server at http ://0.0.0.0:8080/Quit the server with CONTROL -C .
此时数据库label_studio_db
里面会建好所有的表,比如htx_user
存的就是注册的用户信息
如果要后台执行,可以新建一个start.sh脚本
1 DJANGO_DB=default POSTGRE_NAME=label_studio_db POSTGRE_USER=postgres POSTGRE_PASSWORD=xxx POSTGRE_PORT=5432 POSTGRE_HOST=192 .168 .123 .xx label -studio start --data-dir /home/huangyc/label_ws -p 8080
然后执行nohup sh start.sh > log_start.log &
导入现有标注 将现有标注转为json格式可以用label-studio-converter工具 ,安装命令为pip install label-studio-converter
转换的例子 ,目录结构为:
1 2 3 4 5 6 7 8 9 10 11 12 13 Q:. ├─images ├─ 1.jpg ├─ 2.jpg └─labels ├─ 1.txt ├─ 2.txt ├─ classes.txt # 注意这里的classes.txt 里面的类别顺序一定要正确,如果是从label -studio 前端导出的,好像顺序会有问题,可以用notes.json 去获取正确的顺序 # 代码为 annotation_labels = json_load_op ('notes.json ')['categories ']annotation_labels = sorted (annotation_labels , key =lambda k : int (k ['id ']))annotation_labels = [anno ['name '] for anno in annotation_labels ]
命令行为
1 label -studio-converter import yolo -i project-1 -o ls-tasks.json --image-root-url /data/local-files?d=tmp/images
输出ls-tasks.json
和ls-tasks.label_config.xml
两个文件,第一个是数据的索引和标签信息,第二个是项目的标签配置文件
此时要把文件上传到LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT/tmp/images
下才可以看到
如果这里转换出来的路径不对,可以修改label_studio_converter\imports\yolo.py.py
下的
1 2 3 4 5 6 7 8 9 10 11 12 13 task = { "data" : { "image" : f"{image_root_url} {sp} {image_file_base} " }, out_type: [ { "result" : [], "ground_truth" : False , } ] }
不然会访问不到数据
如果数据没有标注信息,可以将数据传到LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT/tmp/images
下,并生成如下json文件,并在项目界面导入该文件即可
1 2 3 4 [ { "image" : "/data/local-files?d=tmp/images/175.jpg" } , { "image" : "/data/local-files?d=tmp/images/696.jpg" } ]
Cloudreve数据导入
cloudreve私有云盘配置后台运行
安装配置Cloudreve
Cloudreve 可助你即刻构建出兼备自用或公用的网盘服务,通过多种存储策略的支持、虚拟文件系统等特性实现灵活的文件管理体验
Linux 下,直接解压并执行主程序即可:
1 2 3 4 5 6 7 8 #解压获取到的主程序 tar -zxvf cloudreve_VERSION_OS_ARCH.tar.gz # 赋予执行权限 chmod +x ./cloudreve # 启动 Cloudreve nohup ./cloudreve > start .log &
Windows下,直接解压获取到的 zip 压缩包,启动 cloudreve.exe
即可
Cloudreve在首次启动时,会创建初始管理员账号,请注意保管管理员密码,此密码只会在首次启动时出现。如果您忘记初始管理员密码,需要删除同级目录下的cloudreve.db
,重新启动主程序以初始化新的管理员账户。或者使用./cloudreve --database-script ResetAdminPassword
重置密码
Cloudreve默认会监听5212
端口。你可以在浏览器中访问http://服务器IP:5212
进入 Cloudreve。
以上步骤操作完后,最简单的部署就完成了。你可能需要一些更为具体的配置,才能让 Cloudreve 更好的工作
登录管理员,编辑存储策略 下的Default storage policy
,将存储文件名改为{originname}
,不然会默认会带上一些前缀
Cloudreve的文件实际上是存储在/home/huangyc/cloudreve/uploads/1
下,在此目录下新建软链接label_ws
到label-studio的工作目录下,至此
1 2 3 [root@uslave02 1 ]# ln -s /home/huangyc/label_ws/uploads label_ws [root@uslave02 1 ]# pwd /home/huangyc/cloudreve/uploads/1
此时,界面网页上可以看到label_ws文件夹,后续的项目文件可以传到这里,至此Cloudreve配置完成
label-studio项目设置
这里可以将label-studio项目的云存储位置设置为本地/home/huangyc/label_ws/uploads
,然后导入一下json文件到项目
1 2 3 [ { "image" : "/data/local-files?d=uploads/tricycle_samples/灭鬼之刃.png" } ]
最后通过Cloudreve,把文件灭鬼之刃.png
上传到label_ws/tricycle_samples
下就可以看到图片了,注意 :这里的label_ws
相当于/home/huangyc/label_ws/uploads
,这就是软链接的魅力
新建标注项目 在 label studio 前端主页中选择创建项目,主要流程
填写项目基本信息
导入数据
选择标记模板: label studio内置了很多常见的深度学习标记模板
以下是目标检测的模板
1 2 3 4 5 6 7 8 9 10 <View> <Image name="image" value="$image" zoom="true" showMousePos="true" zoomControl="true" rotateControl="true" /> <View> <Filter toName="label" minlength="0" name="filter" /> <RectangleLabels name="label" toName="image" > <Label value="tricycle" background="#ca9eff" category="0" /> <Label value="person" background="#50b01c" category="1" /> </RectangleLabels> </View> </View>
category
很重要,能保证标签的顺序,这里要从0开始
此时我们已经可以通过 label studio 进行普通的图片标记工作,如果要使用其提供的辅助预标记功能,则需要进行后续配置
后端 介绍 label studio ml
是label studio的后端配置,其主要提供了一种能够快速将AI模型封装为label studio可使用的预标记服务(提供模型预测服务)
安装: 依赖了rq库和redis库,注意有些库的版本不能过高
1 pip install label -studio-ml
模型创建
创建后端模型
导入相关库
1 2 3 4 5 6 7 8 9 10 11 12 13 import osfrom PIL import Imagefrom label_studio_ml import modelfrom label_studio_ml.model import LabelStudioMLBasefrom label_studio_ml.utils import get_envfrom label_studio_tools.core.utils.io import get_local_pathmodel.LABEL_STUDIO_ML_BACKEND_V2 = True os.environ['HOSTNAME' ] = 'http://192.168.123.xx:8080' os.environ['API_KEY' ] = 'TOKEN,在项目的settings里面复制' os.environ['LABEL_STUDIO_ML_BACKEND_V2' ] = 'True'
模型类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 class MyModel (LabelStudioMLBase ): def __init__ (self, **kwargs ): super (MyModel, self).__init__(**kwargs) print (kwargs) print ("初始化完成" ) def predict (self, tasks, **kwargs ): print ("开始预测" ) images = [get_local_path(task['data' ]['image' ], hostname=HOSTNAME, access_token=API_KEY) for task in tasks] results = [] all_scores = [] for img_path in images: for _ in range (1 ): w, h = Image.open (img_path).size pixel_x, pixel_y, pixel_width, pixel_height = convert_to_ls(0 , 0 , 200 , 250 , w, h) result = { 'id' : '0' , 'from_name' : 'label' , 'to_name' : 'image' , 'type' : 'rectanglelabels' , 'value' : { 'rectanglelabels' : ['tricycle' ], 'x' : pixel_x, 'y' : pixel_y, 'width' : pixel_width, 'height' : pixel_height }, 'score' : 0.95 } results.append(result) all_scores.append(0.95 ) print (tasks) print (kwargs) avg_score = sum (all_scores) / max (len (all_scores), 1 ) results = [{ 'result' : results, 'score' : avg_score }] return results def fit (self, completions, num_epochs=5 , **kwargs ): """ 模型训练 """ print ("开始训练" ) if self.gen_train_data(project_id): return {'model_path' : r'\runs\detect\TriCycle\weights\best.pt' } else : raise "gen_train_data error" def gen_train_data (self, project_id ): """ 获取数据训练数据 """ print ("获取数据 project_id" , project_id) import zipfile import glob download_url = f'{HOSTNAME.rstrip("/" )} /api/projects/{project_id} /export?export_type=COCO&download_all_tasks=false&download_resources=true' response = requests.get(download_url, headers={'Authorization' : f'Token {API_KEY} ' }) zip_path = os.path.join(conf['workdir' ], "train.zip" ) train_path = os.path.join(conf['workdir' ], "train" ) with open (zip_path, 'wb' ) as file: file.write(response.content) file.flush() f = zipfile.ZipFile(zip_path) f.extractall(train_path) f.close() os.remove(zip_path) if not os.path.exists(os.path.join(train_path, "images" , str (project_id))): os.makedirs(os.path.join(train_path, "images" , str (project_id))) for img in glob.glob(os.path.join(train_path, "images" , "*.jpg" )): basename = os.path.basename(img) shutil.move(img, os.path.join(train_path, "images" , str (project_id), basename)) return True
使用步骤
启动后端服务: 分为三步,生成服务代码+启动服务+连接服务+训练模型
第一步 :生成服务代码
1 label -studio-ml init backend/model --script label_studio_backend/yolo_detection.py --force
label-studio-ml init
命令提供了一种根据后端模型自动生成后端服务代码的功能, model
为输出目录, --script
指定后端模型路径, --force
表示覆盖生成。该命令执行成功后会在 backend
目录下生成 model
目录
主要包括了_wsgi.py、docker-compose.yml、Dockerfile、yolo_detection.py等文件
第二步 :启动服务
如果有依赖的文件,需要自己复制到 model
目录下,接着启动后端服务
1 2 3 label -studio-ml start backend/model --host 0 .0 .0 .0 -p 8888 # or python backend/model/_wsgi.py --host 0 .0 .0 .0 -p 8888 # 方便debug
启动成功的话,控制台会输出如下的日志
1 2 3 4 5 6 7 8 9 10 => ROOT = Q:\pyCharmWS\object_detection\smart_city_management\label_studio_backend\backend\yolo-detector => LABEL STUDIO HOSTNAME = http://192.168 .123 .xx:8080 * Serving Flask app "label_studio_ml.api" (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: off [2023 -03-29 14 :21 :59 ,286 ] [WARNING] [werkzeug::_log::225 ] * Running on all addresses. WARNING: This is a development server. Do not use it in a production deployment. [2023 -03-29 14 :21 :59 ,286 ] [INFO] [werkzeug::_log::225 ] * Running on http://10.10 .0 .xx:8888 / (Press CTRL+C to quit)
PS : 测试的时候发现,直接执行_wsgi.py
文件一样可以启动后端,跟踪label_studio_ml
的server.py
文件可以看到执行核心代码
1 2 3 4 5 6 7 8 9 10 def main (): args, subargs = get_args() if args.command == 'init' : create_dir(args) elif args.command == 'start' : start_server(args, subargs) elif args.command == 'deploy' : if args.provider == 'gcp' : deploy_to_gcp(args)
第三步 :连接服务
在我们创建的前端项目中依次选择 Settings -> Machine Learning -> Add model ,然后输入后端地址 http://10.100.143.xxx:8888/
(这里是后端的地址和端口),点击保存
此时我们从前端项目中打开待标记图片,前端会自动请求后端对其进行标记(调用后端的 predict
方法),等待片刻后即可看见预标记结果,我们只需要大致核对无误后点击 submit 即可
如果觉得每次打开图片都需要等待片刻才会收到后端预测结果比较费时,可以在 Settings -> Machine Learning 设置中选择打开 Retrieve predictions when loading a task automatically
,此后前端会在我们每次打开项目时自动对所有任务进行自动预测,基本能够做到无等待
第四步 :训练模型
在 Settings -> Machine Learning 中点击后端服务的 Start Training 按钮,即可调用后端模型使用已标记信息进行训练
也可以 Settings -> Machine Learning 中允许模型自动训练,但训练频率过高会影响程序效率
目标检测任务注意事项
predict返回的坐标为convert_to_ls
转换后的坐标值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 def convert_from_ls (result ): if 'original_width' not in result or 'original_height' not in result: return None value = result['value' ] w, h = result['original_width' ], result['original_height' ] if all ([key in value for key in ['x' , 'y' , 'width' , 'height' ]]): return w * value['x' ] / 100.0 , \ h * value['y' ] / 100.0 , \ w * value['width' ] / 100.0 , \ h * value['height' ] / 100.0 def convert_to_ls (x, y, width, height, original_width, original_height ): return x / original_width * 100.0 , y / original_height * 100.0 , \ width / original_width * 100.0 , height / original_height * 100 output = convert_from_ls(task['annotations' ][0 ]['result' ][0 ]) if output is None : raise Exception('Wrong convert' ) pixel_x, pixel_y, pixel_width, pixel_height = output print (pixel_x, pixel_y, pixel_width, pixel_height)x, y, width, height = convert_to_ls(pixel_x, pixel_y, pixel_width, pixel_height, 600 , 403 ) print (x, y, width, height)
例子
Labelstudio的UIE半监督智能标注方案本地版,赶快用起来啦
基于ner的模型实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 """ @version: v1.0 @author: huangyc @file: uie_model.py @Description: label-studio-ml init backend/model --script uie_model.py --force @time: 2023/12/14 17:04 """ import jsonimport osfrom typing import List , Dict import requestsfrom basic_support.logger.logger_config import loggerfrom label_studio_ml import modelfrom label_studio_ml.model import LabelStudioMLBasefrom label_studio_ml.utils import get_envmodel.LABEL_STUDIO_ML_BACKEND_V2 = True os.environ['HOSTNAME' ] = 'http://192.168.xx.xx:8080' os.environ['API_KEY' ] = 'd818922361ade7e2d570346d2bd6fe1668fd4e81' os.environ['LABEL_STUDIO_ML_BACKEND_V2' ] = 'True' HOSTNAME = get_env('HOSTNAME' ) API_KEY = get_env('API_KEY' ) ROOT = os.path.join(os.path.dirname(__file__)) model_url = 'http://region-46.seetacloud.com:12851/xxx' def anno (sentences: List [str ] ) -> List [Dict ]: """ content = ["2018年10月12日,当事人办理个体工商户营业执照,经营场所位于金湖县园林南路5号自北向南第10间,经营范围为食品经营,新鲜水果零售等。"] resps = [{'日期时间': [{'text': '2018年10月12日', 'start': 0, 'end': 11, 'probability': 0.9213111485894672}], '地名': [{'text': '金湖县', 'start': 33, 'end': 36, 'probability': 0.9889604263625813}, {'text': '园林南路', 'start': 36, 'end': 40, 'probability': 0.8983399204114662}]}] :param sentences: :return: """ x = requests.post(model_url, json=sentences) if x.status_code == 200 : resps = x.json()['response' ] else : raise Exception("访问异常" ) predicts = [] for resp in resps: result = [] scores = [] for label, v in resp.items(): for iv in v: score = iv['probability' ] scores.append(score) res = { 'from_name' : 'label' , 'to_name' : 'text' , 'type' : 'labels' , 'value' : { 'start' : iv['start' ], 'end' : iv['end' ], 'score' : score, 'text' : iv['text' ], 'labels' : [label] } } result.append(res) avg_score = round (sum (scores) / len (scores), 4 ) if scores else 0.0 predicts.append({"result" : result, 'score' : avg_score, 'model_version' : 'uie-ner-large' }) return predicts class UieModel (LabelStudioMLBase ): def __init__ (self, **kwargs ): super (UieModel, self).__init__(**kwargs) print (kwargs) logger.info(f"初始化完成" ) def predict (self, tasks, **kwargs ): sentences = [] for data in tasks: sentence = data['data' ]['text' ] sentences.append(sentence) return anno(sentences=sentences) def fit (self, completions: List [Dict ], workdir=None , **kwargs ): """ 模型训练 :param completions: 标注的样本 :param workdir: :param kwargs: :return: """ logger.info(f"开始构建训练样本" ) sample_path = self.gen_train_data_path(completions=completions) return {'path' : workdir, 'model_path' : None , 'sample_path' : sample_path} def gen_train_data_path (self, completions: List [Dict ] ): """ 获取数据训练数据 :param completions: :return: """ samples = [] for completion in completions: sentence = completion['data' ]['text' ] m_id = completion['id' ] created_at = completion["created_at" ] updated_at = completion["updated_at" ] labels = [] for annotation in completion['annotations' ]: for label_info in annotation['result' ]: labels.append(label_info['value' ]) sample = {"text" : sentence, "id" : m_id, "label" : labels, "created_at" : created_at, "updated_at" : updated_at} samples.append(sample) logger.info(f"构建训练样本完毕,开始输出到文件" ) sample_path = "./doccano_ext.jsonl" with open (sample_path, "w" , encoding="utf-8" ) as outfile: outfile.write(json.dumps(samples, ensure_ascii=False )) logger.info(f"样本路径为:{sample_path} " ) return sample_path
doccano
doccano官网
Roboflow
如何使用 Roboflow 标注关键点