You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

BaseSession.cs 23 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. /*****************************************************************************
  2. Copyright 2018 The TensorFlow.NET Authors. All Rights Reserved.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ******************************************************************************/
  13. using NumSharp;
  14. using System;
  15. using System.Collections;
  16. using System.Collections.Generic;
  17. using System.Linq;
  18. using System.Numerics;
  19. using System.Text;
  20. using Google.Protobuf;
  21. using static Tensorflow.Binding;
  22. using Tensorflow.Util;
  23. namespace Tensorflow
  24. {
  25. public class BaseSession : DisposableObject
  26. {
  27. protected Graph _graph;
  28. protected bool _opened;
  29. protected bool _closed;
  30. protected int _current_version;
  31. protected byte[] _target;
  32. public Graph graph => _graph;
  33. public BaseSession(string target = "", Graph g = null, ConfigProto config = null, Status status = null)
  34. {
  35. _graph = g ?? ops.get_default_graph();
  36. _graph.as_default();
  37. _target = Encoding.UTF8.GetBytes(target);
  38. using (var opts = new SessionOptions(target, config))
  39. {
  40. lock (Locks.ProcessWide)
  41. {
  42. status = status ?? new Status();
  43. _handle = c_api.TF_NewSession(_graph, opts, status.Handle);
  44. status.Check(true);
  45. }
  46. }
  47. }
  48. public virtual void run(Operation op, params FeedItem[] feed_dict)
  49. {
  50. _run(op, feed_dict);
  51. }
  52. public virtual NDArray run(Tensor fetche, params FeedItem[] feed_dict)
  53. {
  54. return _run(fetche, feed_dict)[0];
  55. }
  56. public virtual NDArray run(ITensorOrOperation fetche, params FeedItem[] feed_dict)
  57. {
  58. var results = _run(fetche, feed_dict);
  59. return fetche is Tensor ? results[0] : null;
  60. }
  61. public virtual (NDArray, NDArray, NDArray, NDArray, NDArray) run(
  62. (ITensorOrOperation, ITensorOrOperation, ITensorOrOperation, ITensorOrOperation, ITensorOrOperation) fetches,
  63. params FeedItem[] feed_dict)
  64. {
  65. var results = _run(new object[] { fetches.Item1, fetches.Item2, fetches.Item3, fetches.Item4, fetches.Item5 }, feed_dict);
  66. return (results[0], results[1], results[2], results[3], results[4]);
  67. }
  68. public virtual (NDArray, NDArray, NDArray, NDArray) run((ITensorOrOperation, ITensorOrOperation, ITensorOrOperation, ITensorOrOperation) fetches, params FeedItem[] feed_dict)
  69. {
  70. var results = _run(new object[] {fetches.Item1, fetches.Item2, fetches.Item3, fetches.Item4}, feed_dict);
  71. return (results[0], results[1], results[2], results[3]);
  72. }
  73. public virtual (NDArray, NDArray, NDArray) run((ITensorOrOperation, ITensorOrOperation, ITensorOrOperation) fetches, params FeedItem[] feed_dict)
  74. {
  75. var results = _run(new object[] {fetches.Item1, fetches.Item2, fetches.Item3}, feed_dict);
  76. return (results[0], results[1], results[2]);
  77. }
  78. public virtual (NDArray, NDArray) run((ITensorOrOperation, ITensorOrOperation) fetches, params FeedItem[] feed_dict)
  79. {
  80. var results = _run(new object[] {fetches.Item1, fetches.Item2}, feed_dict);
  81. return (results[0], results[1]);
  82. }
  83. public virtual NDArray[] run(object fetches, params FeedItem[] feed_dict)
  84. {
  85. return _run(fetches, feed_dict);
  86. }
  87. public virtual NDArray[] run(object fetches, Hashtable feed_dict = null)
  88. {
  89. var feed_items = feed_dict == null ? new FeedItem[0] : feed_dict.Keys.OfType<object>().Select(key => new FeedItem(key, feed_dict[key])).ToArray();
  90. return _run(fetches, feed_items);
  91. }
  92. private NDArray[] _run(object fetches, FeedItem[] feed_dict = null)
  93. {
  94. var feed_dict_tensor = new Dictionary<object, object>();
  95. //var feed_map = new Dictionary<object, object>();
  96. // Validate and process feed_dict.
  97. if (feed_dict != null)
  98. {
  99. foreach (var subfeed in feed_dict)
  100. {
  101. var subfeed_t = _graph.as_graph_element(subfeed.Key, allow_tensor: true, allow_operation: false);
  102. //var target_dtype = subfeed_t.dtype.as_numpy_typecode(); // subfeed_dtype was never used
  103. feed_dict_tensor[subfeed_t] = subfeed.Value;
  104. //feed_map[subfeed_t.name] = (subfeed_t, subfeed.Value);
  105. }
  106. }
  107. // Create a fetch handler to take care of the structure of fetches.
  108. var fetch_handler = new _FetchHandler(_graph, fetches, feed_dict_tensor);
  109. // Run request and get response.
  110. // We need to keep the returned movers alive for the following _do_run().
  111. // These movers are no longer needed when _do_run() completes, and
  112. // are deleted when `movers` goes out of scope when this _run() ends.
  113. var _ = _update_with_movers();
  114. var final_fetches = fetch_handler.fetches();
  115. var final_targets = fetch_handler.targets();
  116. // We only want to really perform the run if fetches or targets are provided,
  117. // or if the call is a partial run that specifies feeds.
  118. var results = _do_run(final_targets.Select(x => (Operation) x).ToList(), final_fetches, feed_dict_tensor);
  119. return fetch_handler.build_results(this, results);
  120. }
  121. /// <summary>
  122. /// Runs a step based on the given fetches and feeds.
  123. /// </summary>
  124. /// <typeparam name="T"></typeparam>
  125. /// <param name="target_list">A list of operations to be run, but not fetched.</param>
  126. /// <param name="fetch_list"></param>
  127. /// <param name="feed_dict"></param>
  128. /// <returns>
  129. /// A list of numpy ndarrays, corresponding to the elements of
  130. /// `fetch_list`. If the ith element of `fetch_list` contains the
  131. /// name of an operation, the first Tensor output of that operation
  132. /// will be returned for that element.
  133. /// </returns>
  134. private NDArray[] _do_run(List<Operation> target_list, List<Tensor> fetch_list, Dictionary<object, object> feed_dict)
  135. {
  136. var feeds = new KeyValuePair<TF_Output, Tensor>[feed_dict.Count];
  137. int i = 0;
  138. foreach (var x in feed_dict)
  139. {
  140. if (x.Key is Tensor key)
  141. {
  142. switch (x.Value)
  143. {
  144. case Tensor v:
  145. if (v.dtype != key.dtype)
  146. throw new ValueError($"Tensor {v} does not match the expected dtype {key.dtype}, actual dtype: {v.dtype}");
  147. feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), v);
  148. break;
  149. case NDArray v:
  150. feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype));
  151. break;
  152. case IntPtr v:
  153. var tensor = new Tensor(v);
  154. if (tensor.dtype != key.dtype)
  155. throw new ValueError($"Tensor {v} does not match the expected dtype {key.dtype}, actual dtype: {tensor.dtype}");
  156. feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), tensor);
  157. break;
  158. #if _REGEN
  159. // @formatter:off — disable formatter after this line
  160. %types = ["bool", "sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "Complex"]
  161. %foreach types%
  162. case #1 v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  163. case #1[] v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  164. %
  165. // @formatter:on — enable formatter after this line
  166. #else
  167. // @formatter:off — disable formatter after this line
  168. case bool v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  169. case bool[] v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  170. case sbyte v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  171. case sbyte[] v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  172. case byte v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  173. case byte[] v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  174. case short v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  175. case short[] v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  176. case ushort v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  177. case ushort[] v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  178. case int v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  179. case int[] v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  180. case uint v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  181. case uint[] v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  182. case long v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  183. case long[] v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  184. case ulong v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  185. case ulong[] v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  186. case float v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  187. case float[] v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  188. case double v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  189. case double[] v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  190. case Complex v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  191. case Complex[] v: feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype)); break;
  192. // @formatter:on — enable formatter after this line
  193. #endif
  194. case string v:
  195. feeds[i++] = new KeyValuePair<TF_Output, Tensor>(key._as_tf_output(), TensorConverter.ToTensor(v, key.dtype));
  196. break;
  197. default:
  198. throw new NotImplementedException($"feed_dict data type {x.Value?.GetType().Name ?? "<null>"}");
  199. }
  200. }
  201. }
  202. var fetches = fetch_list.Select(x => x._as_tf_output()).ToArray();
  203. //var targets = target_list;
  204. return _call_tf_sessionrun(feeds, fetches, target_list);
  205. }
  206. private unsafe NDArray[] _call_tf_sessionrun(KeyValuePair<TF_Output, Tensor>[] feed_dict, TF_Output[] fetch_list, List<Operation> target_list)
  207. {
  208. // Ensure any changes to the graph are reflected in the runtime.
  209. _extend_graph();
  210. var status = tf.status;
  211. var output_values = fetch_list.Select(x => IntPtr.Zero).ToArray();
  212. c_api.TF_SessionRun(_handle,
  213. run_options: null,
  214. inputs: feed_dict.Select(f => f.Key).ToArray(),
  215. input_values: feed_dict.Select(f => (IntPtr) f.Value).ToArray(),
  216. ninputs: feed_dict.Length,
  217. outputs: fetch_list,
  218. output_values: output_values,
  219. noutputs: fetch_list.Length,
  220. target_opers: target_list.Select(f => (IntPtr) f).ToArray(),
  221. ntargets: target_list.Count,
  222. run_metadata: IntPtr.Zero,
  223. status: status.Handle);
  224. status.Check(true);
  225. var result = new NDArray[fetch_list.Length];
  226. for (int i = 0; i < fetch_list.Length; i++)
  227. result[i] = fetchValue(output_values[i]);
  228. return result;
  229. }
  230. private static unsafe NDArray fetchValue(IntPtr output)
  231. {
  232. NDArray ret;
  233. using (var tensor = new Tensor(output))
  234. {
  235. var ndims = tensor.shape;
  236. var srcAddress = c_api.TF_TensorData(output).ToInt64();
  237. if (ndims.Length == 0)
  238. {
  239. switch (tensor.dtype)
  240. {
  241. case TF_DataType.TF_BOOL:
  242. ret = NDArray.Scalar(*(bool*) srcAddress);
  243. break;
  244. case TF_DataType.TF_STRING:
  245. using (var reader = new CodedInputStream(new IntPtr(srcAddress).Stream(8, (long) tensor.bytesize)))
  246. ret = new NDArray(reader.ReadBytes().ToByteArray());
  247. break;
  248. case TF_DataType.TF_UINT8:
  249. ret = NDArray.Scalar(*(byte*) srcAddress);
  250. break;
  251. case TF_DataType.TF_INT16:
  252. ret = NDArray.Scalar(*(short*) srcAddress);
  253. break;
  254. case TF_DataType.TF_INT32:
  255. ret = NDArray.Scalar(*(int*) srcAddress);
  256. break;
  257. case TF_DataType.TF_INT64:
  258. ret = NDArray.Scalar(*(long*) srcAddress);
  259. break;
  260. case TF_DataType.TF_UINT16:
  261. ret = NDArray.Scalar(*(ushort*) srcAddress);
  262. break;
  263. case TF_DataType.TF_UINT32:
  264. ret = NDArray.Scalar(*(uint*) srcAddress);
  265. break;
  266. case TF_DataType.TF_UINT64:
  267. ret = NDArray.Scalar(*(ulong*) srcAddress);
  268. break;
  269. case TF_DataType.TF_FLOAT:
  270. ret = NDArray.Scalar(*(float*) srcAddress);
  271. break;
  272. case TF_DataType.TF_DOUBLE:
  273. ret = NDArray.Scalar(*(double*) srcAddress);
  274. break;
  275. default:
  276. throw new NotImplementedException("can't fetch output");
  277. }
  278. } else
  279. {
  280. //var size = (long) tensor.size;
  281. //var itemsize = (long) tensor.itemsize;
  282. var bytesize = (long) tensor.bytesize;
  283. var src = (void*) srcAddress;
  284. #if _REGEN
  285. #region Compute
  286. switch (tensor.dtype)
  287. {
  288. %foreach except(supported_dtypes, "Char"),except(supported_dtypes_lowercase, "char"),except(supported_dtypes_TF_DataType,"TF_STRING")%
  289. case TF_DataType.#3:
  290. {
  291. ret = new NDArray(NPTypeCode.#1, ndims, false);
  292. System.Buffer.MemoryCopy(src, #(#3=="TF_STRING"|"(byte*)ret.Unsafe.Address + 8"|"ret.Unsafe.Address"), bytesize, bytesize);
  293. break;
  294. }
  295. %
  296. case TF_DataType.TF_STRING:
  297. {
  298. //TODO:! This is not the way to handle string[], it should be done with TF_DecodeString
  299. using (var reader = new CodedInputStream(new IntPtr(srcAddress).Stream(8, (long)tensor.bytesize)))
  300. ret = NDArray.FromString(reader.ReadString());
  301. break;
  302. }
  303. default:
  304. throw new NotSupportedException();
  305. }
  306. #endregion
  307. #else
  308. #region Compute
  309. switch (tensor.dtype)
  310. {
  311. case TF_DataType.TF_BOOL:
  312. {
  313. ret = new NDArray(NPTypeCode.Boolean, ndims, false);
  314. System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize);
  315. break;
  316. }
  317. case TF_DataType.TF_UINT8:
  318. {
  319. ret = new NDArray(NPTypeCode.Byte, ndims, false);
  320. System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize);
  321. break;
  322. }
  323. case TF_DataType.TF_INT16:
  324. {
  325. ret = new NDArray(NPTypeCode.Int16, ndims, false);
  326. System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize);
  327. break;
  328. }
  329. case TF_DataType.TF_UINT16:
  330. {
  331. ret = new NDArray(NPTypeCode.UInt16, ndims, false);
  332. System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize);
  333. break;
  334. }
  335. case TF_DataType.TF_INT32:
  336. {
  337. ret = new NDArray(NPTypeCode.Int32, ndims, false);
  338. System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize);
  339. break;
  340. }
  341. case TF_DataType.TF_UINT32:
  342. {
  343. ret = new NDArray(NPTypeCode.UInt32, ndims, false);
  344. System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize);
  345. break;
  346. }
  347. case TF_DataType.TF_INT64:
  348. {
  349. ret = new NDArray(NPTypeCode.Int64, ndims, false);
  350. System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize);
  351. break;
  352. }
  353. case TF_DataType.TF_UINT64:
  354. {
  355. ret = new NDArray(NPTypeCode.UInt64, ndims, false);
  356. System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize);
  357. break;
  358. }
  359. case TF_DataType.TF_DOUBLE:
  360. {
  361. ret = new NDArray(NPTypeCode.Double, ndims, false);
  362. System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize);
  363. break;
  364. }
  365. case TF_DataType.TF_FLOAT:
  366. {
  367. ret = new NDArray(NPTypeCode.Single, ndims, false);
  368. System.Buffer.MemoryCopy(src, ret.Unsafe.Address, bytesize, bytesize);
  369. break;
  370. }
  371. case TF_DataType.TF_STRING:
  372. {
  373. throw new NotImplementedException();
  374. //TODO:! This is not the way to handle string[], it should be done with TF_DecodeString
  375. using (var reader = new CodedInputStream(new IntPtr(srcAddress).Stream(8, (long) tensor.bytesize)))
  376. ret = NDArray.FromString(reader.ReadString());
  377. break;
  378. }
  379. default:
  380. throw new NotSupportedException();
  381. }
  382. #endregion
  383. #endif
  384. }
  385. }
  386. return ret;
  387. }
  388. /// <summary>
  389. /// If a tensor handle that is fed to a device incompatible placeholder,
  390. /// we move the tensor to the right device, generate a new tensor handle,
  391. /// and update feed_dict to use the new handle.
  392. /// </summary>
  393. private List<object> _update_with_movers()
  394. {
  395. return new List<object> { };
  396. }
  397. private void _extend_graph()
  398. { }
  399. public void close()
  400. {
  401. Dispose();
  402. }
  403. protected override void DisposeUnmanagedResources(IntPtr handle)
  404. {
  405. lock (Locks.ProcessWide)
  406. using (var status = new Status())
  407. {
  408. c_api.TF_DeleteSession(handle, status.Handle);
  409. status.Check(true);
  410. }
  411. }
  412. }
  413. }