| #------------------------------------------------------------- |
| # |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you 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. |
| # |
| #------------------------------------------------------------- |
| |
| source("nn/layers/softmax.dml") as softmax |
| source("scripts/nn/util.dml") as util |
| |
| |
| forward = function(matrix[double] query, matrix[double] value, |
| matrix[double] key = matrix("",rows=0, cols=0), integer D, |
| matrix[double] attention) |
| return (matrix[double] attention) { |
| /* |
| * Computes the forward pass for the attention layer. |
| * |
| * Inputs: |
| * - query: Input querys of shape (N,J*D). |
| * - value: Values for keys of shape (N,K*D). |
| * - key: *optional* Keys for values of shape (N,K*D). |
| * - If key is a matrix of length 0 value will be used as key. |
| * - D: Dimensionality of single query, value, key, |
| * - attention: Matrix of shape (N,J*D) on which to put the output. |
| * - |
| * Outputs: |
| * - attention: Attention on value(s) for given query(s), of shape (N,J*D). |
| */ |
| N = nrow(value) |
| K = ncol(value) / D |
| J = ncol(query) / D |
| |
| norm = 1/D^0.5 |
| if (!length(key)) |
| { |
| key = value |
| } |
| key_norm = key * norm |
| attention = matrix(0, rows=N, cols=J*D) |
| query_n = matrix(0, rows=J, cols=D) |
| key_norm_n = matrix(0, rows=K, cols=D) |
| value_n = matrix(0, rows=K, cols=D) |
| probs = matrix(0, rows=J, cols=K) |
| scores = matrix(0, rows=J, cols=K) |
| for (n in 1:N) |
| { |
| #reshape |
| query_n = matrix(query[n], rows=J, cols=D) |
| key_norm_n = matrix(key_norm[n],rows=K, cols=D) |
| value_n = matrix(value[n], rows=K, cols=D) |
| |
| scores = query_n %*% t(key_norm_n) |
| #column wise softmax |
| probs = t(softmax::forward(t(scores))) |
| attention[n] = matrix(probs %*% value_n, rows=1, cols=J*D) |
| } |
| } |
| |
| backward = function(matrix[double] dattention, |
| matrix[double] query, matrix[double] value, matrix[double] key=matrix("",rows=0,cols=0), |
| integer D, matrix[double] dquery, matrix[double] dvalue, matrix[double] dkey) |
| return (matrix[double] dquery, matrix[double] dvalue, matrix[double] dkey) |
| { |
| /* |
| * Computes the backward pass for the attention layer. |
| * |
| * Inputs: |
| * - dattention: Gradient wrt `attention` of shape (N,J*D). |
| * - query: *optional* Query input of shape (N,J*D). |
| * - key: Keys for values of shape (N,K*D). |
| * - If key is of length 0, the gradient dkey will be added to dvalue and dkey is 0. |
| * - value: Values for given key of shape (N,K*D). |
| * - D: Dimensionality of single query, key, value. |
| * - dquery: Matrix of shape (N,J*D) for output allocation. |
| * - dvalue: Matrix of shape (N,K*D) for output allocation. |
| * - dkey: Matrix of shape (N,K*D) for output allocation |
| * - |
| * Outputs: |
| * - dquery: Gradient wrt `query`, of shape (N, J*D). |
| * - dkey: Gradient wrt `key`, of shape (N,K*D). |
| * - dvalue: Gradient wrt `value` of shape (N,K*D). |
| */ |
| |
| N = nrow(value) |
| K = ncol(value) / D |
| J = ncol(query) / D |
| |
| norm = 1 / D^0.5 |
| use_key = length(key) > 0 |
| if (!use_key){ |
| key = value |
| } |
| key_norm = key * norm |
| |
| dquery = matrix(0, rows=N, cols=J*D) |
| dkey = matrix(0, rows=N, cols=K*D) |
| dvalue = matrix(0, rows=N, cols=K*D) |
| |
| query_n = matrix(0, rows=J, cols=D) |
| key_norm_n = matrix(0, rows=K, cols=D) |
| value_n = matrix(0, rows=K, cols=D) |
| dvalue_n = matrix(0, rows=K, cols=D) |
| probs = matrix(0, rows=J, cols=K) |
| scores = matrix(0, rows=J, cols=K) |
| for (n in 1:N) |
| { |
| #reshape |
| query_n = matrix(query[n], rows=J, cols=D) |
| key_norm_n = matrix(key_norm[n], rows=K, cols=D) |
| value_n = matrix(value[n], rows=K, cols=D) |
| dattention_n = matrix(dattention[n], rows=J, cols=D) |
| |
| scores = query_n %*% t(key_norm_n) |
| probs = t(softmax::forward(t(scores))) |
| |
| dscore = t(softmax::backward(value_n %*% t(dattention_n), t(scores))) |
| dquery[n] = matrix(dscore %*% key_norm_n, rows=1, cols=J*D) |
| if (use_key){ |
| dkey[n] = matrix(t(dscore) %*% query_n * norm, rows=1, cols=K*D) |
| dvalue[n] = matrix(t(probs) %*% dattention_n, rows=1, cols=K*D) |
| } |
| else{ |
| dvalue[n] = matrix(t(probs) %*% dattention_n + t(dscore) %*% query_n * norm, rows=1, cols=K*D) |
| } |
| |
| } |
| } |