본문으로 건너뛰기

내 어노테이션으로 첫 모델 학습하기

GPU 없는 노트북에서 Potato의 샘플 리뷰 연구를 실행하고, 시뮬레이터로 리뷰 120개에 레이블을 달고, sklearn-text를 학습시켜 모델의 사전 레이블을 확인합니다.

v2.14.0의 새 기능

이 튜토리얼은 샘플 연구 자체의 레이블로 감성 분류기를 학습시키고, 분류기의 답을 다음 어노테이터에게 사전 레이블로 보여 줍니다. 이 튜토리얼은 Potato의 모델 학습을 처음 써 보는 연구 책임자를 위해 쓰였으며, 모든 단계는 GPU 없는 노트북에서 Potato 외에 다른 설치 없이 실행됩니다. Potato의 샘플 리뷰 프로젝트를 시작하고, 사용자 시뮬레이터로 프로젝트의 리뷰 160개 중 120개에 레이블을 달고, 관리자 Training 페이지에서 sklearn-text를 학습시키고, 실행의 홀드아웃 점수를 읽은 뒤, 새 어노테이터로 로그인해 모델의 답을 확인합니다. 루프의 각 부분이 일반적으로 하는 일은 모델 학습 개요를 참고하십시오.

Potato와 예제 프로젝트 설치

이 튜토리얼은 각 리뷰를 TF-IDF 특징으로 바꾸고 로지스틱 회귀 분류기를 맞추는 트레이너인 sklearn-text를 씁니다. scikit-learn은 Potato의 핵심 의존성이므로 sklearn-text에는 추가 설치가 필요 없습니다. 샘플 프로젝트는 pip 패키지가 아니라 Potato의 GitHub 저장소에 있으므로, Potato를 설치하고 저장소를 클론합니다.

bash
pip install --upgrade potato-annotation
git clone https://github.com/davidjurgens/potato.git
cd potato

아래의 모든 명령은 저장소 루트에서 실행합니다. 프로젝트 폴더 examples/ai-assisted/model-training/에는 파일이 세 개 있습니다.

  • **config.yaml**은 연구 설정이며, 이 튜토리얼의 명령이 주석으로 들어 있습니다.
  • **data/reviews.json**에는 합성 상품 리뷰 160개가 한 줄에 JSON 객체 하나씩, id와 text와 함께 들어 있습니다.
  • **data/gold.json**은 모든 리뷰의 정답 레이블을 담고 있습니다. 시뮬레이터는 이 파일을 보고 답합니다.

예제 설정

설정은 레이블이 positive와 negative인 radio 스키마 sentiment 하나를 정의하고 모델 학습을 켭니다. 아래 블록은 주석을 뺀 배포 파일이며, 이 파일은 수정 없이 python -m potato.validate_cli --strict를 통과합니다.

yaml
annotation_task_name: "Review Sentiment (model training demo)"
 
data_files:
  - "data/reviews.json"
 
item_properties:
  id_key: "id"
  text_key: "text"
 
task_dir: "."
output_annotation_dir: "annotation_output"
 
require_password: false
admin_api_key: "demo-admin-key"
 
num_annotators_per_item: 1
 
annotation_schemes:
  - annotation_type: "radio"
    name: "sentiment"
    description: "Is this review positive or negative?"
    labels:
      - "positive"
      - "negative"
    sequential_key_binding: true
 
model_training:
  enabled: true
  writeback:
    schemas: ["sentiment"]
    include_unlabeled: true
    min_confidence: 0.6

네 가지 설정이 이 튜토리얼의 동작을 정합니다.

  • **num_annotators_per_item: 1**은 모든 리뷰에 레이블을 하나씩 줍니다. 그러면 리뷰를 40개씩 맡는 시뮬레이션 어노테이터 3명이 서로 다른 리뷰 120개를 채우고, 모델용으로 40개를 레이블 없이 남깁니다.
  • **require_password: false**는 새 어노테이터가 사용자 이름만으로 로그인하게 합니다. 마지막 단계는 이 방식으로 로그인합니다.
  • **admin_api_key: "demo-admin-key"**는 관리자 페이지를 엽니다. 다른 사람이 접근할 수 있는 서버에서는 키를 바꾸십시오.
  • **model_training**은 /admin/training 경로를 등록합니다. writeback 블록은 sentiment 스키마의 예측을 어노테이터에게 보여 주며, 각 키는 아래 되쓰기 절에서 설명합니다.

