Skip to content

fix: Support for training a multi-input model using a dataset. #1260

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/TensorFlowNET.Keras/Engine/Model.Evaluate.cs
Original file line number Diff line number Diff line change
@@ -112,7 +112,19 @@ public Dictionary<string, float> evaluate(IDatasetV2 x, int verbose = 1, bool is
Steps = data_handler.Inferredsteps
});

return evaluate(data_handler, callbacks, is_val, test_function);
Func<DataHandler, OwnedIterator, Dictionary<string, float>> testFunction;

if (data_handler.DataAdapter.GetDataset().structure.Length > 2 ||
data_handler.DataAdapter.GetDataset().FirstInputTensorCount > 1)
{
testFunction = test_step_multi_inputs_function;
}
else
{
testFunction = test_function;
}

return evaluate(data_handler, callbacks, is_val, testFunction);
}

/// <summary>
13 changes: 12 additions & 1 deletion src/TensorFlowNET.Keras/Engine/Model.Fit.cs
Original file line number Diff line number Diff line change
@@ -179,9 +179,20 @@ public ICallback fit(IDatasetV2 dataset,
StepsPerExecution = _steps_per_execution
});

Func<DataHandler, OwnedIterator, Dictionary<string, float>> trainStepFunction;

if (data_handler.DataAdapter.GetDataset().structure.Length > 2 ||
data_handler.DataAdapter.GetDataset().FirstInputTensorCount > 1)
{
trainStepFunction = train_step_multi_inputs_function;
}
else
{
trainStepFunction = train_step_function;
}

return FitInternal(data_handler, epochs, validation_step, verbose, callbacks, validation_data: validation_data,
train_step_func: train_step_function);
train_step_func: trainStepFunction);
}

History FitInternal(DataHandler data_handler, int epochs, int validation_step, int verbose, List<ICallback> callbackList, IDatasetV2 validation_data,
82 changes: 82 additions & 0 deletions test/TensorFlowNET.Keras.UnitTest/MultiInputModelTest.cs
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
using System;
using Tensorflow.Keras.Optimizers;
using Tensorflow.NumPy;
using static Tensorflow.Binding;
using static Tensorflow.KerasApi;

namespace Tensorflow.Keras.UnitTest
@@ -54,10 +55,91 @@ public void LeNetModel()
var x = new NDArray[] { x1, x2 };
model.fit(x, dataset.Train.Labels, batch_size: 8, epochs: 3);

x1 = x1["0:8"];
x2 = x1;

x = new NDArray[] { x1, x2 };
var y = dataset.Train.Labels["0:8"];
(model as Engine.Model).evaluate(x, y);

x1 = np.ones((1, 28, 28, 1), TF_DataType.TF_FLOAT);
x2 = np.zeros((1, 28, 28, 1), TF_DataType.TF_FLOAT);
var pred = model.predict((x1, x2));
Console.WriteLine(pred);
}

