File size: 9,044 Bytes
6bc94ac
 
 
 
 
 
 
 
 
b55470f
6bc94ac
 
 
abca9bf
2fc6e46
6bc94ac
db5ef00
 
6bc94ac
 
 
 
 
 
 
 
 
abca9bf
6bc94ac
 
 
 
 
 
5beab45
6bc94ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b55470f
6bc94ac
 
 
 
 
 
 
 
 
5beab45
 
6bc94ac
 
 
 
 
 
 
 
 
 
 
 
5beab45
db5ef00
5beab45
6bc94ac
 
 
 
abca9bf
6bc94ac
 
 
 
 
5beab45
6bc94ac
db5ef00
5beab45
 
 
db5ef00
5beab45
8bc0535
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
db5ef00
 
 
5beab45
db5ef00
 
 
 
 
 
 
8bc0535
 
 
5beab45
8bc0535
db5ef00
8bc0535
 
5beab45
 
 
 
db5ef00
5beab45
db5ef00
 
 
 
7fedcd4
5beab45
db5ef00
5beab45
8bc0535
 
 
5beab45
8bc0535
db5ef00
8bc0535
 
5beab45
 
 
 
db5ef00
5beab45
db5ef00
5beab45
db5ef00
8bc0535
5beab45
6bc94ac
8bc0535
5beab45
8bc0535
 
5beab45
427e102
abca9bf
370e3bc
2a846a9
6bc94ac
 
 
 
 
 
5beab45
6bc94ac
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
import datetime
import numpy as np
import torch
import torch.nn.functional as F
import speech_recognition as sr
import re
import time
import pickle
from sklearn.metrics.pairwise import cosine_similarity


