{"record":{"id":"228db24696aaf48f","repo":"hankcs/HanLP","slug":"dataparallel-not-supported-when-crf-is-used","errorCode":null,"errorMessage":"DataParallel not supported when CRF is used","messagePattern":"DataParallel not supported when CRF is used","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"hanlp/components/taggers/tagger.py","lineNumber":37,"sourceCode":"from hanlp.utils.time_util import CountdownTimer\nfrom hanlp_common.util import reorder\nfrom hanlp_trie import DictInterface, TrieDict\nfrom hanlp_trie.dictionary import TupleTrieDict\n\n\nclass Tagger(DistillableComponent, ABC):\n    def build_optimizer(self, optimizer, lr, **kwargs):\n        if optimizer == 'adam':\n            return optim.Adam(params=self.model.parameters(), lr=lr)\n        elif optimizer == 'sgd':\n            return torch.optim.SGD(self.model.parameters(), lr=lr)\n\n    def build_criterion(self, model=None, reduction='mean', decoder=None, **kwargs):\n        if self.config.get('crf', False):\n            if not model:\n                model = decoder or self.model\n            if isinstance(model, nn.DataParallel):\n                raise ValueError('DataParallel not supported when CRF is used')\n                return self.model_from_config.module.crf\n            return model.crf\n        else:\n            return nn.CrossEntropyLoss(reduction=reduction)\n\n    def build_metric(self, **kwargs):\n        return CategoricalAccuracy()\n\n    @abstractmethod\n    def feed_batch(self, batch):\n        pass\n\n    def compute_loss(self, criterion, out, y, mask):\n        if self.config.get('crf', False):\n            criterion: CRF = criterion\n            loss = -criterion.forward(out, y, mask)\n        else:\n            loss = criterion(out[mask], y[mask])","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/hankcs/HanLP/blob/ddb1299bddff079e447af52ec12549c50636bfa8/hanlp/components/taggers/tagger.py#L19-L55","documentation":"When a tagger is configured with crf=True, build_criterion returns the model's CRF module as the loss (CRF computes its own log-likelihood). A raw CRF has shared parameters, so wrapping the model in nn.DataParallel (which replicates the module and scatters/gathers batch dimensions) breaks CRF's sequence-level computation; HanLP therefore refuses the combination.","triggerScenarios":"Training a tagger (e.g. RNN/CRF tagger) with config crf=True while running on multiple GPUs so HanLP wraps the model in nn.DataParallel, then execute_training_loop calls build_criterion and hits the check. Note: the code after the raise (return self.model_from_config.module.crf) is unreachable dead code.","commonSituations":"Scaling single-GPU CRF training to a multi-GPU machine with devices in config; enabling CRF for better sequence tagging accuracy then hitting multi-GPU training.","solutions":["Train on a single GPU: set devices to one GPU id (e.g. devices=0 or devices=-1 depending on config) so DataParallel is not used","Disable CRF (crf=False) if multi-GPU training is required — you lose sequence-level loss but can parallelize","Use DistributedDataParallel-style training outside HanLP's loop only if you understand CRF gather implications"],"exampleFix":"# before (hanlp.json / training config)\n{\"crf\": true, \"devices\": [0, 1]}\n# after\n{\"crf\": true, \"devices\": [0]}","handlingStrategy":"validation","validationCode":"import torch.nn as nn\nassert not (config.get('crf', False) and isinstance(model, nn.DataParallel)), 'CRF + DataParallel unsupported; use a single device'","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Limit devices to one GPU when crf=True in config","Document single-GPU constraint for CRF-based taggers in team training docs"],"tags":["python","pytorch","crf","multi-gpu","dataparallel"],"backgroundTag":"dataparallel-unsupported","analyzedSha":"ddb1299bddff079e447af52ec12549c50636bfa8","analyzedAt":"2026-08-27T03:36:54.287Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}