This commit is contained in:
JulianZablonski 2023-06-21 00:20:20 +02:00
commit 2a9116b2c3
4 changed files with 5058 additions and 0 deletions

332
project/chatbot.ipynb Normal file
View File

@ -0,0 +1,332 @@
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"gpuType": "T4"
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
},
"accelerator": "GPU"
},
"cells": [
{
"cell_type": "markdown",
"source": [
"# Empathic chatbot\n",
"\n",
"## Dataset:\n",
"- https://huggingface.co/datasets/sedthh/ubuntu_dialogue_qa\n",
"\n",
"## Fine-tuned model:\n",
"- https://huggingface.co/kedudzic/flan_ubuntu_v2\n",
"\n",
"Careful: instatiating the chatbot too many times in one session will crash the notebook due to a RAM shortage!"
],
"metadata": {
"id": "zJPY1-K6m4jQ"
}
},
{
"cell_type": "code",
"source": [
"!pip install -q transformers emoji xformers"
],
"metadata": {
"id": "_fel66sU9WgD"
},
"execution_count": 1,
"outputs": []
},
{
"cell_type": "code",
"source": [
"from transformers import pipeline, AutoModelForSeq2SeqLM, AutoTokenizer, logging\n",
"import torch\n",
"import random\n",
"from emoji import emojize\n",
"import warnings\n",
"warnings.filterwarnings(\"ignore\", category=UserWarning)"
],
"metadata": {
"id": "UhrxmXlO9XFD"
},
"execution_count": 2,
"outputs": []
},
{
"cell_type": "code",
"source": [
"class Chatbot:\n",
" def __init__(self):\n",
" self.emotion_classifier = pipeline('text-classification', model='j-hartmann/emotion-english-distilroberta-base')\n",
" self.qa_model = AutoModelForSeq2SeqLM.from_pretrained('kedudzic/flan_ubuntu_v2')\n",
" self.qa_tokenizer = AutoTokenizer.from_pretrained('kedudzic/flan_ubuntu_v2')\n",
" self.empathic_phrases = {'anger': {'phrases': [\"Grrr! That's a good reason to be angry! But let's cool down slowly.\",\n",
" \"You being angry makes me angry as well! Give half of your anger to me!\",\n",
" \"Let's be angry together and blow off some steam!\",\n",
" \"You're angry? That would make anyone angry! I understand you well.\",\n",
" \"Be angry as much as you want with me! Let it out!\"],\n",
" 'emoji': f\"{emojize(':enraged_face:')}\"},\n",
" 'disgust': {'phrases': [\"Yuck! That's disgusting! I get you.\",\n",
" \"Eughh! Anyone would be disgusted by this!\",\n",
" \"That's so so so disgusting... It's only natural to feel like that.\",\n",
" \"I'm disgusted just by listening to it! You're not alone!\",\n",
" \"Yikes! I understand your disgust.\"],\n",
" 'emoji': f\"{emojize(':nauseated_face:')}\"},\n",
" 'fear': {'phrases': [\"Aah! That's scary! Are you ok?\",\n",
" \"You're scaring me too! Try to think about something else.\",\n",
" \"You're sending shivers down my spine! You're brave to talk about it to me.\",\n",
" \"Stop saying such scary things! Let's change the topic soon.\",\n",
" \"Terrifying stuff! I hope it doesn't make you feel bad.\"],\n",
" 'emoji': f\"{emojize(':face_screaming_in_fear:')}\"},\n",
" 'joy': {'phrases': [\"You're happy? I'm happy!\",\n",
" \"That's good to hear!\",\n",
" \"You're having a good day aren't you?\",\n",
" \"I see you're doing great!\",\n",
" \"Good to see you happy!\"],\n",
" 'emoji': f\"{emojize(':beaming_face_with_smiling_eyes:')}\"},\n",
" 'neutral': {'phrases': [''], 'emoji': f\"{emojize(':slightly_smiling_face:')}\"},\n",
" 'sadness': {'phrases': [\"I'm sorry to hear that!\",\n",
" \"Cheer up please, you're making me sad too!\",\n",
" \"Oh no... it'll be okay.\",\n",
" \"That's so sad... I understand you.\",\n",
" \"Nooo, I'm so sorry... I hope it'll get better.\"],\n",
" 'emoji': f\"{emojize(':worried_face:')}\"},\n",
" 'surprise': {'phrases': [\"Woah, that's unexpected!\",\n",
" \"Wow, really?!\",\n",
" \"That's surprising!\",\n",
" \"What?!\",\n",
" \"Who would've thought, right?\"],\n",
" 'emoji': f\"{emojize(':astonished_face:')}\"}\n",
" }\n",
"\n",
" def answer_question(self, model, tokenizer, question):\n",
" input_ids = tokenizer(f\"Answer the question: {question}\", return_tensors=\"pt\").input_ids\n",
" outputs = model.generate(input_ids, max_new_tokens=64, no_repeat_ngram_size=2)\n",
" answer = tokenizer.decode(outputs[0], skip_special_tokens=True)\n",
" return answer\n",
"\n",
" def add_empathy(self, question, answer):\n",
" emotion = self.emotion_classifier(question)[0]['label']\n",
" answer = f\"{random.choice(self.empathic_phrases[emotion]['phrases'])} {self.empathic_phrases[emotion]['emoji']} I think the answer to your question could be: {answer}\".strip()\n",
" return answer\n",
"\n",
" def generate_reply(self, utterance):\n",
" reply = self.answer_question(self.qa_model, self.qa_tokenizer, utterance)\n",
" reply = self.add_empathy(utterance, reply)\n",
" return reply\n",
"\n",
"chatbot = Chatbot()"
],
"metadata": {
"id": "kEZ4BfWc9XJK"
},
"execution_count": 3,
"outputs": []
},
{
"cell_type": "code",
"source": [
"chatbot.generate_reply(\"I'm furious, I'm mad! I can't play games on Linux!!!!!!!\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 52
},
"id": "AdqiE7sk2FkB",
"outputId": "a0b64407-affd-46b0-fdf8-87be0f716117"
},
"execution_count": 4,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"\"Grrr! That's a good reason to be angry! But let's cool down slowly. 😡 I think the answer to your question could be: i'm not sure if it's a problem with the kernel or something else, but ubuntu is based on linux, so it should work\""
],
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "string"
}
},
"metadata": {},
"execution_count": 4
}
]
},
{
"cell_type": "code",
"source": [
"chatbot.generate_reply(\"Can you run conky in a terminal? I was so surprised when I heard you apparently can!\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 35
},
"id": "eKhxVU4e2FmE",
"outputId": "1f5797c7-b01a-4610-9584-33ffed9de2e6"
},
"execution_count": 5,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"\"Who would've thought, right? 😲 I think the answer to your question could be: if you're using a terminal, you can use'sudo apt-get install conky'\""
],
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "string"
}
},
"metadata": {},
"execution_count": 5
}
]
},
{
"cell_type": "code",
"source": [
"chatbot.generate_reply(\"hi I installed a new gpu but ubuntu wont find it, what can I do to 'rescan' for the newly installed one? I'm scared it's broken!\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 35
},
"id": "sDj84eGY2FoK",
"outputId": "90186b39-d8bb-46eb-83a5-9bbaaa67edbc"
},
"execution_count": 6,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"\"You're scaring me too! Try to think about something else. 😱 I think the answer to your question could be: gparted\""
],
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "string"
}
},
"metadata": {},
"execution_count": 6
}
]
},
{
"cell_type": "code",
"source": [
"chatbot.generate_reply(\"Hello, do you know a good programming language for beginners? I want to program more, it makes me so happy!\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 35
},
"id": "bO5TAJzc2VGC",
"outputId": "70cb9e7e-ea47-4d88-c63f-5ee91a711f61"
},
"execution_count": 10,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"\"That's good to hear! 😁 I think the answer to your question could be: java\""
],
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "string"
}
},
"metadata": {},
"execution_count": 10
}
]
},
{
"cell_type": "code",
"source": [
"chatbot.generate_reply(\"What's pclos?\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 35
},
"id": "xKjFDH3U4PwF",
"outputId": "707ce0e4-5d09-4462-b433-56bcb53011b5"
},
"execution_count": 8,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"'🙂 I think the answer to your question could be: pclos is a linux-based graphical user interface for gdm'"
],
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "string"
}
},
"metadata": {},
"execution_count": 8
}
]
},
{
"cell_type": "markdown",
"source": [
"## Chatbot"
],
"metadata": {
"id": "HXheYhIJn1qr"
}
},
{
"cell_type": "code",
"source": [
"print(f\"Duck: Hello! I'm a rubber ducky chatbot here to help YOU - the troubled programmer! Talk to me about all your coding and computer worries. Quack! {emojize(':duck:')}\")\n",
"while True:\n",
" user_utterance = input(f\"User: \")\n",
" if user_utterance.lower() == 'exit':\n",
" print(f\"Duck: Bye. {emojize(':crying_face:')} Quack! {emojize(':duck:')}\")\n",
" break\n",
" reply = chatbot.generate_reply(user_utterance)\n",
" print('Duck:', reply)\n"
],
"metadata": {
"id": "JWYROrDbn1Rb",
"outputId": "cda894f3-6757-45ca-c010-e917a871e767",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"execution_count": 15,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Duck: Hello! I'm a rubber ducky chatbot here to help YOU - the troubled programmer! Talk to me about all your coding and computer worries. Quack! 🦆\n",
"User: how to remove directory with content? im so pissed right now OMG~!!\n",
"Duck: Grrr! That's a good reason to be angry! But let's cool down slowly. 😡 I think the answer to your question could be: rm -r\n",
"User: HOW TO CHANGE PERMISSIONS OF FILE TO EXECECUTE IT. I'm so affraid because its not working\n",
"Duck: Terrifying stuff! I hope it doesn't make you feel bad. 😱 I think the answer to your question could be: if you want to execute it, you can use'sudo apt-get install gksuite'\n",
"User: The default Ubuntu color scheme is so disgusting! Can I change it?\n",
"Duck: Yikes! I understand your disgust. 🤢 I think the answer to your question could be: i think you can change the color of the background\n",
"User: exit\n",
"Duck: Bye. 😢 Quack! 🦆\n"
]
}
]
}
]
}

