Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ROperator_LayerNormalization.hxx
Go to the documentation of this file.
1#ifndef TMVA_SOFIE_ROPERATOR_LAYERNORMALIZATION
2#define TMVA_SOFIE_ROPERATOR_LAYERNORMALIZATION
3
4#include "TMVA/RModel.hxx"
5#include "TMVA/ROperator.hxx"
7
8#include <sstream>
9#include <string>
10
11namespace TMVA {
12namespace Experimental {
13namespace SOFIE {
14
15template <typename T>
17private:
18 bool fCastToFloat = false; // flag to indicate if operation 1 are in floats (to be impl)
22
23 std::string fNX;
24 std::string fNScale;
25 std::string fNB;
26 std::string fNY;
27 std::string fNMean;
28 std::string fNInvStdDev;
29
30 std::string fNCastedX;
31 std::string fNNormalizedX;
32 std::string fNBroadcastedB;
33
34 std::vector<Dim> fShapeX;
35 std::vector<Dim> fShapeScale;
36 std::vector<Dim> fShapeB;
37 std::vector<Dim> fShapeY;
38 std::vector<Dim> fShapeMean;
39 std::vector<Dim> fShapeInvStdDev;
40
41 size_t fAxis; // axis in [0, size)
42 size_t fSize; // Size of the input
43 // size_t fAxisDim;
44
45 std::vector<Dim> fNormalizedShape; // shape from X[ axis,...,N-1]
46 std::vector<Dim> fAxesShape; // shape from X[0,..,axis-1]
47 // lengths in string format
48 std::string fLength; // Length of the input
49 std::string fNormalizedLength;
50 std::string fAxesLength;
51
52 std::string fType;
53
54public:
56
57 ROperator_LayerNormalization(int axis, float epsilon, size_t stashType, const std::string &nameX,
58 const std::string &nameScale, const std::string &nameB, const std::string &nameY,
59 const std::string &nameMean, const std::string &nameInvStdDev)
60 : fAttrAxis(axis), fAttrEpsilon(epsilon), fAttrStashType(stashType), fNX(UTILITY::Clean_name(nameX)),
61 fNScale(UTILITY::Clean_name(nameScale)), fNB(UTILITY::Clean_name(nameB)),
62 fNY(UTILITY::Clean_name(nameY)), fNMean(UTILITY::Clean_name(nameMean)), fNInvStdDev(UTILITY::Clean_name(nameInvStdDev))
63 {
65 if (!fNB.empty()){
66 fInputTensorNames.emplace_back(fNB);
67 }
68
70 if (!fNMean.empty()){
71 fOutputTensorNames.emplace_back(fNMean);
72 }
73 if (!fNInvStdDev.empty()){
74 fOutputTensorNames.emplace_back(fNInvStdDev);
75 }
76 }
77
78 void Initialize(RModel& model) override {
79 if (!model.CheckIfTensorAlreadyExist(fNX)) {
80 throw std::runtime_error("TMVA::SOFIE - LayerNormalization - Tensor " + fNX + " not found.");
81 }
82 bool isDynamic = model.IsDynamicTensor(fNX);
83 fShapeX = model.GetDimTensorShape(fNX);
85 // Type of the output
86 fType = ConvertTypeToString(model.GetTensorType(fNX));
87 // Size of the input
88 fSize = fShapeX.size();
89 // Axis in [0, size)
91 // Shape of fShapeX[0, ..., fAxis)
92 fAxesShape = std::vector<Dim>(fShapeX.begin(), fShapeX.begin() + fAxis);
93 // Length of the axes
95 // Shape of fShapeX[fAxis, ..., fSize)
96 fNormalizedShape = std::vector<Dim>(fShapeX.begin() + fAxis, fShapeX.end());
97 // Length of the normalized axis
99 // length of the input
101 // Type of mean and std
102 ETensorType type = (fAttrStashType == 1) ? ETensorType::FLOAT : model.GetTensorType(fNX);
103 // Mean
104 if (!fNMean.empty()) {
105 // cannot use initializer list with one element since it is ambiguous
106 if (isDynamic)
107 // add size_t(-1) to indicate that shape is an expression
108 model.AddIntermediateTensor(fNMean, type, std::vector<Dim>(1,Dim{fAxesLength,std::size_t(-1)}));
109 else
110 model.AddIntermediateTensor(fNMean, type, std::vector<size_t>(1,std::stoi(fAxesLength)));
111 }
112 // Inverse Standard Deviation
113 if (!fNInvStdDev.empty()) {
114 if (isDynamic)
115 model.AddIntermediateTensor(fNInvStdDev, type, std::vector<Dim>(1,Dim{fAxesLength,std::size_t(-1)}));
116 else
117 model.AddIntermediateTensor(fNInvStdDev, type, std::vector<size_t>(1,std::stoi(fAxesLength)));
118 }
119 // if mean and stdev are not empty they are not defined in the output list
120 // Cast X to float
121 if (fAttrStashType == 1 && model.GetTensorType(fNX) != ETensorType::FLOAT) {
122 fCastToFloat = true;
123 fType = "float";
124 }
125 // scale shape
126 fShapeScale = model.GetDimTensorShape(fNScale);
127 // appends 1 to scale shapes if missing
128 size_t dimScale = fShapeScale.size();
129 if (dimScale < fSize) {
130 for (size_t i = 0; i < fSize-dimScale; i++)
131 fShapeScale.insert(fShapeScale.begin(), Dim{1});
132 }
133 // check also shape if consistent now
134 for (size_t i = 0; i < fSize; i++) {
135 if (fShapeScale[i].dim != 1 && fShapeScale[i] != fShapeX[i])
136 throw std::runtime_error("TMVA::SOFIE - LayerNormalization - Scale Tensor has invalid shape " + ConvertDimShapeToString(fShapeScale));
137 }
138 if (!fNB.empty()) {
139 fShapeB = model.GetDimTensorShape(fNB);
140 // appends 1 to bias shapes if missing
141 size_t dimB = fShapeB.size();
142 if (dimB < fShapeX.size()) {
143 for (size_t i = 0; i < fSize-dimB; i++)
144 fShapeB.insert(fShapeB.begin(), Dim{1});
145 }
146 for (size_t i = 0; i < fSize; i++) {
147 if (fShapeB[i].dim != 1 && fShapeB[i] != fShapeX[i])
148 throw std::runtime_error("TMVA::SOFIE - LayerNormalization - Bias Tensor has invalid shape " + ConvertDimShapeToString(fShapeScale));
149 }
150 }
151
152 model.AddIntermediateTensor(fNY, model.GetTensorType(fNX), fShapeY);
153 if (model.Verbose()){
154 std::cout << "LayerNormalization : " << fNX << " -> " << fNY << " shape " << ConvertDimShapeToString(fShapeY)
155 << " using bias and scale with shapes " << ConvertDimShapeToString(fShapeB) << " " << ConvertDimShapeToString(fShapeScale)
156 << std::endl;
157 }
158
159 model.AddNeededStdLib("cmath");
160
161 // the generated init code may broadcast the bias with UnidirectionalBroadcast
162 if (!fNBroadcastedB.empty())
163 model.AddNeededHelperFunction("UnidirectionalBroadcast");
164 }
165
166 std::string GenerateInitCode() override
167 {
168 std::stringstream out;
169 if (!fNBroadcastedB.empty()) {
170 out << SP << "// Broadcasting the bias of LayerNormalization op\n";
171 out << SP << "{\n";
172 out << SP << SP << "float* data = UTILITY::UnidirectionalBroadcast(tensor_";
173 out << fNB << ", " << ConvertDimShapeToString(fShapeB) << ", " << ConvertDimShapeToString(fShapeX) << ");\n";
174 out << SP << "std::copy(data, data + " << fLength << ", tensor_" << fNBroadcastedB << ");\n";
175 out << SP << "delete[] data;\n";
176 out << SP << "}\n";
177 }
178 return out.str();
179 }
180
181 std::string Generate(std::string opName) override
182 {
183 opName = "op_" + opName;
184 if (fShapeX.empty()) {
185 throw std::runtime_error("TMVA::SOFIE LayerNormalization operator " + opName +
186 " called to generate without being initialized first.");
187 }
188
189 std::stringstream out;
190
191 out << "//---- Layer Normalization operator " << opName << "\n";
192
193 // Loop over all the normalized axes i.e. [axis, ..., size)
194 std::vector<std::string> inputShape(fSize);
195
196 for (size_t i = 0; i < fSize; i++) {
197 inputShape[i] = fShapeX[i].GetVal();
198 }
199
201 std::string inputIndex = "axis_0 * " + strides[0].GetVal();
202 for (size_t i = 1; i < fSize; i++) {
203 inputIndex += " + axis_" + std::to_string(i);
204 if (i < fSize-1) inputIndex += " * " + strides[i].GetVal();
205 }
206 auto scaleStrides = UTILITY::ComputeStrideFromShape(fShapeScale);
207 std::string scaleIndex;
208 for (size_t i = 0; i < fSize; i++) {
209 if (fShapeScale[i].dim != 1) {
210 if (!scaleIndex.empty()) scaleIndex += " + ";
211 scaleIndex += "axis_" + std::to_string(i);
212 if ( scaleStrides[i].dim != 1) scaleIndex += " * " + scaleStrides[i].GetVal();
213 }
214 }
215 if (scaleIndex.empty()) scaleIndex = "0";
216
217 auto biasStrides = UTILITY::ComputeStrideFromShape(fShapeB);
218 std::string biasIndex;
219 for (size_t i = 0; i < fSize; i++) {
220 if (fShapeB[i].dim != 1) {
221 if (!biasIndex.empty()) biasIndex += " + ";
222 biasIndex += "axis_" + std::to_string(i);
223 if ( biasStrides[i].dim != 1) biasIndex += " * " + biasStrides[i].GetVal();
224 }
225 }
226 if (biasIndex.empty()) biasIndex = "0";
227
229 std::string axesIndex = "axis_" + std::to_string(0) + " * " + axesStrides[0].GetVal();
230 for (size_t i = 1; i < fAxis; i++) {
231 axesIndex += " + axis_" + std::to_string(i) + " * " + axesStrides[i].GetVal();
232 }
233
234
235 // compute mean and std-dev. Save in tensors if requested
236
237 out << SP << "// Compute the mean\n";
238
239 // Loop over all the outer dims in [0, fAxis)
240 for (size_t i = 0; i < fAxis; i++) {
241 std::string iIdx = "axis_" + std::to_string(i);
242 out << SP << "for (size_t " << iIdx << " = 0; " << iIdx << " < " << inputShape[i]
243 << "; " << iIdx << "++) {\n";
244 }
245 out << SP << SP << fType << " mean = 0.;\n";
246 // loop over the normalized dimensions (fAxis,....,N-1)
247 for (size_t j = fAxis; j < fSize; j++) {
248 std::string jIdx = "axis_" + std::to_string(j);
249 out << SP << SP << "for (size_t " << jIdx << " = 0; " << jIdx << " < " << inputShape[j]
250 << "; " << jIdx << "++) {\n";
251 }
252 out << SP << SP << SP << "mean += tensor_" << fNX << "[" << inputIndex << "];\n";
253 for (size_t j = fAxis; j < fSize; j++) {
254 out << SP << SP << "}\n";
255 }
256 out << SP << SP << "mean /= " << fType << "(" << fNormalizedLength << ");\n";
257
258
259 out << SP << "// Compute the inverse Standard Deviation\n";
260
261 // Set sum = 0
262 out << SP << SP << fType << " sum = 0.;\n";
263 // loop over all the dims in [0, fAxis)
264 for (size_t j = fAxis; j < fSize; j++) {
265 std::string jIdx = "axis_" + std::to_string(j);
266 out << SP << SP << "for (size_t " << jIdx << " = 0; " << jIdx << " < " << inputShape[j]
267 << "; " << jIdx << "++){\n";
268 }
269 out << SP << SP << SP << "float tmp = tensor_" << fNX << "[" << inputIndex << "] - mean;\n";
270 out << SP << SP << SP << "sum += tmp*tmp;\n";
271 for (size_t j = fAxis; j < fSize; j++) {
272 out << SP << SP << "}\n";
273 }
274 out << SP << SP << fType << " invStdDev = 1 / std::sqrt(";
275 out << "sum / " << fType << "(" << fNormalizedLength << ") + " << fAttrEpsilon << ");\n";
276
277
278 // set output mean and invStdDev if requested
279 if (!fNMean.empty())
280 out << SP << SP << "tensor_" << fNMean << "[" << axesIndex << "] = mean;\n";
281 if (!fNInvStdDev.empty())
282 out << SP << SP << "tensor_" << fNInvStdDev << "[" << axesIndex << "] = invStdDev;\n";
283
284 // scale and add bias
285
286 out << SP << "// Y = Scale o InvStdDev (X - Mean)\n";
287
288 for (size_t j = fAxis; j < fSize; j++) {
289 std::string jIdx = "axis_" + std::to_string(j);
290 out << SP << SP << "for (size_t " << jIdx << " = 0; " << jIdx << " < " << inputShape[j] << "; " << jIdx
291 << "++){\n";
292 }
293 out << SP << SP << SP << "tensor_" << fNY << "[" << inputIndex << "] = tensor_" << fNScale;
294 out << "[" << scaleIndex << "] * invStdDev * (tensor_" << fNX << "[" << inputIndex << "] - mean)";
295
296 // add bias if needed
297 if (!fNB.empty())
298 // assume bias has index as scale
299 out << " + tensor_" << fNB << "[" << biasIndex << "]";
300 out << ";\n";
301
302 // close loops on normalizing dim [..,fAxis,...fSize-1]
303 for (size_t j = fAxis; j < fSize; j++) {
304 out << SP << SP << "}\n";
305 }
306 // close loops on the other dimensions [0,...,fAxis]
307 for (size_t i = 0; i < fAxis; i++) {
308 out << SP << "}\n";
309 }
310
311 return out.str();
312 }
313
314 std::vector<std::string> GetBlasRoutines() override { return { std::string("Axpy") }; }
315
316 std::vector<std::string> GetStdLibs() override { return { std::string("cmath") }; }
317};
318
319} // namespace SOFIE
320} // namespace Experimental
321} // namespace TMVA
322
323#endif
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
ROperator_LayerNormalization(int axis, float epsilon, size_t stashType, const std::string &nameX, const std::string &nameScale, const std::string &nameB, const std::string &nameY, const std::string &nameMean, const std::string &nameInvStdDev)
std::vector< std::string_view > fInputTensorNames
Definition ROperator.hxx:44
const std::string SP
space used to correctly indent the generated C++ code
Definition ROperator.hxx:40
std::vector< std::string_view > fOutputTensorNames
Definition ROperator.hxx:45
std::vector< size_t > ComputeStrideFromShape(const std::vector< size_t > &shape)
compute stride of a tensor given its shape (assume layout is row-major)
std::string ConvertDimShapeToString(const std::vector< Dim > &shape)
std::string ConvertTypeToString(ETensorType type)
std::string ConvertDimShapeToLength(const std::vector< Dim > &shape)
create variable transformations