Learn AI Series (#144) - Few-Shot and Zero-Shot Learning

avatar

Learn AI Series (#144) - Few-Shot and Zero-Shot Learning

variant-a-09-blue.png

What will I learn

  • The N-way K-shot setup: the vocabulary of few-shot learning, and why we train in "episodes" that simulate learning from almost nothing;
  • Prototypical Networks: the almost-too-simple idea of embedding everything and classifying by distance to a class prototype -- and why it works;
  • MAML: model-agnostic meta-learning, an initialization you can fine-tune to any new task in a handful of gradient steps (with actual second-order gradients);
  • transfer learning as few-shot: the dirty secret that a frozen pretrained backbone plus a linear probe quietly beats most of the clever meta-learning stuff;
  • zero-shot with LLMs: doing tasks the model never saw a single example of, and how in-context learning smuggles examples into the prompt with zero weight updates;
  • an honest verdict on when to reach for which of these -- because in 2026 the right answer is often the least glamorous one.

Requirements

  • A working modern computer running macOS, Windows or Ubuntu;
  • Python 3.10+ with PyTorch installed (pip install torch) -- everything here runs on a CPU in seconds, nothing needs a GPU;
  • You've been through PyTorch fundamentals (#42-44), you remember embeddings and vector search (#63) and prompt engineering (#62), and it helps enormously to have read last episode on continual learning (#143), because we walk straight through the door I left open at the end of it.

Difficulty

  • Beginner

Curriculum (of the Learn AI Series):

Learn AI Series (#144) - Few-Shot and Zero-Shot Learning

I ended last episode with a promise, so let me pay it off straight away. In #143 we spent the whole hour on catastrophic forgetting -- a network that learns birds and quietly forgets cats -- and I closed by pointing at the OPPOSITE problem. Every trick we saw there (EWC, replay, progressive columns, LwF) assumed you have PLENTY of data for each new task: enough to compute a Fisher matrix, fill a replay buffer, train a fresh column. But the most human kind of learning is nothing like that. Show a child a single zebra and they spot the next one across a field. You do not need 50,000 labelled photos of a pangolin to recognise one -- somebody shows you a picture, maybe two, and you've got it for life.

So today we flip the constraint. In stead of "how do I keep learning without forgetting", the question is "how do I learn a brand-new thing from almost nothing -- five examples, one example, or a plain-English description and zero examples at all?" That is few-shot learning and zero-shot learning, and it turns out to be one of the most practically useful corners of the whole field -- because in the real world you very rarely get the tidy 50,000-row dataset the benchmarks assume. You get "here are 5 examples of this new category, I need a classifier by Friday." Let's dive right in ;-)

Solutions to episode #143's exercises

House rules first, same as always -- we settle last time's homework before we open anything new. Episode #143 was continual learning, and all three tasks were about feeling the mechanism in your own hands rather than nodding along to the paper.

Exercise 1 -- Measure the forgetting, then soften it. Take the eight-line catastrophic-forgetting demo (task A = +1, task B = -1), and after training B, mix in a small replay set of task-A examples and train a little more. Report A's error before and after the replay phase.

import torch
import torch.nn as nn

torch.manual_seed(0)
net = nn.Sequential(nn.Linear(4, 32), nn.ReLU(), nn.Linear(32, 1))
opt = torch.optim.Adam(net.parameters(), lr=1e-2)

X = torch.randn(64, 4)
task_a_y = torch.ones(64, 1)      # task A: always +1
task_b_y = -torch.ones(64, 1)     # task B: always -1

def err(target):
    with torch.no_grad():
        return round(nn.functional.mse_loss(net(X), target).item(), 4)

def train(target, steps=200):
    for _ in range(steps):
        loss = nn.functional.mse_loss(net(X), target)
        opt.zero_grad(); loss.backward(); opt.step()

train(task_a_y)
train(task_b_y)
print("after B        -> error on A:", err(task_a_y))   # blown up, A is gone

# THIRD phase: replay just 16 task-A examples alongside B
replay_x, replay_a = X[:16], task_a_y[:16]
for _ in range(200):
    # one batch of new task B + a small replayed slice of task A
    loss = (nn.functional.mse_loss(net(X), task_b_y)
            + nn.functional.mse_loss(net(replay_x), replay_a))
    opt.zero_grad(); loss.backward(); opt.step()

print("after replay   -> error on A:", err(task_a_y))   # pulled back down
print("after replay   -> error on B:", err(task_b_y))   # still fine

The error on A collapses back toward zero the moment those 16 examples re-enter the gradient. Here's the one-sentence why: catastrophic forgetting is a gradient problem, not a memory problem -- the weights drift only because nothing is pushing back, and even a handful of replayed examples restores a gradient term that pulls the weights back toward a solution that satisfies A as well as B. That is exactly why "keep a tiny replay buffer" was the boring winner I kept banging on about last time.

Exercise 2 -- Feel the lambda trade-off in EWC. Train task A, build the EWC object, then train task B with lambda_ewc set to 0, 100, and 100000, and report accuracy on BOTH tasks for each setting.

# assumes the EWC class from episode #143 is in scope
import torch, torch.nn as nn
from torch.utils.data import TensorDataset, DataLoader

def make_task(seed):
    g = torch.Generator().manual_seed(seed)
    X = torch.randn(200, 8, generator=g)
    y = (X[:, 0] + X[:, 1] > 0).long()          # a simple linear rule per task
    return X, y

def accuracy(model, X, y):
    with torch.no_grad():
        return (model(X).argmax(1) == y).float().mean().item()

Xa, ya = make_task(1)
Xb, yb = make_task(2)
loader_a = DataLoader(TensorDataset(Xa, ya), batch_size=32, shuffle=True)

for lam in (0.0, 100.0, 100000.0):
    model = nn.Sequential(nn.Linear(8, 32), nn.ReLU(), nn.Linear(32, 2))
    opt = torch.optim.Adam(model.parameters(), lr=1e-2)
    for _ in range(30):                          # learn task A
        for bx, by in loader_a:
            loss = nn.functional.cross_entropy(model(bx), by)
            opt.zero_grad(); loss.backward(); opt.step()
    ewc = EWC(model, loader_a, lambda_ewc=lam)   # snapshot + Fisher on A
    for _ in range(30):                          # learn task B, guarded by EWC
        loss = nn.functional.cross_entropy(model(Xb), yb) + ewc.penalty()
        opt.zero_grad(); loss.backward(); opt.step()
    print(f"lambda={lam:>8.0f} | A acc={accuracy(model, Xa, ya):.2f}"
          f" | B acc={accuracy(model, Xb, yb):.2f}")

At lambda=0 you get the pure forgetting story: B looks great, A has cratered. Crank it to 100000 and you flip the failure -- A is frozen solid and protected, but the penalty is so heavy the model can barely budge to learn B. The middle value is the whole point. The sentence the exercise wanted: a low lambda buys plasticity at the cost of memory, a huge lambda buys memory at the cost of plasticity, and the entire craft of EWC is finding the knee of that curve for your particular pair of tasks -- which, as I warned, is a fiddle.

Exercise 3 -- Count the cost of a progressive net. Instantiate ProgressiveNet, add five columns, and sum p.numel() after each one to watch the parameter count climb.

# assumes the ProgressiveNet class from episode #143 is in scope
model = ProgressiveNet(input_dim=64, hidden_dim=128, n_classes=10)

def total_params(m):
    return sum(p.numel() for p in m.parameters())

print(f"task 1: {total_params(model):>8d} params")
for task in range(2, 6):
    model.add_column()
    print(f"task {task}: {total_params(model):>8d} params")

Print it and the numbers march upward every single task -- and because each new column also grows lateral connections that read from ALL previous columns, the growth is not even linear, it curves upward. The fatal-flaw sentence: progressive networks guarantee zero forgetting by never touching old weights, but they pay for that guarantee with a parameter budget that grows without bound in the number of tasks, which is precisely the opposite of what "lifelong learning" is supposed to mean. Right -- homework settled. Now let's teach a model to learn from five examples ;-)

The few-shot setup: N-way K-shot

Before any code, we need the vocabulary, because few-shot research has its own dialect and it trips people up.

An N-way K-shot problem means: N classes, K labelled examples per class. A "5-way 1-shot" task hands you 5 classes with exactly 1 labelled example each, and asks you to classify new inputs into one of those 5. The tiny labelled set you're given is called the support set -- think of it as the "training data" for this one little task. The unlabelled inputs you actually have to classify are the query set.

Now here is the clever bit, and it is the thing most people miss. You do not train a few-shot model on a few examples. That would be hopeless -- five examples cannot teach a deep network anything. In stead you train it on THOUSANDS of few-shot tasks, each sampled from a big base dataset with many classes. Every training step you sample a fresh N-way K-shot task (a fresh support set and query set), let the model try to solve it, and update. This is called episodic training, and the shift in objective is the whole idea: the model is not learning to classify any specific set of classes, it is learning how to learn from few examples. Meta-learning, in one sentence. You are training the learner, not the classifier.

Prototypical Networks

Prototypical Networks (Snell et al., 2017) are the cleanest few-shot method there is, and the idea is almost too simple to have earned a paper: embed everything into a space where classification is just nearest-neighbour.

For each class in the support set, compute the prototype -- the mean of that class's embeddings. Then classify each query by finding the nearest prototype. That's it. All the intelligence lives in the encoder that produces the embeddings, and this connects straight back to the embeddings-and-vector-search episode (#63): once you have a good representation, "similar things sit close together" does the heavy lifting for free.

import torch
import torch.nn as nn
import torch.nn.functional as F

class PrototypicalNetwork(nn.Module):
    """Few-shot classification via class prototypes in embedding space."""
    def __init__(self, input_dim=784, embed_dim=64):
        super().__init__()
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, 256),
            nn.ReLU(),
            nn.Linear(256, 128),
            nn.ReLU(),
            nn.Linear(128, embed_dim),
        )

    def forward(self, support_x, support_y, query_x):
        """
        support_x: (n_support, input_dim) - labelled examples
        support_y: (n_support,)           - class labels (0 to N-1)
        query_x:   (n_query, input_dim)   - examples to classify
        """
        support_emb = self.encoder(support_x)  # (n_support, embed_dim)
        query_emb = self.encoder(query_x)      # (n_query, embed_dim)

        # class prototypes: the mean embedding per class
        classes = torch.unique(support_y)
        prototypes = []
        for c in classes:
            mask = (support_y == c)
            prototypes.append(support_emb[mask].mean(dim=0))
        prototypes = torch.stack(prototypes)   # (n_classes, embed_dim)

        # classify queries by (negative) distance to each prototype
        dists = torch.cdist(query_emb, prototypes)  # (n_query, n_classes)
        return -dists                               # closer = higher score

Notice the trick in that last line: we use the negative squared distance as logits. A query close to a prototype gets a high score, a query far away gets a low one, and once you have logits you can run them straight through a softmax and a cross-entropy loss like any ordinary classifier. Now the episodic training loop, which samples a fresh task every step:

def sample_episode(dataset, n_way, k_shot, n_query):
    """Sample one N-way K-shot episode. dataset = {label: tensor_of_examples}."""
    classes = list(dataset.keys())
    selected = torch.randperm(len(classes))[:n_way]

    support_x, support_y, query_x, query_y = [], [], [], []
    for new_label, orig in enumerate(selected):
        examples = dataset[classes[orig]]
        perm = torch.randperm(len(examples))
        s_idx, q_idx = perm[:k_shot], perm[k_shot:k_shot + n_query]
        support_x.append(examples[s_idx]); support_y += [new_label] * k_shot
        query_x.append(examples[q_idx]);   query_y += [new_label] * len(q_idx)

    return (torch.cat(support_x), torch.tensor(support_y),
            torch.cat(query_x), torch.tensor(query_y))


def train_prototypical(model, dataset, n_way=5, k_shot=5, n_query=15,
                       episodes=1000, lr=1e-3):
    """Train via episodic few-shot tasks."""
    opt = torch.optim.Adam(model.parameters(), lr=lr)
    for ep in range(episodes):
        sx, sy, qx, qy = sample_episode(dataset, n_way, k_shot, n_query)
        logits = model(sx, sy, qx)
        loss = F.cross_entropy(logits, qy)
        opt.zero_grad(); loss.backward(); opt.step()
        if (ep + 1) % 100 == 0:
            acc = (logits.argmax(1) == qy).float().mean()
            print(f"episode {ep+1}: loss={loss:.3f}, acc={acc:.1%}")

And because I do not like leaving code that you cannot actually run, here's a tiny self-contained smoke test on synthetic "classes" -- each class is a blob of points around its own random centre, which is exactly the structure prototypes are built to exploit:

torch.manual_seed(0)
# 10 fake classes, each a Gaussian blob around a random centre in 784-D
dataset = {}
for c in range(10):
    centre = torch.randn(784) * 3.0
    dataset[c] = centre + torch.randn(50, 784) * 0.5   # 50 examples per class

model = PrototypicalNetwork(input_dim=784, embed_dim=64)
train_prototypical(model, dataset, n_way=5, k_shot=5, episodes=300)

Run that and the accuracy climbs into the high nineties within a few hundred episodes, on FIVE examples per class at test time. Why does it work so well? Because the encoder learns a representation where each class collapses to a tight cluster, and once your clusters are tight, one prototype per class is enough to say where that class lives. No per-task training, no fine-tuning -- just embed, average, and measure distance.

MAML: learning to learn

Prototypical Networks bet everything on good embeddings. MAML -- Model-Agnostic Meta-Learning (Finn et al., 2017) -- makes a completely different bet: don't learn good features, learn good starting weights. Specifically, an initialization from which a few steps of ordinary gradient descent on a tiny support set produce a model that's good at that task.

Read that twice, because it is genuinely a mind-bender the first time. MAML does not learn to solve any task. It learns a POINT IN WEIGHT SPACE that is a short gradient-hop away from the solution to any task in the family. The training has two nested loops: an inner loop that adapts the current weights to one task with a few gradient steps, and an outer loop that asks "how good was that adapted model?" and nudges the INITIALIZATION so that next time the adaptation lands even better.

import torch
import torch.nn as nn
import torch.nn.functional as F
from collections import OrderedDict

class MAMLClassifier(nn.Module):
    """Simple classifier whose forward pass can run on external params."""
    def __init__(self, input_dim=784, hidden_dim=128, n_classes=5):
        super().__init__()
        self.net = nn.Sequential(OrderedDict([
            ('fc1', nn.Linear(input_dim, hidden_dim)),
            ('relu1', nn.ReLU()),
            ('fc2', nn.Linear(hidden_dim, hidden_dim)),
            ('relu2', nn.ReLU()),
            ('fc3', nn.Linear(hidden_dim, n_classes)),
        ]))

    def forward(self, x):
        return self.net(x)

    def functional_forward(self, x, params):
        """Forward pass using externally supplied weights (for the inner loop)."""
        h = F.linear(x, params['net.fc1.weight'], params['net.fc1.bias'])
        h = F.relu(h)
        h = F.linear(h, params['net.fc2.weight'], params['net.fc2.bias'])
        h = F.relu(h)
        return F.linear(h, params['net.fc3.weight'], params['net.fc3.bias'])


def maml_train_step(model, task_batch, inner_lr=0.01, inner_steps=5):
    """One outer-loop step of MAML over a batch of tasks."""
    meta_loss = 0.0
    for support_x, support_y, query_x, query_y in task_batch:
        params = OrderedDict(model.named_parameters())

        # INNER loop: adapt a temporary copy of the weights to THIS task
        for _ in range(inner_steps):
            logits = model.functional_forward(support_x, params)
            loss = F.cross_entropy(logits, support_y)
            grads = torch.autograd.grad(loss, params.values(), create_graph=True)
            params = OrderedDict(
                (name, p - inner_lr * g)
                for (name, p), g in zip(params.items(), grads)
            )

        # OUTER loop: score the ADAPTED weights on the query set
        query_logits = model.functional_forward(query_x, params)
        meta_loss += F.cross_entropy(query_logits, query_y)

    return meta_loss / len(task_batch)

The single most important line in there is create_graph=True. It tells PyTorch to keep the graph of the gradient computation itself, so that when the outer loss backpropagates, it flows through the inner-loop gradient steps and reaches the original initialization. That is what "learning to learn" cashes out to mechanically: second-order gradients, gradients of the adaptation with respect to the starting point. Beautiful -- and, honestly, expensive. Backpropagating through five inner steps is heavy, which is why the first-order approximations (drop the second-order terms, or Reptile, which is even simpler) exist and, annoyingly for the theory, work almost as well in practice.

Transfer learning as few-shot

Now the dirty secret of the whole subfield, and I would be doing you a disservice not to say it plainly. In a LOT of practical settings, a boring pretrained model with a linear probe beats every fancy meta-learning method on the table.

The recipe could not be simpler. Take a model pretrained on a big dataset (ImageNet for vision, a large language model for text -- foundation models, #137). Freeze the backbone. Run your handful of examples through it to get features, and train a trivial classifier -- often just class centroids or a single linear layer -- on top. The pretrained features are already so good that "which of these general features predict my new classes" is nearly all that's left to learn.

def transfer_few_shot(pretrained_encoder, support_x, support_y, query_x):
    """A simple, hard-to-beat transfer-learning baseline for few-shot."""
    with torch.no_grad():                       # backbone is FROZEN
        support_features = pretrained_encoder(support_x)
        query_features = pretrained_encoder(query_x)

    support_features = F.normalize(support_features, dim=1)
    query_features = F.normalize(query_features, dim=1)

    # class centroids = prototypes, but on frozen pretrained features
    classes = torch.unique(support_y)
    centroids = torch.stack([
        support_features[support_y == c].mean(dim=0) for c in classes
    ])
    centroids = F.normalize(centroids, dim=1)

    # cosine similarity as logits (temperature-scaled)
    return (query_features @ centroids.T) * 10.0

Look closely and you'll notice this is Prototypical Networks with the encoder swapped for a frozen pretrained one and cosine similarity in stead of Euclidean distance. That's not a coincidence -- it's the point. A well-pretrained model is ALREADY a few-shot learner, because generic features learned from massive data transfer to new tasks almost by definition. The better the pretraining, the less there is left for the few examples to teach. This is why "just fine-tune a foundation model" quietly ate a decade of clever few-shot research for lunch.

Zero-shot with LLMs: in-context learning

Zero-shot learning is the extreme case: no task-specific examples at all, not even five. And large language models (episodes #57-62) are startlingly good at it. You can ask an LLM to classify sentiment, translate a sentence, extract fields from a messy invoice -- tasks nobody ever explicitly trained it to do -- purely through the prompt. You do not gather training data. You describe the task in words.

In-context learning blurs the line between zero-shot and few-shot in a way that still slightly amazes me: you put the examples inside the prompt, and the model "learns" the pattern from its context window with zero gradient updates. No weights change. The examples just condition the model's output distribution for that one call.

def build_prompt(task_description, examples, query):
    """Build a zero-shot or few-shot prompt for an LLM (examples may be empty)."""
    prompt = f"Task: {task_description}\n\n"
    for i, (text, label) in enumerate(examples):
        prompt += f"Example {i+1}:\nText: {text}\nLabel: {label}\n\n"
    prompt += f"Now classify:\nText: {query}\nLabel:"
    return prompt

# Zero-shot: just a task description, no examples at all
zero_shot = build_prompt(
    "Classify the sentiment as positive or negative.",
    [],
    "This product exceeded all my expectations."
)

# Few-shot: the SAME task, but with a few in-context examples
few_shot = build_prompt(
    "Classify the sentiment as positive or negative.",
    [
        ("Great quality, fast shipping.", "positive"),
        ("Broke after one day. Waste of money.", "negative"),
        ("Absolutely love it, buying another.", "positive"),
    ],
    "This product exceeded all my expectations."
)

The remarkable part: adding those three examples reliably improves accuracy on the fourth, and NOT a single weight moved. The examples do two jobs -- they pin down the exact output format you want ("positive"/"negative", not a three-paragraph essay), and they calibrate the model's decision boundary. Research even shows the order of the examples in the prompt shifts accuracy, and that a class-balanced set of examples improves calibration even when the model already "knows" the task zero-shot. If you want the mechanical version of few-shot without training anything, in-context learning is it -- and it connects right back to the prompt-engineering episode (#62), because a few-shot prompt is just a prompt with worked examples in it.

There's a deeper way to do zero-shot that doesn't even need a generative model, and it's worth a mention because it ties the whole episode together. Embed your query and embed a plain-text description of each class into the same space (the way multimodal models like CLIP put images and captions in one embedding space, #75, #138), then pick the nearest label description by cosine similarity:

def zero_shot_by_embedding(embed_fn, query_text, label_texts):
    """Zero-shot classify by comparing query and label DESCRIPTIONS in embedding space."""
    q = F.normalize(embed_fn([query_text]), dim=1)        # (1, D)
    labels = F.normalize(embed_fn(label_texts), dim=1)    # (n_labels, D)
    sims = (q @ labels.T).squeeze(0)                      # cosine similarity
    best = sims.argmax().item()
    return label_texts[best], sims[best].item()

# labels are just DESCRIPTIONS, never seen as training examples:
# zero_shot_by_embedding(embed, "the wifi kept dropping all night",
#                        ["a positive hotel review", "a negative hotel review"])

Same prototype-and-distance idea as the very first method in this episode -- except the "prototype" for each class is now a sentence describing it, and you never saw a single labelled example. Few-shot and zero-shot, it turns out, are the same trick viewed at different amounts of data: get everything into a good space, then measure closeness.

When to reach for which

So which do you actually use? Here's my honest field guide, no hedging.

  • Prototypical Networks / metric learning -- when you control the embedding model and have a meta-training set with many classes to do episodic training on. Great for image classification, face verification, product matching.
  • MAML / optimization-based -- when tasks are diverse and you need genuinely fast adaptation to new ones. More flexible than metric learning, but the second-order gradients make it the most expensive to train, and the fiddliest to get stable.
  • Transfer learning + linear probe -- when you have a strong pretrained model and your target isn't a million miles from its pretraining domain. This is the DEFAULT in industry: simple, robust, and embarrassingly hard to beat. If you only remember one thing from today, remember this one.
  • LLM in-context learning -- when the task can be written down in words and you don't need a custom model at all. Fastest path from "idea" to "working prototype": no training run, no labelled set, just a prompt. Your ceiling is the LLM's own capability.

Nota bene: none of these is "the winner". They sit on a spectrum of how much you're willing to build versus how much you're willing to borrow. And notice what ALL of them lean on -- pattern-matching in some representation, whether it's an embedding space or the LLM's context window. Which is exactly the thing to keep in the back of your mind, because there's a whole other tradition that says raw pattern-matching, however clever, is not the same as reasoning with explicit rules... but that thread is for next time ;-)

Exercises

Get your hands dirty before the next episode. Three tasks, climbing in difficulty:

  1. 1-shot vs 5-shot. Take the synthetic-blob smoke test for PrototypicalNetwork and train two models: one with k_shot=1 and one with k_shot=5, everything else identical. Report the final query accuracy of each, and explain in one sentence why more shots per class gives a more reliable prototype (hint: think about what averaging does to noise).

  2. Break the prototype with an outlier. Build a single 3-way 5-shot support set by hand, compute the three prototypes, then corrupt ONE support example of class 0 by adding a huge vector to it. Recompute class 0's prototype and measure how far it moved. In one sentence, explain why the mean is fragile here and name one thing you could do about it (median? trimmed mean?).

  3. Zero-shot vs few-shot prompt, by hand. Using build_prompt, construct a zero-shot and a 3-shot prompt for a NEW task of your choice (spam/not-spam, topic labelling, whatever). You don't need a live LLM -- just print both prompts and, in two or three sentences, argue which one you'd trust more on a genuinely ambiguous input and why the in-context examples help even though no weights change.

We'll open next episode with full solutions, as always.

Quick recap

  • Few-shot learning is the art of learning a new class from a handful of examples; the magic is episodic training, where you train on thousands of tiny N-way K-shot tasks so the model learns how to learn, not how to classify one fixed set;
  • Prototypical Networks embed everything, average each class into a prototype, and classify queries by distance -- simple, effective, and a direct cousin of embedding search (#63);
  • MAML learns an initialization that adapts to any new task in a few gradient steps, using create_graph=True second-order gradients to literally "learn to learn" -- elegant but pricey;
  • transfer learning (frozen pretrained backbone + linear probe or centroids) is the unglamorous baseline that quietly beats most meta-learning methods, because a good pretrained model is already a few-shot learner;
  • zero-shot with LLMs performs tasks with no task-specific examples via the prompt, and in-context learning smuggles examples into the context window with zero weight updates -- same distance-in-a-good-space idea, viewed at the low-data extreme;
  • the right choice is a spectrum of build-versus-borrow: data availability, compute budget, and how far your target sits from an available pretrained model.

And here's the thread I'll leave dangling for next time. Everything today -- prototypes, MAML, in-context prompts -- is still pure pattern recognition. Powerful pattern recognition, but pattern recognition. It has no explicit notion of a RULE. It cannot tell you "all A are B, this is an A, therefore it is a B" and be certain, the way a logic engine can. Symbolic AI could do that reasoning flawlessly for decades but couldn't SEE or read messy real-world data; neural nets can see and read brilliantly but reason fuzzily. So what happens if you bolt the two together -- neural perception feeding hard symbolic logic? That marriage has a name, and it's where we head next ;-)

De groeten, en tot de volgende keer! Go train a prototype on five examples and watch it actually work -- that moment never gets old. Thanks for reading! ;-)

@scipio



0
0
0.000
0 comments