{ "metadata": { "name": "", "signature": "sha256:f4e65c2c06028e2ad8722efdc60c130adbdc9f31bb64184465e88387fec0d09d" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "![jpeg](../galleries/feature-selection/1.jpg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Feature Selection is one of thing that we should pay attention when building machine learning algorithm. For all features available, there might be some unnecessary features that will overfitting your predictive model if you include it. So choose best features that's going to have good perfomance, and prioritize that. On the other hand, we may don't see that some features, that we really need is missing (in the case of using EDA). What we want to do is synthesize a new feature based on features available.\n", "" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "A new Enron Feature" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![jpeg](../galleries/feature-selection/2.jpg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's take a look at the enron dataset. For those who doesn't know about Enron dataset, read my [earlier post](http://napitupulu-jon.appspot.com/posts/datasets-questions.html). Create a new feature, based on the Enron dataset, is as follows:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Use your human intuition. Ask a question about the dataset that you have. Then if the feature for the answer that you asked is not available, try if you can synthesize based on existing features.For example, POI (person of interest) send email with other POI at higher rate than general population.\n", "* Code up the new feature. If you have choose by intuition what could be happening that your features can't explaining, then you can create by code it.\n", "* Visualize. See if you can understand what's this data all about. if you synthesized it, did you get the information as expected?\n", "* Repeat the process. Try to synthesize from other features and see if you can get best features out of it." ] }, { "cell_type": "code", "collapsed": false, "input": [ "%%writefile poi_flag_email.py\n", "\n", "#!/usr/bin/python\n", "\n", "###\n", "### in poiFlagEmail() below, write code that returns a boolean\n", "### indicating if a given emails is from a POI\n", "###\n", "\n", "import sys\n", "import reader\n", "import poi_emails\n", "\n", "def getToFromStrings(f):\n", " f.seek(0)\n", " to_string, from_string, cc_string = reader.getAddresses(f)\n", " to_emails = reader.parseAddresses( to_string )\n", " from_emails = reader.parseAddresses( from_string )\n", " cc_emails = reader.parseAddresses( cc_string )\n", "\n", " return to_emails, from_emails, cc_emails\n", "\n", "\n", "### POI flag an email\n", "\n", "def poiFlagEmail(f):\n", " \"\"\" given an email file f,\n", " return a trio of booleans for whether that email is\n", " to, from, or cc'ing a poi \"\"\"\n", "\n", " to_emails, from_emails, cc_emails = getToFromStrings(f)\n", "\n", " ### list of email addresses of all the POIs\n", " poi_email_list = poi_emails.poiEmails()\n", "\n", " to_poi = False\n", " from_poi = False\n", " cc_poi = False\n", "\n", " ### to_poi and cc_poi are related functions, which flag whether\n", " ### the email under inspection is addressed to a POI, or if a POI is in cc\n", " ### you don't have to change this code at all\n", "\n", " ### there can be many \"to\" emails, but only one \"from\", so the\n", " ### \"to\" processing needs to be a little more complicated\n", " if to_emails:\n", " ctr = 0\n", " while not to_poi and ctr < len(to_emails):\n", " if to_emails[ctr] in poi_email_list:\n", " to_poi = True\n", " ctr += 1\n", " if cc_emails:\n", " ctr = 0\n", " while not to_poi and ctr < len(cc_emails):\n", " if cc_emails[ctr] in poi_email_list:\n", " cc_poi = True\n", " ctr += 1\n", "\n", "\n", " #################################\n", " ######## your code below ########\n", " ### set from_poi to True if #####\n", " ### the email is from a POI #####\n", " #################################\n", "\n", " \n", " if from_emails and from_emails[0] in poi_email_list:\n", " from_poi = True\n", " \n", "\n", " #################################\n", " return to_poi, from_poi, cc_poi" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Overwriting poi_flag_email.py\n" ] } ], "prompt_number": 5 }, { "cell_type": "code", "collapsed": false, "input": [ "# %%writefile new_enron_feature.py\n", "\n", "#!/usr/bin/python\n", "\n", "import os\n", "import sys\n", "import zipfile\n", "from poi_flag_email import poiFlagEmail, getToFromStrings\n", "\n", "data_dict = {}\n", "\n", "with zipfile.ZipFile('emails.zip', \"r\") as z:\n", " z.extractall()\n", "\n", "for email_message in os.listdir(\"emails\"):\n", " if email_message == \".DS_Store\":\n", " continue\n", " message = open(os.getcwd()+\"/emails/\"+email_message, \"r\")\n", " to_addresses, from_addresses, cc_addresses = getToFromStrings(message) \n", " \n", " to_poi, from_poi, cc_poi = poiFlagEmail(message)\n", " \n", " for recipient in to_addresses:\n", " if recipient not in data_dict:\n", " data_dict[recipient] = {\"from_poi_to_this_person\":0}\n", " else:\n", " if from_poi:\n", " data_dict[recipient][\"from_poi_to_this_person\"] += 1\n", "\n", " message.close()\n", "\n", "for item in data_dict:\n", " print item, data_dict[item]\n", " \n", "####################################################### \n", "def submitData():\n", " return data_dict" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Writing new_enron_feature.py\n" ] } ], "prompt_number": 3 }, { "cell_type": "code", "collapsed": false, "input": [ "%%writefile visualize_new_feature.py\n", "\n", "import pickle\n", "from get_data import getData\n", "\n", "def computeFraction( poi_messages, all_messages ):\n", " \"\"\" given a number messages to/from POI (numerator) \n", " and number of all messages to/from a person (denominator),\n", " return the fraction of messages to/from that person\n", " that are from/to a POI\n", " \"\"\"\n", "\n", "\n", " ### you fill in this code, so that it returns either\n", " ### the fraction of all messages to this person that come from POIs\n", " ### or\n", " ### the fraction of all messages from this person that are sent to POIs\n", " ### the same code can be used to compute either quantity\n", "\n", " ### beware of \"NaN\" when there is no known email address (and so\n", " ### no filled email features), and integer division!\n", " ### in case of poi_messages or all_messages having \"NaN\" value, return 0.\n", " fraction = 0.\n", " if all_messages == 'NaN':\n", " return fraction\n", " \n", " if poi_messages == 'NaN':\n", " poi_messages = 0\n", " \n", " fraction = float(poi_messages)/float(all_messages)\n", "\n", " return fraction\n", "\n", "\n", "data_dict = getData() \n", "\n", "submit_dict = {}\n", "for name in data_dict:\n", "\n", " data_point = data_dict[name]\n", "\n", " print\n", " from_poi_to_this_person = data_point[\"from_poi_to_this_person\"]\n", " to_messages = data_point[\"to_messages\"]\n", " fraction_from_poi = computeFraction( from_poi_to_this_person, to_messages )\n", " print fraction_from_poi\n", " data_point[\"fraction_from_poi\"] = fraction_from_poi\n", "\n", "\n", " from_this_person_to_poi = data_point[\"from_this_person_to_poi\"]\n", " from_messages = data_point[\"to_messages\"]\n", " fraction_to_poi = computeFraction( from_this_person_to_poi, from_messages )\n", " print fraction_to_poi\n", " submit_dict[name]={\"from_poi_to_this_person\":fraction_from_poi,\n", " \"from_this_person_to_poi\":fraction_to_poi}\n", " data_point[\"fraction_to_poi\"] = fraction_to_poi\n", " \n", " \n", "#####################\n", "\n", "def submitDict():\n", " return submit_dict\n" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Writing visualize_new_feature.py\n" ] } ], "prompt_number": 7 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Beware of Bugs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's hear Katie and the team when facing bug in Enron dataset." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When Katie was working on the Enron POI identifier, she engineered a feature that identified when a given person was on the same email as a POI. So for example, if Ken Lay and Katie Malone are both recipients of the same email message, then Katie Malone should have her \"shared receipt\" feature incremented. If she shares lots of emails with POIs, maybe she's a POI herself." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here's the problem: there was a subtle bug, that Ken Lay's \"shared receipt\" counter would also be incremented when this happens. And of course, then Ken Lay always shares receipt with a POI, because he is a POI. So the \"shared receipt\" feature became extremely powerful in finding POIs, because it effectively was encoding the label for each person as a feature." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We found this first by being suspicious of a classifier that was always returning 100% accuracy. Then we removed features one at a time, and found that this feature was driving all the performance. Then, digging back through the feature code, we found the bug outlined above. We changed the code so that a person's \"shared receipt\" feature was only incremented if there was a different POI who received the email, reran the code, and tried again. The accuracy dropped to a more reasonable level." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We take a couple of lessons from this:\n", "\n", "* Anyone can make mistakes--be skeptical of your results!\n", "* 100% accuracy should generally make you suspicious. Extraordinary claims require extraordinary proof.\n", "* If there's a feature that tracks your labels a little too closely, it's very likely a bug!\n", "* If you're sure it's not a bug, you probably don't need machine learning--you can just use that feature alone to assign labels." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Getting Rid of Features" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![jpeg](../galleries/feature-selection/3.jpg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Noisy. Every features have their own noise. So to ignore feature can have less noisy.\n", "* Overfitting. We already talked about this, It will overfit the data, because the model with unnecessary feature include. The feature that might not be important, or redundant, or not increase the performance our model.\n", "* This could be feature where for example distance, return two feature (maybe the people gathering the data is different) which is cm and feet. This is the same, and could potentially break your algorithm in mathematical process. Either delete one of them, or maybe create new feature, join them to one feature and exclude both.\n", "* Slow down your training process. We may want to reduce our feature so it doesn't take days or maybe years at worst." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Features vs Information" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![jpeg](../galleries/feature-selection/4.jpg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Features is not the same thing as Information. Is not technically that if you have a lot of features, then you would have gained much information. What do we want is Information. And if minimum amount of features would give you enough information, then you should do that. It's like quantity vs quality. compare to have many quantity(features), you only want to pick those with quality(information)" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Univariate Feature Selection" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And here I'm repeating what Instructor at Udacity stated:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are several go-to methods of automatically selecting your features in sklearn. Many of them fall under the umbrella of univariate feature selection, which treats each feature independently and asks how much power it gives you in classifying or regressing.\n", "\n", "There are two big univariate feature selection tools in sklearn: SelectPercentile and SelectKBest. The difference is pretty apparent by the names: SelectPercentile selects the X% of features that are most powerful (where X is a parameter) and SelectKBest selects the K features that are most powerful (where K is a parameter).\n", "\n", "A clear candidate for feature reduction is text learning, since the data has such high dimension. We actually did feature selection in the Sara/Chris email classification problem during the first few mini-projects; you can see it in the code in tools/email_preprocess.py ." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's take a look at the selectpercentile at sklearn" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " class sklearn.feature_selection.SelectPercentile(score_func=, percentile=10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "score_func = you could take f_classif module from sklearn.feature_selection\n", "\n", "percentile = what percentage of features you want to select. Here sklearn only take top 10% features that contain the most information" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In text learning you can also filter the most frequent word. Take a look at this sklearn tfidfVectorizer, that I use earlier in my [blog post](http://napitupulu-jon.appspot.com/posts/text-learning-ud120.html)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " vectorizer = TFidfVectorizer(sublinear_tf=True,max_df=0.5,stop_words='english')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "the vectorizer will then take off words that in 50% of the document, besides stop_words. The idea behind this is that if the words are so common, it may not be the feature that distinct each of the document. In other word, not have much information. You can also combine this with do SelectPercentile after you have done the vectorizer." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Bias vs Variance" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![jpeg](../galleries/feature-selection/5.jpg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You know that high bias is when your perfomance low on training set(you can test it by residual error or sum of squared error). In terms of number of features you may select too few features in your model." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "As also for high variance, you're too committed to the data, resulting to act poorly on the test set. This also means that you have too much features in your model. Be careful as you don't also try all the features to try to minimize SSE in your training dataset.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![jpeg](../galleries/feature-selection/6.jpg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Suppose that you tried number of features and check the quality of your model. Then as you test it, you're getting more fit of your data, thus overfitting and the quality reduced. What we want to do is at the peak of the curve as graph above." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "The important thing to take away is, if you have score your performance in training set poorly, then you have high bias in your learning model. On the other hand if you have great performance on your training set, but fail when you use it in a test set, then there's a strong indication that you have high variance. You can test how much features you need by keeping this in mind, and achieve large r^2 with low SSE." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Regularization" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "![jpeg](../galleries/feature-selection/7.jpg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There's also method called regurization, which takes penalty as we gain more features. The higher the lambda, the more strength it will penalize the features. This is other smart moves other than feature selection." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Suppose we want to minimize SSE of our model. In doing so, we gather as much features available to us to minimize the SSE. Adding regularization will penalize our model as we get more features, therefore automatically handle tradeoff between low SSE and many features, vs high SSE and low features. Therefore the features that's not important will take higher penalty, and the features with most information will have lower penalty." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![jpeg](../galleries/feature-selection/8.jpg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see that regularization act as coefficient for the features. When the features not gain any performance in our model, the regularization coeffecient will set to zero, therefore nullfying x3 and x4, which then left x1 and x2." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Keep in mind when setting the regularization value(lambda). Too high and it will penalize your data too much(underfit), too low and it will penalize poorly resulting in overfitting. You may want to step your lambda value in repeating process." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is code snippet from sklearn documentation about lasso" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from sklearn.linear_model import Lasso\n", "features,labels = getMyData()\n", "regression = Lasso\n", "regression.fit(features,labels)\n", "regression.predict([[2,4]])\n", "print regression.coef_#This will give you coeffecients of regression of each of your feature" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For more information on mathematical process behind regularization on linear regression, logistic regression, and how it tackles overfitting, please head over my other [blog posts](http://napitupulu-jon.appspot.com/categories/regularization.html)." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Mini Project" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As usual, because this blog post are the note that I have taken from Udacity course, Here I attack some of the problem they have at their mini project. You can see the link of the course for this note at the bottom of this page." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Katie explained in a video a problem that arose in preparing Chris and Sara\u2019s email for the author identification project; it had to do with a feature that was a little too powerful (effectively acting like a signature, which gives an arguably unfair advantage to an algorithm). You\u2019ll work through that discovery process here." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This bug was found when Katie was trying to make an overfit decision tree to use as an example in the decision tree mini-project. A decision tree is classically an algorithm that can be easy to overfit; one of the easiest ways to get an overfit decision tree is to use a small training set and lots of features. \n", "If a decision tree is overfit, would you expect the accuracy on a test set to be very high or pretty low?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The accuracy would be very high on the training set, but would plummet once it was actually tested." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A classic way to overfit an algorithm is by using lots of features and not a lot of training data. You can find the starter code in feature_selection/find_signature.py. Get a decision tree up and training on the training data, and print out the accuracy. How many training points are there, according to the starter code?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%load find_signature.py" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 3 }, { "cell_type": "code", "collapsed": false, "input": [ "%%writefile find_signature.py\n", "import pickle\n", "import numpy\n", "numpy.random.seed(42)\n", "\n", "\n", "### the words (features) and authors (labels), already largely processed\n", "words_file = \"../text_learning/your_word_data.pkl\" ### you made this in previous mini-project\n", "authors_file = \"../text_learning/your_email_authors.pkl\" ### this too\n", "word_data = pickle.load( open(words_file, \"r\"))\n", "authors = pickle.load( open(authors_file, \"r\") )\n", "\n", "\n", "\n", "### test_size is the percentage of events assigned to the test set (remainder go into training)\n", "from sklearn import cross_validation\n", "features_train, features_test, labels_train, labels_test = cross_validation.train_test_split(word_data, authors, test_size=0.1, random_state=42)\n", "\n", "\n", "from sklearn.feature_extraction.text import TfidfVectorizer\n", "vectorizer = TfidfVectorizer(sublinear_tf=True, max_df=0.5,\n", " stop_words='english')\n", "features_train = vectorizer.fit_transform(features_train).toarray()\n", "features_test = vectorizer.transform(features_test).toarray()\n", "\n", "\n", "### a classic way to overfit is to use a small number\n", "### of data points and a large number of features\n", "### train on only 150 events to put ourselves in this regime\n", "features_train = features_train[:150]\n", "labels_train = labels_train[:150]\n" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Overwriting find_signature.py\n" ] } ], "prompt_number": 46 }, { "cell_type": "markdown", "metadata": {}, "source": [ "What\u2019s the accuracy of the decision tree you just made? (Remember, we're setting it up to be overfit--this number should be relatively low.)" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from sklearn.tree import DecisionTreeClassifier\n", "clf = DecisionTreeClassifier()\n", "clf.fit(features_train,labels_train)\n", "clf.score(features_test,labels_test)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 33 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yes, it has an accuracy much higher than it should be." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Take your (overfit) decision tree and use the feature_importances_ attribute to get a list of the relative importance of all the features being used. We suggest iterating through this list (it\u2019s long, since this is text data) and only printing out the feature importance if it\u2019s above some threshold (say, 0.2--remember, if all words were equally important, each one would give an importance of far less than 0.01). What\u2019s the importance of the most important feature? What is the number of this feature?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "importances = clf.feature_importances_\n", "import numpy as np\n", "indices = np.argsort(importances)[::-1]\n", "print 'Feature Ranking: '\n", "for i in range(10):\n", " print \"{} feature no.{} ({})\".format(i+1,indices[i],importances[indices[i]])" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 38 }, { "cell_type": "markdown", "metadata": {}, "source": [ "In order to figure out what words are causing the problem, you need to go back to the TfIdf and use the feature numbers (you got those in Part 2) to get the associated words. You can return a list of all the words in the TfIdf by calling get_feature_names() on it; pull out the word that\u2019s causing most of the discrimination of the decision tree. What is it? Does it make sense as a word that\u2019s uniquely tied to either Chris Germany or Sara Shackleton, a signature of sorts?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "vectorizer.get_feature_names()[33614]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 44, "text": [ "u'sshacklensf'" ] } ], "prompt_number": 44 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This word seems like an outlier in a certain sense, so let\u2019s remove it and refit. Go back to text_learning/vectorize_text.py, and remove this word from the emails using the same method you used to remove \u201csara\u201d, \u201cchris\u201d, etc. Rerun vectorize_text.py, and once that finishes, rerun find_signature.py. Any other outliers pop up? What word is it? Seem like a signature-type word? (Define an outlier as a feature with importance >0.2, as before)." ] }, { "cell_type": "code", "collapsed": false, "input": [ "%load find_signature.py" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 55 }, { "cell_type": "code", "collapsed": false, "input": [ "import pickle\n", "import numpy\n", "numpy.random.seed(42)\n", "\n", "\n", "### the words (features) and authors (labels), already largely processed\n", "words_file = \"../text_learning/your_word_data.pkl\" ### you made this in previous mini-project\n", "authors_file = \"../text_learning/your_email_authors.pkl\" ### this too\n", "word_data = pickle.load( open(words_file, \"r\"))\n", "authors = pickle.load( open(authors_file, \"r\") )\n", "\n", "\n", "\n", "### test_size is the percentage of events assigned to the test set (remainder go into training)\n", "from sklearn import cross_validation\n", "features_train, features_test, labels_train, labels_test = cross_validation.train_test_split(word_data, authors, test_size=0.1, random_state=42)\n", "\n", "\n", "from sklearn.feature_extraction.text import TfidfVectorizer\n", "vectorizer = TfidfVectorizer(sublinear_tf=True, max_df=0.5,\n", " stop_words='english')\n", "features_train = vectorizer.fit_transform(features_train).toarray()\n", "features_test = vectorizer.transform(features_test).toarray()\n", "\n", "\n", "### a classic way to overfit is to use a small number\n", "### of data points and a large number of features\n", "### train on only 150 events to put ourselves in this regime\n", "features_train = features_train[:150]\n", "labels_train = labels_train[:150]" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 56 }, { "cell_type": "code", "collapsed": false, "input": [ "from sklearn.tree import DecisionTreeClassifier\n", "clf = DecisionTreeClassifier()\n", "clf.fit(features_train,labels_train)\n", "clf.score(features_test,labels_test)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 58, "text": [ "0.95051194539249151" ] } ], "prompt_number": 58 }, { "cell_type": "code", "collapsed": false, "input": [ "importances = clf.feature_importances_\n", "import numpy as np\n", "indices = np.argsort(importances)[::-1]\n", "print 'Feature Ranking: '\n", "for i in range(10):\n", " print \"{} feature no.{} ({})\".format(i+1,indices[i],importances[indices[i]])" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Feature Ranking: \n", "1 feature no.14343 (0.666666666667)\n", "2 feature no.8674 (0.162601626016)\n", "3 feature no.16268 (0.093808630394)\n", "4 feature no.14337 (0.0506072874494)\n", "5 feature no.35399 (0.0263157894737)\n", "6 feature no.12617 (0.0)\n", "7 feature no.12623 (0.0)\n", "8 feature no.12622 (0.0)\n", "9 feature no.12621 (0.0)\n", "10 feature no.12620 (0.0)\n" ] } ], "prompt_number": 59 }, { "cell_type": "code", "collapsed": false, "input": [ "vectorizer.get_feature_names()[14343]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 60, "text": [ "u'cgermannsf'" ] } ], "prompt_number": 60 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Update vectorize_test.py one more time, and rerun. Then run find_signature.py again. Any other important features (importance>0.2) arise? How many? Do any of them look like \u201csignature words\u201d, or are they more \u201cemail content\u201d words, that look like they legitimately come from the text of the messages?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import pickle\n", "import numpy\n", "numpy.random.seed(42)\n", "\n", "\n", "### the words (features) and authors (labels), already largely processed\n", "words_file = \"../text_learning/your_word_data.pkl\" ### you made this in previous mini-project\n", "authors_file = \"../text_learning/your_email_authors.pkl\" ### this too\n", "word_data = pickle.load( open(words_file, \"r\"))\n", "authors = pickle.load( open(authors_file, \"r\") )\n", "\n", "\n", "\n", "### test_size is the percentage of events assigned to the test set (remainder go into training)\n", "from sklearn import cross_validation\n", "features_train, features_test, labels_train, labels_test = cross_validation.train_test_split(word_data, authors, test_size=0.1, random_state=42)\n", "\n", "\n", "from sklearn.feature_extraction.text import TfidfVectorizer\n", "vectorizer = TfidfVectorizer(sublinear_tf=True, max_df=0.5,\n", " stop_words='english')\n", "features_train = vectorizer.fit_transform(features_train).toarray()\n", "features_test = vectorizer.transform(features_test).toarray()\n", "\n", "\n", "### a classic way to overfit is to use a small number\n", "### of data points and a large number of features\n", "### train on only 150 events to put ourselves in this regime\n", "features_train = features_train[:150]\n", "labels_train = labels_train[:150]" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 65 }, { "cell_type": "code", "collapsed": false, "input": [ "from sklearn.tree import DecisionTreeClassifier\n", "clf = DecisionTreeClassifier()\n", "clf.fit(features_train,labels_train)\n", "clf.score(features_test,labels_test)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 66, "text": [ "0.81683731513083047" ] } ], "prompt_number": 66 }, { "cell_type": "code", "collapsed": false, "input": [ "importances = clf.feature_importances_\n", "import numpy as np\n", "indices = np.argsort(importances)[::-1]\n", "print 'Feature Ranking: '\n", "for i in range(10):\n", " print \"{} feature no.{} ({})\".format(i+1,indices[i],importances[indices[i]])" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Feature Ranking: \n", "1 feature no.21323 (0.363636363636)\n", "2 feature no.18849 (0.186927243449)\n", "3 feature no.11975 (0.105378579003)\n", "4 feature no.22546 (0.0840692099229)\n", "5 feature no.29690 (0.0475805258904)\n", "6 feature no.16267 (0.0474074074074)\n", "7 feature no.18095 (0.0426666666667)\n", "8 feature no.13080 (0.0262801932367)\n", "9 feature no.25675 (0.0255293305728)\n", "10 feature no.24320 (0.0248101945003)\n" ] } ], "prompt_number": 67 }, { "cell_type": "code", "collapsed": false, "input": [ "vectorizer.get_feature_names()[21323]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 68, "text": [ "u'houectect'" ] } ], "prompt_number": 68 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yes, there is one more word (\"houectect\"). Your guess about what this word means is as good as ours, but it doesn't look like an obvious signature word so let's keep moving." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What\u2019s the accuracy of the decision tree now? Remember, the whole point was to see if we could get it to overfit--a sensible result is one where the accuracy isn't that great!" ] }, { "cell_type": "code", "collapsed": false, "input": [ "clf.score(features_test,labels_test)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 69, "text": [ "0.81683731513083047" ] } ], "prompt_number": 69 }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **REFERENCE**:\n", "\n", "> * http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfVectorizer.html\n", "> * https://www.udacity.com/course/viewer#!/c-ud120/l-2948958577/m-2982298635" ] } ], "metadata": {} } ] }