1 /* Copyright 2016 The TensorFlow Authors. All Rights Reserved. 2 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 ==============================================================================*/ 15 16 // A set of lightweight wrappers which simplify access to Feature protos. 17 // 18 // TensorFlow Example proto uses associative maps on top of oneof fields. 19 // SequenceExample proto uses associative map of FeatureList. 20 // So accessing feature values is not very convenient. 21 // 22 // For example, to read a first value of integer feature "tag": 23 // int id = example.features().feature().at("tag").int64_list().value(0) 24 // 25 // to add a value: 26 // auto features = example->mutable_features(); 27 // (*features->mutable_feature())["tag"].mutable_int64_list()->add_value(id) 28 // 29 // For float features you have to use float_list, for string - bytes_list. 30 // 31 // To do the same with this library: 32 // int id = GetFeatureValues<int64>("tag", example).Get(0); 33 // GetFeatureValues<int64>("tag", &example)->Add(id); 34 // 35 // Modification of bytes features is slightly different: 36 // auto tag = GetFeatureValues<string>("tag", &example); 37 // *tag->Add() = "lorem ipsum"; 38 // 39 // To copy multiple values into a feature: 40 // AppendFeatureValues({1,2,3}, "tag", &example); 41 // 42 // GetFeatureValues gives you access to underlying data - RepeatedField object 43 // (RepeatedPtrField for byte list). So refer to its documentation of 44 // RepeatedField for full list of supported methods. 45 // 46 // NOTE: Due to the nature of oneof proto fields setting a feature of one type 47 // automatically clears all values stored as another type with the same feature 48 // key. 49 // 50 // This library also has tools to work with SequenceExample protos. 51 // 52 // To get a value from SequenceExample.context: 53 // int id = GetFeatureValues<protobuf_int64>("tag", se.context()).Get(0); 54 // To add a value to the context: 55 // GetFeatureValues<protobuf_int64>("tag", se.mutable_context())->Add(42); 56 // 57 // To add values to feature_lists: 58 // AppendFeatureValues({4.0}, 59 // GetFeatureList("images", &se)->Add()); 60 // AppendFeatureValues({5.0, 3.0}, 61 // GetFeatureList("images", &se)->Add()); 62 // This will create a feature list keyed as "images" with two features: 63 // feature_lists { 64 // feature_list { 65 // key: "images" 66 // value { 67 // feature { float_list { value: [4.0] } } 68 // feature { float_list { value: [5.0, 3.0] } } 69 // } 70 // } } 71 // 72 // Functions exposed by this library: 73 // HasFeature<[FeatureType]>(key, proto) -> bool 74 // Returns true if a feature with the specified key, and optionally 75 // FeatureType, belongs to the Features or Example proto. 76 // HasFeatureList(key, sequence_example) -> bool 77 // Returns true if SequenceExample has a feature_list with the key. 78 // GetFeatureValues<FeatureType>(key, proto) -> RepeatedField<FeatureType> 79 // Returns values for the specified key and the FeatureType. 80 // Supported types for the proto: Example, Features. 81 // GetFeatureList(key, sequence_example) -> RepeatedPtrField<Feature> 82 // Returns Feature protos associated with a key. 83 // AppendFeatureValues(begin, end, feature) 84 // AppendFeatureValues(container or initializer_list, feature) 85 // Copies values into a Feature. 86 // AppendFeatureValues(begin, end, key, proto) 87 // AppendFeatureValues(container or initializer_list, key, proto) 88 // Copies values into Features and Example protos with the specified key. 89 // 90 // Auxiliary functions, it is unlikely you'll need to use them directly: 91 // GetFeatures(proto) -> Features 92 // A convenience function to get Features proto. 93 // Supported types for the proto: Example, Features. 94 // GetFeature(key, proto) -> Feature* 95 // Returns a Feature proto for the specified key, creates a new if 96 // necessary. Supported types for the proto: Example, Features. 97 // GetFeatureValues<FeatureType>(feature) -> RepeatedField<FeatureType> 98 // Returns values of the feature for the FeatureType. 99 100 #ifndef TENSORFLOW_CORE_EXAMPLE_FEATURE_UTIL_H_ 101 #define TENSORFLOW_CORE_EXAMPLE_FEATURE_UTIL_H_ 102 103 #include <iterator> 104 #include <type_traits> 105 106 #include "absl/base/macros.h" 107 #include "tensorflow/core/example/example.pb.h" 108 #include "tensorflow/core/example/feature.pb.h" 109 #include "tensorflow/core/lib/core/stringpiece.h" 110 #include "tensorflow/core/platform/protobuf.h" 111 #include "tensorflow/core/platform/types.h" 112 113 namespace tensorflow { 114 115 namespace internal { 116 117 // TODO(gorban): Update all clients in a followup CL. 118 // Returns a reference to a feature corresponding to the name. 119 // Note: it will create a new Feature if it is missing in the example. 120 ABSL_DEPRECATED("Use GetFeature instead.") 121 Feature& ExampleFeature(const string& name, Example* example); 122 123 // Specializations of RepeatedFieldTrait define a type of RepeatedField 124 // corresponding to a selected feature type. 125 template <typename FeatureType> 126 struct RepeatedFieldTrait; 127 128 template <> 129 struct RepeatedFieldTrait<protobuf_int64> { 130 using Type = protobuf::RepeatedField<protobuf_int64>; 131 }; 132 133 template <> 134 struct RepeatedFieldTrait<float> { 135 using Type = protobuf::RepeatedField<float>; 136 }; 137 138 template <> 139 struct RepeatedFieldTrait<string> { 140 using Type = protobuf::RepeatedPtrField<string>; 141 }; 142 143 // Specializations of FeatureTrait define a type of feature corresponding to a 144 // selected value type. 145 template <typename ValueType, class Enable = void> 146 struct FeatureTrait; 147 148 template <typename ValueType> 149 struct FeatureTrait<ValueType, typename std::enable_if< 150 std::is_integral<ValueType>::value>::type> { 151 using Type = protobuf_int64; 152 }; 153 154 template <typename ValueType> 155 struct FeatureTrait< 156 ValueType, 157 typename std::enable_if<std::is_floating_point<ValueType>::value>::type> { 158 using Type = float; 159 }; 160 161 template <typename T> 162 struct is_string 163 : public std::integral_constant< 164 bool, 165 std::is_same<char*, typename std::decay<T>::type>::value || 166 std::is_same<const char*, typename std::decay<T>::type>::value> { 167 }; 168 169 template <> 170 struct is_string<string> : std::true_type {}; 171 172 template <> 173 struct is_string<::tensorflow::StringPiece> : std::true_type {}; 174 175 template <typename ValueType> 176 struct FeatureTrait< 177 ValueType, typename std::enable_if<is_string<ValueType>::value>::type> { 178 using Type = string; 179 }; 180 181 } // namespace internal 182 183 // Returns true if sequence_example has a feature_list with the specified key. 184 bool HasFeatureList(const string& key, const SequenceExample& sequence_example); 185 186 template <typename T> 187 struct TypeHasFeatures : std::false_type {}; 188 189 template <> 190 struct TypeHasFeatures<Example> : std::true_type {}; 191 192 template <> 193 struct TypeHasFeatures<Features> : std::true_type {}; 194 195 // A family of template functions to return mutable Features proto from a 196 // container proto. Supported ProtoTypes: Example, Features. 197 template <typename ProtoType> 198 typename std::enable_if<TypeHasFeatures<ProtoType>::value, Features*>::type 199 GetFeatures(ProtoType* proto); 200 201 template <typename ProtoType> 202 typename std::enable_if<TypeHasFeatures<ProtoType>::value, 203 const Features&>::type 204 GetFeatures(const ProtoType& proto); 205 206 // Base declaration of a family of template functions to return a read only 207 // repeated field of feature values. 208 template <typename FeatureType> 209 const typename internal::RepeatedFieldTrait<FeatureType>::Type& 210 GetFeatureValues(const Feature& feature); 211 212 // Returns a read only repeated field corresponding to a feature with the 213 // specified name and FeatureType. Supported ProtoTypes: Example, Features. 214 template <typename FeatureType, typename ProtoType> 215 const typename internal::RepeatedFieldTrait<FeatureType>::Type& 216 GetFeatureValues(const string& key, const ProtoType& proto) { 217 return GetFeatureValues<FeatureType>(GetFeatures(proto).feature().at(key)); 218 } 219 220 // Returns a mutable repeated field of a feature values. 221 template <typename FeatureType> 222 typename internal::RepeatedFieldTrait<FeatureType>::Type* GetFeatureValues( 223 Feature* feature); 224 225 // Returns a mutable repeated field corresponding to a feature with the 226 // specified name and FeatureType. Supported ProtoTypes: Example, Features. 227 template <typename FeatureType, typename ProtoType> 228 typename internal::RepeatedFieldTrait<FeatureType>::Type* GetFeatureValues( 229 const string& key, ProtoType* proto) { 230 ::tensorflow::Feature& feature = 231 (*GetFeatures(proto)->mutable_feature())[key]; 232 return GetFeatureValues<FeatureType>(&feature); 233 } 234 235 // Returns a Feature proto for the specified key, creates a new if necessary. 236 // Supported types for the proto: Example, Features. 237 template <typename ProtoType> 238 Feature* GetFeature(const string& key, ProtoType* proto) { 239 return &(*GetFeatures(proto)->mutable_feature())[key]; 240 } 241 242 // Returns a repeated field with features corresponding to a feature_list key. 243 const protobuf::RepeatedPtrField<Feature>& GetFeatureList( 244 const string& key, const SequenceExample& sequence_example); 245 246 // Returns a mutable repeated field with features corresponding to a 247 // feature_list key. It will create a new FeatureList if necessary. 248 protobuf::RepeatedPtrField<Feature>* GetFeatureList( 249 const string& feature_list_key, SequenceExample* sequence_example); 250 251 template <typename IteratorType> 252 void AppendFeatureValues(IteratorType first, IteratorType last, 253 Feature* feature) { 254 using FeatureType = typename internal::FeatureTrait< 255 typename std::iterator_traits<IteratorType>::value_type>::Type; 256 std::copy(first, last, 257 protobuf::RepeatedFieldBackInserter( 258 GetFeatureValues<FeatureType>(feature))); 259 } 260 261 template <typename ValueType> 262 void AppendFeatureValues(std::initializer_list<ValueType> container, 263 Feature* feature) { 264 AppendFeatureValues(container.begin(), container.end(), feature); 265 } 266 267 template <typename ContainerType> 268 void AppendFeatureValues(const ContainerType& container, Feature* feature) { 269 using IteratorType = typename ContainerType::const_iterator; 270 AppendFeatureValues<IteratorType>(container.begin(), container.end(), 271 feature); 272 } 273 274 // Copies elements from the range, defined by [first, last) into the feature 275 // obtainable from the (proto, key) combination. 276 template <typename IteratorType, typename ProtoType> 277 void AppendFeatureValues(IteratorType first, IteratorType last, 278 const string& key, ProtoType* proto) { 279 AppendFeatureValues(first, last, GetFeature(key, GetFeatures(proto))); 280 } 281 282 // Copies all elements from the container into a feature. 283 template <typename ContainerType, typename ProtoType> 284 void AppendFeatureValues(const ContainerType& container, const string& key, 285 ProtoType* proto) { 286 using IteratorType = typename ContainerType::const_iterator; 287 AppendFeatureValues<IteratorType>(container.begin(), container.end(), key, 288 proto); 289 } 290 291 // Copies all elements from the initializer list into a Feature contained by 292 // Features or Example proto. 293 template <typename ValueType, typename ProtoType> 294 void AppendFeatureValues(std::initializer_list<ValueType> container, 295 const string& key, ProtoType* proto) { 296 using IteratorType = 297 typename std::initializer_list<ValueType>::const_iterator; 298 AppendFeatureValues<IteratorType>(container.begin(), container.end(), key, 299 proto); 300 } 301 302 // Returns true if a feature with the specified key belongs to the Features. 303 // The template parameter pack accepts zero or one template argument - which 304 // is FeatureType. If the FeatureType not specified (zero template arguments) 305 // the function will not check the feature type. Otherwise it will return false 306 // if the feature has a wrong type. 307 template <typename... FeatureType> 308 bool HasFeature(const string& key, const Features& features); 309 310 // Returns true if a feature with the specified key belongs to the Example. 311 // Doesn't check feature type if used without FeatureType, otherwise the 312 // specialized versions return false if the feature has a wrong type. 313 template <typename... FeatureType> 314 bool HasFeature(const string& key, const Example& example) { 315 return HasFeature<FeatureType...>(key, GetFeatures(example)); 316 } 317 318 // TODO(gorban): update all clients in a followup CL. 319 template <typename... FeatureType> 320 ABSL_DEPRECATED("Use HasFeature instead.") 321 bool ExampleHasFeature(const string& key, const Example& example) { 322 return HasFeature<FeatureType...>(key, example); 323 } 324 325 } // namespace tensorflow 326 #endif // TENSORFLOW_CORE_EXAMPLE_FEATURE_UTIL_H_ 327