포트 8000에서 서버 시작

예제 설정으로 Potato를 시작합니다.

bash
potato start examples/ai-assisted/model-training/config.yaml -p 8000

서버는 리뷰 160개를 불러오고 http://localhost:8000에서 어노테이션 페이지를 제공합니다. 사용 중인 컴퓨터에서 포트 8000을 이미 쓰고 있으면 -p에 다른 포트를 주고, 아래의 모든 URL에서 그 포트를 쓰십시오.

시뮬레이터로 리뷰 120개에 레이블 달기

모델이 학습하려면 사람의 레이블이 필요합니다. 사용자 시뮬레이터가 어노테이터 3명을 대신합니다. 각 시뮬레이션 어노테이터는 정답 파일을 보고 답하며 대부분 맞힙니다. 두 번째 터미널에서 저장소 루트에 서서 시뮬레이터를 실행합니다.

bash
python -m potato.simulator --server http://localhost:8000 --users 3 \
  --strategy gold_standard --competence good=1.0 --max-annotations 40 \
  --gold-file examples/ai-assisted/model-training/data/gold.json \
  --sequential --fast-mode --no-export

--strategy gold_standard는 각 시뮬레이션 어노테이터가 gold.json을 보고 답하게 하고, --competence good=1.0은 세 어노테이터 모두에게 시뮬레이터의 good 프로필을 줍니다. good 어노테이터는 0.80에서 0.90 사이의 정확도를 뽑아 그 확률로 각 리뷰에 맞게 답합니다. 따라서 실제 팀의 레이블처럼 레이블에 오류가 들어 있습니다. --sequential은 세 어노테이터를 차례로 실행하고, --fast-mode는 답 사이의 대기를 없애며, --no-export는 시뮬레이터 자체의 결과 파일을 쓰지 않습니다. 시뮬레이터는 약 2초 안에 끝나고 요약을 출력합니다. 다음으로 넘어가기 전에 요약에 Total annotations: 120이 보고되는지 확인하십시오. http://localhost:8000에서 리뷰에 직접 레이블을 달 수도 있습니다. sklearn-text는 train 분할에 레이블이 달린 항목이 10개 미만이면 학습을 거부합니다.

관리자 키로 Training 페이지 열기

Training 페이지에는 관리자 세션이 필요합니다. http://localhost:8000/admin을 열고, 페이지가 API 키를 물으면 demo-admin-key를 입력한 뒤, http://localhost:8000/admin/training을 엽니다.

sklearn-text, sentiment 스키마, local 백엔드가 선택된 Potato Training 페이지와, 홀드아웃 점수 0.847과 0.840인 완료된 실행 두 개가 나열된 실행 표샘플 프로젝트에서 sklearn-text를 두 번 실행한 뒤의 Training 페이지

맨 위의 Start a run 패널에 설정이 모두 들어 있습니다. Schema에는 프로젝트의 스키마 하나인 sentiment (radio)가 나옵니다. Trainer에는 트레이너 38개가 모두 나오며, 프로젝트에서 실행할 수 없는 트레이너는 이름 옆에 "not installed" 같은 이유가 붙습니다. 페이지는 sklearn-text가 스키마에 맞을 때마다 sklearn-text를 선택합니다. Runs on에는 프로젝트의 유일한 백엔드인 local (this machine)이 나옵니다.

sklearn-text 학습시키기

Parameters를 비워 두면 트레이너의 기본값을 쓰며, Train now를 클릭합니다. 실행은 어노테이션을 리뷰당 하나의 레이블로 통합하고, 레이블이 달린 리뷰 120개를 train, val, test 세트로 나눈 뒤, 별도 프로세스에서 모델을 맞춥니다. 우리 실행은 1.5초가 걸렸고, 실행이 끝나자 패널에 "Run finished."가 나왔습니다.

sklearn-text는 기본적으로 특징에 scikit-learn의 TfidfVectorizer를, 분류기에 LogisticRegression을 씁니다. train 분할에 항목이 50개 이상이면 sklearn-text는 분류기의 신뢰도도 보정합니다. 보정은 학습 항목이 1,000개 미만이면 Platt 스케일링을, 1,000개부터는 isotonic 회귀를 씁니다. 보정된 모델은 보정 때문에 줄어드는 학습 정확도가 0.05 이하일 때만 유지됩니다. 되쓰기 단계가 각 신뢰도를 min_confidence와 비교하므로 여기서는 보정이 중요합니다.

