Views
No views yet
low, medium, high| Criterion | Description |
|---|---|
| Content Accuracy | Factual reliability and use of credible sources |
| Clarity | Clear explanations, well-defined terms, logical flow |
| Coherence | Overall organization and logical progression |
| Grammar and Language | Correctness and audience appropriateness |
| Depth of Information | Level of detail and comprehensiveness |
| Overall Usefulness | Relevance and practical value for a general audience |
low, medium, or high) along with a short justification. Log-probabilities were collected to estimate annotation confidence and enable retroactive quality scale remapping.Below is an extract from a web page. Evaluate the quality of the content based on the following factors:
1. Content Accuracy: Assess the correctness and reliability of the information presented. Consider the factual accuracy, use of credible sources (if mentioned), and absence of misinformation.
2. Clarity: Evaluate how well the information is communicated. Look for clear explanations, well-defined terms, and logical flow of ideas.
3. Coherence: Analyze the overall structure and organization of the content. Consider how well ideas are connected and if the content follows a logical progression.
4. Grammar and Language: Assess the quality of writing, including correct grammar, spelling, and punctuation. Consider the appropriateness of language for the intended audience.
5. Depth of Information: Evaluate the level of detail and thoroughness of the content. Consider whether it provides surface-level information or delves into more comprehensive explanations.
6. Overall Usefulness: Assess the practical value and relevance of the information for a general audience. Consider how applicable or helpful the content would be for someone seeking information on the topic.
Based on these factors, give an overall quality score of low, medium, or high.
Additionally, select one or more domains from the list below. Each domain listed is a single, combined category. Choose the most relevant domain(s). Domain(s) can only be chosen from the list below. Only select "Other" if none of the listed domains are applicable.
- Arts
- Business & Economics & Finance
- Culture & Cultural geography
- Daily Life & Home & Lifestyle
- Education
- Entertainment & Travel & Hobby
- Environment
- Food & Drink & Cooking
- Health & Wellness & Medicine
- Law & Justice
- Natural Science & Formal Science & Technology
- Personal Development & Human Resources & Career
- Politics & Government
- Religion & Spirituality
- Shopping & Commodity
- Society & Social Issues & Human Rights
- Sports
- Other (only if none of the above are relevant)
Additionally, identify the main topic of the extract, which can be any relevant subfield. Don't elaborate on the topic; just provide a concise classification.
Additionally, identify the document type, which can be article, blog post, forum post, or any other relevant type. Don't elaborate on the type; just provide a concise classification.
USER PROMPT:
The extract:
{DOCUMENT}
After examining the extract:
- Briefly justify your quality classification, up to 100 words on one line using the format: "Explanation: <justification>"
- Conclude with the quality classification using the format: "Quality score: <classification>" (on a separate line)
- Continue with the domain classification using the format: "Domain: <classification>, <classification>, ..." (on a separate line)
- Continue with the main topic or subject classification using the format: "Main topic: <classification>" (on a separate line)
- Continue with the document type classification using the format: "Document type: <classification>" (on a separate line)
Evaluate the content based on the quality factors outlined above.| True \ Predicted | Low | Medium | High |
|---|---|---|---|
| Low | 922 | 463 | 77 |
| Medium | 203 | 5,219 | 623 |
| High | 32 | 531 | 1,930 |
1from transformers import pipeline
2
3classifier = pipeline("text-classification", model="almanach/gaperon-quality-classifier")
4documents = ["Your document text goes here."]
5results = classifier(documents)
6for result in results:
7 print(f"Label: {result['label']}, Score: {result['score']}")1import asyncio
2import json
3import logging
4import os
5import time
6from ast import literal_eval
7from typing import Dict, List, Optional
8
9import migraphx as mgx
10import numpy as np
11import uvicorn
12from fastapi import FastAPI, HTTPException
13from pydantic import BaseModel
14from transformers import AutoTokenizer
15
16MAX_BATCH_SIZE = int(os.getenv("MAX_BATCH_SIZE", 512))
17label_list = os.getenv("LABEL_LIST", "")
18if not label_list:
19 raise ValueError("LABEL_LIST environment variable is required")
20elif "json" in label_list:
21 # laoding from config file
22 id2label = json.loads(label_list)["id2label"]
23 # convert keys to int
24 id2label = {int(k): v for k, v in id2label.items()}
25 # list sorted by key
26 label_list = [id2label[i] for i in sorted(id2label.keys())]
27else:
28 label_list = label_list.split(",")
29
30assert len(label_list) > 0, "LABEL_LIST environment variable is required"
31print(f"Label list: {label_list}")
32
33MODEL_PATH = os.getenv("MODEL_PATH", None)
34assert MODEL_PATH is not None, "MODEL_PATH environment variable is required"
35TOKENIZER_PATH = os.getenv("TOKENIZER_PATH", None)
36assert TOKENIZER_PATH is not None, "TOKENIZER_PATH environment variable is required"
37
38
39model = mgx.load(MODEL_PATH, format="msgpack")
40tokenizer = AutoTokenizer.from_pretrained(TOKENIZER_PATH)
41
42LOGGING_CONFIG = {
43 "version": 1,
44 "disable_existing_loggers": True,
45 "formatters": {
46 "standard": {
47 "format": "%(process)d %(asctime)s [%(levelname)s] %(name)s: %(message)s"
48 },
49 },
50 "handlers": {
51 "default": {
52 "level": "INFO",
53 "formatter": "standard",
54 "class": "logging.StreamHandler",
55 "stream": "ext://sys.stdout", # Default is stderr
56 },
57 },
58 "loggers": {
59 "": { # root logger
60 "level": "INFO", # "INFO",
61 "handlers": ["default"],
62 "propagate": False,
63 },
64 "uvicorn.error": {
65 "level": "DEBUG",
66 "handlers": ["default"],
67 },
68 "uvicorn.access": {
69 "level": "WARNING",
70 "handlers": ["default"],
71 },
72 },
73}
74
75logging.config.dictConfig(LOGGING_CONFIG)
76
77logger = logging.getLogger(__name__)
78logger.info("Starting FastAPI server...")
79logger.info(f"Model path: {MODEL_PATH}")
80logger.info(f"Tokenizer path: {TOKENIZER_PATH}")
81logger.info(f"Label list: {label_list}")
82app = FastAPI()
83
84
85class InputData(BaseModel):
86 text: str
87
88
89# Update BatchInputData model
90class BatchInputData(BaseModel):
91 texts: Optional[List[str]] = None
92 input_ids: Optional[List[List[int]]] = None
93 attention_mask: Optional[List[List[int]]] = None
94 token_type_ids: Optional[List[List[int]]] = None
95 is_pre_tokenized: bool = False
96
97
98class LabelScore(BaseModel):
99 label: str
100 score: float
101
102
103class BatchOutputData(BaseModel):
104 results: List[List[LabelScore]]
105
106
107def softmax(_outputs, axis=-1):
108 maxes = np.max(_outputs, axis=axis, keepdims=True)
109 shifted_exp = np.exp(_outputs - maxes)
110 return shifted_exp / shifted_exp.sum(axis=axis, keepdims=True)
111
112
113# Asynchronous function to tokenize the batch
114async def tokenize_batch(texts):
115 tokenized_batch = tokenizer(
116 texts,
117 truncation=True,
118 padding="max_length",
119 max_length=512,
120 return_tensors="np",
121 return_attention_mask=True,
122 return_token_type_ids=True,
123 )
124 return {
125 "input_ids": tokenized_batch["input_ids"],
126 "attention_mask": tokenized_batch["attention_mask"],
127 "token_type_ids": tokenized_batch["token_type_ids"],
128 }
129
130
131# Function to run model inference (blocking)
132def run_inference(batch):
133 logits = np.array(model.run(batch)).reshape(-1, len(label_list))
134 return softmax(logits, axis=-1)
135
136
137# Queues for tokenization and inference
138tokenization_queue = asyncio.Queue()
139inference_queue = asyncio.Queue()
140
141
142# Consumer for inference
143async def inference_consumer():
144 while True:
145 tokenized_batch, result_future = await inference_queue.get()
146 try:
147 # async with inference_semaphore:
148 # Run inference on the GPU
149 result = run_inference(tokenized_batch)
150
151 result_future.set_result(result) # Set the result for the future
152 except Exception as e:
153 result_future.set_exception(e)
154 finally:
155 inference_queue.task_done()
156
157
158# Consumer for tokenization
159async def tokenization_consumer():
160 while True:
161 texts, result_future = await tokenization_queue.get()
162 try:
163 # async with tokenization_semaphore:
164 # Tokenize the batch asynchronously (CPU task)
165 tokenized_batch = await tokenize_batch(texts)
166
167 # Once tokenized, queue for inference (GPU task)
168 await inference_queue.put((tokenized_batch, result_future))
169 except Exception as e:
170 result_future.set_exception(e)
171 finally:
172 tokenization_queue.task_done()
173
174
175# Background tasks for tokenization and inference consumers
176# Define semaphores for tokenization and inference
177# tokenization_semaphore = asyncio.Semaphore(10) # Limit to 5 concurrent tokenizations
178# inference_semaphore = asyncio.Semaphore(5) # Limit to 5 concurrent inferences
179
180
181@app.on_event("startup")
182async def startup_event():
183 asyncio.create_task(tokenization_consumer())
184 asyncio.create_task(inference_consumer())
185
186
187@app.post("/label")
188async def label_text(data: BatchInputData):
189 if data.is_pre_tokenized:
190 # Validate pre-tokenized inputs
191 if not all([data.input_ids, data.attention_mask, data.token_type_ids]):
192 raise HTTPException(
193 status_code=400,
194 detail="When is_pre_tokenized is True, input_ids, attention_mask, and token_type_ids are required.",
195 )
196
197 # Ensure batch sizes are consistent
198 batch_size = len(data.input_ids)
199 if any(
200 len(lst) != batch_size for lst in [data.attention_mask, data.token_type_ids]
201 ):
202 raise HTTPException(
203 status_code=400,
204 detail="All pre-tokenized inputs (input_ids, attention_mask, token_type_ids) must have the same batch size.",
205 )
206
207 # Package the pre-tokenized inputs for inference
208 tokenized_batch = {
209 "input_ids": np.array(data.input_ids, dtype=np.int64),
210 "attention_mask": np.array(data.attention_mask, dtype=np.int64),
211 "token_type_ids": np.array(data.token_type_ids, dtype=np.int64),
212 }
213
214 # Create a future for inference
215 result_future = asyncio.get_event_loop().create_future()
216
217 # Directly add the pre-tokenized data to the inference queue
218 await inference_queue.put((tokenized_batch, result_future))
219
220 else:
221 # Validate and process texts for tokenization
222 if not data.texts:
223 raise HTTPException(
224 status_code=400,
225 detail="Texts field is required when is_pre_tokenized is False.",
226 )
227
228 if len(data.texts) > MAX_BATCH_SIZE:
229 raise HTTPException(
230 status_code=400, detail=f"Batch size is too large (> {MAX_BATCH_SIZE})"
231 )
232
233 # Create a future for tokenization and inference
234 result_future = asyncio.get_event_loop().create_future()
235
236 # Add the texts to the tokenization queue
237 await tokenization_queue.put((data.texts, result_future))
238
239 # Wait for the future result to be set (after tokenization and/or inference completes)
240 predictions = await result_future
241
242 # Process the results into the desired format
243 results = [
244 [LabelScore(label=label, score=score) for label, score in zip(label_list, pred)]
245 for pred in predictions
246 ]
247 # Sort the results by score
248 results = [
249 sorted(result, key=lambda x: x.score, reverse=True) for result in results
250 ]
251
252 return {"results": results}
253
254
255@app.get("/health")
256def health():
257 # check if current SLURM job is ending soon
258 slurm_job_end_time = os.getenv("SLURM_JOB_END_TIME", None)
259 if slurm_job_end_time is not None:
260 slurm_job_end_time = int(slurm_job_end_time)
261 if slurm_job_end_time - time.time() < 300:
262 return {"status": "ending"}
263
264 return {"status": "ok"}
265
266
267@app.get("/get_job_info")
268def get_job_info():
269 job_info = {}
270 for key in os.environ:
271 if key.startswith("SLURM_"):
272 job_info[key] = os.getenv(key)
273 return job_info
274
275
276# run with
277if __name__ == "__main__":
278 uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)1FROM rocm/pytorch:rocm6.0_ubuntu20.04_py3.9_pytorch_2.1.1
2
3ARG ONNXRUNTIME_REPO=https://github.com/Microsoft/onnxruntime
4ARG ONNXRUNTIME_BRANCH=v1.17.3
5
6ENV PATH /code/cmake-3.27.3-linux-x86_64/bin:${PATH}
7
8RUN apt-get update &&\
9 apt-get install -y migraphx
10
11WORKDIR /install_dir
12
13# Prepare onnxruntime repository & build onnxruntime
14RUN git clone --single-branch --branch ${ONNXRUNTIME_BRANCH} --recursive ${ONNXRUNTIME_REPO} onnxruntime &&\
15 /bin/sh onnxruntime/dockerfiles/scripts/install_common_deps.sh &&\
16 cd onnxruntime && pip install --upgrade pip &&\
17 /bin/sh ./build.sh --allow_running_as_root --cmake_extra_defines ONNXRUNTIME_VERSION=`cat ./VERSION_NUMBER` --config Release --parallel \
18 --skip_tests --build_wheel --use_rocm --rocm_version=${ROCM_VERSION} --rocm_home /opt/rocm --use_migraphx && \
19 pip install /install_dir/onnxruntime/build/Linux/Release/dist/*.whl
20
21RUN pip install --upgrade --upgrade-strategy eager optimum[amd]==1.22.0 fastapi[standard]
22
23WORKDIR /workspace1@misc{godey2025gaperonpepperedenglishfrenchgenerative,
2 title={Gaperon: A Peppered English-French Generative Language Model Suite},
3 author={Nathan Godey and Wissam Antoun and Rian Touchent and Rachel Bawden and Éric de la Clergerie and Benoît Sagot and Djamé Seddah},
4 year={2025},
5 eprint={2510.25771},
6 archivePrefix={arXiv},
7 primaryClass={cs.CL},
8 url={https://arxiv.org/abs/2510.25771},
9}