Transcription
Hello everyone and welcome to the natural language processing full course where we will explore how AI learns to read, understand and generate human language. NLP powers everyday tools like chatbots, Siri, Alexa, translation apps and even chat GPT.
So in this course we will start with the basics of text processing and gradually move into advanced deep learning models like transformers and attention. You will also get hands-on experience building projects such as sentiment analysis, summarization and chatbot development using powerful tools like NLTK, hugging face and open AI APIs. And by the end you will be ready to apply NLP in real world applications and unlock exciting career opportunities in AI.
So before we begin, please like, share and subscribe to Edureka's YouTube channel and hit the bell icon to stay updated on the latest tech content from Edureka. Also do check out Edureka's postgraduate program in generative AI and machine learning in collaboration with Illinois Tech. It offers a unique opportunity to explore the cutting-edge world of generative AI and develop advanced AI powered solutions. This program covers in-demand topics including machine learning, deep learning, natural language processing from engineering, generative AI, LLM, RAG, agentic AI and much more. Learn from industry experts through a curriculum built around real-world hands-on use cases designed to equip you with practical and job-ready skills. So check out the course link given in the description box below.
Now let's get started with our first topic that is what is natural language processing. Well, human beings are the most advanced species on earth. There's no doubt in that. And our success as human beings is because of our ability to communicate and share information. Now that's where the concept of developing a language comes in. And when we talk about the human language, it is one of the most diverse and complex part of us. Considering a total of 6,500 languages that exist. So coming to the 21st century, according to industry estimates, only 21% of the available data is present in the structured form. Data is being generated as you speak, tweet and send messages on WhatsApp or the various other groups of Facebook. And majority of this data exists in the textual form which is highly unstructured in nature. Now in order to produce significant and actionable insights from this data, it is important to get acquainted with the techniques of text analysis and natural language processing.
So let's understand what is text mining and natural language processing. So text mining or text analytics is the process of deriving meaningful information from natural language text. It usually involves the process of structuring the input text, deriving patterns within the structured data and finally evaluating and interpreting the output. Now on the other hand, natural language processing refers to the artificial intelligence method of communicating with an intelligence system using the natural language. As text mining refers to the process of deriving high-quality information from the text, the overall goal is here to essentially turn the text into data analysis via the application of natural language processing. That is why text mining and NLP go hand in hand.
So let's understand some of the applications of text mining or natural language processing. So one of the first and the most important applications of natural language processing is sentiment analysis. Be it Twitter sentiment analysis or the Facebook sentiment analysis, it's being used heavily. Now next we have the implementation of chatbots. Now you might have used the customer chat services provided by various companies and the process behind all of that is because of NLP. Now we have speech recognition and here we are also talking about the voice assistants like Siri, Google Assistant and Cortana. And the process behind all of this is because of natural language processing. Now machine translation is also another use case of natural language processing. And the most common example for it is Google Translate which uses NLP to translate data from one language to another and that too in real time. Now other applications of NLP include spell checking, keyword search and also extracting information from any doc or any website. And finally, one of the coolest applications of natural language processing is advertisement matching. Basically, recommendation of ads based on your history.
NLP is divided into two major components that is natural language understanding and natural language generation. The understanding generally refers to mapping the given input into natural language into a useful representation and analyzing those aspects of the language. Whereas generation is the process of producing meaningful phrases and sentences in the form of natural language from some internal representation. Now, natural language understanding is usually harder than natural language generation because it takes a lot of time and a lot of things to usually understand a particular language, especially if you are not a human being.
Now there are various steps involved in natural language processing which are tokenization, stemming, lemmatization, POS tags, named entity recognition and chunking.
Now starting with tokenization. Tokenization is the process of breaking strings into tokens which in turn are small structures or units that can be used for tokenization. So if we have a look at the example here, taking this sentence into consideration, it can be divided into seven tokens. Now this is very useful in the natural language processing part.
Now coming to the second process in natural language processing is stemming. Now stemming usually refers to normalizing the words into its base or root form. So if you have a look at the words here, we have affectation, affects, affections, affected, affection, and affecting. Now all of these words originate from a single root word and as you might have guessed, it is "affect". Now, stemming algorithms work by cutting off the end or the beginning of the word, taking into account a list of common prefixes and suffixes that can be found in an inflected word. This indiscriminate cutting can be successful on some occasions but not always.
So let's understand the concept of lemmatization. Now lemmatization on the other hand takes into consideration the morphological analysis of the word. To do so, it is necessary to have a detailed dictionary which the algorithm can look through to link the form back to its original word or the root word which is also known as a lemma. Now what lemmatization does is it groups together different inflected forms of the word called a lemma and is somehow similar to stemming as it maps several words into one common root. But the major difference between stemming and lemmatization is that the output of lemmatization is a proper word. For example, a lemmatizer should map the words "gone", "going", and "went" into "go". That will not be the output for stemming.
Now, once we have the tokens and once we have divided the tokens into its root form, next comes the POS tags. Now, generally speaking, the grammatical type of the word is referred to as POS tags or parts of speech. Be it the verb, noun, adjective, adverb, article, and many more. It indicates how a word functions in meaning as well as grammatically within the sentence. A word can have more than one part of speech based on the context in which it is used. For example, let's take the sentence "Google something on the internet." Here, "Google" is used as a verb, although it's a proper noun.
Now, these are some of the limitations or I should say the problems that occur while processing natural language. Now to overcome all of these challenges, we have named entity recognition, also known as NER. So it is the process of detecting the named entities such as person names, company names, quantities, or locations. Now it has three steps which are noun phrase identification, phrase classification, and entity disambiguation. So if you look at this particular example here, "Google CEO Sundar Pichai introduced the new Pixel 3 at New York Central Mall." So as you can see here, "Google" is identified as an organization, "Sundar Pichai" as a person, "New York" as a location, and "Central Mall" is also defined as an organization.
Now, once we have divided the sentences into tokens, done the stemming, the lemmatization, added the tags, added the named entity recognition, it's time for us to group it back together and make sense out of it. So for that, we have chunking. So chunking basically means picking up individual pieces of information and grouping them together into bigger pieces. Now, these bigger pieces are also known as chunks. In the context of NLP, chunking means grouping of words or tokens into chunks. So, as you can see here, we have "pink" as an adjective, "panther" as a noun, and "the" as a determiner. And all of these are together chunked into a noun phrase. Now, this helps in getting insights and meaningful information from the given text.
Now, you might be wondering where does one execute or run all of these programs and all of these functions on a given text file. So for that, Python came up with NLTK. Now, what is NLTK? NLTK is the Natural Language Toolkit library which is heavily used for all natural language processing and text analysis.
So what is NLP? Natural language processing, or in short NLP, is an automatic way of presenting or processing human language. What I'm trying to say here is here we try to develop applications and services in order to understand human language. Some of the practical examples of NLP are Google voice search, sentiment analysis, and many more. As I mentioned earlier, we use NLP to extract meaningful data from textual data, right? So do you think NLP is a magical tool that when a text is passed, we get a desired output? Well, this isn't the case. But as a matter of fact, raw text input data has to go through various stages just so that we can perform operations on the textual data set. As you see here in the pipeline, a raw text data undergoes data cleaning, which involves steps like tokenization, stop word removal, lemmatization, and many more. The next step is vectorization, where we convert our text data into numerical format. Finally, based on the requirements, we perform the classification task.
All right. Now, let's see a few of these steps in detail. Starting off with cleaning our data. As mentioned earlier, here the goal is to convert raw text into clean text data. This involves steps like tokenization, stop word removal, stemming, and many more. Speaking about tokenization, tokenization is essential for splitting a sentence, or a paragraph, or an entire text document into smaller units such as individual words or phrases. Each of these smaller units are then called as tokens. Then we have stop word removal. Stop word removal in general refers to filtering words whose presence in a sentence makes no difference to the analysis of our data. So why do we have to remove them? Well, we remove the stop words just so that, you know, our model doesn't get more complicated. In the next step, we have something called stemming. Stemming is a process of reducing a word into its root form. What I'm trying to say here is with stemming, we're basically removing the prefix. For example, consider a word "giving", right? So once stemming is performed on "giving", "giving" ends up becoming "give". Moving ahead, we have vectorization. Text vectorization is a process of converting text into numerical representation. Here we end up creating something called as a bag-of-words model, which is a model that signifies or represents a text and describes the occurrence of words in that document. Finally, coming down to the classification task. Text classification, also known as text tagging or text categorization, is a process of categorizing text into organized groups by using natural language processing. Text classification can automatically analyze text and then assign a set of predefined tags or categories based on the content.
Now let's move ahead and understand an open-source tool called as NLTK. NLTK stands for Natural Language Toolkit. This toolkit is one of the most powerful NLP libraries which contain packages to make machines understand human language and reply them in an appropriate desired response. So why do we need NLTK? You'll see NLTK has many built-in packages to process our textual data at every stage. We can perform tasks like data cleaning, visualization, vectorization that will help us in classifying our text.
So let me now move to my code editor and show you how we can preprocess or clean our data using NLTK. All right guys, as you can see here, I'm going to use Google Colab. Okay, although you can use any code editor like Jupyter Notebook or Visual Studio Code, but I would prefer to go with this. Okay. So in the next stage, now we need a dataset, right? So where will I get that? So in order to get a dataset, what I'm going to do is from sklearn.datasets import fetch_20newsgroups. Right. This fetch_20newsgroups will give us a dataset. So let's quickly see how that would look like. All right. So if I have to execute this, I just need to press Shift Enter. Okay. So let's see our textual data now. Okay. Text data is nothing but we are going to create an instance of our fetch_20newsgroups. So we'll copy this here and call it. So what would this return? This would return us a bunch. Bunch of objects. So let's now quickly execute this. If you're downloading this for the first time, it would take some time to download our dataset. Okay. And this dataset is present in this particular link. Fine. So this is done now. So let's quickly look at how this data would look like. Okay. So in order to do that, we are going to use, like, type data.text, and then you will see here, this is a bunch. So let's now import our NumPy and convert this back to a list because we cannot perform any operations on a bunch. We have to convert this into a list. So import numpy as np. Uh, let's execute this. And now what we're going to do is, let's give it as raw text. So raw text over here would be equal to text_data. Okay. Dot data. Okay. So let's print how this data would look like. Raw text. Okay. So as you can see here, we have a huge amount of dataset. Fine. And all of these are separated by commas. Let me quickly show you here. Okay. So as you can see, first off, we have a list and then we also have huge sentences or you can say paragraphs which are separated by commas. And make sure you don't confuse this to CSV. And now what we're going to do is, as a list, right, we don't want to take the entire dataset because it's going to be computationally expensive and apart from that, it will take more time to execute, right? So, and to get a better understanding of what we're doing, we're going to take only the first four paragraphs or sentences, I would say. Fine. So, in order to do that, all I'm going to do is I'm going to use slice operation, have a colon, and put four. So, let's see how this would look like now. Okay. So, as you can see here, we just have the first four paragraphs of sentences. Let me zoom out here just so that you get a better view. So, yeah, fine. So, now we have our text data and we are supposed to start cleaning our data, right? So let's start doing that. What we're going to do is first off, let's reduce all the upper cases into lower case. Okay. So let me just give here a text and give a heading "Step Stage One: Convert into Lower Text". Fine. And now what we're going to do is in order to convert to a lower text. So we'll just give here lower_text. So this is just going to be an array. Let's ignore this for now. So as we supposed to do this, like we are supposed to, no matter what input you take, right, like whether it's for a training or a testing dataset, you obviously have to convert it. Rather than writing the entire code here, let's give it a method so that, you know, next time if you want to lower our text, we just call the method. So def lower_text(data): This is the name of our method and then we're going to pass data here that would be an argument. Okay. And we have a for loop now. For words in this data, raw_text. Fine. For words in raw_text, what we're going to do is we have to append it to this part. So let's give here as clean_text. In order to get a better understanding, clean_text_stage_one. So I hope this is fine. Okay. Let me quickly copy this and we're going to append this part. Append. Okay. So we're supposed to convert our words into lowercase, right? So it's going to be str.lower(). This is a built-in method and all we're going to do is we're going to pass as words. Okay. So now we have a method. So let's call this method. So to_lower_text(raw_text). Okay. So it's not st, it's going to be str here. Let me quickly execute that once again. Okay. Perfect. So now what we're going to do is let's compare our raw text with this. So we have something like clean_text_stage_one, right? Clean_text. So let me execute this. So as you can see, we have "from" which is capital uppercase and we have "what car is this" and here everything has been converted to lowercase. Okay. In the next stage, we have tokenization. As I've mentioned earlier, what we do with tokenization is whatever the sentence is there or a paragraph, we convert that into either, you know, individual sentences or into individual words. So in order to convert this into words, we have something called as word_tokenizer and to convert this into a sentence, we have sent_tokenizer. So let me quickly go here and give this name of the next block as "Stage Two: Tokenization". Okay. And let's see how our tokens would look like. Fine. Same like before, we have to write one array here. So let's give it as clean_text_stage_two. And this is going to be empty. Right. And now what we're going to do is from nltk.tokenize import sent_tokenize. Okay. And then we also need word_tokenize. Here I'm going to show sentence tokenizer only for the demo sake. We're going to use word tokenizer in future. Okay. So before this, we also have to download something that's called a punkt. Okay. So import nltk and then we have nltk.download('punkt'). Fine. Let me Shift Enter. All right. So now what we're going to do is we are going to perform sentence tokenizer. For sentence in obviously we need this data, right? Clean_text_one. So clean_text_one, what we're going to do is sentence = sent_tokenize(sentence). As simple as that. And we are going to pass this sentence. Okay. And now in order to obviously we have to store this somewhere. We're going to store this in a new variable here. Sent_tokens. And this is just for showing you, right? This is just for the demo. That's why I'm not going to use clean_text_three over here or two. Okay. So we'll append this. Sent_tokens.append(sentence). And we're going to append the sentence here. Okay. So looks good. So let's see how this would look like. So as you can see here, right? Earlier we have a single dimension array. Okay. And here, each of these over here represents a paragraph. Now within a paragraph, we all know we have multiple sentences. So as you can see now, we have become a two-dimension array and each of these within this array represents a paragraph and each of the sentence in this paragraph has become a word or one particular character. Okay. So now we'll do word tokenize and we move ahead. We need word tokenization. So we'll add this to a clean_text_two. Okay. Because this is a part of this thing. So what we're going to do is we're going to perform word tokenization. So let me give a comment here. Okay. So now what I'm going to do is for word_tokenize, you know, rather than writing this for loop, I'm going to show you a simple and easy way. What we were doing so far is we are initializing this array here and then writing a for loop and appending it. Right? We can also do something called as, you know, list comprehension. Let me just show you what it is. So we have this, right? Clean_text_two. Now in list comprehension, what will happen is the for loop we write it within our list. Okay. So let's see word_tokenize. And we obviously want some kind of textual data over here which we'll fill it in a while. So over here I'm going to write a for loop. For sentence in clean_text_one. Okay. And now whatever words that come out of sentences I want them to be tokenized. So let us now see how this clean_text would look like. So as you can see here, we have every word within this sentence converted into form of tokens. Let me scroll this up so that you can see how it looks. So still it's a two-dimension array. You know, whatever is there within this first array here, within this array represents a paragraph. Okay. So everything over here has been converted into single word tokens. Fine. So next stage, right, we want to remove some punctuations. You see in our dataset, we have some special characters, punctuations. We don't want these ampersands and dots. So in order to do that, we are going to use something called as regular expression. Okay. So let me quickly show you how we can implement this using regular expression. So to do that, what we're going to do is we are going to import regular expression. Fine. And now what we want is, as this is a two-dimension data, so we need two for loops. Unlike previous, we had only one one-dimension data, but now it's going to be 2D data structure. Okay. So we'll create something here, an empty list, which is clean_text_three. And then we're going to have a for loop. So for words in clean_text_two, we're going to create one array here. And now we're going to have another for loop to access inner words. Right? So for w in words. Now for regular expression, what we are going to do is we're going to show a pattern. Okay. So S is equal to re.sub. So wherever that particular character is there, we want to substitute it with something else. So re.sub, then we have R, which is going to be obviously a comment over here. Then we have a carrot symbol followed by W followed by S. We want to replace this with an empty string here. And then word. All right. And now what we're going to do is if S is not null, if S is not empty, then what we want to do is we want to append that word. Okay. Clean.append(w). Finally, we have to add this clean to our main array. Okay. So let's define that array as well. Clean_text_three. You have to give it an array here. Correct. Fine. So let us now append this to our array. So it's going to be clean_text_three.append and then we're going to pass just this clean array here. Fine. Okay. So let's now see how our this thing looks like. So we have clean_text_three, right? So clean_text_three. You know, according to our analysis, all of these semicolons, everything should disappear by now. Okay. So as you can see here, we don't have any special characters within our dataset. Okay. Still this is a 2D array, but we don't only difference over here is that we don't have any special characters. We have only alphanumeric values, right? Okay, this is great. So in our next stage, we're going to remove stop words. I hope you remember what is stop words. As I've mentioned earlier, stop words is nothing but, you know, those words which are most commonly repetitive. Okay. So let me now, in order to do that, let me just show you import nltk. Okay. We obviously going to download the stop words. Okay. So we have nltk.download('stopwords'). Although we can find the stop words on Google. What we can do is we can copy those stop words, put them in the form of a list, and if that stop word is present within our dataset here, make sure you don't include that in our clean text for stage. Moving ahead, let me just give a title here for "Stop Word Removal". Okay. So now that we have downloaded our stop words, what we're going to do is from nltk.corpus import stopwords. Looks great, right? Okay. So now what we're going to do is we'll have clean_text_four. Okay. Similar to the previous for loop, we are going to have for words in clean_text_three. And then we'll just create an empty list which is going to be appended to clean_text_four. We'll have for word in words. If the clean_text_three contains this words, right, the stop words, we're going to eliminate that. Okay. So if not word in stopwords.words('english'): if the word is not present in this list, what we're going to do is we're going to append because that means that word is not a stop word, right? We're going to append that word. Okay. And now we have to append this to our clean_text_four. So clean_text_four.append(word_list). So let me quickly execute this. It would take some time to execute because it has to go through a lot of dataset, right? So please be patient and let's see how it would look like. Okay, so this has finally executed and let's now see how this would look like. So clean_text_four. Okay. So as you can see here, we have removed couple of unnecessary words and we have just the important ones. All right. Now, so as you can see, we have removed couple of words over here. And yeah, so now moving ahead to our next stage, that is stemming. I hope you remember what is stemming. Stemming is nothing but, you know, whatever word we have, we have to convert that into its root form. So over here, "data processing" is there, after stemming it becomes "data process". Okay, that's what is stemming. So let me quickly show you how we can perform stemming here. Okay. So as I mentioned earlier, we use stemming just to remove this prefix, right? So for in order to perform stemming, we have various types of stemmers. So we have something like PorterStemmer, SnowballStemmer, LancasterStemmer. So today we're going to use PorterStemmer, right? So in this particular example, so let's get that now. From nltk.stem import PorterStemmer. All right. So once we have imported this, we're going to create an instance of our PorterStemmer. So porter = PorterStemmer(). Okay. So just to give an example of what we're going to do here. Let's take three words. Okay. Let's take a list. Let's say list is A, right? So A has couple of words. Rather than having words, let's pass a list within our list. Okay. So we'll have porter.stem(). This is how we call a stemmer. Okay. And here we're going to pass the word that we want to stem, right? And now we're going to have a for loop. For i in. Okay. So now let's pass couple of words like let it be like reading, washing. Let's give one word which doesn't have a prefix like wash. Then let's give driving. Okay. So let's now print this. So this going to be i here. And what output that I'm expecting over here is that "reading" should be converted into "read". "Washing" will be converted to "wash". And then "wash" would remain the same because there's no prefix. And "driving" would be converted to "driv". So let me just print here and execute this part. I hope you can see this. It read becomes read. Wash remains wash and rest everything remains the same. Okay. I have done a small typo here. So it's going to be driving. Okay. So let me execute this once again. So as you can see here, we have successfully removed all the prefixes and they do make some sense. Okay. This is the case in case of PorterStemmer. But this doesn't hold good for when we're trying to use LancasterStemmer or SnowballStemmer. So let us now quickly move ahead and see how we can implement the stemmer in our dataset here. So we all know we need our loop here. Okay. So before that, we're going to have an array. So clean_text_five. And it's going to be an empty list. So now we'll have a for loop. Another empty list. Okay. So now we're going to pass this list and we're going to append. So it's going to be word_list.append(word). It's going to be word. Okay. And now we're going to append this smaller list to this one. So clean_text_five.append(word_list). I hope this is done, right? Let me quickly execute this. Okay. And let me see how this clean_text_five looks like. Okay. So as you can see here, we don't have any more uh, yeah, obviously there are some errors. That's because, you know, this stemmer might not recognize it. That's why we have multiple other stemmers. But most of the places you can see, you know, our words have been converted down to the stem words. All right. So I hope now you understood how to perform stemming. But, you know, as I've mentioned earlier, we have multiple stemmers. We have like PorterStemmer, we have like LancasterStemmer, and each of those stemmers are unique in their own way. Sometimes what happens is when we perform stemming, we get words which make no sense, right? And that thing can sometimes be really annoying. So in order to overcome that, we have something called as lemmatization. So let me quickly show you what lemmatization is. So let me just give "Lemmatization" here. Okay. So in order to get this lemmatization, we use something called as WordNet. Okay. So from nltk.stem import WordNetLemmatizer. Okay. And we're going to create an instance of this. So it's going to be wordnet_lemmatizer. Okay. And now we obviously have to download couple of packages. So import nltk. And then we have nltk.download('wordnet'). Fine. Perfect. So now in the same way, what we're going to do is we'll create just lemmatized_words. All right. So we have lemmatized_words here. And then we have an empty array. And then we'll have a for loop. For words in clean_text_four. It's not going to be five, obviously. It's going to be clean_text_four because this is the form of stemming, right? And now what we're going to do is just the same drill. We have word_list, which would be an empty list and another for loop. And we are going to append whatever is there. word_list.append(lemmatizer.lemmatize(word)). Okay. So word.lemmatize() and whatever word we want to lemmatize. So it's going to be word here. And now once this is done, we're going to append this word_list to our bigger lemmatized_words. So lemmatized_words.append(word_list). And let me execute this now that this is done. So let's see how this would look like. Okay. So lemmatized_words. Let's print this. Okay. And let me just execute this part here. Okay. We cannot print this. That's because it's saying data is too long. So rather than printing, I'll just do this part here, lemmatized_words, so that we can just see some glimpse of how our data looks like. Okay. So, yeah, so as you can see here, although we are performing lemmatization, but now the words make sense. Just to give you a brief insight, right? Let's compare how our data looked earlier and how it looks now. Okay. So what we'll do is we'll take our raw text, that is this part here, and we'll compare this with our final text, which is nothing but clean_text_five, okay, so let's now see how it would look like. So let me quickly print them. Print raw_text. Okay. Let me execute this first. All right. And now let me print clean_text_five. Okay. As the data is pretty huge, what I'm going to do is I'm just going to slice this up over here. Okay. Let me just take our first one data. Okay. This is going to be the first sentence. And as you can see here, we have all the words which have been tokenized. And it's unlike this part here, we have everything which is, it looks organized. Okay. So obviously, we want to pre-process this data, right? Because this makes more sense and it is more easy on a system to analyze and the classification would be pretty accurate as compared to what it would be over here. All right. Now moving ahead, I hope now you understand why we need to pre-process our data. Okay. So now that we know how to pre-process our data using NLTK, let's see how classification of text is done. So, okay, in order to classify our text, we use something called as Naive Bayes algorithm. So what is this Naive Bayes algorithm? You see, before we understand this Naive Bayes algorithm, right, let us see what is classification. In simple words, classification means grouping of data based on common characteristics. As you see here, we have couple of figures, right? We have triangles, circles, and a square. And now when we pass this through a classification algorithm, all of those get categorized into different different classes. And it's totally based on the shapes, size, and whatever other features are. This is a similar way how the Naive Bayes algorithm works. Okay. So the principle that drives the Naive Bayes algorithm is something called as Bayes' theorem. And we use Bayes' theorem to calculate the conditional probability. So let us now see the math behind our conditional probability. All right. Then, as I mentioned earlier, right, we use Naive Bayes algorithm to perform classification on our textual data and Naive Bayes algorithm has something called as Bayes' theorem. Okay. And the way this Bayes' theorem works is that we have to find a conditional probability. So what is this conditional probability? You see, conditional probability, we can say mathematically like, probability of occurrence of event A when event B has already occurred is equal to probability of occurrence of event B when event A has already occurred times probability of occurrence of event A, and this would be normalized by probability of event B. Okay. I'm sure you might be having confusion like what is this, right? You see this line over here. This represents the conditional probability. Okay. So this is the conditional probability. Now, what we are going to do is let's understand what is this, right? So probability of event A and B. So let us now take event A to be like shopping and event B would be something like rain. Okay. So what is the probability of you going to shopping when it has already started raining? So this is what this means. Okay. Probability of A, B, right? So this represents "and". And you can also say it as conditional probability. So probability of occurrence of event A when B has already occurred. Okay. So when it's already raining, what is the probability that you'd be going down for shopping? And there are a couple of terminologies that you need to know when we are dealing with this. Let's quickly see that. Okay. So when we are dealing with conditional probabilities, we have a couple of terminologies as I've mentioned. So this part over here is referred to as posterior probability. Okay. This is the most important part to be found and this is called as likelihood. This part over here, probability of occurrence of event A is called as prior probability. As you can see by its name, right? Prior refers to something that has already occurred. Okay. And this part over here is the most unused part that is nothing but marginal likelihood. Okay. We call this as marginal likelihood. All right. So now speaking about probability, let's see how this concept came into existence. So we have probability only because we have something called as random variables. Okay. These random variables give rise to a randomness. Okay. To give you a better understanding of what I'm trying to say, let's take an example. Okay. So we have two bags here. We have, say, bag one and let's say bag two. Okay. And now what is happening here is in bag one, we have balls. Okay. We have red balls. Five of them. Okay. So do you think probability exists over here? Obviously not, right? So no matter whichever ball you try to pick out, we're going to get red balls. And randomness over here is zero. Okay. So let's take one more bag over here. So this bag has like five red balls and then four blue balls. So do you think probability exists over here? Absolutely. Over here you can see if I try to put down my hand and pick up any ball. So probability of getting blue is nothing but the total number of elements, right? So we have like five balls and this, so it would be 9 divided by total number of blue balls, that's nothing but four. Okay. So this is what is probability. So over here we have more randomness. Okay. This is how probability came into existence. And speaking about our conditional probability, let's try to derive this conditional probability equation. Okay. So the way we get this conditional probability is by having P(A intersection B). Okay. We all know this is equal to probability of A by B when B has already occurred. Okay. So this would be our equation one. Okay. Similarly, we know that it holds good for probability of B intersection A. Right. The reason for this is because P(A intersection B) and P(B intersection A) is commutative. Okay. So this should be similar. Only difference that we're going to have is the change in the values. So instead of A, it's going to be B here times P(A). Okay. And now when we equate these two, right, we are going to get something like probability of occurrence of event A when B has already occurred times probability of B. And this would be equal to this equation over here. All right. So let's bring this down and then we'll have probability of occurrence of event A times probability of occurrence of A divided by probability of occurrence of B. So this is called as Bayes' theorem and if you see, right, so this is something which is very much similar to what we had over here. Okay. So this is what is Bayes' theorem and this is how we derive it. So now you might be wondering how can I use this Bayes' theorem for classification problems, right? So just a quick recap, as I mentioned earlier, classification is nothing but, you know, categorizing data based on its characteristics. Okay. So here what's going to happen here? We'll have something like, say, we'll have a dataset, right? So let's take X. So we'll have X dataset. So this would be nothing but a group of values. Okay, the text data. And then we'll have Y. Y is nothing but the classes. And Y refers to the class. And what this class means is it can be like 0, 1, and so on and so forth. So here let's take something like 0 and 1. And here zero refers to being it not spam and this is spam. And here X would be nothing but a group of emails. Okay. So now let's put this in Bayes' theorem and see how it would look like. All right. So over here, we have for Bayes' theorem, we'll have something like probability that an email is spam. Okay. When we, we already have the email, is nothing but probability of this particular email being in spam class times probability of that email, all divided by P(X). Okay. And similarly, this is for spam email. And now for not spam, it would be P(Y=0). Okay. Given X, this should be nothing but given that we have a label of non-spam, and then what is the probability of that email being here? This would be times probability of Y=0, all divided by P(X). Okay. So this is how it would look like, you know, Bayes' theorem for finding whether the email is spam or not. So to better understand this, right, let's see what each of this represents. Okay. Okay. So let's take this part here. I'm pretty sure you must be confused what this part represents, right? So what this part says is, think that we have a dataset. Okay. So let's this be our dataset. Okay. So this is our dataset and we have X values here and then we have Y values. What this X values will have is nothing but emails. Okay. So this is nothing but a group of emails and this would be a class. So if the email is spam, it would be 0. If the email is not spam, it would be 1, and then 1 and 0 so on and so forth. Let's just take it as an example. And this is something which is an unknown value, right? So this part over here is an unknown. See, both of these are same. Okay. So as of now, let's just consider for a spam email. Okay. So we need to find a new email. Okay. So we'll be given like, we'll have a test. So let's call this as a train data, right? So X_train. Okay. And this is the output. This is a class. So now what will happen is I'll be given an email. So I'll be like, "Janet, find out whether this email that I'm giving you is a spam or not spam." So this is going to be like this. Okay. And then there'll be X_test. But the only difference is here we don't know which class they belong to. Okay. So what we're supposed to do here is we are supposed to train our model and figure out which class this email would belong to. Okay. So this is an email here. These are the question marks. Okay. We don't know what class does this email belong to. So what does P(X and Y=1) represents is when we are given a class Y. Okay. When we already know that an email is spam, that is this particular email. What is the probability of this email part of being this? Okay. And then we compute this part over here. And then finally, what we're going to do is when we get this test data, right, X_test data, we'll just say what is the probability of this particular email being a part of 0. Okay. Zero means not spam and one means it's a spam. So we'll basically get a numerical value over here. So for example, now let's take an example over here. So I got this X_test value. X_test is something which will be over here. So I got this X_test value, say something like "free food". So now this would be represented in spam. Right? The reason is because "spam" is a keyword which is usually found in a fake email. So what will happen over here? So probability that a given email that is X_test is spam. Okay. So probability that a given email over here, so this email, that is this email is not spam and is spam. So this would say something like this will have a high probability of being in a spam, right? So this would give us a numerical value, say something like 80, which refers to 80%. And this would give us like, say, 20. Okay. And this is nothing but 20%. So which among this is high? So obviously this particular value is high, right? Therefore, this email would be classified as a spam email. So this is how basically it works. Okay. So now in order to find these values over here, you know, that is nothing but in order to find the value of our posterior probability, we have to calculate likelihood, prior probability, and marginal likelihood. Although we can ignore marginal likelihood, this is because we're trying to normalize it. So we can ignore this part. Finding the probability of this is pretty simple because all we need to do is find the total number of spam emails by total number of emails. And similarly, I would do it for total number of non-spam by total number of spam. But the only difficult part over here to find is likelihood. So let's now see how we can do that. So to start off, let's say something like we are given some emails. Okay. So we have 100 emails. Out of this 100 emails, we have 40 of them are spam. We know that these are the 100 emails and 40 of them are spam and 60 of them are not spam. And this not spam is represented by zero and spam is represented by one. That is nothing but Y. Okay. Y is equal to 0 or 1. So how this would look like is, let's say we have a table here and out of this table, we'll have say X_test, which is nothing but the 100 emails. Okay. So we'll have from 0, 1, 2, dot dot dot, and this should end up till 100. And now at the same time, we'll also have Y, which is nothing but
A class, and these emails over here can belong to either 0, 1, 0, 1, or anything. But it should be either 0 or 1. And here it's going to be 0 or 1. It's just that we're taking an assumption. So now, what we're going to do is we'll be calculating our prior, prior probability. So if this is our data set, our prior probability is going to be nothing but P of Y is equal to 1. So, what this means is count all the spam emails. Okay, count all spam emails but total number of emails. Okay. So, let's now see what would be the probability for this. So, what is the total number of emails? It's 100, right? So, it's going to be 100 over here. And what are the total number of spam emails? It's 40. So, let's quickly write 40 over here.
Similarly, we're going to do this for P of Y is equal to 0. Okay. Here is going to be total number of emails. And here we're going to write all the number of non-spam emails. So, what would this give us? This would be 100, which is the total number of emails, and then we'll have 60. So, this is how this particular part would look like.
In order to give this in a mathematical form, because, you know, we obviously will be putting this in the form of a formula, right? So, in mathematical form. So, this is nothing but an average that is 1 by M summation of all the ones for Y is equal to 1 or 0. And here this I will range from 0 to N. So, basically we're trying to add 1, 1, 1 over here. Okay. So, this is how we calculate our prior probability. And as I've mentioned earlier, we don't have to calculate our marginal likelihood.
And finally, we are coming down to important stuff, that is the likelihood. Okay. So, this is the part, this is the likelihood, which is the most important part and the toughest part to calculate. Although it's pretty simple, you have to understand the math behind it. So, in order to calculate our likelihood, what we're going to do is we're going to calculate the probability, right? So, let's see how we can do that. So, we have this P of X when Y is equal to 1. Okay. What this means is when we have this email, right? So, we already know that email belongs to spam. We already know that email belongs to non-spam. So, what is the probability of that email belonging to this particular group? This is nothing but probability that email belongs to class 1. So, probability that email belongs to class 0. Fine.
So, now how X would look like? So, just to give you a brief before we move ahead. X over here would be nothing but a email. So, it will have multiple words and somewhere over here in the middle, it will be like "get unlimited 50% off" and so on and many other words. These are called the features, and based on these features, we calculate whether this email belongs to a spam class or non-spam class. So, how this would work? How this probability over here works? It'll take each of these features. Let's take something like "ultimate". Okay. So, it's going to be like probability of "ultimate" belonging to spam. This would give me some value, say 0.9%. Because it's high probability, right? That an "ultimate" word comes in a spam email. And then we'll also calculate at the same time probability that "ultimate" belongs to non-spam. So, this is going to be less probability. You obviously are not going to use "ultimate" in your day-to-day activities, right? Or day-to-day conversation. So, this is how it's going to be.
So, let's now quickly see how we can calculate for this. So, now we have X, right? So, if X, this is a capital X, is nothing but a list of words, okay? And this is nothing but an email, okay? And then small X represents the words which are there. So, here it can be X1, X2, X3, X4, and this would end up to XN. So, these are nothing but features or words. Okay. And X is the entire email. Then what we're going to find over here is probability that X, nothing but the capital X, belonging to Y is equal to 0 is equal to probability of all of these individual words over here. So, probability of all of these words belonging to a spam. And similarly, we can do this for spam. So, when we have an entire email, what is the probability that all of the words or the content of that email belonging to spam? So, here we'll have X1, X2, X3, X4, so on, Y is equal to 1. So, this is how it works.
Let's see the expanded version of this. Fine. Let me copy this entire equation here. Okay. And let's paste it on a new sheet. And let's see how we can calculate each of these. Right? So, what's going to happen now? Probability P of X that Y is equal to 1 is equal to I'm just expanding this part over here. Okay. Let me just erase this to give you a better insight. All I'm trying to do is I'm trying to expand this part. P of X1, X2, X3, right? So, this is nothing but probability. You see this comma here represents "and". Okay, it's an "and" operator. So, probability of X1 belonging to Y=0 multiplied by probability of X2 when Y=0, that X1 is also not spam. So, this is how it works. This represents "and". And then we'll perform multiplication. And then we'll do something like this again. So, for P3, probability that X3, that is nothing but the word, this X3 belongs to non-spam category when we already know that X1 and X2 also belong to non-spam category. What I'm trying to say over here is each of these words are dependent upon each other only if X2 is considered as not spam, only if X1 is not spam. You know, all of these words are dependent on each other and the probability of them is holds true only if the other one holds true. So, what's the issue with this is by the time it reaches this XN, right? It becomes a pretty huge value and it becomes computationally very expensive.
In order to overcome this, we use something called as Naive Bayes assumption. And what this Naive Bayes assumption says that when we calculate this probability, right? Here we have calculated probability of X1 when Y is equal to 0. And then we have also calculated the probability for the second word. According to Naive Bayes assumption, this word is totally independent of the first word. So, the first word can have a higher probability of being a spam, and the second word can have a higher probability of being not spam, but they are totally independent of each other. So, this is what is Naive assumption is.
So, let's now see how this equation would look like after Naive assumption. So, what we're going to do is I'll write one below the other so that we get a better understanding. So, if Y is equal to 1. If an email, we consider that to be a spam email, only if P of the first letter or the first word of that email is spam is equal to 1. This will give us some probability. And then I'll multiply that with probability of the next word in that email being a spam. So, and then this won't be dependent upon the second value here. And then I'll multiply this again by the third word, P of X of 3, that is nothing but the third word is equal to spam, because over here we're trying to find the probability that email belongs to spam category. And this would continue for the Nth term, P of XN when Y is equal to 1. So, you see here, right? That none of the probabilities are dependent on each other, thus reducing the computation need. Okay.
So, in order to put this in a mathematical form, what will happen over here is P of X, that or we can say P of email belonging to a spam category is equal to the product. Okay. So, this is multiplication, right? So, for summation we use this, and for product we use pi. Okay. So, this is for product, and where I will range from 1 to N. And then we'll have probability of X of I when we know that particular character or word belongs to a spam category. And same way, we'll have for non-spam Y is equal to 0. This is nothing but pi where I ranges from 1 to N, probability of X I when we know Y is not a spam. This is an equation for our likelihood.
So, now as you can see, right? We have found the value for likelihood. We have found the value for posterior probability. We don't have to calculate marginal likelihood. So, we can uh just skip that part. And now finally, we are coming down to posterior probability. So, let's now see how we can substitute our values and calculate our posterior probability. So, posterior probability, that's nothing but P of, we feed an email, right? So, P of X. Let's write a generalized version where Y is equal to C. C here refers to the class, and class here is nothing but either spam or not spam. This is equal to on top, we'll have the likelihood, right? So, let's write the likelihood first. So, here we'll have something, pi I = 1 to N, probability of X I, that is going through each and every word in an email, Y=C, can be either 1 or 0. We will multiply this by prior probability, right? Which is nothing but 1 by M, I = 1 to N, here it's going to be Y is equal to C. And obviously, we have to put the normalization below, but we can skip it because it doesn't make any difference, right? So, this is the equation for our Naive algorithm. This is the way how we can classify our text.
Let us now go to my code editor and try to code the entire algorithm. This would help us in understanding how the underlying working of NLTK works. Okay. So, as you can see, I have come here to my Google Colab. Let me quickly give a name as NLTK or let's give classification implementation. So, let's get started. Now, there are a couple of things that we have to import. So, let it be import pandas as pd. Then let's import numpy as np. And then let's have uh label encoder. So, basically we use label encoder to convert our text into numerical form. And if you are asking why we are going to convert it into numerical form, that's because computer, no matter how advanced it is, it is unable to understand textual form. So, it has to take the data in a numerical format. Okay. From sklearn.preprocessing import LabelEncoder. And finally, we need sklearn.model_selection import train_test_split. All right. So, let us now execute this.
So, in our next stage, right? We will have to get our data set. So, getting data set. So, here we'll be using mushroom data set. So, what we're going to do is df. We know it's going to be in data frame. So, pandas.read_csv. And the place where I have my file over here is here. And I'll just quickly copy the path and paste it over here. So, let me execute this code. Fine. And now let's see the shape of our data set. So, it's going to be df. So, what this represents over here is that we have 8,142 rows and then we have 23 features. So, let's now see each of these. So, let me just print this df.head. Let me slightly zoom it out and let me play this. So, as you can see here, we have all our data in a textual format and then we'll have the values which would range from 0 to 8,124. And then the features over here are nothing but, you know, cap shape, cap color, cap surface, there's nothing but the surface of the mushroom. And the class, you can see here, right? So, this part over here, this class is nothing but Y, and all of these features over here are represented as X.
So, now what we'll do is let's try to, you know, encode this. Let's try to convert these values into numbers. So, what we'll do is le e and we're going to use LabelEncoder. Okay. So, we are creating the instance of this. And then we'll have df DF_encoded, that's df stands for data frame. This is nothing but df.apply. Apply is one of the methods. What this apply method does is it's like a for loop over here. So, it'll go to each and every row and each and every column and apply whatever function we pass. And we're going to pass LabelEncoder, right? So, over here I'm going to pass here le.fit_transform. We'll just have to give the method name and no method call. And then we'll give axis. By default, axis is 0. We want 0 because we want it to go for every row. So, let's now execute this and let's see how our data would look like now. So, as you can see here, this is our data. Or let me just give it as head and let me zoom out so that we can compare this data with this. So, these both are the same thing. Okay. As you can see here, class, class, and then we have cap shape and all of these are the same thing. Only thing after performing label encoding, all of these values have been converted into numbers.
So, now what we'll do is we obviously need to convert this into a list of array, right? So, let's finally do that. So, let's df is equal to encoded df. So, let's see what it is. Yeah, df_encoded.values. And now we'll have to define our X. X is nothing but df. So, as you can see, right? This is Y. This class refers to Y. And X is all of these features except the first column. We want all the other columns, right? So, what we'll do is from column 1, because this is column 0. We want it to all the columns. So, basically here we want all our rows, that is 0 to 8,000 some change, and then except the first column, the zeroth column, we want all of them. Okay. So, this is X. And for Y, it's going to be all the rows. I hope you understand why it's all the rows, right? So, 0, 1, 2, 3, and we need it for all the values. So, it's all the rows and only the zeroth column, because zeroth column gives us the class, right? So, let's quickly execute this and just for your satisfaction, let's see how it would look like. So, here I would press X. So, you'll see here that, yeah, it's going to be capital X, right? Uppercase. So, everything has been converted in an array and except the first part, except this part, we'll have all other values. And similarly, let's see it for Y. So, as you can see here, we have just a single column.
So, now what we'll do is we'll split our data. So, this is going to be, we are going to use train_test_split. So, here we're going to have the value of X and then Y. And then we have to give by what percent we want our value to be splitted, right? So, for that we'll give something like this. Okay. Yeah. So, as you can see here, we need our test size, right? So, we need a test size and a random state. So, let me just copy this from here. Now, what this test size resembles is by what percent we want our data to be splitted. So, let's give it as 20%. And let me now execute this.
So, now that we have all the data and every requirement. So, let us now directly jump into having our Naive Bayes classifier. So, let me give you a quick recap here. In order to get a Naive Bayes classifier, we need to find posterior probability. In order to find the posterior probability, we need to find something called as likelihood and prior probability. And then we're going to have prior probability. Let's now calculate each of this. And we'll start this by calculating prior probability. Okay? Because prior probability is pretty simple to calculate. Fine. So, let's now start off with prior probability. So, we'll create a function here, def prior_probability. So, this is nothing but you're going to pass y_train and then we're going to pass labels. Okay. Labels over here refers to X and 0 values. Instead of giving y_train, let's generalize it to give y. And now what we're going to do is we need to find the size of y, right? So, because prior probability is nothing but sum of either X or 0 all divided by the total number of classes. Okay. So, what I'm trying to say here is if you have 100 emails, out of which 40 are spam and 60 are not spam, then probability of email being spam is 40 by 100, and the other one is going to be 60 by 100. Okay. So, for that we need to find the total size, right? So, it's going to be y.shape and then this will give us an array, right? So, we need the first value. Apart from that, we need a sum, right? So, s is equal to np.sum. y_train or let it be y_values is equal to label. I hope you understand why we are doing this. You see, y_value has only class values, right? Only if the class value and label value are same, you're going to get one. So, this is how we are going to do here. And this is going to return as prior probability. So, return m by s. Fine.
So, now let's find our likelihood. That's our next task. And in order to find our likelihood, we need to have conditional probability. Okay. So, def conditional_probability. This is going to take parameters like X and then Y. So, we give here as X_train and not X. And then we'll have y_train. And then we obviously need to find feature_column. And then we need to have feature_label and then finally label. Okay. So, what this feature_column says is it represents which column we want that particular feature. So, as we all know that this is nothing but a tabular data, right? So, let me just quickly show you how it would look like. Okay. So, over here we have a table and this table will have multiple rows and columns, right? So, what this feature_column represents is within this feature column, within this column, which value represents the values present in the feature columns. Okay. So, let me just quickly now erase this and move ahead. So, what this will do is we're going to just filter out the value, right? So, x_filter this is equal to x_train where y_train is equal equal to label. So, over here we'll just get the email values which is either spam or not spam. That's why I'm giving here as x_filtered and number is equal to np.sum. It's going to be x_filtered where we have all the rows and the columns is going to be feature_column which is going to be equal to feature_values. And now for denominator. So, it's going to be x_filtered.shape, which is nothing but total number of values, right? Zero. And this will return as numerator by denominator. And let's give it as float over here. And it's going to be numerator divided by denominator. Okay. This is nothing but the conditional probability. Okay. I hope you remember what is conditional probability when we were discussing the derivation.
So, now what we're going to do is we obviously have to predict our class, right? So, let's do that. Let's write this function here. Predict. def predict. This predict takes two values, X_train, y_train. Apart from that, we also need to perform predictions, right? So, that's why we also need X_test. So, now what we're going to do is we'll have classes. Classes refers to be spam or not spam. Over here, it's going to be poisonous or not poisonous, because here we are taking mushroom data set, right? So, classes is equal to np.unique(y_train). Okay. What this predict will return is y_test values. Whatever value I feed to this, I'm going to get the answer for that, okay? So, now we also need features. So, n_features. This is going to be equal to x_train.shape, all right? So, let's now calculate posterior probability. So, posterior probability, let's leave it empty as of now. Okay. So, for every value of a posterior probability, we'll get some percentage. So, this percentage represents, you know, probability of that particular word being a part of spam or not spam. So, over here we're going to have for label in classes. This means that we're going to go class by class. So, either it's spam or non-spam, or over here, it's going to be either it's going to be poisonous or non-poisonous. So, the values of classes can be either 0 or 1. Fine. And then we are going to give likelihood. We'll give it as 1.0. And then for features in range n_features. Okay. N_features represents the column over here, right? So, these are the n features except the classes, whatever is that, they are nothing but n features, right? So, yeah, so go through each and every feature, or in short, I can say go through each and every column. And then find the conditional probability. CND is equal to conditional_probability. Here we're going to call this function. So, now we have to pass our X_train values and then we have y_train, then the features, feature_column, and within the features, which feature you want. Okay. And then we'll have label. Okay. This label over here represents whether the class is either X or 0. So, now what we're going to do is we're going to calculate our likelihood. So, likelihood is equal to likelihood plus conditional_probability * conditional_probability. So, this likelihood over here, we're just randomly initializing it over here. And for every iteration of this for loop, likelihood increases. So, now all we need to do is we need to append prior probabilities over here. So, we'll give here as prior is equal to prior_probability. Okay. This is nothing but this function over here, which we have defined, where we're going to give y_train and then label. And then for posterior probability, this is nothing but likelihood times prior. Okay. I hope you remember the mathematical equation for this particular part. So, we are trying to find prior and posterior probability for each of this. And now what we're going to do is we'll just append these values over here. So, we'll have posterior_prob.append. And the value that I want to append is post. And now what we'll do is we need to find the probability which has the max one, right? So, for that we're going to use argmax over here. So, predicted_value is equal to np.argmax. argmax gives us in which place we have the highest value, right? So, that's what argmax does. And so we'll give here posterior_probability, that is nothing but this part. And this would return as the class, right? So, return predicted. Okay. So, this is done. All right.
So, let us now do one thing. Let us now find by what accuracy we are finding this value. So, let's also calculate the accuracy that we'll have correct values, right? So, to do that, def accuracy(X_train, X_test, Y_train, Y_test). Right? So, Y_train and it's going to be Y_test. And within this function, what you're going to do is predicted is going to be an empty list. So, for i in range, so it's going to be through all the values of X, right? So, X_test.shape. Okay. So, now here it's going to be P is equal to predicted_value. So, now we'll call this function here, predict(X_train, Y_train, X_test). And as we are doing this for all the values, we'll just pass here I. Every time I perform this predict, it gives me whether one example belongs to a mushroom class, which is poisonous or not. So, only reason why I'm doing this accuracy or I can say, only reason I'm having this for loop here is to find all the values and put them over here. This predict list over here contains the predicted value of each of the test values. Okay. So, now what I'm going to do is whatever value I get over here. So, pred.append(p). Let's do one thing. Let's give this name as y_pred. And we'll convert this predicted over here to numpy array. So, np.array. And we'll give here y_pred. And now in order to calculate our percentage or in order to calculate the accuracy, all we need to do is accuracy is equal to np.sum. And we'll just compare this predicted value, okay, whatever values because we get only ones and zeros with the value that we already know. Okay. This is just the way to have, you know, testing your data, how accurate it is. So, we'll have y_pred == y_test. And every time these two values are same, right? It will add up one. And in order to get a value in percentage, we'll have y_pred divided by the size, right? So, y_pred.shape and we'll give index is 0. And this will return as accuracy. Fine. So, let's call our accuracy here and let's give all these values. So, before I run this code, right? Let me just give you a quick recap. Our main agenda over here was to classify whether these mushrooms over here belongs to class of poison or not. So, that's what we are doing here. We have to do that obviously using posterior probability. In order to find posterior probability, we need two things. One is nothing but likelihood, and another one is prior probability. Finding prior probability is pretty simple because we just need to find the total number of values by total number of other values, right? That's what prior probability does. And for likelihood, it's pretty simple. Okay? We have to find the conditional probability. And then this should return us the predicted values. And let me now quickly run this and let's see what is the accuracy of our model. Okay, so it's saying X_test is not defined. And the reason why we are getting that is because we have given a smaller case value over here. So, let me quickly rerun this now. Oh, yeah, we have to give X as uppercase. Okay. So, in order to overcome this, what we'll do is we'll quickly run this from the start here. Okay. So, let's wait for our data to get processed over here. Sometimes what happens is when you're when you're trying to execute multiple lines over here, right? You know, one block might get executed before and then other later. So, let's see if this works. This time I have restarted the runtime in order to run it from the beginning. So, let's, I hope it should work now. So, now we don't have any errors. So, let us now quickly see what is the accuracy that we got. So, let's now print this. Okay. So, we have got 0.99, right? So, let me quickly give this over here. Accuracy times 100. So, now when we perform this classification task here, we are getting 99.63 accuracy. So, this is how we can perform classification task using Naive Bayes algorithm.
All right. Now, so now that we know how NLP works. Now that we know what is Naive Bayes classification and how Naive Bayes classification works, and we also know how to pre-process our data. Let's do one thing. Let's take certain amount of sentences. Let it be a small sentence and let's see if we can perform any kind of sentiment analysis on them. And over here, we'll be using a library called as scikit-learn. With scikit-learn, we don't have to write all the number of lines that we wrote now. So, let me now quickly move to my code editor and show you how I can implement that. Okay. So, now let's change the name here. Let's give it as sentiment analysis. Okay. So, what we'll do is we'll have a text over here and we won't have a huge amount of data because if you have huge amount of data, then it would be pretty hard to understand. So, let me get this textual data for you. Okay. So, over here in my notepad, I have some small amount of data and over here, as you can see, so we have X_test, sorry, this is going to be X_train and then we have Y_train. So, let's now analyze our data over here. So, if you can see, right? What is happening here is we have a data set and we're supposed to train our model based on this. And we also have classes over here. This class over here represents that whether this first sentence is a positive or negative sentence. So, if it's a positive, it is 1, and if it's a negative class, it's going to be 0. So, on and so forth, we have it for all the movie rating predictions here. Okay. And now, once we are done training our model, let's, we'll test it by passing this values. Okay. So, over here we have three sentences. "I was happy and happy and I loved acting in the movie" and then "The movie I saw was bad". And we can add some more examples. So, what we'll do now is let me copy this part over here, text_test, and let me paste it for our data set. Okay. And let me quickly execute this. So, let's now see the shape of our X. Let's try making this an uppercase. And the reason why I'm using uppercase here specifically and lowercase and Y, this is because this is a standard in the data science community. All right. So, let's see the shape of our training data set. All right. And then we'll have extra. Okay. So, as you can see, we have a data set here. And let's have shape. Yeah. We won't get the shape here because this is not a numpy array, right? So, this is extra. Fine.
So, now what we'll do is we have to clean our data, right? So, let's do the data cleaning part wherein we'll be doing all kind of stuff like tokenization, stemming, and stopword removal. Okay. So, let's give a heading here as Data Cleaning. And now, rather than writing this as an individual function, what I'll do is I'll write it as the entire method. Okay. So, let's import our values first. From NLTK.tokenize import RegexpTokenizer. And then we have from NLTK.stem import PorterStemmer. And then finally, we have to have a stopword remove, right? So, from NLTK.corpus import stopwords. So, let me now download our stopwords. So, import NLTK. And then we are going to have NLTK.download('stopwords'). All right. So, now, uh, let's now create an object of our tokenizer, PorterStemmer, and stopwords. This is going to be RegexpTokenizer. And then I'm going to pass what pattern I want, right? So, I want only the words. And then I also want to concatenate those words. And then we have stopwords. So, which language I'm using? I'm obviously using English. So, stopwords. This is going to be set(stopwords.words('english')). And now we have PorterStemmer. That's ps is equal to PorterStemmer. All I've done over here is just creating the object of our classes over here. So, let me quickly execute this. Fine.
And now we'll create a function or a method, def clean_data. And then we're going to pass here as text. Fine. And what we'll do is we'll convert our text into lowercase. And now we'll perform tokenization. So, this is nothing but tokens. This will be is equal to tokenizer. We are getting this tokenizer from here. dot tokenize. So, we want the text to be tokenized. And then new_tokens = [token for token in tokens if token not in stopwords]. What I'm trying to do here is I'm just combining stopword remover and tokenizer, right? So, that's why I'm using another for the list over here. So, this part over here, for token, okay, for token in tokens, right? Okay, this part over here gets me the list of tokens. And then what I'm going to do is I'm going to compare this tokens with stopword list. And if that word isn't present, then I print it. Basically, I'm performing tokenization as well as stopword removal at the same time. And now we'll perform stemming. Same way like before, I'm going to give uh list comprehension. So, ps is the name that we have given. PorterStemmer.stem. Obviously, it's going to be words, right? So, for token in new_tokens. What I'm going to do is I want them to be stemmed token. Okay. And now what's going to happen is here I'll have sentence or like it'll be like clean_text. This is going to be " ".join(stem_tokens). And what this method will return is the clean text. So, let me quickly execute this now. All right. So, this is done from our end now. All right.
So, now what I'm going to do is I'm going to use this get_clean_text to clean our text data and train data, right? So, what I'm going to give here is X_clean. Okay. X_clean is going to be get_clean_text. Okay. And I'm going to pass X_train. Similarly, I'm going to do it for X_test. So, for that I'm going to do xt_clean. And this is going to be get_clean_data. And it's going to be Y, sorry, X_test. All right. So, let me now execute this. Okay. So, we are getting an error over here saying that, you know, this word over here has no attribute. That's very true. The reason why we are getting this is because I'm just passing the value, right? So, we don't want that. So, what I'm going to do is I'm going to put this in a form of a list here. And rather than passing the entire X_train, I'll just give one word and pass for i in X_train. Sorry. And now similarly, I'm going to do here for i in X_test. So, yeah, we haven't defined our X_test yet. So, let's quickly define our X_test. All right. So, let me get this coding part over here and let me get my X_test. So, I'll copy the X_test from here and let me paste it here. Fine. So, let us now execute this. Okay. The reason why we are getting an error over here is because over here I have defined as new_tokens and here it is new_token, right? So, let me quickly fix that and run this once again. It says X_test is not defined. Yep. So, we will fix this and rerun this again. Most of the times when you're trying to do this program, right, you'll encounter a lot of issues. And only when you encounter these issues, you're going to learn a lot. So, now we have our clean text. So, let's just compare it and see how it looks. Okay. So, as you can see here, our text has been reduced. And yeah, so in order to get this better, what I can do is I can just pass some space here and then run this here. Let me do one thing. Let me rerun this from the start here and restart the timer. And let me run it all. Okay. So, as you can see here, now we're getting some spaces. All right. So, the reason why we weren't getting any space is because I had not added any space. So, now let's perform our classification task. Let's, we'll be using NLTK, right? Before that, we have to vectorize our text. As I've mentioned, in order for us to perform classification, we need to vectorize it. So, vectorize. So, now what we'll do is from sklearn.feature_extraction.text import CountVectorizer. All right. And now we'll create the instance of a CountVectorizer. CV is equal to CountVectorizer. And we'll give our ngram_range. It's going to be 1 and 2. And now to vectorize our input. So, X_vectorized is equal to CV.fit_transform. And now we're going to pass this value, right? So, we'll pass X_clean and then we want to convert this into an array. So, let us now execute this and let's see how this X_vector looks like. Okay. So, basically for every word, you know, we are getting this vectorizer over here. All right. So, now what we're going to do is we are going to perform our classification task. But before that, let's get our feature names. Okay. So, print CV.get_feature_names(). Okay. So, what this get_feature_names does is now we don't understand these values, right? So, you don't know what 002 here represents. So, in order to know what values over here represents, all we need to do is get_feature_names. And as you can see here, this first over here represents "act". And it's the same for all the five arrays over here. So, what basically this CountVectorizer tells is that this word "act", how many times has it repeated in this sentence, how many times it was present in this sentence, this is what vectorizer does. And this kind of model is usually referred to as bag of word model. Similarly, we'll perform vectorization for our test value. So, X_test_vectorized = CV.transform(X_test_clean). And now we have the clean text. And now we also want to convert into an array. All right. So, now let's execute this part here. Okay. And finally, we'll perform our classification task. And we're going to use Multinomial Naive Bayes here. Okay. If you don't know, there are multiple versions of Naive Bayes that are available. In order to perform text classification, we use Multinomial Naive Bayes. Okay. So, Multinomial. All right. So, let me import that from sklearn.naive_bayes import MultinomialNB. And now we'll create the instance of that. So, MNB is equal to MultinomialNB. So, we'll fit our model. So, it'll be MNB.fit. And we'll give values. Okay. So, it's going to be X_vector. X_vector is nothing but, you know, our vectorized form, which is nothing but this X_vector. And then we're going to give Y values. All right. And let me execute this. So, it says Y isn't defined. So, let's go back here and see where we're going wrong. Oh, yeah, it's not Y, it's going to be Y_train, right? So, let's copy this and let me paste it over here and let me execute this program. So, now we have a Multinomial Naive Bayes. And let us now perform prediction. So, MNB will return this value, right? So, it like Y_pred. This is going to be MNB. Or MN, MultinomialNB.predict. predict. And over here, we're going to pass the test value, right? So, X_test_vectorized. Fine. So, let's now see what what will be the output for this. But before that, I would like you to guess what can be the output. Okay. See, we have performed classification task. And now by doing this predict the value of X_test, all we are trying to do is predict whether that sentence belongs to class A or class B. And class A here refers to spam, and class B here refers to not spam. And in vectorized form, it's going to be either 1 or 0. So, let's now see Y_predicted. It'll give us an array. So, over here it gives 1 and 0. So, let's see what it means. Okay. Here 1 refers to positive value, and 0 refers to negative class. And what was our X_test value? So, we have defined our X_test somewhere over here. So, here "I was happy and happy and I loved acting in the movie". Y refers to positive, right? So, "happy" is a positive word, which we know. Let's see what did our machine identify it as. So, the first word over here or the first sentence refers to this, and it says 1, which means happy. Okay. Instead of doing this, what I'll do is in our test data, we'll just give one value. Okay. This is just so that you better understand this. So, we know that this is a very bad word, right? And when I say bad word, I don't mean, you know, I mean these words, these are the bad words or the words that give us negative feature, right? So, let me execute this. What I'm expecting in the output is 0. So, as you can see here, our predicted value is 0. So, with this, we can say that our classification is working and we can also do this using a pretty huge data set.
So, let's understand what is Natural Language Processing. So, NLP refers to the artificial intelligence method of communicating with an intelligence system using natural language. By utilizing NLP and its components, one can organize the massive chunks of textual data, perform numerous automated tasks, and solve a wide range of problems such as automatic summarization, machine translation, named entity recognition, speech recognition, and topic segmentation.
So, let's understand the basic structure of an NLP application. Considering the chatbot here as an example, we can see first we have the NLP layer, which is connected to the knowledge base and the data storage. Now, the knowledge base is where we have the source content, that is, we have all the chat logs which contain a large history of all the chats which are used to train the particular algorithm. And again, we have the data storage where we have the interaction history and the analytics of that interaction, which in turn helps the NLP layer to generate the meaningful output.
So, now if we have a look at the various applications of NLP. First of all, we have sentiment analysis. Now, this is a field where NLP is used heavily. We have speech recognition. Now, here we are also talking about the voice assistants like Google Assistant, Cortana, and Siri. Now, next, we have the implementation of chatbots, as I discussed earlier, just now. Now, you might have used the customer care chat services of any app. It also uses NLP to process the data entered and provide the response based on the input. Now, machine translation is also another use case of natural language processing. Now, considering the most common example here would be the Google Translate. It uses NLP and translates the data from one language to another, and that too in real time. Now, other applications of NLP includes spellchecking. Then we have the keyword search, which is also a big field where NLP is used. Extracting information from any particular website or any particular document is also a use case of NLP. And one of the coolest applications of NLP is advertisement matching. Now, here what we mean is basically recommendation of the ads based on your history.
Now, NLP is divided into two major components, that is, the Natural Language Understanding, which is also known as NLU, and we have the Natural Language Generation, which is also known as NLG. The understanding involves tasks like mapping the given input into natural language into useful representations, analyzing different aspects of the language. Whereas Natural Language Generation, it is the process of producing the meaningful phrases and sentences in the form of natural language. It involves text planning, sentence planning, and text realization. Now, NLU is usually considered harder than NLG.
Now, you might be thinking that even a small child can understand a language. So, let's see what are the difficulties a machine faces while understanding any particular language. Now, understanding a new language is very hard. Taking our English into consideration, there are a lot of ambiguity and that too in different levels. We have lexical ambiguity, syntactical ambiguity, and referential ambiguity.
So, lexical ambiguity is the presence of two or more possible meanings within a single word. It is also sometimes referred to as semantic ambiguity. For example, let's consider these sentences and let's focus on the italicized words. "She is looking for a match." So, what do you infer by the word "match"? Is it that she is looking for a partner, or is it that she's looking for a match, be it a cricket match or a rugby match? Now, the second sentence here, we have "The fisherman went to the bank." Is it the bank where we go to collect our checks and money, or is it the river bank we are talking about here? Sometimes it is obvious that we are talking about the river bank, but it might be true that he's actually going to a bank to withdraw some money. You never know.
Now, coming to the second type of ambiguity, which is the syntactical ambiguity. In English grammar, this syntactical ambiguity is the presence of two or more possible meanings within a single sentence or a sequence of words. It is also called as structural ambiguity or grammatical ambiguity. Taking these sentences into consideration, we can clearly see what are the ambiguities faced. "The chicken is ready to eat." So, here what do you infer? Is the chicken ready to eat its food, or is the chicken ready for us to eat? Similarly, we have the sentence like "Visiting relatives can be boring." Are the relatives boring, or when we are visiting the relative, it is very boring? You never know.
Coming to the final ambiguity, which is the referential ambiguity. Now, this ambiguity arises when we are referring to something using pronouns. "The boy told his father the theft he was very upset." Who is "he"? Is it the boy? Is it the father, or is it the thief?
So, coming back to NLP, firstly, we need to install the NLTK library, that is
The Natural Language Toolkit. It is the leading platform for building Python programs to work with human language data, and it also provides easy-to-use interfaces to work with 15 corpora and lexical resources. We can use it to perform functions like classification, tokenization, stemming, tagging, and much more.
Now, once you install the NLTK library, you will see an NLTK downloader. It is a pop-up window which will come up, and in that, you have to select the "all" option and press the download button. It will download all the required files, the corpora, the models, and all the different packages which are available in the NLTK.
Now, when we process text, there are a few terminologies that we need to understand. Now, the first one is tokenization. So, tokenization is a process of breaking strings into tokens, which in turn are small structures or units that can be used for tokenization. Now, tokenization involves three steps, which is breaking a complex sentence into words, understanding the importance of each word with respect to the sentence, and finally, producing a structural description on an input sentence. So, if we have a look at the example here, considering this sentence: "Tokenization is the first step in NLP." Now, when we divide it into tokens, as you can see here, we have 1, 2, 3, 4, 5, 6, and 7 tokens here.
Now, NLTK also allows you to tokenize phrases containing more than one word. So, let's go ahead and see how we can implement tokenization using NLTK. So, here I'm using Jupyter Notebook to execute all my practicals and demos. Now, you are free to use any sort of IDE which is supported by Python. It's your choice. So, let me create a new notebook here. Let me rename it as "Text Mining and NLP." So, first of all, let us import all the necessary libraries. Here we are importing the OS, NLTK, and the NLTK corpora.
So, as you can see here, we have various files which represent different types of words, different types of functions. We have samples of Twitter, we have different sentimental WordNet, we have product reviews, we have movie reviews, we have non-breaking prefixes, and many more files here. Now, let's have a look at the Gutenberg file here and see what are all the fields which are present in the Gutenberg file. So, as you can see here, inside this, we have all the different types of text files. We have Austin's Emma, we have Shakespeare's Hamlet, we have Mobex, we have Carol's Alice, and many more. Now, this is just one file we are talking about, and NLTK provides a lot of files.
So, let's consider a document of type string and understand the significance of its tokens. So, if you have a look at the elements of Hamlet, you can see it starts from "The Tragedy of Hamlet by William Shakespeare." So, if you have a look at the first 500 elements of this particular text file. So, as I was saying, "The Tragedy of Hamlet by William Shakespeare, 1599, Actor's Premise." We can use a lot of these files for analysis and text for understanding and analysis purposes, and this is where NLTK comes into picture, and it helps a lot of programmers to learn about the different features and the different applications of language processing.
So, here I have created a paragraph on artificial intelligence. So, let me just execute it. Now, this AI is of the string type, so it will be easier for us to tokenize it. Nonetheless, any of the files can be used to tokenize. For simplicity, here I'm taking a string file. The next, what we are going to do is import the word_tokenize under the NLTK.tokenize library. Now, this will help us to tokenize all the words. Now, we will run the word_tokenize function over the paragraph and assign it a name. So, here I'm considering AI_tokens and I'm using the word_tokenize function on it. Let's see what's the output of this AI_tokens. So, as you can see here, it has divided all the input which was provided here into the tokens.
Now, let's have a look at the number of tokens here we have here. So, in total, we have 273 tokens. Now, these tokens are a list of words and the special characters which are separated items of the list. Now, in order to find the frequency of the distinct elements here in the given AI paragraph, we are going to import the FreqDist function which falls under NLTK.probability. So, let's create an F_dist in which we have the function here, FreqDist, and basically, what we are doing here is finding the word count of all the words in the paragraph. So, as you can see here, we have comma 30 times, we have full stop 9 times, and we have "accomplished" one time, "one" and so on. We have "computer" five times. Now, here we are also converting the tokens into lowercase so as to avoid the probability of considering a word with uppercase and lowercase as different.
Now, suppose we were to select the top 10 tokens with the highest frequency. So, here you can see that we have comma 30 times, "the" 13 times, "of" 12 times, and "and" 12 times. Whereas the meaningful words which are "intelligence" which is six times and "intelligent" six times.
Now, there is another type of tokenizer which is the blank tokenizer. Now, let's use the blank tokenizer over the same string to tokenize the paragraph with respect to the blank string. Now, the output here is 9. Now, this 9 indicates how many paragraphs we have and what all paragraphs are separated by a new line. Although it might seem like one paragraph, it is not. The original structure of the data remains intact.
Now, another important key term in tokenizations are bigrams, trigrams, and n-grams. Now, what does this mean? Now, bigrams refers to tokens of two consecutive words, known as a bigram. Similarly, tokens of three consecutive written words are known as trigrams. And similarly, we have n-grams for the n consecutive written words. So, let's go ahead and execute some demos based on bigrams, trigrams, and n-grams. So, first of all, what we need to do is import bigrams, trigrams, and ngrams from NLTK.util. Now, let's take a string here on which we'll use these functions. So, taking this string into consideration: "The best and the most beautiful thing in the world cannot be seen or even touched. They must be felt with the heart." So, first, what we are going to do is split the above sentence or the string into tokens. So, for that, we are going to use the word_tokenize. So, as you can see here, we have the tokens.
Now, let us now create the bigrams of the list containing tokens. So, for that, we are going to use the nltk.bigrams and pass all the tokens, and since it is a list, we are going to use the list function. So, as you can see under output, we have ('the', 'best'), ('best', 'and'), ('and', 'the'), ('the', 'most'), ('most', 'beautiful'), ('beautiful', 'thing'), ('thing', 'in'), ('in', 'the'), ('the', 'world'). So, as you can see, the tokens are in the form of two words; it's in a pair form. Similarly, if we want to do the trigrams and find out the trigrams, what we need to do is just remove the bigrams and use the trigrams. So, as you can see, we have tokens in the form of three words. And if you want to use the n-grams, let me show you how it's done. So, for n-grams, what we need to do is define a particular number here. So, instead of n, I'm going to use, let's say, 4. So, as you can see, we have the output in the form of four tokens.
Now, once we have the tokens, we need to make some changes to the tokens. So, for that, we have stemming. Now, stemming usually refers to normalizing words into its base form or the root form. So, if we have a look at the words here: "affectation, effects, affections, affected, affection, and affecting." So, as you might have guessed, the root word here is "affect." So, one thing to keep in mind here is that the result may not be the root word always. A stemming algorithm works by cutting off the end or the beginning of the word, taking into account a list of common prefixes and suffixes that can be found in an inflected word. Now, this indiscriminate cutting can be successful in some occasions, but not always, and this is why we affirm that this approach presents some limitations.
So, let's go ahead and see how we can perform stemming on a particular given dataset. Now, there are quite a few types of stemmers. So, starting with the Porter Stemmer, we need to import it from nltk.stem. Let's get the output of the word "having" and see what is the stemming of this word. So, as you can see, we have "hav" as the output. Now, here we have defined words to stem, which are "give, giving, given, and gave." So, let's use the Porter Stemmer and see what is the output of this particular stemming. So, as you can see, it has given "give, give, give, and gave." Now, we can see that the stemmer removed only the "ing" and replaced it with an "e."
Now, let's try to do the same with another stemmer called the Lancaster Stemmer. You can see the stemmer stemmed all the words. As a result of it, you can conclude that the Lancaster Stemmer is more aggressive than the Porter Stemmer. Now, the use of each of these stemmers depends on the type of task that you want to perform. For example, if you want to check how many times the words "GIV" is used above, you can use the Lancaster Stemmer. And for other purposes, you have the Porter Stemmer as well. Now, there are a lot of stemmers. There is one Snowball Stemmer also present, where you need to specify the language which you are using and then use the Snowball Stemmer.
Now, as we discussed that a stemming algorithm works by cutting off the end or the beginning of the word. On the other hand, lemmatization takes into consideration the morphological analysis of the word. Now, in order to do so, it is necessary to have a detailed dictionary which the algorithm can look into to link the form back to its lemma. Now, lemmatization, what it does is groups together different inflected forms of a word, which are called lemmas. It is somehow similar to stemming as it maps several words into a common root. Now, one of the most important things here to consider is that the output of lemmatization is a proper word, unlike stemming, in that case, where we got the output as "GIV." Now, "GIV" is not any word; it's just a stem. Now, for example, if a lemmatizer should work on "go," "going," and "went," it all stems into "go" because that is the root of all the three words here.
So, let's go ahead and see how lemmatization works on the given input data. Now, for that, we are going to import the WordNetLemmatizer from NLTK. Now, we are also importing the WordNet here. As I mentioned earlier, that lemmatization requires a detailed dictionary because the output of it is a root word, which is a particular given word. It's not just any random word; it is a proper word. So, to find that proper word, it needs a dictionary. So, here we are providing the WordNet dictionary and we are using the WordNetLemmatizer. So, passing the word "corpora" into the WordNetLemmatizer. So, can you guys tell me what is the output of this one? I'll leave this up to you guys. I won't execute this sentence. Let me remove this sentence here. You guys tell me in the comments below what will be the output of the lemmatization of the word "corpora." And what will be the output of the stemming? You guys execute that and let me know in the comment section below.
Now, let's take these words into consideration: "give, giving, given, and gave," and see what is the output of the lemmatization. So, as you can see here, the lemmatizer has kept the words as it is, and this is because we haven't assigned any POS tags here, and hence it has assumed all the words as nouns. Now, you might be wondering, what are POS tags? Well, I'll tell you what are POS tags later in this video. So, for just now, let's keep it as simple as that is that POS tags usually tell us what exactly the given word is. Is it a noun? Is it a verb, or is it a different part of speech? Basically, POS stands for Parts of Speech.
Now, do you know that there are several words in the English language such as "I," "ate," "for," "above," "below," which are very useful in the formation of sentences, and without them, the sentence wouldn't make any sense. But these words do not provide any help in natural language processing, and this list of words are also known as stop words. NLTK has its own list of stop words, and you can use the same by importing it from the NLTK.corpus. So, the question arises, are they helpful or not? Yes, they are helpful in the creation of sentences, but they are not helpful in the processing of the language. So, let's check the list of stop words in the NLTK. So, from NLTK.corpus, we are importing the stop words, and if we specify what all stop words are there in the English language. Let's see. So, as you can see here, we have the list of all the stop words which are defined in the English language, and we have 179 total number of stop words.
Now, as you can see here, we have these words which are "few," "more," "most," "other," "some." Now, these words are very necessary in the formation of sentences. You cannot ignore these words, but for processing, these are not important at all. So, if you remember, we had the top 10 tokens from that particular word, that is, the AI paragraph I mentioned earlier, which was given as F_dist.top(10). Let's take that into consideration and see what we can see here is that except "intelligent" and "intelligence," most of the words are either punctuation or stop words, and hence can be removed.
Now, we'll use the compile from the re module to create a string that matches any digit or special character, and then we'll see how we can remove the stop words. So, if you have a look at the output of the post-punctuation, you can see there are no stop words here in the particular given output. And if you have a look at the output of the length of the post-punctuation, it's 233 compared to the 273, the length of the AI tokens. Now, this is very necessary in language processing, as it removes all the unnecessary words which do not hold any much more meaning.
Now, coming to another important topic of natural language processing and text mining or text analysis is the parts of speech. Now, generally speaking, the grammatical type of the word, which is the verb, noun, adjective, adverb, article, indicates how a word functions in the meaning as well as the grammatical within the sentence. Now, a word can have more than one part of speech based on the context in which it is used. For example, if we take the sentence into consideration: "Google something on the internet." Now, here "Google" acts as a verb, although it is a proper noun. So, as you can see here, we have so many types of POS tags, and we have the descriptions of those various tags. So, we have CC as coordinating conjunction, CD as cardinal number, JJ as adjective, MD as modal, NN as proper noun singular, NNS as proper noun plural, VB as verb, VBD as verb, past tense, VBG as verb, gerund or present participle, VBN as verb, past participle, VBP as verb, non-3rd person singular present, VBZ as verb, 3rd person singular present, IN as preposition or subordinating conjunction, JJR as adjective, comparative, JJS as adjective, superlative, RB as adverb, RBR as adverb, comparative, RBS as adverb, superlative, RP as particle, TO as to, UH as interjection, PRP as personal pronoun, and WRB as wh-adverb.
Now, we can use POS tags as a statistical NLP task. It distinguishes the sense of the word, which is very helpful in text realization, and it is easy to evaluate as in how many tags are correct, and you can also infer semantic information from the given text. So, let's have a look at some of the examples of POS. So, take the sentence: "The dog barked." So, here "the" is a determiner, "dog" is a noun, "barked" is a verb. And again, "the" is a determiner and "bat" is a noun, respectively. Now, let's consider another sentence: "The way to clear the plates from the table." So, as you can see here, all the tokens here correspond to a particular type of tag, which is the parts of speech tag. It is very helpful in text realization.
Now, let's consider a string and check how NLTK performs POS tagging on it. So, let's take the sentence: "Timothy is a natural when it comes to drawing." First, we are going to tokenize it. And under NLTK only, we have the pos_tag option. And we'll pass all the tokens here. So, as you can see, we have "Timothy" as NN (noun, singular), "is" as VBZ (verb, 3rd person singular present), "a" as DT (determiner), "natural" as JJ (adjective), "when" as WRB (wh-adverb), "it" as PRP (personal pronoun), "comes" as VBZ (verb, 3rd person singular present), "to" as TO (to), and "drawing" as VBG (verb, gerund or present participle). So, this is how you define the POS tags. The pos_tag function does all the work here.
Now, let's take another example here: "John is eating a delicious cake." And let's see what's the output of this one. Now, here you can see that the tagger has tagged both the word "is" and "eating" as a verb because it has considered "is eating" as a single term. This is one of the few shortcomings of the POS taggers. One thing important to keep in mind.
Now, after POS tagging, there is another important topic which is the named entity recognition. So, what does it mean? Now, the process of detecting the named entities such as the person name, the location name, the company name, the organization, the quantities, and the monetary value is called the named entity recognition. Now, named entity recognition, we have three types of identification. Here we have the non-phrase identification. Now, this step deals with extracting all the noun phrases from a text using dependency parsing and parts of speech tagging. Then we have the phrase classification. The step classification. This is the classification step in which all the extracted noun phrases are classified into respective categories, which are the location, names, organization, and much more. And apart from this, one can curate the lookup tables and dictionaries by combining information from different sources. And finally, we have the entity disambiguation. Now, sometimes it is possible that the entities are misclassified. Hence, creating a validation layer on top of the result is very useful, and the use of knowledge graphs can be exploited for this purpose. Now, the popular knowledge graphs are Google Knowledge Graph, the IBM Watson, and Wikipedia.
So, let's take a sentence into consideration: "The Google CEO Sundar Pichai introduced the new Pixel at Minnesota Roy Center event." So, as you can see here, "Google" is an organization, "Sundar Pichai" is a person, "Minnesota" is a location, and the "Roy Center event" is also tagged as an organization. Now, for using NER in Python, we'll have to import the `ne_chunk` from the NLTK module, which is present in Python. So, let's consider a text data here and see how we can perform the NER using the NLTK library. So, first, we need to import the `ne_chunk` here. Let's consider the sentence here: "The US president stays in the White House." So, we need to do all these processes again. We need to tokenize the sentence first, and then add the POS tags, and then if we use the `ne_chunk` function and pass the list of tuples containing POS tags to it. Let's see the output. So, as you can see, "The US" here is recognized as an organization, and "White House" is clubbed together as a single entity and is recognized as a facility. Now, this is only possible because of the POS tagging. Without the POS tagging, it would be very hard to detect the named entities of the given tokens.
Now, that we have understood what are named entity recognition, and yes, let's go ahead and understand one of the most important topics in NLP and text mining, which is syntax. So, what is syntax? So, in linguistics, syntax is the set of rules, principles, and the processes that govern the structure of a given sentence in a given language. The term syntax is also used to refer to the study of such principles and processes. So, what we have here are certain rules as to what part of the sentence should come at what position. With these rules, one can create a syntax tree whenever there is a sentence input. Now, a syntax tree in layman terms is basically a tree representation of the syntactic structure of the sentence of the strings. It is a way of representing the syntax of a programming language as a hierarchical tree structure. This structure is used for generating symbol tables for compilers and later code generation. The tree represents all the constructs in the language and their subsequent rules.
So, let's consider the statement: "The cat sat on the mat." So, as you can see here, the input is a sentence or a word phrase, and it has been classified into a noun phrase. Then a prepositional phrase. Again, the noun phrase is classified into article and noun. And again, we have the verb, which is "sat." And finally, we have the preposition "on," the article "the," and the noun "mat."
Now, in order to render syntax trees in our notebook, you need to install Ghostscript, which is a rendering engine. Now, this takes a lot of time, and let me show you from where you can download Ghostscript. Just type in "download Ghostscript" and select the latest version here. So, as you can see, we have two types of licenses here. We have the General Public License and the Commercial License. As creating syntax and following it is a very important part, it is also available for commercial license and it is very useful. So, I'm not going to go much deeper into what a syntax tree is and how we can do that.
So, now that we have understood what are syntax trees, let's discuss the important concept with respect to analyzing the sentence structure, which is chunking. So, chunking basically means picking up individual pieces of information and grouping them into bigger pieces. And these bigger pieces are also known as chunks. In the context of NLP and text mining, chunking means grouping of words or tokens into chunks. So, let's have a look at the example here. So, the sentence into consideration here is: "We caught the black panther." "We" is a pronoun, "caught" is a verb, "the" is a determiner, "black" is an adjective, and "panther" is a noun. So, what it has done is here, as you can see, is that "black," which is an adjective, "panther," which is a noun, and "the," a determiner, are chunked together in the noun phrase.
So, let's go ahead and see how we can implement chunking using the NLTK. So, let's take the sentence: "The big cat ate the little mouse who was after the fresh cheese." We'll use the POS tags here and also use the tokenizing function here. So, as you can see here, we have the tokens and we have the POS tags. What we'll do now is create a grammar from a noun phrase and we'll mention the tags that we want in our chunk phrase within the curly braces. So, that will be our grammar NP. Now, here we have created a regular expression matching string. Now, we'll now have to pass the chunk, and hence we'll create a chunk parser and pass our non-free string to it. So, as you can see, we have a certain error, and let me tell you why this error occurred. So, this error occurred because we did not use the Ghostscript and we did not form the syntactical tree. But in the final output, we have a tree structure here, which is not exactly in the visualization part, but it's there. So, as you can see here, we have the NP (noun phrase) for "the little mouse." Again, we have the noun phrase for "fresh cheese" also. Although "fresh" is an adjective and "cheese" is a noun, it has considered a noun phrase of these two words. So, this is how you execute chunking in the NLTK library.
So, by now, we have learned almost all the important steps in text processing, and let's apply them all in building a machine learning classifier on the movie reviews from the NLTK corpora. For that, first, let me import all the libraries, which are the pandas, the numpy library. Now, these are the basic libraries needed in any machine learning algorithm. We are also importing the CountVectorizer. I'll tell you why it is used later. Now, let's just import it for now.
So, again, if we have a look at the different elements of the corpora, as we saw earlier in the beginning of our session, we have so many files in the given NLTK corpora. Now, let's now access the movie reviews corpora under the NLTK corpora. As you can see here, we have the movie reviews. So, for that, we are going to import the movie_reviews from the NLTK.corpus. So, if you have a look at the different categories of the movie reviews, we have two categories, which are the negative and the positive. So, if you have a look at the positive, we can see we have so many text files here. Similarly, if we have a look at the negative, we have 1000 negative files also here, which have the negative feedbacks.
So, let's take a particular positive one into consideration, which is the `cv29590`. You can take any one of the files here, doesn't matter. Now, the above tokenization, as you can see here, the file is already tokenized, but it is generally useful for us to do the tokenization. But the above tokenization has increased our work here, and in order to use the CountVectorizer and the TF-IDF, we must pass the strings instead of the tokens. Now, in order to convert the strings into tokens, we can use the `word_tokenize` within the NLTK, but that has some licensing issues as of now with the Conda environment. So, instead of that, we can also use the `join` method to join all the tokens of the list into a single string, and that's what we are going to use here. So, first, we are going to create an empty list and append all the tokens within it. We have the `review_list` that is an empty list. Now, what we are going to do here is remove all the extra spaces, the commas from the list while appending it to the empty list, and perform the same for the positive and the negative reviews. So, this one we are doing it for the negative reviews, and then we'll do the same for the positive reviews as well. So, if you have a look at the length of this negative review list, it's 1,000. And the moment we add the positive reviews also, I think the length should reach 2,000. So, let me just define the positive reviews. Now, execute the same for positive reviews. And then again, if we have a look at the length of the review list, it should be 2,000. That is good.
Now, let us now create the targets before creating the features for our classifiers. So, while creating the targets, we are using the negative reviews here, we are denoting it as 0, and for the positive reviews, we are converting it into 1. And also, we will create an empty list and we'll add 1000 zeros followed by 1000 ones into the empty list. Now, we'll create a pandas Series for the target list. Now, the type of `y` must result into a pandas Series. So, if we have a look at the output of the type of `y`, it is `pandas.core.series.Series`. That is good. Now, let's have a look at the first five entries of the Series. So, as you can see, it is 1000 zeros, which were followed by 1000 ones. So, the first five inputs are all zeros.
Now, we can start creating features using the CountVectorizer or the bag of words. For that, we need to import the CountVectorizer. Now, once we have initialized the vectorizer, now we need to fit it onto the `review_list`. Now, let us now have a look at the dimensions of this particular vector. So, as you can see, it's 2000 by 16,228. Now, we are going to create a list with the names of all the features by typing the vectorizer name. So, as you can see here, we have our list. Now, what we'll do is we'll create a pandas DataFrame by passing the SciPy CSR matrix as values and feature names as the column names. Now, let us now check the dimension of this particular pandas DataFrame. So, as you can see, it's the same dimension, 2000x16,228. Now, if we have a look at the top five rows of the DataFrame. So, as you can see here, we have 16,228 columns with five rows, and all the inputs are here zero.
Now, the DataFrame we are going to do is now split it into training and testing sets. And let us now examine the training and the test sets as well. So, as you can see, the size here we have defined as 0.25, that is, the test set, that is 25%. The training set will have the 75% of the particular DataFrame. So, if you have a look at the shape of `X_train`, we have 15,000. And if we have a look at the dimension of `X_test`, this is 5,000. So, now our data is split.
Now, we'll use the Naive Bayes classifier for text classification over the training and testing sets. So, now most of you guys might already be aware of what a Naive Bayes classifier is. So, it is basically a classification technique based on the Bayes theorem with an assumption of independence among predictors. In simple terms, a Naive Bayes classifier assumes that the presence of a particular feature in a class is unrelated to the presence of any other feature. To know more, you can watch our Naive Bayes classifier video, the link to which is given in the description box below. If you want to pause at this moment of time and check quickly what a Naive Bayes classifier does and how it works, you can check that video and come back here.
Now, to implement the Naive Bayes algorithm in Python, we'll use the following library and the functions. We are going to import the `GaussianNB` from `sklearn` library, which is scikit-learn. We are going to instantiate the classifier now and fit the classifier with the training features and the labels. We are also going to import the `MultinomialNB` because we do not have only two features here; we have the multinomial features. So, now we have passed the training and the test dataset to this particular `MultinomialNB`, and then we will use the `predict` function and pass the training features. Now, let's have a look and check the accuracy of this particular metrics. So, as you can see here, the accuracy here is 1, that is very highly unlikely, but since it has given 1, that means it is overfitting and it is overly accurate, and you can also check the confusion matrix for the same. For that, what you need to do is use the `confusion_matrix` on these variables, which is `y_test` and `y_predicted`. So, as you can see here, although it has predicted 100% accuracy, the accuracy is 1. This is very highly unlikely, and you might have got a different output for this one. I've got the output here as 1.0. You might have got an output as 0.6, 0.7, or any number in between 0 and 1.
Now, the degree of inflection may be higher or lower in a language. As you have read the definition of inflection with respect to grammar, you can understand that an inflected word will have a common root form. Stemming and lemmatization have been studied, and algorithms have been developed in computer science since the 1960s. In this video, you will learn about stemming and lemmatization in a practical approach, covering the background, some famous algorithms, applications of stemming and lemmatization, and how to stem and lemmatize words, sentences, and documents using the Python NLTK package, which is the Natural Language Toolkit package provided by Python for Natural Language Processing tasks.
Now, stemming and lemmatization are text normalization techniques in the field of natural language processing that are used to prepare text, words, and documents for further processing, and these are widely used in tagging systems, indexing, SEO, web search results, and information retrieval. Now, for example, searching for the word "miss" on Google will also result in "Mrs." and "missing," as "miss" is basically the stem of both these words.
So, let's start with stemming. Now, stemming is the process of reducing inflection in words to their root forms, such as mapping a group of words to the same stem, even if the stem itself is not a valid word in the language. Now, there are English and non-English stemmers available in the NLTK package. Now, for the English language, you can choose between Porter Stemmer and Lancaster Stemmer. Porter Stemmer being the oldest one, originally developed in 1979. Lancaster Stemmer was developed in 1990 and uses a more aggressive approach than the Porter stemming algorithm.
So, let's try out the Porter Stemmer to stem words, and along with it, you will see how it is stemming the words. So, this is how the code for Porter Stemmer works. Now, Porter Stemmer uses suffix stripping to produce stems. Porter Stemmer algorithm does not follow linguistics, rather a set of five rules for different cases that are applied in phases to generate stems. Now, this is the reason why Porter Stemmer does not often generate stems that are actual English words. Now, it does not keep a lookup table for actual stems of the word but applies algorithmic rules to generate stems. It also uses the rules to decide whether it is wise to strip a suffix. Now, one can generate its own set of rules for any language. That is why Python NLTK introduced Snowball Stemmers that are used to create non-English stemmers.
So, why do we use it now? Porter Stemmer is known for its simplicity and speed. It is commonly useful in information retrieval environments, known as IR environments, for fast recall and fetching of search queries, such as for words like "connections," "connected," "connecting," or "connection." All of these words mean "connect." Now, the Lancaster Stemmer is an iterative algorithm with rules saved externally. Lancaster Stemmer is simple but heavy stemming due to iterations, and over-stemming may occur. Now, over-stemming causes the stems to be not linguistic, or they may have no meaning at all. So, now let's have a look at the Lancaster Stemmer code. For example, in the above code, "destabilized" is stemmed to "deesdor stemmer destable." Now, Lancaster Stemmer produces an even shorter stem than Porter because of iterations and over-stemming is occurred.
So, you can stem sentences and documents using NLTK stemmers using the following code. So, as you see, the stemmer sees the entire sentence as a word. So, it runs it as it is. Now, we need to stem each word in the sentence and return a combined sentence. Now, to separate the sentence into words, you can use a tokenizer. The NLTK tokenizer separates the sentence into words. So, let's see how it's done.
Now, to stem a document, we need to do the following steps. First, we have to take a document as the input. Next, we have to read the document line by line and then tokenize the line. Next, we have to stem the words, and finally, we will output the stemmed words. So, let's do some coding now. Open a file that is any text file. So, I have a text file named "deep learning," and you have to provide your complete file path in the open command of Python if it is stored in any other directory. Now, you can see the content of the file using the `.read()` method altogether. Now, you can maintain the lines in a file in a Python list using the `readlines()` method. You can now access each line and use the `tokenize_stem_sentence` function that you created before to tokenize and stem the line. Now, you can save the stemmed sentence to a text file using Python's `writelines()` function. Make a list first to store all the stemmed sentences and simply write the list to the file using `writelines()`. So, the text file created will be as follows.
Python NLTK provides not only two English stemmers, that is, Porter Stemmer and Lancaster Stemmer, but also a lot of non-English stemmers as part of Snowball Stemmer, ISRLPS Stemmer. Now, Python NLTK included Snowball Stemmers as a language to create non-English stemmers. Currently, it supports the following languages: Danish, Dutch, English, French, German, and much more.
Now, lemmatization, unlike stemming, reduces the inflected words properly, ensuring that the root word belongs to the language. In lemmatization, the root word is called a lemma. A lemma is the canonical form, dictionary form, or the citation form of a set of words. Now, it takes into consideration the morphological analysis of the words. To do so, it is necessary to have detailed dictionaries which the algorithm can look through to link the form back to its lemma. For example, a lemmatizer should map "gone," "going," and "went" into "go."
Now, Python NLTK provides WordNetLemmatizer that uses the WordNet database to look up lemmas of words. So, let's have a look at the code. Now, in the above output, you must be wondering that no actual root form has been given for any word. This is because they are given without context. Now, you need to provide the context in which you want to lemmatize, that is, the parts of speech. Remember when we were learning about the different steps in NLP, POS tagging was an important step in the whole process. Now, this is done by giving the value for the `pos` parameter in WordNetLemmatizer.
So, let's have a look at some of the applications of stemming and lemmatization. The first one is sentiment analysis. Now, sentiment analysis is the analysis of people's reviews and comments about something. It is widely used for analysis of products on online retail shops. Now, stemming and lemmatization is used as part of the text preparation process before it is analyzed. Next up is document clustering. Now, document clustering is the application of cluster analysis to textual documents. It has applications in automatic document organization, topic extraction, and fast information retrieval or filtering. Next one is information retrieval environments. Now, it is useful to use stemming and lemmatization to map documents to common topics and display search results by indexing when documents are increasing to mind-boggling numbers.
Now, you may be asking yourself, when should I use stemming and when should I use lemmatization? Now, stemming and lemmatization both generate the root form of the inflected words. The difference is that a stem might not be an actual word, whereas a lemma is an actual language word. Now, stemming follows an algorithm with steps to perform on the words, which makes it faster. Whereas in lemmatization, you use WordNet corpus and a corpus for stop words as well to produce a lemma, which makes it slower than stemming. You also have to define a part of speech to obtain the correct lemma.
So, when to use what? Now, the above points show that if speed is focused, then stemming should be used, since lemmatizers scan a corpus, which consumes time and processing. Now, it depends on the application that you are working on that decides if stemmers should be used or lemmatizers. Now, if you're building a language application in which language is important, you should use lemmatization, as it uses a corpus to match root forms.
So, with this, we come to an end of this video. I hope you guys understood the various steps involved in NLP, the different types of stemmers, and how lemmatization works, and most importantly, where to use which function.
Now, what is a syntax tree? So, this is an important thing. Syntax tree. Syntax is a study of rules governing the way words are combined to form sentences in a language. So, whenever you create a sentence, there's always some rule that you need to start off with some identifier, maybe the "is a" then you have certain verb, a certain noun coming into picture, then you have certain verb coming into picture, then maybe some adjectives come into picture. So, there are rules for creating a sentence; you cannot create a sentence without any rules. We have to have some rules like we have to specify some noun, then verb, then adjective, then prepositions. So, these rules are called as a syntax. So, syntax is the study of rules governing the way words are combined. Sentences are composed of discrete units called rules. So, every sentence has certain rules in it. Whether it's a past continuous or present continuous or present perfect or past perfect, or whether it's a simple sentence, simple tense. So, we have certain rules for defining a sentence, and that is defined by a syntax.
Now, phrase structure rules. Suppose any word can be starting with a noun. Suppose we have a certain word which can start with a noun or a verb. Then we have something called noun, verb. And then we have a determiner, prepositions. Then we can have again nouns, prepositions, and adjectives. Then again, we can have prepositions. Then finally, we can have some closing preposition. Any kind of a word is having some rules. By what we can start it, by what we can end it, what has to be in between. So, these are called as phrases of rules. So, in layman terms, a syntax tree is a tree representation of the syntactic structure of a sentence or a string. So, we have a whole sentence like, for example, "The old tree swayed in the wind." "The" is what? "The" is a determiner, "old" is an adjective, "tree" is a noun, "swayed" is a verb, "in" is a preposition, "the" is a determiner, and "wind" is a noun. So, if you see the rule for this language in this sentence, we have determiner, adjective, noun, verb, preposition, then determiner, then noun. And we can put it in the hierarchy as well: noun phrase, prepositional phrase, verb phrase. And both are combining to become a sentence. So, any word has got certain rules. So, we can define those rules. We can check these rules whether it's working fine or not. And in the same order, the sentences are coming or not, words of sentences are coming or not.
So, in order to render syntax trees in your notebook, you need to install Ghostscript, a rendering engine. For the link, this. So, if you go to this link, I've already downloaded it. You need to download the relevant `.exe` file, Ghostscript.exe, and you need to update the path in the path variable. So, if so, if I'll go to this website, I've already downloaded for 64-bit Ghostscript AGPL release, and it's a `.exe` file which gets downloaded. So, I've already downloaded it on my machine. This is the file which I have downloaded. I ran this file. It's like an installer, just like other software. Once it is done, it is getting installed in C drive. It has got all these rules, which are all the syntaxes defined in them. So, when you go to Program Files, you get a folder called `GS` within `GS-9.25`. Within that, we have a `bin` directory. So, you need to copy this path and put it in the path variable in Windows. You go to C drive properties, This PC properties, advanced system settings, environment variables, and under Path, you need to edit it and add the path. Edit text, and you need to add this path after putting a semicolon. So, I've already added it so that this can be accessed from any location. So, this `.exe` file is having all the syntaxes present in it. Whatever can be possible that a noun can come after an adjective or not, or an adjective can come after a noun or not. How the verb is going to be there in the sentence, where the prepositions can be kept. So, all the rules whichever is required to write an English language, this Ghostscript AGPL release is having. So, you need to download it first, and once you download it, you go to the folder where it is installed, open the `bin` folder, add the path of the `bin` folder in your environment variables. So, you need to copy the path where the `bin` folder is there, and you need to paste that path in the environment variable so that any place wherever you are running NLP, you can access it. So, you need to go to the Path variable, edit it, and add a text there. You need to add a text there. Adding the path. So, once you import the path, once you do that, now you notify the path of the environment variable through a piece of code. You'll say `import os`, `path = 'path/to/your/gs/bin'`, and in the environment variable of `os`, add this path: `os.environ['PATH'] = path + os.pathsep + os.environ['PATH']`. So, `os` library has got some environment variables. Environment variable means path variables would automatically get set when we invoke Python or when we invoke notebook. So, you need to add this path in the environment variable so that you can easily access it. So, once you have invoked this path, now that you have modified the path environment variable, let's discuss some important concepts with respect to analyzing sentence structure.
What is chunking? Chunking is basically means picking up the individual pieces of information and grouping them into a bigger piece. So, what is chunking? It is basically picking the smaller, smaller information and putting them together to make a bigger piece. The bigger piece is also known as a chunk. In the context of NLP, chunking means grouping of words and tokens into chunks. So, chunking is what? Picking up the individual words, grouping them to make a valid sentence, maybe, or make a valid, syntactically correct sentence. And this is what we do in chunking. We take the smaller units called tokens and we club them and make a bigger chunk. For example, you can see here, we have individual words like "we" is a pronoun, "caught" is a verb, "the" is a determiner, "black" is an adjective, and "panther" is a noun. So, what it has done is here, as you can see, is that "black," which is an adjective, "panther," which is a noun, and "the," a determiner, are chunked together in the noun phrase. So, let's go ahead and see how we can implement chunking using the NLTK. So, let's take the sentence: "The big cat ate the little mouse who was after the fresh cheese." We'll use the POS tags here and also use the tokenizing function here. So, as you can see here, we have the tokens and we have the POS tags. What we'll do now is create a grammar from a noun phrase and we'll mention the tags that we want in our chunk phrase within the curly braces. So, that will be our grammar NP. Now, here we have created a regular expression matching string. Now, we'll now have to pass the chunk, and hence we'll create a chunk parser and pass our non-free string to it. So, as you can see, we have a certain error, and let me tell you why this error occurred. So, this error occurred because we did not use the Ghostscript and we did not form the syntactical tree. But in the final output, we have a tree structure here, which is not exactly in the visualization part, but it's there. So, as you can see here, we have the NP (noun phrase) for "the little mouse." Again, we have the noun phrase for "fresh cheese" also. Although "fresh" is an adjective and "cheese" is a noun, it has considered a noun phrase of these two words. So, this is how you execute chunking in the NLTK library.
So, by now, we have learned almost all the important steps in text processing, and let's apply them all in building a machine learning classifier on the movie reviews from the NLTK corpora. For that, first, let me import all the libraries, which are the pandas, the numpy library. Now, these are the basic libraries needed in any machine learning algorithm. We are also importing the CountVectorizer. I'll tell you why it is used later. Now, let's just import it for now.
So, again, if we have a look at the different elements of the corpora, as we saw earlier in the beginning of our session, we have so many files in the given NLTK corpora. Now, let's now access the movie reviews corpora under the NLTK corpora. As you can see here, we have the movie reviews. So, for that, we are going to import the movie_reviews from the NLTK.corpus. So, if you have a look at the different categories of the movie reviews, we have two categories, which are the negative and the positive. So, if you have a look at the positive, we can see we have so many text files here. Similarly, if we have a look at the negative, we have 1000 negative files also here, which have the negative feedbacks.
So, let's take a particular positive one into consideration, which is the `cv29590`. You can take any one of the files here, doesn't matter. Now, the above tokenization, as you can see here, the file is already tokenized, but it is generally useful for us to do the tokenization. But the above tokenization has increased our work here, and in order to use the CountVectorizer and the TF-IDF, we must pass the strings instead of the tokens. Now, in order to convert the strings into tokens, we can use the `word_tokenize` within the NLTK, but that has some licensing issues as of now with the Conda environment. So, instead of that, we can also use the `join` method to join all the tokens of the list into a single string, and that's what we are going to use here. So, first, we are going to create an empty list and append all the tokens within it. We have the `review_list` that is an empty list. Now, what we are going to do here is remove all the extra spaces, the commas from the list while appending it to the empty list, and perform the same for the positive and the negative reviews. So, this one we are doing it for the negative reviews, and then we'll do the same for the positive reviews as well. So, if you have a look at the length of this negative review list, it's 1,000. And the moment we add the positive reviews also, I think the length should reach 2,000. So, let me just define the positive reviews. Now, execute the same for positive reviews. And then again, if we have a look at the length of the review list, it should be 2,000. That is good.
Now, let us now create the targets before creating the features for our classifiers. So, while creating the targets, we are using the negative reviews here, we are denoting it as 0, and for the positive reviews, we are converting it into 1. And also, we will create an empty list and we'll add 1000 zeros followed by 1000 ones into the empty list. Now, we'll create a pandas Series for the target list. Now, the type of `y` must result into a pandas Series. So, if we have a look at the output of the type of `y`, it is `pandas.core.series.Series`. That is good. Now, let's have a look at the first five entries of the Series. So, as you can see, it is 1000 zeros, which were followed by 1000 ones. So, the first five inputs are all zeros.
Now, we can start creating features using the CountVectorizer or the bag of words. For that, we need to import the CountVectorizer. Now, once we have initialized the vectorizer, now we need to fit it onto the `review_list`. Now, let us now have a look at the dimensions of this particular vector. So, as you can see, it's 2000 by 16,228. Now, we are going to create a list with the names of all the features by typing the vectorizer name. So, as you can see here, we have our list. Now, what we'll do is we'll create a pandas DataFrame by passing the SciPy CSR matrix as values and feature names as the column names. Now, let us now check the dimension of this particular pandas DataFrame. So, as you can see, it's the same dimension, 2000x16,228. Now, if we have a look at the top five rows of the DataFrame. So, as you can see here, we have 16,228 columns with five rows, and all the inputs are here zero.
Now, the DataFrame we are going to do is now split it into training and testing sets. And let us now examine the training and the test sets as well. So, as you can see, the size here we have defined as 0.25, that is, the test set, that is 25%. The training set will have the 75% of the particular DataFrame. So, if you have a look at the shape of `X_train`, we have 15,000. And if we have a look at the dimension of `X_test`, this is 5,000. So, now our data is split.
Now, we'll use the Naive Bayes classifier for text classification over the training and testing sets. So, now most of you guys might already be aware of what a Naive Bayes classifier is. So, it is basically a classification technique based on the Bayes theorem with an assumption of independence among predictors. In simple terms, a Naive Bayes classifier assumes that the presence of a particular feature in a class is unrelated to the presence of any other feature. To know more, you can watch our Naive Bayes classifier video, the link to which is given in the description box below. If you want to pause at this moment of time and check quickly what a Naive Bayes classifier does and how it works, you can check that video and come back here.
Now, to implement the Naive Bayes algorithm in Python, we'll use the following library and the functions. We are going to import the `GaussianNB` from `sklearn` library, which is scikit-learn. We are going to instantiate the classifier now and fit the classifier with the training features and the labels. We are also going to import the `MultinomialNB` because we do not have only two features here; we have the multinomial features. So, now we have passed the training and the test dataset to this particular `MultinomialNB`, and then we will use the `predict` function and pass the training features. Now, let's have a look and check the accuracy of this particular metrics. So, as you can see here, the accuracy here is 1, that is very highly unlikely, but since it has given 1, that means it is overfitting and it is overly accurate, and you can also check the confusion matrix for the same. For that, what you need to do is use the `confusion_matrix` on these variables, which is `y_test` and `y_predicted`. So, as you can see here, although it has predicted 100% accuracy, the accuracy is 1. This is very highly unlikely, and you might have got a different output for this one. I've got the output here as 1.0. You might have got an output as 0.6, 0.7, or any number in between 0 and 1.
Now, the degree of inflection may be higher or lower in a language. As you have read the definition of inflection with respect to grammar, you can understand that an inflected word will have a common root form. Stemming and lemmatization have been studied, and algorithms have been developed in computer science since the 1960s. In this video, you will learn about stemming and lemmatization in a practical approach, covering the background, some famous algorithms, applications of stemming and lemmatization, and how to stem and lemmatize words, sentences, and documents using the Python NLTK package, which is the Natural Language Toolkit package provided by Python for Natural Language Processing tasks.
Now, stemming and lemmatization are text normalization techniques in the field of natural language processing that are used to prepare text, words, and documents for further processing, and these are widely used in tagging systems, indexing, SEO, web search results, and information retrieval. Now, for example, searching for the word "miss" on Google will also result in "Mrs." and "missing," as "miss" is basically the stem of both these words.
So, let's start with stemming. Now, stemming is the process of reducing inflection in words to their root forms, such as mapping a group of words to the same stem, even if the stem itself is not a valid word in the language. Now, there are English and non-English stemmers available in the NLTK package. Now, for the English language, you can choose between Porter Stemmer and Lancaster Stemmer. Porter Stemmer being the oldest one, originally developed in 1979. Lancaster Stemmer was developed in 1990 and uses a more aggressive approach than the Porter stemming algorithm.
So, let's try out the Porter Stemmer to stem words, and along with it, you will see how it is stemming the words. So, this is how the code for Porter Stemmer works. Now, Porter Stemmer uses suffix stripping to produce stems. Porter Stemmer algorithm does not follow linguistics, rather a set of five rules for different cases that are applied in phases to generate stems. Now, this is the reason why Porter Stemmer does not often generate stems that are actual English words. Now, it does not keep a lookup table for actual stems of the word but applies algorithmic rules to generate stems. It also uses the rules to decide whether it is wise to strip a suffix. Now, one can generate its own set of rules for any language. That is why Python NLTK introduced Snowball Stemmers that are used to create non-English stemmers.
So, why do we use it now? Porter Stemmer is known for its simplicity and speed. It is commonly useful in information retrieval environments, known as IR environments, for fast recall and fetching of search queries, such as for words like "connections," "connected," "connecting," or "connection." All of these words mean "connect." Now, the Lancaster Stemmer is an iterative algorithm with rules saved externally. Lancaster Stemmer is simple but heavy stemming due to iterations, and over-stemming may occur. Now, over-stemming causes the stems to be not linguistic, or they may have no meaning at all. So, now let's have a look at the Lancaster Stemmer code. For example, in the above code, "destabilized" is stemmed to "deesdor stemmer destable." Now, Lancaster Stemmer produces an even shorter stem than Porter because of iterations and over-stemming is occurred.
So, you can stem sentences and documents using NLTK stemmers using the following code. So, as you see, the stemmer sees the entire sentence as a word. So, it runs it as it is. Now, we need to stem each word in the sentence and return a combined sentence. Now, to separate the sentence into words, you can use a tokenizer. The NLTK tokenizer separates the sentence into words. So, let's see how it's done.
Now, to stem a document, we need to do the following steps. First, we have to take a document as the input. Next, we have to read the document line by line and then tokenize the line. Next, we have to stem the words, and finally, we will output the stemmed words. So, let's do some coding now. Open a file that is any text file. So, I have a text file named "deep learning," and you have to provide your complete file path in the open command of Python if it is stored in any other directory. Now, you can see the content of the file using the `.read()` method altogether. Now, you can maintain the lines in a file in a Python list using the `readlines()` method. You can now access each line and use the `tokenize_stem_sentence` function that you created before to tokenize and stem the line. Now, you can save the stemmed sentence to a text file using Python's `writelines()` function. Make a list first to store all the stemmed sentences and simply write the list to the file using `writelines()`. So, the text file created will be as follows.
Python NLTK provides not only two English stemmers, that is, Porter Stemmer and Lancaster Stemmer, but also a lot of non-English stemmers as part of Snowball Stemmer, ISRLPS Stemmer. Now, Python NLTK included Snowball Stemmers as a language to create non-English stemmers. Currently, it supports the following languages: Danish, Dutch, English, French, German, and much more.
Now, lemmatization, unlike stemming, reduces the inflected words properly, ensuring that the root word belongs to the language. In lemmatization, the root word is called a lemma. A lemma is the canonical form, dictionary form, or the citation form of a set of words. Now, it takes into consideration the morphological analysis of the words. To do so, it is necessary to have detailed dictionaries which the algorithm can look through to link the form back to its lemma. For example, a lemmatizer should map "gone," "going," and "went" into "go."
Now, Python NLTK provides WordNetLemmatizer that uses the WordNet database to look up lemmas of words. So, let's have a look at the code. Now, in the above output, you must be wondering that no actual root form has been given for any word. This is because they are given without context. Now, you need to provide the context in which you want to lemmatize, that is, the parts of speech. Remember when we were learning about the different steps in NLP, POS tagging was an important step in the whole process. Now, this is done by giving the value for the `pos` parameter in WordNetLemmatizer.
So, let's have a look at some of the applications of stemming and lemmatization. The first one is sentiment analysis. Now, sentiment analysis is the analysis of people's reviews and comments about something. It is widely used for analysis of products on online retail shops. Now, stemming and lemmatization is used as part of the text preparation process before it is analyzed. Next up is document clustering. Now, document clustering is the application of cluster analysis to textual documents. It has applications in automatic document organization, topic extraction, and fast information retrieval or filtering. Next one is information retrieval environments. Now, it is useful to use stemming and lemmatization to map documents to common topics and display search results by indexing when documents are increasing to mind-boggling numbers.
Now, you may be asking yourself, when should I use stemming and when should I use lemmatization? Now, stemming and lemmatization both generate the root form of the inflected words. The difference is that a stem might not be an actual word, whereas a lemma is an actual language word. Now, stemming follows an algorithm with steps to perform on the words, which makes it faster. Whereas in lemmatization, you use WordNet corpus and a corpus for stop words as well to produce a lemma, which makes it slower than stemming. You also have to define a part of speech to obtain the correct lemma.
So, when to use what? Now, the above points show that if speed is focused, then stemming should be used, since lemmatizers scan a corpus, which consumes time and processing. Now, it depends on the application that you are working on that decides if stemmers should be used or lemmatizers. Now, if you're building a language application in which language is important, you should use lemmatization, as it uses a corpus to match root forms.
So, with this, we come to an end of this video. I hope you guys understood the various steps involved in NLP, the different types of stemmers, and how lemmatization works, and most importantly, where to use which function.
Now, what is a syntax tree? So, this is an important thing. Syntax tree. Syntax is a study of rules governing the way words are combined to form sentences in a language. So, whenever you create a sentence, there's always some rule that you need to start off with some identifier, maybe the "is a" then you have certain verb, a certain noun coming into picture, then you have certain verb coming into picture, then maybe some adjectives come into picture. So, there are rules for creating a sentence; you cannot create a sentence without any rules. We have to have some rules like we have to specify some noun, then verb, then adjective, then prepositions. So, these rules are called as a syntax. So, syntax is the study of rules governing the way words are combined. Sentences are composed of discrete units called rules. So, every sentence has certain rules in it. Whether it's a past continuous or present continuous or present perfect or past perfect, or whether it's a simple sentence, simple tense. So, we have certain rules for defining a sentence, and that is defined by a syntax.
Now, phrase structure rules. Suppose any word can be starting with a noun. Suppose we have a certain word which can start with a noun or a verb. Then we have something called noun, verb. And then we have a determiner, prepositions. Then we can have again nouns, prepositions, and adjectives. Then again, we can have prepositions. Then finally, we can have some closing preposition. Any kind of a word is having some rules. By what we can start it, by what we can end it, what has to be in between. So, these are called as phrases of rules. So, in layman terms, a syntax tree is a tree representation of the syntactic structure of a sentence or a string. So, we have a whole sentence like, for example, "The old tree swayed in the wind." "The" is what? "The" is a determiner, "old" is an adjective, "tree" is a noun, "swayed" is a verb, "in" is a preposition, "the" is a determiner, and "wind" is a noun. So, if you see the rule for this language in this sentence, we have determiner, adjective, noun, verb, preposition, then determiner, then noun. And we can put it in the hierarchy as well: noun phrase, prepositional phrase, verb phrase. And both are combining to become a sentence. So, any word has got certain rules. So, we can define those rules. We can check these rules whether it's working fine or not. And in the same order, the sentences are coming or not, words of sentences are coming or not.
So, in order to render syntax trees in your notebook, you need to install Ghostscript, a rendering engine. For the link, this. So, if you go to this link, I've already downloaded it. You need to download the relevant `.exe` file, Ghostscript.exe, and you need to update the path in the path variable. So, if so, if I'll go to this website, I've already downloaded for 64-bit Ghostscript AGPL release, and it's a `.exe` file which gets downloaded. So, I've already downloaded it on my machine. This is the file which I have downloaded. I ran this file. It's like an installer, just like other software. Once it is done, it is getting installed in C drive. It has got all these rules, which are all the syntaxes defined in them. So, when you go to Program Files, you get a folder called `GS` within `GS-9.25`. Within that, we have a `bin` directory. So, you need to copy this path and put it in the path variable in Windows. You go to C drive properties, This PC properties, advanced system settings, environment variables, and under Path, you need to edit it and add the path. Edit text, and you need to add this path after putting a semicolon. So, I've already added it so that this can be accessed from any location. So, this `.exe` file is having all the syntaxes present in it. Whatever can be possible that a noun can come after an adjective or not, or an adjective can come after a noun or not. How the verb is going to be there in the sentence, where the prepositions can be kept. So, all the rules whichever is required to write an English language, this Ghostscript AGPL release is having. So, you need to download it first, and once you download it, you go to the folder where it is installed, open the `bin` folder, add the path of the `bin` folder in your environment variables. So, you need to copy the path where the `bin` folder is there, and you need to paste that path in the environment variable so that any place wherever you are running NLP, you can access it. So, you need to go to the Path variable, edit it, and add a text there. You need to add a text there. Adding the path. So, once you import the path, once you do that, now you notify the path of the environment variable through a piece of code. You'll say `import os`, `path = 'path/to/your/gs/bin'`, and in the environment variable of `os`, add this path: `os.environ['PATH'] = path + os.pathsep + os.environ['PATH']`. So, `os` library has got some environment variables. Environment variable means path variables would automatically get set when we invoke Python or when we invoke notebook. So, you need to add this path in the environment variable so that you can easily access it. So, once you have invoked this path, now that you have modified the path environment variable, let's discuss some important concepts with respect to analyzing sentence structure.
What is chunking? Chunking is basically means picking up the individual pieces of information and grouping them into a bigger piece. So, what is chunking? It is basically picking the smaller, smaller information and putting them together to make a bigger piece. The bigger piece is also known as a chunk. In the context of NLP, chunking means grouping of words and tokens into chunks. So, chunking is what? Picking up the individual words, grouping them to make a valid sentence, maybe, or make a valid, syntactically correct sentence. And this is what we do in chunking. We take the smaller units called tokens and we club them and make a bigger chunk. For example, you can see here, we have individual words like "we" is a pronoun, "caught" is a verb, "the" is a determiner, "black" is an adjective, and "panther" is a noun. So, what it has done is here, as you can see, is that "black," which is an adjective, "panther," which is a noun, and "the," a determiner, are chunked together in the noun phrase. So, let's go ahead and see how we can implement chunking using the NLTK. So, let's take the sentence: "The big cat ate the little mouse who was after the fresh cheese." We'll use the POS tags here and also use the tokenizing function here. So, as you can see here, we have the tokens and we have the POS tags. What we'll do now is create a grammar from a noun phrase and we'll mention the tags that we want in our chunk phrase within the curly braces. So, that will be our grammar NP. Now, here we have created a regular expression matching string. Now, we'll now have to pass the chunk, and hence we'll create a chunk parser and pass our non-free string to it. So, as you can see, we have a certain error, and let me tell you why this error occurred. So, this error occurred because we did not use the Ghostscript and we did not form the syntactical tree. But in the final output, we have a tree structure here, which is not exactly in the visualization part, but it's there. So, as you can see here, we have the NP (noun phrase) for "the little mouse." Again, we have the noun phrase for "fresh cheese" also. Although "fresh" is an adjective and "cheese" is a noun, it has considered a noun phrase of these two words. So, this is how you execute chunking in the NLTK library.
So, by now, we have learned almost all the important steps in text processing, and let's apply them all in building a machine learning classifier on the movie reviews from the NLTK corpora. For that, first, let me import all the libraries, which are the pandas, the numpy library. Now, these are the basic libraries needed in any machine learning algorithm. We are also importing the CountVectorizer. I'll tell you why it is used later. Now, let's just import it for now.
So, again, if we have a look at the different elements of the corpora, as we saw earlier in the beginning of our session, we have so many files in the given NLTK corpora. Now, let's now access the movie reviews corpora under the NLTK corpora. As you can see here, we have the movie reviews. So, for that, we are going to import the movie_reviews from the NLTK.corpus. So, if you have a look at the different categories of the movie reviews, we have two categories, which are the negative and the positive. So, if you have a look at the positive, we can see we have so many text files here. Similarly, if we have a look at the negative, we have 1000 negative files also here, which have the negative feedbacks.
So, let's take a particular positive one into consideration, which is the `cv29590`. You can take any one of the files here, doesn't matter. Now, the above tokenization, as you can see here, the file is already tokenized, but it is generally useful for us to do the tokenization. But the above tokenization has increased our work here, and in order to use the CountVectorizer and the TF-IDF, we must pass the strings instead of the tokens. Now, in order to convert the strings into tokens, we can use the `word_tokenize` within the NLTK, but that has some licensing issues as of now with the Conda environment. So, instead of that, we can also use the `join` method to join all the tokens of the list into a single string, and that's what we are going to use here. So, first, we are going to create an empty list and append all the tokens within it. We have the `review_list` that is an empty list. Now, what we are going to do here is remove all the extra spaces, the commas from the list while appending it to the empty list, and perform the same for the positive and the negative reviews. So, this one we are doing it for the negative reviews, and then we'll do the same for the positive reviews as well. So, if you have a look at the length of this negative review list, it's 1,000. And the moment we add the positive reviews also, I think the length should reach 2,000. So, let me just define the positive reviews. Now, execute the same for positive reviews. And then again, if we have a look at the length of the review list, it should be 2,000. That is good.
Now, let us now create the targets before creating the features for our classifiers. So, while creating the targets, we are using the negative reviews here, we are denoting it as 0, and for the positive reviews, we are converting it into 1. And also, we will create an empty list and we'll add 1000 zeros followed by 1000 ones into the empty list. Now, we'll create a pandas Series for the target list. Now, the type of `y` must result into a pandas Series. So, if we have a look at the output of the type of `y`, it is `pandas.core.series.Series`. That is good. Now, let's have a look at the first five entries of the Series. So, as you can see, it is 1000 zeros, which were followed by 1000 ones. So, the first five inputs are all zeros.
Now, we can start creating features using the CountVectorizer or the bag of words. For that, we need to import the CountVectorizer. Now, once we have initialized the vectorizer, now we need to fit it onto the `review_list`. Now, let us now have a look at the dimensions of this particular vector. So, as you can see, it's 2000 by 16,228. Now, we are going to create a list with the names of all the features by typing the vectorizer name. So, as you can see here, we have our list. Now, what we'll do is we'll create a pandas DataFrame by passing the SciPy CSR matrix as values and feature names as the column names. Now, let us now check the dimension of this particular pandas DataFrame. So, as you can see, it's the same dimension, 2000x16,228. Now, if we have a look at the top five rows of the DataFrame. So, as you can see here, we have 16,228 columns with five rows, and all the inputs are here zero.
Now, the DataFrame we are going to do is now split it into training and testing sets. And let us now examine the training and the test sets as well. So, as you can see, the size here we have defined as 0.25, that is, the test set, that is 25%. The training set will have the 75% of the particular DataFrame. So, if you have a look at the shape of `X_train`, we have 15,000. And if we have a look at the dimension of `X_test`, this is 5,000. So, now our data is split.
Now, we'll use the Naive Bayes classifier for text classification over the training and testing sets. So, now most of you guys might already be aware of what a Naive Bayes classifier is. So, it is basically a classification technique based on the Bayes theorem with an assumption of independence among predictors. In simple terms, a Naive Bayes classifier assumes that the presence of a particular feature in a class is unrelated to the presence of any other feature. To know more, you can watch our Naive Bayes classifier video, the link to which is given in the description box below. If you want to pause at this moment of time and check quickly what a Naive Bayes classifier does and how it works, you can check that video and come back here.
Now, to implement the Naive Bayes algorithm in Python, we'll use the following library and the functions. We are going to import the `GaussianNB` from `sklearn` library, which is scikit-learn. We are going to instantiate the classifier now and fit the classifier with the training features and the labels. We are also going to import the `MultinomialNB` because we do not have only two features here; we have the multinomial features. So, now we have passed the training and the test dataset to this particular `MultinomialNB`, and then we will use the `predict` function and pass the training features. Now, let's have a look and check the accuracy of this particular metrics. So, as you can see here, the accuracy here is 1, that is very highly unlikely, but since it has given 1, that means it is overfitting and it is overly accurate, and you can also check the confusion matrix for the same. For that, what you need to do is use the `confusion_matrix` on these variables, which is `y_test` and `y_predicted`. So, as you can see here, although it has predicted 100% accuracy, the accuracy is 1. This is very highly unlikely, and you might have got a different output for this one. I've got the output here as 1.0. You might have got an output as 0.6, 0.7, or any number in between 0 and 1.
Verb, it is a determinant, JJ is characteristic, or NN is a noun. So here, "dog" is a noun. So here, we are doing, you can see here, adjectives and different colors. "Dog" is a noun, and determinants are chunked together into a noun phrase. So we call it as a noun phrase. Three things combined together become a noun phrase: determinant or determiner, and noun, and JJ, which is becoming a noun phrase. So there are certain rules about English. A noun phrase is having a determinant, JJ, and noun. It becomes a rule for this. So let's see how we do chunking in Python. So I'll show you one example. So let's say we import the NLTK and OS library. Then we import nltk.corpus. Then we use word tokenizer and regular expression tokenizer. We import from nltk.data import load. So this is used because we already have some inbuilt data there in this import load.
Now we have a sentence called "Mary is driving a big car." We tokenize it, send tokens. So we print send tokens. So, "Mary is driving a big car." We get the tokens out of it, and we put a POS tag on all those tokens. Once we put POS tags, we get to know what is a noun there, what is a verb, what is an adjective. All those things. Similarly, we have another sentence: "John is eating a delicious cake." We tokenize it. We put it to POS tags. We get for John what is the word having all these things. Similarly for "Jim eats banana." We tokenize it and we put it in the tokenizer. Similarly, using regular expression tokenizer, we can do regular expression tokenizer to tokenize it. We get "Jim eats a banana" with a space in between. And now, if I pass this to POS tagger, we get individual spaces in between them.
So in the corpora, if you're going to find an NLTK data, find the word corpora, you'll see word corpora there. In this, you'll see so many, so many files which are having corpora in it. The word corpora, we see the corpora. Gutenberg fields. We can see all the fields there. So this is just for our knowledge. So let me come to the point. So what we are doing here, we pick up one of the files from there. So we have all the fields in the corpus. All these text files are already present there. We pick up one file, "Shakespeare Hamlet.txt" maybe. So we are already using an inbuilt file rather than passing our own string. And we see the length of this. So it has got 37,360 different words in it. It has got 37,360 words. And we pick only the top 2,000 words. And we pass it to POS tag. And we create a "Hamlet POS." We can create a list having all these words. And this is what the list has become. It is going to print the list having all the words having POS characteristics. So it will take some time because it's a big file. So we get all the words, 2,000 words from this file. "T" is a determinant. "Tragedy" is a noun. "Hamlet" is a noun. "By" is in. "William" is a noun. We get the complete details of this.
Now we are going to pass it. Check only the NNP words in this, which is a noun phrase. We're going to check if it is a noun phrase, then only we are using it. If we get all the nouns from this file, so we get all the things. We got an article, we got a determinant, we got a verb in this file, but we are interested in only filtering NNP. And we create a new list called "Hamlet NNP." Then we are going to put one more filter there. We are going to filter out names like "William," "Williams," "reading," and "reading," and we get to know the context of the same in this file. So if you go down, you'll see in which context they are getting used. So "William" is used as a noun, noun phrase, or it is used as a noun. "Reading" is used as a verb as well as a noun.
Now we import chunk, any chunk, and we say "US president stays in White House." Tokenize it, put it to NLTK chunks, and we have seen all these examples. "The US" is an organization. "White" is a facility. "White House" is a facility. We have seen these examples. Similarly, "The state of New York touches Atlantic Ocean." From there, we come to know geographical place is "New York," organization is "Atlantic." Similarly, "Apple" is a company name. We get geographical location. Person is "Apple," organization is "company." So as I told you, like this is not a very good way of analyzing sentences because it is not going to give you all the time right results to you. So let me open chunking one. Okay. So this is chunking.
So we have seen we have imported word tokenizer. We have used regular expression tokenizer. So we have used NLTK tokenizer, import word tokenizer and regular expression tokenizer. We have added the path "C:\Program Files\..." in the bin path and we add the path in the environment variable. So this is the first step we have to do. We have to download this file "gs9.25." Reason being, we want to have some rules how the words are getting created in a form of a sentence. So let me increase the font size.
Now we have the sentence: "The little mouse ate a fresh cheese." So this sentence, we pass it to P tag. P tag is doing what? Is going to categorize whether it's a noun, it's an adjective, it's a verb. Then we introduce a grammar. We say the grammar always starts with NP, that's a keyword we need to start. And in grammar, we say we have a determinant, then any single word, then JJ, then noun, any of the conjunction words, and then we have a noun. And we define this kind of a grammar and we parse it through a regular expression parser for this. And then we use it to whenever the parser is getting created, we parse it and see the chunk results. Chunking is doing what here? If I run this, it is getting a tree-like structure. So "whole" is a sentence. S is a sentence. Noun phrase is what? It is going to determine internally what all are the nouns in them. Noun phrase. So "the little mouse," this is the noun phrase. And another noun phrase is "the fresh cheese," and "ate" is a verb. So we got a from the grammar, whatever we have decided. The grammar is what we have decided. The grammar on the basis of that, it is going to internally create a tree-like structure, how it is joining two noun phrases. So "ate" is a verb. Verb is joining two noun phrases. So we get a hierarchy of words, how the noun phrases are getting joined. And one noun phrase can have a determinant, JJ, and noun. Adjective. JJ is what? Adjective. We have a determinant, adjective, and noun. Similarly, determinant, adjective, and noun. So if you put it in a form of a clause, we can say noun phrases are made up of determinant, adjective, and noun. And two noun phrases are always joined by the verb. So you can define your own grammar. Say that we want a sentence in which we have noun phrases, and we join noun phrases by a verb, and noun phrases can have a determinant, adjective, and nouns. So you see this, the rule what we have said: determinant, adjective, and noun. And that is what is coming in the answer: determinant, adjective, and noun, determinant, adjective, and noun. So depending on the grammar, the sentence has been chunked out.
Let's see the next example. Okay. So here, "She is wearing a beautiful dress." We POS tag it. We find the adverb, adjectives, and all those things. Then we use chunk parser.parse sentence tokens. Right? We have not defined any grammar in this. We have not seen any grammar in this. And we get the results. So if I run this, see, it is giving me the results as preposition, verb, and again verb. Similarly, preposition, determinant, adjective, and noun. So, a noun phrase always consists of a determinant, adjective, and noun. And a sentence is having prepositions, verbs, and verb, another verb. So, it is determining on its own the tree-like structure, how the complete grammar has been created. So, it is going to take its grammar by default as per the English language and creating our own tree-like structure.
So, what is the need of this chunking? The question comes, why do we choose this chunking? Reason being, we need to figure it out how the sentences have been created in the whole paragraph or maybe in the whole document. And based on those sentences, we are going to create a big tree-like structure and filter out all the noun phrases there, all the adjectives there, all the prepositions there, all the verb phrases there. And figure it out in which tree, at what level they lie. Or a decision-making when we are creating a random forest like a tree, we're going to figure it out how the noun phrases are dependent on adjectives, how adjectives are dependent on determinants, how determinants are dependent on prepositions. We find the dependencies among them and we find out the right clusters. That, okay, these are the clusters where determinant, adjective, and nouns come together always. These are all sentences. These are the sentences in which we have determinant, adjectives, and noun. These are sentences where noun phrases, verbs, and noun phrases are coming. So we can do clustering kind of algorithm internally. So chunking is dividing the whole set of data into individual words and looking at their context, how they're coming in a form of a tree-like structure. And a tree-like structure is a complete hierarchy. A noun phrase can consist of, like in the previous case, you can see a noun phrase was having determinant, adjective, and noun. Here, a noun phrase is having determinant, adjective, and noun again. So a noun phrase is something which is most commonly coming as determinant, adjective, and noun. And the previous case also, "the little mouse," determinant, adjective, and noun.
Let's take one more example. "She's walking quickly to the mall." And here we tokenize it. We define the grammar as preposition, verb, any form of a verb (VB, VBD, VBZ, VBG), and then any form of an RB. So we define our own grammar that I want to filter out the words in a tree according to this rule which I have given. Then I'm parsing this using a regular expression parser and sending these tokens to creating a tree. Now the answer you get here is you get first PRP (preposition) as per our rule, then you get the verb of any form, then you get a verb of any form, then you get an adjective, and the rest of the words which are not lying into this, like "to," "DT," and "NN." If you see "to," "DT," and "NN," so DT is basically a determiner, and NN is a noun. So we already know that "to" is a "to." So "to" is classified as "to." So what we are doing here, we are classifying a sentence as per the rule which we are trying to fix up and building up a hierarchy. Let's say I change this rule up to some extent. Let's say I make this rule as I want to have. So let's say I remove this VB. Now you can see I have changed the rule. I have said I want a preposition, then verb, then adjective. So you can see first I got a preposition, then I get any form of a verb, then VB, which is "quickly," and then any adjective which is there in that or not, or list of the things. The tree gets changed depending on the grammar. It is coming as per the like the first one, we get a determinant, we get an adjective, we get a noun. So we get a determinant, adjective, and noun, determinant, adjective, and noun. So whatever the grammar is going to be there, it is going to branch out to categorize. So D has one branch, adjective has one branch, noun has another branch. And if you do not able to create a branch, it will come to the base branch as a sentence. So in this case, we have a preposition as a separate branch. We have a verb as a separate branch. We have an adjective as a separate branch. We don't have adjective, it is not coming. That's the reason. And let's say we don't give any kind of a rule. It will do it as per its own wish. "He drives fast on highways." So first preposition comes, then verb comes, then RB is what? Adverb comes. RB is adverb. Then adverb comes, then "in" comes. "In" is preposition. "In" is again a preposition that comes, and then noun comes. And if you might have provided some kind of a rule there, let's say I give the same kind of a rule which was there in the top, then things would have changed. If I would have given this rule, the things would have changed in this graph.
Now this is known as chunking. We have discussed this all about chunking. So let's talk in detail about this. So let's consider a scenario: "A little mouse ate the fresh cheese." Convert the sentence into tokens and add POS tags to the same. So POS tag is adding whether what type of word it is: it's adjective, noun, or any adverb. It is going to add it. Now we'll create a grammar from the noun phrase and we'll mention the tags that we want in our chunk phrase. Here we have created regular expression matching this chunk. We want a determinant. We want adjective without a noun. Now we'll have to pass the chunk. Hence, we'll make the chunk parser and pass our noun phrase string to it. We pass the complete string to a regular expression parser. The parser is now ready. We will use to pass within our chunk parser to create our sentence. We'll pass it to the chunk parser. So first of all, we run the regular expression parser to determine what all are determinants, adjectives, and nouns. And then we pass it to chunk parser to create a tree for us. The tokens that match our regular expression are chunked together into a noun phrase. So we have given a noun phrase, right? We have said, "Okay, these are consolidated, should become a noun phrase," and we get NP here. NP, noun phrase, we call it as what? NP. So, noun phrase is this word. So in the previous example, if I determine, give some word to them, like for example, it is a verb phrase. We get as a word as verb phrase. If I give it as noun phrase, NP, it is a noun phrase. We call it as a noun phrase. So my grammar says, "She" is a noun phrase. NP is a verb phrase. Noun phrase. So all the noun phrases get distinguished by this rule. So we say that my noun phrase should contain a preposition, verb, and an adjective. So it is your choice what kind of rule you want to put in. And according to your grammar which you put in, the system is going to create a tree for you. So maybe it is not a noun phrase if I'm not good in English. So I will say it is not a noun phrase. Maybe in the previous example, we say noun phrase is determinant, adjective, and noun. And if I'll give it as verb phrase, the system will call it as a verb phrase. It is you, as a teacher, going to train the machine that, "Okay, what is a noun phrase? What is a verb phrase? How the sentence should look like? What it should contain?" So we need to define that grammar.
So, "She is wearing a beautiful dress," and we supposed tokenize it and pass it to parser. Convert this sentence to tokens and add POS tags. Now we will have to pass the chunk here. We'll create a chunk parser and pass a noun phrase into it. So it is using the chunk parser default parser and it is automatically figuring out what is a noun phrase for us. A noun phrase generally has determinants, adjectives, and nouns. And it is finding out preposition, verb, and verbs, creating our own tree for us. So if you do not have a parser, you can use the default parser. So you do not have a grammar there with you, you can ignore that. The system will automatically do for you. Similarly, let's create a verb phrase. So we define a verb phrase as preposition, then verb of any form, then adjective. I'll create another parser, will pass through a verb phrase. Create another sentence, and we'll tokenize and add a POS to it. Again, pass it to the parser, and we'll create a verb phrase containing all this. A verb phrase where a pronoun followed by two verbs followed by adverb are chunked together into a verb phrase. So as per my rule which I'm defining, the system is chunking out that part from the whole sentence.
So let's consider another sentence below. "He drives fast on highways." And we use a default parser. It gives a verb phrase. Verb phrase consists of preposition, verb, and the verb. First step is tokenize it. That is word tokenizer. Second step is pass it to POS tag to find out whether it's a verb, adjective, or noun, or pronoun. Third step is to pass it to chunk parser to make a tree. So there are three steps.
Now there's another subprogram or subprocess called chunking. So chunking is what? Chunking is dividing the whole set of big sentence into verb phrases or the grammar that we have in mind and divide it into independent sentences or independent tokens. What is chunking? Helps us to define what we want to conclude from a chunk. So if you want to have some meaning from the chunk, we call it as chinking. Under chinking, we create a sequence of tokens which are not included in the chunk. So it is finding insights or context. Like, let's say we did stemming and lemmatization. What was the difference? Chinking is going into the context, but chunking is not doing that. Chunking is just dividing the words into phrases or verb phrase or noun phrase. Chinking is a more deep dive.
Chinking in Python. Let's create a chinking grammar string containing three things: chunk name, the name of the chunk that we want to pass, regular expression sequence of a chunk, and regular expression sequence of our chunk. So it consists of three parts: the name of a chunk, regular expression sequence of a chunk. So whatever the regular expression we want to put in the chunk to filter the data, and regular expression sequence for our chunk, whatever we want to make out of it. So let's say our chunk is going to divide the sentence into preposition, verb of any form, and adverb. I want to keep it as an adverb, and then whatever can come later on. So let's see this example. Let's say we use a regular expression parser. Now we'll create a parser for NLTK regular express. We'll pass the chinking grammar to it. And the grammar which we have created here, whatever the grammar we have created here, we pass it to the parser. Previously, we were passing to chunk parser, we are passing to chunk grammar here. Chinking grammar has been passed to regular expression, and we tokenize it. So you'll see on comparing the syntax tree of chinking parser with that of original chunk, you can see the token "quickly," that is at verb, is chilled out of the chunk. So in the previous case, we were getting this word "quickly" as a part of the chunk, right? So if you see this, what is the sentence? "She is walking quickly to the mall." That was the sentence. So this was the sentence: "She was walking quickly to the mall." Let me give this. See, when I use this grammar: preposition, verb of any form, and adjective to the chunk parser, it was giving "quickly" as a part of the verb phrase. You can see this, it was a part of the verb phrase. But when I pass it to the parser, the adverb has been moved out of it. Why it has been moved? Because we define the grammar that whatever the chunk you are having, take adverb out of it. You can see the curly braces which is out, outer side closing. We have curly braces of opposite sides. We have created one or more adverbs. So whatever the adverbs are going to be, it's going to be chunked out from there. So we get the answer here, adverb coming out of it. So syntax, if you compare the previous one, the adverb was coming as a verb phrase. Now it is not coming as a verb phrase, it is coming out of it. "Quickly." We'll now create a parser from NLTK regular expression. I'll pass chinking grammar to it. So if you use NLTK grammar and pass a chinking grammar, it is going to keep "quickly" out of the chunk because we have given in the rule that I won't want this adverb there in the chunk.
Now we'll discuss about another topic in NLTK for analyzing sentences known as context-free grammar. So this is a big domain, guys. There's a subject called automata. If you research more on that subject, automata is one field wherein how a system is understanding natural language is beautifully explained there. So I'll take one web page and I'll explain you what is context-free grammar. Types of grammar, tutorials point. So let me discuss what are different types of grammar. So introduction to grammar. So the grammar denotes syntactical rules for conversation in natural languages. Linguists have attempted to define grammars since the inception of natural language, like English, Sanskrit, Mandarin, etc. The theory of formal grammar finds its applicability extensively in the field of computer science. Noam Chomsky gave a mathematical model in 1956 for writing computer languages. He's the father of automata, Noam Chomsky. And there's a book in automata, generally you'll get it from Indian authors or foreign authors in the market. The book name is Automata, and it has got all the rules there.
What is grammar? A grammar can be written by four pupils: N, T, S, and P. Where N is a set of variables or non-terminal symbols. T is a set of terminal symbols. S is a special variable called start symbol, which belongs to N. So, start symbol is also one of the non-terminal terminals symbols. P is a production rules or the terminals or non-terminals. What are the rules which need to put? So, in order to form any grammar, we should have a set of variables called non-terminal symbols, which are not ending ones. Some terminals, which are the ending ones. Some special variables which are known as a start variables, and some rules to define it. A production rule has a formula: A pointing to beta, where A and B alpha or beta represents a subset of the whole set of strings. So let's say, for example, we have a grammar: S -> AB, A -> aB, B -> b. Now, S, A, and B are non-terminal symbols. Here, a and b are the terminal symbols. Here, S is the start. And P is the rule production rules. So, rule says what? Rule says that non-terminal symbols giving to some more non-terminal symbols, and non-terminal symbols giving end to terminal symbols.
Now, if I'll take this, let me take this and analyze it more. So we have, let's say, this rule: S pointing to AB, A pointing to aB, B pointing to b. And what is the start symbol? Start is S. So I'll pick up this one, and I can always replace A with what? In the next line, I can say A is pointing to aB. This can be done. Similarly, in the next line, we can put B pointing to b. So the sentence is having "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the production rules are: S is giving rise to this. This is giving rise to this, and this is giving rise to epsilon. Epsilon means empty. So what is the start rule? Now, what you can do is, you can substitute A with what? A with aB. And capital A with what? aB. You can do that. Now you put small a and a with this, right? You can do that. Small a and capital A, you can substitute with aB. Now again, small a and capital A, you can substitute with aB. So I'll put S pointing to aB. A pointing to aB. B pointing to b. So the sentence is "ab." This is the final answer.
Now, if you go down, let's say we have another rule. Now the
About grocery shoppers' habits. So guys, here you find patterns like customers who buy bread also often buy milk. This could be discovered by association rule mining. So that's the application of it. These connections are useful as they can shed light on consumer habits and also inform advertising decisions, such as the placement of similar products in close proximity to one another and to promote cross-selling.
Now that we've taken a good look at clustering and association, let's move ahead with how unsupervised learning works. So guys, for this learning to be effective, the data being studied must be completely unlabeled and unclassified for this to work. So large amounts of data are needed for unsupervised machine learning. So what data scientists do is they start the process by training algorithms on training datasets. These datasets do not have properly labeled and categorized data points. So the learning objective of the method is to discover patterns in the dataset and classify the points according to those patterns. So guys, unsupervised learning systems may be trained to recognize characteristic traits like whiskers, long tails, etc., by analyzing photos of cats, for example, without the requirement for labels. Unsupervised learning improves corporate insights and decision-making related to customer behavior.
Next, let's move on to the advantages of unsupervised learning. So here, the first advantage is that there are no labels required. So since unsupervised learning doesn't require any labeled data, it can be applied to a wide variety of datasets for which getting labels would be inconvenient and comparatively expensive. Next, in terms of exploring the data, unsupervised learning can unearth structures and patterns in the data that could otherwise be concealed from human eyes. Next, we have in terms of future discovery. So, by using unsupervised methods, we may better understand the data and the relevant features will improve the accuracy and efficiency of the subsequent machine learning models. Moving on, we have anomaly detection, which can be useful for spotting fraud, errors, and out-of-the-ordinary behavior. So anomaly detection seeks out to figure and highlight these outlying data points. Lastly, in terms of reduced biases. So unsupervised learning can help estimate the possible biases which can be caused by human assessments because it does not rely on human-labeled data.
So lastly, moving on with the problems of unsupervised learning. So firstly, we have lack of ground truth. So guys, since unsupervised learning does not require labeled data, there is no absolute standard by which to judge the quality of the model's predictions. This can make it hard to evaluate the effectiveness of the acquired clusters and patterns. Next, we have challenges with subjective interpretation. So guys, unsupervised learning algorithms discover patterns or clusters that may lend themselves to easy objective interpretation. Here, the context and an individual's perspective might play a role in determining the meaning of these patterns. Lastly, what we have is overfitting. So guys, unsupervised learning lacks supervision of the labeled data that may produce patterns or clusters that are not transferable to unseen data. Overfitting can occur when a model incorrectly collects data because of its specificity.
So guys, these were quite a few illustrations about unsupervised learning.
[Music]
What is classification? I hope everyone of you must have used Gmail. So, how do you think the mail is getting classified as spam or not spam mail? Well, there's nothing but classification. So, what is it? Well, classification is a process of dividing a dataset into different categories or groups by adding labels. In other words, you can say that it is a technique of categorizing observations into different categories. So basically, what you're doing is you are taking the data, analyzing it, and on the basis of some condition, you finally divide it into various categories. Now, why do we classify it? Well, we classify it to perform predictive analysis on it. Like when you get a mail, the machine predicts it to be spam or not spam mail, and on the basis of that prediction, it adds the irrelevant or spam mail to the respective folder. In general, this classification algorithm handles questions like, "Is this data belongs to A category or B category?" like, "Is this a male or is this a female?" Something like that. Are you getting it? Okay, fine.
Now, the question arises, where will you use it? Well, you can use this for fraud detection or to check whether a transaction is genuine or not. Suppose I'm using a credit card here in India. Now, due to some reason, I had to fly to Dubai. Now, if I'm using the credit card over there, I'll get a notification alert regarding my transaction. They would ask me to confirm about the transaction. So, this is also a kind of predictive analysis, as the machine predicts that something fishy is in the transaction, as 24 hours ago I made the transaction using the same credit card in India, and 24 hours later, the same credit card is being used for payment in Dubai. So, the machine predicts that something fishy is going on in the transaction. So, in order to confirm it, it sends you a notification alert. Well, this is one of the use cases of classification. You can even use it to classify different items like fruits on the basis of their taste, color, size, or weight. A machine well-trained using the classification algorithm can easily predict the class or the type of fruit whenever new data is given to it. Not just fruit, it can be any item. It can be a car, it can be a house, it can be a sign board, or anything.
Have you noticed that while you visit some sites or you try to log in into some, you get a CAPTCHA for that, right? Where you have to identify whether the given image is of a car or it's of a pole or not? You have to select it. For example, there are 10 images, and you're selecting three images out of it. So, in a way, you are training the machine, right? You're telling that these three are the pictures of a car, and the rest are not. So, who knows, you're training it for something big, right?
So, moving on ahead, let's discuss the types of classification. All right. Well, there are several different ways to perform the same task. Like, in order to predict whether a given person is a male or a female, the machine had to be trained first. All right. But there are multiple ways to train the machine, and you can choose any one of them. Just for predictive analytics, there are many different techniques. But the most common of them all is the decision tree, which we'll cover in depth in today's session. So, as a part of classification algorithms, we have decision tree, random forest, K-neighbors, K-nearest neighbor, logistic regression, linear regression, support vector machines, and so on. There are many. All right.
So, let me give you an idea about a few of them. Starting with decision tree. Well, a decision tree is a graphical representation of all the possible solutions to a decision. The decisions which are made, they can be explained very easily. For example, here is a task which says that "Should I go to a restaurant or should I buy a hamburger?" You are confused on that. So, for that, what you'll do? You'll create a decision tree for it. Starting with the root node, it will be first of all, you'll check whether you are hungry or not. All right. If you're not hungry, then just go back to sleep. Right? If you are hungry and you have $25, then you'll decide to go to a restaurant. And if you're hungry and you don't have $25, then you'll just go and buy a hamburger. That's it. All right. So, this is about decision tree.
Now, moving on ahead, let's see what is a random forest. Well, a random forest builds multiple decision trees and merges them together to get a more accurate and stable prediction. All right. Most of the time, a random forest is trained with a bagging method. The bagging method is based on the idea that the combination of learning models increases the overall result. If you're combining the learning from different models and then clubbing it together, what it will do? It will increase the overall result. Fine. Just one more thing. If the size of your dataset is huge, then in that case, one single decision tree would lead to an overfitted model. Same way, like a single person might have its own perspective on the complete population as the population is very huge, right? However, if we implement a voting system and ask different individuals to interpret the data, then we would be able to cover the patterns in a much meticulous way. Even from the diagram, you can see that in section A, we have a large training dataset. What we do? We first divide our training dataset into 'n' subsamples. All right. And we create a decision tree for each subsample. Now, in part B, what we do? We take the vote out of every decision made by every decision tree. And finally, we club the votes to get the random forest decision. Fine.
Let's move on ahead. Next, we have Naive Bayes. So, Naive Bayes is a classification technique which is based on Bayes' theorem. It assumes that the presence of any particular feature in a class is completely unrelated to the presence of any other feature. Naive Bayes is a simple and easy-to-implement algorithm, and due to its simplicity, this algorithm might outperform more complex models when the size of the dataset is not large enough. All right. A classical use case of Naive Bayes is document classification. In that, what you do? You determine whether a given text corresponds to one or more categories. In the text case, the features used might be the presence or absence of any keyword. So, this was about Naive Bayes. From the diagram, you can see that using Naive Bayes, we have to decide whether we have a disease or not. First, what we do? We check the probability of having a disease and not having the disease. Right? Probability of having a disease is 0.1, while on the other hand, probability of not having a disease is 0.9. Okay. First, let's see when we have a disease and we go to the doctor. All right. So, when we visited the doctor and the test is positive. So, probability of having a positive test when you're having a disease is 0.80, and probability of a negative test when you already have a disease is 0.20. This is also a false negative statement, as the test is detecting negative, but you still have the disease. Right? So, it's a false negative statement. Now, let's move ahead when you don't have the disease at all. So, probability of not having a disease is 0.9. And when you visit the doctor, and the doctor is like, "Yes, you have the disease," but you already know that you don't have the disease. So, it's a false positive statement. So, probability of having a disease when you actually know there is no disease is 0.1, and probability of not having a disease when you actually know there is no disease. So, the probability of it is around 0.90. Fine. It is the same as the probability of not having a disease. Even the test is showing the same result. It's a true positive statement. So, it is 0.9. All right.
So, let's move on ahead and discuss about the KNN algorithm. So, this KNN algorithm, or the K-nearest neighbor, it stores all the available cases and classifies new cases based on the similarity measure. The 'K' in the KNN algorithm is the nearest neighbor we wish to take a vote from. For example, if K=1, then the object is simply assigned to the class of that single nearest neighbor. From the diagram, you can see the difference in the image when K=1, K=3, and K=5. Right? Well, the modern systems are now able to use the K-nearest neighbor for visual pattern recognition to scan and detect hidden packages in the bottom bin of a shopping cart at the checkout. If an object is detected which matches exactly to the object listed in the database, then the price of the spotted product could even automatically be added to the customer's bill. While this automated billing practice is not used extensively at this time, but the technology has been developed and is available for use. If you want, you can just use it. And yeah, one more thing, K-nearest neighbor is also used in retail to detect patterns in credit card users. Many new transaction scrutinizing software applications use KNN algorithms to analyze register data and spot unusual patterns that indicate suspicious activity. For example, if register data indicates that a lot of customer information is being entered manually rather than through automated scanning and swiping, then in that case, this could indicate that the employees who are using the register are in fact stealing customers' personal information. Or if register data indicates that a particular good is being returned or exchanged multiple times, this could indicate that employees are misusing the return policy or trying to make money from doing fake returns. Right? So, this was about the KNN algorithm. Fine.
Since our main focus for this session will be on decision trees, so starting with, "What is a decision tree?" But first, let me tell you why we chose decision trees to start with. Well, these decision trees are really very easy to read and understand. It belongs to one of the few models that are interpretable, where you can understand exactly why the classifier has made that particular decision. Right? Let me tell you a fact: for a given dataset, you cannot say that this algorithm performs better than that. It's like you cannot say that a decision tree is better than Naive Bayes or Naive Bayes is performing better than a decision tree. It depends on the dataset, right? You have to apply a hidden trial method with all the algorithms one by one and then compare the results. The model which gives the best result is the model which you can use for better accuracy for your dataset. All right.
So, let's start with, "What is a decision tree?" Well, a decision tree is a graphical representation of all the possible solutions to a decision based on certain conditions. Now, you might be wondering why this thing is called a decision tree. Well, it is called so because it starts with a root and then branches off to a number of solutions, just like a tree, right? Even a tree starts from a root, and it starts growing its branches once it gets bigger and bigger. Similarly, in a decision tree, it has a root which keeps on growing with an increasing number of decisions and conditions. Now, let me tell you a real-life scenario. I won't say that all of you, but most of you must have used it. Remember, whenever you dial the toll-free number of your credit card company, it redirects you to an intelligent computerized assistant where it asks you questions like, "Press one for English or press two for Hindi, press three for this, press four for that," right? Now, once you select one, now again it redirects you to a certain set of questions like, "Press one for this, press one for that," and similarly, right? So, this keeps on repeating until you finally get to the right person, right? You might think that you were caught in a voicemail hell, but what the company was actually doing, it was just using a decision tree to get you to the right person. All right.
I'd like you to focus on this particular image for a moment. On this particular slide, you can see an image where the task is, "Should I accept a new job offer or not?" All right. So, you have to decide that. For that, what you did? You created a decision tree. Starting with the base condition or the root node was that the basic salary or the minimum salary should be $50,000. If it is not $50,000, then you are not at all accepting the offer. All right. So, if your salary is greater than $50,000, then you will further check whether the commute is more than 1 hour or not. If it is more than 1 hour, you'll just decline the offer. If it is less than 1 hour, then you are getting closer to accepting the job offer. Then further, what you'll do? You'll check whether the company is offering free coffee or not, right? If the company is not offering free coffee, then you'll just decline the offer, and if it is offering free coffee, then yeah, you will happily accept the offer, right? There are just an example of a decision tree.
[Music]
Now, let's move ahead and understand a decision tree. Well, here is a sample dataset that I'll be using to explain you about the decision tree. All right. In this dataset, each row is an example, and the first two columns provide features or attributes that describe the data, and the last column gives the label or the class we want to predict. And if you like, you can just modify this data by adding additional features and more examples, and our program will work in exactly the same way. Fine. Now, this dataset is pretty straightforward, except for one thing. I hope you have noticed that it is not perfectly separable. Let me tell you something more about that. As in the second and fifth examples, they have the same features but different labels. Both have yellow as their color and diameter as three, but the labels are mango and lemon. Fine. Let's move on and see how a decision tree handles this case. All right.
In order to build a tree, we'll use a decision tree algorithm called CART. This CART algorithm stands for Classification and Regression Tree algorithm. All right. Let's see a preview of how it worked. All right. To begin with, we'll add a root node for the tree, and all nodes receive a list of rows as input, and the root will receive the entire training dataset. Now, each node will ask a true and false question about one of the features, and in response to that question, we'll split or partition the dataset into two different subsets. These subsets then become input to two child nodes we add to the tree. And the goal of the question is to finally unmix the labels as we proceed down, or in other words, to produce the purest possible distribution of the labels at each node. For example, the input of this node contains only one single type of label. So, we could say that it's perfectly unmixed. There is no uncertainty about the type of label, as it consists of only grapes. Right? On the other hand, the labels in this node are still mixed up. So, we'll ask another question to further drill it down, right? But before that, we need to understand which question to ask and when. And to do that, we need to quantify how much a question helps to unmix the labels. And we can quantify the amount of uncertainty at a single node using a metric called Gini impurity. And we can quantify how much a question reduces that uncertainty using a concept called information gain. We'll use these to select the best question to ask at each point. And then, what we'll do? We'll iterate the steps. We'll recursively build the tree on each of the new nodes. We'll continue dividing the data until there are no further questions to ask. And finally, we reach our leaf. All right.
All right. So, this was about decision trees. So, in order to create a decision tree, first of all, what you have to do, you have to identify different sets of questions that you can ask to a tree, like, "Is this color green?" And what will be these questions? These questions will be decided by your dataset, like, "Is this color green?" "Is the diameter greater than or equal to three?" "Is the color yellow?" Right? Questions resemble your dataset. Remember that. All right? So, if my color is green, then what it will do? It will divide into two parts. First, the green mango will be in the true, while on the false, we have lemon and the mango. All right. If the color is green, or the diameter is greater than or equal to three, or the color is yellow.
Now, let's move on and understand about decision tree terminologies. All right. So, starting with root node. The root node is the base node of a tree. The entire tree starts from a root node. In other words, it is the first node of a tree. It represents the entire population or sample. And this entire population is further segregated or divided into two or more homogeneous sets. Fine. Next is the leaf node. Well, a leaf node is one when you reach at the end of the tree, right? That is, you cannot further segregate it down to any other level. So, that is the leaf node. Next is splitting. Splitting is dividing your root node or your node into different sub-parts on the basis of some condition. All right. Then comes the branch or the subtree. Well, this branch or subtree gets formed when you split the tree. Suppose when you split a root node, it gets divided into two branches or two subtrees. Right? Next is the concept of pruning. Well, you can say that pruning is just the opposite of splitting. What we are doing here, we are just removing the subnodes of a decision tree. We'll see more about pruning later in this session. All right. Let's move on ahead. Next is parent or child node. Well, first of all, the root node is always the parent node, and all other nodes associated with that are known as child nodes. Well, you can understand it in a way that all the top nodes belong to a parent node, and all the bottom nodes which are derived from a top node are child nodes. The node producing a further node is a child node, and the node which is producing it is a parent node. Simple concept, right?
Let's use the CART algorithm and design a tree manually. So, first of all, what you'll do, you decide which question to ask and when. So, how will you do that? So, let's first of all visualize the decision tree. So, this is the decision tree which we'll be creating manually. All right. First of all, let's have a look at the dataset. You have outlook, temperature, humidity, and windy as your different attributes. On the basis of that, you have to predict whether you can play or not. So, which one among them should you pick first? Answer: determine the best attribute that classifies the training data. All right. So, how will you choose the best attribute, or how does a tree decide where to split, or how the tree will decide its root node? Well, before we move on and split a tree, there are some terminologies that you should know. All right. First being the Gini index. So, what is this Gini index? This Gini index is a measure of impurity or purity used in building a decision tree in the CART algorithm. All right. Next is information gain. This information gain is the decrease in entropy after a dataset is split on the basis of an attribute. Constructing a decision tree is all about finding an attribute that returns the highest information gain. All right. So, you'll be selecting the node that would give you the highest information gain. All right. Next is reduction in variance. This reduction in variance is an algorithm which is used for continuous target variables or regression problems. The split with lower variance is selected as a criterion to split the population. See, in general terms, what do you mean by variance? Variance is how much your data is varying. Right? So, if your data is less impure, or it is more pure, then in that case, the variation would be less, as all the data are most similar. Right? So, this is also a way of splitting a tree. The split with lower variance is selected as a criterion to split the population. All right. Next is the G-square. G-square is an algorithm which is used to find out the statistical significance between the differences between subnodes and the parent nodes. Fine. Let's move ahead.
Now, the main question is, how will you decide the best attribute? For now, just understand that you need to calculate something known as information gain. The attribute with the highest information gain is considered the best. Yeah, I know your next question might be like, "What is this information gain?" But before we move on and see what exactly information gain is, let me first introduce you to a term called entropy, because this term will be used in calculating the information gain. Well, entropy is just a metric which measures the impurity of something, or in other words, you can say that it's the first step to do before you solve the problem of a decision tree. As I mentioned here something about impurity. So, let's move on and understand what is impurity. Suppose you have a basket full of apples, and another bowl which is full of the same label which says "apple." Now, if you are asked to pick one item from each basket and bowl, then the probability of getting the apple and its correct label is one. So, in this case, you can say that impurity is zero. All right. Now, what if there are four different fruits in the basket and four different labels in the bowl? Then the probability of matching the fruit to a label is obviously not one. It's something less than that. Well, it could be possible that I picked a banana from the basket, and when I randomly picked a label from the bowl, it says "cherry." Any random permutation and combination can be possible. So, in this case, I'd say that impurity is non-zero. I hope the concept of impurity is clear.
So, coming back to entropy, as I said, entropy is the measure of impurity. From the graph on your left, you can see that as the probability is zero or one, that is, either they are highly impure or they are highly pure, then in that case, the value of entropy is zero, and when the probability is 0.5, then the value of entropy is maximum. Well, what is impurity? Impurity is the degree of randomness, how random a data is. So, if the data is completely pure, in that case, the randomness equals zero. Or if the data is completely impure, even in that case, the value of impurity will be zero. A question like, "Why is it that the value of entropy is maximum at 0.5?" might arise in your mind, right? So, let me discuss about that. Let me derive it mathematically. So, as you can see here on the slide, the mathematical formula of entropy is minus of probability of yes. Let's move on and see what this graph has to say mathematically. Suppose 'S' is our total sample space, and it's divided into two parts: yes and no. Like in our dataset, the result for playing was divided into two parts: either yes or no, which we have to predict whether we have to play or not. Right? So, for that particular case, you can define the formula of entropy as: Entropy of total sample space = -(Probability of yes * log of probability of yes with base 2) - (Probability of no * log of probability of no with base 2), where 'S' is your total sample space, and P(yes) is the probability of yes, and P(no) is the probability of no. Well, if the number of yes equals the number of no, that is, probability of S = 0.5. Right? Since you have an equal number of yes and no. So, in that case, the value of entropy will be one. Just put the value over there. All right. Let me just move to the next slide. I'll show you this. All right.
Next is, if it contains all yes or all no, that is, probability of a sample space is either one or zero, then in that case, entropy will be equal to zero. Let's see it mathematically one by one. So, let's start with the first condition where the probability was 0.5. So, this is our formula for entropy, right? So, this is our first case, right? Which we discussed that when the probability of yes equals the probability of no, that is, in our dataset, we have an equal number of yes and no. All right. So, probability of yes equals probability of no, and that equals 0.5. Or in other words, you can say that yes + no = total sample space. All right. Since the probability is 0.5. So, when you put the values in the formula, you get something like this. And when you calculate it, you'll get the entropy of the total sample space as well. All right. Let's see for the next case. What was the next case? Either you have total yes, or you have total no. So, if you have total yes, let's see the formula when we have total yes. So, you have all yes and zero no. Fine. So, probability of yes = 1, and yes is the total sample space, obviously. So, in the formula, when you put that thing up, you'll get Entropy of sample space = -(1 * log 1). As the value of log 1 equals 0, so the total thing will result to zero. Similarly, is the case with no. Even in that case, you'll get the entropy of the total sample space as zero. So, this was all about entropy. All right.
Next is, what is information gain? Well, information gain, what it does, it measures the reduction in entropy. It decides which attribute should be selected as the decision node. If 'S' is our total collection, then Information Gain = Entropy(S) - Weighted Average * Entropy of each feature. Don't worry, we'll just see how to calculate it with an example. All right.
[Music]
So, let's manually build a decision tree for our dataset. So, this is our dataset which consists of 14 different instances, out of which we have 9 yes and 5 no. All right. So, we have the formula for entropy. Just put over that. Since 9 yes, so total probability of yes = 9/14, and total probability of no = 5/14. And when you put up the value and calculate the result, you'll get the value of entropy as 0.94. All right. So, this was your first step, that is, compute the entropy for the entire dataset. All right. Now, you have to select that out of outlook, temperature, humidity, and windy, which of the nodes should you select as the root node? Big question, right? How will you decide that this particular node should be chosen as the base node, and on the basis of that only, I'll be creating the entire tree? How you'll select that? Let's see. So, you have to do it one by one. You have to calculate the entropy and information gain for all of the different nodes. So, starting with outlook. So, outlook has three different parameters: sunny, overcast, and rainy. So, first of all, select how many number of yes and no are there in the case of sunny. Like, when it is sunny, how many number of yes and how many number of nos are there. So, in total, we have 2 yes and 3 nos in the case of sunny. In the case of overcast, we have all yes. So, if it is overcast, then we'll surely go to play. It's like that. All right. And next, it is rainy, then total number of yes = 3, and total number of no = 2. Fine.
Next, what we do? We calculate the entropy for each feature. For here, we are calculating the entropy when outlook = sunny. First of all, we are assuming that outlook is our root node, and for that, we are calculating the information gain for it. All right. So, in order to calculate the information gain, remember the formula: it was Entropy(S) - Weighted Average * Entropy of each feature. All right. So, what we are doing here, we are calculating the entropy of outlook when it was sunny. So, total number of yes when it was sunny was 2, and total number of no, that was 3. Fine. So, let's put it in the formula. Since the probability of yes is 2/5, and the probability of no is 3/5, so you will get something like this. All right. So, you are getting the entropy of sunny as 0.971. Fine. Next, you'll calculate the entropy for overcast. When it was overcast, remember it was all yes. Right? So, the probability of yes = 1. And when you put over that, you'll get the value of entropy as zero. Fine. And when it was rainy, rainy has 3 yes and 2 nos. So, probability of yes in the case of sunny is 3/5, and probability of no in the case of sunny is 2/5. And when you add the value of probability of yes and probability of no to the formula, you get the entropy of sunny as 0.971. Fine.
Now, you have to calculate how much information you're getting from outlook, that equals weighted average. All right. So, what was this weighted average? Total number of yes and total number of no. Fine. So, information from outlook = (5/14) * Entropy(Sunny) + (4/14) * Entropy(Overcast) + (5/14) * Entropy(Rainy). From where does this 5 came over? We are calculating the total number of sample space within that particular outlook when it was sunny. Right? So, in the case of sunny, there were 2 yes and 3 nos. All right? So, weighted average for sunny would be equal to 5/14. All right? Since the formula was (Number of samples in subset / Total samples) * Entropy of subset. All right. So, as calculated, the entropy for sunny is 0.971. Right? So, what we'll do? We'll multiply 5/14 with 0.971. Right? Well, this was a calculation for information when outlook = sunny. But outlook even equals overcast and rainy. For in that case, what we'll do? Again, similarly, we'll calculate for everything. For overcast, weighted average is 4/14 * its entropy that is 0. And for rainy, it is 5/14 * 3 yes and 2 nos multiplied by its entropy that is 0.971. And finally, we'll take the sum of all of them, which equals to 0.693. Right?
Next, we'll calculate the information gained. This what we did earlier was information taken from outlook. Now, we are calculating what is the information we are gaining from outlook. Right? Now, this information gained that equals to Total Entropy - Information taken from outlook. All right. So, total entropy we had 0.94 minus information we took from outlook is 0.693. So, the value of information gained from outlook results to 0.247. All right.
So, next, what do we have to do? Let's assume that windy is our root node. So, windy consists of two parameters: false and true. Let's see how many yes and how many nos are there in the case of true and false. So, when windy has false as its parameter, then in that case, it has 6 yes and 2 nos. And when it has true as its parameter, it has 3 yes and 3 nos. All right. So, let's move ahead and similarly calculate the information taken from windy. And finally, calculate the information gained from windy. All right. So, first of all, what we'll do, we'll calculate the entropy of each feature, starting with windy = true. So, in the case of true, we had an equal number of yes and equal number of no. Well, remember the graph, when we had the probability as 0.5, as total number of yes = total number of no, and for that case, the entropy equals 1. So, we can directly write entropy of true when it's windy is 1, as we had already proved it when probability equals 0.5, the entropy is the maximum, that equals to 1. All right. Next is entropy of false when it is windy. All right. So, similarly, just put the probability of yes and no in the formula and then calculate the result. Since you have 6 yes and 2 nos. So, in total, you'll get the probability of yes as 6/8, and probability of no as 2/8. All right. So, when you will calculate it, you will get the entropy of false as 0.811. All right.
Now, let's calculate the information from windy. So, total information collected from windy = (8/14) * Entropy(False) + (6/14) * Entropy(True). So, we'll calculate the weighted average for each one of them. And then we'll sum it up to finally get the total information taken from windy. So, in this case, it equals to (8/14) * 0.811 + (6/14) * 1. What is this 8? 8 is the total number of yes and no in the case when windy = false. Right? So, when it was false, so total number of yes that equals to 6, and total number of no that equals to 2, that sums up to 8. All right? So, that is why the weighted average results to 8/14. Similarly, information taken when windy = true = (6/14) * 1. All right? So, it is (8/14) * 0.811 + (6/14) * 1, which results to 0.892. This is information taken from windy. All right. Now, how much information you are gaining from windy? So, for that, what you'll do? So, total information gained from windy = Total Entropy - Information taken from windy. All right. That is 0.94 - 0.892, that equals to 0.048. So, 0.048 is the information gained from windy. All right.
[Music]
Similarly, we calculated for the rest two. All right. So, for outlook, as you can see, the information was 0.693, and its information gain was 0.247. In the case of temperature, the information was around 0.911, and the information gain that was equal to 0.029. In the case of humidity, the information gain was 0.152, and in the case of windy, the information gain was 0.048. So, what we'll do? We'll select the attribute with the maximum information gain. Fine. Now, we have selected outlook as our root node, and it is further subdivided into three different parts: sunny, overcast, and rain. So, in the case of overcast, we have seen that it consists of all yes. So, we can consider it as a leaf node. But in the case of sunny and rainy, it's doubtful, as it consists of both yes and both no. So, you need to recalculate the things, right? Again, for this node, you have to recalculate the things. All right. You have to again select the attribute which is having the maximum information gain. All right. So, this is how your complete tree will look like. All right. So, let's see when you can play. So, you can play when outlook is overcast. All right. In that case, you can always play. If the outlook is sunny, you'll further drill down to check the humidity condition. All right? If the humidity is normal, then you'll play. If the humidity is high, then you won't play, right? When the outlook predicts that it's rainy, then further you'll check whether it's windy or not. If it is a weak wind, then you'll go and opt for play. But if it has strong wind, then you won't play, right? So, this is how your entire decision tree would look like at the end. All right?
[Music]
Okay. Now comes the concept of pruning. Says that, "What should I do to play?" Well, you have to do pruning. Pruning will decide how you will play. What is this pruning? Well, this pruning is nothing but cutting down the nodes in order to get the optimal solution. All right. So, what pruning does? It reduces the complexity. All right. As here you can see on the screen that it's showing only the result for yes, that is, it's showing all the results which say that you can play. All right.
Before we drill down to our practical session, a common question might come in your mind. You might think that a tree-based model is better than a linear model, right? You can think like, "If I can use a logistic regression for a classification problem and linear regression for a regression problem, then why is there a need to use trees?" Well, many of us have this question in their mind, and well, there's a valid question too. Well, actually, as I said earlier, you can use any algorithm. It depends on the type of problem you're solving. Let's look at some key factors which will help you to decide which algorithm to use and when. So, the first point being, if the relationship between dependent and independent variables is well approximated by a linear model, then linear regression will outperform tree-based models. Second case, if there is high nonlinearity and complex relationships between dependent and independent variables, a tree model will outperform a classical regression model. In the third case, if you need to build a model which is easy to explain to people, a decision tree model will always do better than a linear model, as decision tree models are simpler to interpret than linear regression. All right.
Now, let's move on ahead and see how you can write a decision tree classifier from scratch in Python using the CART algorithm. All right. For this, I'll be using Jupyter Notebook with Python 3.0 installed on it. All right. So, let's open the Anaconda and the Jupyter Notebook. Where is that? So, this is our Anaconda Navigator, and I'll directly jump over to Jupyter Notebook and hit the launch button. I guess everyone knows that Jupyter Notebook is a web-based interactive computing notebook environment where you can run your Python codes. So, my Jupyter Notebook, it opens on my localhost 891. So, I'll be using this Jupyter Notebook in order to write my decision tree classifier using Python. For this decision tree classifier, I have already written the set of codes. Let me explain you just one by one.
So, we'll start with initializing our training dataset. So, this is our sample dataset for which each row is an example. The last column is a label, and the first two columns are the features. If you want, you can add some more features and examples for your practice. Interesting fact is that this dataset is designed in a way that the second and the fifth examples have almost the same features, but they have different labels. All right. So, let's move on and see how the tree handles this case. As you can see here, both of them, the second and the fifth column, have the same features. What they differ in is just their label. Fine. So, let's move ahead. So, this is our training dataset. Next, what we are doing, we are adding some column labels. So, they are used only to print the trees. Fine. So, what we'll do? We will add headers to the columns, like the first column is of color, second is of diameter, and third is of label column. All right.
Next, what we'll do? We'll define a function as `unique_values` in which we'll pass the rows and the columns. So, this function, what it will do? It will find the unique values for a column in the dataset. So, this is an example for that. So, what we are doing here? We are passing `training_data` as our rows and column number as zero. So, what we are doing? We are finding unique values in terms of color. And in this, since the row is `training_data` and the column is one. So, what we are doing here? So, we are finding the unique values in terms of diameter. Fine. So, this is just an example.
Next, what we'll do? We'll define a function as `class_count` and we'll pass the rows into it. So, what it does? It counts the number of each type of example within the dataset. So, in this function, what we are basically doing? We are counting the number of each type of example in the dataset, or what we are doing? We are counting the unique values for the label in the dataset. As a sample, you can see here, we can pass the entire training dataset to this particular function. As `class_count`, what it will do? It will find all the different types of labels within the training dataset. As you can see here, the unique labels consist of mango, grape, and lemon.
So, next, what we'll do? We'll define a function `is_numeric` and we'll pass a value into it. So, what it will do? It will just test if the value is numeric or not, and it will return if the value is an integer or a float. For example, you can see `is_numeric(7)`. So, it is an integer, so it will return an `int` value. And if you're passing `red`, it's not a numeric value, right? So, moving on ahead, we'll define a class named as `Question`. So, what this `Question` does? This `Question` is used to partition the dataset. This class, what it does? It just records a column number, for example, zero for color, all right, and a column value, for example, green.
Next, what we are doing? We are defining a `match` method which is used to compare the feature value in the example to the feature value stored in the question. Let's see how. First of all, what we are doing? We are defining an `__init__` function, and inside that, we are passing `self`, `column`, and `value` as parameters. So, next, what we do? We define a function as `match`. What it does? It compares the feature value in an example to the feature value in this question, right?
Next, we'll define a function as `__repr__` which is just a helper method to print the question in a readable format. Next, what we are doing? We are defining a function `partition`. Well, this function is used to partition the dataset. Each row in the dataset, it checks if it matches the question or not. If it does, so it adds it to the true rows, or if not, then it adds to the false rows. All right. For example, as you can see here, let's partition the training dataset based on whether the rows are red or not. Here, we are calling the function `Question` and we are
passing a value of zero and red to it. So what it will do, it will assign all the red rows to true rows and everything else will be assigned to false underscore rows. Fine.
Next, what we'll do? We'll define a gen function and inside that we'll pass the list of rows. So what it will do, it will just calculate the gen impurity for the list of rows.
Next, what we are doing here? We're defining a function as information gain. So what this information gain function does? It calculates the information gain using the uncertainty of the starting node minus the weighted impurity of the child node.
The next function is find the best split. Well, this function is used to find the best question to ask by iterating over every feature or value and then calculating the information gain. For the detailed explanation on the code, you can find the code in the description given below. All right.
Next, we'll define a class as leaf for classifying the data. It holds a dictionary of class like mango for how many times it appears in the row from the training data that reaches this leaf. All right.
Next is the decision node. So this decision node, it will ask a question. This holds a reference to the question and the two child nodes. On the basis of it, you are deciding which node to add further to which branch. All right.
So next, what we are doing? We are defining a function of build tree and inside that we are passing our number of rows. So this is the function that is used to build a tree. So initially, what we did? We defined all the various functions that we'll be using in order to build a tree. So let's start by partitioning the data set for each unique attribute. Then we'll calculate the information gain and then return the question that produces the highest gain and on the basis of that, we'll split the tree. So what we are doing here? We are partitioning the data set, calculating the information gain and then what this is returning? It is returning the question that is producing the highest gain. All right.
Now, if gain equals zero, return leaf rows. So what it will do? So if you are getting no further gain, that is gain equals zero, then in that case, since no further question could be asked, so what it will do? It will return a leaf. Fine.
Now, true underscore rows or false underscore rows equal partition with rows and the question. So if we are reaching till this position, then you have already found a feature value which will be used to partition the data set. Then what you will do? You will recursively build the true branch and similarly recursively build the false branch. So return decision node and inside that we'll be passing question, true branch and false branch. So what it will do? It will return a question node. Now, this question node, this records the best feature or the value to ask at this point. Fine.
Now that we have built our tree, next, what we'll do? We'll define a print tree function which will be used to print the tree. Fine. So finally, what we are doing in this particular function? That we are printing our tree.
Next is the classify function which will use it to decide whether to follow the true branch or the false branch and then compare to the feature values stored in the node to the example we are considering. And last, what we'll do? We'll finally print the prediction and leave. So let's execute it and see. Okay. So this is our testing data. All right. So we printed our leaf as well. Now that we have trained our algorithm with our training data set, now it's time to test it. So this is our testing data set. So let's finally execute it and see what is the result. So this is the result you will get. So first question which is asked by the algorithm is, is diameter greater than equal to three? If it is true, then it will further ask if the color is yellow. Again, if it is true, then it will predict mango as one and lemon with one. All right. And in case it is false, then it will just predict the mango. Now, this was the true part. Now, next coming to if diameter is not greater than or equal to three, then in that case, it's false. And what it will do? It will just predict the grape. Fine. Okay. So this was all about the coding part.
Now, let's conclude this session. But before concluding, let me just show you one more thing. Now, there's a scikit-learn algorithm cheat sheet which explains you which algorithm you should use and when. All right. It's built in a decision tree format. Let's see how it is built. So first condition, it will check whether you have 50 samples or not. If your samples are greater than 50, then it will move ahead. If it is less than 50, then you need to collect more data. If your sample is greater than 50, then you have to decide whether you want to predict a category or not. If you want to predict a category, then further you will see that whether you have label data or not. If you have label data, then that would be a classification algorithm problem. If you don't have the label data, then it would be a clustering problem. Now, if you don't want to predict a category, then what do you want to predict? Predict a quantity. Well, if you want to predict a quantity, then in that case, it would be a regression problem. If you don't want to predict a quantity and you want to keep looking further, then in that case, you should go for dimensionality reduction problems and still, if you don't want to look and the predicting structure is not working, then you have tough luck for that.
Now, without any further ado, let's understand introduction to machine learning. Machine learning is the process of feeding a machine enough data to train and predict a possible outcome using the algorithms at bay. The more the data is fed to the machine, the more efficient the machine will become. So let us try to understand this with a real-life example. So I'm sure most of you are aware of the predictions made in any sport before any major match. So in this case, I'm going to tell you about a football penalty session. So let's say the data of previous performances are considered. So let's say the goalkeeper has saved all the penalties to his right in the last 50 penalties that he has saved. So this data will be crucial to predicting if he will or will not save the next penalty that he faces. Of course, there are other factors to consider as well. Another example is the suggestions that we get while surfing the internet. The data of our previous choices are processed to give us the most favorable content we are most likely to watch. Anyhow, machine learning is not just feeding the machine an ample amount of data. Uh, there goes a lot of processes, algorithms, and decisive factors to get the optimum results.
So in this session, we will go through one such support vector machine algorithm to understand how it works with Python. Before that, let us also take a look at the types of machine learning. So there are three types of machine learning that is supervised learning, unsupervised learning, and reinforcement learning. Supervised learning is contained in a controlled way to oversee the outcome accordingly. It is, as the name suggests, supervised in a way that the machine learns what the user wants it to learn. Coming to unsupervised learning, the machine simply explores the data given to it. The data is sometimes unlabeled and uncategorized, and the machine makes the possible references and predictions without any supervision. And talking about reinforcement learning, it basically means to enforce a pattern of behavior. The machine needs to establish a systematic pattern of approach and reinforcement learning. So these are the types of machine learning that we have.
Let's move on to the next topic that is what is the support vector machine. So what exactly is the support vector machine? A support vector machine, or we can call it as SVM, was first introduced in the 1960s and later improvised in the 1990s. An SVM is a supervised learning machine learning classification algorithm that has become extremely popular nowadays owing to its extremely efficient results. So an SVM is implemented in a slightly different way than other machine learning algorithms. It is capable of performing classification, regression, and outlier detection as well. A support vector machine is a discriminative classifier that is formally designed by a separating hyperplane. It is a representation of examples as points in space that are mapped so that the points of different categories are separated by a gap as wide as possible. In addition to this, an SVM can also perform nonlinear classification.
So I'm going to tell you a few advantages and disadvantages of SVM or support vector machine. So talking about the advantages of SVM, it is effective in high dimensional spaces and it is still effective in cases where the number of dimensions is greater than the number of samples. One more advantage is it uses a subset of training points in the decision function that makes it memory efficient. And the last advantage is different kernel functions can be specified for the decision function, which also makes it versatile. Coming on to the disadvantages, if the number of the features is much larger than the number of samples, we have to avoid overfitting in choosing the kernel functions, and the regularization term is actually crucial. The next disadvantage is SVMs do not directly provide probability estimates. These are calculated using five-fold cross-validation. So these are the advantages and disadvantages of SVM.
Now let's take a look at the next topic, which is how does an SVM work. So the main objective of a support vector machine is to segregate the given data in the best possible way. So when the segregation is done, the distance between the nearest points is known as the margin. The approach is to select a hyperplane with the maximum possible margin between the support vectors in the given data set. Now, to select the maximum hyperplane in the given sets, the support vector machine follows the following. It generates a hyperplane which segregates the classes in the best possible way and then it selects the right hyperplane with the maximum segregation from either nearest data points.
Now let me tell you how we can deal with inseparable and nonlinear planes as well. So in some cases, hyperplanes cannot be very efficient, and in those cases, the support vector machine uses a kernel trick to transform the input into a higher dimensional space. So with this, it becomes easier to segregate the points.
Now let us talk about the SVM kernels. So an SVM kernel is basically used to add more dimensions to a lower dimensional space to make it easier to segregate the data. It converts the inseparable problem to a separable problem by adding more dimensions using the kernel trick. A support vector machine or SVM is always implemented in practice by a kernel. The kernel trick helps to make a more accurate classifier. Let me talk about the different types of kernels that we have in support vector machine. First of all, we have linear kernel. A linear kernel can be used as a normal dot product between any two given observations. So the product between the two vectors is the sum of the multiplication of each pair of input values. And then we have the polynomial kernel. So it is rather a generalized form of the linear kernel. It can distinguish curved and nonlinear input space as well. Now talking about the next kernel that we have, that is a radial basis function kernel. So the radial basis function or RBF kernel is commonly used in SVM classification. It can map the space in infinite dimensions. That is the advantage that we have with RBF kernel. So these are the kernels that we have in SVM, guys.
Now I'm going to talk about a few support vector to machine use cases. So these are a few use cases I have listed down here. We can use SVM for which is face detection. We can use SVM for face detection. Then we can use it for text and hypertext categorization. Then we can use it for classification of images. We can use it for bioinformatics, protein fold and remote homology detection. We can use it for handwriting recognition and we can use it for generalized predictive control as well. So these are the use cases that we can use SVM for.
Now that we are done with the SVM use cases, let me tell you how we can implement SVM. So there are a few steps that we have to follow to implement support vector machine in machine learning. In any machine learning model, we have to follow specific steps. So first of all, we have to load the data in which we are going to perform the classification of data. After that, we are going to explore the data. Now we're going to see how many labels, what are the target variables there, and then we're going to split the data into train and test data, and then we'll generate the model by implementing the support vector machine, and after that, we are going to evaluate the model as well. So I'm going to show you this when we are going to work on the PyCharm. After this, I'm going to show you a simple use case, which is a character recognition using support vector machine. So in this, we're going to get an image recognition something like this. So let's take it up to PyCharm, guys, and I'll show you how you can implement support vector machine in Python.
So we are in PyCharm, guys. Let me go to the presentation mode for better visibility. So first of all, you have to make sure you have all these libraries installed in your system, that is scikit-learn, scikit-learn, and then you have to import all these modules like SVM, metrics, train_test_split from model_selection and datasets to load the data. So first of all, we have that cancer data over here, in which we are going to load the data set, which is the breast cancer data set. So this is a simple example, or this is the way we can load the data. I'll just write a comment over here. Loading the data. This is where we are going to load the data. After this, we can check for what all are there inside the data, like how many images or what all is there. So I'm just going to remove all this for now. And let's see what we have in the data. So I'm just going to print cancer data. Let's see what we have. It's going to take a while because it's loading the data set from the internet. You can see these all points are there inside the data set. So we have data. Then we have target also over here, which is an NP array. This is the data that we have over here. Now I'll just check the shape of the data as well. Let's see the target variable also. So this is our target variable, guys, which is in a binomial form, which is 0 and 1. So we have only two possibilities over here.
Now that we are done exploring the data also, let me show you how you can start splitting the data into test and train sets. So I'll just remove this again and print the previous code. So this is my training and testing variables, that is X_train, X_test. Then we have Y_train, Y_test, and inside this, I have used train_test_split to train my data over here. And as you can see, I'm using the data as cancer data. Then we have the target variable. We are using the test size at 0.4. We can use it at 0.3 as well. We'll be using like 30% for the testing and the one for training. And we have random_state as 209. After this, I am going to generate the model, that is my classifier, using the SVM over here, and I'm going to use the SVC with the kernel linear. We are using the linear kernel over here. So I'll just write generating the model. After this, we are going to train the model. For training the model, we are going to use the fit method over here. Inside this, we are passing the X_train method and Y_train method to train the data. So basically, fit method is used to train the data. After this, we are going to predict the response using the predict method over here. Inside this, we're going to pass the variable, that is X_test. So we have taken the X_test separately for this purpose only, to predict the outcome over here. So now that we are done with the training and testing over here, we are going to print the accuracy. I'll just write one more C over here to make it correct. For this, we are using the metrics from the scikit-learn module, which is going to give us the accuracy score. In this, we have Y_test and Y_prediction. So it's going to give us the accuracy report. After this, we are going to get precision score using the precision_score method in metrics. And then we have recall as well. And to make it easier, I'm also using a classification report. So let's just run this program, guys, and we'll see what happens. We'll get the total report, how efficient our model is actually. So as you can see, we have the accuracy as 0.92, which is close to 92%. And then we have precision of 93% and recall as 94%. And after that, we have a classification report. Inside this, we have also given the accuracy, the F1 score, and the recall as well. So this is a simple example to implement your support vector machine, guys.
Now, moving on, let me show you one more example. Before that, let me just exit the presentation mode. Now I'm going to show you a use case for character recognition using support vector machine. So for this also, we are using one more library that is matplotlib to plot the graph or to plot the image as well. So we have datasets which we are going to import from scikit-learn only. We have SVM and metrics, which we are not going to use in this one for accuracy, which I'll tell you later, guys. So first of all, we are loading the datasets over here. This is the load digits. So we have a dataset with a bunch of digits, the handwritten digits, and now we are generating the model directly. After this, we have X and Y variables, which is, you know, going to take the test data and the target data as well. So we are taking all the data until minus 10 over here for training and for target variable, we're also taking the same number. And after this, we're going to fit the model to train it, and we are predicting the models with the digit data that is over here. After this, I'm just going to show an image or instead of nine, we can use any other number. We have taken interpolation that is nearest, and I'm using the imshow method from the matplotlib. And if you are not familiar with matplotlib or how you can plot graphs, you can check out other tutorials on Udemy on matplotlib to get better insight about this. So let's just run this program, guys. Let me exit the presentation mode, and we are getting a figure something like this. So it looks like 9, but it's quite distorted right now because we have taken the gamma as 0.01 and the C value is 100. So if I increase or decrease this value, the output is going to change. The accuracy is going to be much larger, but the speed is going to become a little slower. So this is the reason. And instead of 9, I can just write, let's say, six over here. So the prediction is actually right, but the image is a little distorted because the accuracy is not quite accurate the way how we want it. But this is a very simple example to make a character recognition model using support vector machine. Also, to increase the accuracy, we can change the gamma values or C values in the SVC parameter, but it will hinder the speed too. So if we increase the gamma values, the accuracy will decrease, but the speed will increase, and vice versa. Okay.
So now let's understand what is a random forest. So random forest is constructed by using multiple decision trees, and the final decision is obtained by majority votes of these decision trees. So let me make things very simple for you by taking an example. Now, suppose we have got three independent decision trees. Here, we are just taking three decision trees, and I have got an unknown fruit, and I want that these trees would give me a result of what exactly this fruit is. So I pass this fruit to the first decision tree, the second decision tree, and the third decision tree. Now, a random forest is nothing but a combination of these decision trees. So the results are been fed into the random forest algorithm. So what it sees is that, okay, the first decision tree classifies it as peach. The second decision tree says that it is an apple, and the third one says that it is a peach. So random forest classifier says that, okay, I've got the result as two peaches and one for an apple. So I would say that the unknown fruit is a peach. All right. So this is based on the majority voting of the decision trees, and that is how a random forest classifier comes to a decision of predicting the unknown value. Okay. Okay. So this was a classification problem. So it took the majority vote. Now, suppose if it was a regression problem, it would have taken the mean of it. Okay.
So now let's move on further to understanding what is a decision tree. But before that, we should understand that random forest, the building blocks are decision trees, and that's why studying decision tree becomes important because if we understand one decision tree, we can apply the same concept to random forest. Okay. So let us understand what is a decision tree. So decision tree has basically three nodes. They're important. The first one is the root node. The root node, as the name suggests, here the data set, the entire data set is fed at the root node. And then there are decision nodes where decisions are being taken and splitting is performed. And then we've got the leaf node. And these leaf nodes are the ending point of the tree where no further division takes place. And we can say that the predictions are made at the leaf nodes. Okay. Now, another thing to note here is that decision nodes provide links to the leaf nodes, and decision tree breaks the data set into smaller subsets. Splitting is done at nodes, and at the end of the tree, the final point, the decision or the prediction is made.
Now let's construct a decision tree and take an example of the penguin's classification. So let me just walk you through what is this penguin classification problem. So we've got some three species of penguin. Let us get familiar with these penguin species. This is Gentoo, this is Adelie, and this is Chinstrap species. So these are the penguin species of Antarctica, and they are found on different islands. So we have to classify these penguin species correctly. So we are using random forest here. But for convenience's sake, let us just work with a decision tree right now, and we will see how a decision tree really classifies these species. So that's really interesting, and let's move on forward and understand some parts of this penguin because we'll be working with this data set. Okay. So this is a penguin, and this is the head, bill, flipper, belly, and claws. The different body parts of the penguin. So we are majorly concerned with the bill and flipper and the body mass of the penguin because our data set contains majorly of these features. So make sure that you understand the flippers and also the bill of the penguin. Okay.
So now moving on forward, and let us now construct a decision tree. So let's see how a decision tree is constructed. So I have taken a subset of the penguin's data. And here we see only two columns, that is, um, island and body mass, and of course, the species of the penguin, that is the outcome or the target variable that we have in this subset. So now we construct a decision tree here, and we take body mass as the first feature, and the splitting is done based on one condition that if the body mass is greater than or equal to 3500, and if it is yes, then based on island, the another feature, we will classify or get the leaf node as either Torresen or Bisco Island. So if the island is either Torresen, then the species would be Adelie, and if the island is Bisco, then the species would be Gentoo. So after Torresen and Bisco, no further division takes place because we are getting the predictions at these leaf nodes. Whereas if the body mass is less than or equal to 3500, and it says that yes, the body mass is less than 3500. So we get the species as Chinstrap, and so no further decision had to be made at this node, and that's why it was been ended here. So this was a very simple basic example of a decision tree. And suppose if we had got a huge data set, this decision tree would have gone into a huge depth, and the depth of the decision tree would have led to overfitting of the data. So that is one of the drawbacks of decision trees that random forest overcomes.
So now let's move on forward and understand the important terms in random forest, and this will also help us to consolidate whatever we have learned so far. So we have taken the same small decision tree of the previous example, and let's understand these are also the important terms which will be relevant to random forest also. So the first is the root node. Now, here what happens is that the entire training data has been fed to the root node. And then we've got here that each node will ask either true or false question with respect to one of the feature, and then in response to that question, it will partition the data set into different subsets. That's what it is doing here, based on the condition that if the mass body mass is greater than or equal to 3500, it asks a question either yes or no, and based on that, again, a partition is done. And if not, then it just classifies the species. And then again, what happens is that the splitting. Now, this is very important here. The splitting takes place either with the help of a Gini or entropy methods, and these help to decide the optimal split, and we will be discussing about splitting methods very soon, right? Okay. And then we've got the decision nodes, which provide the link to the leaf nodes, and these are really important because then only the leaf nodes would tell us what actually the real predictions or to which class does the species belong. So now coming to the leaf node, and these are the endpoints where no further division will take place, and we will obtain our predictions. Okay.
So now coming up to another important thing here is the working of random forest. So now for working of random forest, we will have to understand a few important concepts like random sampling with replacement, feature selection, and also the ensemble technique which is used in random forest, and that is bootstrap aggregation, which is also known as bagging. So we will understand this with the help of an example which will be very simple, and then we will go on understanding how feature selection is done in both the classification and the regression problem. Actually, how random forest selects features for the construction of decision trees. Well, in random forest, the best split is chosen based on Gini impurity or information gain methods. So this also we will understand. Now let us first understand random sampling with replacement. Now, what happens here is that we've got a small subset of the same penguin data set wherein we've got some six rows and four features, that means four columns, and the arrows that you can see is that now we will be creating three subsets from this small subset, right? And these three subsets will become our decision trees, and then we'll be constructing decision trees from these subsets. So let us create our first subset, and you can see here that the subset is randomly being created, and for convenience's sake, let me just also show you the different subsets here. Okay. So now for better understanding, let us understand this that in the first subset, if we focus, we've got certain random rows here, and we've got certain features, but we do not know how this feature has been selected. We got island and we got body mass, but in the second subset, we got island and flipper length, and in the third subset, we got body mass and flipper length, right? Now let's look at the rows. Now, when I am talking about these features, I will say this is feature selection, and remember this term. Now coming to the second concept, that is random sampling. Now, random sampling is nothing but selecting randomly from your subset. So I'm selecting randomly certain rows from my subset and creating further subsets. Okay. So what is replacement here? Replacement is can be seen here and can be understood with this second subset. We see here that the Gentoo species, this row is being repeated again, and this is replacement. That means that when we are working with repeated rows, and this row can be repeated again in the second or the third subset, then this is random sampling with replacement. That means my random forest can use a row multiple times in multiple decision trees, right? So this is the basic concept of random sampling with replacement and feature selection in random forest. Another important term which I would like to bring into the notice is that when we are working with these type of small subsets, these are also known as bootstrap data sets, and when we aggregate the results of all these data sets, it becomes bootstrap aggregation. So just filling in the gaps so that later on the concepts become more clear.
So now let's move on to drawing decision trees of these subsets. Okay. So let's draw the decision tree of the first subset. Again, we are taking body mass as the first root node, and then based on a decision like if the mass is greater than or equal to 3500, then take a decision either yes or no. If it is no, then the species is Chinstrap, and if it is yes, then again, you partition based on island, and if it is Torresen, then it is Adelie, and if it is Bisco, then it is Gentoo species. Okay. So this is how we will construct two more decision trees of the remaining subsets. So on the second subset, let us just again create a decision tree, and here now we are taking flipper length, and then based on a condition that if the flipper length is greater than or equal to 190, then make a split. If it is yes, then the species becomes Gentoo, and if it is no, that means again, make a decision based on island, and if it is Torresen, it is Adelie, and if the island is Dream Island, then it is a Chinstrap species. So this is how the decision tree of the second subset has been created, and this is how it will take decisions, right, based on the tree length, depth, and also the features it is selecting. Okay. So now let's create the third decision tree of the third subset. And we get a decision tree something like this, where in body mass, if it is greater than 4000, and if it is yes, then clearly it is a Gentoo species. And if it is no, then again, make a partition with the with respect to flipper length, another feature here. And then if it is again greater than or equal to 190, then the species would be Adelie, else it would be Chinstrap. So this is how decision tree three will make a decision. Now let's just keep these decision trees with us. Okay. And we will make sense of these trees just in a while. Okay.
But before that, let us understand how feature selection is done in a random forest. How am I selecting the columns? So for classification, by default, the feature selection is taken as the square root of the total number of all the features. Now, suppose I've got here four features. So it is a classification problem. I will take the square root of these four features, which becomes two. So decision trees would be constructed based on two features each. If suppose I had 16 features, then it would be square root of 16, that would be four. So four features would be taken in each decision tree. All right. And suppose if this would have been a regression problem, then by default, what would happen? The features would be selected by taking the total number of features and dividing them by three. Okay. So this is how by default the feature selection is being done by a random forest. Okay.
Now let us move on forward to consolidating our learning. So now we are coming to ensemble techniques, that is also known as bootstrap aggregation. Random forest uses ensemble techniques. And what is ensembling? It just means that you are aggregating the result of the decision trees and taking the majority vote in case of classification and the mean in case of regression problems and giving the output. Okay. So now we have again plotted all our decision trees here, and below we can see that there's an unknown data, and I want to predict the species of these data. So what will happen is that again, let us just feed this problem to each of the decision tree, and let's see what each decision tree makes the prediction. So I just feed this unknown data to decision tree one, and it says that, okay, the species seems to be Chinstrap. Okay. And then decision tree two says that, based on the data it has been found that this species is Adelie. And then decision tree three says that, no, I, with my decision tree, this species is Chinstrap. Okay. Now all these data has been fed to the random forest classifier, and it says that, okay, for Chinstrap, I've got two votes. For Adelie, it's got one vote. So the new species would be Chinstrap, right? So this is how the bootstrap aggregation is done based on the majority voting, and the decisions taken by different decision trees, they have been combined together, aggregated, and we get an assembled result in the random forest. Okay. So this was a very simple concept of ensemble techniques which has been used in random forest. Okay.
So now let's move on forward to splitting methods. So what are the splitting methods that we use in random forest? So splitting methods are many, like Gini impurity, information gain, or chi-square. So let's discuss about Gini impurity. So Gini impurity is nothing but it is used to predict the likelihood that a randomly selected example would be incorrectly classified by a specific node, and it is called impurity metric because it shows how the model differs from a pure division, right? And another interesting fact about Gini impurity is that the impurity ranges from 0 to 1, with zero indicating that all of the elements belong to a single class, and one indicates that only one class exists. Now, a value which is like 0.5. This indicates that the elements, they are uniformly distributed across some classes, right?
Now moving on forward to information gain. Now, this is another splitting method which random forest can use, and information gain utilizes entropy. So entropy is nothing but it is a measure of uncertainty. So information gain, let's talk about that first. So the features, they are selected that provides most of the information about a class, right? And this utilizes the entropy concept. So let's see what is entropy. This is a measure of randomness or uncertainty in the data, right? So we will understand this entropy with the help of a small example. So don't worry about it. So let's understand this entropy. Now, suppose there's a fruit tray with four different fruits, right? And, uh, what do you feel about the entropy here? That means the randomness of the data. Is it really easy to classify these fruits into the respective class? So this becomes really uncertain, and the data looks messy here. But what if we just split here these into two trays, wherein the first tray would have peaches and oranges, and in the second tray, we'll have apples and lemons. So now this becomes a little more certain. We get low randomness here, and this is called as low entropy. So when we move down from the tree, that means from root node to the leaf nodes, the entropy reduces, and we can also calculate information gain from this entropy, that is the difference in entropy before and after the split, that is known as information gain. Okay. So once we move down the tree and start reducing the randomness from the data, the entropy becomes lower, and that is what we want in our data. If there's low entropy, that means we are likely that the predictions would be more accurate, and we can make predictions very easily as compared to very messy data which has high entropy. Okay. So that was about entropy.
So now let's move on to understanding the advantages of random forest. And we see here various advantages. So let's focus on firstly low variance. Now, since random forest overcomes the limitations of decision tree, and it also has the advantage of low variance because it combines the result of multiple decision trees, and each decision tree is been trained on a limited data set that we have seen earlier also. So each tree was making its own subset of data and training the data on that limited length of the tree. So there's less step, there's less overfitting, and low variance of the data. So coming to the next point, that is reduced overfitting. Again, since we were working with multiple decision trees, hence reduced depth of the tree. So we get reduced overfitting, that means the model is fitted well, and it does not try to learn even the noises, right? So, uh, we use the bootstrap aggregation or bagging here in random forest, and that is why we also get reduced overfitting in random forest, and this is one of the reasons that why is it so popular because you don't have to worry about overfitting of the data, right? All right. Now moving on forward to the another advantage is that the normalization is not required in random forest because it works on a rule-based approach, right? And, uh, another advantage is that it gives really good accuracy, which we will also also see hands-on. It really gives a very nice predictions, either precision or recall, and generalizes well on unseen data as compared to other classifiers or machine learning classifiers which are present, like Naive Bayes or SVM or KNN. Random forest really outperforms other classifiers, right? So let's move on to understanding a few more advantages of random forest. It is suitable for both classification and regression problems, and also it works well with both categorical and continuous data. So you can use it well with any of the data sets, right? And it performs well on large data sets, right? So it solves most of the problems, that's why random forest is largely been used in machine learning problems.
Now moving on forward to certain disadvantages of random forest. So the first disadvantage is that it requires more training time because of the multiple decision trees. If you've got a huge data set, you would be constructing hundreds and hundreds of decision trees, and that requires a lot of training time. And here comes one more disadvantage, is that the interpretation becomes really complex when you've got multiple decision trees. So decision tree interpretation is easy because it is an individual decision tree. But when you combine hundreds of decision trees to form a random forest, the interpretation is really very difficult to understand, and it becomes quite complex to apprehend what exactly the model is trying to predict, and where the splitting occurs, and what features are being selected, and so on. And another disadvantage is that it requires more memory. So memory utilization is really heavy in case of random forest because we are working with multiple decision trees. And another disadvantage is that it is computationally expensive and requires a lot of resources because of the training of multiple decision trees and also storing them. All right. So this was all about random forest, the theory part of it, and now let us just move on to the practical demonstration or a hands-on on random forest. Okay.
So now it's time for a hands-on on random forest. So let us just import a few basic libraries of Python in our Jupyter notebook, and we will run this. We will import pandas as pd, numpy as np, and seaborn as sns. Now, seaborn is needed here because we want to load a data set that is a penguins data set with the help of seaborn, and this is already been preloaded in seaborn. This is already a loaded data set, and seaborn has got multiple data sets, you know, for practice for beginners. So it is a good way to practice for data sets. Now we can see this asterisk sign, that means it is telling us to wait. So let us just let it get loaded. So we got our data in an object called df, and we can see the first five entries here, and this data frame is shown in the form of a table, rows and columns, and we see here some species, island, bill length, bill depth, flipper length, body mass, and the sex of the penguin. So our task is to specify or to classify these species of penguins into the respective correct species, right? So we see the shape of our data, and we see that it is like 344 rows and seven columns, and we will see the info. So we see df.info, and this gives us, along with the non-null count, we also get the data type of the values. So we have got species, island as the object data type, whereas the bill length, bill depth, flipper length, and body mass are in floating point or you can say floating data type, and the sex is in object data type, right? So now moving on forward to calculating how many null values are there with the help of df.isnull().sum(). So we get certain like sum around two null values in all these columns, as you can say the features like bill length, bill depth, flipper length, and body mass, whereas there are 11 null values in sex feature, right? So what we do is, since they are very small null values, we can just drop it, or you can also ignore them. So here in this data frame, what I'm doing is, I'm just dropping these null values, and let us just check whether they have been dropped or not with the help of again the same function, .isnull().sum(), and then we see that yes, they have been dropped from our data frame.
Now let us do some feature engineering with our data. Now we have seen that we have got some object data type in our data frame, and before feeding it into the algorithm, that is random forest, we have to transform the categorical data or the object data type into numeric. So we are using here one-hot encoding to convert the categorical data into numeric. Now, there are various ways in Python which we can do that, like one-hot encoding, or you can also use mapping function in Python, but here we are using one-hot encoding. So let us just do that, and we find here, first of all, let us apply it on the sex column, and here we see that we have got two unique values in sex, that is male and female, and we use pandas here to get dummies, that is how we will apply this one-hot encoding, because this is how get_dummies work. So what happens is here is that the new unique values are converted into the respective columns in the data frame. So we see here we have got two unique values, males and female, and they are being converted into the columns. Okay. So one thing to note here is that we also get a problem of dummy trap, because here we see only two unique values. Now, suppose if I had six or seven unique values, and I do this one-hot encoding, I would have lots of features in my data frame, and that would lead to several complexities. So what I do is, uh, to keep things simple, I can use one-hot encoding when my data frame or my unique counts are low, when my unique values are less. So since I had just two or three, I can use it. So I'm using here. So what I do is, again, now one row, one column, as we can see here, that it is redundant, giving me extra information. So I will just drop it. So I drop this first column, and what I get in this data frame is only male. So let us just infer whether I can also infer females from this or not. So if the value is one, that means the penguin is a male, and if the value is zero, that means the penguin is a female. Okay. So only one column is needed for this data frame. So I just kept one and dropped the another one. Okay. Now apply again one-hot encoding to the island feature. So in island, if we check the unique values, we've got three unique values.
here. Toggressen, bisco and dream island. And the object is the data type, right? So again, we will use pandas pd.get_dummies and we will use apply it on the feature island. And let's get the head of it. So we get here again, the unique values were converted into columns and we get here respective three columns. And then again, we will just drop the first column to get the remaining two columns. So here also, we can infer that if the island is Torresen, if it is one, then it is not Dream, neither Bisco, right? So this is how you can read it from the data frame and understand that.
Now, remember this thing that these two, island and here, sex, these are two independent data frames. These are not yet included in the main data frame. So what we will do now is we will concatenate the above two data frames into the original data frame. So what we do, we again create a new data frame that is new_data. And let us just concat with the help of pd.concat function and we will concat what df_island and sex, and axis is one, that means in the column. Okay, so when we will run this, let's see the head of it. So everything gets concatenated in a single data frame, which is good for the feeding this data into or splitting the data into test and train data.
So now we have this new data frame and we've got some repeated columns here which needs to be deleted. So what we do is we will delete sex and island here, which are just repeating because we've got here male and we have also got here dream and toggress. So we do not require this island column, neither the sex. So we just drop it with the help of new_data.drop and the column names, axis is one, inplace equals to true. Right? And let's see the head of this data frame. Head of the data frame gives me five unique values. Right?
And now it is time to create a separate target variable. And what we'll do is we will store in a variable called y, only species. So what we do is from this new_data.species, we will just store the species in this y. And we see this y.head, that is the first five species and we got the values here. That means another target variable is been created now. So and you can also see the y.unique values as adily, chinstrap and genu. So now we see here three unique values of the penguin, that is chinstripe, adily and genu. And the data type is object here. So again, we need to convert this object into the numeric data type. So now what we are doing is we are using the map function in Python. And what we do is we map adily to zero, chinstrap to one and genu to two. So this is how we see that all the values have been mapped to numeric. This is another way to convert a categorical value into a numeric value in Python.
Now what we do is, let us just drop the target value species from our main data frame. So we'll just drop it and let's see our new data frame. So we see that we don't have any target species here. Right? Okay. So in X, let's store this new data and perform the splitting of the data. So what we do is from sklearn.model_selection, we will import our train_test_split and we will split our training data into 70% and 30%. So test data becomes 30% and training data is some 70%. And this random_state is zero, which means that I'm not fixing any random state and this is also used for the code reproducibility. Now suppose if I again run this code, I will get the same result. It will not change. You can set this random_state to any of the random number as per your choice and the result would differ. Okay.
So now, let us print the shape of X_train, Y_train, X_test and Y_test. So we see here that it has been splitted into 70 and 30% and we get X_train has 233 values here and seven features and X_test has 100 values and seven features. Similarly, Y_train you can see 233 values and Y_test has 100 values, that means the species. Okay. So that has been perfectly splitted into 70 and 30%.
Now what we do is, we will train the random forest classifier on the training set. How do we do it? We will import the RandomForestClassifier from sklearn.ensemble. So we've already dealt with what is ensemble and then in classifier, we will store this random forest and this n_estimators is nothing but decision trees. So we are creating some five decision trees here and the criteria is entropy and again, random_state is set to zero. So let's see and then we will fit this X_train and Y_train. So this has been fitted and the criteria is entropy here. All right.
So now, let's make some predictions and let's create a variable called y_predict and we will just predict it on X_test and we have also printed this y_prediction. And now let's print the confusion matrix to check the accuracy of random forest algorithm. And what we do is from metrics, sklearn.metrics, we will import classification_report and confusion_matrix and also the accuracy_score. So we will just import them and then in cm variable, we will print the confusion matrix of Y_test and y_predictions. So we will print it and we see here the accuracy score also, which is 98%. So our random forest classifier is giving us a very good accuracy of 98%. And you can see a confusion matrix that only two cases have been misclassified. Rest all the cases have been correctly classified by random forest classifier. Okay.
So now, let's move on to printing the classification report of Y_test and y_prediction. Let's see and we get the precision as 96%. That means the two predictions by the algorithm is 96%. The recall or the true prediction rate is 100%, which is very nice and F1 score is also good, which is 98%. So this is giving us a good result. But what if if we change the criteria from entropy to gini? So let's just experiment with that too. So let's try this with the different number of trees and change the criteria to gini coefficient.
So now again, from sklearn.ensemble, we will import RandomForestClassifier and fit it. Okay. And here what we are doing is just we are using seven trees. Previously we used five and now in the criteria, we will use gini coefficient and random_state is zero. So let's run this and see whether there's a change in accuracy or not. And let's predict this and let's check the accuracy score. What is the accuracy score for this random forest classifier with seven trees? So we get 99% accuracy with changing the criteria and changing the number of trees. So you can just experiment with different number of trees and different number of decision trees. Let's just experiment with, you know, 12 decision trees and see what happens. So you can see the accuracy reduced to 98%. Okay, with seven, we were getting 99. So let's just keep seven because it is giving us really good accuracy. So this is about random forest classifier and how it works with several trees and different criteria to give us very good accuracy on our training and test data.
From speech recognition and face recognition to healthcare and marketing, neural networks have been used in a varied set of domains. Hi all, I'm Zelka from Edureka and I welcome you to this session on what is a neural network. An artificial neural network is the functional unit of deep learning. Deep learning uses artificial neural networks which mimic the behavior of the human brain to solve complex data-driven problems. Now, deep learning in itself is a part of machine learning, which falls under the larger umbrella of artificial intelligence. Artificial intelligence, machine learning and deep learning are interconnected fields where machine learning and deep learning aids artificial intelligence by providing a set of algorithms and neural networks to solve data-driven problems. Deep learning makes use of artificial neural networks that behave similar to the neural networks in our brain.
A neural network functions when some input data is fed to it. Now, this data is then processed via layers of perceptrons to produce a desired output. So let's understand neural networks with a small example. Now, consider a scenario where you have been given a set of labeled images and you have to classify them into two classes. One class containing images of non-diseased leaves and the other class containing images of diseased leaves. So how would you create a neural network that classifies the leaves into diseased and non-diseased crops? Now, the process always begins with processing and transforming the input in such a way that it can be easily processed. In our case, each leaf image will be broken down into pixels depending on the dimension of the image. For example, if the image is composed of 30x30 pixels, then the total number of pixels will be 900. Now, these pixels are represented as matrices, which are then fed into the input layer of the neural network.
Just like how our brains have neurons that help in building and connecting thoughts, an artificial neural network has perceptrons that accept inputs and process them by passing them on from the input layer to the hidden and finally the output layer. Now, as the input is passed from the input layer to the hidden layer, an initial random weight is assigned to each input. The inputs are then multiplied with their corresponding weights and their sum is further processed through the network. Now, here what you do is you assign a numerical value called bias to each perceptron. Furthermore, each perceptron is passed through activation or something known as the transformation function that determines whether a particular perceptron gets activated or not. An activated perceptron is used to transmit data to the next layer. In this manner, the data is propagated forward through the neural network until the perceptrons reach the output layer. At the output layer, a probability is derived which decides whether the data belongs to class A or class B.
Now, let's assume a case where the predicted output is wrong. In such a situation, we train the neural network by using the back propagation method. Initially, while designing the neural network, we initialize weights to each input with some random values. Now, these weights denote the importance of each input variable. Therefore, if we propagate backward in a neural network and compare the actual output to the predicted output, we can readjust the weights of each input in such a way that the error is minimized. This results in a more accurate output. And this is exactly what back propagation means.
Now, let's discuss a few real-world applications of neural networks. With the help of deep learning techniques, Google can instantly translate between more than 100 different human languages. Visual translation is an interesting application of deep learning. It can be used to identify images that have letters. Now, once you identify them, they can be turned into text, translated, and then the images are recreated with a translated text. In fact, Google has an app for this purpose. It's called the Google Translate app. Let's not forget to mention automated self-driving cars. Deep learning has played a huge role in the field of self-driving cars. From Tesla to Google-owned Waymo, self-driving cars are being perfected with the help of neural networks. Then, of course, we have the virtual assistants like Siri, Alexa, Cortana, that can literally read your mind. These assistants are purely based on technologies including deep learning, machine learning, and natural language processing. Apart from this, deep learning has also made its way into the gaming industry. So, all you Dota fans out there might have already heard of the famous OpenAI 5, which is the first AI to beat the world champions in an esports game after defeating the reigning Dota 2 world champions. Post the victory, Bill Gates tweeted, quote, AI bots just beat humans at the video game Dota 2. That's a big deal because their victory required teamwork and collaboration, a huge milestone in advancing artificial intelligence.
Now guys, the applications of deep learning are not restricted to just games and machine translation. In fact, deep learning has found its way into the creative arts and music field. An AI-based system called MuseNet can now compose classical music that echoes the classical legends like Bach and Mozart. MuseNet is a deep neural network that is capable of generating 4-minute musical compositions with 10 different instruments and can combine styles from country to Mozart and to the Beatles. Another creative product of artificial intelligence is a content automation tool called Wordsmith. Wordsmith is a natural language generation platform that can transform your data into insightful narratives. Tech giants such as Yahoo, Microsoft, Tableau are using Wordsmith to generate around 1.5 billion pieces of content every day. I can go on and on about the applications of deep learning. In the long term, we're hoping to see the use of advanced AI techniques like deep learning for the betterment of humanity. Irrespective of the threat AI is supposedly going to pose on humans, I'm a firm believer that AI will only benefit us in the long run.
So moving on, let's talk about what exactly is a neural network. So, neural network is a simple concept to understand, guys. I mean, if you look at the tutorials that you find online or on YouTube or internet, you will find a simple analogy regarding how the neurons are connected to the brain and it often confuses the beginners who does not know anything about the neural networks because it's not necessarily something that everybody is aware of. So, simply, I'm going to tell you to focus on the mathematical aspect of it. So let's say we have some input values. There is a large connected network which is passing that input value through layers and there is some computation happening and we are getting the desired output. So let's think of it as a network where everything is connected and there are layers and neurons. We'll call them neurons for now and, you know, the data points inside each layer and there are functions, activation functions, you know, computations happening, summation functions. At the end of the day, you're getting the desired output from that particular network. In hindsight, there is input going inside the network and you're getting an output. So it is as simple as that. And now, in the figure also, you can see that there is a network where nodes are interconnected and there are several layers which basically roughly depicts a neural network similar to a brain as well. So that's why most of the people would talk about this analogy.
Now, the importance of neural network in deep learning is quite immense, guys. It basically narrows down the human intervention to a bare minimum and is pretty efficient with multi-dimensional data and we'll talk about that later on. Speeding up the processor with high efficiency is just one of the advantages of the artificial neural networks. And also, I forgot to tell you guys, neural networks are also known as artificial neural networks.
Now, let's try to understand neural networks with a perspective of how it is advantageous and why do we actually need deep learning and neural networks. So, machine learning was a major breakthrough in the technical world. It led to the automation of monotonous and time-consuming tasks. It helped in solving complex problems and making smarter decisions. However, there were few drawbacks in machine learning that led to the emergence of deep learning. So, there are a few pointers that I want to discuss. First is, it was unable to process high-dimensional data. So, this was I was talking about. So, machine learning can process only small dimensions of data that contain a small set of variables. And if you want to analyze data containing hundreds of variables, then machine learning cannot be used. There is one shortcoming that deep learning is actually fulfilling. And next one is, feature engineering is manual in machine learning. So, consider a use case where you have 100 predictor variables and you need to narrow down only the significant ones. To do this, you have to manually study the relationship between each of the variables and figure out which ones are important in predicting the output. And this task is extremely tedious and time-consuming for anyone, including a developer. The next point is, it was not ideal for performing object detection and image processing. So, since object detection requires high-dimensional data being, you know, the images and frames that you have and, you know, all the cascade classifiers, the XML files, these are all high-dimensional data and machine learning cannot be used to process the image data sets. It is only ideal for data sets with a restricted number of features. That's where deep learning comes into the picture.
Fraud risk manager and a data scientist at PayPal, Kwong quoted at one time like, what we enjoy from more modern advanced machine learning is its ability to consume a lot more data and the layers and layers of abstraction and be able to see things that a simpler technology would not be able to see. Even human beings might not be able to see. So, clearly, a simple linear model is capable of consuming around 20 variables. Let's say. However, with deep learning technology, one can run thousands of data points. So that is exactly what we need with deep learning and neural networks is the center point of that.
Now, neural networks as a whole is a complicated concept to understand. We can simplify it like there is an input data going inside a bunch of layers. There is a computation, the calculations happening using some functions, and we are getting the output. But it is not as simple as that. To understand how it works, we'll have to take a look at the components. You know, basically the layers, how many layers are there, what kind of activation functions we are using, and how are we, you know, updating the weights and what are the bias and everything. So, these are the components that we have to talk about. So, the key components that build a neural network includes an input layer. There'll be hidden layers, a bunch of hidden layers where the computations or the calculations will be happening. There is an output layer where we get the output, the weights and biases associated with the input values. There is an activation function and then there is a loss function as well. So let's discuss each of them one by one.
So, first of all, I'm going to talk about what exactly are layers. So, a layer in neural network is basically it's storing the neuron before passing it onto the next layer. So, a neuron is basically the input point. A layer is basically storing that particular neuron before passing it to the next layer. And each neuron is a mathematical operation that takes its input, multiplies it by the weight associated with it, and then passes the sum through the activation functions to the other neurons. And there are basically three types of layers, as I've told you, there is an input layer, hidden layer, and an output layer. So, as the name suggests, the input layer accepts all the inputs provided by the programmer or the user. And the hidden layer, between the input and the output layer, is a set of layers known as hidden layers. And in this layer, computations are performed which results in the output. So, we are getting the results from these hidden layers. And whatever results that we're getting, I mean, the inputs go through a series of transformations via the hidden layer where we get the output. So, it finally results in the output and the output is delivered by the output layer.
So, now we're going to move on and learn about what exactly are weights and biases and why are we using them in neural network. So, to talk about weights and biases, one of the main components of neural network are weights and biases. So, what exactly are weights? We can understand weights as a value associated with the input that basically decides how much importance that particular input has to calculate the desired output. Basically, the priority of, you know, the input. And the weights are optimized during the training phase, which we'll discuss later on while training the model. And to understand this with an example, let's say we have a vintage car. Now, to calculate the price of a vintage car, there would be two very essential factors. First is, how old is the car or which model, I mean, what year the model was made, and how much has it been driven, like how many miles are there on the car. So, the weights would have a negative relationship with respect to the year it was made, because older the model was made, the higher the price would be. And similar for the number of miles associated with the car. The less the number of miles, the more the price will go up.
Now, to understand bias, it is simply a constant value that is added to the weighted sum of inputs to offset the result. We'll understand this bias when we're taking a look at the neural network using Python, guys. So, don't worry. And we're going to take a look at the activation function and understand why it is used in neural network. So, what exactly is an activation function? An activation function is basically normalizing the computed input to produce an output. There can be various activation functions, like there can be a sigmoid function, a linear function, there are softmax and ReLU. So, these are the activation functions that we can use. So, in our model, we're going to use the sigmoid function, guys. Probably because it has a threshold value from 0 to 1. So, there can be only two outputs, either 0 or 1. So, that is why we're going to use sigmoid function, guys. And if you want to learn more about sigmoid function, we have a tutorial on our YouTube channel that you can check out. You can also check out the gradient descent algorithm or tutorial that we have on our YouTube channel. And to understand, okay, let's try to understand the whole process in a systematic way.
So, let's say the input values are I1, I2, I3 and so on. And there is a subsequent weights associated with them, which is W1, W2 and so on until Wn. And the weighted sum will be I1*W1 + I2*W2 and so on. After adding the bias, the weighted sum would become I1*W1 + I2*W2 and so on + bias. Now, the output will be computed using the activation function. So, the output would be sigmoid function on our calculated sum or calculated weighted sum. So, it would look somewhat like this. So, basically, sigmoid function is output is equal to 1 / (1 + e to the power -x). And we will learn more about this when we will train the neural network.
So, now we'll talk about the next step that we have in our neural network. So far, we have discussed the layers, the weight associated with the input, and how the output is calculated using the activation function. Now, to implement a neural network, there are two steps involved. First is feed forward and then there is back propagation. So, what exactly is feed forward? In a feed forward neural network, the weights are taken randomly and the output is calculated using the activation function. These weights are actually taken at random are going to be optimized later on during back propagation. So, the entire process of the input going through all the layers and getting the output is feed forward. Now, on the other hand, back propagation is the process where weights are updated to minimize the calculated error. Error is nothing but, you know, the actual output and the predicted output is compared and the difference between both of them is the error. So, we have to minimize that error. For that, we use back propagation. Now, to do the weights are basically updated using the gradient descent algorithm. That's why I was talking about, you can check it out on our YouTube channel. So, I'll talk about the flow, how we have set the flow, right. So, we take the inputs, we assign the bias and the weights associated with them. And we find the error in prediction after, you know, applying the activation function on the output. So, the idea to minimize the cost function or the error, we do it by using the gradient descent algorithm. And we repeat the training phase with updated weights until we get the lowest error or the lowest error in prediction. And after that, we make predictions. So, that is how the whole process works. And to understand how we calculate the error, we can take an example, you know, in the example, we'll be taking a look at how mean square error is taken and weights are updated using the gradient descent algorithm where we have x is equal to input, the f(x) is the output based on x, and lr is the learning rate. And we are going to find the derivative of f(x) and for that, we'll have three derivatives. We're going to apply the chain rule and there'll be three derivatives. And if you take a look at the gradient descent algorithm, the formula, how we calculate the derivative, you'll understand that. So, we're going to take it up to the actual implementation of neural network using everything that we have discussed so far. So, let's take it up to Jupyter notebook, guys, and we're going to see the practical implementation of how we use a neural network.
So, in this notebook, I have written down the logic for, you know, how we are going to implement a neural network. So, first of all, we're going to need a few components, which is going to be nothing but inputs, you know, the input features and they're going to be the output values and there'll be weights, the bias, and we're going to use the gradient descent algorithm. So, we're going to need the learning rate and derivative of the sigmoid function and of course, there'll be sigmoid function as the activation function. So, initially, I imported the numpy library. And now, guys, I must tell you, before we begin with this, you know, implementation, this is just to make you understand how it works. So, not necessarily you have to deal with, you know, neural networks like this. We have TensorFlow, we have scikit-learn where you can just, you know, uh, import a module and using TensorFlow, there is, you know, you can design a model, you know, sequential models with lot of layers and everything and there you have the activation functions and then you can also calculate the loss and there are metrics such as accuracy and everything. So, that is what you will use to make it easier for you. This is just I'm telling you guys to understand how it really works.
So, we have the input value. All right. So, we have an array with these four values in shape 4x2. And after that, we have the output values in the output array. And of course, we have four values, 0, 1, 1, 0. And this is in shape, we have reshaped into 4x1. And then we have the weights, which is none other than, uh, 0.1 and 0.2 weights associated with this particular program. And then we have the bias is equal to 0.3. And then the activation function is the sigmoid function, which is we have, you know, implemented using the numpy and numpy exponentiation. Now, of course, and this is minus x over here. This is, you know, how you implement a sigmoid function using numpy. And then there's a derivative of the sigmoid function, which we are going to use for chain rule, which will, you know, help us, uh, in the gradient descent algorithm or the formula. And then comes the part where we have the code where we are going to update the weights. So, for example, the epochs are in the range 10,000. And then the input array. So, there's a weighted sum. So, this goes on with the flow that I have mentioned. All right. So, first of all, you have an input value, then you calculate the weighted sum, add bias to it. So, this is basically the weighted sum where we have added the bias, the weights, everything. After that, we have the first output. Okay, so this is the feed forward. The first output is our sigmoid function using, we're getting. And after that, we will have a set of values for first output, which we can compare with the desired output. There we will have a error, basically the difference between both of them. And so we are calculating that using mean squared error. The statement that we have with numpy is calculating the mean squared error. And after that, we have the first derivative. These derivative values, we are practically calculating to incorporate in the gradient descent algorithm. So, first derivative is the error. This is the first derivative in the chain rule. Then we have the second derivative, which is basically nothing but the derivative of the sigmoid function. Then we have the final derivative, which is the dot product of the input values, where we have transposed the input values. Before it was in the shape 4x4x2, now it will become 2x4. And after that, we update the weights using the equation, which is nothing but this is x is equal to x - lr, which we have set as 0.05. And this is basically dy dx of f(x). All right. Right. So, this is the final derivative. That's why we have calculated all of these. And after that, we update the bias as well using the same formula. And now, if I run it. So, the weight value is -0.4 and 11.8, and the bias is -5.6. So, now to get the prediction done, we will check it for the new value. So, we have an array. The input value is 0 and 1. So, for this 0 and 1, the target output is 1. So, we will see how far off our result is. It is basically 0.9979. So, it's pretty accurate, guys. Now, we'll check for other values as well. So, let's check for 0 and 0. So, it should be around 0.3, which is almost, uh, equal to zero again. So, this is how we can implement a neural network in Python. So, this is a basic implementation, guys. So, instead of using a sigmoid function in this, you know, activation function, what we can do is we can use a linear function. There are other options as well. And, you know, that we can use a softmax function.
So, this is it, guys. With this, we have come to the end of the session. And I just want to tell you guys, this is a basic implementation of neural networks that you can find. So, if you really, really want to work with neural networks, this is just a very basic example that I've shown you. What you can do is you can go to TensorFlow official documentation or check out our YouTube channel and you can find a lot of tutorials there where we have image classification, you know, basic image classification, how you can just get data, pre-process the images and feed the input into a network where you can identify or classify the images into different classes. And that's a very basic example again.
So, this is the problem statement, guys. We need to figure out if the bank notes are real or fake. And for that, we'll be using artificial neural networks. And obviously, we need some sort of data in order to train our network. So, let us see how the data set looks like. So, over here, I've taken a screenshot of the data set with few of the rows in it. Data were extracted from images that were taken from genuine and forged bank note like specimens. After that, wavelet transform tools were used to extract features from those images. And these are few features that I'm highlighting with my cursor. And the final column or the last column actually represents the label. So, basically, label tells us to which class that pattern represents, whether that pattern represents a fake note or it represents a real note. Let us discuss these features and labels one by one. So, the first feature or the first column is nothing but variance of wavelength transformed image. The second column is about skewness. The third is curtosis of wavelength transformed image. And finally, fourth one is entropy of the image. After that, when I talk about label, which is nothing but my last column over here, if the value is one, that means the pattern represents a real note. Whereas, when value is zero, that means it represents a fake note.
So, guys, let's move forward and we'll see what are the various steps involved in order to implement this use case. So, over here, we'll first begin by reading the data set that we have. We'll define features and labels. After that, we are going to encode the dependent variable. And what is a dependent variable? It is nothing but your label. Then we are going to divide the data set into two parts, one for training, another for testing. After that, we'll use TensorFlow data structures for holding features, labels, etc. And TensorFlow is nothing but a Python library that is used in order to implement deep learning models or you can say neural networks. Then we'll write the code in order to implement the model. And once this is done, we will train our model on the training data. We'll calculate the error. Error is nothing but your difference between the model output and the actual output. And we'll try to reduce this error. And once this error becomes minimum, we'll make prediction on the test data and we'll calculate the final accuracy.
So, guys, let me quickly open my PyCharm and I'll show you how the output looks like. So, this is my PyCharm, guys. Over here, I've already written the code in order to execute the use case. I'll go ahead and run this and I'll show you the output. So, over here, as you can see, with every iteration, the accuracy is increasing. So, let me just stop it right here. All right. Till now, any questions? Any doubts with respect to what is our use case, what is the data set about? Any questions, guys? You can go ahead and ask me. Okay, there's a question from Arpan. He's asking, can you explain the code? Definitely, Arpan. I'll be doing that at the end of this class when you are done with all the fundamentals of neural networks. I'll explain you the entire code, how I have written that and how I've used TensorFlow in order to implement neural networks. I hope you are satisfied with the answer. Okay, he's fine with it. Any other questions? Any other doubts, guys? Just go ahead and ask me over here. You don't need to worry about code right now, guys, because I'll explain this later in the session.
So, what I'll do, I'll open my slides once more and we'll discuss the fundamentals of neural networks that are required in order to implement this use case. So, in order to understand why we need neural networks, we are going to compare the approach before and after neural networks and we'll see what were the various problems that were there before neural networks. So, earlier, conventional computers use an algorithmic approach, that is, the computer follows a set of instructions in order to solve a problem. And unless the specific steps that the computer needs to follow are known, the computer cannot solve the problem. So, obviously, we need a person who actually knows how to solve that problem and he or she can provide the instructions to the computer as to how to solve that particular problem. Right? So, we first should know the answer to that problem or we should know how to overcome that challenge or problem which is there in front of us. Then only we can provide instructions to the computer. So, this restricts the problem-solving capability of conventional computers to problems that we already understand and know how to solve. But what about those problems whose answer we have no clue of? So, that's where our traditional approach was a failure. So, that's why neural networks were introduced.
Now, let us see what was the scenario after neural networks. So, neural networks basically process information in a similar way the human brain does. And these networks, they actually learn from examples. You cannot program them to perform a specific task. They will learn from their examples, from their experience. So, you don't need to provide all the instructions to perform a specific task and your network will learn on its own with its own experience. All right. So, this is what basically neural network does. So, even if you don't know how to solve a problem, you can train your network in such a way that with experience, it can actually learn how to solve the problem. So, that was a major reason why neural networks came into existence.
We'll move forward and we'll understand what is the motivation behind neural networks. So, these neural networks are basically inspired by neurons, which are nothing but your brain cells. And the exact working of the human brain is still a mystery, though. So, as I've told you earlier as well, that neural networks work like human brain and so the name. And similar to a newborn human baby, as he or she learns from his or her experience, we want a network to do that as well, but we wanted to do it very quickly. So, here's a diagram of a neuron. Basically, a biological neuron receives input from other sources, combines them in some way, perform a generally non-linear operation on the result, and then outputs the final result. So, here, if you notice these dendrites, these dendrites will receive signals from the other neurons. Then what will happen? It will transfer it to the cell body. The cell body will perform some function. It can be summation. It can be multiplication. So, after performing that summation on the set of inputs via axon, it is transferred to the next neuron.
Now, let's understand what exactly are artificial neural networks. It is basically a computing system that is designed to simulate the way the human brain analyzes and processes the information. Artificial neural networks has self-learning capabilities that enable it to produce better results as more data becomes available. So, if you train your network on more data, it'll be more accurate. So, these neural networks, they actually learn by example. And you can configure your neural network for specific applications. It can be pattern recognition or it can be data classification, anything like that. All right. So, because of neural networks, we see a lot of new technology has evolved from translating web pages to other languages to having a virtual assistant to order groceries online to conversing with chatbots. All of these things are possible because of neural networks. So, in a nutshell, if I need to tell you, artificial neural network is nothing but a network of various artificial neurons. All right.
So, let me show you the importance of neural network with two scenarios, before and after neural network. So, over here, we have a machine and we have trained this machine on four types of dogs, as you can see where I'm highlighting with my cursor. And once the training is done, we provide a random image to this particular machine which has a dog, but this dog is not like the other dogs on which we have trained our system on. So, without neural networks, our machine cannot identify that dog in the picture, as you can see it over here. Basically, our machine will be confused. It cannot figure out where the dog is. Now, when I talk about neural networks, even if we have not trained our machine on this specific dog, but still it can identify certain features of the dogs that we have trained on and it can match those features with the dog that is there in this particular image and it can identify that dog. So, this happens all because of neural networks. So, this is just an example to show you how important are neural networks.
Now, I know you all must be thinking how neural networks work. So, for that, we'll move forward and understand how it actually works. So, over here, I'll begin by first explaining a single artificial neuron that is called as perceptron. So, this is an example of a perceptron. Over here, we have multiple inputs, x1, x2, dash, till xn. And we have corresponding weights as well, W1 for x1, W2 for x2, similarly Wn for xn. Then what happens? We calculate the weighted sum of these inputs. And after doing that, we pass it through an activation function. This activation function is nothing but it provides a threshold value. So, above that value, my neuron will fire, else it won't fire. So, this is basically an artificial neuron. So, when I talk about a neural network, it involves a lot of these artificial neurons with their own activation function and their processing element.
Now, we'll move forward and we'll actually understand various modes of this perceptron or single artificial neuron. So, there are two modes in a perceptron. One is training, another is using mode. In training mode, the neuron can be trained to fire for particular input patterns. Which means that we'll actually train our neuron to fire on certain set of inputs and to not fire on the other set of inputs. That's what basically training mode is. When I talk about using mode, it means that when a total input pattern is detected at the input, its associated output becomes the current output. Which means that once the training is done and we provide an input on which the neuron has been trained on, so it'll detect the input and we'll provide the associated output. So, that's what basically using mode is. So, first you need to train it, then only you can use your perceptron or your, uh, network. So, these were the two modes, guys.
Next up, we'll understand what are the various activation functions available. So, these are the three activation functions, although there are many more, but I've listed down three. Step function. So, over here, the moment your input is greater than this particular value, your neuron will fire, else it won't. Similarly for sigmoid and sine function as well. So, these are three activation functions. There are many more that I've told you earlier as well. So, these are the three majorly used activation functions.
Next up, what we are going to do, we are going to understand how a neuron learns from its experience. So, I'll give you a very good analogy in order to understand that. And later on, when we talk about the neural networks or you can say multiple neurons in a network, I'll explain you the math behind it. I'll explain you the math behind learning, how it actually happens. So, right now, I'll explain you with an analogy. And guys, trust me, that analogy is pretty interesting. So, I know all of you must have guessed it. So, these are two beer mugs. And all of you who love beer can actually relate to this analogy a lot. And I know most of you actually love beer. So, that's why I've chosen this particular analogy so that all of you can relate to it. All right, jokes apart. So, fine, guys. So, there's a beer festival happening near your house and you want to badly go there. But your decision actually depends on three factors. First is, how is the weather, whether it is good or bad. Second is, your wife or husband is going with you or not. And the third one is, any public transport is available. So, on these three factors, your decision will depend whether you'll go or not. So, we'll consider these three factors as inputs to our perceptron and we'll consider our decision of going or not going to the beer festival as our output. So, let us move forward with that.
So, the first input is how is the weather? We'll consider it as x1. So, when weather is good, it'll be one, and when it is bad, it'll be zero. Similarly, your wife is going with you or not, that'll be your x2. If she is going, then it's one, if she's not going, then it's zero. Similarly for public transport, if it is available, then it is one, else it is zero. So, these are the three inputs that I'm talking about. Let's see the output. So, output will be one when you're going to the beer festival, and output will be zero when you want to relax at home. You want to have beer at home only. You don't want to go outside. So, these are the two outputs, whether you are going or you're not going.
Now, what a human brain does over here? Okay, fine. I need to go to the beer festival. But there are three things that I need to consider. But will I give importance to all these factors equally? Definitely not. There'll be certain factors which will be of higher priority for me. I'll focus on those factors more, whereas few factors won't affect that much to me. All right. So, let's prioritize our inputs or factors. So, here, our most important factor is weather. So, if weather is good, I love beer so much that I don't care even if my wife is going with me or not, or if there is a public transport available. So, I love beer that much that if weather is good, that definitely I'm going there. That means when x1 is high, output will be definitely high. So, how we do that? How we actually prioritize our factors or how we actually give importance more to a particular input and less to another input in a perceptron or in a neuron? So, we do that by using weights. So, we assign high weights to the more important factors or more important inputs and we assign low weights to those particular inputs which are not that important for us. So, let's assign weights, guys. So, weight w is associated with input x1, w2 with x2, and similarly w3 with x3. Now, as I've told you earlier as well, that weather is a very important factor. So, I'll assign a pretty
high weight to weather and I'll keep it as six. Similarly, W2 and W3 are not that important. So I'll keep it as 22. After that, I've defined a threshold value as five. Which means that when the weighted sum of my input is greater than five, then only my neuron will fire, or you can say, then only I'll be going to the BF festival.
All right. So I'll use my pen and we'll see what happens when weather is good. So when weather is good, our x1 is 1. Our weight is six, we'll multiply it with six. Then, if my wife decides that she is going to stay at home and she will probably be busy with cooking and she doesn't want to drink beer with me, so she's not coming. So that input becomes zero. 0 into two will actually make no difference because it'll be zero. Then again, there is no public transport available also. Then also this will be 0 into 2. So what output I get here? I get here as six. And notice the threshold value, it is five. So definitely, six is greater than five. That means my output will be one, or you can say, my neuron will fire, or I'll actually go to the beer festival. So even if these two inputs are zero for me, that means my wife is not willing to go with me and there is no public transport available, but weather is good, which has a very high weight value and it actually matters a lot to me. So if that is high, it doesn't really matter whether the two inputs are high or not. I will definitely go to the BF festival.
All right. Now I'll explain you a different scenario. So over here, our threshold was five. But what if I change this threshold to three? So in that scenario, even if my weather is not good, uh, I'll give it the zero. So 0 into 6, but my wife and public transport both are available. All right. So 1 into 2 + 1 into 2, which is equal to 4, and it is definitely greater than three. Then also my output will be one. That means I will definitely go to the beer festival, even if the weather is bad, and my neuron will fire. So these are the two scenarios that I have discussed with you.
All right. So there can be many other ways in which you can actually assign weight to your uh problem or to your learning algorithm. So these are the two ways in which you can assign weights and prioritize your inputs or factors on which your output will depend. So obviously, in real life, all the inputs or all the factors are not as important for you. So you actually prioritize them, and how you do that in a perceptron, you provide high weight to it. This is just an analogy so that you can relate to a perceptron to a real life. We'll actually discuss the math behind it later in the session as to how a network or a neuron learns.
All right. So how the weights are actually updated and how the output is changing, that all those things we'll be discussing later in the session. But my aim is to make you understand that you can actually relate to a real life problem with that of a perceptron. All right? And in real life, problems are not that easy. They are very, very complex problems that we actually face. So in order to solve those problems, a single neuron is definitely not enough. So we need networks of neurons, and that's where artificial neural network or you can say multi-layer perceptron comes into the picture.
Now let us discuss that multi-layer perceptron or artificial neural network. So this is how an artificial neural network actually looks like. So over here, we have multiple neurons present in different layers. The first layer is always your input layer. This is where you actually feed in all of your inputs. Then we have the first hidden layer. Then we have the second hidden layer, and then we have the output layer. Although the number of hidden layers depend on your application, on what are you working, what is your problem. So that actually determines how many hidden layers you'll have.
So let me explain you what is actually happening here. So you provide in some input to the first layer, which is nothing but your input layer. You provide inputs to these neurons. All right? And after some function, the output of these neurons will become the input to the next layer, which is nothing but your hidden layer one. Then these hidden layers also have various neurons. These neurons will have different activation functions. So they'll perform their own function on the inputs that it receives from the previous layer. And then the output of this layer will be the input to the next hidden layer, which is hidden layer 2. Similarly, the output of this hidden layer will be the input to the output layer, and finally, we get the output. So this is how basically an artificial neural network looks like.
Now let me explain you this with an example. So over here, I'll take an example of image recognition using neural networks. So over here, what happens? We feed in a lot of images to our input layer. Now, this input layer will actually detect the patterns of local contrast, and then we'll feed that to the next layer, which is hidden layer one. So in this hidden layer one, the face features will be recognized. We'll recognize eyes, nose, ears, things like that. And then that will be again fed as input to the next hidden layer, and in this hidden layer, we'll assemble those features and we'll try to make a face, and then we'll get the output that is the face will be recognized properly. So if you notice here, with every layer, we are trying to get a more abstract version or the generalized version of the input. So this is now basically an artificial neural network, how it works.
All right. And there's a lot of training and learning which is involved that I'll show you now. Training a neural network. So how we actually train a neural network. So basically, the most common algorithm for training a network is called back propagation. So what happens in back propagation? After the weighted sum of inputs and passing through an activation function and getting the output, we compare that output to the actual output that we already know. We figure out how much is the difference. We calculate the error, and based on that error, what we do? We propagate backwards and we'll see what happens when we change the weight, will the error decrease or will it increase? And if it increases, when it increases, by increasing the value of the variables or by decreasing the value of variables. So we kind of calculate all those things and we update our variables in such a way that our error becomes minimum, and it takes a lot of iterations. Trust me, guys, it takes a lot of iterations. We get output a lot of times and then we compare it with the model, with the actual output. Then again, we propagate backwards. We change the variables and again, we calculate the output. We compare it again with the desired output or the actual output. Then again, we propagate backwards. So this process keeps on repeating until we get the minimum value.
All right. So there's an example that is there in front of your screen. Don't be scared of the terms that I used. I'll actually explain you with an example. So this is the example over here. We have 0, 1, and 2 as inputs. And our desired output or the output that we already know is 0, 1, and 4. All right. So over here, we can actually figure out that the desired output is nothing but twice of your input. But I'm training a computer to do that, right? The computer is not a human. So what happens? I actually initialize my weight. I keep the value as three. So the model output will be 3 into 0 is 0. 3 into 1 is 3. 3 into 2 is 6. Now, obviously, it is not equal to your desired output. So we check the error. Now, the error that we have got here is 0, 1, and 2, which is nothing but your difference. So 0 - 0 is 0. 3 - 2 is 1. 6 - 4 is 2. Now, this is called an absolute error. After squaring this error, we get square error, which is nothing but 0, 1, and 4. All right.
So now, what we need to do, we need to update the variables. We have seen that the output that we got is actually different from the desired output. So we need to update the value of the weight. So instead of three, our computer makes it as four. After making the value as four, we get the model output as 0, 4, and 8. And then we saw that the error has actually increased. Instead of decreasing, the error has increased. So after updating the variable, the error has increased. So you can see that square error is now 0, 4, and 16. And earlier it was 0, 1, and 4. That means we cannot increase the weight value right now. But if we decrease that, make it as two, we get the output which is actually equal to the desired output. But is it always the case that we need to only decrease the weight? Definitely not. So in this particular scenario, whenever I'm increasing the weight, error is increasing, and when I'm decreasing the weight, error is decreasing. But as I've told you earlier as well, this is not the case every time. Sometimes you need to increase the weight as well. So how we determine that?
All right. Fine, guys. This is how basically a computer decides whether it has to increase the weight or decrease the weight. So what happens here? This is a graph of square error versus weight. So over here, what happens? Suppose your square error is somewhere here, and your computer, it starts increasing the weight in order to reduce the square error, and it notices that whenever it increases the weight, square error is actually decreasing. So it'll keep on increasing until the square error reaches a minimum value, and after that, when it tries to still increase the weight, the square error will increase. So at that time, our network will recognize that whenever it is increasing the weight after this point, error is increasing. So therefore, it will stop right there, and that will be our weight value. Similarly, there can be one more scenario. Suppose if we increase the weight, but then also the square error is increasing. So at that time, we cannot increase the weight. At that time, the computer will realize, okay, fine, whenever I'm increasing the weight, the square error is increasing. So it'll go in the opposite direction. So it'll start decreasing the weight, and it'll keep on doing that until the square error becomes minimum. And the moment it decreases more, the square error again increases. So our network will know that whenever it decreases the weight value, the square error is increasing. So that point will be our final weight value. So, guys, this is what basically back propagation in a nutshell is.
Fine. So we'll move forward and now is the correct time to understand how to implement the use case that I was talking about in the beginning, that is, how to determine whether a node is fake or real. For that, I'll open my PyCharm. This is my PyCharm again, guys. Uh, let me just close this. All right. So this is the code that I've written in order to implement the use case. So over here, what we do, we import the first important libraries which are required. Matplotlib is used for visualization. TensorFlow, we know, in order to implement the neural networks. NumPy for arrays. Pandas for reading the data set. Similarly, scikit-learn for label encoding, as well as for shuffling, and also to split the data set into training and testing task. All right.
Fine, guys. So we'll begin by first reading the data set, as I've told you earlier as well, when I was explaining the steps. So what I'll do, I'll use pandas in order to read the CSV file which has the data set. After that, I'll define features and labels. So X will be my feature, and Y will contain my label. So basically, X includes all the columns apart from the last column, which is the fifth one. And because the indexing starts from zero, that's why we have written 0 till fourth. So it won't include the fourth column. All right. And so our last column will actually be our label. Then what we need to do, we need to encode the dependent variable. So the dependent variable, as I've told you earlier as well, is nothing but your label. So I've discussed encoding in the TensorFlow tutorial. You can go through it and you can actually get to know why and how we do that.
Then what we have done, we have read the data set. Then what we need to do is to split our data set into training and testing. And these are all optional steps. You can print the shape of your training and test data. If you don't want to do it yourself, fine. Then we have defined learning rate. So learning rate is actually the steps in which the weights will be updated. All right. So that is what basically learning rate is. Then when we talk about epoch, it means iterations. Then we have defined cost history, that will be an empty NumPy array, and its shape will be one, and it'll include the float type objects. Then we have defined N, which is nothing but your X shape of axis one, which means your column. Then we'll print that. After that, we have defined the number of classes. So there can be only two classes: whether the node can be fake or it can be real. And this model path, I've given in order to save my model. So I've just given a path where I need to save it. So I'll just save it here only in the current working directory.
Now is the time to actually define our neural network. So we'll first make sure that we have defined the important parameters like hidden layers, number of neurons in hidden layers. So I'll take 10 neurons in every hidden layer, and I'm taking four layers like that. Then X will be my placeholder, and the shape of this particular placeholder is None, N_dim. N_dim value I'll get it from here, and None can be any value. I'll define one variable W, and I'll initialize it with zeros, and this will be the shape of my weight. Similarly, for bias as well. This will be the particular shape, and there will be one more placeholder, Y_dash, which will actually be used in order to provide us with the actual output of the model. There will be one model output, and there will be one actual output which we use in order to calculate the difference, right? So we'll feed in the actual values of the labels in this particular placeholder Y_dash.
And now we'll define the model. So over here, we have named the function as multi_layer_perceptron, and in it, we'll first define the first layer. So the first hidden layer, and we are going to name it as layer_1, which will be nothing but the matrix multiplication of X and weights of h1, that is the hidden layer 1, and that'll be added to your biases B1. After that, we'll pass it through a sigmoid activation function. Similarly, in layer 2 as well, matrix multiplication of layer_1 and weights of h2. So if you can notice, layer_1 was the network layer just before the layer two, right? So the output of this layer_1 will become input to the layer two, and that's why we have written layer_1. It'll be multiplied by weights H2, and then we'll add it with the bias, similarly for this particular hidden layer as well, and this particular layer as well. But over here, we are going to use the ReLU activation function instead of sigmoid. Then we are going to define the weights and biases. So this is how we basically define weights. This is how we basically define weights. So weights H1 will be a variable which will be a truncated normal with the shape of N_dim and N_hidden_1. So these are nothing but your shapes. All right. And after that, what we have done, we have defined biases as well.
Then we need to initialize all the variables. So all these things actually I've discussed in brief when I was talking about TensorFlow. Since in TensorFlow, we need to initialize the variables before we use it. So that's how we do it. We first initialize it, and then we need to run it. That's when your variables will be initialized. After that, we are going to create a saver object, and then finally, I'm going to call my model, and then comes the part where the training happens. Cost function. Cost function is nothing but, you can say, an error that will be calculated between the actual output and the model output. All right. So Y is nothing but our model output, and Y_dash is nothing but actual output or the output that we already know. All right. And then we are going to use a gradient descent optimizer to reduce error. Then we are going to create a session object as well. And finally, what we are going to do, we are going to run the session. So this is how we basically do that. For every epoch, we will be calculating the change in the error as well as the accuracy that comes after every epoch on the training data. After we have calculated the accuracy on the training data, we're going to plot it for every epoch, how the accuracy is, and after plotting that, we're going to print the final accuracy, which will be on our test data. So using the same model, we'll make predictions on the test data, and after that, we are going to print the final accuracy and the mean squared error. So let's go ahead and execute this, guys.
All right. So training is done, and this is the graph we have got for accuracy versus epoch. This is accuracy. Y-axis represents accuracy, whereas this is epoch. We have taken 100 epochs, and our accuracy has reached somewhere around 99%. So with every epoch, it is actually increasing, apart from a couple of instances, it is actually keep on increasing. So the more data you train your model on, it'll be more accurate. Let me just close it. So now the model has also been saved where I wanted it to be. This is my final test accuracy, and this is the mean squared error. All right. So these are the files that will appear once you save your model. These are the four files that I've highlighted.
Now what we need to do is restore this particular model. And I've explained this in detail how to restore a model that you have already saved. So over here, what I'll do, I'll take some random range. I've taken it actually from 754 to 768. So all the values in the row of 754 and 768 will be fed to our model, and our model will make prediction on that. So let us go ahead and run this. So when I'm restoring my model, it seems that my model is 100% accurate for the values that I have fed in. So whatever values that I have actually given as input to my model, it has correctly identified its class, whether it's a fake node or a real node, because zero stands for fake node and one stands for real node. Okay. So original class is nothing but which is there in my data set. So it is zero already, and what prediction my model has made is zero, that means it is fake. So accuracy becomes 100%. Similarly, for other values as well.
Fine, guys. So this is how we basically implement the use case that we saw in the beginning. So in the slide, you can notice that I've listed out only two applications, although there are many more. So neural networks in medicine. Artificial neural networks are currently a very hot research area in medicine, and it is believed that they will receive extensive application to biomedical systems in the next few years, and currently, the research is mostly on modeling parts of the human body and recognizing diseases from various scans. For example, it can be cardiograms, CAT scans, ultrasonic scans, etc. And currently, the research is going mostly on two major areas. First is modeling and diagnosing the cardiovascular system. So neural networks are used experimentally to model the human cardiovascular system. Diagnosis can be achieved by building a model of the cardiovascular system of an individual and comparing it with the real-time physiological measurements taken from the patient. And trust me, guys, if this routine is carried out regularly, potential harmful medical conditions can be detected at an early stage, and thus make the process of combating disease much easier. Apart from that, it is currently being used in electronic noses as well. Electronic noses have several potential applications in telemedicine.
Now, let me just give you an introduction to telemedicine. Telemedicine is a practice of medicine over long distance via a communication link. So what the electronic noses will do, they would identify odors in the remote surgical environment. These identified odors would then be electronically transmitted to another site where an odor generation system would recreate them. Because the sense of smell can be an important sense to the surgeon. Tele-smell would enhance telerobotic surgery. So these are the two ways in which you can use it in medicine.
You can use it in business as well, guys. So business is basically a diverted field with several general areas of specialization, such as accounting or financial analysis. Almost any neural network application would fit into one business area or financial analysis. Now, there is some potential for using neural networks for business purposes, including resource allocation and scheduling. I've listed down two major areas where it can be used. One is marketing. So there is a marketing application which has been integrated with a neural network system. The airline marketing tactician is a computer system made of various intelligent technologies, including expert systems. A feed-forward neural network is integrated with the AMT, which is nothing but airline marketing tactician, and was trained using back propagation to assist the marketing control of airline seat allocation. So it has wide applications in marketing as well. Now, the second area is credit evaluation. Now, I'll give you an example here. The HNC company has developed several neural network applications, and one of them is a credit scoring system, which increases the profitability of existing models up to 27%. So these are few applications that I'm telling you, guys. Neural network is actually the future. People are talking about neural networks everywhere, and especially after the introduction of GPUs and the amount of data that we have now, neural network is actually spreading like plague right now.
Why can't we use feed-forward networks? Now let us take an example of a feed-forward network that is used for image classification. So we have trained this particular network for classifying various images of animals. Now, if you feed in an image of a dog, it will identify that image and will provide a relevant label to that particular image. Similarly, if you feed in an image of an elephant, it will provide a relevant label to that particular image as well. Now, if you notice, the new output that we have got, that is classifying an elephant, has no relation with the previous output that is of a dog, or you can say that the output at time t is independent of output at time t minus one, as we can see that there is no relation between the new output and the previous output. So we can say that in feed-forward networks, outputs are independent to each other.
Now, there are few scenarios where we actually need the previous output to get the new output. Let us discuss one such scenario. Now, what happens when you read a book? You'll understand that book only on the understanding of your previous words. All right? So if I use a feed-forward network and try to predict the next word in a sentence, I can't do that. Why can't I do that? Because my output will actually depend on the previous outputs. But in the feed-forward network, my new output is independent of the previous outputs. That is, output at t + 1 has no relation with output at t - 2, t - 1, and at t. So basically, we cannot use feed-forward networks for predicting the next word in a sentence. Similarly, you can think of many other examples where we need the previous output, some information from the previous output, so as to infer the new output. This is just one small example. There are many other examples that you can think of.
So we'll move forward and understand how we can solve this particular problem. So over here, what we have done, we have input at t minus one. We'll feed it to our network. Then we'll get the output at t minus one. Then at the next time stamp, that is at time t, we have input at time t, that will be given to our network along with the information from the previous time stamp, that is t minus one, and that will help us to get the output at t. Similarly, at output for t + 1, we have two inputs: one is a new input that we give, another is the information coming from the previous time stamp, that is t, in order to get the output at time t + 1. Similarly, it can go on. So over here, I have just written a generalized way to represent it. There's a loop where the information from the previous time stamp is flowing, and this is how we can solve this particular challenge.
Now let us understand what exactly are recurrent neural networks. So for understanding recurrent neural networks, I'll take an analogy. Suppose your gym trainer has made a schedule for you. The exercises are repeated after every third day. Now, this is the order of your exercises. First day, you'll be doing shoulders. Second day, you'll be doing biceps. Third day, you'll be doing cardio. And all these exercises are repeated in a proper order. Now, what happens when we use a feed-forward network for predicting the exercise today? So we'll provide in the inputs such as day of the week, month of the year, and health status. All right. And we need to train our model or a network on the exercises that we have done in the past. After that, there'll be a complex voting procedure involved that will predict the exercise for us, and that procedure won't be that accurate. So whatever output we'll get won't be as accurate as we want it to be.
Now, what if I change my inputs and I make my inputs as what exercise I've done yesterday? So if I've done shoulder, then definitely today I'll be doing biceps. Similarly, if I've done biceps yesterday, today I'll be doing cardio. Similarly, if I've done cardio yesterday, today I'll be doing shoulder. Now, there can be one scenario where you are unable to go to the gym for one day due to some personal reasons, you could not go to the gym. Now, what will happen at that time? We'll go one time stamp back and we'll feed in what exercise that happened day before yesterday. So if the exercise that happened day before yesterday was shoulder, then yesterday there were biceps exercises. All right. Similarly, biceps happened day before yesterday, then yesterday would have been cardio exercises. Similarly, if cardio would have happened day before yesterday, would have been shoulder exercises. All right. And this prediction, the prediction for the exercise that happened yesterday, will be fed back to our network, and these predictions will be used as inputs in order to predict what exercise will happen today. Similarly, if you have missed your gym, say for 2 days, 3 days, or one week. So, you need to roll back. You need to go to the last day when you went to the gym, you need to figure out what exercise you did on that day. Feed that as an input, and then only you'll be getting the relevant output as to what exercise will happen today.
Now, what I'll do, I'll convert these things into a vector. Now, what is a vector? Vector is nothing but a list of numbers. All right? So, this is the new information, guys, along with the information from the prediction at the previous time step. So we need both of these in order to get the prediction at time t. Imagine if I have done shoulder exercises yesterday. So this will be one, this will be zero, this will be zero. Now, the prediction that will happen will be biceps exercise because if I've done shoulder yesterday, today it will be biceps. So my output will be 0, 1, and zero. And this is how vectors work, guys. So I hope you have understood this, guys.
Now, this is how a neural network looks like, guys. We have new information along with the information from the previous time stamp. The output that we have got in the previous time stamp will uh use certain information from that, will feed into our network as inputs, and then that will help us to get the new output. Similarly, this new output that we have got will take some information from that, feed in as an input to our network along with the new information to get the new prediction, and this process keeps on repeating.
Now let me show you the math behind the recurrent neural networks. So this is the structure of a recurrent neural network, guys. Let me explain you what happens here. Now, consider at time t equals to 0, we have input x naught, and we need to figure out what is h naught. So according to this equation, h of 0 is equal to w i, weight matrix, multiplied by input x of 0, plus w into h of 0 - 1, which is h of minus 1, and time can never be negative. So we, this particular equation cannot be applied here, plus a bias. So w i into x of 0 plus b h passes through a function g of h to get h of 0 over here. After that, I want to calculate y. So for y naught, I'll multiply h of 0 with the weight matrix w y and I'll add a bias to it and pass it through a function g of y to get y naught.
Now, in the next time stamp, that is at time t = 1, things become a bit tricky. Now let me explain you what happens here. So at time t = 1, I have input x1. I need to figure out what is h1. So for that, I'll use this equation. So I'll multiply w i, that is a weight matrix, by the input x1, plus w into h of 1 - 1, which is 0. h of 0, we know what we got from here. So w y into h of 0 plus the bias, pass it through a function g of h to get the output as h1. Now, this h1, we'll use to get y1. We'll multiply h1 with w y, plus a bias, and we'll pass it through a function g of y to get y1. Similarly, the next time stamp, that is at time t = 2, we have input x2. We need to figure out what will be h2. So, we'll multiply the weight matrix w i with x of 2, plus w into h of 1, that we have got here, plus b h, and pass it through a function g of h to get h of 2. From h of 2, we'll calculate y of 2. W y into h of 2, plus b y, that is the bias. Pass it through a function g of y to get y. And this is how recurrent neural network works, guys.
Now you must be thinking, how to train a recurrent neural network? So a recurrent neural network uses back propagation algorithm for training. But back propagation happens for every time stamp. That is why it is commonly called as back propagation through time. Over here, I won't be discussing back propagation uh in detail. I'll just give you a brief introduction of what it is. Now, with back propagation, there are certain issues, namely vanishing and exploding gradients. Let us see those one by one.
So in vanishing gradient, what happens? When you use back propagation, you tend to calculate the error, which is nothing but the actual output that you already know minus the model output, output that you got through your model, and the square of that. So you figure out the error. With that error, what do you do? You tend to find out the change in error with respect to change in weight or any variable. So we'll call it weight here. So change of error with respect to weight multiplied by learning rate will give you the change in weight. Then you need to add that change in weight to the old weight to get the new weight. All right. So obviously, what we are trying to do, we are trying to reduce the error. So for that, we need to figure out what will be the change in error if my variables are changed, right? So that way, we can get the change in the variable and add it to our old variable to get the new variable. Now over here, what can happen? If the value dE by dW, that is the gradient, or you can say the rate of change of error with respect to our variable weight, becomes very small than one, like it is 0. something. So if you multiply that with the learning rate, which is definitely smaller than one, then you get the change of weight, which is negligible. All right. So there might be certain examples where, you know, you are trying to predict, say, a next word in a sentence, and the sentence is pretty long. For example, if I say, "I went to France," dash dash, "I went to France," then there are certain words, then I say, "few of them speak," dash. Now I need to predict, speak, what will come after speak. So for that, I need to go back in time and check what was the context, which will be very complex, and because of that, there will be a lot of iterations, and because of that, this error, this change in weight will become very small, very small. So the new weight that we'll get will be actually almost equal to your old weight. So there won't be any updation of weight that will be happening, and that is nothing but your vanishing gradient. All right. I'll repeat it once more. So what happens in back propagation? You first calculate the error. This error is nothing but the difference between the actual output and the model output, and the square of that. With that error, we figure out what will be the change in error when we change a particular variable, say weight. So, dE by dW, multiply it with learning rate to get the change in the variable or change in the weight. Now, we'll add that change in the weight to our old weight to get the new weight. This is what back propagation is, guys. All right. I've just given you a small introduction to back propagation.
Now consider a scenario where you need to predict the next word in the sentence, and your sentence is something like this: "I have been to France," then there are a lot of words after that, "few people speak," and then you need to predict what comes after "speak." Now, if I need to do that, I need to go back and understand the context, what is it talking about, and that is nothing but your long-term dependencies. So what happens during long-term dependencies? If this dE by dW becomes very small, then when you multiply it with eta, which is again smaller than one, you get delta W, which will be very, very small, that will be negligible. So the new weight that you'll get here will be almost equal to your old weight. So there won't be any updation of weights, guys. This new weight will definitely be will always be almost equal to our old weight. So there won't be any learning here. So that is nothing but your vanishing gradient problem.
Similarly, when I talk about exploding gradient, it is just the opposite of vanishing gradient. So what happens when your gradient, or dE by dW, becomes very large, becomes greater than greater than one. All right? And you have some long-term dependencies. So at that time, your dE by dW will keep on increasing. Delta W will become large, and because of that, your weights, the new weight that will come, will be very different from your old weight. So these two are the problems with back propagation.
Now let us see how to solve these problems. Now, exploding gradients can be solved with the help of truncated BPTT, back propagation through time. So instead of starting back propagation at the last time stamp, we can choose a smaller time stamp, like 10, or we can clip the gradients at a threshold. So there can be a threshold value where we can, you know, clip the gradients, and we can adjust the learning rate as well. Now, for vanishing gradient, we can use a ReLU activation function. We have discussed ReLU activation function in artificial neural network tutorial, guys. Similarly, we can also use LSTMs and GRUs. In this tutorial, we'll be discussing LSTMs, that are Long Short-Term Memory units.
Now, let us understand what exactly are LSTMs. So, guys, we saw what are the two limitations with the recurrent neural networks. Now, we'll understand how we can solve that with the help of LSTMs. Now, what are LSTMs? Long Short-Term Memory networks, usually called as LSTMs, are nothing but a special kind of recurrent neural network. And these recurrent neural networks are capable of learning long-term dependencies. Now, what are long-term dependencies? I've discussed that in the previous slide, but I'll just explain it to you here as well. Now, what happens? Sometimes we only need to look at the recent information to perform the present task. Now, let me give you an example. Consider a language model trying to predict the next word based on the previous ones. If we are trying to predict the last word in the sentence, say, "the clouds are in the sky." So we don't need any further context. It's pretty obvious that the next word is going to be "sky." Now, in such cases where the gap between the relevant information and the place that it's needed is small, RNN can learn to use the past information, and at that time, there won't be such problems like vanishing and exploding gradient. But there are few cases where we need more context. Consider trying to predict the last word in the text "I grew up in France." Then there are some words after that, "I speak fluent French." Now, recent information suggests that the next word is probably the name of a language. But if you want to narrow down which language, we need the context of France from further back, and it's entirely possible for the gap between the relevant information and the point where it is needed to become very large, and this is nothing but long-term dependencies, and the LSTMs are capable of handling such long-term dependencies.
Now, LSTMs also have a chain-like structure like recurrent neural networks. Now, all the recurrent neural networks have the form of a chain of repeating modules of neural networks. Now, in standard RNN, the repeating module will have a very simple structure, such as a single tanh layer that you can see. Now, this tanh layer is nothing but a squashing function. Now, what I mean by squashing function is to convert my values between minus 1 and 1. All right, that's why we use tanh, and this is an example of an RNN. Now, we'll understand what exactly are LSTMs. Now, this is a structure of an LSTM. If you notice, LSTM also have a chain-like structure, but the repeating module has different structures. Instead of having a single neural network layer, there are four interacting in a very special way. Now, the key to LSTM is the cell state. Now, this particular line that I'm highlighting, this is what what is called the cell state. The horizontal line running through the top of the diagram. So this is nothing but your cell state. Now, you can consider the cell state as a kind of a conveyor belt. It runs straight down the entire chain with only some minor linear interactions. Now, what I'll do, I'll give you a walkthrough of LSTM step by step. All right. So we'll start with the first step.
All right, guys. So the first step in our LSTM is to decide what information we are going to throw away from the cell state. And you know what is a cell state, right? I've discussed in the previous slide. Now, this decision is made by the sigmoid layer. So the layer that I'm highlighting with my cursor, it is a sigmoid layer called the forget gate layer. It looks at h t minus 1, that is the information from the previous time stamp, and x t, which is a new input, and outputs a number between zeros and one for each number in the cell state c t minus 1, which is coming from the previous time stamp. One represents completely keep this, while a zero represents completely get rid of this. Now, if you go back to our example of a language model trying to predict the next word based on all the previous ones. In such a problem, the cell state might include the gender of the present subject so that the correct pronouns can be used. When we see a new subject, we want to forget the gender of the old subject, right? We want to use the gender of the new subject. So, we'll forget the gender of the previous subject here. This is just an example to explain you what is happening here. Uh, now, let me explain you the equations which I've written here. So, f_t will be combining with the cell state later on, that I'll tell you. So, currently, f_t will be nothing but the weight matrix multiplied by h t minus 1 and x t, and plus a bias, and this equation is passed through a sigmoid layer. All right, and we get an output that is 0 and 1. Zero means completely uh, get rid of this, and one means completely keep this. All right. So this is what basically is happening in the first step.
Now let us see what happens in the next step. So the next step is to decide what information we are going to store. In the previous step, we decided what information we are going to keep. But here, we are going to decide what information we are going to store here. All right? What new information we are going to store in the cell state. Now, this has two parts. First, a sigmoid layer. This is called a sigmoid layer and which is also known as an input gate layer. Decide which values we'll update. All right? So, what values we need to update? Then there's also a tanh layer that creates a vector of the candidate values c bar of t, that will be added to the state later on. All right. So let me explain it to you in simpler terms. So whatever input that we are getting from the previous time stamp and the new input, it will be passed through a sigmoid function, which will give us i_t. All right? And this i_t will be multiplied by c_t, which is nothing but the input coming from the previous time stamp and the new input with that is passed through a tanh, that will result in c_t, and this will be later added onto our cell state. And then next step, we'll combine these two to update the states. Now let me explain the equations. So i_t will be what? Weight matrix. And then we have h t minus 1, x t multiplied by the weight matrix plus the bias, pass it through a sigmoid function, we get i_t. C bar of t, we'll get by passing a weight matrix h t minus 1, x t plus bias through a tanh squashing function, and we'll get C bar of t. All right. So as I've told you earlier, is where the next step, we'll combine these two to update the state.
Let us see how we do that. So now is the time to update the old cell state c t minus 1 with the new cell state c t. All right. And the previous steps, we have already decided what to do. We just need to actually do it. So what we'll do? We'll multiply the old cell state c t minus 1 with f_t, that we got in the first step. Forgetting the things that we decided to forget earlier in the first step, if you can recall. Then what we do? We add it to it, and c_t. Then we add it by the term that will come after multiplication of i_t and C bar. And this new candidate value scaled by how much we decided to update each state value. All right. So in the case of the language model that we were discussing, this is where we would actually drop the information about the old subject gender and add the new information as we decided in the previous steps. So I hope you are able to follow me, guys. All right. So let us move forward, and we'll see what is the next step.
Now our last step is to decide what we are going to output, and this output will depend on a cell state, but it will be a filtered version. Now, finally, what we need to do is, we need to decide what we are going to output, and this output will be based on our cell state. First, we need to pass h t minus 1 and x t through a sigmoid activation function so that we get an output that is o_t. All right. And this o_t will be in turn multiplied by the cell state after passing it through a tanh squashing function or an activation function. And why we do that? Just
To push the values between minus 1 and 1. So after multiplying OT, that is this value, and a tan CT, we'll get the output H2, which will be our new output. And that will only output the part that we decided to, whatever we have decided in the previous steps, it will only output that value. All right.
Now, I'll take the example of the language model again. Since it just saw a subject, it might want to output information relevant to a verb. And in case that's what is coming next, for example, it might output whether the subject is singular or plural, so that we know what form of a verb should be conjugated into. All right? And you can see from the, you can see the equations as well. Again, we have a sigmoid function, then that, uh, whatever output we get from there, we multiply it with tan H CT to get the new output.
All right, guys. So this is basically LSTMs in a nutshell. So in the first step, we decided what we need to forget. In the next step, we decided what are we going to add to our cell state? What new information are we going to add to our cell state? And we were taking an example of the gender throughout this whole process. All right. And in the third step, what we do, we actually combined it to get the new cell state. Now, in the fourth step, what we did, we finally got the output that we want. And how we did that? Just by passing H T minus 1 and X T through a sigmoid function, multiplying it with the tan H CT, the tan new cell state, and we get the new output. Fine, guys. So this is what basically LSTM is, guys.
Now we'll look at a use case where we'll be using LSTM to predict the next word in a sentence. All right. Let me show you how we are going to do that. So this is what we are trying to do in our use case, guys. We'll feed LSTM with correct sequences from the text of three symbols. For example, had a general and a label that is council in this particular example. Eventually, our network will learn to predict the next symbol correctly. So, obviously, we need to train it on something. Let us see what we are going to train it on. So, we'll be training our LSTM to predict the next word using a sample short story that you can see over here. All right. So, it has basically 112 unique symbols. So, even comma and full stop are considered as symbols. All right. So this is what we are going to train it on.
So technically, we know that LSTMs can only understand real numbers. All right. So what we need to do is we need to convert these unique symbols into a unique integer value based on the frequency of occurrence. And like that, we'll create a dictionary. For example, we have "had" here, that will have value 20. "A" will have value six. "General" will have value 33. All right. And then what happens? Our LSTM will create a 112 element vector that will contain the probability of each of these words or each of these unique integer values. All right. So since 6 has the highest probability in this particular vector, it'll pick the index value of 6. Then it will see what symbol is attached to that particular integer value. So 37 is attached to council. So this will be our prediction, which is absolutely correct, as a label is also council according to our training data. All right. So this is what we are going to do in our use case.
So, guys, this is what we'll be doing in our today's use case. Now I'll quickly open my PyCharm and I'll show you how you can implement it using Python. We'll be using TensorFlow, which is a popular Python library for implementing deep neural networks or neural networks in general. All right. So I'll quickly open my PyCharm now.
So, guys, this is my PyCharm and I over here, I've already written the code in order to execute the use case that we have. So first, we need to do is import the libraries. NumPy for arrays, TensorFlow, we know TensorFlow. From that, we need to import RNN and random collections in time. All right. So this particular block of code is used to evaluate the time taken for the training. After that, we have log path, and this log path is basically telling us a path where the graph will be stored. All right. So there'll be a graph that will be created, and then that graph will be launched. Then only our RNN model will be executed. Then that's how TensorFlow works, guys. So that graph will be created in this particular path. All right. And we are using summary writer. So that will actually create the log file that will be used in order to display the graph using TensorBoard. All right.
So then we have defined a training file, which will have our story on which we'll train our model on. Then what we need to do is read this file. So how are we going to do that? First is, uh, read line by line, whatever content that we have in our file. Then we are going to strip it. That means we are going to, uh, remove the first and the last whitespace. Then again, we are splitting it just to remove all the whitespaces that are there. After that, we're creating an array and then we are reshaping it. Now, in during the reshape, if you notice, this minus one value tells us the compatibility. All right. So when you're reshaping it, you need to make sure that you know, we are providing it the correct parameters to reshape it. So you can convert a 3x2 matrix to a 2x3 matrix, like right. So just to make sure that it is compatible enough, we add this minus one, and it'll be done automatically. All right, then return content.
After that, what we are doing? We are feeding in the training data that we have, training file. We are feeding in our story and calling the function readers data. Then what we are doing? We are creating a dictionary. What is a dictionary? We all know key-value pairs based on the frequency of occurrences of each symbol. All right. So from here, collections.counter.words.most_common. Most common words with their frequency of occurrence. There will be a dictionary created. And after that, we'll call this dict function. And this dict function will feed in word, and which is equal to length of dictionary. That means whatever the length of that particular dictionary is, how many times it is repeated. So we'll have the frequency as well as a symbol that will be our key-value pair, and we are reversing it as well.
Then what we are doing? We are calling it build data set and we're feeding in our training data there. This is our vocabulary size, which is nothing but the length of your dictionary. Then we have defined various parameters such as learning rate, iterations or epochs. Then we have display step and input. Now, learning rate, we all know what it is. Uh, the steps in which our variables are updated. Training iterations is nothing but your epoch, the total number of iterations. So we have given 50,000 iterations here. Then we have display step, that is 1000, which is basically your batch size. So batch size is what? After every thousand epochs, you'll see the output. All right. So it'll be processing it in batches of thousand iterations. Then we have N_input as three. Now, the number of, uh, units in the RNN cell, we'll keep it as 512. Then we need to define X and Y. So X will be our placeholder that will have the input values, and Y will have all the labels. All right. Vocab size. So X is a placeholder where we'll be feeding in our input dictionary. Similarly, Y is also one more placeholder, and it'll have a shape of none, vocab size. Vocab size we have defined earlier, as you can see, which is nothing but the length of your dictionary.
Then we defining weights as well as biases. After that, we have defined our model. All right. So this is how we are going to define it. We'll create a function RNN when we'll have X, weights, and biases. And after that, we are calling in RNN. RNN cell function. And this is basically to create a two-layer LSTM. And each layer has N hidden units. After that, what we are doing? We're generating the predictions. But once we have generated the prediction, there are N_input outputs, but we only want the last output. All right. So for that, we have written this particular line. And then finally, we are making a prediction. We are calling this RNN function, feeding in X, weights, and biases. After that, we are calculating the loss and then we are optimizing it. For calculating the loss, we are using reduce_mean_softmax_cross_entropy, and this will give us basically the probability of each symbol. And then we are optimizing it using RMS prop optimizer. All right. And this gives actually a better accuracy than Adam optimizer, and that's the reason why we are using it.
Then we are going to calculate the accuracy. And after that, we are going to initialize the variables that we have used. As we have seen in TensorFlow, that we need to initialize all the variables, unlike constants and placeholders in TensorFlow. All right. And once we are done with that, we are feeding in our values, then calculating the accuracy, how accurate it is. And then when optimization is done, we are calculating the elapsed time as well. So that will give us how much time it took in order to train our model. Then this is just to run the TensorBoard on a localhost 6006. And, uh, yeah. And this particular block of code is is used in order to handle the exceptions. So exceptions can be like, whatever word that we are putting in might not be there in our dictionary, or might not be there in our training data. So those exceptions will be handled here. And if it is not there in our dictionary, then it'll print "word not in a dictionary." All right.
So, fine, guys. Let's, uh, input some values and we'll have some fun with this model. All right. So the first, uh, thing that I'm going to feed in is "had a general." So whenever I feed in these three values, "had a general," there'll be a story that will be generated by feeding back the predicted output as the next symbol in the inputs. All right. So when I feed in "had a general," so it'll predict the correct output as "council." And this "council" will be fed back as a part of the new input. And our new input will be "a general council." So it'll be "a general council." All right. So these three words will become our new input to predict the new output, which is "to." All right. And so on. So surprisingly, LSTM actually creates a story that, you know, somehow makes sense. So let's just read it. "Had a general counselor to consider what measures they could take to outwit their common enemy, the cat. By this means, we should always know when she was about and could easily." All right. So somehow, it actually makes sense when you feed in that. So what will happen when you feed in these three inputs? It'll predict the next word that is "council." After that, it'll take "council" and it'll feed back as an input along with "a general." So "a general council" will be your next input to predict "to." Similarly, in the next iteration, it will take "general council 2" and predict "consider." For us, and this will keep on repeating.
[Music]
So now, what exactly is deep learning, right? Well, before we go and understand what is deep learning, let me quickly walk you through the chronology over here. Starting off with AI, right? AI or artificial intelligence is basically an entire thing. Okay. So what I'm trying to say here is AI is an area of computer science that emphasizes the creation of intelligence within the machine to work and react like human beings. In short, here we are trying to have a capability of machines to imitate the intelligence of human behavior. Then we have machine learning. All right. Machine learning is basically a science of getting computers to act by feeding them up previous data. Their previous data can also be referred to as historical data. So deep learning is a subset of machine learning, and here we make use of something called as neural networks. You see, neural networks are the set of algorithms and techniques which are modeled in accordance with the human brain. And neural networks are designed to solve complex and advanced machine learning problems. All right.
So what exactly is deep learning, right? Well, deep learning is a part of a broad family of machine learning methods which are based on learning data patterns in opposition to what a machine learning algorithm does. In machine learning, we have algorithms for a specific task. Okay. Here, the deep learning algorithm can be supervised, semi-supervised, or unsupervised. As I mentioned earlier, deep learning is inspired by the human brain and how it perceives information through the interaction of neurons. All right.
So, let us now see what exactly can we do with deep learning. Okay. But before we go there, so why should we choose deep learning for, you know, various tasks? So the big advantage of using deep learning is that we can extract more number of features. And when we have more number of features, and when we can work at the same time with huge amount of data, we can perceive an object like a human being does. What I'm trying to say over here is like, if you want to perform a classification task between a pen and a pencil, you'll obviously know as a human being, you'll know the difference because you have looked at a pen and a pencil a continuous number of times. And now when you're trying to actually classify, you can do it with ease. Okay. And the reason for this is because you know the features of a pen, and you know the features of a pencil. Okay. Similarly, this is how deep learning works. More the data you feed, more the dimensions it can analyze, more the dimensions it can learn. All right.
So, as I've already mentioned, one of the most popular applications of deep learning is image classification. And when it comes to image classification, it can be something as simple as classifying between two different animals to something as complicated as, you know, hiding data or trying to run automated cars using classification tasks. Okay. All right.
So, next type of application using deep learning is using on sequential data. Sequential data basically refers to something like time series data or having to understand natural language. So the reason why we call it sequential data is because here the previous word or the previous feature is dependent upon the next feature. Okay. So as you can see over here, we have "what time is it, right?" So if I just say "it is like over here, what time is," and "it" are basically features, right? And in order for you to make an analogy or to understand, obviously I have to know what has happened in the past. So in order to do this, we use something called as RNNs. Okay. And there are various versions of RNN that go around in order to overcome the disadvantages, which we'll look into in some time. All right.
So, moving on to the next application, that is GANs. GANs, which stands for Generative Adversarial Network, is an unsupervised part of a deep learning application. Some of the common applications which you can see in recent days is nothing but deep fakes and many more. Finally, coming down to performing classification and regression tasks using multi-layer perceptron. If you remember or if you're well-versed with machine learning, in order to perform classification in machine learning, we had algorithms like decision tree, random forest, or something very simple as linear regression or logistic regression. But let me tell you, what when we try to perform classification using MLP or multi-layer perceptron, we get a very high accuracy, even compared to SVM and decision trees. All right.
So now that we know what exactly is deep learning and why we use it, let's now stream down to understand how can we process natural language data using RNN. So what are RNNs, right? Well, RNN basically stands for Recurrent Neural Network, and we usually use this in order to deal with sequential data. Sequential data can be something like a time series data or a textual data of any format. So why should one use RNN, right? Well, this is because there's a concept of internal memory here. RNN can remember important things about the input it has received, which allows them to be very precise in predicting what can be the next outcome. So this is the reason why they are performed or preferred on a sequential data algorithm. Okay. And some of the examples of sequential data can be something like time series, speech, text, financial data, audio, video, weather, and many more.
Although RNN were the state-of-the-art algorithm for dealing with sequential data, they come up with their own drawbacks. And some of the popular drawbacks over here can be like, due to the complication or the complexity of the algorithm, the neural network is pretty slow to train. And as there are huge amount of dimensions here, the training is very long and difficult to do. Okay. Apart from that, the most decisive feature for RNN or for the improvement in RNN is that of a vanishing gradient. What this vanishing gradient is, is that, you know, when we go deeper and deeper into our neural network, the previous data is lost. This is because of a concept called as vanishing gradient, and due to this, we cannot work on a large or a longer sequence of data. Okay.
To overcome this, we came up with some new or upgrades to the current recurrent neural networks or RNN. Starting off with bidirectional recurrent neural network. You see, bidirectional recurrent neural network connects two hidden layers of opposite direction into the same output. With this form of generative deep learning, the output layer can get information from past future states simultaneously. So as you can see here, we have two layers over here. And as they are bidirectional, what happens is when the algorithm feels that it is kind of losing its gradients or the previous data, it can go back and get the data from the past. So why do we need bidirectional recurrent neural network? While bidirectional recurrent neural network duplicates RNN processing chain so that the input process both forward and reverse time order, thus allowing bidirectional recurrent neural network to look into future context as well.
The next one is Long Short-Term Memory. Long Short-Term Memory, or also sometime referred to as LSTM, is an artificial recurrent neural network architecture used in the field of deep learning. Unlike standard feed-forward neural networks, LSTM has feedback connections. It can not only process single data point but also the entire sequence of data. So as you can see here, from what I'm trying to say is, with LSTM or Long Short-Term Memory, it has something like, you know, we can feed a longer sequence compared to what it was with bidirectional RNN or RNN. So why is LSTM better than RNN? We can say that when we move from RNN to LSTM, we are introducing more and more control over the sequence of the data that we can provide. Whereas LSTM gives us more controllability and there's better results. All right.
So, the next type of recurrent neural network is the Gated Recurrent Neural Network, or also referred to as GRUs. You see, GRU is a type of recurrent neural network that is, in certain cases, advantageous over Long Short-Term Memory. GRU makes use of less memory and also is faster than LSTM. But the thing is, LSTMs are more accurate while using longer data sets. I'm sure by now, you might have got a hint about the trend that has led to the improvement, right? So the trend over here is, you know, the model should be capable of remembering and taking in on a longer input sequence. The game-changer part for the sequential data was developed when we came up with something called as Transformers. And this paper was something which is based on a concept called as "Attention is Everything." All right.
So let's take a look at this. The paper "Attention Is All You Need" introduces a novel architecture called as Transformers. Like LSTM, Transformers is an architecture for transforming one sequence into another, while helping other two parts, that is encoders and decoders. But it differs from previously described sequence-to-sequence models because it does not work like GRUs. Okay. So it does not implement recurrent neural networks. Recurrent neural networks until now were one of the best ways to capture the timely dependence on a sequence. However, the team presenting this paper, that is "Attention Is All You Need," proved that an architecture with only attention mechanism, does not use RNN, can improve its results in translation tasks and other NLP tasks. One of the best examples for Transformers is Google's BERT.
So what exactly is this Transformer, right? We see here, we have encoder on the top and decoder on the bottom. Both encoder and decoder are comprised of modules that can stick onto the top of each other multiple times. So what happens here is the inputs and outputs are first embedded into an N-dimensional space, since we cannot use this directly. So we obviously have to encode our inputs, whatever we are providing here. One slight but important part of this model is the positional encoding of different words. Since we have no recurrent neural network that can remember how sequences are fed into the model, we need to somehow give every word or part of our sequence a relative position, since a sequence depends on the order of the elements. Okay. These positions are added to the embedded representation of each word. All right.
So, this was the brief about Transformers. So let us now move ahead and see some of the popular language models that are available in the market. All right. So let us now start off by understanding OpenAI's GPT-3. The successor to GPT and GPT-2 is the GPT-3, and is one of the most controversial pre-trained models by OpenAI. The large-scale transformer-based language model has been trained on 175 billion parameters, which is 10 times more than any previous non-sparse language model. The model has been trained to achieve strong performance on many NLP datasets, including tasks like translation, answering questions, as well as several other tasks.
Then we have Google's BERT. BERT stands for Bidirectional Encoder Representations from Transformers. It is a pre-trained NLP model which is developed by Google in 2018. With this, anyone in the world can train either their own question-answering module with up to 30 minutes on a single Cloud TPU or a few hours using a single GPU. The company then released this, showcasing the performance of 11 NLP tasks, including the very competitive Stanford dataset questions. Unlike other language models, BERT has only been pre-trained on 250 million words of Wikipedia and 800 million words of BookCorpus, and has been successfully used as a pre-trained model in deep neural networks. According to researchers, BERT has achieved 93% accuracy, which has surpassed any previous language models.
Next, we have ELMo. ELMo, also known as Embeddings from Language Model, is a deep contextualized word representation that models syntax and semantic words, as well as their logistic context. The model, developed by Allen AI, has been pre-trained on a huge text corpus and learns functions from bidirectional models, that is, BiLM. ELMo can easily be added to their existing models, which drastically improves the features of functions across vast NLP problems, including answering questions, textual sentiment, and sentiment analysis.
Imagine the world just a few years back when technology quietly advanced in the background. Then suddenly, generative AI hit the headlines. Media channels everywhere were a buzz with articles exploring the capabilities and potential of this new wave of technology. The real tipping point came in March 2023, when OpenAI launched GPT-4, a model so advanced it could outperform 90% of human test takers on the SAT, which determines college admission in the US. However, GPT-4's capabilities extended far beyond academics. OpenAI revealed that it also excelled in fields like law and medicine, taking tests and demonstrating proficiency in knowledge-intensive domains. Within just 2 months of its release, ChatGPT, powered by GPT-4, had captivated over 100 million users. This unprecedented adoption made waves, sparking discussions on AI's role in the future of work, communication, and knowledge sharing. Yet, alongside the fascination came a fair share of concerns. Experts began to speculate about the future. Could AI evolution hit a plateau by 2030? The excitement of this advancement was tempered by reports predicting that AI could significantly impact the job market, as tools like ChatGPT found real-world application in areas as high-stakes as legal trials, where lawyers were reportedly using large language models to assist in cases.
In today's transformative era, it's clear that generative AI is reshaping our world. So what exactly is generative AI and how does it work? More importantly, what does the rise of GPT-4 mean for us and where might it lead? So, what is generative AI? Simply put, it's a type of artificial intelligence that can produce new content across various formats. Here are a few areas where it's making a huge impact. First, content creation. It is generating text, articles, and blog posts, saving hours for writers and content creators. Next, image generation. Creating visuals or art from text prompts, as seen in models like DALL-E, now on its third version. Next, coding assistance. Providing code suggestions and completions with tools like GitHub Copilot, helping developers. Next, language translation. Breaking down language barriers in real-time through advanced translation models. Then, personalized healthcare. Enhancing medical treatments by tailoring recommendations based on individual patient data. And finally, marketing and optimization. It helps establish marketing strategies in businesses. And these capabilities make generative AI a versatile technology that is reshaping industries.
So let's examine some real-world applications and see how they work. Generative AI is at the core of many revolutionary applications today. We have text generation. From producing entire articles to summarizing content, tools like GPT-4 are transforming content creation across industries. Next, language translation. AI-powered translation tools are improving cross-language communications by understanding context. Then, writing assistance. Grammarly or similar tools refine grammar, tone, and clarity, assisting professionals and students alike. Next, we have business AI models are making business insights accessible, automating support, and enhancing decision-making. Next, music generation. AI is now venturing into creativity, composing unique tracks and musical elements. Finally, machine learning models. Platforms like H2O.AI are giving access to machine learning models, allowing users without deep expertise to create powerful models.
Now that we understood the potential of generative AI, and now let's go deeper into how it actually works. So to understand how generative AI operates, let's break down the process. First, define objective. So start with a clear goal, whether it's generating text, creating an image, or assisting with code. Next, gather and preprocess data. So collect and prepare data, ensuring it's clean and structured with the model. Then, choose appropriate model. So select or design a model tailored to your needs. Sometimes building on pre-existing models. Next, train the model. Feed data into the model so it can learn patterns and build into knowledge base. Next, evaluate and refine. So fine-tune the model as needed to optimize its efficiency and accuracy. Then, test and validate. Run tests to measure performance and accuracy, ensuring it aligns with the objectives. And finally, deploy and iterate. So once ready, deploy the model and continue refining it with user feedback and data. And this cycle ensures generative AI models stay relevant and improve over time.
Now, moving on to the examples of generative AI tools. So many generative AI tools are available today, each with its own specialty. Tools like GitHub Copilot for coding, DALL-E 3 for image generation, and advanced language models like GPT are among the top players. And if you're curious to explore these tools further, check out the video link in the description covering popular generative AI examples.
Now, let's look at the growing presence of generative AI across different sectors. First, in healthcare, generative AI is projected to reach a $17.2 billion market by 2032, transforming clinical applications and healthcare systems. Next, education. The AI-driven education market is expanding, especially for students, teachers, and administrators, making personalized learning more accessible. And then, workspace. Generative AI adoption is rising, with the highest impact in marketing and tech industries, followed by sectors like consulting and healthcare. Each graph shows a significant trend, as generative AI is becoming integral in diverse fields and reshaping workplace roles.
As we look ahead, generative AI is set to transform even more areas. And here are some key impacts we can expect. In AI-driven creativity. From art to music, AI will open new creative horizons. Next, AI personalizations. Tailored user experiences will become the norm. Then, real-time generation. Real-time content generation will improve virtual assistants and automated responses. Next, AI in architecture. AI will assist architects in design and material optimization. Then, human-AI collaboration. As AI matures, humans and AI will work in tandem to maximize productivity and innovation. Then, advanced AI models. We will see even more sophisticated models pushing the boundaries of what AI can achieve. Generative AI's future is promising, so with potential benefits that will enhance lives and drive economic growth.
Now, are you guys ready to get hands-on? Stick around for a mini LLM project where we will build a YouTube video summarizer. We will show you how to extract video transcripts, and then we will use an LLM to generate summaries and build a user-friendly interface using Streamlit. Are you excited? So now let's jump in.
So first things first, we need a solid environment for our project. First, open your terminal and let's create a new Conda environment to keep everything organized. Let's run the code using VS Code. You can also use other code editors such as PyCharm, but let's use VS Code for now. Now, in the terminal, let's type the command for setting up the Conda environment in your editor. For that, run this command. Just type `create -p venv python==3.10 -y`. Here, the `-p venv` specifies the path and the environment name, while the `-y` skips prompts for a smoother install. So now, while that's setting up, let's create a few essential files. So first, we will create a `.env` file for our API keys and environment variables. Next, we will create a `requirements.txt` file for the libraries we will need, such as `youtube-transcript-api` to extract transcripts from YouTube, and `streamlit` for our front end. Then `google-generativeai` for accessing the Google Gemini API. Also, `python-dotenv` for handling environment variables, and `pathlib` for better path management.
Now, with our files in place, let's set up the Google Gemini API access. Now, head over to `makers.google.com`, and as you can see on the screen, in the top left corner, you will get the API key interface. And once you click that, it will redirect us to the API key interface. Here you can see a button which is "Create API key." So simply click on that and select your model and press "Create API key" in the existing project, and then your API key will be generated. Now, copy your API key, and once you have got the API key, open your environment file and add it there. So for that, let's create a variable `GOOGLE_API_KEY`. Over here, we will paste the API key.
So once that is done, back in the terminal, activate your new environment with the command `activate venv`. Since I've already installed it in my system, it's not taking much time. So while you are installing, it might take a little time. Now, let us install `requirements.txt` in our terminal, and for that, the command is `pip install -r requirements.txt`. So once you enter, the files present in the `requirements.txt` will get installed. Awesome.
So now let's move to the main code setup. So for that, open `app.py` and start with imports. First, let us import some important libraries. So for that, `import streamlit as st`, `from dotenv import load_dotenv`. And next, `load_dotenv`, which will load environment variables. And next, let's import `google.generativeai as genai`, and also import `os`, and from `youtube_transcript_api import YouTubeTranscriptApi`.
So let's go to the YouTube transcript API and check if you're doing it correctly, because in some systems, it doesn't work. So you can simply go here and copy this command and paste it into your terminal. Now, let's move on to configure the API key. So for that, just type `genai.configure` and inside the bracket, just type `api_key=os.getenv("GOOGLE_API_KEY")`. Now, to prompt a model, we will use a template like this. So simply type, "Please summarize this YouTube video transcript in 250 words or less, highlighting key points."
Now, moving on to extracting YouTube transcript. So for that, to fetch the transcript, we will create a function. So here in this code, as you can see on the screen, the function `extract_transcript_details` takes the YouTube URL as an input and retrieves the transcript of the video if available. First, it extracts the video ID from the URL by splitting the URL at the equal sign and taking the second part. And using `YouTubeTranscriptApi.get_transcript(video_id)`, it fetches the transcript data for the video ID. And it then combines the text from each part of the transcript into a single string. And if there is an error, like, for example, "no transcript available," it prints the error and returns `None`. So this function effectively converts a YouTube video URL into its textual transcript.
Next, let's write a function to generate the summary. Here, the `generate_gemini_content` function generates the content based on the combination of transcript text and a prompt. So it first initializes a generative AI model called Gemini. And then the model creates a content processing the combined input of prompt and transcript text. And finally, it returns the generated text from the model's response. So this function effectively uses the Gemini model to produce AI-generated content based on the specific text inputs.
Now let's move on to building the front end. Now, to connect it all with Streamlit, first add a button, label "Get Detailed Notes." So when clicked, it prompts the user to enter a YouTube link in a text input field. And it uses the `extract_transcript_details(youtube_link)` to get the video transcript. So if the transcript is successfully retrieved, it generates a detailed summary with `generate_gemini_content(transcript_text, prompt)`. So finally, it displays the summary under the heading "Detailed Summary."
Now let's give a finishing touch to our app. So for that, let's make it look great. You can add headers, and also you can give a footer, and maybe you can also add a YouTube icon to it. And even customize colors. And incorporating thumbnails if the link is valid will create an even richer experience. Now let us test the app. So finally, let's run a command `streamlit run app.py`. As you can see on the screen, our app is successfully running. And now let's check if it's running fine. So for that, let's open our YouTube and here, select a video that you want a summary of. So copy the link, paste a YouTube link into the app, and hit "Generate." As you can see on the screen, it is running, and in just a second, you will get the summary of the video. It has given us the summary of the video. Also, there is no limit to the app, and it will consider even the longer videos. And that's it. With just a few lines of code, we created a powerful YouTube summarizer using Streamlit and Google Gemini.
So, let's step back and look at the journey that brought us here. So basically, our journey starts in 1947 with Alan Turing's concept of intelligent machines. By 1961, Joseph Weizenbaum introduced ELIZA, the first chatbot. The 1980s saw the birth of recurrent neural networks, while 1997 brought Long Short-Term Memory networks to tackle sequential data. And then GANs emerged in 2014, transforming creative tasks. Fast forward to 2017, when Transformers like GPT entered the scene. By 2023, GPT-3.5 and Google's PaLM marked significant milestones. And by 2025, we are on the brink of AI breakthroughs in chemistry and genome editing.
So what exactly are these LLMs and why are they so powerful? An LLM, or Large Language Model, analyzes and understands natural language using machine learning. Examples include OpenAI's GPT, Google's Spetas, Llama. These models drive applications such as chatbots, language translation, and more by learning from extensive data to predict and generate text sequences.
But before this, there was a very famous term called language model. A language model is a machine learning model that uses probability, statistics, and mathematics to predict the next sequence of words. Suppose you have a sentence like "I have a boy who is my dash." Here, if we ask a language model to predict the next word, it considers the context provided by the words before the blank. Based on common usage patterns from its training data, it may predict words like "boyfriend," "brother," or "friend," which fit naturally. However, it's less likely to predict "colleague" or "sibling," as those words may not commonly follow this type of phrase. So, this process shows how language models predict text by calculating probabilities for each possible word based on their likelihood in context.
So, when a language model is trained on massive amounts of diverse text, it gains a wider vocabulary and more understanding of language, enabling it to make more accurate predictions. For example, if we give it a phrase like "You are a dash to me," a model trained on extensive data might suggest various fitting words. For example, "friend," "inspiration," or anything else. So, based on the sentiment or context it has learned from the data. Now, here, reinforcement learning is used to improve the model's responses over time. By giving feedback, be it positive or negative, on the responses, we help the model learn which type of responses are preferred in specific contexts. For example, if the model frequently misinterprets the tone or intent, the reinforcement learning helps adjust its productions to be more contextually appropriate and aligned with the intended meaning.
But what do these models look like under the hood? Well, LLMs are built on neural networks composed of input, hidden, and output layers. The hidden layers process information to learn complex patterns, and more layers mean the model can capture deeper insights. This structure allows LLMs to perform tasks from generating text to complex code completions.
Now, how do these layers interact and function in real time? Now, LLM is based on the Transformer, and a Transformer uses deep learning to process any information coming to it. Now, let me tell you a story of three friends. Imagine we have three characters. First is our friend. The next character is Minion Bob, and the third character is Gru. So, our friend asks Bob, "What's the price of the jet? It must be $50,000." Minion Bob isn't sure. So he goes to Gru and asks, "Is the jet $50,000?" Gru replies, "No, it's $70,000." In this back and forth, Minion Bob is like the neural network layer trying to make an accurate guess. So each time he goes back to Gru, like receiving more data or feedback, he gets corrected if his guess is wrong, leading him to refine his response. Now, after the first check, Minion Bob returns to our friend saying, "I guess it's more than $60,000." Our friend assumes it might be around $65,000 and sends Bob back to Gru to verify. Again, Gru corrects him, "No, it's actually $70,000." So this process repeats with Bob adjusting his guess each time. Eventually, he learns that the correct answer is $70,000 and updates his knowledge. So just like Minion Bob, neural networks make initial guesses based on available information. With each feedback loop, like Bob going back to Gru, the model's hidden layers adjust the parameters to refine its guesses, ultimately arriving at the most accurate prediction possible. So after getting corrected multiple times, Minion Bob's guesses improve until he knows the price is $70,000. Similarly, in a neural network, gradually learning the correct answer through training. So once the network learns, it can give accurate answers in future cases without checking every time.
Now let us move on to understand how LLMs work. LLMs begin the collection of datasets, then tokenize text and break it into manageable pieces. Using a Transformer architecture, they process the data sequence all at once, leveraging vast training data. LLMs contain millions of learned parameters that predict the text tokens and generate coherent outputs. Models often undergo pre-training for general knowledge and fine-tuning for specific tasks.
So now let's see some practical uses of LLMs. LLMs power content generation, creating anything from articles to code. They excel in language translation, enhanced search engines, personalized recommendations, code development assistance, and sentiment analysis, which also owe much to LLMs' predictive capabilities. So, guys, are you ready to use all that knowledge in coding and witness how these LLMs come together to drive innovation? Whether through developing applications, analyzing data, or building smart assistants, the gear of technology keeps turning to unlock AI's full potential.
So now let us look at our problem statement. So one of the difficulties in the healthcare industry is effectively evaluating medical pictures, such as MRIs, CT scans, and X-rays, in order to identify anomalies and illnesses. This procedure takes a lot of time and calls for specialized understanding. Automated methods must be developed to help medical personnel recognize possible health problems in medical imaging. In order to provide better patient care, a system that integrates cutting-edge machine learning models with image analysis can greatly help in the early detection of diseases, including cancer, infections, and other illnesses. So, the method uses generative AI to evaluate medical photos and generate a thorough diagnosis report based on the findings. This technology allows users to upload medical images, which the AI model then processes.
Now let us build our project on a medical image analysis application using Streamlit, Python, and an LLM of Google Gemini AI. So this app helps healthcare professionals analyze medical images, such as X-rays, MRIs, and CT scans, to detect anomalies and diseases. First, let's import the necessary libraries. So first, `import streamlit as st`. So if this is not working or showing an error, then open the terminal and write `pip install streamlit`. And `from pathlib import Path`. Next, `import google.generativeai as genai`. So we are importing Streamlit for the app interface, and Path from Pathlib for handling file paths, and Google Generative AI, which allows us to interact with the Gemini AI model.
Next, we will configure Google's Gemini API by setting up our API key. So this will allow us to connect to the AI model and generate insights from medical images. So before proceeding, let's get our API key, and we will go to the Google to generate an API key. So on your left, there is an API key option, and after clicking, you will get the "Create API" option. So just select your model and create your API key. So as you can see on the screen, just copy this API key and go back to the terminal.
So now let's configure our model. So just type `genai.configure` and inside the bracket, give `api_key=` and over here, paste the key. Now, we set up the system prompt, which defines the role of the AI model. So the prompt specifies that our AI is a medical image analysis system capable of detecting diseases like cancer, cardiovascular issues, neurological conditions, and more. So, guys, I have already researched the prompts and written here. So basically, the system prompt should be inside the triple quotes. So this prompt guides the model to analyze medical images for conditions such as cancer, fractures, infections, and more, making it a valuable tool for healthcare professionals.
Now let's configure the model settings for generating responses. We define parameters like temperature and top_p to control the creativity of the model's output. First, type `generation_config =` and inside the double quotes, we will give `temperature: 1`, then `top_p: 0.95`, next `top_k: 40`, then `max_output_tokens: 8192`, two, next `response_mime_type: "text/plain"`. So over here, the temperature 1 that controls randomness, a value of 1 gives balanced output diversity. Next, top_p 0.95 uses nucleus sampling, selecting tokens from the top 25% cumulative probability distribution for diverse responses. Next, the top_k 40, which limits token selection to the top 40 tokens based on probability, narrowing possible outputs to high-probability tokens. Next, max_output_token. This setting allows for longer responses by limiting the maximum length of the generated text to 8,192 tokens. And then we have response_mime_type, which specifies the format of the output as plain text. So for more information, read the Google Gemini documentation.
Next, we will also configure safety settings to ensure that the model doesn't generate harmful content. So for example, we block categories like harassment, hate speech, and sexual explicit content. Here we are using two things. First, categories, and then the threshold. Then copy this four times, like harassment, hate speech, and sexual explicit content.
Now let's set up the layout for our Streamlit application. So for that, we will configure the title and the layout of the page, and even add a logo to make the interface more user-friendly. So first, type `st.set_page_config(page_title="Diagnostic Analytics", page_icon="🤖")`. Now let us type `column1, column2, column3 = st.columns(3)`. Next, with `column2`, I'll be using "edureka" and "medical images." So this will show you how to set up images using Streamlit. Now type `st.image(...)`.
And inside the double quotes, let's type edureka.png and give a comma and give width is equal to 200.
Now let us copy and paste it for medical. So let's type medical.png. Here we are using Streamlit's column to center the logo and title, and this makes the app look professional and visually appealing.
Next, let's allow the user to upload medical images for analysis. So, we use Streamlit's file uploader widget to accept image files in PNG, JPG, or JPEG formats. For that, let's type upload_file = ST.file_uploader and inside the bracket, inside the double quotes, let's type "Please upload the medical images for analysis." Comma, type is equal to, so basically the image type is equal to, and inside the bracket, inside the double quotes, let's give "PNG, JPG, and JPEG."
Next, let us type submit_button = st.button and inside the bracket, let's give "Generate Image Analysis." So here, when the user uploads a file and clicks the "Generate Image Analysis" button, the model processes the image and prepares it for analysis. So, once the user submits the image, we send it to the AI model for analysis, and then the model generates a response based on the prompt and image, which we then display in the app.
So here, as you can see on the screen, we have another function. So, the `if submit_button:` which runs the code when the submit button is pressed. Next, `image_data = upload_file.getvalue()`. This actually gets the raw image data from the uploaded file. And next, we have `image_parts` where it creates a list with the image data in a structured format. Then we have the `prompt_parts`. So, this combines the image data and a text prompt for the model. So, this part of the code actually sends the image and text prompt to the model to generate a response. And then we have `st.write` which displays the model's responses in the app.
So here we use the image data and system prompts to generate content with the Gemini AI model. The result is displayed as a detailed report with insights about the medical image. Now it's time to test the code. So, open the terminal and type `streamlit run main.py`. So, once you enter, it will redirect you to our model interface. And there you go. So, the model is ready. So, here's a live demo of the app. We will upload a sample image, and the app will analyze it and provide a detailed diagnosis based on the AI model's insights. So, this is how we use Streamlit and Google's Gemini AI model to create a medical image analysis app. So, this app can help medical practitioners by offering precise and thorough analysis of medical photos.
Now it is the time for testing. So, let's take one image of any disease and test it. So, upload the image from your computer. Then we will select an image and press the generate button. So, as you can see, it's running. So, it generates a fabulous response and can help doctors in assisting their patients, saving time and money. So, this is how we built a real-time medical diagnostic helper using Streamlit, Python, and Google Gemini AI.
So, what is a transformer? Transformers operate on a concept called sequence-to-sequence learning. Essentially, they take a sequence of tokens as an input and predict the next token in the output. A great example of this is language translation. Imagine inputting "good morning" in English, and the transformer processes this and outputs the translation in languages like Japanese, Korean, or German. The key is how it efficiently processes the relationship between words.
Since we know what a transformer is, let's dig a bit deeper about them. A transformer has two primary components: encoder and decoder. The encoder identifies relationships between parts of the input sequence. Whereas the decoder uses these relationships to generate the output sequence. This division is what allows transformers to handle tasks like text translation or summarization with remarkable accuracy.
Now that we have the idea of transformers, let's discuss how they evolved. Before transformers, there were other neural networks like RNNs (Recurrent Neural Networks), invented by David Rumelhart in 1986. However, RNNs faced significant challenges. They would forget early parts of the sequence as they processed longer ones and couldn't handle dependencies efficiently. Additionally, RNNs relied on recurrence, which made them inefficient and incapable of parallelization.
Then came Long Short-Term Memory (LSTM), introduced by Hochreiter and Schmidhuber in 1997. LSTMs improved by remembering sequences for a longer duration and addressing some of the memory issues in RNNs. However, they were slow to train and difficult to manage at scale.
Finally, transformers transformed neural networks. First introduced in the landmark paper, "Attention Is All You Need," transformers addressed all the problems faced by RNNs and LSTMs. They used a completely attention-based mechanism, eliminating reliance on recurrence. This made transformers capable of remembering context efficiently, training faster, and being parallelized, enabling multitasking and significantly speeding up processes.
Now, let's discuss the attention mechanism. Think about the sentence: "This cat wants to jump on the box." The attention mechanism identifies the most relevant parts of this sentence, like "cat," "jump," and "box," and focuses on these elements while processing the data.
Now that we know how transformers have evolved, now let's discuss their architecture. A transformer consists of two main components: an encoder and a decoder, each typically consisting of six layers. Inside the encoder, there is one attention layer and one feed-forward layer. While the decoder contains two attention layers and one feed-forward layer. The magic of parallelism comes from how data is fed into the network. In the attention layer, all the words are processed simultaneously, with each word forming combinations with others in the sentence. This allows the model to capture relationships and context efficiently. After processing in the attention layer, the data is sent to the feed-forward layer, where it is learned layer by layer.
The input to the encoder and decoder are the raw input embeddings, which are numerical representations of words. On top of these embeddings, positional encodings are added to help the model understand the position and the order of each word in the sequence. If we simplify embeddings, they are essentially vector representations of words in an n-dimensional space. At the top of the architecture, there are two layers of output probabilities, converting the final output into a form that humans can understand. These inputs are represented as vectors with their length corresponding to the size of the vocabulary.
Now, what truly makes transformers unique is the inclusion of normalization layers, which normalize the output from sub-layers. Additionally, skip connections (the dark arrows in the architecture) forward critical information that bypasses self-attention or feed-forward layers directly to the normalization layers. This ensures the model does not forget important details and effectively passes vital information further into the network.
Now, moving forward, let's discuss why transformers are important. Transformers are vital because they utilize semi-supervised learning. They are trained on massive unlabeled datasets, enabling them to generalize across a wide range of tasks. Unlike older models, transformers don't need to process data sequentially. Their attention mechanism allows them to focus on the most relevant context, which significantly speeds up training. Transformers revolutionize data processing by eliminating the need to handle data sequentially, allowing for parallel processing and significantly enhancing efficiency. The attention mechanism lies at the core of transformers, enabling the model to focus on the most relevant parts of the input sequence and improving accuracy and understanding of context. Furthermore, transformers excel at providing context, ensuring that the meaning of each word or token is accurately interpreted within its surroundings. Lastly, these models dramatically speed up the training process, making them faster and more efficient compared to traditional neural networks, thus redefining AI's capabilities across diverse applications.
Now that we know why transformers are important, let's discuss some applications. We have OpenAI's GPT, a groundbreaking model that leverages the power of transformers for natural language processing tasks. Additionally, Google has developed several transformer-based models, including Vision Transformer for image recognition, BERT (Bidirectional Encoder Representations from Transformers) for understanding the context of words in a sentence, and T5 (Text-to-Text Transfer Transformer) for a wide range of text generation tasks. Microsoft has also contributed with Deep Decoding Enhanced BERT with Disentangled Attention, a model designed to improve contextual understanding and enhance NLP applications. These models demonstrate the versatility and impact of transformer architecture across various domains.
Now that we know the applications of transformers, how about checking their real-time products? Transformers have become an integral part of many real-world products that we use daily. Examples include Grammarly, which leverages transformers for advanced grammar and writing assistance; Google Search and its translation tools, powered by models like BERT and T5; and ChatGPT, OpenAI's conversational AI that relies on the Generative Pre-trained Transformer architecture. Additionally, Meta's deepfake detector uses transformer-based models for facial recognition tasks. These applications highlight how transformers have revolutionized technology, seamlessly integrating it into tools that enhance our everyday lives.
In conclusion, transformers are changing the tech world by enabling smarter, faster, and more efficient AI systems. Whether it's generating text, translating languages, or enhancing search engines, these models are the cornerstone of modern AI.
ChatGPT, ChatGPT, ChatGPT. This has been the buzzword since the day of its release, and people are going crazy about everything it can do. Microsoft will be integrating ChatGPT into Teams to automatically take notes and recommend tasks. ChatGPT has also passed the US medical and law exams, and it has got a lot of doctors, lawyers, and engineers concerned about whether it could replace them. People want to know if ChatGPT is the next step towards our evolution by replacing Google and voice assistants like Siri and Alexa. But what exactly is ChatGPT? Why is it changing everything?
Now, I want you to think of ChatGPT like Siri or Alexa, minus the voice capabilities. ChatGPT can give you detailed and contextual answers in a very humanlike manner. It can remember conversations, do math, write essays, and much more. And it does it so well that people are genuinely scared of losing their jobs.
Now, ChatGPT is not really a new concept. Microsoft and other companies have also tried this before but were nowhere near successful. OpenAI developed a model called GPT-3 using huge datasets that had a variety of information. They released it to the public, calling it "Playground," where a lot of developers used it for their daily tasks. ChatGPT has been another implementation of this. OpenAI took a year to make this model faster and more accessible to the general public. And when they released it in November 2022, the crowd went nuts. The site gained more than a million users in just 5 days. And to give you some perspective, this number is bigger than Netflix, Twitter, Facebook, and even Instagram.
All that said, I want to let you know how everyone is using ChatGPT to simplify business operations. For developers, ChatGPT has been a blessing. Since ChatGPT can write code, provide code templates, and fix errors, most of the problems that developers usually run into have been fixed. As a result of this, productivity has greatly increased, and companies are getting more results. It's the same thing with content development. Videos like this one traditionally take a lot of time and effort to be made. We have to think about how best to explain things so that you, the viewer, can easily understand it, while making sure that the content is SEO-friendly and extremely engaging. ChatGPT has also helped us give you better, more optimized content while reducing our workload. By the way, did you know that ChatGPT can explain stuff better than most university professors? I guess it's time we contemplated our learning methods.
Marketing and sales have also been much easier than before. Think about it like this: if you want to make a customized sales pitch, all you need to do is enter the details. ChatGPT can provide a customized sales pitch for each individual lead. How convenient is that? And it doesn't just end there. ChatGPT can also be integrated with accounting and data analytics platforms. So you don't need to know the formulas directly. All you need to have is data, and you just need to type what you want to do.
If all this doesn't blow your mind just yet, then know that psychologists and psychiatrists are using ChatGPT to help and counsel their patients. Now, if you are questioning whether it would really be helpful, then let me tell you, it can. I just had a 30-minute conversation with ChatGPT about my dog, and it seemed way more interested in him than most of the people I usually talk to about this.
All that said, if you want to start using ChatGPT, then all you need to do is open `chat.openai.com`. This is the URL of ChatGPT. And the moment you open this, it will ask you to create an account. Once you do that, you will land on a web page similar to this. Let's start with a simple question about programming. This is just a random question on Stack Overflow which I will be using for this demonstration: "How to check if an array includes a value in JavaScript?" The user has also provided some code and has said that he only knows how to do it like this: "Is there a better way?"
And once we ask ChatGPT the same thing, boom! ChatGPT is on it. It finds a solution and provides the best answer it could think of. If you look at the question, while the solution might be simple, the complexity of conditions and following my instructions properly is quite hard. ChatGPT eases through it and also provided a sample snippet which I can directly copy and paste into my editor. But this is not the only thing. It can also give you complete guides on how to do something. Like, for example, let me type: "Using Python, help me fetch data for Nifty Bank for the past 3 years." ChatGPT is already providing explanations and code of how this can be done, and it's pretty damn cool.
Now, remember that ChatGPT remembers conversations and can have future answers based on it. So now, if I type: "Great, how can I create an AI model that can predict values using this data?" It immediately understands what I want and provides an accurate answer. The answer might not be everything that you hope for, but at least now you know where to start from.
Another use case for ChatGPT that we talked about was content creation. To do this, let's create a new chat thread. This makes sure that the previous conversations don't affect the current conversation. If we start without a new chat thread, ChatGPT might think that we are trying to write content about stocks and machine learning. We don't want that now, do we? So, let me ask ChatGPT to give me blog ideas for ChatGPT itself. And we already have multiple blog ideas for ChatGPT. We can then ask it to write a story based on some option. We can then say, "I like the third option. Could you create a storyline for it?" And we can see that ChatGPT immediately starts generating a storyline based on my option. It includes an introduction and sections of what my blog will contain.
Similarly, we can also create a sales pitch. Let's just say: "Create a motorbike sales pitch for a person with the following details." And then I have listed down a few details like name, product, age, country, and source. And once we submit this prompt, we can see that ChatGPT immediately starts writing a beautiful sales pitch. You can even use the sales pitch directly if you want.
Now, if we dive too deep into this subject, then this video will take forever to finish. So, if you are interested in knowing more about how to use ChatGPT, then check our course on ChatGPT, which will not only cover the basics of ChatGPT but also the advanced and complex usage for different scenarios.
But before we end this video, I definitely need to make you aware of a few limitations. The first thing is that it may occasionally provide incorrect information. You should know that ChatGPT is a relatively new application, and it needs some time to improve. The current version of this is a free research preview, meaning that they have released it to test the application. This statement could also mean that we might see an improved paid version of this in the future. There's a lot of rumor going about that already. The second limitation is that it may occasionally produce harmful or biased content. This is something that we have seen in all chatbots in the past, and ChatGPT has reduced the occurrence, but it's still not perfect. But the biggest drawback is that the model was trained on data collected before 2021. This means that it has very limited knowledge about current affairs. So, make sure to check the facts before drawing conclusions.
And with all this, I want to know what you think of ChatGPT. How do you think it will look in the future? And what do you think about the future of automation and AI? I personally think that we can expect radical changes in all segments. Imagine voice assistants being as intelligent as ChatGPT. And if it is updated with real-time information, things would be much more convenient than now. You could shop online with ChatGPT's recommendations, find good restaurants, meet new people, plan trips, and much more. Let us know what you expect during this change in the comments down below.
What is prompt engineering for code generation? Prompt engineering is a process that creates specific prompts or instructions for AI language models to generate code snippets or scripts. It involves defining objectives, using relevant keywords, providing examples, and being specific and concise. This process enhances the accuracy, efficiency, and relevance of code generation tasks performed by AI models. Understanding how prompts LLMs can lead to more powerful and efficient applications.
Now, let's understand the principles of prompt engineering. These principles provide basic guidelines that can be consistently used to increase prompt engineering effectiveness when it comes to code generation tasks. First one is: clarify objective and understand task or goal. And second one is: utilize keywords and specificity. And the third one is: provide examples for context. Next one is: conciseness and relevance. And the last one is: encourage creativity and adaptability.
Let's understand them in brief. First one is: clarify objective and understand task or goal. Here, understanding what you want from the code output, including inputs, outputs, evaluation criteria, and any constraints or difficulties, is essential before creating a prompt. It is easier to develop prompts that accurately guide the model towards a desired outcome where there is clarity.
And the second one is: utilize keywords and specificity. Including appropriate keywords in a prompt helps to communicate the specifics of the task to the model. By avoiding inconsistency and using clear language and instructions, you can make sure that the model produces precise and focused code rather than requesting a function to process data. For example, be explicit about the kind of data and expected actions.
And the next one is: provide examples for context. The model can better understand the expected output format and functionality by referring to examples. Prompts are made more understandable by providing specific examples of the desired code, which helps the models to understand the task. This will increase the probability of producing code that is in alignment with expectations.
And next one is: conciseness and relevance. The prompts need to be brief, concentrating on relevant information that is crucial to the assignment and excluding unnecessary information. Code generation is made more efficient by the model. Simplified decision-making processes and clear and concise prompts reduce confusion. Removing irrelevant information lowers noise and improves timely efficiency.
And the last one is: encourage creativity and adaptability. Flexible prompt formulation allows for experimentation with various strategies, such as linguistic structures, constraints, or templates. Continuous improvement in prompts by tracking model outputs and iteratively improving prompts. This design creativity maximizes code generation results by adapting to various scenarios.
Now, how is prompt engineering employed in various tools for code generation? First one is: GitHub Copilot. Based on given prompts, GitHub Copilot suggests completions, creates documentation, and suggests new features to help developers write code.
And second one is: Google AI Codey. This helps developers to write code in a variety of programming languages by using prompt engineering, producing code snippets in line with predefined prompts, facilitating a variety of tasks from web development to natural language processing and machine learning.
And the third one is: OpenAI Codex. Codex assists developers with coding tasks in a variety of domains, such as data science, web development, and game development, by utilizing prompt engineering. It creates code in a number of programming languages based on the precise instructions that users provide.
Now, prompt engineering is crucial for guiding AI models in generating code accurately. Let's explore practical examples across different complexity levels. First, we will try for a universal starter code, that is, a "Hello, World!" program in Java. So, for that, we have to give a prompt like: "Generate a Hello, World! program in Java." Hit the enter button. You will get the code for "Hello, World!" program in Java.
Next, we will look into the basics one, that is, the sum of two numbers. For this, we have to give a prompt like: "Generate a function in Python that takes two numbers as input and returns their sum." So, now you can see here the Python code is generated with a function name `add_numbers`. So, now we have seen how easy-level tasks work. Let's move on to the medium-level examples. We'll try to turn commands into code. First, we'll create a list of countries and then generate a list of their respective capitals. After that, merge the lists to create a dictionary mapping each country to its capital. To get this, we have to give a prompt as: "Generate Python code to create a list of countries and generate their corresponding capitals and combine them into a dictionary, mapping countries to capitals." Hit the enter button. Here is the generated Python code for the given prompt. Here you can see the country names and also generated corresponding capitals. So, this function `generate_capitals` gave us a dictionary mapping countries to capitals.
Next, complete function or next line. If I want to complete a function to calculate the area of a rectangle, then we have to give a prompt as: "Write a Python function named `calculate_rectangle_area` that takes two parameters as length and width and returns the area of the rectangle." And to include the comments, we just have to type it as: "Include comments to explain each step of the function." So, in this code, you can see the function name which we have given in the prompt, that is `calculate_rectangle_area`, with the parameters `length` and `width`. You can also see the comments here. If you use this code, you can get the area of the rectangle value.
Next, we will try for MySQL code generation. To get the names of employees, we have to give a prompt like: "Generate a MySQL query to retrieve the names of all employees from the employee table." This also generated a MySQL query with a `SELECT` statement. You can see here.
The last example is about how to get an explanation of generated code. To get an explanation of a code, you have to give a prompt as: "Provide a line-by-line explanation of the Python function named `calculate_factorial` which takes a parameter `n` and returns the factorial of `n`." Here, in this code, you can see a function name so-called `calculate_factorial`, which we mentioned in our prompt. After that, you can see a line-by-line explanation of the code. This is how you can get the explanation of a code.
Now, let us understand what LangChain is and why it is a valuable tool for building AI applications. You must be aware of popular applications such as GPT and Gemini. These applications utilize APIs, and GPT uses OpenAI's API, while Gemini operates through the Gemini API. To process prompts, they leverage models like GPT-3.5, GPT-4, PaLM, and Gemini 1. Additionally, there are other advanced models such as Llama, Gemini, Cohere, Cloud v1, Falcon, PaLM, GPT-4, and GPT-3.5. LangChain is a framework designed to help developers build flexible and powerful AI-driven applications by integrating and utilizing these diverse models effectively.
But why exactly do we need LangChain? You must be thinking, if LangChain is this important, then why do we need LangChain? So, let's break down this question using some real-world examples. So, imagine simply asking an LLM a prompt and getting an answer. That's easy. But what happens when the complexity increases? For example, let's say you're working with data from SQL databases, CSV files, PDFs, or Google Analytics, and you need the model to write code, perform searches, or send emails. Handling such intricate workflows manually can get overwhelming. This is where LangChain steps in. It simplifies the process by offering components like document loaders, text splitters, vector databases, prompt templates, and tools. So, these help you assemble tasks such as document summarization, question-and-answer systems, or even advanced workflows like Google searches or customer support automation.
Let's visualize this process with a diagram. Here's how it works. So, first, you load a document like a CSV file using a document loader. Then, use a text splitter to divide it into smaller chunks, and then store those chunks into a vector database and add a prompt template to guide the model. And finally, use an LLM like GPT-4 or Llama to perform tasks like searching the web or automating workflows. And LangChain also offers chains that will help you assemble components to achieve a single task, such as summarization, and an agenda to figure out what each component must do, like password, customer services, etc.
Now that we understand LangChain's core components, now let's explore how it streamlines the LLM application lifecycle. So, it typically involves three key stages. First is the development, where you build and test your application. Then, productionization, where the system is fine-tuned for real-world use. And finally, deployment, where the final product is launched for users. So, LangChain simplifies this lifecycle, allowing you to focus on building without worrying about the underlying complexity.
Now, let's take a step back and understand the role of APIs in powering these LLM applications and how LangChain effectively integrates them. In all these applications and models, one thing is common: that is, they use APIs. So, now let's discuss APIs. APIs act as intermediaries that enable different systems to communicate with each other. For example, they allow apps like Swiggy or Blinkit to display your delivery driver's location in real-time.
So, now let's look at the steps to explain APIs and API keys. So, apps like Zomato, Swiggy, and Blinkit use APIs to show the location of your delivery driver. So, these apps don't communicate directly with Google Maps but follow a layered process involving servers and security mechanisms. First, the app sends a request to the Google Maps API. Then, the API forwards the request to Google servers. Then, the servers validate the request with the system. So, once approved, the response follows back through the servers, APIs, and finally to the app. So, previously, apps like Swiggy allowed login using phone numbers. Now, they use APIs for login via platforms like Google or Facebook. So, this demonstrates the versatility of APIs in enabling seamless user interactions.
To prevent misuse, APIs require API keys, which are unique identifiers for secure access. So, these keys authenticate requests and ensure that only authorized users can interact with the APIs. Next, security systems closely monitor API usage to detect and prevent misuse. This ensures that APIs remain safe and functional for their intended purpose. And this step simplifies the explanation of how APIs and API keys work in real-world applications. So, this is how LangChain leverages APIs to connect your LLM applications with external tools, making them versatile and secure.
Now that we understand the role of APIs, so let's explore some real-world applications of LangChain. So, what can you build with LangChain? Here are a few applications. First application we have is customer support. So, customer support for your shopping websites to interact with customers. Next, conversational chatbots for helping you study. Content generation tools for blogs or social media. We also have question-answering systems for knowledge bases and then document summarizers for legal or academic content. LangChain simplifies AI development by integrating LLMs with various data sources and tools. Its applications are vast, from chatbots to document summarization.
So, let's examine a practical example to see LangChain in action. All right. In today's data-driven world, understanding and effectively using SQL queries is crucial for managing and analyzing large datasets. However, beginners and even experienced users often need help with complex SQL queries, their syntax, and how they work. This creates a barrier to efficiently interacting with databases and limits their potential to solve real-world problems. To address this challenge, we propose a SQL query fetcher application that leverages the Gemini AI, Python, and Streamlit to simplify SQL learning and usage. The application allows users to input or select a query, generates the SQL syntax, and provides a detailed explanation of its components and functionality. This tool bridges the gap between technical understanding and real-world database operations, empowering users with an intuitive and interactive SQL learning experience.
Let's jump right into the code. So, the first step is setting up your dependencies. Here, we import Streamlit for the user interface and then Google Generative AI for using Gemini. So, first, import `streamlit as st` and next, import `google.generativeai as genai`. So, to get this API, you have to go to the Google Gemini API key and here, click on "Get a Gemini API key" in Google AI Studio. And then, once you scroll, there is a button on the left called "Create API." Now, click on it and select your model here, and let's copy it. And now, let's go back to our VS Code editor and paste it here. So, to paste, let's type `google_api_key` and inside the double quotes, let's paste it. And now, let's type `genai.configure` and inside the bracket, let's keep it as `api_key=` and give it as `google_api_key`. Now, let's type `model = genai.GenerativeModel` and inside the bracket, let's keep it as `gemini-pro`. So, we use the Google Gemini API to generate SQL queries dynamically. So, make sure to configure your API keys securely.
Now, let's display the Streamlit layout code. Now, let's set up the app's user interface. So, we use Streamlit to create an interactive page where users can input plain English queries and get SQL code in return. So, we write `st.set_page_config` and inside the bracket, let's give it as `page_title=` and inside the double quotes, let's give a title as "Edureka SQL Query Generator" and give a comma, and let's type it as `page_icon=` equal to, and inside the double quotes, let's keep it as "robot."
Now, let's put some images. So, I'm using Edureka image and SQL logo. And also, to make them center, we will type it as `column1, column2, column3 = st.columns(3)`. Next, let us type `with column2:` and let's type it as `st.image` and inside the bracket, let's give the image address and then `width=200`. Now, let us add another image. So, let's copy the same and give the other image address. Our layout includes a title, logo, and text input box to keep the interface simple and intuitive.
So, here's where the magic happens. So, when a user clicks the "Generate SQL Query" button, we format their input into a prompt for the Gemini model to generate SQL code. So, let's create a template by writing `template =` and inside the triple quotes, let's type it as "Create a SQL query snippet using the below text. Next, let us also give `text_input` and we'll also type 'I just want a SQL query.'" Now, let's type the response. So, type `response = model.generate_content(template.format(text_input=text_input))` and let's type `sql_query = response.text.strip().lstrip().rstrip()`. So, the AI generates the SQL query, and we clean up the output for display.
So, once the SQL query is ready, we take it a step further by generating a sample expected output and a clear explanation of the query. Now, let's type the logic for showing explanation and output. So, let's type `st.markdown` and inside the bracket, we will give HTML tags. So, first, `div style="text-align: center;"`. So, `text-align: center;` and next, let's give `<h1>SQL Query Generator</h1>`. And let's close the `</h1>` tag. Next, let's open `<h3>` tag and write "I can generate SQL queries for you." And let's close the `</h3>` tag. And inside the `<h4>` tag, let's type it as "with explanation as well." Now, close the `</h4>` tag. And let's open the paragraph tag, which is the `<p>` tag. And inside the `<p>` tag, let's type it as "This tool allows you to generate SQL queries based on your data." Now, let us close the `</p>` tag. Also, close the `</div>` tag. Now, to make the markdown visible, let us type `unsafe_allow_html=True`.
Now, let's write `text_input = st.text_area("Enter your query here in plain English.")`. Now, let us give a submit button. So, for that, let us type `submit_button = st.button("Generate SQL Query")`. Now, `if submit_button:` colon, write it with `st.spinner("Generating SQL query")` and inside the bracket, let's keep "Generating SQL query". And then, let's create a template and inside the triple quotes, let's type it as "Create a SQL query snippet using the below text." Now, using the above template, we will write three more templates for SQL query, which are `text_input`, and then `sql_query`, which will include `expected_output`, and also `explanation_output`.
Now, to merge all the templates together, we will make a container. So, we will write `with st.container():`. So, it's a function. And let us also write `st.success("SQL query generated successfully.")`. Also, we will give "Here is your query below." Next, `st.code(sql_query, language="sql")`. Now, let us give once again `st.success("Expected output of this query will be")`. And now, let us keep it as `st.markdown(output)`. Once again, `st.success("Explanation of this SQL query.")`. Next, let us give `st.markdown(explanation)`.
So, over here, this shows a green success message indicating the SQL query was generated successfully. And next, the `show_sql_query` displays the SQL query as a formatted code block, highlighting it as SQL. Next is the display expected output. So, this provides a success message for the query's expected output, followed by `st.markdown(output)`, which displays the expected output in markdown format, and followed by `st.markdown(output)`, which displays the expected output in the markdown format. So, now this line of code introduces an explanation, and `st.markdown(explanation)` displays that it is in markdown format for clarity. So, this makes the tool valuable for both learning and debugging SQL.
So, now let's see it in action. So, open the terminal and let us type `streamlit run` and give your file name. Now, as you can see on the screen, your SQL query generator is ready to go. Now, let's test it. So, for that, here I will input a prompt asking for a query which is: "Give me the query for `CREATE TABLE`." Now, let's click on "Generate SQL Query" and as you can see, it's running. So, let's wait for it to generate. So, as you can see on the screen, the app generates a SQL query, expected output, and even a plain English explanation in seconds. So, how cool is that, right? And that's it. Our SQL query generator, powered by LangChain, Gemini API, and Streamlit, is complete. So, this project is perfect for simplifying SQL learning and enhancing productivity.
Now, let's begin by understanding what RAG is and how it works. RAG is a hybrid approach in artificial intelligence that combines retrieval systems with generative models to produce highly accurate, contextually relevant responses. It bridges the gap between factual accuracy and natural language generation. Now, let's understand it with the help of a diagram. So, it's a hybrid approach involving artificial intelligence that combines a retrieval system with a generative system to produce highly accurate responses.
Now that we know what RAG is, so let's explore why it is crucial for large language models and see a real-world example. So, RAG addresses several limitations of traditional LLMs. It mitigates hallucinations by grounding responses in factual retrieved data by dynamically accessing up-to-date information. RAG stays relevant in rapidly changing domains. It improves accuracy and relevance by fetching specific relevant documents during inference. By outsourcing factual knowledge retrieval, RAG enables smaller, more efficient models, and it can adapt to domain-specific knowledge bases for specialized applications. Additionally, RAG provides explainability by showing the retrieved documents or data sources, increasing trust and transparency.
Now, let us see some of the use cases. So, without RAG, the sentence would be: "When was the last Mars rover launched?" So, this is just an incorrect response. So, with RAG, the sentence would be: "Dynamically retrieved from NASA's database: The Perseverance rover was launched on July 30, 2020."
Now that we have seen why RAG is important, so let's dive into how it works. Well, RAG operates in a three-step process. A user submits a query, which triggers the retrieval stage. Here, a retriever searches a database or knowledge base using tools like BM25 to fetch the most relevant information. The retrieved data is then fed into a generative model like GPT or T5, which processes it and generates a coherent, contextually grounded natural language response. Now, let's take an example here. The query is: "Who wrote 1984?" Retrieve would be fetching a document containing "George Orwell wrote 1984." Now, generative response would be: "The author of 1984 is George Orwell." This hybrid approach makes RAG ideal for real-world applications like chatbots and knowledge systems.
Now that we understand how RAG works, let's explore some of its real-world applications. RAG's versatile applications span various domains. In knowledge management, it can summarize large databases or documentation, aiding corporate teams. Legal and compliance tasks benefit from RAG's ability to answer queries based on case law and regulations. While in healthcare, it can support medical professionals by summarizing research papers and guidelines. Education and e-learning can leverage RAG for virtual tutoring, providing detailed explanations based on textbooks and research papers. Interactive virtual assistants like Alexa and Siri can utilize RAG to generate accurate and informative responses to user queries, such as news headlines or product recommendations. RAG's unique ability to combine retrieval and generation makes it essential for tasks demanding both factual accuracy and fluent natural language responses.
Now, let's compare Retrieval Augmented Generation with traditional AI models across three features. First, we have factual accuracy. RAG provides highly accurate responses by using real-time data, whereas traditional models may give less accurate answers and may give errors. Next is the context adaptability. So, here, RAG adapts quickly to new queries using live data, whereas traditional models offer fixed answers based only on pre-trained knowledge. Next, we have knowledge updates. RAG is easy to update; just change its data source. Whereas traditional models need retraining, which takes time. Then, we have scalability. Whereas traditional models are limited by data size and training data. And then, we have use cases. RAG is great for tasks like legal advice or customer support. Whereas traditional models work well for creative writing or casual queries. So, here I want to conclude that RAG is ideal for knowledge-based tasks needing accuracy and flexibility, while traditional models are better for creative users.
While RAG offers significant advantages, it's essential to acknowledge its limitations. So, let's discuss the challenges and future of RAG. So, the first challenge is latency. RAG systems can suffer from latency issues, especially when dealing with large datasets or complex queries. Next is data quality dependency. The quality of the generated responses heavily depends on the quality of the underlying data. The next challenge is complex integration. Integrating RAG systems with existing applications and infrastructure can be challenging due to the need for data synchronization, query optimization, and model management. And finally, scalability issues. As RAG systems become more complex and are deployed at scale, they can face scalability issues. This includes handling increased query loads, maintaining data freshness, and ensuring model performance.
Now, while RAG faces limitations, its potential is undeniable. So, now let's discuss RAG's future. The future of RAG holds immense potential. It will power dynamic, real-time applications like news summarization, financial analytics, and live sports commentary. RAG will be customized for specific domains like healthcare, law, and science through integrations with specialized knowledge bases. Advances in retrieval models and compression techniques will reduce latency to enhance efficiency. RAG will expand to handle multimodal data, enabling use cases like multimedia question answering. Additionally, RAG will facilitate personalized AI assistants and improve transparency and explainability by attributing sources and providing clear explanations.
Now, let us move on to generating an AI project using RAG. So, imagine you're working with a massive library of documents. You need a way to quickly search and answer questions based on the content. So, manually flipping through pages takes time and effort. Wouldn't it be great to have a system that retrieves relevant information and answers your questions directly within those documents? So, that's where our Streamlit app comes in. This app utilizes the power of natural language processing and advanced retrieval techniques to turn your complex document collections into a powerful question-and-answer system. So, let's take a look at the code behind this app. This app will allow users to ask questions about a collection of PDFs and get answers directly from the documents using the power of natural language processing.
Now, first, let's create a virtual environment. Now, in the terminal, let's type the command for setting up the environment in your editor. For that, let's type `conda create -n myenv python=3.10 -y`. Okay, let's enter. In this command, `-n myenv` specifies the path and the environment name, while `-y` skips the prompts for a smoother install. Now, while that's setting up, let's create a few essential files. So, let's activate your new environment with the command `conda activate myenv`. And forward slash. So, as you can see, our environment is ready.
Now, let's import libraries. So, let's start by importing the libraries we will need. In the first line, we will import `streamlit as st`. This gives us access to all the functionalities of Streamlit for building our web app interface. So, next, we will import `os` for various operating system functionalities. After that, now we will import libraries from LangChain, which is a framework for building NLP pipelines. So, we will use these for tasks like text splitting, document chain creation, prompting, retrieval, and more. So, we will explain each library in detail as we use them. So, let's type `from langchain_community.chat_models import ChatGroq` and next, we will type `from langchain.text_splitter import RecursiveCharacterTextSplitter`. So, let's type `RecursiveCharacterTextSplitter`. Again, let us type `from langchain.chains.combine_documents import create_stuff_documents_chain`. Again, from `langchain.chains.retrieval import create_retrieval_chain`. Next, import `os` and `load_dotenv` from `dotenv`. From `langchain_community.vectorstores import FAISS`. This will help us to create a vector index for efficient document retrieval. So, let us type `from langchain_community.vectorstores import FAISS`. Similar imports will follow for other functionalities like document loading and generating embeddings. But we will introduce them as they appear in the code.
But before this, go to the Groq Cloud website and on your left, you have the API key option. Select and create your API key and copy this. And if you want to check your model, then go to the playground and at the top right corner, click on the Llama model and check. There are so many of them, latest also. So, choose your model and generate your free API. Now, go to the terminal and paste it in a `.env` file using the variable `GROQ_API_KEY`. Now, again, go to the Gemini AI Studio. On your right, you have the "Create API" option. So, select your model and create your API key. Now, now copy the key and paste it into your environment variable, that is, the `.env` file, using a variable `GOOGLE_API_KEY` and paste it here.
Now, we will load environment variables from a `.env` file that will securely store our API keys. So, for that, use `dotenv` to achieve this. Let's type `from dotenv import load_dotenv`. Also, let's type `load_dotenv()`. Next, we use `os` to retrieve the `GROQ_API_KEY` and `GOOGLE_API_KEY` from the environment variables. `os.getenv`. So, let's type `groq_api_key = os.getenv("GROQ_API_KEY")`. And inside the function, let's type it as `groq_api_key`. And in the next line, let's type `google_api_key = os.getenv("GOOGLE_API_KEY")`. So, here, these keys are required to use specific NLP services.
Now, let us write code for displaying the app title and images. So, for that, load your image. Since I'm using Edureka image name `edureka.png` along with the app title "Edureka Document Question and Answer," we
will use st. dot image and st.title for this purpose. So for that let us type st dot image. So inside the double quotes let us keep the image name and comma width is equal to 200. And let us also keep the title. So for that st.title title and let us type edure document question and answers.
Now the next step is to initialize chat group and prompt template. Now it's time to interact with the lang chain group API. So initialize the chat group object using group API key and specify the llama 38b 8192 which is the language model we will be using for our NLP task. So for that let us type lm equal to chat group and inside the bracket give the group API key equal to group API key comma we will give the model name as well. So for that type model name equal to and inside the double quotes give the model name. So here we are using llama 3 - 8b - 819.
All right. Now let us define a prompt template using chat prompt template. So this template ensures that AI responses are based on the context provided and user questions. So keeping answers accurate and concise. So for that we will type prompt equal to chat prompt template dot from template and inside the bracket let us paste the prompt. So here we have the prompt which says please answer the question strictly based on the provided context. Also ensure the response is accurate, concise and directly addresses the question.
Now let's create function for embedding vectors. For that let's define vector embedding function. So type def vector embedding function and give colon. Next in the next line give if and under the double quotes give vectors not in stession state colon then type st dot session state dot embeddings equal to Google generative AI embeddings give equal to Google generative AI embeddings AI caps and inside In the bracket give the model equal to and inside the double quotes let us type models forward slash embedding -001.
Now make a folder where you will load your PDF. So I am creating ed PDF and paste your PDF here. Now set the session. So that is let us type stession state.loader loader equal to py pdf directory loader. Here inside the double quotes let us paste the path of the pdf. Next is the data ingestion. For that let us type st dot session_state dod docs equal to std dot session state dot loader dot load function. So this particular line of code is for data injection and here this particular line is for document loading.
Next let us type st. dot session state dot text spplitter and give equal to recursive character text splitter and inside the bracket let us give chunk size and mention the size here I'll give equal to,000 and comma chunk_lap is equal to 200. Now here these are for the chunk creation. Now let us type ST dot session state dotfal documents equal to st dot session state dot textsplitter dotsplit documents. Now inside a bracket let us give stession state dot docs. Let us give bracket colon 20. So this line of code is for splitting.
Now let us type ST dot session state dot vectors equal to fis dot from documents and inside the bracket let us again type st dot session state dot final documents comma type st dot session state dot embeddings okay so this line of code is for vector OpenAI embeddings.
Now to input field for question let us type prompt one. So give prompt one equal to st.ext import and let us type here enter your questions from any document.
Now to create a button to load embeddings let us type if st.button button and inside the function let us give under the double quotes load edure DB give colon and in the next line let us type vector embedding function next type s dots success and the message would be edure db is ready for queries if the question is asked then if prompt one is true then type document_chain equal to create underscore star document chain and inside the bracket let us give lm comma prompt and in the next line to retrieve let us type retriever equal to st dot session state dot vectors retriever function and in the next line let us type retrieval_chine equal to createore or retrieval_chain and inside the bracket let us give retriever, document chain.
Now to measure response time let us type start equal to time dot process time function. So for response type response equal to retrieval_chain dot invoke and inside the bracket give input prompt one and in the next line let us type response time equal to time dot process time function start.
Next let us write code to display the response. So for that let us type st dot markdown and inside the bracket let us keep it as AI response. Now in the next line let us type st dots success and inside the bracket give response and give answer inside the single quotes and in the next line write st dot write. Let us type f inside the bracket and and inside the flower bracket let us type as response time colon 2f and seconds.
Now moving on let us write the code to display similar documents in an expander. So for that let us type with st.expander inside the bracket under double quotes type document similarity search results and give colon. And in the next line let us type st dot markdown and inside the bracket let us type it as below are the most relevant document chunks. So type below are the most relevant document chunks. Give colon inside the bracket. Close the double quotes and come to next line. Here let us type for i, comm, dot in en in enumerate and inside the function give response dot get context. Now in the next line let us type st dom markdown and inside the bracket keep f and let us give the html tag which is div class is equal to card and open the p tag and inside the p tag let us keep doc dot page_content and now close the p tag. Now let us close the div tag as well. Now let us come outside the triple code and give comma and type unsafe_all allow html equal to true. So you can also add inline styles and HTML tags and also icons and emojis to make your application fabulous for the user.
Now it's time for testing. For that open your terminal and write streamlit run and give your file name. So once you enter and there we go. Here's our document question and answer loader. Now select the question from the PDF you have loaded in the file and ask your loader. So as you can see this is my PDF. So I'm going to copy some question from here. So let me just copy this. Okay. Once copied. So I'm going to paste it here. So I'm going to click on the load edure DB. So guys as you can see it provides an answer in context given in the PDF. So this is our answer that it has generated. So that's all we have used simple Python code and languin techniques of rack and some inline HTML insights.
Now deep learning is a hot buzz word nowadays and has firmly put down its root in a vast multitude of industries that are investing in fields like artificial intelligence, big data and analytics. Now for example, Google is using deep learning in its voice and image recognition algorithms whereas Netflix and Amazon are using it to understand the behavior of their customers. Even Tesla is using deep learning in their self-driving cars. Now imagine how much potential deep learning has in revolutionizing the world and the way we work around things. Now this growth of deep learning and its application has led to the growth of deep learning jobs even for example we have the data scientist, we have machine learning engineer, we have artificial intelligence engineer and much more roles. So let's begin our deep learning interview question and answer session and understand what are the typical questions which are being asked in deep learning interview.
So the first and foremost question what any deep learning interviewer asks is the basic understanding or the relationship between machine learning, artificial intelligence and deep learning. So basically artificial intelligence is a technique which enables machine to mimic human behavior and machine learning is a subset of artificial intelligence technique which uses statistical methods to enable machines to improve with experience. Now deep learning on the other hand is a subset of machine learning which makes the computational of multi-layer neural network feasible. It uses neural networks to simulate humanlike decision making.
Coming to the second question do you think deep learning is better than machine learning and if so why? Now though machine learning algorithms the traditional machine learning algorithms solve a lot of our cases but they are not very useful while working with highdimensional data. Now that is where we have a large number of inputs and outputs. For example, in case of handwriting recognition, we have large amount of inputs where we have different types of input associated with different types of handwriting. Now, another major challenge is to tell the computer what all features it should look for that will play an important role in predicting the outcome as well as to achieve better accuracy while doing so. So these are some of the few shortcomings what machine learning have and deep learning overcomes all of these shortcomings.
Coming to our third question which is what is a perceptron and how does it work? Now actually our brain has subconsciously trained itself to do a lot of things over the years. Now the question comes of the brain. Well deep learning uses the concept of artificial neuron that functions in a similar This is a sub field of machine learning concerned with algorithms inspired by the structure and the function of the brain called artificial neural networks. Now if you structure of a biological neuron, it has dendrites which is used to receive inputs. Now these inputs are summed in a body and using the axon it is passed on to the next biological neuron. Now similarly a perceptron receive multiple inputs, applies various transformations and functions and provides an output. Now a perceptron is a linear model used for binary classification. It models a neuron which has a set of inputs each of which specific weight. Now the neuron computes some function on these weighted inputs and then finally it provides the output. As we know that our brain consists of multiple connected neurons called the neural network. We can also have a network of artificial neurons called the perceptron to form a deep neural network.
Coming to the next question, what is the role of weights and biases? Now for a percept they can put while the weights determine the slope of the classifier line the bias allows us to shift the line towards left or right. And normally bias is treated as another weighted input with the input value X. In our case, if you have a look at a typical perceptron, what it receives is a set of input not input which it gathers. So weights are an additional input which it takes and according to that it computes and provides us to the next question which is what exactly are activation functions. So activation function translates the inputs into outputs and it uses a threshold to produce an output. So the activation function decides whether a neuron should be activated or not by calculating the weighted sum and for bias with it. And the purpose of the activation function is to introduce a nonlinearity into the output of a neuron. There can be many activation functions like linear or identity. We have the tan, we have ReLU and softmax. These are a lot of active functions which are being heavily used in the deep learning industry. So one should actually know about all of these things.
Now talking about perceptron, our next question what an interviewer might ask is explain the learning of a perceptron. So basically a perceptron has four steps of learning. So the first steps is initializing the weights and threshold. So just now as I mentioned initializing the weights and the threshold so neuron so that it can activate a neuron by calculating the weighted sum and further adding the bias in and all. This is the first step and the second step is providing the input and calculating the output using the activation functions and according to that what we do is the third step involves updating the weights. Now once a particular perceptron learns something it has to update the weights. so that it could learn much more things in a new manner and the next step what comes is just repeat the step number two and three which is provide the input and calculate the output and then update the weights accordingly. Now if you have a look at the equation here we have WJ T + 1 that equals WJ of T + N of T - YX. The WJ of T + 1 is the updated weight whereas WJ of T is the old weight, D is the desired output. Y is the actual output and X is the input. So this is the equation of the learning of a perceptron.
Now the next question is what is the significance of a cost or a loss function? So a cost function is a measure of accuracy of the neural network with respect to a given training sample and expected output. It provides the performance of a neural network as a whole. And in deep learning the goal is to minimize the cost function. So for that we use the concept of gradient descent.
Now which brings us to the next question which is what exactly is gradient descent and what are its various types. So gradient descent is an optimization algorithm which is used to minimize some function by iteratively moving in the direction of the steepest descent as defined by the negative of the gradient. Now think of it as a bowl in which you start from any particular point and the goal is to reach the bottom of the bowl which is the gradient descent. So there are uh three types of gradient descent which are the stochastic batch and the mini batch. So stochastic gradient descent it uses only single training example to calculate the gradient and update the parameters accordingly. Whereas the batch gradient descent calculates the gradients for the whole data set and performs just one update at each iteration. Now mini batch gradient descent is a variation of the stochastic gradient descent where instead of single training example mini batch of samples are used and it is one of the most popular optimization algorithm.
Now if we talk about mini batch gradient descent one might ask is what are the benefits of the mini batch gradient descent or how is it useful than the others. Now the mini batch gradient descent is more efficient when compared to the stoastic gradient descent and the generalization is done by finding the flat minima which allows to help approximate the gradient of the entire training set which help us to avoid the local minima. Now this is why mini batch gradient descent is considered or is preferred over the regular gradient descent algorithm which is the stoastic gradient descent.
Now one might ask what are the steps for using a gradient descent algorithms. So first of all what you need to do is initialize some random weight and bias and after that you need to do is pass an input through the network and get values from the output layer. Next what you're going to do is calculate the error between the actual value and the predicted value. Now this can be done in number of ways. Now the next step involves is to go to each neurons which contributes to the error and change its respective values to reduce the error which is basically our goal is to reduce the cost of any particular function or any particular model. So after that what you do is reiterate until you find the best weights of the network and you find the lowest cost of the particular network.
So one might ask you to write any gradient descent program or write the pseudo code of any gradient descent program. So what you need to do first of all what we do is define the parameters which are the weights the hidden weights the weight output the bias hidden and the bias output. We define a function std with arguments as cost the parameters what we have discussed and the learning rate. Now what we do is then we then define the gradients of our parameters with respect to the cost function. So here we use the theano library to find the gradients and we import theo s and finally iterated through all the parameters to find out the updates for all the possible parameters. So you can see that we use vanilla gradient descent here and as you can see it returns the updates and what we do is update the parameters and the cost in this particular equation. The ultimate goal of any grain descent algorithm is to minimize the cost.
Now talking about perceptron what are the shortcomings of a single layer perceptron. So well there are two major problems. Now first of all is that the single layer perceptron cannot classify nonlinear separable data points and the second point is that the complex problems that involve a lot of parameters cannot be solved by a single layer perceptron. Now consider an example here and the complexity which arises when the parameters are involved to take a decision by a marketing team. So first of all we have the categories which are the email, direct, paid, refer program or the organic and inside these category we have subcategories which are the Google, Facebook, LinkedIn, Twitter, we have Instagram now and inside that we have the type of subcategory which are the search ads, remarketing ads, interested ads, lookike ads and again if we do a subdivision we have the parameters to consider which are the customer acquisition cost, we have the money spent, the click rate or the lead generated the customer generated and the time taken to become a customer. So one neuron cannot take in so many inputs and that is why more than one neuron would be used to solve this problem.
Now which brings us to the question what is a multi-layer perceptron. So a multi-layer perceptron or MLP is a class of feed forward artificial neural network and it is composed of more than one perceptron. They are composed of an input layer to receive the signal. An output layer that makes a decision or the prediction about the input and in between these two an arbitrary number of hidden layers that are the true computational engine of any multi-layer perceptron.
Now one might ask what are the different parts of any multi-layer perceptron or a neural network. So first of all what we have are input nodes. So uh the input nodes provide information from the outside world to the network and are together referred as the input layer. No computation is performed in any of the input nodes. They just pass the information to the hidden layers. Now hidden nodes have no direct connection with the outside world. Hence the name hidden. And what they do is they perform computation and transfer the information from the input nodes to the output nodes. Now a collection of hidden nodes forms the hidden layer and while a network will only have a single input layer and a single output layer it can have zero to n number of hidden layers and a multi-layer perceptron has more than one hidden layer. Now if we talk about output nodes, the output nodes are collectively referred to as the output layer and are responsible for the computation and transferring information from the network to the outside world and hence and hence they are also responsible for the prediction.
Now coming to our next question, what exactly is data normalization and why do we need it? Now data normalization is a very important pre-processing step which is to normalize the data. The data should not be either left skewed or right skewed. I rescale the values to fit in a specific range to assure the better convergence during back propagation. And in general it boils down to subtracting the mean of each data point and dividing by its standard deviation so that we get a normally distributed data and it makes computation easy in terms of the back propagation in case of neural networks. So this is a very important part of any deep neural network.
Now talking about deep neural networks so or neural networks in general. Coming to our next question which is now what is better the deep networks or the shallow ones and why? Now both the networks be it shallow or deep are capable of approximating any function. What matters is how precise that network is in terms of getting the result. Now a shallow network works with only a few features as it cannot extract more. But a deep network goes deep by computing efficiently and working on more features or the parameters. Now deeper networks are able to create deep representation. At every layer the network learns a new more abstract representation of the input and hence deep neural networks are better than the shallow ones.
So what exactly is weight initialization in a neural network? Now as we saw we had weight initialization in perceptron. So weight initialization is one of the very important steps. A bad weight initialization can prevent a network from learning but good weight initialization can help it in giving quicker convergence and a better overall error. Now biases can be generally initialized to zero. The rule for setting the weights is to be close to zero without being too small because every time the weight is being multiplied to the inputs, the result gets smaller and smaller.
Now talking about neural networks, what is the difference between a feed forward and a back propagation neural network. Now a feed forward neural network is a type of neural network architecture where the connections are fed forward that is they do not form cycles. The term feed forward is also used when you input something at the input layer and it travels from the input to the hidden and from the hidden to the output layer. The values are fed forward. Now back propagation is a training algorithm which consists of two steps majorly. The first one is feed forwarding the values and the second one is to calculate the error and propagate it back to the earlier layers. So to be precise forward propagation is a part of back propagation algorithm but it comes before the back propagation.
So one might ask the question which is one of the most important questions is that what are the hyperparameters in a neural networks and name a few of these hyperparameters. So hyperparameters are the variables which determine the network structure that is for example the number of hidden units and or the hidden layers and the variables which determine how the network is trained for example the learning rate. Now there are two types of hyperparameters usually one are the network parameters which are associated to the network. In that case we have the number of layers we have the network weight initialization. We have the activation function and in the training parameters we have the learning rate, we have momentum, number of epochs, we have the batch size and much more. Now a lot of hyperparameters also differ when we work along with different types of neural networks. So as in CNN we get extra parameters to work on when considering CNN which are the convolutional neural networks and sometimes we have to deal with less number of hyperparameters. It all depends upon the type of neural network which you are using.
So uh which brings us to the next question is that explain the different hyperparameters related to networking and training. So in training we have first of all we have the number of hidden layers. So hidden layers are the layers between the input and the output layers as we just discussed and many hidden units within a layer with regularization technique can increase the accuracy as smaller number of units may cause underfitting. Now another important aspect is network weight initialization. So ideally it may be better to use different weight initialization schemes according to the activation function used on each layer. Mostly uniform distribution is used or the normal distribution. Now if we talk about activation function so they are also used to introduce nonlinearity to the models. They're also used to introduce nonlinearity to the models which allows deep learning models to learn nonlinear prediction boundaries. Now generally the rectifier activation function or the relu is the most popular.
Now if we talk about the training parameters. So these were the network parameters which have to be initialized to a deep neural network before the training begins. And just before the training we have the training parameters which are the learning rate. So the learning rate defines how quickly a network updates its parameter. Low learning rate slows down the learning process but converts smoothly. A larger learning rate speeds up the learning but may not converge as smooth as a low learning rate. Usually a decaying learning rate is preferred so that we get the best of both worlds and we get the best expected output. Now another hyperparameter is momentum. So momentum helps us to know the direction of the next step with the knowledge of the previous step. Now it helps to prevent oscillation and a typical choice of momentum is between 0.5 to 0.9. Now if we talk about the number of epochs. So epoch is basically iterations. So number of epochs is the number of times the whole training data is shown to the network while training. So increase the number of epochs until the validation accuracy starts decreasing your invening accuracy is increasing. So that results in sometimes overfitting. And if we talk about the batch size, so mini batch size is the number of subsamples given to the network after which parameters update happen. So a good default for batch size might be 32 or 16 64. It depends upon the size of you know the data you have. It can be any arbitrary number but it's always better to have it in the power of two right.
So while we were talking about overfitting which brings us to our next question which is what exactly is a dropout. So dropout is a regularization technique to avoid overfitting which is to increase the validation accuracy thus increasing the generalization power. Now generally use a small dropward value of 20% to 50% of the neurons with 20% providing a good starting point and a probability too low has minimal effect and a value too high results in underarning by the network. So first of all what you need to do is use a large network and you are likely to get better performance when the dropout is used on a larger network giving the model more of an opportunity to learn independent representation.
Now our next question is in a neural network you notice that the loss does not decrease in the few starting epochs. So what could be the possible reason for this to happen? Now the correct answer is the reason for this could be the learning rate is low first of all or it might be the regularization parameter is high or it can be it is stuck at local minima. So it might take certain iteration to go out of that local minima and finally reach the lowest point. So it might happen in some cases that it is stuck at local minima. So another approach to that sort of problem must be initiated at that particular point of time.
Now talking about deep learning, one might ask to name you a few deep learning frameworks which are being used in the industry. So first of all the foremost and the most amazing deep learning library is the tensorflow. Followed by we have cafe. We have the Microsoft cognitive toolkit which is the CNTK. We have Torch or PyTorch which is giving a good battle or it's standing out from the crowd and people are sometimes preferring PyTorch over TensorFlow. Now MXNet is another deep learning framework. We have Chainer and we have KAS. Now kas as you know can be integrated with theo as well as tensorflow and kas has been considered one of the best or the simplest deep learning framework when it comes to deep learning.
Now one might ask what exactly are tensors? So tensors are nothing but a de factor for representing the data in deep learning. What I meant to say that tensors are just multi-dimensional arrays that allows you to represent the data having higher dimensions. In general deep learning you deal with highdimensional data sets where dimensional refer to the different features present in the data set. So what you need is a multi-dimensional sort of array or a data structure what you could say. So that's what exactly tensor is and in fact the name tensor flow has been derived from the operations which the neural network perform on tensor. So it's literally a flow of tensil.
Now talking about TensorFlow one might ask since it's the most popular deep learning framework and companies prefer people having the knowledge of TensorFlow and been working on it. So what are the few advantages of TensorFlow? So first of all it has the platform flexibility. It is easily trainable on CPU as well as GPU for distributed computing. Now, TensorFlow has auto differentiation capabilities and it has advanced support for threads, asynchronous computation and it is a customizable and open-source framework. And most importantly, if we talk about the latest TensorFlow 2.0 which has just been released. So those come up with a lot of interesting features and it has adopted KAS as its highle API fully. So that the coding aspect of it is much simplified and eager execution is now by default so that you do not have to write loads and loads of line of code. And if you want to know more about TensorFlow 2.0 and why it's the best deep learning framework in the industry right now, just go ahead and check our TensorFlow 2.0 video. I'll leave the link in the description box below. Go check it out guys and understand how exactly is it better from the previous version and why it is the best deep learning framework right now.
Now talking about computational graphs one might ask what exactly they are. So well a computational graph is a series of tensorflow operations arranged as nodes in the graph. Now each node takes zero or more tensors as input and produces a tensor as output. Now basically one can think of a computational graph as an alternative way of conceptualizing mathematical calculation that take place in a tensorflow program. Now the operations assigned to the different nodes of a computational graph can be performed in parallel thus providing better performance in terms of computation.
So one might ask what exactly is a convolution neural network? Now a convolution neural network or CNN or connet is a class of deep learning neural networks which is most commonly applied to analyzing the visual imagery. So CNN use a variation of the multi-layer proceptron uh designed to require minimal processing. Now one might ask the next question if you are going for an interview which requires you to work with a lot of images or videos. So in that case CNN's are very much used. So having a good knowledge of CNN is always better in that case.
So the next question what we have here is what are the various layers of CNN. Now there are four layered concepts everyone should understand in convolutional neural networks are first the convolutional layer, the second is the ReLU layer and finally we have the pooling layer and finally we end up with the full connectedness or the full connected layer.
Now if we talk about CNN we have to talk about RNN also. So one might ask what exactly is RNN? So RNN or the recurren networks are a type of artificial neural networks which are designed to recognize the patterns in the sequence of data such as text genomes handwriting the spoken word numerical time series data from sensors the stock markets and the government agencies. So recurrent neural networks use back propagation algorithm for training but it is applied for every time stamp. It is commonly known as back propagation through time which is BTT.
Now our next question is what are some issues faced while training an RNN? So recurrent neural networks use back propagation algorithm as I just mentioned for training but it is applied for every time stamp and there are some issues with back propagation such as vanishing gradient or the exploring gradient where the gradient vanishes or it is too much to handle which brings us to the next set of questions.
The first of which is what exactly is a vanishing gradient and how is it harmful. Now when we do back propagation that is move backward in the network and calculating gradients of loss which is the error with respect to the weights the gradients tend to get smaller and smaller as we keep on moving backward in the network. Now this means that the neurons in the earlier layers learn very slowly as compared to the neurons in the later layers in the hierarchy. Now the earlier layers in the networks are the slowest to train. Now how is this harmful? So earlier layers in the neural networks are important because they are responsible to learn and detect the simple patterns and are actually the building blocks of our neural network. Obviously if they give improper and inaccurate result then how can we expect the next layer and the complete network to perform nicely and produce the accurate result. So the training process takes too long and the prediction accuracy of the model will decrease.
Now another question here arises is what exactly is then exploding gradient descent. Now this is just the opposite of vanishing gradient descent. So exploding gradients are a problem when large error gradients accumulate and result in very large updates to the neural network model weights during training. So the gradients are used during the training to update the network weights. But typically when this process works best is when this weights are small and controlled when the magnitudes of the gradients accumulate and the unstable network is likely to occur. Now which causes a poor prediction and results or even a model that reports nothing useful whatsoever. So vanishing gradient and the exploding gradient are two problems which occur while the back propagation happens in a recurrent neural network.
So our next question is what are LSTM? So long short-term memory which are the LSTM is an artificial recurrent neural network architecture used in the field of deep learning and unlike standard feed forward neural networks the LSTM has feedback connection that make it a generalpurpose computer. Now it can not only process single data points but also the entire sequence of data. They are a special kind of RNN or the recurren neuron network which are capable of learning long-term dependencies.
Now one might ask what are capsules in a capsule neural network. So capsules are vector or what we can say an element with a size and a direction specifying the features of the object and its likelihood. Now these features can be any of the instantiation parameters like the pose. We have the position, size, orientation, deformationation, velocity, the albido which is the light reflection, hue, texture and much more. A capsule can also specify its attributes like angle and size. So it can represent with the same genic information. Now just like a neural network has layers of neurons, a capsule network can have layers capsules. So there could be higher capsules representing the group of objects or the capsules below them. Now this helps in getting deeper knowledge of a particular object or a particular data set and having the knowledge from different aspects or different angles.
So the next question arises is explain autoenccoders and its uses. So an autoenccoder neural networks is an unsupervised machine learning algorithm that applies the back propagation setting the target values to be equal to the inputs. So autoenccoders are used to reduce the size of our inputs into smaller representation and if anyone needs the original data they can reconstruct it from the compressed data.
Now one might ask the question how does autoenccoder differ from PCA? So an autoenccoder can learn from nonlinear transformation with a nonlinear activation function and multiple layer. It does not have to learn tense layers. It can use convolution layers to learn which is better for video image and series data. It is more efficient to learn several layers with an autoenccoder rather than learn one huge transformation with the PCA. An autoenccoder provides a representation of each layer as the output and can take the use of pre-trained layers from other model to apply transfer the learning to enhance the encoder or the decode. So these are few of the reasons why autoenccoders are better from PCA as we know both of them perform the same task which is mostly dimensionality reduction.
Now give some real life examples where autoenccoders can be applied. So the first of all we talk about dimensionality reduction or the first thing that should pop up in your mind is dimensionality reduction. So the recrossected image is the same as our input image but with reduced dimensions. Now it helps in providing similar image with reduced pixel value and it can be used in various areas where we have limited storage or we have limited processing power. So when there is a high input or an image or a data with high dimension or which has higher values pixel values it can compress and provide the same image with a lower pixel value. Right? Or colors are used for converting any black and white picture into a colored image. Believe it or not and depending on what is in the picture it is possible to tell what the color should be. Now feature variation. If we talk about feature variation, it attracts only the required features of an image and generates the output by removing any unnecessary noise or unnecessary interruption. And if we talk about dnoising image, the input seen by an autoenccoder is not the raw input but a stochastically corrupted version. A dinoising autoenccoder is thus trained to reconstruct the original input from the noisy version.
Now talking about autoenccoders, one might ask about the different layers of the autoenccoders. So basically an autoenccoder consists of three layers which is the encoder, we have the code and the decoder. Which brings us to the next question explain the architecture of an autoenccoder. If you talk about the three layers which are encoder, code and decoder. So if we talk about encoder, this part of the network compresses the input into a latent space representation. Now the encoder layer encodes the input images as a compressed representation in a reduced dimension and the compressed image is the distorted version of the original image. Now coming to the middle part which is the code. So this part of the network represents the compressed input which is fed to the decoder. It is basically the channel. And if you talk about decoder, this layer decodes the encoded image back into the original dimension. And a decoded image is a lossy reconstruction of the original image and it's reconstructed from the latent space representation.
Now one might ask what exactly is bottleneck in an autoenccoder and why is it used. Now the layer between the encoder and the decoder that is the code is also known as bottleneck. So this is a well-designed approach to decide which aspect of the observed data are relevant information and what aspects can be discarded. It does this by balancing two criterias. The first the compactness of the representation measured as the compressibility and second it retains some behaviorally relevant variables from the input.
Now one might ask are there any variation of autoenccoders? Surely there are. So there are convolutional autoenccoders, we have sparse autoenccoders, we have deep autoenccoders, we have contractive autoenccoders. All of these autoenccoders have a different structure or the different code layer. If you talk about the convolutional autoenccoder, we have the convolutional CNN algorithm sort of structure in that particular autoenccoder with encoder in one side. We have the convolution layers, the ReLU layer, the pooling layer inside it. And then finally we have the decoding layer.
So another question what might pop into the interviewer's mind is what are deep autoenccoders? So the extension of simple autoenccoders is a deep autoenccoders. The first layer of the deep autoenccoders is used for first order features in the raw input. Now the second layer is used for second order features corresponding to the patterns in the appearance of the first order features. So the deeper layers of the deep autoenccoders tend to learn even higher order features. So a deep autoenccoders is composed of two symmetrical deep belief networks. first four or five shallow layers representing the encoding half of the net and the second set of four or five layers that make up the decoding half. Interesting, right?
So another important topic in deep learning are the restricted post machine. So one might ask what exactly is an RBM or restricted W machine. So RBM is an undirected graphical model that plays a major role in deep learning framework in recent times and it is an algorithm which is used for dimensionality reduction. Not only that it is used for classification, regression, collaborating filtering, feature learning and topic model. So when we talk about RVM being useful for dimensionality reduction, another question might arise is how does RBM differ from the autoenccoders? So autoenccoders is a simple three-layer neural network where output units are directly connected back to the input units. Typically the number of hidden units is much less than the number of visible ones and the task of training is to minimize an error or the reconstruction that is find the most efficient compact representation for the input data. So RBM share a similar idea but it uses stochastic units with particular distribution instead of deterministic distribution. The task of our training is to find out how these two set of variables are actually connected to each other. One aspect that distinguishes RBM from the autoenccodor is that it has two biases. The hidden bias helps the RBM produce the activations on the forward pass while the visible layer biases help the RBM learn the reconstruction on the backward pass.
Now this brings us to the final question of our deep learning interview is that what are some limitations of deep learning? I bet you weren't thinking of this one but there are some limitations. So deep learning usually requires large amounts of training data and deep neural networks are easily fooled. Now the success of deep learning are purely empirial. Deep learning algorithms have been criticized as uninterpretable black boxes because one important thing about deep learning is that you do not specify what you are looking for. Right? The algorithm learns on its own. So that is one of the shortcomings of deep learning and deep learning thus far has not been well integrated with prior knowledge. So a lot of people still don't feel it as a way to solve their problem as a way to approach to their problems because a lot of people don't understand what exactly is deep learning, how it works, how to initialize all of the variables which are the hyperparameters per se. These all things are some limitations of deep learning as of now and we hope by the time technology advances people get to know more about what deep learning is how artificial intelligence can be achieved through deep learning they'll be more open to this and all of these limitations will be laid off.
So guys, uh that's it from my side and I hope you got to know a lot about deep learning interview questions which might help you in cracking the interviews and landing a great job as data scientists, machine learning engineers or artificial intelligence engineers as a matter of fact. And one important thing what I would like to say is that data scientist role are somewhat you know industry specific or I would say if you are working in healthcare you should know about healthcare industry too rather than just knowing about the datas and the numbers. So if you're working in suppose imagery so you should know about images you should know what you're dealing with. So a good knowledge of the particular industry which you're working for will also provide you a great advantage over other competitors. And since you know a lot of these stuff with this video, I'm sure you might be able to land a job a great job in any of these industries. And with this we have come to an end to this full course on NLP. If you enjoyed listening to this full course, please be kind enough to like it and you can comment on any of your doubts and queries. We will reply to them at the earliest. And do look out for more videos and playlist and subscribe to Idora's YouTube channel to learn more. Thank you for watching and happy learning.