Skip to content

Commit

Permalink
Merge branch 'master' into update-main-doc
Browse files Browse the repository at this point in the history
  • Loading branch information
dsmilkov committed Jan 13, 2020
2 parents da4bdf9 + 695f4aa commit 674ca11
Show file tree
Hide file tree
Showing 105 changed files with 4,005 additions and 640 deletions.
10 changes: 5 additions & 5 deletions tfjs-backend-wasm/WORKSPACE
Expand Up @@ -8,9 +8,9 @@ emsdk_configure(name = "emsdk")

git_repository(
name = "xnnpack",
commit = "403b7d4f2b446f158f1209f26e64104ea772694f",
commit = "3a77ea7bbe30b2411591f2ab15f9c5032a25f688",
remote = "https://github.com/google/XNNPACK.git",
shallow_since = "1575578984 -0800",
shallow_since = "1577131863 -0800",
)

# The libraries below are transitive dependencies of XNNPACK that we need to
Expand Down Expand Up @@ -75,10 +75,10 @@ http_archive(
http_archive(
name = "psimd",
build_file = "@xnnpack//third_party:psimd.BUILD",
sha256 = "7d1795ebf289af26e404cff5877c284775e491414cf41d7d99ab850ceaced458",
strip_prefix = "psimd-4f2c53947184b56f58607b9e777416bb63ebbde1",
sha256 = "1fefd66702cb2eb3462b962f33d4fb23d59a55d5889ee6372469d286c4512df4",
strip_prefix = "psimd-10b4ffc6ea9e2e11668f86969586f88bc82aaefa",
urls = [
"https://github.com/Maratyszcza/psimd/archive/4f2c53947184b56f58607b9e777416bb63ebbde1.tar.gz",
"https://github.com/Maratyszcza/psimd/archive/10b4ffc6ea9e2e11668f86969586f88bc82aaefa.tar.gz",
],
)

Expand Down
30 changes: 30 additions & 0 deletions tfjs-backend-wasm/src/cc/BUILD
Expand Up @@ -104,6 +104,15 @@ tfjs_cc_library(
],
)

tfjs_cc_library(
name = "non_max_suppression_impl",
srcs = ["non_max_suppression_impl.cc"],
hdrs = ["non_max_suppression_impl.h"],
deps = [
":backend",
],
)