[TestMethod]
public void LeNetModelDataset()
{
var inputs = keras.Input((28, 28, 1));
var conv1 = keras.layers.Conv2D(16, (3, 3), activation: "relu", padding: "same").Apply(inputs);
var pool1 = keras.layers.MaxPooling2D((2, 2), 2).Apply(conv1);
var conv2 = keras.layers.Conv2D(32, (3, 3), activation: "relu", padding: "same").Apply(pool1);
var pool2 = keras.layers.MaxPooling2D((2, 2), 2).Apply(conv2);
var flat1 = keras.layers.Flatten().Apply(pool2);

var inputs_2 = keras.Input((28, 28, 1));
var conv1_2 = keras.layers.Conv2D(16, (3, 3), activation: "relu", padding: "same").Apply(inputs_2);
var pool1_2 = keras.layers.MaxPooling2D((4, 4), 4).Apply(conv1_2);
var conv2_2 = keras.layers.Conv2D(32, (1, 1), activation: "relu", padding: "same").Apply(pool1_2);
var pool2_2 = keras.layers.MaxPooling2D((2, 2), 2).Apply(conv2_2);
var flat1_2 = keras.layers.Flatten().Apply(pool2_2);

var concat = keras.layers.Concatenate().Apply((flat1, flat1_2));
var dense1 = keras.layers.Dense(512, activation: "relu").Apply(concat);
var dense2 = keras.layers.Dense(128, activation: "relu").Apply(dense1);
var dense3 = keras.layers.Dense(10, activation: "relu").Apply(dense2);
var output = keras.layers.Softmax(-1).Apply(dense3);

var model = keras.Model((inputs, inputs_2), output);
model.summary();

var data_loader = new MnistModelLoader();

var dataset = data_loader.LoadAsync(new ModelLoadSetting
{
TrainDir = "mnist",
OneHot = false,
ValidationSize = 59900,
}).Result;

var loss = keras.losses.SparseCategoricalCrossentropy();
var optimizer = new Adam(0.001f);
model.compile(optimizer, loss, new string[] { "accuracy" });

NDArray x1 = np.reshape(dataset.Train.Data, (dataset.Train.Data.shape[0], 28, 28, 1));

var multiInputDataset = tf.data.Dataset.zip(
tf.data.Dataset.from_tensor_slices(x1),
tf.data.Dataset.from_tensor_slices(x1),
tf.data.Dataset.from_tensor_slices(dataset.Train.Labels)
).batch(8);
multiInputDataset.FirstInputTensorCount = 2;

model.fit(multiInputDataset, epochs: 3);

x1 = x1["0:8"];

multiInputDataset = tf.data.Dataset.zip(
tf.data.Dataset.from_tensor_slices(x1),
tf.data.Dataset.from_tensor_slices(x1),
tf.data.Dataset.from_tensor_slices(dataset.Train.Labels["0:8"])
).batch(8);
multiInputDataset.FirstInputTensorCount = 2;

(model as Engine.Model).evaluate(multiInputDataset);

x1 = np.ones((1, 28, 28, 1), TF_DataType.TF_FLOAT);
var x2 = np.zeros((1, 28, 28, 1), TF_DataType.TF_FLOAT);

multiInputDataset = tf.data.Dataset.zip(
tf.data.Dataset.from_tensor_slices(x1),
tf.data.Dataset.from_tensor_slices(x2)
).batch(8);
multiInputDataset.FirstInputTensorCount = 2;

var pred = model.predict(multiInputDataset);
Console.WriteLine(pred);
}
}
}

Unchanged files with check annotations Beta

}
// Resume the file from fregments. Thanks for the code in TorchSharp!
static void Restitch(string RestitcherPackage)

Check warning on line 50 in tools/Tensorflow.Redist.NativeLibrarySplitter/Program.cs

GitHub Actions / linux

The local function 'Restitch' is declared but never used

Check warning on line 50 in tools/Tensorflow.Redist.NativeLibrarySplitter/Program.cs

GitHub Actions / windows

The local function 'Restitch' is declared but never used

Check warning on line 50 in tools/Tensorflow.Redist.NativeLibrarySplitter/Program.cs

GitHub Actions / linux

The local function 'Restitch' is declared but never used

Check warning on line 50 in tools/Tensorflow.Redist.NativeLibrarySplitter/Program.cs

GitHub Actions / windows

