From 1bd4f34673d904b1427219fa03a33da54a68a95a Mon Sep 17 00:00:00 2001 From: Deklan Webster Date: Sun, 7 Jan 2024 10:25:59 -0600 Subject: [PATCH 1/7] fix: correct memory-saving taylor forward pass --- zoology/mixers/based.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zoology/mixers/based.py b/zoology/mixers/based.py index 4f6ee3e..76b35e3 100644 --- a/zoology/mixers/based.py +++ b/zoology/mixers/based.py @@ -69,11 +69,11 @@ def forward_mem_save(self, x: torch.Tensor) -> torch.Tensor: -> Assume x.shape is (batch_size, n_heads, seq_len, head_dim) """ # Slow but memory-saving way to compute 2nd-order terms; how do w/o outer-product first? - x2 = oe.contract('...m,...n->...mn', x, x) / self.input_dim + x2 = oe.contract('...m,...n->...mn', x, x) / self.rd x2d = torch.diagonal(x2, dim1=-2, dim2=-1) / self.r2 x2 = x2[..., self.tril_indices[0], self.tril_indices[1]] x = torch.cat([torch.ones(x[..., :1].shape).to(x.device), - x / self.rd, x2d, x2], dim=-1) + x / self.rrd, x2d, x2], dim=-1) return x From 16fb24c22560597daf921b4af4070e5be9e3f85f Mon Sep 17 00:00:00 2001 From: eyuboglu Date: Thu, 11 Jan 2024 17:10:36 +0000 Subject: [PATCH 2/7] Add license --- LICENSE.md | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..8890ad9 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 The Meerkat Team. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file From 0cc839959d5041191bedacc0d8ccd9374671b49a Mon Sep 17 00:00:00 2001 From: eyuboglu Date: Thu, 11 Jan 2024 17:19:15 +0000 Subject: [PATCH 3/7] Fix num_queries in new general ar implementation --- zoology/data/associative_recall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zoology/data/associative_recall.py b/zoology/data/associative_recall.py index 32cdd3e..58bdafb 100644 --- a/zoology/data/associative_recall.py +++ b/zoology/data/associative_recall.py @@ -136,7 +136,7 @@ def _ar( inputs[:, 0:context_size] = kvs # create a matrix of indices, which is needed to index correctly below - rows = np.tile(np.arange(num_examples), (3, 1)).T + rows = np.tile(np.arange(num_examples), (num_queries, 1)).T # sample random kv pairs to use for the queries kv_idx_choices = np.arange(0, num_kv_pairs) From 66f4918beaf998b02742975bb1d747250c43887f Mon Sep 17 00:00:00 2001 From: eyuboglu Date: Thu, 11 Jan 2024 17:47:42 +0000 Subject: [PATCH 4/7] Add instructions for reproducing --- README.md | 25 ++- banner.png => assets/banner.png | Bin assets/figure2.png | Bin 0 -> 108974 bytes zoology/analysis/paper/figure2.py | 71 ++++++++ .../{mqar_d_model.py => mqar_dmodel.py} | 0 zoology/experiments/paper/figure2.py | 159 ++++++++++++++++++ 6 files changed, 250 insertions(+), 5 deletions(-) rename banner.png => assets/banner.png (100%) create mode 100644 assets/figure2.png create mode 100644 zoology/analysis/paper/figure2.py rename zoology/experiments/{mqar_d_model.py => mqar_dmodel.py} (100%) create mode 100644 zoology/experiments/paper/figure2.py diff --git a/README.md b/README.md index be5daca..44d305f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
- Meerkat logo + Meerkat logo [![GitHub](https://img.shields.io/github/license/HazyResearch/meerkat)](https://img.shields.io/github/license/HazyResearch/meerkat) [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) @@ -9,7 +9,7 @@
-Zoology provides machine learning researchers with a simple playground for understanding and testing language model architectures on synthetic tasks. This repository can be used to reproduce the results in our paper *[Zoology: Measuring and Improving Recall in Efficient Language Models](https://arxiv.org/abs/2312.04927)*. +Zoology provides machine learning researchers with a simple playground for understanding and testing language model architectures on synthetic tasks. This repository can be used to reproduce the results in our paper *[Zoology: Measuring and Improving Recall in Efficient Language Models](https://arxiv.org/abs/2312.04927)*. See the section on [reproducing paper experiments](#reproducing-paper-experiments) for details. --- @@ -30,7 +30,7 @@ pip install -e .[extra,analysis] ``` If you want to keep this install as lightweight as possible; the only required dependencies are: `torch, einops, tqdm, pydantic, wandb`. There is some extra functionality (*e.g.* launching sweeps in parallel with Ray) that require additional dependencies. To install without the optional dependencies, run `pip install -e .`. -Then, try running an example experiments with: +Then, try running an example experiment with: ``` python -m zoology.launch zoology/experiments/examples/basic.py ``` @@ -40,6 +40,23 @@ python -m zoology.launch zoology/experiments/examples/basic_sweep.py ``` If you have access to multiple GPUs, you can run the sweep in parallel by adding the `-p` flag. + +## Reproducing paper experiments + In this section, we'll show how to reproduce the results in our paper *[Zoology: Measuring and Improving Recall in Efficient Language Models](https://arxiv.org/abs/2312.04927)* and [blogpost](https://hazyresearch.stanford.edu/blog/2023-12-11-zoology1-analysis). + + The main synthetic data results in our work are summarized in Figure 2. The x-axis is the model dimension and the y-axis is accuracy on Mqar. Increasing the sequence +length correlates with increased task difficulty. The results shown are the maximum performance for each model over four learning rates. + + Figure 2 + +To reproduce these results, ensure you have WandB setup to log all the results and then run the command: +``` +python -m zoology.launch zoology/experiments/paper/figure2.py -p +``` +Note that there are 448 model/data configurations in this sweep, so it takes a while to run. We ran most of our experiments on an 8xA100 with the `-p` flag, which launches configurations in parallel. To run a smaller scale experiment, you can modify the loops in `figure2.py` file to only include a subset of the configurations you're interested in (*e.g.* you can drop some models, sequence lengths, or learning rates). For more details on how the experiments are configured, see the [configuration section](#configuration-experiments-and-sweeps). + +To produce the plot after the run, see the plotting code `zoology/analysis/paper/figure2.py`. + ## Configuration, Experiments, and Sweeps In this section, we'll walk through how to configure an experiment and launch sweeps. @@ -169,8 +186,6 @@ When you launch an experiment with this configuration, the `my_data_builder` fun **Caching dataset creation.** Sometimes it's useful to cache the dataset creation process, especially if it's expensive. To do so you can pass a `cache_dir` to the `DataConfig`: `DataConfig(..., cache_dir="my_cache_dir")`. - - ## About This repo is being developed by members of the HazyResearch group. diff --git a/banner.png b/assets/banner.png similarity index 100% rename from banner.png rename to assets/banner.png diff --git a/assets/figure2.png b/assets/figure2.png new file mode 100644 index 0000000000000000000000000000000000000000..b794671454b8e923a8d914a36ece01182c3c7236 GIT binary patch literal 108974 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%0A3b|5i&2 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 Date: Thu, 11 Jan 2024 09:48:42 -0800 Subject: [PATCH 5/7] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 44d305f..a460772 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,9 @@ If you have access to multiple GPUs, you can run the sweep in parallel by adding The main synthetic data results in our work are summarized in Figure 2. The x-axis is the model dimension and the y-axis is accuracy on Mqar. Increasing the sequence length correlates with increased task difficulty. The results shown are the maximum performance for each model over four learning rates. - +

To reproduce these results, ensure you have WandB setup to log all the results and then run the command: ``` From 59b101274baf5ce1819465ad6443c7dbd2da36ca Mon Sep 17 00:00:00 2001 From: guangyusong <15316444+guangyusong@users.noreply.github.com> Date: Wed, 17 Jan 2024 23:32:48 -0500 Subject: [PATCH 6/7] Add files to make cuda work --- zoology/mixers/rwkv.py | 7 +- zoology/mixers/rwkv/v4/wkv_cuda.cu | 125 +++++++++++++++++++++++++++++ zoology/mixers/rwkv/v4/wkv_op.cpp | 21 +++++ 3 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 zoology/mixers/rwkv/v4/wkv_cuda.cu create mode 100644 zoology/mixers/rwkv/v4/wkv_op.cpp diff --git a/zoology/mixers/rwkv.py b/zoology/mixers/rwkv.py index ec2429e..b4d7c70 100644 --- a/zoology/mixers/rwkv.py +++ b/zoology/mixers/rwkv.py @@ -38,9 +38,10 @@ def backward(ctx, grad_output): # # it's possible to go beyond CUDA limitations if you slice the ctx and pass the hidden state in each slice from torch.utils.cpp_extension import load -wkv_cuda = load(name="wkv", sources= - ["/var/cr05_data/sim_data/code/petting-zoo/src/models/mixers/cuda/wkv_op.cpp", - "/var/cr05_data/sim_data/code/petting-zoo/src/models/mixers/cuda/wkv_cuda.cu"], +dir_path = os.path.dirname(os.path.realpath(__file__)) +wkv_cuda = load(name="wkv", sources=[ + os.path.join(dir_path, "./rwkv/v4/wkv_op.cpp"), + os.path.join(dir_path, "./rwkv/v4/wkv_cuda.cu")], verbose=True, extra_cuda_cflags=['-res-usage', '--maxrregcount 60', '--use_fast_math', '-O3', '-Xptxas -O3', f'-DTmax={T_MAX}']) class WKV(torch.autograd.Function): diff --git a/zoology/mixers/rwkv/v4/wkv_cuda.cu b/zoology/mixers/rwkv/v4/wkv_cuda.cu new file mode 100644 index 0000000..a4522cb --- /dev/null +++ b/zoology/mixers/rwkv/v4/wkv_cuda.cu @@ -0,0 +1,125 @@ +#include +#include + +#define MIN_VALUE (-1e38) + +template +__global__ void kernel_forward(const int B, const int T, const int C, + const F *__restrict__ const _w, const F *__restrict__ const _u, const F *__restrict__ const _k, const F *__restrict__ const _v, + F *__restrict__ const _y) { + const int idx = blockIdx.x * blockDim.x + threadIdx.x; + const int _b = idx / C; + const int _c = idx % C; + const int _offset = _b * T * C + _c; + + F u = _u[_c]; + F w = _w[_c]; + const F *__restrict__ const k = _k + _offset; + const F *__restrict__ const v = _v + _offset; + F *__restrict__ const y = _y + _offset; + + F p = 0, q = 0, o = MIN_VALUE; + // p and q are running sums divided by exp(o) (to avoid overflows) + for (int i = 0; i < T; i++) { + const int ii = i * C; + + F no = max(o, u + k[ii]); + F A = exp(o - no); + F B = exp(u + k[ii] - no); + y[ii] = (A * p + B * v[ii]) / (A * q + B); + + no = max(w + o, k[ii]); + A = exp(w + o - no); + B = exp(k[ii] - no); + p = A * p + B * v[ii]; + q = A * q + B; + o = no; + } +} + +template +__global__ void kernel_backward(const int B, const int T, const int C, + const F *__restrict__ const _w, const F *__restrict__ const _u, const F *__restrict__ const _k, const F *__restrict__ const _v, const F *__restrict__ const _gy, + F *__restrict__ const _gw, F *__restrict__ const _gu, F *__restrict__ const _gk, F *__restrict__ const _gv) { + const int idx = blockIdx.x * blockDim.x + threadIdx.x; + const int _b = idx / C; + const int _c = idx % C; + const int _offset = _b * T * C + _c; + + F u = _u[_c]; + F w = _w[_c]; + const F *__restrict__ const k = _k + _offset; + const F *__restrict__ const v = _v + _offset; + const F *__restrict__ const gy = _gy + _offset; + + F *__restrict__ const gk = _gk + _offset; + F *__restrict__ const gv = _gv + _offset; + + F y[Tmax], z[Tmax], zexp[Tmax]; + + F gw = 0, gu = 0; + F p = 0, q = 0; + F dpdw = 0, dqdw = 0; + F o = MIN_VALUE; + for (int i = 0; i < T; i++) { + const int ii = i * C; + F no = max(o, k[ii] + u); + F A = exp(o - no); + F B = exp(k[ii] + u - no); + + F num = A * p + B * v[ii]; + F iden = 1 / (A * q + B); + + y[i] = num * iden; + z[i] = iden; + zexp[i] = k[ii] + u - no; + + gw += gy[ii] * (dpdw - dqdw * y[i]) * iden * A; + gu += gy[ii] * (v[ii] - y[i]) * B * iden; + + no = max(w + o, k[ii]); + A = exp(w + o - no); + B = exp(k[ii] - no); + dpdw = A * (p + dpdw); + dqdw = A * (q + dqdw); + p = A * p + B * v[ii]; + q = A * q + B; + o = no; + } + + F gp = 0, gq = 0; + o = MIN_VALUE; + for (int i = T - 1; i >= 0; i--) { + const int ii = i * C; + F A = gy[ii] * z[i] * exp(zexp[i]); + F B = exp(k[ii] + o); + gk[ii] = A * (v[ii] - y[i]) + B * (gp * v[ii] + gq); + gv[ii] = A + B * gp; + + F no = max(w + o, zexp[i] - k[ii] - u); + A = exp(w + o - no); + B = gy[ii] * z[i] * exp(zexp[i] - k[ii] - u - no); + gp = A * gp + B; + gq = A * gq - B * y[i]; + o = no; + } + + // Multiply by w because the w -> -exp(w) preprocessing is halfway in the backwards pass, even though it's not in the forward pass + const int _offsetBC = _b * C + _c; + _gw[_offsetBC] += gw * _w[_c]; + _gu[_offsetBC] += gu; +} + +void cuda_forward(int B, int T, int C, float *w, float *u, float *k, float *v, float *y) { + dim3 threadsPerBlock( min(C, 32) ); // requires --maxrregcount 60 for optimal performance + assert(B * C % threadsPerBlock.x == 0); + dim3 numBlocks(B * C / threadsPerBlock.x); + kernel_forward<<>>(B, T, C, w, u, k, v, y); +} + +void cuda_backward(int B, int T, int C, float *w, float *u, float *k, float *v, float *gy, float *gw, float *gu, float *gk, float *gv) { + dim3 threadsPerBlock( min(C, 32) ); // requires --maxrregcount 60 for optimal performance + assert(B * C % threadsPerBlock.x == 0); + dim3 numBlocks(B * C / threadsPerBlock.x); + kernel_backward<<>>(B, T, C, w, u, k, v, gy, gw, gu, gk, gv); +} \ No newline at end of file diff --git a/zoology/mixers/rwkv/v4/wkv_op.cpp b/zoology/mixers/rwkv/v4/wkv_op.cpp new file mode 100644 index 0000000..e59a515 --- /dev/null +++ b/zoology/mixers/rwkv/v4/wkv_op.cpp @@ -0,0 +1,21 @@ +#include + +void cuda_forward(int B, int T, int C, float *w, float *u, float *k, float *v, float *y); +void cuda_backward(int B, int T, int C, float *w, float *u, float *k, float *v, float *gy, float *gw, float *gu, float *gk, float *gv); + +void forward(int64_t B, int64_t T, int64_t C, torch::Tensor &w, torch::Tensor &u, torch::Tensor &k, torch::Tensor &v, torch::Tensor &y) { + cuda_forward(B, T, C, w.data_ptr(), u.data_ptr(), k.data_ptr(), v.data_ptr(), y.data_ptr()); +} +void backward(int64_t B, int64_t T, int64_t C, torch::Tensor &w, torch::Tensor &u, torch::Tensor &k, torch::Tensor &v, torch::Tensor &gy, torch::Tensor &gw, torch::Tensor &gu, torch::Tensor &gk, torch::Tensor &gv) { + cuda_backward(B, T, C, w.data_ptr(), u.data_ptr(), k.data_ptr(), v.data_ptr(), gy.data_ptr(), gw.data_ptr(), gu.data_ptr(), gk.data_ptr(), gv.data_ptr()); +} + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &forward, "wkv forward"); + m.def("backward", &backward, "wkv backward"); +} + +TORCH_LIBRARY(wkv, m) { + m.def("forward", forward); + m.def("backward", backward); +} \ No newline at end of file From 45927c8a228cabd3d2c14c0bc6645cbac5f87555 Mon Sep 17 00:00:00 2001 From: guangyusong <15316444+guangyusong@users.noreply.github.com> Date: Tue, 23 Jan 2024 00:16:00 -0500 Subject: [PATCH 7/7] Add RWKV5 --- zoology/experiments/mqar_dmodel.py | 6 + zoology/mixers/rwkv/v5/wkv5_cuda.cu | 202 +++++++++ zoology/mixers/rwkv/v5/wkv5_op.cpp | 22 + zoology/mixers/rwkv5.py | 616 ++++++++++++++++++++++++++++ 4 files changed, 846 insertions(+) create mode 100644 zoology/mixers/rwkv/v5/wkv5_cuda.cu create mode 100644 zoology/mixers/rwkv/v5/wkv5_op.cpp create mode 100644 zoology/mixers/rwkv5.py diff --git a/zoology/experiments/mqar_dmodel.py b/zoology/experiments/mqar_dmodel.py index 1d10684..3d37a84 100644 --- a/zoology/experiments/mqar_dmodel.py +++ b/zoology/experiments/mqar_dmodel.py @@ -72,6 +72,12 @@ "l_max": input_seq_len, }, ), + "rwkv5": dict( + name="zoology.mixers.rwkv5.RWKVTimeMixer", + kwargs={ + "l_max": input_seq_len, + }, + ), "base_conv": dict( name="zoology.mixers.base_conv.BaseConv", kwargs={ diff --git a/zoology/mixers/rwkv/v5/wkv5_cuda.cu b/zoology/mixers/rwkv/v5/wkv5_cuda.cu new file mode 100644 index 0000000..18bc730 --- /dev/null +++ b/zoology/mixers/rwkv/v5/wkv5_cuda.cu @@ -0,0 +1,202 @@ +#include +#include +#include "ATen/ATen.h" +typedef at::BFloat16 bf16; + +template +__global__ void kernel_forward(const int B, const int T, const int C, const int H, + const F *__restrict__ const _r, const F *__restrict__ const _k, const F *__restrict__ const _v, const float *__restrict__ _w, const F *__restrict__ _u, + F *__restrict__ const _y) +{ + const int b = blockIdx.x / H; + const int h = blockIdx.x % H; + const int i = threadIdx.x; + _w += h*_N_; + _u += h*_N_; + + __shared__ float r[_N_], k[_N_], u[_N_], w[_N_]; + float state[_N_] = {0}; + + __syncthreads(); + w[i] = _w[i]; + u[i] = float(_u[i]); + __syncthreads(); + + for (int t = b*T*C + h*_N_ + i; t < (b+1)*T*C + h*_N_ + i; t += C) + { + __syncthreads(); + r[i] = float(_r[t]); + k[i] = float(_k[t]); + __syncthreads(); + + const float v = float(_v[t]); + float y = 0; + + #pragma unroll + for (int j = 0; j < _N_; j+=4) + { + const float4& r_ = (float4&)(r[j]); + const float4& k_ = (float4&)(k[j]); + const float4& w_ = (float4&)(w[j]); + const float4& u_ = (float4&)(u[j]); + float4& s = (float4&)(state[j]); + float4 x; + + x.x = k_.x * v; + x.y = k_.y * v; + x.z = k_.z * v; + x.w = k_.w * v; + + y += r_.x * (u_.x * x.x + s.x); + y += r_.y * (u_.y * x.y + s.y); + y += r_.z * (u_.z * x.z + s.z); + y += r_.w * (u_.w * x.w + s.w); + + s.x = s.x * w_.x + x.x; + s.y = s.y * w_.y + x.y; + s.z = s.z * w_.z + x.z; + s.w = s.w * w_.w + x.w; + } + _y[t] = F(y); + } +} + +template +__global__ void kernel_backward(const int B, const int T, const int C, const int H, + const F *__restrict__ const _r, const F *__restrict__ const _k, const F *__restrict__ const _v, const float *__restrict__ _w, const float *__restrict__ __w, const F *__restrict__ _u, const F *__restrict__ const _gy, + F *__restrict__ const _gr, F *__restrict__ const _gk, F *__restrict__ const _gv, F *__restrict__ const _gw, F *__restrict__ const _gu) +{ + const int b = blockIdx.x / H; + const int h = blockIdx.x % H; + const int i = threadIdx.x; + _w += h*_N_; + _u += h*_N_; + __w += h*_N_; + + __shared__ float w_[_N_], u_[_N_]; + __shared__ float r[_N_], k[_N_], v[_N_], gy[_N_]; + __syncthreads(); + w_[i] = _w[i]; + u_[i] = float(_u[i]); + __syncthreads(); + + const float w = w_[i]; + const float ww = __w[i]; + const float u = u_[i]; + + float state[_N_] = {0}, saaaa[_N_] = {0}, sbbbb[_N_] = {0}, scccc[_N_] = {0}, sdddd[_N_] = {0}; + + float gw = 0, gu = 0; + const int t000 = b*T*C + h*_N_ + i; + const int t111 = (b+1)*T*C + h*_N_ + i; + const int t222 = t111 - 2*C; + + for (int t = t000; t < t111; t += C) + { + __syncthreads(); + v[i] = float(_v[t]); + gy[i] = float(_gy[t]); + __syncthreads(); + + const float k = float(_k[t]); + float gr = 0, gu_ = 0; + + #pragma unroll + for (int j = 0; j < _N_; j++) + { + float& s = state[j]; + float x = k * v[j]; + + gr += (u * x + s) * gy[j]; + gu_ += x * gy[j]; + s = s * w + x; + } + _gr[t] = F(gr); + gu += float(_r[t]) * gu_; + } + _gu[b*C + h*_N_ + i] = F(gu); + + for (int t = t000; t < t222; t += C) + { + __syncthreads(); + v[i] = float(_v[t]); + gy[i] = float(_gy[t + 2*C]); + __syncthreads(); + + const float k = float(_k[t]); + float gw_ = 0; + + #pragma unroll + for (int j = 0; j < _N_; j++) + { + float& s = saaaa[j]; + float& s2 = sbbbb[j]; + float x = k * v[j]; + + float tmp = w * (x + s); + s = tmp; + s2 = tmp + w * s2; + gw_ += s2 * gy[j]; + } + gw += float(_r[t + 2*C]) * gw_; + } + _gw[b*C + h*_N_ + i] = F(ww * gw); + + for (int t = t111 - C; t >= t000; t -= C) + { + __syncthreads(); + v[i] = float(_v[t]); + gy[i] = float(_gy[t]); + __syncthreads(); + + const float rr = float(_r[t]); + float gk = 0; + + #pragma unroll + for (int j = 0; j < _N_; j++) + { + float& s = scccc[j]; + float x = rr * gy[j]; + + gk += (u * x + s) * v[j]; + s = x + s * w; + } + _gk[t] = F(gk); + } + + for (int t = t111 - C; t >= t000; t -= C) + { + __syncthreads(); + r[i] = float(_r[t]); + k[i] = float(_k[t]); + __syncthreads(); + + const float gyy = float(_gy[t]); + float gv = 0; + + #pragma unroll + for (int j = 0; j < _N_; j++) + { + float& s = sdddd[j]; + float x = gyy * r[j]; + + gv += (u_[j] * x + s) * k[j]; + s = x + s * w_[j]; + } + _gv[t] = F(gv); + } +} + +void cuda_forward(int B, int T, int C, int H, bf16 *r, bf16 *k, bf16 *v, float *w, bf16 *u, bf16 *y) +{ + assert(H*_N_ == C); + assert(_N_%4 == 0); + kernel_forward<<>>(B, T, C, H, r, k, v, w, u, y); +} + +void cuda_backward(int B, int T, int C, int H, bf16 *r, bf16 *k, bf16 *v, float *w, float *ww, bf16 *u, bf16 *gy, bf16 *gr, bf16 *gk, bf16 *gv, bf16 *gw, bf16 *gu) +{ + assert(H*_N_ == C); + assert(_N_%4 == 0); + kernel_backward<<>>(B, T, C, H, r, k, v, w, ww, u, gy, gr, gk, gv, gw, gu); +} \ No newline at end of file diff --git a/zoology/mixers/rwkv/v5/wkv5_op.cpp b/zoology/mixers/rwkv/v5/wkv5_op.cpp new file mode 100644 index 0000000..aef76a4 --- /dev/null +++ b/zoology/mixers/rwkv/v5/wkv5_op.cpp @@ -0,0 +1,22 @@ +#include +#include "ATen/ATen.h" +typedef at::BFloat16 bf16; + +void cuda_forward(int B, int T, int C, int H, bf16 *r, bf16 *k, bf16 *v, float *w, bf16 *u, bf16 *y); +void cuda_backward(int B, int T, int C, int H, bf16 *r, bf16 *k, bf16 *v, float *w, float *ww, bf16 *u, bf16 *gy, bf16 *gr, bf16 *gk, bf16 *gv, bf16 *gw, bf16 *gu); + +void forward(int64_t B, int64_t T, int64_t C, int64_t H, torch::Tensor &r, torch::Tensor &k, torch::Tensor &v, torch::Tensor &w, torch::Tensor &u, torch::Tensor &y) { + cuda_forward(B, T, C, H, r.data_ptr(), k.data_ptr(), v.data_ptr(), w.data_ptr(), u.data_ptr(), y.data_ptr()); +} +void backward(int64_t B, int64_t T, int64_t C, int64_t H, torch::Tensor &r, torch::Tensor &k, torch::Tensor &v, torch::Tensor &w, torch::Tensor &ww, torch::Tensor &u, torch::Tensor &gy, torch::Tensor &gr, torch::Tensor &gk, torch::Tensor &gv, torch::Tensor &gw, torch::Tensor &gu) { + cuda_backward(B, T, C, H, r.data_ptr(), k.data_ptr(), v.data_ptr(), w.data_ptr(), ww.data_ptr(), u.data_ptr(), gy.data_ptr(), gr.data_ptr(), gk.data_ptr(), gv.data_ptr(), gw.data_ptr(), gu.data_ptr()); +} +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &forward, "wkv5 forward"); + m.def("backward", &backward, "wkv5 backward"); +} + +TORCH_LIBRARY(wkv5, m) { + m.def("forward", forward); + m.def("backward", backward); +} \ No newline at end of file diff --git a/zoology/mixers/rwkv5.py b/zoology/mixers/rwkv5.py new file mode 100644 index 0000000..5aa3972 --- /dev/null +++ b/zoology/mixers/rwkv5.py @@ -0,0 +1,616 @@ +######################################################################################################## +# The RWKV Language Model - https://github.com/BlinkDL/RWKV-LM +######################################################################################################## + +import os, math, gc, importlib +import torch +# torch._C._jit_set_profiling_executor(True) +# torch._C._jit_set_profiling_mode(True) +import torch.nn as nn +from torch.nn import functional as F +import pytorch_lightning as pl +from pytorch_lightning.utilities import rank_zero_info, rank_zero_only +from pytorch_lightning.strategies import DeepSpeedStrategy +if importlib.util.find_spec('deepspeed'): + import deepspeed + from deepspeed.ops.adam import DeepSpeedCPUAdam, FusedAdam + +# from deepspeed.runtime.fp16.onebit.zoadam import ZeroOneAdam + +try: + print('RWKV_MY_TESTING', os.environ["RWKV_MY_TESTING"]) +except: + os.environ["RWKV_MY_TESTING"] = '' + +def __nop(ob): + return ob + + +MyModule = nn.Module +MyFunction = __nop +os.environ['RWKV_JIT_ON'] = '1' +if os.environ["RWKV_JIT_ON"] == "1": + MyModule = torch.jit.ScriptModule + MyFunction = torch.jit.script_method + +os.environ['RWKV_FLOAT_MODE'] = 'bf16' + +######################################################################################################## +# CUDA Kernel +######################################################################################################## + + +from torch.utils.cpp_extension import load + +# HEAD_SIZE = int(os.environ["RWKV_HEAD_SIZE_A"]) +HEAD_SIZE = 64 +dir_path = os.path.dirname(os.path.realpath(__file__)) +wkv5_cuda = load(name="wkv5", sources=[ + os.path.join(dir_path, "./rwkv/v5/wkv5_op.cpp"), + os.path.join(dir_path, "./rwkv/v5/wkv5_cuda.cu")], + verbose=True, extra_cuda_cflags=["-res-usage", "--use_fast_math", "-O3", "-Xptxas -O3", "--extra-device-vectorization", f"-D_N_={HEAD_SIZE}"]) + +class WKV_5(torch.autograd.Function): + @staticmethod + def forward(ctx, B, T, C, H, r, k, v, w, u): + with torch.no_grad(): + assert r.dtype == torch.bfloat16 + assert k.dtype == torch.bfloat16 + assert v.dtype == torch.bfloat16 + assert w.dtype == torch.bfloat16 + assert u.dtype == torch.bfloat16 + assert HEAD_SIZE == C // H + ctx.B = B + ctx.T = T + ctx.C = C + ctx.H = H + assert r.is_contiguous() + assert k.is_contiguous() + assert v.is_contiguous() + assert w.is_contiguous() + assert u.is_contiguous() + ew = (-torch.exp(w.float())).contiguous() + eew = (torch.exp(ew)).contiguous() + ctx.save_for_backward(r, k, v, eew, ew, u) + y = torch.empty((B, T, C), device=r.device, dtype=torch.bfloat16, memory_format=torch.contiguous_format) # .uniform_(-1, 1) + wkv5_cuda.forward(B, T, C, H, r, k, v, eew, u, y) + return y + + @staticmethod + def backward(ctx, gy): + with torch.no_grad(): + assert gy.dtype == torch.bfloat16 + B = ctx.B + T = ctx.T + C = ctx.C + H = ctx.H + assert gy.is_contiguous() + r, k, v, eew, ew, u = ctx.saved_tensors + gr = torch.empty((B, T, C), device=gy.device, requires_grad=False, dtype=torch.bfloat16, memory_format=torch.contiguous_format) # .uniform_(-1, 1) + gk = torch.empty((B, T, C), device=gy.device, requires_grad=False, dtype=torch.bfloat16, memory_format=torch.contiguous_format) # .uniform_(-1, 1) + gv = torch.empty((B, T, C), device=gy.device, requires_grad=False, dtype=torch.bfloat16, memory_format=torch.contiguous_format) # .uniform_(-1, 1) + gw = torch.empty((B, C), device=gy.device, requires_grad=False, dtype=torch.bfloat16, memory_format=torch.contiguous_format) # .uniform_(-1, 1) + gu = torch.empty((B, C), device=gy.device, requires_grad=False, dtype=torch.bfloat16, memory_format=torch.contiguous_format) # .uniform_(-1, 1) + wkv5_cuda.backward(B, T, C, H, r, k, v, eew, ew, u, gy, gr, gk, gv, gw, gu) + gw = torch.sum(gw, 0).view(H, C//H) + gu = torch.sum(gu, 0).view(H, C//H) + return (None, None, None, None, gr, gk, gv, gw, gu) + +def RUN_CUDA_RWKV5(B, T, C, H, r, k, v, w, u): + return WKV_5.apply(B, T, C, H, r, k, v, w, u) + +######################################################################################################## + +class RWKVTimeMixer(MyModule): + def __init__(self, l_max: int, d_model: int = 512, n_layer: int=12, layer_idx: int=-1): + + super().__init__() + self.layer_idx = layer_idx + self.ctx_len = l_max + self.d_model = d_model + dim_att = d_model + + # self.head_size = args.head_size_a + self.head_size = 64 + assert HEAD_SIZE == self.head_size # change HEAD_SIZE to match args.head_size_a + self.n_head = dim_att // self.head_size + assert dim_att % self.n_head == 0 + # self.head_size_divisor = args.head_size_divisor + self.head_size_divisor = 8 + + with torch.no_grad(): + ratio_0_to_1 = layer_idx / (n_layer - 1) # 0 to 1 + ratio_1_to_almost0 = 1.0 - (layer_idx / n_layer) # 1 to ~0 + ddd = torch.ones(1, 1, d_model) + for i in range(d_model): + ddd[0, 0, i] = i / d_model + + # fancy time_mix + self.time_mix_k = nn.Parameter(torch.pow(ddd, ratio_1_to_almost0)) + self.time_mix_v = nn.Parameter(torch.pow(ddd, ratio_1_to_almost0) + 0.3 * ratio_0_to_1) + self.time_mix_r = nn.Parameter(torch.pow(ddd, 0.5 * ratio_1_to_almost0)) + self.time_mix_g = nn.Parameter(torch.pow(ddd, 0.5 * ratio_1_to_almost0)) + + # fancy time_decay + decay_speed = torch.ones(dim_att) + for n in range(dim_att): + decay_speed[n] = -6 + 5 * (n / (dim_att - 1)) ** (0.7 + 1.3 * ratio_0_to_1) + self.time_decay = nn.Parameter(decay_speed.reshape(self.n_head, self.head_size)) + # print(layer_idx, self.time_decay.flatten()[:3].cpu().numpy(), '...', self.time_decay.flatten()[-3:].cpu().numpy()) + + tmp = torch.zeros(dim_att) + for n in range(dim_att): + zigzag = ((n + 1) % 3 - 1) * 0.1 + tmp[n] = ratio_0_to_1 * (1 - (n / (dim_att - 1))) + zigzag + + self.time_faaaa = nn.Parameter(tmp.reshape(self.n_head, self.head_size)) + + self.time_shift = nn.ZeroPad2d((0, 0, 1, -1)) + self.receptance = nn.Linear(d_model, dim_att, bias=False) + self.key = nn.Linear(d_model, dim_att, bias=False) + + self.value = nn.Linear(d_model, dim_att, bias=False) + self.output = nn.Linear(dim_att, d_model, bias=False) + self.gate = nn.Linear(d_model, dim_att, bias=False) + self.ln_x = nn.GroupNorm(self.n_head, dim_att) + + @MyFunction + def jit_func(self, x): + B, T, C = x.size() + + xx = self.time_shift(x) # Mix x with the previous timestep to produce xk, xv, xr + xk = x * self.time_mix_k + xx * (1 - self.time_mix_k) + xv = x * self.time_mix_v + xx * (1 - self.time_mix_v) + xr = x * self.time_mix_r + xx * (1 - self.time_mix_r) + xg = x * self.time_mix_g + xx * (1 - self.time_mix_g) + + r = self.receptance(xr) + k = self.key(xk) + v = self.value(xv) + g = F.silu(self.gate(xg)) + + return r, k, v, g + + @MyFunction + def jit_func_2(self, x, g): + B, T, C = x.size() + x = x.view(B * T, C) + + x = self.ln_x(x / self.head_size_divisor).view(B, T, C) + x = self.output(x * g) + return x + + def forward(self, x: torch.Tensor): + B, T, C = x.size() + H = self.n_head + + r, k, v, g = self.jit_func(x) + + # Convert tensors to bfloat16 for CUDA operation + r_bf16 = r.to(dtype=torch.bfloat16) + k_bf16 = k.to(dtype=torch.bfloat16) + v_bf16 = v.to(dtype=torch.bfloat16) + w_bf16 = self.time_decay.to(dtype=torch.bfloat16) + u_bf16 = self.time_faaaa.to(dtype=torch.bfloat16) + + x = RUN_CUDA_RWKV5(B, T, C, H, r_bf16, k_bf16, v_bf16, w_bf16, u_bf16) + + # Convert back to Float for subsequent operations + x_float = x.to(dtype=torch.float32) + + return self.jit_func_2(x_float, g) + +######################################################################################################## + +class RWKV_ChannelMix(MyModule): + def __init__(self, args, d_model=512, n_layer=12, layer_idx=1): + super().__init__() + self.args = args + self.layer_idx = layer_idx + self.time_shift = nn.ZeroPad2d((0, 0, 1, -1)) + + with torch.no_grad(): # fancy init of time_mix + ratio_1_to_almost0 = 1.0 - (layer_idx / n_layer) # 1 to ~0 + ddd = torch.ones(1, 1, d_model) + for i in range(d_model): + ddd[0, 0, i] = i / d_model + self.time_mix_k = nn.Parameter(torch.pow(ddd, ratio_1_to_almost0)) + self.time_mix_r = nn.Parameter(torch.pow(ddd, ratio_1_to_almost0)) + + self.key = nn.Linear(d_model, self.dim_ffn, bias=False) + self.receptance = nn.Linear(d_model, d_model, bias=False) + self.value = nn.Linear(self.dim_ffn, d_model, bias=False) + + @MyFunction + def forward(self, x): + xx = self.time_shift(x) + xk = x * self.time_mix_k + xx * (1 - self.time_mix_k) + xr = x * self.time_mix_r + xx * (1 - self.time_mix_r) + k = self.key(xk) + k = torch.relu(k) ** 2 + kv = self.value(k) + return torch.sigmoid(self.receptance(xr)) * kv + +class MishGLU(MyModule): + def __init__(self, args, d_model, layer_idx, n_layer): + super().__init__() + self.args = args + self.layer_idx = layer_idx + self.time_shift = nn.ZeroPad2d((0, 0, 1, -1)) + + with torch.no_grad(): + ratio_1_to_almost0 = 1.0 - (layer_idx / n_layer) + + x = torch.ones(1, 1,d_model) + for i in range(d_model): + x[0, 0, i] = i /d_model + + self.time_mix_k = nn.Parameter(torch.pow(x, ratio_1_to_almost0)) + self.time_mix_r = nn.Parameter(torch.pow(x, ratio_1_to_almost0)) + self.aa = nn.Linear(d_model, args.dim_ffn, bias=False) + self.bb = nn.Linear(d_model, args.dim_ffn, bias=False) + self.value = nn.Linear(args.dim_ffn,d_model, bias=False) + + @MyFunction + def forward(self, x): + xx = self.time_shift(x) + xa = x * self.time_mix_k + xx * (1 - self.time_mix_k) + xb = x * self.time_mix_r + xx * (1 - self.time_mix_r) + a = self.aa(xa) + b = self.bb(xb) + return self.value(a * F.mish(b)) + +######################################################################################################## +# The RWKV Model with our blocks +######################################################################################################## + + +class Block(nn.Module,): + def __init__(self, args, d_model, layer_idx): + super().__init__() + self.args = args + self.layer_idx = layer_idx + + self.ln1 = nn.LayerNorm(d_model) + self.ln2 = nn.LayerNorm(d_model) + + if self.layer_idx == 0: + self.ln0 = nn.LayerNorm(d_model) + if args.my_pos_emb > 0: + self.pos_emb_x = nn.Parameter(torch.zeros((1,args.my_pos_emb,d_model))) + self.pos_emb_y = nn.Parameter(torch.zeros((args.my_pos_emb,1,d_model))) + + if self.layer_idx == 0 and self.args.pre_ffn > 0: + self.ffnPre = RWKV_ChannelMix(args, 0) + else: + self.att = RWKVTimeMixer(args, layer_idx) + + if 'g' in os.environ["RWKV_MY_TESTING"]: + self.ffn = MishGLU(args, layer_idx) + else: + self.ffn = RWKV_ChannelMix(args, layer_idx) + + if args.tiny_att_dim > 0 and self.layer_idx == args.tiny_att_layer: + self.tiny_ln = nn.LayerNorm(d_model) + self.tiny_q = nn.Linear(d_model, args.tiny_att_dim, bias=False) + self.tiny_k = nn.Linear(d_model, args.tiny_att_dim, bias=False) + self.tiny_v = nn.Linear(d_model,d_model, bias=False) + self.register_buffer("tiny_mask", torch.tril(torch.ones(args.ctx_len, args.ctx_len))) + + if args.dropout > 0: + self.drop0 = nn.Dropout(p = args.dropout) + self.drop1 = nn.Dropout(p = args.dropout) + + def forward(self, x, x_emb=None): + args = self.args + B, T, C = x.size() + if self.layer_idx == 0: + x = self.ln0(x) + if args.my_pos_emb > 0: + pos_emb = (self.pos_emb_x + self.pos_emb_y).reshape(T+1, -1)[:-1,:] + x = x + pos_emb + + if self.args.dropout == 0: + if self.layer_idx == 0 and args.pre_ffn > 0: + x = x + self.ffnPre(self.ln1(x)) + else: + x = x + self.att(self.ln1(x)) + x = x + self.ffn(self.ln2(x)) + else: + if self.layer_idx == 0 and args.pre_ffn > 0: + x = self.drop0(x + self.ffnPre(self.ln1(x))) + else: + x = self.drop0(x + self.att(self.ln1(x))) + x = self.drop1(x + self.ffn(self.ln2(x))) + + if args.tiny_att_dim > 0 and self.layer_idx == args.tiny_att_layer: + xx = self.tiny_ln(x) + q = self.tiny_q(xx)[:, :T, :] + k = self.tiny_k(xx)[:, :T, :] + c = (q @ k.transpose(-2, -1)) * (args.tiny_att_dim ** (-0.5)) + c = c.masked_fill(self.tiny_mask[:T, :T] == 0, 0) + x = x + c @ self.tiny_v(x_emb) + return x + + +class L2Wrap(torch.autograd.Function): + @staticmethod + def forward(ctx, loss, y): + ctx.save_for_backward(y) + return loss + + @staticmethod + def backward(ctx, grad_output): + y = ctx.saved_tensors[0] + # to encourage the logits to be close to 0 + factor = 1e-4 / (y.shape[0] * y.shape[1]) + maxx, ids = torch.max(y, -1, keepdim=True) + gy = torch.zeros_like(y) + gy.scatter_(-1, ids, maxx * factor) + return (grad_output, gy) + + +class RWKV(pl.LightningModule): + def __init__(self, args, vocab_size, d_model, n_layer): + super().__init__() + self.args = args + if not hasattr(args, 'dim_att'): + args.dim_att = args.n_embd + if not hasattr(args, 'dim_ffn'): + args.dim_ffn = args.n_embd * 4 + if not hasattr(args, 'tiny_att_layer'): + args.tiny_att_layer = -1 + if not hasattr(args, 'tiny_att_dim'): + args.tiny_att_dim = -1 + assert args.n_embd % 32 == 0 + assert args.dim_att % 32 == 0 + assert args.dim_ffn % 32 == 0 + + self.emb = nn.Embedding(vocab_size,d_model) + + self.blocks = nn.ModuleList([Block(args, i) for i in range(n_layer)]) + + self.ln_out = nn.LayerNorm(d_model) + self.head = nn.Linear(d_model, vocab_size, bias=False) + + if args.head_qk > 0: + self.head_q = nn.Linear(d_model, args.head_qk, bias=False) + self.head_k = nn.Linear(d_model, args.head_qk, bias=False) + self.register_buffer("copy_mask", torch.tril(torch.ones(args.ctx_len, args.ctx_len))) + if args.dropout > 0: + self.drop0 = nn.Dropout(p = args.dropout) + + def configure_optimizers(self): + args = self.args + + lr_decay = set() + lr_1x = set() + lr_2x = set() + lr_3x = set() + for n, p in self.named_parameters(): + if ("time_mix" in n) and (args.layerwise_lr > 0): + if args.my_pile_stage == 2: + lr_2x.add(n) + else: + lr_1x.add(n) + elif ("time_decay" in n) and (args.layerwise_lr > 0): + if args.my_pile_stage == 2: + lr_3x.add(n) + else: + lr_2x.add(n) + elif ("time_faaaa" in n) and (args.layerwise_lr > 0): + if args.my_pile_stage == 2: + lr_2x.add(n) + else: + lr_1x.add(n) + elif ("time_first" in n) and (args.layerwise_lr > 0): + lr_3x.add(n) + elif (len(p.squeeze().shape) >= 2) and (args.weight_decay > 0): + lr_decay.add(n) + else: + lr_1x.add(n) + + lr_decay = sorted(list(lr_decay)) + lr_1x = sorted(list(lr_1x)) + lr_2x = sorted(list(lr_2x)) + lr_3x = sorted(list(lr_3x)) + # print('decay', lr_decay) + # print('1x', lr_1x) + # print('2x', lr_2x) + # print('3x', lr_3x) + param_dict = {n: p for n, p in self.named_parameters()} + + if args.layerwise_lr > 0: + if args.my_pile_stage == 2: + optim_groups = [ + {"params": [param_dict[n] for n in lr_1x], "weight_decay": 0.0, "my_lr_scale": 1.0}, + {"params": [param_dict[n] for n in lr_2x], "weight_decay": 0.0, "my_lr_scale": 5.0},# test: 2e-3 / args.lr_init}, + {"params": [param_dict[n] for n in lr_3x], "weight_decay": 0.0, "my_lr_scale": 5.0},# test: 3e-3 / args.lr_init}, + ] + else: + optim_groups = [ + {"params": [param_dict[n] for n in lr_1x], "weight_decay": 0.0, "my_lr_scale": 1.0}, + {"params": [param_dict[n] for n in lr_2x], "weight_decay": 0.0, "my_lr_scale": 2.0}, + {"params": [param_dict[n] for n in lr_3x], "weight_decay": 0.0, "my_lr_scale": 3.0}, + ] + else: + optim_groups = [{"params": [param_dict[n] for n in lr_1x], "weight_decay": 0.0, "my_lr_scale": 1.0}] + + if args.weight_decay > 0: + optim_groups += [{"params": [param_dict[n] for n in lr_decay], "weight_decay": args.weight_decay, "my_lr_scale": 1.0}] + if self.deepspeed_offload: + return DeepSpeedCPUAdam(optim_groups, lr=self.args.lr_init, betas=self.args.betas, eps=self.args.adam_eps, bias_correction=True, adamw_mode=True, amsgrad=False) + return FusedAdam(optim_groups, lr=self.args.lr_init, betas=self.args.betas, eps=self.args.adam_eps, bias_correction=True, adam_w_mode=True, amsgrad=False) + else: + if self.deepspeed_offload: + return DeepSpeedCPUAdam(optim_groups, lr=self.args.lr_init, betas=self.args.betas, eps=self.args.adam_eps, bias_correction=True, adamw_mode=False, weight_decay=0, amsgrad=False) + return FusedAdam(optim_groups, lr=self.args.lr_init, betas=self.args.betas, eps=self.args.adam_eps, bias_correction=True, adam_w_mode=False, weight_decay=0, amsgrad=False) + # return ZeroOneAdam(optim_groups, lr=self.args.lr_init, betas=self.args.betas, eps=self.args.adam_eps, bias_correction=True, weight_decay=0, amsgrad=False, cuda_aware=False) + + @property + def deepspeed_offload(self) -> bool: + strategy = self.trainer.strategy + if isinstance(strategy, DeepSpeedStrategy): + cfg = strategy.config["zero_optimization"] + return cfg.get("offload_optimizer") or cfg.get("offload_param") + return False + + def forward(self, idx): + args = self.args + B, T = idx.size() + assert T <= args.ctx_len, "Cannot forward, model ctx_len is exhausted." + + x = self.emb(idx) + x_emb = x + + if args.dropout > 0: + x = self.drop0(x) + if args.tiny_att_dim > 0: + for block in self.blocks: + if args.grad_cp == 1: + x = deepspeed.checkpointing.checkpoint(block, x, x_emb) + else: + x = block(x, x_emb) + else: + for block in self.blocks: + if args.grad_cp == 1: + x = deepspeed.checkpointing.checkpoint(block, x) + else: + x = block(x) + + x = self.ln_out(x) + + if args.head_qk > 0: + q = self.head_q(x)[:, :T, :] + k = self.head_k(x)[:, :T, :] + c = (q @ k.transpose(-2, -1)) * (1.0 / args.head_qk) + c = c.masked_fill(self.copy_mask[:T, :T] == 0, 0) + + if "32" in os.environ["RWKV_FLOAT_MODE"]: + c = c @ F.one_hot(idx, num_classes=args.vocab_size) + elif os.environ["RWKV_FLOAT_MODE"] == "fp16": + c = c @ F.one_hot(idx, num_classes=args.vocab_size).half() + elif os.environ["RWKV_FLOAT_MODE"] == "bf16": + c = c @ F.one_hot(idx, num_classes=args.vocab_size).bfloat16() + + x = self.head(x) + c + else: + x = self.head(x) + + return x + + def training_step(self, batch, batch_idx): + args = self.args + if args.my_qa_mask != 1: + idx, targets = batch + logits = self(idx) + loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1)) + # if '0' in os.environ["RWKV_MY_TESTING"]: + # print('logits', logits) + # torch.set_printoptions(threshold=10000) + # print('idx', idx) + # exit(0) + else: + idx, targets, mask = batch + mask = mask.view(-1) + sum_mask = torch.sum(mask).item() + # if sum_mask == 0: + # return torch.tensor([0.0], requires_grad=True) + + logits = self(idx) + if sum_mask == mask.shape[0]: + loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1)) + # print('rank', self.global_rank, 'loss', loss.item()) + else: + loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1), reduction='none') + # loss_raw = loss + loss = torch.sum(loss * mask) / sum_mask + + # torch.set_printoptions(threshold=10000) + # if True: #self.global_rank == 1: + # tmp = '' + # sss = 0 + # ccc = 0 + # for i in range(mask.shape[0]): + # if mask[i] > 0: + # tmp += str(idx.view(-1)[i].item()) + ',' + # sss += loss_raw.view(-1)[i].float().item() + # ccc += 1 + # print('rank', self.global_rank, 'loss', loss.item(), 'lavg', sss / ccc)#, 'tmp', tmp, 'input', idx) + + return L2Wrap.apply(loss, logits) + + def training_step_end(self, batch_parts): + if pl.__version__[0]!='2': + all = self.all_gather(batch_parts) + if self.trainer.is_global_zero: + self.trainer.my_loss_all = all + + def generate_init_weight(self): + print( + f""" +############################################################################ +# +# Init model weight (slow for large models)... +# +############################################################################ +""" + ) + m = {} + for n in self.state_dict(): + p = self.state_dict()[n] + shape = p.shape + + gain = 1.0 + scale = 1.0 + if "ln_" in n or ".ln" in n or "time_" in n or "_mask" in n or "pos_emb" in n or '.mask.' in n: + if 'ln_x.weight' in n: + layer_scale = (1+int(n.split('.')[1])) / self.n_layer + m[n] = (p * 0.0) + (layer_scale ** 0.7) + else: + m[n] = p + else: + if n == "emb.weight": + scale = -1 * self.args.lr_init + else: + if shape[0] > shape[1]: + gain = math.sqrt(shape[0] / shape[1]) + + zero = [".att.output.", ".ffn.value.", ".ffn.receptance.", ".ffnPre.value.", ".ffnPre.receptance.", "head_q.", '.oo.', '.rr.'] + + for kk in zero: + if kk in n: + scale = 0 + if n == "head.weight": + scale = 0.5 + if "head_k." in n: + scale = 0.1 + if "head_q." in n: + scale = 0 + + print(f"{str(shape[0]).ljust(5)} {str(shape[1]).ljust(5)} {str(scale).ljust(4)} {n}") + + if self.args.accelerator.upper() == "GPU": + m[n] = torch.empty((shape[0], shape[1]), device="cuda") + else: + m[n] = torch.empty((shape[0], shape[1])) + + if scale == 0: + nn.init.zeros_(m[n]) + elif scale < 0: + nn.init.uniform_(m[n], a=scale, b=-scale) + else: + nn.init.orthogonal_(m[n], gain=gain * scale) + + m[n] = m[n].cpu() + if os.environ["RWKV_FLOAT_MODE"] == "fp16": + m[n] = m[n].half() + elif os.environ["RWKV_FLOAT_MODE"] == "bf16": + m[n] = m[n].bfloat16() + + # if n == "emb.weight": + # print(m[n]) + + gc.collect() + torch.cuda.empty_cache() + return m