tfjs_cc_library(
name = "prelu_impl",
srcs = ["prelu_impl.cc"],
Expand Down Expand Up @@ -169,6 +178,7 @@ tfjs_cc_library(
":Minimum",
":Mul",
":NonMaxSuppressionV3",
":NonMaxSuppressionV5",
":PadV2",
":Prelu",
":Relu",
Expand Down Expand Up @@ -537,6 +547,17 @@ tfjs_cc_library(
srcs = ["kernels/NonMaxSuppressionV3.cc"],
deps = [
":backend",
":non_max_suppression_impl",
":util",
],
)

tfjs_cc_library(
name = "NonMaxSuppressionV5",
srcs = ["kernels/NonMaxSuppressionV5.cc"],
deps = [
":backend",
":non_max_suppression_impl",
":util",
],
)
Expand Down Expand Up @@ -619,12 +640,21 @@ tfjs_cc_library(
tfjs_cc_library(
name = "Sigmoid",
srcs = ["kernels/Sigmoid.cc"],
hdrs = ["kernels/Sigmoid.h"],
deps = [
":backend",
":unary",
],
)

tfjs_unit_test(
name = "Sigmoid_test",
srcs = ["kernels/Sigmoid_test.cc"],
deps = [
":Sigmoid",
],
)

tfjs_cc_library(
name = "Square",
srcs = ["kernels/Square.cc"],
Expand Down
111 changes: 9 additions & 102 deletions tfjs-backend-wasm/src/cc/kernels/NonMaxSuppressionV3.cc
Expand Up @@ -22,52 +22,7 @@
#include <memory>
#include <vector>

#include "src/cc/backend.h"
#include "src/cc/util.h"

namespace {

float compute_iou(const float* boxes, const size_t i, const size_t j) {
const float* i_coord = boxes + i * 4;
const float* j_coord = boxes + j * 4;

const float y_min_i = std::min(i_coord[0], i_coord[2]);
const float x_min_i = std::min(i_coord[1], i_coord[3]);

const float y_max_i = std::max(i_coord[0], i_coord[2]);
const float x_max_i = std::max(i_coord[1], i_coord[3]);

const float y_min_j = std::min(j_coord[0], j_coord[2]);
const float x_min_j = std::min(j_coord[1], j_coord[3]);

const float y_max_j = std::max(j_coord[0], j_coord[2]);
const float x_max_j = std::max(j_coord[1], j_coord[3]);

const float area_i = (y_max_i - y_min_i) * (x_max_i - x_min_i);
const float area_j = (y_max_j - y_min_j) * (x_max_j - x_min_j);

if (area_i <= 0 || area_j <= 0) {
return 0.0;
}

const float intersect_y_min = std::max(y_min_i, y_min_j);
const float intersect_x_min = std::max(x_min_i, x_min_j);
const float intersect_y_max = std::min(y_max_i, y_max_j);
const float intersect_x_max = std::min(x_max_i, x_max_j);
const float intersect_area =
std::max(intersect_y_max - intersect_y_min, .0f) *
std::max(intersect_x_max - intersect_x_min, .0f);
return intersect_area / (area_i + area_j - intersect_area);
}

// Structure to store the result of the kernel. In this case we give js a
// a pointer in memory where the result is stored and how big it is.
struct Result {
int32_t* buf;
size_t size;
};

} // namespace
#include "src/cc/non_max_suppression_impl.h"

namespace tfjs {
namespace wasm {
Expand All @@ -77,62 +32,14 @@ extern "C" {
#ifdef __EMSCRIPTEN__
EMSCRIPTEN_KEEPALIVE
#endif
const Result* NonMaxSuppressionV3(const size_t boxes_id, const size_t scores_id,
const size_t max_out_size,
const float iou_threshold,
const float score_threshold) {
auto& boxes_info = backend::get_tensor_info(boxes_id);
auto& scores_info = backend::get_tensor_info_out(scores_id);
const float* boxes = boxes_info.f32();
const float* scores = scores_info.f32();
const size_t num_boxes = boxes_info.size / 4;

// Filter out boxes that are below the score threshold.
std::vector<int32_t> box_indices;
for (int32_t i = 0; i < num_boxes; ++i) {
if (scores[i] > score_threshold) {
box_indices.push_back(i);
}
}

// Sort by remaining boxes by scores.
std::sort(box_indices.begin(), box_indices.end(),
[&scores](const size_t i, const size_t j) {
return scores[i] > scores[j];
});

// Select a box only if it doesn't overlap beyond the threshold with the
// already selected boxes.
std::vector<int32_t> selected;
for (int32_t i = 0; i < box_indices.size(); ++i) {
const size_t box_i = box_indices[i];
bool ignore_candidate = false;
for (int32_t j = 0; j < selected.size(); ++j) {
const int32_t box_j = selected[j];
const float iou = compute_iou(boxes, box_i, box_j);
if (iou >= iou_threshold) {
ignore_candidate = true;
break;
}
}
if (!ignore_candidate) {
selected.push_back(box_i);
if (selected.size() >= max_out_size) {
break;
}
}
}

// Allocate memory on the heap for the resulting indices and copy the data
// from the `selected` vector since we can't "steal" the data from the
// vector.
int32_t* data =
static_cast<int32_t*>(malloc(selected.size() * sizeof(int32_t)));
std::memcpy(data, selected.data(), selected.size() * sizeof(int32_t));

// Allocate the result of the method on the heap so it survives past this
// function and we can read it in js.
return new Result{data, selected.size()};
const NonMaxSuppressionResult* NonMaxSuppressionV3(
const size_t boxes_id, const size_t scores_id, const size_t max_out_size,
const float iou_threshold, const float score_threshold) {
const float dummy_soft_nms_sigma = 0.0;

return tfjs::wasm::non_max_suppression_impl(boxes_id, scores_id, max_out_size,
iou_threshold, score_threshold,
dummy_soft_nms_sigma);
}

} // extern "C"
Expand Down
50 changes: 50 additions & 0 deletions tfjs-backend-wasm/src/cc/kernels/NonMaxSuppressionV5.cc
@@ -0,0 +1,50 @@
/* Copyright 2019 Google Inc. All Rights Reserved.
* Licensed 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.
* ===========================================================================*/

#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <memory>
#include <queue>
#include <vector>

#include "src/cc/non_max_suppression_impl.h"

namespace tfjs {
namespace wasm {
// We use C-style API to interface with Javascript.
extern "C" {

#ifdef __EMSCRIPTEN__
EMSCRIPTEN_KEEPALIVE
#endif
const NonMaxSuppressionResult* NonMaxSuppressionV5(const size_t boxes_id,
const size_t scores_id,
const size_t max_out_size,
const float iou_threshold,
const float score_threshold,
const float soft_nms_sigma) {
return tfjs::wasm::non_max_suppression_impl(boxes_id, scores_id, max_out_size,
iou_threshold, score_threshold,
soft_nms_sigma);
}

} // extern "C"
} // namespace wasm
} // namespace tfjs
63 changes: 60 additions & 3 deletions tfjs-backend-wasm/src/cc/kernels/Sigmoid.cc
Expand Up @@ -16,14 +16,26 @@
#include <emscripten.h>
#endif

#include <xnnpack.h>
#include <cmath>
#include <cstddef>
#include <map>
#include <tuple>

#include "src/cc/kernels/Sigmoid.h"

#include "src/cc/backend.h"
#include "src/cc/unary.h"
#include "src/cc/util.h"

namespace {
inline float oper(const float val) { return 1. / (1. + std::exp(-val)); }
// We use std::tuple as the cache key as it implements the compare operator
// needed for std::map.
typedef std::tuple<size_t> OperatorCacheKey;

// The operator cache maps the weights id to the xnn_operator_t instantiated for
// this set of weights.
std::map<OperatorCacheKey, xnn_operator_t> operator_cache;

} // namespace

namespace tfjs {
Expand All @@ -35,7 +47,52 @@ extern "C" {
EMSCRIPTEN_KEEPALIVE
#endif
void Sigmoid(const size_t x_id, const size_t out_id) {
unary(x_id, out_id, oper);
auto& x_info = backend::get_tensor_info(x_id);
auto& out_info = backend::get_tensor_info_out(out_id);

const float* x_buf = x_info.f32();
float* out_buf = out_info.f32_write();

xnn_operator_t sigmoid_op = nullptr;

const size_t channels = 1;
OperatorCacheKey cache_key = {channels};

auto operator_cache_idx = operator_cache.find(cache_key);
if (operator_cache_idx == operator_cache.end()) {
const size_t input_stride = channels;
const size_t output_stride = channels;
const uint32_t flags = 0;

xnn_status status = xnn_create_sigmoid_nc_f32(
channels, input_stride, output_stride, flags, &sigmoid_op);
if (status != xnn_status_success) {
tfjs::util::warn(
"XNN status for xnn_create_sigmoid_nc_f32 is not "
"successful. Got status %d. Use -c dbg to see XNN logs.",
status);
return;
}

operator_cache.insert({cache_key, sigmoid_op});

tfjs::backend::xnn_operator_count++;
} else {
sigmoid_op = operator_cache_idx->second;
}

const size_t batch = x_info.size;
xnn_status status = xnn_setup_sigmoid_nc_f32(
sigmoid_op, batch, x_buf, out_buf, nullptr /* thread pool */);
if (status != xnn_status_success) {
tfjs::util::warn(
"XNN status for xnn_setup_resize_bilinear2d_nhwc_f32 is not "
"successful. Got status %d. Use -c dbg to see XNN logs.",
status);
return;
}

xnn_run_operator(sigmoid_op, nullptr /* thread pool */);
}

} // extern "C"
Expand Down
31 changes: 31 additions & 0 deletions tfjs-backend-wasm/src/cc/kernels/Sigmoid.h
@@ -0,0 +1,31 @@
/* Copyright 2019 Google Inc. All Rights Reserved.
* Licensed 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.
* ===========================================================================*/

#ifndef KERNELS_SIGMOID_H_
#define KERNELS_SIGMOID_H_

#include <cstddef>

namespace tfjs {
namespace wasm {
extern "C" {

void Sigmoid(const size_t x_id, const size_t out_id);

}

} // namespace wasm
} // namespace tfjs

#endif // KERNELS_SIGMOID_H_

0 comments on commit 674ca11

Please sign in to comment.