# Build the AI
class CelebBot():

    def __init__(self, name, gender, QA_tokenizer, QA_model, sentTr_tokenizer, sentTr_model, spacy_model, knowledge_sents, top_k = 8):
        self.name = name
        self.gender = gender
        print("--- starting up", self.name, self.gender, "---")
        self.text = ""
        self.QA_tokenizer = QA_tokenizer
        self.QA_model = QA_model

        self.sentTr_tokenizer = sentTr_tokenizer
        self.sentTr_model = sentTr_model
        self.spacy_model = spacy_model

        self.all_knowledge = knowledge_sents
        self.top_k = top_k

    def speech_to_text(self):
        recognizer = sr.Recognizer()
        with sr.Microphone() as mic:
            recognizer.adjust_for_ambient_noise(mic, duration=1)
            # flag = input("Are you ready to record?\nProceed (Y/n)")

            # try:
            #     assert flag=='Y'
            # except:
            #     self.text = ""
            #     print(f"me -->  Permission denied")
            time.sleep(1)

            print("listening")
            audio = recognizer.listen(mic)
            try:
                self.text = recognizer.recognize_google(audio)
            except:
                self.text = ""
                print(f"me -->  No audio recognized")

    def text_to_speech(self, autoplay=True):
        import run_tts
        return run_tts.tts(self.text, "_".join(self.name.split(" ")), self.spacy_model, autoplay)

    def sentence_embeds_inference(self, texts: list):
        def _mean_pooling(model_output, attention_mask):
            token_embeddings = model_output[0] #First element of model_output contains all token embeddings
            input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
            return torch.sum(token_embeddings * input_mask_expanded, 1) / torch.clamp(input_mask_expanded.sum(1), min=1e-9)
        # Tokenize sentences
        encoded_input = self.sentTr_tokenizer(texts, padding=True, truncation=True, return_tensors='pt')
        encoded_input["input_ids"] = encoded_input["input_ids"].to(self.sentTr_model.device)
        encoded_input["attention_mask"] = encoded_input["attention_mask"].to(self.sentTr_model.device)

        # Compute token embeddings
        with torch.no_grad():
            model_output = self.sentTr_model(**encoded_input)

        # Perform pooling
        sentence_embeddings = _mean_pooling(model_output, encoded_input['attention_mask'])

        # Normalize embeddings
        sentence_embeddings = F.normalize(sentence_embeddings, p=2, dim=1)

        return sentence_embeddings

    def retrieve_knowledge_assertions(self, change_person=True):
        question_embeddings = self.sentence_embeds_inference([self.text])

        all_knowledge_embeddings = self.sentence_embeds_inference(self.all_knowledge)
        similarity = cosine_similarity(all_knowledge_embeddings.cpu(), question_embeddings.cpu())
        similarity = np.reshape(similarity, (1, -1))[0]
        K = min(self.top_k, len(self.all_knowledge))
        top_K = np.sort(np.argpartition(similarity, -K)[-K: ])
        all_knowledge_assertions = np.array(self.all_knowledge)[top_K]

        # similarities = np.array(similarity)[top_K]

        # print(*all_knowledge_assertions, sep='\n')

        if change_person:
           all_knowledge_assertions = [self.third_to_first_person(sent) for sent in all_knowledge_assertions]
        return " ".join(all_knowledge_assertions)

    def third_to_first_person(self, text):
        text = text.replace(" ", "  ")
        possible_names = [name.lower() for name in self.name.split(" ")]
        if "bundchen" in self.name.lower():
            possible_names.append("bündchen")
        if "beyonce" in self.name.lower():
            possible_names.append("beyoncé")
        if "adele" in self.name.lower():
            possible_names.append("adkins") 
        if "katy perry" in self.name.lower():
            possible_names.append("hudson") 
        if "lady gaga" in self.name.lower():
            possible_names.append("germanotta") 
        if "michelle obama" in self.name.lower():
            possible_names.append("robinson")
        if "natalie portman" in self.name.lower():
            possible_names.append("hershlag") 
        if "rihanna" in self.name.lower():
            possible_names.append("fenty")   
        if "victoria beckham" in self.name.lower():
            possible_names.append("adams")                         
        doc = self.spacy_model(text)
        transformed_text = []

        for i, token in enumerate(doc):
            if self.gender == "M":
                if token.text.lower() == "he":
                    transformed_text.append("I")
                elif token.text.lower() == "him":
                    transformed_text.append("me")
                elif token.text.lower() == "his":
                    transformed_text.append("my")
                elif token.text.lower() == "himself":
                    transformed_text.append("myself")
                elif token.text.lower() in possible_names and token.dep_ in ["nsubj", "nsubjpass"]:
                    transformed_text.append("I")
                elif token.text in ["'s", "’s"] and doc[i-1].text.lower() in possible_names:
                    transformed_text[-1] = "my"
                elif token.text.lower() in possible_names and token.dep_ in ["dobj", "dative"]:
                    transformed_text.append("me")                    
                elif token.text.lower() == "their":
                    transformed_text.append("our")
                elif token.text.lower() == "they":
                    transformed_text.append("we")
                else:
                    transformed_text.append(token.text)
            elif self.gender == "F":
                if token.text.lower() == "she":
                    transformed_text.append("I")
                elif token.text.lower() == "her":
                    if i < len(doc)-2 and doc[i+2].dep_ in ["nsubj", "nsubjpass", "dobj", "appos", "dative", "attr", "amod", "nummod", "compound", "pobj", "pcomp"]:
                        transformed_text.append("my")
                    else:
                        transformed_text.append("me")
                elif token.text.lower() == "herself":
                    transformed_text.append("myself")
                elif token.text.lower() in possible_names and token.dep_ in ["nsubj", "nsubjpass"]:
                    transformed_text.append("I")
                elif token.text in ["'s", "’s"] and doc[i-1].text.lower() in possible_names:
                    transformed_text[-1] = "my"
                elif token.text.lower() in possible_names and token.dep_ in ["dobj", "dative"]:
                    transformed_text.append("me")    
                elif token.text.lower() == "their":
                    transformed_text.append("our")
                elif token.text.lower() == "they":
                    transformed_text.append("we")
                else:
                    transformed_text.append(token.text)

        return "".join(transformed_text)

    def question_answer(self, instruction='', knowledge='', chat_his=''):
        instruction = f"Your name is {self.name}. Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature. If a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don't know the answer to a question, please don't share false information."
        if self.text != "":
            if re.search(re.compile(rf'\b({self.name})\b', flags=re.IGNORECASE), self.text) != None:
              knowledge = self.retrieve_knowledge_assertions(change_person=False)
            else:
              knowledge = self.retrieve_knowledge_assertions()

            query = f"Context: {instruction} {knowledge}\n\nQuestion: {self.text}\n\nAnswer:"
            input_ids = self.QA_tokenizer(f"{query}", truncation=False, return_tensors="pt").input_ids.to(self.QA_model.device)
            outputs = self.QA_model.generate(input_ids, max_length=1024, min_length=8, repetition_penalty=2.5)
            self.text = self.QA_tokenizer.decode(outputs[0], skip_special_tokens=True)
        return self.text

    @staticmethod
    def action_time():
        return f"it's {datetime.datetime.now().time().strftime('%H:%M')}"

    @staticmethod
    def save_kb(kb, filename):
        with open(filename, "wb") as f:
            pickle.dump(kb, f)

    @staticmethod
    def load_kb(filename):
        res = None
        with open(filename, "rb") as f:
            res = pickle.load(f)
        return res