The local function 'Restitch' is declared but never used
{
// !!!!!!!------------------------------NOTE------------------------------------!!!!!!
// !!!!!!! This code is manually copied into pkg\common\RestitchPackage.targets !!!!!!
public Tensor sparse_to_dense<T>(Tensor sparse_indices,
Shape output_shape,
T sparse_values,
T default_value = default,

Check warning on line 52 in src/TensorFlowNET.Core/APIs/tf.sparse.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 52 in src/TensorFlowNET.Core/APIs/tf.sparse.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 52 in src/TensorFlowNET.Core/APIs/tf.sparse.cs

GitHub Actions / windows

Possible null reference assignment.

Check warning on line 52 in src/TensorFlowNET.Core/APIs/tf.sparse.cs

GitHub Actions / windows

Possible null reference assignment.

Check warning on line 52 in src/TensorFlowNET.Core/APIs/tf.sparse.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 52 in src/TensorFlowNET.Core/APIs/tf.sparse.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 52 in src/TensorFlowNET.Core/APIs/tf.sparse.cs

GitHub Actions / windows

Possible null reference assignment.

Check warning on line 52 in src/TensorFlowNET.Core/APIs/tf.sparse.cs

GitHub Actions / windows

Possible null reference assignment.
bool validate_indices = true,
string name = null)
=> gen_sparse_ops.sparse_to_dense(sparse_indices,
public static Tensor sparse_to_dense<T>(Tensor sparse_indices,
Tensor output_shape,
Tensor sparse_values,
T default_value = default,

Check warning on line 55 in src/TensorFlowNET.Core/Operations/gen_sparse_ops.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 55 in src/TensorFlowNET.Core/Operations/gen_sparse_ops.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 55 in src/TensorFlowNET.Core/Operations/gen_sparse_ops.cs

GitHub Actions / windows

Possible null reference assignment.

Check warning on line 55 in src/TensorFlowNET.Core/Operations/gen_sparse_ops.cs

GitHub Actions / windows

Possible null reference assignment.

Check warning on line 55 in src/TensorFlowNET.Core/Operations/gen_sparse_ops.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 55 in src/TensorFlowNET.Core/Operations/gen_sparse_ops.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 55 in src/TensorFlowNET.Core/Operations/gen_sparse_ops.cs

GitHub Actions / windows

Possible null reference assignment.

Check warning on line 55 in src/TensorFlowNET.Core/Operations/gen_sparse_ops.cs

GitHub Actions / windows

Possible null reference assignment.
bool validate_indices = true,
string name = null)
{
});
}
public static Tensor cumsum<T>(Tensor x, T axis = default, bool exclusive = false, bool reverse = false, string name = null)

Check warning on line 163 in src/TensorFlowNET.Core/Operations/math_ops.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 163 in src/TensorFlowNET.Core/Operations/math_ops.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 163 in src/TensorFlowNET.Core/Operations/math_ops.cs

GitHub Actions / windows

Possible null reference assignment.

Check warning on line 163 in src/TensorFlowNET.Core/Operations/math_ops.cs

GitHub Actions / windows

Possible null reference assignment.

Check warning on line 163 in src/TensorFlowNET.Core/Operations/math_ops.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 163 in src/TensorFlowNET.Core/Operations/math_ops.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 163 in src/TensorFlowNET.Core/Operations/math_ops.cs

GitHub Actions / windows

