--- commits touching code/ ---
4c24610 2019-10-30T11:13:58+11:00 Initial commit
1cb98f4 2019-11-02T12:21:41+11:00 Update demo.ipynb
84d24c0 2019-11-03T12:33:31+11:00 Add scalability
1354333 2019-11-03T12:41:21+11:00 Update reproduce_experiments_scalability.py
735b99a 2019-11-06T00:32:32+11:00 Fro additional 2018 datasets
b7532f0 2019-11-06T00:37:12+11:00 Revert "Fro additional 2018 datasets"
6072604 2019-11-06T00:38:21+11:00 For additional 2018 datasets
3b4eeb0 2019-11-06T00:48:30+11:00 Update reproduce_experiments_additional.py
c94985d 2019-11-06T00:51:32+11:00 Update reproduce_experiments_additional.py
3d3ff7b 2019-11-06T00:53:41+11:00 Update reproduce_experiments_additional.py
41cfd4b 2020-07-14T00:09:37+10:00 Update on publication
e2349e0 2020-07-21T10:08:28+10:00 Misc update
--- diff of bakeoff code 4c24610..531be13 (last commit before the July 2020 rewrite) ---
(end diff stat)
--- diff stat 4c24610..36bd908 (HEAD run by the earlier attempt) for code/ ---
 code/demo.ipynb                           | 338 ------------------------------
 code/reproduce_experiments_bakeoff.py     | 243 ---------------------
 code/reproduce_experiments_scalability.py | 286 +++++++++++++++++++++++++
 code/reproduce_experiments_ucr.py         | 138 ++++++++++++
 code/rocket_functions.py                  | 101 +++++----
 5 files changed, 486 insertions(+), 620 deletions(-)
--- code/rocket_functions.py at 4c24610 ---
# Angus Dempster, Francois Petitjean, Geoff Webb

# Dempster A, Petitjean F, Webb GI (2019) ROCKET: Exceptionally fast and
# accurate time series classification using random convolutional kernels.
# arXiv:1910.13051

from numba import njit, prange
import numpy as np

