From e5445a50a4eed19bc88f3937ad3e8f32c7f87388 Mon Sep 17 00:00:00 2001 From: wangwei Date: Sun, 30 Aug 2020 10:34:23 +0800 Subject: [PATCH] remove loss.py and metric.py and the tests for them --- python/singa/loss.py | 216 ------------------------------------ python/singa/metric.py | 220 ------------------------------------- setup.py | 2 +- test/python/test_loss.py | 71 ------------ test/python/test_metric.py | 74 ------------- test/python/test_tensor.py | 6 +- 6 files changed, 4 insertions(+), 585 deletions(-) delete mode 100644 python/singa/loss.py delete mode 100644 python/singa/metric.py delete mode 100644 test/python/test_loss.py delete mode 100644 test/python/test_metric.py diff --git a/python/singa/loss.py b/python/singa/loss.py deleted file mode 100644 index c84ba7696f..0000000000 --- a/python/singa/loss.py +++ /dev/null @@ -1,216 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ============================================================================= -''' -Loss module includes a set of training loss implmentations. Some are converted -from C++ implementation, and the rest are implemented directly using python -Tensor. - -Example usage:: - - from singa import tensor - from singa import loss - import numpy as np - - x = tensor.Tensor((3, 5)) - x.uniform(0, 1) # randomly generate the prediction activation - y = tensor.from_numpy(np.array([0, 1, 3], dtype=np.int)) # set the truth - - f = loss.SoftmaxCrossEntropy() - l = f.forward(True, x, y) # l is tensor with 3 loss values - g = f.backward() # g is a tensor containing all gradients of x w.r.t l -''' -from __future__ import division -from __future__ import absolute_import -from builtins import object - -from . import singa_wrap as singa -from . import tensor -from .proto import model_pb2 - - -class Loss(object): - '''Base loss class. - - Subclasses that wrap the C++ loss classes can use the inherited foward, - backward, and evaluate functions of this base class. Other subclasses need - to override these functions - ''' - - def __init__(self): - self.swig_loss = None - - def forward(self, flag, x, y): - '''Compute the loss values. - - Args: - flag: kTrain/kEval or bool. If it is kTrain/True, then the backward - function must be called before calling forward again. - x (Tensor): the prediction Tensor - y (Tensor): the ground truch Tensor, x.shape[0] must = y.shape[0] - - Returns: - a tensor of floats for the loss values, one per sample - ''' - if type(flag) is bool: - if flag: - flag = model_pb2.kTrain - else: - flag = model_pb2.kEval - return tensor.from_raw_tensor( - self.swig_loss.Forward(flag, x.data, y.data)) - - def backward(self): - ''' - Returns: - the grad of x w.r.t. the loss - ''' - return tensor.from_raw_tensor(self.swig_loss.Backward()) - - def evaluate(self, flag, x, y): # TODO(wangwei) remove flag - ''' - Args: - flag (int): must be kEval, to be removed - x (Tensor): the prediction Tensor - y (Tensor): the ground truth Tnesor - - Returns: - the averaged loss for all samples in x. - ''' - if type(flag) is bool: - if flag: - flag = model_pb2.kTrain - else: - flag = model_pb2.kEval - - return self.swig_loss.Evaluate(flag, x.data, y.data) - - -class SoftmaxCrossEntropy(Loss): - '''This loss function is a combination of SoftMax and Cross-Entropy loss. - - It converts the inputs via SoftMax function and then - computes the cross-entropy loss against the ground truth values. - - For each sample, the ground truth could be a integer as the label index; - or a binary array, indicating the label distribution. The ground truth - tensor thus could be a 1d or 2d tensor. - The data/feature tensor could 1d (for a single sample) or 2d for a batch of - samples. - ''' - - def __init__(self): - super(SoftmaxCrossEntropy, self).__init__() - self.swig_loss = singa.SoftmaxCrossEntropy() - - -class SigmoidCrossEntropy(Loss): - '''This loss evaluates the cross-entropy loss between the prediction and the - truth values with the prediction probability generated from Sigmoid. - ''' - - def __init__(self, epsilon=1e-8): - super(SigmoidCrossEntropy, self).__init__() - self.truth = None - self.prob = None - self.epsilon = epsilon # to avoid log(x) with x being too small - - def forward(self, flag, x, y): - '''loss is -yi * log pi - (1-yi) log (1-pi), where pi=sigmoid(xi) - - Args: - flag (bool): true for training; false for evaluation - x (Tensor): the prediction Tensor - y (Tensor): the truth Tensor, a binary array value per sample - - Returns: - a Tensor with one error value per sample - ''' - p = tensor.sigmoid(x) - if flag: - self.truth = y - self.prob = p - np = 1 - p - p += (p < self.epsilon) * self.epsilon - np += (np < self.epsilon) * self.epsilon - l = (y - 1) * tensor.log(np) - y * tensor.log(p) - # TODO(wangwei): add unary operation -Tensor - return tensor.average(l, axis=1) - - def backward(self): - ''' Compute the gradient of loss w.r.t to x. - - Returns: - dx = pi - yi. - ''' - assert self.truth is not None, 'must call forward in a prior' - dx = self.prob - self.truth - self.truth = None - return dx - - def evaluate(self, flag, x, y): - '''Compuate the averaged error. - - Returns: - a float value as the averaged error - ''' - l = self.forward(False, x, y) - return l.l1() - - -class SquaredError(Loss): - '''This loss evaluates the squared error between the prediction and the - truth values. - - It is implemented using Python Tensor operations. - ''' - - def __init__(self): - super(SquaredError, self).__init__() - self.err = None - - def forward(self, flag, x, y): - '''Compute the error as 0.5 * ||x-y||^2. - - Args: - flag (int): kTrain or kEval; if kTrain, then the backward must be - called before calling forward again. - x (Tensor): the prediction Tensor - y (Tensor): the truth Tensor, an integer value per sample, whose - value is [0, x.shape[1]) - - Returns: - a Tensor with one error value per sample - ''' - self.err = x - y - return tensor.square(self.err) * 0.5 - - def backward(self): - '''Compute the gradient of x w.r.t the error. - - Returns: - x - y - ''' - return self.err - - def evaluate(self, flag, x, y): - '''Compuate the averaged error. - - Returns: - a float value as the averaged error - ''' - return tensor.sum(tensor.square(x - y)) * 0.5 / x.size() diff --git a/python/singa/metric.py b/python/singa/metric.py deleted file mode 100644 index ed6a7c9bb0..0000000000 --- a/python/singa/metric.py +++ /dev/null @@ -1,220 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ============================================================================= -"""This module includes a set of metric classes for evaluating the model's -performance. The specific metric classes could be converted from C++ -implmentation or implemented directly using Python. - -Note: This module is deprecated. Please convert the prediction into numpy -array and use the sklearn to compute the metrics. - -Example usage:: - - from singa import tensor - from singa import metric - import numpy as np - - x = tensor.Tensor((3, 5)) - x.uniform(0, 1) # randomly genearte the prediction activation - x = tensor.Softmax(x) # normalize the prediction into probabilities - y = tensor.from_numpy(np.array([0, 1, 3], dtype=np.int)) # set the truth - - f = metric.Accuracy() - acc = f.evaluate(x, y) # averaged accuracy over all 3 samples in x - -""" -from __future__ import division -from __future__ import absolute_import - -from builtins import range -from builtins import object - -from . import singa_wrap as singa -from . import tensor -import numpy as np - - -class Metric(object): - """Base metric class. - - Subclasses that wrap the C++ loss classes can use the inherited foward, - and evaluate functions of this base class. Other subclasses need - to override these functions. Users need to feed in the **predictions** and - ground truth to get the metric values. - """ - - def __init__(self): - self.swig_metric = None - - def forward(self, x, y): - """Compute the metric for each sample. - - Args: - x (Tensor): predictions, one row per sample - y (Tensor): ground truth values, one row per sample - - Returns: - a tensor of floats, one per sample - """ - return tensor.from_raw_tensor(self.swig_metric.Forward(x.data, y.data)) - - def evaluate(self, x, y): - """Compute the averaged metric over all samples. - - Args: - x (Tensor): predictions, one row per sample - y (Tensor): ground truth values, one row per sample - Returns: - a float value for the averaged metric - """ - return self.swig_metric.Evaluate(x.data, y.data) - - -class Accuracy(Metric): - """Compute the top one accuracy for single label prediction tasks. - - It calls the C++ functions to do the calculation. - """ - - def __init__(self): - self.swig_metric = singa.Accuracy() - - -class Precision(Metric): - """Make the top-k labels of max probability as the prediction - - Compute the precision against the groundtruth labels - """ - - def __init__(self, top_k): - self.top_k = top_k - - def forward(self, x, y): - """Compute the precision for each sample. - - Convert tensor to numpy for computation - - Args: - x (Tensor): predictions, one row per sample - y (Tensor): ground truth labels, one row per sample - - Returns: - a tensor of floats, one per sample - """ - - dev = x.device - x.to_host() - y.to_host() - - x_np = tensor.to_numpy(x) - y_np = tensor.to_numpy(y) - - # Sort in descending order - pred_np = np.argsort(-x_np)[:, 0:self.top_k] - - prcs_np = np.zeros(pred_np.shape[0], dtype=np.float32) - - for i in range(pred_np.shape[0]): - # groundtruth labels - label_np = np.argwhere(y_np[i]) - - # num of common labels among prediction and groundtruth - num_intersect = np.intersect1d(pred_np[i], label_np).size - prcs_np[i] = num_intersect / float(self.top_k) - - precision = tensor.from_numpy(prcs_np) - - x.to_device(dev) - y.to_device(dev) - precision.to_device(dev) - - return precision - - def evaluate(self, x, y): - """Compute the averaged precision over all samples. - - Args: - x (Tensor): predictions, one row per sample - y (Tensor): ground truth values, one row per sample - Returns: - a float value for the averaged metric - """ - - return tensor.average(self.forward(x, y)) - - -class Recall(Metric): - """Make the top-k labels of max probability as the prediction - - Compute the recall against the groundtruth labels - """ - - def __init__(self, top_k): - self.top_k = top_k - - def forward(self, x, y): - """Compute the recall for each sample. - - Convert tensor to numpy for computation - - Args: - x (Tensor): predictions, one row per sample - y (Tensor): ground truth labels, one row per sample - - Returns: - a tensor of floats, one per sample - """ - - dev = x.device - x.to_host() - y.to_host() - - x_np = tensor.to_numpy(x) - y_np = tensor.to_numpy(y) - - # Sort in descending order - pred_np = np.argsort(-x_np)[:, 0:self.top_k] - - recall_np = np.zeros(pred_np.shape[0], dtype=np.float32) - - for i in range(pred_np.shape[0]): - # Return the index of non-zero dimension of i-th sample - label_np = np.argwhere(y_np[i]) - - # Num of common labels among prediction and groundtruth - num_intersect = np.intersect1d(pred_np[i], label_np).size - recall_np[i] = float(num_intersect) / label_np.size - - recall = tensor.from_numpy(recall_np) - - x.to_device(dev) - y.to_device(dev) - recall.to_device(dev) - - return recall - - def evaluate(self, x, y): - """Compute the averaged precision over all samples. - - Args: - x (Tensor): predictions, one row per sample - y (Tensor): ground truth values, one row per sample - Returns: - a float value for the averaged metric - """ - - return tensor.average(self.forward(x, y)) diff --git a/setup.py b/setup.py index b6c7399bb6..fdf9c9810c 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ uploading due to file name error. # repair the wheel pakage and upload to pypi - $ /opt/python/cp36-cp36m/bin/python setup.py upload + $ /opt/python/cp36-cp36m/bin/python setup.py audit For the Dockerfile with CUDA and CUDNN installed, the CUDA version and CUDNN version are exported as environment variable: CUDA_VERSION, CUDNN_VERSION. diff --git a/test/python/test_loss.py b/test/python/test_loss.py deleted file mode 100644 index 1d0361daac..0000000000 --- a/test/python/test_loss.py +++ /dev/null @@ -1,71 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from __future__ import division - -import unittest -import numpy as np - -from singa import loss -from singa import tensor - - -class TestLoss(unittest.TestCase): - - def setUp(self): - self.x_np = np.asarray( - [[0.9, 0.2, 0.1], [0.1, 0.4, 0.5], [0.2, 0.4, 0.4]], - dtype=np.float32) - - self.y_np = np.asarray([[1, 0, 1], [0, 1, 1], [1, 0, 0]], - dtype=np.float32) - - self.x = tensor.from_numpy(self.x_np) - self.y = tensor.from_numpy(self.y_np) - - def test_sigmoid_cross_entropy(self): - sig = loss.SigmoidCrossEntropy() - l1 = sig.forward(True, self.x, self.y) - sig.backward() - l2 = sig.evaluate(True, self.x, self.y) - - p = 1.0 / (1 + np.exp(np.negative(self.x_np))) - l = -(self.y_np * np.log(p) + (1 - self.y_np) * np.log(1 - p)) - self.assertAlmostEqual(l1.l1(), l2) - self.assertAlmostEqual(l1.l1(), np.average(l)) - - def test_squared_error(self): - sqe = loss.SquaredError() - l1 = sqe.forward(True, self.x, self.y) - sqe.backward() - l2 = sqe.evaluate(True, self.x, self.y) - - l = 0.5 * (self.y_np - self.x_np)**2 - self.assertAlmostEqual(l1.l1(), tensor.to_numpy(l2).flatten()[0]) - self.assertAlmostEqual(l1.l1(), np.average(l)) - - def test_softmax_cross_entropy(self): - sce = loss.SoftmaxCrossEntropy() - l1 = sce.forward(True, self.x, self.y) - sce.backward() - l2 = sce.evaluate(True, self.x, self.y) - - self.assertAlmostEqual(l1.l1(), l2) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/python/test_metric.py b/test/python/test_metric.py deleted file mode 100644 index e22b5a4242..0000000000 --- a/test/python/test_metric.py +++ /dev/null @@ -1,74 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from __future__ import division - -import unittest -import numpy as np - -from singa import metric -from singa import tensor - - -class TestPrecision(unittest.TestCase): - - def setUp(self): - x_np = np.asarray([[0.7, 0.2, 0.1], [0.2, 0.4, 0.5], [0.2, 0.4, 0.4]], - dtype=np.float32) - - y_np = np.asarray([[1, 0, 1], [0, 1, 1], [1, 0, 0]], dtype=np.int32) - - self.prcs = metric.Precision(top_k=2) - self.x = tensor.from_numpy(x_np) - self.y = tensor.from_numpy(y_np) - - def test_forward(self): - p = self.prcs.forward(self.x, self.y) - self.assertAlmostEqual(tensor.to_numpy(p)[0], 0.5) - self.assertAlmostEqual(tensor.to_numpy(p)[1], 1) - self.assertAlmostEqual(tensor.to_numpy(p)[2], 0) - - def test_evaluate(self): - e = self.prcs.evaluate(self.x, self.y) - self.assertAlmostEqual(e, (0.5 + 1 + 0) / 3) - - -class TestRecall(unittest.TestCase): - - def setUp(self): - x_np = np.asarray([[0.7, 0.2, 0.1], [0.2, 0.4, 0.5], [0.2, 0.4, 0.4]], - dtype=np.float32) - - y_np = np.asarray([[1, 0, 1], [1, 1, 1], [1, 0, 0]], dtype=np.int32) - - self.recall = metric.Recall(top_k=2) - self.x = tensor.from_numpy(x_np) - self.y = tensor.from_numpy(y_np) - - def test_forward(self): - r = self.recall.forward(self.x, self.y) - self.assertAlmostEqual(tensor.to_numpy(r)[0], 0.5) - self.assertAlmostEqual(tensor.to_numpy(r)[1], 2.0 / 3) - self.assertAlmostEqual(tensor.to_numpy(r)[2], 0) - - def test_evaluate(self): - e = self.recall.evaluate(self.x, self.y) - self.assertAlmostEqual(e, (0.5 + (2.0 / 3) + 0) / 3) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/python/test_tensor.py b/test/python/test_tensor.py index 715af6f755..4d83d32dcf 100644 --- a/test/python/test_tensor.py +++ b/test/python/test_tensor.py @@ -484,7 +484,7 @@ def _kfloat32_int(self, dev=gpu_dev): x.to_device(dev) scalar = np.random.random((1,))[0] * 100 y = x + scalar - self.assertEqual(y.dtype, core_pb2.kFloat32) + self.assertEqual(y.dtype, tensor.float32) np.testing.assert_array_almost_equal(tensor.to_numpy(y), x_val + scalar) @unittest.skipIf(not singa_api.USE_CUDA, 'CUDA is not enabled') @@ -501,7 +501,7 @@ def _kint_float(self, dev=gpu_dev): x.to_device(dev) scalar = np.random.random((1,))[0] * 100 y = x + scalar - self.assertEqual(y.dtype, core_pb2.kFloat32) + self.assertEqual(y.dtype, tensor.float32) np.testing.assert_array_almost_equal(tensor.to_numpy(y), x_val + scalar) @unittest.skipIf(not singa_api.USE_CUDA, 'CUDA is not enabled') @@ -549,7 +549,7 @@ def _kint_kint_bc(self, dev=gpu_dev): [-8, 3, -5, -11, 0], [4, 0, 3, -6, -3]]], dtype=np.int32) b_np = np.array([[-6, -3, -8, -17, 1], [-4, -16, 4, -9, 0], - [7, 1, 11, -12, 4], [-6, -8, -5, -3, 0]], + [7, 1, 11, -12, 4], [-6, -8, -5, -3, 0]], dtype=np.int32) ta = tensor.from_numpy(a_np) tb = tensor.from_numpy(b_np)