diff --git a/.gitignore b/.gitignore index 55d493e7..23696872 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ .ipynb_checkpoints/ data/ +.vector_cache/ # Mono auto generated files mono_crash.* diff --git a/5-NLP/13-TextRep/README.md b/5-NLP/13-TextRep/README.md index 13ab39bc..3853e33a 100644 --- a/5-NLP/13-TextRep/README.md +++ b/5-NLP/13-TextRep/README.md @@ -14,7 +14,7 @@ Our goal would be to classify the news item into one of the categories based on If we want to solve Natural Language Processing (NLP) tasks with neural networks, we need some way to represent text as tensors. Computers already represent textual characters as numbers that map to fonts on your screen using encodings such as ASCII or UTF-8. -![Image showing diagram mapping a character to an ASCII and binary representation](images/ascii-character-map.png) +Image showing diagram mapping a character to an ASCII and binary representation We understand what each letter **represents**, and how all characters come together to form the words of a sentence. However, computers by themselves do not have such an understanding, and neural network has to learn the meaning during training. @@ -34,7 +34,7 @@ In some cases, we may consider using tri-grams -- combinations of three words -- When solving tasks like text classification, we need to be able to represent text by one fixed-size vector, which we will use as an input to final dense classifier. One of the simplest ways to do that is to combine all individual word representations, eg. by adding them. If we add one-hot encodings of each word, we will end up with a vector of frequencies, showing how many times each word appears inside the text. Such representation of text is called **bag of words** (BOW). - + BOW essentially represents which words appear in text and in which quantities, which can indeed be a good indication of what the text is about. For example, news article on politics is likely to contains words such as *president* and *country*, while scientific publication would have something like *collider*, *discovered*, etc. Thus, word frequencies can in many cases be a good indicator of text content. diff --git a/5-NLP/13-TextRep/TextRepresentationPyTorch.ipynb b/5-NLP/13-TextRep/TextRepresentationPyTorch.ipynb index 463039a8..4cb34c33 100644 --- a/5-NLP/13-TextRep/TextRepresentationPyTorch.ipynb +++ b/5-NLP/13-TextRep/TextRepresentationPyTorch.ipynb @@ -8,7 +8,7 @@ "\n", "As we have mentioned, we will focus on simple text classification task based on **AG_NEWS** dataset, which is to classify news headlines into one of 4 categories: World, Sports, Business and Sci/Tech.\n", "\n", - "### The Dataset\n", + "## The Dataset\n", "\n", "This dataset is built into [`torchtext`](https://github.com/pytorch/text) module, so we can easily access it." ] @@ -116,7 +116,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Tokenization\n", + "## Tokenization\n", "\n", "Now we need to convert text into **numbers** that can be represented as tensors. If we want word-level representation, we need to do two things:\n", "* use **tokenizer** to split text into **tokens**\n", @@ -200,7 +200,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Bag of Words text representation\n", + "## Bag of Words text representation\n", "\n", "Because words represent meaning, sometimes we can figure out the meaning of a text by just looking at the individual words, regardless of their order in the sentence. For example, when classifying news, words like *weather*, *snow* are likely to indicate *weather forecast*, while words like *stocks*, *dollar* would count towards *financial news*.\n", "\n", @@ -285,7 +285,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Training BoW classifier\n", + "## Training BoW classifier\n", "\n", "Now that we have learned how to build Bag-of-Words representation of our text, let's train a classifier on top of it. First, we need to convert our dataset for training in such a way, that all positional vector representations are converted to bag-of-words representation. This can be achieved by passing `bowify` function as `collate_fn` parameter to standard torch `DataLoader`:" ] @@ -396,7 +396,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### BiGrams, TriGrams and N-Grams\n", + "## BiGrams, TriGrams and N-Grams\n", "\n", "One limitation of a bag of words approach is that some words are part of multi word expressions, for example, the word 'hot dog' has a completely different meaning than the words 'hot' and 'dog' in other contexts. If we represent words 'hot` and 'dog' always by the same vectors, it can confuse our model.\n", "\n", @@ -488,7 +488,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Term Frequency Inverse Document Frequency TF-IDF\n", + "## Term Frequency Inverse Document Frequency TF-IDF\n", "\n", "In BoW representation, word occurrences are evenly weighted, regardless of the word itself. However, it is clear that frequent words, such as *a*, *in*, etc. are much less important for the classification, than specialized terms. In fact, in most NLP tasks some words are more relevant than others.\n", "\n", @@ -538,10 +538,15 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Conclusion \n", + "## Conclusion \n", "\n", "However even though TF-IDF representations provide frequency weight to different words they are unable to represent meaning or order. As the famous linguist J. R. Firth said in 1935, “The complete meaning of a word is always contextual, and no study of meaning apart from context can be taken seriously.”. We will learn later in the course how to capture contextual information from text using language modeling.\n" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] } ], "metadata": { diff --git a/5-NLP/13-TextRep/TextRepresentationTF.ipynb b/5-NLP/13-TextRep/TextRepresentationTF.ipynb index 2675380e..ebb5799c 100644 --- a/5-NLP/13-TextRep/TextRepresentationTF.ipynb +++ b/5-NLP/13-TextRep/TextRepresentationTF.ipynb @@ -8,59 +8,16 @@ "\n", "In this module, we will start with a simple text classification task based on the **[AG_NEWS](http://www.di.unipi.it/~gulli/AG_corpus_of_news_articles.html)** dataset: we'll classify news headlines into one of 4 categories: World, Sports, Business and Sci/Tech. \n", "\n", - "### The Dataset\n", + "## The Dataset\n", "\n", "To load the dataset, we will use the **[TensorFlow Datasets](https://www.tensorflow.org/datasets)** API." ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[1mDownloading and preparing dataset Unknown size (download: Unknown size, generated: Unknown size, total: Unknown size) to C:\\Users\\dmitryso\\tensorflow_datasets\\ag_news_subset\\1.0.0...\u001b[0m\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Dl Completed...: 0 url [00:00, ? url/s]\n", - "Dl Completed...: 0%| | 0/1 [00:00" + "" ] }, - "execution_count": 18, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -241,16 +198,16 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "array([[1, 1, 0, 2, 0, 0, 0, 0, 0]])" + "array([[1, 1, 0, 2, 0, 0, 0, 0, 0]], dtype=int64)" ] }, - "execution_count": 19, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -264,8 +221,7 @@ " 'Its hot outside.',\n", " ]\n", "sc_vectorizer.fit_transform(corpus)\n", - "sc_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()\n", - "\n" + "sc_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()" ] }, { @@ -277,16 +233,16 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "array([0., 0., 0., ..., 0., 0., 0.], dtype=float32)" + "array([0., 5., 0., ..., 0., 0., 0.], dtype=float32)" ] }, - "execution_count": 20, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -316,7 +272,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -335,23 +291,23 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "938/938 [==============================] - 88s 94ms/step - loss: 0.5466 - acc: 0.8759 - val_loss: 0.3682 - val_acc: 0.8950\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n" + "938/938 [==============================] - 66s 70ms/step - loss: 0.6144 - acc: 0.8427 - val_loss: 0.4416 - val_acc: 0.8697\n" ] }, { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 22, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -379,41 +335,44 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Model: \"functional_1\"\n", + "Model: \"model\"\n", "_________________________________________________________________\n", - "Layer (type) Output Shape Param # \n", + " Layer (type) Output Shape Param # \n", "=================================================================\n", - "input_1 (InputLayer) [(None, 1)] 0 \n", - "_________________________________________________________________\n", - "text_vectorization_6 (TextVe (None, None) 0 \n", - "_________________________________________________________________\n", - "tf_op_layer_OneHot (TensorFl [(None, None, 50000)] 0 \n", - "_________________________________________________________________\n", - "tf_op_layer_Sum (TensorFlowO [(None, 50000)] 0 \n", - "_________________________________________________________________\n", - "dense_1 (Dense) (None, 4) 200004 \n", + " input_1 (InputLayer) [(None, 1)] 0 \n", + " \n", + " text_vectorization (TextVec (None, None) 0 \n", + " torization) \n", + " \n", + " tf.one_hot (TFOpLambda) (None, None, 5335) 0 \n", + " \n", + " tf.math.reduce_sum (TFOpLam (None, 5335) 0 \n", + " bda) \n", + " \n", + " dense_2 (Dense) (None, 4) 21344 \n", + " \n", "=================================================================\n", - "Total params: 200,004\n", - "Trainable params: 200,004\n", + "Total params: 21,344\n", + "Trainable params: 21,344\n", "Non-trainable params: 0\n", "_________________________________________________________________\n", - "938/938 [==============================] - 79s 84ms/step - loss: 0.5221 - acc: 0.8804 - val_loss: 0.3447 - val_acc: 0.9024\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n" + "938/938 [==============================] - 73s 77ms/step - loss: 0.6057 - acc: 0.8414 - val_loss: 0.4202 - val_acc: 0.8736\n" ] }, { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 23, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -451,7 +410,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -465,10 +424,11 @@ { "data": { "text/plain": [ - "array([[1, 0, 1, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])" + "array([[1, 0, 1, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n", + " dtype=int64)" ] }, - "execution_count": 24, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -507,7 +467,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -515,16 +475,16 @@ "output_type": "stream", "text": [ "Training vectorizer\n", - "938/938 [==============================] - 10s 11ms/step - loss: 0.5207 - acc: 0.8826 - val_loss: 0.3430 - val_acc: 0.9051\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n" + "938/938 [==============================] - 7s 7ms/step - loss: 0.5929 - acc: 0.8486 - val_loss: 0.4168 - val_acc: 0.8772\n" ] }, { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 25, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -566,7 +526,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -578,7 +538,7 @@ " 0. ]])" ] }, - "execution_count": 20, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -599,7 +559,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -607,16 +567,16 @@ "output_type": "stream", "text": [ "Training vectorizer\n", - "938/938 [==============================] - 94s 101ms/step - loss: 0.3203 - acc: 0.9039 - val_loss: 0.2542 - val_acc: 0.9186\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n" + "938/938 [==============================] - 12s 12ms/step - loss: 0.4197 - acc: 0.8662 - val_loss: 0.3432 - val_acc: 0.8849\n" ] }, { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 21, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -638,7 +598,7 @@ "source": [ "## Conclusion \n", "\n", - "Even though TF-IDF representations provide frequency weights to different words, they are unable to represent meaning or order. As the famous linguist J. R. Firth said in 1935, \"The complete meaning of a word is always contextual, and no study of meaning apart from context can be taken seriously.\" We will learn how to capture contextual information from text using language modeling in a later unit." + "Even though TF-IDF representations provide frequency weights to different words, they are unable to represent meaning or order. As the famous linguist J. R. Firth said in 1935, \"The complete meaning of a word is always contextual, and no study of meaning apart from context can be taken seriously.\" We will learn how to capture contextual information from text using language modeling later in the course." ] } ], diff --git a/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb b/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb index beb6320e..c77795cf 100644 --- a/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb +++ b/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb @@ -8,22 +8,9 @@ "\n", "In our previous example, we operated on high-dimensional bag-of-words vectors with length `vocab_size`, and we were explicitly converting from low-dimensional positional representation vectors into sparse one-hot representation. This one-hot representation is not memory-efficient, in addition, each word is treated independently from each other, i.e. one-hot encoded vectors do not express any semantic similarity between words.\n", "\n", - "In this unit, we will continue exploring **News AG** dataset. To begin, let's load the data and get some definitions from the previous unit.\n" + "In this unit, we will continue exploring **News AG** dataset. To begin, let's load the data and get some definitions from the previous notebook.\n" ] }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "jupyter": { - "outputs_hidden": true - } - }, - "outputs": [], - "source": [ - "!wget -q https://raw.githubusercontent.com/MicrosoftDocs/pytorchfundamentals/main/nlp-pytorch/torchnlp.py" - ] - }, { "cell_type": "code", "execution_count": 1, @@ -33,7 +20,21 @@ "name": "stdout", "output_type": "stream", "text": [ - "Loading dataset...\n", + "Loading dataset...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "d:\\WORK\\ai-for-beginners\\5-NLP\\14-Embeddings\\data\\train.csv: 29.5MB [00:01, 18.8MB/s] \n", + "d:\\WORK\\ai-for-beginners\\5-NLP\\14-Embeddings\\data\\test.csv: 1.86MB [00:00, 11.2MB/s] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "Building vocab...\n", "Vocab size = 95812\n" ] @@ -54,7 +55,7 @@ "metadata": {}, "source": [ "\n", - "### What is embedding?\n", + "## What is embedding?\n", "\n", "The idea of **embedding** is to represent words by lower-dimensional dense vectors, which somehow reflect semantic meaning of a word. We will later discuss how to build meaningful word embeddings, but for now let's just think of embeddings as a way to lower dimensionality of a word vector. \n", "\n", @@ -62,7 +63,7 @@ "\n", "By using embedding layer as a first layer in our network, we can switch from bag-or-words to **embedding bag** model, where we first convert each word in our text into corresponding embedding, and then compute some aggregate function over all those embeddings, such as `sum`, `average` or `max`. \n", "\n", - "![Image showing an embedding classifier for five sequence words.](./images/embedding-classifier-example.png)\n", + "![Image showing an embedding classifier for five sequence words.](images/embedding-classifier-example.png)\n", "\n", "Our classifier neural network will start with embedding layer, then aggregation layer, and linear classifier on top of it:" ] @@ -136,19 +137,19 @@ "name": "stdout", "output_type": "stream", "text": [ - "3200: acc=0.6428125\n", - "6400: acc=0.68453125\n", - "9600: acc=0.7123958333333333\n", - "12800: acc=0.725703125\n", - "16000: acc=0.7365625\n", - "19200: acc=0.7464583333333333\n", - "22400: acc=0.7548214285714285\n" + "3200: acc=0.6415625\n", + "6400: acc=0.6865625\n", + "9600: acc=0.7103125\n", + "12800: acc=0.726953125\n", + "16000: acc=0.739375\n", + "19200: acc=0.75046875\n", + "22400: acc=0.7572321428571429\n" ] }, { "data": { "text/plain": [ - "(0.9526769402541186, 0.7595969289827256)" + "(0.889799795315499, 0.7623160588611644)" ] }, "execution_count": 4, @@ -176,7 +177,7 @@ "\n", "In the previous architecture, we needed to pad all sequences to the same length in order to fit them into a minibatch. This is not the most efficient way to represent variable length sequences - another apporach would be to use **offset** vector, which would hold offsets of all sequences stored in one large vector.\n", "\n", - "![Image showing an offset sequence representation](./images/offset-sequence-representation.png)\n", + "![Image showing an offset sequence representation](images/offset-sequence-representation.png)\n", "\n", "> **Note**: On the picture above, we show a sequence of characters, but in our example we are working with sequences of words. However, the general principle of representing sequences with offset vector remains the same.\n", "\n", @@ -246,19 +247,19 @@ "name": "stdout", "output_type": "stream", "text": [ - "3200: acc=0.6334375\n", - "6400: acc=0.68234375\n", - "9600: acc=0.7072916666666667\n", - "12800: acc=0.72375\n", - "16000: acc=0.73575\n", - "19200: acc=0.743125\n", - "22400: acc=0.7497767857142857\n" + "3200: acc=0.6153125\n", + "6400: acc=0.6615625\n", + "9600: acc=0.6932291666666667\n", + "12800: acc=0.715078125\n", + "16000: acc=0.7270625\n", + "19200: acc=0.7382291666666667\n", + "22400: acc=0.7486160714285715\n" ] }, { "data": { "text/plain": [ - "(23.37446267194498, 0.754118682021753)" + "(22.771553103007037, 0.7551983365323096)" ] }, "execution_count": 7, @@ -338,13 +339,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "neuronal -> 0.780479907989502\n", + "neuronal -> 0.7804799675941467\n", "neurons -> 0.7326500415802002\n", - "neural_circuits -> 0.7252851128578186\n", + "neural_circuits -> 0.7252851724624634\n", "neuron -> 0.7174385190010071\n", - "cortical -> 0.6941086053848267\n", - "brain_circuitry -> 0.6923245787620544\n", - "synaptic -> 0.6699119210243225\n", + "cortical -> 0.6941086649894714\n", + "brain_circuitry -> 0.6923246383666992\n", + "synaptic -> 0.6699118614196777\n", "neural_circuitry -> 0.6638563275337219\n", "neurochemical -> 0.6555314064025879\n", "neuronal_activity -> 0.6531826257705688\n" @@ -360,7 +361,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We can also extract vector embeddings from the word, to be used in training classification model (we only show first 20 components of the vector for clarity):" + "We can also compute vector embeddings from the word, to be used in training classification model (we only show first 20 components of the vector for clarity):" ] }, { @@ -396,7 +397,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -405,7 +406,7 @@ "('queen', 0.7118192911148071)" ] }, - "execution_count": 11, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -424,7 +425,7 @@ "\n", "Another method, **GloVe**, leverages the idea of co-occurence matrix, uses neural methods to decompose co-occurrence matrix into more expressive and non linear word vectors.\n", "\n", - "You can play with the example by changing embeddings to FastText and GloVe, since gensim supports " + "You can play with the example by changing embeddings to FastText and GloVe, since gensim supports several different word embedding models." ] }, { @@ -438,7 +439,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 11, "metadata": { "tags": [] }, @@ -481,29 +482,29 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "3200: acc=0.63875\n", - "6400: acc=0.693125\n", - "9600: acc=0.7176041666666667\n", - "12800: acc=0.7321875\n", - "16000: acc=0.7454375\n", - "19200: acc=0.7559375\n", - "22400: acc=0.7631696428571428\n" + "3200: acc=0.6359375\n", + "6400: acc=0.68109375\n", + "9600: acc=0.7067708333333333\n", + "12800: acc=0.723671875\n", + "16000: acc=0.73625\n", + "19200: acc=0.7463541666666667\n", + "22400: acc=0.7560714285714286\n" ] }, { "data": { "text/plain": [ - "(218.64081493921944, 0.7667146513115803)" + "(214.1013875559821, 0.7626759436980166)" ] }, - "execution_count": 13, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -528,7 +529,15 @@ "cell_type": "code", "execution_count": 14, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|█████████▉| 399999/400000 [00:15<00:00, 25411.14it/s]\n" + ] + } + ], "source": [ "vocab = torchtext.vocab.GloVe(name='6B', dim=50)" ] @@ -660,12 +669,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "One of the reasons we are not seeing significant increase in accuracy is due to the fact that some words from our dataset are missing in the pre-trained GloVe vocabulary, and thus they are essentially ignored. To overcome this fact, we can train our own embeddings on our dataset. \n", - "\n", - "\n", - "## Training your own embeddings\n", - "\n", - "In our examples, we have been using pre-trained semantic embeddings, but it is interesting to see how those embeddings can be trained using either CBoW, or Skip-gram architectures. This exercise goes beyond this module, but those interested might want to check out this [official PyTorch tutorial on Language Modeling](https://pytorch.org/tutorials/beginner/nlp/word_embeddings_tutorial.html). Also, **gensim** framework can be used to train most commonly used embeddings in a few lines of code, as described [in this documentation](https://pytorch.org/tutorials/beginner/nlp/word_embeddings_tutorial.html)." + "One of the reasons we are not seeing significant increase in accuracy is due to the fact that some words from our dataset are missing in the pre-trained GloVe vocabulary, and thus they are essentially ignored. To overcome this fact, we can train our own embeddings on our dataset. " ] }, { @@ -685,10 +689,13 @@ } ], "metadata": { + "interpreter": { + "hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5" + }, "kernelspec": { "display_name": "py37_pytorch", "language": "python", - "name": "conda-env-py37_pytorch-py" + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -700,7 +707,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.7" + "version": "3.8.12" } }, "nbformat": 4, diff --git a/5-NLP/14-Embeddings/EmbeddingsTF.ipynb b/5-NLP/14-Embeddings/EmbeddingsTF.ipynb index ec520127..5d47a26f 100644 --- a/5-NLP/14-Embeddings/EmbeddingsTF.ipynb +++ b/5-NLP/14-Embeddings/EmbeddingsTF.ipynb @@ -2,48 +2,32 @@ "cells": [ { "cell_type": "markdown", + "metadata": {}, "source": [ "## Embeddings\n", "\n", "In our previous example, we operated on high-dimensional bag-of-words vectors with length `vocab_size`, and we explicitly converted low-dimensional positional representation vectors into sparse one-hot representation. This one-hot representation is not memory-efficient. In addition, each word is treated independently from each other, so one-hot encoded vectors don't express semantic similarities between words.\n", "\n", "In this unit, we will continue exploring the **News AG** dataset. To begin, let's load the data and get some definitions from the previous unit." - ], - "metadata": {} + ] }, { "cell_type": "code", - "source": [ - "import sys\n", - "!{sys.executable} -m pip install --quiet tensorflow_datasets==4.4.0\n", - "!cd ~ && wget -q -O - https://mslearntensorflowlp.blob.core.windows.net/data/tfds-ag-news.tgz | tar xz" - ], - "outputs": [], "execution_count": 2, - "metadata": {} - }, - { - "cell_type": "code", + "metadata": {}, + "outputs": [], "source": [ "import tensorflow as tf\n", "from tensorflow import keras\n", "import tensorflow_datasets as tfds\n", "import numpy as np\n", "\n", - "# In this tutorial, we will be training a lot of models. In order to use GPU memory cautiously,\n", - "# we will set tensorflow option to grow GPU memory allocation when required.\n", - "physical_devices = tf.config.list_physical_devices('GPU') \n", - "if len(physical_devices)>0:\n", - " tf.config.experimental.set_memory_growth(physical_devices[0], True)\n", - "\n", "ds_train, ds_test = tfds.load('ag_news_subset').values()" - ], - "outputs": [], - "execution_count": 3, - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "\n", "### What's an embedding?\n", @@ -62,11 +46,38 @@ "* `Embedding` layer, which takes $n$ numbers, and reduces each number to a dense vector of a given length (100 in our example). Thus, the input tensor of shape $n$ will be transformed into an $n\\times 100$ tensor. \n", "* Aggregation layer, which takes the average of this tensor along the first axis, i.e. it will compute the average of all $n$ input tensors corresponding to different words. To implement this layer, we will use a `Lambda` layer, and pass into it the function to compute the average. The output will have shape of 100, and it will be the numeric representation of the whole input sequence.\n", "* Final `Dense` linear classifier." - ], - "metadata": {} + ] }, { "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model: \"sequential\"\n", + "_________________________________________________________________\n", + " Layer (type) Output Shape Param # \n", + "=================================================================\n", + " text_vectorization (TextVec (None, None) 0 \n", + " torization) \n", + " \n", + " embedding (Embedding) (None, None, 100) 3000000 \n", + " \n", + " lambda (Lambda) (None, 100) 0 \n", + " \n", + " dense (Dense) (None, 4) 404 \n", + " \n", + "=================================================================\n", + "Total params: 3,000,404\n", + "Trainable params: 3,000,404\n", + "Non-trainable params: 0\n", + "_________________________________________________________________\n" + ] + } + ], "source": [ "vocab_size = 30000\n", "batch_size = 128\n", @@ -80,45 +91,41 @@ " keras.layers.Dense(4, activation='softmax')\n", "])\n", "model.summary()" - ], - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Model: \"sequential_1\"\n", - "_________________________________________________________________\n", - "Layer (type) Output Shape Param # \n", - "=================================================================\n", - "text_vectorization_1 (TextVe (None, None) 0 \n", - "_________________________________________________________________\n", - "embedding_1 (Embedding) (None, None, 100) 3000000 \n", - "_________________________________________________________________\n", - "lambda_1 (Lambda) (None, 100) 0 \n", - "_________________________________________________________________\n", - "dense_1 (Dense) (None, 4) 404 \n", - "=================================================================\n", - "Total params: 3,000,404\n", - "Trainable params: 3,000,404\n", - "Non-trainable params: 0\n", - "_________________________________________________________________\n" - ] - } - ], - "execution_count": 6, - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "In the `summary` printout, in the **output shape** column, the first tensor dimension `None` corresponds to the minibatch size, and the second corresponds to the length of the token sequence. All token sequences in the minibatch have different lengths. We'll discuss how to deal with it in the next section.\n", "\n", "Now let's train the network:" - ], - "metadata": {} + ] }, { "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Training vectorizer\n", + "938/938 [==============================] - 20s 20ms/step - loss: 0.7891 - acc: 0.8155 - val_loss: 0.4470 - val_acc: 0.8642\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "def extract_text(x):\n", " return x['title']+' '+x['description']\n", @@ -131,128 +138,140 @@ "\n", "model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'])\n", "model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))" - ], - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Training vectorizer\n", - "938/938 [==============================] - 12s 13ms/step - loss: 0.7953 - acc: 0.8113 - val_loss: 0.4496 - val_acc: 0.8657\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n" - ] - }, - { - "output_type": "execute_result", - "execution_count": 7, - "data": { - "text/plain": "" - }, - "metadata": {} - } - ], - "execution_count": 7, - "metadata": {} + ] }, { "cell_type": "markdown", - "source": [ - "> **Note** that we are building vectorizer based on a subset of the data. This is done in order to speed up the process, and it might result in a situation when not all tokens from our text is present in the vocabulary. In this case, those tokens would be ignored, which may result in slightly lower accuracy. However, in real life a subset of text often gives a good vocabulary estimation." - ], "metadata": { "nteract": { "transient": { "deleting": false } } - } + }, + "source": [ + "> **Note** that we are building vectorizer based on a subset of the data. This is done in order to speed up the process, and it might result in a situation when not all tokens from our text is present in the vocabulary. In this case, those tokens would be ignored, which may result in slightly lower accuracy. However, in real life a subset of text often gives a good vocabulary estimation." + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "### Dealing with variable sequence sizes\n", "\n", "Let's understand how training happens in minibatches. In the example above, the input tensor has dimension 1, and we use 128-long minibatches, so that actual size of the tensor is $128 \\times 1$. However, the number of tokens in each sentence is different. If we apply the `TextVectorization` layer to a single input, the number of tokens returned is different, depending on how the text is tokenized:" - ], - "metadata": {} + ] }, { "cell_type": "code", - "source": [ - "print(vectorizer('Hello, world!'))\n", - "print(vectorizer('I am glad to meet you!'))" - ], + "execution_count": 5, + "metadata": {}, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "tf.Tensor([ 1 45], shape=(2,), dtype=int64)\n", "tf.Tensor([ 112 1271 1 3 1747 158], shape=(6,), dtype=int64)\n" ] } ], - "execution_count": 8, - "metadata": {} + "source": [ + "print(vectorizer('Hello, world!'))\n", + "print(vectorizer('I am glad to meet you!'))" + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "However, when we apply the vectorizer to several sequences, it has to produce a tensor of rectangular shape, so it fills unused elements with the PAD token (which in our case is zero):" - ], - "metadata": {} + ] }, { "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "vectorizer(['Hello, world!','I am glad to meet you!'])" - ], - "outputs": [ - { - "output_type": "execute_result", - "execution_count": 9, - "data": { - "text/plain": "" - }, - "metadata": {} - } - ], - "execution_count": 9, - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "Here we can see the embeddings:" - ], - "metadata": {} + ] }, { "cell_type": "code", - "source": [ - "model.layers[1](vectorizer(['Hello, world!','I am glad to meet you!'])).numpy()" - ], + "execution_count": 7, + "metadata": {}, "outputs": [ { - "output_type": "execute_result", - "execution_count": 10, "data": { - "text/plain": "array([[[-0.02485236, -0.00416857, -0.06599288, ..., -0.02404598,\n 0.03529833, -0.02100844],\n [ 0.22493948, 0.01383338, 0.12420551, ..., 0.19531338,\n 0.13524376, 0.04216914],\n [ 0.04510409, 0.00708018, -0.0310419 , ..., -0.0188726 ,\n -0.0179676 , -0.04813331],\n [ 0.04510409, 0.00708018, -0.0310419 , ..., -0.0188726 ,\n -0.0179676 , -0.04813331],\n [ 0.04510409, 0.00708018, -0.0310419 , ..., -0.0188726 ,\n -0.0179676 , -0.04813331],\n [ 0.04510409, 0.00708018, -0.0310419 , ..., -0.0188726 ,\n -0.0179676 , -0.04813331]],\n\n [[-0.00226152, -0.0972852 , -0.00063103, ..., 0.00504377,\n 0.22460397, 0.1497297 ],\n [-0.15621698, -0.13758421, -0.02889572, ..., -0.02577994,\n 0.03472563, 0.08767739],\n [-0.02485236, -0.00416857, -0.06599288, ..., -0.02404598,\n 0.03529833, -0.02100844],\n [-0.06490357, -0.08200071, -0.06175491, ..., -0.02477042,\n -0.06802022, -0.01040947],\n [ 0.03279151, 0.12563369, 0.06062867, ..., -0.04349922,\n -0.12154414, -0.12533969],\n [-0.14435016, -0.304014 , -0.00378676, ..., 0.05609043,\n 0.20370889, 0.28518862]]], dtype=float32)" + "text/plain": [ + "array([[[ 1.53059261e-02, 6.80514947e-02, 3.14026810e-02, ...,\n", + " -8.92002955e-02, 1.52911525e-04, -5.65562584e-02],\n", + " [ 2.57456154e-01, 2.79364467e-01, -2.03605562e-01, ...,\n", + " -2.07474351e-01, 8.31158683e-02, -2.03911960e-01],\n", + " [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n", + " -7.18549127e-04, 2.66963355e-02, -4.30646613e-02],\n", + " [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n", + " -7.18549127e-04, 2.66963355e-02, -4.30646613e-02],\n", + " [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n", + " -7.18549127e-04, 2.66963355e-02, -4.30646613e-02],\n", + " [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n", + " -7.18549127e-04, 2.66963355e-02, -4.30646613e-02]],\n", + "\n", + " [[ 1.89674050e-01, 2.61548996e-01, -3.67433839e-02, ...,\n", + " -2.07366899e-01, -1.05442435e-01, -2.36952081e-01],\n", + " [ 6.16133213e-02, 1.80511594e-01, 9.77298319e-02, ...,\n", + " -5.46628237e-02, -1.07340455e-01, -1.06589928e-01],\n", + " [ 1.53059261e-02, 6.80514947e-02, 3.14026810e-02, ...,\n", + " -8.92002955e-02, 1.52911525e-04, -5.65562584e-02],\n", + " [-4.84890305e-02, -8.41715634e-02, 1.51529670e-01, ...,\n", + " 1.28192469e-01, -7.77286515e-02, 1.26041949e-01],\n", + " [-4.17212099e-02, -5.60694858e-02, 4.08860669e-02, ...,\n", + " 8.70475471e-02, 8.92383084e-02, 1.67974353e-01],\n", + " [ 2.85779923e-01, 4.57767487e-01, 4.52292450e-02, ...,\n", + " -1.97419018e-01, -2.04659685e-01, -2.79758364e-01]]],\n", + " dtype=float32)" + ] }, - "metadata": {} + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" } ], - "execution_count": 10, - "metadata": {} + "source": [ + "model.layers[1](vectorizer(['Hello, world!','I am glad to meet you!'])).numpy()" + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "> **Note**: To minimize the amount of padding, in some cases it makes sense to sort all sequences in the dataset in the order of increasing length (or, more precisely, number of tokens). This will ensure that each minibatch contains sequences of similar length." - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "\n", "## Semantic embeddings: Word2Vec\n", @@ -271,473 +290,26 @@ "To experiment with the Word2Vec embedding pretrained on Google News dataset, we can use the **gensim** library. Below we find the words most similar to 'neural'.\n", "\n", "> **Note:** When you first create word vectors, downloading them can take some time!" - ], - "metadata": {} + ] }, { "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], "source": [ "import gensim.downloader as api\n", "w2v = api.load('word2vec-google-news-300')" - ], - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "[==================================================] 100.0% 1662.8/1662.8MB downloaded\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n", - "IOPub message rate exceeded.\n", - "The notebook server will temporarily stop sending output\n", - "to the client in order to avoid crashing it.\n", - "To change this limit, set the config variable\n", - "`--NotebookApp.iopub_msg_rate_limit`.\n", - "\n", - "Current values:\n", - "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", - "NotebookApp.rate_limit_window=3.0 (secs)\n", - "\n" - ] - } - ], - "execution_count": 11, - "metadata": {} + ] }, { "cell_type": "code", - "source": [ - "for w,p in w2v.most_similar('neural'):\n", - " print(f\"{w} -> {p}\")" - ], + "execution_count": 12, + "metadata": {}, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "neuronal -> 0.7804799675941467\n", "neurons -> 0.7326500415802002\n", @@ -752,70 +324,94 @@ ] } ], - "execution_count": 12, - "metadata": {} + "source": [ + "for w,p in w2v.most_similar('neural'):\n", + " print(f\"{w} -> {p}\")" + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "We can also extract the vector embedding from the word, to be used in training the classification model. The embedding has 300 components, but here we only show the first 20 components of the vector for clarity:" - ], - "metadata": {} + ] }, { "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 0.01226807, 0.06225586, 0.10693359, 0.05810547, 0.23828125,\n", + " 0.03686523, 0.05151367, -0.20703125, 0.01989746, 0.10058594,\n", + " -0.03759766, -0.1015625 , -0.15820312, -0.08105469, -0.0390625 ,\n", + " -0.05053711, 0.16015625, 0.2578125 , 0.10058594, -0.25976562],\n", + " dtype=float32)" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "w2v['play'][:20]" - ], - "outputs": [ - { - "output_type": "execute_result", - "execution_count": 13, - "data": { - "text/plain": "array([ 0.01226807, 0.06225586, 0.10693359, 0.05810547, 0.23828125,\n 0.03686523, 0.05151367, -0.20703125, 0.01989746, 0.10058594,\n -0.03759766, -0.1015625 , -0.15820312, -0.08105469, -0.0390625 ,\n -0.05053711, 0.16015625, 0.2578125 , 0.10058594, -0.25976562],\n dtype=float32)" - }, - "metadata": {} - } - ], - "execution_count": 13, - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "The great thing about semantic embeddings is that you can manipulate the vector encoding based on semantics. For example, we can ask to find a word whose vector representation is as close as possible to the words *king* and *woman*, and as far as possible from the word *man*:" - ], - "metadata": {} + ] }, { "cell_type": "code", - "source": [ - "w2v.most_similar(positive=['king','woman'],negative=['man'])[0]" - ], + "execution_count": 14, + "metadata": {}, "outputs": [ { - "output_type": "execute_result", - "execution_count": 14, "data": { - "text/plain": "('queen', 0.7118192911148071)" + "text/plain": [ + "('queen', 0.7118192911148071)" + ] }, - "metadata": {} + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" } ], - "execution_count": 14, - "metadata": {} + "source": [ + "w2v.most_similar(positive=['king','woman'],negative=['man'])[0]" + ] }, { "cell_type": "markdown", - "source": [ - "An example above uses some internal GenSym magic, but the underlying logic is actually quite simple. An interesting thing about embeddings is that you can perform normal vector operations on embedding vectors, and that would reflect operations on word **meanings**. The example above can be expressed in terms of vector operations: we calculate the vector corresponding to **KING-MAN+WOMAN** (operations `+` and `-` are performed on vector representations of corresponding words), and then find the closest word in the dictionary to that vector:" - ], "metadata": { "tags": [] - } + }, + "source": [ + "An example above uses some internal GenSym magic, but the underlying logic is actually quite simple. An interesting thing about embeddings is that you can perform normal vector operations on embedding vectors, and that would reflect operations on word **meanings**. The example above can be expressed in terms of vector operations: we calculate the vector corresponding to **KING-MAN+WOMAN** (operations `+` and `-` are performed on vector representations of corresponding words), and then find the closest word in the dictionary to that vector:" + ] }, { "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'queen'" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# get the vector corresponding to kind-man+woman\n", "qvec = w2v['king']-1.7*w2v['man']+1.7*w2v['woman']\n", @@ -824,31 +420,20 @@ "min_idx = np.argmin(d)\n", "# find the corresponding word\n", "w2v.index2word[min_idx]" - ], - "outputs": [ - { - "output_type": "execute_result", - "execution_count": 15, - "data": { - "text/plain": "'queen'" - }, - "metadata": {} - } - ], - "execution_count": 15, - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "> **NOTE**: We had to add a small coefficients to *man* and *woman* vectors - try removing them to see what happens.\n", "\n", "To find the closest vector, we use TensorFlow machinery to compute a vector of distances between our vector and all vectors in the vocabulary, and then find the index of minimal word using `argmin`." - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "While Word2Vec seems like a great way to express word semantics, it has many disadvantages, including the following:\n", "\n", @@ -860,11 +445,11 @@ "Another method, **GloVe**, uses a different approach to word embeddings, based on the factorization of the word-context matrix. First, it builds a large matrix that counts the number of word occurences in different contexts, and then it tries to represent this matrix in lower dimensions in a way that minimizes reconstruction loss.\n", "\n", "The gensim library supports those word embeddings, and you can experiment with them by changing the model loading code above." - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "## Using pretrained embeddings in Keras\n", "\n", @@ -873,11 +458,24 @@ "### Using tokenizer vocabulary\n", "\n", "When using the tokenizer vocabulary, some of the words from the vocabulary will have corresponding Word2Vec embeddings, and some will be missing. Given that our vocabulary size is `vocab_size`, and the Word2Vec embedding vector length is `embed_size`, the embedding layer will be repesented by a weight matrix of shape `vocab_size`$\\times$`embed_size`. We will populate this matrix by going through the vocabulary:" - ], - "metadata": {} + ] }, { "cell_type": "code", + "execution_count": 9, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Embedding size: 300\n", + "Populating matrix, this will take some time...Done, found 4551 words, 784 words missing\n" + ] + } + ], "source": [ "embed_size = len(w2v.get_vector('hello'))\n", "print(f'Embedding size: {embed_size}')\n", @@ -895,33 +493,22 @@ " not_found+=1\n", "\n", "print(f\"Done, found {found} words, {not_found} words missing\")" - ], - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Embedding size: 300\n", - "Populating matrix, this will take some time...Done, found 4551 words, 784 words missing\n" - ] - } - ], - "execution_count": 16, - "metadata": { - "tags": [] - } + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "For words that are not present in the Word2Vec vocabulary, we can either leave them as zeroes, or generate a random vector.\n", "\n", "Now we can define an embedding layer with pretrained weights:" - ], - "metadata": {} + ] }, { "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], "source": [ "emb = keras.layers.Embedding(vocab_size,embed_size,weights=[W],trainable=False)\n", "model = keras.models.Sequential([\n", @@ -929,47 +516,47 @@ " keras.layers.Lambda(lambda x: tf.reduce_mean(x,axis=1)),\n", " keras.layers.Dense(4, activation='softmax')\n", "])" - ], - "outputs": [], - "execution_count": 17, - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "Now let's train our model. " - ], - "metadata": {} + ] }, { "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "938/938 [==============================] - 10s 10ms/step - loss: 1.1075 - acc: 0.7822 - val_loss: 0.9134 - val_acc: 0.8175\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'])\n", "model.fit(ds_train.map(tupelize).batch(batch_size),\n", " validation_data=ds_test.map(tupelize).batch(batch_size))" - ], - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "938/938 [==============================] - 6s 7ms/step - loss: 1.1098 - acc: 0.7849 - val_loss: 0.9145 - val_acc: 0.8159\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n" - ] - }, - { - "output_type": "execute_result", - "execution_count": 18, - "data": { - "text/plain": "" - }, - "metadata": {} - } - ], - "execution_count": 18, - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "> **Note**: Notice that we set `trainable=False` when creating the `Embedding`, which means that we're not retraining the Embedding layer. This may cause accuracy to be slightly lower, but it speeds up the training.\n", "\n", @@ -980,29 +567,58 @@ "* Load our dataset with the vocabulary from the pretrained Word2Vec model. Vocabularies used to load the dataset can be specified during loading.\n", "\n", "The latter approach seems easier, so let's implement it. First of all, we will create a `TextVectorization` layer with the specified vocabulary, taken from the Word2Vec embeddings:" - ], - "metadata": {} + ] }, { "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], "source": [ "vocab = list(w2v.vocab.keys())\n", "vectorizer = keras.layers.experimental.preprocessing.TextVectorization(input_shape=(1,))\n", "vectorizer.set_vocabulary(vocab)" - ], - "outputs": [], - "execution_count": 19, - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "The gensim word embeddings library contains a convenient function, `get_keras_embeddings`, which will automatically create the corresponding Keras embeddings layer for you." - ], - "metadata": {} + ] }, { "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 1/5\n", + "938/938 [==============================] - 20s 14ms/step - loss: 1.3377 - acc: 0.4978 - val_loss: 1.2995 - val_acc: 0.5647\n", + "Epoch 2/5\n", + "938/938 [==============================] - 10s 10ms/step - loss: 1.2587 - acc: 0.5722 - val_loss: 1.2339 - val_acc: 0.5842\n", + "Epoch 3/5\n", + "938/938 [==============================] - 10s 10ms/step - loss: 1.1980 - acc: 0.5884 - val_loss: 1.1826 - val_acc: 0.5954\n", + "Epoch 4/5\n", + "938/938 [==============================] - 12s 13ms/step - loss: 1.1503 - acc: 0.6002 - val_loss: 1.1417 - val_acc: 0.6018\n", + "Epoch 5/5\n", + "938/938 [==============================] - 11s 12ms/step - loss: 1.1120 - acc: 0.6097 - val_loss: 1.1083 - val_acc: 0.6104\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "model = keras.models.Sequential([\n", " vectorizer, \n", @@ -1012,50 +628,18 @@ "])\n", "model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'])\n", "model.fit(ds_train.map(tupelize).batch(128),validation_data=ds_test.map(tupelize).batch(128),epochs=5)" - ], - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Epoch 1/5\n", - "938/938 [==============================] - 7s 7ms/step - loss: 1.3381 - acc: 0.4961 - val_loss: 1.2996 - val_acc: 0.5682\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n", - "Epoch 2/5\n", - "938/938 [==============================] - 7s 7ms/step - loss: 1.2591 - acc: 0.5714 - val_loss: 1.2340 - val_acc: 0.5839\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n", - "Epoch 3/5\n", - "938/938 [==============================] - 7s 7ms/step - loss: 1.1983 - acc: 0.5883 - val_loss: 1.1827 - val_acc: 0.5951\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n", - "Epoch 4/5\n", - "938/938 [==============================] - 7s 7ms/step - loss: 1.1505 - acc: 0.6001 - val_loss: 1.1417 - val_acc: 0.6021\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n", - "Epoch 5/5\n", - "938/938 [==============================] - 7s 7ms/step - loss: 1.1122 - acc: 0.6093 - val_loss: 1.1084 - val_acc: 0.6103\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n" - ] - }, - { - "output_type": "execute_result", - "execution_count": 20, - "data": { - "text/plain": "" - }, - "metadata": {} - } - ], - "execution_count": 20, - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ - "One of the reasons we're not seeing higher accuracy is because some words from our dataset are missing in the pretrained GloVe vocabulary, and thus they are essentially ignored. To overcome this, we can train our own embeddings based on our dataset. \n", - "\n", - "\n", - "## Training your own embeddings\n", - "\n", - "In our examples, we have been using pretrained semantic embeddings, but it is interesting to see how those embeddings can be trained using either CBoW, or skip-gram architectures. This exercise goes beyond this module, but those interested might want to check out this [official TensorFlow tutorial on training Word2Vec model](https://www.tensorflow.org/tutorials/text/word2vec). Also, the **gensim** framework can be used to train the most commonly used embeddings in a few lines of code, as described [in the official documentation](https://radimrehurek.com/gensim/auto_examples/tutorials/run_word2vec.html#training-your-own-model)." - ], - "metadata": {} + "One of the reasons we're not seeing higher accuracy is because some words from our dataset are missing in the pretrained GloVe vocabulary, and thus they are essentially ignored. To overcome this, we can train our own embeddings based on our dataset. " + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "## Contextual embeddings\n", "\n", @@ -1066,30 +650,32 @@ "- John wants to **play** with his friends.\n", "\n", "The pretrained embeddings we talked about represent both meanings of the word 'play' in the same embedding. To overcome this limitation, we need to build embeddings based on the **language model**, which is trained on a large corpus of text, and *knows* how words can be put together in different contexts. Discussing contextual embeddings is out of scope for this tutorial, but we will come back to them when talking about language models in the next unit.\n" - ], - "metadata": {} + ] } ], "metadata": { + "interpreter": { + "hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5" + }, + "kernel_info": { + "name": "conda-env-py37_tensorflow-py" + }, "kernelspec": { - "name": "conda-env-py37_tensorflow-py", + "display_name": "py37_tensorflow", "language": "python", - "display_name": "py37_tensorflow" + "name": "python3" }, "language_info": { - "name": "python", - "version": "3.7.9", - "mimetype": "text/x-python", "codemirror_mode": { "name": "ipython", "version": 3 }, - "pygments_lexer": "ipython3", + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", "nbconvert_exporter": "python", - "file_extension": ".py" - }, - "kernel_info": { - "name": "conda-env-py37_tensorflow-py" + "pygments_lexer": "ipython3", + "version": "3.8.12" }, "nteract": { "version": "nteract-front-end@1.0.0" @@ -1097,4 +683,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/5-NLP/14-Embeddings/README.md b/5-NLP/14-Embeddings/README.md new file mode 100644 index 00000000..e69de29b diff --git a/5-NLP/14-Embeddings/images/embedding-classifier-example.png b/5-NLP/14-Embeddings/images/embedding-classifier-example.png new file mode 100644 index 00000000..3958e3fa Binary files /dev/null and b/5-NLP/14-Embeddings/images/embedding-classifier-example.png differ diff --git a/5-NLP/14-Embeddings/images/example-algorithms-for-converting-words-to-vectors.png b/5-NLP/14-Embeddings/images/example-algorithms-for-converting-words-to-vectors.png new file mode 100644 index 00000000..500bad2f Binary files /dev/null and b/5-NLP/14-Embeddings/images/example-algorithms-for-converting-words-to-vectors.png differ diff --git a/5-NLP/14-Embeddings/images/offset-sequence-representation.png b/5-NLP/14-Embeddings/images/offset-sequence-representation.png new file mode 100644 index 00000000..2eb982e8 Binary files /dev/null and b/5-NLP/14-Embeddings/images/offset-sequence-representation.png differ diff --git a/5-NLP/15-LanguageModeling/README.md b/5-NLP/15-LanguageModeling/README.md new file mode 100644 index 00000000..7541e1f5 --- /dev/null +++ b/5-NLP/15-LanguageModeling/README.md @@ -0,0 +1,20 @@ + +# Language Modeling + +Semantic embeddings, such as Word2Vec and GloVe, are in fact a first step towards **language modeling** - creating models that somehow *understand* (or *represent*) the nature of the language. + +The main idea behind language modeling is training them on unlabeled datesets in unsupervised manner. It is important, because we have huge amounts of unlabeled text available, while the amount of labeled text would always be limited by the amount of effort we can spend on labeling. Most often, we build language models that can **predict missing words** in the text, because it is easy to mask out a random word in text and use it as a training sample. + +## Training embeddings + +In our previous examples, we have been using pre-trained semantic embeddings, but it is interesting to see how those embeddings can be trained using either CBoW, or Skip-gram architectures. + +![](../14-Embeddings/images/example-algorithms-for-converting-words-to-vectors.png) + +The idea of CBoW is exactly predicting a missing word, however, to do this we take a small sliding window of text tokens (we can denote them from W-2 to W2), and train a model to predict the central word W0 from few surrounding words. + +## More Info + +* [Official PyTorch tutorial on Language Modeling](https://pytorch.org/tutorials/beginner/nlp/word_embeddings_tutorial.html). +* [Official TensorFlow tutorial on training Word2Vec model](https://www.tensorflow.org/tutorials/text/word2vec). +* Using **gensim** framework to train most commonly used embeddings in a few lines of code is as described [in this documentation](https://pytorch.org/tutorials/beginner/nlp/word_embeddings_tutorial.html). diff --git a/5-NLP/README.md b/5-NLP/README.md index 5ea06aa8..fa9f7b3f 100644 --- a/5-NLP/README.md +++ b/5-NLP/README.md @@ -33,7 +33,23 @@ pip install -r requirements-torch.txt pip install -r requirements-tf.txt ``` +## GPU Warning + +In this section, in some of the examples we will be training quite large models. It is advisable to run notebooks on GPU-enabled compute to minimize waiting time. + +When running on GPU, you may experience situations when you run out of GPU memory. During training, the amount of GPU memory consumed depends on many factors, including minibatch size. If you experience any memory problems - you may try to minimize the minibatch size in the code. + +Also, some older versions of Tensorflow do not release GPU memory correctly if we are training multiple models in one Python kernel. In order to use GPU memory cautiously, you may set tensorflow option to grow GPU memory allocation only when required. You would need to include the following code in your notebooks: + +```python +physical_devices = tf.config.list_physical_devices('GPU') +if len(physical_devices)>0: + tf.config.experimental.set_memory_growth(physical_devices[0], True) +``` + ## Contents * [Representing text as tensors](13-TextRep/README.md) * [Word Embeddings](14-Emdeddings/README.md) +* [Language Modeling](15-LanguageModeling/README.md) + diff --git a/README.md b/README.md index 0080b20a..42d2a503 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,8 @@ For a gentle introduction to *AI in the Cloud* topic you may consider taking [Ge MS Learn PAT 13Text Representation. Bow/TF-IDFTextPyTorchTensorflow -14Semantic word embeddingsTextPyTorchTensorflow -15Training your own embeddingsTextPyTorchTensorflow +14Semantic word embeddings. Word2Vec and GloVeTextPyTorchTensorflow +15Language Modeling. Training your own embeddingsTextPyTorchTensorflow 16Recurrent Neural NetworksTextPyTorchTensorflow 17Generative Recurrent NetworksTextPyTorchTensorflow 18Language Modelling. Transformers. BERT.TextPyTorchTensorflow