같은 실행을 스크립트에서 시작하려면 X-API-Key 헤더에 관리자 키를 담아 보냅니다.

bash
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
  http://localhost:8000/admin/training/api/runs \
  -d '{"trainer": "sklearn-text", "schemas": ["sentiment"]}'

응답에는 새 run_id와 "started": true가 들어 있습니다.

실행 페이지와 홀드아웃 점수

끝난 실행은 실행 표에 train 크기, val 크기, Held-out score와 함께 나타납니다. 실행 ID를 클릭하면 /admin/training/runs/<run_id>의 실행 페이지가 열립니다. 실행 페이지에는 실행의 상태, 트레이너, 백엔드, 분할 크기, 분할 시드, 홀드아웃 점수, 파라미터가 나열되고, 이어서 모든 지표의 표와 실행 로그가 나옵니다.

실행은 기본적으로 레이블이 달린 항목의 15%를 val용으로, 15%를 test용으로 홀드아웃하며, 어느 항목이 어디에 들어가는지는 split_seed가 정합니다. split_seed의 값이 유지되는 동안 분할은 실행마다 같으므로, 같은 레이블에서 한 두 실행은 같은 항목으로 점수가 매겨집니다. sklearn-text 실행의 홀드아웃 점수는 val의 macro-F1입니다. macro-F1은 각 레이블, 여기서는 positive와 negative의 F1 점수를 평균한 값입니다. 실행은 test의 정확도와 macro-F1도 기록합니다. train 분할의 정확도는 train_accuracy로 기록되며 점수로는 쓰이지 않습니다. 우리 실행의 로그는 다음과 같았습니다.

text
[info] Read 76 labelled rows from the train split
[info] Applied sigmoid calibration over 5 folds
[info] Held-out val macro_f1: 0.725

Potato 자체 문서도 같은 프로젝트에서 한 실행을 보고하며, 아래 표는 그 실행을 우리 실행과 나란히 놓습니다.

Potato 문서우리 실행
레이블이 달린 리뷰120120
train, val, test 항목명시 없음76, 19, 25
홀드아웃 점수 (val macro-F1)0.840.725
test macro-F1명시 없음0.826
게시된 사전 레이블8073
min_confidence 미만의 예측411

각 시뮬레이션 어노테이터가 정확도를 무작위로 뽑고 서로 다른 실수를 하므로, 독자의 숫자는 두 열과 모두 다를 것입니다. 우리 실행에서 시뮬레이션 레이블은 리뷰 120개 중 97개에서, val 리뷰 19개 중 15개에서 gold.json과 일치했습니다. 홀드아웃 점수는 어떤 정답지가 아니라 어노테이터의 레이블과의 일치를 측정하므로, val에 있는 어노테이터의 오류가 점수를 낮춥니다. 우리 실행이 게시한 사전 레이블 73개는 모두 gold.json과 일치했습니다. 정답 파일이 없는 실제 연구에서는 낮은 홀드아웃 점수를 보면 모델을 탓하기 전에 어노테이터 간 일치도를 확인하십시오.

스키마에 되쓰기 켜기

실행의 예측은 되쓰기를 통해서만 어노테이터에게 닿습니다. 예제 설정은 이미 sentiment 스키마에 되쓰기를 켜 두었습니다. 직접 만든 프로젝트에서는 설정의 model_training 아래에 이 블록을 추가하고 서버를 다시 시작합니다.

yaml
model_training:
  enabled: true
  writeback:
    schemas: ["sentiment"]
    include_unlabeled: true
    min_confidence: 0.6

writeback.schemas는 예측을 어노테이터에게 보여 줄 스키마를 지정합니다. 이 목록은 기본적으로 비어 있으므로, 스키마를 지정하기 전에는 학습을 켜도 어노테이션 페이지가 달라지지 않습니다. include_unlabeled: true는 각 실행이 val 리뷰와 test 리뷰뿐 아니라 아무도 레이블을 달지 않은 리뷰도 예측하게 합니다. include_unlabeled가 없으면 실행은 val 리뷰와 test 리뷰만 예측하며, 그 리뷰에는 이미 사람의 레이블이 있습니다. min_confidence: 0.6은 모델의 신뢰도가 0.6 미만이면 그 예측을 어노테이션 페이지에서 뺍니다. Potato는 그 예측을 그래도 저장합니다.

