base.py 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424
  1. # coding=utf-8
  2. # Copyright 2018 The HuggingFace Inc. team.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import collections
  16. import copy
  17. import csv
  18. import importlib
  19. import json
  20. import os
  21. import pickle
  22. import sys
  23. import traceback
  24. import types
  25. import warnings
  26. from abc import ABC, abstractmethod
  27. from collections import UserDict
  28. from contextlib import contextmanager
  29. from os.path import abspath, exists
  30. from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
  31. from ..dynamic_module_utils import custom_object_save
  32. from ..feature_extraction_utils import PreTrainedFeatureExtractor
  33. from ..image_processing_utils import BaseImageProcessor
  34. from ..modelcard import ModelCard
  35. from ..models.auto.configuration_auto import AutoConfig
  36. from ..processing_utils import ProcessorMixin
  37. from ..tokenization_utils import PreTrainedTokenizer
  38. from ..utils import (
  39. ModelOutput,
  40. PushToHubMixin,
  41. add_end_docstrings,
  42. copy_func,
  43. infer_framework,
  44. is_tf_available,
  45. is_torch_available,
  46. is_torch_cuda_available,
  47. is_torch_mlu_available,
  48. is_torch_mps_available,
  49. is_torch_musa_available,
  50. is_torch_npu_available,
  51. is_torch_xpu_available,
  52. logging,
  53. )
  54. GenericTensor = Union[List["GenericTensor"], "torch.Tensor", "tf.Tensor"]
  55. if is_tf_available():
  56. import tensorflow as tf
  57. from ..models.auto.modeling_tf_auto import TFAutoModel
  58. if is_torch_available():
  59. import torch
  60. from torch.utils.data import DataLoader, Dataset
  61. from ..models.auto.modeling_auto import AutoModel
  62. # Re-export for backward compatibility
  63. from .pt_utils import KeyDataset
  64. else:
  65. Dataset = None
  66. KeyDataset = None
  67. if TYPE_CHECKING:
  68. from ..modeling_tf_utils import TFPreTrainedModel
  69. from ..modeling_utils import PreTrainedModel
  70. logger = logging.get_logger(__name__)
  71. def no_collate_fn(items):
  72. if len(items) != 1:
  73. raise ValueError("This collate_fn is meant to be used with batch_size=1")
  74. return items[0]
  75. def _pad(items, key, padding_value, padding_side):
  76. batch_size = len(items)
  77. if isinstance(items[0][key], torch.Tensor):
  78. # Others include `attention_mask` etc...
  79. shape = items[0][key].shape
  80. dim = len(shape)
  81. if dim == 1:
  82. # We have a list of 1-dim torch tensors, which can be stacked without padding
  83. return torch.cat([item[key] for item in items], dim=0)
  84. if key in ["pixel_values", "image"]:
  85. # This is probable image so padding shouldn't be necessary
  86. # B, C, H, W
  87. return torch.cat([item[key] for item in items], dim=0)
  88. elif dim == 4 and key == "input_features":
  89. # this is probably a mel spectrogram batched
  90. return torch.cat([item[key] for item in items], dim=0)
  91. max_length = max(item[key].shape[1] for item in items)
  92. min_length = min(item[key].shape[1] for item in items)
  93. dtype = items[0][key].dtype
  94. if dim == 2:
  95. if max_length == min_length:
  96. # Bypass for `ImageGPT` which doesn't provide a padding value, yet
  97. # we can consistently pad since the size should be matching
  98. return torch.cat([item[key] for item in items], dim=0)
  99. tensor = torch.zeros((batch_size, max_length), dtype=dtype) + padding_value
  100. elif dim == 3:
  101. tensor = torch.zeros((batch_size, max_length, shape[-1]), dtype=dtype) + padding_value
  102. elif dim == 4:
  103. tensor = torch.zeros((batch_size, max_length, shape[-2], shape[-1]), dtype=dtype) + padding_value
  104. for i, item in enumerate(items):
  105. if dim == 2:
  106. if padding_side == "left":
  107. tensor[i, -len(item[key][0]) :] = item[key][0].clone()
  108. else:
  109. tensor[i, : len(item[key][0])] = item[key][0].clone()
  110. elif dim == 3:
  111. if padding_side == "left":
  112. tensor[i, -len(item[key][0]) :, :] = item[key][0].clone()
  113. else:
  114. tensor[i, : len(item[key][0]), :] = item[key][0].clone()
  115. elif dim == 4:
  116. if padding_side == "left":
  117. tensor[i, -len(item[key][0]) :, :, :] = item[key][0].clone()
  118. else:
  119. tensor[i, : len(item[key][0]), :, :] = item[key][0].clone()
  120. return tensor
  121. else:
  122. return [item[key] for item in items]
  123. def pad_collate_fn(tokenizer, feature_extractor):
  124. # Tokenizer
  125. t_padding_side = None
  126. # Feature extractor
  127. f_padding_side = None
  128. if tokenizer is None and feature_extractor is None:
  129. raise ValueError("Pipeline without tokenizer or feature_extractor cannot do batching")
  130. if tokenizer is not None:
  131. if tokenizer.pad_token_id is None:
  132. raise ValueError(
  133. "Pipeline with tokenizer without pad_token cannot do batching. You can try to set it with "
  134. "`pipe.tokenizer.pad_token_id = model.config.eos_token_id`."
  135. )
  136. else:
  137. t_padding_value = tokenizer.pad_token_id
  138. t_padding_side = tokenizer.padding_side
  139. if feature_extractor is not None:
  140. # Feature extractor can be images, where no padding is expected
  141. f_padding_value = getattr(feature_extractor, "padding_value", None)
  142. f_padding_side = getattr(feature_extractor, "padding_side", None)
  143. if t_padding_side is not None and f_padding_side is not None and t_padding_side != f_padding_side:
  144. raise ValueError(
  145. f"The feature extractor, and tokenizer don't agree on padding side {t_padding_side} != {f_padding_side}"
  146. )
  147. padding_side = "right"
  148. if t_padding_side is not None:
  149. padding_side = t_padding_side
  150. if f_padding_side is not None:
  151. padding_side = f_padding_side
  152. def inner(items):
  153. keys = set(items[0].keys())
  154. for item in items:
  155. if set(item.keys()) != keys:
  156. raise ValueError(
  157. f"The elements of the batch contain different keys. Cannot batch them ({set(item.keys())} !="
  158. f" {keys})"
  159. )
  160. # input_values, input_pixels, input_ids, ...
  161. padded = {}
  162. for key in keys:
  163. if key in {"input_ids"}:
  164. # ImageGPT uses a feature extractor
  165. if tokenizer is None and feature_extractor is not None:
  166. _padding_value = f_padding_value
  167. else:
  168. _padding_value = t_padding_value
  169. elif key in {"input_values", "pixel_values", "input_features"}:
  170. _padding_value = f_padding_value
  171. elif key in {"p_mask", "special_tokens_mask"}:
  172. _padding_value = 1
  173. elif key in {"attention_mask", "token_type_ids"}:
  174. _padding_value = 0
  175. else:
  176. # This is likely another random key maybe even user provided
  177. _padding_value = 0
  178. padded[key] = _pad(items, key, _padding_value, padding_side)
  179. return padded
  180. return inner
  181. def infer_framework_load_model(
  182. model,
  183. config: AutoConfig,
  184. model_classes: Optional[Dict[str, Tuple[type]]] = None,
  185. task: Optional[str] = None,
  186. framework: Optional[str] = None,
  187. **model_kwargs,
  188. ):
  189. """
  190. Select framework (TensorFlow or PyTorch) to use from the `model` passed. Returns a tuple (framework, model).
  191. If `model` is instantiated, this function will just infer the framework from the model class. Otherwise `model` is
  192. actually a checkpoint name and this method will try to instantiate it using `model_classes`. Since we don't want to
  193. instantiate the model twice, this model is returned for use by the pipeline.
  194. If both frameworks are installed and available for `model`, PyTorch is selected.
  195. Args:
  196. model (`str`, [`PreTrainedModel`] or [`TFPreTrainedModel]`):
  197. The model to infer the framework from. If `str`, a checkpoint name. The model to infer the framewrok from.
  198. config ([`AutoConfig`]):
  199. The config associated with the model to help using the correct class
  200. model_classes (dictionary `str` to `type`, *optional*):
  201. A mapping framework to class.
  202. task (`str`):
  203. The task defining which pipeline will be returned.
  204. model_kwargs:
  205. Additional dictionary of keyword arguments passed along to the model's `from_pretrained(...,
  206. **model_kwargs)` function.
  207. Returns:
  208. `Tuple`: A tuple framework, model.
  209. """
  210. if not is_tf_available() and not is_torch_available():
  211. raise RuntimeError(
  212. "At least one of TensorFlow 2.0 or PyTorch should be installed. "
  213. "To install TensorFlow 2.0, read the instructions at https://www.tensorflow.org/install/ "
  214. "To install PyTorch, read the instructions at https://pytorch.org/."
  215. )
  216. if isinstance(model, str):
  217. model_kwargs["_from_pipeline"] = task
  218. class_tuple = ()
  219. look_pt = is_torch_available() and framework in {"pt", None}
  220. look_tf = is_tf_available() and framework in {"tf", None}
  221. if model_classes:
  222. if look_pt:
  223. class_tuple = class_tuple + model_classes.get("pt", (AutoModel,))
  224. if look_tf:
  225. class_tuple = class_tuple + model_classes.get("tf", (TFAutoModel,))
  226. if config.architectures:
  227. classes = []
  228. for architecture in config.architectures:
  229. transformers_module = importlib.import_module("transformers")
  230. if look_pt:
  231. _class = getattr(transformers_module, architecture, None)
  232. if _class is not None:
  233. classes.append(_class)
  234. if look_tf:
  235. _class = getattr(transformers_module, f"TF{architecture}", None)
  236. if _class is not None:
  237. classes.append(_class)
  238. class_tuple = class_tuple + tuple(classes)
  239. if len(class_tuple) == 0:
  240. raise ValueError(f"Pipeline cannot infer suitable model classes from {model}")
  241. all_traceback = {}
  242. for model_class in class_tuple:
  243. kwargs = model_kwargs.copy()
  244. if framework == "pt" and model.endswith(".h5"):
  245. kwargs["from_tf"] = True
  246. logger.warning(
  247. "Model might be a TensorFlow model (ending with `.h5`) but TensorFlow is not available. "
  248. "Trying to load the model with PyTorch."
  249. )
  250. elif framework == "tf" and model.endswith(".bin"):
  251. kwargs["from_pt"] = True
  252. logger.warning(
  253. "Model might be a PyTorch model (ending with `.bin`) but PyTorch is not available. "
  254. "Trying to load the model with Tensorflow."
  255. )
  256. try:
  257. model = model_class.from_pretrained(model, **kwargs)
  258. if hasattr(model, "eval"):
  259. model = model.eval()
  260. # Stop loading on the first successful load.
  261. break
  262. except (OSError, ValueError):
  263. all_traceback[model_class.__name__] = traceback.format_exc()
  264. continue
  265. if isinstance(model, str):
  266. error = ""
  267. for class_name, trace in all_traceback.items():
  268. error += f"while loading with {class_name}, an error is thrown:\n{trace}\n"
  269. raise ValueError(
  270. f"Could not load model {model} with any of the following classes: {class_tuple}. See the original errors:\n\n{error}\n"
  271. )
  272. if framework is None:
  273. framework = infer_framework(model.__class__)
  274. return framework, model
  275. def infer_framework_from_model(
  276. model,
  277. model_classes: Optional[Dict[str, Tuple[type]]] = None,
  278. task: Optional[str] = None,
  279. framework: Optional[str] = None,
  280. **model_kwargs,
  281. ):
  282. """
  283. Select framework (TensorFlow or PyTorch) to use from the `model` passed. Returns a tuple (framework, model).
  284. If `model` is instantiated, this function will just infer the framework from the model class. Otherwise `model` is
  285. actually a checkpoint name and this method will try to instantiate it using `model_classes`. Since we don't want to
  286. instantiate the model twice, this model is returned for use by the pipeline.
  287. If both frameworks are installed and available for `model`, PyTorch is selected.
  288. Args:
  289. model (`str`, [`PreTrainedModel`] or [`TFPreTrainedModel]`):
  290. The model to infer the framework from. If `str`, a checkpoint name. The model to infer the framewrok from.
  291. model_classes (dictionary `str` to `type`, *optional*):
  292. A mapping framework to class.
  293. task (`str`):
  294. The task defining which pipeline will be returned.
  295. model_kwargs:
  296. Additional dictionary of keyword arguments passed along to the model's `from_pretrained(...,
  297. **model_kwargs)` function.
  298. Returns:
  299. `Tuple`: A tuple framework, model.
  300. """
  301. if isinstance(model, str):
  302. config = AutoConfig.from_pretrained(model, _from_pipeline=task, **model_kwargs)
  303. else:
  304. config = model.config
  305. return infer_framework_load_model(
  306. model, config, model_classes=model_classes, _from_pipeline=task, task=task, framework=framework, **model_kwargs
  307. )
  308. def get_framework(model, revision: Optional[str] = None):
  309. """
  310. Select framework (TensorFlow or PyTorch) to use.
  311. Args:
  312. model (`str`, [`PreTrainedModel`] or [`TFPreTrainedModel]`):
  313. If both frameworks are installed, picks the one corresponding to the model passed (either a model class or
  314. the model name). If no specific model is provided, defaults to using PyTorch.
  315. """
  316. warnings.warn(
  317. "`get_framework` is deprecated and will be removed in v5, use `infer_framework_from_model` instead.",
  318. FutureWarning,
  319. )
  320. if not is_tf_available() and not is_torch_available():
  321. raise RuntimeError(
  322. "At least one of TensorFlow 2.0 or PyTorch should be installed. "
  323. "To install TensorFlow 2.0, read the instructions at https://www.tensorflow.org/install/ "
  324. "To install PyTorch, read the instructions at https://pytorch.org/."
  325. )
  326. if isinstance(model, str):
  327. if is_torch_available() and not is_tf_available():
  328. model = AutoModel.from_pretrained(model, revision=revision)
  329. elif is_tf_available() and not is_torch_available():
  330. model = TFAutoModel.from_pretrained(model, revision=revision)
  331. else:
  332. try:
  333. model = AutoModel.from_pretrained(model, revision=revision)
  334. except OSError:
  335. model = TFAutoModel.from_pretrained(model, revision=revision)
  336. framework = infer_framework(model.__class__)
  337. return framework
  338. def get_default_model_and_revision(
  339. targeted_task: Dict, framework: Optional[str], task_options: Optional[Any]
  340. ) -> Union[str, Tuple[str, str]]:
  341. """
  342. Select a default model to use for a given task. Defaults to pytorch if ambiguous.
  343. Args:
  344. targeted_task (`Dict`):
  345. Dictionary representing the given task, that should contain default models
  346. framework (`str`, None)
  347. "pt", "tf" or None, representing a specific framework if it was specified, or None if we don't know yet.
  348. task_options (`Any`, None)
  349. Any further value required by the task to get fully specified, for instance (SRC, TGT) languages for
  350. translation task.
  351. Returns
  352. `str` The model string representing the default model for this pipeline
  353. """
  354. if is_torch_available() and not is_tf_available():
  355. framework = "pt"
  356. elif is_tf_available() and not is_torch_available():
  357. framework = "tf"
  358. defaults = targeted_task["default"]
  359. if task_options:
  360. if task_options not in defaults:
  361. raise ValueError(f"The task does not provide any default models for options {task_options}")
  362. default_models = defaults[task_options]["model"]
  363. elif "model" in defaults:
  364. default_models = targeted_task["default"]["model"]
  365. else:
  366. # XXX This error message needs to be updated to be more generic if more tasks are going to become
  367. # parametrized
  368. raise ValueError('The task defaults can\'t be correctly selected. You probably meant "translation_XX_to_YY"')
  369. if framework is None:
  370. framework = "pt"
  371. return default_models[framework]
  372. class PipelineException(Exception):
  373. """
  374. Raised by a [`Pipeline`] when handling __call__.
  375. Args:
  376. task (`str`): The task of the pipeline.
  377. model (`str`): The model used by the pipeline.
  378. reason (`str`): The error message to display.
  379. """
  380. def __init__(self, task: str, model: str, reason: str):
  381. super().__init__(reason)
  382. self.task = task
  383. self.model = model
  384. class ArgumentHandler(ABC):
  385. """
  386. Base interface for handling arguments for each [`~pipelines.Pipeline`].
  387. """
  388. @abstractmethod
  389. def __call__(self, *args, **kwargs):
  390. raise NotImplementedError()
  391. class PipelineDataFormat:
  392. """
  393. Base class for all the pipeline supported data format both for reading and writing. Supported data formats
  394. currently includes:
  395. - JSON
  396. - CSV
  397. - stdin/stdout (pipe)
  398. `PipelineDataFormat` also includes some utilities to work with multi-columns like mapping from datasets columns to
  399. pipelines keyword arguments through the `dataset_kwarg_1=dataset_column_1` format.
  400. Args:
  401. output_path (`str`): Where to save the outgoing data.
  402. input_path (`str`): Where to look for the input data.
  403. column (`str`): The column to read.
  404. overwrite (`bool`, *optional*, defaults to `False`):
  405. Whether or not to overwrite the `output_path`.
  406. """
  407. SUPPORTED_FORMATS = ["json", "csv", "pipe"]
  408. def __init__(
  409. self,
  410. output_path: Optional[str],
  411. input_path: Optional[str],
  412. column: Optional[str],
  413. overwrite: bool = False,
  414. ):
  415. self.output_path = output_path
  416. self.input_path = input_path
  417. self.column = column.split(",") if column is not None else [""]
  418. self.is_multi_columns = len(self.column) > 1
  419. if self.is_multi_columns:
  420. self.column = [tuple(c.split("=")) if "=" in c else (c, c) for c in self.column]
  421. if output_path is not None and not overwrite:
  422. if exists(abspath(self.output_path)):
  423. raise OSError(f"{self.output_path} already exists on disk")
  424. if input_path is not None:
  425. if not exists(abspath(self.input_path)):
  426. raise OSError(f"{self.input_path} doesnt exist on disk")
  427. @abstractmethod
  428. def __iter__(self):
  429. raise NotImplementedError()
  430. @abstractmethod
  431. def save(self, data: Union[dict, List[dict]]):
  432. """
  433. Save the provided data object with the representation for the current [`~pipelines.PipelineDataFormat`].
  434. Args:
  435. data (`dict` or list of `dict`): The data to store.
  436. """
  437. raise NotImplementedError()
  438. def save_binary(self, data: Union[dict, List[dict]]) -> str:
  439. """
  440. Save the provided data object as a pickle-formatted binary data on the disk.
  441. Args:
  442. data (`dict` or list of `dict`): The data to store.
  443. Returns:
  444. `str`: Path where the data has been saved.
  445. """
  446. path, _ = os.path.splitext(self.output_path)
  447. binary_path = os.path.extsep.join((path, "pickle"))
  448. with open(binary_path, "wb+") as f_output:
  449. pickle.dump(data, f_output)
  450. return binary_path
  451. @staticmethod
  452. def from_str(
  453. format: str,
  454. output_path: Optional[str],
  455. input_path: Optional[str],
  456. column: Optional[str],
  457. overwrite=False,
  458. ) -> "PipelineDataFormat":
  459. """
  460. Creates an instance of the right subclass of [`~pipelines.PipelineDataFormat`] depending on `format`.
  461. Args:
  462. format (`str`):
  463. The format of the desired pipeline. Acceptable values are `"json"`, `"csv"` or `"pipe"`.
  464. output_path (`str`, *optional*):
  465. Where to save the outgoing data.
  466. input_path (`str`, *optional*):
  467. Where to look for the input data.
  468. column (`str`, *optional*):
  469. The column to read.
  470. overwrite (`bool`, *optional*, defaults to `False`):
  471. Whether or not to overwrite the `output_path`.
  472. Returns:
  473. [`~pipelines.PipelineDataFormat`]: The proper data format.
  474. """
  475. if format == "json":
  476. return JsonPipelineDataFormat(output_path, input_path, column, overwrite=overwrite)
  477. elif format == "csv":
  478. return CsvPipelineDataFormat(output_path, input_path, column, overwrite=overwrite)
  479. elif format == "pipe":
  480. return PipedPipelineDataFormat(output_path, input_path, column, overwrite=overwrite)
  481. else:
  482. raise KeyError(f"Unknown reader {format} (Available reader are json/csv/pipe)")
  483. class CsvPipelineDataFormat(PipelineDataFormat):
  484. """
  485. Support for pipelines using CSV data format.
  486. Args:
  487. output_path (`str`): Where to save the outgoing data.
  488. input_path (`str`): Where to look for the input data.
  489. column (`str`): The column to read.
  490. overwrite (`bool`, *optional*, defaults to `False`):
  491. Whether or not to overwrite the `output_path`.
  492. """
  493. def __init__(
  494. self,
  495. output_path: Optional[str],
  496. input_path: Optional[str],
  497. column: Optional[str],
  498. overwrite=False,
  499. ):
  500. super().__init__(output_path, input_path, column, overwrite=overwrite)
  501. def __iter__(self):
  502. with open(self.input_path, "r") as f:
  503. reader = csv.DictReader(f)
  504. for row in reader:
  505. if self.is_multi_columns:
  506. yield {k: row[c] for k, c in self.column}
  507. else:
  508. yield row[self.column[0]]
  509. def save(self, data: List[dict]):
  510. """
  511. Save the provided data object with the representation for the current [`~pipelines.PipelineDataFormat`].
  512. Args:
  513. data (`List[dict]`): The data to store.
  514. """
  515. with open(self.output_path, "w") as f:
  516. if len(data) > 0:
  517. writer = csv.DictWriter(f, list(data[0].keys()))
  518. writer.writeheader()
  519. writer.writerows(data)
  520. class JsonPipelineDataFormat(PipelineDataFormat):
  521. """
  522. Support for pipelines using JSON file format.
  523. Args:
  524. output_path (`str`): Where to save the outgoing data.
  525. input_path (`str`): Where to look for the input data.
  526. column (`str`): The column to read.
  527. overwrite (`bool`, *optional*, defaults to `False`):
  528. Whether or not to overwrite the `output_path`.
  529. """
  530. def __init__(
  531. self,
  532. output_path: Optional[str],
  533. input_path: Optional[str],
  534. column: Optional[str],
  535. overwrite=False,
  536. ):
  537. super().__init__(output_path, input_path, column, overwrite=overwrite)
  538. with open(input_path, "r") as f:
  539. self._entries = json.load(f)
  540. def __iter__(self):
  541. for entry in self._entries:
  542. if self.is_multi_columns:
  543. yield {k: entry[c] for k, c in self.column}
  544. else:
  545. yield entry[self.column[0]]
  546. def save(self, data: dict):
  547. """
  548. Save the provided data object in a json file.
  549. Args:
  550. data (`dict`): The data to store.
  551. """
  552. with open(self.output_path, "w") as f:
  553. json.dump(data, f)
  554. class PipedPipelineDataFormat(PipelineDataFormat):
  555. """
  556. Read data from piped input to the python process. For multi columns data, columns should separated by \t
  557. If columns are provided, then the output will be a dictionary with {column_x: value_x}
  558. Args:
  559. output_path (`str`): Where to save the outgoing data.
  560. input_path (`str`): Where to look for the input data.
  561. column (`str`): The column to read.
  562. overwrite (`bool`, *optional*, defaults to `False`):
  563. Whether or not to overwrite the `output_path`.
  564. """
  565. def __iter__(self):
  566. for line in sys.stdin:
  567. # Split for multi-columns
  568. if "\t" in line:
  569. line = line.split("\t")
  570. if self.column:
  571. # Dictionary to map arguments
  572. yield {kwargs: l for (kwargs, _), l in zip(self.column, line)}
  573. else:
  574. yield tuple(line)
  575. # No dictionary to map arguments
  576. else:
  577. yield line
  578. def save(self, data: dict):
  579. """
  580. Print the data.
  581. Args:
  582. data (`dict`): The data to store.
  583. """
  584. print(data)
  585. def save_binary(self, data: Union[dict, List[dict]]) -> str:
  586. if self.output_path is None:
  587. raise KeyError(
  588. "When using piped input on pipeline outputting large object requires an output file path. "
  589. "Please provide such output path through --output argument."
  590. )
  591. return super().save_binary(data)
  592. class _ScikitCompat(ABC):
  593. """
  594. Interface layer for the Scikit and Keras compatibility.
  595. """
  596. @abstractmethod
  597. def transform(self, X):
  598. raise NotImplementedError()
  599. @abstractmethod
  600. def predict(self, X):
  601. raise NotImplementedError()
  602. def build_pipeline_init_args(
  603. has_tokenizer: bool = False,
  604. has_feature_extractor: bool = False,
  605. has_image_processor: bool = False,
  606. has_processor: bool = False,
  607. supports_binary_output: bool = True,
  608. ) -> str:
  609. docstring = r"""
  610. Arguments:
  611. model ([`PreTrainedModel`] or [`TFPreTrainedModel`]):
  612. The model that will be used by the pipeline to make predictions. This needs to be a model inheriting from
  613. [`PreTrainedModel`] for PyTorch and [`TFPreTrainedModel`] for TensorFlow."""
  614. if has_tokenizer:
  615. docstring += r"""
  616. tokenizer ([`PreTrainedTokenizer`]):
  617. The tokenizer that will be used by the pipeline to encode data for the model. This object inherits from
  618. [`PreTrainedTokenizer`]."""
  619. if has_feature_extractor:
  620. docstring += r"""
  621. feature_extractor ([`SequenceFeatureExtractor`]):
  622. The feature extractor that will be used by the pipeline to encode data for the model. This object inherits from
  623. [`SequenceFeatureExtractor`]."""
  624. if has_image_processor:
  625. docstring += r"""
  626. image_processor ([`BaseImageProcessor`]):
  627. The image processor that will be used by the pipeline to encode data for the model. This object inherits from
  628. [`BaseImageProcessor`]."""
  629. if has_processor:
  630. docstring += r"""
  631. processor ([`ProcessorMixin`]):
  632. The processor that will be used by the pipeline to encode data for the model. This object inherits from
  633. [`ProcessorMixin`]. Processor is a composite object that might contain `tokenizer`, `feature_extractor`, and
  634. `image_processor`."""
  635. docstring += r"""
  636. modelcard (`str` or [`ModelCard`], *optional*):
  637. Model card attributed to the model for this pipeline.
  638. framework (`str`, *optional*):
  639. The framework to use, either `"pt"` for PyTorch or `"tf"` for TensorFlow. The specified framework must be
  640. installed.
  641. If no framework is specified, will default to the one currently installed. If no framework is specified and
  642. both frameworks are installed, will default to the framework of the `model`, or to PyTorch if no model is
  643. provided.
  644. task (`str`, defaults to `""`):
  645. A task-identifier for the pipeline.
  646. num_workers (`int`, *optional*, defaults to 8):
  647. When the pipeline will use *DataLoader* (when passing a dataset, on GPU for a Pytorch model), the number of
  648. workers to be used.
  649. batch_size (`int`, *optional*, defaults to 1):
  650. When the pipeline will use *DataLoader* (when passing a dataset, on GPU for a Pytorch model), the size of
  651. the batch to use, for inference this is not always beneficial, please read [Batching with
  652. pipelines](https://huggingface.co/transformers/main_classes/pipelines.html#pipeline-batching) .
  653. args_parser ([`~pipelines.ArgumentHandler`], *optional*):
  654. Reference to the object in charge of parsing supplied pipeline parameters.
  655. device (`int`, *optional*, defaults to -1):
  656. Device ordinal for CPU/GPU supports. Setting this to -1 will leverage CPU, a positive will run the model on
  657. the associated CUDA device id. You can pass native `torch.device` or a `str` too
  658. torch_dtype (`str` or `torch.dtype`, *optional*):
  659. Sent directly as `model_kwargs` (just a simpler shortcut) to use the available precision for this model
  660. (`torch.float16`, `torch.bfloat16`, ... or `"auto"`)"""
  661. if supports_binary_output:
  662. docstring += r"""
  663. binary_output (`bool`, *optional*, defaults to `False`):
  664. Flag indicating if the output the pipeline should happen in a serialized format (i.e., pickle) or as
  665. the raw output data e.g. text."""
  666. return docstring
  667. PIPELINE_INIT_ARGS = build_pipeline_init_args(
  668. has_tokenizer=True,
  669. has_feature_extractor=True,
  670. has_image_processor=True,
  671. has_processor=True,
  672. supports_binary_output=True,
  673. )
  674. if is_torch_available():
  675. from transformers.pipelines.pt_utils import (
  676. PipelineChunkIterator,
  677. PipelineDataset,
  678. PipelineIterator,
  679. PipelinePackIterator,
  680. )
  681. @add_end_docstrings(
  682. build_pipeline_init_args(
  683. has_tokenizer=True, has_feature_extractor=True, has_image_processor=True, has_processor=True
  684. )
  685. )
  686. class Pipeline(_ScikitCompat, PushToHubMixin):
  687. """
  688. The Pipeline class is the class from which all pipelines inherit. Refer to this class for methods shared across
  689. different pipelines.
  690. Base class implementing pipelined operations. Pipeline workflow is defined as a sequence of the following
  691. operations:
  692. Input -> Tokenization -> Model Inference -> Post-Processing (task dependent) -> Output
  693. Pipeline supports running on CPU or GPU through the device argument (see below).
  694. Some pipeline, like for instance [`FeatureExtractionPipeline`] (`'feature-extraction'`) output large tensor object
  695. as nested-lists. In order to avoid dumping such large structure as textual data we provide the `binary_output`
  696. constructor argument. If set to `True`, the output will be stored in the pickle format.
  697. """
  698. # Historically we have pipelines working with `tokenizer`, `feature_extractor`, and `image_processor`
  699. # as separate processing components. While we have `processor` class that combines them, some pipelines
  700. # might still operate with these components separately.
  701. # With the addition of `processor` to `pipeline`, we want to avoid:
  702. # - loading `processor` for pipelines that still work with `image_processor` and `tokenizer` separately;
  703. # - loading `image_processor`/`tokenizer` as a separate component while we operate only with `processor`,
  704. # because `processor` will load required sub-components by itself.
  705. # Below flags allow granular control over loading components and set to be backward compatible with current
  706. # pipelines logic. You may override these flags when creating your pipeline. For example, for
  707. # `zero-shot-object-detection` pipeline which operates with `processor` you should set `_load_processor=True`
  708. # and all the rest flags to `False` to avoid unnecessary loading of the components.
  709. _load_processor = False
  710. _load_image_processor = True
  711. _load_feature_extractor = True
  712. _load_tokenizer = True
  713. default_input_names = None
  714. def __init__(
  715. self,
  716. model: Union["PreTrainedModel", "TFPreTrainedModel"],
  717. tokenizer: Optional[PreTrainedTokenizer] = None,
  718. feature_extractor: Optional[PreTrainedFeatureExtractor] = None,
  719. image_processor: Optional[BaseImageProcessor] = None,
  720. processor: Optional[ProcessorMixin] = None,
  721. modelcard: Optional[ModelCard] = None,
  722. framework: Optional[str] = None,
  723. task: str = "",
  724. args_parser: ArgumentHandler = None,
  725. device: Union[int, "torch.device"] = None,
  726. torch_dtype: Optional[Union[str, "torch.dtype"]] = None,
  727. binary_output: bool = False,
  728. **kwargs,
  729. ):
  730. if framework is None:
  731. framework, model = infer_framework_load_model(model, config=model.config)
  732. self.task = task
  733. self.model = model
  734. self.tokenizer = tokenizer
  735. self.feature_extractor = feature_extractor
  736. self.image_processor = image_processor
  737. self.processor = processor
  738. self.modelcard = modelcard
  739. self.framework = framework
  740. # `accelerate` device map
  741. hf_device_map = getattr(self.model, "hf_device_map", None)
  742. if hf_device_map is not None and device is not None:
  743. raise ValueError(
  744. "The model has been loaded with `accelerate` and therefore cannot be moved to a specific device. Please "
  745. "discard the `device` argument when creating your pipeline object."
  746. )
  747. if device is None:
  748. if hf_device_map is not None:
  749. # Take the first device used by `accelerate`.
  750. device = next(iter(hf_device_map.values()))
  751. else:
  752. device = -1
  753. if (
  754. is_torch_mlu_available()
  755. or is_torch_cuda_available()
  756. or is_torch_npu_available()
  757. or is_torch_xpu_available(check_device=True)
  758. or is_torch_mps_available()
  759. ):
  760. logger.warning(
  761. "Hardware accelerator e.g. GPU is available in the environment, but no `device` argument"
  762. " is passed to the `Pipeline` object. Model will be on CPU."
  763. )
  764. if is_torch_available() and self.framework == "pt":
  765. if device == -1 and self.model.device is not None:
  766. device = self.model.device
  767. if isinstance(device, torch.device):
  768. if device.type == "xpu" and not is_torch_xpu_available(check_device=True):
  769. raise ValueError(f'{device} is not available, you should use device="cpu" instead')
  770. self.device = device
  771. elif isinstance(device, str):
  772. if "xpu" in device and not is_torch_xpu_available(check_device=True):
  773. raise ValueError(f'{device} is not available, you should use device="cpu" instead')
  774. self.device = torch.device(device)
  775. elif device < 0:
  776. self.device = torch.device("cpu")
  777. elif is_torch_mlu_available():
  778. self.device = torch.device(f"mlu:{device}")
  779. elif is_torch_musa_available():
  780. self.device = torch.device(f"musa:{device}")
  781. elif is_torch_cuda_available():
  782. self.device = torch.device(f"cuda:{device}")
  783. elif is_torch_npu_available():
  784. self.device = torch.device(f"npu:{device}")
  785. elif is_torch_xpu_available(check_device=True):
  786. self.device = torch.device(f"xpu:{device}")
  787. elif is_torch_mps_available():
  788. self.device = torch.device(f"mps:{device}")
  789. else:
  790. raise ValueError(f"{device} unrecognized or not available.")
  791. else:
  792. self.device = device if device is not None else -1
  793. self.binary_output = binary_output
  794. # We shouldn't call `model.to()` for models loaded with accelerate as well as the case that model is already on device
  795. if (
  796. self.framework == "pt"
  797. and self.model.device != self.device
  798. and not (isinstance(self.device, int) and self.device < 0)
  799. and hf_device_map is None
  800. ):
  801. self.model.to(self.device)
  802. # If the model can generate, create a local generation config. This is done to avoid side-effects on the model
  803. # as we apply local tweaks to the generation config.
  804. if self.model.can_generate():
  805. self.prefix = self.model.config.prefix if hasattr(self.model.config, "prefix") else None
  806. self.generation_config = copy.deepcopy(self.model.generation_config)
  807. # Update the generation config with task specific params if they exist
  808. # NOTE: `prefix` is pipeline-specific and doesn't exist in the generation config.
  809. task_specific_params = self.model.config.task_specific_params
  810. if task_specific_params is not None and task in task_specific_params:
  811. this_task_params = task_specific_params.get(task)
  812. if "prefix" in this_task_params:
  813. self.prefix = this_task_params.pop("prefix")
  814. self.generation_config.update(**this_task_params)
  815. # If the tokenizer has a pad token but the model doesn't, set it so that `generate` is aware of it.
  816. if (
  817. self.tokenizer is not None
  818. and self.tokenizer.pad_token_id is not None
  819. and self.generation_config.pad_token_id is None
  820. ):
  821. self.generation_config.pad_token_id = self.tokenizer.pad_token_id
  822. self.call_count = 0
  823. self._batch_size = kwargs.pop("batch_size", None)
  824. self._num_workers = kwargs.pop("num_workers", None)
  825. self._preprocess_params, self._forward_params, self._postprocess_params = self._sanitize_parameters(**kwargs)
  826. if self.image_processor is None and self.feature_extractor is not None:
  827. if isinstance(self.feature_extractor, BaseImageProcessor):
  828. # Backward compatible change, if users called
  829. # ImageSegmentationPipeline(.., feature_extractor=MyFeatureExtractor())
  830. # then we should keep working
  831. self.image_processor = self.feature_extractor
  832. def save_pretrained(
  833. self,
  834. save_directory: Union[str, os.PathLike],
  835. safe_serialization: bool = True,
  836. **kwargs,
  837. ):
  838. """
  839. Save the pipeline's model and tokenizer.
  840. Args:
  841. save_directory (`str` or `os.PathLike`):
  842. A path to the directory where to saved. It will be created if it doesn't exist.
  843. safe_serialization (`str`):
  844. Whether to save the model using `safetensors` or the traditional way for PyTorch or Tensorflow.
  845. kwargs (`Dict[str, Any]`, *optional*):
  846. Additional key word arguments passed along to the [`~utils.PushToHubMixin.push_to_hub`] method.
  847. """
  848. use_auth_token = kwargs.pop("use_auth_token", None)
  849. if use_auth_token is not None:
  850. warnings.warn(
  851. "The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers. Please use `token` instead.",
  852. FutureWarning,
  853. )
  854. if kwargs.get("token", None) is not None:
  855. raise ValueError(
  856. "`token` and `use_auth_token` are both specified. Please set only the argument `token`."
  857. )
  858. kwargs["token"] = use_auth_token
  859. if os.path.isfile(save_directory):
  860. logger.error(f"Provided path ({save_directory}) should be a directory, not a file")
  861. return
  862. os.makedirs(save_directory, exist_ok=True)
  863. if hasattr(self, "_registered_impl"):
  864. # Add info to the config
  865. pipeline_info = self._registered_impl.copy()
  866. custom_pipelines = {}
  867. for task, info in pipeline_info.items():
  868. if info["impl"] != self.__class__:
  869. continue
  870. info = info.copy()
  871. module_name = info["impl"].__module__
  872. last_module = module_name.split(".")[-1]
  873. # Change classes into their names/full names
  874. info["impl"] = f"{last_module}.{info['impl'].__name__}"
  875. info["pt"] = tuple(c.__name__ for c in info["pt"])
  876. info["tf"] = tuple(c.__name__ for c in info["tf"])
  877. custom_pipelines[task] = info
  878. self.model.config.custom_pipelines = custom_pipelines
  879. # Save the pipeline custom code
  880. custom_object_save(self, save_directory)
  881. kwargs["safe_serialization"] = safe_serialization
  882. self.model.save_pretrained(save_directory, **kwargs)
  883. if self.tokenizer is not None:
  884. self.tokenizer.save_pretrained(save_directory, **kwargs)
  885. if self.feature_extractor is not None:
  886. self.feature_extractor.save_pretrained(save_directory, **kwargs)
  887. if self.image_processor is not None:
  888. self.image_processor.save_pretrained(save_directory, **kwargs)
  889. if self.modelcard is not None:
  890. self.modelcard.save_pretrained(save_directory)
  891. def transform(self, X):
  892. """
  893. Scikit / Keras interface to transformers' pipelines. This method will forward to __call__().
  894. """
  895. return self(X)
  896. def predict(self, X):
  897. """
  898. Scikit / Keras interface to transformers' pipelines. This method will forward to __call__().
  899. """
  900. return self(X)
  901. @property
  902. def torch_dtype(self) -> Optional["torch.dtype"]:
  903. """
  904. Torch dtype of the model (if it's Pytorch model), `None` otherwise.
  905. """
  906. return getattr(self.model, "dtype", None)
  907. @contextmanager
  908. def device_placement(self):
  909. """
  910. Context Manager allowing tensor allocation on the user-specified device in framework agnostic way.
  911. Returns:
  912. Context manager
  913. Examples:
  914. ```python
  915. # Explicitly ask for tensor allocation on CUDA device :0
  916. pipe = pipeline(..., device=0)
  917. with pipe.device_placement():
  918. # Every framework specific tensor allocation will be done on the request device
  919. output = pipe(...)
  920. ```"""
  921. if self.framework == "tf":
  922. with tf.device("/CPU:0" if self.device == -1 else f"/device:GPU:{self.device}"):
  923. yield
  924. else:
  925. if self.device.type == "cuda":
  926. with torch.cuda.device(self.device):
  927. yield
  928. elif self.device.type == "mlu":
  929. with torch.mlu.device(self.device):
  930. yield
  931. elif self.device.type == "musa":
  932. with torch.musa.device(self.device):
  933. yield
  934. else:
  935. yield
  936. def ensure_tensor_on_device(self, **inputs):
  937. """
  938. Ensure PyTorch tensors are on the specified device.
  939. Args:
  940. inputs (keyword arguments that should be `torch.Tensor`, the rest is ignored):
  941. The tensors to place on `self.device`.
  942. Recursive on lists **only**.
  943. Return:
  944. `Dict[str, torch.Tensor]`: The same as `inputs` but on the proper device.
  945. """
  946. return self._ensure_tensor_on_device(inputs, self.device)
  947. def _ensure_tensor_on_device(self, inputs, device):
  948. if isinstance(inputs, ModelOutput):
  949. return ModelOutput(
  950. {name: self._ensure_tensor_on_device(tensor, device) for name, tensor in inputs.items()}
  951. )
  952. elif isinstance(inputs, dict):
  953. return {name: self._ensure_tensor_on_device(tensor, device) for name, tensor in inputs.items()}
  954. elif isinstance(inputs, UserDict):
  955. return UserDict({name: self._ensure_tensor_on_device(tensor, device) for name, tensor in inputs.items()})
  956. elif isinstance(inputs, list):
  957. return [self._ensure_tensor_on_device(item, device) for item in inputs]
  958. elif isinstance(inputs, tuple):
  959. return tuple([self._ensure_tensor_on_device(item, device) for item in inputs])
  960. elif isinstance(inputs, torch.Tensor):
  961. return inputs.to(device)
  962. else:
  963. return inputs
  964. def check_model_type(self, supported_models: Union[List[str], dict]):
  965. """
  966. Check if the model class is in supported by the pipeline.
  967. Args:
  968. supported_models (`List[str]` or `dict`):
  969. The list of models supported by the pipeline, or a dictionary with model class values.
  970. """
  971. if not isinstance(supported_models, list): # Create from a model mapping
  972. supported_models_names = []
  973. for _, model_name in supported_models.items():
  974. # Mapping can now contain tuples of models for the same configuration.
  975. if isinstance(model_name, tuple):
  976. supported_models_names.extend(list(model_name))
  977. else:
  978. supported_models_names.append(model_name)
  979. if hasattr(supported_models, "_model_mapping"):
  980. for _, model in supported_models._model_mapping._extra_content.items():
  981. if isinstance(model_name, tuple):
  982. supported_models_names.extend([m.__name__ for m in model])
  983. else:
  984. supported_models_names.append(model.__name__)
  985. supported_models = supported_models_names
  986. if self.model.__class__.__name__ not in supported_models:
  987. logger.error(
  988. f"The model '{self.model.__class__.__name__}' is not supported for {self.task}. Supported models are"
  989. f" {supported_models}."
  990. )
  991. @abstractmethod
  992. def _sanitize_parameters(self, **pipeline_parameters):
  993. """
  994. _sanitize_parameters will be called with any excessive named arguments from either `__init__` or `__call__`
  995. methods. It should return 3 dictionaries of the resolved parameters used by the various `preprocess`,
  996. `forward` and `postprocess` methods. Do not fill dictionaries if the caller didn't specify a kwargs. This
  997. lets you keep defaults in function signatures, which is more "natural".
  998. It is not meant to be called directly, it will be automatically called and the final parameters resolved by
  999. `__init__` and `__call__`
  1000. """
  1001. raise NotImplementedError("_sanitize_parameters not implemented")
  1002. @abstractmethod
  1003. def preprocess(self, input_: Any, **preprocess_parameters: Dict) -> Dict[str, GenericTensor]:
  1004. """
  1005. Preprocess will take the `input_` of a specific pipeline and return a dictionary of everything necessary for
  1006. `_forward` to run properly. It should contain at least one tensor, but might have arbitrary other items.
  1007. """
  1008. raise NotImplementedError("preprocess not implemented")
  1009. @abstractmethod
  1010. def _forward(self, input_tensors: Dict[str, GenericTensor], **forward_parameters: Dict) -> ModelOutput:
  1011. """
  1012. _forward will receive the prepared dictionary from `preprocess` and run it on the model. This method might
  1013. involve the GPU or the CPU and should be agnostic to it. Isolating this function is the reason for `preprocess`
  1014. and `postprocess` to exist, so that the hot path, this method generally can run as fast as possible.
  1015. It is not meant to be called directly, `forward` is preferred. It is basically the same but contains additional
  1016. code surrounding `_forward` making sure tensors and models are on the same device, disabling the training part
  1017. of the code (leading to faster inference).
  1018. """
  1019. raise NotImplementedError("_forward not implemented")
  1020. @abstractmethod
  1021. def postprocess(self, model_outputs: ModelOutput, **postprocess_parameters: Dict) -> Any:
  1022. """
  1023. Postprocess will receive the raw outputs of the `_forward` method, generally tensors, and reformat them into
  1024. something more friendly. Generally it will output a list or a dict or results (containing just strings and
  1025. numbers).
  1026. """
  1027. raise NotImplementedError("postprocess not implemented")
  1028. def get_inference_context(self):
  1029. return torch.no_grad
  1030. def forward(self, model_inputs, **forward_params):
  1031. with self.device_placement():
  1032. if self.framework == "tf":
  1033. model_inputs["training"] = False
  1034. model_outputs = self._forward(model_inputs, **forward_params)
  1035. elif self.framework == "pt":
  1036. inference_context = self.get_inference_context()
  1037. with inference_context():
  1038. model_inputs = self._ensure_tensor_on_device(model_inputs, device=self.device)
  1039. model_outputs = self._forward(model_inputs, **forward_params)
  1040. model_outputs = self._ensure_tensor_on_device(model_outputs, device=torch.device("cpu"))
  1041. else:
  1042. raise ValueError(f"Framework {self.framework} is not supported")
  1043. return model_outputs
  1044. def get_iterator(
  1045. self, inputs, num_workers: int, batch_size: int, preprocess_params, forward_params, postprocess_params
  1046. ):
  1047. if isinstance(inputs, collections.abc.Sized):
  1048. dataset = PipelineDataset(inputs, self.preprocess, preprocess_params)
  1049. else:
  1050. if num_workers > 1:
  1051. logger.warning(
  1052. "For iterable dataset using num_workers>1 is likely to result"
  1053. " in errors since everything is iterable, setting `num_workers=1`"
  1054. " to guarantee correctness."
  1055. )
  1056. num_workers = 1
  1057. dataset = PipelineIterator(inputs, self.preprocess, preprocess_params)
  1058. if "TOKENIZERS_PARALLELISM" not in os.environ:
  1059. logger.info("Disabling tokenizer parallelism, we're using DataLoader multithreading already")
  1060. os.environ["TOKENIZERS_PARALLELISM"] = "false"
  1061. # TODO hack by collating feature_extractor and image_processor
  1062. feature_extractor = self.feature_extractor if self.feature_extractor is not None else self.image_processor
  1063. collate_fn = no_collate_fn if batch_size == 1 else pad_collate_fn(self.tokenizer, feature_extractor)
  1064. dataloader = DataLoader(dataset, num_workers=num_workers, batch_size=batch_size, collate_fn=collate_fn)
  1065. model_iterator = PipelineIterator(dataloader, self.forward, forward_params, loader_batch_size=batch_size)
  1066. final_iterator = PipelineIterator(model_iterator, self.postprocess, postprocess_params)
  1067. return final_iterator
  1068. def __call__(self, inputs, *args, num_workers=None, batch_size=None, **kwargs):
  1069. if args:
  1070. logger.warning(f"Ignoring args : {args}")
  1071. if num_workers is None:
  1072. if self._num_workers is None:
  1073. num_workers = 0
  1074. else:
  1075. num_workers = self._num_workers
  1076. if batch_size is None:
  1077. if self._batch_size is None:
  1078. batch_size = 1
  1079. else:
  1080. batch_size = self._batch_size
  1081. preprocess_params, forward_params, postprocess_params = self._sanitize_parameters(**kwargs)
  1082. # Fuse __init__ params and __call__ params without modifying the __init__ ones.
  1083. preprocess_params = {**self._preprocess_params, **preprocess_params}
  1084. forward_params = {**self._forward_params, **forward_params}
  1085. postprocess_params = {**self._postprocess_params, **postprocess_params}
  1086. self.call_count += 1
  1087. if self.call_count > 10 and self.framework == "pt" and self.device.type == "cuda":
  1088. logger.warning_once(
  1089. "You seem to be using the pipelines sequentially on GPU. In order to maximize efficiency please use a"
  1090. " dataset",
  1091. )
  1092. is_dataset = Dataset is not None and isinstance(inputs, Dataset)
  1093. is_generator = isinstance(inputs, types.GeneratorType)
  1094. is_list = isinstance(inputs, list)
  1095. is_iterable = is_dataset or is_generator or is_list
  1096. # TODO make the get_iterator work also for `tf` (and `flax`).
  1097. can_use_iterator = self.framework == "pt" and (is_dataset or is_generator or is_list)
  1098. if is_list:
  1099. if can_use_iterator:
  1100. final_iterator = self.get_iterator(
  1101. inputs, num_workers, batch_size, preprocess_params, forward_params, postprocess_params
  1102. )
  1103. outputs = list(final_iterator)
  1104. return outputs
  1105. else:
  1106. return self.run_multi(inputs, preprocess_params, forward_params, postprocess_params)
  1107. elif can_use_iterator:
  1108. return self.get_iterator(
  1109. inputs, num_workers, batch_size, preprocess_params, forward_params, postprocess_params
  1110. )
  1111. elif is_iterable:
  1112. return self.iterate(inputs, preprocess_params, forward_params, postprocess_params)
  1113. elif self.framework == "pt" and isinstance(self, ChunkPipeline):
  1114. return next(
  1115. iter(
  1116. self.get_iterator(
  1117. [inputs], num_workers, batch_size, preprocess_params, forward_params, postprocess_params
  1118. )
  1119. )
  1120. )
  1121. else:
  1122. return self.run_single(inputs, preprocess_params, forward_params, postprocess_params)
  1123. def run_multi(self, inputs, preprocess_params, forward_params, postprocess_params):
  1124. return [self.run_single(item, preprocess_params, forward_params, postprocess_params) for item in inputs]
  1125. def run_single(self, inputs, preprocess_params, forward_params, postprocess_params):
  1126. model_inputs = self.preprocess(inputs, **preprocess_params)
  1127. model_outputs = self.forward(model_inputs, **forward_params)
  1128. outputs = self.postprocess(model_outputs, **postprocess_params)
  1129. return outputs
  1130. def iterate(self, inputs, preprocess_params, forward_params, postprocess_params):
  1131. # This function should become `get_iterator` again, this is a temporary
  1132. # easy solution.
  1133. for input_ in inputs:
  1134. yield self.run_single(input_, preprocess_params, forward_params, postprocess_params)
  1135. Pipeline.push_to_hub = copy_func(Pipeline.push_to_hub)
  1136. if Pipeline.push_to_hub.__doc__ is not None:
  1137. Pipeline.push_to_hub.__doc__ = Pipeline.push_to_hub.__doc__.format(
  1138. object="pipe", object_class="pipeline", object_files="pipeline file"
  1139. ).replace(".from_pretrained", "")
  1140. class ChunkPipeline(Pipeline):
  1141. def run_single(self, inputs, preprocess_params, forward_params, postprocess_params):
  1142. all_outputs = []
  1143. for model_inputs in self.preprocess(inputs, **preprocess_params):
  1144. model_outputs = self.forward(model_inputs, **forward_params)
  1145. all_outputs.append(model_outputs)
  1146. outputs = self.postprocess(all_outputs, **postprocess_params)
  1147. return outputs
  1148. def get_iterator(
  1149. self, inputs, num_workers: int, batch_size: int, preprocess_params, forward_params, postprocess_params
  1150. ):
  1151. if "TOKENIZERS_PARALLELISM" not in os.environ:
  1152. logger.info("Disabling tokenizer parallelism, we're using DataLoader multithreading already")
  1153. os.environ["TOKENIZERS_PARALLELISM"] = "false"
  1154. if num_workers > 1:
  1155. logger.warning(
  1156. "For ChunkPipeline using num_workers>0 is likely to result in errors since everything is iterable,"
  1157. " setting `num_workers=1` to guarantee correctness."
  1158. )
  1159. num_workers = 1
  1160. dataset = PipelineChunkIterator(inputs, self.preprocess, preprocess_params)
  1161. # TODO hack by collating feature_extractor and image_processor
  1162. feature_extractor = self.feature_extractor if self.feature_extractor is not None else self.image_processor
  1163. collate_fn = no_collate_fn if batch_size == 1 else pad_collate_fn(self.tokenizer, feature_extractor)
  1164. dataloader = DataLoader(dataset, num_workers=num_workers, batch_size=batch_size, collate_fn=collate_fn)
  1165. model_iterator = PipelinePackIterator(dataloader, self.forward, forward_params, loader_batch_size=batch_size)
  1166. final_iterator = PipelineIterator(model_iterator, self.postprocess, postprocess_params)
  1167. return final_iterator
  1168. class PipelineRegistry:
  1169. def __init__(self, supported_tasks: Dict[str, Any], task_aliases: Dict[str, str]) -> None:
  1170. self.supported_tasks = supported_tasks
  1171. self.task_aliases = task_aliases
  1172. def get_supported_tasks(self) -> List[str]:
  1173. supported_task = list(self.supported_tasks.keys()) + list(self.task_aliases.keys())
  1174. supported_task.sort()
  1175. return supported_task
  1176. def check_task(self, task: str) -> Tuple[str, Dict, Any]:
  1177. if task in self.task_aliases:
  1178. task = self.task_aliases[task]
  1179. if task in self.supported_tasks:
  1180. targeted_task = self.supported_tasks[task]
  1181. return task, targeted_task, None
  1182. if task.startswith("translation"):
  1183. tokens = task.split("_")
  1184. if len(tokens) == 4 and tokens[0] == "translation" and tokens[2] == "to":
  1185. targeted_task = self.supported_tasks["translation"]
  1186. task = "translation"
  1187. return task, targeted_task, (tokens[1], tokens[3])
  1188. raise KeyError(f"Invalid translation task {task}, use 'translation_XX_to_YY' format")
  1189. raise KeyError(
  1190. f"Unknown task {task}, available tasks are {self.get_supported_tasks() + ['translation_XX_to_YY']}"
  1191. )
  1192. def register_pipeline(
  1193. self,
  1194. task: str,
  1195. pipeline_class: type,
  1196. pt_model: Optional[Union[type, Tuple[type]]] = None,
  1197. tf_model: Optional[Union[type, Tuple[type]]] = None,
  1198. default: Optional[Dict] = None,
  1199. type: Optional[str] = None,
  1200. ) -> None:
  1201. if task in self.supported_tasks:
  1202. logger.warning(f"{task} is already registered. Overwriting pipeline for task {task}...")
  1203. if pt_model is None:
  1204. pt_model = ()
  1205. elif not isinstance(pt_model, tuple):
  1206. pt_model = (pt_model,)
  1207. if tf_model is None:
  1208. tf_model = ()
  1209. elif not isinstance(tf_model, tuple):
  1210. tf_model = (tf_model,)
  1211. task_impl = {"impl": pipeline_class, "pt": pt_model, "tf": tf_model}
  1212. if default is not None:
  1213. if "model" not in default and ("pt" in default or "tf" in default):
  1214. default = {"model": default}
  1215. task_impl["default"] = default
  1216. if type is not None:
  1217. task_impl["type"] = type
  1218. self.supported_tasks[task] = task_impl
  1219. pipeline_class._registered_impl = {task: task_impl}
  1220. def to_dict(self):
  1221. return self.supported_tasks