哈达玛响应

具体过程

一、初始化方法:

属性输入阈值的大小InputSize,输出阈值的大小OutSize,输出位的大小OutBit,隐私预算PrivacyParameters,

这个方法中输入的值是:阈值的大小AphbetSize,隐私参数,编码精度3个参数

InputSize=AphbetSize

OutSize=int(math.pow(2,math.ceil(math.log(AphbetSize+1,2))))

       (上式中存在math类的三个函数,math.pow(),math.ceil(),math.log()

           math.pow(2,3)表示2的3次方,输出结果为8;math.ceil(2.1)表示去2.1向上的最大整数,输出结果为3;math.log(100,10)表示以10为底100的对数,输出结果为2。

           上式中先对阈值求2的对数,再作为次方求其值,比如AphbetSize=20,输出的结果OutSize=32,总之输出值的大小比输入的可能值要大。

       )

OutBit=int(math.ceil(math.log(AphbetSize+1,2)))   当AphbetSize=7,输出结果OutBit=3,总之输入阈值较大时,输出位仍然会很小。

 PrivacyParameters=1/(1+math.exp(PrivacyParameters))   函数math.exp(3)表示e的3次方,得到的结果是20,PrivacyParameters最后的结果其实是某位(0或1)反转的概率。

在编码方式中,有encode_acc=0和encode_acc=1两种,前者是精确编码,后者是快速编码。

那么接下来要说如何利用哈达玛矩阵快速编码:

def Hadarmard_init(k):
    H = [None] * k
    for row in range(k):
        H[row] = [True] * k
    print(H)

这里的参数k表示OutSize输出位的大小,其实就是矩阵行数和列数都为k的方阵,矩阵的初始化阶段其实就是将一个输出位转化为了矩阵的一列。

二:奇偶校验

    def parity_check(self,x,y): #check if the hadamard entry is one (return 0 for if the entry is 1)
        z = x&y #bit and
        z_bit = bin(z)[2:].zfill(self.outbit)
        check = 0
        for i in range(0,self.outbit): #check = sum_i (x_i y_i) (mod 2)
            check = check^int(z_bit[i]) 
        return check

这个方法的主要作用检查哈达玛矩阵的每一项是否为1,如果为1的项返回0。

具体分析:

z=x&y  先传入两个整型的参数x和y,对其进行求位和&运算,&运算先将x和y转化为二进制,都为真则取真,比如5&3可以计算为0101&0011=0001=1

z_bit=bin(z)[2:].zfill(self.outbit) ,这里的outbit就是OutSize,相对于输入的阈值其实是很小的位数。

outbit=4
z=2
print(bin(z))                          #将2转化成二进制,并在之前用ob区分
print(bin(z)[2:])                      #取二进制位
print(bin(z)[2:].zfill(outbit))        #将二进制填充至输出位的长度

输出的结果为:
0b10
10
0010

check=check^int(z_bit[i])  ^异或运算符,当对位不同输出1,相同输出0,比如输入值=101^110,结果=011

三、编码单个符号

        bitin = bin(int(in_symbol)+1)[2:].zfill(self.outbit)
        out1 = random.randint(0,math.pow(2,self.outbit)-1) 
        bitout1 = bin(out1)[2:].zfill(self.outbit)

bitin=bin(int(in_symbol)+1)[2:].zfill(self.outbit)

in_symbol是输入的位,将其转化为和输出位相等的二进制字符串

out1=random.randint(0,math.pow(2,self.outbit)-1)

在可能的输出大小中随机选择一位OutSize

bitout1=bin(out1)[2:].zfill(self.outbit)

将随机取中的这一位转换为和输出位相等的二进制字符串

        for i in range(0,self.outbit):
            if int(bitin[i]) == 1:
                out2 = out1 ^ (pow(2,self.outbit - i -1))
                break

对Out1再进行扰动,对于输入二进制串的最左边的1进行扰动  out2=out1^(pow(2,self.outbit-i-1))

