Spaces:
No application file
No application file
Delete app.py
Browse files
app.py
DELETED
|
@@ -1,232 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""Practica9.ipynb
|
| 3 |
-
|
| 4 |
-
Automatically generated by Colab.
|
| 5 |
-
|
| 6 |
-
Original file is located at
|
| 7 |
-
https://colab.research.google.com/github/AP2324/practica-9-anhidalg/blob/main/Practica9.ipynb
|
| 8 |
-
|
| 9 |
-
# Práctica 9
|
| 10 |
-
|
| 11 |
-
Ahora es tu turno de crear un sistema de question-answering.
|
| 12 |
-
|
| 13 |
-
## Ejercicio obligatorio (5 puntos)
|
| 14 |
-
|
| 15 |
-
El ejercicio que tenéis que realizar obligatoriamente consiste en elegir uno o varios documentos y crear un sistema de question-answering basado en RAG como el que hemos visto en la práctica. Deberás completar los ejercicios planteados en el informe de prácticas, ten en cuenta que en los ejercicios planteados puedes tener que realizar algún cambio en el código proporcionado.
|
| 16 |
-
|
| 17 |
-
## Ejercicios adicionales
|
| 18 |
-
|
| 19 |
-
Debes crear un nuevo notebook para cada uno de los ejercicios que realices, completar los ejercicios planteados en el informe y guardarlo en este repositorio de GitHub.
|
| 20 |
-
|
| 21 |
-
1. Crea un espacio de HuggingFace para utilizar el sistema que has construido. Deberás usar la componente [chat de Gradio](https://www.gradio.app/docs/chatbot). (1 punto)
|
| 22 |
-
2. A partir de tus documentos construye un dataset de preguntas y respuestas, y evalúa tu sistema contra dicho dataset. Para ello puedes basarte en el siguiente [tutorial](https://huggingface.co/learn/cookbook/rag_evaluation) (2 puntos).
|
| 23 |
-
3. Compara distintos modelos con el dataset que has definido en el apartado anterior (1 punto).
|
| 24 |
-
4. Construye un sistema de question-answering que en lugar de utilizar un documento pdf como entrada, utilice un vídeo de YouTube, para ello deberás transcribirlo primero (1 punto).
|
| 25 |
-
"""
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
"""Al finalizar, recuerda guardar los cambios en GitHub utilizando la opción Archivo -> Guardar una copia en GitHub."""
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
from torch import cuda, bfloat16
|
| 34 |
-
import torch
|
| 35 |
-
import transformers
|
| 36 |
-
from transformers import AutoTokenizer
|
| 37 |
-
from time import time
|
| 38 |
-
#import chromadb
|
| 39 |
-
#from chromadb.config import Settings
|
| 40 |
-
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
| 41 |
-
from langchain.embeddings import HuggingFaceEmbeddings
|
| 42 |
-
from langchain.chains import RetrievalQA
|
| 43 |
-
from langchain.vectorstores import Chroma
|
| 44 |
-
from langchain.document_loaders import PyPDFLoader
|
| 45 |
-
import requests
|
| 46 |
-
|
| 47 |
-
"""**Iniciamos el modelo y el tokenizador**
|
| 48 |
-
|
| 49 |
-
Definimos el identificador del modelo
|
| 50 |
-
"""
|
| 51 |
-
|
| 52 |
-
model_id = 'mistralai/Mistral-7B-Instruct-v0.1'
|
| 53 |
-
|
| 54 |
-
"""Indicamos que el device va a ser la GPU si está disponible"""
|
| 55 |
-
|
| 56 |
-
device = f'cuda:{cuda.current_device()}' if cuda.is_available() else 'cpu'
|
| 57 |
-
|
| 58 |
-
"""Fijamos la configuración para cuantizar el modelo y poder cargarlo en GPU."""
|
| 59 |
-
|
| 60 |
-
bnb_config = transformers.BitsAndBytesConfig(
|
| 61 |
-
load_in_4bit=True,
|
| 62 |
-
bnb_4bit_quant_type='nf4',
|
| 63 |
-
bnb_4bit_use_double_quant=True,
|
| 64 |
-
bnb_4bit_compute_dtype=bfloat16
|
| 65 |
-
)
|
| 66 |
-
|
| 67 |
-
"""A continuación cargamos el modelo, para ello primero hay que cargar su configuración, y luego el modelo. Esta tarea puede llevar algo más de 2 minutos debido a que se debe descargar el modelo y luego cargarlo."""
|
| 68 |
-
|
| 69 |
-
model_config = transformers.AutoConfig.from_pretrained(
|
| 70 |
-
model_id,
|
| 71 |
-
max_new_tokens=200
|
| 72 |
-
)
|
| 73 |
-
|
| 74 |
-
model = transformers.AutoModelForCausalLM.from_pretrained(
|
| 75 |
-
model_id,
|
| 76 |
-
trust_remote_code=True,
|
| 77 |
-
config=model_config,
|
| 78 |
-
quantization_config=bnb_config,
|
| 79 |
-
device_map='auto',
|
| 80 |
-
)
|
| 81 |
-
|
| 82 |
-
"""Cargamos a continuación el tokenizer que se utilizará para procesar los textos."""
|
| 83 |
-
|
| 84 |
-
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
| 85 |
-
|
| 86 |
-
"""Ya tenemos todas las componentes para definir un pipeline que responda preguntas usando el modelo que acabamos de cargar, para ello definimos un pipeline y a continuación definimos una función que nos permite utilizar dicho pipeline."""
|
| 87 |
-
|
| 88 |
-
query_pipeline = transformers.pipeline(
|
| 89 |
-
"text-generation",
|
| 90 |
-
model=model,
|
| 91 |
-
tokenizer=tokenizer,
|
| 92 |
-
torch_dtype=torch.float16,
|
| 93 |
-
device_map="auto", max_new_tokens=200)
|
| 94 |
-
|
| 95 |
-
"""Para usar el pipeline como estamos usando un modelo estilo chat, vamos a utilizar las chat templates de HuggingFace. Notar que cuando llamamos a la pipeline estamos fijando distintos parámetros que son:
|
| 96 |
-
|
| 97 |
-
1. temperature: valor usado para modular las probabilidades de los siguientes
|
| 98 |
-
tokens.
|
| 99 |
-
2. do_sample: booleano que indica si realiza sampling sobre el siguiente token a generar o si se usa una estrategia avariciosa.
|
| 100 |
-
3. top_k: el número de tokens del vocabulario con probabilidad más alta que se mantienen para hacer top-k-filtering.
|
| 101 |
-
4. top_p: real tal que el menor conjunto de los tokens más probables cuyas probabilidades suman hasta el valor top_p se mantienen para la generación.
|
| 102 |
-
"""
|
| 103 |
-
|
| 104 |
-
def test_model(tokenizer, pipeline, prompt_to_test):
|
| 105 |
-
"""
|
| 106 |
-
Perform a query
|
| 107 |
-
print the result
|
| 108 |
-
Args:
|
| 109 |
-
tokenizer: the tokenizer
|
| 110 |
-
pipeline: the pipeline
|
| 111 |
-
prompt_to_test: the prompt
|
| 112 |
-
Returns
|
| 113 |
-
None
|
| 114 |
-
"""
|
| 115 |
-
# adapted from https://huggingface.co/blog/llama2#using-transformers
|
| 116 |
-
time_1 = time()
|
| 117 |
-
# Usamos la chat template.
|
| 118 |
-
messages = [{"role": "user", "content": prompt_to_test}]
|
| 119 |
-
prompt = pipeline.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
|
| 120 |
-
|
| 121 |
-
sequences = pipeline(
|
| 122 |
-
prompt,
|
| 123 |
-
do_sample=True,
|
| 124 |
-
temperature=0.7, top_k=50, top_p=0.95,
|
| 125 |
-
num_return_sequences=1,
|
| 126 |
-
eos_token_id=tokenizer.eos_token_id)
|
| 127 |
-
time_2 = time()
|
| 128 |
-
print(f"Test inference: {round(time_2-time_1, 3)} sec.")
|
| 129 |
-
for seq in sequences:
|
| 130 |
-
res = seq['generated_text']
|
| 131 |
-
print(f"Result: {res[res.rfind('[/INST]')+8:]}")
|
| 132 |
-
|
| 133 |
-
"""Ahora podemos utilizar esta pipeline para hacer preguntas sobre diabetes, pero en general sobre cualquier tema."""
|
| 134 |
-
|
| 135 |
-
test_model(tokenizer,
|
| 136 |
-
query_pipeline,
|
| 137 |
-
"Por favor, indícame cuál es el teorema del cálculo ")
|
| 138 |
-
|
| 139 |
-
test_model(tokenizer,
|
| 140 |
-
query_pipeline,
|
| 141 |
-
"Dame la regla de Barrow")
|
| 142 |
-
|
| 143 |
-
"""**RAG**
|
| 144 |
-
|
| 145 |
-
En este caso para generar nuestras respuestas vamos a utilizar una guía sobre derivadas e integrales. Descargamos a continuación dicha guía
|
| 146 |
-
"""
|
| 147 |
-
|
| 148 |
-
URL = "http://www.ugr.es/~rpaya/documentos/CalculoII/2011-12/TFC.pdf"
|
| 149 |
-
response = requests.get(URL)
|
| 150 |
-
open("TFC.pdf", "wb").write(response.content)
|
| 151 |
-
|
| 152 |
-
loader = PyPDFLoader("TFC.pdf")
|
| 153 |
-
documents = loader.load()
|
| 154 |
-
|
| 155 |
-
"""A continuación vamos a partir el documento en trozos (chunks) usando una partición basada en caracteres recursiva."""
|
| 156 |
-
|
| 157 |
-
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=20)
|
| 158 |
-
all_splits = text_splitter.split_documents(documents)
|
| 159 |
-
|
| 160 |
-
"""**Creando los embeddings y almacenándolos**
|
| 161 |
-
|
| 162 |
-
El siguiente paso es crear los embeddings usando la librería Sentence Transformer y los embeddings de HuggingFace. Esta librería dispone una gran variedad de modelos de embedding, al estar trabajando con un documento en castellano vamos a utilizar un modelo multilingue.
|
| 163 |
-
"""
|
| 164 |
-
|
| 165 |
-
model_name = "sentence-transformers/paraphrase-multilingual-mpnet-base-v2"
|
| 166 |
-
model_kwargs = {"device": "cuda"}
|
| 167 |
-
|
| 168 |
-
embeddings = HuggingFaceEmbeddings(model_name=model_name, model_kwargs=model_kwargs)
|
| 169 |
-
|
| 170 |
-
"""El siguiente paso consiste en inicializar la base de datos con los documentos partidos, y usando los embeddings definidos previamente. Elegimos también persistir la información localmente en un directorio llamado chroma_db."""
|
| 171 |
-
|
| 172 |
-
vectordb = Chroma.from_documents(documents=all_splits, embedding=embeddings, persist_directory="chroma_db")
|
| 173 |
-
|
| 174 |
-
"""**Retrieval-Augmented Generation**
|
| 175 |
-
|
| 176 |
-
Ya tenemos todas las componentes para definir una función que ante una query busque los trozos de documentos más similares a la misma y genere una respuesta en base a ellos. Para ello el proceso que seguimos es el siguiente:
|
| 177 |
-
|
| 178 |
-
1. Buscamos aquellos trozos de los documentos que son más similares a nuestra query.
|
| 179 |
-
2. Para cada uno de los trozos encontrados extraemos el contenido de los mismos y su similitud con la query.
|
| 180 |
-
3. Si hay documentos lo suficientemente cercanos, llamamos al modelo para generar una respuesta en base a ello, en caso contrario generamos una respuesta por defecto.
|
| 181 |
-
"""
|
| 182 |
-
|
| 183 |
-
import gradio as gr
|
| 184 |
-
from transformers import pipeline, AutoModelForCausalLM, AutoTokenizer, AutoConfig
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
# Definir la función interna para testear RAG
|
| 188 |
-
def _test_rag(query, query_pipeline):
|
| 189 |
-
docs = vectordb.similarity_search_with_score(query)
|
| 190 |
-
context = []
|
| 191 |
-
for doc, score in docs:
|
| 192 |
-
if score < 7:
|
| 193 |
-
doc_details = doc.to_json()['kwargs']
|
| 194 |
-
context.append(doc_details['page_content'])
|
| 195 |
-
if len(context) != 0:
|
| 196 |
-
messages = [{"role": "user", "content": "Basándote en la siguiente información:\n" + "\n".join(context) + "\nPor favor, hazme una pregunta en español:"}]
|
| 197 |
-
prompt = query_pipeline.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
|
| 198 |
-
outputs = query_pipeline(prompt, max_new_tokens=256, do_sample=True, temperature=0.7, top_k=50, top_p=0.95)
|
| 199 |
-
answer = outputs[0]["generated_text"]
|
| 200 |
-
return answer[answer.rfind("[/INST]") + 8:], docs
|
| 201 |
-
else:
|
| 202 |
-
return "No tengo información para responder a esta pregunta", docs
|
| 203 |
-
|
| 204 |
-
# Definir la función externa para usar con Gradio
|
| 205 |
-
def test_rag(query):
|
| 206 |
-
return _test_rag(query, query_pipeline)
|
| 207 |
-
|
| 208 |
-
# Crear la interfaz de Gradio
|
| 209 |
-
iface = gr.Interface(fn=test_rag,
|
| 210 |
-
inputs="text",
|
| 211 |
-
outputs="text",
|
| 212 |
-
title="Sistema RAG de Hugging Face",
|
| 213 |
-
description="Este modelo utiliza RAG de Hugging Face para responder preguntas basadas en contexto.")
|
| 214 |
-
|
| 215 |
-
# Lanzar la interfaz de Gradio
|
| 216 |
-
if __name__ == "__main__":
|
| 217 |
-
iface.launch()
|
| 218 |
-
|
| 219 |
-
"""A continuación mostramos unos cuantos ejemplos de su uso."""
|
| 220 |
-
|
| 221 |
-
query = "¿Qué es Regla de Barrow?"
|
| 222 |
-
answer,docs=test_rag(query_pipeline, query)
|
| 223 |
-
answer
|
| 224 |
-
|
| 225 |
-
query = "¿Qué ES EL CAMBIO DE VARIABLE?"
|
| 226 |
-
answer,docs=test_rag(query_pipeline, query)
|
| 227 |
-
answer
|
| 228 |
-
|
| 229 |
-
query = "Cual es la INTEGRACIÓN POR PARTES?"
|
| 230 |
-
answer,docs=test_rag(query_pipeline, query)
|
| 231 |
-
answer
|
| 232 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|