test_hsigmoid_op.py 7.1 KB
Newer Older
W
weixing02 已提交
1
#   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
W
weixing02 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14
#
# 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.

15 16
from __future__ import print_function

Y
Yancey1989 已提交
17 18
import unittest
import numpy as np
Y
Yancey1989 已提交
19
import math
J
JiabinYang 已提交
20 21 22
# import paddle.fluid as fluid
# import paddle.fluid.core as core
# from op_builder import OpBuilder
W
weixing02 已提交
23
from op_test import OpTest
Y
Yancey1989 已提交
24

D
dzhwinter 已提交
25 26
np.random.seed(100)

Y
Yancey1989 已提交
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

def find_latest_set(num):
    return 1 + int(math.floor(math.log(num, 2)))


class CodeTable(object):
    def __init__(self, num_classes, code):
        self.c = num_classes + code

    def cal_index(self, bit):
        return (self.c >> (bit + 1)) - 1

    def get_length(self):
        return find_latest_set(self.c) - 1

    def cal_bit(self, bit):
        return self.c & (1 << bit)


46 47 48 49 50 51 52 53 54 55 56
class CodeTableWithCustomTree(object):
    def __init__(self, ptable, pcode, index):
        self.ptable_ = ptable
        self.pcode_ = pcode
        self.index_ = index

    def cal_index(self, bit):
        return self.ptable_[self.index_][bit]

    def get_length(self):
        length = 0
J
JiabinYang 已提交
57
        for ele in self.ptable_[self.index_]:  # find the first -1 to stop trace
58 59 60 61 62 63 64 65 66 67 68

            if ele >= 0:
                length = length + 1
            else:
                return length
        return length

    def cal_bit(self, bit):
        return self.pcode_[self.index_][bit]


W
weixing02 已提交
69
def hsigmoid(x, w, label, bias, num_classes):
Y
Yancey1989 已提交
70 71 72 73 74 75
    batch_size = x.shape[0]
    code_length = find_latest_set(num_classes - 1)
    code_table = [0 for _ in range(code_length)]
    pre_output = np.zeros((batch_size, code_length))
    pre_sum = np.zeros((batch_size, 1))
    out = np.zeros((batch_size, 1)).astype("float32")
W
weixing02 已提交
76
    for i in range(batch_size):
W
weixing02 已提交
77
        code_table = CodeTable(num_classes, label[i])
Y
Yancey1989 已提交
78
        length = code_table.get_length()
W
weixing02 已提交
79
        for j in range(length):
Y
Yancey1989 已提交
80 81
            idx = code_table.cal_index(j)
            pre_output[i][j] += bias[0][idx]
82 83
    for i in range(batch_size):
        code_table = CodeTable(num_classes, label[i])
W
weixing02 已提交
84
        length = code_table.get_length()
85 86 87
        for j in range(length):
            idx = code_table.cal_index(j)
            pre_output[i][j] += np.dot(w[idx], x[i])
Y
Yancey1989 已提交
88
    # clip[-40.0, 40.0]
W
weixing02 已提交
89
    pre_output = np.clip(pre_output, -40.0, 40.0)
Y
Yancey1989 已提交
90
    # out(i, 0) = \sum_j  bit(i, j) * preout(i, j)
J
JiabinYang 已提交
91
    pre_output = -1 * pre_output
W
weixing02 已提交
92
    for i in range(batch_size):
W
weixing02 已提交
93
        code_table = CodeTable(num_classes, label[i])
Y
Yancey1989 已提交
94 95
        length = code_table.get_length()
        sum = 0.0
W
weixing02 已提交
96
        for j in range(length):
Y
Yancey1989 已提交
97 98 99 100 101 102 103
            if code_table.cal_bit(j):
                sum += pre_output[i][j]
        out[i] = -1.0 * sum
    # soft relu
    pre_output = np.log(1 + np.exp(pre_output))
    pre_sum = pre_output.sum(1).reshape((batch_size, 1))
    out += pre_sum
104
    return pre_output, out
Y
Yancey1989 已提交
105 106


107 108 109 110
def hsigmoidWithCustomTree(x, w, ptable, pcode, label, bias, num_classes):
    batch_size = x.shape[0]
    code_length = len(ptable[0])
    code_table = [0 for _ in range(code_length)]
