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 std::vector<std::vector<size_t>> ShapeInference(std::vector<std::vector<size_t>> input) override { return input; }
79
80 std::vector<ETensorType> TypeInference(std::vector<ETensorType> input) override { return input; }
81
82 void Initialize(RModel& model) override {
83 if (!model.CheckIfTensorAlreadyExist(fNX)) {
84 throw std::runtime_error("TMVA::SOFIE - LayerNormalization - Tensor " + fNX + " not found.");
85 }
86 bool isDynamic = model.IsDynamicTensor(fNX);
87 fShapeX = model.GetDimTensorShape(fNX);
89 // Type of the output
90 fType = ConvertTypeToString(model.GetTensorType(fNX));
91 // Size of the input
92 fSize = fShapeX.size();
93 // Axis in [0, size)
95 // Shape of fShapeX[0, ..., fAxis)
96 fAxesShape = std::vector<Dim>(fShapeX.begin(), fShapeX.begin() + fAxis);
97 // Length of the axes
99 // Shape of fShapeX[fAxis, ..., fSize)
100 fNormalizedShape = std::vector<Dim>(fShapeX.begin() + fAxis, fShapeX.end());
101 // Length of the normalized axis
103 // length of the input
105 // Type of mean and std
106 ETensorType type = (fAttrStashType == 1) ? ETensorType::FLOAT : model.GetTensorType(fNX);
107 // Mean
108 if (!fNMean.empty()) {
109 // cannot use initializer list with one element since it is ambiguous
110 if (isDynamic)
111 // add size_t(-1) to indicate that shape is an expression
112 model.AddIntermediateTensor(fNMean, type, std::vector<Dim>(1,Dim{fAxesLength,std::size_t(-1)}));
113 else
114 model.AddIntermediateTensor(fNMean, type, std::vector<size_t>(1,std::stoi(fAxesLength)));
115 }
116 // Inverse Standard Deviation
117 if (!fNInvStdDev.empty()) {
118 if (isDynamic)
119 model.AddIntermediateTensor(fNInvStdDev, type, std::vector<Dim>(1,Dim{fAxesLength,std::size_t(-1)}));
120 else
121 model.AddIntermediateTensor(fNInvStdDev, type, std::vector<size_t>(1,std::stoi(fAxesLength)));
122 }
123 // if mean and stdev are not empty they are not defined in the output list
124 // Cast X to float
125 if (fAttrStashType == 1 && model.GetTensorType(fNX) != ETensorType::FLOAT) {
126 fCastToFloat = true;
127 fType = "float";
128 }
129 // scale shape
130 fShapeScale = model.GetDimTensorShape(fNScale);
131 // appends 1 to scale shapes if missing
132 size_t dimScale = fShapeScale.size();
133 if (dimScale < fSize) {
134 for (size_t i = 0; i < fSize-dimScale; i++)
135 fShapeScale.insert(fShapeScale.begin(), Dim{1});
136 }
137 // check also shape if consistent now
138 for (size_t i = 0; i < fSize; i++) {
139 if (fShapeScale[i].dim != 1 && fShapeScale[i] != fShapeX[i])
140 throw std::runtime_error("TMVA::SOFIE - LayerNormalization - Scale Tensor has invalid shape " + ConvertDimShapeToString(fShapeScale));
141 }
142 if (!fNB.empty()) {
143 fShapeB = model.GetDimTensorShape(fNB);
144 // appends 1 to bias shapes if missing
145 size_t dimB = fShapeB.size();
146 if (dimB < fShapeX.size()) {
147 for (size_t i = 0; i < fSize-dimB; i++)
148 fShapeB.insert(fShapeB.begin(), Dim{1});
149 }
150 for (size_t i = 0; i < fSize; i++) {
151 if (fShapeB[i].dim != 1 && fShapeB[i] != fShapeX[i])
152 throw std::runtime_error("TMVA::SOFIE - LayerNormalization - Bias Tensor has invalid shape " + ConvertDimShapeToString(fShapeScale));
153 }
154 }
155
156 model.AddIntermediateTensor(fNY, model.GetTensorType(fNX), fShapeY);
157 if (model.Verbose()){
158 std::cout << "LayerNormalization : " << fNX << " -> " << fNY << " shape " << ConvertDimShapeToString(fShapeY)
159 << " using bias and scale with shapes " << ConvertDimShapeToString(fShapeB) << " " << ConvertDimShapeToString(fShapeScale)
160 << std::endl;
161 }
162
163 model.AddNeededStdLib("cmath");
164
165 // the generated init code may broadcast the bias with UnidirectionalBroadcast
166 if (!fNBroadcastedB.empty())
167 model.AddNeededHelperFunction("UnidirectionalBroadcast");
168 }
169
170 std::string GenerateInitCode() override
171 {
172 std::stringstream out;
173 if (!fNBroadcastedB.empty()) {
174 out << SP << "// Broadcasting the bias of LayerNormalization op\n";
175 out << SP << "{\n";
176 out << SP << SP << "float* data = UTILITY::UnidirectionalBroadcast(tensor_";
177 out << fNB << ", " << ConvertDimShapeToString(fShapeB) << ", " << ConvertDimShapeToString(fShapeX) << ");\n";
178 out << SP << "std::copy(data, data + " << fLength << ", tensor_" << fNBroadcastedB << ");\n";
179 out << SP << "delete[] data;\n";
180 out << SP << "}\n";
181 }
182 return out.str();
183 }
184
185 std::string Generate(std::string opName) override
186 {
187 opName = "op_" + opName;
188 if (fShapeX.empty()) {
189 throw std::runtime_error("TMVA::SOFIE LayerNormalization operator " + opName +
190 " called to generate without being initialized first.");
191 }
192
193 std::stringstream out;
194
195 out << "//---- Layer Normalization operator " << opName << "\n";
196
197 // Loop over all the normalized axes i.e. [axis, ..., size)
198 std::vector<std::string> inputShape(fSize);
199
200 for (size_t i = 0; i < fSize; i++) {
201 inputShape[i] = fShapeX[i].GetVal();
202 }
203
205 std::string inputIndex = "axis_0 * " + strides[0].GetVal();
206 for (size_t i = 1; i < fSize; i++) {
207 inputIndex += " + axis_" + std::to_string(i);
208 if (i < fSize-1) inputIndex += " * " + strides[i].GetVal();
209 }
211 std::string scaleIndex;
212 for (size_t i = 0; i < fSize; i++) {
213 if (fShapeScale[i].dim != 1) {
214 if (!scaleIndex.empty()) scaleIndex += " + ";
215 scaleIndex += "axis_" + std::to_string(i);
216 if ( scaleStrides[i].dim != 1) scaleIndex += " * " + scaleStrides[i].GetVal();
217 }
218 }
219 if (scaleIndex.empty()) scaleIndex = "0";
220
222 std::string biasIndex;
223 for (size_t i = 0; i < fSize; i++) {
224 if (fShapeB[i].dim != 1) {
225 if (!biasIndex.empty()) biasIndex += " + ";
226 biasIndex += "axis_" + std::to_string(i);
227 if ( biasStrides[i].dim != 1) biasIndex += " * " + biasStrides[i].GetVal();
228 }
229 }
230 if (biasIndex.empty()) biasIndex = "0";
231
233 std::string axesIndex = "axis_" + std::to_string(0) + " * " + axesStrides[0].GetVal();
234 for (size_t i = 1; i < fAxis; i++) {
235 axesIndex += " + axis_" + std::to_string(i) + " * " + axesStrides[i].GetVal();
236 }
237
238
239 // compute mean and std-dev. Save in tensors if requested
240
241 out << SP << "// Compute the mean\n";
242
243 // Loop over all the outer dims in [0, fAxis)
244 for (size_t i = 0; i < fAxis; i++) {
245 std::string iIdx = "axis_" + std::to_string(i);
246 out << SP << "for (size_t " << iIdx << " = 0; " << iIdx << " < " << inputShape[i]
247 << "; " << iIdx << "++) {\n";
248 }
249 out << SP << SP << fType << " mean = 0.;\n";
250 // loop over the normalized dimensions (fAxis,....,N-1)
251 for (size_t j = fAxis; j < fSize; j++) {
252 std::string jIdx = "axis_" + std::to_string(j);
253 out << SP << SP << "for (size_t " << jIdx << " = 0; " << jIdx << " < " << inputShape[j]
254 << "; " << jIdx << "++) {\n";
255 }
256 out << SP << SP << SP << "mean += tensor_" << fNX << "[" << inputIndex << "];\n";
257 for (size_t j = fAxis; j < fSize; j++) {
258 out << SP << SP << "}\n";
259 }
260 out << SP << SP << "mean /= " << fType << "(" << fNormalizedLength << ");\n";
261
262
263 out << SP << "// Compute the inverse Standard Deviation\n";
264
265 // Set sum = 0
266 out << SP << SP << fType << " sum = 0.;\n";
267 // loop over all the dims in [0, fAxis)
268 for (size_t j = fAxis; j < fSize; j++) {
269 std::string jIdx = "axis_" + std::to_string(j);
270 out << SP << SP << "for (size_t " << jIdx << " = 0; " << jIdx << " < " << inputShape[j]
271 << "; " << jIdx << "++){\n";
272 }
273 out << SP << SP << SP << "float tmp = tensor_" << fNX << "[" << inputIndex << "] - mean;\n";
274 out << SP << SP << SP << "sum += tmp*tmp;\n";
275 for (size_t j = fAxis; j < fSize; j++) {
276 out << SP << SP << "}\n";
277 }
278 out << SP << SP << fType << " invStdDev = 1 / std::sqrt(";
279 out << "sum / " << fType << "(" << fNormalizedLength << ") + " << fAttrEpsilon << ");\n";
280
281
282 // set output mean and invStdDev if requested
283 if (!fNMean.empty())
284 out << SP << SP << "tensor_" << fNMean << "[" << axesIndex << "] = mean;\n";
285 if (!fNInvStdDev.empty())
286 out << SP << SP << "tensor_" << fNInvStdDev << "[" << axesIndex << "] = invStdDev;\n";
287
288 // scale and add bias
289
290 out << SP << "// Y = Scale o InvStdDev (X - Mean)\n";
291
292 for (size_t j = fAxis; j < fSize; j++) {
293 std::string jIdx = "axis_" + std::to_string(j);
294 out << SP << SP << "for (size_t " << jIdx << " = 0; " << jIdx << " < " << inputShape[j] << "; " << jIdx
295 << "++){\n";
296 }
297 out << SP << SP << SP << "tensor_" << fNY << "[" << inputIndex << "] = tensor_" << fNScale;
298 out << "[" << scaleIndex << "] * invStdDev * (tensor_" << fNX << "[" << inputIndex << "] - mean)";
299
300 // add bias if needed
301 if (!fNB.empty())
302 // assume bias has index as scale
303 out << " + tensor_" << fNB << "[" << biasIndex << "]";
304 out << ";\n";
305
306 // close loops on normalizing dim [..,fAxis,...fSize-1]
307 for (size_t j = fAxis; j < fSize; j++) {
308 out << SP << SP << "}\n";
309 }
310 // close loops on the other dimensions [0,...,fAxis]
311 for (size_t i = 0; i < fAxis; i++) {
312 out << SP << "}\n";
313 }
314
315 return out.str();
316 }
317
318 std::vector<std::string> GetBlasRoutines() override { return { std::string("Axpy") }; }
319
320 std::vector<std::string> GetStdLibs() override { return { std::string("cmath") }; }
321};
322
323} // namespace SOFIE
324} // namespace Experimental
325} // namespace TMVA
326
327#endif
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void input
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::vector< size_t > > ShapeInference(std::vector< std::vector< size_t > > input) override
std::vector< ETensorType > TypeInference(std::vector< ETensorType > input) override
std::vector< std::string_view > fInputTensorNames
Definition ROperator.hxx:47
const std::string SP
space used to correctly indent the generated C++ code
Definition ROperator.hxx:42
std::vector< std::string_view > fOutputTensorNames
Definition ROperator.hxx:48
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