这一步骤相当于在可能输出值中随机抽取一位,对这一位再进行确定性的扰动,得到可能潜在的输出。

    if self.ifencode_acc == 1:
            check = 1 - self.H[int(in_symbol)+1][out1]
        else:
            check = 0
            for i in range(0,self.outbit): # check if the Hadamard entry at position (in_symbol+1, out1) is one or not
                check = check^(int(bitout1[i])&int(bitin[i]))
    ra = random.random()
        if check == 0:if ra > self.pri_para:
                return out1
            else:
                return out2
        else: if ra > self.pri_para:
                return out2
            else:
                return out1       
    

ra=random.random()从0-1中随机选择一个小数

将其和隐私参数比较,如果ra大于隐私参数,则不进行扰动输出out1,否则进行扰动输出out2

四、编码串(多个符号)

    def encode_string(self,in_list):  
        out_list = [self.encode_symbol(x) for x in in_list] 
        return out_list

其实就是将输入的列表,将其中的每个元素进行编码,再将其编码后的列表输出。

对于编码部分个人的一些理解:

首先用户的某个属性的阈值是确定的,比如成年人的学历作为一个属性,假设输入值有高中、大专、本科、硕士,我们将用[k]={0,1,2,3}表示,然后将其映射到哈达玛矩阵

映射方式是将每个符号用二进制表示,二进制长度和输出位大小相同,如果位数不够左边用0补齐。比如0映射为矩阵中第一列,1映射为第二列,以此类推...

那么编码之后应该如何对其进行扰动:

当然有一点应该明确,输入的阈值(上面的学历属性)确定后,输出位的大小和可能输出值的大小就确定了,我们在可能的输出值中随机选择一位,并将其转换为二进制串

并且对其最左边为1的位进行反转得到潜在的输出,再随机取一个0-1的概率值和之前的隐私参数比较,来选择返回反转前的值还是反转后的值,这就其大致的扰动方式。

其实在上面的encode_string函数中,in_list并不是一个列表,而是一种自定义数据类型,类似于[14  6  4 ...  1  5 20]方式。

现在需要补充一点重要的知识点:

输入字符串的类型,ndarray类型,这种类型其实就是一个列表,但是其必须要求列表中的元素类型相同,比如[1 3 6 8 9 5],或者['red' 'blue' 'purple' 'pink' 'black']

ndarray和list的转化方式为:

list转numpy     np.array(a)
ndarray转list    a.tolist()

五、解码串

        l = len(out_list) 
        count,edges = np.histogram(out_list,range(self.outsz+1))
        dist = count/float(l)

l=len(out_list) out_list是编码后输出的串,获得其长度;

count,edges=np.histogram(out_list,range(self.outsz+1))

 np.histogram()中的前两个参数,第一个参数out_list类似于数组,第二个参数range(self.outsz+1)如果outsz去8表示0-9,

对于返回值edges表示横坐标的边数,count表示每个范围内的数量,比如0-1这一个段有的数量;

dist=count/float(l)  求得是一个百分比,每一个区域占的百分比;

        if iffast == 1:  # if we use fast hadamard transform
            dist_mid = FWHT_A(self.outsz, dist)  # do fast hadamard transform to the frequency vector
            dist_S = (dist_mid[1:self.insz + 1] + 1) / float(2)  # get the frequencies of C_i

如果使用快速编码:

dist_mid=FWHT_A(outsz,dist),

def FWHT_A(k, dist):
    if k == 1:
        return dist
    dist1 = dist[0 : k//2]
    dist2 = dist[k//2 : k]
    trans1 = FWHT_A(k//2, dist1)
    trans2 = FWHT_A(k//2, dist2)
    trans = np.concatenate((trans1+ trans2, trans1 - trans2))
    return trans

下面举一个实际例子现实编码的解码的过程:

1、首先生成编码字符串:

prob = prob_list['Uniform']
in_list = np.random.choice(10, 10, p=prob)
 
在均匀分布下随机生成一个样本空间,样本空间是[0-9],间隔是1,比如样本空间为:[7 6 2 5 5 5 9 5 6 4] 
 
2、开始编码:
 
一些理论证明

-RR的期望差()

 

 

k-RAPPOR的期望差(

k-HR的期望差(

 

原文地址:https://www.cnblogs.com/Optimism/p/10483099.html