|
--- |
|
license: apache-2.0 |
|
datasets: |
|
- wikimedia/wikipedia |
|
--- |
|
|
|
# FractalGPT/EmbedderDecoder |
|
|
|
* **Оригинальная модель** |
|
[[ai-forever/rugpt3small_based_on_gpt2](https://huggingface.co/ai-forever/rugpt3small_based_on_gpt2)] |
|
|
|
* **Код генерации вдохновлен этим проектом** |
|
[[vector2text](https://github.com/Koziev/vector2text)] |
|
|
|
* Заменен эмбеддер |
|
* Добавлена возможность задать промпт |
|
* Вместо нулей вектор дополняется квадратами чисел (далее можно кубами и т.д.) |
|
* Создан класс для генератора |
|
* Добавлен ранжировщик |
|
* Заменена модель вместо large — small |
|
* Убран top_p |
|
* Добавлен расчет среднего эмбеддинга (для ранжировщика в случае работы с массивом) |
|
* Добавлена работа с матрицами эмбеддингов |
|
* Добавлены 2 новых способа смеси эмбеддингов: |
|
* Эмбеддинги в первой степени из одного текста, а квадраты из другого(в эмбеддинги и их квадраты также можно включать разную по структуре информацию) |
|
* Передавать массив эмбеддингов и их квадратов |
|
|
|
* **Пример использования** |
|
|
|
|
|
```python |
|
import torch |
|
import numpy as np |
|
from torch.nn import functional as F |
|
from transformers import GPT2Tokenizer, GPT2LMHeadModel |
|
|
|
def top_filtering(logits, top_k): |
|
""" |
|
Фильтрация top-k, в фильтрации top-p в этой задаче особо смысла нет |
|
код с top-p: https://github.com/ictnlp/DSTC8-AVSD/blob/master/generate.py |
|
""" |
|
assert logits.dim() == 1 |
|
top_k = min(top_k, logits.size(-1)) |
|
if top_k > 0: |
|
indices_to_remove = logits < torch.topk(logits, top_k)[0][..., -1, None] |
|
logits[indices_to_remove] = -float('Inf') |
|
|
|
return logits |
|
|
|
|
|
class TextEmbdGenerator: |
|
def __init__(self, name_or_path, sbert, device = None): |
|
""" |
|
Инициализация генератора текста с моделью и токенизатором. |
|
name_or_path: путь до модели токенизатора или ее имя для загрузки из Hugging Face. |
|
sbert: модель для ранжирования (такая же что и создает эбеддинги) |
|
""" |
|
self.device = device |
|
|
|
if self.device == None: |
|
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
|
|
|
|
|
self.tokenizer = GPT2Tokenizer.from_pretrained(name_or_path) |
|
self.model = GPT2LMHeadModel.from_pretrained(name_or_path).to(self.device) |
|
self.sbert = sbert |
|
|
|
|
|
def __get_embds(self, embds, sqr_embds): |
|
'''Работает с матрицей эмбеддингов''' |
|
list_emb = [] |
|
sq = embds if sqr_embds == None else sqr_embds |
|
|
|
for i, embd in enumerate(embds): |
|
vector = np.concatenate([embd,sq[i]**2]) |
|
list_emb.append(list(vector)) |
|
|
|
return torch.FloatTensor(list_emb).to(self.device) |
|
|
|
def __det_mean(self, embds): |
|
'''Получение среднего''' |
|
m = np.zeros((384)) |
|
|
|
for embd in embds: |
|
m += embd |
|
|
|
m /= len(embds) |
|
return m |
|
|
|
def generate_embedding(self, embds, prompt = '', sqr_embds = None, temperature=0.26, top_k=4, max_len=100): |
|
""" |
|
Генерация текста на основе начального эмбеддинга и заданного начального текста. |
|
""" |
|
|
|
current_output_ids = self.tokenizer.encode(prompt) # Промпт |
|
embedding = self.__get_embds(embds, sqr_embds) # Матрица входа |
|
word_tokens = self.model.base_model.wte # Словарь токенов |
|
|
|
while len(current_output_ids) < max_len: |
|
with torch.no_grad(): |
|
outp_ids_tensor = torch.LongTensor(current_output_ids).to(self.device) # Выходы |
|
token_embeddings = word_tokens(outp_ids_tensor) # эмбеддинги |
|
input_vectors = torch.vstack((embedding, token_embeddings)).unsqueeze(dim=0) |
|
output_model = self.model(inputs_embeds=input_vectors) |
|
|
|
logits = output_model.logits |
|
if isinstance(logits, tuple): |
|
logits = logits[0] |
|
logits = logits[0, -1, :] |
|
logits /= temperature |
|
logits = top_filtering(logits, top_k) |
|
probs = F.softmax(logits, dim=-1) |
|
|
|
prev = torch.multinomial(probs, 1) |
|
if prev.item() == self.tokenizer.eos_token_id: |
|
break |
|
current_output_ids.append(prev.item()) |
|
|
|
output_text = self.tokenizer.decode(current_output_ids) |
|
return output_text.split('\n')[0] |
|
|
|
|
|
def cosine_similarity(self, x, y): |
|
"""Вычисление косинусного сходства.""" |
|
return np.dot(x, y) / (np.linalg.norm(x) * np.linalg.norm(y)) |
|
|
|
def generate_with_ranker(self, embds, prompt = '', sqr_embds = None, seq=10, temperature=0.6, top_k=10, max_len=100): |
|
"""Генерация и ранжирование текста. Поумолчанию создаются 10 текстов""" |
|
sequences = [self.generate_embedding(embds, prompt, sqr_embds, temperature, top_k, max_len) for _ in range(seq)] |
|
sequences = list(set(sequences)) # Удаление дубликатов |
|
|
|
# Ранжирование |
|
embd = self.__det_mean(embds) |
|
embeddings = self.sbert.encode(sequences) |
|
similarities = [self.cosine_similarity(embd, emb) for emb in embeddings] |
|
best_index = np.argmax(similarities) |
|
|
|
return sequences[best_index] |
|
``` |
|
|
|
--- |
|
|
|
```bash |
|
pip install sentence-transformers -q |
|
``` |
|
|
|
```python |
|
from sentence_transformers import SentenceTransformer |
|
|
|
sbert = SentenceTransformer('FractalGPT/SbertDistil') |
|
generator = TextEmbdGenerator('FractalGPT/EmbedderDecoder', sbert) |
|
``` |
|
|
|
```python |
|
embd = sbert.encode('там живут англичане') |
|
generator.generate_with_ranker([embd]) |
|
``` |
|
```bash |
|
>>> я бы его в Англию привез. |
|
``` |
|
|
|
|
|
```python |
|
embd = sbert.encode('там живут немцы') |
|
generator.generate_with_ranker([embd], prompt = 'он всегда был в') |
|
``` |
|
```bash |
|
>>> он всегда был в Германии |
|
``` |
|
|
|
```python |
|
embd = sbert.encode('он сделает вывод на основе анализа ситуации') |
|
generator.generate_with_ranker(embd) |
|
``` |
|
```bash |
|
>>> в процессе анализа ситуации необходимо выяснить: |
|
``` |
|
|
|
|
|
```python |
|
embd = sbert.encode('машина') - sbert.encode('колеса') + sbert.encode('крылья') |
|
generator.generate_with_ranker([embd], 'это') |
|
``` |
|
```bash |
|
>>> этот самолёт |
|
``` |
|
|
|
```python |
|
embd = sbert.encode('полицейский - главный герой') + sbert.encode('Произошло ужасное событие в фильме') |
|
embd /= 2 |
|
generator.generate_with_ranker([embd], 'Собеседование на') |
|
``` |
|
|
|
```bash |
|
>>> Собеседование на роль главного героя фильма — молодого лейтенанта полиции — происходит в доме |
|
``` |
|
|
|
|
|
**После дообучения** |
|
|
|
|
|
|
|
```python |
|
embd_1 = sbert.encode('полицейский - главный герой') |
|
embd_2 = sbert.encode('Произошло событие в фильме') |
|
|
|
generator.generate_with_ranker([embd_1, embd_2], 'В ') |
|
``` |
|
|
|
```bash |
|
>>> В этом фильме главный герой - полицейский. |
|
``` |
|
|
|
```python |
|
embd_1 = sbert.encode('полицейский - главный герой') |
|
embd_2 = sbert.encode('Произошло событие в фильме') |
|
|
|
generator.generate_with_ranker([embd_1], 'Это', [embd_2]) |
|
``` |
|
|
|
```bash |
|
>>> Это полицейский, который в полицейском участке снимается в фильме. |
|
``` |
|
|
|
```python |
|
embd = sbert.encode('радиоприемник') |
|
ans = vector_answer(embd, 'Как это устроено?') |
|
print(ans) |
|
``` |
|
|
|
```bash |
|
>>> Радиоволны распространяются в воздухе, создавая электромагнитное поле, которое может быть использовано для передачи информации. |
|
``` |