From 3bf73c9b7aa50d4d11652764c670e7af49b2a3cc Mon Sep 17 00:00:00 2001 From: Alex Ratner Date: Tue, 13 Aug 2019 17:25:29 -0700 Subject: [PATCH] Lighter-weight 101 intro version of spam tutorial (#37) * First rev on lighter-weight intro tutorial * Fixing @brahmaneya edits in PR * Editing down code to minimal form as suggested by HE * Simplifying code; adding TF example; editing * Editing pass over text * Transfer tags w jupytext build, minor edits * Filtering abstain values * Added a stub for SFs * Trying to fix style check * Skipping env_* files in flake * Style fixes * PR changes requested by @henryre * Quieting nltk output * Moved to getting_started * Silencing LogReg warning * Forgot to sync style fix... * Spelling fix * Addressing comments * First rev on lighter-weight intro tutorial * Fixing @brahmaneya edits in PR * Hot fix LF names (#63) * Mtl updates (#41) * [EASY] Update Scorer import paths (#58) * Save MTL updates in progress * Give more API hints * More text updates * Hide unnecessary helpers in utils * Drop unused import and sync notebook * Save MTL updates in progress * Give more API hints * More text updates * Hide unnecessary helpers in utils * Drop unused import and sync notebook * Address comments * Rename mtl to multitask so file and tutorial match * Update Scorer import paths * Update names of loss and output funcs * Update name of ce_loss_from_outputs() * Rename SnorkelClassifier to MultitaskClassifier (#59) * Update Scorer import * Update Scorer import in vrd_tutorial * Remove unused import * [EASY] Add links to RTD in multitask tutorial (#65) * Add links to RTD in multitask tutorial * Separate sentences * Sync multitask.ipynb * Editing down code to minimal form as suggested by HE * Add Drybell tutorial (#62) * Add drybell tutorial * Add to tox and README * Install Java on Travis * Pass JAVA_HOME * Add README * Update API * Revisions to crowdsourcing tutorial (#64) * Revisions to crowdsourcing tutorial * Run tox * Address comments * Simplifying code; adding TF example; editing * Recsys novel (#61) * Recsys first commit * Backup * Added second version * Recsys modeling work * Add review processing and LFs. * First complete draft * typo * Add ipynb * Add comments, refactor * Address comments * Updated ipynb * Update tox.ini to allow sync / test for recsys (but not by default) * Address comments * Update ipynb * Address final comments * Fix determinism of TF tutorial (#67) * Fix determinism of TF tutorial * Add os PYTHONGHASHSEED back * Editing pass over text * Transfer tags w jupytext build, minor edits * Filtering abstain values * Added a stub for SFs * Trying to fix style check * Skipping env_* files in flake * Style fixes * PR changes requested by @henryre * Quieting nltk output * Separate download scripts, feedback session (#55) * Moved to getting_started * Silencing LogReg warning * Slicing spam (#18) * Forgot to sync style fix... * Add link checking (#72) * Add link checking * Fix * Only run Travis on changed tutorials (#74) * Only run Travis on changed tutorials * Fix * Address comments * Fix * Fix a couple links (#75) * Fix link * Fix links * Fix Travis branch check (#77) * Spelling fix * Stop training on dev set [EASY] (#71) * Stop training on dev set * Update image link * Add style to run envs (#78) * Add style to run envs * Simplify * Make travis faster for spouse [EASY] (#80) * Make travis faster for spouse * remove extra cell * sync * all caps for constant * More verbose build script (#81) * Add markdown build mode (#79) * Add markdown build mode * Fix kwarg * Be a bit more opinionated * [EASY] Update path to snorkel to reflect ownership transfer (#82) * Update path to snorkel to reflect ownership transfer * Restore path to snorkel-superglue on HazyResearch * Make travis only run on changed dirs (#85) * Make travis only run on changed dirs * Small fix * Add space * Update tutorials with MultitaskClassifier API changes (#68) * Update multitask and scene_graph to last_op * Update spam tutorial * Update notebooks * Sync visual_relation notebook * Update spam notebooks * Run tox -e fix * Remove unused import * Configure markdown generation (#87) * Configure markdown generation * Add comments * [EASY] Replace `mtl` with `multitask` in README (#83) * Deploy tutorial pages via Travis (#88) * Deploy tutorial pages via Travis * Fix commands * Update readme (#73) * Update readme * Address comment * Addressing comments --- .flake8 | 4 +- getting_started/.gitignore | 3 + getting_started/.notebooks | 1 + getting_started/__init__.py | 0 getting_started/download_data.sh | 32 ++ getting_started/getting_started.ipynb | 697 ++++++++++++++++++++++++++ getting_started/getting_started.py | 257 ++++++++++ getting_started/img/snorkel_ops.png | Bin 0 -> 83282 bytes getting_started/requirements.txt | 3 + getting_started/utils.py | 31 ++ pyproject.toml | 2 +- scripts/build.py | 2 + tox.ini | 6 + 13 files changed, 1035 insertions(+), 3 deletions(-) create mode 100644 getting_started/.gitignore create mode 100644 getting_started/.notebooks create mode 100644 getting_started/__init__.py create mode 100755 getting_started/download_data.sh create mode 100644 getting_started/getting_started.ipynb create mode 100644 getting_started/getting_started.py create mode 100644 getting_started/img/snorkel_ops.png create mode 100644 getting_started/requirements.txt create mode 100644 getting_started/utils.py diff --git a/.flake8 b/.flake8 index e037a9f3..827f3241 100644 --- a/.flake8 +++ b/.flake8 @@ -15,8 +15,8 @@ exclude = .git, .mypy_cache, .tox, - .env, - .venv, + .env**, + .venv**, _build, build, dist diff --git a/getting_started/.gitignore b/getting_started/.gitignore new file mode 100644 index 00000000..9fdea153 --- /dev/null +++ b/getting_started/.gitignore @@ -0,0 +1,3 @@ +# Logs +results/ +logs/ \ No newline at end of file diff --git a/getting_started/.notebooks b/getting_started/.notebooks new file mode 100644 index 00000000..b592bd3a --- /dev/null +++ b/getting_started/.notebooks @@ -0,0 +1 @@ +getting_started \ No newline at end of file diff --git a/getting_started/__init__.py b/getting_started/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/getting_started/download_data.sh b/getting_started/download_data.sh new file mode 100755 index 00000000..63a68585 --- /dev/null +++ b/getting_started/download_data.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -euxo pipefail + +# Check that we are running from the right directory. +if [ ! "${PWD##*/}" = "getting_started" ]; then + echo "Script must be run from getting_started directory" >&2 + exit 1 +fi + +FILES=( "Youtube01-Psy.csv" "Youtube02-KatyPerry.csv" "Youtube03-LMFAO.csv" "Youtube04-Eminem.csv" "Youtube05-Shakira.csv" ) +DATA_URL="https://archive.ics.uci.edu/ml/machine-learning-databases/00380/YouTube-Spam-Collection-v1.zip" +RELOAD=false + +# Check if at least any file is missing. If so, reload all data. +for filename in "${FILES[@]}" +do + if [ ! -e "data/$filename" ]; then + RELOAD=true + fi +done + +if [ "$RELOAD" = true ]; then + if [ -d "data/" ]; then rm -Rf "data/"; fi + mkdir -p data + wget $DATA_URL -O data.zip + mv data.zip data/ + cd data + unzip data.zip + rm data.zip + rm -rf __MACOSX + cd .. +fi diff --git a/getting_started/getting_started.ipynb b/getting_started/getting_started.ipynb new file mode 100644 index 00000000..e525ea7e --- /dev/null +++ b/getting_started/getting_started.ipynb @@ -0,0 +1,697 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "tags": [ + "remove-md" + ] + }, + "source": [ + "# Getting Started with Snorkel" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [ + "remove-md" + ] + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "# Make sure we're running from the spam/ directory\n", + "if os.path.basename(os.getcwd()) == \"snorkel-tutorials\":\n", + " os.chdir(\"getting_started\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Programmatically Building & Managing Training Data with Snorkel\n", + "\n", + "Snorkel is a system for _programmatically_ building and managing training datasets **without needing to hand-label _any_ training data**.\n", + "In Snorkel, users can develop training datasets in hours or days rather than hand-labeling them over weeks or months.\n", + "\n", + "Snorkel currently exposes three key programmatic operations:\n", + "- **Labeling data**, for example using heuristic rules or distant supervision techniques\n", + "- **Transforming data**, for example rotating or stretching images to perform data augmentation\n", + "- **Slicing data** into different critical subsets.\n", + "Snorkel then automatically models, cleans, and integrates the resulting training data using novel, theoretically-grounded techniques." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this quick walkthrough, we'll preview the high level workflow and interfaces of Snorkel using a canonical machine learning problem: classifying spam.\n", + "We'll use a public [YouTube comments dataset](https://www.kaggle.com/goneee/youtube-spam-classifiedcomments) from Kaggle, and see how **Snorkel can enable you to train a machine learning model without _any_ hand-labeled training data!**\n", + "For the more detailed version, see the [Introductory Tutorial](#).\n", + "\n", + "We'll walk through five basic steps:\n", + "\n", + "1. **Writing Labeling Functions (LFs):** First, rather than hand-labeling any training data, we'll programmatically label our _unlabeled_ dataset with LFs.\n", + "2. **Modeling & Combining LFs:** Next, we'll use Snorkel's `LabelModel` to automatically learn the accuracies of our LFs and reweight and combine their outputs into a single, confidence-weighted training label per data point.\n", + "3. **Writing Transformation Functions (TFs) for Data Augmentation:** Then, we'll augment this labeled training set by writing a simple TF.\n", + "4. **Writing _Slicing Functions (SFs)_ for Data Subset Selection:** We'll also preview writing an SF to identify a critical subset or _slice_ of our training set.\n", + "5. **Training a final ML model:** Finally, we'll train a simple ML model with our training set!\n", + "\n", + "We'll start first by loading the _unlabeled_ comments, which we'll use as our training data, as a Pandas `DataFrame`:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from utils import load_unlabeled_spam_dataset\n", + "\n", + "df_train = load_unlabeled_spam_dataset()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## (1) Writing Labeling Functions\n", + "\n", + "_Labeling functions (LFs)_ are one of the core operators for building and managing training datasets programmatically in Snorkel.\n", + "The basic idea is simple: **a labeling function is a function that outputs a label for some subset of the training dataset**.\n", + "That is, in our example here, each labeling function takes as input a comment data point, and either outputs a label (`SPAM = 1` or `NOT_SPAM = 0`) or abstains from labeling (`ABSTAIN = -1`):" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Define the label mappings for convenience\n", + "ABSTAIN = -1\n", + "NOT_SPAM = 0\n", + "SPAM = 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Labeling functions can be used to represent many heuristic and/or noisy strategies for labeling data, often referred to as [weak supervision](#).\n", + "The basic idea of labeling functions, and other programmatic operators in Snorkel, is to let users inject domain information into machine learning models in higher level, higher bandwidth ways than manually labeling thousands or millions of individual data points.\n", + "**The key idea is that labeling functions do not need to be perfectly accurate**, and can in fact even be correlated with each other.\n", + "Snorkel will automatically estimate their accuracies and correlations in a [provably consistent way](https://papers.nips.cc/paper/6523-data-programming-creating-large-training-sets-quickly), and then reweight and combine their output labels, leading to high-quality training labels." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In our text data setting here, labeling functions can be keyword matchers:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "from snorkel.labeling import labeling_function\n", + "\n", + "\n", + "@labeling_function()\n", + "def lf_keyword_my(x):\n", + " \"\"\"Many spam comments talk about 'my channel', 'my video', etc.\"\"\"\n", + " return SPAM if \"my\" in x.text.lower() else ABSTAIN" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use regular expressions:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "\n", + "\n", + "@labeling_function()\n", + "def lf_regex_check_out(x):\n", + " \"\"\"Spam comments say 'check out my video', 'check it out', etc.\"\"\"\n", + " return SPAM if re.search(r\"check.*out\", x.text, flags=re.I) else ABSTAIN" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Flexibly leverage arbitrary heuristics:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "@labeling_function()\n", + "def lf_short_comment(x):\n", + " \"\"\"Non-spam comments are often short, such as 'cool video!'.\"\"\"\n", + " return NOT_SPAM if len(x.text.split()) < 5 else ABSTAIN" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use third-party models:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "from textblob import TextBlob\n", + "\n", + "\n", + "@labeling_function()\n", + "def lf_textblob_polarity(x):\n", + " \"\"\"\n", + " We use a third-party sentiment classification model, TextBlob.\n", + "\n", + " We combine this with the heuristic that non-spam comments are often positive.\n", + " \"\"\"\n", + " sentiment = TextBlob(x.text).sentiment\n", + " return NOT_SPAM if sentiment.polarity > 0.3 else ABSTAIN" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And much more!\n", + "For many more types of labeling functions–including over data modalities beyond text–see the other [tutorials](#) and [real-world examples](#).\n", + "\n", + "In general the process of developing labeling functions is, like any other development process, an iterative one that takes time.\n", + "However, in many cases it can be _orders-of-magnitude_ faster that hand-labeling training data.\n", + "For more detail on the process of developing labeling functions and other training data operators in Snorkel, see the [Introductory tutorial](#)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## (2) Combining & Cleaning the Labels\n", + "\n", + "Our next step is to apply the labeling functions we wrote to the unlabeled training data.\n", + "The result is a _label matrix_, `L_train`, where each row corresponds to a data point and each column corresponds to a labeling function.\n", + "Since the labeling functions have unknown accuracies and correlations, their output labels may overlap and conflict.\n", + "We use the `LabelModel` to automatically estimate their accuracies and correlations, reweight and combine their labels, and produce our final set of clean, integrated training labels:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + " 0%| | 0/1956 [00:00 0:\n", + " x.text = \" \".join(words[:idx] + [synonyms[0]] + words[idx + 1 :])\n", + " return x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we apply this transformation function to our training dataset:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + " 0%| | 0/1387 [00:00 + +# %% [markdown] +# In this quick walkthrough, we'll preview the high level workflow and interfaces of Snorkel using a canonical machine learning problem: classifying spam. +# We'll use a public [YouTube comments dataset](https://www.kaggle.com/goneee/youtube-spam-classifiedcomments) from Kaggle, and see how **Snorkel can enable you to train a machine learning model without _any_ hand-labeled training data!** +# For the more detailed version, see the [Introductory Tutorial](#). +# +# We'll walk through five basic steps: +# +# 1. **Writing Labeling Functions (LFs):** First, rather than hand-labeling any training data, we'll programmatically label our _unlabeled_ dataset with LFs. +# 2. **Modeling & Combining LFs:** Next, we'll use Snorkel's `LabelModel` to automatically learn the accuracies of our LFs and reweight and combine their outputs into a single, confidence-weighted training label per data point. +# 3. **Writing Transformation Functions (TFs) for Data Augmentation:** Then, we'll augment this labeled training set by writing a simple TF. +# 4. **Writing _Slicing Functions (SFs)_ for Data Subset Selection:** We'll also preview writing an SF to identify a critical subset or _slice_ of our training set. +# 5. **Training a final ML model:** Finally, we'll train a simple ML model with our training set! +# +# We'll start first by loading the _unlabeled_ comments, which we'll use as our training data, as a Pandas `DataFrame`: + +# %% +from utils import load_unlabeled_spam_dataset + +df_train = load_unlabeled_spam_dataset() + +# %% [markdown] +# ## (1) Writing Labeling Functions +# +# _Labeling functions (LFs)_ are one of the core operators for building and managing training datasets programmatically in Snorkel. +# The basic idea is simple: **a labeling function is a function that outputs a label for some subset of the training dataset**. +# That is, in our example here, each labeling function takes as input a comment data point, and either outputs a label (`SPAM = 1` or `NOT_SPAM = 0`) or abstains from labeling (`ABSTAIN = -1`): + +# %% +# Define the label mappings for convenience +ABSTAIN = -1 +NOT_SPAM = 0 +SPAM = 1 + +# %% [markdown] +# Labeling functions can be used to represent many heuristic and/or noisy strategies for labeling data, often referred to as [weak supervision](#). +# The basic idea of labeling functions, and other programmatic operators in Snorkel, is to let users inject domain information into machine learning models in higher level, higher bandwidth ways than manually labeling thousands or millions of individual data points. +# **The key idea is that labeling functions do not need to be perfectly accurate**, and can in fact even be correlated with each other. +# Snorkel will automatically estimate their accuracies and correlations in a [provably consistent way](https://papers.nips.cc/paper/6523-data-programming-creating-large-training-sets-quickly), and then reweight and combine their output labels, leading to high-quality training labels. + +# %% [markdown] +# In our text data setting here, labeling functions can be keyword matchers: + +# %% +from snorkel.labeling import labeling_function + + +@labeling_function() +def lf_keyword_my(x): + """Many spam comments talk about 'my channel', 'my video', etc.""" + return SPAM if "my" in x.text.lower() else ABSTAIN + + +# %% [markdown] +# Use regular expressions: + +# %% +import re + + +@labeling_function() +def lf_regex_check_out(x): + """Spam comments say 'check out my video', 'check it out', etc.""" + return SPAM if re.search(r"check.*out", x.text, flags=re.I) else ABSTAIN + + +# %% [markdown] +# Flexibly leverage arbitrary heuristics: + +# %% +@labeling_function() +def lf_short_comment(x): + """Non-spam comments are often short, such as 'cool video!'.""" + return NOT_SPAM if len(x.text.split()) < 5 else ABSTAIN + + +# %% [markdown] +# Use third-party models: + +# %% +from textblob import TextBlob + + +@labeling_function() +def lf_textblob_polarity(x): + """ + We use a third-party sentiment classification model, TextBlob. + + We combine this with the heuristic that non-spam comments are often positive. + """ + sentiment = TextBlob(x.text).sentiment + return NOT_SPAM if sentiment.polarity > 0.3 else ABSTAIN + + +# %% [markdown] +# And much more! +# For many more types of labeling functions–including over data modalities beyond text–see the other [tutorials](#) and [real-world examples](#). +# +# In general the process of developing labeling functions is, like any other development process, an iterative one that takes time. +# However, in many cases it can be _orders-of-magnitude_ faster that hand-labeling training data. +# For more detail on the process of developing labeling functions and other training data operators in Snorkel, see the [Introductory tutorial](#). + +# %% [markdown] +# ## (2) Combining & Cleaning the Labels +# +# Our next step is to apply the labeling functions we wrote to the unlabeled training data. +# The result is a _label matrix_, `L_train`, where each row corresponds to a data point and each column corresponds to a labeling function. +# Since the labeling functions have unknown accuracies and correlations, their output labels may overlap and conflict. +# We use the `LabelModel` to automatically estimate their accuracies and correlations, reweight and combine their labels, and produce our final set of clean, integrated training labels: + +# %% +from snorkel.labeling import LabelModel, PandasLFApplier + +# Define the set of labeling functions (LFs) +lfs = [lf_keyword_my, lf_regex_check_out, lf_short_comment, lf_textblob_polarity] + +# Apply the LFs to the unlabeled training data +applier = PandasLFApplier(lfs) +L_train = applier.apply(df_train) + +# Train the label model and compute the training labels +label_model = LabelModel(cardinality=2, verbose=True) +label_model.fit(L_train, n_epochs=500, log_freq=50, seed=123) +df_train["label"] = label_model.predict(L=L_train, tie_break_policy="abstain") + +# %% [markdown] +# Note that we used the `LabelModel` to label data; however, on many data points, all the labeling functions abstain, and so the `LabelModel` abstains as well. +# We'll filter these examples out of our training set now: + +# %% +df_train = df_train[df_train.label != ABSTAIN] + +# %% [markdown] +# Our ultimate goal is to use the resulting labeled training data points to train a machine learning model that can **generalize beyond the coverage of the labeling functions and the `LabelModel`**. +# However first we'll explore some of Snorkel's other operators for building and managing training data. + +# %% [markdown] +# ## (3): Writing Transformation Functions for Data Augmentation +# +# An increasingly popular and critical technique in modern machine learning is [data augmentation](#), the strategy of artificially _augmenting_ existing labeled training datasets by creating transformed copies of the data points. +# Data augmentation is a practical and powerful method for injecting information about domain invariances into ML models via the data, rather than by trying to modify their internal architectures. +# The canonical example is randomly rotating, stretching, and transforming images when training image classifiers---a ubiquitous technique in the field of computer vision today. +# However, data augmentation is increasingly used in a range of settings, including text. +# +# Here, we implement a simple text data augmentation strategy- randomly replacing a word with a synonym. +# We express this as a *transformation function (TF)*: + +# %% +import random + +import nltk +from nltk.corpus import wordnet as wn + +from snorkel.augmentation import transformation_function + +nltk.download("wordnet", quiet=True) + + +def get_synonyms(word): + """Get the synonyms of word from Wordnet.""" + lemmas = set().union(*[s.lemmas() for s in wn.synsets(word)]) + return list(set([l.name().lower().replace("_", " ") for l in lemmas]) - {word}) + + +@transformation_function() +def tf_replace_word_with_synonym(x): + """Try to replace a random word with a synonym.""" + words = x.text.lower().split() + idx = random.choice(range(len(words))) + synonyms = get_synonyms(words[idx]) + if len(synonyms) > 0: + x.text = " ".join(words[:idx] + [synonyms[0]] + words[idx + 1 :]) + return x + + +# %% [markdown] +# Next, we apply this transformation function to our training dataset: + +# %% +from snorkel.augmentation import ApplyOnePolicy, PandasTFApplier + +tf_policy = ApplyOnePolicy(n_per_original=2, keep_original=True) +tf_applier = PandasTFApplier([tf_replace_word_with_synonym], tf_policy) +df_train_augmented = tf_applier.apply(df_train) + +# %% [markdown] +# Note that a common challenge with data augmentation is figuring out how to tune and apply different transformation functions to best augment a training set. +# This is most commonly done as an arduous and ad hoc manual process; however, in Snorkel, various approaches for using automatically learned data augmentation _policies_ are supported. +# For more detail, see the [data augmentation tutorial](#). + +# %% [markdown] +# ## (4) Writing a Slicing Function +# +# Finally, a third operator in Snorkel, *slicing functions (SFs)*, handles the reality that many dataset have certain subsets or _slices_ that are more important than others. +# In Snorkel, we can write SFs to (a) monitor specific slices and (b) improve model performance over them by adding representational capacity. +# +# Writing a slicing function is simple. +# For example, we could write one that looks for suspiciously shortened links, which might be critical due to their likelihood of linking to malicious sites: + +# %% +from snorkel.slicing import slicing_function + + +@slicing_function() +def short_link(x): + """Return whether text matches common pattern for shortened ".ly" links.""" + return int(bool(re.search(r"\w+\.ly", x.text))) + + +# %% [markdown] +# We can now use Snorkel monitor the performance over this slice, and to add representational capacity to whatever model we are using in order to increase performance. +# For a walkthrough of these steps, see the [slicing tutorial](#). + +# %% [markdown] +# ## (5) Training a Machine Learning Model +# +# The ultimate goal in Snorkel is to **create a training dataset**, which can then be plugged into any machine learning framework (e.g. TensorFlow, Keras, PyTorch, Scikit-Learn, Ludwig, XGBoost) to train powerful machine learning models. +# Here, to complete this initial walkthrough, we'll train an extremely simple model–a simple "bag of n-grams" logistic regression model in SciKit-Learn–using the weakly labeled and augmented training set we made with our labeling and transformation functions: + +# %% +from sklearn.feature_extraction.text import CountVectorizer +from sklearn.linear_model import LogisticRegression + +text_train = [row.text for i, row in df_train_augmented.iterrows()] +X_train = CountVectorizer(ngram_range=(1, 2)).fit_transform(text_train) + +clf = LogisticRegression(solver="lbfgs") +clf.fit(X=X_train, y=df_train_augmented.label.values) + +# %% [markdown] +# And that's it- you've trained your first model **without hand-labeling _any_ training data!** +# Next, to learn more about Snorkel, check out the [tutorials](#), [use cases](#), [resources](#), and [documentation](#) for much more on how to use Snorkel to power your own machine learning applications. diff --git a/getting_started/img/snorkel_ops.png b/getting_started/img/snorkel_ops.png new file mode 100644 index 0000000000000000000000000000000000000000..8f58c1be56ada8518f8fd642926b0ec6e571a26d GIT binary patch literal 83282 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 HIzImgTbN%R literal 0 HcmV?d00001 diff --git a/getting_started/requirements.txt b/getting_started/requirements.txt new file mode 100644 index 00000000..b74eb4d2 --- /dev/null +++ b/getting_started/requirements.txt @@ -0,0 +1,3 @@ +pandas>=0.24.0,<0.25.0 +scikit-learn>=0.20.2 +textblob==0.15.3 diff --git a/getting_started/utils.py b/getting_started/utils.py new file mode 100644 index 00000000..19888b72 --- /dev/null +++ b/getting_started/utils.py @@ -0,0 +1,31 @@ +import glob +import os +import subprocess + +import pandas as pd + + +def load_unlabeled_spam_dataset(): + """Load spam training dataset without any labels.""" + if os.path.basename(os.getcwd()) == "snorkel-tutorials": + os.chdir("getting_started") + try: + subprocess.run(["bash", "download_data.sh"], check=True, stderr=subprocess.PIPE) + except subprocess.CalledProcessError as e: + print(e.stderr.decode()) + raise e + filenames = sorted(glob.glob("data/Youtube*.csv")) + dfs = [] + for i, filename in enumerate(filenames, start=1): + df = pd.read_csv(filename) + # Lowercase column names + df.columns = map(str.lower, df.columns) + # Rename fields + df = df.rename(columns={"class": "label", "content": "text"}) + # Remove comment_id, label fields + df = df.drop("comment_id", axis=1) + df = df.drop("label", axis=1) + # Shuffle order + df = df.sample(frac=1, random_state=123).reset_index(drop=True) + dfs.append(df) + return pd.concat(dfs) diff --git a/pyproject.toml b/pyproject.toml index 2605b9fb..66764485 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ exclude = ''' | \.git | \.mypy_cache | \.tox - | \.env + | \.env.* | \.venv | _build | build diff --git a/scripts/build.py b/scripts/build.py index c116a2ba..5c048fa8 100644 --- a/scripts/build.py +++ b/scripts/build.py @@ -178,6 +178,8 @@ def call_jupytext(notebook: Notebook, out_fname: str, to_ipynb: bool) -> None: from_fmt, "--opt", "notebook_metadata_filter=-all", + "--opt", + "cell_metadata_filter=tags", notebook.py if to_ipynb else notebook.ipynb, "-o", out_fname, diff --git a/tox.ini b/tox.ini index 1bf7322d..92eff28c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,11 @@ [tox] skipsdist = true envlist = + getting_started, spouse, visual_relation, spam, + scene_graph, crowdsourcing, multitask, drybell, @@ -13,7 +15,9 @@ envlist = description = test/sync for {envname} deps = -rrequirements.txt + getting_started: -rgetting_started/requirements.txt spouse: -rspouse/requirements.txt + intro: -rintro/requirements.txt spam: -rspam/requirements.txt multitask: -rmultitask/requirements.txt visual_relation: -rvisual_relation/requirements.txt @@ -25,7 +29,9 @@ commands_pre = drybell: python -m spacy download en_core_web_sm # Available posargs: test, sync, html commands = + getting_started: python {toxinidir}/scripts/build.py {posargs:test} getting_started spouse: python {toxinidir}/scripts/build.py {posargs:test} spouse + intro: python {toxinidir}/scripts/build.py {posargs:test} intro spam: python {toxinidir}/scripts/build.py {posargs:test} spam multitask: python {toxinidir}/scripts/build.py {posargs:test} multitask visual_relation: python {toxinidir}/scripts/build.py {posargs:test} visual_relation