moj-2024-ns-cw/03_zadania_helpful_codebloc...

11 KiB

Funkcja build_vocab_from_iterator automatycznie tworzy słownik na podstawie najczęściej występujących słów, jednocześnie traktując pozostałe słowa jako <unk>

from torchtext.vocab import build_vocab_from_iterator
import io
import zipfile
import torch


with zipfile.ZipFile("challenging_america_50k_texts.zip") as zf:
    with io.TextIOWrapper(zf.open("challenging_america_50k_texts.txt"), encoding="utf-8") as f:
        data = f.readlines()

def get_words_from_line(line):
  line = line.rstrip()
  for t in line.split():
    yield t


def get_word_lines_from_list(data):
    for line in data:
       yield get_words_from_line(line)

vocab_size = 3000
vocab = build_vocab_from_iterator(
    get_word_lines_from_list(data),
    max_tokens = vocab_size,
    specials = ['<unk>'])
vocab.set_default_index(vocab['<unk>'])
c:\Users\ryssta\AppData\Local\anaconda3\envs\python39\lib\site-packages\torchtext\vocab\__init__.py:4: UserWarning: 
/!\ IMPORTANT WARNING ABOUT TORCHTEXT STATUS /!\ 
Torchtext is deprecated and the last released version will be 0.18 (this one). You can silence this warning by calling the following at the beginnign of your scripts: `import torchtext; torchtext.disable_torchtext_deprecation_warning()`
  warnings.warn(torchtext._TORCHTEXT_DEPRECATION_MSG)
c:\Users\ryssta\AppData\Local\anaconda3\envs\python39\lib\site-packages\torchtext\utils.py:4: UserWarning: 
/!\ IMPORTANT WARNING ABOUT TORCHTEXT STATUS /!\ 
Torchtext is deprecated and the last released version will be 0.18 (this one). You can silence this warning by calling the following at the beginnign of your scripts: `import torchtext; torchtext.disable_torchtext_deprecation_warning()`
  warnings.warn(torchtext._TORCHTEXT_DEPRECATION_MSG)

Dostęp do par słowo, indeks ze słownika

vocab_dict = vocab.get_stoi()
for x, key in enumerate(vocab_dict):
    print(key, vocab_dict[key])
    if x == 10:
        break
<unk> 0
witness 1755
Supreme 2520
seems 577
her 51
! 503
were 40
Messrs. 1911
small 282
council 2064
but 35

Słownik dla podanego słowa w postaci stringa zwraca indeks w słowniku, który będzie wykorzystywany przez sieć neuronową (słowa nie znajdujące się w słowniku mapowane są na token unk, który posiada indeks 0 - zostało to zdefiniowane poprzez metodę vocab.set_default_index(vocab['<unk>']))

print(vocab["a"])
print(vocab["the"])
print(vocab["John"])
print(vocab["awnifnawonf"])
print(vocab["Poznań"])

print("Dla listy słów:")
print(vocab.lookup_indices(["a", "the"]))
5
1
208
0
0
Dla listy słów:
[5, 1]

Operacja odwrota (czyli odczytanie słowa na podstawie indeksu)

print(vocab.lookup_token(1))
print(vocab.lookup_token(1000))

print("Dla listy indeksów:")
print(vocab.lookup_tokens([0, 1, 1000, 2000]))
the
attempt
Dla listy indeksów:
['<unk>', 'the', 'attempt', 'drew']

Aby przekazać dane słowo (lub słowa) do sieci neuronowej, należy utworzyć na podstawie indeksu danego słowa Tensor

input_to_neural_network = torch.tensor(vocab["the"])
print(input_to_neural_network)

input_to_neural_network = torch.tensor(vocab.lookup_indices(["the", "current"]))
print(input_to_neural_network)
tensor(1)
tensor([   1, 2020])

Zanurzenia słów uzyskujemy poprzez wykorzystanie warstwy torch.nn.Embedding

