← Back to the problem

IOAI 2024 · Solution

Help BOBAI: More Classification

This page gives the expected approach. Read it once you have searched.

Formatted by IOAI Training from the original (IOAI-official/IOAI-2024, licence CC-BY-4.0). The original is authoritative.

Training Dataset

import torch

dataset = torch.load('../training_set/train-dev_dataset_with_labels.pt')

inputs = dataset[:,:,:-1]
labels = dataset[:, :, -1]
inputs.shape
# prompt: count number of different labels and count occurence of each of them

import torch

unique_labels, counts = torch.unique(labels, return_counts=True)
num_labels = unique_labels.numel()

print("Number of different labels:", num_labels)
print("Count of each label:")
for label, count in zip(unique_labels, counts):
    print(f"Label {label}: {count}")
# prompt: split train test with inputs and labels

from sklearn.model_selection import train_test_split

train_inputs, test_inputs, train_labels, test_labels = train_test_split(
    inputs, labels, test_size=0.2, random_state=42
)

Solution

Below you will find a very naive baseline solution: given an input vector, we use either randomly assign one of the new labels (5 and 6) with uniform probability over a 7-way classification, or we use the base classifier to make a prediction.

You can replace the code below with your solution.

# prompt: get the ones with label bigger than 4 into another array and train a KNN on it

# Get the indices of samples with labels greater than 4
indices_greater_than_4 = (train_labels > 4).nonzero()

# Extract the corresponding inputs and labels
train_inputs_greater_than_4 = train_inputs[indices_greater_than_4[:, 0], indices_greater_than_4[:, 1]]
train_labels_greater_than_4 = train_labels[indices_greater_than_4[:, 0], indices_greater_than_4[:, 1]] - 5  # Adjust labels to start from 0
train_inputs_greater_than_4.shape, train_inputs.shape
# prompt: at first create new labels where labels lower than 5 are equal to 0 and labels equal to 5 are 1 and labels equal to 6 are 2
# then built a KNN from new labels
# and remember to use all these inputs and labels from train

# Create new labels
new_train_labels = torch.where(train_labels < 5, 0, torch.where(train_labels == 5, 1, 2))
# prompt: use standard scaler from sklearn to normalize train_input_reshape

import torch
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler

# Reshape the input data for KNN
train_inputs_reshaped = train_inputs.reshape(train_inputs.shape[0] * train_inputs.shape[1], train_inputs.shape[2])
new_train_labels_reshaped = new_train_labels.reshape(-1)

# Normalize the reshaped input data
#scaler = StandardScaler()
#train_inputs_reshaped_normalized = scaler.fit_transform(train_inputs_reshaped)

# Train a KNN classifier on the normalized new labels
knn = KNeighborsClassifier(n_neighbors=3, weights='distance')  # Adjust n_neighbors as needed
knn.fit(train_inputs_reshaped, new_train_labels_reshaped)
train_inputs_reshaped.shape, train_inputs_reshaped.shape
train_inputs_reshaped = torch.tensor(train_inputs_reshaped, dtype=torch.float)
train_inputs_reshaped = train_inputs_reshaped.unsqueeze(1)
train_inputs_reshaped.shape
import torch
import random

class SevenWayClassifier():
  def __init__(self, ):
    base_clf = torch.nn.Linear(in_features=768, out_features=5, bias=True)
    base_clf.load_state_dict(torch.load("../training_set/base_classifier.pth"))
    self.base_clf = base_clf

  def base_classification(self, input_vector):

    with torch.no_grad():
      logits = self.base_clf(input_vector)
      preds = torch.softmax(logits, 1)
      predicted_class = preds.argmax(dim=1).numpy()[0]

    return predicted_class

  def get_preds(self, input_vector):
    with torch.no_grad():
      logits = self.base_clf(input_vector)
      preds = torch.softmax(logits, 1)
    return preds

  def __call__(self, input_vector):
    new_train_labels_reshaped.shape, train_inputs_reshaped.shape
    which = knn.predict(input_vector)[0]
    if (which):
      return which + 4
    return self.base_classification(input_vector)

clf = SevenWayClassifier()

Inference and Evaluation

from sklearn.metrics import f1_score

def compute_f1(labels, predictions):
  return f1_score(labels, predictions, average='macro')
from tqdm import tqdm

def inference(clf, input_vectors):
  predictions = []
  input_vectors = input_vectors.reshape(input_vectors.shape[0] * input_vectors.shape[1], input_vectors.shape[2])
  input_vectors = torch.tensor(input_vectors, dtype=torch.float)
  input_vectors = input_vectors.unsqueeze(1)
  for sample in tqdm(input_vectors):
    predictions.append(clf(sample))
  return predictions
train_inputs.shape, train_inputs_reshaped.shape
predictions = inference(clf, test_inputs)

f1 = compute_f1(predictions, test_labels)
print('\nNaive solution F1', f1)

Validation Dataset

# The leaderboard may or may not work... If it doesn't forgive us. We will try to get it running.

import pandas as pd
import numpy as np

# 30% of the test data
def submission_to_csv(pred: np.ndarray, output_fpath: str = "submission.csv"):
    pred = np.array(pred).flatten()
    data_size = pred.size
    df = pd.DataFrame({
        "ID": np.arange(data_size),
        "class": pred
    })

    df.to_csv(output_fpath, index=False)

eval_inputs = torch.load('./validation_set/eval_dataset.pt')

eval_predictions = inference(clf, eval_inputs)

submission_to_csv(eval_predictions)

Test Dataset

# DO NOT CHANGE THIS CELL
test_inputs = torch.load('./test_set/test_dataset.pt')

split='test'

test_predictions = inference(clf, test_inputs)

with open('{}_predictions.txt'.format("Team Name"), 'w') as outfile:
  outfile.write('\n'.join([str(p) for p in test_predictions]))