J
JiabinYang 已提交
111
    # init pre_out with shape [N, code_length]
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
    pre_output = np.zeros((batch_size, code_length))
    pre_sum = np.zeros((batch_size, 1))
    out = np.zeros((batch_size, 1)).astype("float32")
    for i in range(batch_size):
        code_table = CodeTableWithCustomTree(ptable, pcode, i)
        length = code_table.get_length()
        for j in range(length):
            idx = code_table.cal_index(j)
            pre_output[i][j] += bias[0][idx]
    for i in range(batch_size):
        code_table = CodeTableWithCustomTree(ptable, pcode, i)
        length = code_table.get_length()
        for j in range(length):
            idx = code_table.cal_index(j)
            pre_output[i][j] += np.dot(w[idx], x[i])
    # clip[-40.0, 40.0]
    pre_output = np.clip(pre_output, -40.0, 40.0)
J
JiabinYang 已提交
129
    pre_output = -1 * pre_output
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
    # out(i, 0) = \sum_j  bit(i, j) * preout(i, j)
    for i in range(batch_size):
        code_table = CodeTableWithCustomTree(ptable, pcode, i)
        length = code_table.get_length()
        sum = 0.0
        for j in range(length):
            if code_table.cal_bit(j):
                sum += pre_output[i][j]
        out[i] = -1.0 * sum
    # soft relu
    pre_output = np.log(1 + np.exp(pre_output))
    pre_sum = pre_output.sum(1).reshape((batch_size, 1))
    out += pre_sum
    return pre_output, out


J
JiabinYang 已提交
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
class TestHSigmoidOp(OpTest):
    def setUp(self):
        self.op_type = "hierarchical_sigmoid"
        num_classes = 6
        feature_size = 8
        batch_size = 4
        x = np.random.random((batch_size, feature_size)).astype("float32") * 2
        w = np.random.random(
            (num_classes - 1, feature_size)).astype("float32") * 2
        label = np.random.randint(0, num_classes, (batch_size, 1))
        bias = np.random.random((1, num_classes - 1)).astype("float32")
        self.attrs = {'num_classes': num_classes}
        self.inputs = {'X': x, 'W': w, 'Label': label, 'Bias': bias}
        pre_output, out = hsigmoid(x, w, label, bias, num_classes)
        self.outputs = {'PreOut': pre_output, 'Out': out}
161

J
JiabinYang 已提交
162 163
    def test_check_output(self):
        self.check_output()
164

J
JiabinYang 已提交
165 166
    def test_check_grad(self):
        self.check_grad(['Bias', 'X', 'W'], ['Out'], no_grad_set=set('Label'))
167 168 169


class TestHSigmoidOpWithCostumTree(OpTest):
Y
Yancey1989 已提交
170
    def setUp(self):
Y
Yancey1989 已提交
171
        self.op_type = "hierarchical_sigmoid"
172
        num_classes = 6  #using 1,2,3,4,5,6 to build a huffman tree and select 1,2,5,6 as sample
173
        feature_size = 8
G
guosheng 已提交
174
        batch_size = 4
J
JiabinYang 已提交
175
        x = np.random.random((batch_size, feature_size)).astype("float32") * 2
176
        w = np.random.random(
J
JiabinYang 已提交
177
            (num_classes - 1, feature_size)).astype("float32") * 2
178 179 180 181 182 183 184
        label = np.array([0, 1, 4, 5])
        ptable = np.array(
            [(0, 2, -1, -1, -1), (0, 1, 3, -1, -1), (0, 1, 4, -1, -1),
             (0, 2, -1, -1,
              -1)])  #np.array to store 1,2,5,6s' non-leaf path(root -> leaf)
        pcode = np.array([(0, 0, -1, -1, -1), (1, 1, 1, -1, -1), (
            1, 0, 0, -1, -1), (0, 1, -1, -1, -1)])  #np.array to store 
Y
Yancey1989 已提交
185
        bias = np.random.random((1, num_classes - 1)).astype("float32")
Y
Yancey1989 已提交
186
        self.attrs = {'num_classes': num_classes}
187 188 189 190 191 192 193 194 195 196
        self.inputs = {
            'X': x,
            'W': w,
            'PTable': ptable,
            'PCode': pcode,
            'Label': label,
            'Bias': bias
        }
        pre_output, out = hsigmoidWithCustomTree(x, w, ptable, pcode, label,
                                                 bias, num_classes)
W
weixing02 已提交
197
        self.outputs = {'PreOut': pre_output, 'Out': out}
Y
Yancey1989 已提交
198 199

    def test_check_output(self):
200
        print("checking output in CostumTree")
Y
Yancey1989 已提交
201 202 203
        self.check_output()

    def test_check_grad(self):
204
        print("checking outputGrad in CostumTree")
G
guosheng 已提交
205
        self.check_grad(['Bias', 'X', 'W'], ['Out'], no_grad_set=set('Label'))
Y
Yancey1989 已提交
206 207 208 209


if __name__ == '__main__':
    unittest.main()