├── tools ├── __init__.py ├── output_init.py ├── output_tool.py ├── dataset_tool.py ├── accuracy_init.py ├── test_tool.py ├── init_tool.py ├── poolout_tool.py ├── eval_tool.py ├── accuracy_tool.py └── train_tool.py ├── dataset ├── nlp │ ├── __init__.py │ └── JsonFromFiles.py ├── __init__.py └── others │ └── FilenameOnly.py ├── config_parser ├── __init__.py └── parser.py ├── reader ├── __init__.py └── reader.py ├── examples ├── pretrain_models │ └── Readme ├── task2 │ └── data_sample.json └── task1 │ └── case_para_sample.json ├── requirements.txt ├── .gitignore ├── formatter ├── Basic.py ├── nlp │ ├── AttenRNNFormatter.py │ ├── BertPairTextFormatter.py │ ├── bert_feature_tool.py │ └── BertDocParaFormatter.py └── __init__.py ├── model ├── __init__.py ├── optimizer.py ├── nlp │ ├── BertPoint.py │ ├── BertPoolOutMax.py │ └── AttenRNN.py └── loss.py ├── config ├── default.config └── nlp │ ├── BertPoint.config │ ├── BertPoolOutMax.config │ ├── AttenGRU.config │ └── AttenLSTM.config ├── train.py ├── poolout.py ├── test.py └── README.md /tools/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dataset/nlp/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config_parser/__init__.py: -------------------------------------------------------------------------------- 1 | from .parser import create_config 2 | -------------------------------------------------------------------------------- /reader/__init__.py: -------------------------------------------------------------------------------- 1 | from .reader import init_dataset, init_test_dataset 2 | -------------------------------------------------------------------------------- /examples/pretrain_models/Readme: -------------------------------------------------------------------------------- 1 | Please download the bert-base-uncased from https://github.com/google-research/bert . -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | torchvision==0.2.1 2 | torch==1.0.0 3 | tqdm==4.31.1 4 | pytorch_pretrained_bert==0.6.2 5 | numpy==1.16.2 6 | tensorboardX==1.8 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *swp 3 | *swo 4 | .idea 5 | __pycache__ 6 | *un~ 7 | **/.DS_Store 8 | 9 | config/default_local.config 10 | 11 | temp 12 | notebook 13 | result 14 | output/ 15 | 16 | -------------------------------------------------------------------------------- /dataset/__init__.py: -------------------------------------------------------------------------------- 1 | from .nlp.JsonFromFiles import JsonFromFilesDataset 2 | from .others.FilenameOnly import FilenameOnlyDataset 3 | 4 | dataset_list = { 5 | "JsonFromFiles": JsonFromFilesDataset, 6 | "FilenameOnly": FilenameOnlyDataset 7 | } 8 | -------------------------------------------------------------------------------- /formatter/Basic.py: -------------------------------------------------------------------------------- 1 | class BasicFormatter: 2 | def __init__(self, config, mode, *args, **params): 3 | self.config = config 4 | self.mode = mode 5 | 6 | def process(self, data, config, mode, *args, **params): 7 | return data 8 | 9 | -------------------------------------------------------------------------------- /model/__init__.py: -------------------------------------------------------------------------------- 1 | from .nlp.BertPoint import BertPoint 2 | from .nlp.BertPoolOutMax import BertPoolOutMax 3 | from .nlp.AttenRNN import AttentionRNN 4 | 5 | model_list = { 6 | "BertPoint": BertPoint, 7 | "BertPoolOutMax": BertPoolOutMax, 8 | "AttenRNN": AttentionRNN 9 | } 10 | 11 | 12 | def get_model(model_name): 13 | if model_name in model_list.keys(): 14 | return model_list[model_name] 15 | else: 16 | raise NotImplementedError 17 | -------------------------------------------------------------------------------- /tools/output_init.py: -------------------------------------------------------------------------------- 1 | from .output_tool import basic_output_function, null_output_function 2 | 3 | output_function_dic = { 4 | "Basic": basic_output_function, 5 | "Null": null_output_function 6 | } 7 | 8 | 9 | def init_output_function(config, *args, **params): 10 | name = config.get("output", "output_function") 11 | 12 | if name in output_function_dic: 13 | return output_function_dic[name] 14 | else: 15 | raise NotImplementedError 16 | -------------------------------------------------------------------------------- /tools/output_tool.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from .accuracy_tool import gen_micro_macro_result 4 | 5 | 6 | def null_output_function(data, config, *args, **params): 7 | return "" 8 | 9 | 10 | def basic_output_function(data, config, *args, **params): 11 | which = config.get("output", "output_value").replace(" ", "").split(",") 12 | temp = gen_micro_macro_result(data) 13 | result = {} 14 | for name in which: 15 | result[name] = temp[name] 16 | 17 | return json.dumps(result, sort_keys=True) 18 | -------------------------------------------------------------------------------- /tools/dataset_tool.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def dfs_search(path, recursive): 5 | if os.path.isfile(path): 6 | return [path] 7 | file_list = [] 8 | name_list = os.listdir(path) 9 | name_list.sort() 10 | for filename in name_list: 11 | real_path = os.path.join(path, filename) 12 | 13 | if os.path.isdir(real_path): 14 | if recursive: 15 | file_list = file_list + dfs_search(real_path, recursive) 16 | else: 17 | file_list.append(real_path) 18 | 19 | return file_list 20 | -------------------------------------------------------------------------------- /tools/accuracy_init.py: -------------------------------------------------------------------------------- 1 | from .accuracy_tool import single_label_top1_accuracy, single_label_top2_accuracy, multi_label_accuracy, \ 2 | null_accuracy_function 3 | 4 | accuracy_function_dic = { 5 | "SingleLabelTop1": single_label_top1_accuracy, 6 | "MultiLabel": multi_label_accuracy, 7 | "Null": null_accuracy_function 8 | } 9 | 10 | 11 | def init_accuracy_function(config, *args, **params): 12 | name = config.get("output", "accuracy_method") 13 | if name in accuracy_function_dic: 14 | return accuracy_function_dic[name] 15 | else: 16 | raise NotImplementedError 17 | -------------------------------------------------------------------------------- /dataset/others/FilenameOnly.py: -------------------------------------------------------------------------------- 1 | import os 2 | from torch.utils.data import Dataset 3 | 4 | from tools.dataset_tool import dfs_search 5 | 6 | 7 | class FilenameOnlyDataset(Dataset): 8 | def __init__(self, config, mode, *args, **params): 9 | self.config = config 10 | self.mode = mode 11 | self.file_list = [] 12 | self.data_path = config.get("data", "%s_data_path" % mode) 13 | 14 | filename_list = config.get("data", "%s_file_list" % mode).replace(" ", "").split(",") 15 | recursive = config.getboolean("data", "recursive") 16 | 17 | for name in filename_list: 18 | self.file_list = self.file_list + dfs_search(os.path.join(self.data_path, name), recursive) 19 | self.file_list.sort() 20 | 21 | def __getitem__(self, item): 22 | return self.file_list[item] 23 | 24 | def __len__(self): 25 | return len(self.file_list) 26 | -------------------------------------------------------------------------------- /model/optimizer.py: -------------------------------------------------------------------------------- 1 | import torch.optim as optim 2 | from pytorch_pretrained_bert import BertAdam 3 | 4 | 5 | def init_optimizer(model, config, *args, **params): 6 | optimizer_type = config.get("train", "optimizer") 7 | learning_rate = config.getfloat("train", "learning_rate") 8 | if optimizer_type == "adam": 9 | optimizer = optim.Adam(model.parameters(), lr=learning_rate, 10 | weight_decay=config.getfloat("train", "weight_decay")) 11 | elif optimizer_type == "sgd": 12 | optimizer = optim.SGD(model.parameters(), lr=learning_rate, 13 | weight_decay=config.getfloat("train", "weight_decay")) 14 | elif optimizer_type == "bert_adam": 15 | optimizer = BertAdam(model.parameters(), lr=learning_rate, 16 | weight_decay=config.getfloat("train", "weight_decay")) 17 | else: 18 | raise NotImplementedError 19 | 20 | return optimizer 21 | -------------------------------------------------------------------------------- /config/default.config: -------------------------------------------------------------------------------- 1 | [train] #train parameters 2 | epoch = 16 3 | batch_size = 128 4 | 5 | shuffle = True 6 | 7 | reader_num = 8 8 | 9 | optimizer = adam 10 | learning_rate = 1e-3 11 | weight_decay = 0 12 | step_size = 1 13 | lr_multiplier = 1 14 | 15 | [eval] #eval parameters 16 | batch_size = 128 17 | 18 | shuffle = False 19 | 20 | reader_num = 4 21 | 22 | [data] #data parameters 23 | train_dataset_type = FilenameOnly 24 | train_formatter_type = Basic 25 | train_data_path = data 26 | train_file_list = train.json 27 | 28 | valid_dataset_type = FilenameOnly 29 | valid_formatter_type = Basic 30 | valid_data_path = data 31 | valid_file_list = valid.json 32 | 33 | test_dataset_type = FilenameOnly 34 | test_formatter_type = Basic 35 | test_data_path = data 36 | test_file_list = test.json 37 | 38 | load_into_mem = True 39 | 40 | [model] #model parameters 41 | model_name = BasicBert 42 | 43 | [output] #output parameters 44 | output_time = 1 45 | test_time = 1 46 | save_as_dict=False 47 | model_path = model 48 | model_name = name 49 | 50 | tensorboard_path = tensorboard 51 | 52 | accuracy_method = SingleLabelTop1 53 | output_function = Basic 54 | output_value = micro_precision,macro_precision,macro_recall,macro_f1 55 | -------------------------------------------------------------------------------- /formatter/nlp/AttenRNNFormatter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'yshao' 3 | 4 | import json 5 | import torch 6 | import os 7 | 8 | from formatter.Basic import BasicFormatter 9 | 10 | 11 | class AttenRNNFormatter(BasicFormatter): 12 | def __init__(self, config, mode, *agrs, **params): 13 | super().__init__(config, mode, *agrs, **params) 14 | self.max_para_q = config.getint('model', 'max_para_q') 15 | self.mode = mode 16 | 17 | def process(self, data, config, mode, *args, **params): 18 | inputs = [] 19 | guids = [] 20 | if mode != 'test': 21 | labels = [] 22 | 23 | for temp in data: 24 | guid = temp['guid'] 25 | emb_mtx = temp['res'] 26 | assert (len(emb_mtx) == self.max_para_q) 27 | inputs.append(emb_mtx) 28 | guids.append(guid) 29 | 30 | if mode != 'test': 31 | labels.append(temp['label']) 32 | 33 | inputs = torch.tensor(inputs) 34 | 35 | if mode != 'test': 36 | labels = torch.LongTensor(labels) 37 | return {'guid': guids, 'input': inputs, 'label': labels} 38 | else: 39 | return {'guid': guids, 'input': inputs} 40 | 41 | -------------------------------------------------------------------------------- /formatter/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from .Basic import BasicFormatter 4 | from .nlp.BertPairTextFormatter import BertPairTextFormatter 5 | from .nlp.BertDocParaFormatter import BertDocParaFormatter 6 | from .nlp.AttenRNNFormatter import AttenRNNFormatter 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | formatter_list = { 11 | "Basic": BasicFormatter, 12 | "BertPairText": BertPairTextFormatter, 13 | "BertDocPara": BertDocParaFormatter, 14 | 'AttenRNN': AttenRNNFormatter 15 | } 16 | 17 | 18 | def init_formatter(config, mode, *args, **params): 19 | temp_mode = mode 20 | if mode != "train": 21 | try: 22 | config.get("data", "%s_formatter_type" % temp_mode) 23 | except Exception as e: 24 | logger.warning( 25 | "[reader] %s_formatter_type has not been defined in config file, use [dataset] train_formatter_type instead." % temp_mode) 26 | temp_mode = "train" 27 | which = config.get("data", "%s_formatter_type" % temp_mode) 28 | 29 | if which in formatter_list: 30 | formatter = formatter_list[which](config, mode, *args, **params) 31 | 32 | return formatter 33 | else: 34 | logger.error("There is no formatter called %s, check your config." % which) 35 | raise NotImplementedError 36 | -------------------------------------------------------------------------------- /config/nlp/BertPoint.config: -------------------------------------------------------------------------------- 1 | [train] #train parameters 2 | epoch = 3 3 | batch_size = 16 4 | 5 | reader_num = 4 6 | 7 | optimizer = bert_adam 8 | learning_rate = 1e-5 9 | weight_decay = 0 10 | step_size = 1 11 | lr_multiplier = 1 12 | 13 | [eval] #eval parameters 14 | batch_size = 16 15 | 16 | reader_num = 2 17 | 18 | [data] #data parameters 19 | train_dataset_type = JsonFromFiles 20 | train_formatter_type = BertPairText 21 | train_data_path = ../../examples/task2 22 | train_file_list = data_sample.json 23 | 24 | valid_dataset_type = JsonFromFiles 25 | valid_formatter_type = BertPairText 26 | valid_data_path = ../../examples/task2 27 | valid_file_list = data_sample.json 28 | 29 | test_dataset_type = JsonFromFiles 30 | test_formatter_type = BertPairText 31 | test_data_path = ../../examples/task2 32 | test_file_list = data_sample.json 33 | 34 | recursive = False 35 | json_format = line 36 | 37 | max_seq_length = 512 38 | 39 | [model] #model parameters 40 | model_name = BertPoint 41 | 42 | bert_path = ../../sample/pretrain_models/bert-base-uncased 43 | 44 | output_dim = 2 45 | output_mode = classification 46 | 47 | [output] #output parameters 48 | output_time = 1 49 | test_time = 1 50 | 51 | model_path = output/model 52 | model_name = task2 53 | 54 | pool_out = True 55 | save_as_dict = True 56 | 57 | tensorboard_path = output/tensorboard 58 | 59 | accuracy_method = SingleLabelTop1 60 | output_function = Basic 61 | output_value = micro_precision,macro_precision,macro_recall,macro_f1 62 | 63 | tqdm_ncols = 150 64 | -------------------------------------------------------------------------------- /config/nlp/BertPoolOutMax.config: -------------------------------------------------------------------------------- 1 | [train] #train parameters 2 | epoch = 1 3 | batch_size = 1 4 | 5 | reader_num = 1 6 | 7 | optimizer = bert_adam 8 | learning_rate = 1e-5 9 | weight_decay = 0 10 | step_size = 1 11 | lr_multiplier = 1 12 | 13 | [eval] #eval parameters 14 | batch_size = 1 15 | 16 | reader_num = 1 17 | 18 | [data] #data parameters 19 | train_dataset_type = JsonFromFiles 20 | train_formatter_type = BertDocPara 21 | train_data_path = ../../examples/task1 22 | train_file_list = case_para_sample.json 23 | 24 | valid_dataset_type = JsonFromFiles 25 | valid_formatter_type = BertDocPara 26 | valid_data_path = ../../examples/task1 27 | valid_file_list = case_para_sample.json 28 | 29 | test_dataset_type = JsonFromFiles 30 | test_formatter_type = BertDocPara 31 | test_data_path = ../../examples/task1 32 | test_file_list = case_para_sample.json 33 | 34 | recursive = False 35 | json_format = line 36 | 37 | max_seq_length = 512 38 | 39 | [model] #model parameters 40 | model_name = BertPoolOutMax 41 | max_para_c = 40 42 | max_para_q = 54 43 | step=3 44 | 45 | bert_path = ../../sample/pretrain_models/bert-base-uncased 46 | 47 | output_dim = 2 48 | output_mode = classification 49 | 50 | [output] #output parameters 51 | output_time = 1 52 | test_time = 1 53 | save_step = 100 54 | 55 | model_path = output/model 56 | model_name = pool_out_max 57 | 58 | tensorboard_path = output/tensorboard 59 | 60 | accuracy_method = SingleLabelTop1 61 | output_function = Basic 62 | output_value = micro_precision,macro_precision,macro_recall,macro_f1 63 | 64 | tqdm_ncols = 150 65 | -------------------------------------------------------------------------------- /config/nlp/AttenGRU.config: -------------------------------------------------------------------------------- 1 | [train] #train parameters 2 | epoch = 60 3 | batch_size = 64 4 | 5 | reader_num = 4 6 | 7 | optimizer = adam 8 | learning_rate = 1e-4 9 | weight_decay = 1e-6 10 | step_size = 1 11 | lr_multiplier = 1 12 | 13 | [eval] #eval parameters 14 | batch_size = 32 15 | 16 | reader_num = 2 17 | 18 | [data] #data parameters 19 | train_dataset_type = JsonFromFiles 20 | train_formatter_type = AttenRNN 21 | train_data_path = ../../examples/task1 22 | train_file_list = embedding_sample.json 23 | 24 | valid_dataset_type = JsonFromFiles 25 | valid_formatter_type = AttenRNN 26 | valid_data_path = ../../examples/task1 27 | valid_file_list = embedding_sample.json 28 | 29 | test_dataset_type = JsonFromFiles 30 | test_formatter_type = AttenRNN 31 | test_data_path = ../../examples/task1 32 | test_file_list = embedding_sample.json 33 | 34 | load_into_mem = True 35 | 36 | recursive = False 37 | json_format = line 38 | 39 | max_seq_length = 512 40 | 41 | [model] #model parameters 42 | model_name = AttenRNN 43 | max_para_q = 54 44 | 45 | rnn = gru 46 | hidden_dim = 256 47 | output_dim = 2 48 | bidirectional = False 49 | num_layers = 1 50 | dropout_rnn = 0 51 | dropout_fc = 0 52 | output_mode = classification 53 | label_weight = 1.0 54 | 55 | [output] #output parameters 56 | output_time = 1 57 | test_time = 1 58 | save_step = -1 59 | save_as_dict = False 60 | model_path = output/model 61 | model_name = attengru 62 | 63 | tensorboard_path = output/tensorboard 64 | 65 | accuracy_method = SingleLabelTop1 66 | output_function = Basic 67 | output_value = micro_precision,macro_precision,macro_recall,macro_f1 68 | 69 | tqdm_ncols = 150 70 | -------------------------------------------------------------------------------- /config/nlp/AttenLSTM.config: -------------------------------------------------------------------------------- 1 | [train] #train parameters 2 | epoch = 60 3 | batch_size = 64 4 | 5 | reader_num = 4 6 | 7 | optimizer = adam 8 | learning_rate = 1e-4 9 | weight_decay = 1e-6 10 | step_size = 1 11 | lr_multiplier = 1 12 | 13 | [eval] #eval parameters 14 | batch_size = 32 15 | 16 | reader_num = 2 17 | 18 | [data] #data parameters 19 | train_dataset_type = JsonFromFiles 20 | train_formatter_type = AttenRNN 21 | train_data_path = ../../examples/task1 22 | train_file_list = embedding_sample.json 23 | 24 | valid_dataset_type = JsonFromFiles 25 | valid_formatter_type = AttenRNN 26 | valid_data_path = ../../examples/task1 27 | valid_file_list = embedding_sample.json 28 | 29 | test_dataset_type = JsonFromFiles 30 | test_formatter_type = AttenRNN 31 | test_data_path = ../../examples/task1 32 | test_file_list = embedding_sample.json 33 | 34 | load_into_mem = True 35 | 36 | recursive = False 37 | json_format = line 38 | 39 | max_seq_length = 512 40 | 41 | [model] #model parameters 42 | model_name = AttenRNN 43 | max_para_q = 54 44 | 45 | rnn = lstm 46 | hidden_dim = 256 47 | output_dim = 2 48 | bidirectional = False 49 | num_layers = 1 50 | dropout_rnn = 0 51 | dropout_fc = 0 52 | output_mode = classification 53 | label_weight = 1.0 54 | 55 | [output] #output parameters 56 | output_time = 1 57 | test_time = 1 58 | save_step = -1 59 | save_as_dict = False 60 | model_path = output/model 61 | model_name = attenlstm 62 | 63 | tensorboard_path = output/tensorboard 64 | 65 | accuracy_method = SingleLabelTop1 66 | output_function = Basic 67 | output_value = micro_precision,macro_precision,macro_recall,macro_f1 68 | 69 | tqdm_ncols = 150 70 | -------------------------------------------------------------------------------- /train.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import torch 4 | import logging 5 | 6 | from tools.init_tool import init_all 7 | from config_parser import create_config 8 | from tools.train_tool import train 9 | 10 | logging.basicConfig(format='%(asctime)s - %(levelname)s - %(name)s - %(message)s', 11 | datefmt='%m/%d/%Y %H:%M:%S', 12 | level=logging.INFO) 13 | 14 | logger = logging.getLogger(__name__) 15 | 16 | if __name__ == "__main__": 17 | parser = argparse.ArgumentParser() 18 | parser.add_argument('--config', '-c', help="specific config file", required=True) 19 | parser.add_argument('--gpu', '-g', help="gpu id list") 20 | parser.add_argument('--checkpoint', help="checkpoint file path") 21 | args = parser.parse_args() 22 | 23 | configFilePath = args.config 24 | 25 | use_gpu = True 26 | gpu_list = [] 27 | if args.gpu is None: 28 | use_gpu = False 29 | else: 30 | use_gpu = True 31 | os.environ["CUDA_VISIBLE_DEVICES"] = args.gpu 32 | 33 | device_list = args.gpu.split(",") 34 | for a in range(0, len(device_list)): 35 | gpu_list.append(int(a)) 36 | 37 | os.system("clear") 38 | 39 | config = create_config(configFilePath) 40 | 41 | cuda = torch.cuda.is_available() 42 | logger.info("CUDA available: %s" % str(cuda)) 43 | if not cuda and len(gpu_list) > 0: 44 | logger.error("CUDA is not available but specific gpu id") 45 | raise NotImplementedError 46 | 47 | parameters = init_all(config, gpu_list, args.checkpoint, "train") 48 | 49 | train(parameters, config, gpu_list) 50 | -------------------------------------------------------------------------------- /config_parser/parser.py: -------------------------------------------------------------------------------- 1 | import configparser 2 | import os 3 | import functools 4 | 5 | 6 | class ConfigParser: 7 | def __init__(self, *args, **params): 8 | self.default_config = configparser.RawConfigParser(*args, **params) 9 | self.local_config = configparser.RawConfigParser(*args, **params) 10 | self.config = configparser.RawConfigParser(*args, **params) 11 | 12 | def read(self, filenames, encoding=None): 13 | if os.path.exists("config/default_local.config"): 14 | self.local_config.read("config/default_local.config", encoding=encoding) 15 | else: 16 | self.local_config.read("config/default.config", encoding=encoding) 17 | 18 | self.default_config.read("config/default.config", encoding=encoding) 19 | self.config.read(filenames, encoding=encoding) 20 | 21 | 22 | def _build_func(func_name): 23 | @functools.wraps(getattr(configparser.RawConfigParser, func_name)) 24 | def func(self, *args, **kwargs): 25 | try: 26 | return getattr(self.config, func_name)(*args, **kwargs) 27 | except Exception as e: 28 | try: 29 | return getattr(self.local_config, func_name)(*args, **kwargs) 30 | except Exception as e: 31 | return getattr(self.default_config, func_name)(*args, **kwargs) 32 | 33 | return func 34 | 35 | 36 | def create_config(path): 37 | for func_name in dir(configparser.RawConfigParser): 38 | if not func_name.startswith('_') and func_name != "read": 39 | setattr(ConfigParser, func_name, _build_func(func_name)) 40 | 41 | config = ConfigParser() 42 | config.read(path) 43 | 44 | return config 45 | -------------------------------------------------------------------------------- /tools/test_tool.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import torch 4 | from torch.autograd import Variable 5 | from timeit import default_timer as timer 6 | 7 | from tools.eval_tool import gen_time_str, output_value 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | def test(parameters, config, gpu_list): 13 | model = parameters["model"] 14 | dataset = parameters["test_dataset"] 15 | model.eval() 16 | 17 | acc_result = None 18 | total_loss = 0 19 | cnt = 0 20 | total_len = len(dataset) 21 | start_time = timer() 22 | output_info = "testing" 23 | 24 | output_time = config.getint("output", "output_time") 25 | step = -1 26 | result = [] 27 | 28 | for step, data in enumerate(dataset): 29 | for key in data.keys(): 30 | if isinstance(data[key], torch.Tensor): 31 | if len(gpu_list) > 0: 32 | data[key] = Variable(data[key].cuda()) 33 | else: 34 | data[key] = Variable(data[key]) 35 | 36 | results = model(data, config, gpu_list, acc_result, "test") 37 | result = result + results["output"] 38 | cnt += 1 39 | 40 | if step % output_time == 0: 41 | delta_t = timer() - start_time 42 | 43 | output_value(0, "test", "%d/%d" % (step + 1, total_len), "%s/%s" % ( 44 | gen_time_str(delta_t), gen_time_str(delta_t * (total_len - step - 1) / (step + 1))), 45 | "%.3lf" % (total_loss / (step + 1)), output_info, '\r', config) 46 | 47 | if step == -1: 48 | logger.error("There is no data given to the model in this epoch, check your data.") 49 | raise NotImplementedError 50 | 51 | delta_t = timer() - start_time 52 | output_info = "testing" 53 | output_value(0, "test", "%d/%d" % (step + 1, total_len), "%s/%s" % ( 54 | gen_time_str(delta_t), gen_time_str(delta_t * (total_len - step - 1) / (step + 1))), 55 | "%.3lf" % (total_loss / (step + 1)), output_info, None, config) 56 | 57 | return result 58 | -------------------------------------------------------------------------------- /poolout.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'yshao' 3 | 4 | import argparse 5 | import os 6 | import torch 7 | import json 8 | import logging 9 | 10 | from tools.init_tool import init_all 11 | from tools.poolout_tool import pool_out 12 | from config_parser import create_config 13 | 14 | logging.basicConfig(format='%(asctime)s - %(levelname)s - %(name)s - %(message)s', 15 | datefmt='%m/%d/%Y %H:%M:%S', 16 | level=logging.INFO) 17 | 18 | logger = logging.getLogger(__name__) 19 | 20 | if __name__ == "__main__": 21 | parser = argparse.ArgumentParser() 22 | parser.add_argument('--config', '-c', help="specific config file", required=True) 23 | parser.add_argument('--gpu', '-g', help="gpu id list") 24 | parser.add_argument('--checkpoint', help="checkpoint file path") 25 | parser.add_argument('--result', help="result file path", required=True) 26 | args = parser.parse_args() 27 | 28 | 29 | configFilePath = args.config 30 | 31 | use_gpu = True 32 | gpu_list = [] 33 | if args.gpu is None: 34 | use_gpu = False 35 | else: 36 | use_gpu = True 37 | os.environ["CUDA_VISIBLE_DEVICES"] = args.gpu 38 | 39 | device_list = args.gpu.split(",") 40 | for a in range(0, len(device_list)): 41 | gpu_list.append(int(a)) 42 | 43 | os.system("clear") 44 | 45 | config = create_config(configFilePath) 46 | 47 | cuda = torch.cuda.is_available() 48 | logger.info("CUDA available: %s" % str(cuda)) 49 | if not cuda and len(gpu_list) > 0: 50 | logger.error("CUDA is not available but specific gpu id") 51 | raise NotImplementedError 52 | 53 | parameters = init_all(config, gpu_list, args.checkpoint, "poolout") 54 | 55 | out_file = open(args.result, 'w', encoding='utf-8') 56 | outputs = pool_out(parameters, config, gpu_list, args.result) 57 | for output in outputs: 58 | tmp_dict = { 59 | 'id_': output[0], 60 | 'res': output[1] 61 | } 62 | out_line = json.dumps(tmp_dict, ensure_ascii=False) + '\n' 63 | out_file.write(out_line) 64 | out_file.close() 65 | 66 | # train(parameters, config, gpu_list) 67 | -------------------------------------------------------------------------------- /formatter/nlp/BertPairTextFormatter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'yshao' 3 | 4 | import json 5 | import torch 6 | import os 7 | 8 | from pytorch_pretrained_bert.tokenization import BertTokenizer 9 | 10 | from formatter.Basic import BasicFormatter 11 | from .bert_feature_tool import example_item_to_feature 12 | 13 | 14 | class BertPairTextFormatter(BasicFormatter): 15 | def __init__(self, config, mode, *args, **params): 16 | super().__init__(config, mode, *args, **params) 17 | self.tokenizer = BertTokenizer.from_pretrained(config.get("model", "bert_path")) 18 | self.max_len = config.getint("data", "max_seq_length") 19 | self.mode = mode 20 | self.output_mode = config.get('model', 'output_mode') 21 | 22 | def process(self, data, config, mode, *args, **params): 23 | guids = [] 24 | input_ids = [] 25 | attention_mask = [] 26 | token_type_ids = [] 27 | if mode != 'test': 28 | labels = [] 29 | 30 | for temp in data: 31 | res_dict = example_item_to_feature(temp, self.max_len, self.tokenizer, self.output_mode, 32 | cls_token_at_end=False, pad_on_left=False, 33 | cls_token_segment_id=0, pad_token_segment_id=0) 34 | input_ids.append(res_dict['input_ids']) 35 | attention_mask.append(res_dict['input_mask']) 36 | token_type_ids.append(res_dict['segment_ids']) 37 | guids.append(temp['guid']) 38 | 39 | if mode != 'test': 40 | labels.append(res_dict['label_id']) 41 | 42 | input_ids = torch.LongTensor(input_ids) 43 | attention_mask = torch.LongTensor(attention_mask) 44 | token_type_ids = torch.LongTensor(token_type_ids) 45 | if mode != 'test': 46 | labels = torch.LongTensor(labels) 47 | 48 | if mode != 'test': 49 | return {'guid': guids, 'input_ids': input_ids, 'attention_mask': attention_mask, 'token_type_ids': token_type_ids, 50 | 'label': labels} 51 | else: 52 | return {'guid': guids, 'input_ids': input_ids, 'attention_mask': attention_mask, 'token_type_ids': token_type_ids} 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import torch 4 | import logging 5 | import json 6 | 7 | from tools.init_tool import init_all 8 | from config_parser import create_config 9 | from tools.test_tool import test 10 | 11 | logging.basicConfig(format='%(asctime)s - %(levelname)s - %(name)s - %(message)s', 12 | datefmt='%m/%d/%Y %H:%M:%S', 13 | level=logging.INFO) 14 | 15 | logger = logging.getLogger(__name__) 16 | 17 | if __name__ == "__main__": 18 | parser = argparse.ArgumentParser() 19 | parser.add_argument('--config', '-c', help="specific config file", required=True) 20 | parser.add_argument('--gpu', '-g', help="gpu id list") 21 | parser.add_argument('--checkpoint', help="checkpoint file path", required=True) 22 | parser.add_argument('--result', help="result file path", required=True) 23 | args = parser.parse_args() 24 | 25 | configFilePath = args.config 26 | 27 | use_gpu = True 28 | gpu_list = [] 29 | if args.gpu is None: 30 | use_gpu = False 31 | else: 32 | use_gpu = True 33 | os.environ["CUDA_VISIBLE_DEVICES"] = args.gpu 34 | 35 | device_list = args.gpu.split(",") 36 | for a in range(0, len(device_list)): 37 | gpu_list.append(int(a)) 38 | 39 | os.system("clear") 40 | 41 | config = create_config(configFilePath) 42 | 43 | cuda = torch.cuda.is_available() 44 | logger.info("CUDA available: %s" % str(cuda)) 45 | if not cuda and len(gpu_list) > 0: 46 | logger.error("CUDA is not available but specific gpu id") 47 | raise NotImplementedError 48 | 49 | parameters = init_all(config, gpu_list, args.checkpoint, "test") 50 | 51 | if config.getboolean('output', 'save_as_dict'): 52 | out_file = open(args.result, 'w', encoding='utf-8') 53 | outputs = test(parameters, config, gpu_list) 54 | for output in outputs: 55 | tmp_dict = { 56 | 'id_': output[0], 57 | 'res': output[1] 58 | } 59 | out_line = json.dumps(tmp_dict, ensure_ascii=False) + '\n' 60 | out_file.write(out_line) 61 | else: 62 | json.dump(test(parameters, config, gpu_list), open(args.result, "w", encoding="utf8"), ensure_ascii=False, 63 | sort_keys=True, indent=2) 64 | -------------------------------------------------------------------------------- /examples/task2/data_sample.json: -------------------------------------------------------------------------------- 1 | {"guid": "001_017", "text_a": "Content of the decision paragraph of the 1st query case. ", "text_b": "Text of the 17th paragraph.", "label": 0} 2 | {"guid": "001_022", "text_a": "Content of the decision paragraph of the 1st query case. ", "text_b": "Text of the 22nd paragraph.", "label": 0} 3 | {"guid": "001_001", "text_a": "Content of the decision paragraph of the 1st query case. ", "text_b": "Text of the 1st paragraph.", "label": 1} 4 | {"guid": "001_004", "text_a": "Content of the decision paragraph of the 1st query case. ", "text_b": "Text of the 4th paragraph.", "label": 0} 5 | {"guid": "001_025", "text_a": "Content of the decision paragraph of the 1st query case. ", "text_b": "Text of the 25th paragraph.", "label": 0} 6 | {"guid": "001_005", "text_a": "Content of the decision paragraph of the 1st query case. ", "text_b": "Text of the 5th paragraph.", "label": 0} 7 | {"guid": "001_016", "text_a": "Content of the decision paragraph of the 1st query case. ", "text_b": "Text of the 16th paragraph.", "label": 0} 8 | {"guid": "002_002", "text_a": "Content of the decision paragraph of the 2nd query case. ", "text_b": "Text of the 2nd paragraph.", "label": 0} 9 | {"guid": "002_017", "text_a": "Content of the decision paragraph of the 2nd query case. ", "text_b": "Text of the 17th paragraph.", "label": 0} 10 | {"guid": "002_018", "text_a": "Content of the decision paragraph of the 2nd query case. ", "text_b": "Text of the 18th paragraph.", "label": 1} 11 | {"guid": "002_001", "text_a": "Content of the decision paragraph of the 2nd query case. ", "text_b": "Text of the 1st paragraph.", "label": 0} 12 | {"guid": "002_006", "text_a": "Content of the decision paragraph of the 2nd query case. ", "text_b": "Text of the 6th paragraph.", "label": 0} 13 | {"guid": "002_009", "text_a": "Content of the decision paragraph of the 2nd query case. ", "text_b": "Text of the 9th paragraph.", "label": 0} 14 | {"guid": "003_001", "text_a": "Content of the decision paragraph of the 3rd query case. ", "text_b": "Text of the 1st paragraph.", "label": 0} 15 | {"guid": "003_005", "text_a": "Content of the decision paragraph of the 3rd query case. ", "text_b": "Text of the 5th paragraph.", "label": 0} 16 | {"guid": "003_004", "text_a": "Content of the decision paragraph of the 3rd query case. ", "text_b": "Text of the 4th paragraph.", "label": 0} 17 | -------------------------------------------------------------------------------- /model/nlp/BertPoint.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'yshao' 3 | 4 | import torch 5 | import torch.nn as nn 6 | import torch.nn.functional as F 7 | from pytorch_pretrained_bert import BertModel 8 | 9 | from tools.accuracy_init import init_accuracy_function 10 | 11 | 12 | class BertPoint(nn.Module): 13 | def __init__(self, config, gpu_list, *args, **params): 14 | super(BertPoint, self).__init__() 15 | 16 | self.output_dim = config.getint("model", "output_dim") 17 | self.output_mode = config.get('model', 'output_mode') 18 | 19 | self.bert = BertModel.from_pretrained(config.get("model", "bert_path")) 20 | self.fc = nn.Linear(768, self.output_dim) 21 | if self.output_mode == 'classification': 22 | self.criterion = nn.CrossEntropyLoss() 23 | else: 24 | self.criterion = nn.MSELoss() 25 | self.accuracy_function = init_accuracy_function(config, *args, **params) 26 | 27 | def init_multi_gpu(self, device, config, *args, **params): 28 | self.bert = nn.DataParallel(self.bert, device_ids=device) 29 | 30 | def forward(self, data, config, gpu_list, acc_result, mode): 31 | input_ids, attention_mask, token_type_ids = data['input_ids'], data['attention_mask'], data['token_type_ids'] 32 | _, y = self.bert(input_ids, token_type_ids=token_type_ids, attention_mask=attention_mask, 33 | output_all_encoded_layers=False) 34 | y = y.view(y.size()[0], -1) 35 | 36 | if mode == 'test' and config.getboolean('output', 'pool_out'): 37 | output = [] 38 | y = y.cpu().detach().numpy().tolist() 39 | for i, guid in enumerate(data['guid']): 40 | output.append([guid, y[i]]) 41 | return {"output": output} 42 | 43 | y = self.fc(y) 44 | y = y.view(y.size()[0], -1) 45 | 46 | if "label" in data.keys(): 47 | label = data["label"] 48 | loss = self.criterion(y, label.view(-1)) 49 | acc_result = self.accuracy_function(y, label, config, acc_result) 50 | return {"loss": loss, "acc_result": acc_result} 51 | 52 | else: 53 | output = [] 54 | y = y.cpu().detach().numpy().tolist() 55 | for i, guid in enumerate(data['guid']): 56 | output.append([guid, y[i]]) 57 | return {"output": output} 58 | 59 | -------------------------------------------------------------------------------- /model/loss.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | from torch.autograd import Variable 5 | import numpy as np 6 | 7 | 8 | class MultiLabelSoftmaxLoss(nn.Module): 9 | def __init__(self, config): 10 | super(MultiLabelSoftmaxLoss, self).__init__() 11 | self.task_num = config.getint("model", "output_dim") 12 | self.criterion = [] 13 | for a in range(0, self.task_num): 14 | try: 15 | ratio = config.getfloat("train", "loss_weight_%d" % a) 16 | self.criterion.append( 17 | nn.CrossEntropyLoss(weight=torch.from_numpy(np.array([1.0, ratio], dtype=np.float32)).cuda())) 18 | # print_info("Task %d with weight %.3lf" % (task, ratio)) 19 | except Exception as e: 20 | self.criterion.append(nn.CrossEntropyLoss()) 21 | 22 | def forward(self, outputs, labels): 23 | loss = 0 24 | for a in range(0, len(outputs[0])): 25 | o = outputs[:, a, :].view(outputs.size()[0], -1) 26 | loss += self.criterion[a](o, labels[:, a]) 27 | 28 | return loss 29 | 30 | 31 | def multi_label_cross_entropy_loss(outputs, labels): 32 | labels = labels.float() 33 | temp = outputs 34 | res = - labels * torch.log(temp) - (1 - labels) * torch.log(1 - temp) 35 | res = torch.mean(torch.sum(res, dim=1)) 36 | 37 | return res 38 | 39 | 40 | def cross_entropy_loss(outputs, labels): 41 | criterion = nn.CrossEntropyLoss() 42 | return criterion(outputs, labels) 43 | 44 | 45 | class FocalLoss(nn.Module): 46 | def __init__(self, gamma=0, alpha=None, size_average=True): 47 | super(FocalLoss, self).__init__() 48 | self.gamma = gamma 49 | self.alpha = alpha 50 | self.size_average = size_average 51 | 52 | def forward(self, input, target): 53 | if input.dim() > 2: 54 | input = input.view(input.size(0), input.size(1), -1) # N,C,H,W => N,C,H*W 55 | input = input.transpose(1, 2) # N,C,H*W => N,H*W,C 56 | input = input.contiguous().view(-1, input.size(2)) # N,H*W,C => N*H*W,C 57 | target = target.view(-1, 1) 58 | 59 | logpt = F.log_softmax(input) 60 | logpt = logpt.gather(1, target) 61 | logpt = logpt.view(-1) 62 | pt = Variable(logpt.data.exp()) 63 | 64 | if self.alpha is not None: 65 | if self.alpha.type() != input.data.type(): 66 | self.alpha = self.alpha.type_as(input.data) 67 | at = self.alpha.gather(0, target.data.view(-1)) 68 | logpt = logpt * Variable(at) 69 | 70 | loss = -1 * (1 - pt) ** self.gamma * logpt 71 | if self.size_average: 72 | return loss.mean() 73 | else: 74 | return loss.sum() 75 | -------------------------------------------------------------------------------- /tools/init_tool.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import torch 3 | import os 4 | 5 | from reader.reader import init_dataset, init_formatter, init_test_dataset 6 | from model import get_model 7 | from model.optimizer import init_optimizer 8 | from .output_init import init_output_function 9 | from .poolout_tool import load_state_keywise 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | 14 | def init_all(config, gpu_list, checkpoint, mode, *args, **params): 15 | result = {} 16 | 17 | logger.info("Begin to initialize dataset and formatter..., mode=%s", mode) 18 | if mode == "train": 19 | init_formatter(config, ["train", "valid"], *args, **params) 20 | result["train_dataset"], result["valid_dataset"] = init_dataset(config, *args, **params) 21 | else: 22 | init_formatter(config, ["test"], *args, **params) 23 | result["test_dataset"] = init_test_dataset(config, *args, **params) 24 | 25 | logger.info("Begin to initialize models...") 26 | 27 | model = get_model(config.get("model", "model_name"))(config, gpu_list, *args, **params) 28 | optimizer = init_optimizer(model, config, *args, **params) 29 | trained_epoch = 0 30 | global_step = 0 31 | 32 | if len(gpu_list) > 0: 33 | model = model.cuda() 34 | 35 | try: 36 | model.init_multi_gpu(gpu_list, config, *args, **params) 37 | except Exception as e: 38 | logger.warning("No init_multi_gpu implemented in the model, use single gpu instead.") 39 | 40 | try: 41 | parameters = torch.load(checkpoint) 42 | if mode == 'poolout': 43 | model = load_state_keywise(model, parameters["model"]) 44 | 45 | else: 46 | model.load_state_dict(parameters["model"]) 47 | 48 | if mode == "train": 49 | trained_epoch = parameters["trained_epoch"] 50 | if config.get("train", "optimizer") == parameters["optimizer_name"]: 51 | optimizer.load_state_dict(parameters["optimizer"]) 52 | else: 53 | logger.warning("Optimizer changed, do not load parameters of optimizer.") 54 | 55 | if "global_step" in parameters: 56 | global_step = parameters["global_step"] 57 | except Exception as e: 58 | information = "Cannot load checkpoint file with error %s" % str(e) 59 | if mode == "test": 60 | logger.error(information) 61 | raise e 62 | else: 63 | logger.warning(information) 64 | 65 | result["model"] = model 66 | if mode == "train": 67 | result["optimizer"] = optimizer 68 | result["trained_epoch"] = trained_epoch 69 | result["output_function"] = init_output_function(config) 70 | result["global_step"] = global_step 71 | 72 | logger.info("Initialize done.") 73 | 74 | return result 75 | -------------------------------------------------------------------------------- /model/nlp/BertPoolOutMax.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'yshao' 3 | 4 | 5 | import torch 6 | import torch.nn as nn 7 | import torch.nn.functional as F 8 | from pytorch_pretrained_bert import BertModel 9 | 10 | 11 | class BertPoolOutMax(nn.Module): 12 | def __init__(self, config, gpu_list, *args, **params): 13 | super(BertPoolOutMax, self).__init__() 14 | self.max_para_c = config.getint('model', 'max_para_c') 15 | self.max_para_q = config.getint('model', 'max_para_q') 16 | self.step = config.getint('model', 'step') 17 | self.max_len = config.getint("data", "max_seq_length") 18 | self.bert = BertModel.from_pretrained(config.get("model", "bert_path")) 19 | # self.maxpool = nn.MaxPool1d(kernel_size=self.max_para_c) 20 | self.maxpool = nn.MaxPool2d(kernel_size=(1, self.max_para_c)) 21 | 22 | def init_multi_gpu(self, device, config, *args, **params): 23 | self.bert = nn.DataParallel(self.bert, device_ids=device) 24 | 25 | def forward(self, data, config, gpu_list, acc_result, mode): 26 | input_ids, attention_mask, token_type_ids = data['input_ids'], data['attention_mask'], data['token_type_ids'] 27 | 28 | with torch.no_grad(): 29 | output = [] 30 | for k in range(input_ids.size()[0]): 31 | q_lst = [] 32 | for i in range(0, self.max_para_q, self.step): 33 | # print(input_ids[k, i:i+self.step].view(-1, self.max_len).size()) 34 | _, lst = self.bert(input_ids[k, i:i+self.step].view(-1, self.max_len), 35 | token_type_ids=token_type_ids[k, i:i+self.step].view(-1, self.max_len), 36 | attention_mask=attention_mask[k, i:i+self.step].view(-1, self.max_len)) 37 | # print('before view', lst.size()) 38 | lst = lst.view(self.step, self.max_para_c, -1) 39 | # print('after view', lst.size()) 40 | lst = lst.permute(2, 0, 1) 41 | # print('after permute', lst.size()) 42 | lst = lst.unsqueeze(0) 43 | # print('after unsquezze', lst.size()) 44 | max_out = self.maxpool(lst) 45 | # print('after maxpool', max_out.size()) 46 | max_out = max_out.squeeze() 47 | # print('after squeeze', max_out.size()) 48 | max_out = max_out.transpose(0, 1) 49 | q_lst.extend(max_out.cpu().tolist()) 50 | #input('continue?') 51 | # print(len(q_lst)) 52 | #exit() 53 | assert (len(q_lst) == self.max_para_q) 54 | output.append([data['guid'][k], q_lst]) 55 | return {"output": output} 56 | 57 | -------------------------------------------------------------------------------- /tools/poolout_tool.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'yshao' 3 | 4 | 5 | import logging 6 | import torch 7 | import json 8 | 9 | from torch.autograd import Variable 10 | from timeit import default_timer as timer 11 | 12 | from tools.eval_tool import gen_time_str, output_value 13 | 14 | logger = logging.getLogger(__name__) 15 | 16 | 17 | def load_state_keywise(model, pretrained_dict): 18 | logger.info("load state keywise start ...") 19 | model_dict = model.state_dict() 20 | # print(model_dict.keys()) 21 | # input("continue?") 22 | tmp_cnt = 0 23 | for k, v in pretrained_dict.items(): 24 | # kk = k.replace("module.", "") 25 | # print('k=', k) 26 | # input("continue?") 27 | # print('kk=', kk) 28 | # input("continue?") 29 | if k in model_dict and v.size() == model_dict[k].size(): 30 | model_dict[k] = v 31 | tmp_cnt += 1 32 | else: 33 | continue 34 | logger.info('tot #para=%d, load from pretrained #paras=%d' % (len(model_dict), tmp_cnt)) 35 | model.load_state_dict(model_dict) 36 | return model 37 | 38 | 39 | def pool_out(parameters, config, gpu_list, _outname): 40 | model = parameters["model"] 41 | dataset = parameters["test_dataset"] 42 | model.eval() 43 | 44 | acc_result = None 45 | total_loss = 0 46 | cnt = 0 47 | total_len = len(dataset) 48 | start_time = timer() 49 | output_info = "Pool_Out" 50 | 51 | output_time = config.getint("output", "output_time") 52 | save_step = config.getint("output", "save_step") 53 | step = -1 54 | result = [] 55 | 56 | for step, data in enumerate(dataset): 57 | for key in data.keys(): 58 | if isinstance(data[key], torch.Tensor): 59 | if len(gpu_list) > 0: 60 | data[key] = Variable(data[key].cuda()) 61 | else: 62 | data[key] = Variable(data[key]) 63 | 64 | results = model(data, config, gpu_list, acc_result, "poolout") 65 | result = result + results["output"] 66 | cnt += 1 67 | 68 | if step % output_time == 0: 69 | delta_t = timer() - start_time 70 | 71 | output_value(0, "poolout", "%d/%d" % (step + 1, total_len), "%s/%s" % ( 72 | gen_time_str(delta_t), gen_time_str(delta_t * (total_len - step - 1) / (step + 1))), 73 | "%.3lf" % (total_loss / (step + 1)), output_info, '\r', config) 74 | 75 | if save_step > 0 and step % save_step == 0: 76 | out_file = open(_outname, 'w', encoding='utf-8') 77 | for item in result: 78 | tmp_dict = { 79 | 'id_': item[0], 80 | 'res': item[1] 81 | } 82 | out_line = json.dumps(tmp_dict, ensure_ascii=False) + '\n' 83 | out_file.write(out_line) 84 | out_file.close() 85 | 86 | if step == -1: 87 | logger.error("There is no data given to the model in this epoch, check your data.") 88 | raise NotImplementedError 89 | 90 | delta_t = timer() - start_time 91 | output_info = "Pool_Out" 92 | output_value(0, "poolout", "%d/%d" % (step + 1, total_len), "%s/%s" % ( 93 | gen_time_str(delta_t), gen_time_str(delta_t * (total_len - step - 1) / (step + 1))), 94 | "%.3lf" % (total_loss / (step + 1)), output_info, None, config) 95 | 96 | return result 97 | -------------------------------------------------------------------------------- /reader/reader.py: -------------------------------------------------------------------------------- 1 | from torch.utils.data import DataLoader 2 | import logging 3 | 4 | import formatter as form 5 | from dataset import dataset_list 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | collate_fn = {} 10 | formatter = {} 11 | 12 | 13 | def init_formatter(config, task_list, *args, **params): 14 | for task in task_list: 15 | formatter[task] = form.init_formatter(config, task, *args, **params) 16 | 17 | def train_collate_fn(data): 18 | return formatter["train"].process(data, config, "train") 19 | 20 | def valid_collate_fn(data): 21 | return formatter["valid"].process(data, config, "valid") 22 | 23 | def test_collate_fn(data): 24 | return formatter["test"].process(data, config, "test") 25 | 26 | if task == "train": 27 | collate_fn[task] = train_collate_fn 28 | elif task == "valid": 29 | collate_fn[task] = valid_collate_fn 30 | else: 31 | collate_fn[task] = test_collate_fn 32 | 33 | 34 | def init_one_dataset(config, mode, *args, **params): 35 | temp_mode = mode 36 | if mode != "train": 37 | try: 38 | config.get("data", "%s_dataset_type" % temp_mode) 39 | except Exception as e: 40 | logger.warning( 41 | "[reader] %s_dataset_type has not been defined in config file, use [dataset] train_dataset_type instead." % temp_mode) 42 | temp_mode = "train" 43 | which = config.get("data", "%s_dataset_type" % temp_mode) 44 | 45 | if which in dataset_list: 46 | dataset = dataset_list[which](config, mode, *args, **params) 47 | batch_size = config.getint("train", "batch_size") 48 | shuffle = config.getboolean("train", "shuffle") 49 | reader_num = config.getint("train", "reader_num") 50 | drop_last = True 51 | if mode in ["valid", "test"]: 52 | if mode == "test": 53 | drop_last = False 54 | 55 | try: 56 | batch_size = config.getint("eval", "batch_size") 57 | except Exception as e: 58 | logger.warning("[eval] batch size has not been defined in config file, use [train] batch_size instead.") 59 | 60 | try: 61 | shuffle = config.getboolean("eval", "shuffle") 62 | except Exception as e: 63 | shuffle = False 64 | logger.warning("[eval] shuffle has not been defined in config file, use false as default.") 65 | try: 66 | reader_num = config.getint("eval", "reader_num") 67 | except Exception as e: 68 | logger.warning("[eval] reader num has not been defined in config file, use [train] reader num instead.") 69 | 70 | dataloader = DataLoader(dataset=dataset, 71 | batch_size=batch_size, 72 | shuffle=shuffle, 73 | num_workers=reader_num, 74 | collate_fn=collate_fn[mode], 75 | drop_last=drop_last) 76 | 77 | return dataloader 78 | else: 79 | logger.error("There is no dataset called %s, check your config." % which) 80 | raise NotImplementedError 81 | 82 | 83 | def init_test_dataset(config, *args, **params): 84 | init_formatter(config, ["test"], *args, **params) 85 | test_dataset = init_one_dataset(config, "test", *args, **params) 86 | 87 | return test_dataset 88 | 89 | 90 | def init_dataset(config, *args, **params): 91 | init_formatter(config, ["train", "valid"], *args, **params) 92 | train_dataset = init_one_dataset(config, "train", *args, **params) 93 | valid_dataset = init_one_dataset(config, "valid", *args, **params) 94 | 95 | return train_dataset, valid_dataset 96 | 97 | 98 | if __name__ == "__main__": 99 | pass 100 | -------------------------------------------------------------------------------- /formatter/nlp/bert_feature_tool.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'yshao' 3 | 4 | 5 | def _truncate_seq_pair(tokens_a, tokens_b, max_length): 6 | """Truncates a sequence pair in place to the maximum length.""" 7 | 8 | # This is a simple heuristic which will always truncate the longer sequence 9 | # one token at a time. This makes more sense than truncating an equal percent 10 | # of tokens from each, since if one sequence is very short then each token 11 | # that's truncated likely contains more information than a longer sequence. 12 | while True: 13 | total_length = len(tokens_a) + len(tokens_b) 14 | if total_length <= max_length: 15 | break 16 | if len(tokens_a) > len(tokens_b): 17 | tokens_a.pop() 18 | else: 19 | tokens_b.pop() 20 | 21 | 22 | def example_item_to_feature(example, max_seq_length, 23 | tokenizer, output_mode, mode='test', 24 | cls_token_at_end=False, pad_on_left=False, 25 | cls_token='[CLS]', sep_token='[SEP]', pad_token=0, 26 | sequence_a_segment_id=0, sequence_b_segment_id=1, 27 | cls_token_segment_id=1, pad_token_segment_id=0, 28 | mask_padding_with_zero=True): 29 | 30 | tokens_a = tokenizer.tokenize(example['text_a']) 31 | 32 | tokens_b = None 33 | if 'text_b' in example: 34 | tokens_b = tokenizer.tokenize(example['text_b']) 35 | # Modifies `tokens_a` and `tokens_b` in place so that the total 36 | # length is less than the specified length. 37 | # Account for [CLS], [SEP], [SEP] with "- 3" 38 | _truncate_seq_pair(tokens_a, tokens_b, max_seq_length - 3) 39 | else: 40 | # Account for [CLS] and [SEP] with "- 2" 41 | if len(tokens_a) > max_seq_length - 2: 42 | tokens_a = tokens_a[:(max_seq_length - 2)] 43 | 44 | tokens = tokens_a + [sep_token] 45 | segment_ids = [sequence_a_segment_id] * len(tokens) 46 | 47 | if tokens_b: 48 | tokens += tokens_b + [sep_token] 49 | segment_ids += [sequence_b_segment_id] * (len(tokens_b) + 1) 50 | 51 | if cls_token_at_end: 52 | tokens = tokens + [cls_token] 53 | segment_ids = segment_ids + [cls_token_segment_id] 54 | else: 55 | tokens = [cls_token] + tokens 56 | segment_ids = [cls_token_segment_id] + segment_ids 57 | 58 | input_ids = tokenizer.convert_tokens_to_ids(tokens) 59 | 60 | # The mask has 1 for real tokens and 0 for padding tokens. Only real 61 | # tokens are attended to. 62 | input_mask = [1 if mask_padding_with_zero else 0] * len(input_ids) 63 | 64 | # Zero-pad up to the sequence length. 65 | padding_length = max_seq_length - len(input_ids) 66 | if pad_on_left: 67 | input_ids = ([pad_token] * padding_length) + input_ids 68 | input_mask = ([0 if mask_padding_with_zero else 1] * padding_length) + input_mask 69 | segment_ids = ([pad_token_segment_id] * padding_length) + segment_ids 70 | else: 71 | input_ids = input_ids + ([pad_token] * padding_length) 72 | input_mask = input_mask + ([0 if mask_padding_with_zero else 1] * padding_length) 73 | segment_ids = segment_ids + ([pad_token_segment_id] * padding_length) 74 | 75 | assert len(input_ids) == max_seq_length 76 | assert len(input_mask) == max_seq_length 77 | assert len(segment_ids) == max_seq_length 78 | 79 | if mode == 'test': 80 | res_dict = { 81 | 'input_ids': input_ids, 82 | 'input_mask': input_mask, 83 | 'segment_ids': segment_ids, 84 | } 85 | return res_dict 86 | 87 | if output_mode == "classification": 88 | label_id = int(example['label']) 89 | else: 90 | label_id = float(example['label']) 91 | 92 | res_dict = { 93 | 'input_ids': input_ids, 94 | 'input_mask': input_mask, 95 | 'segment_ids': segment_ids, 96 | 'label_id': label_id, 97 | } 98 | 99 | return res_dict 100 | 101 | 102 | -------------------------------------------------------------------------------- /dataset/nlp/JsonFromFiles.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | from torch.utils.data import Dataset 4 | 5 | from tools.dataset_tool import dfs_search 6 | 7 | 8 | class JsonFromFilesDataset(Dataset): 9 | def __init__(self, config, mode, encoding="utf8", *args, **params): 10 | self.config = config 11 | self.mode = mode 12 | self.file_list = [] 13 | self.data_path = config.get("data", "%s_data_path" % mode) 14 | self.encoding = encoding 15 | 16 | filename_list = config.get("data", "%s_file_list" % mode).replace(" ", "").split(",") 17 | recursive = config.getboolean("data", "recursive") 18 | 19 | for name in filename_list: 20 | self.file_list = self.file_list + dfs_search(os.path.join(self.data_path, name), recursive) 21 | self.file_list.sort() 22 | 23 | self.load_mem = config.getboolean("data", "load_into_mem") 24 | self.json_format = config.get("data", "json_format") 25 | 26 | if self.load_mem: 27 | self.data = [] 28 | for filename in self.file_list: 29 | if self.json_format == "single": 30 | self.data = self.data + json.load(open(filename, "r", encoding=encoding)) 31 | else: 32 | f = open(filename, "r", encoding=encoding) 33 | for line in f: 34 | self.data.append(json.loads(line)) 35 | 36 | else: 37 | self.total = 0 38 | self.prefix_file_cnt = [] 39 | 40 | if self.json_format == "single": 41 | self.temp_data = { 42 | "data": json.load(open(self.file_list[0], "r", encoding=encoding)), 43 | "file_id": 0 44 | } 45 | else: 46 | self.temp_file_list = [] 47 | 48 | for filename in self.file_list: 49 | if self.json_format == "single": 50 | data = json.load(open(filename, "r", encoding=encoding)) 51 | self.prefix_file_cnt.append(len(data)) 52 | else: 53 | f = open(filename, "r", encoding=encoding) 54 | cnt = 0 55 | for line in f: 56 | cnt += 1 57 | f.close() 58 | self.temp_file_list.append({ 59 | "file": open(filename, "r", encoding=encoding), 60 | "cnt": 0 61 | }) 62 | self.prefix_file_cnt.append(cnt) 63 | 64 | for a in range(1, len(self.prefix_file_cnt)): 65 | self.prefix_file_cnt[a] += self.prefix_file_cnt[a - 1] 66 | self.total = self.prefix_file_cnt[-1] 67 | 68 | def get_file_id(self, item): 69 | l = 0 70 | r = len(self.prefix_file_cnt) 71 | while l + 1 != r: 72 | m = (l + r) // 2 73 | if self.prefix_file_cnt[m-1] <= item: 74 | l = m 75 | else: 76 | r = m 77 | 78 | return l 79 | 80 | def __getitem__(self, item): 81 | if self.load_mem: 82 | return self.data[item] 83 | else: 84 | which = self.get_file_id(item) 85 | if which == 0: 86 | idx = item 87 | else: 88 | idx = item - self.prefix_file_cnt[which - 1] 89 | 90 | if self.json_format == "single": 91 | if self.temp_data["file_id"] != which: 92 | self.temp_data = { 93 | "data": json.load(open(self.file_list[which], "r", encoding=self.encoding)), 94 | "file_id": 0 95 | } 96 | 97 | return self.temp_data["data"][idx] 98 | 99 | else: 100 | if self.temp_file_list[which]["cnt"] > idx: 101 | self.temp_file_list[which] = { 102 | "file": open(self.file_list[which], "r", encoding=self.encoding), 103 | "cnt": 0 104 | } 105 | 106 | delta = idx - self.temp_file_list[which]["cnt"] 107 | self.temp_file_list[which]["file"].readlines(delta) 108 | 109 | data = json.loads(self.temp_file_list[which]["file"].readline()) 110 | self.temp_file_list[which]["cnt"] = idx + 1 111 | 112 | return data 113 | 114 | def __len__(self): 115 | if self.load_mem: 116 | return len(self.data) 117 | else: 118 | return self.total 119 | -------------------------------------------------------------------------------- /tools/eval_tool.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import torch 4 | import numpy as np 5 | from collections import defaultdict 6 | from torch.autograd import Variable 7 | from torch.optim import lr_scheduler 8 | from tensorboardX import SummaryWriter 9 | from timeit import default_timer as timer 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | 14 | def gen_time_str(t): 15 | t = int(t) 16 | minute = t // 60 17 | second = t % 60 18 | return '%2d:%02d' % (minute, second) 19 | 20 | 21 | def output_value(epoch, mode, step, time, loss, info, end, config): 22 | try: 23 | delimiter = config.get("output", "delimiter") 24 | except Exception as e: 25 | delimiter = " " 26 | s = "" 27 | s = s + str(epoch) + " " 28 | while len(s) < 7: 29 | s += " " 30 | s = s + str(mode) + " " 31 | while len(s) < 14: 32 | s += " " 33 | s = s + str(step) + " " 34 | while len(s) < 25: 35 | s += " " 36 | s += str(time) 37 | while len(s) < 40: 38 | s += " " 39 | s += str(loss) 40 | while len(s) < 48: 41 | s += " " 42 | s += str(info) 43 | s = s.replace(" ", delimiter) 44 | if not (end is None): 45 | print(s, end=end) 46 | else: 47 | print(s) 48 | 49 | 50 | def eval_micro_query(_result_list): 51 | label_dict = defaultdict(lambda: []) 52 | pred_dict = defaultdict(lambda: defaultdict(lambda: 0)) 53 | for item in _result_list: 54 | guid = item[0] 55 | label = int(item[1]) 56 | pred = np.argmax(item[2]) 57 | qid, cid = guid.split('_') 58 | if label > 0: 59 | label_dict[qid].append(cid) 60 | pred_dict[qid][cid] = pred 61 | assert (len(pred_dict) == len(label_dict)) 62 | 63 | correct = 0 64 | label = 0 65 | predict = 0 66 | for qid in label_dict: 67 | label += len(label_dict[qid]) 68 | for qid in pred_dict: 69 | for cid in pred_dict[qid]: 70 | if pred_dict[qid][cid] == 1: 71 | predict += 1 72 | if cid in label_dict[qid]: 73 | correct += 1 74 | if correct == 0: 75 | micro_prec_query = 0 76 | micro_recall_query = 0 77 | else: 78 | micro_prec_query = float(correct) / predict 79 | micro_recall_query = float(correct) / label 80 | if micro_prec_query > 0 or micro_recall_query > 0: 81 | micro_f1_query = (2 * micro_prec_query * micro_recall_query) / (micro_prec_query + micro_recall_query) 82 | else: 83 | micro_f1_query = 0 84 | return micro_prec_query, micro_recall_query, micro_f1_query 85 | 86 | 87 | def valid(model, dataset, epoch, writer, config, gpu_list, output_function, mode="valid"): 88 | model.eval() 89 | 90 | acc_result = None 91 | total_loss = 0 92 | cnt = 0 93 | total_len = len(dataset) 94 | start_time = timer() 95 | output_info = "" 96 | 97 | output_time = config.getint("output", "output_time") 98 | step = -1 99 | more = "" 100 | if total_len < 10000: 101 | more = "\t" 102 | result = [] 103 | 104 | for step, data in enumerate(dataset): 105 | for key in data.keys(): 106 | if isinstance(data[key], torch.Tensor): 107 | if len(gpu_list) > 0: 108 | data[key] = Variable(data[key].cuda()) 109 | else: 110 | data[key] = Variable(data[key]) 111 | 112 | results = model(data, config, gpu_list, acc_result, "valid") 113 | 114 | loss, acc_result, output = results["loss"], results["acc_result"], results["output"] 115 | total_loss += float(loss) 116 | result = result + output 117 | cnt += 1 118 | 119 | if step % output_time == 0: 120 | delta_t = timer() - start_time 121 | 122 | output_value(epoch, mode, "%d/%d" % (step + 1, total_len), "%s/%s" % ( 123 | gen_time_str(delta_t), gen_time_str(delta_t * (total_len - step - 1) / (step + 1))), 124 | "%.3lf" % (total_loss / (step + 1)), output_info, '\r', config) 125 | 126 | if step == -1: 127 | logger.error("There is no data given to the model in this epoch, check your data.") 128 | raise NotImplementedError 129 | 130 | delta_t = timer() - start_time 131 | output_info = output_function(acc_result, config) 132 | output_value(epoch, mode, "%d/%d" % (step + 1, total_len), "%s/%s" % ( 133 | gen_time_str(delta_t), gen_time_str(delta_t * (total_len - step - 1) / (step + 1))), 134 | "%.3lf" % (total_loss / (step + 1)), output_info, None, config) 135 | 136 | writer.add_scalar(config.get("output", "model_name") + "_eval_epoch", float(total_loss) / (step + 1), 137 | epoch) 138 | 139 | # eval results based on query micro F1 140 | micro_prec_query, micro_recall_query, micro_f1_query = eval_micro_query(result) 141 | loss_tmp = total_loss / (step + 1) 142 | print('valid set: micro_prec_query=%.4f, micro_recall_query=%.4f, micro_f1_query=%.4f' % 143 | (micro_prec_query, micro_recall_query, micro_f1_query)) 144 | 145 | model.train() 146 | return {'precision': micro_prec_query, 'recall': micro_recall_query, 'f1': micro_f1_query, 'loss': loss_tmp} 147 | -------------------------------------------------------------------------------- /tools/accuracy_tool.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import torch 3 | 4 | logger = logging.Logger(__name__) 5 | 6 | 7 | def get_prf(res): 8 | # According to https://github.com/dice-group/gerbil/wiki/Precision,-Recall-and-F1-measure 9 | if res["TP"] == 0: 10 | if res["FP"] == 0 and res["FN"] == 0: 11 | precision = 1.0 12 | recall = 1.0 13 | f1 = 1.0 14 | else: 15 | precision = 0.0 16 | recall = 0.0 17 | f1 = 0.0 18 | else: 19 | precision = 1.0 * res["TP"] / (res["TP"] + res["FP"]) 20 | recall = 1.0 * res["TP"] / (res["TP"] + res["FN"]) 21 | f1 = 2 * precision * recall / (precision + recall) 22 | 23 | return precision, recall, f1 24 | 25 | 26 | def gen_micro_macro_result(res): 27 | precision = [] 28 | recall = [] 29 | f1 = [] 30 | total = {"TP": 0, "FP": 0, "FN": 0, "TN": 0} 31 | for a in range(0, len(res)): 32 | total["TP"] += res[a]["TP"] 33 | total["FP"] += res[a]["FP"] 34 | total["FN"] += res[a]["FN"] 35 | total["TN"] += res[a]["TN"] 36 | 37 | p, r, f = get_prf(res[a]) 38 | precision.append(p) 39 | recall.append(r) 40 | f1.append(f) 41 | 42 | micro_precision, micro_recall, micro_f1 = get_prf(total) 43 | 44 | macro_precision = 0 45 | macro_recall = 0 46 | macro_f1 = 0 47 | for a in range(0, len(f1)): 48 | macro_precision += precision[a] 49 | macro_recall += recall[a] 50 | macro_f1 += f1[a] 51 | 52 | macro_precision /= len(f1) 53 | macro_recall /= len(f1) 54 | macro_f1 /= len(f1) 55 | 56 | return { 57 | "micro_precision": round(micro_precision, 3), 58 | "micro_recall": round(micro_recall, 3), 59 | "micro_f1": round(micro_f1, 3), 60 | "macro_precision": round(macro_precision, 3), 61 | "macro_recall": round(macro_recall, 3), 62 | "macro_f1": round(macro_f1, 3) 63 | } 64 | 65 | 66 | def null_accuracy_function(outputs, label, config, result=None): 67 | return None 68 | 69 | 70 | def single_label_top1_accuracy(outputs, label, config, result=None): 71 | if result is None: 72 | result = [] 73 | id1 = torch.max(outputs, dim=1)[1] 74 | # id2 = torch.max(label, dim=1)[1] 75 | id2 = label 76 | nr_classes = outputs.size(1) 77 | while len(result) < nr_classes: 78 | result.append({"TP": 0, "FN": 0, "FP": 0, "TN": 0}) 79 | for a in range(0, len(id1)): 80 | # if len(result) < a: 81 | # result.append({"TP": 0, "FN": 0, "FP": 0, "TN": 0}) 82 | 83 | it_is = int(id1[a]) 84 | should_be = int(id2[a]) 85 | if it_is == should_be: 86 | result[it_is]["TP"] += 1 87 | else: 88 | result[it_is]["FP"] += 1 89 | result[should_be]["FN"] += 1 90 | 91 | return result 92 | 93 | 94 | def multi_label_accuracy(outputs, label, config, result=None): 95 | if len(label[0]) != len(outputs[0]): 96 | raise ValueError('Input dimensions of labels and outputs must match.') 97 | 98 | outputs = outputs.data 99 | labels = label.data 100 | 101 | if result is None: 102 | result = [] 103 | 104 | total = 0 105 | nr_classes = outputs.size(1) 106 | 107 | while len(result) < nr_classes: 108 | result.append({"TP": 0, "FN": 0, "FP": 0, "TN": 0}) 109 | 110 | for i in range(nr_classes): 111 | outputs1 = (outputs[:, i] >= 0.5).long() 112 | labels1 = (labels[:, i].float() >= 0.5).long() 113 | total += int((labels1 * outputs1).sum()) 114 | total += int(((1 - labels1) * (1 - outputs1)).sum()) 115 | 116 | if result is None: 117 | continue 118 | 119 | # if len(result) < i: 120 | # result.append({"TP": 0, "FN": 0, "FP": 0, "TN": 0}) 121 | 122 | result[i]["TP"] += int((labels1 * outputs1).sum()) 123 | result[i]["FN"] += int((labels1 * (1 - outputs1)).sum()) 124 | result[i]["FP"] += int(((1 - labels1) * outputs1).sum()) 125 | result[i]["TN"] += int(((1 - labels1) * (1 - outputs1)).sum()) 126 | 127 | return result 128 | 129 | 130 | def single_label_top2_accuracy(outputs, label, config, result=None): 131 | raise NotImplementedError 132 | # still bug here 133 | 134 | if result is None: 135 | result = [] 136 | # print(label) 137 | 138 | id1 = torch.max(outputs, dim=1)[1] 139 | # id2 = torch.max(label, dim=1)[1] 140 | id2 = label 141 | nr_classes = outputs.size(1) 142 | while len(result) < nr_classes: 143 | result.append({"TP": 0, "FN": 0, "FP": 0, "TN": 0}) 144 | for a in range(0, len(id1)): 145 | # if len(result) < a: 146 | # result.append({"TP": 0, "FN": 0, "FP": 0, "TN": 0}) 147 | 148 | it_is = int(id1[a]) 149 | should_be = int(id2[a]) 150 | if it_is == should_be: 151 | result[it_is]["TP"] += 1 152 | else: 153 | result[it_is]["FP"] += 1 154 | result[should_be]["FN"] += 1 155 | 156 | _, prediction = torch.topk(outputs, 2, 1, largest=True) 157 | prediction1 = prediction[:, 0:1] 158 | prediction2 = prediction[:, 1:] 159 | 160 | prediction1 = prediction1.view(-1) 161 | prediction2 = prediction2.view(-1) 162 | 163 | return result 164 | -------------------------------------------------------------------------------- /formatter/nlp/BertDocParaFormatter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'yshao' 3 | 4 | import json 5 | import torch 6 | import os 7 | 8 | from pytorch_pretrained_bert.tokenization import BertTokenizer 9 | 10 | from formatter.Basic import BasicFormatter 11 | from .bert_feature_tool import example_item_to_feature 12 | 13 | 14 | class BertDocParaFormatter(BasicFormatter): 15 | def __init__(self, config, mode, *args, **params): 16 | super().__init__(config, mode, *args, **params) 17 | self.tokenizer = BertTokenizer.from_pretrained(config.get("model", "bert_path")) 18 | self.max_len = config.getint("data", "max_seq_length") 19 | self.mode = mode 20 | self.output_mode = config.get('model', 'output_mode') 21 | self.max_para_c = config.getint('model', 'max_para_c') 22 | self.max_para_q = config.getint('model', 'max_para_q') 23 | 24 | def process(self, data, config, mode, *args, **params): 25 | # query_para_num=m, doc_para_num = n, matrix = m * n 26 | guids = [] 27 | input_ids = [] 28 | attention_mask = [] 29 | token_type_ids = [] 30 | if mode != 'test': 31 | labels = [] 32 | 33 | for temp in data: 34 | guid = temp['guid'] 35 | label = temp['label'] 36 | q_paras = temp['q_paras'] 37 | c_paras = temp['c_paras'] 38 | input_ids_item = [] 39 | attention_mask_item = [] 40 | token_type_ids_item = [] 41 | for m in range(min(self.max_para_q, len(q_paras))): 42 | q_p = q_paras[m] 43 | input_ids_row = [] 44 | attention_mask_row = [] 45 | token_type_ids_row = [] 46 | for n in range(min(self.max_para_c, len(c_paras))): 47 | c_p = c_paras[n] 48 | example = { 49 | 'text_a': q_p, 50 | 'text_b': c_p, 51 | 'label': label 52 | } 53 | res_dict = example_item_to_feature(example, self.max_len, self.tokenizer, self.output_mode, 54 | mode=mode, cls_token_at_end=False, pad_on_left=False, 55 | cls_token_segment_id=0, pad_token_segment_id=0) 56 | input_ids_row.append(res_dict['input_ids']) 57 | attention_mask_row.append(res_dict['input_mask']) 58 | token_type_ids_row.append(res_dict['segment_ids']) 59 | if len(c_paras) < self.max_para_c: 60 | for j in range(len(c_paras), self.max_para_c): 61 | input_ids_row.append([0] * self.max_len) 62 | attention_mask_row.append([0] * self.max_len) 63 | token_type_ids_row.append([0] * self.max_len) 64 | assert (len(input_ids_row) == self.max_para_c) 65 | assert (len(attention_mask_row) == self.max_para_c) 66 | assert (len(token_type_ids_row) == self.max_para_c) 67 | input_ids_item.append(input_ids_row) 68 | attention_mask_item.append(attention_mask_row) 69 | token_type_ids_item.append(token_type_ids_row) 70 | if len(q_paras) < self.max_para_q: 71 | for i in range(len(q_paras), self.max_para_q): 72 | input_ids_row = [] 73 | attention_mask_row = [] 74 | token_type_ids_row = [] 75 | for j in range(self.max_para_c): 76 | input_ids_row.append([0] * self.max_len) 77 | attention_mask_row.append([0] * self.max_len) 78 | token_type_ids_row.append([0] * self.max_len) 79 | input_ids_item.append(input_ids_row) 80 | attention_mask_item.append(attention_mask_row) 81 | token_type_ids_item.append(token_type_ids_row) 82 | assert (len(input_ids_item) == self.max_para_q) 83 | assert (len(attention_mask_item) == self.max_para_q) 84 | assert (len(token_type_ids_item) == self.max_para_q) 85 | 86 | guids.append(guid) 87 | input_ids.append(input_ids_item) 88 | attention_mask.append(attention_mask_item) 89 | token_type_ids.append(token_type_ids_item) 90 | 91 | if mode != 'test': 92 | labels.append(label) 93 | 94 | input_ids = torch.LongTensor(input_ids) 95 | attention_mask = torch.LongTensor(attention_mask) 96 | token_type_ids = torch.LongTensor(token_type_ids) 97 | 98 | # print('tensor size: ', input_ids.size(), attention_mask.size(), token_type_ids.size()) 99 | # input('continue?') 100 | 101 | if mode != 'test': 102 | labels = torch.LongTensor(labels) 103 | 104 | if mode != 'test': 105 | return {'guid': guids, 'input_ids': input_ids, 'attention_mask': attention_mask, 106 | 'token_type_ids': token_type_ids, 107 | 'label': labels} 108 | else: 109 | return {'guid': guids, 'input_ids': input_ids, 'attention_mask': attention_mask, 110 | 'token_type_ids': token_type_ids} 111 | 112 | -------------------------------------------------------------------------------- /tools/train_tool.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import torch 4 | from torch.autograd import Variable 5 | from torch.optim import lr_scheduler 6 | from tensorboardX import SummaryWriter 7 | import shutil 8 | from timeit import default_timer as timer 9 | from collections import defaultdict 10 | import json 11 | 12 | from tools.eval_tool import valid, gen_time_str, output_value 13 | from tools.init_tool import init_test_dataset, init_formatter 14 | 15 | logger = logging.getLogger(__name__) 16 | 17 | 18 | def checkpoint(filename, model, optimizer, trained_epoch, config, global_step): 19 | model_to_save = model.module if hasattr(model, 'module') else model 20 | save_params = { 21 | "model": model_to_save.state_dict(), 22 | "optimizer_name": config.get("train", "optimizer"), 23 | "optimizer": optimizer.state_dict(), 24 | "trained_epoch": trained_epoch, 25 | "global_step": global_step 26 | } 27 | 28 | try: 29 | torch.save(save_params, filename) 30 | except Exception as e: 31 | logger.warning("Cannot save models with error %s, continue anyway" % str(e)) 32 | 33 | 34 | def train(parameters, config, gpu_list): 35 | epoch = config.getint("train", "epoch") 36 | batch_size = config.getint("train", "batch_size") 37 | 38 | output_time = config.getint("output", "output_time") 39 | test_time = config.getint("output", "test_time") 40 | 41 | output_path = os.path.join(config.get("output", "model_path"), config.get("output", "model_name")) 42 | if os.path.exists(output_path): 43 | logger.warning("Output path exists, check whether need to change a name of model") 44 | os.makedirs(output_path, exist_ok=True) 45 | 46 | trained_epoch = parameters["trained_epoch"] + 1 47 | model = parameters["model"] 48 | optimizer = parameters["optimizer"] 49 | dataset = parameters["train_dataset"] 50 | global_step = parameters["global_step"] 51 | output_function = parameters["output_function"] 52 | 53 | if trained_epoch == 0: 54 | shutil.rmtree( 55 | os.path.join(config.get("output", "tensorboard_path"), config.get("output", "model_name")), True) 56 | 57 | os.makedirs(os.path.join(config.get("output", "tensorboard_path"), config.get("output", "model_name")), 58 | exist_ok=True) 59 | 60 | writer = SummaryWriter(os.path.join(config.get("output", "tensorboard_path"), config.get("output", "model_name")), 61 | config.get("output", "model_name")) 62 | 63 | step_size = config.getint("train", "step_size") 64 | gamma = config.getfloat("train", "lr_multiplier") 65 | exp_lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=step_size, gamma=gamma) 66 | exp_lr_scheduler.step(trained_epoch) 67 | 68 | logger.info("Training start....") 69 | 70 | print("Epoch Stage Iterations Time Usage Loss Output Information") 71 | 72 | total_len = len(dataset) 73 | more = "" 74 | if total_len < 10000: 75 | more = "\t" 76 | for epoch_num in range(trained_epoch, epoch): 77 | start_time = timer() 78 | current_epoch = epoch_num 79 | 80 | exp_lr_scheduler.step(current_epoch) 81 | 82 | acc_result = None 83 | total_loss = 0 84 | 85 | output_info = "" 86 | step = -1 87 | for step, data in enumerate(dataset): 88 | for key in data.keys(): 89 | if isinstance(data[key], torch.Tensor): 90 | if len(gpu_list) > 0: 91 | data[key] = Variable(data[key].cuda()) 92 | else: 93 | data[key] = Variable(data[key]) 94 | 95 | optimizer.zero_grad() 96 | 97 | results = model(data, config, gpu_list, acc_result, "train") 98 | 99 | loss, acc_result = results["loss"], results["acc_result"] 100 | total_loss += float(loss) 101 | 102 | loss.backward() 103 | optimizer.step() 104 | 105 | if step % output_time == 0: 106 | output_info = output_function(acc_result, config) 107 | 108 | delta_t = timer() - start_time 109 | 110 | output_value(current_epoch, "train", "%d/%d" % (step + 1, total_len), "%s/%s" % ( 111 | gen_time_str(delta_t), gen_time_str(delta_t * (total_len - step - 1) / (step + 1))), 112 | "%.3lf" % (total_loss / (step + 1)), output_info, '\r', config) 113 | 114 | global_step += 1 115 | writer.add_scalar(config.get("output", "model_name") + "_train_iter", float(loss), global_step) 116 | 117 | output_value(current_epoch, "train", "%d/%d" % (step + 1, total_len), "%s/%s" % ( 118 | gen_time_str(delta_t), gen_time_str(delta_t * (total_len - step - 1) / (step + 1))), 119 | "%.3lf" % (total_loss / (step + 1)), output_info, None, config) 120 | 121 | if step == -1: 122 | logger.error("There is no data given to the model in this epoch, check your data.") 123 | raise NotImplementedError 124 | 125 | checkpoint(os.path.join(output_path, "%d.pkl" % current_epoch), model, optimizer, current_epoch, config, 126 | global_step) 127 | writer.add_scalar(config.get("output", "model_name") + "_train_epoch", float(total_loss) / (step + 1), 128 | current_epoch) 129 | 130 | if current_epoch % test_time == 0: 131 | with torch.no_grad(): 132 | eval_res = valid(model, parameters["valid_dataset"], current_epoch, writer, config, gpu_list, 133 | output_function) 134 | 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BERT-PLI: Modeling Paragraph-Level Interactions for Legal Case Retrieval 2 | 3 | This repository contains the code for BERT-PLI in our IJCAI-PRICAI 2020 paper: *BERT-PLI: Modeling Paragraph-Level Interactions for Legal Case Retrieval*. 4 | 5 | ## File Outline 6 | 7 | ### Model 8 | 9 | - ``./model/nlp/BertPoint.py``: model for Stage2: fine-tune a paragraph pair classification Task. 10 | 11 | - ``./model/nlp/BertPoolOutMax.py`` : model paragraph-level interactions between documents. 12 | 13 | - ``./model/nlp/AttenRNN.py`` : aggregate paragraph-level representations. 14 | 15 | ### Config 16 | 17 | - ``./config/nlp/BertPoint.config`` : configuration of ``./model/nlp/BertPoint.py`` (Stage 2, fine-tune). 18 | 19 | - ``./config/nlp/BertPoolOutMax.config`` : configuration of ``./model/nlp/BertPoolOutMax.py`` . 20 | 21 | - ``./config/nlp/AttenGRU.config`` / ``./config/nlp/AttenLSTM.config`` : configuration of ``./model/nlp/AttenRNN.py`` (GRU / LSTM, repectively) 22 | 23 | 24 | ### Formatter 25 | 26 | - ``./formatter/nlp/BertPairTextFormatter.py``: prepare input for ``./model/nlp/BertPoint.py`` (Stage 2, fine-tune) 27 | 28 | - ``./formatter/nlp/BertDocParaFormatter.py`` : prepare input for ``./model/nlp/BertPoolOutMax.py`` 29 | 30 | - ``./formatter/nlp/AttenRNNFormatter.py`` : prepare input for ``./model/nlp/AttenRNN.py`` 31 | 32 | 33 | ### Examples 34 | 35 | Examples of input data. Note that we cannot make the raw data public according to the memorandum we signed for the dataset. The examples here have been processed manually and differ from the true data. 36 | 37 | - ``./examples/task2/data_sample.json``: example input for Stage 2 (fine-tune). 38 | 39 | The format: 40 | 41 | ``` 42 | { 43 | "guid": "queryID_paraID", 44 | "text_a": text of the decision paragraph, 45 | "text_b": text of the candidate paragraph, 46 | "label": 0 or 1 47 | } 48 | ``` 49 | 50 | - ``./examples/task1/case_para_sample.json``: example input used in ``./config/nlp/BertPoolOutMax.config``. 51 | 52 | The format: 53 | 54 | ``` 55 | { 56 | "guid": "queryID_docID", 57 | "q_paras": [...], // a list of paragraphs in query case, 58 | "c_paras": [...], // a list of parameters in candidate case, 59 | "label": 0, // 0 or 1, denote the relevance 60 | } 61 | ``` 62 | 63 | - ``./examples/task1/embedding_sample.json``: example input used in ``./config/nlp/AttenGRU.config`` and ``./config/nlp/AttenLSTM.config`` 64 | 65 | The format: 66 | 67 | ``` 68 | { 69 | "guid": "queryID_docID", 70 | "res": [[],...,[]], // N * 768, result of BertPoolOutMax, 71 | "label": 0, // 0 or 1, denote the relevance 72 | } 73 | ``` 74 | 75 | ### Scripts 76 | 77 | - ``poolout.py``/``train.py``/``test.py``, main entrance for *poolling out*, *training*, and *testing*. 78 | 79 | ### Requirements 80 | 81 | - See ``requirements.txt`` 82 | 83 | ## How to Run? 84 | 85 | - Stage 1: BM25 Selection: 86 | 87 | The BM25 score is calculated according to the standard [scoring function](https://en.wikipedia.org/wiki/Okapi_BM25). We set $k_1=1.5$, $b=0.75$. 88 | 89 | - Stage 2: BERT Fine-tuning: 90 | 91 | ```bash 92 | python3 train.py -c config/nlp/BertPoint.config -g [GPU_LIST] 93 | ``` 94 | 95 | - Stage 3: 96 | 97 | Get paragraph-level interactions by BERT: 98 | 99 | ```bash 100 | python3 poolout.py -c config/nlp/BertPoolOutMax.config -g [GPU_LIST] --checkpoint [path of Bert checkpoint] --result [path to save results] 101 | ``` 102 | Train 103 | 104 | ```bash 105 | python3 train.py -c config/nlp/AttenGRU.config -g [GPU_LIST] 106 | 107 | python3 train.py -c config/nlp/AttenLSTM.config -g [GPU_LIST] 108 | ``` 109 | 110 | Test 111 | 112 | ```bash 113 | python3 test.py -c config/nlp/AttenGRU.config -g [GPU_LIST] --checkpoint [path of model checkpoint] --result [path to save results] 114 | 115 | python3 test.py -c config/nlp/AttenLSTM.config -g [GPU_LIST] --checkpoint [path of Bert checkpoint] --result [path to save results] 116 | ``` 117 | 118 | 119 | ## Experimental Settings 120 | 121 | ### Data 122 | 123 | Please visit [COLIEE 2019](https://sites.ualberta.ca/~rabelo/COLIEE2019/) to apply for the whole dataset. 124 | 125 | Please email shaoyq18@mails.tsinghua.edu.cn for the checkpoint of fine-tuned BERT. 126 | 127 | ### Evaluation Metric 128 | 129 | We follow the evaluation metrics in [COLIEEE 2019](https://sites.ualberta.ca/~rabelo/COLIEE2019/). Note that results should be evaluated on the whole document pool (e.g., 200 candidate documents for each query case.) 130 | 131 | $$ Precision = \dfrac{\textrm{correctly retrieved cases (paragraphs) for all queries}}{\textrm{retrieved cases (paragraphs) for all queries}} $$ 132 | 133 | $$ Recall = \dfrac{\textrm{correctly retrieved cases (paragraphs) for all queries}}{\textrm{relevant cases (paragraphs) for all queries}} $$ 134 | 135 | $$ F-measure = \dfrac{2 \times Precision \times Recall}{Precision + Recall} $$ 136 | 137 | ### Parameter Settings 138 | 139 | Please refer to the configuration files for parameters for each step. 140 | 141 | For example, in Stage 2, $learning rate = 10^{-5}$, $batch size = 16$, $training epoch = 3$. In Stage 3, $N=54$, $M=40$, $learning rate=10^{-4}$, $weight_decay=10^{-6}$. 142 | 143 | 144 | ## Contact 145 | For more details, please refer to our paper [BERT-PLI: Modeling Paragraph-Level Interactions for Legal Case Retrieval](https://www.ijcai.org/Proceedings/2020/0484.pdf). 146 | If you have any questions, please email shaoyq18@mails.tsinghua.edu.cn . 147 | 148 | 149 | ## Citation 150 | ``` 151 | @inproceedings{shao2020bert, 152 | title={BERT-PLI: Modeling Paragraph-Level Interactions for Legal Case Retrieval}, 153 | author={Shao, Yunqiu and Mao, Jiaxin and Liu, Yiqun and Ma, Weizhi and Satoh, Ken and Zhang, Min and Ma, Shaoping}, 154 | booktitle={Proceedings of the Twenty-Ninth International Joint Conference on Artificial Intelligence, IJCAI-20}, 155 | pages={3501--3507}, 156 | year={2020} 157 | } 158 | ``` 159 | -------------------------------------------------------------------------------- /model/nlp/AttenRNN.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'yshao' 3 | 4 | import torch 5 | import torch.nn as nn 6 | import torch.nn.functional as F 7 | 8 | from tools.accuracy_init import init_accuracy_function 9 | 10 | 11 | class Attention(nn.Module): 12 | def __init__(self, config): 13 | super(Attention, self).__init__() 14 | pass 15 | 16 | def forward(self, feature, hidden): 17 | # hidden: B * M * H, feature: B * H * 1 18 | ratio = torch.bmm(hidden, feature) 19 | # ratio: B * M * 1 20 | ratio = ratio.view(ratio.size(0), ratio.size(1)) 21 | ratio = F.softmax(ratio, dim=1).unsqueeze(2) 22 | # result: B * H 23 | result = torch.bmm(hidden.permute(0, 2, 1), ratio) 24 | result = result.view(result.size(0), -1) 25 | return result 26 | 27 | 28 | class AttentionRNN(nn.Module): 29 | def __init__(self, config, gpu_list, *args, **params): 30 | super(AttentionRNN, self).__init__() 31 | 32 | self.input_dim = 768 33 | self.hidden_dim = config.getint('model', 'hidden_dim') 34 | self.dropout_rnn = config.getfloat('model', 'dropout_rnn') 35 | self.dropout_fc = config.getfloat('model', 'dropout_fc') 36 | self.bidirectional = config.getboolean('model', 'bidirectional') 37 | if self.bidirectional: 38 | self.direction = 2 39 | else: 40 | self.direction = 1 41 | self.num_layers = config.getint("model", 'num_layers') 42 | self.output_dim = config.getint("model", "output_dim") 43 | self.max_para_q = config.getint('model', 'max_para_q') 44 | 45 | if config.get('model', 'rnn') == 'lstm': 46 | self.rnn = nn.LSTM(self.input_dim, self.hidden_dim, batch_first=True, num_layers=self.num_layers, 47 | bidirectional=self.bidirectional, dropout=self.dropout_rnn) 48 | else: 49 | self.rnn = nn.GRU(self.input_dim, self.hidden_dim, batch_first=True, num_layers=self.num_layers, 50 | bidirectional=self.bidirectional, dropout=self.dropout_rnn) 51 | 52 | self.max_pool = nn.MaxPool1d(kernel_size=self.max_para_q) 53 | self.fc_a = nn.Linear(self.hidden_dim*self.direction, self.hidden_dim*self.direction) 54 | self.attention = Attention(config) 55 | self.fc_f = nn.Linear(self.hidden_dim*self.direction, self.output_dim) 56 | # self.soft_max = nn.Softmax(dim=1) 57 | self.dropout = nn.Dropout(self.dropout_fc) 58 | self.weight = self.init_weight(config, gpu_list) 59 | self.criterion = nn.CrossEntropyLoss(weight=self.weight) 60 | self.accuracy_function = init_accuracy_function(config, *args, **params) 61 | 62 | def init_weight(self, config, gpu_list): 63 | try: 64 | label_weight = config.getfloat('model', 'label_weight') 65 | except Exception: 66 | return None 67 | weight_lst = torch.ones(self.output_dim) 68 | weight_lst[-1] = label_weight 69 | if torch.cuda.is_available() and len(gpu_list) > 0: 70 | weight_lst = weight_lst.cuda() 71 | return weight_lst 72 | 73 | def init_hidden(self, config, batch_size, gpu_list): 74 | if torch.cuda.is_available() and len(gpu_list) > 0: 75 | if config.get('model', 'rnn') == 'lstm': 76 | self.hidden = ( 77 | torch.autograd.Variable( 78 | torch.zeros((self.direction * self.num_layers, batch_size, 79 | self.hidden_dim)).cuda()), 80 | torch.autograd.Variable( 81 | torch.zeros((self.direction * self.num_layers, batch_size, 82 | self.hidden_dim)).cuda()) 83 | ) 84 | else: 85 | self.hidden = ( 86 | torch.autograd.Variable( 87 | torch.zeros((self.direction * self.num_layers, batch_size, 88 | self.hidden_dim)).cuda()) 89 | ) 90 | else: 91 | if config.get('model', 'rnn') == 'lstm': 92 | self.hidden = ( 93 | torch.autograd.Variable( 94 | torch.zeros((self.direction * self.num_layers, batch_size, 95 | self.hidden_dim))), 96 | torch.autograd.Variable( 97 | torch.zeros((self.direction * self.num_layers, batch_size, 98 | self.hidden_dim))) 99 | ) 100 | else: 101 | self.hidden = ( 102 | torch.autograd.Variable( 103 | torch.zeros((self.direction * self.num_layers, batch_size, 104 | self.hidden_dim))) 105 | ) 106 | 107 | def init_multi_gpu(self, device, config, *args, **params): 108 | self.rnn = nn.DataParallel(self.rnn, device_ids=device) 109 | self.max_pool = nn.DataParallel(self.max_pool, device_ids=device) 110 | self.fc_a = nn.DataParallel(self.fc_a, device_ids=device) 111 | self.attention = nn.DataParallel(self.attention, device_ids=device) 112 | self.fc_f = nn.DataParallel(self.fc_f, device_ids=device) 113 | # self.soft_max = nn.DataParallel(self.soft_max, device_ids=device) 114 | 115 | def forward(self, data, config, gpu_list, acc_result, mode): 116 | x = data['input'] # B * M * I 117 | batch_size = x.size()[0] 118 | self.init_hidden(config, batch_size, gpu_list) # 2 * B * H 119 | 120 | rnn_out, self.hidden = self.rnn(x, self.hidden) # rnn_out: B * M * 2H, hidden: 2 * B * H 121 | tmp_rnn = rnn_out.permute(0, 2, 1) # B * 2H * M 122 | 123 | feature = self.max_pool(tmp_rnn) # B * 2H * 1 124 | feature = feature.squeeze(2) # B * 2H 125 | feature = self.fc_a(feature) # B * 2H 126 | feature = feature.unsqueeze(2) # B * 2H * 1 127 | 128 | atten_out = self.attention(feature, rnn_out) # B * (2H) 129 | atten_out = self.dropout(atten_out) 130 | y = self.fc_f(atten_out) 131 | # y = self.soft_max(y) 132 | y = y.view(y.size()[0], -1) 133 | 134 | if 'label' in data.keys(): 135 | label = data['label'] 136 | loss = self.criterion(y, label.view(-1)) 137 | acc_result = self.accuracy_function(y, label, config, acc_result) 138 | if mode == 'valid': 139 | output = [] 140 | y = y.cpu().detach().numpy().tolist() 141 | for i, guid in enumerate(data['guid']): 142 | output.append([guid, label[i], y[i]]) 143 | return {"loss": loss, "acc_result": acc_result, "output": output} 144 | elif mode == 'test': 145 | output = [] 146 | y = y.cpu().detach().numpy().tolist() 147 | for i, guid in enumerate(data['guid']): 148 | output.append([guid, y[i]]) 149 | return {"output": output} 150 | return {"loss": loss, "acc_result": acc_result} 151 | else: 152 | output = [] 153 | y = y.cpu().detach().numpy().tolist() 154 | for i, guid in enumerate(data['guid']): 155 | output.append([guid, y[i]]) 156 | return {"output": output} 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /examples/task1/case_para_sample.json: -------------------------------------------------------------------------------- 1 | {"guid": "005_001", "q_paras": ["Strickland, J. : This is an application for judicial review of two decisions of the Canadian Human Rights Commission (the Commission), both dated August 27, 2014, declining to deal with the Applicant's complaints, pursuant to s 41(1)(a) of the Canadian Human Rights Act , RSC 1985, c H-6 ( CHR Act ), because the Applicant had not exhausted grievance or review procedures otherwise reasonably available to him. The application for judicial review is brought pursuant to s 18.1 of the Federal Courts Act , RSC 1985, c F-7. Background", "The Applicant is a member of the Canadian Forces (CF), the Respondent herein as represented by the Attorney General of Canada. He alleges that in 2010, while he was serving in Haiti, his immediate superior officer discriminated against him, largely on racial grounds. In June 2010, the Applicant filed a complaint with the CF Ombudsman who decided not to deal with the case because other avenues of redress had not yet been exhausted.", "In August 2010 the Applicant met with his Commanding Officer (CO) and the harassment complaint was discussed. His CO subsequently wrote to him on two occasions in September 2010, requesting additional information so as to determine whether the Applicant's allegations constituted a harassment complaint pursuant to the Harassment Prevention and Resolution Guidelines . No response was received from the Applicant.", "By letter of October 5, 2010 counsel for the Applicant advised the CO, amongst other things, that the Applicant did not intend to avail himself of a harassment complaint pursuant to the Department of Nation Defence/CF Harassment Prevention and Resolution policy but reserved his right to make any such complaints in another forum.", "In December 2010 the Applicant filed a complaint with the Commission. By decisions dated July 6, 2011 the Commission decided not to deal with the complaint at that time, pursuant to s 41(1)(a) of the CHR Act , as the Applicant ought first to exhaust grievance or review procedures otherwise reasonably available to him. It stated that at the end of the grievance or review process, the Applicant could ask the Commission to reactivate the complaint. The Commission's decisions of July 6, 2011 are not under review.", "The Applicant then engaged in the CF Redress of Grievance process. On November 29, 2011 the Commander of Canadian Forces Joint Support Group released terms of reference, thereby convening an administrative investigation into the Applicant's allegations of racist conduct, discrimination and harassment. The Administrative Investigation report of the Initial Authority (IA) was released on April 20, 2012. The Applicant was dissatisfied with the IA decision and requested that his grievance be forwarded for determination by the Final Authority (FA).", "On July 4, 2012 the Applicant's grievance was referred to the Canadian Forces Grievance Board (CFGB) which released its findings and recommendations on September 14, 2012. By letter of March 4, 2013, the Chief of the Defence Staff advised that he concurred with the CFGB findings and recommendations, which included that there were important evidentiary omissions and procedural errors that compromised the integrity of the process and resulted in a fatally flawed Administrative Investigation and, therefore, an equally flawed IA decision. The Chief of the Defence Staff also stated that he was concerned that the Applicant had chosen not to avail himself of the CF harassment process and that this had contributed to the CFGB's findings. Given his finding that the IA decision was invalid, the Chief of the Defence Staff concluded that he could not make a properly informed decision on the validity of the allegations and, therefore, that it was imperative that the Applicant initiate the harassment investigation process. The Applicant was given 90 days to do so. The Chief of the Defence Staff stated that when the investigation was complete the Applicant could grieve its outcome if its conclusions and recommendations were not to his satisfaction.", "On May 14, 2013 the Applicant submitted a copy of his original grievance, not a harassment complaint. The Responsible Officer (RO) wrote to the Applicant on May 22, 2013, requesting submission of a proper complaint and inclusion of any information necessary to enable the conduct of a thorough and complete investigation of the allegations of harassment. The RO sent a second letter on July 19, 2013. The Applicant did not respond to either letter.", "On December 24, 2012, while the grievance process was in progress, the Applicant requested the Commission to reactivate his complaint. While the Commission was considering whether s 41(1)(a) applied to the complaint, the FA decision was issued. The Applicant filed an application for judicial review of the FA on April 24, 2012. A November 4, 2013 Section 40/41 Report recommended that the Commission not deal with the complaint while the judicial review was before this Court. On January 23, 2014, before the Commission decided that matter, the Applicant discontinued his application for judicial review of the FA and asked the Commission to deal with his complaint.", "On May 6, 2014 two Section 40/41 Reports were prepared. The first concerned the Applicant's complaint against the CF (file no 20101251) and the second concerned his complaint against his CO (file no 20101252) (collectively, the Section 40/41 Report). Substantially similar Records of Decision Under Section 40/41 were issued by the Commission on August 27, 2014, for both matters (collectively, the Decision). For the purposes of this judicial review, all references shall be to the Section 40/41 Report and decision of the Commission pertaining to the complaint against the CF.", "On September 26, 2014 the Applicant filed his application for judicial review of the Commission's Decision. Decision Under Review", "The Commission decided not to deal with the complaint pursuant to s 41(1)(a) of the CHR Act , as the Applicant ought to have exhausted grievance or review procedures otherwise reasonably available to him. A decision under s 41(1)(d) of the CHR Act was, therefore, unnecessary. The Commission adopted the conclusion of the May 6, 2014 Section 40/41 Report: The complainant's allegations are serious, and in the public interest. On the other hand, the harassment complaint process was reasonably available to the complainant after the Final Authority decision in 2013. The Final Authority decision included measures to be taken as part of the harassment complaint process in order to address the concerns with timeliness and fairness from the preceding administrative investigation. The complainant was provided multiple opportunities to participate in this harassment complaint process, but he chose not to. He was clearly aware of the inadequacies of the grievance form to initiate a harassment complaint as several attempts were made to obtain the correct information from him. Based on the above, the failure to exhaust that process was attributable to him.", "In rendering its decision the Commission reviewed the December 8, 2010 complaint form, the Section 40/41 Report of May 6, 2014 and Supplemental Section 40/41 Report of June 12, 2014, the Applicant's submissions of June 17, 2014, the CF's submissions of July 2, 2014, and cross-disclosure submissions of the CF dated July 25, 2014.", "The Section 40/41 Report set out the background to the complaint and summarized the positions of the parties based on their submissions. It noted that, based on the CF's position, it appeared that the remaining alternative redress process, the CF's harassment complaint process, was no longer available to the Applicant because he did not file a formal harassment complaint prior to the deadline set out in the FA. The question to be answered was whether the harassment complaint process was reasonably available to the Applicant and, if so, whether he should have exhausted that procedure. And, if that process ought to have been exhausted, whether the failure to do so was solely attributable to the Applicant in accordance with s 42(2) of the CHR Act .", "On the first point, it is expedient to reproduce particular paragraphs of the report as it sets out relevance background information as well as its analysis: Was the harassment complaint process reasonably available to the complainant? 64. Some relevant background information can be found in the \"Findings and Recommendations\" of the Canadian Forces Grievance Board (\"CFGB\"), dated September 14, 2012, which was provided by the complainant. In August 2011, after the Commission's decision not to deal with his complaint at that time, the complainant filed a grievance with the respondent. In March 2012, a final report on the Administrative investigation of the complainant's grievance was issued and disclosed to the grievor who provided comments. On April 20, 2012, the Initial Authority decision was rendered, finding \"professional shortcomings\" on the part of the superior officer but not granting the grievor's requests for apologies, damages, or legal fees. The complainant was unsatisfied with this decision and asked for a decision by the Final Authority. The matter was referred to the CFGB for review and recommendations. 65. The CFGB found that because a harassment investigation did not occur, the Initial Authority did not have adequate information upon which to make his decision, even though he attempted to insert elements of a harassment investigation into the grievance process. This investigation was found to have various defects, such as not all witnesses were interviewed, not all relevant information appeared to be considered, and the fact that the superior officer, alleged to be the harasser, was not provided an opportunity to respond to the investigation. For these reasons, the CFGB recommended that the Final Authority not make a final decision on the matter, but order a new harassment investigation be initiated. It also noted that the respondent's policies should be amended to ensure that harassment complaints are required to be dealt with under the harassment policy prior to resorting to the grievance process, given that neither the CFGB nor the CDS is equipped to conduct its own investigation in such situations. 66. On March 4, 2013, the Final Authority rendered his decision. Referring to the CFGB findings and recommendations, the CDS found that there were \" important evidentiary omissions and procedural errors that compromised the integrity of the process and resulted in what the CFGB quite rightly referred to as a 'fatally flawed' administrative investigation and consequently led to an equally flawed IA decision based solely on that investigation.\" The CDS determined that due to these errors and the incompleteness of the underlying investigation, he could not render \" ...a properly informed decision on the validity and reliability of the allegations... \" 67. The CDS also raised concerns that a harassment investigation did not occur. In the Final Authority decision, the CDS stated: Yet, I am troubled by your allegations and most particularly, by the lack of a proper investigation into their validity. As I mentioned, the CF takes this issue very seriously and consequently, it is imperative that you initiate the harassment investigation process by submitting a formal harassment complaint to the CO of 4 CFMCU. You have 90 days from the receipt of this letter to make your submission. The CO of 4 CFMCU shall, upon receipt of your harassment complaint, initiate a harassment investigation into the allegations therein. Let me be very clear on this point: an investigation will not commence until there is a formal harassment complaint submitted to the CO of 4 CFMCU and you commit, in writing, that you will fully participate in such an investigation to ensure that a thorough and impartial analysis may determine the legitimacy of your allegations. If you do not respond within 90 days, there will be no further action taken on your alleged complaints. Given the complexities of this case, compounded by the length of time that has elapsed since the alleged incidents, the CO of the 4 CFMCU might consider engaging a private consulting firm to assist with the investigation. Such corporations specialize in the investigation of harassment cases. Additionally, a monthly situation report will be sent to the Director General Canadian forces [ sic ] Grievance Authority (DGCFGA) Implementation Officer on the status of the forthcoming harassment investigation. When the investigation has been completed, you may grieve its outcome if the conclusions and recommendations are not to your satisfaction. 68. The CDS suggested that a private consulting firm be engaged to investigate the matter. The complainant is of the view that this demonstrates that the respondent is unable to investigate a complaint of this complexity. He suggests that this is further supported by the finding of the CFGB that the initial administrative investigation to be [ sic ] ' fatally flawed '. 69. The lack of procedural fairness in the IA decision was acknowledged in the Final Authority decision. The CDS attempted to address the concerns for procedural fairness and expediency with his recommendations: a) that the matter be dealt with through a formal harassment complaint; and, b) that they hire an outside firm to investigate the matter. 70. The respondent's \"Harassment Prevention and Resolution Guidelines\" (the Guidelines) outline a complaint process which is overseen by Responsible Officers (ROs) in the respondent's organization. The Guidelines define harassment the same as it is defined under the Act. The Guidelines further provide that other workplace harassment may also be addressed through this process. Once a formal harassment complaint is received, the RO oversees a situational assessment to determine whether or not the complaint is related to harassment - if it is, efforts are made to resolve the matter, identify other possible redress avenues such as mediation, and the appropriate action(s) will be initiated. If mediation is not appropriate, an administrative investigation is to be undertaken by a harassment investigator. The harassment investigator prepares a draft report on which both parties may comment before the final report is completed and submitted to the RO for decision. If any person is not in agreement with the RO's decision, they may file a grievance. Although the harassment and grievance processes do not provide for an independent third party decision-maker, matters dealing with harassment and racist conduct are referred to an independent body (the CFGB, now the MGERC) for findings and recommendation prior to the Final Authority decision being rendered. In addition, a review by the Federal Court is also available. 71. The complainant states that only the Commission will be able to fully deal with all of the matters in his complaint. While the complaint deals with harassment, there are also allegations of adverse differential treatment within the workplace. If the harassment complaint process were still available to him, it is not clear whether it could deal with the allegations with respect to the transfer occurring as a result of his marital status, for example. However, it could have dealt with much [ sic ] of the substantive allegations in the complaint. 72. Timeliness is another consideration with respect to this complaint, which is nearly four years old. It appears that both parties bear some responsibility for the delays in the process. In 2010, the complainant did attempt to speak to his superiors about his allegations, but he did not provide them with the requested information when asked in September 2010. As outlined in paragraph 64, the respondent's process is lengthy, and there appears [ sic ] to have been delays in the processing of the complainant's grievance. 73. In terms of delay, it is not clear that the Commission's process would be more expedient than that of the respondent's internal harassment complaint process, if it were still available. Nonetheless, it appears at this stage, the Commission's process is the only remaining process available to him. 74. The complainant's human rights allegations, if proven, are serious; this is acknowledged by the respondent. The complainant has not provided any information to indicate that he is vulnerable personally, but there is a public interest in investigating allegations of racism in Canadian government institutions such as the Canadian Forces. Although the complainant describes the complaint as one of systemic discrimination, no policy or practice has been identified in the summary of complaint. There is no indication that there is or was any potential for harm to anyone if the respondent's harassment complaint process had been used. 75. The complainant suggests that that [ sic ] requiring him to engage in the harassment complaint process is unfair. Although the complainant's scepticism may be understandable given the results of the IA decision, there is no indication that the respondent's alternate redress procedures overall are so flawed that it could not have dealt with the complainant's allegations within its harassment complaint process. In this instance, the Final Authority made recommendations in order to specifically address the issues that occurred previously, including any potential unfairness and the issue of delay. 76. Having regard for all of the above, it appears that the complainant had another review procedure, the harassment complaint process, reasonably available to him after the Final Authority decision in 2013.", "On the question of whether the failure to exhaust the CF's harassment complaint process was attributable to the Applicant and not another, as set out in s 42(2) of the CHR Act , the Section 40/41 Report noted that the history of the complaint illustrated that much of the discord related to the appropriate process for addressing a complaint that raises a variety of issues such as was the case in this matter, which involved allegations of harassment, abuse of authority and adverse differential treatment.", "Ultimately, however, it concluded that: 80. The complainant acknowledged the internal harassment complaint process. After the Final Authority decision, the complainant sent in his grievance form in lieu of a harassment complaint. In his letter to the complainant dated May 22, 2013, the RO responded to the complainant to explain that the grievance form did not provide the information necessary to initiate a harassment complaint. The letter described what was required in order to file a harassment complaint and also offered assistance to the complainant. On July 19, 2013, the RO sent a follow-up letter to the complainant requesting additional information, but the complainant did not provide it. Therefore, while he acknowledged this process, he refused to participate by not providing the appropriate material in order for it to have any possibility of success. This is solely attributable to the complainant. Relevant Legislation", "The sole issue in this application for judicial review is whether the Commission's interpretation and application of s 41(1)(a) were reasonable.", "Jurisprudence has previously held that the appropriate standard of review with respect to decisions of the Commission not to deal with a complaint pursuant to s 41(1) of the CHR Act is reasonableness ( FRAGMENT_SUPPRESSED).", "Reasonableness is concerned with the existence of justification, transparency and intelligibility within the decision-making process. It is also concerned with whether the decision falls within a range of possible, acceptable outcomes which are defensible in respect of the facts and the law ( FRAGMENT_SUPPRESSED). Preliminary Issue", "A preliminary issue that has been raised by the Respondent is the admissibility of the affidavit of Nathalie Bui sworn October 24, 2014 (the Bui Affidavit). It attaches what the affiant describes as the entire record of official correspondence between the Commission and the Applicant concerning files 20101251 and 20101252.", "As noted by the Respondent, this is the Applicant's second attempt to place this material before the Court. The Applicant previously brought a motion, pursuant to Rule 317 of the Federal Courts Rules , SOR/98-106 [ FC Rules ], seeking to have the Commission's entire file produced as part of the Certified Tribunal Record (CTR).", "By Order dated November 26, 2014, Prothonotary Tabib refused that request on the basis that the Applicant was not entitled to further production, noting, in particular, that the Applicant had not identified any document that might have been considered by the Commission, or might be relevant to the determining of the issues before the Court, and which had not been included in the CTR.", "Therefore, in my view, this Court has already ruled on this issue. The conduct of this judicial review on the merits of the Applicant's claim does not serve as an appeal of the Prothonotary's decision. An appeal of a prothonotary's decision must be brought by motion to the Federal Court ( FC Rules , R 51(1), the Applicant did not bring such a motion and the time within which he was required to do so has elapsed. On that basis alone the Bui Affidavit is inadmissible and submissions made in reliance on it or in reference to it are to be disregarded.", "In any event, this Court has also previously held that material that was not before the decision-maker should generally be excluded for purposes of judicial review ( FRAGMENT_SUPPRESSED). And in this case, the Bui Affidavit does not fall under any of the recognized exceptions to this rule ( FRAGMENT_SUPPRESSED).", "The Commission listed the documentation that was reviewed when making its decision, which was contained in the CTR. Accordingly, the additional material contained in the Bui Affidavit is not relevant to the issue before this Court, being whether the Commission's Decision was reasonable based on the record that was before it.", "Further, the May 6, 2014 Section 40/41 Report specifically acknowledges that the parties had raised facts in their submissions that had occurred prior to the original Section 40/41 Report of July 6, 2011. The May 6, 2014 Section 40/41 Report stated that, because the Commission had already dealt with the alternative redress procedures available to the Applicant prior to July 2011, its current analysis would be based only on information arising after the Commission's original decision (para 61). As seen from the analysis contained in the May 6, 2014 Section 40/41 Report, prior facts which were set out in the parties' submissions do not constitute a part of the analysis.", "For all of these reasons, it is my view that the Bui Affidavit is inadmissible. Accordingly, the Applicant's submissions made in reliance on or in reference to that affidavit have not been considered in this judicial review. Position of the Parties", "The Applicant's position is that he has spent years attempting to satisfy the CF grievance system and acted in good faith including through a CF led biased and fatally flawed investigation and ensuing administrative investigation report. He characterises as cyclical, absurd and unreasonable the suggestion that to satisfy all reasonable steps he should now engage the harassment procedure and, if unsatisfied, pursue a new grievance, potentially all the way to judicial review. That process would create an endless procedural loop that would prevent the Applicant from exercising his right to complain under the CHR Act . Accordingly, the Commission unreasonably concluded that the CF's internal process was reasonably available to him.", "The Applicant further submits that the Commission ought to have accepted his complaint. The CF grievance process was exhausted because the Chief of the Defence Staff failed to make a decision on the grievance and instead passed it off, requiring the harassment procedures to be initiated. Further, the CF has demonstrated an unwillingness and an inability to handle this complex matter.", "The Respondent submits that a review of the reasonableness of the Commission's Decision should include an examination of some or all of the relevant factors ( FRAGMENT_SUPPRESSED), including any delay attributable to the Applicant. It should not include the Applicant's subjective and unsubstantiated view that the harassment process is not reasonably available and inadequate because the Applicant has never engaged the process.", "The Respondent further submits that the Commission's Decision that the Applicant ought to have used the appropriate CF internal process, which was reasonably available, falls within a range of possible acceptable outcomes. Any circularity in the process, as alleged by the Applicant, is purely attributable to his own refusal to utilize the process that the CF, the FA and the Commission all found to be the appropriate avenue for redress. Analysis", "In this matter the Commission adopted the recommendations made in the Section 40/41 Report, which, in that circumstance, constitute the reasons of the Commission ( FRAGMENT_SUPPRESSED). The Applicant takes no issue with the Commission's form of response.", "The Section 40/41 Report stated that s 41(1)(a) and s 42 together mean that the Commission can decide not to deal with a complaint under s 41(1)(a) if it finds that the complainant chose not to finish another process that was reasonably available to him or her. This is not an unreasonable interpretation of these provisions. In FRAGMENT_SUPPRESSED", "The Section 40/41 Report then listed factors that may be considered when deciding under s 41(1)(a) whether or not a complainant should keep using the other complaint or grievance process. These were: why the parties did not finish the other process; how much longer that process was likely to take; whether there is new information in that regard; and whether there is anything other than the amount of time the other process may take, such as vulnerability arising from the complainant's current situation or a risk of harm to any participant, that makes it not \"reasonably available\" to the complainant such that the Commission should deal with the complaint. The Section 40/41 Report also summarized the CF's submissions of February 20, 2014, and the Applicant's submissions of February 21, 2014 and April 10, 2013.", "The question of whether the harassment complaint process was reasonably available to the Applicant is largely a factual question. In my view, there is no question that it was available. The only question being was it reasonably so.", "In July 2011 the Commission decided not to deal with the complaint at that time under s 41(1)(a); instead, it asked the Applicant to use another complaint or grievance process first. At the end of that process the complainant could come back to the Commission and ask it to reactivate the complaint.", "Given this, the Applicant then filed a grievance pursuant to s 29 of the National Defence Act , RSC 1985, c N-5. The process thus engaged is set out in the Queen's Regulations and Orders for the Canadian Forces (QROCF) (online: ). This envisions consideration and determination of the grievance by the IA (s 7.15). If the complainant is not satisfied with the IA decision, he or she may request that the matter be referred to the FA (s 7.18). The FA for all grievances is the Chief of the Defence Staff ( National Defence Act , s 29) or his delegate (QROCF, s 7.17). Pursuant to s 29.12 of the National Defence Act, the Chief of the Defence Staff shall refer every grievance that is of a type prescribed by regulation to the Grievances Committee for its findings and recommendations before the Chief of the Defence Staff considers and determines the grievance. These types of grievances are set out in s 7.21 of the QROCF. They include any decision of the Chief of the Defence Staff in respect of a particular officer or non-commissioned officer.", "In March 2012 the IA decision was issued. As the Applicant was unsatisfied with that decision, in May 2012 he sought an FA decision. As seen from the March 4, 2013 letter from the Chief of the Defence Staff, the grievance was submitted to the CFGB which conducted an independent review of the grievance and provided findings and recommendations which were disclosed to the Applicant.", "The Chief of the Defence Staff stated that the CFGB had analysed each of the contentions in the grievance file and found that the administrative investigation on which the IA had based its decision was \"fatally flawed\" on several levels.", "He also stated: The CF's policies on racist conduct and on harassment prevention and resolution are outlined in Canadian Forces Administrative Order 19-43 (Racist Conduct) and Defence Administrative Order and Directive (DAOD) 5012-0 (Harassment Prevention and Resolution). Both are unequivocal in their condemnation of any conduct that causes offence or harm to any person through acts, comments, or displays that demean, belittle, and cause personal humiliation or embarrassment, and any act of intimidation or threat. As much as we in the CF would like to think that we are immune from such behaviours, as a microcosm of the society that we serve, we would be very naïve to think that we, as an organization, would not be subject to instances of this insidious conduct. Accordingly, the CF has a comprehensive framework to deal with such instances in the form of the Harassment Prevention and Resolution Guidelines . The CF policy and guidelines flow directly from and are consistent with the Treasury Board's Policy on Harassment Prevention and Resolution in the Workplace and go beyond the requirements of the Canadian Human Rights Act in their scope.", "The Chief of the Defence Staff expressed concern that the Applicant had chosen not to avail himself of the CF harassment process. His lack of cooperation by failing to fulfil the obligation to provide full written details of his complaint was an impediment to the fair, impartial and expedient harassment investigation and contributed to the \"procedural morass\" that followed. The Chief of the Defence Staff went on to say that there had been important evidentiary omissions and procedural errors that compromised the integrity of the administrative investigation process resulting in what the CFGB had rightly referred to as a fatally flawed administrative investigation; consequently, the IA decision that was based on that investigation was equally flawed.", "Although the Chief of the Defence Staff had identified his role as being to determine whether the harassment occurred and whether the Applicant was a victim of mistreatment at the hands of a superior officer, he concluded that in light of the CFGB findings, he could not render a properly informed decision. He therefore decided to order a new harassment investigation.", "The Chief of the Defence Staff's reference to a lack of clarity as to the order in which the CF harassment and grievance processes are to be utilized is of note. He stated that the CFGB had highlighted that there was lack of clear policy direction regarding the relationship between the harassment and grievance processes. DAOD 2017-1 states: The following are situations in which other complaint and mechanisms should be used prior to proceeding with a grievance: • a complaint of harassment or abuse of authority", "While s 4.10 (Coincidental Complaints and Grievances) of the Harassment Prevention and Resolution Guidelines states: If an individual decides to file a grievance on the same issue as a harassment complaint, the applicable grievance mechanism will apply and the harassment complaint will be closed.", "The Chief of the Defence Staff stated that determination of misconduct, as it pertains to harassment or acts of racism, must be ascertained through a procedurally fair and impartial investigation process, such as that outlined in the Harassment Prevention and Resolution Guidelines . The grievance process should be engaged only if the complainant feels they have been wronged as a result of the outcome of the investigation process or if there is an excessive and unjustifiable delay in handling the complaint.", "The Chief of Military Personnel (CMP), in consultation with the Assistant Deputy Minister Human Resources - Civilian, \"will harmonize the Harassment Prevention and Resolution Guidelines with DAOD 2017-1 so that it is very clear that harassment complaints or grievances containing allegations of harassment must be dealt with first under the harassment policy and only after a proper harassment investigation has been completed\".", "The Chief of the Defence Staff decided that:", "am not prepared to grant the redress that you seek. Yet, I am troubled by your allegations and most particularly, by the lack of a proper investigation into their validity. As I mentioned, the CF takes this issue very seriously and consequently, it is imperative that you initiate the harassment investigation process by submitting a formal harassment complaint to the CO of 4 CFMCU. You have 90 days from the receipt of this letter to make your submission. The CO of 4 CFMCU shall, upon receipt of your harassment complaint, initiate a harassment investigation into the allegations therein. Let me be very clear on this point: an investigation will not commence until there is a formal harassment complaint submitted to the CO of 4 CFMCU and you commit, in writing, that you will fully participate in such an investigation to ensure that a thorough and impartial analysis may determine the legitimacy of your allegations. If you do not respond within 90 days, there will be no further action taken on your alleged complaints. Given the complexities of this case, compounded by the length of time that has elapsed since the alleged incidents, the CO of 4 CFMCU might consider engaging a private consulting firm to assist with the investigation ... When the investigation has been completed, you may grieve its outcome if the conclusions and recommendations are not to your satisfaction.", "In my view, regardless of the factual background prior to the Chief of the Defence Staff's decision and regardless of the lack of clarity as to the order in which harassment complaints or grievances containing allegations of harassment were to be dealt with and any delay or confusion that this may have previously caused, as of March 13, 2013, the Applicant was on clear notice of what was required of him, and that was that he initiate a harassment complaint within 90 days. He was also aware that if he failed to do so, there would be no further action taken with respect to the allegations.", "The CF's submission, as summarized by the Section 40/41 Report, stated that on May 14, 2013 it received from the Applicant a photocopy of his original grievance, not a formal harassment complaint as required by the Chief of the Defence Staff. On May 22, 2013, the RO sent the Applicant a letter requesting that he redraft his submission, explain each allegation specifically and provide any information that would enable the harassment investigation to be thorough and complete. The RO also notified the Applicant's current CO in order to ensure that an assistant could be appointed for the Applicant to help him with the process. The Applicant did not respond to the RO's request. The RO sent another letter to the Applicant on July 19, 2013 requesting an update on the preparation of this harassment complaint but again did not receive a response.", "The Section 40/41 Report noted the other factors that were considered in reaching its recommendation, including the attempt of the Chief of the Defence Staff to address the procedural fairness and other concerns by having the matter dealt with by a formal harassment complaint, with an outside firm being hired to investigate. Further, as to timeliness, that both parties bore some responsibility for the prior delays in the process and that it was not clear that the Commission's process would be more expedient than the CF's harassment policy, if it were still available to the Applicant. However, at this stage, the Commission's process was the only one still available to the Applicant. Further, that the Applicant had provided no information to indicate that he was personally vulnerable.", "The Applicant's position is that the harassment complaint process as proposed by the Chief of the Defence Staff cannot be considered to be reasonably available. The Applicant submits that there has already been a considerable delay, that he commenced the grievance process and that, at the end of that process, rather than rendering a decision, the Chief of the Defence Staff instead attempted to effect a harassment investigation. This is \"cyclical\" because he was required to commence the grievance process, which now requires a harassment investigation, which in turn can be grieved, and that this has the potential of becoming an endless loop.", "In my view, this argument cannot succeed. Although the CFGB's report was not in the CTR, it is clear from the Chief of the Defence Staff's letter that there was a lack of clarity regarding the relationship between the harassment and grievance processes. This may well have contributed to the prior delay and procedural missteps in this matter. And, unless the relationship is clarified, it may have the same impact on other matters in the future.", "Regardless, in this case, the decision by the Chief of the Defence Staff had the effect of resetting the clock. While it is true that a grievance from the conclusions and recommendations of the harassment investigation would have been available to the Applicant, this did not mean that an endless procedural circle would follow. The problem identified in the original grievance process initiated by the Applicant was that there was insufficient information to deal with the harassment complaint. This led to the Chief of the Defence Staff's decision that a harassment investigation was required. Had that been carried out, any further grievance process then open to the Applicant would have been based on the findings of the harassment investigation, not the absence of one, and would not have resulted in a decision that a harassment investigation was required. Thus, this does not result in an endless cycle, nor does it establish that the process proposed by the Chief of the Defence Staff was not \"reasonably available\" to the Applicant.", "Further, the original grievance process would have been exhausted if the Applicant had participated in the proposed harassment investigation. The Applicant does not submit that it was beyond the authority of the Chief of the Defence Staff to proceed as he did, by requiring the harassment investigation. In addition, he discontinued his judicial review of the FA.", "also do not agree with the Applicant's submission that the Chief of the Defence Staff admitted that the CF is unable or unwilling to fully and objectively determine a complaint of racial discrimination when he stated the CO \"might consider engaging a private consulting firm to assist the investigation\" in his letter given the complexity of the case, compounded by the length of time that had elapsed since the alleged incidents. A suggestion that an independent third party with experience in the investigation of harassment complaints could be utilized, if anything, would seem to suggest the incorporation of an additional measure to ensure procedural fairness in the process.", "The Applicant also submits that the Chief of the Defence Staff's statement quoted at paragraph 42 above is an acknowledgment that racism exists within the CF, at least to some degree, and that his inaction to combat racism may mean that it will only continue to exist. However, read in whole, the statement clearly expresses that the CF Harassment Prevention Resolution Guidelines are intended to deal with any instance of racism and harassment.", "As to systemic discrimination, the Section 40/41 Report stated that although the Applicant described the complaint as one of systemic discrimination, no policy or practice had been identified in the summary of complaint. A review of the complaint confirms that the complaint clearly pertained to the Applicant's CO and made no allegation of systemic discrimination. At the hearing before me, the Applicant confirmed that this was the case and that systemic discrimination was therefore not pursued as an issue in this matter.", "In summary, the Commission considered the factual basis of this matter and found that there was a review procedure that was reasonably available to the Applicant and that had not been exhausted by the Applicant prior to the expiry of the 90 day deadline set by the Chief of the Defence Staff. Based on the record, this finding was reasonably open to it.", "The Commission also recognised that this finding would leave the Applicant without recourse. However, as noted by the Respondent, this Court has held that the reasonableness of a decision is not affected by the absence of the \"safety net\" of renewed access to the Commission ( FRAGMENT_SUPPRESSED). In this case, while the CF review process itself is no longer available to the Applicant, nor is there a safety net, the Commission recognized this outcome when making its decision.", "In the circumstance of this matter, the Commission's finding that the Applicant was aware of, but refused to participate in, the CF's internal harassment complaint process and that the failure to exhaust the procedure was solely attributable to the Applicant is to be afforded deference ( FRAGMENT_SUPPRESSED) and also fell within a range of possible acceptable outcomes defensible in respect of the facts and law ( FRAGMENT_SUPPRESSED).", "For these reasons the application is dismissed. The parties have agreed to costs in the amount of $2,500.00, accordingly, the Respondent shall have its costs in that amount. JUDGMENT", "THIS COURT'S JUDGMENT is that 1. The application for judicial review is dismissed. 2. The Respondent shall have its costs in the amount of $2,500.00. Application dismissed. Editor: Jana A. Andersen/nmg [End of document]"], "c_paras": ["Russell, J. : This is an application under s. 18.1 of the Federal Courts Act , RSC 1985, c F-7 for judicial review of a decision of the Canadian Human Rights Commission [Commission], dated May 7, 2014 [Decision], which decided not to deal with the Applicant's complaint pursuant to s. 41(1)(d) of the Canadian Human Rights Act , RSC 1985, c H-6 [Act].", "The Applicant started working as a Heavy Equipment Operator for Canadian National Railway Company [CN] in August 1981. He was a member of a bargaining unit represented by the National Automobile, Aerospace, Transportation and General Workers Union of Canada (CAW-Canada) [ Union].", "On December 19, 2012, the Applicant's employment was terminated because he refused to attend a medical assessment to determine his fitness for work. CN requested the medical assessment because the Applicant's position was a safety sensitive position and there were concerns regarding his behaviour. The Applicant says that he refused to attend the medical assessment because he was receiving treatment for drug dependency and believed that was sufficient to address CN's concerns.", "On January 8, 2013, the Union filed a grievance to contest the Applicant's termination.", "On February 14, 2013, the Union closed the Applicant's file. The Union said that the Applicant had not responded to their requests for information and it \"was not in a position to advance the matter with the limited information at hand.\"", "In March 2013, the Applicant submitted a complaint to the Commission. He alleged that CN had discriminated against him on the ground of disability by terminating his employment contrary to s. 7 of the Act. The Commission decided not to deal with the complaint pursuant to s. 41(1)(a) because the Applicant had not exhausted the Respondent's grievance process.", "On April 19, 2013, CN denied the Union's grievance. The Union did not refer the grievance to arbitration.", "On October 1, 2013, the Applicant submitted another complaint to the Commission. He alleged that, again, CN had discriminated against him on the ground of disability by terminating his employment contrary to s. 7 of the Act.", "On October 29, 2013, the Applicant was advised that the Commission would be preparing a s. 40/41 report to determine whether it should deal with his complaint. The Applicant was invited to prepare a letter stating his position on whether the Commission should not deal with the issues because \"the human rights issues in this complaint may have already been dealt with through another process.\" Counsel for the Applicant made submissions to the Commission both in advance of the preparation of the report and after being provided a copy of the s. 40/41 report [Report].", "On May 7, 2014, the Commission decided not to deal with the Applicant's complaint pursuant to s. 41(1)(d) of the Act. The Commission adopted the Report's conclusions and decided that the complaint was vexatious under s. 41(1)(d) of the Act (Applicant's Record at 48): The complainant's human rights allegations have been addressed by an alternate decision maker with authority to consider human rights issues. The allegations raised in the complaint before the Commission are the same as those addressed in the final level grievance response. Given that the alternate decision-maker dealt with the human rights issues raised in this complaint, and that process was fair, the Commission must respect the finality of that decision and should not deal with this complaint. It is therefore plain and obvious that this complaint is vexatious within the meaning of section 41(1)(d) of the Act.", "The Applicant raises the following issues in this application: 1. Whether the Commission unreasonably refused to exercise its jurisdiction; 2. Whether the Commission erred in law by: a) Unreasonably finding the Applicant's complaint to be vexatious; or, b) Having found the complaint to be vexatious, unreasonably ignoring that justice required it to deal with the complaint anyway; and, 3. Whether the Commission unreasonably based its decision on erroneous findings of fact made without regard to the material before it.", "The Supreme Court of Canada in Dunsmuir v New Brunswick , 2008 SCC 9 [ Dunsmuir ] held that a standard of review analysis need not be conducted in every instance. Instead, where the standard of review applicable to a particular question before the court is settled in a satisfactory manner by past jurisprudence, the reviewing court may adopt that standard of review. Only where this search proves fruitless, or where the relevant precedents appear to be inconsistent with new developments in the common law principles of judicial review, must the reviewing court undertake a consideration of the four factors comprising the standard of review analysis: Agraira v Canada (Public Safety and Emergency Preparedness) , 2013 SCC 36 at para 48.", "The Applicant submits that decisions under s. 41(1)(d) of the Human Rights Act are reviewed on a standard of reasonableness: Chan v Canada (Attorney General) , 2010 FC 1232 [ Chan ]. The Respondent submits that the Commission's decision not to deal with a complaint under s. 41 of the Human Rights Act is a discretionary decision reviewable on a standard of reasonableness: Exeter v Canada (Attorney General) , 2011 FC 86 at para 19, aff'd 2012 FCA 119 at para 6 [ Exeter ]; Morin v Canada (Attorney General) , 2007 FC 1355 at para 25, aff'd 2008 FCA 269.", "All of the issues question the reasonableness of the Commission's decision to not deal with the complaint. The Court agrees that these decisions are reviewable on a standard of reasonableness: Chan , above, at para 15; Exeter , above, at para 6.", "When reviewing a decision on the standard of reasonableness, the analysis will be concerned with \"the existence of justification, transparency and intelligibility within the decision-making process [and also with] whether the decision falls within a range of possible, acceptable outcomes which are defensible in respect of the facts and law\": see Dunsmuir , above, at para 47; Canada (Citizenship and Immigration) v Khosa , 2009 SCC 12 at para 59. Put another way, the Court should intervene only if the Decision was unreasonable in the sense that it falls outside the \"range of possible, acceptable outcomes which are defensible in respect of the facts and law.\"", "A. Applicant", "The Applicant submits that the Commission unreasonably refused to exercise its jurisdiction. The Applicant concedes that the Commission is entitled to adopt the Report for its reasons: Chan , above, at paras 39-40. However, the Applicant distinguishes the present proceeding from the Chan case on two grounds. First, the Commission's adoption of the Report was inadequate because it fails to show that the Commission considered the submissions before it and fails to recognize that the human rights issues were not considered in the grievance process: Vancouver International Airport Authority v Public Service Alliance of Canada , 2010 FCA 158. Second, the internal grievance process does not constitute a proper decision-maker. The grievance process is not an independent arbitrator and it failed to provide reasons for its decision on the human rights issues.", "The Applicant characterizes the grievance process as an internal negotiation between the Union and CN. If the Commission does not deal with the complaint then he says the Union's Decision to not refer the grievance to arbitration will have denied him the ability to have his human rights issue considered by a decision-maker. The Union's Decision to not proceed to arbitration was based, in part, on the Applicant's refusal to cooperate but the Applicant says that the nature of his disability carries the need for reasonable flexibility regarding deadlines and expectations. The Union's Decision was also based on other factors including time, money and resources.", "In the alternative, if the internal grievance process constituted a decision-maker, the Decision is unreasonable because the grievance did not address the human rights issues. It referenced \"drug dependency\" and concluded there was insufficient evidence.", "If the complaint was correctly deemed vexatious, then the Applicant submits that the Commission erred in law by ignoring that justice required the Commission to deal with the complaint anyway. The Decision says the internal grievance process was fair but fails to consider the Applicant's reply submissions.", "Finally, the Decision is unreasonable because it relies on the erroneous finding that the Applicant's human rights issues were already addressed by a decision-maker. B. Respondent", "The Respondent submits that it was reasonable for the Commission to refuse to deal with the complaint. The Report constitutes the reasons for the Decision: Canada (Attorney General) v Sketchley , 2005 FCA 404 at para 37 [ Sketchley ]; Bergeron v Canada (Attorney General) , 2013 FC 301 at paras 28-29 [ Bergeron ]. The Applicant had the opportunity to address the human rights issues through the Union but he failed to cooperate with the search for accommodation. The Commission may refuse to deal with a complaint if it is obvious that the complaint cannot succeed. A complainant who refuses to collaborate in the search for accommodation will have his or her complaint dismissed: Central Okanogan School District No 23 v Renaud , [1992] 2 SCR 970.", "The Commission's Decision to refuse to deal with the complaint is also in accordance with Supreme Court of Canada jurisprudence regarding the importance of permitting administrative tribunals to curb abuse of process: British Columbia (Workers' Compensation Board) v Figliola , 2011 SCC 52. It would be an abuse of process to advance a human rights complaint where the complainant has failed to cooperate with their union to have the same human rights issues addressed.", "Contrary to the Applicant's submissions, s. 41(1)(d) does not require that a decision be made by an arbitrator. The Commission is granted great latitude in exercising its discretion and assessing the appropriate factors in performing its screening function: Sketchley , above, at para 38; Bergeron , above, at para 39. Further, the Federal Court has held that s. 41(1)(d) may apply in situations where a union has decided not to pursue a grievance to arbitration: Bergeron , above, at para 38. There is also no evidence that those who decided the Applicant's grievances were not impartial: Bergeron , above, at para 43.", "The Report shows that the Investigator turned her mind to the outcome of the grievance process, the Applicant's allegations relating to substance abuse, and the question of reasonable accommodation. The Commission reasonably concluded that the allegations raised in the complaint had already been addressed in the grievance process and that the grievance process was fair.", "The Applicant raises three (3) grounds for reviewable error but, in the end, they all come back to the issue of the Applicant's own failure to cooperate in the grievance process. Essentially, the Commission came to the conclusion that the Applicant's complaint was vexatious under s. 41(1)(d) of the Act because the Applicant's human rights allegations had already been addressed by the grievance process.", "As the Report found, the Applicant's Union representative filed a grievance on his behalf that raised the same human rights issues as those in the complaint to the Commission. The Union had to close out its grievance file because the Applicant would not cooperate with its attempts at obtaining accommodation for him. The Union concluded that it could not advance the grievance to arbitration because, given Applicant's failure to cooperate in providing the information requested and required for the grievance process, there was insufficient information to advance the matter. In the end, the grievance process was exhausted without going to arbitration because the Applicant failed to cooperate. This was the final decision in the grievance process.", "The Applicant attempted to convince the Commission, and he has attempted to convince this Court, that his disability prevented him from providing the materials and cooperation required by the grievance process. In his submissions to the Commission, he alleged as follows (CTR at 14): Given the nature of [the Applicant's] disability, it follows that it would be logical for the Company to have been in closer contact with the Union in order to determine what the correct situation, and prognosis, for [the Applicant] was. It is understandable that an individual with a disability would encounter difficulty in navigating deadlines without reasonable assistance, and it was further understandable that [the Applicant] mistakenly believed the matter was being dealt with by his Union and his doctor.", "There was no evidence before the Commission, and there is none before me, to support this bare allegation that the Applicant had difficulties with deadlines and mistaken beliefs because of his disability. The Applicant simply expected the Commission, and now asks the Court, to draw an inference to this effect from the nature of his disability which is drug dependency. It is noticeable that, in the affidavit he has filed with this application, the Applicant says nothing about difficulties with deadlines and mistaken beliefs. Further, his evidence before me clearly indicates the considerable lengths to which the Union went to make clear to the Applicant what was required of him and to encourage him to comply. The letter of April 4, 2013 to the Applicant from Mr. Robert Fitzgerald, the Union's National Representative, sets out the whole picture: To date, none of this information has been provided. The Union only has two pieces of medical documentation. One dated January 24, 2013 stating that you will be seeing an addiction counsellor, but no confirmation that you did. The second one verified that you do not have any disability as it relates to psychiatric issues. Although your doctor invited us to follow up with him providing we had the necessary medical release to do so, you failed to return the release form that the Regional Representative provided to you, enclosed with his letter of February 01, 2013. There have been literally hundreds of phone calls between you and the Union at different levels. However, you have not acknowledged the Union's request for information. You have abated the Union's request in must the same way you have declined to cooperate with the Company. In our opinion, the negative connotation of your actions would not be lost on an Arbitrator. At some point you did advise the Company that you had an addiction problem and that you were seeking help for such. However, there is no evidence that you have been diagnosed with such an addiction nor is there any evidence to show that you are being treated for such. As we said earlier, there is only two pieces of medical documentation on file and neither provide a diagnoses or address treatment. If in fact there was a clinical diagnosis of addiction, treatment and rehabilitation, the Company may well have been obligated to provide accommodation. However, with such an obligation, there also comes an onus on the employees to cooperate with the efforts to accommodate. It was put this way by the arbitrator in CROADR case 3354: The Arbitrator must agree. As confirmed by the Supreme Court of Canada in Central Okanogan School District No. 23 v. Renaud,", "2 S.C.R. 970, the obligation of accommodation involves the cooperative participation of the employer, the trade union and the employee . That was reflected in an award of this Office in CROA 3173 : The Arbitrator is satisfied that the approach adopted by the Company is in keeping with its obligations under the Canadian Human Rights Act . It now seems well-established that when an employee seeks accommodation by reason of a status that is protected under the Canadian Human Rights Act, it is incumbent upon the employee concerned to contribute positively to the process , and to accept an offer of reasonable accommodation, even though it might not be the specific accommodation which the employee would prefer. That is reflected, in part, in the decision of the Supreme Court of Canada in Central Okanogan School District No. 23 v. Renaud,", "2 S.C.R. 970. In that decision, for a unanimous court, Sopinkla J. wrote as follows : To facilitate the search for an accommodation, the complainant must do his or her part as well . Concomitant with a search for reasonable accommodation is a duty to facilitate the search for such an accommodation. Thus in determining whether the duty of accommodation has been fulfilled the conduct of the complainant must be considered. [Emphasis in original]", "On the basis of the record that was before the Commission, and that is before me, the only possible inference is that the Union made every effort to advance the Applicant's grievance but had to abandon the process at step III because of the Applicant's refusal to provide the necessary information, a refusal that has not been linked to his alleged disability. The Commission deals with this matter extensively in the Decision by referring to Mr. Fitzgerald's letter and the Step III Grievance Response dated April 19, 2013. The Applicant provided nothing to counter the information regarding his non-cooperation. It has to be remembered that it was the Applicant who provided the letter from Mr. Fitzgerald so that he was well-aware of what it said about him, and it also has to be borne in mind that his non-cooperation is evidenced by his own Union who had supported him in the grievance process. There was nothing to suggest that the Applicant's failure to cooperate had anything to do with his disability.", "It is also noteworthy that the Canadian Industrial Relations Board came to a similar conclusion when the Applicant alleged a violation of s. 37 of the Canada Labour Code , and alleged that the Union breached its duty of fair representation by failing to properly represent him when it decided not to proceed further with his grievance ( Mulligan v National Automobile, Aerospace, Transportation and General Workers Union of Canada (CAW-Canada) (31 July 2013), 29997-C (CIRB):", "In this case, the complainant requests that the Board hold a hearing. Section 16.1 of the Code provides that the Board may decide any matter before it without holding an oral hearing. Having reviewed all of the material on file, the Board is satisfied that the documentation before it is sufficient for it to decide the matter without holding an oral hearing. As mentioned above, the complainant alleges that the union acted in an arbitrary manner and in bad faith when it did not properly investigate his grievance, did not contact him and did not seek the proper information from his doctors and counsellor. The complainant also alleges that the union violated his rights with respect to article 23.2 of the collective agreement. Section 37 of the Code reads as follows: 37. A trade union or representative of a trade union that is the bargaining agent for a bargaining unit shall not act in a manner that is arbitrary, discriminatory or in bad faith in the representation of any of the employees in the unit with respect to their rights under the collective agreement that is applicable to them. The Board's role in the context of a duty of fair representation complaint is to examine the union's conduct in handling the employee's grievance (see Bugay , 1999 CIRB 45). A section 37 complaint cannot serve to appeal a union's decision not to refer a grievance to arbitration, or to assess the merits of the grievance, but it is used to assess how the union handled the grievance (see Presseault , 2001 CIRB 138). In a complaint under section 37, the complainant bears the onus of presenting evidence that is sufficient to raise a presumption that the union has breached its duty of fair representation. The Board will normally find that the union has fulfilled its duty of fair representation if it has investigated the circumstances, considered the merits of the grievance, made a reasoned judgment about whether to pursue the issue, and if it advised the employee of the reason for its ultimate decision not to proceed any further. The duty of a member to cooperate with his union is described in the following passage from McRaeJackson , 2004 CIRB 290:", "The union's duty of fair representation is predicated on the requirement that employees take the necessary steps to protect their own interests. Employees must make the union aware of potential grievances and ask the union to act on their behalf within the time limits provided in the collective agreement. They must cooperate with their union throughout the grievance procedure, for example by providing the union with the information necessary to investigate a grievance, by attending any medical examinations or other assessments. The evidence on file indicates that the union filed a grievance on behalf of the complainant, processed the grievance to step three of the grievance procedure, sent several letters to the complainant seeking medical information and had numerous telephone conversation with the complainant, with limited success in getting the information needed to further his case. In the Board's opinion, the complainant did not provide any evidence of wrongdoing by the union. The documentation submitted indicates that the complainant brought his termination upon himself by not submitting the information requested by the union. Failure by the complainant to take such action, along with his refusal to cooperate with the union, leads the Board to conclude that the union did not act in an arbitrary manner or in bad faith. Having reviewed the facts submitted, the Board finds that the complainant did not provide sufficient facts to establish that the union has violated its duty of fair representation. For the above reasons, the complaint is dismissed.", "This decision by the Canadian Industrial Relations Board was not before the Commission, but it confirms the Commission's conclusions that the Applicant is the one who, for no apparent reason, thwarted the grievance process that the Commission had earlier told him he had to exhaust before bringing his complaint to the Commission.", "As the Report makes clear, all of the Applicant's submissions were considered including the \"issue of consent and ongoing substance use\" that he claims was not addressed by the Respondent, as well as the correspondence from the Applicant's doctor and personnel in the Respondent's Employee Assistance Program.", "can find no reviewable error in the Commission's Decision (which includes the Report), which ably sets out the relevant facts and the governing jurisprudence. This is simply a case where the Applicant, for no apparent reason, refused to cooperate in the grievance process that could have dealt with his human rights issues and left his Union with no alternative but to close out the file.", "The Commission provides full reasons as to why the complaint was vexatious and why justice did not require the Commission to deal with the complaint.", "Subsection 41(1)(d) of the Act does not require a decision by a grievance arbitrator. As Justice Zinn pointed out in Bergeron , above:", "The jurisprudence is clear that the Commission is to be afforded great latitude in exercising its judgment and in assessing the appropriate factors when considering the application of paragraph 41(1)(d) of the CHRA and performing this \"screening function:\" See, e.g., Sketchley at para 38.", "Bergeron , above, makes it clear that s. 41(1)(d) of the Act may apply in situations where a union has decided not to pursue a grievance to arbitration. In the present case, as the Union letter makes clear, the Applicant refused, for no reason that is established, to engage in a grievance process that could have provided him with accommodation and arbitration and that could have dealt with his human rights issues. The Union makes it clear that his failure to cooperate meant that there was no point in proceeding to arbitration. Having failed to exhaust a grievance process that could have provided him with the remedy he sought before the Commission, the Applicant then filed his complaint with the Commission. The Applicant failed to show that his complaint could not have reasonably been dealt with by the grievance process. The Commission's Decision should not be disturbed.", "The Commission's Decision is transparent, intelligible and justifiable. I can find no reviewable error. It falls within the range of possible acceptable outcomes which are defensible in respect of the facts and the law. JUDGMENT", "THIS COURT'S JUDGMENT is that 1. This application is dismissed with costs to the Respondent. Application dismissed. Editor: J. Danielle King/bk [End of document]"], "label": 1} 2 | {"guid": "005_017", "q_paras": ["Strickland, J. : This is an application for judicial review of two decisions of the Canadian Human Rights Commission (the Commission), both dated August 27, 2014, declining to deal with the Applicant's complaints, pursuant to s 41(1)(a) of the Canadian Human Rights Act , RSC 1985, c H-6 ( CHR Act ), because the Applicant had not exhausted grievance or review procedures otherwise reasonably available to him. The application for judicial review is brought pursuant to s 18.1 of the Federal Courts Act , RSC 1985, c F-7. Background", "The Applicant is a member of the Canadian Forces (CF), the Respondent herein as represented by the Attorney General of Canada. He alleges that in 2010, while he was serving in Haiti, his immediate superior officer discriminated against him, largely on racial grounds. In June 2010, the Applicant filed a complaint with the CF Ombudsman who decided not to deal with the case because other avenues of redress had not yet been exhausted.", "In August 2010 the Applicant met with his Commanding Officer (CO) and the harassment complaint was discussed. His CO subsequently wrote to him on two occasions in September 2010, requesting additional information so as to determine whether the Applicant's allegations constituted a harassment complaint pursuant to the Harassment Prevention and Resolution Guidelines . No response was received from the Applicant.", "By letter of October 5, 2010 counsel for the Applicant advised the CO, amongst other things, that the Applicant did not intend to avail himself of a harassment complaint pursuant to the Department of Nation Defence/CF Harassment Prevention and Resolution policy but reserved his right to make any such complaints in another forum.", "In December 2010 the Applicant filed a complaint with the Commission. By decisions dated July 6, 2011 the Commission decided not to deal with the complaint at that time, pursuant to s 41(1)(a) of the CHR Act , as the Applicant ought first to exhaust grievance or review procedures otherwise reasonably available to him. It stated that at the end of the grievance or review process, the Applicant could ask the Commission to reactivate the complaint. The Commission's decisions of July 6, 2011 are not under review.", "The Applicant then engaged in the CF Redress of Grievance process. On November 29, 2011 the Commander of Canadian Forces Joint Support Group released terms of reference, thereby convening an administrative investigation into the Applicant's allegations of racist conduct, discrimination and harassment. The Administrative Investigation report of the Initial Authority (IA) was released on April 20, 2012. The Applicant was dissatisfied with the IA decision and requested that his grievance be forwarded for determination by the Final Authority (FA).", "On July 4, 2012 the Applicant's grievance was referred to the Canadian Forces Grievance Board (CFGB) which released its findings and recommendations on September 14, 2012. By letter of March 4, 2013, the Chief of the Defence Staff advised that he concurred with the CFGB findings and recommendations, which included that there were important evidentiary omissions and procedural errors that compromised the integrity of the process and resulted in a fatally flawed Administrative Investigation and, therefore, an equally flawed IA decision. The Chief of the Defence Staff also stated that he was concerned that the Applicant had chosen not to avail himself of the CF harassment process and that this had contributed to the CFGB's findings. Given his finding that the IA decision was invalid, the Chief of the Defence Staff concluded that he could not make a properly informed decision on the validity of the allegations and, therefore, that it was imperative that the Applicant initiate the harassment investigation process. The Applicant was given 90 days to do so. The Chief of the Defence Staff stated that when the investigation was complete the Applicant could grieve its outcome if its conclusions and recommendations were not to his satisfaction.", "On May 14, 2013 the Applicant submitted a copy of his original grievance, not a harassment complaint. The Responsible Officer (RO) wrote to the Applicant on May 22, 2013, requesting submission of a proper complaint and inclusion of any information necessary to enable the conduct of a thorough and complete investigation of the allegations of harassment. The RO sent a second letter on July 19, 2013. The Applicant did not respond to either letter.", "On December 24, 2012, while the grievance process was in progress, the Applicant requested the Commission to reactivate his complaint. While the Commission was considering whether s 41(1)(a) applied to the complaint, the FA decision was issued. The Applicant filed an application for judicial review of the FA on April 24, 2012. A November 4, 2013 Section 40/41 Report recommended that the Commission not deal with the complaint while the judicial review was before this Court. On January 23, 2014, before the Commission decided that matter, the Applicant discontinued his application for judicial review of the FA and asked the Commission to deal with his complaint.", "On May 6, 2014 two Section 40/41 Reports were prepared. The first concerned the Applicant's complaint against the CF (file no 20101251) and the second concerned his complaint against his CO (file no 20101252) (collectively, the Section 40/41 Report). Substantially similar Records of Decision Under Section 40/41 were issued by the Commission on August 27, 2014, for both matters (collectively, the Decision). For the purposes of this judicial review, all references shall be to the Section 40/41 Report and decision of the Commission pertaining to the complaint against the CF.", "On September 26, 2014 the Applicant filed his application for judicial review of the Commission's Decision. Decision Under Review", "The Commission decided not to deal with the complaint pursuant to s 41(1)(a) of the CHR Act , as the Applicant ought to have exhausted grievance or review procedures otherwise reasonably available to him. A decision under s 41(1)(d) of the CHR Act was, therefore, unnecessary. The Commission adopted the conclusion of the May 6, 2014 Section 40/41 Report: The complainant's allegations are serious, and in the public interest. On the other hand, the harassment complaint process was reasonably available to the complainant after the Final Authority decision in 2013. The Final Authority decision included measures to be taken as part of the harassment complaint process in order to address the concerns with timeliness and fairness from the preceding administrative investigation. The complainant was provided multiple opportunities to participate in this harassment complaint process, but he chose not to. He was clearly aware of the inadequacies of the grievance form to initiate a harassment complaint as several attempts were made to obtain the correct information from him. Based on the above, the failure to exhaust that process was attributable to him.", "In rendering its decision the Commission reviewed the December 8, 2010 complaint form, the Section 40/41 Report of May 6, 2014 and Supplemental Section 40/41 Report of June 12, 2014, the Applicant's submissions of June 17, 2014, the CF's submissions of July 2, 2014, and cross-disclosure submissions of the CF dated July 25, 2014.", "The Section 40/41 Report set out the background to the complaint and summarized the positions of the parties based on their submissions. It noted that, based on the CF's position, it appeared that the remaining alternative redress process, the CF's harassment complaint process, was no longer available to the Applicant because he did not file a formal harassment complaint prior to the deadline set out in the FA. The question to be answered was whether the harassment complaint process was reasonably available to the Applicant and, if so, whether he should have exhausted that procedure. And, if that process ought to have been exhausted, whether the failure to do so was solely attributable to the Applicant in accordance with s 42(2) of the CHR Act .", "On the first point, it is expedient to reproduce particular paragraphs of the report as it sets out relevance background information as well as its analysis: Was the harassment complaint process reasonably available to the complainant? 64. Some relevant background information can be found in the \"Findings and Recommendations\" of the Canadian Forces Grievance Board (\"CFGB\"), dated September 14, 2012, which was provided by the complainant. In August 2011, after the Commission's decision not to deal with his complaint at that time, the complainant filed a grievance with the respondent. In March 2012, a final report on the Administrative investigation of the complainant's grievance was issued and disclosed to the grievor who provided comments. On April 20, 2012, the Initial Authority decision was rendered, finding \"professional shortcomings\" on the part of the superior officer but not granting the grievor's requests for apologies, damages, or legal fees. The complainant was unsatisfied with this decision and asked for a decision by the Final Authority. The matter was referred to the CFGB for review and recommendations. 65. The CFGB found that because a harassment investigation did not occur, the Initial Authority did not have adequate information upon which to make his decision, even though he attempted to insert elements of a harassment investigation into the grievance process. This investigation was found to have various defects, such as not all witnesses were interviewed, not all relevant information appeared to be considered, and the fact that the superior officer, alleged to be the harasser, was not provided an opportunity to respond to the investigation. For these reasons, the CFGB recommended that the Final Authority not make a final decision on the matter, but order a new harassment investigation be initiated. It also noted that the respondent's policies should be amended to ensure that harassment complaints are required to be dealt with under the harassment policy prior to resorting to the grievance process, given that neither the CFGB nor the CDS is equipped to conduct its own investigation in such situations. 66. On March 4, 2013, the Final Authority rendered his decision. Referring to the CFGB findings and recommendations, the CDS found that there were \" important evidentiary omissions and procedural errors that compromised the integrity of the process and resulted in what the CFGB quite rightly referred to as a 'fatally flawed' administrative investigation and consequently led to an equally flawed IA decision based solely on that investigation.\" The CDS determined that due to these errors and the incompleteness of the underlying investigation, he could not render \" ...a properly informed decision on the validity and reliability of the allegations... \" 67. The CDS also raised concerns that a harassment investigation did not occur. In the Final Authority decision, the CDS stated: Yet, I am troubled by your allegations and most particularly, by the lack of a proper investigation into their validity. As I mentioned, the CF takes this issue very seriously and consequently, it is imperative that you initiate the harassment investigation process by submitting a formal harassment complaint to the CO of 4 CFMCU. You have 90 days from the receipt of this letter to make your submission. The CO of 4 CFMCU shall, upon receipt of your harassment complaint, initiate a harassment investigation into the allegations therein. Let me be very clear on this point: an investigation will not commence until there is a formal harassment complaint submitted to the CO of 4 CFMCU and you commit, in writing, that you will fully participate in such an investigation to ensure that a thorough and impartial analysis may determine the legitimacy of your allegations. If you do not respond within 90 days, there will be no further action taken on your alleged complaints. Given the complexities of this case, compounded by the length of time that has elapsed since the alleged incidents, the CO of the 4 CFMCU might consider engaging a private consulting firm to assist with the investigation. Such corporations specialize in the investigation of harassment cases. Additionally, a monthly situation report will be sent to the Director General Canadian forces [ sic ] Grievance Authority (DGCFGA) Implementation Officer on the status of the forthcoming harassment investigation. When the investigation has been completed, you may grieve its outcome if the conclusions and recommendations are not to your satisfaction. 68. The CDS suggested that a private consulting firm be engaged to investigate the matter. The complainant is of the view that this demonstrates that the respondent is unable to investigate a complaint of this complexity. He suggests that this is further supported by the finding of the CFGB that the initial administrative investigation to be [ sic ] ' fatally flawed '. 69. The lack of procedural fairness in the IA decision was acknowledged in the Final Authority decision. The CDS attempted to address the concerns for procedural fairness and expediency with his recommendations: a) that the matter be dealt with through a formal harassment complaint; and, b) that they hire an outside firm to investigate the matter. 70. The respondent's \"Harassment Prevention and Resolution Guidelines\" (the Guidelines) outline a complaint process which is overseen by Responsible Officers (ROs) in the respondent's organization. The Guidelines define harassment the same as it is defined under the Act. The Guidelines further provide that other workplace harassment may also be addressed through this process. Once a formal harassment complaint is received, the RO oversees a situational assessment to determine whether or not the complaint is related to harassment - if it is, efforts are made to resolve the matter, identify other possible redress avenues such as mediation, and the appropriate action(s) will be initiated. If mediation is not appropriate, an administrative investigation is to be undertaken by a harassment investigator. The harassment investigator prepares a draft report on which both parties may comment before the final report is completed and submitted to the RO for decision. If any person is not in agreement with the RO's decision, they may file a grievance. Although the harassment and grievance processes do not provide for an independent third party decision-maker, matters dealing with harassment and racist conduct are referred to an independent body (the CFGB, now the MGERC) for findings and recommendation prior to the Final Authority decision being rendered. In addition, a review by the Federal Court is also available. 71. The complainant states that only the Commission will be able to fully deal with all of the matters in his complaint. While the complaint deals with harassment, there are also allegations of adverse differential treatment within the workplace. If the harassment complaint process were still available to him, it is not clear whether it could deal with the allegations with respect to the transfer occurring as a result of his marital status, for example. However, it could have dealt with much [ sic ] of the substantive allegations in the complaint. 72. Timeliness is another consideration with respect to this complaint, which is nearly four years old. It appears that both parties bear some responsibility for the delays in the process. In 2010, the complainant did attempt to speak to his superiors about his allegations, but he did not provide them with the requested information when asked in September 2010. As outlined in paragraph 64, the respondent's process is lengthy, and there appears [ sic ] to have been delays in the processing of the complainant's grievance. 73. In terms of delay, it is not clear that the Commission's process would be more expedient than that of the respondent's internal harassment complaint process, if it were still available. Nonetheless, it appears at this stage, the Commission's process is the only remaining process available to him. 74. The complainant's human rights allegations, if proven, are serious; this is acknowledged by the respondent. The complainant has not provided any information to indicate that he is vulnerable personally, but there is a public interest in investigating allegations of racism in Canadian government institutions such as the Canadian Forces. Although the complainant describes the complaint as one of systemic discrimination, no policy or practice has been identified in the summary of complaint. There is no indication that there is or was any potential for harm to anyone if the respondent's harassment complaint process had been used. 75. The complainant suggests that that [ sic ] requiring him to engage in the harassment complaint process is unfair. Although the complainant's scepticism may be understandable given the results of the IA decision, there is no indication that the respondent's alternate redress procedures overall are so flawed that it could not have dealt with the complainant's allegations within its harassment complaint process. In this instance, the Final Authority made recommendations in order to specifically address the issues that occurred previously, including any potential unfairness and the issue of delay. 76. Having regard for all of the above, it appears that the complainant had another review procedure, the harassment complaint process, reasonably available to him after the Final Authority decision in 2013.", "On the question of whether the failure to exhaust the CF's harassment complaint process was attributable to the Applicant and not another, as set out in s 42(2) of the CHR Act , the Section 40/41 Report noted that the history of the complaint illustrated that much of the discord related to the appropriate process for addressing a complaint that raises a variety of issues such as was the case in this matter, which involved allegations of harassment, abuse of authority and adverse differential treatment.", "Ultimately, however, it concluded that: 80. The complainant acknowledged the internal harassment complaint process. After the Final Authority decision, the complainant sent in his grievance form in lieu of a harassment complaint. In his letter to the complainant dated May 22, 2013, the RO responded to the complainant to explain that the grievance form did not provide the information necessary to initiate a harassment complaint. The letter described what was required in order to file a harassment complaint and also offered assistance to the complainant. On July 19, 2013, the RO sent a follow-up letter to the complainant requesting additional information, but the complainant did not provide it. Therefore, while he acknowledged this process, he refused to participate by not providing the appropriate material in order for it to have any possibility of success. This is solely attributable to the complainant. Relevant Legislation", "The sole issue in this application for judicial review is whether the Commission's interpretation and application of s 41(1)(a) were reasonable.", "Jurisprudence has previously held that the appropriate standard of review with respect to decisions of the Commission not to deal with a complaint pursuant to s 41(1) of the CHR Act is reasonableness ( FRAGMENT_SUPPRESSED).", "Reasonableness is concerned with the existence of justification, transparency and intelligibility within the decision-making process. It is also concerned with whether the decision falls within a range of possible, acceptable outcomes which are defensible in respect of the facts and the law ( FRAGMENT_SUPPRESSED). Preliminary Issue", "A preliminary issue that has been raised by the Respondent is the admissibility of the affidavit of Nathalie Bui sworn October 24, 2014 (the Bui Affidavit). It attaches what the affiant describes as the entire record of official correspondence between the Commission and the Applicant concerning files 20101251 and 20101252.", "As noted by the Respondent, this is the Applicant's second attempt to place this material before the Court. The Applicant previously brought a motion, pursuant to Rule 317 of the Federal Courts Rules , SOR/98-106 [ FC Rules ], seeking to have the Commission's entire file produced as part of the Certified Tribunal Record (CTR).", "By Order dated November 26, 2014, Prothonotary Tabib refused that request on the basis that the Applicant was not entitled to further production, noting, in particular, that the Applicant had not identified any document that might have been considered by the Commission, or might be relevant to the determining of the issues before the Court, and which had not been included in the CTR.", "Therefore, in my view, this Court has already ruled on this issue. The conduct of this judicial review on the merits of the Applicant's claim does not serve as an appeal of the Prothonotary's decision. An appeal of a prothonotary's decision must be brought by motion to the Federal Court ( FC Rules , R 51(1), the Applicant did not bring such a motion and the time within which he was required to do so has elapsed. On that basis alone the Bui Affidavit is inadmissible and submissions made in reliance on it or in reference to it are to be disregarded.", "In any event, this Court has also previously held that material that was not before the decision-maker should generally be excluded for purposes of judicial review ( FRAGMENT_SUPPRESSED). And in this case, the Bui Affidavit does not fall under any of the recognized exceptions to this rule ( FRAGMENT_SUPPRESSED).", "The Commission listed the documentation that was reviewed when making its decision, which was contained in the CTR. Accordingly, the additional material contained in the Bui Affidavit is not relevant to the issue before this Court, being whether the Commission's Decision was reasonable based on the record that was before it.", "Further, the May 6, 2014 Section 40/41 Report specifically acknowledges that the parties had raised facts in their submissions that had occurred prior to the original Section 40/41 Report of July 6, 2011. The May 6, 2014 Section 40/41 Report stated that, because the Commission had already dealt with the alternative redress procedures available to the Applicant prior to July 2011, its current analysis would be based only on information arising after the Commission's original decision (para 61). As seen from the analysis contained in the May 6, 2014 Section 40/41 Report, prior facts which were set out in the parties' submissions do not constitute a part of the analysis.", "For all of these reasons, it is my view that the Bui Affidavit is inadmissible. Accordingly, the Applicant's submissions made in reliance on or in reference to that affidavit have not been considered in this judicial review. Position of the Parties", "The Applicant's position is that he has spent years attempting to satisfy the CF grievance system and acted in good faith including through a CF led biased and fatally flawed investigation and ensuing administrative investigation report. He characterises as cyclical, absurd and unreasonable the suggestion that to satisfy all reasonable steps he should now engage the harassment procedure and, if unsatisfied, pursue a new grievance, potentially all the way to judicial review. That process would create an endless procedural loop that would prevent the Applicant from exercising his right to complain under the CHR Act . Accordingly, the Commission unreasonably concluded that the CF's internal process was reasonably available to him.", "The Applicant further submits that the Commission ought to have accepted his complaint. The CF grievance process was exhausted because the Chief of the Defence Staff failed to make a decision on the grievance and instead passed it off, requiring the harassment procedures to be initiated. Further, the CF has demonstrated an unwillingness and an inability to handle this complex matter.", "The Respondent submits that a review of the reasonableness of the Commission's Decision should include an examination of some or all of the relevant factors ( FRAGMENT_SUPPRESSED), including any delay attributable to the Applicant. It should not include the Applicant's subjective and unsubstantiated view that the harassment process is not reasonably available and inadequate because the Applicant has never engaged the process.", "The Respondent further submits that the Commission's Decision that the Applicant ought to have used the appropriate CF internal process, which was reasonably available, falls within a range of possible acceptable outcomes. Any circularity in the process, as alleged by the Applicant, is purely attributable to his own refusal to utilize the process that the CF, the FA and the Commission all found to be the appropriate avenue for redress. Analysis", "In this matter the Commission adopted the recommendations made in the Section 40/41 Report, which, in that circumstance, constitute the reasons of the Commission ( FRAGMENT_SUPPRESSED). The Applicant takes no issue with the Commission's form of response.", "The Section 40/41 Report stated that s 41(1)(a) and s 42 together mean that the Commission can decide not to deal with a complaint under s 41(1)(a) if it finds that the complainant chose not to finish another process that was reasonably available to him or her. This is not an unreasonable interpretation of these provisions. In FRAGMENT_SUPPRESSED", "The Section 40/41 Report then listed factors that may be considered when deciding under s 41(1)(a) whether or not a complainant should keep using the other complaint or grievance process. These were: why the parties did not finish the other process; how much longer that process was likely to take; whether there is new information in that regard; and whether there is anything other than the amount of time the other process may take, such as vulnerability arising from the complainant's current situation or a risk of harm to any participant, that makes it not \"reasonably available\" to the complainant such that the Commission should deal with the complaint. The Section 40/41 Report also summarized the CF's submissions of February 20, 2014, and the Applicant's submissions of February 21, 2014 and April 10, 2013.", "The question of whether the harassment complaint process was reasonably available to the Applicant is largely a factual question. In my view, there is no question that it was available. The only question being was it reasonably so.", "In July 2011 the Commission decided not to deal with the complaint at that time under s 41(1)(a); instead, it asked the Applicant to use another complaint or grievance process first. At the end of that process the complainant could come back to the Commission and ask it to reactivate the complaint.", "Given this, the Applicant then filed a grievance pursuant to s 29 of the National Defence Act , RSC 1985, c N-5. The process thus engaged is set out in the Queen's Regulations and Orders for the Canadian Forces (QROCF) (online: ). This envisions consideration and determination of the grievance by the IA (s 7.15). If the complainant is not satisfied with the IA decision, he or she may request that the matter be referred to the FA (s 7.18). The FA for all grievances is the Chief of the Defence Staff ( National Defence Act , s 29) or his delegate (QROCF, s 7.17). Pursuant to s 29.12 of the National Defence Act, the Chief of the Defence Staff shall refer every grievance that is of a type prescribed by regulation to the Grievances Committee for its findings and recommendations before the Chief of the Defence Staff considers and determines the grievance. These types of grievances are set out in s 7.21 of the QROCF. They include any decision of the Chief of the Defence Staff in respect of a particular officer or non-commissioned officer.", "In March 2012 the IA decision was issued. As the Applicant was unsatisfied with that decision, in May 2012 he sought an FA decision. As seen from the March 4, 2013 letter from the Chief of the Defence Staff, the grievance was submitted to the CFGB which conducted an independent review of the grievance and provided findings and recommendations which were disclosed to the Applicant.", "The Chief of the Defence Staff stated that the CFGB had analysed each of the contentions in the grievance file and found that the administrative investigation on which the IA had based its decision was \"fatally flawed\" on several levels.", "He also stated: The CF's policies on racist conduct and on harassment prevention and resolution are outlined in Canadian Forces Administrative Order 19-43 (Racist Conduct) and Defence Administrative Order and Directive (DAOD) 5012-0 (Harassment Prevention and Resolution). Both are unequivocal in their condemnation of any conduct that causes offence or harm to any person through acts, comments, or displays that demean, belittle, and cause personal humiliation or embarrassment, and any act of intimidation or threat. As much as we in the CF would like to think that we are immune from such behaviours, as a microcosm of the society that we serve, we would be very naïve to think that we, as an organization, would not be subject to instances of this insidious conduct. Accordingly, the CF has a comprehensive framework to deal with such instances in the form of the Harassment Prevention and Resolution Guidelines . The CF policy and guidelines flow directly from and are consistent with the Treasury Board's Policy on Harassment Prevention and Resolution in the Workplace and go beyond the requirements of the Canadian Human Rights Act in their scope.", "The Chief of the Defence Staff expressed concern that the Applicant had chosen not to avail himself of the CF harassment process. His lack of cooperation by failing to fulfil the obligation to provide full written details of his complaint was an impediment to the fair, impartial and expedient harassment investigation and contributed to the \"procedural morass\" that followed. The Chief of the Defence Staff went on to say that there had been important evidentiary omissions and procedural errors that compromised the integrity of the administrative investigation process resulting in what the CFGB had rightly referred to as a fatally flawed administrative investigation; consequently, the IA decision that was based on that investigation was equally flawed.", "Although the Chief of the Defence Staff had identified his role as being to determine whether the harassment occurred and whether the Applicant was a victim of mistreatment at the hands of a superior officer, he concluded that in light of the CFGB findings, he could not render a properly informed decision. He therefore decided to order a new harassment investigation.", "The Chief of the Defence Staff's reference to a lack of clarity as to the order in which the CF harassment and grievance processes are to be utilized is of note. He stated that the CFGB had highlighted that there was lack of clear policy direction regarding the relationship between the harassment and grievance processes. DAOD 2017-1 states: The following are situations in which other complaint and mechanisms should be used prior to proceeding with a grievance: • a complaint of harassment or abuse of authority", "While s 4.10 (Coincidental Complaints and Grievances) of the Harassment Prevention and Resolution Guidelines states: If an individual decides to file a grievance on the same issue as a harassment complaint, the applicable grievance mechanism will apply and the harassment complaint will be closed.", "The Chief of the Defence Staff stated that determination of misconduct, as it pertains to harassment or acts of racism, must be ascertained through a procedurally fair and impartial investigation process, such as that outlined in the Harassment Prevention and Resolution Guidelines . The grievance process should be engaged only if the complainant feels they have been wronged as a result of the outcome of the investigation process or if there is an excessive and unjustifiable delay in handling the complaint.", "The Chief of Military Personnel (CMP), in consultation with the Assistant Deputy Minister Human Resources - Civilian, \"will harmonize the Harassment Prevention and Resolution Guidelines with DAOD 2017-1 so that it is very clear that harassment complaints or grievances containing allegations of harassment must be dealt with first under the harassment policy and only after a proper harassment investigation has been completed\".", "The Chief of the Defence Staff decided that:", "am not prepared to grant the redress that you seek. Yet, I am troubled by your allegations and most particularly, by the lack of a proper investigation into their validity. As I mentioned, the CF takes this issue very seriously and consequently, it is imperative that you initiate the harassment investigation process by submitting a formal harassment complaint to the CO of 4 CFMCU. You have 90 days from the receipt of this letter to make your submission. The CO of 4 CFMCU shall, upon receipt of your harassment complaint, initiate a harassment investigation into the allegations therein. Let me be very clear on this point: an investigation will not commence until there is a formal harassment complaint submitted to the CO of 4 CFMCU and you commit, in writing, that you will fully participate in such an investigation to ensure that a thorough and impartial analysis may determine the legitimacy of your allegations. If you do not respond within 90 days, there will be no further action taken on your alleged complaints. Given the complexities of this case, compounded by the length of time that has elapsed since the alleged incidents, the CO of 4 CFMCU might consider engaging a private consulting firm to assist with the investigation ... When the investigation has been completed, you may grieve its outcome if the conclusions and recommendations are not to your satisfaction.", "In my view, regardless of the factual background prior to the Chief of the Defence Staff's decision and regardless of the lack of clarity as to the order in which harassment complaints or grievances containing allegations of harassment were to be dealt with and any delay or confusion that this may have previously caused, as of March 13, 2013, the Applicant was on clear notice of what was required of him, and that was that he initiate a harassment complaint within 90 days. He was also aware that if he failed to do so, there would be no further action taken with respect to the allegations.", "The CF's submission, as summarized by the Section 40/41 Report, stated that on May 14, 2013 it received from the Applicant a photocopy of his original grievance, not a formal harassment complaint as required by the Chief of the Defence Staff. On May 22, 2013, the RO sent the Applicant a letter requesting that he redraft his submission, explain each allegation specifically and provide any information that would enable the harassment investigation to be thorough and complete. The RO also notified the Applicant's current CO in order to ensure that an assistant could be appointed for the Applicant to help him with the process. The Applicant did not respond to the RO's request. The RO sent another letter to the Applicant on July 19, 2013 requesting an update on the preparation of this harassment complaint but again did not receive a response.", "The Section 40/41 Report noted the other factors that were considered in reaching its recommendation, including the attempt of the Chief of the Defence Staff to address the procedural fairness and other concerns by having the matter dealt with by a formal harassment complaint, with an outside firm being hired to investigate. Further, as to timeliness, that both parties bore some responsibility for the prior delays in the process and that it was not clear that the Commission's process would be more expedient than the CF's harassment policy, if it were still available to the Applicant. However, at this stage, the Commission's process was the only one still available to the Applicant. Further, that the Applicant had provided no information to indicate that he was personally vulnerable.", "The Applicant's position is that the harassment complaint process as proposed by the Chief of the Defence Staff cannot be considered to be reasonably available. The Applicant submits that there has already been a considerable delay, that he commenced the grievance process and that, at the end of that process, rather than rendering a decision, the Chief of the Defence Staff instead attempted to effect a harassment investigation. This is \"cyclical\" because he was required to commence the grievance process, which now requires a harassment investigation, which in turn can be grieved, and that this has the potential of becoming an endless loop.", "In my view, this argument cannot succeed. Although the CFGB's report was not in the CTR, it is clear from the Chief of the Defence Staff's letter that there was a lack of clarity regarding the relationship between the harassment and grievance processes. This may well have contributed to the prior delay and procedural missteps in this matter. And, unless the relationship is clarified, it may have the same impact on other matters in the future.", "Regardless, in this case, the decision by the Chief of the Defence Staff had the effect of resetting the clock. While it is true that a grievance from the conclusions and recommendations of the harassment investigation would have been available to the Applicant, this did not mean that an endless procedural circle would follow. The problem identified in the original grievance process initiated by the Applicant was that there was insufficient information to deal with the harassment complaint. This led to the Chief of the Defence Staff's decision that a harassment investigation was required. Had that been carried out, any further grievance process then open to the Applicant would have been based on the findings of the harassment investigation, not the absence of one, and would not have resulted in a decision that a harassment investigation was required. Thus, this does not result in an endless cycle, nor does it establish that the process proposed by the Chief of the Defence Staff was not \"reasonably available\" to the Applicant.", "Further, the original grievance process would have been exhausted if the Applicant had participated in the proposed harassment investigation. The Applicant does not submit that it was beyond the authority of the Chief of the Defence Staff to proceed as he did, by requiring the harassment investigation. In addition, he discontinued his judicial review of the FA.", "also do not agree with the Applicant's submission that the Chief of the Defence Staff admitted that the CF is unable or unwilling to fully and objectively determine a complaint of racial discrimination when he stated the CO \"might consider engaging a private consulting firm to assist the investigation\" in his letter given the complexity of the case, compounded by the length of time that had elapsed since the alleged incidents. A suggestion that an independent third party with experience in the investigation of harassment complaints could be utilized, if anything, would seem to suggest the incorporation of an additional measure to ensure procedural fairness in the process.", "The Applicant also submits that the Chief of the Defence Staff's statement quoted at paragraph 42 above is an acknowledgment that racism exists within the CF, at least to some degree, and that his inaction to combat racism may mean that it will only continue to exist. However, read in whole, the statement clearly expresses that the CF Harassment Prevention Resolution Guidelines are intended to deal with any instance of racism and harassment.", "As to systemic discrimination, the Section 40/41 Report stated that although the Applicant described the complaint as one of systemic discrimination, no policy or practice had been identified in the summary of complaint. A review of the complaint confirms that the complaint clearly pertained to the Applicant's CO and made no allegation of systemic discrimination. At the hearing before me, the Applicant confirmed that this was the case and that systemic discrimination was therefore not pursued as an issue in this matter.", "In summary, the Commission considered the factual basis of this matter and found that there was a review procedure that was reasonably available to the Applicant and that had not been exhausted by the Applicant prior to the expiry of the 90 day deadline set by the Chief of the Defence Staff. Based on the record, this finding was reasonably open to it.", "The Commission also recognised that this finding would leave the Applicant without recourse. However, as noted by the Respondent, this Court has held that the reasonableness of a decision is not affected by the absence of the \"safety net\" of renewed access to the Commission ( FRAGMENT_SUPPRESSED). In this case, while the CF review process itself is no longer available to the Applicant, nor is there a safety net, the Commission recognized this outcome when making its decision.", "In the circumstance of this matter, the Commission's finding that the Applicant was aware of, but refused to participate in, the CF's internal harassment complaint process and that the failure to exhaust the procedure was solely attributable to the Applicant is to be afforded deference ( FRAGMENT_SUPPRESSED) and also fell within a range of possible acceptable outcomes defensible in respect of the facts and law ( FRAGMENT_SUPPRESSED).", "For these reasons the application is dismissed. The parties have agreed to costs in the amount of $2,500.00, accordingly, the Respondent shall have its costs in that amount. JUDGMENT", "THIS COURT'S JUDGMENT is that 1. The application for judicial review is dismissed. 2. The Respondent shall have its costs in the amount of $2,500.00. Application dismissed. Editor: Jana A. Andersen/nmg [End of document]"], "c_paras": ["Scott, J. : This is an application by William A. Lawrence (Mr. Lawrence), pursuant to subsection 18.1(1) of the Federal Courts Act , RSC, 1985, c F-7, for judicial review of a decision rendered by the Canadian Human Rights Commission [CHRC] on October 12, 2011, wherein the CHRC decided not to deal with Mr. Lawrence's complaint under paragraph 41(1)(d) of the Canadian Human Rights Act , RSC, 1985, c H-6 [the Act].", "Mr. Lawrence is asking this Court to award him compensation for pain and suffering, and special compensation due to differential treatment. He is also requesting an order forcing the Canada Post Corporation (Canada Post) to recognize Mr. Lawrence's inability to work night shifts and a written apology from them.", "For the following reasons, this application for judicial review is dismissed.", "Mr. Lawrence is an employee of Canada Post.", "On September 29, 2010, the Canadian Union of Postal Workers [CUPW] filed a grievance on behalf of Mr. Lawrence. The grievance states that Canada Post violated articles 2, 5, 54 and 56 of the Act and failed to accommodate Mr. Lawrence.", "CUPW filed a second grievance on October 7, 2010. It reads \"the Union grieves on behalf of William Lawrence that the employer has violated Article 10 and all other provisions of the Collective Agreement. By letter dated October 3, 2010, informing Bill Lawrence of his termination upon receipt of said letter by HMPP Manager Mary Pretty. The griever was importuned, reprimanded and terminated from employment, without just, reasonable and sufficient cause\".", "CUPW filed a total of eight grievances on behalf of Mr. Lawrence.", "On December 13, 2010, Mr. Lawrence filed a complaint with the CHRC alleging he was discriminated due to his \"race and medical issues which would also be considered an adverse differential treatment\" (see Respondent's Record, Tab 1, pages 8 to 11).", "The CHRC concluded, in its Section 40/41 Report dated April 8, 2011, that Mr. Lawrence was covered by a Collective Agreement and had full access to another redress procedure. The CHRC also found that it did not have any information regarding a final decision in the other redress procedure but understood that the grievance was proceeding and that arbitration had been scheduled for April 5, 2011 (see Applicant's Record, page 14). The CHRC made the following recommendation: . . . It is recommended, pursuant to paragraph 41(1)(a) of the Canadian Human Right Act , that the Commission not deal with the complaint at this time: as the complainant ought to exhaust the grievance or review procedures otherwise reasonably available. At the end of the grievance or review procedure, the complainant may ask the Commission to reactivate the complaint (see Respondent's Record, Tab 1, page 25).", "On May 12, 2011, Mr. Lawrence's grievances were resolved and a Memorandum of Settlement was executed by CUPW on behalf of Mr. Lawrence and Canada Post. The terms of the settlement are as follows: 1. The Corporation agrees to remove letters dated August 6, 11, 20, 26; September 14, 22 and October 3, 2010, from the grievor's file and return to the grievor his substantive position of a full-time PO4 in the Distribution Parcel Section with an assignment on shift 3 as of May 15, 2011. 2. The Corporation agrees that letters sent to Mr. Lawrence since October 3, 2010, regarding an overpayment shall be disregarded and all issues of wages, benefits and benefit arrears owing will be dealt with in this memorandum. 3. The Corporation will amend the grievor's leave accounts with the following credits to be used in the future: 80 hours of vacation leave; 80 hours of sick time. Further, because this vacation should have been taken in the 2010/2011 leave year, Mr. Lawrence will be able to use these weeks outside the regular vacation pick of his section and will only have to give the Corporation reasonable advance notice of ten (10) working days to be granted these days off. 4. The Corporation will compensate the grievor with a lump sum of $25,000 as compensation for all wages missed from August 1, 2010 through May 13, 2011. Further, from this lump sum, the employer will repay Employment Insurance the sum of $11, 425.00 and the remaining amount of the lump sum will be subject to statutory deductions, such as income tax, union dues and benefits. 5. The grievor is considered to be an employee for the period August 1, 2010 through May 13, 2011. The Corporation will pay the employer's portion of pension and benefits for the above period. Further, the grievor will be responsible to pay for his portion of pension and benefits for the same period. If Great West Life refuses coverage to the grievor for the above-noted period, the Corporation will compensate the grievor for the coverage he was entitled to. 6. Arbitrator MacLellan shall remain seized in the event there are any difficulties between the parties with the implementation of this settlement.", "On June 9, 2011, Mr. Lawrence requested that his Complaint be reactivated with the CHRC on grounds that Canada Post had never acknowledged that he cannot work night shifts due to his medical condition.", "The CHRC issued a supplementary report dealing with Mr. Lawrence's request to reactivate his complaint. The Commission decided not to deal with the Complaint because \"it appears that all of the human rights allegations were addressed and that all the remedies sought were awarded. While the Complainant is concerned by the lack of a provision recognizing that he cannot work overnight shifts, this alone is not sufficient for the Commission to deal with the complaint. If the complainant wishes, he may choose to raise the issue with the arbitrator who oversaw the settlement, which would appear to be the more appropriate body to deal with the matter at this point\" (see Respondent's Record, Tab 1, page 49).", "On September 26, 2011, Mr. Lawrence provided his comments regarding the CHRC's supplementary report. He made submissions regarding his medical conditions and alleged that he had provided Canada Post with medical evidence to clearly establish his inability to work overnight shifts.", "On October 31, 2011, the CHRC considered Mr. Lawrence's complaint and concluded that it did not have to deal with the complaint pursuant to paragraph 41(1)( d ) of the Act .", "On November 8, 2011, Mr. Lawrence filed the present application for judicial review of the CHRC's decision not to deal with his complaint under paragraph 41(1)( d ) of the Act .", "A. Issues 1. Is the Court empowered to grant Mr. Lawrence's request for relief under section 18.1 of the Federal Courts Act? 2. Did the CHRC err in deciding not to deal with Mr. Lawrence's complaint pursuant to paragraph 41(1)(d) of the Act. B. Standard of review", "In Smith v Alliance Pipeline Ltd , 2011 SCC 7, [2011] SCR 160 at para 26, the Supreme Court of Canada specified that \"reasonableness is normally the governing standard where the question: (1) relates to the interpretation of the tribunals enabling (or \"home\") statute or \"statutes closely connected to its function, with which it will have particular familiarity\".", "This Court determined, in Chan v Canada (Attorney General) , 2010 FC 1232, that the reasonableness standard must be applied to the CHRC's decisions not to deal with a complaint under paragraph 41(1)( d ) of the Act .", "In conducting a review under the reasonableness standard, the Court is mostly concerned \"with the existence of justification, transparency and intelligibility within the decision-making process. But it is also concerned with whether the decision falls within the range of possible, acceptable outcomes which are defensible in respect of the facts and law\" (see Dunsmuir v New Brunswick , 2008 SCC 9, [2008] SCJ No 9 at para 47).", "A. Mr. Lawrence's submissions", "Mr. Lawrence requests an Order from this Court quashing or setting aside the CHRC's decision not to deal with his complaint.", "Mr. Lawrence also seeks a letter from Canada Post stating that it agrees to a permanent shift accommodation. He further demands financial \"compensation for pain and suffering and for differential treatment which was wilful and reckless\" (see Mr. Lawrence's Application Record, page 2).", "He requires also a letter of apology from Canada Post and costs for this application.", "In support of his application for judicial review, Mr. Lawrence adduced a copy of his doctor's recommendation and an affidavit supporting his need for a permanent shift accommodation (see Applicant's Record, pages 32, 33 and 39). B. Canada Post's Submissions", "Canada Post argues that remedies available in judicial review are limited to those set out in subsection 18.1(3) of the Federal Courts Act . It cites Whitehead v Pelican Lake First Nation , [2010] 2 CNLR 371. At para 53, Justice Shore wrote that \"It is well-known that the Federal Court has no jurisdiction to grant damages on an application for judicial review\".", "Accordingly, Canada Post submits that the Court lacks jurisdiction to award any form of compensation for damages in this application nor can it order Canada Post to provide Mr. Lawrence with a written apology since this remedy does not fall within the scope of subsection 18.1(3) of the Federal Courts Act .", "Canada Post further submits that the CHRC considered the grievance process and the settlement and properly concluded that all of the human rights allegations were addressed and that all the remedies sought were awarded. The CHRC made the following remark: While the complainant is concerned by the lack of a provision recognizing that he cannot work overnight shifts, this alone is not sufficient for the Commission to deal with the complaint. If the complainant wishes, he may choose to raise the issue with the arbitrator who oversaw the settlement, which would appear to be the more appropriate body to deal with the matter at this point (see Respondent's Record, Tab 1, page 58).", "Canada Post claims that the CHRC's decision is reasonable as the Commission found that the allegation of discrimination had been resolved before the arbitrator. As a result, the CHRC properly discharged its screening function pursuant to section 41 of the Act . It relies on English-Baker v Canada (Attorney General) , 2009 FC 1253, [2009] FCJ No 1604), in support of that proposition.", "It also relies on Canada Post Corp. v Barette , [2000] FCJ No 539, [2000] 4 FC 145 at para 28) to assert that the CHRC reasonably turned its mind to the arbitrator's decision.", "Canada Post disputes the Applicant's claim that the remedies he is seeking are not available under the grievance process. It contends that Mr. Lawrence could have sought these remedies before the arbitrator. As evidenced by the decision of Calgary Board of Education v Alberta Teachers' Assn. (Mackonka Grievance) , [2002] AGAA No 10, arbitrator Ponak awarded a written apology as a remedy.", "Canada Post also refers to Ontario Public Service Employees Union v Ontario (Ministry of Community Safety and Correctional Services) (Latimer Grievance) , [2004] OGSBA No 30. In that case the arbitrator awarded the grievor $7,500.00 in damages for pain and suffering and directed the employer to provide a written apology to the grievor.", "Canada Post maintains that Mr. Lawrence had an alternative avenue for redress in the grievance process. If he is unsatisfied with the implementation of the terms of the Settlement Agreement, arbitrator MacLellan remains seized of the matter. The arbitrator has the power to apply and determine obligations under the Act (see Parry Sound (District) Social Services Administration Board v Ontario Public Service Employees Union, Local 324 , [2003] 2 SCR 157, 2003 SCC 42 [ Parry Sound ]).", "1. Is the Court empowered to grant Mr. Lawrence's request for relief under section 18.1 of the Federal Courts Act?", "Mr. Lawrence explained to this Court that his situation is not settled because he has no guarantee that Canada Post will not send him back on the night shift. He never received a letter of apology or adequate financial compensation and part of the settlement package is still to be implemented.", "The Court is unable to grant part of the relief Mr. Lawrence is seeking.", "As stated in his notice of application dated November 8, 2011, Mr. Lawrence is asking the Court to award him the following relief: 1. Pain and suffering compensation 2. Special compensation due to differential treatment which was wilful and reckless 3. Canada Post Corporation recognize in writing I cannot work overnight shifts to prevent this differential treatment from repeating in the future 4. A written apology from Canada Post Corporation 5. An order for the cost of this application 6. An order quashing or setting aside the Canadian Human Rights decision and referring the decision back to the Canadian Human Rights Commission", "It is well known that the Court has no jurisdiction, under subsection 18.1(3) of the Federal Courts Act , to award damages in judicial review proceedings (see Canada (Attorney General) v TeleZone Inc , [2010] 3 SCR 585; Al-Mhamad v Canada (Canadian Radio-Television and Telecommunications Commission) , 2003 FCA 45, [2003] FCJ No 145 at para 3). Under subsection 18.1(3) of the Federal Courts Act , the Court's main concern is to determine whether the tribunal under review properly exercised the powers conferred by its authorizing statute. Consequently, the Court cannot grant any damages nor can it Order Canada Post to recognize in writing Mr. Lawrence's inability to work overnight shifts. Moreover, it is not empowered to order Canada Post to present a written apology to Mr. Lawrence.", "In the present case the Court can review the CHRC's decision and determine whether it properly exercised its duty to Mr. Lawrence when it decided not to deal with his complaint and make a pronouncement as to costs on this application. 2. Did the CHRC err in deciding not to deal with Mr. Lawrence's complaint pursuant to paragraph 41(1)(d) of the Act?", "The CHRC did not err when it decided not to deal with Mr. Lawrence's complaint, for the following reasons.", "In order to determine whether the CHRC properly dealt with Mr. Lawrence's complaint the Court reviewed the authorities cited by the Respondent and existing case law.", "In Boudreault v Canada (Attorney General) (1995), 99 FTR 293, [1995] FCJ No 1055, Justice Tremblay-Lamer relied on Burke v Canada (Canadian Human Rights Commission) (1987), 125 NR 239 (FCA) and Pitawanakwat v Canada (Human Rights Commission) (1987), 125 NR 237 (FCA) to affirm that if an applicant \"has taken advantage of the available internal remedies, the Commission may not refuse to exercise its jurisdiction on the ground that the matter has already been decided\".", "In the Court's opinion, after a thorough review of the documents filed, it is apparent in the present case that, when the CHRC declined to exercise its discretion, it did not merely rely on a previous decision but carefully analysed the settlement agreement. As indicated in the CHRC's Supplementary Report: . . . the only term the settlement appears to be lacking is a clause recognizing that the complainant cannot work overnight shifts. This alone however, does not appear to constitute a sufficiently strong reason for the Commission to deal with the complaint at this point; the omission of the clause does not in and of itself constitutes an act of discrimination, and it appears that the complainant is currently not working overnight shifts ... In the meantime should the complainant be seriously concerned about the omission of the clause, the arbitrator that oversaw the settlement remains seized of the matter and may be asked to address the issue. (see Respondent's Record, Tab 1, page 49)", "In his memorandum, Mr. Lawrence alleges that his doctor requested he be accommodated to the evening shift which Canada Post refused to do (see Applicant's Record, page 69). However, this request was dealt with in clause number 1 of the Settlement Agreement dated May 12, 2011.", "It was reasonable for the CHRC not to deal with Mr. Lawrence's complaint. If Mr. Lawrence is looking for a declaration that he be permanently accommodated to the evening shifts, clause number 6 provides that \"arbitrator MacLellan shall remain seized in the event there are any difficulties between the parties with the implementation of this settlement\" (see Respondent's Record, Tab 1, page 28). This request is not related to any act of discrimination and ought to be dealt within the grievance process before the arbitrator.", "Moreover, the Court does not agree that remedies sought by Mr. Lawrence were not available in the grievance process. \"An arbitrator has a broad authority to provide a remedy for breach of a collective agreement\" (see Greater Toronto Airports Authority v Public Service Alliance of Canada, Local 0004 , 2011 ONSC 487 at para 45).", "In Parry Sound , the Supreme Court of Canada held that an arbitrator has the power to apply and determine obligations under the human rights legislation. It is clear that the CUPW could have negotiated such relief with Canada Post and arbitrator MacLellan.", "The financial compensation and letter of apologies are not part of the settlement. That does not mean the parties to the settlement agreement never discussed these issues. By its very nature a settlement is a compromise.", "This application for judicial review is dismissed. The CHRC reasonably concluded not to deal with Mr. Lawrence's complaint before he had exhausted all his remedies before the arbitrator. JUDGMENT", "THIS COURT'S JUDGMENT is that 1. This application for judicial review is dismissed; 2. Each party shall pay his own costs. Application dismissed. Editor: E. Joanne Oley/vnh"], "label": 1} 3 | --------------------------------------------------------------------------------