Checklist NEOAI 2026 Day 2 · Task 6
SOTA-AI Editorial
CutiesNLP
Answer 1,000 multiple-choice questions using a stripped Qwen3.5-2B model whose embedding table, first layer and language-modelling head have been removed.
Hints
Hint 1
What is the purpose of the final layers?
Hint 2
Which layers provide the most useful representations for this task?
The solution in one line
Extract hidden states from different layers and use them as features for a logistic regression classifier.
The editorial
Solution
The baseline extracts the last hidden state and trains a logistic regression classifier. However, the last layer may produce less useful representations for this task because its outputs are optimized for next-token prediction. We hypothesized that intermediate layers contain more useful features for this task.
After experimenting with each layer and using cross-validation, we obtained the following results. Each logistic regression uses C=0.01.
| Layer | Train acc | Val acc |
|---|---|---|
| layer 0 | 0.778 | 0.280 |
| layer 1 | 0.828 | 0.400 |
| layer 2 | 0.912 | 0.490 |
| layer 3 | 0.933 | 0.530 |
| layer 4 | 0.972 | 0.450 |
| layer 5 | 0.967 | 0.460 |
| layer 6 | 0.963 | 0.480 |
| layer 7 | 0.985 | 0.430 |
| layer 8 | 0.990 | 0.510 |
| layer 9 | 0.997 | 0.550 |
| layer 10 | 0.997 | 0.640 |
| layer 11 | 0.997 | 0.690 |
| layer 12 | 0.997 | 0.710 |
| layer 13 | 0.993 | 0.740 |
| layer 14 | 1.000 | 0.750 |
| layer 15 | 0.988 | 0.750 |
| layer 16 | 0.988 | 0.710 |
| layer 17 | 0.988 | 0.710 |
| layer 18 | 0.988 | 0.700 |
| layer 19 | 0.988 | 0.670 |
| layer 20 | 0.988 | 0.440 |
| layer 21 | 0.965 | 0.400 |
| layer 22 | 0.948 | 0.470 |
| layer 23 | 0.907 | 0.420 |
Using each of the three layers with the highest validation accuracy (layers 13, 14, and 15), we obtained the test accuracies reported in the Results section.
After experimenting with individual layers, we tried combining multiple layers by concatenating hidden states.
| Layers | Train acc | Val acc |
|---|---|---|
| layer 13, 14 | 1.000 | 0.750 |
| layer 13, 15 | 1.000 | 0.750 |
| layer 14, 15 | 1.000 | 0.750 |
| layer 13, 14, 15 | 1.000 | 0.760 |
Results
| Method | Public score | Private score |
|---|---|---|
| baseline | 0.370 | 0.348 |
| layer 13 | 0.760 | 0.748 |
| layer 14 | 0.752 | 0.763 |
| layer 15 | 0.731 | 0.750 |
| layer 13, 14 | 0.762 | 0.763 |
| layer 13, 15 | 0.750 | 0.748 |
| layer 14, 15 | 0.739 | 0.753 |
| layer 13, 14, 15 | 0.754 | 0.751 |
Running Time
The solution using layers 13 and 14 takes approximately 2 minutes on a T4. Experimenting with all layers and some combinations, as in the Solution section, takes an additional minute.
Code
Solution code
import numpy as np
import torch
from sklearn.model_selection import StratifiedKFold
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
def get_hs(model, train, test):
Xs = []
y = []
y2 = []
for idx in range(len(train)):
data = train[idx]
pos = data["eol_positions"]
cr = data["correct_idx"]
embs = torch.from_numpy(data["embeds_after_l0"]).unsqueeze(0).cuda().half()
with torch.no_grad():
out = model.model(inputs_embeds=embs, output_hidden_states=True)
hs = torch.cat(out.hidden_states)
for i in range(4):
Xs.append(hs[:, pos[i]].cpu().numpy())
y.append(int(i == cr))
y2.append(cr)
Xs = np.stack(Xs).reshape(len(Xs) // 4, 4, 24, -1)
y = np.array(y).reshape(len(y) // 4, 4)
y2 = np.array(y2)
Xs_test = []
for idx in range(len(test)):
data = test[idx]
pos = data["eol_positions"]
embs = torch.from_numpy(data["embeds_after_l0"]).unsqueeze(0).cuda().half()
with torch.no_grad():
out = model.model(inputs_embeds=embs, output_hidden_states=True)
hs = torch.cat(out.hidden_states)
for i in range(4):
Xs_test.append(hs[:, pos[i]].cpu().numpy())
Xs_test = np.stack(Xs_test).reshape(len(Xs_test) // 4, 4, 24, -1)
return Xs, y, y2, Xs_test
def evaluate(Xs, y, y2, ly):
X_layer = Xs[:, :, ly]
train_accs = []
val_accs = []
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=0)
for train_idx, val_idx in skf.split(X_layer, y2):
X_train = X_layer[train_idx].reshape(4 * len(train_idx), -1)
X_val = X_layer[val_idx].reshape(4 * len(val_idx), -1)
y_train = y[train_idx].reshape(-1)
clf = Pipeline([
("scaler", StandardScaler()),
("logreg", LogisticRegression(random_state=0, max_iter=1000, C=1e-2)),
])
clf.fit(X_train, y_train)
pred = clf.predict_proba(X_train)[:, 1]
train_acc = (pred.reshape(-1, 4).argmax(axis=1) == y2[train_idx]).mean()
pred = clf.predict_proba(X_val)[:, 1]
val_acc = (pred.reshape(-1, 4).argmax(axis=1) == y2[val_idx]).mean()
train_accs.append(train_acc)
val_accs.append(val_acc)
return np.mean(train_accs), np.mean(val_accs)
def experiment(Xs, y, y2):
for ly in range(24):
train_acc, val_acc = evaluate(Xs, y, y2, ly)
print(f"Layer {ly}" f" {train_acc:.3f}" f" {val_acc:.3f}")
for ly in [[13, 14], [13, 15], [14, 15], [13, 14, 15]]:
train_acc, val_acc = evaluate(Xs, y, y2, ly)
print(f"Layer {ly}" f" {train_acc:.3f}" f" {val_acc:.3f}")
def solution(model, train, test):
Xs, y, y2, Xs_test = get_hs(model, train, test)
ly = [13, 14]
X = Xs[:, :, ly]
X = X.reshape(len(X) * 4, -1)
clf = Pipeline([
("scaler", StandardScaler()),
("logreg", LogisticRegression(random_state=0, max_iter=1000, C=1e-2)),
]).fit(X, y.reshape(-1))
X_test = Xs_test[:, :, ly]
X_test = X_test.reshape(len(X_test) * 4, -1)
test_preds = clf.predict_proba(X_test)[:, 1].reshape(-1, 4).argmax(axis=1)
return test_preds
Editorials are written by members of the SOTA community, not by the olympiad. The official statement and scoring win wherever an editorial differs.