You've already forked Curso-lenguaje-python
Compare commits
2 Commits
ce1df90cb1
...
1b5a79be68
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b5a79be68 | |||
| 91154fa094 |
@@ -617,5 +617,112 @@ El fichero del código completo es [traducir_texto.py](src/07_traducir_texto.py)
|
||||
|
||||
## TEMA 5 - Otras consideraciones para la integración
|
||||
|
||||
### 5.1. - Filtrar respuestas – Palabras prohibidas
|
||||
|
||||
Vamos a recuperar el código del chatbot que creamos y le vamos a añadir la biblioteca spacy, que nos ayudará al procesamiento del lenguaje natural. Así que importamos spacy, añadimos la variable cargando el modelo de procesamiento de lenguaje natural y otra de palabras_prohibidas:
|
||||
```python
|
||||
import spacy
|
||||
modelo_spacy = spacy.load("es_core_news_md")
|
||||
palabras_prohibidas = ["palabra1", "palabra2"]
|
||||
```
|
||||
|
||||
En palabras_prohibidas hemos creado una lista ficticia pero estás palabras podrían ser palabras malsonantes, palabras de la competencia, palabras de jerga o cualquier listado que creamos que no debe salir en el output que nos devuelve chatgpt.
|
||||
|
||||
Ahora vamos a crear la función para el filtrado:
|
||||
```python
|
||||
def filtrar_lista_negra(texto, lista_negra):
|
||||
token = modelo_spacy(texto)
|
||||
resultado = []
|
||||
for t in token:
|
||||
# Si el token no está en la lista negra, agregarlo al resultado
|
||||
if t.text not in lista_negra:
|
||||
resultado.append(t.text)
|
||||
else:
|
||||
resultado.append("[xxxxx]")
|
||||
|
||||
return " ".join(resultado)
|
||||
```
|
||||
|
||||
Ahora tenemos que retocar la función preguntar_chat_gpt para que llame a la función nueva de filtrado en cada respuesta cambiando el return que teníamos:
|
||||
```python
|
||||
respuesta_sin_filtrar = respuesta.choices[0].text.strip()
|
||||
respuesta_filtrada = filtrar_lista_negra(respuesta_sin_filtrar, palabras_prohibidas)
|
||||
return respuesta_filtrada
|
||||
```
|
||||
|
||||
Antes de hacer la prueba, y para que quede más despejada la ejecución del programa en la terminal, vamos a añadir una función que limpie el terminal con la biblioteca que tenemos importada os:
|
||||
```python
|
||||
def clearConsole():
|
||||
# Función limpiar consola
|
||||
os.system('clear')
|
||||
```
|
||||
|
||||
Vamos a llamarla al inicio de la dinámica del programa, antes de la bienvenida.
|
||||
|
||||
Y para probarlo, vamos a añadir en las palabras_prohibidas la palabra "paella" y le vamos a preguntar por el plato típico valenciano:
|
||||
|
||||

