The language half of this course, from counting words to contextual representations, in one comparison on the same task.
import os
os.environ['HF_HUB_OFFLINE'] = '0'
import torch
from transformers import AutoTokenizer, AutoModel
NAME = 'prajjwal1/bert-tiny'
tok = AutoTokenizer.from_pretrained(NAME)
model = AutoModel.from_pretrained(NAME)
model.eval()
import random
random.seed(3)
CATEGORY_WORDS = {
'laptop': ['screen', 'keyboard', 'battery', 'trackpad', 'charger'],
'kitchen': ['pan', 'lid', 'handle', 'blade', 'kettle'],
'clothing': ['stitching', 'zip', 'collar', 'sleeve', 'fabric'],
}
GOOD = ['excellent', 'solid', 'reliable', 'sturdy', 'sharp']
BAD = ['flimsy', 'loose', 'faulty', 'scratched', 'noisy']
DEGREE = ['very', 'quite', 'surprisingly', 'really']
NEGATE = ['not', 'far from', 'hardly']
FRAME = [
'the {part} is {opinion}, {tail}',
'arrived quickly and the {part} was {opinion}, {tail}',
'i wanted to like it but the {part} is {opinion}, {tail}',
'after a month the {part} is still {opinion}, {tail}',
'for the price the {part} is {opinion}, {tail}',
]
TAIL = ['ordered on tuesday', 'the packaging was fine',
'second one i have bought', 'delivery took a week',
'as described in the listing']
def make_review(category, positive):
part = random.choice(CATEGORY_WORDS[category])
if positive:
if random.random() < 0.5:
opinion = '%s %s' % (random.choice(NEGATE), random.choice(BAD))
else:
opinion = '%s %s' % (random.choice(DEGREE), random.choice(GOOD))
tail = random.choice(TAIL)
else:
if random.random() < 0.5:
opinion = '%s %s' % (random.choice(NEGATE), random.choice(GOOD))
else:
opinion = '%s %s' % (random.choice(DEGREE), random.choice(BAD))
tail = random.choice(TAIL)
return random.choice(FRAME).format(part=part, opinion=opinion,
tail=tail)
texts, sentiment, category = [], [], []
for _ in range(400):
for cat in CATEGORY_WORDS:
for pos in (True, False):
texts.append(make_review(cat, pos))
sentiment.append(1 if pos else 0)
category.append(cat)
from sklearn.model_selection import train_test_split
(train_x, test_x, train_s, test_s,
train_c, test_c) = train_test_split(texts, sentiment, category,
test_size=0.3, random_state=0,
stratify=sentiment)
import time
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
def timed(fn):
start = time.perf_counter()
score = fn()
return score, time.perf_counter() - start
def unigrams():
m = make_pipeline(CountVectorizer(),
LogisticRegression(max_iter=2000))
m.fit(train_x, train_s)
return m.score(test_x, test_s)
def bigrams():
m = make_pipeline(TfidfVectorizer(ngram_range=(1, 2)),
LogisticRegression(max_iter=2000))
m.fit(train_x, train_s)
return m.score(test_x, test_s)
def transformer():
def embed(sentences, batch=32):
out = []
for i in range(0, len(sentences), batch):
enc = tok(sentences[i:i + batch], return_tensors='pt',
padding=True, truncation=True, max_length=48)
with torch.no_grad():
h = model(**enc).last_hidden_state
mask = enc['attention_mask'].unsqueeze(-1)
out.append(((h * mask).sum(1) / mask.sum(1)).numpy())
return np.vstack(out)
clf = LogisticRegression(max_iter=3000).fit(embed(train_x), train_s)
return clf.score(embed(test_x), test_s)
print('%-30s %10s %10s' % ('', 'accuracy', 'seconds'))
for name, fn in [('bag of words, week 4', unigrams),
('tfidf bigrams, week 4', bigrams),
('frozen transformer, week 7', transformer)]:
score, took = timed(fn)
print('%-30s %10.4f %10.2f' % (name, score, took))
accuracy seconds
bag of words, week 4 0.4931 0.03
tfidf bigrams, week 4 1.0000 0.03
frozen transformer, week 7 0.6236 0.62
Three generations of technique on one task, with the time each took. The middle row is the best value by a wide margin here, and that ordering is specific to this corpus rather than general. What is general is that the comparison is cheap to run and almost nobody runs it.
What the language half established
- Text becomes numbers before anything else happens, and that step decides more than the model does.
- Word order is worth everything on some tasks and nothing on others. Measure which you have.
- Meaning can be learned from context alone, and compressed into vectors where related words sit near each other.
- Contextual models solve ambiguity and order together, which is why they replaced static vectors.
- Generation is next token prediction, which is why fluency and accuracy are unrelated in these systems.
- The expensive method is not automatically better, and the comparison takes an afternoon.
Going into the vision weeks
Weeks 8 to 10 do the same journey for images: raw pixels, then features somebody designed, then features learned from data. The arc is deliberately the same, because the lesson is the same one, and the habits from this half transfer intact.