Possible null reference assignment.
=> tf_with(ops.name_scope(name, "Cumsum", new { x }), scope =>
{
name = scope;
public Tensor sparse_to_dense<T>(Tensor sparse_indices,
int[] output_shape,
T sparse_values,
T default_value = default,

Check warning on line 19 in src/TensorFlowNET.Core/Operations/sparse_ops.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 19 in src/TensorFlowNET.Core/Operations/sparse_ops.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 19 in src/TensorFlowNET.Core/Operations/sparse_ops.cs

GitHub Actions / windows

Possible null reference assignment.

Check warning on line 19 in src/TensorFlowNET.Core/Operations/sparse_ops.cs

GitHub Actions / windows

Possible null reference assignment.

Check warning on line 19 in src/TensorFlowNET.Core/Operations/sparse_ops.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 19 in src/TensorFlowNET.Core/Operations/sparse_ops.cs

GitHub Actions / linux

Possible null reference assignment.

Check warning on line 19 in src/TensorFlowNET.Core/Operations/sparse_ops.cs

GitHub Actions / windows

Possible null reference assignment.
bool validate_indices = true,
string name = null)
=> gen_sparse_ops.sparse_to_dense(sparse_indices,
return value();
}
public override (IDictionary<Trackable, Trackable>, IDictionary<Tensor, Tensor>) map_resources(SaveOptions save_options)

Check warning on line 304 in src/TensorFlowNET.Core/Variables/BaseResourceVariable.cs

GitHub Actions / linux

Nullability of type of parameter 'save_options' doesn't match overridden member (possibly because of nullability attributes).

Check warning on line 304 in src/TensorFlowNET.Core/Variables/BaseResourceVariable.cs

GitHub Actions / windows

Nullability of type of parameter 'save_options' doesn't match overridden member (possibly because of nullability attributes).

Check warning on line 304 in src/TensorFlowNET.Core/Variables/BaseResourceVariable.cs

GitHub Actions / windows

Nullability of type of parameter 'save_options' doesn't match overridden member (possibly because of nullability attributes).

Check warning on line 304 in src/TensorFlowNET.Core/Variables/BaseResourceVariable.cs

GitHub Actions / linux

Nullability of type of parameter 'save_options' doesn't match overridden member (possibly because of nullability attributes).

Check warning on line 304 in src/TensorFlowNET.Core/Variables/BaseResourceVariable.cs

GitHub Actions / linux

Nullability of type of parameter 'save_options' doesn't match overridden member (possibly because of nullability attributes).

Check warning on line 304 in src/TensorFlowNET.Core/Variables/BaseResourceVariable.cs

GitHub Actions / windows

Nullability of type of parameter 'save_options' doesn't match overridden member (possibly because of nullability attributes).
{
BaseResourceVariable new_variable;
if (save_options.experimental_variable_policy.save_variable_devices())
}
else
{
IInitializer init1 = null;

Check warning on line 103 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / linux

Converting null literal or possible null value to non-nullable type.

Check warning on line 103 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / windows

Converting null literal or possible null value to non-nullable type.

Check warning on line 103 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / windows

Converting null literal or possible null value to non-nullable type.

Check warning on line 103 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / linux

Converting null literal or possible null value to non-nullable type.

Check warning on line 103 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / linux

Converting null literal or possible null value to non-nullable type.

Check warning on line 103 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / windows

Converting null literal or possible null value to non-nullable type.
return _get_single_variable(name: name,
shape: shape,
dtype: dtype,
throw new NotImplementedException("_get_single_variable");
}
IVariableV1 v = null;

Check warning on line 142 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / linux

Converting null literal or possible null value to non-nullable type.

Check warning on line 142 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / windows

Converting null literal or possible null value to non-nullable type.

Check warning on line 142 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / windows

Converting null literal or possible null value to non-nullable type.

Check warning on line 142 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / linux

Converting null literal or possible null value to non-nullable type.

Check warning on line 142 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / linux

Converting null literal or possible null value to non-nullable type.

Check warning on line 142 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / windows

Converting null literal or possible null value to non-nullable type.
// Create the tensor to initialize the variable with default value.
if (initializer == null && init_value == null)
{
v = new ResourceVariable(init_value,
name: name,
validate_shape: validate_shape,
trainable: trainable.Value);

Check warning on line 161 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / linux

Nullable value type may be null.

Check warning on line 161 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / windows

Nullable value type may be null.

Check warning on line 161 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / windows

Nullable value type may be null.

Check warning on line 161 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / linux

Nullable value type may be null.

Check warning on line 161 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / linux

Nullable value type may be null.

Check warning on line 161 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / windows

Nullable value type may be null.
}
else
{
Func<Tensor> init_val = () => initializer.Apply(new InitializerArgs(shape, dtype: dtype));

Check warning on line 165 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / linux

Dereference of a possibly null reference.

Check warning on line 165 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / windows

Dereference of a possibly null reference.

Check warning on line 165 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / windows

Dereference of a possibly null reference.

Check warning on line 165 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / linux

Dereference of a possibly null reference.

Check warning on line 165 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / linux

Dereference of a possibly null reference.

Check warning on line 165 in src/TensorFlowNET.Core/Variables/_VariableStore.cs

GitHub Actions / windows

Dereference of a possibly null reference.
var variable_dtype = dtype.as_base_dtype();
v = variable_scope.default_variable_creator(init_val,
/// </summary>
public static partial class Binding
{
public static T2 get<T1, T2>(this Dictionary<T1, T2> dict, T1 key)

Check warning on line 34 in src/TensorFlowNET.Core/Binding.Util.cs

GitHub Actions / linux

The type 'T1' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'T1' doesn't match 'notnull' constraint.

Check warning on line 34 in src/TensorFlowNET.Core/Binding.Util.cs

GitHub Actions / windows

The type 'T1' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'T1' doesn't match 'notnull' constraint.
=> key == null ?
default :
(dict.ContainsKey(key) ? dict[key] : default);
return e1.Zip(e2, (t1, t2) => (t1, t2));
}
public static IEnumerable<(TKey, TValue)> enumerate<TKey, TValue>(Dictionary<TKey, TValue> values)

Check warning on line 270 in src/TensorFlowNET.Core/Binding.Util.cs

GitHub Actions / linux

The type 'TKey' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'TKey' doesn't match 'notnull' constraint.

Check warning on line 270 in src/TensorFlowNET.Core/Binding.Util.cs

GitHub Actions / windows

The type 'TKey' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'TKey' doesn't match 'notnull' constraint.
{
foreach (var item in values)
yield return (item.Key, item.Value);
public static int sum(IEnumerable<int> enumerable)
=> enumerable.Sum();
public static double sum<TKey, TValue>(Dictionary<TKey, TValue> values)

Check warning on line 358 in src/TensorFlowNET.Core/Binding.Util.cs

GitHub Actions / linux

The type 'TKey' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'TKey' doesn't match 'notnull' constraint.

Check warning on line 358 in src/TensorFlowNET.Core/Binding.Util.cs

GitHub Actions / windows

The type 'TKey' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'TKey' doesn't match 'notnull' constraint.
{
return sum(values.Keys);
}
public static bool empty<T>(this Queue<T> queue)
=> queue.Count == 0;
public static TValue SetDefault<TKey, TValue>(this Dictionary<TKey, TValue> dic, TKey key, TValue defaultValue)

Check warning on line 450 in src/TensorFlowNET.Core/Binding.Util.cs

GitHub Actions / linux

The type 'TKey' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'TKey' doesn't match 'notnull' constraint.

Check warning on line 450 in src/TensorFlowNET.Core/Binding.Util.cs

GitHub Actions / windows

The type 'TKey' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'TKey' doesn't match 'notnull' constraint.
{
if (dic.ContainsKey(key))
return dic[key];
return defaultValue;
}
public static TValue Get<TKey, TValue>(this Dictionary<TKey, TValue> dic, TKey key, TValue defaultValue)

Check warning on line 459 in src/TensorFlowNET.Core/Binding.Util.cs

GitHub Actions / linux

The type 'TKey' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'TKey' doesn't match 'notnull' constraint.

Check warning on line 459 in src/TensorFlowNET.Core/Binding.Util.cs

GitHub Actions / windows

The type 'TKey' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'TKey' doesn't match 'notnull' constraint.
{
if (dic.ContainsKey(key))
return dic[key];
return _handle == IntPtr.Zero ? "tf.Operation Undefined" : $"<tf.Operation '{name}' type={OpType}>";
}
public override bool Equals(object obj)

Check warning on line 40 in src/TensorFlowNET.Core/Operations/Operation.Implicit.cs

GitHub Actions / linux

Nullability of type of parameter 'obj' doesn't match overridden member (possibly because of nullability attributes).
{
switch (obj)
{
protected Graph _graph;
bool _building_function;
public variable_scope(string name,

Check warning on line 47 in src/TensorFlowNET.Core/Variables/variable_scope.py.cs

GitHub Actions / windows

Non-nullable field '_scope' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 47 in src/TensorFlowNET.Core/Variables/variable_scope.py.cs

GitHub Actions / windows

Non-nullable field '_scope' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
string default_name = "",
Tensor[] values = null,
bool? reuse = null,
// A handle is created for every return, even if rawReturns[i] is null. The resulting handle will be
// non-null but invalid, which is the same behavior P/Invoke gives for non-array SafeHandle return
// values.
retvals[i] = new SafeEagerTensorHandle(rawReturns[i]);

Check warning on line 179 in src/TensorFlowNET.Core/Eager/c_api.eager.cs

GitHub Actions / linux

Dereference of a possibly null reference.
}
}
}
first = pair.Key;
second = pair.Value;
}
public static void Update<T1, T2>(this Dictionary<T1, T2> dic, IDictionary<T1, T2> other)

Check warning on line 15 in src/TensorFlowNET.Core/Common/Extensions/DictionaryExtension.cs

GitHub Actions / windows

The type 'T1' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'T1' doesn't match 'notnull' constraint.
{
foreach(var (key, value) in other)
{
dic[key] = value;
}
}
public static T2 GetOrDefault<T1, T2>(this Dictionary<T1, T2> dic, T1 key, T2 defaultValue)

Check warning on line 22 in src/TensorFlowNET.Core/Common/Extensions/DictionaryExtension.cs

GitHub Actions / windows

The type 'T1' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>'. Nullability of type argument 'T1' doesn't match 'notnull' constraint.
{
if (dic.ContainsKey(key))
{