bakhshaliyev's picture
added all the files
556d303 verified
# Modified from MultiRocket (https://github.com/ChangWeiTan/MultiRocket)
# Copyright (C) 2025 Jafar Bakhshaliyev
# Licensed under GNU General Public License v3.0
import argparse
import os
import time
import sys
import socket
import platform
from datetime import datetime
import numba
import numpy as np
import pandas as pd
import psutil
import pytz
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import LabelEncoder
from sktime.utils.data_io import load_from_tsfile_to_dataframe
from multirocket.multirocket_multivariate import MultiRocket
from utils.data_loader import process_ts_data
from utils.tools import create_directory
import augmentation as aug
pd.set_option('display.max_columns', 500)
def run_augmentation(x, y, args):
"""
Apply data augmentation to the input data based on args.
Parameters:
-----------
x : numpy.ndarray
Original time series data
y : numpy.ndarray
Original labels
args : argparse.Namespace
Command line arguments containing augmentation options
Returns:
--------
x_aug : numpy.ndarray
Augmented time series data
y_aug : numpy.ndarray
Augmented labels
augmentation_tags : str
String describing the applied augmentations
"""
print("Augmenting data for dataset %s" % args.problem)
np.random.seed(args.seed)
x_aug = x.copy()
y_aug = y.copy()
augmentation_tags = ""
if args.augmentation_ratio > 0:
augmentation_tags = "%d" % args.augmentation_ratio
print(f"Original training size: {x.shape[0]} samples")
for n in range(args.augmentation_ratio):
x_temp, current_tags = augment(x, y, args)
if x_temp.shape != x.shape:
print(f"Warning: Augmented data shape {x_temp.shape} doesn't match original shape {x.shape}")
continue
x_aug = np.concatenate((x_aug, x_temp), axis=0)
y_aug = np.append(y_aug, y)
print(f"Round {n+1}: {current_tags} done - Added {x_temp.shape[0]} samples")
if n == 0:
augmentation_tags += current_tags
print(f"Augmented training size: {x_aug.shape[0]} samples")
if args.extra_tag:
augmentation_tags += "_" + args.extra_tag
else:
augmentation_tags = "none"
if args.extra_tag:
augmentation_tags = args.extra_tag
return x_aug, y_aug, augmentation_tags
def augment(x, y, args):
"""
Apply specified augmentations to the multivariate time series data.
Parameters:
-----------
x : numpy.ndarray
Original time series data with shape (n_samples, n_dimensions, n_timesteps)
y : numpy.ndarray
Original labels
args : argparse.Namespace
Command line arguments containing augmentation options
Returns:
--------
x : numpy.ndarray
Augmented time series data
augmentation_tags : str
String describing the applied augmentations
"""
augmentation_tags = ""
x_aug = x.copy()
if len(x_aug.shape) != 3:
if len(x_aug.shape) == 2:
x_aug = x_aug.reshape(x_aug.shape[0], 1, x_aug.shape[1])
print(f"Reshaped to {x_aug.shape} for processing")
if args.jitter:
x_aug = aug.jitter(x_aug)
augmentation_tags += "_jitter"
if args.tps and args.patch_len > 0:
x_aug = aug.tps(x_aug, y, args.patch_len, args.stride, args.shuffle_rate)
augmentation_tags += "_tps"
if args.scaling:
x_aug = aug.scaling(x_aug)
augmentation_tags += "_scaling"
if args.rotation:
x_aug = aug.rotation(x_aug)
augmentation_tags += "_rotation"
if args.permutation:
x_aug = aug.permutation(x_aug)
augmentation_tags += "_permutation"
if args.randompermutation:
x_aug = aug.permutation(x_aug, seg_mode="random")
augmentation_tags += "_randomperm"
if args.magwarp:
x_aug = aug.magnitude_warp(x_aug)
augmentation_tags += "_magwarp"
if args.timewarp:
x_aug = aug.time_warp(x_aug)
augmentation_tags += "_timewarp"
if args.windowslice:
x_aug = aug.window_slice(x_aug)
augmentation_tags += "_windowslice"
if args.windowwarp:
x_aug = aug.window_warp(x_aug)
augmentation_tags += "_windowwarp"
if args.spawner:
x_aug = aug.spawner(x_aug, y)
augmentation_tags += "_spawner"
if args.dtwwarp:
x_aug = aug.random_guided_warp(x_aug, y)
augmentation_tags += "_rgw"
if args.shapedtwwarp:
x_aug = aug.random_guided_warp_shape(x_aug, y)
augmentation_tags += "_rgws"
if args.wdba:
x_aug = aug.wdba(x_aug, y)
augmentation_tags += "_wdba"
if args.discdtw:
x_aug = aug.discriminative_guided_warp(x_aug, y)
augmentation_tags += "_dgw"
if args.discsdtw:
x_aug = aug.discriminative_guided_warp_shape(x_aug, y)
augmentation_tags += "_dgws"
if not augmentation_tags:
augmentation_tags = "_none"
return x_aug, augmentation_tags
def run_multirocket_experiment(args):
"""
Run MultiRocket on a dataset with multiple iterations and optional augmentation.
Parameters:
-----------
args : argparse.Namespace
Command line arguments containing options
Returns:
--------
results_df : pandas.DataFrame
DataFrame containing results of the experiment
"""
problem = args.problem
data_path = args.datapath
data_folder = data_path + problem + "/"
# Set output directory
output_path = os.getcwd() + "/output/"
classifier_name = f"MultiRocket_{args.num_features}"
output_dir = "{}/multirocket/resample_{}/{}/{}/".format(
output_path,
args.iter,
classifier_name,
problem
)
if args.save:
create_directory(output_dir)
train_file = data_folder + problem + "_TRAIN.ts"
test_file = data_folder + problem + "_TEST.ts"
# Loading data
X_train, y_train = load_from_tsfile_to_dataframe(train_file)
X_test, y_test = load_from_tsfile_to_dataframe(test_file)
encoder = LabelEncoder()
y_train = encoder.fit_transform(y_train)
y_test = encoder.transform(y_test)
X_train_processed = process_ts_data(X_train, normalise=False)
X_test_processed = process_ts_data(X_test, normalise=False)
# Apply augmentation
augmentation_tags = "none"
if args.use_augmentation:
X_train_processed, y_train, augmentation_tags = run_augmentation(X_train_processed, y_train, args)
accuracies = []
train_times = []
test_times = []
for iteration in range(args.iterations):
print(f"Running iteration {iteration+1}/{args.iterations}")
start_time = time.perf_counter()
np.random.seed(args.seed + iteration)
classifier = MultiRocket(
num_features=args.num_features,
classifier="logistic",
verbose=args.verbose
)
yhat_train = classifier.fit(
X_train_processed, y_train,
predict_on_train=False
)
yhat_test = classifier.predict(X_test_processed)
test_acc = accuracy_score(y_test, yhat_test)
if yhat_train is not None:
train_acc = accuracy_score(y_train, yhat_train)
else:
train_acc = -1
accuracies.append(test_acc)
train_times.append(classifier.train_duration)
test_times.append(classifier.test_duration)
print(f"Iteration {iteration+1} - Test Accuracy: {test_acc:.4f}, Train Time: {classifier.train_duration:.2f} seconds")
mean_accuracy = np.mean(accuracies)
std_accuracy = np.std(accuracies)
mean_train_time = np.mean(train_times)
mean_test_time = np.mean(test_times)
print(f"\nResults for {problem} with augmentation: {augmentation_tags}")
print(f"Train size: {X_train_processed.shape[0]} samples")
print(f"Mean Accuracy: {mean_accuracy:.4f} ± {std_accuracy:.4f}")
print(f"Mean Train Time: {mean_train_time:.2f} seconds")
print(f"Mean Test Time: {mean_test_time:.2f} seconds")
print(f"Individual Accuracies: {accuracies}")
results_df = pd.DataFrame({
'dataset': [problem],
'augmentation': [augmentation_tags],
'train_size': [X_train_processed.shape[0]],
'test_size': [X_test_processed.shape[0]],
'mean_accuracy': [mean_accuracy],
'std_accuracy': [std_accuracy],
'mean_train_time': [mean_train_time],
'mean_test_time': [mean_test_time],
'iterations': [args.iterations],
'features': [args.num_features],
'individual_accuracies': [','.join(map(str, accuracies))],
'patch_len': [args.patch_len] if hasattr(args, 'patch_len') else [0],
'stride': [args.stride] if hasattr(args, 'stride') else [0],
'shuffle_rate': [args.shuffle_rate] if hasattr(args, 'shuffle_rate') else [0.0],
})
if args.save:
results_filename = f"{output_dir}/multirocket_results_{problem}_{augmentation_tags}.csv"
if os.path.exists(results_filename):
existing_df = pd.read_csv(results_filename)
combined_df = pd.concat([existing_df, results_df], ignore_index=True)
combined_df.to_csv(results_filename, index=False)
print(f"Results appended to {results_filename}")
else:
results_df.to_csv(results_filename, index=False)
print(f"Results saved to new file {results_filename}")
return results_df
def run_all_datasets(args):
"""
Run MultiRocket on all available datasets in the data path.
Parameters:
-----------
args : argparse.Namespace
Command line arguments containing options
"""
# list of available datasets
data_path = args.datapath
datasets = [d for d in os.listdir(data_path) if os.path.isdir(os.path.join(data_path, d))]
if not datasets:
print(f"No datasets found in {data_path}")
return
print(f"Found {len(datasets)} datasets: {', '.join(datasets)}")
results = []
for dataset_name in datasets:
print(f"\n{'='*50}")
print(f"Processing dataset: {dataset_name}")
print(f"{'='*50}")
try:
args.problem = dataset_name
result_df = run_multirocket_experiment(args)
results.append(result_df)
except Exception as e:
print(f"Error processing dataset {dataset_name}: {e}")
if results:
all_results_df = pd.concat(results, ignore_index=True)
overall_mean = all_results_df['mean_accuracy'].mean()
print("\n" + "="*80)
print("SUMMARY OF RESULTS")
print("="*80)
print(f"{'Dataset':<25} {'Augmentation':<25} {'Mean Accuracy':<15} {'Std Dev':<10}")
print("-"*80)
for _, row in all_results_df.iterrows():
print(f"{row['dataset']:<25} {row['augmentation']:<25} {row['mean_accuracy']:.4f}{' '*8} {row['std_accuracy']:.4f}")
print("-"*80)
print(f"{'OVERALL':<25} {'':<25} {overall_mean:.4f}")
print("="*80)
aug_tag = "none" if not args.use_augmentation else "aug"
output_path = os.getcwd() + "/output/"
all_results_df.to_csv(f"{output_path}/multirocket_summary_results_{aug_tag}.csv", index=False)
print(f"\nSummary results saved to {output_path}/multirocket_summary_results_{aug_tag}.csv")
def list_available_datasets(args):
"""
List all available datasets in the data path.
Parameters:
-----------
args : argparse.Namespace
Command line arguments containing options
"""
data_path = args.datapath
try:
datasets = [d for d in os.listdir(data_path) if os.path.isdir(os.path.join(data_path, d))]
print("Available datasets:")
for dataset in sorted(datasets):
print(f" - {dataset}")
except Exception as e:
print(f"Error listing datasets: {e}")
return []
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Run MultiRocket on multivariate time series datasets with optional augmentation')
# Dataset selection
parser.add_argument("-d", "--datapath", type=str, required=False, default="/home/bakhshaliyev/classification-aug/MultiRocket/data/Multivariate_ts/") # change to your data path
parser.add_argument("-p", "--problem", type=str, required=False, default="UWaveGestureLibrary")
parser.add_argument("-i", "--iter", type=int, required=False, default=0)
parser.add_argument("-n", "--num_features", type=int, required=False, default=50000)
parser.add_argument("-t", "--num_threads", type=int, required=False, default=-1)
parser.add_argument("-s", "--save", type=bool, required=False, default=True)
parser.add_argument("-v", "--verbose", type=int, required=False, default=2)
# Added arguments for augmentation
parser.add_argument('--iterations', type=int, default=5, help='Number of iterations for each experiment (default: 5)')
parser.add_argument('--seed', type=int, default=42, help='Random seed (default: 42)')
parser.add_argument('--list', action='store_true', help='List available datasets')
parser.add_argument('--all', action='store_true', help='Run on all available datasets')
# Augmentation control
parser.add_argument('--use-augmentation', action='store_true', help='Use data augmentation')
parser.add_argument('--augmentation-ratio', type=int, default=0,
help='Number of augmented copies to add (default: 0)')
parser.add_argument('--extra-tag', type=str, default='',
help='Extra tag to add to augmentation tags')
# Augmentation methods
parser.add_argument('--jitter', action='store_true', help='Apply jitter augmentation')
parser.add_argument('--scaling', action='store_true', help='Apply scaling augmentation')
parser.add_argument('--rotation', action='store_true', help='Apply rotation augmentation')
parser.add_argument('--permutation', action='store_true', help='Apply permutation augmentation')
parser.add_argument('--randompermutation', action='store_true', help='Apply random permutation augmentation')
parser.add_argument('--magwarp', action='store_true', help='Apply magnitude warp augmentation')
parser.add_argument('--timewarp', action='store_true', help='Apply time warp augmentation')
parser.add_argument('--windowslice', action='store_true', help='Apply window slice augmentation')
parser.add_argument('--windowwarp', action='store_true', help='Apply window warp augmentation')
parser.add_argument('--spawner', action='store_true', help='Apply spawner augmentation')
parser.add_argument('--dtwwarp', action='store_true', help='Apply DTW-based warp augmentation')
parser.add_argument('--shapedtwwarp', action='store_true', help='Apply shape DTW warp augmentation')
parser.add_argument('--wdba', action='store_true', help='Apply WDBA augmentation')
parser.add_argument('--discdtw', action='store_true', help='Apply discriminative DTW augmentation')
parser.add_argument('--discsdtw', action='store_true', help='Apply discriminative shape DTW augmentation')
parser.add_argument('--tps', action='store_true', help='Apply TPS augmentation')
# TPS specific parameters
parser.add_argument('--stride', type=int, default=0, help='# of patches stride')
parser.add_argument('--patch_len', type=int, default=0, help='# of patches')
parser.add_argument('--shuffle_rate', type=float, default=0.0, help='shuffle rate')
args = parser.parse_args()
if args.num_threads > 0:
numba.set_num_threads(args.num_threads)
if args.list:
list_available_datasets(args)
sys.exit(0)
# Run on all datasets
if args.all:
print(f"Running MultiRocket on all available datasets")
print(f"Using {args.num_features} features and {args.iterations} iterations")
if args.use_augmentation:
print(f"Using data augmentation with ratio {args.augmentation_ratio}")
run_all_datasets(args)
sys.exit(0)
# Run on specific dataset
print(f"Running MultiRocket on {args.problem} dataset")
print(f"Using {args.num_features} features and {args.iterations} iterations")
if args.use_augmentation:
print(f"Using data augmentation with ratio {args.augmentation_ratio}")
run_multirocket_experiment(args)