Skip to content

Commit 9e895b3

Browse files
committed
add tf.summary SciSharp#248
1 parent bd04f5b commit 9e895b3

6 files changed

Lines changed: 199 additions & 2 deletions

File tree

graph/InceptionV3.meta

-365 KB
Binary file not shown.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using Tensorflow.IO;
5+
using Tensorflow.Summaries;
6+
7+
namespace Tensorflow
8+
{
9+
public static partial class tf
10+
{
11+
public static Summary summary = new Summary();
12+
public static Tensor scalar(string name, Tensor tensor)
13+
=> summary.scalar(name, tensor);
14+
}
15+
}

src/TensorFlowNET.Core/Operations/gen_logging_ops.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,65 @@ public static Operation _assert(Tensor condition, object[] data, int? summarize
1717

1818
return _op;
1919
}
20+
21+
/// <summary>
22+
/// Outputs a <c>Summary</c> protocol buffer with scalar values.
23+
/// </summary>
24+
/// <param name="tags">
25+
/// Tags for the summary.
26+
/// </param>
27+
/// <param name="values">
28+
/// Same shape as <c>tags. Values for the summary.
29+
/// </param>
30+
/// <param name="name">
31+
/// If specified, the created operation in the graph will be this one, otherwise it will be named 'ScalarSummary'.
32+
/// </param>
33+
/// <returns>
34+
/// Scalar. Serialized <c>Summary</c> protocol buffer.
35+
/// The Operation can be fetched from the resulting Tensor, by fetching the Operation property from the result.
36+
/// </returns>
37+
/// <remarks>
38+
/// The input <c>tags</c> and <c>values</c> must have the same shape. The generated summary
39+
/// has a summary value for each tag-value pair in <c>tags</c> and <c>values</c>.
40+
/// </remarks>
41+
public static Tensor scalar_summary(string tags, Tensor values, string name = "ScalarSummary")
42+
{
43+
var dict = new Dictionary<string, object>();
44+
dict["tags"] = tags;
45+
dict["values"] = values;
46+
var op = _op_def_lib._apply_op_helper("ScalarSummary", name: name, keywords: dict);
47+
return op.output;
48+
}
49+
50+
/// <summary>
51+
/// Merges summaries.
52+
/// </summary>
53+
/// <param name="inputs">
54+
/// Can be of any shape. Each must contain serialized <c>Summary</c> protocol
55+
/// buffers.
56+
/// </param>
57+
/// <param name="name">
58+
/// If specified, the created operation in the graph will be this one, otherwise it will be named 'MergeSummary'.
59+
/// </param>
60+
/// <returns>
61+
/// Scalar. Serialized <c>Summary</c> protocol buffer.
62+
/// The Operation can be fetched from the resulting Tensor, by fetching the Operation property from the result.
63+
/// </returns>
64+
/// <remarks>
65+
/// This op creates a
66+
/// [<c>Summary</c>](https://www.tensorflow.org/code/tensorflow/core/framework/summary.proto)
67+
/// protocol buffer that contains the union of all the values in the input
68+
/// summaries.
69+
///
70+
/// When the Op is run, it reports an <c>InvalidArgument</c> error if multiple values
71+
/// in the summaries to merge use the same tag.
72+
/// </remarks>
73+
public static Tensor merge_summary(Tensor[] inputs, string name = "MergeSummary")
74+
{
75+
var dict = new Dictionary<string, object>();
76+
dict["inputs"] = inputs;
77+
var op = _op_def_lib._apply_op_helper("MergeSummary", name: name, keywords: dict);
78+
return op.output;
79+
}
2080
}
2181
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using static Tensorflow.Python;
6+
7+
namespace Tensorflow.Summaries
8+
{
9+
public class Summary
10+
{
11+
public Tensor merge_all(string key = ops.GraphKeys.SUMMARIES, string scope= null, string name= null)
12+
{
13+
var summary_ops = ops.get_collection(key, scope: scope);
14+
if (summary_ops == null)
15+
return null;
16+
else
17+
return merge((summary_ops as List<ITensorOrOperation>).Select(x => x as Tensor).ToArray(), name: name);
18+
}
19+
20+
/// <summary>
21+
/// Merges summaries.
22+
/// </summary>
23+
/// <param name="inputs"></param>
24+
/// <param name="collections"></param>
25+
/// <param name="name"></param>
26+
/// <returns></returns>
27+
public Tensor merge(Tensor[] inputs, string[] collections = null, string name = null)
28+
{
29+
return with(ops.name_scope(name, "Merge", inputs), delegate
30+
{
31+
var val = gen_logging_ops.merge_summary(inputs: inputs, name: name);
32+
collect(val, collections?.ToList(), new List<string>());
33+
return val;
34+
});
35+
}
36+
37+
public Tensor scalar(string name, Tensor tensor, string[] collections = null, string family = null)
38+
{
39+
var (tag, scope) = summary_scope(name, family: family, values: new Tensor[] { tensor });
40+
var val = gen_logging_ops.scalar_summary(tags: tag, values: tensor, name: scope);
41+
collect(val, collections?.ToList(), new List<string> { ops.GraphKeys.SUMMARIES });
42+
return val;
43+
}
44+
45+
/// <summary>
46+
/// Adds keys to a collection.
47+
/// </summary>
48+
/// <param name="val"The value to add per each key.></param>
49+
/// <param name="collections">A collection of keys to add.</param>
50+
/// <param name="default_collections">Used if collections is None.</param>
51+
public void collect(ITensorOrOperation val, List<string> collections, List<string> default_collections)
52+
{
53+
if (collections == null)
54+
collections = default_collections;
55+
foreach (var key in collections)
56+
ops.add_to_collection(key, val);
57+
}
58+
59+
public (string, string) summary_scope(string name, string family = null, string default_name = null, Tensor[] values = null)
60+
{
61+
string scope_base_name = string.IsNullOrEmpty(family) ? name : $"{family}/{name}";
62+
return with(ops.name_scope(scope_base_name, default_name: default_name, values), scope =>
63+
{
64+
var tag = scope._name_scope;
65+
if (string.IsNullOrEmpty(family))
66+
tag = tag.Remove(tag.Length - 1);
67+
else
68+
tag = $"{family}/{tag.Remove(tag.Length - 1)}";
69+
70+
return (tag, scope._name_scope);
71+
});
72+
}
73+
}
74+
}

