Compare commits
79 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4dd4b5c538 | ||
|
|
6120e5aaea | ||
|
|
2eaa81b236 | ||
|
|
81ce2a4b26 | ||
|
|
5dd80d3777 | ||
|
|
beeee69bc9 | ||
|
|
9bf28d0b69 | ||
|
|
c0ce15dfb2 | ||
|
|
b9bcdc7158 | ||
|
|
4ff0203987 | ||
|
|
b5f882cc98 | ||
|
|
2e8fc0d4c3 | ||
|
|
dacaf5a400 | ||
|
|
24cde76a15 | ||
|
|
1aa1361510 | ||
|
|
fe470ae5ad | ||
|
|
3a8c2381f7 | ||
|
|
c85b80c2b6 | ||
|
|
2b981012a6 | ||
|
|
6ccc0bfffb | ||
|
|
c8e7eb1eb3 | ||
|
|
24f60a54f4 | ||
|
|
42c02f5892 | ||
|
|
ebede26ebf | ||
|
|
d940ce497e | ||
|
|
05ff90b692 | ||
|
|
1d9b737e05 | ||
|
|
60dc62dc9e | ||
|
|
0f90effc66 | ||
|
|
464dd985e3 | ||
|
|
c07a442854 | ||
|
|
cd3aa153a4 | ||
|
|
9b294976a2 | ||
|
|
5313c2cb8b | ||
|
|
5f09cbdb63 | ||
|
|
4cefa9b49b | ||
|
|
f86bd6190a | ||
|
|
e5452ddfd6 | ||
|
|
d06980dfa7 | ||
|
|
66785cc05c | ||
|
|
05a38612b0 | ||
|
|
d27f4bae39 | ||
|
|
8d8c2f6ffe | ||
|
|
51d3cb951d | ||
|
|
e74b1736a1 | ||
|
|
f07c1ceaa5 | ||
|
|
63b2206ad0 | ||
|
|
27feead2f8 | ||
|
|
c782195662 | ||
|
|
0f621c2c7d | ||
|
|
a9e4574261 | ||
|
|
0229c386c5 | ||
|
|
a7b3e33078 | ||
|
|
e19a64c7ef | ||
|
|
1cb4ad8de9 | ||
|
|
6ed068a71a | ||
|
|
708e6c18b0 | ||
|
|
b943890484 | ||
|
|
a1125ad4df | ||
|
|
a8b150c595 | ||
|
|
665cbcec4b | ||
|
|
7c600440f7 | ||
|
|
e0c6f556e8 | ||
|
|
de23687d16 | ||
|
|
4cea74c73b | ||
|
|
a921d8be9d | ||
|
|
094f716bf2 | ||
|
|
7d761fe3c1 | ||
|
|
cf35d8f3d7 | ||
|
|
4bb6b67188 | ||
|
|
819b18e7ba | ||
|
|
19849db573 | ||
|
|
3d4ceb292c | ||
|
|
f5a37c6c6c | ||
|
|
32c927b53f | ||
|
|
5ffc0d13a2 | ||
|
|
112627e8b2 | ||
|
|
37c1e3c218 | ||
|
|
06e9ebebd5 |
@@ -1,4 +1,4 @@
|
|||||||
name: pylint
|
name: ruff
|
||||||
|
|
||||||
on:
|
on:
|
||||||
# Trigger the workflow on push or pull request,
|
# Trigger the workflow on push or pull request,
|
||||||
@@ -11,7 +11,7 @@ on:
|
|||||||
- main
|
- main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pylint:
|
ruff:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pylint==2.8.2
|
pip install ruff==0.1.5
|
||||||
- name: Analysing the code with pylint
|
- name: Analysing the code with ruff
|
||||||
run: |
|
run: |
|
||||||
pylint vllm tests
|
ruff vllm tests
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -177,3 +177,7 @@ _build/
|
|||||||
# vim swap files
|
# vim swap files
|
||||||
*.swo
|
*.swo
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
|
# hip files generated by PyTorch
|
||||||
|
*.hip
|
||||||
|
*_hip*
|
||||||
|
|||||||
434
.pylintrc
434
.pylintrc
@@ -1,434 +0,0 @@
|
|||||||
# This Pylint rcfile contains a best-effort configuration to uphold the
|
|
||||||
# best-practices and style described in the Google Python style guide:
|
|
||||||
# https://google.github.io/styleguide/pyguide.html
|
|
||||||
#
|
|
||||||
# Its canonical open-source location is:
|
|
||||||
# https://google.github.io/styleguide/pylintrc
|
|
||||||
|
|
||||||
[MASTER]
|
|
||||||
|
|
||||||
# Files or directories to be skipped. They should be base names, not paths.
|
|
||||||
ignore=docs
|
|
||||||
|
|
||||||
# Files or directories matching the regex patterns are skipped. The regex
|
|
||||||
# matches against base names, not paths.
|
|
||||||
ignore-patterns=
|
|
||||||
|
|
||||||
# Pickle collected data for later comparisons.
|
|
||||||
persistent=no
|
|
||||||
|
|
||||||
# List of plugins (as comma separated values of python modules names) to load,
|
|
||||||
# usually to register additional checkers.
|
|
||||||
load-plugins=
|
|
||||||
|
|
||||||
# Use multiple processes to speed up Pylint.
|
|
||||||
jobs=4
|
|
||||||
|
|
||||||
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
|
||||||
# active Python interpreter and may run arbitrary code.
|
|
||||||
unsafe-load-any-extension=no
|
|
||||||
|
|
||||||
|
|
||||||
[MESSAGES CONTROL]
|
|
||||||
|
|
||||||
# Only show warnings with the listed confidence levels. Leave empty to show
|
|
||||||
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
|
|
||||||
confidence=
|
|
||||||
|
|
||||||
# Enable the message, report, category or checker with the given id(s). You can
|
|
||||||
# either give multiple identifier separated by comma (,) or put this option
|
|
||||||
# multiple time (only on the command line, not in the configuration file where
|
|
||||||
# it should appear only once). See also the "--disable" option for examples.
|
|
||||||
#enable=
|
|
||||||
|
|
||||||
# Disable the message, report, category or checker with the given id(s). You
|
|
||||||
# can either give multiple identifiers separated by comma (,) or put this
|
|
||||||
# option multiple times (only on the command line, not in the configuration
|
|
||||||
# file where it should appear only once).You can also use "--disable=all" to
|
|
||||||
# disable everything first and then reenable specific checks. For example, if
|
|
||||||
# you want to run only the similarities checker, you can use "--disable=all
|
|
||||||
# --enable=similarities". If you want to run only the classes checker, but have
|
|
||||||
# no Warning level messages displayed, use"--disable=all --enable=classes
|
|
||||||
# --disable=W"
|
|
||||||
disable=abstract-method,
|
|
||||||
apply-builtin,
|
|
||||||
arguments-differ,
|
|
||||||
attribute-defined-outside-init,
|
|
||||||
backtick,
|
|
||||||
bad-option-value,
|
|
||||||
basestring-builtin,
|
|
||||||
buffer-builtin,
|
|
||||||
c-extension-no-member,
|
|
||||||
consider-using-enumerate,
|
|
||||||
cmp-builtin,
|
|
||||||
cmp-method,
|
|
||||||
coerce-builtin,
|
|
||||||
coerce-method,
|
|
||||||
delslice-method,
|
|
||||||
div-method,
|
|
||||||
duplicate-code,
|
|
||||||
eq-without-hash,
|
|
||||||
execfile-builtin,
|
|
||||||
file-builtin,
|
|
||||||
filter-builtin-not-iterating,
|
|
||||||
fixme,
|
|
||||||
getslice-method,
|
|
||||||
global-statement,
|
|
||||||
hex-method,
|
|
||||||
idiv-method,
|
|
||||||
implicit-str-concat-in-sequence,
|
|
||||||
import-error,
|
|
||||||
import-self,
|
|
||||||
import-star-module-level,
|
|
||||||
inconsistent-return-statements,
|
|
||||||
input-builtin,
|
|
||||||
intern-builtin,
|
|
||||||
invalid-str-codec,
|
|
||||||
locally-disabled,
|
|
||||||
logging-fstring-interpolation, # added by vLLM
|
|
||||||
logging-not-lazy, # added by vLLM
|
|
||||||
long-builtin,
|
|
||||||
long-suffix,
|
|
||||||
map-builtin-not-iterating,
|
|
||||||
misplaced-comparison-constant,
|
|
||||||
missing-class-docstring, # TODO (vLLM): enable
|
|
||||||
missing-function-docstring,
|
|
||||||
missing-module-docstring, # TODO (vLLM): enable
|
|
||||||
metaclass-assignment,
|
|
||||||
next-method-called,
|
|
||||||
next-method-defined,
|
|
||||||
no-absolute-import,
|
|
||||||
no-else-break,
|
|
||||||
no-else-continue,
|
|
||||||
no-else-raise,
|
|
||||||
no-else-return,
|
|
||||||
no-init, # added
|
|
||||||
no-member,
|
|
||||||
no-name-in-module,
|
|
||||||
no-self-use,
|
|
||||||
nonzero-method,
|
|
||||||
oct-method,
|
|
||||||
old-division,
|
|
||||||
old-ne-operator,
|
|
||||||
old-octal-literal,
|
|
||||||
old-raise-syntax,
|
|
||||||
parameter-unpacking,
|
|
||||||
print-statement,
|
|
||||||
raising-string,
|
|
||||||
range-builtin-not-iterating,
|
|
||||||
raw_input-builtin,
|
|
||||||
rdiv-method,
|
|
||||||
reduce-builtin,
|
|
||||||
relative-import,
|
|
||||||
reload-builtin,
|
|
||||||
round-builtin,
|
|
||||||
setslice-method,
|
|
||||||
signature-differs,
|
|
||||||
standarderror-builtin,
|
|
||||||
suppressed-message,
|
|
||||||
sys-max-int,
|
|
||||||
too-few-public-methods,
|
|
||||||
too-many-ancestors,
|
|
||||||
too-many-arguments,
|
|
||||||
too-many-boolean-expressions,
|
|
||||||
too-many-branches,
|
|
||||||
too-many-instance-attributes,
|
|
||||||
too-many-locals,
|
|
||||||
too-many-nested-blocks,
|
|
||||||
too-many-public-methods,
|
|
||||||
too-many-return-statements,
|
|
||||||
too-many-statements,
|
|
||||||
trailing-newlines,
|
|
||||||
unichr-builtin,
|
|
||||||
unicode-builtin,
|
|
||||||
unnecessary-pass,
|
|
||||||
unpacking-in-except,
|
|
||||||
unspecified-encoding,
|
|
||||||
useless-else-on-loop,
|
|
||||||
useless-object-inheritance,
|
|
||||||
useless-suppression,
|
|
||||||
using-cmp-argument,
|
|
||||||
wrong-import-order,
|
|
||||||
xrange-builtin,
|
|
||||||
zip-builtin-not-iterating,
|
|
||||||
|
|
||||||
|
|
||||||
[REPORTS]
|
|
||||||
|
|
||||||
# Set the output format. Available formats are text, parseable, colorized, msvs
|
|
||||||
# (visual studio) and html. You can also give a reporter class, eg
|
|
||||||
# mypackage.mymodule.MyReporterClass.
|
|
||||||
output-format=text
|
|
||||||
|
|
||||||
# Tells whether to display a full report or only the messages
|
|
||||||
reports=no
|
|
||||||
|
|
||||||
# Python expression which should return a note less than 10 (10 is the highest
|
|
||||||
# note). You have access to the variables errors warning, statement which
|
|
||||||
# respectively contain the number of errors / warnings messages and the total
|
|
||||||
# number of statements analyzed. This is used by the global evaluation report
|
|
||||||
# (RP0004).
|
|
||||||
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
|
||||||
|
|
||||||
# Template used to display messages. This is a python new-style format string
|
|
||||||
# used to format the message information. See doc for all details
|
|
||||||
#msg-template=
|
|
||||||
|
|
||||||
|
|
||||||
[BASIC]
|
|
||||||
|
|
||||||
# Good variable names which should always be accepted, separated by a comma
|
|
||||||
good-names=main,_
|
|
||||||
|
|
||||||
# Bad variable names which should always be refused, separated by a comma
|
|
||||||
bad-names=
|
|
||||||
|
|
||||||
# Colon-delimited sets of names that determine each other's naming style when
|
|
||||||
# the name regexes allow several styles.
|
|
||||||
name-group=
|
|
||||||
|
|
||||||
# Include a hint for the correct naming format with invalid-name
|
|
||||||
include-naming-hint=no
|
|
||||||
|
|
||||||
# List of decorators that produce properties, such as abc.abstractproperty. Add
|
|
||||||
# to this list to register other decorators that produce valid properties.
|
|
||||||
property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl
|
|
||||||
|
|
||||||
# Regular expression matching correct function names
|
|
||||||
function-rgx=^(?:(?P<exempt>setUp|tearDown|setUpModule|tearDownModule)|(?P<camel_case>_?[A-Z][a-zA-Z0-9]*)|(?P<snake_case>_?[a-z][a-z0-9_]*))$
|
|
||||||
|
|
||||||
# Regular expression matching correct variable names
|
|
||||||
variable-rgx=^[a-z][a-z0-9_]*$
|
|
||||||
|
|
||||||
# Regular expression matching correct constant names
|
|
||||||
const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
|
|
||||||
|
|
||||||
# Regular expression matching correct attribute names
|
|
||||||
attr-rgx=^_{0,2}[a-z][a-z0-9_]*$
|
|
||||||
|
|
||||||
# Regular expression matching correct argument names
|
|
||||||
argument-rgx=^[a-z][a-z0-9_]*$
|
|
||||||
|
|
||||||
# Regular expression matching correct class attribute names
|
|
||||||
class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
|
|
||||||
|
|
||||||
# Regular expression matching correct inline iteration names
|
|
||||||
inlinevar-rgx=^[a-z][a-z0-9_]*$
|
|
||||||
|
|
||||||
# Regular expression matching correct class names
|
|
||||||
class-rgx=^_?[A-Z][a-zA-Z0-9]*$
|
|
||||||
|
|
||||||
# Regular expression matching correct module names
|
|
||||||
module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$
|
|
||||||
|
|
||||||
# Regular expression matching correct method names
|
|
||||||
method-rgx=(?x)^(?:(?P<exempt>_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P<camel_case>_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P<snake_case>_{0,2}[a-z][a-z0-9_]*))$
|
|
||||||
|
|
||||||
# Regular expression which should only match function or class names that do
|
|
||||||
# not require a docstring.
|
|
||||||
no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$
|
|
||||||
|
|
||||||
# Minimum line length for functions/classes that require docstrings, shorter
|
|
||||||
# ones are exempt.
|
|
||||||
docstring-min-length=10
|
|
||||||
|
|
||||||
|
|
||||||
[TYPECHECK]
|
|
||||||
|
|
||||||
# List of decorators that produce context managers, such as
|
|
||||||
# contextlib.contextmanager. Add to this list to register other decorators that
|
|
||||||
# produce valid context managers.
|
|
||||||
contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager
|
|
||||||
|
|
||||||
# Tells whether missing members accessed in mixin class should be ignored. A
|
|
||||||
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
|
||||||
ignore-mixin-members=yes
|
|
||||||
|
|
||||||
# List of module names for which member attributes should not be checked
|
|
||||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
|
||||||
# and thus existing member attributes cannot be deduced by static analysis. It
|
|
||||||
# supports qualified module names, as well as Unix pattern matching.
|
|
||||||
ignored-modules=
|
|
||||||
|
|
||||||
# List of class names for which member attributes should not be checked (useful
|
|
||||||
# for classes with dynamically set attributes). This supports the use of
|
|
||||||
# qualified names.
|
|
||||||
ignored-classes=optparse.Values,thread._local,_thread._local
|
|
||||||
|
|
||||||
# List of members which are set dynamically and missed by pylint inference
|
|
||||||
# system, and so shouldn't trigger E1101 when accessed. Python regular
|
|
||||||
# expressions are accepted.
|
|
||||||
generated-members=
|
|
||||||
|
|
||||||
|
|
||||||
[FORMAT]
|
|
||||||
|
|
||||||
# Maximum number of characters on a single line.
|
|
||||||
max-line-length=80
|
|
||||||
|
|
||||||
# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt
|
|
||||||
# lines made too long by directives to pytype.
|
|
||||||
|
|
||||||
# Regexp for a line that is allowed to be longer than the limit.
|
|
||||||
ignore-long-lines=(?x)(
|
|
||||||
^\s*(\#\ )?<?https?://\S+>?$|
|
|
||||||
^\s*(from\s+\S+\s+)?import\s+.+$)
|
|
||||||
|
|
||||||
# Allow the body of an if to be on the same line as the test if there is no
|
|
||||||
# else.
|
|
||||||
single-line-if-stmt=yes
|
|
||||||
|
|
||||||
# Maximum number of lines in a module
|
|
||||||
max-module-lines=99999
|
|
||||||
|
|
||||||
# String used as indentation unit. The internal Google style guide mandates 2
|
|
||||||
# spaces. Google's externaly-published style guide says 4, consistent with
|
|
||||||
# PEP 8. Here, we use 2 spaces, for conformity with many open-sourced Google
|
|
||||||
# projects (like TensorFlow).
|
|
||||||
indent-string=' '
|
|
||||||
|
|
||||||
# Number of spaces of indent required inside a hanging or continued line.
|
|
||||||
indent-after-paren=4
|
|
||||||
|
|
||||||
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
|
||||||
expected-line-ending-format=
|
|
||||||
|
|
||||||
|
|
||||||
[MISCELLANEOUS]
|
|
||||||
|
|
||||||
# List of note tags to take in consideration, separated by a comma.
|
|
||||||
notes=TODO
|
|
||||||
|
|
||||||
|
|
||||||
[STRING]
|
|
||||||
|
|
||||||
# This flag controls whether inconsistent-quotes generates a warning when the
|
|
||||||
# character used as a quote delimiter is used inconsistently within a module.
|
|
||||||
check-quote-consistency=yes
|
|
||||||
|
|
||||||
|
|
||||||
[VARIABLES]
|
|
||||||
|
|
||||||
# Tells whether we should check for unused import in __init__ files.
|
|
||||||
init-import=no
|
|
||||||
|
|
||||||
# A regular expression matching the name of dummy variables (i.e. expectedly
|
|
||||||
# not used).
|
|
||||||
dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_)
|
|
||||||
|
|
||||||
# List of additional names supposed to be defined in builtins. Remember that
|
|
||||||
# you should avoid to define new builtins when possible.
|
|
||||||
additional-builtins=
|
|
||||||
|
|
||||||
# List of strings which can identify a callback function by name. A callback
|
|
||||||
# name must start or end with one of those strings.
|
|
||||||
callbacks=cb_,_cb
|
|
||||||
|
|
||||||
# List of qualified module names which can have objects that can redefine
|
|
||||||
# builtins.
|
|
||||||
redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools
|
|
||||||
|
|
||||||
|
|
||||||
[LOGGING]
|
|
||||||
|
|
||||||
# Logging modules to check that the string format arguments are in logging
|
|
||||||
# function parameter format
|
|
||||||
logging-modules=logging,absl.logging,tensorflow.io.logging
|
|
||||||
|
|
||||||
|
|
||||||
[SIMILARITIES]
|
|
||||||
|
|
||||||
# Minimum lines number of a similarity.
|
|
||||||
min-similarity-lines=4
|
|
||||||
|
|
||||||
# Ignore comments when computing similarities.
|
|
||||||
ignore-comments=yes
|
|
||||||
|
|
||||||
# Ignore docstrings when computing similarities.
|
|
||||||
ignore-docstrings=yes
|
|
||||||
|
|
||||||
# Ignore imports when computing similarities.
|
|
||||||
ignore-imports=no
|
|
||||||
|
|
||||||
|
|
||||||
[SPELLING]
|
|
||||||
|
|
||||||
# Spelling dictionary name. Available dictionaries: none. To make it working
|
|
||||||
# install python-enchant package.
|
|
||||||
spelling-dict=
|
|
||||||
|
|
||||||
# List of comma separated words that should not be checked.
|
|
||||||
spelling-ignore-words=
|
|
||||||
|
|
||||||
# A path to a file that contains private dictionary; one word per line.
|
|
||||||
spelling-private-dict-file=
|
|
||||||
|
|
||||||
# Tells whether to store unknown words to indicated private dictionary in
|
|
||||||
# --spelling-private-dict-file option instead of raising a message.
|
|
||||||
spelling-store-unknown-words=no
|
|
||||||
|
|
||||||
|
|
||||||
[IMPORTS]
|
|
||||||
|
|
||||||
# Deprecated modules which should not be used, separated by a comma
|
|
||||||
deprecated-modules=regsub,
|
|
||||||
TERMIOS,
|
|
||||||
Bastion,
|
|
||||||
rexec,
|
|
||||||
sets
|
|
||||||
|
|
||||||
# Create a graph of every (i.e. internal and external) dependencies in the
|
|
||||||
# given file (report RP0402 must not be disabled)
|
|
||||||
import-graph=
|
|
||||||
|
|
||||||
# Create a graph of external dependencies in the given file (report RP0402 must
|
|
||||||
# not be disabled)
|
|
||||||
ext-import-graph=
|
|
||||||
|
|
||||||
# Create a graph of internal dependencies in the given file (report RP0402 must
|
|
||||||
# not be disabled)
|
|
||||||
int-import-graph=
|
|
||||||
|
|
||||||
# Force import order to recognize a module as part of the standard
|
|
||||||
# compatibility libraries.
|
|
||||||
known-standard-library=
|
|
||||||
|
|
||||||
# Force import order to recognize a module as part of a third party library.
|
|
||||||
known-third-party=enchant, absl
|
|
||||||
|
|
||||||
# Analyse import fallback blocks. This can be used to support both Python 2 and
|
|
||||||
# 3 compatible code, which means that the block might have code that exists
|
|
||||||
# only in one or another interpreter, leading to false positives when analysed.
|
|
||||||
analyse-fallback-blocks=no
|
|
||||||
|
|
||||||
|
|
||||||
[CLASSES]
|
|
||||||
|
|
||||||
# List of method names used to declare (i.e. assign) instance attributes.
|
|
||||||
defining-attr-methods=__init__,
|
|
||||||
__new__,
|
|
||||||
setUp
|
|
||||||
|
|
||||||
# List of member names, which should be excluded from the protected access
|
|
||||||
# warning.
|
|
||||||
exclude-protected=_asdict,
|
|
||||||
_fields,
|
|
||||||
_replace,
|
|
||||||
_source,
|
|
||||||
_make
|
|
||||||
|
|
||||||
# List of valid names for the first argument in a class method.
|
|
||||||
valid-classmethod-first-arg=cls,
|
|
||||||
class_
|
|
||||||
|
|
||||||
# List of valid names for the first argument in a metaclass class method.
|
|
||||||
valid-metaclass-classmethod-first-arg=mcs
|
|
||||||
|
|
||||||
|
|
||||||
[EXCEPTIONS]
|
|
||||||
|
|
||||||
# Exceptions that will emit a warning when being caught. Defaults to
|
|
||||||
# "Exception"
|
|
||||||
overgeneral-exceptions=StandardError,
|
|
||||||
Exception,
|
|
||||||
BaseException
|
|
||||||
14
Dockerfile
14
Dockerfile
@@ -18,6 +18,11 @@ RUN --mount=type=cache,target=/root/.cache/pip \
|
|||||||
# image to build pytorch extensions
|
# image to build pytorch extensions
|
||||||
FROM dev AS build
|
FROM dev AS build
|
||||||
|
|
||||||
|
# install build dependencies
|
||||||
|
COPY requirements-build.txt requirements-build.txt
|
||||||
|
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||||
|
pip install -r requirements-build.txt
|
||||||
|
|
||||||
# copy input files
|
# copy input files
|
||||||
COPY csrc csrc
|
COPY csrc csrc
|
||||||
COPY setup.py setup.py
|
COPY setup.py setup.py
|
||||||
@@ -25,8 +30,15 @@ COPY requirements.txt requirements.txt
|
|||||||
COPY pyproject.toml pyproject.toml
|
COPY pyproject.toml pyproject.toml
|
||||||
COPY vllm/__init__.py vllm/__init__.py
|
COPY vllm/__init__.py vllm/__init__.py
|
||||||
|
|
||||||
|
ARG torch_cuda_arch_list='7.0 7.5 8.0 8.6 8.9 9.0+PTX'
|
||||||
|
ENV TORCH_CUDA_ARCH_LIST=${torch_cuda_arch_list}
|
||||||
# max jobs used by Ninja to build extensions
|
# max jobs used by Ninja to build extensions
|
||||||
ENV MAX_JOBS=$max_jobs
|
ARG max_jobs=2
|
||||||
|
ENV MAX_JOBS=${max_jobs}
|
||||||
|
# number of threads used by nvcc
|
||||||
|
ARG nvcc_threads=8
|
||||||
|
ENV NVCC_THREADS=$nvcc_threads
|
||||||
|
|
||||||
RUN python3 setup.py build_ext --inplace
|
RUN python3 setup.py build_ext --inplace
|
||||||
|
|
||||||
# image to run unit testing suite
|
# image to run unit testing suite
|
||||||
|
|||||||
62
Dockerfile.rocm
Normal file
62
Dockerfile.rocm
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
FROM rocm/pytorch:rocm5.7_ubuntu22.04_py3.10_pytorch_2.0.1
|
||||||
|
|
||||||
|
# Install some basic utilities
|
||||||
|
RUN apt-get update && apt-get install python3 python3-pip -y
|
||||||
|
|
||||||
|
# Install some basic utilities
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
curl \
|
||||||
|
ca-certificates \
|
||||||
|
sudo \
|
||||||
|
git \
|
||||||
|
bzip2 \
|
||||||
|
libx11-6 \
|
||||||
|
build-essential \
|
||||||
|
wget \
|
||||||
|
unzip \
|
||||||
|
nvidia-cuda-toolkit \
|
||||||
|
tmux \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
### Mount Point ###
|
||||||
|
# When launching the container, mount the code directory to /app
|
||||||
|
ARG APP_MOUNT=/app
|
||||||
|
VOLUME [ ${APP_MOUNT} ]
|
||||||
|
WORKDIR ${APP_MOUNT}
|
||||||
|
|
||||||
|
RUN python3 -m pip install --upgrade pip
|
||||||
|
RUN python3 -m pip install --no-cache-dir fastapi ninja tokenizers pandas
|
||||||
|
|
||||||
|
ENV LLVM_SYMBOLIZER_PATH=/opt/rocm/llvm/bin/llvm-symbolizer
|
||||||
|
ENV PATH=$PATH:/opt/rocm/bin:/libtorch/bin:
|
||||||
|
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/rocm/lib/:/libtorch/lib:
|
||||||
|
ENV CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/libtorch/include:/libtorch/include/torch/csrc/api/include/:/opt/rocm/include/:
|
||||||
|
|
||||||
|
# Install ROCm flash-attention
|
||||||
|
RUN mkdir libs \
|
||||||
|
&& cd libs \
|
||||||
|
&& git clone https://github.com/ROCmSoftwarePlatform/flash-attention.git \
|
||||||
|
&& cd flash-attention \
|
||||||
|
&& git checkout 3d2b6f5 \
|
||||||
|
&& git submodule update --init \
|
||||||
|
&& export GPU_ARCHS=$(/opt/rocm/llvm/bin/amdgpu-offload-arch) \
|
||||||
|
&& patch /opt/conda/envs/py_3.10/lib/python3.10/site-packages/torch/utils/hipify/hipify_python.py hipify_patch.patch \
|
||||||
|
&& python3 setup.py install \
|
||||||
|
&& cd ..
|
||||||
|
|
||||||
|
COPY ./ /app/vllm
|
||||||
|
|
||||||
|
RUN python3 -m pip install --upgrade pip
|
||||||
|
RUN pip install xformers==0.0.22.post7 --no-deps
|
||||||
|
|
||||||
|
RUN cd /app \
|
||||||
|
&& cd vllm \
|
||||||
|
&& pip install -U -r requirements-rocm.txt \
|
||||||
|
&& bash patch_xformers-0.0.22.post7.rocm.sh \
|
||||||
|
&& python3 setup.py install \
|
||||||
|
&& cd ..
|
||||||
|
|
||||||
|
RUN python3 -m pip install --upgrade pip
|
||||||
|
RUN python3 -m pip install --no-cache-dir ray[all]
|
||||||
|
|
||||||
|
CMD ["/bin/bash"]
|
||||||
11
README.md
11
README.md
@@ -10,13 +10,14 @@ Easy, fast, and cheap LLM serving for everyone
|
|||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
| <a href="https://vllm.readthedocs.io/en/latest/"><b>Documentation</b></a> | <a href="https://vllm.ai"><b>Blog</b></a> | <a href="https://arxiv.org/abs/2309.06180"><b>Paper</b></a> | <a href="https://discord.gg/jz7wjKhh6g"><b>Discord</b></a> |
|
| <a href="https://docs.vllm.ai"><b>Documentation</b></a> | <a href="https://vllm.ai"><b>Blog</b></a> | <a href="https://arxiv.org/abs/2309.06180"><b>Paper</b></a> | <a href="https://discord.gg/jz7wjKhh6g"><b>Discord</b></a> |
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Latest News* 🔥
|
*Latest News* 🔥
|
||||||
|
- [2023/12] Added ROCm support to vLLM.
|
||||||
- [2023/10] We hosted [the first vLLM meetup](https://lu.ma/first-vllm-meetup) in SF! Please find the meetup slides [here](https://docs.google.com/presentation/d/1QL-XPFXiFpDBh86DbEegFXBXFXjix4v032GhShbKf3s/edit?usp=sharing).
|
- [2023/10] We hosted [the first vLLM meetup](https://lu.ma/first-vllm-meetup) in SF! Please find the meetup slides [here](https://docs.google.com/presentation/d/1QL-XPFXiFpDBh86DbEegFXBXFXjix4v032GhShbKf3s/edit?usp=sharing).
|
||||||
- [2023/09] We created our [Discord server](https://discord.gg/jz7wjKhh6g)! Join us to discuss vLLM and LLM serving! We will also post the latest announcements and updates there.
|
- [2023/09] We created our [Discord server](https://discord.gg/jz7wjKhh6g)! Join us to discuss vLLM and LLM serving! We will also post the latest announcements and updates there.
|
||||||
- [2023/09] We released our [PagedAttention paper](https://arxiv.org/abs/2309.06180) on arXiv!
|
- [2023/09] We released our [PagedAttention paper](https://arxiv.org/abs/2309.06180) on arXiv!
|
||||||
@@ -43,11 +44,12 @@ vLLM is flexible and easy to use with:
|
|||||||
- Tensor parallelism support for distributed inference
|
- Tensor parallelism support for distributed inference
|
||||||
- Streaming outputs
|
- Streaming outputs
|
||||||
- OpenAI-compatible API server
|
- OpenAI-compatible API server
|
||||||
|
- Support NVIDIA CUDA and AMD ROCm.
|
||||||
|
|
||||||
vLLM seamlessly supports many Hugging Face models, including the following architectures:
|
vLLM seamlessly supports many Hugging Face models, including the following architectures:
|
||||||
|
|
||||||
- Aquila & Aquila2 (`BAAI/AquilaChat2-7B`, `BAAI/AquilaChat2-34B`, `BAAI/Aquila-7B`, `BAAI/AquilaChat-7B`, etc.)
|
- Aquila & Aquila2 (`BAAI/AquilaChat2-7B`, `BAAI/AquilaChat2-34B`, `BAAI/Aquila-7B`, `BAAI/AquilaChat-7B`, etc.)
|
||||||
- Baichuan (`baichuan-inc/Baichuan-7B`, `baichuan-inc/Baichuan-13B-Chat`, etc.)
|
- Baichuan & Baichuan2 (`baichuan-inc/Baichuan2-13B-Chat`, `baichuan-inc/Baichuan-7B`, etc.)
|
||||||
- BLOOM (`bigscience/bloom`, `bigscience/bloomz`, etc.)
|
- BLOOM (`bigscience/bloom`, `bigscience/bloomz`, etc.)
|
||||||
- ChatGLM (`THUDM/chatglm2-6b`, `THUDM/chatglm3-6b`, etc.)
|
- ChatGLM (`THUDM/chatglm2-6b`, `THUDM/chatglm3-6b`, etc.)
|
||||||
- Falcon (`tiiuae/falcon-7b`, `tiiuae/falcon-40b`, `tiiuae/falcon-rw-7b`, etc.)
|
- Falcon (`tiiuae/falcon-7b`, `tiiuae/falcon-40b`, `tiiuae/falcon-rw-7b`, etc.)
|
||||||
@@ -58,6 +60,7 @@ vLLM seamlessly supports many Hugging Face models, including the following archi
|
|||||||
- InternLM (`internlm/internlm-7b`, `internlm/internlm-chat-7b`, etc.)
|
- InternLM (`internlm/internlm-7b`, `internlm/internlm-chat-7b`, etc.)
|
||||||
- LLaMA & LLaMA-2 (`meta-llama/Llama-2-70b-hf`, `lmsys/vicuna-13b-v1.3`, `young-geng/koala`, `openlm-research/open_llama_13b`, etc.)
|
- LLaMA & LLaMA-2 (`meta-llama/Llama-2-70b-hf`, `lmsys/vicuna-13b-v1.3`, `young-geng/koala`, `openlm-research/open_llama_13b`, etc.)
|
||||||
- Mistral (`mistralai/Mistral-7B-v0.1`, `mistralai/Mistral-7B-Instruct-v0.1`, etc.)
|
- Mistral (`mistralai/Mistral-7B-v0.1`, `mistralai/Mistral-7B-Instruct-v0.1`, etc.)
|
||||||
|
- Mixtral (`mistralai/Mixtral-8x7B-v0.1`, `mistralai/Mixtral-8x7B-Instruct-v0.1`, etc.)
|
||||||
- MPT (`mosaicml/mpt-7b`, `mosaicml/mpt-30b`, etc.)
|
- MPT (`mosaicml/mpt-7b`, `mosaicml/mpt-30b`, etc.)
|
||||||
- OPT (`facebook/opt-66b`, `facebook/opt-iml-max-30b`, etc.)
|
- OPT (`facebook/opt-66b`, `facebook/opt-iml-max-30b`, etc.)
|
||||||
- Phi-1.5 (`microsoft/phi-1_5`, etc.)
|
- Phi-1.5 (`microsoft/phi-1_5`, etc.)
|
||||||
@@ -69,6 +72,10 @@ Install vLLM with pip or [from source](https://vllm.readthedocs.io/en/latest/get
|
|||||||
```bash
|
```bash
|
||||||
pip install vllm
|
pip install vllm
|
||||||
```
|
```
|
||||||
|
**NOTE:** The Mixtral model additionally requires `megablocks` which can be installed with pip or [from source](https://github.com/stanford-futuredata/megablocks) on **Python 3.10**:
|
||||||
|
```bash
|
||||||
|
pip install megablocks
|
||||||
|
```
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
"""Benchmark the latency of processing a single batch of requests."""
|
"""Benchmark the latency of processing a single batch of requests."""
|
||||||
import argparse
|
import argparse
|
||||||
import time
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import torch
|
import torch
|
||||||
@@ -12,7 +14,6 @@ from vllm import LLM, SamplingParams
|
|||||||
def main(args: argparse.Namespace):
|
def main(args: argparse.Namespace):
|
||||||
print(args)
|
print(args)
|
||||||
|
|
||||||
# Process all the requests in a single batch if possible.
|
|
||||||
# NOTE(woosuk): If the request cannot be processed in a single batch,
|
# NOTE(woosuk): If the request cannot be processed in a single batch,
|
||||||
# the engine will automatically process the request in multiple batches.
|
# the engine will automatically process the request in multiple batches.
|
||||||
llm = LLM(
|
llm = LLM(
|
||||||
@@ -20,8 +21,6 @@ def main(args: argparse.Namespace):
|
|||||||
tokenizer=args.tokenizer,
|
tokenizer=args.tokenizer,
|
||||||
quantization=args.quantization,
|
quantization=args.quantization,
|
||||||
tensor_parallel_size=args.tensor_parallel_size,
|
tensor_parallel_size=args.tensor_parallel_size,
|
||||||
max_num_seqs=args.batch_size,
|
|
||||||
max_num_batched_tokens=args.batch_size * args.input_len,
|
|
||||||
trust_remote_code=args.trust_remote_code,
|
trust_remote_code=args.trust_remote_code,
|
||||||
dtype=args.dtype,
|
dtype=args.dtype,
|
||||||
)
|
)
|
||||||
@@ -37,28 +36,43 @@ def main(args: argparse.Namespace):
|
|||||||
print(sampling_params)
|
print(sampling_params)
|
||||||
dummy_prompt_token_ids = [[0] * args.input_len] * args.batch_size
|
dummy_prompt_token_ids = [[0] * args.input_len] * args.batch_size
|
||||||
|
|
||||||
def run_to_completion(profile: bool = False):
|
def run_to_completion(profile_dir: Optional[str] = None):
|
||||||
if profile:
|
if profile_dir:
|
||||||
torch.cuda.cudart().cudaProfilerStart()
|
with torch.profiler.profile(
|
||||||
start_time = time.perf_counter()
|
activities=[
|
||||||
|
torch.profiler.ProfilerActivity.CPU,
|
||||||
llm.generate(prompt_token_ids=dummy_prompt_token_ids,
|
torch.profiler.ProfilerActivity.CUDA,
|
||||||
sampling_params=sampling_params,
|
],
|
||||||
use_tqdm=False)
|
on_trace_ready=torch.profiler.tensorboard_trace_handler(
|
||||||
|
str(profile_dir))) as p:
|
||||||
end_time = time.perf_counter()
|
llm.generate(prompt_token_ids=dummy_prompt_token_ids,
|
||||||
latency = end_time - start_time
|
sampling_params=sampling_params,
|
||||||
if profile:
|
use_tqdm=False)
|
||||||
torch.cuda.cudart().cudaProfilerStop()
|
print(p.key_averages())
|
||||||
return latency
|
else:
|
||||||
|
start_time = time.perf_counter()
|
||||||
|
llm.generate(prompt_token_ids=dummy_prompt_token_ids,
|
||||||
|
sampling_params=sampling_params,
|
||||||
|
use_tqdm=False)
|
||||||
|
end_time = time.perf_counter()
|
||||||
|
latency = end_time - start_time
|
||||||
|
return latency
|
||||||
|
|
||||||
print("Warming up...")
|
print("Warming up...")
|
||||||
run_to_completion(profile=False)
|
run_to_completion(profile_dir=None)
|
||||||
|
|
||||||
|
if args.profile:
|
||||||
|
profile_dir = args.profile_result_dir
|
||||||
|
if not profile_dir:
|
||||||
|
profile_dir = Path(".") / "vllm_benchmark_result" / f"latency_result_{time.time()}"
|
||||||
|
print(f"Profiling (results will be saved to '{profile_dir}')...")
|
||||||
|
run_to_completion(profile_dir=args.profile_result_dir)
|
||||||
|
return
|
||||||
|
|
||||||
# Benchmark.
|
# Benchmark.
|
||||||
latencies = []
|
latencies = []
|
||||||
for _ in tqdm(range(args.num_iters), desc="Profiling iterations"):
|
for _ in tqdm(range(args.num_iters), desc="Profiling iterations"):
|
||||||
latencies.append(run_to_completion(profile=False))
|
latencies.append(run_to_completion(profile_dir=None))
|
||||||
print(f'Avg latency: {np.mean(latencies)} seconds')
|
print(f'Avg latency: {np.mean(latencies)} seconds')
|
||||||
|
|
||||||
|
|
||||||
@@ -97,5 +111,17 @@ if __name__ == '__main__':
|
|||||||
'The "auto" option will use FP16 precision '
|
'The "auto" option will use FP16 precision '
|
||||||
'for FP32 and FP16 models, and BF16 precision '
|
'for FP32 and FP16 models, and BF16 precision '
|
||||||
'for BF16 models.')
|
'for BF16 models.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--profile',
|
||||||
|
action='store_true',
|
||||||
|
help='profile the generation process of a single batch')
|
||||||
|
parser.add_argument(
|
||||||
|
'--profile-result-dir',
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help=(
|
||||||
|
'path to save the pytorch profiler output. Can be visualized '
|
||||||
|
'with ui.perfetto.dev or Tensorboard.'
|
||||||
|
))
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
main(args)
|
main(args)
|
||||||
|
|||||||
@@ -17,9 +17,8 @@ def sample_requests(
|
|||||||
tokenizer: PreTrainedTokenizerBase,
|
tokenizer: PreTrainedTokenizerBase,
|
||||||
fixed_output_len: Optional[int],
|
fixed_output_len: Optional[int],
|
||||||
) -> List[Tuple[str, int, int]]:
|
) -> List[Tuple[str, int, int]]:
|
||||||
if fixed_output_len is not None:
|
if fixed_output_len is not None and fixed_output_len < 4:
|
||||||
if fixed_output_len < 4:
|
raise ValueError("output_len too small")
|
||||||
raise ValueError("output_len too small")
|
|
||||||
|
|
||||||
# Load the dataset.
|
# Load the dataset.
|
||||||
with open(dataset_path) as f:
|
with open(dataset_path) as f:
|
||||||
@@ -70,6 +69,7 @@ def run_vllm(
|
|||||||
use_beam_search: bool,
|
use_beam_search: bool,
|
||||||
trust_remote_code: bool,
|
trust_remote_code: bool,
|
||||||
dtype: str,
|
dtype: str,
|
||||||
|
max_model_len: Optional[int] = None,
|
||||||
) -> float:
|
) -> float:
|
||||||
from vllm import LLM, SamplingParams
|
from vllm import LLM, SamplingParams
|
||||||
llm = LLM(
|
llm = LLM(
|
||||||
@@ -80,6 +80,7 @@ def run_vllm(
|
|||||||
seed=seed,
|
seed=seed,
|
||||||
trust_remote_code=trust_remote_code,
|
trust_remote_code=trust_remote_code,
|
||||||
dtype=dtype,
|
dtype=dtype,
|
||||||
|
max_model_len=max_model_len,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add the requests to the engine.
|
# Add the requests to the engine.
|
||||||
@@ -202,7 +203,8 @@ def main(args: argparse.Namespace):
|
|||||||
elapsed_time = run_vllm(requests, args.model, args.tokenizer,
|
elapsed_time = run_vllm(requests, args.model, args.tokenizer,
|
||||||
args.quantization, args.tensor_parallel_size,
|
args.quantization, args.tensor_parallel_size,
|
||||||
args.seed, args.n, args.use_beam_search,
|
args.seed, args.n, args.use_beam_search,
|
||||||
args.trust_remote_code, args.dtype)
|
args.trust_remote_code, args.dtype,
|
||||||
|
args.max_model_len)
|
||||||
elif args.backend == "hf":
|
elif args.backend == "hf":
|
||||||
assert args.tensor_parallel_size == 1
|
assert args.tensor_parallel_size == 1
|
||||||
elapsed_time = run_hf(requests, args.model, tokenizer, args.n,
|
elapsed_time = run_hf(requests, args.model, tokenizer, args.n,
|
||||||
@@ -262,6 +264,12 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument('--trust-remote-code',
|
parser.add_argument('--trust-remote-code',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='trust remote code from huggingface')
|
help='trust remote code from huggingface')
|
||||||
|
parser.add_argument(
|
||||||
|
'--max-model-len',
|
||||||
|
type=int,
|
||||||
|
default=None,
|
||||||
|
help='Maximum length of a sequence (including prompt and output). '
|
||||||
|
'If None, will be derived from the model.')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--dtype',
|
'--dtype',
|
||||||
type=str,
|
type=str,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import time
|
|||||||
|
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
from vllm import attention_ops
|
from vllm._C import ops
|
||||||
|
|
||||||
NUM_BLOCKS = 1024
|
NUM_BLOCKS = 1024
|
||||||
PARTITION_SIZE = 512
|
PARTITION_SIZE = 512
|
||||||
@@ -37,10 +37,6 @@ def main(
|
|||||||
query.uniform_(-scale, scale)
|
query.uniform_(-scale, scale)
|
||||||
|
|
||||||
assert num_query_heads % num_kv_heads == 0
|
assert num_query_heads % num_kv_heads == 0
|
||||||
num_queries_per_kv = num_query_heads // num_kv_heads
|
|
||||||
head_mapping = torch.repeat_interleave(
|
|
||||||
torch.arange(num_kv_heads, dtype=torch.int32, device="cuda"),
|
|
||||||
num_queries_per_kv)
|
|
||||||
alibi_slopes = None
|
alibi_slopes = None
|
||||||
if use_alibi:
|
if use_alibi:
|
||||||
alibi_slopes = torch.randn(num_query_heads,
|
alibi_slopes = torch.randn(num_query_heads,
|
||||||
@@ -98,12 +94,12 @@ def main(
|
|||||||
|
|
||||||
for _ in range(num_iters):
|
for _ in range(num_iters):
|
||||||
if version == "v1":
|
if version == "v1":
|
||||||
attention_ops.paged_attention_v1(
|
ops.paged_attention_v1(
|
||||||
output,
|
output,
|
||||||
query,
|
query,
|
||||||
key_cache,
|
key_cache,
|
||||||
value_cache,
|
value_cache,
|
||||||
head_mapping,
|
num_kv_heads,
|
||||||
scale,
|
scale,
|
||||||
block_tables,
|
block_tables,
|
||||||
context_lens,
|
context_lens,
|
||||||
@@ -112,7 +108,7 @@ def main(
|
|||||||
alibi_slopes,
|
alibi_slopes,
|
||||||
)
|
)
|
||||||
elif version == "v2":
|
elif version == "v2":
|
||||||
attention_ops.paged_attention_v2(
|
ops.paged_attention_v2(
|
||||||
output,
|
output,
|
||||||
exp_sums,
|
exp_sums,
|
||||||
max_logits,
|
max_logits,
|
||||||
@@ -120,7 +116,7 @@ def main(
|
|||||||
query,
|
query,
|
||||||
key_cache,
|
key_cache,
|
||||||
value_cache,
|
value_cache,
|
||||||
head_mapping,
|
num_kv_heads,
|
||||||
scale,
|
scale,
|
||||||
block_tables,
|
block_tables,
|
||||||
context_lens,
|
context_lens,
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
#include <torch/extension.h>
|
|
||||||
|
|
||||||
void silu_and_mul(
|
|
||||||
torch::Tensor& out,
|
|
||||||
torch::Tensor& input);
|
|
||||||
|
|
||||||
void gelu_new(
|
|
||||||
torch::Tensor& out,
|
|
||||||
torch::Tensor& input);
|
|
||||||
|
|
||||||
void gelu_fast(
|
|
||||||
torch::Tensor& out,
|
|
||||||
torch::Tensor& input);
|
|
||||||
|
|
||||||
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
|
||||||
m.def(
|
|
||||||
"silu_and_mul",
|
|
||||||
&silu_and_mul,
|
|
||||||
"Activation function used in SwiGLU.");
|
|
||||||
m.def(
|
|
||||||
"gelu_new",
|
|
||||||
&gelu_new,
|
|
||||||
"GELU implementation used in GPT-2.");
|
|
||||||
m.def(
|
|
||||||
"gelu_fast",
|
|
||||||
&gelu_fast,
|
|
||||||
"Approximate GELU implementation.");
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <torch/extension.h>
|
#include <torch/extension.h>
|
||||||
#include <ATen/cuda/CUDAContext.h>
|
#include <ATen/cuda/CUDAContext.h>
|
||||||
|
|
||||||
|
#include "cuda_compat.h"
|
||||||
#include "dispatch_utils.h"
|
#include "dispatch_utils.h"
|
||||||
|
|
||||||
namespace vllm {
|
namespace vllm {
|
||||||
@@ -18,8 +19,8 @@ __global__ void silu_and_mul_kernel(
|
|||||||
const int d) {
|
const int d) {
|
||||||
const int64_t token_idx = blockIdx.x;
|
const int64_t token_idx = blockIdx.x;
|
||||||
for (int64_t idx = threadIdx.x; idx < d; idx += blockDim.x) {
|
for (int64_t idx = threadIdx.x; idx < d; idx += blockDim.x) {
|
||||||
const scalar_t x = __ldg(&input[token_idx * 2 * d + idx]);
|
const scalar_t x = VLLM_LDG(&input[token_idx * 2 * d + idx]);
|
||||||
const scalar_t y = __ldg(&input[token_idx * 2 * d + d + idx]);
|
const scalar_t y = VLLM_LDG(&input[token_idx * 2 * d + d + idx]);
|
||||||
out[token_idx * d + idx] = silu(x) * y;
|
out[token_idx * d + idx] = silu(x) * y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,7 +58,7 @@ __global__ void activation_kernel(
|
|||||||
const int d) {
|
const int d) {
|
||||||
const int64_t token_idx = blockIdx.x;
|
const int64_t token_idx = blockIdx.x;
|
||||||
for (int64_t idx = threadIdx.x; idx < d; idx += blockDim.x) {
|
for (int64_t idx = threadIdx.x; idx < d; idx += blockDim.x) {
|
||||||
const scalar_t x = __ldg(&input[token_idx * d + idx]);
|
const scalar_t x = VLLM_LDG(&input[token_idx * d + idx]);
|
||||||
out[token_idx * d + idx] = ACT_FN(x);
|
out[token_idx * d + idx] = ACT_FN(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
#include <torch/extension.h>
|
|
||||||
#include <c10/util/Optional.h>
|
|
||||||
|
|
||||||
void paged_attention_v1(
|
|
||||||
torch::Tensor& out,
|
|
||||||
torch::Tensor& query,
|
|
||||||
torch::Tensor& key_cache,
|
|
||||||
torch::Tensor& value_cache,
|
|
||||||
torch::Tensor& head_mapping,
|
|
||||||
float scale,
|
|
||||||
torch::Tensor& block_tables,
|
|
||||||
torch::Tensor& context_lens,
|
|
||||||
int block_size,
|
|
||||||
int max_context_len,
|
|
||||||
const c10::optional<torch::Tensor>& alibi_slopes);
|
|
||||||
|
|
||||||
void paged_attention_v2(
|
|
||||||
torch::Tensor& out,
|
|
||||||
torch::Tensor& exp_sums,
|
|
||||||
torch::Tensor& max_logits,
|
|
||||||
torch::Tensor& tmp_out,
|
|
||||||
torch::Tensor& query,
|
|
||||||
torch::Tensor& key_cache,
|
|
||||||
torch::Tensor& value_cache,
|
|
||||||
torch::Tensor& head_mapping,
|
|
||||||
float scale,
|
|
||||||
torch::Tensor& block_tables,
|
|
||||||
torch::Tensor& context_lens,
|
|
||||||
int block_size,
|
|
||||||
int max_context_len,
|
|
||||||
const c10::optional<torch::Tensor>& alibi_slopes);
|
|
||||||
|
|
||||||
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
|
||||||
m.def(
|
|
||||||
"paged_attention_v1",
|
|
||||||
&paged_attention_v1,
|
|
||||||
"Compute the attention between an input query and the cached keys/values using PagedAttention.");
|
|
||||||
m.def(
|
|
||||||
"paged_attention_v2",
|
|
||||||
&paged_attention_v2,
|
|
||||||
"PagedAttention V2.");
|
|
||||||
}
|
|
||||||
@@ -15,6 +15,10 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
#ifdef USE_ROCM
|
||||||
|
#include <hip/hip_runtime.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <torch/extension.h>
|
#include <torch/extension.h>
|
||||||
#include <ATen/cuda/CUDAContext.h>
|
#include <ATen/cuda/CUDAContext.h>
|
||||||
|
|
||||||
@@ -23,7 +27,11 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#ifndef USE_ROCM
|
||||||
#define WARP_SIZE 32
|
#define WARP_SIZE 32
|
||||||
|
#else
|
||||||
|
#define WARP_SIZE warpSize
|
||||||
|
#endif
|
||||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
#define DIVIDE_ROUND_UP(a, b) (((a) + (b) - 1) / (b))
|
#define DIVIDE_ROUND_UP(a, b) (((a) + (b) - 1) / (b))
|
||||||
@@ -40,7 +48,7 @@ inline __device__ float block_sum(float* red_smem, float sum) {
|
|||||||
// Compute the sum per warp.
|
// Compute the sum per warp.
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int mask = WARP_SIZE / 2; mask >= 1; mask /= 2) {
|
for (int mask = WARP_SIZE / 2; mask >= 1; mask /= 2) {
|
||||||
sum += __shfl_xor_sync(uint32_t(-1), sum, mask);
|
sum += VLLM_SHFL_XOR_SYNC(sum, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warp leaders store the data to shared memory.
|
// Warp leaders store the data to shared memory.
|
||||||
@@ -59,11 +67,11 @@ inline __device__ float block_sum(float* red_smem, float sum) {
|
|||||||
// Parallel reduction inside the warp.
|
// Parallel reduction inside the warp.
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int mask = NUM_WARPS / 2; mask >= 1; mask /= 2) {
|
for (int mask = NUM_WARPS / 2; mask >= 1; mask /= 2) {
|
||||||
sum += __shfl_xor_sync(uint32_t(-1), sum, mask);
|
sum += VLLM_SHFL_XOR_SYNC(sum, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast to other threads.
|
// Broadcast to other threads.
|
||||||
return __shfl_sync(uint32_t(-1), sum, 0);
|
return VLLM_SHFL_SYNC(sum, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(woosuk): Merge the last two dimensions of the grid.
|
// TODO(woosuk): Merge the last two dimensions of the grid.
|
||||||
@@ -81,7 +89,7 @@ __device__ void paged_attention_kernel(
|
|||||||
const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
|
const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
|
||||||
const scalar_t* __restrict__ k_cache, // [num_blocks, num_kv_heads, head_size/x, block_size, x]
|
const scalar_t* __restrict__ k_cache, // [num_blocks, num_kv_heads, head_size/x, block_size, x]
|
||||||
const scalar_t* __restrict__ v_cache, // [num_blocks, num_kv_heads, head_size, block_size]
|
const scalar_t* __restrict__ v_cache, // [num_blocks, num_kv_heads, head_size, block_size]
|
||||||
const int* __restrict__ head_mapping, // [num_heads]
|
const int num_kv_heads, // [num_heads]
|
||||||
const float scale,
|
const float scale,
|
||||||
const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
|
const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
|
||||||
const int* __restrict__ context_lens, // [num_seqs]
|
const int* __restrict__ context_lens, // [num_seqs]
|
||||||
@@ -124,7 +132,8 @@ __device__ void paged_attention_kernel(
|
|||||||
|
|
||||||
const int head_idx = blockIdx.x;
|
const int head_idx = blockIdx.x;
|
||||||
const int num_heads = gridDim.x;
|
const int num_heads = gridDim.x;
|
||||||
const int kv_head_idx = head_mapping[head_idx];
|
const int num_queries_per_kv = num_heads / num_kv_heads;
|
||||||
|
const int kv_head_idx = head_idx / num_queries_per_kv;
|
||||||
const float alibi_slope = alibi_slopes == nullptr ? 0.f : alibi_slopes[head_idx];
|
const float alibi_slope = alibi_slopes == nullptr ? 0.f : alibi_slopes[head_idx];
|
||||||
|
|
||||||
// A vector type to store a part of a key or a query.
|
// A vector type to store a part of a key or a query.
|
||||||
@@ -223,7 +232,7 @@ __device__ void paged_attention_kernel(
|
|||||||
// The 0-th thread of each thread group already has its max qk value.
|
// The 0-th thread of each thread group already has its max qk value.
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int mask = WARP_SIZE / 2; mask >= THREAD_GROUP_SIZE; mask /= 2) {
|
for (int mask = WARP_SIZE / 2; mask >= THREAD_GROUP_SIZE; mask /= 2) {
|
||||||
qk_max = fmaxf(qk_max, __shfl_xor_sync(uint32_t(-1), qk_max, mask));
|
qk_max = fmaxf(qk_max, VLLM_SHFL_XOR_SYNC(qk_max, mask));
|
||||||
}
|
}
|
||||||
if (lane == 0) {
|
if (lane == 0) {
|
||||||
red_smem[warp_idx] = qk_max;
|
red_smem[warp_idx] = qk_max;
|
||||||
@@ -235,10 +244,10 @@ __device__ void paged_attention_kernel(
|
|||||||
qk_max = lane < NUM_WARPS ? red_smem[lane] : -FLT_MAX;
|
qk_max = lane < NUM_WARPS ? red_smem[lane] : -FLT_MAX;
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int mask = NUM_WARPS / 2; mask >= 1; mask /= 2) {
|
for (int mask = NUM_WARPS / 2; mask >= 1; mask /= 2) {
|
||||||
qk_max = fmaxf(qk_max, __shfl_xor_sync(uint32_t(-1), qk_max, mask));
|
qk_max = fmaxf(qk_max, VLLM_SHFL_XOR_SYNC(qk_max, mask));
|
||||||
}
|
}
|
||||||
// Broadcast the max qk value to all threads.
|
// Broadcast the max qk value to all threads.
|
||||||
qk_max = __shfl_sync(uint32_t(-1), qk_max, 0);
|
qk_max = VLLM_SHFL_SYNC(qk_max, 0);
|
||||||
|
|
||||||
// Get the sum of the exp values.
|
// Get the sum of the exp values.
|
||||||
float exp_sum = 0.f;
|
float exp_sum = 0.f;
|
||||||
@@ -326,7 +335,7 @@ __device__ void paged_attention_kernel(
|
|||||||
float acc = accs[i];
|
float acc = accs[i];
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int mask = NUM_V_VECS_PER_ROW / 2; mask >= 1; mask /= 2) {
|
for (int mask = NUM_V_VECS_PER_ROW / 2; mask >= 1; mask /= 2) {
|
||||||
acc += __shfl_xor_sync(uint32_t(-1), acc, mask);
|
acc += VLLM_SHFL_XOR_SYNC(acc, mask);
|
||||||
}
|
}
|
||||||
accs[i] = acc;
|
accs[i] = acc;
|
||||||
}
|
}
|
||||||
@@ -393,7 +402,7 @@ __global__ void paged_attention_v1_kernel(
|
|||||||
const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
|
const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
|
||||||
const scalar_t* __restrict__ k_cache, // [num_blocks, num_kv_heads, head_size/x, block_size, x]
|
const scalar_t* __restrict__ k_cache, // [num_blocks, num_kv_heads, head_size/x, block_size, x]
|
||||||
const scalar_t* __restrict__ v_cache, // [num_blocks, num_kv_heads, head_size, block_size]
|
const scalar_t* __restrict__ v_cache, // [num_blocks, num_kv_heads, head_size, block_size]
|
||||||
const int* __restrict__ head_mapping, // [num_heads]
|
const int num_kv_heads, // [num_heads]
|
||||||
const float scale,
|
const float scale,
|
||||||
const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
|
const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
|
||||||
const int* __restrict__ context_lens, // [num_seqs]
|
const int* __restrict__ context_lens, // [num_seqs]
|
||||||
@@ -404,7 +413,7 @@ __global__ void paged_attention_v1_kernel(
|
|||||||
const int kv_head_stride) {
|
const int kv_head_stride) {
|
||||||
paged_attention_kernel<scalar_t, HEAD_SIZE, BLOCK_SIZE, NUM_THREADS>(
|
paged_attention_kernel<scalar_t, HEAD_SIZE, BLOCK_SIZE, NUM_THREADS>(
|
||||||
/* exp_sums */ nullptr, /* max_logits */ nullptr,
|
/* exp_sums */ nullptr, /* max_logits */ nullptr,
|
||||||
out, q, k_cache, v_cache, head_mapping, scale, block_tables, context_lens,
|
out, q, k_cache, v_cache, num_kv_heads, scale, block_tables, context_lens,
|
||||||
max_num_blocks_per_seq, alibi_slopes, q_stride, kv_block_stride, kv_head_stride);
|
max_num_blocks_per_seq, alibi_slopes, q_stride, kv_block_stride, kv_head_stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,7 +431,7 @@ __global__ void paged_attention_v2_kernel(
|
|||||||
const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
|
const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
|
||||||
const scalar_t* __restrict__ k_cache, // [num_blocks, num_kv_heads, head_size/x, block_size, x]
|
const scalar_t* __restrict__ k_cache, // [num_blocks, num_kv_heads, head_size/x, block_size, x]
|
||||||
const scalar_t* __restrict__ v_cache, // [num_blocks, num_kv_heads, head_size, block_size]
|
const scalar_t* __restrict__ v_cache, // [num_blocks, num_kv_heads, head_size, block_size]
|
||||||
const int* __restrict__ head_mapping, // [num_heads]
|
const int num_kv_heads, // [num_heads]
|
||||||
const float scale,
|
const float scale,
|
||||||
const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
|
const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
|
||||||
const int* __restrict__ context_lens, // [num_seqs]
|
const int* __restrict__ context_lens, // [num_seqs]
|
||||||
@@ -432,7 +441,7 @@ __global__ void paged_attention_v2_kernel(
|
|||||||
const int kv_block_stride,
|
const int kv_block_stride,
|
||||||
const int kv_head_stride) {
|
const int kv_head_stride) {
|
||||||
paged_attention_kernel<scalar_t, HEAD_SIZE, BLOCK_SIZE, NUM_THREADS, PARTITION_SIZE>(
|
paged_attention_kernel<scalar_t, HEAD_SIZE, BLOCK_SIZE, NUM_THREADS, PARTITION_SIZE>(
|
||||||
exp_sums, max_logits, tmp_out, q, k_cache, v_cache, head_mapping, scale,
|
exp_sums, max_logits, tmp_out, q, k_cache, v_cache, num_kv_heads, scale,
|
||||||
block_tables, context_lens, max_num_blocks_per_seq, alibi_slopes,
|
block_tables, context_lens, max_num_blocks_per_seq, alibi_slopes,
|
||||||
q_stride, kv_block_stride, kv_head_stride);
|
q_stride, kv_block_stride, kv_head_stride);
|
||||||
}
|
}
|
||||||
@@ -492,7 +501,7 @@ __global__ void paged_attention_v2_reduce_kernel(
|
|||||||
// Reduce within the warp.
|
// Reduce within the warp.
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int mask = WARP_SIZE / 2; mask >= 1; mask /= 2) {
|
for (int mask = WARP_SIZE / 2; mask >= 1; mask /= 2) {
|
||||||
max_logit = fmaxf(max_logit, __shfl_xor_sync(uint32_t(-1), max_logit, mask));
|
max_logit = fmaxf(max_logit, VLLM_SHFL_XOR_SYNC(max_logit, mask));
|
||||||
}
|
}
|
||||||
if (lane == 0) {
|
if (lane == 0) {
|
||||||
red_smem[warp_idx] = max_logit;
|
red_smem[warp_idx] = max_logit;
|
||||||
@@ -502,10 +511,10 @@ __global__ void paged_attention_v2_reduce_kernel(
|
|||||||
max_logit = lane < NUM_WARPS ? red_smem[lane] : -FLT_MAX;
|
max_logit = lane < NUM_WARPS ? red_smem[lane] : -FLT_MAX;
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int mask = NUM_WARPS / 2; mask >= 1; mask /= 2) {
|
for (int mask = NUM_WARPS / 2; mask >= 1; mask /= 2) {
|
||||||
max_logit = fmaxf(max_logit, __shfl_xor_sync(uint32_t(-1), max_logit, mask));
|
max_logit = fmaxf(max_logit, VLLM_SHFL_XOR_SYNC(max_logit, mask));
|
||||||
}
|
}
|
||||||
// Broadcast the max value to all threads.
|
// Broadcast the max value to all threads.
|
||||||
max_logit = __shfl_sync(uint32_t(-1), max_logit, 0);
|
max_logit = VLLM_SHFL_SYNC(max_logit, 0);
|
||||||
|
|
||||||
// Load rescaled exp sums to shared memory.
|
// Load rescaled exp sums to shared memory.
|
||||||
float* shared_exp_sums = reinterpret_cast<float*>(shared_mem + sizeof(float) * num_partitions);
|
float* shared_exp_sums = reinterpret_cast<float*>(shared_mem + sizeof(float) * num_partitions);
|
||||||
@@ -539,16 +548,16 @@ __global__ void paged_attention_v2_reduce_kernel(
|
|||||||
} // namespace vllm
|
} // namespace vllm
|
||||||
|
|
||||||
#define LAUNCH_PAGED_ATTENTION_V1(HEAD_SIZE) \
|
#define LAUNCH_PAGED_ATTENTION_V1(HEAD_SIZE) \
|
||||||
cudaFuncSetAttribute( \
|
VLLM_DevFuncAttribute_SET_MaxDynamicSharedMemorySize( \
|
||||||
vllm::paged_attention_v1_kernel<T, HEAD_SIZE, BLOCK_SIZE, NUM_THREADS>, \
|
((void*)vllm::paged_attention_v1_kernel<T, HEAD_SIZE, BLOCK_SIZE, NUM_THREADS>), \
|
||||||
cudaFuncAttributeMaxDynamicSharedMemorySize, shared_mem_size); \
|
shared_mem_size); \
|
||||||
vllm::paged_attention_v1_kernel<T, HEAD_SIZE, BLOCK_SIZE, NUM_THREADS> \
|
vllm::paged_attention_v1_kernel<T, HEAD_SIZE, BLOCK_SIZE, NUM_THREADS> \
|
||||||
<<<grid, block, shared_mem_size, stream>>>( \
|
<<<grid, block, shared_mem_size, stream>>>( \
|
||||||
out_ptr, \
|
out_ptr, \
|
||||||
query_ptr, \
|
query_ptr, \
|
||||||
key_cache_ptr, \
|
key_cache_ptr, \
|
||||||
value_cache_ptr, \
|
value_cache_ptr, \
|
||||||
head_mapping_ptr, \
|
num_kv_heads, \
|
||||||
scale, \
|
scale, \
|
||||||
block_tables_ptr, \
|
block_tables_ptr, \
|
||||||
context_lens_ptr, \
|
context_lens_ptr, \
|
||||||
@@ -568,7 +577,7 @@ void paged_attention_v1_launcher(
|
|||||||
torch::Tensor& query,
|
torch::Tensor& query,
|
||||||
torch::Tensor& key_cache,
|
torch::Tensor& key_cache,
|
||||||
torch::Tensor& value_cache,
|
torch::Tensor& value_cache,
|
||||||
torch::Tensor& head_mapping,
|
int num_kv_heads,
|
||||||
float scale,
|
float scale,
|
||||||
torch::Tensor& block_tables,
|
torch::Tensor& block_tables,
|
||||||
torch::Tensor& context_lens,
|
torch::Tensor& context_lens,
|
||||||
@@ -594,7 +603,6 @@ void paged_attention_v1_launcher(
|
|||||||
T* query_ptr = reinterpret_cast<T*>(query.data_ptr());
|
T* query_ptr = reinterpret_cast<T*>(query.data_ptr());
|
||||||
T* key_cache_ptr = reinterpret_cast<T*>(key_cache.data_ptr());
|
T* key_cache_ptr = reinterpret_cast<T*>(key_cache.data_ptr());
|
||||||
T* value_cache_ptr = reinterpret_cast<T*>(value_cache.data_ptr());
|
T* value_cache_ptr = reinterpret_cast<T*>(value_cache.data_ptr());
|
||||||
int* head_mapping_ptr = reinterpret_cast<int*>(head_mapping.data_ptr());
|
|
||||||
int* block_tables_ptr = block_tables.data_ptr<int>();
|
int* block_tables_ptr = block_tables.data_ptr<int>();
|
||||||
int* context_lens_ptr = context_lens.data_ptr<int>();
|
int* context_lens_ptr = context_lens.data_ptr<int>();
|
||||||
|
|
||||||
@@ -643,7 +651,7 @@ void paged_attention_v1_launcher(
|
|||||||
query, \
|
query, \
|
||||||
key_cache, \
|
key_cache, \
|
||||||
value_cache, \
|
value_cache, \
|
||||||
head_mapping, \
|
num_kv_heads, \
|
||||||
scale, \
|
scale, \
|
||||||
block_tables, \
|
block_tables, \
|
||||||
context_lens, \
|
context_lens, \
|
||||||
@@ -673,7 +681,7 @@ void paged_attention_v1(
|
|||||||
torch::Tensor& query, // [num_seqs, num_heads, head_size]
|
torch::Tensor& query, // [num_seqs, num_heads, head_size]
|
||||||
torch::Tensor& key_cache, // [num_blocks, num_heads, head_size/x, block_size, x]
|
torch::Tensor& key_cache, // [num_blocks, num_heads, head_size/x, block_size, x]
|
||||||
torch::Tensor& value_cache, // [num_blocks, num_heads, head_size, block_size]
|
torch::Tensor& value_cache, // [num_blocks, num_heads, head_size, block_size]
|
||||||
torch::Tensor& head_mapping, // [num_heads]
|
int num_kv_heads, // [num_heads]
|
||||||
float scale,
|
float scale,
|
||||||
torch::Tensor& block_tables, // [num_seqs, max_num_blocks_per_seq]
|
torch::Tensor& block_tables, // [num_seqs, max_num_blocks_per_seq]
|
||||||
torch::Tensor& context_lens, // [num_seqs]
|
torch::Tensor& context_lens, // [num_seqs]
|
||||||
@@ -700,7 +708,7 @@ void paged_attention_v1(
|
|||||||
query_ptr, \
|
query_ptr, \
|
||||||
key_cache_ptr, \
|
key_cache_ptr, \
|
||||||
value_cache_ptr, \
|
value_cache_ptr, \
|
||||||
head_mapping_ptr, \
|
num_kv_heads, \
|
||||||
scale, \
|
scale, \
|
||||||
block_tables_ptr, \
|
block_tables_ptr, \
|
||||||
context_lens_ptr, \
|
context_lens_ptr, \
|
||||||
@@ -731,7 +739,7 @@ void paged_attention_v2_launcher(
|
|||||||
torch::Tensor& query,
|
torch::Tensor& query,
|
||||||
torch::Tensor& key_cache,
|
torch::Tensor& key_cache,
|
||||||
torch::Tensor& value_cache,
|
torch::Tensor& value_cache,
|
||||||
torch::Tensor& head_mapping,
|
int num_kv_heads,
|
||||||
float scale,
|
float scale,
|
||||||
torch::Tensor& block_tables,
|
torch::Tensor& block_tables,
|
||||||
torch::Tensor& context_lens,
|
torch::Tensor& context_lens,
|
||||||
@@ -760,7 +768,6 @@ void paged_attention_v2_launcher(
|
|||||||
T* query_ptr = reinterpret_cast<T*>(query.data_ptr());
|
T* query_ptr = reinterpret_cast<T*>(query.data_ptr());
|
||||||
T* key_cache_ptr = reinterpret_cast<T*>(key_cache.data_ptr());
|
T* key_cache_ptr = reinterpret_cast<T*>(key_cache.data_ptr());
|
||||||
T* value_cache_ptr = reinterpret_cast<T*>(value_cache.data_ptr());
|
T* value_cache_ptr = reinterpret_cast<T*>(value_cache.data_ptr());
|
||||||
int* head_mapping_ptr = reinterpret_cast<int*>(head_mapping.data_ptr());
|
|
||||||
int* block_tables_ptr = block_tables.data_ptr<int>();
|
int* block_tables_ptr = block_tables.data_ptr<int>();
|
||||||
int* context_lens_ptr = context_lens.data_ptr<int>();
|
int* context_lens_ptr = context_lens.data_ptr<int>();
|
||||||
|
|
||||||
@@ -815,7 +822,7 @@ void paged_attention_v2_launcher(
|
|||||||
query, \
|
query, \
|
||||||
key_cache, \
|
key_cache, \
|
||||||
value_cache, \
|
value_cache, \
|
||||||
head_mapping, \
|
num_kv_heads, \
|
||||||
scale, \
|
scale, \
|
||||||
block_tables, \
|
block_tables, \
|
||||||
context_lens, \
|
context_lens, \
|
||||||
@@ -848,7 +855,7 @@ void paged_attention_v2(
|
|||||||
torch::Tensor& query, // [num_seqs, num_heads, head_size]
|
torch::Tensor& query, // [num_seqs, num_heads, head_size]
|
||||||
torch::Tensor& key_cache, // [num_blocks, num_heads, head_size/x, block_size, x]
|
torch::Tensor& key_cache, // [num_blocks, num_heads, head_size/x, block_size, x]
|
||||||
torch::Tensor& value_cache, // [num_blocks, num_heads, head_size, block_size]
|
torch::Tensor& value_cache, // [num_blocks, num_heads, head_size, block_size]
|
||||||
torch::Tensor& head_mapping, // [num_heads]
|
int num_kv_heads, // [num_heads]
|
||||||
float scale,
|
float scale,
|
||||||
torch::Tensor& block_tables, // [num_seqs, max_num_blocks_per_seq]
|
torch::Tensor& block_tables, // [num_seqs, max_num_blocks_per_seq]
|
||||||
torch::Tensor& context_lens, // [num_seqs]
|
torch::Tensor& context_lens, // [num_seqs]
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "../cuda_compat.h"
|
||||||
#include "attention_dtypes.h"
|
#include "attention_dtypes.h"
|
||||||
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
@@ -39,7 +40,7 @@ inline __device__ float qk_dot_(const Vec (&q)[N], const Vec (&k)[N]) {
|
|||||||
float qk = sum(qk_vec);
|
float qk = sum(qk_vec);
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int mask = THREAD_GROUP_SIZE / 2; mask >= 1; mask /= 2) {
|
for (int mask = THREAD_GROUP_SIZE / 2; mask >= 1; mask /= 2) {
|
||||||
qk += __shfl_xor_sync(uint32_t(-1), qk, mask);
|
qk += VLLM_SHFL_XOR_SYNC(qk, mask);
|
||||||
}
|
}
|
||||||
return qk;
|
return qk;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,17 @@
|
|||||||
#include "attention_generic.cuh"
|
#include "attention_generic.cuh"
|
||||||
#include "dtype_float32.cuh"
|
#include "dtype_float32.cuh"
|
||||||
|
|
||||||
#include <cuda_bf16.h>
|
#ifndef USE_ROCM
|
||||||
#include <cuda_fp16.h>
|
#include <cuda_bf16.h>
|
||||||
|
#include <cuda_fp16.h>
|
||||||
|
#else
|
||||||
|
#include <hip/hip_bf16.h>
|
||||||
|
#include <hip/hip_fp16.h>
|
||||||
|
|
||||||
|
typedef __hip_bfloat162 __nv_bfloat162;
|
||||||
|
typedef __hip_bfloat16 __nv_bfloat16;
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace vllm {
|
namespace vllm {
|
||||||
@@ -98,7 +107,11 @@ inline __device__ __nv_bfloat16 add(__nv_bfloat16 a, __nv_bfloat16 b) {
|
|||||||
#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
|
#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800
|
||||||
assert(false);
|
assert(false);
|
||||||
#else
|
#else
|
||||||
return a + b;
|
#ifndef USE_ROCM
|
||||||
|
return a + b;
|
||||||
|
#else
|
||||||
|
return __hadd(a, b);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,10 @@
|
|||||||
#include "attention_generic.cuh"
|
#include "attention_generic.cuh"
|
||||||
#include "dtype_float32.cuh"
|
#include "dtype_float32.cuh"
|
||||||
|
|
||||||
|
#ifdef USE_ROCM
|
||||||
|
#include <hip/hip_fp16.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace vllm {
|
namespace vllm {
|
||||||
@@ -63,21 +67,47 @@ struct FloatVec<uint4> {
|
|||||||
|
|
||||||
// Utility functions for type conversions.
|
// Utility functions for type conversions.
|
||||||
inline __device__ uint32_t h0_h0(uint16_t a) {
|
inline __device__ uint32_t h0_h0(uint16_t a) {
|
||||||
|
#ifndef USE_ROCM
|
||||||
uint32_t b;
|
uint32_t b;
|
||||||
asm volatile("mov.b32 %0, {%1, %1};" : "=r"(b) : "h"(a));
|
asm volatile("mov.b32 %0, {%1, %1};" : "=r"(b) : "h"(a));
|
||||||
return b;
|
return b;
|
||||||
|
#else
|
||||||
|
union {
|
||||||
|
uint32_t u32;
|
||||||
|
uint16_t u16[2];
|
||||||
|
} tmp;
|
||||||
|
tmp.u16[0] = a;
|
||||||
|
tmp.u16[1] = a;
|
||||||
|
return tmp.u32;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline __device__ float half_to_float(uint16_t h) {
|
inline __device__ float half_to_float(uint16_t h) {
|
||||||
float f;
|
float f;
|
||||||
|
#ifndef USE_ROCM
|
||||||
asm volatile("cvt.f32.f16 %0, %1;\n" : "=f"(f) : "h"(h));
|
asm volatile("cvt.f32.f16 %0, %1;\n" : "=f"(f) : "h"(h));
|
||||||
|
#else
|
||||||
|
asm volatile("v_cvt_f32_f16 %0, %1;" : "=v"(f) : "v"(h));
|
||||||
|
#endif
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline __device__ float2 half2_to_float2(uint32_t v) {
|
inline __device__ float2 half2_to_float2(uint32_t v) {
|
||||||
|
#ifndef USE_ROCM
|
||||||
uint16_t lo, hi;
|
uint16_t lo, hi;
|
||||||
asm volatile("mov.b32 {%0, %1}, %2;\n" : "=h"(lo), "=h"(hi) : "r"(v));
|
asm volatile("mov.b32 {%0, %1}, %2;\n" : "=h"(lo), "=h"(hi) : "r"(v));
|
||||||
return make_float2(half_to_float(lo), half_to_float(hi));
|
return make_float2(half_to_float(lo), half_to_float(hi));
|
||||||
|
#else
|
||||||
|
union {
|
||||||
|
uint32_t u32;
|
||||||
|
uint16_t u16[2];
|
||||||
|
} tmp;
|
||||||
|
tmp.u32 = v;
|
||||||
|
float2 ret;
|
||||||
|
ret.x = half_to_float(tmp.u16[0]);
|
||||||
|
ret.y = half_to_float(tmp.u16[1]);
|
||||||
|
return ret;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline __device__ uint16_t float_to_half(float f) {
|
inline __device__ uint16_t float_to_half(float f) {
|
||||||
@@ -85,7 +115,11 @@ inline __device__ uint16_t float_to_half(float f) {
|
|||||||
uint32_t u32;
|
uint32_t u32;
|
||||||
uint16_t u16[2];
|
uint16_t u16[2];
|
||||||
} tmp;
|
} tmp;
|
||||||
|
#ifndef USE_ROCM
|
||||||
asm volatile("cvt.rn.f16.f32 %0, %1;\n" : "=h"(tmp.u16[0]) : "f"(f));
|
asm volatile("cvt.rn.f16.f32 %0, %1;\n" : "=h"(tmp.u16[0]) : "f"(f));
|
||||||
|
#else
|
||||||
|
asm volatile("v_cvt_f16_f32 %0, %1;\n" : "=v"(tmp.u32) : "v"(f));
|
||||||
|
#endif
|
||||||
return tmp.u16[0];
|
return tmp.u16[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,12 +128,16 @@ inline __device__ uint32_t float2_to_half2(float2 f) {
|
|||||||
uint32_t u32;
|
uint32_t u32;
|
||||||
uint16_t u16[2];
|
uint16_t u16[2];
|
||||||
} tmp;
|
} tmp;
|
||||||
|
#ifndef USE_ROCM
|
||||||
#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 800
|
#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 800
|
||||||
asm volatile("cvt.rn.f16x2.f32 %0, %1, %2;\n" : "=r"(tmp.u32) : "f"(f.y), "f"(f.x));
|
asm volatile("cvt.rn.f16x2.f32 %0, %1, %2;\n" : "=r"(tmp.u32) : "f"(f.y), "f"(f.x));
|
||||||
|
#else
|
||||||
|
asm volatile("cvt.rn.f16.f32 %0, %1;\n" : "=h"(tmp.u16[0]) : "f"(f.x));
|
||||||
|
asm volatile("cvt.rn.f16.f32 %0, %1;\n" : "=h"(tmp.u16[1]) : "f"(f.y));
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
asm volatile("cvt.rn.f16.f32 %0, %1;\n" : "=h"(tmp.u16[0]) : "f"(f.x));
|
tmp.u16[0] = float_to_half(f.x);
|
||||||
asm volatile("cvt.rn.f16.f32 %0, %1;\n" : "=h"(tmp.u16[1]) : "f"(f.y));
|
tmp.u16[1] = float_to_half(f.y);
|
||||||
#endif
|
#endif
|
||||||
return tmp.u32;
|
return tmp.u32;
|
||||||
}
|
}
|
||||||
@@ -107,13 +145,21 @@ inline __device__ uint32_t float2_to_half2(float2 f) {
|
|||||||
// Vector addition.
|
// Vector addition.
|
||||||
inline __device__ uint16_t add(uint16_t a, uint16_t b) {
|
inline __device__ uint16_t add(uint16_t a, uint16_t b) {
|
||||||
uint16_t c;
|
uint16_t c;
|
||||||
|
#ifndef USE_ROCM
|
||||||
asm volatile("add.f16 %0, %1, %2;\n" : "=h"(c) : "h"(a), "h"(b));
|
asm volatile("add.f16 %0, %1, %2;\n" : "=h"(c) : "h"(a), "h"(b));
|
||||||
|
#else
|
||||||
|
asm volatile("v_add_f16 %0, %1, %2;\n" : "=v"(c) : "v"(a), "v"(b));
|
||||||
|
#endif
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline __device__ uint32_t add(uint32_t a, uint32_t b) {
|
inline __device__ uint32_t add(uint32_t a, uint32_t b) {
|
||||||
uint32_t c;
|
uint32_t c;
|
||||||
|
#ifndef USE_ROCM
|
||||||
asm volatile("add.f16x2 %0, %1, %2;\n" : "=r"(c) : "r"(a), "r"(b));
|
asm volatile("add.f16x2 %0, %1, %2;\n" : "=r"(c) : "r"(a), "r"(b));
|
||||||
|
#else
|
||||||
|
asm volatile("v_pk_add_f16 %0, %1, %2;\n" : "=v"(c) : "v"(a), "v"(b));
|
||||||
|
#endif
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,14 +204,22 @@ inline __device__ Float8_ add(uint4 a, Float8_ fb) {
|
|||||||
template<>
|
template<>
|
||||||
inline __device__ uint16_t mul(uint16_t a, uint16_t b) {
|
inline __device__ uint16_t mul(uint16_t a, uint16_t b) {
|
||||||
uint16_t c;
|
uint16_t c;
|
||||||
|
#ifndef USE_ROCM
|
||||||
asm volatile("mul.f16 %0, %1, %2;\n" : "=h"(c) : "h"(a), "h"(b));
|
asm volatile("mul.f16 %0, %1, %2;\n" : "=h"(c) : "h"(a), "h"(b));
|
||||||
|
#else
|
||||||
|
asm volatile("v_mul_f16 %0, %1, %2;\n" : "=v"(c) : "v"(a), "v"(b));
|
||||||
|
#endif
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline __device__ uint32_t mul(uint32_t a, uint32_t b) {
|
inline __device__ uint32_t mul(uint32_t a, uint32_t b) {
|
||||||
uint32_t c;
|
uint32_t c;
|
||||||
|
#ifndef USE_ROCM
|
||||||
asm volatile("mul.f16x2 %0, %1, %2;\n" : "=r"(c) : "r"(a), "r"(b));
|
asm volatile("mul.f16x2 %0, %1, %2;\n" : "=r"(c) : "r"(a), "r"(b));
|
||||||
|
#else
|
||||||
|
asm volatile("v_pk_mul_f16 %0, %1, %2;\n" : "=v"(c) : "v"(a), "v"(b));
|
||||||
|
#endif
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,7 +326,11 @@ inline __device__ Float8_ mul(uint16_t a, uint4 b) {
|
|||||||
// Vector fused multiply-add.
|
// Vector fused multiply-add.
|
||||||
inline __device__ uint32_t fma(uint32_t a, uint32_t b, uint32_t c) {
|
inline __device__ uint32_t fma(uint32_t a, uint32_t b, uint32_t c) {
|
||||||
uint32_t d;
|
uint32_t d;
|
||||||
|
#ifndef USE_ROCM
|
||||||
asm volatile("fma.rn.f16x2 %0, %1, %2, %3;\n" : "=r"(d) : "r"(a), "r"(b), "r"(c));
|
asm volatile("fma.rn.f16x2 %0, %1, %2, %3;\n" : "=r"(d) : "r"(a), "r"(b), "r"(c));
|
||||||
|
#else
|
||||||
|
asm volatile("v_pk_fma_f16 %0, %1, %2, %3;\n" : "=v"(d) : "v"(a), "v"(b), "v"(c));
|
||||||
|
#endif
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,22 +26,3 @@ void gather_cached_kv(
|
|||||||
torch::Tensor& key_cache,
|
torch::Tensor& key_cache,
|
||||||
torch::Tensor& value_cache,
|
torch::Tensor& value_cache,
|
||||||
torch::Tensor& slot_mapping);
|
torch::Tensor& slot_mapping);
|
||||||
|
|
||||||
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
|
||||||
m.def(
|
|
||||||
"swap_blocks",
|
|
||||||
&swap_blocks,
|
|
||||||
"Swap in (out) the cache blocks from src to dst");
|
|
||||||
m.def(
|
|
||||||
"copy_blocks",
|
|
||||||
©_blocks,
|
|
||||||
"Copy the cache blocks from src to dst");
|
|
||||||
m.def(
|
|
||||||
"reshape_and_cache",
|
|
||||||
&reshape_and_cache,
|
|
||||||
"Reshape the key and value tensors and cache them");
|
|
||||||
m.def(
|
|
||||||
"gather_cached_kv",
|
|
||||||
&gather_cached_kv,
|
|
||||||
"Gather key and value from the cache into contiguous QKV tensors");
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <torch/extension.h>
|
#include <torch/extension.h>
|
||||||
#include <ATen/cuda/CUDAContext.h>
|
#include <ATen/cuda/CUDAContext.h>
|
||||||
|
|
||||||
|
#include "cuda_compat.h"
|
||||||
#include "dispatch_utils.h"
|
#include "dispatch_utils.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -28,8 +29,8 @@ void swap_blocks(
|
|||||||
TORCH_CHECK(false, "Invalid device combination");
|
TORCH_CHECK(false, "Invalid device combination");
|
||||||
}
|
}
|
||||||
|
|
||||||
void *src_ptr = src.data_ptr();
|
char *src_ptr = static_cast<char*>(src.data_ptr());
|
||||||
void *dst_ptr = dst.data_ptr();
|
char *dst_ptr = static_cast<char*>(dst.data_ptr());
|
||||||
|
|
||||||
const int64_t block_size_in_bytes = src.element_size() * src[0].numel();
|
const int64_t block_size_in_bytes = src.element_size() * src[0].numel();
|
||||||
const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
|
const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
|
||||||
@@ -267,8 +268,8 @@ __global__ void gather_cached_kv_kernel(
|
|||||||
+ head_offset * block_size
|
+ head_offset * block_size
|
||||||
+ block_offset;
|
+ block_offset;
|
||||||
|
|
||||||
key[tgt_key_idx] = __ldg(&key_cache[src_key_idx]);
|
key[tgt_key_idx] = VLLM_LDG(&key_cache[src_key_idx]);
|
||||||
value[tgt_value_idx] = __ldg(&value_cache[src_value_idx]);
|
value[tgt_value_idx] = VLLM_LDG(&value_cache[src_value_idx]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,8 +334,8 @@ __global__ void gather_cached_kv_kernel_optimized(
|
|||||||
src_key_indices[j] = src_key_idx;
|
src_key_indices[j] = src_key_idx;
|
||||||
src_value_indices[j] = src_value_idx;
|
src_value_indices[j] = src_value_idx;
|
||||||
|
|
||||||
keys_to_store[j] = __ldg(&key_cache[src_key_idx]);
|
keys_to_store[j] = VLLM_LDG(&key_cache[src_key_idx]);
|
||||||
values_to_store[j] = __ldg(&value_cache[src_value_idx]);
|
values_to_store[j] = VLLM_LDG(&value_cache[src_value_idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
|
|||||||
28
csrc/cuda_compat.h
Normal file
28
csrc/cuda_compat.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef USE_ROCM
|
||||||
|
#define VLLM_LDG(arg) __ldg(arg)
|
||||||
|
#else
|
||||||
|
#define VLLM_LDG(arg) *(arg)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_ROCM
|
||||||
|
#define VLLM_SHFL_XOR_SYNC(var, lane_mask) __shfl_xor_sync(uint32_t(-1), var, lane_mask)
|
||||||
|
#else
|
||||||
|
#define VLLM_SHFL_XOR_SYNC(var, lane_mask) __shfl_xor(var, lane_mask)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_ROCM
|
||||||
|
#define VLLM_SHFL_SYNC(var, src_lane) __shfl_sync(uint32_t(-1), var, src_lane)
|
||||||
|
#else
|
||||||
|
#define VLLM_SHFL_SYNC(var, src_lane) __shfl(var, src_lane)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_ROCM
|
||||||
|
#define VLLM_DevFuncAttribute_SET_MaxDynamicSharedMemorySize(FUNC, VAL) \
|
||||||
|
cudaFuncSetAttribute(FUNC, cudaFuncAttributeMaxDynamicSharedMemorySize, VAL)
|
||||||
|
#else
|
||||||
|
#define VLLM_DevFuncAttribute_SET_MaxDynamicSharedMemorySize(FUNC, VAL) \
|
||||||
|
hipFuncSetAttribute(FUNC, hipFuncAttributeMaxDynamicSharedMemorySize, VAL)
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#include <torch/extension.h>
|
|
||||||
|
|
||||||
int get_device_attribute(
|
|
||||||
int attribute,
|
|
||||||
int device_id);
|
|
||||||
|
|
||||||
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
|
||||||
m.def(
|
|
||||||
"get_device_attribute",
|
|
||||||
&get_device_attribute,
|
|
||||||
"Gets the specified device attribute.");
|
|
||||||
}
|
|
||||||
|
|
||||||
5
csrc/cuda_utils.h
Normal file
5
csrc/cuda_utils.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#include <torch/extension.h>
|
||||||
|
|
||||||
|
int get_device_attribute(
|
||||||
|
int attribute,
|
||||||
|
int device_id);
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
#ifdef USE_ROCM
|
||||||
|
#include <hip/hip_runtime.h>
|
||||||
|
#endif
|
||||||
int get_device_attribute(
|
int get_device_attribute(
|
||||||
int attribute,
|
int attribute,
|
||||||
int device_id)
|
int device_id)
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
#include <torch/extension.h>
|
|
||||||
|
|
||||||
void rms_norm(
|
|
||||||
torch::Tensor& out,
|
|
||||||
torch::Tensor& input,
|
|
||||||
torch::Tensor& weight,
|
|
||||||
float epsilon);
|
|
||||||
|
|
||||||
void fused_add_rms_norm(
|
|
||||||
torch::Tensor& input,
|
|
||||||
torch::Tensor& residual,
|
|
||||||
torch::Tensor& weight,
|
|
||||||
float epsilon);
|
|
||||||
|
|
||||||
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
|
||||||
m.def(
|
|
||||||
"rms_norm",
|
|
||||||
&rms_norm,
|
|
||||||
"Apply Root Mean Square (RMS) Normalization to the input tensor.");
|
|
||||||
m.def(
|
|
||||||
"fused_add_rms_norm",
|
|
||||||
&fused_add_rms_norm,
|
|
||||||
"In-place fused Add and RMS Normalization");
|
|
||||||
}
|
|
||||||
77
csrc/ops.h
Normal file
77
csrc/ops.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#include <torch/extension.h>
|
||||||
|
|
||||||
|
void paged_attention_v1(
|
||||||
|
torch::Tensor& out,
|
||||||
|
torch::Tensor& query,
|
||||||
|
torch::Tensor& key_cache,
|
||||||
|
torch::Tensor& value_cache,
|
||||||
|
int num_kv_heads,
|
||||||
|
float scale,
|
||||||
|
torch::Tensor& block_tables,
|
||||||
|
torch::Tensor& context_lens,
|
||||||
|
int block_size,
|
||||||
|
int max_context_len,
|
||||||
|
const c10::optional<torch::Tensor>& alibi_slopes);
|
||||||
|
|
||||||
|
void paged_attention_v2(
|
||||||
|
torch::Tensor& out,
|
||||||
|
torch::Tensor& exp_sums,
|
||||||
|
torch::Tensor& max_logits,
|
||||||
|
torch::Tensor& tmp_out,
|
||||||
|
torch::Tensor& query,
|
||||||
|
torch::Tensor& key_cache,
|
||||||
|
torch::Tensor& value_cache,
|
||||||
|
int num_kv_heads,
|
||||||
|
float scale,
|
||||||
|
torch::Tensor& block_tables,
|
||||||
|
torch::Tensor& context_lens,
|
||||||
|
int block_size,
|
||||||
|
int max_context_len,
|
||||||
|
const c10::optional<torch::Tensor>& alibi_slopes);
|
||||||
|
|
||||||
|
void rms_norm(
|
||||||
|
torch::Tensor& out,
|
||||||
|
torch::Tensor& input,
|
||||||
|
torch::Tensor& weight,
|
||||||
|
float epsilon);
|
||||||
|
|
||||||
|
void fused_add_rms_norm(
|
||||||
|
torch::Tensor& input,
|
||||||
|
torch::Tensor& residual,
|
||||||
|
torch::Tensor& weight,
|
||||||
|
float epsilon);
|
||||||
|
|
||||||
|
void rotary_embedding(
|
||||||
|
torch::Tensor& positions,
|
||||||
|
torch::Tensor& query,
|
||||||
|
torch::Tensor& key,
|
||||||
|
int head_size,
|
||||||
|
torch::Tensor& cos_sin_cache,
|
||||||
|
bool is_neox);
|
||||||
|
|
||||||
|
void silu_and_mul(
|
||||||
|
torch::Tensor& out,
|
||||||
|
torch::Tensor& input);
|
||||||
|
|
||||||
|
void gelu_new(
|
||||||
|
torch::Tensor& out,
|
||||||
|
torch::Tensor& input);
|
||||||
|
|
||||||
|
void gelu_fast(
|
||||||
|
torch::Tensor& out,
|
||||||
|
torch::Tensor& input);
|
||||||
|
|
||||||
|
#ifndef USE_ROCM
|
||||||
|
torch::Tensor awq_gemm(
|
||||||
|
torch::Tensor _in_feats,
|
||||||
|
torch::Tensor _kernel,
|
||||||
|
torch::Tensor _scaling_factors,
|
||||||
|
torch::Tensor _zeros,
|
||||||
|
int split_k_iters);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void squeezellm_gemm(
|
||||||
|
torch::Tensor vec,
|
||||||
|
torch::Tensor mat,
|
||||||
|
torch::Tensor mul,
|
||||||
|
torch::Tensor lookup_table);
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#include <torch/extension.h>
|
|
||||||
|
|
||||||
void rotary_embedding(
|
|
||||||
torch::Tensor& positions,
|
|
||||||
torch::Tensor& query,
|
|
||||||
torch::Tensor& key,
|
|
||||||
int head_size,
|
|
||||||
torch::Tensor& cos_sin_cache,
|
|
||||||
bool is_neox);
|
|
||||||
|
|
||||||
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
|
||||||
m.def(
|
|
||||||
"rotary_embedding",
|
|
||||||
&rotary_embedding,
|
|
||||||
"Apply GPT-NeoX or GPT-J style rotary embedding to query and key");
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <torch/extension.h>
|
#include <torch/extension.h>
|
||||||
#include <ATen/cuda/CUDAContext.h>
|
#include <ATen/cuda/CUDAContext.h>
|
||||||
|
|
||||||
|
#include "cuda_compat.h"
|
||||||
#include "dispatch_utils.h"
|
#include "dispatch_utils.h"
|
||||||
|
|
||||||
namespace vllm {
|
namespace vllm {
|
||||||
@@ -19,14 +20,14 @@ inline __device__ void apply_rotary_embedding(
|
|||||||
// GPT-NeoX style rotary embedding.
|
// GPT-NeoX style rotary embedding.
|
||||||
x_index = rot_offset;
|
x_index = rot_offset;
|
||||||
y_index = embed_dim + rot_offset;
|
y_index = embed_dim + rot_offset;
|
||||||
cos = __ldg(cos_ptr + x_index);
|
cos = VLLM_LDG(cos_ptr + x_index);
|
||||||
sin = __ldg(sin_ptr + x_index);
|
sin = VLLM_LDG(sin_ptr + x_index);
|
||||||
} else {
|
} else {
|
||||||
// GPT-J style rotary embedding.
|
// GPT-J style rotary embedding.
|
||||||
x_index = 2 * rot_offset;
|
x_index = 2 * rot_offset;
|
||||||
y_index = 2 * rot_offset + 1;
|
y_index = 2 * rot_offset + 1;
|
||||||
cos = __ldg(cos_ptr + x_index / 2);
|
cos = VLLM_LDG(cos_ptr + x_index / 2);
|
||||||
sin = __ldg(sin_ptr + x_index / 2);
|
sin = VLLM_LDG(sin_ptr + x_index / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
const scalar_t x = arr[x_index];
|
const scalar_t x = arr[x_index];
|
||||||
|
|||||||
84
csrc/pybind.cpp
Normal file
84
csrc/pybind.cpp
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#include "cache.h"
|
||||||
|
#include "cuda_utils.h"
|
||||||
|
#include "ops.h"
|
||||||
|
#include <torch/extension.h>
|
||||||
|
|
||||||
|
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
||||||
|
// vLLM custom ops
|
||||||
|
pybind11::module ops = m.def_submodule("ops", "vLLM custom operators");
|
||||||
|
|
||||||
|
// Attention ops
|
||||||
|
ops.def(
|
||||||
|
"paged_attention_v1",
|
||||||
|
&paged_attention_v1,
|
||||||
|
"Compute the attention between an input query and the cached keys/values using PagedAttention.");
|
||||||
|
ops.def(
|
||||||
|
"paged_attention_v2",
|
||||||
|
&paged_attention_v2,
|
||||||
|
"PagedAttention V2.");
|
||||||
|
|
||||||
|
// Activation ops
|
||||||
|
ops.def(
|
||||||
|
"silu_and_mul",
|
||||||
|
&silu_and_mul,
|
||||||
|
"Activation function used in SwiGLU.");
|
||||||
|
ops.def(
|
||||||
|
"gelu_new",
|
||||||
|
&gelu_new,
|
||||||
|
"GELU implementation used in GPT-2.");
|
||||||
|
ops.def(
|
||||||
|
"gelu_fast",
|
||||||
|
&gelu_fast,
|
||||||
|
"Approximate GELU implementation.");
|
||||||
|
|
||||||
|
// Layernorm
|
||||||
|
ops.def(
|
||||||
|
"rms_norm",
|
||||||
|
&rms_norm,
|
||||||
|
"Apply Root Mean Square (RMS) Normalization to the input tensor.");
|
||||||
|
|
||||||
|
ops.def(
|
||||||
|
"fused_add_rms_norm",
|
||||||
|
&fused_add_rms_norm,
|
||||||
|
"In-place fused Add and RMS Normalization");
|
||||||
|
|
||||||
|
// Rotary embedding
|
||||||
|
ops.def(
|
||||||
|
"rotary_embedding",
|
||||||
|
&rotary_embedding,
|
||||||
|
"Apply GPT-NeoX or GPT-J style rotary embedding to query and key");
|
||||||
|
|
||||||
|
#ifndef USE_ROCM
|
||||||
|
// Quantization ops
|
||||||
|
ops.def("awq_gemm", &awq_gemm, "Quantized GEMM for AWQ");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
ops.def("squeezellm_gemm", &squeezellm_gemm, "Quantized GEMM for SqueezeLLM");
|
||||||
|
|
||||||
|
// Cache ops
|
||||||
|
pybind11::module cache_ops = m.def_submodule("cache_ops", "vLLM cache ops");
|
||||||
|
cache_ops.def(
|
||||||
|
"swap_blocks",
|
||||||
|
&swap_blocks,
|
||||||
|
"Swap in (out) the cache blocks from src to dst");
|
||||||
|
cache_ops.def(
|
||||||
|
"copy_blocks",
|
||||||
|
©_blocks,
|
||||||
|
"Copy the cache blocks from src to dst");
|
||||||
|
cache_ops.def(
|
||||||
|
"reshape_and_cache",
|
||||||
|
&reshape_and_cache,
|
||||||
|
"Reshape the key and value tensors and cache them");
|
||||||
|
cache_ops.def(
|
||||||
|
"gather_cached_kv",
|
||||||
|
&gather_cached_kv,
|
||||||
|
"Gather key and value from the cache into contiguous QKV tensors");
|
||||||
|
|
||||||
|
// Cuda utils
|
||||||
|
pybind11::module cuda_utils = m.def_submodule("cuda_utils", "vLLM cuda utils");
|
||||||
|
cuda_utils.def(
|
||||||
|
"get_device_attribute",
|
||||||
|
&get_device_attribute,
|
||||||
|
"Gets the specified device attribute.");
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#include <torch/extension.h>
|
|
||||||
|
|
||||||
torch::Tensor awq_gemm(
|
|
||||||
torch::Tensor _in_feats,
|
|
||||||
torch::Tensor _kernel,
|
|
||||||
torch::Tensor _scaling_factors,
|
|
||||||
torch::Tensor _zeros,
|
|
||||||
int split_k_iters);
|
|
||||||
|
|
||||||
void squeezellm_gemm(
|
|
||||||
torch::Tensor vec,
|
|
||||||
torch::Tensor mat,
|
|
||||||
torch::Tensor mul,
|
|
||||||
torch::Tensor lookup_table);
|
|
||||||
|
|
||||||
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
|
||||||
m.def("awq_gemm", &awq_gemm, "Quantized GEMM for AWQ");
|
|
||||||
m.def("squeezellm_gemm", &squeezellm_gemm, "Quantized GEMM for SqueezeLLM");
|
|
||||||
}
|
|
||||||
@@ -20,9 +20,17 @@ __device__ inline unsigned int as_unsigned(int i) {
|
|||||||
|
|
||||||
// 4-bit matvec kernel (LUT-based)
|
// 4-bit matvec kernel (LUT-based)
|
||||||
__global__ void NUQ4MatMulKernel(
|
__global__ void NUQ4MatMulKernel(
|
||||||
|
#ifndef USE_ROCM
|
||||||
const half2* __restrict__ vec,
|
const half2* __restrict__ vec,
|
||||||
|
#else
|
||||||
|
const __half2* __restrict__ vec,
|
||||||
|
#endif
|
||||||
const int* __restrict__ mat,
|
const int* __restrict__ mat,
|
||||||
|
#ifndef USE_ROCM
|
||||||
half2* __restrict__ mul,
|
half2* __restrict__ mul,
|
||||||
|
#else
|
||||||
|
float2* __restrict__ mul,
|
||||||
|
#endif
|
||||||
const __half* __restrict__ lookup_table,
|
const __half* __restrict__ lookup_table,
|
||||||
int height,
|
int height,
|
||||||
int width,
|
int width,
|
||||||
@@ -35,7 +43,11 @@ __global__ void NUQ4MatMulKernel(
|
|||||||
int row = BLOCKHEIGHT4 * blockIdx.x;
|
int row = BLOCKHEIGHT4 * blockIdx.x;
|
||||||
int col = BLOCKWIDTH * blockIdx.y + threadIdx.x;
|
int col = BLOCKWIDTH * blockIdx.y + threadIdx.x;
|
||||||
|
|
||||||
|
#ifndef USE_ROCM
|
||||||
__shared__ half2 blockvec[blockwidth2];
|
__shared__ half2 blockvec[blockwidth2];
|
||||||
|
#else
|
||||||
|
__shared__ __half2 blockvec[blockwidth2];
|
||||||
|
#endif
|
||||||
|
|
||||||
__shared__ __half deq2[16][BLOCKWIDTH];
|
__shared__ __half deq2[16][BLOCKWIDTH];
|
||||||
int off = threadIdx.x;
|
int off = threadIdx.x;
|
||||||
@@ -46,8 +58,13 @@ __global__ void NUQ4MatMulKernel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
__half res;
|
__half res;
|
||||||
|
#ifndef USE_ROCM
|
||||||
half2 res2;
|
half2 res2;
|
||||||
half2 tmp2;
|
half2 tmp2;
|
||||||
|
#else
|
||||||
|
__half2 res2;
|
||||||
|
__half2 tmp2;
|
||||||
|
#endif
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
int k;
|
int k;
|
||||||
@@ -68,48 +85,96 @@ __global__ void NUQ4MatMulKernel(
|
|||||||
while (k < blockwidth2) {
|
while (k < blockwidth2) {
|
||||||
tmp1 = as_unsigned(mat[i]);
|
tmp1 = as_unsigned(mat[i]);
|
||||||
|
|
||||||
|
#ifndef USE_ROCM
|
||||||
res2 = {};
|
res2 = {};
|
||||||
tmp2 = {};
|
tmp2 = {};
|
||||||
|
#else
|
||||||
|
res2.x = __half_as_ushort(__float2half(0));
|
||||||
|
res2.y = __half_as_ushort(__float2half(0));
|
||||||
|
tmp2.x = __half_as_ushort(__float2half(0));
|
||||||
|
tmp2.y = __half_as_ushort(__float2half(0));
|
||||||
|
#endif
|
||||||
|
|
||||||
lut_index1 = tmp1 & 0xF;
|
lut_index1 = tmp1 & 0xF;
|
||||||
lut_index2 = (tmp1 >> 4) & 0xF;
|
lut_index2 = (tmp1 >> 4) & 0xF;
|
||||||
|
#ifndef USE_ROCM
|
||||||
tmp2.x = deq2[lut_index1][off];
|
tmp2.x = deq2[lut_index1][off];
|
||||||
tmp2.y = deq2[lut_index2][off];
|
tmp2.y = deq2[lut_index2][off];
|
||||||
|
#else
|
||||||
|
tmp2.x = __half_as_ushort(deq2[lut_index1][off]);
|
||||||
|
tmp2.y = __half_as_ushort(deq2[lut_index2][off]);
|
||||||
|
#endif
|
||||||
res2 = __hfma2(tmp2, blockvec[k + 0], res2);
|
res2 = __hfma2(tmp2, blockvec[k + 0], res2);
|
||||||
|
|
||||||
lut_index1 = (tmp1 >> 8) & 0xF;
|
lut_index1 = (tmp1 >> 8) & 0xF;
|
||||||
lut_index2 = (tmp1 >> 12) & 0xF;
|
lut_index2 = (tmp1 >> 12) & 0xF;
|
||||||
|
#ifndef USE_ROCM
|
||||||
tmp2.x = deq2[lut_index1][off];
|
tmp2.x = deq2[lut_index1][off];
|
||||||
tmp2.y = deq2[lut_index2][off];
|
tmp2.y = deq2[lut_index2][off];
|
||||||
|
#else
|
||||||
|
tmp2.x = __half_as_ushort(deq2[lut_index1][off]);
|
||||||
|
tmp2.y = __half_as_ushort(deq2[lut_index2][off]);
|
||||||
|
#endif
|
||||||
res2 = __hfma2(tmp2, blockvec[k + 1], res2);
|
res2 = __hfma2(tmp2, blockvec[k + 1], res2);
|
||||||
|
|
||||||
lut_index1 = (tmp1 >> 16) & 0xF;
|
lut_index1 = (tmp1 >> 16) & 0xF;
|
||||||
lut_index2 = (tmp1 >> 20) & 0xF;
|
lut_index2 = (tmp1 >> 20) & 0xF;
|
||||||
|
#ifndef USE_ROCM
|
||||||
tmp2.x = deq2[lut_index1][off];
|
tmp2.x = deq2[lut_index1][off];
|
||||||
tmp2.y = deq2[lut_index2][off];
|
tmp2.y = deq2[lut_index2][off];
|
||||||
|
#else
|
||||||
|
tmp2.x = __half_as_ushort(deq2[lut_index1][off]);
|
||||||
|
tmp2.y = __half_as_ushort(deq2[lut_index2][off]);
|
||||||
|
#endif
|
||||||
res2 = __hfma2(tmp2, blockvec[k + 2], res2);
|
res2 = __hfma2(tmp2, blockvec[k + 2], res2);
|
||||||
|
|
||||||
lut_index1 = (tmp1 >> 24) & 0xF;
|
lut_index1 = (tmp1 >> 24) & 0xF;
|
||||||
lut_index2 = (tmp1 >> 28) & 0xF;
|
lut_index2 = (tmp1 >> 28) & 0xF;
|
||||||
|
#ifndef USE_ROCM
|
||||||
tmp2.x = deq2[lut_index1][off];
|
tmp2.x = deq2[lut_index1][off];
|
||||||
tmp2.y = deq2[lut_index2][off];
|
tmp2.y = deq2[lut_index2][off];
|
||||||
|
#else
|
||||||
|
tmp2.x = __half_as_ushort(deq2[lut_index1][off]);
|
||||||
|
tmp2.y = __half_as_ushort(deq2[lut_index2][off]);
|
||||||
|
#endif
|
||||||
res2 = __hfma2(tmp2, blockvec[k + 3], res2);
|
res2 = __hfma2(tmp2, blockvec[k + 3], res2);
|
||||||
|
|
||||||
|
#ifndef USE_ROCM
|
||||||
res = __hadd(__hadd(res2.x, res2.y), res);
|
res = __hadd(__hadd(res2.x, res2.y), res);
|
||||||
|
#else
|
||||||
|
res = __hadd(__hadd(__ushort_as_half(res2.x), __ushort_as_half(res2.y)), res);
|
||||||
|
#endif
|
||||||
|
|
||||||
i += width;
|
i += width;
|
||||||
k += 4;
|
k += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// col%2 -> only set one of the two values
|
// col%2 -> only set one of the two values
|
||||||
|
#ifndef USE_ROCM
|
||||||
half2 res3 = {};
|
half2 res3 = {};
|
||||||
if (col % 2 == 0) {
|
if (col % 2 == 0) {
|
||||||
res3.x = res;
|
res3.x = res;
|
||||||
} else {
|
} else {
|
||||||
res3.y = res;
|
res3.y = res;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
__half2 res3;
|
||||||
|
res3.x = __half_as_ushort(__float2half(0));
|
||||||
|
res3.y = __half_as_ushort(__float2half(0));
|
||||||
|
if (col % 2 == 0) {
|
||||||
|
res3.x = __half_as_ushort(res);
|
||||||
|
} else {
|
||||||
|
res3.y = __half_as_ushort(res);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_ROCM
|
||||||
atomicAdd(&mul[b * width / 2 + col / 2], res3);
|
atomicAdd(&mul[b * width / 2 + col / 2], res3);
|
||||||
|
#else
|
||||||
|
int tmp_addr = b * width / 2 + col / 2;
|
||||||
|
atomicAdd(&(mul[tmp_addr].x), __half2float(__ushort_as_half(res3.x)));
|
||||||
|
atomicAdd(&(mul[tmp_addr].y), __half2float(__ushort_as_half(res3.y)));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,10 +201,19 @@ void squeezellm_gemm(
|
|||||||
dim3 threads(BLOCKWIDTH);
|
dim3 threads(BLOCKWIDTH);
|
||||||
|
|
||||||
vllm::squeezellm::NUQ4MatMulKernel<<<blocks, threads>>>(
|
vllm::squeezellm::NUQ4MatMulKernel<<<blocks, threads>>>(
|
||||||
|
#ifndef USE_ROCM
|
||||||
(half2*) vec.data<at::Half>(),
|
(half2*) vec.data<at::Half>(),
|
||||||
|
#else
|
||||||
|
(__half2*) vec.data_ptr<at::Half>(),
|
||||||
|
#endif
|
||||||
mat.data_ptr<int>(),
|
mat.data_ptr<int>(),
|
||||||
|
#ifndef USE_ROCM
|
||||||
(half2*) mul.data<at::Half>(),
|
(half2*) mul.data<at::Half>(),
|
||||||
(__half*) lookup_table.data<at::Half>(),
|
(__half*) lookup_table.data<at::Half>(),
|
||||||
|
#else
|
||||||
|
(float2*) mul.data_ptr<float>(),
|
||||||
|
(__half*) lookup_table.data_ptr<at::Half>(),
|
||||||
|
#endif
|
||||||
height, width, batch, vec_height
|
height, width, batch, vec_height
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,13 +17,15 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "cuda_compat.h"
|
||||||
|
|
||||||
namespace vllm {
|
namespace vllm {
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
__inline__ __device__ T warpReduceSum(T val) {
|
__inline__ __device__ T warpReduceSum(T val) {
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int mask = 16; mask > 0; mask >>= 1)
|
for (int mask = 16; mask > 0; mask >>= 1)
|
||||||
val += __shfl_xor_sync(0xffffffff, val, mask, 32);
|
val += VLLM_SHFL_XOR_SYNC(val, mask);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
143
docs/source/getting_started/amd-installation.rst
Normal file
143
docs/source/getting_started/amd-installation.rst
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
.. _installation_rocm:
|
||||||
|
|
||||||
|
Installation with ROCm
|
||||||
|
======================
|
||||||
|
|
||||||
|
vLLM 0.2.x onwards supports model inferencing and serving on AMD GPUs with ROCm.
|
||||||
|
At the moment AWQ quantization is not supported in ROCm, but SqueezeLLM quantization has been ported.
|
||||||
|
Data types currently supported in ROCm are FP16 and BF16.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
* OS: Linux
|
||||||
|
* Python: 3.8 -- 3.11 (Verified on 3.10)
|
||||||
|
* GPU: MI200s
|
||||||
|
* Pytorch 2.0.1/2.1.1/2.2
|
||||||
|
* ROCm 5.7
|
||||||
|
|
||||||
|
Installation options:
|
||||||
|
|
||||||
|
#. :ref:`(Recommended) Quick start with vLLM pre-installed in Docker Image <quick_start_docker_rocm>`
|
||||||
|
#. :ref:`Build from source <build_from_source_rocm>`
|
||||||
|
#. :ref:`Build from source with docker <build_from_source_docker_rocm>`
|
||||||
|
|
||||||
|
.. _quick_start_docker_rocm:
|
||||||
|
|
||||||
|
(Recommended) Option 1: Quick start with vLLM pre-installed in Docker Image
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ docker pull embeddedllminfo/vllm-rocm:vllm-v0.2.3
|
||||||
|
$ docker run -it \
|
||||||
|
--network=host \
|
||||||
|
--group-add=video \
|
||||||
|
--ipc=host \
|
||||||
|
--cap-add=SYS_PTRACE \
|
||||||
|
--security-opt seccomp=unconfined \
|
||||||
|
--device /dev/kfd \
|
||||||
|
--device /dev/dri \
|
||||||
|
-v <path/to/model>:/app/model \
|
||||||
|
embeddedllminfo/vllm-rocm \
|
||||||
|
bash
|
||||||
|
|
||||||
|
|
||||||
|
.. _build_from_source_rocm:
|
||||||
|
|
||||||
|
Option 2: Build from source
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
You can build and install vLLM from source:
|
||||||
|
|
||||||
|
0. Install prerequisites (skip if you are already in an environment/docker with the following installed):
|
||||||
|
|
||||||
|
- `ROCm <https://rocm.docs.amd.com/en/latest/deploy/linux/index.html>`_
|
||||||
|
- `Pytorch <https://pytorch.org/>`_
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ pip install torch==2.2.0.dev20231206+rocm5.7 --index-url https://download.pytorch.org/whl/nightly/rocm5.7 # tested version
|
||||||
|
|
||||||
|
|
||||||
|
1. Install `flash attention for ROCm <https://github.com/ROCmSoftwarePlatform/flash-attention/tree/flash_attention_for_rocm>`_
|
||||||
|
|
||||||
|
Install ROCm's flash attention (v2.0.4) following the instructions from `ROCmSoftwarePlatform/flash-attention <https://github.com/ROCmSoftwarePlatform/flash-attention/tree/flash_attention_for_rocm#amd-gpurocm-support>`_
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
- If you are using rocm5.7 with pytorch 2.1.0 onwards, you don't need to apply the `hipify_python.patch`. You can build the ROCm flash attention directly.
|
||||||
|
- If you fail to install `ROCmSoftwarePlatform/flash-attention`, try cloning from the commit `6fd2f8e572805681cd67ef8596c7e2ce521ed3c6`.
|
||||||
|
- ROCm's Flash-attention-2 (v2.0.4) does not support sliding windows attention.
|
||||||
|
- You might need to downgrade the "ninja" version to 1.10 it is not used when compiling flash-attention-2 (e.g. `pip install ninja==1.10.2.4`)
|
||||||
|
|
||||||
|
2. Setup `xformers==0.0.22.post7` without dependencies, and apply patches to adapt for ROCm flash attention
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ pip install xformers==0.0.22.post7 --no-deps
|
||||||
|
$ bash patch_xformers-0.0.22.post7.rocm.sh
|
||||||
|
|
||||||
|
3. Build vLLM.
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ cd vllm
|
||||||
|
$ pip install -U -r requirements-rocm.txt
|
||||||
|
$ python setup.py install # This may take 5-10 minutes. Currently, `pip install .`` does not work for ROCm installation
|
||||||
|
|
||||||
|
|
||||||
|
.. _build_from_source_docker_rocm:
|
||||||
|
|
||||||
|
Option 3: Build from source with docker
|
||||||
|
-----------------------------------------------------
|
||||||
|
|
||||||
|
You can build and install vLLM from source:
|
||||||
|
|
||||||
|
Build a docker image from `Dockerfile.rocm`, and launch a docker container.
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ docker build -f Dockerfile.rocm -t vllm-rocm .
|
||||||
|
$ docker run -it \
|
||||||
|
--network=host \
|
||||||
|
--group-add=video \
|
||||||
|
--ipc=host \
|
||||||
|
--cap-add=SYS_PTRACE \
|
||||||
|
--security-opt seccomp=unconfined \
|
||||||
|
--device /dev/kfd \
|
||||||
|
--device /dev/dri \
|
||||||
|
-v <path/to/model>:/app/model \
|
||||||
|
vllm-rocm \
|
||||||
|
bash
|
||||||
|
|
||||||
|
Alternatively, if you plan to install vLLM-ROCm on a local machine or start from a fresh docker image (e.g. rocm/pytorch), you can follow the steps below:
|
||||||
|
|
||||||
|
0. Install prerequisites (skip if you are already in an environment/docker with the following installed):
|
||||||
|
|
||||||
|
- `ROCm <https://rocm.docs.amd.com/en/latest/deploy/linux/index.html>`_
|
||||||
|
- `Pytorch <https://pytorch.org/>`_
|
||||||
|
|
||||||
|
1. Install `flash attention for ROCm <https://github.com/ROCmSoftwarePlatform/flash-attention/tree/flash_attention_for_rocm>`_
|
||||||
|
|
||||||
|
Install ROCm's flash attention (v2.0.4) following the instructions from `ROCmSoftwarePlatform/flash-attention <https://github.com/ROCmSoftwarePlatform/flash-attention/tree/flash_attention_for_rocm#amd-gpurocm-support>`_
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
- If you are using rocm5.7 with pytorch 2.1.0 onwards, you don't need to apply the `hipify_python.patch`. You can build the ROCm flash attention directly.
|
||||||
|
- If you fail to install `ROCmSoftwarePlatform/flash-attention`, try cloning from the commit `6fd2f8e572805681cd67ef8596c7e2ce521ed3c6`.
|
||||||
|
- ROCm's Flash-attention-2 (v2.0.4) does not support sliding windows attention.
|
||||||
|
- You might need to downgrade the "ninja" version to 1.10 it is not used when compiling flash-attention-2 (e.g. `pip install ninja==1.10.2.4`)
|
||||||
|
|
||||||
|
2. Setup `xformers==0.0.22.post7` without dependencies, and apply patches to adapt for ROCm flash attention
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ pip install xformers==0.0.22.post7 --no-deps
|
||||||
|
$ bash patch_xformers-0.0.22.post7.rocm.sh
|
||||||
|
|
||||||
|
3. Build vLLM.
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ cd vllm
|
||||||
|
$ pip install -U -r requirements-rocm.txt
|
||||||
|
$ python setup.py install # This may take 5-10 minutes.
|
||||||
@@ -3,14 +3,14 @@
|
|||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
|
|
||||||
vLLM is a Python library that also contains pre-compiled C++ and CUDA (11.8) binaries.
|
vLLM is a Python library that also contains pre-compiled C++ and CUDA (12.1) binaries.
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* OS: Linux
|
* OS: Linux
|
||||||
* Python: 3.8 -- 3.11
|
* Python: 3.8 -- 3.11
|
||||||
* GPU: compute capability 7.0 or higher (e.g., V100, T4, RTX20xx, A100, L4, etc.)
|
* GPU: compute capability 7.0 or higher (e.g., V100, T4, RTX20xx, A100, L4, H100, etc.)
|
||||||
|
|
||||||
Install with pip
|
Install with pip
|
||||||
----------------
|
----------------
|
||||||
@@ -23,9 +23,24 @@ You can install vLLM using pip:
|
|||||||
$ conda create -n myenv python=3.8 -y
|
$ conda create -n myenv python=3.8 -y
|
||||||
$ conda activate myenv
|
$ conda activate myenv
|
||||||
|
|
||||||
$ # Install vLLM.
|
$ # Install vLLM with CUDA 12.1.
|
||||||
$ pip install vllm
|
$ pip install vllm
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
As of now, vLLM's binaries are compiled on CUDA 12.1 by default.
|
||||||
|
However, you can install vLLM with CUDA 11.8 by running:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ # Install vLLM with CUDA 11.8.
|
||||||
|
$ # Replace `cp310` with your Python version (e.g., `cp38`, `cp39`, `cp311`).
|
||||||
|
$ pip install https://github.com/vllm-project/vllm/releases/download/v0.2.2/vllm-0.2.2+cu118-cp310-cp310-manylinux1_x86_64.whl
|
||||||
|
|
||||||
|
$ # Re-install PyTorch with CUDA 11.8.
|
||||||
|
$ pip uninstall torch -y
|
||||||
|
$ pip install torch --upgrade --index-url https://download.pytorch.org/whl/cu118
|
||||||
|
|
||||||
|
|
||||||
.. _build_from_source:
|
.. _build_from_source:
|
||||||
|
|
||||||
@@ -45,6 +60,5 @@ You can also build and install vLLM from source:
|
|||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ # Pull the Docker image with CUDA 11.8.
|
|
||||||
$ # Use `--ipc=host` to make sure the shared memory is large enough.
|
$ # Use `--ipc=host` to make sure the shared memory is large enough.
|
||||||
$ docker run --gpus all -it --rm --ipc=host nvcr.io/nvidia/pytorch:22.12-py3
|
$ docker run --gpus all -it --rm --ipc=host nvcr.io/nvidia/pytorch:23.10-py3
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ OpenAI-Compatible Server
|
|||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
vLLM can be deployed as a server that mimics the OpenAI API protocol. This allows vLLM to be used as a drop-in replacement for applications using OpenAI API.
|
vLLM can be deployed as a server that mimics the OpenAI API protocol. This allows vLLM to be used as a drop-in replacement for applications using OpenAI API.
|
||||||
|
By default, it starts the server at ``http://localhost:8000``. You can specify the address with ``--host`` and ``--port`` arguments. The server currently hosts one model at a time (OPT-125M in the above command) and implements `list models <https://platform.openai.com/docs/api-reference/models/list>`_, `create chat completion <https://platform.openai.com/docs/api-reference/chat/completions/create>`_, and `create completion <https://platform.openai.com/docs/api-reference/completions/create>`_ endpoints. We are actively adding support for more endpoints.
|
||||||
|
|
||||||
Start the server:
|
Start the server:
|
||||||
|
|
||||||
@@ -122,7 +123,13 @@ Use model from www.modelscope.cn
|
|||||||
$ VLLM_USE_MODELSCOPE=True python -m vllm.entrypoints.openai.api_server \
|
$ VLLM_USE_MODELSCOPE=True python -m vllm.entrypoints.openai.api_server \
|
||||||
$ --model="qwen/Qwen-7B-Chat" --revision="v1.1.8" --trust-remote-code
|
$ --model="qwen/Qwen-7B-Chat" --revision="v1.1.8" --trust-remote-code
|
||||||
|
|
||||||
By default, it starts the server at ``http://localhost:8000``. You can specify the address with ``--host`` and ``--port`` arguments. The server currently hosts one model at a time (OPT-125M in the above command) and implements `list models <https://platform.openai.com/docs/api-reference/models/list>`_ and `create completion <https://platform.openai.com/docs/api-reference/completions/create>`_ endpoints. We are actively adding support for more endpoints.
|
By default, the server uses a predefined chat template stored in the tokenizer. You can override this template by using the ``--chat-template`` argument:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ python -m vllm.entrypoints.openai.api_server \
|
||||||
|
$ --model facebook/opt-125m \
|
||||||
|
$ --chat-template ./examples/template_chatml.jinja
|
||||||
|
|
||||||
This server can be queried in the same format as OpenAI API. For example, list the models:
|
This server can be queried in the same format as OpenAI API. For example, list the models:
|
||||||
|
|
||||||
@@ -130,6 +137,9 @@ This server can be queried in the same format as OpenAI API. For example, list t
|
|||||||
|
|
||||||
$ curl http://localhost:8000/v1/models
|
$ curl http://localhost:8000/v1/models
|
||||||
|
|
||||||
|
Using OpenAI Completions API with vLLM
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Query the model with input prompts:
|
Query the model with input prompts:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
@@ -147,12 +157,65 @@ Since this server is compatible with OpenAI API, you can use it as a drop-in rep
|
|||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
import openai
|
from openai import OpenAI
|
||||||
|
|
||||||
# Modify OpenAI's API key and API base to use vLLM's API server.
|
# Modify OpenAI's API key and API base to use vLLM's API server.
|
||||||
openai.api_key = "EMPTY"
|
openai_api_key = "EMPTY"
|
||||||
openai.api_base = "http://localhost:8000/v1"
|
openai_api_base = "http://localhost:8000/v1"
|
||||||
completion = openai.Completion.create(model="facebook/opt-125m",
|
client = OpenAI(
|
||||||
|
api_key=openai_api_key,
|
||||||
|
base_url=openai_api_base,
|
||||||
|
)
|
||||||
|
completion = client.completions.create(model="facebook/opt-125m",
|
||||||
prompt="San Francisco is a")
|
prompt="San Francisco is a")
|
||||||
print("Completion result:", completion)
|
print("Completion result:", completion)
|
||||||
|
|
||||||
For a more detailed client example, refer to `examples/openai_completion_client.py <https://github.com/vllm-project/vllm/blob/main/examples/openai_completion_client.py>`_.
|
For a more detailed client example, refer to `examples/openai_completion_client.py <https://github.com/vllm-project/vllm/blob/main/examples/openai_completion_client.py>`_.
|
||||||
|
|
||||||
|
Using OpenAI Chat API with vLLM
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The vLLM server is designed to support the OpenAI Chat API, allowing you to engage in dynamic conversations with the model. The chat interface is a more interactive way to communicate with the model, allowing back-and-forth exchanges that can be stored in the chat history. This is useful for tasks that require context or more detailed explanations.
|
||||||
|
|
||||||
|
Querying the model using OpenAI Chat API:
|
||||||
|
|
||||||
|
You can use the `create chat completion <https://platform.openai.com/docs/api-reference/chat/completions/create>`_ endpoint to communicate with the model in a chat-like interface:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ curl http://localhost:8000/v1/chat/completions \
|
||||||
|
$ -H "Content-Type: application/json" \
|
||||||
|
$ -d '{
|
||||||
|
$ "model": "facebook/opt-125m",
|
||||||
|
$ "messages": [
|
||||||
|
$ {"role": "system", "content": "You are a helpful assistant."},
|
||||||
|
$ {"role": "user", "content": "Who won the world series in 2020?"}
|
||||||
|
$ ]
|
||||||
|
$ }'
|
||||||
|
|
||||||
|
Python Client Example:
|
||||||
|
|
||||||
|
Using the `openai` python package, you can also communicate with the model in a chat-like manner:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from openai import OpenAI
|
||||||
|
# Set OpenAI's API key and API base to use vLLM's API server.
|
||||||
|
openai_api_key = "EMPTY"
|
||||||
|
openai_api_base = "http://localhost:8000/v1"
|
||||||
|
|
||||||
|
client = OpenAI(
|
||||||
|
api_key=openai_api_key,
|
||||||
|
base_url=openai_api_base,
|
||||||
|
)
|
||||||
|
|
||||||
|
chat_response = client.chat.completions.create(
|
||||||
|
model="facebook/opt-125m",
|
||||||
|
messages=[
|
||||||
|
{"role": "system", "content": "You are a helpful assistant."},
|
||||||
|
{"role": "user", "content": "Tell me a joke."},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
print("Chat response:", chat_response)
|
||||||
|
|
||||||
|
For more in-depth examples and advanced features of the chat API, you can refer to the official OpenAI documentation.
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ vLLM is flexible and easy to use with:
|
|||||||
* Tensor parallelism support for distributed inference
|
* Tensor parallelism support for distributed inference
|
||||||
* Streaming outputs
|
* Streaming outputs
|
||||||
* OpenAI-compatible API server
|
* OpenAI-compatible API server
|
||||||
|
* Support NVIDIA CUDA and AMD ROCm.
|
||||||
|
|
||||||
For more information, check out the following:
|
For more information, check out the following:
|
||||||
|
|
||||||
@@ -56,6 +57,7 @@ Documentation
|
|||||||
:caption: Getting Started
|
:caption: Getting Started
|
||||||
|
|
||||||
getting_started/installation
|
getting_started/installation
|
||||||
|
getting_started/amd-installation
|
||||||
getting_started/quickstart
|
getting_started/quickstart
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
@@ -66,6 +68,8 @@ Documentation
|
|||||||
serving/run_on_sky
|
serving/run_on_sky
|
||||||
serving/deploying_with_triton
|
serving/deploying_with_triton
|
||||||
serving/deploying_with_docker
|
serving/deploying_with_docker
|
||||||
|
serving/serving_with_langchain
|
||||||
|
serving/metrics
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
@@ -73,6 +77,7 @@ Documentation
|
|||||||
|
|
||||||
models/supported_models
|
models/supported_models
|
||||||
models/adding_model
|
models/adding_model
|
||||||
|
models/engine_args
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ This document provides a high-level guide on integrating a `HuggingFace Transfor
|
|||||||
0. Fork the vLLM repository
|
0. Fork the vLLM repository
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
Start by forking our `GitHub <https://github.com/vllm-project/vllm/>`_ repository and then :ref:`build it from source <build_from_source>`.
|
Start by forking our `GitHub`_ repository and then :ref:`build it from source <build_from_source>`.
|
||||||
This gives you the ability to modify the codebase and test your model.
|
This gives you the ability to modify the codebase and test your model.
|
||||||
|
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ This gives you the ability to modify the codebase and test your model.
|
|||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
Clone the PyTorch model code from the HuggingFace Transformers repository and put it into the `vllm/model_executor/models <https://github.com/vllm-project/vllm/tree/main/vllm/model_executor/models>`_ directory.
|
Clone the PyTorch model code from the HuggingFace Transformers repository and put it into the `vllm/model_executor/models <https://github.com/vllm-project/vllm/tree/main/vllm/model_executor/models>`_ directory.
|
||||||
For instance, vLLM's `OPT model <https://github.com/vllm-project/vllm/blob/main/vllm/model_executor/models/opt.py>`_ was adpated from the HuggingFace's `modeling_opt.py <https://github.com/huggingface/transformers/blob/main/src/transformers/models/opt/modeling_opt.py>`_ file.
|
For instance, vLLM's `OPT model <https://github.com/vllm-project/vllm/blob/main/vllm/model_executor/models/opt.py>`_ was adapted from the HuggingFace's `modeling_opt.py <https://github.com/huggingface/transformers/blob/main/src/transformers/models/opt/modeling_opt.py>`_ file.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
When copying the model code, make sure to review and adhere to the code's copyright and licensing terms.
|
When copying the model code, make sure to review and adhere to the code's copyright and licensing terms.
|
||||||
|
|||||||
114
docs/source/models/engine_args.rst
Normal file
114
docs/source/models/engine_args.rst
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
.. _engine_args:
|
||||||
|
|
||||||
|
Engine Arguments
|
||||||
|
================
|
||||||
|
|
||||||
|
Below, you can find an explanation of every engine argument for vLLM:
|
||||||
|
|
||||||
|
.. option:: --model <model_name_or_path>
|
||||||
|
|
||||||
|
Name or path of the huggingface model to use.
|
||||||
|
|
||||||
|
.. option:: --tokenizer <tokenizer_name_or_path>
|
||||||
|
|
||||||
|
Name or path of the huggingface tokenizer to use.
|
||||||
|
|
||||||
|
.. option:: --revision <revision>
|
||||||
|
|
||||||
|
The specific model version to use. It can be a branch name, a tag name, or a commit id. If unspecified, will use the default version.
|
||||||
|
|
||||||
|
.. option:: --tokenizer-revision <revision>
|
||||||
|
|
||||||
|
The specific tokenizer version to use. It can be a branch name, a tag name, or a commit id. If unspecified, will use the default version.
|
||||||
|
|
||||||
|
.. option:: --tokenizer-mode {auto,slow}
|
||||||
|
|
||||||
|
The tokenizer mode.
|
||||||
|
|
||||||
|
* "auto" will use the fast tokenizer if available.
|
||||||
|
* "slow" will always use the slow tokenizer.
|
||||||
|
|
||||||
|
.. option:: --trust-remote-code
|
||||||
|
|
||||||
|
Trust remote code from huggingface.
|
||||||
|
|
||||||
|
.. option:: --download-dir <directory>
|
||||||
|
|
||||||
|
Directory to download and load the weights, default to the default cache dir of huggingface.
|
||||||
|
|
||||||
|
.. option:: --load-format {auto,pt,safetensors,npcache,dummy}
|
||||||
|
|
||||||
|
The format of the model weights to load.
|
||||||
|
|
||||||
|
* "auto" will try to load the weights in the safetensors format and fall back to the pytorch bin format if safetensors format is not available.
|
||||||
|
* "pt" will load the weights in the pytorch bin format.
|
||||||
|
* "safetensors" will load the weights in the safetensors format.
|
||||||
|
* "npcache" will load the weights in pytorch format and store a numpy cache to speed up the loading.
|
||||||
|
* "dummy" will initialize the weights with random values, mainly for profiling.
|
||||||
|
|
||||||
|
.. option:: --dtype {auto,half,float16,bfloat16,float,float32}
|
||||||
|
|
||||||
|
Data type for model weights and activations.
|
||||||
|
|
||||||
|
* "auto" will use FP16 precision for FP32 and FP16 models, and BF16 precision for BF16 models.
|
||||||
|
* "half" for FP16. Recommended for AWQ quantization.
|
||||||
|
* "float16" is the same as "half".
|
||||||
|
* "bfloat16" for a balance between precision and range.
|
||||||
|
* "float" is shorthand for FP32 precision.
|
||||||
|
* "float32" for FP32 precision.
|
||||||
|
|
||||||
|
.. option:: --max-model-len <length>
|
||||||
|
|
||||||
|
Model context length. If unspecified, will be automatically derived from the model config.
|
||||||
|
|
||||||
|
.. option:: --worker-use-ray
|
||||||
|
|
||||||
|
Use Ray for distributed serving, will be automatically set when using more than 1 GPU.
|
||||||
|
|
||||||
|
.. option:: --pipeline-parallel-size (-pp) <size>
|
||||||
|
|
||||||
|
Number of pipeline stages.
|
||||||
|
|
||||||
|
.. option:: --tensor-parallel-size (-tp) <size>
|
||||||
|
|
||||||
|
Number of tensor parallel replicas.
|
||||||
|
|
||||||
|
.. option:: --max-parallel-loading-workers <workers>
|
||||||
|
|
||||||
|
Load model sequentially in multiple batches, to avoid RAM OOM when using tensor parallel and large models.
|
||||||
|
|
||||||
|
.. option:: --block-size {8,16,32}
|
||||||
|
|
||||||
|
Token block size for contiguous chunks of tokens.
|
||||||
|
|
||||||
|
.. option:: --seed <seed>
|
||||||
|
|
||||||
|
Random seed for operations.
|
||||||
|
|
||||||
|
.. option:: --swap-space <size>
|
||||||
|
|
||||||
|
CPU swap space size (GiB) per GPU.
|
||||||
|
|
||||||
|
.. option:: --gpu-memory-utilization <percentage>
|
||||||
|
|
||||||
|
The percentage of GPU memory to be used for the model executor.
|
||||||
|
|
||||||
|
.. option:: --max-num-batched-tokens <tokens>
|
||||||
|
|
||||||
|
Maximum number of batched tokens per iteration.
|
||||||
|
|
||||||
|
.. option:: --max-num-seqs <sequences>
|
||||||
|
|
||||||
|
Maximum number of sequences per iteration.
|
||||||
|
|
||||||
|
.. option:: --max-paddings <paddings>
|
||||||
|
|
||||||
|
Maximum number of paddings in a batch.
|
||||||
|
|
||||||
|
.. option:: --disable-log-stats
|
||||||
|
|
||||||
|
Disable logging statistics.
|
||||||
|
|
||||||
|
.. option:: --quantization (-q) {awq,squeezellm,None}
|
||||||
|
|
||||||
|
Method used to quantize the weights.
|
||||||
@@ -19,7 +19,7 @@ Alongside each architecture, we include some popular models that use it.
|
|||||||
- :code:`BAAI/Aquila-7B`, :code:`BAAI/AquilaChat-7B`, etc.
|
- :code:`BAAI/Aquila-7B`, :code:`BAAI/AquilaChat-7B`, etc.
|
||||||
* - :code:`BaiChuanForCausalLM`
|
* - :code:`BaiChuanForCausalLM`
|
||||||
- Baichuan
|
- Baichuan
|
||||||
- :code:`baichuan-inc/Baichuan-7B`, :code:`baichuan-inc/Baichuan-13B-Chat`, etc.
|
- :code:`baichuan-inc/Baichuan2-13B-Chat`, :code:`baichuan-inc/Baichuan-7B`, etc.
|
||||||
* - :code:`ChatGLMModel`
|
* - :code:`ChatGLMModel`
|
||||||
- ChatGLM
|
- ChatGLM
|
||||||
- :code:`THUDM/chatglm2-6b`, :code:`THUDM/chatglm3-6b`, etc.
|
- :code:`THUDM/chatglm2-6b`, :code:`THUDM/chatglm3-6b`, etc.
|
||||||
@@ -50,6 +50,9 @@ Alongside each architecture, we include some popular models that use it.
|
|||||||
* - :code:`MistralForCausalLM`
|
* - :code:`MistralForCausalLM`
|
||||||
- Mistral, Mistral-Instruct
|
- Mistral, Mistral-Instruct
|
||||||
- :code:`mistralai/Mistral-7B-v0.1`, :code:`mistralai/Mistral-7B-Instruct-v0.1`, etc.
|
- :code:`mistralai/Mistral-7B-v0.1`, :code:`mistralai/Mistral-7B-Instruct-v0.1`, etc.
|
||||||
|
* - :code:`MixtralForCausalLM`
|
||||||
|
- Mixtral-8x7B, Mixtral-8x7B-Instruct
|
||||||
|
- :code:`mistralai/Mixtral-8x7B-v0.1`, :code:`mistralai/Mixtral-8x7B-Instruct-v0.1`, etc.
|
||||||
* - :code:`MPTForCausalLM`
|
* - :code:`MPTForCausalLM`
|
||||||
- MPT, MPT-Instruct, MPT-Chat, MPT-StoryWriter
|
- MPT, MPT-Instruct, MPT-Chat, MPT-StoryWriter
|
||||||
- :code:`mosaicml/mpt-7b`, :code:`mosaicml/mpt-7b-storywriter`, :code:`mosaicml/mpt-30b`, etc.
|
- :code:`mosaicml/mpt-7b`, :code:`mosaicml/mpt-7b-storywriter`, :code:`mosaicml/mpt-30b`, etc.
|
||||||
|
|||||||
@@ -3,6 +3,12 @@
|
|||||||
AutoAWQ
|
AutoAWQ
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
Please note that AWQ support in vLLM is under-optimized at the moment. We would recommend using the unquantized version of the model for better
|
||||||
|
accuracy and higher throughput. Currently, you can use AWQ as a way to reduce memory footprint. As of now, it is more suitable for low latency
|
||||||
|
inference with small number of concurrent requests. vLLM's AWQ implementation have lower throughput than unquantized version.
|
||||||
|
|
||||||
To create a new 4-bit quantized model, you can leverage `AutoAWQ <https://github.com/casper-hansen/AutoAWQ>`_.
|
To create a new 4-bit quantized model, you can leverage `AutoAWQ <https://github.com/casper-hansen/AutoAWQ>`_.
|
||||||
Quantizing reduces the model's precision from FP16 to INT4 which effectively reduces the file size by ~70%.
|
Quantizing reduces the model's precision from FP16 to INT4 which effectively reduces the file size by ~70%.
|
||||||
The main benefits are lower latency and memory usage.
|
The main benefits are lower latency and memory usage.
|
||||||
|
|||||||
@@ -3,11 +3,41 @@
|
|||||||
Deploying with Docker
|
Deploying with Docker
|
||||||
============================
|
============================
|
||||||
|
|
||||||
|
vLLM offers official docker image for deployment.
|
||||||
|
The image can be used to run OpenAI compatible server.
|
||||||
|
The image is available on Docker Hub as `vllm/vllm-openai <https://hub.docker.com/r/vllm/vllm-openai/tags>`_.
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ docker run --runtime nvidia --gpus all \
|
||||||
|
-v ~/.cache/huggingface:/root/.cache/huggingface \
|
||||||
|
--env "HUGGING_FACE_HUB_TOKEN=<secret>" \
|
||||||
|
-p 8000:8000 \
|
||||||
|
--ipc=host \
|
||||||
|
vllm/vllm-openai:latest \
|
||||||
|
--model mistralai/Mistral-7B-v0.1
|
||||||
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
You can either use the ``ipc=host`` flag or ``--shm-size`` flag to allow the
|
||||||
|
container to access the host's shared memory. vLLM uses PyTorch, which uses shared
|
||||||
|
memory to share data between processes under the hood, particularly for tensor parallel inference.
|
||||||
|
|
||||||
|
|
||||||
You can build and run vLLM from source via the provided dockerfile. To build vLLM:
|
You can build and run vLLM from source via the provided dockerfile. To build vLLM:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ DOCKER_BUILDKIT=1 docker build . --target vllm --tag vllm --build-arg max_jobs=8
|
$ DOCKER_BUILDKIT=1 docker build . --target vllm-openai --tag vllm/vllm-openai # optionally specifies: --build-arg max_jobs=8 --build-arg nvcc_threads=2
|
||||||
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
By default vLLM will build for all GPU types for widest distribution. If you are just building for the
|
||||||
|
current GPU type the machine is running on, you can add the argument ``--build-arg torch_cuda_arch_list=""``
|
||||||
|
for vLLM to find the current GPU type and build for that.
|
||||||
|
|
||||||
|
|
||||||
To run vLLM:
|
To run vLLM:
|
||||||
|
|
||||||
@@ -17,5 +47,5 @@ To run vLLM:
|
|||||||
-v ~/.cache/huggingface:/root/.cache/huggingface \
|
-v ~/.cache/huggingface:/root/.cache/huggingface \
|
||||||
-p 8000:8000 \
|
-p 8000:8000 \
|
||||||
--env "HUGGING_FACE_HUB_TOKEN=<secret>" \
|
--env "HUGGING_FACE_HUB_TOKEN=<secret>" \
|
||||||
vllm <args...>
|
vllm/vllm-openai <args...>
|
||||||
|
|
||||||
|
|||||||
13
docs/source/serving/metrics.rst
Normal file
13
docs/source/serving/metrics.rst
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Production Metrics
|
||||||
|
==================
|
||||||
|
|
||||||
|
vLLM exposes a number of metrics that can be used to monitor the health of the
|
||||||
|
system. These metrics are exposed via the `/metrics` endpoint on the vLLM
|
||||||
|
OpenAI compatible API server.
|
||||||
|
|
||||||
|
The following metrics are exposed:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../vllm/engine/metrics.py
|
||||||
|
:language: python
|
||||||
|
:start-after: begin-metrics-definitions
|
||||||
|
:end-before: end-metrics-definitions
|
||||||
@@ -55,7 +55,7 @@ Start the serving the LLaMA-13B model on an A100 GPU:
|
|||||||
|
|
||||||
$ sky launch serving.yaml
|
$ sky launch serving.yaml
|
||||||
|
|
||||||
Check the output of the command. There will be a sharable gradio link (like the last line of the following). Open it in your browser to use the LLaMA model to do the text completion.
|
Check the output of the command. There will be a shareable gradio link (like the last line of the following). Open it in your browser to use the LLaMA model to do the text completion.
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
|
|||||||
31
docs/source/serving/serving_with_langchain.rst
Normal file
31
docs/source/serving/serving_with_langchain.rst
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
.. _run_on_langchain:
|
||||||
|
|
||||||
|
Serving with Langchain
|
||||||
|
============================
|
||||||
|
|
||||||
|
vLLM is also available via `Langchain <https://github.com/langchain-ai/langchain>`_ .
|
||||||
|
|
||||||
|
To install langchain, run
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ pip install langchain -q
|
||||||
|
|
||||||
|
To run inference on a single or multiple GPUs, use ``VLLM`` class from ``langchain``.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from langchain.llms import VLLM
|
||||||
|
|
||||||
|
llm = VLLM(model="mosaicml/mpt-7b",
|
||||||
|
trust_remote_code=True, # mandatory for hf models
|
||||||
|
max_new_tokens=128,
|
||||||
|
top_k=10,
|
||||||
|
top_p=0.95,
|
||||||
|
temperature=0.8,
|
||||||
|
# tensor_parallel_size=... # for distributed inference
|
||||||
|
)
|
||||||
|
|
||||||
|
print(llm("What is the capital of France ?"))
|
||||||
|
|
||||||
|
Please refer to this `Tutorial <https://github.com/langchain-ai/langchain/blob/master/docs/extras/integrations/llms/vllm.ipynb>`_ for more details.
|
||||||
@@ -1,18 +1,19 @@
|
|||||||
import openai
|
from openai import OpenAI
|
||||||
|
|
||||||
# Modify OpenAI's API key and API base to use vLLM's API server.
|
# Modify OpenAI's API key and API base to use vLLM's API server.
|
||||||
openai.api_key = "EMPTY"
|
openai_api_key = "EMPTY"
|
||||||
openai.api_base = "http://localhost:8000/v1"
|
openai_api_base = "http://localhost:8000/v1"
|
||||||
|
|
||||||
# List models API
|
client = OpenAI(
|
||||||
models = openai.Model.list()
|
# defaults to os.environ.get("OPENAI_API_KEY")
|
||||||
print("Models:", models)
|
api_key=openai_api_key,
|
||||||
|
base_url=openai_api_base,
|
||||||
|
)
|
||||||
|
|
||||||
model = models["data"][0]["id"]
|
models = client.models.list()
|
||||||
|
model = models.data[0].id
|
||||||
|
|
||||||
# Chat completion API
|
chat_completion = client.chat.completions.create(
|
||||||
chat_completion = openai.ChatCompletion.create(
|
|
||||||
model=model,
|
|
||||||
messages=[{
|
messages=[{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful assistant."
|
"content": "You are a helpful assistant."
|
||||||
@@ -27,7 +28,10 @@ chat_completion = openai.ChatCompletion.create(
|
|||||||
}, {
|
}, {
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": "Where was it played?"
|
"content": "Where was it played?"
|
||||||
}])
|
}],
|
||||||
|
model=model,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
print("Chat completion results:")
|
print("Chat completion results:")
|
||||||
print(chat_completion)
|
print(chat_completion)
|
||||||
|
|||||||
@@ -1,24 +1,28 @@
|
|||||||
import openai
|
from openai import OpenAI
|
||||||
|
|
||||||
# Modify OpenAI's API key and API base to use vLLM's API server.
|
# Modify OpenAI's API key and API base to use vLLM's API server.
|
||||||
openai.api_key = "EMPTY"
|
openai_api_key = "EMPTY"
|
||||||
openai.api_base = "http://localhost:8000/v1"
|
openai_api_base = "http://localhost:8000/v1"
|
||||||
|
|
||||||
# List models API
|
client = OpenAI(
|
||||||
models = openai.Model.list()
|
# defaults to os.environ.get("OPENAI_API_KEY")
|
||||||
print("Models:", models)
|
api_key=openai_api_key,
|
||||||
|
base_url=openai_api_base,
|
||||||
|
)
|
||||||
|
|
||||||
model = models["data"][0]["id"]
|
models = client.models.list()
|
||||||
|
model = models.data[0].id
|
||||||
|
|
||||||
# Completion API
|
# Completion API
|
||||||
stream = False
|
stream = False
|
||||||
completion = openai.Completion.create(
|
completion = client.completions.create(
|
||||||
model=model,
|
model=model,
|
||||||
prompt="A robot may not injure a human being",
|
prompt="A robot may not injure a human being",
|
||||||
echo=False,
|
echo=False,
|
||||||
n=2,
|
n=2,
|
||||||
stream=stream,
|
stream=stream,
|
||||||
logprobs=3)
|
logprobs=3
|
||||||
|
)
|
||||||
|
|
||||||
print("Completion results:")
|
print("Completion results:")
|
||||||
if stream:
|
if stream:
|
||||||
|
|||||||
29
examples/template_alpaca.jinja
Normal file
29
examples/template_alpaca.jinja
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{{ (messages|selectattr('role', 'equalto', 'system')|list|last).content|trim if (messages|selectattr('role', 'equalto', 'system')|list) else '' }}
|
||||||
|
|
||||||
|
{% for message in messages %}
|
||||||
|
{% if message['role'] == 'user' %}
|
||||||
|
### Instruction:
|
||||||
|
{{ message['content']|trim -}}
|
||||||
|
{% if not loop.last %}
|
||||||
|
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% elif message['role'] == 'assistant' %}
|
||||||
|
### Response:
|
||||||
|
{{ message['content']|trim -}}
|
||||||
|
{% if not loop.last %}
|
||||||
|
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% elif message['role'] == 'user_context' %}
|
||||||
|
### Input:
|
||||||
|
{{ message['content']|trim -}}
|
||||||
|
{% if not loop.last %}
|
||||||
|
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% if add_generation_prompt and messages[-1]['role'] != 'assistant' %}
|
||||||
|
### Response:
|
||||||
|
{% endif %}
|
||||||
2
examples/template_chatml.jinja
Normal file
2
examples/template_chatml.jinja
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content']}}{% if (loop.last and add_generation_prompt) or not loop.last %}{{ '<|im_end|>' + '\n'}}{% endif %}{% endfor %}
|
||||||
|
{% if add_generation_prompt and messages[-1]['role'] != 'assistant' %}{{ '<|im_start|>assistant\n' }}{% endif %}
|
||||||
30
examples/template_inkbot.jinja
Normal file
30
examples/template_inkbot.jinja
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<#meta#>
|
||||||
|
- Date: {{ (messages|selectattr('role', 'equalto', 'meta-current_date')|list|last).content|trim if (messages|selectattr('role', 'equalto', 'meta-current_date')|list) else '' }}
|
||||||
|
- Task: {{ (messages|selectattr('role', 'equalto', 'meta-task_name')|list|last).content|trim if (messages|selectattr('role', 'equalto', 'meta-task_name')|list) else '' }}
|
||||||
|
<#system#>
|
||||||
|
{{ (messages|selectattr('role', 'equalto', 'system')|list|last).content|trim if (messages|selectattr('role', 'equalto', 'system')|list) else '' }}
|
||||||
|
<#chat#>
|
||||||
|
{% for message in messages %}
|
||||||
|
{% if message['role'] == 'user' %}
|
||||||
|
<#user#>
|
||||||
|
{{ message['content']|trim -}}
|
||||||
|
{% if not loop.last %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% elif message['role'] == 'assistant' %}
|
||||||
|
<#bot#>
|
||||||
|
{{ message['content']|trim -}}
|
||||||
|
{% if not loop.last %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% elif message['role'] == 'user_context' %}
|
||||||
|
<#user_context#>
|
||||||
|
{{ message['content']|trim -}}
|
||||||
|
{% if not loop.last %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% if add_generation_prompt and messages[-1]['role'] != 'assistant' %}
|
||||||
|
<#bot#>
|
||||||
|
{% endif %}
|
||||||
16
format.sh
16
format.sh
@@ -7,7 +7,7 @@
|
|||||||
# # Format files that differ from origin/main.
|
# # Format files that differ from origin/main.
|
||||||
# bash format.sh
|
# bash format.sh
|
||||||
|
|
||||||
# # Commit changed files with message 'Run yapf and pylint'
|
# # Commit changed files with message 'Run yapf and ruff'
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# YAPF + Clang formatter (if installed). This script formats all changed files from the last mergebase.
|
# YAPF + Clang formatter (if installed). This script formats all changed files from the last mergebase.
|
||||||
@@ -22,7 +22,7 @@ ROOT="$(git rev-parse --show-toplevel)"
|
|||||||
builtin cd "$ROOT" || exit 1
|
builtin cd "$ROOT" || exit 1
|
||||||
|
|
||||||
YAPF_VERSION=$(yapf --version | awk '{print $2}')
|
YAPF_VERSION=$(yapf --version | awk '{print $2}')
|
||||||
PYLINT_VERSION=$(pylint --version | head -n 1 | awk '{print $2}')
|
RUFF_VERSION=$(ruff --version | awk '{print $2}')
|
||||||
MYPY_VERSION=$(mypy --version | awk '{print $2}')
|
MYPY_VERSION=$(mypy --version | awk '{print $2}')
|
||||||
|
|
||||||
# # params: tool name, tool version, required version
|
# # params: tool name, tool version, required version
|
||||||
@@ -34,7 +34,7 @@ tool_version_check() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tool_version_check "yapf" $YAPF_VERSION "$(grep yapf requirements-dev.txt | cut -d'=' -f3)"
|
tool_version_check "yapf" $YAPF_VERSION "$(grep yapf requirements-dev.txt | cut -d'=' -f3)"
|
||||||
tool_version_check "pylint" $PYLINT_VERSION "$(grep "pylint==" requirements-dev.txt | cut -d'=' -f3)"
|
tool_version_check "ruff" $RUFF_VERSION "$(grep "ruff==" requirements-dev.txt | cut -d'=' -f3)"
|
||||||
tool_version_check "mypy" "$MYPY_VERSION" "$(grep mypy requirements-dev.txt | cut -d'=' -f3)"
|
tool_version_check "mypy" "$MYPY_VERSION" "$(grep mypy requirements-dev.txt | cut -d'=' -f3)"
|
||||||
|
|
||||||
YAPF_FLAGS=(
|
YAPF_FLAGS=(
|
||||||
@@ -95,14 +95,14 @@ echo 'vLLM yapf: Done'
|
|||||||
|
|
||||||
# Lint specified files
|
# Lint specified files
|
||||||
lint() {
|
lint() {
|
||||||
pylint "$@"
|
ruff "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Lint files that differ from main branch. Ignores dirs that are not slated
|
# Lint files that differ from main branch. Ignores dirs that are not slated
|
||||||
# for autolint yet.
|
# for autolint yet.
|
||||||
lint_changed() {
|
lint_changed() {
|
||||||
# The `if` guard ensures that the list of filenames is not empty, which
|
# The `if` guard ensures that the list of filenames is not empty, which
|
||||||
# could cause pylint to receive 0 positional arguments, making it hang
|
# could cause ruff to receive 0 positional arguments, making it hang
|
||||||
# waiting for STDIN.
|
# waiting for STDIN.
|
||||||
#
|
#
|
||||||
# `diff-filter=ACM` and $MERGEBASE is to ensure we only lint files that
|
# `diff-filter=ACM` and $MERGEBASE is to ensure we only lint files that
|
||||||
@@ -111,13 +111,13 @@ lint_changed() {
|
|||||||
|
|
||||||
if ! git diff --diff-filter=ACM --quiet --exit-code "$MERGEBASE" -- '*.py' '*.pyi' &>/dev/null; then
|
if ! git diff --diff-filter=ACM --quiet --exit-code "$MERGEBASE" -- '*.py' '*.pyi' &>/dev/null; then
|
||||||
git diff --name-only --diff-filter=ACM "$MERGEBASE" -- '*.py' '*.pyi' | xargs \
|
git diff --name-only --diff-filter=ACM "$MERGEBASE" -- '*.py' '*.pyi' | xargs \
|
||||||
pylint
|
ruff
|
||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Run Pylint
|
# Run Ruff
|
||||||
echo 'vLLM Pylint:'
|
echo 'vLLM Ruff:'
|
||||||
## This flag lints individual files. --files *must* be the first command line
|
## This flag lints individual files. --files *must* be the first command line
|
||||||
## arg to use this option.
|
## arg to use this option.
|
||||||
if [[ "$1" == '--files' ]]; then
|
if [[ "$1" == '--files' ]]; then
|
||||||
|
|||||||
22
patch_xformers-0.0.22.post7.rocm.sh
Normal file
22
patch_xformers-0.0.22.post7.rocm.sh
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
export XFORMERS_FMHA_FLASH_PATH=$(python -c 'from xformers import ops as xops; print(xops.fmha.flash.__file__)')
|
||||||
|
export XFORMERS_FMHA_COMMON_PATH=$(python -c 'from xformers import ops as xops; print(xops.fmha.common.__file__)')
|
||||||
|
|
||||||
|
echo $XFORMERS_FMHA_FLASH_PATH
|
||||||
|
echo $XFORMERS_FMHA_COMMON_PATH
|
||||||
|
|
||||||
|
if ! patch -R -p0 -s -f --dry-run $XFORMERS_FMHA_FLASH_PATH "./rocm_patch/flashpy_xformers-0.0.22.post7.rocm.patch"; then
|
||||||
|
echo "Applying patch to ${XFORMERS_FMHA_FLASH_PATH}"
|
||||||
|
patch -p0 $XFORMERS_FMHA_FLASH_PATH "./rocm_patch/flashpy_xformers-0.0.22.post7.rocm.patch"
|
||||||
|
echo "Successfully patch ${XFORMERS_FMHA_FLASH_PATH}"
|
||||||
|
else
|
||||||
|
echo "${XFORMERS_FMHA_FLASH_PATH} was patched before"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! patch -R -p0 -s -f --dry-run $XFORMERS_FMHA_COMMON_PATH "./rocm_patch/commonpy_xformers-0.0.22.post7.rocm.patch"; then
|
||||||
|
echo "Applying patch to ${XFORMERS_FMHA_COMMON_PATH}"
|
||||||
|
patch -p0 $XFORMERS_FMHA_COMMON_PATH "./rocm_patch/commonpy_xformers-0.0.22.post7.rocm.patch"
|
||||||
|
echo "Successfully patch ${XFORMERS_FMHA_COMMON_PATH}"
|
||||||
|
else
|
||||||
|
echo "${XFORMERS_FMHA_COMMON_PATH} was patched before"
|
||||||
|
fi
|
||||||
@@ -1,9 +1,34 @@
|
|||||||
[build-system]
|
[build-system]
|
||||||
|
# Should be mirrored in requirements-build.txt
|
||||||
requires = [
|
requires = [
|
||||||
"ninja",
|
"ninja",
|
||||||
"packaging",
|
"packaging",
|
||||||
"setuptools",
|
"setuptools >= 49.4.0",
|
||||||
"torch >= 2.1.0",
|
"torch >= 2.1.0",
|
||||||
"wheel",
|
"wheel",
|
||||||
]
|
]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
select = [
|
||||||
|
# pycodestyle
|
||||||
|
"E",
|
||||||
|
# Pyflakes
|
||||||
|
"F",
|
||||||
|
# pyupgrade
|
||||||
|
# "UP",
|
||||||
|
# flake8-bugbear
|
||||||
|
"B",
|
||||||
|
# flake8-simplify
|
||||||
|
"SIM",
|
||||||
|
# isort
|
||||||
|
# "I",
|
||||||
|
]
|
||||||
|
ignore = [
|
||||||
|
# star imports
|
||||||
|
"F405", "F403",
|
||||||
|
# lambda expression assignment
|
||||||
|
"E731",
|
||||||
|
# line too long, handled by black formatting
|
||||||
|
"E501",
|
||||||
|
]
|
||||||
|
|||||||
6
requirements-build.txt
Normal file
6
requirements-build.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Should be mirrored in pyproject.toml
|
||||||
|
ninja
|
||||||
|
packaging
|
||||||
|
setuptools>=49.4.0
|
||||||
|
torch>=2.1.0
|
||||||
|
wheel
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# formatting
|
# formatting
|
||||||
yapf==0.32.0
|
yapf==0.32.0
|
||||||
pylint==2.8.2
|
ruff==0.1.5
|
||||||
|
|
||||||
# type checking
|
# type checking
|
||||||
mypy==0.991
|
mypy==0.991
|
||||||
|
|||||||
17
requirements-rocm.txt
Normal file
17
requirements-rocm.txt
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
ninja # For faster builds.
|
||||||
|
typing-extensions>=4.8.0
|
||||||
|
starlette
|
||||||
|
psutil
|
||||||
|
ray >= 2.5.1
|
||||||
|
pandas # Required for Ray data.
|
||||||
|
pyarrow # Required for Ray data.
|
||||||
|
sentencepiece # Required for LLaMA tokenizer.
|
||||||
|
numpy
|
||||||
|
tokenizers>=0.15.0
|
||||||
|
huggingface_hub<0.18,>=0.16.4
|
||||||
|
einops # Required for phi-1_5
|
||||||
|
transformers >= 4.34.0 # Required for Mistral.
|
||||||
|
fastapi
|
||||||
|
uvicorn[standard]
|
||||||
|
pydantic == 1.10.13 # Required for OpenAI server.
|
||||||
|
aioprometheus[starlette]
|
||||||
@@ -12,3 +12,4 @@ xformers >= 0.0.22.post7 # Required for CUDA 12.1.
|
|||||||
fastapi
|
fastapi
|
||||||
uvicorn[standard]
|
uvicorn[standard]
|
||||||
pydantic == 1.10.13 # Required for OpenAI server.
|
pydantic == 1.10.13 # Required for OpenAI server.
|
||||||
|
aioprometheus[starlette]
|
||||||
|
|||||||
13
rocm_patch/commonpy_xformers-0.0.22.post7.rocm.patch
Normal file
13
rocm_patch/commonpy_xformers-0.0.22.post7.rocm.patch
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
--- /opt/conda/envs/py_3.10/lib/python3.10/site-packages/xformers/ops/fmha/common.py 2023-11-29 03:17:03.930103539 +0000
|
||||||
|
+++ common.py 2023-11-28 16:14:19.846233146 +0000
|
||||||
|
@@ -298,8 +298,8 @@
|
||||||
|
dtype = d.query.dtype
|
||||||
|
if device_type not in cls.SUPPORTED_DEVICES:
|
||||||
|
reasons.append(f"device={device_type} (supported: {cls.SUPPORTED_DEVICES})")
|
||||||
|
- if device_type == "cuda" and not _built_with_cuda:
|
||||||
|
- reasons.append("xFormers wasn't build with CUDA support")
|
||||||
|
+ #if device_type == "cuda" and not _built_with_cuda:
|
||||||
|
+ # reasons.append("xFormers wasn't build with CUDA support")
|
||||||
|
if device_type == "cuda":
|
||||||
|
device_capability = torch.cuda.get_device_capability(d.device)
|
||||||
|
if device_capability < cls.CUDA_MINIMUM_COMPUTE_CAPABILITY:
|
||||||
134
rocm_patch/flashpy_xformers-0.0.22.post7.rocm.patch
Normal file
134
rocm_patch/flashpy_xformers-0.0.22.post7.rocm.patch
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
--- /opt/conda/envs/py_3.10/lib/python3.10/site-packages/xformers/ops/fmha/flash.py 2023-11-29 03:17:03.930103539 +0000
|
||||||
|
+++ flash.py 2023-11-28 16:14:25.206128903 +0000
|
||||||
|
@@ -31,39 +31,39 @@
|
||||||
|
|
||||||
|
FLASH_VERSION = "0.0.0"
|
||||||
|
try:
|
||||||
|
- try:
|
||||||
|
- from ... import _C_flashattention # type: ignore[attr-defined]
|
||||||
|
- from ..._cpp_lib import _build_metadata
|
||||||
|
-
|
||||||
|
- if _build_metadata is not None:
|
||||||
|
- FLASH_VERSION = _build_metadata.flash_version
|
||||||
|
- except ImportError:
|
||||||
|
- import flash_attn
|
||||||
|
- from flash_attn.flash_attn_interface import flash_attn_cuda as _C_flashattention
|
||||||
|
-
|
||||||
|
- FLASH_VERSION = flash_attn.__version__
|
||||||
|
- flash_ver_parsed = tuple(int(s) for s in FLASH_VERSION.split(".")[:2])
|
||||||
|
- if flash_ver_parsed < (2, 3):
|
||||||
|
- raise ImportError("Requires 2.3 for sliding window support")
|
||||||
|
+ #try:
|
||||||
|
+ # from ... import _C_flashattention # type: ignore[attr-defined]
|
||||||
|
+ # from ..._cpp_lib import _build_metadata
|
||||||
|
+
|
||||||
|
+ # if _build_metadata is not None:
|
||||||
|
+ # FLASH_VERSION = _build_metadata.flash_version
|
||||||
|
+ #except ImportError:
|
||||||
|
+ import flash_attn
|
||||||
|
+ from flash_attn.flash_attn_interface import flash_attn_cuda as _C_flashattention
|
||||||
|
+
|
||||||
|
+ FLASH_VERSION = flash_attn.__version__
|
||||||
|
+ # flash_ver_parsed = tuple(int(s) for s in FLASH_VERSION.split(".")[:2])
|
||||||
|
+ # if flash_ver_parsed < (2, 3):
|
||||||
|
+ # raise ImportError("Requires 2.3 for sliding window support")
|
||||||
|
|
||||||
|
# create library so that flash-attn goes through the PyTorch Dispatcher
|
||||||
|
- _flash_lib = torch.library.Library("xformers_flash", "DEF")
|
||||||
|
+ #_flash_lib = torch.library.Library("xformers_flash", "DEF")
|
||||||
|
|
||||||
|
- _flash_lib.define(
|
||||||
|
- "flash_fwd(Tensor query, Tensor key, Tensor value, "
|
||||||
|
- "Tensor? cu_seqlens_q, Tensor? cu_seqlens_k, "
|
||||||
|
- "int max_seqlen_q, int max_seqlen_k, "
|
||||||
|
- "float p, float softmax_scale, "
|
||||||
|
- "bool is_causal, int window_size, bool return_softmax) -> (Tensor, Tensor, Tensor)"
|
||||||
|
- )
|
||||||
|
-
|
||||||
|
- _flash_lib.define(
|
||||||
|
- "flash_bwd(Tensor dout, Tensor query, Tensor key, Tensor value, "
|
||||||
|
- "Tensor out, Tensor softmax_lse_, Tensor dq, Tensor dk, Tensor dv, "
|
||||||
|
- "Tensor cu_seqlens_q, Tensor cu_seqlens_k, "
|
||||||
|
- "int max_seqlen_q, int max_seqlen_k, "
|
||||||
|
- "float p, float softmax_scale, bool is_causal, int window_size, Tensor rng_state) -> (Tensor, Tensor, Tensor)"
|
||||||
|
- )
|
||||||
|
+ #_flash_lib.define(
|
||||||
|
+ # "flash_fwd(Tensor query, Tensor key, Tensor value, "
|
||||||
|
+ # "Tensor? cu_seqlens_q, Tensor? cu_seqlens_k, "
|
||||||
|
+ # "int max_seqlen_q, int max_seqlen_k, "
|
||||||
|
+ # "float p, float softmax_scale, "
|
||||||
|
+ # "bool is_causal, int window_size, bool return_softmax) -> (Tensor, Tensor, Tensor)"
|
||||||
|
+ #)
|
||||||
|
+
|
||||||
|
+ #_flash_lib.define(
|
||||||
|
+ # "flash_bwd(Tensor dout, Tensor query, Tensor key, Tensor value, "
|
||||||
|
+ # "Tensor out, Tensor softmax_lse_, Tensor dq, Tensor dk, Tensor dv, "
|
||||||
|
+ # "Tensor cu_seqlens_q, Tensor cu_seqlens_k, "
|
||||||
|
+ # "int max_seqlen_q, int max_seqlen_k, "
|
||||||
|
+ # "float p, float softmax_scale, bool is_causal, int window_size, Tensor rng_state) -> (Tensor, Tensor, Tensor)"
|
||||||
|
+ #)
|
||||||
|
|
||||||
|
def _flash_fwd(
|
||||||
|
query,
|
||||||
|
@@ -98,8 +98,8 @@
|
||||||
|
p,
|
||||||
|
softmax_scale,
|
||||||
|
is_causal,
|
||||||
|
- window_size - 1, # window_size_left
|
||||||
|
- -1, # window_size_right
|
||||||
|
+ # window_size - 1, # window_size_left
|
||||||
|
+ # -1, # window_size_right
|
||||||
|
return_softmax,
|
||||||
|
None, # rng
|
||||||
|
)
|
||||||
|
@@ -127,8 +127,8 @@
|
||||||
|
softmax_scale,
|
||||||
|
False,
|
||||||
|
is_causal,
|
||||||
|
- window_size - 1, # window_size_left
|
||||||
|
- -1, # window_size_right
|
||||||
|
+ # window_size - 1, # window_size_left
|
||||||
|
+ # -1, # window_size_right
|
||||||
|
return_softmax,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
@@ -169,8 +169,8 @@
|
||||||
|
p,
|
||||||
|
softmax_scale,
|
||||||
|
is_causal,
|
||||||
|
- window_size - 1, # window_size_left
|
||||||
|
- -1, # window_size_right
|
||||||
|
+ # window_size - 1, # window_size_left
|
||||||
|
+ # -1, # window_size_right
|
||||||
|
None,
|
||||||
|
rng_state,
|
||||||
|
)
|
||||||
|
@@ -193,15 +193,15 @@
|
||||||
|
softmax_scale,
|
||||||
|
False, # zero_tensors
|
||||||
|
is_causal,
|
||||||
|
- window_size - 1, # window_size_left
|
||||||
|
- -1, # window_size_right
|
||||||
|
+ # window_size - 1, # window_size_left
|
||||||
|
+ # -1, # window_size_right
|
||||||
|
None,
|
||||||
|
rng_state,
|
||||||
|
)
|
||||||
|
return dq, dk, dv
|
||||||
|
|
||||||
|
- _flash_lib.impl("flash_fwd", _flash_fwd, "CUDA")
|
||||||
|
- _flash_lib.impl("flash_bwd", _flash_bwd, "CUDA")
|
||||||
|
+ #_flash_lib.impl("flash_fwd", _flash_fwd, "CUDA")
|
||||||
|
+ #_flash_lib.impl("flash_bwd", _flash_bwd, "CUDA")
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@@ -348,7 +348,7 @@
|
||||||
|
implementation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
- OPERATOR = get_operator("xformers_flash", "flash_fwd")
|
||||||
|
+ OPERATOR = _flash_fwd # get_operator("xformers_flash", "flash_fwd")
|
||||||
|
SUPPORTED_DEVICES: Set[str] = {"cuda"}
|
||||||
|
CUDA_MINIMUM_COMPUTE_CAPABILITY = (8, 0)
|
||||||
|
SUPPORTED_DTYPES: Set[torch.dtype] = {torch.half, torch.bfloat16}
|
||||||
295
setup.py
295
setup.py
@@ -8,27 +8,83 @@ import warnings
|
|||||||
from packaging.version import parse, Version
|
from packaging.version import parse, Version
|
||||||
import setuptools
|
import setuptools
|
||||||
import torch
|
import torch
|
||||||
from torch.utils.cpp_extension import BuildExtension, CUDAExtension, CUDA_HOME
|
from torch.utils.cpp_extension import BuildExtension, CUDAExtension, CUDA_HOME, ROCM_HOME
|
||||||
|
|
||||||
ROOT_DIR = os.path.dirname(__file__)
|
ROOT_DIR = os.path.dirname(__file__)
|
||||||
|
|
||||||
MAIN_CUDA_VERSION = "12.1"
|
MAIN_CUDA_VERSION = "12.1"
|
||||||
|
|
||||||
# Supported NVIDIA GPU architectures.
|
# Supported NVIDIA GPU architectures.
|
||||||
SUPPORTED_ARCHS = {"7.0", "7.5", "8.0", "8.6", "8.9", "9.0"}
|
NVIDIA_SUPPORTED_ARCHS = {"7.0", "7.5", "8.0", "8.6", "8.9", "9.0"}
|
||||||
|
ROCM_SUPPORTED_ARCHS = {"gfx90a", "gfx908", "gfx906", "gfx1030", "gfx1100"}
|
||||||
|
# SUPPORTED_ARCHS = NVIDIA_SUPPORTED_ARCHS.union(ROCM_SUPPORTED_ARCHS)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_hip() -> bool:
|
||||||
|
return torch.version.hip is not None
|
||||||
|
|
||||||
|
|
||||||
|
def _is_cuda() -> bool:
|
||||||
|
return torch.version.cuda is not None
|
||||||
|
|
||||||
|
|
||||||
# Compiler flags.
|
# Compiler flags.
|
||||||
CXX_FLAGS = ["-g", "-O2", "-std=c++17"]
|
CXX_FLAGS = ["-g", "-O2", "-std=c++17"]
|
||||||
# TODO(woosuk): Should we use -O3?
|
# TODO(woosuk): Should we use -O3?
|
||||||
NVCC_FLAGS = ["-O2", "-std=c++17"]
|
NVCC_FLAGS = ["-O2", "-std=c++17"]
|
||||||
|
|
||||||
|
if _is_hip():
|
||||||
|
if ROCM_HOME is None:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Cannot find ROCM_HOME. ROCm must be available to build the package."
|
||||||
|
)
|
||||||
|
NVCC_FLAGS += ["-DUSE_ROCM"]
|
||||||
|
|
||||||
|
if _is_cuda() and CUDA_HOME is None:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Cannot find CUDA_HOME. CUDA must be available to build the package.")
|
||||||
|
|
||||||
ABI = 1 if torch._C._GLIBCXX_USE_CXX11_ABI else 0
|
ABI = 1 if torch._C._GLIBCXX_USE_CXX11_ABI else 0
|
||||||
CXX_FLAGS += [f"-D_GLIBCXX_USE_CXX11_ABI={ABI}"]
|
CXX_FLAGS += [f"-D_GLIBCXX_USE_CXX11_ABI={ABI}"]
|
||||||
NVCC_FLAGS += [f"-D_GLIBCXX_USE_CXX11_ABI={ABI}"]
|
NVCC_FLAGS += [f"-D_GLIBCXX_USE_CXX11_ABI={ABI}"]
|
||||||
|
|
||||||
if CUDA_HOME is None:
|
|
||||||
raise RuntimeError(
|
def get_amdgpu_offload_arch():
|
||||||
"Cannot find CUDA_HOME. CUDA must be available to build the package.")
|
command = "/opt/rocm/llvm/bin/amdgpu-offload-arch"
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output([command])
|
||||||
|
return output.decode('utf-8').strip()
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
error_message = f"Error: {e}"
|
||||||
|
raise RuntimeError(error_message) from e
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
# If the command is not found, print an error message
|
||||||
|
error_message = f"The command {command} was not found."
|
||||||
|
raise RuntimeError(error_message) from e
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_hipcc_rocm_version():
|
||||||
|
# Run the hipcc --version command
|
||||||
|
result = subprocess.run(['hipcc', '--version'],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
text=True)
|
||||||
|
|
||||||
|
# Check if the command was executed successfully
|
||||||
|
if result.returncode != 0:
|
||||||
|
print("Error running 'hipcc --version'")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Extract the version using a regular expression
|
||||||
|
match = re.search(r'HIP version: (\S+)', result.stdout)
|
||||||
|
if match:
|
||||||
|
# Return the version string
|
||||||
|
return match.group(1)
|
||||||
|
else:
|
||||||
|
print("Could not find HIP version in the output")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_nvcc_cuda_version(cuda_dir: str) -> Version:
|
def get_nvcc_cuda_version(cuda_dir: str) -> Version:
|
||||||
@@ -61,27 +117,30 @@ def get_torch_arch_list() -> Set[str]:
|
|||||||
return set()
|
return set()
|
||||||
|
|
||||||
# Filter out the invalid architectures and print a warning.
|
# Filter out the invalid architectures and print a warning.
|
||||||
valid_archs = SUPPORTED_ARCHS.union({s + "+PTX" for s in SUPPORTED_ARCHS})
|
valid_archs = NVIDIA_SUPPORTED_ARCHS.union(
|
||||||
|
{s + "+PTX"
|
||||||
|
for s in NVIDIA_SUPPORTED_ARCHS})
|
||||||
arch_list = torch_arch_list.intersection(valid_archs)
|
arch_list = torch_arch_list.intersection(valid_archs)
|
||||||
# If none of the specified architectures are valid, raise an error.
|
# If none of the specified architectures are valid, raise an error.
|
||||||
if not arch_list:
|
if not arch_list:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"None of the CUDA architectures in `TORCH_CUDA_ARCH_LIST` env "
|
"None of the CUDA/ROCM architectures in `TORCH_CUDA_ARCH_LIST` env "
|
||||||
f"variable ({env_arch_list}) is supported. "
|
f"variable ({env_arch_list}) is supported. "
|
||||||
f"Supported CUDA architectures are: {valid_archs}.")
|
f"Supported CUDA/ROCM architectures are: {valid_archs}.")
|
||||||
invalid_arch_list = torch_arch_list - valid_archs
|
invalid_arch_list = torch_arch_list - valid_archs
|
||||||
if invalid_arch_list:
|
if invalid_arch_list:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
f"Unsupported CUDA architectures ({invalid_arch_list}) are "
|
f"Unsupported CUDA/ROCM architectures ({invalid_arch_list}) are "
|
||||||
"excluded from the `TORCH_CUDA_ARCH_LIST` env variable "
|
"excluded from the `TORCH_CUDA_ARCH_LIST` env variable "
|
||||||
f"({env_arch_list}). Supported CUDA architectures are: "
|
f"({env_arch_list}). Supported CUDA/ROCM architectures are: "
|
||||||
f"{valid_archs}.")
|
f"{valid_archs}.",
|
||||||
|
stacklevel=2)
|
||||||
return arch_list
|
return arch_list
|
||||||
|
|
||||||
|
|
||||||
# First, check the TORCH_CUDA_ARCH_LIST environment variable.
|
# First, check the TORCH_CUDA_ARCH_LIST environment variable.
|
||||||
compute_capabilities = get_torch_arch_list()
|
compute_capabilities = get_torch_arch_list()
|
||||||
if not compute_capabilities:
|
if _is_cuda() and not compute_capabilities:
|
||||||
# If TORCH_CUDA_ARCH_LIST is not defined or empty, target all available
|
# If TORCH_CUDA_ARCH_LIST is not defined or empty, target all available
|
||||||
# GPUs on the current machine.
|
# GPUs on the current machine.
|
||||||
device_count = torch.cuda.device_count()
|
device_count = torch.cuda.device_count()
|
||||||
@@ -92,135 +151,90 @@ if not compute_capabilities:
|
|||||||
"GPUs with compute capability below 7.0 are not supported.")
|
"GPUs with compute capability below 7.0 are not supported.")
|
||||||
compute_capabilities.add(f"{major}.{minor}")
|
compute_capabilities.add(f"{major}.{minor}")
|
||||||
|
|
||||||
nvcc_cuda_version = get_nvcc_cuda_version(CUDA_HOME)
|
if _is_cuda():
|
||||||
if not compute_capabilities:
|
nvcc_cuda_version = get_nvcc_cuda_version(CUDA_HOME)
|
||||||
# If no GPU is specified nor available, add all supported architectures
|
if not compute_capabilities:
|
||||||
# based on the NVCC CUDA version.
|
# If no GPU is specified nor available, add all supported architectures
|
||||||
compute_capabilities = SUPPORTED_ARCHS.copy()
|
# based on the NVCC CUDA version.
|
||||||
if nvcc_cuda_version < Version("11.1"):
|
compute_capabilities = NVIDIA_SUPPORTED_ARCHS.copy()
|
||||||
compute_capabilities.remove("8.6")
|
if nvcc_cuda_version < Version("11.1"):
|
||||||
if nvcc_cuda_version < Version("11.8"):
|
compute_capabilities.remove("8.6")
|
||||||
compute_capabilities.remove("8.9")
|
if nvcc_cuda_version < Version("11.8"):
|
||||||
compute_capabilities.remove("9.0")
|
compute_capabilities.remove("8.9")
|
||||||
|
compute_capabilities.remove("9.0")
|
||||||
# Validate the NVCC CUDA version.
|
# Validate the NVCC CUDA version.
|
||||||
if nvcc_cuda_version < Version("11.0"):
|
if nvcc_cuda_version < Version("11.0"):
|
||||||
raise RuntimeError("CUDA 11.0 or higher is required to build the package.")
|
raise RuntimeError(
|
||||||
if nvcc_cuda_version < Version("11.1"):
|
"CUDA 11.0 or higher is required to build the package.")
|
||||||
if any(cc.startswith("8.6") for cc in compute_capabilities):
|
if (nvcc_cuda_version < Version("11.1")
|
||||||
|
and any(cc.startswith("8.6") for cc in compute_capabilities)):
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"CUDA 11.1 or higher is required for compute capability 8.6.")
|
"CUDA 11.1 or higher is required for compute capability 8.6.")
|
||||||
if nvcc_cuda_version < Version("11.8"):
|
if nvcc_cuda_version < Version("11.8"):
|
||||||
if any(cc.startswith("8.9") for cc in compute_capabilities):
|
if any(cc.startswith("8.9") for cc in compute_capabilities):
|
||||||
# CUDA 11.8 is required to generate the code targeting compute capability 8.9.
|
# CUDA 11.8 is required to generate the code targeting compute capability 8.9.
|
||||||
# However, GPUs with compute capability 8.9 can also run the code generated by
|
# However, GPUs with compute capability 8.9 can also run the code generated by
|
||||||
# the previous versions of CUDA 11 and targeting compute capability 8.0.
|
# the previous versions of CUDA 11 and targeting compute capability 8.0.
|
||||||
# Therefore, if CUDA 11.8 is not available, we target compute capability 8.0
|
# Therefore, if CUDA 11.8 is not available, we target compute capability 8.0
|
||||||
# instead of 8.9.
|
# instead of 8.9.
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"CUDA 11.8 or higher is required for compute capability 8.9. "
|
"CUDA 11.8 or higher is required for compute capability 8.9. "
|
||||||
"Targeting compute capability 8.0 instead.")
|
"Targeting compute capability 8.0 instead.",
|
||||||
compute_capabilities = set(cc for cc in compute_capabilities
|
stacklevel=2)
|
||||||
if not cc.startswith("8.9"))
|
compute_capabilities = set(cc for cc in compute_capabilities
|
||||||
compute_capabilities.add("8.0+PTX")
|
if not cc.startswith("8.9"))
|
||||||
if any(cc.startswith("9.0") for cc in compute_capabilities):
|
compute_capabilities.add("8.0+PTX")
|
||||||
|
if any(cc.startswith("9.0") for cc in compute_capabilities):
|
||||||
|
raise RuntimeError(
|
||||||
|
"CUDA 11.8 or higher is required for compute capability 9.0.")
|
||||||
|
|
||||||
|
# Add target compute capabilities to NVCC flags.
|
||||||
|
for capability in compute_capabilities:
|
||||||
|
num = capability[0] + capability[2]
|
||||||
|
NVCC_FLAGS += ["-gencode", f"arch=compute_{num},code=sm_{num}"]
|
||||||
|
if capability.endswith("+PTX"):
|
||||||
|
NVCC_FLAGS += [
|
||||||
|
"-gencode", f"arch=compute_{num},code=compute_{num}"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Use NVCC threads to parallelize the build.
|
||||||
|
if nvcc_cuda_version >= Version("11.2"):
|
||||||
|
nvcc_threads = int(os.getenv("NVCC_THREADS", 8))
|
||||||
|
num_threads = min(os.cpu_count(), nvcc_threads)
|
||||||
|
NVCC_FLAGS += ["--threads", str(num_threads)]
|
||||||
|
|
||||||
|
elif _is_hip():
|
||||||
|
amd_arch = get_amdgpu_offload_arch()
|
||||||
|
if amd_arch not in ROCM_SUPPORTED_ARCHS:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"CUDA 11.8 or higher is required for compute capability 9.0.")
|
f"Only the following arch is supported: {ROCM_SUPPORTED_ARCHS}"
|
||||||
|
f"amdgpu_arch_found: {amd_arch}")
|
||||||
# Add target compute capabilities to NVCC flags.
|
|
||||||
for capability in compute_capabilities:
|
|
||||||
num = capability[0] + capability[2]
|
|
||||||
NVCC_FLAGS += ["-gencode", f"arch=compute_{num},code=sm_{num}"]
|
|
||||||
if capability.endswith("+PTX"):
|
|
||||||
NVCC_FLAGS += ["-gencode", f"arch=compute_{num},code=compute_{num}"]
|
|
||||||
|
|
||||||
# Use NVCC threads to parallelize the build.
|
|
||||||
if nvcc_cuda_version >= Version("11.2"):
|
|
||||||
num_threads = min(os.cpu_count(), 8)
|
|
||||||
NVCC_FLAGS += ["--threads", str(num_threads)]
|
|
||||||
|
|
||||||
ext_modules = []
|
ext_modules = []
|
||||||
|
|
||||||
# Cache operations.
|
vllm_extension_sources = [
|
||||||
cache_extension = CUDAExtension(
|
"csrc/cache_kernels.cu",
|
||||||
name="vllm.cache_ops",
|
"csrc/attention/attention_kernels.cu",
|
||||||
sources=["csrc/cache.cpp", "csrc/cache_kernels.cu"],
|
"csrc/pos_encoding_kernels.cu",
|
||||||
extra_compile_args={
|
"csrc/activation_kernels.cu",
|
||||||
"cxx": CXX_FLAGS,
|
"csrc/layernorm_kernels.cu",
|
||||||
"nvcc": NVCC_FLAGS,
|
"csrc/quantization/squeezellm/quant_cuda_kernel.cu",
|
||||||
},
|
"csrc/cuda_utils_kernels.cu",
|
||||||
)
|
"csrc/pybind.cpp",
|
||||||
ext_modules.append(cache_extension)
|
]
|
||||||
|
|
||||||
# Attention kernels.
|
if _is_cuda():
|
||||||
attention_extension = CUDAExtension(
|
vllm_extension_sources.append("csrc/quantization/awq/gemm_kernels.cu")
|
||||||
name="vllm.attention_ops",
|
|
||||||
sources=["csrc/attention.cpp", "csrc/attention/attention_kernels.cu"],
|
|
||||||
extra_compile_args={
|
|
||||||
"cxx": CXX_FLAGS,
|
|
||||||
"nvcc": NVCC_FLAGS,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
ext_modules.append(attention_extension)
|
|
||||||
|
|
||||||
# Positional encoding kernels.
|
vllm_extension = CUDAExtension(
|
||||||
positional_encoding_extension = CUDAExtension(
|
name="vllm._C",
|
||||||
name="vllm.pos_encoding_ops",
|
sources=vllm_extension_sources,
|
||||||
sources=["csrc/pos_encoding.cpp", "csrc/pos_encoding_kernels.cu"],
|
|
||||||
extra_compile_args={
|
extra_compile_args={
|
||||||
"cxx": CXX_FLAGS,
|
"cxx": CXX_FLAGS,
|
||||||
"nvcc": NVCC_FLAGS,
|
"nvcc": NVCC_FLAGS,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
ext_modules.append(positional_encoding_extension)
|
ext_modules.append(vllm_extension)
|
||||||
|
|
||||||
# Layer normalization kernels.
|
|
||||||
layernorm_extension = CUDAExtension(
|
|
||||||
name="vllm.layernorm_ops",
|
|
||||||
sources=["csrc/layernorm.cpp", "csrc/layernorm_kernels.cu"],
|
|
||||||
extra_compile_args={
|
|
||||||
"cxx": CXX_FLAGS,
|
|
||||||
"nvcc": NVCC_FLAGS,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
ext_modules.append(layernorm_extension)
|
|
||||||
|
|
||||||
# Activation kernels.
|
|
||||||
activation_extension = CUDAExtension(
|
|
||||||
name="vllm.activation_ops",
|
|
||||||
sources=["csrc/activation.cpp", "csrc/activation_kernels.cu"],
|
|
||||||
extra_compile_args={
|
|
||||||
"cxx": CXX_FLAGS,
|
|
||||||
"nvcc": NVCC_FLAGS,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
ext_modules.append(activation_extension)
|
|
||||||
|
|
||||||
# Quantization kernels.
|
|
||||||
quantization_extension = CUDAExtension(
|
|
||||||
name="vllm.quantization_ops",
|
|
||||||
sources=[
|
|
||||||
"csrc/quantization.cpp",
|
|
||||||
"csrc/quantization/awq/gemm_kernels.cu",
|
|
||||||
"csrc/quantization/squeezellm/quant_cuda_kernel.cu",
|
|
||||||
],
|
|
||||||
extra_compile_args={
|
|
||||||
"cxx": CXX_FLAGS,
|
|
||||||
"nvcc": NVCC_FLAGS,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
ext_modules.append(quantization_extension)
|
|
||||||
|
|
||||||
# Misc. CUDA utils.
|
|
||||||
cuda_utils_extension = CUDAExtension(
|
|
||||||
name="vllm.cuda_utils",
|
|
||||||
sources=["csrc/cuda_utils.cpp", "csrc/cuda_utils_kernels.cu"],
|
|
||||||
extra_compile_args={
|
|
||||||
"cxx": CXX_FLAGS,
|
|
||||||
"nvcc": NVCC_FLAGS,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
ext_modules.append(cuda_utils_extension)
|
|
||||||
|
|
||||||
|
|
||||||
def get_path(*filepath) -> str:
|
def get_path(*filepath) -> str:
|
||||||
@@ -242,10 +256,19 @@ def find_version(filepath: str) -> str:
|
|||||||
|
|
||||||
def get_vllm_version() -> str:
|
def get_vllm_version() -> str:
|
||||||
version = find_version(get_path("vllm", "__init__.py"))
|
version = find_version(get_path("vllm", "__init__.py"))
|
||||||
cuda_version = str(nvcc_cuda_version)
|
|
||||||
if cuda_version != MAIN_CUDA_VERSION:
|
if _is_hip():
|
||||||
cuda_version_str = cuda_version.replace(".", "")[:3]
|
# Get the HIP version
|
||||||
version += f"+cu{cuda_version_str}"
|
hipcc_version = get_hipcc_rocm_version()
|
||||||
|
if hipcc_version != MAIN_CUDA_VERSION:
|
||||||
|
rocm_version_str = hipcc_version.replace(".", "")[:3]
|
||||||
|
version += f"+rocm{rocm_version_str}"
|
||||||
|
else:
|
||||||
|
cuda_version = str(nvcc_cuda_version)
|
||||||
|
if cuda_version != MAIN_CUDA_VERSION:
|
||||||
|
cuda_version_str = cuda_version.replace(".", "")[:3]
|
||||||
|
version += f"+cu{cuda_version_str}"
|
||||||
|
|
||||||
return version
|
return version
|
||||||
|
|
||||||
|
|
||||||
@@ -260,8 +283,12 @@ def read_readme() -> str:
|
|||||||
|
|
||||||
def get_requirements() -> List[str]:
|
def get_requirements() -> List[str]:
|
||||||
"""Get Python package dependencies from requirements.txt."""
|
"""Get Python package dependencies from requirements.txt."""
|
||||||
with open(get_path("requirements.txt")) as f:
|
if _is_hip():
|
||||||
requirements = f.read().strip().split("\n")
|
with open(get_path("requirements-rocm.txt")) as f:
|
||||||
|
requirements = f.read().strip().split("\n")
|
||||||
|
else:
|
||||||
|
with open(get_path("requirements.txt")) as f:
|
||||||
|
requirements = f.read().strip().split("\n")
|
||||||
return requirements
|
return requirements
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ app = vllm.entrypoints.api_server.app
|
|||||||
|
|
||||||
class AsyncLLMEngineWithStats(AsyncLLMEngine):
|
class AsyncLLMEngineWithStats(AsyncLLMEngine):
|
||||||
|
|
||||||
# pylint: disable=redefined-outer-name
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self._num_aborts = 0
|
self._num_aborts = 0
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ def _query_server(prompt: str) -> dict:
|
|||||||
def api_server():
|
def api_server():
|
||||||
script_path = Path(__file__).parent.joinpath(
|
script_path = Path(__file__).parent.joinpath(
|
||||||
"api_server_async_engine.py").absolute()
|
"api_server_async_engine.py").absolute()
|
||||||
# pylint: disable=consider-using-with
|
|
||||||
uvicorn_process = subprocess.Popen([
|
uvicorn_process = subprocess.Popen([
|
||||||
sys.executable, "-u",
|
sys.executable, "-u",
|
||||||
str(script_path), "--model", "facebook/opt-125m"
|
str(script_path), "--model", "facebook/opt-125m"
|
||||||
@@ -33,7 +32,6 @@ def api_server():
|
|||||||
uvicorn_process.terminate()
|
uvicorn_process.terminate()
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=redefined-outer-name, unused-argument
|
|
||||||
def test_api_server(api_server):
|
def test_api_server(api_server):
|
||||||
"""
|
"""
|
||||||
Run the API server and test it.
|
Run the API server and test it.
|
||||||
@@ -49,11 +47,10 @@ def test_api_server(api_server):
|
|||||||
prompts = ["Hello world"] * 1
|
prompts = ["Hello world"] * 1
|
||||||
result = None
|
result = None
|
||||||
while not result:
|
while not result:
|
||||||
# pylint: disable=bare-except
|
|
||||||
try:
|
try:
|
||||||
for result in pool.map(_query_server, prompts):
|
for _ in pool.map(_query_server, prompts):
|
||||||
break
|
break
|
||||||
except:
|
except Exception:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
# Actual tests start here
|
# Actual tests start here
|
||||||
|
|||||||
119
tests/async_engine/test_openai_server.py
Normal file
119
tests/async_engine/test_openai_server.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
from argparse import Namespace
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from vllm.entrypoints.openai.api_server import *
|
||||||
|
|
||||||
|
# Define models, templates, and their corresponding expected outputs
|
||||||
|
MODEL_TEMPLATE_GENERATON_OUTPUT = [
|
||||||
|
("facebook/opt-125m", None, True,
|
||||||
|
"Hello</s>Hi there!</s>What is the capital of</s>"),
|
||||||
|
("facebook/opt-125m", None, False,
|
||||||
|
"Hello</s>Hi there!</s>What is the capital of</s>"),
|
||||||
|
("facebook/opt-125m", "../../examples/template_chatml.jinja", True,
|
||||||
|
"""<|im_start|>user
|
||||||
|
Hello<|im_end|>
|
||||||
|
<|im_start|>assistant
|
||||||
|
Hi there!<|im_end|>
|
||||||
|
<|im_start|>user
|
||||||
|
What is the capital of<|im_end|>
|
||||||
|
<|im_start|>assistant
|
||||||
|
"""),
|
||||||
|
("facebook/opt-125m", "../../examples/template_chatml.jinja", False,
|
||||||
|
"""<|im_start|>user
|
||||||
|
Hello<|im_end|>
|
||||||
|
<|im_start|>assistant
|
||||||
|
Hi there!<|im_end|>
|
||||||
|
<|im_start|>user
|
||||||
|
What is the capital of""")
|
||||||
|
]
|
||||||
|
|
||||||
|
TEST_MESSAGES = [
|
||||||
|
{
|
||||||
|
'role': 'user',
|
||||||
|
'content': 'Hello'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'role': 'assistant',
|
||||||
|
'content': 'Hi there!'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'role': 'user',
|
||||||
|
'content': 'What is the capital of'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MockTokenizer:
|
||||||
|
chat_template = None
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_chat_template():
|
||||||
|
# Testing chatml template
|
||||||
|
template = "../../examples/template_chatml.jinja"
|
||||||
|
mock_args = Namespace(chat_template=template)
|
||||||
|
tokenizer = MockTokenizer()
|
||||||
|
|
||||||
|
# Call the function with the mocked args
|
||||||
|
load_chat_template(mock_args, tokenizer)
|
||||||
|
|
||||||
|
template_content = tokenizer.chat_template
|
||||||
|
|
||||||
|
# Test assertions
|
||||||
|
assert template_content is not None
|
||||||
|
# Hard coded value for template_chatml.jinja
|
||||||
|
assert template_content == """{% for message in messages %}{{'<|im_start|>' + message['role'] + '\\n' + message['content']}}{% if (loop.last and add_generation_prompt) or not loop.last %}{{ '<|im_end|>' + '\\n'}}{% endif %}{% endfor %}
|
||||||
|
{% if add_generation_prompt and messages[-1]['role'] != 'assistant' %}{{ '<|im_start|>assistant\\n' }}{% endif %}"""
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_load_chat_template():
|
||||||
|
# Testing chatml template
|
||||||
|
template = "../../examples/does_not_exist"
|
||||||
|
mock_args = Namespace(chat_template=template)
|
||||||
|
tokenizer = MockTokenizer()
|
||||||
|
|
||||||
|
# Call the function with the mocked args
|
||||||
|
load_chat_template(mock_args, tokenizer=tokenizer)
|
||||||
|
template_content = tokenizer.chat_template
|
||||||
|
|
||||||
|
# Test assertions
|
||||||
|
assert template_content is not None
|
||||||
|
# Hard coded value for template_chatml.jinja
|
||||||
|
assert template_content == """../../examples/does_not_exist"""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"model,template,add_generation_prompt,expected_output",
|
||||||
|
MODEL_TEMPLATE_GENERATON_OUTPUT)
|
||||||
|
async def test_get_gen_prompt(model, template, add_generation_prompt,
|
||||||
|
expected_output):
|
||||||
|
# Initialize the tokenizer
|
||||||
|
tokenizer = get_tokenizer(tokenizer_name=model)
|
||||||
|
|
||||||
|
mock_args = Namespace(chat_template=template)
|
||||||
|
load_chat_template(mock_args, tokenizer)
|
||||||
|
|
||||||
|
# Create a mock request object using keyword arguments
|
||||||
|
mock_request = ChatCompletionRequest(
|
||||||
|
model=model,
|
||||||
|
messages=TEST_MESSAGES,
|
||||||
|
add_generation_prompt=add_generation_prompt)
|
||||||
|
|
||||||
|
# Call the function and get the result
|
||||||
|
result = tokenizer.apply_chat_template(
|
||||||
|
conversation=mock_request.messages,
|
||||||
|
tokenize=False,
|
||||||
|
add_generation_prompt=mock_request.add_generation_prompt)
|
||||||
|
|
||||||
|
# Test assertion
|
||||||
|
assert result == expected_output, f"The generated prompt does not match the expected output for model {model} and template {template}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_health_endpoint():
|
||||||
|
response = client.get("/health")
|
||||||
|
assert response.status_code == 200
|
||||||
@@ -8,7 +8,6 @@ from vllm import LLM, SamplingParams
|
|||||||
from vllm.transformers_utils.tokenizer import get_tokenizer
|
from vllm.transformers_utils.tokenizer import get_tokenizer
|
||||||
|
|
||||||
_TEST_PROMPTS = [
|
_TEST_PROMPTS = [
|
||||||
# pylint: disable=line-too-long
|
|
||||||
"vLLM is a high-throughput and memory-efficient inference and serving engine for LLMs.",
|
"vLLM is a high-throughput and memory-efficient inference and serving engine for LLMs.",
|
||||||
"Briefly describe the major milestones in the development of artificial intelligence from 1950 to 2020.",
|
"Briefly describe the major milestones in the development of artificial intelligence from 1950 to 2020.",
|
||||||
"Compare and contrast artificial intelligence with human intelligence in terms of processing information.",
|
"Compare and contrast artificial intelligence with human intelligence in terms of processing information.",
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ from transformers import AutoTokenizer
|
|||||||
from vllm.transformers_utils.tokenizer import detokenize_incrementally
|
from vllm.transformers_utils.tokenizer import detokenize_incrementally
|
||||||
|
|
||||||
TRUTH = [
|
TRUTH = [
|
||||||
# pylint: disable=line-too-long
|
"Hello here, this is a simple test", # noqa: E501
|
||||||
"Hello here, this is a simple test",
|
"vLLM is a high-throughput and memory-efficient inference and serving engine for LLMs. It is designed to be used in production environments, where inference and serving", # noqa: E501
|
||||||
"vLLM is a high-throughput and memory-efficient inference and serving engine for LLMs. It is designed to be used in production environments, where inference and serving",
|
"我很感谢你的热情" # noqa: E501
|
||||||
"我很感谢你的热情"
|
|
||||||
]
|
]
|
||||||
TOKENIZERS = [
|
TOKENIZERS = [
|
||||||
"facebook/opt-125m",
|
"facebook/opt-125m",
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
import torch
|
import torch
|
||||||
import torch.nn.functional as F
|
|
||||||
from transformers.activations import get_activation
|
|
||||||
|
|
||||||
from vllm import activation_ops
|
from vllm.model_executor.layers.activation import FastGELU, NewGELU, SiluAndMul
|
||||||
|
|
||||||
DTYPES = [torch.half, torch.bfloat16, torch.float]
|
DTYPES = [torch.half, torch.bfloat16, torch.float]
|
||||||
NUM_TOKENS = [7, 83, 2048] # Arbitrary values for testing
|
NUM_TOKENS = [7, 83, 2048] # Arbitrary values for testing
|
||||||
@@ -11,11 +9,6 @@ D = [512, 4096, 5120, 13824] # Arbitrary values for testing
|
|||||||
SEEDS = [0]
|
SEEDS = [0]
|
||||||
|
|
||||||
|
|
||||||
def ref_silu_and_mul(x: torch.Tensor) -> torch.Tensor:
|
|
||||||
x1, x2 = x.chunk(chunks=2, dim=1)
|
|
||||||
return F.silu(x1) * x2
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("num_tokens", NUM_TOKENS)
|
@pytest.mark.parametrize("num_tokens", NUM_TOKENS)
|
||||||
@pytest.mark.parametrize("d", D)
|
@pytest.mark.parametrize("d", D)
|
||||||
@pytest.mark.parametrize("dtype", DTYPES)
|
@pytest.mark.parametrize("dtype", DTYPES)
|
||||||
@@ -30,9 +23,9 @@ def test_silu_and_mul(
|
|||||||
torch.random.manual_seed(seed)
|
torch.random.manual_seed(seed)
|
||||||
torch.cuda.manual_seed(seed)
|
torch.cuda.manual_seed(seed)
|
||||||
x = torch.randn(num_tokens, 2 * d, dtype=dtype, device="cuda")
|
x = torch.randn(num_tokens, 2 * d, dtype=dtype, device="cuda")
|
||||||
out = torch.empty(num_tokens, d, dtype=dtype, device="cuda")
|
layer = SiluAndMul()
|
||||||
activation_ops.silu_and_mul(out, x)
|
out = layer(x)
|
||||||
ref_out = ref_silu_and_mul(x)
|
ref_out = layer._forward(x)
|
||||||
assert torch.allclose(out, ref_out, atol=1e-5, rtol=1e-5)
|
assert torch.allclose(out, ref_out, atol=1e-5, rtol=1e-5)
|
||||||
|
|
||||||
|
|
||||||
@@ -50,9 +43,9 @@ def test_gelu_new(
|
|||||||
torch.random.manual_seed(seed)
|
torch.random.manual_seed(seed)
|
||||||
torch.cuda.manual_seed(seed)
|
torch.cuda.manual_seed(seed)
|
||||||
x = torch.randn(num_tokens, d, dtype=dtype, device="cuda")
|
x = torch.randn(num_tokens, d, dtype=dtype, device="cuda")
|
||||||
out = torch.empty(num_tokens, d, dtype=dtype, device="cuda")
|
layer = NewGELU()
|
||||||
activation_ops.gelu_new(out, x)
|
out = layer(x)
|
||||||
ref_out = get_activation("gelu_new")(x)
|
ref_out = layer._forward(x)
|
||||||
assert torch.allclose(out, ref_out, atol=1e-5, rtol=1e-5)
|
assert torch.allclose(out, ref_out, atol=1e-5, rtol=1e-5)
|
||||||
|
|
||||||
|
|
||||||
@@ -69,7 +62,7 @@ def test_gelu_fast(
|
|||||||
torch.random.manual_seed(seed)
|
torch.random.manual_seed(seed)
|
||||||
torch.cuda.manual_seed(seed)
|
torch.cuda.manual_seed(seed)
|
||||||
x = torch.randn(num_tokens, d, dtype=dtype, device="cuda")
|
x = torch.randn(num_tokens, d, dtype=dtype, device="cuda")
|
||||||
out = torch.empty(num_tokens, d, dtype=dtype, device="cuda")
|
layer = FastGELU()
|
||||||
activation_ops.gelu_fast(out, x)
|
out = layer(x)
|
||||||
ref_out = get_activation("gelu_fast")(x)
|
ref_out = layer._forward(x)
|
||||||
assert torch.allclose(out, ref_out, atol=1e-5, rtol=1e-5)
|
assert torch.allclose(out, ref_out, atol=1e-5, rtol=1e-5)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import torch
|
|||||||
from xformers import ops as xops
|
from xformers import ops as xops
|
||||||
from xformers.ops.fmha.attn_bias import BlockDiagonalCausalMask
|
from xformers.ops.fmha.attn_bias import BlockDiagonalCausalMask
|
||||||
|
|
||||||
from vllm import attention_ops
|
from vllm._C import ops
|
||||||
from vllm.utils import get_max_shared_memory_bytes
|
from vllm.utils import get_max_shared_memory_bytes
|
||||||
|
|
||||||
FLOAT32_BYTES = torch.finfo(torch.float).bits // 8
|
FLOAT32_BYTES = torch.finfo(torch.float).bits // 8
|
||||||
@@ -131,9 +131,6 @@ def test_paged_attention(
|
|||||||
|
|
||||||
assert num_query_heads % num_kv_heads == 0
|
assert num_query_heads % num_kv_heads == 0
|
||||||
num_queries_per_kv = num_query_heads // num_kv_heads
|
num_queries_per_kv = num_query_heads // num_kv_heads
|
||||||
head_mapping = torch.repeat_interleave(
|
|
||||||
torch.arange(num_kv_heads, dtype=torch.int32, device="cuda"),
|
|
||||||
num_queries_per_kv)
|
|
||||||
alibi_slopes = None
|
alibi_slopes = None
|
||||||
if use_alibi:
|
if use_alibi:
|
||||||
alibi_slopes = torch.randn(num_query_heads,
|
alibi_slopes = torch.randn(num_query_heads,
|
||||||
@@ -165,12 +162,12 @@ def test_paged_attention(
|
|||||||
# Call the paged attention kernel.
|
# Call the paged attention kernel.
|
||||||
output = torch.empty_like(query)
|
output = torch.empty_like(query)
|
||||||
if version == "v1":
|
if version == "v1":
|
||||||
attention_ops.paged_attention_v1(
|
ops.paged_attention_v1(
|
||||||
output,
|
output,
|
||||||
query,
|
query,
|
||||||
key_cache,
|
key_cache,
|
||||||
value_cache,
|
value_cache,
|
||||||
head_mapping,
|
num_kv_heads,
|
||||||
scale,
|
scale,
|
||||||
block_tables,
|
block_tables,
|
||||||
context_lens,
|
context_lens,
|
||||||
@@ -194,7 +191,7 @@ def test_paged_attention(
|
|||||||
device=output.device,
|
device=output.device,
|
||||||
)
|
)
|
||||||
max_logits = torch.empty_like(exp_sums)
|
max_logits = torch.empty_like(exp_sums)
|
||||||
attention_ops.paged_attention_v2(
|
ops.paged_attention_v2(
|
||||||
output,
|
output,
|
||||||
exp_sums,
|
exp_sums,
|
||||||
max_logits,
|
max_logits,
|
||||||
@@ -202,7 +199,7 @@ def test_paged_attention(
|
|||||||
query,
|
query,
|
||||||
key_cache,
|
key_cache,
|
||||||
value_cache,
|
value_cache,
|
||||||
head_mapping,
|
num_kv_heads,
|
||||||
scale,
|
scale,
|
||||||
block_tables,
|
block_tables,
|
||||||
context_lens,
|
context_lens,
|
||||||
@@ -211,7 +208,7 @@ def test_paged_attention(
|
|||||||
alibi_slopes,
|
alibi_slopes,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
assert False, f"Unknown version: {version}"
|
raise AssertionError(f"Unknown version: {version}")
|
||||||
|
|
||||||
# Run the reference implementation.
|
# Run the reference implementation.
|
||||||
ref_output = torch.empty_like(query)
|
ref_output = torch.empty_like(query)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import random
|
|||||||
import pytest
|
import pytest
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
from vllm import cache_ops
|
from vllm._C import cache_ops
|
||||||
|
|
||||||
DTYPES = [torch.half, torch.bfloat16, torch.float]
|
DTYPES = [torch.half, torch.bfloat16, torch.float]
|
||||||
NUM_TOKENS = [83] # Arbitrary values for testing
|
NUM_TOKENS = [83] # Arbitrary values for testing
|
||||||
|
|||||||
@@ -1,58 +1,47 @@
|
|||||||
import pytest
|
import pytest
|
||||||
import torch
|
import torch
|
||||||
import torch.nn as nn
|
|
||||||
|
|
||||||
from vllm import layernorm_ops
|
from vllm.model_executor.layers.layernorm import RMSNorm
|
||||||
|
|
||||||
DTYPES = [torch.half, torch.bfloat16, torch.float]
|
DTYPES = [torch.half, torch.bfloat16, torch.float]
|
||||||
HIDDEN_SIZES = [67, 768, 2048, 5120, 8192] # Arbitrary values for testing
|
|
||||||
NUM_TOKENS = [7, 83, 4096] # Arbitrary values for testing
|
NUM_TOKENS = [7, 83, 4096] # Arbitrary values for testing
|
||||||
|
HIDDEN_SIZES = [768, 5120, 8192] # Arbitrary values for testing
|
||||||
|
ADD_RESIDUAL = [False, True]
|
||||||
SEEDS = [0]
|
SEEDS = [0]
|
||||||
|
|
||||||
|
|
||||||
class RefRMSNorm(nn.Module):
|
|
||||||
|
|
||||||
def __init__(self, hidden_size, eps=1e-6):
|
|
||||||
super().__init__()
|
|
||||||
weight = torch.empty(hidden_size)
|
|
||||||
weight.normal_(mean=1.0, std=0.1)
|
|
||||||
self.weight = nn.Parameter(weight)
|
|
||||||
self.variance_epsilon = eps
|
|
||||||
|
|
||||||
def forward(self, hidden_states):
|
|
||||||
input_dtype = hidden_states.dtype
|
|
||||||
hidden_states = hidden_states.to(torch.float32)
|
|
||||||
variance = hidden_states.pow(2).mean(-1, keepdim=True)
|
|
||||||
hidden_states = hidden_states * torch.rsqrt(variance +
|
|
||||||
self.variance_epsilon)
|
|
||||||
return self.weight * hidden_states.to(input_dtype)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("num_tokens", NUM_TOKENS)
|
@pytest.mark.parametrize("num_tokens", NUM_TOKENS)
|
||||||
@pytest.mark.parametrize("hidden_size", HIDDEN_SIZES)
|
@pytest.mark.parametrize("hidden_size", HIDDEN_SIZES)
|
||||||
|
@pytest.mark.parametrize("add_residual", ADD_RESIDUAL)
|
||||||
@pytest.mark.parametrize("dtype", DTYPES)
|
@pytest.mark.parametrize("dtype", DTYPES)
|
||||||
@pytest.mark.parametrize("seed", SEEDS)
|
@pytest.mark.parametrize("seed", SEEDS)
|
||||||
@torch.inference_mode()
|
@torch.inference_mode()
|
||||||
def test_rms_norm(
|
def test_rms_norm(
|
||||||
num_tokens: int,
|
num_tokens: int,
|
||||||
hidden_size: int,
|
hidden_size: int,
|
||||||
|
add_residual: bool,
|
||||||
dtype: torch.dtype,
|
dtype: torch.dtype,
|
||||||
seed: int,
|
seed: int,
|
||||||
) -> None:
|
) -> None:
|
||||||
torch.random.manual_seed(seed)
|
torch.random.manual_seed(seed)
|
||||||
torch.cuda.manual_seed(seed)
|
torch.cuda.manual_seed(seed)
|
||||||
|
|
||||||
scale = float(hidden_size**-0.5)
|
layer = RMSNorm(hidden_size).to(dtype).cuda()
|
||||||
x = torch.empty(num_tokens, hidden_size, dtype=dtype, device="cuda")
|
layer.weight.data.normal_(mean=1.0, std=0.1)
|
||||||
x.uniform_(-scale, scale)
|
scale = 1 / (2 * hidden_size)
|
||||||
ref = RefRMSNorm(hidden_size).to(dtype).cuda()
|
x = torch.randn(num_tokens, hidden_size, dtype=dtype, device="cuda")
|
||||||
|
x *= scale
|
||||||
|
residual = torch.randn_like(x) * scale if add_residual else None
|
||||||
|
|
||||||
out = torch.empty_like(x)
|
# NOTE(woosuk): The reference implementation should be executed first
|
||||||
layernorm_ops.rms_norm(
|
# because the custom kernel is in-place.
|
||||||
out,
|
ref_out = layer._forward(x, residual)
|
||||||
x,
|
out = layer(x, residual)
|
||||||
ref.weight.data,
|
# NOTE(woosuk): LayerNorm operators (including RMS) typically have larger
|
||||||
ref.variance_epsilon,
|
# numerical errors than other operators because they involve reductions.
|
||||||
)
|
# Therefore, we use a larger tolerance.
|
||||||
ref_out = ref(x)
|
if add_residual:
|
||||||
assert torch.allclose(out, ref_out, atol=1e-2, rtol=1e-5)
|
assert torch.allclose(out[0], ref_out[0], atol=1e-2, rtol=1e-2)
|
||||||
|
assert torch.allclose(out[1], ref_out[1], atol=1e-2, rtol=1e-2)
|
||||||
|
else:
|
||||||
|
assert torch.allclose(out, ref_out, atol=1e-2, rtol=1e-2)
|
||||||
|
|||||||
@@ -1,105 +1,23 @@
|
|||||||
from typing import Optional, Tuple
|
from typing import Optional
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import torch
|
import torch
|
||||||
import torch.nn as nn
|
|
||||||
import torch.nn.functional as F
|
|
||||||
|
|
||||||
from vllm import pos_encoding_ops
|
from vllm.model_executor.layers.rotary_embedding import get_rope
|
||||||
|
|
||||||
IS_NEOX_STYLE = [True, False]
|
IS_NEOX_STYLE = [True, False]
|
||||||
DTYPES = [torch.half, torch.bfloat16, torch.float]
|
DTYPES = [torch.half, torch.bfloat16, torch.float]
|
||||||
HEAD_SIZES = [64, 80, 96, 112, 128, 256]
|
HEAD_SIZES = [64, 80, 96, 112, 128, 256]
|
||||||
ROTARY_DIMS = [None, 32] # None means rotary dim == head size
|
ROTARY_DIMS = [None, 32] # None means rotary dim == head size
|
||||||
NUM_HEADS = [7, 12, 40, 52] # Arbitrary values for testing
|
NUM_HEADS = [7, 17] # Arbitrary values for testing
|
||||||
NUM_TOKENS = [11, 83, 2048] # Arbitrary values for testing
|
BATCH_SIZES = [1, 5] # Arbitrary values for testing
|
||||||
|
SEQ_LENS = [11, 8192] # Arbitrary values for testing
|
||||||
SEEDS = [0]
|
SEEDS = [0]
|
||||||
|
|
||||||
|
|
||||||
def rotate_neox(x: torch.Tensor) -> torch.Tensor:
|
|
||||||
x1 = x[..., :x.shape[-1] // 2]
|
|
||||||
x2 = x[..., x.shape[-1] // 2:]
|
|
||||||
return torch.cat((-x2, x1), dim=-1)
|
|
||||||
|
|
||||||
|
|
||||||
def rotate_gptj(x: torch.Tensor) -> torch.Tensor:
|
|
||||||
x1 = x[..., ::2]
|
|
||||||
x2 = x[..., 1::2]
|
|
||||||
x = torch.stack((-x2, x1), dim=-1)
|
|
||||||
return x.flatten(-2)
|
|
||||||
|
|
||||||
|
|
||||||
def apply_rope(
|
|
||||||
q: torch.Tensor,
|
|
||||||
k: torch.Tensor,
|
|
||||||
cos: torch.Tensor,
|
|
||||||
sin: torch.Tensor,
|
|
||||||
is_neox_style: bool,
|
|
||||||
) -> Tuple[torch.Tensor, torch.Tensor]:
|
|
||||||
rotate_fn = rotate_neox if is_neox_style else rotate_gptj
|
|
||||||
q_embed = (q * cos) + (rotate_fn(q) * sin)
|
|
||||||
k_embed = (k * cos) + (rotate_fn(k) * sin)
|
|
||||||
return q_embed, k_embed
|
|
||||||
|
|
||||||
|
|
||||||
class RefRotaryEmbedding(nn.Module):
|
|
||||||
"""Reference implementation of rotary embedding."""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
dim: int,
|
|
||||||
is_neox_style: bool,
|
|
||||||
max_position_embeddings: int = 8192,
|
|
||||||
base: int = 10000,
|
|
||||||
) -> None:
|
|
||||||
super().__init__()
|
|
||||||
self.rotary_dim = dim
|
|
||||||
self.is_neox_style = is_neox_style
|
|
||||||
self.max_position_embeddings = max_position_embeddings
|
|
||||||
|
|
||||||
# Create cos and sin embeddings.
|
|
||||||
inv_freq = 1.0 / (base**(torch.arange(0, dim, 2) / dim))
|
|
||||||
t = torch.arange(max_position_embeddings).float()
|
|
||||||
freqs = torch.einsum("i,j->ij", t, inv_freq.float())
|
|
||||||
if is_neox_style:
|
|
||||||
emb = torch.cat((freqs, freqs), dim=-1)
|
|
||||||
else:
|
|
||||||
emb = torch.repeat_interleave(freqs, 2, -1)
|
|
||||||
cos = emb.cos().to(dtype=inv_freq.dtype)
|
|
||||||
sin = emb.sin().to(dtype=inv_freq.dtype)
|
|
||||||
self.register_buffer("cos_cached", cos, persistent=False)
|
|
||||||
self.register_buffer("sin_cached", sin, persistent=False)
|
|
||||||
|
|
||||||
def forward(
|
|
||||||
self,
|
|
||||||
positions: torch.Tensor, # [num_tokens]
|
|
||||||
query: torch.Tensor, # [num_tokens, num_heads, head_size]
|
|
||||||
key: torch.Tensor, # [num_tokens, num_heads, head_size]
|
|
||||||
) -> Tuple[torch.Tensor, torch.Tensor]:
|
|
||||||
query_rot = query[..., :self.rotary_dim]
|
|
||||||
query_pass = query[..., self.rotary_dim:]
|
|
||||||
key_rot = key[..., :self.rotary_dim]
|
|
||||||
key_pass = key[..., self.rotary_dim:]
|
|
||||||
|
|
||||||
query_rot = query_rot.transpose(0, 1)
|
|
||||||
key_rot = key_rot.transpose(0, 1)
|
|
||||||
cos = F.embedding(positions, self.cos_cached)
|
|
||||||
sin = F.embedding(positions, self.sin_cached)
|
|
||||||
|
|
||||||
query_rot, key_rot = apply_rope(query_rot, key_rot, cos, sin,
|
|
||||||
self.is_neox_style)
|
|
||||||
query_rot = query_rot.transpose(0, 1).contiguous()
|
|
||||||
key_rot = key_rot.transpose(0, 1).contiguous()
|
|
||||||
|
|
||||||
query = torch.cat((query_rot, query_pass), dim=-1)
|
|
||||||
key = torch.cat((key_rot, key_pass), dim=-1)
|
|
||||||
|
|
||||||
# Output query/key shape: [num_tokens, num_tokens, head_size]
|
|
||||||
return query, key
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("is_neox_style", IS_NEOX_STYLE)
|
@pytest.mark.parametrize("is_neox_style", IS_NEOX_STYLE)
|
||||||
@pytest.mark.parametrize("num_tokens", NUM_TOKENS)
|
@pytest.mark.parametrize("batch_size", BATCH_SIZES)
|
||||||
|
@pytest.mark.parametrize("seq_len", SEQ_LENS)
|
||||||
@pytest.mark.parametrize("num_heads", NUM_HEADS)
|
@pytest.mark.parametrize("num_heads", NUM_HEADS)
|
||||||
@pytest.mark.parametrize("head_size", HEAD_SIZES)
|
@pytest.mark.parametrize("head_size", HEAD_SIZES)
|
||||||
@pytest.mark.parametrize("rotary_dim", ROTARY_DIMS)
|
@pytest.mark.parametrize("rotary_dim", ROTARY_DIMS)
|
||||||
@@ -108,7 +26,8 @@ class RefRotaryEmbedding(nn.Module):
|
|||||||
@torch.inference_mode()
|
@torch.inference_mode()
|
||||||
def test_rotary_embedding(
|
def test_rotary_embedding(
|
||||||
is_neox_style: bool,
|
is_neox_style: bool,
|
||||||
num_tokens: int,
|
batch_size: int,
|
||||||
|
seq_len: int,
|
||||||
num_heads: int,
|
num_heads: int,
|
||||||
head_size: int,
|
head_size: int,
|
||||||
rotary_dim: Optional[int],
|
rotary_dim: Optional[int],
|
||||||
@@ -122,53 +41,25 @@ def test_rotary_embedding(
|
|||||||
torch.random.manual_seed(seed)
|
torch.random.manual_seed(seed)
|
||||||
torch.cuda.manual_seed(seed)
|
torch.cuda.manual_seed(seed)
|
||||||
|
|
||||||
positions = torch.randint(0, max_position, (num_tokens, ), device="cuda")
|
if rotary_dim is None:
|
||||||
query = torch.randn(num_tokens,
|
rotary_dim = head_size
|
||||||
|
rope = get_rope(head_size, rotary_dim, max_position, base, is_neox_style)
|
||||||
|
rope = rope.to(dtype).cuda()
|
||||||
|
|
||||||
|
positions = torch.randint(0,
|
||||||
|
max_position, (batch_size, seq_len),
|
||||||
|
device="cuda")
|
||||||
|
query = torch.randn(batch_size,
|
||||||
|
seq_len,
|
||||||
num_heads * head_size,
|
num_heads * head_size,
|
||||||
dtype=dtype,
|
dtype=dtype,
|
||||||
device="cuda")
|
device="cuda")
|
||||||
key = torch.randn(num_tokens,
|
key = torch.randn_like(query)
|
||||||
num_heads * head_size,
|
|
||||||
dtype=dtype,
|
|
||||||
device="cuda")
|
|
||||||
|
|
||||||
# Create the rotary embedding.
|
|
||||||
inv_freq = 1.0 / (base**(
|
|
||||||
torch.arange(0, rotary_dim, 2, dtype=torch.float) / rotary_dim))
|
|
||||||
t = torch.arange(max_position).float()
|
|
||||||
freqs = torch.einsum("i,j -> ij", t, inv_freq)
|
|
||||||
cos = freqs.cos()
|
|
||||||
sin = freqs.sin()
|
|
||||||
cos_sin_cache = torch.cat((cos, sin), dim=-1)
|
|
||||||
cos_sin_cache = cos_sin_cache.to(dtype=dtype, device="cuda")
|
|
||||||
|
|
||||||
# Run the kernel. The kernel is in-place, so we need to clone the inputs.
|
|
||||||
out_query = query.clone()
|
|
||||||
out_key = key.clone()
|
|
||||||
pos_encoding_ops.rotary_embedding(
|
|
||||||
positions,
|
|
||||||
out_query,
|
|
||||||
out_key,
|
|
||||||
head_size,
|
|
||||||
cos_sin_cache,
|
|
||||||
is_neox_style,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Run the reference implementation.
|
|
||||||
ref_rotary_embedding = RefRotaryEmbedding(
|
|
||||||
dim=rotary_dim,
|
|
||||||
is_neox_style=is_neox_style,
|
|
||||||
max_position_embeddings=max_position,
|
|
||||||
base=base,
|
|
||||||
).to(dtype=dtype, device="cuda")
|
|
||||||
ref_query, ref_key = ref_rotary_embedding(
|
|
||||||
positions,
|
|
||||||
query.view(num_tokens, num_heads, head_size),
|
|
||||||
key.view(num_tokens, num_heads, head_size),
|
|
||||||
)
|
|
||||||
ref_query = ref_query.view(num_tokens, num_heads * head_size)
|
|
||||||
ref_key = ref_key.view(num_tokens, num_heads * head_size)
|
|
||||||
|
|
||||||
|
# NOTE(woosuk): The reference implementation should be executed first
|
||||||
|
# because the custom kernel is in-place.
|
||||||
|
ref_query, ref_key = rope._forward(positions, query, key)
|
||||||
|
out_query, out_key = rope.forward(positions, query, key)
|
||||||
# Compare the results.
|
# Compare the results.
|
||||||
assert torch.allclose(out_query, ref_query, atol=1e-5, rtol=1e-5)
|
assert torch.allclose(out_query, ref_query, atol=1e-5, rtol=1e-5)
|
||||||
assert torch.allclose(out_key, ref_key, atol=1e-5, rtol=1e-5)
|
assert torch.allclose(out_key, ref_key, atol=1e-5, rtol=1e-5)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# pylint: disable=protected-access
|
|
||||||
import random
|
import random
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
@@ -9,7 +8,7 @@ import torch
|
|||||||
from vllm.model_executor.layers.sampler import Sampler
|
from vllm.model_executor.layers.sampler import Sampler
|
||||||
from vllm.model_executor.utils import set_random_seed
|
from vllm.model_executor.utils import set_random_seed
|
||||||
from vllm.sequence import SamplingParams, SequenceData, SequenceGroupMetadata
|
from vllm.sequence import SamplingParams, SequenceData, SequenceGroupMetadata
|
||||||
from vllm.worker.worker import Worker
|
from vllm.worker.model_runner import ModelRunner
|
||||||
|
|
||||||
|
|
||||||
class MockLogitsSampler(Sampler):
|
class MockLogitsSampler(Sampler):
|
||||||
@@ -20,15 +19,15 @@ class MockLogitsSampler(Sampler):
|
|||||||
|
|
||||||
def forward(self, *args, **kwargs):
|
def forward(self, *args, **kwargs):
|
||||||
with patch("vllm.model_executor.layers.sampler._prune_hidden_states",
|
with patch("vllm.model_executor.layers.sampler._prune_hidden_states",
|
||||||
lambda x, y: x):
|
lambda x, y: x), patch(
|
||||||
with patch("vllm.model_executor.layers.sampler._get_logits",
|
"vllm.model_executor.layers.sampler._get_logits",
|
||||||
lambda *args, **kwargs: self.fake_logits):
|
lambda *args, **kwargs: self.fake_logits):
|
||||||
return super().forward(*args, **kwargs)
|
return super().forward(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def _prepare_test(
|
def _prepare_test(
|
||||||
batch_size: int
|
batch_size: int
|
||||||
) -> Tuple[torch.Tensor, torch.Tensor, MockLogitsSampler, Worker]:
|
) -> Tuple[torch.Tensor, torch.Tensor, MockLogitsSampler, ModelRunner]:
|
||||||
vocab_size = 32000
|
vocab_size = 32000
|
||||||
input_tensor = torch.rand((batch_size, 1024),
|
input_tensor = torch.rand((batch_size, 1024),
|
||||||
device="cuda",
|
device="cuda",
|
||||||
@@ -38,9 +37,8 @@ def _prepare_test(
|
|||||||
device=input_tensor.device,
|
device=input_tensor.device,
|
||||||
dtype=input_tensor.dtype)
|
dtype=input_tensor.dtype)
|
||||||
sampler = MockLogitsSampler(32000, fake_logits)
|
sampler = MockLogitsSampler(32000, fake_logits)
|
||||||
worker = Worker(None, None, None)
|
model_runner = ModelRunner(None, None, None)
|
||||||
worker.block_size = 16
|
return input_tensor, fake_logits, sampler, model_runner
|
||||||
return input_tensor, fake_logits, sampler, worker
|
|
||||||
|
|
||||||
|
|
||||||
RANDOM_SEEDS = list(range(128))
|
RANDOM_SEEDS = list(range(128))
|
||||||
@@ -50,9 +48,11 @@ RANDOM_SEEDS = list(range(128))
|
|||||||
def test_sampler_all_greedy(seed: int):
|
def test_sampler_all_greedy(seed: int):
|
||||||
set_random_seed(seed)
|
set_random_seed(seed)
|
||||||
batch_size = random.randint(1, 256)
|
batch_size = random.randint(1, 256)
|
||||||
input_tensor, fake_logits, sampler, worker = _prepare_test(batch_size)
|
input_tensor, fake_logits, sampler, model_runner = _prepare_test(
|
||||||
|
batch_size)
|
||||||
|
|
||||||
seq_group_metadata_list = []
|
seq_group_metadata_list = []
|
||||||
|
prompt_lens = []
|
||||||
for i in range(batch_size):
|
for i in range(batch_size):
|
||||||
seq_group_metadata_list.append(
|
seq_group_metadata_list.append(
|
||||||
SequenceGroupMetadata(
|
SequenceGroupMetadata(
|
||||||
@@ -62,11 +62,13 @@ def test_sampler_all_greedy(seed: int):
|
|||||||
sampling_params=SamplingParams(temperature=0, ),
|
sampling_params=SamplingParams(temperature=0, ),
|
||||||
block_tables={0: [1]},
|
block_tables={0: [1]},
|
||||||
))
|
))
|
||||||
|
prompt_lens.append(seq_group_metadata_list[-1].seq_data[0].get_len())
|
||||||
|
|
||||||
_, _, input_metadata = worker._prepare_inputs(seq_group_metadata_list)
|
sampling_metadata = model_runner._prepare_sample(seq_group_metadata_list,
|
||||||
|
prompt_lens)
|
||||||
sampler_output = sampler(embedding=None,
|
sampler_output = sampler(embedding=None,
|
||||||
hidden_states=input_tensor,
|
hidden_states=input_tensor,
|
||||||
input_metadata=input_metadata)
|
sampling_metadata=sampling_metadata)
|
||||||
expected = torch.argmax(fake_logits, dim=-1)
|
expected = torch.argmax(fake_logits, dim=-1)
|
||||||
for i, sequence_output in enumerate(sampler_output):
|
for i, sequence_output in enumerate(sampler_output):
|
||||||
for nth_output in sequence_output.samples:
|
for nth_output in sequence_output.samples:
|
||||||
@@ -77,12 +79,14 @@ def test_sampler_all_greedy(seed: int):
|
|||||||
def test_sampler_all_random(seed: int):
|
def test_sampler_all_random(seed: int):
|
||||||
set_random_seed(seed)
|
set_random_seed(seed)
|
||||||
batch_size = random.randint(1, 256)
|
batch_size = random.randint(1, 256)
|
||||||
input_tensor, fake_logits, sampler, worker = _prepare_test(batch_size)
|
input_tensor, fake_logits, sampler, model_runner = _prepare_test(
|
||||||
|
batch_size)
|
||||||
|
|
||||||
for i in range(batch_size):
|
for i in range(batch_size):
|
||||||
fake_logits[i, i] = 1e2
|
fake_logits[i, i] = 1e2
|
||||||
|
|
||||||
seq_group_metadata_list = []
|
seq_group_metadata_list = []
|
||||||
|
prompt_lens = []
|
||||||
for i in range(batch_size):
|
for i in range(batch_size):
|
||||||
seq_group_metadata_list.append(
|
seq_group_metadata_list.append(
|
||||||
SequenceGroupMetadata(
|
SequenceGroupMetadata(
|
||||||
@@ -95,11 +99,13 @@ def test_sampler_all_random(seed: int):
|
|||||||
),
|
),
|
||||||
block_tables={0: [1]},
|
block_tables={0: [1]},
|
||||||
))
|
))
|
||||||
|
prompt_lens.append(seq_group_metadata_list[-1].seq_data[0].get_len())
|
||||||
|
|
||||||
_, _, input_metadata = worker._prepare_inputs(seq_group_metadata_list)
|
sampling_metadata = model_runner._prepare_sample(seq_group_metadata_list,
|
||||||
|
prompt_lens)
|
||||||
sampler_output = sampler(embedding=None,
|
sampler_output = sampler(embedding=None,
|
||||||
hidden_states=input_tensor,
|
hidden_states=input_tensor,
|
||||||
input_metadata=input_metadata)
|
sampling_metadata=sampling_metadata)
|
||||||
for i, sequence_output in enumerate(sampler_output):
|
for i, sequence_output in enumerate(sampler_output):
|
||||||
for nth_output in sequence_output.samples:
|
for nth_output in sequence_output.samples:
|
||||||
assert nth_output.output_token == i
|
assert nth_output.output_token == i
|
||||||
@@ -109,9 +115,10 @@ def test_sampler_all_random(seed: int):
|
|||||||
def test_sampler_all_beam(seed: int):
|
def test_sampler_all_beam(seed: int):
|
||||||
set_random_seed(seed)
|
set_random_seed(seed)
|
||||||
batch_size = random.randint(1, 256)
|
batch_size = random.randint(1, 256)
|
||||||
input_tensor, _, sampler, worker = _prepare_test(batch_size)
|
input_tensor, _, sampler, model_runner = _prepare_test(batch_size)
|
||||||
|
|
||||||
seq_group_metadata_list = []
|
seq_group_metadata_list = []
|
||||||
|
prompt_lens = []
|
||||||
for i in range(batch_size):
|
for i in range(batch_size):
|
||||||
seq_group_metadata_list.append(
|
seq_group_metadata_list.append(
|
||||||
SequenceGroupMetadata(
|
SequenceGroupMetadata(
|
||||||
@@ -125,11 +132,13 @@ def test_sampler_all_beam(seed: int):
|
|||||||
),
|
),
|
||||||
block_tables={0: [1]},
|
block_tables={0: [1]},
|
||||||
))
|
))
|
||||||
|
prompt_lens.append(seq_group_metadata_list[-1].seq_data[0].get_len())
|
||||||
|
|
||||||
_, _, input_metadata = worker._prepare_inputs(seq_group_metadata_list)
|
sampling_metadata = model_runner._prepare_sample(seq_group_metadata_list,
|
||||||
|
prompt_lens)
|
||||||
sampler(embedding=None,
|
sampler(embedding=None,
|
||||||
hidden_states=input_tensor,
|
hidden_states=input_tensor,
|
||||||
input_metadata=input_metadata)
|
sampling_metadata=sampling_metadata)
|
||||||
# no assertion here as I am not sure how to determine whether
|
# no assertion here as I am not sure how to determine whether
|
||||||
# the outputs are expected - in other words, this just tests
|
# the outputs are expected - in other words, this just tests
|
||||||
# whether there are no exceptions in the sampler
|
# whether there are no exceptions in the sampler
|
||||||
@@ -140,10 +149,12 @@ def test_sampler_all_beam(seed: int):
|
|||||||
def test_sampler_mixed(seed: int):
|
def test_sampler_mixed(seed: int):
|
||||||
set_random_seed(seed)
|
set_random_seed(seed)
|
||||||
batch_size = random.randint(1, 256)
|
batch_size = random.randint(1, 256)
|
||||||
input_tensor, fake_logits, sampler, worker = _prepare_test(batch_size)
|
input_tensor, fake_logits, sampler, model_runner = _prepare_test(
|
||||||
|
batch_size)
|
||||||
|
|
||||||
seq_group_metadata_list = []
|
seq_group_metadata_list = []
|
||||||
expected_tokens = []
|
expected_tokens = []
|
||||||
|
prompt_lens = []
|
||||||
for i in range(batch_size):
|
for i in range(batch_size):
|
||||||
n = 1
|
n = 1
|
||||||
sampling_type = random.randint(0, 2)
|
sampling_type = random.randint(0, 2)
|
||||||
@@ -173,11 +184,13 @@ def test_sampler_mixed(seed: int):
|
|||||||
sampling_params=sampling_params,
|
sampling_params=sampling_params,
|
||||||
block_tables={0: [1]},
|
block_tables={0: [1]},
|
||||||
))
|
))
|
||||||
|
prompt_lens.append(seq_group_metadata_list[-1].seq_data[0].get_len())
|
||||||
|
|
||||||
_, _, input_metadata = worker._prepare_inputs(seq_group_metadata_list)
|
sampling_metadata = model_runner._prepare_sample(seq_group_metadata_list,
|
||||||
|
prompt_lens)
|
||||||
sampler_output = sampler(embedding=None,
|
sampler_output = sampler(embedding=None,
|
||||||
hidden_states=input_tensor,
|
hidden_states=input_tensor,
|
||||||
input_metadata=input_metadata)
|
sampling_metadata=sampling_metadata)
|
||||||
for i, sequence_output in enumerate(sampler_output):
|
for i, sequence_output in enumerate(sampler_output):
|
||||||
if seq_group_metadata_list[i].sampling_params.use_beam_search:
|
if seq_group_metadata_list[i].sampling_params.use_beam_search:
|
||||||
continue
|
continue
|
||||||
@@ -189,7 +202,7 @@ def test_sampler_mixed(seed: int):
|
|||||||
def test_sampler_logits_processors(seed: int):
|
def test_sampler_logits_processors(seed: int):
|
||||||
set_random_seed(seed)
|
set_random_seed(seed)
|
||||||
batch_size = random.randint(1, 256)
|
batch_size = random.randint(1, 256)
|
||||||
input_tensor, _, sampler, worker = _prepare_test(batch_size)
|
input_tensor, _, sampler, model_runner = _prepare_test(batch_size)
|
||||||
|
|
||||||
# This sample logits processor gives infinite score to the i-th token,
|
# This sample logits processor gives infinite score to the i-th token,
|
||||||
# where i is the length of the input sequence.
|
# where i is the length of the input sequence.
|
||||||
@@ -199,6 +212,7 @@ def test_sampler_logits_processors(seed: int):
|
|||||||
return logits
|
return logits
|
||||||
|
|
||||||
seq_group_metadata_list = []
|
seq_group_metadata_list = []
|
||||||
|
prompt_lens = []
|
||||||
for i in range(batch_size):
|
for i in range(batch_size):
|
||||||
seq_group_metadata_list.append(
|
seq_group_metadata_list.append(
|
||||||
SequenceGroupMetadata(
|
SequenceGroupMetadata(
|
||||||
@@ -209,11 +223,13 @@ def test_sampler_logits_processors(seed: int):
|
|||||||
logits_processors=[pick_ith]),
|
logits_processors=[pick_ith]),
|
||||||
block_tables={0: [1]},
|
block_tables={0: [1]},
|
||||||
))
|
))
|
||||||
|
prompt_lens.append(seq_group_metadata_list[-1].seq_data[0].get_len())
|
||||||
|
|
||||||
_, _, input_metadata = worker._prepare_inputs(seq_group_metadata_list)
|
sampling_metadata = model_runner._prepare_sample(seq_group_metadata_list,
|
||||||
|
prompt_lens)
|
||||||
sampler_output = sampler(embedding=None,
|
sampler_output = sampler(embedding=None,
|
||||||
hidden_states=input_tensor,
|
hidden_states=input_tensor,
|
||||||
input_metadata=input_metadata)
|
sampling_metadata=sampling_metadata)
|
||||||
for i, sequence_output in enumerate(sampler_output):
|
for _, sequence_output in enumerate(sampler_output):
|
||||||
for idx, nth_output in enumerate(sequence_output.samples):
|
for idx, nth_output in enumerate(sequence_output.samples):
|
||||||
assert nth_output.output_token == idx
|
assert nth_output.output_token == idx
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
# pylint: disable=protected-access
|
|
||||||
import random
|
import random
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
from vllm.sequence import SamplingParams, SequenceData, SequenceGroupMetadata
|
from vllm.sequence import SamplingParams, SequenceData, SequenceGroupMetadata
|
||||||
from vllm.worker.worker import Worker
|
from vllm.worker.model_runner import ModelRunner
|
||||||
|
|
||||||
|
|
||||||
def test_worker_prepare_inputs_for_prompt():
|
def test_prepare_prompt():
|
||||||
worker = Worker(None, None, None)
|
model_runner = ModelRunner(None, None, None)
|
||||||
worker.block_size = 16
|
model_runner.set_block_size(16)
|
||||||
|
|
||||||
batch_size = random.randint(1, 256)
|
batch_size = random.randint(1, 256)
|
||||||
prompt_lens = []
|
prompt_lens = []
|
||||||
seq_group_metadata_list = []
|
seq_group_metadata_list = []
|
||||||
for i in range(batch_size):
|
for i in range(batch_size):
|
||||||
# make sure all tokens fit into one block
|
# make sure all tokens fit into one block
|
||||||
prompt_len = i % (worker.block_size - 1) + 1
|
prompt_len = i % (model_runner.block_size - 1) + 1
|
||||||
prompt_lens.append(prompt_len)
|
prompt_lens.append(prompt_len)
|
||||||
seq_data = list(range(prompt_len))
|
seq_data = list(range(prompt_len))
|
||||||
seq_group_metadata_list.append(
|
seq_group_metadata_list.append(
|
||||||
@@ -25,6 +25,7 @@ def test_worker_prepare_inputs_for_prompt():
|
|||||||
sampling_params=SamplingParams(temperature=0),
|
sampling_params=SamplingParams(temperature=0),
|
||||||
block_tables={0: [1]},
|
block_tables={0: [1]},
|
||||||
))
|
))
|
||||||
|
|
||||||
expected_selected_token_indices = []
|
expected_selected_token_indices = []
|
||||||
selected_token_start_idx = 0
|
selected_token_start_idx = 0
|
||||||
max_seq_len = max(prompt_lens)
|
max_seq_len = max(prompt_lens)
|
||||||
@@ -32,12 +33,15 @@ def test_worker_prepare_inputs_for_prompt():
|
|||||||
expected_selected_token_indices.append(selected_token_start_idx +
|
expected_selected_token_indices.append(selected_token_start_idx +
|
||||||
prompt_len - 1)
|
prompt_len - 1)
|
||||||
selected_token_start_idx += max_seq_len
|
selected_token_start_idx += max_seq_len
|
||||||
input_tokens, input_positions, input_metadata = worker._prepare_inputs(
|
input_tokens, input_positions, _ = model_runner._prepare_prompt(
|
||||||
seq_group_metadata_list)
|
seq_group_metadata_list)
|
||||||
assert input_tokens.shape == input_positions.shape == (batch_size,
|
sampling_metadata = model_runner._prepare_sample(seq_group_metadata_list,
|
||||||
max_seq_len)
|
prompt_lens)
|
||||||
|
assert input_tokens.shape == (batch_size, max_seq_len)
|
||||||
|
assert input_positions.shape == (batch_size, max_seq_len)
|
||||||
torch.testing.assert_close(input_tokens, input_positions)
|
torch.testing.assert_close(input_tokens, input_positions)
|
||||||
actual = input_metadata.selected_token_indices
|
|
||||||
|
actual = sampling_metadata.selected_token_indices
|
||||||
expected = torch.tensor(expected_selected_token_indices,
|
expected = torch.tensor(expected_selected_token_indices,
|
||||||
device=actual.device,
|
device=actual.device,
|
||||||
dtype=actual.dtype)
|
dtype=actual.dtype)
|
||||||
@@ -8,7 +8,7 @@ from vllm.entrypoints.llm import LLM
|
|||||||
from vllm.outputs import CompletionOutput, RequestOutput
|
from vllm.outputs import CompletionOutput, RequestOutput
|
||||||
from vllm.sampling_params import SamplingParams
|
from vllm.sampling_params import SamplingParams
|
||||||
|
|
||||||
__version__ = "0.2.2"
|
__version__ = "0.2.4"
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"LLM",
|
"LLM",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from transformers import PretrainedConfig
|
|||||||
|
|
||||||
from vllm.logger import init_logger
|
from vllm.logger import init_logger
|
||||||
from vllm.transformers_utils.config import get_config
|
from vllm.transformers_utils.config import get_config
|
||||||
from vllm.utils import get_cpu_memory
|
from vllm.utils import get_cpu_memory, is_hip
|
||||||
|
|
||||||
logger = init_logger(__name__)
|
logger = init_logger(__name__)
|
||||||
|
|
||||||
@@ -98,12 +98,37 @@ class ModelConfig:
|
|||||||
|
|
||||||
def _verify_load_format(self) -> None:
|
def _verify_load_format(self) -> None:
|
||||||
load_format = self.load_format.lower()
|
load_format = self.load_format.lower()
|
||||||
if load_format not in [
|
supported_load_format = [
|
||||||
"auto", "pt", "safetensors", "npcache", "dummy"
|
"auto", "pt", "safetensors", "npcache", "dummy"
|
||||||
]:
|
]
|
||||||
|
rocm_not_supported_load_format = ["safetensors"]
|
||||||
|
if load_format not in supported_load_format:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Unknown load format: {self.load_format}. Must be one of "
|
f"Unknown load format: {self.load_format}. Must be one of "
|
||||||
"'auto', 'pt', 'safetensors', 'npcache', or 'dummy'.")
|
"'auto', 'pt', 'safetensors', 'npcache', or 'dummy'.")
|
||||||
|
if is_hip():
|
||||||
|
if load_format in ["safetensors"]:
|
||||||
|
rocm_supported_load_format = [
|
||||||
|
f for f in supported_load_format
|
||||||
|
if (f not in rocm_not_supported_load_format)
|
||||||
|
]
|
||||||
|
raise ValueError(
|
||||||
|
f"load format \'{load_format}\' is not supported in ROCm. "
|
||||||
|
f"Supported load format are "
|
||||||
|
f"{rocm_supported_load_format}")
|
||||||
|
# Force ROCm to load from pt weights if nothing specific is set
|
||||||
|
if load_format == "auto":
|
||||||
|
load_format = "pt"
|
||||||
|
|
||||||
|
# FIXME(woosuk): This is a temporary hack. Support safetensor weights.
|
||||||
|
architectures = getattr(self.hf_config, "architectures", [])
|
||||||
|
if "MixtralForCausalLM" in architectures and load_format != "pt":
|
||||||
|
logger.info(
|
||||||
|
"Currently, only 'pt' format is supported for Mixtral. "
|
||||||
|
"Changing the format to 'pt'. This may re-download the "
|
||||||
|
"weights if you have downloaded the safetensor weights.")
|
||||||
|
load_format = "pt"
|
||||||
|
|
||||||
self.load_format = load_format
|
self.load_format = load_format
|
||||||
|
|
||||||
def _verify_tokenizer_mode(self) -> None:
|
def _verify_tokenizer_mode(self) -> None:
|
||||||
@@ -116,6 +141,7 @@ class ModelConfig:
|
|||||||
|
|
||||||
def _verify_quantization(self) -> None:
|
def _verify_quantization(self) -> None:
|
||||||
supported_quantization = ["awq", "squeezellm"]
|
supported_quantization = ["awq", "squeezellm"]
|
||||||
|
rocm_not_supported_quantization = ["awq"]
|
||||||
if self.quantization is not None:
|
if self.quantization is not None:
|
||||||
self.quantization = self.quantization.lower()
|
self.quantization = self.quantization.lower()
|
||||||
|
|
||||||
@@ -137,6 +163,11 @@ class ModelConfig:
|
|||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Unknown quantization method: {self.quantization}. Must "
|
f"Unknown quantization method: {self.quantization}. Must "
|
||||||
f"be one of {supported_quantization}.")
|
f"be one of {supported_quantization}.")
|
||||||
|
if is_hip(
|
||||||
|
) and self.quantization in rocm_not_supported_quantization:
|
||||||
|
raise ValueError(
|
||||||
|
f"{self.quantization} quantization is currently not supported "
|
||||||
|
f"in ROCm.")
|
||||||
logger.warning(f"{self.quantization} quantization is not fully "
|
logger.warning(f"{self.quantization} quantization is not fully "
|
||||||
"optimized yet. The speed can be slower than "
|
"optimized yet. The speed can be slower than "
|
||||||
"non-quantized models.")
|
"non-quantized models.")
|
||||||
@@ -161,6 +192,12 @@ class ModelConfig:
|
|||||||
"must be divisible by pipeline parallel size "
|
"must be divisible by pipeline parallel size "
|
||||||
f"({pipeline_parallel_size}).")
|
f"({pipeline_parallel_size}).")
|
||||||
|
|
||||||
|
def get_sliding_window(self) -> Optional[int]:
|
||||||
|
return getattr(self.hf_config, "sliding_window", None)
|
||||||
|
|
||||||
|
def get_vocab_size(self) -> int:
|
||||||
|
return self.hf_config.vocab_size
|
||||||
|
|
||||||
def get_hidden_size(self) -> int:
|
def get_hidden_size(self) -> int:
|
||||||
return self.hf_config.hidden_size
|
return self.hf_config.hidden_size
|
||||||
|
|
||||||
@@ -285,10 +322,12 @@ class ParallelConfig:
|
|||||||
pipeline_parallel_size: int,
|
pipeline_parallel_size: int,
|
||||||
tensor_parallel_size: int,
|
tensor_parallel_size: int,
|
||||||
worker_use_ray: bool,
|
worker_use_ray: bool,
|
||||||
|
max_parallel_loading_workers: Optional[int] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.pipeline_parallel_size = pipeline_parallel_size
|
self.pipeline_parallel_size = pipeline_parallel_size
|
||||||
self.tensor_parallel_size = tensor_parallel_size
|
self.tensor_parallel_size = tensor_parallel_size
|
||||||
self.worker_use_ray = worker_use_ray
|
self.worker_use_ray = worker_use_ray
|
||||||
|
self.max_parallel_loading_workers = max_parallel_loading_workers
|
||||||
|
|
||||||
self.world_size = pipeline_parallel_size * tensor_parallel_size
|
self.world_size = pipeline_parallel_size * tensor_parallel_size
|
||||||
if self.world_size > 1:
|
if self.world_size > 1:
|
||||||
@@ -356,6 +395,8 @@ _STR_DTYPE_TO_TORCH_DTYPE = {
|
|||||||
"bfloat16": torch.bfloat16,
|
"bfloat16": torch.bfloat16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ROCM_NOT_SUPPORTED_DTYPE = ["float", "float32"]
|
||||||
|
|
||||||
|
|
||||||
def _get_and_verify_dtype(
|
def _get_and_verify_dtype(
|
||||||
config: PretrainedConfig,
|
config: PretrainedConfig,
|
||||||
@@ -385,6 +426,14 @@ def _get_and_verify_dtype(
|
|||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown dtype: {dtype}")
|
raise ValueError(f"Unknown dtype: {dtype}")
|
||||||
|
|
||||||
|
if is_hip() and torch_dtype == torch.float32:
|
||||||
|
rocm_supported_dtypes = [
|
||||||
|
k for k, v in _STR_DTYPE_TO_TORCH_DTYPE.items()
|
||||||
|
if (k not in _ROCM_NOT_SUPPORTED_DTYPE)
|
||||||
|
]
|
||||||
|
raise ValueError(f"dtype \'{dtype}\' is not supported in ROCm. "
|
||||||
|
f"Supported dtypes are {rocm_supported_dtypes}")
|
||||||
|
|
||||||
# Verify the dtype.
|
# Verify the dtype.
|
||||||
if torch_dtype != config_dtype:
|
if torch_dtype != config_dtype:
|
||||||
if torch_dtype == torch.float32:
|
if torch_dtype == torch.float32:
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
"""A block manager that manages token blocks."""
|
"""A block manager that manages token blocks."""
|
||||||
|
import enum
|
||||||
from typing import Dict, List, Optional, Set, Tuple
|
from typing import Dict, List, Optional, Set, Tuple
|
||||||
|
|
||||||
from vllm.block import PhysicalTokenBlock
|
from vllm.block import PhysicalTokenBlock
|
||||||
from vllm.sequence import Sequence, SequenceGroup, SequenceStatus
|
from vllm.sequence import Sequence, SequenceGroup, SequenceStatus
|
||||||
from vllm.utils import Device
|
from vllm.utils import Device
|
||||||
|
|
||||||
|
# Mapping: logical block number -> physical block.
|
||||||
|
BlockTable = List[PhysicalTokenBlock]
|
||||||
|
|
||||||
|
|
||||||
class BlockAllocator:
|
class BlockAllocator:
|
||||||
"""Manages free physical token blocks for a device.
|
"""Manages free physical token blocks for a device.
|
||||||
@@ -25,7 +29,7 @@ class BlockAllocator:
|
|||||||
self.num_blocks = num_blocks
|
self.num_blocks = num_blocks
|
||||||
|
|
||||||
# Initialize the free blocks.
|
# Initialize the free blocks.
|
||||||
self.free_blocks: List[PhysicalTokenBlock] = []
|
self.free_blocks: BlockTable = []
|
||||||
for i in range(num_blocks):
|
for i in range(num_blocks):
|
||||||
block = PhysicalTokenBlock(device=device,
|
block = PhysicalTokenBlock(device=device,
|
||||||
block_number=i,
|
block_number=i,
|
||||||
@@ -50,8 +54,18 @@ class BlockAllocator:
|
|||||||
return len(self.free_blocks)
|
return len(self.free_blocks)
|
||||||
|
|
||||||
|
|
||||||
# Mapping: logical block number -> physical block.
|
class AllocStatus(enum.Enum):
|
||||||
BlockTable = List[PhysicalTokenBlock]
|
"""Result for BlockSpaceManager.can_allocate
|
||||||
|
|
||||||
|
1. Ok: seq_group can be allocated now.
|
||||||
|
2. Later: seq_group cannot be allocated.
|
||||||
|
The capacity of allocator is larger than seq_group required.
|
||||||
|
3. Never: seq_group can never be allocated.
|
||||||
|
The seq_group is too large to allocated in GPU.
|
||||||
|
"""
|
||||||
|
OK = enum.auto()
|
||||||
|
LATER = enum.auto()
|
||||||
|
NEVER = enum.auto()
|
||||||
|
|
||||||
|
|
||||||
class BlockSpaceManager:
|
class BlockSpaceManager:
|
||||||
@@ -86,7 +100,7 @@ class BlockSpaceManager:
|
|||||||
# Mapping: seq_id -> BlockTable.
|
# Mapping: seq_id -> BlockTable.
|
||||||
self.block_tables: Dict[int, BlockTable] = {}
|
self.block_tables: Dict[int, BlockTable] = {}
|
||||||
|
|
||||||
def can_allocate(self, seq_group: SequenceGroup) -> bool:
|
def can_allocate(self, seq_group: SequenceGroup) -> AllocStatus:
|
||||||
# FIXME(woosuk): Here we assume that all sequences in the group share
|
# FIXME(woosuk): Here we assume that all sequences in the group share
|
||||||
# the same prompt. This may not be true for preempted sequences.
|
# the same prompt. This may not be true for preempted sequences.
|
||||||
seq = seq_group.get_seqs()[0]
|
seq = seq_group.get_seqs()[0]
|
||||||
@@ -95,9 +109,15 @@ class BlockSpaceManager:
|
|||||||
num_required_blocks = min(num_required_blocks,
|
num_required_blocks = min(num_required_blocks,
|
||||||
self.block_sliding_window)
|
self.block_sliding_window)
|
||||||
num_free_gpu_blocks = self.gpu_allocator.get_num_free_blocks()
|
num_free_gpu_blocks = self.gpu_allocator.get_num_free_blocks()
|
||||||
|
|
||||||
# Use watermark to avoid frequent cache eviction.
|
# Use watermark to avoid frequent cache eviction.
|
||||||
return (num_free_gpu_blocks - num_required_blocks >=
|
if (self.num_total_gpu_blocks - num_required_blocks <
|
||||||
self.watermark_blocks)
|
self.watermark_blocks):
|
||||||
|
return AllocStatus.NEVER
|
||||||
|
if num_free_gpu_blocks - num_required_blocks >= self.watermark_blocks:
|
||||||
|
return AllocStatus.OK
|
||||||
|
else:
|
||||||
|
return AllocStatus.LATER
|
||||||
|
|
||||||
def allocate(self, seq_group: SequenceGroup) -> None:
|
def allocate(self, seq_group: SequenceGroup) -> None:
|
||||||
# NOTE: Here we assume that all sequences in the group have the same
|
# NOTE: Here we assume that all sequences in the group have the same
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import time
|
|||||||
from typing import Dict, Iterable, List, Optional, Tuple, Union
|
from typing import Dict, Iterable, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from vllm.config import CacheConfig, SchedulerConfig
|
from vllm.config import CacheConfig, SchedulerConfig
|
||||||
from vllm.core.block_manager import BlockSpaceManager
|
from vllm.core.block_manager import AllocStatus, BlockSpaceManager
|
||||||
from vllm.core.policy import PolicyFactory
|
from vllm.core.policy import PolicyFactory
|
||||||
from vllm.logger import init_logger
|
from vllm.logger import init_logger
|
||||||
from vllm.sequence import (Sequence, SequenceData, SequenceGroup,
|
from vllm.sequence import (Sequence, SequenceData, SequenceGroup,
|
||||||
@@ -154,8 +154,18 @@ class Scheduler:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# If the sequence group cannot be allocated, stop.
|
# If the sequence group cannot be allocated, stop.
|
||||||
if not self.block_manager.can_allocate(seq_group):
|
can_allocate = self.block_manager.can_allocate(seq_group)
|
||||||
|
if can_allocate == AllocStatus.LATER:
|
||||||
break
|
break
|
||||||
|
elif can_allocate == AllocStatus.NEVER:
|
||||||
|
logger.warning(
|
||||||
|
f"Input prompt ({num_prompt_tokens} tokens) is too long"
|
||||||
|
f" and exceeds the capacity of block_manager")
|
||||||
|
for seq in seq_group.get_seqs():
|
||||||
|
seq.status = SequenceStatus.FINISHED_IGNORED
|
||||||
|
ignored_seq_groups.append(seq_group)
|
||||||
|
self.waiting.pop(0)
|
||||||
|
continue
|
||||||
|
|
||||||
# If the number of batched tokens exceeds the limit, stop.
|
# If the number of batched tokens exceeds the limit, stop.
|
||||||
new_seq_lens = seq_lens + [num_prompt_tokens]
|
new_seq_lens = seq_lens + [num_prompt_tokens]
|
||||||
@@ -186,7 +196,8 @@ class Scheduler:
|
|||||||
scheduler_outputs = SchedulerOutputs(
|
scheduler_outputs = SchedulerOutputs(
|
||||||
scheduled_seq_groups=scheduled,
|
scheduled_seq_groups=scheduled,
|
||||||
prompt_run=True,
|
prompt_run=True,
|
||||||
num_batched_tokens=len(seq_lens) * max(seq_lens),
|
num_batched_tokens=len(seq_lens) *
|
||||||
|
max(seq_lens) if seq_lens else 0,
|
||||||
blocks_to_swap_in=blocks_to_swap_in,
|
blocks_to_swap_in=blocks_to_swap_in,
|
||||||
blocks_to_swap_out=blocks_to_swap_out,
|
blocks_to_swap_out=blocks_to_swap_out,
|
||||||
blocks_to_copy=blocks_to_copy,
|
blocks_to_copy=blocks_to_copy,
|
||||||
@@ -350,7 +361,7 @@ class Scheduler:
|
|||||||
elif preemption_mode == PreemptionMode.SWAP:
|
elif preemption_mode == PreemptionMode.SWAP:
|
||||||
self._preempt_by_swap(seq_group, blocks_to_swap_out)
|
self._preempt_by_swap(seq_group, blocks_to_swap_out)
|
||||||
else:
|
else:
|
||||||
assert False, "Invalid preemption mode."
|
raise AssertionError("Invalid preemption mode.")
|
||||||
|
|
||||||
def _preempt_by_recompute(
|
def _preempt_by_recompute(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class EngineArgs:
|
|||||||
worker_use_ray: bool = False
|
worker_use_ray: bool = False
|
||||||
pipeline_parallel_size: int = 1
|
pipeline_parallel_size: int = 1
|
||||||
tensor_parallel_size: int = 1
|
tensor_parallel_size: int = 1
|
||||||
|
max_parallel_loading_workers: Optional[int] = None
|
||||||
block_size: int = 16
|
block_size: int = 16
|
||||||
swap_space: int = 4 # GiB
|
swap_space: int = 4 # GiB
|
||||||
gpu_memory_utilization: float = 0.90
|
gpu_memory_utilization: float = 0.90
|
||||||
@@ -41,6 +42,10 @@ class EngineArgs:
|
|||||||
def add_cli_args(
|
def add_cli_args(
|
||||||
parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
|
parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
|
||||||
"""Shared CLI arguments for vLLM engine."""
|
"""Shared CLI arguments for vLLM engine."""
|
||||||
|
|
||||||
|
# NOTE: If you update any of the arguments below, please also
|
||||||
|
# make sure to update docs/source/models/engine_args.rst
|
||||||
|
|
||||||
# Model arguments
|
# Model arguments
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--model',
|
'--model',
|
||||||
@@ -128,6 +133,12 @@ class EngineArgs:
|
|||||||
type=int,
|
type=int,
|
||||||
default=EngineArgs.tensor_parallel_size,
|
default=EngineArgs.tensor_parallel_size,
|
||||||
help='number of tensor parallel replicas')
|
help='number of tensor parallel replicas')
|
||||||
|
parser.add_argument(
|
||||||
|
'--max-parallel-loading-workers',
|
||||||
|
type=int,
|
||||||
|
help='load model sequentially in multiple batches, '
|
||||||
|
'to avoid RAM OOM when using tensor '
|
||||||
|
'parallel and large models')
|
||||||
# KV cache arguments
|
# KV cache arguments
|
||||||
parser.add_argument('--block-size',
|
parser.add_argument('--block-size',
|
||||||
type=int,
|
type=int,
|
||||||
@@ -190,12 +201,14 @@ class EngineArgs:
|
|||||||
self.dtype, self.seed, self.revision,
|
self.dtype, self.seed, self.revision,
|
||||||
self.tokenizer_revision, self.max_model_len,
|
self.tokenizer_revision, self.max_model_len,
|
||||||
self.quantization)
|
self.quantization)
|
||||||
cache_config = CacheConfig(
|
cache_config = CacheConfig(self.block_size,
|
||||||
self.block_size, self.gpu_memory_utilization, self.swap_space,
|
self.gpu_memory_utilization,
|
||||||
getattr(model_config.hf_config, 'sliding_window', None))
|
self.swap_space,
|
||||||
|
model_config.get_sliding_window())
|
||||||
parallel_config = ParallelConfig(self.pipeline_parallel_size,
|
parallel_config = ParallelConfig(self.pipeline_parallel_size,
|
||||||
self.tensor_parallel_size,
|
self.tensor_parallel_size,
|
||||||
self.worker_use_ray)
|
self.worker_use_ray,
|
||||||
|
self.max_parallel_loading_workers)
|
||||||
scheduler_config = SchedulerConfig(self.max_num_batched_tokens,
|
scheduler_config = SchedulerConfig(self.max_num_batched_tokens,
|
||||||
self.max_num_seqs,
|
self.max_num_seqs,
|
||||||
model_config.max_model_len,
|
model_config.max_model_len,
|
||||||
|
|||||||
@@ -301,7 +301,16 @@ class AsyncLLMEngine:
|
|||||||
elif self.worker_use_ray:
|
elif self.worker_use_ray:
|
||||||
engine_class = ray.remote(num_cpus=0)(self._engine_class).remote
|
engine_class = ray.remote(num_cpus=0)(self._engine_class).remote
|
||||||
else:
|
else:
|
||||||
engine_class = ray.remote(num_gpus=1)(self._engine_class).remote
|
# FIXME(woosuk): This is a bit hacky. Be careful when changing the
|
||||||
|
# order of the arguments.
|
||||||
|
cache_config = args[1]
|
||||||
|
parallel_config = args[2]
|
||||||
|
if parallel_config.tensor_parallel_size == 1:
|
||||||
|
num_gpus = cache_config.gpu_memory_utilization
|
||||||
|
else:
|
||||||
|
num_gpus = 1
|
||||||
|
engine_class = ray.remote(num_gpus=num_gpus)(
|
||||||
|
self._engine_class).remote
|
||||||
return engine_class(*args, **kwargs)
|
return engine_class(*args, **kwargs)
|
||||||
|
|
||||||
async def engine_step(self) -> bool:
|
async def engine_step(self) -> bool:
|
||||||
|
|||||||
@@ -7,13 +7,14 @@ from vllm.config import (CacheConfig, ModelConfig, ParallelConfig,
|
|||||||
SchedulerConfig)
|
SchedulerConfig)
|
||||||
from vllm.core.scheduler import Scheduler, SchedulerOutputs
|
from vllm.core.scheduler import Scheduler, SchedulerOutputs
|
||||||
from vllm.engine.arg_utils import EngineArgs
|
from vllm.engine.arg_utils import EngineArgs
|
||||||
from vllm.engine.ray_utils import RayWorker, initialize_cluster, ray
|
from vllm.engine.metrics import record_metrics
|
||||||
|
from vllm.engine.ray_utils import RayWorkerVllm, initialize_cluster, ray
|
||||||
from vllm.logger import init_logger
|
from vllm.logger import init_logger
|
||||||
from vllm.outputs import RequestOutput
|
from vllm.outputs import RequestOutput
|
||||||
from vllm.sampling_params import SamplingParams
|
from vllm.sampling_params import SamplingParams
|
||||||
from vllm.sequence import (SamplerOutput, Sequence, SequenceGroup,
|
from vllm.sequence import (SamplerOutput, Sequence, SequenceGroup,
|
||||||
SequenceGroupMetadata, SequenceGroupOutputs,
|
SequenceGroupMetadata, SequenceGroupOutput,
|
||||||
SequenceOutputs, SequenceStatus)
|
SequenceOutput, SequenceStatus)
|
||||||
from vllm.transformers_utils.tokenizer import (detokenize_incrementally,
|
from vllm.transformers_utils.tokenizer import (detokenize_incrementally,
|
||||||
get_tokenizer)
|
get_tokenizer)
|
||||||
from vllm.utils import Counter
|
from vllm.utils import Counter
|
||||||
@@ -88,8 +89,6 @@ class LLMEngine:
|
|||||||
|
|
||||||
self.model_config = model_config
|
self.model_config = model_config
|
||||||
self.cache_config = cache_config
|
self.cache_config = cache_config
|
||||||
assert self.cache_config.sliding_window == getattr(
|
|
||||||
self.model_config.hf_config, "sliding_window", None)
|
|
||||||
self.parallel_config = parallel_config
|
self.parallel_config = parallel_config
|
||||||
self.scheduler_config = scheduler_config
|
self.scheduler_config = scheduler_config
|
||||||
self.log_stats = log_stats
|
self.log_stats = log_stats
|
||||||
@@ -125,7 +124,7 @@ class LLMEngine:
|
|||||||
def _init_workers(self, distributed_init_method: str):
|
def _init_workers(self, distributed_init_method: str):
|
||||||
# Lazy import the Worker to avoid importing torch.cuda/xformers
|
# Lazy import the Worker to avoid importing torch.cuda/xformers
|
||||||
# before CUDA_VISIBLE_DEVICES is set in the Worker
|
# before CUDA_VISIBLE_DEVICES is set in the Worker
|
||||||
from vllm.worker.worker import Worker # pylint: disable=import-outside-toplevel
|
from vllm.worker.worker import Worker
|
||||||
|
|
||||||
assert self.parallel_config.world_size == 1, (
|
assert self.parallel_config.world_size == 1, (
|
||||||
"Ray is required if parallel_config.world_size > 1.")
|
"Ray is required if parallel_config.world_size > 1.")
|
||||||
@@ -143,25 +142,35 @@ class LLMEngine:
|
|||||||
"init_model",
|
"init_model",
|
||||||
get_all_outputs=True,
|
get_all_outputs=True,
|
||||||
)
|
)
|
||||||
|
self._run_workers(
|
||||||
|
"load_model",
|
||||||
|
get_all_outputs=True,
|
||||||
|
max_concurrent_workers=self.parallel_config.
|
||||||
|
max_parallel_loading_workers,
|
||||||
|
)
|
||||||
|
|
||||||
def _init_workers_ray(self, placement_group: "PlacementGroup",
|
def _init_workers_ray(self, placement_group: "PlacementGroup",
|
||||||
**ray_remote_kwargs):
|
**ray_remote_kwargs):
|
||||||
# Lazy import the Worker to avoid importing torch.cuda/xformers
|
# Lazy import the Worker to avoid importing torch.cuda/xformers
|
||||||
# before CUDA_VISIBLE_DEVICES is set in the Worker
|
# before CUDA_VISIBLE_DEVICES is set in the Worker
|
||||||
from vllm.worker.worker import Worker # pylint: disable=import-outside-toplevel
|
from vllm.worker.worker import Worker
|
||||||
|
|
||||||
self.workers: List[Worker] = []
|
self.workers: List[Worker] = []
|
||||||
for bundle in placement_group.bundle_specs:
|
for bundle in placement_group.bundle_specs:
|
||||||
if not bundle.get("GPU", 0):
|
if not bundle.get("GPU", 0):
|
||||||
continue
|
continue
|
||||||
|
if self.parallel_config.tensor_parallel_size == 1:
|
||||||
|
num_gpus = self.cache_config.gpu_memory_utilization
|
||||||
|
else:
|
||||||
|
num_gpus = 1
|
||||||
worker = ray.remote(
|
worker = ray.remote(
|
||||||
num_cpus=0,
|
num_cpus=0,
|
||||||
num_gpus=1,
|
num_gpus=num_gpus,
|
||||||
scheduling_strategy=PlacementGroupSchedulingStrategy(
|
scheduling_strategy=PlacementGroupSchedulingStrategy(
|
||||||
placement_group=placement_group,
|
placement_group=placement_group,
|
||||||
placement_group_capture_child_tasks=True),
|
placement_group_capture_child_tasks=True),
|
||||||
**ray_remote_kwargs,
|
**ray_remote_kwargs,
|
||||||
)(RayWorker).remote(self.model_config.trust_remote_code)
|
)(RayWorkerVllm).remote(self.model_config.trust_remote_code)
|
||||||
self.workers.append(worker)
|
self.workers.append(worker)
|
||||||
|
|
||||||
# Initialize torch distributed process group for the workers.
|
# Initialize torch distributed process group for the workers.
|
||||||
@@ -182,6 +191,12 @@ class LLMEngine:
|
|||||||
"init_model",
|
"init_model",
|
||||||
get_all_outputs=True,
|
get_all_outputs=True,
|
||||||
)
|
)
|
||||||
|
self._run_workers(
|
||||||
|
"load_model",
|
||||||
|
get_all_outputs=True,
|
||||||
|
max_concurrent_workers=self.parallel_config.
|
||||||
|
max_parallel_loading_workers,
|
||||||
|
)
|
||||||
|
|
||||||
def _verify_args(self) -> None:
|
def _verify_args(self) -> None:
|
||||||
self.model_config.verify_with_parallel_config(self.parallel_config)
|
self.model_config.verify_with_parallel_config(self.parallel_config)
|
||||||
@@ -351,7 +366,7 @@ class LLMEngine:
|
|||||||
return current_worst_score >= highest_attainable_score
|
return current_worst_score >= highest_attainable_score
|
||||||
|
|
||||||
def _process_sequence_group_outputs(self, seq_group: SequenceGroup,
|
def _process_sequence_group_outputs(self, seq_group: SequenceGroup,
|
||||||
outputs: SequenceGroupOutputs) -> None:
|
outputs: SequenceGroupOutput) -> None:
|
||||||
# Process prompt logprobs
|
# Process prompt logprobs
|
||||||
prompt_logprobs = outputs.prompt_logprobs
|
prompt_logprobs = outputs.prompt_logprobs
|
||||||
if prompt_logprobs is not None:
|
if prompt_logprobs is not None:
|
||||||
@@ -372,7 +387,7 @@ class LLMEngine:
|
|||||||
|
|
||||||
# Process the child samples for each parent sequence
|
# Process the child samples for each parent sequence
|
||||||
for parent in parent_seqs:
|
for parent in parent_seqs:
|
||||||
child_samples: List[SequenceOutputs] = parent_child_dict[
|
child_samples: List[SequenceOutput] = parent_child_dict[
|
||||||
parent.seq_id]
|
parent.seq_id]
|
||||||
if len(child_samples) == 0:
|
if len(child_samples) == 0:
|
||||||
# This parent sequence has no children samples. Remove
|
# This parent sequence has no children samples. Remove
|
||||||
@@ -581,8 +596,8 @@ class LLMEngine:
|
|||||||
else:
|
else:
|
||||||
self.num_generation_tokens.append((now, num_batched_tokens))
|
self.num_generation_tokens.append((now, num_batched_tokens))
|
||||||
|
|
||||||
elapsed_time = now - self.last_logging_time
|
should_log = now - self.last_logging_time >= _LOGGING_INTERVAL_SEC
|
||||||
if elapsed_time < _LOGGING_INTERVAL_SEC:
|
if not should_log:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Discard the old stats.
|
# Discard the old stats.
|
||||||
@@ -621,6 +636,16 @@ class LLMEngine:
|
|||||||
else:
|
else:
|
||||||
cpu_cache_usage = 0.0
|
cpu_cache_usage = 0.0
|
||||||
|
|
||||||
|
record_metrics(
|
||||||
|
avg_prompt_throughput=avg_prompt_throughput,
|
||||||
|
avg_generation_throughput=avg_generation_throughput,
|
||||||
|
scheduler_running=len(self.scheduler.running),
|
||||||
|
scheduler_swapped=len(self.scheduler.swapped),
|
||||||
|
scheduler_waiting=len(self.scheduler.waiting),
|
||||||
|
gpu_cache_usage=gpu_cache_usage,
|
||||||
|
cpu_cache_usage=cpu_cache_usage,
|
||||||
|
)
|
||||||
|
|
||||||
logger.info("Avg prompt throughput: "
|
logger.info("Avg prompt throughput: "
|
||||||
f"{avg_prompt_throughput:.1f} tokens/s, "
|
f"{avg_prompt_throughput:.1f} tokens/s, "
|
||||||
"Avg generation throughput: "
|
"Avg generation throughput: "
|
||||||
@@ -682,16 +707,15 @@ class LLMEngine:
|
|||||||
seq.status = SequenceStatus.FINISHED_STOPPED
|
seq.status = SequenceStatus.FINISHED_STOPPED
|
||||||
return
|
return
|
||||||
|
|
||||||
def _run_workers(
|
def _run_workers_in_batch(
|
||||||
self,
|
self,
|
||||||
|
workers,
|
||||||
method: str,
|
method: str,
|
||||||
*args,
|
*args,
|
||||||
get_all_outputs: bool = False,
|
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Any:
|
):
|
||||||
"""Runs the given method on all workers."""
|
|
||||||
all_outputs = []
|
all_outputs = []
|
||||||
for worker in self.workers:
|
for worker in workers:
|
||||||
if self.parallel_config.worker_use_ray:
|
if self.parallel_config.worker_use_ray:
|
||||||
executor = partial(worker.execute_method.remote, method)
|
executor = partial(worker.execute_method.remote, method)
|
||||||
else:
|
else:
|
||||||
@@ -699,9 +723,31 @@ class LLMEngine:
|
|||||||
|
|
||||||
output = executor(*args, **kwargs)
|
output = executor(*args, **kwargs)
|
||||||
all_outputs.append(output)
|
all_outputs.append(output)
|
||||||
|
|
||||||
if self.parallel_config.worker_use_ray:
|
if self.parallel_config.worker_use_ray:
|
||||||
all_outputs = ray.get(all_outputs)
|
all_outputs = ray.get(all_outputs)
|
||||||
|
return all_outputs
|
||||||
|
|
||||||
|
def _run_workers(
|
||||||
|
self,
|
||||||
|
method: str,
|
||||||
|
*args,
|
||||||
|
get_all_outputs: bool = False,
|
||||||
|
max_concurrent_workers: Optional[int] = None,
|
||||||
|
**kwargs,
|
||||||
|
) -> Any:
|
||||||
|
"""Runs the given method on all workers."""
|
||||||
|
all_outputs = []
|
||||||
|
if max_concurrent_workers:
|
||||||
|
work_groups = [
|
||||||
|
self.workers[i:i + max_concurrent_workers]
|
||||||
|
for i in range(0, len(self.workers), max_concurrent_workers)
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
work_groups = [self.workers]
|
||||||
|
|
||||||
|
for workers in work_groups:
|
||||||
|
all_outputs.extend(
|
||||||
|
self._run_workers_in_batch(workers, method, *args, **kwargs))
|
||||||
|
|
||||||
if get_all_outputs:
|
if get_all_outputs:
|
||||||
return all_outputs
|
return all_outputs
|
||||||
|
|||||||
51
vllm/engine/metrics.py
Normal file
51
vllm/engine/metrics.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
from aioprometheus import Gauge
|
||||||
|
|
||||||
|
# The begin-* and end* here are used by the documentation generator
|
||||||
|
# to extract the metrics definitions.
|
||||||
|
|
||||||
|
# begin-metrics-definitions
|
||||||
|
gauge_avg_prompt_throughput = Gauge("vllm:avg_prompt_throughput_toks_per_s",
|
||||||
|
"Average prefill throughput in tokens/s.")
|
||||||
|
gauge_avg_generation_throughput = Gauge(
|
||||||
|
"vllm:avg_generation_throughput_toks_per_s",
|
||||||
|
"Average generation throughput in tokens/s.")
|
||||||
|
|
||||||
|
gauge_scheduler_running = Gauge(
|
||||||
|
"vllm:num_requests_running",
|
||||||
|
"Number of requests that is currently running for inference.")
|
||||||
|
gauge_scheduler_swapped = Gauge("vllm:num_requests_swapped",
|
||||||
|
"Number requests swapped to CPU.")
|
||||||
|
gauge_scheduler_waiting = Gauge("vllm:num_requests_waiting",
|
||||||
|
"Number of requests waiting to be processed.")
|
||||||
|
|
||||||
|
gauge_gpu_cache_usage = Gauge(
|
||||||
|
"vllm:gpu_cache_usage_perc",
|
||||||
|
"GPU KV-cache usage. 1 means 100 percent usage.")
|
||||||
|
gauge_cpu_cache_usage = Gauge(
|
||||||
|
"vllm:cpu_cache_usage_perc",
|
||||||
|
"CPU KV-cache usage. 1 means 100 percent usage.")
|
||||||
|
# end-metrics-definitions
|
||||||
|
|
||||||
|
labels = {}
|
||||||
|
|
||||||
|
|
||||||
|
def add_global_metrics_labels(**kwargs):
|
||||||
|
labels.update(kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def record_metrics(
|
||||||
|
avg_prompt_throughput: float,
|
||||||
|
avg_generation_throughput: float,
|
||||||
|
scheduler_running: int,
|
||||||
|
scheduler_swapped: int,
|
||||||
|
scheduler_waiting: int,
|
||||||
|
gpu_cache_usage: float,
|
||||||
|
cpu_cache_usage: float,
|
||||||
|
):
|
||||||
|
gauge_avg_prompt_throughput.set(labels, avg_prompt_throughput)
|
||||||
|
gauge_avg_generation_throughput.set(labels, avg_generation_throughput)
|
||||||
|
gauge_scheduler_running.set(labels, scheduler_running)
|
||||||
|
gauge_scheduler_swapped.set(labels, scheduler_swapped)
|
||||||
|
gauge_scheduler_waiting.set(labels, scheduler_waiting)
|
||||||
|
gauge_gpu_cache_usage.set(labels, gpu_cache_usage)
|
||||||
|
gauge_cpu_cache_usage.set(labels, cpu_cache_usage)
|
||||||
@@ -3,6 +3,7 @@ from typing import Optional, Tuple, TYPE_CHECKING
|
|||||||
|
|
||||||
from vllm.config import ParallelConfig
|
from vllm.config import ParallelConfig
|
||||||
from vllm.logger import init_logger
|
from vllm.logger import init_logger
|
||||||
|
from vllm.utils import is_hip
|
||||||
|
|
||||||
logger = init_logger(__name__)
|
logger = init_logger(__name__)
|
||||||
|
|
||||||
@@ -10,13 +11,12 @@ try:
|
|||||||
import ray
|
import ray
|
||||||
from ray.air.util.torch_dist import TorchDistributedWorker
|
from ray.air.util.torch_dist import TorchDistributedWorker
|
||||||
|
|
||||||
class RayWorker(TorchDistributedWorker):
|
class RayWorkerVllm(TorchDistributedWorker):
|
||||||
"""Ray wrapper for vllm.worker.Worker, allowing Worker to be
|
"""Ray wrapper for vllm.worker.Worker, allowing Worker to be
|
||||||
lazliy initialized after Ray sets CUDA_VISIBLE_DEVICES."""
|
lazliy initialized after Ray sets CUDA_VISIBLE_DEVICES."""
|
||||||
|
|
||||||
def __init__(self, init_cached_hf_modules=False) -> None:
|
def __init__(self, init_cached_hf_modules=False) -> None:
|
||||||
if init_cached_hf_modules:
|
if init_cached_hf_modules:
|
||||||
# pylint: disable=import-outside-toplevel
|
|
||||||
from transformers.dynamic_module_utils import init_hf_modules
|
from transformers.dynamic_module_utils import init_hf_modules
|
||||||
init_hf_modules()
|
init_hf_modules()
|
||||||
self.worker = None
|
self.worker = None
|
||||||
@@ -37,7 +37,7 @@ except ImportError as e:
|
|||||||
"`pip install ray pandas pyarrow`.")
|
"`pip install ray pandas pyarrow`.")
|
||||||
ray = None
|
ray = None
|
||||||
TorchDistributedWorker = None
|
TorchDistributedWorker = None
|
||||||
RayWorker = None # pylint: disable=invalid-name
|
RayWorkerVllm = None
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ray.util.placement_group import PlacementGroup
|
from ray.util.placement_group import PlacementGroup
|
||||||
@@ -74,7 +74,12 @@ def initialize_cluster(
|
|||||||
"Ray is not installed. Please install Ray to use distributed "
|
"Ray is not installed. Please install Ray to use distributed "
|
||||||
"serving.")
|
"serving.")
|
||||||
# Connect to a ray cluster.
|
# Connect to a ray cluster.
|
||||||
ray.init(address=ray_address, ignore_reinit_error=True)
|
if is_hip():
|
||||||
|
ray.init(address=ray_address,
|
||||||
|
ignore_reinit_error=True,
|
||||||
|
num_gpus=parallel_config.world_size)
|
||||||
|
else:
|
||||||
|
ray.init(address=ray_address, ignore_reinit_error=True)
|
||||||
|
|
||||||
if not parallel_config.worker_use_ray:
|
if not parallel_config.worker_use_ray:
|
||||||
# Initialize cluster locally.
|
# Initialize cluster locally.
|
||||||
|
|||||||
@@ -134,25 +134,21 @@ class LLM:
|
|||||||
if isinstance(prompts, str):
|
if isinstance(prompts, str):
|
||||||
# Convert a single prompt to a list.
|
# Convert a single prompt to a list.
|
||||||
prompts = [prompts]
|
prompts = [prompts]
|
||||||
if prompts is not None and prompt_token_ids is not None:
|
if (prompts is not None and prompt_token_ids is not None
|
||||||
if len(prompts) != len(prompt_token_ids):
|
and len(prompts) != len(prompt_token_ids)):
|
||||||
raise ValueError("The lengths of prompts and prompt_token_ids "
|
raise ValueError("The lengths of prompts and prompt_token_ids "
|
||||||
"must be the same.")
|
"must be the same.")
|
||||||
if sampling_params is None:
|
if sampling_params is None:
|
||||||
# Use default sampling params.
|
# Use default sampling params.
|
||||||
sampling_params = SamplingParams()
|
sampling_params = SamplingParams()
|
||||||
|
|
||||||
# Add requests to the engine.
|
# Add requests to the engine.
|
||||||
if prompts is not None:
|
num_requests = len(prompts) if prompts is not None else len(
|
||||||
num_requests = len(prompts)
|
prompt_token_ids)
|
||||||
else:
|
|
||||||
num_requests = len(prompt_token_ids)
|
|
||||||
for i in range(num_requests):
|
for i in range(num_requests):
|
||||||
prompt = prompts[i] if prompts is not None else None
|
prompt = prompts[i] if prompts is not None else None
|
||||||
if prompt_token_ids is None:
|
token_ids = None if prompt_token_ids is None else prompt_token_ids[
|
||||||
token_ids = None
|
i]
|
||||||
else:
|
|
||||||
token_ids = prompt_token_ids[i]
|
|
||||||
self._add_request(prompt, sampling_params, token_ids)
|
self._add_request(prompt, sampling_params, token_ids)
|
||||||
return self._run_engine(use_tqdm)
|
return self._run_engine(use_tqdm)
|
||||||
|
|
||||||
|
|||||||
@@ -3,21 +3,24 @@
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import codecs
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from typing import AsyncGenerator, Dict, List, Optional, Tuple, Union
|
from typing import AsyncGenerator, Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
|
from aioprometheus import MetricsMiddleware
|
||||||
|
from aioprometheus.asgi.starlette import metrics
|
||||||
import fastapi
|
import fastapi
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
from fastapi.exceptions import RequestValidationError
|
from fastapi.exceptions import RequestValidationError
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.responses import JSONResponse, StreamingResponse, Response
|
from fastapi.responses import JSONResponse, StreamingResponse, Response
|
||||||
from packaging import version
|
|
||||||
|
|
||||||
from vllm.engine.arg_utils import AsyncEngineArgs
|
from vllm.engine.arg_utils import AsyncEngineArgs
|
||||||
from vllm.engine.async_llm_engine import AsyncLLMEngine
|
from vllm.engine.async_llm_engine import AsyncLLMEngine
|
||||||
|
from vllm.engine.metrics import add_global_metrics_labels
|
||||||
from vllm.entrypoints.openai.protocol import (
|
from vllm.entrypoints.openai.protocol import (
|
||||||
CompletionRequest, CompletionResponse, CompletionResponseChoice,
|
CompletionRequest, CompletionResponse, CompletionResponseChoice,
|
||||||
CompletionResponseStreamChoice, CompletionStreamResponse,
|
CompletionResponseStreamChoice, CompletionStreamResponse,
|
||||||
@@ -31,20 +34,59 @@ from vllm.sampling_params import SamplingParams
|
|||||||
from vllm.transformers_utils.tokenizer import get_tokenizer
|
from vllm.transformers_utils.tokenizer import get_tokenizer
|
||||||
from vllm.utils import random_uuid
|
from vllm.utils import random_uuid
|
||||||
|
|
||||||
try:
|
|
||||||
import fastchat
|
|
||||||
from fastchat.conversation import Conversation, SeparatorStyle
|
|
||||||
from fastchat.model.model_adapter import get_conversation_template
|
|
||||||
_fastchat_available = True
|
|
||||||
except ImportError:
|
|
||||||
_fastchat_available = False
|
|
||||||
|
|
||||||
TIMEOUT_KEEP_ALIVE = 5 # seconds
|
TIMEOUT_KEEP_ALIVE = 5 # seconds
|
||||||
|
|
||||||
logger = init_logger(__name__)
|
logger = init_logger(__name__)
|
||||||
served_model = None
|
served_model = None
|
||||||
app = fastapi.FastAPI()
|
app = fastapi.FastAPI()
|
||||||
engine = None
|
engine = None
|
||||||
|
response_role = None
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="vLLM OpenAI-Compatible RESTful API server.")
|
||||||
|
parser.add_argument("--host", type=str, default=None, help="host name")
|
||||||
|
parser.add_argument("--port", type=int, default=8000, help="port number")
|
||||||
|
parser.add_argument("--allow-credentials",
|
||||||
|
action="store_true",
|
||||||
|
help="allow credentials")
|
||||||
|
parser.add_argument("--allowed-origins",
|
||||||
|
type=json.loads,
|
||||||
|
default=["*"],
|
||||||
|
help="allowed origins")
|
||||||
|
parser.add_argument("--allowed-methods",
|
||||||
|
type=json.loads,
|
||||||
|
default=["*"],
|
||||||
|
help="allowed methods")
|
||||||
|
parser.add_argument("--allowed-headers",
|
||||||
|
type=json.loads,
|
||||||
|
default=["*"],
|
||||||
|
help="allowed headers")
|
||||||
|
parser.add_argument("--served-model-name",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help="The model name used in the API. If not "
|
||||||
|
"specified, the model name will be the same as "
|
||||||
|
"the huggingface name.")
|
||||||
|
parser.add_argument("--chat-template",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help="The file path to the chat template, "
|
||||||
|
"or the template in single-line form "
|
||||||
|
"for the specified model")
|
||||||
|
parser.add_argument("--response-role",
|
||||||
|
type=str,
|
||||||
|
default="assistant",
|
||||||
|
help="The role name to return if "
|
||||||
|
"`request.add_generation_prompt=true`.")
|
||||||
|
|
||||||
|
parser = AsyncEngineArgs.add_cli_args(parser)
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
app.add_middleware(MetricsMiddleware) # Trace HTTP server metrics
|
||||||
|
app.add_route("/metrics", metrics) # Exposes HTTP metrics
|
||||||
|
|
||||||
|
|
||||||
def create_error_response(status_code: HTTPStatus,
|
def create_error_response(status_code: HTTPStatus,
|
||||||
@@ -54,8 +96,27 @@ def create_error_response(status_code: HTTPStatus,
|
|||||||
status_code=status_code.value)
|
status_code=status_code.value)
|
||||||
|
|
||||||
|
|
||||||
|
def load_chat_template(args, tokenizer):
|
||||||
|
if args.chat_template is not None:
|
||||||
|
try:
|
||||||
|
with open(args.chat_template, "r") as f:
|
||||||
|
chat_template = f.read()
|
||||||
|
except OSError:
|
||||||
|
# If opening a file fails, set chat template to be args to
|
||||||
|
# ensure we decode so our escape are interpreted correctly
|
||||||
|
chat_template = codecs.decode(args.chat_template, "unicode_escape")
|
||||||
|
|
||||||
|
tokenizer.chat_template = chat_template
|
||||||
|
logger.info(
|
||||||
|
f"Using supplied chat template:\n{tokenizer.chat_template}")
|
||||||
|
elif tokenizer.chat_template is not None:
|
||||||
|
logger.info(f"Using default chat template:\n{tokenizer.chat_template}")
|
||||||
|
else:
|
||||||
|
logger.warning("No chat template provided. Chat API will not work.")
|
||||||
|
|
||||||
|
|
||||||
@app.exception_handler(RequestValidationError)
|
@app.exception_handler(RequestValidationError)
|
||||||
async def validation_exception_handler(request, exc): # pylint: disable=unused-argument
|
async def validation_exception_handler(_, exc):
|
||||||
return create_error_response(HTTPStatus.BAD_REQUEST, str(exc))
|
return create_error_response(HTTPStatus.BAD_REQUEST, str(exc))
|
||||||
|
|
||||||
|
|
||||||
@@ -69,53 +130,6 @@ async def check_model(request) -> Optional[JSONResponse]:
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
async def get_gen_prompt(request) -> str:
|
|
||||||
if not _fastchat_available:
|
|
||||||
raise ModuleNotFoundError(
|
|
||||||
"fastchat is not installed. Please install fastchat to use "
|
|
||||||
"the chat completion and conversation APIs: `$ pip install fschat`"
|
|
||||||
)
|
|
||||||
if version.parse(fastchat.__version__) < version.parse("0.2.23"):
|
|
||||||
raise ImportError(
|
|
||||||
f"fastchat version is low. Current version: {fastchat.__version__} "
|
|
||||||
"Please upgrade fastchat to use: `$ pip install -U fschat`")
|
|
||||||
|
|
||||||
conv = get_conversation_template(request.model)
|
|
||||||
conv = Conversation(
|
|
||||||
name=conv.name,
|
|
||||||
system_template=conv.system_template,
|
|
||||||
system_message=conv.system_message,
|
|
||||||
roles=conv.roles,
|
|
||||||
messages=list(conv.messages), # prevent in-place modification
|
|
||||||
offset=conv.offset,
|
|
||||||
sep_style=SeparatorStyle(conv.sep_style),
|
|
||||||
sep=conv.sep,
|
|
||||||
sep2=conv.sep2,
|
|
||||||
stop_str=conv.stop_str,
|
|
||||||
stop_token_ids=conv.stop_token_ids,
|
|
||||||
)
|
|
||||||
|
|
||||||
if isinstance(request.messages, str):
|
|
||||||
prompt = request.messages
|
|
||||||
else:
|
|
||||||
for message in request.messages:
|
|
||||||
msg_role = message["role"]
|
|
||||||
if msg_role == "system":
|
|
||||||
conv.system_message = message["content"]
|
|
||||||
elif msg_role == "user":
|
|
||||||
conv.append_message(conv.roles[0], message["content"])
|
|
||||||
elif msg_role == "assistant":
|
|
||||||
conv.append_message(conv.roles[1], message["content"])
|
|
||||||
else:
|
|
||||||
raise ValueError(f"Unknown role: {msg_role}")
|
|
||||||
|
|
||||||
# Add a blank message for the assistant.
|
|
||||||
conv.append_message(conv.roles[1], None)
|
|
||||||
prompt = conv.get_prompt()
|
|
||||||
|
|
||||||
return prompt
|
|
||||||
|
|
||||||
|
|
||||||
async def check_length(
|
async def check_length(
|
||||||
request: Union[ChatCompletionRequest, CompletionRequest],
|
request: Union[ChatCompletionRequest, CompletionRequest],
|
||||||
prompt: Optional[str] = None,
|
prompt: Optional[str] = None,
|
||||||
@@ -124,10 +138,8 @@ async def check_length(
|
|||||||
assert (not (prompt is None and prompt_ids is None)
|
assert (not (prompt is None and prompt_ids is None)
|
||||||
and not (prompt is not None and prompt_ids is not None)
|
and not (prompt is not None and prompt_ids is not None)
|
||||||
), "Either prompt or prompt_ids should be provided."
|
), "Either prompt or prompt_ids should be provided."
|
||||||
if prompt_ids is not None:
|
input_ids = prompt_ids if prompt_ids is not None else tokenizer(
|
||||||
input_ids = prompt_ids
|
prompt).input_ids
|
||||||
else:
|
|
||||||
input_ids = tokenizer(prompt).input_ids
|
|
||||||
token_num = len(input_ids)
|
token_num = len(input_ids)
|
||||||
|
|
||||||
if request.max_tokens is None:
|
if request.max_tokens is None:
|
||||||
@@ -162,16 +174,26 @@ async def show_available_models():
|
|||||||
return ModelList(data=model_cards)
|
return ModelList(data=model_cards)
|
||||||
|
|
||||||
|
|
||||||
def create_logprobs(token_ids: List[int],
|
def create_logprobs(
|
||||||
id_logprobs: List[Dict[int, float]],
|
token_ids: List[int],
|
||||||
initial_text_offset: int = 0) -> LogProbs:
|
top_logprobs: Optional[List[Optional[Dict[int, float]]]] = None,
|
||||||
|
num_output_top_logprobs: Optional[int] = None,
|
||||||
|
initial_text_offset: int = 0,
|
||||||
|
) -> LogProbs:
|
||||||
"""Create OpenAI-style logprobs."""
|
"""Create OpenAI-style logprobs."""
|
||||||
logprobs = LogProbs()
|
logprobs = LogProbs()
|
||||||
last_token_len = 0
|
last_token_len = 0
|
||||||
for token_id, id_logprob in zip(token_ids, id_logprobs):
|
if num_output_top_logprobs:
|
||||||
|
logprobs.top_logprobs = []
|
||||||
|
for i, token_id in enumerate(token_ids):
|
||||||
|
step_top_logprobs = top_logprobs[i]
|
||||||
|
if step_top_logprobs is not None:
|
||||||
|
token_logprob = step_top_logprobs[token_id]
|
||||||
|
else:
|
||||||
|
token_logprob = None
|
||||||
token = tokenizer.convert_ids_to_tokens(token_id)
|
token = tokenizer.convert_ids_to_tokens(token_id)
|
||||||
logprobs.tokens.append(token)
|
logprobs.tokens.append(token)
|
||||||
logprobs.token_logprobs.append(id_logprob[token_id])
|
logprobs.token_logprobs.append(token_logprob)
|
||||||
if len(logprobs.text_offset) == 0:
|
if len(logprobs.text_offset) == 0:
|
||||||
logprobs.text_offset.append(initial_text_offset)
|
logprobs.text_offset.append(initial_text_offset)
|
||||||
else:
|
else:
|
||||||
@@ -179,10 +201,11 @@ def create_logprobs(token_ids: List[int],
|
|||||||
last_token_len)
|
last_token_len)
|
||||||
last_token_len = len(token)
|
last_token_len = len(token)
|
||||||
|
|
||||||
logprobs.top_logprobs.append({
|
if num_output_top_logprobs:
|
||||||
tokenizer.convert_ids_to_tokens(i): p
|
logprobs.top_logprobs.append({
|
||||||
for i, p in id_logprob.items()
|
tokenizer.convert_ids_to_tokens(i): p
|
||||||
})
|
for i, p in step_top_logprobs.items()
|
||||||
|
} if step_top_logprobs else None)
|
||||||
return logprobs
|
return logprobs
|
||||||
|
|
||||||
|
|
||||||
@@ -198,8 +221,6 @@ async def create_chat_completion(request: ChatCompletionRequest,
|
|||||||
- function_call (Users should implement this by themselves)
|
- function_call (Users should implement this by themselves)
|
||||||
- logit_bias (to be supported by vLLM engine)
|
- logit_bias (to be supported by vLLM engine)
|
||||||
"""
|
"""
|
||||||
logger.info(f"Received chat completion request: {request}")
|
|
||||||
|
|
||||||
error_check_ret = await check_model(request)
|
error_check_ret = await check_model(request)
|
||||||
if error_check_ret is not None:
|
if error_check_ret is not None:
|
||||||
return error_check_ret
|
return error_check_ret
|
||||||
@@ -209,7 +230,15 @@ async def create_chat_completion(request: ChatCompletionRequest,
|
|||||||
return create_error_response(HTTPStatus.BAD_REQUEST,
|
return create_error_response(HTTPStatus.BAD_REQUEST,
|
||||||
"logit_bias is not currently supported")
|
"logit_bias is not currently supported")
|
||||||
|
|
||||||
prompt = await get_gen_prompt(request)
|
try:
|
||||||
|
prompt = tokenizer.apply_chat_template(
|
||||||
|
conversation=request.messages,
|
||||||
|
tokenize=False,
|
||||||
|
add_generation_prompt=request.add_generation_prompt)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in applying chat template from request: {str(e)}")
|
||||||
|
return create_error_response(HTTPStatus.BAD_REQUEST, str(e))
|
||||||
|
|
||||||
token_ids, error_check_ret = await check_length(request, prompt=prompt)
|
token_ids, error_check_ret = await check_length(request, prompt=prompt)
|
||||||
if error_check_ret is not None:
|
if error_check_ret is not None:
|
||||||
return error_check_ret
|
return error_check_ret
|
||||||
@@ -217,14 +246,17 @@ async def create_chat_completion(request: ChatCompletionRequest,
|
|||||||
model_name = request.model
|
model_name = request.model
|
||||||
request_id = f"cmpl-{random_uuid()}"
|
request_id = f"cmpl-{random_uuid()}"
|
||||||
created_time = int(time.monotonic())
|
created_time = int(time.monotonic())
|
||||||
|
chunk_object_type = "chat.completion.chunk"
|
||||||
try:
|
try:
|
||||||
spaces_between_special_tokens = request.spaces_between_special_tokens
|
spaces_between_special_tokens = request.spaces_between_special_tokens
|
||||||
sampling_params = SamplingParams(
|
sampling_params = SamplingParams(
|
||||||
n=request.n,
|
n=request.n,
|
||||||
presence_penalty=request.presence_penalty,
|
presence_penalty=request.presence_penalty,
|
||||||
frequency_penalty=request.frequency_penalty,
|
frequency_penalty=request.frequency_penalty,
|
||||||
|
repetition_penalty=request.repetition_penalty,
|
||||||
temperature=request.temperature,
|
temperature=request.temperature,
|
||||||
top_p=request.top_p,
|
top_p=request.top_p,
|
||||||
|
min_p=request.min_p,
|
||||||
stop=request.stop,
|
stop=request.stop,
|
||||||
stop_token_ids=request.stop_token_ids,
|
stop_token_ids=request.stop_token_ids,
|
||||||
max_tokens=request.max_tokens,
|
max_tokens=request.max_tokens,
|
||||||
@@ -241,128 +273,161 @@ async def create_chat_completion(request: ChatCompletionRequest,
|
|||||||
result_generator = engine.generate(prompt, sampling_params, request_id,
|
result_generator = engine.generate(prompt, sampling_params, request_id,
|
||||||
token_ids)
|
token_ids)
|
||||||
|
|
||||||
def create_stream_response_json(
|
def get_role() -> str:
|
||||||
index: int,
|
if request.add_generation_prompt:
|
||||||
text: str,
|
return response_role
|
||||||
finish_reason: Optional[str] = None,
|
else:
|
||||||
usage: Optional[UsageInfo] = None,
|
return request.messages[-1]["role"]
|
||||||
) -> str:
|
|
||||||
choice_data = ChatCompletionResponseStreamChoice(
|
|
||||||
index=index,
|
|
||||||
delta=DeltaMessage(content=text),
|
|
||||||
finish_reason=finish_reason,
|
|
||||||
)
|
|
||||||
response = ChatCompletionStreamResponse(
|
|
||||||
id=request_id,
|
|
||||||
created=created_time,
|
|
||||||
model=model_name,
|
|
||||||
choices=[choice_data],
|
|
||||||
)
|
|
||||||
if usage is not None:
|
|
||||||
response.usage = usage
|
|
||||||
# exclude unset to leave details out of each sse
|
|
||||||
response_json = response.json(exclude_unset=True, ensure_ascii=False)
|
|
||||||
|
|
||||||
return response_json
|
|
||||||
|
|
||||||
async def completion_stream_generator() -> AsyncGenerator[str, None]:
|
async def completion_stream_generator() -> AsyncGenerator[str, None]:
|
||||||
# First chunk with role
|
# Send first response for each request.n (index) with the role
|
||||||
|
role = get_role()
|
||||||
for i in range(request.n):
|
for i in range(request.n):
|
||||||
choice_data = ChatCompletionResponseStreamChoice(
|
choice_data = ChatCompletionResponseStreamChoice(
|
||||||
index=i,
|
index=i, delta=DeltaMessage(role=role), finish_reason=None)
|
||||||
delta=DeltaMessage(role="assistant"),
|
|
||||||
finish_reason=None,
|
|
||||||
)
|
|
||||||
chunk = ChatCompletionStreamResponse(id=request_id,
|
chunk = ChatCompletionStreamResponse(id=request_id,
|
||||||
|
object=chunk_object_type,
|
||||||
|
created=created_time,
|
||||||
choices=[choice_data],
|
choices=[choice_data],
|
||||||
model=model_name)
|
model=model_name)
|
||||||
data = chunk.json(exclude_unset=True, ensure_ascii=False)
|
data = chunk.json(exclude_unset=True, ensure_ascii=False)
|
||||||
yield f"data: {data}\n\n"
|
yield f"data: {data}\n\n"
|
||||||
|
|
||||||
|
# Send response to echo the input portion of the last message
|
||||||
|
if request.echo:
|
||||||
|
last_msg_content = ""
|
||||||
|
if request.messages and isinstance(
|
||||||
|
request.messages, list) and request.messages[-1].get(
|
||||||
|
"content") and request.messages[-1].get(
|
||||||
|
"role") == role:
|
||||||
|
last_msg_content = request.messages[-1]["content"]
|
||||||
|
if last_msg_content:
|
||||||
|
for i in range(request.n):
|
||||||
|
choice_data = ChatCompletionResponseStreamChoice(
|
||||||
|
index=i,
|
||||||
|
delta=DeltaMessage(content=last_msg_content),
|
||||||
|
finish_reason=None)
|
||||||
|
chunk = ChatCompletionStreamResponse(
|
||||||
|
id=request_id,
|
||||||
|
object=chunk_object_type,
|
||||||
|
created=created_time,
|
||||||
|
choices=[choice_data],
|
||||||
|
model=model_name)
|
||||||
|
data = chunk.json(exclude_unset=True, ensure_ascii=False)
|
||||||
|
yield f"data: {data}\n\n"
|
||||||
|
|
||||||
|
# Send response for each token for each request.n (index)
|
||||||
previous_texts = [""] * request.n
|
previous_texts = [""] * request.n
|
||||||
previous_num_tokens = [0] * request.n
|
previous_num_tokens = [0] * request.n
|
||||||
|
finish_reason_sent = [False] * request.n
|
||||||
async for res in result_generator:
|
async for res in result_generator:
|
||||||
res: RequestOutput
|
res: RequestOutput
|
||||||
for output in res.outputs:
|
for output in res.outputs:
|
||||||
i = output.index
|
i = output.index
|
||||||
delta_text = output.text[len(previous_texts[i]):]
|
|
||||||
previous_texts[i] = output.text
|
if finish_reason_sent[i]:
|
||||||
completion_tokens = len(output.token_ids)
|
continue
|
||||||
previous_num_tokens[i] = completion_tokens
|
|
||||||
response_json = create_stream_response_json(
|
if output.finish_reason is None:
|
||||||
index=i,
|
# Send token-by-token response for each request.n
|
||||||
text=delta_text,
|
delta_text = output.text[len(previous_texts[i]):]
|
||||||
)
|
previous_texts[i] = output.text
|
||||||
yield f"data: {response_json}\n\n"
|
previous_num_tokens[i] = len(output.token_ids)
|
||||||
if output.finish_reason is not None:
|
choice_data = ChatCompletionResponseStreamChoice(
|
||||||
|
index=i,
|
||||||
|
delta=DeltaMessage(content=delta_text),
|
||||||
|
finish_reason=None)
|
||||||
|
chunk = ChatCompletionStreamResponse(
|
||||||
|
id=request_id,
|
||||||
|
object=chunk_object_type,
|
||||||
|
created=created_time,
|
||||||
|
choices=[choice_data],
|
||||||
|
model=model_name)
|
||||||
|
data = chunk.json(exclude_unset=True, ensure_ascii=False)
|
||||||
|
yield f"data: {data}\n\n"
|
||||||
|
else:
|
||||||
|
# Send the finish response for each request.n only once
|
||||||
prompt_tokens = len(res.prompt_token_ids)
|
prompt_tokens = len(res.prompt_token_ids)
|
||||||
final_usage = UsageInfo(
|
final_usage = UsageInfo(
|
||||||
prompt_tokens=prompt_tokens,
|
prompt_tokens=prompt_tokens,
|
||||||
completion_tokens=completion_tokens,
|
completion_tokens=previous_num_tokens[i],
|
||||||
total_tokens=prompt_tokens + completion_tokens,
|
total_tokens=prompt_tokens + previous_num_tokens[i],
|
||||||
)
|
)
|
||||||
response_json = create_stream_response_json(
|
choice_data = ChatCompletionResponseStreamChoice(
|
||||||
index=i,
|
index=i, delta=[], finish_reason=output.finish_reason)
|
||||||
text="",
|
chunk = ChatCompletionStreamResponse(
|
||||||
finish_reason=output.finish_reason,
|
id=request_id,
|
||||||
usage=final_usage,
|
object=chunk_object_type,
|
||||||
)
|
created=created_time,
|
||||||
yield f"data: {response_json}\n\n"
|
choices=[choice_data],
|
||||||
|
model=model_name)
|
||||||
|
if final_usage is not None:
|
||||||
|
chunk.usage = final_usage
|
||||||
|
data = chunk.json(exclude_unset=True,
|
||||||
|
exclude_none=True,
|
||||||
|
ensure_ascii=False)
|
||||||
|
yield f"data: {data}\n\n"
|
||||||
|
finish_reason_sent[i] = True
|
||||||
|
# Send the final done message after all response.n are finished
|
||||||
yield "data: [DONE]\n\n"
|
yield "data: [DONE]\n\n"
|
||||||
|
|
||||||
|
async def completion_full_generator():
|
||||||
|
final_res: RequestOutput = None
|
||||||
|
async for res in result_generator:
|
||||||
|
if await raw_request.is_disconnected():
|
||||||
|
# Abort the request if the client disconnects.
|
||||||
|
await engine.abort(request_id)
|
||||||
|
return create_error_response(HTTPStatus.BAD_REQUEST,
|
||||||
|
"Client disconnected")
|
||||||
|
final_res = res
|
||||||
|
assert final_res is not None
|
||||||
|
|
||||||
|
choices = []
|
||||||
|
role = get_role()
|
||||||
|
for output in final_res.outputs:
|
||||||
|
choice_data = ChatCompletionResponseChoice(
|
||||||
|
index=output.index,
|
||||||
|
message=ChatMessage(role=role, content=output.text),
|
||||||
|
finish_reason=output.finish_reason,
|
||||||
|
)
|
||||||
|
choices.append(choice_data)
|
||||||
|
|
||||||
|
if request.echo:
|
||||||
|
last_msg_content = ""
|
||||||
|
if request.messages and isinstance(
|
||||||
|
request.messages, list) and request.messages[-1].get(
|
||||||
|
"content") and request.messages[-1].get(
|
||||||
|
"role") == role:
|
||||||
|
last_msg_content = request.messages[-1]["content"]
|
||||||
|
|
||||||
|
for choice in choices:
|
||||||
|
full_message = last_msg_content + choice.message.content
|
||||||
|
choice.message.content = full_message
|
||||||
|
|
||||||
|
num_prompt_tokens = len(final_res.prompt_token_ids)
|
||||||
|
num_generated_tokens = sum(
|
||||||
|
len(output.token_ids) for output in final_res.outputs)
|
||||||
|
usage = UsageInfo(
|
||||||
|
prompt_tokens=num_prompt_tokens,
|
||||||
|
completion_tokens=num_generated_tokens,
|
||||||
|
total_tokens=num_prompt_tokens + num_generated_tokens,
|
||||||
|
)
|
||||||
|
response = ChatCompletionResponse(
|
||||||
|
id=request_id,
|
||||||
|
created=created_time,
|
||||||
|
model=model_name,
|
||||||
|
choices=choices,
|
||||||
|
usage=usage,
|
||||||
|
)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
# Streaming response
|
# Streaming response
|
||||||
if request.stream:
|
if request.stream:
|
||||||
return StreamingResponse(completion_stream_generator(),
|
return StreamingResponse(completion_stream_generator(),
|
||||||
media_type="text/event-stream")
|
media_type="text/event-stream")
|
||||||
|
else:
|
||||||
# Non-streaming response
|
return await completion_full_generator()
|
||||||
final_res: RequestOutput = None
|
|
||||||
async for res in result_generator:
|
|
||||||
if await raw_request.is_disconnected():
|
|
||||||
# Abort the request if the client disconnects.
|
|
||||||
await engine.abort(request_id)
|
|
||||||
return create_error_response(HTTPStatus.BAD_REQUEST,
|
|
||||||
"Client disconnected")
|
|
||||||
final_res = res
|
|
||||||
assert final_res is not None
|
|
||||||
choices = []
|
|
||||||
for output in final_res.outputs:
|
|
||||||
choice_data = ChatCompletionResponseChoice(
|
|
||||||
index=output.index,
|
|
||||||
message=ChatMessage(role="assistant", content=output.text),
|
|
||||||
finish_reason=output.finish_reason,
|
|
||||||
)
|
|
||||||
choices.append(choice_data)
|
|
||||||
|
|
||||||
num_prompt_tokens = len(final_res.prompt_token_ids)
|
|
||||||
num_generated_tokens = sum(
|
|
||||||
len(output.token_ids) for output in final_res.outputs)
|
|
||||||
usage = UsageInfo(
|
|
||||||
prompt_tokens=num_prompt_tokens,
|
|
||||||
completion_tokens=num_generated_tokens,
|
|
||||||
total_tokens=num_prompt_tokens + num_generated_tokens,
|
|
||||||
)
|
|
||||||
response = ChatCompletionResponse(
|
|
||||||
id=request_id,
|
|
||||||
created=created_time,
|
|
||||||
model=model_name,
|
|
||||||
choices=choices,
|
|
||||||
usage=usage,
|
|
||||||
)
|
|
||||||
|
|
||||||
if request.stream:
|
|
||||||
# When user requests streaming but we don't stream, we still need to
|
|
||||||
# return a streaming response with a single event.
|
|
||||||
response_json = response.json(ensure_ascii=False)
|
|
||||||
|
|
||||||
async def fake_stream_generator() -> AsyncGenerator[str, None]:
|
|
||||||
yield f"data: {response_json}\n\n"
|
|
||||||
yield "data: [DONE]\n\n"
|
|
||||||
|
|
||||||
return StreamingResponse(fake_stream_generator(),
|
|
||||||
media_type="text/event-stream")
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/v1/completions")
|
@app.post("/v1/completions")
|
||||||
@@ -373,23 +438,17 @@ async def create_completion(request: CompletionRequest, raw_request: Request):
|
|||||||
for the API specification. This API mimics the OpenAI Completion API.
|
for the API specification. This API mimics the OpenAI Completion API.
|
||||||
|
|
||||||
NOTE: Currently we do not support the following features:
|
NOTE: Currently we do not support the following features:
|
||||||
- echo (since the vLLM engine does not currently support
|
|
||||||
getting the logprobs of prompt tokens)
|
|
||||||
- suffix (the language models we currently support do not support
|
- suffix (the language models we currently support do not support
|
||||||
suffix)
|
suffix)
|
||||||
- logit_bias (to be supported by vLLM engine)
|
- logit_bias (to be supported by vLLM engine)
|
||||||
"""
|
"""
|
||||||
logger.info(f"Received completion request: {request}")
|
|
||||||
|
|
||||||
error_check_ret = await check_model(request)
|
error_check_ret = await check_model(request)
|
||||||
if error_check_ret is not None:
|
if error_check_ret is not None:
|
||||||
return error_check_ret
|
return error_check_ret
|
||||||
|
|
||||||
if request.echo:
|
# OpenAI API supports echoing the prompt when max_tokens is 0.
|
||||||
# We do not support echo since the vLLM engine does not
|
echo_without_generation = request.echo and request.max_tokens == 0
|
||||||
# currently support getting the logprobs of prompt tokens.
|
|
||||||
return create_error_response(HTTPStatus.BAD_REQUEST,
|
|
||||||
"echo is not currently supported")
|
|
||||||
|
|
||||||
if request.suffix is not None:
|
if request.suffix is not None:
|
||||||
# The language models we currently support do not support suffix.
|
# The language models we currently support do not support suffix.
|
||||||
@@ -439,15 +498,19 @@ async def create_completion(request: CompletionRequest, raw_request: Request):
|
|||||||
best_of=request.best_of,
|
best_of=request.best_of,
|
||||||
presence_penalty=request.presence_penalty,
|
presence_penalty=request.presence_penalty,
|
||||||
frequency_penalty=request.frequency_penalty,
|
frequency_penalty=request.frequency_penalty,
|
||||||
|
repetition_penalty=request.repetition_penalty,
|
||||||
temperature=request.temperature,
|
temperature=request.temperature,
|
||||||
top_p=request.top_p,
|
top_p=request.top_p,
|
||||||
top_k=request.top_k,
|
top_k=request.top_k,
|
||||||
|
min_p=request.min_p,
|
||||||
stop=request.stop,
|
stop=request.stop,
|
||||||
stop_token_ids=request.stop_token_ids,
|
stop_token_ids=request.stop_token_ids,
|
||||||
ignore_eos=request.ignore_eos,
|
ignore_eos=request.ignore_eos,
|
||||||
max_tokens=request.max_tokens,
|
max_tokens=request.max_tokens
|
||||||
|
if not echo_without_generation else 1,
|
||||||
logprobs=request.logprobs,
|
logprobs=request.logprobs,
|
||||||
use_beam_search=request.use_beam_search,
|
use_beam_search=request.use_beam_search,
|
||||||
|
prompt_logprobs=request.logprobs if request.echo else None,
|
||||||
skip_special_tokens=request.skip_special_tokens,
|
skip_special_tokens=request.skip_special_tokens,
|
||||||
spaces_between_special_tokens=spaces_between_special_tokens,
|
spaces_between_special_tokens=spaces_between_special_tokens,
|
||||||
)
|
)
|
||||||
@@ -497,24 +560,47 @@ async def create_completion(request: CompletionRequest, raw_request: Request):
|
|||||||
async def completion_stream_generator() -> AsyncGenerator[str, None]:
|
async def completion_stream_generator() -> AsyncGenerator[str, None]:
|
||||||
previous_texts = [""] * request.n
|
previous_texts = [""] * request.n
|
||||||
previous_num_tokens = [0] * request.n
|
previous_num_tokens = [0] * request.n
|
||||||
|
has_echoed = [False] * request.n
|
||||||
async for res in result_generator:
|
async for res in result_generator:
|
||||||
res: RequestOutput
|
res: RequestOutput
|
||||||
for output in res.outputs:
|
for output in res.outputs:
|
||||||
i = output.index
|
i = output.index
|
||||||
delta_text = output.text[len(previous_texts[i]):]
|
delta_text = output.text[len(previous_texts[i]):]
|
||||||
|
token_ids = output.token_ids[previous_num_tokens[i]:]
|
||||||
|
if request.logprobs is not None:
|
||||||
|
top_logprobs = output.logprobs[previous_num_tokens[i]:]
|
||||||
|
else:
|
||||||
|
top_logprobs = None
|
||||||
|
offsets = len(previous_texts[i])
|
||||||
|
if request.echo and not has_echoed[i]:
|
||||||
|
if not echo_without_generation:
|
||||||
|
delta_text = res.prompt + delta_text
|
||||||
|
token_ids = res.prompt_token_ids + token_ids
|
||||||
|
if top_logprobs:
|
||||||
|
top_logprobs = res.prompt_logprobs + top_logprobs
|
||||||
|
else: # only just return the prompt
|
||||||
|
delta_text = res.prompt
|
||||||
|
token_ids = res.prompt_token_ids
|
||||||
|
if top_logprobs:
|
||||||
|
top_logprobs = res.prompt_logprobs
|
||||||
|
has_echoed[i] = True
|
||||||
if request.logprobs is not None:
|
if request.logprobs is not None:
|
||||||
logprobs = create_logprobs(
|
logprobs = create_logprobs(
|
||||||
output.token_ids[previous_num_tokens[i]:],
|
token_ids=token_ids,
|
||||||
output.logprobs[previous_num_tokens[i]:],
|
top_logprobs=top_logprobs,
|
||||||
len(previous_texts[i]))
|
num_output_top_logprobs=request.logprobs,
|
||||||
|
initial_text_offset=offsets,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logprobs = None
|
logprobs = None
|
||||||
previous_texts[i] = output.text
|
previous_texts[i] = output.text
|
||||||
previous_num_tokens[i] = len(output.token_ids)
|
previous_num_tokens[i] = len(output.token_ids)
|
||||||
|
finish_reason = output.finish_reason
|
||||||
response_json = create_stream_response_json(
|
response_json = create_stream_response_json(
|
||||||
index=i,
|
index=i,
|
||||||
text=delta_text,
|
text=delta_text,
|
||||||
logprobs=logprobs,
|
logprobs=logprobs,
|
||||||
|
finish_reason=finish_reason,
|
||||||
)
|
)
|
||||||
yield f"data: {response_json}\n\n"
|
yield f"data: {response_json}\n\n"
|
||||||
if output.finish_reason is not None:
|
if output.finish_reason is not None:
|
||||||
@@ -553,14 +639,36 @@ async def create_completion(request: CompletionRequest, raw_request: Request):
|
|||||||
final_res = res
|
final_res = res
|
||||||
assert final_res is not None
|
assert final_res is not None
|
||||||
choices = []
|
choices = []
|
||||||
|
prompt_token_ids = final_res.prompt_token_ids
|
||||||
|
prompt_logprobs = final_res.prompt_logprobs
|
||||||
|
prompt_text = final_res.prompt
|
||||||
for output in final_res.outputs:
|
for output in final_res.outputs:
|
||||||
if request.logprobs is not None:
|
if request.logprobs is not None:
|
||||||
logprobs = create_logprobs(output.token_ids, output.logprobs)
|
if not echo_without_generation:
|
||||||
|
token_ids = output.token_ids
|
||||||
|
top_logprobs = output.logprobs
|
||||||
|
if request.echo:
|
||||||
|
token_ids = prompt_token_ids + token_ids
|
||||||
|
top_logprobs = prompt_logprobs + top_logprobs
|
||||||
|
else:
|
||||||
|
token_ids = prompt_token_ids
|
||||||
|
top_logprobs = prompt_logprobs
|
||||||
|
logprobs = create_logprobs(
|
||||||
|
token_ids=token_ids,
|
||||||
|
top_logprobs=top_logprobs,
|
||||||
|
num_output_top_logprobs=request.logprobs,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logprobs = None
|
logprobs = None
|
||||||
|
if not echo_without_generation:
|
||||||
|
output_text = output.text
|
||||||
|
if request.echo:
|
||||||
|
output_text = prompt_text + output_text
|
||||||
|
else:
|
||||||
|
output_text = prompt_text
|
||||||
choice_data = CompletionResponseChoice(
|
choice_data = CompletionResponseChoice(
|
||||||
index=output.index,
|
index=output.index,
|
||||||
text=output.text,
|
text=output_text,
|
||||||
logprobs=logprobs,
|
logprobs=logprobs,
|
||||||
finish_reason=output.finish_reason,
|
finish_reason=output.finish_reason,
|
||||||
)
|
)
|
||||||
@@ -598,34 +706,7 @@ async def create_completion(request: CompletionRequest, raw_request: Request):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(
|
args = parse_args()
|
||||||
description="vLLM OpenAI-Compatible RESTful API server.")
|
|
||||||
parser.add_argument("--host", type=str, default=None, help="host name")
|
|
||||||
parser.add_argument("--port", type=int, default=8000, help="port number")
|
|
||||||
parser.add_argument("--allow-credentials",
|
|
||||||
action="store_true",
|
|
||||||
help="allow credentials")
|
|
||||||
parser.add_argument("--allowed-origins",
|
|
||||||
type=json.loads,
|
|
||||||
default=["*"],
|
|
||||||
help="allowed origins")
|
|
||||||
parser.add_argument("--allowed-methods",
|
|
||||||
type=json.loads,
|
|
||||||
default=["*"],
|
|
||||||
help="allowed methods")
|
|
||||||
parser.add_argument("--allowed-headers",
|
|
||||||
type=json.loads,
|
|
||||||
default=["*"],
|
|
||||||
help="allowed headers")
|
|
||||||
parser.add_argument("--served-model-name",
|
|
||||||
type=str,
|
|
||||||
default=None,
|
|
||||||
help="The model name used in the API. If not "
|
|
||||||
"specified, the model name will be the same as "
|
|
||||||
"the huggingface name.")
|
|
||||||
|
|
||||||
parser = AsyncEngineArgs.add_cli_args(parser)
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
@@ -642,6 +723,8 @@ if __name__ == "__main__":
|
|||||||
else:
|
else:
|
||||||
served_model = args.model
|
served_model = args.model
|
||||||
|
|
||||||
|
response_role = args.response_role
|
||||||
|
|
||||||
engine_args = AsyncEngineArgs.from_cli_args(args)
|
engine_args = AsyncEngineArgs.from_cli_args(args)
|
||||||
engine = AsyncLLMEngine.from_engine_args(engine_args)
|
engine = AsyncLLMEngine.from_engine_args(engine_args)
|
||||||
engine_model_config = asyncio.run(engine.get_model_config())
|
engine_model_config = asyncio.run(engine.get_model_config())
|
||||||
@@ -652,6 +735,10 @@ if __name__ == "__main__":
|
|||||||
engine_model_config.tokenizer,
|
engine_model_config.tokenizer,
|
||||||
tokenizer_mode=engine_model_config.tokenizer_mode,
|
tokenizer_mode=engine_model_config.tokenizer_mode,
|
||||||
trust_remote_code=engine_model_config.trust_remote_code)
|
trust_remote_code=engine_model_config.trust_remote_code)
|
||||||
|
load_chat_template(args, tokenizer)
|
||||||
|
|
||||||
|
# Register labels for metrics
|
||||||
|
add_global_metrics_labels(model_name=engine_args.model)
|
||||||
|
|
||||||
uvicorn.run(app,
|
uvicorn.run(app,
|
||||||
host=args.host,
|
host=args.host,
|
||||||
|
|||||||
@@ -73,6 +73,10 @@ class ChatCompletionRequest(BaseModel):
|
|||||||
stop_token_ids: Optional[List[int]] = Field(default_factory=list)
|
stop_token_ids: Optional[List[int]] = Field(default_factory=list)
|
||||||
skip_special_tokens: Optional[bool] = True
|
skip_special_tokens: Optional[bool] = True
|
||||||
spaces_between_special_tokens: Optional[bool] = True
|
spaces_between_special_tokens: Optional[bool] = True
|
||||||
|
add_generation_prompt: Optional[bool] = True
|
||||||
|
echo: Optional[bool] = False
|
||||||
|
repetition_penalty: Optional[float] = 1.0
|
||||||
|
min_p: Optional[float] = 0.0
|
||||||
|
|
||||||
|
|
||||||
class CompletionRequest(BaseModel):
|
class CompletionRequest(BaseModel):
|
||||||
@@ -100,14 +104,15 @@ class CompletionRequest(BaseModel):
|
|||||||
stop_token_ids: Optional[List[int]] = Field(default_factory=list)
|
stop_token_ids: Optional[List[int]] = Field(default_factory=list)
|
||||||
skip_special_tokens: Optional[bool] = True
|
skip_special_tokens: Optional[bool] = True
|
||||||
spaces_between_special_tokens: Optional[bool] = True
|
spaces_between_special_tokens: Optional[bool] = True
|
||||||
|
repetition_penalty: Optional[float] = 1.0
|
||||||
|
min_p: Optional[float] = 0.0
|
||||||
|
|
||||||
|
|
||||||
class LogProbs(BaseModel):
|
class LogProbs(BaseModel):
|
||||||
text_offset: List[int] = Field(default_factory=list)
|
text_offset: List[int] = Field(default_factory=list)
|
||||||
token_logprobs: List[Optional[float]] = Field(default_factory=list)
|
token_logprobs: List[Optional[float]] = Field(default_factory=list)
|
||||||
tokens: List[str] = Field(default_factory=list)
|
tokens: List[str] = Field(default_factory=list)
|
||||||
top_logprobs: List[Optional[Dict[str,
|
top_logprobs: Optional[List[Optional[Dict[int, float]]]] = None
|
||||||
float]]] = Field(default_factory=list)
|
|
||||||
|
|
||||||
|
|
||||||
class CompletionResponseChoice(BaseModel):
|
class CompletionResponseChoice(BaseModel):
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
from vllm.model_executor.input_metadata import InputMetadata
|
from vllm.model_executor.input_metadata import InputMetadata
|
||||||
from vllm.model_executor.model_loader import get_model
|
from vllm.model_executor.model_loader import get_model
|
||||||
|
from vllm.model_executor.sampling_metadata import SamplingMetadata
|
||||||
from vllm.model_executor.utils import set_random_seed
|
from vllm.model_executor.utils import set_random_seed
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"InputMetadata",
|
"InputMetadata",
|
||||||
"get_model",
|
"get_model",
|
||||||
|
"SamplingMetadata",
|
||||||
"set_random_seed",
|
"set_random_seed",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,91 +1,42 @@
|
|||||||
from typing import Dict, List, Optional, Tuple
|
from typing import List, Optional
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
from xformers.ops import AttentionBias
|
|
||||||
|
|
||||||
from vllm.sampling_params import SamplingParams, SamplingType
|
|
||||||
from vllm.sequence import SequenceData
|
|
||||||
|
|
||||||
|
|
||||||
class InputMetadata:
|
class InputMetadata:
|
||||||
"""Metadata for input sequences. Used for PagedAttention.
|
"""Metadata for input sequences. Used in PagedAttention.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
seq_groups: List of (seq_ids, sampling_params).
|
|
||||||
seq_data: Seq_id -> SequenceData.
|
|
||||||
prompt_lens: Lengths of prompts.
|
prompt_lens: Lengths of prompts.
|
||||||
slot_mapping: The address to write the new KV to of each token.
|
slot_mapping: The address to write the new KV to of each token.
|
||||||
context_lens: the length of attention context for each generation token.
|
|
||||||
max_context_len: The maximum context length.
|
max_context_len: The maximum context length.
|
||||||
|
context_lens: the length of attention context for each sequence.
|
||||||
block_tables: The block tables. (Seq id -> list of physical block)
|
block_tables: The block tables. (Seq id -> list of physical block)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
seq_groups: List[Tuple[List[int], SamplingParams]],
|
|
||||||
seq_data: Dict[int, SequenceData],
|
|
||||||
prompt_lens: List[int],
|
prompt_lens: List[int],
|
||||||
slot_mapping: torch.Tensor,
|
slot_mapping: torch.Tensor,
|
||||||
context_lens: torch.Tensor,
|
max_context_len: Optional[int],
|
||||||
max_context_len: int,
|
context_lens: Optional[torch.Tensor],
|
||||||
block_tables: torch.Tensor,
|
block_tables: Optional[torch.Tensor],
|
||||||
selected_token_indices: torch.Tensor,
|
|
||||||
categorized_sample_indices: Dict[SamplingType, torch.Tensor],
|
|
||||||
sliding_window: Optional[int] = None,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
self.seq_groups = seq_groups
|
|
||||||
self.seq_data = seq_data
|
|
||||||
self.prompt_lens = prompt_lens
|
self.prompt_lens = prompt_lens
|
||||||
|
self.max_context_len = max_context_len
|
||||||
self.slot_mapping = slot_mapping
|
self.slot_mapping = slot_mapping
|
||||||
self.context_lens = context_lens
|
self.context_lens = context_lens
|
||||||
self.max_context_len = max_context_len
|
|
||||||
self.block_tables = block_tables
|
self.block_tables = block_tables
|
||||||
self.selected_token_indices = selected_token_indices
|
|
||||||
self.categorized_sample_indices = categorized_sample_indices
|
|
||||||
|
|
||||||
self.max_prompt_len = max(prompt_lens) if prompt_lens else 0
|
|
||||||
self.to_cache = None
|
|
||||||
if sliding_window is not None:
|
|
||||||
# We need to keep the positions of sliding windows within
|
|
||||||
# the key / value tables, this is helpful to know which
|
|
||||||
# elements we need to cache.
|
|
||||||
to_cache, start_idx = [], 0
|
|
||||||
for prompt_len in self.prompt_lens:
|
|
||||||
to_cache.extend(
|
|
||||||
range(
|
|
||||||
start_idx + max(0, prompt_len - sliding_window),
|
|
||||||
start_idx + prompt_len,
|
|
||||||
))
|
|
||||||
start_idx += self.max_prompt_len
|
|
||||||
to_cache.extend(range(start_idx, slot_mapping.shape[0]))
|
|
||||||
self.to_cache = torch.tensor(to_cache,
|
|
||||||
dtype=torch.int32,
|
|
||||||
device=self.slot_mapping.device)
|
|
||||||
|
|
||||||
self.num_prompts = len(prompt_lens)
|
|
||||||
self.num_prompt_tokens = self.num_prompts * self.max_prompt_len
|
|
||||||
self.num_generation_tokens = context_lens.shape[0]
|
|
||||||
if block_tables.numel() > 0:
|
|
||||||
self.max_num_blocks_per_seq = block_tables.shape[1]
|
|
||||||
else:
|
|
||||||
self.max_num_blocks_per_seq = 0
|
|
||||||
assert block_tables.shape[0] == self.num_generation_tokens
|
|
||||||
|
|
||||||
|
self.is_prompt = len(prompt_lens) > 0
|
||||||
# Set during the execution of the first attention op.
|
# Set during the execution of the first attention op.
|
||||||
self.attn_bias: Optional[AttentionBias] = None
|
# FIXME(woosuk): This is a hack.
|
||||||
|
self.attn_bias = None
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
# Print only useful metadata.
|
return ("InputMetadata("
|
||||||
return (
|
f"prompt_lens={self.prompt_lens}, "
|
||||||
f'InputMetadata('
|
f"max_context_len={self.max_context_len}, "
|
||||||
f'num_prompt_tokens={self.num_prompt_tokens}, '
|
f"slot_mapping={self.slot_mapping}, "
|
||||||
f'num_prompts={self.num_prompts}, '
|
f"context_lens={self.context_lens}, "
|
||||||
f'prompt_lens={self.prompt_lens}, '
|
f"block_tables={self.block_tables})")
|
||||||
f'num_generation_tokens={self.num_generation_tokens}, '
|
|
||||||
f'context_lens={self.context_lens}, '
|
|
||||||
f'max_context_len={self.max_context_len}), '
|
|
||||||
f'max_num_blocks_per_seq={self.max_num_blocks_per_seq}, '
|
|
||||||
f'block_tables={self.block_tables}, '
|
|
||||||
f'selected_token_indices={self.selected_token_indices}, '
|
|
||||||
f'categorized_sample_indices={self.categorized_sample_indices}, '
|
|
||||||
f'slot_mapping={self.slot_mapping})')
|
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
"""Custom activation functions."""
|
"""Custom activation functions."""
|
||||||
|
import math
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
import torch.nn as nn
|
import torch.nn as nn
|
||||||
|
import torch.nn.functional as F
|
||||||
|
|
||||||
from vllm import activation_ops
|
from vllm._C import ops
|
||||||
from vllm.model_executor.layers.quantization import QuantizationConfig
|
from vllm.model_executor.layers.quantization import QuantizationConfig
|
||||||
|
from vllm.model_executor.parallel_utils.parallel_state import (
|
||||||
|
get_tensor_model_parallel_rank, get_tensor_model_parallel_world_size)
|
||||||
|
from vllm.model_executor.parallel_utils.utils import divide
|
||||||
|
from vllm.model_executor.utils import set_weight_attrs
|
||||||
|
|
||||||
|
|
||||||
class SiluAndMul(nn.Module):
|
class SiluAndMul(nn.Module):
|
||||||
@@ -18,27 +24,43 @@ class SiluAndMul(nn.Module):
|
|||||||
return: (batch_size, seq_len, d) or (num_tokens, d)
|
return: (batch_size, seq_len, d) or (num_tokens, d)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def _forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||||
|
"""PyTorch-native implementation equivalent to forward()."""
|
||||||
|
d = x.shape[-1] // 2
|
||||||
|
return F.silu(x[..., :d]) * x[..., d:]
|
||||||
|
|
||||||
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||||
d = x.shape[-1] // 2
|
d = x.shape[-1] // 2
|
||||||
output_shape = (x.shape[:-1] + (d, ))
|
output_shape = (x.shape[:-1] + (d, ))
|
||||||
out = torch.empty(output_shape, dtype=x.dtype, device=x.device)
|
out = torch.empty(output_shape, dtype=x.dtype, device=x.device)
|
||||||
activation_ops.silu_and_mul(out, x)
|
ops.silu_and_mul(out, x)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
class NewGELU(nn.Module):
|
class NewGELU(nn.Module):
|
||||||
|
|
||||||
|
def _forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||||
|
"""PyTorch-native implementation equivalent to forward()."""
|
||||||
|
c = math.sqrt(2.0 / math.pi)
|
||||||
|
return 0.5 * x * (1.0 + torch.tanh(c *
|
||||||
|
(x + 0.044715 * torch.pow(x, 3.0))))
|
||||||
|
|
||||||
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||||
out = torch.empty_like(x)
|
out = torch.empty_like(x)
|
||||||
activation_ops.gelu_new(out, x)
|
ops.gelu_new(out, x)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
class FastGELU(nn.Module):
|
class FastGELU(nn.Module):
|
||||||
|
|
||||||
|
def _forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||||
|
"""PyTorch-native implementation equivalent to forward()."""
|
||||||
|
return 0.5 * x * (1.0 + torch.tanh(x * 0.7978845608 *
|
||||||
|
(1.0 + 0.044715 * x * x)))
|
||||||
|
|
||||||
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||||
out = torch.empty_like(x)
|
out = torch.empty_like(x)
|
||||||
activation_ops.gelu_fast(out, x)
|
ops.gelu_fast(out, x)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
@@ -51,17 +73,40 @@ class ScaledActivation(nn.Module):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
act_module: nn.Module,
|
act_module: nn.Module,
|
||||||
hidden_size: int,
|
intermediate_size: int,
|
||||||
params_dtype: torch.dtype,
|
input_is_parallel: bool = True,
|
||||||
|
params_dtype: Optional[torch.dtype] = None,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.act = act_module
|
self.act = act_module
|
||||||
|
self.input_is_parallel = input_is_parallel
|
||||||
|
if input_is_parallel:
|
||||||
|
tp_size = get_tensor_model_parallel_world_size()
|
||||||
|
intermediate_size_per_partition = divide(intermediate_size,
|
||||||
|
tp_size)
|
||||||
|
else:
|
||||||
|
intermediate_size_per_partition = intermediate_size
|
||||||
|
if params_dtype is None:
|
||||||
|
params_dtype = torch.get_default_dtype()
|
||||||
self.scales = nn.Parameter(
|
self.scales = nn.Parameter(
|
||||||
torch.empty(hidden_size, dtype=params_dtype, device="cuda"))
|
torch.empty(intermediate_size_per_partition,
|
||||||
|
dtype=params_dtype,
|
||||||
|
device="cuda"))
|
||||||
|
set_weight_attrs(self.scales, {"weight_loader": self.weight_loader})
|
||||||
|
|
||||||
def forward(self, x: torch.Tensor):
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||||
return self.act(x) / self.scales
|
return self.act(x) / self.scales
|
||||||
|
|
||||||
|
def weight_loader(self, param: nn.Parameter, loaded_weight: torch.Tensor):
|
||||||
|
param_data = param.data
|
||||||
|
if self.input_is_parallel:
|
||||||
|
tp_rank = get_tensor_model_parallel_rank()
|
||||||
|
shard_size = param_data.shape[0]
|
||||||
|
start_idx = tp_rank * shard_size
|
||||||
|
loaded_weight = loaded_weight.narrow(0, start_idx, shard_size)
|
||||||
|
assert param_data.shape == loaded_weight.shape
|
||||||
|
param_data.copy_(loaded_weight)
|
||||||
|
|
||||||
|
|
||||||
_ACTIVATION_REGISTRY = {
|
_ACTIVATION_REGISTRY = {
|
||||||
"gelu": nn.GELU(),
|
"gelu": nn.GELU(),
|
||||||
@@ -76,6 +121,8 @@ def get_act_fn(
|
|||||||
act_fn_name: str,
|
act_fn_name: str,
|
||||||
quant_config: Optional[QuantizationConfig] = None,
|
quant_config: Optional[QuantizationConfig] = None,
|
||||||
intermediate_size: Optional[int] = None,
|
intermediate_size: Optional[int] = None,
|
||||||
|
input_is_parallel: bool = True,
|
||||||
|
params_dtype: Optional[torch.dtype] = None,
|
||||||
) -> nn.Module:
|
) -> nn.Module:
|
||||||
"""Get an activation function by name."""
|
"""Get an activation function by name."""
|
||||||
act_fn_name = act_fn_name.lower()
|
act_fn_name = act_fn_name.lower()
|
||||||
@@ -84,15 +131,11 @@ def get_act_fn(
|
|||||||
f"Activation function {act_fn_name!r} is not supported.")
|
f"Activation function {act_fn_name!r} is not supported.")
|
||||||
|
|
||||||
act_fn = _ACTIVATION_REGISTRY[act_fn_name]
|
act_fn = _ACTIVATION_REGISTRY[act_fn_name]
|
||||||
if quant_config is not None:
|
if (quant_config is not None
|
||||||
if act_fn_name in quant_config.get_scaled_act_names():
|
and act_fn_name in quant_config.get_scaled_act_names()):
|
||||||
if intermediate_size is None:
|
if intermediate_size is None:
|
||||||
raise ValueError(
|
raise ValueError("intermediate_size must be specified for scaled "
|
||||||
"intermediate_size must be specified for scaled "
|
"activation functions.")
|
||||||
"activation functions.")
|
return ScaledActivation(act_fn, intermediate_size, input_is_parallel,
|
||||||
return ScaledActivation(
|
params_dtype)
|
||||||
act_fn,
|
|
||||||
intermediate_size,
|
|
||||||
params_dtype=torch.get_default_dtype(),
|
|
||||||
)
|
|
||||||
return act_fn
|
return act_fn
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""Multi-head attention."""
|
"""Multi-head attention."""
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
import torch.nn as nn
|
import torch.nn as nn
|
||||||
@@ -7,10 +7,10 @@ from xformers import ops as xops
|
|||||||
from xformers.ops.fmha.attn_bias import (BlockDiagonalCausalMask,
|
from xformers.ops.fmha.attn_bias import (BlockDiagonalCausalMask,
|
||||||
LowerTriangularMaskWithTensorBias)
|
LowerTriangularMaskWithTensorBias)
|
||||||
|
|
||||||
from vllm import attention_ops
|
from vllm._C import ops
|
||||||
from vllm import cache_ops
|
from vllm._C import cache_ops
|
||||||
from vllm.model_executor.input_metadata import InputMetadata
|
from vllm.model_executor.input_metadata import InputMetadata
|
||||||
from vllm.model_executor.layers.rotary_embedding import get_rope
|
from vllm.utils import is_hip
|
||||||
|
|
||||||
_SUPPORTED_HEAD_SIZES = [64, 80, 96, 112, 128, 256]
|
_SUPPORTED_HEAD_SIZES = [64, 80, 96, 112, 128, 256]
|
||||||
# Should be the same as PARTITION_SIZE in `paged_attention_v2_launcher`.
|
# Should be the same as PARTITION_SIZE in `paged_attention_v2_launcher`.
|
||||||
@@ -18,191 +18,47 @@ _PARTITION_SIZE = 512
|
|||||||
|
|
||||||
|
|
||||||
class PagedAttention(nn.Module):
|
class PagedAttention(nn.Module):
|
||||||
# pylint: disable=line-too-long
|
"""MHA/MQA/GQA layer with PagedAttention.
|
||||||
"""GPT-style multi-head PagedAttention.
|
|
||||||
|
|
||||||
This class takes query, key, and value tensors as input. The input tensors
|
This class takes query, key, and value tensors as input. The input tensors
|
||||||
can either contain prompt tokens or generation tokens, in addition to
|
can either contain prompt tokens or generation tokens.
|
||||||
paddings.
|
|
||||||
|
|
||||||
The class does the following:
|
The class does the following:
|
||||||
1. Perform multi_query_kv_attention for the prompts. This operation does
|
|
||||||
not use the KV cache.
|
1. Wait for the cache operations (e.g., swap, copy) to finish. The cache
|
||||||
2. Wait for the cache operations (e.g., swap, copy) to finish. The cache
|
|
||||||
operations are issued by the cache engine before executing the forward
|
operations are issued by the cache engine before executing the forward
|
||||||
pass of the model, and they are executed asynchronously.
|
pass of the model, and they are executed asynchronously.
|
||||||
3. Reshape and store the input key and value tensors in the KV cache.
|
2. Reshape and store the input key and value tensors in the KV cache.
|
||||||
4. Perform single_query_cached_kv_attention for the generation tokens.
|
3. Perform (multi-head/multi-query/grouped-query) attention using either
|
||||||
This operation reads the previous key and value tensors from the KV
|
xformers or the PagedAttention custom op.
|
||||||
cache.
|
4. Return the output tensor.
|
||||||
5. Return the output tensor.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(
|
||||||
num_heads: int,
|
self,
|
||||||
head_size: int,
|
num_heads: int,
|
||||||
scale: float,
|
head_size: int,
|
||||||
num_kv_heads: Optional[int] = None,
|
scale: float,
|
||||||
sliding_window: Optional[int] = None) -> None:
|
num_kv_heads: Optional[int] = None,
|
||||||
|
alibi_slopes: Optional[List[float]] = None,
|
||||||
|
sliding_window: Optional[int] = None,
|
||||||
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.num_heads = num_heads
|
self.num_heads = num_heads
|
||||||
self.head_size = head_size
|
self.head_size = head_size
|
||||||
self.scale = float(scale)
|
self.scale = float(scale)
|
||||||
self.num_kv_heads = num_heads if num_kv_heads is None else num_kv_heads
|
self.num_kv_heads = num_heads if num_kv_heads is None else num_kv_heads
|
||||||
self.sliding_window = sliding_window
|
self.sliding_window = sliding_window
|
||||||
|
if alibi_slopes is not None:
|
||||||
|
alibi_slopes = torch.tensor(alibi_slopes, dtype=torch.float32)
|
||||||
|
self.register_buffer("alibi_slopes", alibi_slopes, persistent=False)
|
||||||
|
|
||||||
assert self.num_heads % self.num_kv_heads == 0
|
assert self.num_heads % self.num_kv_heads == 0
|
||||||
self.num_queries_per_kv = self.num_heads // self.num_kv_heads
|
self.num_queries_per_kv = self.num_heads // self.num_kv_heads
|
||||||
self.head_mapping = torch.repeat_interleave(
|
|
||||||
torch.arange(self.num_kv_heads, dtype=torch.int32, device="cuda"),
|
|
||||||
self.num_queries_per_kv)
|
|
||||||
|
|
||||||
if self.head_size not in _SUPPORTED_HEAD_SIZES:
|
if self.head_size not in _SUPPORTED_HEAD_SIZES:
|
||||||
raise ValueError(f"head_size ({self.head_size}) is not supported. "
|
raise ValueError(f"head_size ({self.head_size}) is not supported. "
|
||||||
f"Supported head sizes: {_SUPPORTED_HEAD_SIZES}.")
|
f"Supported head sizes: {_SUPPORTED_HEAD_SIZES}.")
|
||||||
|
|
||||||
def set_attn_bias(
|
|
||||||
self,
|
|
||||||
input_metadata: InputMetadata,
|
|
||||||
dtype: torch.dtype,
|
|
||||||
) -> None:
|
|
||||||
del dtype # Unused.
|
|
||||||
if input_metadata.attn_bias is not None:
|
|
||||||
# Already set by a previous layer.
|
|
||||||
return
|
|
||||||
prompt_lens = [input_metadata.max_prompt_len
|
|
||||||
] * input_metadata.num_prompts
|
|
||||||
attn_bias = BlockDiagonalCausalMask.from_seqlens(prompt_lens)
|
|
||||||
if self.sliding_window is not None:
|
|
||||||
attn_bias = attn_bias.make_local_attention(self.sliding_window)
|
|
||||||
input_metadata.attn_bias = attn_bias
|
|
||||||
|
|
||||||
def multi_query_kv_attention(
|
|
||||||
self,
|
|
||||||
output: torch.Tensor,
|
|
||||||
query: torch.Tensor,
|
|
||||||
key: torch.Tensor,
|
|
||||||
value: torch.Tensor,
|
|
||||||
input_metadata: InputMetadata,
|
|
||||||
) -> torch.Tensor:
|
|
||||||
"""Normal attention for the prompt tokens.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
output: shape = [num_prompt_tokens, num_heads, head_size]
|
|
||||||
query: shape = [num_prompt_tokens, num_heads, head_size]
|
|
||||||
key: shape = [num_prompt_tokens, num_kv_heads, head_size]
|
|
||||||
value: shape = [num_prompt_tokens, num_kv_heads, head_size]
|
|
||||||
input_metadata: metadata for paged attention.
|
|
||||||
"""
|
|
||||||
if self.num_kv_heads != self.num_heads:
|
|
||||||
# Project the key and value tensors to the desired number of heads.
|
|
||||||
key = torch.repeat_interleave(key, self.num_queries_per_kv, dim=1)
|
|
||||||
value = torch.repeat_interleave(value,
|
|
||||||
self.num_queries_per_kv,
|
|
||||||
dim=1)
|
|
||||||
|
|
||||||
# TODO(woosuk): The unsqueeze op may incur some CPU overhead. Optimize.
|
|
||||||
out = xops.memory_efficient_attention_forward(
|
|
||||||
query.unsqueeze(0),
|
|
||||||
key.unsqueeze(0),
|
|
||||||
value.unsqueeze(0),
|
|
||||||
attn_bias=input_metadata.attn_bias,
|
|
||||||
p=0.0,
|
|
||||||
scale=self.scale,
|
|
||||||
)
|
|
||||||
# TODO(woosuk): Unnecessary copy. Optimize.
|
|
||||||
output.copy_(out.squeeze(0))
|
|
||||||
return output
|
|
||||||
|
|
||||||
def get_alibi_slopes(self) -> Optional[torch.Tensor]:
|
|
||||||
"""Returns the slopes for the alibi attention bias.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
slopes: shape = [num_heads]
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
def single_query_cached_kv_attention(
|
|
||||||
self,
|
|
||||||
output: torch.Tensor,
|
|
||||||
query: torch.Tensor,
|
|
||||||
key_cache: torch.Tensor,
|
|
||||||
value_cache: torch.Tensor,
|
|
||||||
input_metadata: InputMetadata,
|
|
||||||
alibi_slopes: Optional[torch.Tensor],
|
|
||||||
) -> None:
|
|
||||||
"""PagedAttention for the generation tokens.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
output: shape = [num_generation_tokens, num_heads, head_size]
|
|
||||||
query: shape = [num_generation_tokens, num_heads, head_size]
|
|
||||||
key_cache: shape = [num_blocks, num_kv_heads, head_size/x,
|
|
||||||
block_size, x]
|
|
||||||
value_cache: shape = [num_blocks, num_kv_heads, head_size,
|
|
||||||
block_size]
|
|
||||||
input_metadata: metadata for paged attention.
|
|
||||||
alibi_slopes: shape = [num_heads]
|
|
||||||
"""
|
|
||||||
block_size = value_cache.shape[3]
|
|
||||||
num_seqs, num_heads, head_size = query.shape
|
|
||||||
max_num_partitions = (
|
|
||||||
(input_metadata.max_context_len + _PARTITION_SIZE - 1) //
|
|
||||||
_PARTITION_SIZE)
|
|
||||||
# NOTE(woosuk): We use a simple heuristic to decide whether to use
|
|
||||||
# PagedAttention V1 or V2. If the number of partitions is 1, we use
|
|
||||||
# V1 to avoid the overhead of reduction. Also, if the number of
|
|
||||||
# sequences or heads is large, we use V1 since there is enough work
|
|
||||||
# to parallelize.
|
|
||||||
# TODO(woosuk): Tune this heuristic.
|
|
||||||
# For context len > 8192, use V2 kernel to avoid shared memory shortage.
|
|
||||||
use_v1 = input_metadata.max_context_len <= 8192 and (
|
|
||||||
max_num_partitions == 1 or num_seqs * num_heads > 512)
|
|
||||||
if use_v1:
|
|
||||||
# Run PagedAttention V1.
|
|
||||||
attention_ops.paged_attention_v1(
|
|
||||||
output,
|
|
||||||
query,
|
|
||||||
key_cache,
|
|
||||||
value_cache,
|
|
||||||
self.head_mapping,
|
|
||||||
self.scale,
|
|
||||||
input_metadata.block_tables,
|
|
||||||
input_metadata.context_lens,
|
|
||||||
block_size,
|
|
||||||
input_metadata.max_context_len,
|
|
||||||
alibi_slopes,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# Run PagedAttention V2.
|
|
||||||
assert _PARTITION_SIZE % block_size == 0
|
|
||||||
tmp_output = torch.empty(
|
|
||||||
size=(num_seqs, num_heads, max_num_partitions, head_size),
|
|
||||||
dtype=output.dtype,
|
|
||||||
device=output.device,
|
|
||||||
)
|
|
||||||
exp_sums = torch.empty(
|
|
||||||
size=(num_seqs, num_heads, max_num_partitions),
|
|
||||||
dtype=torch.float32,
|
|
||||||
device=output.device,
|
|
||||||
)
|
|
||||||
max_logits = torch.empty_like(exp_sums)
|
|
||||||
attention_ops.paged_attention_v2(
|
|
||||||
output,
|
|
||||||
exp_sums,
|
|
||||||
max_logits,
|
|
||||||
tmp_output,
|
|
||||||
query,
|
|
||||||
key_cache,
|
|
||||||
value_cache,
|
|
||||||
self.head_mapping,
|
|
||||||
self.scale,
|
|
||||||
input_metadata.block_tables,
|
|
||||||
input_metadata.context_lens,
|
|
||||||
block_size,
|
|
||||||
input_metadata.max_context_len,
|
|
||||||
alibi_slopes,
|
|
||||||
)
|
|
||||||
|
|
||||||
def forward(
|
def forward(
|
||||||
self,
|
self,
|
||||||
query: torch.Tensor,
|
query: torch.Tensor,
|
||||||
@@ -215,126 +71,7 @@ class PagedAttention(nn.Module):
|
|||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
"""PagedAttention forward pass.
|
"""PagedAttention forward pass.
|
||||||
|
|
||||||
NOTE: The query, key, and value tensors must be sliced from a qkv
|
|
||||||
tensor of shape [batch_size, seq_len, 3 * num_heads * head_size].
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
query: shape = [batch_size, seq_len, num_heads * head_size]
|
|
||||||
key: shape = [batch_size, seq_len, num_kv_heads * head_size]
|
|
||||||
value: shape = [batch_size, num_kv_heads * head_size]
|
|
||||||
key_cache: shape = [num_blocks, num_kv_heads, head_size/x,
|
|
||||||
block_size, x]
|
|
||||||
value_cache: shape = [num_blocks, num_kv_heads, head_size,
|
|
||||||
block_size]
|
|
||||||
input_metadata: metadata for paged attention.
|
|
||||||
cache_event: event to wait for the cache operations to finish.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
shape = [batch_size, seq_len, num_heads * head_size]
|
|
||||||
"""
|
|
||||||
batch_size, seq_len, _ = query.shape
|
|
||||||
# Reshape the query, key, and value tensors.
|
|
||||||
query = query.view(-1, self.num_heads, self.head_size)
|
|
||||||
key = key.view(-1, self.num_kv_heads, self.head_size)
|
|
||||||
value = value.view(-1, self.num_kv_heads, self.head_size)
|
|
||||||
|
|
||||||
# Pre-allocate the output tensor.
|
|
||||||
output = torch.empty_like(query)
|
|
||||||
|
|
||||||
# Compute the attention op for prompts.
|
|
||||||
num_prompt_tokens = input_metadata.num_prompt_tokens
|
|
||||||
if num_prompt_tokens > 0:
|
|
||||||
# Prompt run.
|
|
||||||
assert input_metadata.num_generation_tokens == 0
|
|
||||||
self.set_attn_bias(input_metadata, dtype=query.dtype)
|
|
||||||
self.multi_query_kv_attention(
|
|
||||||
output,
|
|
||||||
query,
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
input_metadata,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Wait until the cache op is done.
|
|
||||||
if cache_event is not None:
|
|
||||||
cache_event.wait()
|
|
||||||
|
|
||||||
# Reshape the keys and values and store them in the cache.
|
|
||||||
# When key_cache and value_cache are not provided, the new key
|
|
||||||
# and value vectors will not be cached.
|
|
||||||
if key_cache is not None and value_cache is not None:
|
|
||||||
key_to_cache = key
|
|
||||||
value_to_cache = value
|
|
||||||
slot_mapping = input_metadata.slot_mapping.view(-1)
|
|
||||||
if input_metadata.to_cache is not None:
|
|
||||||
key_to_cache = key_to_cache[input_metadata.to_cache]
|
|
||||||
value_to_cache = value_to_cache[input_metadata.to_cache]
|
|
||||||
slot_mapping = slot_mapping[input_metadata.to_cache]
|
|
||||||
|
|
||||||
cache_ops.reshape_and_cache(
|
|
||||||
key_to_cache,
|
|
||||||
value_to_cache,
|
|
||||||
key_cache,
|
|
||||||
value_cache,
|
|
||||||
slot_mapping,
|
|
||||||
)
|
|
||||||
|
|
||||||
if input_metadata.num_generation_tokens > 0:
|
|
||||||
# Decoding run.
|
|
||||||
assert input_metadata.num_prompt_tokens == 0
|
|
||||||
assert key_cache is not None and value_cache is not None, (
|
|
||||||
"key_cache and value_cache must be provided when "
|
|
||||||
"generating tokens.")
|
|
||||||
# Compute the attention op for generation tokens.
|
|
||||||
self.single_query_cached_kv_attention(output, query, key_cache,
|
|
||||||
value_cache, input_metadata,
|
|
||||||
self.get_alibi_slopes())
|
|
||||||
|
|
||||||
# Reshape the output tensor.
|
|
||||||
# NOTE(woosuk): The output tensor may include paddings.
|
|
||||||
return output.view(batch_size, seq_len,
|
|
||||||
self.num_heads * self.head_size)
|
|
||||||
|
|
||||||
|
|
||||||
class PagedAttentionWithRoPE(PagedAttention):
|
|
||||||
"""PagedAttention with rotary positional embedding."""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
num_heads: int,
|
|
||||||
head_size: int,
|
|
||||||
scale: float,
|
|
||||||
rotary_dim: int,
|
|
||||||
max_position: int = 8192,
|
|
||||||
base: int = 10000,
|
|
||||||
num_kv_heads: Optional[int] = None,
|
|
||||||
is_neox_style: bool = True,
|
|
||||||
rope_scaling: Optional[Dict[str, Any]] = None,
|
|
||||||
sliding_window: Optional[int] = None,
|
|
||||||
) -> None:
|
|
||||||
super().__init__(num_heads,
|
|
||||||
head_size,
|
|
||||||
scale,
|
|
||||||
num_kv_heads,
|
|
||||||
sliding_window=sliding_window)
|
|
||||||
self.rotary_emb = get_rope(head_size, rotary_dim, max_position, base,
|
|
||||||
is_neox_style, rope_scaling)
|
|
||||||
|
|
||||||
def forward(
|
|
||||||
self,
|
|
||||||
positions: torch.Tensor,
|
|
||||||
query: torch.Tensor,
|
|
||||||
key: torch.Tensor,
|
|
||||||
value: torch.Tensor,
|
|
||||||
key_cache: torch.Tensor,
|
|
||||||
value_cache: torch.Tensor,
|
|
||||||
input_metadata: InputMetadata,
|
|
||||||
cache_event: Optional[torch.cuda.Event],
|
|
||||||
) -> torch.Tensor:
|
|
||||||
""" PagedAttention forward pass with rotary embedding.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
positions: shape = [batch_size, seq_len]
|
|
||||||
query: shape = [batch_size, seq_len, num_heads * head_size]
|
query: shape = [batch_size, seq_len, num_heads * head_size]
|
||||||
key: shape = [batch_size, seq_len, num_kv_heads * head_size]
|
key: shape = [batch_size, seq_len, num_kv_heads * head_size]
|
||||||
value: shape = [batch_size, seq_len, num_kv_heads * head_size]
|
value: shape = [batch_size, seq_len, num_kv_heads * head_size]
|
||||||
@@ -342,110 +79,204 @@ class PagedAttentionWithRoPE(PagedAttention):
|
|||||||
block_size, x]
|
block_size, x]
|
||||||
value_cache: shape = [num_blocks, num_kv_heads, head_size,
|
value_cache: shape = [num_blocks, num_kv_heads, head_size,
|
||||||
block_size]
|
block_size]
|
||||||
input_metadata: metadata for paged attention.
|
input_metadata: metadata for the inputs.
|
||||||
cache_event: event to wait for the cache operations to finish.
|
cache_event: event to wait for the cache operations to finish.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
shape = [batch_size, seq_len, num_heads * head_size]
|
shape = [batch_size, seq_len, num_heads * head_size]
|
||||||
"""
|
"""
|
||||||
|
batch_size, seq_len, hidden_size = query.shape
|
||||||
|
# Reshape the query, key, and value tensors.
|
||||||
|
query = query.view(-1, self.num_heads, self.head_size)
|
||||||
|
key = key.view(-1, self.num_kv_heads, self.head_size)
|
||||||
|
value = value.view(-1, self.num_kv_heads, self.head_size)
|
||||||
|
slot_mapping = input_metadata.slot_mapping.flatten()
|
||||||
|
|
||||||
# Apply rotary embedding to the query and key before passing them
|
if cache_event is not None:
|
||||||
# to the attention op.
|
cache_event.wait()
|
||||||
query, key = self.rotary_emb(positions, query, key)
|
|
||||||
return super().forward(
|
# Reshape the keys and values and store them in the cache.
|
||||||
|
# If key_cache and value_cache are not provided, the new key and value
|
||||||
|
# vectors will not be cached. This happens during the initial memory
|
||||||
|
# profiling run.
|
||||||
|
if key_cache is not None and value_cache is not None:
|
||||||
|
cache_ops.reshape_and_cache(
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
key_cache,
|
||||||
|
value_cache,
|
||||||
|
slot_mapping,
|
||||||
|
)
|
||||||
|
|
||||||
|
if input_metadata.is_prompt:
|
||||||
|
# Prompt run.
|
||||||
|
if self.num_kv_heads != self.num_heads:
|
||||||
|
# As of Nov 2023, xformers only supports MHA. For MQA/GQA,
|
||||||
|
# project the key and value tensors to the desired number of
|
||||||
|
# heads.
|
||||||
|
# TODO(woosuk): Use MQA/GQA kernels for higher performance.
|
||||||
|
query = query.view(query.shape[0], self.num_kv_heads,
|
||||||
|
self.num_queries_per_kv, query.shape[-1])
|
||||||
|
key = key[:, :,
|
||||||
|
None, :].expand(key.shape[0], self.num_kv_heads,
|
||||||
|
self.num_queries_per_kv,
|
||||||
|
key.shape[-1])
|
||||||
|
value = value[:, :, None, :].expand(value.shape[0],
|
||||||
|
self.num_kv_heads,
|
||||||
|
self.num_queries_per_kv,
|
||||||
|
value.shape[-1])
|
||||||
|
|
||||||
|
# Set attention bias if not provided. This typically happens at the
|
||||||
|
# very attention layer of every iteration.
|
||||||
|
# FIXME(woosuk): This is a hack.
|
||||||
|
if input_metadata.attn_bias is None:
|
||||||
|
if self.alibi_slopes is None:
|
||||||
|
attn_bias = BlockDiagonalCausalMask.from_seqlens(
|
||||||
|
[seq_len] * batch_size)
|
||||||
|
if self.sliding_window is not None:
|
||||||
|
attn_bias = attn_bias.make_local_attention(
|
||||||
|
self.sliding_window)
|
||||||
|
input_metadata.attn_bias = attn_bias
|
||||||
|
else:
|
||||||
|
input_metadata.attn_bias = _make_alibi_bias(
|
||||||
|
self.alibi_slopes, batch_size, seq_len, query.dtype)
|
||||||
|
|
||||||
|
# TODO(woosuk): Too many view operations. Let's try to reduce them
|
||||||
|
# in the future for code readability.
|
||||||
|
if self.alibi_slopes is None:
|
||||||
|
query = query.unsqueeze(0)
|
||||||
|
key = key.unsqueeze(0)
|
||||||
|
value = value.unsqueeze(0)
|
||||||
|
else:
|
||||||
|
query = query.unflatten(0, (batch_size, seq_len))
|
||||||
|
key = key.unflatten(0, (batch_size, seq_len))
|
||||||
|
value = value.unflatten(0, (batch_size, seq_len))
|
||||||
|
|
||||||
|
out = xops.memory_efficient_attention_forward(
|
||||||
|
query,
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
attn_bias=input_metadata.attn_bias,
|
||||||
|
p=0.0,
|
||||||
|
scale=self.scale,
|
||||||
|
op=xops.fmha.MemoryEfficientAttentionFlashAttentionOp[0] if
|
||||||
|
(is_hip()) else None,
|
||||||
|
)
|
||||||
|
output = out.view_as(query)
|
||||||
|
else:
|
||||||
|
# Decoding run.
|
||||||
|
output = _paged_attention(
|
||||||
|
query,
|
||||||
|
key_cache,
|
||||||
|
value_cache,
|
||||||
|
input_metadata,
|
||||||
|
self.num_kv_heads,
|
||||||
|
self.scale,
|
||||||
|
self.alibi_slopes,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Reshape the output tensor.
|
||||||
|
return output.view(batch_size, seq_len, hidden_size)
|
||||||
|
|
||||||
|
|
||||||
|
def _make_alibi_bias(
|
||||||
|
alibi_slopes: torch.Tensor,
|
||||||
|
batch_size: int,
|
||||||
|
seq_len: int,
|
||||||
|
dtype: torch.dtype,
|
||||||
|
) -> LowerTriangularMaskWithTensorBias:
|
||||||
|
bias = torch.arange(seq_len, dtype=dtype)
|
||||||
|
# NOTE(zhuohan): HF uses
|
||||||
|
# `bias = bias[None, :].repeat(prompt_len, 1)`
|
||||||
|
# here. We find that both biases give the same results, but
|
||||||
|
# the bias below more accurately follows the original ALiBi
|
||||||
|
# paper.
|
||||||
|
bias = bias[None, :] - bias[:, None]
|
||||||
|
bias = bias.to(alibi_slopes.device)
|
||||||
|
|
||||||
|
# When using custom attention bias, xformers requires the bias to
|
||||||
|
# be sliced from a tensor whose length is a multiple of 8.
|
||||||
|
padded_len = (seq_len + 7) // 8 * 8
|
||||||
|
bias = torch.empty(
|
||||||
|
batch_size,
|
||||||
|
alibi_slopes.shape[0],
|
||||||
|
seq_len,
|
||||||
|
padded_len,
|
||||||
|
device=alibi_slopes.device,
|
||||||
|
dtype=dtype,
|
||||||
|
)[:, :, :, :seq_len].copy_(bias)
|
||||||
|
bias.mul_(alibi_slopes[:, None, None])
|
||||||
|
attn_bias = LowerTriangularMaskWithTensorBias(bias)
|
||||||
|
return attn_bias
|
||||||
|
|
||||||
|
|
||||||
|
def _paged_attention(
|
||||||
|
query: torch.Tensor,
|
||||||
|
key_cache: torch.Tensor,
|
||||||
|
value_cache: torch.Tensor,
|
||||||
|
input_metadata: InputMetadata,
|
||||||
|
num_kv_heads: int,
|
||||||
|
scale: float,
|
||||||
|
alibi_slopes: Optional[torch.Tensor],
|
||||||
|
) -> torch.Tensor:
|
||||||
|
output = torch.empty_like(query)
|
||||||
|
|
||||||
|
block_size = value_cache.shape[3]
|
||||||
|
num_seqs, num_heads, head_size = query.shape
|
||||||
|
max_num_partitions = (
|
||||||
|
(input_metadata.max_context_len + _PARTITION_SIZE - 1) //
|
||||||
|
_PARTITION_SIZE)
|
||||||
|
# NOTE(woosuk): We use a simple heuristic to decide whether to use
|
||||||
|
# PagedAttention V1 or V2. If the number of partitions is 1, we use
|
||||||
|
# V1 to avoid the overhead of reduction. Also, if the number of
|
||||||
|
# sequences or heads is large, we use V1 since there is enough work
|
||||||
|
# to parallelize.
|
||||||
|
# TODO(woosuk): Tune this heuristic.
|
||||||
|
# For context len > 8192, use V2 kernel to avoid shared memory shortage.
|
||||||
|
use_v1 = input_metadata.max_context_len <= 8192 and (
|
||||||
|
max_num_partitions == 1 or num_seqs * num_heads > 512)
|
||||||
|
if use_v1:
|
||||||
|
# Run PagedAttention V1.
|
||||||
|
ops.paged_attention_v1(
|
||||||
|
output,
|
||||||
query,
|
query,
|
||||||
key,
|
|
||||||
value,
|
|
||||||
key_cache,
|
key_cache,
|
||||||
value_cache,
|
value_cache,
|
||||||
input_metadata,
|
num_kv_heads,
|
||||||
cache_event,
|
scale,
|
||||||
|
input_metadata.block_tables,
|
||||||
|
input_metadata.context_lens,
|
||||||
|
block_size,
|
||||||
|
input_metadata.max_context_len,
|
||||||
|
alibi_slopes,
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
# Run PagedAttention V2.
|
||||||
class PagedAttentionWithALiBi(PagedAttention):
|
assert _PARTITION_SIZE % block_size == 0
|
||||||
"""PagedAttention with ALiBi attention bias."""
|
tmp_output = torch.empty(
|
||||||
|
size=(num_seqs, num_heads, max_num_partitions, head_size),
|
||||||
def __init__(self,
|
dtype=output.dtype,
|
||||||
num_heads: int,
|
device=output.device,
|
||||||
head_size: int,
|
|
||||||
scale: float,
|
|
||||||
slopes: List[float],
|
|
||||||
num_kv_heads: Optional[int] = None) -> None:
|
|
||||||
super().__init__(num_heads, head_size, scale, num_kv_heads)
|
|
||||||
assert len(slopes) == num_heads
|
|
||||||
|
|
||||||
slopes = torch.tensor(slopes, dtype=torch.float32)
|
|
||||||
self.register_buffer("alibi_slopes", slopes, persistent=False)
|
|
||||||
|
|
||||||
def set_attn_bias(self, input_metadata: InputMetadata,
|
|
||||||
dtype: torch.dtype) -> None:
|
|
||||||
if input_metadata.attn_bias is not None:
|
|
||||||
# Already set by a previous layer.
|
|
||||||
return
|
|
||||||
# Generates ALiBi mask based on the max prompt length.
|
|
||||||
max_prompt_len = input_metadata.max_prompt_len
|
|
||||||
bias = torch.arange(max_prompt_len, dtype=dtype)
|
|
||||||
# NOTE(zhuohan): HF uses
|
|
||||||
# `bias = bias[None, :].repeat(prompt_len, 1)`
|
|
||||||
# here. We find that both biases give the same results, but
|
|
||||||
# the bias below more accurately follows the original ALiBi
|
|
||||||
# paper.
|
|
||||||
bias = bias[None, :] - bias[:, None]
|
|
||||||
bias = bias.to(self.alibi_slopes.device)
|
|
||||||
|
|
||||||
# When using custom attention bias, xformers requires the bias to
|
|
||||||
# be sliced from a tensor whose length is a multiple of 8.
|
|
||||||
padded_len = (max_prompt_len + 7) // 8 * 8
|
|
||||||
bias = torch.empty(
|
|
||||||
input_metadata.num_prompts,
|
|
||||||
self.num_heads,
|
|
||||||
max_prompt_len,
|
|
||||||
padded_len,
|
|
||||||
device=self.alibi_slopes.device,
|
|
||||||
dtype=dtype,
|
|
||||||
)[:, :, :, :max_prompt_len].copy_(bias)
|
|
||||||
bias.mul_(self.alibi_slopes[:, None, None])
|
|
||||||
attn_bias = LowerTriangularMaskWithTensorBias(bias)
|
|
||||||
input_metadata.attn_bias = attn_bias
|
|
||||||
|
|
||||||
def multi_query_kv_attention(
|
|
||||||
self,
|
|
||||||
output: torch.Tensor,
|
|
||||||
query: torch.Tensor,
|
|
||||||
key: torch.Tensor,
|
|
||||||
value: torch.Tensor,
|
|
||||||
input_metadata: InputMetadata,
|
|
||||||
) -> torch.Tensor:
|
|
||||||
"""Attention with ALiBi bias for the prompt tokens.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
output: shape = [num_prompt_tokens, num_heads, head_size]
|
|
||||||
query: shape = [num_prompt_tokens, num_heads, head_size]
|
|
||||||
key: shape = [num_prompt_tokens, num_kv_heads, head_size]
|
|
||||||
value: shape = [num_prompt_tokens, num_kv_heads, head_size]
|
|
||||||
input_metadata: metadata for paged attention.
|
|
||||||
"""
|
|
||||||
if self.num_kv_heads != self.num_heads:
|
|
||||||
# Project the key and value tensors to the desired number of heads.
|
|
||||||
key = torch.repeat_interleave(key, self.num_queries_per_kv, dim=1)
|
|
||||||
value = torch.repeat_interleave(value,
|
|
||||||
self.num_queries_per_kv,
|
|
||||||
dim=1)
|
|
||||||
batch_size = input_metadata.num_prompts
|
|
||||||
seq_len = input_metadata.max_prompt_len
|
|
||||||
|
|
||||||
out = xops.memory_efficient_attention_forward(
|
|
||||||
query.view(batch_size, seq_len, self.num_heads, self.head_size),
|
|
||||||
key.view(batch_size, seq_len, self.num_heads, self.head_size),
|
|
||||||
value.view(batch_size, seq_len, self.num_heads, self.head_size),
|
|
||||||
attn_bias=input_metadata.attn_bias,
|
|
||||||
p=0.0,
|
|
||||||
scale=self.scale,
|
|
||||||
)
|
)
|
||||||
# TODO(woosuk): Unnecessary copy. Optimize.
|
exp_sums = torch.empty(
|
||||||
output.copy_(out.view(-1, self.num_heads, self.head_size))
|
size=(num_seqs, num_heads, max_num_partitions),
|
||||||
return output
|
dtype=torch.float32,
|
||||||
|
device=output.device,
|
||||||
def get_alibi_slopes(self) -> Optional[torch.Tensor]:
|
)
|
||||||
return self.alibi_slopes
|
max_logits = torch.empty_like(exp_sums)
|
||||||
|
ops.paged_attention_v2(
|
||||||
|
output,
|
||||||
|
exp_sums,
|
||||||
|
max_logits,
|
||||||
|
tmp_output,
|
||||||
|
query,
|
||||||
|
key_cache,
|
||||||
|
value_cache,
|
||||||
|
num_kv_heads,
|
||||||
|
scale,
|
||||||
|
input_metadata.block_tables,
|
||||||
|
input_metadata.context_lens,
|
||||||
|
block_size,
|
||||||
|
input_metadata.max_context_len,
|
||||||
|
alibi_slopes,
|
||||||
|
)
|
||||||
|
return output
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from typing import Optional, Tuple, Union
|
|||||||
import torch
|
import torch
|
||||||
import torch.nn as nn
|
import torch.nn as nn
|
||||||
|
|
||||||
from vllm import layernorm_ops
|
from vllm._C import ops
|
||||||
|
|
||||||
|
|
||||||
class RMSNorm(nn.Module):
|
class RMSNorm(nn.Module):
|
||||||
@@ -23,13 +23,33 @@ class RMSNorm(nn.Module):
|
|||||||
self.weight = nn.Parameter(torch.ones(hidden_size))
|
self.weight = nn.Parameter(torch.ones(hidden_size))
|
||||||
self.variance_epsilon = eps
|
self.variance_epsilon = eps
|
||||||
|
|
||||||
|
def _forward(
|
||||||
|
self,
|
||||||
|
x: torch.Tensor,
|
||||||
|
residual: Optional[torch.Tensor] = None,
|
||||||
|
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
||||||
|
"""PyTorch-native implementation equivalent to forward()."""
|
||||||
|
orig_dtype = x.dtype
|
||||||
|
x = x.to(torch.float32)
|
||||||
|
if residual is not None:
|
||||||
|
x = x + residual.to(torch.float32)
|
||||||
|
residual = x.to(orig_dtype)
|
||||||
|
|
||||||
|
variance = x.pow(2).mean(dim=-1, keepdim=True)
|
||||||
|
x = x * torch.rsqrt(variance + self.variance_epsilon)
|
||||||
|
x = x.to(orig_dtype) * self.weight
|
||||||
|
if residual is None:
|
||||||
|
return x
|
||||||
|
else:
|
||||||
|
return x, residual
|
||||||
|
|
||||||
def forward(
|
def forward(
|
||||||
self,
|
self,
|
||||||
x: torch.Tensor,
|
x: torch.Tensor,
|
||||||
residual: Optional[torch.Tensor] = None,
|
residual: Optional[torch.Tensor] = None,
|
||||||
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
||||||
if residual is not None:
|
if residual is not None:
|
||||||
layernorm_ops.fused_add_rms_norm(
|
ops.fused_add_rms_norm(
|
||||||
x,
|
x,
|
||||||
residual,
|
residual,
|
||||||
self.weight.data,
|
self.weight.data,
|
||||||
@@ -37,7 +57,7 @@ class RMSNorm(nn.Module):
|
|||||||
)
|
)
|
||||||
return x, residual
|
return x, residual
|
||||||
out = torch.empty_like(x)
|
out = torch.empty_like(x)
|
||||||
layernorm_ops.rms_norm(
|
ops.rms_norm(
|
||||||
out,
|
out,
|
||||||
x,
|
x,
|
||||||
self.weight.data,
|
self.weight.data,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Any, Dict, List, Optional
|
|||||||
import torch
|
import torch
|
||||||
from torch.nn.parameter import Parameter
|
from torch.nn.parameter import Parameter
|
||||||
|
|
||||||
from vllm import quantization_ops
|
from vllm._C import ops
|
||||||
from vllm.model_executor.layers.linear import (LinearMethodBase,
|
from vllm.model_executor.layers.linear import (LinearMethodBase,
|
||||||
set_weight_attrs)
|
set_weight_attrs)
|
||||||
from vllm.model_executor.layers.quantization.base_config import QuantizationConfig
|
from vllm.model_executor.layers.quantization.base_config import QuantizationConfig
|
||||||
@@ -50,7 +50,7 @@ class AWQConfig(QuantizationConfig):
|
|||||||
def get_config_filenames() -> List[str]:
|
def get_config_filenames() -> List[str]:
|
||||||
return [
|
return [
|
||||||
"quant_config.json", # E.g., casperhansen/vicuna-7b-v1.5-awq
|
"quant_config.json", # E.g., casperhansen/vicuna-7b-v1.5-awq
|
||||||
"quantize_config.json", # E.g., abhinavkulkarni/mosaicml-mpt-7b-instruct-w4-g128-awq # pylint: disable=line-too-long
|
"quantize_config.json", # E.g., abhinavkulkarni/mosaicml-mpt-7b-instruct-w4-g128-awq
|
||||||
]
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -151,8 +151,7 @@ class AWQLinearMethod(LinearMethodBase):
|
|||||||
pack_factor = self.quant_config.pack_factor
|
pack_factor = self.quant_config.pack_factor
|
||||||
out_shape = (x.shape[:-1] + (qweight.shape[-1] * pack_factor, ))
|
out_shape = (x.shape[:-1] + (qweight.shape[-1] * pack_factor, ))
|
||||||
reshaped_x = x.reshape(-1, x.shape[-1])
|
reshaped_x = x.reshape(-1, x.shape[-1])
|
||||||
out = quantization_ops.awq_gemm(reshaped_x, qweight, scales, qzeros,
|
out = ops.awq_gemm(reshaped_x, qweight, scales, qzeros, pack_factor)
|
||||||
pack_factor)
|
|
||||||
if bias is not None:
|
if bias is not None:
|
||||||
out = out + bias
|
out = out + bias
|
||||||
return out.reshape(out_shape)
|
return out.reshape(out_shape)
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ from typing import Any, Dict, List, Optional
|
|||||||
import torch
|
import torch
|
||||||
from torch.nn.parameter import Parameter
|
from torch.nn.parameter import Parameter
|
||||||
|
|
||||||
from vllm import quantization_ops
|
from vllm._C import ops
|
||||||
from vllm.model_executor.layers.linear import (LinearMethodBase,
|
from vllm.model_executor.layers.linear import (LinearMethodBase,
|
||||||
set_weight_attrs)
|
set_weight_attrs)
|
||||||
from vllm.model_executor.layers.quantization.base_config import QuantizationConfig
|
from vllm.model_executor.layers.quantization.base_config import QuantizationConfig
|
||||||
|
from vllm.utils import is_hip
|
||||||
|
|
||||||
|
|
||||||
class SqueezeLLMConfig(QuantizationConfig):
|
class SqueezeLLMConfig(QuantizationConfig):
|
||||||
@@ -114,10 +115,14 @@ class SqueezeLLMLinearMethod(LinearMethodBase):
|
|||||||
lookup_table = weights["lookup_table"]
|
lookup_table = weights["lookup_table"]
|
||||||
out_shape = x.shape[:-1] + (qweight.shape[-1], )
|
out_shape = x.shape[:-1] + (qweight.shape[-1], )
|
||||||
reshaped_x = x.reshape(-1, x.shape[-1])
|
reshaped_x = x.reshape(-1, x.shape[-1])
|
||||||
# NOTE: The output tensor should be zero-initialized.
|
if is_hip():
|
||||||
out = torch.zeros(out_shape, device="cuda", dtype=torch.float16)
|
out_f = torch.zeros(out_shape, device="cuda", dtype=torch.float)
|
||||||
quantization_ops.squeezellm_gemm(reshaped_x, qweight, out,
|
ops.squeezellm_gemm(reshaped_x, qweight, out_f, lookup_table)
|
||||||
lookup_table)
|
out = out_f.to(dtype=torch.float16)
|
||||||
|
else:
|
||||||
|
# NOTE: The output tensor should be zero-initialized.
|
||||||
|
out = torch.zeros(out_shape, device="cuda", dtype=torch.float16)
|
||||||
|
ops.squeezellm_gemm(reshaped_x, qweight, out, lookup_table)
|
||||||
|
|
||||||
if bias is not None:
|
if bias is not None:
|
||||||
out = out + bias
|
out = out + bias
|
||||||
|
|||||||
@@ -27,7 +27,20 @@ from typing import Any, Dict, Optional, Tuple, Union
|
|||||||
import torch
|
import torch
|
||||||
import torch.nn as nn
|
import torch.nn as nn
|
||||||
|
|
||||||
from vllm import pos_encoding_ops
|
from vllm._C import ops
|
||||||
|
|
||||||
|
|
||||||
|
def _rotate_neox(x: torch.Tensor) -> torch.Tensor:
|
||||||
|
x1 = x[..., :x.shape[-1] // 2]
|
||||||
|
x2 = x[..., x.shape[-1] // 2:]
|
||||||
|
return torch.cat((-x2, x1), dim=-1)
|
||||||
|
|
||||||
|
|
||||||
|
def _rotate_gptj(x: torch.Tensor) -> torch.Tensor:
|
||||||
|
x1 = x[..., ::2]
|
||||||
|
x2 = x[..., 1::2]
|
||||||
|
x = torch.stack((-x2, x1), dim=-1)
|
||||||
|
return x.flatten(-2)
|
||||||
|
|
||||||
|
|
||||||
class RotaryEmbedding(nn.Module):
|
class RotaryEmbedding(nn.Module):
|
||||||
@@ -81,17 +94,57 @@ class RotaryEmbedding(nn.Module):
|
|||||||
cache = torch.cat((cos, sin), dim=-1)
|
cache = torch.cat((cos, sin), dim=-1)
|
||||||
return cache
|
return cache
|
||||||
|
|
||||||
|
def _forward(
|
||||||
|
self,
|
||||||
|
positions: torch.Tensor,
|
||||||
|
query: torch.Tensor,
|
||||||
|
key: torch.Tensor,
|
||||||
|
) -> Tuple[torch.Tensor, torch.Tensor]:
|
||||||
|
"""PyTorch-native implementation equivalent to forward()."""
|
||||||
|
query = query.view(*query.shape[:-1], -1, self.head_size)
|
||||||
|
key = key.view(*key.shape[:-1], -1, self.head_size)
|
||||||
|
|
||||||
|
query_rot = query[..., :self.rotary_dim]
|
||||||
|
key_rot = key[..., :self.rotary_dim]
|
||||||
|
if self.rotary_dim < self.head_size:
|
||||||
|
query_pass = query[..., self.rotary_dim:]
|
||||||
|
key_pass = key[..., self.rotary_dim:]
|
||||||
|
|
||||||
|
cos_sin = self.cos_sin_cache[positions]
|
||||||
|
cos, sin = cos_sin.chunk(2, dim=-1)
|
||||||
|
if self.is_neox_style:
|
||||||
|
# NOTE(woosuk): Here we assume that the positions tensor has the
|
||||||
|
# shape [batch_size, seq_len].
|
||||||
|
cos = cos.repeat(1, 1, 2).unsqueeze(-2)
|
||||||
|
sin = sin.repeat(1, 1, 2).unsqueeze(-2)
|
||||||
|
else:
|
||||||
|
cos = cos.repeat_interleave(2, dim=-1).unsqueeze(-2)
|
||||||
|
sin = sin.repeat_interleave(2, dim=-1).unsqueeze(-2)
|
||||||
|
|
||||||
|
rotate_fn = _rotate_neox if self.is_neox_style else _rotate_gptj
|
||||||
|
query_rot = query_rot * cos + rotate_fn(query_rot) * sin
|
||||||
|
key_rot = key_rot * cos + rotate_fn(key_rot) * sin
|
||||||
|
|
||||||
|
if self.rotary_dim < self.head_size:
|
||||||
|
query = torch.cat((query_rot, query_pass), dim=-1)
|
||||||
|
key = torch.cat((key_rot, key_pass), dim=-1)
|
||||||
|
else:
|
||||||
|
query = query_rot
|
||||||
|
key = key_rot
|
||||||
|
query = query.flatten(-2)
|
||||||
|
key = key.flatten(-2)
|
||||||
|
return query, key
|
||||||
|
|
||||||
def forward(
|
def forward(
|
||||||
self,
|
self,
|
||||||
positions: torch.Tensor,
|
positions: torch.Tensor,
|
||||||
query: torch.Tensor,
|
query: torch.Tensor,
|
||||||
key: torch.Tensor,
|
key: torch.Tensor,
|
||||||
) -> Tuple[torch.Tensor, torch.Tensor]:
|
) -> Tuple[torch.Tensor, torch.Tensor]:
|
||||||
# pos_encoding_ops.rotary_embedding() is an in-place operation that
|
# ops.rotary_embedding() is an in-place operation that
|
||||||
# updates the query and key tensors.
|
# updates the query and key tensors.
|
||||||
pos_encoding_ops.rotary_embedding(positions, query, key,
|
ops.rotary_embedding(positions, query, key, self.head_size,
|
||||||
self.head_size, self.cos_sin_cache,
|
self.cos_sin_cache, self.is_neox_style)
|
||||||
self.is_neox_style)
|
|
||||||
return query, key
|
return query, key
|
||||||
|
|
||||||
|
|
||||||
@@ -273,14 +326,22 @@ class YaRNScalingRotaryEmbedding(RotaryEmbedding):
|
|||||||
return cache
|
return cache
|
||||||
|
|
||||||
|
|
||||||
|
_ROPE_DICT: Dict[Tuple, RotaryEmbedding] = {}
|
||||||
|
|
||||||
|
|
||||||
def get_rope(
|
def get_rope(
|
||||||
head_size: int,
|
head_size: int,
|
||||||
rotary_dim: int,
|
rotary_dim: int,
|
||||||
max_position: int,
|
max_position: int,
|
||||||
base: int,
|
base: int,
|
||||||
is_neox_style: bool,
|
is_neox_style: bool = True,
|
||||||
rope_scaling: Optional[Dict[str, Any]],
|
rope_scaling: Optional[Dict[str, Any]] = None,
|
||||||
) -> RotaryEmbedding:
|
) -> RotaryEmbedding:
|
||||||
|
key = (head_size, rotary_dim, max_position, base, is_neox_style,
|
||||||
|
tuple(rope_scaling.items()) if rope_scaling is not None else None)
|
||||||
|
if key in _ROPE_DICT:
|
||||||
|
return _ROPE_DICT[key]
|
||||||
|
|
||||||
if rope_scaling is None:
|
if rope_scaling is None:
|
||||||
rotary_emb = RotaryEmbedding(head_size, rotary_dim, max_position, base,
|
rotary_emb = RotaryEmbedding(head_size, rotary_dim, max_position, base,
|
||||||
is_neox_style)
|
is_neox_style)
|
||||||
@@ -313,4 +374,5 @@ def get_rope(
|
|||||||
**extra_kwargs)
|
**extra_kwargs)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown RoPE scaling type {scaling_type}")
|
raise ValueError(f"Unknown RoPE scaling type {scaling_type}")
|
||||||
|
_ROPE_DICT[key] = rotary_emb
|
||||||
return rotary_emb
|
return rotary_emb
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ from typing import Dict, List, Optional, Tuple
|
|||||||
import torch
|
import torch
|
||||||
import torch.nn as nn
|
import torch.nn as nn
|
||||||
|
|
||||||
from vllm.model_executor.input_metadata import InputMetadata
|
|
||||||
from vllm.model_executor.parallel_utils.communication_op import (
|
from vllm.model_executor.parallel_utils.communication_op import (
|
||||||
tensor_model_parallel_all_gather)
|
tensor_model_parallel_all_gather)
|
||||||
|
from vllm.model_executor.sampling_metadata import SamplingMetadata
|
||||||
from vllm.sampling_params import SamplingParams, SamplingType
|
from vllm.sampling_params import SamplingParams, SamplingType
|
||||||
from vllm.sequence import (PromptLogprobs, SampleLogprobs, SamplerOutput,
|
from vllm.sequence import (PromptLogprobs, SampleLogprobs, SamplerOutput,
|
||||||
SequenceData, SequenceGroupOutputs, SequenceOutputs)
|
SequenceData, SequenceGroupOutput, SequenceOutput)
|
||||||
|
|
||||||
_SAMPLING_EPS = 1e-5
|
_SAMPLING_EPS = 1e-5
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ class Sampler(nn.Module):
|
|||||||
1. Discard the hidden states that are not used for sampling (i.e., all
|
1. Discard the hidden states that are not used for sampling (i.e., all
|
||||||
tokens except the final one in each prompt).
|
tokens except the final one in each prompt).
|
||||||
2. Compute the logits for the next tokens.
|
2. Compute the logits for the next tokens.
|
||||||
3. Apply presence and frequency penalties.
|
3. Apply presence, frequency and repetition penalties.
|
||||||
4. Apply temperature scaling.
|
4. Apply temperature scaling.
|
||||||
5. Apply top-p and top-k truncation.
|
5. Apply top-p and top-k truncation.
|
||||||
6. Sample the next tokens.
|
6. Sample the next tokens.
|
||||||
@@ -37,31 +37,30 @@ class Sampler(nn.Module):
|
|||||||
self,
|
self,
|
||||||
embedding: torch.Tensor,
|
embedding: torch.Tensor,
|
||||||
hidden_states: torch.Tensor,
|
hidden_states: torch.Tensor,
|
||||||
input_metadata: InputMetadata,
|
sampling_metadata: SamplingMetadata,
|
||||||
embedding_bias: Optional[torch.Tensor] = None,
|
embedding_bias: Optional[torch.Tensor] = None,
|
||||||
) -> SamplerOutput:
|
) -> SamplerOutput:
|
||||||
# Get the hidden states that we use for sampling.
|
# Get the hidden states that we use for sampling.
|
||||||
hidden_states = _prune_hidden_states(hidden_states, input_metadata)
|
hidden_states = _prune_hidden_states(hidden_states, sampling_metadata)
|
||||||
|
|
||||||
# Get the logits for the next tokens.
|
# Get the logits for the next tokens.
|
||||||
logits = _get_logits(hidden_states, embedding, embedding_bias,
|
logits = _get_logits(hidden_states, embedding, embedding_bias,
|
||||||
self.vocab_size)
|
self.vocab_size)
|
||||||
|
|
||||||
# Apply logits processors (if any).
|
# Apply logits processors (if any).
|
||||||
logits = _apply_logits_processors(logits, input_metadata)
|
logits = _apply_logits_processors(logits, sampling_metadata)
|
||||||
# Apply presence and frequency penalties.
|
# Apply presence and frequency penalties.
|
||||||
output_tokens = _get_output_tokens(input_metadata)
|
|
||||||
assert len(output_tokens) == logits.shape[0]
|
|
||||||
presence_penalties, frequency_penalties, repetition_penalties = (
|
presence_penalties, frequency_penalties, repetition_penalties = (
|
||||||
_get_penalties(input_metadata))
|
_get_penalties(sampling_metadata))
|
||||||
assert len(presence_penalties) == logits.shape[0]
|
assert len(presence_penalties) == logits.shape[0]
|
||||||
assert len(frequency_penalties) == logits.shape[0]
|
assert len(frequency_penalties) == logits.shape[0]
|
||||||
assert len(repetition_penalties) == logits.shape[0]
|
assert len(repetition_penalties) == logits.shape[0]
|
||||||
logits = _apply_penalties(logits, output_tokens, presence_penalties,
|
logits = _apply_penalties(logits, sampling_metadata,
|
||||||
frequency_penalties, repetition_penalties)
|
presence_penalties, frequency_penalties,
|
||||||
|
repetition_penalties)
|
||||||
|
|
||||||
# Apply temperature scaling.
|
# Apply temperature scaling.
|
||||||
temperatures = _get_temperatures(input_metadata)
|
temperatures = _get_temperatures(sampling_metadata)
|
||||||
assert len(temperatures) == logits.shape[0]
|
assert len(temperatures) == logits.shape[0]
|
||||||
if any(t != 1.0 for t in temperatures):
|
if any(t != 1.0 for t in temperatures):
|
||||||
t = torch.tensor(temperatures,
|
t = torch.tensor(temperatures,
|
||||||
@@ -72,7 +71,7 @@ class Sampler(nn.Module):
|
|||||||
|
|
||||||
# Apply top-p and top-k truncation.
|
# Apply top-p and top-k truncation.
|
||||||
top_ps, top_ks, min_ps = _get_top_p_top_k_min_p(
|
top_ps, top_ks, min_ps = _get_top_p_top_k_min_p(
|
||||||
input_metadata, self.vocab_size)
|
sampling_metadata, self.vocab_size)
|
||||||
assert len(top_ps) == len(top_ks) == logits.shape[0]
|
assert len(top_ps) == len(top_ks) == logits.shape[0]
|
||||||
do_top_p = any(p < 1.0 - _SAMPLING_EPS for p in top_ps)
|
do_top_p = any(p < 1.0 - _SAMPLING_EPS for p in top_ps)
|
||||||
do_top_k = any(k != self.vocab_size for k in top_ks)
|
do_top_k = any(k != self.vocab_size for k in top_ks)
|
||||||
@@ -91,11 +90,11 @@ class Sampler(nn.Module):
|
|||||||
logprobs = torch.log_softmax(logits, dim=-1, dtype=torch.float)
|
logprobs = torch.log_softmax(logits, dim=-1, dtype=torch.float)
|
||||||
|
|
||||||
# Sample the next tokens.
|
# Sample the next tokens.
|
||||||
sample_results = _sample(probs, logprobs, input_metadata)
|
sample_results = _sample(probs, logprobs, sampling_metadata)
|
||||||
# Get the logprobs query results.
|
# Get the logprobs query results.
|
||||||
prompt_logprobs, sample_logprobs = _get_logprobs(
|
prompt_logprobs, sample_logprobs = _get_logprobs(
|
||||||
logprobs, input_metadata, sample_results)
|
logprobs, sampling_metadata, sample_results)
|
||||||
return _build_sampler_output(sample_results, input_metadata,
|
return _build_sampler_output(sample_results, sampling_metadata,
|
||||||
prompt_logprobs, sample_logprobs)
|
prompt_logprobs, sample_logprobs)
|
||||||
|
|
||||||
|
|
||||||
@@ -114,29 +113,30 @@ def _get_logits(hidden_states: torch.Tensor, embedding: torch.Tensor,
|
|||||||
|
|
||||||
def _prune_hidden_states(
|
def _prune_hidden_states(
|
||||||
hidden_states: torch.Tensor,
|
hidden_states: torch.Tensor,
|
||||||
input_metadata: InputMetadata,
|
sampling_metadata: SamplingMetadata,
|
||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
|
hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
|
||||||
return hidden_states.index_select(0, input_metadata.selected_token_indices)
|
return hidden_states.index_select(0,
|
||||||
|
sampling_metadata.selected_token_indices)
|
||||||
|
|
||||||
|
|
||||||
def _get_penalties(
|
def _get_penalties(
|
||||||
input_metadata: InputMetadata
|
sampling_metadata: SamplingMetadata
|
||||||
) -> Tuple[List[float], List[float], List[float]]:
|
) -> Tuple[List[float], List[float], List[float]]:
|
||||||
# Collect the presence and frequency penalties.
|
# Collect the presence and frequency penalties.
|
||||||
presence_penalties: List[float] = []
|
presence_penalties: List[float] = []
|
||||||
frequency_penalties: List[float] = []
|
frequency_penalties: List[float] = []
|
||||||
repetition_penalties: List[float] = []
|
repetition_penalties: List[float] = []
|
||||||
for i, seq_group in enumerate(input_metadata.seq_groups):
|
for i, seq_group in enumerate(sampling_metadata.seq_groups):
|
||||||
seq_ids, sampling_params = seq_group
|
seq_ids, sampling_params = seq_group
|
||||||
p = sampling_params.presence_penalty
|
p = sampling_params.presence_penalty
|
||||||
f = sampling_params.frequency_penalty
|
f = sampling_params.frequency_penalty
|
||||||
r = sampling_params.repetition_penalty
|
r = sampling_params.repetition_penalty
|
||||||
if (i < input_metadata.num_prompts
|
if (i < sampling_metadata.num_prompts
|
||||||
and sampling_params.prompt_logprobs is not None):
|
and sampling_params.prompt_logprobs is not None):
|
||||||
# NOTE: We do not apply presence and frequency penalties for the
|
# NOTE: We do not apply presence and frequency penalties for the
|
||||||
# prompt token positions where we don't sample new tokens.
|
# prompt token positions where we don't sample new tokens.
|
||||||
prompt_len = input_metadata.prompt_lens[i]
|
prompt_len = sampling_metadata.prompt_lens[i]
|
||||||
presence_penalties += [0] * (prompt_len - 1)
|
presence_penalties += [0] * (prompt_len - 1)
|
||||||
frequency_penalties += [0] * (prompt_len - 1)
|
frequency_penalties += [0] * (prompt_len - 1)
|
||||||
repetition_penalties += [1] * (prompt_len - 1)
|
repetition_penalties += [1] * (prompt_len - 1)
|
||||||
@@ -146,33 +146,66 @@ def _get_penalties(
|
|||||||
return presence_penalties, frequency_penalties, repetition_penalties
|
return presence_penalties, frequency_penalties, repetition_penalties
|
||||||
|
|
||||||
|
|
||||||
def _get_output_tokens(input_metadata: InputMetadata) -> List[List[int]]:
|
def _get_prompt_and_output_tokens(
|
||||||
|
sampling_metadata: SamplingMetadata,
|
||||||
|
) -> Tuple[List[List[int]], List[List[int]]]:
|
||||||
|
prompt_tokens: List[List[int]] = []
|
||||||
output_tokens: List[List[int]] = []
|
output_tokens: List[List[int]] = []
|
||||||
for i, seq_group in enumerate(input_metadata.seq_groups):
|
for i, seq_group in enumerate(sampling_metadata.seq_groups):
|
||||||
seq_ids, sampling_params = seq_group
|
seq_ids, sampling_params = seq_group
|
||||||
if (i < input_metadata.num_prompts
|
if (i < sampling_metadata.num_prompts
|
||||||
and sampling_params.prompt_logprobs is not None):
|
and sampling_params.prompt_logprobs is not None):
|
||||||
# NOTE: prompt token positions do not need output tokens to
|
# NOTE: prompt token positions do not need output tokens to
|
||||||
# compute penalties.
|
# compute penalties.
|
||||||
prompt_len = input_metadata.prompt_lens[i]
|
prompt_len = sampling_metadata.prompt_lens[i]
|
||||||
|
prompt_tokens.extend([] for _ in range(prompt_len - 1))
|
||||||
output_tokens.extend([] for _ in range(prompt_len - 1))
|
output_tokens.extend([] for _ in range(prompt_len - 1))
|
||||||
for seq_id in seq_ids:
|
for seq_id in seq_ids:
|
||||||
seq_data = input_metadata.seq_data[seq_id]
|
seq_data = sampling_metadata.seq_data[seq_id]
|
||||||
|
prompt_tokens.append(seq_data.prompt_token_ids)
|
||||||
output_tokens.append(seq_data.output_token_ids)
|
output_tokens.append(seq_data.output_token_ids)
|
||||||
return output_tokens
|
return prompt_tokens, output_tokens
|
||||||
|
|
||||||
|
|
||||||
def _apply_logits_processors(logits: torch.Tensor,
|
def _get_bin_counts_and_mask(
|
||||||
input_metadata: InputMetadata) -> torch.Tensor:
|
logits: torch.Tensor,
|
||||||
|
tokens: List[List[int]],
|
||||||
|
vocab_size: int,
|
||||||
|
num_seqs: int,
|
||||||
|
) -> Tuple[torch.Tensor, torch.Tensor]:
|
||||||
|
max_len = max(len(tokens) for tokens in tokens)
|
||||||
|
padded_tokens = [
|
||||||
|
tokens + [vocab_size] * (max_len - len(tokens)) for tokens in tokens
|
||||||
|
]
|
||||||
|
tokens_tensor = torch.tensor(padded_tokens,
|
||||||
|
dtype=torch.long,
|
||||||
|
device=logits.device)
|
||||||
|
|
||||||
|
# Compute the bin counts for the tokens.
|
||||||
|
# vocab_size + 1 for padding.
|
||||||
|
bin_counts = torch.zeros((num_seqs, vocab_size + 1),
|
||||||
|
dtype=torch.long,
|
||||||
|
device=logits.device)
|
||||||
|
bin_counts.scatter_add_(1, tokens_tensor, torch.ones_like(tokens_tensor))
|
||||||
|
bin_counts = bin_counts[:, :vocab_size]
|
||||||
|
mask = bin_counts > 0
|
||||||
|
|
||||||
|
return bin_counts, mask
|
||||||
|
|
||||||
|
|
||||||
|
def _apply_logits_processors(
|
||||||
|
logits: torch.Tensor,
|
||||||
|
sampling_metadata: SamplingMetadata,
|
||||||
|
) -> torch.Tensor:
|
||||||
logits_row_idx = 0
|
logits_row_idx = 0
|
||||||
found_logits_processors = False
|
found_logits_processors = False
|
||||||
for seq_ids, sampling_params in input_metadata.seq_groups:
|
for seq_ids, sampling_params in sampling_metadata.seq_groups:
|
||||||
logits_processors = sampling_params.logits_processors
|
logits_processors = sampling_params.logits_processors
|
||||||
if logits_processors:
|
if logits_processors:
|
||||||
found_logits_processors = True
|
found_logits_processors = True
|
||||||
for seq_id in seq_ids:
|
for seq_id in seq_ids:
|
||||||
logits_row = logits[logits_row_idx]
|
logits_row = logits[logits_row_idx]
|
||||||
token_ids = input_metadata.seq_data[seq_id].output_token_ids
|
token_ids = sampling_metadata.seq_data[seq_id].output_token_ids
|
||||||
for logits_processor in logits_processors:
|
for logits_processor in logits_processors:
|
||||||
logits_row = logits_processor(token_ids, logits_row)
|
logits_row = logits_processor(token_ids, logits_row)
|
||||||
logits[logits_row_idx] = logits_row
|
logits[logits_row_idx] = logits_row
|
||||||
@@ -186,15 +219,13 @@ def _apply_logits_processors(logits: torch.Tensor,
|
|||||||
|
|
||||||
def _apply_penalties(
|
def _apply_penalties(
|
||||||
logits: torch.Tensor,
|
logits: torch.Tensor,
|
||||||
output_tokens: List[List[int]],
|
sampling_metadata: SamplingMetadata,
|
||||||
presence_penalties: List[float],
|
presence_penalties: List[float],
|
||||||
frequency_penalties: List[float],
|
frequency_penalties: List[float],
|
||||||
repetition_penalties: List[float],
|
repetition_penalties: List[float],
|
||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
num_seqs, vocab_size = logits.shape
|
num_seqs, vocab_size = logits.shape
|
||||||
for i in range(num_seqs):
|
for i in range(num_seqs):
|
||||||
if not output_tokens[i]:
|
|
||||||
continue
|
|
||||||
p = presence_penalties[i]
|
p = presence_penalties[i]
|
||||||
f = frequency_penalties[i]
|
f = frequency_penalties[i]
|
||||||
r = repetition_penalties[i]
|
r = repetition_penalties[i]
|
||||||
@@ -206,24 +237,15 @@ def _apply_penalties(
|
|||||||
# Return early if all sequences have zero penalties.
|
# Return early if all sequences have zero penalties.
|
||||||
return logits
|
return logits
|
||||||
|
|
||||||
max_output_len = max(len(tokens) for tokens in output_tokens)
|
prompt_tokens, output_tokens = (
|
||||||
padded_output_tokens = [
|
_get_prompt_and_output_tokens(sampling_metadata))
|
||||||
tokens + [vocab_size] * (max_output_len - len(tokens))
|
assert len(prompt_tokens) == logits.shape[0]
|
||||||
for tokens in output_tokens
|
assert len(output_tokens) == logits.shape[0]
|
||||||
]
|
|
||||||
output_tokens_tensor = torch.tensor(padded_output_tokens,
|
|
||||||
dtype=torch.long,
|
|
||||||
device=logits.device)
|
|
||||||
|
|
||||||
# Compute the bin counts for the output tokens.
|
prompt_bin_counts, prompt_mask = _get_bin_counts_and_mask(
|
||||||
# vocab_size + 1 for padding.
|
logits, prompt_tokens, vocab_size, num_seqs)
|
||||||
bin_counts = torch.zeros((num_seqs, vocab_size + 1),
|
output_bin_counts, output_mask = _get_bin_counts_and_mask(
|
||||||
dtype=torch.long,
|
logits, output_tokens, vocab_size, num_seqs)
|
||||||
device=logits.device)
|
|
||||||
bin_counts.scatter_add_(1, output_tokens_tensor,
|
|
||||||
torch.ones_like(output_tokens_tensor))
|
|
||||||
bin_counts = bin_counts[:, :vocab_size] # Remove the padding bin.
|
|
||||||
mask = bin_counts > 0
|
|
||||||
|
|
||||||
repetition_penalties = torch.tensor(repetition_penalties,
|
repetition_penalties = torch.tensor(repetition_penalties,
|
||||||
dtype=logits.dtype,
|
dtype=logits.dtype,
|
||||||
@@ -236,21 +258,21 @@ def _apply_penalties(
|
|||||||
device=logits.device)
|
device=logits.device)
|
||||||
|
|
||||||
repetition_penalties = repetition_penalties[:, None].repeat(1, vocab_size)
|
repetition_penalties = repetition_penalties[:, None].repeat(1, vocab_size)
|
||||||
repetition_penalties[~mask] = 1.0
|
repetition_penalties[~(prompt_mask | output_mask)] = 1.0
|
||||||
logits = torch.where(logits > 0, logits / repetition_penalties,
|
logits = torch.where(logits > 0, logits / repetition_penalties,
|
||||||
logits * repetition_penalties)
|
logits * repetition_penalties)
|
||||||
|
|
||||||
# We follow the definition in OpenAI API.
|
# We follow the definition in OpenAI API.
|
||||||
# Refer to https://platform.openai.com/docs/api-reference/parameter-details
|
# Refer to https://platform.openai.com/docs/api-reference/parameter-details
|
||||||
logits -= frequency_penalties.unsqueeze(dim=1) * bin_counts
|
logits -= frequency_penalties.unsqueeze(dim=1) * output_bin_counts
|
||||||
logits -= presence_penalties.unsqueeze(dim=1) * mask
|
logits -= presence_penalties.unsqueeze(dim=1) * output_mask
|
||||||
return logits
|
return logits
|
||||||
|
|
||||||
|
|
||||||
def _get_temperatures(input_metadata: InputMetadata) -> List[float]:
|
def _get_temperatures(sampling_metadata: SamplingMetadata) -> List[float]:
|
||||||
# Collect the temperatures for the logits.
|
# Collect the temperatures for the logits.
|
||||||
temperatures: List[float] = []
|
temperatures: List[float] = []
|
||||||
for i, seq_group in enumerate(input_metadata.seq_groups):
|
for i, seq_group in enumerate(sampling_metadata.seq_groups):
|
||||||
seq_ids, sampling_params = seq_group
|
seq_ids, sampling_params = seq_group
|
||||||
temperature = sampling_params.temperature
|
temperature = sampling_params.temperature
|
||||||
if temperature < _SAMPLING_EPS:
|
if temperature < _SAMPLING_EPS:
|
||||||
@@ -258,22 +280,22 @@ def _get_temperatures(input_metadata: InputMetadata) -> List[float]:
|
|||||||
# (i.e., greedy sampling or beam search).
|
# (i.e., greedy sampling or beam search).
|
||||||
# Set the temperature to 1 to avoid division by zero.
|
# Set the temperature to 1 to avoid division by zero.
|
||||||
temperature = 1.0
|
temperature = 1.0
|
||||||
if (i < input_metadata.num_prompts
|
if (i < sampling_metadata.num_prompts
|
||||||
and sampling_params.prompt_logprobs is not None):
|
and sampling_params.prompt_logprobs is not None):
|
||||||
prompt_len = input_metadata.prompt_lens[i]
|
prompt_len = sampling_metadata.prompt_lens[i]
|
||||||
temperatures += [temperature] * (prompt_len - 1)
|
temperatures += [temperature] * (prompt_len - 1)
|
||||||
temperatures += [temperature] * len(seq_ids)
|
temperatures += [temperature] * len(seq_ids)
|
||||||
return temperatures
|
return temperatures
|
||||||
|
|
||||||
|
|
||||||
def _get_top_p_top_k_min_p(
|
def _get_top_p_top_k_min_p(
|
||||||
input_metadata: InputMetadata,
|
sampling_metadata: SamplingMetadata,
|
||||||
vocab_size: int,
|
vocab_size: int,
|
||||||
) -> Tuple[List[float], List[int], List[float]]:
|
) -> Tuple[List[float], List[int], List[float]]:
|
||||||
top_ps: List[float] = []
|
top_ps: List[float] = []
|
||||||
top_ks: List[int] = []
|
top_ks: List[int] = []
|
||||||
min_ps: List[float] = []
|
min_ps: List[float] = []
|
||||||
for i, seq_group in enumerate(input_metadata.seq_groups):
|
for i, seq_group in enumerate(sampling_metadata.seq_groups):
|
||||||
seq_ids, sampling_params = seq_group
|
seq_ids, sampling_params = seq_group
|
||||||
top_p = sampling_params.top_p
|
top_p = sampling_params.top_p
|
||||||
min_p = sampling_params.min_p
|
min_p = sampling_params.min_p
|
||||||
@@ -281,9 +303,9 @@ def _get_top_p_top_k_min_p(
|
|||||||
top_k = min(sampling_params.top_k, vocab_size)
|
top_k = min(sampling_params.top_k, vocab_size)
|
||||||
# k=-1 means no truncation.
|
# k=-1 means no truncation.
|
||||||
top_k = vocab_size if top_k == -1 else top_k
|
top_k = vocab_size if top_k == -1 else top_k
|
||||||
if (i < input_metadata.num_prompts
|
if (i < sampling_metadata.num_prompts
|
||||||
and sampling_params.prompt_logprobs is not None):
|
and sampling_params.prompt_logprobs is not None):
|
||||||
prompt_len = input_metadata.prompt_lens[i]
|
prompt_len = sampling_metadata.prompt_lens[i]
|
||||||
top_ps += [top_p] * (prompt_len - 1)
|
top_ps += [top_p] * (prompt_len - 1)
|
||||||
top_ks += [top_k] * (prompt_len - 1)
|
top_ks += [top_k] * (prompt_len - 1)
|
||||||
min_ps += [min_p] * (prompt_len - 1)
|
min_ps += [min_p] * (prompt_len - 1)
|
||||||
@@ -453,11 +475,11 @@ def _beam_search_sample(
|
|||||||
def _sample(
|
def _sample(
|
||||||
probs: torch.Tensor,
|
probs: torch.Tensor,
|
||||||
logprobs: torch.Tensor,
|
logprobs: torch.Tensor,
|
||||||
input_metadata: InputMetadata,
|
sampling_metadata: SamplingMetadata,
|
||||||
) -> List[Tuple[List[int], List[int]]]:
|
) -> List[Tuple[List[int], List[int]]]:
|
||||||
categorized_seq_group_ids = {t: [] for t in SamplingType}
|
categorized_seq_group_ids = {t: [] for t in SamplingType}
|
||||||
categorized_sample_indices = input_metadata.categorized_sample_indices
|
categorized_sample_indices = sampling_metadata.categorized_sample_indices
|
||||||
for i, seq_group in enumerate(input_metadata.seq_groups):
|
for i, seq_group in enumerate(sampling_metadata.seq_groups):
|
||||||
_, sampling_params = seq_group
|
_, sampling_params = seq_group
|
||||||
sampling_type = sampling_params.sampling_type
|
sampling_type = sampling_params.sampling_type
|
||||||
categorized_seq_group_ids[sampling_type].append(i)
|
categorized_seq_group_ids[sampling_type].append(i)
|
||||||
@@ -465,8 +487,8 @@ def _sample(
|
|||||||
sample_results_dict: Dict[int, Tuple[List[int], List[int]]] = {}
|
sample_results_dict: Dict[int, Tuple[List[int], List[int]]] = {}
|
||||||
for sampling_type in SamplingType:
|
for sampling_type in SamplingType:
|
||||||
seq_group_ids = categorized_seq_group_ids[sampling_type]
|
seq_group_ids = categorized_seq_group_ids[sampling_type]
|
||||||
seq_groups = [input_metadata.seq_groups[i] for i in seq_group_ids]
|
seq_groups = [sampling_metadata.seq_groups[i] for i in seq_group_ids]
|
||||||
is_prompts = [i < input_metadata.num_prompts for i in seq_group_ids]
|
is_prompts = [i < sampling_metadata.num_prompts for i in seq_group_ids]
|
||||||
sample_indices = categorized_sample_indices[sampling_type]
|
sample_indices = categorized_sample_indices[sampling_type]
|
||||||
num_tokens = len(sample_indices)
|
num_tokens = len(sample_indices)
|
||||||
if num_tokens == 0:
|
if num_tokens == 0:
|
||||||
@@ -481,21 +503,22 @@ def _sample(
|
|||||||
elif sampling_type == SamplingType.BEAM:
|
elif sampling_type == SamplingType.BEAM:
|
||||||
category_logprobs = logprobs[sample_indices]
|
category_logprobs = logprobs[sample_indices]
|
||||||
sample_results = _beam_search_sample(seq_groups, is_prompts,
|
sample_results = _beam_search_sample(seq_groups, is_prompts,
|
||||||
input_metadata.seq_data,
|
sampling_metadata.seq_data,
|
||||||
category_logprobs)
|
category_logprobs)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unsupported sampling type: {sampling_type}")
|
raise ValueError(f"Unsupported sampling type: {sampling_type}")
|
||||||
sample_results_dict.update(zip(seq_group_ids, sample_results))
|
sample_results_dict.update(zip(seq_group_ids, sample_results))
|
||||||
|
|
||||||
sample_results = [
|
sample_results = [
|
||||||
sample_results_dict[i] for i in range(len(input_metadata.seq_groups))
|
sample_results_dict[i]
|
||||||
|
for i in range(len(sampling_metadata.seq_groups))
|
||||||
]
|
]
|
||||||
return sample_results
|
return sample_results
|
||||||
|
|
||||||
|
|
||||||
def _get_logprobs(
|
def _get_logprobs(
|
||||||
logprobs: torch.Tensor,
|
logprobs: torch.Tensor,
|
||||||
input_metadata: InputMetadata,
|
sampling_metadata: SamplingMetadata,
|
||||||
sample_results: List[Tuple[List[int], List[int]]],
|
sample_results: List[Tuple[List[int], List[int]]],
|
||||||
) -> Tuple[List[Optional[List[Optional[Dict[int, float]]]]], List[List[Dict[
|
) -> Tuple[List[Optional[List[Optional[Dict[int, float]]]]], List[List[Dict[
|
||||||
int, float]]]]:
|
int, float]]]]:
|
||||||
@@ -505,16 +528,16 @@ def _get_logprobs(
|
|||||||
largest_num_logprobs = 0
|
largest_num_logprobs = 0
|
||||||
sample_idx = 0
|
sample_idx = 0
|
||||||
for i, (seq_group, sample_result) in enumerate(
|
for i, (seq_group, sample_result) in enumerate(
|
||||||
zip(input_metadata.seq_groups, sample_results)):
|
zip(sampling_metadata.seq_groups, sample_results)):
|
||||||
seq_ids, sampling_params = seq_group
|
seq_ids, sampling_params = seq_group
|
||||||
next_token_ids, parent_ids = sample_result
|
next_token_ids, parent_ids = sample_result
|
||||||
num_parent_seqs = len(seq_ids)
|
num_parent_seqs = len(seq_ids)
|
||||||
if (i < input_metadata.num_prompts
|
if (i < sampling_metadata.num_prompts
|
||||||
and sampling_params.prompt_logprobs is not None):
|
and sampling_params.prompt_logprobs is not None):
|
||||||
largest_num_logprobs = max(largest_num_logprobs,
|
largest_num_logprobs = max(largest_num_logprobs,
|
||||||
sampling_params.prompt_logprobs)
|
sampling_params.prompt_logprobs)
|
||||||
prompt_len = input_metadata.prompt_lens[i]
|
prompt_len = sampling_metadata.prompt_lens[i]
|
||||||
prompt_tokens = input_metadata.seq_data[
|
prompt_tokens = sampling_metadata.seq_data[
|
||||||
seq_ids[0]].prompt_token_ids
|
seq_ids[0]].prompt_token_ids
|
||||||
batched_logprobs_query_seq_indices.extend(
|
batched_logprobs_query_seq_indices.extend(
|
||||||
sample_idx + j for j in range(prompt_len - 1))
|
sample_idx + j for j in range(prompt_len - 1))
|
||||||
@@ -552,16 +575,16 @@ def _get_logprobs(
|
|||||||
sample_idx = 0
|
sample_idx = 0
|
||||||
query_result_idx = 0
|
query_result_idx = 0
|
||||||
for i, (seq_group, sample_result) in enumerate(
|
for i, (seq_group, sample_result) in enumerate(
|
||||||
zip(input_metadata.seq_groups, sample_results)):
|
zip(sampling_metadata.seq_groups, sample_results)):
|
||||||
seq_ids, sampling_params = seq_group
|
seq_ids, sampling_params = seq_group
|
||||||
next_token_ids, parent_ids = sample_result
|
next_token_ids, parent_ids = sample_result
|
||||||
|
|
||||||
# Prompt logprobs
|
# Prompt logprobs
|
||||||
if (i < input_metadata.num_prompts
|
if (i < sampling_metadata.num_prompts
|
||||||
and sampling_params.prompt_logprobs is not None):
|
and sampling_params.prompt_logprobs is not None):
|
||||||
num_logprobs = sampling_params.prompt_logprobs
|
num_logprobs = sampling_params.prompt_logprobs
|
||||||
prompt_len = input_metadata.prompt_lens[i]
|
prompt_len = sampling_metadata.prompt_lens[i]
|
||||||
prompt_tokens = input_metadata.seq_data[
|
prompt_tokens = sampling_metadata.seq_data[
|
||||||
seq_ids[0]].prompt_token_ids
|
seq_ids[0]].prompt_token_ids
|
||||||
group_prompt_logprobs: PromptLogprobs = [None]
|
group_prompt_logprobs: PromptLogprobs = [None]
|
||||||
for token_id in prompt_tokens[1:]:
|
for token_id in prompt_tokens[1:]:
|
||||||
@@ -607,13 +630,13 @@ def _get_logprobs(
|
|||||||
|
|
||||||
def _build_sampler_output(
|
def _build_sampler_output(
|
||||||
sample_results: List[Tuple[List[int], List[int]]],
|
sample_results: List[Tuple[List[int], List[int]]],
|
||||||
input_metadata: InputMetadata,
|
sampling_metadata: SamplingMetadata,
|
||||||
prompt_logprobs: List[Optional[PromptLogprobs]],
|
prompt_logprobs: List[Optional[PromptLogprobs]],
|
||||||
sample_logprobs: List[SampleLogprobs],
|
sample_logprobs: List[SampleLogprobs],
|
||||||
) -> SamplerOutput:
|
) -> SamplerOutput:
|
||||||
sampler_output = []
|
sampler_output = []
|
||||||
for (seq_group, sample_result, group_prompt_logprobs,
|
for (seq_group, sample_result, group_prompt_logprobs,
|
||||||
group_sample_logprobs) in zip(input_metadata.seq_groups,
|
group_sample_logprobs) in zip(sampling_metadata.seq_groups,
|
||||||
sample_results, prompt_logprobs,
|
sample_results, prompt_logprobs,
|
||||||
sample_logprobs):
|
sample_logprobs):
|
||||||
seq_ids, _ = seq_group
|
seq_ids, _ = seq_group
|
||||||
@@ -623,7 +646,7 @@ def _build_sampler_output(
|
|||||||
next_token_ids,
|
next_token_ids,
|
||||||
group_sample_logprobs):
|
group_sample_logprobs):
|
||||||
seq_outputs.append(
|
seq_outputs.append(
|
||||||
SequenceOutputs(seq_ids[parent_id], next_token_id, logprobs))
|
SequenceOutput(seq_ids[parent_id], next_token_id, logprobs))
|
||||||
sampler_output.append(
|
sampler_output.append(
|
||||||
SequenceGroupOutputs(seq_outputs, group_prompt_logprobs))
|
SequenceGroupOutput(seq_outputs, group_prompt_logprobs))
|
||||||
return sampler_output
|
return sampler_output
|
||||||
|
|||||||
@@ -7,9 +7,13 @@ import torch.nn as nn
|
|||||||
from transformers import PretrainedConfig
|
from transformers import PretrainedConfig
|
||||||
|
|
||||||
from vllm.config import ModelConfig
|
from vllm.config import ModelConfig
|
||||||
from vllm.model_executor.models import * # pylint: disable=wildcard-import
|
from vllm.model_executor.models import *
|
||||||
from vllm.model_executor.weight_utils import (get_quant_config,
|
from vllm.model_executor.weight_utils import (get_quant_config,
|
||||||
initialize_dummy_weights)
|
initialize_dummy_weights)
|
||||||
|
from vllm.utils import is_hip
|
||||||
|
from vllm.logger import init_logger
|
||||||
|
|
||||||
|
logger = init_logger(__name__)
|
||||||
|
|
||||||
# TODO(woosuk): Lazy-load the model classes.
|
# TODO(woosuk): Lazy-load the model classes.
|
||||||
_MODEL_REGISTRY = {
|
_MODEL_REGISTRY = {
|
||||||
@@ -19,6 +23,7 @@ _MODEL_REGISTRY = {
|
|||||||
"BaichuanForCausalLM": BaichuanForCausalLM, # baichuan-13b
|
"BaichuanForCausalLM": BaichuanForCausalLM, # baichuan-13b
|
||||||
"BloomForCausalLM": BloomForCausalLM,
|
"BloomForCausalLM": BloomForCausalLM,
|
||||||
"ChatGLMModel": ChatGLMForCausalLM,
|
"ChatGLMModel": ChatGLMForCausalLM,
|
||||||
|
"ChatGLMForConditionalGeneration": ChatGLMForCausalLM,
|
||||||
"FalconForCausalLM": FalconForCausalLM,
|
"FalconForCausalLM": FalconForCausalLM,
|
||||||
"GPT2LMHeadModel": GPT2LMHeadModel,
|
"GPT2LMHeadModel": GPT2LMHeadModel,
|
||||||
"GPTBigCodeForCausalLM": GPTBigCodeForCausalLM,
|
"GPTBigCodeForCausalLM": GPTBigCodeForCausalLM,
|
||||||
@@ -28,6 +33,7 @@ _MODEL_REGISTRY = {
|
|||||||
"LlamaForCausalLM": LlamaForCausalLM,
|
"LlamaForCausalLM": LlamaForCausalLM,
|
||||||
"LLaMAForCausalLM": LlamaForCausalLM, # For decapoda-research/llama-*
|
"LLaMAForCausalLM": LlamaForCausalLM, # For decapoda-research/llama-*
|
||||||
"MistralForCausalLM": MistralForCausalLM,
|
"MistralForCausalLM": MistralForCausalLM,
|
||||||
|
"MixtralForCausalLM": MixtralForCausalLM,
|
||||||
# transformers's mpt class has lower case
|
# transformers's mpt class has lower case
|
||||||
"MptForCausalLM": MPTForCausalLM,
|
"MptForCausalLM": MPTForCausalLM,
|
||||||
"MPTForCausalLM": MPTForCausalLM,
|
"MPTForCausalLM": MPTForCausalLM,
|
||||||
@@ -38,6 +44,18 @@ _MODEL_REGISTRY = {
|
|||||||
"YiForCausalLM": YiForCausalLM,
|
"YiForCausalLM": YiForCausalLM,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Models to be disabled in ROCm
|
||||||
|
_ROCM_UNSUPPORTED_MODELS = []
|
||||||
|
if is_hip():
|
||||||
|
for rocm_model in _ROCM_UNSUPPORTED_MODELS:
|
||||||
|
del _MODEL_REGISTRY[rocm_model]
|
||||||
|
|
||||||
|
# Models partially supported in ROCm
|
||||||
|
_ROCM_PARTIALLY_SUPPORTED_MODELS = {
|
||||||
|
"MistralForCausalLM":
|
||||||
|
"Sliding window attention is not supported in ROCm's flash attention",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def _set_default_torch_dtype(dtype: torch.dtype):
|
def _set_default_torch_dtype(dtype: torch.dtype):
|
||||||
@@ -52,7 +70,15 @@ def _get_model_architecture(config: PretrainedConfig) -> Type[nn.Module]:
|
|||||||
architectures = getattr(config, "architectures", [])
|
architectures = getattr(config, "architectures", [])
|
||||||
for arch in architectures:
|
for arch in architectures:
|
||||||
if arch in _MODEL_REGISTRY:
|
if arch in _MODEL_REGISTRY:
|
||||||
|
if is_hip() and arch in _ROCM_PARTIALLY_SUPPORTED_MODELS:
|
||||||
|
logger.warning(
|
||||||
|
f"{arch} is not fully supported in ROCm. Reason: "
|
||||||
|
f"{_ROCM_PARTIALLY_SUPPORTED_MODELS[arch]}")
|
||||||
return _MODEL_REGISTRY[arch]
|
return _MODEL_REGISTRY[arch]
|
||||||
|
elif arch in _ROCM_UNSUPPORTED_MODELS:
|
||||||
|
raise ValueError(
|
||||||
|
f"Model architecture {arch} is not supported by ROCm for now. \n"
|
||||||
|
f"Supported architectures {list(_MODEL_REGISTRY.keys())}")
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Model architectures {architectures} are not supported for now. "
|
f"Model architectures {architectures} are not supported for now. "
|
||||||
f"Supported architectures: {list(_MODEL_REGISTRY.keys())}")
|
f"Supported architectures: {list(_MODEL_REGISTRY.keys())}")
|
||||||
@@ -87,9 +113,9 @@ def get_model(model_config: ModelConfig) -> nn.Module:
|
|||||||
with _set_default_torch_dtype(model_config.dtype):
|
with _set_default_torch_dtype(model_config.dtype):
|
||||||
# Create a model instance.
|
# Create a model instance.
|
||||||
# The weights will be initialized as empty tensors.
|
# The weights will be initialized as empty tensors.
|
||||||
model = model_class(model_config.hf_config, linear_method)
|
with torch.device("cuda"):
|
||||||
|
model = model_class(model_config.hf_config, linear_method)
|
||||||
if model_config.load_format == "dummy":
|
if model_config.load_format == "dummy":
|
||||||
model = model.cuda()
|
|
||||||
# NOTE(woosuk): For accurate performance evaluation, we assign
|
# NOTE(woosuk): For accurate performance evaluation, we assign
|
||||||
# random values to the weights.
|
# random values to the weights.
|
||||||
initialize_dummy_weights(model)
|
initialize_dummy_weights(model)
|
||||||
@@ -97,5 +123,4 @@ def get_model(model_config: ModelConfig) -> nn.Module:
|
|||||||
# Load the weights from the cached or downloaded files.
|
# Load the weights from the cached or downloaded files.
|
||||||
model.load_weights(model_config.model, model_config.download_dir,
|
model.load_weights(model_config.model, model_config.download_dir,
|
||||||
model_config.load_format, model_config.revision)
|
model_config.load_format, model_config.revision)
|
||||||
model = model.cuda()
|
|
||||||
return model.eval()
|
return model.eval()
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from vllm.model_executor.models.gpt_neox import GPTNeoXForCausalLM
|
|||||||
from vllm.model_executor.models.internlm import InternLMForCausalLM
|
from vllm.model_executor.models.internlm import InternLMForCausalLM
|
||||||
from vllm.model_executor.models.llama import LlamaForCausalLM
|
from vllm.model_executor.models.llama import LlamaForCausalLM
|
||||||
from vllm.model_executor.models.mistral import MistralForCausalLM
|
from vllm.model_executor.models.mistral import MistralForCausalLM
|
||||||
|
from vllm.model_executor.models.mixtral import MixtralForCausalLM
|
||||||
from vllm.model_executor.models.mpt import MPTForCausalLM
|
from vllm.model_executor.models.mpt import MPTForCausalLM
|
||||||
from vllm.model_executor.models.opt import OPTForCausalLM
|
from vllm.model_executor.models.opt import OPTForCausalLM
|
||||||
from vllm.model_executor.models.phi_1_5 import PhiForCausalLM
|
from vllm.model_executor.models.phi_1_5 import PhiForCausalLM
|
||||||
@@ -35,5 +36,6 @@ __all__ = [
|
|||||||
"PhiForCausalLM",
|
"PhiForCausalLM",
|
||||||
"QWenLMHeadModel",
|
"QWenLMHeadModel",
|
||||||
"MistralForCausalLM",
|
"MistralForCausalLM",
|
||||||
|
"MixtralForCausalLM",
|
||||||
"YiForCausalLM",
|
"YiForCausalLM",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -20,11 +20,7 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
"""Inference-only LLaMA model compatible with HuggingFace weights.
|
"""Inference-only LLaMA model compatible with HuggingFace weights."""
|
||||||
|
|
||||||
The input of the model is flattened to a 1D tensor of tokens. The model uses
|
|
||||||
InputMetadata to extract the original 2D shape of the input.
|
|
||||||
"""
|
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
@@ -32,16 +28,18 @@ from torch import nn
|
|||||||
|
|
||||||
from vllm.model_executor.input_metadata import InputMetadata
|
from vllm.model_executor.input_metadata import InputMetadata
|
||||||
from vllm.model_executor.layers.activation import SiluAndMul
|
from vllm.model_executor.layers.activation import SiluAndMul
|
||||||
from vllm.model_executor.layers.attention import PagedAttentionWithRoPE
|
from vllm.model_executor.layers.attention import PagedAttention
|
||||||
from vllm.model_executor.layers.linear import (LinearMethodBase,
|
from vllm.model_executor.layers.linear import (LinearMethodBase,
|
||||||
MergedColumnParallelLinear,
|
MergedColumnParallelLinear,
|
||||||
QKVParallelLinear,
|
QKVParallelLinear,
|
||||||
RowParallelLinear)
|
RowParallelLinear)
|
||||||
|
from vllm.model_executor.layers.rotary_embedding import get_rope
|
||||||
from vllm.model_executor.layers.sampler import Sampler
|
from vllm.model_executor.layers.sampler import Sampler
|
||||||
from vllm.model_executor.layers.vocab_parallel_embedding import (
|
from vllm.model_executor.layers.vocab_parallel_embedding import (
|
||||||
VocabParallelEmbedding, ParallelLMHead)
|
VocabParallelEmbedding, ParallelLMHead)
|
||||||
from vllm.model_executor.parallel_utils.parallel_state import (
|
from vllm.model_executor.parallel_utils.parallel_state import (
|
||||||
get_tensor_model_parallel_world_size)
|
get_tensor_model_parallel_world_size)
|
||||||
|
from vllm.model_executor.sampling_metadata import SamplingMetadata
|
||||||
from vllm.model_executor.weight_utils import (default_weight_loader,
|
from vllm.model_executor.weight_utils import (default_weight_loader,
|
||||||
hf_model_weights_iterator)
|
hf_model_weights_iterator)
|
||||||
from vllm.sequence import SamplerOutput
|
from vllm.sequence import SamplerOutput
|
||||||
@@ -142,15 +140,17 @@ class AquilaAttention(nn.Module):
|
|||||||
bias=False,
|
bias=False,
|
||||||
linear_method=linear_method,
|
linear_method=linear_method,
|
||||||
)
|
)
|
||||||
self.attn = PagedAttentionWithRoPE(
|
self.rotary_emb = get_rope(
|
||||||
self.num_heads,
|
|
||||||
self.head_dim,
|
self.head_dim,
|
||||||
self.scaling,
|
|
||||||
base=self.rope_theta,
|
|
||||||
max_position=self.max_position_embeddings,
|
|
||||||
rotary_dim=self.head_dim,
|
rotary_dim=self.head_dim,
|
||||||
num_kv_heads=self.num_kv_heads,
|
max_position=self.max_position_embeddings,
|
||||||
rope_scaling=rope_scaling)
|
base=self.rope_theta,
|
||||||
|
rope_scaling=rope_scaling,
|
||||||
|
)
|
||||||
|
self.attn = PagedAttention(self.num_heads,
|
||||||
|
self.head_dim,
|
||||||
|
self.scaling,
|
||||||
|
num_kv_heads=self.num_kv_heads)
|
||||||
|
|
||||||
def forward(
|
def forward(
|
||||||
self,
|
self,
|
||||||
@@ -162,9 +162,10 @@ class AquilaAttention(nn.Module):
|
|||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
qkv, _ = self.qkv_proj(hidden_states)
|
qkv, _ = self.qkv_proj(hidden_states)
|
||||||
q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
|
q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
|
||||||
|
q, k = self.rotary_emb(positions, q, k)
|
||||||
k_cache, v_cache = kv_cache
|
k_cache, v_cache = kv_cache
|
||||||
attn_output = self.attn(positions, q, k, v, k_cache, v_cache,
|
attn_output = self.attn(q, k, v, k_cache, v_cache, input_metadata,
|
||||||
input_metadata, cache_event)
|
cache_event)
|
||||||
output, _ = self.o_proj(attn_output)
|
output, _ = self.o_proj(attn_output)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
@@ -261,10 +262,7 @@ class AquilaModel(nn.Module):
|
|||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
hidden_states = self.embed_tokens(input_ids)
|
hidden_states = self.embed_tokens(input_ids)
|
||||||
for i in range(len(self.layers)):
|
for i in range(len(self.layers)):
|
||||||
if cache_events is None:
|
cache_event = None if cache_events is None else cache_events[i]
|
||||||
cache_event = None
|
|
||||||
else:
|
|
||||||
cache_event = cache_events[i]
|
|
||||||
layer = self.layers[i]
|
layer = self.layers[i]
|
||||||
hidden_states = layer(
|
hidden_states = layer(
|
||||||
positions,
|
positions,
|
||||||
@@ -299,11 +297,18 @@ class AquilaForCausalLM(nn.Module):
|
|||||||
kv_caches: List[KVCache],
|
kv_caches: List[KVCache],
|
||||||
input_metadata: InputMetadata,
|
input_metadata: InputMetadata,
|
||||||
cache_events: Optional[List[torch.cuda.Event]],
|
cache_events: Optional[List[torch.cuda.Event]],
|
||||||
) -> SamplerOutput:
|
) -> torch.Tensor:
|
||||||
hidden_states = self.model(input_ids, positions, kv_caches,
|
hidden_states = self.model(input_ids, positions, kv_caches,
|
||||||
input_metadata, cache_events)
|
input_metadata, cache_events)
|
||||||
|
return hidden_states
|
||||||
|
|
||||||
|
def sample(
|
||||||
|
self,
|
||||||
|
hidden_states: torch.Tensor,
|
||||||
|
sampling_metadata: SamplingMetadata,
|
||||||
|
) -> SamplerOutput:
|
||||||
next_tokens = self.sampler(self.lm_head.weight, hidden_states,
|
next_tokens = self.sampler(self.lm_head.weight, hidden_states,
|
||||||
input_metadata)
|
sampling_metadata)
|
||||||
return next_tokens
|
return next_tokens
|
||||||
|
|
||||||
def load_weights(self,
|
def load_weights(self,
|
||||||
|
|||||||
@@ -17,11 +17,7 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
"""Inference-only BaiChuan model compatible with HuggingFace weights.
|
"""Inference-only BaiChuan model compatible with HuggingFace weights."""
|
||||||
|
|
||||||
The input of the model is flattened to a 1D tensor of tokens. The model uses
|
|
||||||
InputMetadata to extract the original 2D shape of the input.
|
|
||||||
"""
|
|
||||||
import math
|
import math
|
||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
@@ -30,18 +26,19 @@ from torch import nn
|
|||||||
|
|
||||||
from vllm.model_executor.input_metadata import InputMetadata
|
from vllm.model_executor.input_metadata import InputMetadata
|
||||||
from vllm.model_executor.layers.activation import SiluAndMul
|
from vllm.model_executor.layers.activation import SiluAndMul
|
||||||
from vllm.model_executor.layers.attention import (PagedAttentionWithRoPE,
|
from vllm.model_executor.layers.attention import PagedAttention
|
||||||
PagedAttentionWithALiBi)
|
|
||||||
from vllm.model_executor.layers.layernorm import RMSNorm
|
from vllm.model_executor.layers.layernorm import RMSNorm
|
||||||
from vllm.model_executor.layers.linear import (LinearMethodBase,
|
from vllm.model_executor.layers.linear import (LinearMethodBase,
|
||||||
MergedColumnParallelLinear,
|
MergedColumnParallelLinear,
|
||||||
QKVParallelLinear,
|
QKVParallelLinear,
|
||||||
RowParallelLinear)
|
RowParallelLinear)
|
||||||
|
from vllm.model_executor.layers.rotary_embedding import get_rope
|
||||||
from vllm.model_executor.layers.sampler import Sampler
|
from vllm.model_executor.layers.sampler import Sampler
|
||||||
from vllm.model_executor.layers.vocab_parallel_embedding import (
|
from vllm.model_executor.layers.vocab_parallel_embedding import (
|
||||||
VocabParallelEmbedding, ParallelLMHead)
|
VocabParallelEmbedding, ParallelLMHead)
|
||||||
from vllm.model_executor.parallel_utils.parallel_state import (
|
from vllm.model_executor.parallel_utils.parallel_state import (
|
||||||
get_tensor_model_parallel_rank, get_tensor_model_parallel_world_size)
|
get_tensor_model_parallel_rank, get_tensor_model_parallel_world_size)
|
||||||
|
from vllm.model_executor.sampling_metadata import SamplingMetadata
|
||||||
from vllm.model_executor.weight_utils import (default_weight_loader,
|
from vllm.model_executor.weight_utils import (default_weight_loader,
|
||||||
hf_model_weights_iterator)
|
hf_model_weights_iterator)
|
||||||
from vllm.sequence import SamplerOutput
|
from vllm.sequence import SamplerOutput
|
||||||
@@ -154,17 +151,20 @@ class BaiChuanAttention(nn.Module):
|
|||||||
alibi_slopes = alibi_slopes[head_start:head_end].tolist()
|
alibi_slopes = alibi_slopes[head_start:head_end].tolist()
|
||||||
|
|
||||||
scaling = self.head_dim**-0.5
|
scaling = self.head_dim**-0.5
|
||||||
self.attn = PagedAttentionWithALiBi(self.num_heads, self.head_dim,
|
self.attn = PagedAttention(self.num_heads,
|
||||||
scaling, alibi_slopes)
|
self.head_dim,
|
||||||
|
scaling,
|
||||||
|
alibi_slopes=alibi_slopes)
|
||||||
else:
|
else:
|
||||||
self.scaling = self.head_dim**-0.5
|
self.rotary_emb = get_rope(
|
||||||
self.attn = PagedAttentionWithRoPE(
|
|
||||||
self.num_heads,
|
|
||||||
self.head_dim,
|
self.head_dim,
|
||||||
self.scaling,
|
|
||||||
rotary_dim=self.head_dim,
|
rotary_dim=self.head_dim,
|
||||||
|
max_position=self.max_position_embeddings,
|
||||||
base=self.rope_theta,
|
base=self.rope_theta,
|
||||||
max_position=self.max_position_embeddings)
|
)
|
||||||
|
self.scaling = self.head_dim**-0.5
|
||||||
|
self.attn = PagedAttention(self.num_heads, self.head_dim,
|
||||||
|
self.scaling)
|
||||||
|
|
||||||
def forward(
|
def forward(
|
||||||
self,
|
self,
|
||||||
@@ -176,14 +176,11 @@ class BaiChuanAttention(nn.Module):
|
|||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
qkv, _ = self.W_pack(hidden_states)
|
qkv, _ = self.W_pack(hidden_states)
|
||||||
q, k, v = qkv.chunk(chunks=3, dim=-1)
|
q, k, v = qkv.chunk(chunks=3, dim=-1)
|
||||||
|
if self.postion_embedding != "ALIBI":
|
||||||
|
q, k = self.rotary_emb(positions, q, k)
|
||||||
k_cache, v_cache = kv_cache
|
k_cache, v_cache = kv_cache
|
||||||
if self.postion_embedding == "ALIBI":
|
attn_output = self.attn(q, k, v, k_cache, v_cache, input_metadata,
|
||||||
attn_output = self.attn(q, k, v, k_cache, v_cache, input_metadata,
|
cache_event)
|
||||||
cache_event)
|
|
||||||
else:
|
|
||||||
attn_output = self.attn(positions, q, k, v, k_cache, v_cache,
|
|
||||||
input_metadata, cache_event)
|
|
||||||
|
|
||||||
output, _ = self.o_proj(attn_output)
|
output, _ = self.o_proj(attn_output)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
@@ -281,10 +278,7 @@ class BaiChuanModel(nn.Module):
|
|||||||
hidden_states = self.embed_tokens(input_ids)
|
hidden_states = self.embed_tokens(input_ids)
|
||||||
residual = None
|
residual = None
|
||||||
for i in range(len(self.layers)):
|
for i in range(len(self.layers)):
|
||||||
if cache_events is None:
|
cache_event = None if cache_events is None else cache_events[i]
|
||||||
cache_event = None
|
|
||||||
else:
|
|
||||||
cache_event = cache_events[i]
|
|
||||||
layer = self.layers[i]
|
layer = self.layers[i]
|
||||||
hidden_states, residual = layer(
|
hidden_states, residual = layer(
|
||||||
positions,
|
positions,
|
||||||
@@ -318,11 +312,18 @@ class BaiChuanBaseForCausalLM(nn.Module):
|
|||||||
kv_caches: List[KVCache],
|
kv_caches: List[KVCache],
|
||||||
input_metadata: InputMetadata,
|
input_metadata: InputMetadata,
|
||||||
cache_events: Optional[List[torch.cuda.Event]],
|
cache_events: Optional[List[torch.cuda.Event]],
|
||||||
) -> SamplerOutput:
|
) -> torch.Tensor:
|
||||||
hidden_states = self.model(input_ids, positions, kv_caches,
|
hidden_states = self.model(input_ids, positions, kv_caches,
|
||||||
input_metadata, cache_events)
|
input_metadata, cache_events)
|
||||||
|
return hidden_states
|
||||||
|
|
||||||
|
def sample(
|
||||||
|
self,
|
||||||
|
hidden_states: torch.Tensor,
|
||||||
|
sampling_metadata: SamplingMetadata,
|
||||||
|
) -> SamplerOutput:
|
||||||
next_tokens = self.sampler(self.lm_head.weight, hidden_states,
|
next_tokens = self.sampler(self.lm_head.weight, hidden_states,
|
||||||
input_metadata)
|
sampling_metadata)
|
||||||
return next_tokens
|
return next_tokens
|
||||||
|
|
||||||
def load_weights(self,
|
def load_weights(self,
|
||||||
@@ -340,6 +341,17 @@ class BaiChuanBaseForCausalLM(nn.Module):
|
|||||||
model_name_or_path, cache_dir, load_format, revision):
|
model_name_or_path, cache_dir, load_format, revision):
|
||||||
if "rotary_emb.inv_freq" in name:
|
if "rotary_emb.inv_freq" in name:
|
||||||
continue
|
continue
|
||||||
|
if name == "lm_head.weight":
|
||||||
|
# Unlike Baichuan, Baichuan2 normalizes the head weights. Refer to:
|
||||||
|
# https://huggingface.co/baichuan-inc/Baichuan2-7B-Chat/blob/84603cde5ebffb6084e476cfaeceaf0b8b91fe54/modeling_baichuan.py#L508
|
||||||
|
# Distinguish between Baichuan and Baichuan2 by checking the
|
||||||
|
# vocab size. This is suggested by
|
||||||
|
# https://github.com/vllm-project/vllm/pull/1022#discussion_r1325652704
|
||||||
|
is_baichuan2 = self.config.vocab_size == 125696
|
||||||
|
if is_baichuan2:
|
||||||
|
loaded_weight = torch.nn.functional.normalize(
|
||||||
|
loaded_weight)
|
||||||
|
|
||||||
for (param_name, weight_name, shard_id) in stacked_params_mapping:
|
for (param_name, weight_name, shard_id) in stacked_params_mapping:
|
||||||
if weight_name not in name:
|
if weight_name not in name:
|
||||||
continue
|
continue
|
||||||
@@ -354,15 +366,20 @@ class BaiChuanBaseForCausalLM(nn.Module):
|
|||||||
weight_loader(param, loaded_weight)
|
weight_loader(param, loaded_weight)
|
||||||
|
|
||||||
|
|
||||||
class BaichuanForCausalLM(BaiChuanBaseForCausalLM): # baichuan 13b
|
class BaichuanForCausalLM(BaiChuanBaseForCausalLM):
|
||||||
|
"""Baichuan 13B and Baichuan2 7B/13B."""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
config,
|
config,
|
||||||
linear_method: Optional[LinearMethodBase] = None):
|
linear_method: Optional[LinearMethodBase] = None):
|
||||||
super().__init__(config, "ALIBI", linear_method)
|
if config.hidden_size == 4096: # baichuan2 7b
|
||||||
|
super().__init__(config, "ROPE", linear_method)
|
||||||
|
else: # baichuan 13b, baichuan2 13b
|
||||||
|
super().__init__(config, "ALIBI", linear_method)
|
||||||
|
|
||||||
|
|
||||||
class BaiChuanForCausalLM(BaiChuanBaseForCausalLM): # baichuan 7b
|
class BaiChuanForCausalLM(BaiChuanBaseForCausalLM):
|
||||||
|
"""Baichuan 7B."""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
config,
|
config,
|
||||||
|
|||||||
@@ -15,11 +15,7 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
"""Inference-only BLOOM model compatible with HuggingFace weights.
|
"""Inference-only BLOOM model compatible with HuggingFace weights."""
|
||||||
|
|
||||||
The input of the model is flattened to a 1D tensor of tokens. The model uses
|
|
||||||
InputMetadata to extract the original 2D shape of the input.
|
|
||||||
"""
|
|
||||||
import math
|
import math
|
||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
@@ -29,7 +25,7 @@ from transformers import BloomConfig
|
|||||||
|
|
||||||
from vllm.model_executor.input_metadata import InputMetadata
|
from vllm.model_executor.input_metadata import InputMetadata
|
||||||
from vllm.model_executor.layers.activation import get_act_fn
|
from vllm.model_executor.layers.activation import get_act_fn
|
||||||
from vllm.model_executor.layers.attention import PagedAttentionWithALiBi
|
from vllm.model_executor.layers.attention import PagedAttention
|
||||||
from vllm.model_executor.layers.linear import (ColumnParallelLinear,
|
from vllm.model_executor.layers.linear import (ColumnParallelLinear,
|
||||||
LinearMethodBase,
|
LinearMethodBase,
|
||||||
QKVParallelLinear,
|
QKVParallelLinear,
|
||||||
@@ -39,6 +35,7 @@ from vllm.model_executor.layers.vocab_parallel_embedding import (
|
|||||||
VocabParallelEmbedding)
|
VocabParallelEmbedding)
|
||||||
from vllm.model_executor.parallel_utils.parallel_state import (
|
from vllm.model_executor.parallel_utils.parallel_state import (
|
||||||
get_tensor_model_parallel_rank, get_tensor_model_parallel_world_size)
|
get_tensor_model_parallel_rank, get_tensor_model_parallel_world_size)
|
||||||
|
from vllm.model_executor.sampling_metadata import SamplingMetadata
|
||||||
from vllm.model_executor.weight_utils import (default_weight_loader,
|
from vllm.model_executor.weight_utils import (default_weight_loader,
|
||||||
hf_model_weights_iterator)
|
hf_model_weights_iterator)
|
||||||
from vllm.sequence import SamplerOutput
|
from vllm.sequence import SamplerOutput
|
||||||
@@ -110,8 +107,10 @@ class BloomAttention(nn.Module):
|
|||||||
alibi_slopes = alibi_slopes[head_start:head_end].tolist()
|
alibi_slopes = alibi_slopes[head_start:head_end].tolist()
|
||||||
|
|
||||||
scaling = self.head_dim**-0.5
|
scaling = self.head_dim**-0.5
|
||||||
self.attn = PagedAttentionWithALiBi(self.num_heads, self.head_dim,
|
self.attn = PagedAttention(self.num_heads,
|
||||||
scaling, alibi_slopes)
|
self.head_dim,
|
||||||
|
scaling,
|
||||||
|
alibi_slopes=alibi_slopes)
|
||||||
|
|
||||||
def forward(
|
def forward(
|
||||||
self,
|
self,
|
||||||
@@ -256,10 +255,7 @@ class BloomModel(nn.Module):
|
|||||||
hidden_states = self.word_embeddings(input_ids)
|
hidden_states = self.word_embeddings(input_ids)
|
||||||
hidden_states = self.word_embeddings_layernorm(hidden_states)
|
hidden_states = self.word_embeddings_layernorm(hidden_states)
|
||||||
for i in range(len(self.h)):
|
for i in range(len(self.h)):
|
||||||
if cache_events is None:
|
cache_event = None if cache_events is None else cache_events[i]
|
||||||
cache_event = None
|
|
||||||
else:
|
|
||||||
cache_event = cache_events[i]
|
|
||||||
layer = self.h[i]
|
layer = self.h[i]
|
||||||
hidden_states = layer(
|
hidden_states = layer(
|
||||||
position_ids,
|
position_ids,
|
||||||
@@ -293,11 +289,18 @@ class BloomForCausalLM(nn.Module):
|
|||||||
kv_caches: List[KVCache],
|
kv_caches: List[KVCache],
|
||||||
input_metadata: InputMetadata,
|
input_metadata: InputMetadata,
|
||||||
cache_events: Optional[List[torch.cuda.Event]],
|
cache_events: Optional[List[torch.cuda.Event]],
|
||||||
) -> SamplerOutput:
|
) -> torch.Tensor:
|
||||||
hidden_states = self.transformer(input_ids, positions, kv_caches,
|
hidden_states = self.transformer(input_ids, positions, kv_caches,
|
||||||
input_metadata, cache_events)
|
input_metadata, cache_events)
|
||||||
|
return hidden_states
|
||||||
|
|
||||||
|
def sample(
|
||||||
|
self,
|
||||||
|
hidden_states: torch.Tensor,
|
||||||
|
sampling_metadata: SamplingMetadata,
|
||||||
|
) -> SamplerOutput:
|
||||||
next_tokens = self.sampler(self.lm_head_weight, hidden_states,
|
next_tokens = self.sampler(self.lm_head_weight, hidden_states,
|
||||||
input_metadata)
|
sampling_metadata)
|
||||||
return next_tokens
|
return next_tokens
|
||||||
|
|
||||||
def load_weights(self,
|
def load_weights(self,
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
# Adapted from
|
# Adapted from
|
||||||
# https://github.com/THUDM/ChatGLM2-6B
|
# https://github.com/THUDM/ChatGLM2-6B
|
||||||
"""Inference-only ChatGLM model compatible with THUDM weights.
|
"""Inference-only ChatGLM model compatible with THUDM weights."""
|
||||||
|
|
||||||
The input of the model is flattened to a 1D tensor of tokens. The model uses
|
|
||||||
InputMetadata to extract the original 2D shape of the input.
|
|
||||||
"""
|
|
||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
@@ -14,17 +10,19 @@ from torch.nn import LayerNorm
|
|||||||
|
|
||||||
from vllm.model_executor.input_metadata import InputMetadata
|
from vllm.model_executor.input_metadata import InputMetadata
|
||||||
from vllm.model_executor.layers.activation import SiluAndMul
|
from vllm.model_executor.layers.activation import SiluAndMul
|
||||||
from vllm.model_executor.layers.attention import PagedAttentionWithRoPE
|
from vllm.model_executor.layers.attention import PagedAttention
|
||||||
from vllm.model_executor.layers.layernorm import RMSNorm
|
from vllm.model_executor.layers.layernorm import RMSNorm
|
||||||
from vllm.model_executor.layers.linear import (LinearMethodBase,
|
from vllm.model_executor.layers.linear import (LinearMethodBase,
|
||||||
MergedColumnParallelLinear,
|
MergedColumnParallelLinear,
|
||||||
QKVParallelLinear,
|
QKVParallelLinear,
|
||||||
RowParallelLinear)
|
RowParallelLinear)
|
||||||
|
from vllm.model_executor.layers.rotary_embedding import get_rope
|
||||||
from vllm.model_executor.layers.sampler import Sampler
|
from vllm.model_executor.layers.sampler import Sampler
|
||||||
from vllm.model_executor.layers.vocab_parallel_embedding import (
|
from vllm.model_executor.layers.vocab_parallel_embedding import (
|
||||||
VocabParallelEmbedding, ParallelLMHead)
|
VocabParallelEmbedding, ParallelLMHead)
|
||||||
from vllm.model_executor.parallel_utils.parallel_state import (
|
from vllm.model_executor.parallel_utils.parallel_state import (
|
||||||
get_tensor_model_parallel_world_size)
|
get_tensor_model_parallel_world_size)
|
||||||
|
from vllm.model_executor.sampling_metadata import SamplingMetadata
|
||||||
from vllm.model_executor.weight_utils import (default_weight_loader,
|
from vllm.model_executor.weight_utils import (default_weight_loader,
|
||||||
hf_model_weights_iterator)
|
hf_model_weights_iterator)
|
||||||
from vllm.sequence import SamplerOutput
|
from vllm.sequence import SamplerOutput
|
||||||
@@ -79,13 +77,21 @@ class GLMAttention(nn.Module):
|
|||||||
linear_method=linear_method,
|
linear_method=linear_method,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.attn = PagedAttentionWithRoPE(
|
# https://huggingface.co/THUDM/chatglm3-6b-32k/blob/e210410255278dd9d74463cf396ba559c0ef801c/modeling_chatglm.py#L141
|
||||||
|
rope_ratio = getattr(config, "rope_ratio", 1.0)
|
||||||
|
max_positions = getattr(config, "seq_length", 8192)
|
||||||
|
self.rotary_emb = get_rope(
|
||||||
|
self.head_dim,
|
||||||
|
rotary_dim=self.head_dim // 2,
|
||||||
|
max_position=max_positions,
|
||||||
|
base=10000 * rope_ratio,
|
||||||
|
is_neox_style=False,
|
||||||
|
)
|
||||||
|
self.attn = PagedAttention(
|
||||||
self.num_heads,
|
self.num_heads,
|
||||||
self.head_dim,
|
self.head_dim,
|
||||||
self.scaling,
|
self.scaling,
|
||||||
rotary_dim=self.head_dim // 2,
|
|
||||||
num_kv_heads=self.num_kv_heads,
|
num_kv_heads=self.num_kv_heads,
|
||||||
is_neox_style=False,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def forward(
|
def forward(
|
||||||
@@ -98,10 +104,9 @@ class GLMAttention(nn.Module):
|
|||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
qkv, _ = self.query_key_value(hidden_states)
|
qkv, _ = self.query_key_value(hidden_states)
|
||||||
q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
|
q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
|
||||||
|
q, k = self.rotary_emb(position_ids, q, k)
|
||||||
key_cache, value_cache = kv_cache
|
key_cache, value_cache = kv_cache
|
||||||
|
|
||||||
context_layer = self.attn(
|
context_layer = self.attn(
|
||||||
position_ids,
|
|
||||||
q,
|
q,
|
||||||
k,
|
k,
|
||||||
v,
|
v,
|
||||||
@@ -110,9 +115,7 @@ class GLMAttention(nn.Module):
|
|||||||
input_metadata,
|
input_metadata,
|
||||||
cache_event,
|
cache_event,
|
||||||
)
|
)
|
||||||
|
|
||||||
attn_output, _ = self.dense(context_layer)
|
attn_output, _ = self.dense(context_layer)
|
||||||
|
|
||||||
return attn_output
|
return attn_output
|
||||||
|
|
||||||
|
|
||||||
@@ -269,10 +272,7 @@ class GLMTransformer(nn.Module):
|
|||||||
cache_events: Optional[List[torch.cuda.Event]],
|
cache_events: Optional[List[torch.cuda.Event]],
|
||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
for i in range(self.num_layers):
|
for i in range(self.num_layers):
|
||||||
if cache_events is None:
|
cache_event = None if cache_events is None else cache_events[i]
|
||||||
cache_event = None
|
|
||||||
else:
|
|
||||||
cache_event = cache_events[i]
|
|
||||||
layer = self.layers[i]
|
layer = self.layers[i]
|
||||||
hidden_states = layer(
|
hidden_states = layer(
|
||||||
hidden_states=hidden_states,
|
hidden_states=hidden_states,
|
||||||
@@ -351,11 +351,18 @@ class ChatGLMForCausalLM(nn.Module):
|
|||||||
kv_caches: List[KVCache],
|
kv_caches: List[KVCache],
|
||||||
input_metadata: InputMetadata,
|
input_metadata: InputMetadata,
|
||||||
cache_events: Optional[List[torch.cuda.Event]],
|
cache_events: Optional[List[torch.cuda.Event]],
|
||||||
) -> SamplerOutput:
|
) -> torch.Tensor:
|
||||||
hidden_states = self.transformer(input_ids, positions, kv_caches,
|
hidden_states = self.transformer(input_ids, positions, kv_caches,
|
||||||
input_metadata, cache_events)
|
input_metadata, cache_events)
|
||||||
|
return hidden_states
|
||||||
|
|
||||||
|
def sample(
|
||||||
|
self,
|
||||||
|
hidden_states: torch.Tensor,
|
||||||
|
sampling_metadata: SamplingMetadata,
|
||||||
|
) -> SamplerOutput:
|
||||||
next_tokens = self.sampler(self.lm_head_weight, hidden_states,
|
next_tokens = self.sampler(self.lm_head_weight, hidden_states,
|
||||||
input_metadata)
|
sampling_metadata)
|
||||||
return next_tokens
|
return next_tokens
|
||||||
|
|
||||||
def load_weights(self,
|
def load_weights(self,
|
||||||
|
|||||||
@@ -28,13 +28,12 @@ from transformers import FalconConfig as HF_FalconConfig
|
|||||||
|
|
||||||
from vllm.model_executor.input_metadata import InputMetadata
|
from vllm.model_executor.input_metadata import InputMetadata
|
||||||
from vllm.model_executor.layers.activation import get_act_fn
|
from vllm.model_executor.layers.activation import get_act_fn
|
||||||
from vllm.model_executor.layers.attention import (PagedAttention,
|
from vllm.model_executor.layers.attention import PagedAttention
|
||||||
PagedAttentionWithALiBi,
|
|
||||||
PagedAttentionWithRoPE)
|
|
||||||
from vllm.model_executor.layers.linear import (ColumnParallelLinear,
|
from vllm.model_executor.layers.linear import (ColumnParallelLinear,
|
||||||
LinearMethodBase,
|
LinearMethodBase,
|
||||||
QKVParallelLinear,
|
QKVParallelLinear,
|
||||||
RowParallelLinear)
|
RowParallelLinear)
|
||||||
|
from vllm.model_executor.layers.rotary_embedding import get_rope
|
||||||
from vllm.model_executor.layers.sampler import Sampler
|
from vllm.model_executor.layers.sampler import Sampler
|
||||||
from vllm.model_executor.layers.vocab_parallel_embedding import (
|
from vllm.model_executor.layers.vocab_parallel_embedding import (
|
||||||
VocabParallelEmbedding, ParallelLMHead)
|
VocabParallelEmbedding, ParallelLMHead)
|
||||||
@@ -42,6 +41,7 @@ from vllm.model_executor.parallel_utils.communication_op import (
|
|||||||
tensor_model_parallel_all_reduce)
|
tensor_model_parallel_all_reduce)
|
||||||
from vllm.model_executor.parallel_utils.parallel_state import (
|
from vllm.model_executor.parallel_utils.parallel_state import (
|
||||||
get_tensor_model_parallel_rank, get_tensor_model_parallel_world_size)
|
get_tensor_model_parallel_rank, get_tensor_model_parallel_world_size)
|
||||||
|
from vllm.model_executor.sampling_metadata import SamplingMetadata
|
||||||
from vllm.model_executor.weight_utils import (default_weight_loader,
|
from vllm.model_executor.weight_utils import (default_weight_loader,
|
||||||
hf_model_weights_iterator)
|
hf_model_weights_iterator)
|
||||||
from vllm.sequence import SamplerOutput
|
from vllm.sequence import SamplerOutput
|
||||||
@@ -144,14 +144,16 @@ class FalconAttention(nn.Module):
|
|||||||
rope_theta = getattr(config, "rope_theta", 10000)
|
rope_theta = getattr(config, "rope_theta", 10000)
|
||||||
max_position_embeddings = getattr(config,
|
max_position_embeddings = getattr(config,
|
||||||
"max_position_embeddings", 8192)
|
"max_position_embeddings", 8192)
|
||||||
self.attn = PagedAttentionWithRoPE(
|
self.rotary_emb = get_rope(
|
||||||
self.num_heads,
|
|
||||||
self.head_dim,
|
self.head_dim,
|
||||||
self.inv_norm_factor,
|
|
||||||
base=rope_theta,
|
|
||||||
max_position=max_position_embeddings,
|
|
||||||
rotary_dim=self.head_dim,
|
rotary_dim=self.head_dim,
|
||||||
num_kv_heads=self.num_kv_heads)
|
max_position=max_position_embeddings,
|
||||||
|
base=rope_theta,
|
||||||
|
)
|
||||||
|
self.attn = PagedAttention(self.num_heads,
|
||||||
|
self.head_dim,
|
||||||
|
self.inv_norm_factor,
|
||||||
|
num_kv_heads=self.num_kv_heads)
|
||||||
elif self.use_alibi:
|
elif self.use_alibi:
|
||||||
tp_rank = get_tensor_model_parallel_rank()
|
tp_rank = get_tensor_model_parallel_rank()
|
||||||
head_start = tp_rank * self.num_heads
|
head_start = tp_rank * self.num_heads
|
||||||
@@ -159,11 +161,11 @@ class FalconAttention(nn.Module):
|
|||||||
alibi_slopes = (_get_alibi_slopes(self.total_num_heads) *
|
alibi_slopes = (_get_alibi_slopes(self.total_num_heads) *
|
||||||
self.inv_norm_factor)
|
self.inv_norm_factor)
|
||||||
alibi_slopes = alibi_slopes[head_start:head_end].tolist()
|
alibi_slopes = alibi_slopes[head_start:head_end].tolist()
|
||||||
self.attn = PagedAttentionWithALiBi(self.num_heads,
|
self.attn = PagedAttention(self.num_heads,
|
||||||
self.head_dim,
|
self.head_dim,
|
||||||
self.inv_norm_factor,
|
self.inv_norm_factor,
|
||||||
alibi_slopes,
|
num_kv_heads=self.num_kv_heads,
|
||||||
num_kv_heads=self.num_kv_heads)
|
alibi_slopes=alibi_slopes)
|
||||||
else:
|
else:
|
||||||
self.attn = PagedAttention(self.num_heads,
|
self.attn = PagedAttention(self.num_heads,
|
||||||
self.head_dim,
|
self.head_dim,
|
||||||
@@ -182,13 +184,11 @@ class FalconAttention(nn.Module):
|
|||||||
if bias is not None:
|
if bias is not None:
|
||||||
qkv += bias
|
qkv += bias
|
||||||
q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
|
q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
|
||||||
k_cache, v_cache = kv_cache
|
|
||||||
if self.use_rotary:
|
if self.use_rotary:
|
||||||
attn_output = self.attn(positions, q, k, v, k_cache, v_cache,
|
q, k = self.rotary_emb(positions, q, k)
|
||||||
input_metadata, cache_event)
|
k_cache, v_cache = kv_cache
|
||||||
else:
|
attn_output = self.attn(q, k, v, k_cache, v_cache, input_metadata,
|
||||||
attn_output = self.attn(q, k, v, k_cache, v_cache, input_metadata,
|
cache_event)
|
||||||
cache_event)
|
|
||||||
attn_output, bias = self.dense(attn_output)
|
attn_output, bias = self.dense(attn_output)
|
||||||
return attn_output, bias
|
return attn_output, bias
|
||||||
|
|
||||||
@@ -353,10 +353,7 @@ class FalconModel(nn.Module):
|
|||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
hidden_states = self.word_embeddings(input_ids)
|
hidden_states = self.word_embeddings(input_ids)
|
||||||
for i in range(len(self.h)):
|
for i in range(len(self.h)):
|
||||||
if cache_events is None:
|
cache_event = None if cache_events is None else cache_events[i]
|
||||||
cache_event = None
|
|
||||||
else:
|
|
||||||
cache_event = cache_events[i]
|
|
||||||
layer = self.h[i]
|
layer = self.h[i]
|
||||||
hidden_states = layer(
|
hidden_states = layer(
|
||||||
positions,
|
positions,
|
||||||
@@ -393,7 +390,7 @@ class FalconForCausalLM(nn.Module):
|
|||||||
kv_caches: List[KVCache],
|
kv_caches: List[KVCache],
|
||||||
input_metadata: InputMetadata,
|
input_metadata: InputMetadata,
|
||||||
cache_events: Optional[List[torch.cuda.Event]],
|
cache_events: Optional[List[torch.cuda.Event]],
|
||||||
) -> SamplerOutput:
|
) -> torch.Tensor:
|
||||||
hidden_states = self.transformer(
|
hidden_states = self.transformer(
|
||||||
input_ids,
|
input_ids,
|
||||||
positions,
|
positions,
|
||||||
@@ -401,9 +398,15 @@ class FalconForCausalLM(nn.Module):
|
|||||||
input_metadata,
|
input_metadata,
|
||||||
cache_events,
|
cache_events,
|
||||||
)
|
)
|
||||||
next_tokens = self.sampler(self.lm_head.weight, hidden_states,
|
return hidden_states
|
||||||
input_metadata)
|
|
||||||
|
|
||||||
|
def sample(
|
||||||
|
self,
|
||||||
|
hidden_states: torch.Tensor,
|
||||||
|
sampling_metadata: SamplingMetadata,
|
||||||
|
) -> SamplerOutput:
|
||||||
|
next_tokens = self.sampler(self.lm_head.weight, hidden_states,
|
||||||
|
sampling_metadata)
|
||||||
return next_tokens
|
return next_tokens
|
||||||
|
|
||||||
def load_weights(self,
|
def load_weights(self,
|
||||||
|
|||||||
@@ -16,11 +16,7 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
"""Inference-only GPT-2 model compatible with HuggingFace weights.
|
"""Inference-only GPT-2 model compatible with HuggingFace weights."""
|
||||||
|
|
||||||
The input of the model is flattened to a 1D tensor of tokens. The model uses
|
|
||||||
InputMetadata to extract the original 2D shape of the input.
|
|
||||||
"""
|
|
||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
@@ -39,6 +35,7 @@ from vllm.model_executor.layers.vocab_parallel_embedding import (
|
|||||||
VocabParallelEmbedding)
|
VocabParallelEmbedding)
|
||||||
from vllm.model_executor.parallel_utils.parallel_state import (
|
from vllm.model_executor.parallel_utils.parallel_state import (
|
||||||
get_tensor_model_parallel_world_size)
|
get_tensor_model_parallel_world_size)
|
||||||
|
from vllm.model_executor.sampling_metadata import SamplingMetadata
|
||||||
from vllm.model_executor.weight_utils import (default_weight_loader,
|
from vllm.model_executor.weight_utils import (default_weight_loader,
|
||||||
hf_model_weights_iterator)
|
hf_model_weights_iterator)
|
||||||
from vllm.sequence import SamplerOutput
|
from vllm.sequence import SamplerOutput
|
||||||
@@ -206,10 +203,7 @@ class GPT2Model(nn.Module):
|
|||||||
hidden_states = inputs_embeds + position_embeds
|
hidden_states = inputs_embeds + position_embeds
|
||||||
|
|
||||||
for i in range(len(self.h)):
|
for i in range(len(self.h)):
|
||||||
if cache_events is None:
|
cache_event = None if cache_events is None else cache_events[i]
|
||||||
cache_event = None
|
|
||||||
else:
|
|
||||||
cache_event = cache_events[i]
|
|
||||||
layer = self.h[i]
|
layer = self.h[i]
|
||||||
hidden_states = layer(hidden_states, kv_caches[i], input_metadata,
|
hidden_states = layer(hidden_states, kv_caches[i], input_metadata,
|
||||||
cache_event)
|
cache_event)
|
||||||
@@ -239,11 +233,18 @@ class GPT2LMHeadModel(nn.Module):
|
|||||||
kv_caches: List[KVCache],
|
kv_caches: List[KVCache],
|
||||||
input_metadata: InputMetadata,
|
input_metadata: InputMetadata,
|
||||||
cache_events: Optional[List[torch.cuda.Event]],
|
cache_events: Optional[List[torch.cuda.Event]],
|
||||||
) -> SamplerOutput:
|
) -> torch.Tensor:
|
||||||
hidden_states = self.transformer(input_ids, positions, kv_caches,
|
hidden_states = self.transformer(input_ids, positions, kv_caches,
|
||||||
input_metadata, cache_events)
|
input_metadata, cache_events)
|
||||||
|
return hidden_states
|
||||||
|
|
||||||
|
def sample(
|
||||||
|
self,
|
||||||
|
hidden_states: torch.Tensor,
|
||||||
|
sampling_metadata: SamplingMetadata,
|
||||||
|
) -> SamplerOutput:
|
||||||
next_tokens = self.sampler(self.lm_head_weight, hidden_states,
|
next_tokens = self.sampler(self.lm_head_weight, hidden_states,
|
||||||
input_metadata)
|
sampling_metadata)
|
||||||
return next_tokens
|
return next_tokens
|
||||||
|
|
||||||
def load_weights(self,
|
def load_weights(self,
|
||||||
|
|||||||
@@ -17,11 +17,7 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
"""Inference-only GPTBigCode model compatible with HuggingFace weights.
|
"""Inference-only GPTBigCode model compatible with HuggingFace weights."""
|
||||||
|
|
||||||
The input of the model is flattened to a 1D tensor of tokens. The model uses
|
|
||||||
InputMetadata to extract the original 2D shape of the input.
|
|
||||||
"""
|
|
||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
@@ -40,6 +36,7 @@ from vllm.model_executor.layers.vocab_parallel_embedding import (
|
|||||||
VocabParallelEmbedding)
|
VocabParallelEmbedding)
|
||||||
from vllm.model_executor.parallel_utils.parallel_state import (
|
from vllm.model_executor.parallel_utils.parallel_state import (
|
||||||
get_tensor_model_parallel_world_size)
|
get_tensor_model_parallel_world_size)
|
||||||
|
from vllm.model_executor.sampling_metadata import SamplingMetadata
|
||||||
from vllm.model_executor.weight_utils import (default_weight_loader,
|
from vllm.model_executor.weight_utils import (default_weight_loader,
|
||||||
hf_model_weights_iterator)
|
hf_model_weights_iterator)
|
||||||
from vllm.sequence import SamplerOutput
|
from vllm.sequence import SamplerOutput
|
||||||
@@ -225,10 +222,7 @@ class GPTBigCodeModel(nn.Module):
|
|||||||
hidden_states = inputs_embeds + position_embeds
|
hidden_states = inputs_embeds + position_embeds
|
||||||
|
|
||||||
for i in range(len(self.h)):
|
for i in range(len(self.h)):
|
||||||
if cache_events is None:
|
cache_event = None if cache_events is None else cache_events[i]
|
||||||
cache_event = None
|
|
||||||
else:
|
|
||||||
cache_event = cache_events[i]
|
|
||||||
layer = self.h[i]
|
layer = self.h[i]
|
||||||
hidden_states = layer(hidden_states, kv_caches[i], input_metadata,
|
hidden_states = layer(hidden_states, kv_caches[i], input_metadata,
|
||||||
cache_event)
|
cache_event)
|
||||||
@@ -258,11 +252,18 @@ class GPTBigCodeForCausalLM(nn.Module):
|
|||||||
kv_caches: List[KVCache],
|
kv_caches: List[KVCache],
|
||||||
input_metadata: InputMetadata,
|
input_metadata: InputMetadata,
|
||||||
cache_events: Optional[List[torch.cuda.Event]],
|
cache_events: Optional[List[torch.cuda.Event]],
|
||||||
) -> SamplerOutput:
|
) -> torch.Tensor:
|
||||||
hidden_states = self.transformer(input_ids, positions, kv_caches,
|
hidden_states = self.transformer(input_ids, positions, kv_caches,
|
||||||
input_metadata, cache_events)
|
input_metadata, cache_events)
|
||||||
|
return hidden_states
|
||||||
|
|
||||||
|
def sample(
|
||||||
|
self,
|
||||||
|
hidden_states: torch.Tensor,
|
||||||
|
sampling_metadata: SamplingMetadata,
|
||||||
|
) -> SamplerOutput:
|
||||||
next_tokens = self.sampler(self.lm_head_weight, hidden_states,
|
next_tokens = self.sampler(self.lm_head_weight, hidden_states,
|
||||||
input_metadata)
|
sampling_metadata)
|
||||||
return next_tokens
|
return next_tokens
|
||||||
|
|
||||||
def load_weights(self,
|
def load_weights(self,
|
||||||
|
|||||||
@@ -15,11 +15,7 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
"""Inference-only GPT-J model compatible with HuggingFace weights.
|
"""Inference-only GPT-J model compatible with HuggingFace weights."""
|
||||||
|
|
||||||
The input of the model is flattened to a 1D tensor of tokens. The model uses
|
|
||||||
InputMetadata to extract the original 2D shape of the input.
|
|
||||||
"""
|
|
||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
@@ -28,16 +24,18 @@ from transformers import GPTJConfig
|
|||||||
|
|
||||||
from vllm.model_executor.input_metadata import InputMetadata
|
from vllm.model_executor.input_metadata import InputMetadata
|
||||||
from vllm.model_executor.layers.activation import get_act_fn
|
from vllm.model_executor.layers.activation import get_act_fn
|
||||||
from vllm.model_executor.layers.attention import PagedAttentionWithRoPE
|
from vllm.model_executor.layers.attention import PagedAttention
|
||||||
from vllm.model_executor.layers.linear import (ColumnParallelLinear,
|
from vllm.model_executor.layers.linear import (ColumnParallelLinear,
|
||||||
LinearMethodBase,
|
LinearMethodBase,
|
||||||
QKVParallelLinear,
|
QKVParallelLinear,
|
||||||
RowParallelLinear)
|
RowParallelLinear)
|
||||||
|
from vllm.model_executor.layers.rotary_embedding import get_rope
|
||||||
from vllm.model_executor.layers.sampler import Sampler
|
from vllm.model_executor.layers.sampler import Sampler
|
||||||
from vllm.model_executor.layers.vocab_parallel_embedding import (
|
from vllm.model_executor.layers.vocab_parallel_embedding import (
|
||||||
VocabParallelEmbedding, ParallelLMHead)
|
VocabParallelEmbedding, ParallelLMHead)
|
||||||
from vllm.model_executor.parallel_utils.parallel_state import (
|
from vllm.model_executor.parallel_utils.parallel_state import (
|
||||||
get_tensor_model_parallel_world_size)
|
get_tensor_model_parallel_world_size)
|
||||||
|
from vllm.model_executor.sampling_metadata import SamplingMetadata
|
||||||
from vllm.model_executor.weight_utils import (default_weight_loader,
|
from vllm.model_executor.weight_utils import (default_weight_loader,
|
||||||
hf_model_weights_iterator)
|
hf_model_weights_iterator)
|
||||||
from vllm.sequence import SamplerOutput
|
from vllm.sequence import SamplerOutput
|
||||||
@@ -81,15 +79,14 @@ class GPTJAttention(nn.Module):
|
|||||||
rope_theta = getattr(config, "rope_theta", 10000)
|
rope_theta = getattr(config, "rope_theta", 10000)
|
||||||
max_position_embeddings = getattr(config, "max_position_embeddings",
|
max_position_embeddings = getattr(config, "max_position_embeddings",
|
||||||
8192)
|
8192)
|
||||||
self.attn = PagedAttentionWithRoPE(
|
self.rotary_emb = get_rope(
|
||||||
self.num_heads,
|
|
||||||
self.head_size,
|
self.head_size,
|
||||||
scaling,
|
rotary_dim=config.rotary_dim,
|
||||||
config.rotary_dim,
|
|
||||||
base=rope_theta,
|
|
||||||
max_position=max_position_embeddings,
|
max_position=max_position_embeddings,
|
||||||
is_neox_style=False)
|
base=rope_theta,
|
||||||
self.warmup = False
|
is_neox_style=False,
|
||||||
|
)
|
||||||
|
self.attn = PagedAttention(self.num_heads, self.head_size, scaling)
|
||||||
|
|
||||||
def forward(
|
def forward(
|
||||||
self,
|
self,
|
||||||
@@ -101,9 +98,10 @@ class GPTJAttention(nn.Module):
|
|||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
qkv, _ = self.qkv_proj(hidden_states)
|
qkv, _ = self.qkv_proj(hidden_states)
|
||||||
q, k, v = qkv.chunk(chunks=3, dim=-1)
|
q, k, v = qkv.chunk(chunks=3, dim=-1)
|
||||||
|
q, k = self.rotary_emb(position_ids, q, k)
|
||||||
k_cache, v_cache = kv_cache
|
k_cache, v_cache = kv_cache
|
||||||
attn_output = self.attn(position_ids, q, k, v, k_cache, v_cache,
|
attn_output = self.attn(q, k, v, k_cache, v_cache, input_metadata,
|
||||||
input_metadata, cache_event)
|
cache_event)
|
||||||
attn_output, _ = self.out_proj(attn_output)
|
attn_output, _ = self.out_proj(attn_output)
|
||||||
return attn_output
|
return attn_output
|
||||||
|
|
||||||
@@ -147,10 +145,7 @@ class GPTJBlock(nn.Module):
|
|||||||
linear_method: Optional[LinearMethodBase] = None,
|
linear_method: Optional[LinearMethodBase] = None,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
if config.n_inner is None:
|
inner_dim = 4 * config.n_embd if config.n_inner is None else config.n_inner
|
||||||
inner_dim = 4 * config.n_embd
|
|
||||||
else:
|
|
||||||
inner_dim = config.n_inner
|
|
||||||
self.ln_1 = nn.LayerNorm(config.n_embd, eps=config.layer_norm_epsilon)
|
self.ln_1 = nn.LayerNorm(config.n_embd, eps=config.layer_norm_epsilon)
|
||||||
self.attn = GPTJAttention(config, linear_method)
|
self.attn = GPTJAttention(config, linear_method)
|
||||||
self.mlp = GPTJMLP(inner_dim, config, linear_method)
|
self.mlp = GPTJMLP(inner_dim, config, linear_method)
|
||||||
@@ -205,10 +200,7 @@ class GPTJModel(nn.Module):
|
|||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
hidden_states = self.wte(input_ids)
|
hidden_states = self.wte(input_ids)
|
||||||
for i in range(len(self.h)):
|
for i in range(len(self.h)):
|
||||||
if cache_events is None:
|
cache_event = None if cache_events is None else cache_events[i]
|
||||||
cache_event = None
|
|
||||||
else:
|
|
||||||
cache_event = cache_events[i]
|
|
||||||
layer = self.h[i]
|
layer = self.h[i]
|
||||||
hidden_states = layer(
|
hidden_states = layer(
|
||||||
position_ids,
|
position_ids,
|
||||||
@@ -247,11 +239,18 @@ class GPTJForCausalLM(nn.Module):
|
|||||||
kv_caches: List[KVCache],
|
kv_caches: List[KVCache],
|
||||||
input_metadata: InputMetadata,
|
input_metadata: InputMetadata,
|
||||||
cache_events: Optional[List[torch.cuda.Event]],
|
cache_events: Optional[List[torch.cuda.Event]],
|
||||||
) -> SamplerOutput:
|
) -> torch.Tensor:
|
||||||
hidden_states = self.transformer(input_ids, positions, kv_caches,
|
hidden_states = self.transformer(input_ids, positions, kv_caches,
|
||||||
input_metadata, cache_events)
|
input_metadata, cache_events)
|
||||||
|
return hidden_states
|
||||||
|
|
||||||
|
def sample(
|
||||||
|
self,
|
||||||
|
hidden_states: torch.Tensor,
|
||||||
|
sampling_metadata: SamplingMetadata,
|
||||||
|
) -> SamplerOutput:
|
||||||
next_tokens = self.sampler(self.lm_head.weight, hidden_states,
|
next_tokens = self.sampler(self.lm_head.weight, hidden_states,
|
||||||
input_metadata, self.lm_head.bias)
|
sampling_metadata, self.lm_head.bias)
|
||||||
return next_tokens
|
return next_tokens
|
||||||
|
|
||||||
def load_weights(self,
|
def load_weights(self,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user