vocab_size = 10
embedding_size = 3
embedding_layer = torch.nn.Embedding(vocab_size, embedding_size)
input_to_embedding_layer = torch.IntTensor([1, 5])
print("Uzyskanie wartości embeddingów dla tokenu o indeksie 1 oraz tokenu o indeksie 5")
print(embedding_layer((input_to_embedding_layer)))

batched_input_to_embedding_layer = torch.IntTensor([[1, 4],
                                                    [2, 9],
                                                    [3, 1]])
print("##########################################################")
print("Uzyskanie wartości embeddingów dla batcha z tokenami")
print(embedding_layer((batched_input_to_embedding_layer)))
Uzyskanie wartości embeddingów dla tokenu o indeksie 1 oraz tokenu o indeksie 5
tensor([[-1.0945, -0.1461,  1.2927],
        [-0.0303,  0.5213,  1.1486]], grad_fn=<EmbeddingBackward0>)
##########################################################
Uzyskanie wartości embeddingów dla batcha z tokenami
tensor([[[-1.0945, -0.1461,  1.2927],
         [ 0.2963,  0.1083,  0.0797]],

        [[-0.9783,  1.1639,  0.3828],
         [ 1.1856,  1.1943, -0.5562]],

        [[-0.3472,  0.5670, -1.2830],
         [-1.0945, -0.1461,  1.2927]]], grad_fn=<EmbeddingBackward0>)

Aby połączyć ze sobą zanurzenia kilku słów (podczas trenowania modelu ngramowego innego niż unigramowy/bigramowy), należy użyć konkatenacji. W torchu do tego wykorzystujemy funkcję torch.cat

first_embedding = torch.Tensor([0.5, 0.2, 0.2])
second_embedding = torch.Tensor([0.91, 0.92, 0.93])

concatenated_embeddings = torch.cat([first_embedding, second_embedding])
print(concatenated_embeddings)
tensor([0.5000, 0.2000, 0.2000, 0.9100, 0.9200, 0.9300])

W przypadku korzystania z batchy musimy zwrócić uwagę na właściwą oś (axis) po której będziemy dokonywać konkatenacji

first_batched_embedding = torch.Tensor([[0.5, 0.2, 0.2], 
                                        [0.51, 0.21, 0.21]])
second_batched_embedding = torch.Tensor([[0.91, 0.92, 0.93],
                                         [0.911, 0.922, 0.933]])

concatenated_embeddings = torch.cat([first_batched_embedding, second_batched_embedding])
print("Nieprawidłowa konkatenacja embeddingów (stworzenie 4 tensorów o wymiarze 3, zamiast 2 tensorów o wymiarze 6)")
print(concatenated_embeddings)

properly_concatenated_embeddings = torch.cat([first_batched_embedding, second_batched_embedding], axis=1)

print("#########################################################################")
print("Prawidłowa konkatenacja embeddingów dzięki właściwej wartości parametru axis")
print("Wtedy pierwsze 3 wartości (czyli dla pierwszego Tensora są to wartości 0.5000, 0.2000, 0.2000) reprezentują embedding pierwszego słowa")
print("a kolejne 3 wartości reprezentują embedding drugiego słowa (czyli 0.9100, 0.9200, 0.9300)")
print(properly_concatenated_embeddings)
Nieprawidłowa konkatenacja embeddingów (stworzenie 4 tensorów o wymiarze 3, zamiast 2 tensorów o wymiarze 6)
tensor([[0.5000, 0.2000, 0.2000],
        [0.5100, 0.2100, 0.2100],
        [0.9100, 0.9200, 0.9300],
        [0.9110, 0.9220, 0.9330]])
#########################################################################
Prawidłowa konkatenacja embeddingów dzięki właściwej wartości parametru axis
Wtedy pierwsze 3 wartości (czyli dla pierwszego Tensora są to wartości 0.5000, 0.2000, 0.2000) reprezentują embedding pierwszego słowa
a kolejne 3 wartości reprezentują embedding drugiego słowa (czyli 0.9100, 0.9200, 0.9300)
tensor([[0.5000, 0.2000, 0.2000, 0.9100, 0.9200, 0.9300],
        [0.5100, 0.2100, 0.2100, 0.9110, 0.9220, 0.9330]])