{
"cells": [
{
"metadata": {},
"cell_type": "markdown",
"source": "# Usage Example",
"id": "e781bc3c0109b62b"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-06T04:28:37.694211Z",
"start_time": "2025-11-06T04:28:32.520235Z"
}
},
"cell_type": "code",
"source": [
"import torch\n",
"from accelerate import Accelerator\n",
"from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline\n",
"import re\n",
"\n",
"###############################################\n",
"# Assuming this script is outside the model dir\n",
"###############################################\n",
"\n",
"model_checkpoint_path = './EduRABSA_SLM_v1_SLERP_phi4mini'\n",
"\n",
"# inference config for evaluation\n",
"generation_args = {\"max_new_tokens\": 1024,\n",
" \"return_full_text\": False,\n",
" \"temperature\": None, # or a float after setting 'do_sample=True' \n",
" \"top_p\": None,\n",
" \"top_k\": None,\n",
" \"do_sample\": False\n",
" }\n",
"\n",
"model_kwargs = {\n",
" 'use_cache': False,\n",
" 'trust_remote_code': False, # or True if you haven't downloaded the model to local\n",
" 'attn_implementation': 'flash_attention_2',\n",
" 'torch_dtype': torch.bfloat16, # loaded pre-trained model torch dtype\n",
" 'device_map': {\"\": 0} # or 'cpu', \n",
"}\n",
"\n",
"accelerator = Accelerator()\n",
"device = accelerator.device\n",
"\n",
"###############################################\n",
"# Load tokenizer \n",
"###############################################\n",
"tokenizer = AutoTokenizer.from_pretrained(model_checkpoint_path)\n",
"tokenizer.model_max_length = generation_args['max_new_tokens']\n",
"\n",
"###############################################\n",
"# Load pre-trained model & set up trainer\n",
"###############################################\n",
"\n",
"tokenizer.padding_side = 'left' \n",
"\n",
"merged_model = AutoModelForCausalLM.from_pretrained(model_checkpoint_path, **model_kwargs)\n",
"\n",
"# move model to GPU \n",
"merged_model = merged_model.to(device) \n",
"\n",
"###############################################\n",
"# Create pipeline for inference\n",
"###############################################\n",
"\n",
"pipeline = pipeline(\"text-generation\", model=merged_model, tokenizer=tokenizer, torch_dtype=model_kwargs['torch_dtype']) \n",
"\n",
"pipeline([{\"role\": \"user\", \"content\": \"hi\"}], **generation_args)[0]['generated_text']"
],
"id": "3a62acfc182e1e65",
"outputs": [
{
"data": {
"text/plain": [
"Loading checkpoint shards: 0%| | 0/2 [00:00, ?it/s]"
],
"application/vnd.jupyter.widget-view+json": {
"version_major": 2,
"version_minor": 0,
"model_id": "2df33416573642fcb4c06cec112a115f"
}
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Device set to use cuda:0\n"
]
},
{
"data": {
"text/plain": [
"'Hello! How can I assist you today?'"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 1
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-06T04:28:37.702020Z",
"start_time": "2025-11-06T04:28:37.697801Z"
}
},
"cell_type": "code",
"source": [
"###############################################\n",
"# Mini-example on ABSA tasks\n",
"# The instruction is a prompt used for training\n",
"# and is licensed and distributed under CC BY-NC-SA 4.0.\n",
"# Copyright (c) 2025 Authors of https://arxiv.org/abs/2511.03034.\n",
"###############################################\n",
"\n",
"instruction = \"\"\"### Instruction:\n",
"Given the input text, extract ALL pairs of opinion expressions and their corresponding aspect terms about the course, staff, or university. Then classify the category and sentiment for each aspect-opinion pair.\n",
"Opinion expressions are words/phrases expressing evaluation, feeling, or judgment (including both explicit and implicit opinions, not objective facts).\n",
"Aspect terms are opinion targets. Only use a pronoun if you cannot find a direct aspect term in the same sentence or adjacent context. \n",
"Each aspect-opinion-category-sentiment combination is a quadruplet. \n",
"\n",
"**Rules:**\n",
"- Extract EVERY opinion in the text, including both explicit and implicit opinion expressions.\n",
"- Extract all opinion and aspect terms VERBATIM and as CONSECUTIVE tokens. \n",
"- Use 'null' for implicit aspects. Opinions cannot be null.\n",
"- If an aspect is mapped to multiple opinion expressions, or vice versa, extract each 1:1 pair separately. \n",
"- Categorise each aspect-opinion pair first into one main category (the keys) in the category_mapping below, and then into one of its appropriate subcategories (values for the key). The category label follows \"Main category - subcategory\" format.\n",
"category_mapping = {\n",
" \"Course\": [\"Content\", \"Learning activity\", \"Assessment\", \"Workload\", \"Difficulty\", \"Course materials\", \"Technology & tools\", \"Overall\"],\n",
" \"Staff\": [\"Teaching\", \"Knowledge & skills\", \"Helpfulness\", \"Attitude\", \"Personal traits\", \"Overall\"],\n",
" \"University\": [\"Cost\", \"Opportunities\", \"Programme\", \"Campus & facilities\", \"Culture & diversity\", \"Information & Services\", \"Social engagement & activities\", \"Overall\"]\n",
"}\n",
"\n",
"- Classify the sentiment into one of 'positive', 'neutral', 'negative'. \n",
"- Use these specific tags for each component within each quadruplet: aspect terms, opinion expressions, category, sentiment\n",
"\n",
"**Critical formatting requirements:**\n",
"- Output MUST be a valid Python list\n",
"- Quadruplets MUST be separated by commas\n",
"\n",
"**Output format:** \n",
"[............, ............, ..., ............]\n",
"\n",
"### Examples:\n",
"Input: \"The professor was knowledgeable but the assignments were too hard.\"\n",
"Output: [professorknowledgeableStaff - Knowledge & skillspositive, assignmentstoo hardCourse - Assessmentnegative]\n",
"\n",
"Input: \"It was disappointing overall.\"\n",
"Output: [nulldisappointingCourse - Overallnegative]\n",
"\n",
"Input: \"She never reply to emails or answer questions\"\n",
"Output: [Shenever reply to emails or answer questionsStaff - Helpfulnessnegative]\n",
"\n",
"Input: \"There were 10 assignments, 5 quizzes, 1 final exam.\"\n",
"Output: []\n",
"\"\"\"\n",
"\n",
"system_prompt = \"You are an expert in aspect-based sentiment analysis.\"\n",
"\n",
"test_input = \"The course itself was OK, but the lectures needed better organisation. The final assignment was waaaay too hard IMO, but what do I know I only got a B- ...\"\n",
"\n",
"user_content = f\"\"\"## Task type: ASQE\n",
"{instruction}\n",
"## Input text:\n",
"{test_input}\n",
"\"\"\"\n",
"\n",
"messages = [{\"role\": \"system\", \"content\": system_prompt},\n",
" {\"role\": \"user\", \"content\": user_content}]\n",
"\n",
"\n",
"def parse_output(output: str) -> list:\n",
" \"\"\"Parse ABSA output into DataFrame.\"\"\"\n",
" output = output.strip().strip('[]').strip()\n",
" quadruplets = re.split(r'(?=)', output)\n",
" quadruplets = [q.strip().rstrip(',').strip() for q in quadruplets if q.strip()]\n",
"\n",
" results = []\n",
" for quad in quadruplets:\n",
" aspect_match = re.search(r'(.*?)', quad)\n",
" opinion_match = re.search(r'(.*?)', quad)\n",
" category_match = re.search(r'(.*?)', quad)\n",
" sentiment_match = re.search(r'(.*?)', quad)\n",
"\n",
" if all([aspect_match, opinion_match, category_match, sentiment_match]):\n",
" results.append({\n",
" 'aspect': aspect_match.group(1).strip(),\n",
" 'opinion': opinion_match.group(1).strip(),\n",
" 'category': category_match.group(1).strip(),\n",
" 'sentiment': sentiment_match.group(1).strip()\n",
" })\n",
" return results"
],
"id": "8853d606a4ae5cb4",
"outputs": [],
"execution_count": 2
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-06T04:28:40.279836Z",
"start_time": "2025-11-06T04:28:37.784327Z"
}
},
"cell_type": "code",
"source": [
"raw_model_output = pipeline(messages, **generation_args)[0]['generated_text']\n",
"\n",
"output = parse_output(raw_model_output)\n",
"\n",
"print(test_input, '\\n')\n",
"\n",
"output"
],
"id": "78be9cff66c43965",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The course itself was OK, but the lectures needed better organisation. The final assignment was waaaay too hard IMO, but what do I know I only got a B- ... \n",
"\n"
]
},
{
"data": {
"text/plain": [
"[{'aspect': 'course',\n",
" 'opinion': 'OK',\n",
" 'category': 'Course - Overall',\n",
" 'sentiment': 'neutral'},\n",
" {'aspect': 'lectures',\n",
" 'opinion': 'needed better organisation',\n",
" 'category': 'Course - Learning activity',\n",
" 'sentiment': 'negative'},\n",
" {'aspect': 'final assignment',\n",
" 'opinion': 'waaaay too hard',\n",
" 'category': 'Course - Assessment',\n",
" 'sentiment': 'negative'}]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 3
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}