← Back to Projects

Genre-Controlled Story Generation using QLoRA

Feb 2026 - May 2026
LLM Fine-Tuning / QLoRA / Streamlit

Project Type

  • Association: University of Missouri-Kansas City
  • Role: Applied AI Developer focused on LLM fine-tuning and controlled generation
  • Focus: Reducing generic LLM storytelling by improving genre alignment, tone consistency, and narrative control
  • Interface: Interactive Streamlit app for selecting story genres and generating genre-aligned narratives

Objective

Built an end-to-end genre-controlled short story generation system using google/gemma-3-1b-it and QLoRA adaptation. I created a manually annotated fantasy, romance, and science-fiction subset from the WritingPrompts dataset, cleaned and preprocessed 709 prompt-story examples, fine-tuned the pretrained model with 4-bit QLoRA, compared the adapted model against the base model using validation loss, perplexity, and LLM-as-judge evaluation, and deployed the workflow through a Streamlit demo for interactive story generation.

Tools & Technologies

PythonPyTorchHugging FacePEFTQLoRAGemma 3-1BLLM-as-JudgeStreamlit

Project Details

Problem: General-purpose pretrained LLMs can generate fluent short stories, but they often struggle with controlled creative generation. When a user asks for a fantasy, romance, or science-fiction story, the model may produce readable text while still drifting away from the requested genre, losing tone consistency, repeating ideas, or failing to preserve a clean narrative ending. Goal: This project tests whether QLoRA-based parameter-efficient fine-tuning can improve genre-conditioned short story generation under limited compute resources.

Source: I used a processed subset of the WritingPrompts dataset, originally collected from a Reddit creative writing community and accessed through Kaggle. The full dataset contains approximately 272K prompt-story pairs. Genre Scope: For this project, I focused only on fantasy, romance, and science fiction because these genres have distinct narrative patterns, tone, vocabulary, and structure. Manual Annotation: Since the raw dataset was not directly structured for this task, I manually annotated and organized examples into the target genres. Each final example contained a genre label, a story prompt, and a target story. Final Dataset Size: The final processed dataset contained 709 examples: 567 training examples, 70 validation examples, and 72 test examples.

Text Cleaning: I normalized Unicode text using NFKC, replaced <newline> markers with real line breaks, standardized carriage returns, cleaned whitespace, and preserved paragraph structure where needed. Quality Filtering: I removed deleted, removed, empty, noisy, or incomplete stories. I also removed prompt artifacts and meta-writing language that could weaken the story-generation task. Length Filtering: I filtered stories shorter than 180 words or longer than 450 words so the model could focus on short-story generation rather than extremely short fragments or long-form stories. Narrative Quality Checks: I checked that stories ended with a clean final sentence and removed examples with repeated full sentences to reduce repetition issues during fine-tuning.

Instruction Format: Each cleaned example was converted into a supervised fine-tuning format with an explicit genre, prompt, and story structure. Input Structure: The formatted examples followed this pattern: <GENRE>, followed by the selected genre, then <PROMPT>, followed by the story prompt, and finally <STORY>, followed by the target story. Why This Format: This structure helped the model learn that story generation should be conditioned on both the user prompt and the selected genre, instead of only continuing generic text.

Base Model: I used google/gemma-3-1b-it as the pretrained base model. Tokenizer: I loaded the tokenizer using AutoTokenizer from the same google/gemma-3-1b-it checkpoint to keep tokenization consistent between the base and adapted systems. Task Setup: The project was framed as causal language modeling for genre-controlled short story generation. Sequence Length: I used a maximum sequence length of 1024 tokens for supervised fine-tuning and generated outputs with approximately 300 new tokens.

Adaptation Method: I used QLoRA to adapt the pretrained model efficiently instead of training the full model from scratch. Why QLoRA: QLoRA makes fine-tuning more practical under limited compute by using 4-bit quantization and training lightweight LoRA adapter parameters while keeping the original base model weights frozen. Trainable Parameters: Only the LoRA adapter weights were updated during training. The base Gemma model weights remained frozen. Target Modules: I applied LoRA to self_attn.q_proj and self_attn.v_proj so the model could adapt attention behavior for genre-conditioned generation.

Quantization Setup: The adapted model used 4-bit NF4 quantization with double quantization and bfloat16 compute. LoRA Setup: The LoRA rank was 16, LoRA alpha was 16, and LoRA dropout was 0.10. Optimization Setup: I used paged_adamw_32bit as the optimizer, a learning rate of 5e-5, batch size 1, and gradient accumulation steps of 4. Training Constraint: This setup allowed the model to adapt to fantasy, romance, and science-fiction writing patterns while keeping memory usage manageable.

Systems Compared: I compared two systems: the original pretrained google/gemma-3-1b-it model without adaptation and the same model after QLoRA fine-tuning. Fair Comparison Setup: Both systems used the same task definition, same held-out evaluation set, same prompt format, same tokenizer, same generation settings, same quantitative metrics, and same qualitative review process. Purpose: The comparison was designed to test whether QLoRA adaptation improved genre-conditioned story generation compared with the base pretrained model.

Metrics Used: I evaluated both systems using validation loss and perplexity on the same held-out evaluation set. Base Model Result: The base Gemma-3-1B-it model achieved a validation loss of 3.8268 and perplexity of 45.9134. QLoRA Model Result: The QLoRA-adapted model achieved a validation loss of 3.3188 and perplexity of 27.6261. Improvement: QLoRA adaptation reduced perplexity by approximately 39.8%, showing stronger token-level modeling performance on the genre-controlled evaluation set.

Review Criteria: I evaluated generated stories using genre fidelity, prompt relevance, coherence, ending quality, repetition, style quality, format following, and overall quality. Judge Model: I used llama-3.1-8b-instant through the Groq API as an LLM-as-judge evaluator for sample outputs. Important Limitation: I treated the LLM judge as a supporting evaluation tool, not as the only source of judgment, because creative writing quality is subjective and sample-level judging can vary.

Base Model Score: On the small judged sample, the base model achieved an overall score of 3.767. QLoRA Model Score: The QLoRA-adapted model achieved an overall score of 3.000 on the same judged sample. Interpretation: These results showed that although QLoRA improved validation loss and perplexity, it did not consistently improve perceived creative-writing quality in the small qualitative sample. This highlighted an important lesson: lower token-level error does not automatically guarantee better narrative quality, style, or reader preference.

Interface Goal: I built a Streamlit application to make the model workflow usable and interactive instead of leaving it only as a notebook experiment. User Flow: The app allows users to select a genre, enter a story prompt, generate a short story, inspect model outputs, and view the end-to-end workflow interactively. Demo Value: This turned the fine-tuned model into a practical prototype for controlled creative text generation.

Outcome: The project produced an end-to-end genre-controlled short story generation system using Gemma-3-1B-it, QLoRA adaptation, structured preprocessing, quantitative evaluation, qualitative review, and Streamlit deployment. Main Result: QLoRA improved validation loss from 3.8268 to 3.3188 and reduced perplexity from 45.9134 to 27.6261. Key Learning: The project showed that parameter-efficient fine-tuning can improve token-level performance under limited compute, but creative generation still requires careful qualitative evaluation because better perplexity does not always mean better perceived story quality.