🧠 antfr99/mistral-7B-psycho-1960-movie

A fine-tuned and merged variant of Mistral-7B-Instruct-v0.3, themed around Alfred Hitchcock’s Psycho (1960)


🔬 Merge Behavior and Thematic Focus

This model merges a LoRA fine-tuned adapter trained on Psycho (1960) text with the base Mistral model. The merged result:

  • Retains general knowledge and conversational abilities of Mistral.
  • Leans heavily toward Psycho-focused analysis, producing responses in a psychological, suspenseful, or analytical tone.

🧩 Model Overview

Property Detail
Base Model mistralai/Mistral-7B-Instruct-v0.3
Fine-tuning Type QLoRA (Quantized Low-Rank Adaptation)
Quantization 4-bit NF4
Domain Focus Psychological analysis, classic suspense/horror cinema, character monologues
Persona Psychoanalytic narrative tone (1960s-inspired)
License MIT

🔬 Training Methodology

The model was fine-tuned using the QLoRA technique with Hugging Face’s trl SFTTrainer.

1. Dataset

  • Dataset name: psycho_datasetv2.jsonl
  • Source: Custom dataset (Google Drive)
  • Format: Instruction-based JSONL

Example format:

{"instruction": "Analyze the main character’s psychological state.", "response": "The subject displays..."}



💻 Inference Example (Google Colab + Gradio)

You can test the model interactively using the python code below. Change Runtime Type to T4 GPU.




# -------------------------
# 1️⃣ Install Dependencies
# -------------------------
!pip install -q --upgrade gradio transformers accelerate peft safetensors bitsandbytes huggingface_hub sentence-transformers datasets faiss-cpu

# -------------------------
# 2️⃣ Imports
# -------------------------
import gradio as gr
import torch, os, shutil, re
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from huggingface_hub import snapshot_download
from datasets import load_dataset
from sentence_transformers import SentenceTransformer
import faiss
import numpy as np

# -------------------------
# 3️⃣ Load Mistral Psycho Model
# -------------------------
model_name = "antfr99/mistral-7B-psycho-1960-movie"
expected_file = "model-001.safetensors"
target_file = "model.safetensors"

cache_path = snapshot_download(repo_id=model_name, allow_patterns=["*.json", "*.py", "*.model", "*.safetensors"])
src = os.path.join(cache_path, expected_file)
dst = os.path.join(cache_path, target_file)
if os.path.exists(src) and not os.path.exists(dst):
    try: os.symlink(src, dst)
    except OSError: shutil.copy2(src, dst)

tokenizer = AutoTokenizer.from_pretrained(cache_path)
quant_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_use_double_quant=True,
    bnb_4bit_compute_dtype=torch.float16
)
model = AutoModelForCausalLM.from_pretrained(
    cache_path,
    device_map="auto",
    torch_dtype=torch.float16,
    quantization_config=quant_config,
    trust_remote_code=True
)


# -------------------------
# 3️⃣ Load Psycho Dataset (Prompt-Completion)
# -------------------------
dataset = load_dataset("antfr99/psycho-1960-film-dataset", split="train")
psycho_passages = [f"{row['prompt']} {row['completion']}" for row in dataset]

# -------------------------
# 4️⃣ Build FAISS Index
# -------------------------
embed_model = SentenceTransformer('all-MiniLM-L6-v2')
passage_embeddings = embed_model.encode(psycho_passages, convert_to_numpy=True)
dim = passage_embeddings.shape[1]
index = faiss.IndexFlatL2(dim)
index.add(passage_embeddings)

# -------------------------
# 5️⃣ Stopwords / Psycho Keywords
# -------------------------
STOPWORDS = set([
    "the","in","her","his","their","a","an","and","or","of","on","for","to","with",
    "at","by","is","was","as","from","that","which","it","he","she","they","this",
    "these","those","but","not","had","have","has","also","after","before","so",
    "would","could","should","who","what","when","where","why","how","does","did",
    "do","i","you","we","me","my","your"
])