|
||||
|
||||
### 5.2. - Verificar respuestas – Relevancia
|
||||
|
||||
Vamos a realizar tres pasos:
|
||||
- Calcular similitudes
|
||||
- Vectorizar los valores
|
||||
- Interceptar la respuesta
|
||||
|
||||
#### 5.2.1. - Calcular similitudes
|
||||
|
||||
Vamos a seguir con el chatbot para añadirle esta funcionalidad. Lo primero es instalar numpy que lo necesitaremos para realizar ciertos calculos. Importamos la biblioteca y le damos un alias:
|
||||
```python
|
||||
import numpy as np
|
||||
```
|
||||
|
||||
Y vamos a crear una función con algunos principios matemáticos para aplicar a nuestro texto. Vamos a hacer que calcule lo que se llama la similitud coseno de dos vectores.
|
||||
```python
|
||||
def similitud_coseno(vec1, vec2):
|
||||
superposicion = np.dot(vec1, vec2)
|
||||
magnitud1 = np.linalg.norm(vec1) # Longitud del vector
|
||||
magnitud2 = np.linalg.norm(vec2) # Longitud del vector
|
||||
sim_cos = superposicion / (magnitud1 * magnitud2)
|
||||
return sim_cos
|
||||
```
|
||||
|
||||
El resultado de esta operación va a ser un valor entre -1 y uno. Este valor va a indicar la similitud, o sea, entre los dos vectores.
|
||||
|
||||
Si se obtiene un valor uno indica que los vectores son idénticos, que tienen el mismo ángulo si hablamos de números. Mientras que un valor de -1 indicaría que son completamente opuestos, que su ángulo es de 180 grados.
|
||||
En el caso de los análisis de texto, que es lo que nos interesa a nosotros, los vectores se generan a partir de los textos utilizando un modelo de lenguaje como spacy que tiene la capacidad de convertir cada texto en un vector numérico que representa su contenido semántico, así como lo escuchas 🤯
|
||||
|
||||
Con lo cuál, la función similitud_coseno puede utilizarse para comparar estos vectores que son textos que han sido convertidos a valores numéricos según su valor semántico y así determinar cómo de similares son, en términos de contenido semántico.
|
||||
|
||||
#### 5.2.2. - Vectorizar los valores
|
||||
|
||||
Ahora, vamos a crear otra función que recogerá la respuesta y la entrada para calcular la similitud_coseno:
|
||||
def es_relevante(respuesta, entrada, umbral=0.5):
|
||||
```python
|
||||
entrada_vectorizada = modelo_spacy(entrada).vector
|
||||
respuesta_vectorizada = modelo_spacy(respuesta).vector
|
||||
similitud = similitud_coseno(entrada_vectorizada, respuesta_vectorizada)
|
||||
return similitud >= umbral
|
||||
```
|
||||
|
||||
#### 5.2.3. - Interceptar la respuesta
|
||||
Ahora ya volvemos al loop que da la dinámica y lógica al chatbot, justo antes de imprimir la respuesta debemos añadir en una variable la llamada a la función anterior y el print y la recogida de respuestas meterlas en un condicional si la respuesta el relevante, según nuestra función. Si es false
|
||||
```python
|
||||
relevante = es_relevante(respuesta_gpt, ingreso_usuario)
|
||||
|
||||
if relevante:
|
||||
print(f"{respuesta_gpt}")
|
||||
|
||||
preguntas_anteriores.append(ingreso_usuario)
|
||||
respuestas_anteriores.append(respuesta_gpt)
|
||||
else:
|
||||
print(Fore.RED + "La respuesta no es relevante ¿podrías reformularla?" + Fore.RESET)
|
||||
```
|
||||
|
||||
Le he dado colorcito rojo a la respuesta errónea. Vamos a probarlo. Le pido un poema y le pregunto sobre mi opinión:
|
||||
|
||||

|
||||
|
||||
Y hasta aquí el curso. ¡Sigue cortando leña! 🪓
|
||||
|
||||

