340 lines
14 KiB
Markdown
340 lines
14 KiB
Markdown
|
|
---
|
|||
|
|
library_name: transformers
|
|||
|
|
license: mit
|
|||
|
|
datasets:
|
|||
|
|
- yowww1094/tourism-llm-fine-tuning-dataset
|
|||
|
|
language:
|
|||
|
|
- en
|
|||
|
|
base_model:
|
|||
|
|
- Qwen/Qwen2.5-1.5B-Instruct
|
|||
|
|
tags:
|
|||
|
|
- tourism
|
|||
|
|
- travel
|
|||
|
|
- question-answering
|
|||
|
|
- lora
|
|||
|
|
- fine-tuned
|
|||
|
|
- rag
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Tourism Assistant — Qwen2.5-1.5B Fine-Tuned
|
|||
|
|
|
|||
|
|
A fine-tuned version of [Qwen/Qwen2.5-1.5B-Instruct](https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct) trained on a custom tourism Q&A dataset generated through a RAG-grounded pipeline. Built as a personal end-to-end learning project covering data collection, dataset engineering, supervised fine-tuning, and deployment.
|
|||
|
|
|
|||
|
|
> ⚠️ This model is not production-ready. It is a learning project. Outputs can be incorrect, incomplete, or inconsistent — especially on topics not well-represented in the training data. Do not rely on this model for real travel decisions.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Model Details
|
|||
|
|
|
|||
|
|
### Model Description
|
|||
|
|
|
|||
|
|
This model was fine-tuned using FP16 LoRA (Low-Rank Adaptation) on a small custom dataset of ~150–200 tourism-focused question-answer pairs. The training examples were generated through a RAG pipeline: Reddit travel posts were scraped, embedded, and stored in a Qdrant vector store, then a locally-hosted Qwen2.5-7B (via Ollama) was used to generate grounded Q&A pairs from retrieved context chunks. The resulting dataset was formatted in ChatML and used to fine-tune this model.
|
|||
|
|
|
|||
|
|
The goal of fine-tuning was to adjust the model's **behavioral style** — making it more focused, concise, and consistently helpful for travel queries — rather than to inject new factual knowledge. Factual grounding at inference time is handled by a RAG pipeline backed by Qdrant Cloud.
|
|||
|
|
|
|||
|
|
- **Developed by:** Younes
|
|||
|
|
- **Model type:** Causal Language Model — fine-tuned for instruction following
|
|||
|
|
- **Language:** English
|
|||
|
|
- **License:** MIT
|
|||
|
|
- **Base model:** [Qwen/Qwen2.5-1.5B-Instruct](https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct)
|
|||
|
|
- **Fine-tuning method:** FP16 LoRA (merged into base weights)
|
|||
|
|
- **Training dataset:** [yowww1094/tourism-llm-fine-tuning-dataset](https://huggingface.co/datasets/yowww1094/tourism-llm-fine-tuning-dataset)
|
|||
|
|
|
|||
|
|
### Model Sources
|
|||
|
|
|
|||
|
|
- **Repository:** [Github](https://github.com/yowww1094/llm-tourism-assistant)
|
|||
|
|
- **Demo:** [Demo](https://huggingface.co/spaces/yowww1094/AI-tourism-chatbot)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Uses
|
|||
|
|
|
|||
|
|
### Direct Use
|
|||
|
|
|
|||
|
|
This model can be used as a conversational assistant for general tourism and travel questions — destination information, logistics, visa guidance, packing advice, and similar topics. It works best when paired with a retrieval pipeline that provides relevant context at inference time.
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from transformers import AutoModelForCausalLM, AutoTokenizer
|
|||
|
|
|
|||
|
|
model_id = "yowww1094/tourism-assistant-qwen2"
|
|||
|
|
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
|||
|
|
model = AutoModelForCausalLM.from_pretrained(model_id, torch_dtype="auto", device_map="auto")
|
|||
|
|
|
|||
|
|
messages = [
|
|||
|
|
{"role": "system", "content": "You are a helpful tourism assistant."},
|
|||
|
|
{"role": "user", "content": "What are the must-visit places in Marrakech?"}
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
|
|||
|
|
inputs = tokenizer([text], return_tensors="pt").to(model.device)
|
|||
|
|
|
|||
|
|
outputs = model.generate(**inputs, max_new_tokens=256, temperature=0.7, do_sample=True)
|
|||
|
|
response = tokenizer.decode(outputs[0][len(inputs.input_ids[0]):], skip_special_tokens=True)
|
|||
|
|
print(response)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Downstream Use
|
|||
|
|
|
|||
|
|
This model is designed to be used as the generation component of a RAG pipeline. The recommended usage is:
|
|||
|
|
|
|||
|
|
1. Embed the user query with `all-MiniLM-L6-v2`
|
|||
|
|
2. Retrieve top-k relevant chunks from a Qdrant vector store
|
|||
|
|
3. Inject retrieved context into the prompt before generation
|
|||
|
|
4. Pass the full prompt to this model
|
|||
|
|
|
|||
|
|
Pairing the model with retrieval significantly improves factual accuracy on specific travel queries compared to using the model standalone.
|
|||
|
|
|
|||
|
|
### Out-of-Scope Use
|
|||
|
|
|
|||
|
|
- **Real-time travel information:** The model has no access to live data. Flight prices, visa requirements, and safety conditions change frequently — do not rely on this model for current information.
|
|||
|
|
- **Medical or legal travel advice:** The model is not equipped to give reliable guidance on health requirements, legal restrictions, or emergency situations.
|
|||
|
|
- **Non-English queries:** The model was trained exclusively on English data and is not reliable for other languages.
|
|||
|
|
- **High-stakes decisions:** This is a learning project. Outputs should not be used to make actual travel bookings, visa applications, or safety assessments.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Bias, Risks, and Limitations
|
|||
|
|
|
|||
|
|
**Dataset limitations — primary source of inaccuracy:**
|
|||
|
|
The training dataset contains only ~150–200 examples sourced from Reddit travel communities. This introduces several compounding problems:
|
|||
|
|
|
|||
|
|
- Reddit's user base skews toward English-speaking, Western travellers — advice reflects this demographic and may not generalise to other travel styles or origins
|
|||
|
|
- The dataset covers only a narrow slice of the tourism domain; large topic areas have no representation
|
|||
|
|
- Source Reddit posts were not independently fact-checked; incorrect or outdated community advice may appear in training examples
|
|||
|
|
- Only ~40 examples (~20%) were manually reviewed for quality before training
|
|||
|
|
|
|||
|
|
**Model behaviour limitations:**
|
|||
|
|
- The model hallucinates specific facts (prices, distances, operating hours, visa fees) when retrieved context does not provide explicit grounding
|
|||
|
|
- It does not reliably abstain from answering when it does not know — it tends to produce a confident-sounding response regardless
|
|||
|
|
- Response consistency is variable; the same query may produce meaningfully different answers across runs
|
|||
|
|
- The model may reflect biases present in Reddit data, including opinions presented as facts
|
|||
|
|
|
|||
|
|
### Recommendations
|
|||
|
|
|
|||
|
|
- Always pair this model with a retrieval pipeline for factual queries
|
|||
|
|
- Display a disclaimer to end users that outputs may be inaccurate
|
|||
|
|
- Do not deploy in contexts where incorrect travel information could cause harm
|
|||
|
|
- Treat all specific factual claims (prices, hours, requirements) as unverified until confirmed from an authoritative source
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## How to Get Started with the Model
|
|||
|
|
|
|||
|
|
### Basic inference (no RAG)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from transformers import AutoModelForCausalLM, AutoTokenizer
|
|||
|
|
import torch
|
|||
|
|
|
|||
|
|
model_id = "yowww1094/tourism-assistant-qwen2"
|
|||
|
|
|
|||
|
|
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
|||
|
|
model = AutoModelForCausalLM.from_pretrained(
|
|||
|
|
model_id,
|
|||
|
|
torch_dtype=torch.float16,
|
|||
|
|
device_map="auto"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
messages = [
|
|||
|
|
{"role": "system", "content": "You are a helpful tourism assistant. Answer travel questions clearly and concisely."},
|
|||
|
|
{"role": "user", "content": "What is the best time of year to visit Morocco?"}
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
|
|||
|
|
inputs = tokenizer([text], return_tensors="pt").to(model.device)
|
|||
|
|
|
|||
|
|
with torch.no_grad():
|
|||
|
|
outputs = model.generate(
|
|||
|
|
**inputs,
|
|||
|
|
max_new_tokens=300,
|
|||
|
|
temperature=0.7,
|
|||
|
|
top_p=0.9,
|
|||
|
|
do_sample=True,
|
|||
|
|
repetition_penalty=1.1
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
response = tokenizer.decode(outputs[0][len(inputs.input_ids[0]):], skip_special_tokens=True)
|
|||
|
|
print(response)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Recommended inference (with RAG context)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# retrieved_context = top-k chunks from your Qdrant vector store
|
|||
|
|
|
|||
|
|
system_prompt = "You are a helpful tourism assistant. Use only the provided context to answer. If the context does not contain enough information, say so."
|
|||
|
|
|
|||
|
|
user_prompt = f"""Context:
|
|||
|
|
{retrieved_context}
|
|||
|
|
|
|||
|
|
Question: {user_query}"""
|
|||
|
|
|
|||
|
|
messages = [
|
|||
|
|
{"role": "system", "content": system_prompt},
|
|||
|
|
{"role": "user", "content": user_prompt}
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
# then apply chat template and generate as above
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Training Details
|
|||
|
|
|
|||
|
|
### Training Data
|
|||
|
|
|
|||
|
|
The model was fine-tuned on [yowww1094/tourism-llm-fine-tuning-dataset](https://huggingface.co/datasets/yowww1094/tourism-llm-fine-tuning-dataset), a custom dataset generated through the following pipeline:
|
|||
|
|
|
|||
|
|
1. Reddit travel posts and comments were scraped from subreddits including r/travel, r/Morocco, r/solotravel, and r/backpacking using PRAW
|
|||
|
|
2. Raw text was cleaned (length filter, deduplication, HTML stripping, encoding fix) and chunked
|
|||
|
|
3. Chunks were embedded with `all-MiniLM-L6-v2` and indexed in a Qdrant vector store
|
|||
|
|
4. A locally-hosted Qwen2.5-7B (via Ollama) was prompted to generate grounded question-answer pairs from retrieved context chunks
|
|||
|
|
5. Outputs were post-processed into ChatML format and manually sampled for quality (~40 examples reviewed)
|
|||
|
|
|
|||
|
|
**Dataset size:** ~150–200 question-answer pairs
|
|||
|
|
**Format:** JSON Lines
|
|||
|
|
**Split:** 90% train / 10% validation
|
|||
|
|
**Quality note:** Only ~65% of manually reviewed examples were rated acceptable. No automated quality filter was applied to the full dataset.
|
|||
|
|
|
|||
|
|
### Training Procedure
|
|||
|
|
|
|||
|
|
#### Preprocessing
|
|||
|
|
|
|||
|
|
Each example was formatted using the Qwen2.5-Instruct chat template via `tokenizer.apply_chat_template()`. Sequences were truncated to a maximum length of 512 tokens. No data augmentation was applied.
|
|||
|
|
|
|||
|
|
#### Training Hyperparameters
|
|||
|
|
|
|||
|
|
| Parameter | Value |
|
|||
|
|
|---|---|
|
|||
|
|
| Training regime | FP16 mixed precision |
|
|||
|
|
| LoRA rank (r) | 16 |
|
|||
|
|
| LoRA alpha | 32 |
|
|||
|
|
| LoRA dropout | 0.05 |
|
|||
|
|
| Target modules | q_proj, v_proj |
|
|||
|
|
| Epochs | 3 |
|
|||
|
|
| Batch size | 4 |
|
|||
|
|
| Gradient accumulation steps | 4 (effective batch size: 16) |
|
|||
|
|
| Learning rate | 2e-4 |
|
|||
|
|
| LR scheduler | Cosine annealing |
|
|||
|
|
| Warmup ratio | 0.03 |
|
|||
|
|
| Max sequence length | 512 tokens |
|
|||
|
|
| Optimizer | AdamW (default via transformers) |
|
|||
|
|
|
|||
|
|
#### Hardware and Duration
|
|||
|
|
|
|||
|
|
- **Hardware:** NVIDIA T4 GPU (15 GB VRAM) — Kaggle free tier
|
|||
|
|
- **Training time:** ~45–60 minutes for 3 epochs on ~150 examples
|
|||
|
|
- **Framework:** transformers 4.x + peft + trl (SFTTrainer) + accelerate
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Evaluation
|
|||
|
|
|
|||
|
|
### Testing Data
|
|||
|
|
|
|||
|
|
Evaluation was performed on a held-out validation split of ~15–20 examples (10% of the total dataset). Due to the small size of this split, quantitative metrics should be interpreted with significant caution — they are not statistically reliable estimates of generalisation performance.
|
|||
|
|
|
|||
|
|
In addition to automatic metrics, a qualitative evaluation was performed by manually inspecting model outputs on ~20 held-out queries not present in the training data.
|
|||
|
|
|
|||
|
|
### Metrics
|
|||
|
|
|
|||
|
|
| Metric | Value | Notes |
|
|||
|
|
|---|---|---|
|
|||
|
|
| Training loss (epoch 1) | ~1.62 | |
|
|||
|
|
| Training loss (epoch 3) | ~0.85 | Consistent decrease across epochs |
|
|||
|
|
| Validation loss (epoch 1) | ~1.74 | |
|
|||
|
|
| Validation loss (epoch 3) | ~1.29 | Slight divergence from train loss — mild overfitting |
|
|||
|
|
| Validation perplexity | ~2.3 | On 15–20 examples only; not a reliable generalisation estimate |
|
|||
|
|
|
|||
|
|
### Results
|
|||
|
|
|
|||
|
|
**What improved after fine-tuning:**
|
|||
|
|
- Response style and tone became more focused and consistently helpful compared to the base model
|
|||
|
|
- The model more readily uses tourism-relevant vocabulary and response structure
|
|||
|
|
- For in-distribution queries (topics well-represented in the dataset), the combination of fine-tuning + RAG outperforms either mechanism alone
|
|||
|
|
|
|||
|
|
**What did not improve:**
|
|||
|
|
- Factual accuracy on out-of-distribution queries is comparable to the base model — fine-tuning at this scale does not inject meaningful new factual knowledge
|
|||
|
|
- Hallucination rate on specific facts (prices, dates, requirements) is unchanged without retrieval grounding
|
|||
|
|
- The model occasionally produces responses that closely paraphrase training examples, suggesting partial memorisation
|
|||
|
|
|
|||
|
|
#### Summary
|
|||
|
|
|
|||
|
|
Fine-tuning on this small dataset improved behavioral style but not factual coverage. The model is more useful when paired with a retrieval pipeline than when used standalone. A dataset 10–20× larger with verified factual content would be needed to produce a genuinely reliable tourism assistant.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Environmental Impact
|
|||
|
|
|
|||
|
|
Carbon emissions were not formally measured. Estimated figures based on training setup:
|
|||
|
|
|
|||
|
|
- **Hardware type:** NVIDIA T4 GPU (Kaggle free tier)
|
|||
|
|
- **Hours used:** ~1 hour total training time
|
|||
|
|
- **Cloud provider:** Google (Kaggle infrastructure)
|
|||
|
|
- **Compute region:** Unknown
|
|||
|
|
- **Carbon emitted:** Estimated < 0.05 kg CO₂eq (based on [ML Impact Calculator](https://mlco2.github.io/impact#compute))
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Technical Specifications
|
|||
|
|
|
|||
|
|
### Model Architecture
|
|||
|
|
|
|||
|
|
- **Architecture:** Qwen2.5 decoder-only transformer (causal LM)
|
|||
|
|
- **Parameters:** 1.5 billion (base model)
|
|||
|
|
- **Context window:** 32,768 tokens (base model capability; fine-tuning used max 512 tokens)
|
|||
|
|
- **Fine-tuning method:** LoRA adapters applied to `q_proj` and `v_proj` attention matrices, then merged into base weights before upload
|
|||
|
|
|
|||
|
|
### Compute Infrastructure
|
|||
|
|
|
|||
|
|
- **Training:** Kaggle free-tier notebook, T4 GPU, 15 GB VRAM
|
|||
|
|
- **Inference:** Hugging Face Inference Endpoints (free tier)
|
|||
|
|
- **Vector store:** Qdrant Cloud (free tier, ~1,200 indexed chunks)
|
|||
|
|
- **Embedding:** `sentence-transformers/all-MiniLM-L6-v2`, CPU inference
|
|||
|
|
|
|||
|
|
### Software
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
transformers>=4.40.0
|
|||
|
|
peft>=0.10.0
|
|||
|
|
trl>=0.8.0
|
|||
|
|
accelerate>=0.28.0
|
|||
|
|
sentence-transformers>=2.6.0
|
|||
|
|
qdrant-client>=1.9.0
|
|||
|
|
torch>=2.1.0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Citation
|
|||
|
|
|
|||
|
|
If you reference this project, please cite it as:
|
|||
|
|
|
|||
|
|
```bibtex
|
|||
|
|
@misc{younes2026tourismllm,
|
|||
|
|
author = {Younes},
|
|||
|
|
title = {End-to-End LLM Pipeline for Tourism Assistant},
|
|||
|
|
year = {2026},
|
|||
|
|
publisher = {Hugging Face},
|
|||
|
|
url = {[Model](https://huggingface.co/yowww1094/tourism-llm-fine-tuned-qwen2-1.5b-lora-merged)}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## More Information
|
|||
|
|
|
|||
|
|
Full technical report (covering pipeline design, training decisions, limitations, and results in detail):
|
|||
|
|
[docs/technical_report.pdf](https://github.com/yowww1094/llm-tourism-assistant/blob/master/docs/technical_report.pdf)
|
|||
|
|
|
|||
|
|
GitHub repository with full pipeline code:
|
|||
|
|
[Github](https://github.com/yowww1094/llm-tourism-assistant)
|
|||
|
|
|
|||
|
|
Training dataset:
|
|||
|
|
[yowww1094/tourism-llm-fine-tuning-dataset](https://huggingface.co/datasets/yowww1094/tourism-llm-fine-tuning-dataset)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Model Card Author
|
|||
|
|
|
|||
|
|
Younes AIT SI ABBOU — personal learning project, April 2026
|