1 /* Copyright 2015 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 // Operators that deal with SummaryProtos (encoded as DT_STRING tensors) as 17 // inputs or outputs in various ways. 18 19 // See docs in ../ops/summary_ops.cc. 20 21 #include <unordered_set> 22 23 #include "tensorflow/core/framework/op_kernel.h" 24 #include "tensorflow/core/framework/register_types.h" 25 #include "tensorflow/core/framework/resource_mgr.h" 26 #include "tensorflow/core/framework/summary.pb.h" 27 #include "tensorflow/core/lib/core/errors.h" 28 #include "tensorflow/core/lib/histogram/histogram.h" 29 #include "tensorflow/core/platform/logging.h" 30 #include "tensorflow/core/platform/protobuf.h" 31 32 namespace tensorflow { 33 34 template <typename T> 35 class SummaryScalarOp : public OpKernel { 36 public: SummaryScalarOp(OpKernelConstruction * context)37 explicit SummaryScalarOp(OpKernelConstruction* context) : OpKernel(context) {} 38 Compute(OpKernelContext * c)39 void Compute(OpKernelContext* c) override { 40 const Tensor& tags = c->input(0); 41 const Tensor& values = c->input(1); 42 43 OP_REQUIRES( 44 c, 45 tags.IsSameSize(values) || 46 (IsLegacyScalar(tags.shape()) && IsLegacyScalar(values.shape())), 47 errors::InvalidArgument( 48 "tags and values not the same shape: ", tags.shape().DebugString(), 49 " != ", values.shape().DebugString(), SingleTag(tags))); 50 auto Ttags = tags.flat<string>(); 51 auto Tvalues = values.flat<T>(); 52 Summary s; 53 for (int i = 0; i < Ttags.size(); i++) { 54 Summary::Value* v = s.add_value(); 55 v->set_tag(Ttags(i)); 56 v->set_simple_value(float(Tvalues(i))); 57 } 58 59 Tensor* summary_tensor = nullptr; 60 OP_REQUIRES_OK(c, c->allocate_output(0, TensorShape({}), &summary_tensor)); 61 CHECK(s.SerializeToString(&summary_tensor->scalar<string>()())); 62 } 63 64 // If there's only one tag, include it in the error message SingleTag(const Tensor & tags)65 static string SingleTag(const Tensor& tags) { 66 if (tags.NumElements() == 1) { 67 return strings::StrCat(" (tag '", tags.flat<string>()(0), "')"); 68 } else { 69 return ""; 70 } 71 } 72 }; 73 74 template <typename T> 75 class SummaryHistoOp : public OpKernel { 76 public: 77 // SummaryHistoOp could be extended to take a list of custom bucket 78 // boundaries as an option. SummaryHistoOp(OpKernelConstruction * context)79 explicit SummaryHistoOp(OpKernelConstruction* context) : OpKernel(context) {} 80 Compute(OpKernelContext * c)81 void Compute(OpKernelContext* c) override { 82 const Tensor& tags = c->input(0); 83 const Tensor& values = c->input(1); 84 const auto flat = values.flat<T>(); 85 OP_REQUIRES(c, IsLegacyScalar(tags.shape()), 86 errors::InvalidArgument("tags must be scalar")); 87 // Build histogram of values in "values" tensor 88 histogram::Histogram histo; 89 for (int64 i = 0; i < flat.size(); i++) { 90 const double double_val = static_cast<double>(flat(i)); 91 if (Eigen::numext::isnan(double_val)) { 92 c->SetStatus( 93 errors::InvalidArgument("Nan in summary histogram for: ", name())); 94 break; 95 } else if (Eigen::numext::isinf(double_val)) { 96 c->SetStatus(errors::InvalidArgument( 97 "Infinity in summary histogram for: ", name())); 98 break; 99 } 100 histo.Add(double_val); 101 } 102 103 Summary s; 104 Summary::Value* v = s.add_value(); 105 v->set_tag(tags.scalar<string>()()); 106 histo.EncodeToProto(v->mutable_histo(), false /* Drop zero buckets */); 107 108 Tensor* summary_tensor = nullptr; 109 OP_REQUIRES_OK(c, c->allocate_output(0, TensorShape({}), &summary_tensor)); 110 CHECK(s.SerializeToString(&summary_tensor->scalar<string>()())); 111 } 112 }; 113 114 #define REGISTER(T) \ 115 REGISTER_KERNEL_BUILDER( \ 116 Name("ScalarSummary").Device(DEVICE_CPU).TypeConstraint<T>("T"), \ 117 SummaryScalarOp<T>); \ 118 REGISTER_KERNEL_BUILDER( \ 119 Name("HistogramSummary").Device(DEVICE_CPU).TypeConstraint<T>("T"), \ 120 SummaryHistoOp<T>); 121 TF_CALL_REAL_NUMBER_TYPES(REGISTER) 122 #undef REGISTER 123 124 struct HistogramResource : public ResourceBase { 125 histogram::ThreadSafeHistogram histogram; 126 DebugStringtensorflow::HistogramResource127 string DebugString() const override { 128 return "A histogram summary. Stats ..."; 129 } 130 }; 131 132 class SummaryMergeOp : public OpKernel { 133 public: SummaryMergeOp(OpKernelConstruction * context)134 explicit SummaryMergeOp(OpKernelConstruction* context) : OpKernel(context) {} 135 Compute(OpKernelContext * c)136 void Compute(OpKernelContext* c) override { 137 Summary s; 138 std::unordered_set<string> tags; 139 for (int input_num = 0; input_num < c->num_inputs(); input_num++) { 140 const Tensor& in = c->input(input_num); 141 auto in_vec = in.flat<string>(); 142 for (int i = 0; i < in_vec.dimension(0); i++) { 143 const string& s_in = in_vec(i); 144 Summary summary_in; 145 if (!ParseProtoUnlimited(&summary_in, s_in)) { 146 c->SetStatus(errors::InvalidArgument( 147 "Could not parse one of the summary inputs")); 148 return; 149 } 150 151 for (int v = 0; v < summary_in.value_size(); v++) { 152 const string& tag = summary_in.value(v).tag(); 153 // The tag is unused by the TensorSummary op, so no need to check 154 // for duplicates. 155 if ((!tag.empty()) && !tags.insert(tag).second) { 156 c->SetStatus(errors::InvalidArgument(strings::StrCat( 157 "Duplicate tag ", tag, " found in summary inputs"))); 158 return; 159 } 160 *s.add_value() = summary_in.value(v); 161 } 162 } 163 } 164 165 Tensor* summary_tensor = nullptr; 166 OP_REQUIRES_OK(c, c->allocate_output(0, TensorShape({}), &summary_tensor)); 167 CHECK(s.SerializeToString(&summary_tensor->scalar<string>()())); 168 } 169 }; 170 171 REGISTER_KERNEL_BUILDER(Name("MergeSummary").Device(DEVICE_CPU), 172 SummaryMergeOp); 173 174 } // namespace tensorflow 175