|
||||
|
||||
BIN
python-chatgpt/img/python-chatgpt12.png
Normal file
BIN
python-chatgpt/img/python-chatgpt12.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
BIN
python-chatgpt/img/python-chatgpt13.png
Normal file
BIN
python-chatgpt/img/python-chatgpt13.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
BIN
python-chatgpt/img/python-chatgpt14.png
Normal file
BIN
python-chatgpt/img/python-chatgpt14.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
139
python-chatgpt/src/08_filtrar_respuestas.py
Normal file
139
python-chatgpt/src/08_filtrar_respuestas.py
Normal file
@@ -0,0 +1,139 @@
|
||||
"""
|
||||
Chatbot con OpenAI GPT-3
|
||||
"""
|
||||
|
||||
import openai
|
||||
import os
|
||||
import spacy
|
||||
import numpy as np
|
||||
from dotenv import load_dotenv
|
||||
from colorama import init, Fore
|
||||
|
||||
load_dotenv()
|
||||
api_key = os.getenv("OPENAI_API_KEY")
|
||||
|
||||
openai.api_key = api_key
|
||||
|
||||
preguntas_anteriores = []
|
||||
respuestas_anteriores = []
|
||||
modelo_spacy = spacy.load("es_core_news_md")
|
||||
palabras_prohibidas = ["palabra1", "palabra2", "paella"]
|
||||
|
||||
|
||||
# Inicializar colorama
|
||||
init()
|
||||
|
||||
|
||||
def clearConsole():
|
||||
"""
|
||||
Limpia la consola
|
||||
"""
|
||||
|
||||
os.system('clear')
|
||||
|
||||
|
||||
def similitud_coseno(vec1, vec2):
|
||||
"""
|
||||
Calcula la similitud coseno entre dos vectores
|
||||
"""
|
||||
|
||||
superposicion = np.dot(vec1, vec2)
|
||||
magnitud1 = np.linalg.norm(vec1) # Longitud del vector
|
||||
magnitud2 = np.linalg.norm(vec2) # Longitud del vector
|
||||
|
||||
sim_cos = superposicion / (magnitud1 * magnitud2)
|
||||
|
||||
return sim_cos
|
||||
|
||||
|
||||
def es_relevante(respuesta, entrada, umbral=0.2):
|
||||
"""
|
||||
Determina si una respuesta es relevante para una entrada
|
||||
"""
|
||||
|
||||
entrada_vectorizada = modelo_spacy(entrada).vector
|
||||
respuesta_vectorizada = modelo_spacy(respuesta).vector
|
||||
|
||||
# Ahora que tenemos las anteriores variables transformadas en vectores,
|
||||
# podemos calcular la similitud coseno
|
||||
similitud = similitud_coseno(entrada_vectorizada, respuesta_vectorizada)
|
||||
|
||||
# Si la similitud es mayor o igual al umbral, la respuesta es relevante
|
||||
return similitud >= umbral
|
||||
|
||||
|
||||
def filtrar_lista_negra(texto, lista_negra):
|
||||
"""
|
||||
Filtra palabras de una lista negra
|
||||
"""
|
||||
|
||||
# Separar el texto en tokens
|
||||
token = modelo_spacy(texto)
|
||||
# Crear una lista vacía para guardar los tokens permitidos
|
||||
resultado = []
|
||||
|
||||
for t in token:
|
||||
# Si el token no está en la lista negra, agregarlo al resultado
|
||||
if t.text not in lista_negra:
|
||||
resultado.append(t.text)
|
||||
|
||||
else:
|
||||
# Si el token está en la lista negra, agregarlo al resultado
|
||||
resultado.append("[xxxxx]")
|
||||
|
||||
return " ".join(resultado)
|
||||
|
||||
|
||||
def preguntar_chat_gpt(prompt, modelo="text-davinci-002"):
|
||||
"""
|
||||
Pregunta a la API de OpenAI GPT-3
|
||||
"""
|
||||
|
||||
respuesta = openai.Completion.create(
|
||||
engine=modelo,
|
||||
prompt=prompt,
|
||||
n=1,
|
||||
temperature=1,
|
||||
max_tokens=150
|
||||
)
|
||||
|
||||
respuesta_sin_filtrar = respuesta.choices[0].text.strip()
|
||||
|
||||
respuesta_filtrada = filtrar_lista_negra(
|
||||
respuesta_sin_filtrar, palabras_prohibidas)
|
||||
|
||||
return respuesta_filtrada
|
||||
|
||||
|
||||
# Bienvenida
|
||||
clearConsole()
|
||||
print(Fore.BLUE + "Bienvenido al chatbot de OpenAI GPT-3." + Fore.RESET)
|
||||
print(Fore.BLUE + "Escribe \"salir\" cuando quieras terminar la conversación." + Fore.RESET)
|
||||
|
||||
# Loop para controlar el flujo de la conversación
|
||||
while True:
|
||||
|
||||
conversacion_historica = ""
|
||||
|
||||
ingreso_usuario = input(Fore.MAGENTA + "Tú: " + Fore.RESET)
|
||||
|
||||
if ingreso_usuario == "salir":
|
||||
break
|
||||
|
||||
for pregunta, respuesta in zip(preguntas_anteriores, respuestas_anteriores):
|
||||
conversacion_historica += f"{Fore.BLUE}Usuario pregunta: {Fore.RESET}{pregunta}"
|
||||
conversacion_historica += f"{Fore.GREEN}Bot responde: {Fore.RESET}{respuesta}\n"
|
||||
|
||||
prompt = f"{Fore.CYAN}Usuario pregunta: {Fore.RESET}{ingreso_usuario}"
|
||||
conversacion_historica += prompt
|
||||
respuesta_gpt = preguntar_chat_gpt(conversacion_historica)
|
||||
|
||||
relevante = es_relevante(respuesta_gpt, ingreso_usuario)
|
||||
|
||||
if relevante:
|
||||
print(f"{respuesta_gpt}")
|
||||
|
||||
preguntas_anteriores.append(ingreso_usuario)
|
||||
respuestas_anteriores.append(respuesta_gpt)
|
||||
else:
|
||||
print(Fore.RED + "La respuesta no es relevante ¿podrías reformularla?" + Fore.RESET)
|
||||
Reference in New Issue
Block a user