AI Hints
Home›NLP Tutorial›Languages, Speech, and Scale
NLP · Lesson 42 of 106

NLP at Scale

Speed and cost.

⏱ 2 min read▶ Video at 1:14:00🎓 Free
Watch this lesson in the NLP courseWatch on YouTube ↗Full NLP course

Scribe works beautifully on a hundred tickets. Bright Books now has two hundred thousand a day. Costs that seemed tiny become a real bill, and waits that seemed short become complaints. Three simple ideas solve most of this.

NLP at Scale
From the video: NLP at Scale
  • A hundred tickets is easy
  • Two hundred thousand isn't
  • Three simple ideas

Batching

Processing text in batches is far more efficient than one at a time, because the maths runs in parallel. We time both. Run it. Each item in a batch takes a fraction of the time. It's free, and it's usually a one line change.

batching.pypython
import time
from sentence_transformers import SentenceTransformer

model = SentenceTransformer("all-MiniLM-L6-v2", device="cpu")
texts = ["where is my parcel"] * 256

start = time.time()
for t in texts[:32]:
    model.encode(t)
one = (time.time() - start) / 32

start = time.time()
model.encode(texts, batch_size=32)
many = (time.time() - start) / 256

print(f"One at a time: {one * 1000:.1f} ms each")
print(f"In batches:    {many * 1000:.1f} ms each")
Output
One at a time: 7.3 ms each
In batches:    0.4 ms each

Caching

Customers ask the same questions, again and again. So store each answer, and reuse it. We normalise the text, make a key, and check the cache first. Sixteen questions are sent, in two spellings. Run it. Only one real model call. Everything else came straight from memory. In real support systems, this can remove a large share of the bill.

cache.pypython
import hashlib
import time

cache = {}

def slow_model(text):
    time.sleep(0.05)
    return f"Answer to: {text}"

def cached(text):
    key = hashlib.sha256(text.strip().lower().encode()).hexdigest()
    if key not in cache:
        cache[key] = slow_model(text)
    return cache[key]

messages = ["What are your hours?"] * 8 + ["what are your hours? "] * 8
start = time.time()
for m in messages:
    cached(m)
print(f"16 questions in {time.time() - start:.2f}s, model calls: {len(cache)}")
Output
16 questions in 0.05s, model calls: 1

Routing by difficulty

Here's the biggest saving of all. Don't use a big model for everything. Simple tickets go to a template or a regex. Medium ones go to a small model. And only hard ones go to the large model. We estimate a day of two thousand tickets. Run it. Routing cuts the bill by a huge factor, compared with sending everything to the large model. Most messages are easy. Use the cheapest tool that does the job.

routing.pypython
def route(ticket):
    if len(ticket.split()) < 12 and "refund" in ticket.lower():
        return "regex + template", 0.0
    if len(ticket.split()) < 30:
        return "small model", 0.0002
    return "large model", 0.003

tickets = ["refund please"] * 1200 + ["short question " * 5] * 600 + ["long story " * 40] * 200

total = sum(route(t)[1] for t in tickets)
print(f"Routed cost per day:  ${total:.2f}")
print(f"All large model:      ${len(tickets) * 0.003:.2f}")
Output
Routed cost per day:  $0.72
All large model:      $6.00

Summary

Batch, cache, and route. Those three ideas cut most NLP bills dramatically. Scribe is fast and affordable. But we skipped some of the deepest ideas in language. Next, we go back and build a language model the old way, from pure counting.

Key takeaways
  • Batching is free speed
  • Caching removes repeat work
  • Route easy tickets to cheap tools
Next up: N-Gram Language Models — language from pure counting