53
project/documentation.md Normal file
View File

@ -0,0 +1,53 @@
# Dokumentacja
<br/><br/>
## 1. Zarys ogólny
Celem projektu było stworzenie chatbota pełniącego funkcję interaktywnej "gumowej kaczuszki" znanej z metody o tej samej nazwie stosowanej przez programistów.
## 2. Mapa empatii
![mapa](empathy_map.png)
## 3. Plan systemu sporządzony metodą "writing hills"
1. Sfera działalności:
- interaktywna żółta kaczka dla programisty
2. Użytkownik:
- programiści i osoby techniczne
3. Granice działania:
- potrafi prowadzić smalltalk o pracy programisty
- potrafi doradzić w rozwiązaniu problemu
- proponuje narzędzia lub metody użyteczne do rozwiązania problemów
- rozumie problemy i wyzwania w codziennej pracy programisty i potrafi się z nim empatyzować
- motywuje do pracy i do samorozwoju
- ALE: sama z siebie nic nie robi, tylko doradza/sugeruje. Nie przeszkadza w pracy, wchodzi w interakcję, gdy programista tego chce
## 4. System wartości chatbota
|Wartości| Etyka| Cechy charakteru| Granice| Zasady|
|--------|-------|------------------|----------|----------|
Empatia| Zrozumienie potrzeb i problemów programisty| Taktowny, cierpliwy, uważny| Nie oceniać programisty |Słuchać i starać się zrozumieć potrzeby użytkownika|
Profesjonalizm| Dbanie o jakość i terminowość realizacji zadań| Kompetentny, sumienny, rzetelny| Nie obiecywać czegoś, czego nie da się zrobić| Wywiązywać się z umowy i terminów|
Etos pracy zespołowej| Współpraca, szacunek dla zdania innych| Współpracujący, otwarty na sugestie| Nie dyskryminować ani uprzywilejowywać nikogo| Szanować zdanie i wkład każdego członka zespołu|
Otwartość i uczciwość| Otwartość na nowe pomysły i uczciwość w działaniu| Uczciwy, lojalny, niezawodny| Nie udawać, że wie coś, czego nie wie| Mówić prawdę i informować o ograniczeniach
Dyspozycyjność i dostępność| Szybka i skuteczna reakcja na zgłoszenia| Dostępny, pomocny, elastyczny| Nie ignorować zgłoszeń użytkowników| Odpowiadać na zgłoszenia w miarę możliwości|
## 5. Model empatii
Chatbot wykorzystuje model matrioszki. Ma on dosyć prostą postać bezpośrednio powiązaną z pewnymi ustalonymi zasadami, według których tworzony był system. Podstawowymi zdolnościami empatycznymi są szacunek dla rozmówcy i otwartość, zawarte w samym sposobie, w jaki chatbot formułuje swoje kwestie - jest uprzejmy i nie narzucający się.
Przechodząc do specyficznych sytuacji konwersacyjnych, gdy np. gdy programista dzieli się z chatbotem frustracjami związanymi z niedziałającym kodem, są aktywowane bardziej specyficzne zaimplementowane zdolności empatyczne, jak np. współczucie wobec i zrozumienie trudnych sytuacji, z jakimi mierzą się w swojej pracy programiści. Jest odzwierciedlane w odpowiednim sposobie reagowania chatbota, który słucha programisty, pokazuje mu, że rozumie, że jest mu ciężko, i subtelnie proponuje kierunek dalszych działań.
## 6. Struktura systemu
1. Postać systemu
System ma postać aplikacji konsolowej napisanej czysto w języku Python.
2. Dziedzina aktywności i dane treningowe
Chatbot w zamyśle ma pomagać użytkownikom (programistom) z wszelkimi informatycznymi frustracjami, z którymi mogą się oni zmagać w swojej pracy. Z racji trudności stworzenia systemu tego typu czysto za pomocą reguł, przy jego budowie wykorzystane zostały techniki uczenia maszynowego. Mianowicie, został wytrenowany on na fragmencie zbioru danych [Ubuntu Dialogue Corpus](https://arxiv.org/abs/1506.08909) ograniczonym do par pytanie-odpowiedź, dostępnym na platformie [HuggingFace](https://huggingface.co/datasets/sedthh/ubuntu_dialogue_qa). Ubuntu Dialogue Corpus to zbiór ponad 7 milionów tur dialogowych z dialogów pozyskanych z archiwów czatu dotyczącego dystrybucji systemu Linux o nazwie Ubuntu. Ich tematyką jest pomoc techniczna i programistyczna.
3. Architektura
System ma prostą architekturę. Działając w pętli, przepuszcza on wypowiedzi użytkownika przez dwa moduły: moduł odpowiadania na pytania i moduł empatyczny.
3.1. moduł odpowiadania na pytania
Do generacji odpowiedzi na pytania moduł ten wykorzystuje oparty na architekturze Transformer model [FLAN-T5](https://arxiv.org/pdf/2210.11416.pdf). Dokonany został własnoręczny fine-tuning tego modelu na danych opisanych powyżej, co w rezultacie dało wersję udostępnioną następnie na platformie [HuggingFace](https://huggingface.co/kedudzic/flan_ubuntu_v2). Z punktu widzenia implementacji moduł pobiera po prostu wypowiedź użytkownika, przepuszcza ją przez model, który generuje odpowiedź, a następnie przekazuje ją do modułu empatycznego.
3.2. moduł empatyczny
Celem modułu empatycznego jest wzbogacenie odpowiedzi na pytanie o wyrażenie empatyczne i emotkę pełniącą funkcję "avatara" bota. Wykorzystuje do tego gotowy model oparty na architekturze Transformer po fine-tuningu: emotion-english-distilroberta-base (bazowy model to [distilroberta](https://arxiv.org/abs/1910.01108)) dostępny na platformie [HuggingFace](https://huggingface.co/j-hartmann/emotion-english-distilroberta-base). Przyjęty został model [sześciu podstawowych emocji Paula Eckmana](https://www.paulekman.com/wp-content/uploads/2013/07/An-Argument-For-Basic-Emotions.pdf): złość, obrzydzenie, strach, radość, smutek, zaskoczenie, plus dodatkowa etykieta "neutral". Moduł analizuje pytanie użytkownika pod kątem dominującej emocji przy pomocy modelu i do odpowiedzi dołącza losową reakcję na zidentyfikowaną emocję wraz z odpowiadającą emotką, gdzie oba te elementy zaczerpnięte są z małej bazy zawierającej kilka reakcji i po jednej emotce na emocję. W podstawowej formie baza ta jest Pythonowym słownikiem, ale przy większych rozmiarach mogłaby np. być ładowana z pliku json. Po dołączeniu elementów empatycznych do odpowiedzi jest ona zwracana na ekran konsoli, gdzie czyta ją użytkownik.

BIN
project/empathy_map.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

File diff suppressed because it is too large Load Diff