keyword_file_path = snapshot_download(repo_id=model_name, allow_patterns=["psycho_keywords.txt"])
with open(os.path.join(keyword_file_path, "psycho_keywords.txt"), "r", encoding="utf-8") as f:
    PSYCHO_REFERENCES = [line.strip() for line in f if line.strip() != ""]

# -------------------------
# 6️⃣ Prompt Validation
# -------------------------
def check_prompt_relevance(prompt):
    clean_prompt = re.sub(r"[’']", "", prompt)  # remove apostrophes
    words = re.findall(r"\b[A-Za-z0-9]+\b", clean_prompt.lower())  # lowercase all words

    # Preprocess Psycho keywords (lowercased and stripped)
    valid_terms = set([k.lower().rstrip('s') for k in PSYCHO_REFERENCES])
    
    # Ignore stopwords and simple plural/possessive forms
    non_psycho = []
    for w in words:
        w_clean = w.rstrip('s')  # handle "Crane’s" or "Crane" or "Cranes"
        if w_clean not in valid_terms and w not in STOPWORDS:
            non_psycho.append(w)

    if non_psycho:
        return "⚠️ Warning: Your question may contain terms NOT related to Psycho (1960): " + ", ".join(non_psycho)
    return "✅ Prompt appears Psycho-related."

# -------------------------
# 7️⃣ Response Generation with Retrieval
# -------------------------
def generate_response(prompt, max_new_tokens=256, temperature=0.2, top_p=0.98, top_k=30, strict_mode=True):
    # Validate prompt
    prompt_warning = check_prompt_relevance(prompt)
    if strict_mode and "⚠️" in prompt_warning:
        return f"{prompt_warning}\n\n❌ Model refusal: This question is not about Psycho (1960)."

    # Retrieve top 3 passages
    query_vec = embed_model.encode([prompt], convert_to_numpy=True)
    D, I = index.search(query_vec, k=3)
    top_passages = "\n".join([psycho_passages[i] for i in I[0]])

    # Format prompt for model
    system_prompt = (
        "You are a film scholar. Answer questions strictly about the 1960 film 'Psycho' by Alfred Hitchcock. "
        "Do not hallucinate. Base your answers only on the retrieved passages below:\n\n"
        f"{top_passages}"
    ) if strict_mode else ""
    
    formatted = f"<s>[INST] {system_prompt}\n\nUser: {prompt} [/INST]" if strict_mode else f"<s>[INST] {prompt} [/INST]"

    # Generate
    inputs = tokenizer(formatted, return_tensors="pt").to(model.device)
    with torch.no_grad():
        output = model.generate(
            **inputs,
            max_new_tokens=max_new_tokens,
            temperature=temperature,
            top_p=top_p,
            top_k=top_k,
            do_sample=True,
            pad_token_id=tokenizer.eos_token_id
        )
    response = tokenizer.decode(output[0][inputs.input_ids.shape[-1]:], skip_special_tokens=True)
    return f"{prompt_warning}\n\n{response}"

# -------------------------
# 8️⃣ Gradio Interface
# -------------------------

with gr.Blocks() as demo:
    gr.Markdown("### 🎥 Psycho (1960) Film Q&A Interface")

    # Input prompt (left) & output box (right)
    with gr.Row():
        prompt_input = gr.Textbox(lines=5, label="Input Prompt", placeholder="Ask about a scene in Psycho...")
        output_box = gr.Textbox(lines=5, label="Model Response", placeholder="Model output will appear here...")

    # Submit button below output
    with gr.Row():
        submit_btn = gr.Button("Submit")

    # When calling generate_response, provide default values for sliders/checkbox
    submit_btn.click(
        fn=generate_response,
        inputs=[prompt_input, 
                gr.State(256),   # max_tokens
                gr.State(0.2),   # temperature
                gr.State(0.98),  # top_p
                gr.State(30),    # top_k
                gr.State(True)], # strict_mode
        outputs=output_box
    )

demo.launch()
Downloads last month
19
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support