src/TensorFlowNET.Core/ops.GraphKeys.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ public static class GraphKeys
4747
/// </summary>
4848
public static string UPDATE_OPS = "update_ops";
4949

50+
// Key to collect summaries.
51+
public const string SUMMARIES = "summaries";
52+
5053
// Used to store v2 summary names.
5154
public static string _SUMMARY_COLLECTION = "_SUMMARY_V2";
5255

test/TensorFlowNET.Examples/ImageProcess/RetrainImageClassifier.cs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ public bool Run()
4545
tf.train.import_meta_graph("graph/InceptionV3.meta");
4646
Tensor bottleneck_tensor = graph.OperationByName("module_apply_default/hub_output/feature_vector/SpatialSqueeze");
4747
Tensor resized_image_tensor = graph.OperationByName("Placeholder");
48+
Tensor final_tensor = graph.OperationByName("final_result");
49+
Tensor ground_truth_input = graph.OperationByName("input/GroundTruthInput");
4850

4951
var sw = new Stopwatch();
5052

@@ -63,11 +65,45 @@ public bool Run()
6365
bottleneck_dir, jpeg_data_tensor,
6466
decoded_image_tensor, resized_image_tensor,
6567
bottleneck_tensor, tfhub_module);
68+
69+
// Create the operations we need to evaluate the accuracy of our new layer.
70+
var (evaluation_step, _) = add_evaluation_step(final_tensor, ground_truth_input);
71+
72+
// Merge all the summaries and write them out to the summaries_dir
73+
var merged = tf.summary.merge_all();
6674
});
6775

6876
return false;
6977
}
7078

