File size: 169,041 Bytes
e0c2d04 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 |
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"# Inference acceleration of `T5` with `bfloat16`\n",
"\n",
"This is a spin-off of the `T5` inference notebook. \n",
"We reuse most of the recipes with a notable change: we replace `float16` (`fp16` below) format by `bfloat16` (`bf16` below). \n",
"\n",
"## About `bf16` format\n",
"\n",
"Most deep learning models are trained and inferred nowadays in mixed precision. \n",
"It means that most operations are done in either half precision (`fp16`) or single precision (`float32`, `fp32` below). \n",
"\n",
"Doing operations in half precision means reducing memory footprint of weights by half but also increasing the speed of big matrix multiplication.\n",
"\n",
"Unfortunately, by using only 16 bits instead of 32, we can encode only a part of the original information. \n",
"Sometimes it's even impossible to encode numbers, for instance, if their value is above 65K or below -65K. \n",
"Also, numbers very close to 0 may be rounded to 0 which may be an issue if the number is then used as the denominator of a division.\n",
"\n",
"These limitations are the consequences of how float numbers are encoded. There are 3 parts:\n",
"\n",
"* the **sign** (1 bit);\n",
"* the **mantissa** or **significand** (10 bits in `fp16`, 22 in `fp32`): the number representation before scaling;\n",
"* the **exponent** (5 bits in `fp16`, 8 in `fp32`): it's used to scale the mantissa, basically it decides where is the coma.\n",
"\n",
"`float number = sign X base^exponent X significand`\n",
"\n",
"The mix precision setup implies some complexity explained in the `t5.ipynb` (available in the same folder that this notebook).\n",
"\n",
"Recently, Google Brain has created a new format, `bfloat16` (the `b` stands for brain).\n",
"\n",
"To make it short, a `bf16` number is just the 16 most significant bits of a `fp32` number, aka 8 bits for the exponent and 8 for the mantissa. Therefore it has the same range as `fp32` but a much lower precision.\n",
"\n",
"To convert a number from `fp32` to `bf16`, we just take the 16 most significant bits, it's called the truncation approach.\n",
"To get the most exact result, you need a second step, which is called the rounding to nearest even to retrieve a part of the information of the original number.\n",
"\n",
"Because we have the same range as `fp32`, in theory, we do not need anymore the complex mechanic to make it work in mixed precision.\n",
"\n",
"There are 2 shortcomings:\n",
"- this format is not supported by all the GPUs (since Nvidia Ampere generation) and CPUs\n",
"- accuracy of `bfloat16` is lower than `fp16` (when in `fp16` range): usually when this format is used for some computation, the accumulation is still done with `fp32` for maximum precision. To get the best results below, we do not use any mixed precision mechanism. It appeared that the tolerance needs to be set 10X higher than in the mixed precision notebook for the same model size.\n",
"\n",
"More about this format can be found in this [paper](https://arxiv.org/pdf/1905.12322.pdf).\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:06:25.155428Z",
"iopub.status.busy": "2022-07-01T16:06:25.154762Z",
"iopub.status.idle": "2022-07-01T16:06:25.720135Z",
"shell.execute_reply": "2022-07-01T16:06:25.718633Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Sun Jul 3 20:37:18 2022 \r\n",
"+-----------------------------------------------------------------------------+\r\n",
"| NVIDIA-SMI 515.48.07 Driver Version: 515.48.07 CUDA Version: 11.7 |\r\n",
"|-------------------------------+----------------------+----------------------+\r\n",
"| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |\r\n",
"| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |\r\n",
"| | | MIG M. |\r\n",
"|===============================+======================+======================|\r\n",
"| 0 NVIDIA GeForce ... On | 00000000:03:00.0 On | N/A |\r\n",
"| 55% 55C P5 49W / 350W | 1055MiB / 24576MiB | 6% Default |\r\n",
"| | | N/A |\r\n",
"+-------------------------------+----------------------+----------------------+\r\n",
" \r\n",
"+-----------------------------------------------------------------------------+\r\n",
"| Processes: |\r\n",
"| GPU GI CI PID Type Process name GPU Memory |\r\n",
"| ID ID Usage |\r\n",
"|=============================================================================|\r\n",
"| 0 N/A N/A 2317 G /usr/lib/xorg/Xorg 97MiB |\r\n",
"| 0 N/A N/A 7571 G /usr/bin/gnome-shell 35MiB |\r\n",
"| 0 N/A N/A 9177 G ...on/Bin/AgentConnectix.bin 4MiB |\r\n",
"| 0 N/A N/A 26231 G ...164143431494779369,131072 107MiB |\r\n",
"| 0 N/A N/A 315590 C ...st_transformer/bin/python 805MiB |\r\n",
"+-----------------------------------------------------------------------------+\r\n"
]
}
],
"source": [
"! nvidia-smi"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"## `ONNX Runtime` compilation\n",
"\n",
"You need at least version 1.12 of `ONNX Runtime` which is not yet released at the time of this writing. If not yet the case, it will require you to compile it."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:06:25.726247Z",
"iopub.status.busy": "2022-07-01T16:06:25.725753Z",
"iopub.status.idle": "2022-07-01T16:06:27.901495Z",
"shell.execute_reply": "2022-07-01T16:06:27.900846Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"import json\n",
"import random\n",
"from transformer_deploy.backends.ort_utils import get_keep_fp32_nodes\n",
"from transformer_deploy.backends.ort_utils import convert_fp16\n",
"import time\n",
"from typing import Callable, Dict, Optional, List\n",
"import matplotlib.pylab as plt\n",
"from onnxruntime import IOBinding\n",
"import numpy as np\n",
"import torch\n",
"from pathlib import Path\n",
"from typing import Tuple\n",
"from transformer_deploy.backends.onnx_utils import save_onnx, merge_autoregressive_model_graphs\n",
"from transformer_deploy.backends.ort_utils import search_fp32_nodes\n",
"from transformer_deploy.backends.ort_utils import add_output_nodes\n",
"import onnx\n",
"\n",
"from torch.nn import Linear\n",
"from transformers import AutoModelForSeq2SeqLM, AutoTokenizer, PretrainedConfig, T5ForConditionalGeneration, TensorType\n",
"from transformers.generation_utils import GenerationMixin\n",
"from transformers.modeling_outputs import BaseModelOutputWithPastAndCrossAttentions, Seq2SeqLMOutput\n",
"from transformers.models.t5.modeling_t5 import T5Stack\n",
"from nvtx import nvtx\n",
"from copy import copy\n",
"\n",
"from transformer_deploy.backends.ort_utils import create_model_for_provider, inference_onnx_binding\n",
"from transformer_deploy.backends.pytorch_utils import convert_to_onnx\n",
"import seaborn as sns\n",
"import operator\n",
"from collections import defaultdict\n",
"import gc\n",
"from transformer_deploy.backends.onnx_utils import patch_constant_node_bf16"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"## Loading `Hugging Face` model / tokenizer\n",
"\n",
"Below we load the model and set global variables of this notebook."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:06:27.908115Z",
"iopub.status.busy": "2022-07-01T16:06:27.907443Z",
"iopub.status.idle": "2022-07-01T16:07:45.726098Z",
"shell.execute_reply": "2022-07-01T16:07:45.725449Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"np.random.seed(123)\n",
"torch.random.manual_seed(123)\n",
"# other possible values: t5-small, t5-base, t5-large. t5-3b should work when ORT library is fixed\n",
"model_name = \"t5-small\"\n",
"# shape of output generation for benchmarks (seqlen, batch).\n",
"# You can provide 2 tasks to benchmark.\n",
"# On 3b model, 128 tokens with batch 2 is the maximum without OOM Exception on GPU with 24Gb of RAM.\n",
"# On smaller models/more ram GPUs, use bigger values like [(1024, 1), (1024, 4)]\n",
"benchmark_tasks = [(1024, 1), (1024, 4)]\n",
"# T5 has no max len\n",
"# https://github.com/huggingface/transformers/issues/5204\n",
"tokenizer = AutoTokenizer.from_pretrained(model_name, model_max_length=512)\n",
"input_ids: torch.Tensor = tokenizer(\n",
" 'translate English to French: Transfer learning, where a model is first pre-trained on a data-rich task before being fine-tuned on a downstream task, has emerged as a powerful technique in natural language processing (NLP). The effectiveness of transfer learning has given rise to a diversity of approaches, methodology, and practice. In this paper, we explore the landscape of transfer learning techniques for NLP by introducing a unified framework that converts all text-based language problems into a text-to-text format. Our systematic study compares pre-training objectives, architectures, unlabeled data sets, transfer approaches, and other factors on dozens of language understanding tasks. By combining the insights from our exploration with scale and our new \"Colossal Clean Crawled Corpus\", we achieve state-of-the-art results on many benchmarks covering summarization, question answering, text classification, and more. To facilitate future work on transfer learning for NLP, we release our data set, pre-trained models, and code.',\n",
" return_tensors=TensorType.PYTORCH,\n",
").input_ids\n",
"input_ids = input_ids.type(torch.int32)\n",
"pytorch_model: T5ForConditionalGeneration = AutoModelForSeq2SeqLM.from_pretrained(model_name)\n",
"pytorch_model = pytorch_model.eval()\n",
"pytorch_model = pytorch_model.to(torch.bfloat16) # bf16 is the new format\n",
"\n",
"\n",
"pytorch_model.config.use_cache = True # not really needed, just to make things obvious\n",
"num_layers = pytorch_model.config.num_layers\n",
"# tolerance between ONNX FP16 and Pytorch FP32.\n",
"# expect a 10X bigger tolerance compared to mix precision!\n",
"fp16_default_tolerance = 1\n",
"\n",
"\n",
"def are_equal(a: torch.Tensor, b: torch.Tensor, atol: float = fp16_default_tolerance) -> None:\n",
" assert np.allclose(\n",
" a=a.type(torch.float32).detach().cpu().numpy(), b=b.type(torch.float32).detach().cpu().numpy(), atol=atol\n",
" ), f\"{a}\\n\\nVS\\n\\n{b}\"\n",
"\n",
"\n",
"def prepare_folder(path: str) -> Tuple[str, str]:\n",
" p = Path(path)\n",
" p.mkdir(parents=True, exist_ok=True)\n",
" [item.unlink() for item in Path(path).glob(\"*\") if item.is_file()]\n",
" return path + \"/model.onnx\", path + \"/model_fp16.onnx\"\n",
"\n",
"\n",
"# create/clean folders where each model will be stored.\n",
"# as multiple files will be saved for T5-3B and 11B, we use different folders for the encoder and the decoders.\n",
"encoder_model_path, encoder_fp16_model_path = prepare_folder(path=\"./test-enc\")\n",
"dec_cache_model_path, dec_cache_fp16_model_path = prepare_folder(path=\"./test-dec-cache\")\n",
"dec_no_cache_model_path, dec_no_cache_fp16_model_path = prepare_folder(path=\"./test-dec-no-cache\")\n",
"dec_if_model_path, dec_if_fp16_model_path = prepare_folder(path=\"./test-dec-if\")\n",
"\n",
"# some outputs to compare with\n",
"out_enc: BaseModelOutputWithPastAndCrossAttentions = pytorch_model.encoder(input_ids=input_ids)\n",
"out_full: Seq2SeqLMOutput = pytorch_model(input_ids=input_ids, decoder_input_ids=input_ids)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"# Export to ONNX\n",
"\n",
"## Export encoder part\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:07:45.729937Z",
"iopub.status.busy": "2022-07-01T16:07:45.729459Z",
"iopub.status.idle": "2022-07-01T16:08:04.399210Z",
"shell.execute_reply": "2022-07-01T16:08:04.398381Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"convert_to_onnx(\n",
" model_pytorch=pytorch_model.encoder,\n",
" output_path=encoder_model_path,\n",
" inputs_pytorch={\"input_ids\": input_ids},\n",
" var_output_seq=True,\n",
" quantization=False,\n",
" output_names=[\"output\"],\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:08:04.402291Z",
"iopub.status.busy": "2022-07-01T16:08:04.402076Z",
"iopub.status.idle": "2022-07-01T16:08:04.665456Z",
"shell.execute_reply": "2022-07-01T16:08:04.664654Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"model_onnx = onnx.load(encoder_model_path, load_external_data=False)\n",
"patched_model_onnx = patch_constant_node_bf16(model=model_onnx)\n",
"save_onnx(patched_model_onnx, encoder_model_path)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"## Export decoder\n",
"\n",
"### Wrapper to include some post-processing on the decoder output\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:08:04.671558Z",
"iopub.status.busy": "2022-07-01T16:08:04.671209Z",
"iopub.status.idle": "2022-07-01T16:08:41.055430Z",
"shell.execute_reply": "2022-07-01T16:08:41.054659Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"class ExportT5(torch.nn.Module):\n",
" def __init__(self, decoder: T5Stack, lm_head: Linear):\n",
" super(ExportT5, self).__init__()\n",
" self.decoder = decoder\n",
" self.lm_head = lm_head\n",
"\n",
" def forward(self, input_ids: torch.Tensor, encoder_hidden_states: torch.Tensor, past_key_values: Tuple = None):\n",
" # cast fp16 inputs to bfloat16 as ORT does not support bf16 inputs...\n",
" encoder_hidden_states = encoder_hidden_states.type(torch.bfloat16)\n",
"\n",
" if past_key_values is not None:\n",
" past_key_values_bfloat16 = list()\n",
" for tensor_tuple in past_key_values:\n",
" res = tuple([tensor.type(torch.bfloat16) for tensor in tensor_tuple])\n",
" past_key_values_bfloat16.append(res)\n",
" past_key_values_bfloat16 = list(past_key_values_bfloat16)\n",
" else:\n",
" past_key_values_bfloat16 = None\n",
"\n",
" out_dec = self.decoder.forward(\n",
" input_ids=input_ids, encoder_hidden_states=encoder_hidden_states, past_key_values=past_key_values_bfloat16\n",
" )\n",
" # weight tying -> rescale output before projecting on vocab\n",
" # to comment for T0 for instance\n",
" out_dec[\"last_hidden_state\"] = out_dec[\"last_hidden_state\"] * (pytorch_model.model_dim**-0.5)\n",
" out_dec[\"last_hidden_state\"] = self.lm_head(out_dec[\"last_hidden_state\"])\n",
" out_dec[\"last_hidden_state\"] = out_dec[\"last_hidden_state\"].half()\n",
" past_key_values_tuples = list()\n",
" for tensor_tuple in out_dec[\"past_key_values\"]:\n",
" res = tuple([tensor.half() for tensor in tensor_tuple])\n",
" past_key_values_tuples.append(res)\n",
" out_dec[\"past_key_values\"] = tuple(past_key_values_tuples)\n",
" return out_dec\n",
"\n",
"\n",
"model_decoder = ExportT5(decoder=pytorch_model.decoder, lm_head=pytorch_model.lm_head).eval()\n",
"out_model_export: torch.Tensor = model_decoder(input_ids=input_ids, encoder_hidden_states=out_enc.last_hidden_state)\n",
"\n",
"are_equal(a=out_model_export[\"last_hidden_state\"], b=out_full.logits)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"### Export decoder part to `ONNX`"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:08:41.059613Z",
"iopub.status.busy": "2022-07-01T16:08:41.059296Z",
"iopub.status.idle": "2022-07-01T16:09:17.501345Z",
"shell.execute_reply": "2022-07-01T16:09:17.500697Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"# decoder output one step before\n",
"out_dec_pytorch = model_decoder(input_ids=input_ids[:, :-1], encoder_hidden_states=out_enc.last_hidden_state)\n",
"\n",
"model_inputs = {\n",
" \"input_ids\": input_ids[:, -1:].type(torch.int32),\n",
" \"encoder_hidden_states\": out_enc.last_hidden_state.half(),\n",
" \"past_key_values\": out_dec_pytorch.past_key_values,\n",
"}\n",
"\n",
"input_names = [\"input_ids\", \"encoder_hidden_states\"]\n",
"\n",
"for i in range(num_layers):\n",
" input_names.append(f\"past_key_values.{i}.decoder.key\")\n",
" input_names.append(f\"past_key_values.{i}.decoder.value\")\n",
" input_names.append(f\"past_key_values.{i}.encoder.key\")\n",
" input_names.append(f\"past_key_values.{i}.encoder.value\")\n",
"\n",
"output_names = [\"logits\"]\n",
"\n",
"for i in range(num_layers):\n",
" output_names.append(f\"present.{i}.decoder.key\")\n",
" output_names.append(f\"present.{i}.decoder.value\")\n",
" output_names.append(f\"present.{i}.encoder.key\")\n",
" output_names.append(f\"present.{i}.encoder.value\")\n",
"\n",
"dynamic_axis = {\n",
" \"input_ids\": {0: \"batch\", 1: \"encoder_sequence\"},\n",
" \"encoder_hidden_states\": {0: \"batch\", 1: \"encoder_sequence\"},\n",
" \"logits\": {0: \"batch\", 1: \"decoder_sequence\"},\n",
"}\n",
"\n",
"\n",
"for i in range(num_layers):\n",
" dynamic_axis[f\"past_key_values.{i}.decoder.key\"] = {0: \"batch\", 2: \"past_decoder_sequence\"}\n",
" dynamic_axis[f\"past_key_values.{i}.decoder.value\"] = {0: \"batch\", 2: \"past_decoder_sequence\"}\n",
" dynamic_axis[f\"past_key_values.{i}.encoder.key\"] = {0: \"batch\", 2: \"encoder_sequence_length\"}\n",
" dynamic_axis[f\"past_key_values.{i}.encoder.value\"] = {0: \"batch\", 2: \"encoder_sequence_length\"}\n",
"\n",
" dynamic_axis[f\"present.{i}.decoder.key\"] = {0: \"batch\", 2: \"decoder_sequence\"}\n",
" dynamic_axis[f\"present.{i}.decoder.value\"] = {0: \"batch\", 2: \"decoder_sequence\"}\n",
" dynamic_axis[f\"present.{i}.encoder.key\"] = {0: \"batch\", 2: \"encoder_sequence_length\"}\n",
" dynamic_axis[f\"present.{i}.encoder.value\"] = {0: \"batch\", 2: \"encoder_sequence_length\"}"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"Export of the model with cache support:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:09:17.505944Z",
"iopub.status.busy": "2022-07-01T16:09:17.505344Z",
"iopub.status.idle": "2022-07-01T16:09:22.956971Z",
"shell.execute_reply": "2022-07-01T16:09:22.956345Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/geantvert/.local/share/virtualenvs/fast_transformer/lib/python3.9/site-packages/transformers/modeling_utils.py:781: TracerWarning: Converting a tensor to a Python boolean might cause the trace to be incorrect. We can't record the data flow of Python values, so this value will be treated as a constant in the future. This means that the trace might not generalize to other inputs!\n",
" if causal_mask.shape[1] < attention_mask.shape[1]:\n",
"In-place op on output of tensor.shape. See https://pytorch.org/docs/master/onnx.html#avoid-inplace-operations-when-using-tensor-shape-in-tracing-mode\n",
"In-place op on output of tensor.shape. See https://pytorch.org/docs/master/onnx.html#avoid-inplace-operations-when-using-tensor-shape-in-tracing-mode\n"
]
}
],
"source": [
"with torch.no_grad():\n",
" pytorch_model.config.return_dict = True\n",
" pytorch_model.eval()\n",
"\n",
" # export can works with named args but the dict containing named args as to be last element of the args tuple\n",
" torch.onnx.export(\n",
" model_decoder,\n",
" (model_inputs,),\n",
" f=dec_cache_model_path,\n",
" input_names=input_names,\n",
" output_names=output_names,\n",
" dynamic_axes=dynamic_axis,\n",
" do_constant_folding=True,\n",
" opset_version=13,\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"The graph generated by Pytorch being illegal (includes some operators with unsupported attributes), we fix it manually below."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:09:22.961604Z",
"iopub.status.busy": "2022-07-01T16:09:22.961112Z",
"iopub.status.idle": "2022-07-01T16:09:23.358172Z",
"shell.execute_reply": "2022-07-01T16:09:23.357006Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"model_onnx = onnx.load(dec_cache_model_path, load_external_data=False)\n",
"model_onnx = patch_constant_node_bf16(model=model_onnx)\n",
"save_onnx(model_onnx, dec_cache_model_path)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:09:23.364298Z",
"iopub.status.busy": "2022-07-01T16:09:23.363763Z",
"iopub.status.idle": "2022-07-01T16:10:04.272285Z",
"shell.execute_reply": "2022-07-01T16:10:04.271664Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"model_inputs_no_cache = {\n",
" \"input_ids\": input_ids,\n",
" \"encoder_hidden_states\": out_enc.last_hidden_state.half(),\n",
"}\n",
"\n",
"with torch.no_grad():\n",
" pytorch_model.config.return_dict = False\n",
" pytorch_model.eval()\n",
"\n",
" # export can works with named args but the dict containing named args as to be last element of the args tuple\n",
" torch.onnx.export(\n",
" model_decoder,\n",
" (model_inputs_no_cache,),\n",
" f=dec_no_cache_model_path,\n",
" input_names=list(model_inputs_no_cache.keys()),\n",
" output_names=output_names,\n",
" dynamic_axes={k: v for k, v in dynamic_axis.items() if \"past_key_values\" not in k},\n",
" do_constant_folding=True,\n",
" opset_version=13,\n",
" export_modules_as_functions=False,\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:10:04.277101Z",
"iopub.status.busy": "2022-07-01T16:10:04.276910Z",
"iopub.status.idle": "2022-07-01T16:10:04.702396Z",
"shell.execute_reply": "2022-07-01T16:10:04.701023Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"model_onnx = onnx.load(dec_no_cache_model_path, load_external_data=False)\n",
"model_onnx = patch_constant_node_bf16(model=model_onnx)\n",
"save_onnx(model_onnx, dec_no_cache_model_path)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"## Merge `ONNX` computation graph to deduplicate weights"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:10:04.709130Z",
"iopub.status.busy": "2022-07-01T16:10:04.708589Z",
"iopub.status.idle": "2022-07-01T16:10:06.339197Z",
"shell.execute_reply": "2022-07-01T16:10:06.338545Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n"
]
},
{
"data": {
"text/plain": "0"
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"merge_autoregressive_model_graphs(\n",
" model_cache_path=dec_cache_model_path,\n",
" model_no_cache_path=dec_no_cache_model_path,\n",
" output_path=dec_if_model_path,\n",
")\n",
"\n",
"torch.cuda.empty_cache()\n",
"gc.collect()"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"### Check `ONNX` decoder output\n",
"\n",
"Compare `ONNX` output with and without cache, plus compare with `Pytorch` output."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:10:06.345756Z",
"iopub.status.busy": "2022-07-01T16:10:06.345276Z",
"iopub.status.idle": "2022-07-01T16:10:07.635199Z",
"shell.execute_reply": "2022-07-01T16:10:07.634561Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"pytorch_model = pytorch_model.cuda()\n",
"model_decoder = model_decoder.cuda()\n",
"input_ids = input_ids.cuda()\n",
"pytorch_model = pytorch_model.eval()\n",
"model_decoder = model_decoder.eval()"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"## Zero copy output"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:10:07.640175Z",
"iopub.status.busy": "2022-07-01T16:10:07.639903Z",
"iopub.status.idle": "2022-07-01T16:10:08.503187Z",
"shell.execute_reply": "2022-07-01T16:10:08.502567Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"with torch.inference_mode():\n",
" out_enc_pytorch: BaseModelOutputWithPastAndCrossAttentions = pytorch_model.encoder(input_ids=input_ids)\n",
" previous_step_pytorch: BaseModelOutputWithPastAndCrossAttentions = model_decoder(\n",
" input_ids=input_ids[:, :-1], encoder_hidden_states=out_enc_pytorch.last_hidden_state\n",
" )\n",
" out_dec_pytorch: BaseModelOutputWithPastAndCrossAttentions = model_decoder(\n",
" input_ids=input_ids, encoder_hidden_states=out_enc_pytorch.last_hidden_state\n",
" )\n",
"pytorch_model = pytorch_model.cpu()\n",
"model_decoder = model_decoder.cpu()\n",
"torch.cuda.empty_cache()"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:10:08.506058Z",
"iopub.status.busy": "2022-07-01T16:10:08.505778Z",
"iopub.status.idle": "2022-07-01T16:10:09.262085Z",
"shell.execute_reply": "2022-07-01T16:10:09.261261Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"enc_fp16_onnx = create_model_for_provider(encoder_model_path, \"CUDAExecutionProvider\", log_severity=3)\n",
"enc_fp16_onnx_binding: IOBinding = enc_fp16_onnx.io_binding()\n",
"dec_onnx = create_model_for_provider(dec_if_model_path, \"CUDAExecutionProvider\", log_severity=3)\n",
"dec_onnx_binding: IOBinding = dec_onnx.io_binding()"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:10:09.266537Z",
"iopub.status.busy": "2022-07-01T16:10:09.266351Z",
"iopub.status.idle": "2022-07-01T16:10:09.297567Z",
"shell.execute_reply": "2022-07-01T16:10:09.296969Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"fp16_default_tolerance = 100\n",
"\n",
"\n",
"def decoder_pytorch_inference(decoder_input_ids: torch.Tensor, encoder_hidden_states: torch.Tensor, **_):\n",
" with torch.inference_mode():\n",
" return model_decoder(input_ids=decoder_input_ids, encoder_hidden_states=encoder_hidden_states)\n",
"\n",
"\n",
"def decoder_onnx_inference(\n",
" decoder_input_ids: torch.Tensor,\n",
" encoder_hidden_states: torch.Tensor,\n",
" enable_cache: torch.Tensor,\n",
" past_key_values: Optional[torch.Tensor],\n",
"):\n",
" inputs_onnx_dict = {\n",
" \"input_ids\": decoder_input_ids,\n",
" \"encoder_hidden_states\": encoder_hidden_states,\n",
" \"enable_cache\": enable_cache,\n",
" }\n",
"\n",
" if past_key_values is not None:\n",
" for index, (k_dec, v_dec, k_enc, v_enc) in enumerate(past_key_values):\n",
" inputs_onnx_dict[f\"past_key_values.{index}.decoder.key\"] = k_dec\n",
" inputs_onnx_dict[f\"past_key_values.{index}.decoder.value\"] = v_dec\n",
" inputs_onnx_dict[f\"past_key_values.{index}.encoder.key\"] = k_enc\n",
" inputs_onnx_dict[f\"past_key_values.{index}.encoder.value\"] = v_enc\n",
"\n",
" result_dict = inference_onnx_binding(\n",
" model_onnx=dec_onnx,\n",
" inputs=inputs_onnx_dict,\n",
" binding=dec_onnx_binding, # recycle the binding\n",
" device=decoder_input_ids.device.type,\n",
" clone_tensor=False, # no memory copy -> best perf and lowest memory footprint!\n",
" )\n",
" past_states = list()\n",
" for index in range(pytorch_model.config.num_layers):\n",
" kv = (\n",
" result_dict[f\"present.{index}.decoder.key\"],\n",
" result_dict[f\"present.{index}.decoder.value\"],\n",
" result_dict[f\"present.{index}.encoder.key\"],\n",
" result_dict[f\"present.{index}.encoder.value\"],\n",
" )\n",
" past_states.append(kv)\n",
" return BaseModelOutputWithPastAndCrossAttentions(\n",
" last_hidden_state=result_dict[\"logits\"],\n",
" past_key_values=past_states,\n",
" )\n",
"\n",
"\n",
"out_dec_onnx_no_cache = decoder_onnx_inference(\n",
" decoder_input_ids=input_ids,\n",
" encoder_hidden_states=out_enc_pytorch.last_hidden_state.half(),\n",
" enable_cache=torch.tensor([False], device=\"cuda\", dtype=torch.bool),\n",
" past_key_values=None,\n",
")\n",
"are_equal(a=out_dec_onnx_no_cache.last_hidden_state[:, -1:, :], b=out_dec_pytorch.last_hidden_state[:, -1:, :])\n",
"\n",
"# check that past states are identical between ONNX and Pytorch\n",
"assert len(out_dec_onnx_no_cache.past_key_values) == len(out_dec_pytorch.past_key_values)\n",
"for (o_dec_k, o_dev_v, o_enc_k, o_enc_v), (p_dec_k, p_dev_v, p_enc_k, p_enc_v) in zip(\n",
" out_dec_onnx_no_cache.past_key_values, out_dec_pytorch.past_key_values\n",
"):\n",
" are_equal(a=o_dec_k, b=p_dec_k)\n",
" are_equal(a=o_dev_v, b=p_dev_v)\n",
" are_equal(a=o_enc_k, b=p_enc_k)\n",
" are_equal(a=o_enc_v, b=p_enc_v)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:10:09.301940Z",
"iopub.status.busy": "2022-07-01T16:10:09.301736Z",
"iopub.status.idle": "2022-07-01T16:10:09.327946Z",
"shell.execute_reply": "2022-07-01T16:10:09.327267Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"# convert ONNX inputs to FP16\n",
"previous_step_pytorch.past_key_values = tuple(\n",
" [tuple([past.half() for past in layer_state]) for layer_state in previous_step_pytorch.past_key_values]\n",
")\n",
"out_enc_pytorch.last_hidden_state = out_enc_pytorch.last_hidden_state.half()\n",
"\n",
"out_dec_onnx_cache = decoder_onnx_inference(\n",
" decoder_input_ids=input_ids[:, -1:],\n",
" encoder_hidden_states=out_enc_pytorch.last_hidden_state,\n",
" enable_cache=torch.tensor([True], device=\"cuda\", dtype=torch.bool),\n",
" past_key_values=previous_step_pytorch.past_key_values,\n",
")\n",
"\n",
"are_equal(a=out_dec_onnx_cache.last_hidden_state[:, -1:, :], b=out_dec_pytorch.last_hidden_state[:, -1:, :])\n",
"\n",
"# check that past states are identical between ONNX and Pytorch\n",
"assert len(out_dec_onnx_cache.past_key_values) == len(out_dec_pytorch.past_key_values)\n",
"for (o_dec_k, o_dev_v, o_enc_k, o_enc_v), (p_dec_k, p_dev_v, p_enc_k, p_enc_v) in zip(\n",
" out_dec_onnx_cache.past_key_values, out_dec_pytorch.past_key_values\n",
"):\n",
" are_equal(a=o_dec_k, b=p_dec_k)\n",
" are_equal(a=o_dev_v, b=p_dev_v)\n",
" are_equal(a=o_enc_k, b=p_enc_k)\n",
" are_equal(a=o_enc_v, b=p_enc_v)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"## Benchmarks!\n",
"\n",
"Finally, we will compare the performances of 4 setup in end-to-end scenarii:\n",
"\n",
"* `Pytorch`\n",
"* `Pytorch` + cache\n",
"* `ONNX`\n",
"* `ONNX` + cache\n",
"\n",
"For the comparison, we first do a sanity check by just generating a short sequence (we already have checked that output tensors are OK).\n",
"\n",
"Then we force each model to generate:\n",
"\n",
"* 256 tokens + batch size 1 (similar to `TensorRT` demo)\n",
"* 1000 tokens + batch size 4\n"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:10:09.332647Z",
"iopub.status.busy": "2022-07-01T16:10:09.332463Z",
"iopub.status.idle": "2022-07-01T16:10:09.875038Z",
"shell.execute_reply": "2022-07-01T16:10:09.874408Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"text generated by ONNX:\n",
"L'apprentissage en transfert, où un modèle est d'abord préscolaire à une tâche riche de données avant de l'être ajusté et de la mettre au point, est devenue une technique puissante dans le traitement des langages naturels (PNL). Cette efficacité s'est traduite par diverses approches, méthodologies ou pratiques. Dans le présent document, nous étudions le paysage des techniques \n",
"\n",
"\n"
]
}
],
"source": [
"def encoder_onnx_inference(input_ids: torch.Tensor, **_) -> BaseModelOutputWithPastAndCrossAttentions:\n",
" last_hidden_state = inference_onnx_binding(\n",
" model_onnx=enc_fp16_onnx, # noqa: F821\n",
" inputs={\"input_ids\": input_ids},\n",
" device=input_ids.device.type,\n",
" binding=enc_fp16_onnx_binding,\n",
" )[\"output\"]\n",
" return BaseModelOutputWithPastAndCrossAttentions(last_hidden_state=last_hidden_state.type(torch.float16))\n",
"\n",
"\n",
"def encoder_pytorch_inference(input_ids, **_) -> BaseModelOutputWithPastAndCrossAttentions:\n",
" with torch.inference_mode():\n",
" res = pytorch_model.encoder(input_ids=input_ids).type(torch.float16)\n",
" return res\n",
"\n",
"\n",
"# https://github.com/NVIDIA/TensorRT/blob/main/demo/HuggingFace/T5/export.py\n",
"class ExtT5(torch.nn.Module, GenerationMixin):\n",
" def __init__(self, config: PretrainedConfig, device: torch.device, encoder_func: Callable, decoder_func: Callable):\n",
" super(ExtT5, self).__init__()\n",
" self.main_input_name = \"input_ids\" # https://github.com/huggingface/transformers/pull/14803\n",
" self.config: PretrainedConfig = config\n",
" self.device: torch.device = device\n",
"\n",
" self.encoder_func = encoder_func\n",
" self.decoder_func = decoder_func\n",
" self.use_cache = True\n",
" self.timings = list()\n",
"\n",
" def get_encoder(self):\n",
" return self.encoder_func\n",
"\n",
" def get_decoder(self):\n",
" return self.decoder_func\n",
"\n",
" def set_cache(self, enable: bool) -> None:\n",
" self.use_cache = enable\n",
"\n",
" # from transformers library (modeling_t5.py)\n",
" def _reorder_cache(self, past, beam_idx):\n",
" reordered_decoder_past = ()\n",
" for layer_past_states in past:\n",
" # get the correct batch idx from layer past batch dim\n",
" # batch dim of `past` is at 2nd position\n",
" reordered_layer_past_states = ()\n",
" for layer_past_state in layer_past_states:\n",
" # need to set correct `past` for each of the four key / value states\n",
" reordered_layer_past_states = reordered_layer_past_states + (\n",
" layer_past_state.index_select(0, beam_idx),\n",
" )\n",
"\n",
" assert reordered_layer_past_states[0].shape == layer_past_states[0].shape\n",
" assert len(reordered_layer_past_states) == len(layer_past_states)\n",
"\n",
" reordered_decoder_past = reordered_decoder_past + (reordered_layer_past_states,)\n",
" return reordered_decoder_past\n",
"\n",
" def prepare_inputs_for_generation(self, input_ids, past=None, use_cache=None, **kwargs) -> Dict[str, torch.Tensor]:\n",
" params = {\n",
" \"encoder_hidden_states\": kwargs[\"encoder_outputs\"][\"last_hidden_state\"],\n",
" }\n",
" if past is None: # this is the 1st inferred token\n",
" self.timings = list()\n",
" if not self.use_cache:\n",
" past = None\n",
" if past is None:\n",
" params[self.main_input_name] = input_ids\n",
" params[\"enable_cache\"] = torch.tensor([False], device=\"cuda\", dtype=torch.bool)\n",
" else:\n",
" params[self.main_input_name] = input_ids[:, -1:]\n",
" params[\"enable_cache\"] = torch.tensor([True], device=\"cuda\", dtype=torch.bool)\n",
" params[\"past_key_values\"] = past\n",
"\n",
" return params\n",
"\n",
" def forward(\n",
" self,\n",
" input_ids: torch.Tensor,\n",
" encoder_hidden_states: torch.Tensor,\n",
" enable_cache: torch.Tensor,\n",
" past_key_values: Optional[torch.Tensor] = None,\n",
" **_,\n",
" ):\n",
" start_timer = time.monotonic()\n",
" dec_output = self.get_decoder()(\n",
" decoder_input_ids=input_ids,\n",
" encoder_hidden_states=encoder_hidden_states,\n",
" enable_cache=enable_cache,\n",
" past_key_values=past_key_values,\n",
" )\n",
" self.timings.append(time.monotonic() - start_timer)\n",
" return Seq2SeqLMOutput(logits=dec_output.last_hidden_state, past_key_values=dec_output.past_key_values)\n",
"\n",
"\n",
"model_gen = (\n",
" ExtT5(\n",
" config=pytorch_model.config,\n",
" device=pytorch_model.device,\n",
" encoder_func=encoder_onnx_inference, # encoder_pytorch_inference\n",
" decoder_func=decoder_onnx_inference, # decoder_pytorch_inference\n",
" )\n",
" .cuda()\n",
" .eval()\n",
")\n",
"\n",
"torch.cuda.synchronize()\n",
"with torch.inference_mode():\n",
" print(\"text generated by ONNX:\")\n",
" onnx_tokens = model_gen.generate(\n",
" inputs=input_ids,\n",
" min_length=10,\n",
" max_length=128,\n",
" num_beams=2,\n",
" no_repeat_ngram_size=2,\n",
" )[0]\n",
" print(tokenizer.decode(onnx_tokens, skip_special_tokens=True, clean_up_tokenization_spaces=True))\n",
" print(\"\\n\")"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:10:09.879424Z",
"iopub.status.busy": "2022-07-01T16:10:09.879232Z",
"iopub.status.idle": "2022-07-01T16:10:11.976198Z",
"shell.execute_reply": "2022-07-01T16:10:11.974991Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"seq len: 128 / # beam (batch size): 1\n",
"ONNX: 0.5, including inference: 0.4 (89.4%)\n",
"ONNX + cache: 0.4, including inference: 0.4 (88.7%)\n",
"seq len: 128 / # beam (batch size): 2\n",
"ONNX: 0.6, including inference: 0.4 (77.3%)\n",
"ONNX + cache: 0.6, including inference: 0.5 (77.3%)\n"
]
}
],
"source": [
"def print_timings(name: str, total: float, inference: float):\n",
" percent_inference = 100 * inference / total\n",
" print(f\"{name}: {total:.1f}, including inference: {inference:.1f} ({percent_inference:.1f}%)\")\n",
"\n",
"\n",
"all_timings: Dict[str, Dict[str, List[float]]] = defaultdict(dict)\n",
"for seq_len, num_beam in benchmark_tasks:\n",
" timings_key = f\"{seq_len} / {num_beam}\"\n",
"\n",
" print(f\"seq len: {seq_len} / # beam (batch size): {num_beam}\")\n",
" task = \"ONNX\"\n",
" with nvtx.annotate(\n",
" task, color=\"red\"\n",
" ): # nvtx is for Nvidia nsight profiler, you can remove the line or install the library\n",
" model_gen.set_cache(enable=False)\n",
" # warmup\n",
" model_gen.generate(inputs=input_ids, max_length=10, num_beams=num_beam, min_length=10)\n",
" start = time.monotonic()\n",
" model_gen.generate(inputs=input_ids, max_length=seq_len, num_beams=num_beam, min_length=seq_len)\n",
" total_time = time.monotonic() - start\n",
" print_timings(name=task, total=total_time, inference=sum(model_gen.timings))\n",
" all_timings[timings_key][f\"{task}\"] = model_gen.timings\n",
"\n",
" task = \"ONNX + cache\"\n",
" with nvtx.annotate(task, color=\"red\"):\n",
" model_gen.set_cache(enable=True)\n",
" # warmup\n",
" model_gen.generate(inputs=input_ids, max_length=10, num_beams=num_beam, min_length=10)\n",
" start = time.monotonic()\n",
" model_gen.generate(inputs=input_ids, max_length=seq_len, num_beams=num_beam, min_length=seq_len)\n",
" total_time = time.monotonic() - start\n",
" print_timings(name=task, total=total_time, inference=sum(model_gen.timings))\n",
" all_timings[timings_key][f\"{task}\"] = model_gen.timings"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:10:11.979420Z",
"iopub.status.busy": "2022-07-01T16:10:11.979224Z",
"iopub.status.idle": "2022-07-01T16:10:12.031384Z",
"shell.execute_reply": "2022-07-01T16:10:12.030741Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"del enc_fp16_onnx, enc_fp16_onnx_binding, dec_onnx, dec_onnx_binding\n",
"\n",
"pytorch_model = pytorch_model.cuda()\n",
"model_decoder = model_decoder.cuda()"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:10:12.035842Z",
"iopub.status.busy": "2022-07-01T16:10:12.035624Z",
"iopub.status.idle": "2022-07-01T16:10:12.949942Z",
"shell.execute_reply": "2022-07-01T16:10:12.949379Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"text generated by Pytorch:\n",
"L'apprentissage de transfert, où un modèle est d'abord pré-instruit sur une tâche riche en données avant de l'adapter à une task aval, est devenue une technique puissante dans le traitement des langues naturelles (LPN). L’efficacité de cet apprentissage s’est traduite par une diversité et il y avait toutes les approches, les méthodologies ou la pratique. Dans ce document, nous \n",
"\n",
"\n"
]
}
],
"source": [
"torch.cuda.synchronize()\n",
"with torch.inference_mode():\n",
" print(\"text generated by Pytorch:\")\n",
" pytorch_tokens = pytorch_model.generate(\n",
" inputs=input_ids,\n",
" min_length=10,\n",
" max_length=128,\n",
" num_beams=2,\n",
" no_repeat_ngram_size=2,\n",
" )[0]\n",
" print(tokenizer.decode(pytorch_tokens, skip_special_tokens=True, clean_up_tokenization_spaces=True))\n",
" print(\"\\n\")"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:10:12.954392Z",
"iopub.status.busy": "2022-07-01T16:10:12.954209Z",
"iopub.status.idle": "2022-07-01T16:10:17.651350Z",
"shell.execute_reply": "2022-07-01T16:10:17.650735Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Pytorch: 1.2, including inference: 1.1 (96.5%)\n",
"Pytorch + cache: 1.0, including inference: 1.0 (95.5%)\n",
"Pytorch: 1.4, including inference: 1.3 (92.3%)\n",
"Pytorch + cache: 1.1, including inference: 1.0 (89.5%)\n"
]
}
],
"source": [
"for seq_len, num_beam in benchmark_tasks:\n",
" timings_key = f\"{seq_len} / {num_beam}\"\n",
"\n",
" # monckey patching of forward function to add a timer per generated token\n",
" old_fw = pytorch_model.forward\n",
" timing_pytorch = list()\n",
"\n",
" def new_fw(self, *args, **kwargs):\n",
" timer_start = time.monotonic()\n",
" res = old_fw(self, *args, **kwargs)\n",
" torch.cuda.synchronize() # makes timings correct without having significant impact on e2e latency\n",
" total_time = time.monotonic() - timer_start\n",
" timing_pytorch.append(total_time)\n",
" return res\n",
"\n",
" task = \"Pytorch\"\n",
" with nvtx.annotate(task, color=\"orange\"):\n",
" pytorch_model.config.use_cache = False\n",
" with torch.inference_mode():\n",
" with torch.cuda.amp.autocast():\n",
" # warmup\n",
" pytorch_model.generate(inputs=input_ids, max_length=10, num_beams=num_beam, min_length=10)\n",
" pytorch_model.forward = new_fw.__get__(pytorch_model)\n",
" start = time.monotonic()\n",
" pytorch_model.generate(inputs=input_ids, max_length=seq_len, num_beams=num_beam, min_length=seq_len)\n",
" total_time = time.monotonic() - start\n",
" pytorch_model.forward = old_fw\n",
" inference_time = np.sum(timing_pytorch)\n",
" print_timings(name=\"Pytorch\", total=total_time, inference=inference_time)\n",
" timing_pytorch_no_cache = copy(timing_pytorch)\n",
" all_timings[timings_key][f\"{task}\"] = copy(timing_pytorch)\n",
" timing_pytorch.clear()\n",
" torch.cuda.empty_cache()\n",
"\n",
" task = \"Pytorch + cache\"\n",
" with nvtx.annotate(\"Pytorch + cache\", color=\"green\"):\n",
" pytorch_model.config.use_cache = True\n",
" with torch.inference_mode():\n",
" with torch.cuda.amp.autocast():\n",
" # warmup\n",
" pytorch_model.generate(inputs=input_ids, max_length=10, num_beams=num_beam, min_length=10)\n",
" pytorch_model.forward = new_fw.__get__(pytorch_model)\n",
" start = time.monotonic()\n",
" pytorch_model.generate(inputs=input_ids, max_length=seq_len, num_beams=num_beam, min_length=seq_len)\n",
" total_time = time.monotonic() - start\n",
" pytorch_model.forward = old_fw\n",
" print_timings(name=\"Pytorch + cache\", total=total_time, inference=sum(timing_pytorch))\n",
" all_timings[timings_key][f\"{task}\"] = copy(timing_pytorch)\n",
" timing_pytorch.clear()\n",
" torch.cuda.empty_cache()\n",
"\n",
"pytorch_model = pytorch_model.cpu()\n",
"model_decoder = model_decoder.cpu()\n",
"torch.cuda.empty_cache()"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"## Benchmark analysis\n"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:10:17.657077Z",
"iopub.status.busy": "2022-07-01T16:10:17.656486Z",
"iopub.status.idle": "2022-07-01T16:10:18.591997Z",
"shell.execute_reply": "2022-07-01T16:10:18.591253Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [
{
"data": {
"text/plain": "<Figure size 864x576 with 4 Axes>",
"image/png": "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/cMNyOyPU8diPs5rcgvuECScoprXua/HVcCL4XZirX0J5+/tWfc4XseZajYaJxC6kKiIiIikWE5trWY0ERFJNONM+/m0tfaRVLcl2Ywx7wL3WGvjVUMCd4aMJ4Ado8kGiWG7M4ly2s847W9HnHOzYzL215gYZyrVB621u6S6LSIiDfHDD5NndumyRcpmxhBpqLlzZ7QbMKB/j2DLNGRDREQSbQLwSTw36E4fOSie20yhq1PdgGxkrZ0CKBghIiKSxhSQEBGRhLLW3prqNqQra+23qW6DiIiISKooICEikgTW2r1S3QaJzFrbI9VtEBEREWksVNRSREREREREEqq2tpaXXnq+7YgRx271t7/tNmDffXcfcNppJ5kPPnivVeC6V1xxcY/dd99h4DXXXLHJVOsnnnjM1ldccXEP7/eXXnq+7e677zDw6KMP7VtTU7PRurfcckOXww4bsq33+yuvvNh28OBBAydNmtjcv15ZWVnuwQfv1+/SS0f3jMexptrs2TMLd999h4EfffT+Juc23SggISIiIiIiIgl13XX/6nb33WN69O691dqrrrp+xuWXXz2jffsO5ddd968tH3zw3o7BnjNhwsdt58+fVxDN9hcsmN/k3Xffah1unSOPPGZZ795brRkz5tZu1dV/zSx+993/2ay8vDx39OhL5sR0UNJgCkiIiIiIiIhIwowf/07Jhx+OLz3zzL/PvuKKa+bsuefeq/fZZ7/VN95428zDDjty4dNPj9tsypTJzfzP2WyzLhuaNWtWPW7co0GDFYG23rpv2XPPPdUp0noXX/zPWXPnzm761FOPtwf46acpTcePf7v98OEj5nfo0LEqluM67LAh27700vNtY3mObEwBCREREREREUmYV155sX2HDh3Ljz12+JLAZaeffvbCJk2aVr/44nPt/Y8XFhbWDBt25KKPPnq/3dKlSyLWPjzppFMWzJo1s2mkYQpbbdVnw9Chhy5+5pknOs+fP6/g9ttv6t6tW/d1w4efvDj2I6uf5557ut2xxx7eZ6+9dtn+oIP22W706HM3X7VqVR7Ad9992/y880ZtOXTovv322We3Accff2Sf1157uU3gNubMmVV4ySX/6DlkyF7b7bPPrgOOO+6IPq+//spG661fvz73mmuu6L7ffoP7H3LI/v3Gjr2jc+Cwll9/ndrknHPO2HLffXcfsO++uw+48MJzN1+0aGHSak0qICEiIiIiItII5JSvym06+aF2LSb8c7Omkx9ql1O+KuH9waqqKqZN+73FoEE7rczLy9tkeatWrar79t2m7Jdffm4RuOz4409aXFCQXztu3KMdIu1nwICBa7fZpt/qp58eFzFL4uyzz5/ftGnTmrPPPm2rP/6Y3vyCCy6ZHaxtiXDvvXd1uu++u7pvs02/NVdf/e/p5547elbz5s2r165dkwswf/68wr59t10zevSls6677qbpu+02eMWdd97W4403Xq0LNixZsjj/rLNO3Wr69GnNTz111Nxrr71p+v77H7R00aKFhf59PfroA12aNm1afeWV1/2x5577LHvxxec6vfPOm3XDWv74Y0bReeeN2qqysiL34osv/3P06Ev/nDNndtOLLjq/V21tbVLOh2bZkIxmjBkHbGOt3SGG57QHzgbGWWtnJqhpCWOMOR24HOgKfJHJszcYY3oAfwKHWGvfjvG5ZwCLrbWv12O/44jxuhGRzKPPiMR8RhhjLgG+tdZOCHi8FjjXWntPvPfpbn8YcDOwBTA/2llxjDHXAOdYa9u5v+8FfAJsa639ORFtFUlHBbMntGj13um9qK0lp3pDbm1ek5rm39zWddWBD0+r7LbXmkTtd9mypflVVZU5HTp0qgi1Tvv2HSt+/PGHTTIbiouLa4YOHbb4zTdf7XDqqWcubN26dXWw53tOOmnkwksuuaD3559/Wjx48J5lodZr0aJFzVFHHbvgoYfu67bHHnsvGzhw0NpojqWqatMRHbW1NRs9npeXR05OTtDnr1y5Mu+VV17oOHTooYsuu+xfc73HDzzw4JXez4ceeviKv7Zdy84771q2ZMmSwnfeebPdsGFHLAd48snHOqxbtz7vkUee+rVjx06VAMGOd+uttym79NIr5wLsuefeq7//fmKrzz6b0PqQQw5bAfDww/d1btWqpOruux+aVlhYWAtgzNbrTz75uG0+/vjDVvvuu/+qaM5LQyggIY1Re+BqYAIwM6UtiZExpiNwP3AP8BKwIvwzstoZwM/A6yluh4hkF31GRHaJu48JCdr+JowxecCTwHvA6UBUnQcRceSUr8pt9d7pvXKq1tdlRORUb8gFaPXe6b2WnTzpx9qiljWht5A6J544ctGbb77a4emnx7U/99wLFoRbd9ddB5f17r3VmieffKxTuIBEdXU177//XrucnBys/bVFRUVFjtchD2evvXYeGPjY2LFjeowdO6aH9/v551808+ijj10W7Pnffz+xeUVFRe6hhx4edDnAihUr8u67767O3377dcmKFcsLvSEWrVu3qfTWmTJlcnH//gNWe8GIUHbccafV/t+7du22fsmSJXVZFFOm/Fi89977LsvNza31girdu/cob9eutPzXX6c2U0BCRAJtCeQBj1lrpzRkQ8aYptba9fFploiIpIFs/ozoBLQEnrXWfpHqxohkmia/vtCGUCn4tbU0+fWFNuv7n740Eftu27ZdVX5+Qe2iRQsKQ62zePHCwtat2wTNoGjdunX1kCFDF7/99hsdRo48fVGk/Q0fPmLBVVf9s9fEid80D7XOU0893n7u3NlNr7jimhk333z95o888kCHs88+b2Gkbd9zz0O/+n+/8spLttx//4OW7rHHXiu9x7p1614e6vmrVq3MB+jQoWPIQMI111zeY9o02+LYY4fP33zzLTe0aNGi+pVXXiydOPHrEm+dsrKy/F69zLpI7S0ubrlRRkl+fn5tZWVFXVBqzZqy/Ndff7nj66+/vEnh0CVLFoV8veJJAQnJKsaYTsANwF44X17mAC8C11lrK9whAj+5q39ijAHAWpvjPr8NTjroMKAV8D1wgbX2G98+aoF/AB1w7tLU4tyJGm2tLfet193d1n5AM2A6cLO19lljzLfAL9bakwPaPw7Yzlo7IMixXYNz1w7gR7ftI62144wx7YA7gIOBpsC3wEXW2u98z58JvAKsBM502x9yGiVjzGnABThfcBcC91prb/Ut3wX4JzAI50viNOA2a+0zAdsJeR58qzUzxjwIHAuUAY8C11prg0bqjTETgIHAQGPMCPdh71zkAf8CTnGPcTpwQ8D+ArdXCDwP7ADsY62dbozpBtwK7A80AT4HzrPWWvc5PXCGm/wf8Ldo2y4iqaPPiIZ/RrjrtQWuNsZ4+9vbN3wjzxhzY4RjD/v+GmSfJwOPu7++4R7btcA4ggz7i8ewPGNMAXATcAzOuVgGfAP8n7W2ItrjMMZ0BR4E9gYWAdcDQ4B23nCaYO0NNqTRGJOLk51yGs6QnFk4n29P+J43AVgKvApch5Px8yVwurV2rm+9pjjn8P+AjsB84Hlr7T9964T9HiCZJW/ln0VeRkSgnOoNuXmr/ixK1L7z8/Pp1av3mu+++7akpqZmbm7uxs1YvXp17i+//Fy84467rAy1jREjTl307rtvtX/22SdLI+1vn332W/344w+vGzfukU7duvXYELh80aKF+c8880Tngw8etnjIkKErf/31l4Uvv/xCp0MOGba8a9fuIYeVAPTvv/1GQYD8/PzaTp06lwc+HkqrViVVbhsK2rZtt8n4jw0bNuR8//13JaNGnTP7hBNG1BUAffnl5zcaA1JcXFy1fPmyqKZDDad58+bVO+20y4phw47cJBjVpk2bmGYcqS8VtZRs0w5YDozG+cC/DRgJ3O0uXwCc4P78d2AX9x/GmCLgI2Bf4GLgMGAJ8JGbBut3IdAZGO7u40zgfG+hOwb5K5zO+kXAITgd1a7uKo8CRxljWvie0wI4CngsxLE94rYZ9xh2Ad5xf38dOMDd1//h/G1/YozZMmAbxwN74oyP/r8Q+8EYczFO2u/rOF9g7wf+bYw5x7dad5wvOae6x/cK8Lgx5rgYzoPnVmCNe/xPA1e5P4dyNvAb8C5/vYbeubgOuAJ4CDjUbeMz/nYFHGsT4DVgO2CwG4xoA3wBGGAUzhfS5jjXQtMGtl1EUkefEQ3/jDgcWOW20Ts/38dw7LG8v3reAY5wf77I3ecjIdaNl3/inMd/4QSN/oFz3HkQ3XEYY3KAN4BtcD4rR+Oci13q2aa7gStxPt+G4nx2PWaMOThgvZ2Ac3BeizOA7d3nENCus4B7gYNwglntfOtE8z1AMkh1Sc/y2rwmQW+W1OY1qalu1TPkXf14OPLIYxYvXLig6IUXnmkXuOyRR+7vtH79+rxjjjku5CwXpaXtq/bd94Clr7/+SseKioqIfdjjjx+x4Mcff2g1Y8a0ZoHLxoy5pWvTpk1rzjrrvPkAo0ads6Bly+KqO+64JfD7adxtv/2gtYWFhTVvvfVG0KlCy8vLc2trayksLKx7rcrKynInTvy2xL/edtsNKPvxxx9aLl68qEEJBttuu93q2bNnNd1uuwHr+vfffqN/3br1CBuciRdlSEhWsdb+hPNlBQBjzJc440wfM8aca60tN8Z4aay/WGu/9j19OM6Xhr7W2mnu8z8CLM6H+sW+dWf67ly9b4zZDefLknfn4AKcu2cDrbXeWLePfc9/DhgDHM1fd32OwbkbFfROvrV2rjHmF/fXKV4hLmPMEGA3YC9r7afuY//FGft8Mc6XQb+DrbWbRIs9xpiWOF9MrrfWXus+/KExphlwpTHmfmtttbX2ed9zcoDPgC44d8Wei/I8eD6z1l7o29cQnPP5Yohz8YsxZi2wxP8aul8Q/+G2/Xr34feNMV2Aa3zt8tZvBrzptnsPa+08X7ubA/2ttcvddb/EOaen4HyBq1fbRSR19BnR8M8Ia+0PxpgqYG7A+Ynl2KN9f/X2ucQY88Nfvzr7dbMIEmVHnOEhT/ge87+vR3McBwIDgJ29LBpjzCRgBk5WYdTc4NFZOFkvXps+crN+rgb8haFbAkOttSvc53YE/uMbhrM/TpBlmLX2Td/znnTXj+p7QCztl9TbsPX/LW/+zW3BO9w5OWzY+v+WJ3L/Q4YMXfnNN18teeCBe7r9+eefTXfffY+V1dVVOR9//EGbCRP+2/bEE0+e169f/7BZBiNGnLrwgw/eK129elX+llv2CltHZsiQg1aOG/fwhl9++bm4Xbt2dR3rL774rPjLLz9v889/XvVHixYtagCaNm1aO2rUuXOuv/7qLT755KOWe++97+rQW26YkpKS6mOOOX7BM888sVlVVWXOrrvuvqqioiL3f//7otWZZ/59fufOm1VuvvkW65555snOzZu3qM7JyeX555/q2KxZs+r169fVBWJOOumURZ988nHbs846davjjjtxQceOnSr+/HNGk/Xr1+eeccbZEYe1eE4//ez5Z5116tbnnHPGlgcddMiy1q1bVy1atKjgu+++aTl06KHLdt11cMg6HPGigIRkFbdjfD7OHYGeOCmUnm44KbGh7AtMAv40xvj/Nj7FSeX3+yDg918C1tkHGO/7orkRa+1qY8zLwMn89WXzZOBNa23IIjch7Igz28Snvu2vNca8DewesO7H4b5ounbB+ZL1UsB5+C/OnaIuwCxjTGucdM9hwGa4d42Aeb7nhD0PPsHOZ7cIzwlmG5zU55cCHn8BGGeMKbXWeulvzYHxQAmwp7XW/+a9L/AhsNp3Dspwro9oroX6tF1EEkyfEXXbb8hnRCSRjj2W99dUmgycZYxZhPNZ8ZO11j8AP5rj2BFY5B/SY62d5QYlYvU3oAZ4LeD6+xg4zhiT5wsSTPSCES4vULUZzjW+D7A8IBjhF9X3gHocg6RQbVGrmlUHPjwtcJYNcnJYdeDD05JR0PKqq/49u0+fbda+/fbrpR999H673NwcevTYfN1VV/17+v77HxixeOJmm3Wp3HPPfZZ9/PEHm2RZBMrJyeG4405ccPvtN/X0HquoqMi5667bu2277Xarhw49dKOiv0OGDF359ttvrLrnnju77brr4KlFRUUJm/Ny1KhzFrZs2bLq9ddf6fDhh+NLmzVrXt2nT9+yFi2KqwGuuebGP2655d/db7/95p4tWrSoOvjgYYs3bNiQ9+67b9YNV2nXrrTqvvse/m3s2DFdHnro3q5VVVU5HTp0Kj/uuOGRvnNvZMste5Xfd9/Dv91//z2dx469o3tFRUVu69ZtKvr161/WvXvPhn4eREUBCck2/8BJEb0F50viCpyU2HvZ+ItnMO2AnYFgRWZmBPy+MuD3ioDttwUmRtjfo8AEY8zmQA4wGCdtMladgGApbouANkEei8R7k58aYrk3bnUczvn6N86XndU4d2+G+daN5jxA5PMZLW/e6cDj9H5vg5NiDU5KcS+c8beB63vXQrCU5cAMj5UBv9e37SKSeP9AnxGe+n5GRLIy4PfAY4/l/TWVrscJAJyNc73MM8bcZq29y10ezXF0JPi5XwwUx9iedjiB/1Cdtk6AVyNiZcAy7+6w9zq0xRmeFG5fEPl7gGSYym57rVl28qQfm/z6Qpu8VX8WVbfqWb5h6/9bnqzZNXJycjj66GOXhZqBwu+GG26bGezxa6+9cda119640fUXapuHHXbk8sMOO7Iu86OwsLD2pZfeDHVdc889D4ULSgf1+uvjf4q81qaOP/6kpccff1LQIqKbb75F+YMPjvs98PHzzhs93/97167dK2677a4/gm2jW7ceFV988d0mwc9g57VXL7NhzJi7g24nGRSQkGxzNPCytfYK7wFjTJ8on7sc+A6nUx0o1nF1y/ircxyUtfYzY8w0nLteOTgFpQLvLEVjAU7RqkAdcI7JL5por/ecgwn+5dS6dRcOBv5urX3AW+AW3PKLeB7izPuC1d7dt6eD+7//fEwD7sLJnFhorb3ft2w5zlCOfwfZR8JT10QkYfQZ8Zf6fkY0VDzfX727d4GV4FvH2qhAbqbIVcBVxpheOHUi7jTGWGvteKI7joUEP/ftAf8MJhuIfAzLgSqc4TfBOo8hx94HEen6i/g9IIZ9SZqpLWpZk6jZNETqQwEJyTZN2fSL4QkBvwfeKfB8jDOucra1NpYP9mA+Bs4zxnQIcvfd7zGcuy8AT9ZzTOY3wLXGmD2stZ9BXW0Er+BVrL7C+aLU2Vr7TrAVjDGtcIqi+aumF+MUkfR/oY32PNRHsEyEn4F1OJ2O63yPHwP87huuAYC19im3UNw9xpgya+3TvnYfA0y16TXtnYg0jD4jaPBnBDQsEyye76+LcTJWtvYecN/TdyWOd/CttdOMMRfhFA3tgzOEI5rjmIgzG8lOvhoS3XCKTH7pW28u0MMY08Q3ZGb/gG39FydDopW19sMGHtLHwCXGmIP9s5P4RPweICISLwpISLb5EOdL3jc4KbQn4ExX5Tcb54N2hDFmFVDpTn32JM4dkAnGmNuBP3DSGncEFlpr/xNDO/4DnAR8boy5AWdqua2B5gFTZj2Bkxaaz1/jhGNirX3fGPM/4AVjzGU4dz4uwvnifVs9trfSONPH3WWcaek+wwk+9MaZ2u1wa+0qY8xEnDtHq3Hu1lyGk0ra0re5aM9DffwGHGCMOQDnmP+01i4zxtyJU3SrCudu5hE4ac5BZ9mw1t7vfoF93Bizxlr7Ok4xueHAf40xd+PUxeiAU33+C2vtc8G2JSJpT58RDfyMcP0GDDXGjMeZZchaa6PNbojb+6u1tsYY8wZwgTFmFs5QhQvZOPugXowxr+HUg/jB3d5ROK/DZzEcx7vAjzi1GC7FCYZdy6bZDK/jBNEfMc4UoANwCmP6j9UaYx4AnjfG3Irz+dYE6Av0ttaeFsPhfQi8DzxrjLkOZ5aUTjjFnc+M5ntADPsSEQlL035KtrkOpzr59e7/FcB5/hXcuw+nAwNxxhBP9D2+N84H9bU4qbF34dQZ+DaWRrh34nfD+SJzJ0716zNwvuj611uIc/fqS2vtJmPFYnCY2+47cQo65gD7WGtjHgvntutWt70H4kwN9hzOF/fPfasdj/OF/Emc8/SK+7N/O1Gdh3q6HvgVp+r5RJxp88BJsb0JJ636bWAPYLh/VpBA1trb3Oc8b4zZz1q7FGds8G84HYcPcCrEtwKmhNqOiKQ9fUbE4TMCZ3aOtTjTcU7EOVdRScD76zk42Qb34dQCeQ4nm6Ch/odz3p7F+RwcCBzpBqeiOg63COahOHWWHnPXuwcnA6GOOyPKKTjFJN/ECWqMDNKmv+MMETkJJ9gxDifT5bMg64bktutwnKlA/wG8h/M3sdS3TjTfA0REGiyntjYZwwVFJBh3msp5wDnW2kdT3R4REUkf+ozITu4MKu2stXului2SGX74YfLMLl22UN0HyVhz585oN2BA/x7BlmnIhkgKuPUW+uBMP1eGc+dBREREnxEiItJoKCAhkhoDgU9wim6dZK1dl+L2iIhI+tBnhIiINAoKSIikgLV2As4YXhERkY3oMyK7WWuPSnUbRETShYpaioiIiIiIiEjSKSAhIiIiIiIiIkmnIRsh1NTU1FZXN3wGkry8HOKxnXSTrccFOrZMla3Hlq3HBelzbAUFeUuB0lS3wxOPz590ObeJoGPLPNl6XKBjy1TpcGzp9tmTaGPHjun84ovPdvJ+Lykpqezde6u15547em7PnpuXR7ON5557ql3r1m2qhgwZujJhDQ0wduyYzu+++2bp+PETfkzWPhsbBSRCqK6uZeXKhteQKilpFpftpJtsPS7QsWWqbD22bD0uSJ9jKy0tnpXqNvjF4/MnXc5tIujYMk+2Hhfo2DJVOhxbun32JEPTps2qb7759mkAc+bMKXriiUc7X3DB2b2feeaVqc2bN6+J9Px3332rtFu37uuTGZCQxFNAQkRERERERBIqLy+3duDAHdcCDBy449pOnTpXXHjhueaTTz5qdfDBw1Yksy3V1dVUV1fnFBYWZmcaUAZJekDCGJMH3AycDDQBPgDOtNYuDbH+EOAOYHNgBjDaWvuBb/kjwC6AAcZZa09ryP5ERERERESy0ZrKstz35r7dZt7auUWbNe9SfmCXg5e3KCiOmJ2QCP369V8L8OeffzQZPHjQwKeffumn7t17VHjLZ82aWTh8+NHbXnnltTNefvn5Dn/++UezP//8o9nuu+/QFuD88y+aefTRxy6rrq7m3nvv6vzRR++3Xb16VUGHDh3LjzvuxAWHHXbkcm9bV1xxcY/Zs2c1PeGEEQsef/zhzRYuXFB0yy3/+X3nnXdd895775Q899yTnebMmd20sLCoZsste6257LIrZ3ft2r2uLT/99GPT22+/ufucObOadurUufzcc0fP3nnnXdck83xlq1RkSFwGDAN2ApYBjwFPAQcGrmiM2Rx4FTgDeBE4GnjNGNPXWjvTXW0K8BJwZkP3JyIiIiIiko0mLvmmxdXf/7NXTW0tFTXluYW5RTWP//5w12u3v2naoNKdkt65njNnVhFAp06dK0pKWle+8car7c47b/R8b/kbb7zSrri4ZdXf/rb/yh49em648spLt+jQoWP5yJGnLQDo3r1nOcDYsXds9vrrr3Q45pjjF/Ttu83aTz75uPXtt9/UMycnh2HDjqgLSixZsrjw4Yfv7zJ8+Ij5bduWVnbt2q381VdfbDNmzK09d9119+Unnjhyfm1tLd99N7HlsmXL8r2AREVFRe6NN17b84gjjl7Utm27yieeeKzztddescUrr7zzU7NmzVISzMkmqQhInAFcZ639A8AYcwkw3RjT3VobOJZqBDDJWvu0+/szxphR7uPXAlhrx7rbOS4O+xMREREREckqayrLcq/+/p+9NlRvqJtlsaKmPBfg6u//2evFfd78sUVBi4R3rquqqgCYOfPPottuu6lbkyZNanbffY/Vc+bMLvrvfz9se+65F8zPycmhtraW//73w7Z77rnPsvz8fIzZekOTJk1qWrVqVeUN+wBYsWJ53ltvvdH+qKOOXfD3v5+/AGCvvf62eunSJQVPPfV4Z39AYs2aNfm33Tb292237bceoKamhscee7jLoEE7rbz11jv/9Nbbf/8DV/nbXFFRkfv3v/9jzu6771EGUFraofKss07p8803/2ux9977rk7oCWsEkjrtpzGmBOgGTPIes9bOAFYD2wV5ynb+dV3fh1g3HvsTERERERHJKu/NfbtNTW3wcgk1tbWMn/t2m0S3Yc2aNfl77bXzwL322nngyScft83ixYuKLr/86hkdO3aqPPzwo5YuW7a08H//+7wY4MsvPy9eunRp4bBhh4cdZv/bb782ragoz91//yEb1aDYe+99VyxcuKBo6dIldTfgW7duU+kFIwCmT/+9ycqVKwoOOuiQsPvIz8+v3XXX3cu8343ZagPAokWLCmM7AxJMsjMkit3/VwU8vhJoGWL9YOv2TdD+6uTl5VBS0izK3YTbTm5ctpNusvW4QMeWqbL12LL1uCC7j01ERCSdzFs7t8jLiAhUUVOeO3ft3KJEt6Fp02bVt9125+85OTmUlrav7NSpc2VOTg4APXr0rNh6675lb7/9Ztvddtuj7J133mi3xRZbrt1qqz4bwm1z6dLFBQClpe0r/Y+3bdu2EmDFihV57dqVVgG0atVqo3VWrFiRH+y5gZo0aVKdm/vXqfMKYVZUlOdEd+QSTrIDEl5kqVXA4yU4WQvB1o923Xjsr46m/QwvW48LdGyxmrriJ56c9hgn9TqFvq23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/gKAvfb62+qlS5cUPPXU4539AYk1a9bk33bb2N+33bbfeoCamhoee+zhLoMG7bTy1lvv/NNbb//9D1zlb3dFRUXu3//+jzm7775HGUBpaYfKs846pc833/yvxd5777s61PFWV1dTW/tXH722Fmpra+rOA0Bubi7+TAa/aNo3ePCeZYMH71nmbL+WHXfcpWzDhg2577//bqkXkHjttZfazps3t+l99z36i3fsQFng/kpL21fccMNtM71zOHXqTy2+/PLz1l5A4oknHutQXl6e98QTz/3SunWbaoCBA3dcc8wxh2778svPtzvxxJExF8RUQCJD6I1W4inYnQSlO4pHwamQVuAEGJoCD4Vb0RjTCngPuB0YDOwBvGaMmWGt/SrRDRURyUaaOaP+asrKcldfeWkvNmz4q+dbXp4LsPrKS3u1ee3dH3NbtEjonf41a9bk77XXzgO939u2bVdx+eVXz+jYsVMlwOGHH7X01Vdf7PS//31evNtue5R9+eXnxUuXLi0cNuzwpeG2+9tvvzatqCjP3X//IRvVodh7731X3HnnbT2WLl2S365daRVA69ZtKn0dcqZP/73JypUrCg466JCw+8jPz6/dddfd6zrwxmy1AWDRokWF4Z535JFDt126dOlG6/z22y8tXnrp+boZR4455vgFoWpKRNO+DRs25Dz00L2dJkz4b5tly5YWVldX53jLqqqqyM/P54cfJrXs3r3HOv+xB7P99jtsFFzp1q37hhkzpjf3fp88eVLLfv36ry4ublntBVWKi4ure/bcYp21vzYHFJAQkciCBbgyLd0xUqdZner6U3AqOGvt+wDGmL2iWP0IYB1wq7W2FvjQGPMacAaggISISJRCZUVIbMrffbMNtSGy6WtrKX/nzTZN/+/4sJ3yhmratFn1bbfd+XtOTg6lpe0rO3XqXJmTU9d3pkePnhVbb9237O2332y72257lL3zzhvttthiy7VbbdVnQ7jtLl26uACgtLR9pf/xtm3bVgKsWLEizwtItGrVaqN1VqxYkR/suYGaNGlS7c9i8AphVlSU54R8EnDjjbdPr6ioqFvn9ttv6r7FFr3WHX74UXUd9w4dOobcdzTtGzPmli4fffRBu2OPPWH+Vlv1WdeyZcvqCRP+W/Lyy8932rBhQ26LFi1qVq9end+6dZuwxwhQXFxc5f89P7+gtrLyr/aX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\n"
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"sns.set_style(\"darkgrid\") # darkgrid, whitegrid, dark, white and ticks\n",
"plt.rc(\"axes\", titlesize=15) # fontsize of the axes title\n",
"plt.rc(\"axes\", labelsize=14) # fontsize of the x and y labels\n",
"plt.rc(\"xtick\", labelsize=13) # fontsize of the tick labels\n",
"plt.rc(\"ytick\", labelsize=13) # fontsize of the tick labels\n",
"plt.rc(\"legend\", fontsize=15) # legend fontsize\n",
"plt.rc(\"font\", size=13) # controls default text sizes\n",
"\n",
"colors = sns.color_palette(\"deep\")\n",
"fig = plt.figure(constrained_layout=True, figsize=(12, 8))\n",
"subfigs = fig.subfigures(nrows=2, ncols=1)\n",
"\n",
"fig.supxlabel(\"seq len (# tokens)\")\n",
"fig.supylabel(\"latency (s)\")\n",
"fig.suptitle(f\"Small seq len and greedy search on {model_name} don't tell the whole (inference) story...\")\n",
"for row, (plot_name, timings) in enumerate(all_timings.items()):\n",
" subfigs[row].suptitle(f\"setup #{1+row}: {plot_name} (seq len / beam search)\")\n",
" axs = subfigs[row].subplots(nrows=1, ncols=2)\n",
" for col, accumulated in enumerate([False, True]):\n",
" plot_axis = axs[col]\n",
" for index, (k, v) in enumerate(timings.items()):\n",
" axis = range(len(v))\n",
" color = colors[index]\n",
" v = np.array(v)\n",
" # remove extreme values\n",
" p99 = np.percentile(v, 99)\n",
" v[v > p99] = p99\n",
" v = np.cumsum(v) if accumulated else v\n",
" plot_axis.scatter(axis, v, label=k, s=2)\n",
"\n",
" title = f\"latency for the full sequence\" if accumulated else f\"latency for each token\"\n",
" plot_axis.title.set_text(title)\n",
"\n",
"# legend deduplication\n",
"handles, labels = plt.gca().get_legend_handles_labels()\n",
"by_label = dict(zip(labels, handles))\n",
"fig.legend(by_label.values(), by_label.keys(), bbox_to_anchor=(1, 1), loc=\"upper left\", markerscale=5)\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"## Profiling model at the kernel level\n"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"execution": {
"iopub.execute_input": "2022-07-01T16:10:18.594960Z",
"iopub.status.busy": "2022-07-01T16:10:18.594654Z",
"iopub.status.idle": "2022-07-01T16:10:19.583408Z",
"shell.execute_reply": "2022-07-01T16:10:19.582609Z"
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [
{
"data": {
"text/plain": "<Figure size 432x288 with 1 Axes>",
"image/png": "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\n"
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"enc_fp16_onnx = create_model_for_provider(encoder_model_path, \"CUDAExecutionProvider\", log_severity=3)\n",
"enc_fp16_onnx_binding: IOBinding = enc_fp16_onnx.io_binding()\n",
"dec_onnx = create_model_for_provider(dec_if_model_path, \"CUDAExecutionProvider\", enable_profiling=True, log_severity=3)\n",
"dec_onnx_binding: IOBinding = dec_onnx.io_binding()\n",
"_ = model_gen.generate(inputs=input_ids, max_length=10, min_length=10)\n",
"profile_name = dec_onnx.end_profiling()\n",
"\n",
"with open(profile_name) as f:\n",
" content = json.load(f)\n",
"\n",
"op_timings = defaultdict(lambda: 0)\n",
"for c in content:\n",
" if \"op_name\" not in c[\"args\"]:\n",
" continue\n",
" op_name = c[\"args\"][\"op_name\"]\n",
" if op_name == \"If\":\n",
" continue # subgraph\n",
" time_taken = c[\"dur\"]\n",
" op_timings[op_name] += time_taken\n",
"\n",
"op_timings_filter = dict(sorted(op_timings.items(), key=operator.itemgetter(1), reverse=True)[:10])\n",
"total_kernel_timing = sum(op_timings.values())\n",
"op_timings_percent = {k: 100 * v / total_kernel_timing for k, v in op_timings_filter.items()}\n",
"\n",
"plt.barh(list(op_timings_percent.keys()), list(op_timings_percent.values()))\n",
"plt.title(\"Time spent per kernel\\n(top 10 kernels)\")\n",
"plt.xlabel(\"% total inference time\")\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": []
}
],
"metadata": {
"interpreter": {
"hash": "366de2154be95d5d8dfb409ee394df3294ef97f2f15d871b0df4dd91a89cdb4b"
},
"kernelspec": {
"display_name": "Python 3.9.12 ('fast_transformer')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
}
},
"nbformat": 4,
"nbformat_minor": 1
} |