@njit
def generate_kernels(input_length, num_kernels):

    candidate_lengths = np.array((7, 9, 11))

    # initialise kernel parameters
    weights = np.zeros((num_kernels, candidate_lengths.max())) # see note
    lengths = np.zeros(num_kernels, dtype = np.int32) # see note
    biases = np.zeros(num_kernels)
    dilations = np.zeros(num_kernels, dtype = np.int32)
    paddings = np.zeros(num_kernels, dtype = np.int32)

    # note: only the first *lengths[i]* values of *weights[i]* are used

    for i in range(num_kernels):

        length = np.random.choice(candidate_lengths)
        _weights = np.random.normal(0, 1, length)
        bias = np.random.uniform(-1, 1)
        dilation = 2 ** np.random.uniform(0, np.log2((input_length - 1) // (length - 1)))
        padding = ((length - 1) * dilation) // 2 if np.random.randint(2) == 1 else 0

        weights[i, :length] = _weights - _weights.mean()
        lengths[i], biases[i], dilations[i], paddings[i] = length, bias, dilation, padding

    return weights, lengths, biases, dilations, paddings

@njit(fastmath = True)
def apply_kernel(X, weights, length, bias, dilation, padding):

    # zero padding
    if padding > 0:
        _input_length = len(X)
        _X = np.zeros(_input_length + (2 * padding))
        _X[padding:(padding + _input_length)] = X
        X = _X

    input_length = len(X)

    output_length = input_length - ((length - 1) * dilation)

    _ppv = 0 # "proportion of positive values"
    _max = np.NINF

    for i in range(output_length):

        _sum = bias

        for j in range(length):

            _sum += weights[j] * X[i + (j * dilation)]

        if _sum > 0:
            _ppv += 1

        if _sum > _max:
            _max = _sum

    return _ppv / output_length, _max

@njit(parallel = True, fastmath = True)
def apply_kernels(X, kernels):

    weights, lengths, biases, dilations, paddings = kernels

    num_examples = len(X)
    num_kernels = len(weights)

    # initialise output
    _X = np.zeros((num_examples, num_kernels * 2)) # 2 features per kernel

    for i in prange(num_examples):

        for j in range(num_kernels):

            _X[i, (j * 2):((j * 2) + 2)] = \
            apply_kernel(X[i], weights[j][:lengths[j]], lengths[j], biases[j], dilations[j], paddings[j])

    return _X
--- code/reproduce_experiments_bakeoff.py at 4c24610 ---
# Angus Dempster, Francois Petitjean, Geoff Webb

# Dempster A, Petitjean F, Webb GI (2019) ROCKET: Exceptionally fast and
# accurate time series classification using random convolutional kernels.
# arXiv:1910.13051

import argparse
import numpy as np
import pandas as pd
import time

from sklearn.linear_model import RidgeClassifierCV

from rocket_functions import generate_kernels, apply_kernels

# == notes =====================================================================

# - This script is intended to allow for reproduction of the experiments on the
#   "bake off" datasets, using the txt versions of the "bake off" datasets from
#   timeseriesclassification.com (Univariate2018_arff.zip).
# - The required arguments for this script are:
#   - -i or --input_path, the parent directory for the datasets; and
#   - -o or --output_path, to save "bakeoff_results.csv".
# - Optional arguments allow you to set the number of runs, -n or --num_runs,
#   and the number of kernels, -k or --num_kernels.
# - If input_path is ".../Univariate_arff/", then each dataset should be
#   located at "{input_path}/{dataset_name}/{dataset_name}_TRAIN.txt", etc.
# - Most of the code herein relates to handling input/output, multiple runs,
#   timings, etc.  The key code to use ROCKET is simply:
#       kernels = generate_kernels(X_training.shape[1], 10_000)
#       X_training_transform = apply_kernels(X_training, kernels)
#       classifier.fit(X_training_transform, Y_training)
#       X_test_transform = apply_kernels(X_test)
#       classifier.predict(X_test_transform)

# == parse arguments ===========================================================

parser = argparse.ArgumentParser()

parser.add_argument("-i", "--input_path", required = True)
parser.add_argument("-o", "--output_path", required = True)
parser.add_argument("-n", "--num_runs", type = int, default = 10)
parser.add_argument("-k", "--num_kernels", type = int, default = 10_000)

arguments = parser.parse_args()

# == bake off dataset names ====================================================

dataset_names_bakeoff = \
(
    "Adiac",
    "ArrowHead",
    "Beef",
    "BeetleFly",
    "BirdChicken",
    "Car",
    "CBF",
    "ChlorineConcentration",
    "CinCECGTorso",
    "Coffee",
    "Computers",
    "CricketX",
    "CricketY",
    "CricketZ",
    "DiatomSizeReduction",
    "DistalPhalanxOutlineCorrect",
    "DistalPhalanxOutlineAgeGroup",
    "DistalPhalanxTW",
    "Earthquakes",
    "ECG200",
    "ECG5000",
    "ECGFiveDays",
    "ElectricDevices",
    "FaceAll",
    "FaceFour",
    "FacesUCR",
    "FiftyWords",
    "Fish",
    "FordA",
    "FordB",
    "GunPoint",
    "Ham",
    "HandOutlines",
    "Haptics",
    "Herring",
    "InlineSkate",
    "InsectWingbeatSound",
    "ItalyPowerDemand",
    "LargeKitchenAppliances",
    "Lightning2",
    "Lightning7",
    "Mallat",
    "Meat",
    "MedicalImages",
    "MiddlePhalanxOutlineCorrect",
    "MiddlePhalanxOutlineAgeGroup",
    "MiddlePhalanxTW",
    "MoteStrain",
    "NonInvasiveFetalECGThorax1",
    "NonInvasiveFetalECGThorax2",
    "OliveOil",
    "OSULeaf",
    "PhalangesOutlinesCorrect",
    "Phoneme",
    "Plane",
    "ProximalPhalanxOutlineCorrect",
    "ProximalPhalanxOutlineAgeGroup",
    "ProximalPhalanxTW",
    "RefrigerationDevices",
    "ScreenType",
    "ShapeletSim",
    "ShapesAll",
    "SmallKitchenAppliances",
    "SonyAIBORobotSurface1",
    "SonyAIBORobotSurface2",
    "StarLightCurves",
    "Strawberry",
    "SwedishLeaf",
    "Symbols",
    "SyntheticControl",
    "ToeSegmentation1",
    "ToeSegmentation2",
    "Trace",
    "TwoLeadECG",
    "TwoPatterns",
    "UWaveGestureLibraryX",
    "UWaveGestureLibraryY",
    "UWaveGestureLibraryZ",
    "UWaveGestureLibraryAll",
    "Wafer",
    "Wine",
    "WordSynonyms",
    "Worms",
    "WormsTwoClass",
    "Yoga"
)

# == bake off convenience function =============================================

def run(training_data, test_data, num_runs = 10, num_kernels = 10_000):

    results = np.zeros(num_runs)
    timings = np.zeros([4, num_runs]) # training transform, test transform, training, test

    Y_training, X_training = training_data[:, 0].astype(np.int), training_data[:, 1:]
    Y_test, X_test = test_data[:, 0].astype(np.int), test_data[:, 1:]

    for i in range(num_runs):

        input_length = X_training.shape[1]
        kernels = generate_kernels(input_length, num_kernels)

        # -- transform training ------------------------------------------------

        time_a = time.perf_counter()
        X_training_transform = apply_kernels(X_training, kernels)
        time_b = time.perf_counter()
        timings[0, i] = time_b - time_a

        # -- transform test ----------------------------------------------------

        time_a = time.perf_counter()
        X_test_transform = apply_kernels(X_test, kernels)
        time_b = time.perf_counter()
        timings[1, i] = time_b - time_a

        # -- training ----------------------------------------------------------

        time_a = time.perf_counter()
        classifier = RidgeClassifierCV(alphas = 10 ** np.linspace(-3, 3, 10), normalize = True)
        classifier.fit(X_training_transform, Y_training)
        time_b = time.perf_counter()
        timings[2, i] = time_b - time_a

        # -- test --------------------------------------------------------------

        time_a = time.perf_counter()
        results[i] = classifier.score(X_test_transform, Y_test)
        time_b = time.perf_counter()
        timings[3, i] = time_b - time_a

    return results, timings

# == run through the bake off datasets =========================================

results_bakeoff = pd.DataFrame(index = dataset_names_bakeoff,
                               columns = ["accuracy_mean",
                                          "accuracy_standard_deviation",
                                          "time_training_seconds",
                                          "time_test_seconds"],
                               data = 0)
results_bakeoff.index.name = "dataset"

compiled = False

print(f"RUNNING".center(80, "="))

for dataset_name in dataset_names_bakeoff:

    print(f"{dataset_name}".center(80, "-"))

    # -- read data -------------------------------------------------------------

    print(f"Loading data".ljust(80 - 5, "."), end = "", flush = True)

    training_data = np.loadtxt(f"{arguments.input_path}/{dataset_name}/{dataset_name}_TRAIN.txt")
    test_data = np.loadtxt(f"{arguments.input_path}/{dataset_name}/{dataset_name}_TEST.txt")

    print("Done.")

    # -- precompile ------------------------------------------------------------

    if not compiled:

        print(f"Compiling ROCKET functions (once only)".ljust(80 - 5, "."), end = "", flush = True)

        _ = generate_kernels(100, 10)
        apply_kernels(np.zeros_like(training_data)[:, 1:], _)
        compiled = True

        print("Done.")

    # -- run -------------------------------------------------------------------

    print(f"Performing runs".ljust(80 - 5, "."), end = "", flush = True)

    results, timings = run(training_data, test_data,
                           num_runs = arguments.num_runs,
                           num_kernels = arguments.num_kernels)
    timings_mean = timings.mean(1)

    print("Done.")

    # -- store results ---------------------------------------------------------

    results_bakeoff.loc[dataset_name, "accuracy_mean"] = results.mean()
    results_bakeoff.loc[dataset_name, "accuracy_standard_deviation"] = results.std()
    results_bakeoff.loc[dataset_name, "time_training_seconds"] = timings_mean[[0, 2]].sum()
    results_bakeoff.loc[dataset_name, "time_test_seconds"] = timings_mean[[1, 3]].sum()

print(f"FINISHED".center(80, "="))

results_bakeoff.to_csv(f"{arguments.output_path}/results_bakeoff.csv")