79+
/// <summary>
80+
/// Inserts the operations we need to evaluate the accuracy of our results.
81+
/// </summary>
82+
/// <param name="result_tensor"></param>
83+
/// <param name="ground_truth_tensor"></param>
84+
/// <returns></returns>
85+
private (Tensor, Tensor) add_evaluation_step(Tensor result_tensor, Tensor ground_truth_tensor)
86+
{
87+
Tensor evaluation_step = null, correct_prediction = null, prediction = null;
88+
89+
with(tf.name_scope("accuracy"), scope =>
90+
{
91+
with(tf.name_scope("correct_prediction"), delegate
92+
{
93+
prediction = tf.argmax(result_tensor, 1);
94+
correct_prediction = tf.equal(prediction, ground_truth_tensor);
95+
});
96+
97+
with(tf.name_scope("accuracy"), delegate
98+
{
99+
evaluation_step = tf.reduce_mean(tf.cast(correct_prediction, tf.float32));
100+
});
101+
});
102+
103+
tf.summary.scalar("accuracy", evaluation_step);
104+
return (evaluation_step, prediction);
105+
}
106+
71107
/// <summary>
72108
/// Ensures all the training, testing, and validation bottlenecks are cached.
73109
/// </summary>
@@ -95,12 +131,15 @@ private void cache_bottlenecks(Session sess, Dictionary<string, Dictionary<strin
95131
get_or_create_bottleneck(sess, image_lists, label_name, index, image_dir, category,
96132
bottleneck_dir, jpeg_data_tensor, decoded_image_tensor,
97133
resized_input_tensor, bottleneck_tensor, module_name);
134+
how_many_bottlenecks++;
135+
if (how_many_bottlenecks % 100 == 0)
136+
print($"{how_many_bottlenecks} bottleneck files created.");
98137
}
99138
}
100139
}
101140
}
102141

103-
private void get_or_create_bottleneck(Session sess, Dictionary<string, Dictionary<string, string[]>> image_lists,
142+
private float[] get_or_create_bottleneck(Session sess, Dictionary<string, Dictionary<string, string[]>> image_lists,
104143
string label_name, int index, string image_dir, string category, string bottleneck_dir,
105144
Tensor jpeg_data_tensor, Tensor decoded_image_tensor, Tensor resized_input_tensor,
106145
Tensor bottleneck_tensor, string module_name)
@@ -116,6 +155,9 @@ private void get_or_create_bottleneck(Session sess, Dictionary<string, Dictionar
116155
image_dir, category, sess, jpeg_data_tensor,
117156
decoded_image_tensor, resized_input_tensor,
118157
bottleneck_tensor);
158+
var bottleneck_string = File.ReadAllText(bottleneck_path);
159+
var bottleneck_values = Array.ConvertAll(bottleneck_string.Split(','), x => float.Parse(x));
160+
return bottleneck_values;
119161
}
120162

121163
private void create_bottleneck_file(string bottleneck_path, Dictionary<string, Dictionary<string, string[]>> image_lists,
@@ -132,13 +174,16 @@ private void create_bottleneck_file(string bottleneck_path, Dictionary<string, D
132174
var bottleneck_values = run_bottleneck_on_image(
133175
sess, image_data, jpeg_data_tensor, decoded_image_tensor,
134176
resized_input_tensor, bottleneck_tensor);
177+
var values = bottleneck_values.Data<float>();
178+
var bottleneck_string = string.Join(",", values);
179+
File.WriteAllText(bottleneck_path, bottleneck_string);
135180
}
136181

137182
/// <summary>
138183
/// Runs inference on an image to extract the 'bottleneck' summary layer.
139184
/// </summary>
140185
/// <param name="sess">Current active TensorFlow Session.</param>
141-
/// <param name="image_path">Path of raw JPEG data.</param>
186+
/// <param name="image_data">Data of raw JPEG data.</param>
142187
/// <param name="image_data_tensor">Input data layer in the graph.</param>
143188
/// <param name="decoded_image_tensor">Output of initial image resizing and preprocessing.</param>
144189
/// <param name="resized_input_tensor">The input node of the recognition graph.</param>

0 commit comments

Comments
 (0)