되쓰기는 성공한 실행마다 실행되며 실행의 train 분할에 있는 항목은 건너뜁니다. 우리 실행에서 모델은 리뷰 84개(val 19개, test 25개, 레이블 없는 40개)를 예측했고, 예측 11개가 0.6 미만이었으며, 실행은 나머지 73개를 게시했습니다. GET /admin/training/api/status의 writeback 필드가 같은 수를 보여 주며, 요약은 "Published 73 prediction(s) as prelabels."입니다. 저장된 예측은 서버가 시작할 때 다시 되쓰이므로 사전 레이블은 재시작 후에도 남습니다. 실행의 사전 레이블을 항목에서 빼려면 DELETE /admin/training/api/runs/<run_id>/predictions를 보냅니다.

새 어노테이터가 본 사전 레이블

비공개 브라우저 창에서 http://localhost:8000을 열고 아무도 쓰지 않은 사용자 이름, 예를 들어 newannotator로 로그인합니다. 레이블이 달린 리뷰는 이미 레이블이 하나씩 있으므로 새 어노테이터에게는 레이블 없는 리뷰 40개가 배정되며, 우리 실행의 진행 카운터는 0/40이었습니다. 우리의 첫 리뷰는 "The toaster: Disappointed, it stopped working within a week. Much smaller than the photos suggest."였고, Negative가 신뢰도 0.68로 이미 선택되어 있었습니다.

모델이 Positive 옵션을 이미 선택해 둔 토스터 리뷰의 Potato 어노테이션 페이지와, 아직 Not labeled로 표시된 항목어노테이터가 아무것도 저장하기 전에 모델의 답이 선택된 채 열린 리뷰

어노테이터가 리뷰를 보기만 하는 동안 사전 레이블은 저장되지 않으며, 상태 칩은 어노테이터가 답을 저장할 때까지 "Not labeled"로 표시됩니다. 모델의 답은 어노테이터가 선택을 바꾸거나 Next를 클릭할 때 어노테이터의 답이 됩니다. 우리 실행에서 예측이 min_confidence 미만이었던 레이블 없는 리뷰 5개는 아무것도 선택되지 않은 채 열렸습니다. ai_support.holdout으로 기계 지원에서 제외된 어노테이터에게는 사전 레이블이 전혀 보이지 않습니다. 사전 레이블은 어노테이터를 모델의 답 쪽으로 끌어당기므로, 홀드아웃 점수가 믿을 만큼 높아진 뒤에 되쓰기를 켜고, 모든 사전 레이블을 그대로 받아들이는 어노테이터는 무심코 승인한 사전 레이블 탐지의 점검으로 살피십시오.

다음 단계

같은 프로젝트로 자기 데이터로 넘어가기 전에 몇 가지 실험을 더 할 수 있습니다.

  • 파라미터 바꾸기. Parameters에 {"vectorizer_kwargs": {"ngram_range": [1, 2]}}를 입력하고 다시 학습시키면 특징에 단어 쌍이 추가됩니다. 단어 쌍을 쓴 우리의 두 번째 실행은 같은 분할에서 val macro-F1 0.774를 기록했고 사전 레이블 74개를 게시했습니다. sklearn-text가 받지 않는 파라미터 이름은 트레이너가 받는 이름의 목록과 함께 거부됩니다.
  • 트랜스포머 써 보기. hf-text-classification(pip install "potato-annotation[train-text]")과 setfit(pip install "potato-annotation[embeddings]")이 같은 스키마를 학습하며, 각 트레이너의 파라미터는 트레이너 페이지에 있습니다.
  • 루프 닫기. 루프 안의 모델은 검토 대기열, auto_retrain으로 어노테이션이 들어올 때마다 재학습하기, 실행의 분할을 데이터셋 버전으로 저장하기를 더합니다.
  • 학습을 GPU로 옮기기. GPU 서버, SLURM 클러스터, NAT 뒤의 컴퓨터에서 학습하기는 같은 실행을 다른 컴퓨터로 보냅니다.
  • 다른 종류의 모델 학습하기. LLM 파인튜닝, 비전 모델, 오디오 모델, 과학 모델 튜토리얼은 다른 트레이너와 스키마로 같은 단계를 따릅니다.

여기서 쓴 모든 키는 모델 학습 설정 레퍼런스에 나열되어 있습니다. 위의 model_training 블록을 설정에 복사하고 writeback.schemas 아래에 스키마 이름을 적으면 자기 프로젝트의 첫 실행을 시작할 수 있습니다.

더 읽어보기