<jupyter_start><jupyter_text>Introduction to ML in PracticeIn the previous lecture, we worked through an example of machine learning "by hand," in a very abstract mathematical context. All of the data was given to us as floating point numbers, and there was no messy data preparation to do. This enabled us to go into some detail about what the model was actually doing. We worked with a model with several parameters; generated instances of that model; and saw that selecting the model with the smallest loss led to a good apparent match to the data. In practice, the situation is not nearly so neat. There is missing data; some of the data has `na` values; some of the data is formatted as strings. Furthermore, it's arduous to do all of the steps of the machine learning pipeline by hand, and only machine learning researchers do this on a regular basis. Most programming languages offer simplified interfaces for efficiently working with machine learning models. In this lecture, we'll work through a similar machine learning pipeline on a more complex historical data set. Along the way, we'll introduce the `scikit-learn` package, which offers an effective and concise set of tools for carrying out complicated machine learning tasks.<jupyter_code>import numpy as np
from matplotlib import pyplot as plt
import pandas as pd<jupyter_output><empty_output><jupyter_text>To help us get acquainted with the many tools that `scikit-learn` offers us, we are going to work through the "Hello World" of machine learning -- the Titanic data set.<jupyter_code>url = "https://philchodrow.github.io/PIC16A/datasets/titanic.csv"
titanic = pd.read_csv(url)
titanic<jupyter_output><empty_output><jupyter_text>The Titanic data set collects information about almost 900 passangers aboard the Titanic during the fateful voyage when it crashed into an iceberg in 1912 and sank. The information includes their age; the fare they paid for their ticket (in British pounds); their sex; and the passenger class `Pclass`, with 1st class corresponding to VIP treatment and 3rd class corresponding to a much less luxurious experience. Crucially, the data set also records whether that passenger survived the sinking of the ship, with `1` indicating that the passenger survived and `0` indicating that the passenger tragically perished. We are eventually going to train an algorithm to predict whether a passenger survived the Titanic based on their available information. Before we do, let's get a sense for some trends using familiar `pandas` summarization. How wealthy were these passengers? We can't know for certain, but we can get a sense for how much was paid for each passenger class.<jupyter_code>titanic.groupby('Pclass')[['Fare']].aggregate([np.mean, len]).round(2)<jupyter_output><empty_output><jupyter_text>- The average price of 84 pounds for a first-class ticket corresponds to nearly \$15,000 USD today.- The second-class ticket corresponds to roughly \$3,500- The third class ticket corresponds to roughly \$2,500. We can safely assume that the first-class passengers were indeed substantially more wealthy on average than the others. This difference in wealth made a considerable difference in how likely passengers were to survive.<jupyter_code>titanic.groupby(['Pclass', 'Sex'])[['Survived']].aggregate([np.mean,len ]).round(2)<jupyter_output><empty_output><jupyter_text>This table reflects the famous maritime tradition of prioritizing women and children first into the lifeboats, resulting in vastly higher survival rates among women in these data. Note the role of class: a 1st-class woman was twice as likely to survive as a third class woman, and a 1st-class man was nearly three times as likely to survive as a 3rd class man.   Towards ModelingWe'd like to develop automated models that can use these trends and others to make predictions about survival. However, we need to do a bit of data cleaning before we're ready for this. In particular, **machine learning algorithms don't really get text**, so we need to transform text data into numbers before we can proceed. Here's how we can encode the `Sex` data, using our first handy tool from `sklearn`.<jupyter_code>from sklearn import preprocessing
le = preprocessing.LabelEncoder()
le.fit_transform(titanic['Sex'])<jupyter_output><empty_output><jupyter_text>The result is a binary array in which `0` encodes `female` and `1` encodes `male`. Let's just replace the `Sex` column in the data with the encoded version.<jupyter_code>titanic['Sex'] = le.fit_transform(titanic['Sex'])<jupyter_output><empty_output><jupyter_text>We also don't really have any use for the actual names of passengers, so let's just remove that column. |<jupyter_code>titanic = titanic.drop(['Name'], axis = 1)
titanic<jupyter_output><empty_output><jupyter_text>This looks better! We have a data frame full of sweet, delicious numbers. Mmmmm.....The next thing to do is to separate out the target data `Survived` from the predictor data (everything else).<jupyter_code>X = titanic.drop(['Survived'], axis = 1)
X
y = titanic['Survived']
y<jupyter_output><empty_output><jupyter_text>ModelingTo use a machine learning model from `scikit-learn`, you should import the relevant model. For example, let's use a decision tree classifier.<jupyter_code>from sklearn import tree # scikit-learn is actually called sklearn when importing<jupyter_output><empty_output><jupyter_text>In `scikit-learn`, abstract models are represented as classes. To make a specific model, instantiate the class.<jupyter_code>T = tree.DecisionTreeClassifier(max_depth = 2)<jupyter_output><empty_output><jupyter_text>Arguments passed to the model upon instantiation are typically used to control how complex the model can be. These arguments are often referred to as *hyperparameters*. In practice, we don't usually know what the *right* hyperparameters are, and so we need to resort to various computational techniques (in coming lectures) to select good ones. Now, here's the good news. Once we've carefully prepared our data and instantiated a model, actually fitting the model is unreasonably easy.<jupyter_code>T.fit(X, y)<jupyter_output><empty_output><jupyter_text>Remember that part in the last lecture where we defined a loss function and then searched around for models in our specified model family that minimized the loss? Yeah, `scikit-learn` has taken care of all that for us. We can then *score* our model to see how we did in terms of accuracy. The `score` method requires both the predictor and target variables. What does the `score` function calculate? It depends by model, but in the case of classifiers, the score is the fraction of the time that the model made the correct prediction. The score does the same job as the loss function, but the signs are flipped -- high scores mean good models.<jupyter_code>T.score(X, y)<jupyter_output><empty_output><jupyter_text>Hey, not bad! Our model was able to use the predictor variables to be right nearly 80\% of the time. That's pretty impressive, but there is an important problem here. When doing machine learning, it's not advised to *score* or *evaluate* your model on the same data used for *training* or *fitting* the model. We'll come back to this in the next lecture.  InterpretationIn most cases, you should aim to understand how your model makes the decisions that it does. This is the problem of machine learning *interpretation*. Every machine learning algorithm has both strengths and weaknesses when it comes to interpretation. Decision trees are pretty pleasant to interpret, as they correspond to "flow-chart" style reasoning that many of us are familiar with. The `tree` module of `scikit-learn` provides a convenient method for visualizing decision trees.<jupyter_code>fig, ax = plt.subplots(1, figsize = (10, 10))
p = tree.plot_tree(T, 
                   filled = True, 
                   feature_names = titanic.drop(['Survived'], axis = 1).columns)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.06268844221105528, 'fraction_numerical': 0.005778894472361809, 'mean_word_length': 5.323272438443209, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 53, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '344311', 'license': 'MIT', 'n_tokens_mistral': 2111, 'n_tokens_neox': 1995, 'n_words': 1083, 'path': 'content/ML/ML_3.ipynb', 'repo_name': 'mm5110/PIC16A', 'size': '7960'}