DVP IBM AI Engineering
Reference

Primer 5 — LLM-era Python

Dicts/JSON, **kwargs, decorators, with-blocks, lambdas — the Hugging Face reading patterns

Do this before Course 7. The GenAI half of the certificate (Hugging Face, LangChain) reads differently from the sklearn half: dicts everywhere, **kwargs, decorators, with blocks, lambdas. Five patterns, and all of it opens up.


Dicts everywhere — configs, batches, JSON

The LLM stack passes labelled data, not positional lists:

enc = {"input_ids": [101, 7592, 102], "attention_mask": [1, 1, 1]}

print(enc["input_ids"])          # read by name
enc["labels"] = [1]              # add by name
print(enc.keys())                # what's in the box?

When Course 7 shows enc = tokenizer("Hello world", return_tensors='pt') — that result behaves like this dict. enc['input_ids'], enc['attention_mask']: keys in a labelled box. JSON, the format of every API and dataset file in Courses 11–13, is this same structure written as text; {"prompt": ..., "chosen": ..., "rejected": ...} is a dict per training example.

import json
text = json.dumps(enc)      # dict → text  (to save/send)
back = json.loads(text)     # text → dict  (to use)

**kwargs and the double-star — the syntax you'll see hourly

Two related uses. First, in a call — unpack a dict into named arguments:

settings = {"epochs": 3, "batch_size": 16}
train(**settings)            # EXACTLY: train(epochs=3, batch_size=16)

Now decode the line from the Course 9 notes that looks like wizardry:

out = model.generate(**tok("Once upon a time", return_tensors='pt'), max_new_tokens=50)

Inside-out: tokenize the text → get a dict-like with input_ids and attention_mask → ** spreads those into generate as named arguments, alongside max_new_tokens. One line, three ideas you already own.

Second, in a definition — accept any named arguments:

def flexible(**kwargs):      # kwargs arrives as a dict
    print(kwargs)

flexible(lr=0.1, epochs=3)   # {'lr': 0.1, 'epochs': 3}

That's why Hugging Face functions accept dozens of options you never see listed: they take **kwargs and pass them along. (*args is the same trick for positional arguments — a tuple instead of a dict.)

Self-check — answer without looking

Rewrite without the star: LoraConfig(**{"r": 8, "lora_alpha": 16})


Decorators — the @ line above a function

Course 12 defines tools like this:

@tool
def get_frame_rate(codec: str) -> str:
    """Return the standard frame rate for a codec."""
    ...

A decorator wraps a function to give it extra behaviour. @tool is exactly get_frame_rate = tool(get_frame_rate) — the function is handed to tool, which returns an upgraded version (here: one registered so an LLM agent can call it).

You need to read decorators, not write them. When you see @something, think: "the function below is being handed to something for superpowers."

Two more pieces of that snippet, while we're here:


with — blocks that clean up after themselves

with open("notes.txt") as f:
    text = f.read()
# file is closed here, automatically, even if something failed

with runs a block under a manager that handles setup/teardown. Files close themselves. And now this line from every evaluation loop in Courses 4–13 decodes:

with torch.no_grad():
    preds = model(X_test)

"For this indented block, PyTorch: don't build the gradient machinery." Cheaper and correct at inference — the notes use it every time a model is evaluated. Same shape as the file example: enter a context, do the block, the context restores things after.


Lambdas and functions-as-values

A lambda is a one-line unnamed function:

double = lambda x: x * 2        # same as: def double(x): return x * 2

clips = ["b.mov", "a.mov", "c.mov"]
clips.sort(key=lambda name: name.lower())    # pass a FUNCTION as an argument

The deeper idea: functions are values. They can be stored, passed, returned. That's what made decorators possible (tool(get_frame_rate) — a function passed to a function), what key= is, and what .map(...) does across LangChain and the datasets library in Courses 12–13.

Self-check — answer without looking
  1. Rewrite lambda t: t > 10 as a def.
  2. In dataset.map(lambda ex: tokenizer(ex["text"])) — what gets passed to the lambda, and what does it return? (Hint: ex is a dict. You know dicts now.)

Reading a full LLM-era snippet, unassisted

From the Course 12 notes — the RAG chain:

rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)
rag_chain.invoke("What is the retention policy?")

Nothing left in this certificate's code that you can't at least parse. Whenever a line resists: unfold it (comprehensions), name the construct (**? decorator? context manager?), check shapes/keys, and if it still resists — highlight it and ask the teacher to walk it line by line. That's what it's there for.


Primer 5 in one page