base.py 57 KB

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